Decoding Astrocast 0.1

Astrocast 0.1 is an Amateur satellite built by the Lucerne University of Applied Sciences and Arts (Hochschule Luzern). It is an in-orbit demonstrator for a future constellation of small satellites providing L-band data services for internet of things applications. The Amateur payload includes an on-board GPS receiver and a PRBS ranging signal transmitter for precise orbit determination .

This satellite was launched on December 3 on the SSO-A launch, but we only have payed attention to it recently. Its IARU coordinated frequency is 437.175MHz (actually it is a bit strange, because the IARU coordination data speaks about Astrocast 0.2, which hasn’t been launched yet). However, the satellite appears to be transmitting on 437.150MHz.

As it turns out, we had an unidentified object transmitting on 437.150MHz. This object was first thought to be RANGE-A, which was also on the SSO-A launch, as this frequency was assigned to RANGE-A. However, the RANGE-A team confirmed that this wasn’t their satellite, and I wasn’t able to identify the modem used by the mystery 437.150MHz signal.

Yesterday, Mike Rupprecht DK3WN noticed that this unidentified signal corresponded to Astrocast 0.1, and sent me some technical documentation about the protocols used by this satellite. Using that information, I confirmed that the mystery satellite at 437.150MHz was indeed Astrocast 0.1 and now I have added a decoder to gr-satellites.

The documentation for Astrocast says that it uses FX.25 and claims that it is fully compatible with AX.25. The FX.25 protocol is a backwards-compatible way of adding FEC to AX.25. I have already spoken (for instance in my ESEO decoding post) about how AX.25 is not really well suited for using FEC and the dangers of adding FEC carelessly, due to possible traps such as bit-stuffing. Although not used much, FX.25 is a good way of adding FEC to AX.25, if implemented correctly.

However, the implementation of FX.25 by Astrocast is not correct, rendering it incompatible with standard AX.25. This is one of the reasons why I couldn’t identify this signal at first. Fortunately, the documentation is well written, so it has not been difficult to do a decoder for Astrocast, including FEC decoding.

The modulation is 1k2 FSK (not AFSK). This is a bit unusual, as most AX.25 systems at 1k2 use AFSK, but it is not a major problem. One major difference which does break compatibility with standard AX.25 is that Astrocast uses NRZ encoding, while standard AX.25 uses the differential NRZ-I encoding. This is important. The AX.25 bit stuffing ensures that there are no long runs of ones, which in NRZ-I encoding correspond to a constant value. Nothing prevents long runs of zeros, since these are alright in NRZ-I coding, because they correspond to a toggling of the line value. However, niether long runs of ones nor long runs of zeros are acceptable in NRZ, so using AX.25 with NRZ is a bad idea.

The frame structure, as shown in the Astrocast documentation, can be seen in the figure below.

Astrocast packet structure

This is the standard structure for an FX.25 frame, except for the fact that Astrocast has included a PRBS inside the FEC codeblock but outside the AX.25 frame. While this wouldn’t break AX.25 compatibility, I don’t think it is a good idea. The problem is that this spends FEC capability to correct bit errors in the PRBS, which is not useful. I would transmit the PRBS outside the FEC codeblock. For instance, just after the postamble. Also, Astrocast always sends FEC codeblocks of 255 bytes, probably for simplicity. This means that a lot of padding is used, thus wasting FEC capabilities.

An interesting thing is the way they have tried to deal with bit-stuffing. Of course, bit-stuffing complicates the creation of FX.25 frames because the bit-stuffed AX.25 frame has to be padded to an integer number of bytes before applying FEC. The Astrocast team has tried to eliminate this difficulty by making sure that their AX.25 packet doesn’t need bit stuffing, because there are no runs of 5 ones inside the frame. In this way, they don’t need to implement bit-stuffing or care about padding to bytes.

Since the payload of their AX.25 frames is ASCII, they just forbid some particular ASCII characters that would cause runs of 5 ones. Also, their AX.25 header doesn’t happen to have runs of 5 ones. However, it seems they have forgotten about the CRC-16. A run of 5 or more ones will sometimes happen in the CRC-16, and this inevitably requires bit stuffing. Since they are not doing bit stuffing at all, this is another reason why the protocol used by Astrocast is incompatible with AX.25, besides their use of NRZ.

The Astrocast decoder in gr-satellites is shown in the figure below.

Astrocast decoder

The Sync and create packet PDU block is used to detect the correlation tag and extract the FEC codeblock. The Reflect bytes block reverses the bit ordering of each byte, since bytes are transmitted in LSB order, as it is the case for AX.25. Then we perform Reed-Solomon decoding. After this, the Check Astrocast CRC-16 block preforms the following functions: it drops the initial 0x7e flag marking the start of the AX.25 frame, finds the next 0x7e flag, which marks the end of the AX.25 frame, and checks the AX.25 frame CRC-16.

Note that in this unlikely (but possible) case that the CRC-16 contains a 0x7e byte, then this decoder will fail. This is one of the problems of not using bit stuffing in the CRC.

The payload of the AX.25 frames transmitted by Astrocast contains ASCII text with a NMEA GPRMC message from the on-board GPS receiver and a NMEA-like HK message containing telemetry.

Below you can see the frames contained in the recording that Mike DK3WN sent me.

The GPRMC information is invalid (the date and time is wrong and the position doesn’t match the orbit). The telemetry fields are the following:

Time since 2016-01-01, in units of 2^-16 seconds. In the case of the first packet, this works out to 2018-12-29 18:52:52, which I’m not sure if it matches the time when Mike did the recording (according to the TLEs I’m using, Astrocast wasn’t over Germany at that moment).