Gr-modtool

1 view
Skip to first unread message

Luciana

unread,
Aug 3, 2024, 4:40:35 PM8/3/24
to avargeshe

An Out-Of-Tree (OOT) module is a GNU Radio component that does not live within the GNU Radio source tree. The tree is the group of blocks already provided by GNU Radio. Thus, an OOT block is a custom block created to extend GNU Radio with specific functions desired. OOT blocks allow you to maintain the code yourself and have additional functionality alongside the main code. Their functionality can be defined in Python or in C++. Their configuration is described via a yaml file.

The previous tutorial, Creating Python OOT with gr-modtool, describes how to create a Python block in an OOT module. This C++ OOT tutorial builds upon the previous Python one, so it is is suggested to at least complete the Creating an OOT Module portion of that tutorial before completing this one.

Enter sync as the block type, because the block we're making will produce the same number of output items on the output port for each item it consumes from the input port. See Types of Blocks for more info on different block types that are available.

Many of the files are automatically generated wrapper code that do not need to be modified. However, the multDivSelect_impl.h and multDivSelect_impl.cc files defines the operation of the block and must be modified. Open the file with a text editor:

gnuradio-companion uses files in the YAML (yet another markup language) format to learn about our OOT block and how to call it. More information about this kind of file may be found in the YAML GRC page.

Deprecation Warning: This article has been superseded by newer tutorials for C++ ( Creating C++ OOT with gr-modtool ) and Python ( Creating Python OOT with gr-modtool ). This article is being kept only as a reference. Please use the newer tutorials instead of this aricle. In turn, this article superseded the original "How to write a block?" article written by Eric Blossom.

An out-of-tree module is a GNU Radio component that does not live within the GNU Radio source tree. Typically, if you want to extend GNU Radio with your own functions and blocks, such a module is what you create (i.e. you wouldn't usually add stuff to the actual GNU Radio source tree unless you're planning to submit it to the devs for upstream integration). This allows you to maintain the code yourself and have additional functionality alongside the main code.

A lot of OOT projects are hosted at CGRAN -- the Comprehensive GNU Radio Archive Network. CGRAN projects are all available through our tool PyBOMBS. In fact, when you add your project to the PyBOMBS recipe repo, it will automatically update the CGRAN website.

The example of such a module is the GNU Radio Digital Audio Broadcasting module, which extends GNU Radio with everything needed to get audio from DAB and DAB+. When installed, you have more blocks available (i.e. in the GNU Radio companion) which behave just like the rest of GNU Radio; however, the developers are different people.

When developing a module, there's a lot of boring, monotonous work involved: boilerplate code, makefile editing, etc. gr_modtool is a script which aims to help with all these things by automatically editing makefiles, using templates, and doing as much work as possible for the developer such that you can jump straight into the DSP coding.

Note that gr_modtool makes a lot of assumptions on what the code looks like. The more your module is custom and has specific changes, the less useful gr_modtool will be, but it is probably the best place to start with any new module or block.

Most important is definitely the block coding guide. While this is written for the GNU Radio main tree, this should also be applied to all modules. Specifically, have a look at the naming conventions!

GNU Radio uses CMake as a build system. Building a module, therefore, requires you to have cmake installed, and whatever build manager you prefer (most often this is 'make', but you could also be using Eclipse or MS Visual Studio).

You probably know already that GNU Radio blocks are available in Python even if they were written in C++. This is done by the help of the Simplified Wrapper and Interface Generator (SWIG), which automatically creates glue code to make this possible. SWIG needs some instructions on how to do this, which are put into the swig/ subdirectory. Unless doing something extra clever with your block, you will not need to go into the swig/ directory; gr_modtool handles all of that for us.

If you want your blocks to be available in the GNU Radio companion, the graphical UI for GNU Radio, you need to add descriptions of the blocks and put them into grc/. Prior to version 3.8 these descriptions were XML files, but from 3.8 onward they use YAML instead.

For documentation, docs/ contains some instructions on how to extract documentation from the C++ files and Python files (we use Doxygen and Sphinx for this) and also make sure they're available as docstrings in Python. Of course, you can add custom documentation here as well.

