···82828383(* {1 Reed-Solomon}
84848585- CCSDS RS(255,223) with interleaving.
8585+ CCSDS RS(255,223) with dual-basis representation and interleaving.
8686+8787+ Per CCSDS 131.0-B-4 Annex F, the code operates in the Berlekamp dual
8888+ basis. Data symbols are converted from conventional to dual basis before
8989+ encoding, and parity symbols are converted back from dual to conventional
9090+ after encoding. Decoding reverses the process.
9191+9292+ The RS code parameters in the conventional basis:
9393+ - Field polynomial: 0x187 (x^8 + x^7 + x^2 + x + 1)
9494+ - Primitive element: alpha^11 = 173 (in conventional representation)
9595+ - First consecutive root: 112
9696+ - Error correction capability: t = 16
86978798 For interleave depth I:
8899 - Encode: split the input (223*I bytes) into I sub-streams via round-robin
···103114 | I5 -> 5
104115 | I8 -> 8
105116106106- let rs = Reed_solomon.ccsds
117117+ (* CCSDS RS uses alpha^11 = 173 as the primitive element in the
118118+ conventional representation. The generic reed_solomon library uses
119119+ alpha=11, so we override the primitive_element here. *)
120120+ let rs = { Reed_solomon.ccsds with primitive_element = 173 }
121121+122122+ (* {2 Dual-basis transformation}
123123+124124+ CCSDS 131.0-B-4 Annex F2 defines two 8x8 binary matrices for converting
125125+ between conventional binary representation and Berlekamp dual basis:
126126+ - T_inv (conventional -> dual): applied to data before encoding
127127+ - T_str (dual -> conventional): applied to parity after encoding
128128+129129+ Each matrix is stored column-wise: entry [i] is a bitmask where bit j
130130+ (MSB = bit 7) indicates row j of column i. Matrix-vector multiplication
131131+ is computed as XOR of selected columns via bitwise AND + popcount. *)
132132+133133+ let straight_t = [| 0xFE; 0x69; 0x6B; 0x0D; 0xEF; 0xF2; 0x5B; 0xC7 |]
134134+ let inverted_t = [| 0x9B; 0xDD; 0x3E; 0x1C; 0x37; 0xB3; 0x60; 0x94 |]
135135+136136+ (* Population count mod 2 for a byte: 1 if odd number of set bits. *)
137137+ let parity8 x =
138138+ let x = x lxor (x lsr 4) in
139139+ let x = x lxor (x lsr 2) in
140140+ x lxor (x lsr 1) land 1
141141+142142+ let matrix_mul_byte matrix x =
143143+ let result = ref 0 in
144144+ for i = 0 to 7 do
145145+ let product = x land matrix.(i) land 0xFF in
146146+ if parity8 product = 1 then result := !result lor (1 lsl (7 - i))
147147+ done;
148148+ !result
149149+150150+ (* Pre-compute 256-entry lookup tables for fast byte-level conversion. *)
151151+ let make_lut matrix = Array.init 256 (matrix_mul_byte matrix)
152152+ let conv_to_dual = make_lut inverted_t
153153+ let dual_to_conv = make_lut straight_t
154154+155155+ let transform_bytes lut b =
156156+ Bytes.init (Bytes.length b) (fun i ->
157157+ Char.chr lut.(Char.code (Bytes.get b i)))
158158+159159+ (* Encode a single 223-byte block: conv data -> dual -> RS -> dual parity -> conv *)
160160+ let encode_block data =
161161+ let data_dual = transform_bytes conv_to_dual data in
162162+ let cw = Reed_solomon.encode_systematic rs data_dual in
163163+ (* Build output: original (conventional) data + conventional parity *)
164164+ let out = Bytes.make rs.n '\x00' in
165165+ Bytes.blit data 0 out 0 rs.k;
166166+ for i = 0 to (2 * rs.t) - 1 do
167167+ Bytes.set out (rs.k + i)
168168+ (Char.chr dual_to_conv.(Char.code (Bytes.get cw (rs.k + i))))
169169+ done;
170170+ out
171171+172172+ (* Decode a single 255-byte block: conv cw -> dual -> RS decode -> dual data -> conv *)
173173+ let decode_block cw =
174174+ let cw_dual = transform_bytes conv_to_dual cw in
175175+ match Reed_solomon.decode rs cw_dual with
176176+ | Ok data_dual -> Ok (transform_bytes dual_to_conv data_dual)
177177+ | Error e -> Error (Fmt.str "%a" Reed_solomon.pp_error e)
107178108179 let encode ?(interleave = I1) data =
109180 let depth = interleave_depth interleave in
···112183 invalid_arg
113184 (Fmt.str "Reed_solomon.encode: expected %d bytes (223*%d), got %d"
114185 data_len depth (Bytes.length data));
115115- if depth = 1 then Reed_solomon.encode_systematic rs data
186186+ if depth = 1 then encode_block data
116187 else begin
117188 (* De-interleave input into [depth] sub-streams of 223 bytes each *)
118189 let subs = Array.init depth (fun _ -> Bytes.make rs.k '\x00') in
···122193 Bytes.set subs.(sub_idx) byte_idx (Bytes.get data i)
123194 done;
124195 (* Encode each sub-stream *)
125125- let coded = Array.map (Reed_solomon.encode_systematic rs) subs in
196196+ let coded = Array.map encode_block subs in
126197 (* Interleave the [depth] codewords of 255 bytes each *)
127198 let out_len = rs.n * depth in
128199 let out = Bytes.make out_len '\x00' in
···141212 Error
142213 (Fmt.str "Reed_solomon.decode: expected %d bytes (255*%d), got %d"
143214 code_len depth (Bytes.length codeword))
144144- else if depth = 1 then
145145- match Reed_solomon.decode rs codeword with
146146- | Ok data -> Ok data
147147- | Error e -> Error (Fmt.str "%a" Reed_solomon.pp_error e)
215215+ else if depth = 1 then decode_block codeword
148216 else begin
149217 (* De-interleave into [depth] sub-streams of 255 bytes each *)
150218 let subs = Array.init depth (fun _ -> Bytes.make rs.n '\x00') in
···154222 done
155223 done;
156224 (* Decode each sub-stream *)
157157- let results = Array.map (Reed_solomon.decode rs) subs in
225225+ let results = Array.map decode_block subs in
158226 (* Check for errors *)
159227 let err =
160228 Array.fold_left
161229 (fun acc r ->
162230 match acc with
163231 | Some _ -> acc
164164- | None -> (
165165- match r with
166166- | Ok _ -> None
167167- | Error e -> Some (Fmt.str "%a" Reed_solomon.pp_error e)))
232232+ | None -> ( match r with Ok _ -> None | Error e -> Some e))
168233 None results
169234 in
170235 match err with
+6
lib/coding.mli
···6363 of correcting up to 16 symbol errors per codeword. Interleaving depths of 1,
6464 2, 3, 4, 5, and 8 are defined.
65656666+ This implementation uses the Berlekamp dual-basis representation specified
6767+ in CCSDS 131.0-B-4 Annex F. Data symbols are transparently converted between
6868+ conventional binary and dual-basis representation during encoding and
6969+ decoding. The output codeword has data in conventional representation
7070+ followed by parity in conventional representation.
7171+6672 For interleave depth I, the encoder takes [223*I] data bytes, splits them
6773 round-robin into I sub-streams, encodes each with RS(255,223), and
6874 interleaves the I codewords into [255*I] output bytes.