Repository navigation
Expand file tree
/
Copy path.cursorrules
More file actions
107 lines (97 loc) · 36.6 KB
/
Copy path.cursorrules
File metadata and controls
107 lines (97 loc) · 36.6 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
# JoystickPushTask — Project Plan (condensed 9 Oct 2026)
Head-fixed mouse 1D forelimb object-push task, forked from the Mathis joystick-pull LabVIEW rig. This file is force-tracked (path is in `.gitignore`, but edits still commit). Keep one copy only. Older detailed notes live in git history. Where this file disagrees with older notes, this file wins.
## Safety rules (always)
- Class 3B 635 nm laser on `PFI3` / terminal 42: keep keyed OFF. Never toggle PFI3 blind. Do not touch the laser pulse nodes in the magnet loop.
- Never power the Ledex coil from AO0 (AO0 is only the 0–10 V command; PCIe-6321 AO limit ±5 mA).
- 1 A inline fuse must stay in the 12 V positive lead of the PVA/Ledex supply. Do not use the magnet's reserved 12 V Goobay supply for anything else.
- Normal Stop only; toolbar Abort is emergency-only (skips cleanup frames and the auto timing file).
- Never press an animal's paw onto the pad. Keep `mag = 0` until the unperturbed task is stable.
- Never hand-edit `.vi` binaries or internal names. Do not Git-merge independently edited `.vi` files; the repo copy is the canonical hierarchy.
## Working conventions
- Two machines share this repo through GitHub `master`: the rig PC (Windows, LabVIEW, hardware, data) and a Mac (planning, docs, parameter files, Python scripts, CAD). Pull before editing; commit and push after each finished step. Commits use the `maxchalabi` identity and imperative sentence messages ending in a period; no Cursor co-author trailer (a local commit-msg hook strips it).
- The VIs are edited by hand in LabVIEW on the rig PC, so guide changes one small step at a time (which node, which wire, how to check it) and have the user confirm before moving on. Agents never edit `.vi` binaries. Prefer parameter files, front-panel controls and Python/scripts over diagram changes where possible.
- After every step, update this file with the new status (and keep it short: replace stale items, do not append logs). The repo is public: keep this file free of private chat, people's names and purchasing/admin details.
- Bench-test everything animal-free first; one variable at a time; no improvising circuit changes during an animal session.
- Parameter files must be validated after editing: exact row count, 27 tab-separated columns per row, consistent row definition.
## Rig and DAQ
- NI PCIe-6321, SCB-68A breakout (terminal map in `RIG_INVENTORY.md`). Saved NI-MAX tasks are incomplete; audit inline channels. NI-MAX backup: `ni-max/JoystickPushTask_NIMAX_2026-08-20.nce`.
- Channels:
- AI task `joysticklickframe_push`, continuous 10 kHz, order `ai1` (Y = push, white, queue idx 0), `ai0` (X = lateral, red, idx 1), `ai2` (rest pad, RSE 0–5 V). `joystick pos` = Y, X, 0, rest pad (idx 3, the slot the lab DataJoint layout calls FSR).
- AO0 `MagnetPush_Dev1` (Ledex via PVA); AO1 `LickSpout_Dev1` (Actuonix L12-30-50-12-I, 0–5 V, own 12 V PSU).
- DO `Water_Dev1` P0.0; `SuccessCue_Dev1` P0.1 (terminal 17, D GND 15; Adafruit #1536 buzzer).
- `frame counter_Dev1` on PFI8 (ctr0); pulse out ctr1 on PFI13 at 50 Hz. Camera runs on the rig PC.
- Rest pad terminals: +5 V terminal 8, `ai2` terminal 65, AI GND terminal 64.
- `avg joystick and frame trig lick3.vi` acquisition loop: Read stays **10** samples (1 kHz logged), input buffer **100000** installed, in-loop `stop in` Value property node deleted, `stop all` global (`PushTask Globals.vi`) drives all loops. Earlier -200279 overrun is provisionally fixed; a 25–30 min no-animal run with rewards, stopped cleanly, is still to be reported. Do not switch Read to 100.
- Do not run two simultaneous AI tasks (one AI timing engine). 1 MΩ source impedance ghosts `ai0` onto `ai2` on this multiplexed ADC.
- Data on the rig: `C:\Users\Mathis_Lab_1\Desktop\RIG1_DATA` (`joystick_`, `REWARD_`, `TRIAL_`, `EVENTS_`, `TIMESTAMPS_LABVIEW_`). Python 3.6.4 at `C:\Users\Mathis_Lab_1\AppData\Local\Programs\Python\Python36-32\python.exe`.
## Current status (18 Oct 2026)
- **Rest-pad sensor (capacitive, installed and bench-validated 14 Oct).** Arduino Nano (classic ATmega328, not Nano Every) + 10×10 mm copper-tape pad under one layer of tape + 10 kΩ pot (sensitivity knob). Sketch `arduino/touch_pad/touch_pad.ino`, wiring and calibration in `arduino/touch_pad/README.md`. Output is push-pull 0 V / 5 V on D2 into `ai2` (terminal 65; 5 V = touch; low impedance, no X ghosting); Nano powered from rig +5 V (terminal 8), GND to AI GND (64); unplug USB when running from the rig. `rest pad threshold` 2.5 V (default saved in the VI, `3fe0e0f`); Case 0 starts only with pad touched AND joystick home. Re-tare the knob (hands away) after any change to the pad or its cover. Normal Stop clean; 25–30 min no-animal run done.
- **50 Hz chatter (found in mouse session 1, 16 Oct).** With the paw on the pad, `ai2` toggled every ~20 ms (high ~13 ms, low ~8 ms; 10,572 touch episodes, only 87 ≥ 100 ms) whenever the knob put the threshold near the paw signal (mostly from minute 16 on, when trial starts stopped). The period drifts against the DAQ clock, so it is mains hum picked up through the mouse, not the camera pulse train. The knob cannot fix it. Sketch fix uploaded, bench-tested and reinstalled on the rig (16 Oct, `ai2` ~0 / ~5 V in NI-MAX): readings averaged over exactly one mains period (20 ms, decision every 10 ms), `OFF_FRACTION` 0.5, touch hold 20 ms, release hold 40 ms, Serial Plotter `ripple` field. New timing: touch-on ~30–50 ms, release ~50–60 ms. `archive/restpad_chatter.py joystick_<name>` (moved from `scripts/` 18 Oct) reports chatter per session (session 1 baseline: 210 chatter seconds, 0.8 % of episodes ≥ 100 ms).
- Mains-filter sketch confirmed in mouse sessions (16 Oct, zuchon and blackmamba): 0 chatter seconds, touch episodes median 1.2–1.7 s, ≥ 99 % of episodes ≥ 100 ms (anaconda, old sketch: 210 chatter seconds, median 13 ms). Pad sensitivity, sketch and position are final. Deferred: check with the Fluke whether the metal head-fixation clamp is connected to AI GND 64; if not, add one wire clamp → 64 and compare `ripple` with and without (do this if chatter remains).
- Open items: water-drop/licking check on the rig with the spout extended; pad lead is ~40 cm plain wire (target <30 cm); shielding deferred (2-photon first, ephys uncertain; compare imaging noise with the sensor on and off at the first imaging session).
- Data impact: joystick-file column 4 (`ai2`, `joystick pos` idx 3) is a ~0 V / ~5 V touch signal (switch lag ~30–50 ms on, ~50–60 ms off with the mains-filter sketch; no force information); file layout unchanged. DataJoint must treat it as Boolean for new sessions and record the sensor type (`fsr402` or `capacitive`) per session; no other DataJoint change needed (column renamed fsr→rest pad).
- Rejected for good: AT42QT1010 (recalibrates after ~60 s), Omron E2K-F (too big), M12/M18 industrial sensors, load cell (fallback only), SingleTact and FlexiForce. Earlier FSR 402 history: `archive/FSR_rest_pad_history.md`.
- **Mice (back on water restriction Wed 14 Oct).** Thu 15 Oct: ~25 min head-fixation per mouse at 4 µl rewards (habituation to the smaller volume; earlier 8 µl). Fri 16 Oct: first rest-pad hold + micro-push session for 3 mice (numbers below from the files; 4 µl rewards):
- `anaconda` (`02_micro_push_day1_restpad.txt`: home 2.47–2.56, success < 2.47, `home TO` 100, ITI 2000; 29 min): 37 rewards / 161 trials (23 %), 31 of them in the first 15 min; trial starts stopped after ~20 min (old sketch, 50 Hz chatter). Home lower bound = success bound, so trials starting at ~2.47 V succeeded on noise (2 rewards with a 3 mV excursion). In 28/37 rewards the pad stayed touched during the push (other paw, or paw on cube and pad together, cube was in the middle hole).
- `zuchon` (`_home2p44.txt`: home 2.43–2.52, success < 2.43, ITI 2000; 28 min; `_2` is a 10 s false start, discard): 4 rewards / 85 trials, all four the intended sequence (pad released 150–390 ms after trial start, then push). Joystick/cube moved during the session (home drifted 2.45 → 2.47 V; needed ~40 mV push). From minute ~11 the paw never returned to the pad (no touch for the last 16 min) and kept pushing the cube (45–52 push bouts per 5 min, 197/204 outside trial windows): cube too close.
- `blackmamba` (`_home2p475.txt`: home 2.475–2.51, success < 2.47, `home TO` 50, ITI 1500; final position; 34 min): 19 rewards / 250 trials (7.6 %; user recalled ~37), rising later (14 after minute 17). 12/19 rewards with the pad released first (0.1–2.8 s), 7 with the pad touched throughout (check video). 169/231 failed trials: paw rested on the pad for the whole 3 s window. 179 push bouts (median 78 mV, ≫ the 16 mV needed), only 29 inside trial windows; most outside pushes had no pad touch in the second before (paw went to the cube without a pad hold). All 250 rows of the file were used (last trial at 34.0 min).
- Next: next session in the final position with `_home2p475` settings but a longer file (≥ 500 rows; check what the VI does past the last row) and consider `move time` 3000 → 5000–8000 ms (pushes starting within 3 / 5 / 8 s of trial start in blackmamba: 17 / 28 / 40 trials). Check video for pad-touched successes. Water earned is small (≈ 150 / 16 / 76 µl), so supplement to the daily allowance.
- Data issues: zuchon's magnet loop missed trial 84 (after a 328 s pause: no code 20/21, TRIAL file 84 rows vs 85 trial starts in EVENTS), so `make_labview_timing.py` refuses that session; anaconda and blackmamba timing files build (drift +4.5 ppm, worst mismatch 6–8 ms). Find the cause (magnet-loop Wait on Occurrence) before any nonzero `mag`. **Fixed joystick/cube position (16 Oct, final after session 1 adjustments): the front face of the cube is 6 mm from the edge of the platform and 7 mm from the edge of the rest pad (the pad sits on the platform); reuse these distances on any other rig or mount.** Changes made during/after it: the cube is mounted in the hole nearest the rest pad (not the middle hole) so the paw cannot touch cube and pad together; the rest pad moved slightly to the right so only the right paw can reach it. Remeasure home voltage after these moves. Stage-1 procedure, if used instead: use only `deliverWater_RIG1_cue.vi` (50 ms cue then water), `Water on time (ms)` 240 (~8 µl), extend spout to 3.0 V before the first reward and retract to 0 V at the end (that VI does not move the spout). Keep the push VI closed. Cap 25 min and the approved water limit; stop when licking fades; tally rewards. Pad-contact shaping fallbacks `01_pad_contact_shaping_50ms.txt` / `_250ms.txt` (`home TO` 50 / 250 ms, otherwise identical) are untested with the capacitive pad; do a bench check first. First pilot (28 Sep) and shaping attempts showed contact activation was too rare for contingency learning.
- **Ledex `195224-230` + PVA 1.6 (0–10 V)**, following `MAGNET_COMMISSIONING.md`. Checkpoints 1–4 DONE (11 Oct), Checkpoint 5 (mount and direction) waits for the printed holder (next week).
- Wiring (verified): 12 V Goobay + → 1 A ESKA fuse (TC-R3-32AA1 holder) → `D`, − → `F`; SCB 22 → `H`, 55 → `G` (twisted); coil on `C`/`A` (no polarity); Enable `J` on a wire with a clip that is clamped on the `D` screw to enable and removed to disable. `K`, `M`, `E` empty. Supply 11.91 V, LED red when disabled. Leave the supply unplugged and the clip off between tests.
- Trimmers (left to right, from the datasheet legend): rising ramp, falling ramp, Imax (3), Imin (4), dither. Imin and Imax are ~30 turns counterclockwise (slip clutch, no hard stop); counterclockwise = minimum confirmed for Imin (≤ 0.006 A at 0 V with Enable on).
- Coil current vs AO0 (plunger seated, cold, 2–3 s pulses, Fluke 10 A DC in series): 0.5 V 0.053, 1 V 0.074, 2 V 0.106, 3 V 0.121, 4 V 0.147, 5 V 0.180, 6 V 0.201, 8 V 0.273, 10 V 0.310 A. No sound or heat, clean release. 0.58 A cannot be exceeded at this Imax, so no command clamp is needed for current; Imax stays at minimum for now (raise clockwise in small steps only if more force is needed).
- Pulse method used: set ao0 in NI-MAX Test Panels (Analog Output tab, `Dev1/ao0`, never `ao1`) with Enable off, clip on `D` for 2–3 s, unclip, back to 0 V, 10 s off. Measure H–G resistance only with the SCB end disconnected (a live ao0 makes the Ω reading fluctuate). Move the Fluke red lead back to `VΩ` after current tests.
- Plunger: 60° (cone-ended) type; the cone end goes inside against the matching taper and the plain pull end protrudes (tip still protrudes when seated); never reverse it, never run it loose. Without the plunger no pull could be felt even at 10 V. Design chosen: plunger linked to the joystick shaft (see "Plunger link"); the washer target is rejected (re-checked on the measured rig, 15 Oct).
- Datasheet (STA 20 × 40 mm pull, 30 AWG, 1491 turns; PDF and digitized 60° curves in `simulations/ledex_femm/datasheet/`): seated holding force 12.8 N (60°) at 0.57 A; plunger pull at 0.57 A 5.35 / 3.38 / 2.14 / 0.78 N at 1.5 / 3 / 5 / 10 mm stroke. Seated pull end is 23.81 mm beyond the body face (M14 boss 9.52 mm on the plunger side). Nothing is published for an external target.
- Ledex force models (`simulations/ledex_femm/`, README there; FEMM 4.2 in `C:\femm42` on the rig and under Wine on the Mac; `femm_run.py` runs it in parallel, `calibrate.py` fits it). FEMM (2D) calibrated against the datasheet curves (12 Oct): error ±15–20 % incl. a held-out curve; still 1.4–2× high beyond 8 mm stroke. One Ledex geometry for all models: `simulations/ledex_femm/ledex_geometry.py` (body, plunger, slot, cross hole; shared by the reference CAD, rig model, FEMM and 3D). FEMM uses axisymmetric stand-ins for the slot and cross hole (plunger force changed ≤ 2 %, calibration error 0.178 → 0.180, kept); the 3D model now uses the real slotted, cross-drilled tip (washer pull: short towers +15–25 %, tall towers −7 %). Washer forces come from the 3D model (`fea3d.py`, NGSolve venv `/tmp/ngenv`, set `FEA3D_THREADS` when running sweeps in parallel; `sweep3d.py`, incl. the `offset` group: tower moved vertically, half model); treat as ±40 %. Previous results are in `results/*/axisym_tip/`. Holder axis range 38–78 mm is one setting (`HOLDER_H_RANGE` in `rig_geometry.py`) used by the CAD, forces and explorer. Mathis 2017 gives no force at the joystick (replaced the stock ~4 N base spring to get 0.1–0.2 N = 10–20 g).
- **Rig geometry (15 Oct; `rig_geometry.py`, README "Rig geometry").** Built from the measured joystick component and component holder CAD (`mechanical/joystick-component/`, `joystick-holder/`); world X = push (+ away from the mouse), Z up from the breadboard, origin under the joystick axis. Shaft base Z 43.18 (screw head glued in the shaft's bottom bore, screw into the stub, cone seated on the stub top), constrainer 15.41–71.41 on the holder frame; its home-wall arch (top Z 63.41) is cut square up to the ceiling Z 68.41 in the window revision (`joystick-1d-axis-constrainer/make_ledex_window.py`, print that one), cube contact Z 135.6. Pivot assumed mid-body, 23.5 mm below the shaft base (bounds 19.3–27.3); rig check: push the cube 10 mm against a ruler, r = shaft travel at height z / 10, pivot depth = (92.4 r − z)/(1 − r). The shaft is in the cube's front hole (push face 2.05 mm from the axis, `CUBE_HOLE` in `rig_geometry.py`; forces recomputed 16 Oct: change < 1 %; models switched to the narrow cube 17 Oct: effective mass 4.2 g, forces unchanged). A 15 mm push tilts the shaft 7.4°; lever predicts ~0.49 V per 10 mm push vs the provisional 0.30 V reading; remeasure. The constrainer slot's home end stops the shaft 1.15 mm (at the cube) on the home side of the spring's neutral, so the magnet pulls the handle onto that stop at home (home shifts ~0.06 V on `ai1` with the magnet on): move the stop to neutral (slot end Y −1.5 or a 0.5 mm shim) or record it.
- **Plunger link (chosen design; `plunger_link.py`, `rig_forces.py`, README "Plunger link" and "Washer target vs plunger link").** Rigid 14 mm link (1.8 × 5 mm bar in the plunger's 2.5 × 9.5 mm tip slot) pinned through the plunger's factory 2.5/2.6 mm cross hole 4.76 mm from the tip (datasheet F34; nothing to drill; slot turned vertical, 2.5 mm × 10 mm brass/A2 pin) to a collar fork pin 6 mm off the shaft axis on the home side (collar: ring with 3.7 mm bore, fork, one M2 set screw on +Y; nothing may stick out on +X, it would hit the constrainer near 15 mm). Ledex body and plunger modelled from the drawing in `mechanical/ledex-195224-model/` (plunger 59.7 mm long, mesh masses match the datasheet). Link height limits: shaft z 15 to 20.3 (window top); height barely changes force. Recommended: link z 17.5 = Ledex axis Z 60.7, home stroke 3 mm at neutral = body face X −42.05; moving parts clear by ≥ 2 mm from the home stop to 15 mm (bar play 0.35–0.4 mm in the slot and fork); stroke 3.0 → 8.35 mm over 15 mm. Resistance at the cube, push 0 → 15 mm: 1 V 2.3 → 0.64, 5 V 13 → 3.8, 10 V 40 → 11, 0.58 A 125 → 39 gf (pivot bounds −7/+6 %). Washer target at its best layout (Z 58.7, 1 mm gap): 10 V 0.75 → 0.06 gf, i.e. the link is ~53× stronger. Spring: fitted, no preload (force = k × push, k adjustable); plunger friction at the cube is 1.2 / 2.3 / 3.0 gf (mu 0.15 / 0.3 / 0.4), leaving a dead band where k × push < friction. Model at mu 0.3: k 0.2 gf/mm never returns (stops at +8.4 mm), 0.5 gf/mm returns in ~60 ms, 0.67 gf/mm in ~48 ms; measure the real k on the bench (gram scale). The explorer has no preload slider (removed 18 Oct). Its sliders show physical distances rather than world coordinates: **stroke length (mm)** (plunger stroke from seated at neutral, 0 = fully seated; link option) or **distance to target (mm)** (gap to the washer tower at neutral; washer option), **magnet height (mm)** (height distance above the pivot), and **pivot depth (mm)** (relative to the bottom face of the TS-1D1S00A component, 2.0–10.0 mm, default 5.77 mm). All sliders span the feasible range (recomputed from the feasibility checks; beyond a 12.5 mm stroke the pull is extrapolated) and can also be typed. Moving the pivot depth changes only the magnet-height reading and range, never the magnet position. A "set default" button resets every slider (layout, pivot, push, drive, spring, friction). A button switches all forces (readout, plots, stiffness) between gf and N (stiffness as N/m). Push at the cube runs from the home stop to 20 mm (the slot would allow ~36 mm at the cube). Explorer: `figures/rig/rig_explorer.html` (all parts, sliders, live forces, design map; force plots use linear gf axes, lateral Y fixed at 0).
- Final install rules: PVA outside the mouse box; command wires 30–100 cm twisted; coil cable 1–2 m twisted and away from `ai0/ai1/ai2`; short pulses and long cool-downs; stop on heat, smell, binding, wrong direction or failure to release; re-check 0 V and a small command after moving anything. Random-force sessions need this proportional driver (not a binary switch).
- **Adjustable Ledex holder (15 Oct; `mechanical/ledex-holder-adjustable/`, generated by `make_ledex_holder.py`, not printed).** Base with two M6 slots along X (33 mm travel, 25 mm or 1 inch grid), upright with lateral M4 slots (±12.75 mm) and two long vertical M3 slots (axis Z 38–78, 1 mm scale), carriage on the M14 boss held by 4 × M3 above and below the case into captive nuts (adjustable from outside with the Ledex fitted); link collar (gap and stroke gauges archived in `archive/ledex-gauges/`, 17 Oct, and removed from the generator) and bar, `fit_test` (print first: holes print ~0.3–0.4 mm small, cube's 3.4 mm holes just pass the 3 mm shaft; allowances in the `FIT` block). Clearances to the joystick holder/constrainer: link layout ≥ 16 mm, washer layout ≥ 4.3 mm; `make_ledex_holder.py check` reruns the static and moving checks.
- Next: caliper-check the plunger (slot width, cross hole Ø and distance from the tip, seated protrusion 23.81, length) and the M14 nut; print `fit_test` (adjust `FIT` if needed), then the window constrainer, holder, collar and link, fix the home stop, then bench checks without mice: spring curve with a gram scale at the cube (0 / 5 / 10 / 15 mm, with and without the plunger, Ledex off; preload ≥ 2× the difference), release test 10× from 15 mm back to home within ±0.035 V on `ai1`, hard stop holds at 10 V with clean release at 0 V, scale force at 5 V and 10 V at a 5 mm push to calibrate the model; then Checkpoint 5 (spring return, no binding, release at 0 V). LabVIEW magnet safety (guaranteed 0 V on every exit path, logging) still to do before any nonzero `mag`. Spare: Bürkert 316532 (due 2 Dec) if not cancelled.
- **Rest-pad release clause (built and diagram-reviewed, bench test pending, 19 Oct).** Problem in session 1: `move time` started at the Case 0 pass while the paw stayed on the pad, so the mouse started many trials it never completed. Case 0 next state is now **5**; new **Case 5** waits for paw release only (`ai2` = `joystick pos` idx 3 < `rest pad threshold`, read local variable; joystick ignored); on release it writes Tick Count → `trail start` (the `move time` clock, moved from Case 2), logs new EVENTS **code 6** (paw release), then → Case 1. Case 2 keeps Set Occurrence (magnet loop: `Trial #` + TRIAL row) and code 3 at the start zone, so `make_labview_timing.py` (code 3 must equal TRIAL samples) is unaffected. Case 1 keeps the legacy start box = the "start position" (later halfway between home and end); paw re-touch does not reset. Before making the start box nontrivial, Case 1 needs a `move time` timeout → Case 0 (write current time to `start time`, log code 5, set `in trigROI` F), otherwise failure to reach a separated start zone would wait forever. Bench test with `parameters/training/02_micro_push_day1_restpad_home2p44-2p49.txt`: home 2.44–2.49 V held for 100 ms, full-range start box 0–5 V (irrelevant), success 0–2.35 V. Next: animal-free bench test (finger held on pad: no trial; lift: code 6 then trial start; lift + push: reward; movement timeout back to Case 0; Normal Stop), then record result. Data impact: code 6 marks paw release + ~50–60 ms sensor lag; code 2 values now include 5.
- **Head-fixation post moved (20 Oct).** The post was raised a bit and moved back so the first two mice can reach and use the rest pad more easily, and so the body or arm does not touch the pad by accident while the mouse works the joystick. Home voltage, pad-touch behaviour and the cube/pad reach may change: remeasure home before any joystick session. Rest platform repositioned at the same time. Verdict after the 20 Oct session: fine for all 3 mice, so the post and platform position is final (no more changes).
- **Spout at Run:** set `Session start spout (V)` = 3.0 V before Run. Day-one file holds extend and retract at 3.00 V, so the spout stays extended. Normal Stop retracts to 0 V.
- **Home voltage depends on whether the shaft and object are fitted.** Remeasure before loading any file. Current release-gated day-one file: `parameters/training/02_micro_push_day1_restpad_home2p44-2p49.txt`: home 2.44–2.49 V, `home TO` 50 ms, start box 0–5 V (irrelevant), success 0–2.40 V, ITI 1500 ms, `move time` 3000 ms, water 130 ms (~4 ul), cue 50 ms, delay 0, consumption 1500 ms, 250 identical rows, `mag=0`, spout columns 0 V. Earlier `02_micro_push_day1_restpad.txt` and `_home2p44.txt` preserve the 16 Oct settings; `02_micro_push_day1_5s_8ul.txt` is the older 2.25 V FSR-era file. `02_micro_push.txt` and stages 03–08 still use the 2.50 V / `home TO` 250 ms calibration.
- **Touch-and-release-only file (run 20 Oct with all 3 mice; user reports it went really well; numbers from the files not yet analysed, rewards/release timing/chatter still to check; file still uncommitted).** `parameters/training/01_pad_touch_50ms_release.txt` (250 rows, 27 columns checked): home, start and end boxes all 0–5 V push and lateral, `home TO` 50, `end TO` 0, so the joystick is irrelevant; pad touch → release (Case 5, code 6) → trial start → reward as soon as Case 3 runs. Rest as the day-one file (`move time` 3000, ITI 1500, 130 ms water, 50 ms cue, spout columns 0 V). Expect reward ~50–60 ms after real lift-off; set `Session start spout (V)` 3.0 first. Bench test (finger on pad, lift: code 6, one cue and one water pulse) before a mouse; needs the release-clause VI.
- **Next stage plan (20 Oct): hold + easiest micro-push.** Pad hold goes 50 → 100 → 200 ms over the next days (touch-and-release sessions); the micro-push stage should reward any push above noise. Current day-one file leaves a dead band (home 2.44–2.49, success < 2.40: a cube at 2.47 V needs ~70 mV, ~2 mm). Simplest: per-session file from the measured rest voltage r (home r ± 0.010, success < r − 0.020, keep ≥ 10 mV gap between home lower bound and success bound, optional `end TO` 10–20 ms, `move time` 5000); measure real 1 kHz noise from today's touch-only joystick files first (99.9th percentile; the ±0.035 V in `calibration/joystick-volts-mm.md` is the old live-graph value). Robust option after the release-clause bench test: Case 5 stores the push value at release as a baseline and Case 3 rewards when push < baseline − Δ (new front-panel control, e.g. 0.015 V) OR the end box; Δ = 5 V recovers the old behaviour. Drop the pad hold to 50–100 ms when the push requirement is added.
- **Success bound raised (19 Oct, committed 20 Oct); `home TO` back to 50 ms (20 Oct).** In `02_micro_push_day1_restpad_home2p44-2p49.txt` the end box is now 0.00–2.40 V (column 12; was 2.35; 250 rows, 27 columns checked). Home stays 2.44–2.49 V, `move time` 3000 ms, `home TO` 50 ms (column 4, all 250 rows; 27 columns checked). Reason: mouse `zuchon` rests the paw on the cube and settles at 2.37–2.40 V, so 2.35 V was just out of reach; a resting paw alone may now succeed after a trial start, so watch whether it is a deliberate push.
- **Bench presets** (no animal): `parameters/push_bench_full-system.txt` (20 rows; home/start match `training/07_expert.txt`; success 0.00–2.22 V, no end dwell; 50 ms cue, 500 ms reward delay, spout 0→3 V, 185 ms water, 1500 ms consumption; set Session start spout to 0 V so motion is visible; `mag=0`; never use to energise the Ledex/PVA) and `push_bench_full-system_nocue.txt` (same, cue 0 ms; if a click is heard disconnect the buzzer from P0.1). Procedure in `parameters/README.md`. `parameters/push_bench_wide-zone.txt` is the older hand-test file.
- **Repo layout (cleaned 16 Oct).** Docs describe the capacitive pad with no FSR history; old FSR bench logs are in `archive/FSR_rest_pad_history.md` and the old fixed `ledex-holder/` is in `archive/` (`archive/` is for history worth keeping). Original 20 mm cube and original (arched) 1D-axis constrainer STEP/STL moved to `archive/` (17 Oct); the generator scripts `make_cube_11p5.py` and `make_ledex_window.py` were deleted (the final STEP/STL models are the record); empty `mechanical/laser-cut` removed. Shaping fallbacks renamed `parameters/training/01_pad_contact_shaping_50ms.txt` / `_250ms.txt` (`home TO` 50 / 250 ms; untested with the capacitive pad). The docs website (Jupyter Book from the upstream repo: `_config.yml`, `_toc.yml`, `requirements.txt`, `.github/workflows/publish-book.yml`, `docs/images/logo.png`) is kept; `_toc.yml` lists all current docs (check on GitHub that the publish Action/Pages works). Upstream boilerplate kept (`LICENSE`, `CONTRIBUTING.md`, `CONDUCT.md`, `media/output.png`). Washer-simulation figures and data are kept on purpose (they show why the washer target was rejected). `.DS_Store` files are untracked. `mechanical/` has one README (parts table and notes) plus the Ledex holder README; per-folder READMEs removed and the mechanical/3d-print level flattened into mechanical/ on 18 Oct; the 38 mm bar handle moved to `archive/joystick-handle-bar/`. Joystick component model renamed `mechanical/TS-1D1S00A-1294_stripped/` (files `TS-1D1S00A-1294_stripped.step/.stl`; `rig_geometry.py` updated).
- **Repo:** the rig PC was re-cloned on 11 Oct because `origin/master` had its history rewritten (force-pushed twice); local `master` was reset to `origin/master` at `5302f6d`. Always `git fetch origin` and check `git status` before committing; if it says ahead and behind, do not merge, ask first. Rig PC synced with `origin/master` at `cb6f911` (16 Oct, day-one rest pad file, includes the Mac repo cleanup).
## Logging and sync
- Joystick file: 5 doubles per 1 kHz sample. TRIAL/REWARD files unchanged. Frame count is software-polled once per loop, not hardware-aligned.
- **EVENTS logger** (done, bench-validated, `f9a363c`; plan in `EVENTS_LOGGER_PLAN.md`): file `EVENTS_<name>`, big-endian float64, 4 per record `[code, sample_index, unix_time_s, value]`. Codes: 1 start, 2 state change, 3 trial start (start zone, Case 2), 6 paw release (Case 5, pending), 4 success (Case 4), 5 timeout, 10–18 water-loop steps (13 = extend V, 15 = water on with ms, 18 = retract V), 20/21 magnet on/off, 90 heartbeat (1 s), 99 stop. Named queue `push events`, reentrant `Log Event.vi`, one writer loop, global `sample count` (reset to 0 before code 1). Known: code 99 sample is ~0.3 s stale (excluded from fits). REWARD col 0 matches code 15, not code 4. Pending tests: camera sync run, stop from inside a reward sequence.
- **Auto timing file:** `scripts/run_timing.bat` is called by System Exec at Stop after the EVENTS Close/Release (commit `92c1cc5`) and writes `TIMESTAMPS_LABVIEW_<name>.npy` via `scripts/make_labview_timing.py` (numpy only; refuses on mismatched files, empty REWARD, >5% outliers, drift ≥100 ms). Output in `timing_log.txt`. Merge Errors for the other file closes was skipped; revisit only if "Missing session file" errors appear.
- Video timestamps (`TIMESTAMPS_VIDEO_*.npy`) are absolute Unix float64 at ~100 Hz; PC Unix time is the shared clock. File creation times are not session markers.
## Task design
- Trial: forepaw on rest pad AND object in home → trial starts → push forward into target band (held for `end TO`) → immediate auditory cue (50 ms) → reward delay → spout extends → water → spout retracts. Overshoot is not an abort; leaving the band resets the dwell; trial ends at `move time`.
- Session blocks (later): Baseline (`mag=0`), Random perturbation, Fixed perturbation, Washout. Perturbation = axial resistance along the push axis (Ledex mounted on the home side, attraction opposes the push), pre-scripted `mag` per row, force magnitudes must be varied (proportional driver).
- Training progression (criterion on two consecutive days, change one thing at a time, ~3–6 weeks): 1) head-fix habituation with cue→water (short interval); 2) micro-push discovery; 3) proximal broad zone; 4) zone translation; 5) zone refinement; 6) delay 0/250/500/1000 ms; 7) expert stabilisation (≥80% success); 8) zero-force shadow sessions; 9) resistance pilot (separate mice), then main experiment. Optional rest-only stage as fallback.
- Parameter files: one identical-row file per stage in `parameters/training/` (27 columns; see `parameters/README.md`). Loader reads cases 0–26.
Columns (0-based): 0,2 `home` push bounds (named X); 1,3 `home` lateral (named Y); 4 `home TO`; 5–8 legacy `start` box; 9 `start TO` (keep 0); 10,12 `end` push bounds; 11,13 `end` lateral; 14 `end TO`; 15 `mag`; 16 `magtime`; 17 `watertime` (130/185/240 ms → 4/6/8 µl); 18 `timeout` (Case 0 ITI); 19 `move time`; 20 `light` (0); 21 `reward delay`; 22 `cue duration`; 23 `spout extend`; 24 `spout settle`; 25 `consumption`; 26 `spout retract`. White/push values go in the columns named X; there is no ×2 on `home X2`. Do not reuse the old pull files' end voltages or `magtime=100`.
- Calibrate before final numbers: volts-to-mm (`calibration/joystick-volts-mm.md`), joystick noise, water volume (`calibration/water-volume.md`), spout settle, spring return.
- A fixed-spout 500 ms ITI with a 1.5 s consumption loop needs a rapid-success bench check (one cue and one water pulse per success).
- Perturbation notes: original Mathis kick was lateral at trial start; ours is sustained axial resistance. Minimal option `magtime ≈ move time`; cleaner is holding `mag` until a trial-end occurrence (Case 4 or Case 3 timeout) and a ~100 ms movement-onset delay. Do not reuse old `magtime=100` or the 2.2 V value; force-calibrate with a gauge/load cell and check heating.
- **Planned next LabVIEW change: start the trial (and `move time`) at pad release.** Use the existing Case 1 gate: Case 0 (pad + home held `home TO`, ITI elapsed) → Case 1 waits while the paw is on the pad; pad released AND object in the start box (= home box, `start TO` 0) → Case 2 (trial start, code 3, magnet occurrence); pad released but object out of the box → Case 0. No parameter-file change. Code 2 (state 1) then marks "armed", code 3 marks release (~50–60 ms after the real lift-off). Replaying the 16 Oct data through this rule: blackmamba 83 trials / 22 rewards (27 %; 25 and 31 rewards with `move time` 5 / 8 s) instead of 250 / 19; zuchon 21 / 6; anaconda 28 / 15 (its pushes with the paw on the pad no longer pay). Separate ITIs after success vs timeout were considered and not needed for this. Step 1 (pending): user inspects Case 1 (what it does when its condition is false; where `paw on rest pad` is computed). DataJoint: trial start means pad release from that session on.
- Future LabVIEW work: habituation mode (cue→delay→water without Case 4), rest-pad hysteresis, movement-onset-triggered or held `mag`, session metadata, one shared parameter typedef (the water-loop and loader clusters had mismatched element order; fixed, FP captions swapped for reward delay and spout retract).
## VI architecture (`Push Behaviour_MCHALABI.vi` + `avg joystick and frame trig lick3.vi`, `frame counter.vi`, `PushTask Globals.vi`)
- State machine in the middle While loop (state in a shift register, initial 0): Case 0 gate (object in home ROI AND rest pad `ai2` > threshold, held `home TO`, AND `timeout` elapsed), Case 1 start ROI, Case 2 stamp trial start and set the magnet occurrence, Case 3 movement window (timeout → Case 0; end ROI held `end TO` → Case 4), Case 4 set the reward occurrence.
- Water loop (occurrence-driven flat sequence): cue HIGH → cue wait → cue LOW → reward delay → spout extend → settle → water HIGH → `watertime` → water LOW → consumption → spout retract.
- Magnet loop: occurrence → AO0 `mag` for `magtime` (plus laser counter pulse, keyed off) → reset 0.
- Startup writes cue LOW, water LOW, magnet 0 V, spout to `Session start spout (V)`; normal Stop propagates through `stop all`; Stop tests from idle, reward delay and extended spout passed.
- Rest-pad threshold control and `paw on rest pad` Boolean sit before the `home TO` hold. Home gate and rest pad share one timer; ROI X-in-range AND Y-in-range per row.
- Known: `Trial #` is incremented at trial start (magnet loop) but indexed at success (water loop); verify alignment. The magnet loop can miss a trial-start occurrence (zuchon 16 Oct, once, after a 328 s pause), which also drops a TRIAL row.
## Mechanics and hardware facts
- Joystick: APEM TS-1D1S00A-1294 Hall thumbstick base (castle cap removed; printed shaft screwed onto the lever stub) in the printed component holder, 89 mm shaft + interchangeable tip handle (narrow rounded cube 11.5 × 11.5 × 10 mm `joystick_handle_cube_11p5` chosen 17 Oct (not yet printed; replaces the 20 mm-wide cube used in session 1; same push face and holes), shaft in the front hole; the 38 mm bar alternative is archived), 1D-axis constrainer (`38x38x56` revision, sits on the holder frame; not yet printed/fit-checked), adjustable Ledex holder. CAD (STEP + STL) in `mechanical/`. The adjustable return spring and the plunger link are still to be fitted.
- Magnet coupling: plunger linked to the shaft (see Ledex "Plunger link"). The steel washers (SFS M4 `111952`, M3 `132845`) are no longer the target.
- Ledex `195224-230`: 12 V, 7 W, 20.7 Ω, ~0.58 A nominal, pull type, mounted axially opposing the push.
- Water: bubble-free priming before every session; calibrated pulse volumes in `calibration/water-volume.md`.
- Actuonix spout: bench-tested extend/settle/water/retract; mouse-relative extend voltage and settle time still to calibrate.
## Source context
- Design authority: Weekly Update 29/06/26 (approved by the PI). Upstream: Mathis et al. 2017 (2D joystick pull).
- Training precedent: Mathis 2017; Wagner 2020 (manipulandum troubleshooting); Roth lever-push and Linares-García 2025 (push less natural than pull). Rest pad defines a pre-contact start state, not identical posture.
- The untouched `Pull Behavior_march2020.vi` hierarchy stays on the rig PC as the fallback. Keep the dated working archive through at least the first successful animal pilot.
- Repo docs: `README.md`, `RIG_INVENTORY.md`, `MAGNET_COMMISSIONING.md`, `EVENTS_LOGGER_PLAN.md`, `parameters/README.md`, `calibration/`, `simulations/ledex_femm/README.md`.
- Rig-PC Python is 3.6.4 (system numpy 1.14, no matplotlib; do not upgrade it, `make_labview_timing.py` depends on it; use a throwaway venv for plotting); Git for Windows 2.55 and FEMM 4.2 (`C:\femm42`) installed. Mac: FEMM 4.2 under Wine (prefix `~/.wine-femm`, install steps in the ledex_femm README).