3、数字下变频(DDC):NCO原理、混频器实现、CIC滤波器设计

各位同学,咱们今天聊聊数字下变频。说实话,DDC 是雷达信号处理里最基础、也最容易被忽视的模块。我刚开始做嵌入式雷达时,总觉得这玩意儿不就是个混频加滤波嘛,有啥难的?结果第一次上板调试,频谱乱七八糟,折腾了两天才发现是 NCO 的相位截断没处理好。嗯,从那以后我再也不敢小看 DDC 了。

3.1 为什么要做数字下变频?

雷达接收到的信号,通常是中频信号。比如 70MHz 的中频,采样率可能到 100MHz 甚至更高。你想想看,如果直接拿这个高速信号去做脉冲压缩、做 MTD,FPGA 的资源消耗会非常恐怖。

DDC 的核心目的就两个:

  • 频谱搬移:把中频信号搬移到零中频(基带)
  • 降采样:降低数据率,减轻后续处理负担

说白了,就是把 100MHz 的数据率降到 10MHz 甚至更低,同时保留所有有用信息。我见过有人图省事,直接降采样不做滤波,结果频谱混叠得一塌糊涂,目标都找不到了。

3.2 NCO 原理:怎么产生正交本振?

NCO,全称 Numerically Controlled Oscillator,数字控制振荡器。它的任务就是产生两路正交的正弦波:cos 和 sin。

核心公式很简单:

s(n) = cos(2π · f0/fs · n)
c(n) = sin(2π · f0/fs · n)

其中 f0 是本振频率,fs 是采样率。实际实现时,我们不会实时算三角函数,而是查表。

NCO 的典型结构:

  1. 相位累加器:每个时钟周期累加一个频率控制字
  2. 相位截断:只取高位地址去查表
  3. 正弦/余弦查找表:存储一个周期的波形数据

关键参数:

参数说明我的建议
累加器位宽 N决定频率分辨率一般取 32 位,分辨率够用
查找表位宽 M决定幅度精度12~16 位,看系统需求
相位截断位数N - 地址位宽截断太多会产生杂散

我曾经在一个项目里,为了省 LUT,把查找表压缩到 8 位地址。结果频谱上出现了明显的杂散,差点把弱小目标淹没了。后来老老实实用了 12 位地址,问题解决。所以,相位截断的杂散抑制是 NCO 设计的第一要务。

3.3 混频器实现:乘法器与符号处理

混频器就是做乘法。输入信号 x(n) 分别乘以 cos 和 sin,得到 I/Q 两路:

I(n) = x(n) · cos(2π · f0/fs · n)
Q(n) = x(n) · sin(2π · f0/fs · n)

在 FPGA 里实现时,有几个坑要注意:

  • 位宽扩展:两个 16 位数相乘,结果 32 位。你得想好截断策略。
  • 符号处理:有符号数乘法,别搞成无符号了。
  • 流水线:DSP48 块一般有内置流水线,记得用上。

我的小技巧:混频后的数据,先不要急着截断。保留完整位宽送到 CIC 滤波器,CIC 内部会做累加,精度损失更小。等 CIC 输出后再统一截位。

3.4 CIC 滤波器设计:降采样的利器

CIC 滤波器,全称 Cascaded Integrator-Comb。它最大的优点是:不需要乘法器,只有加法器和减法器。在 FPGA 里实现,资源消耗极低。

CIC 的结构:

  1. 积分器级:N 级积分器串联,每个积分器就是一个累加器
  2. 降采样:按抽取因子 R 降采样
  3. 梳状器级:N 级梳状器串联,每个梳状器做差分

频率响应公式:

H(z) = [ (1 - z^(-R)) / (1 - z^(-1)) ]^N

其中 R 是抽取因子,N 是级数。

注意!CIC 的增益很大:

CIC 的增益是 R^N。比如 R=16,N=3,增益就是 4096。输出位宽需要预留足够的比特,否则会溢出。我见过有人没算增益,结果输出全是饱和值,信号全丢了。

设计步骤:

  • 第一步:确定抽取因子 R。根据输入数据率和输出数据率计算。
  • 第二步:确定级数 N。一般取 3~5 级。级数越多,阻带衰减越大,但通带滚降也越严重。
  • 第三步:计算位宽。输出位宽 = 输入位宽 + N · log2(R)。
  • 第四步:加补偿滤波器。CIC 的通带不平坦,需要 FIR 补偿。

3.5 实战案例:70MHz 中频 DDC 设计

我拿一个实际项目举例。输入信号:70MHz 中频,采样率 100MHz。需要降到 10MHz 基带数据。

参数计算:

参数说明
本振频率 f070MHz搬移到零中频
抽取因子 R10100MHz → 10MHz
CIC 级数 N4阻带衰减约 60dB
输入位宽14 bitADC 输出
输出位宽14 + 4·log2(10) ≈ 28 bit实际取 32 bit 安全

代码片段(Verilog 风格):

// NCO 相位累加器
always @(posedge clk) begin
    phase_acc <= phase_acc + freq_word;  // 32-bit 累加
end

// 查表(用 Block RAM 实现)
assign sin_addr = phase_acc[31:20];  // 取高 12 位
assign cos_addr = sin_addr + 256;    // 90 度偏移

// 混频器
assign i_mix = adc_data * cos_table[cos_addr];
assign q_mix = adc_data * sin_table[sin_addr];

// CIC 积分器级
always @(posedge clk) begin
    integ1 <= integ1 + i_mix;
    integ2 <= integ2 + integ1;
    integ3 <= integ3 + integ2;
    integ4 <= integ4 + integ3;
end

// 降采样 + 梳状器
always @(posedge clk_out) begin  // clk_out = clk / 10
    comb1 <= integ4 - integ4_d1;
    comb2 <= comb1 - comb1_d1;
    comb3 <= comb2 - comb2_d1;
    comb4 <= comb3 - comb3_d1;
end

避坑指南:我曾经在 CIC 的积分器里用了 28 位宽,结果累加时溢出了。后来改成 32 位,并在每个积分器后加了饱和处理。记住,积分器一定要防溢出,否则信号会失真。

3.6 性能验证与调试

DDC 设计完,怎么验证对不对?我一般做三步:

  1. 看频谱:输入单频信号,看输出频谱是否干净。杂散应该低于 -60dBc。
  2. 测延时:I/Q 两路的延时必须一致。用阶跃信号测,看两路是否同时跳变。
  3. 测动态范围:输入大信号和小信号,看 CIC 是否饱和。

嗯,这里要注意。CIC 的通带滚降在雷达里是个大问题。如果你要处理的信号带宽比较宽(比如 5MHz 以上),一定要加 FIR 补偿滤波器。我习惯在 CIC 后面级联一个 32 阶的 FIR,把通带补偿平。

好了,DDC 的核心内容就这些。说白了,NCO 是灵魂,混频器是手脚,CIC 是心脏。三个模块配合好了,你的雷达信号处理就成功了一半。下一章咱们聊聊脉冲压缩,那才是真正考验功力的地方。