GNU Radio3.8创建OOT的详细过程(基础/C++)

2023-05-16

GNU Radio 学习使用 OOT 系列教程:

GNU Radio3.8创建OOT的详细过程(基础/C++)

GNU Radio3.8创建OOT的详细过程(进阶/C++)

GNU Radio3.8创建OOT的详细过程(python)

GNU Radio自定义模块:Embedded Python Block的使用

GNU Radio3.8:编辑yaml文件的方法

GNU Radio3.8:创建自定义的QPSK块(C++)

GNURadio 3.9 使用 OOT 自定义模块问题记录 

GNURadio3.9.4创建OOT模块实例

----------------------------------------------------------------------------------------

目录

一、什么是Out of Tree(OOT)

二、工具软件的准备

1、gr_modtool

2、代码格式

3、CMake及Make等

三、创建一个OOT模块(module)的过程

1、创建module

2、为module添加block

3、编写block的测试代码(可选,不想写可略过直接到下一步)

4、编写block的C++代码

5、使用CMake编译并测试代码

6、使自定义的block在GRC中生效

7、最后一步:安装并加载

8、卸载已安装的block

9、module 创建流程总结

四、结语


文章虽长,但他也详细丫~只要认真学总会学会的哦~首先总结下所有设计的步骤流程以供参考:

  1. 创建module:gr_modtool newmod MODULENAME
  2. 为module添加block:gr_modtool add BLOCKNAME
  3. 创建build文件夹:mkdir build/
  4. 编译:cd build && cmake <OPTIONS> ../ && make
  5. 调用测试文件:make test 或 ctest or ctest -V for more verbosity(可选)
  6. 生成yaml文件,需要时手动修改:gr_modtool makeyaml BLOCKNAME
  7. 安装:sudo make install
  8. ubuntu环境下重新加载库文件:sudo ldconfig
  9. 卸载或删除block:sudo make uninstall 或 gr_modtool rm REGEX

一、什么是Out of Tree(OOT)

OOT模块是不存在于 GNU Radio 源代码树中的 GNU Radio 组件。如果想自定义一个全新的GR模块,那么创建一个OOT就是不二之选。可以使用C++或Python来建立OOT模块,下面将逐个介绍

二、工具软件的准备

1、gr_modtool

gr_modtool是在安装GR时自带的一个模块编辑脚本,可以帮助生成模板、自动生成makefile文件等,大大减轻了模块编辑前的工作量。另外gr_modtool对代码的格式做了很多限定,因此在自定义代码块的过程中,自定义的代码部分越多则gr_modtool所提供的帮助就越少,但gr_modtool仍是着手自定义一个模块的最好选择。

2、代码格式

GR源代码树中的代码遵循一个统一的编写格式,其中的主要定义的说明及编码范式可以参考这个链接,特别注意其中的命名规范。例如本例中使用的名为 square1_ff 的 block ,注意后缀 ff 的名规则,意味着输入是 float 类型的数据,输出也是 float 类型的数据。

3、CMake及Make等

众所周知,这两个工具是编译C++代码的必备技能,再次不再赘述了哈~

三、创建一个OOT模块(module)的过程

1、创建module

以创建一个名为 mymod 的模块(一个模块可以含有很多block)为例。在目标文件夹(我这里是建了一个空文件夹 temp,注意不要在GR源代码树文件夹中创建OOT)中打开终端输入命令gr_modtool newmod mymod,会出现以下结果:

wsx@wsx:~/temp$ gr_modtool newmod mymod
Creating out-of-tree module in ./gr-mymod...
Done.
Use 'gr_modtool add' to add a new block to this currently empty module.

这时 temp 文件夹中就会多了一个名为 gr-mymod 的文件夹,打开该文件夹可以看到里面的内容如下:

在这些子文件夹中,未来所有使用C++或者其他非Python语言所编写的代码将会放到 lib/ 文件夹中。对于C++文件来说,其头文件一般放入 include/(一般是创建module时就有的基础的底层头文件)或 lib/(一般是跟创建的block有关的头文件,例如 **_impl.h 文件,后面会看到)中 。

python 相关的文件放在 python/ 文件夹中。

尽管有些GR块是使用C++编写,其仍可使用 python 进行调用并运行,在 GR3.8 中,这得益于 SWIG(simplified wrapper and interface generator) , 它会自动创建粘合代码来实现这一点。但是swig需要一些生成粘合代码的指引,这些文件都放在 swig/ 文件夹中,一般不需要进入 swig/ 目录进行编辑,gr_modtool 自动会为我们生成这些文件。

:从 GNU Radio 3.9 版本开始,python 与 c++ 的绑定不再使用swig,而是使用 pybind11 (version > 2.5.0)进行处理的,这与以前的版本有本质的不同。因此如果是使用 GR3.9 创建OOT,虽然仍然还默认生成 swig/ 文件夹,但是里面自始至终都没有文件,这时的一些粘和代码文件都放在了 python/bindings 中了。

另外,如果想让这些模块在 gnuradio-companion 中使用,则需要在 grc/ 文件夹中添加这些块的描述文件,在GR3.8版本之前是XML文件,但是在3.8及之后的版本需要YAML文件。

文档 docs/ 主要包含一些类似于readme的说明性文档。

apps/ 子目录包含与块(block)一起安装到系统的所有完整应用程序(包括 GRC 和独立可执行文件)。

目录 examples/ 可用于保存示例,这是一个很好的文档附录,因为其他开发人员可以直接查看代码以了解如何使用编写的块。

构建系统也带来了一些其他文件:CMakeLists.txt 文件(存在于每个子目录中)和 cmake/ 文件夹。cmake/ 文件夹主要为 CMake 提供有关如何查找 GNU Radio 库等的说明,需要编辑 CMakeLists.txt 文件以确保模块正确构建。

下面介绍往模块(module)中添加一个块(block)的过程及实例

该文件计算输入的一个float类型数据的平方并输出一个float型数据(根据命名规范,square1_ff代表输入是float数据输出也是float数据的块)。

我们将使这个块以及我们在本文中编写的其他块,最终出现在 mymod Python 模块中。这样我们就可以在 Python 中访问它,类似如下的操作:

import mymod
sqr = mymod.square_ff()

2、为module添加block

首先需要为这个块创建空文件,然后编辑 CMakeLists.txt 文件。可以注意到,前面使用gr_modtool创建一个新的module后terminal打印的log的最后一行的提示信息是:

Use 'gr_modtool add' to add a new block to this currently empty module.

因此,我们在 gr-mymod 文件夹中打开终端输入命令:gr_modtool add -t general -l cpp square1_ff来创建一个名为 square1_ff 的 block,结果如下:

wsx@wsx:~/temp/gr-mymod$ gr_modtool add -t general -l cpp square1_ff
GNU Radio module name identified: mymod
Language: C++
Block/code identifier: square1_ff
Please specify the copyright holder: 
Enter valid argument list, including default arguments: 
Add Python QA code? [Y/n] y
Add C++ QA code? [y/N] n
Adding file 'lib/square1_ff_impl.h'...
Adding file 'lib/square1_ff_impl.cc'...
Adding file 'include/mymod/square1_ff.h'...
Editing swig/mymod_swig.i...
Adding file 'python/qa_square1_ff.py'...
Editing python/CMakeLists.txt...
Adding file 'grc/mymod_square1_ff.block.yml'...
Editing grc/CMakeLists.txt...

命令中使用到的一些指令的含义如下(可通过命令 gr_modtool add --help 查看):

  • -t:block-type,即block的类型,可选类型有:[sink、source、sync、decimator、interpolator、general、tagged_stream、hier、noblock]
  • -l:language,即想使用什么语言编写该block的代码,可选类型有[cpp、python、c++]

在上述命令及打印的log信息中可以看出,我们指定添加一个block块,块的名字叫square1_ff,它的类型是‘general’(因为我们还不知道这个块属于什么类型,所以这里就使用”通用“类型)。这个块是使用C++编写的。执行命令后,gr_modtool会询问块是否接受参数(此处没有,所以我们将其留空,直接回车进行下一步),下面是询问我们是否需要 Python 的 QA 代码(即测试文件),此处选择yes;后面是C++的QA,选择no(python版的就够了哈~)。最后可以看到,gr_modtool创建了很多文件,我们需要对这些文件进行编辑才能让我们的square_ff块起作用。

