mirror of
https://github.com/azaion/gps-denied-onboard.git
synced 2026-06-23 06:21:14 +00:00
[AZ-263] Bootstrap: repo skeleton + Docker + CI + Alembic + Tier-1 tests
Implements the AZ-263 / E-BOOT initial structure task:
- Python src/-layout package `gps_denied_onboard/` with per-component
interface stubs (14 components), type-only DTOs under `_types/`,
shared helpers under `helpers/` (R14 LightGlue ownership), structured
JSON logging, runtime composition root with env-var fail-fast gate,
healthcheck module shared by Docker and CI smoke.
- CMake top-level + `cmake/{build_options,dependencies,strategies}.cmake`
with the BUILD_* per-binary flags (ADR-002) and pinned external git
refs for OKVIS2 / VINS-Mono / GTSAM / FAISS / OpenCV >=4.12.0.
- Three Dockerfiles (companion-tier1, operator-tooling,
mock-suite-sat-service) + two compose files (dev + Tier-1 test).
- Four GitHub Actions workflows: ci.yml (lint/unit/integration/dual
binary build/SBOM diff/security), ci-tier2.yml (self-hosted Jetson
AC-bound NFTs), release.yml, cve-rescan.yml.
- Two CI gate scripts: `ci/sbom_diff.py` (deployment SBOM subset +
R02 exclusion), `ci/opencv_pin_gate.py` (>=4.12.0 enforcement,
D-CROSS-CVE-1).
- Alembic-driven Postgres 16 initial migration `0001_initial.py`
mirroring satellite-provider tiles + flights + sector_classifications
+ manifests + engine_cache_entries (data_model.md s 2).
- Tier-1 test scaffolding: 95 passing unit tests covering every AC,
per-component smoke tests, structured logging JSON output check,
env-var gate check, healthcheck import check. Two CI-gated tests
(cmake configure, actionlint) skip locally with explicit reasons.
- Batch report + code review report under `_docs/03_implementation/`.
Verdict: PASS_WITH_WARNINGS (two Low findings, both informational).
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -0,0 +1 @@
|
||||
"""Component subpackage — one folder per interface-first component (ADR-009)."""
|
||||
@@ -0,0 +1,6 @@
|
||||
"""C10 Cache Provisioning component — Public API."""
|
||||
|
||||
from gps_denied_onboard._types.manifests import EngineCacheEntry, Manifest
|
||||
from gps_denied_onboard.components.c10_provisioning.interface import CacheProvisioner
|
||||
|
||||
__all__ = ["CacheProvisioner", "EngineCacheEntry", "Manifest"]
|
||||
@@ -0,0 +1,18 @@
|
||||
"""C10 `CacheProvisioner` Protocol.
|
||||
|
||||
Concrete impl: engine compile + descriptors + manifest + content-hash gate. See
|
||||
`_docs/02_document/components/11_c10_provisioning/`.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from pathlib import Path
|
||||
from typing import Protocol
|
||||
|
||||
from gps_denied_onboard._types.manifests import Manifest
|
||||
|
||||
|
||||
class CacheProvisioner(Protocol):
|
||||
"""Pre-flight cache provisioning (engine compile + descriptor batch + manifest)."""
|
||||
|
||||
def provision(self, flight_id: str, output_root: Path) -> Manifest: ...
|
||||
@@ -0,0 +1,8 @@
|
||||
"""C11 Tile Manager component — Public API."""
|
||||
|
||||
from gps_denied_onboard.components.c11_tile_manager.interface import (
|
||||
TileDownloader,
|
||||
TileUploader,
|
||||
)
|
||||
|
||||
__all__ = ["TileDownloader", "TileUploader"]
|
||||
@@ -0,0 +1,27 @@
|
||||
"""C11 `TileDownloader` + `TileUploader` Protocols.
|
||||
|
||||
Operator-side ONLY — excluded from airborne via CMake (`BUILD_C11_TILE_MANAGER=OFF`).
|
||||
See `_docs/02_document/components/12_c11_tilemanager/`.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from collections.abc import Iterable
|
||||
from pathlib import Path
|
||||
from typing import Protocol
|
||||
|
||||
from gps_denied_onboard._types.tile import TileRecord
|
||||
|
||||
|
||||
class TileDownloader(Protocol):
|
||||
"""Pre-flight tile download from `satellite-provider`."""
|
||||
|
||||
def download(
|
||||
self, lat_lon_box: tuple[float, float, float, float], zoom: int, output_root: Path
|
||||
) -> Iterable[TileRecord]: ...
|
||||
|
||||
|
||||
class TileUploader(Protocol):
|
||||
"""Post-landing batch upload to the `satellite-provider` ingest endpoint (D-PROJ-2)."""
|
||||
|
||||
def upload(self, tiles: Iterable[TileRecord], flight_id: str) -> None: ...
|
||||
@@ -0,0 +1,8 @@
|
||||
"""C12 Operator Pre-flight Tooling component — Public API."""
|
||||
|
||||
from gps_denied_onboard.components.c12_operator_tooling.interface import (
|
||||
CacheBuildWorkflow,
|
||||
OperatorReLocService,
|
||||
)
|
||||
|
||||
__all__ = ["CacheBuildWorkflow", "OperatorReLocService"]
|
||||
@@ -0,0 +1,21 @@
|
||||
"""C12 `CacheBuildWorkflow` + `OperatorReLocService` Protocols.
|
||||
|
||||
See `_docs/02_document/components/13_c12_operator_tooling/`.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from pathlib import Path
|
||||
from typing import Protocol
|
||||
|
||||
|
||||
class CacheBuildWorkflow(Protocol):
|
||||
"""Operator CLI workflow that orchestrates C11 download → C10 provisioning."""
|
||||
|
||||
def run(self, flight_id: str, output_root: Path) -> None: ...
|
||||
|
||||
|
||||
class OperatorReLocService(Protocol):
|
||||
"""Operator-side re-localization request service (GUI deferred per epic)."""
|
||||
|
||||
def request_relocalization(self, flight_id: str, hint: dict) -> None: ...
|
||||
@@ -0,0 +1,5 @@
|
||||
"""C13 FDR Writer component — Public API."""
|
||||
|
||||
from gps_denied_onboard.components.c13_fdr.interface import FdrWriter
|
||||
|
||||
__all__ = ["FdrWriter"]
|
||||
@@ -0,0 +1,21 @@
|
||||
"""C13 `FdrWriter` Protocol (consumer side).
|
||||
|
||||
Producer-side `FdrClient` lives in `gps_denied_onboard.fdr_client` (cross-cutting,
|
||||
AZ-247); this consumer-side writer is owned by AZ-248 / E-C13.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Protocol
|
||||
|
||||
from gps_denied_onboard.fdr_client.records import FdrRecord
|
||||
|
||||
|
||||
class FdrWriter(Protocol):
|
||||
"""FDR consumer: writer thread + segment rotation + ≤64 GB capacity cap."""
|
||||
|
||||
def start(self) -> None: ...
|
||||
|
||||
def stop(self) -> None: ...
|
||||
|
||||
def consume(self, record: FdrRecord) -> None: ...
|
||||
@@ -0,0 +1,6 @@
|
||||
"""C1 VIO component — Public API."""
|
||||
|
||||
from gps_denied_onboard._types.vio import VioOutput
|
||||
from gps_denied_onboard.components.c1_vio.interface import VioStrategy
|
||||
|
||||
__all__ = ["VioOutput", "VioStrategy"]
|
||||
@@ -0,0 +1,4 @@
|
||||
"""pybind11 wrappers for `cpp/okvis2/`, `cpp/vins_mono/`, `cpp/klt_ransac/`.
|
||||
|
||||
Placeholders shipped by AZ-263; real wrappers land with the concrete strategies.
|
||||
"""
|
||||
@@ -0,0 +1,20 @@
|
||||
"""C1 `VioStrategy` Protocol.
|
||||
|
||||
Concrete strategies: OKVIS2 (default), VINS-Mono (research-only), KLT/RANSAC
|
||||
(mandatory simple baseline). See `_docs/02_document/components/01_c1_vio/`.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Protocol
|
||||
|
||||
from gps_denied_onboard._types.nav import ImuWindow, NavCameraFrame
|
||||
from gps_denied_onboard._types.vio import VioOutput
|
||||
|
||||
|
||||
class VioStrategy(Protocol):
|
||||
"""Visual-Inertial-Odometry strategy."""
|
||||
|
||||
def step(self, frame: NavCameraFrame, imu: ImuWindow) -> VioOutput:
|
||||
"""Process a single nav-camera frame + IMU window and return a VIO update."""
|
||||
...
|
||||
@@ -0,0 +1,6 @@
|
||||
"""C2.5 Rerank component — Public API."""
|
||||
|
||||
from gps_denied_onboard._types.vpr import RerankResult
|
||||
from gps_denied_onboard.components.c2_5_rerank.interface import RerankStrategy
|
||||
|
||||
__all__ = ["RerankResult", "RerankStrategy"]
|
||||
@@ -0,0 +1,17 @@
|
||||
"""C2.5 `RerankStrategy` Protocol.
|
||||
|
||||
Default: `InlierBasedReranker` (single-pair LightGlue inlier counter, K=10 → N=3).
|
||||
See `_docs/02_document/components/03_c2_5_rerank/`.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Protocol
|
||||
|
||||
from gps_denied_onboard._types.vpr import RerankResult, VprResult
|
||||
|
||||
|
||||
class RerankStrategy(Protocol):
|
||||
"""Re-rank C2's top-K candidates down to N via cross-domain match scoring."""
|
||||
|
||||
def rerank(self, vpr_result: VprResult, n_keep: int = 3) -> RerankResult: ...
|
||||
@@ -0,0 +1,6 @@
|
||||
"""C2 VPR component — Public API."""
|
||||
|
||||
from gps_denied_onboard._types.vpr import VprQuery, VprResult
|
||||
from gps_denied_onboard.components.c2_vpr.interface import VprStrategy
|
||||
|
||||
__all__ = ["VprQuery", "VprResult", "VprStrategy"]
|
||||
@@ -0,0 +1 @@
|
||||
"""Native bindings for VPR runtime — placeholder."""
|
||||
@@ -0,0 +1,17 @@
|
||||
"""C2 `VprStrategy` Protocol.
|
||||
|
||||
Concrete strategies: UltraVPR (primary), MegaLoc, MixVPR, SelaVPR, EigenPlaces,
|
||||
NetVLAD, SALAD. See `_docs/02_document/components/02_c2_vpr/`.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Protocol
|
||||
|
||||
from gps_denied_onboard._types.vpr import VprQuery, VprResult
|
||||
|
||||
|
||||
class VprStrategy(Protocol):
|
||||
"""Visual Place Recognition strategy: encode → retrieve top-K candidates."""
|
||||
|
||||
def retrieve(self, query: VprQuery, top_k: int = 10) -> VprResult: ...
|
||||
@@ -0,0 +1,5 @@
|
||||
"""C3.5 AdHoP Refinement component — Public API."""
|
||||
|
||||
from gps_denied_onboard.components.c3_5_adhop.interface import AdHoPRefinementStrategy
|
||||
|
||||
__all__ = ["AdHoPRefinementStrategy"]
|
||||
@@ -0,0 +1,16 @@
|
||||
"""C3.5 `AdHoPRefinementStrategy` Protocol.
|
||||
|
||||
Concrete impl: AdHoP refiner. See `_docs/02_document/components/05_c3_5_adhop/`.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Protocol
|
||||
|
||||
from gps_denied_onboard._types.matching import MatchResult
|
||||
|
||||
|
||||
class AdHoPRefinementStrategy(Protocol):
|
||||
"""Conditional refinement of a `MatchResult` (geometric verification + outlier purge)."""
|
||||
|
||||
def refine(self, match: MatchResult) -> MatchResult: ...
|
||||
@@ -0,0 +1,6 @@
|
||||
"""C3 Cross-Domain Matcher component — Public API."""
|
||||
|
||||
from gps_denied_onboard._types.matching import MatchResult
|
||||
from gps_denied_onboard.components.c3_matcher.interface import CrossDomainMatcher
|
||||
|
||||
__all__ = ["CrossDomainMatcher", "MatchResult"]
|
||||
@@ -0,0 +1 @@
|
||||
"""Native bindings for the cross-domain matcher runtime — placeholder."""
|
||||
@@ -0,0 +1,19 @@
|
||||
"""C3 `CrossDomainMatcher` Protocol.
|
||||
|
||||
Concrete impls: DISK+LightGlue (primary), ALIKED+LightGlue, XFeat. See
|
||||
`_docs/02_document/components/04_c3_matcher/`.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Protocol
|
||||
|
||||
from gps_denied_onboard._types.matching import MatchResult
|
||||
from gps_denied_onboard._types.nav import NavCameraFrame
|
||||
from gps_denied_onboard._types.tile import Tile
|
||||
|
||||
|
||||
class CrossDomainMatcher(Protocol):
|
||||
"""Match a nav-camera frame against a satellite tile."""
|
||||
|
||||
def match(self, frame: NavCameraFrame, tile: Tile) -> MatchResult: ...
|
||||
@@ -0,0 +1,6 @@
|
||||
"""C4 Pose Estimator component — Public API."""
|
||||
|
||||
from gps_denied_onboard._types.pose import EstimatorOutput, PoseEstimate
|
||||
from gps_denied_onboard.components.c4_pose.interface import PoseEstimator
|
||||
|
||||
__all__ = ["EstimatorOutput", "PoseEstimate", "PoseEstimator"]
|
||||
@@ -0,0 +1 @@
|
||||
"""pybind11 wrapper for `cpp/gtsam_bindings/` (READ-ONLY consumer; primary owner is c5_state)."""
|
||||
@@ -0,0 +1,18 @@
|
||||
"""C4 `PoseEstimator` Protocol.
|
||||
|
||||
Concrete impl: OpenCV `solvePnPRansac` + GTSAM Marginals. See
|
||||
`_docs/02_document/components/06_c4_pose/`.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Protocol
|
||||
|
||||
from gps_denied_onboard._types.matching import MatchResult
|
||||
from gps_denied_onboard._types.pose import PoseEstimate
|
||||
|
||||
|
||||
class PoseEstimator(Protocol):
|
||||
"""Estimate a 6-DoF pose from a verified cross-domain match."""
|
||||
|
||||
def estimate(self, match: MatchResult) -> PoseEstimate: ...
|
||||
@@ -0,0 +1,6 @@
|
||||
"""C5 State Estimator component — Public API."""
|
||||
|
||||
from gps_denied_onboard._types.pose import EstimatorHealth, EstimatorOutput
|
||||
from gps_denied_onboard.components.c5_state.interface import StateEstimator
|
||||
|
||||
__all__ = ["EstimatorHealth", "EstimatorOutput", "StateEstimator"]
|
||||
@@ -0,0 +1 @@
|
||||
"""pybind11 wrapper for `cpp/gtsam_bindings/` (primary owner; also used READ-ONLY by c4_pose)."""
|
||||
@@ -0,0 +1,23 @@
|
||||
"""C5 `StateEstimator` Protocol.
|
||||
|
||||
Concrete impls: `GtsamIsam2StateEstimator` (production-default; iSAM2 +
|
||||
IncrementalFixedLagSmoother), `EskfStateEstimator` (mandatory simple baseline).
|
||||
See `_docs/02_document/components/07_c5_state/`.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Protocol
|
||||
|
||||
from gps_denied_onboard._types.pose import EstimatorOutput, PoseEstimate
|
||||
from gps_denied_onboard._types.vio import VioOutput
|
||||
|
||||
|
||||
class StateEstimator(Protocol):
|
||||
"""Smoothed state estimator (fuses VIO + satellite anchors + IMU)."""
|
||||
|
||||
def add_vio(self, vio: VioOutput) -> None: ...
|
||||
|
||||
def add_pose_anchor(self, anchor: PoseEstimate) -> None: ...
|
||||
|
||||
def latest_output(self) -> EstimatorOutput | None: ...
|
||||
@@ -0,0 +1,21 @@
|
||||
"""C6 Tile Cache & Vector Index component — Public API."""
|
||||
|
||||
from gps_denied_onboard._types.tile import (
|
||||
SectorClassification,
|
||||
Tile,
|
||||
TileQualityMetadata,
|
||||
TileRecord,
|
||||
)
|
||||
from gps_denied_onboard.components.c6_tile_cache.interface import (
|
||||
DescriptorIndex,
|
||||
TileStore,
|
||||
)
|
||||
|
||||
__all__ = [
|
||||
"DescriptorIndex",
|
||||
"SectorClassification",
|
||||
"Tile",
|
||||
"TileQualityMetadata",
|
||||
"TileRecord",
|
||||
"TileStore",
|
||||
]
|
||||
@@ -0,0 +1 @@
|
||||
"""pybind11 wrapper for `cpp/faiss_index/` — placeholder."""
|
||||
@@ -0,0 +1,32 @@
|
||||
"""C6 `TileStore` + `DescriptorIndex` Protocols.
|
||||
|
||||
Concrete impl: `PostgresFilesystemStore` (Postgres mirror + filesystem mmap +
|
||||
FAISS HNSW). See `_docs/02_document/components/08_c6_tile_cache/`.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from collections.abc import Iterable
|
||||
from typing import Protocol
|
||||
|
||||
from gps_denied_onboard._types.tile import Tile, TileRecord
|
||||
|
||||
|
||||
class TileStore(Protocol):
|
||||
"""Tile metadata + body store (mirrors satellite-provider; cached locally)."""
|
||||
|
||||
def get(self, tile_id: str) -> Tile | None: ...
|
||||
|
||||
def query_by_lat_lon(
|
||||
self, lat: float, lon: float, zoom: int, radius_m: float
|
||||
) -> Iterable[TileRecord]: ...
|
||||
|
||||
def put(self, record: TileRecord) -> None: ...
|
||||
|
||||
|
||||
class DescriptorIndex(Protocol):
|
||||
"""Vector index over tile descriptors (FAISS HNSW concrete impl)."""
|
||||
|
||||
def add(self, tile_id: str, descriptor) -> None: ...
|
||||
|
||||
def search(self, descriptor, top_k: int) -> Iterable[tuple[str, float]]: ...
|
||||
@@ -0,0 +1,6 @@
|
||||
"""C7 Inference Runtime component — Public API."""
|
||||
|
||||
from gps_denied_onboard._types.manifests import EngineCacheEntry
|
||||
from gps_denied_onboard.components.c7_inference.interface import InferenceRuntime
|
||||
|
||||
__all__ = ["EngineCacheEntry", "InferenceRuntime"]
|
||||
@@ -0,0 +1,18 @@
|
||||
"""C7 `InferenceRuntime` Protocol.
|
||||
|
||||
Concrete impls: `TensorrtRuntime` (production-default; TensorRT 10.3),
|
||||
`OnnxTrtEpRuntime` (ONNX Runtime + TensorRT EP), `PytorchFp16Runtime` (research
|
||||
baseline). See `_docs/02_document/components/09_c7_inference/`.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Any, Protocol
|
||||
|
||||
|
||||
class InferenceRuntime(Protocol):
|
||||
"""Compiled-engine inference runtime."""
|
||||
|
||||
def infer(self, inputs: Any) -> Any: ...
|
||||
|
||||
def load(self, engine_path: str) -> None: ...
|
||||
@@ -0,0 +1,10 @@
|
||||
"""C8 FC + GCS Adapter component — Public API."""
|
||||
|
||||
from gps_denied_onboard._types.emitted import EmittedExternalPosition
|
||||
from gps_denied_onboard.components.c8_fc_adapter.interface import (
|
||||
FcAdapter,
|
||||
GcsAdapter,
|
||||
ReplaySink,
|
||||
)
|
||||
|
||||
__all__ = ["EmittedExternalPosition", "FcAdapter", "GcsAdapter", "ReplaySink"]
|
||||
@@ -0,0 +1,47 @@
|
||||
"""C8 Adapter Protocols: `FcAdapter`, `GcsAdapter`, `ReplaySink`.
|
||||
|
||||
Concrete impls: `PymavlinkArdupilotAdapter`, `Msp2InavAdapter`,
|
||||
`MavlinkGcsAdapter`, `TlogReplayFcAdapter`, `JsonlReplaySink`. See
|
||||
`_docs/02_document/components/10_c8_fc_adapter/`.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from collections.abc import Iterator
|
||||
from typing import Protocol
|
||||
|
||||
from gps_denied_onboard._types.emitted import EmittedExternalPosition
|
||||
from gps_denied_onboard._types.nav import (
|
||||
AttitudeWindow,
|
||||
FlightStateSignal,
|
||||
GpsHealth,
|
||||
ImuSample,
|
||||
)
|
||||
|
||||
|
||||
class FcAdapter(Protocol):
|
||||
"""Bidirectional flight-controller adapter."""
|
||||
|
||||
def outbound(self, position: EmittedExternalPosition) -> None: ...
|
||||
|
||||
def inbound_imu(self) -> Iterator[ImuSample]: ...
|
||||
|
||||
def inbound_attitude(self) -> Iterator[AttitudeWindow]: ...
|
||||
|
||||
def inbound_gps_health(self) -> Iterator[GpsHealth]: ...
|
||||
|
||||
def inbound_flight_state(self) -> Iterator[FlightStateSignal]: ...
|
||||
|
||||
|
||||
class GcsAdapter(Protocol):
|
||||
"""Ground-control-station adapter (telemetry + operator commands)."""
|
||||
|
||||
def emit_summary(self, summary: dict) -> None: ...
|
||||
|
||||
def operator_commands(self) -> Iterator[dict]: ...
|
||||
|
||||
|
||||
class ReplaySink(Protocol):
|
||||
"""Replay-mode estimate sink (e.g. JSONL writer)."""
|
||||
|
||||
def write(self, estimate: dict) -> None: ...
|
||||
Reference in New Issue
Block a user