Building a Burst FSK Modem in GNU Radio with Message Lambda Blocks and Eventstream

Lots of cheap electronics tend to use burst FSK modems for wireless communications. GNU Radio has long been able to work with these sorts of communications, but typically it has done so by running an FSK demodulator continuously and then correlating the output for a known preamble sequence such as a with the correlate access code block, and then adding some kind of monolithic special purpose block. This article proposes a slightly different approach to building such a burst FSK waveform to inter-operate with many wireless devices of this style and to be a bit more modular, flexible, simple and intuitive to work with.

Building the Transmitter

In most communications systems, the transmitter is computationally less demanding than the receiver, additionally many FSK burst modems are low baud rate and not particularly demanding from a computational complexity standpoint. I use these two facts as motivation to justify an exceedingly lazy modem design which boils a burst FSK transmitter block down to three simple message lambda blocks as shown below.


Since they aren’t visible in the screenshot above, the block expression used for the three message lambda blocks are:

Map [0,1] bits to symbols [-1.0,+1.0]
 * lambda x: numpy.array(x, dtype=numpy.float32)*2-1
Interp to Samps per Sym
 * lambda x: numpy.tile(x,[sps,1]).T.reshape([1,len(x)*sps])
FSK Modulate
 * lambda x: numpy.array(numpy.exp(1j*2*numpy.pi*((dev*x*numpy.arange(len(x)))/samp_rate)),dtype="complex64")

We generate random packets of bits, prepend an arbitrary known preamble, map bits to symbols, interpolate to N samples per symbol, and finally modulate our symbols up by a complex sinusoid at plus or minus some FSK deviation frequency. At this point, the modulated bursts can simply be dropped into a transmit sample stream using an eventstream source block whose output is then throttled and run through a simple channel model.


Plotting our FSK burst transmit waveform’s output through the pulse shaping filter, we get a reasonable looking time and frequency profile of the transmitted output signal. This is kind of a fun example to show the power of lamda blocks, in this case we’ve been able to basically write an entire waveform from a couple python expressions all from within GRC with really minimal effort, for a nominal performance penalty, which in this case doesn’t really matter.   If performance was at some point deemed to be important here, blocks could easily be ported one by one to optimized C++ based equivalents.

Building the Receiver

We can take a similar approach to building a burst FSK receiver waveform with stream blocks, message blocks, and event detection. We simply run the received samples through a standard quadrature demodulator block matched roughly to the FSK deviation magnitude. Since this FLL is essentially a random walk when no signal is on the air, and nominally tracking carrier tightly when locked to a burst, we use the variance of the FLL’s output as a detection statistic for bursts, triggering an event when the variance goes below 15 in this case. (or the negative variance rises above -15 in this case).   This can be seen in the graph below.


In this case at every event we latch in a maximum burst length’s worth of FM demodulated float values containing one burst every time this threshold is reached using the trigger rising edge and eventstream sink blocks. These FM demodulated samples are run through a burst FSK time sync, which performs symbol timing recovery and outputs one soft float value per symbol. Please note this synchronization block is by no means an optimal implementation, and represents only a very quick stab at the problem – but seems to perform well enough in initial test. (I would love to clean this up with more optimal ML synchronization algorithms but haven’t had a chance yet).   The output bits can then be sliced to bits, packed into 8 bit chars, and passed in an output PDU downstream to feed a PDU Socket or TUNTAP interface. A screenshot of the receive waveform is shown below.


Our diagnostic output on the terminal shows that we are indeed recovering the inserted preamble bits correctly (0x036cf is consistently received in our output) Although sometimes with alignment slipping a bit or two in either direction and adjacent noise and data bits flipping randomly – A problem that could be easily taken care of with a MAC header search and framer.

