1. COSTAS环的锁定能力边界残余频偏的物理来源1.1 抑制载波调制的载波同步为什么必须上COSTAS环做过QPSK/16QAM解调器的同学应该都有这个经验明明COSTAS环已经锁定星座图看起来也收敛了但误码率就是比理论曲线差那么零点几个dB甚至星座点还在以极低速率旋转。这个问题背后往往就是COSTAS环的残余频偏偏差在作祟。我最近完成的一个基于COSTAS环的载波同步与残余频偏补偿方案从MATLAB仿真一路做到FPGA板级验证把这个问题从原理到实现完整走了一遍。这篇博文就围绕这个项目讲讲残余频偏是怎么产生的、如何在仿真里量化它以及最后在FPGA上用什么结构把它补掉。先明确一点在抑制载波的M-PSK/QAM系统里接收端必须自己恢复载波因为发射端为了节省功率不会发送一个单独的载波分量。COSTAS环本质上是同相-正交双路联合工作的锁相环它用鉴相器从解调后的I/Q数据里提取相位误差再用环路滤波器驱动NCO数控振荡器去消除这个误差。相比普通PLL需要平方或四次方先恢复载波分量COSTAS环直接工作在基带符号上结构紧凑而且高信噪比下等效噪声带宽可以做得比较窄所以几乎成了QPSK解调器的默认选择。但COSTAS环不是万能钥匙。它的捕获能力、跟踪精度都依赖环路阶数和噪声带宽。很多项目里我们把COSTAS环跑通之后就不再看它的输出了认为锁定了就万事大吉。恰恰是这个“锁定”状态掩盖了残余频偏的累积效应。1.2 二阶环的跟踪能力与稳态误差分析从自动控制角度COSTAS环可以抽象成一个二阶锁相环。二阶环对频率阶跃的稳态响应是相位误差有界但频率误差可以趋近于零——这么说来环路在理论上应该能把残余频偏完全消除。问题在于理论分析里的“稳态”有两个隐含前提一是环路增益无穷大二是环路滤波器积分支路没有量化误差。实际FPGA实现里环路滤波器的积分器是一个有限位宽的累加器比例-积分系数C1、C2也要用定点数表达。当残余频偏折算到NCO的相位增量只有几个LSB时累加器每次只加1或2个最小位NCO输出的频率就无法精确对准信号的实际频偏最后会形成一个几赫兹到几十赫兹的慢旋转。这个频率很小示波器上肉眼看星座图甚至觉得是静止的但放到几十万个符号里相位已经跑掉不知道多少圈了。另一个经常被忽略的来源是捕获过程。如果输入频偏超过了环路快捕带COSTAS环可能暂时“锁”到奇点附近比如QPSK鉴相特性在错误相位点产生的假锁定。环路最终可能跳入正轨也可能一直停留在靠近边缘的工作点此时NCO中心频率和真实载波之间就留着一个固定频率差。尤其是在突发通信里接收机要在一段很短的前导码时间内完成载波同步频偏大、前导短环路来不及完全收敛解调数据就已经开始了这就相当于把残余频偏直接带入了后续的均衡和解调。顺便说一句COSTAS环对相位噪声和信噪比也很敏感。低信噪比下鉴相器输出的误差信号被噪声污染环路滤波器输出的频率控制量一直在抖动。这个抖动的均值是零但如果后面接的是判决器的硬门限抖动瞬时值大到一定程度就会导致符号相位跨出正确判决区域产生突发错误。很多人把这类错误归咎于信道其实根源是残余频偏搭配低信噪比这两者叠加后让相位裕量进一步缩小。1.3 这个偏差会造成什么实际影响残余频偏对解调性能的伤害主要体现在三个方面。第一星座点旋转。即便频偏只有符号速率的万分之一在1000个符号后累积相位误差也有36度QPSK的判决区域只有90度宽这已经接近误判边缘。如果符号速率本身低比如1Msps下100Hz的残余频偏星座图就会以肉眼可见的速度旋转根本没法判决。第二判决反馈错误。COSTAS环通常是判决反馈结构环路先做一个临时判决再根据判决结果产生误差信号。如果星座因为残余频偏旋转到跨过判决边界临时判决就是错的错误会被反馈进鉴相器环路可能在错误方向上调整极端情况下会产生“滑周”现象导致整个载波同步失锁。第三影响均衡器收敛。对于一个带均衡的接收机COSTAS环是一个粗同步均衡器负责消除符号间干扰但它对相位旋转的跟踪能力有限。尤其是线性均衡器它靠自适应系数去补偿信道响应而残余频偏是一种随时间累积的相位旋转不满足信道响应的平稳性假设。我实测过在16QAM系统里残余频偏只要大于符号速率的万分之一均衡器就明显出现误码平台再增加均衡器抽头也没用。所以在做到FPGA之前我先把这些问题放进MATLAB里量化了一遍。2. MATLAB仿真链路搭建用误差曲线定位环路短板2.1 发射端、信道与接收端的模型化设计MATLAB仿真的目的不是“复现一个COSTAS环”而是把COSTAS环在特定频偏下的行为完整测一遍。我建议分三步搭链路不要一上来就拖动工具箱里的器件模块。第一步发射端生成随机QPSK符号然后用成形滤波器输出调制波形。这里要明确符号速率R_s和过采样倍数N_sps这两个参数直接决定了环路噪声带宽的归一化。第二步在基带信号上叠加一个固定频偏Δf和AWGN噪声。我习惯把频偏折合成“归一化频偏”Δf·T_s其中T_s是符号周期这样仿真结果可以直接迁移到不同符号速率下。第三步才是COSTAS环接收机包含一个NCO、一对I/Q混频器、匹配滤波器、定时采样和鉴相器。COSTAS环内部的鉴相器有很多种。我第一次尝试的是经典乘法鉴相器I*Q这种鉴相器在QPSK下每四分之一个符号周期都耦合了信号幅度容易受AM噪声影响。后来项目里改成了判决辅助鉴相器误差表达式可以写成e(n) sign(I(n)) * Q(n) - sign(Q(n)) * I(n)这个公式在QPSK里等价于求相位误差的正弦值并且对幅度做了归一化工程上比I*Q乘法鉴相稳定得多。仿真里两种鉴相器都跑一下你会发现相同噪声带宽下判决辅助鉴相器能允许的初始频偏范围更大这就是后面FPGA实现选它的原因。2.2 环路滤波器参数整定的工程公式COSTAS环的数字环路滤波器通常是一个比例-积分PI结构滤波器的输出同时包含误差的比例项和积分项。设符号周期为T_s鉴相器增益为K_pNCO增益为K_0取环路自然角频率为ω_n、阻尼系数为ζ那么离散PI系数可以近似写成C1 (2 * ζ * ω_n * T_s) / (K_p * K_0) C2 (ω_n^2 * T_s^2) / (K_p * K_0)对应的等效单边噪声带宽B_L约为B_L ω_n * (ζ 1/(4ζ)) / 2这里不要直接套用连续时间PLL的公式因为符号率采样会引入一个符号周期的延迟实际仿真下来把C1系数比理论值再降10%~20%能明显减少环路振荡。我的一般做法是先根据符号速率和最大可容忍频偏设定B_L通常取B_L·T_s在0.01到0.05之间。然后选ζ0.707再反推出ω_n和C1、C2。拿一个典型参数举例QPSK符号速率1Msps归一化频偏1e-4信噪比20dB。取B_L5kHzζ0.707算得ω_n约等于13.3krad/sC1和C2在K_p1、K_01时分别是0.0188和8.9e-6。这个搭配在MATLAB里跑出来锁定时间大约是200个符号稳定后残余频偏在100Hz量级。如果想把残余频偏压到10Hz以下需要把B_L降到1kHz但锁定时间会拉长到几千个符号。这就是前导设计要做的取舍。2.3 仿真判据锁定时间、稳态误差与星座散布仿真不是为了看“最后星座图漂亮不漂亮”而是要量化三个指标。第一锁定时间。观察NCO输出频率和真实频偏之差定义误差进入±5%范围内并且不再出去的时间点为锁定时间。不同B_L下锁定时间差异很大这个指标直接决定前导码长度。第二稳态频率误差。锁定后取NCO频率控制量的平均值减去真实频偏得到的就是残余频偏。在浮点仿真里二阶环的稳态频率误差理论上应该是零但实际上由于环路滤波器积分支路的数值截断仍然会有10Hz~100Hz的偏差。把相位累加器改为double类型也一样会看到极小偏差这部分是数字环路固有属性。第三星座散布。把锁定后的符号画在同一张图计算每个星座云团相对于理想点的半径方差。你会发现即使环路锁定星座云团也不是一个圆点而是在径向上有一个窄条切向上有一个更窄的条。切向方差对应的就是残余频偏和相位噪声的累积。我还会额外画一条“相位误差时间序列”也就是每个符号的相位误差取出来看它是否有缓慢的单向漂移。如果看到类似锯齿波的曲线说明环路确实没能完全消化频偏。这些仿真做完结论已经很清楚了单靠COSTAS环无法在同时满足“快速锁定”和“低残余频偏”这两个要求。前导短的突发系统必须牺牲捕获带宽代价就是残余频偏变高反过来强制压低环路带宽又会拉长捕获时间。单独调环路参数不是出路需要在COSTAS环后面再加一级专门补偿残余频偏的模块。3. 残余频偏补偿的工程化设计估计、校准与回退3.1 判决辅助跨符号频偏估计的基本原理残余频偏补偿通常采用“先估计、再校正”的结构而不是让COSTAS环自己去吃掉所有频偏。估计残余频偏最实用的方法是判决辅助跨符号相位差分。思路是这样的COSTAS环输出已经完成粗同步星座点基本位于正确象限附近只不过还存在一个很小的频率旋转。对每个符号将它除以它的判决值或者乘以判决值的共轭就得到去调制后的残余相位误差序列。这个序列包含两项一个是不随符号变化的常数相位差另一个是随符号线性增长的频率分量。如果把相隔L个符号的误差值共轭相乘并累加求平均常数相位差会被消掉剩下的就是L个符号内累积的相位旋转除以L就得到残余频偏估计。写成离散形式\hat{f}r \frac{1}{2\pi L T_s} \cdot \arg\left( \sum{n} z_{n} \cdot z_{nL}^{*} \right)其中z_n是去调制后的符号arg()取幅角T_s是符号周期。L不能太大否则相位旋转会超出±π范围导致模糊也不能太小太小则抗噪声能力差。对QPSKL一般取16~64个符号就能在20dB信噪比下把残余频偏估计到1Hz以下。仿真时我对比了直接对接收符号四次方再FFT的估计方法。四次方方法不需要判决抗盲估计能力强但计算量大、频率分辨率受FFT点数限制适合捕获阶段的大频偏粗估计。跨符号差分方法依赖判决结果但COSTAS环已经锁定判决错误率已经在1e-2以下所以估计精度高很多适合作为细补偿。3.2 前馈补偿与环路回退的交互策略频偏补偿结构上有两种选择一种是把估计出的频偏量直接反馈给COSTAS环的NCO修正其频率控制字另一种是在COSTAS环后面加一个复数旋转器用独立的旋转频率去抵消残余频偏。我个人推荐先用前馈补偿不要动COSTAS环本身的环路参数。原因是COSTAS环的环路响应已经根据捕获性能设计好了你在它内部再插入一个频率修正支路等于给环路增加了一个延迟环节极容易导致环路稳定性下降。前馈旋转器则完全独立不参与环路反馈不影响COSTAS环的相位裕度。工程实现里常见的结构是Costas环输出数据送入一个滑动窗口窗口内做跨符号差分频偏估计得到f_est然后生成一个频率控制字给NCONCO输出两路正交的cos/sin序列。前馈补偿后的符号再用一个复数乘法器完成旋转。这里的NCO可以和COSTAS环内部NCO完全独立也可以共用一个相位累加器的不同初始相位关键是保证旋转频率恒定且不与COSTAS环NCO耦合。还有一个容易被忽略的点估计出的残余频偏需要先通过一个低通滤波器再做校正因为单次估计结果里有大量噪声直接旋转会额外引入相位抖动。低通滤波器时间常数可以设置成几百个符号周期与COSTAS环的环路带宽匹配。这里的设计原则是“慢跟踪、稳补偿”追求的是把残余频偏慢慢收干净而不是追求瞬时响应。3.3 MATLAB验证补偿效果的量化对比补偿算法在MATLAB里验证时我习惯做三组对比实验第一组只有COSTAS环没有补偿第二组是COSTAS环加理想精确频偏补偿直接用真实频偏做校正作为性能上界第三组是COSTAS环加本文的跨符号差分估计补偿。三组都在相同信噪比和频偏条件下跑足够多的符号然后统计误码率或者误差矢量幅度。实测下来在1Msps符号速率、100Hz残余频偏、20dB信噪比条件下不补偿的QPSK系统误码率大约在1e-3量级理想精确补偿可以压到接近理论值1e-5实际跨符号差分补偿也能达到2e-5左右比理想上界差0.2dB左右。这个差距主要来自频偏估计误差和低通滤波带来的残余相位抖动。星座图上的对比更直观。补偿前星座点呈圆环状旋转四个象限云团呈弧形拖尾补偿后云团被压缩成接近圆形的点簇弧形拖尾消失。注意观测的时候要把符号数拉长到10万个以上才能看到微弱的慢旋转是否真的被消除只看几千个符号很容易误判。4. FPGA落地的定点与时序优化要点4.1 定点化方案从浮点到定点哪些量必须重标定MATLAB仿真跑通后真正麻烦的是把浮点模型改写成FPGA可实现的定点逻辑。COSTAS环内部最容易出问题的是三个量环路滤波器系数C1、C2NCO的频率控制字以及积分累加器。C1和C2在数值上都很小。以之前的参数为例C2大概在1e-6量级如果用16位定点数直接表示最低有效位对应的数值精度根本不够。我的做法是把环路滤波器所有系数整体放大2^N倍用Q格式表示最后在累加结果输出到NCO之前再截位调整。具体N的取值要根据环路增益和噪声带宽来确定一般从Q24开始试如果星座图出现极限环振荡就把N调大。NCO频率控制字位宽决定频率分辨率。对1Msps符号速率、10Hz残余频偏目标NCO累加器至少需要25位位宽因为2^25 / 1e6 ≈ 33也就是一个LSB对应约30Hz。想覆盖到1Hz分辨率就得30位以上。Xilinx的NCO IP核默认支持32位相位累加器输出截位到16位查表我用下来资源开销不大直接选32位最省事。积分累加器是另一个容易溢出或精度不足的点。COSTAS环锁定后积分支路维持一个接近常数的频率控制量这个数值可能很小。如果积分器位数不够量化台阶会让输出频率在相邻两个整数之间跳动星座图会出现周期性抖动。建议积分器位宽和NCO相位累加器一致至少32位并且做饱和处理防止捕获过程中误差太大导致溢出。4.2 CORDIC与查找表的选择旋转器硬件实现细节前馈补偿需要用NCO产生正交本振并与I/Q数据做复数乘法。NCO本身在FPGA里一般用IP核内部是相位累加器加正弦/余弦查找表。复数旋转则需要4个乘法器I_out Icos - QsinQ_out Isin Qcos。如果系统时钟是符号率的2倍以上可以通过时分复用一个复数乘法器来减少资源但考虑到时序和代码复杂度直接例化4个DSP48也不贵。我用的是Xilinx 7系列4个18x25乘法器DSP48数量完全够用。另一种方案是用CORDIC IP核做坐标旋转但CORDIC是迭代结构延迟周期数不确定而且每级迭代会带来截位误差。单纯做频偏补偿时不推荐CORDIC因为相位增量是已知的直接查表加乘法器的开销比CORDIC更可控。CORDIC更适合用在需要实时计算反正切值的鉴相环节。NCO查找表深度方面16位相位截位256点正弦表就能达到SFDR约70dBc对QPSK足够。16QAM要求高一些可以把查找表加深到1024点。注意查找表输出必须做符号位扩展和归一化不然cos/sin幅度不匹配会导致旋转后星座幅度变化。4.3 时序收敛、资源共享与流水线设计把COSTAS环和补偿模块放在同一个工程里容易出现两个时序问题一是乘法器链路过长组合逻辑延迟过大二是跨时钟域处理不当。复数乘法器级联后从I/Q输入到旋转输出经过两级乘法加一级加法在200MHz时钟下勉强能收敛但在更高时钟频率下最好在乘法器之间插入流水线寄存器。补偿模块工作在符号率本身不要求太高的吞吐所以插入两级流水线完全不损失有效数据率。另一个隐蔽问题是估计模块的滑动窗口。跨符号差分估计需要缓存L个符号的I/Q数据如果L取64用移位寄存器缓存I和Q两组64x16bit资源不大但如果同时还需要低通滤波滤波循环里存在反馈累加就要把累加操作放到同一个时钟周期的组合逻辑里否则时序不收敛。解决方法是把低通滤波拆成两级第一级做乘加第二级做累加中间用流水线寄存器切开。资源占用方面我最终在XC7A100T FPGA上实现了这个链路主要模块消耗情况如下模块DSP48BRAM(18Kb)FF/LUTCOSTAS环含NCO40约1200前馈旋转器含NCO40约800频偏估计与低通21约600整体开销101约2600放在资源富余的芯片上绰绰有余但要注意如果目标芯片是低成本小逻辑量型号可以把COSTAS环NCO和前馈NCO合并复用一个相位累加器只是需要额外处理两个独立的初始相位。这种共享策略省一个BRAM但会让代码可读性变差不推荐在项目前期图省事。5. 板级调试复盘仿真里看不出来的三个问题5.1 信号源相位噪声与观测时刻的选择板级调试的第一步是用信号源产生一个带有已知频偏的QPSK信号直接灌进FPGA的ADC前端。我遇到的最初问题是信号源本身有相位噪声这个噪声经过下变频后会和残余频偏混在一起导致COSTAS环的输出星座图始终有轻微旋转。我开始以为是补偿模块没生效后来用频谱仪看信号源的载波纯度才知道是信号源的问题。这种场景下不能用信号源自带的本振来评估残余频偏补偿精度因为信号源的相位噪声远远大于FPGA内部NCO的量化误差。建议在调试时把信号源频偏设得大一点比如1kHz然后看补偿后的残余频偏是否降到几十赫兹以内。要验证更精细的补偿效果必须在MATLAB里用理想合成信号产生基带数据再通过DAC或逻辑分析仪回灌给FPGA这样才能把信号源相位噪声排除在指标之外。5.2 位定时偏差对残余频偏测量的影响第二个坑是位定时同步和载波同步的耦合。如果接收机还没有建立符号定时同步采样点不在最佳眼图中心COSTAS环鉴相器输出的误差信号会包含符号间干扰表现出来就是星座点变宽残余频偏估计值的方差变大。我们一开始先在FPGA里用过采样直接抓数据跑COSTAS环发现频偏估计结果上下跳动了将近20Hz怎么调环路参数都压不下去。后来先加了一个Gardner符号同步环把采样时刻对准到最佳点频偏估计值的抖动立刻降到了2Hz以内。所以建议调试顺序一定是先做符号同步再做载波同步最后做残余频偏补偿。如果颠倒顺序载波同步会看到一个被ISI污染的误差信号环路的锁定点不再是星座中心而是一个被扭曲的工作点。这一点在仿真里很容易被忽略因为MATLAB模型通常假设采样时刻是理想的。5.3 一个关于调试状态寄存器的个人建议最后分享一个纯工程经验在FPGA里给COSTAS环和补偿模块加一组调试状态寄存器把NCO频率控制字、频偏估计值、低通滤波器输出、补偿旋转器的当前相位都暴露出来。不需要额外占用太多逻辑但调试效率能提升一大截。我项目里设计了一个基于AXI-Lite的寄存器组上位机通过串口或者USB可以实时读取这些状态量。调试时先看COSTAS环的NCO频率控制字是否稳定稳定后再看补偿模块的频偏估计值是否接近预期最后看旋转器相位是否随时间线性增长。这一步能把“星座图看起来还行”但“误码率不达标”之类的问题快速定位到具体模块比拿逻辑分析仪去抓引脚信号省太多时间。板级调试还遇到过一个小问题COSTAS环的初始相位模糊在QPSK里是90度整数倍临时判决辅助鉴相器很容易锁在任意一个模糊点上。这个不是残余频偏问题但会干扰频偏估计因为去调制时除以一个错误判决值会引入一个固定的相位偏置。解决办法是采用差分编码或者在帧头加入已知序列来消除相位模糊。如果项目里已经用了绝对相位编码那补偿模块前面一定要加一个相位去模糊校正否则整个链路看起来锁定了但数据全是错的。
