"""``replay_input/`` DTOs (AZ-405 / E-DEMO-REPLAY). Frozen + slotted dataclasses per ADR-002 / module-layout.md so the composition root and the coordinator can pass these by value without fear of mutation downstream. The DTOs come in two flavours: - :class:`AutoSyncConfig` — operator-tunable thresholds for the auto-sync algorithm. The composition root builds an instance from ``config.replay.auto_sync`` (owned by AZ-269 / AZ-270) and passes it to :class:`ReplayInputAdapter`. Defaults match the contract in :mod:`auto_sync` and the AC-1 / AC-2 / AC-3 thresholds. - :class:`AutoSyncDecision` — the outcome of one auto-sync run. The composition root attaches this to the FDR record so an operator can audit how the offset was resolved. - :class:`ReplayInputBundle` — the trio of strategies the composition root consumes after :meth:`ReplayInputAdapter.open` returns. The bundle also carries the resolved offset so the FDR write at the start of the replay run can record provenance. """ from __future__ import annotations from dataclasses import dataclass from typing import TYPE_CHECKING if TYPE_CHECKING: from gps_denied_onboard._types.fc import FcKind # noqa: F401 # for docstrings. from gps_denied_onboard.clock import Clock from gps_denied_onboard.components.c8_fc_adapter.tlog_replay_adapter import ( TlogReplayFcAdapter, ) from gps_denied_onboard.frame_source.video_file import VideoFileFrameSource __all__ = [ "AutoSyncConfig", "AutoSyncDecision", "ReplayInputBundle", ] @dataclass(frozen=True, slots=True) class AutoSyncConfig: """Operator-tunable thresholds for the AZ-405 auto-sync algorithm. Defaults match the contract in ``_docs/02_document/contracts/replay/replay_protocol.md`` v2.0.0 and the AC-1 / AC-2 / AC-3 thresholds in the AZ-405 spec. Attributes: takeoff_accel_threshold_g: Sustained vertical-acceleration magnitude (in g) above which a tlog sample is considered part of a take-off pattern. Default 0.5 (AC-1). takeoff_attitude_rate_threshold_rad_s: Sustained attitude-rate magnitude (rad/s) above which an ``ATTITUDE`` pair is considered part of a take-off pattern. Default 1.0. sustained_seconds: Minimum duration both signals must persist above their thresholds for a candidate to be accepted. Default 0.5. prescan_max_messages: Upper bound on tlog messages walked by the take-off detector. ~30 s of telemetry at 200 Hz = 6000 messages, matching the AZ-399 pre-scan budget. video_motion_threshold: Mean optical-flow magnitude (pixels) above which a video frame pair is considered ``moving``. Default 1.5 (calibrated for 720p footage). video_motion_scan_seconds: Length of the leading video segment inspected for the motion onset. Default 10.0 (AC-4 covers an onset at frame 11 of a 60-frame fixture). match_threshold_pct: AC-9 frame-window match-percentage threshold (default 95.0). Configurable per ``config.replay.auto_sync_match_threshold_pct``. match_window_ms: AC-9 per-frame matching tolerance in milliseconds (default 100). low_confidence_threshold: Combined-confidence cut-off below which :meth:`ReplayInputAdapter.open` logs WARN and uses the best-guess offset (AC-6). Default 0.80. """ takeoff_accel_threshold_g: float = 0.5 takeoff_attitude_rate_threshold_rad_s: float = 1.0 sustained_seconds: float = 0.5 prescan_max_messages: int = 6000 video_motion_threshold: float = 1.5 video_motion_scan_seconds: float = 10.0 match_threshold_pct: float = 95.0 match_window_ms: int = 100 low_confidence_threshold: float = 0.80 @dataclass(frozen=True, slots=True) class AutoSyncDecision: """Outcome of one auto-sync run (AZ-405). Attributes: offset_ms: Resolved offset to be applied to tlog timestamps. ``offset_ms = tlog_takeoff_ns - video_motion_onset_ns`` converted to milliseconds. tlog_takeoff_ns: Detected tlog take-off timestamp. video_motion_onset_ns: Detected video motion-onset timestamp. tlog_confidence: Take-off detector confidence in [0, 1]. video_confidence: Motion-onset detector confidence in [0, 1]. combined_confidence: Aggregated confidence in [0, 1]. Below :attr:`AutoSyncConfig.low_confidence_threshold` the coordinator logs WARN and proceeds (AC-6). """ offset_ms: int tlog_takeoff_ns: int video_motion_onset_ns: int tlog_confidence: float video_confidence: float combined_confidence: float @dataclass(frozen=True, slots=True) class ReplayInputBundle: """Trio of strategies returned by :meth:`ReplayInputAdapter.open`. The composition root wires the bundle into the same C1–C7 + C13 pipeline as live (replay protocol Invariant 1 — the components see only the standard :class:`FrameSource` / :class:`FcAdapter` / :class:`Clock` interfaces past this point). Attributes: frame_source: :class:`VideoFileFrameSource` instance ready for ``next_frame()`` calls. fc_adapter: :class:`TlogReplayFcAdapter` instance with its decode thread already started by :meth:`open`. clock: :class:`TlogDerivedClock` (pace=ASAP) or :class:`WallClock` (pace=REALTIME). resolved_time_offset_ms: Offset applied to tlog timestamps. Equals either the ``manual_time_offset_ms`` constructor argument or :attr:`AutoSyncDecision.offset_ms`. auto_sync_result: Auto-sync outcome; ``None`` when the constructor received an explicit ``manual_time_offset_ms``. """ frame_source: "VideoFileFrameSource" fc_adapter: "TlogReplayFcAdapter" clock: "Clock" resolved_time_offset_ms: int auto_sync_result: AutoSyncDecision | None