creating event @ time 368183, length = 5000
sps = 8.000000
((tau . 5) (meta . 8) (es::event_length . 5000) (es::event_time . 368183) (es::event_type . edge_event))
pdu_length = 78
contents = 
0000: 40 36 cf 02 c5 d2 b7 48 0c bd 22 44 5c d1 84 c3 
0010: 0b 2e 49 2f 29 7b 96 dd 74 61 88 82 83 47 db 0b 
0020: 2a 40 20 55 81 7d b4 29 36 0e 1d 9e 65 86 4b af 
0030: 3e b9 b1 65 b0 f9 a1 4a be f2 54 1a b2 41 bf 02 
0040: 94 a9 c3 44 3d b2 bb b6 db 6d 2b e4 b4 67 
creating event @ time 421379, length = 5000
sps = 8.000000
((tau . 3) (meta . 8) (es::event_length . 5000) (es::event_time . 421379) (es::event_type . edge_event))
pdu_length = 78
contents = 
0000: 00 36 cf 0a 59 29 cb 7b 83 67 5f ac 29 c7 87 d7 
0010: 2e 63 d4 c4 3e ff 68 d2 01 56 cc ba 09 aa 33 16 
0020: 00 7a c7 54 57 12 e3 7b 5a 72 d0 4c 80 aa 44 f1 
0030: cf 77 16 28 65 31 a4 11 f3 12 07 ef e0 42 cc 47 
0040: 57 90 be aa 15 02 89 19 91 f6 c9 83 8a dc 

This waveform is available on github for your enjoyment.  Please send pull requests for any improvements you make!

GNU Radio Stream Lambda Blocks and Dynamic Runtime Updates

Since adding message based lambda blocks the other day, it seemed an obvious pairing to include a stream equivalent block as well for really rapid prototyping needs. The interface is a bit less simple because we can have N input ports and M output ports, I settled on defining a lambda function which takes a list of input vectors (the same input_items list of numpy arrays normal python blocks get, and an integer output port index). To implement a simplish stream block now that consumes N input streams and produces M output streams, this function prototype simply needs to be implemented. This won’t let you do everything, for instance output streams still need to produce the same number of items, and they are assumed to consume at this same rate. Extending this to a sync decimate or interpolater should be straight forward, but the function prototype would need to become a bit more complex for a “general work” kind of interface which allows producing/consuming each port at different rates, so I chose not to address that for now.

Example Stream Lambda Block  Flowgraph

A simple example graph which demonstrates the use of these blocks is shown below, this graph takes a simple Gaussian noise source in, throttles it to a fixed throughput rate, and then feeds it through a 2-in 2-out stream lambda block. In this case, we provide the following lambda function for the block,

lambda input_items, output_index: (input_items[output_index][::2]**2)*[0.1,0.2][output_index] + [-1,1][output_index]

That is to say:

output_items[0] = input_items[0][::2]**2*0.1 – 1

output_items[1] = input_items[1][::2]**2*0.2 + 1

Here, each stream is independent of the other output, and they could have been implemented as two seperate blocks, but this is just provided as an illustration.

The second block then is a 2-in 1-out stream lambda block which merges the two streams into one output stream, this mapping is given by:

lambda input_items, output_index: (input_items[0][:] * numpy.conj(input_items[1][:]))

This one is a bit simpler, as we will simply have a single evaluation:

output_items[0] = input_items[0]*conj(input_items[1])

The resulting output signal is then shown in the output plot.


Dynamic Runtime Updates

What’s really fun is these lambda functions can be updated at runtime to change the block’s functionality. I’ve added a variable_text_input block to gr-pyqt which allows for runtime input of new lambda functions to each block which are passed to the GRC setter callback for each stream lambda block. By running this graph, you can now fiddle with the block’s algorithms at run time and immediately see the output effect on the stream signal. Just don’t type any invalid python at run-time or you’ll crash the block’s thread context.

The dynamic update version of the stream lambda block demo is shown below.


These dynamic function updates should work with the message lambda block as well, but that is not shown here.

The GRC graph for this example can be found @

GNU Radio Message Lambda Blocks

It occurred randomly tonight that both python blocks and passing around lambda functions are awesome. The clear conclusion of this was that we should add python lambda blocks to GNU Radio. Since ~15 lines of python and all the installing, importing and xml wrapping is a bit lengthy for writing new python blocks, it occurred to me that in many of these blocks, the only thing that is changing is really the mapping from input vector to output vector of a PDU.

