# Recovering an image transmitted by DSLWP-B

The image accompanying this post has a nice story to it. It was taken by the Amateur camera in DSLWP-B, the Chinese microsatellite in lunar orbit. On February 27, a download of this image was attempted by transmitting the image in SSDV format in the 70cm band and receiving it in the Dwingeloo radiotelescope, in the Netherlands.

The download was attempted twice, but due to errors in the transmission, a small piece of the image was still missing. Today, the Amateur payload of DSLWP-B was active again, and the plan was to download the missing piece, as well as other images. However, after the payload turned on and transmitted its first telemetry beacons, we discovered that the image had been overwritten.

The camera on-board DSLWP-B has a buffer that stores the last 16 images taken. Any of these images can be selected to be transmitted (completely or partially) while the Amateur payload is active. An image can be taken manually by issuing a command from ground. Besides this, every time the Amateur payload powers on, an image is taken. Of course, taking new images overwrites the older ones.

This is what happened today. The image we wanted to download was the oldest one in the buffer and got overwritten as soon as the payload turned on. This is a pity, especially because there was another activation of the payload last Friday, but a large storm in Germany prevented Reinhard Kuehn DK5LA’ from moving his antennas safely, so the satellite couldn’t be commanded to start the download.

After seeing that the image had been overwritten, Tammo Jan Dijkema suggested that I try to recover manually the missing chunk in the recording made on February 27. As you can see, I was successful. This is a report of how I proceeded.

First I downloaded the IQ recording and SSDV data from the CAMRAS DSLWP data server. By running the ssdv_sort.py script with the SSDV data recorded in Dwingeloo, we get the following.

