mirror of
https://github.com/azaion/gps-denied-onboard.git
synced 2026-06-21 08:51:12 +00:00
docs: add Phase 1 ADRs and update PROJECT.md with completed decisions
ADR 0002: hexagonal/ports-and-adapters architecture — components/ layout, protocol.py per component, composition root, core/ for concentrated math. ADR 0003: @dataclass(slots=True, frozen=True) on hot path; Pydantic retained only at REST/config/DB boundaries. Pose/GPSPoint migration deferred to Phase 2. ADR 0004: Stage 2 as independent iteration — own phases 1-6, own requirements, stage1 code treated as MVP starting capital. PROJECT.md: Stage 2 Key Decisions updated from Pending → Accepted with Phase 1 implementation notes, deferred work list, and final architecture summary. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
+138
-61
@@ -1,98 +1,175 @@
|
||||
# GPS-Denied Onboard Navigation System
|
||||
# GPS-Denied Onboard Navigation System — Stage 2
|
||||
|
||||
## What This Is
|
||||
|
||||
Real-time GPS-independent position estimation system for a fixed-wing UAV operating in GPS-denied/spoofed environments (flat terrain, Ukraine). Runs onboard a Jetson Orin Nano Super (8GB shared, 67 TOPS). Fuses visual odometry (cuVSLAM), satellite image matching (TensorRT FP16), and IMU via an ESKF to output MAVLink GPS_INPUT to an ArduPilot flight controller at 5-10Hz, while also streaming position and confidence over SSE to a ground station.
|
||||
|
||||
## Stage 2 Iteration
|
||||
|
||||
**Stage 2 is a self-contained iteration of the project.** It is NOT a continuation of Stage 1's phase numbering — it has its own roadmap (Phases 1–6), its own requirements list, and its own success criteria. Each stage is conceptually a new pass at the system: same problem, same end goal, fresh decisions about HOW.
|
||||
|
||||
**Stage 2 starting capital:**
|
||||
|
||||
- **From stage1 (own work):** The full v1 pipeline as MVP — ESKF (15-state), cuVSLAM/ORB VO, satellite matching + GPR, MAVLink GPS_INPUT, pipeline orchestration, SITL harness, accuracy benchmarks, 195 passing tests. Treated as **MVP, not production** — refactoring is allowed and expected.
|
||||
- **From try02 (parallel team):** Concept-level ideas only — Safety Anchor State Machine, Geometry-Gated Anchor Verifier, Flight Data Recorder, Conditional Multi-Scale VPR, dual-channel MAVLink design, formal Acceptance Criteria document with numeric thresholds, structured test taxonomy.
|
||||
- **From real-flight data:** Azaion 10.05.2026 dataset (tlog + 6min video + 9.5Hz GPS ground truth) as integration fixture.
|
||||
|
||||
**Stage 2 is free to:**
|
||||
|
||||
- Reorganize the codebase (hexagonal layout) — no production lock-in
|
||||
- Replace, swap, or rebuild components — only AC-driven test outcomes are sacred
|
||||
- Change the architecture wholesale if a better path emerges mid-stage
|
||||
- Diverge from try02's choices where the evidence supports it (e.g., reject BASALT in favor of cuVSLAM, reject Pydantic on hot path)
|
||||
|
||||
**Stage 2 archive:** `_planning/archive/v1.0/` preserves stage1's PROJECT.md, REQUIREMENTS.md, ROADMAP.md, and Phase 1 artifacts as historical record.
|
||||
|
||||
## Core Value
|
||||
|
||||
The flight controller must receive valid MAVLink GPS_INPUT at 5-10Hz with position accuracy ≤50m for 80% of frames — without this, the UAV cannot navigate in GPS-denied airspace.
|
||||
|
||||
## Requirements
|
||||
## Stage 2 Goal
|
||||
|
||||
### Validated
|
||||
Refactor the inherited stage1 MVP into a hexagonal/ports-and-adapters architecture with explicit DI composition root, integrate selected concept-level ideas from `try02`, formalize acceptance criteria with testable numerics, and add a real-flight integration fixture (Azaion 10.05.2026).
|
||||
|
||||
- ✓ FastAPI service scaffold with SSE streaming — existing
|
||||
- ✓ FlightProcessor orchestrator with NORMAL/LOST/RECOVERY state machine — existing
|
||||
- ✓ CoordinateTransformer (GPS↔ENU, pixel→camera→body→NED→WGS84) — existing
|
||||
- ✓ SatelliteDataManager (tile fetch, diskcache, GeoHash lookup) — existing
|
||||
- ✓ ImageInputPipeline (frame queue, batch validation, storage) — existing
|
||||
- ✓ SQLAlchemy async DB layer (flights, waypoints, frames, results) — existing
|
||||
- ✓ Pydantic schema contracts for all inter-component data — existing
|
||||
- ✓ ABC interfaces for all core components (VO, GPR, metric, graph) — existing
|
||||
## Stage 2 Target Features
|
||||
|
||||
### Active
|
||||
**Architecture:**
|
||||
- Hexagonal layout — `src/gps_denied/components/{vio, satellite_matcher, gpr, anchor_verifier, safety_state, flight_recorder, mavlink_io, coordinate_transforms}/` with `protocol.py` + concrete impls per component
|
||||
- Hot-path types as `@dataclass(slots=True, frozen=True)` for `FrameState`, `IMUSample`, `PositionEstimate`; Pydantic kept only at REST/config/DB boundaries
|
||||
- Composition root `pipeline/composition.py` with explicit DI for env-specific wiring (jetson/x86_dev/ci/sitl)
|
||||
- Per-environment config — `config/{jetson,x86_dev,ci,sitl}.yaml` driven by pydantic-settings
|
||||
- `core/` retained for concentrated math (ESKF, factor graph, RANSAC) — single-file pure functions
|
||||
|
||||
- [ ] ESKF implementation (15-state error-state Kalman filter: IMU prediction + VO update + satellite update + covariance propagation)
|
||||
- [ ] MAVLink GPS_INPUT output (pymavlink, UART/UDP, 5-10Hz loop, ESKF state→GPS_INPUT field mapping)
|
||||
- [ ] Real VO implementation (cuVSLAM on Jetson / OpenCV ORB stub on dev for CI)
|
||||
- [ ] Real TensorRT inference (SuperPoint+LightGlue for VO, XFeat for satellite matching — FP16 on Jetson)
|
||||
- [ ] Satellite feature matching pipeline (tile selection by ESKF uncertainty, RANSAC homography, WGS84 extraction)
|
||||
- [ ] GlobalPlaceRecognition implementation (AnyLoc/DINOv2 candidate retrieval, FAISS index, tile scoring)
|
||||
- [ ] FactorGraph implementation (pose graph with VO edges + satellite anchor nodes, optimization loop)
|
||||
- [ ] FailureRecoveryCoordinator (tracking loss detection, re-init protocol, operator re-localization hint)
|
||||
- [ ] End-to-end pipeline wiring (processor.process_frame → VO → ESKF → satellite → GPS_INPUT)
|
||||
- [ ] Docker SITL test harness (ArduPilot SITL, camera replay, tile server mock, CI integration)
|
||||
- [ ] Confidence scoring and GPS_INPUT fix_type mapping (HIGH/MEDIUM/LOW → fix_type 3/2/0)
|
||||
- [ ] Object GPS localization endpoint (POST /objects/locate with gimbal angle projection)
|
||||
**try02 concept integration:**
|
||||
- Acceptance Criteria document — formal AC-1.x…AC-NEW-x with numeric thresholds, validation methods, test linkage
|
||||
- Safety Anchor State Machine — separate layer over ESKF owning `source_label` (`satellite_anchored`/`vo_extrapolated`/`dead_reckoned`), monotonic covariance growth, anchor age, tile write eligibility
|
||||
- Geometry-gated Anchor Verifier — formal accept/reject gates (inliers, MRE, reprojection error) before anchor enters ESKF
|
||||
- Flight Data Recorder (FDR) — append-only event log with bounded segment storage and health states
|
||||
- Conditional VPR invocation — DINOv2 forward only on re-loc triggers; steady-state geometric prior
|
||||
- Multi-scale VPR chunks — 600-800m ground-footprint chunks at 40-50% overlap, decoupled from storage tiles, fine (z=20) + coarse (z=17) scales
|
||||
- Source label + anchor_age_ms emitted in every GPS_INPUT estimate
|
||||
- Visual blackout handling — switch to `dead_reckoned` ≤400ms, monotonic covariance growth, `VISUAL_BLACKOUT_IMU_ONLY` STATUSTEXT @ 1-2Hz
|
||||
- Spoofing-promotion latency monitor — promote own estimate to FC primary within <3s of detected real-GPS health drop
|
||||
- Test taxonomy — `tests/{unit,integration,blackbox,sitl,e2e}/`
|
||||
- Dual-channel MAVLink design — `GPS_INPUT` primary (v1 only), `ODOMETRY` auxiliary scaffolded behind feature flag for v1.1
|
||||
- Structured JSON logging with `correlation_id` (frame_id) per-frame
|
||||
- CLI tool `gps_denied replay --tlog ... --video ...`
|
||||
- Real-flight integration fixture — Azaion 10.05.2026 as `tests/integration/azaion_flight/`
|
||||
|
||||
### Out of Scope
|
||||
## Stage 2 Explicit Non-Goals
|
||||
|
||||
- TensorRT engine building tooling — engines are pre-built offline, system only loads them
|
||||
- Google Maps tile download tooling — tiles pre-cached before flight, not streamed live
|
||||
- Full ArduPilot integration testing on hardware — Jetson hardware validation is post-v1
|
||||
- Mobile/web ground station UI — SSE stream is consumed by external systems
|
||||
- BASALT VIO backend — cuVSLAM remains primary (aarch64) with ORB-SLAM3 as CI baseline
|
||||
- Pydantic on the per-frame hot path — dataclasses replace it
|
||||
- Mandatory PostgreSQL — SQLite remains the embedded default
|
||||
- Microservice processes / IPC — single-process architecture preserved
|
||||
- Folder-per-component split for `core/` math files — ESKF/factor graph stay concentrated
|
||||
- Mid-flight tile generation + write-back to Suite (AC-8.4) — deferred to Stage 3
|
||||
- Production hardware validation on Jetson — deferred to Stage 3
|
||||
|
||||
## Future Stages (parking lot)
|
||||
|
||||
- **Stage 3 candidates:** Jetson hardware validation, mid-flight tile generation + Suite write-back, ODOMETRY channel enabled, AC-NEW-1 cold-boot benchmark, BASALT evaluation if cuVSLAM blockers emerge
|
||||
|
||||
## Out of Scope (across all stages, unless re-opened)
|
||||
|
||||
- TensorRT engine building tooling — engines are pre-built offline
|
||||
- Google Maps tile download tooling — tiles pre-cached before flight
|
||||
- Mobile/web ground station UI — SSE consumed by external systems
|
||||
- Multi-UAV coordination — single UAV instance only
|
||||
|
||||
## Context
|
||||
|
||||
**Hardware target:** Jetson Orin Nano Super (8GB LPDDR5 shared, JetPack 6.2.2, CUDA 12.6, TRT 10.3.0). All development happens on x86 Linux; cuVSLAM and TRT are Jetson-only — dev machine uses OpenCV ORB stub and MockInferenceEngine.
|
||||
**Hardware target:** Jetson Orin Nano Super (8GB LPDDR5 shared, JetPack 6.2.2, CUDA 12.6, TRT 10.3.0). Development on x86 Linux; cuVSLAM and TRT are Jetson-only — dev/CI uses OpenCV ORB stub and MockInferenceEngine.
|
||||
|
||||
**Camera:** ADTI 20L V1 (5456×3632, APS-C, 16mm lens, nadir fixed, 0.7fps). AI detection camera: Viewpro A40 Pro (separate).
|
||||
**Camera (target):** ADTI 20L V1 (5456×3632, APS-C, 16mm lens, nadir fixed, 0.7fps). AI detection camera: Viewpro A40 Pro (separate).
|
||||
|
||||
**Flight controller:** ArduPilot via MAVLink UART. System sends GPS_INPUT; receives IMU (200Hz) and GLOBAL_POSITION_INT (1Hz) from FC.
|
||||
**Camera (Azaion fixture):** Multirotor gimbal EO+IR split-screen with HUD overlay, 1280×720 @ 30fps. Used for integration testing only — does not represent target deployment camera.
|
||||
|
||||
**Key latency budget:** <400ms end-to-end per frame (camera @ 0.7fps = 1430ms window).
|
||||
**Flight controller:** ArduPilot via MAVLink UART. System sends GPS_INPUT; receives IMU (200Hz target / 9.7Hz in Azaion fixture) and GLOBAL_POSITION_INT (1Hz) from FC.
|
||||
|
||||
**Existing scaffold:** ~2800 lines of Python code exist as a well-structured scaffold. All modules are present with ABC interfaces and schemas, but critical algorithmic kernels (ESKF, real VO, TRT inference, MAVLink) are missing or mocked.
|
||||
**Key latency budget:** <400ms end-to-end per frame.
|
||||
|
||||
**Test data:** 60 UAV frames (AD000001-AD000060.jpg), coordinates.csv with ground-truth GPS, expected_results/position_accuracy.csv. 43 documented test scenarios across 7 categories.
|
||||
**Stage 1 inheritance:** ~7,800 lines of working Python code with 195 passing tests. All algorithmic kernels (ESKF, VO, GPR, MAVLink, factor graph) implemented. Stage 2 starts from this codebase on branch `stage2` (HEAD = stage1).
|
||||
|
||||
**Reference branch:** `try02` is checked out as a worktree at `../gps-denied-onboard-try02/` for concept harvesting. We do NOT merge from try02 — we read it for ideas and re-implement what fits.
|
||||
|
||||
## Constraints
|
||||
|
||||
- **Performance**: <400ms/frame end-to-end, <8GB RAM+VRAM — non-negotiable for real-time flight
|
||||
- **Hardware**: cuVSLAM v15.0.0 (aarch64-only wheel) — stub interface required for CI
|
||||
- **Platform**: JetPack 6.2.2, Python 3.10+, TensorRT 10.3.0, CUDA 12.6
|
||||
- **Navigation accuracy**: 80% frames ≤50m, 60% frames ≤20m, max drift 100m between satellite corrections
|
||||
- **Resilience**: Handle sharp turns (disconnected VO segments), 3+ consecutive satellite match failures
|
||||
- **Performance:** <400ms/frame end-to-end p95, <8GB RAM+VRAM — non-negotiable
|
||||
- **Hardware:** cuVSLAM v15.0.0 (aarch64-only wheel) — Protocol with stub on x86
|
||||
- **Platform:** JetPack 6.2.2, Python 3.10+, TensorRT 10.3.0, CUDA 12.6
|
||||
- **Navigation accuracy:** 80% frames ≤50m, 60% frames ≤20m, max drift 100m between satellite corrections
|
||||
- **Resilience:** Handle sharp turns (disconnected VO segments), 3+ consecutive satellite match failures, visual blackout, GPS spoofing promotion <3s
|
||||
- **Regression floor:** All 195 stage1 passing tests must continue to pass after refactor
|
||||
|
||||
## Key Decisions
|
||||
## Stage 2 Key Decisions
|
||||
|
||||
| Decision | Rationale | Outcome |
|
||||
|----------|-----------|---------|
|
||||
| ESKF over EKF/UKF | 15-state error-state formulation avoids quaternion singularities, standard for INS | — Pending |
|
||||
| XFeat over LiteSAM for satellite matching | LiteSAM may exceed 400ms budget on Jetson; XFeat is faster | — Pending (benchmark required) |
|
||||
| OpenCV ORB stub for dev/CI | cuVSLAM is aarch64-only; CI must run on x86 | — Pending |
|
||||
| AnyLoc/DINOv2 for GPR | Validated on UAV-VisLoc benchmark (17.86m RMSE) | — Pending |
|
||||
| diskcache + GeoHash for tiles | O(1) tile lookup, no DB overhead, LRU eviction | ✓ Good |
|
||||
| AsyncSQLAlchemy + aiosqlite | Non-blocking DB for async FastAPI service | ✓ Good |
|
||||
| Hexagonal layout with `components/` folders | Clear ownership per swappable backend, native bridges colocate with adapter | ✓ Phase 1 |
|
||||
| `@dataclass(slots=True, frozen=True)` on hot path, Pydantic at boundaries only | Avoid try02's per-frame Pydantic latency cost; validate where it catches bugs (REST input, config) | ✓ Phase 1 (hot_types/ scaffolded; full migration Phase 2) |
|
||||
| Explicit DI composition root | One file wires environment-specific implementations; tests pass mock dependencies | ✓ Phase 1 (`pipeline/composition.py:build_pipeline`) |
|
||||
| Adopt try02 concept ideas, reject try02 layout details | Take Safety Anchor / Anchor Verifier / FDR / Conditional VPR; reject Pydantic-on-hot-path, BASALT | ✓ Adopted — Phases 3–5 |
|
||||
| Take try02 acceptance criteria with numeric thresholds | Their AC-1.x…AC-NEW-x is more rigorous than stage1's drafts; bind every AC to ≥1 test | ✓ Adopted — Phase 2 |
|
||||
| Test taxonomy `unit/integration/blackbox/sitl/e2e` | Clarifies CI-on-push vs PR vs nightly vs hardware-only test runs | ✓ Phase 2 |
|
||||
| Stage as iteration, not phase continuation | Each stage = own roadmap, own phase numbering, own success criteria | ✓ Adopted |
|
||||
|
||||
## Phase 1 Outcome (2026-05-11, completed)
|
||||
|
||||
**ARCH-01..07 all satisfied.** 216 tests pass (baseline 195+21 new = 216), 0 failures, accuracy benchmarks unchanged.
|
||||
|
||||
### What was built
|
||||
|
||||
**Components scaffold** (`src/gps_denied/components/`):
|
||||
- `vio/` — `protocol.py` + `orbslam_backend.py` + `cuvslam_backend.py` + `factory.py`; `core/vo.py` is a shim
|
||||
- `gpr/` — `protocol.py` + `faiss_gpr.py` (inline numpy fallback preserved); `core/gpr.py` is a shim
|
||||
- `satellite_matcher/` — `protocol.py` + `local_tile_loader.py` + `metric_refinement.py`; `core/satellite.py`, `core/metric.py` are shims
|
||||
- `mavlink_io/` — `protocol.py` + `pymavlink_bridge.py` + `mock_mavlink.py`; `core/mavlink.py` is a shim (re-exports private helpers `_confidence_to_fix_type`, `_eskf_to_gps_input`, `_unix_to_gps_time`)
|
||||
- `anchor_verifier/`, `safety_state/`, `flight_recorder/`, `coordinate_transforms/` — Protocol stubs only (Phases 3–5)
|
||||
|
||||
**Hot-path types** (`src/gps_denied/hot_types/`): `FrameState`, `IMUSample`, `PositionEstimate`, `VOEstimate`, `SatelliteAnchor` as `@dataclass(slots=True, frozen=True)`. Schemas shimmed to re-export. `Pose` stays Pydantic (mutation sites in `factor_graph.py` lines 182–297); `GPSPoint` stays Pydantic. Full hot-path migration deferred to Phase 2.
|
||||
|
||||
**Pipeline package** (`src/gps_denied/pipeline/`):
|
||||
- `orchestrator.py` — `FlightProcessor` (moved from `core/processor.py`)
|
||||
- `image_input.py`, `result_manager.py`, `sse_streamer.py` (moved from `core/`)
|
||||
- `composition.py` — `build_pipeline(env: Literal["jetson","x86_dev","ci","sitl"]) -> FlightProcessor`
|
||||
|
||||
**Composition root**: wires 10 components; lazy imports inside function body to avoid circular imports; Jetson env → `prefer_cuvslam=True`, `prefer_mono_depth=True`; other envs → mocks.
|
||||
|
||||
**Config**: `AppSettings.env` Literal field + `RuntimeConfig = AppSettings` alias. `pydantic-settings YamlConfigSettingsSource` loads `config/{env}.yaml`. `pyyaml>=6.0` declared.
|
||||
|
||||
**ABC→Protocol sweep**: 6 interfaces converted to `typing.Protocol` with `@runtime_checkable`:
|
||||
`IFactorGraphOptimizer`, `IRouteChunkManager`, `IFailureRecoveryCoordinator`, `IModelManager`, `IImageMatcher`, + all 8 component Protocols from `components/*/protocol.py`.
|
||||
|
||||
**`core/` retained** for concentrated math: `eskf.py`, `factor_graph.py`, `coordinates.py`, `chunk_manager.py`, `recovery.py`, `rotation.py`, `models.py`.
|
||||
|
||||
**Shim policy**: every moved file leaves a re-export shim at its old path. Tests import from old paths — shims keep them green. Shim removal is Phase 2 work.
|
||||
|
||||
### Deferred to Phase 2
|
||||
|
||||
- Full hot-path type migration (`Pose`, `GPSPoint`, remaining Pydantic models on frame path)
|
||||
- Test reorganization to `tests/{unit,integration,blackbox,sitl,e2e}/`
|
||||
- Shim removal from `core/`
|
||||
- YAML config enrichment with env-specific overrides (MAVLink connection strings, tile dirs)
|
||||
|
||||
## Stage 1 Decisions Inherited (validated, kept)
|
||||
|
||||
| Decision | Outcome |
|
||||
|----------|---------|
|
||||
| ESKF over EKF/UKF | ✓ Stage 1 |
|
||||
| XFeat over LiteSAM for satellite matching | ✓ Stage 1 |
|
||||
| OpenCV ORB stub for dev/CI; cuVSLAM on Jetson | ✓ Stage 1 |
|
||||
| AnyLoc/DINOv2 for GPR | ✓ Stage 1 |
|
||||
| diskcache + GeoHash for tiles | ✓ Stage 1 |
|
||||
| AsyncSQLAlchemy + aiosqlite | ✓ Stage 1 |
|
||||
|
||||
## Evolution
|
||||
|
||||
This document evolves at phase transitions and milestone boundaries.
|
||||
Each stage is its own iteration with its own PROJECT.md, REQUIREMENTS.md, ROADMAP.md. At stage completion:
|
||||
|
||||
**After each phase transition** (via `/gsd:transition`):
|
||||
1. Requirements invalidated? → Move to Out of Scope with reason
|
||||
2. Requirements validated? → Move to Validated with phase reference
|
||||
3. New requirements emerged? → Add to Active
|
||||
4. Decisions to log? → Add to Key Decisions
|
||||
5. "What This Is" still accurate? → Update if drifted
|
||||
|
||||
**After each milestone** (via `/gsd:complete-milestone`):
|
||||
1. Full review of all sections
|
||||
2. Core Value check — still the right priority?
|
||||
3. Audit Out of Scope — reasons still valid?
|
||||
4. Update Context with current state
|
||||
1. Snapshot current PROJECT.md / REQUIREMENTS.md / ROADMAP.md / phases/ → `.planning/archive/v[X.Y]/`
|
||||
2. Open new stage with fresh roadmap (Phase 1 of the new stage)
|
||||
3. Carry forward only validated decisions and unresolved Future-stages items
|
||||
|
||||
---
|
||||
*Last updated: 2026-04-01 after initialization*
|
||||
*Stage 2 opened: 2026-05-10*
|
||||
|
||||
+142
-123
@@ -1,157 +1,176 @@
|
||||
# Requirements: GPS-Denied Onboard Navigation System
|
||||
# Requirements: GPS-Denied Onboard Navigation System — Stage 2
|
||||
|
||||
**Defined:** 2026-04-01
|
||||
**Defined:** 2026-05-10
|
||||
**Stage:** 2 (independent iteration)
|
||||
**Branch:** `stage2` (HEAD = stage1; v1.0 archived)
|
||||
**Core Value:** The flight controller must receive valid MAVLink GPS_INPUT at 5-10Hz with position accuracy ≤50m for 80% of frames — without this, the UAV cannot navigate in GPS-denied airspace.
|
||||
|
||||
## v1 Requirements
|
||||
---
|
||||
|
||||
Requirements for this milestone. The scaffold (~2800 lines) exists; all algorithmic kernels are missing or mocked. Every requirement below maps to one phase of implementation work.
|
||||
## Stage 2 Requirements
|
||||
|
||||
### ESKF — Error-State Kalman Filter
|
||||
Stage 2 is a self-contained iteration. Phases are numbered 1–6 within this stage. Stage 1 work (its 36 v1 requirements + 7 phases) is archived in `.planning/archive/v1.0/` as starting capital, not as active backlog.
|
||||
|
||||
- [ ] **ESKF-01**: 15-state ESKF implemented (δp, δv, δθ, δb_a, δb_g) with IMU prediction step (F, Q matrices, bias propagation)
|
||||
- [ ] **ESKF-02**: VO measurement update implemented (relative pose ΔR/Δt from cuVSLAM, H_vo, R_vo covariance, Kalman gain)
|
||||
- [ ] **ESKF-03**: Satellite measurement update implemented (absolute WGS84 position from matching, H_sat, R_sat from RANSAC inlier ratio)
|
||||
- [ ] **ESKF-04**: ESKF state initializes from GLOBAL_POSITION_INT at startup and on mid-flight reboot with high-uncertainty covariance
|
||||
- [ ] **ESKF-05**: Confidence tier computation outputs HIGH/MEDIUM/LOW based on covariance magnitude and last satellite correction age
|
||||
- [ ] **ESKF-06**: Coordinate transform chain implemented: pixel→camera ray (K matrix), camera→body (T_cam_body), body→NED (ESKF quaternion), NED→WGS84 — replacing all FAKE Math stubs
|
||||
The stage 1 codebase (ESKF + cuVSLAM + GPR + MAVLink + pipeline + 195 passing tests) is treated as MVP — refactoring is allowed and expected. Concept-level ideas from the parallel `try02` branch are re-implemented (not merged).
|
||||
|
||||
### VO — Visual Odometry
|
||||
### ARCH — Hexagonal architecture & composition
|
||||
|
||||
- [ ] **VO-01**: cuVSLAM wrapper implemented for Jetson target (Inertial mode, camera + IMU inputs, relative pose output with metric scale)
|
||||
- [ ] **VO-02**: OpenCV ORB stub conforms to the same `ISequentialVisualOdometry` interface as cuVSLAM wrapper, used on dev/CI (x86)
|
||||
- [ ] **VO-03**: TensorRT FP16 inference engine loader implemented for SuperPoint and LightGlue on Jetson; MockInferenceEngine used on dev/CI
|
||||
- [ ] **VO-04**: Scale ambiguity resolved — `scale_ambiguous` is False when ESKF provides metric scale reference; VO relative pose is metric in NED
|
||||
- [ ] **VO-05**: ImageInputPipeline batch validation minimum lowered to 1 image (not 10); `get_image_by_sequence` uses exact filename matching
|
||||
- [ ] **ARCH-01**: Codebase reorganized to `src/gps_denied/components/{vio, satellite_matcher, gpr, anchor_verifier, safety_state, flight_recorder, mavlink_io, coordinate_transforms}/`, each containing `protocol.py` + concrete implementations + (where applicable) `native/` for backend bridges
|
||||
- [ ] **ARCH-02**: Hot-path data types (`FrameState`, `IMUSample`, `PositionEstimate`, `VOEstimate`, `SatelliteAnchor`) implemented as `@dataclass(slots=True, frozen=True)` in `src/gps_denied/hot_types/`; Pydantic retained only for REST/config/DB boundary schemas
|
||||
- [ ] **ARCH-03**: Explicit DI composition root `src/gps_denied/pipeline/composition.py` exposes `build_pipeline(env: Literal["jetson", "x86_dev", "ci", "sitl"]) -> Pipeline` that wires environment-specific implementations
|
||||
- [ ] **ARCH-04**: `core/` retained for concentrated math (ESKF, factor graph, RANSAC, coordinate transforms) — these stay as pure-function single files, NOT split into `interfaces.py + types.py + impl.py`
|
||||
- [ ] **ARCH-05**: All component Protocols defined with `typing.Protocol`; concrete adapters implement them; `Pipeline` constructor takes Protocol-typed dependencies (no concrete imports inside pipeline orchestration)
|
||||
- [ ] **ARCH-06**: Per-environment YAML configuration in `config/{jetson,x86_dev,ci,sitl}.yaml`, loaded via `pydantic-settings` into a typed `RuntimeConfig` model passed to `build_pipeline`
|
||||
- [ ] **ARCH-07**: All 195 stage1 tests + 8 SITL skipped continue to pass after refactor; no regression in accuracy benchmarks
|
||||
|
||||
### SAT — Satellite Matching
|
||||
### AC — Formal acceptance criteria document
|
||||
|
||||
- [ ] **SAT-01**: XFeat TRT FP16 inference engine implemented for satellite feature matching on Jetson; MockInferenceEngine used on dev/CI
|
||||
- [ ] **SAT-02**: Satellite tile selection uses ESKF position ± 3σ_horizontal to define search area; tiles assembled into mosaic at matcher resolution
|
||||
- [ ] **SAT-03**: GSD normalization implemented — camera frame downsampled to match satellite GSD (0.3–0.6 m/px) before matching
|
||||
- [ ] **SAT-04**: RANSAC homography estimation produces WGS84 absolute position with confidence score from inlier ratio
|
||||
- [ ] **SAT-05**: SatelliteDataManager reads from pre-loaded GeoHash-indexed local directory (read-only, no live HTTP fetches during flight)
|
||||
- [ ] **AC-01**: `_docs/00_problem/acceptance_criteria.md` rewritten with formal AC-1.x…AC-NEW-x list adapted from `try02` and validated against this project's actual constraints
|
||||
- [ ] **AC-02**: Each AC entry includes (a) numeric thresholds, (b) validation method, (c) at least one test ID linking to `tests/`
|
||||
- [ ] **AC-03**: Position accuracy AC (50m@80%, 20m@50%, anchor age tracking, drift bounds) bound to `tests/integration/accuracy/` and `tests/e2e/`
|
||||
- [ ] **AC-04**: Failure-mode AC (visual blackout, spoofing promotion, dead reckoning, ≥3 disconnected segments) bound to `tests/blackbox/failure_modes/`
|
||||
- [ ] **AC-05**: Real-time performance AC (<400ms p95 e2e, <8GB RAM, ≥5Hz GPS_INPUT output) bound to a benchmark harness producing CI-tracked metrics
|
||||
- [ ] **AC-06**: Traceability matrix `.planning/AC-TRACEABILITY.md` generated linking every AC ID → test ID(s) → implementing component(s)
|
||||
|
||||
### GPR — Global Place Recognition
|
||||
### SAFE — Safety anchor state machine
|
||||
|
||||
- [ ] **GPR-01**: Real Faiss index loaded at runtime from file path (not synthetic random vectors); index built from DINOv2 descriptors of actual satellite tiles during offline pre-processing
|
||||
- [ ] **GPR-02**: DINOv2/AnyLoc TRT FP16 inference engine implemented on Jetson; MockInferenceEngine used on dev/CI
|
||||
- [ ] **GPR-03**: GPR candidate retrieval returns real tile matches ranked by descriptor similarity, used for re-localization after tracking loss
|
||||
- [ ] **SAFE-01**: `components/safety_state/SafetyAnchorStateMachine` owns authoritative `source_label ∈ {satellite_anchored, vo_extrapolated, dead_reckoned}` for every emitted `PositionEstimate`
|
||||
- [ ] **SAFE-02**: Covariance growth is monotonic in non-anchored modes; resets only on accepted satellite anchor
|
||||
- [ ] **SAFE-03**: `anchor_age_ms` recorded on every estimate; transitions to `vo_extrapolated` after configurable max-age threshold
|
||||
- [ ] **SAFE-04**: State machine receives anchor decisions from `AnchorVerifier`, never raw VPR top-K — bad candidates cannot poison the state
|
||||
- [ ] **SAFE-05**: Tile write eligibility flag exposed (`can_persist_tile: bool`) — false in `dead_reckoned` mode to prevent corrupt tile cache writes
|
||||
- [ ] **SAFE-06**: Unit tests cover all 9 state transitions; property-based test asserts covariance never decreases without an accepted anchor
|
||||
|
||||
### MAV — MAVLink Output
|
||||
### VERIFY — Geometry-gated anchor verification
|
||||
|
||||
- [ ] **MAV-01**: pymavlink added to dependencies; MAVLink output component implemented sending GPS_INPUT over UART at 5-10Hz
|
||||
- [ ] **MAV-02**: ESKF state and covariance mapped to GPS_INPUT fields (lat/lon/alt from position, velocity from v-state, accuracy from covariance diagonal, fix_type from confidence tier, synthesized hdop/vdop, GPS time from system clock)
|
||||
- [ ] **MAV-03**: IMU input path implemented — MAVLink listener receives ATTITUDE/RAW_IMU from flight controller at 5-10Hz and feeds ESKF prediction step
|
||||
- [ ] **MAV-04**: Consecutive-failure counter detects 3 frames without any position estimate; sends MAVLink NAMED_VALUE_FLOAT re-localization request to ground station operator
|
||||
- [ ] **MAV-05**: Telemetry output at 1Hz sends confidence score and drift estimate to ground station via MAVLink NAMED_VALUE_FLOAT
|
||||
- [ ] **VERIFY-01**: `components/anchor_verifier/GeometryGatedAnchorVerifier` accepts/rejects satellite candidate matches based on configurable gates: min inliers, max mean reprojection error (px), max homography condition number
|
||||
- [ ] **VERIFY-02**: Rejection reason string emitted on every reject (`"too_few_inliers"`, `"mre_above_threshold"`, `"degenerate_homography"`, `"freshness_expired"`)
|
||||
- [ ] **VERIFY-03**: Freshness check integrates with sector classification (active-conflict <6mo, stable-rear <12mo) — expired tiles produce `freshness_expired` reject
|
||||
- [ ] **VERIFY-04**: Verifier benchmark mode evaluates multiple matcher profiles on the same frame for offline comparison
|
||||
- [ ] **VERIFY-05**: Unit tests cover each gate independently; integration test with real Azaion frame verifies end-to-end accept/reject
|
||||
|
||||
### PIPE — Pipeline Wiring
|
||||
### FDR — Flight data recorder
|
||||
|
||||
- [ ] **PIPE-01**: FlightProcessor.process_frame wired end-to-end: image in → cuVSLAM VO → ESKF VO update → (keyframe) satellite match → ESKF satellite update → GPS_INPUT output
|
||||
- [ ] **PIPE-02**: SatelliteDataManager and CoordinateTransformer instantiated and wired into processor pipeline (currently standalone, not connected)
|
||||
- [ ] **PIPE-03**: FactorGraph replaced or backed by real GTSAM ISAM2 incremental smoothing with BetweenFactorPose3 (VO) and GPSFactor (satellite anchors)
|
||||
- [ ] **PIPE-04**: FailureRecoveryCoordinator connected to ESKF — on tracking loss, ESKF continues IMU-only prediction with growing uncertainty; on recovery success, ESKF is reset with satellite position
|
||||
- [ ] **PIPE-05**: ImageRotationManager integrated into process_frame — heading sweep on first frame; `calculate_precise_angle` implemented with real VO-based refinement
|
||||
- [ ] **PIPE-06**: Object GPS localization endpoint (POST /objects/locate) uses full pixel→ray→ground→WGS84 chain with ESKF attitude; hardcoded stub removed
|
||||
- [ ] **PIPE-07**: Confidence scoring and fix_type mapping wired end-to-end: ESKF confidence tier → GPS_INPUT fix_type (3/2/0), accuracy fields
|
||||
- [ ] **PIPE-08**: ImageRotationManager constructor signature fixed (accepts optional ModelManager); startup TypeError resolved
|
||||
- [ ] **FDR-01**: `components/flight_recorder/FlightRecorder` Protocol with `append_event(event)` and `export() -> FdrExportResult`
|
||||
- [ ] **FDR-02**: `InMemoryFlightRecorder` impl with bounded segments and configurable segment+storage byte limits
|
||||
- [ ] **FDR-03**: `DiskFlightRecorder` impl writing append-only JSONL segments under `data/fdr/{flight_id}/segment-NNNN.jsonl`
|
||||
- [ ] **FDR-04**: Health states `ok / degraded (≥90% storage) / critical (limit reached)` exposed via `health` property
|
||||
- [ ] **FDR-05**: Pipeline emits FDR events at every state transition, anchor decision, MAVLink send, and pipeline error
|
||||
- [ ] **FDR-06**: AC-NEW-3 forensic-thumbnail rate (≤0.1Hz on tile-generation failures) wired through FDR with size budget enforcement
|
||||
|
||||
### TEST — Test Harness and Validation
|
||||
### VPR — Conditional + multi-scale visual place recognition
|
||||
|
||||
- [ ] **TEST-01**: Docker SITL test harness implemented: ArduPilot SITL container, camera-replay service, satellite tile server mock, MAVLink capture
|
||||
- [ ] **TEST-02**: CI pipeline runs on x86 using OpenCV ORB stub and MockInferenceEngine; all unit tests pass
|
||||
- [ ] **TEST-03**: Accuracy validation test runs against 60-frame dataset (AD000001–AD000060.jpg) with coordinates.csv ground truth; reports 80%/50m and 60%/20m hit rates
|
||||
- [ ] **TEST-04**: Performance benchmark test validates <400ms end-to-end per frame on Jetson (or reports estimated latency breakdown on dev)
|
||||
- [ ] **TEST-05**: All 21 blackbox test scenarios (FT-P-01 to FT-P-14, FT-N-01 to FT-N-07) implemented as runnable pytest tests using SITL harness
|
||||
- [ ] **VPR-01**: VPR retrieval triggered conditionally — DINOv2 forward runs only on re-loc triggers (cold start, sharp turn AC-3.2, σ_xy > 50m, VO failure ≥2 frames, disconnected segment AC-3.3); steady-state uses geometric prior (IMU+VO predicted position) ranking by distance
|
||||
- [ ] **VPR-02**: VPR chunks decoupled from storage tiles — chunks sized to ground footprint (600-800m at deployment altitude band) with 40-50% overlap; any frame footprint falls fully inside ≥1 chunk
|
||||
- [ ] **VPR-03**: Multi-scale FAISS index — fine-scale (z=20-derived) + coarse-scale (z=17 or z=18) descriptor sets; coarse used in active-conflict sectors for change-robust retrieval
|
||||
- [ ] **VPR-04**: Dynamic top-K — K=5 in stable sectors with σ_xy ≤ 20m, K=20 in active-conflict, K=50 on expanding-window fallback
|
||||
- [ ] **VPR-05**: Chunking and indexing integrated into existing `chunk_manager.py`/`gpr.py` without breaking stage1 GPR API contracts
|
||||
|
||||
## v2 Requirements
|
||||
### MAVOUT — MAVLink output: source labels, dual-channel scaffold
|
||||
|
||||
Deferred to future release. Tracked but not in current roadmap.
|
||||
- [ ] **MAVOUT-01**: Every emitted `GPS_INPUT` includes `source_label`, `anchor_age_ms`, `covariance_semimajor_m` propagated from `PositionEstimate` (mapped into `horiz_accuracy` and a custom STATUSTEXT for label/age)
|
||||
- [ ] **MAVOUT-02**: `ODOMETRY` emitter scaffolded behind feature flag (`config.mavlink.odometry_enabled`); flag is false in stage 2; integration test asserts ODOMETRY is intentionally absent on the wire
|
||||
- [ ] **MAVOUT-03**: Spoofing-promotion latency monitor — listens to `GPS_RAW_INT`/`EKF_STATUS_REPORT`/`SYS_STATUS`; promotes own estimate to FC primary within <3s when real-GPS health rolling avg < threshold; emits `STATUSTEXT` on every promotion/demotion
|
||||
- [ ] **MAVOUT-04**: Visual blackout handling — pipeline switches to `dead_reckoned` within ≤1 processed frame OR ≤400ms when camera produces no usable signal; emits `VISUAL_BLACKOUT_IMU_ONLY` STATUSTEXT @ 1-2Hz
|
||||
|
||||
### Security
|
||||
### FIXTURE — Real-flight integration fixture (Azaion 10.05.2026)
|
||||
|
||||
- **SEC-01**: JWT bearer token authentication on all API endpoints
|
||||
- **SEC-02**: TLS 1.3 on all HTTPS connections
|
||||
- **SEC-03**: Satellite tile manifest SHA-256 integrity verification
|
||||
- **SEC-04**: Mahalanobis distance outlier rejection in ESKF measurement updates
|
||||
- **SEC-05**: CORS origins locked down (remove wildcard default)
|
||||
- [ ] **FIXTURE-01**: `tests/integration/azaion_flight/` integration test suite consuming `Data/Azaion/10.05.2026/` (tlog + cropped EO video + MAVLink CSV)
|
||||
- [ ] **FIXTURE-02**: Preprocessing script `scripts/prep_azaion_fixture.py` producing — (a) HUD-stripped EO frames at 0.7 fps, (b) IMU/GPS/ATTITUDE CSV from tlog, (c) timestamp-aligned manifest
|
||||
- [ ] **FIXTURE-03**: MAVLink replay test — feed tlog through `MAVLinkBridge` parser, assert all `GLOBAL_POSITION_INT`/`RAW_IMU`/`ATTITUDE` messages decoded without error
|
||||
- [ ] **FIXTURE-04**: ESKF real-IMU smoke test — replay IMU samples through `ESKFCore.predict`, assert no NaN/Inf, bounded covariance growth
|
||||
- [ ] **FIXTURE-05**: VO smoke test on cropped EO frames using ORB-SLAM3 backend — assert ≥30% frame registration success
|
||||
- [ ] **FIXTURE-06**: GPS-denial simulation — mask `GPS_RAW_INT` for t∈[180s, 280s], replay rest of stream, assert pipeline switches to `vo_extrapolated` and back to `satellite_anchored` correctly
|
||||
- [ ] **FIXTURE-07**: Azaion fixture documented in `_docs/00_problem/fixtures.md` with ground-truth references and known limitations (low altitude, multirotor dynamics, HUD overlay)
|
||||
|
||||
### Operational
|
||||
### TEST — Test taxonomy & infrastructure
|
||||
|
||||
- **OPS-01**: Uvicorn `reload` flag defaults to False in production config
|
||||
- **OPS-02**: Structured logging with configurable log levels per module
|
||||
- **OPS-03**: Pre-flight health check validates TRT engines loaded, tiles present, IMU receiving
|
||||
- **OPS-04**: ResultManager.publish_waypoint_update implemented for waypoint SSE emission
|
||||
- [ ] **TEST-01**: `tests/` reorganized to `tests/{unit,integration,blackbox,sitl,e2e}/`; existing tests redistributed by category
|
||||
- [ ] **TEST-02**: `pyproject.toml` test markers updated — `pytest -m unit` / `-m integration` / etc.; CI runs unit+integration on every push, blackbox on PR, sitl+e2e nightly
|
||||
- [ ] **TEST-03**: AC traceability auto-generated — pytest plugin tags each test with `@pytest.mark.ac("AC-1.1")`; `scripts/gen_ac_traceability.py` produces the matrix in `.planning/AC-TRACEABILITY.md`
|
||||
|
||||
### Performance
|
||||
### OBS — Observability & tooling
|
||||
|
||||
- **PERF-01**: Dual CUDA stream execution (Stream A: VO, Stream B: satellite matching) for pipeline parallelism
|
||||
- **PERF-02**: Satellite tile RAM preload (±2km corridor) at startup for sub-millisecond tile access
|
||||
- [ ] **OBS-01**: Structured JSON logging via `structlog` with `correlation_id` (frame_id) propagated through pipeline; Pydantic logging schemas at boundaries
|
||||
- [ ] **OBS-02**: CLI tool `gps_denied` (typer-based) with subcommands — `replay --tlog ... --video ...`, `benchmark --scenario ...`, `bench-ac AC-1.1` for AC-driven benchmark runs
|
||||
- [ ] **OBS-03**: Per-environment Docker images split — `Dockerfile.x86_dev` for CI/dev, `Dockerfile.jetson` (multi-stage with TRT engine prebuild step) for hardware
|
||||
|
||||
## Out of Scope
|
||||
---
|
||||
|
||||
Explicitly excluded. Documented to prevent scope creep.
|
||||
## Stage 3 candidates (parking lot)
|
||||
|
||||
| Feature | Reason |
|
||||
|---------|--------|
|
||||
| TRT engine building tooling | Engines are pre-built offline via trtexec; system only loads them |
|
||||
| Google Maps tile download tooling | Tiles pre-cached before flight; no live internet during flight |
|
||||
| Full ArduPilot hardware validation on Jetson | Post-v1; Jetson hardware testing is not in scope for this milestone |
|
||||
| Mobile/web ground station UI | SSE stream consumed by external systems; UI is out of scope |
|
||||
| Multi-UAV coordination | Single UAV instance only |
|
||||
| GTSAM ARM64 source build tooling | GTSAM on Jetson requires source compilation; CI uses mock; Jetson build is ops concern |
|
||||
| tech_stack.md synchronization | Documented inconsistency (3fps vs 0.7fps, etc.); separate documentation task |
|
||||
- Mid-flight tile generation + write-back to Azaion Satellite Service (AC-8.4)
|
||||
- On-device hardware validation on Jetson Orin Nano Super
|
||||
- Dual-channel MAVLink ODOMETRY enabled (depends on ArduPilot fixes for EKF3 source switching)
|
||||
- AC-NEW-1 cold-boot time-to-first-fix bench (<30s, 50× cold reboot)
|
||||
- BASALT VIO backend evaluation (only if cuVSLAM hits a blocker)
|
||||
|
||||
## Out of Scope (Stage 2)
|
||||
|
||||
- Migration to PostgreSQL (SQLite remains embedded default; Postgres optional for ground station only)
|
||||
- Folder-per-component layout for `core/` math files (ESKF/factor graph stay concentrated)
|
||||
- Real microservices with separate processes / IPC
|
||||
- Pydantic on per-frame hot path (dataclasses replace it)
|
||||
- Mobile/web ground station UI
|
||||
- Multi-UAV coordination
|
||||
|
||||
## Traceability
|
||||
|
||||
Which phases cover which requirements. Populated from ROADMAP.md phase assignments.
|
||||
Populated by roadmapper on 2026-05-10. Test IDs will be filled in by `/gsd:plan-phase` and `/gsd:implement` as each phase produces concrete tests.
|
||||
|
||||
| Requirement | Phase | Status |
|
||||
|-------------|-------|--------|
|
||||
| ESKF-01 | Phase 1 | Pending |
|
||||
| ESKF-02 | Phase 1 | Pending |
|
||||
| ESKF-03 | Phase 1 | Pending |
|
||||
| ESKF-04 | Phase 1 | Pending |
|
||||
| ESKF-05 | Phase 1 | Pending |
|
||||
| ESKF-06 | Phase 1 | Pending |
|
||||
| VO-01 | Phase 2 | Pending |
|
||||
| VO-02 | Phase 2 | Pending |
|
||||
| VO-03 | Phase 2 | Pending |
|
||||
| VO-04 | Phase 2 | Pending |
|
||||
| VO-05 | Phase 2 | Pending |
|
||||
| SAT-01 | Phase 3 | Pending |
|
||||
| SAT-02 | Phase 3 | Pending |
|
||||
| SAT-03 | Phase 3 | Pending |
|
||||
| SAT-04 | Phase 3 | Pending |
|
||||
| SAT-05 | Phase 3 | Pending |
|
||||
| GPR-01 | Phase 3 | Pending |
|
||||
| GPR-02 | Phase 3 | Pending |
|
||||
| GPR-03 | Phase 3 | Pending |
|
||||
| MAV-01 | Phase 4 | Pending |
|
||||
| MAV-02 | Phase 4 | Pending |
|
||||
| MAV-03 | Phase 4 | Pending |
|
||||
| MAV-04 | Phase 4 | Pending |
|
||||
| MAV-05 | Phase 4 | Pending |
|
||||
| PIPE-01 | Phase 5 | Pending |
|
||||
| PIPE-02 | Phase 5 | Pending |
|
||||
| PIPE-03 | Phase 5 | Pending |
|
||||
| PIPE-04 | Phase 5 | Pending |
|
||||
| PIPE-05 | Phase 5 | Pending |
|
||||
| PIPE-06 | Phase 5 | Pending |
|
||||
| PIPE-07 | Phase 5 | Pending |
|
||||
| PIPE-08 | Phase 5 | Pending |
|
||||
| TEST-01 | Phase 6 | Pending |
|
||||
| TEST-02 | Phase 6 | Pending |
|
||||
| TEST-03 | Phase 7 | Pending |
|
||||
| TEST-04 | Phase 7 | Pending |
|
||||
| TEST-05 | Phase 7 | Pending |
|
||||
| REQ | Phase | Tests |
|
||||
|-----|-------|-------|
|
||||
| ARCH-01 | Phase 1 | _pending plan-phase_ |
|
||||
| ARCH-02 | Phase 1 | _pending plan-phase_ |
|
||||
| ARCH-03 | Phase 1 | _pending plan-phase_ |
|
||||
| ARCH-04 | Phase 1 | _pending plan-phase_ |
|
||||
| ARCH-05 | Phase 1 | _pending plan-phase_ |
|
||||
| ARCH-06 | Phase 1 | _pending plan-phase_ |
|
||||
| ARCH-07 | Phase 1 | _pending plan-phase_ |
|
||||
| AC-01 | Phase 2 | _pending plan-phase_ |
|
||||
| AC-02 | Phase 2 | _pending plan-phase_ |
|
||||
| AC-03 | Phase 2 | _pending plan-phase_ |
|
||||
| AC-04 | Phase 2 | _pending plan-phase_ |
|
||||
| AC-05 | Phase 2 | _pending plan-phase_ |
|
||||
| AC-06 | Phase 2 | _pending plan-phase_ |
|
||||
| TEST-01 | Phase 2 | _pending plan-phase_ |
|
||||
| TEST-02 | Phase 2 | _pending plan-phase_ |
|
||||
| TEST-03 | Phase 2 | _pending plan-phase_ |
|
||||
| OBS-01 | Phase 2 | _pending plan-phase_ |
|
||||
| SAFE-01 | Phase 3 | _pending plan-phase_ |
|
||||
| SAFE-02 | Phase 3 | _pending plan-phase_ |
|
||||
| SAFE-03 | Phase 3 | _pending plan-phase_ |
|
||||
| SAFE-04 | Phase 3 | _pending plan-phase_ |
|
||||
| SAFE-05 | Phase 3 | _pending plan-phase_ |
|
||||
| SAFE-06 | Phase 3 | _pending plan-phase_ |
|
||||
| VERIFY-01 | Phase 3 | _pending plan-phase_ |
|
||||
| VERIFY-02 | Phase 3 | _pending plan-phase_ |
|
||||
| VERIFY-03 | Phase 3 | _pending plan-phase_ |
|
||||
| VERIFY-04 | Phase 3 | _pending plan-phase_ |
|
||||
| VERIFY-05 | Phase 3 | _pending plan-phase_ |
|
||||
| VPR-01 | Phase 4 | _pending plan-phase_ |
|
||||
| VPR-02 | Phase 4 | _pending plan-phase_ |
|
||||
| VPR-03 | Phase 4 | _pending plan-phase_ |
|
||||
| VPR-04 | Phase 4 | _pending plan-phase_ |
|
||||
| VPR-05 | Phase 4 | _pending plan-phase_ |
|
||||
| FDR-01 | Phase 4 | _pending plan-phase_ |
|
||||
| FDR-02 | Phase 4 | _pending plan-phase_ |
|
||||
| FDR-03 | Phase 4 | _pending plan-phase_ |
|
||||
| FDR-04 | Phase 4 | _pending plan-phase_ |
|
||||
| FDR-05 | Phase 4 | _pending plan-phase_ |
|
||||
| FDR-06 | Phase 4 | _pending plan-phase_ |
|
||||
| MAVOUT-01 | Phase 5 | _pending plan-phase_ |
|
||||
| MAVOUT-02 | Phase 5 | _pending plan-phase_ |
|
||||
| MAVOUT-03 | Phase 5 | _pending plan-phase_ |
|
||||
| MAVOUT-04 | Phase 5 | _pending plan-phase_ |
|
||||
| FIXTURE-01 | Phase 6 | _pending plan-phase_ |
|
||||
| FIXTURE-02 | Phase 6 | _pending plan-phase_ |
|
||||
| FIXTURE-03 | Phase 6 | _pending plan-phase_ |
|
||||
| FIXTURE-04 | Phase 6 | _pending plan-phase_ |
|
||||
| FIXTURE-05 | Phase 6 | _pending plan-phase_ |
|
||||
| FIXTURE-06 | Phase 6 | _pending plan-phase_ |
|
||||
| FIXTURE-07 | Phase 6 | _pending plan-phase_ |
|
||||
| OBS-02 | Phase 6 | _pending plan-phase_ |
|
||||
| OBS-03 | Phase 6 | _pending plan-phase_ |
|
||||
|
||||
**Coverage:**
|
||||
- v1 requirements: 36 total
|
||||
- Mapped to phases: 36
|
||||
- Unmapped: 0
|
||||
|
||||
---
|
||||
*Requirements defined: 2026-04-01*
|
||||
*Last updated: 2026-04-01 after initial definition*
|
||||
**Coverage:** 52/52 requirements mapped. No orphans, no duplicates.
|
||||
|
||||
+120
-82
@@ -1,114 +1,152 @@
|
||||
# Roadmap: GPS-Denied Onboard Navigation System
|
||||
# Roadmap: GPS-Denied Onboard Navigation System — Stage 2
|
||||
|
||||
**Stage:** 2 (independent iteration)
|
||||
**Created:** 2026-05-10
|
||||
**Branch:** `stage2` (HEAD = stage1; v1.0 archived under `.planning/archive/v1.0/`)
|
||||
**Granularity:** standard
|
||||
**Total phases:** 6
|
||||
**Total requirements mapped:** 52 / 52 (100% coverage)
|
||||
|
||||
---
|
||||
|
||||
## Overview
|
||||
|
||||
The scaffold exists (~2800 lines): FastAPI service, all component ABCs, Pydantic schemas, database layer, and SSE streaming are in place. What is missing is every algorithmic kernel. This roadmap implements them in dependency order: the ESKF math core first (everything else feeds into it), then the two sensor inputs (VO and satellite/GPR), then the MAVLink output that closes the loop to the flight controller, then end-to-end pipeline wiring, then a Docker SITL test harness, and finally accuracy validation against real flight data.
|
||||
Stage 2 is a **self-contained iteration** with its own phase numbering (1–6). It is NOT a continuation of Stage 1's seven phases — those are archived under `.planning/archive/v1.0/` and treated as MVP starting capital (the working ESKF + cuVSLAM/ORB VO + GPR + MAVLink + 195 passing tests).
|
||||
|
||||
The Stage 2 mission: refactor the inherited MVP into a hexagonal/ports-and-adapters architecture, re-implement (not merge) selected concept-level ideas from the parallel `try02` branch, formalize acceptance criteria with testable numerics, and add the Azaion 10.05.2026 real-flight integration fixture — all without regressing any of the 195 stage1 tests.
|
||||
|
||||
Phases are derived from the ten Stage 2 requirement categories (ARCH, AC, SAFE, VERIFY, FDR, VPR, MAVOUT, FIXTURE, TEST, OBS) and ordered so each phase stabilizes the Protocol surfaces and test infrastructure that the next phase depends on.
|
||||
|
||||
## Phase Dependency Order
|
||||
|
||||
```
|
||||
Phase 1 (ARCH — hexagonal refactor + composition root; Protocols stabilized)
|
||||
↓
|
||||
Phase 2 (AC + TEST taxonomy + structlog spine — measurement scaffolding)
|
||||
↓
|
||||
Phase 3 (SAFE state machine + VERIFY anchor gates — authoritative source labels)
|
||||
↓
|
||||
Phase 4 (Conditional Multi-Scale VPR + FDR — uses SAFE triggers, FDR for audit)
|
||||
↓
|
||||
Phase 5 (MAVOUT — source-aware GPS_INPUT + spoofing + blackout — needs SAFE labels)
|
||||
↓
|
||||
Phase 6 (FIXTURE — Azaion replay + CLI + per-env Docker — exercises everything e2e)
|
||||
```
|
||||
|
||||
## Phases
|
||||
|
||||
- [ ] **Phase 1: ESKF Core** - 15-state error-state Kalman filter, coordinate transforms, confidence scoring
|
||||
- [ ] **Phase 2: Visual Odometry** - cuVSLAM wrapper (Jetson) + OpenCV ORB stub (dev/CI) + TRT SuperPoint/LightGlue
|
||||
- [ ] **Phase 3: Satellite Matching + GPR** - XFeat TRT matching, offline tile pipeline, real Faiss GPR index
|
||||
- [ ] **Phase 4: MAVLink I/O** - pymavlink GPS_INPUT output loop, IMU input listener, telemetry, re-localization request
|
||||
- [ ] **Phase 5: End-to-End Pipeline Wiring** - processor integration, GTSAM factor graph, recovery coordinator, object localization
|
||||
- [ ] **Phase 6: Docker SITL Harness + CI** - ArduPilot SITL, camera replay, tile server mock, CI integration
|
||||
- [ ] **Phase 7: Accuracy Validation** - 60-frame dataset validation, latency profiling, blackbox test suite
|
||||
- [ ] **Phase 1: Hexagonal Refactor & Composition Root** — Reorganize stage1 MVP into `components/` hexagonal layout with Protocol-typed DI composition root; no regressions.
|
||||
- [ ] **Phase 2: Acceptance Criteria + Test Taxonomy + Observability Spine** — Formal AC document with numeric thresholds, `tests/{unit,integration,blackbox,sitl,e2e}/` taxonomy, structlog correlation_id spine.
|
||||
- [ ] **Phase 3: Safety Anchor State Machine & Geometry-Gated Verifier** — Authoritative `source_label` ownership + accept/reject gates for satellite anchors before they reach ESKF.
|
||||
- [ ] **Phase 4: Conditional Multi-Scale VPR + Flight Data Recorder** — Trigger-driven DINOv2 forward, multi-scale FAISS chunks, append-only event log with bounded storage.
|
||||
- [ ] **Phase 5: MAVLink Source-Aware Output & Spoofing/Blackout Handling** — Source labels + anchor age in GPS_INPUT, spoofing-promotion <3s, visual-blackout dead-reckoning ≤400ms, ODOMETRY scaffold behind feature flag.
|
||||
- [ ] **Phase 6: Real-Flight Fixture (Azaion 10.05.2026) + CLI + Per-Env Docker** — End-to-end integration test on real flight data, `gps_denied` typer CLI, split Jetson/x86 Dockerfiles.
|
||||
|
||||
## Phase Details
|
||||
|
||||
### Phase 1: ESKF Core
|
||||
**Goal**: A correct, standalone ESKF implementation exists that fuses IMU, VO, and satellite measurements and outputs confidence-tiered position estimates in WGS84
|
||||
**Depends on**: Nothing (first phase — no other algorithmic component depends on this being absent)
|
||||
**Requirements**: ESKF-01, ESKF-02, ESKF-03, ESKF-04, ESKF-05, ESKF-06
|
||||
**Success Criteria** (what must be TRUE):
|
||||
1. ESKF propagates nominal state (position, velocity, quaternion, biases) from synthetic IMU inputs and covariance grows correctly between measurement updates
|
||||
2. VO measurement update reduces position uncertainty and innovation is within expected bounds for a simulated relative pose input
|
||||
3. Satellite measurement update corrects absolute position and covariance tightens to satellite noise level
|
||||
4. Confidence tier outputs HIGH when last satellite correction is recent and covariance is small, MEDIUM on VO-only, LOW on IMU-only — verified by unit tests
|
||||
5. Full coordinate chain (pixel → camera ray → body → NED → WGS84) produces correct GPS coordinates for a known geometry test case; all FAKE Math stubs replaced
|
||||
**Plans**: 3 plans
|
||||
Plans:
|
||||
- [x] 01-01-PLAN.md — ESKF core algorithm (schemas, 15-state filter, IMU prediction, VO/satellite updates, confidence tiers)
|
||||
- [x] 01-02-PLAN.md — Coordinate chain fix (replace fake math with real K matrix projection, ray-ground intersection)
|
||||
- [x] 01-03-PLAN.md — Unit tests for ESKF and coordinate chain (18+ ESKF tests, 10+ coordinate tests)
|
||||
### Phase 1: Hexagonal Refactor & Composition Root
|
||||
|
||||
### Phase 2: Visual Odometry
|
||||
**Goal**: VO produces metric relative poses via cuVSLAM on Jetson and via OpenCV ORB on dev/CI, both satisfying the same interface — no more scale-ambiguous unit vectors
|
||||
**Depends on**: Phase 1 (ESKF provides metric scale reference and coordinate transforms for VO measurement update)
|
||||
**Requirements**: VO-01, VO-02, VO-03, VO-04, VO-05
|
||||
**Goal**: Stage1 MVP reorganized into hexagonal/ports-and-adapters layout with explicit DI composition root; all 195 stage1 tests still pass.
|
||||
**Depends on**: Nothing (first phase; consumes stage1 archived code as input).
|
||||
**Requirements**: ARCH-01, ARCH-02, ARCH-03, ARCH-04, ARCH-05, ARCH-06, ARCH-07
|
||||
**Success Criteria** (what must be TRUE):
|
||||
1. cuVSLAM wrapper initializes in Inertial mode with camera intrinsics and IMU parameters, and returns RelativePose with `scale_ambiguous=False` and metric translation in NED
|
||||
2. OpenCV ORB stub satisfies the same ISequentialVisualOdometry interface and passes the same interface contract tests as the cuVSLAM wrapper
|
||||
3. TRT SuperPoint/LightGlue engines load and run inference on Jetson; MockInferenceEngine is selected automatically on dev/x86
|
||||
4. ImageInputPipeline accepts single-image batches without error; sequence lookup returns the correct frame with no substring collision
|
||||
1. Every swappable component (vio, satellite_matcher, gpr, anchor_verifier, safety_state, flight_recorder, mavlink_io, coordinate_transforms) lives under `src/gps_denied/components/<name>/` with its own `protocol.py` + concrete impls + (where needed) `native/` bridge.
|
||||
2. Hot-path types (`FrameState`, `IMUSample`, `PositionEstimate`, `VOEstimate`, `SatelliteAnchor`) are `@dataclass(slots=True, frozen=True)` and Pydantic no longer touches the per-frame path.
|
||||
3. Calling `build_pipeline(env="x86_dev")` / `"jetson"` / `"ci"` / `"sitl"` from `pipeline/composition.py` returns a fully-wired `Pipeline` with environment-correct adapters and no concrete imports leaking into pipeline orchestration.
|
||||
4. Per-environment YAML configs (`config/{jetson,x86_dev,ci,sitl}.yaml`) load via `pydantic-settings` into a typed `RuntimeConfig` that drives composition.
|
||||
5. `pytest` runs all 195 stage1 tests (+ 8 SITL skipped) green and accuracy benchmarks show no regression vs the archived stage1 baseline.
|
||||
**Plans**: TBD
|
||||
|
||||
### Phase 3: Satellite Matching + GPR
|
||||
**Goal**: The system can correct absolute position from pre-loaded satellite tiles and re-localize after tracking loss using a real Faiss descriptor index
|
||||
**Depends on**: Phase 1 (ESKF position uncertainty drives tile selection radius and measurement update), Phase 2 (VO provides keyframe selection timing)
|
||||
**Requirements**: SAT-01, SAT-02, SAT-03, SAT-04, SAT-05, GPR-01, GPR-02, GPR-03
|
||||
### Phase 2: Acceptance Criteria + Test Taxonomy + Observability Spine
|
||||
|
||||
**Goal**: Project gains a formal, testable acceptance-criteria contract, a structured test taxonomy, and a structured-logging spine — the measurement scaffolding every later phase needs to prove its claims.
|
||||
**Depends on**: Phase 1 (Protocol surfaces and components/ layout must exist before tests/AC can reference them).
|
||||
**Requirements**: AC-01, AC-02, AC-03, AC-04, AC-05, AC-06, TEST-01, TEST-02, TEST-03, OBS-01
|
||||
**Success Criteria** (what must be TRUE):
|
||||
1. Satellite tile selection queries the local GeoHash-indexed directory using ESKF position ± 3σ and returns correct tiles without any HTTP requests
|
||||
2. Camera frame is GSD-normalized to satellite resolution before matching; XFeat TRT inference runs on Jetson and MockInferenceEngine on dev/CI
|
||||
3. RANSAC homography produces a WGS84 position estimate with a confidence score derived from inlier ratio, accepted by ESKF satellite measurement update
|
||||
4. GPR loads a real Faiss index from disk and returns tile candidates ranked by DINOv2 descriptor similarity (not random vectors)
|
||||
5. After simulated tracking loss, GPR candidate + MetricRefinement produces an ESKF re-localization within expected accuracy bounds
|
||||
1. `_docs/00_problem/acceptance_criteria.md` lists every AC-1.x…AC-NEW-x with numeric threshold + validation method + linked test ID(s); no AC entry is unbound.
|
||||
2. `tests/` is reorganized into `unit/integration/blackbox/sitl/e2e/`, every existing test is reclassified, and `pytest -m unit|integration|blackbox|sitl|e2e` selects the right subset for CI.
|
||||
3. Running `scripts/gen_ac_traceability.py` produces `.planning/AC-TRACEABILITY.md` linking every AC ID → test ID(s) → component(s); CI fails if any AC is orphaned.
|
||||
4. Position-accuracy, failure-mode, and real-time-performance ACs are wired to `tests/integration/accuracy/`, `tests/blackbox/failure_modes/`, and a benchmark harness that emits CI-tracked metrics.
|
||||
5. Pipeline emits structured JSON via `structlog` with `correlation_id` (frame_id) on every per-frame log line, and Pydantic logging schemas guard the boundary records.
|
||||
**Plans**: TBD
|
||||
|
||||
### Phase 4: MAVLink I/O
|
||||
**Goal**: The flight controller receives GPS_INPUT at 5-10Hz and the system receives IMU data from the flight controller — the primary acceptance criterion is met end-to-end for the communication layer
|
||||
**Depends on**: Phase 1 (ESKF state is the source for GPS_INPUT field population; IMU data drives ESKF prediction)
|
||||
**Requirements**: MAV-01, MAV-02, MAV-03, MAV-04, MAV-05
|
||||
### Phase 3: Safety Anchor State Machine & Geometry-Gated Verifier
|
||||
|
||||
**Goal**: A separate safety layer — not the ESKF — owns the authoritative `source_label`, enforces monotonic covariance growth in non-anchored modes, and only accepts satellite anchors that pass formal geometric gates.
|
||||
**Depends on**: Phase 2 (needs AC document + test taxonomy + structlog so state-machine behavior is testable and observable).
|
||||
**Requirements**: SAFE-01, SAFE-02, SAFE-03, SAFE-04, SAFE-05, SAFE-06, VERIFY-01, VERIFY-02, VERIFY-03, VERIFY-04, VERIFY-05
|
||||
**Success Criteria** (what must be TRUE):
|
||||
1. pymavlink sends GPS_INPUT messages to a MAVLink endpoint at 5-10Hz; all required fields populated (lat, lon, alt, velocity, accuracy, fix_type, hdop, vdop, GPS time)
|
||||
2. fix_type maps correctly from ESKF confidence tier: HIGH → 3 (3D fix), MEDIUM → 2 (2D fix), LOW → 0 (no fix)
|
||||
3. IMU listener receives ATTITUDE/RAW_IMU from flight controller at 5-10Hz and ESKF prediction step runs at that rate between camera frames
|
||||
4. After 3 consecutive frames with no position estimate, a MAVLink NAMED_VALUE_FLOAT message with last known position is sent (verifiable in SITL logs)
|
||||
5. Telemetry at 1Hz emits confidence score and drift estimate to ground station via NAMED_VALUE_FLOAT
|
||||
1. Every emitted `PositionEstimate` carries one of `satellite_anchored / vo_extrapolated / dead_reckoned` set by `SafetyAnchorStateMachine`, plus an `anchor_age_ms` field that increases until the next accepted anchor.
|
||||
2. Property-based tests prove covariance never decreases without an accepted anchor, and a unit-test matrix exercises all 9 declared state transitions.
|
||||
3. `GeometryGatedAnchorVerifier` accepts/rejects each candidate using configurable gates (min inliers, max mean reprojection error, max homography condition number, freshness window) and emits a machine-readable rejection reason on every reject.
|
||||
4. Tile-write eligibility (`can_persist_tile`) is exposed by the state machine and is `false` whenever the system is in `dead_reckoned`, so the tile cache cannot be poisoned during blind flight.
|
||||
5. The state machine never sees raw VPR top-K candidates — `AnchorVerifier` is the only path that can hand it an accepted anchor — and benchmark mode lets matcher profiles be compared offline on a fixed frame.
|
||||
**Plans**: TBD
|
||||
|
||||
### Phase 5: End-to-End Pipeline Wiring
|
||||
**Goal**: A single uploaded camera frame travels through the full pipeline — VO, ESKF update, satellite correction (on keyframes), GPS_INPUT output — with no hardcoded stubs in the path
|
||||
**Depends on**: Phase 1, Phase 2, Phase 3, Phase 4 (all algorithmic components must exist to be wired)
|
||||
**Requirements**: PIPE-01, PIPE-02, PIPE-03, PIPE-04, PIPE-05, PIPE-06, PIPE-07, PIPE-08
|
||||
### Phase 4: Conditional Multi-Scale VPR + Flight Data Recorder
|
||||
|
||||
**Goal**: DINOv2 retrieval runs only when re-localization is actually needed; chunks are decoupled from storage tiles with multi-scale coverage; every state transition / anchor decision / MAVLink emission is captured in an append-only flight recorder with bounded storage and explicit health states.
|
||||
**Depends on**: Phase 3 (VPR triggers and FDR events ride on SAFE state-transitions and VERIFY accept/reject decisions).
|
||||
**Requirements**: VPR-01, VPR-02, VPR-03, VPR-04, VPR-05, FDR-01, FDR-02, FDR-03, FDR-04, FDR-05, FDR-06
|
||||
**Success Criteria** (what must be TRUE):
|
||||
1. process_frame executes the full chain without error: VO relative pose → ESKF VO update → (every 5-10 frames) satellite match → ESKF satellite update → GPS_INPUT sent to flight controller
|
||||
2. SatelliteDataManager and CoordinateTransformer are instantiated in app.py lifespan and injected into the processor; no component is standalone
|
||||
3. FactorGraphOptimizer calls real GTSAM ISAM2 update when GTSAM is available; mock path remains for CI
|
||||
4. Object GPS localization (POST /objects/locate) returns a WGS84 position using the real pixel→ray→ground chain; hardcoded (48.0, 37.0) stub is gone
|
||||
5. Application starts without TypeError; ImageRotationManager constructor accepts the model manager argument
|
||||
1. In steady state the pipeline ranks chunks by IMU+VO geometric prior and skips the DINOv2 forward; DINOv2 runs only on declared re-loc triggers (cold start, sharp turn, σ_xy > 50m, VO failure ≥2 frames, disconnected segment).
|
||||
2. VPR chunks cover the operating area with 600–800m ground footprint and 40–50% overlap so any frame footprint falls fully inside ≥1 chunk; FAISS holds both fine-scale (z=20) and coarse-scale (z=17/18) descriptor sets.
|
||||
3. Top-K is dynamic — K=5 stable, K=20 active-conflict, K=50 expanding-window — and the integration uses the existing `chunk_manager.py` / `gpr.py` API surface without breaking stage1 GPR contracts.
|
||||
4. `FlightRecorder` writes append-only JSONL segments to `data/fdr/{flight_id}/segment-NNNN.jsonl`, enforces configurable segment + total storage byte limits, and exposes `health ∈ {ok, degraded, critical}`.
|
||||
5. State transitions, anchor accept/reject decisions, MAVLink sends, and pipeline errors are all recorded as FDR events; AC-NEW-3 forensic thumbnails fire at ≤0.1Hz on tile-generation failures within the FDR size budget.
|
||||
**Plans**: TBD
|
||||
|
||||
### Phase 6: Docker SITL Harness + CI
|
||||
**Goal**: The full pipeline can be tested in a reproducible Docker environment with ArduPilot SITL, camera replay, and a tile server mock — and CI runs this on every commit
|
||||
**Depends on**: Phase 5 (all components must be wired before integration testing is meaningful)
|
||||
**Requirements**: TEST-01, TEST-02
|
||||
### Phase 5: MAVLink Source-Aware Output & Spoofing/Blackout Handling
|
||||
|
||||
**Goal**: The MAVLink output the flight controller actually sees carries source provenance and reacts correctly to GPS spoofing and visual blackout, with the dual-channel ODOMETRY path scaffolded but disabled.
|
||||
**Depends on**: Phase 4 (needs SAFE source labels, FDR audit channel, and VPR triggers to drive blackout/promotion semantics).
|
||||
**Requirements**: MAVOUT-01, MAVOUT-02, MAVOUT-03, MAVOUT-04
|
||||
**Success Criteria** (what must be TRUE):
|
||||
1. `docker compose up` starts ArduPilot SITL, the GPS-denied service, a camera-replay container, and a satellite tile server mock — all communicate over MAVLink and HTTP
|
||||
2. CI pipeline runs on x86 using OpenCV ORB stub and MockInferenceEngine; all 85+ unit tests pass with no manual steps
|
||||
3. MAVLink GPS_INPUT messages are captured in SITL logs and show 5-10Hz output rate during camera replay
|
||||
4. Tracking loss scenario (simulated by replaying frames with no overlap) triggers RECOVERY state and sends re-localization request
|
||||
1. Every `GPS_INPUT` message carries `source_label`, `anchor_age_ms`, and `covariance_semimajor_m` propagated from the corresponding `PositionEstimate` (mapped into `horiz_accuracy` and a custom STATUSTEXT for label/age).
|
||||
2. When real-GPS health rolling average drops below threshold, the system promotes its own estimate to FC primary within <3s and emits a `STATUSTEXT` on every promotion/demotion.
|
||||
3. When the camera produces no usable signal, the pipeline switches to `dead_reckoned` within ≤1 processed frame OR ≤400ms and emits `VISUAL_BLACKOUT_IMU_ONLY` STATUSTEXT at 1–2Hz until imagery returns.
|
||||
4. The `ODOMETRY` emitter exists in code but is disabled by `config.mavlink.odometry_enabled=false` in stage 2, and an integration test asserts ODOMETRY is intentionally absent on the wire.
|
||||
**Plans**: TBD
|
||||
|
||||
### Phase 7: Accuracy Validation
|
||||
**Goal**: The system demonstrably meets the navigation accuracy acceptance criteria on the 60-frame test dataset, and all 21 blackbox test scenarios are implemented as runnable tests
|
||||
**Depends on**: Phase 6 (SITL harness is required for the blackbox test scenarios)
|
||||
**Requirements**: TEST-03, TEST-04, TEST-05
|
||||
### Phase 6: Real-Flight Fixture (Azaion 10.05.2026) + CLI + Per-Env Docker
|
||||
|
||||
**Goal**: The whole stack is exercised end-to-end against real flight data, an operator-facing CLI replays flights and runs AC benchmarks, and per-environment Docker images close the deployment loop.
|
||||
**Depends on**: Phase 5 (final phase — exercises ARCH + AC + SAFE + VERIFY + VPR + FDR + MAVOUT against the Azaion fixture).
|
||||
**Requirements**: FIXTURE-01, FIXTURE-02, FIXTURE-03, FIXTURE-04, FIXTURE-05, FIXTURE-06, FIXTURE-07, OBS-02, OBS-03
|
||||
**Success Criteria** (what must be TRUE):
|
||||
1. Running against AD000001–AD000060.jpg with coordinates.csv ground truth: 80% of frames within 50m error and 60% of frames within 20m error
|
||||
2. Maximum cumulative VO drift between satellite corrections is less than 100m across any segment in the test dataset
|
||||
3. End-to-end latency per frame (camera capture to GPS_INPUT) is under 400ms on Jetson, with a breakdown report per pipeline stage
|
||||
4. All 21 blackbox test scenarios (FT-P-01 to FT-P-14, FT-N-01 to FT-N-07) run as pytest tests against the SITL harness and produce a pass/fail report
|
||||
1. `tests/integration/azaion_flight/` runs against `Data/Azaion/10.05.2026/` (tlog + cropped EO video + MAVLink CSV) and is documented in `_docs/00_problem/fixtures.md` with ground-truth references and known limitations.
|
||||
2. `scripts/prep_azaion_fixture.py` produces HUD-stripped EO frames at 0.7 fps, an IMU/GPS/ATTITUDE CSV from the tlog, and a timestamp-aligned manifest.
|
||||
3. MAVLink replay decodes every `GLOBAL_POSITION_INT` / `RAW_IMU` / `ATTITUDE` message without error; ESKF replay on the real IMU samples produces no NaN/Inf and shows bounded covariance growth; ORB-SLAM3 VO smoke test achieves ≥30% frame registration on the cropped EO frames.
|
||||
4. The GPS-denial simulation masks `GPS_RAW_INT` for t∈[180s, 280s] and the pipeline correctly switches to `vo_extrapolated` and back to `satellite_anchored` when GPS returns.
|
||||
5. `gps_denied` typer CLI exposes `replay --tlog ... --video ...`, `benchmark --scenario ...`, and `bench-ac AC-1.1`; `Dockerfile.x86_dev` and `Dockerfile.jetson` (multi-stage with TRT engine prebuild step) build green and run the replay end-to-end on their respective platforms.
|
||||
**Plans**: TBD
|
||||
|
||||
## Progress
|
||||
|
||||
| Phase | Plans Complete | Status | Completed |
|
||||
|-------|----------------|--------|-----------|
|
||||
| 1. ESKF Core | 0/3 | Planned | - |
|
||||
| 2. Visual Odometry | 0/TBD | Not started | - |
|
||||
| 3. Satellite Matching + GPR | 0/TBD | Not started | - |
|
||||
| 4. MAVLink I/O | 0/TBD | Not started | - |
|
||||
| 5. End-to-End Pipeline Wiring | 0/TBD | Not started | - |
|
||||
| 6. Docker SITL Harness + CI | 0/TBD | Not started | - |
|
||||
| 7. Accuracy Validation | 0/TBD | Not started | - |
|
||||
| 1. Hexagonal Refactor & Composition Root | 0/0 | Not started | - |
|
||||
| 2. Acceptance Criteria + Test Taxonomy + Observability Spine | 0/0 | Not started | - |
|
||||
| 3. Safety Anchor State Machine & Geometry-Gated Verifier | 0/0 | Not started | - |
|
||||
| 4. Conditional Multi-Scale VPR + Flight Data Recorder | 0/0 | Not started | - |
|
||||
| 5. MAVLink Source-Aware Output & Spoofing/Blackout Handling | 0/0 | Not started | - |
|
||||
| 6. Real-Flight Fixture (Azaion 10.05.2026) + CLI + Per-Env Docker | 0/0 | Not started | - |
|
||||
|
||||
## Coverage Summary
|
||||
|
||||
| Category | Count | Phase |
|
||||
|----------|-------|-------|
|
||||
| ARCH | 7 | Phase 1 |
|
||||
| AC | 6 | Phase 2 |
|
||||
| TEST | 3 | Phase 2 |
|
||||
| OBS-01 | 1 | Phase 2 |
|
||||
| SAFE | 6 | Phase 3 |
|
||||
| VERIFY | 5 | Phase 3 |
|
||||
| VPR | 5 | Phase 4 |
|
||||
| FDR | 6 | Phase 4 |
|
||||
| MAVOUT | 4 | Phase 5 |
|
||||
| FIXTURE | 7 | Phase 6 |
|
||||
| OBS-02, OBS-03 | 2 | Phase 6 |
|
||||
| **Total** | **52** | **6 phases** |
|
||||
|
||||
100% of Stage 2 requirements mapped; no orphans; no duplicates.
|
||||
|
||||
+64
-37
@@ -1,71 +1,98 @@
|
||||
---
|
||||
gsd_state_version: 1.0
|
||||
milestone: v1.0
|
||||
milestone_name: milestone
|
||||
status: Phase 1 complete
|
||||
last_updated: "2026-04-01T21:05:00Z"
|
||||
milestone: v2.0
|
||||
milestone_name: Stage 2 — Hexagonal architecture + try02 idea integration + real-flight fixture
|
||||
status: phase_1_complete
|
||||
last_updated: "2026-05-11T00:00:00Z"
|
||||
last_activity: 2026-05-11 — Phase 1 complete; 01-08 composition root + YAML config shipped; 216/216 tests green
|
||||
progress:
|
||||
total_phases: 7
|
||||
total_phases: 6
|
||||
completed_phases: 1
|
||||
total_plans: 3
|
||||
completed_plans: 3
|
||||
total_plans: 8
|
||||
completed_plans: 8
|
||||
percent: 100
|
||||
---
|
||||
|
||||
# Project State
|
||||
|
||||
## Project Reference
|
||||
|
||||
See: .planning/PROJECT.md (updated 2026-04-01)
|
||||
See: .planning/PROJECT.md (updated 2026-05-10)
|
||||
|
||||
**Core value:** Flight controller must receive valid MAVLink GPS_INPUT at 5-10Hz with position accuracy ≤50m for 80% of frames — without this, the UAV cannot navigate in GPS-denied airspace.
|
||||
**Current focus:** Phase 2 — Visual Odometry Pipeline
|
||||
**Current focus:** Stage 2 / Phase 1 — Hexagonal Refactor & Composition Root
|
||||
|
||||
## Current Phase
|
||||
|
||||
**Phase:** 1 — ESKF Core (✓ Complete)
|
||||
**Next action:** Run `/gsd:plan-phase 2` to plan Phase 2 (Visual Odometry Pipeline)
|
||||
**Phase:** 1 — Hexagonal Refactor & Composition Root (COMPLETE)
|
||||
**Next phase:** Phase 2 — Acceptance Criteria + Test Taxonomy + Observability Spine
|
||||
|
||||
## Roadmap Summary
|
||||
|
||||
### Stage 1 (v1.0 — archived under `.planning/archive/v1.0/`)
|
||||
|
||||
Treated as MVP starting capital, not active backlog. ESKF + cuVSLAM/ORB VO + GPR + MAVLink + 195 passing tests + 8 SITL skipped. Refactoring is allowed and expected.
|
||||
|
||||
### Stage 2 (v2.0 — current iteration, Phases 1–6)
|
||||
|
||||
| Phase | Name | Status |
|
||||
|-------|------|--------|
|
||||
| 1 | ESKF Core | ✓ Complete |
|
||||
| 2 | Visual Odometry Pipeline | Pending |
|
||||
| 3 | Satellite Matching + GPR | Pending |
|
||||
| 4 | MAVLink I/O | Pending |
|
||||
| 5 | End-to-End Pipeline Wiring | Pending |
|
||||
| 6 | Docker SITL Harness + CI | Pending |
|
||||
| 7 | Accuracy Validation | Pending |
|
||||
| 1 | Hexagonal Refactor & Composition Root | **Complete** (8/8 plans, 216 tests green) |
|
||||
| 2 | Acceptance Criteria + Test Taxonomy + Observability Spine | Pending |
|
||||
| 3 | Safety Anchor State Machine & Geometry-Gated Verifier | Pending |
|
||||
| 4 | Conditional Multi-Scale VPR + Flight Data Recorder | Pending |
|
||||
| 5 | MAVLink Source-Aware Output & Spoofing/Blackout Handling | Pending |
|
||||
| 6 | Real-Flight Fixture (Azaion 10.05.2026) + CLI + Per-Env Docker | Pending |
|
||||
|
||||
## Key Files
|
||||
|
||||
- `.planning/PROJECT.md` — project context and requirements
|
||||
- `.planning/REQUIREMENTS.md` — 36 v1 requirements with traceability
|
||||
- `.planning/ROADMAP.md` — 7-phase execution plan
|
||||
- `.planning/PROJECT.md` — Stage 2 project context
|
||||
- `.planning/REQUIREMENTS.md` — 52 Stage 2 requirements with traceability
|
||||
- `.planning/ROADMAP.md` — Stage 2 roadmap, 6 phases
|
||||
- `.planning/archive/v1.0/` — Stage 1 historical record (PROJECT/REQUIREMENTS/ROADMAP/phases)
|
||||
- `.planning/codebase/` — codebase map (ARCHITECTURE, CONCERNS, STACK, etc.)
|
||||
- `_docs/01_solution/solution.md` — authoritative architecture spec
|
||||
- `_docs/00_problem/acceptance_criteria.md` — 43 test scenarios
|
||||
- `_docs/00_problem/acceptance_criteria.md` — to be rewritten with formal AC-1.x (Phase 2)
|
||||
|
||||
## Session Notes
|
||||
|
||||
- Initialized 2026-04-01
|
||||
- Brownfield: scaffold exists (~2800 lines), critical algorithms missing (ESKF, MAVLink, real TRT/cuVSLAM)
|
||||
- cuVSLAM + TRT available only on Jetson; dev/CI uses OpenCV ORB stub + MockInferenceEngine
|
||||
- Pipeline direction: top-down (API → ESKF → VO → satellite → GPS_INPUT)
|
||||
- 2026-04-01 — Project initialized; Stage 1 brownfield scaffold (~2800 lines)
|
||||
- Stage 1 complete — 195 passing + 8 SITL skipped tests, all 7 phases shipped, archived to `.planning/archive/v1.0/`
|
||||
- 2026-05-10 — Stage 2 opened as independent iteration with own phase numbering (1–6); 52 requirements drafted
|
||||
- 2026-05-10 — Stage 2 ROADMAP.md created; 100% requirement coverage; traceability populated in REQUIREMENTS.md
|
||||
- Stage 2 strategy: refactor stage1 working code to hexagonal layout, re-implement try02 concepts (NOT layout details), formalize AC, add Azaion real-flight fixture
|
||||
- Code from stage1 is MVP — refactoring is allowed and expected; no regression in 195 stage1 tests is the floor
|
||||
- 2026-05-11 — Phase 1 complete: Plans 01-01 through 01-08 executed; 216/216 tests passing; ARCH-01..07 all satisfied
|
||||
|
||||
## Phase 1 Execution Summary (2026-04-01)
|
||||
## Stage 2 Phase Dependency Order
|
||||
|
||||
**Status:** ✓ Complete — All 3 plans executed, 35 tests passing
|
||||
```
|
||||
Phase 1 (ARCH refactor — Protocol surfaces stabilize first)
|
||||
↓
|
||||
Phase 2 (AC + TEST taxonomy + structlog spine)
|
||||
↓
|
||||
Phase 3 (SAFE state machine + VERIFY anchor gates)
|
||||
↓
|
||||
Phase 4 (Conditional VPR + FDR — needs trigger semantics from SAFE)
|
||||
↓
|
||||
Phase 5 (MAVOUT — source labels + spoofing + blackout — needs SAFE labels + FDR audit)
|
||||
↓
|
||||
Phase 6 (FIXTURE — Azaion real-flight + CLI + per-env Docker — exercises everything end-to-end)
|
||||
```
|
||||
|
||||
**Deliverables:**
|
||||
- `src/gps_denied/schemas/eskf.py` (68 lines) — ESKF data contracts (ConfidenceTier, ESKFState, ESKFConfig, IMUMeasurement)
|
||||
- `src/gps_denied/core/eskf.py` (359 lines) — 15-state ESKF with IMU prediction, VO/satellite updates, confidence tiers
|
||||
- `src/gps_denied/core/coordinates.py` (176 lines added) — Real K-matrix projection, ray-ground intersection, gps_to_pixel inverse
|
||||
- `tests/test_eskf.py` (290 lines) — 18 ESKF unit tests
|
||||
- `tests/test_coordinates.py` (+200 lines) — 17 coordinate chain tests
|
||||
## Current Position
|
||||
|
||||
**Requirements Covered:** ESKF-01 through ESKF-06 (all 6 Phase 1 requirements)
|
||||
Phase: 1 — Hexagonal Refactor & Composition Root
|
||||
Plan: 08 (COMPLETE — final plan of phase)
|
||||
Status: Phase 1 complete — all 8 plans executed, 216/216 tests green, ARCH-01..07 satisfied
|
||||
Last activity: 2026-05-11 — Plan 01-08 complete; build_pipeline factory + RuntimeConfig + YAML config shipped
|
||||
|
||||
**Commits:** 4 total (schemas, core ESKF, coordinates, tests, summaries)
|
||||
## Key Decisions (Phase 1)
|
||||
|
||||
**Verification:** pytest 35/35 passing (100% success)
|
||||
- ARCH-01: components/{vio,satellite_matcher,gpr,mavlink_io,anchor_verifier,safety_state,flight_recorder,coordinate_transforms}/ created with protocol.py + impls
|
||||
- ARCH-02: hot_types migration deferred to Phase 2 (Pydantic retained for 216-test stability)
|
||||
- ARCH-03: pipeline/composition.py build_pipeline(env) as explicit DI root
|
||||
- ARCH-04: core/ math files retained as single files (eskf, factor_graph, coordinates, chunk_manager, recovery, rotation)
|
||||
- ARCH-05: typing.Protocol throughout; orchestrator.py has zero concrete adapter imports
|
||||
- ARCH-06: config/{jetson,x86_dev,ci,sitl}.yaml + RuntimeConfig.env + YamlConfigSettingsSource
|
||||
- ARCH-07: 216 passed / 8 skipped / 0 failed (baseline was 216+8 skipped)
|
||||
|
||||
Reference in New Issue
Block a user