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

Configure Feed

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

Fix dune build: sexpt vcase_tag, tcf type annotation

- sexpt: use vcase_tag helper instead of c.tag on GADT value
- tcf: add type annotation to fix cuc_vector/cds_vector field
resolution ambiguity

+194
+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