mcpField
M
matlab-generate-gnss-waveform

matlab-generate-gnss-waveform

github.com/matlab其他1.2k75 次安装1 天前更新
代码仓库

关于 matlab-generate-gnss-waveform

这项 MATLAB 技能可生成带导航数据的 GPS、Galileo 和 NavIC GNSS 基带波形。信道损伤参数可根据卫星场景计算,也可由用户指定,适用于接收机仿真与测试。

主要功能

  1. 01生成 GPS、Galileo 和 NavIC 波形
  2. 02基于场景计算多普勒与延迟
  3. 03自定义信道参数
  4. 04编码导航数据
  5. 05生成多卫星信号

适用场景

  1. 01测试 GNSS 接收机的捕获与跟踪
  2. 02根据卫星场景模拟信道
  3. 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.yaml
references/
galileo-pipeline.md
gps-pipeline.md
helper-files.md
navic-pipeline.md
physical-constraints.md
rinex-integration.md
signal-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 下载)。

决策规则:

  1. 用户提供 RINEX → 同时用于场景和导航数据。
  2. 未提供 RINEX → 使用随附年历(gpsAlmanac.txt 或 galileoAlmanac.xml)。
  3. 需要当前历元数据 → 提供下载最新年历的选项(需要互联网)。
  4. 用户要求使用 RINEX,但没有 Nav Toolbox → 回退到年历或手动解析(参阅 references/rinex-integration.md)。

所有随附文件都来自固定的过去历元。 这适用于接收机算法测试(轨道几何仍然有效),但不能代表今天的实际天空情况。Agent MUST 说明正在使用哪个文件及其历元。

导航数据意图

导航数据可以是:

  • 真实星历(来自年历/RINEX)——端到端接收机测试需要能够估算位置时必须使用
  • 随机/虚拟比特——适用于信号层测试(捕获、跟踪、调制质量),此类测试不以导航解码为目标

如果用户意图不明确,请询问:

您需要波形携带真实导航数据(用于位置估算),还是虚拟比特也可以(用于信号层测试)?

停止 — 生成前先展示选项

在编写或执行任何代码之前,打印一份独立的配置摘要,说明信号类型、中心频率、采样率、时长、数据源 + 历元、开始时间和导航数据类型。此摘要 MUST 以可见文本形式出现在任何代码块之前,不得藏在注释或运行后的摘要中。在交互模式下,等待用户确认。

步骤 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(发射功率)
GPS44.8 W
Galileo160 W
NavIC50 W

查找可见卫星(第一个时间步的延迟值非 NaN):

satIndices = find(~isnan(ltncy(1,:)));

验证:numel(satIndices) 应 >= 4(定位所需的最小值)。 如果为零,说明开始时间与数据源历元不匹配(注意事项 #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

**自定义模式下不进行物理约束验证。**用户可能有意提供极端值。请勿依据物理范围进行断言。

步骤 4:编码导航数据(两种模式,针对特定星座)

导航数据包含接收机进行位置估计所需的星历。

如果用户需要真实星历(位置估计):

  • 使用与场景相同的数据源(步骤 2)
  • 如果场景使用了 RINEX,导航数据也使用 RINEX(首选)
  • 历书仅补充非星历字段(健康状态位、历书页面)
  • 导航数据编码的开始时间必须与场景开始时间一致—— 时间不匹配会导致导航位与卫星位置不对应,进而造成隐蔽的位置估计错误

如果用户只需要信号级测试(捕获/跟踪):

  • 可以使用随机位:navdata = randi([0 1], numsteps, numSats);
  • 完全不需要历书或 RINEX 文件
  • 波形的调制和信道正确,但导航数据解码会失败

关键: 如果步骤 2 中使用 RINEX 进行轨道传播,导航数据编码时必须 使用 HelperGPSRINEX2Config(而不是 HelperGPSAlmanac2Config)。 同一个 RINEX 结构体既用于轨道,也用于星历。场景使用 RINEX 时,导航数据却改用 历书,会违反单一数据源原则,并可能导致星历不一致。

使用真实星历的场景模式: satIndices 来自步骤 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);

完整的编码模式请参阅各星座对应的参考文件。

步骤 5:使用信道生成波形(两种模式)

对于场景模式和自定义模式,此步骤完全相同。到步骤 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 或手动调制 (不使用系统对象)。该助手返回多列输出(每颗卫星一列)——直接传递给 HelperGNSSChannel,不要先求和。完整模式请参阅 references/navic-pipeline.md。

验证: length(waveform) == numsteps * samplesPerStep。波形不应全为零 (这表示信道 SNR 过低或导航数据不匹配)。

