mirror of
https://github.com/azaion/gps-denied-onboard.git
synced 2026-06-22 21:41:13 +00:00
[AZ-390] [AZ-392] C8 FC/GCS adapter foundation + covariance projector
Adds the C8 foundation: - FcAdapter / GcsAdapter / ReplaySink Protocols + contract DTOs in _types/fc.py (PortConfig, FcKind, FlightState, GpsStatus, Severity, TelemetryKind, FcTelemetryFrame, FlightStateSignal, GpsHealth, OperatorCommand, Subscription, Imu/Attitude samples). - Disjoint FcAdapterError / GcsAdapterError trees with SourceSetSwitchNotSupportedError <: SourceSetSwitchError per AC-9. - FcConfig + GcsConfig cross-cutting Config blocks with config-load validation (unknown strategy rejected at __post_init__). - runtime_root/fc_factory.py: build_fc_adapter / build_gcs_adapter with BUILD_FC_*/BUILD_GCS_* flag gating + INFO log on load + single-writer outbound-thread binding. - CovarianceProjector (helper, AZ-392): 6x6 -> 3x3 -> 2x2 -> sqrt(lambda_max) reduction; AP returns float m, iNav returns int mm with uint16 clamp + WARN + FDR record. Non-SPD / NaN / wrong-shape raise FcEmitError and emit an FDR ERROR record carrying frame_id. Contracts: - composition_root_protocol.md 1.1.0 -> 1.2.0 (added fc/gcs blocks + build_fc_adapter / build_gcs_adapter + outbound-thread binding). - fc_adapter_protocol.md unchanged (this batch implements v1.0.0). Tests: 410 pass / 2 skip / 0 fail (+53 new tests in batch 8). AZ-391 (inbound subscription) deferred to batch 9 — pulls YAMSPy as a new external dependency (iNav MSP2 decode). Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -23,7 +23,9 @@ import yaml
|
||||
from gps_denied_onboard.config.schema import (
|
||||
_COMPONENT_REGISTRY,
|
||||
Config,
|
||||
FcConfig,
|
||||
FdrConfig,
|
||||
GcsConfig,
|
||||
LogConfig,
|
||||
RequiredFieldMissingError,
|
||||
RuntimeConfig,
|
||||
@@ -49,6 +51,15 @@ ENV_KEY_MAP: Final[dict[str, tuple[str, str]]] = {
|
||||
"LOG_SINK": ("log", "sink"),
|
||||
"FDR_PATH": ("fdr", "path"),
|
||||
"FDR_QUEUE_SIZE": ("fdr", "queue_size"),
|
||||
# C8 FC + GCS adapter blocks (AZ-390)
|
||||
"FC_ADAPTER": ("fc", "adapter"),
|
||||
"FC_PORT_DEVICE": ("fc", "port_device"),
|
||||
"FC_PORT_BAUD": ("fc", "port_baud"),
|
||||
"FC_SIGNING_KEY_SOURCE": ("fc", "signing_key_source"),
|
||||
"GCS_ADAPTER": ("gcs", "adapter"),
|
||||
"GCS_PORT_DEVICE": ("gcs", "port_device"),
|
||||
"GCS_PORT_BAUD": ("gcs", "port_baud"),
|
||||
"GCS_SUMMARY_RATE_HZ": ("gcs", "summary_rate_hz"),
|
||||
}
|
||||
|
||||
# Env vars that MUST resolve to a non-empty value before `load_config`
|
||||
@@ -81,6 +92,12 @@ _FIELD_COERCIONS: Final[dict[str, type]] = {
|
||||
"inference_backend": str,
|
||||
"tile_cache_path": str,
|
||||
"overrun_policy": str,
|
||||
# C8 FC + GCS adapter coercions (AZ-390)
|
||||
"adapter": str,
|
||||
"port_device": str,
|
||||
"port_baud": int,
|
||||
"signing_key_source": str,
|
||||
"summary_rate_hz": float,
|
||||
}
|
||||
|
||||
|
||||
@@ -160,6 +177,14 @@ def load_config(
|
||||
FdrConfig(),
|
||||
{k: _coerce_value(k, v) for k, v in yaml_overrides.get("fdr", {}).items()},
|
||||
)
|
||||
fc_block = _replace_block(
|
||||
FcConfig(),
|
||||
{k: _coerce_value(k, v) for k, v in yaml_overrides.get("fc", {}).items()},
|
||||
)
|
||||
gcs_block = _replace_block(
|
||||
GcsConfig(),
|
||||
{k: _coerce_value(k, v) for k, v in yaml_overrides.get("gcs", {}).items()},
|
||||
)
|
||||
|
||||
component_blocks = _resolve_component_blocks()
|
||||
for slug, dataclass_type in _COMPONENT_REGISTRY.items():
|
||||
@@ -174,5 +199,7 @@ def load_config(
|
||||
runtime=runtime_block,
|
||||
log=log_block,
|
||||
fdr=fdr_block,
|
||||
fc=fc_block,
|
||||
gcs=gcs_block,
|
||||
components=component_blocks,
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user