4、GNU Radio入门:架构、安装与第一个流图
好,咱们进入正题。GNU Radio,说白了就是软件无线电界的「乐高积木」。你不需要懂底层硬件怎么折腾,只需要把现成的信号处理模块拖拽连接,就能搭出一个完整的通信系统。我在做航空电台原型验证时,80%的算法验证都是靠它完成的。
4.1 GNU Radio架构:到底长什么样?
先看整体架构。GNU Radio分三层:
- 底层(C++核心):负责高性能计算。所有信号处理模块,比如滤波器、FFT、调制解调,都是用C++写的。为什么?因为快。你想想看,一个40MHz的采样率,每秒要处理4000万个样本,Python扛不住。
- 中间层(SWIG胶水):把C++模块包装成Python可调用的接口。说白了就是翻译官,让Python能指挥C++干活。
- 顶层(Python + GRC):用户交互层。你可以写Python脚本,也可以用GNU Radio Companion(GRC)图形界面拖拽画流图。
核心概念:流图(Flowgraph)
流图就是信号处理管道。数据从源端(比如声卡、文件、USRP)流入,经过一系列处理模块,最终到达宿端(比如示波器、频谱仪、文件输出)。每个模块叫Block,Block之间用边(Edge)连接,数据以「样本流」的形式传递。
我在项目中遇到过一个问题:两个模块的采样率不匹配,结果出来的信号全是乱的。后来才意识到,GNU Radio不会自动帮你做采样率转换,你得手动插一个「重采样」模块。嗯,这个坑我替你们踩过了。
4.2 安装与编译:别怕,有手就行
安装GNU Radio,我建议用包管理器。Ubuntu/Debian用户直接:
sudo apt update
sudo apt install gnuradio
Fedora用户:
sudo dnf install gnuradio
如果你想从源码编译——我个人习惯这么做,因为可以定制模块——那就麻烦一点:
git clone https://github.com/gnuradio/gnuradio.git
cd gnuradio
mkdir build && cd build
cmake ..
make -j4 # 4核编译,根据你的CPU调整
sudo make install
sudo ldconfig
注意:从源码编译需要安装一堆依赖。建议先运行 sudo apt build-dep gnuradio 把依赖一次性装好。我曾经因为漏了libvolk库,编译到80%报错,气得我直接去泡了杯咖啡才回来重编。
装完后验证一下:
gnuradio-config-info --version
如果输出版本号,恭喜你,环境搭好了。
4.3 第一个流图:信号源 + 示波器
咱们来画第一个流图。打开GRC:
gnuradio-companion
你会看到一个空白画布,右侧是模块列表。按以下步骤操作:
- 添加信号源:在右侧搜索「Signal Source」,拖到画布上。双击设置参数:
- Type:float(浮点型)
- Waveform:Cosine(余弦波)
- Frequency:1000(1kHz)
- Amplitude:1
- 添加示波器:搜索「QT GUI Time Sink」,拖到画布上。参数保持默认。
- 连接:从Signal Source的输出端口(右侧小圆点)拖一根线到QT GUI Time Sink的输入端口。
- 运行:点击顶部绿色三角按钮。
你会看到一个跳动的正弦波。嗯,这就是你的第一个软件无线电程序。
小技巧:如果波形不动,检查一下采样率。Signal Source的采样率默认是32kHz,示波器也是32kHz,两者必须一致。我曾经把信号源设成32k,示波器设成320k,结果波形像心电图一样乱跳。
完整的流图代码(Python)长这样:
from gnuradio import gr, blocks, qtgui
from gnuradio.eng_option import eng_option
from gnuradio.filter import firdes
import sys, sip
class my_top_block(gr.top_block):
def __init__(self):
gr.top_block.__init__(self, "First Flowgraph")
# 信号源:1kHz余弦波
self.signal_source = blocks.sig_source_f(
sampling_freq=32000, # 采样率32kHz
waveform=blocks.GR_COS_WAVE,
freq=1000, # 频率1kHz
amp=1, # 幅度1
offset=0
)
# 示波器
self.sink = qtgui.sink_f(
fftsize=1024,
wintype=firdes.WIN_BLACKMAN_HARRIS,
fc=0,
bw=32000,
name="示波器",
plotfreq=False,
plotwaterfall=False,
plottime=True,
plotconst=False
)
# 连接
self.connect(self.signal_source, self.sink)
def main():
tb = my_top_block()
tb.start()
input("按回车退出...\n")
tb.stop()
if __name__ == "__main__":
main()
你直接运行这个Python脚本,效果和GRC画出来的一样。我个人习惯先用GRC快速验证,再转成Python脚本做二次开发。
4.4 避坑指南
- 采样率必须匹配:源端和宿端的采样率不一致,波形必乱。这是新手最容易犯的错误。
- 数据类型要一致:float模块只能连float模块,complex连complex。GRC里端口颜色不同就代表类型不同,别硬连。
- 流图跑起来后别乱点:运行中的流图如果突然断开连接,GRC可能会崩溃。先停止再修改。
我曾经在调试一个航空电台的FM解调器时,因为采样率设错了,解出来的音频全是「嗡嗡嗡」的噪声。排查了整整一下午,最后发现是信号源的采样率比后续模块高了10倍。从那以后,我每次建流图第一件事就是检查采样率。
好了,这一章就到这儿。下一章咱们会深入GNU Radio的模块开发,教你写自己的C++信号处理模块。到时候你会明白,为什么GNU Radio能成为软件无线电领域的「瑞士军刀」。