第三章 数字信号处理基础(下):FIR滤波器设计、CIC滤波器、多速率信号处理

好,咱们接着往下聊。上一章我们把数字信号处理的基础概念过了一遍,这一章我打算聚焦在三个在雷达系统里天天打交道的模块:FIR滤波器、CIC滤波器,还有多速率信号处理。说实话,这三个东西在FPGA里实现的时候,坑不少,但用好了,效果也是立竿见影的。

3.1 FIR滤波器设计——从理论到FPGA实现

FIR滤波器,全称是有限长单位冲激响应滤波器。为什么雷达里爱用FIR?说白了,就两个原因:线性相位绝对稳定。线性相位意味着信号经过滤波器后,不同频率分量的延迟是一样的,不会把脉冲波形给“扯歪”了。这在雷达脉冲压缩里是致命的——波形一歪,测距精度就没了。

我个人习惯,在设计FIR滤波器时,第一步不是写代码,而是先在MATLAB里把系数算好。用fir1或者firpm函数,把通带、阻带、阶数定下来。这里有个经验:滤波器阶数越高,过渡带越陡,但资源消耗也越大。在FPGA里,乘法器是稀缺资源,所以别一味追求高指标。

核心要点: FIR滤波器的FPGA实现结构主要有三种:直接型、转置型、对称型。雷达里最常用的是对称型,因为可以利用系数对称性,把乘法器数量砍掉一半。

举个例子,一个32阶的FIR滤波器,如果系数是对称的,你只需要16个乘法器。我在项目中遇到过,一个脉冲压缩模块里用了4个128阶的FIR,如果不用对称结构,光乘法器就能吃掉大半个芯片。

3.1.1 量化效应——别让精度坑了你

嗯,这里要注意。MATLAB算出来的系数是双精度浮点,但FPGA里只能用定点数。量化成16位还是12位?这直接影响滤波器的性能。

我曾经在一个项目里,为了省资源,把系数从16位砍到12位。结果仿真时没发现问题,一上板子,脉冲压缩的旁瓣抬高了3个dB。后来查了半天,就是量化误差闹的。所以我的建议是:系数量化至少保留14位以上,如果资源允许,16位更稳妥。

// Verilog示例:对称型FIR滤波器核心代码
// 假设系数已经量化并存储在ROM中
always @(posedge clk or negedge rst_n) begin
    if (!rst_n) begin
        sum <= 0;
    end else begin
        // 对称加法:x[n] + x[N-1-n]
        sym_add <= data_in + data_delay[N-1];
        // 乘加运算
        sum <= sum + sym_add * coeff[0];
        // ... 依次类推,流水线处理
    end
end

小技巧: 在FPGA里实现FIR时,记得用流水线结构。把乘法和加法拆成多级,可以跑更高的时钟频率。我一般会插入2-3级寄存器,这样时钟频率能轻松上200MHz。

3.2 CIC滤波器——抽取与插值的利器

CIC滤波器,全称是级联积分梳状滤波器。这玩意儿在雷达里太常见了,尤其是做降采样的时候。为什么用它?因为它不需要乘法器,只用加法器和寄存器,资源消耗极低。

你想想看,如果ADC采样率是200MHz,但你的基带处理只需要50MHz,中间差了4倍。直接用FIR做抽取?那滤波器阶数会高得吓人。用CIC先粗抽一把,再用FIR做精细滤波,这是标准做法。

3.2.1 CIC的结构与设计

CIC由两部分组成:积分器梳状器。积分器就是累加器,梳状器就是延迟相减。级联的级数N决定了阻带衰减特性。N越大,阻带衰减越陡,但通带衰减也越大。

我记得有一次,一个同事设计了一个5级CIC,抽取因子是16。仿真时通带衰减只有0.2dB,觉得没问题。结果级联了后面的FIR后,整体通带衰减到了1.5dB。这就是没算清楚CIC的通带“滚降”。

避坑指南: CIC滤波器的通带衰减是固定的,与级数N和抽取因子R有关。公式是:衰减 = 20 * N * log10( (R * sin(π * f_c / f_s)) / (π * f_c / f_s) )。设计时一定要预留补偿空间,或者在后面加一个CIC补偿滤波器。

