Skip to main content

LDPC

The Problem

A satellite sends 1024 bits of telemetry to Earth. The channel is noisy — individual bits can flip with some probability. We want the receiver to correct as many flipped bits as possible, without retransmission.

Reed-Solomon (covered in Reed-Solomon) corrects byte errors and works best against short bursts. LDPC codes work at the bit level and are designed for channels where errors are spread randomly across the frame. They get closer to the theoretical Shannon limit — meaning they can correct more errors for the same amount of redundancy.

The Idea

Instead of building a polynomial over a Galois field (like RS), LDPC uses a large, sparse parity-check matrix HH. Each row of HH defines one parity-check equation: the XOR of a small subset of codeword bits must equal zero. If the codeword is valid, every equation is satisfied. If bits are flipped, some equations fail, and the pattern of failures reveals which bits are wrong.

"Low-density" means most entries in HH are zero — each check involves only a few bits, and each bit participates in only a few checks. This sparsity is what makes decoding tractable: instead of solving a huge linear system, the decoder passes messages between checks and bits along the edges of a sparse graph.

Parity-Check Matrix

A codeword c\mathbf{c} of nn bits is valid if and only if:

HcT=0(mod2)H \cdot \mathbf{c}^T = \mathbf{0} \pmod{2}

For a code with kk information bits and nn transmitted bits, HH has (nk)(n - k) rows and nn columns (plus some extra columns for punctured bits — more on that later). Each row is one check equation. Each column corresponds to one bit.

Example: A Tiny Code

Consider a toy (7,4)(7, 4) code with 4 info bits and 3 checks:

H=(110110001110101011001)H = \begin{pmatrix} 1 & 1 & 0 & 1 & 1 & 0 & 0 \\ 0 & 1 & 1 & 1 & 0 & 1 & 0 \\ 1 & 0 & 1 & 1 & 0 & 0 & 1 \end{pmatrix}

Row 0 says: c0c1c3c4=0c_0 \oplus c_1 \oplus c_3 \oplus c_4 = 0. If the received bits satisfy all three equations, the codeword is valid. If not, the decoder uses the failed checks to figure out which bits flipped.

AR4JA: The CCSDS LDPC Code

CCSDS 131.0-B-5 specifies a family of Accumulate-Repeat-4-Jagged-Accumulate (AR4JA) codes for telemetry. They come in three rates:

RatekknnParityPunctured
1/2102420481024512
2/310241536512256
4/510241280256128

The k=4096k = 4096 variants use the same structure with larger matrices.

The code is systematic: the first kk bits of the codeword are the original data, unchanged. Only parity bits are appended.

Block Structure

The HH matrix is not stored as a dense array of bits. Instead it is defined as a grid of M×MM \times M sub-matrix blocks, where MM is the submatrix size (e.g. 512 for rate 1/2 with k=1024k = 1024).

For rate 1/2, the base matrix has 3 block-rows and 5 block-columns. Each block is one of three types:

  • Zero block (HZH_Z): an M×MM \times M all-zeros matrix — this check doesn't connect to any bit in this column.
  • Identity block (HIH_I): the M×MM \times M identity matrix — check ii connects to bit ii in this column.
  • Permutation block (HPkH_{P_k}): an M×MM \times M matrix defined by the πk\pi_k permutation — check ii connects to bit πk(i)\pi_k(i).

The base matrices for the three rates are:

Rate 1/2 (3 x 5 blocks):

Col 0Col 1Col 2Col 3Col 4
Row 0HZH_ZHZH_ZHIH_IHZH_ZHI+HP0H_I + H_{P_0}
Row 1HIH_IHIH_IHZH_ZHIH_IHP1+HP2+HP3H_{P_1} + H_{P_2} + H_{P_3}
Row 2HIH_IHP4+HP5H_{P_4} + H_{P_5}HZH_ZHP6+HP7H_{P_6} + H_{P_7}HIH_I

Each HPkH_{P_k} uses a different permutation. The + denotes XOR (mod-2 addition of matrices), so HI+HP0H_I + H_{P_0} means one matrix where row ii has 1s at both column ii (identity) and column π0(i)\pi_0(i) (permutation).

The last column (column 4 for rate 1/2) corresponds to punctured bits: they are part of the code's internal structure but are not transmitted. The transmitted codeword comes from columns 0--3.

The πk\pi_k Permutation

Each permutation πk\pi_k maps row position ii (in 0M10 \ldots M-1) to a column position within the same block. It is defined by two lookup tables, θ\theta and ϕ\phi, from the CCSDS standard:

