北斗B1I跟踪环路设计:二阶FLL辅助三阶PLL与载波辅助码环的MATLAB实现
2026/9/13 12:59:15 网站建设 项目流程

简介:面向北斗二代接收机研发与学习者,Matlab跟踪程序仿真资源完整覆盖接收机基带信号处理中的关键环路:载波环采用二阶FLL辅助三阶PLL实现频率与相位分级锁定,码环为二阶DLL,并引入载波环辅助以提升低信噪比下的码相位估计精度。适用对象是掌握一定卫星导航原理、希望深入跟踪环路设计的工程师与学生,尤其适合需要理解FLL与PLL协同机制、码环与载波环耦合关系的人群,可用来完成环路参数整定、系统联合调试及Matlab仿真验证。包体共8个文件,全部为.m源码,体积仅6KB,目录虽小但功能完整,涵盖参数设置、Gold码生成、本地码生成、环路参数计算、信号处理与启动流程等功能模块,代码结构清晰,便于逐模块阅读。已有579人学习下载,对理解北斗二号接收机跟踪环路的工作原理、工程实现与优化方法具有直接参考价值。通过阅读源码,可快速掌握FLL/PLL切换逻辑与码环辅助机制,无论是课程设计还是工程预研,都能从中获得直接的代码参考与二次开发基础。

1. 北斗二代接收机跟踪程序中的环路选型:二阶FLL、三阶PLL与二阶DLL

北斗二代接收机拿到捕获结果后,真正决定定位精度的是跟踪程序。B1I信号载波频率1561.098MHz,码率2.046Mcps,捕获器给出的频率误差往往还有几十到几百赫兹,码相位误差约在0.5个码片以内。工程上最稳的组合是载波环用二阶FLL配合三阶PLL,码环用二阶DLL,再让载波多普勒估计去辅助码NCO。这样做的原因是:FLL对频率跳变不敏感,牵引范围大;PLL锁定后相位误差小,能支撑载波相位观测;DLL只跟踪码相位,带宽可以压得很窄,载波辅助又能把高动态带来的码率变化提前补偿掉。下面这套MATLAB实现按“先FLL牵引,再PLL锁定,DLL全程用载波辅助”推进,适合从静态基带调试起步,再向中低动态场景扩展。

2. 载波环的程序骨架:二阶FLL鉴别器、三阶PLL滤波器与FLL辅助PLL

载波环的输入不是原始中频采样,而是相关器在每个相干积分周期结束时输出的即时支路IP、QP。跟踪程序里每个毫秒都会做一次“积分清零”,把1ms内的I、Q累加结果交给鉴别器。载波环的误差鉴别是在这一点上完成的。

2.1 FLL和PLL鉴别器:I、Q积分结果怎么变成误差量

二阶FLL常用叉积点积鉴别器,输出频率误差;三阶PLL用四象限反正切鉴别器,输出相位误差。两者的输入相同,区别只在观测量和后续环路滤波器的维数。

function [f_err, p_err] = carrier_discriminators(IP, QP, IP_prev, QP_prev, T) % IP/QP:当前毫秒即时支路相关值,IP_prev/QP_prev:上一毫秒值 % T:相干积分时间,北斗B1I常用T = 0.001,单位秒 cross = IP*QP_prev - IP_prev*QP; dot = IP*IP_prev + QP*QP_prev; % FLL频率误差,单位Hz % 2*pi*T把相位差转成每秒多少弧度的频率差,再除以2*pi得到Hz f_err = atan2(cross, dot) / (2*pi*T); % PLL相位误差,单位rad,四象限反正切保证误差范围是[-pi, pi) p_err = atan2(QP, IP); end

FLL那条公式里,atan2(cross, dot)得到的是相邻两个积分周期之间的相位旋转角,除以积分时间就是频率偏差。用四象限反正切而不是atan(cross/dot),是为了避免在constellation点跨越第一、第四象限时出现±π跳变。PLL鉴别器在数据位翻转面前会受影响,所以后面一般还要根据导航电文比特做符号修正,或者用costas形式的atan( QP / IP ) 先消掉数据位。这里直接给atan2是为了锁定阶段观测方便,实际代码里会再套一层符号判决。

