Astro~/0.3
** Docs / Guides / Handle a lossy link * PAGE 08 / 27
** Astro * Docs
** /docs/guides/lossy-link

Handle a lossy link

What happens when frames are dropped and corrupted, and how the protocols cope.

Real links lose frames and corrupt bits. CCSDS has four separate mechanisms for that, working at different layers, and this shows all four doing their job at once.

The complete program is examples/lossylink. Run it:

go run ./examples/lossylink/

Output varies run to run, the link drops and corrupts at random.

The four mechanisms

LayerMechanismCatches
CodingReed-SolomonBit errors, up to 16 bad bytes per codeword
FrameCRC-16Anything RS could not fix
FrameMC and VC countersFrames that never arrived
FrameFirst Header PointerWhere to start reading again after a gap

They are layered on purpose. RS fixes what it can. CRC rejects what RS could not. Counters notice what is missing. The FHP gets the packet stream back in sync.

The chain

Smaller frames than the downlink guide, to make packets span more often:

Frame (128 B) -> RS encode (128->160 B) -> randomize -> ASM -> CADU (259 B)

and backwards on the ground.

Spacecraft: generating 20 telemetry packets...
  Sent: 20 packets (2233 bytes) in 19 frames
  Each frame: 128 bytes -> RS(255 bytes) -> CADU(259 bytes)

RF Link statistics:
  Delivered intact:  16 frames
  Dropped (lost):    1 frames
  Corrupted:         2 frames

One frame vanished. Two arrived with bit errors.

What the ground makes of it

Ground Station: processing received CADUs...

  [RS OK] Corrected 1 symbol errors
  [GAP] MC counter gap: 1 frame(s) lost
  [RS OK] Corrected 2 symbol errors

Frame reception summary:
  Good frames accepted:  18 / 19 transmitted
  RS corrections:        3 symbols across all frames
  RS failures:           0 (uncorrectable, >16 errors)
  CRC rejects:           0
  MC frame gaps:         1 (frames lost in transit)

Both corrupted frames were repaired, not discarded. Reed-Solomon fixed three bad symbols across them and handed up clean frames. The CRC never had to reject anything.

The dropped frame is a different problem. Nothing can fix a frame that did not arrive, but the Master Channel Frame Count jumps, so the receiver knows exactly that one is gone.

Getting the packet stream back

Packet recovery (FHP-based resync after gaps):
  Recovered packet: APID=100 Seq=0 Size=10 bytes
  ...
  Recovered packet: APID=100 Seq=8 Size=115 bytes
  Recovered packet: APID=100 Seq=11 Size=35 bytes
  ...

Sequence 8, then 11. Packets 9 and 10 were inside the frame that vanished.

The receiver does not stall on them. It throws away the partial packet it was accumulating, waits for the next frame with a usable First Header Pointer, and starts reading from that offset. That is the whole resync mechanism.

=== Results ===
  Packets sent:       20
  Packets recovered:  18 (90%)
  Packets lost:       2 (spanned a dropped/corrupted frame)
  CRC failures:       0 (partial packet from lost frame)

One lost frame cost exactly the packets that lived in it. Everything after resynchronized cleanly.

Why each layer is needed

Why not just retransmit? The round trip is minutes to hours. Asking again does not finish in time. Everything on a downlink is forward error correction and graceful degradation.

Why RS and a CRC? RS corrects up to 16 bad symbols per codeword. Past that it can fail loudly, or (worse) "correct" into wrong data. The frame CRC is the independent check that catches both cases.

Why two counters? The Master Channel count says the spacecraft lost a frame. The Virtual Channel count says this channel lost one. An MC gap with no VC gap means the missing frame belonged to another channel, and your stream is intact.

Why does the FHP matter so much? Frames are fixed length, packets are not, so packets span. After losing a frame the receiver holds a fragment of a packet it can never complete. Without the FHP it would have no idea where the next real packet header starts, and the stream would be dead. With it, recovery costs exactly the packets that were in the lost frame.

Tuning it

Interleaving depth spreads a burst error across codewords. Depth 5 with RS(255,223) means a 30-byte burst becomes 6 bytes in each of five codewords, all correctable, where one codeword alone would have failed.

RS(255,223) or RS(255,239). The first corrects 16 symbols and costs 32 bytes of parity per codeword. The second corrects 8 and costs 16. Deep space takes the stronger code; a good LEO link often does not need it.

Frame length trades overhead against loss granularity. Shorter frames lose less when one goes missing, but pay the header and parity cost more often.

Things that will bite you

Reed-Solomon can "correct" into wrong data. Past its correction capacity it either fails loudly or produces a clean-looking codeword that is not what was sent. That second case is the reason the frame CRC exists as an independent check, and it is why RS failures: 0 is not on its own evidence that the data is good.

A corrupted frame is counted twice. It shows up as a CRC reject and then again as a counter gap, because the frame it would have been is missing from the sequence. Adding the two together double-counts the loss.

An MC gap with no VC gap is not your problem. It means the missing frame belonged to a different virtual channel. Alarming on the master channel count alone produces alerts for streams you are not watching.

The FHP is per frame, not per packet. After a loss the receiver waits for the next frame carrying a packet start. A run of frames in the middle of one large packet carries no usable pointer, so recovery can take several frames longer than the one that was lost.

Randomization is not error correction. It keeps the receiver's clock locked, which prevents errors rather than fixing them, and getting it wrong on one end produces garbage that Reed-Solomon cannot rescue. The TC randomizer is not the TM randomizer.

Next