TutorialsWritePythonApplications

= Introduction =

Welcome, GNU Radio beginners. If you are reading this tutorial, you probably already have some very basic knowledge about how GNU Radio works, what it is and what it can do - and now you want to enter this exciting world of Open Source digital signal processing (DSP) yourself.

This is a tutorial on how to write applications for GNU Radio in Python using the version 3.7 API. It is no introduction to programming, software radio or signal processing, nor does it cover how to extend GNU Radio by creating new blocks or adding code to the source tree. If you have some background in the mentioned topics and are starting to work with GNU Radio, this probably is the correct tutorial for you. If you don't know what a Software Radio is or what a FIR filter does, you should probably go a few steps back and get a more solid background on signal processing theory. But don't let this discourage you - the best way to learn something is by trying it out.

Although this tutorial is designed to make your introduction to GNU Radio as easy as possible, it is not a definitive guide. In fact, I might sometimes simply not tell the real truth to make explanations easier. I might even contradict myself in later chapters. Usage of brain power is still necessary to develop GNU Radio applications.

= Preliminaries =

Before you get started with this tutorial, make sure your GNU Radio installation is ready and working. You don't necessarily need a USRP, but some kind of source and sink (USRP, audio or other hardware) is helpful, although not strictly required. If the GNU Radio examples work (such as dial_tone.py in gr-audio/examples/python), you're ready to go.

You should also have some background in programming - but don't worry if you've never programmed Python, it is a very easy language to learn.

= Understanding flow graphs =

Before we start banging out code, first we need to understand the most basic concepts about GNU Radio: flow graphs (as in graph theory) and blocks. Many GNU Radio applications contain nothing other than a flow graph. The nodes of such a graph are called blocks, and the data flows along the edges.

Any actual signal processing is done in the blocks. Ideally, every block does exactly one job - this way GNU Radio stays modular and flexible. Blocks are written in C++; writing new blocks is not very difficult (but explained elsewhere).

The data passing between blocks can be of any kind - practically any type of data you can define in C++ is possible. In practice, the most common data types are complex and real short or long integers and floating point values as most of the time, data passing from one block to the next will either be samples or bits.

Examples
In order to illuminate this diffuse topic a little, let's start with some examples:

Low-pass filtered audio recorder

+-+ +-+  ++  | Mic +--+ LPF +--+ Record to file | +-+ +-+  ++ First, an audio signal from a microphone is recorded by your PCs sound card and converted into a digital signal. The samples are streamed to the next block, the low pass filter (LPF), which could be implemented as an FIR filter. The filtered signal is passed on to the final block, which records the filtered audio signal into a file.

This is a simple, yet complete flow graph. The first and last block serve a special purpose: they operate as source and sink. Every flow graph needs at least one source and sink to be able to function.

Dial tone generator

++ | Sine generator (350Hz) +---+ ++  |   ++                               +---+            |                                   | Audio sink | +---+           |  ++   |   ++  | Sine generator (440Hz) +---+ ++ This simple example is often called the &quot;Hello World of GNU Radio&quot;. Other than the first example, it has two sources. The sink, on the other hand, has two inputs - in this case for the left and right channel of the sound card. Code for this example is available at gr-audio/examples/python/dial_tone.py.

QPSK Demodulator

+-+ ++  +--+   | USRP Source +--+ Frequency sync +--+ Matched filter   | +-+ ++  +---+--+                                                    |           COMPLEX SAMPLES +-+--+                                     | Symbol demodulator | +-+--+                                                   |           COMPLEX SYMBOLS +-+ +-+  +--+--+   | Source decoder  +--+ Channel decoder +--+ Bit mapping | +++ +-+  +-+            |                                                   BITS +++  | Application     |                                          DATA +-+ This example is a bit more sophisticated, but should look quite familiar to RF engineers. In this case, the source is a USRP which is connected to an antenna. This kind of source sends complex samples to the following blocks.

The interesting part about this kind of flow graph is that the data types change during the flow graph: at first, complex baseband samples are passed along. Then, complex symbols are gathered from the signal. Next, these symbols are turned into bits which again are processed further. Finally, the decoded bits are passed to some application which makes use of the data.

Walkie Talkie