2.2 从噪声带宽到环路滤波器系数:二阶FLL和三阶PLL的参数表

环路滤波器的输入是鉴别器误差,输出是载波NCO的频率修正量。设计时真正要定的是噪声带宽Bn和阻尼系数ζ。噪声带宽决定环路对热噪声的抑制能力和对动态应力的跟踪能力。载波环带宽太宽,相位抖动大;太窄,高动态下会失锁。常见取值如下。

环路鉴别器输出常见Bn典型ζ适用场景
二阶FLL频率误差Hz3~10Hz0.7频率牵引、高动态初期
三阶PLL相位误差rad10~20Hz1.1静态、中低动态相位锁定
二阶DLL码相位误差chip0.5~1.0Hz0.7码相位平稳跟踪

二阶PLL的连续域传递函数可以用 w0^2 / (s^2 + 2ζw0s + w0^2) 描述,其中w0是自然角频率。二阶环的 w0 与Bn换算关系是 Bn = w0(ζ + 1/(4ζ))/2。三阶PLL没有这么简单的换算,工程上经常按照 Bn ≈ 1.2w0 估算,再用仿真微调。MATLAB里可以直接写成一小组系数。

% 三阶PLL环路滤波器状态更新 % Bn_pll = 15; % 噪声带宽,单位Hz,初值按上表取 w0_pll = Bn_pll / 1.2; % 三阶环的工程近似 a3 = 1.1; b3 = 1.75; c3 = 3.15; % p_state1是相位误差的一阶积分,p_state2是二阶积分 p_state1 = p_state1 + p_err * T; p_state2 = p_state2 + p_state1 * T; % 三阶PLL给出的频率修正量,单位rad/s freq_adj = a3*w0_pll*p_err + b3*w0_pll^2*p_state1 + c3*w0_pll^3*p_state2;

这里的a3、b3、c3是三阶环路滤波器的标准系数。p_state1和p_state2两个状态变量相当于连续域里的一阶积分和二阶积分,每毫秒更新一次。T参与了状态更新,所以换相干积分时长时必须同步重算状态,不能只改T不改积分状态。三阶PLL的优势是能跟踪载波相位加速度,对应接收机持续加速或载体转弯的场景,代价是环路稳定性更差,启动时容易被大相位误差推成周期跳变。

2.3 FLL辅助PLL的NCO更新逻辑和切换权重

“辅助”不是先跑FLL再突然切到PLL,而是两个误差源同时作用在载波NCO上,只是权重随锁定状态变化。启动初期,频率误差很大,PLL相位误差在噪声里不占优,这时候FLL频率误差直接加到NCO,把频率拉进几十赫兹以内。等PLL的相位误差方差降下来,再把FLL权重降下去,最后只留PLL。

% 载波NCO更新片段,每次循环执行一次 % lock_state: 0=失锁/牵引, 1=频率锁定, 2=相位锁定 if lock_state < 2 nco_freq = nco_freq + f_err * 0.6 * (2 - lock_state); end nco_freq = nco_freq + freq_adj / (2*pi); % 把rad/s换算成Hz % 载波NCO相位累加 carrier_phase = carrier_phase + nco_freq * T;

FLL辅助增益0.6是折中值。增益太小,牵引时间长;增益太大,PLL相位误差里会混入FLL的高频噪声,明明已经锁定还是会看到IP、QP星座连续旋转。lock_state的判定可以看f_err连续50ms小于20Hz,再切到状态1;phase误差连续50ms小于0.1rad,切到状态2。频率和相位是同一个NCO,所以这个切换过程是连续无突变的,这也是FLL辅助PLL比单纯切换更好的原因。

