A first look at DSLWP SSDV downlink

The Chang’e 4 is a Chinese lunar mission that will land a rover on the far side of the Moon by the end of 2018. To support this mission, the Chang’e 4 relay satellite will be launched six months before and put into a halo orbit around the Earth-Moon Lagrange L2 point. The relay will provide four 256Kbps links with the rover and lander on X-band and a 2Mbps link with Earth on S-band using a 4.2m dish. Two CE-4 microsatellites will be launched together with the relay satellite. They will be put in a 200km x 9000km lunar elliptical orbit. The main mission of the CE-4 microsatellites is to perform HF interferometry of celestial bodies, using the Moon as a shield from the radiation of the Sun and Earth. The satellites also carry an Amateur radio system called DSLWP, which will provide telecommand, telemetry and image downlink.

A team at Harbin Institute of Technology is currently designing the Amateur radio payload. As it is the case with previous HIT satellites such as BY70-1 and LilacSat-1, the payload will have a camera which can be telecommanded by radio Amateurs, which can use it to take and download pictures. Yesterday, Wei BG2BHC has released some work in progress of the image downlink. Many important parts of the downlink will still change, but releasing the work in progress at this early stage is a very good idea. Probably it is not too late in the development process so that the Amateur community can contribute with ideas and improvements.

The release consists of an IQ recording of the signal containing a full image and a decoder in gr-lilacsat. The IQ recording is at 2ksamp/s, since the signal is FSK at 250baud. Note that the recording is almost 32 minutes long. It takes a while to transmit an image at such a low rate. However, a low baudrate and a good amount of FEC are needed for an effective downlink from the Moon, given the huge path loss of around 197dB in the 70cm band.

The good news about this work in progress is that SSDV is now used to transmit the image. SSDV is a packetised protocol based on JPEG, but which is tolerant to packet loss. In contrast, BY70-1 and LilacSat-1 send JPEG images in 64byte chunks, and a single lost chunk can destroy the image completely. SSDV was originally developed to transmit images from Amateur high altitude ballons, so it is a good idea to use it also for DSLWP.

The bad news is that the way that SSDV has been included into the downlink protocol is not very optimal. In the rest of this post I do an in-depth look at the protocol, point out the main problems and suggest some solutions. Hopefully the protocol can still be modified and improved.

The current downlink protocol of DSLWP is based on the same CCSDS stack using an r=1/2, k=7 convolutional code and Reed-Solomon (255,223) that the rest of the HIT satellites use. In fact, it is very similar to the 4k8 GFSK downlink of LilacSat-2. However, DSLWP uses 250baud GFSK. Wei has already stated that he intends to change GFSK for GMSK with coherent receiving and the convolutional code by a Turbo code. He also said that he will design a new packet header, although it is not clear what he has in mind.

In the following, I will describe the protocol from the point of view of the transmitter. There are 3 independent KISS streams, which are called virtual channels. All the packets from DSLWP are 223 bytes long. They contain a 5 byte header, whose contents are specified in dslwp_tm_header.h, and 218 bytes from one of the 3 virtual channels (the header states which channel). The 223 byte packets are encoded with the CCSDS Reed-Solomon (255, 223) code using the conventional basis and scrambled with the CCSDS synchronous scrambler. Then the CCSDS 32-bit syncword 0x1acffc1d is added in front of the packet and the packets are sent through the CCSDS r=1/2, k=7 convolutional encoder (following the CCSDS/NASA-GSFC convention for the polynomials). The output of the convolutional encoder is sent as 250baud GFSK.

In the sample recording by Wei, the SSDV packets corresponding to the image are sent in the second virtual channel. They are sent in a consecutive manner, leaving only two c0 bytes between them (c0 is the frame delimiter or idle marker in the KISS protocol). There are a total of 95 SSDV packets. I have saved the packets in the file ssdv.bin. This file can be decoded with SSDV to produce the JPEG image below. It is a 640 x 480 image and its size is 20056 bytes.

Test image for the DSWLP SSDV downlink

SSDV packets are always 256 bytes long, so the SSDV packets amount to a total of 24320 bytes. This is a reasonable overhead to pay, since the SSDV packets include 32 bytes of Reed-Solomon check bytes, which accounts for most of this overhead.

The main problem that I find with the current protocol is that the SSDV packets, which are 256 bytes long and include their own Reed-Solomon FEC, do not fit well within the scheme of sending a KISS stream by chunks of 218 bytes. Since the DSLWP packets are also protected by Reed-Solomon, there are two independent layers of Reed-Solomon FEC. This adds an unreasonable amount of overhead and doesn’t provide much extra protection (on the other hand, a concatenated code with a convolutional code and Reed-Solomon is usually a good idea). Also, since SSDV packets are slightly larger than the KISS stream chunks, each SSDV packet is sent inside two or three DSLWP packets. This means that if a DSLWP packet is lost, we will usually lose two SSDV packets, since in general a DSLWP packet contains bytes from two different SSDV packets.

For the best performance, SSDV packets should not be fragmented. If they need to be fragmented, each fragment should contain bytes from only one SSDV packet. Also, there should be a single layer of Reed-Solomon FEC.

