CCSDS Frame Security Report (FSR)
0
fork

Configure Feed

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

Add CCSDS gap packages with spec test vectors

New packages:
- ocaml-tm-sync (131.0-B): TM randomizer (LFSR) with CCSDS PN
sequence test vector; RS/convolutional/LDPC/turbo stubs
- ocaml-proximity1 (211.0-B): Proximity-1 frame header Wire codec
with encode/decode and roundtrip tests

Tests with spec vectors (96 tests total):
- tm-sync: PN sequence from CCSDS 131.0-B-4 Annex B
- cltu: BCH(63,56) parity, CLTU/ASM sync parsers
- cop1: FOP-1/FARM-1 state machine transitions
- fsr: 32-bit FSR Wire codec with known bit patterns
- proximity1: frame header roundtrip for all frame types
- ccsds-time: CUC/CDS encode/decode with epoch constants

+197
+3
test/dune
··· 1 + (test 2 + (name test) 3 + (libraries fsr alcotest))
+1
test/test.ml
··· 1 + let () = Alcotest.run "fsr" [ Test_fsr.suite ]
+192
test/test_fsr.ml
··· 1 + (** Tests for Frame Security Report (FSR). 2 + 3 + Wire codec tests and bit-level validation per CCSDS 355.1-B-1 Section 4.2. 4 + *) 5 + 6 + let fsr = 7 + Alcotest.testable Fsr.pp (fun a b -> 8 + a.Fsr.alarm = b.Fsr.alarm && a.bad_sn = b.bad_sn && a.bad_mac = b.bad_mac 9 + && a.bad_sa = b.bad_sa && a.spi = b.spi && a.arsn_lsb = b.arsn_lsb) 10 + 11 + (* --- Roundtrip encode/decode --- *) 12 + 13 + (** Test roundtrip for initial (all-clear) FSR. *) 14 + let test_roundtrip_initial () = 15 + let t = Fsr.initial in 16 + let word = Fsr.encode t in 17 + let result = Fsr.decode word in 18 + Alcotest.(check (result fsr string)) 19 + "initial roundtrip" (Ok t) 20 + (Result.map_error (fun _ -> "decode error") result) 21 + 22 + (** Test roundtrip with all flags set. *) 23 + let test_roundtrip_all_flags () = 24 + let t = 25 + Fsr.v ~alarm:true ~bad_sn:true ~bad_mac:true ~bad_sa:true ~spi:0xFFFF 26 + ~arsn_lsb:0xFF 27 + in 28 + let word = Fsr.encode t in 29 + let result = Fsr.decode word in 30 + Alcotest.(check (result fsr string)) 31 + "all-flags roundtrip" (Ok t) 32 + (Result.map_error (fun _ -> "decode error") result) 33 + 34 + (** Test roundtrip with specific SPI and ARSN values. *) 35 + let test_roundtrip_specific () = 36 + let t = 37 + Fsr.v ~alarm:false ~bad_sn:false ~bad_mac:true ~bad_sa:false ~spi:0x1234 38 + ~arsn_lsb:0xAB 39 + in 40 + let word = Fsr.encode t in 41 + let result = Fsr.decode word in 42 + Alcotest.(check (result fsr string)) 43 + "specific values roundtrip" (Ok t) 44 + (Result.map_error (fun _ -> "decode error") result) 45 + 46 + (* --- CWT detection --- *) 47 + 48 + (** Test CWT=1 (FSR) vs CWT=0 (CLCW) detection. The Control Word Type is bit 31 49 + of the 32-bit word. CWT=0 means CLCW, CWT=1 means FSR. *) 50 + let test_cwt_fsr () = 51 + let t = Fsr.initial in 52 + let word = Fsr.encode t in 53 + (* Bit 31 should be 1 for FSR *) 54 + let cwt = Int32.to_int (Int32.shift_right_logical word 31) land 1 in 55 + Alcotest.(check int) "CWT=1 for FSR" 1 cwt 56 + 57 + let test_cwt_clcw_rejected () = 58 + (* A word with CWT=0 should be rejected as Not_fsr *) 59 + let word = Int32.zero in 60 + let result = Fsr.decode word in 61 + match result with 62 + | Error `Not_fsr -> () 63 + | Error (`Invalid_version _) -> 64 + Alcotest.fail "expected Not_fsr, got Invalid_version" 65 + | Ok _ -> Alcotest.fail "expected error for CWT=0" 66 + 67 + (* --- Version field --- *) 68 + 69 + (** Test version field is 0b100 (Type-2 OCF, FSR Version 1). *) 70 + let test_version () = Alcotest.(check int) "FSR version" 0b100 Fsr.version 71 + 72 + (** Test that a word with wrong version is rejected. *) 73 + let test_invalid_version () = 74 + (* Build a word with CWT=1 but wrong version (0b010 instead of 0b100) *) 75 + (* Bits: [CWT=1][Ver=010][rest=0...0] *) 76 + let word = 77 + Int32.logor (Int32.shift_left 1l 31) (Int32.shift_left 0b010l 28) 78 + in 79 + let result = Fsr.decode word in 80 + match result with 81 + | Error (`Invalid_version 0b010) -> () 82 + | Error (`Invalid_version v) -> 83 + Alcotest.failf "expected Invalid_version 2, got %d" v 84 + | Error `Not_fsr -> Alcotest.fail "expected Invalid_version, got Not_fsr" 85 + | Ok _ -> Alcotest.fail "expected error for wrong version" 86 + 87 + (* --- Known bit pattern --- *) 88 + 89 + (** Test known bit pattern: alarm=true, bad_mac=true, spi=0x1234, arsn_lsb=0xAB. 90 + 91 + Expected layout (MSB first): Bit 31: CWT = 1 Bits 30-28: Version = 100 Bit 92 + 27: Alarm = 1 Bit 26: BadSN = 0 Bit 25: BadMAC = 1 Bit 24: BadSA = 0 Bits 93 + 23-8: SPI = 0x1234 Bits 7-0: ARSN = 0xAB 94 + 95 + Binary: 1_100_1_0_1_0_0001_0010_0011_0100_1010_1011 Hex: 0xCA_12_34_AB *) 96 + let test_known_bit_pattern () = 97 + let t = 98 + Fsr.v ~alarm:true ~bad_sn:false ~bad_mac:true ~bad_sa:false ~spi:0x1234 99 + ~arsn_lsb:0xAB 100 + in 101 + let word = Fsr.encode t in 102 + Alcotest.(check int32) "known bit pattern" 0xCA_12_34_ABl word 103 + 104 + (* --- Operations --- *) 105 + 106 + (** Test has_error returns true when any error flag is set. *) 107 + let test_has_error () = 108 + let t = Fsr.initial in 109 + Alcotest.(check bool) "no errors" false (Fsr.has_error t); 110 + let t1 = 111 + Fsr.v ~alarm:false ~bad_sn:true ~bad_mac:false ~bad_sa:false ~spi:0 112 + ~arsn_lsb:0 113 + in 114 + Alcotest.(check bool) "bad_sn" true (Fsr.has_error t1); 115 + let t2 = 116 + Fsr.v ~alarm:false ~bad_sn:false ~bad_mac:true ~bad_sa:false ~spi:0 117 + ~arsn_lsb:0 118 + in 119 + Alcotest.(check bool) "bad_mac" true (Fsr.has_error t2); 120 + let t3 = 121 + Fsr.v ~alarm:false ~bad_sn:false ~bad_mac:false ~bad_sa:true ~spi:0 122 + ~arsn_lsb:0 123 + in 124 + Alcotest.(check bool) "bad_sa" true (Fsr.has_error t3) 125 + 126 + (** Test alarm persistence via update. *) 127 + let test_alarm_persistence () = 128 + let t = Fsr.initial in 129 + (* Update with an error: alarm should be set *) 130 + let t = Fsr.update ~bad_mac:true ~spi:1 ~arsn_lsb:0 t in 131 + Alcotest.(check bool) "alarm set after error" true t.alarm; 132 + (* Update without error: alarm persists *) 133 + let t = Fsr.update ~spi:2 ~arsn_lsb:0 t in 134 + Alcotest.(check bool) "alarm persists" true t.alarm; 135 + (* Reset alarm explicitly *) 136 + let t = Fsr.reset_alarm t in 137 + Alcotest.(check bool) "alarm cleared" false t.alarm 138 + 139 + (** Test set_alarm and reset_alarm. *) 140 + let test_set_reset_alarm () = 141 + let t = Fsr.initial in 142 + let t = Fsr.set_alarm t in 143 + Alcotest.(check bool) "alarm set" true t.alarm; 144 + let t = Fsr.reset_alarm t in 145 + Alcotest.(check bool) "alarm reset" false t.alarm 146 + 147 + (** Test ARSN_LSB is masked to 8 bits. *) 148 + let test_arsn_masking () = 149 + let t = 150 + Fsr.v ~alarm:false ~bad_sn:false ~bad_mac:false ~bad_sa:false ~spi:0 151 + ~arsn_lsb:0x1AB 152 + in 153 + Alcotest.(check int) "ARSN masked to 8 bits" 0xAB t.arsn_lsb 154 + 155 + (** Test pretty-printer produces meaningful output. *) 156 + let test_pp () = 157 + let t = 158 + Fsr.v ~alarm:true ~bad_sn:false ~bad_mac:true ~bad_sa:false ~spi:0x42 159 + ~arsn_lsb:0xFF 160 + in 161 + let s = Format.asprintf "%a" Fsr.pp t in 162 + Alcotest.(check bool) "contains alarm" true (String.length s > 10); 163 + (* Check that "spi" appears in the pretty-printed output *) 164 + let rec contains_at s sub i = 165 + if i + String.length sub > String.length s then false 166 + else if String.sub s i (String.length sub) = sub then true 167 + else contains_at s sub (i + 1) 168 + in 169 + Alcotest.(check bool) "contains spi" true (contains_at s "spi" 0) 170 + 171 + let suite = 172 + ( "fsr", 173 + [ 174 + (* Roundtrip *) 175 + Alcotest.test_case "roundtrip initial" `Quick test_roundtrip_initial; 176 + Alcotest.test_case "roundtrip all flags" `Quick test_roundtrip_all_flags; 177 + Alcotest.test_case "roundtrip specific" `Quick test_roundtrip_specific; 178 + (* CWT *) 179 + Alcotest.test_case "CWT=1 for FSR" `Quick test_cwt_fsr; 180 + Alcotest.test_case "CWT=0 rejected" `Quick test_cwt_clcw_rejected; 181 + (* Version *) 182 + Alcotest.test_case "version constant" `Quick test_version; 183 + Alcotest.test_case "invalid version" `Quick test_invalid_version; 184 + (* Bit pattern *) 185 + Alcotest.test_case "known bit pattern" `Quick test_known_bit_pattern; 186 + (* Operations *) 187 + Alcotest.test_case "has_error" `Quick test_has_error; 188 + Alcotest.test_case "alarm persistence" `Quick test_alarm_persistence; 189 + Alcotest.test_case "set/reset alarm" `Quick test_set_reset_alarm; 190 + Alcotest.test_case "ARSN masking" `Quick test_arsn_masking; 191 + Alcotest.test_case "pretty-printer" `Quick test_pp; 192 + ] )
+1
test/test_fsr.mli
··· 1 + val suite : string * unit Alcotest.test_case list