3. 码环用二阶DLL实现,载波辅助怎么加进码NCO

码环的任务是把本地码相位对准卫星信号里调制的伪随机码。北斗B1I的码周期是1ms,所以码环通常也按1ms工作。码环的输入是超前、即时、滞后三条码支路的相关值,经过鉴别器得到码相位误差,再通过环路滤波器调整码NCO频率。

3.1 E、P、L相关器与非相干超前减滞后功率鉴别器

本地码发生器同时产生三份码序列,通常超前和滞后相对即时码错开0.5个码片。相关器输出IE、QE、IP、QP、IL、QL,其中I和Q是载波剥离后的同相正交分量。非相干鉴别器会先对每条支路求功率,再比较超前和滞后支路的功率差,这样可以摆脱对载波相位的依赖,即使PLL还没锁定,码环也能正常工作。

% 非相干超前减滞后功率鉴别器,输出单位是码片 E_power = IE^2 + QE^2; L_power = IL^2 + QL^2; if (E_power + L_power) > 1e-10 dll_err = 0.5 * (E_power - L_power) / (E_power + L_power); else dll_err = 0; end

0.5这个系数是把归一化功率差换算成码片误差。E_power大于L_power说明本地码相位偏后,码NCO需要加快;反之减慢。这种鉴别器在码相位误差超过1个码片时会进入非线性区,所以捕获阶段要把码相位误差压到0.5个码片以内再启动跟踪。

3.2 二阶DLL环路滤波器的离散化实现

码环用二阶DLL足够。码相位不像载波相位那样受到高动态加速度的直接影响,它的动态主要由卫星和接收机的相对运动通过多普勒效应映射而来,映射比例又很小,所以码环带宽可以比载波环低一个数量级。二阶DLL环路滤波器结构与二阶PLL类似,只是输入是码片误差,输出是码率修正量。

% 二阶DLL环路滤波器 ze = 0.7071; w0_dll = DLL_Bn / ((ze + 1/(4*ze))/2); % DLL_Bn取0.5~1.0Hz Kp_dll = 2*ze*w0_dll; Ki_dll = w0_dll^2; % dll_state是码相位误差的积分状态 dll_state = dll_state + dll_err * T; code_nco = chip_rate + Kp_dll*dll_err + Ki_dll*dll_state;

chip_rate是B1I的标称码率2.046Mcps。DLL带宽设窄后,热噪声对码相位的扰动小,但动态响应慢。如果接收机装在车上,DLL带宽建议放到1Hz以上;静态基带调试可以先用0.5Hz。

3.3 B1I载波辅助的比例关系和代码实验

载波辅助的核心思想是:码率也会被多普勒效应拉伸或压缩,而这个多普勒可以通过载波NCO已经估计出来的频率差换算得到。载波多普勒和码多普勒的比例等于载波频率与码率之比。B1I的载波频率是1561.098MHz,码率2.046Mcps,比例约为763.05。也就是说,1kHz的载波多普勒对应约1.31Hz的码率变化。

% 载波辅助码环示例 doppler = nco_freq - IF; % nco_freq是当前载波NCO频率,IF是中频 code_aux = chip_rate * (1 + doppler / carrier_freq); % 把DLL的修正量叠加到载波辅助的码率上 code_nco = code_aux + Kp_dll*dll_err + Ki_dll*dll_state;

参数说明:IF是信号模拟或采集时设定的中频频率。如果接收信号直接是零中频,IF就是0。doppler去掉中频后得到的是真正的多普勒频率偏移。code_aux里面已经把码率多普勒补偿掉了,DLL环路滤波器只需要处理残余误差。这样做的好处是:在高动态场景下,码环不需要靠增大带宽去追码率变化,环路带宽可以维持0.5Hz左右,码相位噪声自然小。载波辅助增益默认取1,如果PLL还没锁定,载波NCO频率本身波动大,此时把辅助项乘一个0.7~0.9的系数会更安全。

