#!/usr/bin/env python3 """Offline analysis of a shm_capture.py recording (runs on the PC). Ground truth is the colour light value (g4) read from the VR_CameraPassthroughState file, available only while RGB is selected. Every 32-bit word of every other captured buffer is correlated with it during the RGB phase; the best candidates are then printed across the whole run so we can see whether they still follow the lights while mono is selected. Usage: shm_analyse.py CAPTURE [--top N] [--min-r R] """ import argparse import struct import zlib from collections import defaultdict import numpy as np STATE_SIZE = 0x6200 GAMMA = bytes.fromhex("2fbae83e") def load(path): series = defaultdict(list) with open(path, "rb") as f: data = f.read() pos = 0 while pos < len(data): (n,) = struct.unpack_from(" best[0]: best = (ts, light) return best def main(): ap = argparse.ArgumentParser() ap.add_argument("capture") ap.add_argument("--top", type=int, default=25) ap.add_argument("--min-r", type=float, default=0.8) args = ap.parse_args() series = load(args.capture) state_name = next(n for n, s in series.items() if len(s[0][1]) == STATE_SIZE) state = series.pop(state_name) # Ground truth: g4 while RGB is selected and the colour record is fresh. truth_t, truth_v, mono_t = [], [], [] last_ts = None for t, buf in state: rgb = buf[4] if not rgb: mono_t.append(t) continue cl = colour_light(buf) if cl and cl[0] != last_ts: truth_t.append(t) truth_v.append(cl[1]) last_ts = cl[0] truth_t = np.array(truth_t) truth_v = np.array(truth_v) print(f"state file {state_name}: {len(state)} snapshots, {len(truth_t)} fresh RGB light samples, " f"mono from {min(mono_t, default=float('nan')):.1f} to {max(mono_t, default=float('nan')):.1f} s") print("light over time (RGB phase):", " ".join(f"{t:.0f}s={v:.2f}" for t, v in zip(truth_t[::8], truth_v[::8]))) candidates = [] for name, snaps in series.items(): times = np.array([t for t, _ in snaps]) rgb_idx = [i for i, t in enumerate(times) if truth_t.size and truth_t[0] <= t <= truth_t[-1] and not any(abs(t - m) < 0.5 for m in mono_t[:1] + mono_t[-1:]) and not (mono_t and mono_t[0] - 1 <= t <= mono_t[-1] + 1)] if len(rgb_idx) < 6: continue size = len(snaps[0][1]) // 4 * 4 mat = np.frombuffer(b"".join(snaps[i][1][:size] for i in rgb_idx), dtype=" 0) & finite, (m * r0[:, None]).sum(axis=0) / denom, 0) for w in np.argsort(-np.abs(r))[:args.top]: if abs(r[w]) >= args.min_r: candidates.append((abs(r[w]), r[w], name, int(w) * 4)) candidates.sort(reverse=True) print(f"\n{len(candidates)} words with |r| >= {args.min_r} against the RGB light value") for absr, r, name, off in candidates[:args.top]: snaps = series[name] vals = [struct.unpack_from("