多速率信号处理源码深度剖析

模块总览

块 / 类 文件 能力
rational_resampler rational_resampler_impl.cc 有理数倍率 (L/M)
interp_fir_filter interp_fir_filter_impl.cc 整数插值 (L)
pfb_interpolator_ccf pfb_interpolator_ccf_impl.cc PFB 插值
pfb_decimator_ccf pfb_decimator_ccf_impl.cc PFB 抽取 + 选信道
pfb_channelizer_ccf pfb_channelizer_ccf_impl.cc 多信道化
pfb_arb_resampler_* pfb_arb_resampler.cc 任意实数倍率
mmse_resampler_* mmse_resampler_impl.cc 符号同步 / 细粒度重采样
polyphase_filterbank polyphase_filterbank.cc PFB 公共基类
firdes::low_pass firdes.cc 抗混叠原型滤波器

1. 采样率转换底层原理

1.1 数学模型

设输入采样率 (f_{s,in}),目标 (f_{s,out}),转换比:

r = \\frac{f_{s,out}}{f_{s,in}} = \\frac{L}{M}

经典三阶段结构:

  1. 上采样 (L) 倍:插入 (L-1) 个零 → 频谱压缩并产生 (L-1) 组镜像
  2. 抗镜像/抗混叠 FIR:截止频率 (\le \min(f_{s,in}/2,, f_{s,out}/2))
  3. 下采样 (M) 倍:每 (M) 点取 1 点

Noble 恒等式(多相实现的基础):先插零再 FIR 再抽取,等价于把原型 FIR (h[n]) 拆成 (L) 条多相支路,每条只算"有效"样本,计算量从 (O(N \cdot L)) 降到 (O(N))。

GNU Radio 中 rational_resampler 把插值、滤波、抽取合成一步,用多相 FIR 数组 d_firs[L] 实现。

1.2 源码:set_relative_rate 与 GCD 化简

127:156:gr-filter/lib/rational_resampler_impl.cc 复制代码
    auto d = std::gcd(interpolation, decimation);
    // ...
    if (taps.empty()) {
        interpolation /= d;
        decimation /= d;
        staps = design_resampler_filter<TAP_T>(interpolation, decimation, fractional_bw);
    }
    // ...
    this->set_relative_rate(uint64_t{ interpolation }, uint64_t{ decimation });
  • set_relative_rate(L, M) 告诉调度器:每产出 (M) 个输出样本,大约消耗 (L) 个输入样本(general_work 块用 forecast 精确估算)。
  • GCD 约分:(48/32 \to 3/2),滤波器支路数从 48 降到 3,显著省内存与 CPU。

1.3 general_work:多相指针调度

244:260:gr-filter/lib/rational_resampler_impl.cc 复制代码
    unsigned int ctr = d_ctr;
    while ((i < noutput_items) && (count < ninput_items[0])) {
        out[i++] = d_firs[ctr].filter(in);
        ctr += this->decimation();
        while (ctr >= this->interpolation()) {
            ctr -= this->interpolation();
            in++;
            count++;
        }
    }
    d_ctr = ctr;

含义

  • ctr:当前使用的多相支路索引 (0 \ldots L-1)
  • 每输出 1 点:ctr += M;当 ctr >= L 时输入前进 1 样本并 ctr -= L
  • 等价于:在"虚拟"的 (L) 倍插值流上,每 (M) 点取 1 点,但只对非零相位做 FIR

在信号链中的位置 :ADC 采样率与算法符号率不匹配时的粗对齐(如 2 Msps → 1.28 Msps)。

1.4 涉及语法

语法 用途
C++ 类模板 rational_resampler<IN_T,OUT_T,TAP_T> 多类型实例化(ccc/ccf/fff 等)
general_work + forecast 非固定 1:1 的 block
reinterpret_cast 无类型 buffer → 样本指针
std::gcd 有理数约分

1.5 接口调用

python 复制代码
from gnuradio import filter

# L=3, M=2 → 输出率 = 输入率 × 3/2
# taps 为空则自动设计 Kaiser 低通
rr = filter.rational_resampler_ccc(
    interpolation=3,
    decimation=2,
    taps=[],           # 或自定义 taps
    fractional_bw=0.4  # 自动设计时的通带比例
)
tb.connect(src, rr, sink)

