diff --git a/TOPOLOGY.md b/TOPOLOGY.md index d627bd6..639d2e6 100644 --- a/TOPOLOGY.md +++ b/TOPOLOGY.md @@ -14,13 +14,7 @@ │ ▼ ┌─────────────────────────────────────────────┐ - │ zig API LAYER │ - │ api/v/src/ecm_api.v │ - │ Consumer-facing types + classification │ - └──────────────────┬──────────────────────────┘ - │ - ┌──────────────────┼──────────────────────────┐ - │ ZIG FFI BRIDGE │ + │ ZIG API + FFI BRIDGE │ │ ffi/zig/src/main.zig │ │ C-ABI ← generated/abi/robodog_ffi.h │ └──────────────────┬──────────────────────────┘ @@ -77,12 +71,10 @@ IDRIS2 ABI (src/abi/) Formation Types ███░░░░░░░ 30% Coordinates, separation proofs FFI Declarations ███░░░░░░░ 30% Zig bridge bindings -ZIG FFI (ffi/zig/) +ZIG API + FFI (ffi/zig/) Signal Classification ████░░░░░░ 40% 3 rules, band checking Distance / Separation █████░░░░░ 50% Ground + aerial, integer math Formation Computation ███░░░░░░░ 30% Line + circle, rest TBD - -zig API (api/v/) ECM API ████░░░░░░ 40% Full type mirror, classify, recommend ABI Verification Types ███░░░░░░░ 30% Round-trip proofs diff --git a/api/v/src/ecm_api.v b/api/v/src/ecm_api.v deleted file mode 100644 index ca4e8df..0000000 --- a/api/v/src/ecm_api.v +++ /dev/null @@ -1,177 +0,0 @@ -// SPDX-License-Identifier: MPL-2.0 -// Copyright (c) 2026 Jonathan D.A. Jewell (hyperpolymath) - -// ecm_api.v — zig API for Robodog ECM consumers. -// -// DEFENSIVE USE ONLY — Wassenaar Cat 5A2 / Cat 11. -// Public API surface for the Robodog ECM platform. -// Wraps the Zig FFI bridge with V-native types and error handling. - -module ecm_api - -// ── Type definitions (matching Idris2 ABI) ────────────────────────────── - -// FreqBand represents supported frequency bands for ECM simulation. -pub enum FreqBand { - hf // 0–30 MHz: SAR coordination - vhf // 30–300 MHz: formation comms - uhf // 300 MHz–3 GHz: general ECM - shf // 3–30 GHz: radar countermeasures -} - -// Modulation represents detected or simulated signal modulation. -pub enum Modulation { - cw // Continuous wave - am // Amplitude modulation - fm // Frequency modulation - psk // Phase-shift keying - fsk // Frequency-shift keying - ofdm // Orthogonal frequency-division multiplexing - fhss // Frequency hopping spread spectrum - dsss // Direct-sequence spread spectrum -} - -// SignalClass is the defensive classification of a detected signal. -// There is intentionally NO offensive/targeting variant. -pub enum SignalClass { - friendly - neutral - interference - suspected_jammer -} - -// FormationShape defines formation geometry templates. -pub enum FormationShape { - line - wedge - circle - diamond - grid -} - -// Position3D represents a 3D position in millimetres. -pub struct Position3D { -pub mut: - x i64 // East displacement (mm) - y i64 // North displacement (mm) - z i64 // Altitude (mm AGL) -} - -// DetectedSignal represents a signal found during spectrum monitoring. -pub struct DetectedSignal { -pub: - frequency_hz u64 - bandwidth_hz u64 - snr_db f64 - modulation Modulation - classification SignalClass - bearing_deg ?f64 - timestamp_s f64 -} - -// SpectrumSnapshot captures the state of monitored spectrum. -pub struct SpectrumSnapshot { -pub: - centre_freq_hz f64 - bandwidth_hz f64 - noise_floor_dbm f64 - signals []DetectedSignal - timestamp_s f64 -} - -// DefensiveRecommendation is the advised response to detected threats. -pub enum DefensiveRecommendation { - no_action - increased_monitoring - frequency_hop - increase_power - alert_operator -} - -// ── API functions ─────────────────────────────────────────────────────── - -// classify_signal determines the defensive classification of a signal. -// Uses the same rule-based classifier as the Rust core and Zig FFI. -pub fn classify_signal(sig DetectedSignal) SignalClass { - // Rule 1: CW with high SNR and wide bandwidth = barrage jamming. - if sig.modulation == .cw && sig.snr_db > 20.0 && sig.bandwidth_hz > 10_000_000 { - return .suspected_jammer - } - // Rule 2: Exceeding maximum legitimate bandwidth. - if sig.bandwidth_hz > 40_000_000 { - return .suspected_jammer - } - // Rule 3: Unknown high-power modulation — not representable in - // the enum, so this path is structurally unreachable in V. - // Kept as documentation of the Rust/Zig equivalent. - return .neutral -} - -// has_jamming checks whether any signals in a snapshot are suspected jammers. -pub fn has_jamming(snap SpectrumSnapshot) bool { - for sig in snap.signals { - if sig.classification == .suspected_jammer { - return true - } - } - return false -} - -// recommend_response analyses a snapshot and returns a defensive action. -pub fn recommend_response(snap SpectrumSnapshot) DefensiveRecommendation { - mut jamming_count := 0 - mut interference_count := 0 - for sig in snap.signals { - match sig.classification { - .suspected_jammer { jamming_count++ } - .interference { interference_count++ } - else {} - } - } - if jamming_count > 0 { - return .alert_operator - } - if interference_count > 1 { - return .frequency_hop - } - if interference_count == 1 { - return .increased_monitoring - } - return .no_action -} - -// freq_in_band checks whether a frequency falls within a given band. -pub fn freq_in_band(freq_hz u64, band FreqBand) bool { - lower := match band { - .hf { u64(0) } - .vhf { u64(30_000_000) } - .uhf { u64(300_000_000) } - .shf { u64(3_000_000_000) } - } - upper := match band { - .hf { u64(30_000_000) } - .vhf { u64(300_000_000) } - .uhf { u64(3_000_000_000) } - .shf { u64(30_000_000_000) } - } - return freq_hz >= lower && freq_hz < upper -} - -// distance_squared_mm computes squared distance between two positions. -// Integer arithmetic for determinism — no floating-point. -pub fn distance_squared_mm(a Position3D, b Position3D) i64 { - dx := a.x - b.x - dy := a.y - b.y - dz := a.z - b.z - return dx * dx + dy * dy + dz * dz -} - -// ground_safe checks ground separation (2.0m minimum). -pub fn ground_safe(a Position3D, b Position3D) bool { - return distance_squared_mm(a, b) >= 4_000_000 // 2000mm^2 -} - -// aerial_safe checks aerial separation (10.0m minimum). -pub fn aerial_safe(a Position3D, b Position3D) bool { - return distance_squared_mm(a, b) >= 100_000_000 // 10000mm^2 -} diff --git a/api/v/src/ecm_api_test.v b/api/v/src/ecm_api_test.v deleted file mode 100644 index 11ac978..0000000 --- a/api/v/src/ecm_api_test.v +++ /dev/null @@ -1,163 +0,0 @@ -// SPDX-License-Identifier: MPL-2.0 -// Copyright (c) 2026 Jonathan D.A. Jewell (hyperpolymath) - -// ecm_api_test.v — Tests for the zig ECM API. - -module ecm_api - -// ── Point-to-point tests ──────────────────────────────────────────────── - -fn test_classify_cw_jammer() { - sig := DetectedSignal{ - frequency_hz: 2_400_000_000 - bandwidth_hz: 50_000_000 - snr_db: 30.0 - modulation: .cw - classification: .neutral - bearing_deg: none - timestamp_s: 0.0 - } - assert classify_signal(sig) == .suspected_jammer -} - -fn test_classify_normal_ofdm() { - sig := DetectedSignal{ - frequency_hz: 2_412_000_000 - bandwidth_hz: 20_000_000 - snr_db: 25.0 - modulation: .ofdm - classification: .neutral - bearing_deg: none - timestamp_s: 0.0 - } - assert classify_signal(sig) == .neutral -} - -fn test_classify_wideband_jammer() { - sig := DetectedSignal{ - frequency_hz: 1_000_000_000 - bandwidth_hz: 50_000_000 - snr_db: 15.0 - modulation: .fm - classification: .neutral - bearing_deg: none - timestamp_s: 0.0 - } - assert classify_signal(sig) == .suspected_jammer -} - -// ── Frequency band tests ──────────────────────────────────────────────── - -fn test_freq_in_band_vhf() { - assert freq_in_band(150_000_000, .vhf) == true - assert freq_in_band(500_000_000, .vhf) == false -} - -fn test_freq_in_band_uhf() { - assert freq_in_band(1_000_000_000, .uhf) == true - assert freq_in_band(30_000_000, .uhf) == false -} - -fn test_freq_bands_contiguous() { - // HF ends where VHF starts - assert freq_in_band(29_999_999, .hf) == true - assert freq_in_band(30_000_000, .hf) == false - assert freq_in_band(30_000_000, .vhf) == true -} - -// ── Distance and separation tests ─────────────────────────────────────── - -fn test_distance_squared_basic() { - a := Position3D{ x: 0, y: 0, z: 0 } - b := Position3D{ x: 3000, y: 4000, z: 0 } - // 3^2 + 4^2 = 25 (in metres), so 3000^2 + 4000^2 = 25,000,000 mm^2 - assert distance_squared_mm(a, b) == 25_000_000 -} - -fn test_distance_symmetric() { - a := Position3D{ x: 1000, y: 2000, z: 3000 } - b := Position3D{ x: -500, y: 1500, z: 0 } - assert distance_squared_mm(a, b) == distance_squared_mm(b, a) -} - -fn test_ground_safe() { - a := Position3D{ x: 0, y: 0, z: 0 } - far := Position3D{ x: 3000, y: 0, z: 0 } - close := Position3D{ x: 1000, y: 0, z: 0 } - assert ground_safe(a, far) == true - assert ground_safe(a, close) == false -} - -fn test_aerial_safe() { - a := Position3D{ x: 0, y: 0, z: 0 } - far := Position3D{ x: 15000, y: 0, z: 0 } - close := Position3D{ x: 5000, y: 0, z: 0 } - assert aerial_safe(a, far) == true - assert aerial_safe(a, close) == false -} - -// ── Snapshot and recommendation tests ─────────────────────────────────── - -fn test_has_jamming_false() { - snap := SpectrumSnapshot{ - centre_freq_hz: 2_400_000_000.0 - bandwidth_hz: 20_000_000.0 - noise_floor_dbm: -90.0 - signals: [] - timestamp_s: 0.0 - } - assert has_jamming(snap) == false -} - -fn test_has_jamming_true() { - snap := SpectrumSnapshot{ - centre_freq_hz: 2_400_000_000.0 - bandwidth_hz: 20_000_000.0 - noise_floor_dbm: -90.0 - signals: [ - DetectedSignal{ - frequency_hz: 2_400_000_000 - bandwidth_hz: 50_000_000 - snr_db: 40.0 - modulation: .cw - classification: .suspected_jammer - bearing_deg: none - timestamp_s: 0.0 - }, - ] - timestamp_s: 0.0 - } - assert has_jamming(snap) == true -} - -fn test_recommend_no_action() { - snap := SpectrumSnapshot{ - centre_freq_hz: 150_000_000.0 - bandwidth_hz: 10_000_000.0 - noise_floor_dbm: -100.0 - signals: [] - timestamp_s: 0.0 - } - assert recommend_response(snap) == .no_action -} - -fn test_recommend_alert_on_jammer() { - snap := SpectrumSnapshot{ - centre_freq_hz: 2_400_000_000.0 - bandwidth_hz: 20_000_000.0 - noise_floor_dbm: -90.0 - signals: [ - DetectedSignal{ - frequency_hz: 2_400_000_000 - bandwidth_hz: 50_000_000 - snr_db: 40.0 - modulation: .cw - classification: .suspected_jammer - bearing_deg: none - timestamp_s: 0.0 - }, - ] - timestamp_s: 0.0 - } - assert recommend_response(snap) == .alert_operator -} diff --git a/contractiles/trust/Trustfile.a2ml b/contractiles/trust/Trustfile.a2ml index c592e0d..a3f9b64 100644 --- a/contractiles/trust/Trustfile.a2ml +++ b/contractiles/trust/Trustfile.a2ml @@ -323,15 +323,8 @@ languages: memory_safety: "Compile-time bounds checking, no hidden allocations" abi_compat: "Native C ABI, zero runtime overhead" - - name: "V" - role: "API layer (consumer-facing)" - location: "api/v/" - components: ["ecm_api", "crypto_api", "formation_api"] - build: "v build api/v/" - test: "v test api/v/" - security: - input_validation: "All external inputs validated at API boundary" - abi_verification: "Types checked against Idris2 ABI definitions" + # V-lang API layer removed 2026-05-28; the Zig FFI at ffi/zig/ is now the + # canonical consumer-facing API per estate-wide Zig=APIs+FFIs policy. --- ### [CONTAINER_SUPPLY_CHAIN]