CCSDS Proximity-1 Space Link Protocol (211.0-B)
0
fork

Configure Feed

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

Remove fake interop tests (FSR, Proximity-1, SDLS)

These tests used pure Python generators that reimplemented the spec
encoding — violating the interop testing principle that the oracle
must be an independent implementation, not a transcription.

A Python script that encodes the same spec our OCaml code does is
just a unit test with extra steps. It validates nothing about
cross-implementation compatibility.

Real interop tests require actual external tools:
- FSR/SDLS: NASA CryptoLib (Docker)
- Proximity-1: no known public oracle (skip until one exists)

-327
-19
test/interop/python/dune
··· 1 - (test 2 - (name test) 3 - (libraries proximity1 alcotest) 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 ./generate.py))))
-138
test/interop/python/scripts/generate.py
··· 1 - #!/usr/bin/env python3 2 - """Generate Proximity-1 interop traces for ocaml-proximity1. 3 - 4 - Oracle: Python (no deps needed for frame header encoding) 5 - Install: no dependencies 6 - 7 - Tests Proximity-1 Transfer Frame encoding/decoding per CCSDS 211.0-B-5. 8 - 9 - Frame Layout (7-byte header + variable data): 10 - Bytes 0-1 (U16 big-endian): 11 - Bits 15-13: Version Number (3 bits) 12 - Bits 12-5: Spacecraft ID (8 bits) 13 - Bits 4-2: Frame Type (3 bits, 0=Data 1=Ack 2=Expedited) 14 - Bits 1-0: Reserved (2 bits, always 0) 15 - Byte 2: Sequence Count high (top 8 of 24 bits) 16 - Bytes 3-4: Sequence Count low (bottom 16 of 24 bits, U16 big-endian) 17 - Bytes 5-6: Frame Length (total frame size in bytes, U16 big-endian) 18 - Bytes 7+: Data (frame_length - 7 bytes) 19 - 20 - Traces are committed to git. Only re-run when changing inputs 21 - or upgrading the oracle. 22 - """ 23 - import os, struct 24 - 25 - TRACE_DIR = os.path.join(os.path.dirname(__file__), "..", "traces") 26 - HEADER_SIZE = 7 27 - 28 - 29 - def encode_frame(version, scid, frame_type, sequence_count, data): 30 - """Encode a Proximity-1 frame to bytes per CCSDS 211.0-B-5.""" 31 - # First 16-bit word: Version(3) | SCID(8) | FrameType(3) | Reserved(2) 32 - word0 = 0 33 - word0 |= (version & 0x7) << 13 34 - word0 |= (scid & 0xFF) << 5 35 - word0 |= (frame_type & 0x7) << 2 36 - # reserved bits are 0 37 - 38 - seq_hi = (sequence_count >> 16) & 0xFF 39 - seq_lo = sequence_count & 0xFFFF 40 - frame_length = HEADER_SIZE + len(data) 41 - 42 - header = struct.pack(">HBH H", word0, seq_hi, seq_lo, frame_length) 43 - assert len(header) == HEADER_SIZE, f"header is {len(header)} bytes, expected {HEADER_SIZE}" 44 - return header + data 45 - 46 - 47 - # Frame type constants 48 - DATA = 0 49 - ACK = 1 50 - EXPEDITED = 2 51 - 52 - FRAME_TYPE_NAMES = {DATA: "Data", ACK: "Ack", EXPEDITED: "Expedited"} 53 - 54 - # Test vectors: various Proximity-1 frame configurations 55 - vectors = [ 56 - { 57 - "name": "data_hello", 58 - "version": 0, "scid": 42, "frame_type": DATA, 59 - "sequence_count": 1000, 60 - "data": b"hello", 61 - }, 62 - { 63 - "name": "ack_minimal", 64 - "version": 0, "scid": 100, "frame_type": ACK, 65 - "sequence_count": 0, 66 - "data": b"\x01", 67 - }, 68 - { 69 - "name": "expedited_max_seq", 70 - "version": 0, "scid": 255, "frame_type": EXPEDITED, 71 - "sequence_count": 0xFFFFFF, 72 - "data": b"\xff" * 100, 73 - }, 74 - { 75 - "name": "data_all_zeros", 76 - "version": 0, "scid": 0, "frame_type": DATA, 77 - "sequence_count": 0, 78 - "data": b"", 79 - }, 80 - { 81 - "name": "data_large_scid", 82 - "version": 0, "scid": 200, "frame_type": DATA, 83 - "sequence_count": 50000, 84 - "data": b"\xde\xad\xbe\xef", 85 - }, 86 - { 87 - "name": "ack_mid_seq", 88 - "version": 0, "scid": 77, "frame_type": ACK, 89 - "sequence_count": 123456, 90 - "data": b"\xaa\xbb", 91 - }, 92 - { 93 - "name": "data_seq_boundary", 94 - "version": 0, "scid": 1, "frame_type": DATA, 95 - "sequence_count": 0x010000, 96 - "data": b"boundary", 97 - }, 98 - { 99 - "name": "expedited_scid_128", 100 - "version": 0, "scid": 128, "frame_type": EXPEDITED, 101 - "sequence_count": 999999, 102 - "data": b"expedited payload", 103 - }, 104 - { 105 - "name": "data_binary_payload", 106 - "version": 0, "scid": 50, "frame_type": DATA, 107 - "sequence_count": 7, 108 - "data": bytes(range(256)), 109 - }, 110 - { 111 - "name": "version_nonzero", 112 - "version": 3, "scid": 10, "frame_type": DATA, 113 - "sequence_count": 42, 114 - "data": b"v3", 115 - }, 116 - ] 117 - 118 - os.makedirs(TRACE_DIR, exist_ok=True) 119 - with open(os.path.join(TRACE_DIR, "vectors.csv"), "w") as f: 120 - f.write("# Proximity-1 interop test vectors\n") 121 - f.write("# Oracle: Python (CCSDS 211.0-B-5 reference implementation)\n") 122 - f.write("# Format: name,version,scid,frame_type,sequence_count,data_hex,encoded_hex\n") 123 - for v in vectors: 124 - encoded = encode_frame( 125 - v["version"], v["scid"], v["frame_type"], 126 - v["sequence_count"], v["data"] 127 - ) 128 - f.write( 129 - f"{v['name']}," 130 - f"{v['version']}," 131 - f"{v['scid']}," 132 - f"{v['frame_type']}," 133 - f"{v['sequence_count']}," 134 - f"{v['data'].hex()}," 135 - f"{encoded.hex()}\n" 136 - ) 137 - 138 - print(f"Wrote {TRACE_DIR}/vectors.csv ({len(vectors)} vectors)")
-9
test/interop/python/scripts/generate.sh
··· 1 - #!/bin/bash 2 - set -euo pipefail 3 - SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" 4 - TRACE_DIR="$SCRIPT_DIR/../traces" 5 - mkdir -p "$TRACE_DIR" 6 - TRACE_DIR="$(cd "$TRACE_DIR" && pwd)" 7 - 8 - cd "$SCRIPT_DIR" 9 - python3 generate.py "$TRACE_DIR"
-148
test/interop/python/test.ml
··· 1 - (** Python interop tests for ocaml-proximity1. 2 - 3 - Tests Proximity-1 Transfer Frame encoding/decoding against a Python 4 - reference implementation of CCSDS 211.0-B-5. 5 - 6 - Traces generated by: Python (CCSDS 211.0-B-5 reference implementation) 7 - Regenerate: REGEN_TRACES=1 dune build @regen-traces *) 8 - 9 - let trace path = Filename.concat "traces" path 10 - 11 - (* Parse CSV trace: skip comments (#), split on comma *) 12 - let parse_csv path = 13 - let ic = open_in (trace path) in 14 - let lines = ref [] in 15 - (try 16 - while true do 17 - let line = input_line ic in 18 - if String.length line > 0 && line.[0] <> '#' then 19 - lines := String.split_on_char ',' line :: !lines 20 - done 21 - with End_of_file -> ()); 22 - close_in ic; 23 - List.rev !lines 24 - 25 - let bytes_of_hex hex = 26 - let len = String.length hex / 2 in 27 - String.init len (fun i -> 28 - Char.chr (int_of_string ("0x" ^ String.sub hex (i * 2) 2))) 29 - 30 - let hex_of_string s = 31 - let buf = Buffer.create (String.length s * 2) in 32 - String.iter 33 - (fun c -> Buffer.add_string buf (Printf.sprintf "%02x" (Char.code c))) 34 - s; 35 - Buffer.contents buf 36 - 37 - type vector = { 38 - name : string; 39 - version : int; 40 - scid : int; 41 - frame_type : int; 42 - sequence_count : int; 43 - data_hex : string; 44 - encoded : string; 45 - } 46 - 47 - let parse_vectors () = 48 - let rows = parse_csv "vectors.csv" in 49 - List.filter_map 50 - (fun row -> 51 - match row with 52 - | [ 53 - name; version; scid; frame_type; sequence_count; data_hex; encoded_hex; 54 - ] -> 55 - Some 56 - { 57 - name; 58 - version = int_of_string version; 59 - scid = int_of_string scid; 60 - frame_type = int_of_string frame_type; 61 - sequence_count = int_of_string sequence_count; 62 - data_hex; 63 - encoded = bytes_of_hex encoded_hex; 64 - } 65 - | _ -> Alcotest.failf "bad CSV row: %s" (String.concat "," row)) 66 - rows 67 - 68 - let frame_type_of_int n = 69 - match Proximity1.frame_type_of_int n with 70 - | Some ft -> ft 71 - | None -> Alcotest.failf "invalid frame type: %d" n 72 - 73 - let test_encode vec () = 74 - let ft = frame_type_of_int vec.frame_type in 75 - let data = bytes_of_hex vec.data_hex in 76 - let frame = 77 - Proximity1. 78 - { 79 - version = vec.version; 80 - scid = vec.scid; 81 - frame_type = ft; 82 - sequence_count = vec.sequence_count; 83 - data; 84 - } 85 - in 86 - let got = Proximity1.encode frame in 87 - if got <> vec.encoded then 88 - Alcotest.failf "%s: encode mismatch\n expected: %s\n got: %s" 89 - vec.name 90 - (hex_of_string vec.encoded) 91 - (hex_of_string got) 92 - 93 - let test_decode vec () = 94 - match Proximity1.decode vec.encoded with 95 - | Error `Truncated -> Alcotest.failf "%s: decode returned Truncated" vec.name 96 - | Error (`Invalid_frame_type n) -> 97 - Alcotest.failf "%s: decode returned Invalid_frame_type %d" vec.name n 98 - | Ok frame -> 99 - Alcotest.(check int) (vec.name ^ ": version") vec.version frame.version; 100 - Alcotest.(check int) (vec.name ^ ": scid") vec.scid frame.scid; 101 - Alcotest.(check int) 102 - (vec.name ^ ": frame_type") 103 - vec.frame_type 104 - (Proximity1.int_of_frame_type frame.frame_type); 105 - Alcotest.(check int) 106 - (vec.name ^ ": sequence_count") 107 - vec.sequence_count frame.sequence_count; 108 - let expected_data = bytes_of_hex vec.data_hex in 109 - Alcotest.(check string) (vec.name ^ ": data") expected_data frame.data 110 - 111 - let test_roundtrip vec () = 112 - let ft = frame_type_of_int vec.frame_type in 113 - let data = bytes_of_hex vec.data_hex in 114 - let frame = 115 - Proximity1. 116 - { 117 - version = vec.version; 118 - scid = vec.scid; 119 - frame_type = ft; 120 - sequence_count = vec.sequence_count; 121 - data; 122 - } 123 - in 124 - let encoded = Proximity1.encode frame in 125 - match Proximity1.decode encoded with 126 - | Error _ -> Alcotest.failf "%s: roundtrip decode failed" vec.name 127 - | Ok decoded -> 128 - if not (Proximity1.equal frame decoded) then 129 - Alcotest.failf "%s: roundtrip mismatch\n original: %a\n decoded: %a" 130 - vec.name Proximity1.pp frame Proximity1.pp decoded 131 - 132 - let () = 133 - let vectors = parse_vectors () in 134 - Alcotest.run "proximity1-interop" 135 - [ 136 - ( "encode", 137 - List.map 138 - (fun v -> Alcotest.test_case v.name `Quick (test_encode v)) 139 - vectors ); 140 - ( "decode", 141 - List.map 142 - (fun v -> Alcotest.test_case v.name `Quick (test_decode v)) 143 - vectors ); 144 - ( "roundtrip", 145 - List.map 146 - (fun v -> Alcotest.test_case v.name `Quick (test_roundtrip v)) 147 - vectors ); 148 - ]
-13
test/interop/python/traces/vectors.csv
··· 1 - # Proximity-1 interop test vectors 2 - # Oracle: Python (CCSDS 211.0-B-5 reference implementation) 3 - # Format: name,version,scid,frame_type,sequence_count,data_hex,encoded_hex 4 - data_hello,0,42,0,1000,68656c6c6f,05400003e8000c68656c6c6f 5 - ack_minimal,0,100,1,0,01,0c84000000000801 6 - expedited_max_seq,0,255,2,16777215,ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff,1fe8ffffff006bffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff 7 - data_all_zeros,0,0,0,0,,00000000000007 8 - data_large_scid,0,200,0,50000,deadbeef,190000c350000bdeadbeef 9 - ack_mid_seq,0,77,1,123456,aabb,09a401e2400009aabb 10 - data_seq_boundary,0,1,0,65536,626f756e64617279,0020010000000f626f756e64617279 11 - expedited_scid_128,0,128,2,999999,657870656469746564207061796c6f6164,10080f423f0018657870656469746564207061796c6f6164 12 - data_binary_payload,0,50,0,7,000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1a2a3a4a5a6a7a8a9aaabacadaeafb0b1b2b3b4b5b6b7b8b9babbbcbdbebfc0c1c2c3c4c5c6c7c8c9cacbcccdcecfd0d1d2d3d4d5d6d7d8d9dadbdcdddedfe0e1e2e3e4e5e6e7e8e9eaebecedeeeff0f1f2f3f4f5f6f7f8f9fafbfcfdfeff,06400000070107000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1a2a3a4a5a6a7a8a9aaabacadaeafb0b1b2b3b4b5b6b7b8b9babbbcbdbebfc0c1c2c3c4c5c6c7c8c9cacbcccdcecfd0d1d2d3d4d5d6d7d8d9dadbdcdddedfe0e1e2e3e4e5e6e7e8e9eaebecedeeeff0f1f2f3f4f5f6f7f8f9fafbfcfdfeff 13 - version_nonzero,3,10,0,42,7633,614000002a00097633