Software for running head-fixed mouse auditory-behaviour experiments on the training rigs in room 311. A Python control panel reads each mouse's settings, starts the Bonsai workflow that runs the task, and stops it at the end of the session. The panel also has the day-to-day utilities: camera preview, water-line flush and copying data to the server.
This is lab instrumentation. It drives specific hardware on specific Windows PCs and is not meant to run anywhere else.
| If you want to… | read |
|---|---|
| start in the lab | New here?, below |
| train a mouse: water restriction, the daily routine, moving through the presets, the experiments | TRAINING.md |
| run a session on the control panel | INSTRUCTIONS.md |
| know what a parameter does | PARAMETERS.md |
| set up, calibrate or fix a rig | HARDWARE.md |
| run opto, masking or ALM control sessions | ZAPIT.md |
| do the head-plate surgery | SURGERY.md |
| perfuse a mouse at the end of an experiment | PERFUSION.md |
| understand how the pieces fit, read the data files or change the code | this file |
For the project as a whole (the analysis, your data folder on ceph, who to ask), start with START_HERE.md
in the sound_categorisation repository. For the rig, read in this order:
- Access you need and Contacts, below.
- TRAINING.md: the whole routine, from surgery to the experiments.
- INSTRUCTIONS.md: the control panel, step by step. Read it before your first session, and try a session in the emulator first; it needs no mouse.
- HARDWARE.md: what is on the rigs, and fixes for the common problems.
- Before any opto, masking or ALM control session: ZAPIT.md.
PARAMETERS.md is the reference for every setting; SURGERY.md covers the head-plate surgery and PERFUSION.md the perfusion at the end of an experiment.
- This repository. It is at https://github.com/serkanshentyurk/behaviour_rig for now and will move to the lab's GitHub. The lab manual, also on the lab's GitHub, says how to get access to the lab's repositories.
- The rig PCs. Each rig has its own PC in the room, with one account; the password is the user name.
- The server. It is mapped as drive Z: on the rig PCs; how to map it is on the SWC IT wiki. Make your own folder there; the suggested layout is
Z:\<Name>\Head_Fixed_Behavior\Data\Raw. - Push Data. Add
GUI/paths/<name>.jsonwith your initials and your server folder without the drive letter, for example{"initials": "AB", "data_path": "/Name/Head_Fixed_Behavior/Data/Raw"}, then commit and push. The Experimenter list on the Setup tab is built from these files, and Push Data only copies mice whose nickname starts with the initials. (params_pathin the existing files is not used.) - The restriction sheet, on SharePoint under SWC PIL › restriction sheets › Akrami Lab. SWC accounts normally have access; if yours doesn't, ask SWC IT.
- Inductions and licences (the SWC inductions, the PIL course and assessments) are not covered here: ask the PI or the lab manager, or see the lab manual.
- Quentin Pajot-Moric: the rig hardware, Zapit and this code.
- Serkan Shentyurk: this code and the analysis of its data.
- Lillianne Teachen, or other lab members: other access.
- The
#ithpcSlack channel: the cluster and computing.
- The control panel (
GUI/Bonsai_GUI.py) reads the chosen mouse's row fromParams/Mouse_Room_Params.xlsx. If the row's Preset is notcustom, the values its preset sets come fromParams/Presets.csvinstead (see PARAMETERS.md). It also reads this PC's row fromParams/Rigs.csvand writes both as one-line files,Params/Subject_Params.csvandParams/Rig_Params.csv. - Launch Bonsai starts Bonsai on
Protocols/Auditory_discrimination/Sound_Cat_V2.bonsai, withGUI/as the working folder. All relative paths in the workflow (..\Params\…,Extensions\…,../Data) are relative toGUI/. - The workflow reads the two files, connects to the hardware, and runs trials until you press End Session. It writes six CSV files (plus camera video) to
Data/<subject>/<protocol>_<subject>_<date>/. - End Session stops the trials, closes the valves and Zapit, and closes the data files about 2 s later. Kill Bonsai then closes Bonsai.
- Push Data copies new session folders to the lab server.
A SOUND_CAT trial in the full task (Full_Task_Cont / Full_Task_Disc) goes:
- Sound: white noise at a level set by the trial's stimulus value, for Sound_Duration.
- Delay: a silent delay of Go_Cue_Duration (shown as "Delay before window" in the panel; the trial phase is still called
Go_Cuein the data). - Response window: up to Response_Window seconds. The first lick that starts after the window opens is the choice.
- Outcome:
- Correct: a reward from that spout's valve.
- Incorrect: a timeout of Timeout_Duration.
- No lick: an abort, with neither.
- Inter-trial interval.
If Early Lick Abort is on, a new lick during the sound or the delay ends the trial at once: no window, no reward, the usual timeout, outcome Early.
GUI/
Launch_Bonsai_GUI.bat start here on a rig: activates gui_env, syncs the code, opens the panel
Bonsai_GUI.py the control panel (tkinter)
params_io.py the parameter table (SPEC), presets, rig identity, and the code that writes the parameter files
push_data.py Push Data: copies new session files to the server
GUI_env.yml conda environment "gui_env" (mac_env.yml: macOS, cannot run a rig)
Extensions/ Bonsai include files and zapit_TCPclient.cs, as used by the running workflow
Extensions.csproj needed for Bonsai to compile zapit_TCPclient.cs - do not delete
paths/<name>.json one per experimenter: initials and server folder, used by Push Data
Params/
Mouse_Room_Params.xlsx one row per mouse (sheet "Params"); its Preset column says custom or names a preset
Presets.csv one row per preset, holding the values that preset sets
Rigs.csv one row per rig PC: ports, valve times, speaker calibration
Camera.bonsai, Flush_Rig.bonsai utility workflows behind the Camera and Flush Rig buttons
Flush_Rig_Harp.bonsai Flush Rig on the Harp rigs (Arduino FALSE in Rigs.csv)
Subject_Params.csv, Rig_Params.csv written by the panel; not in git
Protocols/
Auditory_discrimination/Sound_Cat_V2.bonsai the task workflow the panel runs
Auditory_discrimination/Sound_Cat_V2.bonsai.layout its window layout - keep it next to the workflow
Arduino/ firmware for the Arduino lick/valve board
… other task families, calibration workflows and sound files
tests/
test_rig_consistency.py the panel, the parameter files and the workflow agree
test_params.py presets, value checks, Rigs.csv, rig identity, Push Data
test_docs.py PARAMETERS.md matches the code
Mouse_Room_Params_Example.xlsx an example only: the values a mouse runs with at each training step
Data/ session data, written by Bonsai; not in git
The other task folders in Protocols/ (PWM_auditory, DMTS, Amplitude_Modulation, and Pro_Anti/Pro_Anti.bonsai) are older standalone tasks. The panel does not run them, and they use paths from earlier layouts.
Setting up a rig PC, calibration and the hardware itself are in HARDWARE.md.
Stack.
- The panel is Python 3.10 with tkinter, pandas/openpyxl, psutil and python-osc. It talks to the running workflow over OSC on port 1334.
- The task runs in Bonsai 2.7, which saves the workflow as version 2.7.0.
- Custom operators are written in C#, and the lick/valve board is an Arduino.
- Hardware: Harp sound card, plus a Harp behaviour board or an Arduino Nano for lick sensors and valves, camera, and optionally Zapit or fibre optogenetics with an Arduino Mega for the masking light.
Each session writes into Data/<subject>/<protocol>_<subject>_<YYYY>_<M>_<D>/, for example Data/SS14/SOUND_CAT_SS14_2026_10_5/. Every file name ends with the session's start time, e.g. Trial_Summary2026-10-05T15_16_16.csv, so several sessions on one day share a folder.
All Time columns are seconds since midnight on the rig PC's clock, and every file uses the same clock, so they can be aligned directly.
To analyse sessions, use behav_utils, which reads these files. It will move to the lab's GitHub along with this repository.
| File | One row per | Columns |
|---|---|---|
Trial_Summary |
completed trial | 54 columns: trial result, timing, and the session settings repeated on every row |
Session_Parameters |
session (one data row) | every parameter, as name, value, name, value … |
Trial_Epochs |
change of trial phase | Epoch, Time |
Long_Form_Timestamps |
event | Label, Data, Time |
Detected_Licks |
change of a lick sensor | State, Time, Spout |
Zapit_Timestamps |
message to or from Zapit | Label, Data, Time |
Video_data…, Video_timestamps… |
camera frame | camera video and frame times |
A row is written when a trial ends. A trial cut short by End Session has no row, although its events are still in the other files.
The main columns:
- Trial and stimulus.
Trial_NumberandStim_Relative(the stimulus on a −1…+1 scale; 0 is the category boundary). - Response.
First_Lick(Left or Right),CorrectandAbort_Trial.Trial_Outcome: Correct, Incorrect, Abort (no lick in the window) or Early (ended by an early lick, when Early Lick Abort is on). Early trials also haveAbort_TrialTrue, and blankWindow_Open_Time,First_Lick_TimeandResponse_Latency.Reward_Side.
- Timing.
Window_Open_TimeandFirst_Lick_Time, plusResponse_Latencyin ms.Early_Lick_Time: the first new lick between the sound and the window opening. The trial carries on regardless.Trial_End_Time.
- Labels.
Session_Type,Stimulation_SiteandStimulation_Type. - Repeated session settings. Durations appear as h:mm:ss text, so
00:00:00.3000000is 300 ms.
Things to know when analysing it:
- Response_Latency changed meaning on 5 October 2026.
- Files that have a
Window_Open_Timecolumn use the new meaning: (First_Lick_Time − Window_Open_Time) × 1000, blank on aborts. - Older files used a timer that counts in screen frames. It reads 0 when a lick was already in progress as the window opened, and on abort rows it holds the previous trial's value.
- Files that have a
- Which licks count changed on the same date. From then on, only a lick that starts after the window opens counts. Before, a contact already in progress when the window opened was taken as the choice.
- On abort rows, ignore
First_Lick,CorrectandReward_Side: they keep the previous trial's values. Reward_Sideonly means something whenCorrectis True.Correct_Sideis not updated during the stable start. That's the first Stable_Start_Window trials when Stable_Start is on. Work the correct side out fromStim_RelativeandSound_Contingencyinstead: withLow_Left_High_Right, a negative value means left.- Ignore trial 1. Its timing is unreliable because Bonsai is still starting up.
- Session_Parameters.
- Ignore the header row: it's Bonsai's automatic naming and has duplicates. The data row alternates name and value.
- In Python:
row = list(csv.reader(open(path)))[1]; params = dict(zip(row[0::2], row[1::2])).
- Trial_Epochs.
- A trial runs
Sound,Go_Cue,Response_Window, thenFeedbackfollowed byRewardorTimeout, thenInter_Trial_Interval. Aborts go straight from the response window to the interval. - An
Earlytrial goes fromSound(orGo_Cue) straight toFeedbackandTimeout, so opto light set to switch off atFeedbackstill does. - The first row is a blank start-up value.
- A trial runs
- Long_Form_Timestamps.
Sound: the stimulus value, at the moment the sound is triggered.First_Lick: Left or Right.Left_Valve,Right_Valve: True when a valve opens, False when it closes.- The first rows are start-up values: a blank
First_LickandSound0.0.
- Detected_Licks.
Beam1Boolis the left spout andBeam2Boolthe right.Falsemeans the beam was broken (a lick starts) andTruemeans it was released.- It is not a clean list of licks. Both sensors start as
Falsewhen the session starts, and the workflow writesTrueat trial starts. Count licks asFalserows, skipping the first two. - On Arduino rigs the time is when Bonsai received the event, about 25 ms after the lick.
- Zapit_Timestamps.
- Each row is a command to Zapit or Zapit's reply.
- The
Datafield is a tuple such as(254, 0, 0, 0, 0, 0, 0). It contains commas and isn't quoted, so a normal CSV reader splits it. Read the file line by line instead.
- Change things on a development machine, then commit and push. Every rig resets its copy to
origin/maineach time the launcher starts, so changes made on a rig are lost. That includes the spreadsheet. - Open the workflow with the panel's Edit Workflow button, or start Bonsai with
GUI/as the working folder. Started from the Start menu, the relative paths don't resolve. - Keep
Sound_Cat_V2.bonsai.layoutnext to the workflow, under the same name.- It holds the windows that open during a session, and Bonsai rewrites it whenever you save.
- If you replace the workflow file in some other way, replace the layout with its matching file, or the windows won't open.
- Don't commit an empty
.bonsai/Settings/Sound_Cat_V2.layout: Bonsai 2.8 and later would use it instead of the layout above.
- Keep
GUI/Extensions.csproj. Without it, Bonsai doesn't compileGUI/Extensions/zapit_TCPclient.cs. The workflow then fails with "proxy for the unknown type zapit_TCPclient" and won't start, even in emulator mode. - Two
Extensions/folders exist. The running workflow usesGUI/Extensions/, because paths are relative toGUI/. Change that one.Protocols/Auditory_discrimination/Extensions/is an older copy and already differs from it (Send_Samples.bonsai). - To add a parameter, you need four things: a row in
SPECinGUI/params_io.py, a column in the spreadsheet, a parser in the workflow'sParamsgroup, and a row inPARAMETERS.md. The tests fail until all four match. If a preset should set it, add a row toParams/Presets.csvas well. - Run the tests before pushing.
From the repository root, in gui_env (GUI_env.yml includes pytest; an environment created before October 2026 needs pip install pytest):
python -m pytest tests
They read the workflow file directly, with no Bonsai or hardware, and check that:
- every value the workflow reads is written by the panel, for every mouse and rig, and that every panel parameter is read;
- every Trial_Summary column points at the right value;
- every data file is closed when a session ends;
GUI/Extensions.csprojis present;- in
SOUND_CAT, each spout's valve is timed by its own calibration; - in
Full_Task_Cont, the stimulus stage ends only after the sound has played; - the early-lick abort cuts the trial before the window and ends with the timeout and outcome
Early; - no group's connections form a loop (Bonsai can't build or open a group with one);
- the window layout lines up with the workflow;
- the response, timing and latency rules described above are wired as intended;
- every mouse loads, alone and with every preset, and
Presets.csvnames only real parameters and valid values; Rigs.csvrepeats no rig or hostname, and the rig check refuses a missingrig.json, an unknown rig or the wrong PC;- Push Data never overwrites a file and never leaves a half-copied one;
PARAMETERS.mdlists every parameter with the panel's label, column and options.
They can't tell you whether Bonsai builds and runs the workflow. For that, open it in Bonsai and run a short emulator session.
These are current as of 31 October 2026. Data recorded before 5 October 2026 carry the faults fixed that day. The analysis keeps those sessions and states it as a caveat: the same mouse ran on different rigs on different days, so there is no per-rig correction.
- Right valve time, fixed on 5 October 2026 for
SOUND_CAT. Before that, every right reward opened the right valve forLeft_Valve_Time. On 31104 and 31105 the right spout was open about 26% longer than its calibration; on the other rigs the difference was under 5%. Each session'sSession_Parametersrecords itsRig_ID.PRO_ANTIstill opens the right valve forLeft_Valve_Time; that is left for the protocol's owner to change. - Trial 1 timing is unreliable. Ignore trial 1 in analysis.
Correct_Sideisn't updated during the stable start (see Reading Trial_Summary).- Some old option values start no trials. Protocol
SOUND_CAT_DISCandSOUND_CAT_CONT, and StageHabituation_contandLick_To_Release_cont, have no trial logic in the workflow. The panel no longer offers them, and Load refuses a spreadsheet row that uses them. - Asymmetric distributions, fixed on 5 October 2026. Before that, with
Asym_LeftorAsym_Rightthe stimulus stage ended as soon as the side was chosen. The go-cue delay started at sound onset, so the window opened as the sound ended, and some trials played no sound at all; those trials were scored, and rewarded, against the previous trial's stimulus. Sessions run with these distributions before that date are affected.Asym_Leftwas checked in the emulator before the fix; check both after it. - The "fresh licks only" rule hasn't been tested on hardware with a contact held through the window opening.
- Trial timing follows the display refresh. The sound and the delay before the window usually vary by about one frame (17 ms), but they can end tens of ms early when the screen loop stalls. On opto sessions this happens on most opto trials, because the trial waits for Zapit's reply before the sound starts. The times recorded in the files are exact.
- There is no go-cue sound. The delay before the window is silent. A go-cue tone using the sound card's built-in tone generator was tried on 7 October 2026 and removed. On the cards' firmware (2.1), the tone left the card refusing the command that ends the white noise until it was switched off and on, which stopped sessions. Any card that played the tone needs a power cycle before it's used again.
- Lick times lag on Arduino rigs by about 25 ms, because the serial link runs at 9600 baud.
- Masking and opto sessions look identical in the data except for
Session_Type. The laser power is set in Zapit. - Harp behaviour boards sometimes switch their valve outputs to pulse mode on their own. The valves then only open for an instant, so little or no water comes out, in Flush Rig or a session. Why is not known; the fix is a setting in the Harp Behaviour app (HARDWARE.md).
- The calibration workflows point at an old port and an old location. The Harp water calibration has
COM4written in, and the sound calibration reads the sound card's port from the repository's old folder (Desktop\HeadFixedBehavior). Each needs one setting changed before use; see HARDWARE.md.