================================  BUG ==================================

可能有的小伙伴在执行上述操作时会遇到找不到clang-format文件的错误:

。。。。。。
Add Python QA code? [Y/n] y
Add C++ QA code? [y/N] n
Adding file 'lib/square1_ff_impl.h'...
Failed to run clang-format: %s [Errno 2] No such file or directory: 'clang-format'
Adding file 'lib/square1_ff_impl.cc'...
Failed to run clang-format: %s [Errno 2] No such file or directory: 'clang-format'
Adding file 'include/mymod/square1_ff.h'...
Failed to run clang-format: %s [Errno 2] No such file or directory: 'clang-format'
。。。。。。

这时只需输入以下命令安装 clang-format 即可:

sudo apt install clang-format

================================  END ==================================

强行剧透:为便于理解,就提前将前面创建的名为 mymod 的 module 以及名为 square1_ff 的 block 最终在GRC中的显示放出来,注意 module 与 block 之间的包含关系:

3、编写block的测试代码(可选,不想写可略过直接到下一步)

在开始编写C++程序之前,我们最好编写测试程序(非必要但却是一个比较好的习惯,而且过程很简单哦~)。针对块square1_ff就是输入一个单精度数据流,输出一个平方后的数据流,并判断结果是否正确。我们打开文件python/qa_square1_ff.py,可以像下面这样编写代码:

from gnuradio import gr, gr_unittest
from gnuradio import blocks
import mymod_swig as mymod

class qa_square1_ff(gr_unittest.TestCase):

    def setUp(self):
        self.tb = gr.top_block()

    def tearDown(self):
        self.tb = None

    def test_001_square1_ff(self):
        src_data = (-3, 4, -5.5, 2, 3)  # 给出源数据
        expected_result = (9, 16, 30.25, 4, 9) # 正确的结果
        src = blocks.vector_source_f(src_data)  # 获取src_data的元素
        sqr = mymod.square1_ff()  # 需要进行测试的块  注意,这行代码在GR3.9中为 sqr =square1_ff(),因为3.9中的默认加载方式为 from mymod import square1
        dst = blocks.vector_sink_f()  # 用来获取mymodule.square_ff的输出
        self.tb.connect(src, sqr) # 依次连接上述src\sqr\dst三个模块,生成相应的流图
        self.tb.connect(sqr,dst)
        self.tb.run()
        result_data = dst.data()
        self.assertFloatTuplesAlmostEqual(expected_result, result_data, 6)  # 判断结果是否正确
        # check data


if __name__ == '__main__':
    gr_unittest.run(qa_square1_ff)  # 运行生成的图

其中,gr_unittest 是标准 Python 模块 unittest 的扩展。 gr_unittest 添加了对检查浮点数和复数元组的近似相等性的支持。详细的机制可以参考 Python unittest documentation。为了后面跟好的调试,最好先要弄懂代码的意思。

Unittest 使用了python的反射机制来查找所有以 test_ 开头的方法并运行它们。当我们运行这个测试文件时,gr_unittest.main 将按顺序调用  setUptest_001_square1_ff  和  tearDown 这三个函数。在代码的主体 test_001_square1_ff 函数中创建了一个包含三个节点的小型流图,在在代码中的体现就是 src、sqr、dst。run() 函数运行这个流图,之后对输出的结果进行验证。

另外,需要注意的是,这些测试一般是在模块进行安装之前运行的,即在 make 操作之后,sudo make install 之前。(如果还记得编译安装gnuradio时的操作,当时执行 make 命令之后执行的 make test 命令就是这个原理)

为了让后面使用CMake时系统知道测试文件的存在,gr_modtool 在生成测试文件的同时自动修改了python/CMakeLists.txt 文件,这个就不需要手动修改了。

4、编写block的C++代码

所有信号处理块都是由gr::block或其子类来进行驱动的。gr_modtool已经为我们提供了三个文件来定义块:lib/square_ff_impl.hlib/square1_ff_impl.cc 以及 include/mymod/square1_ff.h。我们要做的就是对这些文件进行修改以满足我们的需求。代码编写的结构可以参考 Blocks Coding Guide。

