# Fact Cards — Index & Summary

> Mode A Phase 2 — engine Step 3 (Fact Extraction & Evidence Cards). Extracted from sources logged in `../01_source_registry/` (see `../01_source_registry/00_summary.md` for index). Confidence labels: ✅ High (L1 / verified source code), ⚠️ Medium (L1/L2 with caveat), ❓ Low (L3/L4 inferential).
>
> Bound to sub-questions in `../00_question_decomposition.md`. Many SQ6 facts also bind directly to the Project Constraint Matrix (`../../00_problem/acceptance_criteria.md` / `../../00_problem/restrictions.md`); per the engine's "Per-Mode API Capability Verification" rule, MAVLink/MSP messages are treated as candidate **modes** and are bound `Pass/Fail/Verify/N/A` against numbered ACs and restrictions.

This folder replaces the previous monolithic `02_fact_cards.md` (1480 lines, too large to navigate). Each category lives in its own file. Open the file matching the area you need — every fact and conclusion is preserved verbatim.

---

## Category index

| File | Sub-question / Component | Facts (count) | Scope summary |
| --- | --- | --- | --- |
| [`SQ6_fc_external_positioning.md`](SQ6_fc_external_positioning.md) | **SQ6** — ArduPilot Plane vs iNav external positioning | #1–#10 (10 facts) | MAVLink `GPS_INPUT` (232) ingestion in EKF3, iNav MSP `MSP2_SENSOR_GPS` ingestion via INAV BlackBox, covariance honesty, lane-fusion / lane-switch on (NSats, HDOP, fix_type), spoof-promotion via UBX emulation, dead-reckoning behaviour, `EK3_GPS_CHECK` bit-mask gates. Working conclusions: ArduPilot is the cooperative path, iNav requires UBX impersonation. |
| [`SQ1_existing_systems.md`](SQ1_existing_systems.md) | **SQ1** — Existing / competitor GPS-denied UAV navigation systems | #11–#20 (10 facts) | Twist Robotics OSCAR (Ukrainian peer), Auterion Artemis OS, Vantor Raptor, NGPS class systems, SPRIN-D winner, RTAB-Map / ORB-SLAM3 pruning rationale, DSMAC/TERCOM lineage, hierarchical retrieval-matching SOTA, AerialExtreMatch benchmark, DARPA FLA + USAF SBIR programs. Working conclusions: VPR-anchored hybrid pipeline is canonical. |
| [`SQ2_canonical_pipeline.md`](SQ2_canonical_pipeline.md) | **SQ2** — Canonical GPS-denied pipeline & SOTA components | #21–#27 (7 facts) | Two-stage canonical pipeline (global VPR → local alignment → PnP-RANSAC → EKF), end-to-end visual-localization rejection (poor generalization, no covariance), cross-domain sat ↔ UAV registration, hardware MVE doctrine, Top-N inlier re-rank gate. Working conclusions: VIO + VPR + Matcher + PnP + EKF is the design floor. |
| [`C1_vio.md`](C1_vio.md) | **C1** — Visual / Visual-Inertial Odometry | Candidate enumeration + decisions | VINS-Mono (BSD/permissive baseline), VINS-Fusion (GPL-3.0 alternate), OpenVINS (GPL-3.0), OKVIS2 (BSD), Kimera-VIO (BSD), DROID-SLAM (BSD non-VIO), DPVO (Apache-2.0 non-VIO), KLT+RANSAC (homemade fallback). Decisions: D-C1-1 license posture, D-C1-2 IMU rate. |
| [`C2_vpr.md`](C2_vpr.md) | **C2** — Visual Place Recognition | Candidate enumeration + decisions | MixVPR, SALAD (GPL-3.0 disqualifier), SelaVPR, NetVLAD, EigenPlaces, AnyLoc, BoQ, DINOv2-VLAD. Decisions: D-C2-1 aerial-domain training, D-C2-2 cache budget, D-C2-3 input resolution shape, D-C2-N TensorRT export gate. |
| [`C3_matchers.md`](C3_matchers.md) | **C3** — Cross-domain registration (Matchers) | Candidate enumeration + decisions | SP+LightGlue (Magic Leap noncommercial disqualifier on canonical SP), DISK+LightGlue (RECOMMENDED-PRIMARY-MITIGATION), ALIKED+LightGlue, XFeat (alternate-modern lead), SuperGlue+SuperPoint (deprecated by LightGlue authors), MASt3R (CC-BY-NC), RoMa, DKM, LoFTR. Decisions: D-C3-1 modern-competitive lead, D-C3-2 ONNX/TensorRT export path, D-C3-6 re-rank strategy. |
| [`C4_pose_estimation.md`](C4_pose_estimation.md) | **C4** — Pose estimation (PnP + RANSAC + LM) | #52–#54 (3 facts, in progress) | OpenCV `cv::solvePnPRansac` mandatory simple-baseline (Apache-2.0 throughout, 9 SolvePnPMethod enum values with 2 BROKEN, paired `solvePnPRefineLM`/`solvePnPRefineVVS`/`solvePnPGeneric`, 7 USAC RANSAC variants); OpenGV modern-competitive-lead-richer-minimal-solver (BSD-3-Clause-equivalent NOASSERTION-SPDX-detector contingent + ~3-year stale + 4 algorithm-selectable RANSAC enums [KNEIP/GAO/EPNP/GP3P] + 2 P3P variants + UPnP global-optimal + GP3P generalized-camera; NO planar-scene dedicated solver vs OpenCV's IPPE); GTSAM modern-competitive-lead-covariance-honest (BSD-3-Clause clean throughout, daily-active maintenance, **NATIVE 6×6 pose covariance via `Marginals.marginalCovariance` — only C4 candidate to satisfy AC-NEW-4 NATIVELY**, no native RANSAC, ~50-200 MB footprint, tight AC-4.1 latency margin). Decisions: D-C4-1 (carry-forward) 2D-3D-lift; D-C4-2 (NEW + UPDATED) covariance-recovery-strategy; D-C4-3 (NEW) OpenGV license-clearance-verification; D-C4-4 (NEW) OpenGV maintenance-staleness-mitigation. Subsequent candidates pending: Theia / Ceres-only (likely deferrable — D-C4 row may already have sufficient coverage). |
| [`C5_state_estimator.md`](C5_state_estimator.md) | **C5** — State estimator / sensor fusion | #88–#89 (2 facts, **batch 1 closed at 2/N 2026-05-08**) | Manual ESKF reference (Solà 2017 canonical aerial/quaternion arXiv preprint — public-domain canonical equations + project-side custom implementation under project's Apache-2.0; mandatory simple-baseline; trivial dependency footprint at ~kilobytes of NumPy/SciPy code; native 6×6 covariance via analytic Jacobian propagation per Solà §6 canonical recipe; quaternion-correct attitude integration on SO(3) via small-angle approximation in error-state; **fastest C5 candidate by an order of magnitude** at ~5-15 ms per update on Jetson CPU); GTSAM `iSAM2` + `CombinedImuFactor` (Forster et al. RSS 2015) + `PreintegratedCombinedMeasurements` + `BetweenFactorPose3` + `GenericProjectionFactorCal3DS2` + `PriorFactorPose3` + smart projection factors + `Marginals.marginalCovariance` + `gtsam_unstable.IncrementalFixedLagSmoother` modern-competitive-lead-factor-graph (clean BSD-3-Clause throughout, daily-active maintenance with last-pushed 2026-05-08T13:00:22Z = TODAY at access time, **architecturally couples with C4 Fact #54 via shared GTSAM substrate**, native 6×6 posterior covariance via `Marginals` — same NATIVE AC-NEW-4 satisfaction pathway as C4 Fact #54, IMU pre-integration via Forster et al. RSS 2015 `CombinedImuFactor` 6-key per-keyframe-pair factor with bias evolution for asynchronous IMU+camera fusion at ~100-200 Hz IMU + 3 Hz camera, ~50-200 MB footprint, incremental smoothing via iSAM2 amortizes per-frame cost, **NATIVE AC-4.5 look-back refinement** unique among C5 candidates). Decisions: D-C5-1 (NEW) reference-implementation-license-verification; D-C5-2 (NEW) long-cruise-observability-strategy; D-C5-3 (NEW) sliding-window-primitive-choice; D-C5-4 (NEW) IMU-gap-handling-strategy; D-C5-5 (NEW) factor-density-choice (recommended D-C5-5 = (c) couples C4 Fact #54 D-C4-2 = (b) with C5 Fact #89 architectural integration via shared GTSAM substrate). |
| [`C6_tile_cache_spatial_index.md`](C6_tile_cache_spatial_index.md) | **C6** — Tile cache + spatial index | #92–#93 (2 facts, **batch 1 closed at 2/N 2026-05-08**) | **Cand 1 RECOMMENDED PRIMARY**: Manual mirror of existing parent-suite `satellite-provider` pattern (verified directly via Source #92 filesystem read at /Users/obezdienie001/dev/azaion/suite/satellite-provider/) — PostgreSQL btree composite on slippy-map `(tile_zoom, tile_x, tile_y, version)` for geographic spatial-grid range queries + `bytea` descriptor blobs + app-side FAISS `IndexHNSWFlat(d, M=32)` loaded at takeoff via `faiss.read_index` for descriptor ANN + filesystem tile storage at `./tiles/{zoom}/{x}/{y}.{image_type}` slippy-map convention; clean PostgreSQL License + MIT + LGPL/MIT-Apache; trivial dependency footprint (no Postgres extensions); empirically-confirmed Postgres-on-Jetson viability per Source #97 March 2026 article (CPU cores limiting, NOT memory); ~6-54 ms per cache hit comfortably within AC-4.1 400 ms p95 budget; ~700 MB-1.5 GB total memory footprint within AC-4.2 8 GB budget. **Cand 2 DEFERRED secondary**: PostgreSQL + PostGIS 3.4 GiST on `geography(POINT,4326)` with KNN distance ordering (`<->`) + pgvector 0.7+ HNSW for descriptor ANN + same filesystem tile storage; native KNN + radius + combined-SQL capabilities are real improvements BUT 5-10× slower geographic lookup than Cand 1 + heavier dependency (~50-100 MB additional memory + ~50-200 MB additional disk install) + PostGIS GPL-2.0-or-later license-complexity (CONTINGENT REJECT under D-C1-1 = (b) BSD/permissive-only-track) + DIVERGENT from suite pattern + improvements marginal-to-negative in project's pinned 3 Hz spatial-grid query operating context. **Comparative-improvement-vs-Cand-1 verdict**: per user's session-start "significant-improvement-only" bar, no material justification to deviate from existing satellite-provider pattern. Decisions: D-C6-1 (NEW) descriptor-storage-format choice (halfvec recommended); D-C6-2 (NEW Cand-1-only) FAISS index variant choice (IndexHNSWFlat M=32 recommended); D-C6-3 (NEW Cand-1-only CROSS-COMPONENT with C10) descriptor-cache-rebuild-trigger strategy (periodic-during-C10-pre-flight recommended); D-C6-4 (NEW Cand-1-only) geographic-spatial-grid radius (dynamic recommended); D-C6-5 (NEW Cand-2-only contingent) Jetson PostGIS+pgvector co-installation Plan-phase verification (verify-on-Jetson-MVE recommended); D-C6-6 (NEW Cand-2-only contingent) pgvector descriptor-storage-type choice (halfvec recommended); D-C6-7 (NEW CROSS-COMPONENT affects parent-suite satellite-provider) cascade-changes-back-to-suite strategy (leave-unchanged recommended given Cand 1 closure verdict). |
| [`C7_inference_runtime.md`](C7_inference_runtime.md) | **C7** — On-Jetson inference runtime | #94–#96 (3 facts, **batch 1 closed at 3/N 2026-05-08**) | **Cand 1 RECOMMENDED PRIMARY**: TensorRT native — JetPack 6.2 bundled TensorRT 10.3 + `IInt8EntropyCalibrator2` + `BuilderFlag.FP16+INT8` mixed-precision + engines built directly on Jetson Orin Nano Super SM 87 (Apache-2.0 in TensorRT 10.x; ships with JetPack so zero-effort install; lowest-latency primary path; 2-3× speedup at INT8 vs FP16 per Source #102 YOLO26 benchmark; engines tied to SM 87 hardware-specific per Source #105 — must be built on deployed Jetson via D-C7-7); **Cand 2 modern-competitive-lead-cross-architecture-portability**: ONNX Runtime + TensorRT EP — `onnxruntime-gpu` via Jetson AI Lab JP6/CU126 wheel index + `TensorrtExecutionProvider` config + automatic CUDA EP / CPU EP subgraph fallback (MIT throughout; cross-architecture portability for replay/SITL on x86 dev hosts; `pip install onnxruntime-gpu` does NOT work on Jetson — needs Jetson AI Lab community wheel via D-C7-3 + numpy<2.0.0 pin via D-C7-4); **Cand 3 mandatory simple-baseline**: pure PyTorch FP16 — `torch.amp.autocast` + `model.half()` + Jetson AI Lab PyTorch 2.5 ARM64 wheel (BSD-3-Clause throughout; zero-conversion regression baseline; reference-correctness oracle for accuracy validation of TRT-built engines; standard `pip install torch` lacks CUDA on Jetson — needs Jetson AI Lab wheel via D-C7-5). **Cross-cutting precision policy** (D-C7-6 NEW CROSS-COMPONENT, affects C2+C3+C1+C7): VPR backbones (CNN-class MixVPR/EigenPlaces/NetVLAD) → INT8+FP16 mixed; ViT-class VPR (SelaVPR DINOv2-L; conditional AnyLoc/BoQ/DINOv2-VLAD) → FP16-only initially, INT8 deferred to Jetson MVE per D-C2-5; matchers (LightGlue with SP/DISK/ALIKED, XFeat, XFeat+LighterGlue) → **FP16-only — NO INT8** per Source #103 quantization-sensitivity finding (LightGlue FP8 ModelOpt collapsed match counts); learned VIO frontends → FP16-only initially. **Triton/DeepStream/CUDA-Python custom kernels considered-and-rejected** (server/video-pipeline class + out-of-budget for embedded 8 h mission) per c7_overkill_options scope choice. Decisions: D-C7-1 (NEW Cand-1-only CROSS-COMPONENT with C9) calibration-dataset-strategy (AerialVL S03 + AerialExtreMatch recommended); D-C7-2 (NEW Cand-1-only) TensorRT mixed-precision flag matrix (per-family policy per D-C7-6 recommended); D-C7-3 (NEW Cand-2-only) ORT-Jetson-wheel-index-pin (mirror to project artifact registry + cu126 recommended); D-C7-4 (NEW Cand-2-only) numpy-version-pin (`numpy<2.0.0` recommended); D-C7-5 (NEW Cand-3-only) PyTorch-Jetson-wheel-pin (PyTorch 2.5 + torchvision 0.20 recommended); D-C7-6 (NEW CROSS-COMPONENT C2+C3+C1+C7) INT8-vs-FP16-per-model-family-precision-policy (per-family policy recommended); D-C7-7 (NEW Cand-1-only CROSS-COMPONENT with C10) engine-build-on-Jetson-vs-prebuilt strategy (primary build-on-target + reference-Jetson fallback recommended); D-C7-8 (NEW Cand-1-only) `config.max_workspace_size` cap (1 GB safe default recommended); D-C7-9 (NEW Cand-1-only) TensorRT version pin within JetPack lifecycle (JetPack 6.2 + TensorRT 10.3 recommended). |
| [`C10_preflight_provisioning.md`](C10_preflight_provisioning.md) | **C10** — Pre-flight cache provisioning (CROSS-COUPLING MINIMAL scope per 2026-05-08 user choice C; only D-C6-3 + D-C7-7 confirmation pipelines researched here, operator tooling design deferred to Plan-phase) | #100–#101 (2 facts, **batch 1 closed at 2/N 2026-05-08**) | **D-C6-3 confirmation (Fact #100)**: descriptor-cache rebuild trigger + atomic-write strategy via direct `faiss.write_index`/`faiss.read_index` Python API + `python-atomicwrites` (write-temp + `fsync` + atomic rename) + content-hash (SHA-256) verification gate at takeoff load + `IO_FLAG_MMAP_IFC` mmap load with `madvise(MADV_WILLNEED)` pre-fault + manifest-hash-driven rebuild trigger; FAISS MIT + atomicwrites MIT throughout; FAISS warns "no internal integrity check, expects validated input" — MITIGATED by content-hash gate at takeoff (binds AC-NEW-7 cache-poisoning safety); rebuild-while-not-flying constraint per restrictions.md. **D-C7-7 confirmation (Fact #101)**: hybrid TensorRT engine-build orchestration — Polygraphy CLI primary for INT8-calibrating builds (`polygraphy convert --int8 --calib-cache=<path> ...` Apache-2.0 + Calibrator API replaces hand-written `IInt8EntropyCalibrator2`) + `trtexec` for fast cache-reuse rebuilds (`--fp16 --int8 --calib=<existing_cache>`) + direct `IBuilderConfig` Python API as escape hatch for unusual models (LightGlue dynamic-shape profiles); calibration cache binary-blob reuse keyed by `SHA-256(calib_corpus)` per D-C10-6; engines tied to SM 87 hardware-specific per Source #105 → must be built on deployed Jetson per D-C7-7 closure (D-C10-8 reference-Jetson-at-HQ + deployed-Jetson-copy-to-archive prebuilt-fallback venue); self-describing filename schema `<model>_sm<SM>_jp<JP>_trt<TRT>_<precision>.engine` per D-C10-7; binds AC-4.1/4.2 latency+memory budgets via D-C7-2 mixed-precision flag matrix + D-C7-1 calibration corpus closure. |
| [`MODEB_addendum.md`](MODEB_addendum.md) | **Mode B addendum** — solution_draft01 assessment (2026-05-08) | #102–#113 (12 facts) | Documentary-audit findings (Facts #102–#108): VINS-Mono BSD/GPL deliverable-formatting error (#102), AC-4.1 latency budget overrun (#103), camera calibration unspecified (#104), Suite Sat Service voting-layer contract gap (#105), `00_ac_assessment.md` BLOCKING-gate skip acknowledged (#106), AC-4.5 FC-consumption pathway scope clarification (#107), SQ2 AdHoP + Top-N re-rank sub-stage absence in solution_draft01 architecture (#108). Web-research findings (Facts #109–#113): MAVLink no-default-auth + MAVLink-2.0 message-signing per FC (#109), MegaLoc + UltraVPR D-C2-11 deferred-evaluation revision (#110), `MAV_CMD_SET_EKF_SOURCE_SET` no-deployed-GCS-implementer re-confirmation (#111), OpenCV ≥4.12.0 CVE pin (#112), XoFTR + DINOv2-features cross-modal contrarian evidence (#113). |
| [`C8_fc_adapter.md`](C8_fc_adapter.md) | **C8** — MAVLink / MSP2 FC adapter | #97–#99 (3 facts, **batch 1 closed at 3/N 2026-05-08**) | **Cand 1 RECOMMENDED PRIMARY for ArduPilot**: pymavlink → MAVLink `GPS_INPUT` (msg 232) cooperative-path; `master.mav.gps_input_send(time_usec, gps_id, ignore_flags, time_week_ms, time_week, fix_type, lat, lon, alt, hdop, vdop, vn, ve, vd, speed_accuracy, horiz_accuracy, vert_accuracy, satellites_visible, yaw)` periodic injection at 5 Hz over MAVLink (UART/USB/UDP per D-C8-1); FC-side `GPS1_TYPE=14` MAVLink + `EK3_SRC1_POSXY=3` GPS source-set drives EKF3 ingestion via `AP_GPS_MAV` (verified Source #4 SQ6 + Source #106 + Source #107); pymavlink LGPL-3.0 linkable from Apache-2.0 app per LGPL §6 (D-C8-3 mitigation). **Cand 2 RECOMMENDED PRIMARY for iNav**: `MSP2_SENSOR_GPS` (id 7939 / 0x1F03) via Python MSP V2 (YAMSPy or INAV-Toolkit `msp_v2_encode`); `mspGPSReceiveNewData()` direct passthrough (no validation gate beyond data parse); covariance fields `hPosAccuracy`/`vPosAccuracy`/`hVelAccuracy` align directly with AP `GPS_INPUT.horiz_accuracy`/`vert_accuracy`/`speed_accuracy`; YAMSPy + INAV-Toolkit MIT throughout; `USE_GPS_PROTO_MSP` enabled by default in iNav target/common.h (verified Source #111 + #112 + #113); locked SQ6 + AC-4.3 + restrictions.md transport. **Cand 3 DEFERRED secondary for iNav**: UBX impersonation via pyubx2 NAV-PVT — forging u-blox NAV-PVT frames through standard GPS pipeline; iNav-side `gpsMapFixType()` validation gate requires `flags & 0x01 = 1` (gnssFixOK) AND `fixType ∈ {2,3}` per Source #110 `gps_ublox.c` lines 215-220 + 654; pyubx2 BSD-3-Clause clean dual-use; **does NOT clear user's "significant-improvement-only" bar over Cand 2** — richer protocol surface (NAV-PVT periodic + NAV-VER startup + CFG-MSG/CFG-RATE ACK behaviour) + AC-NEW-7 forgery posture + stricter validation gate + AP-path field-name divergence outweigh pyubx2 library-maturity advantage. **Mid-batch correction**: I caught a contradiction between my own initial AskQuestion phrasing ("UBX impersonation as ONLY iNav path") and locked SQ6 + AC-4.3 + restrictions.md verdicts; user re-locked scope via `c8_inav_recovery=B` to evaluate both as parallel candidates. Decisions: D-C8-1 (NEW Cand-1-only) pymavlink connection-string transport choice (env-driven default-UART recommended); D-C8-2 (NEW Cand-1-only CROSS-COMPONENT with AC-NEW-2) `MAV_CMD_SET_EKF_SOURCE_SET` companion-driven switch ownership pattern (companion publishes to source-set 2 + auto-switches FC recommended); D-C8-3 (NEW Cand-1-only) pymavlink LGPL-3.0 license-posture verification (bundle-unmodified-with-version-pin recommended); D-C8-4 (NEW Cand-2-only) Python MSP V2 implementation choice (YAMSPy primary + thin custom encoder fallback recommended); D-C8-5 (NEW Cand-2-only) MSP2_SENSOR_GPS injection rate (5 Hz periodic recommended); D-C8-6 (NEW Cand-3-only contingent) UBX-version-advertisement strategy (advertise version ≥ 15.0 recommended); D-C8-7 (NEW Cand-3-only contingent CROSS-COMPONENT with AC-NEW-7) AC-NEW-7 audit-trail posture for UBX impersonation (explicit FDR audit entry recommended); D-C8-8 (NEW CROSS-COMPONENT C5+C8) covariance-honesty cross-FC enforcement strategy (per-FC unit conversion recommended via 95% confidence ellipse semi-major axis from C5 GTSAM `Marginals.marginalCovariance`). |

