GNU Radio
GNU Radio is an open-source framework for creating signal processing applications that run on general-purpose processors. It is widely used in research, industry, and academia, and it can be combined with SDR hardware to create real-world communication systems.
GNU Radio will be the main framework supported in CDC for implementing your design project solutions.
Contents
- Documentation
- Flowgraphs
- The GNU Radio Companion (GRC)
- Python Flowgraphs and Blocks
- Out-of-tree Modules and C++ Blocks
- Advanced GNU Radio Concepts
Documentation
There is a large amount of documentation available for GNU Radio.
In particular, the GNU Radio wiki has a suite of useful beginner and intermedia tutorials, many of which are referenced below. These should be your starting point to better understand concepts important in developing GNU Radio applications.
The wiki also has documentation for many GNU Radio signal processing blocks which explain their configuration and operation. Unfortunately, not all of the documentation is complete, so at times you may need to investigate a block or object's source code should you wish to use it. See the Python and C++ sections below for more information on understanding GNU Radio blocks.
Flowgraphs
Applications are created in GNU Radio by constructing flowgraphs, which are collections of interconnected signal processing blocks. The specific blocks used, their configuration, and the manner in which they are connected dictates the functionality of your application. Flowgraphs can be created graphically, in Python, or in C++.
Blocks in a flowgraph perform defined signal processing operations, with source blocks generating data, other blocks processing data, and sink blocks consuming data. In the above example, two Signal Source blocks generate cosine waveforms with frequencies of 350 and 440 Hz, the Add block combines these two waveforms, and then the result is played on the computer's sound card.
Once execution of a flowgraph commences, the GNU Radio scheduler handles scheduling block processing and the piping of generated data at the output port of an upstream block to the input port of the block downstream. This results in data streams flowing through the graph, where streams of data can be of different types (bytes, integers, floats, complex, etc.). For real-time operation, all blocks in the flowgraph must be able to keep up with the rate of arriving data on their input ports. GNU Radio functionality can be extended by writing new blocks in either Python or C++.
See What is GNU Radio for an additional overview.
The GNU Radio Companion (GRC)
The GNU Radio Companion, or GRC, is a graphical tool for creating and running GNU Radio flowgraphs and can be used very much like Simulink or LabVIEW. It is the easiet way to create new applications when the libary of existing signal processing blocks is sufficient for your needs.
See the following GRC tutorials:
- Your First Flowgraph
- Python Variables in GRC
- Runtime Updating Variables
- Signal Data Types
- Converting Data Types
- Packing Bits
- Streams and Vectors
It is often convenient to make commonly used block combinations into a hierarchical block that can be imported into a flowgraph as a single super block. To do so in GRC, see:
Python Flowgraphs and Blocks
The Python programming language is one of two languages used within GNU Radio. As an interpreted language with intuitive syntax, it is well-suited for specifying flowgraphs to create applications. In fact, under the hood GRC is just generating a Python script that instantiates your designed flowgraph. Flowgraphs can be defined directly in Python instead.
See the following GNU Radio tutorial on understanding a flowgraph's Python code:
Python is also quick and convenient for implementing some signal processing blocks in GNU Radio, but these should generally be blocks that are not computationally intensive. As an interpreted language, Python will generally be slower than compiled C++ code, the other language used in GNU Radio.
See the following GNU Radio tutorials on creating Python blocks:
Out-of-tree Modules and C++ Blocks
As an interpreted language, Python is very flexibile and easy to program in but may be too slow for creating blocks that must perform a large amount of computations. Instead, the bulk of blocks in GNU Radio are implemented in C++ for execution speed. You can create your own C++ blocks but this is more challenging than writing blocks in Python. GNU Radio functionality is extended through the creation of Out-of-Tree (OOT) modules that also conveniently setup the details you need to successfully compile new C++ blocks.
See the following GNU Radio tutorials on out-of-tree modules and writing C++ blocks:
There is an extensive library of third-party OOTs available on the Comprehensive GNU Radio Archive Network (CGRAN).
Advanced GNU Radio Concepts
Polymorphic Types (PMTs)
Polymorphic Types (PMTs) are a GNU Radio construct used to represent concrete data types (bytes, integers, vectors, etc.) in an abstract way. They are a fundamental enabler of passing data between blocks through the use of stream tags and message passing.
See the following GNU Radio tutorial on PMTs:
Message Passing
By default, data passes between blocks as a continuous stream of samples. Many functions in communication systems, however, are better modeled by the asynchronous passing of data blocks of a given size. These data blocks are called messages in GNU Radio, and message passing is an alternative way of passing data or control information between blocks. Message passing also simplifies the interaction of external entities with a flowgraph.
See the following GNU Radio tutorials on message passing:
Stream Tags
Stream tags allow control information to be attached or tagged to a specific data sample in a given stream. This is useful for passing control or configuration information to downstream blocks with an associated time validity.
See the following GNU Radio tutorials on stream tags:
Profiling
Because communication systems interact with the physical world, they must be able to process their data in real-time, that is to say, they must be able to keep up with the associated sample rate at which they are operating. For a GNU Radio flowgraph, this means all blocks in the flowgraph must be able to keep up with the rate data are arriving on their input ports. This is obviously limited by the overall processing power of the machine on which the application is running and the complexity of the flowgraph. It can be very helpful to know how much processing time each block in a flowgraph is taking in order to look for bottlenecks, which can be done using performance counters.
See the following GNU Radio usage page on performance counters:
To enable performance counters in your flowgraph, open the GNU Radio configuration file in an editor (in this case Microsoft Visual Studio Code).
$ code ~/.gnuradio/config.conf
Look for the [perfcounters]
section and ensure that on
is set to True
. Add this section to the configuration file if it is missing.
[perfcounters]
clock = thread
export = False
on = True
Performance counters will add overhead to your application, so you only want to enable them when you are profiling.
The list of available performance counter functions can be found in gr::block
class reference.
We list some of the most useful counters in the following table.
Class Method | Description |
---|---|
pc_nproduced_avg() |
Gets average num items produced. |
pc_work_time_avg() |
Gets average clock cycles spent in work. |
pc_work_time_total() |
Gets total clock cycles spent in work. |
pc_throughput_avg() |
Gets average throughput. |
reset_perf_counters() |
Resets the performance counters. |
As an example, if you wished to know how efficiently your block was implemented, you could look at the ratio of the average number of items produced to average time spent producing those items.
# Example of accessing performance counters in a Python block
work_time_avg = self.pc_work_time_avg()
nproduced_avg = self.pc_nproduced_avg()
nitems_per_cycle = nproduced_avg / work_time_avg
Here a larger nitems_per_cycle
would be a more efficient block.
VOLK
Hardware-based solutions have historically been the preferred way to implement the physical layer of communication systems as they allow a high degree of parallelism to be achieved and can thus keep up with the processing needs of such applications. The microprocessors that run software-based solutions operate sequentially, but modern processors have vectorised instructions that allow the same operation to be applied to multiple data points simultaneously. This can significantly increase the rate data can be processed. GNU Radio uses a convenient library called the Vectorized Optimized Library of Kernels (VOLK) to make these vectorised instructions easily accessible.
See the following GNU Radio tutorial on using VOLK: