
简介这份资源面向卫星导航、通信工程方向的研究人员、工程师与学生聚焦北斗B1I频点信号的生成与软件接收仿真帮助读者在MATLAB环境中理解扩频码构造、信号捕获与解扩解码的完整链路。压缩包共3个文件均为.m脚本整体约6KB分别承担GPS信号追踪、北斗B1I信号捕获以及软件接收机核心处理等任务覆盖从信号发射、大气传播到接收端解扩解码的仿真流程。已有642人学习下载说明其在导航仿真入门与教学实践中具有一定参考价值。读者可借助脚本对比GPS与北斗的处理差异掌握FFT等捕获搜索算法的实现思路并在此基础上开展复杂环境下的定位精度测试与性能优化适合作为课程实验、课题预研或自学练手的实践素材。1. 北斗 B1I 信号仿真从一份 SoftReceiver 压缩包说起很多人第一次接触北斗 B1I 信号仿真是从一个叫BD2B1_SoftReceiver.rar的压缩包开始的。解压后往往是一堆 MATLAB 脚本、几个.mat数据文件外加一份语焉不详的说明。你打开主脚本跑起来屏幕上跳出一堆星座图、相关峰、捕获结果但你并不清楚这些信号到底是怎么生成出来的——这正是标题里beidou simulation和生成北斗信号要解决的核心问题不是接收而是从零把一颗北斗卫星的 B1I 信号在软件里造出来。这件事的价值在于做接收机算法验证、做干扰抑制研究、做捕获跟踪性能评估你都需要一份已知真值的信号。真实采集的信号你永远不知道它下一秒的码相位偏移了多少、多普勒是多少、信噪比精确值是多少。而自己生成的信号每一个参数都是你亲手设的这就是仿真不可替代的地方。适合谁做 GNSS 基带算法、通信信号处理、以及需要可控信号源做测试的工程师。下面我从信号结构讲起一路讲到怎么把信号生成脚本跑通、参数怎么调、哪里最容易翻车。2. 北斗 B1I 信号结构生成之前必须搞清的四个参数2.1 B1I 的码速率、载频与测距码从哪来北斗 B1I 信号的中心频率是 1561.098 MHz这个数字不是随便定的它和 GPS L1 的 1575.42 MHz 错开是为了避免系统间干扰。B1I 的测距码速率是 2.046 Mcps码周期 1 ms一个周期 2046 个码片。测距码用的是 Gold 码由两个 11 级线性反馈移位寄存器生成这一点和 GPS C/A 码的 Gold 码思路一致但抽头位置和初相不同。生成信号时你首先要能自己产生这段测距码。常见做法是用两个 m 序列异或。下面是我一般会用的 MATLAB 生成片段% 生成北斗 B1I 测距码简化示意抽头以实际 ICD 为准 function code gen_b1i_code(prn) % 两个 11 级移位寄存器初相由 PRN 决定 g1 zeros(1, 11); g1(1) 1; g2 zeros(1, 11); g2(1) 1; % 根据 PRN 设置 g2 的初相此处为示意实际需查 ICD 表 g2 set_phase_by_prn(g2, prn); code zeros(1, 2046); for n 1:2046 % 抽头异或具体抽头位置查 ICD fb1 xor(g1(1), g1(3)); fb2 xor(g2(1), g2(2)); code(n) xor(g1(11), g2(11)); g1 [fb1, g1(1:10)]; g2 [fb2, g2(1:10)]; end code 1 - 2*code; % 映射到 1/-1 end这段代码的逻辑是两个移位寄存器每个时钟节拍右移一位最高位异或后输出同时反馈位插回最低位。参数说明prn决定 g2 的初相不同卫星用不同初相区分2046是码长对应 1 ms。注意这里的抽头是示意实际抽头必须查北斗 ICD 文件抽头错了整个码就是错的后面全白搭。2.2 导航电文、NH 码与二次编码B1I 在测距码之上还叠加了 NHNeumann-Hoffman码码长 20速率 1000 bps周期 20 ms。NH 码的作用是降低数据比特跳变对跟踪环路的影响。再往上才是导航电文速率 50 bps一个完整的子帧 300 比特、6 秒。生成信号时这三层是串行叠加的导航电文比特 → 与 NH 码相乘 → 再与测距码相乘 → 最后调制到载波。很多人第一次做仿真会漏掉 NH 码结果跟踪环路在数据跳变处频繁失锁还以为是环路带宽没调好其实是信号本身少了一层。层级速率周期作用导航电文50 bps6 s/子帧承载星历、时间NH 码1000 bps20 ms抗数据跳变测距码2.046 Mcps1 ms测距、捕获载波1561.098 MHz—调制2.3 多普勒与码相位的初始设置仿真信号要真实必须给每颗卫星设一个初始码相位和一个多普勒频移。码相位决定信号从码周期的哪个位置开始多普勒则来自卫星与接收机的相对运动B1I 上最大约 ±5 kHz静态接收、中低动态场景。我一般这样设码相位随机取 0~2045 的整数多普勒在 ±5000 Hz 内取码多普勒按载波多普勒的比例缩放约 -多普勒/1540。这个比例关系不能忽略否则长时间积分时码相位和载波相位会失配相关峰展宽甚至裂开。2.4 采样率与中频的选择生成数字信号最后一步是采样。中频一般选 0 Hz零中频或几 MHz。采样率要满足 Nyquist同时最好是码速率的整数倍方便后续处理。常见组合采样率 8.184 MHz4 倍码速率中频 0这样频谱搬移干净不会引入额外镜像。选采样率时有个坑如果你选了个非整数倍的采样率生成端和接收端对码相位的理解会出现小数偏移捕获时相关峰位置对不上排查半天以为是算法问题。我一般直接锁 4 倍或 8 倍码速率。3. 用 MATLAB 把 B1I 信号生成跑通从参数到波形3.1 主生成脚本的骨架把前面几节的东西串起来一个最小可用的生成脚本大概长这样% B1I 信号生成主脚本最小可用版 fs 8.184e6; % 采样率4 倍码速率 fc 1561.098e6; % 标称载频仿真中常用中频代替 fi 0; % 中频零中频 T 0.01; % 生成时长 10 ms t 0:1/fs:T-1/fs; % 时间轴 prn 1; % 卫星号 doppler 1500; % 多普勒 Hz codePhase 300; % 初始码相位码片 code gen_b1i_code(prn); % 1 ms 测距码 nh gen_nh_code(); % 20 ms NH 码 navBit 1; % 导航电文比特示意 % 把 1 ms 码扩展到 10 ms codeSeq repmat(code, 1, 10); % NH 码按 1 ms 一片扩展 nhSeq repelem(nh, 2046); nhSeq nhSeq(1:length(codeSeq)); % 叠加导航电文 baseband codeSeq .* nhSeq * navBit; % 加多普勒与码相位 phase 2*pi*doppler*t; carrier exp(1j*phase); signal baseband .* carrier; % 加噪声 snr -20; % 载噪比折算后的 SNR signal awgn(signal, snr, measured);逻辑说明先造 1 ms 测距码复制到 10 msNH 码每个码片对应 2046 个采样点扩展后截断到相同长度两者相乘再乘导航比特得到基带最后乘复载波加多普勒。参数说明fs决定时间分辨率doppler决定载波旋转速度codePhase这里没显式用上实际要在取码序列时做循环移位。snr用awgn的measured模式按信号实际功率加噪避免手算功率出错。3.2 码相位偏移的正确实现方式上面代码里codePhase没真正生效这是个常见疏漏。正确做法是对码序列做循环移位% 对码序列做循环移位实现初始码相位 shift mod(codePhase, 2046); codeShifted [code(shift1:end), code(1:shift)];注意移位是在 1 ms 码周期内做的扩展成 10 ms 时要先移位再复制否则每个 1 ms 边界都会跳变。这个细节不注意生成出来的信号相关峰会在每个码周期边界出现异常你会以为是多径其实是自己造出来的。3.3 验证生成信号是否正确的三个手段生成完不能直接拿去用要先自检。我一般看三样东西第一看频谱。对信号做 FFT中心应该在多普勒频移处带宽约 2.046 MHz 主瓣。如果频谱出现对称的双峰说明载波用了实信号没做希尔伯特变换或者中频设置有问题。第二看自相关。把生成信号和本地码做相关应该在码相位处出现尖峰峰宽约 1 个码片。如果峰是平的或者有多个峰码生成或移位有问题。第三看星座图。如果是 BPSK 调制星座应该是两个点。如果散成一团噪声加多了或者调制方式错了。% 自相关验证 corr abs(ifft(fft(signal(1:2046)) .* conj(fft(code)))); [~, peakIdx] max(corr); fprintf(相关峰位置: %d, 期望: %d\n, peakIdx-1, codePhase);这段用 FFT 做循环相关比直接xcorr快适合长序列。peakIdx-1是因为 MATLAB 索引从 1 开始。期望值和实际值差 1 以内算正常差多了就是移位逻辑错了。3.4 批量生成多颗卫星信号单颗卫星验证通过后实际场景往往要生成多颗。做法是把每颗卫星的信号独立生成再叠加注意每颗的多普勒和码相位都不同prnList [1, 3, 5, 7]; signalSum zeros(1, length(t)); for k 1:length(prnList) sig gen_one_sat(prnList(k), fs, T, dopplerList(k), phaseList(k)); signalSum signalSum sig; end叠加时要注意功率归一化否则卫星多了总功率爆掉后面加噪的 SNR 就不准了。我一般每颗信号功率设成 1叠加后除以 sqrt(N)保持总功率稳定。4. 避坑与排查B1I 信号生成里最容易翻车的五件事4.1 相关峰对不上差了几个码片现象自相关峰位置和设定的codePhase差了几个甚至几十个码片。原因多半是码生成时抽头错了或者移位时用了circshift但方向反了。解决先单独验证码生成把生成的码和标准码逐位比对移位方向用一个小例子手算确认别凭感觉。4.2 跟踪环路频繁失锁尤其在数据跳变处现象捕获能成功但跟踪时每隔 20 ms 就失锁一次。原因漏了 NH 码或者 NH 码的相位和测距码没对齐。解决检查 NH 码是否按 1 ms 一片正确扩展确认 NH 码的 20 ms 周期和导航电文的 6 s 子帧边界对齐。4.3 频谱出现镜像中心频率偏了一倍现象FFT 看到两个对称峰或者中心频率是设定值的两倍。原因用了实信号载波cos而不是复指数或者采样率不够导致混叠。解决仿真里统一用复指数exp(1j*...)最后取实部才转实信号采样率至少 2 倍带宽建议 4 倍码速率以上。4.4 加噪后 SNR 和预期不符现象设了 -20 dB 的 SNR但相关峰信噪比明显偏高或偏低。原因awgn的measured模式按信号实际功率算如果信号功率没归一化结果就偏。解决加噪前先把信号功率归一到 1或者手动算噪声功率n0 1/10^(snr/10)再加。4.5 长时间生成内存爆掉现象生成 1 秒以上信号时 MATLAB 卡死或报内存不足。原因时间轴t和信号向量都是 double1 秒 8.184 MHz 采样就是 800 多万点几个中间变量一乘内存就上 G。解决分段生成每段 10 ms处理完存盘再生成下一段或者用single精度内存直接减半。5. 进阶技巧用生成信号反推接收机性能边界信号生成不只是造数据它最大的价值是当标尺。我一般会用它做两件事一是扫参数二是定边界。扫参数的做法固定其他条件只变多普勒从 -5000 到 5000 Hz 每隔 250 Hz 生成一次喂给捕获算法记录捕获成功率和耗时。这样你能画出捕获算法的多普勒容限曲线知道它在哪个频偏下开始失效。这个曲线比任何理论推导都直观。定边界的做法固定多普勒只降 SNR从 -30 dB 一路降到 -45 dB看相关峰什么时候淹没在噪声里。这个临界 SNR 就是你接收机灵敏度的仿真上限。真实系统因为前端损耗、量化损失实际会比这个值差 2~3 dB心里有数。% 扫多普勒记录捕获成功率 dopplerScan -5000:250:5000; successRate zeros(size(dopplerScan)); for i 1:length(dopplerScan) sig gen_one_sat(1, fs, 0.01, dopplerScan(i), 0); sig awgn(sig, -25, measured); [acqOk, ~] myAcquisition(sig, code, fs); successRate(i) acqOk; end plot(dopplerScan, successRate); xlabel(多普勒 Hz); ylabel(捕获成功);这段代码的关键是myAcquisition换成你自己的捕获函数acqOk返回 0 或 1。跑完画图你能一眼看出算法的多普勒盲区在哪。我踩过的坑是一开始没归一化功率扫出来的曲线忽高忽低后来统一归一化才稳定。还有一个技巧是加多径。生成信号时把码序列延迟几个码片再叠加模拟一条反射路径幅度设成主径的 0.3~0.5。这样你能测接收机在多径下的跟踪偏差。多径延迟设 0.5、1、1.5 个码片各试一遍偏差曲线会告诉你跟踪环路的抗多径能力。最后说个习惯每次生成信号我都会把参数写进文件名比如b1i_prn1_dop1500_ph300_snr-20.mat。这样过一个月回头看不用打开文件就知道里面是什么。血泪经验不写清楚回头全是黑匣子后悔药没处买。希望帮到你。本文还有配套的精品资源点击获取