πk(i)=M4((θk+4i/M)mod4)+(ϕ4i/M,k+i)modM4\pi_k(i) = \frac{M}{4} \cdot ((\theta_k + \lfloor 4i / M \rfloor) \bmod 4) + (\phi_{\lfloor 4i/M \rfloor, k} + i) \bmod \frac{M}{4}

The formula splits MM into 4 quadrants. θk\theta_k rotates which quadrant the output lands in; ϕj,k\phi_{j,k} offsets within the quadrant. Together they produce a bijection (every output appears exactly once), which is essential for HH to have full rank.

For M=512M = 512 and k=0k = 0: θ0=3\theta_0 = 3, ϕ0,0=16\phi_{0,0} = 16. So:

π0(0)=128((3+0)mod4)+(16+0)mod128=384+16=400\pi_0(0) = 128 \cdot ((3 + 0) \bmod 4) + (16 + 0) \bmod 128 = 384 + 16 = 400

π0(200)=128((3+1)mod4)+(16+200)mod128=0+88=88\pi_0(200) = 128 \cdot ((3 + 1) \bmod 4) + (16 + 200) \bmod 128 = 0 + 88 = 88

The CCSDS standard defines 26 permutations (k=025k = 0 \ldots 25). How many are used depends on the rate: rate 1/2 uses k=07k = 0 \ldots 7, rate 2/3 uses k=013k = 0 \ldots 13, rate 4/5 uses k=025k = 0 \ldots 25.

Encoding

Encoding computes the parity bits from the information bits. CCSDS AR4JA codes use a precomputed compact generator matrix PP such that:

parity=info×P(mod2)\text{parity} = \text{info} \times P \pmod{2}

The codeword is then [infoparity][\text{info} | \text{parity}], with kk info bits followed by nkn - k parity bits.

Circulant Structure

The matrix PP has dimensions k×(nk)k \times (n - k). Storing it directly would require 1024×1024=1310721024 \times 1024 = 131072 bits for rate 1/2. Instead, PP is decomposed into circulant blocks of size b×bb \times b, where bb is the circulant size (b=M/4b = M/4).

A circulant block is fully defined by its first row — every subsequent row is the previous row rotated right by one position. So we only store one row per block:

Pcompact=k/b rows×(nk)/64 u64 values per rowP_\text{compact} = k/b \text{ rows} \times (n-k)/64 \text{ u64 values per row}

For rate 1/2: 1024/128=81024 / 128 = 8 rows of 1024/64=161024 / 64 = 16 u64 each =128= 128 u64 values total (1024 bytes to represent a million-bit matrix).

Encoding Algorithm

The encoder processes info bits in groups aligned to the circulant structure:

  1. For each rotation offset o=0,1,,b1o = 0, 1, \ldots, b - 1:
    1. For each compact row r=0,1,,k/b1r = 0, 1, \ldots, k/b - 1:
      • The info bit at position rb+or \cdot b + o: if it is 1, XOR the compact generator row rr into the parity accumulator.
    2. Left-rotate each parity block by 1 bit (simulating the circulant shift for the next offset).

After bb rotations, every info bit has contributed its generator row at the correct rotation, and the parity accumulator holds the final parity bits.

Syndrome Check

To verify a received codeword, we check HcT=0H \cdot \mathbf{c}^T = \mathbf{0}. But the transmitted codeword has nn bits, while HH has n+Mn + M columns (the extra MM are the punctured bits). The punctured bits are not transmitted, so they must be recovered first.

Recovering Punctured Bits

For all AR4JA rates, block-row 2 of the punctured column (the last column) is a pure identity block. This means check equation (2,i)(2, i) connects to punctured bit ii via identity, plus some transmitted bits via other columns. So:

punctured[i]=all other connections in row 2, pos icodeword[bit]\text{punctured}[i] = \bigoplus_{\text{all other connections in row 2, pos } i} \text{codeword}[\text{bit}]

We compute this for every i0M1i \in 0 \ldots M - 1, giving us all MM punctured bits.

Verifying Rows 0 and 1

With the punctured bits recovered, we check rows 0 and 1 of HH. For each check position, XOR all connected codeword bits (from the transmitted columns) and all connected punctured bits (from the recovered column). If every check evaluates to zero, the codeword is valid.

Row 2 is not checked separately — it was used to define the punctured bits, so it passes by construction. Row 3 of the base matrix is all zeros and contributes no checks.

Decoding: Belief Propagation

Not yet implemented. The decoder will use layered min-sum belief propagation with fixed-point (i16) log-likelihood ratios.

