mirror of
https://github.com/azaion/gps-denied-onboard.git
synced 2026-04-22 11:16:37 +00:00
fix: post-audit — runtime bugs, functional gaps, docs, hardening
Phase A — Runtime bugs: - SSE: add push_event() method to SSEEventStreamer (was missing, masked by mocks) - MAVLink: satellites_visible=10 (was 0, triggers ArduPilot failsafe) - MAVLink: horiz_accuracy=sqrt(P[0,0]+P[1,1]) per spec (was sqrt(avg)) - MAVLink: MEDIUM confidence → fix_type=3 per solution.md (was 2) Phase B — Functional gaps: - handle_user_fix() injects operator GPS into ESKF with noise=500m - app.py uses create_vo_backend() factory (was hardcoded SequentialVO) - ESKF: Mahalanobis gating on satellite updates (rejects outliers >5σ) - ESKF: public accessors (position, quaternion, covariance, last_timestamp) - Processor: no more private ESKF field access Phase C — Documentation: - README: correct API endpoints, CLI command, 40+ env vars documented - Dockerfile: ENV prefixes match pydantic-settings (DB_, SATELLITE_, MAVLINK_) - tech_stack.md marked ARCHIVED (contradicts solution.md) Phase D — Hardening: - JWT auth middleware (AUTH_ENABLED=false default, verify_token on /flights) - TLS config env vars (AUTH_SSL_CERTFILE, AUTH_SSL_KEYFILE) - SHA-256 tile manifest verification in SatelliteDataManager - AuthConfig, ESKFSettings, MAVLinkConfig, SatelliteConfig in config.py Also: conftest.py shared fixtures, download_tiles.py, convert_to_trt.py scripts, config wiring into app.py lifespan, config-driven ESKF, calculate_precise_angle fix. Tests: 196 passed / 8 skipped. Ruff clean. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,61 @@
|
||||
"""Shared test fixtures for GPS-denied-onboard test suite."""
|
||||
|
||||
from unittest.mock import AsyncMock, MagicMock
|
||||
|
||||
import numpy as np
|
||||
import pytest
|
||||
|
||||
from gps_denied.core.coordinates import CoordinateTransformer
|
||||
from gps_denied.core.models import ModelManager
|
||||
from gps_denied.schemas import CameraParameters, GPSPoint
|
||||
|
||||
# ---------------------------------------------------------------
|
||||
# Common constants
|
||||
# ---------------------------------------------------------------
|
||||
|
||||
TEST_ORIGIN = GPSPoint(lat=49.0, lon=32.0)
|
||||
TEST_CAMERA = CameraParameters(
|
||||
focal_length=4.5,
|
||||
sensor_width=6.17,
|
||||
sensor_height=4.55,
|
||||
resolution_width=640,
|
||||
resolution_height=480,
|
||||
)
|
||||
|
||||
|
||||
# ---------------------------------------------------------------
|
||||
# Shared fixtures
|
||||
# ---------------------------------------------------------------
|
||||
|
||||
@pytest.fixture
|
||||
def mock_repo():
|
||||
"""Mock FlightRepository."""
|
||||
return MagicMock()
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def mock_streamer():
|
||||
"""Mock SSEEventStreamer with async push_event."""
|
||||
s = MagicMock()
|
||||
s.push_event = AsyncMock()
|
||||
return s
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def model_manager():
|
||||
"""Singleton ModelManager (mock engines)."""
|
||||
return ModelManager()
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def coord_transformer():
|
||||
"""CoordinateTransformer with a preset test ENU origin."""
|
||||
ct = CoordinateTransformer()
|
||||
ct.set_enu_origin("test_flight", TEST_ORIGIN)
|
||||
return ct
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def sample_image():
|
||||
"""Random 200x200 RGB image for pipeline tests."""
|
||||
return np.random.randint(0, 255, (200, 200, 3), dtype=np.uint8)
|
||||
+2
-1
@@ -187,7 +187,8 @@ class TestESKFSatelliteUpdate:
|
||||
eskf.predict(imu)
|
||||
|
||||
pos_before = eskf.get_state().position.copy()
|
||||
target_pos = np.array([50.0, 50.0, 0.0])
|
||||
# Use target within Mahalanobis gate (small shift relative to P)
|
||||
target_pos = np.array([5.0, 5.0, 0.0])
|
||||
|
||||
eskf.update_satellite(target_pos, noise_meters=10.0)
|
||||
|
||||
|
||||
@@ -76,7 +76,7 @@ def test_unix_to_gps_time_recent():
|
||||
def test_confidence_to_fix_type():
|
||||
"""MAV-02: confidence tier → fix_type mapping."""
|
||||
assert _confidence_to_fix_type(ConfidenceTier.HIGH) == 3
|
||||
assert _confidence_to_fix_type(ConfidenceTier.MEDIUM) == 2
|
||||
assert _confidence_to_fix_type(ConfidenceTier.MEDIUM) == 3
|
||||
assert _confidence_to_fix_type(ConfidenceTier.LOW) == 0
|
||||
assert _confidence_to_fix_type(ConfidenceTier.FAILED) == 0
|
||||
|
||||
@@ -125,7 +125,14 @@ def test_eskf_to_gps_input_accuracy_from_covariance():
|
||||
confidence=ConfidenceTier.HIGH,
|
||||
)
|
||||
msg = _eskf_to_gps_input(state, ORIGIN)
|
||||
assert math.isclose(msg.horiz_accuracy, 10.0, abs_tol=0.01)
|
||||
# horiz_accuracy = sqrt(P[0,0] + P[1,1]) = sqrt(200) ≈ 14.14
|
||||
assert math.isclose(msg.horiz_accuracy, math.sqrt(200.0), abs_tol=0.1)
|
||||
|
||||
|
||||
def test_eskf_to_gps_input_satellites_visible_10():
|
||||
"""Synthetic satellites_visible = 10 to prevent ArduPilot failsafes."""
|
||||
msg = _eskf_to_gps_input(_make_state(), ORIGIN)
|
||||
assert msg.satellites_visible == 10
|
||||
|
||||
|
||||
def test_eskf_to_gps_input_returns_message():
|
||||
|
||||
Reference in New Issue
Block a user