打印

Gnuradio GNU Radio How to write a block 转

[复制链接]
757|0
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
gaochy1126|  楼主 | 2020-1-31 19:46 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
本帖最后由 gaochy1126 于 2020-1-31 19:49 编辑

本文讲解如何在GNU Radio中添加用户开发的信号处理模块,译文如有不当之处可参考原文地址:http://gnuradio.microembedded.com/outoftreemodules
回到顶部

Out-of-tree modules
  利用用户自定义的功能模块扩展GNU Radio。
  This article borrows heavily from the original (but very outdated) "How to write a block?" written by Eric Blossom.
回到顶部

1. What is an out-of-tree module?
  外部模块(Out-of-tree Module)是不存在与GNU Radio源代码树的GNU Radio组件。通常,用户自己扩展GNU Radio的功能模块,被称作外部模块。一般我们不会向实际的GNU Radio源代码树中添加东西,除非你是想把他上传给发开者们整合使用。添加外部模块既允许你维护你自己的代码,并且延续主代码的功能。
  很多这样的外部模块由CGRAN主持,这个一个关于GNU Radio的智囊团。如果你开发了非常nice的东西,请将他提交到CGRAN!
   spectral estimation toolbox是外部模块的一个例子,它的谱估计功能扩展了GNU Radio。安装之后,在GRC(GNU Radio Companion)中你会拥有更多的模块(blocks)可以使用,其特性和原有的模块一样。然而,它是有很多开发者共同开发的。
回到顶部

2. Tools and resources at my disposal
  有很多工具,脚本和文档可以作为第三方程序或者作为GNU Radio的一部分。
2.1 gr-how-to-write-a-block
  这里有一个已经实现了的外部模块的例子,它作为GNU Radio源代码树的一部分。如果你跟随本文的教程,你就会成功得到一个名为gr-howto-write-a-block的模块。总之,如果你想知道你的模块应该张什么样子,这是一个很好的参考。
  因为经历测试和维护之后,它会作为GNU Radio树的一部分。GNU Radio的当前版本将需要更新模块结构。
2.2 gr_modtool - The swiss army knife of module editing
  开发一个GNU Radio模块的时候,涉及很多单调和枯燥的工作:样板代码,makefile文件编写等。gr_modtool作为一个脚本,目的是使所有的文件都自动生成,如编写makefile文件,使用模板等,并且尽可能的为开发者做很多工作,这样开发者就只可以直接开始DSP的编程工作了。
  需要注意的是:gr_modtool在你看到的代码上做了很多假设,越是你自己定制的,或者有特定变化的,gr_modtool使用的就变得越少,但是它可能是编写一个新模块最好的开端。
  gr_modtool 在GNU Radio代码树中是可用的,并且它是被默认安装的。
2.3 Cmake, make. etc.
  在GNU Radio中使用cmake来作为系统的构建,因此build一个模块需要你安装cmake(最常见的是make,但是也可以使用Eclipse或者MS Visual Studio)。
回到顶部

3. Tutorial 1: Creating an out-of-tree module
  在下面的教程中,我们将使用名为howto的模块。第一步是创建这个模块。
  利用gr_modtool是一个非常简单的方式。无论你想要在那里创建新模块,只需要在命令行输入命令(这应该是GNU Radio源代码树以外),然后继续:
1 hao@hao:~$ gr_modtool newmod howto2 Creating out-of-tree module in ./gr-howto... Done.3 Use 'gr_modtool add' to add a new block to this currently empty module.
  如果一切顺利,你会在主文件夹下得到一个名为gr-howto的文件目录。
回到顶部

4. Structure of a module
  我们可以看看gr-howto文件目录的组成。
