Files
gps-denied-onboard/_docs/03_implementation/batch_99_cycle2_report.md
T
Oleksandr Bezdieniezhnykh f5366bbca1 [AZ-698] Multi-flight tlog handling: segment first, pick last flight
Real derkachi.tlog covers 3 takeoffs at the same field but the
uploaded video covers only the last. Original NCC argmax + AZ-405
head-takeoff fallback both biased toward flight 1, violating the
spec's "the last chunk in tlog is relevant" framing.

Patch: pre-NCC flight segmenter partitions the IMU energy stream
into distinct flights (threshold + gap walk); find_aligned_window
restricts NCC search to the last segment; low-confidence fallback
uses that segment's start instead of head-takeoff detection.
AlignedWindow gains flight_count_detected + selected_flight_index
for FDR-visible audit.

7 new unit tests (segmenter shapes + end-to-end multi-flight
pipeline + segmented fallback path). 19 AZ-698 tests pass, 113
in the regression slice. Zero new mypy --strict errors.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-20 16:44:41 +03:00

5.7 KiB

Batch 99 — Cycle 2 — AZ-698

Date: 2026-05-20 Tasks: AZ-698 (Tlog trim + mid-flight alignment for replay). Story points: 5. Jira status: AZ-698 → In Testing.

What shipped

A normalised-cross-correlation aligner that finds the video's playback window inside a longer tlog, plus the plumbing to honor that window across the replay-mode composition root, replay coordinator, replay-side FC adapter, config schema, and CLI.

  • find_aligned_window(tlog_path, video_path, config, ...) -> AlignedWindow in src/gps_denied_onboard/replay_input/auto_sync.py. Returns (tlog_start_ns, tlog_end_ns, offset_ms, confidence, used_fallback).
  • AlignedWindow DTO + auto_trim flag + alignment_* knobs on ReplayConfig / ReplayAutoSyncConfig.
  • TlogReplayFcAdapter skips messages with _timestamp < tlog_start_ns when seeded (AC-3).
  • --auto-trim CLI flag on gps-denied-replay, mutually exclusive with --time-offset-ms.

Files changed

Production (8):

  • src/gps_denied_onboard/replay_input/interface.py
  • src/gps_denied_onboard/replay_input/auto_sync.py
  • src/gps_denied_onboard/replay_input/tlog_video_adapter.py
  • src/gps_denied_onboard/replay_input/__init__.py (re-export AlignedWindow)
  • src/gps_denied_onboard/components/c8_fc_adapter/tlog_replay_adapter.py
  • src/gps_denied_onboard/config/schema.py
  • src/gps_denied_onboard/config/loader.py
  • src/gps_denied_onboard/runtime_root/_replay_branch.py
  • src/gps_denied_onboard/cli/replay.py

Tests (1 new):

  • tests/unit/replay_input/test_az698_window_alignment.py

Specs:

  • _docs/02_tasks/done/AZ-698_tlog_trim_midflight_alignment.md (moved from todo/, completion notes appended).

AC coverage

AC Test Result
AC-1 test_ac1_takeoff_aligned_offset_matches_az405_within_50ms PASS
AC-2 test_ac2_mid_flight_alignment_locates_correct_window PASS
AC-3 test_ac3_adapter_seek_skips_pre_window_messages, _default_no_seek_* PASS
AC-4 test_ac4_validator_passes_for_takeoff_aligned_offset, _mid_flight_* PASS
AC-5 test_ac5_cli_auto_trim_smoke_uses_find_aligned_window SKIPPED

AC-5 skip reason: the repo's flight_derkachi.mp4 is a 134-byte placeholder, not a real recording. Live CLI smoke is covered by AZ-699 (validation runner) once the real video is sourced.

Test run

tests/unit/replay_input/test_az698_window_alignment.py        12 PASS  1 SKIP
tests/unit/replay_input/test_az405_auto_sync.py               14 PASS
tests/unit/replay_input/test_az405_replay_input_adapter.py    13 PASS
tests/unit/c8_fc_adapter/test_az399_tlog_replay_adapter.py    24 PASS  1 SKIP
tests/unit/replay_input/test_tlog_ground_truth.py             12 PASS
tests/unit/test_az697_gps_compare.py                          10 PASS
tests/unit/calibration/test_khp20s30_factory.py                9 PASS
tests/unit/runtime_root/test_az687_pre_constructed_replay_mode.py 3 PASS
tests/unit/test_az269_config_loader.py                         9 PASS

Totals: 106 passed, 2 skipped, 0 failed. No regressions in adjacent suites.

Strict typing

Baseline (pre-batch, by stash-and-rerun): 17 mypy --strict errors across 6 of the 8 touched src/ files. After batch: 17 errors — same count, same kinds, with line numbers shifted only by added code. Zero new strict-typing errors introduced by AZ-698. Pre-existing errors are out-of-scope per coderule.mdc ("Pre-existing lint errors should only be fixed if they're in the modified area" — they were not in the bytes modified for AZ-698 ACs).

The new public symbols (find_aligned_window, AlignedWindow, _zero_mean_normalise, _resample_uniform) carry explicit npt.NDArray[np.float64] annotations so they don't add to the debt.

Code review verdict

Inline self-review: code paths cover the spec's scope, fallback to head-takeoff on low confidence preserves AZ-405 behavior, adapter seek is opt-in via constructor kwarg so the --skip-auto-sync path is untouched. The normalised-cross-correlation switch is documented in the spec's "Implementation Notes" appendix as the algorithmic decision of record.

Follow-up commit: multi-flight handling

User-reported gap during the AZ-698 "In Testing" phase: real derkachi.tlog contains three takeoffs; the video covers only the last. The original AZ-698 happy path (np.argmax) and fallback (detect_tlog_takeoff on head) were both biased toward flight 1.

Patched in a follow-up commit on top of batch 99:

  • New _segment_flights_from_imu_energy helper partitions the IMU energy stream into distinct flights using a motion-threshold + gap-tolerance walk.
  • find_aligned_window now restricts NCC search to the last detected segment.
  • Low-confidence fallback uses the last segment's start instead of re-running head-takeoff detection on the whole tlog.
  • AlignedWindow gains flight_count_detected + selected_flight_index for observability; both are surfaced in the replay.auto_trim.resolved / …fallback_to_takeoff log records.
  • New unit tests: segmenter happy paths (1-flight, 3-flight), ground-blip rejection, cruise-lull preservation; integration test proving find_aligned_window on a 3-flight tlog picks flight 3.

Test totals after follow-up: 113 passed, 2 skipped, 0 failed. Zero new mypy --strict errors (12 errors in scope, all pre-existing and unchanged).

Next batch

Batch 100 — AZ-699 (real-flight validation runner). Depends on AZ-697 (ground truth) and AZ-698 (alignment) — both now in testing.