4. 用MATLAB把载波环和码环拼成北斗B1I跟踪主循环

有了载波环和码环的独立实现,下一步是把它们放进同一个1ms循环里。MATLAB里做这件事的关键是先把数据分段,每段长度等于采样率乘以积分时间T。比如fs=16.369MHz时,T=1ms对应16369个采样点。主循环里每个循环处理一个分段,依次完成载波剥离、码相关、鉴别器、环路滤波器、NCO相位累加。

4.1 启动参数和数据分段约定

以下参数是北斗B1I跟踪程序的标准起点。载波环和码环带宽先按静态场景设置,动态测试再调。

参数取值说明
fs16.369MHz采样率,B1I码率的8倍附近较常见
IF3.996MHz中频,取决于前端射频配置
chip_rate2.046McpsB1I伪码码率
carrier_freq1561.098MHzB1I载波频率
T0.001s相干积分时间
block_lenround(fs*T)每毫秒采样点数
DLL_Bn0.5Hz码环噪声带宽
PLL_Bn15Hz载波相位环噪声带宽
code_spacing0.5码片超前滞后相关器间距

注意block_len必须是整数,MATLAB里用round处理。采样率如果不是码率的整数倍,码相位累加时也要用真实码率除采样率,不能直接按整数码片计数。

4.2 主循环里的载波与码更新顺序

主循环的顺序是:先更新载波NCO相位,生成本地载波并剥离载波;再用码NCO生成超前、即时、滞后码,做相关累加;最后根据IP、QP和IE、QE、IL、QL更新环路。

% 跟踪主循环骨架 for k = 1:num_blocks seg = data((k-1)*block_len+1 : k*block_len); % 1. 载波NCO,生成本地载波 t = (0:block_len-1) / fs; lo = exp(1j * (2*pi * nco_freq * t + carrier_phase)); baseband = seg .* conj(lo)'; carrier_phase = wrapTo2Pi(carrier_phase + 2*pi*nco_freq*T); % 2. 码相关,返回超前/即时/滞后相关值 % 这里省略码相位内插实现,接口如下 [IE, QE, IP, QP, IL, QL] = correlate_code(baseband, code_phase, code_nco, fs, code_spacing); % 3. 鉴别器 [f_err, p_err] = carrier_discriminators(IP, QP, IP_prev, QP_prev, T); E_power = IE^2 + QE^2; L_power = IL^2 + QL^2; dll_err = 0.5 * (E_power - L_power) / (E_power + L_power + 1e-10); % 4. 三阶PLL状态更新 w0_pll = PLL_Bn / 1.2; p_state1 = p_state1 + p_err * T; p_state2 = p_state2 + p_state1 * T; freq_adj = 1.1*w0_pll*p_err + 1.75*w0_pll^2*p_state1 + 3.15*w0_pll^3*p_state2; % 5. FLL辅助PLL if lock_state < 2 nco_freq = nco_freq + f_err * 0.6 * (2 - lock_state); end nco_freq = nco_freq + freq_adj / (2*pi); % 6. 码环载波辅助 doppler = nco_freq - IF; code_aux = chip_rate * (1 + doppler / carrier_freq); w0_dll = DLL_Bn / ((0.7071 + 1/(4*0.7071))/2); dll_state = dll_state + dll_err * T; code_nco = code_aux + 2*0.7071*w0_dll*dll_err + w0_dll^2*dll_state; % 7. 保存上一毫秒状态 IP_prev = IP; QP_prev = QP; end

逻辑说明:第1步先把接收信号与本地载波的共轭相乘,把信号从IF搬移到基带。第2步的correlate_code内部会按照code_phase和code_nco生成超前、即时、滞后三路码,并用积分清零器做1ms累加。第5步和第6步共用同一个nco_freq,因此载波环的每次频率修正都会影响码率辅助量,形成“载波辅助码环”的闭环。

