[AZ-405] Replay — replay_input/ coordinator + IMU take-off auto-sync

Adds the Layer-4 cross-cutting `replay_input/` module per ADR-011:
ReplayInputAdapter converges (video, tlog) into the standard
FrameSource + FcAdapter + Clock surfaces the airborne composition
root consumes. Owns time-alignment between video frames and tlog
IMU/attitude ticks (manual via --time-offset-ms or auto via the
AZ-405 IMU-take-off detector + Farneback motion-onset detector).

Auto-sync algorithm (auto_sync.py):
- Tlog take-off detector: sustained vertical-accel excess > 0.5 g for
  >= 0.5 s + sustained attitude-rate magnitude > 1 rad/s.
- Video motion-onset detector: dense Farneback flow magnitude > 1.5 px
  sustained >= 0.5 s (deterministic per AC-10).
- compute_offset combines the two; confidence = min(tlog, video).
- validate_offset_or_fail implements the AC-9 95 % frame-window match
  validator with configurable threshold + window.

ReplayInputAdapter.open() ordering (AC-13):
1. Load tlog samples + fail-fast on missing RAW_IMU/SCALED_IMU2 or
   ATTITUDE BEFORE any video read.
2. Resolve offset (auto-sync OR manual override; manual bypasses the
   detectors entirely per AC-8).
3. Run AC-9 validator on resolved offset; raise auto-sync hard-fail
   for AC-7 (CLI exit 2 mapping).
4. Build single Clock instance per pace (TlogDerived/ASAP, Wall/REAL).
5. Construct VideoFileFrameSource and TlogReplayFcAdapter with the
   resolved offset baked in (replay protocol Invariant 8).

Structured log + FDR records on auto-sync detected / low-confidence /
AC-8 hard-fail kinds. Idempotent close (AC-12).

Tests: 25 unit tests across tests/unit/replay_input/ covering all 13
ACs (kernel-level synthetic fixtures for AC-1..AC-10; coordinator-
level OpenCV synthetic videos + faked pymavlink for AC-6..AC-13).

Contract update: replay_protocol.md v2.0.0 added fdr_client to the
ReplayInputAdapter __init__ signature (was missing in the prose; the
task spec already listed it in the allowed-imports section).

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Oleksandr Bezdieniezhnykh
2026-05-14 09:50:51 +03:00
parent f9b4241d3a
commit 8149083cac
14 changed files with 2979 additions and 4 deletions
@@ -0,0 +1,36 @@
"""``replay_input/`` cross-cutting coordinator (AZ-405 / E-DEMO-REPLAY).
Layer-4 module per ``_docs/02_document/module-layout.md``. Converges
``(video, tlog)`` inputs into the standard :class:`FrameSource`,
:class:`FcAdapter`, and :class:`Clock` surfaces consumed by the
airborne composition root. Owns the time-alignment concern between
video frames and tlog IMU/attitude ticks (manual via
``--time-offset-ms`` or automatic via the AZ-405 IMU-take-off
detector).
New under ADR-011 (replay-as-configuration) — replaces the v1.0.0
design where replay had its own composition root.
Public surface re-exports the coordinator class, the bundle DTO, the
auto-sync decision DTO, the auto-sync config DTO, and the coordinator
error class. The detector functions in :mod:`auto_sync` are NOT
re-exported here so the public API stays focused on the composition
root's wiring needs; tests import the detectors via their full module
path.
"""
from gps_denied_onboard.replay_input.errors import ReplayInputAdapterError
from gps_denied_onboard.replay_input.interface import (
AutoSyncConfig,
AutoSyncDecision,
ReplayInputBundle,
)
from gps_denied_onboard.replay_input.tlog_video_adapter import ReplayInputAdapter
__all__ = [
"AutoSyncConfig",
"AutoSyncDecision",
"ReplayInputAdapter",
"ReplayInputAdapterError",
"ReplayInputBundle",
]