Guided Tutorial GNU Radio in Python

&lt;. Previous: Working with GRC &gt;. Next: Programming GNU Radio in C++

= Tutorial: Working with GNU Radio in Python =

Objectives

 * Python Blocks
 * OOT modules make the actual apps / functionality (GR is the API!)
 * How to add OOTs
 * How to add Python blocks with gr_modtool and how to code them
 * QPSK mapping
 * How to add GRC bindings for block

Prerequisites

 * Working Installation of GNU Radio 3.7.4 or later
 * GRC Tutorial (Recommended)
 * Familiar with Python

-

3.1. Intro to Using GNU Radio with Python
This tutorial goes through three parts. The first is how to modify, create, or simply understand the Python generated files GRC produces for us. The second is how to create our own custom out-of-tree (OOT) modules from the ground up. Lastly we go through an actual project to get more practice and build intuition on how we can use GNU Radio in our own project. As with the last tutorial, all the content - pictures, source code, and grc files - is included in the gr-tutorial repository which we should have a local copy if we followed the directions from the GRC Tutorial

Again we should have a directory with the solutions and a directory with our work as below:

As a rule, if we hover over the GRC flowgraph images, we will be able to see the corresponding filename. Same applies for other images. Full code files are collapsed with the filename in the collapsed handle.

3.1.1. GRC Generated Python Files
Let us look at a dial-tone example on the GRC:

https://raw.githubusercontent.com/gnuradio/gr-tutorial/master/examples/tutorial3/images/tutorial_three_1.png

When we click the Generate button, the terminal tells us it produced a .py file so let's open that to examine its code which is reproduced below:

The first thing for us to realize is that the GRC can generate Python files that we can then modify to do things we wouldn't be able to do in GNU Radio Companion such as perform simulations. The libraries available in Python open up a whole new realm of possibilities! For now, we will explore the structure of the GRC Python files so we are comfortable creating more interesting applications.

3.1.2. Hello World Dissected
While examining the code, we need to get familiar with documentation. GNU Radio uses Doxygen (the software) for the GNU Radio Manual. The easiest way to go through the documentation is to go through the functions that we use so let us simplify our code by only including the bare bones needed to run the dial-tone example.

which is used to test our code, one new file  which is the functional part, and one new file , which is used to link the block to the GRC. Again all this happens in the Python and grc subfolders.

3.2.2.1. What's with the ?
For blocks with strict types, we use suffixes to declare the input and output types. This block operates on floats, so we give it the suffix : Float in, float out. Other suffixes are  (complex in, complex out), or simply   (a sink or source with no in- or outputs that uses floats). For a more detailed description, see the FAQ or the BlocksCodingGuide.

3.2.3. Modifying the Python Block File
Let's begin with the multiply_py_ff.py file found in the Python folder. Opening it without any changes gives the following:

to return  since output_items is empty). Our changes to the first placeholders should appear as follows:

We can change the name that appears and the category it will appear in GRC. The category is where the block will be found in GRC. Examples of categories tag are Audio and Waveform Generators used in previous examples. Examples of names tag are the QT GUI Time Sink or the Audio Sink. Again, we can go through the file and find the modtool place holders. The first is copied below:

}}

Once we have our constructor setup, we can go onto the work function. For simplicity and beauty, let us call the pseudocode we made above a function &quot;get_minimum_distance&quot; that takes samples as input arguments. In our multiply_py_ff example, we took all the samples and multiplied them with with out[:] = in0*self.multiple. The in0 is actually a vector so contains many samples within it. The multiply example required the same operation for each sample so it was okay to simply operate on the entire vector but now we need to have different operations per sample so what do we do?

Now we can move onto the get_minimum_distances(self, sample) function. We already have pseudo code so the next step is translating to Python. Below is a snip of what the code can look like. Again there are multiple ways to do this

   def get_minimum_distances(self, sample): if self.gray_code == 1: if (sample.imag &gt;= 0 and sample.real &gt;= 0): return 0 # 1+1j elif (sample.imag &gt;= 0 and sample.real &lt; 0): return 2 # -1+1j Let us try to fill in the other cases for gray code and non-gray code. Below is what the entire file Python file can look like once complete:

Now that we have code, we know what's next!

3.3.4. Multiple QA Tests
We can test our qpsk_demod_py for when it is in gray_code mode and when it's not in gray_code mode. To do that we need to setup multiple tests in our single QA file. QA tests generally follow the same setup from before. We select some inputs as tests and we check them against what we expect the outputs to be. The only difference from the multiply qa test is that this qa test requires more cases. There are four quadrants we need to map and two modes so in total there are eight test cases. We can open up our qa_qpsk_demod_py_ff.py file to change things.

We can copy the def test_001_t from the qa_multiply_py_ff code which is copied below:

   def test_001_t (self): src_data = (-3, 4, -5.5, 2, 3) expected_result = (-6, 8, -11, 4, 6) src = blocks.vector_source_f (src_data) mult = multiply_py_ff (2) dst = blocks.vector_sink_f self.tb.connect (src, mult) self.tb.connect (mult, dst) self.tb.run result_data = dst.data self.assertFloatTuplesAlmostEqual (expected_result, result_data, 6) This time we are working with a complex input so our src = blocks.vector_source_f must change. If we use the search bar in the manual we can find the other options:

PIC of SEARCH

b - bytes/unsigned char/int8

c - complex

f - float

i - int

s - short

Before we move onto actual test cases, let us decide which mode we are testing for the test_001_t. We can create a new variable and assign it False (translates to 0) to test non-Gray code

gray_code = False Once we know we want to test non gray_code mappings, we can refer to our chart above and start placing in the proper inputs and outputs into the src_data and the expected_results. For instance if we were testing only two cases for non gray_code, we would do:

src_data = ((-1-1j), (-1+1j)) expected_result = (2, 3) Last thing to do is call upon our new block in the &quot;qpsk =&quot; line and pass it the gray_code parameter

Now that we are done with the non gray_code test, we can simply create another test &quot;def test_002_t (self):&quot; and copy the contents underneath making sure that for this test we set gray_code = True and change the expected_result so it matches gray_code mapping. The full test is copied below:

We can then run the test in Python and all should say something similar to:

Ran 2 tests in 0.005s

OK So once we verify it works as expected, we can then edit our XML file so that it is usable inside GRC.

3.3.5. XML Mods, Installation, and Running
This XML is very similar to the XML file for the multiply_py_ff block so all we need to do is set the gray_code parameter and pick the correct input (complex) and output (byte) types. A copy of the full XML file is below:

We can then install as we did for the multiply block however we need to rerun cmake in order to take into account the new block:

 cd build cmake ../ make sudo make install sudo ldconfig <\syntaxhighlight> Then we can open up our GRC file from the beginning and place our missing block we just made.

3.4. Conclusion
And that is it for now with Python. Let us know your thoughts before going on to the C++ tutorial.

3.4.1. Questions We Should Now be Able to Answer
1. How do we set input- and output signatures in Python blocks?

2. Consider this I/O signature: (FIXME). Which input types and output types does it take?

3.4.2. Links to Further Information

 * Blocks Coding Guide
 * Out-of-Tree Modules
 * Writing Python Applications

3.5. Candidates for Future Sections
Possible topics we may want to add depending on feedback and questions on the mailing list

- How to add documentation to blocks

- Constructor arguments, History, Output Multiple

-

&lt;. Previous: Working with GRC &gt;. Next: Programming GNU Radio in C++