GNU Radio是一个设计框架,用户可以用来设计、模拟和部署的真实高效无线电系统。这是一个高度模块化,自带flowgraph
流程图,集成处理各种信号的库,可以很方便的将各个模块结合到一起来处理复杂的信号。
GNU Radio已经被广泛应用于无线电世界,可以用电脑软件处理包括音频处理、移动通信、卫星跟踪、雷达系统、GSM网络、数字信号广播等等。
GNU Radio 硬件平台本身并不是非常特殊的硬件,也没有包含现成的通信协议和通信标准,比如802.11、Zigbee、LTE等,但它可以用于开发并实现任何频率、任何协议的无线电通信。
曾经无线电通信方面的开发,工程师需要设计用于检测特定信号的特定设备,设计专用IC用来解码或者编码特定信号,才能进行数据通信和传输,整个调试过程成本很高而且很费时间。
Software-Defined Radio 软件无线电SDR使得开发变得非常简单,只要有钱买一块SDR板子并且有能力使用它,就可以用算法在计算机的软件上处理无线电信号。
当你的电脑连接了软件无线电设备,编写一个程序,用于串联各个算法和数据,每次都从头开始写你会觉得非常麻烦:为什么每次都要重新实现一个标准的过滤器?为什么要关注数据在不同处理模块之间的通信?为什么不能用高度集成的模块进行实现,非得自己写?而且怎么才能让程序能更好的兼容各种平台,比如一个功率只有几W的嵌入式设备?
在GNU Radio中,这是一个集成度非常好的信号处理模块框架,这里面封装了非常多的可以重复使用的模块,而且扩展性非常出色,提供了标准算法的扩展库,并且在各种平台上都有很大程度的优化,它还有很多很多例子让你可以很轻松的上手。
作为一个软件框架,GNU Radio可以让通用的计算机平台进行数字信号处理。
用软件处理信号就是信号是数字信号,但什么是数字信号?
为了更好的理解,我们看一个很常见的信号
处理场景:用手机录制语音并传输
从物理层面上讲,声音信号的信号
:由人的声带产生的空气震荡波,这也是一个时变
的物理量,比如空气震荡,就可以被定义为信号
:
当声波到达麦克风,麦克风将变化的空气震动转成电信号,一个可变的电压:
现在就是电信号了,我们在处理音频信号时,因为现在是模拟信号,计算机还不能处理,计算机只能处理数字信号,这有两点特征:
只能是有限数量的数值
只有时间是变量
数字信号能够用一个数字序列来表示,我们通常称作样本,样本之间固定的时间间隔决定了采样频率。
我们通常将物理量(电压)转换成数字样本的采样方法,由模拟/数字转换器(ADC)
来完成,当然也有反向的数字/模拟转换器(DAC)
,可以将数字信号转换成模拟信号。
现在我们有了一个数字序列,就可以用电脑来做任何事,比如可以用数字滤波器,压缩、语音识别或者用数字通信链路传输它。
无线电信号和声音信号是同样的原理。
一个信号,也就是电磁波,同样可以通过天线将它转换成一个变化的电压。当然,这个电信号可能是在几M甚至几千M的载波
上。
可以利用不同类型的接收机来接收,例如超外差接收机、直通机或者中低频接收机等。能用获得软件无线电支持的商业设备(比如连到声卡的专业无线电接收机),也能用低成本消费者考虑的数字电视接收机(比如著名的RTL-SDR)
为了处理数字信号,可以将整个流程的各个处理阶段(滤波、校正、分析、检测等等)作为模块,可以用简单的指示箭头表示流程方向:
当创建一个信号处理应用,也就是一个完整的块处理图,这样的图在GNU Radio中称为flowgraph
流程图。
GNU Radio就是开发这些处理模块,创建flowgraph
,用来处理无线电信号的应用程序框架。
作为GNU Radio用户,可以将现有的模块组成一个高层次的flowgraph
流程图用来处理非常复杂的数字信号,当整个模块和连线构建完毕,点击运行
就能自动处理信号。
GNU Radio带有非常非常多的模块,这里就贴几个流行的类别和他们的成员:
Constant Source
Noise Source
Signal Source
…
AM Demod
Continuous Phase Modulation
PSK Mod / Demod
DPSK Mod / Demod
GMSK Mod / Demod
QAM Mod / Demod
WBFM Receive
NBFM Receive
…
Constellation Sink
Frequency Sink
Histogram Sink
Number Sink
Time Raster Sink
Time Sink
Waterfall Sink
…
Abs
Add
Complex Conjugate
Divide
Integrate
Log10
Multiply
RMS
Subtract
…
Channel Model
Fading Model
Dynamic Channel Model
Frequency Selective Fading Model
…
Band Pass / Reject Filter
Low / High Pass Filter
IIR Filter
Generic Filterbank
Hilbert
Decimating FIR Filter
Root Raised Cosine Filter
FFT Filter
…
FFT
Log Power FFT
Goertzel collapse(Resamplers)
Fractional Resampler
Polyphase Arbitrary Resampler
Rational Resampler collapse(Synchronizers)
Clock Recovery MM
Correlate and Sync
Costas Loop
FLL Band-Edge
PLL Freq Det
PN Correlator
Polyphase Clock Sync
…
通过这些模块,很多标准处理流程,比如正交信号、同步、测量和可视化,都可以通过合适的模块连入流程图就可以完成。
此外,你还可以自己编写模块,比如一些逻辑处理相结合或者新的自动化处理流程,或者用自己编写的模块进行数据输入或者输出操作也可以。
所以,GNU Radio是主要用于信号处理的开发框架,它配备了很多标准模块库,供给开发人员建立各种通信或者处理系统。其实,GNU Radio本身并不是一个软件,你不能直接用它产生结果,它只是用来做准备工作,之后用户再用它来做自己想要做的事情,尽管它本身包含了很多案例,但可以只把它看成一堆模块就行了。
安装方法有很多种,GNU Radio也支持Win、Linux和Mac,但这里只介绍最稳定的Linux下的安装。
如果图省事,可以用发行版的源中带的gnuradio包,但GNU Radio更新很快,发行版提供的软件包很可能是过时的老版本(至少大版本一定要最新,也就是版本号的前两位),不然遇到了问题时候在官方的邮件列表支持中很少会有人回答。
ubuntu/debian/kali:
1 2 |
fedora:
1 2 |
对于其他的发行版,使用相应的包管理工具安装就行。
如果要更新的版本或者在特殊平台的安装,可以看看GNU Radio官方源
pybombs是封装好的自动程序,会自动下载源码包然后编译安装,过程中会自己下载依赖包,比自己下载源码编译安装省事很多。官网https://github.com/gnuradio/pybombs
因为某些原因,安装GNU Radio时候最好,拨VPN或者用SS代理都行
安装pip和pybombs
1 2 3 4 5 6 |
获取安装库
1 2 3 4 |
安装到/usr/local
目录,安装目录下会有.pybombs
目录,配置文件保存在用户目录的.pybombs
文件夹
1 2 |
默认会安装很多组件,如果安装到中途断了,再用上面的命令无法执行怎么办?
可以删掉/usr/local
下的.pybombs
,然后整个重新安装,提示配置文件直接覆盖就行,如果只是个别组件导致的中断,可以先用pybombs
卸载该组件,再重新安装,比如安装rtl-sdr
出错:
1 2 3 4 |
安装完成后检查组件,再加上一些没有自动安装的组件:
1 2 |
glog
和gflags
时候会因为autoconf
版本较高导致编译错误,所以用pybombs
将几个依赖包先装上再安装gnss-sdr
即可: 1 2 3 4 |
安装完成后,执行环境变量脚本,然后执行gnuradio-companion
就能打开界面,gnuradio-config-info
可以查看版本修改配置:
1 2 3 4 5 6 7 8 |
在开始之前,先说说后面2-7小节,每个图片、grc文件和模块文件,都已经放到了github,可以下下来作为参考,用两个目录分区分自己的目录和参考目录:
1 2 3 4 |
用git下载示例
1 2 |
之后如果找不到相应案例的教程,就在github
上点击鼠标查找
下载后进入目录编译安装即可:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
如果要卸载,也很简单:
1 2 3 4 5 6 |
在第一章我们了解到GNU Radio可以用来开发无线电系统中的软件,而不是完全运行在硬件上。在这里我们介绍如何使用GNU Radio的图形化工具GNU Radio Companion(GRC)
来创建不同的音调,我们可以使用GRC
可以图形化创建Python文件,而不是在代码字符中编写,GRC
是为了简化GNU Radio的使用而存在的。
首先我们先了解下GRC
界面,它由库
、工具栏
、终端
和工作区
四部分组成:
库里面的模块太多了,找一个模块一个个看太慢,点击工具栏的放大镜
图标或者Ctrl+F
快捷键,就能在库
里面搜索自己需要的模块:
找到了自己想用的模块,比如QT GUI Time Sink
,单击
模块名然后拖到工作区或者直接双击
模块名也能将模块添加到工作区。
双击
工作区的模块或者在工作区的模块上右键
-属性
,进入到模块属性界面,比如工作区的Option
模块:
对于不同的任务可以修改不同的属性,当我们修改参数后,前面的名称就会变成蓝色,表示内容已经修改,但还没有保存,Canvas Size
表示的是工作区大小,比如改成300,300
,然后OK
就可以看到工作区变得特别小,用Ctrl+Z
和Ctrl+Y
可以撤销
或者重做
,或者点击工具栏按钮也可以。
我们打开Option
模块属性中的说明,上面有说到,定义的ID
就是模块保存python文件的名称。
如果我们删除ID
内容,再Apply
应用,可以看到下面的错误提示信息,整个模块的标题变成红色,然后ID
也变成红色,提示我们哪里有错:
这里我们改成tutorial_two_1
,确保Generate Options
设置为QT GUI
,OK
保存:
设置完成后,点击工具栏或者Ctrl+S
快捷键保存,将GRC
流程图保存为t1.grc
,然后点击工具栏的或者F5
生成Python
文件,调用*Generate* flowgraph
,在终端可以看到输出:
因为GRC
只是一个图形化编辑器,正常的GNU Radio是使用Python
进行编程的,使用GRC
图形化编辑更加直观,F5
会在GRC
保存的目录生成以ID
为名的py
文件,之后点击工具栏的或者F6
运行,其实就是执行之前生成Python
文件,调用*Execute* flowgraph
,可以看到终端输出:
在桌面会出现这个程序生成的QT窗口:
直接关闭窗口,或者点击工具栏按钮或者F7
快捷键可以关闭窗口。
关闭GRC
编辑器后,直接执行生成的tutorial_two_1.py
也能打开这个QT窗口。
1 2 |
所以GRC
的本质只是一个Python的IDE。事实上,在模块属性或者模块的变量输入框都是Python
生成的,也就是说,我们可以直接将Python
调用作为输入,比如说调用一个numpy
或者其他的GNU Radio函数作为输入。一个很常见的应用就是调用filter.firdes
滤波器设计工具来构建我们自己的滤波器。
还有就是,在设置窗口中,不同颜色的数据输入区域对应不同类型数据,在Help
->Type
可以看到对应:
为了更容易理解,我们构建一个Flowgraph
,首先按照下图将各个模块添加到工作区,然后按照图示修改模块属性,再点击工具栏或者直接直接点击模块的突出小方块进行连接。
这里先说下Throttle
模块,具体作用是啥以后再说,现在你只需知道这个模块可以确保这个Flowgraph
不会将你的CPU
跑到100%
,防止你的电脑跑死。
工具栏上的图标功能依次如下:
新建
打开
保存
关闭
连线
打印
剪切
复制
粘贴
删除
撤销
重做
查错
生成
执行
关闭
逆时针旋转90°
顺时针旋转90°
启用模块
禁用模块
跳过模块
隐藏禁用模块
查找
重载
打开源码
如果有错,点击Generate
按钮会在终端输出log
,包括错误和警告,简单的错误,工具栏查错按钮会显示出来,生成和执行按钮都变灰无法使用,点击查错按钮能够看到错误信息,比如我们在Option
选项中设置为WX GUI
,但我们的工作区放了一个QT GUI
的模块,就可以看到模块标题变红,然后查错按钮显现,点击出现错误信息:
执行程序,可以看到两个正弦波输出,上面的Re
和Im
可以切换实数
和虚数
的输出显示。鼠标左键在图形上圈选可以放大,鼠标右键缩小。
这是一个复杂的e jwt傅立叶变换正弦波,这里就不讨论复杂的信号了,只讲简单的操作。
在模块的选项可以看到数据类型,如果将Complex Float 32
数据类型改为Float
,可以看到输入输出的颜色接口变成了橙色,
如果至少变更了两个模块的数据格式,没有做到数据格式统一,就会报错,可以看到错误信息可出错地方:
全部修改完毕后,命名为tutorial_two_2
,生成后执行,可以看到就只有一个实数
正弦波了:
现在已经能够创建flowgraphs
流程图了,那么可以开始使用flowgraphs
研究GNU Radio中有用的功能了。
创建一个Flowgraph
流程图:
Options
,ID
为tutorial_two_3
QT GUI Tab Widget
,ID
为tab
,Num Tabs
为2
,Label 0
为Time
,Label 1
为Frequency
QT GUI Range
,ID
为samp_rate
,Default Value
为5*freq
,Start
为0.5*freq
,Stop
为20*freq
,Step
为200
Variable
,ID
为freq
,Value
为2e3
Signal Source
,Frequency
为freq
,Waveform
为Sine
Throttle
,Sample Rate
为f32e3
(为什么要这么多后面会讲)
QT GUI Time Sink
,GUI Hint
为tab@0
,QT GUI Frequency Sink
,GUI Hint
为tab@1
这个流程图会绘制正弦波,并且有时间和频率滑块,使我们在保持频率不变的情况下能够动态调整采样率。
我们勾选4ms
的波形,可以看到在10k
采样率下的正弦波行非常糟糕,我们可以通过移动滑块或者直接改输入框修改采样频率,不需要修改流程图重新生成,比如调到最大40k
,然后再用鼠标选4ms
波形:
在10k
采样率,我们可以得到初始频率2 KHz
,提高采样率,我们仍然会在2 KHz
得到高峰,奈奎斯特采样定理
告诉我们,如果采样率低于2*original_signal_freq
,波形将会失真,我们将无法再现信号波形,虽然教科书上这么说,但我们不相信,让我们将采样率下调到3.85 KHz
,看看时间波形是什么?
看起来不像一个正弦波,而且我们的眼睛会被欺骗,让我们转向FFT得到其他频率。
我们看到的频率不再是2 KHz
,也不像我们输入的信号,而且,也高于奈奎斯特率0.15 KHz
,这是因为信号具有折叠性
,所以0.15 KHz
低于实际频率。这就是利用GNU Radio 模拟DSP进行的一次信号完整性检查。
更详细的解释请看采样率和FFT快速傅氏变换
现在我们已经知道了奈奎斯特,也知道了问题是怎么出现的,如果我们不考虑速率的话,可以了解下Resampling重采样
。重采样用于改变信号的采样率,用于满足另一个系统的要去,比如声卡的采样率。重采样有两个重要方面:插值
和抽取
,插值
增加采样点,抽取
删除采样点。这里我们用一个例子来理解它们:
Options
,ID
为tutorial_two_4
Variable
,ID
为samp_rate
,Value
为48e3
Signal Source
,Frequency
为1e3
,Waveform
为Cosine
Rational Resampler
,都改成Float-->Float
,一个Interpolation
为4
,一个Decimation
为4
Throttle
,Sample Rate
为samp_rate
QT GUI Time Sink
,GUI Hint
清空,Number of Inputs
为3
,Config
栏目打开,Line 1 Label
为Normal
,Line 2 Label
为Interpolated
,Line 3 Label
为Decimated
默认情况下,Line 1
为蓝色
,Line 2
为红色
,Line 3
为绿色
,颜色和线宽都可以在Config
中调整,让我们看看输出的波形是咋样的?
我们可以看到,通过重采样/抽取/插值的方法,可以改变原始信号的采样速率。所以,我们在一个非标准采样率的信号上进行标准采样率的信号采集,可以得到比原始信号更高或者更低的频率。
当我们设计硬件接口或者调制解调器的时候如果想要提高工作效率,这一点非常重要。
#####注意:尽管这个程序能运行并得出结果,但可能会意外停止或者假死,因为QT Sink
不允许同时绘制不同采样率信号波形图,所以最好是每一个采样率的信号都用一个单独的QT Sink
。这里只是一个简单样例用于对比。
如果要显示多个图形有很多种方法,比如说我们有一个大屏幕,我们可以用GUI Hint
中的Grid Position
网格表示法来划分,或者给每个输入创建一个不同的sinks
。
GRC提供了好几种图形sink
和图形空间来创建wx-gui
流程图(scope sink, fft sink, number sink, waterfall sink, constellation sink, slider control, and chooser control
) ,每个图形元素都有精确定位的网格位置参数。
网格位置参数形式是4个整数(行,列,行宽,列宽)。行和列指定图形元素左上角的位置,最小位置0,0
,指定网格的左上角。
如果参数留空,这个网格参数指定的图形元素会按照从上到下竖直排列,大小为前面图形最大大小,如果不想让你的图形竖着排列,请不要将参数留空。
行宽和列宽指定拉伸程度,或者说图形所占据的行宽和列宽,行宽从行号开始向下占据的总行数,列宽从列号开始向右占据的总列数,因此,行宽和列宽最少是1,1
,占据一个网格单元。每一
比如说,用户希望放置一个位2行2列的滑块,占据2行4列,再放一个文本框在5行0列,占据4行2列:
在WX GUI
中新的图形的行号和列号必须大于前一个图形行+行宽
和列+列宽
,不然会报错。QT GUI
中的GUI Hit
属性类似,但QT GUI
可以被覆盖,也就是说,行号和列号设置不当不会报错,只会让结果图形出现覆盖现象。
QT GUI Time Sink
的GUI Hit
分别为:0,0,2,2
1,1,3,2
0,1,1,2
,运行效果如下:
有很多种方法用于改变参数变量比如Chooser
,也有不同的信号显示方法比如Waterfall Sink
,现在我们从最基础的开始学习,我们可以选择任何模块,然后看它的文档,再找出使用的方法。
当我们在mail list
被问到怎么用Probe Signal Block
时候,可以先添加一个Porbe Signal
模块,将ID
设置为probe_signal
,然后将数据格式设置为Float
,请看下面的属性和文档:
通过文档可以知道,这个模块的函数是level()
,需要和Function Probe
模块一起使用:
首先,我们同样要给它一个ID
,叫probe_var
,再看看文档,要求Block ID
必须是流程图中另一个模块的ID
,所以我们将Block ID
设置为Probe Signal
模块的ID
也就是probe_signal
。文档还是告诉我们,Function Name
必须是那个模块的类``class method
,而且这个函数需要传递参数,如果没有参数,就将函数参数位置留空,前面看到Probe Signal
模块的函数是level()
,没有参数,所以这里就将Function Name
设置为level
即可。
如果加上Signal Source
和Throttle
,然后执行上面的流程图会发现什么输出都没有,弹出的窗口里面是空的。所以,我们还需要一种方法来显示数据。我们不能再加一个Sink
,因为Probe Signa
这个模块本身就是一个Sink
,这时候我们就需要看看QT
能用的显示模块:
我们可以看到一堆Sink
,但这里没法使用,所以我们看的GUI
显示有8个模块可以选择,前后那6个看着像数据输入用的,这里也不用(可以自己都试试),所以这里使用QT GUI Entry
和QT GUI Label
来显示数据,我们将Default Value
改成Function Probe
模块的ID
,也就是probe_var
,数据格式改为Float
,输出结果如下:
这里我们讲一下,为什么所有的流程图都要用Throttle
模块,先看下用与没用Throttle
模块的正弦波流程图上的系统资源监控对比:
CPU i7-4790,分给虚拟机双核四线程
很明显可以看到,没有连接硬件的情况下,CPU基本跑满了,在整个流程图中,只需要有一个Throttle
模块就行,哪怕有很多个sources/sinks
。Throttle
模块的作用可以理解为限速:速度越高我们流程图运行的速度越高,速度越低流程图运行的速度越低。我们如果将Throttle
模块的采样率调到1e10
比调到1e8
(CPU配置低就调的更低一些防止跑死)的CPU负载要高很多。
再就是Throttle
模块会强加给硬件一个吞吐率的限制,所以在使用硬件的时候,不要使用Throttle
模块。
1 2 3 4 5 6 7 8 |
之前说过很多硬件需要正确的采样率才能正常运行,这里讲讲采样率不匹配的例子。我们建立如下流程图:
Chooser
,配置3
个不同采样率的采样,分别为48000
,24000
和160000
,数据类型Float
Variable
,ID
为audio_rate
,Value
为48000
Signal Source
,Sampling Rate
都为samp_rate
所有的sinks (audio, time, frequency)
,Sampling Rate
都为audio_rate
我们可以看到没有Throttle
模块,并且注意到,虽然将audio音频采样率设置为变量,但在流程图运行时是无法改变的,这是因为这个xml文件没有合适的回调来支持这个功能(具体在下一章会讲)
当我们将采样率设置为48000
时候,可以听到熟悉的电话拨号音,也能看到FFT频率在350Hz到440Hz之间。
如果采样率不管是高于或者低于音频采样率,使声卡收到的信号频率较高或者较低,都无法听到拨号音。
所以,请确保采样率总保持一致,否则会导致我们的数据中存在频率的升降。关于采样率更多的介绍可以看附录。
之前学习了这么多,现在我们可以结合probe
,soundcard
以及QT Instrumentation Sinks
制作一个会唱歌的正弦波,流程图如下:
Variable
,ID
为samp_rate
,Value
为48000
Porbe Signal
,ID
为probe_signal
Signal Source
,Sampling Rate
都为samp_rate
,一个Waveform
为Triangle
,频率为100e-6
,另一个频率为20e3*probe_var
,Amplitude
为500e-3
QT GUI Tab Widget
的ID
改为tab
,另外两个QT GUI Sink
的GUI Hint
分别为tab@0
和tab@1
其他如图设置即可,在这里,我们能够通过观察这个流程图来创建它(仅仅只是一个测试)。不要忘了修改Audio Sink
的采样率,因为声卡只能输出特定的频率。当设置好全部,打开音箱,运行,看看有什么效果?瀑布图是咋样的?
在学习python之前,先要学习GRC,这一章讲的就是GRC的用法。
http://gnuradio.org/redmine/projects/gnuradio/wiki/Guided_Tutorials