Creating C++ OOT with gr-modtool: Difference between revisions

From GNU Radio
Jump to navigation Jump to search
(adding description of OOT block received via docs channel)
 
(61 intermediate revisions by 5 users not shown)
Line 1: Line 1:
<div style="float:right">
This tutorial describes how to create a custom C++ block and use it in a flowgraph:
{{Template:BeginnerTutorials}}
* Create a new C++ block using ''gr_modtool''
</div>
* Modify the C++ .h and .cc code so the block will function
* Modify the YAML file so it can be read in GRC
* Install and run the block in a flowgraph


TODO: intro material
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|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.
The previous tutorial, [[Creating_Python_OOT_with_gr-modtool|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.
 
 
TODO: pick up from python OOT tutorial
 
TODO: link back to python tutorial


== Installation Note ==
== Installation Note ==
Line 22: Line 19:
<pre>$ sudo apt-get install gnuradio-dev cmake libspdlog-dev clang-format</pre>
<pre>$ sudo apt-get install gnuradio-dev cmake libspdlog-dev clang-format</pre>


== Adding a New Block ==
== Creating an OOT Block ==


Move to the ''gr-customModule'' directory created in the [[Creating_Python_OOT_with_gr-modtool|Creating Python OOT with gr-modtool]] tutorial:
Move to the ''gr-customModule'' directory created in the [[Creating_Python_OOT_with_gr-modtool|Creating Python OOT with gr-modtool]] tutorial:
<pre>cd your-path/gr-customModule</pre>
<pre>cd your-path/gr-customModule</pre>


Add a new block named ''multDivSelector'':
Add a new block named ''multDivSelect'':
<pre>$ gr_modtool add multDivSelector</pre>
<pre>$ gr_modtool add multDivSelect</pre>


The types of blocks will be displayed:
The types of blocks will be displayed:
Line 35: Line 32:
('sink', 'source', 'sync', 'decimator', 'interpolator', 'general', 'tagged_stream', 'hier', 'noblock')</pre>
('sink', 'source', 'sync', 'decimator', 'interpolator', 'general', 'tagged_stream', 'hier', 'noblock')</pre>


Enter ''sync'' as the block type:
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.
<pre>Enter block type: sync</pre>
<pre>Enter block type: sync</pre>


Line 41: Line 38:
<pre>Language (python/cpp): cpp
<pre>Language (python/cpp): cpp
Language: C++
Language: C++
Block/code identifier: multDivSelector</pre>
Block/code identifier: multDivSelect</pre>


Enter the name or organization of the copyright holder:
Enter the name or organization of the copyright holder:
<pre>Please specify the copyright holder: YourName</pre>
<pre>Please specify the copyright holder: YourName</pre>


Create a boolean variable ''selector'' with a default value of ''true'':
Our OOT block allows the ''gnuradio-companion'' user to specify a selector value, which at the C++ level is a boolean (true/false) value.  To enable this, we need to enter the C++ expression shown below that declares the ''selector'' variable with a default value of ''true'' as an argument to our OOT block:
 
<pre>Enter valid argument list, including default arguments:  
<pre>Enter valid argument list, including default arguments:  
bool selector=true</pre>
bool selector=true</pre>
Line 55: Line 53:


Multiple files will then be created or modified:
Multiple files will then be created or modified:
<pre>Adding file 'lib/multDivSelector_impl.h'...
<pre>Adding file 'lib/multDivSelect_impl.h'...
Adding file 'lib/multDivSelector_impl.cc'...
Adding file 'lib/multDivSelect_impl.cc'...
Adding file 'include/gnuradio/customModule/multDivSelector.h'...
Adding file 'include/gnuradio/customModule/multDivSelect.h'...
Adding file 'python/customModule/bindings/docstrings/multDivSelector_pydoc_template.h'...
Adding file 'python/customModule/bindings/docstrings/multDivSelect_pydoc_template.h'...
Adding file 'python/customModule/bindings/multDivSelector_python.cc'...
Adding file 'python/customModule/bindings/multDivSelect_python.cc'...
Adding file 'grc/customModule_multDivSelector.block.yml'...
Adding file 'grc/customModule_multDivSelect.block.yml'...
Editing grc/CMakeLists.txt...</pre>
Editing grc/CMakeLists.txt...</pre>


== Editing the c++ Header ==
== Modifying the C++ impl.h Header ==


Many of the files are automatically generated wrapper code that do not need to be modified. However, the ''multDivSelector_impl.h'' and ''multDivSelector_impl.cc'' files defines the operation of the block and must be modified. Open the file with a text editor:
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:


<pre>$ gedit lib/multDivSelector_impl.h &</pre>
<pre>$ gedit lib/multDivSelect_impl.h &</pre>


The following code will be displayed:
The following code will be displayed:
Line 78: Line 76:
  */
  */


#ifndef INCLUDED_CUSTOMMODULE_MULTDIVSELECTOR_IMPL_H
#ifndef INCLUDED_CUSTOMMODULE_MULTDIVSELECT_IMPL_H
#define INCLUDED_CUSTOMMODULE_MULTDIVSELECTOR_IMPL_H
#define INCLUDED_CUSTOMMODULE_MULTDIVSELECT_IMPL_H


#include <gnuradio/customModule/multDivSelector.h>
#include <gnuradio/customModule/multDivSelect.h>


namespace gr {
namespace gr {
namespace customModule {
namespace customModule {


class multDivSelector_impl : public multDivSelector
class multDivSelect_impl : public multDivSelect
{
{
private:
private:
Line 92: Line 90:


public:
public:
     multDivSelector_impl(bool selector);
     multDivSelect_impl(bool selector);
     ~multDivSelector_impl();
     ~multDivSelect_impl();


     // Where all the action really happens
     // Where all the action really happens
Line 104: Line 102:
} // namespace gr
} // namespace gr


#endif /* INCLUDED_CUSTOMMODULE_MULTDIVSELECTOR_IMPL_H */</syntaxhighlight>
#endif /* INCLUDED_CUSTOMMODULE_MULTDIVSELECT_IMPL_H */</syntaxhighlight>


Create a boolean private member ''_selector'' which will hold the value of the ''selector'' parameter:
Create a boolean private member ''_selector'' which will hold the value of the ''selector'' parameter:


<syntaxhighlight lang="cpp" line>class multDivSelector_impl : public multDivSelector
<syntaxhighlight lang="cpp" line>class multDivSelect_impl : public multDivSelect
{
{
private:
private:
Line 116: Line 114:
Press CTRL + S to save the file.
Press CTRL + S to save the file.


== Editing the c++ impl ==
== Modifying the C++ impl.cc File ==


The ''.cc'' file needs to be modified to define the desired operation of the block. Open the file with a text editor:
The ''.cc'' file needs to be modified to define the desired operation of the block. Open the file with a text editor:


<pre>$ gedit multDivSelector_impl.cc &</pre>
<pre>$ gedit lib/multDivSelect_impl.cc &</pre>


The code will be as displayed:
The code will be as displayed:


<syntaxhighlight lang="cpp" line>/* -*- c++ -*- */
<syntaxhighlight lang="cpp" line>/* -*- C++ -*- */
/*
/*
  * Copyright 2022 YourName.
  * Copyright 2022 YourName.
Line 131: Line 129:
  */
  */


#include "multDivSelector_impl.h"
#include "multDivSelect_impl.h"
#include <gnuradio/io_signature.h>
#include <gnuradio/io_signature.h>


Line 141: Line 139:
#pragma message("set the following appropriately and remove this warning")
#pragma message("set the following appropriately and remove this warning")
using output_type = float;
using output_type = float;
multDivSelector::sptr multDivSelector::make(bool selector)
multDivSelect::sptr multDivSelect::make(bool selector)
{
{
     return gnuradio::make_block_sptr<multDivSelector_impl>(selector);
     return gnuradio::make_block_sptr<multDivSelect_impl>(selector);
}
}


Line 150: Line 148:
  * The private constructor
  * The private constructor
  */
  */
multDivSelector_impl::multDivSelector_impl(bool selector)
multDivSelect_impl::multDivSelect_impl(bool selector)
     : gr::sync_block("multDivSelector",
     : gr::sync_block("multDivSelect",
                     gr::io_signature::make(
                     gr::io_signature::make(
                         1 /* min inputs */, 1 /* max inputs */, sizeof(input_type)),
                         1 /* min inputs */, 1 /* max inputs */, sizeof(input_type)),
Line 162: Line 160:
  * Our virtual destructor.
  * Our virtual destructor.
  */
  */
multDivSelector_impl::~multDivSelector_impl() {}
multDivSelect_impl::~multDivSelect_impl() {}


int multDivSelector_impl::work(int noutput_items,
int multDivSelect_impl::work(int noutput_items,
                               gr_vector_const_void_star& input_items,
                               gr_vector_const_void_star& input_items,
                               gr_vector_void_star& output_items)
                               gr_vector_void_star& output_items)
Line 188: Line 186:
</syntaxhighlight>
</syntaxhighlight>


== Compile and Install ==
Update to two inputs and store the value of the ''selector'' parameter using the private member ''_selector'' as defined in ''multDivSelector_impl.h'':
 
<syntaxhighlight lang="cpp" line>
/*
* The private constructor
*/
multDivSelect_impl::multDivSelect_impl(bool selector)
    : gr::sync_block("multDivSelect",
                    gr::io_signature::make(
                        2 /* min inputs */, 2 /* max inputs */, sizeof(input_type)),
                    gr::io_signature::make(
                        1 /* min outputs */, 1 /*max outputs */, sizeof(output_type)))
{
    _selector = selector;
}</syntaxhighlight>
 
Modify the ''work()'' function by removing the ''pragma'' message, defining the variables ''in0'' and ''in1'' corresponding to the two input ports, and multiply the two inputs if ''_selector'' is true and divide them if ''_selector'' is false:
 
<syntaxhighlight lang="cpp" line>
int multDivSelect_impl::work(int noutput_items,
                              gr_vector_const_void_star& input_items,
                              gr_vector_void_star& output_items)
{
    auto in0 = static_cast<const input_type*>(input_items[0]);
    auto in1 = static_cast<const input_type*>(input_items[1]);
    auto out = static_cast<output_type*>(output_items[0]);
 
    for (int index = 0; index < noutput_items; index++) {
        if (_selector) { out[index] = in0[index] * in1[index]; }
        else{ out[index] = in0[index] / in1[index]; }
    }
 
 
    // Tell runtime system how many output items we produced.
    return noutput_items;
}</syntaxhighlight>
 
Press CTRL + S to save the file.
 
== Modifying the YAML .yml File ==
 
''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.
 
Open our block's YAML file using a text editor:
 
<pre>$ gedit grc/customModule_multDivSelect.block.yml &</pre>
 
The code will be displayed:
 
<syntaxhighlight lang="yaml" line>
id: customModule_multDivSelect
label: multDivSelect
category: '[customModule]'
 
templates:
  imports: from gnuradio import customModule
  make: customModule.multDivSelect(${selector})
 
#  Make one 'parameters' list entry for every parameter you want settable from the GUI.
#    Keys include:
#    * id (makes the value accessible as keyname, e.g. in the make entry)
#    * label (label shown in the GUI)
#    * dtype (e.g. int, float, complex, byte, short, xxx_vector, ...)
#    * default
parameters:
- id: parametername_replace_me
  label: FIX ME:
  dtype: string
  default: You need to fill in your grc/customModule_multDivSelect.block.yaml
#- id: ...
#  label: ...
#  dtype: ...
 
#  Make one 'inputs' list entry per input and one 'outputs' list entry per output.
#  Keys include:
#      * label (an identifier for the GUI)
#      * domain (optional - stream or message. Default is stream)
#      * dtype (e.g. int, float, complex, byte, short, xxx_vector, ...)
#      * vlen (optional - data stream vector length. Default is 1)
#      * optional (optional - set to 1 for optional inputs. Default is 0)
inputs:
#- label: ...
#  domain: ...
#  dtype: ...
#  vlen: ...
#  optional: ...
 
outputs:
#- label: ...
#  domain: ...
#  dtype: ...
#  vlen: ...
#  optional: ...
 
#  'file_format' specifies the version of the GRC yml format used in the file
#  and should usually not be changed.
file_format: 1</syntaxhighlight>
 
Update the parameter definition with the information for ''selector'':
 
<syntaxhighlight lang="yaml" line>
parameters:
- id: selector
  label: Selector, Multiply (true) or Divide (false)
  dtype: bool
  default: true</syntaxhighlight>
 
Update the input port and output port definitions:
 
<syntaxhighlight lang="yaml" line>
inputs:
- label: in0
  domain: stream
  dtype: complex
- label: in1
  domain: stream
  dtype: complex
outputs:
- label: out0
  domain: stream
  dtype: complex</syntaxhighlight>
 
Press CTRL + S to save the file.
 
== Compiling and Installing the Block ==
 
The block needs to be compiled and installed. Ensure you are in the ''gr-customModule'' directory:
 
<pre>$ cd your-path/gr-customModule</pre>
 
If the ''build/'' directory already exists, remove it:
 
<pre>rm -rf build/</pre>
 
Create the build directory:
 
<pre>$ mkdir build</pre>
 
Move into the build directory:
 
<pre>cd build</pre>
 
Run cmake to build the makefiles:
 
<pre>cmake ..</pre>
 
Compile the module:
 
<pre>make</pre>
 
Install the module:
 
<pre>sudo make install</pre>
 
The new files will then be installed:
 
<pre>-- Install configuration: "Release"
-- Up-to-date: /usr/local/lib/cmake/gnuradio-customModule/gnuradio-customModuleConfig.cmake
-- Up-to-date: /usr/local/include/gnuradio/customModule/api.h
-- Installing: /usr/local/include/gnuradio/customModule/multDivSelect.h
-- Installing: /usr/local/lib/x86_64-linux-gnu/libgnuradio-customModule.so.1.0.0.0
-- Installing: /usr/local/lib/x86_64-linux-gnu/libgnuradio-customModule.so.1.0.0
-- Installing: /usr/local/lib/x86_64-linux-gnu/libgnuradio-customModule.so
-- Installing: /usr/local/lib/cmake/gnuradio-customModule/gnuradio-customModuleTargets.cmake
-- Installing: /usr/local/lib/cmake/gnuradio-customModule/gnuradio-customModuleTargets-release.cmake
-- Installing: /usr/local/lib/cmake/gnuradio-customModule/gnuradio-customModuleConfig.cmake
-- Up-to-date: /usr/local/lib/python3.9/dist-packages/gnuradio/customModule/__init__.py
-- Up-to-date: /usr/local/lib/python3.9/dist-packages/gnuradio/customModule/addSubSelect.py
-- Installing: /usr/local/lib/python3.9/dist-packages/gnuradio/customModule/__init__.pyc
-- Installing: /usr/local/lib/python3.9/dist-packages/gnuradio/customModule/addSubSelect.pyc
-- Installing: /usr/local/lib/python3.9/dist-packages/gnuradio/customModule/__init__.pyo
-- Installing: /usr/local/lib/python3.9/dist-packages/gnuradio/customModule/addSubSelect.pyo
-- Installing: /usr/local/lib/python3.9/dist-packages/gnuradio/customModule/customModule_python.cpython-39-x86_64-linux-gnu.so
-- Set runtime path of "/usr/local/lib/python3.9/dist-packages/gnuradio/customModule/customModule_python.cpython-39-x86_64-linux-gnu.so" to ""
-- Up-to-date: /usr/local/share/gnuradio/grc/blocks/customModule_addSubSelect.block.yml
-- Installing: /usr/local/share/gnuradio/grc/blocks/customModule_multDivSelect.block.yml</pre>
 
Some of the files are listed as ''Up-to-date'' because they correspond to the ''addSubSelect'' block created in the [[Creating_Python_OOT_with_gr-modtool|Creating Python OOT with gr-modtool]] tutorial. All of the files corresponding to ''multDivSelect'' are now installed to ''/usr/local/''.
 
Run ''ldconfig'' to update the linking for the customModule library:
 
<pre>$ sudo ldconfig </pre>
 
== Using the Custom Block in a Flowgraph ==
 
If GRC is already running, press the ''Reload'' button to refresh the list of blocks in the library:
 
[[File:ReloadBlockLibrary.png|800px]]
 
Otherwise, start GRC from the command line:
<pre>$ gnuradio-companion &</pre>
 
The ''multDivBlock'' can now be seen under the ''customModule'' tab. The ''addSubSelect'' is a Python block created in the [[Creating_Python_OOT_with_gr-modtool|Creating Python OOT with gr-modtool]] tutorial.
 
[[File:MultDivBlockLibrary.png|800px]]
 
Drag the ''multDivSelect'' block into the flowgraph:
 
[[File:AddMultDivToWorkspace.png|800px]]
 
Now drag in the following blocks and update their properties:
* Signal Source
** Frequency: 100
* Constant Source
** Constant: 2
* Throttle
* QT GUI Time Sink
** Autoscale: Yes
 
Connect the flowgraph accordingly:
 
[[File:MultDivFlowgraph.png|800px]]
 
== Running the Flowgraph ==
 
Run the flowgraph. The ''QT GUI Time Sink'' will display the following output. Notice that the amplitude of the sinusoid is 2, due to the multiplication by 2 in the ''multDivSelect'' block.
 
[[File:MultDivMultOutput.png|700px]]
 
Edit the properties of the ''multDivSelect'' block and enter ''False'' for the ''selector'':
 
[[File:MultDivProperties.png|500px]]
 
Click ''OK'' to save the property.
 
Run the flowgraph. The ''QT GUI Time Sink'' will display the following output. Notice that the amplitude of the sinusoid is 0.5, due to the division by 2 in the ''multDivSelect'' block.
 
[[File:MultDiv_DivOutput.png|700px]]
 
== Making Changes ==
 
It is suggested to recompile and reinstall the module any time a change is made, followed by reloading the block library in GRC. This includes changes such as:
* Number of parameters
* Type of parameters
* Number of input ports or output ports
* Types of input ports or output ports
* Modifying the YAML .yml file
* Modifying any C++ .h or .cc files
 
Removing and re-creating the ''build/'' directory may be necessary before recompiling and reinstalling the module depending on the scope of the change:
 
<pre>$ rm -rf gr-customModule/build
$ mkdir gr-customModule/build</pre>


rm -rf build
The previous tutorial, [[Creating_Python_OOT_with_gr-modtool|Creating Python OOT with gr-modtool]], describes how to build a custom Python OOT module.
mkdir build
cd build
cmake ..

Latest revision as of 15:13, 18 May 2024

This tutorial describes how to create a custom C++ block and use it in a flowgraph:

  • Create a new C++ block using gr_modtool
  • Modify the C++ .h and .cc code so the block will function
  • Modify the YAML file so it can be read in GRC
  • Install and run the block in a flowgraph

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.

Installation Note

This tutorial was written using GNU Radio v3.10.1.1 on Ubuntu 21.10, installed using the Ubuntu PPA from the Installation Wiki Page. The basic GNU Radio install using:

$ sudo apt-get install gnuradio

does not come with the proper libraries needed to compile and install OOT modules. Consider installing the following packages before continuing:

$ sudo apt-get install gnuradio-dev cmake libspdlog-dev clang-format

Creating an OOT Block

Move to the gr-customModule directory created in the Creating Python OOT with gr-modtool tutorial:

cd your-path/gr-customModule

Add a new block named multDivSelect:

$ gr_modtool add multDivSelect

The types of blocks will be displayed:

GNU Radio module name identified: customModule
('sink', 'source', 'sync', 'decimator', 'interpolator', 'general', 'tagged_stream', 'hier', 'noblock')

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.

Enter block type: sync

Enter cpp as the language:

Language (python/cpp): cpp
Language: C++
Block/code identifier: multDivSelect

Enter the name or organization of the copyright holder:

Please specify the copyright holder: YourName

Our OOT block allows the gnuradio-companion user to specify a selector value, which at the C++ level is a boolean (true/false) value. To enable this, we need to enter the C++ expression shown below that declares the selector variable with a default value of true as an argument to our OOT block:

Enter valid argument list, including default arguments: 
bool selector=true

Select whether or not QA code is desired:

Add Python QA code? [Y/n] n
Add C++ QA code? [Y/n] n

Multiple files will then be created or modified:

Adding file 'lib/multDivSelect_impl.h'...
Adding file 'lib/multDivSelect_impl.cc'...
Adding file 'include/gnuradio/customModule/multDivSelect.h'...
Adding file 'python/customModule/bindings/docstrings/multDivSelect_pydoc_template.h'...
Adding file 'python/customModule/bindings/multDivSelect_python.cc'...
Adding file 'grc/customModule_multDivSelect.block.yml'...
Editing grc/CMakeLists.txt...

Modifying the C++ impl.h Header

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:

$ gedit lib/multDivSelect_impl.h &

The following code will be displayed:

/* -*- c++ -*- */
/*
 * Copyright 2022 YourName.
 *
 * SPDX-License-Identifier: GPL-3.0-or-later
 */

#ifndef INCLUDED_CUSTOMMODULE_MULTDIVSELECT_IMPL_H
#define INCLUDED_CUSTOMMODULE_MULTDIVSELECT_IMPL_H

#include <gnuradio/customModule/multDivSelect.h>

namespace gr {
namespace customModule {

class multDivSelect_impl : public multDivSelect
{
private:
    // Nothing to declare in this block.

public:
    multDivSelect_impl(bool selector);
    ~multDivSelect_impl();

    // Where all the action really happens
    int work(int noutput_items,
             gr_vector_const_void_star& input_items,
             gr_vector_void_star& output_items);
};

} // namespace customModule
} // namespace gr

#endif /* INCLUDED_CUSTOMMODULE_MULTDIVSELECT_IMPL_H */

Create a boolean private member _selector which will hold the value of the selector parameter:

class multDivSelect_impl : public multDivSelect
{
private:
    bool _selector;

Press CTRL + S to save the file.

Modifying the C++ impl.cc File

The .cc file needs to be modified to define the desired operation of the block. Open the file with a text editor:

$ gedit lib/multDivSelect_impl.cc &

The code will be as displayed:

/* -*- C++ -*- */
/*
 * Copyright 2022 YourName.
 *
 * SPDX-License-Identifier: GPL-3.0-or-later
 */

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

namespace gr {
namespace customModule {

#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;
multDivSelect::sptr multDivSelect::make(bool selector)
{
    return gnuradio::make_block_sptr<multDivSelect_impl>(selector);
}


/*
 * The private constructor
 */
multDivSelect_impl::multDivSelect_impl(bool selector)
    : gr::sync_block("multDivSelect",
                     gr::io_signature::make(
                         1 /* min inputs */, 1 /* max inputs */, sizeof(input_type)),
                     gr::io_signature::make(
                         1 /* min outputs */, 1 /*max outputs */, sizeof(output_type)))
{
}

/*
 * Our virtual destructor.
 */
multDivSelect_impl::~multDivSelect_impl() {}

int multDivSelect_impl::work(int noutput_items,
                               gr_vector_const_void_star& input_items,
                               gr_vector_void_star& output_items)
{
    auto in = static_cast<const input_type*>(input_items[0]);
    auto out = static_cast<output_type*>(output_items[0]);

#pragma message("Implement the signal processing in your block and remove this warning")
    // Do <+signal processing+>

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

} /* namespace customModule */
} /* namespace gr */

Remove the pragma messages and define the input and output type to be gr_complex:

using input_type = gr_complex;
using output_type = gr_complex;

Update to two inputs and store the value of the selector parameter using the private member _selector as defined in multDivSelector_impl.h:

/*
 * The private constructor
 */
multDivSelect_impl::multDivSelect_impl(bool selector)
    : gr::sync_block("multDivSelect",
                     gr::io_signature::make(
                         2 /* min inputs */, 2 /* max inputs */, sizeof(input_type)),
                     gr::io_signature::make(
                         1 /* min outputs */, 1 /*max outputs */, sizeof(output_type)))
{
    _selector = selector;
}

Modify the work() function by removing the pragma message, defining the variables in0 and in1 corresponding to the two input ports, and multiply the two inputs if _selector is true and divide them if _selector is false:

int multDivSelect_impl::work(int noutput_items,
                               gr_vector_const_void_star& input_items,
                               gr_vector_void_star& output_items)
{
    auto in0 = static_cast<const input_type*>(input_items[0]);
    auto in1 = static_cast<const input_type*>(input_items[1]);
    auto out = static_cast<output_type*>(output_items[0]);

    for (int index = 0; index < noutput_items; index++) {
        if (_selector) { out[index] = in0[index] * in1[index]; }
        else{ out[index] = in0[index] / in1[index]; }
    }


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

Press CTRL + S to save the file.

Modifying the YAML .yml File

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.

Open our block's YAML file using a text editor:

$ gedit grc/customModule_multDivSelect.block.yml &

The code will be displayed:

id: customModule_multDivSelect
label: multDivSelect
category: '[customModule]'

templates:
  imports: from gnuradio import customModule
  make: customModule.multDivSelect(${selector})

#  Make one 'parameters' list entry for every parameter you want settable from the GUI.
#     Keys include:
#     * id (makes the value accessible as keyname, e.g. in the make entry)
#     * label (label shown in the GUI)
#     * dtype (e.g. int, float, complex, byte, short, xxx_vector, ...)
#     * default
parameters:
- id: parametername_replace_me
  label: FIX ME:
  dtype: string
  default: You need to fill in your grc/customModule_multDivSelect.block.yaml
#- id: ...
#  label: ...
#  dtype: ...

#  Make one 'inputs' list entry per input and one 'outputs' list entry per output.
#  Keys include:
#      * label (an identifier for the GUI)
#      * domain (optional - stream or message. Default is stream)
#      * dtype (e.g. int, float, complex, byte, short, xxx_vector, ...)
#      * vlen (optional - data stream vector length. Default is 1)
#      * optional (optional - set to 1 for optional inputs. Default is 0)
inputs:
#- label: ...
#  domain: ...
#  dtype: ...
#  vlen: ...
#  optional: ...

outputs:
#- label: ...
#  domain: ...
#  dtype: ...
#  vlen: ...
#  optional: ...

#  'file_format' specifies the version of the GRC yml format used in the file
#  and should usually not be changed.
file_format: 1

Update the parameter definition with the information for selector:

parameters:
- id: selector
  label: Selector, Multiply (true) or Divide (false)
  dtype: bool
  default: true

Update the input port and output port definitions:

inputs:
- label: in0
  domain: stream
  dtype: complex
- label: in1
  domain: stream
  dtype: complex
outputs:
- label: out0
  domain: stream
  dtype: complex

Press CTRL + S to save the file.

Compiling and Installing the Block

The block needs to be compiled and installed. Ensure you are in the gr-customModule directory:

$ cd your-path/gr-customModule

If the build/ directory already exists, remove it:

rm -rf build/

Create the build directory:

$ mkdir build

Move into the build directory:

cd build

Run cmake to build the makefiles:

cmake ..

Compile the module:

make

Install the module:

sudo make install

The new files will then be installed:

-- Install configuration: "Release"
-- Up-to-date: /usr/local/lib/cmake/gnuradio-customModule/gnuradio-customModuleConfig.cmake
-- Up-to-date: /usr/local/include/gnuradio/customModule/api.h
-- Installing: /usr/local/include/gnuradio/customModule/multDivSelect.h
-- Installing: /usr/local/lib/x86_64-linux-gnu/libgnuradio-customModule.so.1.0.0.0
-- Installing: /usr/local/lib/x86_64-linux-gnu/libgnuradio-customModule.so.1.0.0
-- Installing: /usr/local/lib/x86_64-linux-gnu/libgnuradio-customModule.so
-- Installing: /usr/local/lib/cmake/gnuradio-customModule/gnuradio-customModuleTargets.cmake
-- Installing: /usr/local/lib/cmake/gnuradio-customModule/gnuradio-customModuleTargets-release.cmake
-- Installing: /usr/local/lib/cmake/gnuradio-customModule/gnuradio-customModuleConfig.cmake
-- Up-to-date: /usr/local/lib/python3.9/dist-packages/gnuradio/customModule/__init__.py
-- Up-to-date: /usr/local/lib/python3.9/dist-packages/gnuradio/customModule/addSubSelect.py
-- Installing: /usr/local/lib/python3.9/dist-packages/gnuradio/customModule/__init__.pyc
-- Installing: /usr/local/lib/python3.9/dist-packages/gnuradio/customModule/addSubSelect.pyc
-- Installing: /usr/local/lib/python3.9/dist-packages/gnuradio/customModule/__init__.pyo
-- Installing: /usr/local/lib/python3.9/dist-packages/gnuradio/customModule/addSubSelect.pyo
-- Installing: /usr/local/lib/python3.9/dist-packages/gnuradio/customModule/customModule_python.cpython-39-x86_64-linux-gnu.so
-- Set runtime path of "/usr/local/lib/python3.9/dist-packages/gnuradio/customModule/customModule_python.cpython-39-x86_64-linux-gnu.so" to ""
-- Up-to-date: /usr/local/share/gnuradio/grc/blocks/customModule_addSubSelect.block.yml
-- Installing: /usr/local/share/gnuradio/grc/blocks/customModule_multDivSelect.block.yml

Some of the files are listed as Up-to-date because they correspond to the addSubSelect block created in the Creating Python OOT with gr-modtool tutorial. All of the files corresponding to multDivSelect are now installed to /usr/local/.

Run ldconfig to update the linking for the customModule library:

$ sudo ldconfig 

Using the Custom Block in a Flowgraph

If GRC is already running, press the Reload button to refresh the list of blocks in the library:

ReloadBlockLibrary.png

Otherwise, start GRC from the command line:

$ gnuradio-companion &

The multDivBlock can now be seen under the customModule tab. The addSubSelect is a Python block created in the Creating Python OOT with gr-modtool tutorial.

MultDivBlockLibrary.png

Drag the multDivSelect block into the flowgraph:

AddMultDivToWorkspace.png

Now drag in the following blocks and update their properties:

  • Signal Source
    • Frequency: 100
  • Constant Source
    • Constant: 2
  • Throttle
  • QT GUI Time Sink
    • Autoscale: Yes

Connect the flowgraph accordingly:

MultDivFlowgraph.png

Running the Flowgraph

Run the flowgraph. The QT GUI Time Sink will display the following output. Notice that the amplitude of the sinusoid is 2, due to the multiplication by 2 in the multDivSelect block.

MultDivMultOutput.png

Edit the properties of the multDivSelect block and enter False for the selector:

MultDivProperties.png

Click OK to save the property.

Run the flowgraph. The QT GUI Time Sink will display the following output. Notice that the amplitude of the sinusoid is 0.5, due to the division by 2 in the multDivSelect block.

MultDiv DivOutput.png

Making Changes

It is suggested to recompile and reinstall the module any time a change is made, followed by reloading the block library in GRC. This includes changes such as:

  • Number of parameters
  • Type of parameters
  • Number of input ports or output ports
  • Types of input ports or output ports
  • Modifying the YAML .yml file
  • Modifying any C++ .h or .cc files

Removing and re-creating the build/ directory may be necessary before recompiling and reinstalling the module depending on the scope of the change:

$ rm -rf gr-customModule/build
$ mkdir gr-customModule/build

The previous tutorial, Creating Python OOT with gr-modtool, describes how to build a custom Python OOT module.