多速率信号处理源码深度剖析
模块总览
| 块 / 类 | 文件 | 能力 |
|---|---|---|
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}
]
经典三阶段结构:
- 上采样 (L) 倍:插入 (L-1) 个零 → 频谱压缩并产生 (L-1) 组镜像
- 抗镜像/抗混叠 FIR:截止频率 (\le \min(f_{s,in}/2,, f_{s,out}/2))
- 下采样 (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 化简
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.3general_work:多相指针调度
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 接口调用
fromgnuradioimportfilter# L=3, M=2 → 输出率 = 输入率 × 3/2# taps 为空则自动设计 Kaiser 低通rr=filter.rational_resampler_ccc(interpolation=3,decimation=2,taps=[],# 或自定义 tapsfractional_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) 选过渡带:
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
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 与有理重采样共用同一拆分逻辑:
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:
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) 信道:
// 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.py中decimator_ccf/interpolator_ccf用optfir.low_pass生成覆盖 0.4 归一化带宽的原型滤波器,并包装stream_to_streams。
fromgnuradio.filterimportpfb# 8 倍抽取,选第 0 信道,自动设计 tapsdec=pfb.decimator_ccf(decim=8,channel=0,atten=100)interp=pfb.interpolator_ccf(interp=4,atten=100)3. 多相滤波组(PFB)核心源码解析
3.1 基类polyphase_filterbank
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_ccf;d_fft_filters供长滤波器 batch 处理。
3.2 PFB 插值器
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
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.1pfb_arb_resampler:多相 + 线性插值
速率分解
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):相邻两支路输出的线性插值权重
差分滤波器(导数支路)
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,实现相邻多相滤波器之间的亚滤波器相位插值。
核心循环
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_filter、d_acc跨work()调用保持连续。
Block 包装
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)声明非整数比;forecast按noutput/rate估算输入需求。
自动滤波器设计(Python)
if(rate < 1): halfband = 0.5 * rate # 降采样:截止 ∝ 输出 Nyquist return filter.firdes.low_pass_2(flt_size, flt_size, bw, tb, atten, ...) else: # 升采样:防镜像,halfband = 0.54.2mmse_resampler:Farrow / MMSE 插值
用于符号定时恢复:输入率与输出率接近 1:1,只需微调相位 (\mu)。
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) 量化到最近步进:
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 接口示例
fromgnuradioimportfilter# 8000 → 20000 Hz,等价 rate=2.5rate=20000.0/8000.0resamp=filter.pfb.arb_resampler_ccf(rate,taps=None,flt_size=32)# 符号同步:timing error 反馈到 resamp_ratiommse=filter.mmse_resampler_cc(phase_shift=0.0,resamp_ratio=1.0+epsilon)5. 通信系统中重采样工程应用
5.1 发射链:成形 + 整数/任意上采样
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.py用pfb_arb_resampler做 RRC 匹配滤波)
5.3 分组 / 突发通信
gr-digital/examples/packet/tx_stage6.grc:
burst_shaper → pfb_arb_resampler → fir_filter → USRP突发成形后按信道带宽重采样,再经额外 FIR 整形。
5.4 多载波 / 频谱分析
fromgnuradio.filterimportpfb ch=pfb.channelizer_ccf(numchans=8,oversample_rate=2)8 路并行子信道,每路 (f_s/8),用于多信号并行解调或窄带 DDC。
5.5 工程选型建议
| 场景 | 推荐块 |
|---|---|
| (L/M) 固定有理数 | rational_resampler |
| 整数 (L) 上采样 + 自定义 taps | interp_fir_filter或pfb.interpolator_ccf |
| 整数 (M) 下采样 + 选频 | pfb.decimator_ccf |
| 任意实数比 + 抗混叠 | pfb.arb_resampler_ccf |
| 符号时钟跟踪 | mmse_resampler_cc |
| 多信道并行 | pfb.channelizer_ccf |
5.6 完整 Python 流图示例
参考gr-filter/examples/resampler.py:
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_filter、kernel::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_resampler的ctr状态机,任意实数倍用pfb_arb_resampler的 (N/D) 步进 + 差分线性插值,符号级微调用mmse_resampler的 (\mu) accumulator。抗混叠/抗镜像由firdes::low_pass或 Pythonpfb.create_taps按 (r) 与 Nyquist 约束自动(或手动)设计。
