# C3 — Cross-domain Matcher ## 1. High-Level Overview **Purpose**: produce 2D-3D correspondences between the current `NavCameraFrame` and the top-N=3 satellite tiles from C2.5, with RANSAC-filtered inliers and reprojection residual statistics. C3 is the **cross-domain** step (nav-camera ↔ satellite-imagery domain gap) and is the dominant compute cost in F3. **Architectural Pattern**: Strategy — `CrossDomainMatcher` interface, with concrete implementations DISK+LightGlue (D-C3-1 = (a) primary), ALIKED+LightGlue (secondary), XFeat (alternate). Selection at startup by config (ADR-001); build-time gating by `BUILD_*` flags (ADR-002); composition-root wired (ADR-009). **Cycle-1 operational reality**: the airborne binary wires C3 through `_STRATEGY_REGISTRY` + `register_airborne_strategies()` (AZ-591) on top of the `BUILD_MATCHER_*` build-flag matrix (`disk_lightglue` / `aliked_lightglue` / `xfeat` — see `runtime_root/airborne_bootstrap.py::C3_MATCHER_BUILD_FLAGS`). The `c3_matcher` airborne slot registers **only `disk_lightglue` + `aliked_lightglue`** — `xfeat` has its build-flag wired but is parked as a Tier-2 follow-up (no airborne registry slot, no airborne `_C3_MATCHER_STRATEGIES` entry). Constructor injection flows through the `pre_constructed` dict passed to `compose_root(config, pre_constructed=...)` (AZ-618 umbrella → AZ-621 c3 helpers phase + AZ-622 LightGlue runtime builder + AZ-623 c7 inference phase). The `c3_matcher` slot lists `("c3_lightglue_runtime", "c282_ransac_filter", "c7_inference")` in `AIRBORNE_REQUIRED_PRE_CONSTRUCTED_KEYS`; `clock` and `c13_fdr` are optional. Missing required keys raise `AirborneBootstrapError` at composition time, naming the consumer and missing key. **Upstream dependencies**: - C2.5 → `RerankResult` (top-N=3 candidates). - C7 InferenceRuntime → backbone forward pass. - Camera calibration artifact — for nav-frame preprocessing. - C6 TileStore — for tile pixels (handle carried in `RerankCandidate`). **Downstream consumers**: - C3.5 ConditionalRefiner (consumes `MatchResult`; passthrough or AdHoP refinement). ## 2. Internal Interfaces ### Interface: `CrossDomainMatcher` | Method | Input | Output | Async | Error Types | |--------|-------|--------|-------|-------------| | `match` | `NavCameraFrame, RerankResult, CameraCalibration` | `MatchResult` | No | `MatcherBackboneError`, `InsufficientInliersError` | | `health_snapshot` | `()` | `MatcherHealth` | No | — | **Input DTOs**: ``` NavCameraFrame: see C1 RerankResult: see C2.5 CameraCalibration: see C5 ``` **Output DTOs**: ``` MatchResult: frame_id: uuid per_candidate: list[CandidateMatchSet] (length up to N=3, drop on failure) best_candidate_idx: int — argmax(inlier_count) among per_candidate reprojection_residual_px: float — best candidate's median residual matched_at: monotonic_ns matcher_label: string — for FDR provenance CandidateMatchSet: tile_id: composite (zoomLevel, lat, lon) inlier_count: int inlier_correspondences: ndarray[I, 4, dtype=float32] — (px_query, py_query, px_tile, py_tile) ransac_outlier_count: int per_candidate_residual_px: float MatcherHealth: consecutive_low_inlier: int mean_inliers_60s: float ``` ## 3. External API Specification Not applicable. ## 4. Data Access Patterns | Query | Frequency | Hot Path | Index Needed | |-------|-----------|----------|--------------| | Tile pixel access (3 tiles per frame) | 3 Hz × 3 = 9 Hz | Yes | C6 mmap | No additional caching beyond C6. ## 5. Implementation Details **Algorithmic Complexity**: `O(N · F · F)` for the matching pass per backbone (N=3 candidates, F features per image), plus RANSAC `O(I · trials)` on inliers. Dominant cost is the backbone forward pass. **State Management**: stateless per-frame. Holds the shared LightGlue / DISK runtime handle. **Key Dependencies**: | Library | Version | Purpose | |---------|---------|---------| | DISK (Python) | upstream HEAD pinned per Plan-phase | Primary feature extractor (D-C3-1 = (a)) | | LightGlue (Python) | upstream HEAD pinned per Plan-phase | Primary matcher; replaces SuperPoint+SuperGlue (Magic Leap noncommercial) | | ALIKED (Python) | upstream HEAD pinned per Plan-phase | Secondary feature extractor | | XFeat (Python) | upstream HEAD pinned per Plan-phase | Alternate (lightweight) feature+matcher | | OpenCV | `>=4.11.0.86,<4.12` (cycle-1 relaxed pin; D-CROSS-CVE-1 deferred — see `_docs/_process_leftovers/2026-05-11_d_cross_cve_1_opencv_pin_deferred.md`) | RANSAC + reprojection residual computation (consumed via the shared `RansacFilter` helper) | | TensorRT | matches C7 | Backbone engine compilation/runtime | **Error Handling Strategy**: - `MatcherBackboneError`: backbone forward pass failed on a candidate. Candidate dropped from `per_candidate`; if all N=3 candidates fail, emit `InsufficientInliersError`. - `InsufficientInliersError`: RANSAC inlier count below configurable threshold on every candidate. C5 falls back to VIO-only with provenance label `visual_propagated`. F6 satellite re-localization may trigger if the condition persists. - Catastrophic backbone failure (engine unloadable): treated as `MatcherBackboneError` for every frame until F8 reboot recovery. ## 6. Extensions and Helpers | Helper | Purpose | Used By | |--------|---------|---------| | `LightGlueRuntime` | shared LightGlue inference handle | C2.5, C3 | | `RansacFilter` | RANSAC + reprojection residual computation thin wrapper | C3, C3.5, C4 | ## 7. Caveats & Edge Cases **Known limitations**: - The cross-domain gap (nav-camera vs satellite tile) is the hardest step in the pipeline. Backbone choice depends on the deployment camera's spectral and resolution characteristics; the current default (DISK+LightGlue) is locked per Mode B Fact #110 / D-C3-1 = (a) pending IT-12 verdict. - D-C2-12 (DINOv2-feature-based matcher) is a carryforward research item that may displace DISK in a future cycle. **Cycle-1 Tier-2 follow-up dependencies**: - XFeat — build-flag (`BUILD_MATCHER_XFEAT`) + `matcher_factory._STRATEGY_TO_BUILD_FLAG` + concrete `c3_matcher/xfeat.py` module are all in place, but `c3_matcher`'s `_C3_MATCHER_STRATEGIES` tuple in `runtime_root/airborne_bootstrap.py` registers only `disk_lightglue` + `aliked_lightglue`. Selecting `xfeat` via airborne config currently raises `StrategyNotLinkedError` from the `_STRATEGY_REGISTRY` lookup. Tier-2 follow-up: extend the airborne registration tuple + airborne Jetson validation against Derkachi-class fixtures. **Potential race conditions**: - Shared LightGlue runtime with C2.5; serial access from one ingest thread. **Performance bottlenecks**: - C3 dominates the F3 latency budget. The D-CROSS-LATENCY-1 hybrid does NOT change C3 (K=N=3 stays); it changes C4 covariance recovery. ## 8. Dependency Graph **Must be implemented after**: C2.5 (input), C7 (inference runtime). **Can be implemented in parallel with**: C1, C6 — independent paths. **Blocks**: C3.5 / C4 / C5, F3 / F6. ## 9. Logging Strategy | Log Level | When | Example | |-----------|------|---------| | ERROR | `InsufficientInliersError`; all N=3 candidates failed | `C3 zero-inlier on all candidates; frame=12345; backbone=disk_lightglue` | | WARN | reprojection residual above threshold (will trigger AdHoP at C3.5) | `C3 residual=4.2px > threshold=2.5px; frame=12345; will refine` | | INFO | Strategy ready | `C3 ready: matcher=disk_lightglue` | | DEBUG | per-candidate inlier + residual | `C3 frame=12345 candidates=[(412,1.1px),(287,1.4px),(198,2.0px)]` | **Log format**: structured JSON. **Log storage**: stdout / journald / FDR via C13 (ERROR + WARN only).