Sending data from GNU Radio into Linrad using the network

During the last few days I’ve been experimenting with feeding signals from GNU Radio into Linrad using Linrad’s network protocol. Linrad has several network protocols designed to share data between different instances of Linrad, but generally these protocols are only supported by Linrad itself. The only other example I know of is MAP65, which can receive noise-blanked data from Linrad using the timf2 format.

The result of these experiments is a GNU Radio out-of-tree module called gr-linrad which allows to send data from GNU Radio and receive the data in Linrad. Currently, gr-linrad only supports sending a one-channel complex IQ signal using the raw 24 bit format, but I’ll probably add more options in the future. The intended application of gr-linrad is to easily add support for SDR hardware to Linrad. Usually GNU Radio has support for most SDR hardware in the market, perhaps through osmocom or other libraries. Linrad has support for a good amount of SDR hardware, but there are some notable exceptions of unsupported hardware, such as the HackRF One. I also want to use my Hermes-Lite 2.0 beta2 in Linrad, and this seems the easiest way to do it.

Another possible use of gr-linrad is as an instrumentation for any kind of GNU Radio flowgraph. It is very easy to stream data into Linrad, so it can be used as a very nice waterfall display or to do any sort of signal processing, such as noise blanking or adaptive polarization. Here I describe how to get the test flowgraph in gr-linrad working and some aspects of the network protocol.

The first step to set up the test flowgraph is to open and run examples/test.grc in gr-linrad with gnuradio-companion. Next, we open Linrad and configure the network input as follows.

From the main menu, we press U, A and Y to select network input and get into the network setup screen. Then we set our receive address to and the RX input data format to raw data, 24 bit. The network setup screen should be as in the image below.

Linrad network setup

Next we press Z to disable the output soundcard (or we select some soundcard with B instead). The RX A/D setup should be as in the image below.

Linrad RX A/D setup

Then we press X and W to exit the setup and save the parameters, and we press D to enter SSB mode. Linrad should start receiving data from GNU Radio in real time and we should see something as in the image below.

Linrad receiving test signal

Linrad’s network protocol has two parts. The first is the data streamer, which sends the samples in UDP packets in real time. The different formats use different UDP destination ports, so several formats can be enabled simultaneously. The second part is a TCP server that always listens on port 49812 and is interrogated by the clients on start up to get some parameters concerning the stream.

The contents of the UDP packets have the following structure, which is defined in globdef.h in Linrad’s source code:

typedef struct {
double passband_center;        //  8
int time;                      //  4
float userx_freq;              //  4
int ptr;                       //  4
unsigned short int block_no;   //  2
signed char userx_no;                 //  1
signed char passband_direction;       //  1

There is a header which has some parameters which can change dynamically and then NET_MULTICAST_PAYLOAD bytes worth of samples follow. Everything is in machine endianness. The samples are interleaved for all the channels, so for a single IQ channel in 24 bit raw format the first 3 bytes are the first sample from the I channel, the next 3 bytes are the first sample from the Q channel and so on. passband_center is the centre frequency of the passband in MHz, time is the time of day in milliseconds, and block_no is the packet number. The ptr is really important and depends on the concept of buffer. Linrad has a lot of circular buffers of a fixed size which are used for signal processing. The size of the input buffer is computed in the master and the clients inherit the same buffer size (which they get from the master using the TCP protocol). ptr points to the position of the circular buffer where the samples in the next UDP packet would start. Therefore, ptr is NET_MULTICAST_PAYLOAD % buffer_size for the first packet and then for each subsequent packet it gets incremented by NET_MULTICAST_PAYLOAD, always taking modulo buffer_size. In gr-linrad, the user has to decide an appropriate buffer size, which should be a power of two, although it would be good to decide it from the number of channels and sample rate. The default of 4096 should be good in many cases.

So far I only understand Linrad’s TCP protocol enough to implement something that works. The scheme is simple. The client can send a packet with one of several kinds of requests and the server responds with the appropriate data. The most important request is NETMSG_MODE_REQUEST, which is a packet that starts by 0xb8 (and it has some other data I don’t understand). The answer is a packet with 8 32-bit integers as follows (code taken from network.c in Linrad’s source).

    if(kill_all_flag)goto netserver_error_exit;

The important parameters are the following: rx_ad_speed is the sample rate, rx_ad_channels is the number of real channels, rx_rf_channels is the number of RF channels (so, for instance, a single channel IQ signal would have 2 AD channels and 1 RF channel, while a double channel IQ signal would have 4 AD channels and 2 RF channels), rx_input_mode is an OR of several flags (in practice, it has to be 1 for real format and 5 for IQ format), and block_bytes is the buffer size I have mentioned before. The rest of the parameters are not important for raw format streaming (Linrad can also stream FFT transforms instead of raw samples).

There are two other requests which start by 0xb5 and 0xb6. When I tested Linrad to understand how the protocol works, it seems that Linrad responds to these requests with 0x00 always, and that is what gr-linrad does as well. They seem to be for calibration data. In fact, the constants NETMSG_CAL_REQUEST = 0xa2b5 and NETMSG_CALIQ_REQUEST = 0xa2b6 appear in network.c.

The implementation in gr-linrad follows this description of Linrad’s protocol. There is a block which implements the TCP server, and the user can set all the important parameters. It is coded in Python for simplicity. There is also a block which implements the UDP streamer and is coded in C++ for performance. There is no callback implemented yet for updating the centre frequency of the passband, which can change dynamically. As you can see below, the test flowgraph in gr-linrad is really simple.

Test flowgraph in gr-linrad


  1. Hi Daniel. This is interesting although a bit above my level. I am looking for something similar. I am using MAP65. I have two IQ outputs pairs one IQ pair for Horizontal the other for Vertical. I can feed these one pair at a time into MAP65 using the soundcard input, but cannot see how to get both IQ channels into MAP65.
    Maybe this can only be done through the network UDP connection in MAP65. Hence my interest in what you have done here.
    Any ideas? I would appreciate if you could point me in the right direction.
    Gary G8EOH

    1. Hi Gary,
      MAP65 supports dual polarization (it is the whole purpose of using MAP65), so it should be possible to do what you want. I don’t use MAP65, so I don’t know all the details. If you are using a 4 channel sound card, this should be supported in MAP65 out of the box. If you are using a different dual-channel SDR, then it might not be supported directly in MAP65, so you would need to use Linrad and feed IQ data to MAP65 using the network protocol. What is the SDR device you are using?

      1. Hi Sorry for delay. I am using the RSP Duo. with the RSP UNO software.
        I have two rxs running with IQ Outputs to 2 virtual audio channels. I can link these one rx at a time to MAP65 and they work fine. but I need them both for the polarisation part. I dont think you can get the two rx outputs via the MAP soundcard connection. If there is a way please tell me.

        So I am trying to connect them via UDP.
        I have a simple GNU ap running with the two virtual sound cards connected, and I also have a noise source connected to a UDP sink that seems to connect to MAP65. It seems to be in bursts so its not quite right. I was hoping it was possible to use GNU Radio to get these two rx IQ signals out via UDP (Using your config for the UDP Sink. Does this make sense to you.? I appreciate your time. I am going through a rapid learning curve here.

        1. I am pretty sure that MAP65 can use a 4 channel audio device as dual polarization IQ source. There are many people using this configuration, with an RF receiver that goes to audio frequency and a Delta 44 or similar 4 channel soundcard.

          However, it might not be so easy to route 4 channel audio between RSP UNO and MAP65.

          The protocol used by gr-linrad to send data to linrad is not the same as the protocol used by linrad to send data to MAP65 (linrad has several different network protocols). Thus, if going this route, you should use RSP Duo -> GNU Radio -> gr-linrad -> linrad -> MAP65.

  2. Hi Having problems getting the example running. When I try to run I get the message :
    File “C:\Radio\GNURadio-3.7\gr-linrad-master\examples\”, line 28, in
    import linrad
    ImportError: No module named linrad
    I am using windows. Can you help?

      1. Thanks. This module is a great addition and works great! It allows me to add other hardware to linrad. I got gr-linrad not working with linrad 4.15 compiled directly from the source. gr-linrad works fine with a pre-4.15 version. Something seems to have changed with the network connection in the latest version of linrad.

Leave a comment

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.