Reed-Solomon error correction over GF(2^8)
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

Upgrade to ocamlformat 0.29.0; fix csvt/sexpt streaming; reformat

- Update .ocamlformat to 0.29.0 across all 591 files
- csvt: reuse single Buffer.t for field reads (no alloc per field)
- sexpt: Obj members decoded from stream into Dict, typed Variant GADT
- Reformat all source files for 0.29.0

+20 -214
+1 -1
.ocamlformat
··· 1 - version = 0.28.1 1 + version = 0.29.0
+8 -8
lib/gf256.mli
··· 7 7 {2 Usage} 8 8 9 9 {[ 10 - (* CCSDS field: polynomial 0x187, primitive element 11 *) 11 - let module F = Gf256.Make (struct 12 - let primitive_poly = 0x187 13 - let primitive_element = 11 14 - end) in 15 - let product = F.mul 0xA3 0x7B in 16 - let inverse = F.inv 0xA3 in 17 - assert (F.mul product inverse = 0x7B) 10 + (* CCSDS field: polynomial 0x187, primitive element 11 *) 11 + let module F = Gf256.Make (struct 12 + let primitive_poly = 0x187 13 + let primitive_element = 11 14 + end) in 15 + let product = F.mul 0xA3 0x7B in 16 + let inverse = F.inv 0xA3 in 17 + assert (F.mul product inverse = 0x7B) 18 18 ]} 19 19 20 20 {2 References}
+11 -11
lib/reed_solomon.mli
··· 9 9 {2 Usage} 10 10 11 11 {[ 12 - let data = Bytes.of_string "Hello, Reed-Solomon!" in 13 - (* Pad to 223 bytes for CCSDS RS(255,223) *) 14 - let padded = Bytes.make 223 '\x00' in 15 - Bytes.blit data 0 padded 0 (Bytes.length data); 16 - let codeword = Reed_solomon.encode_systematic ccsds padded in 17 - (* Inject up to 16 symbol errors... *) 18 - Bytes.set codeword 0 '\xff'; 19 - Bytes.set codeword 42 '\xab'; 20 - match Reed_solomon.decode ccsds codeword with 21 - | Ok recovered -> assert (recovered = padded) 22 - | Error e -> Printf.eprintf "decode failed: %s\n" e 12 + let data = Bytes.of_string "Hello, Reed-Solomon!" in 13 + (* Pad to 223 bytes for CCSDS RS(255,223) *) 14 + let padded = Bytes.make 223 '\x00' in 15 + Bytes.blit data 0 padded 0 (Bytes.length data); 16 + let codeword = Reed_solomon.encode_systematic ccsds padded in 17 + (* Inject up to 16 symbol errors... *) 18 + Bytes.set codeword 0 '\xff'; 19 + Bytes.set codeword 42 '\xab'; 20 + match Reed_solomon.decode ccsds codeword with 21 + | Ok recovered -> assert (recovered = padded) 22 + | Error e -> Printf.eprintf "decode failed: %s\n" e 23 23 ]} 24 24 25 25 {2 References}
-24
test/interop/dune
··· 1 - (test 2 - (name test) 3 - (libraries reed_solomon alcotest) 4 - (deps 5 - (source_tree traces) 6 - (source_tree scripts))) 7 - 8 - (rule 9 - (alias regen-traces) 10 - (enabled_if 11 - (= %{env:REGEN_TRACES=0} 1)) 12 - (deps 13 - (source_tree scripts)) 14 - (action 15 - (with-stdout-to 16 - rs_vectors.hex 17 - (run python3 scripts/generate_rs.py)))) 18 - 19 - (rule 20 - (alias regen-traces) 21 - (enabled_if 22 - (= %{env:REGEN_TRACES=0} 1)) 23 - (action 24 - (diff traces/rs_vectors.hex rs_vectors.hex)))
-21
test/interop/regenerate.sh
··· 1 - #!/bin/bash 2 - # Regenerate Reed-Solomon interop test traces. 3 - # 4 - # Requires: pip install reedsolo 5 - # Usage: ./regenerate.sh 6 - # 7 - # Preferred method: REGEN_TRACES=1 dune build @regen-traces 8 - # Then: dune promote 9 - 10 - set -euo pipefail 11 - 12 - SCRIPT_DIR="$(cd "$(dirname "$0")/scripts" && pwd)" 13 - TRACE_DIR="$(cd "$(dirname "$0")/traces" && pwd)" 14 - 15 - echo "Scripts: $SCRIPT_DIR" 16 - echo "Traces: $TRACE_DIR" 17 - echo 18 - 19 - python3 "$SCRIPT_DIR/generate_rs.py" > "$TRACE_DIR/rs_vectors.hex" 20 - echo "Regenerated $TRACE_DIR/rs_vectors.hex" 21 - echo "Review with: git diff $TRACE_DIR"
-44
test/interop/scripts/generate_rs.py
··· 1 - #!/usr/bin/env python3 2 - """Generate Reed-Solomon test vectors using Python reedsolo. 3 - 4 - Outputs pipe-delimited lines: 5 - name|data_hex|codeword_hex 6 - 7 - Uses CCSDS RS(255,223) parameters: 8 - - Field polynomial: 0x187 (x^8 + x^7 + x^2 + x + 1) 9 - - Primitive element (alpha): 11 10 - - First consecutive root: 112 11 - - t = 16 (corrects up to 16 symbol errors) 12 - 13 - NOTE: reedsolo defaults to alpha=2 and fcr=0. The CCSDS standard uses 14 - alpha=11, primitive poly 0x187, and fcr=112. We configure reedsolo with 15 - these parameters explicitly. 16 - 17 - Requires: pip install reedsolo 18 - """ 19 - 20 - try: 21 - from reedsolo import RSCodec, init_tables 22 - except ImportError: 23 - print("ERROR: reedsolo not installed. Run: pip install reedsolo", flush=True) 24 - raise SystemExit(1) 25 - 26 - # CCSDS RS(255,223) parameters 27 - NSYM = 32 # 2*t = 2*16 = 32 parity symbols 28 - PRIM = 0x187 # primitive polynomial 29 - ALPHA = 11 # primitive element 30 - FCR = 112 # first consecutive root 31 - 32 - # Initialize with CCSDS field parameters 33 - rs = RSCodec(NSYM, prim=PRIM, generator=ALPHA, fcr=FCR) 34 - 35 - messages = [ 36 - ("zeros", bytes(223)), 37 - ("ones", bytes([0x01] * 223)), 38 - ("ascending", bytes([i % 256 for i in range(223)])), 39 - ("hello", b"Hello, Reed-Solomon!" + bytes(203)), 40 - ] 41 - 42 - for name, data in messages: 43 - codeword = rs.encode(data) 44 - print(f"{name}|{data.hex()}|{bytes(codeword).hex()}")
-101
test/interop/test.ml
··· 1 - (** Python reedsolo interop tests for ocaml-reed-solomon. 2 - 3 - Reads committed traces from [traces/rs_vectors.hex] and verifies that the 4 - OCaml RS(255,223) encoder produces identical codewords. 5 - 6 - Traces generated by: [REGEN_TRACES=1 dune build @regen-traces] Oracle: 7 - Python reedsolo (configured with CCSDS parameters) *) 8 - 9 - let trace path = Filename.concat "traces" path 10 - 11 - let read_file path = 12 - let ic = open_in path in 13 - let n = in_channel_length ic in 14 - let s = Bytes.create n in 15 - really_input ic s 0 n; 16 - close_in ic; 17 - Bytes.to_string s 18 - 19 - let bytes_of_hex hex = 20 - let len = String.length hex / 2 in 21 - Bytes.init len (fun i -> 22 - Char.chr (int_of_string ("0x" ^ String.sub hex (i * 2) 2))) 23 - 24 - let hex_of_bytes b = 25 - let buf = Buffer.create (Bytes.length b * 2) in 26 - Bytes.iter 27 - (fun c -> Buffer.add_string buf (Printf.sprintf "%02x" (Char.code c))) 28 - b; 29 - Buffer.contents buf 30 - 31 - type vector = { name : string; data : bytes; expected_codeword : bytes } 32 - 33 - let parse_vectors path = 34 - let content = read_file path in 35 - let lines = String.split_on_char '\n' content in 36 - List.filter_map 37 - (fun line -> 38 - let line = String.trim line in 39 - if line = "" then None 40 - else 41 - match String.split_on_char '|' line with 42 - | [ name; data_hex; codeword_hex ] -> 43 - Some 44 - { 45 - name; 46 - data = bytes_of_hex data_hex; 47 - expected_codeword = bytes_of_hex codeword_hex; 48 - } 49 - | _ -> Alcotest.failf "bad vector line: %s" line) 50 - lines 51 - 52 - let test_encode vec () = 53 - let cfg = Reed_solomon.ccsds in 54 - let got = Reed_solomon.encode_systematic cfg vec.data in 55 - if got <> vec.expected_codeword then 56 - Alcotest.failf "%s: codeword mismatch\n expected: %s\n got: %s" 57 - vec.name 58 - (hex_of_bytes vec.expected_codeword) 59 - (hex_of_bytes got) 60 - 61 - let test_roundtrip vec () = 62 - let cfg = Reed_solomon.ccsds in 63 - let codeword = Reed_solomon.encode_systematic cfg vec.data in 64 - match Reed_solomon.decode cfg codeword with 65 - | Ok decoded -> 66 - if decoded <> vec.data then 67 - Alcotest.failf "%s: decode mismatch after clean roundtrip" vec.name 68 - | Error _ -> Alcotest.failf "%s: decode failed on clean codeword" vec.name 69 - 70 - let test_error_correction vec () = 71 - let cfg = Reed_solomon.ccsds in 72 - let codeword = Bytes.copy vec.expected_codeword in 73 - (* Inject 8 symbol errors (well within t=16 correction capability) *) 74 - for i = 0 to 7 do 75 - let pos = i * 30 in 76 - let orig = Bytes.get codeword pos in 77 - Bytes.set codeword pos (Char.chr (Char.code orig lxor 0xFF)) 78 - done; 79 - match Reed_solomon.decode cfg codeword with 80 - | Ok decoded -> 81 - if decoded <> vec.data then 82 - Alcotest.failf "%s: decode mismatch after error correction" vec.name 83 - | Error _ -> Alcotest.failf "%s: decode failed with 8 errors (t=16)" vec.name 84 - 85 - let () = 86 - let vectors = parse_vectors (trace "rs_vectors.hex") in 87 - Alcotest.run "rs-interop" 88 - [ 89 - ( "encode", 90 - List.map 91 - (fun v -> Alcotest.test_case v.name `Quick (test_encode v)) 92 - vectors ); 93 - ( "roundtrip", 94 - List.map 95 - (fun v -> Alcotest.test_case v.name `Quick (test_roundtrip v)) 96 - vectors ); 97 - ( "error-correction", 98 - List.map 99 - (fun v -> Alcotest.test_case v.name `Quick (test_error_correction v)) 100 - vectors ); 101 - ]
-4
test/interop/traces/rs_vectors.hex
··· 1 - zeros|00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000|000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 2 - ones|01010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101|010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101 3 - ascending|000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1a2a3a4a5a6a7a8a9aaabacadaeafb0b1b2b3b4b5b6b7b8b9babbbcbdbebfc0c1c2c3c4c5c6c7c8c9cacbcccdcecfd0d1d2d3d4d5d6d7d8d9dadbdcddde|000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1a2a3a4a5a6a7a8a9aaabacadaeafb0b1b2b3b4b5b6b7b8b9babbbcbdbebfc0c1c2c3c4c5c6c7c8c9cacbcccdcecfd0d1d2d3d4d5d6d7d8d9dadbdcddde1634409f25d4df227b645413120dd393f2977059c0aff86a1aa2d479bce372ee 4 - hello|48656c6c6f2c20526565642d536f6c6f6d6f6e210000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000|48656c6c6f2c20526565642d536f6c6f6d6f6e210000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f96c21f073f9eecb55c62b9e70bc317abacd52432ac4f4579b82e612755ed134