#!/usr/bin/env python3 """Measure adjacent route-relative mouth-profile steps; period fits are diagnostic.""" from __future__ import annotations import argparse from collections import defaultdict from pathlib import Path import re import sys PROFILE = re.compile( r"\[PassageFloorRippleSample\] id=(\S+) distance_m=([-0-9.]+).*?" r"residual_m=([-0-9.]+)") RIPPLE = re.compile(r"\[PassageRippleAudit\] id=(\S+).*?period_m=([-0-9.]+)") STEP_LIMIT_M = 0.100 MAX_ADJACENT_DISTANCE_M = 0.750 def measure(text: str) -> dict[str, object]: profiles: dict[str, list[tuple[float, float]]] = defaultdict(list) for match in PROFILE.finditer(text): passage, distance, residual = match.groups() profiles[passage].append((float(distance), float(residual))) adjacent: list[tuple[float, str, float, float]] = [] group_counts: dict[str, int] = {} for key, samples in profiles.items(): samples.sort() group_counts[key] = len(samples) for before, after in zip(samples, samples[1:]): delta_d = abs(after[0] - before[0]) if delta_d <= MAX_ADJACENT_DISTANCE_M: adjacent.append((abs(after[1] - before[1]), key, before[0], after[0])) periods = {passage: float(period) for passage, period in RIPPLE.findall(text)} maximum = max((item[0] for item in adjacent), default=0.0) return { "maximum_adjacent_step_m": maximum, "adjacent_pairs": len(adjacent), "profile_groups": len(profiles), "profile_group_counts": group_counts, "fitted_periods_m": periods, "passed": bool(adjacent) and maximum <= STEP_LIMIT_M, "worst": max(adjacent, default=None), } def main() -> int: parser = argparse.ArgumentParser(description=__doc__) parser.add_argument("log", type=Path) parser.add_argument("--expect-fail", action="store_true", help="pass only when the adjacent-step limit is exceeded") args = parser.parse_args() result = measure(args.log.read_text(encoding="utf-8", errors="replace")) detected = not bool(result["passed"]) ok = detected if args.expect_fail else not detected print(f"[OwnerAdjacentProfileStep] log={args.log} pairs={result['adjacent_pairs']} " f"groups={result['profile_groups']} max_step_m={result['maximum_adjacent_step_m']:.6f} " f"limit_m={STEP_LIMIT_M:.3f} max_adjacent_distance_m={MAX_ADJACENT_DISTANCE_M:.3f} " f"fitted_periods_m={result['fitted_periods_m']} " "period_role=diagnostic " f"detected={str(detected).lower()} status={'PASS' if ok else 'FAIL'}") return 0 if ok else 1 if __name__ == "__main__": raise SystemExit(main())