首先,对于头文件(include/)来说,由于此处实现的功能比较简单,一般不需要修改,因为在使用 gr_modtool 时就已经将需要的头文件包含的很全面了。

下面我们来看对lib/square1_ff_impl.cc的修改(哔哔一句,impl即implement,意思是实现,懂了吧^.^)。

对于GR3.8来说,gr_modtool 将需要修改代码的位置用 <+×××+> 提示出来,如下:

square1_ff_impl::square1_ff_impl()
  : gr::block("square1_ff",
          gr::io_signature::make(<+MIN_IN+>, <+MAX_IN+>, sizeof(<+ITYPE+>)),
          gr::io_signature::make(<+MIN_OUT+>, <+MAX_OUT+>, sizeof(<+OTYPE+>)))
{}

对于GR3.9版本来说,gr_modtool 将需要修改代码的位置用 预处理指令#pragma message 提示出来,但是最终的代码都是一样的。如下:

#pragma message("set the following appropriately and remove this warning")
using input_type = float;
#pragma message("set the following appropriately and remove this warning")
using output_type = float;
。。。省略部分代码。。。
#pragma message("Implement the signal processing in your block and remove this warning")
    // Do <+signal processing+>

square1_ff_impl.cc文件中,我们需要修改的函数为:

square1_ff_impl::square1_ff_impl()

square1_ff_impl::forecast()

square1_ff_impl::general_work()

修改方式以及各个函数的作用如下(注意看代码注释里的解释哦):

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#include <gnuradio/io_signature.h>
#include "square1_ff_impl.h"

namespace gr {
  namespace mymod {

    square1_ff::sptr
    square1_ff::make()
    {
      return gnuradio::get_initial_sptr
        (new square1_ff_impl());
    }

    /* 私有构造函数 所有信号处理块都是 gr::block 或其子类的派生类*/
    square1_ff_impl::square1_ff_impl()
      : gr::block("square1_ff",
                  gr::io_signature::make(1, 1, sizeof (float)),
                  gr::io_signature::make(1, 1, sizeof (float)))
    { }// 构造函数留空,因为该block不需要设置任何东西。 

    /*  虚拟析构函数 */
    square1_ff_impl::~square1_ff_impl()
    {
    }
    /* forecast函数作用为:告诉调度器有多少输入items被用来生成多少个输出items,这里他们是一样的 */
    void
    square1_ff_impl::forecast (int noutput_items, gr_vector_int &ninput_items_required)
    {
      ninput_items_required[0] = noutput_items;// 索引0代表这是第一个输入端口,这里我们只有一个输入端口
    }
    // general_work()是定义在基类gr::block中的纯虚函数,因此在子类中必须进行重写
    // general_work()是执行实际信号处理任务的函数
    int
    square1_ff_impl::general_work (int noutput_items,
                                  gr_vector_int &ninput_items,
                                  gr_vector_const_void_star &input_items,
                                  gr_vector_void_star &output_items)
    {
      const float *in = (const float *) input_items[0];
      float *out = (float *) output_items[0];

      for(int i = 0; i < noutput_items; i++) {
        out[i] = in[i] * in[i];
      }

      // Tell runtime system how many input items we consumed on
      // 每个输入数据流需要消耗多少items,由于ninput_items=noutput_items,这里填noutput_items
      consume_each (noutput_items);

      // Tell runtime system how many output items we produced.
      return noutput_items;
    }

  } /* namespace mymod */
} /* namespace gr */

=========================== 相关函数具体解释 ============================

(参考自:GNU Radio Manual and C++ API Reference: gr::block Class Reference)

square1_ff_impl() 函数中第一个参数就是模块的名字,后两个函数参数依次用来定义输入和输出端口的属性,gr::io_signature::make 函数前两个参数分别代表该模块所允许的最少/最多的输入/输出端口数,第三个参数代表一个数据项(items)的字节数(这里注意一个数据项可以是一般的int、float、char 等数据类型,也可以是一个向量(如一个 OFDM sambol)等数据类型,一堆数据项组成一个数据流)。

这里解释下 consume_each() 函数。不过在这之前首先要介绍下 consume() 函数,该函数的原型如下:

void gr::block::consume	(int which_input,
                        int how_many_items )		

作用是告诉调度器每生成一次输出数据,数据流“which_input”(即哪个输入端口)消耗了多少个输入数据项(how_many_items个)。该函数用来设置单个输入端口,并用在 general_work() 中调用,告诉调度程序处理的输入数据项(items)的数量。但是如果所有的输入端口的输入输出比是一样的话,就可以调用 consume_each() 函数进行统一设置。该函数的原型如下:

void gr::block::consume_each( int how_many_items )	

然后是 forecast() 函数。该函数原型如下:

virtual void gr::block::forecast(int noutput_items,
                                gr_vector_int & ninput_items_required )	

参数 noutput_items 代表产生的输出数据项的数量,ninput_items_required 表示需要从输入端口中使用多少个数据项。在代码运行过程中,系统需要知道需要多少数据才能确保每个输入数组的有效性,forecast() 方法提供了此信息,因此必须在编写 gr::block 派生类的任何时候重写这个函数(对于 sync 类型的块,这是隐式的,文章结尾会提到这种 block)。函数的作用为评估生成 noutput_items 个输出,需要多少(ninput_items_required)个输入 items 。不过官方文档中特别说明了这个估计值不一定要准确,但应该接近。感觉系统可能会根据我们在代码中指定的输入输出的 items 的数量关系来设置一个具有盈余的存储空间吧,这样可以提升系统的鲁棒性。

general_work() 函数的返回值可以是输出数据项的数量也可以是 WORK_CALLED_PRODUCE 或者 WORK_DONE 。WORK_CALLED_PRODUCE 用于并非所有输出都产生相同数量的数据项的场合,WORK_DONE 表示这个块将不再产生数据。

=====================================================================

5、使用CMake编译并测试代码

在顶层文件夹gr-howto中依次输入以下命令(C++通用话术,懂得都懂—_—):

cd path-to-your-top-directory
mkdir build
cd build/
cmake ../
make
# sudo make install  莫慌莫慌,现在先不急着安装哈~

make 之后执行 make test 命令进行功能测试,如果不是测试文件本身编写有误,一般不会出现问题(如果是GR-v3.9,则可能会出现bug,解决方法见文末):

wsx@wsx:~/temp/gr-mymod/build$ make test
Running tests...
Test project /home/wsx/temp/gr-mymod/build
    Start 1: qa_square1_ff
1/1 Test #1: qa_square1_ff ....................   Passed    0.48 sec

100% tests passed, 0 tests failed out of 1

Total Test time (real) =   0.48 sec

如果在 make test 的过程中遇到了测试失败的问题,可以通过指令 ctest -V (仍然在build文件夹中)来查询具体测试失败的原因。在这里可以使用 ctest -V -R square 来查看详细的错误信息,其中 “-V ”可以提供详细的输出信息,“-R” 后跟的是一个正则表达式,只运行那些匹配的测试,这里匹配的是square。

6、使自定义的block在GRC中生效

若想使得自定义的 block 像其他 GR 自带的 block 一样出现在 GRC 中使用,就需要编辑相应的 YAML文件,YAML文件在使用 gr_modtool add ××× 命令创建 block 时已经自动生成,但只是生成了框架,在该例中,yaml文件路径为:grc/mymod_square1_ff.block.yml

另外,在完成代码编译之后,可以在 gr_mymod 文件夹中使用 gr_modtool 工具辅助生成 YAML 文件来覆盖之前生成的框架文件。命令行操作如下:

wsx@wsx:~/temp/gr-mymod$ gr_modtool makeyaml square1_ff
GNU Radio module name identified: mymod
Warning: This is an experimental feature. Don't expect any magic.
Searching for matching files in lib/:
Making GRC bindings for lib/square1_ff_impl.cc...
Overwrite existing GRC file? [y/N] y

执行命令后,会自动生成新的名为 mymod_square1_ff.block.yml 的 yaml 文件并覆盖原来的文件。如下所示:

id: mymod_square1_ff
label: square1 ff
category: '[Mymod]'
templates:
  imports: import mymod
  make: mymod.square1_ff()
inputs:
- label: in
  domain: stream
  dtype: float
