Item 2 (C1) + item 3 batch 1 of ~5 (C2 VPR, C2.5 Rerank, C3 Matcher) of the cycle-1 component-description reconciliation called out in ripple_log_cycle1.md. For each touched description.md: - Add a "Cycle-1 operational reality" paragraph in section 1 that names the _STRATEGY_REGISTRY + register_airborne_strategies() runtime gate (AZ-591), the pre_constructed dict path through compose_root (AZ-618 umbrella), the per-component AIRBORNE_REQUIRED_PRE_CONSTRUCTED_KEYS row, and any cycle-1 strategy-default vs documented-primary disambiguation (net_vlad as the C2 default; xfeat parked from the C3 airborne registry). - Relax the OpenCV row in section 5 Key Dependencies to the D-CROSS-CVE-1 cycle-1 pin (>=4.11.0.86,<4.12) wherever the component imports cv2 (C2 preprocessors, C2.5 ORB placeholder, C3 RANSAC + reprojection). - Add a "Cycle-1 Tier-2 follow-up dependencies" subsection in section 7 only for components with a strategy module that is built but parked from the airborne registry (C3 xfeat). Refresh ripple_log_cycle1.md follow-up ordering with per-batch progress + extracted batch pattern so the next batch session has a self-contained recipe. Bump _autodev_state.md sub_step.detail to reflect batch 1 completion (10 components + 8 helpers + tests/ remain). Co-authored-by: Cursor <cursoragent@cursor.com>
7.7 KiB
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 fromper_candidate; if all N=3 candidates fail, emitInsufficientInliersError.InsufficientInliersError: RANSAC inlier count below configurable threshold on every candidate. C5 falls back to VIO-only with provenance labelvisual_propagated. F6 satellite re-localization may trigger if the condition persists.- Catastrophic backbone failure (engine unloadable): treated as
MatcherBackboneErrorfor 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+ concretec3_matcher/xfeat.pymodule are all in place, butc3_matcher's_C3_MATCHER_STRATEGIEStuple inruntime_root/airborne_bootstrap.pyregisters onlydisk_lightglue+aliked_lightglue. Selectingxfeatvia airborne config currently raisesStrategyNotLinkedErrorfrom the_STRATEGY_REGISTRYlookup. 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).