From 09e756ecbb99ad72de84b2211fea32b4a4d6ff68 Mon Sep 17 00:00:00 2001 From: Yuzviak Date: Mon, 11 May 2026 18:10:23 +0300 Subject: [PATCH] docs(02-01): rewrite acceptance_criteria.md with formal AC-1.x..AC-NEW-8 contract MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Archive previous 51-line prose draft as acceptance_criteria_draft.md (git mv) - Create canonical Stage 2 AC document with 39 AC entries (31 base + 8 AC-NEW) - Every AC follows per-AC schema: Statement / Numeric threshold / Rationale / Validation method / Test IDs (placeholder) / Implementing components / Status - All numeric thresholds from PATTERNS.md §2.3 present verbatim (50 m, 20 m, 400 ms p95, 64 GB, 8 GB, 0.5 m/px, 30 s TTFF, etc.) - Deferred-hardware ACs marked: AC-NEW-1, AC-NEW-5, AC-NEW-7 (multi-flight), plus AC-NEW-3 partial hardware validation - Source-label vocab {satellite_anchored, vo_extrapolated, dead_reckoned} canonical - AC IDs match regex ^\s*-\s*\*\*(AC-(?:\d+\.\d+[a-z]?|NEW-\d+))\*\* confirmed by Plan 02-04 traceability script - Baseline 216 tests still pass (regression floor unchanged) - AC-4.3 v1 scope clause reproduced verbatim (ODOMETRY disabled; GPS_INPUT only) - AC-8.4 marked deferred-stage3 per REQUIREMENTS.md parking lot --- _docs/00_problem/acceptance_criteria.md | 951 +++++++++++++++++- _docs/00_problem/acceptance_criteria_draft.md | 50 + 2 files changed, 966 insertions(+), 35 deletions(-) create mode 100644 _docs/00_problem/acceptance_criteria_draft.md diff --git a/_docs/00_problem/acceptance_criteria.md b/_docs/00_problem/acceptance_criteria.md index 9199b33..98852ea 100644 --- a/_docs/00_problem/acceptance_criteria.md +++ b/_docs/00_problem/acceptance_criteria.md @@ -1,50 +1,931 @@ -# Position Accuracy +# Acceptance Criteria -- The system should determine GPS coordinates of frame centers for 80% of photos within 50m error compared to real GPS -- The system should determine GPS coordinates of frame centers for 60% of photos within 20m error compared to real GPS -- Maximum cumulative VO drift between satellite correction anchors should be less than 100 meters -- System should report a confidence score per position estimate (high = satellite-anchored, low = VO-extrapolated with drift) +> **Last revised**: 2026-05-11 (Phase 2 / Stage 2 rewrite from try02 skeleton, validated against Stage 2 constraints). +> +> Revision log: +> - 2026-05-11: Initial Stage 2 rewrite — adopted try02 AC vocabulary (AC-1.1..AC-8.6 + AC-NEW-1..AC-NEW-8), added per-AC schema (Statement / Numeric threshold / Rationale / Validation method / Test IDs / Implementing components / Status). Previous draft archived at `acceptance_criteria_draft.md`. +> - 2026-05-01 (try02): AC-1.3 anchor-age reporting clarified; AC-2.1 split; AC-5.2 and AC-NEW-2 now require ArduPilot Plane SITL; AC-8.3 storage and AC-NEW-7 clarified. +> - 2026-04-29 (try02): AC-3.5 and AC-NEW-8 added for visual blackout/cloud occlusion. +> - 2026-04-26 (try02): AC-4.3 extended; AC-8.6 added; AC-NEW-7 added. +> - 2026-04-25 (try02): initial AC list. -# Image Processing Quality +## Schema -- Image Registration Rate > 95% for normal flight segments. The system can find enough matching features to confidently calculate the camera's 6-DoF pose and stitch that image into the trajectory -- Mean Reprojection Error (MRE) < 1.0 pixels +Every AC follows the same template: -# Resilience & Edge Cases +- **AC-ID** — Short title -- The system should correctly continue work even in the presence of up to 350m outlier between 2 consecutive photos (due to tilt of the plane) -- System should correctly continue work during sharp turns, where the next photo doesn't overlap at all or overlaps less than 5%. The next photo should be within 200m drift and at an angle of less than 70 degrees. Sharp-turn frames are expected to fail VO and should be handled by satellite-based re-localization -- System should operate when UAV makes a sharp turn and next photos have no common points with previous route. It should figure out the location of the new route segment and connect it to the previous route. There could be more than 2 such disconnected segments, so this strategy must be core to the system -- In case the system cannot determine the position of 3 consecutive frames by any means, it should send a re-localization request to the ground station operator via telemetry link. While waiting for operator input, the system continues attempting VO/IMU dead reckoning and the flight controller uses last known position + IMU extrapolation + **Statement.** The system shall … -# Real-Time Onboard Performance + **Numeric threshold.** Quantitative bound(s); measurement methodology. -- Less than 400ms end-to-end per frame: from camera capture to GPS coordinate output to the flight controller (camera shoots at ~3fps) -- Memory usage should stay below 8GB shared memory (Jetson Orin Nano Super — CPU and GPU share the same 8GB LPDDR5 pool) -- The system must output calculated GPS coordinates directly to the flight controller via MAVLink GPS_INPUT messages (using MAVSDK) -- Position estimates are streamed to the flight controller frame-by-frame; the system does not batch or delay output -- The system may refine previously calculated positions and send corrections to the flight controller as updated estimates + **Rationale.** Risk if violated; tie-back to PROJECT.md core value. -# Startup & Failsafe + **Validation method.** One of: `unit-test`, `integration-test`, `blackbox-replay`, `sitl-simulation`, `benchmark`, `deferred-hardware`. May list more than one. -- The system initializes using the last known valid GPS position from the flight controller before GPS denial begins -- If the system completely fails to produce any position estimate for more than N seconds (TBD), the flight controller should fall back to IMU-only dead reckoning and the system should log the failure -- On companion computer reboot mid-flight, the system should attempt to re-initialize from the flight controller's current IMU-extrapolated position + **Test IDs.** + - _populated by scripts/gen_ac_traceability.py_ -# Ground Station & Telemetry + **Implementing components.** `src/gps_denied/...` paths the AC binds to. -- Position estimates and confidence scores should be streamed to the ground station via telemetry link for operator situational awareness -- The ground station can send commands to the onboard system (e.g., operator-assisted re-localization hint with approximate coordinates) -- Output coordinates in WGS84 format + **Status.** `active` / `deferred-stage3` / `superseded-by(AC-Z)`. -# Object Localization +--- -- Other onboard AI systems can request GPS coordinates of objects detected by the AI camera -- The GPS-Denied system calculates object coordinates trigonometrically using: current UAV GPS position (from GPS-Denied), known AI camera angle, zoom, and current flight altitude. Flat terrain is assumed -- Accuracy is consistent with the frame-center position accuracy of the GPS-Denied system +## Position Accuracy -# Satellite Reference Imagery +- **AC-1.1** — Position accuracy (50 m @ 80 %) -- Satellite reference imagery resolution must be at least 0.5 m/pixel, ideally 0.3 m/pixel -- Satellite imagery for the operational area should be less than 2 years old where possible -- Satellite imagery must be pre-processed and loaded onto the companion computer before flight. Offline preprocessing time is not time-critical (can take minutes/hours) + **Statement.** The system shall determine GPS coordinates of frame centres within **50 m** of true GPS for **≥80 %** of photos in normal flight segments. + + **Numeric threshold.** + - `error_m = ‖estimate_xy − ground_truth_xy‖` (haversine, WGS84) + - normal_flight_segment = nadir ±10° bank/pitch, ≥40 % overlap, daytime, no full visual blackout + - PASS iff `count(error_m < 50) / count(normal_segment_frames) ≥ 0.80` + - Window: per 5-minute rolling window over the full flight + + **Rationale.** Below this threshold the flight controller cannot safely geofence; navigation in GPS-denied airspace is impossible. This is the core-value AC. + + **Validation method.** `integration-test` + `benchmark` on AerialVL S03 and Azaion 10.05.2026 fixtures. + + **Test IDs.** + - _populated by scripts/gen_ac_traceability.py_ + + **Implementing components.** + - `src/gps_denied/components/safety_state/` + - `src/gps_denied/components/anchor_verifier/` + - `src/gps_denied/core/eskf.py` + + **Status.** active + +- **AC-1.2** — Position accuracy (20 m @ 50 %) + + **Statement.** The system shall determine GPS coordinates of frame centres within **20 m** of true GPS for **≥50 %** of photos in normal flight segments. + + **Numeric threshold.** + - Same measurement methodology as AC-1.1 + - PASS iff `count(error_m < 20) / count(normal_segment_frames) ≥ 0.50` + + **Rationale.** Stretch target for precision operations; failing this but passing AC-1.1 is an acceptable Stage 2 outcome. Violating both degrades all geofence and targeting accuracy. + + **Validation method.** `integration-test` + `benchmark` on AerialVL S03 and Azaion 10.05.2026 fixtures. + + **Test IDs.** + - _populated by scripts/gen_ac_traceability.py_ + + **Implementing components.** + - `src/gps_denied/components/safety_state/` + - `src/gps_denied/components/anchor_verifier/` + - `src/gps_denied/core/eskf.py` + + **Status.** active + +- **AC-1.3** — Cumulative VO drift bound + anchor-age reporting + + **Statement.** Maximum cumulative VO drift between two consecutive satellite-anchored fixes shall be **<100 m** (VO-only fallback) or **<50 m** (when IMU is fused). Every emitted estimate shall include `last_satellite_anchor_age_ms`; validation results shall be binned by anchor age, and the solution draft must define the maximum anchor age after which estimates are treated as degraded (`vo_extrapolated` or `dead_reckoned`) with monotonically growing covariance. + + **Numeric threshold.** + - VO-only fallback: `drift_m < 100 m` + - IMU-fused: `drift_m < 50 m` + - Drift measured as `‖VO-extrapolated centre − next anchor centre‖` at the moment of anchor fix + + **Rationale.** Unbounded drift invalidates the position guarantee of AC-1.1. Anchor-age reporting is required for the traceability script and for Phase 3/6 observability. Cross-references: AC-NEW-1 (cold-start anchor age budget). + + **Validation method.** `integration-test` + + **Test IDs.** + - _populated by scripts/gen_ac_traceability.py_ + + **Implementing components.** + - `src/gps_denied/core/eskf.py` + - `src/gps_denied/components/safety_state/` + + **Status.** active (Phase 3 SAFE-03 wires `anchor_age_ms` field; Phase 2 records the AC text only) + +- **AC-1.4** — Quantitative confidence score per estimate + + **Statement.** The system shall report a **quantitative confidence score** per position estimate, comprising: the 95 % covariance ellipse semi-major axis in meters AND a categorical label `{satellite_anchored, vo_extrapolated, dead_reckoned}`. + + **Numeric threshold.** + - `source_label ∈ {satellite_anchored, vo_extrapolated, dead_reckoned}` (exhaustive; no other values permitted) + - `cov_semi_major_m ≥ 0` (non-negative real) + + **Rationale.** Without a machine-readable label the flight controller cannot distinguish a high-quality fix from dead reckoning; the FC applies no covariance-appropriate safety margin. Cross-references: AC-NEW-8 (source_label vocab used in blackout failsafe). + + **Validation method.** `unit-test` (schema) + `integration-test` + + **Test IDs.** + - _populated by scripts/gen_ac_traceability.py_ + + **Implementing components.** + - `src/gps_denied/components/safety_state/` + - `src/gps_denied/hot_types/position_estimate.py` + + **Status.** active + +--- + +## Image Processing Quality + +- **AC-2.1a** — VO registration rate + + **Statement.** Frame-to-frame visual registration shall succeed for **>95 %** of normal flight segments (defined as: nadir flight ±10° bank/pitch, ≥40 % overlap with prior frame, daytime, usable texture, no full visual blackout). + + **Numeric threshold.** + - `count(vo_success) / count(normal_segment_frames) > 0.95` + + **Rationale.** VO failure rate above 5 % causes excessive dead reckoning in steady-state conditions, violating AC-1.3 drift bound. + + **Validation method.** `integration-test` + + **Test IDs.** + - _populated by scripts/gen_ac_traceability.py_ + + **Implementing components.** + - `src/gps_denied/components/vio/` + - `src/gps_denied/pipeline/orchestrator.py` + + **Status.** active + +- **AC-2.1b** — Satellite-anchor registration + + **Statement.** Cross-domain UAV-photo to satellite/cache registration is measured separately from AC-2.1a and must satisfy AC-1.1/AC-1.2 position accuracy, AC-2.2 cross-domain MRE, AC-8.2 freshness, and AC-8.6 retrieval behavior on season-matched tiles. + + **Numeric threshold.** + - Satellite-anchor MRE: **<2.5 px** (see AC-2.2) + - Anchor acceptance rate: bounded by AC-1.1 (≥80 % of frames within 50 m) + + **Rationale.** Satellite registration rate and quality must be tracked independently of VO to isolate failure modes (tile staleness vs. VO failure vs. cross-domain mismatch). + + **Validation method.** `integration-test` + `benchmark` + + **Test IDs.** + - _populated by scripts/gen_ac_traceability.py_ + + **Implementing components.** + - `src/gps_denied/components/satellite_matcher/` + - `src/gps_denied/components/anchor_verifier/` + + **Status.** active + +- **AC-2.2** — Mean Reprojection Error + + **Statement.** Mean Reprojection Error (MRE) shall be: **<1.0 px** for VO frame-to-frame homography on overlapping aerial pairs; **<2.5 px** for satellite-anchored cross-domain (UAV photo ↔ ortho satellite tile) registration. + + **Numeric threshold.** + - VO MRE: `mre_px < 1.0` + - Satellite MRE: `mre_px < 2.5` + + **Rationale.** MRE above these thresholds indicates degenerate homography; the resulting position shift can exceed 10 m per pixel of MRE at 0.5 m/px resolution. Exceeding satellite MRE violates AC-1.1. + + **Validation method.** `integration-test` + + **Test IDs.** + - _populated by scripts/gen_ac_traceability.py_ + + **Implementing components.** + - `src/gps_denied/components/satellite_matcher/metric_refinement.py` + - `src/gps_denied/components/anchor_verifier/` + + **Status.** active + +--- + +## Resilience & Edge Cases + +- **AC-3.1** — Outlier tolerance + + **Statement.** The system shall correctly continue work in the presence of up to **350 m** outliers between two consecutive photos (caused by airframe tilt up to ±20°). + + **Numeric threshold.** + - Maximum inter-frame displacement handled: 350 m + - System continues producing valid estimates after the outlier frame + + **Rationale.** Fixed-wing tilt events create apparent ground displacement beyond normal VO search radius; failure to handle them causes tracking loss on routine maneuvers. + + **Validation method.** `integration-test` + + **Test IDs.** + - _populated by scripts/gen_ac_traceability.py_ + + **Implementing components.** + - `src/gps_denied/core/recovery.py` + - `src/gps_denied/components/satellite_matcher/` + + **Status.** active + +- **AC-3.2** — Sharp-turn handling + + **Statement.** The system shall correctly continue work during sharp turns where the next photo overlaps **<5 %** with the previous, drifts **<200 m**, and changes heading **<70°**. Sharp-turn frames are expected to fail VO and shall be handled by satellite-based re-localization (place recognition over the satellite tile cache). + + **Numeric threshold.** + - Overlap threshold: `<5 %` + - Drift after turn: `<200 m` + - Heading change: `<70°` + + **Rationale.** Fixed-wing survey patterns include 180° turn-arounds with near-zero overlap. Without satellite re-localization the system drifts unbounded on every headland. + + **Validation method.** `integration-test` + + **Test IDs.** + - _populated by scripts/gen_ac_traceability.py_ + + **Implementing components.** + - `src/gps_denied/core/recovery.py` + - `src/gps_denied/components/satellite_matcher/` + - `src/gps_denied/core/factor_graph.py` + + **Status.** active + +- **AC-3.3** — Disconnected segments + + **Statement.** The system shall handle **≥3 disconnected segments** per flight, connecting each new segment to the previous trajectory via global descriptor retrieval + RANSAC pose-graph relocalization. This is a core capability, not a degraded mode. + + **Numeric threshold.** + - Minimum disconnected segments handled per flight: 3 + - Each segment successfully reconnected to the global trajectory + + **Rationale.** Multi-strip survey missions have multiple heading reversals with full visual discontinuity. If the system treats this as exceptional, it fails on every second strip. + + **Validation method.** `integration-test` + + **Test IDs.** + - _populated by scripts/gen_ac_traceability.py_ + + **Implementing components.** + - `src/gps_denied/core/recovery.py` + - `src/gps_denied/components/satellite_matcher/` + - `src/gps_denied/core/factor_graph.py` + + **Status.** active + +- **AC-3.4** — Re-localization request trigger + + **Statement.** When the system cannot determine position for **≥3 consecutive frames AND ≥2 s**, it shall send a re-localization request to the ground station via telemetry. While waiting, it continues VO/IMU dead reckoning and the flight controller uses last known position + IMU extrapolation. + + **Numeric threshold.** + - Dual trigger: `consecutive_frames_failed ≥ 3 AND time_failed_s ≥ 2` + - Re-loc request must be emitted within one processing cycle of threshold crossing + + **Rationale.** A single trigger threshold (frame count only) can fire on sub-second bursts; dual-trigger prevents spurious requests during transient VO failures. + + **Validation method.** `blackbox-replay` + `sitl-simulation` + + **Test IDs.** + - _populated by scripts/gen_ac_traceability.py_ + + **Implementing components.** + - `src/gps_denied/core/recovery.py` + - `src/gps_denied/components/mavlink_io/pymavlink_bridge.py` + + **Status.** active + +- **AC-3.5** — Visual blackout mode switch + + **Statement.** During temporary **visual blackout** where the navigation camera provides no usable ground signal (e.g., clouds/occlusion/whiteout) while GPS is denied or spoofed, the system shall switch to `{dead_reckoned}` within **≤1 processed frame OR ≤400 ms**, reject the spoofed GPS as an estimator input, and propagate position solely from the last trusted state + flight-controller IMU/attitude/airspeed/altitude inputs until visual or satellite anchoring recovers. During this mode, covariance shall grow monotonically, `GPS_INPUT.horiz_accuracy` shall not under-report the 95 % covariance semi-major axis, and QGroundControl shall receive a `VISUAL_BLACKOUT_IMU_ONLY` status at **1–2 Hz**. Cross-references: AC-NEW-8 (extended blackout failsafe budget). + + **Numeric threshold.** + - Mode switch latency: `≤1 processed frame OR ≤400 ms` + - QGC status rate: `1–2 Hz` + - Covariance: monotonically non-decreasing in blackout mode + + **Rationale.** A cloud/whiteout period removes all visual correction exactly when spoofed GPS cannot be trusted. Pretending a stale visual position remains valid is operationally dangerous. + + **Validation method.** `blackbox-replay` + `sitl-simulation` + + **Test IDs.** + - _populated by scripts/gen_ac_traceability.py_ + + **Implementing components.** + - `src/gps_denied/components/safety_state/` + - `src/gps_denied/components/mavlink_io/pymavlink_bridge.py` + - `src/gps_denied/core/eskf.py` + + **Status.** active + +--- + +## Real-Time Onboard Performance + +- **AC-4.1** — End-to-end latency + + **Statement.** End-to-end latency from camera capture to GPS coordinate output to the flight controller shall be **<400 ms p95**. Up to ~10 % of frames may be dropped under sustained load (skip-allowed). Heavy global VPR / cross-domain re-ranking shall be conditional, not part of the steady-state per-frame path. + + **Numeric threshold.** + - p95 end-to-end latency: `<400 ms` + - Frame-drop budget: `≤10 %` + + **Rationale.** ArduPilot EKF failsafe fires after ~3 s without a GPS update at 5 Hz; 400 ms p95 keeps the GPS_INPUT stream healthy. Beyond 400 ms, EKF position uncertainty grows fast enough to trigger safety modes. + + **Validation method.** `benchmark` + + **Test IDs.** + - _populated by scripts/gen_ac_traceability.py_ + + **Implementing components.** + - `src/gps_denied/pipeline/orchestrator.py` + - `src/gps_denied/components/vio/` + - `src/gps_denied/components/satellite_matcher/` + + **Status.** active + +- **AC-4.2** — Memory budget + + **Statement.** Memory usage shall remain below **8 GB** shared on Jetson Orin Nano Super (CPU and GPU share the same 8 GB LPDDR5 pool). + + **Numeric threshold.** + - Peak shared memory: `<8 GB` + + **Rationale.** Exceeding 8 GB triggers OOM-killer; the system crashes mid-flight. No recovery path exists without a reboot. + + **Validation method.** `benchmark` + + **Test IDs.** + - _populated by scripts/gen_ac_traceability.py_ + + **Implementing components.** + - `src/gps_denied/pipeline/orchestrator.py` + - `src/gps_denied/components/gpr/` + + **Status.** active + +- **AC-4.3** — MAVLink output channel (GPS_INPUT primary) + + **Statement.** The system shall output its position estimate to the flight controller via **two parallel MAVLink channels**, both emitted by **pymavlink**: Primary (`GPS_INPUT` targeting ArduPilot `GPS1_TYPE=14`) and Auxiliary (`ODOMETRY` when EKF emits fix with full 6-DoF covariance and quality > `VISO_QUAL_MIN`). FC source priorities configured so GPS_INPUT remains the failover path if ODOMETRY trips a parameter gate. + + **v1 scope clause (added 2026-04-26)**: v1 ships **GPS_INPUT only**; the ODOMETRY auxiliary channel is intentionally **disabled** in v1 because feeding both `GPS_INPUT` and `ODOMETRY` for overlapping axes triggers ArduPilot EKF3 double-fusion bugs (issues #30076 / #32506). `EK3_SRC1_*=GPS+Compass`; ODOMETRY emission re-enables in v1.1 once F-T9 SITL confirms PR #30080-class clean source-switching. Tests therefore assert v1 emits GPS_INPUT only and that ODOMETRY is *intentionally absent* on the wire. + + **Numeric threshold.** + - v1: `GPS_INPUT` present on wire; `ODOMETRY` absent + - FC parameter: `GPS1_TYPE=14`; quality gate: `VISO_QUAL_MIN` + + **Rationale.** EKF3 double-fusion causes divergent position estimates that cannot be diagnosed in the field. v1 single-channel keeps the behavior predictable until the upstream ArduPilot fix is confirmed. (See MAVOUT-02 requirement.) + + **Validation method.** `blackbox-replay` (channel absence) + `sitl-simulation` (FC ingest) + + **Test IDs.** + - _populated by scripts/gen_ac_traceability.py_ + + **Implementing components.** + - `src/gps_denied/components/mavlink_io/pymavlink_bridge.py` + - `src/gps_denied/pipeline/orchestrator.py` + + **Status.** active (v1: GPS_INPUT only; MAVOUT-02 scaffolds ODOMETRY behind feature flag) + +- **AC-4.4** — Frame-by-frame streaming + + **Statement.** Position estimates are streamed to the flight controller frame-by-frame; the system shall not batch or delay output. + + **Numeric threshold.** + - Every processed frame produces a `GPS_INPUT` emit within the AC-4.1 latency budget + - No buffering / batching of position output + + **Rationale.** Batching introduces burst latency; ArduPilot's EKF treats inter-packet gaps as data-rate drops, degrading its own estimate quality. + + **Validation method.** `sitl-simulation` + + **Test IDs.** + - _populated by scripts/gen_ac_traceability.py_ + + **Implementing components.** + - `src/gps_denied/components/mavlink_io/pymavlink_bridge.py` + + **Status.** active + +- **AC-4.5** — Estimate refinement / corrections + + **Statement.** The system may refine previously calculated positions and send corrections to the flight controller as updated estimates. + + **Numeric threshold.** + - Correction delta: no hard limit (any refinement may be sent) + - FC must not reject corrections; system must not suppress them + + **Rationale.** Factor-graph back-end refines past poses when new satellite anchors arrive; sending the refined estimate keeps the FC's EKF state consistent with our back-end. + + **Validation method.** `sitl-simulation` + + **Test IDs.** + - _populated by scripts/gen_ac_traceability.py_ + + **Implementing components.** + - `src/gps_denied/pipeline/orchestrator.py` + - `src/gps_denied/components/mavlink_io/pymavlink_bridge.py` + + **Status.** active + +--- + +## Startup & Failsafe + +- **AC-5.1** — Initialization from FC EKF + + **Statement.** The system shall initialise using the last known valid GPS position from the flight controller's EKF, plus IMU-extrapolated position at the moment of GPS denial. + + **Numeric threshold.** + - Initialization must complete before first `GPS_INPUT` emit + - Initial position: FC EKF position at denial onset, not a default/zero position + + **Rationale.** Starting from zero coordinates puts the FC in a state where GPS_INPUT contradicts the last real GPS; the EKF rejects the first several estimates, creating a cold-start gap in coverage. + + **Validation method.** `sitl-simulation` + + **Test IDs.** + - _populated by scripts/gen_ac_traceability.py_ + + **Implementing components.** + - `src/gps_denied/components/mavlink_io/` + - `src/gps_denied/core/recovery.py` + + **Status.** active + +- **AC-5.2** — Failsafe timeout + + **Statement.** If the system fails to produce any position estimate for **>3 s**, the flight controller shall fall back to IMU-only dead reckoning and the system shall log the failure. Because ArduPilot failsafe timing depends on vehicle type and parameters, this fallback behavior must be verified specifically in **ArduPilot Plane SITL** with the production parameter set; Copter defaults are reference evidence only. + + **Numeric threshold.** + - Failsafe trigger: `gap_s > 3` + - Validation scope: ArduPilot Plane SITL (not Copter) + + **Rationale.** Plane and Copter have different EKF lane-switch thresholds; verifying on Copter SITL only leaves the Plane-specific failsafe path untested. + + **Validation method.** `sitl-simulation` (ArduPilot Plane SITL specifically) + + **Test IDs.** + - _populated by scripts/gen_ac_traceability.py_ + + **Implementing components.** + - `src/gps_denied/components/mavlink_io/` + - `src/gps_denied/core/recovery.py` + + **Status.** active + +- **AC-5.3** — Mid-flight reboot recovery + + **Statement.** On companion computer reboot mid-flight, the system shall attempt to re-initialise from the flight controller's current IMU-extrapolated position. See AC-NEW-1 for the cold-start time-to-first-fix budget. + + **Numeric threshold.** + - Recovery attempt must start within `AC-NEW-1` TTFF budget (<30 s) + - Recovery position: FC IMU-extrapolated position at reboot time + + **Rationale.** Brown-out / watchdog resets are realistic on 8-hour missions; the UAV must not enter an unrecoverable state when the companion reboots. + + **Validation method.** `sitl-simulation` + + **Test IDs.** + - _populated by scripts/gen_ac_traceability.py_ + + **Implementing components.** + - `src/gps_denied/components/mavlink_io/` + - `src/gps_denied/core/recovery.py` + + **Status.** active + +--- + +## Ground Station & Telemetry + +- **AC-6.1** — GCS position stream + + **Statement.** Position estimates and confidence scores shall be streamed to **QGroundControl** via the MAVLink telemetry link. High-rate (per-frame) content stays on the local link for forensics; the GCS link is downsampled to **1–2 Hz** for situational awareness. + + **Numeric threshold.** + - GCS stream rate: `1–2 Hz` + - Local forensic stream: per-frame (AC-4.1 rate) + + **Rationale.** QGC's map view cannot render per-frame updates usefully; downsampling reduces bandwidth without losing situational awareness. Full-rate data is preserved in FDR (AC-NEW-3). + + **Validation method.** `sitl-simulation` (QGC downsample observable on the wire) + + **Test IDs.** + - _populated by scripts/gen_ac_traceability.py_ + + **Implementing components.** + - `src/gps_denied/components/mavlink_io/pymavlink_bridge.py` + + **Status.** active + +- **AC-6.2** — Operator re-localization commands + + **Statement.** The ground station can send commands to the onboard system (e.g., operator-assisted re-localization hint with approximate coordinates) via STATUSTEXT, NAMED_VALUE_FLOAT, or a custom MAVLink dialect. + + **Numeric threshold.** + - Command must be received and acted upon within one processing cycle + - Protocol: STATUSTEXT / NAMED_VALUE_FLOAT / custom dialect (implementation choice) + + **Rationale.** Operator-assisted re-localization is the fallback when automatic re-localization fails (AC-3.4 trigger path); without this channel the operator cannot intervene. + + **Validation method.** `sitl-simulation` + + **Test IDs.** + - _populated by scripts/gen_ac_traceability.py_ + + **Implementing components.** + - `src/gps_denied/components/mavlink_io/pymavlink_bridge.py` + + **Status.** active + +- **AC-6.3** — WGS84 coordinate format + + **Statement.** Output coordinates are in **WGS84** format (matches GPS_INPUT spec). + + **Numeric threshold.** + - All lat/lon fields in GPS_INPUT in WGS84 decimal degrees × 1e7 (MAVLink convention) + - No local coordinate systems exported to FC + + **Rationale.** GPS_INPUT spec requires WGS84; using a local ENU frame crashes the EKF. + + **Validation method.** `unit-test` (WGS84 coord format) + + **Test IDs.** + - _populated by scripts/gen_ac_traceability.py_ + + **Implementing components.** + - `src/gps_denied/components/mavlink_io/pymavlink_bridge.py` + - `src/gps_denied/core/eskf.py` + + **Status.** active + +--- + +## Object Localization (AI Camera) + +- **AC-7.1** — AI camera localization accuracy + + **Statement.** Other onboard AI systems may request GPS coordinates of objects detected by the AI camera. Localization accuracy is **consistent with the frame-center accuracy of the GPS-Denied system in level flight (bank/pitch <5°)**. In maneuvering flight, ground-projection error is bounded by `altitude × |sin(unknown_bank_or_pitch)|` and the system shall publish that bound alongside the estimate. + + **Numeric threshold.** + - Level flight (bank/pitch <5°): accuracy consistent with AC-1.1 / AC-1.2 + - Maneuvering flight: bound reported = `altitude_m × |sin(attitude_rad)|` + + **Rationale.** Object localization inherits all position error from the GPS-Denied system plus the camera projection uncertainty; the bound must be propagated honestly. + + **Validation method.** `unit-test` (trig) + `integration-test` + + **Test IDs.** + - _populated by scripts/gen_ac_traceability.py_ + + **Implementing components.** + - `src/gps_denied/components/coordinate_transforms/` + + **Status.** active + +- **AC-7.2** — AI camera trigonometric computation + + **Statement.** The system computes object coordinates trigonometrically using: current UAV GPS position (from GPS-Denied), known AI-camera gimbal angle, zoom, and current flight altitude. Flat-terrain assumption applies. + + **Numeric threshold.** + - Flat-terrain assumption: explicit, documented per-request + - Input: GPS-Denied position + gimbal angle + zoom + altitude (all mandatory fields) + + **Rationale.** Without explicit flat-terrain documentation, operators may use the output for terrain-varying tasks where it is not valid. + + **Validation method.** `unit-test` (trig) + `integration-test` + + **Test IDs.** + - _populated by scripts/gen_ac_traceability.py_ + + **Implementing components.** + - `src/gps_denied/components/coordinate_transforms/` + + **Status.** active + +--- + +## Satellite Reference Imagery + +- **AC-8.1** — Satellite imagery source and resolution + + **Statement.** Satellite reference imagery is provided by the **Azaion Suite Satellite Service** (a separate component of the Suite). The runtime onboard system consumes this service through an offline tile cache interface; it does **not** call commercial providers directly. Required resolution at the cache interface: **at least 0.5 m/pixel, ideally 0.3 m/pixel**. + + **Numeric threshold.** + - Minimum resolution: `0.5 m/px` + - Ideal resolution: `0.3 m/px` + + **Rationale.** Below 0.5 m/px, keypoint density drops below the minimum for reliable homography; the satellite-anchor MRE (AC-2.2) cannot be met. + + **Validation method.** `integration-test` + + **Test IDs.** + - _populated by scripts/gen_ac_traceability.py_ + + **Implementing components.** + - `src/gps_denied/components/satellite_matcher/local_tile_loader.py` + + **Status.** active + +- **AC-8.2** — Tile freshness by sector + + **Statement.** Satellite tiles consumed at runtime shall be: **<6 months old** for active-conflict sectors; **<12 months old** for stable rear sectors. System shall reject or downgrade-confidence on tiles older than these thresholds (see AC-NEW-6). + + **Numeric threshold.** + - Active-conflict sectors: `age_months < 6` + - Stable rear sectors: `age_months < 12` + + **Rationale.** Stale tiles in active-conflict sectors (building destruction, cratering) produce confident-but-wrong satellite anchors — worse than no anchor. + + **Validation method.** `integration-test` + + **Test IDs.** + - _populated by scripts/gen_ac_traceability.py_ + + **Implementing components.** + - `src/gps_denied/components/anchor_verifier/` + - `src/gps_denied/components/satellite_matcher/local_tile_loader.py` + + **Status.** active + +- **AC-8.3** — Pre-flight imagery loading + + **Statement.** Satellite imagery for the operational area shall be **pre-loaded and pre-processed** onto the companion computer before flight. Offline preprocessing time is not time-critical (minutes/hours). Pre-extracted tile descriptors (SuperPoint keypoints/descriptors and DINOv2-VLAD global descriptors) are part of the cache and count against the storage budget unless the solution draft defines a separate descriptor/index budget. + + **Numeric threshold.** + - All operational-area tiles available before `GPS_INPUT` is first emitted + - Storage: within 64 GB FDR cap (AC-NEW-3); descriptor index explicitly budgeted + + **Rationale.** In-flight tile downloads require connectivity that is unavailable in GPS-denied airspace. + + **Validation method.** `integration-test` + + **Test IDs.** + - _populated by scripts/gen_ac_traceability.py_ + + **Implementing components.** + - `src/gps_denied/components/satellite_matcher/local_tile_loader.py` + - `src/gps_denied/components/gpr/` + + **Status.** active + +- **AC-8.4** — Mid-flight tile generation and write-back + + **Statement.** During flight, the system shall continuously orthorectify navigation-camera frames into tiles aligned with the basemap projection and store them in the local cache, **deduplicated** so each ground sector is stored at most once (latest/highest-quality tile wins). On landing, the companion computer shall upload newly generated tiles back to the Azaion Suite Satellite Service so the next mission cache contains imagery refreshed by the previous flight. + + **Numeric threshold.** + - Deduplication: one tile per ground sector (latest wins) + - Upload: on-landing, before next mission + - Write eligibility gated by `can_persist_tile` (see SAFE-05) + + **Rationale.** Without write-back, the cache becomes stale relative to conflict-sector ground truth after the first mission; every subsequent mission anchors against pre-conflict imagery. + + **Validation method.** `integration-test` + + **Test IDs.** + - _populated by scripts/gen_ac_traceability.py_ + + **Implementing components.** + - `src/gps_denied/components/satellite_matcher/` + - `src/gps_denied/components/flight_recorder/` + + **Status.** deferred-stage3 (mid-flight orthorectification + write-back is a Stage 3 deliverable per REQUIREMENTS.md parking lot) + +- **AC-8.5** — Storage policy (no raw frame retention) + + **Statement.** The system shall **not** retain raw navigation-camera frames or AI-camera frames as part of normal operation. Tiles are the only persistent imagery artifact. Forensic exception: a low-rate (**≤0.1 Hz**) thumbnail log of frames that failed tile generation may be retained for debugging within the FDR budget (AC-NEW-3). + + **Numeric threshold.** + - Raw frame retention: zero bytes in normal operation + - Forensic thumbnail rate: `≤0.1 Hz` + + **Rationale.** Raw nav-cam frames at 3 Hz × 8 hours exceeds any reasonable storage budget and provides no operational value once tiles exist. + + **Validation method.** `integration-test` + `benchmark` + + **Test IDs.** + - _populated by scripts/gen_ac_traceability.py_ + + **Implementing components.** + - `src/gps_denied/components/flight_recorder/` + - `src/gps_denied/pipeline/orchestrator.py` + + **Status.** active + +- **AC-8.6** — VPR retrieval unit and change-robustness + + **Statement.** The Visual Place Recognition (VPR) FAISS index shall be built over ground-footprint-sized "VPR chunks" (~**600–800 m** at deployment altitude with **40–50 % overlap**), decoupled from the slippy-XYZ storage tile (z=20). VPR shall be **multi-scale** (fine-scale z=20-derived + coarser-scale z=17 or z=18 for active-conflict change-robustness). VPR top-K shall be **dynamically sized**: K=5 (stable, σ_xy ≤ 20 m), K=20 (active-conflict), K=50 (expanding-window fallback). VPR shall be **invoked conditionally** — not on every frame; DINOv2 forward runs only on re-loc triggers (cold start, sharp turn AC-3.2, σ_xy > 50 m, VO failure ≥2 frames, disconnected segment AC-3.3). + + **Numeric threshold.** + - Chunk size: `600–800 m` at deployment altitude + - Chunk overlap: `40–50 %` + - Multi-scale: z=20 (fine) + z=17 or z=18 (coarse) + - Dynamic K: K=5 / K=20 / K=50 per sector/covariance + - VPR invocation: conditional on re-loc triggers only + + **Rationale.** Unconditional DINOv2 inference at every frame violates AC-4.1 (400 ms budget). Change-robust coarse index prevents anchor failure in active-conflict sectors where the fine-scale tile may not match post-conflict ground truth. + + **Validation method.** `integration-test` + `benchmark` + + **Test IDs.** + - _populated by scripts/gen_ac_traceability.py_ + + **Implementing components.** + - `src/gps_denied/components/gpr/` + - `src/gps_denied/core/chunk_manager.py` + + **Status.** active + +--- + +## Extended Operational AC + +### AC-NEW-1 — Time-to-first-fix on cold start + +- **AC-NEW-1** — Time-to-first-fix on cold start + + **Statement.** From companion-computer boot, the system shall emit its first valid `GPS_INPUT` message in **<30 s**, given an IMU-extrapolated initial position handed over from the flight controller's EKF. + + **Numeric threshold.** + - TTFF: `95th percentile < 30 s` (measured over 50× cold reboots) + - Implementation drivers: TRT engines built at install time; CUDA/TRT init <5 s; FAISS index loaded before MAVLink connect + + **Rationale.** A mid-flight reboot at 60 km/h creates ~500 m IMU dead-reckoning drift in 30 s; the EKF can absorb this when the first fix arrives. Beyond 30 s the drift compounds. Cross-references: AC-1.3 (anchor-age at cold start); AC-5.3 (reboot recovery). + + **Validation method.** `deferred-hardware` (cold-boot bench requires Jetson hardware; cold-boot 50× with simulated FC-pose input) + + **Test IDs.** + - _deferred_hardware_ + + **Implementing components.** + - `src/gps_denied/pipeline/orchestrator.py` + - `src/gps_denied/core/models.py` + + **Status.** deferred-hardware-validation (Stage 3 on Jetson) + +### AC-NEW-2 — Spoofing-promotion latency + +- **AC-NEW-2** — Spoofing-promotion latency + + **Statement.** When the flight controller signals GPS denial or spoofing (ArduPilot fix-loss / EKF lane-switch event), the GPS-Denied system shall promote its own estimate to the FC's primary GPS source within **<3 s**. + + **Numeric threshold.** + - Promotion latency: `95th percentile < 3 s` + - Trigger: real-GPS health rolling average below threshold for ≥1 s + + **Rationale.** Without this gate, the FC may continue following a spoofed real-GPS source while our valid estimate sits idle. 3 s is short enough to prevent malicious heading changes but long enough to avoid single-frame anomaly false positives. + + **Validation method.** `sitl-simulation` (ArduPilot Plane SITL: inject false GPS_RAW_INT, measure time from spoof onset to promotion) + + **Test IDs.** + - _populated by scripts/gen_ac_traceability.py_ + + **Implementing components.** + - `src/gps_denied/components/mavlink_io/pymavlink_bridge.py` + + **Status.** active + +### AC-NEW-3 — Flight Data Recorder storage budget + +- **AC-NEW-3** — Flight Data Recorder storage budget + + **Statement.** The system shall retain to non-volatile storage, per flight: per-frame position estimates with covariance and source-label, IMU traces from the FC at full rate, all emitted `GPS_INPUT` frames, MAVLink raw stream (tlog), system health (CPU/GPU/temp/throttle), tiles generated mid-flight (AC-8.4), and a ≤0.1 Hz thumbnail log of failed-tile frames. **Raw nav-cam frames and AI-cam frames are NOT retained** (AC-8.5). Storage cap **64 GB / flight**; recorder rolls over (oldest segment dropped first) after cap. + + **Numeric threshold.** + - Storage cap: `≤64 GB per flight` + - Forensic thumbnail rate: `≤0.1 Hz` + - Rollover policy: oldest segment dropped; rollover event logged + + **Rationale.** Without bounded storage, an 8-hour mission fills any NVMe and silently drops forensic data. The 64 GB cap is sized for 8-hour mission with tiles + IMU + telemetry, leaving headroom for the persistent tile cache (AC-8.3). + + **Validation method.** `integration-test` (rollover behavior) + `benchmark` (synthetic 8-hour load) + `deferred-hardware` (real Jetson NVMe throughput in Stage 3) + + **Test IDs.** + - _populated by scripts/gen_ac_traceability.py_ + + **Implementing components.** + - `src/gps_denied/components/flight_recorder/` + + **Status.** active (CI scope) + deferred-hardware-validation (Stage 3) + +### AC-NEW-4 — False-position safety budget + +- **AC-NEW-4** — False-position safety budget + + **Statement.** + - P(reported estimate error > **500 m**) **< 0.1 %** per flight. + - P(reported estimate error > **1 km**) **< 0.01 %** per flight. + + **Numeric threshold.** + - `P(error > 500 m) < 0.001` + - `P(error > 1 km) < 0.0001` + - Measurement: Monte Carlo over AerialVL S03 + Mavic + Azaion 10.05.2026, ≥100 simulated flights + + **Rationale.** A single 1-km-off GPS_INPUT can fly the UAV outside the geofence in seconds. EKF covariance must be calibrated, not optimistic; outlier rejection (Mahalanobis gate) is mandatory. + + **Validation method.** `benchmark` (Monte Carlo over AerialVL S03 + Mavic + Azaion 10.05.2026) + + **Test IDs.** + - _populated by scripts/gen_ac_traceability.py_ + + **Implementing components.** + - `src/gps_denied/core/eskf.py` + - `src/gps_denied/components/anchor_verifier/` + + **Status.** active + +### AC-NEW-5 — Operational environmental envelope + +- **AC-NEW-5** — Operational environmental envelope + + **Statement.** Operating temperature **−20 °C to +50 °C**; vibration/shock per RTCA DO-160G low-altitude UAV-class envelope. The cooling solution shall sustain the **25 W** power mode at the upper temperature bound for the full **8-hour duty cycle** without thermal throttling. + + **Numeric threshold.** + - Temperature range: `−20 °C to +50 °C` + - Power mode: `25 W` sustained + - Duration: `8 hours` + - Pass: no thermal throttling events during hot-soak + + **Rationale.** Without this, all latency/accuracy ACs are conditional on a benign thermal day. Eastern Ukraine summers easily exceed +35 °C ambient inside a UAV bay; without active cooling, Jetson throttles to 15 W and AC-4.1 (400 ms budget) collapses. + + **Validation method.** `deferred-hardware` (hot-soak chamber at +50 °C for 8 h + cold-soak at −20 °C to AC-NEW-1 TTFF) + + **Test IDs.** + - _deferred_hardware_ + + **Implementing components.** + - Hardware cooling assembly (outside software scope) + - `src/gps_denied/components/flight_recorder/` (thermal event logging via FDR) + + **Status.** deferred-hardware-validation (Stage 3 on Jetson) + +### AC-NEW-6 — Imagery freshness enforcement + +- **AC-NEW-6** — Imagery freshness enforcement + + **Statement.** The system shall reject (or downgrade confidence on) any satellite tile whose capture date violates AC-8.2 (>6 months old in active-conflict sectors; >12 months old in stable rear sectors). Tiles generated mid-flight (AC-8.4) and not yet uploaded to the Suite Satellite Service are timestamped with the current flight date and treated as fresh. + + **Numeric threshold.** + - Confidence weight: `1.0` within freshness budget; linearly decayed to `0.0` over a 30-day grace zone; hard reject beyond grace + - Sector classification: provided pre-flight in operational area definition + + **Rationale.** Stale satellite tiles are the dominant cross-view-matching failure mode in active-conflict sectors. A confident match against a stale tile is worse than no match. + + **Validation method.** `unit-test` (rejection curve) + `integration-test` (cache integration) + + **Test IDs.** + - _populated by scripts/gen_ac_traceability.py_ + + **Implementing components.** + - `src/gps_denied/components/anchor_verifier/` + - `src/gps_denied/components/satellite_matcher/local_tile_loader.py` + + **Status.** active + +### AC-NEW-7 — Cache-poisoning safety budget + +- **AC-NEW-7** — Cache-poisoning safety budget + + **Statement.** Per flight, across all onboard tiles written by the in-flight ortho-tile generator: + - P(onboard tile geo-misaligned > **30 m**) **< 1 %**. + - P(onboard tile geo-misaligned > **100 m**) **< 0.1 %**. + + Component-1b (tile generator) enforces: σ_xy ≤ 5 m hard write gate (`trust_level = candidate`); σ_xy ≤ 3 m for full quality; σ_xy ∈ (3, 5] m marked `trust_level = soft`. Multi-flight voting (N≥2 independent flights confirm consistent geo-alignment within X m) is a Suite Satellite Service dependency for final promotion to trusted basemap. Cross-references: AC-8.4 (write-back); AC-NEW-4 (single-flight false-position budget). + + **Numeric threshold.** + - `P(geo-misalignment > 30 m) < 0.01` + - `P(geo-misalignment > 100 m) < 0.001` + - Onboard trust_level gate: σ_xy ≤ 5 m (candidate), σ_xy ≤ 3 m (full quality) + - Multi-flight voting: N≥2 flights (Service-side, deferred) + + **Rationale.** Onboard tiles feed back into the Suite Satellite Service basemap (AC-8.4); a confidently-bad EKF pose can write a misaligned tile that becomes the next flight's satellite anchor, compounding cross-flight error beyond what AC-NEW-4 covers. + + **Validation method.** + - `integration-test`: onboard `trust_level` gating (σ_xy ≤ 5 m hard, ≤ 3 m full quality) + - `deferred-hardware`: multi-flight voting (N≥2 flights, Service-side voting layer outside this build) + + **Test IDs.** + - _populated by scripts/gen_ac_traceability.py_ (for onboard trust_level gating) + - _deferred_hardware_ (for multi-flight voting) + + **Implementing components.** + - `src/gps_denied/components/satellite_matcher/` + - `src/gps_denied/components/anchor_verifier/` + + **Status.** active (onboard gate — Stage 2); deferred-hardware-validation (multi-flight voting — Stage 3) + +### AC-NEW-8 — Visual blackout + GPS spoofing degraded-mode budget + +- **AC-NEW-8** — Visual blackout + GPS spoofing degraded-mode budget + + **Statement.** When the navigation camera is fully unusable for visual localization and the flight controller simultaneously reports GPS denial/spoofing, the onboard system shall: + - continue emitting `GPS_INPUT` from IMU-only propagation for **up to 30 s** after the last trusted visual/satellite anchor, unless the estimator covariance exceeds the fail threshold earlier; + - label every estimate `{dead_reckoned}` and set `fix_type=2` or lower when the 95 % covariance semi-major axis exceeds **100 m**; + - emit `fix_type=0`, `horiz_accuracy=999.0`, and `STATUSTEXT: VISUAL_BLACKOUT_FAILSAFE` when the 95 % covariance semi-major axis exceeds **500 m** OR visual blackout exceeds **30 s** without a trusted re-anchor; + - never promote spoofed real-GPS measurements back into the estimator during blackout unless the FC GPS health has been stable and non-spoofed for **≥10 s** and a visual/satellite consistency check has succeeded. Cross-references: AC-1.4 (source_label vocab); AC-3.5 (blackout mode switch). + + **Numeric threshold.** + - IMU-only emit duration: `≤30 s` + - `dead_reckoned` + `fix_type=2` when `σ_xy > 100 m` + - `fix_type=0` + `horiz_accuracy=999.0` + `STATUSTEXT VISUAL_BLACKOUT_FAILSAFE` when `σ_xy > 500 m` OR blackout > 30 s + - GPS re-promotion gate: ≥10 s non-spoofed health + visual/satellite consistency check + + **Rationale.** A cloud/whiteout period removes all visual correction exactly when spoofed GPS cannot be trusted. Honest IMU-only dead reckoning with rapidly growing uncertainty is the only safe behavior. + + **Validation method.** `blackbox-replay` + `sitl-simulation` (inject 5 s, 15 s, 35 s full-camera blackout while spoofing GPS_RAW_INT; assert mode transition ≤400 ms, spoofed GPS ignored, covariance grows monotonically, GPS_INPUT fields degrade at thresholds, recovery only after trusted anchor or 10 s GPS-health gate) + + **Test IDs.** + - _populated by scripts/gen_ac_traceability.py_ + + **Implementing components.** + - `src/gps_denied/components/safety_state/` + - `src/gps_denied/core/eskf.py` + - `src/gps_denied/components/mavlink_io/pymavlink_bridge.py` + + **Status.** active diff --git a/_docs/00_problem/acceptance_criteria_draft.md b/_docs/00_problem/acceptance_criteria_draft.md new file mode 100644 index 0000000..9199b33 --- /dev/null +++ b/_docs/00_problem/acceptance_criteria_draft.md @@ -0,0 +1,50 @@ +# Position Accuracy + +- The system should determine GPS coordinates of frame centers for 80% of photos within 50m error compared to real GPS +- The system should determine GPS coordinates of frame centers for 60% of photos within 20m error compared to real GPS +- Maximum cumulative VO drift between satellite correction anchors should be less than 100 meters +- System should report a confidence score per position estimate (high = satellite-anchored, low = VO-extrapolated with drift) + +# Image Processing Quality + +- Image Registration Rate > 95% for normal flight segments. The system can find enough matching features to confidently calculate the camera's 6-DoF pose and stitch that image into the trajectory +- Mean Reprojection Error (MRE) < 1.0 pixels + +# Resilience & Edge Cases + +- The system should correctly continue work even in the presence of up to 350m outlier between 2 consecutive photos (due to tilt of the plane) +- System should correctly continue work during sharp turns, where the next photo doesn't overlap at all or overlaps less than 5%. The next photo should be within 200m drift and at an angle of less than 70 degrees. Sharp-turn frames are expected to fail VO and should be handled by satellite-based re-localization +- System should operate when UAV makes a sharp turn and next photos have no common points with previous route. It should figure out the location of the new route segment and connect it to the previous route. There could be more than 2 such disconnected segments, so this strategy must be core to the system +- In case the system cannot determine the position of 3 consecutive frames by any means, it should send a re-localization request to the ground station operator via telemetry link. While waiting for operator input, the system continues attempting VO/IMU dead reckoning and the flight controller uses last known position + IMU extrapolation + +# Real-Time Onboard Performance + +- Less than 400ms end-to-end per frame: from camera capture to GPS coordinate output to the flight controller (camera shoots at ~3fps) +- Memory usage should stay below 8GB shared memory (Jetson Orin Nano Super — CPU and GPU share the same 8GB LPDDR5 pool) +- The system must output calculated GPS coordinates directly to the flight controller via MAVLink GPS_INPUT messages (using MAVSDK) +- Position estimates are streamed to the flight controller frame-by-frame; the system does not batch or delay output +- The system may refine previously calculated positions and send corrections to the flight controller as updated estimates + +# Startup & Failsafe + +- The system initializes using the last known valid GPS position from the flight controller before GPS denial begins +- If the system completely fails to produce any position estimate for more than N seconds (TBD), the flight controller should fall back to IMU-only dead reckoning and the system should log the failure +- On companion computer reboot mid-flight, the system should attempt to re-initialize from the flight controller's current IMU-extrapolated position + +# Ground Station & Telemetry + +- Position estimates and confidence scores should be streamed to the ground station via telemetry link for operator situational awareness +- The ground station can send commands to the onboard system (e.g., operator-assisted re-localization hint with approximate coordinates) +- Output coordinates in WGS84 format + +# Object Localization + +- Other onboard AI systems can request GPS coordinates of objects detected by the AI camera +- The GPS-Denied system calculates object coordinates trigonometrically using: current UAV GPS position (from GPS-Denied), known AI camera angle, zoom, and current flight altitude. Flat terrain is assumed +- Accuracy is consistent with the frame-center position accuracy of the GPS-Denied system + +# Satellite Reference Imagery + +- Satellite reference imagery resolution must be at least 0.5 m/pixel, ideally 0.3 m/pixel +- Satellite imagery for the operational area should be less than 2 years old where possible +- Satellite imagery must be pre-processed and loaded onto the companion computer before flight. Offline preprocessing time is not time-critical (can take minutes/hours)