参数说明:第4步里的1.1、1.75、3.15是三阶PLL标准系数,不要随意改,除非你从环路传函重新推导。FLL辅助增益0.6是牵引阶段的起点,锁定后自动降到0。DLL带宽DLL_Bn取0.5Hz时,动态响应会比较慢,如果可以看到码相位误差出现周期性摆动,先把带宽调到1Hz再排查是不是载波辅助增益太高。

4.3 启动阶段和锁定状态的判定

跟踪启动时,载波NCO初始频率来自捕获模块的频率估计。捕获频率误差在几百赫兹内时,FLL可以负责把残差拉进PLL的捕获带。lock_state的判定不能只看一两个毫秒的误差值,因为多径和噪声会造成偶然抖动。常见做法是维护一个50ms的滑动窗,统计f_err的标准差。

% 启动阶段的状态推进 if lock_state == 0 if mean(abs(f_err_valid)) < 20 % 近50ms平均频率误差小于20Hz lock_state = 1; end elseif lock_state == 1 if std(p_err_valid) < 0.1 % 近50ms相位误差标准差小于0.1rad lock_state = 2; end end

这里f_err_valid和p_err_valid是最近50个毫秒的误差存储。lock_state到2后,FLL辅助项会逐渐退出,三阶PLL独立锁定。此时如果相位误差标准差持续超过0.3rad,说明PLL带宽太窄或动态过大,需要先调带宽再继续解码。

5. 用跟踪结果反推参数是否合理:C/N0与载波辅助调整技巧

跟踪程序写完后,最需要验证的不是代码跑不跑得通,而是环路有没有真正锁住。判断依据主要有三件事:C/N0是否达到合理区间、IP/QP星座是否稳定、码NCO和载波NCO修正量是否收敛。这三个指标比单纯看某些中间变量更直接。

5.1 用IP/QP的统计量估计C/N0

C/N0是载波功率与噪声功率谱密度之比,单位是dB-Hz。MATLAB里最常见的估算方法是窄带宽带功率比法。这里的宽带功率是每个毫秒的IP^2+QP^2,窄带功率是先把1ms复数值做滑动平均再求功率。

WBP = IP.^2 + QP.^2; % 宽带功率,向量,历史上1秒长度 NBP = abs(mean(IP + 1j*QP)).^2; % 1秒窄带功率,1024点平均 CN0 = 10*log10( NBP / mean(WBP) / T ); % 单位dB-Hz

北斗B1I正常跟踪时,室外天线的C/N0在40到50dB-Hz之间。如果C/N0低于35且直流偏置稳定,多半是天线增益或射频链路问题;如果C/N0很高但相位误差标准差仍偏大,要考虑多径或环路边带干扰。

5.2 动态应力下的带宽联动

FLL、PLL、DLL三个带宽不是独立变量。载波环的PLL带宽决定载波相位跟踪精度,也决定载波辅助量的噪声。PLL带宽加宽10Hz,载波辅助量里的噪声就更大,码环相当于接收了一个高温噪声源。此时如果DLL带宽仍然压得很低,码环反而会被辅助量拖出偏差。所以调参时口诀是:PLL带宽加大,DLL带宽也要适度加大,或者降低载波辅助增益。载波辅助增益降到0.8以下后,DLL需要靠自身滤波器承担更多动态应力,DLL_Bn就要在原来基础上提高30%左右。

5.3 三个可以立刻试的调参动作

第一,启动牵引时把FLL辅助增益从0.6改成0.3,看C/N0是否反而变高。如果变高,说明初始频率误差其实很小,FLL辅助只是在注入噪声。第二,把DLL带宽从0.5Hz改到1Hz,观察码环误差的收敛速度。静态场景下两者都应收敛到同一个码相位均值,但收敛时间和相位抖动会有差异。第三,把载波辅助项中的doppler做一个低通滤波再送入code_aux,滤波时间常数为20ms,能明显减小PLL高频抖动对码NCO的影响。这个技巧在动态不高的场合最有效,实现时只需多一个状态变量即可。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询