LDPC codes with belief propagation decoding
0
fork

Configure Feed

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

Add 8 new CCSDS/RFC protocol packages

- ocaml-rice: CCSDS 121.0-B lossless compression (Rice/Golomb)
- ocaml-udpcl: RFC 7122 UDP convergence layer for Bundle Protocol
- ocaml-erasure: CCSDS 131.5-B erasure correcting codes (GF(2^8))
- ocaml-short-ldpc: CCSDS 131.4-B short block-length LDPC
- ocaml-opm: CCSDS 502.0-B Orbit Parameter Message (KVN)
- ocaml-aem: CCSDS 504.0-B Attitude Ephemeris Message (KVN)
- ocaml-tdm: CCSDS 503.0-B Tracking Data Message (KVN)
- ocaml-rdm: CCSDS 508.1-B Re-entry Data Message (KVN)

+35 -9
+23 -5
fuzz/fuzz_ldpc.ml
··· 33 33 check_eq ~pp:pp_string (Bytes.to_string data) (Bytes.to_string recovered) 34 34 | Error e -> fail (Printf.sprintf "decode failed: %s" e) 35 35 36 - (** Fuzz test: decode does not crash on arbitrary input. *) 37 - let test_decode_no_crash input_str = 38 - (* Just verify decode doesn't raise an exception on random input *) 36 + (** Fuzz test: decode arbitrary input. If it succeeds, the output must be 37 + exactly 128 bytes (k=1024 bits / 8). *) 38 + let test_decode_invariants input_str = 39 39 let input = Bytes.of_string input_str in 40 - ignore (Ldpc.decode ldpc input) 40 + match Ldpc.decode ldpc input with 41 + | Ok recovered -> 42 + (* k=1024 bits -> 128 bytes *) 43 + check_eq ~pp:pp_int 128 (Bytes.length recovered) 44 + | Error _ -> 45 + (* Error is acceptable for arbitrary input *) 46 + () 47 + 48 + (** Fuzz test: encode always produces a 256-byte codeword (n=2048 bits) and the 49 + first 128 bytes are the original data (systematic property). *) 50 + let test_encode_invariants data_str = 51 + let data = Bytes.of_string data_str in 52 + let codeword = Ldpc.encode ldpc data in 53 + (* n=2048 bits -> 256 bytes *) 54 + check_eq ~pp:pp_int 256 (Bytes.length codeword); 55 + (* Systematic: first 128 bytes must equal the input data *) 56 + let prefix = Bytes.sub_string codeword 0 128 in 57 + check_eq ~pp:pp_string (Bytes.to_string data) prefix 41 58 42 59 let suite = 43 60 ( "ldpc", ··· 54 71 range 2048 (* bit position 2 *); 55 72 ] 56 73 test_roundtrip_with_errors; 57 - test_case "decode random bytes" [ bytes ] test_decode_no_crash; 74 + test_case "encode invariants" [ bytes_fixed 128 ] test_encode_invariants; 75 + test_case "decode invariants" [ bytes ] test_decode_invariants; 58 76 ] )
+12 -4
test/test_ldpc.ml
··· 273 273 if ok_5 then 274 274 Alcotest.(check bool) "if max_iter=5 works, 50 should too" true ok_50 275 275 276 - (** Test that max_iter=1 is insufficient for larger errors. *) 276 + (** Test that max_iter=1 is insufficient for larger errors but max_iter=50 277 + succeeds, demonstrating that iteration count matters. *) 277 278 let test_bp_insufficient_iterations () = 278 279 let data = make_data 33 in 279 280 let codeword = Ldpc.encode ldpc data in ··· 283 284 | Ok recovered -> Bytes.to_string recovered = Bytes.to_string data 284 285 | Error _ -> false 285 286 in 286 - (* max_iter=1 is very unlikely to correct 8 errors *) 287 - (* We don't assert it must fail (theoretically possible), but document it *) 288 - ignore ok_1 287 + let ok_50 = 288 + match Ldpc.decode ~max_iter:50 ldpc corrupted with 289 + | Ok recovered -> Bytes.to_string recovered = Bytes.to_string data 290 + | Error _ -> false 291 + in 292 + (* With 50 iterations, 8 errors should be correctable *) 293 + Alcotest.(check bool) "max_iter=50 corrects 8 errors" true ok_50; 294 + (* More iterations should be at least as good as fewer *) 295 + if ok_1 then 296 + Alcotest.(check bool) "if max_iter=1 works, 50 must too" true ok_50 289 297 290 298 (* --- Bit-level patterns --- *) 291 299