Status, plan and log for the show machine
Approved by Gavin 18 Oct 15:36 (with session A review 15:45: no all-ropes white frame; lit mask from bit/inverse pairs). Progress: B.md.
river-local-oct-18.toe)Plan item L. Today TD sends Art-Net to 20 ropes (18 light; ropes 9 and 17, 1-based, are dark) and HDMI to HUB75 quad 1. Rope positions are only known per pile (rope map, 14:11). The goal is a self-contained checkpoint that needs no Modal or web: a camera-space rain/river field, sampled onto every rope pixel at the point where the gallery camera sees it, plus HUB75 quad 1. Seen from the camera, the result should read as one coherent piece.
State found (14:47): TD 2025.33230 is running river.toe at 60 fps with the bridge OK. Ropes are Active, Source=black, 0 W. Panels are Source=test with the window open. The gallery cam is H.264 2880×1616 @ 40 fps. The repo is clean at 4a4f464. The room is sunny and people walk through.
git pull. Create docs/sessions/B.md (header ## 2026-10-18 HH:MM start: bitcube · Opus 5.5 plus the step list B1–B8 below). In docs/execution-plan.md, split the B row into B1…B8 (➡️). Commit and push.B.md with the commit hash, the Status-table mark, and a push. Decisions go under ### Waiting on Gavin..venv/ to .gitignore. Create river-bitcube\.venv (py 3.13) with numpy, pillow and pytest, plus tools/pixelmap/requirements.txt.TD side (td/ropes/artnet_out.py, td/build/build_ropes.py):
gray_frame(ropes, pixels, bit, invert). Bits 0–4 encode Gray(rope index 0–19). Bits 5–13 encode Gray(pixel index 0–399). Lit = white.gray (new pars Graybit 0–13 and Grayinvert) and white. Rebuild the menu with build_ropes.py (it is idempotent and only appends missing pars).tools/pixelmap/graycode.py (pure): gray(n) and ungray(g), plus the pattern list: black, white, then 14 × (bit, inverse) = 30 patterns.
tools/pixelmap/capture.py (venv Python):
-rtsp_transport tcp, scaled to 1440×808, rawvideo pipe). A thread keeps the latest frame.127.0.0.1:9980/exec, so Source, bit and invert change atomically.frames/pixelmap-<stamp>/{A,B}/<pattern>.png and meta.json, and records drift (black at start vs black at end).black and Brightness restored. Takes about 2–3 minutes.tools/pixelmap/decode.py: pure functions, each written test-first against synthetic frames in tools/pixelmap/tests/.
lit_mask(white, black): the camera pixels that rope light reaches.decode_bits(pos, neg): bit = sign(pos − neg), confidence = |
pos − neg | / (white − black). |
disagreement_mask(A, B): drops camera pixels where the two passes differ, such as a person moving.decode(frames): per camera pixel → (rope, px, conf). All 5 rope bits must be confident. Pixel bits may let the lowest 1–2 bits run weak; that is recorded in conf.aggregate(...): per (rope, px) → confidence-weighted median (u, v) and a sample count. Visible means count ≥ N.reject_outliers(...): drops points far from the median of their along-rope neighbours, such as floor reflections and stray decodes.fill_along_rope(...): linear interpolation between visible neighbours. Ends hold the nearest visible point. Ropes with no samples are left out.tools/pixelmap/pixelmap-2026-10-18.json (meta: camera, resolution, capture stamp, drift, UV convention; points: rope 0-based, px, u, v normalised in TD convention with origin bottom-left, visible). It also writes frames/pixelmap-<stamp>/overlay.png: decoded pixels coloured by rope, with the px gradient as brightness and rope labels.tools/pixelmap/hub75.py (pure plus TDD; capture reuses the reader in capture.py):
Source=test vs black, twice each, ropes black.hub75_quad1, and adds them to the overlay./project1/river_local base COMP, field 1280×720 (camera aspect; the Non-Commercial cap is 1280):
tx drifts with absTime (slow) and tz evolves it. Then a Level TOP for contrast, so about half the area is black. Then a Lookup TOP with a Ramp TOP: black → deep blue → teal → white highlights. Breathing: the Level brightness expression is 0.6 + 0.25*sin(2π·absTime/12).null field_out.Rainspeed, Raindensity, Flowspeed, Breathe, Gain) so B7 can tune them live.rope*400+px) and columns u and v. Ropes with no mapping get u = v = −1. DAT to CHOP gives channels u and v.field_out, with the UV CHOP connected to its input; verified that it has one CHOP input, interp linear, and extend = default with defcolor 0, so −1 samples as black) outputs r, g, b × 8,000 samples. Then a Shuffle CHOP splits them into 20 groups of 400. CHOP to TOP (RGB per row) gives 400×20, and a Flip TOP (flop) turns it into 20×400 rope_canvas with the top row = px 0 (plug end), which is what canvas_frame() expects.
field_out.numpyArray() at the table’s UVs in numpy and compares the result with artnet_out.canvas_frame(rope_tex). Max error should be ≤ 2/255./project1/ropes: Canvas = /project1/river_local/rope_canvas, crops 0…1, Source=canvas.field_out to 256×128 as quad1. Panels get Quad1 = that TOP, Quad2 = a black Constant TOP, Quad2off on, and Source=canvas.tools/td_save.ps1: add -Target <name.toe>. The default becomes the currently open project file (not hard-coded river.toe), so a plain save in the local file can never overwrite river.toe. Keep the general.inc check, the timestamp check and the stray-<base>.N.toe cleanup per target.river.toe is clean in git.td_save.ps1 -Target river-local-oct-18.toe. This is a Save As of the running river.toe, which is unchanged except for live state, and TD now runs the copy.td.ps1 -File td/build/build_ropes.py, then the new td/build/build_river_local.py. It is idempotent: it creates or updates /project1/river_local, loads the pixel-map JSON into the Table DAT, deactivates Andi’s Mask_Panels/webrender1 (no web) and wires ropes and panels.td_save.ps1.river.toe on disk stays untouched, and the login autostart is not changed. After a reboot, river.toe opens with ropes black and the panels’ test pattern. Commit river-local-oct-18.toe (the gitignore’s *.[0-9]*.toe doesn’t match it).tools\cam_snap.ps1 gallery stills, plus a 10 s ffmpeg clip into frames/. Look for drops travelling down every pile and across the HUB75 in step, the river drift reading left to right, a black background and no flicker.Brightness (≤ 0.4) and panels Brightness to balance the HUB75 against the ropes.artnet_out.status() watts per box, project.cookRate and the actual fps over 10 s, and dropped frames. Save the tuned values into the build script’s defaults and the .toe.docs/img/rain-checkpoint-2026-10-18.jpg. Optionally add an overlay image if it is people-free.docs/index.md (still image, “Status as of” line, Next list), tick L in docs/plan.md, add a log entry (including the new td_save default and the gray/white sources), and refresh the CLAUDE.md facts if useful./code-review on the session’s diff and fix findings. Add the SESSION SUMMARY to B.md, push, and print it.river-local-oct-18.toe with the rain on the ropes and quad 1. Autostart still points at river.toe.tools/pixelmap/{graycode,capture,decode,hub75}.py, tools/pixelmap/tests/test_*.py, tools/pixelmap/requirements.txt, tools/pixelmap/pixelmap-2026-10-18.json, td/build/build_river_local.py, river-local-oct-18.toe, docs/sessions/B.md, docs/img/rain-checkpoint-2026-10-18.jpg.td/ropes/artnet_out.py (gray/white sources), td/build/build_ropes.py (menu and pars), tools/td_save.ps1 (-Target), .gitignore (.venv/), docs (plan.md, index.md, execution-plan.md, log).tools/td.ps1, cam_snap.ps1, artnet_out.canvas_frame/status, build_panels.py Source/Quad pars, and the median/drift ideas from rope_identify.py..venv\Scripts\python -m pytest tools/pixelmap -q: all green before running on real data.rope_tex within 2/255.