From 91bf95849edbc9fce4361ab88e62f3ccc776f00e Mon Sep 17 00:00:00 2001 From: Michael B Reiser Date: Thu, 29 Oct 2026 13:05:04 -0400 Subject: [PATCH] =?UTF-8?q?protocols=20+=20rig=20records=20=E2=86=92=20pri?= =?UTF-8?q?vate=20repos=20(LAB-227)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Rig-specific protocols and per-rig records leave the public repo so a rig's protocol tweaks never need a webDisplayTools release. Rule: a protocol stays here only if a tool, test, QC profile or fixture depends on it. - Removed (copied first to reiserlab/arena-experiments 8db48e6 and reiserlab/scanimage-2026-bergamo 00bccfd, each with PROVENANCE.md): the three bias_camera_closed_loop_poc* protocols (→ protocols/ww18/), fictrac_bias_test, fictrac_direction_test, fictrac_coupling_bias_fly_test, led_activation_quadrant_test (+ patterns), mode3_drill_1trial (→ protocols/shared/); the three 2P protocols + two pattern folders (→ bergamo protocols/); the ww18 and okey-cokey (ww14) run sheets, handover, dashboard plan, BIAS config and .ps1 start scripts (→ rigs/ww18/, rigs/ww14/). - protocols/index.json drops the moved keys; protocols/MOVED.md maps every old path to its new home. - tests/test-protocol-roundtrip-v3.js parses the 2P sweep from a fixture copy (tests/fixtures/v3_g6_2p_color_led_sweep.yaml). - scripts/make-color-optomotor-patterns.js --gs16 and scripts/make-2p-diagnostic-patterns.js take --out (the 2P targets are no longer in this repo; without --out they explain where they went and exit 2). - rig/README.md, rig/WINDOWS-TEST.md, the protocol-yaml skill, two course docs and the decision record's "stays" row point at the new locations. Co-Authored-By: Claude Fable 5.1 --- .agents/skills/protocol-yaml/SKILL.md | 5 +- course/cshl-2026/docs/fictrac.md | 2 +- course/cshl-2026/docs/protocols/README.md | 3 +- .../rig-software-repos-and-runners.md | 2 +- protocols/MOVED.md | 29 ++ protocols/bias_camera_closed_loop_poc.yaml | 136 ----- .../bias_camera_closed_loop_poc_ww18.yaml | 139 ----- ...as_camera_closed_loop_poc_ww18_soak10.yaml | 140 ----- protocols/fictrac_bias_test.yaml | 306 ----------- protocols/fictrac_coupling_bias_fly_test.yaml | 493 ------------------ protocols/fictrac_direction_test.yaml | 271 ---------- .../G6_2x10_toprow_grat20_blue_gs16.pat | Bin 81338 -> 0 bytes .../G6_2x10_toprow_grat20_green_gs16.pat | Bin 81338 -> 0 bytes .../G6_2x10_toprow_grat20_violet_gs16.pat | Bin 81338 -> 0 bytes .../G6_2x10_toprow_grat20_yellow_gs16.pat | Bin 81338 -> 0 bytes .../g6_2p_color_led_sweep_patterns/README.txt | 15 - protocols/g6_2x10_2p_diagnostics.yaml | 226 -------- .../G6_2x10_ff_flash_0_15.pat | Bin 8150 -> 0 bytes .../G6_2x10_ff_ramp16.pat | Bin 65074 -> 0 bytes .../G6_2x10_ff_steps_pr16.pat | Bin 65074 -> 0 bytes .../G6_2x10_sparse10.pat | Bin 4084 -> 0 bytes .../README.txt | 11 - protocols/g6_2x10_2p_duty_sweep.yaml | 267 ---------- protocols/index.json | 50 -- protocols/led_activation_quadrant_test.yaml | 212 -------- .../001_all_on.pat | Bin 1084 -> 0 bytes .../002_grating_sq.pat | Bin 21338 -> 0 bytes .../003_grating_sine.pat | Bin 81338 -> 0 bytes .../004_frame2_h_ccw_200f.pat | Bin 813218 -> 0 bytes protocols/mode3_drill_1trial.yaml | 67 --- rig/HANDOVER-okey-cokey-2026-10-21.md | 102 ---- rig/README.md | 6 +- rig/WINDOWS-TEST.md | 8 +- rig/config/bias/okey-cokey-bfs-u3-51s5m.json | 253 --------- .../okey-cokey-foxglove-dashboard-plan.md | 176 ------- rig/okey-cokey-pc-2026-10-21.md | 167 ------ rig/okey-cokey-pc-results-2026-10-21.md | 36 -- rig/okey-cokey-pc-setup.md | 133 ----- rig/okey-cokey-pc-ww14-setup-2026-10-22.md | 87 ---- rig/tools/bias-ufmf.ps1 | 41 -- rig/tools/okey-cokey-start.ps1 | 83 --- rig/windows-results-ww18-2026-10-15.md | 231 -------- scripts/make-2p-diagnostic-patterns.js | 74 ++- scripts/make-color-optomotor-patterns.js | 25 +- .../fixtures/v3_g6_2p_color_led_sweep.yaml | 0 tests/test-protocol-roundtrip-v3.js | 7 +- 46 files changed, 122 insertions(+), 3681 deletions(-) create mode 100644 protocols/MOVED.md delete mode 100644 protocols/bias_camera_closed_loop_poc.yaml delete mode 100644 protocols/bias_camera_closed_loop_poc_ww18.yaml delete mode 100644 protocols/bias_camera_closed_loop_poc_ww18_soak10.yaml delete mode 100644 protocols/fictrac_bias_test.yaml delete mode 100644 protocols/fictrac_coupling_bias_fly_test.yaml delete mode 100644 protocols/fictrac_direction_test.yaml delete mode 100644 protocols/g6_2p_color_led_sweep_patterns/G6_2x10_toprow_grat20_blue_gs16.pat delete mode 100644 protocols/g6_2p_color_led_sweep_patterns/G6_2x10_toprow_grat20_green_gs16.pat delete mode 100644 protocols/g6_2p_color_led_sweep_patterns/G6_2x10_toprow_grat20_violet_gs16.pat delete mode 100644 protocols/g6_2p_color_led_sweep_patterns/G6_2x10_toprow_grat20_yellow_gs16.pat delete mode 100644 protocols/g6_2p_color_led_sweep_patterns/README.txt delete mode 100644 protocols/g6_2x10_2p_diagnostics.yaml delete mode 100644 protocols/g6_2x10_2p_diagnostics_patterns/G6_2x10_ff_flash_0_15.pat delete mode 100644 protocols/g6_2x10_2p_diagnostics_patterns/G6_2x10_ff_ramp16.pat delete mode 100644 protocols/g6_2x10_2p_diagnostics_patterns/G6_2x10_ff_steps_pr16.pat delete mode 100644 protocols/g6_2x10_2p_diagnostics_patterns/G6_2x10_sparse10.pat delete mode 100644 protocols/g6_2x10_2p_diagnostics_patterns/README.txt delete mode 100644 protocols/g6_2x10_2p_duty_sweep.yaml delete mode 100644 protocols/led_activation_quadrant_test.yaml delete mode 100644 protocols/led_activation_quadrant_test_patterns/001_all_on.pat delete mode 100644 protocols/led_activation_quadrant_test_patterns/002_grating_sq.pat delete mode 100644 protocols/led_activation_quadrant_test_patterns/003_grating_sine.pat delete mode 100644 protocols/led_activation_quadrant_test_patterns/004_frame2_h_ccw_200f.pat delete mode 100644 protocols/mode3_drill_1trial.yaml delete mode 100644 rig/HANDOVER-okey-cokey-2026-10-21.md delete mode 100644 rig/config/bias/okey-cokey-bfs-u3-51s5m.json delete mode 100644 rig/docs/okey-cokey-foxglove-dashboard-plan.md delete mode 100644 rig/okey-cokey-pc-2026-10-21.md delete mode 100644 rig/okey-cokey-pc-results-2026-10-21.md delete mode 100644 rig/okey-cokey-pc-setup.md delete mode 100644 rig/okey-cokey-pc-ww14-setup-2026-10-22.md delete mode 100644 rig/tools/bias-ufmf.ps1 delete mode 100644 rig/tools/okey-cokey-start.ps1 delete mode 100644 rig/windows-results-ww18-2026-10-15.md rename protocols/g6_2p_color_led_sweep.yaml => tests/fixtures/v3_g6_2p_color_led_sweep.yaml (100%) diff --git a/.agents/skills/protocol-yaml/SKILL.md b/.agents/skills/protocol-yaml/SKILL.md index 8324f96b..005b3ea9 100644 --- a/.agents/skills/protocol-yaml/SKILL.md +++ b/.agents/skills/protocol-yaml/SKILL.md @@ -55,7 +55,8 @@ it does NOT advance the protocol clock. Only `wait` commands do. Therefore: Editing only ONE of the pair is **masked** by the other — "the timeline won't update" is almost always a half-edited pair, not a render bug. - **Bind both fields to one anchor** (`duration: *show_dur` on both) so they can never - drift — the `open_dur`/`cl_dur` pattern from `protocols/fictrac_direction_test`. + drift — the `open_dur`/`cl_dur` pattern from `fictrac_direction_test` (private + `reiserlab/arena-experiments`, `protocols/shared/`). - Waits between two `trialParams` in one condition define how long the FIRST one actually shows (the second overrides it). Waits longer than the duration = intentional hold/blank; fine, but mean it. @@ -163,7 +164,7 @@ rejection). Authored as an added rotational **velocity**: A negative frequency is a harmless no-op (warned): negate `bias_amplitude` instead. - `stopClosedLoop` clears the bias automatically. Reference: `docs/development/closed-loop-bias.md`; worked example: - `protocols/fictrac_bias_test.yaml`. + `protocols/shared/fictrac_bias_test.yaml` in the private `reiserlab/arena-experiments`. **Per-trial start position = the trialParams `frame_index` (Studio v0.91 / bridge 3.4).** A closed-loop trial opens on the Mode-3 `trialParams` `frame_index` (0-based, wraps mod the pattern's diff --git a/course/cshl-2026/docs/fictrac.md b/course/cshl-2026/docs/fictrac.md index 3ae857ee..0a7a7221 100644 --- a/course/cshl-2026/docs/fictrac.md +++ b/course/cshl-2026/docs/fictrac.md @@ -119,7 +119,7 @@ Closed-loop and the live scope need the **bridge** running on the rig computer: bridge"* to a live trace once data flows. Closed-loop trials show a green **CLOSED LOOP** tag. -**Quick check:** load [`fictrac_direction_test`](protocols/README.md) — it runs +**Quick check:** load [`fictrac_direction_test`](protocols/README.md) (since 2026-10-29 in the lab's private `reiserlab/arena-experiments`, `protocols/shared/`) — it runs open-loop motion first, then a closed-loop block that the fly steers. If the closed-loop block responds to nudging the ball, the bridge is working. diff --git a/course/cshl-2026/docs/protocols/README.md b/course/cshl-2026/docs/protocols/README.md index 6daded02..963fbed8 100644 --- a/course/cshl-2026/docs/protocols/README.md +++ b/course/cshl-2026/docs/protocols/README.md @@ -53,7 +53,8 @@ Animated pattern previews are embedded on all four protocol pages. FicTrac): full-field brightness, panel map, a moving bar, and LED levels 2/5/10/20/40%. Use it to confirm a rig works after setup. - **`fictrac_direction_test`** — open-loop motion then a closed-loop block, to - confirm the [FicTrac](../fictrac.md) bridge is working. + confirm the [FicTrac](../fictrac.md) bridge is working. Since 2026-10-29 it lives in + the lab's private `reiserlab/arena-experiments` (`protocols/shared/`), not in the site library. - **`optomotor_led_test`** — a slow grating with the LED switched on for the last few seconds; the reference example of "fire a command partway through a trial." diff --git a/docs/development/rig-software-repos-and-runners.md b/docs/development/rig-software-repos-and-runners.md index 37ec574d..8df3ffc8 100644 --- a/docs/development/rig-software-repos-and-runners.md +++ b/docs/development/rig-software-repos-and-runners.md @@ -111,7 +111,7 @@ stay as history. | **pyDisplayTools** | `tools/patfile.py` (pattern-file reader; the repo already has `pyPatternTools`), `nodes/arena_display.py` (Python port of `js/panel-color.js`, okey-cokey branch), the pure functions of `fictrac-bridge/bridge.py` (`Pipeline` mapping, `frame_schema_line`, `compact_arena_command`), `fictrac-bridge/fictrac_sim.py` moves to rig-nodes as a device simulator | | **stays** (gateway, export, composition) | `nodes/bridge_node.py`, `nodes/mcap_convert.py`, `nodes/_rows.py` (behavior_v2 `kf` rows), `nodes/export_log.py`, `nodes/rig.py` (arena composition; its generic preflight/supervision helpers are extracted to rig-nodes first), `pixi.toml`, `pixi.lock`, `.gitignore`, `tests/studio_emulator.py`, `tests/check_runlog_readers.js`, the gateway half of `tests/test_ros2_poc.py` (node-level tests move with their nodes) | | **stays** (arena visualization) | `nodes/viz_node.py`, `tests/test_viz_node.py`, the arena derivation in `nodes/foxglove_json.py`, `foxglove/arena-fictrac.json`, `foxglove/arena-bias.json`, `docs/foxglove-arena-layout.json`, `tools/tiff_run_to_mcap.py`; from okey-cokey: `nodes/stimulus_node.py`, `nodes/stimulus_core.py`, `nodes/dashboard_replay.py`, `tests/test_stimulus.py`, `tests/test_stimulus_live.py`, `foxglove/arena-okey-cokey.json`, `tests/fixtures/protocols-2026-10-14/*` | -| **stays** (rig-specific config and records) | `README.md`, `WINDOWS-TEST.md`, `SCANIMAGE-DAY.md`, `scanimage-day-2026-10-16.md`, `windows-results-ww18-2026-10-15.md`, `docs/plan-b-explainer*.html`, `docs/build-standalone-explainer.py`, `docs/img/*`; from okey-cokey: `config/bias/okey-cokey-bfs-u3-51s5m.json`, `tools/okey-cokey-start.ps1`, `tools/bias-ufmf.ps1`, the `okey-cokey-*.md` run sheets, `HANDOVER-okey-cokey-2026-10-21.md`, `docs/okey-cokey-foxglove-dashboard-plan.md` | +| **stays** (tool code and generic records) | `README.md`, `WINDOWS-TEST.md`, `docs/plan-b-explainer*.html`, `docs/build-standalone-explainer.py`, `docs/img/*`, `foxglove/arena-okey-cokey.json` (a layout `rig.py --foxglove` loads), `tools/ufmf.py`, `tools/patfile.py`. **Moved 2026-10-29 (LAB-227) to the private `reiserlab/arena-experiments` `rigs//`:** `windows-results-ww18-2026-10-15.md` → `rigs/ww18/`; from okey-cokey → `rigs/ww14/`: `config/bias/okey-cokey-bfs-u3-51s5m.json`, `tools/okey-cokey-start.ps1`, `tools/bias-ufmf.ps1`, the `okey-cokey-*.md` run sheets, `HANDOVER-okey-cokey-2026-10-21.md`, `docs/okey-cokey-foxglove-dashboard-plan.md`. `SCANIMAGE-DAY.md` and `scanimage-day-2026-10-16.md` went to the private 2P repo with #284. | #### Amendment 2026-10-28: the imaging work is private diff --git a/protocols/MOVED.md b/protocols/MOVED.md new file mode 100644 index 00000000..b3c06e27 --- /dev/null +++ b/protocols/MOVED.md @@ -0,0 +1,29 @@ +# Protocols and rig records that left this repo (2026-10-29, LAB-227) + +Experiment material now lives in private repos so that a rig's protocol tweaks never need a +webDisplayTools release. The rule: a protocol stays here only if a tool, test, QC profile or doc +fixture depends on it. Copies were made from commit `59a377b` (Release 2026-10-28); each destination +has a `PROVENANCE.md`. Open a moved protocol in the Studio, signed in, with +`arena_studio.html?repo=&p=`. + +| Was here | Now (repo → path) | +|---|---| +| `protocols/bias_camera_closed_loop_poc.yaml` | `reiserlab/arena-experiments` → `protocols/ww18/bias_camera_closed_loop_poc.yaml` | +| `protocols/bias_camera_closed_loop_poc_ww18.yaml` | `reiserlab/arena-experiments` → `protocols/ww18/bias_camera_closed_loop_poc_ww18.yaml` | +| `protocols/bias_camera_closed_loop_poc_ww18_soak10.yaml` | `reiserlab/arena-experiments` → `protocols/ww18/bias_camera_closed_loop_poc_ww18_soak10.yaml` | +| `protocols/fictrac_bias_test.yaml` | `reiserlab/arena-experiments` → `protocols/shared/fictrac_bias_test.yaml` | +| `protocols/fictrac_direction_test.yaml` | `reiserlab/arena-experiments` → `protocols/shared/fictrac_direction_test.yaml` | +| `protocols/fictrac_coupling_bias_fly_test.yaml` | `reiserlab/arena-experiments` → `protocols/shared/fictrac_coupling_bias_fly_test.yaml` | +| `protocols/led_activation_quadrant_test.yaml` + `_patterns/` | `reiserlab/arena-experiments` → `protocols/shared/led_activation_quadrant_test.yaml` + `_patterns/` | +| `protocols/mode3_drill_1trial.yaml` | `reiserlab/arena-experiments` → `protocols/shared/mode3_drill_1trial.yaml` | +| `protocols/g6_2p_color_led_sweep.yaml` + `_patterns/` | `reiserlab/scanimage-2026-bergamo` → `protocols/g6_2p_color_led_sweep.yaml` + `_patterns/` | +| `protocols/g6_2x10_2p_diagnostics.yaml` + `_patterns/` | `reiserlab/scanimage-2026-bergamo` → `protocols/g6_2x10_2p_diagnostics.yaml` + `_patterns/` | +| `protocols/g6_2x10_2p_duty_sweep.yaml` | `reiserlab/scanimage-2026-bergamo` → `protocols/g6_2x10_2p_duty_sweep.yaml` | +| `rig/windows-results-ww18-2026-10-15.md` | `reiserlab/arena-experiments` → `rigs/ww18/` | +| `rig/okey-cokey-pc-*.md`, `rig/HANDOVER-okey-cokey-2026-10-21.md`, `rig/docs/okey-cokey-foxglove-dashboard-plan.md`, `rig/config/bias/okey-cokey-bfs-u3-51s5m.json`, `rig/tools/okey-cokey-start.ps1`, `rig/tools/bias-ufmf.ps1` | `reiserlab/arena-experiments` → `rigs/ww14/` (same names; `config/` and `tools/` subfolders) | + +A copy of `g6_2p_color_led_sweep.yaml` stays as the test fixture `tests/fixtures/v3_g6_2p_color_led_sweep.yaml`. +The pattern generators `scripts/make-color-optomotor-patterns.js --gs16` and +`scripts/make-2p-diagnostic-patterns.js` now take `--out ` pointing into the 2P repo. + +Pages under `docs/development/` that cite the old paths are historical records and were not edited. diff --git a/protocols/bias_camera_closed_loop_poc.yaml b/protocols/bias_camera_closed_loop_poc.yaml deleted file mode 100644 index 19069352..00000000 --- a/protocols/bias_camera_closed_loop_poc.yaml +++ /dev/null @@ -1,136 +0,0 @@ -version: 3 - -# BIAS camera + FicTrac closed loop through the ROS 2 bridge node (LAB-303 connectors). -# The `camera` plugin (BiasPlugin) is driven by the WEB runner: each command goes to the -# bridge as connector_command, the bridge's BIAS node runs it over BIAS's HTTP port, and -# the reply (with the video artifact for startRecording) lands in the run log. -# -# cam_start : connect → startRecording (one video for the whole run) → getTimestamp -# 2 closed-loop trials (none, then a constant +90°/s bias), 15 s each -# cam_sync : getTimestamp (a mid-run sync point against the bridge clock) -# cam_stop : getTimestamp → stopRecording -# -# Video path: relative to the bias node's --video-dir (like MATLAB's experiment folder). - -experiment_info: - name: "BIAS camera + closed loop — ROS 2 PoC Mac bench (LAB-303)" - author: "webDisplayTools" - -rig: "./configs/rigs/cshl_g6_2x10_ball.yaml" - -plugins: - - name: "fictrac" - type: "class" - matlab: - class: "FicTracPlugin" - config: - bridge_url: "ws://localhost:8765" - fictrac_port: 60000 - proto: "udp" - offset: 0 - - name: "camera" - type: "class" - matlab: - class: "BiasPlugin" - -variables: - cl_dur: &cl_dur 15 - -experiment: - - name: "camera_run" - trials: - - "cam_start" - - "cl_bias_none" - - "cam_sync" - - "cl_bias_constant" - - "cam_stop" - repetitions: 1 - - "shutdown" - -conditions: - - name: "cam_start" - commands: - - type: "plugin" - plugin_name: "camera" - command_name: "connect" - - type: "plugin" - plugin_name: "camera" - command_name: "startRecording" - params: - filename: "poc_studio_run.avi" - - type: "plugin" - plugin_name: "camera" - command_name: "getTimestamp" - - type: "wait" - duration: 1 - - - name: "cl_bias_none" - commands: - - type: "controller" - command_name: "trialParams" - pattern: "G6_2x10_grat_rot_20px_50pct_4c-B-Y" - pattern_ID: 1 - duration: *cl_dur - mode: 3 - frame_index: 0 - frame_rate: 0 - gain: 0 - - type: "plugin" - plugin_name: "fictrac" - command_name: "startClosedLoop" - params: - bias_type: "none" - - type: "wait" - duration: *cl_dur - - type: "plugin" - plugin_name: "fictrac" - command_name: "stopClosedLoop" - - - name: "cam_sync" - commands: - - type: "plugin" - plugin_name: "camera" - command_name: "getTimestamp" - - type: "wait" - duration: 1 - - - name: "cl_bias_constant" - commands: - - type: "controller" - command_name: "trialParams" - pattern: "G6_2x10_grat_rot_20px_50pct_4c-B-Y" - pattern_ID: 1 - duration: *cl_dur - mode: 3 - frame_index: 0 - frame_rate: 0 - gain: 0 - - type: "plugin" - plugin_name: "fictrac" - command_name: "startClosedLoop" - params: - bias_type: "constant" - bias_amplitude: 90 - - type: "wait" - duration: *cl_dur - - type: "plugin" - plugin_name: "fictrac" - command_name: "stopClosedLoop" - - - name: "cam_stop" - commands: - - type: "plugin" - plugin_name: "camera" - command_name: "getTimestamp" - - type: "plugin" - plugin_name: "camera" - command_name: "stopRecording" - - type: "wait" - duration: 1 - - - name: "shutdown" - commands: - - type: "controller" - command_name: "stopDisplay" - - type: "controller" - command_name: "allOff" diff --git a/protocols/bias_camera_closed_loop_poc_ww18.yaml b/protocols/bias_camera_closed_loop_poc_ww18.yaml deleted file mode 100644 index 7a3d89c9..00000000 --- a/protocols/bias_camera_closed_loop_poc_ww18.yaml +++ /dev/null @@ -1,139 +0,0 @@ -version: 3 -# ww18 variant (2026-10-15): the site library's G6 2x10 rotating square grating, SD slot 2 -# (patterns/g6_2x10/patterns/002_grating_sq.pat, 20 frames), in place of the Mac bench card's -# G6_2x10_grat_rot_20px_50pct_4c-B-Y. Otherwise identical to bias_camera_closed_loop_poc.yaml. - -# BIAS camera + FicTrac closed loop through the ROS 2 bridge node (LAB-303 connectors). -# The `camera` plugin (BiasPlugin) is driven by the WEB runner: each command goes to the -# bridge as connector_command, the bridge's BIAS node runs it over BIAS's HTTP port, and -# the reply (with the video artifact for startRecording) lands in the run log. -# -# cam_start : connect → startRecording (one video for the whole run) → getTimestamp -# 2 closed-loop trials (none, then a constant +90°/s bias), 15 s each -# cam_sync : getTimestamp (a mid-run sync point against the bridge clock) -# cam_stop : getTimestamp → stopRecording -# -# Video path: relative to the bias node's --video-dir (like MATLAB's experiment folder). - -experiment_info: - name: "BIAS camera + closed loop — ROS 2 PoC, Windows rig ww18 (LAB-303)" - author: "webDisplayTools" - -rig: "./configs/rigs/cshl_g6_2x10_ball.yaml" - -plugins: - - name: "fictrac" - type: "class" - matlab: - class: "FicTracPlugin" - config: - bridge_url: "ws://localhost:8765" - fictrac_port: 60000 - proto: "udp" - offset: 0 - - name: "camera" - type: "class" - matlab: - class: "BiasPlugin" - -variables: - cl_dur: &cl_dur 15 - -experiment: - - name: "camera_run" - trials: - - "cam_start" - - "cl_bias_none" - - "cam_sync" - - "cl_bias_constant" - - "cam_stop" - repetitions: 1 - - "shutdown" - -conditions: - - name: "cam_start" - commands: - - type: "plugin" - plugin_name: "camera" - command_name: "connect" - - type: "plugin" - plugin_name: "camera" - command_name: "startRecording" - params: - filename: "poc_studio_run.avi" - - type: "plugin" - plugin_name: "camera" - command_name: "getTimestamp" - - type: "wait" - duration: 1 - - - name: "cl_bias_none" - commands: - - type: "controller" - command_name: "trialParams" - pattern: "grating_sq" - pattern_ID: 2 - duration: *cl_dur - mode: 3 - frame_index: 0 - frame_rate: 0 - gain: 0 - - type: "plugin" - plugin_name: "fictrac" - command_name: "startClosedLoop" - params: - bias_type: "none" - - type: "wait" - duration: *cl_dur - - type: "plugin" - plugin_name: "fictrac" - command_name: "stopClosedLoop" - - - name: "cam_sync" - commands: - - type: "plugin" - plugin_name: "camera" - command_name: "getTimestamp" - - type: "wait" - duration: 1 - - - name: "cl_bias_constant" - commands: - - type: "controller" - command_name: "trialParams" - pattern: "grating_sq" - pattern_ID: 2 - duration: *cl_dur - mode: 3 - frame_index: 0 - frame_rate: 0 - gain: 0 - - type: "plugin" - plugin_name: "fictrac" - command_name: "startClosedLoop" - params: - bias_type: "constant" - bias_amplitude: 90 - - type: "wait" - duration: *cl_dur - - type: "plugin" - plugin_name: "fictrac" - command_name: "stopClosedLoop" - - - name: "cam_stop" - commands: - - type: "plugin" - plugin_name: "camera" - command_name: "getTimestamp" - - type: "plugin" - plugin_name: "camera" - command_name: "stopRecording" - - type: "wait" - duration: 1 - - - name: "shutdown" - commands: - - type: "controller" - command_name: "stopDisplay" - - type: "controller" - command_name: "allOff" diff --git a/protocols/bias_camera_closed_loop_poc_ww18_soak10.yaml b/protocols/bias_camera_closed_loop_poc_ww18_soak10.yaml deleted file mode 100644 index 1cdcd5c8..00000000 --- a/protocols/bias_camera_closed_loop_poc_ww18_soak10.yaml +++ /dev/null @@ -1,140 +0,0 @@ -version: 3 -# ww18 SOAK variant (2026-10-15): both closed-loop epochs 300 s (~10 min run). -# ww18 variant (2026-10-15): the site library's G6 2x10 rotating square grating, SD slot 2 -# (patterns/g6_2x10/patterns/002_grating_sq.pat, 20 frames), in place of the Mac bench card's -# G6_2x10_grat_rot_20px_50pct_4c-B-Y. Otherwise identical to bias_camera_closed_loop_poc.yaml. - -# BIAS camera + FicTrac closed loop through the ROS 2 bridge node (LAB-303 connectors). -# The `camera` plugin (BiasPlugin) is driven by the WEB runner: each command goes to the -# bridge as connector_command, the bridge's BIAS node runs it over BIAS's HTTP port, and -# the reply (with the video artifact for startRecording) lands in the run log. -# -# cam_start : connect → startRecording (one video for the whole run) → getTimestamp -# 2 closed-loop trials (none, then a constant +90°/s bias), 15 s each -# cam_sync : getTimestamp (a mid-run sync point against the bridge clock) -# cam_stop : getTimestamp → stopRecording -# -# Video path: relative to the bias node's --video-dir (like MATLAB's experiment folder). - -experiment_info: - name: "BIAS camera + closed loop — ROS 2 PoC, Windows rig ww18, 10-min soak (2 x 300 s closed loop) (LAB-303)" - author: "webDisplayTools" - -rig: "./configs/rigs/cshl_g6_2x10_ball.yaml" - -plugins: - - name: "fictrac" - type: "class" - matlab: - class: "FicTracPlugin" - config: - bridge_url: "ws://localhost:8765" - fictrac_port: 60000 - proto: "udp" - offset: 0 - - name: "camera" - type: "class" - matlab: - class: "BiasPlugin" - -variables: - cl_dur: &cl_dur 300 - -experiment: - - name: "camera_run" - trials: - - "cam_start" - - "cl_bias_none" - - "cam_sync" - - "cl_bias_constant" - - "cam_stop" - repetitions: 1 - - "shutdown" - -conditions: - - name: "cam_start" - commands: - - type: "plugin" - plugin_name: "camera" - command_name: "connect" - - type: "plugin" - plugin_name: "camera" - command_name: "startRecording" - params: - filename: "poc_studio_run.avi" - - type: "plugin" - plugin_name: "camera" - command_name: "getTimestamp" - - type: "wait" - duration: 1 - - - name: "cl_bias_none" - commands: - - type: "controller" - command_name: "trialParams" - pattern: "grating_sq" - pattern_ID: 2 - duration: *cl_dur - mode: 3 - frame_index: 0 - frame_rate: 0 - gain: 0 - - type: "plugin" - plugin_name: "fictrac" - command_name: "startClosedLoop" - params: - bias_type: "none" - - type: "wait" - duration: *cl_dur - - type: "plugin" - plugin_name: "fictrac" - command_name: "stopClosedLoop" - - - name: "cam_sync" - commands: - - type: "plugin" - plugin_name: "camera" - command_name: "getTimestamp" - - type: "wait" - duration: 1 - - - name: "cl_bias_constant" - commands: - - type: "controller" - command_name: "trialParams" - pattern: "grating_sq" - pattern_ID: 2 - duration: *cl_dur - mode: 3 - frame_index: 0 - frame_rate: 0 - gain: 0 - - type: "plugin" - plugin_name: "fictrac" - command_name: "startClosedLoop" - params: - bias_type: "constant" - bias_amplitude: 90 - - type: "wait" - duration: *cl_dur - - type: "plugin" - plugin_name: "fictrac" - command_name: "stopClosedLoop" - - - name: "cam_stop" - commands: - - type: "plugin" - plugin_name: "camera" - command_name: "getTimestamp" - - type: "plugin" - plugin_name: "camera" - command_name: "stopRecording" - - type: "wait" - duration: 1 - - - name: "shutdown" - commands: - - type: "controller" - command_name: "stopDisplay" - - type: "controller" - command_name: "allOff" diff --git a/protocols/fictrac_bias_test.yaml b/protocols/fictrac_bias_test.yaml deleted file mode 100644 index 2e936592..00000000 --- a/protocols/fictrac_bias_test.yaml +++ /dev/null @@ -1,306 +0,0 @@ -version: 3 - -# ============================================================================ -# FicTrac closed-loop BIAS (disturbance) test — web runner only. -# -# A "bias" is a smooth disturbance added to the closed loop, so the display -# keeps rotating even when the fly holds still. Sweeping it lets you measure how -# well the fly REJECTS the disturbance (does it counter-turn to null the motion?). -# -# Uses ONLY built-in g6_2x10 patterns already on the SD card: -# 4 frame2_h_ccw_200f (a 200-frame full-azimuth rotation) -# -# The bias is authored as an added rotational VELOCITY in deg/s (`bias_amplitude` -# = the PEAK velocity), which the bridge integrates into a bias ANGLE and folds -# into its heading→frame-index mapping. At the rig's 1.8°/frame pitch (360/200 px), -# a bias of 90 deg/s advances the display 90°/s = 50 frames/s — independent of the -# closed-loop `coupling` (default 1 = the display follows the ball 1:1). -# -# bias_type what the display does (with a still fly) -# none nothing — the plain closed-loop baseline -# constant rotates steadily, one direction, forever -# sine swings smoothly back and forth over ±A/(2πf) -# square constant-speed rotation that reverses, over ±A/(4f) -# -# DIRECTION (confirmed on the arena, 2026-08-04, at the normal positive gain 1.8): -# a POSITIVE bias_amplitude turns the display CLOCKWISE — equivalently, the pattern -# sweeps rightward across the fly's visual field. Negative is counter-clockwise. -# This is the sign your analysis must assume; get it backwards and a fly correctly -# counter-turning against the disturbance will read as following it. -# -# The periodic waveforms are ZERO-MEAN in position: the display is pushed equally -# left and right rather than drifting to one side, and always starts at the frame -# it was already on (no jump at onset). Note the excursion SHRINKS as frequency -# rises for a fixed amplitude — at 90 deg/s the sine covers ±28.6° at 0.5 Hz but -# only ±14.3° at 1 Hz. Full semantics: docs/development/closed-loop-bias.md. -# -# DURATION knobs (`variables:` below): cl_dur drives every trial's trialParams -# duration AND its bracketing wait, so they always move together. -# -# TIMING — the `wait` is load-bearing in every condition here: it holds the -# closed loop ACTIVE for cl_dur before stopClosedLoop fires. Drop it and the -# stop fires immediately, so no disturbance would ever be seen. -# -# Total ≈ 4 min at defaults (8 trials × 30 s). Needs the FicTrac bridge -# (`pixi run bridge`); with no fly, `pixi run sim` supplies synthetic heading. -# NOTE (web-first): MATLAB has no FicTracPlugin (its closed loop is analog -# Mode 4, computed on the controller, where the host cannot inject a bias), so -# this protocol runs on the WEB runner only. -# ============================================================================ - -experiment_info: - name: "FicTrac closed-loop bias (disturbance) test" - author: "webDisplayTools" - -rig: "./configs/rigs/cshl_g6_2x10_ball.yaml" - -plugins: - - name: "fictrac" - type: "class" - matlab: - class: "FicTracPlugin" - config: - bridge_url: "ws://localhost:8765" - fictrac_port: 60000 - proto: "udp" - offset: 0 - -variables: - # ── Duration knob ────────────────────────────────────────────────────────── - cl_dur: &cl_dur 30 # seconds per closed-loop trial (drives duration AND wait) - # ── Disturbance knobs ────────────────────────────────────────────────────── - # amp is the PEAK velocity in deg/s; freq only applies to sine/square. Change - # these once here (or in the Edit view's Variables panel) to rescale the sweep. - bias_amp: &bias_amp 90 # 90 deg/s = 50 frames/s at gain 1.8 - bias_amp_slow: &bias_amp_slow 30 # a gentler disturbance for comparison - bias_freq: &bias_freq 0.5 # Hz — one back-and-forth cycle every 2 s - bias_freq_fast: &bias_freq_fast 1.0 # Hz — halves the position excursion - -experiment: - - name: "bias_sweep" - trials: - - "cl_bias_none" - - "cl_bias_constant" - - "cl_bias_constant_rev" - - "cl_bias_sine" - - "cl_bias_sine_fast" - - "cl_bias_sine_slow" - - "cl_bias_square" - - "cl_bias_square_fast" - repetitions: 1 - - "shutdown" - -conditions: - # NOTE: no explicit fictrac connect/disconnect. The runner auto-connects the - # bridge at run start, and must NOT disconnect it at the end — the recorded-run - # log is exported from the bridge AFTER the sequence finishes, so a - # protocol-level disconnect would close the bridge before the log commits. - - # ---- baseline: closed loop with NO disturbance ------------------------------ - # The control condition. Omitting bias_type entirely is equivalent to - # bias_type "none"; it is spelled out here so the contrast is obvious. - - name: "cl_bias_none" - commands: - - type: "controller" - command_name: "trialParams" - pattern: "frame2_h_ccw_200f" - pattern_ID: 4 - duration: *cl_dur - mode: 3 - frame_index: 0 - frame_rate: 0 - gain: 0 - - type: "plugin" - plugin_name: "fictrac" - command_name: "startClosedLoop" - params: - bias_type: "none" - - type: "wait" - duration: *cl_dur - - type: "plugin" - plugin_name: "fictrac" - command_name: "stopClosedLoop" - - # ---- constant: a steady drift the fly must walk against -------------------- - # bias_frequency is ignored by `constant` (there is no period), so it is omitted. - # NEGATE bias_amplitude to reverse the direction — negating the FREQUENCY does - # nothing, because the waveform is even in frequency. - - name: "cl_bias_constant" - commands: - - type: "controller" - command_name: "trialParams" - pattern: "frame2_h_ccw_200f" - pattern_ID: 4 - duration: *cl_dur - mode: 3 - frame_index: 0 - frame_rate: 0 - gain: 0 - - type: "plugin" - plugin_name: "fictrac" - command_name: "startClosedLoop" - params: - bias_type: "constant" - bias_amplitude: *bias_amp - - type: "wait" - duration: *cl_dur - - type: "plugin" - plugin_name: "fictrac" - command_name: "stopClosedLoop" - - - name: "cl_bias_constant_rev" - commands: - - type: "controller" - command_name: "trialParams" - pattern: "frame2_h_ccw_200f" - pattern_ID: 4 - duration: *cl_dur - mode: 3 - frame_index: 0 - frame_rate: 0 - gain: 0 - - type: "plugin" - plugin_name: "fictrac" - command_name: "startClosedLoop" - params: - bias_type: "constant" - bias_amplitude: -90 - - type: "wait" - duration: *cl_dur - - type: "plugin" - plugin_name: "fictrac" - command_name: "stopClosedLoop" - - # ---- sine: the classic frequency-response disturbance ---------------------- - # Position excursion is ±A/(2πf): ±28.6° at 90 deg/s + 0.5 Hz, ±14.3° at 1 Hz. - - name: "cl_bias_sine" - commands: - - type: "controller" - command_name: "trialParams" - pattern: "frame2_h_ccw_200f" - pattern_ID: 4 - duration: *cl_dur - mode: 3 - frame_index: 0 - frame_rate: 0 - gain: 0 - - type: "plugin" - plugin_name: "fictrac" - command_name: "startClosedLoop" - params: - bias_type: "sine" - bias_amplitude: *bias_amp - bias_frequency: *bias_freq - - type: "wait" - duration: *cl_dur - - type: "plugin" - plugin_name: "fictrac" - command_name: "stopClosedLoop" - - - name: "cl_bias_sine_fast" - commands: - - type: "controller" - command_name: "trialParams" - pattern: "frame2_h_ccw_200f" - pattern_ID: 4 - duration: *cl_dur - mode: 3 - frame_index: 0 - frame_rate: 0 - gain: 0 - - type: "plugin" - plugin_name: "fictrac" - command_name: "startClosedLoop" - params: - bias_type: "sine" - bias_amplitude: *bias_amp - bias_frequency: *bias_freq_fast - - type: "wait" - duration: *cl_dur - - type: "plugin" - plugin_name: "fictrac" - command_name: "stopClosedLoop" - - - name: "cl_bias_sine_slow" - commands: - - type: "controller" - command_name: "trialParams" - pattern: "frame2_h_ccw_200f" - pattern_ID: 4 - duration: *cl_dur - mode: 3 - frame_index: 0 - frame_rate: 0 - gain: 0 - - type: "plugin" - plugin_name: "fictrac" - command_name: "startClosedLoop" - params: - bias_type: "sine" - bias_amplitude: *bias_amp_slow - bias_frequency: *bias_freq - - type: "wait" - duration: *cl_dur - - type: "plugin" - plugin_name: "fictrac" - command_name: "stopClosedLoop" - - # ---- square: constant-speed rotation that reverses ------------------------- - # Position is a symmetric triangle spanning ±A/(4f): ±45° at 90 deg/s + 0.5 Hz. - - name: "cl_bias_square" - commands: - - type: "controller" - command_name: "trialParams" - pattern: "frame2_h_ccw_200f" - pattern_ID: 4 - duration: *cl_dur - mode: 3 - frame_index: 0 - frame_rate: 0 - gain: 0 - - type: "plugin" - plugin_name: "fictrac" - command_name: "startClosedLoop" - params: - bias_type: "square" - bias_amplitude: *bias_amp - bias_frequency: *bias_freq - - type: "wait" - duration: *cl_dur - - type: "plugin" - plugin_name: "fictrac" - command_name: "stopClosedLoop" - - - name: "cl_bias_square_fast" - commands: - - type: "controller" - command_name: "trialParams" - pattern: "frame2_h_ccw_200f" - pattern_ID: 4 - duration: *cl_dur - mode: 3 - frame_index: 0 - frame_rate: 0 - gain: 0 - - type: "plugin" - plugin_name: "fictrac" - command_name: "startClosedLoop" - params: - bias_type: "square" - bias_amplitude: *bias_amp - bias_frequency: *bias_freq_fast - - type: "wait" - duration: *cl_dur - - type: "plugin" - plugin_name: "fictrac" - command_name: "stopClosedLoop" - - # ---- shutdown -------------------------------------------------------------- - # Blank the arena only. Do NOT disconnect the bridge here — the run-log export - # runs after the sequence, and needs the bridge still connected. - - name: "shutdown" - commands: - - type: "controller" - command_name: "stopDisplay" - - type: "controller" - command_name: "allOff" diff --git a/protocols/fictrac_coupling_bias_fly_test.yaml b/protocols/fictrac_coupling_bias_fly_test.yaml deleted file mode 100644 index 21ce8efb..00000000 --- a/protocols/fictrac_coupling_bias_fly_test.yaml +++ /dev/null @@ -1,493 +0,0 @@ -version: 3 - -# ============================================================================ -# Closed-loop COUPLING × BIAS — a first test on a real fly (web runner, Mode 3) -# -# Needs Arena Studio v0.85+ and bridge 3.3+ (`pixi run bridge` prints the version). -# Pattern: the built-in 200-frame full-azimuth rotation on the G6 2×10 card: -# 4 frame2_h_ccw_200f (1 frame = 1 azimuth pixel = 1.8°, the pitch the rig supplies) -# -# COUPLING SIGN. On the fly-on-ball rigs the NATURAL, stabilizing closed loop is -# coupling **−1** (it is exactly what the old `gain: -1.8` did: same frame index for -# the same turn). `k_nat` below is that value; every other coupling is expressed -# relative to it, so flipping ONE number adapts the whole protocol to a +1 rig: -# k_nat -1 natural closed loop (the display counter-rotates the fly's turn 1:1) -# k_under -0.75 the world turns LESS than the ball -# k_over -1.25 the world turns MORE than the ball -# k_rev +1 reversed — the sanity check; the fly should NOT be able to stabilize -# -# BIAS SIGN (bridge 3.3): the disturbance is defined in DISPLAY degrees and is NOT -# scaled by the coupling — a positive bias_amplitude turns the display CLOCKWISE from -# above (pattern sweeps rightward across the fly's view) on every rig, including the -# −1 rigs. (Under the old `gain: -1.8` it was counter-clockwise — do not carry that over.) -# -# BLOCKS (20 s closed-loop trials, 5 s dark ITI, 2 repetitions each, randomized): -# A. coupling only: k_nat · k_under · k_over · k_rev -# B. bias at k_nat: constant ±30 °/s (one revolution per 12 s) -# sine 30 °/s @ 0.25 Hz (position ±19°) · sine 60 °/s @ 0.5 Hz (±19°) -# square 30 °/s @ 0.25 Hz (position ±30°) -# C. bias × coupling: k_under + constant 30 · k_over + constant 30 -# k_under + sine 30 @ 0.25 · k_over + sine 30 @ 0.25 -# coupling 0 + constant 30 — OPEN-LOOP REPLAY: the display -# ignores the fly and just drifts; the control for block B -# plus a k_nat baseline at the start and the end. ≈ 13 min total. -# -# WHAT TO LOOK FOR. Bridge 3.3 keeps the heading unwrapped, so the fractional -# couplings must show NO once-per-revolution jump (the old `gain` bug). The report -# (`pixi run python scripts/closed-loop-report.py --svg cl.svg`) gives, -# per trial, the fly's turning, the feature's frontal fraction, the reconstructed bias -# and a REJECTION index (1 = fully counter-turned the bias, 0 = ignored it). Expect a -# high frontal fraction at k_nat and none at k_rev; rejection > 0 on block B; ≈ 0 -# rejection and a steady 30 °/s drift on the coupling-0 control; block C shows how a -# weaker / stronger loop changes the rejection. -# -# Dry run without a fly: `pixi run sim -- --model fly --kp 2 --frame-dir -1` for a −1 -# rig (docs/development/model-fly-bench.md). Web-only: MATLAB has no FicTracPlugin. -# ============================================================================ - -experiment_info: - name: "Closed-loop coupling × bias — fly test" - author: "webDisplayTools" - -rig: "./configs/rigs/cshl_g6_2x10_ball.yaml" - -plugins: - - name: "fictrac" - type: "class" - matlab: - class: "FicTracPlugin" - config: - bridge_url: "ws://localhost:8765" - fictrac_port: 60000 - proto: "udp" - offset: 0 - -variables: - # ── durations (drive trialParams duration AND the closed-loop wait together) ── - cl_dur: &cl_dur 20 # seconds per closed-loop trial - iti_dur: &iti_dur 5 # dark inter-trial interval, s - # ── couplings — flip the signs of all four for a +1 rig ───────────────────── - k_nat: &k_nat -1 # natural closed loop on the fly-on-ball rigs (= the old gain -1.8) - k_under: &k_under -0.75 # the world turns 0.75× the ball - k_over: &k_over -1.25 # the world turns 1.25× the ball - k_rev: &k_rev 1 # reversed — the fly cannot stabilize - # ── disturbances (peak velocity deg/s, frequency Hz) — gentle on purpose ───── - amp_slow: &_slow 30 # 30 deg/s: one revolution per 12 s - amp_fast: &_fast 60 # 60 deg/s at twice the frequency = the same ±19° excursion - f_slow: &f_slow 0.25 # Hz — one back-and-forth every 4 s - f_fast: &f_fast 0.5 # Hz - -experiment: - - "cl_baseline" - - name: "A_coupling" - trials: - - "cl_nat" - - "cl_under" - - "cl_over" - - "cl_rev" - repetitions: 2 - randomize: true - intertrial: "dark_iti" - - name: "B_bias_at_natural" - trials: - - "cl_bias_const_pos" - - "cl_bias_const_neg" - - "cl_bias_sine_slow" - - "cl_bias_sine_fast" - - "cl_bias_square" - repetitions: 2 - randomize: true - intertrial: "dark_iti" - - name: "C_bias_x_coupling" - trials: - - "cl_under_bias_const" - - "cl_over_bias_const" - - "cl_under_bias_sine" - - "cl_over_bias_sine" - - "cl_k0_bias_const" - repetitions: 2 - randomize: true - intertrial: "dark_iti" - - "cl_baseline" - - "shutdown" - -conditions: - # NOTE: no explicit fictrac connect/disconnect — the runner auto-connects the bridge - # at run start and the run log is exported from the bridge AFTER the sequence, so a - # protocol-level disconnect would lose the log. - - # ---- baseline: natural closed loop, no disturbance ------------------------- - - name: "cl_baseline" - commands: - - type: "controller" - command_name: "trialParams" - pattern: "frame2_h_ccw_200f" - pattern_ID: 4 - duration: *cl_dur - mode: 3 - frame_index: 0 - frame_rate: 0 - gain: 0 - - type: "plugin" - plugin_name: "fictrac" - command_name: "startClosedLoop" - params: - coupling: *k_nat - - type: "wait" - duration: *cl_dur - - type: "plugin" - plugin_name: "fictrac" - command_name: "stopClosedLoop" - - # ---- A. coupling sweep ------------------------------------------------------- - - name: "cl_nat" - commands: - - type: "controller" - command_name: "trialParams" - pattern: "frame2_h_ccw_200f" - pattern_ID: 4 - duration: *cl_dur - mode: 3 - frame_index: 0 - frame_rate: 0 - gain: 0 - - type: "plugin" - plugin_name: "fictrac" - command_name: "startClosedLoop" - params: - coupling: *k_nat - - type: "wait" - duration: *cl_dur - - type: "plugin" - plugin_name: "fictrac" - command_name: "stopClosedLoop" - - - name: "cl_under" - commands: - - type: "controller" - command_name: "trialParams" - pattern: "frame2_h_ccw_200f" - pattern_ID: 4 - duration: *cl_dur - mode: 3 - frame_index: 0 - frame_rate: 0 - gain: 0 - - type: "plugin" - plugin_name: "fictrac" - command_name: "startClosedLoop" - params: - coupling: *k_under - - type: "wait" - duration: *cl_dur - - type: "plugin" - plugin_name: "fictrac" - command_name: "stopClosedLoop" - - - name: "cl_over" - commands: - - type: "controller" - command_name: "trialParams" - pattern: "frame2_h_ccw_200f" - pattern_ID: 4 - duration: *cl_dur - mode: 3 - frame_index: 0 - frame_rate: 0 - gain: 0 - - type: "plugin" - plugin_name: "fictrac" - command_name: "startClosedLoop" - params: - coupling: *k_over - - type: "wait" - duration: *cl_dur - - type: "plugin" - plugin_name: "fictrac" - command_name: "stopClosedLoop" - - - name: "cl_rev" - commands: - - type: "controller" - command_name: "trialParams" - pattern: "frame2_h_ccw_200f" - pattern_ID: 4 - duration: *cl_dur - mode: 3 - frame_index: 0 - frame_rate: 0 - gain: 0 - - type: "plugin" - plugin_name: "fictrac" - command_name: "startClosedLoop" - params: - coupling: *k_rev - - type: "wait" - duration: *cl_dur - - type: "plugin" - plugin_name: "fictrac" - command_name: "stopClosedLoop" - - # ---- B. bias at the natural coupling ----------------------------------------- - - name: "cl_bias_const_pos" - commands: - - type: "controller" - command_name: "trialParams" - pattern: "frame2_h_ccw_200f" - pattern_ID: 4 - duration: *cl_dur - mode: 3 - frame_index: 0 - frame_rate: 0 - gain: 0 - - type: "plugin" - plugin_name: "fictrac" - command_name: "startClosedLoop" - params: - coupling: *k_nat - bias_type: "constant" - bias_amplitude: *amp_slow # clockwise from above - - type: "wait" - duration: *cl_dur - - type: "plugin" - plugin_name: "fictrac" - command_name: "stopClosedLoop" - - - name: "cl_bias_const_neg" - commands: - - type: "controller" - command_name: "trialParams" - pattern: "frame2_h_ccw_200f" - pattern_ID: 4 - duration: *cl_dur - mode: 3 - frame_index: 0 - frame_rate: 0 - gain: 0 - - type: "plugin" - plugin_name: "fictrac" - command_name: "startClosedLoop" - params: - coupling: *k_nat - bias_type: "constant" - bias_amplitude: -30 # counter-clockwise (negate the amplitude, never the frequency) - - type: "wait" - duration: *cl_dur - - type: "plugin" - plugin_name: "fictrac" - command_name: "stopClosedLoop" - - - name: "cl_bias_sine_slow" - commands: - - type: "controller" - command_name: "trialParams" - pattern: "frame2_h_ccw_200f" - pattern_ID: 4 - duration: *cl_dur - mode: 3 - frame_index: 0 - frame_rate: 0 - gain: 0 - - type: "plugin" - plugin_name: "fictrac" - command_name: "startClosedLoop" - params: - coupling: *k_nat - bias_type: "sine" - bias_amplitude: *amp_slow - bias_frequency: *f_slow - - type: "wait" - duration: *cl_dur - - type: "plugin" - plugin_name: "fictrac" - command_name: "stopClosedLoop" - - - name: "cl_bias_sine_fast" - commands: - - type: "controller" - command_name: "trialParams" - pattern: "frame2_h_ccw_200f" - pattern_ID: 4 - duration: *cl_dur - mode: 3 - frame_index: 0 - frame_rate: 0 - gain: 0 - - type: "plugin" - plugin_name: "fictrac" - command_name: "startClosedLoop" - params: - coupling: *k_nat - bias_type: "sine" - bias_amplitude: *amp_fast - bias_frequency: *f_fast - - type: "wait" - duration: *cl_dur - - type: "plugin" - plugin_name: "fictrac" - command_name: "stopClosedLoop" - - - name: "cl_bias_square" - commands: - - type: "controller" - command_name: "trialParams" - pattern: "frame2_h_ccw_200f" - pattern_ID: 4 - duration: *cl_dur - mode: 3 - frame_index: 0 - frame_rate: 0 - gain: 0 - - type: "plugin" - plugin_name: "fictrac" - command_name: "startClosedLoop" - params: - coupling: *k_nat - bias_type: "square" - bias_amplitude: *amp_slow - bias_frequency: *f_slow - - type: "wait" - duration: *cl_dur - - type: "plugin" - plugin_name: "fictrac" - command_name: "stopClosedLoop" - - # ---- C. bias × coupling ------------------------------------------------------ - - name: "cl_under_bias_const" - commands: - - type: "controller" - command_name: "trialParams" - pattern: "frame2_h_ccw_200f" - pattern_ID: 4 - duration: *cl_dur - mode: 3 - frame_index: 0 - frame_rate: 0 - gain: 0 - - type: "plugin" - plugin_name: "fictrac" - command_name: "startClosedLoop" - params: - coupling: *k_under - bias_type: "constant" - bias_amplitude: *amp_slow - - type: "wait" - duration: *cl_dur - - type: "plugin" - plugin_name: "fictrac" - command_name: "stopClosedLoop" - - - name: "cl_over_bias_const" - commands: - - type: "controller" - command_name: "trialParams" - pattern: "frame2_h_ccw_200f" - pattern_ID: 4 - duration: *cl_dur - mode: 3 - frame_index: 0 - frame_rate: 0 - gain: 0 - - type: "plugin" - plugin_name: "fictrac" - command_name: "startClosedLoop" - params: - coupling: *k_over - bias_type: "constant" - bias_amplitude: *amp_slow - - type: "wait" - duration: *cl_dur - - type: "plugin" - plugin_name: "fictrac" - command_name: "stopClosedLoop" - - - name: "cl_under_bias_sine" - commands: - - type: "controller" - command_name: "trialParams" - pattern: "frame2_h_ccw_200f" - pattern_ID: 4 - duration: *cl_dur - mode: 3 - frame_index: 0 - frame_rate: 0 - gain: 0 - - type: "plugin" - plugin_name: "fictrac" - command_name: "startClosedLoop" - params: - coupling: *k_under - bias_type: "sine" - bias_amplitude: *amp_slow - bias_frequency: *f_slow - - type: "wait" - duration: *cl_dur - - type: "plugin" - plugin_name: "fictrac" - command_name: "stopClosedLoop" - - - name: "cl_over_bias_sine" - commands: - - type: "controller" - command_name: "trialParams" - pattern: "frame2_h_ccw_200f" - pattern_ID: 4 - duration: *cl_dur - mode: 3 - frame_index: 0 - frame_rate: 0 - gain: 0 - - type: "plugin" - plugin_name: "fictrac" - command_name: "startClosedLoop" - params: - coupling: *k_over - bias_type: "sine" - bias_amplitude: *amp_slow - bias_frequency: *f_slow - - type: "wait" - duration: *cl_dur - - type: "plugin" - plugin_name: "fictrac" - command_name: "stopClosedLoop" - - # The control: coupling 0 = the display ignores the fly and simply drifts at 30 °/s. - - name: "cl_k0_bias_const" - commands: - - type: "controller" - command_name: "trialParams" - pattern: "frame2_h_ccw_200f" - pattern_ID: 4 - duration: *cl_dur - mode: 3 - frame_index: 0 - frame_rate: 0 - gain: 0 - - type: "plugin" - plugin_name: "fictrac" - command_name: "startClosedLoop" - params: - coupling: 0 - bias_type: "constant" - bias_amplitude: *amp_slow - - type: "wait" - duration: *cl_dur - - type: "plugin" - plugin_name: "fictrac" - command_name: "stopClosedLoop" - - # ---- dark ITI ------------------------------------------------------------------ - - name: "dark_iti" - commands: - - type: "controller" - command_name: "allOff" - - type: "wait" - duration: *iti_dur - - # ---- shutdown ------------------------------------------------------------------ - - name: "shutdown" - commands: - - type: "controller" - command_name: "stopDisplay" - - type: "controller" - command_name: "allOff" diff --git a/protocols/fictrac_direction_test.yaml b/protocols/fictrac_direction_test.yaml deleted file mode 100644 index e4e3f194..00000000 --- a/protocols/fictrac_direction_test.yaml +++ /dev/null @@ -1,271 +0,0 @@ -version: 3 - -# ============================================================================ -# FicTrac closed-loop + open-loop direction/speed test (web runner). -# -# Uses ONLY the built-in g6_2x10 patterns already on the SD card — no new -# patterns needed: -# 2 grating_sq · 3 grating_sine · 4 frame2_h_ccw_200f -# -# BLOCK 1 — OPEN LOOP (Mode 2), ~4 min: -# both gratings (sq, sine) × 3 speeds (5/15/30 Hz) × 2 directions -# (forward = +frame_rate, reverse = −frame_rate) = 12 trials, repeated 2×. -# Each trial displays for `open_dur` seconds. Reverse uses NEGATIVE frame_rate -# (int16, fw #4). -# -# BLOCK 2 — CLOSED LOOP (Mode 3 + FicTrac), ~1.5 min: -# frame2_h_ccw_200f (a 200-frame full-azimuth rotation) host-stepped by FicTrac -# heading through the bridge; gain 1.8 = 360/200 (one azimuth pixel per index), -# `cl_dur` seconds per trial, repeated 3×. -# -# DURATION knobs (`variables:` below): change ONE number to rescale a block. -# open_dur → every open-loop trial · cl_dur → the closed-loop trial. -# -# TIMING — no redundant waits. The web runner holds each trial for its -# trialParams `duration` on the HOST (the firmware auto-stop, issue #4, isn't -# landed), so a plain display trial needs NO `wait` — the open-loop trials are -# just a trialParams, and `open_dur` is their single duration knob. The -# closed-loop trial DOES need a wait: it brackets the closed-loop hold -# (startClosedLoop → wait → stopClosedLoop), so `cl_dur` drives both its -# trialParams duration and that wait. -# -# Total ≈ 5.5 min at defaults. Needs the FicTrac bridge (`pixi run bridge`); -# with no fly, `pixi run sim` supplies synthetic heading for the closed-loop -# block. NOTE (web-first): MATLAB has no FicTracPlugin (its closed-loop is -# analog Mode 4), so the closed-loop block runs on the WEB runner only. -# ============================================================================ - -experiment_info: - name: "FicTrac closed-loop + open-loop direction/speed test" - author: "webDisplayTools" - -rig: "./configs/rigs/cshl_g6_2x10_ball.yaml" - -plugins: - - name: "fictrac" - type: "class" - matlab: - class: "FicTracPlugin" - config: - bridge_url: "ws://localhost:8765" - fictrac_port: 60000 - proto: "udp" - offset: 0 - -variables: - # ── Speed knobs ──────────────────────────────────────────────────────────── - # Change the number here (or in the Edit view's Variables panel) to rescale a - # whole block. Open-loop trials have a single duration field (no wait), so - # open_dur is unambiguous. The closed-loop trial binds BOTH its trialParams - # duration and its bracketing wait to cl_dur, so they move together. - open_dur: &open_dur 10 # Block 1 open-loop grating trials (10 s each) - cl_dur: &cl_dur 30 # Block 2 closed-loop rotation trials (30 s each) - -experiment: - - name: "open_loop_gratings" - trials: - - "sq_fwd_05" - - "sq_fwd_15" - - "sq_fwd_30" - - "sq_rev_05" - - "sq_rev_15" - - "sq_rev_30" - - "sine_fwd_05" - - "sine_fwd_15" - - "sine_fwd_30" - - "sine_rev_05" - - "sine_rev_15" - - "sine_rev_30" - repetitions: 2 - - name: "closed_loop_ball" - trials: - - "closed_loop_rotation" - repetitions: 3 - - "shutdown" - -conditions: - # NOTE: no explicit fictrac connect/disconnect. The runner auto-connects the - # bridge at run start, and — crucially — must NOT disconnect it at the end: - # the recorded-run log is exported from the bridge AFTER the sequence finishes, - # so a protocol-level disconnect would close the bridge before the log commits - # (that broke the log on the first bench run). Let the run own the lifecycle. - - # ---- BLOCK 1: open-loop drifting gratings (Mode 2) ------------------------- - # frame_rate is the frame-advance rate in Hz; its SIGN is the drift direction - # (+ forward, − reverse). No `wait`: the runner holds the trial for `duration` - # (= open_dur) host-side, so each open-loop trial is a single trialParams with - # one duration knob. - - # grating_sq (pattern 2) — forward - - name: "sq_fwd_05" - commands: - - type: "controller" - command_name: "trialParams" - pattern: "grating_sq" - pattern_ID: 2 - duration: *open_dur - mode: 2 - frame_index: 0 - frame_rate: 5 - gain: 0 - - name: "sq_fwd_15" - commands: - - type: "controller" - command_name: "trialParams" - pattern: "grating_sq" - pattern_ID: 2 - duration: *open_dur - mode: 2 - frame_index: 0 - frame_rate: 15 - gain: 0 - - name: "sq_fwd_30" - commands: - - type: "controller" - command_name: "trialParams" - pattern: "grating_sq" - pattern_ID: 2 - duration: *open_dur - mode: 2 - frame_index: 0 - frame_rate: 30 - gain: 0 - # grating_sq (pattern 2) — reverse (negative frame_rate) - - name: "sq_rev_05" - commands: - - type: "controller" - command_name: "trialParams" - pattern: "grating_sq" - pattern_ID: 2 - duration: *open_dur - mode: 2 - frame_index: 0 - frame_rate: -5 - gain: 0 - - name: "sq_rev_15" - commands: - - type: "controller" - command_name: "trialParams" - pattern: "grating_sq" - pattern_ID: 2 - duration: *open_dur - mode: 2 - frame_index: 0 - frame_rate: -15 - gain: 0 - - name: "sq_rev_30" - commands: - - type: "controller" - command_name: "trialParams" - pattern: "grating_sq" - pattern_ID: 2 - duration: *open_dur - mode: 2 - frame_index: 0 - frame_rate: -30 - gain: 0 - - # grating_sine (pattern 3) — forward - - name: "sine_fwd_05" - commands: - - type: "controller" - command_name: "trialParams" - pattern: "grating_sine" - pattern_ID: 3 - duration: *open_dur - mode: 2 - frame_index: 0 - frame_rate: 5 - gain: 0 - - name: "sine_fwd_15" - commands: - - type: "controller" - command_name: "trialParams" - pattern: "grating_sine" - pattern_ID: 3 - duration: *open_dur - mode: 2 - frame_index: 0 - frame_rate: 15 - gain: 0 - - name: "sine_fwd_30" - commands: - - type: "controller" - command_name: "trialParams" - pattern: "grating_sine" - pattern_ID: 3 - duration: *open_dur - mode: 2 - frame_index: 0 - frame_rate: 30 - gain: 0 - # grating_sine (pattern 3) — reverse (negative frame_rate) - - name: "sine_rev_05" - commands: - - type: "controller" - command_name: "trialParams" - pattern: "grating_sine" - pattern_ID: 3 - duration: *open_dur - mode: 2 - frame_index: 0 - frame_rate: -5 - gain: 0 - - name: "sine_rev_15" - commands: - - type: "controller" - command_name: "trialParams" - pattern: "grating_sine" - pattern_ID: 3 - duration: *open_dur - mode: 2 - frame_index: 0 - frame_rate: -15 - gain: 0 - - name: "sine_rev_30" - commands: - - type: "controller" - command_name: "trialParams" - pattern: "grating_sine" - pattern_ID: 3 - duration: *open_dur - mode: 2 - frame_index: 0 - frame_rate: -30 - gain: 0 - - # ---- BLOCK 2: closed-loop rotation (Mode 3 + FicTrac) ---------------------- - # A 200-frame azimuthal rotation loaded static in Mode 3; FicTrac heading then - # drives the frame index for `cl_dur` s (host streams SET_FRAME_POSITION via - # the bridge). gain 1.8 = 360°/200 frames, so the arena rotates with the fly. - # The `wait` is load-bearing here — it holds closed-loop ACTIVE for cl_dur - # before stopClosedLoop fires (drop it and the stop would fire immediately). - - name: "closed_loop_rotation" - commands: - - type: "controller" - command_name: "trialParams" - pattern: "frame2_h_ccw_200f" - pattern_ID: 4 - duration: *cl_dur - mode: 3 - frame_index: 0 - frame_rate: 0 - gain: 0 - - type: "plugin" - plugin_name: "fictrac" - command_name: "startClosedLoop" - - type: "wait" - duration: *cl_dur - - type: "plugin" - plugin_name: "fictrac" - command_name: "stopClosedLoop" - - # ---- shutdown -------------------------------------------------------------- - # Blank the arena only. Do NOT disconnect the bridge here — the run-log export - # runs after the sequence, and needs the bridge still connected. - - name: "shutdown" - commands: - - type: "controller" - command_name: "stopDisplay" - - type: "controller" - command_name: "allOff" diff --git a/protocols/g6_2p_color_led_sweep_patterns/G6_2x10_toprow_grat20_blue_gs16.pat b/protocols/g6_2p_color_led_sweep_patterns/G6_2x10_toprow_grat20_blue_gs16.pat deleted file mode 100644 index 8de7ef748bb8c4eafd9110fe10340bdc0a3f8ac8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 81338 zcmeI5zltSA6vk^uUEN*R@n0YqDEI;!85>9$m`G`0Dk6x17#ax91E}Cz_yoR$_y!^- zVk8R&rfYTGuKQ;w;nH87+oy})B;B`nPW3t8?>j}ES$5~Ww?Fvk&39f6SNE@;Jh=`b z{1HoQ9hKiA}(y~yI zWv@WF>1<96wwJfFvTVjh*^vzP`FAlr9hfEeE#v2Ave_VCl;RO2RG?z200uLacj4lN zildXEaH^z~g9rQ&g$h&-3Y5FP=EPtJdHX6G*o-G>M>5#=4`X^JFiRd<#?4)^nVW2+ zL?}g)&nOrwM#4(VLPe6#D0nm~eh!8lDvM={!JtyK@FN3M4*Mxo49tnaFxWu>3xoaq zWlVPi6aFogJ$x+9?;#OA2TIX3?7sq3`fIAM2o+t!{woYe#m{+N81Z*hD5dz_jSNsZ z+JB(Z-<%i>gPHy+n2`+j;3}qP0~7vH%Xqon+008eQXZ5d$!8P{6(eD#WuYR;XB0df z6*rSd2DUht9hJr5%By?FQp!>1x*-Y`s4z>Ow?We@E_>zCCow%2nDCF_c)8tmjF)Vr zJSaty&nOrwM#4(VLPe6#D0n<7Zleh%5mpHrKr(U1`LM5t}TCB zfx%oF?6)6cx)+%6kKs7EpF74$Hc}dtBFSeI3>70`rDdTa$!8Qi9+jTc!zB6|>&;Zw zcy;fRDdo6xy%2>8R2YlT+uZ3Dm%Z}W-!a_}O!&ueoZQbH<0Km?4N8&ZGYW=^k+9OT zP?6*_3LcP(mnmu@wlJ4UWnp;nij!DMIq6)e^fo63!(h3qPDEI|@wUu~!A`XGvV*<# zO-v606aEPtA0KMR_{c`ef>I>;jDn$JB&@V7R3!O~f(NAHEJELk@)N7rmKN7+cJP>Ljy@yY7!u2MN7@U#8+c#dGtclxn+k6}!auK?N$NyCep~V73PP`1P2s z1G8lHnWJlNen;6zsZfd}pHVPWjD(ezg^DDfQSew)E-@Tk;vJdFC0=uhcTh_0+)Jo^ z?mM6Y725%f9c*^kxh;8#?;EM@Km9tU7X!0o-N+nWv&|f3Bc(zql6*$NP%#o#S{5pj zd`7`zQK^U4`Lt9t+v2lKWs7OIa;B8pxh-|pdIlA!%AjU`Td`mUJA^T)n|^b*=CNiky4=)Nj{@ss2B+=EejP%KBM5VsI*QsTYQ$OY%%Rt z&XiI+x24Wn&!7SoCj9#Ex2hM~Kj@%Wsy=J&R;~Bm{P|-{F9&AH3dhkkcZ{QKq*N$H zlFuj@Dn`Oe%R)tx&nS2-D)}fdpV|&`ZSh&AvcIm$*#g;FH>jDn$JB&@V7R3!O~g2$rL nI@N6PS*EhZv|Bk-O6}a1I%_?H3RIZz&--yOz2dT0-u>x6@4JGi diff --git a/protocols/g6_2p_color_led_sweep_patterns/G6_2x10_toprow_grat20_green_gs16.pat b/protocols/g6_2p_color_led_sweep_patterns/G6_2x10_toprow_grat20_green_gs16.pat deleted file mode 100644 index 38c9d004060046cabc5816498b71d7cbecfcc6db..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 81338 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 Z;dOy=md?XqDrJ`vI5ZC`E~z~H^d;0m diff --git a/protocols/g6_2p_color_led_sweep_patterns/README.txt b/protocols/g6_2p_color_led_sweep_patterns/README.txt deleted file mode 100644 index 9d7971a2..00000000 --- a/protocols/g6_2p_color_led_sweep_patterns/README.txt +++ /dev/null @@ -1,15 +0,0 @@ -Patterns for protocols/g6_2p_color_led_sweep.yaml (G6 2x10 four-color panels, GS16 — lit pixels 15, 20 frames each; -built by scripts/make-color-optomotor-patterns.js --gs16 — regenerate rather than edit). GS16 so the controller -re-streams at 300 Hz (its GS16 default), as in the 22 Sept 2026 2P duty-sweep runs; the GS2 set would default to 1000 Hz. -Upload the whole folder from Arena Studio (Console → Patterns → Add ▾ → this folder); the Studio resolves -patterns by NAME, so the pattern_IDs in the YAML only matter as the fallback when the SD listing is unavailable. - -Each file: square-wave grating, period 20 px = 36°, 10 px on / 10 px off, one pixel of shift toward increasing -column index per frame (20 frames = one period; playback wraps), TOP panel row only (pixel rows 20..39), and only -the pixels of ONE LED color bank lit (2×2-cell bank map of js/panel-color.js, layout "four-color"). -Frame_rate +80 in mode 2 = 4 Hz temporal frequency (80 px/s ÷ 20 px); negative reverses. - -G6_2x10_toprow_grat20_violet_gs16.pat — bank 0 = violet 405 nm; frames 20, lit px/frame 500, stray px 0, 81338 bytes OK -G6_2x10_toprow_grat20_blue_gs16.pat — bank 1 = blue 470 nm; frames 20, lit px/frame 500, stray px 0, 81338 bytes OK -G6_2x10_toprow_grat20_green_gs16.pat — bank 2 = green 525 nm; frames 20, lit px/frame 500, stray px 0, 81338 bytes OK -G6_2x10_toprow_grat20_yellow_gs16.pat — bank 3 = yellow-orange 590 nm; frames 20, lit px/frame 500, stray px 0, 81338 bytes OK diff --git a/protocols/g6_2x10_2p_diagnostics.yaml b/protocols/g6_2x10_2p_diagnostics.yaml deleted file mode 100644 index 0809e7f4..00000000 --- a/protocols/g6_2x10_2p_diagnostics.yaml +++ /dev/null @@ -1,226 +0,0 @@ -version: 3 - -# ============================================================================ -# 2P line-sync DIAGNOSTICS — G6 2x10 arena on the Bergamo (Sept 2026). ≈ 52 s. -# -# Companion to g6_2x10_2p_duty_sweep.yaml. Same wiring and ScanImage setup -# (panel mode 2, DIO 1 = out_debug_framescan → Aux trigger 1 on a free D2.x, -# DIO 2 = in_trigger ← line clock; see the sweep file's header). The SWEEP is -# re-run unchanged for the hardware-only controls (objective capped, arena -# covered, SiPM bias series). THIS file varies the pattern CONTENT at a fixed -# duty, which the sweep cannot do: -# -# The residual offset in the SiPM channels is expected to follow the TOTAL -# light the display emits. Full-field GS16 patterns make that a clean test: -# ramp_128 grey level 0→15 in 16 steps of 0.5 s (ascending) → offset vs emitted light, one trial -# steps_128 the same 16 levels in a fixed pseudo-random order → same, immune to slow drift -# flash_128 full field 0/15 square wave at 1 Hz → the stimulus-locked offset step and its -# build-up/decay kinetics, 8 repeats to average -# sparse_128 10 % of the LEDs at full, static → is 10 % of LEDs at full the same as all LEDs -# at ~10 % (ramp level 1–2)? per-LED vs total light -# flash_255 the 1 Hz flash at duty 255 → does the duty-255 anomaly (Ch3) follow lit -# fraction / on-time; followed by 6 s all-off -# so the suspected slow tail can be followed -# to zero with margin -# -# Patterns: protocols/g6_2x10_2p_diagnostics_patterns/ (built by -# scripts/make-2p-diagnostic-patterns.js; README.txt lists the per-frame mean -# level of each). Upload the folder once from Arena Studio: Console → Patterns → -# Add ▾ → From course repo… → this protocol's folder. The Studio resolves patterns -# by NAME; the pattern_ID numbers below are only the fallback (alphabetical SD -# index after the four existing patterns) and the run-view preflight will flag -# a mismatch. Keep the four existing patterns on the card. -# -# Epochs in the TIFF header (auxTrigger0 blocks separated by the allOff gaps), in -# order: ramp_128, steps_128, flash_128, sparse_128, flash_255, then 6 s off. -# Within ramp/steps, level k occupies [t0 + 0.5 k, t0 + 0.5 (k+1)) s of the block -# (frame_rate 2). Within flash, frames alternate 0.5 s dark / 0.5 s full. -# Analysis: check_aux_triggers.py prints the blocks; compare the core mean per -# level with the display-off frames of the same file (see quick_checks_review.py -# for the local-reference and frame-block-SE approach). -# -# ALIGNMENT of the level changes inside a block. Three marks are available: -# 1. The aux-trigger block start (first J3 pulse of the block) + k × 0.5 s. -# The controller steps frames on its own clock (frame_rate 2), so this is -# good to a few ms (host start jitter) — enough for 0.5 s levels. -# 2. Ch3 itself (ungated, red-channel detector): it sees the LED light of every -# row flash, so each level change is a step in the Ch3 row mean at the exact -# line where the new frame was applied (126 µs resolution). Use this for the -# flash kinetics — it is the cleanest mark and needs no extra wiring. -# 3. The setAnalogOut markers below (0.5–2.5 V per condition, arena J27). Only -# recorded if the vDAQ Data Recorder logs an AI (docs/development/ -# 2p-scanimage-signal-logging.md § 2c, in the private 2P rig repo); host-timed like (1), so they add a -# cross-check, not precision. A frame-locked AO ("frame_number" role) is not -# validated on this controller yet — do not rely on it. -# ============================================================================ - -experiment_info: - name: "2P line-sync diagnostics: brightness steps, flash, sparse (G6 2x10)" - author: "mreiser" - date_created: "2026-09-23" - -rig: "./configs/rigs/bergamo_g6_2x10_2p.yaml" - -# The runner must implement the controller: block below (a runner that does not — MATLAB -# today — refuses to run this file instead of running it in the wrong panel mode). -requires: [controller_block] - -# Sticky controller settings asserted by Arena Studio before EVERY run and recorded in the -# run header (docs/development/controller-settings-strategy.md). Replaces the manual -# "set panel mode 2 by hand" step: the Studio reads the mode, sets it if needed, reads it -# back, and refuses to run if it cannot. -controller: - panel_mode: triggered # 2 — LEDs only inside the line-clock LOW gap - refresh_policy: line_sync_safe # cap the 300 Hz re-stream at the rig's limits.max_refresh_hz - panel_firmware: "2p" # SD footer "2p-[-d]" = the eintlow_2p line-sync build - -plugins: [] - -variables: - - &ramp_pattern "G6_2x10_ff_ramp16" # 16 frames, full field, level = frame index - - &ramp_id 6 - - &steps_pattern "G6_2x10_ff_steps_pr16" # 16 frames, full field, levels in pseudo-random order - - &steps_id 7 - - &flash_pattern "G6_2x10_ff_flash_0_15" # 2 frames: full field dark / full - - &flash_id 5 - - &sparse_pattern "G6_2x10_sparse10" # 1 frame: 10 % of LEDs at 15 - - &sparse_id 8 - - &level_hz 2 # frames/s: 0.5 s per grey level; 16 levels → 8 s; flash → 1 Hz - - &pre_dur 3 - - &ramp_dur 8 # = 16 levels / level_hz: exactly one pass - - &flash_dur 8 # 8 flashes at 1 Hz - - &sparse_dur 5 - - &flash255_dur 6 - - &iti_dur 2 # allOff between steps: pauses the J3 pulses → epoch marker - - &long_off 6 # allOff after the last step: slow-tail test - - &duty_50 128 - - &duty_100 255 # diagnostics only; experiments stay ≤ ~190 - -experiment: - - "pre_blank" - - name: "diagnostics" - trials: ["ramp_128", "steps_128", "flash_128", "sparse_128", "flash_255"] - repetitions: 1 - intertrial: "iti" - - "post_blank" - -conditions: - - name: "pre_blank" - commands: - - type: "controller" - command_name: "allOff" - - type: "controller" - command_name: "setAnalogOut" - mv: 0 - - type: "wait" - duration: *pre_dur - - - name: "ramp_128" - commands: - - type: "controller" - command_name: "setAnalogOut" - mv: 500 - - type: "controller" - command_name: "trialParams" - pattern: *ramp_pattern - pattern_ID: *ramp_id - duration: *ramp_dur - mode: 2 - frame_index: 0 - frame_rate: *level_hz - gain: 0 - duty: *duty_50 - - type: "wait" - duration: *ramp_dur - - - name: "steps_128" - commands: - - type: "controller" - command_name: "setAnalogOut" - mv: 1000 - - type: "controller" - command_name: "trialParams" - pattern: *steps_pattern - pattern_ID: *steps_id - duration: *ramp_dur - mode: 2 - frame_index: 0 - frame_rate: *level_hz - gain: 0 - duty: *duty_50 - - type: "wait" - duration: *ramp_dur - - - name: "flash_128" - commands: - - type: "controller" - command_name: "setAnalogOut" - mv: 1500 - - type: "controller" - command_name: "trialParams" - pattern: *flash_pattern - pattern_ID: *flash_id - duration: *flash_dur - mode: 2 - frame_index: 0 - frame_rate: *level_hz - gain: 0 - duty: *duty_50 - - type: "wait" - duration: *flash_dur - - # static: frame_rate 0 holds frame 0 (the sparse frame) - - name: "sparse_128" - commands: - - type: "controller" - command_name: "setAnalogOut" - mv: 2000 - - type: "controller" - command_name: "trialParams" - pattern: *sparse_pattern - pattern_ID: *sparse_id - duration: *sparse_dur - mode: 2 - frame_index: 0 - frame_rate: 0 - gain: 0 - duty: *duty_50 - - type: "wait" - duration: *sparse_dur - - - name: "flash_255" - commands: - - type: "controller" - command_name: "setAnalogOut" - mv: 2500 - - type: "controller" - command_name: "trialParams" - pattern: *flash_pattern - pattern_ID: *flash_id - duration: *flash255_dur - mode: 2 - frame_index: 0 - frame_rate: *level_hz - gain: 0 - duty: *duty_100 - - type: "wait" - duration: *flash255_dur - - # Between steps: display dark AND controller refresh stopped (J3 pulses pause). - - name: "iti" - commands: - - type: "controller" - command_name: "allOff" - - type: "wait" - duration: *iti_dur - - # The long all-off after flash_255: recovery / slow-tail measurement. End with allOff, not stopDisplay. - - name: "post_blank" - commands: - - type: "controller" - command_name: "allOff" - - type: "controller" - command_name: "setAnalogOut" - mv: 0 - - type: "wait" - duration: *long_off diff --git a/protocols/g6_2x10_2p_diagnostics_patterns/G6_2x10_ff_flash_0_15.pat b/protocols/g6_2x10_2p_diagnostics_patterns/G6_2x10_ff_flash_0_15.pat deleted file mode 100644 index 986eb79da3d26382a383a57946f04407ba3c409a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 8150 zcmeH_u?c`c5Crc(G!Z+n0x>c+(p1EPY|xH`*BTZUo@Xw)WEXDc_T{+7HAn7pDSbqA z+ZmDOe`{iHPN~o%Rw|SdSiy>w01sxxE0$wL1ZRz5#fn!f$BGEf8pDbeuUL*15u7!4 z57Tz0NONgq{x@snlENu9n#2lLtOR&4D_*f2Doj5`NIJ0$NIoDI zHa3!9h=1U9zG1_L;jpHfW^dSQooA2tUOard6^rF!z21z7_pL{iI({kM5mP4gh?NPY z1Xi$OCBT7M@rvbG5y4qwSh3<2%dsMYv&OJu#VeL$MFeM!9jzX3oWE*)5v6V?dBaVT zCEHAy(Ii%|VkN+VS@DYHSP{WlV_31`70a@sCWlUTuul>i53#VeL$MFeM!Va1A9EXRrn&Kkpt6|Y#16%m{@mdVPy z()x`kbw9})Zjvn7XUdEwv4RyV0S?THS1iYh2+kVAiWRR|jujD{HHH-{Ua=f2A~=>l;z(VUjo8Bw2FElo?H81uIqp9GDfaSdJADoHd3OD_*f2Dqx1-dPByYG$vgCv*Gn&K-R;&a#Fe_fM94jI?YYZz^yka?4 zL~zy^R;+l%a;%8ptg%d1mQPw=MyaPs-f)v-$thE2G>H|gSP5`oR=i?4Rzz^t7*?!! z#d55O;H)vMSn-PGSP{WlW0|ac_}luODD^DK8*Y*;Ib+IIaWk)))-c-c*Sz8h~TWTOjh1(wSGTJy-Mzh&PI>{Suk}O#>Wk!=& s!HSgt2WG`9mSaT(XN_USidQVhiU`gc!-^HJSdJADoHdro%FmDg0aY4>pa1{> diff --git a/protocols/g6_2x10_2p_diagnostics_patterns/G6_2x10_ff_steps_pr16.pat b/protocols/g6_2x10_2p_diagnostics_patterns/G6_2x10_ff_steps_pr16.pat deleted file mode 100644 index 0f332059943a6a98e3e527f37c4a64c3582795c2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 65074 zcmeI5J!=&}6h!YnqsI6VgN3a;uwMxXS_KI!Sjl2U!NMwqwGzve5p3*CZ##jZat#ZlO*q$DHEE+%7ju* zu!0pU0S?THS1iYh2+kVAiWRR|jujD{HHH-{Ua=f2A~IaWk)))-c-c*Sz8h~TWTOjcfe zZ2fAKx|`$;H%XT4GG#`SSiy>w00(BpE0$wL1ZRz5#fn!f$BGEf8pDbeuUL*15u7!a z$;zJ}tzU~$capr}CdrZ=rp#y(D_F4-;J~bS#d55O;H)vMSn-PGSP{WlV_31`70aMv{F|5J4L2!vj1f$k(Ii%|VkN+VS@DYHSP{WlV_31`70a|q$O=1NrRstND6|Y#16%m{@h7~Jbu^cNR zIBN_mR=i?4Rzz^tSSBlv-nD)sO5IQLhMOcy_L(xHNvvSSN`M2i;uXuWB7(EVuwun4 zmSaT(XN_USidQVhiU`gc%Vg!r@7A}X)Mb)4+$34DWXg;tv4RyV0S?THS1iYh2+kVA ziWRR|jujD{HHH-{Ua=f2A~27N&yu|1CdraBrp#y(D_F4-;J~bS#d55O z;H)vMSn-PGSP{WlV_31`70a3-22!1ohbDp$s2BxEV*FHj3%*y z6)OP_%!*em$BGEf8pDbeuUL*15u7!K6)Rq`94jI?Yb=wM<ku=yzb1``* z_#xD4+%*%gEXA|u;OGIq)reRsitWrwsPtGYasJ<9e9sv9(nwS=J*RivH1s(;ZDs}2 zIf-MPSW2r<&mVGK6P?p2&6&EmIE?Oa!=e!PkRb}&4BHPUy;yGL!)W$DV*ozk;Abh+ zEGO0cKbCVAgIxMLv8)rdz!L}W;#>oP7E$l5uXyMd0j6dig_rA=&stzH(hOg)%g0F1 zYz!$P;Z|^TKwc7=STViB3&L84L|2+jNb)SuG;L|j7MzGRjG{ms_#o?42$rMnU#ryX zrhypTe6+bln(q1l_@c|xrYOm0jav%1Jx@jFnM_i-byVUh=TzvE_+AU{hvfD_Nkq&J zd3{hMq6|=|H;nmkr6#wa^1Qp<99N>eGRJ{hvI z4B3)Lk}40C;KqQP1w&MW=xGF_;iZbpsXZlb#2|^SRHw4inrrorqk*OJ@BhukpS6X! z5~!QJb-!ZmtxT!2uiO2Z=Viqg9J9;ohO)DqbC}>ZhkDtVEn8j zgqxa5%y!1%z*6g_tmQ2Q#ACLAiqq&y>9uupV((mOR)bQ>3|20N==3@Vq?j{hY7psG zh7TOepT-|9GmG`IJHQ>JTClQ~u(_sq_DzbG?szUV9i{Ya+9&jGT6;3s!cWVF7=#%9 zoWvd|VN0->xk{asd%$>UG+AzUBko71=WUqByprkf%OwKS50i>un!Wc?(vZuR*o)5! zafYNw8rgThUbhsTYQ0n?Lx&J0uu>@$ony)[-d]" = the eintlow_2p line-sync build - -plugins: [] - -variables: - - &pattern_name "frame2_h_ccw_200f" - - &pattern_id 4 - - &rotate_hz 20 # frames/s; 200-frame pattern → one revolution per 10 s - - &blank_dur 4 - - &step_dur 7 - - &iti_dur 2 # allOff between steps: stops the 300 Hz re-stream → gap in the J3 aux-trigger record - - &duty_10 25 - - &duty_25 64 - - &duty_50 128 - - &duty_75 191 - - &duty_100 255 # ~15 µs row on the 2P firmware: tight-margin case; overruns on 9014b5b - -experiment: - - "pre_blank" - - name: "duty_sweep" - trials: ["duty_10", "duty_25", "duty_50", "duty_75", "duty_100", "static_50"] - repetitions: 1 - intertrial: "iti" - - "post_blank" - -conditions: - # Display dark, AO marker 0 V. Baseline for the SiPM channels. - - name: "pre_blank" - commands: - - type: "controller" - command_name: "allOff" - - type: "controller" - command_name: "setAnalogOut" - mv: 0 - - type: "wait" - duration: *blank_dur - - - name: "duty_10" - commands: - - type: "controller" - command_name: "setAnalogOut" - mv: 500 - - type: "controller" - command_name: "trialParams" - pattern: *pattern_name - pattern_ID: *pattern_id - duration: *step_dur - mode: 2 - frame_index: 0 - frame_rate: *rotate_hz - gain: 0 - duty: *duty_10 - - type: "wait" - duration: *step_dur - - - name: "duty_25" - commands: - - type: "controller" - command_name: "setAnalogOut" - mv: 1000 - - type: "controller" - command_name: "trialParams" - pattern: *pattern_name - pattern_ID: *pattern_id - duration: *step_dur - mode: 2 - frame_index: 0 - frame_rate: *rotate_hz - gain: 0 - duty: *duty_25 - - type: "wait" - duration: *step_dur - - - name: "duty_50" - commands: - - type: "controller" - command_name: "setAnalogOut" - mv: 1500 - - type: "controller" - command_name: "trialParams" - pattern: *pattern_name - pattern_ID: *pattern_id - duration: *step_dur - mode: 2 - frame_index: 0 - frame_rate: *rotate_hz - gain: 0 - duty: *duty_50 - - type: "wait" - duration: *step_dur - - - name: "duty_75" - commands: - - type: "controller" - command_name: "setAnalogOut" - mv: 2000 - - type: "controller" - command_name: "trialParams" - pattern: *pattern_name - pattern_ID: *pattern_id - duration: *step_dur - mode: 2 - frame_index: 0 - frame_rate: *rotate_hz - gain: 0 - duty: *duty_75 - - type: "wait" - duration: *step_dur - - - name: "duty_100" - commands: - - type: "controller" - command_name: "setAnalogOut" - mv: 2500 - - type: "controller" - command_name: "trialParams" - pattern: *pattern_name - pattern_ID: *pattern_id - duration: *step_dur - mode: 2 - frame_index: 0 - frame_rate: *rotate_hz - gain: 0 - duty: *duty_100 - - type: "wait" - duration: *step_dur - - # Same pattern and duty as duty_50 but frame_rate 0 (static frame). The - # controller still re-streams at the refresh rate, so any difference from - # duty_50 isolates pattern-content change from re-streaming. - - name: "static_50" - commands: - - type: "controller" - command_name: "setAnalogOut" - mv: 3000 - - type: "controller" - command_name: "trialParams" - pattern: *pattern_name - pattern_ID: *pattern_id - duration: *step_dur - mode: 2 - frame_index: 0 - frame_rate: 0 - gain: 0 - duty: *duty_50 - - type: "wait" - duration: *step_dur - - # Between steps: display dark AND controller refresh stopped, so the J3 - # frame-transfer pulses pause — the epoch boundary marker in the TIFF header. - - name: "iti" - commands: - - type: "controller" - command_name: "allOff" - - type: "wait" - duration: *iti_dur - - - name: "post_blank" - commands: - - type: "controller" - command_name: "allOff" - - type: "controller" - command_name: "setAnalogOut" - mv: 0 - - type: "wait" - duration: *blank_dur diff --git a/protocols/index.json b/protocols/index.json index 0de3b79e..aa85e4c6 100644 --- a/protocols/index.json +++ b/protocols/index.json @@ -13,26 +13,6 @@ "label": "FicTrac closed-loop (Mode 3)", "path": "./protocols/fictrac_closed_loop.yaml" }, - { - "key": "fictrac_direction_test", - "label": "FicTrac closed-loop + open-loop direction/speed test (~5.5 min)", - "path": "./protocols/fictrac_direction_test.yaml" - }, - { - "key": "fictrac_bias_test", - "label": "FicTrac closed-loop bias/disturbance test (~4 min)", - "path": "./protocols/fictrac_bias_test.yaml" - }, - { - "key": "led_activation_quadrant_test", - "label": "Conditional LED activation — graded zones test (Mode-3 closed loop, ~2 min)", - "path": "./protocols/led_activation_quadrant_test.yaml" - }, - { - "key": "fictrac_coupling_bias_fly_test", - "label": "Closed-loop coupling × bias — fly test (Mode 3, natural coupling −1, ~13 min)", - "path": "./protocols/fictrac_coupling_bias_fly_test.yaml" - }, { "key": "opto_intensity_runtime_test", "label": "Opto intensity as a runtime variable (Mode 2, ledDrive; ~2 min)", @@ -43,31 +23,6 @@ "label": "Full experiment (blocks, plugins, ITI)", "path": "./protocols/full_experiment.yaml" }, - { - "key": "g6_2x10_2p_duty_sweep", - "label": "2P line-sync duty sweep — G6 2×10 on the Bergamo (~62 s, 2 s allOff between steps)", - "path": "./protocols/g6_2x10_2p_duty_sweep.yaml" - }, - { - "key": "g6_2x10_2p_diagnostics", - "label": "2P line-sync diagnostics — full-field brightness ramp/steps, 1 Hz flash, 10 % sparse, 6 s all-off (~52 s; upload its _patterns folder first)", - "path": "./protocols/g6_2x10_2p_diagnostics.yaml" - }, - { - "key": "bias_camera_closed_loop_poc", - "label": "BIAS camera + FicTrac closed loop through the ROS 2 bridge (LAB-303 spike, ~40 s)", - "path": "./protocols/bias_camera_closed_loop_poc.yaml" - }, - { - "key": "bias_camera_closed_loop_poc_ww18", - "label": "BIAS camera + FicTrac closed loop, ROS 2 bridge — ww18 variant (site 2x10 grating_sq, slot 2)", - "path": "./protocols/bias_camera_closed_loop_poc_ww18.yaml" - }, - { - "key": "bias_camera_closed_loop_poc_ww18_soak10", - "label": "BIAS + FicTrac closed loop, ROS 2 bridge - ww18 10-min soak (2 x 300 s, grating_sq slot 2)", - "path": "./protocols/bias_camera_closed_loop_poc_ww18_soak10.yaml" - }, { "key": "g6_color_optomotor_test", "label": "Color optomotor test — one LED color at a time, top row, 25/50/100 % duty (G6 four-color, BIAS, ~18 min)", @@ -77,11 +32,6 @@ "key": "g6_color_optomotor_smoke", "label": "Color optomotor SMOKE — 10 s per pattern, duty 100 %, BIAS check (~2 min)", "path": "./protocols/g6_color_optomotor_smoke.yaml" - }, - { - "key": "g6_2p_color_led_sweep", - "label": "2P color LED-intensity sweep — one ScanImage grab per color, 6 duty levels × 3 s at 20 fps (G6 four-color, ScanImage JSON connector, ~2.5 min)", - "path": "./protocols/g6_2p_color_led_sweep.yaml" } ] } diff --git a/protocols/led_activation_quadrant_test.yaml b/protocols/led_activation_quadrant_test.yaml deleted file mode 100644 index b19fc5b2..00000000 --- a/protocols/led_activation_quadrant_test.yaml +++ /dev/null @@ -1,212 +0,0 @@ -version: 3 - -# ============================================================================ -# Conditional LED activation — graded zones test (web runner, Mode-3 closed loop) -# -# Tests the per-trial `led_activation` on trialParams (Arena Studio v0.86: graded -# ZONES with linear ramp edges over a baseline; v0.59's hard-edged `on_ranges` -# still works as sugar). Reuses the SAME built-in SD pattern the closed-loop -# block uses — NO new patterns needed: -# 4 frame2_h_ccw_200f (a 200-frame full-azimuth rotation) -# -# The 200 frames map 1:1 onto azimuth (1.8°/frame = 360°/200 px), so the pattern -# divides into four 50-frame QUADRANTS: -# Q1 frames 0– 49 ( 0– 90°) -# Q2 frames 50– 99 ( 90–180°) -# Q3 frames 100–149 (180–270°) -# Q4 frames 150–199 (270–360°) -# In closed loop the displayed frame index tracks the fly's heading, so gating -# the LED on quadrant zones drives it as the fly rotates through them. The -# index the LED follows is the DISPLAYED one (after any bias), i.e. where the -# stimulus actually is on the arena. -# -# HOW IT DRIVES THE LED: `startClosedLoop` enables the bridge's frame apply — -# that same event stream is what feeds led_activation, so the LED gating only -# runs INSIDE the startClosedLoop → wait → stopClosedLoop bracket. (This is also -# the bench check for "is closed-loop apply active?" — if the arena rotates with -# the fly, the LED gating is receiving frames too.) -# -# FOUR conditions, each 30 s (`hold_dur`): -# 1. led_one_quadrant — hard edges: LED on in Q2 only (on_ranges sugar) -# 2. led_opposite — hard edges: LED on in Q1 + Q3 (two bands; 4 flips / rev) -# 3. led_ramped — one zone, 10-frame (18°) ramps in/out around Q2 -# 4. led_baseline_probe — dim baseline everywhere + a brighter ramped probe zone -# that WRAPS through frame 0 (Q4 → Q1) -# -# Needs the FicTrac bridge (`pixi run bridge`); with no fly, `pixi run sim` -# supplies synthetic heading (`--model fly` for a fixating one). Requires fw #33+ -# (duty/AO) and a BuckPuck LED on Analog Out. Web-only — MATLAB does not read -# led_activation. Bench check: photodiode on the LED — the ramps must be -# monotone with no flicker, and the `cf` rows' max req_age_us must not grow -# while a ramp is active (the LED writes yield to frame writes). -# ============================================================================ - -experiment_info: - name: "Conditional LED activation — graded zones test" - author: "webDisplayTools" - -rig: "./configs/rigs/cshl_g6_2x10_ball.yaml" - -plugins: - - name: "fictrac" - type: "class" - matlab: - class: "FicTracPlugin" - config: - bridge_url: "ws://localhost:8765" - fictrac_port: 60000 - proto: "udp" - offset: 0 - -variables: - # Change ONE number to rescale. hold_dur drives each trial's trialParams - # duration AND its bracketing wait (they must agree — see the waits rule). - hold_dur: &hold_dur 30 # seconds of closed-loop hold per condition - led_pct: &led_pct 20 # LED brightness % inside a zone (BuckPuck curve; fractional OK) - base_pct: &base_pct 2 # dim baseline % for the baseline+probe condition - # Ramp endpoints are literal frame indices below (10-frame ramps = 18° at - # 1.8°/frame); bind them to anchors from the Studio's 🔗 if you sweep them. - -experiment: - - "led_one_quadrant" - - "led_opposite" - - "led_ramped" - - "led_baseline_probe" - - "shutdown" - -conditions: - # ---- 1) single quadrant, hard edges (legacy on_ranges sugar) --------------- - # LED on across Q2 (frames 50–99), off elsewhere. As the fly's heading sweeps - # through Q2 the LED switches on entering frame 50 and off leaving frame 99. - # `on_ranges: [[50, 99]]` ≡ `zones: [{level, ramp_in: [50, 50], ramp_out: [100, 100]}]`. - - name: "led_one_quadrant" - commands: - - type: "controller" - command_name: "trialParams" - pattern: "frame2_h_ccw_200f" - pattern_ID: 4 - duration: *hold_dur - mode: 3 - frame_index: 0 - frame_rate: 0 - gain: 0 - led_activation: - level: *led_pct - on_ranges: - - [50, 99] - - type: "plugin" - plugin_name: "fictrac" - command_name: "startClosedLoop" - params: - coupling: -1 - - type: "wait" - duration: *hold_dur - - type: "plugin" - plugin_name: "fictrac" - command_name: "stopClosedLoop" - - # ---- 2) two opposite quadrants, hard edges --------------------------------- - # LED on across Q1 (0–49) and Q3 (100–149) — four transitions per revolution, - # exercising the multi-range on_ranges list. - - name: "led_opposite" - commands: - - type: "controller" - command_name: "trialParams" - pattern: "frame2_h_ccw_200f" - pattern_ID: 4 - duration: *hold_dur - mode: 3 - frame_index: 0 - frame_rate: 0 - gain: 0 - led_activation: - level: *led_pct - on_ranges: - - [0, 49] - - [100, 149] - - type: "plugin" - plugin_name: "fictrac" - command_name: "startClosedLoop" - params: - coupling: -1 - - type: "wait" - duration: *hold_dur - - type: "plugin" - plugin_name: "fictrac" - command_name: "stopClosedLoop" - - # ---- 3) one zone with linear ramps ----------------------------------------- - # Dark at frame 40, full `led_pct` by frame 50, held through 99, back to dark - # at frame 110. A fly dithering on a boundary now just modulates the LED a - # little instead of chattering it (this is what hysteresis used to be for). - - name: "led_ramped" - commands: - - type: "controller" - command_name: "trialParams" - pattern: "frame2_h_ccw_200f" - pattern_ID: 4 - duration: *hold_dur - mode: 3 - frame_index: 0 - frame_rate: 0 - gain: 0 - led_activation: - baseline: 0 - zones: - - level: *led_pct - ramp_in: [40, 50] # baseline at 40 → level at 50 - ramp_out: [100, 110] # level at 100 → baseline at 110 - - type: "plugin" - plugin_name: "fictrac" - command_name: "startClosedLoop" - params: - coupling: -1 - - type: "wait" - duration: *hold_dur - - type: "plugin" - plugin_name: "fictrac" - command_name: "stopClosedLoop" - - # ---- 4) dim baseline + brighter probe zone that wraps through frame 0 ------ - # The LED sits at `base_pct` everywhere and ramps up to `led_pct` in a zone - # centred on frame 0 (Q4 → Q1): ramp in 170→180, hold 180..19 (through the - # 199/0 seam), ramp out 20→30. Checks the wrap unrolling and that ramps start - # from the BASELINE, not from dark. - - name: "led_baseline_probe" - commands: - - type: "controller" - command_name: "trialParams" - pattern: "frame2_h_ccw_200f" - pattern_ID: 4 - duration: *hold_dur - mode: 3 - frame_index: 0 - frame_rate: 0 - gain: 0 - led_activation: - baseline: *base_pct - zones: - - level: *led_pct - ramp_in: [170, 180] - ramp_out: [20, 30] - - type: "plugin" - plugin_name: "fictrac" - command_name: "startClosedLoop" - params: - coupling: -1 - - type: "wait" - duration: *hold_dur - - type: "plugin" - plugin_name: "fictrac" - command_name: "stopClosedLoop" - - # ---- shutdown ------------------------------------------------------------- - # Blank the arena (also forces the LED off via the runner's activator - # teardown on stopDisplay). Do NOT disconnect the bridge — the run-log export - # runs after the sequence and needs it connected. - - name: "shutdown" - commands: - - type: "controller" - command_name: "stopDisplay" - - type: "controller" - command_name: "allOff" diff --git a/protocols/led_activation_quadrant_test_patterns/001_all_on.pat b/protocols/led_activation_quadrant_test_patterns/001_all_on.pat deleted file mode 100644 index ed8f96e945c2bc17b8fc1df8413298967591f66d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1084 zcmZ=a3kXqgU}Rw8V*LM~pMilP*e!^Gp;6#J$)I6W6C}(=0|J_MMgw9v2ZYBT00Nu; AWdHyG diff --git a/protocols/led_activation_quadrant_test_patterns/002_grating_sq.pat b/protocols/led_activation_quadrant_test_patterns/002_grating_sq.pat deleted file mode 100644 index 65ca95b67fb663b30777d7fb7fd412121eacb370..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 21338 zcmeI4yGmqX5QP63@8f003m7T}g88Csv_@^9pfs9_f)C&u%*-PQrapuSCJJjJKEMWI zwxOAZ3gWR^8)ghUlctgroeIuJ{RMx~eKuBib{E&@O1)95s%9zWpJ*xFR&~>SE1^e+ zqAy?s;m#q5%pgveMQfH6orRZL=wZH~2-FC|okI|rL0s>N)-5T@pXY(l!+b#zs1bxa zhagge_$xYqqf;_apby9r3U)I#{}`U`c^S17AjwH5CNm z9T!1l261vOI)tPMmK10x;0ubZ+93$9s|X@9h|S-k!$^u?Nr8p}zM#me9fI(>iXbwB zxcDVHf}{wR6eu0w3yQ4TAqc;46GUbZ>kZLSBt>|fqJl3dvLO_L@If4c$P8kBMRW{F z5uS{&147Z~5DG!~U=2Ye22pMM zhK}&vsgxkjRz;_f6tSHTTlfGDiU^`_riPC2-Kmry4j)CENQ&6bhwX3x2So(YH&a7L zcyE^y#MOal3rP|EJ4JN^92D8+3qg2YMG%=mY`=<5BPpVPr>Hvc1x0pCCkUS+Ac)K$ zuJ1%=kQC9sQ&a`MpvZ3N1YvCrLG(^TN=K|+iq0Y_!jlnpKq#_XIzd=lLlC{wkkS!L F#~(t7xw`-W diff --git a/protocols/led_activation_quadrant_test_patterns/003_grating_sine.pat b/protocols/led_activation_quadrant_test_patterns/003_grating_sine.pat deleted file mode 100644 index 9f5b4fdc69d2cc24b0190c882aa9430a4fe7fb09..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 81338 zcmeI5J&P6F1LoAh<$~9g?%M22Ush4aniNK zAyi3JzIeB8?uA&%PRjYlw*nja-sJ4Ux589>v(V9#N`F3PS=B`|242?9g9a`Bs>U zZx%XwQt2+h;MR!7WPU{|q=GYvb}mT=>n@9##&|kCQXv)7PQ<>#zGCWmbTB#?9V{;g z+RuNinWJOe&;e+KJd#*jG$Fj}Ar$ql4w;K>N$rb@NofT2f3Y_*SXA$kcbfRq`=X zN!Mcm8@WZ02i&RJjcSY=6k`C5?gKrt* z>GVj2RB*RLyYs#k`wIIC`$}F8uvYTov~Q2P`*|f%dHu_}c_ze4_BKNFt-wb1f_JyR z6{g~wg^r$7`tN&}Rb8Y)D!40R=aO`=?ufo=jHlBh6;i?74(-nSR_rV6E2f@D2cv`0 z!SZsT{rOSdJR4#qJ1KGHTcP-6hyM23x589>v(V9#O1gueuFPso=2xUbDmd9`=aO`= zbO%5E;D%j{r$3ogNX4`hv9GYNn0g)^j1EQz%gcfGt9R<=eu$OqIHm)yy zD@?^V3mrYF3=8~4RTrs{3Qnupxg;HISl}DRcse~&Ar;*1(C)l%#lFJ6!oHH11FV(2 zIPKe`?tWfLRDOH4ZXSeK$*$x2#J2()+1t3>`c{~VZx%XwQjynIYfR=>q(Ukoo5^Q=_F7sa$S}QJl{Fou9?LXhv&GR8vvXgRl;aefiWQYEI<6B`W zzFFw#Nkv{;tudKjkqW8cw3?kumg!)pW5j%_GO1YRv0a7elSw80UPbzG8jZ>Pid0Ai z_n_=Pt0BSW&+`3@pv+qF+2e)i>8AbUySjNH#7e%+y14SKkY=*$xHmn_o3PREEjRYg)U&11U?(I=D2u)sI;BaL>hA{A1>Jt(`+ zYDlo$TwMC!6z*Z@+oJZXO1#CFR5J&bLYxBc>F5t5h+hf+SrOM^cg3CoieGNQG2z zGR@8}rLUx>+53@O#adyl9Hdsd->_OzmzX{l)(UHdwUUq(Uk z(I=CNyrNoTGQT1fQZZcx>xpq63EX~1&riqAoR;5uH7WjsFY*!)rWKvloM^Ygb zoOrhTtcC&SvWC>!J$95H>PbQV_h`y;GX|!_{sgMdzJllO% ULxSbz;?n=Fuv#gl+yDOQe|H4c_W%F@ diff --git a/protocols/led_activation_quadrant_test_patterns/004_frame2_h_ccw_200f.pat b/protocols/led_activation_quadrant_test_patterns/004_frame2_h_ccw_200f.pat deleted file mode 100644 index 8e0b0fa9690ee402dffbf8cb14eb71ea5be9a858..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 813218 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 diff --git a/protocols/mode3_drill_1trial.yaml b/protocols/mode3_drill_1trial.yaml deleted file mode 100644 index de624540..00000000 --- a/protocols/mode3_drill_1trial.yaml +++ /dev/null @@ -1,67 +0,0 @@ -version: 3 - -# ============================================================================ -# One-trial Mode-3 drill — for the recovery drill before an overnight soak and -# for the lab's injected-stall test (lab-test-day-windows-2026-09-14.md, test B): -# a single 180 s closed-loop trial on the 813 KB bar with the simulator, nothing -# else (≈ 36k reads at 200 Hz — more than one of the old ~24.5k-read stall periods, -# so the injected legacy arm has time to stall). Run it as a Test run; the banner at the end is the result. -# before: await Studio.setSdDiag(3) (inject the stall arm) -# after: await Studio.setSdDiag(0) -# ============================================================================ - -experiment_info: - name: "Mode-3 drill (one 180 s bar trial)" - author: "webDisplayTools" - -rig: "./configs/rigs/cshl_g6_2x10_ball.yaml" - -plugins: - - name: "fictrac" - type: "class" - matlab: - class: "FicTracPlugin" - config: - bridge_url: "ws://localhost:8765" - fictrac_port: 60000 - proto: "udp" - offset: 0 - -experiment: - - "drill_setup" - - "cl_bar_200f" - - "drill_shutdown" - -conditions: - - name: "drill_setup" - commands: - - type: "plugin" - plugin_name: "fictrac" - command_name: "connect" - - # The 813 KB, 200-frame bar on the CSHL course card is p3's pattern (036_p3_heisenberg_ts.pat); - # names resolve against the card listing, pattern_ID is only the fallback. - - name: "cl_bar_200f" - commands: - - type: "controller" - command_name: "trialParams" - pattern: "p3_heisenberg_ts" - pattern_ID: 36 - duration: 180 - mode: 3 - frame_index: 0 - frame_rate: 0 - gain: 0 - - type: "plugin" - plugin_name: "fictrac" - command_name: "startClosedLoop" - - type: "wait" - duration: 180 - - type: "plugin" - plugin_name: "fictrac" - command_name: "stopClosedLoop" - - - name: "drill_shutdown" - commands: - - type: "controller" - command_name: "stopDisplay" diff --git a/rig/HANDOVER-okey-cokey-2026-10-21.md b/rig/HANDOVER-okey-cokey-2026-10-21.md deleted file mode 100644 index c1ef0e8a..00000000 --- a/rig/HANDOVER-okey-cokey-2026-10-21.md +++ /dev/null @@ -1,102 +0,0 @@ -# Handover: okey-cokey rig, BIAS test + Foxglove dashboard (2026-10-21) - -For a Claude Code session running **on the okey-cokey lab PC** (Windows; the room with the G6 2×10 -four-color arena, an overhead BIAS camera, freely walking flies, no FicTrac). Michael's Mac has no -remote path into the rig PCs, so start the session there: open Claude Code in any folder (the -checkout does not exist yet) and paste the kickoff prompt below as the first message. - -**This PC starts bare** (no Foxglove, no webDisplayTools checkout, maybe no git or pixi). Setup is -its own checklist, [`okey-cokey-pc-setup.md`](okey-cokey-pc-setup.md), done first. Claude Code -cannot install itself: a person installs the Claude desktop app (or Git for Windows, then the CLI) -over Remote Desktop; from there a Claude session can do most of the checklist and asks for the rest. -Without Claude on the PC, the checklist and the run sheet work by hand too. - -Then two jobs, in this order, both on ONE run sheet: -[`okey-cokey-pc-2026-10-21.md`](okey-cokey-pc-2026-10-21.md). - -1. **Test** today's protocol and BIAS on this PC. -2. **Test the camera + G6 Foxglove dashboard** on the real rig, live and on the run's replay. It was - BUILT and tested off-rig on Michael's Mac the same day - ([`docs/okey-cokey-foxglove-dashboard-plan.md`](docs/okey-cokey-foxglove-dashboard-plan.md), the - status block at the top); nothing of it has met this rig, its camera or a real `.ufmf` from it. - -## Kickoff prompt (paste on the lab PC) - -``` -Okey-cokey rig work, LAB-303. This PC may be bare (no webDisplayTools checkout, no Foxglove, maybe -no git or pixi). Start with the setup checklist; read it on GitHub until the repo is cloned: -https://github.com/reiserlab/webDisplayTools/blob/claude/okey-cokey-rig/rig/okey-cokey-pc-setup.md -It clones the repo to C:\w on branch claude/okey-cokey-rig. Then read -C:\w\rig\HANDOVER-okey-cokey-2026-10-21.md and run C:\w\rig\okey-cokey-pc-2026-10-21.md -in order, keeping C:\w\rig\okey-cokey-pc-results-2026-10-21.md as you go (one row per step: -pass/fail, timings, sizes, every error verbatim; the setup facts first). -Ask me (Michael) before every download or install, any admin/UAC step, and anything that needs a -click: the BIAS GUI, the Chrome serial-port picker, the IR backlight, Foxglove, putting flies in. -If BIAS is not on this PC, stop and tell me. Rules are in the handover's "Constraints" section. -Commit fixes and the results file to claude/okey-cokey-rig and push; open no PR. -``` - -## Where things stand - -- **Branch `claude/okey-cokey-rig`** = release candidate 2026-10-20 rc1 (Studio v0.97, the ROS 2 - graph, the BIAS connector; on Next) plus: - - `claude/okey-cokey-pairs`: `protocols/g6_color_optomotor_test.yaml` + `_smoke.yaml` in **Grace's - trial shape (2026-10-21)** — each trial a CW/CCW pair, 15 s CW then 15 s CCW with no gap, then 10 s - dark (also across block boundaries). Full run 24 pairs, 16.1 min; smoke 4 pairs (10 s per - direction, 5 s dark), 1.7 min. Parsed and flattened with the real modules; `pixi run test -- - protocol` 106/106. - - the dashboard (plan doc status block): `nodes/stimulus_node.py` + `stimulus_core.py` + - `arena_display.py` (expected/observed display in the four-color LEDs, arena rate/lag/health from - controller telemetry), BIAS rate/drops in `viz_node.py`, `rig.py --no-fictrac --stimulus`, the - replay (`fox-json --stimulus`, `tools/ufmf.py`), `foxglove/arena-okey-cokey.json`. Mac: 54/54 + - 10/10 + harness 54/54. -- **Not on any Pages tier**: Next serves the candidate *without* the new protocol shape. Serve this - checkout locally (`pixi run python .claude\nocache-server.py 8092` from `C:\w`) or open the YAML via Protocol ▾ → Open…. -- **Background reading**: the ww18 results, `windows-results-ww18-2026-10-15.md` (every Windows finding - is in its table; most are fixed on this branch). -- **Settled**: the okey-cokey controller runs telemetry firmware; BIAS is NOT changed now (no live - camera in Foxglove — the BIAS window sits beside it; the replay has the camera). -- **Record in job 1**: which BIAS build (the Atlas10 fork reports drop counters, the Spinnaker build - may not: those panels then read "no data", never 0). - -## What could go wrong on this rig (look here first) - -- **Windows paths and the pattern/protocol lookup**: the stimulus node finds `.pat` under - the repo's `protocols/` + `patterns/` and the run's protocol by its `protocol_filename` under - `protocols/`. A protocol opened from elsewhere (Downloads) still runs, but its patterns' NAMES may - not resolve → a gray hatched placeholder and "pattern N" in the health line. Fix with - `--stimulus-args "--protocol-dir --pattern-dir "`, not code. -- **The camera's clock**: the replay places camera frames with the run's `getTimestamp` replies. - `camera.offset_ms` in the converter's report should be stable; if `camera.error` says no reply - had a value > 0, the protocol's first getTimestamp came too early (BIAS reads 0 before its first - frame). -- **uFMF from this camera**: the reader follows BIAS's writer (v4, MONO8, index or scan). A different - coding (e.g. RGB) is refused with a clear error: record it. -- **foxglove_bridge on Windows**: never run on Windows yet (ww18 did not install `viz`). If it fails - to start, the rig prints it and the rest runs; record the error verbatim. -- **Load**: the stimulus node renders 800×160 PNGs at 15 Hz (a few ms each on the Mac). If the - bridge's latency numbers (`connector_status`) get worse with it on, record both. - -## Constraints - -- **Never touch Production or Next**, never merge to `main`, open no PR. Code reaches `main` only - through a release candidate, which Michael builds (`docs/development/release-process.md`). -- **No Studio changes** (`arena_studio.html`, `js/*`), **no bridge 3.4 changes** - (`fictrac-bridge/bridge.py`), **no BIAS changes**. Everything lives in `rig/`. If a Studio - change turns out to be needed, stop and write it up instead. -- Tests: `pixi run test`, `pixi run test-viz`, `pixi run test-stimulus` and `pixi run test-stimulus-live` - inside `rig/` (the last needs the rig stopped), plus `pixi run test` at the repo root, all green - before every commit. Prettier only on `*.js`, never on HTML. -- Windows habits from ww18: write text with `newline="\n"`; take durations from `perf_counter`; never - run `bias_sim` or the harness's sim while the real BIAS is open on 5010; Ctrl-C the rig to stop it - (the SIGBREAK handler closes the bag cleanly). -- Capture an error's full text before you try to fix it. Commit small fixes to - `claude/okey-cokey-rig` with a message that names the step. -- American English in code, docs and commits. - -## What to send back - -The results file (both jobs): the PC facts, BIAS build, both runs' checks, the two test counts on -Windows, a screenshot of the live dashboard mid-rotation, the replay converter's printed report, -and anything that needed a manual step. Push `claude/okey-cokey-rig`, and end with a short -summary Michael can paste into the LAB-303 ticket. diff --git a/rig/README.md b/rig/README.md index a1a90aab..45352b2d 100644 --- a/rig/README.md +++ b/rig/README.md @@ -57,7 +57,7 @@ Pieces, individually (each is also a `pixi run` task; `--help` on every script): | `sim` | `rig-fictrac-sim` (rig_nodes.sims.fictrac_sim, moved from `fictrac-bridge/`; `-- --model fly --kp 2` for a fixating fly) | | `kinefly-sim` / `bias-sim` | synthetic KF/1 producer; a fake BIAS program (HTTP, state machine, writes the video file) | | `studio-emulator` | the Studio runner's exact bridge conversation, for machines without a browser | -| `stimulus` | `nodes/stimulus_node.py`: the arena's EXPECTED display (the trial's `.pat` frame × duty in the panels' LED colors) and its OBSERVED display (controller telemetry) as PNG images, plus arena rate / lag / health, from the live `/arena/browser`; `pixi run rig -- --stimulus` (included by `--foxglove`). Layout `foxglove/arena-okey-cokey.json` (camera + G6, no FicTrac); the same layout on a finished run: `pixi run fox-json -- --stimulus --panel-layout four-color` (adds `/camera/image` from the run's uFMF). Plan + status: `docs/okey-cokey-foxglove-dashboard-plan.md` | +| `stimulus` | `nodes/stimulus_node.py`: the arena's EXPECTED display (the trial's `.pat` frame × duty in the panels' LED colors) and its OBSERVED display (controller telemetry) as PNG images, plus arena rate / lag / health, from the live `/arena/browser`; `pixi run rig -- --stimulus` (included by `--foxglove`). Layout `foxglove/arena-okey-cokey.json` (camera + G6, no FicTrac); the same layout on a finished run: `pixi run fox-json -- --stimulus --panel-layout four-color` (adds `/camera/image` from the run's uFMF). Plan + status: `rigs/ww14/okey-cokey-foxglove-dashboard-plan.md` in the private `reiserlab/arena-experiments` (rig records left this repo on 2026-10-29, LAB-227) | | `test-stimulus` / `test-stimulus-live` | the dashboard's pure parts (panel colors pinned to `js/panel-color.js`, renderer, tracker on synthetic + a real 80 fps trial, uFMF reader, replay converter); the live path through router + bridge + stimulus node (~25 s, needs ports 7447/8765 free) | Environment set by the workspace (`[activation.env]`): `RMW_IMPLEMENTATION=rmw_zenoh_cpp`, @@ -168,7 +168,7 @@ starts at its first frame, ≈ 300 ms later — take the alignment stamp ≥ 0.5 startRecording, or anchor on the first frame. The harness (`pixi run test`) exercises the same path through the emulator: 32 / 32. -One detour worth knowing: the stock `fictrac_bias_test.yaml` names `frame2_h_ccw_200f` at `pattern_ID 4`; this card's slot 4 is a 1-frame color test pattern, the runner read `frames: 1` from the card (correctly) and the display sat on frame 0. The protocol was re-pointed at the card's 20-frame grating — a card-content mismatch the Studio already warns about, not a bridge issue. +One detour worth knowing: the stock `fictrac_bias_test.yaml` (now `protocols/shared/` in the private `reiserlab/arena-experiments`) names `frame2_h_ccw_200f` at `pattern_ID 4`; this card's slot 4 is a 1-frame color test pattern, the runner read `frames: 1` from the card (correctly) and the display sat on frame 0. The protocol was re-pointed at the card's 20-frame grating — a card-content mismatch the Studio already warns about, not a bridge issue. **Still not verified:** Windows execution (`win-64` only resolves and locks; the rig-PC session is scripted step by step in `WINDOWS-TEST.md`), and the macOS router with the stock IPv6 config (the @@ -282,7 +282,7 @@ without further changes; **"ROS Studio" = that Studio served from the `/ros/` Pa Studio v0.95 served from `localhost:8092` (this branch), `pixi run rig -- --sim --bias` (model fly at 50 Hz, typed FicTrac topic, real BIAS Mac port on :5010, bridge node recording with the `crashsafe` profile), the G6 2×10 bench controller over Web Serial (Michael clicked the port -picker), protocol `protocols/bias_camera_closed_loop_poc.yaml` (6 steps: camera start → 15 s +picker), protocol `protocols/ww18/bias_camera_closed_loop_poc.yaml` from the private `reiserlab/arena-experiments` (in this repo's `protocols/` until 2026-10-29; 6 steps: camera start → 15 s closed loop → camera sync → 15 s closed loop + 90 °/s bias → camera stop → shutdown). | | result | diff --git a/rig/WINDOWS-TEST.md b/rig/WINDOWS-TEST.md index b783a7c2..a62804a4 100644 --- a/rig/WINDOWS-TEST.md +++ b/rig/WINDOWS-TEST.md @@ -52,7 +52,7 @@ pass/fail, timings, sizes and every error verbatim. 3. Follow rig/WINDOWS-TEST.md in order. Steps 1–3 need no hardware and can run unattended; stop and ask me to be at the keyboard before step 4 (BIAS) and step 5 (Studio + controller over Web Serial). Stop bridge 3.4 first if it is running. -4. Results: rig/windows-results-ww18-.md committed to the branch and pushed; add a +4. Results: a `windows-results-ww18-.md` committed to the private `reiserlab/arena-experiments` under `rigs/ww18/` (rig records left this repo on 2026-10-29, LAB-227); add a "Windows results" row to the test-plan table in PR #254's description. Small fixes may go on the branch (message names the step); never touch Production or Next. ``` @@ -70,7 +70,7 @@ Windows build with its FLIR camera, the G6 controller on USB, optionally FicTrac Visual Studio 2022 Build Tools (Desktop C++) is installed — step 1b measures the typed-message build with and without it. -**Windows notes from the first run (ww18, 2026-10-15 — `windows-results-ww18-2026-10-15.md`):** +**Windows notes from the first run (ww18, 2026-10-15 — `rigs/ww18/windows-results-ww18-2026-10-15.md` in `reiserlab/arena-experiments`):** - **Short build path for the typed build**: under `C:\Users\\Documents\GitHub\…` it hits Windows' 260-character path limit (`C1083`). Either clone to under ~45 characters before `\rig`, or, from any @@ -83,7 +83,7 @@ build with and without it. since 538cc85: 1b / 7 install on ww18 (35 s, harness 48 / 48). - **Check the controller's SD card first**: an empty SD listing / "pattern missing" can mean no card is mounted at all (ww18's old card). The site library's 2×10 set (`patterns/g6_2x10/patterns/`) plus - `protocols/bias_camera_closed_loop_poc_ww18*.yaml` (slot 2 `grating_sq`) work on any 2×10 card loaded that way. + `protocols/ww18/bias_camera_closed_loop_poc_ww18*.yaml` (`reiserlab/arena-experiments`; slot 2 `grating_sq`) work on any 2×10 card loaded that way. - **FicTrac** (pixi-built): start it with `build\Release\fictrac.bat ` from the config folder — `fictrac.exe` alone exits silently with `0xC0000135` (DLL not found). - **BIAS Atlas10 build** (atlas-bench): HTTP server on by default at 5010; uFMF by default, so `.avi` names @@ -224,7 +224,7 @@ cd .. # repo root python .claude\nocache-server.py 8092 ``` -Chrome: `http://localhost:8092/arena_studio.html?p=bias_camera_closed_loop_poc&rig=&mode=run` +Chrome: `http://localhost:8092/arena_studio.html?repo=reiserlab/arena-experiments&p=protocols/ww18/bias_camera_closed_loop_poc.yaml&rig=&mode=run` (signed in; the protocol moved to the private repo on 2026-10-29) with the rig key from `configs\rigs\index.json` that matches this arena (`cshl_g6_2x10_ball` for a 2×10 ball rig). Then: diff --git a/rig/config/bias/okey-cokey-bfs-u3-51s5m.json b/rig/config/bias/okey-cokey-bfs-u3-51s5m.json deleted file mode 100644 index ac2ce960..00000000 --- a/rig/config/bias/okey-cokey-bfs-u3-51s5m.json +++ /dev/null @@ -1,253 +0,0 @@ -{ - "camera": { - "format7Settings": { - "mode": "0", - "pixelFormat": "MONO8", - "roi": { - "height": 2048, - "offsetX": 0, - "offsetY": 0, - "width": 2448 - } - }, - "frameRate": "Format7", - "guid": "24203703", - "model": "Blackfly S BFS-U3-51S5M", - "properties": { - "autoExposure": { - "absoluteControl": false, - "absoluteValue": 0.0, - "autoActive": false, - "on": false, - "onePush": false, - "present": false, - "value": 0 - }, - "brightness": { - "absoluteControl": true, - "absoluteValue": 0.0, - "autoActive": false, - "on": true, - "onePush": false, - "present": true, - "value": 33 - }, - "focus": { - "absoluteControl": false, - "absoluteValue": 0.0, - "autoActive": false, - "on": false, - "onePush": false, - "present": false, - "value": 0 - }, - "frameRate": { - "absoluteControl": true, - "absoluteValue": 73.3232421875, - "autoActive": false, - "on": false, - "onePush": false, - "present": true, - "value": 95 - }, - "gain": { - "absoluteControl": true, - "absoluteValue": 0.0, - "autoActive": false, - "on": false, - "onePush": false, - "present": true, - "value": 0 - }, - "gamma": { - "absoluteControl": true, - "absoluteValue": 0.800048828125, - "autoActive": false, - "on": true, - "onePush": false, - "present": true, - "value": 14 - }, - "hue": { - "absoluteControl": false, - "absoluteValue": 0.0, - "autoActive": false, - "on": false, - "onePush": false, - "present": false, - "value": 0 - }, - "iris": { - "absoluteControl": false, - "absoluteValue": 0.0, - "autoActive": false, - "on": false, - "onePush": false, - "present": false, - "value": 0 - }, - "pan": { - "absoluteControl": false, - "absoluteValue": 0.0, - "autoActive": false, - "on": false, - "onePush": false, - "present": false, - "value": 0 - }, - "saturation": { - "absoluteControl": false, - "absoluteValue": 0.0, - "autoActive": false, - "on": false, - "onePush": false, - "present": false, - "value": 0 - }, - "sharpness": { - "absoluteControl": false, - "absoluteValue": 0.0, - "autoActive": false, - "on": false, - "onePush": false, - "present": false, - "value": 0 - }, - "shutter": { - "absoluteControl": true, - "absoluteValue": 998.0, - "autoActive": false, - "on": false, - "onePush": false, - "present": true, - "value": 0 - }, - "temperature": { - "absoluteControl": true, - "absoluteValue": 52.0, - "autoActive": false, - "on": false, - "onePush": false, - "present": true, - "value": 70 - }, - "tilt": { - "absoluteControl": false, - "absoluteValue": 0.0, - "autoActive": false, - "on": false, - "onePush": false, - "present": false, - "value": 0 - }, - "triggerDelay": { - "absoluteControl": true, - "absoluteValue": 13.0, - "autoActive": false, - "on": false, - "onePush": false, - "present": true, - "value": 0 - }, - "triggerMode": { - "absoluteControl": false, - "absoluteValue": 0.0, - "autoActive": false, - "on": false, - "onePush": false, - "present": true, - "value": 0 - }, - "whiteBalance": { - "absoluteControl": false, - "absoluteValue": 0.0, - "autoActive": false, - "on": false, - "onePush": false, - "present": false, - "valueBlue": 0, - "valueRed": 0 - }, - "zoom": { - "absoluteControl": false, - "absoluteValue": 0.0, - "autoActive": false, - "on": false, - "onePush": false, - "present": false, - "value": 0 - } - }, - "triggerType": "Internal", - "vendor": "Point Grey Research", - "videoMode": "Format7" - }, - "configuration": { - "directory": "C:/Users/labadmin/Documents", - "fileName": "bias_config" - }, - "display": { - "colorMap": "None", - "orientation": { - "flipHorizontal": false, - "flipVertical": false - }, - "rotation": 0, - "updateFrequency": 15.0 - }, - "logging": { - "autoNamingOptions": { - "cameraIdentifier": "CameraNumber", - "includeCameraIdentifier": true, - "includeTimeAndDate": true, - "includeVersionNumber": true, - "timeAndDateFormat": "\u0027date\u0027_yyyy_MM_dd_\u0027time\u0027_hh_mm_ss" - }, - "directory": "C:/rec/okey-cokey", - "enabled": false, - "fileName": "bias_video", - "format": "ufmf", - "settings": { - "avi": { - "codec": "MJPG", - "frameSkip": 1 - }, - "bmp": { - "frameSkip": 1 - }, - "fmf": { - "frameSkip": 1 - }, - "jpg": { - "compressionThreads": 10, - "frameSkip": 1, - "mjpg": true, - "mjpgMaxFramePerFile": 5000000, - "mjpgMaxFramePerFileFlag": false, - "quality": 90 - }, - "ufmf": { - "backgroundThreshold": 40, - "boxLength": 30, - "compressionThreads": 15, - "dilate": { - "on": true, - "windowSize": 1 - }, - "frameSkip": 1, - "medianUpdateCount": 100, - "medianUpdateInterval": 50 - } - } - }, - "server": { - "enabled": true, - "port": 5010 - }, - "timer": { - "enabled": false, - "settings": { - "duration": 40 - } - } -} diff --git a/rig/docs/okey-cokey-foxglove-dashboard-plan.md b/rig/docs/okey-cokey-foxglove-dashboard-plan.md deleted file mode 100644 index 055cf91e..00000000 --- a/rig/docs/okey-cokey-foxglove-dashboard-plan.md +++ /dev/null @@ -1,176 +0,0 @@ -# Plan: a purpose-built Foxglove dashboard for the okey-cokey rig (camera + G6, no FicTrac) - -> **Status 2026-10-21: phases 1, 1b and 2 BUILT** on branch `claude/okey-cokey-rig` (off -> `claude/okey-cokey-pairs`, itself the 2026-10-20 candidate + Grace's protocol shape). Phase 3 -> (live camera) NOT built, by decision: BIAS stays unchanged for now. The okey-cokey controller -> runs telemetry firmware (Michael), so the observed side is live. Part of LAB-303. -> -> | built | where | -> |---|---| -> | `/arena/browser` published live | `nodes/bridge_node.py` (one publisher; the bag record is unchanged) | -> | tracker: expected/observed index, lag, display rate, health | `nodes/stimulus_core.py` (pure) | -> | renderer + Python port of the panel colors | `nodes/arena_display.py` (pinned cell by cell to `js/panel-color.js` under Node) | -> | live node | `nodes/stimulus_node.py`; `pixi run rig -- --stimulus` (`--foxglove` includes it), `--no-fictrac` for this rig | -> | BIAS rate / drops / rtt / writer queue | `rig_nodes.bias_health` (reiserlab/rig-nodes), `viz_node.py` (`--sources bias` hides the unused FicTrac/Kinefly/arena-frame statuses) | -> | replay: same topics from a finished bag + `/camera/image` from the uFMF | `nodes/dashboard_replay.py` via `pixi run fox-json -- --stimulus`; `tools/ufmf.py` (BIAS v4 layout, index or scan) | -> | layout | `foxglove/arena-okey-cokey.json` (15 panels) | -> -> Tested: `pixi run test-stimulus` 54/54 (incl. a REAL 80 fps trial from the 2026-10-14 smoke run: -> 800+ frames, every lag 0, 80 Hz); `pixi run test-stimulus-live` 10/10 (router + bridge + -> stimulus node, the real trial played into the Studio WebSocket); ROS harness `pixi run test` -> 54/54 and `test-viz` unchanged; `rig.py --no-fictrac --bias-sim --bias --foxglove` brings every -> dashboard topic up with foxglove_bridge on :8766; uFMF reader on three real BIAS movies -> (1920×1080, up to 2.1 GB, indexed). On the real 2026-10-14 bag the replay MCAP is 612 kB for -> 2.5 min. **Not verified:** the layout opened in the Foxglove app (written to Foxglove's layout -> format by hand; its topics are checked by the test), a live run on the okey-cokey rig, a real -> uFMF from that rig's camera through the replay. -> -> Deviations from the plan below: (1) the arena status is the stimulus node's own -> `arena/display` diagnostic instead of re-feeding viz_node's FicTrac-frame `arena` entry, which -> keeps meaning closed-loop frames; (2) an OBSERVED display image (`/arena/display_observed`) -> was added beside the expected one; (3) `/viz/arena/observed_idx` and `lag_frames` are one sample -> per displayed frame (live and replay alike); (4) the expected display's stall check is STALE only -> while a pattern plays, since the telemetry ring has nothing to send in the dark; (5) the tiff -> tool's PNG writer was not refactored into the shared module (left as it was). - -## Goal - -One Foxglove layout for a freely-walking-fly rig (overhead BIAS camera + G6 2×10 four-color arena, no -FicTrac) showing three things: - -1. **The camera.** -2. **The expected display**: what the arena should be showing right now (pattern, frame, color, duty), - rendered as an image of the unrolled arena. -3. **Health and rate** for the arena and for BIAS: is each one alive, at what rate, and has anything - dropped. - -The same layout should work **live** (foxglove_bridge during the run) and on **replay** (the run's MCAP -afterwards). - -## What exists today (release 2026-10-20, `rig/`) - -| Need | Today | Gap | -|---|---|---| -| BIAS health | `/bias/status` at 5 Hz (`bias_node.py`): reachable, capturing, logging, frameCount, framesPerSec, rtt_ms; on Atlas10 also frameIdGaps, cameraSkipped, loggerSkipped, writerSkipped, logQueueSize. `viz_node` turns these into `/viz/bias/*` and a `bias` entry in `/diagnostics`. `foxglove/arena-bias.json` shows them. | Mostly there. Missing: a rate from the frameCount slope (independent of BIAS's own estimate) and the drop counters on a plot. | -| Arena health | `viz_node`'s `arena` entry is fed only by `/arena/frame`, which only FicTrac lines produce. | **Always "never seen" on this rig.** | -| What the arena is doing | Every Studio message (trialParams `a` echoes, runner status events with `conditionName` / `params` / `durationSec`, controller telemetry `cc`/`cf`/`cs` row batches) reaches `bridge_node`. | It is **written to the bag only** (`/arena/browser`, `/arena/event` via `rec.write_raw`), never published, so nothing live can subscribe. The `a` hex is truncated to 8 bytes (no gain/duration). | -| Expected display image | `tools/patfile.py` reads `.pat`; `tools/tiff_run_to_mcap.py` already renders `/arena/display` as a `foxglove.CompressedImage` (200×40, upscaled, labeled RECONSTRUCTED). | Offline only, and wired to the 2P color-sweep file names and DO1 timing. Monochrome. | -| Camera image | Nothing. BIAS's HTTP API has no frame grab (only status, timestamps, frame count, video file). | **No live frames anywhere.** After the run, the `.ufmf` plus the per-frame stamps on `/bias/frames` (recorded at stopRecording) are enough to rebuild a timed image stream. | - -## Design - -### A. Make the arena's activity a live topic (bridge_node, small) - -`bridge_node` publishes what it already records: - -- `/arena/browser`: String JSON, verbatim, the same record it writes to the bag (QoS reliable, depth 50). - It stays in the bag as now. - -That one change lets any node watch the run without touching the Studio or bridge 3.4. - -### B. `stimulus_node.py`: expected display + arena health (new node, the core of the work) - -It subscribes to `/arena/browser` and keeps the current stimulus state: - -- From runner status events and the trialParams echoes: condition name, pattern name, mode, frame - rate, start frame, duty, and the trial start time (the echo's `ms`, i.e. when the command left the - browser). -- Expected frame for Mode 2: `idx = (init + round(fps · (t − t_start))) mod n_frames`. `allOff` = dark. - Mode 3 is out of scope here (it needs FicTrac). -- Observed frame: telemetry `cf` rows carry the frame the controller actually put up (idx + pattern - per frame change). Arrival is in ~10 Hz batches, so the observed stream lags by ~100 ms, but each row - has its own controller timestamp. - -It publishes: - -| Topic | Type | Rate | What | -|---|---|---|---| -| `/arena/stimulus` | String JSON (later typed) | on change | `{condition, pattern, mode, fps, init, duty, t_start}` | -| `/arena/display` | `foxglove.CompressedImage` (PNG) | 10–15 Hz | the expected display: unrolled 200×40, ×4 upscale, top row = top | -| `/viz/arena/expected_idx`, `/viz/arena/observed_idx` | Float64 | 10–15 Hz / per row | for one plot of the two indices together | -| `/viz/arena/display_rate_hz` | Float64 | 1 Hz | frame changes per second from `cf` rows; should equal \|fps\| (80) during a trial and 0 in the dark | -| `/viz/arena/lag_frames` | Float64 | 1 Hz | expected − observed (mod n), the "is the arena keeping up" number | -| `/diagnostics` entry `arena` | DiagnosticStatus | 1 Hz | OK / WARN (rate off by > 5 %, lag > 2 frames, a `display_gap` or flagged `trial_quality`) / STALE (no telemetry for > 2 s while a run is open) / ERROR (non-ok command status) | - -**Color, not green.** The four-color panels light one bank per pattern, so a green ramp would show the -wrong thing. Port the minimum of `js/panel-color.js` to Python (the layout table, and -`bankAt(row, col) = 2·((row+1)%2) + (col%2)` in host coordinates), and pin it to the JS module with a -test. Duty scales the brightness. Without that test, the two copies will drift. - -**Patterns.** The node needs the `.pat` files. It looks them up by name under a `--pattern-dir` (the -protocol's `*_patterns/` folder) and caches them. If a pattern is missing, the image shows the name on -a gray placeholder; the run is never refused. - -**If the firmware has no telemetry** (GET_FIRMWARE_VERSION flag bit 2 off, or the poller is off), there -are no `cf` rows: show the expected display only, observed = "unknown", and arena health from command -status alone. It must never claim "OK" without evidence (the same rule as `trial-quality.js`). - -### C. BIAS health additions (viz_node, small) - -- `/viz/bias/fps_from_count`: frameCount slope over the last 2 s of 5 Hz polls, next to BIAS's own - `framesPerSec`. -- `/viz/bias/drops`: the sum of the Atlas10 counters (frameIdGaps + cameraSkipped + loggerSkipped + - writerSkipped) as a plot; WARN on any increase while logging. Absent keys (Spinnaker build) mean - "not reported", not 0. -- `/viz/bias/rtt_ms` from the status polls; WARN at > 100 ms. - -### D. Camera - -- **Replay (phase 2).** `ufmf_to_mcap.py` reads the run's `.ufmf` and writes a sidecar MCAP with - `/camera/image` as `foxglove.CompressedImage` JPEG, timed by the `/bias/frames` stamps from the run - bag, downsampled (for example 10 Hz, half resolution) so the file stays small. Foxglove opens the run - bag and the sidecar together. uFMF needs a reader: write a small one (the format is documented, and - motmot's reader is Python 2-era), and test it on a ww18 recording. -- **Live (phase 3, only if wanted).** BIAS has to push frames out: a small BIAS patch (in our - atlas-bench fork) publishing a rate-limited JPEG preview over ZMQ. A node then mirrors it as - `/camera/image`, copying `scanimage_frames_node.py` (rate limit + loss stats). Until then, keep the - BIAS window beside Foxglove during the run. - -### E. The layout: `foxglove/arena-okey-cokey.json` - -``` -┌──────────────────────────────┬─────────────────────────────────────┐ -│ Image /camera/image │ Image /arena/display (expected) │ -│ (replay; live after phase 3) │ caption: condition · pattern · duty │ -├──────────────┬───────────────┼─────────────────────────────────────┤ -│ Indicators │ Plot: BIAS fps (own + from count), drops │ -│ BIAS reach/ ├─────────────────────────────────────────────────────┤ -│ capture/log; │ Plot: arena expected vs observed idx; display rate │ -│ arena state ├─────────────────────────────────────────────────────┤ -│ │ StateTransitions: /arena/stimulus condition │ -├──────────────┴──────────────────┬──────────────────────────────────┤ -│ DiagnosticSummary (/diagnostics)│ RawMessages /bias/reply │ -└─────────────────────────────────┴──────────────────────────────────┘ -``` - -### F. Replay parity - -The derived topics (B, C) do not exist in bags recorded before this work, and live ones are not -recorded unless we choose to. Do both: - -- Record `/arena/stimulus` live (it is small). Do not record `/arena/display`: it is rebuilt from - stimulus + patterns. -- Extend `nodes/foxglove_json.py` (the offline converter) to run the same `stimulus_node` logic over - the bag's `/arena/browser` records and emit `/arena/display` and the `/viz/arena/*` topics. Then the - layout works on any run, including today's. - -The logic lives in one pure module (`nodes/stimulus_core.py`) used both by the live node and by the -converter, the same split as `studio-replay.js`. - -## Phases and size - -| Phase | What | Size | Gate | -|---|---|---|---| -| 1 | A + B (no color port yet) + C + layout; harness test with the Studio emulator sending trialParams + `cf` rows | ~1 day | live on the Mac bench with the 2×10 arena | -| 1b | Python color port + test pinned to `js/panel-color.js` | ~2 h | the four banks show their colors | -| 2 | F (offline converter) + D replay (uFMF reader → sidecar MCAP) | ~1 day | today's okey-cokey run opens with camera + expected display in sync | -| 3 | D live (BIAS ZMQ preview patch + node) | ~1–2 days, BIAS C++ | only if a live camera in Foxglove is worth more than the BIAS window | - -## Open questions - -1. ~~Is a live camera in Foxglove needed?~~ Not now (2026-10-21): BIAS stays unchanged; replay has the camera. -2. ~~Telemetry firmware on the okey-cokey controller?~~ Yes (Michael, 2026-10-21). -3. Which BIAS build is on the okey-cokey PC: the Atlas10 fork has the drop counters, the Spinnaker - build may not (the panels then read "no data", never 0). -4. Typed messages: `viz_node` subscribes to `/arena/frame` as String, which breaks in the `typed` env. - That doesn't affect this rig (no `/arena/frame`), but fix it when touching viz_node. diff --git a/rig/okey-cokey-pc-2026-10-21.md b/rig/okey-cokey-pc-2026-10-21.md deleted file mode 100644 index 10b3171a..00000000 --- a/rig/okey-cokey-pc-2026-10-21.md +++ /dev/null @@ -1,167 +0,0 @@ -# Okey-cokey rig PC — BIAS + G6 color optomotor test, 2026-10-21 - -The run sheet for today's test on the new lab PC in the okey-cokey room (freely walking flies, overhead -BIAS camera, G6 2×10 four-color arena, **no FicTrac**). It is `WINDOWS-TEST.md` cut down to what this -rig needs, with the ww18 findings of 2026-10-15 (`windows-results-ww18-2026-10-15.md`) applied up front. -Record results in a sibling `okey-cokey-pc-results-2026-10-21.md` in the same shape as the ww18 file. - -Branch: `claude/okey-cokey-rig` = the 2026-10-20 release candidate (Studio v0.97, ROS 2 graph, -BIAS connector) + the CW/CCW-pair protocols Grace asked for (`claude/okey-cokey-pairs`) + the camera + -G6 Foxglove dashboard (`docs/okey-cokey-foxglove-dashboard-plan.md`). Neither is on a Pages tier, so -serve the checkout locally (step 4). - -## 0. Setup (bare PC → ready) - -Do [`okey-cokey-pc-setup.md`](okey-cokey-pc-setup.md) first: PC access, git / pixi / Chrome / -Foxglove, BIAS + camera SDK, the controller, the clone at `C:\w` on `claude/okey-cokey-rig`, -the three `pixi install`s, both dashboard tests and a no-flies smoke of the whole stack. The steps -below assume it is done. - -- [ ] IR backlight on by hand (Arduino). The web runner does not drive the backlight yet. - -## 1. BIAS itself (10 min) - -- [ ] **Which BIAS build?** FLIR/Spinnaker build or the atlas-bench Atlas10 fork (ww18 ran the fork). - Note the exe path and camera model. -- [ ] External-control server on, port **5010** for camera 0: - `curl.exe "http://127.0.0.1:5010/?get-status"` returns `"success" : true`. Note any extra keys. -- [ ] Logging format = **ufmf** in BIAS's own settings (BIAS writes its configured format whatever - extension it is sent; ww18 B1). BIAS v0.73.1 (Spinnaker) resets to jpg on **every** restart (ww14), - so after each BIAS start run - `powershell -ExecutionPolicy Bypass -File tools\bias-ufmf.ps1 -VideoDir