1 hao@hao:~$ cd gr-howto/2 hao@hao:~/gr-howto$ ls3 apps  cmake  CMakeLists.txt  docs  examples  grc  include  lib  python  swig
  它由多个子目录组成,凡是用C/C++(或者其他非python语言)写的程序都将放在lib/文件夹中。对于C++文件我们通常只有头文件放在include/文件夹中(如果它们是接口),或者放在lib/文件夹中(如果它们只有在编译时用到,在安装之后用不到,如*_impl.h文件,你会在接下来的教程中看到里面有些什么)。
  当然python写的程序将进入python/文件夹下,这包括未安装的测试单元和已安装的python模块。
  你可能已经知道,虽然GNU Radio的模块是用C++写的,但是它可以在python中调用。这是通过SWIG (the simplified wrapper and interface generator)的帮助,这是一个简化包装和接口生成器,它会自动创建链接代码来实现这一点。SWIG需要一些指令来完成这些事情,这些指令在swig/的子目录中。
  除非你为你的模块做一些更好的事情,一般你将不需要去swig/的子目录。gr_modtool将会为我们处理所有的这一切。
  如果你想让你的模块在GNU Radio companion(the graphical UI for GNU Radio)中也是可用的,你需要在grc/文件夹中添加.xml描述文件。
  docs/文件夹中包含一些说明,如何从C++文件和python文件中提取文件的说明(我们使用Doxygen和Sphinx来完成),并确保它们可以在python代码中有效。当然,你可以添加自定义的文件在这里。
  最后,在apps/的子目录中包含一些完整的安装到系统的应用程序(包括在GRC中执行,和单独的执行的文件)。
  一些模块还包括文件夹examples/,作为文件的附录,用来保存例子。其他开发者简单直接的看例子里面的block如何使用。
  这个构建系统还带来了一些其他的独立的包:Cmakelist.txt文件(其中一个是存在于每一个子目录)和cmake/的文件夹。你可以无视后者,因为它主要是用于cmake如何找到GNU Radio的库等的一些说明。为了确保你的模块构建正确,Cmakelist.txt这个文件需要做很多修改。
回到顶部

5. Tutorial 2: Writing a block (square_ff) in C++
  对于我们第一个例子,我们创建一个用于对浮点信号的求平方的模块。此模块将接受一个浮点输入流并产生一个浮点输出流。也就是说对于每个输入的浮点信号,将其平方后输出。
  下面的命名规则,我们将使用包名howto作为前缀,并命名这个模块为howto_square_ff,因为他的输入输出都是float型。
  我们要整理这个模块,把我们写好的其他东西放到里面,最终放到howto Python中。这样就可以用Python来访问它。
1 import howto2 sqr = howto.square_ff()
5.1 Creating the files
  第一步是创建一个空文件,并且编辑Cmakelist.txt。gr_modtool会再次帮我们完成这个工作。运行:

1 hao@hao:~/gr-howto$ gr_modtool add -t general square_ff 2 GNU Radio module name identified: howto 3 Language: C++ 4 Block/code identifier: square_ff 5 Enter valid argument list, including default arguments:  6 Add Python QA code? [Y/n]   7 Add C++ QA code? [y/N]  8 Adding file 'lib/square_ff_impl.h'... 9 Adding file 'lib/square_ff_impl.cc'...10 Adding file 'include/howto/square_ff.h'...11 Editing swig/howto_swig.i...12 Adding file 'python/qa_square_ff.py'...13 Editing python/CMakeLists.txt...14 Adding file 'grc/howto_square_ff.xml'...15 Editing grc/CMakeLists.txt...

  在命令行中我们指定要添加一个模块,他的类型是general(但是我们也不知道模块的类型是什么),并且它叫做square_ff。类型有一下几种可选:'sink', 'source', 'sync', 'decimator', 'interpolator', 'general', 'tagged_stream', 'hier', 'noblock'。然后gr_modtool会问你是否需要参数(这里不需要,所以令他为空)?是否添加Python 测试文件(默认为Yes)?是否添加C++测试文件(默认为no)?
  现在,你可以看看Cmakelist.txt的不同和gr_modtool做了些什么,你还可以看到添加了许多新文件,如果你想要你的模块能够工作,现在必须来编辑这些文件。
