关于 matlab-generate-gnss-waveform
这项 MATLAB 技能可生成带导航数据的 GPS、Galileo 和 NavIC GNSS 基带波形。信道损伤参数可根据卫星场景计算,也可由用户指定,适用于接收机仿真与测试。
主要功能
- 01生成 GPS、Galileo 和 NavIC 波形
- 02基于场景计算多普勒与延迟
- 03自定义信道参数
- 04编码导航数据
- 05生成多卫星信号
适用场景
- 01测试 GNSS 接收机的捕获与跟踪
- 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)。
所有随附文件都来自固定的过去历元。 这适用于接收机算法测试(轨道几何仍然有效),但不能代表今天的实际天空情况。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(发射功率) |
|---|---|
| GPS | 44.8 W |
| Galileo | 160 W |
| NavIC | 50 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 核心 |
注意事项
-
仅在用户明确同意时才使用随机导航位。
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)需要的是rinexread()返回的结构体,而不是.rnx文件名字符串。传递 文件名会导致令人困惑的“轨道数据无效”错误。 -
中心频率必须与信号类型匹配。 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。预计可见约 4-6 颗,而 L1 C/A 约为 8-12 颗。
-
根据导航数据可用性筛选 PRN/SVID。 编码前,将可见卫星与导航数据对象中可用的 ID 取交集。
-
时间/历元一致性至关重要。 以下三个时间必须一致:
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.