Files
IQ.Pilot/tools/diagnostics/lag_probe.py
T
2026-07-11 09:58:33 -05:00

77 lines
2.1 KiB
Python

#!/usr/bin/env python3
import argparse
import statistics
import time
import cereal.messaging as messaging
SERVICES = [
"carState",
"selfdriveState",
"controlsState",
"modelV2",
"uiDebug",
"liveCalibration",
]
def summarize(values: list[float]) -> str:
if not values:
return "n=0"
return f"n={len(values)} avg_ms={statistics.fmean(values):.2f} max_ms={max(values):.2f}"
def main() -> None:
parser = argparse.ArgumentParser(description="On-device runtime lag probe")
parser.add_argument("--seconds", type=float, default=15.0, help="Sampling window")
args = parser.parse_args()
sm = messaging.SubMaster(SERVICES)
last_seen: dict[str, float] = {}
gaps: dict[str, list[float]] = {service: [] for service in SERVICES}
ui_draw_times: list[float] = []
car_cum_lag: list[float] = []
model_frame_drop: list[float] = []
deadline = time.monotonic() + args.seconds
while time.monotonic() < deadline:
sm.update(100)
now = time.monotonic()
for service in SERVICES:
if not sm.updated[service]:
continue
previous = last_seen.get(service)
if previous is not None:
gaps[service].append((now - previous) * 1000.0)
last_seen[service] = now
if sm.updated["uiDebug"]:
ui_draw_times.append(float(sm["uiDebug"].drawTimeMillis))
if sm.updated["carState"]:
car_cum_lag.append(float(sm["carState"].cumLagMs))
if sm.updated["modelV2"]:
model_frame_drop.append(float(sm["modelV2"].frameDropPerc))
print("Lag probe summary")
for service in SERVICES:
print(f"{service}: {summarize(gaps[service])}")
print(f"uiDebug.drawTimeMillis: {summarize(ui_draw_times)}")
print(f"carState.cumLagMs: {summarize(car_cum_lag)}")
print(f"modelV2.frameDropPerc: {summarize(model_frame_drop)}")
if sm.seen["liveCalibration"]:
live_calib = sm["liveCalibration"]
print(
"liveCalibration:"
f" status={int(live_calib.calStatus)}"
f" calPerc={int(live_calib.calPerc)}"
f" rpy={list(live_calib.rpyCalib)}"
f" spread={list(live_calib.rpyCalibSpread)}"
)
if __name__ == "__main__":
main()