File rs_codec_core.h¶
FileList > inc > rs_codec > rs_codec_core.h
Go to the source code of this file
The Reed-Solomon codec, as an object over rs .More...
#include "clib_common.h"#include "jm_perf.h"#include "rs/rs_core.h"
Classes¶
| Type | Name |
|---|---|
| struct | rs_codec_state_t A code and the tables derived from it. |
Public Functions¶
| Type | Name |
|---|---|
| int | rs_codec_codeword_ok (rs_codec_state_t * state, const uint8_t * codeword, size_t codeword_len) Is this a valid codeword? — every syndrome zero. |
| rs_codec_state_t * | rs_codec_create (uint32_t nroots, uint32_t symbol_bits, uint32_t field_poly, uint32_t first_root, uint32_t root_stride) Create a codec for the code named by the five arguments. |
| int | rs_codec_decode (rs_codec_state_t * state, uint8_t * codeword, size_t codeword_len) Correct up to E symbol errors, IN PLACE. |
| void | rs_codec_destroy (rs_codec_state_t * state) Destroy a codec and release all memory. |
| size_t | rs_codec_encode (rs_codec_state_t * state, const uint8_t * in, size_t n_in, uint8_t * out, size_t max_out) Encode k information symbols into a wholen -symbol codeword. |
| size_t | rs_codec_encode_max_out (rs_codec_state_t * state, size_t n_in) Symbols rs_codec_encode writes for n_in information symbols: a whole codeword,n . |
| size_t | rs_codec_generator (rs_codec_state_t * state, uint8_t * out, size_t out_len) The nroots + 1 coefficients ofg(x) ,out[i] forx^i . |
| size_t | rs_codec_get_e (const rs_codec_state_t * state) Correctable symbols per codeword, nroots / 2 . |
| size_t | rs_codec_get_k (const rs_codec_state_t * state) Information symbols per codeword, n - nroots . |
| size_t | rs_codec_get_n (const rs_codec_state_t * state) Symbols per codeword, 2^J - 1 . |
| size_t | rs_codec_get_nroots (const rs_codec_state_t * state) Parity symbols per codeword, 2E . |
| size_t | rs_codec_get_symbol_bits (const rs_codec_state_t * state) Symbol width J , in bits. |
| size_t | rs_codec_syndromes (rs_codec_state_t * state, const uint8_t * in, size_t n_in, uint8_t * out, size_t max_out) The nroots syndromes of ann -symbol word. |
| size_t | rs_codec_syndromes_max_out (rs_codec_state_t * state, size_t n_in) Syndromes rs_codec_syndromes writes: nroots . |
Detailed Description¶
rs owns the CODE — the field, the derived tables, the systematic encoder, the syndromes and the Berlekamp-Massey / Chien / Forney decoder, for any RS code over GF(2^J). This owns the OBJECT built over one: the five numbers that name a code, bound to the tables derived from them, so a caller cannot pair the wrong two.
It is not a second implementation. Every function here calls the matching rs_* kernel. Two Reed-Solomon implementations for one code family is how a root offset or a basis convention comes to differ between them — and both of those are invisible to a round trip, because a matched decoder inverts whatever the encoder did.
Why this exists at all¶
rs_encode, rs_syndromes and rs_codeword_ok were reachable from Python only through wfm_frame_desc_t's Reed-Solomon stage, which binds to ccsds_tm_frame_ops and carries an interleaving depth rather than a code. So Python could run exactly ONE Reed-Solomon code — CCSDS's — and only inside a frame (doppler#900).
Matching the algebra is not matching the wire¶
The five numbers here are the CODE. A standard adds conventions that are not properties of it, and CCSDS adds two: symbols travel in the dual (Berlekamp) basis (131.0-B 4.3.9) and codewords are interleaved (4.4.1). ccsds_tm/ccsds_tm_rs.h holds both. Construct this with CCSDS's five numbers and the arithmetic is right and the wire format is not; a conventional-basis codeword is self-consistent and matches no spacecraft.
Conventions, inherited from <tt>rs</tt>¶
- Symbols are packed, one per byte — an RS symbol is a byte at
J = 8. This differs fromconv_encand the randomiser, which take unpacked bits; the boundary belongs to the frame assembler. - A codeword is
kinformation symbols thennrootsparity, index 0 first on the wire. nis2^J - 1by construction, so a SHORTENED code is not expressible — DVB's RS(204,188) and CCSDS 4.4.2's shortened codeblock are the full codes with leading zeros the sender never transmits, and that virtual fill is gh-813.
Lifecycle: create -> [encode / decode / syndromes / codeword_ok]* -> destroy.
See also: rs/rs_core.h for the code description and every kernel.
See also: ccsds_tm/ccsds_tm_rs.h for CCSDS's configuration and its two conventions.
See also: docs/design/reed-solomon.md for the algebra.
Public Functions Documentation¶
function rs_codec_codeword_ok¶
Is this a valid codeword? — every syndrome zero.
int rs_codec_codeword_ok (
rs_codec_state_t * state,
const uint8_t * codeword,
size_t codeword_len
)
Parameters:
stateThe codec.codewordnsymbols.codeword_lenNumber of symbols incodeword.
Returns:
1 when every syndrome is zero, 0 otherwise — including when codeword_len is not n, since a word of the wrong length is not a codeword of this code.
>>> import numpy as np
>>> from doppler.coding import ReedSolomon
>>> rs = ReedSolomon(nroots=32)
>>> rs.codeword_ok(np.zeros(rs.n, np.uint8)) # all-zero IS a codeword
1
>>> rs.codeword_ok(np.zeros(rs.n - 1, np.uint8)) # at the right size
0
function rs_codec_create¶
Create a codec for the code named by the five arguments.
rs_codec_state_t * rs_codec_create (
uint32_t nroots,
uint32_t symbol_bits,
uint32_t field_poly,
uint32_t first_root,
uint32_t root_stride
)
Two of them are VALIDATED rather than trusted, because both produce arithmetic that is entirely self-consistent — a round trip against a matching encoder cannot see either: field_poly must be primitive, and root_stride must be coprime with n, or the nroots roots are not distinct and the code corrects fewer errors than its parity count claims.
Parameters:
nrootsParity symbols2E; even, >= 2, leavingk >= 1.symbol_bitsJ, 2..8.field_polyF(x), lowJbits,x^Jimplicit; PRIMITIVE.first_rootj0: the first root isa^(root_stride * j0).root_strides; coprime withn.
Returns:
Heap-allocated state, or NULL if the five do not name a usable code.
Note:
Caller must call rs_codec_destroy() when done.
>>> from doppler.coding import ReedSolomon
>>> rs = ReedSolomon(nroots=32) # RS(255,223) over the usual GF(256)
>>> rs.n, rs.k, rs.e
(255, 223, 16)
>>> ReedSolomon(nroots=4, symbol_bits=4, field_poly=0b0011).n
15
function rs_codec_decode¶
Correct up to E symbol errors, IN PLACE.
rs_decode, over the caller's own buffer: the corrected symbols land in codeword itself, which is why the binding demands a writable array rather than quietly working on a copy the caller would then discard.
It either refuses or leaves a codeword. On success the key equation has zeroed every syndrome by construction, so the result passes rs_codec_codeword_ok. On refusal codeword is untouched.
A refusal is not the same claim as "more than E errors". Beyond E a bounded-distance decoder can land inside another codeword's sphere and miscorrect — a property of the code, not of this implementation — which is why this reports a COUNT rather than a verdict, and why frame-level accounting is the protection.
Parameters:
stateThe codec.codewordnsymbols, corrected in place.codeword_lenNumber of symbols incodeword.
Returns:
Symbols corrected, 0 for an already-valid codeword, -1 when the word is too far from every codeword to name one, or -2 when codeword_len is not n. Two negative codes rather than one because they are different kinds of fact: -1 is the channel's answer and -2 is the caller's mistake.
>>> import numpy as np
>>> from doppler.coding import ReedSolomon
>>> rs = ReedSolomon(nroots=32)
>>> word = rs.encode(np.arange(rs.k, dtype=np.uint8))
>>> word[3] ^= 0xFF # one symbol, however many bits it moved
>>> word[40] ^= 0x01
>>> rs.decode(word) # corrected in place
2
>>> bool(np.array_equal(word[: rs.k], np.arange(rs.k, dtype=np.uint8)))
True
function rs_codec_destroy¶
Destroy a codec and release all memory.
Parameters:
stateMay be NULL.
function rs_codec_encode¶
Encode k information symbols into a wholen -symbol codeword.
size_t rs_codec_encode (
rs_codec_state_t * state,
const uint8_t * in,
size_t n_in,
uint8_t * out,
size_t max_out
)
Systematic: the information symbols are copied through untouched and the nroots parity symbols follow them, which is the order they are transmitted in. rs_encode computes the parity; this places it.
The WHOLE codeword rather than the parity alone, because that is the unit every other method here takes — rs_codec_decode, rs_codec_syndromes and rs_codec_codeword_ok all read n symbols, and a caller who wants the parity by itself can take the last nroots of the answer. (rs_encode is the other split, and is still there for a frame assembler that has already placed the information.)
out may alias in — rs_codec_encode (rs, buf, k, buf, n) appends the parity to a buffer that already holds the information, which is the call a frame assembler makes and the one rs_encode exists for.
Parameters:
stateThe codec.inExactlykinformation symbols.n_inNumber of symbols inin.outReceivesnsymbols; may bein.max_outCapacity ofout.
Returns:
n on success, or 0 if n_in is not exactly k or out is too small — refusing rather than truncating, since a short codeword is not a codeword.
>>> import numpy as np
>>> from doppler.coding import ReedSolomon
>>> rs = ReedSolomon(nroots=32)
>>> info = np.arange(rs.k, dtype=np.uint8)
>>> word = rs.encode(info)
>>> word.size, bool(np.array_equal(word[: rs.k], info))
(255, True)
>>> rs.codeword_ok(word)
1
function rs_codec_encode_max_out¶
Symbols rs_codec_encode writes forn_in information symbols: a whole codeword,n .
function rs_codec_generator¶
The nroots + 1 coefficients ofg(x) ,out[i] forx^i .
Exposed because standards PUBLISH them — CCSDS 131.0-B Annex G prints all 33 for E = 16 — so a caller who has just configured a code from a document can check that they read the five numbers correctly, against the document rather than against this implementation.
The caller supplies the buffer rather than being handed one, because the length is a property of the CODE and not of the call: g(x) has exactly nroots + 1 coefficients and there is no other number a caller could ask for. A self-sizing method would carry a count parameter that means nothing, which is a worse trade than one line of allocation.
Parameters:
stateThe codec.outReceivesnroots + 1coefficients;out[i]is the coefficient ofx^i, soout[nroots]is 1.out_lenLength ofout; fewer thannroots + 1writes nothing.
Returns:
nroots + 1, or 0 if out is too small.
>>> import numpy as np
>>> from doppler.coding import ReedSolomon
>>> rs = ReedSolomon(nroots=32, field_poly=0x87, first_root=112,
... root_stride=11) # CCSDS 131.0-B 4.3
>>> g = np.empty(rs.nroots + 1, np.uint8)
>>> rs.generator(g) # Annex G prints all 33
33
>>> int(g[0]), int(g[-1])
(1, 1)
function rs_codec_get_e¶
Correctable symbols per codeword, nroots / 2 .
function rs_codec_get_k¶
Information symbols per codeword, n - nroots .
function rs_codec_get_n¶
Symbols per codeword, 2^J - 1 .
function rs_codec_get_nroots¶
Parity symbols per codeword, 2E .
function rs_codec_get_symbol_bits¶
Symbol width J , in bits.
function rs_codec_syndromes¶
The nroots syndromes of ann -symbol word.
size_t rs_codec_syndromes (
rs_codec_state_t * state,
const uint8_t * in,
size_t n_in,
uint8_t * out,
size_t max_out
)
All zero is the DEFINING property of the code: it needs no encoder and no decoder to check, which is what makes it usable both as a test oracle and as a receiver's error detector. rs_codec_codeword_ok is this reduced to the one bit most callers want.
Parameters:
stateThe codec.innsymbols.n_inNumber of symbols inin.outReceivesnrootssyndromes.max_outCapacity ofout.
Returns:
nroots, or 0 if n_in is not n or out is too small.
>>> import numpy as np
>>> from doppler.coding import ReedSolomon
>>> rs = ReedSolomon(nroots=32)
>>> word = rs.encode(np.zeros(rs.k, dtype=np.uint8))
>>> bool(rs.syndromes(word).any()) # a codeword has none
False
>>> word[7] ^= 0x20
>>> bool(rs.syndromes(word).any())
True
function rs_codec_syndromes_max_out¶
Syndromes rs_codec_syndromes writes:nroots .
The documentation for this class was generated from the following file native/inc/rs_codec/rs_codec_core.h