Report articulated sweep overlaps
This commit is contained in:
parent
34183e6a79
commit
dc3fe32bb3
2 changed files with 38 additions and 1 deletions
|
|
@ -296,3 +296,4 @@
|
|||
{"ts": "2026-05-31T23:14:03+01:00", "type": "implementation", "status": "ok", "note": "Changed music_keyed_move_chirp_sync.py visual pulses from square on/off flashes into sampled Move visual words. The default mimir.move_visual_contour_word.v1 uses identity color bias, attack/release envelope, symbol-dependent hue glide, tremolo articulation, emphasized Move lane, and an explicit visual_gesture_hz rate knob. Synthetic dry smoke wrote contour samples; a short non-dry Nightwing HID test at 80 Hz completed without writer failure. The camera-decodable rate still needs offline recovery scoring from captured frames."}
|
||||
{"ts": "2026-05-31T23:31:00+01:00", "type": "implementation", "status": "ok", "note": "sync_sweep.py now accepts a mimir.music_keyed_move_chirp_plan.v1 as the shared gesture authority for articulated chirps and chirp-flashes. The sweep copies/renders the plan WAV, sends exact Move visual contour samples over HID, attaches RGB visual_contour decoder scores to video receipts, and runs Mimir perfect-machine live receipts for local Leap/Kiyos plus Raven/Eve configs unless disabled. This fixes the earlier DirectShow/Nightwing-only sweep omission path: Leap/Eve are explicit receipt surfaces instead of assumed present."}
|
||||
{"ts": "2026-05-31T23:48:00+01:00", "type": "verification", "status": "partial", "note": "Ran an articulated all-sensor sweep from a live Scarlett-derived music plan at artifacts/runtime/all-sensor-articulated-20260531-232349. ASIO loopback channels 2/3 matched 77 chirp events with 0.083/0.292 ms median residuals; Nightwing builtin cam and nw-eye-1 each emitted 100/100 Move observations; Kiyo Pro/Kiyo captured RGB video and offline crunch extracted 47/51 marker observations. tools/crunch_sync_sweep.py wrote 298 total marker observations to calibration-observations.json/csv. Missing witnesses are explicit: nw-eye-0 timed out, Nightwing mic only matched one chirp, Focusrite DShow was unavailable while ASIO succeeded, LeapUVC KS open failed, and Raven/Eve receiver runtime saw no camera textures."}
|
||||
{"ts": "2026-05-31T23:54:00+01:00", "type": "verification", "status": "ok", "note": "The articulated sweep crunch now reports pairwise shared marker events for calibration solve selection. In the 20260531-232349 run: Nightwing builtin cam and nw-eye-1 share 100 events; Kiyo shares 51 with each Nightwing view; Kiyo Pro shares 47 with each Nightwing view; Kiyo and Kiyo Pro share 28. This is enough correspondence substrate for initial multi-view frustum fitting on the sensors that actually saw the Move spheres."}
|
||||
|
|
|
|||
|
|
@ -208,12 +208,16 @@ def main() -> int:
|
|||
for name in ("kiyo-pro", "kiyo"):
|
||||
sensors.append(crunch_video(sweep_dir, name, events, args))
|
||||
observations = [obs for sensor in sensors for obs in sensor.get("observations", [])]
|
||||
pair_overlaps = compute_pair_overlaps(observations)
|
||||
event_coverage = compute_event_coverage(observations)
|
||||
document = {
|
||||
"kind": "mimir.articulated_sync_observation_crunch.v1",
|
||||
"sweep_dir": str(sweep_dir),
|
||||
"event_count": len(events),
|
||||
"sensor_count": len(sensors),
|
||||
"observation_count": len(observations),
|
||||
"pair_overlaps": pair_overlaps,
|
||||
"event_coverage": event_coverage,
|
||||
"sensors": sensors,
|
||||
}
|
||||
output_json = Path(args.output_json) if args.output_json else sweep_dir / "calibration-observations.json"
|
||||
|
|
@ -224,9 +228,41 @@ def main() -> int:
|
|||
writer = csv.DictWriter(handle, fieldnames=fields, extrasaction="ignore")
|
||||
writer.writeheader()
|
||||
writer.writerows(observations)
|
||||
print(json.dumps({"output_json": str(output_json), "output_csv": str(output_csv), "observation_count": len(observations)}, indent=2))
|
||||
print(json.dumps({
|
||||
"output_json": str(output_json),
|
||||
"output_csv": str(output_csv),
|
||||
"observation_count": len(observations),
|
||||
"pair_overlaps": pair_overlaps,
|
||||
}, indent=2))
|
||||
return 0 if observations else 1
|
||||
|
||||
|
||||
def compute_pair_overlaps(observations: list[dict]) -> list[dict]:
|
||||
by_event: dict[int, dict[str, dict]] = {}
|
||||
for obs in observations:
|
||||
event_index = int(obs["event_index"])
|
||||
by_event.setdefault(event_index, {})[str(obs["source_id"])] = obs
|
||||
pairs: dict[tuple[str, str], int] = {}
|
||||
for sensors in by_event.values():
|
||||
names = sorted(sensors)
|
||||
for left_index, left in enumerate(names):
|
||||
for right in names[left_index + 1:]:
|
||||
pairs[(left, right)] = pairs.get((left, right), 0) + 1
|
||||
return [
|
||||
{"left": left, "right": right, "shared_events": count}
|
||||
for (left, right), count in sorted(pairs.items(), key=lambda item: (-item[1], item[0]))
|
||||
]
|
||||
|
||||
|
||||
def compute_event_coverage(observations: list[dict]) -> list[dict]:
|
||||
by_event: dict[int, set[str]] = {}
|
||||
for obs in observations:
|
||||
by_event.setdefault(int(obs["event_index"]), set()).add(str(obs["source_id"]))
|
||||
return [
|
||||
{"event_index": event_index, "sensor_count": len(sources), "sources": sorted(sources)}
|
||||
for event_index, sources in sorted(by_event.items())
|
||||
]
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
|
|
|
|||
Loading…
Reference in a new issue