5.2 Test Driven Programming
  我们可以直接运行刚才写的C++代码,但是作为一个高度发展的程序我们必须要先编写测试代码,毕竟我们有一个良好的习惯。取一个的浮点流作为输入,再取一个的浮点流作为输出。输出应该是输入的平方。
  这很简单,我们打开python文件夹下的qa_square_ff.py,我们将编写这个文件如下:

1 from gnuradio import gr, gr_unittest 2 from gnuradio import blocks 3 import howto_swig as howto 4  5 class qa_square_ff (gr_unittest.TestCase): 6     def setUp (self): 7         self.tb = gr.top_block () 8      9     def tearDown (self):10         self.tb = None11     12     def test_001_square_ff(self):13         src_data = (-3, 4, -5.5, 2, 3)14         expected_result = (9, 16, 30.25, 4, 9)15         src = blocks.vector_source_f(src_data)16         sqr = howto.square_ff()17         dst = blocks.vector_sink_f()18         self.tb.connect(src, sqr)19         self.tb.connect(sqr, dst)20         self.tb.run()21         result_data = dst.data()22         self.assertFloatTuplesAlmostEqual(expected_result, result_data, 6)23 24 if __name__ == '__main__':25     gr_unittest.run(qa_square_ff, "qa_square_ff.xml")

  gr_unittest是一个扩展的标准Python模块单元测试。gr_unittest增加了对检查浮点和复数的元组的近似相等的支持。unittest使用Python的反射机制来发现所有运行的方法和运行它们。unittest包装每次调用test_*来匹配建立和拆除的调用。
  当我们运行测试,在这种秩序中gr_unittest.main要调用setUP, test_001_square_ff, 和tearDown。
  test_001_square_ff构建了一个graph,包含三个节点(分别是:source, signal processing block, sink)。 blocks.vector_source_f(src_data)作为信号源。howto.square_ff是我们正在测试的模块。blocks.vector_sink_f是howto.square_ff的输出。
  在run()方法下运行graph,直到所有的模块完成各自的工作。最后,我们检查的src_data经过square_ff输出的结果与我们所期望的结果是否一致。
  注意,这样的测试通常称为在安装模块之前的测试。这意味着我们需要一些类似的代码,以能够加载模块时测试。cmake可以适当的改变PYTHONPATH。此外这个文件中的第3行的意思是导入howto_swig并给它起个别名叫howto。
  为了让CMake的实际知道这个测试存在,gr_modtool修改python文件夹的CMakeLists.txt如下:

1 ########################################################################2 # Handle the unit tests3 ########################################################################4 include(GrTest)5 6 set(GR_TEST_TARGET_DEPS gnuradio-howto)7 set(GR_TEST_PYTHON_DIRS ${CMAKE_BINARY_DIR}/swig)8 GR_ADD_TEST(qa_howto ${PYTHON_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/qa_howto.py)

5.3 Build Tree vs. Install Tree
  当您运行cmake的时候,你通常运行在一个单独的目录(如build/),这就是构建树。安装的路径是$prefix/lib/$pythonversion/dist-packages,$prefix是无论你在哪里(通常是/usr/local/)进行cmake的配置和-DCMAKE_INSTALL_PREFIX的开关。
  在编译过程中,该库将被复制到build目录,只有在安装过程中,文件安装到安装目录,从而使GNU Radio能够应用我们提供的模块。我们写的应用程序,使它们在访问安装目录中的代码和库。在另一方面,我们希望在构建树(build tree)中运行测试程序,这样可以在安装之前发现问题。
