Some Background
The GNU Radio project is pretty awesome.
My research focusses on wireless communications. The concept of radio receiver incredibly simple: use an antenna to receive a signal through the air and process that signal to extract the useful information (voice, data, etc). A transmitter is the same idea, in reverse order. The challenging part is the signal processing turn radio waves into information you care about.
When doing work in signal processing for radio communications it is very valuable to be able to test out your ideas with real radios transmitting over the air. In the not-to-distant past this would mean fabricating a new circuit board for every new design. The concept behind software-defined radio is to replace the signal processing done by the fixed hardware with signal processing done digitally. With a software-defined radio we can change the radio just by changing code and test out many new radio designs without having to build new hardware each time.
GNU Radio provides an excellent framework for experimenting signal processing for communication. A receiver or transmitter design in GNU Radio is made up of many blocks each performing one specific function (modulation, encoding, scaling, etc). Each block, written in C++ or Python, takes in data through an input port, processes it, and outputs it to an output port. GNU Radio provides the framework to glue the inputs and outputs together to form a chain of blocks to accomplish a task. This collection of connected blocks is called a flow graph. It even comes with a Simulink-style GUI where connections are shown graphically.
What A Flow Graph in GNU Radio Companion Looks Like
With a flow graph implementing the signal processing necessary for, let’s say, an FM radio receiver, you can connect a software-defined radio to your PC and process the data from raw signal to recovered audio.
GNU Radio also gives users the ability to save data at any point in the flow graph to a binary file using file sinks and file sources. The resulting files can be read after the flow graph has run and are useful tools for debugging complicated flow graphs.
Working With File Sinks
The file sink block uses stdio to write raw samples to a file. By default, during this process a certain number of samples will be buffered before being written to file. The number of samples varies by operating system. If there is only a short amount of data going into that file sink you may find the file to be empty after the flow graph is stopped. For this case, we can use the “unbuffered” option. This bypasses the buffering operation and writes all samples directly to file.
Another thing to note is that GNU Radio companion uses absolute file paths. So be sure to change the paths if you move your flow graph to another directory or machine.
There are several ways that data can be represented in GNU Radio. These data types can be selected in the file sink block and must match the data type of the samples that are streaming to it. The data types used are:
Complex | 32 bit floating point for both I and 32 Q |
Float | 32 bit single precision floating point |
Integer | 32 bit signed integer |
Short | 16 bit signed integer |
Byte | 8 bit signed integer |
Reading Binary Files in MATLAB
Once data has been written to a file there are several ways we can examine it. A file source can be used to read back the file into another GNU Radio flow graph. We could also use Python to read and analyze the data. While it’s not free, MATLAB can be a particularly useful tool and one which most engineers are rather familiar with.
Raw data files can be read into MATLAB quite easily with a few lines of code:
f = fopen(filename, 'rb');
v = fread(f,count);
fclose(f);
The first line tells MATLAB to open the file given by filename as read-only with Big-Endian encoding. The file identifier is stored in the variable f
. The last line closes the file.
The MATALB function fread does the work of pulling data out of the file and into the array v
. We give it the file identifier and the number of items to read (or tell it to read the file until empty). By default, MATLAB will read the file assuming the data type is an 8-bit integer. To read different file types we will need to add an extra parameter. For example, to read floats from a file we will use
v = fread (f, count, 'float');
For the case of complex numbers the I and Q samples are interleaved. The data file will contain the I for sample 1, the Q for sample 1, the I for sample 2, etc. The read_complex_binary.m file distributed with GNU Radio shows how to take care of this
t = fread (f, [2, count], 'float');
v = t(1,:) + t(2,:)*i;
[r, c] = size (v);
v = reshape (v, c, r);
We will read the data two floats at a time. Then convert the two floats to one complex number. That will then be reshaped into one complex vector.
It is convenient to create a function that does the file read operation for each data type. Several of these files come with GNU Radio such as read_float_binary.m and read_complex_binary.m. I’ve written functions that do the same for all the other data types used in GNU Radio. They are available to download from GitHub.
Writing Binary Files From MATLAB
We can also write data from MATLAB to a file and play that file back in GNU Radio using a file source. We’ll use a similar set of commands.
f = fopen (filename, 'wb');
v = fwrite (f, data);
fclose (f);
We’ll open a file specified by filename, this time making it writable. We’ll write the vector data
into that file using MATLAB’s fwrite
command. The variable v
will show the number of elements in data
that were successfully written to file.
We will need to give fwrite
the same parameters to deal with different data types as in fread
. For example, to write a vector of floats to file we will use
v = fwrite (f, data, 'float');
As with the read functions, there are several MATLAB files distributed with GNU Radio to handle writing to files from MATLAB. I’ve added functions to handle other data types which are also available in the GitHub repository.