#!/usr/bin/env python3 import sys import math import datetime from collections import Counter from pprint import pprint from tqdm import tqdm from typing import List, Tuple, cast from cereal.services import SERVICE_LIST from openpilot.tools.lib.route import Route from openpilot.tools.lib.logreader import LogReader if __name__ == "__main__": r = Route(sys.argv[1]) cnt_valid: Counter = Counter() cnt_events: Counter = Counter() cams = [s for s in SERVICE_LIST if s.endswith('CameraState')] cnt_cameras = dict.fromkeys(cams, 0) alerts: List[Tuple[float, str]] = [] start_time = math.inf end_time = -math.inf ignition_off = None for q in tqdm(r.qlog_paths()): if q is None: continue lr = list(LogReader(q)) for msg in lr: end_time = max(end_time, msg.logMonoTime) start_time = min(start_time, msg.logMonoTime) if msg.which() == 'carEvents': for e in msg.carEvents: cnt_events[e.name] += 1 elif msg.which() == 'controlsState': if len(alerts) == 0 or alerts[-1][1] != msg.controlsState.alertType: t = (msg.logMonoTime - start_time) / 1e9 alerts.append((t, msg.controlsState.alertType)) elif msg.which() == 'pandaStates': if ignition_off is None: ign = any(ps.ignitionLine or ps.ignitionCan for ps in msg.pandaStates) if not ign: ignition_off = msg.logMonoTime elif msg.which() in cams: cnt_cameras[msg.which()] += 1 if not msg.valid: cnt_valid[msg.which()] += 1 duration = (end_time - start_time) / 1e9 print("Events") pprint(cnt_events) print("\n") print("Not valid") pprint(cnt_valid) print("\n") print("Cameras") for k, v in cnt_cameras.items(): s = SERVICE_LIST[k] expected_frames = int(s.frequency * duration / cast(float, s.decimation)) print(" ", k.ljust(20), f"{v}, {v/expected_frames:.1%} of expected") print("\n") print("Alerts") for t, a in alerts: print(f"{t:8.2f} {a}") if ignition_off is not None: ignition_off = round((ignition_off - start_time) / 1e9, 2) print("Ignition off at", ignition_off) print("\n") print("Route duration", datetime.timedelta(seconds=duration))