In the recording, a total of 114 DSLWP packets were sent. Since each packet is 259 bytes long (counting the CCSDS syncword), this amounts to 29526 bytes, which is an overhead of 21.4% in comparison with the SSDV packets alone. Since the bit rate is already quite low, the overhead should be as low as possible. An overhead of 20% is too much.

There are many different ways in which the current protocol can be improved. Ideally, we would like to send SSDV packets without fragmentation and as little overhead as possible. A simple idea is to attach the CCSDS syncword in front of each SSDV packet and send these packets through the convolutional encoder. Optionally, each SSDV packet can be scrambled with the CCSDS scrambler. However, it is necessary to study whether scrambling provides any advantage. The drawback of the scrambler is that it transforms each bit error uncorrected by the Viterbi decoder into three byte errors for the Reed-Solomon decoder. Probably, since most of the contents of an SSDV packet is scan data from a JPEG file, the data is random enough that a scrambler is not necessary.

This approach doesn’t preclude the possibility of sending telemetry and other data using the current protocol. SSDV packets (perhaps unscrambled) and scrambled DSWLP packets with their Reed-Solomon FEC can both be sent without any type marker, since the SSDV decoder and the DSWLP Reed-Solomon decoder will reject packets of the other type.

A very small improvement can also be gained if one notes that it is not necessary to send the first byte of each SSDV packet over the air. This byte is always 0x55 and it serves as a sync byte. In our case, we are using CCSDS syncword, so it is not necessary to send the sync byte. The receiver can insert it later, before passing the packets to the SSDV decoder.

Also, one should note that the SSDV protocols supports uncoded packets. In this mode, no FEC is used, and instead there are 32 extra bytes for JPEG scan data. Uncoded SSDV therefore adds very little overhead to the original JPEG file. It makes sense to use uncoded SSDV if one wishes to replace the Reed-Solomon (255,223) code by another outer code. In this case, the uncoded SSDV packets would be encoded by the outer code and then passed to the convolutional encoder, together with their syncword. Probably uncoded SSDV shouldn’t be used with a convolutional code alone, since the Viterbi decoder can still leave some remaining bit errors and the SSDV decoder rejects any packet with incorrect CRC32. However, if the convolutional code is replaced by a code with better performance, then it can also be a good idea to use uncoded SSDV.

I can’t talk about SSDV and FEC without mentioning Wenet from Project Horus. This is a 115kbps FSK modem for Amateur high altitude balloons which sends SSDV images using an LDPC code. This project is definitely worth looking at carefully. Perhaps it’s not necessary to reinvent the wheel for DSLWP and an adaptation of Wenet will perform well.

I have added a decoder for the DSWLP test recording to gr-satellites. The main improvement of my decoder compared to the decoder in gr-lilacsat is that it uses soft Viterbi decoding, which improves the BER performance noticeably. Still, this is not a fully functioning decoder. The SSDV decoder expects complete SSDV packets on its input. If a DSWLP packet is lost, then a partial fragment of an SSDV packet is stored in the output file and the SSDV decoder complains. A tool that only passes complete SSDV packets to the decoder will be needed.


  1. Removing the sync byte, packet type, callsign and RS codes from the SSDV packet would leave you with 218 bytes and would fit quite nicely into the system you’ve described. These could all be re-applied by the receiver, and means you won’t have any fragmented packets.

    1. Hi Phil, that’s a very good and simple idea. 218 bytes fits exactly into one of the virtual channels, without using KISS.

      Then only drawback is that the receiver needs to reconstruct the RS check bytes or use a modified SSDV decoder that ignores RS. The first is rather easy to do and the second is probably not a good idea, since ssdv.habhub.org runs its own SSDV decoder and we probably want to use habhub for collaborative decoding.

      1. It’s not documented, but the habhub server will accept 224 byte packets so long as the CRC is valid.

  2. We’ve done a lot of analysis/bench testing on Wenet, and obtained practical results very close to theory. Couple of suggestions:

    1/ This channel is AWGN, I’m not sure the RS code is useful. Suggest a powerful LDPC/Turbo instead.

    2/ Carefully test the GNU radio demod and make sure it’s performing close to theory for your modulation method.

    3/ Scaling our Wenet results for this baud rate:

    MDS = Eb/No + 10log10(bit rate) + NoiseFigure – 174 = 6 + 10log10(250) + 1 = -143dBm

    So you need some antenna gain or Tx power to get over that path loss. The Eb/No figure is for 1% BER with 4FSK that our rate 2/3 LDPC code cleans up just 1dB overhead for the parity bits. If you have more bandwidth available, a longer code can be used.

    Wei or Daniel – happy to discuss further if useful…

    Cheers from David VK5DGR

  3. Oh I forget to mention, 4FSK is cool as it gets about the same performance as coherently demodulated GMSK (e.g 1% BER at Eb/No=6dB) but is much simpler to implement:


    It’s kind of a sweet spot between the extra bandwidth mFSK uses and demod performance. We have developed an optimal demodulator inf C and Octave, part of the codec 2 repo that we use for Wenet, no GNU radio builds required….

    Also the LDPC codes we have been using for Wenet can be encoded on an 8-bit micro-controller, all the decode complexity is on the ground.

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.