The idea: each bit starts with a "soft" confidence value from the channel (positive = probably 0, negative = probably 1). The decoder iteratively passes messages between check nodes and variable nodes:

  1. Variable -> Check: each bit tells each of its checks "here's my current belief, excluding what you told me last time."
  2. Check -> Variable: each check combines messages from all its other bits and sends back "based on everyone else, I think you should be..."

After enough iterations (typically 20--50), the bits converge to their corrected values. The min-sum variant approximates the optimal sum-product algorithm using only additions and comparisons — no multiplications or transcendental functions — making it suitable for embedded and FPGA implementations.

Why These Numbers

Why three rates? Different missions have different channel conditions. A deep-space probe with a weak signal uses rate 1/2 (sends 2 bits for every 1 bit of data — maximum protection). A low-Earth-orbit satellite with a strong link uses rate 4/5 (sends only 1.25 bits per data bit — maximum throughput).

Why is a column punctured? The AR4JA code's internal structure needs an extra set of MM "accumulator seed" bits to make the encoder work. These bits are determined by the code constraints and don't carry independent information, so transmitting them would waste bandwidth. The decoder recovers them from the parity checks.

Why M=512M = 512 for rate 1/2? The base matrix has nb=5n_b = 5 columns. The transmitted codeword has (nb1)M=4M(n_b - 1) \cdot M = 4M bits. For n=2048n = 2048: M=2048/4=512M = 2048 / 4 = 512.

Why circulant size b=M/4b = M/4? The πk\pi_k permutation splits each MM-row block into 4 quadrants. The circulant structure of the generator aligns with these quadrants, so b=M/4b = M/4.

CCSDS Parameters

ParameterValue
StandardCCSDS 131.0-B-5
Code familyAR4JA (Accumulate-Repeat-4-Jagged-Accumulate)
Rates1/2, 2/3, 4/5
Info lengthsk=1024k = 1024 or k=4096k = 4096
Base matrix rows3 (plus 1 all-zero row)
Base matrix cols5 (r1/2), 7 (r2/3), 11 (r4/5)
Block typesHZH_Z (zero), HIH_I (identity), HPkH_{P_k} (permutation)
Permutationsπk\pi_k with θ\theta/ϕ\phi lookup tables
EncodingCompact generator with circulant rotation
DecodingLayered min-sum belief propagation

End-to-End Example

We encode 1024 bits using the rate 1/2 code (n=2048n = 2048), corrupt some bits, and verify detection.

1. Encoding

The 1024 info bits (128 bytes) are the input. For this example, we use the test vector info[i] = i for i=0127i = 0 \ldots 127:

Info (128 bytes):
[ 0x00, 0x01, 0x02, 0x03, ..., 0x7E, 0x7F ]

The encoder computes 1024 parity bits (128 bytes) using the compact generator matrix. The first few parity bytes are:

Parity (128 bytes):
[ 0xEE, 0xA9, 0xAA, 0xAF, 0x98, 0xD9, 0x16, 0xCE, ... ]

The codeword is systematic — info followed by parity:

Codeword (256 bytes = 2048 bits):
[ 0x00, 0x01, ..., 0x7F, 0xEE, 0xA9, 0xAA, 0xAF, ... ]
|---- 128 info bytes ---|---- 128 parity bytes ------|

2. Syndrome Check (Valid)

The syndrome checker first recovers the 512 punctured bits from row 2 of the HH matrix, then verifies rows 0 and 1. For this valid codeword, all 2×512=10242 \times 512 = 1024 check equations evaluate to zero.

3. Corruption

Flip bit 0 of the codeword (the MSB of the first byte):

Received:
[ 0x80, 0x01, 0x02, ..., 0x7F, 0xEE, 0xA9, ... ]
^^^^
was 0x00, now 0x80

4. Syndrome Check (Invalid)

The recovered punctured bits are now different (because the transmitted bits changed), and at least one check in rows 0 or 1 evaluates to 1. The syndrome check returns false — the codeword is corrupted.

5. Correction (Future)

A belief propagation decoder would take the received bits with soft channel information, run iterative message passing, and converge on the corrected codeword. This is not yet implemented.

Comparison with Reed-Solomon

Reed-SolomonLDPC (AR4JA)
Error modelByte errorsBit errors
ArithmeticGF(282^8) field opsBinary XOR only
MatrixDense (syndrome)Sparse (parity-check)
DecodingAlgebraic (BM + Forney)Iterative (belief prop.)
Best forBurst errorsRandom bit errors
Performance~2 dB from Shannon~0.5 dB from Shannon
ComplexityLow (255 symbols)Higher (thousands of bits)

In the CCSDS stack, both are used: RS protects the Transfer Frame layer, while LDPC provides the physical-layer FEC. They can be concatenated for extra robustness.