信号类型速查

完整的信号类型字符串、中心频率、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 基带波形卫星通信
galileoWaveformGenerator生成 Galileo 基带波形卫星通信
satelliteScenario创建场景(场景模式)卫星通信
dopplershift根据轨道动力学计算多普勒频移卫星通信
latency计算传播时延卫星通信
rinexread读取 RINEX 导航文件(仅限 RINEX 路径)导航(可选)
physconst物理常数(玻尔兹曼常数、光速)MATLAB 核心

注意事项

  1. 仅在用户明确同意时才使用随机导航位。 randi([0 1], ...) 可用于信号级测试(捕获、跟踪),但无法进行位置估算。如果用户需要端到端接收机测试, 请使用特定星座的导航数据编码器生成真实星历。 在步骤 0 中务必确认导航数据的使用意图。

  2. 在场景模式下,绝不要手动计算多普勒频移或时延。 不要使用 exp(1j*2*pi*f*t) 计算多普勒频移,也不要使用 circshift 计算时延。使用 satelliteScenario -> dopplershift() -> latency() -> HelperGNSSChannel 进行基于物理模型的信道建模。在自定义模式下, 用户提供的值会直接传递给 HelperGNSSChannel。

  3. 导航数据的行数多于 numsteps。 导航数据编码器会生成完整消息(例如,GPS LNAV 为 37500 行),因为它们会对完整的导航帧结构进行编码。生成循环只会使用前 numsteps 行。不要调整大小或断言行数相等——多余的行会直接被忽略。

  4. RINEX:传递结构体,而不是文件名。 satellite(sc, rinexData) 需要的是 rinexread() 返回的结构体,而不是 .rnx 文件名字符串。传递 文件名会导致令人困惑的“轨道数据无效”错误。

  5. 中心频率必须与信号类型匹配。 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() 中的频率匹配。

  6. GPS 使用 PRNID,Galileo 使用 SVID。 使用错误的属性名 会静默地在系统对象上创建动态属性——波形生成器会忽略它,并使用默认卫星 ID 生成波形, 导致扩频码不正确。

  7. Galileo 的发射功率是 160 W,而非 GPS 的 44.8 W。 对 Galileo 使用 GPS 功率 会导致 SNR 不正确。NavIC 使用 50 W。

  8. R2026a 中没有 NavIC 系统对象。 调用 navicWaveformGenerator 会报错。请使用手动调制(interplexmod、bocmod、gnssCACode) 或 HelperNavICBBWaveform 辅助函数。该功能将在 R2026b 中作为系统对象提供。

  9. satellite() 原生只接受 GPS SEM 星历。 将 Galileo XML 或 NavIC RINEX 文件直接传给 satellite() 会报错或产生错误的轨道。请使用步骤 2 中特定星座的辅助函数。

  10. GPS L5 和 L1C:并非所有卫星都会播发现代化信号。 Block IIF+ 卫星支持 L5;Block III+ 卫星支持 L1C。预计可见约 4-6 颗,而 L1 C/A 约为 8-12 颗。

  11. 根据导航数据可用性筛选 PRN/SVID。 编码前,将可见卫星与导航数据对象中可用的 ID 取交集。

  12. 时间/历元一致性至关重要。 以下三个时间必须一致: sc.StartTime、导航数据编码器的 startTime 以及数据源历元。如果 sc.StartTime 与星历/RINEX 历元相差很远, latency() 会返回全 NaN(没有可见卫星,但不会报错)。如果导航数据的 startTime 与 sc.StartTime 不同,星历将与卫星位置不匹配, 从而导致不易察觉的位置错误。随附文件都来自固定的过去历元;务必说明使用了哪个文件和历元。

约定

  • 所有 GNSS 时间引用均使用 datetime 和 TimeZone="UTC"
  • 使用名称-值语法(Name=Value),而非旧式的 'Name', Value 对
  • GPS 时间转换:HelperGPSConvertTime;Galileo/NavIC:HelperGNSSConvertTime
  • 始终转置 dopplershift/latency 输出(.';)——它们返回 每个时间步对应一列,但生成循环按每个时间步对应一行进行索引
  • 预分配波形:zeros(numsteps * samplesPerStep, 1)
  • 时长运算使用 seconds()(不要使用裸数字)
  • 信号类型字符串区分大小写:"legacy" 而非 "Legacy","l5" 而非 "L5"
  • 优先使用 find(~isnan(ltncy(1,:))) 检测可见卫星——比按仰角筛选更简单、 更稳健

版权所有 2026 The MathWorks, Inc.

评论

支持 Markdown