This weekend, I spent time learning about GNURadio and it’s applications, with a goal of building a generic packet-based (non-stream) messaging interface between SDR devices. While I was not entirely successful, I was able to fake this behaviour by manipulating a stream transmission. In this process, I also gained some understanding of the intricacies of GNURadio.
The hardware used in this process was a USRP B200 attached to a Linux system, and a BladeRF attached to a Windows system, sending traffic over a quiet part of the 2.4Ghz spectrum: however, this should work anywhere (just adjust frequency and sample rate, and fix the osmocom source and sink).
I will document my progress here for future reference.
Simple File Transfer
I began by simply transferring a file between one system and another. We start by creating a file source block, with output format selected to byte. We then pass this through a Packet Encoder block, and then prepare for transmission via a GMSK modulator. We multiply the output complex number by 1.0 (todo: why?), and pass this via an osmocom sink. The flow graph looks something like this:
We can confirm this “works”, via both the FFT sink (showing power over frequency over time), as well as an independent GQRX.
The illustration above shows a vector source pointing to a variable, but this is functionally equivalent (and works as well).
To receive the file, we simply do the reverse: from an osmocom source, we multiply by complex 1.0, decode the packet, send it through a low pass filter (to remove noise from nearby frequencies), demodulate the data (GMSK demod), decode the packets and write to file:
A quick test confirms we can send and receive an arbitrary stream of data via GNURadio.
Message PDUs / Message Strobe
From there, my research (and by research I mean Google) showed that I could presumably use the “Message Passing” interface to translate messages. Beginning from Dr. Aaron Scher’s example here, I attempted to glue this on to my existing flow graph, replacing the source with a Message Strobe, presumably converted to bytes:
- Using a Message Strobe function to send a message every 500 ms
- Create a tagged stream with an output format of byte
- Build a header, and repack at a 1:8 compression rate (i.e. each bit is now represented as one byte in the output)
- Treat this as a stream, pass it by GMSK modulation and send, as if it were a file.
Unfortunately, while an FFT sink showed that *some* signal was coming out of the GMSK modulator, a stream receive on the other end (to file) did not show the expected data. For reference, my flow graph was similar to the below:
On further research, it appears that this is not the intent of the message passing interface: instead, it is intended for use as “commands” to send around your flow graph: for example, if you receive a signal above a certain strength, retune your SDR Device to another frequency (via a “Message”). This will no doubt be useful later – I’m thinking jamming-resistant SDR.
It should be noted here that a quirk of the UI is apparent: the “repack bits” block actually requires a length tag to function correctly, but even if you set it, it’s not shown in the graphical view of the flow graph.
Modifying the Vector Source
After a few hours of learning about the Message interface, I attempted to solve this problem by utilising the file transfer code above, and simply modifying the Vector Source to transmit different data. This could be done by directly modifying the Python generated by gnuradio, to introduce a new thread created as the top block is initialized, which modifies the data used by the vector source:
This vector represents a simple custom packet format:
- The first four bytes are a static header, consisting of the bytes 1, 2, 3 and 4
- The next byte is the length of the packet data
- The next byte is a packet ID. The receiver should reject packets with ID’s it’s seen before.
- The next four bytes are the packet data. This is variable length.
- The next and last four bytes are a footer, consisting of the bytes 5, 6, 7 and 8.
To receive this, we simply modify the file receive script, with a custom Python sink:
The flow graph is extremely simple, with the only change to accomodate the new packet sink:
This works, but causes overflows on the receiver. I suspect this is due to the Python message sink being too slow: that is, it takes too long to process messages, so the hardware’s receive buffer fills, and it “drops packets”.
I believe I can optimize this via a Vector sink, and using gnuradio’s “Probe” mechanism to constantly poll the value of the resultant Vector, leaving gnuradio to do the heavy lifting of data transfer.
I will continue to improve this work and document my progress.