Decoding LilacSat-2 telemetry

LilacSat-2 9k6 BPSK packet (left) and 9k6 FSK packet (right)

After having my first QSO through the Harbin Institute of Technology amateur radio satellite LilacSat-2, I decided to give a serious try to the telemetry decoding software. This is available as a GNURadio module. A Linux distribution with all the proper software installed and configured is provided, for an easy use. However, I have used GNURadio in the past, so I wanted to try to setup the GNURadio module directly on my machine.

The web page for LilacSat-2 gives also a description of the different telemetry formats. The satellite has an SDR radio transmitting on 437.200MHz. This radio is used for the FM amateur radio transponder and also to transmit several different telemetry formats. The satellite also transmits telemetry on 437.225MHz, presumably using a different (non-SDR) radio and a different antenna, so that the satellite can keep transmitting telemetry even if the SDR system fails. Typically, when the FM transponder is disabled, the satellite will transmit 9k6 BPSK telemetry on 437.200MHz and 4k8 GFSK telemetry on 437.225MHz. These can be seen in the picture above, which was made using my RF recording and baudline. The packet on the upper right is 4k8 GFSK and the packet on the lower left is 9k6 BPSK. Notice the slight slant due to Doppler.

I’m running GNURadio 3.7.7 on my Gentoo Linux machine. I started by cloning the git repository for gr-lilacsat and compiling and installing the GNURadio module. Several GNURadio Companion flowgraphs are provided in the examples folder. There are three different frontends for receiving the telemetry with a FUNcube Dongle Pro+, an RTLSDR or a USRP. It is very easy to create new frontends for anything that can work in GNURadio. There are four telemetry decoders, for different telemetry formats. These will listen in a UDP port and the frontend will send the samples through UDP to the four decoders, which can run simultaneously. The frontend will also record the IQ samples to a file.

There is also another piece of software that will receive the telemetry PDUs from the decoders and upload them to the servers of the Harbin Institute of Technology. This is supposed to be run when decoding and uploading telemetry in real time. As I was going to receive the telemetry without an internet connection available, I didn’t mess with this. Therefore, I disabled the block that sends the PDUs by TCP on each of the decoders, because otherwise the decoders will not run if they cannot connect to the uploader program.

I was going to use my FUNcube Dongle Pro+. The frontend for this device uses the module gr-fcdproplus by DL1KSV. I didn’t manage to make this module work. I always got some kind of error when importing fcdproplus into python2.7. However, I really don’t need to use this module, because I can fetch samples from the Dongle just by using an Audio Source block in GNURadio and set the frequency using Qthid (the decoder expects a centre frequency of 437.260MHz). Therefore, I created a new frontend flowgraph to get samples from an Audio Source block.

The decoder blocks use a custom block called Plan13 CC which performs Doppler correction. You will have to input the TLEs and your coordinates into this block, and it will compute the Doppler shift (presumably according to your computers clock) and correct for it. If you’re using the launcher that comes with the software, it should download the TLEs and set your coordinates in all the decoders for you. However, as I’m running the flowgraphs manually, I had to set these data by hand into the block. I also needed to disable the block that imports grc_param.

With all the software set up and seemingly working, I went out to the field (locator IO94ex) with my FUNcube Dongle Pro+ and hand pointed Arrow satellite antenna to decode the telemetry in real time on the 16:46UTC pass of 11/11/2015. Since the satellite transmits using circular polarization, I made no effort to match the polarization and kept roughly a “horizontal” polarization all the time.

Surprisingly, the Doppler correction didn’t work at all. It kept using absurd values for Doppler shift during the pass. In the debug information, it also shows the elevation and azimuth to the satellite, and these were wrong as well, so apparently the block has no idea where the satellite is. It might be that it didn’t like my TLEs, but I had just copied them from Celestrak and I’m pretty confident I got this right. With the Doppler correction block working badly, I only got one telemetry packet to decode.

Fortunately, I had the IQ recording of the pass. I didn’t like this Doppler correction method at all, because it can’t be used to process recordings, and this is something that I would be doing to try out several things. Therefore, I used an alternative solution: gr-gpredict-doppler. This GNURadio block allows the Doppler control in Gpredict to update a variable inside the GNURadio flowgraph. This variable can be then used to correct for Doppler by mixing the RF with a cosine signal of the appropriate frequency. The good thing about this is that you can both run it in realtime and use it to process recordings by feeding Gpredict the correct TLEs and using the time control feature.

Another good thing is that it seems that the cosine signal gets updated in a phase continuous manner. When correcting for Doppler, if the correction is not done properly, every time the frequency changes in the middle of a packet, the decoding will fail, because the frequency change messes up the decoder. For that reason, I first ran Gpredict using a 5 second update interval, to minimize the chance of getting an update in the middle of a packet. However, I have also tried using a 100ms update interval and this doesn’t seem to upset the decoder at all.

With this setup I managed to decode plenty of packets. I have posted to Gist the PDUs decoded from 4k8 GFSK telemetry and 9k6 BPSK telemetry (obviously the time marks on these logs are not realtime, but taken from the time when I decoded the recording). Surprisingly, I got good decodes even down to about 4º elevation on the LOS. It would be nice to have the binary format for this PDUs, so that I can decode the data, but so far I’ve been unable to find it.

There are some things that I find a bit weird in the gr-lilacsat flowgraphs. One is that tThe decoder will first shift the 437.200MHz and 437.225MHz signals to baseband and then decimate them to 48kHz. However, no low-pass filtering is applied before decimating, thus aliasing noise and potentially other signals into the desired signal. Another is that, for some reason, although the 9k6 BPSK decoder listens on UDP port 7200, the 1k2 AFKS and FM subaudio decoders listen both on port 7201. Not surprisingly, they don’t seem able to listen simultaneously on the same port. The first decoder run will be the one that gets the data. Also, the frontend sends the 437.225MHz samples to ports 7225 and 7226, but no decoder listens on 7226.


  1. Hello Daniel, I would like to decode the Lilacsat-2, but I am unable to find the decoder circuit from the Github repositry. Can you please guide me on where to find the circuit or a program that can be used for decoding.
    May I also know where I can find decoding circuits for any other satellites.

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.