mirror of
https://github.com/azaion/gps-denied-onboard.git
synced 2026-04-23 00:26:36 +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:
@@ -97,6 +97,61 @@ class RotationConfig(BaseSettings):
|
||||
return int(360 / self.step_degrees)
|
||||
|
||||
|
||||
class AuthConfig(BaseSettings):
|
||||
"""JWT authentication settings."""
|
||||
|
||||
model_config = SettingsConfigDict(env_prefix="AUTH_")
|
||||
|
||||
enabled: bool = False # False для dev/SITL, True для production
|
||||
secret_key: str = "dev-secret-change-in-production"
|
||||
algorithm: str = "HS256"
|
||||
ssl_certfile: str = "" # шлях до TLS cert (порожній = без TLS)
|
||||
ssl_keyfile: str = "" # шлях до TLS key
|
||||
|
||||
|
||||
class MAVLinkConfig(BaseSettings):
|
||||
"""MAVLink I/O bridge settings."""
|
||||
|
||||
model_config = SettingsConfigDict(env_prefix="MAVLINK_")
|
||||
|
||||
connection: str = Field(
|
||||
default="udp:127.0.0.1:14550",
|
||||
description="pymavlink connection string (serial:/dev/ttyTHS1:57600 or tcp:host:port)",
|
||||
)
|
||||
output_hz: float = 5.0
|
||||
telemetry_hz: float = 1.0
|
||||
|
||||
|
||||
class SatelliteConfig(BaseSettings):
|
||||
"""Pre-loaded satellite tile directory settings."""
|
||||
|
||||
model_config = SettingsConfigDict(env_prefix="SATELLITE_")
|
||||
|
||||
tile_dir: str = ".satellite_tiles"
|
||||
zoom_level: int = 18
|
||||
|
||||
|
||||
class ESKFSettings(BaseSettings):
|
||||
"""ESKF tuning parameters (overridable via env vars)."""
|
||||
|
||||
model_config = SettingsConfigDict(env_prefix="ESKF_")
|
||||
|
||||
accel_noise_density: float = 6.86e-4
|
||||
gyro_noise_density: float = 5.24e-5
|
||||
accel_random_walk: float = 2.0e-3
|
||||
gyro_random_walk: float = 8.73e-7
|
||||
vo_position_noise: float = 0.3
|
||||
sat_noise_min: float = 5.0
|
||||
sat_noise_max: float = 20.0
|
||||
satellite_max_age: float = 30.0
|
||||
covariance_high_threshold: float = 400.0
|
||||
init_pos_var: float = 100.0
|
||||
init_vel_var: float = 1.0
|
||||
init_att_var: float = 0.01
|
||||
init_accel_bias_var: float = 1e-4
|
||||
init_gyro_bias_var: float = 1e-6
|
||||
|
||||
|
||||
class AppSettings(BaseSettings):
|
||||
"""Root settings — aggregates all sub-configs."""
|
||||
|
||||
@@ -114,8 +169,18 @@ class AppSettings(BaseSettings):
|
||||
area: OperationalArea = Field(default_factory=OperationalArea)
|
||||
recovery: RecoveryConfig = Field(default_factory=RecoveryConfig)
|
||||
rotation: RotationConfig = Field(default_factory=RotationConfig)
|
||||
auth: AuthConfig = Field(default_factory=AuthConfig)
|
||||
mavlink: MAVLinkConfig = Field(default_factory=MAVLinkConfig)
|
||||
satellite: SatelliteConfig = Field(default_factory=SatelliteConfig)
|
||||
eskf: ESKFSettings = Field(default_factory=ESKFSettings)
|
||||
|
||||
|
||||
_settings: AppSettings | None = None
|
||||
|
||||
|
||||
def get_settings() -> AppSettings:
|
||||
"""Singleton-like factory for application settings."""
|
||||
return AppSettings()
|
||||
"""Cached singleton for application settings."""
|
||||
global _settings
|
||||
if _settings is None:
|
||||
_settings = AppSettings()
|
||||
return _settings
|
||||
|
||||
Reference in New Issue
Block a user