news 2026/10/5 6:02:26

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

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
FPGA双通道中频信号数字下变频与相位差估计实现详解

接手这个项目的时候,我的第一反应是链路不算特别复杂——双通道中频信号进来,数字下变频(DDC)以后做相位差估计,听起来就是“采样、混频、滤波、反正切”四步。但真正把工程做细之后才发现,这套东西想要稳定、高精度地跑起来,水相当深。

这类需求常见于干涉仪测向、阵列测向、无源定位、雷达或通信的多通道相位比较场景。简单说就是两个接收通道输出中频信号,FPGA负责把中频信号搬移到基带,再通过I/Q数据估计两路之间的相位差。相位差的精度直接决定了后续测向或定位的精度,所以整个链路的每一个环节都不能糊弄。这篇文章我以一套实测过的双通道中频信号数字下变频及相位差估计方案为主线,采用FPGA实现,中频70MHz、采样率100Msps,把工程思路、模块设计、参数计算、仿真调试完整拆开讲,适合正在做接收机、测向设备以及FPGA信号处理的朋友参考。

1. 方案定调:数字下变频与相位差估计的整体链路设计

1.1 双通道中频信号和相位差估计到底在解决什么问题

在很多接收设备里,射频信号并不会有直接被ADC采样,而是经过射频前端先下变频到一个固定的中频,比如70MHz、140MHz,然后以中频形式进入数字处理板。之所以采用中频方案,是因为直接在射频段采样对ADC速率和前端动态范围要求太高,工程上不划算。而对FPGA来说,处理一个几十到几百兆赫兹的中频信号,就从容得多了。

双通道的含义也非常明确:设备里有两个接收通道。可能是两天线干涉仪的两个通道,也可能是相控阵两个阵元的输出。无论哪种场景,下游都需要比较这两个通道输出信号之间的相位关系。以干涉仪测向为例,两天线间距为d,信号到达角为θ,波长λ与相位差Δφ的关系是:

Δφ = 2π · d · sinθ / λ

也就是说,只要估计出相位差Δφ,就能反推到达角θ。相位差1°的误差,在d=0.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位并行数据进入FPGA;FPGA内部先做两个完全对称的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位,Fs=100MHz,f0=70MHz,代入计算:

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到16,M取1或2,N取3到5。

本项目信号带宽约2MHz,输入采样率100Msps,我选择R=8、M=1、N=4。抽取后数据率变为12.5Msps,信号带宽仍占2MHz,频谱利用率合适,CIC阻带抑制也足够。

CIC有一个必须注意的问题:增益会随着抽取率和级数成指数增长。输出位宽计算公式是:

B_out = B_in + N · ceil(log2(R · M))

代入本项目的参数:输入16位,R=8,M=1,N=4,那么log2(8)=3,扩展位数为4·3=12位,输出位宽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量化,输入输出位宽选择16bit,FIR Compiler IP配置成单通道即可,两路DDC各用一个FIR实例。

有一个细节容易忽略:FIR的群延迟是固定的,但它在CIC之后,而CIC的群延迟与R和N有关。工程上两路通道使用完全相同的CIC和FIR配置、相同的数据流时序,所以通道之间不会因为群延迟产生相对误差。这一点是双通道对称性的关键保障,也是为什么我反复强调“两个DDC必须镜像一致”。

2.4 DDC链路参数速查与资源评估

为了方便大家直接抄作业,我把整套DDC链路的核心参数整理成一张表。

参数项数值说明
中频频率 f070 MHz由射频前端决定
ADC采样率 Fs100 Msps双通道同步采集
ADC位宽16 bit输入到FPGA
NCO相位累加器位宽32 bit频率分辨率为0.023Hz
NCO频率字0xB3333333对应70MHz
CIC抽取率R8输出速率降为12.5Msps
CIC差分延迟M1通带特性更好
CIC级数N4阻带抑制30dB以上
CIC输出位宽28 bit按16+4·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进入FPGA,DDC与相位估计都在这个时钟域处理,避免了异步FIFO的麻烦。但如果ADC接口是JESD204B或LVDS串行协议,接收端会涉及恢复时钟,这时候一定要用协议自带的同步机制保证两个通道的采样点对齐。

如果两路数据进入FPGA后没有对齐,比如相差了一个或几个采样周期,相位差估计会出现一个与频率相关的固定偏差。这个偏差等于2π·f·ΔT,70MHz信号差一个采样周期就是252度,完全不能接受。我在项目里加了一个通道对齐状态机:先用已知的测试源或训练序列让两路产生可识别的标志,通过FIFO读指针调整两路延迟,直到标志对齐,再进行正常处理。

流水线时序方面,整个链路的延迟可以分解为:NCO混频1拍、CIC滤波器约2·R·N拍、FIR约半阶数拍、CORDIC 16拍、相位差计算与平均约2拍。两路通道延迟完全一致,所以输出相位差不会因为链路处理顺序产生额外偏差。

这里还要提一个容易被忽视的点:如果采用共享NCO的方式为两路提供本振,需注意DDS Compiler IP输出的正弦和余弦是分时还是并行。并行输出时两路直接使用;若是时分复用,需要通过寄存器把两路本振采样到同一拍,避免一个通道用第N拍本振、另一个通道用第N+1拍本振的情况。

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实测波形与性能结果

上板调试时,我首选ILA(Xilinx)或SignalTap(Altera)抓内部信号。抓取顺序有讲究,先把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以内。这一步花半天时间,能避免之后在板子上反复试错,是整套开发流程里性价比最高的投入。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/5 6:02:03

Windows跑Gromacs环境搭建:虚拟机、WSL2与双系统全攻略

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/5 6:01:33

IEEE33配电网P2P能量共享:双层演化博弈复现与避坑指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/5 6:01:03

自动化应届生入行指南:从PLC编程到现场调试的12年经验

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/5 5:59:45

RFM模型实战:三步完成电商用户分层与精细化运营

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/5 5:59:21

ISO 26262产品开发过程详解:从HARA到FMEDA的落地实践

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/5 5:59:19

Rolldown 模块联邦探索:在 Vite 7.0 中实现微前端资产极速共享

Rolldown 模块联邦探索:在 Vite 7.0 中实现微前端资产极速共享在大型前端微前端(Micro-frontends)与巨型 Monorepo 架构的落地过程中,**模块联邦(Module Federation)**始终是一项让人又爱又恨的王牌技术。 …

作者头像 李华