C++:filter::rational_resampler_ccc::make(3, 2, taps, 0.4f)


2. 抽取、插值抗混叠滤波源码逻辑

2.1 理论:为何要滤波

操作 风险 滤波要求
插值 (L) 镜像出现在 (\pm k f_s/L) 低通,截止 (\le f_{s,out}/2)
抽取 (M) 频谱折叠(混叠) 先低通至 (f_{s,out}/2),再抽点

自动设计函数 design_resampler_filter 根据 (r = L/M) 选过渡带:

57:73:gr-filter/lib/rational_resampler_impl.cc 复制代码
    float rate = float(interpolation) / float(decimation);
    if (rate >= 1.0) {
        trans_width = halfband - fractional_bw;           // 扩采样:防镜像
        mid_transition_band = halfband - trans_width / 2.0;
    } else {
        trans_width = rate * (halfband - fractional_bw); // 降采样:防混叠
        mid_transition_band = rate * halfband - trans_width / 2.0;
    }
    return firdes::low_pass(interpolation, interpolation,
                            mid_transition_band, trans_width,
                            fft::window::WIN_KAISER, beta);

设计采样率取 interpolation :文档说明当 (L \ge M) 时,滤波器工作在插值后的最高速率 (L \cdot f_{s,in}) 上。

2.2 原型 FIR:firdes::low_pass

77:101:gr-filter/lib/firdes.cc 复制代码
    int ntaps = compute_ntaps(sampling_freq, transition_width, window_type, param);
    double fwT0 = 2 * GR_M_PI * cutoff_freq / sampling_freq;
    for (int n = -M; n <= M; n++) {
        if (n == 0)
            taps[n + M] = fwT0 / GR_M_PI * w[n + M];
        else
            taps[n + M] = sin(n * fwT0) / (n * GR_M_PI) * w[n + M];  // sinc × 窗
    }

理想低通冲激响应 (h_d[n] = \frac{\sin(\omega_c n)}{\pi n}),再乘 Kaiser 窗控制旁瓣。

2.3 多相拆分:install_taps

插值 FIR 与有理重采样共用同一拆分逻辑:

200:204:gr-filter/lib/rational_resampler_impl.cc 复制代码
    for (int i = 0; i < (int)taps.size(); i++)
        xtaps[i % nfilters][i / nfilters] = taps[i];

原型 (h[0], h[1], \ldots, h[N-1]) → 第 (k) 相:(h[k], h[k+L], h[k+2L], \ldots)

插值 interp_fir_filter 的 work

126:134:gr-filter/lib/interp_fir_filter_impl.cc 复制代码
    for (int i = 0; i < ni; i++) {
        for (int nf = 0; nf < nfilters; nf++) {
            out[nf] = d_firs[nf].filter(&in[i]);  // 1 输入 → L 输出
        }
        out += nfilters;
    }

继承 sync_interpolator,调度器已知输出是输入的 (L) 倍。

2.4 PFB 抽取:pfb_decimator_ccf

输入经 stream_to_streams 分成 (M) 路(相位 0...M-1),每路进一条多相 FIR,再旋转相加选出第 (k) 信道:

189:194:gr-filter/lib/pfb_decimator_ccf_impl.cc 复制代码
    // y[i] = Σ_{j=0}^{M-1} x[j][i] · exp(2π j k / M)
    for (unsigned int j = 0; j < d_rate; j++) {
        out[i] += d_tmp[j * noutput_items + i] * d_rotator[j];
    }

或用 FFT 代替显式复指数(work_fir_fft),计算 (O(M \log M)) 但 (M) 大时更高效。

在链路中的位置

  • 发射:符号流 → 插值 + RRC(抗镜像)
  • 接收:高采样 IQ → 抽取 + 低通(抗混叠)→ 符号同步

2.5 Python 便捷封装(自动 taps)

gr-filter/python/filter/pfb.pydecimator_ccf / interpolator_ccfoptfir.low_pass 生成覆盖 0.4 归一化带宽的原型滤波器,并包装 stream_to_streams

