本节摘要:本节把第 4.2 节的 Python FM 解调逐行翻译成 GRC 流图:RTL-SDR Source、RF 与 IF 滤波、WBFM Receive 模块、重采样、音频输出,外加 Variable 与 Slider 实现的实时调谐。每个模块的参数都给出推荐值与理由,并对照第 4 章代码建立"模块与代码行"的映射表。
先把 4.2 节脚本与流图模块对应起来,你会发现翻译几乎是一对一的:
| 4.2 节代码 | GNU Radio 模块 | 关键参数 |
|---|---|---|
| np.fromfile 采集 | SoapySDR Source | driver=rtlsdr,samp_rate |
| firwin 200 kHz 低通 | Low Pass Filter | cutoff 200 kHz,transition 20 kHz |
| angle 共轭乘积 | WBFM Receive | quad_rate 与 audio_rate |
| demod[::42] 抽取 | Rational Resampler | 内插 1,抽取 42 |
| 去加重 lfilter | (WBFM Receive 内置) | tau 75e-6 |
| wave 写文件 | Audio Sink | 48000 Hz |
注意去加重与鉴频在 GNU Radio 里被打包进 WBFM Receive 一个模块——工程封装常把"总是同时出现"的步骤合并。模块的 quad_rate 参数(鉴频前速率)应设为 200 kHz 左右,audio_rate 设 48 kHz,比例由模块内部的重采样自动完成。
按顺序搭建(括号内为参数要点):
运行后拖动调谐滑块:频谱窗口里整片频段横向滚动(实际是中心频率在动),听到下一个电台时停住,微调增益到音质最干净。一台可以用旋钮操作的软件收音机完成。

**故障一,无声但频谱正常。**按链路逐块排查速率:Source 2.048 MSPS、滤波后 204.8 kSPS、WBFM 输出 48 kHz——任一处断言失败(比如 WBFM 的 quad_rate 填了 2.048e6 而上游实际已是 204.8 kSPS),音频就是静音或纯噪声。速率是流图的血压,第一件事就是量它。
**故障二,声音有周期性抽搐。**这是音频设备与样本速率不匹配的典型表现:重采样后实际速率与 Audio Sink 声明的 48 kHz 有偏差,缓冲周期性空转。解法是统一用 Rational Resampler 精确换算,或在 WBFM 参数里直接对齐 48000。
**故障三,噪声底极高且不受增益影响。**八成是天线附近有强干扰源(USB 3.0 口、显示器电源),或者是过增益导致 ADC 削顶——削顶把宽带噪声"焊死"在信号里。把增益从 40 降到 25,噪声底应明显变化;不动则是环境干扰,换 USB 2.0 口或加磁环。
流图搭通后有三个自然的进阶方向:加 RDS 模块(RTL-SDR 的 FM 广播带数字副载波,台名与电台文本可解码显示);加 QT GUI Sink 的偏移参数绕开直流尖峰;把 Audio Sink 换成 File Sink 录制 WAV,做无人值守定时录音。每个方向都只加一个块——流图架构的扩展性就体现在这里。
对照一下 4.2 节的手写代码:三十行 Python 一次成型、但改参数要重跑;GRC 流图拖了十来个块、却换来实时调参与旋钮交互。两种形态是同一门技术的两种性格——理解靠前者,生产靠后者。
数值上略有差异,感知上无区别。GNU Radio 的实现是 90 度移相乘法鉴频(即正交鉴频的工程形态),与共轭乘积取辐角在数学上等价;模块内部还做了量化与查找表优化来省算力。这正是"原理与实现可以不同、输出语义一致"的典型例子。
FM 广播把左右声道的和信号(L 加 R)放主信道,差信号(L 减 R)调制到 38 kHz 的副载波上。接收端用 19 kHz 导频恢复副载波、解出差信号、加减还原左右声道——加一个 Stereo Demod 相关的模块链即可。主信道只有和信号,这就是 5.4 节的单声道输出听起来正常但无立体感的原因。
GRC 默认参数偏保守:滤波器过渡带窄、FFT 显示点数多、块缓冲大。按第 6 章账本放宽过渡带、降低显示刷新率、合并抽取级,占用通常能降一半。另外 Python 生成的流图比手写 C++ 的调度开销略高,属正常代价。
到此为止,滤波都只是"拿来就用"的模块。下一章打开滤波器本身:抽头、响应、设计工具,让你知道 Low Pass Filter 块里住着什么。