ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

FPGA双通道中频信号数字下变频与相位差估计实现详解

FPGA双通道中频信号数字下变频与相位差估计实现详解 接手这个项目的时候我的第一反应是链路不算特别复杂——双通道中频信号进来数字下变频DDC以后做相位差估计听起来就是“采样、混频、滤波、反正切”四步。但真正把工程做细之后才发现这套东西想要稳定、高精度地跑起来水相当深。这类需求常见于干涉仪测向、阵列测向、无源定位、雷达或通信的多通道相位比较场景。简单说就是两个接收通道输出中频信号FPGA负责把中频信号搬移到基带再通过I/Q数据估计两路之间的相位差。相位差的精度直接决定了后续测向或定位的精度所以整个链路的每一个环节都不能糊弄。这篇文章我以一套实测过的双通道中频信号数字下变频及相位差估计方案为主线采用FPGA实现中频70MHz、采样率100Msps把工程思路、模块设计、参数计算、仿真调试完整拆开讲适合正在做接收机、测向设备以及FPGA信号处理的朋友参考。1. 方案定调数字下变频与相位差估计的整体链路设计1.1 双通道中频信号和相位差估计到底在解决什么问题在很多接收设备里射频信号并不会有直接被ADC采样而是经过射频前端先下变频到一个固定的中频比如70MHz、140MHz然后以中频形式进入数字处理板。之所以采用中频方案是因为直接在射频段采样对ADC速率和前端动态范围要求太高工程上不划算。而对FPGA来说处理一个几十到几百兆赫兹的中频信号就从容得多了。双通道的含义也非常明确设备里有两个接收通道。可能是两天线干涉仪的两个通道也可能是相控阵两个阵元的输出。无论哪种场景下游都需要比较这两个通道输出信号之间的相位关系。以干涉仪测向为例两天线间距为d信号到达角为θ波长λ与相位差Δφ的关系是Δφ 2π · d · sinθ / λ也就是说只要估计出相位差Δφ就能反推到达角θ。相位差1°的误差在d0.5λ的布局下大约对应0.5°左右的角度误差具体取决于阵元间距。所以相位差估计的精度是直接影响系统核心指标的关键项。这里需要强调一个工程前提两个通道的采样必须严格同步。如果两路ADC不是由同一个采样时钟驱动它们之间的相对相位会随时间抖动后级估计得再好都没有意义。因此项目一开始就要把“双通道同步采样”当成强制约束不能留半点妥协空间。1.2 为什么优先选数字下变频而不是模拟混频下变频早期接收机普遍用模拟正交混频方案也就是两路本振正交信号与中频信号混频得到I/Q基带后再进ADC。模拟方案的缺点非常明显本振两路幅度和相位很难做到完全一致I/Q通道之间存在增益失配和正交误差而且温漂会改变失配程度直接污染相位差估计结果。数字下变频的思路完全不同。ADC直接采样中频信号在FPGA内部用数字的方式产生正交本振再与采样数据相乘完成混频。这样做有三个实打实的优势数字本振幅度天然一致I/Q正交性只取决于相位累加器位宽和查找表精度远优于模拟方案。两通道可以共享同一个相位累加器或使用完全同源的NCO配置通道间一致性极好。频率字可随时修改一套硬件适应不同中频灵活度大幅提升。当然数字下变频对ADC性能提出了要求。好在现代ADC在中频直接采样方面的能力已经足够成熟100Msps到250Msps的器件非常多覆盖几十到一百多MHz中频完全不是问题。对FPGA工程师来说DDC链路本身不算重负载关键是每个模块的参数都要算明白。1.3 顶层架构从ADC采样到相位差数值输出整个系统的顶层数据流向是这样的双通道ADC以100Msps对70MHz中频信号采样输出16位并行数据进入FPGAFPGA内部先做两个完全对称的DDC通道每个通道包括NCO混频、CIC抽取滤波、FIR补偿整形DDC输出低速基带I/Q接着用CORDIC把I/Q转为相位最后对两路相位做差、解模糊、平均输出相位差结果。这里有一个设计要点两个DDC通道之间要做到“严格对称”。所谓对称不只是代码结构对称还包括混频器、CIC、FIR的流水线级数一致、截位策略一致。如果两路处理延迟不同哪怕只差一个时钟周期也会给相位差带来固定偏差而且这种偏差很难在后期通过滤波去掉。整个工程我用的是Xilinx平台Vivado里直接使用DDS Compiler、CIC Compiler、FIR Compiler和CORDIC这些IP核开发效率非常高。同样的思路搬到Altera Quartus或高云平台也没有障碍IP核的端口大同小异核心参数计算逻辑完全一致。2. 数字下变频链路拆解NCO、混频与抽取滤波的工程取舍2.1 数控振荡器NCO频率字计算与四种实现路径数字下变频的第一步是产生本振信号。数控振荡器在FPGA里一般就是一个相位累加器加上一个正余弦查找表。假设相位累加器位宽为N采样率为Fs想产生的本振频率为f0那么频率控制字FTW的计算公式是FTW round(f0 · 2^N / Fs)本项目NCO位宽取32位Fs100MHzf070MHz代入计算FTW round(70e6 · 2^32 / 100e6) round(0.7 · 4294967296) ≈ 3006477107这个数换算成十六进制正好是0xB3333333。在实际代码里直接给这个频率字即可。32位相位累加器的频率分辨率是Fs / 2^32 ≈ 0.023Hz对于70MHz本振来说绰绰有余。实现NCO有四种常见路径我按工程推荐顺序排一下直接使用Xilinx DDS Compiler或Altera NCO IP核。最省事输出正弦和余弦两路信号相位累加器位宽可配置SFDR无杂散动态范围通常能做到100dB以上。自己写相位累加器加LUT查找表。适合想控制资源或避免IP授权场景查找表深度用2^10或2^12足够配合象限压缩可以省一半存储。当f0恰好等于Fs/4时本振序列退化为1, 0, -1, 0的循环。这时混频器变成简单的符号切换和数据选通连乘法器都不用资源最省。用CORDIC IP生成正余弦适合只需要极窄带信号且已有CORDIC模块可复用的场景但输出速率有限一般不作为首选。本项目因为中频70MHz不等于Fs/4某个特殊关系所以直接采用DDS Compiler IP输出两路正交本振一路正弦一路余弦相位累加器32位正余弦输出位宽16位。混频器用DSP48实现两个通道共需要4个乘法器每通道I/Q各一个这在Artix-7级别器件上完全不是压力。2.2 CIC抽取滤波器参数到底怎么定、位宽怎么涨混频之后的数据率仍然是100Msps信号频率搬到了基带附近但带宽外的镜像分量和噪声还在。如果直接用FIR滤波到目标速率FIR阶数会高得离谱。所以在高速端先用CIC滤波器完成第一级抽取把数据率降下来是DDC链路的标准做法。CIC滤波器最大的特点是结构里只有加法器和延迟器没有乘法器天然适合高抽取率场景。它有三个关键参数抽取率R、差分延迟M、级数N。工程上常用的搭配是R取8到16M取1或2N取3到5。本项目信号带宽约2MHz输入采样率100Msps我选择R8、M1、N4。抽取后数据率变为12.5Msps信号带宽仍占2MHz频谱利用率合适CIC阻带抑制也足够。CIC有一个必须注意的问题增益会随着抽取率和级数成指数增长。输出位宽计算公式是B_out B_in N · ceil(log2(R · M))代入本项目的参数输入16位R8M1N4那么log2(8)3扩展位数为4·312位输出位宽28位。如果截位不当很容易出现溢出或者正弦波顶部被削平相位估计自然就不准了。这里分享一个我踩过的坑CIC截位不能简单粗暴地只取高16位那样会丢太多低位精度导致输出波形虽然“看起来正常”但相位噪声明显偏大。比较稳妥的做法是从28位结果里取出高18位或20位保留符号位并在CIC IP核中开启适当的dither选项。虽然多消耗几个LUT但对后续相位估计精度的帮助非常显著。2.3 FIR补偿滤波与整条DDC的级联配合CIC输出一个12.5Msps、相对粗糙的基带IQ信号但CIC通带内存在sin(x)/x形状的幅度跌落。按本项目参数在2MHz通带边缘跌落大约0.8到1.2dB。这个跌落如果不补偿会对两路信号的幅度带来轻微影响更重要的是会改变信号在不同频点上的权重间接压低相位估计的稳定性。因此必须在CIC后面接一级FIR滤波器完成三件事补偿CIC的幅度跌落、滤除带外噪声和残余镜像分量、把数据整形到合适的输出位宽。FIR设计我习惯先在MATLAB里做。用Filter Designer目标通带2MHz过渡带从2MHz到4MHz阻带抑制60dB以上。因为前面已经有CIC做粗滤波FIR阶数不用特别高60阶左右足够。系数位宽用16bit量化输入输出位宽选择16bitFIR Compiler IP配置成单通道即可两路DDC各用一个FIR实例。有一个细节容易忽略FIR的群延迟是固定的但它在CIC之后而CIC的群延迟与R和N有关。工程上两路通道使用完全相同的CIC和FIR配置、相同的数据流时序所以通道之间不会因为群延迟产生相对误差。这一点是双通道对称性的关键保障也是为什么我反复强调“两个DDC必须镜像一致”。2.4 DDC链路参数速查与资源评估为了方便大家直接抄作业我把整套DDC链路的核心参数整理成一张表。参数项数值说明中频频率 f070 MHz由射频前端决定ADC采样率 Fs100 Msps双通道同步采集ADC位宽16 bit输入到FPGANCO相位累加器位宽32 bit频率分辨率为0.023HzNCO频率字0xB3333333对应70MHzCIC抽取率R8输出速率降为12.5MspsCIC差分延迟M1通带特性更好CIC级数N4阻带抑制30dB以上CIC输出位宽28 bit按164·ceil(log2(8))计算FIR通带0 ~ 2 MHz补偿CIC跌落FIR过渡带2 ~ 4 MHz约60阶DDC输出速率12.5 Msps基带IQ对资源方面以Artix-7 XC7A35T为例两个DDC通道加两个CORDIC相位估计模块大约消耗不到2000个LUT、4个DSP48、6块BRAM整体非常轻量。哪怕是资源更少的小型FPGA只要LUT有1万以上基本都能跑得动。3. 相位差估计算法的FPGA实现细节3.1 CORDIC反正切如何把I/Q变成稳定相位数字下变频输出的是基带I/Q信号相位信息就藏在IQ比值的反正切里φ atan2(Q, I)。FPGA里没有现成的atan2指令实现路径无非三种查表、用多项式逼近、用CORDIC迭代。查表实现最简单但精度与存储深度矛盾想要在全象限达到0.01°级别精度表会大得离谱。多项式逼近适合计算资源密集的场合但在FPGA里浮点运算成本高。CORDIC是同步旋转迭代算法只需要移位和加法非常契合FPGA的并行流水线结构。本项目使用Xilinx CORDIC IP配置成Vector Polar模式输入16位有符号I和Q输出16位相位和16位幅度。迭代次数选择16次相位量化误差在0.01°以下完全满足系统需求。需要提醒的是CORDIC IP对输入幅度范围有要求太小的输入会使迭代收敛变慢输出相位噪声变大。我在设计里加了一个简单的幅度门限当CORDIC输出的幅度低于某个阈值时直接把该采样点的相位判为无效不参与后续平均这样能防止低幅度时刻的随机相位拉坏整体结果。3.2 相位差计算、±π解模糊与多周期平均两路通道分别得到相位φ1和φ2之后最直觉的计算是Δφ φ1 - φ2。但这里藏着第一个坑CORDIC输出的相位范围通常是-π到π直接相减的结果范围是-2π到2π必须重新映射回[-π, π)。映射逻辑其实就三行代码的思路先做减法如果结果大于π就减去2π如果结果小于-π就加上2π。写成RTL就是两三个比较器和加减法器。但这个操作后还有一个隐患——当真实相位差正好在±π附近摆动时输出会在π和-π之间来回跳变表现出来就是相位差数值瞬间翻转180度。解决翻转问题的方法是相位解缠。我实现的方式是对相位差增量进行累积每个有效采样点计算Δφ与上一时刻Δφ的差值如果差值超过π就判断发生了跨越边界对当前值进行±2π修正后再累积。这样相位差曲线就能保持连续。这个逻辑在连续波场景下非常稳如果是脉冲或突发信号则需要在每个脉冲内单独完成解缠不能跨脉冲累积。解缠之后的相位差信号仍然有随机抖动来源包括ADC量化噪声、时钟抖动、CIC/FIR截位噪声等。为了提高估计精度我会对解缠后的相位差做滑动平均。平均点数选择64或256对应12.5Msps数据率下5~20微秒的等效时间。平均点数越大精度越高但响应变慢如果是运动目标或频率快速变化场景需要折中。实测下来输入SNR约40dB时64点滑动平均可以把相位差标准偏差压到0.1°以内效果非常理想。3.3 跨时钟域、数据对齐与流水线时序安排相位差估计模块看起来是纯组合逻辑加流水线但工程里最让人头疼的不是算法而是数据什么时候有效以及两路数据是否对齐。ADC输出的双通道数据以100Msps进入FPGADDC与相位估计都在这个时钟域处理避免了异步FIFO的麻烦。但如果ADC接口是JESD204B或LVDS串行协议接收端会涉及恢复时钟这时候一定要用协议自带的同步机制保证两个通道的采样点对齐。如果两路数据进入FPGA后没有对齐比如相差了一个或几个采样周期相位差估计会出现一个与频率相关的固定偏差。这个偏差等于2π·f·ΔT70MHz信号差一个采样周期就是252度完全不能接受。我在项目里加了一个通道对齐状态机先用已知的测试源或训练序列让两路产生可识别的标志通过FIFO读指针调整两路延迟直到标志对齐再进行正常处理。流水线时序方面整个链路的延迟可以分解为NCO混频1拍、CIC滤波器约2·R·N拍、FIR约半阶数拍、CORDIC 16拍、相位差计算与平均约2拍。两路通道延迟完全一致所以输出相位差不会因为链路处理顺序产生额外偏差。这里还要提一个容易被忽视的点如果采用共享NCO的方式为两路提供本振需注意DDS Compiler IP输出的正弦和余弦是分时还是并行。并行输出时两路直接使用若是时分复用需要通过寄存器把两路本振采样到同一拍避免一个通道用第N拍本振、另一个通道用第N1拍本振的情况。4. 仿真验证与板上调试实录4.1 用MATLAB生成测试激励把信号灌进Testbench在写RTL之前我习惯先用MATLAB把整个链路用定点模型跑通一遍同时生成用于RTL Testbench的激励数据。这个步骤能提前发现参数选择问题省下大量联调时间。激励生成代码很简单核心就是产生两路中频正弦信号一路相位为0另一路相位为45度并加上白噪声和量化fs 100e6; f0 70e6; n (0:8191); snr_db 40; s1 sin(2*pi*f0/fs*n); s2 sin(2*pi*f0/fs*n deg2rad(45)); s1n awgn(s1, snr_db); s2n awgn(s2, snr_db); q1 round(8192*s1n); q2 round(8192*s2n); fid fopen(ch1.txt, w); fprintf(fid, %d\n, q1); fclose(fid); fid fopen(ch2.txt, w); fprintf(fid, %d\n, q2); fclose(fid);Testbench读取这两个文件按100MHz时钟送入DUT的ADC输入端口。仿真时重点关注几个信号NCO输出的正弦余弦、CIC中间数据和使能信号、FIR输出的I/Q、CORDIC输出相位、最终相位差结果。建议在Testbench里直接做数据记录把最终相位差写入文本文件仿真结束后用MATLAB计算均值和标准偏差这样比肉眼看波形可靠得多。4.2 ILA/SignalTap实测波形与性能结果上板调试时我首选ILAXilinx或SignalTapAltera抓内部信号。抓取顺序有讲究先把ADC输入和NCO输出抓出来确认基础链路正常再看CIC输出有没有削顶或溢出最后才盯相位差寄存器。实测一个典型的调试场景输入信号幅度调到ADC满量程的80%左右观察CIC输出波形如果正弦波波形顶部出现平台状说明CIC位宽不够或者截位策略有问题需要回头增大保位宽度。DSP计算链路中溢出问题是隐性的——波形看起来是正弦但如果多次出现轻微削顶相位估计结果会呈现固定方向的偏差很难发现所以必须主动检查。仿真阶段的结果一般是SNR40dB、64点平均时相位差估计均值接近45°标准偏差在0.05°到0.1°之间。上板后因为各种真实噪声和时钟抖动标准偏差会略微变大但只要不超过0.2°系统测向指标就完全能合格。存在固定偏差的话首先排查两路ADC通道有没有对齐其次检查两路DDC链路截位是否一致通常这两个点能解决九成以上的固定偏差问题。4.3 常见问题速查表与避坑经验把调试过程中遇到的典型问题和解决方案整理成一张表方便后续直接查阅。问题现象可能原因解决手段相位差在±180°附近跳变atan2边界翻转增加相位解缠逻辑增量超过π时修正2πCIC输出波形削顶位宽扩展不足或截位过多按B_out公式扩展保留高18~20位再加dither两路相位差存在固定偏差通道延迟未对齐使用总开关对齐FIFO确认两路标志对齐后再处理CORDIC输出噪声偏大输入I/Q幅度过小加入AGC或幅度门限低于阈值时丢弃该点仿真正常但上板结果差板上时钟抖动或干扰检查采样时钟质量和电源纹波优先换干净时钟源FIR补偿后通带仍有波动系数位宽过窄系数位宽提升到18或24bit动目标场景相位差平滑过度平均点数太多减少滑动平均点数或用加权平均这里单独说一个经验在所有调试环节里最容易让人迷惑的是“仿真结果很好、上板相位差乱跳”。这种情况我碰过两次一次是板上采样时钟源抖动过大一次是ADC输入差分走线之间串扰严重。时钟问题可以换一个低抖动时钟芯片或检查时钟buffer供电走线问题只能改PCB新增蛇形等长和包地处理。所以这个项目的成败从原理图到FPGA代码、再到PCB每一个环节都在给相位差精度做贡献。最后再分享一个实操技巧上板之前先用MATLAB把整个DDC加相位差估计的定点模型完整跑一遍最后对比MATLAB定点输出与RTL仿真输出两者差别应该在1~2个LSB以内。这一步花半天时间能避免之后在板子上反复试错是整套开发流程里性价比最高的投入。
返回列表