+--+ +--+  +-+  ++  | USRP Source  +--+ NBFM Demodulator +--+ Squelch +--+ Audio Sink | +--+ +--+  +-+  ++  +--+          ++         ++  | Audio Source +--+ NBFM Modulator +-+  USRP Sink | +--+         ++         ++ This applications consists of two separate flow graphs, both running in parallel. One of them deals with the Tx path, the other with the Rx path. This kind of application would require some extra code (outside the flow graphs) to mute one path while the other is active. Both flow graphs still require at least one source and sink, each. You can find a GNU Radio application that does this (only a bit more sophisticated) at gr-uhd/examples/python/usrp_nbfm_ptt.py.

Summary
This concludes the chapter about flow graphs. Here's a quick summary about the most vital points you really need to know:


 * All signal processing in GNU Radio is done through flow graphs.
 * A flow graph consists of blocks. A block does one signal processing operation, such as filtering, adding signals, transforming, decoding, hardware access or many others.
 * Data passes between blocks in various formats, complex or real integers, floats or basically any kind of data type you can define.
 * Every flow graph needs at least one sink and source.

= A first working code example =

Next step is to find out how to write those flow graphs in real Python. Let's start by examining some code line-by-line. If you are familiar with Python, you can probably skip some of the explanations, but don't rush to the next section yet - the explanations are both for Python and GNU Radio beginners.

The following code example represents the flow graph from example 2. It is actually a slightly modified version of the code example you can find in gr-audio/examples/python/dial_tone.py.


 * 1) !/usr/bin/env python3

from gnuradio import gr from gnuradio import audio, analog

class my_top_block(gr.top_block): def __init__(self): gr.top_block.__init__(self)

sample_rate = 32000 ampl = 0.1

src0 = analog.sig_source_f(sample_rate, analog.GR_SIN_WAVE, 350, ampl) src1 = analog.sig_source_f(sample_rate, analog.GR_SIN_WAVE, 440, ampl) dst = audio.sink(sample_rate, &quot;&quot;) self.connect(src0, (dst, 0)) self.connect(src1, (dst, 1))

if __name__ == '__main__': try: my_top_block.run except KeyboardInterrupt: pass The first line should look familiar to anyone with some Unix or Linux background: It tells the shell that this file is a Python file and to use the Python interpreter to run this file. You need this line if you want to run this file directly from the command line.

Lines 3 and 4 import necessary Python modules to run GNU Radio. The  command is similar to the   directive in C/C++. Here, three modules from the gnuradio package are imported:,  , and. The first module,, is the basic GNU Radio module. You will always have to import this to run a GNU Radio application. The second loads audio device blocks, and the third is where the blocks related to analog signal functionality and modulation are located. There are many GNU Radio modules, a short list of modules will be presented later on.

Lines 6-17 define a class called  which is derived from another class,. This class is basically a container for the flow graph. By deriving from, you get all the hooks and functions you need to add blocks and connect them.

Only one member function is defined for this class: the function, which is the constructor of this class. In the first line of this function (line 8), the parent constructor is called (in Python, this needs to be done explicitly. Most things in Python need to be done explicitly; in fact, this is one main Python principle).

Next, two variables are defined:  and. These will control sampling rate and amplitude of the signal generators.

Before explaining the next lines, have another look at the sketched flow graph chart in the previous section: it consists of three blocks and two edges. The blocks are defined in lines 13-15: Two signal sources are generated (called  and  ). These sources continuously create sine waves at given frequencies (350 and 440Hz) and a given sampling rate (here 32kHz). The amplitude is controlled by the ampl variable and set to 0.1. The prefix &quot;f&quot; of the block type  indicates the output is of type , which is a good thing because the audio sink accepts floating point samples in the range between 1 and +1. These kind of things must be taken care of by the programmer: although GNU Radio does some checks to make sure the connections make sense, there is still some things that must be taken care of manually. For example, if you wanted to feed integer samples to, GNU Radio would throw an error but if you would set the amplitude in the above example to anything larger than 1, you would get a distorted signal without receiving an error.

The signal sink is defined in line 15:  returns a block which acts as a soundcard control and plays back any samples piped into it. As in the blocks beforehand, the sampling rate needs to be set explicitly, even though this was set already for the signal sources. GNU Radio cannot guess the correct sampling rate from the context, as it is not part of the information flow between blocks.

