GNU Radio代码框架简介
1、GNU Radio框架

图1 gnuradio框架
2、GNU Radio系统层次结构图

图2 gnuradio系统层次结构
3、GNU Radio软件架构
图3 gnuradio软件架构
4、代码阅读
4.1 block信号处理基类

图4 gnuradio信号处理类(C++)
4.2 block应用关系

图5 gnuradio block应用
上图中,箭头表示引用关系,如通过调用block::detail()可以得倒block_detail实例对象的指针。
block主要关注数据的处理、运算和生成;
block_detail主要关注block的输入、输出及连接关系等;
buffer主要关注buffer的分配、数据的写入及交换等;
buffer_reader主要关注buffer的读取。
每个block的输出可以连接到多个block的输入,两个block之间的连接(或者说是数据交换)是通过buffer类的对象完成的。
从buffer的角度来看,每个buffer都与一个block的输出和多个block的输入相关联,可以看成是单写入多读取的环形缓冲区。
对于一个指向buffer对象的指针buf来说,通过buf->link()可以获得与之关联的上游block指针(该block向buffer中写入);同时,该buffer还暴露出若干个buffer_reader对象指针,用于下游block的读取,每个buffer_reader各自通过link()成员函数与单个下游block相关联。通过调用全局函数buffer_add_reader可以为buffer绑定一个新的reader。
4.3 block调度运行机制
一个系统由多个block连接而成,并形成一个top_block,通过调用top_block的run()成员函数,gnuradio启动一系列的线程,并通过调度机制将来自于源的数据顺序经过各个block处理,最终送到sink。

图6 gnuradio调度流程


top_block持有一个指向类top_block_impl的数据成员d_impl,进入top_block_impl类的start函数查看:

上述代码执行若干检查后,最后调用阴影部分的代码创建gnuradio的调度器,接着我们继续查看make_scheduler()函数:


代码可以看出,首先检查是否设置了linux的环境变量GR_SCHEDULER,如果没有则使用默认的TPB(thread per block)调度器,否则根据环境变量使用所需要的调度器。
TPB(default):multi-threaded scheduler
STS:single-threaded scheduler
默认情况下,调用scheduler_tpb::make来创建调度器。

该代码中,对flowgraph中的block进行排序,系统按照排列的结果,逐个block进行调度。最重要的是最后阴影部分的代码,thread_body_wrapper是一个仿函数类,该类用来封装线程的主体函数,其构造函数第一个参数为实际要执行的函数体。该部分代码中,这个参数是tpb_container类的对象。
tpb_container类的定义如下:

对于每个block来说,线程的入口就在tpb_thread_body body(d_block, d_max_noutput_items);每个线程的工作步骤有以下几步:
1、每个block对应的线程都有一个while(1)循环
2、循环内处理各种消息并运行关键处理函数run_one_iteration()
3、run_one_iteration()检查是否有足够多的输入数据和足够的输出空间
4、如果是,调用general_work()函数完成block的功能
5、否则相应的返回BLKD_IN,BLKD_OUT


可以看出,run_one_iteration()是线程运行的主体,包含了模块的主要功能。
其主要流程如下:
总的来说,该函数针对block的每个输出端口,检查各自对应的buffer的剩余空间,从而获得block可以输出的items数目。如果没有用于输出的足够的空间,则返回BLKD_OUT(输出阻塞);同时,根据前面计算出的输出items数目,计算出所需要的输入items数目(通过forecast函数),综合检查输入的items数目是否满足要求,不满足则返回BLKD_IN;如果输入的items足够,也有足够的空间存放输出的items,则执行block::general_work()完成模块的功能。
5、代码调试
5.1 日志
在block_executor.cc文件中,将#define ENABLE_LOGGING 0修改为#define ENABLE_LOGGING 1,即打开日志的使能开关,就可以看到各个block在运行中的各步骤打印信息。
5.2 block的GDB调试
block的调试主要包含以下几个步骤:
(1)编译模块代码 cmake的时候,按照以下命令执行
cmake .. -DCMAKE_BUILD_TYPE=Debug
(2)在生成的py文件最前面加上
import os
最后面的 if __name__ == '__main__':中,main前增加以下两行:
print "pid=%d" %(os.getpid(),)
raw_input()
(3)如果gdb中出现设置断点提示make breakpoint pending on future shared library? 则按照下面步骤操作:
打开文件:sudo文件:sudo gedit /etc/sysctl.d/10-prtace.conf
修改kernel.yama.ptrace_scope=0
然后执行service procps restart
(4)在终端中执行python *.py 回车后显示pid,然后在新的终端中“gdb -p pid”进入gdb设置断点后,在显示PID号的终端上再次回车启动流图运行,进入断点调试。
6、缩略语
| 简写 | 全称 |
| SWIG(Simplified Wrapper and Interface Generator) | 简化封装接口生成器 |
| SDR(Software Defined Radio) | 软件无线电 |
| USRP(Universal software Radio Peripheral) | 通用软件无线电外围设备 |
| GPP(General-Purpose Processors) | 通用处理器 |
| TPB(thread per block) | 每模块线程 |
| STS(single-threaded scheduler) | 单线程调度 |
全部回复 (0)
cll
社区创作者