python 复制代码
from gnuradio.filter import pfb

# 8 倍抽取,选第 0 信道,自动设计 taps
dec = pfb.decimator_ccf(decim=8, channel=0, atten=100)

interp = pfb.interpolator_ccf(interp=4, atten=100)

3. 多相滤波组(PFB)核心源码解析

3.1 基类 polyphase_filterbank

39:66:gr-filter/lib/polyphase_filterbank.cc 复制代码
    d_taps_per_filter = ceil(ntaps / d_nfilts);
    for (i = 0; i < d_nfilts; i++) {
        for (j = 0; j < d_taps_per_filter; j++) {
            d_taps[i][j] = tmp_taps[i + j * d_nfilts];  // 多相分解
        }
        d_fir_filters[i].set_taps(d_taps[i]);
        d_fft_filters[i].set_taps(d_taps[i]);         // 可选 FFT 卷积
    }

每条支路一个 kernel::fir_filter_ccfd_fft_filters 供长滤波器 batch 处理。

3.2 PFB 插值器

71:77:gr-filter/lib/pfb_interpolator_ccf_impl.cc 复制代码
    while (i < noutput_items) {
        for (unsigned int j = 0; j < d_rate; j++) {
            out[i] = d_fir_filters[j].filter(&in[count]);
            i++;
        }
        count++;
    }

1 个输入样本 → 依次通过 (L) 条多相 FIR → (L) 个输出。与 interp_fir_filter 数学等价,接口与 PFB 家族一致。

3.3 PFB 信道化器 pfb_channelizer_ccf

