Two results and one retraction, all from the same session. REFUTED: 'an ~8-10 s idle returns to the title' does not apply to the main menu. Held untouched it stayed put for >= 60 s, correlation never leaving 0.9245-0.9249. That timer is real but belongs to the TITLE. It was the only reason 'B leaves the main menu' was classed as authored, so Q5's B rule is upgraded to measured-ordering: B is delivered (canary logs vk=5801) and is the only input in >= 100 s before the return. The PRESS (A) plate: the boot title presents build 4 WITHOUT the plate first -- green-glyph 154, against 159 on the committed no-plate capture and 753/977/1493 on plate titles -- and the plate arrives after. That is the port's third option. RETRACTED: four durations taken the same day. classify_array costs 1503 ms per frame; running it per frame against an 8 fps x11grab drained the pipe at 0.64 fps, so every classified frame was stale and increasingly so. It manufactured 'plate 24.66 s after the title art', 'B->title 15.58 s', 'B->title 25.60 s' and 'A->menu 20.26 s'. The tell: a transition, a press and a fade do not share a duration, and the two B figures GREW across a longer run. A backlog preserves ordering and destroys durations, which is why the sequence results above stand and every timing does not. The ring's period is unaffected and that was checked, not assumed -- ring_period ran at 15.03 fps against a requested 15. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01KNR5Y79D1T4bBr6gJQaWFP
88 lines
3.4 KiB
Python
88 lines
3.4 KiB
Python
#!/usr/bin/env python3
|
|
"""Does (B) leave the main menu -- and does the menu self-return to the title?
|
|
|
|
HANDOFF downgraded "(B) on the main menu returns to the title" to authored,
|
|
because the corpus also carries "an ~8-10 s idle returns to the title" and one
|
|
unrecorded observation cannot separate the two causes. This separates them by
|
|
ordering: hold the menu UNTOUCHED for an idle window several times longer than
|
|
the claimed 8-10 s and timestamp what happens, THEN press (B) and timestamp
|
|
again. If the idle window passes with the menu still up, the idle cause is
|
|
gone and the (B) observation is unambiguous.
|
|
|
|
Screen identity comes from screen_match.py, whose control includes the movie
|
|
frames that broke the statistics-based oracle.
|
|
|
|
Usage: menu_b_probe.py IDLE_SECONDS AFTER_SECONDS
|
|
"""
|
|
import os, subprocess, sys, time
|
|
import numpy as np
|
|
from PIL import Image
|
|
|
|
SD = os.path.dirname(os.path.abspath(__file__))
|
|
sys.path.insert(0, SD)
|
|
from screen_match import classify_array
|
|
|
|
W, H = 1280, 720
|
|
IDLE = float(sys.argv[1]) if len(sys.argv) > 1 else 60.0
|
|
AFTER = float(sys.argv[2]) if len(sys.argv) > 2 else 30.0
|
|
OUT = "/sylph-home/re/ringcap"
|
|
|
|
|
|
def stream():
|
|
return subprocess.Popen(
|
|
["ffmpeg", "-loglevel", "error", "-f", "x11grab", "-draw_mouse", "0",
|
|
"-video_size", f"{W}x{H}", "-i", ":98", "-r", "4",
|
|
"-f", "rawvideo", "-pix_fmt", "rgb24", "-"],
|
|
stdout=subprocess.PIPE, bufsize=W * H * 3 * 2)
|
|
|
|
|
|
def main():
|
|
p = stream(); n = W * H * 3
|
|
t0 = time.time(); seg = t0; last = None; prev = None
|
|
phase = "IDLE"; pressed_at = None
|
|
log = []
|
|
while True:
|
|
el = time.time() - t0
|
|
if phase == "IDLE" and el >= IDLE:
|
|
subprocess.run(["python3", f"{SD}/pad.py", "tap", "B", "0.3"], check=False)
|
|
pressed_at = time.time() - t0
|
|
print(f"t={pressed_at:6.2f}s >>> (B) PRESSED", flush=True)
|
|
phase = "AFTER"
|
|
if phase == "AFTER" and el >= IDLE + AFTER:
|
|
break
|
|
if time.time() - seg > 25:
|
|
p.kill(); p = stream(); seg = time.time()
|
|
b = p.stdout.read(n)
|
|
if len(b) < n:
|
|
p.kill(); p = stream(); seg = time.time(); continue
|
|
a = np.frombuffer(b, np.uint8).reshape(H, W, 3)
|
|
last = a
|
|
c, sc = classify_array(a)
|
|
log.append((el, c, sc["title"], sc["menu"]))
|
|
if c != prev:
|
|
print(f"t={el:6.2f}s screen={c:<6} title={sc['title']:+.3f} "
|
|
f"menu={sc['menu']:+.3f}", flush=True)
|
|
Image.fromarray(a).save(f"{OUT}/b-{el:06.2f}-{c}.png")
|
|
prev = c
|
|
p.kill()
|
|
with open(f"{OUT}/menu-b-trace.tsv", "w") as f:
|
|
f.write("t_s\tscreen\tcorr_title\tcorr_menu\n")
|
|
for r in log:
|
|
f.write(f"{r[0]:.3f}\t{r[1]}\t{r[2]:.4f}\t{r[3]:.4f}\n")
|
|
idle = [r for r in log if r[0] < IDLE]
|
|
aft = [r for r in log if pressed_at and r[0] > pressed_at + 1.0]
|
|
print(f"\nIDLE phase : {len(idle)} samples over {IDLE:.0f}s, "
|
|
f"screens seen = {sorted(set(r[1] for r in idle))}")
|
|
print(f"AFTER (B) : {len(aft)} samples, "
|
|
f"screens seen = {sorted(set(r[1] for r in aft))}")
|
|
first_title = next((r[0] for r in aft if r[1] == "title"), None)
|
|
if first_title:
|
|
print(f" first 'title' at t={first_title:.2f}s = "
|
|
f"{first_title - pressed_at:.2f}s after the (B) press")
|
|
print(f"trace: {OUT}/menu-b-trace.tsv")
|
|
return 0
|
|
|
|
|
|
if __name__ == "__main__":
|
|
sys.exit(main())
|