在FPGA里实现CIC,关键是要注意位宽增长。积分器是累加器,位宽会一直增长。如果不做截位,位宽会变得非常大。我一般会在每级之间做截位,或者用ceil(log2(R * N))来估算最大位宽。

// Verilog示例:单级CIC积分器
always @(posedge clk or negedge rst_n) begin
    if (!rst_n) begin
        integrator <= 0;
    end else begin
        integrator <= integrator + data_in;
    end
end

// 梳状器(在抽取之后)
always @(posedge clk_dec or negedge rst_n) begin
    if (!rst_n) begin
        comb_delay <= 0;
        comb_out <= 0;
    end else begin
        comb_delay <= integrator_out;
        comb_out <= integrator_out - comb_delay;
    end
end

3.3 多速率信号处理——降采样与升采样

多速率信号处理,说白了就是改变信号的采样率。在雷达里,这个需求太普遍了。ADC采样率很高,但后续处理不需要那么高,那就降采样。DAC需要高采样率,但基带数据率低,那就升采样。

这里有个核心概念:抽取插值。抽取就是扔掉一些样本,插值就是在样本之间插入零值。但直接抽取会引入混叠,直接插值会引入镜像。所以必须配合滤波器使用。

3.3.1 抽取与抗混叠滤波

抽取之前,一定要加一个抗混叠滤波器。这个滤波器的截止频率要小于抽取后采样率的一半。比如,采样率100MHz,抽取因子2,抽取后采样率50MHz,那抗混叠滤波器的截止频率要小于25MHz。

我见过有人图省事,直接抽取不滤波。结果频谱里全是混叠的杂散,目标都找不到了。所以,抽取必滤波,这是铁律

3.3.2 插值与镜像抑制

插值之后,一定要加一个镜像抑制滤波器。插值会在原始频谱周围产生镜像,必须滤掉。这个滤波器的截止频率也是原始信号带宽的一半。

在实际工程中,我经常用多级实现。比如,要做8倍降采样,我不会直接用一个FIR做8倍抽取,而是用CIC做4倍抽取,再用FIR做2倍抽取。这样总资源更少,而且滤波器设计更灵活。

经验之谈: 多速率系统的设计原则是“先滤波,后抽取;先插值,后滤波”。这个顺序不能乱。另外,多级实现时,每一级的滤波器指标可以放宽,因为每一级处理的采样率不同,过渡带要求也不同。

3.3.3 多相滤波器结构

当抽取或插值因子很大时,直接实现滤波器效率很低。这时候就要用多相结构。多相结构把滤波器系数分成多组,每组只处理对应相位的样本。这样,计算量可以降低到原来的1/R。

我在一个项目里,需要做32倍降采样。如果用直接型FIR,每个输出点需要做128次乘加。用了多相结构后,每个输出点只需要做4次乘加。效率提升了32倍。你说香不香?

// 多相抽取滤波器示意(R=4)
// 系数分成4组:h0, h1, h2, h3
// 每个时钟周期,只处理一组系数
always @(posedge clk) begin
    case (phase_counter)
        0: sum <= sum + data_in * h0[addr];
        1: sum <= sum + data_in * h1[addr];
        2: sum <= sum + data_in * h2[addr];
        3: sum <= sum + data_in * h3[addr];
    endcase
end

设计建议: 多相滤波器的系数分组,可以用MATLAB的reshape函数轻松实现。把系数向量重排成R行,每行就是一组多相系数。然后FPGA里用循环寻址,就能高效实现。

3.4 本章小结

这一章的内容,说白了就是三个核心模块:FIR、CIC、多速率处理。FIR是精细滤波的主力,CIC是粗抽粗插的利器,多速率处理是连接不同采样率的桥梁。在雷达信号处理链里,它们经常组合使用。

我个人建议,初学者可以先从FIR的对称结构入手,再学CIC的位宽控制,最后掌握多相结构。每一步都动手写写Verilog代码,跑跑仿真。纸上得来终觉浅,绝知此事要躬行。

下一章,我们会聊到FFT和脉冲压缩。那才是雷达信号处理的重头戏。咱们到时候见。