CCSDS Synchronization and Channel Coding (131.0-B, 231.0-B)
0
fork

Configure Feed

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

irmin: schema2 prototype — node/=>/fix/self_describing

+435
+1
test/interop/dariol83/.gitignore
··· 1 + scripts/.jbang/
+19
test/interop/dariol83/dune
··· 1 + (test 2 + (name test) 3 + (libraries scc alcotest csvt) 4 + (deps 5 + (source_tree traces) 6 + (source_tree scripts))) 7 + 8 + ; Regenerate traces: REGEN_TRACES=1 dune build @regen-traces 9 + 10 + (rule 11 + (alias regen-traces) 12 + (enabled_if 13 + (= %{env:REGEN_TRACES=0} 1)) 14 + (deps 15 + (source_tree scripts)) 16 + (action 17 + (chdir 18 + scripts 19 + (run bash ./generate.sh))))
+222
test/interop/dariol83/scripts/generate.java
··· 1 + ///usr/bin/env jbang "$0" "$@" ; exit $? 2 + //DEPS eu.dariolucia.ccsds:eu.dariolucia.ccsds.tmtc:1.0.6 3 + 4 + // Generate CCSDS SCC interop traces for ocaml-scc. 5 + // 6 + // Covers CLTU encoding (BCH codeblocks) and CCSDS TM randomizer. 7 + // Oracle: dariol83/ccsds (Java) -- an independent CCSDS library. 8 + // Run: jbang generate.java <TRACE_DIR> 9 + 10 + import eu.dariolucia.ccsds.tmtc.algorithm.BchCltuAlgorithm; 11 + import eu.dariolucia.ccsds.tmtc.algorithm.RandomizerAlgorithm; 12 + 13 + import java.io.FileWriter; 14 + import java.io.PrintWriter; 15 + import java.nio.file.Files; 16 + import java.nio.file.Path; 17 + import java.nio.file.Paths; 18 + 19 + public class generate { 20 + 21 + static byte[] hexToBytes(String hex) { 22 + int len = hex.length(); 23 + byte[] out = new byte[len / 2]; 24 + for (int i = 0; i < len; i += 2) { 25 + out[i / 2] = (byte) ((Character.digit(hex.charAt(i), 16) << 4) 26 + + Character.digit(hex.charAt(i + 1), 16)); 27 + } 28 + return out; 29 + } 30 + 31 + static String bytesToHex(byte[] bytes) { 32 + StringBuilder sb = new StringBuilder(bytes.length * 2); 33 + for (byte b : bytes) { 34 + sb.append(String.format("%02x", b & 0xff)); 35 + } 36 + return sb.toString(); 37 + } 38 + 39 + /** Fill a byte array with a repeating value. */ 40 + static byte[] filled(int len, int val) { 41 + byte[] out = new byte[len]; 42 + java.util.Arrays.fill(out, (byte) val); 43 + return out; 44 + } 45 + 46 + /** Alternating 0xAA / 0x55 pattern. */ 47 + static byte[] alternating(int len) { 48 + byte[] out = new byte[len]; 49 + for (int i = 0; i < len; i++) { 50 + out[i] = (i % 2 == 0) ? (byte) 0xAA : (byte) 0x55; 51 + } 52 + return out; 53 + } 54 + 55 + /** Counting bytes: 0x00, 0x01, ..., 0xFF, 0x00, ... */ 56 + static byte[] counting(int len) { 57 + byte[] out = new byte[len]; 58 + for (int i = 0; i < len; i++) { 59 + out[i] = (byte) (i & 0xFF); 60 + } 61 + return out; 62 + } 63 + 64 + // ----------------------------------------------------------------------- 65 + // CLTU traces 66 + // ----------------------------------------------------------------------- 67 + 68 + record CltuVector(String name, byte[] tcFrame) {} 69 + 70 + static void generateCltuTraces(Path traceDir) throws Exception { 71 + CltuVector[] vectors = { 72 + // Minimal: 1 byte -> 1 codeblock 73 + new CltuVector("cltu_1byte", hexToBytes("00")), 74 + new CltuVector("cltu_1byte_ff", hexToBytes("ff")), 75 + // Exact 7 bytes -> 1 codeblock, no padding needed 76 + new CltuVector("cltu_7bytes", hexToBytes("01020304050607")), 77 + // 8 bytes -> 2 codeblocks, second has 1 data + 6 fill 78 + new CltuVector("cltu_8bytes", hexToBytes("0102030405060708")), 79 + // 14 bytes -> 2 codeblocks, both full 80 + new CltuVector("cltu_14bytes", hexToBytes("0102030405060708090a0b0c0d0e")), 81 + // 15 bytes -> 3 codeblocks, last has 1 data + 6 fill 82 + new CltuVector("cltu_15bytes", hexToBytes("0102030405060708090a0b0c0d0e0f")), 83 + // All zeros 84 + new CltuVector("cltu_zeros_7", filled(7, 0x00)), 85 + new CltuVector("cltu_zeros_21", filled(21, 0x00)), 86 + // All ones 87 + new CltuVector("cltu_ff_7", filled(7, 0xFF)), 88 + new CltuVector("cltu_ff_21", filled(21, 0xFF)), 89 + // Alternating pattern 90 + new CltuVector("cltu_alt_14", alternating(14)), 91 + new CltuVector("cltu_alt_21", alternating(21)), 92 + // Counting bytes 93 + new CltuVector("cltu_count_28", counting(28)), 94 + // Typical TC frame sizes 95 + new CltuVector("cltu_tc_small", hexToBytes("deadbeef")), 96 + new CltuVector("cltu_tc_medium", counting(50)), 97 + new CltuVector("cltu_tc_large", counting(100)), 98 + // Edge: 6 bytes -> 1 codeblock, 1 fill byte 99 + new CltuVector("cltu_6bytes", hexToBytes("010203040506")), 100 + // Edge: 2 bytes -> 1 codeblock, 5 fill bytes 101 + new CltuVector("cltu_2bytes", hexToBytes("aabb")), 102 + // Edge: 3 bytes -> 1 codeblock, 4 fill bytes 103 + new CltuVector("cltu_3bytes", hexToBytes("aabbcc")), 104 + // Longer frames 105 + new CltuVector("cltu_200bytes", counting(200)), 106 + }; 107 + 108 + // dariol83 BchCltuAlgorithm: encode/decode TC frame data as CLTU 109 + // with BCH(63,56) codeblocks, 0xEB90 start, 0xC5..79 tail, 0x55 fill. 110 + BchCltuAlgorithm bch = new BchCltuAlgorithm(); 111 + 112 + try (PrintWriter pw = new PrintWriter(new FileWriter( 113 + traceDir.resolve("cltu.csv").toFile()))) { 114 + pw.println("name,tc_frame_hex,cltu_hex"); 115 + 116 + for (CltuVector v : vectors) { 117 + // Encode: TC frame -> CLTU 118 + byte[] cltu = bch.encodeCltu(v.tcFrame()); 119 + 120 + // Sanity: decode back and verify 121 + byte[] decoded = bch.decodeCltu(cltu); 122 + // The decoder returns data including fill bytes from the last 123 + // codeblock. The original TC frame should be a prefix. 124 + boolean prefixMatch = true; 125 + if (decoded.length < v.tcFrame().length) { 126 + prefixMatch = false; 127 + } else { 128 + for (int i = 0; i < v.tcFrame().length; i++) { 129 + if (decoded[i] != v.tcFrame()[i]) { 130 + prefixMatch = false; 131 + break; 132 + } 133 + } 134 + } 135 + assert prefixMatch 136 + : v.name() + ": roundtrip prefix mismatch"; 137 + 138 + String tcHex = bytesToHex(v.tcFrame()); 139 + String cltuHex = bytesToHex(cltu); 140 + 141 + pw.printf("%s,%s,%s%n", v.name(), tcHex, cltuHex); 142 + } 143 + } 144 + System.out.println("Wrote " + traceDir.resolve("cltu.csv")); 145 + } 146 + 147 + // ----------------------------------------------------------------------- 148 + // Randomizer traces 149 + // ----------------------------------------------------------------------- 150 + 151 + record RandVector(String name, byte[] input) {} 152 + 153 + static void generateRandomizerTraces(Path traceDir) throws Exception { 154 + RandVector[] vectors = { 155 + // All zeros: output is the raw PRBS 156 + new RandVector("rand_zeros_10", filled(10, 0x00)), 157 + new RandVector("rand_zeros_20", filled(20, 0x00)), 158 + new RandVector("rand_zeros_100", filled(100, 0x00)), 159 + new RandVector("rand_zeros_255", filled(255, 0x00)), 160 + // All 0xFF: output is complement of PRBS 161 + new RandVector("rand_ff_10", filled(10, 0xFF)), 162 + new RandVector("rand_ff_20", filled(20, 0xFF)), 163 + new RandVector("rand_ff_100", filled(100, 0xFF)), 164 + // Alternating 165 + new RandVector("rand_alt_20", alternating(20)), 166 + new RandVector("rand_alt_100", alternating(100)), 167 + // Counting bytes 168 + new RandVector("rand_count_20", counting(20)), 169 + new RandVector("rand_count_100", counting(100)), 170 + new RandVector("rand_count_255", counting(255)), 171 + // Single byte 172 + new RandVector("rand_1byte_00", hexToBytes("00")), 173 + new RandVector("rand_1byte_ff", hexToBytes("ff")), 174 + new RandVector("rand_1byte_aa", hexToBytes("aa")), 175 + // Typical TM frame payload 176 + new RandVector("rand_tm_payload", hexToBytes("1acffc1d0000000000000000")), 177 + }; 178 + 179 + // dariol83 RandomizerAlgorithm.randomizeFrameTm: applies the CCSDS TM 180 + // pseudo-random sequence (polynomial x^8 + x^7 + x^5 + x^3 + 1) to a 181 + // byte array in-place via XOR. 182 + 183 + try (PrintWriter pw = new PrintWriter(new FileWriter( 184 + traceDir.resolve("randomizer.csv").toFile()))) { 185 + pw.println("name,input_hex,randomized_hex"); 186 + 187 + for (RandVector v : vectors) { 188 + // Copy input since randomizeFrameTm is in-place 189 + byte[] data = v.input().clone(); 190 + RandomizerAlgorithm.randomizeFrameTm(data); 191 + 192 + // Verify self-inverse: randomize again should give back original 193 + byte[] check = data.clone(); 194 + RandomizerAlgorithm.randomizeFrameTm(check); 195 + assert java.util.Arrays.equals(check, v.input()) 196 + : v.name() + ": randomizer not self-inverse"; 197 + 198 + String inputHex = bytesToHex(v.input()); 199 + String randHex = bytesToHex(data); 200 + 201 + pw.printf("%s,%s,%s%n", v.name(), inputHex, randHex); 202 + } 203 + } 204 + System.out.println("Wrote " + traceDir.resolve("randomizer.csv")); 205 + } 206 + 207 + // ----------------------------------------------------------------------- 208 + // Main 209 + // ----------------------------------------------------------------------- 210 + 211 + public static void main(String[] args) throws Exception { 212 + if (args.length < 1) { 213 + System.err.println("Usage: jbang generate.java <TRACE_DIR>"); 214 + System.exit(1); 215 + } 216 + Path traceDir = Paths.get(args[0]); 217 + Files.createDirectories(traceDir); 218 + 219 + generateCltuTraces(traceDir); 220 + generateRandomizerTraces(traceDir); 221 + } 222 + }
+7
test/interop/dariol83/scripts/generate.sh
··· 1 + #!/bin/bash 2 + set -euo pipefail 3 + SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" 4 + TRACE_DIR="$(cd "$SCRIPT_DIR/../traces" && pwd)" 5 + 6 + cd "$SCRIPT_DIR" 7 + jbang generate.java "$TRACE_DIR"
+186
test/interop/dariol83/test.ml
··· 1 + (** Java interop tests for ocaml-scc. 2 + 3 + Tests CLTU encoding (BCH codeblocks) and CCSDS TM randomizer against 4 + dariol83/ccsds (Java), an independent CCSDS library. 5 + 6 + Traces generated by: dariol83/ccsds 1.0.6 via jbang 7 + Regenerate: REGEN_TRACES=1 dune build @regen-traces *) 8 + 9 + let trace path = Filename.concat "traces" path 10 + 11 + let bytes_of_hex hex = 12 + let len = String.length hex / 2 in 13 + Bytes.init len (fun i -> 14 + Char.chr (int_of_string ("0x" ^ String.sub hex (i * 2) 2))) 15 + 16 + let hex_of_bytes b = 17 + let buf = Buffer.create (Bytes.length b * 2) in 18 + Bytes.iter 19 + (fun c -> Buffer.add_string buf (Printf.sprintf "%02x" (Char.code c))) 20 + b; 21 + Buffer.contents buf 22 + 23 + (* ----------------------------------------------------------------------- 24 + CLTU traces 25 + ----------------------------------------------------------------------- *) 26 + 27 + type cltu_row = { name : string; tc_frame_hex : string; cltu_hex : string } 28 + 29 + let cltu_codec = 30 + Csvt.( 31 + Row.( 32 + obj (fun name tc_frame_hex cltu_hex -> { name; tc_frame_hex; cltu_hex }) 33 + |> col "name" string ~enc:(fun r -> r.name) 34 + |> col "tc_frame_hex" string ~enc:(fun r -> r.tc_frame_hex) 35 + |> col "cltu_hex" string ~enc:(fun r -> r.cltu_hex) 36 + |> finish)) 37 + 38 + let parse_cltu_rows () = 39 + match Csvt.decode_file cltu_codec (trace "cltu.csv") with 40 + | Ok rows -> rows 41 + | Error e -> Alcotest.failf "failed to parse cltu.csv: %a" Csvt.pp_error e 42 + 43 + let test_cltu_encode (r : cltu_row) () = 44 + let tc_frame = bytes_of_hex r.tc_frame_hex in 45 + let cltu = Scc.Sync.Cltu.encode tc_frame in 46 + let got_hex = hex_of_bytes cltu in 47 + if got_hex <> r.cltu_hex then 48 + Alcotest.failf "%s: encode mismatch\n expected: %s\n got: %s" r.name 49 + r.cltu_hex got_hex 50 + 51 + let test_cltu_decode (r : cltu_row) () = 52 + let cltu = bytes_of_hex r.cltu_hex in 53 + match Scc.Sync.Cltu.decode cltu with 54 + | Error e -> Alcotest.failf "%s: decode failed: %a" r.name Scc.Sync.pp_error e 55 + | Ok (data, _remaining) -> 56 + (* dariol83 preserves fill bytes in decoded output; our decode also 57 + preserves fill bytes per CCSDS 231.0-B-4 Section 3.4.2. 58 + The original TC frame should be a prefix of the decoded data. *) 59 + let tc_frame = bytes_of_hex r.tc_frame_hex in 60 + let tc_len = Bytes.length tc_frame in 61 + if Bytes.length data < tc_len then 62 + Alcotest.failf "%s: decoded too short: %d < %d" r.name 63 + (Bytes.length data) tc_len 64 + else 65 + let prefix = Bytes.sub data 0 tc_len in 66 + let prefix_hex = hex_of_bytes prefix in 67 + if prefix_hex <> r.tc_frame_hex then 68 + Alcotest.failf 69 + "%s: decoded prefix mismatch\n expected: %s\n got: %s" r.name 70 + r.tc_frame_hex prefix_hex 71 + 72 + let test_cltu_roundtrip (r : cltu_row) () = 73 + let tc_frame = bytes_of_hex r.tc_frame_hex in 74 + let cltu = Scc.Sync.Cltu.encode tc_frame in 75 + match Scc.Sync.Cltu.decode cltu with 76 + | Error e -> 77 + Alcotest.failf "%s: roundtrip decode failed: %a" r.name Scc.Sync.pp_error 78 + e 79 + | Ok (data, _remaining) -> 80 + let tc_len = Bytes.length tc_frame in 81 + if Bytes.length data < tc_len then 82 + Alcotest.failf "%s: roundtrip decoded too short: %d < %d" r.name 83 + (Bytes.length data) tc_len 84 + else 85 + let prefix = Bytes.sub data 0 tc_len in 86 + if prefix <> tc_frame then 87 + Alcotest.failf 88 + "%s: roundtrip prefix mismatch\n expected: %s\n got: %s" 89 + r.name r.tc_frame_hex (hex_of_bytes prefix) 90 + 91 + (* ----------------------------------------------------------------------- 92 + Randomizer traces 93 + ----------------------------------------------------------------------- *) 94 + 95 + type rand_row = { name : string; input_hex : string; randomized_hex : string } 96 + 97 + let rand_codec = 98 + Csvt.( 99 + Row.( 100 + obj (fun name input_hex randomized_hex -> 101 + { name; input_hex; randomized_hex }) 102 + |> col "name" string ~enc:(fun r -> r.name) 103 + |> col "input_hex" string ~enc:(fun r -> r.input_hex) 104 + |> col "randomized_hex" string ~enc:(fun r -> r.randomized_hex) 105 + |> finish)) 106 + 107 + let parse_rand_rows () = 108 + match Csvt.decode_file rand_codec (trace "randomizer.csv") with 109 + | Ok rows -> rows 110 + | Error e -> 111 + Alcotest.failf "failed to parse randomizer.csv: %a" Csvt.pp_error e 112 + 113 + let test_randomize (r : rand_row) () = 114 + let input = bytes_of_hex r.input_hex in 115 + let rand = Scc.Coding.Randomizer.create () in 116 + Scc.Coding.Randomizer.apply rand input 0 (Bytes.length input); 117 + let got_hex = hex_of_bytes input in 118 + if got_hex <> r.randomized_hex then 119 + Alcotest.failf "%s: randomize mismatch\n expected: %s\n got: %s" 120 + r.name r.randomized_hex got_hex 121 + 122 + let test_derandomize (r : rand_row) () = 123 + (* Applying the randomizer twice should return the original. *) 124 + let data = bytes_of_hex r.randomized_hex in 125 + let rand = Scc.Coding.Randomizer.create () in 126 + Scc.Coding.Randomizer.apply rand data 0 (Bytes.length data); 127 + let got_hex = hex_of_bytes data in 128 + if got_hex <> r.input_hex then 129 + Alcotest.failf "%s: derandomize mismatch\n expected: %s\n got: %s" 130 + r.name r.input_hex got_hex 131 + 132 + let test_randomize_string (r : rand_row) () = 133 + let input = Bytes.to_string (bytes_of_hex r.input_hex) in 134 + let rand = Scc.Coding.Randomizer.create () in 135 + let got = Scc.Coding.Randomizer.apply_string rand input in 136 + let got_hex = 137 + let buf = Buffer.create (String.length got * 2) in 138 + String.iter 139 + (fun c -> Buffer.add_string buf (Printf.sprintf "%02x" (Char.code c))) 140 + got; 141 + Buffer.contents buf 142 + in 143 + if got_hex <> r.randomized_hex then 144 + Alcotest.failf "%s: apply_string mismatch\n expected: %s\n got: %s" 145 + r.name r.randomized_hex got_hex 146 + 147 + (* ----------------------------------------------------------------------- 148 + Test runner 149 + ----------------------------------------------------------------------- *) 150 + 151 + let () = 152 + let cltu_rows = parse_cltu_rows () in 153 + let rand_rows = parse_rand_rows () in 154 + Alcotest.run "scc-interop" 155 + [ 156 + ( "cltu-encode", 157 + List.map 158 + (fun (r : cltu_row) -> 159 + Alcotest.test_case r.name `Quick (test_cltu_encode r)) 160 + cltu_rows ); 161 + ( "cltu-decode", 162 + List.map 163 + (fun (r : cltu_row) -> 164 + Alcotest.test_case r.name `Quick (test_cltu_decode r)) 165 + cltu_rows ); 166 + ( "cltu-roundtrip", 167 + List.map 168 + (fun (r : cltu_row) -> 169 + Alcotest.test_case r.name `Quick (test_cltu_roundtrip r)) 170 + cltu_rows ); 171 + ( "randomize", 172 + List.map 173 + (fun (r : rand_row) -> 174 + Alcotest.test_case r.name `Quick (test_randomize r)) 175 + rand_rows ); 176 + ( "derandomize", 177 + List.map 178 + (fun (r : rand_row) -> 179 + Alcotest.test_case r.name `Quick (test_derandomize r)) 180 + rand_rows ); 181 + ( "randomize-string", 182 + List.map 183 + (fun (r : rand_row) -> 184 + Alcotest.test_case r.name `Quick (test_randomize_string r)) 185 + rand_rows ); 186 + ]