将宽带信号分成 (M) 个等带宽子信道,每信道采样率 (f_s/M)。

  • 输入:(M) 路 deinterleaved 流(stream_to_streams
  • 每路多相 FIR → IFFT/FFT 旋转 → 输出 (M) 信道向量
  • oversample_rate 限制为 (N/i,, i \in [1,N]),支持 fractional 信道化

作用 :频谱监测、多载波并行解调、信道选择(配合 set_channel_map)。

3.4 底层 FIR:kernel::fir_filter

91:99:gr-filter/lib/fir_filter.cc 复制代码
float fir_filter<float,float,float>::filter(const float input[]) const
{
    volk_32f_x2_dot_prod_32f_a(..., ar, d_aligned_taps[al].data(), d_ntaps + al);
    return d_output[0];
}
  • taps 内部反转(卷积 vs 相关)
  • VOLK 对齐点积,SIMD 加速
  • filterNdec 支持抽 decimate 步长的批量滤波

3.5 涉及语法

  • 继承pfb_decimator_ccf_impl : sync_block, polyphase_filterbank
  • sync_interpolator:固定 (L:1) 输出比
  • gr::thread::scoped_lock :运行时 set_taps 线程安全
  • set_history(nt):FIR 需要过去 (nt-1) 个样本

4. 任意分数倍重采样实现机制

GNU Radio 提供两条路径:PFB 任意重采样 (宽带、带抗混叠)和 MMSE 重采样(窄带、低延迟、适合符号同步)。

4.1 pfb_arb_resampler:多相 + 线性插值

速率分解
148:152:gr-filter/lib/pfb_arb_resampler.cc 复制代码
void pfb_arb_resampler_ccf::set_rate(float rate)
{
    d_dec_rate = (unsigned int)floor(d_int_rate / rate);   // D = floor(N/r)
    d_flt_rate = (d_int_rate / rate) - d_dec_rate;         // 小数部分 μ
}
  • (N =) filter_size(默认 32):多相滤波器个数
  • (D = \lfloor N/r \rfloor):支路步进
  • (\mu = N/r - D):相邻两支路输出的线性插值权重
差分滤波器(导数支路)
108:124:gr-filter/lib/pfb_arb_resampler.cc 复制代码
    diff_filter[0] = -1; diff_filter[1] = 1;
    for (i = 0; i < newtaps.size() - 1; i++) {
        difftaps.push_back(newtaps[i+1] - newtaps[i]);  // 近似 dh/dn
    }

输出:

y \\approx y_0 + \\mu \\cdot \\frac{dy}{dn}

其中 (y_0 = h_j * x),(dy/dn) 来自差分 taps 的 FIR,实现相邻多相滤波器之间的亚滤波器相位插值。

核心循环
189:206:gr-filter/lib/pfb_arb_resampler.cc 复制代码
        while (j < d_int_rate) {
            o0 = d_filters[j].filter(&input[i_in]);
            o1 = d_diff_filters[j].filter(&input[i_in]);
            output[i_out] = o0 + o1 * d_acc;

            d_acc += d_flt_rate;
            j += d_dec_rate + (int)floor(d_acc);
            d_acc = fmodf(d_acc, 1.0);
        }
        i_in += (int)(j / d_int_rate);
        j = j % d_int_rate;

状态变量d_last_filterd_accwork() 调用保持连续。

Block 包装
120:140:gr-filter/lib/pfb_arb_resampler_ccf_impl.cc 复制代码
int pfb_arb_resampler_ccf_impl::general_work(...)
{
    int nitems = floorf((float)noutput_items / relative_rate());
    int processed = d_resamp.filter(out, in, nitems, nitems_read);
    consume_each(nitems_read);
    return processed;
}

set_relative_rate(rate) 声明非整数比;forecastnoutput/rate 估算输入需求。

自动滤波器设计(Python)
256:265:gr-filter/python/filter/pfb.py 复制代码
        if(rate < 1):
            halfband = 0.5 * rate   # 降采样:截止 ∝ 输出 Nyquist
            return filter.firdes.low_pass_2(flt_size, flt_size, bw, tb, atten, ...)
        else:
            # 升采样:防镜像,halfband = 0.5

4.2 mmse_resampler:Farrow / MMSE 插值

用于符号定时恢复:输入率与输出率接近 1:1,只需微调相位 (\mu)。

95:105:gr-filter/lib/mmse_resampler_impl.cc 复制代码
    while (idx_out < noutput_items && idx_in < max_input_index) {
        out[idx_out++] = d_resamp.interpolate(&in[idx_in], static_cast<float>(d_mu));
        d_mu += d_delta_mu;
        idx_in += static_cast<int>(floor(d_mu));
        d_mu -= floor(d_mu);
    }
  • d_delta_mu = 重采样比(输出/输入,可 <1 或 >1)
  • mmse_fir_interpolator 预存 129 组 FIR(NSTEPS+1),(\mu) 量化到最近步进:
56:62:gr-filter/lib/mmse_fir_interpolator.cc 复制代码
    int imu = (int)rint(mu * NSTEPS);
    return filters[imu].filter(input);

支持第二路输入动态比率 ,以及 PMT 消息改 resamp_ratio / mu

4.3 两种任意重采样对比

pfb_arb_resampler mmse_resampler
典型 (r) 任意(如 2.5、0.73) ≈1 ± 小偏差
抗混叠 有(完整 LPF) 弱(短 MMSE FIR)
用途 脉冲成形、速率匹配 symbol_sync、Gardner 后微调
延迟 较大(原型 FIR 群时延) 较小

4.4 接口示例

python 复制代码
from gnuradio import filter

# 8000 → 20000 Hz,等价 rate=2.5
rate = 20000.0 / 8000.0
resamp = filter.pfb.arb_resampler_ccf(rate, taps=None, flt_size=32)

# 符号同步:timing error 反馈到 resamp_ratio
mmse = filter.mmse_resampler_cc(phase_shift=0.0, resamp_ratio=1.0 + epsilon)

5. 通信系统中重采样工程应用

5.1 发射链:成形 + 整数/任意上采样

generic_mod_demod.py 标准做法:

133:140:gr-digital/python/digital/generic_mod_demod.py 复制代码
        self.rrc_taps = filter.firdes.root_raised_cosine(
            nfilts, nfilts, 1.0, self._excess_bw, ntaps)
        self.rrc_filter = filter.pfb_arb_resampler_ccf(
            self._samples_per_symbol, self.rrc_taps)

流程位置

复制代码
比特 → 映射 → 符号 → [pfb_arb_resampler @ sps] → USRP/DAC
                      ↑ RRC 内置,同时完成 sps 倍上采样
  • RRC taps 按 nfilts=32 为虚拟采样率设计(见注释),与 PFB 的 32 相结构匹配
  • skiphead 去掉 FIR 群时延引入的瞬态

5.2 接收链:多级速率变换

典型接收机:

复制代码
USRP (高 fs)
  → [可选 rational_resampler 或 pfb_decimator]  粗降到几 MHz
  → AGC / FLL
  → symbol_sync (内部 mmse_resampler_cc)         细调 TO
  → 判决
  • 粗匹配rational_resampler(3,2)pfb_decimator_ccf
  • 细同步mmse_resampler_cc + Gardner / PFB 时钟同步(qa_pfb_clock_sync.pypfb_arb_resampler 做 RRC 匹配滤波)

5.3 分组 / 突发通信

gr-digital/examples/packet/tx_stage6.grc

复制代码
burst_shaper → pfb_arb_resampler → fir_filter → USRP

突发成形后按信道带宽重采样,再经额外 FIR 整形。

5.4 多载波 / 频谱分析

python 复制代码
from gnuradio.filter import pfb
ch = pfb.channelizer_ccf(numchans=8, oversample_rate=2)

8 路并行子信道,每路 (f_s/8),用于多信号并行解调或窄带 DDC。

5.5 工程选型建议

场景 推荐块
(L/M) 固定有理数 rational_resampler
整数 (L) 上采样 + 自定义 taps interp_fir_filterpfb.interpolator_ccf
整数 (M) 下采样 + 选频 pfb.decimator_ccf
任意实数比 + 抗混叠 pfb.arb_resampler_ccf
符号时钟跟踪 mmse_resampler_cc
多信道并行 pfb.channelizer_ccf

5.6 完整 Python 流图示例

参考 gr-filter/examples/resampler.py

python 复制代码
rerate = fs_out / fs_in
taps = filter.firdes.low_pass_2(32, 32, 0.25, 0.1, 80)
self.resamp = filter.pfb.arb_resampler_ccf(rerate, taps, flt_size=32)
self.connect(src, head, self.resamp, sink)

语法知识汇总

层级 知识点
GNU Radio 块模型 sync_block / sync_interpolator / general_work + forecast + consume_each
C++ 模板 rational_resampler<IN_T,OUT_T,TAP_T> 多类型导出
Kernel 层 kernel::fir_filterkernel::pfb_arb_resampler_* 可被 block 复用
Python filter.pfb.* hier_block2 包装;firdes / optfir 设计 taps
性能 VOLK SIMD、set_output_multiple 批量对齐
线程安全 scoped_lock + d_updated 延迟重装 taps

小结

GNU Radio 多速率处理的统一思想 是:用 Noble 恒等式 把"插零→FIR→抽取"折叠成 多相 FIR 阵列 + 指针调度 ;有理数倍用 rational_resamplerctr 状态机,任意实数倍用 pfb_arb_resampler 的 (N/D) 步进 + 差分线性插值,符号级微调用 mmse_resampler 的 (\mu) accumulator。抗混叠/抗镜像由 firdes::low_pass 或 Python pfb.create_taps 按 (r) 与 Nyquist 约束自动(或手动)设计。

相关推荐
玖釉-2 小时前
下一个排列:从字典序到原地算法的完整推导
数据结构·c++·windows·算法
IronMurphy2 小时前
【算法五十】62. 不同路径
算法
影寂ldy3 小时前
C#一维数组
算法
过期动态3 小时前
【LeetCode 热题 100】移动零
java·数据结构·算法·leetcode·职场和发展·rabbitmq
计算机安禾4 小时前
【算法分析与设计】第10篇:下界理论与NP完全性初步
大数据·人工智能·算法
水木流年追梦5 小时前
大模型入门-大模型分布式训练2
开发语言·分布式·python·算法·正则表达式·prompt
sali-tec5 小时前
C# 基于OpenCv的视觉工作流-章78-KRT测量
图像处理·人工智能·数码相机·opencv·算法·计算机视觉
菜菜的顾清寒5 小时前
力扣HOT100(32)二叉树的中序遍历
数据结构·算法·leetcode
x2c5 小时前
数据结构:线性表中链表的建立和基本操作(C)
算法