模块总览
| 块 / 类 | 文件 | 能力 |
|---|---|---|
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 化简
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.py 中 decimator_ccf / interpolator_ccf 用 optfir.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_ccf;d_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_filter、d_acc 跨 work() 调用保持连续。
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) 声明非整数比;forecast 按 noutput/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.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 多载波 / 频谱分析
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_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:
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_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 或 Python pfb.create_taps 按 (r) 与 Nyquist 约束自动(或手动)设计。