mirror of
https://github.com/azaion/gps-denied-onboard.git
synced 2026-04-27 16:46:36 +00:00
9eba1689b3
- Revised acceptance criteria in the acceptance_criteria.md file to clarify metrics and expectations, including updates to GPS accuracy and image processing quality. - Enhanced restrictions documentation to reflect operational parameters and constraints for UAV flights, including camera specifications and satellite imagery usage. - Added new research documents for acceptance criteria assessment and question decomposition to support ongoing project evaluation and decision-making.
514 lines
72 KiB
Markdown
514 lines
72 KiB
Markdown
# Solution Draft 02
|
||
|
||
> **Mode**: B (Solution Assessment of `solution_draft01.md`).
|
||
> **Inputs**: `solution_draft01.md` (Mode A) + `_docs/00_research/{03_mode_b_decomposition,04_reasoning_chain_mode_b,05_validation_log_mode_b}.md` + Mode B sources S40–S57 in `01_source_registry.md` + Mode B fact cards M-1..M-21 in `02_fact_cards.md`.
|
||
> **Date**: 2026-04-26 (revised after user lock-in of open items Q1–Q5).
|
||
> **Self-contained**: yes — this draft is the new source of truth and supersedes `solution_draft01.md`.
|
||
>
|
||
> **Locked-in user decisions (2026-04-26)**:
|
||
>
|
||
> - **Q1** → A: GPS_INPUT + ODOMETRY hybrid output (M-1). Codified in AC-4.3.
|
||
> - **Q2** → A: distinct system-IDs via ArduPilot native MAVLink routing; **no `mavlink-router` daemon** (M-6).
|
||
> - **Q3** → A: AC-NEW-7 thresholds confirmed at P(>30 m)<1 %, P(>100 m)<0.1 % per flight (M-9). Codified in AC-NEW-7.
|
||
> - **Q4** → A: TartanAir V2 included as early-stage synthetic baseline in the bench-off (M-13).
|
||
> - **Q5** → B: proceed to Plan in a fresh conversation (no further Mode B round).
|
||
> - Camera spec → ADTi 20MP 20L V1 APS-C; storage zoom → z=20 (M-20). Codified in `restrictions.md`.
|
||
|
||
---
|
||
|
||
## Assessment Findings
|
||
|
||
The "old solution → weak point → new solution" table for the v1 commitments. Every row references the corresponding fact card (M-X) for traceability. **15 findings**: 4 high-severity functional, 2 high-severity security, 1 high-severity safety, 1 high-severity-positive (latency easier than thought), 6 medium, 1 open question.
|
||
|
||
|
||
| Old Component Solution | Weak Point | New Solution |
|
||
| ------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
||
| **C-6**: emit `GPS_INPUT` only via pymavlink (`GPS1_TYPE=14`) — covariance collapsed to scalar `h_acc`/`v_acc`. | **Functional** (M-1). ArduPilot dev docs (S41) call **ODOMETRY the preferred external-nav channel**; ODOMETRY carries quaternion + 6-DoF covariance + native quality field. GPS_INPUT-only under-utilises the FC's EKF3 and erases our yaw covariance — directly hurts AC-NEW-4 (false-position safety). | **Hybrid output**. GPS_INPUT remains the primary "GPS-substitute" channel (matches AC-4.3 framing). When the companion EKF emits a fix with full 6-DoF covariance and observability, **also emit ODOMETRY** so EKF3 can fuse the richer signal. FC source priorities config'd so GPS_INPUT is the failover if ODOMETRY trips VISO_QUAL_MIN. |
|
||
| **C-3**: bench-off shortlist = {SP+LG, XFeat sparse, XFeat semi-dense, MASt3R (stretch), RoMa/DKM (bench-off candidate), classical (last-resort)}. | **Functional** (M-2). MASt3R `mast3r-runtime` lists Jetson Orin support as **"Planned"**, not implemented (S57). Speedy MASt3R = 91 ms/pair on **A40 GPU**; Orin Nano Super throughput ≈ 1/30 of A40 → MASt3R ≈ **2.5–3 s/pair**, ~7× over the 400 ms p95 budget. | Drop MASt3R from the v1 bench-off; mark it **research-track-only** (long-horizon distillation experiment). |
|
||
| **C-3**: bench-off shortlist (same row, expansion). | **Functional** (M-3). GIM (S48, ICLR 2024 spotlight) gives drop-in 8.4–18.1 % zero-shot improvement over LightGlue/RoMa/DKM/LoFTR by self-training on internet videos. Same TRT path as vanilla SP+LG, better cross-domain transfer — exactly our regime (zero training data on eastern-Ukraine 1 km AGL). | Add **GIM-LightGlue** to the bench-off as a peer of vanilla SP+LG. |
|
||
| **C-2**: VPR shortlist = AnyLoc (primary) + MixVPR (degraded-power fast lane). | **Functional** (M-4). Two CVPR 2024 papers landed after the Mode A draft was written: **DINOv2 SALAD** (S47) — DINOv2 + Sinkhorn-VLAD, R@1 = 75 % MSLS Challenge / 92.2 % MSLS Val / 76 % NordLand; **BoQ** (S46) — bag of learnable queries, beats NetVLAD/MixVPR/EigenPlaces/Patch-NetVLAD/TransVPR/R2Former on 14 benchmarks. | VPR shortlist grows to **{AnyLoc, SALAD, BoQ, MixVPR}**. AnyLoc retained as training-free fallback; SALAD and BoQ are likely primaries. |
|
||
| **C-2 / C-9**: latency budget for AnyLoc (DINOv2 ViT-B) = 50–80 ms/inf at 224×224 (estimated). | **Performance, positive direction** (M-5). Jetson AI Lab L1 measurements (S40): **DINOv2-base-patch14 = 126 inf/s = ~8 ms/inf at 224×224** on Orin Nano Super (FP16 trtexec). Real number is ~6–10× better than draft's estimate. | AC-4.1 (400 ms p95) is comfortably feasible. **R2 (latency) downgraded High → Medium**. Empirical confirmation still required, but no longer make-or-break. |
|
||
| **C-6**: "MAVSDK + pymavlink share the same serial / TCP MAVLink endpoint via a single `mavlink-router` instance." | **Security** (M-6). mavlink-router has a public, fuzzing-discovered, easily-triggered **stack-based buffer overflow** in config-file parsing (S45 issue #436). Repo has **no SECURITY.md**, no formal advisory process. Drops a known-vulnerable C++ daemon onto a flight-critical companion. | **Replace mavlink-router**. v1 default: distinct system-IDs for MAVSDK and pymavlink, sharing the serial port via ArduPilot's native MAVLink routing — no router daemon at all. v1.1 fallback: in-process MAVLink endpoint multiplexer (~150 LOC). |
|
||
| **C-6 / Security**: "MAVLink2 signing is recommended (deferred to a Phase-4 security pass)." | **Security** (M-7). GPS_INPUT (and now ODOMETRY) is a high-trust local channel feeding the flight-critical EKF. Without signing, anyone with serial-line access on the airframe can crash the vehicle by injecting a malicious fix. Cost of enabling signing is one operator key-provisioning step per airframe (S44). | Promote MAVLink2 signing to **v1 hard configuration item**. Document the key-provisioning procedure in the deploy runbook. Verify signing-on at boot; refuse to inject GPS_INPUT/ODOMETRY if the FC reports signing-off on our link. |
|
||
| **C-1**: "Tile format = MBTiles SQLite + per-tile metadata. Single file, mmap-friendly, ubiquitous." | **Performance** (M-8). Default SQLite rollback journal mode + concurrent reader (matcher cache lookup at ≤3 fps × ~30 candidate tiles) + writer (Component 1b ortho-tile write at ≤1–2 Hz × ~30 tiles) → guaranteed `database is locked` failures (S54). | Specify **MBTiles SQLite + WAL + connection pool + per-cycle transaction batching**. Multiple read connections + one write connection. Tile-cache lookup p95 ≤ 5 ms is now a measurable AC-4.1 sub-budget. |
|
||
| **C-1b**: tile dedup rule "If cache has stale service tile AND our quality > existing → write (overwrites with `source = onboard`)". Quality = inlier count + sharpness. | **Safety** (M-9). EKF over-confidence (a known failure mode) escapes the σ_xy ≤ 10 m generation gate; a confidently-bad pose writes a misaligned tile that becomes the next flight's anchor → cross-flight error compounding. AC-NEW-4 doesn't model this. | (a) **Service tiles are immutable within freshness budget** — onboard tiles overwrite only stale or other-onboard tiles. (b) **Voting layer at the Service ingest**: onboard tile gets promoted to "trusted basemap" only after **N≥2 independent flights** confirm consistent geo-alignment. (c) Quality score includes **parent-pose covariance as a hard gate** (σ_xy ≤ 5 m, tighter than the 10 m generation gate); tiles above that gate are marked "soft" in their sidecar. (d) New AC: **AC-NEW-7 — cache-poisoning safety budget**: P(onboard tile mis-aligned > 30 m) per flight < 1 %; P(>100 m) per flight < 0.1 %. |
|
||
| **C-9**: "single Python process (asyncio) with TRT inference workers via CUDA IPC for tensor handoff." | **Functional** (M-10). Free-threaded Python 3.13 is **experimental**, has substantial single-threaded perf hit, and **GIL re-enables on import of any non-FT-aware C extension** (S55) — which would silently include numba, possibly TRT bindings, possibly older pymavlink. Free-threading is not a v1 escape hatch. | Stay on **CPython 3.11 or 3.12** for v1. Sharpen the rationale: the choice is "asyncio + TRT subprocess workers + numba JIT on hot path is the production-ready combination today; revisit free-threading in v1.1 once NumPy/SciPy/numba/TRT bindings stabilise on PEP 703". |
|
||
| **C-5 / C-6**: source-promotion logic "we **immediately** promote our `GPS_INPUT` to fix_type=3D and assert" on FC fix degradation. | **Functional / safety** (M-11). ArduPilot's external-nav source-switching path has known production gotchas (S41, S42 PR #19563, S43 PR #30080 active 2025): companion-derived velocity errors, position-estimate resets when external-nav reference is lost, conflicts when running alongside GPS. AC-NEW-2 (3 s spoofing-promotion latency) **is** that path. | Promote **F-T9 SITL coverage of source-switching** from "verify the loop closes" to a **hard test gate**. Test matrix: jam-onset → our channel; spoofed-real-GPS recovery → operator-confirmed source-restore; `EK3_SRC1_`* parameter combinations across both GPS_INPUT-primary and ODOMETRY-primary. Pin ArduPilot to the version containing PR #30080. |
|
||
| **C-1b / R-Terrain**: "flat-Earth model" everywhere; "operational area is flat steppe (R-Terrain)". | **Functional / safety** (M-12). Eastern-Ukraine relief amplitude reaches **~24 m peak-to-trough** in Kharkiv survey areas (S56), with creek + gully (yary/balky) systems. At 1 km AGL with 35° HFOV, that produces ~17 m horizontal misalignment at frame edge under flat-Earth ortho. Inside AC-1.1 (50 m@80 %) but eats into AC-1.2 (20 m@50 %). | **Per-sector DEM lookup** in pre-flight. Classify sectors: **flat** (≤5 m amplitude, full anchor weight), **moderate** (5–15 m, weight × 0.7), **rugged** (>15 m, skip ortho-tile generation, weight × 0.3 with `rugged_sector` telemetry flag). Use SRTM 30 m DEM (free; ~30 MB for 400 km²). Add a runtime self-classifier: if matcher RANSAC inlier ratio drops < threshold for K consecutive frames, auto-promote the sector to "rugged" for the rest of the flight. |
|
||
| **C-3 / V&V**: bench-off targets = AerialVL + UAV-VisLoc + internal Mavic. | **Functional** (M-14). None of those grade **extreme-pitch / extreme-scale / extreme-overlap separately**. AerialExtreMatch (S49, 1.5 M synthetic pairs, 32 difficulty levels) covers exactly the failure-mode axes that matter; **2chADCNN** (S50, MDPI Drones 2023) is a published season-robustness ceiling reference. | Add **AerialExtreMatch** as a primary structured-difficulty regression bench. Use **2chADCNN as a season-robustness ceiling reference number only** — *not* as a bench-off candidate. (2chADCNN's outputs are template-overlap regions, not sub-pixel keypoints; its tested altitude band is 252–500 m, not 1 km; and it has no Jetson benchmark. Keypoint-grade modern matchers — SP+LG, GIM-LightGlue, GIM-RoMa — are the bench-off candidates.) |
|
||
| **C-4 / AC-1.3**: "<100 m drift VO-only / <50 m with IMU" budget — implicit confidence based on ORB-SLAM3 / VINS-Fusion baselines. | **Functional** (M-15). S52 (AFIT thesis) — SVO/DSO/ORB-SLAM2 all **had significant difficulty** maintaining localisation on real fixed-wing flights. Our framing (VO between satellite anchors, not standalone metric SLAM) is correct, but the AC-1.3 budget needs validation against a real fixed-wing baseline — *not* Mavic-class footage. | New risk **R8 — fixed-wing VO drift under AC-1.3 budget is unconfirmed**. Mitigations: (a) borrow AerialVL's 70 km of fixed-wing trajectories for **F-T1b** AC-1.3 regression; (b) plan the first internal fixed-wing flight before AC lock, not as a stretch. |
|
||
| **R7 / Datasets**: "MidAir / synthetic IMU is dropped; AerialVL primary; internal Mavic for deployment-domain proxy." | M-13. TartanAir V2 (S51) is photo-realistic synthetic with **native IMU + 12-cam + 65 environments + season variation + custom camera models**, configurable motion patterns — dynamics-mismatch argument weaker than for MidAir. | **CONFIRMED (Q4 = A, 2026-04-26)** — TartanAir V2 added as early-stage synthetic baseline alongside AerialVL + UAV-VisLoc + AerialExtreMatch + 2chADCNN-season-set + Mavic. Used for sweeping seasons / lighting / pitches before real fixed-wing flight (FT-3) lands. |
|
||
| **C-2 / Granularity**: "FAISS IVF over **per-tile** DINOv2-VLAD vectors" using z=20 storage tiles (~154 × 154 m). | **Functional, high** (M-16). A 1 km AGL frame covers 30–100 z=20 tiles. Cosine similarity between a frame descriptor (covers ~600 × 450 m of ground) and a single-tile descriptor (covers 154 × 154 m) is fundamentally mismatched. None of AerialVL / AnyLoc / NaviLoc do per-storage-tile retrieval; they use frame-footprint-sized reference chunks with overlap. | **Decouple VPR chunk from storage tile.** Storage tile = z=20 / 512×512 (kept for orthorect + dedup). **VPR chunk** = ground-footprint-sized window (e.g. ~600 × 450 m at the deployment altitude band) with **40–50 % overlap**, optionally multi-scale across altitude bands. FAISS index is over chunks, not tiles. Frame descriptor is computed once per *invoked* frame after IMU-heading de-rotation. |
|
||
| **C-2 / Invocation**: VPR runs on every retrieval cycle. | **Performance, medium** (M-17). VPR's value is concentrated in re-loc paths (cold start, sharp turn, disconnected segment, large σ_xy). In steady state — recent anchor < 2 s, σ_xy < 20 m, VO healthy — a **geometric prior** from IMU+VO predicted position picks top-K candidate chunks by distance alone, no DINOv2 forward needed. | **Conditional VPR invocation.** `if (steady_state) { rank top-K by geometric distance } else { invoke VPR }`. Saves ~10–35 ms/frame in cruise. DINOv2 TRT engine stays resident for low-latency wake-up. |
|
||
| **C-2 / Fallback**: no defined behaviour when top-1 retrieval is "unconvinced". | **Resilience, medium** (M-18). If top-1 similarity is below threshold OR top-1/top-2 similarity gap is below threshold, the system today goes straight to "no anchor → VO/IMU dead-reckoning" — wasteful, since an adjacent chunk is often correct. | **Expanding-window retry.** On unconvincing top-1, expand the candidate set to adjacent VPR chunks (±1 in each direction; ~8 neighbours for square-grid layout) and let the matcher (Component 3) decide via inlier ratio + reprojection error. Same FAISS index, larger K, no extra DINOv2 forward. |
|
||
|
||
|
||
---
|
||
|
||
## Product Solution Description
|
||
|
||
A companion-computer software stack that runs on the **Jetson Orin Nano Super** alongside an **ArduPilot 4.5+** flight controller and provides **GPS-equivalent position fixes** to the autopilot when real GPS is jammed, spoofed, or denied. It does so by continuously matching the downward navigation-camera feed against a **pre-cached satellite basemap supplied by the Azaion Suite Satellite Service** and fusing match-derived absolute positions with onboard **Visual Odometry** and the autopilot's high-rate **IMU** in a loosely-coupled EKF. The fused estimate is exported on **two MAVLink channels in parallel**: `GPS_INPUT` (the primary "GPS-substitute" channel matching AC-4.3) and `ODOMETRY` (when our pose has full 6-DoF covariance, so the FC's EKF3 can fuse the richer signal).
|
||
|
||
During flight the system also **generates fresh tiles** from the navigation camera, classifies each sector against a pre-loaded SRTM-30 m DEM (skip rugged sectors), deduplicates new tiles against the existing cache (service tiles immutable within freshness budget), and uploads the new tiles to the **Suite Satellite Service candidate pool** on landing — where a **2-flight voting layer** promotes onboard tiles to "trusted basemap" only after independent confirmation. **No raw frames are persisted** — the tile is the unit of storage.
|
||
|
||
A separate path computes ground-projected GPS coordinates for objects detected by the AI camera using gimbal angle, airframe attitude, and altitude.
|
||
|
||
The MAVLink endpoint is shared between MAVSDK (telemetry) and pymavlink (`GPS_INPUT` + `ODOMETRY`) by **distinct system-IDs through ArduPilot's native MAVLink routing** — no `mavlink-router` daemon. **MAVLink2 signing is mandatory in v1** between companion and FC, with a documented per-airframe key-provisioning procedure.
|
||
|
||
```
|
||
Pre-flight (ground)
|
||
┌────────────────────────────────────────────────┐
|
||
│ Azaion Suite Satellite Service │
|
||
│ (sources commercial / agency imagery; │
|
||
│ ingests onboard tiles via candidate pool + │
|
||
│ 2-flight voting layer) │
|
||
└──────────────┬───────────────────┬─────────────┘
|
||
│ sync down │ upload back (post-flight, candidate pool)
|
||
▼ ▲
|
||
┌─────────────────┐
|
||
│ DEM (SRTM 30 m) │ ─────► sector classification (flat/moderate/rugged)
|
||
└─────────────────┘
|
||
Onboard (in-flight)
|
||
Nav Cam: ADTi 20MP, 3 fps AI Cam (gimbal+zoom, on-demand)
|
||
│ │
|
||
▼ ▼
|
||
┌────────────────────────────────┐ ┌──────────────────────┐
|
||
│ GPS-Denied Pipeline │ │ Object Geo-Locator │
|
||
│ ┌──────────────────────┐ │ │ (pinhole + ATTITUDE │
|
||
│ │ Visual Odometry │ │ │ MAVLink fusion) │
|
||
│ │ (SP+LG 2-frame homog)│ │ └──────────┬───────────┘
|
||
│ └──────────┬───────────┘ │ │
|
||
│ ▼ │ │
|
||
│ ┌──────────────────────┐ │ │
|
||
│ │ Place Recognition │←──┐ │ │
|
||
│ │ (SALAD/BoQ lead, │ │ │ │
|
||
│ │ AnyLoc fallback) │ │ │ │
|
||
│ └──────────┬───────────┘ │ │ │
|
||
│ ▼ │ │ │
|
||
│ ┌──────────────────────┐ │ │ │
|
||
│ │ Cross-view Matcher │ │ │ │
|
||
│ │ (SP+LG TRT/FP16 lead │ │ │ │
|
||
│ │ + GIM-LG bench peer)│ │ │ │
|
||
│ └──────────┬───────────┘ │ │ │
|
||
│ ▼ │ │ │
|
||
│ ┌──────────────────────┐ │ │ │
|
||
│ │ EKF Fusion │←──┼───┼── IMU (FC) │
|
||
│ │ (loose-coupled, │ │ │ │
|
||
│ │ Python + numba) │ │ │ │
|
||
│ └──────────┬───────────┘ │ │ │
|
||
│ │ │ │ │
|
||
│ ├──► Ortho-Tile Generator ──► Tile Cache (NVMe, MBTiles+WAL+pool)
|
||
│ │ (skip if rugged sector; │ ▲
|
||
│ │ σ_xy hard gate ≤5m for hard │ │ dedup w/
|
||
│ │ write; soft tiles flagged) │ │ service-tile
|
||
│ │ └───┘ immutability
|
||
│ ▼ │
|
||
└────────────┼──────────────────────────────────┘
|
||
▼
|
||
GPS_INPUT (pymavlink, signed MAVLink2) ─────► ArduPilot (GPS1_TYPE=14)
|
||
ODOMETRY (pymavlink, signed MAVLink2) ─────► ArduPilot (EK3_SRC1_* = ExternalNav)
|
||
│
|
||
▼
|
||
Telemetry summary 1–2 Hz ───────────► QGroundControl (signed)
|
||
│
|
||
▼
|
||
Flight Data Recorder (NVMe, 64 GB cap)
|
||
(tiles + telemetry + IMU + tlog + per-sector flags; NO raw frames)
|
||
│
|
||
▼
|
||
Post-flight (landing)
|
||
┌────────────────────────────────────────────────┐
|
||
│ Tile uploader → Suite Satellite Service │
|
||
│ → CANDIDATE POOL │
|
||
│ → 2-flight voting → trusted-basemap promotion │
|
||
└────────────────────────────────────────────────┘
|
||
```
|
||
|
||
---
|
||
|
||
## Architecture
|
||
|
||
### Overall principles
|
||
|
||
1. **Pipeline = stages with explicit confidence**. Each stage emits a pose hypothesis + covariance + categorical label. Downstream EKF fuses by covariance.
|
||
2. **All heavy NN inference runs on GPU via TensorRT** (FP16, INT8 where validated). Pre-extract satellite-tile descriptors offline (AC-8.3).
|
||
3. **Single-process Python orchestrator** (asyncio, **CPython 3.11/3.12**) owns I/O, MAVLink, telemetry, FDR. **Inference workers** are TRT-engine processes on GPU. Free-threaded Python deferred to v1.1 (M-10).
|
||
4. **Persistent satellite cache** across flights (~10 GB for 400 km²); per-flight FDR (ACvisu-NEW-3) is separate.
|
||
5. **Every output to the FC carries a covariance** — both GPS_INPUT (`h_acc`, `v_acc`, `vel_acc`) and ODOMETRY (full 21-element matrix). Never a bare lat/lon.
|
||
6. **Service tiles are basemap truth**; onboard tiles are candidate input that goes through a Service-side voting layer before becoming basemap (M-9).
|
||
7. **MAVLink2 signing on every companion↔FC link** (M-7). USB bypasses signing — bench-only access.
|
||
|
||
---
|
||
|
||
### Component 1: Satellite Tile Cache & Descriptor Index
|
||
|
||
|
||
| Aspect | Choice | Rationale / change vs. Mode A |
|
||
| ----------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||
| Tile format | **MBTiles SQLite + WAL mode + connection pool + per-cycle transaction batching** | M-8: WAL is mandatory under our concurrent reader+writer load. Pool gives multiple read connections + one write connection. Without these, `database is locked` errors are guaranteed. |
|
||
| Tile coordinate system | Slippy-map XYZ at zoom 20 (~30 cm/px) | Unchanged. |
|
||
| Tile size | 512 × 512 | Unchanged. |
|
||
| Descriptor index | FAISS IVF (cosine) over per-tile DINOv2-VLAD vectors | Unchanged. **New constraint**: index loadable on ≤4 GB GPU RAM (Jetson budget, M-5 / W13 cross-check). |
|
||
| Per-tile keypoints | SuperPoint + LightGlue descriptors precomputed pre-flight | Unchanged. **Parallel index** for GIM-LightGlue keypoints if the bench-off picks GIM. |
|
||
| Freshness metadata | `capture_date`, `sector_class ∈ {active,stable}`, `source ∈ {service,onboard}`, `terrain_class ∈ {flat,moderate,rugged}`, `trust_level ∈ {basemap,candidate,soft}` | Adds `terrain_class` (M-12) and `trust_level` (M-9). |
|
||
| Encryption at rest | AES-GCM, key from secure element on the FC or the companion's TPM | Unchanged. |
|
||
| **Service-tile immutability** | **Service-source tiles are immutable within freshness budget; onboard tiles overwrite only stale or other-onboard tiles** | **New (M-9).** Critical to prevent cross-flight cache poisoning. |
|
||
|
||
|
||
**Per-flight storage budget.** ~10 GB persistent for the 400 km² operational-area cache. Plus ~30 MB for SRTM-30 m DEM coverage (M-12).
|
||
|
||
---
|
||
|
||
### Component 1b: Ortho-Tile Generator (in-flight tile creation & write-back)
|
||
|
||
**Responsibility (AC-8.4).** Same as Mode A draft, with the following changes:
|
||
|
||
**Pipeline per frame:**
|
||
|
||
1. **Eligibility check** (changed). Skip tile generation when:
|
||
- EKF source label is `dead_reckoned`.
|
||
- **σ_xy > 5 m** (was 10 m — M-9 hard gate).
|
||
- Airframe roll/pitch (from FC `ATTITUDE`) > 10°.
|
||
- VPR + cross-view match returned no inliers.
|
||
- **Sector is classified as `rugged` in the pre-flight DEM lookup** (M-12) — skip ortho-tile generation entirely for that sector.
|
||
For sectors classified as `moderate`, generate but flag the tile sidecar `terrain_uncertainty=true`.
|
||
2. **Orthorectification.** Pinhole projection on the **per-sector DEM** (flat-Earth in flat sectors; SRTM-30 m DEM lookup in moderate sectors).
|
||
3. **Resampling to basemap projection.** Unchanged.
|
||
4. **Quality scoring** (changed). Adds **σ_xy from EKF as hard gate**:
|
||
- `sharpness` (variance of Laplacian),
|
||
- `coverage` (fraction inside source frame),
|
||
- `match_inliers` (RANSAC inlier count),
|
||
- `parent_pose_sigma_xy` (EKF position covariance — a tile written from σ_xy ∈ [3, 5] m is `trust_level = soft`; σ_xy ≤ 3 m is `trust_level = candidate`),
|
||
- `glare/cloud` flag.
|
||
5. **Deduplication / write decision** (changed per M-9):
|
||
- If cache has no tile at that key → **write** (`source = onboard`, `trust_level = candidate` or `soft`).
|
||
- If cache has a tile and it's `source = service` and **within** AC-8.2 freshness budget → **never overwrite** (was: overwrite if our quality > existing).
|
||
- If cache has a tile and it's `source = service` and **outside** AC-8.2 freshness budget → write only if our parent-pose σ_xy ≤ 3 m AND quality score > existing.
|
||
- If cache has a tile from `source = onboard` from this same flight, but our quality is materially better → write.
|
||
- Otherwise → skip.
|
||
6. **Sidecar metadata** (extended). Includes `parent_pose_sigma_xy`, `terrain_class`, `trust_level`.
|
||
|
||
**Post-flight uploader** (changed). Onboard tiles are pushed to the Suite Service **candidate pool**, not directly to the basemap. Service ingest applies the **2-flight voting rule** (M-9) before promoting candidate tiles to trusted basemap. Tiles already at `trust_level = soft` upload but with the soft-trust flag preserved.
|
||
|
||
---
|
||
|
||
### Component 2: Visual Place Recognition (Global Retrieval)
|
||
|
||
**Role.** VPR is a **resilience module**, not an every-frame primary-loop module. Its job is to narrow ~10⁴–10⁵ candidate ground-footprint chunks down to a top-K (5–10) when a geometric prior from IMU+VO is unavailable or untrusted. In steady-state cruise we use the geometric prior alone; we invoke VPR on **re-loc triggers** (M-17). VPR is essential for the resilience ACs (AC-NEW-1 cold start, AC-3.2 sharp turn re-loc, AC-3.3 disconnected segment); it is *not* essential to every steady-state frame.
|
||
|
||
#### Retrieval unit (revised — M-16)
|
||
|
||
The VPR retrieval unit is **decoupled** from the storage tile:
|
||
|
||
|
||
| Concept | Size | Purpose |
|
||
| ------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------- |
|
||
| **Storage tile** (Component 1) | z=20 slippy XYZ, 512×512 (~154 × 154 m ground) | Orthorectification, dedup, basemap update. Storage layer only. |
|
||
| **VPR chunk** *(new)* | Ground-footprint-sized to expected frame coverage (~600 × 450 m at 1 km AGL with the v1 lens — re-pinned per camera spec); **40–50 % overlap** between adjacent chunks; optionally multi-scale across altitude bands | The unit FAISS retrieval works on. Decoupled from storage so any frame footprint always falls inside ≥1 chunk regardless of position. |
|
||
|
||
|
||
The FAISS IVF index is over **VPR chunk descriptors**, not storage-tile descriptors. Chunks are derived from the storage tile cache pre-flight (one batch DINOv2 forward per chunk); refreshed when tiles inside a chunk change beyond a threshold. Index size for a 400 km² operational area at ~600×450 m chunk size with 50 % overlap ≈ **6 000–8 000 entries**, well within FAISS-on-Jetson memory.
|
||
|
||
Frame descriptor pipeline (only on VPR invocation): **IMU-heading de-rotate frame → downsample to backbone input size → DINOv2 forward → VLAD/SALAD/BoQ aggregator → cosine retrieval against FAISS chunk index**.
|
||
|
||
#### Invocation policy (revised — M-17)
|
||
|
||
```
|
||
on each EKF cycle:
|
||
if steady_state(last_anchor_age < 2s, sigma_xy < 20m, vo_healthy):
|
||
candidates = top_K_chunks_by_predicted_position() # geometric prior, no DINOv2
|
||
else:
|
||
# Re-loc path — cold start, sharp turn, disconnected segment, sigma_xy > 50m, VO failed
|
||
candidates = vpr_top_K_chunks(frame_descriptor) # DINOv2 + FAISS
|
||
if not convincing_match(candidates): # M-18
|
||
candidates = expand_to_adjacent_chunks(candidates)
|
||
pose, covariance = matcher_pnp(frame, candidates) # Component 3
|
||
```
|
||
|
||
Telemetry exposes `vpr_invoked` per cycle so the FDR captures the steady-state-vs-reloc fraction over a flight.
|
||
|
||
#### Backbone bench-off candidates
|
||
|
||
|
||
| Solution | Tools | Advantages | Limitations | Performance | Fit |
|
||
| --------------------------------------- | -------------------------------- | --------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------- | -------------------------------------------------------------- | --------------------------------------------------------------------- |
|
||
| **AnyLoc** (DINOv2 + unsupervised VLAD) | dinov2 ViT-B/14, VLAD aggregator | Training-free; up to 4× R@1 over specialised methods on aerial cross-domain (F-C2) | Needs bench-off vs SALAD/BoQ on aerial cross-domain | DINOv2-base ~8 ms/inf at 224×224 on Orin Nano Super (S40, M-5) | **Bench-off candidate** — keep as fallback even if not picked primary |
|
||
| **DINOv2 SALAD** *(new)* | SALAD repo (CVPR 2024) | DINOv2-trained Sinkhorn-VLAD; R@1 = 75 % MSLS Challenge / 92.2 % MSLS Val / 76 % NordLand; in `aero-vloc` | Requires training data — but published checkpoints are usable zero-shot | Same backbone as AnyLoc → similar latency | **Bench-off primary candidate** (M-4) |
|
||
| **BoQ** *(new)* | Bag-of-Queries (CVPR 2024) | Beats NetVLAD/MixVPR/EigenPlaces/Patch-NetVLAD/TransVPR/R2Former on 14 benchmarks | Aerial-domain ranking TBD by bench-off | Lower compute than AnyLoc/SALAD when used with a CNN backbone | **Bench-off primary candidate** (M-4) |
|
||
| **MixVPR** | mixvpr trained on GSV-Cities | Lighter than AnyLoc; degraded-power fast-lane | Trained on street-view; weaker out-of-domain on aerial | Lower latency than ViT-class | **Fast-lane on degraded power** |
|
||
| **EigenPlaces / SelaVPR** | aero-vloc | Recent SOTA on some aerial | Mixed wins vs AnyLoc | — | Bench-off candidates |
|
||
|
||
|
||
**Bench-off scope expanded** (M-4): AnyLoc + SALAD + BoQ + MixVPR primary; EigenPlaces / SelaVPR secondary. **Each candidate is benched on the new chunk-based retrieval unit, not per-tile.**
|
||
|
||
#### Active-conflict scene change (destroyed buildings, cratering, dam flooding)
|
||
|
||
This is a frequent operational reality, not an edge case. Layered mitigations:
|
||
|
||
|
||
| Mitigation | What it does | Cost |
|
||
| ----------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ---------------------------------------------------------------------------------------- |
|
||
| **Multi-scale VPR chunks** *(new)* — z=17 / z=18-effective coarse chunks alongside z=20-derived fine chunks | Coarse-scale chunk descriptors describe road-network + field-boundary + waterway structure that survives building destruction. When the fine-scale top-K is unconvinced, the system falls back to coarse-scale top-K. | ~12 MB extra disk; ~3 min one-time DINOv2 forward over coarse chunks pre-flight |
|
||
| **OSM road-network overlay** as stable-feature anchor *(new)* | OSM road geometry persists even when buildings are destroyed. Extract from OpenStreetMap as a binary "road-mask" tile sidecar. The matcher applies a bonus inlier weighting on keypoints that fall on road edges (~1.3× confidence multiplier). GISNav (closest published reference architecture) does this. | One-time pre-flight OSM extract for the operational area (~minutes); ~5 % bigger sidecar |
|
||
| **Sector volatility classification drives K** *(new)* — bound to AC-NEW-6 `sector_class` | K=5 in stable sectors; K=20 in active-conflict sectors; K=50 in expanding-window fallback. Bigger candidate pool absorbs stale-tile false negatives. | Pure config; no compute or storage cost |
|
||
| **Onboard-tile rapid promotion in active sectors** *(new)* — refines M-9's 2-flight voting | In active sectors specifically, allow promotion to "trusted basemap" after 1 flight if σ_xy ≤ 3 m AND OSM-road-overlap ≥ 70 % (dual gate). Faster basemap refresh keeps up with active-sector change rate. Stable sectors keep the conservative N≥2 voting. | Branch in Service ingest voting layer; no onboard cost |
|
||
| **Negative cache** *(new)* | When the matcher rejects a tile pair (RANSAC inlier ratio < 0.3) repeatedly across multiple flights, mark that tile's `trust_level = stale_destroyed` and exclude from retrieval until refreshed by Service. | One extra metadata field; trivial at retrieval time |
|
||
|
||
|
||
Of these, **multi-scale VPR chunks + OSM road overlay** are the two with the biggest payoff for active-conflict scene change. Sector-driven K is essentially free. Negative cache is cheap insurance.
|
||
|
||
#### Stale-tile / cloud robustness
|
||
|
||
- **Stale tiles** in stable sectors (seasonal mismatch only): bench-off includes AerialExtreMatch (S49, structured-difficulty). 2chADCNN (S50) is the season-robustness ceiling reference. Production-side mitigation: top-K is **dynamically sized** by sector + σ_xy (see table above). Stale-tile false-negatives are absorbed by larger K + matcher-driven verification.
|
||
- **Cloudy stored tile**: deprioritised at retrieval time via the `glare/cloud` sidecar flag (Component 1).
|
||
- **Cloudy live frame**: not VPR-specific — the matcher and orthorectifier also fail. System falls back to VO/IMU dead-reckoning. **F-T16** (new test) synthesises cloud-occlusion injection on AerialVL frames to characterise the recovery profile (see Testing Strategy).
|
||
|
||
---
|
||
|
||
### Component 3: Cross-View Matching & PnP
|
||
|
||
> **⚠ Deep-research item.** Highest-leverage decision in the system. Mode B updates the candidate list.
|
||
|
||
**Bench-off candidates (revised vs Mode A):**
|
||
|
||
|
||
| Candidate | Status vs Mode A | Rationale |
|
||
| -------------------------------------- | ------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------- |
|
||
| **SuperPoint + LightGlue (TRT, FP16)** | **Lead candidate** (unchanged) | Well-trodden TRT path, ~286 FPS on RTX 3080 @ 320×240 baseline (F-B1) |
|
||
| **GIM-LightGlue** *(new — M-3)* | **Bench-off candidate, peer of SP+LG** | 8.4–18.1 % zero-shot improvement over LightGlue baseline (S48). Same TRT path. |
|
||
| **XFeat (sparse + semi-dense)** | **Bench-off candidates** (unchanged) | Embedded-class throughput; CPU-viable as failover (S08) |
|
||
| **MASt3R** | **DROPPED from primary list** (was stretch — M-2) | mast3r-runtime Jetson support "Planned"; ~3 s/pair on Orin Nano Super extrapolated. Research-track-only. |
|
||
| **GIM-RoMa / RoMa** | Bench-off candidate | Best Map-free / aerial cross-view in 2024 papers; needs distillation work |
|
||
| **GIM-DKM** | Bench-off candidate | Same as RoMa — bench-off only if SP+LG variants fall short |
|
||
| **2chADCNN** *(new — M-14)* | **Season-aware reference** | UAV↔satellite season-aware template-matching (S50). Either bench-off candidate or season-robustness ceiling reference. |
|
||
| **Classical (SIFT/ORB/AKAZE)** | Last-resort degraded mode | Cross-view domain gap kills these (F-A5) |
|
||
|
||
|
||
**Bench-off targets (revised):**
|
||
|
||
1. **AerialVL** — primary public benchmark (S03), 70 km of fixed-wing trajectories.
|
||
2. **UAV-VisLoc** — accuracy regression at 405–840 m (S01).
|
||
3. **AerialExtreMatch** *(new — M-14)* — 1.5 M synthetic pairs, 32 difficulty levels (overlap × scale × pitch). Direct grading of failure-mode axes.
|
||
4. **2chADCNN season set** — season-aware **ceiling reference number only** (M-21); not a candidate matcher.
|
||
5. **TartanAir V2** *(confirmed — M-13, Q4=A)* — early-stage synthetic baseline; sweeps seasons / lighting / pitches before the first internal fixed-wing flight lands.
|
||
6. **Internal Mavic flight footage** — closest to deployment domain.
|
||
7. **First internal fixed-wing flight** (FT-3) — lands before AC-1.3 lock per M-15.
|
||
|
||
**Score on:** AC-1.1 / AC-1.2 / AC-2.2 / p95 latency on Orin Nano Super 25 W / sustained 30-min thermal stability / peak GPU memory / **plus seasonal-robustness score from the 2chADCNN-axis tests**.
|
||
|
||
**PnP & projection:** Unchanged from Mode A, except output schema adds `parent_pose_sigma_xy` for downstream Component-1b dedup gate.
|
||
|
||
**Input downsampling:** Empirical pin during research pass. Latency budget is more comfortable than Mode A assumed (M-5 / S40), so 1024×768 is a low-risk starting point for SP+LG / GIM-LG; 1024×768 semi-dense or 640×480 sparse for XFeat.
|
||
|
||
---
|
||
|
||
### Component 4: Visual Odometry
|
||
|
||
Unchanged from Mode A (custom 2-frame VO via SuperPoint+LightGlue / GIM-LightGlue homography). New risk **R8** (M-15) added: AC-1.3 drift budget needs validation against AerialVL's fixed-wing trajectories before lock — *not* against Mavic-class footage.
|
||
|
||
---
|
||
|
||
### Component 5: IMU + Visual EKF Fusion
|
||
|
||
**Working choice (revised from Mode A):** Onboard loosely-coupled EKF in our process emits **two parallel MAVLink streams**:
|
||
|
||
1. **GPS_INPUT** (primary, GPS-substitute framing per AC-4.3) with `h_acc`/`v_acc` derived from EKF covariance.
|
||
2. **ODOMETRY** (auxiliary, when full 6-DoF covariance is available and quality > VISO_QUAL_MIN) with the full 21-element pos+att covariance (M-1).
|
||
|
||
ArduPilot is configured with `EK3_SRC1_`* set to GPS-with-fallback-to-ExternalNav so GPS_INPUT remains the failover path. Mode/source labels (`satellite_anchored / vo_extrapolated / dead_reckoned`) are emitted on both channels.
|
||
|
||
**Key tuning:** the EKF's Mahalanobis gate and process-noise covariances are calibrated against AC-NEW-4 budget through Monte Carlo (which now also includes M-9 cache-poisoning injection — see "Testing Strategy").
|
||
|
||
---
|
||
|
||
### Component 6: MAVLink Integration & Source Promotion
|
||
|
||
**Working choice (revised from Mode A):**
|
||
|
||
- **MAVSDK for telemetry** + **pymavlink for `GPS_INPUT` and `ODOMETRY` lines**.
|
||
- **No mavlink-router daemon** (M-6). Instead: distinct system-IDs for MAVSDK (sysid=10) and pymavlink (sysid=11), sharing the serial port via ArduPilot's native MAVLink routing (S35-class). Single endpoint configuration, no third-party C++ daemon, no #436-class CVE risk.
|
||
- **MAVLink2 signing mandatory** (M-7) on every companion↔FC link. Per-airframe key in FC FRAM; provisioning runbook is part of the deploy procedure.
|
||
|
||
**Source-promotion logic (revised per M-11):** unchanged behaviourally, but **F-T9 SITL test scope expanded** to include source-switching combinations across both GPS_INPUT-primary and ODOMETRY-primary modes. Pin ArduPilot to the version containing PR #30080.
|
||
|
||
**Spoofing-promotion latency budget:** <3 s (AC-NEW-2) — unchanged.
|
||
|
||
---
|
||
|
||
### Component 7: Failsafe, Health & Re-Localization
|
||
|
||
Unchanged from Mode A.
|
||
|
||
---
|
||
|
||
### Component 8: Object Localization (AI Camera)
|
||
|
||
Unchanged from Mode A (trig + airframe-attitude fusion via `ATTITUDE` MAVLink stream).
|
||
|
||
---
|
||
|
||
### Component 9: Software Platform & Process Topology
|
||
|
||
**Working choice (revised rationale per M-10):**
|
||
|
||
- **Single Python process (asyncio) on CPython 3.11 or 3.12** (well-supported by JetPack / numba / TRT bindings).
|
||
- **TRT inference workers as subprocesses**, tensor handoff via CUDA IPC (Jetson unified-memory aware: zero-copy possible since CPU and GPU share the LPDDR5 pool).
|
||
- **numba JIT** for EKF math hot paths.
|
||
- Configuration via YAML; logging via structured JSON to FDR.
|
||
- **Free-threaded Python (3.13+) is v1.1 territory.** Reason: experimental, single-threaded perf hit, GIL re-enables on import of any non-FT-aware C extension (S55). Revisit when NumPy/SciPy/numba/TRT bindings are FT-aware.
|
||
|
||
---
|
||
|
||
### Component 10: Flight Data Recorder
|
||
|
||
Unchanged from Mode A, except the per-sector `terrain_class` and `trust_level` flags are recorded alongside the position-estimate stream so post-mission analysis can filter on them.
|
||
|
||
---
|
||
|
||
### Component 11: Confidence Score (cross-cutting)
|
||
|
||
**Computed** from: RANSAC inlier ratio, reprojection error variance, top-K retrieval similarity gap, EKF covariance, **plus** parent-pose σ_xy gate result (M-9 hard gate).
|
||
|
||
**Emitted on:**
|
||
|
||
1. GPS_INPUT (`h_acc`).
|
||
2. ODOMETRY (full 21-element covariance + `quality` field 0–100).
|
||
3. NAMED_VALUE_FLOAT "CONF_M" on the GCS link.
|
||
4. Per-tile sidecar (`parent_pose_sigma_xy`) for Component-1b dedup gate.
|
||
|
||
---
|
||
|
||
## Testing Strategy
|
||
|
||
### Functional / Integration
|
||
|
||
- **F-T1** Tile cache load/lookup (unchanged).
|
||
- **F-T1b** *(new — M-15)* AC-1.3 drift regression against **AerialVL's fixed-wing trajectories** (70 km of real flight). Pass = drift ≤100 m VO-only / ≤50 m IMU-fused between satellite anchors at 95th percentile.
|
||
- **F-T2** Tile generation + dedup *(extended — M-9)*: replay a recorded flight; assert (a) for any ground sector covered ≥2× by the nav cam, exactly **one** tile is written; (b) the chosen tile has `parent_pose_sigma_xy` ≤ the hard gate; (c) **service tiles are never overwritten when within freshness budget**, regardless of our quality score.
|
||
- **F-T3** Tile uploader → candidate pool *(extended — M-9)*: post-flight, the diff against the Service candidate pool is correct; freshness + trust_level metadata round-trips; 2-flight voting promotes candidates to basemap only after 2nd-flight confirmation.
|
||
- **F-T4** End-to-end against **AerialVL** (S03).
|
||
- **F-T5** End-to-end against **UAV-VisLoc** (S01).
|
||
- **F-T5b** *(new — M-14)* End-to-end against **AerialExtreMatch** (S49) — structured-difficulty regression. For each of 32 difficulty levels, log AC-1.1 / AC-1.2 pass/fail.
|
||
- **F-T5c** *(new — M-14)* Season-robustness regression against **2chADCNN season set** (S50) — assert AC-1.1 holds across summer↔winter pairs.
|
||
- **F-T6** End-to-end against **internal Mavic flight footage** — deployment-domain proxy.
|
||
- **F-T7** Sharp-turn handling (unchanged).
|
||
- **F-T8** Disconnected-segment re-localization (unchanged).
|
||
- **F-T9** ArduPilot SITL: full MAVLink loop *(extended — M-11)*. Test matrix:
|
||
- GPS_INPUT-only mode (Mode A baseline).
|
||
- GPS_INPUT + ODOMETRY hybrid mode.
|
||
- Source switching: jam-onset → our channel; spoofed-real-GPS recovery → operator-confirmed source-restore.
|
||
- `EK3_SRC1_`* parameter combinations across both modes.
|
||
- **MAVLink2 signing on**: assert injection refused on signing failure; assert acceptance on valid signing.
|
||
- **F-T10** Operator re-loc workflow via QGC `STATUSTEXT`.
|
||
- **F-T11** Cold-start TTFF <30 s (AC-NEW-1).
|
||
- **F-T12** Spoofing-promotion <3 s (AC-NEW-2).
|
||
- **F-T13** Object localization with airframe-attitude fusion (unchanged).
|
||
- **F-T14** *(new — M-12)* Per-sector DEM classification: load SRTM-30 m for the operational area; assert sector classes (`flat`, `moderate`, `rugged`) line up with ground-truth DEM amplitudes; assert ortho-tile generation is skipped in `rugged` sectors.
|
||
- **F-T15** *(new — M-16/17/18 cluster)* **VPR retrieval-unit bench**: build the chunk-based FAISS index over a 400 km² synthetic operational area; assert that for any ground point, ≥1 chunk fully contains the expected frame footprint (overlap correctness). Bench top-K recall at K = {3, 5, 10, 50} for steady-state, re-loc, and expanding-window modes against AerialVL + AerialExtreMatch + 2chADCNN season set.
|
||
- **F-T16** *(new — concern #3 cloud robustness)* Synthetic cloud-occlusion injection: inject 0–60 % cloud cover on AerialVL frames (and on cached basemap tiles independently); assert the system gracefully degrades (top-K expansion → matcher failure → VO/IMU fallback) rather than emitting a confident bad fix.
|
||
- **F-T17** *(new — M-17 invocation policy)* Mission replay assertion: in a typical mission replay (steady cruise + 1 sharp turn + 1 simulated reboot), measure the % of cycles VPR is invoked. Pass criterion: ≥80 % of steady-state cycles use the geometric-prior path; 100 % of re-loc-trigger cycles invoke VPR.
|
||
|
||
### Non-Functional
|
||
|
||
- **NF-T1** Latency p95 <400 ms on Orin Nano Super 25 W (AC-4.1).
|
||
- **NF-T2** Memory <8 GB shared (AC-4.2).
|
||
- **NF-T3** Thermal: 8 h sustained 25 W (AC-NEW-5).
|
||
- **NF-T4** *(extended — M-9)* False-position safety budget (AC-NEW-4) — Monte Carlo over AerialVL + Mavic + AerialExtreMatch with **synthetic over-confidence injection**: artificially deflate EKF covariance by 1.5×–3× and verify the EKF's Mahalanobis gate still rejects the bad fix and the cache-poisoning hazard does not trigger.
|
||
- **NF-T4b** *(new — M-9)* **AC-NEW-7 cache-poisoning safety budget** validation — Monte Carlo over multi-flight replays: assert P(onboard tile mis-aligned > 30 m) per flight < 1 %; P(>100 m) per flight < 0.1 %.
|
||
- **NF-T5** Storage: 64 GB FDR cap with rollover.
|
||
- **NF-T6** Imagery freshness gate (AC-NEW-6).
|
||
|
||
### Security
|
||
|
||
- **S-T1** GPS_INPUT + ODOMETRY not accepted from any non-whitelisted MAVLink source-system-id.
|
||
- **S-T2** Tile cache encrypted at rest.
|
||
- **S-T3** *(promoted to v1-mandatory — M-7)* MAVLink2 signing between companion and FC. Verified at boot. Refuse to inject GPS_INPUT/ODOMETRY if FC reports signing-off on our link.
|
||
- **S-T4** *(new — M-6)* No `mavlink-router` binary on the deployed companion image. The CI image-build step verifies absence.
|
||
- **S-T5** *(new — M-6)* MAVLink endpoint multiplexing via distinct system-IDs is exercised in CI integration tests.
|
||
|
||
### Field
|
||
|
||
- **FT-1** Flight-data-recorder review of ≥5 real-world test flights at progressive altitudes (200 m → 1 km AGL).
|
||
- **FT-2** Single 8-hour sortie endurance / thermal soak.
|
||
- **FT-3** *(new — M-15)* **First internal fixed-wing flight at 1 km AGL** before AC-1.3 lock. Synced IMU + GPS truth + nav-cam stream collected; replayed through the pipeline.
|
||
|
||
---
|
||
|
||
## Key Risks & Open Items (carried into Plan step)
|
||
|
||
|
||
| ID | Risk | Severity | Mitigation |
|
||
| --------------------------- | --------------------------------------------------------------------------------- | ---------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
||
| R1 | Imagery licensing lead time (Service-side concern) | Med (was High; now upstream) | Suite Service procurement; not on this build's critical path |
|
||
| R2 | Latency budget on Orin Nano Super at 1024×768 | **Med (was High — M-5)** | DINOv2-base measured at ~8 ms/inf at 224×224 (S40); empirical bench-off in week 1 of impl |
|
||
| R3 | Cross-view accuracy at 1 km AGL with Ukrainian seasonal change | Med | 50 %@20m hard floor; bench-off now includes SALAD/BoQ/GIM-LightGlue/2chADCNN before lock (M-3, M-4, M-14) |
|
||
| R4 | MAVSDK + pymavlink coexistence (resolved: distinct system-IDs, no router, M-6) | **Resolved** | — |
|
||
| R5 | Thermal at 25 W for 8 h | Med | Cooling validation in NF-T3 |
|
||
| R6 | AC-7.1 in turning flight (gimbal-only pose) | Low (scoped to level flight in v1) | Add airframe-attitude fusion in v1.1 |
|
||
| R7 | Public dataset gap (V&V) | Med | AerialVL primary; AerialExtreMatch + 2chADCNN added (M-14); internal Mavic for deployment proxy; first fixed-wing flight scheduled before AC-1.3 lock (M-15) |
|
||
| **R8** *(new — M-15)* | **Fixed-wing VO drift under AC-1.3 budget unconfirmed** | Med | F-T1b regression on AerialVL's fixed-wing trajectories; FT-3 first internal fixed-wing flight |
|
||
| **R9** *(new — M-9)* | **Cross-flight cache poisoning via onboard tile overwrite of stale service tile** | High (safety) | Service-tile immutability inside freshness budget; 2-flight voting at Service ingest; parent-pose σ_xy hard gate; AC-NEW-7 numeric budget |
|
||
| **R10** *(new — M-7 / M-6)* | **Companion↔FC link is a flight-critical attack surface** | High (security) | MAVLink2 signing v1-mandatory; mavlink-router replaced by native MAVLink routing with distinct system-IDs |
|
||
| **R11** *(new — M-11)* | **ArduPilot external-nav source-switching has known production gotchas** | Med | F-T9 SITL test matrix; pin ArduPilot version containing PR #30080 |
|
||
| **R12** *(new — M-12)* | **Eastern-Ukraine relief amplitude breaks flat-Earth assumption near frame edge** | Med | Pre-flight SRTM-30 m DEM lookup; per-sector terrain class; runtime self-classifier |
|
||
|
||
|
||
## Proposed AC additions
|
||
|
||
**AC-NEW-7 — Cache-poisoning safety budget** *(new — M-9)*
|
||
|
||
- P(onboard tile geo-misaligned > **30 m**) per flight **<1 %**.
|
||
- P(onboard tile geo-misaligned > **100 m**) per flight **<0.1 %**.
|
||
|
||
**Why it matters.** Cross-flight error compounding. Validated by **NF-T4b**.
|
||
|
||
**Implementation drivers.** Service-tile immutability within freshness budget; 2-flight voting at Service ingest; parent-pose σ_xy hard gate (≤5 m for hard write, ≤3 m for `trust_level = candidate`).
|
||
|
||
---
|
||
|
||
## Open Research (deferred to dedicated research passes before Plan)
|
||
|
||
|
||
| Topic | Why now | Output | Owner |
|
||
| -------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------ | ----------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------- |
|
||
| **Cross-view matcher bench-off** (Component 3) — *expanded list per M-2/M-3/M-14* | Highest-leverage decision; expanded shortlist requires explicit empirical comparison | Selected matcher + chosen input resolution + measured latency / accuracy / memory + season-robustness score | Research skill, follow-up Mode A pass scoped to "matcher selection" |
|
||
| **Input-resolution sweep** | Coupled with the matcher bench (latency budget more comfortable than Mode A assumed — M-5 → bigger sweep range possible) | Resolution per matcher candidate; sensitivity curves for AC-1.1 / AC-1.2 vs resolution | Same pass |
|
||
| **VPR backbone bench-off** (Component 2) — *expanded list per M-4* (AnyLoc + SALAD + BoQ + MixVPR) | Cheaper than the matcher decision but feeds it | Selected VPR backbone + measured Recall@K on AerialVL + AerialExtreMatch + Mavic | Same pass |
|
||
| **Tile-generator quality scoring** (Component 1b) | Need empirically-grounded thresholds for σ_xy (M-9), sharpness, glare | Calibrated thresholds | Implementation phase |
|
||
| **Internal Mavic-flight V&V dataset** | Closest proxy to deployment domain | Curated, ground-truth-labelled clips | Operations / data team |
|
||
| **First internal fixed-wing flight** *(new priority — M-15)* | AC-1.3 drift budget unconfirmed | Recorded sortie with synced IMU + GPS truth + nav-cam stream | Field-test plan; **before AC lock**, not stretch |
|
||
| **Encryption-at-rest key management** | Tile cache + FDR are operationally sensitive | Threat-modelled design | Phase 4 security analysis |
|
||
| ~~*(Open question — M-13)*~~ TartanAir V2 as early-stage synthetic baseline | **Confirmed yes (Q4 = A, 2026-04-26)** | Folded into bench-off plan | — |
|
||
|
||
|
||
---
|
||
|
||
## References
|
||
|
||
All citations are by ID from `_docs/00_research/01_source_registry.md`. Mode B sources: **S40–S57**.
|
||
|
||
- **Latency / hardware**: S40 (Jetson AI Lab L1).
|
||
- **MAVLink integration**: S41–S44 (ArduPilot dev docs L1, ODOMETRY PR #19563, External-nav fix PR #30080, MAVLink2 signing).
|
||
- **Security**: S44 (signing), S45 (mavlink-router CVE class).
|
||
- **VPR SOTA 2024**: S46 (BoQ), S47 (SALAD).
|
||
- **Matcher SOTA 2024**: S48 (GIM), S57 (MASt3R Jetson status).
|
||
- **Datasets**: S49 (AerialExtreMatch), S50 (2chADCNN), S51 (TartanAir V2), S52 (AFIT fixed-wing VO), S53 (high-altitude VIO).
|
||
- **Tile cache**: S54 (MBTiles WAL recipe).
|
||
- **Python topology**: S55 (free-threaded Python 3.13).
|
||
- **Terrain**: S56 (eastern-Ukraine relief).
|
||
|
||
---
|
||
|
||
## Related Artifacts
|
||
|
||
- Mode A draft (superseded by this draft): `_docs/01_solution/solution_draft01.md`.
|
||
- Mode B decomposition: `_docs/00_research/03_mode_b_decomposition.md`.
|
||
- Mode B reasoning chain: `_docs/00_research/04_reasoning_chain_mode_b.md`.
|
||
- Mode B validation log: `_docs/00_research/05_validation_log_mode_b.md`.
|
||
- AC & Restrictions assessment (Phase 1): `_docs/00_research/00_ac_assessment.md`.
|
||
- Source registry: `_docs/00_research/01_source_registry.md` (S01–S57).
|
||
- Fact cards: `_docs/00_research/02_fact_cards.md` (Phase 1 + Mode B M-1..M-15).
|
||
- Tech stack consolidation: `_docs/01_solution/tech_stack.md` (deferred — Phase 3 optional).
|
||
- Security analysis: `_docs/01_solution/security_analysis.md` (deferred — Phase 4 optional, but **promoted to recommended-before-Plan-lock** because of M-6/M-7 promotion).
|
||
|