Lines 16 and 17 connect the blocks. The general syntax for connecting blocks is  which would connect the output of   with the input of , the output of   with the input of   and so on. You can connect as many blocks as you wish with one  call. Here, a special syntax is necessary because we want to connect  with the first input of   and   with the second one. does exactly this: it specifically connects  to port 0 of. is called a &quot;tuple&quot; in Python jargon. In the  call it is used to specify the port number. When the port number is zero, the block may be used alone. An equivalent command to the one in line 16 would thus have been

self.connect((src0, 0), (dst, 0)) That's all there is to create a flow graph. The last 5 lines do nothing but start the flow graph (line 22). The  and   statements simply make sure the flow graph (which would otherwise run infinitely) are stopped when Ctrl+C is pressed (which triggers a   Python exception).

For Python-beginners, two more remarks should not be left out: As you might have noticed, the class my_top_block is run without creating an instance beforehand. In Python, this is a quite common thing to do, especially if you have a class which would only get one instance anyway. However, you could just as well create one or more instances of the class and then call the  method on the instance(es).

Second, the indenting is part of the code and not, like in C++, simply for the programmers convenience. If you try and modify this code, make sure you don't start mixing tabs and spaces. Every level must be consistently indented.

If you want to go on with this tutorial, you should first get a more solid Python background. Python documentation can be found at the Python web site http://www.python.org/, or a library of your choice. A good place to start for people with prior programming experience is http://wiki.python.org/moin/BeginnersGuide/Programmers.

Summary

 * You need to import required GNU Radio modules with the  command. You always need the module.
 * A flow graph is contained in a class which itself is derived from.
 * Blocks are created by calling functions such as  and saving the return value to a variable.
 * Blocks are connected by calling  from within the flow graph class
 * If you don't feel comfortable writing some basic Python code now, have a break and go through some Python tutorials.

The next section will give a more detailed overview about writing GNU Radio applications in Python.

= Coding Python GNU Radio Applications =

The example above already covers quite a lot of how to write Python GNU Radio applications. This chapter and the next will try to show the possibilities of GNU Radio applications and how to use them. From now on, there is no need to linearly read these chapters section-for-section, it probably makes more sense to go over the titles and find out what you want to know.

GNU Radio Modules
GNU Radio comes with quite a lot of libraries and modules. You will usually include modules with the following syntax:

from gnuradio import MODULENAME Some modules work a bit differently, see the following list on the most common modules.

This is by far not a complete list, nor are the descriptions of the modules very useful by themselves. GNU Radio code changes a lot, so creating a static documentation would not be very sensible. GNU Radio uses Doxygen and Sphinx to dynamically create documentation of the APIs.

Instead, you will have to use the good old Star Wars motto to delve further into the details of the modules: &quot;Use the source!&quot;. If you feel GNU Radio should really already have some functionality you want to use, either browse through the module directory Python uses or go through the source directory of GNU Radio. In particular, pay attention to the directories starting with  in the source directory, such as gr-trellis. These produce their own code and, consequently, their own modules.

Of course, Python itself comes with a lot of modules, some of which are extremely useful - if not necessary - to write GNU Radio applications. Check the Python documentation and the SciPy website for more information.

