Decoding GOMX-1 telemetry

GOMX-1 is a 2U cubesat from GomSpace that was launched in November 2013 into a sun-synchronous orbit. As far as I know, it was the first satellite with an ADS-B receiver payload. It transmits telemetry on the 70cm Amateur band, including some data from the ADS-B receiver, as GOMX-3 does. Some Amateurs, including me, had tried to decode its telemetry on several occasions, without success. GOMX-3 will decay in about 4 weeks, as it was launched from the ISS on October 2015. Therefore, it now becomes more interesting to decode GOMX-1, which is in a longer term orbit. After one more serious try, I’ve been able to decode the telemetry. This is the first time that an Amateur decodes telemetry from GOMX-1 completely. The decoder code can be found in gr-satellites and gr-ax100, including an example wav file in gr-ax100/examples/gomx-1.wav.

GOMX-1 uses an unusual modulation: 4800bps AF GMSK, with tones at 2.4kHz and 4.8kHz. This means that the data is first modulated as an audio frequency waveform using GMSK modulation with a centre frequency of 3.6kHz (with the corresponding 1.2kHz MSK deviation this produces tones at 2.4kHz and 4.8kHz). This audio waveform is then FM modulated and transmitted.

Mike DK3WN reports that he learnt the following details from GomSpace:

Preamble: 50 ms of 0x55 transmitted only when the radio keys up. There is no gap between packets.
Sync word: 4 bytes: 0xC3AA6655 – Unencoded and unscambled
Length field: 3 bytes: The first 12 bits are golay encoded parity, and the final 12 bits contain 8 bits length field and a 4 bit optional FEC field
Data field: CCSDS scrambled, Viterbi encoded and/or Reed-solomon checksum added

Knowing the sync word, a look at the audio frequency GMSK waveform reveals that the 2.4kHz tone corresponds to the bit 1 and the 4.8kHz tone corresponds to the bit 0, contrary to what one may first expect.

I’ve being using a packet from an old recording I did on March 2015. From measuring the packet length in Audacity, I’ve determined that the packet is 251 bytes long, not counting the preamble, sync word, and two 0x55 bytes that are transmitted at the end of the packet as some kind of short postamble. The length field is 0x1c 0x96 0xf8. Since 0xf8 = 248, we see that the last byte of the length field is the length of the packet, not counting the 3 bytes of the length field. Thus, 0x6 is the “optional FEC field” and 0x1c9 is the Golay parity.

We don’t know anything about the “optional FEC field”, and we will probably never know. However, the Golay parity provides reasonable protection against bit errors, so this FEC field is not very important. Regarding Golay parity, the code used here is the extended binary Golay code. This is a (24,12,8) linear code which adds 12 parity bits to 12 bits of data and is able to correct up to 3 bit errors. This Golay code is unique up to permutation of the parity bits. Unfortunately, we don’t know which permutation is used here, and without that information we can’t use the Golay code to correct for errors. I’ve computed the Golay parity bits of the 0x6f8 and the number of parity bits that are 1 matches the number of bits that are 1 in 0x1c9. This is good, but still leaves many possibilities for the permutation.

Mike has some packets whose length field is 0x29 0x86 0xf6. Assuming no bit errors (several packets have this same length field), this indicates a length of 246 bytes, which is 2 bytes shorter than my packet. Surely this corresponds to a beacon of type B. I’ll talk about the beacon format below, but for now the only thing you need to know is that there are two beacon types: A and B, and B is 2 bytes shorter than A. The current version of the decoder only supports beacons of type A, but support for type B will come soon. Perhaps it’s possible that the knowledge of the length fields 0x1c 0x96 0xf8 and 0x29 0x86 0xf6 is enough to determine the permutation of the Golay parity bits. I haven’t tried this yet nor run the numbers on whether this is feasible.

The easiest solution to this length field problem is to assume that the satellite only uses packets which are 248 or 246 bytes long, for which we know the corresponding 3 byte length fields. When we receive the length field of a packet, we can compute its Hamming distance to the two known length fields and pick the one which is nearest.

The 248 (or 246) bytes of the packet are scrambled using the CCSDS scrambler. This is the same scrambler that AAUSAT-4 uses, so I’ve been able to reuse the code. The FEC used is a shortened code obtained from a Reed-Solomon (255,223). The encoding conventions are the same as in AAUSAT-4 and GOMX-3. Thus, the decode_rs_8() function from Phil Karn KA9Q’s libfec can be used.

After Reed-Solomon decoding, we are left with a 216 (or 214) byte CSP packet. The 4 bytes of the CSP header are in reversed order, as it is the case for GOMX-3 (but not for AAUSAT-4). It is easy to determine this because if you read the CSP header in the wrong order you get a non-zero reserved field, and some other fields don’t make sense. The contents of the CSP header of the “packet under test” are:

