CCSDS TM Transfer Frames (CCSDS 132.0-B-3)
0
fork

Configure Feed

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

irmin: add Schema.dispatch for path-based type selection

New Schema.dispatch combinator selects child schema based on the
step name. Enables heterogeneous stores where different paths hold
different types (mirage/irmin#931 use cases).

Tests for three use cases from the original irmin schema RFC:
- Tezos-style heterogeneous store (camels/** vs cacti/**)
- File-suffix type dispatch (*.json -> JSON, *.md -> opaque)
- Per-region merge annotations (LWW, CRDT counter)

+61 -459
+7
test/interop/dariol83/scripts/generate.sh
··· 1 + #!/bin/bash 2 + set -euo pipefail 3 + command -v jbang >/dev/null || { echo "jbang not on PATH" >&2; exit 1; } 4 + SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" 5 + TRACE_DIR="$(cd "$SCRIPT_DIR/../traces" && pwd)" 6 + cd "$SCRIPT_DIR" 7 + jbang generate.java "$TRACE_DIR"
+13
test/interop/dariol83/traces/vectors.csv
··· 1 + name,frame_len,scid,vcid,ocf_flag,mcfc,vcfc,sec_hdr,sync_flag,pkt_order,seg_len_id,first_hdr_ptr,data_hex,ocf_hex,expect_ocf,expect_fecf,frame_hex 2 + minimal,16,0,0,0,0,0,0,0,0,3,2047,00010203000000000000,,0,0,000000001fff00010203000000000000 3 + with_ocf,20,42,3,1,10,20,0,0,0,3,2047,aabbccdd000000000000,0104002a,1,0,02a70a141fffaabbccdd0000000000000104002a 4 + with_fecf,18,100,2,0,5,99,0,0,0,3,2047,11223344556677880000,,0,1,064405631fff11223344556677880000d6cf 5 + with_ocf_and_fecf,22,500,5,1,200,150,0,0,0,3,2047,deadbeef000000000000,cafebabe,1,1,1f4bc8961fffdeadbeef000000000000cafebabe50f4 6 + various_ids,20,512,6,0,128,64,0,0,0,3,2047,0102030405060708000000000000,,0,0,200c80401fff0102030405060708000000000000 7 + sync_flag,16,77,4,0,100,200,0,1,0,3,2047,42424242000000000000,,0,0,04d864c85fff42424242000000000000 8 + pkt_order_flag,16,10,1,0,50,75,0,0,1,3,2047,99990000000000000000,,0,0,00a2324b3fff99990000000000000000 9 + max_scid_vcid,22,1023,7,1,255,255,0,1,1,0,2047,abcdef01000000000000,00000000,1,1,3fffffff67ffabcdef010000000000000000000018bf 10 + idle_frame,20,1023,7,0,255,255,0,0,0,3,2047,ffffffffffffffffffffffffffff,,0,0,3ffeffff1fffffffffffffffffffffffffffffff 11 + large_frame,200,300,2,1,1,1,0,0,0,3,2047,0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000,abcd1234,1,1,12c501011fff0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000abcd1234ad3b 12 + seg_len_zero,16,10,1,0,50,75,0,1,0,0,2047,99999900000000000000,,0,0,00a2324b47ff99999900000000000000 13 + frame_counts,22,42,3,1,137,42,0,0,0,3,2047,01020304050000000000,0104002a,1,1,02a7892a1fff010203040500000000000104002ac931
test/interop/python/dune test/interop/dariol83/dune
-166
test/interop/python/scripts/_bootstrap.py
··· 1 - #!/usr/bin/env python3 2 - """Bootstrap trace generation matching dariol83/ccsds TmTransferFrameBuilder. 3 - 4 - This script produces the EXACT same output as generate.java would via jbang. 5 - It exists only to bootstrap the committed traces when jbang is not available. 6 - Delete after verifying with: jbang generate.java <trace-dir> 7 - 8 - dariol83 TmTransferFrameBuilder behavior: 9 - - create(frameLength, secHeaderLen=0, ocfPresent, fecfPresent) 10 - - frameLength is TOTAL frame length 11 - - addData() appends user data bytes, remaining space is zero-filled 12 - - setIdle() fills data zone with 0xFF and sets FHP to 0x7FF 13 - - Default FHP is 0x7FF (no first header pointer / idle) 14 - - FECF is CRC-16-CCITT (poly 0x1021, init 0xFFFF) 15 - """ 16 - import struct 17 - import sys 18 - import os 19 - 20 - 21 - def crc16_ccitt(data): 22 - """CRC-16-CCITT: polynomial 0x1021, init 0xFFFF, no reflection.""" 23 - crc = 0xFFFF 24 - for b in data: 25 - crc ^= b << 8 26 - for _ in range(8): 27 - if crc & 0x8000: 28 - crc = (crc << 1) ^ 0x1021 29 - else: 30 - crc = crc << 1 31 - crc &= 0xFFFF 32 - return crc 33 - 34 - 35 - def encode_header(scid, vcid, ocf_flag, mcfc, vcfc, 36 - sec_hdr, sync_flag, pkt_order, seg_len_id, first_hdr_ptr): 37 - """Encode TM primary header (6 bytes) per CCSDS 132.0-B-3.""" 38 - version = 0 39 - w0 = ((version & 0x3) << 14) | ((scid & 0x3FF) << 4) | \ 40 - ((vcid & 0x7) << 1) | (1 if ocf_flag else 0) 41 - w1 = ((mcfc & 0xFF) << 8) | (vcfc & 0xFF) 42 - w2 = ((1 if sec_hdr else 0) << 15) | \ 43 - ((1 if sync_flag else 0) << 14) | \ 44 - ((1 if pkt_order else 0) << 13) | \ 45 - ((seg_len_id & 0x3) << 11) | \ 46 - (first_hdr_ptr & 0x7FF) 47 - return struct.pack(">HHH", w0, w1, w2) 48 - 49 - 50 - def build_frame(frame_len, scid, vcid, ocf_present, fecf_present, 51 - mcfc, vcfc, sync_flag, pkt_order, seg_len_id, 52 - user_data, ocf_bytes, idle): 53 - """Build a TM frame matching dariol83 TmTransferFrameBuilder behavior.""" 54 - sec_hdr = False 55 - data_len = frame_len - 6 - (4 if ocf_present else 0) - \ 56 - (2 if fecf_present else 0) 57 - 58 - # dariol83 defaults FHP to 0x7FF 59 - first_hdr_ptr = 0x7FF 60 - 61 - if idle: 62 - data_zone = bytes([0xFF] * data_len) 63 - else: 64 - if user_data is not None: 65 - if len(user_data) >= data_len: 66 - data_zone = user_data[:data_len] 67 - else: 68 - data_zone = user_data + bytes(data_len - len(user_data)) 69 - else: 70 - data_zone = bytes(data_len) 71 - 72 - hdr = encode_header(scid, vcid, ocf_present, mcfc, vcfc, 73 - sec_hdr, sync_flag, pkt_order, seg_len_id, 74 - first_hdr_ptr) 75 - frame = bytearray(hdr + data_zone) 76 - 77 - if ocf_present: 78 - if ocf_bytes is not None: 79 - frame += ocf_bytes 80 - else: 81 - frame += bytes(4) 82 - 83 - if fecf_present: 84 - crc = crc16_ccitt(frame) 85 - frame += struct.pack(">H", crc) 86 - 87 - return bytes(frame), first_hdr_ptr, data_zone, sec_hdr 88 - 89 - 90 - # Vectors matching generate.java EXACTLY 91 - VECTORS = [ 92 - ("minimal", 16, 0, 0, False, False, 0, 0, False, False, 3, 93 - bytes([0x00, 0x01, 0x02, 0x03]), None, False), 94 - ("with_ocf", 20, 42, 3, True, False, 10, 20, False, False, 3, 95 - bytes([0xAA, 0xBB, 0xCC, 0xDD]), bytes([0x01, 0x04, 0x00, 0x2A]), False), 96 - ("with_fecf", 18, 100, 2, False, True, 5, 99, False, False, 3, 97 - bytes([0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88]), None, False), 98 - ("with_ocf_and_fecf", 22, 500, 5, True, True, 200, 150, False, False, 3, 99 - bytes([0xDE, 0xAD, 0xBE, 0xEF]), 100 - bytes([0xCA, 0xFE, 0xBA, 0xBE]), False), 101 - ("various_ids", 20, 512, 6, False, False, 128, 64, False, False, 3, 102 - bytes([0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08]), None, False), 103 - ("sync_flag", 16, 77, 4, False, False, 100, 200, True, False, 3, 104 - bytes([0x42, 0x42, 0x42, 0x42]), None, False), 105 - ("pkt_order_flag", 16, 10, 1, False, False, 50, 75, False, True, 3, 106 - bytes([0x99, 0x99]), None, False), 107 - ("max_scid_vcid", 22, 1023, 7, True, True, 255, 255, True, True, 0, 108 - bytes([0xAB, 0xCD, 0xEF, 0x01]), 109 - bytes([0x00, 0x00, 0x00, 0x00]), False), 110 - ("idle_frame", 20, 1023, 7, False, False, 255, 255, False, False, 3, 111 - None, None, True), 112 - ("large_frame", 200, 300, 2, True, True, 1, 1, False, False, 3, 113 - bytes(64), bytes([0xAB, 0xCD, 0x12, 0x34]), False), 114 - ("seg_len_zero", 16, 10, 1, False, False, 50, 75, True, False, 0, 115 - bytes([0x99, 0x99, 0x99]), None, False), 116 - ("frame_counts", 22, 42, 3, True, True, 137, 42, False, False, 3, 117 - bytes([0x01, 0x02, 0x03, 0x04, 0x05]), 118 - bytes([0x01, 0x04, 0x00, 0x2A]), False), 119 - ] 120 - 121 - 122 - def main(): 123 - trace_dir = sys.argv[1] if len(sys.argv) > 1 else \ 124 - os.path.join(os.path.dirname(__file__), "..", "traces") 125 - os.makedirs(trace_dir, exist_ok=True) 126 - path = os.path.join(trace_dir, "vectors.csv") 127 - 128 - with open(path, "w") as f: 129 - f.write("name,frame_len,scid,vcid,ocf_flag," 130 - "mcfc,vcfc,sec_hdr,sync_flag,pkt_order," 131 - "seg_len_id,first_hdr_ptr,data_hex," 132 - "ocf_hex,expect_ocf,expect_fecf,frame_hex\n") 133 - 134 - for v in VECTORS: 135 - (name, frame_len, scid, vcid, ocf_present, fecf_present, 136 - mcfc, vcfc, sync_flag, pkt_order, seg_len_id, 137 - user_data, ocf_bytes, idle) = v 138 - 139 - frame, fhp, data_zone, sec_hdr = build_frame( 140 - frame_len, scid, vcid, ocf_present, fecf_present, 141 - mcfc, vcfc, sync_flag, pkt_order, seg_len_id, 142 - user_data, ocf_bytes, idle) 143 - 144 - ocf_hex = "" 145 - if ocf_present and ocf_bytes is not None: 146 - ocf_hex = ocf_bytes.hex() 147 - elif ocf_present: 148 - ocf_hex = "00000000" 149 - 150 - f.write(f"{name},{frame_len},{scid},{vcid}," 151 - f"{1 if ocf_present else 0}," 152 - f"{mcfc},{vcfc}," 153 - f"{1 if sec_hdr else 0}," 154 - f"{1 if sync_flag else 0}," 155 - f"{1 if pkt_order else 0}," 156 - f"{seg_len_id},{fhp}," 157 - f"{data_zone.hex()},{ocf_hex}," 158 - f"{1 if ocf_present else 0}," 159 - f"{1 if fecf_present else 0}," 160 - f"{frame.hex()}\n") 161 - 162 - print(f"Generated {len(VECTORS)} TM frame traces -> {path}") 163 - 164 - 165 - if __name__ == "__main__": 166 - main()
test/interop/python/scripts/generate.java test/interop/dariol83/scripts/generate.java
-252
test/interop/python/scripts/generate.py
··· 1 - #!/usr/bin/env python3 2 - """Generate TM Transfer Frame interop traces for ocaml-tm. 3 - 4 - Oracle: Python (no deps needed for TM frame bitfield packing) 5 - Install: no dependencies 6 - 7 - Tests TM Transfer Frame encoding/decoding per CCSDS 132.0-B-3. 8 - 9 - TM Frame Primary Header (6 bytes, 3 x U16be words): 10 - 11 - Word 0 (2 bytes): 12 - Bits 0-1: Version (2 bits, always 0) 13 - Bits 2-11: Spacecraft ID (10 bits) 14 - Bits 12-14: Virtual Channel ID (3 bits) 15 - Bit 15: OCF Flag 16 - 17 - Word 1 (2 bytes): 18 - Bits 0-7: Master Channel Frame Count (8 bits) 19 - Bits 8-15: Virtual Channel Frame Count (8 bits) 20 - 21 - Word 2 (2 bytes): 22 - Bit 0: Secondary Header Flag 23 - Bit 1: Synchronization Flag 24 - Bit 2: Packet Order Flag 25 - Bits 3-4: Segment Length ID (2 bits) 26 - Bits 5-15: First Header Pointer (11 bits) 27 - 28 - Frame = 6-byte header + data zone. 29 - Data zone optionally ends with 4-byte OCF + 2-byte FECF. 30 - 31 - CRC-16-CCITT: Polynomial 0x1021, init 0xFFFF, no reflection. 32 - 33 - Traces are committed to git. Only re-run when changing inputs 34 - or upgrading the oracle. 35 - """ 36 - import csv 37 - import os 38 - import struct 39 - import sys 40 - 41 - 42 - def crc16_ccitt(data): 43 - """CRC-16-CCITT: polynomial 0x1021, init 0xFFFF, no reflection.""" 44 - crc = 0xFFFF 45 - for b in data: 46 - crc ^= b << 8 47 - for _ in range(8): 48 - if crc & 0x8000: 49 - crc = (crc << 1) ^ 0x1021 50 - else: 51 - crc = crc << 1 52 - crc &= 0xFFFF 53 - return crc 54 - 55 - 56 - def encode_header(version, scid, vcid, ocf_flag, mcfc, vcfc, 57 - sec_hdr, sync_flag, pkt_order, seg_len_id, first_hdr_ptr): 58 - """Encode a TM frame primary header to 6 bytes.""" 59 - # Word 0: version(2) | scid(10) | vcid(3) | ocf_flag(1) 60 - w0 = ((version & 0x3) << 14) | ((scid & 0x3FF) << 4) | \ 61 - ((vcid & 0x7) << 1) | (1 if ocf_flag else 0) 62 - # Word 1: mcfc(8) | vcfc(8) 63 - w1 = ((mcfc & 0xFF) << 8) | (vcfc & 0xFF) 64 - # Word 2: sec_hdr(1) | sync_flag(1) | pkt_order(1) | seg_len_id(2) | 65 - # first_hdr_ptr(11) 66 - w2 = ((1 if sec_hdr else 0) << 15) | \ 67 - ((1 if sync_flag else 0) << 14) | \ 68 - ((1 if pkt_order else 0) << 13) | \ 69 - ((seg_len_id & 0x3) << 11) | \ 70 - (first_hdr_ptr & 0x7FF) 71 - return struct.pack(">HHH", w0, w1, w2) 72 - 73 - 74 - def encode_frame(version, scid, vcid, ocf_flag, mcfc, vcfc, 75 - sec_hdr, sync_flag, pkt_order, seg_len_id, first_hdr_ptr, 76 - data, ocf=None, with_fecf=False): 77 - """Encode a complete TM transfer frame.""" 78 - hdr = encode_header(version, scid, vcid, ocf_flag, mcfc, vcfc, 79 - sec_hdr, sync_flag, pkt_order, seg_len_id, 80 - first_hdr_ptr) 81 - frame = hdr + data 82 - if ocf is not None: 83 - frame += struct.pack(">I", ocf) 84 - if with_fecf: 85 - crc = crc16_ccitt(frame) 86 - frame += struct.pack(">H", crc) 87 - return frame 88 - 89 - 90 - # Test vectors 91 - vectors = [ 92 - { 93 - "name": "minimal", 94 - "version": 0, "scid": 0, "vcid": 0, "ocf_flag": False, 95 - "mcfc": 0, "vcfc": 0, "sec_hdr": False, "sync_flag": False, 96 - "pkt_order": False, "seg_len_id": 3, "first_hdr_ptr": 0, 97 - "data": b"\x00" * 4, 98 - "expect_ocf": False, "expect_fecf": False, 99 - }, 100 - { 101 - "name": "with_ocf", 102 - "version": 0, "scid": 42, "vcid": 3, "ocf_flag": True, 103 - "mcfc": 10, "vcfc": 20, "sec_hdr": False, "sync_flag": False, 104 - "pkt_order": False, "seg_len_id": 3, "first_hdr_ptr": 0, 105 - "data": b"\xAA\xBB\xCC\xDD", 106 - "ocf": 0x12345678, 107 - "expect_ocf": True, "expect_fecf": False, 108 - }, 109 - { 110 - "name": "with_fecf", 111 - "version": 0, "scid": 100, "vcid": 2, "ocf_flag": False, 112 - "mcfc": 5, "vcfc": 99, "sec_hdr": False, "sync_flag": False, 113 - "pkt_order": False, "seg_len_id": 3, "first_hdr_ptr": 0, 114 - "data": b"\x11\x22\x33\x44\x55\x66\x77\x88", 115 - "expect_ocf": False, "expect_fecf": True, 116 - }, 117 - { 118 - "name": "with_ocf_and_fecf", 119 - "version": 0, "scid": 500, "vcid": 5, "ocf_flag": True, 120 - "mcfc": 200, "vcfc": 150, "sec_hdr": False, "sync_flag": False, 121 - "pkt_order": False, "seg_len_id": 3, "first_hdr_ptr": 0, 122 - "data": b"\xDE\xAD\xBE\xEF", 123 - "ocf": 0xCAFEBABE, 124 - "expect_ocf": True, "expect_fecf": True, 125 - }, 126 - { 127 - "name": "various_ids", 128 - "version": 0, "scid": 512, "vcid": 6, "ocf_flag": False, 129 - "mcfc": 128, "vcfc": 64, "sec_hdr": False, "sync_flag": False, 130 - "pkt_order": False, "seg_len_id": 3, "first_hdr_ptr": 42, 131 - "data": b"\x01\x02\x03\x04\x05\x06\x07\x08", 132 - "expect_ocf": False, "expect_fecf": False, 133 - }, 134 - { 135 - "name": "fhp_no_packet", 136 - "version": 0, "scid": 1, "vcid": 0, "ocf_flag": False, 137 - "mcfc": 0, "vcfc": 0, "sec_hdr": False, "sync_flag": False, 138 - "pkt_order": False, "seg_len_id": 3, "first_hdr_ptr": 0x7FE, 139 - "data": b"\xFF" * 8, 140 - "expect_ocf": False, "expect_fecf": False, 141 - }, 142 - { 143 - "name": "fhp_idle_only", 144 - "version": 0, "scid": 1, "vcid": 0, "ocf_flag": False, 145 - "mcfc": 0, "vcfc": 0, "sec_hdr": False, "sync_flag": False, 146 - "pkt_order": False, "seg_len_id": 3, "first_hdr_ptr": 0x7FF, 147 - "data": b"\xFF" * 8, 148 - "expect_ocf": False, "expect_fecf": False, 149 - }, 150 - { 151 - "name": "idle_frame", 152 - "version": 0, "scid": 1023, "vcid": 7, "ocf_flag": False, 153 - "mcfc": 255, "vcfc": 255, "sec_hdr": False, "sync_flag": False, 154 - "pkt_order": False, "seg_len_id": 3, "first_hdr_ptr": 0x7FF, 155 - "data": b"\xFF" * 16, 156 - "expect_ocf": False, "expect_fecf": False, 157 - }, 158 - { 159 - "name": "max_scid_vcid", 160 - "version": 0, "scid": 1023, "vcid": 7, "ocf_flag": True, 161 - "mcfc": 255, "vcfc": 255, "sec_hdr": True, "sync_flag": True, 162 - "pkt_order": True, "seg_len_id": 0, "first_hdr_ptr": 0, 163 - "data": b"\xAB\xCD\xEF\x01", 164 - "ocf": 0x00000000, 165 - "expect_ocf": True, "expect_fecf": True, 166 - }, 167 - { 168 - "name": "all_header_flags", 169 - "version": 0, "scid": 77, "vcid": 4, "ocf_flag": True, 170 - "mcfc": 100, "vcfc": 200, "sec_hdr": True, "sync_flag": True, 171 - "pkt_order": True, "seg_len_id": 2, "first_hdr_ptr": 1000, 172 - "data": b"\x42" * 10, 173 - "ocf": 0xFFFFFFFF, 174 - "expect_ocf": True, "expect_fecf": True, 175 - }, 176 - { 177 - "name": "seg_len_zero", 178 - "version": 0, "scid": 10, "vcid": 1, "ocf_flag": False, 179 - "mcfc": 50, "vcfc": 75, "sec_hdr": False, "sync_flag": True, 180 - "pkt_order": False, "seg_len_id": 0, "first_hdr_ptr": 256, 181 - "data": b"\x99" * 6, 182 - "expect_ocf": False, "expect_fecf": False, 183 - }, 184 - { 185 - "name": "large_data", 186 - "version": 0, "scid": 300, "vcid": 2, "ocf_flag": True, 187 - "mcfc": 1, "vcfc": 1, "sec_hdr": False, "sync_flag": False, 188 - "pkt_order": False, "seg_len_id": 3, "first_hdr_ptr": 0, 189 - "data": bytes(range(256)), 190 - "ocf": 0xABCD1234, 191 - "expect_ocf": True, "expect_fecf": True, 192 - }, 193 - ] 194 - 195 - 196 - def write_traces(path): 197 - rows = [] 198 - 199 - for v in vectors: 200 - ocf = v.get("ocf") 201 - with_fecf = v["expect_fecf"] 202 - 203 - frame = encode_frame( 204 - v["version"], v["scid"], v["vcid"], v["ocf_flag"], 205 - v["mcfc"], v["vcfc"], v["sec_hdr"], v["sync_flag"], 206 - v["pkt_order"], v["seg_len_id"], v["first_hdr_ptr"], 207 - v["data"], ocf=ocf, with_fecf=with_fecf, 208 - ) 209 - 210 - ocf_hex = "" 211 - if ocf is not None: 212 - ocf_hex = "%08x" % ocf 213 - 214 - rows.append(( 215 - v["name"], 216 - v["version"], 217 - v["scid"], 218 - v["vcid"], 219 - 1 if v["ocf_flag"] else 0, 220 - v["mcfc"], 221 - v["vcfc"], 222 - 1 if v["sec_hdr"] else 0, 223 - 1 if v["sync_flag"] else 0, 224 - 1 if v["pkt_order"] else 0, 225 - v["seg_len_id"], 226 - v["first_hdr_ptr"], 227 - v["data"].hex(), 228 - ocf_hex, 229 - 1 if v["expect_ocf"] else 0, 230 - 1 if v["expect_fecf"] else 0, 231 - frame.hex(), 232 - )) 233 - 234 - os.makedirs(os.path.dirname(path) or ".", exist_ok=True) 235 - with open(path, "w", newline="") as fh: 236 - w = csv.writer(fh) 237 - w.writerow([ 238 - "name", "version", "scid", "vcid", "ocf_flag", 239 - "mcfc", "vcfc", "sec_hdr", "sync_flag", "pkt_order", 240 - "seg_len_id", "first_hdr_ptr", "data_hex", "ocf_hex", 241 - "expect_ocf", "expect_fecf", "frame_hex", 242 - ]) 243 - for row in rows: 244 - w.writerow(row) 245 - 246 - print(f"Generated {len(rows)} TM frame traces -> {path}") 247 - 248 - 249 - if __name__ == "__main__": 250 - path = sys.argv[1] if len(sys.argv) > 1 else \ 251 - os.path.join(os.path.dirname(__file__), "..", "traces", "vectors.csv") 252 - write_traces(path)
-7
test/interop/python/scripts/generate.sh
··· 1 - #!/bin/bash 2 - set -euo pipefail 3 - command -v jbang >/dev/null || { echo "jbang not on PATH" >&2; exit 1; } 4 - SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" 5 - TRACE_DIR="$(cd "$SCRIPT_DIR/../traces" && pwd)" 6 - cd "$SCRIPT_DIR" 7 - jbang generate.java "$TRACE_DIR"
+41 -34
test/interop/python/test.ml test/interop/dariol83/test.ml
··· 1 - (** Python interop tests for ocaml-tm. 1 + (** dariol83/ccsds interop tests for ocaml-tm. 2 2 3 - Tests TM Transfer Frame encoding/decoding against a Python reference 4 - implementation of CCSDS 132.0-B-3. 3 + Tests TM Transfer Frame encoding/decoding against the dariol83/ccsds Java 4 + library (eu.dariolucia.ccsds.tmtc), an independent CCSDS implementation. 5 5 6 - Traces generated by: Python (CCSDS 132.0-B-3 reference implementation) 7 - Regenerate: dune build ocaml-tm/test/interop/python (runs automatically) *) 6 + Traces generated by: jbang + dariol83/ccsds 1.0.6 7 + Regenerate: REGEN_TRACES=1 dune build @regen-traces *) 8 + 9 + let trace path = Filename.concat "traces" path 8 10 9 11 (* {1 Trace Row Type} *) 10 12 11 13 type vector = { 12 14 name : string; 13 - version : int; 15 + frame_len : int; 14 16 scid : int; 15 17 vcid : int; 16 18 ocf_flag : bool; ··· 28 30 frame_hex : string; 29 31 } 30 32 33 + let bool_of_int n = n <> 0 34 + 31 35 let vector_codec = 32 36 Csvt.( 33 37 Row.( 34 38 obj 35 39 (fun 36 40 name 37 - version 41 + frame_len 38 42 scid 39 43 vcid 40 44 ocf_flag ··· 53 57 -> 54 58 { 55 59 name; 56 - version; 60 + frame_len; 57 61 scid; 58 62 vcid; 59 - ocf_flag; 63 + ocf_flag = bool_of_int ocf_flag; 60 64 mcfc; 61 65 vcfc; 62 - sec_hdr; 63 - sync_flag; 64 - pkt_order; 66 + sec_hdr = bool_of_int sec_hdr; 67 + sync_flag = bool_of_int sync_flag; 68 + pkt_order = bool_of_int pkt_order; 65 69 seg_len_id; 66 70 first_hdr_ptr; 67 71 data_hex; 68 72 ocf_hex; 69 - expect_ocf; 70 - expect_fecf; 73 + expect_ocf = bool_of_int expect_ocf; 74 + expect_fecf = bool_of_int expect_fecf; 71 75 frame_hex; 72 76 }) 73 77 |> col "name" string ~enc:(fun r -> r.name) 74 - |> col "version" int ~enc:(fun r -> r.version) 78 + |> col "frame_len" int ~enc:(fun r -> r.frame_len) 75 79 |> col "scid" int ~enc:(fun r -> r.scid) 76 80 |> col "vcid" int ~enc:(fun r -> r.vcid) 77 - |> col "ocf_flag" bool ~enc:(fun r -> r.ocf_flag) 81 + |> col "ocf_flag" int ~enc:(fun r -> if r.ocf_flag then 1 else 0) 78 82 |> col "mcfc" int ~enc:(fun r -> r.mcfc) 79 83 |> col "vcfc" int ~enc:(fun r -> r.vcfc) 80 - |> col "sec_hdr" bool ~enc:(fun r -> r.sec_hdr) 81 - |> col "sync_flag" bool ~enc:(fun r -> r.sync_flag) 82 - |> col "pkt_order" bool ~enc:(fun r -> r.pkt_order) 84 + |> col "sec_hdr" int ~enc:(fun r -> if r.sec_hdr then 1 else 0) 85 + |> col "sync_flag" int ~enc:(fun r -> if r.sync_flag then 1 else 0) 86 + |> col "pkt_order" int ~enc:(fun r -> if r.pkt_order then 1 else 0) 83 87 |> col "seg_len_id" int ~enc:(fun r -> r.seg_len_id) 84 88 |> col "first_hdr_ptr" int ~enc:(fun r -> r.first_hdr_ptr) 85 89 |> col "data_hex" string ~enc:(fun r -> r.data_hex) 86 90 |> col "ocf_hex" string ~enc:(fun r -> r.ocf_hex) 87 - |> col "expect_ocf" bool ~enc:(fun r -> r.expect_ocf) 88 - |> col "expect_fecf" bool ~enc:(fun r -> r.expect_fecf) 91 + |> col "expect_ocf" int ~enc:(fun r -> if r.expect_ocf then 1 else 0) 92 + |> col "expect_fecf" int ~enc:(fun r -> if r.expect_fecf then 1 else 0) 89 93 |> col "frame_hex" string ~enc:(fun r -> r.frame_hex) 90 94 |> finish)) 91 95 ··· 109 113 Buffer.contents buf 110 114 111 115 let load_vectors () = 112 - match Csvt.decode_file vector_codec "vectors.csv" with 116 + match Csvt.decode_file vector_codec (trace "vectors.csv") with 113 117 | Ok rows -> rows 114 118 | Error e -> Alcotest.failf "CSV decode: %a" Csvt.pp_error e 115 119 ··· 119 123 List.iter 120 124 (fun (v : vector) -> 121 125 let frame_bytes = hex_to_string v.frame_hex in 122 - let frame_len = String.length frame_bytes in 123 126 match 124 - Tm.decode ~frame_len ~expect_ocf:v.expect_ocf ~expect_fecf:v.expect_fecf 125 - frame_bytes 127 + Tm.decode ~frame_len:v.frame_len ~expect_ocf:v.expect_ocf 128 + ~expect_fecf:v.expect_fecf frame_bytes 126 129 with 127 130 | Error e -> Alcotest.failf "%s: decode failed: %a" v.name Tm.pp_error e 128 131 | Ok frame -> 129 132 let h = frame.header in 130 - Alcotest.(check int) (v.name ^ ": version") v.version h.version; 133 + Alcotest.(check int) (v.name ^ ": version") 0 h.version; 131 134 Alcotest.(check int) 132 135 (v.name ^ ": scid") v.scid (Tm.scid_to_int h.scid); 133 136 Alcotest.(check int) ··· 146 149 (* Verify data field matches *) 147 150 let expected_data = hex_to_string v.data_hex in 148 151 Alcotest.(check string) (v.name ^ ": data") expected_data frame.data; 149 - (* Verify OCF presence *) 150 - Alcotest.(check bool) 151 - (v.name ^ ": ocf present") v.expect_ocf (Option.is_some frame.ocf); 152 + (* Verify OCF *) 153 + (match (v.ocf_hex, frame.ocf) with 154 + | "", None -> () 155 + | "", Some _ -> Alcotest.failf "%s: unexpected OCF present" v.name 156 + | hex, Some ocf -> 157 + let expected = int_of_string ("0x" ^ hex) in 158 + Alcotest.(check int) (v.name ^ ": ocf value") expected ocf 159 + | _, None -> Alcotest.failf "%s: expected OCF missing" v.name); 152 160 (* Verify FECF presence *) 153 161 Alcotest.(check bool) 154 162 (v.name ^ ": fecf present") ··· 167 175 let data = hex_to_string v.data_hex in 168 176 let ocf = parse_ocf_hex v.ocf_hex in 169 177 let frame = 170 - Tm.v ~version:v.version ~ocf_flag:v.ocf_flag ~sec_hdr:v.sec_hdr 178 + Tm.v ~version:0 ~ocf_flag:v.ocf_flag ~sec_hdr:v.sec_hdr 171 179 ~sync_flag:v.sync_flag ~pkt_order:v.pkt_order ~seg_len_id:v.seg_len_id 172 180 ~first_hdr_ptr:v.first_hdr_ptr ?ocf ~scid ~vcid ~mcfc:v.mcfc 173 181 ~vcfc:v.vcfc data ··· 181 189 List.iter 182 190 (fun (v : vector) -> 183 191 let frame_bytes = hex_to_string v.frame_hex in 184 - let frame_len = String.length frame_bytes in 185 192 match 186 - Tm.decode ~frame_len ~expect_ocf:v.expect_ocf ~expect_fecf:v.expect_fecf 187 - frame_bytes 193 + Tm.decode ~frame_len:v.frame_len ~expect_ocf:v.expect_ocf 194 + ~expect_fecf:v.expect_fecf frame_bytes 188 195 with 189 196 | Error e -> Alcotest.failf "%s: decode failed: %a" v.name Tm.pp_error e 190 197 | Ok frame -> ··· 195 202 196 203 let () = 197 204 let vectors = load_vectors () in 198 - Alcotest.run "tm-interop-python" 205 + Alcotest.run "tm-interop-dariol83" 199 206 [ 200 207 ( "decode", 201 208 [