outputs:
- label: out
  domain: stream
  dtype: float
file_format: 1

对于该例来说,由于逻辑比较简单,所以生成的yaml文件不用修改即可使用。但是在 block 的功能比较复杂的情况下,这样生成的 yaml 文件并不能完成所有参数的配置,还需要手动进行额外的配置。至于如何进行进一步修改以及 yaml 文件中每个关键字的用法及含义可以参考这篇文章。

====================== 执行 makeyaml 时遇到的bug =====================

在执行 makeyaml 时遇到了问题:

wsx@wsx:~/temp/gr-mymod$ gr_modtool makeyaml square1_ff
GNU Radio module name identified: mymod
。。。。。。
Error: Can't parse input signature.
Error: Can't parse output signature.
。。。。。。

主要是说没法解析输入输出端口,这个问题其实有点奇怪。我第一次遇到这个问题竟然是因为在设置端口的那两句代码后面打了注释。。emmmm,就像下面这样:

square1_ff_impl::square1_ff_impl()
  : gr::block("square1_ff",
          gr::io_signature::make(1 , 1 , sizeof(input_type)),  // 这里是个注释
          gr::io_signature::make(1 , 1 , sizeof(output_type)))
{}

当我把注释去掉后,bug 就没了(别问我怎么知道这个问题的,问就是掐之一算猜出来的,泪),这可能是这个官方的 makeyaml 的脚本本身在解析代码时的小bug吧。

放个github链接

=================================================================

7、最后一步:安装并加载

在完成以上所有工作之后,回到 build/ 文件夹中,在终端中再次输入指令进行安装:

cd build/
sudo make install
sudo ldconfig  # 重新加载ubuntu库文件

至此,创建一个OOT module的工作就完成了,我们打开GRC,来看一下刚刚创建的block吧~

运行该流图,可以发现输出波的幅值(2.25)为输入(1.5)的平方,yep!

8、卸载已安装的block

1、如果是想卸载已经安装到系统中的 block,则 cd 到 build 文件夹中执行 sudo make uninstall 命令即可。

2、如果单单是想删除 module 中的 block 有关的文件(如果已安装,并不卸载安装,一般用于block的编辑阶段),则 cd 到 module 文件夹中,在命令行中执行 gr_modtool rm REGEX 命令即可,如下:

wsx@wsx:~/temp/gr-mymod$ gr_modtool rm square
GNU Radio module name identified: mymod
Searching for matching files in lib/:
Really delete lib/square1_ff_impl.cc? [Y/n/a/q]: y
Deleting lib/square1_ff_impl.cc.
Deleting occurrences of square1_ff_impl.cc from lib/CMakeLists.txt...
Really delete lib/square1_ff_impl.h? [Y/n/a/q]: y
Deleting lib/square1_ff_impl.h.
Deleting occurrences of square1_ff_impl.h from lib/CMakeLists.txt...
Searching for matching files in include/mymod/:
Really delete include/mymod/square1_ff.h? [Y/n/a/q]: y
Deleting include/mymod/square1_ff.h.
Deleting occurrences of square1_ff.h from include/mymod/CMakeLists.txt...
Searching for matching files in swig/:
None found.
Searching for matching files in python/:
Really delete python/qa_square1_ff.py? [Y/n/a/q]: y
Deleting python/qa_square1_ff.py.
Deleting occurrences of qa_square1_ff.py from python/CMakeLists.txt...
Searching for matching files in grc/:
Really delete grc/mymod_square1_ff.block.yml? [Y/n/a/q]: y
Deleting grc/mymod_square1_ff.block.yml.
Deleting occurrences of mymod_square1_ff.block.yml from grc/CMakeLists.txt...

9、module 创建流程总结

  1. 创建module:gr_modtool newmod MODULENAME
  2. 为module添加block:gr_modtool add BLOCKNAME
  3. 创建build文件夹:mkdir build/
  4. 编译:cd build && cmake <OPTIONS> ../ && make
  5. 调用测试文件:make test 或 ctest or ctest -V for more verbosity
  6. 生成yaml文件,需要时手动修改:gr_modtool makeyaml BLOCKNAME
  7. 安装:sudo make install
  8. ubuntu环境下重新加载库文件:sudo ldconfig
  9. 卸载或删除block:sudo make uninstall 或 gr_modtool rm REGEX