CSP header:
        Priority:               2
        Source:                 1
        Destination:            10
        Destination port:       30
        Source port:            0
        Reserved field:         0
        HMAC:                   0
        XTEA:                   0
        RDP:                    0
        CRC:                    0

These match well the network diagram of the satellite: priority 2 means normal priority, the CSP address 1 corresponds to the NanoMind OBC and the CSP address 10 corresponds to the ground station.

The beacon payload is then 212 (or 210) bytes long. The format of the beacon was provided by Mike as structures in C. It can be seen in this gist. There are two beacon types: A and B. Type A is 212 bytes long and type B is 210 bytes long. Type A is the regular beacon with data about all systems (including the ADS-B receiver, called GATOSS) and type B has only extended ADCS data. The two beacon types have a common header which consists of a time stamp and a byte of flags, which presumably determines the type of beacon. The remaining fields are all different. It would be good to find out how the byte of flags works, but this is not necessary, as we can tell the two beacon types apart just from their size. By simple trial and error I’ve found out that all the fields are in big-endian format. The contents of my packet are:

Timestamp:      2015-03-31 20:57:01
Flags:          0x79
Beacon A:
        Boot count:     573
        Board temp 1:   -6.0ºC
        Board temp 2:   -4.0ºC
        Panel temps:    [0.0, -28.5, -26.75, -13.25, -28.25, -20.0]ºC
        Bytes corrected by RS:  187
        RX packets:             55
        RX errors:              35
        TX packets:             4633
        Last temp A:            -2ºC
        Last temp B:            -3ºC
        Last RSSI:              -106dBm
        Last RF error:          -10840Hz
        Last battery voltage:   8.42V
        Last TX current:        848mA
        Boot count:             1104
        Boost converter voltages:       [5.837, 5.82, 0.0]V
        Battery voltage:                8.251V
        Current out:                    (4, 2, 146, 30, 7, 0)mA
        Current in:                     (81, 438, 0)mA
        Boost converter current:        308mA
        Battery current:                184mA
        Temperature sensors:            (-4, -3, -4, -4, -1, -2)ºC
        Output status:                  0x1c
        EPS reboots:                    81
        WDT I2C reboots:                42
        WDT GND reboots:                28
        Boot cause:                     8
        Latchups:                       (0, 0, 0, 0, 0, 0)
        Battery mode:                   invalid mode 4
        Average FPS 5min:       0
        Average FPS 1min:       0
        Average FPS 10sec:      0
        Plane count:            0
        Frame count:            0
        Last ICAO:              0x0
        Last timestamp:         1970-01-01 00:00:00
        Last latitude:          0.0
        Last longitude:         0.0
        Last altitude:          0ft
        CRC corrected:          0
        Boot count:             0
        Boot cause:             0
        Temp:           -8ºC
        Boot count:     124
        Reset cause:    2
        Switch status:  0xfc
        Burn tries:     (0, 0)
        Tumble rate:    (-0.652618408203125, -3.70880126953125, 0.2416229248046875)
        Tumble norm:    (3.9943442344665527, 0.5196681618690491)
        Magnetometer:   (-344.3216247558594, 178.07089233398438, -84.8233642578125)
        Status:         0x3
        Torquer duty:   (85.0, 85.0, -85.0)
        ADS state:      0x22
        ACS state:      0x22
        Sun sensor:     (4, 5, 77, 110, 4, 0, 2, 0)

Unfortunately the data corresponding to the ADS-B receiver is zeroed out. I wonder whether the ADS-B receiver is still working, as this is the most interesting aspect of GOMX-1.

In a few days, I’ll add support for beacons of type B, decoding the length field one way or another. For now, I leave some open questions:

  1. In the length field, is it possible to determine the permutation of the Golay parity bits just using the values 0x1c 0x96 0xf8 and 0x29 0x86 0xf6? Are any other length fields ever transmitted?
  2. What is the meaning of the flags field in the beacon? Can we tell beacon types A and B apart from the contents of this field?
  3. Does the satellite ever transmit valid data from the ADS-B receiver these days?

Update September 18th: Andy UZ7HO has managed to find the parity check matrix of the Golay code used in the length field. It is the same matrix that is used in the book The Art of Error Correcting Coding, Section 2.2.3, but the bit order has to be reversed. I have used his findings to add Golay decoding to the GNUradio decoder. This is quite fortunate, because “reverse engineering” the Golay code is not so easy as I thought. Although it is true that the extended binary Golay code is unique up to a permutation of the coordinates, it is not true that when two different Golay codes are used as systematic codes the parity bits of a certain word differ in a permutation, as I had stated above.


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.