The directory, examples/ can be used to save (guess what) examples, which are a great addendum to documentation because other developers can simply look straight at the code to see how your blocks are used.

The build system brings some baggage along, as well: the CMakeLists.txt file (one of which is present in every subdirectory) and the cmake/ folder. You can ignore the latter for now, as it brings along mainly instructions for CMake on how to find GNU Radio libraries etc. The CMakeLists.txt files need to be edited a lot in order to make sure your module builds correctly.

For our first example, we'll create a block that computes the square of its single float input. This block will accept a single float input stream and produce a single float output stream, i.e., for every incoming float item, we output one float item which is the square of that input item.

We could just start banging out the C++ code, but being highly evolved modern programmers, we're going to write the test code first. After all, we do have a good spec for the behavior: take a single stream of floats as the input and produce a single stream of floats as the output. The output should be the square of the input.

gr_unittest is an extension to the standard Python module unittest. gr_unittest adds support for checking approximate equality of tuples of float and complex numbers. Unittest uses Python's reflection mechanism to find all methods that start with test_ and runs them. Unittest wraps each call to test_* with matching calls to setUp and tearDown. See the Python unittest documentation for details.

test_001_square_ff builds a small graph that contains three nodes. blocks.vector_source_f(src_data) will source the elements of src_data and then say that it's finished. howto.square_ff is the block we're testing. blocks.vector_sink_f gathers the output of howto.square_ff.

Note that such a test is usually called before installing the module. This means that we need some trickery to be able to load the blocks when testing. CMake takes care of most things by changing PYTHONPATH appropriately. Also, we import howto_swig instead of howto in this file.

Now that we've got a test case, let's write the C++ code. All signal processing blocks are derived from gr::block or one of its subclasses. Go check out the block documentation on the Doxygen-generated manual.

gr_modtool already provided us with three files that define the block: lib/square_ff_impl.h, lib/square_ff_impl.cc and include/howto/square_ff.h. All we have to do is modify them to do our bidding. After you've finished with this tutorial please read and understand the Blocks Coding Guide to find out how these files are structured and why!

First of all, we have a look at our header files. Because the block we're writing is so simple, we don't have to actually change them (the header file in include/ is often quite complete after running gr_modtool, unless we need to add some public methods such as mutator methods, i.e., getters and setters). That leaves us with lib/square_ff_impl.cc.

forecast() is a function which tells the scheduler how many input items are required to produce noutput_items output items. In this case, they're the same. The index 0 indicates that this is for the first port, but we only have one anyway. This is generally the case for forecast in a lot of blocks. For examples, you can look at how gr::block, gr::sync_block, gr::sync_decimator, and gr::sync_interpolator define the default forecast functions to account for things like rate changes and history.

If you've never used CMake before, this is a good time to give it a try. The typical workflow of a CMake-based project as seen from the command line is this (if using PyBOMBS, first read Build Tree vs. Install Tree):

If you installed GNU Radio using PyBOMBS, the install tree is located in the target/ directory set during the initial PyBOMBS configuration. Make sure to add the -DCMAKE_INSTALL_PREFIX switch for CMake, so that it will correctly locate your GNU Radio installation. The command should look similar to this:

Now we have a new directory build/ in our module's directory. All the compiling etc. is done in here, so the actual source tree is not littered with temporary files. If we change any CMakeLists.txt files, we should re-run cmake ../ (although in truth, cmake detects these changes and reruns automatically when you next run make). During compilation, the libraries are copied into the build tree. Only during installation, files are installed to the install tree, thus making our blocks available to GNU Radio apps.

We write our applications such that they access the code and libraries in the install tree. On the other hand, we want our test code to run on the build tree, where we can detect problems before installation.

We use make test to run our tests (run this from the build/ subdirectory, after calling cmake and make). This invokes a shell script which sets up the PYTHONPATH environment variable so that our tests use the build tree versions of our code and libraries. It then runs all files which have names of the form qa_*.py and reports the overall success or failure.

c80f0f1006
Reply all
Reply to author
Forward
0 new messages