#!/usr/bin/env python3 """Build the measured BOC lock root-cause report from replay JSON.""" from __future__ import annotations import argparse import json from pathlib import Path def main() -> int: parser = argparse.ArgumentParser() parser.add_argument( "--comparison", default="reports/e1_tracking_mode_comparison/e1_tracking_mode_comparison.json", ) parser.add_argument("--output-dir", default="reports/boc_lock_detector_validation") args = parser.parse_args() comparison_path = Path(args.comparison).resolve() comparison = json.loads(comparison_path.read_text(encoding="utf-8")) e1b_report_path = comparison_path.parent / "e1b_only" / "inav_collection_report.json" e1b_report = json.loads(e1b_report_path.read_text(encoding="utf-8")) output = Path(args.output_dir).resolve() output.mkdir(parents=True, exist_ok=True) satellites: dict[str, dict] = {} for prn, item in e1b_report["satellites"].items(): lock = item["lock_analysis"] satellites[str(prn)] = { "legacy_code_lock_falling_edges": lock["legacy_code_lock_falling_edges"], "boc_aware_code_lock_falling_edges": lock["boc_aware_code_lock_falling_edges"], "boc_aware_longest_code_lock_s": lock["boc_aware_longest_code_lock_s"], "legacy_failure_behavior": lock["legacy_code_metric_failure_behavior"], "legacy_pass_behavior": lock["legacy_code_metric_pass_behavior"], "side_peak_suspected_epochs": lock["boc_side_peak_suspected_epochs"], "cycle_slip_candidates": item["cycle_slips"], "sample_discontinuities": item["sample_discontinuities"], "crc_valid_pages": item["crc_passes"], "crc_invalid_pages": item["crc_failures"], "complete_ephemeris": item["complete_ephemeris"], } report = { "classification": "LOCK DETECTOR CHATTER CONFIRMED", "primary_cause": "instantaneous BPSK-style Prompt/Early/Late power ratio is invalid on the BOC(1,1) main-peak shoulder", "signal_modulation": "SignalSim BOC(1,1); no BOC(6,1) CBOC term", "loop_coefficients_changed": False, "sample_file": e1b_report["sample_file"], "sample_sha256": e1b_report["sample_sha256"], "sample_count": e1b_report["sample_count"], "sampling_frequency_hz": e1b_report["sampling_frequency_hz"], "intermediate_frequency_hz": e1b_report["intermediate_frequency_hz"], "exact_replay_command": "python tools/compare_e1_tracking_modes.py --config configs/navigation_collection.yaml --output-dir reports/e1_tracking_mode_comparison", "legacy_total_code_lock_falling_edges": sum( item["legacy_code_lock_falling_edges"] for item in satellites.values() ), "boc_aware_total_code_lock_falling_edges": sum( item["boc_aware_code_lock_falling_edges"] for item in satellites.values() ), "crc_valid_pages": sum(item["crc_valid_pages"] for item in satellites.values()), "crc_invalid_pages": sum(item["crc_invalid_pages"] for item in satellites.values()), "complete_ephemerides": sum(item["complete_ephemeris"] for item in satellites.values()), "satellites": satellites, "regression_tests": { "main_peak_and_metric_validity": "tests/unit/test_lock_detector.py", "quarter_chip_shoulder_false_unlock": "tests/unit/test_lock_detector.py", "outer_tap_side_peak": "tests/unit/test_lock_detector.py", "zero_signal_true_loss": "tests/unit/test_lock_detector.py", "invalid_and_single_bad_epoch": "tests/unit/test_lock_detector.py", "e1c_pilot_secondary_and_e1b_branch": "tests/integration/test_signalsim_e1c_pilot_aided_tracking.py", }, } (output / "boc_lock_detector_validation.json").write_text( json.dumps(report, indent=2) + "\n", encoding="utf-8" ) lines = [ "# BOC-aware lock-detector validation", "", "**LOCK DETECTOR CHATTER CONFIRMED**", "", "The loops were not retuned. The failure source was the legacy instantaneous `P² / mean(E²,L²)` predicate, which falls to 0.1 at an ideal quarter-chip BOC(1,1) shoulder even though the five-tap energy remains in the central correlation basin.", "", f"Input evidence: {report['sample_count']} complex IQ8 samples at {report['sampling_frequency_hz']:.0f} Hz ({report['sample_count'] / report['sampling_frequency_hz']:.3f} s stored from the 32.2 s configured run), SHA-256 `{report['sample_sha256']}`.", "", f"Replay: `{report['exact_replay_command']}`", "", "| PRN | Legacy falls | Corrected falls | Longest corrected lock (s) | DLL RMS during old failures (chips) | Failure P power median | Failure E power median | Failure L power median | Max side metric | Min main coherence | CRC valid/invalid | Ephemeris |", "| --: | --: | --: | --: | --: | --: | --: | --: | --: | --: | --: | ---: |", ] for prn, item in satellites.items(): failure = item["legacy_failure_behavior"] lines.append( f"| {prn} | {item['legacy_code_lock_falling_edges']} | {item['boc_aware_code_lock_falling_edges']} | " f"{item['boc_aware_longest_code_lock_s']:.3f} | {failure['dll_error_rms_chips']:.6f} | " f"{failure['prompt_power_median']:.3e} | {failure['early_power_median']:.3e} | " f"{failure['late_power_median']:.3e} | {failure['boc_side_peak_metric_max']:.4f} | " f"{failure['boc_main_peak_coherence_min']:.4f} | " f"{item['crc_valid_pages']}/{item['crc_invalid_pages']} | {item['complete_ephemeris']} |" ) lines.extend([ "", f"Total legacy/corrected falling edges: **{report['legacy_total_code_lock_falling_edges']}/{report['boc_aware_total_code_lock_falling_edges']}**.", f"Navigation result: **{report['crc_valid_pages']}/{report['crc_valid_pages'] + report['crc_invalid_pages']} CRC-valid pages**, {report['complete_ephemerides']} complete ephemerides.", "", "Each PRN artifact directory contains the complete double-precision measurement NPZ, a CSV row for every legacy falling edge, and a six-panel plot with old metric, corrected BOC metrics, VE/E/P/L/VL powers, PLL/DLL errors, NCOs, C/N0, and transition markers.", "", "The observed smoothed outer/central metric and coherent tap-sign metric remain in the calibrated central basin during the legacy failures. Thus none was a main-to-side-peak transition. Synthetic +/-0.5-chip negative Prompt sidelobes and sustained outer-tap dominance both clear lock after debounce; zero-signal input clears signal presence and code lock. Invalid and incomplete integrations cannot seed the smoothing window.", "", "Two E1B phase-jump candidates (PRN 1 and 4) were recorded by the pre-existing one-epoch cycle-slip candidate test; neither coincided with NCO divergence, a discontinuity, CRC loss, or corrected code-lock loss. E1C-aided tracking recorded zero such candidates. They are reported rather than silently discarded.", "", "See `boc11_correlation_and_lock_metrics.png` in the comparison report and per-PRN `tracking_lock_diagnostics.png` under `verification/navigation/signalsim_navigation_multisat_e1b_only/`.", ]) (output / "boc_lock_detector_validation.md").write_text("\n".join(lines) + "\n", encoding="utf-8") return 0 if __name__ == "__main__": raise SystemExit(main())