四、结语

大家还记得在本文这个例子中,我们创建的 block 为 general 类型的,这种类型的 block 在数据量的输入输出比的设置上(通过 forecast 和 consume 函数设置)具有很高的灵活性,这点体现出了其“通用”性所在。但是在一般的信号处理任务中,数据量的输入输出比往往是固定的,常用的是 1:1 、1:N 或 N:1这样的固定比例。所以现在就可以思考一个问题:如果一个 block 的输入数据量等于其输出数据,我们有必要再去将这个相同的数据量告诉 GR 两次吗?答案一定是否定的!因此在这种情况下我们就有理由去写一个相相对简化版的 block ,这就是 下篇文章 要说的 sync 类型的 block。

sync 类型的块派生自 gr::sync_block ,而 gr::sync_block 同时是这篇文章中 square1_ff 所继承的父类 gr::block 的子类,可以用来实现一个1:1输入输出的 block。本例中的有关继承关系可以查看 include/mymod/square1_ff.h 以及 lib/square1_ff_impl.h 。

另外还有一个事实必须要清楚,即所有信号处理块都是 gr::block 或其子类的派生类

另外,在使用GR-v3.9创建OOT的过程中可能会出现一些bug:

1、Unable to coerce endpoints when making a block in OOT module

解决方法可参考:

Unable to coerce endpoints when making a block in OOT module · Issue #4773 · gnuradio/gnuradio · GitHub

GNU Radio 3.9 OOT Module Porting Guide - GNU Radio

2、ImportError: generic_type: type "square_ff" referenced unknown base type "gr::block"

解决方法可参考:

ImportError: generic_type: type "square_ff" referenced unknown base type "gr::block" · Issue #4841 · gnuradio/gnuradio · GitHub

参考整理自:

OutOfTreeModules - GNU Radio

BlocksCodingGuide - GNU Radio

YAML GRC - GNU Radio

GNU Radio Manual and C++ API Reference: gr::block Class Reference

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

GNU Radio3.8创建OOT的详细过程(基础/C++) 的相关文章

