SpaceWire (ECSS-E-ST-50-12C) and RMAP (ECSS-E-ST-50-52C)
0
fork

Configure Feed

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

irmin: flatten Schema API — remove Struct/Map/Repr submodules

-676
-11
test/interop/python/dune
··· 1 - (rule 2 - (deps 3 - (source_tree scripts)) 4 - (targets vectors_packets.csv vectors_instructions.csv vectors_crc.csv) 5 - (action 6 - (run python3 scripts/generate.py vectors))) 7 - 8 - (test 9 - (name test) 10 - (libraries spacewire csvt alcotest) 11 - (deps vectors_packets.csv vectors_instructions.csv vectors_crc.csv))
-301
test/interop/python/scripts/generate.py
··· 1 - #!/usr/bin/env python3 2 - """Generate SpaceWire / RMAP interop traces for ocaml-spacewire. 3 - 4 - Oracle: Python (no deps needed for bitfield packing from the specs) 5 - Install: no dependencies 6 - 7 - Tests SpaceWire packet encoding (ECSS-E-ST-50-12C), RMAP instruction byte 8 - encoding and RMAP CRC-8 (ECSS-E-ST-50-52C). 9 - 10 - SpaceWire Packet wire format: 11 - [addr_count : 1 byte] 12 - [addr_bytes : addr_count bytes] 13 - [eop_marker : 1 byte] (0x00 = EOP, 0x01 = EEP) 14 - [cargo : remaining bytes] 15 - 16 - RMAP Instruction byte (1 byte): 17 - Bit 7: reserved (0) 18 - Bit 6: command (1) / reply (0) 19 - Bit 5: write (1) / read (0) 20 - Bit 4: verify (1) / no verify (0) 21 - Bit 3: ack (1) / no ack (0) 22 - Bit 2: increment (1) / no increment (0) 23 - Bits 1-0: reply address length (0-3, in 4-byte units) 24 - 25 - RMAP CRC-8: Polynomial x^8 + x^2 + x + 1 (0x07). 26 - Per ECSS-E-ST-50-52C Annex A. 27 - 28 - Traces are committed to git. Only re-run when changing inputs 29 - or upgrading the oracle. 30 - """ 31 - import csv 32 - import os 33 - import sys 34 - 35 - 36 - # --------------------------------------------------------------------------- 37 - # SpaceWire packet encoding 38 - # --------------------------------------------------------------------------- 39 - 40 - def encode_packet(address, cargo, eop): 41 - """Encode a SpaceWire packet to bytes. 42 - 43 - address: list of int (0-255) 44 - cargo: bytes 45 - eop: "EOP" or "EEP" 46 - """ 47 - addr_count = len(address) 48 - eop_byte = 0x00 if eop == "EOP" else 0x01 49 - buf = bytes([addr_count]) + bytes(address) + bytes([eop_byte]) + cargo 50 - return buf 51 - 52 - 53 - # --------------------------------------------------------------------------- 54 - # RMAP CRC-8 55 - # --------------------------------------------------------------------------- 56 - 57 - def rmap_crc8_table(): 58 - """Build the CRC-8 lookup table for polynomial 0x07.""" 59 - table = [] 60 - for i in range(256): 61 - crc = i 62 - for _ in range(8): 63 - if crc & 0x80: 64 - crc = ((crc << 1) ^ 0x07) & 0xFF 65 - else: 66 - crc = (crc << 1) & 0xFF 67 - table.append(crc) 68 - return table 69 - 70 - CRC_TABLE = rmap_crc8_table() 71 - 72 - 73 - def rmap_crc8(data): 74 - """Compute RMAP CRC-8 over bytes.""" 75 - crc = 0 76 - for b in data: 77 - crc = CRC_TABLE[crc ^ b] 78 - return crc 79 - 80 - 81 - # --------------------------------------------------------------------------- 82 - # RMAP instruction byte 83 - # --------------------------------------------------------------------------- 84 - 85 - def encode_instruction(is_command, is_write, is_verify, is_ack, is_increment, 86 - reply_addr_len): 87 - """Encode the RMAP instruction byte. 88 - 89 - reply_addr_len: 0-3 (in 4-byte units) 90 - """ 91 - byte = 0 92 - # bit 7: reserved (0) 93 - if is_command: 94 - byte |= (1 << 6) 95 - if is_write: 96 - byte |= (1 << 5) 97 - if is_verify: 98 - byte |= (1 << 4) 99 - if is_ack: 100 - byte |= (1 << 3) 101 - if is_increment: 102 - byte |= (1 << 2) 103 - byte |= (reply_addr_len & 0x03) 104 - return byte 105 - 106 - 107 - # --------------------------------------------------------------------------- 108 - # Test vectors 109 - # --------------------------------------------------------------------------- 110 - 111 - # 1. Packet encoding vectors 112 - packet_vectors = [ 113 - { 114 - "name": "no_addr_eop", 115 - "address": [], 116 - "cargo": b"\xDE\xAD", 117 - "eop": "EOP", 118 - }, 119 - { 120 - "name": "no_addr_eep", 121 - "address": [], 122 - "cargo": b"\xBE\xEF", 123 - "eop": "EEP", 124 - }, 125 - { 126 - "name": "single_addr", 127 - "address": [42], 128 - "cargo": b"\x01\x02\x03", 129 - "eop": "EOP", 130 - }, 131 - { 132 - "name": "three_addr_eop", 133 - "address": [5, 17, 200], 134 - "cargo": b"\xCA\xFE\xBA\xBE", 135 - "eop": "EOP", 136 - }, 137 - { 138 - "name": "three_addr_eep", 139 - "address": [0, 128, 255], 140 - "cargo": b"\x42", 141 - "eop": "EEP", 142 - }, 143 - { 144 - "name": "single_byte_cargo", 145 - "address": [1], 146 - "cargo": b"\xFF", 147 - "eop": "EOP", 148 - }, 149 - { 150 - "name": "binary_cargo_256", 151 - "address": [10], 152 - "cargo": bytes(range(256)), 153 - "eop": "EOP", 154 - }, 155 - { 156 - "name": "max_addr_byte", 157 - "address": [255], 158 - "cargo": b"\xAA\xBB", 159 - "eop": "EEP", 160 - }, 161 - ] 162 - 163 - # 2. RMAP instruction byte vectors 164 - instruction_vectors = [ 165 - { 166 - "name": "read_no_ack_no_inc", 167 - "is_command": True, "is_write": False, "is_verify": False, 168 - "is_ack": False, "is_increment": False, "reply_addr_len": 0, 169 - }, 170 - { 171 - "name": "read_ack_inc", 172 - "is_command": True, "is_write": False, "is_verify": False, 173 - "is_ack": True, "is_increment": True, "reply_addr_len": 0, 174 - }, 175 - { 176 - "name": "write_ack_inc", 177 - "is_command": True, "is_write": True, "is_verify": False, 178 - "is_ack": True, "is_increment": True, "reply_addr_len": 0, 179 - }, 180 - { 181 - "name": "write_verify_ack_inc", 182 - "is_command": True, "is_write": True, "is_verify": True, 183 - "is_ack": True, "is_increment": True, "reply_addr_len": 0, 184 - }, 185 - { 186 - "name": "rmw_ack_inc", 187 - "is_command": True, "is_write": False, "is_verify": True, 188 - "is_ack": True, "is_increment": True, "reply_addr_len": 0, 189 - }, 190 - { 191 - "name": "read_reply", 192 - "is_command": False, "is_write": False, "is_verify": False, 193 - "is_ack": True, "is_increment": True, "reply_addr_len": 0, 194 - }, 195 - { 196 - "name": "write_reply", 197 - "is_command": False, "is_write": True, "is_verify": False, 198 - "is_ack": True, "is_increment": True, "reply_addr_len": 0, 199 - }, 200 - { 201 - "name": "cmd_reply_addr_1", 202 - "is_command": True, "is_write": False, "is_verify": False, 203 - "is_ack": True, "is_increment": True, "reply_addr_len": 1, 204 - }, 205 - { 206 - "name": "cmd_reply_addr_3", 207 - "is_command": True, "is_write": True, "is_verify": True, 208 - "is_ack": True, "is_increment": True, "reply_addr_len": 3, 209 - }, 210 - { 211 - "name": "all_zero", 212 - "is_command": False, "is_write": False, "is_verify": False, 213 - "is_ack": False, "is_increment": False, "reply_addr_len": 0, 214 - }, 215 - { 216 - "name": "cmd_write_no_ack_no_inc", 217 - "is_command": True, "is_write": True, "is_verify": False, 218 - "is_ack": False, "is_increment": False, "reply_addr_len": 2, 219 - }, 220 - ] 221 - 222 - # 3. RMAP CRC-8 vectors 223 - # Known test vectors from ECSS-E-ST-50-52C Annex A Table A-1 224 - crc_vectors = [ 225 - {"name": "empty", "data": b""}, 226 - {"name": "single_zero", "data": b"\x00"}, 227 - {"name": "single_ff", "data": b"\xFF"}, 228 - {"name": "single_01", "data": b"\x01"}, 229 - {"name": "two_bytes", "data": b"\x01\x02"}, 230 - {"name": "three_bytes", "data": b"\x01\x02\x03"}, 231 - {"name": "ascending_8", "data": bytes(range(8))}, 232 - {"name": "ascending_16", "data": bytes(range(16))}, 233 - {"name": "all_ff_4", "data": b"\xFF\xFF\xFF\xFF"}, 234 - {"name": "all_ff_8", "data": b"\xFF" * 8}, 235 - {"name": "alternating", "data": b"\xAA\x55\xAA\x55"}, 236 - {"name": "rmap_header_like", "data": b"\xFE\x01\x6C\x00\x67\x00\x00"}, 237 - {"name": "long_payload", "data": bytes(range(256))}, 238 - ] 239 - 240 - 241 - # --------------------------------------------------------------------------- 242 - # Trace writing 243 - # --------------------------------------------------------------------------- 244 - 245 - def write_traces(base_path): 246 - os.makedirs(os.path.dirname(base_path) or ".", exist_ok=True) 247 - 248 - # Packet vectors 249 - pkt_path = base_path + "_packets.csv" 250 - with open(pkt_path, "w", newline="") as fh: 251 - w = csv.writer(fh) 252 - w.writerow(["name", "address", "cargo_hex", "eop", "encoded_hex"]) 253 - for v in packet_vectors: 254 - addr_str = ";".join(str(a) for a in v["address"]) 255 - cargo_hex = v["cargo"].hex() 256 - encoded = encode_packet(v["address"], v["cargo"], v["eop"]) 257 - w.writerow([ 258 - v["name"], addr_str, cargo_hex, v["eop"], encoded.hex() 259 - ]) 260 - print(f"Generated {len(packet_vectors)} packet traces -> {pkt_path}") 261 - 262 - # Instruction byte vectors 263 - instr_path = base_path + "_instructions.csv" 264 - with open(instr_path, "w", newline="") as fh: 265 - w = csv.writer(fh) 266 - w.writerow(["name", "is_command", "is_write", "is_verify", 267 - "is_ack", "is_increment", "reply_addr_len", "byte_hex"]) 268 - for v in instruction_vectors: 269 - byte = encode_instruction( 270 - v["is_command"], v["is_write"], v["is_verify"], 271 - v["is_ack"], v["is_increment"], v["reply_addr_len"] 272 - ) 273 - w.writerow([ 274 - v["name"], 275 - 1 if v["is_command"] else 0, 276 - 1 if v["is_write"] else 0, 277 - 1 if v["is_verify"] else 0, 278 - 1 if v["is_ack"] else 0, 279 - 1 if v["is_increment"] else 0, 280 - v["reply_addr_len"], 281 - "%02x" % byte, 282 - ]) 283 - print(f"Generated {len(instruction_vectors)} instruction traces -> {instr_path}") 284 - 285 - # CRC-8 vectors 286 - crc_path = base_path + "_crc.csv" 287 - with open(crc_path, "w", newline="") as fh: 288 - w = csv.writer(fh) 289 - w.writerow(["name", "data_hex", "crc_hex"]) 290 - for v in crc_vectors: 291 - crc = rmap_crc8(v["data"]) 292 - w.writerow([v["name"], v["data"].hex(), "%02x" % crc]) 293 - print(f"Generated {len(crc_vectors)} CRC traces -> {crc_path}") 294 - 295 - 296 - if __name__ == "__main__": 297 - if len(sys.argv) > 1: 298 - base = sys.argv[1] 299 - else: 300 - base = os.path.join(os.path.dirname(__file__), "..", "vectors") 301 - write_traces(base)
-364
test/interop/python/test.ml
··· 1 - (** Python interop tests for ocaml-spacewire. 2 - 3 - Tests SpaceWire packet encoding and RMAP instruction byte / CRC-8 against a 4 - Python reference implementation of ECSS-E-ST-50-12C and ECSS-E-ST-50-52C. 5 - 6 - Traces generated by: Python (ECSS-E-ST-50-12C / ECSS-E-ST-50-52C reference) 7 - Regenerate: dune build ocaml-spacewire/test/interop/python (runs 8 - automatically) *) 9 - 10 - (* {1 Helpers} *) 11 - 12 - let hex_to_string hex = 13 - let len = String.length hex / 2 in 14 - String.init len (fun i -> 15 - let digit c = 16 - if c >= '0' && c <= '9' then Char.code c - Char.code '0' 17 - else if c >= 'a' && c <= 'f' then Char.code c - Char.code 'a' + 10 18 - else Char.code c - Char.code 'A' + 10 19 - in 20 - Char.chr ((digit hex.[i * 2] lsl 4) lor digit hex.[(i * 2) + 1])) 21 - 22 - let string_to_hex s = 23 - let buf = Buffer.create (String.length s * 2) in 24 - String.iter 25 - (fun c -> Buffer.add_string buf (Printf.sprintf "%02x" (Char.code c))) 26 - s; 27 - Buffer.contents buf 28 - 29 - let parse_address s = 30 - if String.length s = 0 then [] 31 - else String.split_on_char ';' s |> List.map int_of_string 32 - 33 - (* {1 Packet Vectors} *) 34 - 35 - type packet_vector = { 36 - name : string; 37 - address : string; 38 - cargo_hex : string; 39 - eop : string; 40 - encoded_hex : string; 41 - } 42 - 43 - let packet_codec = 44 - Csvt.( 45 - Row.( 46 - obj (fun name address cargo_hex eop encoded_hex -> 47 - { name; address; cargo_hex; eop; encoded_hex }) 48 - |> col "name" string ~enc:(fun r -> r.name) 49 - |> col "address" string ~enc:(fun r -> r.address) 50 - |> col "cargo_hex" string ~enc:(fun r -> r.cargo_hex) 51 - |> col "eop" string ~enc:(fun r -> r.eop) 52 - |> col "encoded_hex" string ~enc:(fun r -> r.encoded_hex) 53 - |> finish)) 54 - 55 - let load_packet_vectors () = 56 - match Csvt.decode_file packet_codec "vectors_packets.csv" with 57 - | Ok rows -> rows 58 - | Error e -> Alcotest.failf "CSV decode: %a" Csvt.pp_error e 59 - 60 - let eop_of_string = function 61 - | "EOP" -> Spacewire.EOP 62 - | "EEP" -> Spacewire.EEP 63 - | s -> Alcotest.failf "bad eop: %s" s 64 - 65 - let test_packet_encode vectors () = 66 - List.iter 67 - (fun (v : packet_vector) -> 68 - let address = parse_address v.address in 69 - let cargo = hex_to_string v.cargo_hex in 70 - let eop = eop_of_string v.eop in 71 - let p = Spacewire.packet_exn ~address ~cargo eop in 72 - let encoded = Spacewire.encode_packet p in 73 - let got_hex = string_to_hex encoded in 74 - Alcotest.(check string) (v.name ^ ": encode") v.encoded_hex got_hex) 75 - vectors 76 - 77 - let test_packet_decode vectors () = 78 - List.iter 79 - (fun (v : packet_vector) -> 80 - let buf = hex_to_string v.encoded_hex in 81 - match Spacewire.decode_packet buf with 82 - | Error `Truncated -> 83 - Alcotest.failf "%s: decode returned Truncated" v.name 84 - | Error `Cargo_empty -> 85 - Alcotest.failf "%s: decode returned Cargo_empty" v.name 86 - | Ok p -> 87 - let expected_addr = parse_address v.address in 88 - Alcotest.(check (list int)) 89 - (v.name ^ ": address") expected_addr (Spacewire.address p); 90 - let expected_cargo = hex_to_string v.cargo_hex in 91 - Alcotest.(check string) 92 - (v.name ^ ": cargo") expected_cargo (Spacewire.cargo p); 93 - let expected_eop = eop_of_string v.eop in 94 - Alcotest.(check bool) 95 - (v.name ^ ": eop") true 96 - (Spacewire.eop p = expected_eop)) 97 - vectors 98 - 99 - let test_packet_roundtrip vectors () = 100 - List.iter 101 - (fun (v : packet_vector) -> 102 - let address = parse_address v.address in 103 - let cargo = hex_to_string v.cargo_hex in 104 - let eop = eop_of_string v.eop in 105 - let p = Spacewire.packet_exn ~address ~cargo eop in 106 - let encoded = Spacewire.encode_packet p in 107 - match Spacewire.decode_packet encoded with 108 - | Error _ -> Alcotest.failf "%s: roundtrip decode failed" v.name 109 - | Ok decoded -> 110 - Alcotest.(check bool) 111 - (v.name ^ ": roundtrip") true 112 - (Spacewire.equal_packet p decoded)) 113 - vectors 114 - 115 - (* {1 RMAP Instruction Vectors} *) 116 - 117 - type instruction_vector = { 118 - instr_name : string; 119 - is_command : bool; 120 - is_write : bool; 121 - is_verify : bool; 122 - is_ack : bool; 123 - is_increment : bool; 124 - reply_addr_len : int; 125 - byte_hex : string; 126 - } 127 - 128 - let instruction_codec = 129 - Csvt.( 130 - Row.( 131 - obj 132 - (fun 133 - instr_name 134 - is_command 135 - is_write 136 - is_verify 137 - is_ack 138 - is_increment 139 - reply_addr_len 140 - byte_hex 141 - -> 142 - { 143 - instr_name; 144 - is_command; 145 - is_write; 146 - is_verify; 147 - is_ack; 148 - is_increment; 149 - reply_addr_len; 150 - byte_hex; 151 - }) 152 - |> col "name" string ~enc:(fun r -> r.instr_name) 153 - |> col "is_command" bool ~enc:(fun r -> r.is_command) 154 - |> col "is_write" bool ~enc:(fun r -> r.is_write) 155 - |> col "is_verify" bool ~enc:(fun r -> r.is_verify) 156 - |> col "is_ack" bool ~enc:(fun r -> r.is_ack) 157 - |> col "is_increment" bool ~enc:(fun r -> r.is_increment) 158 - |> col "reply_addr_len" int ~enc:(fun r -> r.reply_addr_len) 159 - |> col "byte_hex" string ~enc:(fun r -> r.byte_hex) 160 - |> finish)) 161 - 162 - let load_instruction_vectors () = 163 - match Csvt.decode_file instruction_codec "vectors_instructions.csv" with 164 - | Ok rows -> rows 165 - | Error e -> Alcotest.failf "CSV decode: %a" Csvt.pp_error e 166 - 167 - (* Build an rmap_command with the given flags, just for instruction encoding. 168 - We use dummy values for fields that don't affect the instruction byte. *) 169 - let make_test_command ~is_write ~is_verify ~is_ack ~is_increment ~reply_addr_len 170 - = 171 - let command : Spacewire.rmap_command_code = 172 - match (is_write, is_verify) with 173 - | false, false -> Read 174 - | true, false -> Write 175 - | true, true -> Write_verify 176 - | false, true -> Read_modify_write 177 - in 178 - let ack : Spacewire.rmap_ack = if is_ack then Ack else No_ack in 179 - let increment : Spacewire.rmap_increment = 180 - if is_increment then Increment else No_increment 181 - in 182 - (* reply_addr_len is in 4-byte units; create that many dummy bytes *) 183 - let reply_address = List.init (reply_addr_len * 4) (fun _ -> 1) in 184 - Spacewire.rmap_command_exn ~command ~ack ~increment ~key:0 ~reply_address 185 - ~initiator_logical_address:0 ~transaction_id:0 ~address:0L ~data_length:0 () 186 - 187 - let test_instruction_encode_via_command vectors () = 188 - (* For command instructions (is_command=true), we can test via 189 - encode_rmap_command and check byte 2 of the output. *) 190 - List.iter 191 - (fun (v : instruction_vector) -> 192 - if v.is_command then begin 193 - let cmd = 194 - make_test_command ~is_write:v.is_write ~is_verify:v.is_verify 195 - ~is_ack:v.is_ack ~is_increment:v.is_increment 196 - ~reply_addr_len:v.reply_addr_len 197 - in 198 - let encoded = 199 - Spacewire.encode_rmap_command ~target_logical_address:0 cmd 200 - in 201 - (* Byte 2 is the instruction byte *) 202 - let got = Char.code encoded.[2] in 203 - let expected = int_of_string ("0x" ^ v.byte_hex) in 204 - Alcotest.(check int) (v.instr_name ^ ": instruction byte") expected got 205 - end) 206 - vectors 207 - 208 - let test_instruction_encode_via_reply vectors () = 209 - (* For reply instructions (is_command=false), we test via 210 - encode_rmap_reply and check byte 2 of the output. 211 - Note: encode_rmap_reply hardcodes ack=true, increment=true, 212 - reply_addr_len=0, so we only check vectors matching those. *) 213 - List.iter 214 - (fun (v : instruction_vector) -> 215 - if 216 - (not v.is_command) && v.is_ack && v.is_increment && v.reply_addr_len = 0 217 - then begin 218 - let command : Spacewire.rmap_command_code = 219 - match (v.is_write, v.is_verify) with 220 - | false, false -> Read 221 - | true, false -> Write 222 - | true, true -> Write_verify 223 - | false, true -> Read_modify_write 224 - in 225 - let rpl = 226 - Spacewire.rmap_reply ~command ~status:Success 227 - ~initiator_logical_address:0 ~transaction_id:0 () 228 - in 229 - let encoded = 230 - Spacewire.encode_rmap_reply ~target_logical_address:0 rpl 231 - in 232 - let got = Char.code encoded.[2] in 233 - let expected = int_of_string ("0x" ^ v.byte_hex) in 234 - Alcotest.(check int) 235 - (v.instr_name ^ ": reply instruction byte") 236 - expected got 237 - end) 238 - vectors 239 - 240 - let test_instruction_decode_via_command vectors () = 241 - (* For command instructions, encode then decode and verify fields match. *) 242 - List.iter 243 - (fun (v : instruction_vector) -> 244 - if v.is_command then begin 245 - let cmd = 246 - make_test_command ~is_write:v.is_write ~is_verify:v.is_verify 247 - ~is_ack:v.is_ack ~is_increment:v.is_increment 248 - ~reply_addr_len:v.reply_addr_len 249 - in 250 - let encoded = 251 - Spacewire.encode_rmap_command ~target_logical_address:0xFE cmd 252 - in 253 - match Spacewire.decode_rmap_command encoded with 254 - | Error _ -> 255 - Alcotest.failf "%s: decode_rmap_command failed" v.instr_name 256 - | Ok (target, decoded) -> 257 - Alcotest.(check int) (v.instr_name ^ ": target") 0xFE target; 258 - let expected_code : Spacewire.rmap_command_code = 259 - match (v.is_write, v.is_verify) with 260 - | false, false -> Read 261 - | true, false -> Write 262 - | true, true -> Write_verify 263 - | false, true -> Read_modify_write 264 - in 265 - Alcotest.(check bool) 266 - (v.instr_name ^ ": cmd_code") 267 - true 268 - (Spacewire.rmap_cmd_code decoded = expected_code); 269 - let expected_ack : Spacewire.rmap_ack = 270 - if v.is_ack then Ack else No_ack 271 - in 272 - Alcotest.(check bool) 273 - (v.instr_name ^ ": ack") true 274 - (Spacewire.rmap_cmd_ack decoded = expected_ack); 275 - let expected_inc : Spacewire.rmap_increment = 276 - if v.is_increment then Increment else No_increment 277 - in 278 - Alcotest.(check bool) 279 - (v.instr_name ^ ": increment") 280 - true 281 - (Spacewire.rmap_cmd_increment decoded = expected_inc) 282 - end) 283 - vectors 284 - 285 - (* {1 RMAP CRC-8 Vectors} *) 286 - 287 - type crc_vector = { crc_name : string; data_hex : string; crc_hex : string } 288 - 289 - let crc_codec = 290 - Csvt.( 291 - Row.( 292 - obj (fun crc_name data_hex crc_hex -> { crc_name; data_hex; crc_hex }) 293 - |> col "name" string ~enc:(fun r -> r.crc_name) 294 - |> col "data_hex" string ~enc:(fun r -> r.data_hex) 295 - |> col "crc_hex" string ~enc:(fun r -> r.crc_hex) 296 - |> finish)) 297 - 298 - let load_crc_vectors () = 299 - match Csvt.decode_file crc_codec "vectors_crc.csv" with 300 - | Ok rows -> rows 301 - | Error e -> Alcotest.failf "CSV decode: %a" Csvt.pp_error e 302 - 303 - let test_crc vectors () = 304 - List.iter 305 - (fun (v : crc_vector) -> 306 - let data = hex_to_string v.data_hex in 307 - let got = Spacewire.rmap_crc data in 308 - let expected = int_of_string ("0x" ^ v.crc_hex) in 309 - Alcotest.(check int) (v.crc_name ^ ": crc8") expected got) 310 - vectors 311 - 312 - let test_crc_bytes vectors () = 313 - List.iter 314 - (fun (v : crc_vector) -> 315 - let data = hex_to_string v.data_hex in 316 - let buf = Bytes.of_string data in 317 - let got = Spacewire.rmap_crc_bytes buf 0 (Bytes.length buf) in 318 - let expected = int_of_string ("0x" ^ v.crc_hex) in 319 - Alcotest.(check int) (v.crc_name ^ ": crc8_bytes") expected got) 320 - vectors 321 - 322 - (* {1 Runner} *) 323 - 324 - let () = 325 - let pkt_vectors = load_packet_vectors () in 326 - let instr_vectors = load_instruction_vectors () in 327 - let crc_vectors = load_crc_vectors () in 328 - Alcotest.run "spacewire-interop-python" 329 - [ 330 - ( "packet-encode", 331 - [ 332 - Alcotest.test_case "encode matches reference" `Quick 333 - (test_packet_encode pkt_vectors); 334 - ] ); 335 - ( "packet-decode", 336 - [ 337 - Alcotest.test_case "decode reference packets" `Quick 338 - (test_packet_decode pkt_vectors); 339 - ] ); 340 - ( "packet-roundtrip", 341 - [ 342 - Alcotest.test_case "encode-decode roundtrip" `Quick 343 - (test_packet_roundtrip pkt_vectors); 344 - ] ); 345 - ( "rmap-instruction-encode", 346 - [ 347 - Alcotest.test_case "command instruction byte" `Quick 348 - (test_instruction_encode_via_command instr_vectors); 349 - Alcotest.test_case "reply instruction byte" `Quick 350 - (test_instruction_encode_via_reply instr_vectors); 351 - ] ); 352 - ( "rmap-instruction-decode", 353 - [ 354 - Alcotest.test_case "command roundtrip decode" `Quick 355 - (test_instruction_decode_via_command instr_vectors); 356 - ] ); 357 - ( "rmap-crc8", 358 - [ 359 - Alcotest.test_case "rmap_crc matches reference" `Quick 360 - (test_crc crc_vectors); 361 - Alcotest.test_case "rmap_crc_bytes matches reference" `Quick 362 - (test_crc_bytes crc_vectors); 363 - ] ); 364 - ]