Choosing, defining and configuring blocks
GNU Radio comes with an abundance of pre-defined blocks, so for beginners, it is often quite confusing to find the correct blocks for their applications and set them up correctly. Doxygen and Sphinx are used to automatically generate documentation for the C++ and Python APIs (C++ Manual, Python Manual.

You can also generate this documentation locally so it always matches the version you have installed. If you have Doxygen and Sphinx installed, CMake takes care of this automatically.

Learning how to use these documentations is a major part of learning how to use GNU Radio!

Let's get practical. Here's the three lines from the previous example which define the blocks:

src0 = analog.sig_source_f (sample_rate, analog.GR_SIN_WAVE, 350, ampl) src1 = analog.sig_source_f (sample_rate, analog.GR_SIN_WAVE, 440, ampl) dst = audio.sink (sample_rate, &quot;&quot;) Here's a simplified version of what happens when this code is executed: First, a function called  in the module   is executed. It receives four function arguments:


 * sample_rate, which is a Python variable,
 * analog.GR_SIN_WAVE, which is a constant defined in the  module (defined here,
 * 350, a normal literal constant that's the frequency of the sine wave (relative to the sample rate),
 * ampl, another variable to set the amplitude of the sine wave.

This function creates a class which is subsequently assigned to. The same happens on the other two lines, although the sink is fetched from a different module.

So how did I know which block to use and what to pass to ? This is where the documentation comes in. If you use the Sphinx-generated docs, click on &quot;gnuradio.analog&quot;. Proceed to &quot;Signal Sources&quot;. You will find a list of signal generators, including the  family. The suffix defines the data type at the output:


 * f = float
 * c = complex float
 * i = int
 * s = short int
 * b = bits (actually an integer type)

These suffixes are used for all types of blocks, e.g.  will define an FIR filter with complex input, complex output and float taps, and   will define a block which adds incoming short values with another, constant, short int.

Even if you don't want to touch C+, it's worth having a look at the Doxygen-generated documentation as well, since most of the blocks are actually written in C+ but then exported to Python.

At this point, it is worth having a closer look behind the curtains of GNU Radio. The reason you can easily use the blocks - written in C++ - in your Python code is because GNU Radio uses a tool called SWIG to create an interface between Python and C+''. Every block in C''+ comes with a creating function, called  (  in the example mentioned above). This function is always documented on the same page as the matching class, and this function is what gets exported to Python, so  in Python calls   in C++. For the same reason, it takes the same arguments - that's how you know how to initialize a block in Python.

Once you're browsing the Doxygen documentation of the class, you might notice many other class methods, such as. These functions get exported to Python as well. So if you have created a signal source and want to change the frequency (say your application has a user frequency control) you can use this method on your Python defined block:

# We're in some cool application here

src0 = analog.sig_source_f (sample_rate, analog.GR_SIN_WAVE, 350, ampl) # Other, fantastic things happen here

src0.set_frequency(880) # Change frequency will change the frequency of the first signal generator to 880Hz.

Hopefully, GNU Radio documentation will grow and become more and more complete. But to completely understand the workings of blocks in detail, you will probably have to have a look at the code sooner or later, no matter how good the documentation gets.

Connecting blocks
Use the  method of   to connect blocks. Some things are worth mentioning:


 * You can only connect inputs and outputs if the data types match. If you try to connect a float output with a complex input, you will get an error.
 * One output can be connected to several inputs; you don't need an extra block to duplicate signal paths.

These are basic rules for connecting blocks and they work in most cases. However, when mixing data types some more notes are worth mentioning.


 * GNU Radio checks if input and output types match by checking their size. If you happen to connect up ports with different types but the same size, you will most definitely get data junk.
 * When processing single bits, be careful. In some cases, you will work with binary data in a usual sense, in other cases you want to handle a specific number of bits at a time. Have a look at the  and   blocks for this.
 * Be careful with dynamic ranges. When you're using float or complex data types, you have a larger range than you'll ever need concerning the machine, but some sinks and sources have specific ranges you need to stick to. For example, audio sinks require samples within -1 and will clip anything outside this interval. The USRP sink on the other hand needs samples in the-32767 range (signed 16 bit values) because that's the dynamic range of the DAC.

Hierarchical blocks
Sometimes it makes sense to combine several blocks into a new block. Say you have several applications which all have a common signal processing component which consists of several blocks. These blocks can be combined into a new block, which in turn can be used in your applications is if it were a normal GNU Radio block.

Example: Say you have two different flow graphs, FG1 and FG2. Both use - among others - the blocks B1 and B2. You want to combine them to a hierarchical block called :

+---+ |  +-+  ++  | --+--+ B1  +--+ B2 +--+--- | +-+  ++  |  |     HierBlock     | +---+ This is what you do: create a flow graph which derives from  and use   as source and sink:

class HierBlock(gr.hier_block2): def __init__(self, audio_rate, if_rate): gr.hier_block2.__init__(self, &quot;HierBlock&quot;,                      gr.io_signature(1, 1, gr.sizeof_float),                       gr.io_signature(1, 2, gr.sizeof_gr_complex))

B1 = blocks.block1(...) # Put in proper code here! B2 = blocks.block2(...)

self.connect(self, B1, B2, self) As you can see, creating a hierarchical block is very similar to creating a flow graph with. Apart from using  as source and sink, there is another difference: the constructor for the parent class (called in line 3) needs to receive additional information. The call to  takes four parameters:


 * self (which is always passed to the constructor as first argument),
 * a string with an identifier for the hierarchical block (change at your convenience),
 * an input signature and an
 * output signature.

The last two require some extra explanation unless you have already written your own blocks in C++. GNU Radio needs to know what types of input and output the block uses. Creating an input/output signature can be done by calling, as is done here. This function call takes 3 arguments:


 * minimum number of ports,
 * maximum number of ports and
 * size of the input/output elements.

For the hierarchical block, you can see that it has exactly one input and one or two outputs. The incoming objects are of size, so the block processes incoming real float values. Somewhere in B1 or B2, the data is converted to complex float values, so the output signature declares outgoing objects to be of size. The 'gr.sizeof_float@ and  are equivalent to the C++ return values of the   call. Other predefined constants are


 * gr.sizeof_int
 * gr.sizeof_short
 * gr.sizeof_char

Use gr.io_signature(0, 0, 0) to create a null IO signature, i.e. for defining hierarchical blocks as sources or sinks.

That's all. You can now use  as you would use a regular block. For example, you could put this code in the same file:

class FG1(gr.top_block): def __init__(self): gr.top_block.__init__(self)

... # Sources and other blocks are defined here other_block1 = blocks.other_block hierblock   = HierBlock other_block2 = blocks.other_block

self.connect(other_block1, hierblock, other_block2)

... # Define rest of FG1 Of course, to make use of Pythons modularity, you could also put the code for  in an extra file called. To use this block from another file, simply add an import directive to your code:

from hier_block import HierBlock and you can use  as mentioned above.

Examples for hierarchical blocks:

gr-uhd/examples/python/fm_tx_2_daughterboards.py gr-uhd/examples/python/fm_tx4.py gr-digital/examples/narrowband/tx_voice.py

Multiple flow graphs
In some cases, you might want to have completely separate flow graphs, e.g. for receive and transmit paths (see the example 'Walkie-Talkie' above). Currently (June 2008), it is not possible to have multiple top_blocks running at the same time, but what you can do is create full flow graphs as hierarchical blocks using  like in the section above. Then, create a top_block to hold the flow graphs.

Example:

class transmit_path(gr.hier_block2): def __init__(self): gr.hier_block2.__init__(self, &quot;transmit_path&quot;,                               gr.io_signature(0, 0, 0), # Null signature                                gr.io_signature(0, 0, 0))

source_block = blocks.source signal_proc = blocks.other_block sink_block  = blocks.sink

self.connect(source_block, signal_proc, sink_block)

class receive_path(gr.hier_block2): def __init__(self): gr.hier_block2.__init__(self, &quot;receive_path&quot;,                               gr.io_signature(0, 0, 0), # Null signature                                gr.io_signature(0, 0, 0))

source_block = blocks.source signal_proc = blocks.other_block sink_block  = blocks.sink

self.connect(source_block, signal_proc, sink_block)

class my_top_block(gr.top_block): def __init__(self): gr.top_block.__init__(self)

tx_path = transmit_path

rx_path = receive_path

self.connect(tx_path) self.connect(rx_path) Now, when you start, both flow graphs are started in parallel. Note that the hierarchical blocks have explicitly no inputs and outputs defined, they have a null IO signature. Consequently, they don't connect to  as source or sink; they rather define their own sources or sink (just as you would do when defining a hierarchical block as source or sink). The top block simply connects the hierarchical blocks to itself, but does not connect them up in any way.

Examples for multiple flow graphs:

gr-uhd/examples/python/usrp_nbfm_ptt.py

GNU Radio extensions and tools
GNU Radio is more than blocks and flow graphs - it comes with a lot of tools and code to help you write DSP applications.

A collection of useful GNU Radio applications designed to aid you is in gr-utils/.

Browse the source code in gnuradio-runtime/python/gnuradio (and other gr- /python directories) to find utilities you can use in your Python code such as filter design code, modulation utilities, and more.

Controlling flow graphs
If you have followed the tutorial so far, you will have noticed that a flow graph has always been implemented as a class, derived from. The question remains on how to control one of these classes.

As mentioned before, deriving the class from gr.top_block brings along all the functionality you might need. To run or stop an existing flow graph, use the following methods:

See the documentation for for more details.

Example:

class my_top_block(gr.top_block): def __init__(self): gr.top_block.__init__(self) ... # Define blocks etc. here

if __name__ == '__main__': my_top_block.start sleep(5) # Wait 5 secs (assuming sleep was imported!) my_top_block.stop my_top_block.wait # If the graph is needed to run again, wait must be called after stop ... # Reconfigure the graph or modify it   my_top_block.start # start it again sleep(5) # Wait 5 secs (assuming sleep was imported!) my_top_block.stop # since (assuming) the graph will not run again, no need for wait to be called These methods help you to control the flow graph from the outside. For many problems this might not be enough: you don't simply want to start or stop a flow graph, you want to reconfigure the way it behaves. For example, imagine your application has a volume control somewhere in your flow graph. This volume control is implemented by inserting a multiplier into the sample stream. This multiplier is of type. If you check the documentation for this kind of of block, you will find a function  which sets the multiplication factor.

You need to make the settings visible to the outside in order to control it. The simplest way is to make the block an attribute of the flow graph class.

Example:

class my_top_block(gr.top_block): def __init__(self): gr.top_block.__init__(self) ... # Define some blocks self.amp = blocks.multiply_const_ff(1) # Define multiplier block ... # Define more blocks

self.connect(..., self.amp, ...) # Connect all blocks

def set_volume(self, volume): self.amp.set_k(volume)

if __name__ == '__main__': my_top_block.start sleep(2) # Wait 2 secs (assuming sleep was imported!) my_top_block.set_volume(2) # Pump up the volume (by factor 2) sleep(2) # Wait 2 secs (assuming sleep was imported!) my_top_block.stop This example runs the flow graph for 2 seconds and then doubles the volume by accessing the  block through a member function called. Of course, one could have accessed the  attribute directly, omitting the member function.

Hint: making blocks attributes of the flow graph is generally a good idea as it makes extending the flow graph with extra member functions easier.

Non-flow graph centered applications
Up until now, GNU Radio applications in this tutorial have always been centered around the one class derived from. However, this is not necessarily how GNU Radio needs to be used. GNU Radio was designed to develop DSP applications from Python, so there's no reason to not use the full power of Python when using GNU Radio.

Python is an extremely powerful language, and new libraries and functionalities are constantly being added. In a way, GNU Radio extends Python with a powerful, real-time-capable DSP library. By combining this with other libraries you have immense functionality right there at your fingertips. For example, by combining GNU Radio with SciPy, a collection of scientific Python libraries, you can record RF signals in real time and do extensive mathematical operations off line, save statistics to a database and so on - all in the same application. Even expensive engineering software such as Matlab might become unnecessary if you combine all these libraries.

http://www.scipy.org/

= Advanced Topics =

If you have really read the previous sections, you already know enough to write your first Python GNU Radio applications. This section will address some slightly more advanced functionalities for Python GNU Radio applications.

Dynamic flow graph creation
For most cases, the aforementioned way to define flow graphs is completely adequate. If you need more flexibility in your application, you might want to have even more control over the flow graph from outside the class.

This can be achieved by taking the code out of the  function and simply using   as a container. Example:

... # We are inside some application tb = gr.top_block # Define the container

block1 = blocks.some_other_block block2 = blocks.yet_another_block

tb.connect(block1, block2)

... # The application does some wonderful things here

tb.start # Start the flow graph

... # Do some more incredible and fascinating stuff here If you are writing some application which needs to dynamically stop a flow graph (reconfigure it, re-start it and so) on this might be a more practical way to do it.

Examples for this kind of flow graph setup:

gr-uhd/apps/hf_explorer/hfx.py

Command Line Options
Python has its own libraries to parse command line options. See the documentation for the module  to find out how to use it.

GNU Radio extends optparse by new command line option types. Use @from gnuradio.eng_option import eng_option' to import this extension. With eng_option, you have the following types:

If your application supports command line options, it would be ever so nice if you could stick to the GNU Radio conventions for command line options. You can find these (along with more hints for developers) in README.hacking.

Nearly every GNU Radio example uses this feature. Try dial_tone.py for an easy example.

Graphical User Interfaces
If you are a Python expert and also have some experience in writing GUIs for Python (using whatever GUI toolkit you like), you might not even need this section. As mentioned before, GNU Radio merely extends Python with DSP routines - so if you like, just go ahead and write a GUI application, add a GNU Radio flow graph to it and define some interfaces to carry GNU Radio information to your application and vice versa. If you want to plot your data, you could use Matplotlib or Qwt.

However, sometimes you simply want to write a quick GUI application without bothering with setting up widgets, defining all the menus etc. GNU Radio comes with some predefined classes to help you write graphical GNU Radio applications.

These modules are based on wxWidgets (or to be precise, wxPython), a platform-independent GUI toolkit. You will need some background in wxPython - but don't worry, it is not that complicated and there are several tutorials available on the net. Check the wxPython website for documentation (http://www.wxpython.org/).

To use the GNU Radio wxWidgets tools, you need to import some modules:

from gnuradio.wxgui import stdgui2, fftsink2, slider, form Here, 4 components were imported from the gnuradio.wxgui submodule. Here's a quick list of the modules (again, not necessarily complete. You will have to browse the modules or the source code in gr-wxgui/python).

Next, we have to define a new flow graph. This time, we don't derive from  but from  :

class my_gui_flow_graph(stdgui2.std_top_block): def __init__(self, frame, panel, vbox, argv): stdgui2.std_top_block.__init__ (self, frame, panel, vbox, argv) As you can see, there's another difference: the constructor gets a couple of new parameters. This is because a  does not only include flow graph functionality (it is derived from gr.top_block itself), but also directly creates a window with some basic components (like a menu). This is good news for all of those who just want to quickly hack a graphical application: GNU Radio creates the window and everything, you just need to add the widgets. Here's a list of what you can do with these new objects (this probably won't mean much to you if you have no idea about GUI programming):

Now you have all you need to create your GUI. You can simply add new box sizers and widgets to vbox, change the menu or whatever. Some typical functions have been simplified further in the GNU Radio GUI library.

form has a great number of input widgets:  for static text field (display only), , to input float values,   to input text,   for checkboxes,   for radioboxes etc. Check the source code of gr-wxgui/python/form.py for the complete list. Most of these calls pass most of their arguments to the appropriate wxPython objects, so the function arguments are quite self-explanatory.

See one of the examples mentioned below on how to add widgets using form.

Probably the most useful part of  is the possibility to directly plot incoming data. To do this, you need one of the sinks that come with, such as. These sinks work just as any other GNU Radio sink, but also have properties needed for use with wxPython. Example:

from gnuradio.wxgui import stdgui2, fftsink2


 * 1) App gets defined here ...

my_fft = fftsink2.fft_sink_f(panel, title=&quot;FFT of some Signal&quot;, fft_size=512,            sample_rate=sample_rate, ref_level=0, y_per_div=20) self.connect(source_block, my_fft) vbox.Add(my_fft.win, 1, wx.EXPAND) First, the block is defined. Apart from typical DSP parameters such as the sampling rate, it also needs the  object which is passed to the constructor. Next, the block is connected to a source. Finally, the FFT window is placed inside the   BoxSizer to actually display it. Remember that a signal block output can be connected to any amount of inputs.
 * 1) FFT display (pseudo-spectrum analyzer)

Finally, the whole thing needs to be started. Because we need an  to run the GUI, the start-up code is a bit different from a regular flow graph:

if __name__ == '__main__': app = stdgui2.stdapp(my_gui_flow_graph, &quot;GUI GNU Radio Application&quot;) app.MainLoop creates the  with   (the first argument). The window title is set to &quot;GUI GNU Radio Application&quot;.

Examples for simple GNU Radio GUIs:

gr-uhd/apps/uhd_fft gr-audio/examples/python/audio_fft.py ./gr-uhd/examples/python/usrp_am_mw_rcv.py And many more.

= What next? =

Young Padawan, no more there is I can teach you. If you have any more questions on how to write GNU Radio applications in Python, there are still a number of resources you can use:


 * Use the source. Especially the examples in gr- /examples/ and gr-utils/ can be very helpful.
 * Check the mailing list archives. Chances are very high your problem has been asked before.