ektologo

Understanding CCSDS Convolutional Coding and Puncturing Patterns

by [gero]

Over the past few weeks, I’ve been digging into something that might help anyone working with radios that follow the CCSDS standard.

For those who are not familiar with it, CCSDS is a well-known standard in the space industry, developed by NASA and international agencies, defining modulation and coding schemes for space communications.

Reference: CCSDS 131.0-B-3 Telemetry Channel Coding

One of the recommendations in CCSDS is to use a convolutional code for encoding transmitted data. On the receiver side, we decode this using the inverse algorithm — typically a Viterbi decoder. While other algorithms exist, Viterbi is widely used due to its efficiency & performance.


### Convolutional Code Parameters

A convolutional code is defined by a few key parameters. For CCSDS, these values are standardized (Section 3 of the CCSDS document):

### Using GNU Radio

I usually rely on GNU Radio’s built-in FEC blocks, which make most of the work straightforward.

For the encoder:

fec_rate = 2  # equivalent to 1/2
G1= 109
G2 = 79
constraint_length = 7

self.cc_encoder = fec.cc_encoder_make(
    128, constraint_length, fec_rate, [G1, G2], 0, fec.CC_STREAMING, False
)

self.fec_encoder = fec.extended_encoder(
    encoder_obj_list=self.cc_encoder,
    threading="capillary",
    puncpat="11",   # no puncturing (rate 1/2)
)

Once this block is connected in your chain (mind the input/output types), you are ready to go and your packet will be convolutional encoded.

Disclaimer: As it can be seen, there are some extra parameters to configure, but most rarely change and they are not relevant for this topic so I will ignore them.

For the decoder, we simply mirror the same configuration:

self.cc_decoder = fec.cc_decoder_make(
    128, constraint_length, fec_rate, [G1, G2], 0, -1, fec.CC_STREAMING, False
)

self.fec_decoder = fec.extended_decoder(
    decoder_obj_list=self.cc_decoder,
    threading="capillary",
    ann=None,
    puncpat="11",   # no puncturing (rate 1/2)
    integration_period=10000
)

Up to this point, nothing surprising. This setup is described in many forums and examples.

But the question I was trying to answer these last days was…

### What if we want to use a different rate, like 5/6?

This is not very common — and therefore not well documented online (or at least I did not find much information).

GNU Radio’s cc_encoder and cc_decoder officially only support rate = 1/2. So at first I was tempted to use a totally different approach... but after digging a bit deeper, I realized the important detail:

Even if someone says the code rate is 5/6, the convolutional encoder still works at 1/2.

So… how does that make sense?

### Why change the code rate at all?

Well, the "problem" of 1/2 is that you are sending twice the bits that you originally had, so you are wasting half of the bandwidth. The whole idea of using a different rate is to optimize the bandwidth and send less parity bytes (of course the link will be less robust.)

### How can we do this and use convolutional encoder 1/2 at the same time?

There is a trick or better said, a technique called puncturing.

### What is Puncturing?

As defined in CCSDS 131.0-B-3, Section 3.4, puncturing allows the system to increase data rate by selectively removing parity bits while transmitting.

The encoder still generates two output bits per input bit (rate 1/2). Then, before transmission, it applies a puncturing pattern to decide which bits will be transmitted, and which not.

This pattern is repeated continuously for the whole packet and effectively changes the code rate. Of course you need to share the same pattern with the demodulator to be able to retrieve the original signal.

Luckily for us, the gnuradio FEC blocks already take care of this, so it is easy to implement (now that we know the trick). If we go back to the piece of code we show before, we can notice that one of the argument we avoid explaining was the one called puncpat, that as you may imagine is the punctured pattern that we can configure.

By default it is set to in "11" that according to gnu-radio documentation, means that no puncturing is applied, so the rate is 1/2.

I magically found in a GNU Radio Maillist thread the rest of the possible patterns for CCSDS standard.

Code RatePattern (binary)
1/211
2/31101
3/4110110
5/61101100110
7/811010101100110

### In short:

Using a convolutional encoder with a rate different from 1/2 is simply a matter of selecting the correct puncturing pattern, both in the encoder and decoder.