From f7c2ac18fed3c3fc3db4f3dfc7b5faebeb572f62 Mon Sep 17 00:00:00 2001 From: Quintine Date: Sat, 25 Jul 2026 19:49:01 +1000 Subject: [PATCH] =?UTF-8?q?tools:=20byd=5Fcan=5Fdump=20=E2=80=94=20show=20?= =?UTF-8?q?openpilot=20TX=20(sendcan),=20decode=20CRUISE=5FSTATE=20+=20LKA?= =?UTF-8?q?S=5FACTIVE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The RX table filters panda's own transmissions (bus+128), so the 508 camera-feedback spoof was invisible. Now the dump also prints the sendcan table (proves the spoof is being sent and on which bus), a CRUISE_STATE histogram from 813 (tells instantly if the ADAS ECU is in failure states 8/9 or healthy), and the camera's 790 LKAS_ACTIVE ratio. --- tools/byd_can_dump.py | 45 +++++++++++++++++++++++++++++++++++++------ 1 file changed, 39 insertions(+), 6 deletions(-) diff --git a/tools/byd_can_dump.py b/tools/byd_can_dump.py index 2149f08..dc626c4 100755 --- a/tools/byd_can_dump.py +++ b/tools/byd_can_dump.py @@ -98,24 +98,57 @@ def dump_from_route(route_dir=None): print(f"reading {log_path} ...") stats = {} + tx_stats = {} + cruise_states = defaultdict(int) + lkas_active_count = [0, 0] # [active, total] from camera's 790 on bus 2 n_frames = 0 for m in LogReader(log_path): - if m.which() != "can": + w = m.which() + if w not in ("can", "sendcan"): continue t = m.logMonoTime * 1e-9 - for f in m.can: - if f.src >= 128: + frames = m.can if w == "can" else m.sendcan + target = stats if w == "can" else tx_stats + for f in frames: + if w == "can" and f.src >= 128: continue key = (f.src, f.address) - if key not in stats: - stats[key] = [len(f.dat), 0, t, t] - s = stats[key] + if key not in target: + target[key] = [len(f.dat), 0, t, t] + s = target[key] s[1] += 1 s[3] = t n_frames += 1 + # decode ADAS cruise state (813 ACC_HUD_ADAS): CRUISE_STATE = byte5 >> 4 & 0xF + if f.address == 813 and len(f.dat) >= 6: + cruise_states[(f.dat[5] >> 4) & 0xF] += 1 + # decode camera LKAS active (790 MPC_LKAS_CMD): LKAS_ACTIVE = bit 28 = byte3 bit4 + if f.address == 790 and f.src == 2 and len(f.dat) >= 4: + lkas_active_count[1] += 1 + if (f.dat[3] >> 4) & 0x1: + lkas_active_count[0] += 1 + print(f"{n_frames} CAN frames") + print("\n--- received (car -> panda) ---") print_table(stats) + if tx_stats: + print("\n--- openpilot TX (sendcan) ---") + print_table(tx_stats) + + if cruise_states: + names = {0: "Off", 1: "Standby", 3: "ACTIVE", 5: "Override", 8: "FAILURE", 9: "PERMANENT_FAILURE"} + print("\n--- ACC_HUD_ADAS CRUISE_STATE histogram ---") + for state, count in sorted(cruise_states.items()): + print(f" state {state:>2} ({names.get(state, '?')}): {count}") + if cruise_states.get(8, 0) or cruise_states.get(9, 0): + print(" !!! ADAS still in FAILURE state !!!") + else: + print(" OK: no failure states — ADAS ECU healthy") + + if lkas_active_count[1]: + pct = 100.0 * lkas_active_count[0] / lkas_active_count[1] + print(f"\n--- camera 790 LKAS_ACTIVE: {lkas_active_count[0]}/{lkas_active_count[1]} frames ({pct:.0f}%) ---") def dump_live(seconds):