5.4 The C++ code(part 1)
现在,我们已经有了一个测试案例(test case),让我们写的C++代码。所有的信号处理块都继承自gr::block或者gr::block的子类。gr_modtool已经为我们提供了三个文件定义块:
lib/square_ff_impl.h
lib/square_ff_impl.cc
include/howto/square_ff.h
我们要做的就是修改他们。
  首先,我们来看看我们的头文件。因为我们正在编写的块就是这么简单,所以我们没有必要真正改变他们(在include文件夹加入头文件往往是相当完整的运行gr_modtool后,除非我们需要添加一些公共方法,如赋值方法,即getter和setter)。并且我们需要在lib/square_ff_impl.cc中将头文件中声明的方法实现。
  自动生成的square_ff_impl.cc文件内容如下:

1 /* -*- c++ -*- */ 2 /*  3  * Copyright 2016 <+YOU OR YOUR COMPANY+>. 4  *  5  * This is free software; you can redistribute it and/or modify 6  * it under the terms of the GNU General Public License as published by 7  * the Free Software Foundation; either version 3, or (at your option) 8  * any later version. 9  * 10  * This software is distributed in the hope that it will be useful,11  * but WITHOUT ANY WARRANTY; without even the implied warranty of12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the13  * GNU General Public License for more details.14  * 15  * You should have received a copy of the GNU General Public License16  * along with this software; see the file COPYING.  If not, write to17  * the Free Software Foundation, Inc., 51 Franklin Street,18  * Boston, MA 02110-1301, USA.19  */20 21 #ifdef HAVE_CONFIG_H22 #include "config.h"23 #endif24 25 #include <gnuradio/io_signature.h>26 #include "square_ff_impl.h"27 28 namespace gr {29   namespace howto {30 31     square_ff::sptr32     square_ff::make()33     {34       return gnuradio::get_initial_sptr35         (new square_ff_impl());36     }37 38     /*39      * The private constructor40      */41     square_ff_impl::square_ff_impl()42       : gr::block("square_ff",43               gr::io_signature::make(<+MIN_IN+>, <+MAX_IN+>, sizeof(<+ITYPE+>)),44               gr::io_signature::make(<+MIN_OUT+>, <+MAX_OUT+>, sizeof(<+OTYPE+>)))45     {}46 47     /*48      * Our virtual destructor.49      */50     square_ff_impl::~square_ff_impl()51     {52     }53 54     void55     square_ff_impl::forecast (int noutput_items, gr_vector_int &ninput_items_required)56     {57         /* <+forecast+> e.g. ninput_items_required[0] = noutput_items */58     }59 60     int61     square_ff_impl::general_work (int noutput_items,62                        gr_vector_int &ninput_items,63                        gr_vector_const_void_star &input_items,64                        gr_vector_void_star &output_items)65     {66         const <+ITYPE*> *in = (const <+ITYPE*> *) input_items[0];67         <+OTYPE*> *out = (<+OTYPE*> *) output_items[0];68 69         // Do <+signal processing+>70         // Tell runtime system how many input items we consumed on71         // each input stream.72         consume_each (noutput_items);73 74         // Tell runtime system how many output items we produced.75         return noutput_items;76     }77 78   } /* namespace howto */79 } /* namespace gr */

  gr_modtool提示我们需要在<++>的地方添加我们需要的代码,如43行44行。首先修改构造函数(41~45行)如下:构造函数本来就是空的,因为square求平方不需要构造函数初始化。唯一有兴趣的部分是输入和输出签名的定义:在输入端,我们有1个端口,允许浮点数输入。输出端口同理。