Therefore I introduce to you the new Python PDU Lambda message block. Now from GRC you can make up a completely new message block by simply writing a lambda function in a block parameter field which defines the mapping from input vector to output vector. Since pmt’s to_python and to_pmt methods handle typing for you, this works for any PMT vector type and you can generally use any python or numpy calls within your lambda argument to the block to quickly add completely new functionality from GRC with really minimal effort.


Testing with Such Samples

Dropping a lambda block into the such samples waveform, we make a new block which simply computes a log-power over time signal of a decimated input signal. Dropping this into a standard plotting block, we immediately have promising looking results. Any desired transform of the input data set that can be represented as a lambda function using numpy/etc can now be used to plot random segments of data from our input file now!


Also note that the gr-pyqt plots now support adding markers with “shift + left-click” as well as bring up a context menu (which allows you to clear markers) with “middle-click”.

This pdu_lambda block is now readily available in the gr-pyqt out-of-tree module.  The potential damage of new monolithic Balint-style GRC graphs using this block is frightening.

Such Samples: A GNU Radio Tool for Rapid Sample Data File Visualization with Message Passing

GNU Radio has always been a great stream processing system for building signal processing systems. Less noticed perhaps is that GNU Radio’s QTGui component matured, it came along with a handful of useful little tools such as gr_time_plot_c which allowed for inspection of sample data files in the time and frequency domain using the same plot widgets we typically use for streaming data. Since this makes use of a handful of GNU Radio stream blocks to perform file reading, sample dropping and alignment, and vector sources to stream data into the stream sink, and we have recently done quite a bit of work with message based plotting, I wondered if it was time to start trying to re-think these a bit. Aside from that, recently I was trying to look at some really large simulated data sets using this tool and getting frustrated with the slugish nature of the tool every time I wanted to move around in time.

So using message passing in GNU Radio I tried to re-think what a sample data analysis tool might look like, and how it might behave responsively to a use interface, accessing only the data of interest and not wasting time reading and trimming large streams using the traditional GNU Radio stream operations on the fixed data file.

Tool Composition

The tool, which I call “Such samples” is actually quite simple when built using GNU Radio Companion. It really only represents two new blocks aside from using the existing gr-pyqt based PDU message plotters, these blocks are:

1. A message based file source, which takes in a tuple message of (start item, item length) and reads out a PDU containing the requested item range after performing a low level fseek to align correctly (never reading in items outside that range).  Reading data is then conducted transparently through numpy’s from_file and pmt’s to_pmt methods which are quite fast and able to handle large chunks.

2. A message based range tool, which allows for a user to manipulate the location and sample range to view within the file. This really just sends out the (start item, item length) tuples to the message file source and doesn’t do much else.

Both of these are initially implemented as pretty concise simple python blocks, but connecting them up in GNU Radio Companion we see that it’s created a pretty simple but nicely performing tool for looking at data files.

The flowgraph is shown below, and could be easily tailored to include all kinds of message operations and plots that one might want to add or include in such a tool.


Tool Operation

The tools is pretty primitive right now, but operation is simple – a command line parameter, or GNU Radio Companion variable default specified which sample data file to open, and by default the sample range [0,4096] is displayed in a time and frequency plot. Moving a slider around allows you to move the start sample throughout the file, and changing the “sample length” field allows you to change the number of complex samples plotted at a time down below. When the file is opened, it tells the range widget the start and end sample within the file so that the bar correctly spans the range of the file.

A screenshot of the tool in operation can be seen below.


Still to do…

There is still lots of clean up remaining, the file source is hard wired for complx64 type samples right now, the gr-pyqt plots still have relatively meaningless labels, no nice context menus, and generally things look a little bit sloppy, but the basic architecture is in place and feels solid and responsive to me. If anyone wants to help with these other items please send pull requests on github!

I would love to switch to using the normal gr-qtgui plotters, once they support message based inputs, but for now these PyQWT based plotters aren’t too bad when passing numpy vectors around in python by reference.

The tool can be found within the gr-pyqt repo’s app’s directory on github @