matlab-generate-gnss-waveform について
GPS、Galileo、NavICのナビゲーションデータを含むGNSSベースバンド波形を生成するMATLABスキルです。衛星シナリオから算出した、またはユーザーが指定したチャネル変動を使い、受信機のシミュレーションやテストに利用できます。
主な機能
- 01GPS、Galileo、NavICの波形生成
- 02シナリオに基づくドップラーと遅延
- 03チャネルパラメーターの指定
- 04ナビゲーションデータの符号化
- 05複数衛星信号の生成
活用シーン
- 01GNSS受信機の捕捉と追尾のテスト
- 02衛星シナリオに基づくチャネルのシミュレーション
- 03位置推定テスト用の復号可能な信号の生成
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ matlab/ matlab-agentic-toolkit --skill matlab-generate-gnss-waveformインストール後、Claude Code で/matlab-generate-gnss-waveformを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
manifest.yamlreferences/galileo-pipeline.mdgps-pipeline.mdhelper-files.mdnavic-pipeline.mdphysical-constraints.mdrinex-integration.mdsignal-type-mapping.md詳細
GNSS 波形の生成
適切に符号化された航法データを含む、複数衛星の GNSS ベースバンド波形を生成します。チャネルの劣化要因(ドップラー、遅延、ノイズ)には、現実的な衛星シナリオから算出した値、またはユーザー指定のカスタム値を使用できます。GPS、Galileo、NavIC の衛星測位システムに対応しています。波形には実際のエフェメリスが含まれるため、受信機で復号して位置を推定できます。
使用する場合
- 受信機テスト用に GNSS ベースバンド信号(GPS、Galileo、NavIC)を生成する場合
- 軌道力学に基づくドップラーと遅延を使って複数衛星のチャネルをシミュレートする場合
- 特定のドップラー/遅延/SNR 値を使ってテストする場合(カスタムチャネルモード)
- 復号可能な航法データを必要とする、GNSS 受信機のエンドツーエンドシミュレーションを構築する場合
使用しない場合
- 信号捕捉、トラッキングループ、航法データの復号、位置推定を行う場合
- BeiDou/GLONASS(非対応)または SDR ハードウェアでの送信を行う場合
前提条件:ヘルパーファイル
MathWorks のサンプルからヘルパーファイルをコピーして、プロジェクトフォルダーに配置してください。
衛星測位システム(GPS、Galileo、NavIC)ごとのコピー元サンプルとファイルの一覧は、references/helper-files.md を参照してください。
ワークフロー
パイプラインは 6 つのステップ(0~5)で構成されます。ステップ 0 で信号の種類、チャネルモード、データソースを決定します。チャネルモードによって、ステップ 2~3 が必要かどうかが決まります。
Step 0: Determine signal type + channel mode + data source + nav data intent
Step 1: Configure waveform generator
├── Scenario mode → Step 2 (satellite scenario) + Step 3 (compute channel from physics)
└── Custom mode → Step 3c (user provides Doppler/delay/SNR directly)
Step 4: Encode navigation data (both modes)
Step 5: Generate waveform with channel (both modes)
ステップ 0:信号の種類、チャネルモード、データソースを決定する
ユーザーのプロンプトで信号の種類が明確に指定されていない場合は、次のように尋ねます。
どの GNSS 波形を生成しますか?
GPS: L1 C/A、L1C、L2C、L5 Galileo: E1、E1C(パイロットのみ)、E5a、E5b、E5(広帯域) NavIC: L5、S、L1
また、チャネルの劣化要因(ドップラーシフト、伝搬遅延、 ノイズ)はどのように適用しますか?
(a)シナリオベース — 衛星シナリオから現実的な値を計算します。受信機の位置(緯度/経度)を指定していただければ、軌道力学は こちらで処理します。
(b)カスタム値 — 衛星ごとにドップラー(Hz)、遅延(s)、および/または SNR(dB)を指定していただきます。制御されたテストや、独自のチャネルモデルがすでにある場合に便利です。
判定の目安: 位置/都市名/緯度・経度 → シナリオベース。具体的な ドップラー/遅延/SNR の数値 → カスタム。 「ストレステスト」や「極端」→ カスタム。 手がかりがない場合 → 尋ねます。信号の種類が明確でモードが曖昧な場合は、モードについてのみ尋ねます。
データソースの選択
利用可能: gpsAlmanac.txt(常にパス上にあります)、RINEX .rnx ファイル
(Nav Toolbox)。セットアップが必要: galileoAlmanac.xml(サンプルからコピー)、最新のアルマナック(USCG NAVCEN からダウンロード)。
選択ルール:
- ユーザーが RINEX を提供する場合 → シナリオと航法データの両方に使用します。
- RINEX がない場合 → 同梱のアルマナック(
gpsAlmanac.txtまたはgalileoAlmanac.xml)を使用します。 - 現在のエポックが必要な場合 → 最新のアルマナックのダウンロードを提案します(インターネット接続が必要です)。
- RINEX が要求されたものの Nav Toolbox がない場合 → アルマナックに切り替えるか、手動で解析します
(
references/rinex-integration.mdを参照)。
同梱ファイルはすべて、過去の固定されたエポックのものです。 受信機アルゴリズムのテストには問題ありません(軌道配置は引き続き有効です)が、今日の実際の衛星配置を表すものではありません。エージェントは、使用するファイルとエポックを必ず伝えてください。
航法データの意図
航法データには、次のいずれかを使用できます。
- 実際のエフェメリス(アルマナック/RINEX 由来)— 位置推定が機能する必要のある、受信機のエンドツーエンドテストに必須です
- ランダム/ダミービット — 航法データの復号が目的ではなく、信号レベルのテスト(捕捉、 トラッキング、変調品質)を行う場合に有効です
ユーザーの意図が明確でない場合は、次のように尋ねます。
位置推定のために、実際の航法データを含む波形が必要ですか? それとも、信号レベルのテストならダミービットで問題ありませんか?
停止 — 生成前に選択内容を提示する
コードを記述または実行する前に、信号の種類、中心周波数、サンプルレート、継続時間、 データソースとエポック、開始時刻、航法データの種類を示す設定概要を、単独のテキストとして出力してください。これはコードブロックより前に、ユーザーに見えるテキストとして必ず表示してください。コメントや実行後の概要に埋め込んではいけません。対話モードでは、ユーザーの確認を待ってください。
ステップ 1:波形ジェネレーターを設定する(コンステレーション固有)
GPS:
sampleRate = 5e6;
wavegenobj = gpsWaveformGenerator(SampleRate=sampleRate);
wavegenobj.SignalType = "legacy"; % L1 C/A
navDataType = "LNAV";
centerFrequency = 1575.42e6;
stepTime = wavegenobj.BitDuration;
確認: L1 C/A では stepTime が 0.02(20 ms)、L1C/L5 では 0.01 であることを確認します。
Galileo:
sampleRate = 25e6; % Use 60e6 for E5 wideband
wavegenobj = galileoWaveformGenerator(SampleRate=sampleRate);
wavegenobj.SignalType = "E1"; % or "E1C","E5a","E5b","E5"
centerFrequency = 1575.42e6; % E5 uses 1191.795e6
stepTime = wavegenobj.BitDuration;
確認: E1/E1C/E5b では stepTime が 0.004(4 ms)、E5a/E5 では 0.02 であることを確認します。
E5 には重大な注意点があります — ナビゲーションデータは {fnavbits, inavbits} セル配列である必要があり(行列ではありません)、I/NAV には F/NAV の 5 倍の行数が必要です。
references/galileo-pipeline.md の E5 セクションを参照してください。
NavIC(R2026a -- まだシステムオブジェクトはありません):
sampleRate = 24e6;
centerFrequency = 1176.45e6; % L5 (or 2492.028e6 for S, 1575.42e6 for L1)
stepTime = 0.02; % 20 ms for L5/S, 10 ms for L1
NavIC L1 は L5/S とは大きく異なります(SBOC、IZ4 コード、サポートファイルのダウンロード)。references/navic-pipeline.md の §NavIC L1 SPS を参照してください。
信号タイプの一覧については、references/gps-pipeline.md、references/galileo-pipeline.md、または references/navic-pipeline.md を参照してください。
ステップ 2:衛星シナリオを設定する(シナリオモードのみ)
カスタムモードでは、このステップを完全にスキップしてください。
シナリオ、地上局、タイミング(共通)を作成します。その後、コンステレーション固有の方法で 衛星を追加します。
sc = satelliteScenario;
rx = groundStation(sc, lat, lon, Altitude=alt);
rx.MinElevationAngle = 10;
waveDuration = 10; % seconds
sc.StartTime = startTime;
sc.StopTime = sc.StartTime + seconds(waveDuration - stepTime);
sc.SampleTime = stepTime;
衛星を追加します(コンステレーション固有)。ユーザーが RINEX ファイルを明示的に持っている場合、または RINEX を要求した場合を除き、既定ではアルマナックを使用します。アルマナックを使う方法では、Navigation Toolbox への依存(rinexread)を回避できます。同じデータソースをステップ 4(ナビゲーションデータのエンコード)にも使用する必要があります。
GPS(アルマナックを使う方法 — 推奨):
sat = satellite(sc, "gpsAlmanac.txt", OrbitPropagator="gps");
GPS(RINEX を使う方法): まず ~isempty(ver('nav')) を確認します。Navigation
Toolbox が利用できない場合は、アルマナックを使用するか、RINEX を手動で解析します
(references/rinex-integration.md を参照)。
rinexdata = rinexread(rinexFileName);
sat = satellite(sc, rinexdata, OrbitPropagator="gps");
Galileo(アルマナックを使う方法 — 推奨) — satellite() は Galileo XML アルマナックファイルを受け付けません。次のヘルパーを使用します。
sat = HelperAddGalileoSatellitesToScenario(sc, "galileoAlmanac.xml");
Galileo(RINEX を使う方法): 同じくツールボックスの有無を確認し、利用できない場合はアルマナックに切り替えます。
rinexdata = rinexread(rinexFileName);
sat = satellite(sc, rinexdata);
NavIC — RINEX のみ(Navigation Toolbox が必要です)。
HelperAddSatellite(sc, selectedRows) を使用します — references/navic-pipeline.md を参照してください。
重要: 軌道伝搬(このステップ)とナビゲーションデータのエンコード(ステップ 4)の両方で、同じ startTime と同じデータソース(アルマナックまたは RINEX)を使用してください。
確認: 衛星を追加した後、sat が空でないことを確認します。GPS のアルマナックを使用する場合、シナリオには合計で約 30 基の衛星が含まれる見込みです。
ステップ 3:物理法則からチャネルパラメーターを計算する(シナリオモードのみ)
カスタムモードでは、このステップを完全にスキップし、代わりにステップ 3c に進んでください。
dopShifts = dopplershift(sat, rx, Frequency=centerFrequency).';
ltncy = latency(sat, rx).';
自由空間伝搬損失から SNR を計算します:
DtLin = db2pow(12); % Transmit antenna directivity
DrLin = db2pow(4); % Receive antenna directivity
k = physconst("boltzmann");
T = 300; % Temperature (K)
Pr = Pt*DtLin*DrLin ./ ((4*pi*(centerFrequency + dopShifts).*ltncy).^2);
snrs = 10*log10(Pr/(k*T*sampleRate)) + 3;
| コンステレーション | Pt(送信電力) |
|---|---|
| GPS | 44.8 W |
| Galileo | 160 W |
| NavIC | 50 W |
最初の時間ステップで遅延が NaN でない、可視の衛星を見つけます:
satIndices = find(~isnan(ltncy(1,:)));
確認: numel(satIndices) が 4 以上であることを確認します(測位に必要な最小数)。
0 の場合、開始時刻がデータソースのエポックと一致していません(注意点 #12)。
ステップ 3c:カスタムチャネルパラメーターを設定する(カスタムモードのみ)
シナリオモードでは、このステップを完全にスキップしてください。
ユーザーはドップラー、遅延、および/または SNR の値を直接指定します。
numSats = 4;
dopShifts = [2000, -1500, 800, 3200]; % Hz per satellite
ltncy = [0.072, 0.074, 0.069, 0.078]; % seconds per satellite
snrs = [-10, -12, -11, -9]; % dB per satellite
% For time-varying custom values, use matrices (numsteps x numSats)
% dopShifts = [...]; % each row is one time step
waveDuration = 10; % seconds
numsteps = round(waveDuration / stepTime);
satIndices = 1:numSats;
ユーザーから必ず指定してもらう項目: 衛星数、ドップラー(Hz)、遅延(s)、 PRN/SVID リスト。省略可能な項目(既定値): SNR(-10 dB)、継続時間(10 s)、 時間変動(なし、静的)。
生成ループ用に静的な値を展開します:
if isvector(dopShifts) && size(dopShifts,1) == 1
dopShifts = repmat(dopShifts, numsteps, 1);
ltncy = repmat(ltncy, numsteps, 1);
snrs = repmat(snrs, numsteps, 1);
end
カスタムモードでは物理的制約の検証を行いません。 ユーザーは意図的に極端な値を指定する場合があります。物理的な範囲に対するアサーションは行わないでください。
Step 4: ナビゲーションデータを符号化する(両モード、コンステレーション固有)
ナビゲーションデータには、位置推定に必要なエフェメリスが含まれています。
ユーザーが実際のエフェメリス(位置推定)を必要とする場合:
- シナリオと同じデータソースを使用します(Step 2)
- シナリオで RINEX を使用した場合は、ナビゲーションデータにも RINEX を使用します(推奨)
- アルマナックで補えるのは、エフェメリス以外のフィールドのみです(衛星の健全性ビット、アルマナックページ)
- ナビゲーションデータの符号化開始時刻は、シナリオの開始時刻と一致させる必要があります — 時刻が一致しないと、衛星位置に対応しないナビゲーションビットが生成され、 気づかないまま位置推定エラーが発生します
ユーザーが信号レベルのテスト(捕捉/追尾)のみを必要とする場合:
- ランダムビットを使用できます:
navdata = randi([0 1], numsteps, numSats); - アルマナックファイルも RINEX ファイルも不要です
- 波形の変調とチャネルは正しくなりますが、ナビゲーションデータのデコードには失敗します
重要: Step 2 で軌道伝搬に RINEX を使用した場合、ナビゲーションデータの符号化には
HelperGPSAlmanac2Config ではなく、必ず HelperGPSRINEX2Config を使用してください。
同じ RINEX 構造体を軌道とエフェメリスの両方に使用します。シナリオに RINEX を使用しながら、
ナビゲーションデータにはアルマナックを使用すると、単一データソースの原則に反し、
エフェメリスに不整合が生じる可能性があります。
実際のエフェメリスを使うシナリオモード: satIndices は、Step 3 で計算した可視衛星から取得します。
実際のエフェメリスを使うカスタムモード: データソースにはアルマナックを使用します(Navigation Toolbox は不要です)。符号化する PRN/SVID 値をユーザーが指定する必要があります。以下のアルマナックのパス例を使用してください。
GPS(アルマナックのパス):
navcfg = HelperGPSAlmanac2Config(almFileName, navDataType, satIndices, startTime);
visiblePRN = [navcfg(:).PRNID];
% Encode nav data per satellite (HelperGPSNAVDataEncode accepts scalar config only)
tempnavdata = HelperGPSNAVDataEncode(navcfg(1));
navdata = zeros(length(tempnavdata), length(navcfg));
navdata(:,1) = tempnavdata;
for isat = 2:length(navcfg)
navdata(:,isat) = HelperGPSNAVDataEncode(navcfg(isat));
end
GPS(RINEX のパス) — 完全な HelperGPSRINEX2Config のパターンについては、references/gps-pipeline.md を参照してください。
Galileo(アルマナックのパス): references/galileo-pipeline.md のナビゲーションデータ信号タイプのマッピング(E1→E1B、E5a→E5a、E5b→E5b)を使用します。
galNavObj = HelperGalileoNavigationData(almFileName, true, SignalType="E1B");
visibleSVIDs = galNavObj.Ephemeris.SatelliteID(satIndices);
[~, inittow] = HelperGPSConvertTime(startTime);
navdata = navdata2bits(galNavObj, visibleSVIDs, inittow, numpages);
Galileo(RINEX のパス) — references/galileo-pipeline.md を参照してください。
NavIC — RINEX のみです。HelperNavICRINEX2Config には、RINEX データテーブルを直接渡します(ファイル名ではありません)。
navicCfg = HelperNavICRINEX2Config(selectedRows);
完全な符号化パターンについては、コンステレーションごとのリファレンスファイルを参照してください。
Step 5: チャネルを適用して波形を生成する(両モード)
このステップは、シナリオモードとカスタムモードで共通です。Step 5 までに、
dopShifts、ltncy、snrs は、取得方法にかかわらず、すべて numsteps x numSats 行列になります。
GPS と Galileo では、ステップごとのループで各システムオブジェクトを使用します。
wavegenobj.PRNID = visiblePRN; % GPS uses PRNID
% wavegenobj.SVID = visibleSVID; % Galileo uses SVID
gnsschannelobj = HelperGNSSChannel( ...
FrequencyOffset=dopShifts(1,satIndices), ...
SignalDelay=ltncy(1,satIndices), ...
SignalToNoiseRatio=snrs(1,satIndices), ...
SampleRate=sampleRate, ...
RandomStream="mt19937ar with seed", Seed=73);
numsteps = round(waveDuration / stepTime);
samplesPerStep = sampleRate * stepTime;
waveform = zeros(numsteps * samplesPerStep, 1);
for istep = 1:numsteps
idx = (istep-1)*samplesPerStep + (1:samplesPerStep);
waveform(idx,:) = gnsschannelobj(wavegenobj(navdata(istep,:)));
gnsschannelobj.SignalToNoiseRatio = snrs(istep, satIndices);
gnsschannelobj.FrequencyOffset = dopShifts(istep, satIndices);
gnsschannelobj.SignalDelay = ltncy(istep, satIndices);
end
NavIC(R2026a) では、HelperNavICBBWaveform または手動変調を使用します
(システムオブジェクトはありません)。ヘルパーは複数列の出力(衛星ごとに1列)を返すため、
そのまま HelperGNSSChannel に渡してください。先に合計してはいけません。
完全なパターンについては、references/navic-pipeline.md を参照してください。
確認: length(waveform) == numsteps * samplesPerStep を満たすこと。波形がすべてゼロではないことを確認してください(チャネルの SNR が低すぎるか、ナビゲーションデータが一致していない可能性があります)。
信号タイプのクイックリファレンス
各コンステレーションの SignalType 文字列、中心周波数、BitDuration、ナビゲーションデータタイプの一覧については、references/signal-type-mapping.md を参照してください。
検証(シナリオモードのみ)
シナリオモードで波形を生成した後、チャネルパラメーターが物理的に妥当であることを確認してください。コンステレーションごとの検証関数と想定範囲の全容については、references/physical-constraints.md を参照してください。主な確認項目は、可視衛星が4機以上であること、ドップラーシフトが ±10 kHz(L1)の範囲内であること、遅延が67~86 ms(MEO)または119~130 ms(GEO/GSO)であること、衛星ごとにドップラーシフトが異なること、ドップラーシフトが時間とともに変化することです。
カスタムモードでは、これらの確認を実行しないでください — ユーザーが意図的に物理範囲外の値を指定する場合があります。
主な機能
| 機能 | 目的 | ツールボックス |
|---|---|---|
gpsWaveformGenerator | GPS ベースバンド波形を生成します | Satellite Communications |
galileoWaveformGenerator | Galileo ベースバンド波形を生成します | Satellite Communications |
satelliteScenario | シナリオを作成します(シナリオモード) | Satellite Communications |
dopplershift | 軌道力学からドップラーシフトを計算します | Satellite Communications |
latency | 伝搬遅延を計算します | Satellite Communications |
rinexread | RINEX 航法ファイルを読み込みます(RINEX パスのみ) | Navigation(任意) |
physconst | 物理定数(ボルツマン定数、光速) | Core MATLAB |
注意点
-
ユーザーが明示的に同意した場合に限り、ランダムな航法ビットを使用してください。
randi([0 1], ...)は信号レベルのテスト(捕捉、追尾)には使用できますが、測位は不可能になります。ユーザーが受信機のエンドツーエンドテストを必要とする場合は、 実際のエフェメリスを使うため、衛星コンステレーション固有の航法データエンコーダーを使用してください。 ステップ 0 で航法データの用途を必ず確認してください。 -
シナリオモードでは、ドップラーや遅延を独自に実装しないでください。 ドップラーに
exp(1j*2*pi*f*t)を、遅延にcircshiftを使用しないでください。物理ベースのチャネルモデリングには、satelliteScenario->dopplershift()->latency()->HelperGNSSChannelを使用してください。カスタムモードでは、 ユーザー指定の値がそのままHelperGNSSChannelに渡されます。 -
航法データの行数は numsteps より多くなります。 航法データエンコーダーは、 完全な航法フレーム構造をエンコードするため、メッセージ全体(例:GPS LNAV では 37500 行)を生成します。 生成ループで使用されるのは、先頭の
numsteps行だけです。サイズ変更や一致のアサーションは行わないでください。余分な行は 使用されません。 -
RINEX:ファイル名ではなく構造体を渡してください。
satellite(sc, rinexData)には.rnxファイル名文字列ではなく、rinexread()が返す構造体を渡します。ファイル名を渡すと、 わかりにくい「invalid orbit data」エラーが発生します。 -
中心周波数は信号の種類と一致させる必要があります。 L1/E1 = 1575.42 MHz、 L2 = 1227.60 MHz、L5/E5a = 1176.45 MHz、E5b = 1207.14 MHz、 E5 = 1191.795 MHz、S = 2492.028 MHz です。
dopplershift()でも一致させる必要があります。 -
GPS では
PRNID、Galileo ではSVIDを使用します。 間違ったプロパティ名を使うと、 システムオブジェクトに動的プロパティが暗黙的に作成されます。波形ジェネレーターはそのプロパティを無視し、デフォルトの衛星 ID で波形を生成するため、 拡散コードが誤ったものになります。 -
Galileo の送信電力は 160 W で、GPS の 44.8 W ではありません。 Galileo に GPS の電力を使うと、 SNR が誤った値になります。NavIC では 50 W を使用します。
-
R2026a には NavIC のシステムオブジェクトがありません。
navicWaveformGeneratorを呼び出すとエラーになります。手動変調(interplexmod、bocmod、gnssCACode) またはHelperNavICBBWaveformヘルパーを使用してください。R2026b ではシステムオブジェクトとして提供されます。 -
satellite()がネイティブに受け付けるのは GPS SEM アルマナックのみです。 Galileo XML ファイルや NavIC RINEX ファイルを直接satellite()に渡すと、エラーが発生するか、軌道が誤って計算されます。ステップ 2 では、 コンステレーション固有のヘルパーを使用してください。 -
GPS L5 と L1C:最新化された信号を送信する衛星はすべてではありません。 Block IIF 以降は L5 を、Block III 以降は L1C を送信します。L1 C/A の約 8~12 基に対し、可視衛星は約 4~6 基になると想定してください。
-
航法データの利用可能性に基づいて PRN/SVID をフィルタリングしてください。 可視衛星と、エンコード前に航法データオブジェクトで利用可能な ID との積集合を取ってください。
-
時刻とエポックの整合性が重要です。 次の 3 つの時刻を一致させる必要があります。
sc.StartTime、航法データエンコーダーのstartTime、データソースのエポックです。sc.StartTimeがアルマナック/RINEX のエポックから大きく離れていると、latency()はすべて NaN を返します(可視衛星がゼロになり、エラーは発生しません)。航法データのstartTimeがsc.StartTimeと異なると、エフェメリスが衛星位置と一致せず、 気づかないまま測位誤差が発生します。同梱ファイルは過去の固定されたエポックのものです。使用するファイルとエポックを必ず伝えてください。
規約
- すべての GNSS 時刻参照には、
TimeZone="UTC"を指定したdatetimeを使用します - 旧式の
'Name', Valueペアではなく、名前と値の構文(Name=Value)を使用します - GPS 時刻の変換には
HelperGPSConvertTimeを、Galileo/NavIC の時刻変換にはHelperGNSSConvertTimeを使用します dopplershift/latencyの出力は必ず転置します(.';)。これらはタイムステップごとに列を返しますが、生成ループではタイムステップごとに行をインデックスします- 波形は事前に確保します:
zeros(numsteps * samplesPerStep, 1) - 期間の計算には
seconds()を使用します(数値をそのまま使用しないでください) - 信号タイプの文字列では大文字と小文字が区別されます:
"Legacy"ではなく"legacy"、"L5"ではなく"l5"を使用します - 可視衛星の検出には
find(~isnan(ltncy(1,:)))を優先します。仰角によるフィルタリングよりもシンプルで堅牢です
Copyright 2026 The MathWorks, Inc.