随机推荐

  • Vscode C++的基础配置文件以及无法产生可编译文件exe的处理方法(undefined reference)

    采用排除法 xff1a 1 是否将工作文件夹添加工作区 xff1f 打开vscode 文件 打开文件夹 文件 将文件夹添加工作区 xff08 或者另存为一个 xff09 xff0c 把工作区文件放到工作文件夹里 如下 xff1a Manag
  • Arduino零基础实践2——串口数据发送

    具体的原理在微机开发中详细介绍了 xff0c 下面直接使用arduino进行数据收发 13条消息 单片机攻略4 中断和串口 r135792uuuu的博客 CSDN博客 void setup Serial begin 9600 void lo
  • px4开发bug记录

    一 仿真问题 1 roslaunch无法启动px4 gazebo的无人机仿真 xff0c 但是make px4 sitl gazbeo可以正常启动 2 make px4 sitl gazbeo启动到一半无法启动 xff0c 显示无法连接ga
  • linux无损扩容

    linux笔记本上空间不够用了 xff0c 重新从windows里划分30个g出来给linux xff0c 记录一下 1 准备u盘 xff0c u盘里面全部清空 xff0c 不能有任何东西 下载一个Ubuntu的桌面文件 xff0c 大概有
  • Linux更新源 source.list 自定义第三方源

    1 官方默认源 打开软件和更新 xff0c 直接图形化操作 但是只能设置一个源 xff0c 对于下载多种多样的包可能不够 xff0c 所以需要自定义多个不同源 2 自定义添加源 源的文件 sources list在 etc apt sour
  • 现在没有可用的软件包 *** ,但是它被其它的软件包引用了 和 E: 无法定位软件包 ***问题解决

    root 64 zhouls virtual machine snort src apt get install bison flex 正在读取软件包列表 完成 正在分析软件包的依赖关系树 正在读取状态信息 完成 现在没有可用的软件包 fl
  • sdf文件轨范

    sdf文件规范 xff1a a href https www zhihu com org aigraphx posts page 61 3 title 深圳季连AIGRAPHX 知乎 深圳季连AIGRAPHX 知乎 a span style
  • HBase基本操作

    HBase Java API 操作 Tips xff1a 其实每一个操作都可以简化为 xff1a 1 配置并连接数据库 2 编写 Java API 的 HBase 的操作 3 使用权限 执行操作 要对一个Hbase数据库进行操作的话 xff
  • GNU Radio3.8:编辑yaml文件的方法

    GNU Radio 学习使用 OOT 系列教程 xff1a GNU Radio3 8创建OOT的详细过程 基础 C 43 43 GNU Radio3 8创建OOT的详细过程 进阶 C 43 43 GNU Radio3 8创建OOT的详细过程
  • input上传图片并且实现预览

    文章目录 前言一 确定思路二 书写代码1 HTML部分2 CSS部分3 JS部分 xff08 重点 xff09 3 1 点击选择图片按钮 xff0c 调用input文件框事件的的代码3 2 转换格式3 3 发送图片给后端 三 编写优化1 i
  • hashcode()和equals()方法详解

    hashcode 和equals 方法详解 1 何为hashcode hash是一个函数 xff0c 就是通过一种算法来得到一个hash值 通过hash算法得到的hash值就存放在这张hash表中 xff0c 也就是说hash表示所有的ha
  • ubuntu安装kvm

    kvm是linux下的虚拟机 文章目录 一 电脑硬件支持二 安装ubuntu三 安装kvm 一 电脑硬件支持 首先不用多说啦 xff0c 既然是虚拟机 xff0c 当然要自己的机器支持虚拟技术 xff0c 重启机器进入biso xff0c
  • 类的静态(static)成员

    有时候类需要它的一些成员与类本身直接相关 xff0c 而不是与类的各个对象保持关联 xff08 这意味着无论创建多少个类的对象 xff0c 静态成员都只有一个副本 xff09 我们通过在成员的声明前加上关键字static使得其与类关联在一起
  • Keil uvision5 介绍

    keil 5 Keil uvision5 安装过程Keil uvision5安装包1 Keil uvision5 介绍2 Keil uVision5 特点3 Keil uVision5 功能4 Keil uVision5 快捷键 Keil
  • px4仿真时,/mavros/state现实连接不上

    仿真时 xff0c 使用px4 xff0c 启动 PX4 Firmware launch文件中的launch文件 进入gazebo世界中 xff0c 通过 xff1a rostopic list 查看发布的话题 xff0c 并且打印 mav
  • 插值方法(一维插值、三次样条插值、二维插值的matlab自带函数,python实现/作图)

    数模比赛中 xff0c 常常需要根据已知的函数点进行数据 模型的处理和分析 xff0c 而有时候现有的数据是极少的 xff0c 不足以支撑分析的进行 xff0c 这时就需要使用一些数学的方法 xff0c 模拟产生 一些新的单又比较靠谱的值来
  • ROS中常用命令

    1 xff09 工作空间初始化 xff1a catkin init workspace 2 xff09 创建功能包 xff1a catkin create pkg pkg name reply 3 xff09 编译工作空间中的功能包 xff
  • 【DL】CNN的前向传播和反向传播(python手动实现)

    卷积层的前向传播和反向传播 说明 本文中 xff0c 只实现一层卷积层的正反向传播 xff08 带激活函数Relu xff09 xff0c 实现的是多通道输入 xff0c 多通道输出的 之前对深度学习的理解大多止于pytorch里面现成的A
  • Postman使用教程详解

    目录 1 Postman安装与接口请求基本操作1 1Postman安装1 2发起一个接口请求的小测试 2 接口测试实战2 1百度IP查询接口从抓包到测试实战2 2需要设置头域的请求实战2 3文件上传与json请求实战 3 Newman命令行
  • GNU Radio3.8创建OOT的详细过程(基础/C++)

    GNU Radio 学习使用 OOT 系列教程 xff1a GNU Radio3 8创建OOT的详细过程 基础 C 43 43 GNU Radio3 8创建OOT的详细过程 进阶 C 43 43 GNU Radio3 8创建OOT的详细过程