**Cross-cutting consumers** (do not duplicate facts here, just point in):
- The Component Fit Matrix (`../06_component_fit_matrix/`) cites every fact here by `Fact #N` or by candidate row.

---

## Confidence-label legend

| Label | Meaning | Source class |
| --- | --- | --- |
| ✅ High | Source code / official spec / canonical repo verified | L1 (primary code, official docs, published benchmarks) |
| ⚠️ Medium | Authoritative but with stated caveat (out-of-date version, partial coverage, single-source confirmation) | L1 / L2 |
| ❓ Low | Inferential or extrapolated (vendor blog, secondary commentary, candidate not yet runtime-verified on target hardware) | L3 / L4 |

Whenever a candidate is marked **Selected** in `../06_component_fit_matrix/`, its row depends on at least one ✅ High fact in the corresponding C-file plus a `context7` per-mode API capability verification.

---

## Editing rules

1. Add new facts only inside their owning category file. Cross-reference siblings; do not duplicate text.
2. Each fact keeps the existing schema — `### Fact #N — title`, `**Statement**`, `**Source**`, `**Phase**`, `**Confidence**`, `**Sub-Question Binding**`, `**Implication**`.
3. When extending C-rows, also touch the corresponding component file in `../06_component_fit_matrix/` so the matrix stays in sync.
4. Working conclusions and decisions (`D-Cx-y`) live at the bottom of their owning file, not here.