1 square_ff_impl::square_ff_impl()2 : gr::block("square_ff",3       gr::io_signature::make(1, 1, sizeof(float)),4       gr::io_signature::make(1, 1, sizeof(float)))5 {6     //empty constructor7 }

  修改54~58行代码如下:

1 square_ff_impl::forecast (int noutput_items, gr_vector_int &ninput_items_required)2 {3     /* <+forecast+> e.g. ninput_items_required[0] = noutput_items */4     nintput_items_required[0] = noutput_items;5 }

  forecast()是一个函数,它告诉调度器产生noutput_items个输出items需要从输入多少items。在本例中,输入输出的items数量是相同的。
  index 0表示这是用于第一端口(port),但是我们只有一个端口。当一个模块有很多端口(port)时需要用下标指明端口(port)。
  通常在大多数block中都有不同的forecast()函数,举例来说,你可以看看gr::block, gr::sync_block, gr::sync_decimator和gr::sync_interpolator是怎么来定义默认的forecast()统计rate change和history。
  最后,还有general_work(),在头文件中声明它是纯虚函数,所以我们一定要重写。

1 int 2 square_ff_impl::general_work (int noutput_items, 3                    gr_vector_int &ninput_items, 4                    gr_vector_const_void_star &input_items, 5                    gr_vector_void_star &output_items) 6 { 7     const float *in = (const float *) input_items[0]; 8     float *out = (float *) output_items[0]; 9 10     // Do <+signal processing+>11     // Tell runtime system how many input items we consumed on12     // each input stream.13     for(int i = 0; i<noutput_items; ++i) {14       out = in * in;15     }16     consume_each (noutput_items);17 18     // Tell runtime system how many output items we produced.19     return noutput_items;20 }

  这里分别有一个指针指向输入缓冲区和输出缓冲区,for循环可以将输入缓冲区的平方复制到输出缓冲区。
5.5 Using CMake
  如果您以前从未使用CMake,这是很好的时间来进行尝试。一个在命令行中看到的以CMake为基础的项目的典型的工作流程是这样的:
1 $ mkdir build2 $ cd build/3 $ cmake ../   #告诉CMake,它的所有配置文件为一目录了4 $ make        #start building(应该在前一节之后工作)
  在我们的模块的目录中有一个新的目录build/。所有的编译等在这里完成,所以实际的源代码树不是充斥着临时文件。如果我们改变任何的CMakeLists.txt文件,我们应该重新运行cmake ../(不过当你下一次运行make,cmake的检测这些变化并且自动返回)。具体运行过程如下:

1 hao@hao:~/gr-howto$ mkdir build 2 hao@hao:~/gr-howto$ cd build/ 3 hao@hao:~/gr-howto/build$ cmake ../ 4 -- The CXX compiler identification is GNU 4.8.4 5 -- The C compiler identification is GNU 4.8.4 6 -- Check for working CXX compiler: /usr/bin/c++ 7 -- Check for working CXX compiler: /usr/bin/c++ -- works 8 -- Detecting CXX compiler ABI info 9 -- Detecting CXX compiler ABI info - done10 -- Check for working C compiler: /usr/bin/cc11 -- Check for working C compiler: /usr/bin/cc -- works12 -- Detecting C compiler ABI info13 -- Detecting C compiler ABI info - done14 -- Build type not specified: defaulting to release.15 -- Boost version: 1.55.016 -- Found the following Boost libraries:17 --   filesystem18 --   system19 -- Found PkgConfig: /usr/bin/pkg-config (found version "0.26") 20 -- checking for module 'cppunit'21 --   found cppunit, version 1.13.122 -- Found CPPUNIT: /usr/lib/x86_64-linux-gnu/libcppunit.so;dl  23 -- Found Doxygen: /usr/bin/doxygen (found version "1.8.6") 24 Checking for GNU Radio Module: RUNTIME25 -- checking for module 'gnuradio-runtime'26 --   found gnuradio-runtime, version 3.7.5.127  * INCLUDES=/usr/local/include28  * LIBS=/usr/local/lib/libgnuradio-runtime.so;/usr/local/lib/libgnuradio-pmt.so29 -- Found GNURADIO_RUNTIME: /usr/local/lib/libgnuradio-runtime.so;/usr/local/lib/libgnuradio-pmt.so  30 GNURADIO_RUNTIME_FOUND = TRUE31 -- Found SWIG: /usr/bin/swig2.0 (found version "2.0.11") 32 -- Found PythonLibs: /usr/lib/x86_64-linux-gnu/libpython2.7.so (found suitable version "2.7.6", minimum required is "2") 33 -- Found PythonInterp: /usr/bin/python2 (found suitable version "2.7.6", minimum required is "2") 34 -- Looking for sys/types.h35 -- Looking for sys/types.h - found36 -- Looking for stdint.h37 -- Looking for stdint.h - found38 -- Looking for stddef.h39 -- Looking for stddef.h - found40 -- Check size of size_t41 -- Check size of size_t - done42 -- Check size of unsigned int43 -- Check size of unsigned int - done44 -- Performing Test HAVE_WNO_UNUSED_BUT_SET_VARIABLE45 -- Performing Test HAVE_WNO_UNUSED_BUT_SET_VARIABLE - Success46 -- Configuring done47 -- Generating done48 -- Build files have been written to: /home/hao/gr-howto/build49 hao@hao:~/gr-howto/build$ make50 Scanning dependencies of target gnuradio-howto51 [  6%] Building CXX object lib/CMakeFiles/gnuradio-howto.dir/square_ff_impl.cc.o52 Linking CXX shared library libgnuradio-howto.so53 [  6%] Built target gnuradio-howto54 Scanning dependencies of target test-howto55 [ 13%] Building CXX object lib/CMakeFiles/test-howto.dir/test_howto.cc.o56 [ 20%] Building CXX object lib/CMakeFiles/test-howto.dir/qa_howto.cc.o57 Linking CXX executable test-howto58 [ 20%] Built target test-howto59 Scanning dependencies of target _howto_swig_doc_tag60 [ 26%] Building CXX object swig/CMakeFiles/_howto_swig_doc_tag.dir/_howto_swig_doc_tag.cpp.o61 Linking CXX executable _howto_swig_doc_tag62 [ 26%] Built target _howto_swig_doc_tag63 Scanning dependencies of target howto_swig_swig_doc64 [ 33%] Generating doxygen xml for howto_swig_doc docs65 [ 40%] Generating python docstrings for howto_swig_doc66 [ 40%] Built target howto_swig_swig_doc67 Scanning dependencies of target _howto_swig_swig_tag68 [ 46%] Building CXX object swig/CMakeFiles/_howto_swig_swig_tag.dir/_howto_swig_swig_tag.cpp.o69 Linking CXX executable _howto_swig_swig_tag70 [ 46%] Built target _howto_swig_swig_tag71 [ 53%] Generating howto_swig.tag72 Scanning dependencies of target howto_swig_swig_2d0df73 [ 60%] Building CXX object swig/CMakeFiles/howto_swig_swig_2d0df.dir/howto_swig_swig_2d0df.cpp.o74 Linking CXX executable howto_swig_swig_2d0df75 Swig source76 [ 60%] Built target howto_swig_swig_2d0df77 Scanning dependencies of target _howto_swig78 [ 66%] Building CXX object swig/CMakeFiles/_howto_swig.dir/howto_swigPYTHON_wrap.cxx.o79 Linking CXX shared module _howto_swig.so80 [ 66%] Built target _howto_swig81 Scanning dependencies of target pygen_swig_387be82 [ 73%] Generating howto_swig.pyc83 [ 80%] Generating howto_swig.pyo84 [ 80%] Built target pygen_swig_387be85 Scanning dependencies of target pygen_python_54f4d86 [ 86%] Generating __init__.pyc87 [ 93%] Generating __init__.pyo88 [ 93%] Built target pygen_python_54f4d89 Scanning dependencies of target pygen_apps_9a6dd90 [ 93%] Built target pygen_apps_9a6dd91 Scanning dependencies of target doxygen_target92 [100%] Generating documentation with doxygen93 [100%] Built target doxygen_target


使用特权

评论回复

相关帖子

发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

个人签名:这个社会混好的两种人:一是有权有势,二是没脸没皮的。

1051

主题

11300

帖子

26

粉丝