mirror of
https://github.com/azaion/gps-denied-onboard.git
synced 2026-06-21 09:01:14 +00:00
feat(01-02): add migration-target Protocols for vio/gpr/satellite_matcher/mavlink_io/coordinate_transforms (ARCH-05)
- VisualOdometry mirrors ISequentialVisualOdometry (4 methods) - GlobalPlaceRecognition mirrors IGlobalPlaceRecognition (7 methods) - SatelliteTileLoader mirrors SatelliteDataManager public API (11 methods) - MetricRefiner mirrors IMetricRefinement (6 methods) - MAVLinkBridgeProtocol mirrors MAVLinkBridge public API (8 methods) - CoordinateTransformsProtocol mirrors CoordinateTransformer (9 methods) - All Protocols runtime_checkable; backwards-compat I-prefixed aliases exposed for vio/gpr/metric (deprecated in Phase 2) - Pure-additive: zero existing files touched - isinstance check confirms SatelliteDataManager and CoordinateTransformer already satisfy the new Protocols structurally
This commit is contained in:
@@ -0,0 +1,68 @@
|
|||||||
|
"""Protocol surface for the coordinate_transforms component (ARCH-05).
|
||||||
|
|
||||||
|
Per ARCH-04 the implementation stays in ``core/coordinates.py`` —
|
||||||
|
this directory holds ONLY the Protocol so the ARCH-01 directory
|
||||||
|
inventory is complete. Method signatures mirror the concrete
|
||||||
|
``CoordinateTransformer`` public surface.
|
||||||
|
"""
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from typing import Protocol, runtime_checkable
|
||||||
|
|
||||||
|
import numpy as np
|
||||||
|
|
||||||
|
from gps_denied.schemas import CameraParameters, GPSPoint
|
||||||
|
|
||||||
|
|
||||||
|
@runtime_checkable
|
||||||
|
class CoordinateTransformsProtocol(Protocol):
|
||||||
|
"""ENU origin management + WGS84⇄ENU⇄pixel transforms."""
|
||||||
|
|
||||||
|
def set_enu_origin(self, flight_id: str, origin_gps: GPSPoint) -> None: ...
|
||||||
|
|
||||||
|
def get_enu_origin(self, flight_id: str) -> GPSPoint: ...
|
||||||
|
|
||||||
|
def gps_to_enu(
|
||||||
|
self, flight_id: str, gps: GPSPoint
|
||||||
|
) -> tuple[float, float, float]: ...
|
||||||
|
|
||||||
|
def enu_to_gps(
|
||||||
|
self, flight_id: str, enu: tuple[float, float, float]
|
||||||
|
) -> GPSPoint: ...
|
||||||
|
|
||||||
|
def pixel_to_gps(
|
||||||
|
self,
|
||||||
|
flight_id: str,
|
||||||
|
pixel: tuple[float, float],
|
||||||
|
frame_pose: dict,
|
||||||
|
camera_params: CameraParameters,
|
||||||
|
altitude: float,
|
||||||
|
quaternion: np.ndarray | None = None,
|
||||||
|
) -> GPSPoint: ...
|
||||||
|
|
||||||
|
def gps_to_pixel(
|
||||||
|
self,
|
||||||
|
flight_id: str,
|
||||||
|
gps: GPSPoint,
|
||||||
|
frame_pose: dict,
|
||||||
|
camera_params: CameraParameters,
|
||||||
|
altitude: float,
|
||||||
|
quaternion: np.ndarray | None = None,
|
||||||
|
) -> tuple[float, float]: ...
|
||||||
|
|
||||||
|
def image_object_to_gps(
|
||||||
|
self,
|
||||||
|
flight_id: str,
|
||||||
|
frame_id: int,
|
||||||
|
object_pixel: tuple[float, float],
|
||||||
|
frame_pose: dict | None = None,
|
||||||
|
camera_params: CameraParameters | None = None,
|
||||||
|
altitude: float = 100.0,
|
||||||
|
quaternion: np.ndarray | None = None,
|
||||||
|
) -> GPSPoint: ...
|
||||||
|
|
||||||
|
def transform_points(
|
||||||
|
self,
|
||||||
|
points: list[tuple[float, float]],
|
||||||
|
transformation: list[list[float]],
|
||||||
|
) -> list[tuple[float, float]]: ...
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
"""Protocol surface for the GPR component (ARCH-05).
|
||||||
|
|
||||||
|
Phase 1: mirrors ``IGlobalPlaceRecognition`` from ``core/gpr.py``.
|
||||||
|
Adapters move here in Plan 05 (GPR).
|
||||||
|
"""
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from typing import List, Protocol, runtime_checkable
|
||||||
|
|
||||||
|
import numpy as np
|
||||||
|
|
||||||
|
from gps_denied.schemas.gpr import DatabaseMatch, TileCandidate
|
||||||
|
|
||||||
|
|
||||||
|
@runtime_checkable
|
||||||
|
class GlobalPlaceRecognition(Protocol):
|
||||||
|
"""Coarse localisation surface (mirrors IGlobalPlaceRecognition)."""
|
||||||
|
|
||||||
|
def retrieve_candidate_tiles(
|
||||||
|
self, image: np.ndarray, top_k: int
|
||||||
|
) -> List[TileCandidate]: ...
|
||||||
|
|
||||||
|
def compute_location_descriptor(self, image: np.ndarray) -> np.ndarray: ...
|
||||||
|
|
||||||
|
def query_database(
|
||||||
|
self, descriptor: np.ndarray, top_k: int
|
||||||
|
) -> List[DatabaseMatch]: ...
|
||||||
|
|
||||||
|
def rank_candidates(
|
||||||
|
self, candidates: List[TileCandidate]
|
||||||
|
) -> List[TileCandidate]: ...
|
||||||
|
|
||||||
|
def load_index(self, flight_id: str, index_path: str) -> bool: ...
|
||||||
|
|
||||||
|
def retrieve_candidate_tiles_for_chunk(
|
||||||
|
self, chunk_images: List[np.ndarray], top_k: int
|
||||||
|
) -> List[TileCandidate]: ...
|
||||||
|
|
||||||
|
def compute_chunk_descriptor(self, chunk_images: List[np.ndarray]) -> np.ndarray: ...
|
||||||
|
|
||||||
|
|
||||||
|
# Backwards-compat alias.
|
||||||
|
IGlobalPlaceRecognition = GlobalPlaceRecognition
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
"""Protocol surface for the MAVLink I/O component (ARCH-05).
|
||||||
|
|
||||||
|
Phase 1: mirrors the concrete ``MAVLinkBridge`` public surface from
|
||||||
|
``core/mavlink.py`` (no ABC today). Adapters move here in Plan 07
|
||||||
|
(mavlink_io); private helpers ``_confidence_to_fix_type`` and
|
||||||
|
``_eskf_to_gps_input`` MUST stay re-exported from the old path.
|
||||||
|
"""
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from typing import Callable, Optional, Protocol, runtime_checkable
|
||||||
|
|
||||||
|
from gps_denied.schemas import GPSPoint
|
||||||
|
from gps_denied.schemas.eskf import ESKFState, IMUMeasurement
|
||||||
|
from gps_denied.schemas.mavlink import GPSInputMessage, RelocalizationRequest
|
||||||
|
|
||||||
|
|
||||||
|
@runtime_checkable
|
||||||
|
class MAVLinkBridgeProtocol(Protocol):
|
||||||
|
"""Public surface of the MAVLink GPS_INPUT/IMU/telemetry bridge."""
|
||||||
|
|
||||||
|
def set_imu_callback(
|
||||||
|
self, cb: Callable[[IMUMeasurement], None]
|
||||||
|
) -> None: ...
|
||||||
|
|
||||||
|
def set_reloc_callback(
|
||||||
|
self, cb: Callable[[RelocalizationRequest], None]
|
||||||
|
) -> None: ...
|
||||||
|
|
||||||
|
def update_state(self, state: ESKFState, altitude_m: float = 0.0) -> None: ...
|
||||||
|
|
||||||
|
def notify_satellite_correction(self) -> None: ...
|
||||||
|
|
||||||
|
def update_drift_estimate(self, drift_m: float) -> None: ...
|
||||||
|
|
||||||
|
async def start(self, origin: GPSPoint) -> None: ...
|
||||||
|
|
||||||
|
async def stop(self) -> None: ...
|
||||||
|
|
||||||
|
def build_gps_input(self) -> Optional[GPSInputMessage]: ...
|
||||||
@@ -0,0 +1,103 @@
|
|||||||
|
"""Protocol surfaces for the satellite_matcher component (ARCH-05).
|
||||||
|
|
||||||
|
Two Protocols live here per PATTERNS.md §3:
|
||||||
|
|
||||||
|
* ``SatelliteTileLoader`` — mirrors the concrete ``SatelliteDataManager``
|
||||||
|
public surface from ``core/satellite.py`` (no existing ABC).
|
||||||
|
* ``MetricRefiner`` — mirrors ``IMetricRefinement`` from ``core/metric.py``.
|
||||||
|
|
||||||
|
Adapters move here in Plan 06 (satellite_matcher).
|
||||||
|
"""
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from typing import List, Optional, Protocol, Tuple, runtime_checkable
|
||||||
|
|
||||||
|
import numpy as np
|
||||||
|
|
||||||
|
from gps_denied.schemas import GPSPoint
|
||||||
|
from gps_denied.schemas.metric import AlignmentResult, ChunkAlignmentResult
|
||||||
|
from gps_denied.schemas.satellite import TileBounds, TileCoords
|
||||||
|
|
||||||
|
|
||||||
|
@runtime_checkable
|
||||||
|
class SatelliteTileLoader(Protocol):
|
||||||
|
"""Local satellite-tile reader / mosaic builder.
|
||||||
|
|
||||||
|
Mirrors the public surface of ``SatelliteDataManager`` (no ABC today).
|
||||||
|
"""
|
||||||
|
|
||||||
|
def load_local_tile(self, tile_coords: TileCoords) -> np.ndarray | None: ...
|
||||||
|
|
||||||
|
def select_tiles_for_eskf_position(
|
||||||
|
self, gps: GPSPoint, sigma_h_m: float, zoom: int
|
||||||
|
) -> list[TileCoords]: ...
|
||||||
|
|
||||||
|
def assemble_mosaic(
|
||||||
|
self,
|
||||||
|
tile_list: list[tuple[TileCoords, np.ndarray]],
|
||||||
|
target_size: int = 512,
|
||||||
|
) -> tuple[np.ndarray, TileBounds] | None: ...
|
||||||
|
|
||||||
|
def fetch_tiles_for_position(
|
||||||
|
self, gps: GPSPoint, sigma_h_m: float, zoom: int
|
||||||
|
) -> tuple[np.ndarray, TileBounds] | None: ...
|
||||||
|
|
||||||
|
def get_cached_tile(
|
||||||
|
self, flight_id: str, tile_coords: TileCoords
|
||||||
|
) -> np.ndarray | None: ...
|
||||||
|
|
||||||
|
def cache_tile(
|
||||||
|
self, flight_id: str, tile_coords: TileCoords, tile_data: np.ndarray
|
||||||
|
) -> bool: ...
|
||||||
|
|
||||||
|
def compute_tile_coords(self, lat: float, lon: float, zoom: int) -> TileCoords: ...
|
||||||
|
|
||||||
|
def compute_tile_bounds(self, tile_coords: TileCoords) -> TileBounds: ...
|
||||||
|
|
||||||
|
def clear_flight_cache(self, flight_id: str) -> bool: ...
|
||||||
|
|
||||||
|
def expand_search_grid(
|
||||||
|
self, center: TileCoords, current_size: int, new_size: int
|
||||||
|
) -> list[TileCoords]: ...
|
||||||
|
|
||||||
|
def get_tile_grid(self, center: TileCoords, grid_size: int) -> list[TileCoords]: ...
|
||||||
|
|
||||||
|
|
||||||
|
@runtime_checkable
|
||||||
|
class MetricRefiner(Protocol):
|
||||||
|
"""LiteSAM-style satellite alignment surface (mirrors IMetricRefinement)."""
|
||||||
|
|
||||||
|
def align_to_satellite(
|
||||||
|
self,
|
||||||
|
uav_image: np.ndarray,
|
||||||
|
satellite_tile: np.ndarray,
|
||||||
|
tile_bounds: TileBounds,
|
||||||
|
) -> Optional[AlignmentResult]: ...
|
||||||
|
|
||||||
|
def compute_homography(
|
||||||
|
self, uav_image: np.ndarray, satellite_tile: np.ndarray
|
||||||
|
) -> Optional[np.ndarray]: ...
|
||||||
|
|
||||||
|
def extract_gps_from_alignment(
|
||||||
|
self,
|
||||||
|
homography: np.ndarray,
|
||||||
|
tile_bounds: TileBounds,
|
||||||
|
image_center: Tuple[int, int],
|
||||||
|
) -> GPSPoint: ...
|
||||||
|
|
||||||
|
def compute_match_confidence(self, alignment: AlignmentResult) -> float: ...
|
||||||
|
|
||||||
|
def align_chunk_to_satellite(
|
||||||
|
self,
|
||||||
|
chunk_images: List[np.ndarray],
|
||||||
|
satellite_tile: np.ndarray,
|
||||||
|
tile_bounds: TileBounds,
|
||||||
|
) -> Optional[ChunkAlignmentResult]: ...
|
||||||
|
|
||||||
|
def match_chunk_homography(
|
||||||
|
self, chunk_images: List[np.ndarray], satellite_tile: np.ndarray
|
||||||
|
) -> Optional[np.ndarray]: ...
|
||||||
|
|
||||||
|
|
||||||
|
# Backwards-compat alias for the only ABC that previously existed.
|
||||||
|
IMetricRefinement = MetricRefiner
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
"""Protocol surface for the VIO component (ARCH-05).
|
||||||
|
|
||||||
|
Phase 1: defines the Protocol that concrete adapters in this directory
|
||||||
|
implement. Method signatures mirror ``ISequentialVisualOdometry`` from
|
||||||
|
``core/vo.py``. Adapters are NOT moved here yet — see Plan 04 (VIO).
|
||||||
|
"""
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from typing import Protocol, runtime_checkable
|
||||||
|
|
||||||
|
import numpy as np
|
||||||
|
|
||||||
|
from gps_denied.schemas import CameraParameters
|
||||||
|
from gps_denied.schemas.vo import Features, Matches, Motion, RelativePose
|
||||||
|
|
||||||
|
|
||||||
|
@runtime_checkable
|
||||||
|
class VisualOdometry(Protocol):
|
||||||
|
"""Sequential visual odometry surface (mirrors ISequentialVisualOdometry)."""
|
||||||
|
|
||||||
|
def compute_relative_pose(
|
||||||
|
self, prev_image: np.ndarray, curr_image: np.ndarray, camera_params: CameraParameters
|
||||||
|
) -> RelativePose | None: ...
|
||||||
|
|
||||||
|
def extract_features(self, image: np.ndarray) -> Features: ...
|
||||||
|
|
||||||
|
def match_features(self, features1: Features, features2: Features) -> Matches: ...
|
||||||
|
|
||||||
|
def estimate_motion(
|
||||||
|
self, matches: Matches, camera_params: CameraParameters
|
||||||
|
) -> Motion | None: ...
|
||||||
|
|
||||||
|
|
||||||
|
# Backwards-compat alias — Phase 2 will deprecate the I-prefix.
|
||||||
|
ISequentialVisualOdometry = VisualOdometry
|
||||||
Reference in New Issue
Block a user