$~/jupyter_notebooks/dslwp/ssdv_sort.py DSLWP-B_PI9CAM_2019-02-27T07_27_27.bin /tmp/camrasCalling SSDV decoder for image 0xb0Callsign: DSLWPImage ID: B0Resolution: 640x480MCU blocks: 2400Sampling factor: 2x1Quality level: 5Gap detected between packets -1 and 1Gap detected between packets 17 and 19Gap detected between packets 31 and 33Gap detected between packets 68 and 70Read 70 packetsCalling SSDV decoder for image 0xabCallsign: DSLWPImage ID: ABResolution: 640x480MCU blocks: 2400Sampling factor: 2x1Quality level: 5Packets are not in order. 1 > 1Packets are not in order. 1 > 1Packets are not in order. 2 > 2Packets are not in order. 2 > 2Gap detected between packets 2 and 4Packets are not in order. 4 > 4Packets are not in order. 4 > 4Packets are not in order. 5 > 5Packets are not in order. 7 > 7Packets are not in order. 8 > 8Packets are not in order. 9 > 9Packets are not in order. 10 > 10Packets are not in order. 10 > 10Packets are not in order. 14 > 14Packets are not in order. 15 > 15Packets are not in order. 16 > 16Packets are not in order. 17 > 17Packets are not in order. 19 > 19Packets are not in order. 20 > 20Packets are not in order. 21 > 21Packets are not in order. 22 > 22Packets are not in order. 23 > 23Packets are not in order. 24 > 24Packets are not in order. 25 > 25Packets are not in order. 26 > 26Packets are not in order. 28 > 28Packets are not in order. 29 > 29Packets are not in order. 31 > 31Packets are not in order. 32 > 32Packets are not in order. 33 > 33Packets are not in order. 34 > 34Packets are not in order. 35 > 35Packets are not in order. 36 > 36Packets are not in order. 37 > 37Packets are not in order. 38 > 38Packets are not in order. 39 > 39Packets are not in order. 40 > 40Packets are not in order. 41 > 41Packets are not in order. 45 > 45Packets are not in order. 48 > 48Packets are not in order. 49 > 49Packets are not in order. 51 > 51Packets are not in order. 52 > 52Packets are not in order. 53 > 53Packets are not in order. 54 > 54Packets are not in order. 55 > 55Packets are not in order. 56 > 56Packets are not in order. 57 > 57Packets are not in order. 58 > 58Packets are not in order. 60 > 60Packets are not in order. 61 > 61Packets are not in order. 62 > 62Packets are not in order. 63 > 63Packets are not in order. 64 > 64Packets are not in order. 65 > 65Packets are not in order. 68 > 68Packets are not in order. 69 > 69Gap detected between packets 69 and 71Read 126 packets We see that two images, 0xb0 and 0xab are present in the data. The image we are interested in is 0xab (or 171), which has most of its packets duplicated (hence the “packets are not in order” messages) because it was transmitted twice completely. However, we see that the decoder detects some gaps, meaning that the following chunks are missing: 3 and 70. The decoded image is shown below. Chunk 70 is at the bottom of the image, and we don’t care about it, because it is completely black. However, the missing chunk 3 is what causes the purple strip at the top of the image. This is the chunk that we intended to download again today. Next, I play back the IQ recording with my GNU Radio decoder flowgraph and use inspectrum to view the waterfall of the recording. I try to find in what parts of the recording chunk 3 is transmitted and what is the problem that prevents the decoder from getting this chunk intact. Maybe I’m lucky and it is just a matter of tuning parameters such as the PLL bandwidth. However, we are used to seeing corrupted frames caused by jumps in the TCXO on-board DSLWP, so I expect to find this kind of problem. Fortunately, I had already thought a solution for this problem, which is the reason why Tammo suggested me to fix this image. The problem with the frequency jump suffered by the TCXO is that it upsets the OQPSK receiver PLL, so it loses phase lock. Instead of reading the GMSK signal as OQPSK, we can read it as FSK. This has the advantage that the small frequency jump is barely noticeable, so we can read off the symbols from the FSK demodulation without any problems. This is not the end of the story, though. The GMSK signal is precoded to allow it to be read off as OQPSK directly (you can read an in-depth discussion about precoding in this post). If we read the signal as FSK, we need to undo this precoding. The bad news is that the precoder is a differential encoder. By this, I mean something that transmits $$y_n = x_n – x_{n-1}$$, not the usual encoder that you would use together with a differential decoder. This usual encoder I call integral encoder, because it transmits $$y_n = \sum_{k = -\infty}^n x_k$$ (and then the differential decoder undoes the integral). Analogously, the decoder for a differential encoder is an integral decoder, which recovers $$x_n = \sum_{k=-\infty}^n y_k$$ (note that this is a telescoping sum). The unpleasant thing about integral decoders is that if one of the received $$y_n$$ has a bit error, then the whole recovered sequence will be wrong from that point on. For this reason, no one uses differential encoders/integral decoders in a real communications system. In contrast, if we deal with a differential decoder, and one received $$y_n$$ is wrong, then only $$x_n$$ and $$x_{n+1}$$ will be recovered incorrectly. For this reason, integral encoders/differential decoders are used in real communications systems. The thing that saves us is that the SNR with which the large radiotelescope at Dwingeloo receives the signal from DSLWP-B is so large that we usually don’t get any bit errors in the FSK symbols. Therefore, we can run our integral decoder to undo the precoder. Even one bit error in the FSK symbols would cause bit errors from that point onwards in the frame, preventing its correct decoding, despite the use of a Turbo code. The complete signal processing can be seen in this Jupyter notebook. We start with a segment of the IQ recording containing a transmission of chunk 3, and we end up with the Turbo codewords transmitted in that segment. For simplicity, the Turbo codewords are extracted using hard symbols. It is convenient to remark that, from the two times that chunk 3 is transmitted in the recording, I have used the second one. When analyzing the first one, I found out that the symbol clock of the GMSK signal seemed to jump. This most likely indicates that some IQ samples were lost in the receiver when doing the recording. This problem is difficult to fix, so I used the second transmission, which just has a clock jump in the transmitter TCXO. The Turbo codewords obtained in the Jupyter notebook are stored in a file. Then they are read with this GNU Radio flowgraph, which contains the last half of a DSLWP-B decoder (from the Turbo decoding onwards). The flowgraph performs Turbo decoding and telemetry parsing, outputting the SSDV frames to a file called dslwp_ssdv_recording_fix.bin, which can be obtained in this gist. After running this flowgraph, using cat we can concatenate the SSDV data received at Dwingeloo with the data that was decoded manually using FSK demodulation. Then we can run ssdv_sort.py again. $ cat  ~/Descargas/DSLWP-B_PI9CAM_2019-02-27T07_27_27.bin /tmp/dslwp_ssdv_recording_fix.bin > /tmp/fixed.bin\$ ~/jupyter_notebooks/dslwp/ssdv_sort.py /tmp/fixed.bin /tmp/camras_fixedCalling SSDV decoder for image 0xb0Callsign: DSLWPImage ID: B0Resolution: 640x480MCU blocks: 2400Sampling factor: 2x1Quality level: 5Gap detected between packets -1 and 1Gap detected between packets 17 and 19Gap detected between packets 31 and 33Gap detected between packets 68 and 70Read 70 packetsCalling SSDV decoder for image 0xabCallsign: DSLWPImage ID: ABResolution: 640x480MCU blocks: 2400Sampling factor: 2x1Quality level: 5Packets are not in order. 1 > 1Packets are not in order. 1 > 1Packets are not in order. 1 > 1Packets are not in order. 2 > 2Packets are not in order. 2 > 2Packets are not in order. 2 > 2Packets are not in order. 4 > 4Packets are not in order. 4 > 4Packets are not in order. 4 > 4Packets are not in order. 5 > 5Packets are not in order. 5 > 5Packets are not in order. 6 > 6Packets are not in order. 7 > 7Packets are not in order. 7 > 7Packets are not in order. 8 > 8Packets are not in order. 9 > 9Packets are not in order. 10 > 10Packets are not in order. 10 > 10Packets are not in order. 14 > 14Packets are not in order. 15 > 15Packets are not in order. 16 > 16Packets are not in order. 17 > 17Packets are not in order. 19 > 19Packets are not in order. 20 > 20Packets are not in order. 21 > 21Packets are not in order. 22 > 22Packets are not in order. 23 > 23Packets are not in order. 24 > 24Packets are not in order. 25 > 25Packets are not in order. 26 > 26Packets are not in order. 28 > 28Packets are not in order. 29 > 29Packets are not in order. 31 > 31Packets are not in order. 32 > 32Packets are not in order. 33 > 33Packets are not in order. 34 > 34Packets are not in order. 35 > 35Packets are not in order. 36 > 36Packets are not in order. 37 > 37Packets are not in order. 38 > 38Packets are not in order. 39 > 39Packets are not in order. 40 > 40Packets are not in order. 41 > 41Packets are not in order. 45 > 45Packets are not in order. 48 > 48Packets are not in order. 49 > 49Packets are not in order. 51 > 51Packets are not in order. 52 > 52Packets are not in order. 53 > 53Packets are not in order. 54 > 54Packets are not in order. 55 > 55Packets are not in order. 56 > 56Packets are not in order. 57 > 57Packets are not in order. 58 > 58Packets are not in order. 60 > 60Packets are not in order. 61 > 61Packets are not in order. 62 > 62Packets are not in order. 63 > 63Packets are not in order. 64 > 64Packets are not in order. 65 > 65Packets are not in order. 68 > 68Packets are not in order. 69 > 69Gap detected between packets 69 and 71Read 133 packets

We see that chunk 3 is no longer missing from image 0xab. The SSDV decoder now produces the following image, which is also shown at the top of this post.

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