From ee6a0c3d19f610d0c34b43a09763cd906dbaf3ef Mon Sep 17 00:00:00 2001 From: viktorpm <50667179+viktorpm@users.noreply.github.com> Date: Tue, 29 Oct 2024 13:00:14 +0100 Subject: [PATCH 001/164] Update _Settings_Custom.conf (#4) replace specific rig ID with NaN --- ExperPort/Settings/_Settings_Custom.conf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ExperPort/Settings/_Settings_Custom.conf b/ExperPort/Settings/_Settings_Custom.conf index 6b80fd87..d4105e3b 100644 --- a/ExperPort/Settings/_Settings_Custom.conf +++ b/ExperPort/Settings/_Settings_Custom.conf @@ -11,7 +11,7 @@ GENERAL; Protocols_Directory; C:\ratter\Protocols ; % Directory for experime % Rig Settings %============================================================================== RIGS; fake_rp_box; 30; % 30 = Bpod state machine -RIGS; Rig_ID; 10; % ID of the rig box (NaN means no specific rig assigned) +RIGS; Rig_ID; NaN; % ID of the rig box (NaN means no specific rig assigned) RIGS; state_machine_server; localhost; % Server address for state machine control RIGS; sound_machine_server; localhost; % Server address for sound machine control RIGS; server_slot; 0; % Slot for server connection From bdd3e7f337d85c70cf558b4bc4e012ab95380b83 Mon Sep 17 00:00:00 2001 From: Viktor Plattner Date: Wed, 30 Oct 2024 11:22:01 +0000 Subject: [PATCH 002/164] reformating using Matlab smart indent --- Protocols/@AthenaDelayComp/StimulusSection.m | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/Protocols/@AthenaDelayComp/StimulusSection.m b/Protocols/@AthenaDelayComp/StimulusSection.m index fc021aaa..7f13a25b 100644 --- a/Protocols/@AthenaDelayComp/StimulusSection.m +++ b/Protocols/@AthenaDelayComp/StimulusSection.m @@ -55,7 +55,7 @@ 'new', x, y, 'labelfraction', 0.35, 'TooltipString', sprintf(['\nnew means at each trial, a new noise pattern will be generated,\n' ... '"library" means for each trial stimulus is loaded from a library with limited number of noise patterns'])); next_row(y, 1.3) set_callback(StimulusType, {mfilename, 'StimulusType'}); - NumeditParam(obj,'nPatt',50,x,y,'label','Num Nois Patt','TooltipString','Number of Noise Patters for the library'); + NumeditParam(obj,'nPatt',50,x,y,'label','Num Nois Patt','TooltipString','Number of Noise Patters for the library'); next_row(y); next_row(y); @@ -88,27 +88,27 @@ '\n''EQUIRIP'':Eqiripple FIR filter ''HAMMING'': Hamming-window based FIR'])); next_row(y, 1) DispParam(obj, 'A1_sigma', 0.01, x,y,'label','A1_sigma','TooltipString','Sigma value for the first stimulus'); - next_row(y); + next_row(y); DispParam(obj, 'A2_sigma', 0.01, x,y,'label','A2_sigma','TooltipString','Sigma value for the first stimulus'); - next_row(y); - NumeditParam(obj,'fcut',110,x,y,'label','fcut','TooltipString','Cut off frequency on the original white noise'); next_row(y); - NumeditParam(obj,'lfreq',2000,x,y,'label','Modulator_LowFreq','TooltipString','Lower bound for the frequency modulator'); - next_row(y); - NumeditParam(obj,'hfreq',20000,x,y,'label','Modulator_HighFreq','TooltipString','Upper bound for the frequency modulator'); + NumeditParam(obj,'fcut',110,x,y,'label','fcut','TooltipString','Cut off frequency on the original white noise'); + next_row(y); + NumeditParam(obj,'lfreq',2000,x,y,'label','Modulator_LowFreq','TooltipString','Lower bound for the frequency modulator'); + next_row(y); + NumeditParam(obj,'hfreq',20000,x,y,'label','Modulator_HighFreq','TooltipString','Upper bound for the frequency modulator'); next_row(y); % NumeditParam(obj,'outband',60,x,y,'label','Outband','TooltipString','outband on the distribution from which white noise is produced'); % next_row(y); - NumeditParam(obj,'minS1',0.007,x,y,'label','minS1','TooltipString','min sigma value for AUD1'); + NumeditParam(obj,'minS1',0.007,x,y,'label','minS1','TooltipString','min sigma value for AUD1'); next_row(y); - DispParam(obj,'maxS',40,x,y,'label','maxS','TooltipString','max sigma value for AUD1'); + DispParam(obj,'maxS',40,x,y,'label','maxS','TooltipString','max sigma value for AUD1'); NumeditParam(obj,'s2_s1_ratio',2.6,x,y,'label','s2_s1_ratio','TooltipString','Intensity index i.e. Ind=(S1-S2)/(S1+S2)'); next_row(y); ToggleParam(obj, 'psych_pairs', 0, x,y,... 'OnString', 'Psych Pairs ON',... 'OffString', 'Psych Pairs OFF',... 'TooltipString', sprintf('If on (black) then it disable the presentation of psychometric pairs')); - next_row(y); + next_row(y); NumeditParam(obj,'nPsych',6,x,y,'label','Num Psych Pairs','TooltipString','Number of psychometric pairs'); next_row(y); NumeditParam(obj,'from',0.047,x,y,'label','lowest pair','TooltipString','Psychometric pairs will be put between this pair, and an upper pair based on Ratio'); From e0b6473decc483619029623bd6e7c679bd36bca8 Mon Sep 17 00:00:00 2001 From: Viktor Plattner Date: Wed, 30 Oct 2024 13:28:59 +0000 Subject: [PATCH 003/164] Refine StimulusSection GUI layout - Adjust figure position to center on screen - Make figure visible by default - Update position calculations --- Protocols/@AthenaDelayComp/StimulusSection.m | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Protocols/@AthenaDelayComp/StimulusSection.m b/Protocols/@AthenaDelayComp/StimulusSection.m index 7f13a25b..19f99ef3 100644 --- a/Protocols/@AthenaDelayComp/StimulusSection.m +++ b/Protocols/@AthenaDelayComp/StimulusSection.m @@ -24,8 +24,8 @@ SoloParamHandle(obj, 'myfig', 'value', figure('closerequestfcn', [mfilename '(' class(obj) ', ''hide'');'], 'MenuBar', 'none', ... 'Name', mfilename), 'saveable', 0); screen_size = get(0, 'ScreenSize'); - set(value(myfig),'Position',[1 screen_size(4)-740, 1000 1000]); % put fig at top right - set(double(gcf), 'Visible', 'off'); + set(value(myfig),'Position',[(screen_size(3)-1100)/2 (screen_size(4)-800)/2 1100 800]); + set(double(gcf), 'Visible', 'on'); x=10;y=10; SoloParamHandle(obj, 'ax', 'saveable', 0, ... From 2db74ff81903db07000e7d8f1ac2f3a6fbbe8b4a Mon Sep 17 00:00:00 2001 From: viktorpm <50667179+viktorpm@users.noreply.github.com> Date: Thu, 31 Oct 2024 15:33:39 +0000 Subject: [PATCH 004/164] Update README.md Clarifying excluded directories and files --- README.md | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index a825d6c0..b30044b5 100644 --- a/README.md +++ b/README.md @@ -1,18 +1,16 @@ ## BControl - Behavioral Experimentation System -BControl is a sophisticated system designed for conducting behavioral experiments with high precision and flexibility. It aims to facilitate rapid interaction with experimental subjects, provide high-time-resolution measurements, and offer ease of programming and modification. For more information, check out the [Brody lab wiki](https://brodylabwiki.princeton.edu/bcontrol/index.php?title=General_overview). +BControl is a sophisticated system designed for conducting behavioural experiments with high precision and flexibility. It aims to facilitate rapid interaction with experimental subjects, provide high-time-resolution measurements, and offer ease of programming and modification. For more information, check out the [Brody lab wiki](https://brodylabwiki.princeton.edu/bcontrol/index.php?title=General_overview). ## `ratter` Repository This repository stores the BControl code for our high-throughput behavior training facility. Certain directories and files within the `ratter` repository are intentionally excluded from version control: -- `/SoloData/`: Contains raw data and configuration files essential for running experiments. - -- `/.svn/`: Houses Subversion (SVN) related files, which are typically hidden files generated by SVN for tracking changes and managing versions. +- The `/SoloData/` directory contains raw data and configuration files essential for running experiments. It is version-controlled with SVN and stored on our internal server. -- `/ExperPort/Settings/Settings_Custom.conf`: rig specific configurations +- The `/ExperPort/Settings/Settings_Custom.conf` file contains rig-specific configurations. Instead, we provide `/ExperPort/Settings/_Settings_Custom.conf`, which is a template. After downloading, users should rename it to `Settings_Custom.conf` and add their rig-specific settings. -- `/PASSWORD_CONFIG-DO_NOT_VERSIONCONTROL.mat`: stores the hostnames, users and passwords +- The `/PASSWORD_CONFIG-DO_NOT_VERSIONCONTROL.mat` file stores hostnames, users, and passwords. It is version-controlled with SVN and stored on our internal server. ## Setting Up Connection with GitHub on Windows 7 Machines From 9a777c91dc4a3937b752cc51dafa4c140dd39752 Mon Sep 17 00:00:00 2001 From: Akrami Lab Date: Wed, 6 Nov 2024 11:29:59 +0000 Subject: [PATCH 005/164] undo 04fac26329037c1e813fb3455d3fa4269e80f033: removing JavaFrame --- ExperPort/Modules/@runrats/runrats.m | 24 +++++++----------------- 1 file changed, 7 insertions(+), 17 deletions(-) diff --git a/ExperPort/Modules/@runrats/runrats.m b/ExperPort/Modules/@runrats/runrats.m index 619e4bc2..aeececc4 100644 --- a/ExperPort/Modules/@runrats/runrats.m +++ b/ExperPort/Modules/@runrats/runrats.m @@ -113,24 +113,14 @@ 'NumberTitle','off','Name','RunRats V2.2 ','Resize','off',... 'closerequestfcn', [mfilename '(''close'')']); SoloParamHandle(obj,'myfig', 'value',fig); + try - % Assuming myfig is your original figure handle - originalFig = value(myfig); - - % Create a new figure that will stay on top - newFig = figure; - set(newFig, 'Position', get(originalFig, 'Position')); % Set the new figure's position to cover the original one - - % Optionally, you can set the Name property of the new figure to distinguish it - set(newFig, 'Name', ['New Figure: ', get(originalFig, 'Name')]); - - % Perform operations on newFig as needed - % For demonstration, let's just pause here - pause(10); % Keep the new figure open for 10 seconds - - % Close the new figure after the operation is done - close(newFig); - catch + jf=get(value(myfig), 'JavaFrame'); + pause(0.1); + javaMethod('setAlwaysOnTop', jf.fFigureClient.getWindow, 1); + %delete or change - sharbat + catch %#ok + disp('WARNING: Failed to keep runrats on top'); end From 974418e76677ac4a14d85ba4c20f9210a8c49dbb Mon Sep 17 00:00:00 2001 From: Viktor Plattner Date: Wed, 6 Nov 2024 11:50:35 +0000 Subject: [PATCH 006/164] Update runrats module to use modern MATLAB UI methods - Replace deprecated JavaFrame usage with WindowStyle property - Remove commented-out legacy code - Improve compatibility with future MATLAB releases --- ExperPort/Modules/@runrats/runrats.m | 17 +++-------------- 1 file changed, 3 insertions(+), 14 deletions(-) diff --git a/ExperPort/Modules/@runrats/runrats.m b/ExperPort/Modules/@runrats/runrats.m index aeececc4..0f635491 100644 --- a/ExperPort/Modules/@runrats/runrats.m +++ b/ExperPort/Modules/@runrats/runrats.m @@ -115,26 +115,15 @@ SoloParamHandle(obj,'myfig', 'value',fig); try - jf=get(value(myfig), 'JavaFrame'); + set(myfig, 'WindowStyle', 'modal'); pause(0.1); - javaMethod('setAlwaysOnTop', jf.fFigureClient.getWindow, 1); - %delete or change - sharbat + catch %#ok disp('WARNING: Failed to keep runrats on top'); end - - % try - % jf=get(value(myfig), 'JavaFrame'); - % pause(0.1); - % javaMethod('setAlwaysOnTop', jf.fFigureClient.getWindow, 1); - % %delete or change - sharbat - % catch %#ok - % disp('WARNING: Failed to keep runrats on top'); - % end - - + %Create the non-gui variables we will need SoloParamHandle(obj,'RigID', 'value',bSettings('get','RIGS','Rig_ID')); %The rig ID SoloParamHandle(obj,'RatSch', 'value',cell(10,1)); %The rats that run in this rig in session order From 69278e1cec9f6b807c8b2a13b1c9dbc7a995d290 Mon Sep 17 00:00:00 2001 From: Viktor Plattner Date: Fri, 8 Nov 2024 13:41:57 +0000 Subject: [PATCH 007/164] restructuring StimulusSection.m, no functional change --- Protocols/@AthenaDelayComp/StimulusSection.m | 283 +++++++++++++------ 1 file changed, 189 insertions(+), 94 deletions(-) diff --git a/Protocols/@AthenaDelayComp/StimulusSection.m b/Protocols/@AthenaDelayComp/StimulusSection.m index 19f99ef3..7a4f3407 100644 --- a/Protocols/@AthenaDelayComp/StimulusSection.m +++ b/Protocols/@AthenaDelayComp/StimulusSection.m @@ -16,29 +16,37 @@ end x = varargin{1}; y = varargin{2}; + % Create toggle parameter for stimulus visibility ToggleParam(obj, 'StimulusShow', 0, x, y, 'OnString', 'Stimuli', ... 'OffString', 'Stimuli', 'TooltipString', 'Show/Hide Stimulus panel'); set_callback(StimulusShow, {mfilename, 'show_hide'}); %#ok (Defined just above) next_row(y); - SoloParamHandle(obj, 'myfig', 'value', figure('closerequestfcn', [mfilename '(' class(obj) ', ''hide'');'], 'MenuBar', 'none', ... - 'Name', mfilename), 'saveable', 0); + % SoloParamHandle(obj, 'myfig', 'value', figure('closerequestfcn', [mfilename '(' class(obj) ', ''hide'');'], 'MenuBar', 'none', ... + % 'Name', mfilename), 'saveable', 0); + + % creating figure window + SoloParamHandle(obj, 'myfig', 'value', figure('CloseRequestFcn', {@hide, obj}, ... + 'MenuBar', 'none', 'Name', mfilename), 'saveable', 0); screen_size = get(0, 'ScreenSize'); set(value(myfig),'Position',[(screen_size(3)-1100)/2 (screen_size(4)-800)/2 1100 800]); set(double(gcf), 'Visible', 'on'); - x=10;y=10; + % adding plot SoloParamHandle(obj, 'ax', 'saveable', 0, ... - 'value', axes('Position', [0.01 0.5 0.45 0.45])); + 'value', axes('Position', [0.1 0.5 0.45 0.45])); ylabel('log_e \sigma_2','FontSize',16,'FontName','Cambria Math'); set(value(ax),'Fontsize',15) xlabel('log_e \sigma_1','FontSize',16,'FontName','Cambria Math') + axis square; + % adding performance plot SoloParamHandle(obj, 'axperf', 'saveable', 0, ... - 'value', axes('Position', [0.5 0.5 0.45 0.45])); + 'value', axes('Position', [0.55 0.5 0.45 0.45])); ylabel('log_e \sigma_2','FontSize',16,'FontName','Cambria Math'); set(value(axperf),'Fontsize',15) xlabel('log_e \sigma_1','FontSize',16,'FontName','Cambria Math') + axis square; SoundManagerSection(obj, 'declare_new_sound', 'StimAUD1') SoundManagerSection(obj, 'declare_new_sound', 'StimAUD2') @@ -50,26 +58,33 @@ SoloParamHandle(obj, 'h1', 'value', []); SoloParamHandle(obj, 'thisclass', 'value', []); - y=5; + x=5; y=5; MenuParam(obj, 'StimulusType', {'library', 'new'}, ... - 'new', x, y, 'labelfraction', 0.35, 'TooltipString', sprintf(['\nnew means at each trial, a new noise pattern will be generated,\n' ... - '"library" means for each trial stimulus is loaded from a library with limited number of noise patterns'])); next_row(y, 1.3) + 'new', x, y, 'labelfraction', 0.35, ... + 'TooltipString', ... + sprintf(['\n"new" means at each trial, a new noise pattern will be generated,\n' ... + '"library" means for each trial stimulus is loaded from a library with limited number of noise patterns']) ... + ); + next_row(y, 1.3) set_callback(StimulusType, {mfilename, 'StimulusType'}); NumeditParam(obj,'nPatt',50,x,y,'label','Num Nois Patt','TooltipString','Number of Noise Patters for the library'); next_row(y); next_row(y); - PushbuttonParam(obj, 'refresh_pairs', x,y , 'TooltipString', 'Instantiates the pairs given the new set of parameters'); + PushbuttonParam(obj, 'refresh_pairs', x,y , ... + 'TooltipString', 'Instantiates the pairs given the new set of parameters'); set_callback(refresh_pairs, {mfilename, 'plot_pairs'}); next_row(y); - PushbuttonParam(obj, 'plot_performance', x,y , 'TooltipString', 'Plots the class design with mean performance for each class'); + PushbuttonParam(obj, 'plot_performance', x,y , ... + 'TooltipString', 'Plots the class design with mean performance for each class'); set_callback(plot_performance, {mfilename, 'plot_perf'}); next_row(y); next_row(y); MenuParam(obj, 'Rule', {'S2>S1 Left','S2>S1 Right'}, ... - 'S2>S1 Left', x, y, 'labelfraction', 0.35, 'TooltipString', sprintf(['\nThis bottom determines the rule\n', ... + 'S2>S1 Left', x, y, 'labelfraction', 0.35, ... + 'TooltipString', sprintf(['\nThis bottom determines the rule\n', ... '\n''S2>S1 Left'' means if Aud2 > Aud1 then reward will be delivered from the left water spout and if Aud2 < Aud1 then water comes form right\n',... '\n''S2>S1 Right'' means if Aud2 < Aud1 then reward will be delivered from the left water spout and if Aud2 > Aud1 then water comes from right\n'])); next_row(y, 1) @@ -82,48 +97,87 @@ next_column(x); y=5; MenuParam(obj, 'filter_type', {'GAUS','LPFIR', 'FIRLS','BUTTER','MOVAVRG','KAISER','EQUIRIP','HAMMING'}, ... - 'GAUS', x, y, 'labelfraction', 0.35, 'TooltipString', sprintf(['\nDifferent filters. ''LPFIR'': lowpass FIR ''FIRLS'': Least square linear-phase FIR filter design\n', ... + 'GAUS', x, y, 'labelfraction', 0.35, ... + 'TooltipString', sprintf(['\nDifferent filters. ''LPFIR'': lowpass FIR ''FIRLS'': Least square linear-phase FIR filter design\n', ... '\n''BUTTER'': IIR Butterworth lowpass filter ''GAUS'': Gaussian filter (window)\n', ... '\n''MOVAVRG'': Moving average FIR filter ''KAISER'': Kaiser-window FIR filtering\n', ... '\n''EQUIRIP'':Eqiripple FIR filter ''HAMMING'': Hamming-window based FIR'])); next_row(y, 1) - DispParam(obj, 'A1_sigma', 0.01, x,y,'label','A1_sigma','TooltipString','Sigma value for the first stimulus'); + + DispParam(obj, 'A1_sigma', 0.01, x,y, ... + 'label','A1_sigma', ... + 'TooltipString','Sigma value for the first stimulus'); next_row(y); - DispParam(obj, 'A2_sigma', 0.01, x,y,'label','A2_sigma','TooltipString','Sigma value for the first stimulus'); + + DispParam(obj, 'A2_sigma', 0.01, x,y, ... + 'label','A2_sigma', ... + 'TooltipString','Sigma value for the first stimulus'); next_row(y); - NumeditParam(obj,'fcut',110,x,y,'label','fcut','TooltipString','Cut off frequency on the original white noise'); + + NumeditParam(obj,'fcut',110,x,y, ... + 'label','fcut', ... + 'TooltipString','Cut off frequency on the original white noise'); next_row(y); - NumeditParam(obj,'lfreq',2000,x,y,'label','Modulator_LowFreq','TooltipString','Lower bound for the frequency modulator'); + + NumeditParam(obj,'lfreq',2000,x,y, ... + 'label','Modulator_LowFreq', ... + 'TooltipString','Lower bound for the frequency modulator'); next_row(y); - NumeditParam(obj,'hfreq',20000,x,y,'label','Modulator_HighFreq','TooltipString','Upper bound for the frequency modulator'); + + NumeditParam(obj,'hfreq',20000,x,y, ... + 'label','Modulator_HighFreq', ... + 'TooltipString','Upper bound for the frequency modulator'); next_row(y); - % NumeditParam(obj,'outband',60,x,y,'label','Outband','TooltipString','outband on the distribution from which white noise is produced'); + + % NumeditParam(obj,'outband',60,x,y,'label','Outband', ... + % 'TooltipString','outband on the distribution from which white noise is produced'); % next_row(y); - NumeditParam(obj,'minS1',0.007,x,y,'label','minS1','TooltipString','min sigma value for AUD1'); + + NumeditParam(obj,'minS1',0.007,x,y, ... + 'label','minS1','TooltipString','min sigma value for AUD1'); next_row(y); + DispParam(obj,'maxS',40,x,y,'label','maxS','TooltipString','max sigma value for AUD1'); - NumeditParam(obj,'s2_s1_ratio',2.6,x,y,'label','s2_s1_ratio','TooltipString','Intensity index i.e. Ind=(S1-S2)/(S1+S2)'); + + NumeditParam(obj,'s2_s1_ratio',2.6,x,y,'label','s2_s1_ratio', ... + 'TooltipString','Intensity index i.e. Ind=(S1-S2)/(S1+S2)'); next_row(y); + ToggleParam(obj, 'psych_pairs', 0, x,y,... 'OnString', 'Psych Pairs ON',... 'OffString', 'Psych Pairs OFF',... 'TooltipString', sprintf('If on (black) then it disable the presentation of psychometric pairs')); next_row(y); - NumeditParam(obj,'nPsych',6,x,y,'label','Num Psych Pairs','TooltipString','Number of psychometric pairs'); + + NumeditParam(obj,'nPsych',6,x,y, ... + 'label','Num Psych Pairs','TooltipString','Number of psychometric pairs'); next_row(y); - NumeditParam(obj,'from',0.047,x,y,'label','lowest pair','TooltipString','Psychometric pairs will be put between this pair, and an upper pair based on Ratio'); + + NumeditParam(obj,'from',0.047,x,y, ... + 'label','lowest pair', ... + 'TooltipString','Psychometric pairs will be put between this pair, and an upper pair based on Ratio'); next_row(y); + MenuParam(obj, 'psych_type', {'horizpairs', 'vertpairs'}, ... - 'horizpairs', x, y, 'labelfraction', 0.35, 'TooltipString', sprintf(['\nhorizpairs means psychometric pairs will be built with "from" as their fixed S2 while S1 will increase,\n' ... - '"vertpairs" means psychometric pairs will be built with "from" as their fixed S1 while S2 will increase'])); next_row(y, 1.3) - NumeditParam(obj,'probpsych',0.9,x,y,'label','probpsych','TooltipString','probability of having a psychometric pair as the stimulus pair at each trial'); + 'horizpairs', x, y, 'labelfraction', 0.35, ... + 'TooltipString', sprintf(['\n"horizpairs" means psychometric pairs will be built with "from" as their fixed S2 while S1 will increase,\n' ... + '"vertpairs" means psychometric pairs will be built with "from" as their fixed S1 while S2 will increase'])); + next_row(y, 1.3) + + NumeditParam(obj,'probpsych',0.9,x,y, ... + 'label','probpsych', ... + 'TooltipString','probability of having a psychometric pair as the stimulus pair at each trial'); disable(nPatt); next_column(x); y=5; + SoloParamHandle(obj, 'existing_numClassPsych', 'value', 0, 'saveable', 0); SoloParamHandle(obj, 'existing_numClass', 'value', 0, 'saveable', 0); - SoloParamHandle(obj, 'my_window_info', 'value', [x, y, double(value(myfig))], 'saveable', 0); - NumeditParam(obj,'numClass',4,x,y,'label','numClass','TooltipString','Number of stimulus pairs'); + SoloParamHandle(obj, 'my_window_info', ... + 'value', [x, y, double(value(myfig))], 'saveable', 0); + + NumeditParam(obj,'numClass',4,x,y, ... + 'label','numClass','TooltipString','Number of stimulus pairs'); set_callback_on_load(numClass, 4); %#ok set_callback(numClass, {mfilename, 'numClass'}); @@ -466,123 +520,164 @@ %% Case plot_pais case 'plot_pairs' + % Generate pairs and calculate max value StimulusSection(obj,'make_pairs'); - maxS.value=max(thesepairs(:)); - %% plot the pair set - cla(value(ax)) - xd=log(thesepairs(:,1)); - yd=log(thesepairs(:,2)); - for ii=1:length(xd) + maxS.value = max(thesepairs(:)); + + %% Plot the pair set + cla(value(ax)); + + % Transform data to log scale + xd = log(thesepairs(:,1)); + yd = log(thesepairs(:,2)); + + % Plot individual points + for ii = 1:length(xd) axes(value(ax)); - plot(xd(ii),yd(ii),'s','MarkerSize',15,'MarkerEdgeColor',[0 0 0],'LineWidth',2) - hold on - eval(sprintf('hperf%d=text(xd(ii),yd(ii),num2str(ii));',ii)); - hold on + plot(xd(ii), yd(ii), 's', 'MarkerSize', 15, 'MarkerEdgeColor', [0 0 0], 'LineWidth', 2); + hold on; + eval(sprintf('hperf%d=text(xd(ii),yd(ii),num2str(ii));', ii)); + hold on; end - axis square + + % Adjust plot appearance + axis square; + + % Calculate and set tick values % Ytick=get(value(ax),'YtickLabel'); % Xtick=get(value(ax),'XtickLabel'); - set(value(ax),'ytick',((yd(1:1+value(midclass_pairs):(end-nPsych*psych_pairs)/2))),'xtick',((xd(1:1+value(midclass_pairs):(end-nPsych*psych_pairs)/2)))); - set(value(ax),'yticklabel',num2str(exp(yd(1:1+value(midclass_pairs):(end-nPsych*psych_pairs)/2)),2),'xticklabel',num2str(exp(xd(1:1+value(midclass_pairs):(end-nPsych*psych_pairs)/2)),2)); - ylabel('\sigma_2 in log scale','FontSize',16,'FontName','Cambria Math'); - set(value(ax),'Fontsize',15) - xlabel('\sigma_1 in log scale','FontSize',16,'FontName','Cambria Math') + set(value(ax), 'ytick', ((yd(1:1+value(midclass_pairs):(end-nPsych*psych_pairs)/2)))); + set(value(ax), 'xtick', ((xd(1:1+value(midclass_pairs):(end-nPsych*psych_pairs)/2)))); + set(value(ax), 'yticklabel', num2str(exp(yd(1:1+value(midclass_pairs):(end-nPsych*psych_pairs)/2)), 2)); + set(value(ax), 'xticklabel', num2str(exp(xd(1:1+value(midclass_pairs):(end-nPsych*psych_pairs)/2)), 2)); + + % Set labels and font sizes + ylabel('\sigma_2 in log scale', 'FontSize', 16, 'FontName', 'Cambria Math'); + set(value(ax), 'Fontsize', 15); + xlabel('\sigma_1 in log scale', 'FontSize', 16, 'FontName', 'Cambria Math'); + %% Select side and generate pair based on rule and trial SideSection(obj,'get_current_side'); - if strcmp(Rule,'S2>S1 Left') + + if strcmp(Rule, 'S2>S1 Left') + % Handle LEFT trial case if strcmp(ThisTrial, 'LEFT') - bag=[]; - for cc=1:value(numClass)+(numClass-1)*value(midclass_pairs) - eval(sprintf('pr=value(probClass%d);',cc+value(numClass))); - bag=[bag ones(1,(10-10*value(probpsych))*pr)*value(cc)]; + bag = []; + for cc = 1:value(numClass)+(numClass-1)*value(midclass_pairs) + eval(sprintf('pr=value(probClass%d);', cc+value(numClass))); + bag = [bag ones(1,(10-10*value(probpsych))*pr)*value(cc)]; end - if midclass_pairs ==1 - for cc=1:midclass_pairs - bag=[bag value(cc)]; + % Handle midclass pairs + if midclass_pairs == 1 + for cc = 1:midclass_pairs + bag = [bag value(cc)]; end end - pp=1; - if psych_pairs ==1 - for cc=value(numClass)+1:value(numClass)+value(nPsych)/2 + % Handle psych pairs + pp = 1; + if psych_pairs == 1 + for cc = value(numClass)+1:value(numClass)+value(nPsych)/2 bag = [bag ones(1,(probpsych*10))*value(cc)]; end end - pp=randsample(bag,length(bag)); - thispair=[pairs_u(pp(1),1) pairs_u(pp(1),2)]; + + % Select pair + pp = randsample(bag, length(bag)); + thispair = [pairs_u(pp(1),1) pairs_u(pp(1),2)]; + + % Determine class if pp(1) > value(numClass*2) - thisclass(n_done_trials+1)=pp(1)+numClass*2; + thisclass(n_done_trials+1) = pp(1)+numClass*2; else - thisclass(n_done_trials+1)=pp(1)+numClass; + thisclass(n_done_trials+1) = pp(1)+numClass; end else - - bag=[]; - for cc=1:value(numClass)+(numClass-1)*value(midclass_pairs) - eval(sprintf('pr=value(probClass%d);',cc)); - bag=[bag ones(1,(10-10*value(probpsych))*pr)*value(cc)]; + % Handle RIGHT trial case + bag = []; + for cc = 1:value(numClass)+(numClass-1)*value(midclass_pairs) + eval(sprintf('pr=value(probClass%d);', cc)); + bag = [bag ones(1,(10-10*value(probpsych))*pr)*value(cc)]; end - pp=1; - pp=randsample(bag,length(bag)); - thispair=[pairs_d(pp(1),1) pairs_d(pp(1),2)]; + + % Select pair + pp = randsample(bag, length(bag)); + thispair = [pairs_d(pp(1),1) pairs_d(pp(1),2)]; + + % Determine class if pp(1) > value(numClass) - thisclass(n_done_trials+1)=pp(1)+numClass; + thisclass(n_done_trials+1) = pp(1)+numClass; else - thisclass(n_done_trials+1)=pp(1); + thisclass(n_done_trials+1) = pp(1); end end + elseif strcmp(Rule,'S2>S1 Right') + % Handle RIGHT rule case if strcmp(ThisTrial, 'RIGHT') - bag=[]; - for cc=1:value(numClass)+(numClass-1)*value(midclass_pairs) - eval(sprintf('pr=value(probClass%d);',cc+value(numClass))); - bag=[bag ones(1,(10-10*value(probpsych))*pr)*value(cc)]; + bag = []; + for cc = 1:value(numClass)+(numClass-1)*value(midclass_pairs) + eval(sprintf('pr=value(probClass%d);', cc+value(numClass))); + bag = [bag ones(1,(10-10*value(probpsych))*pr)*value(cc)]; end - if psych_pairs ==1 - for cc=value(numClass)+1:value(numClass)+value(nPsych)/2 + % Handle psych pairs + if psych_pairs == 1 + for cc = value(numClass)+1:value(numClass)+value(nPsych)/2 bag = [bag ones(1,(probpsych*10))*(value(cc)+numClass*2)]; end end - pp=randsample(bag,length(bag)); - thispair=[pairs_u(pp(1),1) pairs_u(pp(1),2)]; + % Select pair + pp = randsample(bag, length(bag)); + thispair = [pairs_u(pp(1),1) pairs_u(pp(1),2)]; + + % Determine class if pp(1) > value(numClass*2) - thisclass(n_done_trials+1)=pp(1)+numClass*2; + thisclass(n_done_trials+1) = pp(1)+numClass*2; else - thisclass(n_done_trials+1)=pp(1)+numClass; + thisclass(n_done_trials+1) = pp(1)+numClass; end - else - bag=[]; - for cc=1:value(numClass)+(numClass-1)*value(midclass_pairs) - eval(sprintf('pr=value(probClass%d);',cc)); - bag=[bag ones(1,(10-10*value(probpsych))*pr)*value(cc)]; + % Handle LEFT trial case + bag = []; + for cc = 1:value(numClass)+(numClass-1)*value(midclass_pairs) + eval(sprintf('pr=value(probClass%d);', cc)); + bag = [bag ones(1,(10-10*value(probpsych))*pr)*value(cc)]; end - if psych_pairs ==1 - for cc=value(numClass)+1:value(numClass)+value(nPsych)/2 + % Handle psych pairs + if psych_pairs == 1 + for cc = value(numClass)+1:value(numClass)+value(nPsych)/2 bag = [bag ones(1,(probpsych*10))*(value(cc)+numClass)]; end end - pp=randsample(bag,length(bag)); - thispair=[pairs_d(pp(1),1) pairs_d(pp(1),2)]; + % Select pair + pp = randsample(bag, length(bag)); + thispair = [pairs_d(pp(1),1) pairs_d(pp(1),2)]; + % Determine class if pp(1) > value(numClass) - thisclass(n_done_trials+1)=pp(1)+numClass; + thisclass(n_done_trials+1) = pp(1)+numClass; else - thisclass(n_done_trials+1)=pp(1); + thisclass(n_done_trials+1) = pp(1); end end end - A1_sigma.value=thispair(1); - A2_sigma.value=thispair(2); - %% plot the pair - h1.value=plot(log(value(A1_sigma)),log(value(A2_sigma)),'s','color',[0.8 0.4 0.1],'markerfacecolor',[0.8 0.4 0.1],'MarkerSize',15,'LineWidth',3); - %LOGplotPairs(thesepairs(:,1),thesepairs(:,2),'s',15,'k',1,16,thispair(1),thispair(2),value(ax),'init') + % Set sigma values + A1_sigma.value = thispair(1); + A2_sigma.value = thispair(2); + + %% Plot the selected pair + h1.value = plot(log(value(A1_sigma)),log(value(A2_sigma)),'s','color',[0.8 0.4 0.1],'markerfacecolor',[0.8 0.4 0.1],'MarkerSize',15,'LineWidth',3); + + % Commented-out alternative plotting function + % LOGplotPairs(thesepairs(:,1),thesepairs(:,2),'s',15,'k',1,16,thispair(1),thispair(2),value(ax),'init') + + %% Case Plot_perf case 'plot_perf' From c6a1e9bfdbdb6ac3ab452b9431fff0d595795d8e Mon Sep 17 00:00:00 2001 From: Viktor Plattner Date: Fri, 8 Nov 2024 15:39:56 +0000 Subject: [PATCH 008/164] Refactor StimulusSection.m: - reposition ToggleParam for stimulus visibility to make it visible in the GUI - Simplify axis labels and font sizes for consistency --- Protocols/@AthenaDelayComp/StimulusSection.m | 34 +++++++++++--------- 1 file changed, 18 insertions(+), 16 deletions(-) diff --git a/Protocols/@AthenaDelayComp/StimulusSection.m b/Protocols/@AthenaDelayComp/StimulusSection.m index 7a4f3407..e448a29a 100644 --- a/Protocols/@AthenaDelayComp/StimulusSection.m +++ b/Protocols/@AthenaDelayComp/StimulusSection.m @@ -14,13 +14,7 @@ if length(varargin) < 2 error('Need at least two arguments, x and y position, to initialize %s', mfilename); end - x = varargin{1}; y = varargin{2}; - - % Create toggle parameter for stimulus visibility - ToggleParam(obj, 'StimulusShow', 0, x, y, 'OnString', 'Stimuli', ... - 'OffString', 'Stimuli', 'TooltipString', 'Show/Hide Stimulus panel'); - set_callback(StimulusShow, {mfilename, 'show_hide'}); %#ok (Defined just above) - next_row(y); + % x = varargin{1}; y = varargin{2}; % SoloParamHandle(obj, 'myfig', 'value', figure('closerequestfcn', [mfilename '(' class(obj) ', ''hide'');'], 'MenuBar', 'none', ... % 'Name', mfilename), 'saveable', 0); @@ -30,23 +24,24 @@ 'MenuBar', 'none', 'Name', mfilename), 'saveable', 0); screen_size = get(0, 'ScreenSize'); set(value(myfig),'Position',[(screen_size(3)-1100)/2 (screen_size(4)-800)/2 1100 800]); - set(double(gcf), 'Visible', 'on'); + % set(double(gcf), 'Visible', 'on'); % adding plot SoloParamHandle(obj, 'ax', 'saveable', 0, ... 'value', axes('Position', [0.1 0.5 0.45 0.45])); - ylabel('log_e \sigma_2','FontSize',16,'FontName','Cambria Math'); - set(value(ax),'Fontsize',15) - xlabel('log_e \sigma_1','FontSize',16,'FontName','Cambria Math') + ylabel('log_e \sigma_2'); + %set(value(ax),'Fontsize',15) + xlabel('log_e \sigma_1') axis square; % adding performance plot SoloParamHandle(obj, 'axperf', 'saveable', 0, ... 'value', axes('Position', [0.55 0.5 0.45 0.45])); - ylabel('log_e \sigma_2','FontSize',16,'FontName','Cambria Math'); - set(value(axperf),'Fontsize',15) - xlabel('log_e \sigma_1','FontSize',16,'FontName','Cambria Math') + ylabel('log_e \sigma_2'); + %set(value(axperf),'Fontsize',15) + %xlabel('log_e \sigma_1','FontSize',16,'FontName','Cambria Math') axis square; + SoundManagerSection(obj, 'declare_new_sound', 'StimAUD1') SoundManagerSection(obj, 'declare_new_sound', 'StimAUD2') @@ -59,8 +54,15 @@ SoloParamHandle(obj, 'thisclass', 'value', []); x=5; y=5; - MenuParam(obj, 'StimulusType', {'library', 'new'}, ... - 'new', x, y, 'labelfraction', 0.35, ... + % Create toggle parameter for stimulus visibility + ToggleParam(obj, 'StimulusShow', 0, x, y, ... + 'OnString', 'Stimuli', 'OffString', 'Stimuli', ... + 'TooltipString', 'Show/Hide Stimulus panel'); + set_callback(StimulusShow, {mfilename, 'show_hide'}); %#ok (Defined just above) + next_row(y); + + MenuParam(obj, 'StimulusType', {'library', 'new'}, 'new', ... + x, y, 'labelfraction', 0.35, ... 'TooltipString', ... sprintf(['\n"new" means at each trial, a new noise pattern will be generated,\n' ... '"library" means for each trial stimulus is loaded from a library with limited number of noise patterns']) ... From 964e3e6c23b66a117e204374cdab118da57b0c56 Mon Sep 17 00:00:00 2001 From: Viktor Plattner Date: Fri, 8 Nov 2024 15:44:53 +0000 Subject: [PATCH 009/164] small update --- Protocols/@AthenaDelayComp/StimulusSection.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Protocols/@AthenaDelayComp/StimulusSection.m b/Protocols/@AthenaDelayComp/StimulusSection.m index e448a29a..721f35bd 100644 --- a/Protocols/@AthenaDelayComp/StimulusSection.m +++ b/Protocols/@AthenaDelayComp/StimulusSection.m @@ -39,7 +39,7 @@ 'value', axes('Position', [0.55 0.5 0.45 0.45])); ylabel('log_e \sigma_2'); %set(value(axperf),'Fontsize',15) - %xlabel('log_e \sigma_1','FontSize',16,'FontName','Cambria Math') + xlabel('log_e \sigma_1') axis square; From 344e4672a0ac56c27a4763978498d5931bd12265 Mon Sep 17 00:00:00 2001 From: ledapent Date: Fri, 29 Nov 2024 15:03:39 +0000 Subject: [PATCH 010/164] adding and updating protocol files from rig 31 (opto setup) --- .../SoundCatSMA-old.m | 406 ++++++++++++++++++ .../SoundCatSMA.m | 12 +- 2 files changed, 413 insertions(+), 5 deletions(-) create mode 100644 Protocols/@AltSoundCategorizationCatch/SoundCatSMA-old.m diff --git a/Protocols/@AltSoundCategorizationCatch/SoundCatSMA-old.m b/Protocols/@AltSoundCategorizationCatch/SoundCatSMA-old.m new file mode 100644 index 00000000..c96f9a77 --- /dev/null +++ b/Protocols/@AltSoundCategorizationCatch/SoundCatSMA-old.m @@ -0,0 +1,406 @@ + +function [varargout] = SoundCatSMA(obj, action) + +GetSoloFunctionArgs; + + +switch action + + case 'init' + + srate=SoundManagerSection(obj,'get_sample_rate'); + freq1=5; + dur1=1.5*1000; + Vol=1; + tw=Vol*(MakeBupperSwoop(srate,0, freq1 , freq1 , dur1/2 , dur1/2,0,0.1)); + SoundManagerSection(obj, 'declare_new_sound', 'LRewardSound', [tw ; zeros(1, length(tw))]) + SoundManagerSection(obj, 'declare_new_sound', 'RRewardSound', [zeros(1, length(tw));tw]) + SoundManagerSection(obj, 'send_not_yet_uploaded_sounds'); + + case 'prepare_next_trial', + + %% Setup water + min_time= 2.5E-4; % This is less than the minumum time allowed for a state transition. + + left1led = bSettings('get', 'DIOLINES', 'left1led'); + center1led = bSettings('get', 'DIOLINES', 'center1led'); + right1led = bSettings('get', 'DIOLINES', 'right1led'); + left1water = bSettings('get', 'DIOLINES', 'left1water'); + right1water = bSettings('get', 'DIOLINES', 'right1water'); + + + %% Setup sounds + sone_sound_id = SoundManagerSection(obj, 'get_sound_id', 'SOneSound'); + go_sound_id = SoundManagerSection(obj, 'get_sound_id', 'GoSound'); + go_cue_duration = SoundManagerSection(obj, 'get_sound_duration', 'GoSound'); + RLreward_sound_id = SoundManagerSection(obj, 'get_sound_id', 'RewardSound'); + err_sound_id = SoundManagerSection(obj, 'get_sound_id', 'ErrorSound'); + viol_sound_id = SoundManagerSection(obj, 'get_sound_id', 'ViolationSound'); + viol_snd_duration = SoundManagerSection(obj, 'get_sound_duration', 'ViolationSound'); + to_sound_id = SoundManagerSection(obj, 'get_sound_id', 'TimeoutSound'); + timeout_duration = SoundManagerSection(obj, 'get_sound_duration', 'TimeoutSound'); + Lreward_sound_id = SoundManagerSection(obj, 'get_sound_id', 'LRewardSound'); + Rreward_sound_id = SoundManagerSection(obj, 'get_sound_id', 'RRewardSound'); + + + A1_sound_id = SoundManagerSection(obj, 'get_sound_id', 'StimAUD1'); + + %% Declare variables + % These will get moved to other functions as SoloParamHandles. + + WaterAmount=maxasymp + (minasymp./(1+(n_done_trials/inflp).^slp).^assym); +% WaterValvesSection(obj, 'set_water_amounts', WaterAmount, WaterAmount); +% [LeftWValveTime RightWValveTime] = WaterValvesSection(obj, 'get_water_times'); + WValveTimes = GetValveTimes(WaterAmount, [2 3]); + LeftWValveTime = WValveTimes(1); + RightWValveTime = WValveTimes(2); + [LeftWMult RightWMult] = SideSection(obj, 'get_water_mult'); + LeftWValveTime=LeftWValveTime*LeftWMult; + RightWValveTime=RightWValveTime*RightWMult; + + side = SideSection(obj, 'get_current_side'); + if side == 'l' + HitEvent = 'Lin'; ErrorEvent = 'Rin'; + HitState = 'lefthit'; SideLight = left1led; + SecondHitState = 'secondlefthit'; + + else + HitEvent = 'Rin'; ErrorEvent = 'Lin'; + HitState = 'righthit'; SideLight = right1led; + SecondHitState = 'secondrighthit'; + end; + + + if strcmp(reward_type, 'Always') + LEDOn=1; + AnyReward=1; + wait_for_second_hit=30000; + error_iti=0; + else + LEDOn=0; + if strcmp(reward_type, 'NoReward') + AnyReward=0; + wait_for_second_hit=error_iti; + else + AnyReward=1; + wait_for_second_hit=30000; + error_iti=0; + end + end; + + + sma = StateMachineAssembler('full_trial_structure','use_happenings', 1); + + sma = add_scheduled_wave(sma, 'name', 'center_poke', 'preamble', CP_duration, ... + 'sustain', go_cue_duration, 'sound_trig', go_sound_id); + + sma = add_scheduled_wave(sma, 'name', 'settling_period', 'preamble', SettlingIn_time); + + + % to modify it for widefield imagine + if value(imaging)==1 && ~isnan(bSettings('get', 'DIOLINES', 'scope')); + trigscope = bSettings('get', 'DIOLINES', 'scope'); + else + trigscope = nan; + end + + if value(imaging)==1 + sma = add_scheduled_wave(sma, 'name', 'TrigScope', 'preamble', 0, 'sustain', ... + 0.5, 'DOut', trigscope, 'loop', 0); + else + sma = add_scheduled_wave(sma, 'name', 'TrigScope', 'preamble', 0, 'sustain', 0); %dummy wave. + end + + % ---BEGIN: for training stage 0 only--- + if side=='l', + sma = add_scheduled_wave(sma, 'name', 'reward_delivery', 'preamble', reward_delay, ... + 'sustain', LeftWValveTime, 'DOut', left1water); + reward_sound_id=Lreward_sound_id; + else + sma = add_scheduled_wave(sma, 'name', 'reward_delivery', 'preamble', reward_delay, ... + 'sustain', RightWValveTime, 'DOut', right1water); + reward_sound_id=Rreward_sound_id; + end; + % ---END: for training stage 0 only--- + + sma = add_scheduled_wave(sma, 'name', 'stimA1', 'preamble', PreStim_time, ... + 'sustain', A1_time, 'sound_trig', A1_sound_id); + + + + switch value(training_stage) + + case 0 %% learning the reward sound association -left or right led on -> poke -> sound+reward + sma = add_state(sma, 'name', 'sideled_on', 'self_timer', SideLed_duration, ... + 'output_actions', {'DOut', SideLight}, ... + 'input_to_statechange',{'Tup','wait_for_collecting_reward'}); + + sma = add_state(sma, 'name', 'wait_for_collecting_reward', 'self_timer', RewardCollection_duration, ... + 'output_actions', {'DOut', SideLight}, ... + 'input_to_statechange',{HitEvent,'hit_state','Tup','wait_for_collecting_reward',ErrorEvent,'second_hit_state'}); + + sma = add_state(sma,'name','second_hit_state','self_timer',RewardCollection_duration,... + 'output_actions',{'DOut', SideLight},... + 'input_to_statechange',{'Tup','second_hit_state',HitEvent,'hit_state'}); + + sma = add_state(sma,'name','hit_state','self_timer',0.01,... + 'output_actions', {'DOut', SideLight,'SchedWaveTrig','reward_delivery','SoundOut',reward_sound_id},... + 'input_to_statechange',{'Tup','drink_state'}); + + + case 1 %% center led on -> poke in the center -> go cue -> reward light and sound -- waiting time grows slowlly -stimuli can be present + + sma = add_state(sma,'name','wait_for_cpoke','self_timer',CenterLed_duration, ... + 'output_actions', {'DOut', center1led}, ... + 'input_to_statechange', {'Cin','cp';'Tup','timeout_state'}); + + if stimuli_on ==0 || n_done_trials <1 + % center poke starts: trigger center_poke scheduled wave, + % and when that ends go to side_led_on + sma = add_state(sma,'name','cp','self_timer', SettlingIn_time, ... + 'output_actions', {'SchedWaveTrig', 'center_poke + settling_period'}, ... + 'input_to_statechange', {'Tup', 'cp_legal_cbreak_period', ... + 'Cout','current_state+1', ... + 'center_poke_Out', 'wait_for_collecting_reward', ... + 'Rin', 'violation_state', ... + 'Rout', 'violation_state', ... + 'Lin', 'violation_state', ... + 'Lout', 'violation_state'}); + else + + % center poke starts: trigger center_poke scheduled wave, + % and when that ends go to side_led_on + sma = add_state(sma,'name','cp','self_timer', SettlingIn_time+0.00001, ... + 'output_actions', {'SchedWaveTrig', 'center_poke + settling_period +stimA1'}, ... + 'input_to_statechange', {'Tup', 'cp_legal_cbreak_period', ... + 'Cout','current_state+1', ... + 'center_poke_Out', 'wait_for_collecting_reward', ... + 'Rin', 'violation_state', ... + 'Rout', 'violation_state', ... + 'Lin', 'violation_state', ... + 'Lout', 'violation_state'}); + end + + + % nose is out and we're in "SettlingIn_time": + % if settling_legal_cbreak time elapses, go to violation state, + % if nose is put back in, go to copy of cp start + % when SettlingIn_time elapses (settling_period_In) "legal cbreaks" changes to usueal legal_cbreaks + sma = add_state(sma, 'self_timer', settling_legal_cbreak+0.00001, ... + 'output_actions', {'DOut', center1led*LED_during_settling_legal_cbreak}, ... + 'input_to_statechange', {'Tup', 'violation_state', ... + 'Cin', 'current_state+1', ... + 'settling_period_In', 'cp_legal_cbreak_period', ... + 'center_poke_Out', 'wait_for_collecting_reward', ... + 'Rin', 'violation_state', ... + 'Rout', 'violation_state', ... + 'Lin', 'violation_state', ... + 'Lout', 'violation_state'}); + + % center poke: + % A copy of two states above, but without triggering the + % start of the center_poke scheduled wave. + sma = add_state(sma, 'self_timer', 10000, ... + 'input_to_statechange', {'Cout', 'current_state-1', ... + 'settling_period_In','cp_legal_cbreak_period', ... + 'center_poke_Out', 'wait_for_collecting_reward', ... + 'Rin', 'violation_state', ... + 'Rout', 'violation_state', ... + 'Lin', 'violation_state', ... + 'Lout', 'violation_state'}); + + % SettlingIn_time elapsed and from now on cbreaks are treated given legal_cbreaks + sma = add_state(sma,'name','cp_legal_cbreak_period', 'self_timer', 10000, ... + 'input_to_statechange', {'Cout', 'current_state+1', ... + 'Clo', 'current_state+1', ... + 'center_poke_Out', 'wait_for_collecting_reward', ... + 'Rin', 'violation_state', ... + 'Rout', 'violation_state', ... + 'Lin', 'violation_state', ... + 'Lout', 'violation_state'}); + + % nose is out and we're still in legal_cbreak: + % if legal_cbreak time elapses, go to violation_state, + % if nose is put back in, go to copy of cp start + sma = add_state(sma, 'self_timer', legal_cbreak+0.00001, ... + 'output_actions', {'DOut', center1led*LED_during_legal_cbreak}, ... + 'input_to_statechange', {'Tup', 'violation_state', ... + 'Cin', 'current_state+1', ... + 'center_poke_Out', 'wait_for_collecting_reward', ... + 'Rin', 'violation_state', ... + 'Rout', 'violation_state', ... + 'Lin', 'violation_state', ... + 'Lout', 'violation_state'}); + + % center poke: + % A copy of two states above, but without triggering the + % start of the center_poke scheduled wave. + sma = add_state(sma, 'self_timer', 10000, ... + 'input_to_statechange', {'Cout', 'current_state-1', ... + 'center_poke_Out', 'wait_for_collecting_reward', ... + 'Rin', 'violation_state', ... + 'Rout', 'violation_state', ... + 'Lin', 'violation_state', ... + 'Lout', 'violation_state'}); + + + sma = add_state(sma, 'name', 'wait_for_collecting_reward', 'self_timer', 30000, ... + 'output_actions', {'DOut', LEDOn*SideLight}, ... + 'input_to_statechange',{HitEvent, HitState, ErrorEvent, 'second_hit_state'}); + + % The two states that make a LeftHit: + %with reward sound + + sma = add_state(sma,'name', 'lefthit','self_timer', reward_delay, ... + 'output_actions', {'DOut', SideLight', 'SoundOut', Lreward_sound_id}, ... + 'input_to_statechange', {'Tup', 'current_state+1'}); + + % without reward sound +% sma = add_state(sma,'name', 'lefthit','self_timer', reward_delay, ... +% 'output_actions', {'DOut', SideLight'}, ... +% 'input_to_statechange', {'Tup', 'current_state+1'}); +% + sma = add_state(sma, 'self_timer', LeftWValveTime, ... + 'output_actions', {'DOut', SideLight+left1water,},... + 'input_to_statechange',{'Tup','hit_state'}); + + % The two states that make a RightHit: + + %with reward sound + sma = add_state(sma,'name', 'righthit','self_timer', reward_delay, ... + 'output_actions', {'DOut', SideLight', 'SoundOut', Rreward_sound_id}, ... + 'input_to_statechange', {'Tup', 'current_state+1'}); + + % without reward sound +% sma = add_state(sma,'name', 'righthit','self_timer', reward_delay, ... +% 'output_actions', {'DOut', SideLight'}, ... +% 'input_to_statechange', {'Tup', 'current_state+1'}); +% + sma = add_state(sma, 'self_timer', RightWValveTime, ... + 'output_actions', {'DOut', SideLight+right1water,},... + 'input_to_statechange',{'Tup','hit_state'}); + + + sma = add_state(sma,'name','second_hit_state','self_timer', wait_for_second_hit,... + 'output_actions',{'DOut', LEDOn*SideLight},... + 'input_to_statechange',{HitEvent, SecondHitState,'Tup','check_next_trial_ready'}); + + + % The two states that make a SecondLeftHit: + sma = add_state(sma,'name', 'secondlefthit','self_timer', secondhit_delay, ... + 'input_to_statechange', {'Tup', 'current_state+1'}); + sma = add_state(sma, 'self_timer', reward_delay, ... + 'output_actions', {'DOut', AnyReward*SideLight', 'SoundOut', AnyReward*Lreward_sound_id}, ... + 'input_to_statechange', {'Tup', 'current_state+1'}); + sma = add_state(sma, 'self_timer', LeftWValveTime, ... + 'output_actions', {'DOut', AnyReward*(SideLight+left1water)},... + 'input_to_statechange',{'Tup','hit_state'}); + + % The two states that make a SecondRightHit: + sma = add_state(sma,'name', 'secondrighthit','self_timer', secondhit_delay, ... + 'input_to_statechange', {'Tup', 'current_state+1'}); + sma = add_state(sma, 'self_timer', reward_delay, ... + 'output_actions', {'DOut', AnyReward*SideLight', 'SoundOut', AnyReward*Rreward_sound_id}, ... + 'input_to_statechange', {'Tup', 'current_state+1'}); + sma = add_state(sma, 'self_timer', RightWValveTime, ... + 'output_actions', {'DOut', AnyReward*(SideLight+right1water)},... + 'input_to_statechange',{'Tup','hit_state'}); + + % and a common hit_state that we flick through + sma = add_state(sma, 'name', 'hit_state', 'self_timer', 0.0001, ... + 'input_to_statechange', {'Tup', 'drink_state'}); + + + end %end of swith for different training_stages + + +% sma = add_state(sma,'name','drink_state','self_timer',AnyReward*drink_time+error_iti,... +% 'input_to_statechange',{'Tup','check_next_trial_ready'}); + + sma = add_state(sma,'name','drink_state','self_timer',AnyReward*drink_time+error_iti,... + 'input_to_statechange',{'Tup','preclean_up_state'}); + + if stimuli_on ==0 + sma = add_state(sma,'name','violation_state','self_timer',viol_snd_duration,... + 'output_actions',{'SchedWaveTrig', '-center_poke', ... + 'SoundOut',viol_sound_id, 'DOut', center1led},... + 'input_to_statechange', {'Tup', 'current_state+1'}); + else + + sma = add_state(sma,'name','violation_state','self_timer',viol_snd_duration,... + 'output_actions',{'SchedWaveTrig', '-center_poke-stimA1', ... + 'SoundOut',viol_sound_id, 'DOut', center1led},... + 'input_to_statechange', {'Tup', 'current_state+1'}); + end + + sma = add_state(sma, 'self_timer', max(0.001, violation_iti-viol_snd_duration), ... + 'input_to_statechange',{'Tup','preclean_up_state'}); + + sma = add_state(sma,'name','timeout_state','self_timer', timeout_duration,... + 'output_actions',{'SoundOut',to_sound_id},... + 'input_to_statechange',{'Tup','preclean_up_state'}); + + + sma = add_state(sma,'name','preclean_up_state','self_timer',0.5,... + 'output_actions',{ 'SchedWaveTrig','+TrigScope'},... + 'input_to_statechange',{'Tup','check_next_trial_ready'}); + + varargout{2} = {'check_next_trial_ready'}; + + varargout{1} = sma; + + % Not all 'prepare_next_trial_states' are defined in all training + % stages. So we send to dispatcher only those states that are + % defined. + state_names = get_labels(sma); state_names = state_names(:,1); + prepare_next_trial_states = {'lefthit', 'righthit', 'hit_state','second_hit_state', 'error_state', 'violation_state','timeout_state'}; + + sma = StimulatorSection(obj,'prepare_next_trial',sma); + dispatcher('send_assembler', sma, intersect(state_names, prepare_next_trial_states)); + + case 'get_state_colors', + varargout{1} = struct( ... + 'wait_for_cpoke', [0.68 1 0.63], ... + 'cp', [0.63 1 0.94], ... + 'cp_legal_cbreak_period', [0.63 1 0.94]*0.8, ... + 'sideled_on', [1 0.79 0.63], ... + 'wait_for_collecting_reward', [0.53 0.78 1.00],... + 'righthit', [0.3 0.9 0], ... + 'lefthit', [0 0.9 0.3], ... + 'hit_state', [0.77 0.60 0.48], ... + 'second_hit_state', [0.25 0.45 0.48], ... + 'drink_state', [0 1 0], ... + 'error_state', [1 0.54 0.54], ... + 'violation_state', [0.31 0.48 0.30], ... + 'timeout_state', 0.8*[0.31 0.48 0.30]); + % 'go_cue_on', [0.63 1 0.94]*0.6, ... + % 'prerw_postcs', [0.25 0.45 0.48], ... + % 'lefthit', [0.53 0.78 1.00], ... + % 'lefthit_pasound', [0.53 0.78 1.00]*0.7, ... + % 'righthit', [0.52 1.0 0.60], ... + % 'righthit_pasound', [0.52 1.0 0.60]*0.7, ... + % 'warning', [0.3 0 0], ... + % 'danger', [0.5 0.05 0.05], ... + % 'hit', [0 1 0] + + + + + case 'reinit', + currfig = gcf; + + % Delete all SoloParamHandles who belong to this object and whose + % fullname starts with the name of this mfile: + delete_sphandle('owner', ['^@' class(obj) '$'], ... + 'fullname', ['^' mfilename]); + + + % Reinitialise at the original GUI position and figure: + feval(mfilename, obj, 'init'); + + % Restore the current figure: + figure(currfig); + + otherwise + warning('do not know how to do %s',action); +end \ No newline at end of file diff --git a/Protocols/@AltSoundCategorizationCatch/SoundCatSMA.m b/Protocols/@AltSoundCategorizationCatch/SoundCatSMA.m index 2a5b94ab..46449b14 100644 --- a/Protocols/@AltSoundCategorizationCatch/SoundCatSMA.m +++ b/Protocols/@AltSoundCategorizationCatch/SoundCatSMA.m @@ -88,7 +88,8 @@ end end; - +% sma = StateMachineAssembler('full_trial_structure','use_happenings',1, ... +% 'n_input_lines',4,'line_names','CLRA'); sma = StateMachineAssembler('full_trial_structure','use_happenings', 1); sma = add_scheduled_wave(sma, 'name', 'center_poke', 'preamble', CP_duration, ... @@ -107,6 +108,7 @@ if value(imaging)==1 sma = add_scheduled_wave(sma, 'name', 'TrigScope', 'preamble', 0, 'sustain', ... 0.5, 'DOut', trigscope, 'loop', 0); + %99999, 'DOut', trigscope, 'loop', 0); %for Miniscope Camera else sma = add_scheduled_wave(sma, 'name', 'TrigScope', 'preamble', 0, 'sustain', 0); %dummy wave. end @@ -151,7 +153,7 @@ case 1 %% center led on -> poke in the center -> go cue -> reward light and sound -- waiting time grows slowlly -stimuli can be present sma = add_state(sma,'name','wait_for_cpoke','self_timer',CenterLed_duration, ... - 'output_actions', {'DOut', center1led}, ... + 'output_actions', {'DOut', center1led, 'SchedWaveTrig','+TrigScope'}, ... 'input_to_statechange', {'Cin','cp';'Tup','timeout_state'}); if stimuli_on ==0 || n_done_trials <1 @@ -342,8 +344,8 @@ sma = add_state(sma,'name','preclean_up_state','self_timer',0.5,... - 'output_actions',{ 'SchedWaveTrig','+TrigScope'},... - 'input_to_statechange',{'Tup','check_next_trial_ready'}); + 'output_actions',{'SchedWaveTrig','-TrigScope'},... + 'input_to_statechange',{'Tup','check_next_trial_ready'}); varargout{2} = {'check_next_trial_ready'}; @@ -387,7 +389,7 @@ case 'reinit', - currfig = double(gcf); + currfig = gcf; % Delete all SoloParamHandles who belong to this object and whose % fullname starts with the name of this mfile: From e2d00869949a7dccb70db7f53b41fcde26401bcc Mon Sep 17 00:00:00 2001 From: viktorpm Date: Fri, 29 Nov 2024 15:25:24 +0000 Subject: [PATCH 011/164] adding IP adresses to start_camera.m from rig31 --- ExperPort/Modules/start_camera.m | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/ExperPort/Modules/start_camera.m b/ExperPort/Modules/start_camera.m index deb3542f..fca5116e 100644 --- a/ExperPort/Modules/start_camera.m +++ b/ExperPort/Modules/start_camera.m @@ -20,13 +20,23 @@ '172.24.155.117', '172.24.155.118', '172.24.155.119', - '172.24.155.120' - }; + '172.24.155.120', + '172.24.155.121', + '172.24.155.122', + '172.24.155.123', + '172.24.155.124', + '172.24.155.125', + '172.24.155.126', + '172.24.155.127', + '172.24.155.128', + '172.24.155.129', + '172.24.155.130', + '172.24.155.131'}; rig_camIP = ips{rig_id}; if strcmp(flag,'start') filename = [rat_id,'_',datestr(date,'yymmdd')]; disp('created filename') - command_string = ['plink.exe -ssh pi@',rig_camIP,' -pw raspberry cd Pi_camera; python3 streamnrecord.py ', ' ',filename,' ',rat_id,' ',protocol,' "COMMAND >/dev/null &"']; + command_string = ['plink.exe -ssh pi@',rig_camIP,' -pw raspberry cd pi_camera; python3 streamnrecord.py ', ' ',filename,' ',rat_id,' ',protocol,' "COMMAND >/dev/null &"']; system(command_string); disp('sent starting command') From c4db5ee51196b93e7d53fa3444db01c6a77689e2 Mon Sep 17 00:00:00 2001 From: viktorpm <50667179+viktorpm@users.noreply.github.com> Date: Thu, 5 Dec 2024 15:13:54 +0000 Subject: [PATCH 012/164] Copy plots to new figure in StimulusSection.m (#9) * Copy plots to new figure in StimulusSection.m - Add copyobj calls to duplicate axes and plots - Enhance responsiveness by copying plot objects - Improve GUI management with copied elements - Note: This is a hacky workaround since plots don't display correctly on the original figure - Optimize figure positioning and sizing - Update axis labels and tick marks * Improve figure positioning and sizing - Center PokesPlotSection figure while maintaining original aspect ratio - Center AthenaDelayComp protocol figure on screen while preserving original size - Use dynamic positioning based on screen size for better cross-platform compatibility * clearing axes, creating axes in loop * Refactor StimulusSection plotting of pairs - Implement new axes (ax2 and axperf2) for pairs performance - Copy of ax and axperf axes content to ax2 and axperf2 --- .../Plugins/@pokesplot2/PokesPlotSection.m | 17 ++- Protocols/@AthenaDelayComp/AthenaDelayComp.m | 15 ++- Protocols/@AthenaDelayComp/StimulusSection.m | 125 +++++++++++++----- 3 files changed, 122 insertions(+), 35 deletions(-) diff --git a/ExperPort/Plugins/@pokesplot2/PokesPlotSection.m b/ExperPort/Plugins/@pokesplot2/PokesPlotSection.m index e25487c1..de731ad1 100644 --- a/ExperPort/Plugins/@pokesplot2/PokesPlotSection.m +++ b/ExperPort/Plugins/@pokesplot2/PokesPlotSection.m @@ -355,10 +355,25 @@ %% Formatting graphics elements %myfig + original_width = 0.46875; + original_height = 0.815; + + max_size = min(original_width, original_height); + aspect_ratio = original_width / original_height; + new_width = max_size; + new_height = new_width / aspect_ratio; + + center_x = 0.5 - (new_width / 2); + center_y = 0.5 - (new_height / 2); + + position_vector = [center_x center_y new_width new_height]; + + + set(value(myfig), ... 'Units', 'normalized', ... 'Name', mfilename, ... - 'Position', [0.27031 0.0275 0.46875 0.815]); + 'Position', position_vector); %textHeader set(get_ghandle(textHeader), ... diff --git a/Protocols/@AthenaDelayComp/AthenaDelayComp.m b/Protocols/@AthenaDelayComp/AthenaDelayComp.m index 42523992..148b64fb 100644 --- a/Protocols/@AthenaDelayComp/AthenaDelayComp.m +++ b/Protocols/@AthenaDelayComp/AthenaDelayComp.m @@ -47,8 +47,21 @@ set(value(myfig), 'Name', name, 'Tag', name, ... 'closerequestfcn', 'dispatcher(''close_protocol'')', 'MenuBar', 'none'); % At this point we have one SoloParamHandle, myfig + % Let's put the figure where we want it and give it a reasonable size: - set(value(myfig), 'Position', [485 144 850 680]); + original_width = 850; + original_height = 680; + + % Get screen size + scrsz = get(0,'ScreenSize'); + + % Center the figure while maintaining original size + center_x = (scrsz(3) - original_width) / 2; + center_y = (scrsz(4) - original_height) / 2; + + position_vector = [center_x center_y original_width original_height]; + + set(value(myfig), 'Position', position_vector); SoloParamHandle(obj, 'nsessions_healthy_number_of_pokes', 'value', 0, 'save_with_settings', 1); SoloParamHandle(obj, 'post_DelComp_protocol', 'value', '', 'save_with_settings', 1); diff --git a/Protocols/@AthenaDelayComp/StimulusSection.m b/Protocols/@AthenaDelayComp/StimulusSection.m index 721f35bd..f6d94551 100644 --- a/Protocols/@AthenaDelayComp/StimulusSection.m +++ b/Protocols/@AthenaDelayComp/StimulusSection.m @@ -14,34 +14,71 @@ if length(varargin) < 2 error('Need at least two arguments, x and y position, to initialize %s', mfilename); end - % x = varargin{1}; y = varargin{2}; + % x = varargin{1}; y = varargin{2}; + + % creating pairs and performance figure window with axes + SoloParamHandle( ... + obj, 'pairs_plot_figure', ... + 'value', figure( ... + 'CloseRequestFcn', {@hide, obj}, ... + 'MenuBar', 'none', ... + 'Name', 'Pairs and performance'), ... + 'saveable', 0); + screen_size = get(0, 'ScreenSize'); + set(double(value(pairs_plot_figure)), ... + 'Position',[ ... + (screen_size(3)-800)/2 ... + (screen_size(4)-600)/2 800 600]); + + SoloParamHandle( ... + obj, 'ax2', ... + 'saveable', 0, ... + 'value', axes('Position',[0.05 0.3 0.45 0.45])); + ylabel('log_e \sigma_2'); + xlabel('log_e \sigma_1'); + axis square; + + SoloParamHandle( ... + obj, 'axperf2', ... + 'saveable', 0, ... + 'value', axes('Position',[0.55 0.3 0.45 0.45])); + ylabel('log_e \sigma_2'); + xlabel('log_e \sigma_1'); + axis square; - % SoloParamHandle(obj, 'myfig', 'value', figure('closerequestfcn', [mfilename '(' class(obj) ', ''hide'');'], 'MenuBar', 'none', ... - % 'Name', mfilename), 'saveable', 0); - % creating figure window - SoloParamHandle(obj, 'myfig', 'value', figure('CloseRequestFcn', {@hide, obj}, ... - 'MenuBar', 'none', 'Name', mfilename), 'saveable', 0); + + % creating StimulusSection figure window for GUI elements + SoloParamHandle( ... + obj, 'myfig', ... + 'value', figure( ... + 'CloseRequestFcn', {@hide, obj}, ... + 'MenuBar', 'none', ... + 'Name', mfilename), ... + 'saveable', 0); screen_size = get(0, 'ScreenSize'); - set(value(myfig),'Position',[(screen_size(3)-1100)/2 (screen_size(4)-800)/2 1100 800]); - % set(double(gcf), 'Visible', 'on'); + set(double(value(myfig)), ... + 'Position',[ ... + (screen_size(3)-1100)/2 ... + (screen_size(4)-300)/2 1100 300] ... + ); % adding plot - SoloParamHandle(obj, 'ax', 'saveable', 0, ... - 'value', axes('Position', [0.1 0.5 0.45 0.45])); + SoloParamHandle( ... + obj, 'ax', ... + 'saveable', 0, ... + 'value', axes('Position',[0.05 0.3 0.45 0.45])); ylabel('log_e \sigma_2'); - %set(value(ax),'Fontsize',15) - xlabel('log_e \sigma_1') + xlabel('log_e \sigma_1'); axis square; % adding performance plot SoloParamHandle(obj, 'axperf', 'saveable', 0, ... - 'value', axes('Position', [0.55 0.5 0.45 0.45])); + 'value', axes('Position',[0.55 0.3 0.45 0.45])); ylabel('log_e \sigma_2'); - %set(value(axperf),'Fontsize',15) - xlabel('log_e \sigma_1') + xlabel('log_e \sigma_1'); axis square; - + SoundManagerSection(obj, 'declare_new_sound', 'StimAUD1') SoundManagerSection(obj, 'declare_new_sound', 'StimAUD2') @@ -55,15 +92,22 @@ x=5; y=5; % Create toggle parameter for stimulus visibility - ToggleParam(obj, 'StimulusShow', 0, x, y, ... - 'OnString', 'Stimuli', 'OffString', 'Stimuli', ... + ToggleParam( ... + obj, ... + 'StimulusShow', 0, ... + x, y, ... + 'OnString', 'Stimuli', ... + 'OffString', 'Stimuli', ... 'TooltipString', 'Show/Hide Stimulus panel'); set_callback(StimulusShow, {mfilename, 'show_hide'}); %#ok (Defined just above) next_row(y); - - MenuParam(obj, 'StimulusType', {'library', 'new'}, 'new', ... - x, y, 'labelfraction', 0.35, ... - 'TooltipString', ... + + MenuParam( ... + obj, 'StimulusType', ... + {'library', 'new'}, 'new', ... + x, y, ... + 'labelfraction', 0.35, ... + 'TooltipString', ... sprintf(['\n"new" means at each trial, a new noise pattern will be generated,\n' ... '"library" means for each trial stimulus is loaded from a library with limited number of noise patterns']) ... ); @@ -183,7 +227,8 @@ set_callback_on_load(numClass, 4); %#ok set_callback(numClass, {mfilename, 'numClass'}); - numClass.value = 4; callback(numClass); + numClass.value = 4; + callback(numClass); StimulusSection(obj,'plot_pairs'); set_callback(psych_pairs, {mfilename, 'PsychPairs'}); @@ -528,11 +573,13 @@ %% Plot the pair set cla(value(ax)); + cla(value(ax2)); % Transform data to log scale xd = log(thesepairs(:,1)); yd = log(thesepairs(:,2)); + % Plot individual points for ii = 1:length(xd) axes(value(ax)); @@ -548,15 +595,16 @@ % Calculate and set tick values % Ytick=get(value(ax),'YtickLabel'); % Xtick=get(value(ax),'XtickLabel'); + set(value(ax), 'ytick', ((yd(1:1+value(midclass_pairs):(end-nPsych*psych_pairs)/2)))); set(value(ax), 'xtick', ((xd(1:1+value(midclass_pairs):(end-nPsych*psych_pairs)/2)))); set(value(ax), 'yticklabel', num2str(exp(yd(1:1+value(midclass_pairs):(end-nPsych*psych_pairs)/2)), 2)); set(value(ax), 'xticklabel', num2str(exp(xd(1:1+value(midclass_pairs):(end-nPsych*psych_pairs)/2)), 2)); % Set labels and font sizes - ylabel('\sigma_2 in log scale', 'FontSize', 16, 'FontName', 'Cambria Math'); - set(value(ax), 'Fontsize', 15); - xlabel('\sigma_1 in log scale', 'FontSize', 16, 'FontName', 'Cambria Math'); + ylabel('log_e \sigma_2'); + xlabel('log_e \sigma_1'); + %% Select side and generate pair based on rule and trial SideSection(obj,'get_current_side'); @@ -676,6 +724,9 @@ %% Plot the selected pair h1.value = plot(log(value(A1_sigma)),log(value(A2_sigma)),'s','color',[0.8 0.4 0.1],'markerfacecolor',[0.8 0.4 0.1],'MarkerSize',15,'LineWidth',3); + axChil = ax.Children; + copyobj(axChil, value(ax2)); + % Commented-out alternative plotting function % LOGplotPairs(thesepairs(:,1),thesepairs(:,2),'s',15,'k',1,16,thispair(1),thispair(2),value(ax),'init') @@ -742,23 +793,30 @@ end %% plot the pair set - cla(value(axperf)) + cla(value(axperf)); + cla(value(axperf2)); + xd=log(thesepairs(:,1)); yd=log(thesepairs(:,2)); + + axes(value(axperf)); + for ii=1:length(xd) - axes(value(axperf)); plot(xd(ii),yd(ii),'s','MarkerSize',31,'MarkerEdgeColor',[0 0 0],'LineWidth',1.5) hold on eval(sprintf('perf=value(perfClass%d);',ii)) text(xd(ii)-0.14,yd(ii),num2str(round(perf*1000)/10)); hold on end + axis square - set(value(ax),'ytick',((yd(1:1+value(midclass_pairs):(end-nPsych*psych_pairs)/2))),'xtick',((xd(1:1+value(midclass_pairs):(end-nPsych*psych_pairs)/2)))); - set(value(ax),'yticklabel',num2str(exp(yd(1:1+value(midclass_pairs):(end-nPsych*psych_pairs)/2)),2),'xticklabel',num2str(exp(xd(1:1+value(midclass_pairs):(end-nPsych*psych_pairs)/2)),2)); - ylabel('\sigma_2 in log scale','FontSize',16,'FontName','Cambria Math'); - set(value(axperf),'Fontsize',15) - xlabel('\sigma_1 in log scale','FontSize',16,'FontName','Cambria Math') + set(value(axperf),'ytick',((yd(1:1+value(midclass_pairs):(end-nPsych*psych_pairs)/2))),'xtick',((xd(1:1+value(midclass_pairs):(end-nPsych*psych_pairs)/2)))); + set(value(axperf),'yticklabel',num2str(exp(yd(1:1+value(midclass_pairs):(end-nPsych*psych_pairs)/2)),2),'xticklabel',num2str(exp(xd(1:1+value(midclass_pairs):(end-nPsych*psych_pairs)/2)),2)); + ylabel('\sigma_2 in log scale'); + xlabel('\sigma_1 in log scale'); + + axperfChil = axperf.Children; + copyobj(axperfChil, value(axperf2)); %% Case psych_pairs @@ -855,6 +913,7 @@ %% Case hide case 'hide' StimulusShow.value = 0; set(value(myfig), 'Visible', 'off'); + set(value(pairs_plot_figure), 'Visible', 'off'); %% Case show case 'show' StimulusShow.value = 1; set(value(myfig), 'Visible', 'on'); From 6133e3a56af8ec1b16efc5303206c278a3fecbf8 Mon Sep 17 00:00:00 2001 From: viktorpm <50667179+viktorpm@users.noreply.github.com> Date: Wed, 18 Dec 2024 11:16:29 +0000 Subject: [PATCH 013/164] Opto setup rig31 (#11) * updating AthenaDelayComp with opto settings * adding SoundCatContinuous protocol from rig 31 --- .../@AthenaDelayComp/StimulatorSection.m | 17 +- .../AntibiasSectionAthena.m | 315 ++++++++++++ Protocols/@SoundCatContinuous/AthenaSMA_aa.m | 392 +++++++++++++++ Protocols/@SoundCatContinuous/DelayComp.m | 62 +++ Protocols/@SoundCatContinuous/DelayComp1.m | 124 +++++ Protocols/@SoundCatContinuous/LOGplotPairs.m | 71 +++ .../OverallPerformanceSection.m | 127 +++++ Protocols/@SoundCatContinuous/PlayStimuli.m | 87 ++++ .../@SoundCatContinuous/ProduceNoiseStimuli.m | 33 ++ .../@SoundCatContinuous/PunishmentSection.m | 133 +++++ .../@SoundCatContinuous/RewardsSection.m | 237 +++++++++ Protocols/@SoundCatContinuous/SideSection.m | 469 ++++++++++++++++++ .../@SoundCatContinuous/SideSection_aa.m | 378 ++++++++++++++ .../@SoundCatContinuous/SoundCatContinuous.m | 261 ++++++++++ .../@SoundCatContinuous/SoundCatSMA - Copy.m | 424 ++++++++++++++++ .../@SoundCatContinuous/SoundCatSMA-30.m | 407 +++++++++++++++ .../@SoundCatContinuous/SoundCatSMA-video.m | 418 ++++++++++++++++ Protocols/@SoundCatContinuous/SoundCatSMA.m | 424 ++++++++++++++++ Protocols/@SoundCatContinuous/SoundCatSMART.m | 413 +++++++++++++++ Protocols/@SoundCatContinuous/SoundSection.m | 63 +++ .../@SoundCatContinuous/StimulatorSection.m | 325 ++++++++++++ .../@SoundCatContinuous/StimulusSection.m | 435 ++++++++++++++++ .../@SoundCatContinuous/StimulusSection_.m | 349 +++++++++++++ Protocols/@SoundCatContinuous/noiselib.m | 51 ++ Protocols/@SoundCatContinuous/private/filt.m | 60 +++ .../@SoundCatContinuous/private/noisestim.m | 47 ++ .../@SoundCatContinuous/private/singlenoise.m | 32 ++ Protocols/@SoundCatContinuous/state_colors.m | 16 + Protocols/@SoundCatContinuous/wave_colors.m | 11 + 29 files changed, 6177 insertions(+), 4 deletions(-) create mode 100644 Protocols/@SoundCatContinuous/AntibiasSectionAthena.m create mode 100644 Protocols/@SoundCatContinuous/AthenaSMA_aa.m create mode 100644 Protocols/@SoundCatContinuous/DelayComp.m create mode 100644 Protocols/@SoundCatContinuous/DelayComp1.m create mode 100644 Protocols/@SoundCatContinuous/LOGplotPairs.m create mode 100644 Protocols/@SoundCatContinuous/OverallPerformanceSection.m create mode 100644 Protocols/@SoundCatContinuous/PlayStimuli.m create mode 100644 Protocols/@SoundCatContinuous/ProduceNoiseStimuli.m create mode 100644 Protocols/@SoundCatContinuous/PunishmentSection.m create mode 100644 Protocols/@SoundCatContinuous/RewardsSection.m create mode 100644 Protocols/@SoundCatContinuous/SideSection.m create mode 100644 Protocols/@SoundCatContinuous/SideSection_aa.m create mode 100644 Protocols/@SoundCatContinuous/SoundCatContinuous.m create mode 100644 Protocols/@SoundCatContinuous/SoundCatSMA - Copy.m create mode 100644 Protocols/@SoundCatContinuous/SoundCatSMA-30.m create mode 100644 Protocols/@SoundCatContinuous/SoundCatSMA-video.m create mode 100644 Protocols/@SoundCatContinuous/SoundCatSMA.m create mode 100644 Protocols/@SoundCatContinuous/SoundCatSMART.m create mode 100644 Protocols/@SoundCatContinuous/SoundSection.m create mode 100644 Protocols/@SoundCatContinuous/StimulatorSection.m create mode 100644 Protocols/@SoundCatContinuous/StimulusSection.m create mode 100644 Protocols/@SoundCatContinuous/StimulusSection_.m create mode 100644 Protocols/@SoundCatContinuous/noiselib.m create mode 100644 Protocols/@SoundCatContinuous/private/filt.m create mode 100644 Protocols/@SoundCatContinuous/private/noisestim.m create mode 100644 Protocols/@SoundCatContinuous/private/singlenoise.m create mode 100644 Protocols/@SoundCatContinuous/state_colors.m create mode 100644 Protocols/@SoundCatContinuous/wave_colors.m diff --git a/Protocols/@AthenaDelayComp/StimulatorSection.m b/Protocols/@AthenaDelayComp/StimulatorSection.m index 73f43b29..972a4bb4 100644 --- a/Protocols/@AthenaDelayComp/StimulatorSection.m +++ b/Protocols/@AthenaDelayComp/StimulatorSection.m @@ -49,6 +49,8 @@ [dionums order] = sort(dionums); dionames2 = cell(0); for i = 1:length(dionums); if ~isnan(dionums(i)); dionames2{end+1} = dionames{order(i)}; end; end %#ok + dionames2 = cell(0); + dionames2{1} = 'opto'; MenuParam(obj,'StimLine',dionames2,1,x,y,'labelfraction',0.30); next_row(y); @@ -57,7 +59,7 @@ states = fieldnames(SC); waves = fieldnames(WC); states(2:end+1) = states; - states{1} = 'none'; + states{1} = 'cp'; states(end+1:end+length(waves)) = waves; MenuParam(obj,'StimState',states,1,x,y,'labelfraction',0.30); next_row(y); @@ -68,14 +70,14 @@ NumeditParam(obj,'StartDelay', 0,x,y,'position',[x y 100 20],'labelfraction',0.60); NumeditParam(obj,'StimFreq', 20,x,y,'position',[x+100 y 100 20],'labelfraction',0.60); next_row(y); NumeditParam(obj,'PulseWidth',15,x,y,'position',[x y 100 20],'labelfraction',0.60); - NumeditParam(obj,'NumPulses', 10,x,y,'position',[x+100 y 100 20],'labelfraction',0.60); next_row(y); + NumeditParam(obj,'NumPulses', 1,x,y,'position',[x+100 y 100 20],'labelfraction',0.60); next_row(y); DispParam(obj,'SD',0 ,x,y,'position',[x y 50 20],'labelfraction',0.4); DispParam(obj,'SF',20,x,y,'position',[x+50 y 50 20],'labelfraction',0.4); DispParam(obj,'PW',15,x,y,'position',[x+100 y 50 20],'labelfraction',0.4); DispParam(obj,'NP',1,x,y,'position',[x+150 y 50 20],'labelfraction',0.4); next_row(y); - NumeditParam(obj,'StimProb', 0,x,y,'position',[x y 100 20],'labelfraction',0.65); + NumeditParam(obj,'StimProb', 1,x,y,'position',[x y 100 20],'labelfraction',0.65); ToggleParam( obj,'ShuffleValues',0,x,y,'position',[x+100 y 100 20],'OnString','Shuffle','OffString','Lock'); next_row(y); SoloParamHandle(obj, 'stimulator_history', 'value', []); @@ -159,6 +161,8 @@ disp('StimState value greater than list of possible stim states'); else StimState.value = ss; + disp('test ss') + value(ss) end if sl > length(psl) @@ -192,7 +196,12 @@ for i = 1:length(sl) stimline = bSettings('get','DIOLINES',psl{sl(i)}); - + + disp('stimlinevalue') + psl{sl(i)} + disp('stimlinevalue2') + stimline + sma = add_scheduled_wave(sma,... 'name', ['stimulator_wave',num2str(i)],... 'preamble', (1/sf)-(pw/1000),... %%%% Remember: change it such that if this is negative makes it 0 diff --git a/Protocols/@SoundCatContinuous/AntibiasSectionAthena.m b/Protocols/@SoundCatContinuous/AntibiasSectionAthena.m new file mode 100644 index 00000000..cb34d14a --- /dev/null +++ b/Protocols/@SoundCatContinuous/AntibiasSectionAthena.m @@ -0,0 +1,315 @@ +% [x, y] = AntibiasSection(obj, action, [arg1], [arg2], [arg3]) +% +% Section that calculates biases and calculates probability of choosing a stimulus +% given the previous history. +% +% Antibias assumes that trials are of two classes, Left desired answer +% and Right desired answer, and that their outcome is either Correct or +% Incorrect. Given the history of previous trial classes, and the history +% of previous corrects/incorrects, Antibias makes a local estimate of +% fraction correct for each class, combines that with a prior probability +% of making the next trial Left, and produces a recommended probability +% for choosing the next trial as Left. Antibias will tend to make the +% class with the smaller frac correct the one with the higher probability. +% The strength of that tendency is quantified by a parameter, beta. +% (See probabilistic_trial_selector.m for details on the math of how the +% tendency is generated.) +% +% Local estimates of fraction correct are computed using an exponential +% kernel, most recent trial the most strongly weighted. The tau of this +% kernel is a GUI parameter. Two different estimates are computed: one for +% use in computing Left probability and Right probability; and a second +% simply for GUI display purposes. The two estimates can have different +% taus for their kernels. +% +% GUI DISPLAY: When initialized, this plugin will put up two panels and a +% title. In each panel, there is a slider that controls the tau of the +% recent-trials exponential kernel. One panel will display the percent +% corrects for Right and Left, as computed with its kernel. The second panel +% will display the a posteriori probabilities of making the next trial a +% "Left" trial or making the next trial a "Right" trial. This second panel +% has its own tau slider, and it also has a GUI parameter, beta, that +% controls how strongly the history matters. If beta=0, history doesn't +% matter, and the a priori LeftProbability dominates. If beta=Inf, then +% history matters above all: the next trial will be of the type with lowest +% fraction correct, for sure. +% +% See the bottom of this help file for examples of usage. +% +% arg1, arg2, arg3 are optional and their meaning depends on action (see +% below). +% +% PARAMETERS: +% ----------- +% +% obj Default object argument. +% +% action One of: +% +% 'init' To initialise the plugin and set up the GUI for it. This +% action requires two more arguments: The bottom left +% x y position (in units of pixels) of where to start placing +% the GUI elements of this plugin. +% +% 'update' This call will recompute both local estimates of +% fraction correct, and will recompute the recommended +% LeftProb, p(Left). This action requires three more arguments: +% HitHist, LProb, a scalar b/w 0 and 1; HitHist, a vector of 1s +% SidesHist and 0s and of length n_done_trials where 1 represents +% correct and 0 represents incorrect, first element +% corresponds to first trial; and SidesHist, a vector of +% 'l's and 'r's and of length n_done_trials where 'l' +% represents 'left', 'r' represents 'right' first element +% corresponds to first trial. +% +% 'get_posterior_probs' Returns a vector with two components, +% [p(Left) p(Right)]. +% +% +% 'update_biashitfrac' This call will recompute the local estimate of fraction +% Left correct and fraction Right correct used for +% LeftProb, antibiasing, and will also recompute the recommended +% HitHist, Left probability. This action +% SidesHist, requires three more arguments: LProb, a scalar b/w 0 +% and 1; HitHist, a vector of 1s and 0s and of length +% n_done_trials where 1 represents correct and 0 +% represents incorrect, first element corresponds to +% first trial; and SidesHist, a vector of 'l's and 'r's +% and of length n_done_trials where 'l' represents +% 'left', 'r' represents 'right' first element +% corresponds to first trial. +% +% 'update_hitfrac' This call is not related to computing the posterior +% Left probability, but will recompute only the local estimate +% HitHist, of fraction correct that is not used for antibiasing. +% SidesHist This action requires two more arguments: HitHist, a +% vector of 1s and 0s and of length n_done_trials where 1 +% represents correct and 0 represents incorrect, first +% element corresponds to first trial; and SidesHist, a vector of +% 'l's and 'r's and of length n_done_trials where 'l' +% represents 'left', 'r' represents 'right' first element +% corresponds to first trial. +% +% 'get' Needs one extra parameter, either 'Beta' or +% 'antibias_tau', and returns the corresponding scalar. +% +% 'reinit' Delete all of this section's GUIs and data, +% and reinit, at the same position on the same +% figure as the original section GUI was placed. +% +% +% x, y Relevant to action = 'init'; they indicate the initial +% position to place the GUI at, in the current figure window +% +% RETURNS: +% -------- +% +% if action == 'init' : +% +% [x1, y1, w, h] When action == 'init', Antibias will put up GUIs and take +% up a certain amount of space of the figure that was current when +% AntiBiasSection(obj, 'init', x, y) was called. On return, [x1 y1] +% will be the top left corner of the space used; [x y] (as passed +% to Antibias in the init call) will be the bottom left corner; +% [x+w y1] will be the top right; and [x+w y] will be the bottom +% right. h = y1-y. All these are in units of pixels. +% +% +% if action == 'get_posterior_probs' : +% +% [L R] When action == 'get_posterior_probs', a two-component vector is +% returned, with p(Left) and p(Right). If beta=0, then p(Left) +% will be the same as the last LeftProb that was passed in. +% +% +% USAGE: +% ------ +% +% To use this plugin, the typical calls would be: +% +% 'init' : On initializing your protocol, call +% AntibiasSection(obj, 'init', x, y); +% +% 'update' : After a trial is completed, call +% AntibiasSection(obj, 'update', LeftProb, HitHist, SidesHist) +% +% 'get_posterior_probs' : After a trial is completed, and when you are +% deciding what kind of trial to make the next trial, get the plugins +% opinion on whether the next trial should be Left or Right by calling +% AntibiasSection(obj, 'get_posterior_probs') +% +% See PARAMETERS section above for the documentation of each of these calls. +% + + +function [x, y, w, h] = AntibiasSection(obj, action, varargin) + +GetSoloFunctionArgs(obj); + +switch action + + case 'init', % ------------ CASE INIT ---------------- + x = varargin{1}; y = varargin{2}; y0 = y; + % Save the figure and the position in the figure where we are + % going to start adding GUI elements: + SoloParamHandle(obj, 'my_gui_info', 'value', [x y double(gcf)]); + + LogsliderParam(obj, 'HitFracTau', 30, 10, 400, x, y, 'label', 'hits frac tau', ... + 'TooltipString', ... + sprintf(['\nnumber of trials back over which to compute fraction of correct trials.\n' ... + 'This is just for displaying info-- for the bias calculation, see BiasTau above'])); + set_callback(HitFracTau, {mfilename, 'update_hitfrac'}); + next_row(y); + DispParam(obj, 'LtHitFrac', 0, x, y); next_row(y); + DispParam(obj, 'RtHitFrac', 0, x, y); next_row(y); + DispParam(obj, 'HitFrac', 0, x, y); next_row(y); + + next_row(y, 0.5); + LogsliderParam(obj, 'BiasTau', 30, 10, 400, x, y, 'label', 'antibias tau', ... + 'TooltipString', ... + sprintf(['\nnumber of trials back over\nwhich to compute fraction of correct trials\n' ... + 'for the antibias function.'])); next_row(y); + NumeditParam(obj, 'Beta', 0, x, y, ... + 'TooltipString', ... + sprintf(['When this is 0, past performance doesn''t affect choice\n' ... + 'of next trial. When this is large, the next trial is ' ... + 'almost guaranteed\nto be the one with smallest %% correct'])); next_row(y); + set_callback({BiasTau, Beta}, {mfilename, 'update_biashitfrac'}); + DispParam(obj, 'LtProb', 0, x, y); next_row(y); + DispParam(obj, 'RtProb', 0, x, y); next_row(y); + SoloParamHandle(obj, 'BiasLtHitFrac', 'value', 0); + SoloParamHandle(obj, 'BiasRtHitFrac', 'value', 0); + + SoloParamHandle(obj, 'LocalLeftProb', 'value', 0.5); + SoloParamHandle(obj, 'LocalHitHistory', 'value', []); + SoloParamHandle(obj, 'LocalPrevSides', 'value', []); + + + SubheaderParam(obj, 'title', mfilename, x, y); + next_row(y, 1.5); + + w = gui_position('get_width'); + h = y-y0; + + + case 'update', % --- CASE UPDATE ------------------- + if length(varargin)>0, LocalLeftProb.value = varargin{1}; end; + if length(varargin)>1, LocalHitHistory.value = varargin{2}; end; + if length(varargin)>2, LocalPrevSides.value = varargin{3}; end; + % Protect against somebody passing in SPHs, not actual values, by mistake: + if isa(value(LocalLeftProb), 'SoloParamHandle'), LocalLeftProb.value = value(value(LocalLeftProb)); end; + if isa(value(LocalHitHistory), 'SoloParamHandle'), LocalHitHistory.value = value(value(LocalHitHistory)); end; + if isa(value(LocalPrevSides), 'SoloParamHandle'), LocalPrevSides.value = value(value(LocalPrevSides)); end; + + feval(mfilename, obj, 'update_hitfrac'); + feval(mfilename, obj, 'update_biashitfrac'); + + + + case 'update_biashitfrac', % ------- CASE UPDATE_BIASHITFRAC ------------- + if length(varargin)>0, LocalLeftProb.value = varargin{1}; end; + if length(varargin)>1, LocalHitHistory.value = varargin{2}; end; + if length(varargin)>2, LocalPrevSides.value = varargin{3}; end; + % Protect against somebody passing in SPHs, not actual values, by mistake: + if isa(value(LocalLeftProb), 'SoloParamHandle'), LocalLeftProb.value = value(value(LocalLeftProb)); end; + if isa(value(LocalHitHistory), 'SoloParamHandle'), LocalHitHistory.value = value(value(LocalHitHistory)); end; + if isa(value(LocalPrevSides), 'SoloParamHandle'), LocalPrevSides.value = value(value(LocalPrevSides)); end; + + LeftProb = value(LocalLeftProb); + hit_history = value(LocalHitHistory); hit_history = colvec(hit_history); + previous_sides = value(LocalPrevSides); + + kernel = exp(-(0:length(hit_history)-1)/BiasTau)'; + kernel = kernel(end:-1:1); + + prevs = previous_sides(1:length(hit_history))'; + ul = find(prevs == 'l'); + if isempty(ul), BiasLtHitFrac.value = 1; + else BiasLtHitFrac.value = sum(hit_history(ul) .* kernel(ul))/sum(kernel(ul)); + end; + + ur = find(prevs == 'r'); + if isempty(ur), BiasRtHitFrac.value = 1; + else BiasRtHitFrac.value = sum(hit_history(ur) .* kernel(ur))/sum(kernel(ur)); + end; + + if isempty(ul) && ~isempty(ur), BiasLtHitFrac.value = value(BiasRtHitFrac); end; + if isempty(ur) && ~isempty(ul), BiasRtHitFrac.value = value(BiasLtHitFrac); end; + + choices = probabilistic_trial_selector([value(BiasLtHitFrac), value(BiasRtHitFrac)], ... + [LeftProb, 1-LeftProb], value(Beta)); + LtProb.value = choices(1); + RtProb.value = choices(2); + + + case 'get_posterior_probs', % ------- CASE GET_POSTERIOR_PROBS ------------- + x = [value(LtProb) ; value(RtProb)]; %#ok + + + case 'update_hitfrac', % ------- CASE UPDATE_HITFRAC ------------- + if length(varargin)>0, LocalHitHistory.value = varargin{1}; end; + if length(varargin)>1, LocalPrevSides.value = varargin{2}; end; + % Protect against somebody passing in SPHs, not actual values, by mistake: + if isa(value(LocalHitHistory), 'SoloParamHandle'), LocalHitHistory.value = value(value(LocalHitHistory)); end; + if isa(value(LocalPrevSides), 'SoloParamHandle'), LocalPrevSides.value = value(value(LocalPrevSides)); end; + + hit_history = value(LocalHitHistory); hit_history = colvec(hit_history); + previous_sides = value(LocalPrevSides); + + + if length(hit_history)>0, + kernel = exp(-(0:length(hit_history)-1)/HitFracTau)'; + kernel = kernel(end:-1:1); + HitFrac.value = sum(hit_history .* kernel)/sum(kernel); + + prevs = previous_sides(1:length(hit_history))'; + u = find(prevs == 'l'); + if isempty(u), LtHitFrac.value = NaN; + else LtHitFrac.value = sum(hit_history(u) .* kernel(u))/sum(kernel(u)); + end; + + u = find(prevs == 'r'); + if isempty(u), RtHitFrac.value = NaN; + else RtHitFrac.value = sum(hit_history(u) .* kernel(u))/sum(kernel(u)); + end; + end; + + + case 'get', % ------- CASE GET ------------- + if length(varargin)~=1, + error('AntibiasSection:Invalid', '''get'' needs one extra param'); + end; + switch varargin{1}, + case 'Beta', + x = value(Beta); + case 'antibias_tau', + x = value(BiasTau); + otherwise + error('AntibiasSection:Invalid', 'Don''t know how to get %s', varargin{1}); + end; + + + case 'reinit', % ------- CASE REINIT ------------- + currfig = double(gcf); + + % Get the original GUI position and figure: + x = my_gui_info(1); y = my_gui_info(2); figure(my_gui_info(3)); + + % Delete all SoloParamHandles who belong to this object and whose + % fullname starts with the name of this mfile: + delete_sphandle('owner', ['^@' class(obj) '$'], ... + 'fullname', ['^' mfilename]); + + % Reinitialise at the original GUI position and figure: + [x, y] = feval(mfilename, obj, 'init', x, y); + + % Restore the current figure: + figure(currfig); +end; + + + + +function [x] = colvec(x) + if size(x,2) > size(x,1), x = x'; end; + \ No newline at end of file diff --git a/Protocols/@SoundCatContinuous/AthenaSMA_aa.m b/Protocols/@SoundCatContinuous/AthenaSMA_aa.m new file mode 100644 index 00000000..4e6f72e3 --- /dev/null +++ b/Protocols/@SoundCatContinuous/AthenaSMA_aa.m @@ -0,0 +1,392 @@ + +function [varargout] = AthenaSMA(obj, action) + +GetSoloFunctionArgs; + + +switch action + + case 'init' + + srate=SoundManagerSection(obj,'get_sample_rate'); + freq1=5; + dur1=1.5*1000; + Vol=1; + tw=Vol*(MakeBupperSwoop(srate,0, freq1 , freq1 , dur1/2 , dur1/2,0,0.1)); + SoundManagerSection(obj, 'declare_new_sound', 'LRewardSound', [tw ; zeros(1, length(tw))]) + SoundManagerSection(obj, 'declare_new_sound', 'RRewardSound', [zeros(1, length(tw));tw]) + SoundManagerSection(obj, 'send_not_yet_uploaded_sounds'); + + case 'prepare_next_trial', + + %% Setup water + min_time= 2.5E-4; % This is less than the minumum time allowed for a state transition. + + left1led = bSettings('get', 'DIOLINES', 'left1led'); + center1led = bSettings('get', 'DIOLINES', 'center1led'); + right1led = bSettings('get', 'DIOLINES', 'right1led'); + left1water = bSettings('get', 'DIOLINES', 'left1water'); + right1water = bSettings('get', 'DIOLINES', 'right1water'); + + + %% Setup sounds + sone_sound_id = SoundManagerSection(obj, 'get_sound_id', 'SOneSound'); + stwo_sound_id = SoundManagerSection(obj, 'get_sound_id', 'STwoSound'); + go_sound_id = SoundManagerSection(obj, 'get_sound_id', 'GoSound'); + go_cue_duration = SoundManagerSection(obj, 'get_sound_duration', 'GoSound'); + RLreward_sound_id = SoundManagerSection(obj, 'get_sound_id', 'RewardSound'); + err_sound_id = SoundManagerSection(obj, 'get_sound_id', 'ErrorSound'); + viol_sound_id = SoundManagerSection(obj, 'get_sound_id', 'ViolationSound'); + viol_snd_duration = SoundManagerSection(obj, 'get_sound_duration', 'ViolationSound'); + to_sound_id = SoundManagerSection(obj, 'get_sound_id', 'TimeoutSound'); + timeout_duration = SoundManagerSection(obj, 'get_sound_duration', 'TimeoutSound'); + Lreward_sound_id = SoundManagerSection(obj, 'get_sound_id', 'LRewardSound'); + Rreward_sound_id = SoundManagerSection(obj, 'get_sound_id', 'RRewardSound'); + + A1_sound_id = SoundManagerSection(obj, 'get_sound_id', 'StimAUD1'); + A2_sound_id = SoundManagerSection(obj, 'get_sound_id', 'StimAUD2'); + + %% Declare variables + % These will get moved to other functions as SoloParamHandles. + + [LeftWMult RightWMult] = SideSection(obj, 'get_water_mult'); + [LeftWValveTime RightWValveTime] = WaterValvesSection(obj, 'get_water_times'); + LeftWValveTime=LeftWValveTime*LeftWMult; + RightWValveTime=RightWValveTime*RightWMult; + + + side = SideSection(obj, 'get_current_side'); + if side == 'l' + HitEvent = 'Lin'; HitState = 'LeftHit'; WaterTime = LeftWValveTime; WaterValve = left1water; SideLight = left1led; + RightWValveTime=0; correct_response='Lin'; error_response='Rin'; reward_sound_id=Lreward_sound_id; first_wrong='Rin'; + + else + HitEvent = 'Rin'; HitState = 'RightHit'; WaterTime = RightWValveTime; WaterValve = right1water; SideLight = right1led; + LeftWValveTime=0; correct_response='Rin'; error_response='Lin'; reward_sound_id=Rreward_sound_id; first_wrong='Lin'; + end; + +% +% maxasymp=38; +% slp=3; +% inflp=300; +% minasymp=-20; +% assym=0.7; +% WaterTime=maxasymp + (minasymp./(1+(n_done_trials/inflp).^slp).^assym); + + sma = StateMachineAssembler('full_trial_structure','use_happenings', 1); + + sma = add_scheduled_wave(sma, 'name', 'reward_delivery', 'preamble', reward_delay, ... + 'sustain', WaterTime, 'DOut', WaterValve); + sma = add_scheduled_wave(sma, 'name', 'center_poke', 'preamble', CP_duration, ... + 'sustain', go_cue_duration, 'sound_trig', go_sound_id); + + sma = add_scheduled_wave(sma, 'name', 'settling_period', 'preamble', SettlingIn_time); + + + sma = add_scheduled_wave(sma, 'name', 'stimA1', 'preamble', PreStim_time, ... + 'sustain', A1_time, 'sound_trig', A1_sound_id); + + sma = add_scheduled_wave(sma, 'name', 'stimA2', 'preamble', PreStim_time+A1_time+Del_time, ... + 'sustain', A2_time,'sound_trig', A2_sound_id); + + switch value(training_stage) + + case 0 %% learning the reward sound association -left or right led on -> poke -> sound+reward + sma = add_state(sma, 'name', 'sideled_on', 'self_timer', SideLed_duration, ... + 'output_actions', {'DOut', SideLight}, ... + 'input_to_statechange',{'Tup','wait_for_collecting_reward'}); + + sma = add_state(sma, 'name', 'wait_for_collecting_reward', 'self_timer', RewardCollection_duration, ... + 'output_actions', {'DOut', SideLight}, ... + 'input_to_statechange',{HitEvent,'hit_state','Tup','wait_for_collecting_reward',first_wrong,'second_hit_state'}); + + sma = add_state(sma,'name','second_hit_state','self_timer',RewardCollection_duration,... + 'output_actions',{'DOut', SideLight},... + 'input_to_statechange',{'Tup','second_hit_state',HitEvent,'hit_state'}); + + sma = add_state(sma,'name','hit_state','self_timer',0.01,... + 'output_actions', {'DOut', SideLight,'SchedWaveTrig','reward_delivery','SoundOut',reward_sound_id},... + 'input_to_statechange',{'Tup','drink_state'}); + + + case 1 %% center led on -> poke in the center -> go cue -> reward light and sound -- waiting time grows slowlly + sma = add_state(sma,'name','wait_for_cpoke','self_timer',CenterLed_duration, ... + 'output_actions', {'DOut', center1led}, ... + 'input_to_statechange', {'Cin','cp';'Tup','timeout_state'}); + + % center poke starts: trigger center_poke scheduled wave, + % and when that ends go to side_led_on + sma = add_state(sma,'name','cp','self_timer', SettlingIn_time, ... + 'output_actions', {'SchedWaveTrig', 'center_poke + settling_period'}, ... + 'input_to_statechange', {'Tup', 'cp_legal_cbreak_period', ... + 'Cout','current_state+1', ... + 'center_poke_Out', 'sideled_on', ... + 'Rin', 'violation_state', ... + 'Rout', 'violation_state', ... + 'Lin', 'violation_state', ... + 'Lout', 'violation_state'}); + + + % nose is out and we're in "SettlingIn_time": + % if settling_legal_cbreak time elapses, go to violation state, + % if nose is put back in, go to copy of cp start + % when SettlingIn_time elapses (settling_period_In) "legal cbreaks" changes to usueal legal_cbreaks + sma = add_state(sma, 'self_timer', settling_legal_cbreak+0.00001, ... + 'output_actions', {'DOut', center1led*LED_during_settling_legal_cbreak}, ... + 'input_to_statechange', {'Tup', 'violation_state', ... + 'Cin', 'current_state+1', ... + 'settling_period_In', 'cp_legal_cbreak_period', ... + 'center_poke_Out', 'sideled_on', ... + 'Rin', 'violation_state', ... + 'Rout', 'violation_state', ... + 'Lin', 'violation_state', ... + 'Lout', 'violation_state'}); + + % center poke: + % A copy of two states above, but without triggering the + % start of the center_poke scheduled wave. + sma = add_state(sma, 'self_timer', 10000, ... + 'input_to_statechange', {'Cout', 'current_state-1', ... + 'settling_period_In','cp_legal_cbreak_period', ... + 'center_poke_Out', 'sideled_on', ... + 'Rin', 'violation_state', ... + 'Rout', 'violation_state', ... + 'Lin', 'violation_state', ... + 'Lout', 'violation_state'}); + + % SettlingIn_time elapsed and from now on cbreaks are treated given legal_cbreaks + sma = add_state(sma,'name','cp_legal_cbreak_period', 'self_timer', 10000, ... + 'input_to_statechange', {'Cout', 'current_state+1', ... + 'Clo', 'current_state+1', ... + 'center_poke_Out', 'sideled_on', ... + 'Rin', 'violation_state', ... + 'Rout', 'violation_state', ... + 'Lin', 'violation_state', ... + 'Lout', 'violation_state'}); + + % nose is out and we're still in legal_cbreak: + % if legal_cbreak time elapses, go to violation_state, + % if nose is put back in, go to copy of cp start + sma = add_state(sma, 'self_timer', legal_cbreak+0.00001, ... + 'output_actions', {'DOut', center1led*LED_during_legal_cbreak}, ... + 'input_to_statechange', {'Tup', 'violation_state', ... + 'Cin', 'current_state+1', ... + 'center_poke_Out', 'sideled_on', ... + 'Rin', 'violation_state', ... + 'Rout', 'violation_state', ... + 'Lin', 'violation_state', ... + 'Lout', 'violation_state'}); + + % center poke: + % A copy of two states above, but without triggering the + % start of the center_poke scheduled wave. + sma = add_state(sma, 'self_timer', 10000, ... + 'input_to_statechange', {'Cout', 'current_state-1', ... + 'center_poke_Out', 'sideled_on', ... + 'Rin', 'violation_state', ... + 'Rout', 'violation_state', ... + 'Lin', 'violation_state', ... + 'Lout', 'violation_state'}); + + sma = add_state(sma, 'name', 'sideled_on', 'self_timer', SideLed_duration, ... + 'output_actions', {'DOut', SideLight}, ... + 'input_to_statechange',{'Tup','wait_for_collecting_reward'}); + + sma = add_state(sma, 'name', 'wait_for_collecting_reward', 'self_timer', RewardCollection_duration, ... + 'output_actions', {'DOut', SideLight}, ... + 'input_to_statechange',{HitEvent,'hit_state','Tup','wait_for_collecting_reward',first_wrong,'second_hit_state'}); + + sma = add_state(sma,'name','second_hit_state','self_timer',RewardCollection_duration,... + 'output_actions',{'DOut', SideLight},... + 'input_to_statechange',{'Tup','second_hit_state',HitEvent,'hit_state'}); + + sma = add_state(sma,'name','hit_state','self_timer',0.01,... + 'output_actions', {'DOut', SideLight,'SchedWaveTrig','reward_delivery','SoundOut',reward_sound_id},... + 'input_to_statechange',{'Tup','drink_state'}); + + case 2 % like stage 1 - now passive exposure to the stimuli - reward comes anyway + sma = add_state(sma,'name','wait_for_cpoke','self_timer',CenterLed_duration, ... + 'output_actions', {'DOut', center1led}, ... + 'input_to_statechange', {'Cin','cp';'Tup','timeout_state'}); + + % center poke starts: trigger center_poke scheduled wave, + % and when that ends go to side_led_on + sma = add_state(sma,'name','cp','self_timer', SettlingIn_time, ... + 'output_actions', {'SchedWaveTrig', 'center_poke + settling_period+stimA1+stimA2'}, ... + 'input_to_statechange', {'Tup', 'cp_legal_cbreak_period', ... + 'Cout','current_state+1', ... + 'center_poke_Out', 'sideled_on', ... + 'Rin', 'violation_state', ... + 'Rout', 'violation_state', ... + 'Lin', 'violation_state', ... + 'Lout', 'violation_state'}); + + + % nose is out and we're in "SettlingIn_time": + % if settling_legal_cbreak time elapses, go to violation state, + % if nose is put back in, go to copy of cp start + % when SettlingIn_time elapses (settling_period_In) "legal cbreaks" changes to usueal legal_cbreaks + sma = add_state(sma, 'self_timer', settling_legal_cbreak+0.00001, ... + 'output_actions', {'DOut', center1led*LED_during_settling_legal_cbreak}, ... + 'input_to_statechange', {'Tup', 'violation_state', ... + 'Cin', 'current_state+1', ... + 'settling_period_In', 'cp_legal_cbreak_period', ... + 'center_poke_Out', 'sideled_on', ... + 'Rin', 'violation_state', ... + 'Rout', 'violation_state', ... + 'Lin', 'violation_state', ... + 'Lout', 'violation_state'}); + + % center poke: + % A copy of two states above, but without triggering the + % start of the center_poke scheduled wave. + sma = add_state(sma, 'self_timer', 10000, ... + 'input_to_statechange', {'Cout', 'current_state-1', ... + 'settling_period_In','cp_legal_cbreak_period', ... + 'center_poke_Out', 'sideled_on', ... + 'Rin', 'violation_state', ... + 'Rout', 'violation_state', ... + 'Lin', 'violation_state', ... + 'Lout', 'violation_state'}); + + % SettlingIn_time elapsed and from now on cbreaks are treated given legal_cbreaks + sma = add_state(sma,'name','cp_legal_cbreak_period', 'self_timer', 10000, ... + 'input_to_statechange', {'Cout', 'current_state+1', ... + 'Clo', 'current_state+1', ... + 'center_poke_Out', 'sideled_on', ... + 'Rin', 'violation_state', ... + 'Rout', 'violation_state', ... + 'Lin', 'violation_state', ... + 'Lout', 'violation_state'}); + + % nose is out and we're still in legal_cbreak: + % if legal_cbreak time elapses, go to violation_state, + % if nose is put back in, go to copy of cp start + sma = add_state(sma, 'self_timer', legal_cbreak+0.00001, ... + 'output_actions', {'DOut', center1led*LED_during_legal_cbreak}, ... + 'input_to_statechange', {'Tup', 'violation_state', ... + 'Cin', 'current_state+1', ... + 'center_poke_Out', 'sideled_on', ... + 'Rin', 'violation_state', ... + 'Rout', 'violation_state', ... + 'Lin', 'violation_state', ... + 'Lout', 'violation_state'}); + + % center poke: + % A copy of two states above, but without triggering the + % start of the center_poke scheduled wave. + sma = add_state(sma, 'self_timer', 10000, ... + 'input_to_statechange', {'Cout', 'current_state-1', ... + 'center_poke_Out', 'sideled_on', ... + 'Rin', 'violation_state', ... + 'Rout', 'violation_state', ... + 'Lin', 'violation_state', ... + 'Lout', 'violation_state'}); + + sma = add_state(sma, 'name', 'sideled_on', 'self_timer', SideLed_duration, ... + 'output_actions', {'DOut', SideLight}, ... + 'input_to_statechange',{'Tup','wait_for_collecting_reward'}); + + sma = add_state(sma, 'name', 'wait_for_collecting_reward', 'self_timer', RewardCollection_duration, ... + 'output_actions', {'DOut', SideLight}, ... + 'input_to_statechange',{HitEvent,'hit_state','Tup','wait_for_collecting_reward',first_wrong,'second_hit_state'}); + + sma = add_state(sma,'name','second_hit_state','self_timer',RewardCollection_duration,... + 'output_actions',{'DOut', SideLight},... + 'input_to_statechange',{'Tup','second_hit_state',HitEvent,'hit_state'}); + + sma = add_state(sma,'name','hit_state','self_timer',0.01,... + 'output_actions', {'DOut', SideLight,'SchedWaveTrig','reward_delivery','SoundOut',reward_sound_id},... + 'input_to_statechange',{'Tup','drink_state'}); + + case 3 % like stage 2, + + + case 4 %% now reward comes only if rat goes to the correct side + sma = add_state(sma, 'name', 'sideled_on', 'self_timer', SideLed_duration, ... + 'output_actions', {'DOut', SideLight}, ... + 'input_to_statechange',{correct_response, 'hit_state'; error_response,'error_state';... + 'Tup','timeout_state'}); + + sma = add_state(sma,'name','error_state','self_timer',error_iti,... + 'output_actions',{'SoundOut',err_sound_id},... + 'input_to_statechange',{'Tup','check_next_trial_ready'}); + + sma = add_state(sma,'name','hit_state','self_timer',WaterTime+0.1,... + 'output_actions',{'SchedWaveTrig','reward_delivery','SoundOut',reward_sound_id},... + 'input_to_statechange',{'Tup','drink_state'}); + + end %end of swith for different training_stages + + + sma = add_state(sma,'name','drink_state','self_timer',drink_time,... + 'input_to_statechange',{'Tup','check_next_trial_ready'}); + + sma = add_state(sma,'name','violation_state','self_timer',viol_snd_duration,... + 'output_actions',{'SchedWaveTrig', '-center_poke-stimA1-stimA2', ... + 'SoundOut',viol_sound_id, 'DOut', center1led},... + 'input_to_statechange', {'Tup', 'current_state+1'}); + sma = add_state(sma, 'self_timer', max(0.001, violation_iti-viol_snd_duration), ... + 'input_to_statechange',{'Tup','check_next_trial_ready'}); + + sma = add_state(sma,'name','timeout_state','self_timer', timeout_duration,... + 'output_actions',{'SoundOut',to_sound_id},... + 'input_to_statechange',{'Tup','check_next_trial_ready'}); + + + varargout{2} = {'check_next_trial_ready'}; + + varargout{1} = sma; + + % Not all 'prepare_next_trial_states' are defined in all training + % stages. So we send to dispatcher only those states that are + % defined. + state_names = get_labels(sma); state_names = state_names(:,1); + prepare_next_trial_states = {'hit_state','second_hit_state', 'error_state', 'violation_state','timeout_state'}; + dispatcher('send_assembler', sma, intersect(state_names, prepare_next_trial_states)); + + case 'get_state_colors', + varargout{1} = struct( ... + 'wait_for_cpoke', [0.68 1 0.63], ... + 'cp', [0.63 1 0.94], ... + 'cp_legal_cbreak_period', [0.63 1 0.94]*0.8, ... + 'sideled_on', [1 0.79 0.63], ... + 'wait_for_collecting_reward', [0.53 0.78 1.00],... + 'hit_state', [0.77 0.60 0.48], ... + 'second_hit_state', [0.25 0.45 0.48], ... + 'drink_time', [0 1 0], ... + 'drink_state', [0 1 0], ... + 'error_state', [1 0.54 0.54], ... + 'violation_state', [0.31 0.48 0.30], ... + 'timeout_state', 0.8*[0.31 0.48 0.30]); + % 'go_cue_on', [0.63 1 0.94]*0.6, ... + % 'prerw_postcs', [0.25 0.45 0.48], ... + % 'lefthit', [0.53 0.78 1.00], ... + % 'lefthit_pasound', [0.53 0.78 1.00]*0.7, ... + % 'righthit', [0.52 1.0 0.60], ... + % 'righthit_pasound', [0.52 1.0 0.60]*0.7, ... + % 'warning', [0.3 0 0], ... + % 'danger', [0.5 0.05 0.05], ... + % 'hit', [0 1 0] + + + + + case 'reinit', + currfig = double(gcf); + + % Delete all SoloParamHandles who belong to this object and whose + % fullname starts with the name of this mfile: + delete_sphandle('owner', ['^@' class(obj) '$'], ... + 'fullname', ['^' mfilename]); + + + % Reinitialise at the original GUI position and figure: + feval(mfilename, obj, 'init'); + + % Restore the current figure: + figure(currfig); + + otherwise + warning('do not know how to do %s',action); +end \ No newline at end of file diff --git a/Protocols/@SoundCatContinuous/DelayComp.m b/Protocols/@SoundCatContinuous/DelayComp.m new file mode 100644 index 00000000..e4dd2ffe --- /dev/null +++ b/Protocols/@SoundCatContinuous/DelayComp.m @@ -0,0 +1,62 @@ + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% +% ------------ STAGE SEPARATOR ------- (do not edit this line) +% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% --- STAGE NAME: --- (do not edit this line) +Growing nose in center time + +% --- VAR NAMES: --- (do not edit this line) +% Maximum duration of center poke, in secs: +cp_max 5 forceinit=1 +% Maximum value of CP_duration reached during the last session +cp_pre 2 +% Fractional increment in center poke duration every time there is a non-cp-violation trial: +cp_fraction 0.002 +% Minimum increment (in secs) in center poke duration every time there is a non-cp-violation trial: +cp_minimum_increment 0.001 + +% Minimum CP_duration at which a settling_in legal_cbreak is different to +% the regular legal_cbreak: +start_settling_in_at 0.5 +% Once we've reached CP_duration > start_settling_in_at, the parameters of +% the settling in: +settling_in_time 0.25 +settling_in_legal_cbreak 0.2 + +% --- TRAINING STRING: --- (do not edit this line) +if n_done_trials ==1 + cp_pre.value=value(SideSection_CP_duration); + SideSection_CP_duration.value = 0.5; +elseif n_done_trials > 1 && n_done_trials <20 ... + && ~violation_history(end) && SideSection_CP_duration < cp_max + slope = (value(cp_pre)-0.5)/19; + SideSection_CP_duration.value = n_done_trials*slope + 0.5; + end +elseif n_done_trials>20 + if ~violation_history(end) && SideSection_CP_duration < cp_max, + increment = SideSection_CP_duration*cp_fraction; + if increment < cp_minimum_increment, + increment = value(cp_minimum_increment); + end; + SideSection_CP_duration.value = SideSection_CP_duration + increment; + end; + + if SideSection_CP_duration >= start_settling_in_at + SideSection_SettlingIn_time.value = value(settling_in_time); + SideSection_settling_legal_cbreak.value = value(settling_in_legal_cbreak); + else + SideSection_SettlingIn_time.value = 0; + SideSection_settling_legal_cbreak.value = value(SideSection_legal_cbreak); + end; +end + +% --- END-OF-DAY LOGIC: -- (do not edit this line) + + + +% --- COMPLETION STRING: --- (do not edit this line) +0 + + diff --git a/Protocols/@SoundCatContinuous/DelayComp1.m b/Protocols/@SoundCatContinuous/DelayComp1.m new file mode 100644 index 00000000..6ad3acd2 --- /dev/null +++ b/Protocols/@SoundCatContinuous/DelayComp1.m @@ -0,0 +1,124 @@ + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% +% ------------ STAGE SEPARATOR ------- (do not edit this line) +% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% --- STAGE NAME: --- (do not edit this line) +Growing nose in center time + +% --- VAR NAMES: --- (do not edit this line) +% Maximum duration of center poke (including Go cue), in secs: +max_total_cp 6.2 forceinit=1 +% Standard Go cue duration +target_go_cue_duration 0.2 +% CP_duration reached at the end of the last session +last_session_total_cp 0 +% Fractional increment in center poke duration every time there is a non-cp-violation trial: +cp_fraction 0.001 +% Minimum increment (in secs) in center poke duration every time there is a non-cp-violation trial: +cp_minimum_increment 0.001 + + +% Initial startup trials over which to gradually grow cp duration +% Only do the initial startup stuff if cp_duration is longer than: +cp_duration_threshold_for_initial_trials 1.5 +% Number of initial trials over which to gradually grow cp duration: +n_initial_trials 10 +% Starting total center poke duration: +starting_total_cp 0.5 + + +% Minimum CP_duration at which a settling_in legal_cbreak is different to +% the regular legal_cbreak: +start_settling_in_at 1000 +% Once we've reached CP_duration > start_settling_in_at, the parameters of +% the settling in: +settling_in_time 0 +settling_in_legal_cbreak 0.05 + + + + +% --- TRAINING STRING: --- (do not edit this line) + +% Check to see whether we're doing the initial startup stuff. Assumes that +% we're NOT changing the Go cue duration. +if SideSection_Total_CP_duration+0.0001 < last_session_total_cp & ... + last_session_total_cp > cp_duration_threshold_for_initial_trials, + % Check to see whether we're done with initial stuff within numerical + % rounding error: + if abs(last_session_total_cp - SideSection_Total_CP_duration) < 0.0001, + SideSection_CP_duration.value = last_session_total_cp - SideSection_time_go_cue; + elseif ~violation_history(end), + increment = ... + (last_session_total_cp - starting_total_cp)/(n_initial_trials - 1); + SideSection_CP_duration.value = SideSection_CP_duration + increment; + end; +elseif ~violation_history(end) && SideSection_Total_CP_duration < max_total_cp, + % We're in regular increasing territory + increment = SideSection_Total_CP_duration*cp_fraction; + if increment < cp_minimum_increment, + increment = value(cp_minimum_increment); + end; + % If we're growing the CP duration, grow the Go cue duration first + % until it reaches its target; after that, grow CP_duration, the + % pre-Go cue time. + if SideSection_time_go_cue < target_go_cue_duration, + SideSection_time_go_cue.value = SideSection_time_go_cue + increment; + else + SideSection_CP_duration.value = SideSection_CP_duration + increment; + end; +end; +% make sure the total reflects all the changes: +callback(SideSection_CP_duration); + +% Double-check that we don't go over the desired max value: +if SideSection_Total_CP_duration > max_total_cp, + SideSection_CP_duration.value = max_total_cp - SideSection_time_go_cue; + % once again, make sure the total reflects any the changes: + callback(SideSection_CP_duration); +end; + +% Settling in code: +if SideSection_CP_duration >= start_settling_in_at + SideSection_SettlingIn_time.value = value(settling_in_time); + SideSection_settling_legal_cbreak.value = value(settling_in_legal_cbreak); +else + SideSection_SettlingIn_time.value = 0; + SideSection_settling_legal_cbreak.value = value(SideSection_legal_cbreak); +end; + + + + +% --- END-OF-DAY LOGIC: -- (do not edit this line) + +% Store the value of the total cp duration reached: +last_session_total_cp.value = value(SideSection_Total_CP_duration); + +% Check whether we're going to do the initial startup trials on the next +% day: +if SideSection_Total_CP_duration > cp_duration_threshold_for_initial_trials, + % Yup, doing initial startup. Set CP_duration to the necessary duration: + SideSection_CP_duration.value = starting_total_cp - SideSection_time_go_cue; + % Error check, make sure we don't set it to something nonsensical: + if SideSection_CP_duration < 0.001, + SideSection_CP_duration = 0.001; + end; + % Callback to make sure the calculation of Total_CP_duration is made. + callback(SideSection_CP_duration); +end; + + + + + + +% --- COMPLETION STRING: --- (do not edit this line) +0 + + + + + diff --git a/Protocols/@SoundCatContinuous/LOGplotPairs.m b/Protocols/@SoundCatContinuous/LOGplotPairs.m new file mode 100644 index 00000000..275f736e --- /dev/null +++ b/Protocols/@SoundCatContinuous/LOGplotPairs.m @@ -0,0 +1,71 @@ +function LOGplotPairs(x,y,marker,markersize,markeredgecolor,thislinewidth,FONTSIZE) + + +% delete(gca) +% load('NEWHOT','HOTETOBOKHORAM') + +% position = [16 124 1019 761]; +% % +% figure('Position',position); + +% if nargin <8 +% LogOrLin='linear'; +% end + +if nargin <4 + marker='.'; +end + + +% Plot the points +hold on +x=log(x); +y=log(y); +for i=1:length(x) + +% loglog(x(i),y(i),marker,'color',map(in,:),'markerfacecolor',map(in,:),'MarkerSize',markersize) +% plot3(x(i),y(i),v(i),marker,'MarkerSize',markersize,'MarkerEdgeColor',markeredgecolor,'LineWidth',thislinewidth) + plot(x(i),y(i),marker,'MarkerSize',markersize,'MarkerEdgeColor',markeredgecolor,'LineWidth',thislinewidth) + +end + +xlim([min([x ;y])-min([x ;y])/2 max([x ;y])+min([x ;y])/2]) +ylim([min([x; y])-min([x ;y])/2 max([x; y])+-min([x ;y])/2]) +hold off + +% figure('Position',[1 scrsz(4)/2 scrsz(3)/2 scrsz(4)/2]) + +Ylabel('log_e \sigma_2','FontSize',FONTSIZE,'FontName','Cambria Math'); +set(double(gca),'Fontsize',15) +Xlabel('log_e \sigma_1','FontSize',FONTSIZE,'FontName','Cambria Math') + +% grid on +setyticklabels=1 + +if setyticklabels==1 + +Ytick=get(double(gca),'YtickLabel'); +Xtick=get(double(gca),'XtickLabel'); +% +% Ytick=num2str((3:0.5:6)'); +% Xtick=num2str((3:0.5:6)'); + +% set(gca,'ytick',[],'xtick',[]); +end +% +% axis square +% HUMANORRAT=2 +% if HUMANORRAT==2 +% ylim([2.5 6]) +% xlim([2.2 6.3]) +% else +% ylim([3.5 6]) +% xlim([3.5 6.5]) +% end +if setyticklabels==1 +set(double(gca),'ytick',str2num(Ytick),'xtick',str2num(Xtick)); +set(double(gca),'yticklabel',num2str(round(round(exp(str2num(Ytick)).*100)./100)),'xticklabel',num2str(round(round(exp(str2num(Xtick)).*100)./100))); +end + +% set(gca,'yticklabel',num2str(round(exp(str2num(Ytick)).*100)./100),'xticklabel',num2str(round(exp(str2num(Xtick)).*100)./100)); +view(2) diff --git a/Protocols/@SoundCatContinuous/OverallPerformanceSection.m b/Protocols/@SoundCatContinuous/OverallPerformanceSection.m new file mode 100644 index 00000000..b377fc98 --- /dev/null +++ b/Protocols/@SoundCatContinuous/OverallPerformanceSection.m @@ -0,0 +1,127 @@ +% [x, y] = OverallPerformanceSection(obj, action, x,y) +% +% Reports overall performance. Uses training_stage from SideSection. +% +% PARAMETERS: +% ----------- +% +% action One of: +% 'init' To initialise the section and set up the GUI +% for it +% +% 'close' Delete all of this section's GUIs and data +% +% 'reinit' Delete all of this section's GUIs and data, +% and reinit, at the same position on the same +% figure as the original section GUI was placed. +% +% 'evalueta' Look at history and compute hit fraction, etc. +% +% x, y Only relevant to action = 'init'; they indicate the initial +% position to place the GUI at, in the current figure window +% +% RETURNS: +% -------- +% +% perf When action == 'init', returns x and y, pixel positions on +% the current figure, updated after placing of this section's GUI. +% When action == 'evaluate', returns a vector with elements +% [ntrials, violation_rate, left_hit_frac, right_hit_frac, hit_frac] +% +% + +% CDB, 23-March-2013 + +function [x, y] = OverallPerformanceSection(obj, action, x,y) + +GetSoloFunctionArgs(obj); + +switch action, + + % ------------------------------------------------------------------ + % INIT + % ------------------------------------------------------------------ + + case 'init' + + SoloParamHandle(obj, 'my_gui_info', 'value', [x y double(gcf)], 'saveable', 0); + + DispParam(obj, 'ntrials', 0, x, y); next_row(y); + DispParam(obj, 'violation_rate', 0, x, y, 'TooltipString', ... + 'Fraction of trials with a center poke violation'); next_row(y); + DispParam(obj, 'timeout_rate', 0, x, y, 'TooltipString', ... + 'Fraction of trials with timeout'); next_row(y); + DispParam(obj, 'Left_hit_frac', 0, x, y, 'TooltipString', ... + 'Fraction of correct Left trials'); next_row(y); + DispParam(obj, 'Right_hit_frac', 0, x, y, 'TooltipString', ... + 'Fraction of correct Right trials'); next_row(y); + DispParam(obj, 'hit_frac', 0, x, y, 'TooltipString', ... + 'Fraction of correct trials'); next_row(y); + + SubheaderParam(obj, 'title', 'Overall Performance', x, y); + next_row(y, 1.5); + SoloParamHandle(obj, 'previous_parameters', 'value', []); + + % ------------------------------------------------------------------ + % evaluate + % ------------------------------------------------------------------ + + case 'evaluate' + + switch value(training_stage) + case 1, %% center led on -> poke in the center -> go cue -> reward light and sound + if n_done_trials > 1, + ntrials.value = n_done_trials; + %violation_rate.value = numel(find(isnan(hit_history)))/n_done_trials; + violation_rate.value = numel(find(violation_history))/n_done_trials; + timeout_rate.value = numel(find(timeout_history))/n_done_trials; + goods = ~isnan(hit_history)'; + lefts = previous_sides(1:n_done_trials)=='l'; + rights = previous_sides(1:n_done_trials)=='r'; + Left_hit_frac.value = mean(hit_history(goods & lefts)); + Right_hit_frac.value = mean(hit_history(goods & rights)); + hit_frac.value = mean(hit_history(goods)); + end; + end + + if nargout > 0, + x = [n_done_trials, value(violation_rate), value(timeout_rate), value(Left_hit_frac), ... + value(Right_hit_frac), value(hit_frac)]; + end; + + + % ------------------------------------------------------------------ + % close + % ------------------------------------------------------------------ + + case 'close', + % Delete all SoloParamHandles who belong to this object and whose + % fullname starts with the name of this mfile: + delete_sphandle('owner', ['^@' class(obj) '$'], ... + 'fullname', ['^' mfilename]); + + + % ------------------------------------------------------------------ + % reinit + % ------------------------------------------------------------------ + + case 'reinit', + currfig = double(gcf); + + % Get the original GUI position and figure: + x = my_gui_info(1); y = my_gui_info(2); figure(my_gui_info(3)); + + % Delete all SoloParamHandles who belong to this object and whose + % fullname starts with the name of this mfile: + delete_sphandle('owner', ['^@' class(obj) '$'], ... + 'fullname', ['^' mfilename]); + + % Reinitialise at the original GUI position and figure: + [x, y] = feval(mfilename, obj, 'init', x, y); + + % Restore the current figure: + figure(currfig); + +end + + diff --git a/Protocols/@SoundCatContinuous/PlayStimuli.m b/Protocols/@SoundCatContinuous/PlayStimuli.m new file mode 100644 index 00000000..52ca7c8f --- /dev/null +++ b/Protocols/@SoundCatContinuous/PlayStimuli.m @@ -0,0 +1,87 @@ + +function [x, y] = PlayStimuli(obj, action, varargin) +GetSoloFunctionArgs(obj); + +switch action, + + case 'init' + + if length(varargin) < 2, + error('Need at least two arguments, x and y position, to initialize %s', mfilename); + end; + x = varargin{1}; y = varargin{2}; + + ToggleParam(obj, 'StimuliPlayShow', 0, x, y, 'OnString', 'StimToPlay', ... + 'OffString', 'StimToPlay', 'TooltipString', 'Show/Hide Sounds panel'); + set_callback(StimuliPlayShow, {mfilename, 'show_hide'}); %#ok (Defined just above) + next_row(y); + oldx=x; oldy=y; parentfig=double(gcf); + + SoloParamHandle(obj, 'myfig', 'value', figure('Position', [100 100 560 440], ... + 'closerequestfcn', [mfilename '(' class(obj) ', ''hide'');'], 'MenuBar', 'none', ... + 'Name', mfilename), 'saveable', 0); + set(double(gcf), 'Visible', 'off'); + x=10;y=10; + + MenuParam(obj, 'filter_type', {'GAUS','LPFIR', 'FIRLS','BUTTER','MOVAVRG','KAISER','EQUIRIP','HAMMING'}, ... + 'GAUS', x, y, 'labelfraction', 0.35, 'TooltipString', sprintf(['\nDifferent filters. ''LPFIR'': lowpass FIR ''FIRLS'': Least square linear-phase FIR filter design\n', ... + '\n''BUTTER'': IIR Butterworth lowpass filter ''GAUS'': Gaussian filter (window)\n', ... + '\n''MOVAVRG'': Moving average FIR filter ''KAISER'': Kaiser-window FIR filtering\n', ... + '\n''EQUIRIP'':Eqiripple FIR filter ''HAMMING'': Hamming-window based FIR'])); + next_row(y, 1.3) + NumeditParam(obj, 'A1_sigma', 0.01, x,y,'label','A1_sigma','TooltipString','Sigma value for the stimulus'); + next_row(y); + NumeditParam(obj,'fcut',110,x,y,'label','fcut','TooltipString','Cut off frequency on the original white noise'); + next_row(y); + NumeditParam(obj,'lfreq',2000,x,y,'label','Modulator_LowFreq','TooltipString','Lower bound for the frequency modulator'); + next_row(y); + NumeditParam(obj,'hfreq',20000,x,y,'label','Modulator_HighFreq','TooltipString','Upper bound for the frequency modulator'); + next_row(y); + NumeditParam(obj,'dur',0.5,x,y,'label','dur','TooltipString','duration of stimulus in ms'); + next_row(y); + + sname='GaussNoise'; + + srate=SoundManagerSection(obj,'get_sample_rate'); + Fs=srate; + replace=1; + T=dur; + L=floor(T*Fs); % Length of signal + sigma_1=1; + + pos1 = sigma_1*randn(Fs,1); + base = randsample(pos1,L,replace); + filtbase=filt(base,fcut,Fs,value(filter_type)); + normbase=filtbase./(max(abs(filtbase))); + + mod1 = sigma_1*randn(Fs,1); + mod1 = randsample(mod1,L,replace); + hf = design(fdesign.bandpass('N,F3dB1,F3dB2',10,value(lfreq),value(hfreq),Fs)); + filtmod=filter(hf,mod1); + modulator=filtbase./(max(abs(filtmod))); + + AUD1=normbase(1:dur*srate).*modulator(1:dur*srate).*A1_sigma; + w=[AUD1'; AUD1']; + SoundManagerSection(obj, 'declare_new_sound', sname); + SoundManagerSection(obj, 'set_sound',sname,w) + + SubheaderParam(obj, [sname 'Head'], sname, x,y,'TooltipString',''); + PushbuttonParam(obj, [sname 'Play'], x,y, 'label', 'Play', 'position', [x y 30 20]); + set_callback(eval([sname 'Play']),{'SoundManagerSection', 'play_sound', sname}); + PushbuttonParam(obj, [sname 'Stop'], x,y, 'label', 'Stop', 'position', [x+30 y 30 20]); + set_callback(eval([sname 'Stop']),{'SoundManagerSection', 'stop_sound', sname}); + + x=oldx; y=oldy; + figure(parentfig); + + case 'hide', + StimuliPlayShow.value = 0; set(value(myfig), 'Visible', 'off'); + + case 'show', + StimuliPlayShow.value = 1; set(value(myfig), 'Visible', 'on'); + + case 'show_hide', + if StimuliPlayShow == 1, set(value(myfig), 'Visible', 'on'); %#ok (defined by GetSoloFunctionArgs) + else set(value(myfig), 'Visible', 'off'); + end; +end diff --git a/Protocols/@SoundCatContinuous/ProduceNoiseStimuli.m b/Protocols/@SoundCatContinuous/ProduceNoiseStimuli.m new file mode 100644 index 00000000..262d6a6d --- /dev/null +++ b/Protocols/@SoundCatContinuous/ProduceNoiseStimuli.m @@ -0,0 +1,33 @@ +clear all + +T=1000; +fcut=110; +Fs=10000; +filter_type='GAUS'; +outband=55; +Ind=[0.4]; + +minS1_d=1; +maxS1_d=30; +numClass=8; + +S1_d(1)=minS1_d; +S2_d(1)=S1_d(1)*(1-Ind)/(1+Ind); +S1_u(1)=S1_d; +S2_u(1)=S1_u(1)*(1+Ind)/(1-(Ind)); +for ii=2:numClass +S1_d(ii)=S2_u(ii-1); +S2_d(ii)=S1_d(ii)*(1-Ind)/(1+Ind); +S1_u(ii)=S1_d(ii); +S2_u(ii)=S1_u(ii)*(1+Ind)/(1-Ind); +end + +[rawA rawB filtA filtB]=noise(Sigma1(ss),Sigma2(ss),T,fcut,Fs,filter_type,outband); + + +pairs=[]; +pairs(:,1)=[S1_d S1_u]; +pairs(:,2)=[S2_d S2_u]; +thesepairs=pairs(2:end-1,:) +LOGplotPairs(thesepairs(:,1),thesepairs(:,2),'s',18,'k',1,16) + diff --git a/Protocols/@SoundCatContinuous/PunishmentSection.m b/Protocols/@SoundCatContinuous/PunishmentSection.m new file mode 100644 index 00000000..5289350e --- /dev/null +++ b/Protocols/@SoundCatContinuous/PunishmentSection.m @@ -0,0 +1,133 @@ +% Typical section code-- this file may be used as a template to be added +% on to. The code below stores the current figure and initial position when +% the action is 'init'; and, upon 'reinit', deletes all SoloParamHandles +% belonging to this section, then calls 'init' at the proper GUI position +% again. + + +% [x, y] = YOUR_SECTION_NAME(obj, action, x, y) +% +% Section that takes care of YOUR HELP DESCRIPTION +% +% PARAMETERS: +% ----------- +% +% obj Default object argument. +% +% action One of: +% 'init' To initialise the section and set up the GUI +% for it +% +% 'reinit' Delete all of this section's GUIs and data, +% and reinit, at the same position on the same +% figure as the original section GUI was placed. +% +% 'prepare_next_trial' Goes through the processing necessary +% to compute what the next trial's correct side +% should be. +% +% 'get_current_side' Returns either the string 'l' or the +% string 'r', for which side is the current trial's +% correcy side. +% +% +% x, y Relevant to action = 'init'; they indicate the initial +% position to place the GUI at, in the current figure window +% +% RETURNS: +% -------- +% +% [x, y] When action == 'init', returns x and y, pixel positions on +% the current figure, updated after placing of this section's GUI. +% +% +% x When action == 'get_current_side', returns either the string 'l' +% or the string 'r', for Left and Right, respectively. +% + + +function [x, y] = PunishmentSection(obj, action, x, y) + +GetSoloFunctionArgs(obj); + +switch action + case 'init', + % Save the figure and the position in the figure where we are + % going to start adding GUI elements: + SoloParamHandle(obj, 'my_gui_info', 'value', [x y double(gcf)], 'saveable', 0); + + NumeditParam(obj, 'DrinkTime', 20, x, y, 'TooltipString', sprintf('\nTime over which drinking is ok')); next_row(y); + ToggleParam(obj, 'WarningSoundPanel', 1, x, y, 'OnString', 'warn show', 'OffString', 'warn hide', 'position', [x y 80 20]); + NumeditParam(obj, 'WarnDur', 4, x, y, 'labelfraction', 0.6, 'TooltipString', 'Warning sound duration in secs', 'position', [x+80 y 60 20]); + NumeditParam(obj, 'DangerDur',15, x, y, 'labelfraction', 0.6, 'TooltipString', sprintf('\nDuration of post-drink period where poking is punished'), 'position', [x+140 y 60 20]); next_row(y); + set_callback(WarningSoundPanel, {mfilename, 'WarningSoundPanel'}); + % start subpanel + oldx = x; oldy = y; oldfigure = double(gcf); + SoloParamHandle(obj, 'WarningSoundPanelFigure', 'saveable', 0, 'value', figure('Position', [120 120 430 156])); + sfig = value(WarningSoundPanelFigure); + set(sfig, 'MenuBar', 'none', 'NumberTitle', 'off', ... + 'Name', 'Warning sound', 'CloseRequestFcn', 'Classical(classical, ''closeWarningSoundPanel'')'); + SoundInterface(obj, 'add', 'WarningSound', 10, 10); + SoundInterface(obj, 'set', 'WarningSound', 'Vol', 0.0002); + SoundInterface(obj, 'set', 'WarningSound', 'Vol2', 0.004); + SoundInterface(obj, 'set', 'WarningSound', 'Dur1', 4); + SoundInterface(obj, 'set', 'WarningSound', 'Loop', 0); + SoundInterface(obj, 'set', 'WarningSound', 'Style', 'WhiteNoiseRamp'); + + SoundInterface(obj, 'add', 'DangerSound', 215, 10); + SoundInterface(obj, 'set', 'DangerSound', 'Vol', 0.004); + SoundInterface(obj, 'set', 'DangerSound', 'Dur1', 1); + SoundInterface(obj, 'set', 'DangerSound', 'Loop', 1); + SoundInterface(obj, 'set', 'DangerSound', 'Style', 'WhiteNoise'); + + x = oldx; y = oldy; figure(oldfigure); + % end subpanel + SoloFunctionAddVars('SMASection', 'ro_args', {'DrinkTime', 'WarnDur', 'DangerDur'}); + + [x, y] = PunishInterface(obj, 'add', 'PostDrinkPun', x, y); %#ok + next_row(y); + + %--------------------------------------------------------------- + % WarningSoundPanel + %--------------------------------------------------------------- + + case 'WarningSoundPanel' + if WarningSoundPanel==0, set(value(WarningSoundPanelFigure), 'Visible', 'off'); + else set(value(WarningSoundPanelFigure), 'Visible', 'on'); + end; + + %--------------------------------------------------------------- + % CLOSE + %--------------------------------------------------------------- + + case 'close', + if exist('WarningSoundPanelFigure', 'var') && ishandle(value(WarningSoundPanelFigure)), + delete(value(WarningSoundPanelFigure)); + end; + + if exist('PostDrinkPun_SoundsPanel', 'var') && ishandle(value(PostDrinkPun_SoundsPanel)), + delete(value(PostDrinkPun_SoundsPanel)); + end + + %--------------------------------------------------------------- + % REINIT + %--------------------------------------------------------------- + case 'reinit', + currfig = double(gcf); + + % Get the original GUI position and figure: + x = my_gui_info(1); y = my_gui_info(2); figure(my_gui_info(3)); + + % Delete all SoloParamHandles who belong to this object and whose + % fullname starts with the name of this mfile: + delete_sphandle('owner', ['^@' class(obj) '$'], ... + 'fullname', ['^' mfilename '_']); + + % Reinitialise at the original GUI position and figure: + [x, y] = feval(mfilename, obj, 'init', x, y); + + % Restore the current figure: + figure(currfig); +end; + + diff --git a/Protocols/@SoundCatContinuous/RewardsSection.m b/Protocols/@SoundCatContinuous/RewardsSection.m new file mode 100644 index 00000000..65ead5c1 --- /dev/null +++ b/Protocols/@SoundCatContinuous/RewardsSection.m @@ -0,0 +1,237 @@ +% Typical section code-- this file may be used as a template to be added +% on to. The code below stores the current figure and initial position when +% the action is 'init'; and, upon 'reinit', deletes all SoloParamHandles +% belonging to this section, then calls 'init' at the proper GUI position +% again. +% +% +% [x, y] = YOUR_SECTION_NAME(obj, action, x, y) +% +% Section that takes care of YOUR HELP DESCRIPTION +% +% PARAMETERS: +% ----------- +% +% obj Default object argument. +% +% action One of: +% 'init' To initialise the section and set up the GUI +% for it +% +% 'reinit' Delete all of this section's GUIs and data, +% and reinit, at the same position on the same +% figure as the original section GUI was placed. +% +% 'get_stimulus' Returns either (1) a structure with fields 'type' +% and 'duration', with the contents of 'type' being +% 'lights' and the contents of 'duration' the +% maximum duration, in secs, of the stimulus; or (2) a +% structure with the fields 'type', 'duration', and +% 'id', with contents 'sounds', duration of sound in +% secs, and integer sound_id, respectively. +% +% 'get_poked_trials' Returns a double, number of trials in +% which subject poked in the appropriate poke at +% some point. +% +% +% x, y Relevant to action = 'init'; they indicate the initial +% position to place the GUI at, in the current figure window +% +% RETURNS: +% -------- +% +% [x, y] When action == 'init', returns x and y, pixel positions on +% the current figure, updated after placing of this section's GUI. +% +% +% x When action == 'get_current_side', returns either the string 'l' +% or the string 'r', for Left and Right, respectively. +% + + +function [x, y] = RewardsSection(obj, action, x, y) + +GetSoloFunctionArgs(obj); + +switch action + case 'init', + % Save the figure and the position in the figure where we are + % going to start adding GUI elements: + SoloParamHandle(obj, 'my_gui_info', 'value', [x y double(gcf)]); + + + DispParam(obj, 'n_trials', 0, x, y, ... + 'TooltipString', 'total # of elapsed trials'); next_row(y); + DispParam(obj, 'poked_trials', 0, x, y, ... + 'TooltipString', '# of trials in which subject poked in the appropriate poke at some point'); next_row(y); + DispParam(obj, 'consec_p_trials', 0, x, y, ... + 'TooltipString', '# of consecutive "poked_trials"'); next_row(y); + DispParam(obj, 'consec_up_trials', 0, x, y, ... + 'TooltipString', '# of consecutive UN"poked_trials"'); + ToggleParam(obj, 'WaterBlock', 0, x, y, 'position', [x+185 y 15 15], 'label', '', 'TooltipString', ... + sprintf('\nif BLACK, no water delivery, in "direct" mode. If BROWN, normal water delivery'), ... + 'OnString', '', 'OffString', ''); next_row(y); + DispParam(obj, 'rewarded_trials', 0, x, y, ... + 'TooltipString', '# of trials in which subject poked and got water'); next_row(y); + DispParam(obj, 'consec_r_trials', 0, x, y, ... + 'TooltipString', '# of consecutive trials, ending in last trial, in which subject poked and got water'); next_row(y); + DispParam(obj, 'rt', 0, x, y, ... + 'TooltipString', 'reaction time'); next_row(y); + NumeditParam(obj, 'rtThreshold', 4, x, y, 'TooltipString', ... + sprintf('\nrt less than this defines a "quick" trial')); next_row(y); + DispParam(obj, 'consec_q_trials', 0, x, y, ... + 'TooltipString', '# of consecutive trials with rt less than rtThreshold'); next_row(y); + DispParam(obj, 'good_trials', 0, x, y, ... + 'TooltipString', '# of trials in which subject exceeded post CS/pre CS poke ratio'); next_row(y); + DispParam(obj, 'consec_g_trials', 0, x, y, ... + 'TooltipString', '# of consecutive trials, ending in last trial, in which subject exceeded post CS/pre CS poke ratio'); next_row(y); + + % ------- + + SoloParamHandle(obj, 'r_trials', 'value', []); + SoloParamHandle(obj, 'q_trials', 'value', []); + SoloParamHandle(obj, 'g_trials', 'value', []); + + SubheaderParam(obj, 'title', 'Rewards Section', x, y); + next_row(y, 1.5); + + SoloFunctionAddVars('SMASection', ... + 'ro_args', {'WaterDelivery', 'RewardTime', 'PokeMeasureTime', 'WaterBlock'}); + feval(mfilename, obj, 'WaterDelivery'); % Set whatever is appropriate for current WaterDelivery + + + % --------------------------------------------------------------------- + % + % CASE GET_POKED_TRIALS + % + % --------------------------------------------------------------------- + + + case 'get_poked_trials', + x = value(poked_trials); %#ok + return; + + case 'add_to_pd' + %% add to pd + x.reward_time=cell2mat(get_history(RewardTime)); + + % --------------------------------------------------------------------- + % + % CASE PREPARE_NEXT_TRIAL + % + % --------------------------------------------------------------------- + + + case 'prepare_next_trial', + if isempty(parsed_events), return; end; + if ~isempty(previous_sides), %#ok + previous_sides = previous_sides(:); + wdh = get_history(WaterDelivery); + if isempty(wdh), wdh{1}='direct'; end; % fix for wierd bug + switch value(wdh{end}), + case 'direct', csstate = 'direct_cs'; + case 'on correct poke', csstate = 'cs'; + case 'on correctly timed poke', csstate = 'rewardable_cs'; + otherwise + error('huh?'); + end; + cs_onset = parsed_events.states.(csstate)(1,1); + if isequal(StimulusSection(obj, 'get_last_stimulus_loc'), 'anti-loc'), + if previous_sides(end)=='l', poke = 'R'; else poke = 'L'; end; + else + if previous_sides(end)=='l', poke = 'L'; else poke = 'R'; end; + end; + mypokes = parsed_events.pokes.(poke)(:,1); + + if ~isempty(find(mypokes > cs_onset,1)) + poked_trials.value = poked_trials+1; %#ok + consec_p_trials.value = consec_p_trials+1; %#ok + consec_up_trials.value = 0; + else + consec_p_trials.value = 0; + consec_up_trials.value = consec_up_trials+1; %#ok + end + end; + + if rows(parsed_events.states.lefthit)>0 || rows(parsed_events.states.righthit)>0, + r_trials.value = [r_trials(1:n_done_trials-1) 1]; %#ok + rewarded_trials.value = rewarded_trials+1; %#ok + consec_r_trials.value = consec_r_trials+1; %#ok + else + r_trials.value = [r_trials(1:n_done_trials-1) 0]; %#ok + consec_r_trials.value = 0; + end; + + hit_history.value = value(r_trials); + + n_trials.value = n_trials+1; %#ok + + + % Compute reaction times only for non-direct delivery modes: + if strcmp(csstate, 'direct_cs'), + consec_q_trials.value = 0; + else + if rows(parsed_events.states.lefthit>0) + rt.value = parsed_events.states.lefthit(1,1) - parsed_events.states.(csstate)(1,1); + elseif rows(parsed_events.states.righthit>0) + rt.value = parsed_events.states.righthit(1,1) - parsed_events.states.(csstate)(1,1); + else + warning('CLASSICAL:No_hit_state', 'No lefthit or righthit -- not computing rt!'); + end; + + if rt < rtThreshold, consec_q_trials.value = consec_q_trials + 1; %#ok + else consec_q_trials.value = 0; + end; + end; + + % Now compute the good poke ratio stuff + if ~isempty(previous_sides), + preCSonset_pokes = ... + length(find(parsed_events.states.(csstate)(1,1)-PokeMeasureTime < mypokes & ... + mypokes < parsed_events.states.(csstate)(1,1))); + postCSonset_pokes = ... + length(find(parsed_events.states.(csstate)(1,1) < mypokes & ... + mypokes < parsed_events.states.(csstate)(1,1)+PokeMeasureTime)); + if preCSonset_pokes > 0, PokeRatio.value = postCSonset_pokes / preCSonset_pokes; + elseif postCSonset_pokes > 0, PokeRatio.value = Inf; + else PokeRatio.value = 0; + end; + if PokeRatio > PokeRatioThreshold, + good_trials.value = good_trials+1; %#ok + consec_g_trials.value = consec_g_trials+1; %#ok + else + consec_g_trials.value = 0; + end; + end; + + + + + % --------------------------------------------------------------------- + % + % CASE REINIT + % + % --------------------------------------------------------------------- + + case 'reinit', + currfig = double(gcf); + + % Get the original GUI position and figure: + x = my_gui_info(1); y = my_gui_info(2); figure(my_gui_info(3)); + + % Delete all SoloParamHandles who belong to this object and whose + % fullname starts with the name of this mfile: + delete_sphandle('owner', ['^@' class(obj) '$'], ... + 'fullname', ['^' mfilename]); + + + % Reinitialise at the original GUI position and figure: + [x, y] = feval(mfilename, obj, 'init', x, y); + + % Restore the current figure: + figure(currfig); +end + + + diff --git a/Protocols/@SoundCatContinuous/SideSection.m b/Protocols/@SoundCatContinuous/SideSection.m new file mode 100644 index 00000000..fe36faec --- /dev/null +++ b/Protocols/@SoundCatContinuous/SideSection.m @@ -0,0 +1,469 @@ + + +function [x, y] = SideSection(obj, action, x,y) + +GetSoloFunctionArgs(obj); + +switch action, + + % ------------------------------------------------------------------ + % INIT + % ------------------------------------------------------------------ + + case 'init' + + SoloParamHandle(obj, 'my_gui_info', 'value', [x y double(gcf)], 'saveable', 0); + y0 = y; + + [x, y] = AntibiasSectionAthena(obj, 'init', x, y); + + + ToggleParam(obj, 'antibias_LRprob', 0, x,y,... + 'OnString', 'AB_Prob ON',... + 'OffString', 'AB_Prob OFF',... + 'TooltipString', sprintf(['If on (Yellow) then it enables the AntiBias algorithm\n'... + 'based on changing the probablity of Left vs Right'])); + + next_row(y); + NumeditParam(obj, 'LeftProb', 0.5, x, y); next_row(y); + set_callback(LeftProb, {mfilename, 'new_leftprob'}); + MenuParam(obj, 'MaxSame', {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, Inf}, Inf, x, y, ... + 'TooltipString', sprintf(['\nMaximum number of consecutive trials where correct\n' ... + 'response is on the same side. Overrides antibias. Thus, for\n' ... + 'example, if MaxSame=5 and there have been 5 Left trials, the\n' ... + 'next trial is guaranteed to be Right'])); next_row(y); + + DispParam(obj, 'ThisTrial', 'LEFT', x, y); next_row(y); + SoloParamHandle(obj, 'previous_sides', 'value', []); + DeclareGlobals(obj, 'ro_args', 'previous_sides'); + SubheaderParam(obj, 'title', 'Sides Section', x, y); + next_row(y, 1.5); + next_column(x); y = 5; + NumeditParam(obj, 'RewardCollection_duration', 10, x,y,'label','RewardCollection_duration','TooltipString','Wait until rat collects the reward'); + next_row(y); + NumeditParam(obj, 'CenterLed_duration', 200, x,y,'label','Central LED duration','TooltipString','Duration of Center Led'); + next_row(y); + NumeditParam(obj, 'SideLed_duration', 0.5, x,y,'label','Side LED duration','TooltipString','Duration of SideLed'); + next_row(y); + NumeditParam(obj, 'legal_cbreak', 0.1, x,y, 'position', [x, y, 175 20], 'TooltipString','Time in sec for which it is ok to be outside the center port before a violation occurs.'); + ToggleParam(obj, 'LED_during_legal_cbreak', 1, x, y, 'OnString', 'LED ON LcB', 'OffString', 'LED off LcB', ... + 'position', [x+180 y 20 20], 'TooltipString', ... + 'If 1 (black), turn center port LED back on during legal_cbreak; if 0 (brown), leave LED off'); + next_row(y); + NumeditParam(obj, 'SettlingIn_time', 0.2, x,y, 'position', [x, y, 175 20], 'TooltipString','Initial settling period during which "legal cbreak period" can be longer than the usual "legal_cbreak"'); + next_row(y); + NumeditParam(obj, 'settling_legal_cbreak', 0.1, x,y, 'position', [x, y, 175 20], 'TooltipString','Time in sec for which it is ok during the "SettlingIn_time" to be outside the center port before a violation occurs.'); + ToggleParam(obj, 'LED_during_settling_legal_cbreak', 0, x, y, 'OnString', 'LED ON SetLcB', 'OffString', 'LED OFF setLcB', ... + 'position', [x+180 y 20 20], 'TooltipString', ... + 'If 1 (black), turn center port LED back on during settling_legal_cbreak; if 0 (brown), leave LED off'); + next_row(y); + MenuParam(obj, 'side_lights' ,{'none','both','correct side','anti side'},1, x,y,'label','Side Lights','TooltipString','Controls the side LEDs during wait_for_spoke'); + next_row(y); + + + NumeditParam(obj, 'A1_time', 0.4, x,y,'label','AUD1 on Time','TooltipString','Duration of first stimulus'); + next_row(y); + set_callback(A1_time, {mfilename, 'new_CP_duration'}); + NumeditParam(obj, 'PreStim_time', 0.20, x,y,'label','Pre-Stim NIC time','TooltipString','Time in NIC before starting the stimulus'); + next_row(y); + set_callback(PreStim_time, {mfilename, 'new_CP_duration'}); + NumeditParam(obj, 'time_bet_aud1_gocue', 0, x,y,'label','A1-GoCue time','TooltipString','time between the end of the stimulus and the go cue '); + next_row(y); + set_callback(time_bet_aud1_gocue, {mfilename, 'new_CP_duration'}); + DispParam(obj, 'init_CP_duration', 0.01, x,y,'label','init_CP duration','TooltipString','Duration of Nose in Central Poke before Go cue starts (see Total_CP_duration)'); + next_row(y); + DispParam(obj, 'CP_duration', PreStim_time+A1_time+time_bet_aud1_gocue, x,y,'label','CP duration','TooltipString','Duration of Nose in Central Poke before Go cue starts (see Total_CP_duration)'); + set_callback(CP_duration, {mfilename, 'new_CP_duration'}); + next_row(y); + NumeditParam(obj, 'time_go_cue' ,0, x,y,'label','Go Cue Duration','TooltipString','duration of go cue (see Total_CP_duration)'); + set_callback(time_go_cue, {mfilename, 'new_time_go_cue'}); + next_row(y); + DispParam(obj, 'Total_CP_duration', CP_duration+time_go_cue, x, y, 'TooltipString', 'Total nose in center port time, in secs. Sum of CP_duration and Go Cue duration'); %#ok<*NODEF> + next_row(y); + ToggleParam(obj, 'warmup_on', 1, x,y,... + 'OnString', 'Warmup ON',... + 'OffString', 'Warmup OFF',... + 'TooltipString', sprintf(['If on (Yellow) then it applies the initial warming up phase, during which the\n',... + 'CP_duration starts small and gradually grows to last_session_max_cp_duration'])); + next_row(y); + % to modify it for widefield imagine + NumeditParam(obj,'imaging',0,x,y,'label','Scope Trigger'); + next_row(y); + NumeditParam(obj, 'reward_delay', 0.01, x,y,'label','Reward Delay','TooltipString','Delay between side poke and reward delivery'); + next_row(y); + NumeditParam(obj, 'reward_duration', 0.1, x,y,'label','Reward Duration','TooltipString','Duration of reward sound'); + next_row(y); + NumeditParam(obj, 'drink_time', 1, x,y,'label','Drink Time','TooltipString','waits to finish water delivery'); + next_row(y); + NumeditParam(obj, 'error_iti', 5, x,y,'label','Error Timeout','TooltipString','ITI on error trials'); + next_row(y); + NumeditParam(obj, 'violation_iti', 1, x,y,'label','Violation Timeout','TooltipString','Center poke violation duration'); + next_row(y); + MenuParam(obj, 'reward_type', {'Always','DelayedReward', 'NoReward'}, ... + 'Always', x, y, 'labelfraction', 0.35, 'TooltipString', sprintf(['\nThis menu is to determine the Reward delivery on wrong-hit trials\n',... + '\nIf ''Always'': reward will be available on each trial no matter which side rat goes first\n',... + '\n If rat pokes first on the wrong side, then reward will be delivered with a delay (if DelayedReward) or not delivered at all (if NoReward)'])); + set_callback(reward_type, {mfilename, 'new_reward_type'}); + next_row(y); + NumeditParam(obj,'secondhit_delay',0,x,y,'label','SecondHit Delay','TooltipString','Reward will be delayed with this amount if reward_type=DelayedReward'); + + next_row(y); + ToggleParam(obj, 'random_A1_time', 0, x,y,... + 'OnString', 'random A1_time ON',... + 'OffString', 'random A1_time OFF',... + 'TooltipString', sprintf('If on (black) then it enables the random sampling of A1_time')); + next_row(y); + ToggleParam(obj, 'random_prego_time', 0, x,y,... + 'OnString', 'random prego_time ON',... + 'OffString', 'random prego_time OFF',... + 'TooltipString', sprintf('If on (black) then it enables the random sampling of time between the end of the stimulus and the go cue')); + + next_column(x); + y=5; + NumeditParam(obj,'trials_in_stage',1,x,y,'label','Trial Counter'); + next_row(y); + NumeditParam(obj,'training_stage',1,x,y,'label','Training Stage'); + next_row(y); + ToggleParam(obj,'use_training',0,x,y,'OnString','Using Autotrain','OffString','Manual Settings'); + + next_row(y); + NumeditParam(obj, 'ntrial_correct_bias', 0, x, y, ... + 'TooltipString', 'antibias starts from trial=ntrial_correct_bias'); + next_row(y); + NumeditParam(obj, 'right_left_diff', .12, x, y, ... + 'TooltipString', 'antibias applies if difference between right and left sides is bigger than this number'); + next_row(y); + NumeditParam(obj, 'max_wtr_mult', 4, x, y, ... + 'TooltipString', 'wtr_mult will be min(max_wtr_mult,right_hit/left_hit)'); + next_row(y); + NumeditParam(obj, 'left_wtr_mult', 1, x, y, ... + 'TooltipString', 'all left reward times are multiplied by this number'); + next_row(y); + NumeditParam(obj, 'right_wtr_mult', 1, x, y, ... + 'TooltipString', 'all right reward times are multiplied by this number'); + next_row(y); + ToggleParam(obj, 'antibias_wtr_mult', 0, x,y,... + 'OnString', 'AB ON',... + 'OffString', 'AB OFF',... + 'TooltipString', sprintf(['If on (black) then it disables the wtr_mult entries\n'... + 'and uses hitfrac to adjust the water times'])); + + next_row(y); + ToggleParam(obj, 'stimuli_on', 1, x,y,... + 'OnString', 'Stimuli ON',... + 'OffString', 'Stimuli OFF',... + 'TooltipString', sprintf('If on (black) then it disable the presentation of sound stimuli during nose poke')); + set_callback(stimuli_on, {mfilename, 'new_CP_duration'}); + + next_row(y); + SoloFunctionAddVars('SoundCatSMA', 'ro_args', ... + {'CP_duration';'SideLed_duration';'CenterLed_duration';'side_lights' ; ... + 'RewardCollection_duration';'training_stage'; ... + 'legal_cbreak' ; 'LED_during_legal_cbreak' ; ... + 'SettlingIn_time';'settling_legal_cbreak' ; 'LED_during_settling_legal_cbreak' ; ... + 'time_go_cue'; ... + 'stimuli_on';'A1_time';'time_bet_aud1_gocue' ; ... + 'PreStim_time';'warmup_on' + 'drink_time';'reward_delay';'reward_duration';'left_wtr_mult';... + 'right_wtr_mult';'antibias_wtr_mult';... + 'reward_type';'secondhit_delay';'error_iti';'violation_iti';'imaging'}); + + SoloFunctionAddVars('StimulusSection', 'ro_args', ... + {'ThisTrial';'A1_time';'time_bet_aud1_gocue' ; ... + 'PreStim_time'}); + SoloFunctionAddVars('StimulatorSection', 'ro_args', ... + {'A1_time';'time_bet_aud1_gocue';'time_go_cue'; ... + 'PreStim_time';'CP_duration';'Total_CP_duration'}); + + % History of hit/miss: + SoloParamHandle(obj, 'deltaf_history', 'value', []); + + SoloFunctionAddVars('OverallPerformanceSection', 'ro_args', ... + {'training_stage'}); + + + SoloParamHandle(obj, 'previous_parameters', 'value', []); + + case 'new_leftprob', + AntibiasSectionAthena(obj, 'update_biashitfrac', value(LeftProb)); + + + case 'new_CP_duration', + if stimuli_on == 0 + PreStim_time.value=0; + A1_time.value=0; + time_bet_aud1_gocue.value=0; + disable(PreStim_time); + disable(A1_time); + disable(time_bet_aud1_gocue); + else + enable(PreStim_time); + enable(A1_time); + enable(time_bet_aud1_gocue); + end + CP_duration.value=PreStim_time + A1_time + time_bet_aud1_gocue; + Total_CP_duration.value = CP_duration + time_go_cue; %#ok<*NASGU> + + case 'new_time_go_cue', + Total_CP_duration.value = CP_duration + time_go_cue; + SoundInterface(obj, 'set', 'GoSound', 'Dur1', value(time_go_cue)); + + case 'new_reward_type' + if strcmp(reward_type,'DelayedReward') + enable(secondhit_delay) + else + secondhit_delay=0; + disable(secondhit_delay) + + end + + + case 'prepare_next_trial' + + + switch value(training_stage) + case 0, %% learning the reward sound association -left or right led on -> poke -> sound+reward + settling_time.value=0.01; + delay_time.value=0; + allow_nic_breaks.value=1; + side_lights.value=3; + trials_in_stage.value=0; + reward_delay.value=0.01; + left_prob.value=0.5; + right_prob.value=0.5; + time_go_cue.value=0.200; + reward_duration=0.200; + + + case 1, %% center led on -> poke in the center -> go cue -> reward light and sound + %what A1_time and time before go cue? + if random_A1_time + A1_times = [0.2, 0.4]; + randomix = randi(length(A1_times), 1); + A1_time.value = A1_times(randomix); + end + + if random_prego_time + prego_times = [0.2, 0.4, 0.6, 0.8, 1]; + randomixx = randi(length(prego_times), 1); + time_bet_aud1_gocue.value = prego_times(randomixx); + end + settling_time.value=0.25; + delay_time.value=0; + allow_nic_breaks.value=1; + side_lights.value=3; + training_stage.value=1; + trials_in_stage.value=0; + reward_delay.value=0.01; + left_prob.value=0.5; + right_prob.value=0.5; + if n_done_trials <1 && warmup_on ==1 + CP_duration.value=value(init_CP_duration); + else + CP_duration.value=PreStim_time + A1_time + time_bet_aud1_gocue; + end + Total_CP_duration.value = CP_duration + time_go_cue; %#ok<*NASGU> + + + case 3, % like stage 2, now passive exposure to the stimuli - reward comes anyway + case 4 %% now reward comes only if rat goes to the correct side + + end + + + %% update hit_history, previous_sides, etc + was_viol=false; + was_hit=false; + was_timeout=false; + if n_done_trials>0 + if ~isempty(parsed_events) + if isfield(parsed_events,'states') + if isfield(parsed_events.states,'timeout_state') + was_timeout=rows(parsed_events.states.timeout_state)>0; + end + if isfield(parsed_events.states,'violation_state') + was_viol=rows(parsed_events.states.violation_state)>0; + end + end + + end + + violation_history.value=[violation_history(:); was_viol]; + timeout_history.value=[timeout_history(:); was_timeout]; + + SideSection(obj,'update_side_history'); + + if ~was_viol && ~was_timeout + %was_hit=rows(parsed_events.states.hit_state)>0; + was_hit=rows(parsed_events.states.second_hit_state)==0; + hit_history.value=[hit_history(:); was_hit]; + + else + % There was a violation or timeout + hit_history.value=[hit_history(:); nan]; + end + + % Now calculate the deltaF and sides - this maybe interesting + % even in a violation or timeout case. + + fn=fieldnames(parsed_events.states); + led_states=find(strncmp('led',fn,3)); + deltaF=0; + n_l=0; + n_r=0; + for lx=1:numel(led_states) + lind=led_states(lx); + if rows(parsed_events.states.(fn{lind}))>0 + if fn{lind}(end)=='l' + deltaF=deltaF-1; + n_l=n_l+1; + elseif fn{lind}(end)=='r' + deltaF=deltaF+1; + n_r=n_r+1; + elseif fn{lind}(end)=='b' + n_l=n_l+1; + n_r=n_r+1; + + end + end + + end + + % if deltaF>0 then a right poke is a hit + % if deltaF<0 then a left poke is a hit + + deltaf_history.value=[deltaf_history(:); deltaF]; + + end + + if antibias_LRprob ==1 + if n_done_trials >ntrial_correct_bias && ~was_viol && ~was_timeout + nonan_hit_history=value(hit_history); + nonan_hit_history(isnan(nonan_hit_history))=[]; + nonan_previous_sides=value(previous_sides); + nan_history=value(hit_history); + nonan_previous_sides(isnan(nan_history))=[]; + AntibiasSectionAthena(obj, 'update', value(LeftProb), nonan_hit_history(:)',nonan_previous_sides(:)); % <~> Transposed hit history so that it is the expected column vector. (Antibias errors out otherwise.) 2007.09.05 01:39 + end + + + if ~isinf(MaxSame) && length(previous_sides) > MaxSame && ... + all(previous_sides(n_done_trials-MaxSame+1:n_done_trials) == previous_sides(n_done_trials)), %#ok + if previous_sides(end)=='l', ThisTrial.value = 'RIGHT'; + else ThisTrial.value = 'LEFT'; + end; + else + choiceprobs = AntibiasSectionAthena(obj, 'get_posterior_probs'); + if rand(1) <= choiceprobs(1), ThisTrial.value = 'LEFT'; + else ThisTrial.value = 'RIGHT'; + end; + end; + + else + if (rand(1)<=LeftProb) + ThisTrial.value='LEFT'; + + else + ThisTrial.value='RIGHT'; + end + + end + + + + +% %% Do the anti-bias with changing reward delivery +% % reset anti-bias +% left_wtr_mult.value=1; +% right_wtr_mult.value=1; +% if n_done_trials>ntrial_correct_bias && antibias_wtr_mult==1 +% hh=hit_history(n_done_trials-ntrial_correct_bias:n_done_trials); +% ps=previous_sides(n_done_trials-ntrial_correct_bias:n_done_trials); +% +% right_hit=nanmean(hh(ps=='r')); +% left_hit=nanmean(hh(ps=='l')); +% +% if abs(right_hit-left_hit) + + case 'get_left_prob' + x = value(LeftProb); + + case 'get_cp_history' + x = cell2mat(get_history(CP_duration)); + + case 'get_stimdur_history' + x = cell2mat(get_history(A1_time)); + + case 'update_side_history' + if strcmp(ThisTrial, 'LEFT') + ps=value(previous_sides); + ps(n_done_trials)='l'; + previous_sides.value=ps; + + else + ps=value(previous_sides); + ps(n_done_trials)='r'; + previous_sides.value=ps; + end; + + case 'get_current_side' + if strcmp(ThisTrial, 'LEFT') + x = 'l'; %#ok + else + x = 'r'; + end; + + + case 'close' + % Delete all SoloParamHandles who belong to this object and whose + % fullname starts with the name of this mfile: + delete_sphandle('owner', ['^@' class(obj) '$'], ... + 'fullname', ['^' mfilename]); + + case 'reinit', + currfig = double(gcf); + + % Get the original GUI position and figure: + x = my_gui_info(1); y = my_gui_info(2); figure(my_gui_info(3)); + + % Delete all SoloParamHandles who belong to this object and whose + % fullname starts with the name of this mfile: + delete_sphandle('owner', ['^@' class(obj) '$'], ... + 'fullname', ['^' mfilename]); + + % Reinitialise at the original GUI position and figure: + [x, y] = feval(mfilename, obj, 'init', x, y); + + % Restore the current figure: + figure(currfig); + +end + + diff --git a/Protocols/@SoundCatContinuous/SideSection_aa.m b/Protocols/@SoundCatContinuous/SideSection_aa.m new file mode 100644 index 00000000..086836f0 --- /dev/null +++ b/Protocols/@SoundCatContinuous/SideSection_aa.m @@ -0,0 +1,378 @@ + + +function [x, y] = SideSection(obj, action, x,y) + +GetSoloFunctionArgs(obj); + +switch action, + + % ------------------------------------------------------------------ + % INIT + % ------------------------------------------------------------------ + + case 'init' + + SoloParamHandle(obj, 'my_gui_info', 'value', [x y double(gcf)], 'saveable', 0); + y0 = y; + + [x, y] = AntibiasSection(obj, 'init', x, y); + + NumeditParam(obj, 'LeftProb', 0.5, x, y); next_row(y); + set_callback(LeftProb, {mfilename, 'new_leftprob'}); + MenuParam(obj, 'MaxSame', {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, Inf}, Inf, x, y, ... + 'TooltipString', sprintf(['\nMaximum number of consecutive trials where correct\n' ... + 'response is on the same side. Overrides antibias. Thus, for\n' ... + 'example, if MaxSame=5 and there have been 5 Left trials, the\n' ... + 'next trial is guaranteed to be Right'])); next_row(y); + + DispParam(obj, 'ThisTrial', 'LEFT', x, y); next_row(y); + SoloParamHandle(obj, 'previous_sides', 'value', []); + DeclareGlobals(obj, 'ro_args', 'previous_sides'); + SubheaderParam(obj, 'title', 'Sides Section', x, y); + next_row(y, 1.5); + next_column(x); y = 5; + NumeditParam(obj, 'CP_duration', 0.250, x,y,'label','CP duration','TooltipString','Duration of Nose in Central Poke before Go cue starts (see Total_CP_duration)'); + set_callback(CP_duration, {mfilename, 'new_CP_duration'}); + next_row(y); + NumeditParam(obj, 'time_go_cue' ,0.2, x,y,'label','Go Cue Duration','TooltipString','duration of go cue (see Total_CP_duration)'); + set_callback(time_go_cue, {mfilename, 'new_time_go_cue'}); + next_row(y); + DispParam(obj, 'Total_CP_duration', CP_duration+time_go_cue, x, y, 'TooltipString', 'Total nose in center port time, in secs. Sum of CP_duration and Go Cue duration'); %#ok<*NODEF> + next_row(y); + NumeditParam(obj, 'RewardCollection_duration', 10, x,y,'label','RewardCollection_duration','TooltipString','Wait until rat collects the reward'); + next_row(y); + NumeditParam(obj, 'CenterLed_duration', 20, x,y,'label','Central LED duration','TooltipString','Duration of Center Led'); + next_row(y); + NumeditParam(obj, 'SideLed_duration', 0.5, x,y,'label','Side LED duration','TooltipString','Duration of SideLed'); + next_row(y); + NumeditParam(obj, 'legal_cbreak', 0, x,y, 'position', [x, y, 175 20], 'TooltipString','Time in sec for which it is ok to be outside the center port before a violation occurs.'); + ToggleParam(obj, 'LED_during_legal_cbreak', 1, x, y, 'OnString', 'LED ON LcB', 'OffString', 'LED off LcB', ... + 'position', [x+180 y 20 20], 'TooltipString', ... + 'If 1 (black), turn center port LED back on during legal_cbreak; if 0 (brown), leave LED off'); + next_row(y); + NumeditParam(obj, 'SettlingIn_time', 0.2, x,y, 'position', [x, y, 175 20], 'TooltipString','Initial settling period during which "legal cbreak period" can be longer than the usual "legal_cbreak"'); + next_row(y); + NumeditParam(obj, 'settling_legal_cbreak', 0.05, x,y, 'position', [x, y, 175 20], 'TooltipString','Time in sec for which it is ok during the "SettlingIn_time" to be outside the center port before a violation occurs.'); + ToggleParam(obj, 'LED_during_settling_legal_cbreak', 0, x, y, 'OnString', 'LED ON SetLcB', 'OffString', 'LED OFF setLcB', ... + 'position', [x+180 y 20 20], 'TooltipString', ... + 'If 1 (black), turn center port LED back on during settling_legal_cbreak; if 0 (brown), leave LED off'); + next_row(y); + MenuParam(obj, 'side_lights' ,{'none','both','correct side','anti side'},1, x,y,'label','Side Lights','TooltipString','Controls the side LEDs during wait_for_spoke'); + next_row(y); + % NumeditParam(obj, 'wait_for_cpoke_timeout', 4, x,y,'label','Response Timeout','TooltipString','Time after NIC to wait for a side poke'); + % next_row(y); + + + NumeditParam(obj, 'A1_time', 3, x,y,'label','AUD1 on Time','TooltipString','Duration of first stimulus'); + next_row(y); + NumeditParam(obj, 'A2_time', 3, x,y,'label','AUD2 On Time','TooltipString','Duration of second stimulus'); + next_row(y); + NumeditParam(obj, 'Del_time', 2, x,y,'label','Delay Duration Time','TooltipString','Duration of delay period'); + next_row(y); + NumeditParam(obj, 'PreStim_time', 0.05, x,y,'label','Pre-Stim NIC time','TooltipString','Time in NIC before starting the stimulus'); + next_row(y); + NumeditParam(obj, 'time_bet_aud2_gocue', 0.05, x,y,'label','A2-GoCue time','TooltipString','time between the end of the second stimulus and the go cue '); + next_row(y); + NumeditParam(obj, 'reward_delay', 0.01, x,y,'label','Reward Delay','TooltipString','Delay between side poke and reward delivery'); + next_row(y); + NumeditParam(obj, 'reward_duration', 0.1, x,y,'label','Reward Duration','TooltipString','Duration of reward sound'); + next_row(y); + NumeditParam(obj, 'drink_time', 1, x,y,'label','Drink Time','TooltipString','waits to finish water delivery'); + next_row(y); + ToggleParam(obj, 'ignore_errors', 0, x,y,'OffString','Errors Punished','OnString','Errors Ignored','TooltipString','If ignore errors, then subject will eventually get the reward at the correct poke'); + next_row(y); + NumeditParam(obj, 'error_iti', 10, x,y,'label','Error Timeout','TooltipString','ITI on error trials'); + next_row(y); + + NumeditParam(obj, 'violation_iti', 1, x,y,'label','Violation Timeout','TooltipString','Center poke violation duration'); + + next_column(x); + y=5; + NumeditParam(obj,'trials_in_stage',1,x,y,'label','Trial Counter'); + next_row(y); + NumeditParam(obj,'training_stage',2,x,y,'label','Training Stage'); + next_row(y); + ToggleParam(obj,'use_training',0,x,y,'OnString','Using Autotrain','OffString','Manual Settings'); + + next_row(y); + NumeditParam(obj, 'ntrial_correct_bias', 30, x, y, ... + 'TooltipString', 'antibias starts from trial=ntrial_correct_bias'); + next_row(y); + NumeditParam(obj, 'right_left_diff', .12, x, y, ... + 'TooltipString', 'antibias applies if difference between right and left sides is bigger than this number'); + next_row(y); + NumeditParam(obj, 'max_wtr_mult', 4, x, y, ... + 'TooltipString', 'wtr_mult will be min(max_wtr_mult,right_hit/left_hit)'); + next_row(y); + NumeditParam(obj, 'left_wtr_mult', 1, x, y, ... + 'TooltipString', 'all left reward times are multiplied by this number'); + next_row(y); + NumeditParam(obj, 'right_wtr_mult', 1, x, y, ... + 'TooltipString', 'all right reward times are multiplied by this number'); + next_row(y); + ToggleParam(obj, 'antibias_wtr_mult', 0, x,y,... + 'OnString', 'AB ON',... + 'OffString', 'AB OFF',... + 'TooltipString', sprintf(['If on (black) then it disables the wtr_mult entries\n'... + 'and uses hitfrac to adjust the water times'])); + + next_row(y); + SoloFunctionAddVars('AthenaSMA', 'ro_args', ... + {'CP_duration';'SideLed_duration';'CenterLed_duration';'side_lights' ; ... + 'RewardCollection_duration';'ignore_errors';'training_stage'; ... + 'legal_cbreak' ; 'LED_during_legal_cbreak' ; ... + 'SettlingIn_time';'settling_legal_cbreak' ; 'LED_during_settling_legal_cbreak' ; ... + 'time_go_cue'; ... + 'A1_time';'A2_time';'Del_time';'time_bet_aud2_gocue' ; ... + 'PreStim_time' + 'drink_time';'reward_delay';'reward_duration';'left_wtr_mult';... + 'right_wtr_mult';'antibias_wtr_mult';... + 'error_iti';'violation_iti'}); + + SoloFunctionAddVars('StimulusSection', 'ro_args', ... + {'ThisTrial';'A1_time';'A2_time';'Del_time';'time_bet_aud2_gocue' ; ... + 'PreStim_time'}); + + % History of hit/miss: + SoloParamHandle(obj, 'deltaf_history', 'value', []); + + SoloFunctionAddVars('OverallPerformanceSection', 'ro_args', ... + {'training_stage'}); + + + SoloParamHandle(obj, 'previous_parameters', 'value', []); + + case 'new_leftprob', + %AntibiasSection(obj, 'update_biashitfrac', value(LeftProb)); + + + case 'new_CP_duration', + if training_stage ==2 + CP_duration=PreStim_time + A1_time + A2_time + Del_time + time_bet_aud2_gocue; + end + Total_CP_duration.value = CP_duration + time_go_cue; %#ok<*NASGU> + + case 'new_time_go_cue', + Total_CP_duration.value = CP_duration + time_go_cue; + SoundInterface(obj, 'set', 'GoSound', 'Dur1', value(time_go_cue)); + + case 'prepare_next_trial' + + + switch value(training_stage) + case 0, %% learning the reward sound association -left or right led on -> poke -> sound+reward + settling_time.value=0.01; + delay_time.value=0; + allow_nic_breaks.value=1; + ignore_errors.value=1; + side_lights.value=3; + trials_in_stage.value=0; + reward_delay.value=0.01; + error_iti.value=4; + left_prob.value=0.5; + right_prob.value=0.5; + time_go_cue.value=0.200; + reward_duration=0.200; + + + case 1, %% center led on -> poke in the center -> go cue -> reward light and sound + settling_time.value=0.25; + delay_time.value=0; + allow_nic_breaks.value=1; + ignore_errors.value=1; + side_lights.value=3; + training_stage.value=1; + trials_in_stage.value=0; + reward_delay.value=0.01; + error_iti.value=5; + left_prob.value=0.5; + right_prob.value=0.5; + + + case 2, % like stage 1 - now waiting time grows + CP_duration=PreStim_time + A1_time + A2_time + Del_time + time_bet_aud2_gocue; + case 3, % like stage 2, now passive exposure to the stimuli - reward comes anyway + case 4 %% now reward comes only if rat goes to the correct side + + end + + + %% update hit_history, previous_sides, etc + was_viol=false; + was_hit=false; + was_timeout=false; + if n_done_trials>0 + if ~isempty(parsed_events) + if isfield(parsed_events,'states') + if isfield(parsed_events.states,'timeout_state') + was_timeout=rows(parsed_events.states.timeout_state)>0; + end + if isfield(parsed_events.states,'violation_state') + was_viol=rows(parsed_events.states.violation_state)>0; + end + end + + end + + violation_history.value=[violation_history(:); was_viol]; + timeout_history.value=[timeout_history(:); was_timeout]; + + SideSection(obj,'update_side_history'); + + if ~was_viol && ~was_timeout + was_hit=rows(parsed_events.states.hit_state)>0; + hit_history.value=[hit_history(:); was_hit]; + + else + % There was a violation or timeout + hit_history.value=[hit_history(:); nan]; + end + + % Now calculate the deltaF and sides - this maybe interesting + % even in a violation or timeout case. + + fn=fieldnames(parsed_events.states); + led_states=find(strncmp('led',fn,3)); + deltaF=0; + n_l=0; + n_r=0; + for lx=1:numel(led_states) + lind=led_states(lx); + if rows(parsed_events.states.(fn{lind}))>0 + if fn{lind}(end)=='l' + deltaF=deltaF-1; + n_l=n_l+1; + elseif fn{lind}(end)=='r' + deltaF=deltaF+1; + n_r=n_r+1; + elseif fn{lind}(end)=='b' + n_l=n_l+1; + n_r=n_r+1; + + end + end + + end + + % if deltaF>0 then a right poke is a hit + % if deltaF<0 then a left poke is a hit + + deltaf_history.value=[deltaf_history(:); deltaF]; + + end + + % if n_done_trials >3 && ~was_viol + % AntibiasSection(obj, 'update', value(LeftProb), hit_history(1:n_done_trials)',previous_sides(1:n_done_trials)); % <~> Transposed hit history so that it is the expected column vector. (Antibias errors out otherwise.) 2007.09.05 01:39 + % end + % + % + % if ~isinf(MaxSame) && length(previous_sides) > MaxSame && ... + % all(previous_sides(n_done_trials-MaxSame+1:n_done_trials) == previous_sides(n_done_trials)), %#ok + % if previous_sides(end)=='l', ThisTrial.value = 'RIGHT'; + % else ThisTrial.value = 'LEFT'; + % end; + % else + % choiceprobs = AntibiasSection(obj, 'get_posterior_probs'); + % if rand(1) <= choiceprobs(1), ThisTrial.value = 'LEFT'; + % else ThisTrial.value = 'RIGHT'; + % end; + % end; + + + %% Do the anti-bias with changing reward delivery + % reset anti-bias + left_wtr_mult.value=1; + right_wtr_mult.value=1; + if n_done_trials>ntrial_correct_bias && antibias_wtr_mult==1 + hh=hit_history(n_done_trials-ntrial_correct_bias:n_done_trials); + ps=previous_sides(n_done_trials-ntrial_correct_bias:n_done_trials); + + right_hit=nanmean(hh(ps=='r')); + left_hit=nanmean(hh(ps=='l')); + + if abs(right_hit-left_hit) + + case 'get_left_prob' + x = value(LeftProb); + + case 'get_cp_history' + x = cell2mat(get_history(CP_duration)); + + case 'update_side_history' + if strcmp(ThisTrial, 'LEFT') + ps=value(previous_sides); + ps(n_done_trials)='l'; + previous_sides.value=ps; + + else + ps=value(previous_sides); + ps(n_done_trials)='r'; + previous_sides.value=ps; + end; + + case 'get_current_side' + if strcmp(ThisTrial, 'LEFT') + x = 'l'; %#ok + else + x = 'r'; + end; + + + case 'close' + % Delete all SoloParamHandles who belong to this object and whose + % fullname starts with the name of this mfile: + delete_sphandle('owner', ['^@' class(obj) '$'], ... + 'fullname', ['^' mfilename]); + + case 'reinit', + currfig = double(gcf); + + % Get the original GUI position and figure: + x = my_gui_info(1); y = my_gui_info(2); figure(my_gui_info(3)); + + % Delete all SoloParamHandles who belong to this object and whose + % fullname starts with the name of this mfile: + delete_sphandle('owner', ['^@' class(obj) '$'], ... + 'fullname', ['^' mfilename]); + + % Reinitialise at the original GUI position and figure: + [x, y] = feval(mfilename, obj, 'init', x, y); + + % Restore the current figure: + figure(currfig); + +end + + diff --git a/Protocols/@SoundCatContinuous/SoundCatContinuous.m b/Protocols/@SoundCatContinuous/SoundCatContinuous.m new file mode 100644 index 00000000..63ebc611 --- /dev/null +++ b/Protocols/@SoundCatContinuous/SoundCatContinuous.m @@ -0,0 +1,261 @@ +% AltSoundCatCatch protocol +% EM, October 2020 + +function [obj] = SoundCatContinuous(varargin) + +% Default object is of our own class (mfilename); +% we inherit only from Plugins + +obj = class(struct, mfilename, pokesplot2, saveload, sessionmodel, soundmanager, soundui, antibias, ... + water, distribui, punishui, comments, soundtable, sqlsummary,reinforcement); + +%--------------------------------------------------------------- +% BEGIN SECTION COMMON TO ALL PROTOCOLS, DO NOT MODIFY +%--------------------------------------------------------------- + +% If creating an empty object, return without further ado: +if nargin==0 || (nargin==1 && ischar(varargin{1}) && strcmp(varargin{1}, 'empty')), + return; +end; + +if isa(varargin{1}, mfilename), % If first arg is an object of this class itself, we are + % Most likely responding to a callback from + % a SoloParamHandle defined in this mfile. + if length(varargin) < 2 || ~ischar(varargin{2}), + error(['If called with a "%s" object as first arg, a second arg, a ' ... + 'string specifying the action, is required\n']); + else action = varargin{2}; varargin = varargin(3:end); %#ok + end; +else % Ok, regular call with first param being the action string. + action = varargin{1}; varargin = varargin(2:end); %#ok +end; + +GetSoloFunctionArgs(obj); + +switch action, + + %% init + case 'init' + dispatcher('set_trialnum_indicator_flag'); + hackvar = 10; SoloFunctionAddVars('SessionModel', 'ro_args', 'hackvar'); %#ok + SoloParamHandle(obj, 'myfig', 'saveable', 0); myfig.value = figure; + + % Make the title of the figure be the protocol name, and if someone tries + % to close this figure, call dispatcher's close_protocol function, so it'll know + % to take it off the list of open protocols. + name = mfilename; + set(value(myfig), 'Name', name, 'Tag', name, ... + 'closerequestfcn', 'dispatcher(''close_protocol'')', 'MenuBar', 'none'); + % At this point we have one SoloParamHandle, myfig + % Let's put the figure where we want it and give it a reasonable size: + set(value(myfig), 'Position', [485 144 850 680]); + + SoloParamHandle(obj, 'nsessions_healthy_number_of_pokes', 'value', 0, 'save_with_settings', 1); + SoloParamHandle(obj, 'post_DelComp_protocol', 'value', '', 'save_with_settings', 1); + SoloParamHandle(obj, 'post_DelComp_settings_filename', 'value', '', 'save_with_settings', 1); + + + SoloParamHandle(obj, 'hit_history', 'value', []); + DeclareGlobals(obj, 'ro_args', {'hit_history'}); + SoloFunctionAddVars('SideSection', 'rw_args', 'hit_history'); + + %pair_history changed to stimulus_history (from AthenaDelayComp) + SoloParamHandle(obj, 'stimulus_history', 'value', []); + DeclareGlobals(obj, 'ro_args', {'stimulus_history'}); + SoloFunctionAddVars('StimulusSection', 'rw_args', 'stimulus_history'); + + SoloParamHandle(obj, 'violation_history', 'value', []); + DeclareGlobals(obj, 'ro_args', {'violation_history'}); + SoloFunctionAddVars('SideSection', 'rw_args', 'violation_history'); + + SoloParamHandle(obj, 'timeout_history', 'value', []); + DeclareGlobals(obj, 'ro_args', {'timeout_history'}); + SoloFunctionAddVars('SideSection', 'rw_args', 'timeout_history'); + + + SoundManagerSection(obj, 'init'); + x = 5; y = 5; % Initial position on main GUI window + [x, y] = SavingSection(obj, 'init', x, y); + + %% slow ramp up of water amount + %%the water volume is controlled by a 5-parameter logistic function: WaterAmount(t) = maxasymp + (minasymp/(1+(t/inflp)^slp).^assym) + NumeditParam(obj, 'maxasymp', 38, x,y,'label','maxasymp','TooltipString',... + 'the water volume is controlled by a 5-parameter logistic function: WaterAmount(trialnum) = maxasymp + (minasymp/(1+(trialnum/inflp)^slp).^assym)'); + next_row(y); + NumeditParam(obj, 'slp', 3, x,y,'label','slp','TooltipString','Water Modulation: Slope of the logistic function'); + next_row(y); + NumeditParam(obj, 'inflp', 350, x,y,'label','inflp','TooltipString','Water Modulation: concentration at the inflection point'); + next_row(y); + NumeditParam(obj, 'minasymp', -21, x,y,'label','inflp','TooltipString','Water Modulation: minimum asymptote'); + next_row(y); + NumeditParam(obj, 'assym', 0.7, x,y,'label','assym','TooltipString','Water Modulation: asymmetry factor'); + next_row(y); + DispParam(obj, 'trial_1', 0, x, y, 'TooltipString', 'uL on first trial'); + next_row(y); + DispParam(obj, 'trial_150', 0, x, y, 'TooltipString', 'uL on trial 150'); + next_row(y); + DispParam(obj, 'trial_300', 0, x, y, 'TooltipString', 'uL on trial 300'); + next_row(y); + set_callback({maxasymp;slp;inflp;minasymp;assym}, {mfilename, 'change_water_modulation_params'}); + feval(mfilename, obj, 'change_water_modulation_params'); + + %AthenaSMA changed to SoundCatSMA (From AthenaDelayComp) + SoloFunctionAddVars('SoundCatSMA', 'ro_args', ... + {'maxasymp';'slp';'inflp';'minasymp';'assym'}); + [x, y] = WaterValvesSection(obj, 'init', x, y); + + % For plotting with the pokesplot plugin, we need to tell it what + % colors to plot with: + my_state_colors = SoundCatSMA(obj, 'get_state_colors'); + % In pokesplot, the poke colors have a default value, so we don't need + % to specify them, but here they are so you know how to change them. + my_poke_colors = struct( ... + 'L', 0.6*[1 0.66 0], ... + 'C', [0 0 0], ... + 'R', 0.9*[1 0.66 0]); + + [x, y] = PokesPlotSection(obj, 'init', x, y, ... + struct('states', my_state_colors, 'pokes', my_poke_colors)); next_row(y); + + [x, y] = CommentsSection(obj, 'init', x, y); + SessionDefinition(obj, 'init', x, y, value(myfig)); next_row(y, 2); %#ok + SessionDefinition(obj, 'set_old_style_parsing_flag',0); + % [x, y] = PunishmentSection(obj, 'init', x, y); %#ok + + next_column(x); y=5; + [x, y] = OverallPerformanceSection(obj, 'init', x, y); + [x, y] = StimulatorSection(obj, 'init', x, y); next_row(y, 1.3); + [x, y] = SideSection(obj, 'init', x, y); %#ok + [x, y] = SoundSection(obj,'init',x,y); +% [x, y] = PlayStimuli(obj,'init',x,y); + [x, y] = StimulusSection(obj,'init',x,y); + + figpos = get(double(gcf), 'Position'); + [expmtr, rname]=SavingSection(obj, 'get_info'); + HeaderParam(obj, 'prot_title', [mfilename ': ' expmtr ', ' rname], x, y, 'position', [10 figpos(4)-25, 800 20]); + + SoundCatSMA(obj, 'init'); + feval(mfilename, obj, 'prepare_next_trial'); + + %% change_water_modulation_params + case 'change_water_modulation_params', + display_guys = [1 150 300]; + for i=1:numel(display_guys), + t = display_guys(i); + + myvar = eval(sprintf('trial_%d', t)); + myvar.value = maxasymp + (minasymp/(1+(t/inflp)^slp).^assym); + end; + + %% prepare next trial + case 'prepare_next_trial' + + SideSection(obj, 'prepare_next_trial'); + % Run SessionDefinition *after* SideSection so we know whether the + % trial was a violation or not + SessionDefinition(obj, 'next_trial'); + StimulatorSection(obj, 'update_values'); + OverallPerformanceSection(obj, 'evaluate'); + StimulusSection(obj,'prepare_next_trial'); + SoundManagerSection(obj, 'send_not_yet_uploaded_sounds'); + + + nTrials.value = n_done_trials; + + [sma, prepare_next_trial_states] = SoundCatSMA(obj, 'prepare_next_trial'); + + % Default behavior of following call is that every 20 trials, the data + % gets saved, not interactive, no commit to CVS. + SavingSection(obj, 'autosave_data'); + + CommentsSection(obj, 'clear_history'); % Make sure we're not storing unnecessary history + if n_done_trials==1, % Auto-append date for convenience. + CommentsSection(obj, 'append_date'); CommentsSection(obj, 'append_line', ''); + end; + + if n_done_trials==1 + [expmtr, rname]=SavingSection(obj, 'get_info'); + prot_title.value=[mfilename ' on rig ' get_hostname ' : ' expmtr ', ' rname '. Started at ' datestr(now, 'HH:MM')]; + end + + try send_n_done_trials(obj); end + + %% trial_completed + case 'trial_completed' + + % Do any updates in the protocol that need doing: + feval(mfilename, 'update'); + % And PokesPlot needs completing the trial: + PokesPlotSection(obj, 'trial_completed'); + %% update + case 'update' + PokesPlotSection(obj, 'update'); + + + %% close + case 'close' + PokesPlotSection(obj, 'close'); + %PunishmentSection(obj, 'close'); + SideSection(obj, 'close'); + StimulusSection(obj,'close'); + + if exist('myfig', 'var') && isa(myfig, 'SoloParamHandle') && ishandle(value(myfig)), %#ok + delete(value(myfig)); + end; + delete_sphandle('owner', ['^@' class(obj) '$']); + + + %% end_session + case 'end_session' + prot_title.value = [value(prot_title) ', Ended at ' datestr(now, 'HH:MM')]; + + + %% pre_saving_settings + case 'pre_saving_settings' + + StimulusSection(obj,'hide'); + SessionDefinition(obj, 'run_eod_logic_without_saving'); + perf = OverallPerformanceSection(obj, 'evaluate'); + cp_durs = SideSection(obj, 'get_cp_history'); +% [classperf tot_perf]= StimulusSection(obj, 'get_class_perform'); + + %% + % YOU NEED TO CHANGE THIS +% [stimuli] = StimulusSection(obj,'get_stimuli'); + %% + + [stim1dur] = SideSection(obj,'get_stimdur_history'); + %stim_history = StimulatorSection(obj,'get_history'); + +% CommentsSection(obj, 'append_line', ... +% sprintf(['ntrials = %d, violations = %.2f, timeouts=%.2f, hits = %.2f\n', ... +% 'pre-Go cue went from %.3f to %.3f (delta=%.3f)\n', ... +% 'Low = %.2f, High = %.2f'], ... +% perf(1), perf(2), perf(3), perf(6), cp_durs(1), cp_durs(end), cp_durs(end)-cp_durs(1), classperf(1),classperf(2))); + + pd.hits=hit_history(:); + pd.sides=previous_sides(:); + pd.viols=violation_history(:); + pd.timeouts=timeout_history(:); +% pd.performance=tot_perf(:); + pd.cp_durs=cp_durs(:); + + +% pd.stimuli=stimuli(:); + % Athena: look into pair_history perhaps stimulus_history and stimuli + % are the same + %pd.stimulus=stimulus_history(:); + + pd.stim1dur=stim1dur(:); + + %pd.stimul=stim_history(:); + + sendsummary(obj,'protocol_data',pd); + + %% otherwise + otherwise, + warning('Unknown action! "%s"\n', action); +end; + +return; + diff --git a/Protocols/@SoundCatContinuous/SoundCatSMA - Copy.m b/Protocols/@SoundCatContinuous/SoundCatSMA - Copy.m new file mode 100644 index 00000000..265125ce --- /dev/null +++ b/Protocols/@SoundCatContinuous/SoundCatSMA - Copy.m @@ -0,0 +1,424 @@ + +function [varargout] = SoundCatSMA(obj, action) + +GetSoloFunctionArgs; + + +switch action + + case 'init' + + srate=SoundManagerSection(obj,'get_sample_rate'); + freq1=5; + dur1=1.5*1000; + Vol=1; + tw=Vol*(MakeBupperSwoop(srate,0, freq1 , freq1 , dur1/2 , dur1/2,0,0.1)); + SoundManagerSection(obj, 'declare_new_sound', 'LRewardSound', [tw ; zeros(1, length(tw))]) + SoundManagerSection(obj, 'declare_new_sound', 'RRewardSound', [zeros(1, length(tw));tw]) + SoundManagerSection(obj, 'send_not_yet_uploaded_sounds'); + + case 'prepare_next_trial', + + %% Setup water + min_time= 2.5E-4; % This is less than the minumum time allowed for a state transition. + + left1led = bSettings('get', 'DIOLINES', 'left1led'); + center1led = bSettings('get', 'DIOLINES', 'center1led'); + right1led = bSettings('get', 'DIOLINES', 'right1led'); + left1water = bSettings('get', 'DIOLINES', 'left1water'); + right1water = bSettings('get', 'DIOLINES', 'right1water'); + + + %% Setup sounds + sone_sound_id = SoundManagerSection(obj, 'get_sound_id', 'SOneSound'); + go_sound_id = SoundManagerSection(obj, 'get_sound_id', 'GoSound'); + go_cue_duration = value(time_go_cue); %SoundManagerSection(obj, 'get_sound_duration', 'GoSound'); + RLreward_sound_id = SoundManagerSection(obj, 'get_sound_id', 'RewardSound'); + err_sound_id = SoundManagerSection(obj, 'get_sound_id', 'ErrorSound'); + viol_sound_id = SoundManagerSection(obj, 'get_sound_id', 'ViolationSound'); + viol_snd_duration = SoundManagerSection(obj, 'get_sound_duration', 'ViolationSound'); + to_sound_id = SoundManagerSection(obj, 'get_sound_id', 'TimeoutSound'); + timeout_duration = SoundManagerSection(obj, 'get_sound_duration', 'TimeoutSound'); + Lreward_sound_id = SoundManagerSection(obj, 'get_sound_id', 'LRewardSound'); + Rreward_sound_id = SoundManagerSection(obj, 'get_sound_id', 'RRewardSound'); + + + A1_sound_id = SoundManagerSection(obj, 'get_sound_id', 'StimAUD1'); + + %% Declare variables + % These will get moved to other functions as SoloParamHandles. + + WaterAmount=maxasymp + (minasymp./(1+(n_done_trials/inflp).^slp).^assym); +% WaterValvesSection(obj, 'set_water_amounts', WaterAmount, WaterAmount); +% [LeftWValveTime RightWValveTime] = WaterValvesSection(obj, 'get_water_times'); + WValveTimes = GetValveTimes(WaterAmount, [2 3]); + LeftWValveTime = WValveTimes(1); + RightWValveTime = WValveTimes(2); + [LeftWMult RightWMult] = SideSection(obj, 'get_water_mult'); + LeftWValveTime=LeftWValveTime*LeftWMult; + RightWValveTime=RightWValveTime*RightWMult; + + side = SideSection(obj, 'get_current_side'); + if side == 'l' + HitEvent = 'Lin'; ErrorEvent = 'Rin'; + HitState = 'lefthit'; SideLight = left1led; + SecondHitState = 'secondlefthit'; + + else + HitEvent = 'Rin'; ErrorEvent = 'Lin'; + HitState = 'righthit'; SideLight = right1led; + SecondHitState = 'secondrighthit'; + end; + + + if strcmp(reward_type, 'Always') + LEDOn=1; + AnyReward=1; + wait_for_second_hit=30000; + error_iti=0; + else + LEDOn=0; + if strcmp(reward_type, 'NoReward') + AnyReward=0; + wait_for_second_hit=error_iti; + else + AnyReward=1; + wait_for_second_hit=30000; + error_iti=0; + end + end; + + + sma = StateMachineAssembler('full_trial_structure','use_happenings', 1); + + sma = add_scheduled_wave(sma, 'name', 'center_poke', 'preamble', CP_duration, ... + 'sustain', go_cue_duration, 'sound_trig', go_sound_id); + + sma = add_scheduled_wave(sma, 'name', 'settling_period', 'preamble', SettlingIn_time); + + + % to modify it for widefield imagine + if value(imaging)==1 && ~isnan(bSettings('get', 'DIOLINES', 'scope')); + trigscope = bSettings('get', 'DIOLINES', 'scope'); + else + trigscope = nan; + end + + if value(imaging)==1 + sma = add_scheduled_wave(sma, 'name', 'TrigScope', 'preamble', 0, 'sustain', ... + 0.5, 'DOut', trigscope, 'loop', 0); %for Ephys +% 99999, 'DOut', trigscope, 'loop', 0); %for Miniscope Camera + + else + sma = add_scheduled_wave(sma, 'name', 'TrigScope', 'preamble', 0, 'sustain', 0); %dummy wave. + end + + % for video imagine + if ~isnan(bSettings('get', 'DIOLINES', 'video')) + trigvideo = bSettings('get', 'DIOLINES', 'video'); + else + trigvideo = nan; + end + + sma = add_scheduled_wave(sma, 'name', 'TrigVideo', 'preamble', 0, 'sustain', 99999, 'DOut', trigvideo); %dummy wave. + + + % ---BEGIN: for training stage 0 only--- + if side=='l', + sma = add_scheduled_wave(sma, 'name', 'reward_delivery', 'preamble', reward_delay, ... + 'sustain', LeftWValveTime, 'DOut', left1water); + reward_sound_id=Lreward_sound_id; + else + sma = add_scheduled_wave(sma, 'name', 'reward_delivery', 'preamble', reward_delay, ... + 'sustain', RightWValveTime, 'DOut', right1water); + reward_sound_id=Rreward_sound_id; + end; + % ---END: for training stage 0 only--- + + sma = add_scheduled_wave(sma, 'name', 'stimA1', 'preamble', PreStim_time, ... + 'sustain', A1_time, 'sound_trig', A1_sound_id); + + + + switch value(training_stage) + + case 0 %% learning the reward sound association -left or right led on -> poke -> sound+reward + sma = add_state(sma, 'name', 'sideled_on', 'self_timer', SideLed_duration, ... + 'output_actions', {'DOut', SideLight}, ... + 'input_to_statechange',{'Tup','wait_for_collecting_reward'}); + + sma = add_state(sma, 'name', 'wait_for_collecting_reward', 'self_timer', RewardCollection_duration, ... + 'output_actions', {'DOut', SideLight}, ... + 'input_to_statechange',{HitEvent,'hit_state','Tup','wait_for_collecting_reward',ErrorEvent,'second_hit_state'}); + + sma = add_state(sma,'name','second_hit_state','self_timer',RewardCollection_duration,... + 'output_actions',{'DOut', SideLight},... + 'input_to_statechange',{'Tup','second_hit_state',HitEvent,'hit_state'}); + + sma = add_state(sma,'name','hit_state','self_timer',0.01,... + 'output_actions', {'DOut', SideLight,'SchedWaveTrig','reward_delivery','SoundOut',reward_sound_id},... + 'input_to_statechange',{'Tup','drink_state'}); + + + case 1 %% center led on -> poke in the center -> go cue -> reward light and sound -- waiting time grows slowlly -stimuli can be present + + sma = add_state(sma,'name','wait_for_cpoke','self_timer',CenterLed_duration, ... + 'output_actions', {'DOut', center1led, 'SchedWaveTrig','+TrigScope+TrigVideo'}, ... + 'input_to_statechange', {'Cin','cp';'Tup','timeout_state'}); + + if stimuli_on ==0 || n_done_trials <1 + % center poke starts: trigger center_poke scheduled wave, + % and when that ends go to side_led_on + sma = add_state(sma,'name','cp','self_timer', SettlingIn_time, ... + 'output_actions', {'SchedWaveTrig', 'center_poke + settling_period'}, ... + 'input_to_statechange', {'Tup', 'cp_legal_cbreak_period', ... + 'Cout','current_state+1', ... + 'center_poke_Out', 'wait_for_collecting_reward', ... + 'Rin', 'violation_state', ... + 'Rout', 'violation_state', ... + 'Lin', 'violation_state', ... + 'Lout', 'violation_state'}); + else + + % center poke starts: trigger center_poke scheduled wave, + % and when that ends go to side_led_on + sma = add_state(sma,'name','cp','self_timer', SettlingIn_time+0.00001, ... + 'output_actions', {'SchedWaveTrig', 'center_poke + settling_period +stimA1'}, ... + 'input_to_statechange', {'Tup', 'cp_legal_cbreak_period', ... + 'Cout','current_state+1', ... + 'center_poke_Out', 'wait_for_collecting_reward', ... + 'Rin', 'violation_state', ... + 'Rout', 'violation_state', ... + 'Lin', 'violation_state', ... + 'Lout', 'violation_state'}); + end + + + % nose is out and we're in "SettlingIn_time": + % if settling_legal_cbreak time elapses, go to violation state, + % if nose is put back in, go to copy of cp start + % when SettlingIn_time elapses (settling_period_In) "legal cbreaks" changes to usueal legal_cbreaks + sma = add_state(sma, 'self_timer', settling_legal_cbreak+0.00001, ... + 'output_actions', {'DOut', center1led*LED_during_settling_legal_cbreak}, ... + 'input_to_statechange', {'Tup', 'violation_state', ... + 'Cin', 'current_state+1', ... + 'settling_period_In', 'cp_legal_cbreak_period', ... + 'center_poke_Out', 'wait_for_collecting_reward', ... + 'Rin', 'violation_state', ... + 'Rout', 'violation_state', ... + 'Lin', 'violation_state', ... + 'Lout', 'violation_state'}); + + % center poke: + % A copy of two states above, but without triggering the + % start of the center_poke scheduled wave. + sma = add_state(sma, 'self_timer', 10000, ... + 'input_to_statechange', {'Cout', 'current_state-1', ... + 'settling_period_In','cp_legal_cbreak_period', ... + 'center_poke_Out', 'wait_for_collecting_reward', ... + 'Rin', 'violation_state', ... + 'Rout', 'violation_state', ... + 'Lin', 'violation_state', ... + 'Lout', 'violation_state'}); + + % SettlingIn_time elapsed and from now on cbreaks are treated given legal_cbreaks + sma = add_state(sma,'name','cp_legal_cbreak_period', 'self_timer', 10000, ... + 'input_to_statechange', {'Cout', 'current_state+1', ... + 'Clo', 'current_state+1', ... + 'center_poke_Out', 'wait_for_collecting_reward', ... + 'Rin', 'violation_state', ... + 'Rout', 'violation_state', ... + 'Lin', 'violation_state', ... + 'Lout', 'violation_state'}); + + % nose is out and we're still in legal_cbreak: + % if legal_cbreak time elapses, go to violation_state, + % if nose is put back in, go to copy of cp start + sma = add_state(sma, 'self_timer', legal_cbreak+0.00001, ... + 'output_actions', {'DOut', center1led*LED_during_legal_cbreak}, ... + 'input_to_statechange', {'Tup', 'violation_state', ... + 'Cin', 'current_state+1', ... + 'center_poke_Out', 'wait_for_collecting_reward', ... + 'Rin', 'violation_state', ... + 'Rout', 'violation_state', ... + 'Lin', 'violation_state', ... + 'Lout', 'violation_state'}); + + % center poke: + % A copy of two states above, but without triggering the + % start of the center_poke scheduled wave. + sma = add_state(sma, 'self_timer', 10000, ... + 'input_to_statechange', {'Cout', 'current_state-1', ... + 'center_poke_Out', 'wait_for_collecting_reward', ... + 'Rin', 'violation_state', ... + 'Rout', 'violation_state', ... + 'Lin', 'violation_state', ... + 'Lout', 'violation_state'}); + + + sma = add_state(sma, 'name', 'wait_for_collecting_reward', 'self_timer', 30000, ... + 'output_actions', {'DOut', LEDOn*SideLight}, ... + 'input_to_statechange',{HitEvent, HitState, ErrorEvent, 'second_hit_state'}); + + % The two states that make a LeftHit: + %with reward sound + +% sma = add_state(sma,'name', 'lefthit','self_timer', reward_delay, ... +% 'output_actions', {'DOut', SideLight', 'SoundOut', Lreward_sound_id}, ... +% 'input_to_statechange', {'Tup', 'current_state+1'}); + + % without reward sound + sma = add_state(sma,'name', 'lefthit','self_timer', reward_delay, ... + 'output_actions', {'DOut', SideLight'}, ... + 'input_to_statechange', {'Tup', 'current_state+1'}); +% + sma = add_state(sma, 'self_timer', LeftWValveTime, ... + 'output_actions', {'DOut', SideLight+left1water,},... + 'input_to_statechange',{'Tup','hit_state'}); + + % The two states that make a RightHit: + + %with reward sound +% sma = add_state(sma,'name', 'righthit','self_timer', reward_delay, ... +% 'output_actions', {'DOut', SideLight', 'SoundOut', Rreward_sound_id}, ... +% 'input_to_statechange', {'Tup', 'current_state+1'}); + + % without reward sound + sma = add_state(sma,'name', 'righthit','self_timer', reward_delay, ... + 'output_actions', {'DOut', SideLight'}, ... + 'input_to_statechange', {'Tup', 'current_state+1'}); +% + sma = add_state(sma, 'self_timer', RightWValveTime, ... + 'output_actions', {'DOut', SideLight+right1water,},... + 'input_to_statechange',{'Tup','hit_state'}); + + + sma = add_state(sma,'name','second_hit_state','self_timer', wait_for_second_hit,... + 'output_actions',{'DOut', LEDOn*SideLight},... + 'input_to_statechange',{HitEvent, SecondHitState,'Tup','check_next_trial_ready'}); + + + % The two states that make a SecondLeftHit: + sma = add_state(sma,'name', 'secondlefthit','self_timer', secondhit_delay, ... + 'input_to_statechange', {'Tup', 'current_state+1'}); +% sma = add_state(sma, 'self_timer', reward_delay, ... +% 'output_actions', {'DOut', AnyReward*SideLight', 'SoundOut', AnyReward*Lreward_sound_id}, ... +% 'input_to_statechange', {'Tup', 'current_state+1'}); + sma = add_state(sma, 'self_timer', reward_delay, ... + 'output_actions', {'DOut', AnyReward*SideLight'}, ... + 'input_to_statechange', {'Tup', 'current_state+1'}); + sma = add_state(sma, 'self_timer', LeftWValveTime, ... + 'output_actions', {'DOut', AnyReward*(SideLight+left1water)},... + 'input_to_statechange',{'Tup','hit_state'}); + + % The two states that make a SecondRightHit: + sma = add_state(sma,'name', 'secondrighthit','self_timer', secondhit_delay, ... + 'input_to_statechange', {'Tup', 'current_state+1'}); +% sma = add_state(sma, 'self_timer', reward_delay, ... +% 'output_actions', {'DOut', AnyReward*SideLight', 'SoundOut', AnyReward*Rreward_sound_id}, ... +% 'input_to_statechange', {'Tup', 'current_state+1'}); + sma = add_state(sma, 'self_timer', reward_delay, ... + 'output_actions', {'DOut', AnyReward*SideLight'}, ... + 'input_to_statechange', {'Tup', 'current_state+1'}); + sma = add_state(sma, 'self_timer', RightWValveTime, ... + 'output_actions', {'DOut', AnyReward*(SideLight+right1water)},... + 'input_to_statechange',{'Tup','hit_state'}); + + % and a common hit_state that we flick through + sma = add_state(sma, 'name', 'hit_state', 'self_timer', 0.0001, ... + 'input_to_statechange', {'Tup', 'drink_state'}); + + + end %end of swith for different training_stages + + +% sma = add_state(sma,'name','drink_state','self_timer',AnyReward*drink_time+error_iti,... +% 'input_to_statechange',{'Tup','check_next_trial_ready'}); + + sma = add_state(sma,'name','drink_state','self_timer',AnyReward*drink_time+error_iti,... + 'input_to_statechange',{'Tup','preclean_up_state'}); + + if stimuli_on ==0 + sma = add_state(sma,'name','violation_state','self_timer',viol_snd_duration,... + 'output_actions',{'SchedWaveTrig', '-center_poke', ... + 'SoundOut',viol_sound_id, 'DOut', center1led},... + 'input_to_statechange', {'Tup', 'current_state+1'}); + else + + sma = add_state(sma,'name','violation_state','self_timer',viol_snd_duration,... + 'output_actions',{'SchedWaveTrig', '-center_poke-stimA1', ... + 'SoundOut',viol_sound_id, 'DOut', center1led},... + 'input_to_statechange', {'Tup', 'current_state+1'}); + end + + sma = add_state(sma, 'self_timer', max(0.001, violation_iti-viol_snd_duration), ... + 'input_to_statechange',{'Tup','preclean_up_state'}); + + sma = add_state(sma,'name','timeout_state','self_timer', timeout_duration,... + 'output_actions',{'SoundOut',to_sound_id},... + 'input_to_statechange',{'Tup','preclean_up_state'}); + + + sma = add_state(sma,'name','preclean_up_state','self_timer',0.5,... + 'output_actions',{ 'SchedWaveTrig','-TrigScope-TrigVideo'},... + 'input_to_statechange',{'Tup','check_next_trial_ready'}); + + varargout{2} = {'check_next_trial_ready'}; + + varargout{1} = sma; + + % Not all 'prepare_next_trial_states' are defined in all training + % stages. So we send to dispatcher only those states that are + % defined. + state_names = get_labels(sma); state_names = state_names(:,1); + prepare_next_trial_states = {'lefthit', 'righthit', 'hit_state','second_hit_state', 'error_state', 'violation_state','timeout_state'}; + + sma = StimulatorSection(obj,'prepare_next_trial',sma); + dispatcher('send_assembler', sma, intersect(state_names, prepare_next_trial_states)); + + case 'get_state_colors', + varargout{1} = struct( ... + 'wait_for_cpoke', [0.68 1 0.63], ... + 'cp', [0.63 1 0.94], ... + 'cp_legal_cbreak_period', [0.63 1 0.94]*0.8, ... + 'sideled_on', [1 0.79 0.63], ... + 'wait_for_collecting_reward', [0.53 0.78 1.00],... + 'righthit', [0.3 0.9 0], ... + 'lefthit', [0 0.9 0.3], ... + 'hit_state', [0.77 0.60 0.48], ... + 'second_hit_state', [0.25 0.45 0.48], ... + 'drink_state', [0 1 0], ... + 'error_state', [1 0.54 0.54], ... + 'violation_state', [0.31 0.48 0.30], ... + 'timeout_state', 0.8*[0.31 0.48 0.30]); + % 'go_cue_on', [0.63 1 0.94]*0.6, ... + % 'prerw_postcs', [0.25 0.45 0.48], ... + % 'lefthit', [0.53 0.78 1.00], ... + % 'lefthit_pasound', [0.53 0.78 1.00]*0.7, ... + % 'righthit', [0.52 1.0 0.60], ... + % 'righthit_pasound', [0.52 1.0 0.60]*0.7, ... + % 'warning', [0.3 0 0], ... + % 'danger', [0.5 0.05 0.05], ... + % 'hit', [0 1 0] + + + + + case 'reinit', + currfig = double(gcf); + + % Delete all SoloParamHandles who belong to this object and whose + % fullname starts with the name of this mfile: + delete_sphandle('owner', ['^@' class(obj) '$'], ... + 'fullname', ['^' mfilename]); + + + % Reinitialise at the original GUI position and figure: + feval(mfilename, obj, 'init'); + + % Restore the current figure: + figure(currfig); + + otherwise + warning('do not know how to do %s',action); +end \ No newline at end of file diff --git a/Protocols/@SoundCatContinuous/SoundCatSMA-30.m b/Protocols/@SoundCatContinuous/SoundCatSMA-30.m new file mode 100644 index 00000000..48e0983c --- /dev/null +++ b/Protocols/@SoundCatContinuous/SoundCatSMA-30.m @@ -0,0 +1,407 @@ + +function [varargout] = SoundCatSMA(obj, action) + +GetSoloFunctionArgs; + + +switch action + + case 'init' + + srate=SoundManagerSection(obj,'get_sample_rate'); + freq1=5; + dur1=1.5*1000; + Vol=0.01; + tw=Vol*(MakeBupperSwoop(srate,0, freq1 , freq1 , dur1/2 , dur1/2,0,0.1)); + SoundManagerSection(obj, 'declare_new_sound', 'LRewardSound', [tw ; zeros(1, length(tw))]) + SoundManagerSection(obj, 'declare_new_sound', 'RRewardSound', [zeros(1, length(tw));tw]) + SoundManagerSection(obj, 'send_not_yet_uploaded_sounds'); + + case 'prepare_next_trial', + + %% Setup water + min_time= 2.5E-4; % This is less than the minumum time allowed for a state transition. + + left1led = bSettings('get', 'DIOLINES', 'left1led'); + center1led = bSettings('get', 'DIOLINES', 'center1led'); + right1led = bSettings('get', 'DIOLINES', 'right1led'); + left1water = bSettings('get', 'DIOLINES', 'left1water'); + right1water = bSettings('get', 'DIOLINES', 'right1water'); + + + %% Setup sounds + sone_sound_id = SoundManagerSection(obj, 'get_sound_id', 'SOneSound'); + go_sound_id = SoundManagerSection(obj, 'get_sound_id', 'GoSound'); + go_cue_duration = value(time_go_cue); %SoundManagerSection(obj, 'get_sound_duration', 'GoSound'); + RLreward_sound_id = SoundManagerSection(obj, 'get_sound_id', 'RewardSound'); + err_sound_id = SoundManagerSection(obj, 'get_sound_id', 'ErrorSound'); + viol_sound_id = SoundManagerSection(obj, 'get_sound_id', 'ViolationSound'); + viol_snd_duration = SoundManagerSection(obj, 'get_sound_duration', 'ViolationSound'); + to_sound_id = SoundManagerSection(obj, 'get_sound_id', 'TimeoutSound'); + timeout_duration = SoundManagerSection(obj, 'get_sound_duration', 'TimeoutSound'); + Lreward_sound_id = SoundManagerSection(obj, 'get_sound_id', 'LRewardSound'); + Rreward_sound_id = SoundManagerSection(obj, 'get_sound_id', 'RRewardSound'); + + + A1_sound_id = SoundManagerSection(obj, 'get_sound_id', 'StimAUD1'); + + %% Declare variables + % These will get moved to other functions as SoloParamHandles. + + WaterAmount=maxasymp + (minasymp./(1+(n_done_trials/inflp).^slp).^assym); +% WaterValvesSection(obj, 'set_water_amounts', WaterAmount, WaterAmount); +% [LeftWValveTime RightWValveTime] = WaterValvesSection(obj, 'get_water_times'); + WValveTimes = GetValveTimes(WaterAmount, [2 3]); + LeftWValveTime = WValveTimes(1); + RightWValveTime = WValveTimes(2); + [LeftWMult RightWMult] = SideSection(obj, 'get_water_mult'); + LeftWValveTime=LeftWValveTime*LeftWMult; + RightWValveTime=RightWValveTime*RightWMult; + + side = SideSection(obj, 'get_current_side'); + if side == 'l' + HitEvent = 'Lin'; ErrorEvent = 'Rin'; + HitState = 'lefthit'; SideLight = left1led; + SecondHitState = 'secondlefthit'; + + else + HitEvent = 'Rin'; ErrorEvent = 'Lin'; + HitState = 'righthit'; SideLight = right1led; + SecondHitState = 'secondrighthit'; + end; + + + if strcmp(reward_type, 'Always') + LEDOn=1; + AnyReward=1; + wait_for_second_hit=30000; + error_iti=0; + else + LEDOn=0; + if strcmp(reward_type, 'NoReward') + AnyReward=0; + wait_for_second_hit=error_iti; + else + AnyReward=1; + wait_for_second_hit=30000; + error_iti=0; + end + end; + + + sma = StateMachineAssembler('full_trial_structure','use_happenings', 1); + + sma = add_scheduled_wave(sma, 'name', 'center_poke', 'preamble', CP_duration, ... + 'sustain', go_cue_duration, 'sound_trig', go_sound_id); + + sma = add_scheduled_wave(sma, 'name', 'settling_period', 'preamble', SettlingIn_time); + + + % to modify it for widefield imagine + if value(imaging)==1 && ~isnan(bSettings('get', 'DIOLINES', 'scope')); + trigscope = bSettings('get', 'DIOLINES', 'scope'); + else + trigscope = nan; + end + + if value(imaging)==1 + sma = add_scheduled_wave(sma, 'name', 'TrigScope', 'preamble', 0, 'sustain', ... + 0.5, 'DOut', trigscope, 'loop', 0); %for Miniscope Camera + %0.5, 'DOut', trigscope, 'loop', 0); for Ephys + else + sma = add_scheduled_wave(sma, 'name', 'TrigScope', 'preamble', 0, 'sustain', 0); %dummy wave. + end + + % ---BEGIN: for training stage 0 only--- + if side=='l', + sma = add_scheduled_wave(sma, 'name', 'reward_delivery', 'preamble', reward_delay, ... + 'sustain', LeftWValveTime, 'DOut', left1water); + reward_sound_id=Lreward_sound_id; + else + sma = add_scheduled_wave(sma, 'name', 'reward_delivery', 'preamble', reward_delay, ... + 'sustain', RightWValveTime, 'DOut', right1water); + reward_sound_id=Rreward_sound_id; + end; + % ---END: for training stage 0 only--- + + sma = add_scheduled_wave(sma, 'name', 'stimA1', 'preamble', PreStim_time, ... + 'sustain', A1_time, 'sound_trig', A1_sound_id); + + + + switch value(training_stage) + + case 0 %% learning the reward sound association -left or right led on -> poke -> sound+reward + sma = add_state(sma, 'name', 'sideled_on', 'self_timer', SideLed_duration, ... + 'output_actions', {'DOut', SideLight}, ... + 'input_to_statechange',{'Tup','wait_for_collecting_reward'}); + + sma = add_state(sma, 'name', 'wait_for_collecting_reward', 'self_timer', RewardCollection_duration, ... + 'output_actions', {'DOut', SideLight}, ... + 'input_to_statechange',{HitEvent,'hit_state','Tup','wait_for_collecting_reward',ErrorEvent,'second_hit_state'}); + + sma = add_state(sma,'name','second_hit_state','self_timer',RewardCollection_duration,... + 'output_actions',{'DOut', SideLight},... + 'input_to_statechange',{'Tup','second_hit_state',HitEvent,'hit_state'}); + + sma = add_state(sma,'name','hit_state','self_timer',0.01,... + 'output_actions', {'DOut', SideLight,'SchedWaveTrig','reward_delivery','SoundOut',reward_sound_id},... + 'input_to_statechange',{'Tup','drink_state'}); + + + case 1 %% center led on -> poke in the center -> go cue -> reward light and sound -- waiting time grows slowlly -stimuli can be present + + sma = add_state(sma,'name','wait_for_cpoke','self_timer',CenterLed_duration, ... + 'output_actions', {'DOut', center1led, 'SchedWaveTrig','+TrigScope'}, ... + 'input_to_statechange', {'Cin','cp';'Tup','timeout_state'}); + + if stimuli_on ==0 || n_done_trials <1 + % center poke starts: trigger center_poke scheduled wave, + % and when that ends go to side_led_on + sma = add_state(sma,'name','cp','self_timer', SettlingIn_time, ... + 'output_actions', {'SchedWaveTrig', 'center_poke + settling_period'}, ... + 'input_to_statechange', {'Tup', 'cp_legal_cbreak_period', ... + 'Cout','current_state+1', ... + 'center_poke_Out', 'wait_for_collecting_reward', ... + 'Rin', 'violation_state', ... + 'Rout', 'violation_state', ... + 'Lin', 'violation_state', ... + 'Lout', 'violation_state'}); + else + + % center poke starts: trigger center_poke scheduled wave, + % and when that ends go to side_led_on + sma = add_state(sma,'name','cp','self_timer', SettlingIn_time+0.00001, ... + 'output_actions', {'SchedWaveTrig', 'center_poke + settling_period +stimA1'}, ... + 'input_to_statechange', {'Tup', 'cp_legal_cbreak_period', ... + 'Cout','current_state+1', ... + 'center_poke_Out', 'wait_for_collecting_reward', ... + 'Rin', 'violation_state', ... + 'Rout', 'violation_state', ... + 'Lin', 'violation_state', ... + 'Lout', 'violation_state'}); + end + + + % nose is out and we're in "SettlingIn_time": + % if settling_legal_cbreak time elapses, go to violation state, + % if nose is put back in, go to copy of cp start + % when SettlingIn_time elapses (settling_period_In) "legal cbreaks" changes to usueal legal_cbreaks + sma = add_state(sma, 'self_timer', settling_legal_cbreak+0.00001, ... + 'output_actions', {'DOut', center1led*LED_during_settling_legal_cbreak}, ... + 'input_to_statechange', {'Tup', 'violation_state', ... + 'Cin', 'current_state+1', ... + 'settling_period_In', 'cp_legal_cbreak_period', ... + 'center_poke_Out', 'wait_for_collecting_reward', ... + 'Rin', 'violation_state', ... + 'Rout', 'violation_state', ... + 'Lin', 'violation_state', ... + 'Lout', 'violation_state'}); + + % center poke: + % A copy of two states above, but without triggering the + % start of the center_poke scheduled wave. + sma = add_state(sma, 'self_timer', 10000, ... + 'input_to_statechange', {'Cout', 'current_state-1', ... + 'settling_period_In','cp_legal_cbreak_period', ... + 'center_poke_Out', 'wait_for_collecting_reward', ... + 'Rin', 'violation_state', ... + 'Rout', 'violation_state', ... + 'Lin', 'violation_state', ... + 'Lout', 'violation_state'}); + + % SettlingIn_time elapsed and from now on cbreaks are treated given legal_cbreaks + sma = add_state(sma,'name','cp_legal_cbreak_period', 'self_timer', 10000, ... + 'input_to_statechange', {'Cout', 'current_state+1', ... + 'Clo', 'current_state+1', ... + 'center_poke_Out', 'wait_for_collecting_reward', ... + 'Rin', 'violation_state', ... + 'Rout', 'violation_state', ... + 'Lin', 'violation_state', ... + 'Lout', 'violation_state'}); + + % nose is out and we're still in legal_cbreak: + % if legal_cbreak time elapses, go to violation_state, + % if nose is put back in, go to copy of cp start + sma = add_state(sma, 'self_timer', legal_cbreak+0.00001, ... + 'output_actions', {'DOut', center1led*LED_during_legal_cbreak}, ... + 'input_to_statechange', {'Tup', 'violation_state', ... + 'Cin', 'current_state+1', ... + 'center_poke_Out', 'wait_for_collecting_reward', ... + 'Rin', 'violation_state', ... + 'Rout', 'violation_state', ... + 'Lin', 'violation_state', ... + 'Lout', 'violation_state'}); + + % center poke: + % A copy of two states above, but without triggering the + % start of the center_poke scheduled wave. + sma = add_state(sma, 'self_timer', 10000, ... + 'input_to_statechange', {'Cout', 'current_state-1', ... + 'center_poke_Out', 'wait_for_collecting_reward', ... + 'Rin', 'violation_state', ... + 'Rout', 'violation_state', ... + 'Lin', 'violation_state', ... + 'Lout', 'violation_state'}); + + + sma = add_state(sma, 'name', 'wait_for_collecting_reward', 'self_timer', 30000, ... + 'output_actions', {'DOut', LEDOn*SideLight}, ... + 'input_to_statechange',{HitEvent, HitState, ErrorEvent, 'second_hit_state'}); + + % The two states that make a LeftHit: + %with reward sound + + sma = add_state(sma,'name', 'lefthit','self_timer', reward_delay, ... + 'output_actions', {'DOut', SideLight', 'SoundOut', Lreward_sound_id}, ... + 'input_to_statechange', {'Tup', 'current_state+1'}); + + % without reward sound +% sma = add_state(sma,'name', 'lefthit','self_timer', reward_delay, ... +% 'output_actions', {'DOut', SideLight'}, ... +% 'input_to_statechange', {'Tup', 'current_state+1'}); +% + sma = add_state(sma, 'self_timer', LeftWValveTime, ... + 'output_actions', {'DOut', SideLight+left1water,},... + 'input_to_statechange',{'Tup','hit_state'}); + + % The two states that make a RightHit: + + %with reward sound + sma = add_state(sma,'name', 'righthit','self_timer', reward_delay, ... + 'output_actions', {'DOut', SideLight', 'SoundOut', Rreward_sound_id}, ... + 'input_to_statechange', {'Tup', 'current_state+1'}); + + % without reward sound +% sma = add_state(sma,'name', 'righthit','self_timer', reward_delay, ... +% 'output_actions', {'DOut', SideLight'}, ... +% 'input_to_statechange', {'Tup', 'current_state+1'}); +% + sma = add_state(sma, 'self_timer', RightWValveTime, ... + 'output_actions', {'DOut', SideLight+right1water,},... + 'input_to_statechange',{'Tup','hit_state'}); + + + sma = add_state(sma,'name','second_hit_state','self_timer', wait_for_second_hit,... + 'output_actions',{'DOut', LEDOn*SideLight},... + 'input_to_statechange',{HitEvent, SecondHitState,'Tup','check_next_trial_ready'}); + + + % The two states that make a SecondLeftHit: + sma = add_state(sma,'name', 'secondlefthit','self_timer', secondhit_delay, ... + 'input_to_statechange', {'Tup', 'current_state+1'}); + sma = add_state(sma, 'self_timer', reward_delay, ... + 'output_actions', {'DOut', AnyReward*SideLight', 'SoundOut', AnyReward*Lreward_sound_id}, ... + 'input_to_statechange', {'Tup', 'current_state+1'}); + sma = add_state(sma, 'self_timer', LeftWValveTime, ... + 'output_actions', {'DOut', AnyReward*(SideLight+left1water)},... + 'input_to_statechange',{'Tup','hit_state'}); + + % The two states that make a SecondRightHit: + sma = add_state(sma,'name', 'secondrighthit','self_timer', secondhit_delay, ... + 'input_to_statechange', {'Tup', 'current_state+1'}); + sma = add_state(sma, 'self_timer', reward_delay, ... + 'output_actions', {'DOut', AnyReward*SideLight', 'SoundOut', AnyReward*Rreward_sound_id}, ... + 'input_to_statechange', {'Tup', 'current_state+1'}); + sma = add_state(sma, 'self_timer', RightWValveTime, ... + 'output_actions', {'DOut', AnyReward*(SideLight+right1water)},... + 'input_to_statechange',{'Tup','hit_state'}); + + % and a common hit_state that we flick through + sma = add_state(sma, 'name', 'hit_state', 'self_timer', 0.0001, ... + 'input_to_statechange', {'Tup', 'drink_state'}); + + + end %end of swith for different training_stages + + +% sma = add_state(sma,'name','drink_state','self_timer',AnyReward*drink_time+error_iti,... +% 'input_to_statechange',{'Tup','check_next_trial_ready'}); + + sma = add_state(sma,'name','drink_state','self_timer',AnyReward*drink_time+error_iti,... + 'input_to_statechange',{'Tup','preclean_up_state'}); + + if stimuli_on ==0 + sma = add_state(sma,'name','violation_state','self_timer',viol_snd_duration,... + 'output_actions',{'SchedWaveTrig', '-center_poke', ... + 'SoundOut',viol_sound_id, 'DOut', center1led},... + 'input_to_statechange', {'Tup', 'current_state+1'}); + else + + sma = add_state(sma,'name','violation_state','self_timer',viol_snd_duration,... + 'output_actions',{'SchedWaveTrig', '-center_poke-stimA1', ... + 'SoundOut',viol_sound_id, 'DOut', center1led},... + 'input_to_statechange', {'Tup', 'current_state+1'}); + end + + sma = add_state(sma, 'self_timer', max(0.001, violation_iti-viol_snd_duration), ... + 'input_to_statechange',{'Tup','preclean_up_state'}); + + sma = add_state(sma,'name','timeout_state','self_timer', timeout_duration,... + 'output_actions',{'SoundOut',to_sound_id},... + 'input_to_statechange',{'Tup','preclean_up_state'}); + + + sma = add_state(sma,'name','preclean_up_state','self_timer',0.5,... + 'output_actions',{ 'SchedWaveTrig','-TrigScope'},... + 'input_to_statechange',{'Tup','check_next_trial_ready'}); + + varargout{2} = {'check_next_trial_ready'}; + + varargout{1} = sma; + + % Not all 'prepare_next_trial_states' are defined in all training + % stages. So we send to dispatcher only those states that are + % defined. + state_names = get_labels(sma); state_names = state_names(:,1); + prepare_next_trial_states = {'lefthit', 'righthit', 'hit_state','second_hit_state', 'error_state', 'violation_state','timeout_state'}; + + sma = StimulatorSection(obj,'prepare_next_trial',sma); + dispatcher('send_assembler', sma, intersect(state_names, prepare_next_trial_states)); + + case 'get_state_colors', + varargout{1} = struct( ... + 'wait_for_cpoke', [0.68 1 0.63], ... + 'cp', [0.63 1 0.94], ... + 'cp_legal_cbreak_period', [0.63 1 0.94]*0.8, ... + 'sideled_on', [1 0.79 0.63], ... + 'wait_for_collecting_reward', [0.53 0.78 1.00],... + 'righthit', [0.3 0.9 0], ... + 'lefthit', [0 0.9 0.3], ... + 'hit_state', [0.77 0.60 0.48], ... + 'second_hit_state', [0.25 0.45 0.48], ... + 'drink_state', [0 1 0], ... + 'error_state', [1 0.54 0.54], ... + 'violation_state', [0.31 0.48 0.30], ... + 'timeout_state', 0.8*[0.31 0.48 0.30]); + % 'go_cue_on', [0.63 1 0.94]*0.6, ... + % 'prerw_postcs', [0.25 0.45 0.48], ... + % 'lefthit', [0.53 0.78 1.00], ... + % 'lefthit_pasound', [0.53 0.78 1.00]*0.7, ... + % 'righthit', [0.52 1.0 0.60], ... + % 'righthit_pasound', [0.52 1.0 0.60]*0.7, ... + % 'warning', [0.3 0 0], ... + % 'danger', [0.5 0.05 0.05], ... + % 'hit', [0 1 0] + + + + + case 'reinit', + currfig = double(gcf); + + % Delete all SoloParamHandles who belong to this object and whose + % fullname starts with the name of this mfile: + delete_sphandle('owner', ['^@' class(obj) '$'], ... + 'fullname', ['^' mfilename]); + + + % Reinitialise at the original GUI position and figure: + feval(mfilename, obj, 'init'); + + % Restore the current figure: + figure(currfig); + + otherwise + warning('do not know how to do %s',action); +end \ No newline at end of file diff --git a/Protocols/@SoundCatContinuous/SoundCatSMA-video.m b/Protocols/@SoundCatContinuous/SoundCatSMA-video.m new file mode 100644 index 00000000..3ce436c5 --- /dev/null +++ b/Protocols/@SoundCatContinuous/SoundCatSMA-video.m @@ -0,0 +1,418 @@ + +function [varargout] = SoundCatSMA(obj, action) + +GetSoloFunctionArgs; + + +switch action + + case 'init' + + srate=SoundManagerSection(obj,'get_sample_rate'); + freq1=5; + dur1=1.5*1000; + Vol=1; + tw=Vol*(MakeBupperSwoop(srate,0, freq1 , freq1 , dur1/2 , dur1/2,0,0.1)); + SoundManagerSection(obj, 'declare_new_sound', 'LRewardSound', [tw ; zeros(1, length(tw))]) + SoundManagerSection(obj, 'declare_new_sound', 'RRewardSound', [zeros(1, length(tw));tw]) + SoundManagerSection(obj, 'send_not_yet_uploaded_sounds'); + + case 'prepare_next_trial', + + %% Setup water + min_time= 2.5E-4; % This is less than the minumum time allowed for a state transition. + + left1led = bSettings('get', 'DIOLINES', 'left1led'); + center1led = bSettings('get', 'DIOLINES', 'center1led'); + right1led = bSettings('get', 'DIOLINES', 'right1led'); + left1water = bSettings('get', 'DIOLINES', 'left1water'); + right1water = bSettings('get', 'DIOLINES', 'right1water'); + + + %% Setup sounds + sone_sound_id = SoundManagerSection(obj, 'get_sound_id', 'SOneSound'); + go_sound_id = SoundManagerSection(obj, 'get_sound_id', 'GoSound'); + go_cue_duration = value(time_go_cue); %SoundManagerSection(obj, 'get_sound_duration', 'GoSound'); + RLreward_sound_id = SoundManagerSection(obj, 'get_sound_id', 'RewardSound'); + err_sound_id = SoundManagerSection(obj, 'get_sound_id', 'ErrorSound'); + viol_sound_id = SoundManagerSection(obj, 'get_sound_id', 'ViolationSound'); + viol_snd_duration = SoundManagerSection(obj, 'get_sound_duration', 'ViolationSound'); + to_sound_id = SoundManagerSection(obj, 'get_sound_id', 'TimeoutSound'); + timeout_duration = SoundManagerSection(obj, 'get_sound_duration', 'TimeoutSound'); + Lreward_sound_id = SoundManagerSection(obj, 'get_sound_id', 'LRewardSound'); + Rreward_sound_id = SoundManagerSection(obj, 'get_sound_id', 'RRewardSound'); + + + A1_sound_id = SoundManagerSection(obj, 'get_sound_id', 'StimAUD1'); + + %% Declare variables + % These will get moved to other functions as SoloParamHandles. + + WaterAmount=maxasymp + (minasymp./(1+(n_done_trials/inflp).^slp).^assym); +% WaterValvesSection(obj, 'set_water_amounts', WaterAmount, WaterAmount); +% [LeftWValveTime RightWValveTime] = WaterValvesSection(obj, 'get_water_times'); + WValveTimes = GetValveTimes(WaterAmount, [2 3]); + LeftWValveTime = WValveTimes(1); + RightWValveTime = WValveTimes(2); + [LeftWMult RightWMult] = SideSection(obj, 'get_water_mult'); + LeftWValveTime=LeftWValveTime*LeftWMult; + RightWValveTime=RightWValveTime*RightWMult; + + side = SideSection(obj, 'get_current_side'); + if side == 'l' + HitEvent = 'Lin'; ErrorEvent = 'Rin'; + HitState = 'lefthit'; SideLight = left1led; + SecondHitState = 'secondlefthit'; + + else + HitEvent = 'Rin'; ErrorEvent = 'Lin'; + HitState = 'righthit'; SideLight = right1led; + SecondHitState = 'secondrighthit'; + end; + + + if strcmp(reward_type, 'Always') + LEDOn=1; + AnyReward=1; + wait_for_second_hit=30000; + error_iti=0; + else + LEDOn=0; + if strcmp(reward_type, 'NoReward') + AnyReward=0; + wait_for_second_hit=error_iti; + else + AnyReward=1; + wait_for_second_hit=30000; + error_iti=0; + end + end; + + + sma = StateMachineAssembler('full_trial_structure','use_happenings', 1); + + sma = add_scheduled_wave(sma, 'name', 'center_poke', 'preamble', CP_duration, ... + 'sustain', go_cue_duration, 'sound_trig', go_sound_id); + + sma = add_scheduled_wave(sma, 'name', 'settling_period', 'preamble', SettlingIn_time); + + + % to modify it for widefield imagine + if value(imaging)==1 && ~isnan(bSettings('get', 'DIOLINES', 'scope')); + trigscope = bSettings('get', 'DIOLINES', 'scope'); + else + trigscope = nan; + end + + if value(imaging)==1 + sma = add_scheduled_wave(sma, 'name', 'TrigScope', 'preamble', 0, 'sustain', ... + 0.5, 'DOut', trigscope, 'loop', 0); %for Ephys +% 99999, 'DOut', trigscope, 'loop', 0); %for Miniscope Camera + + else + sma = add_scheduled_wave(sma, 'name', 'TrigScope', 'preamble', 0, 'sustain', 0); %dummy wave. + end + + % for video imagine + if ~isnan(bSettings('get', 'DIOLINES', 'video')) + trigvideo = bSettings('get', 'DIOLINES', 'video'); + else + trigvideo = nan; + end + + sma = add_scheduled_wave(sma, 'name', 'TrigVideo', 'preamble', 0, 'sustain', 99999, 'DOut', trigvideo); + + + % ---BEGIN: for training stage 0 only--- + if side=='l', + sma = add_scheduled_wave(sma, 'name', 'reward_delivery', 'preamble', reward_delay, ... + 'sustain', LeftWValveTime, 'DOut', left1water); + reward_sound_id=Lreward_sound_id; + else + sma = add_scheduled_wave(sma, 'name', 'reward_delivery', 'preamble', reward_delay, ... + 'sustain', RightWValveTime, 'DOut', right1water); + reward_sound_id=Rreward_sound_id; + end; + % ---END: for training stage 0 only--- + + sma = add_scheduled_wave(sma, 'name', 'stimA1', 'preamble', PreStim_time, ... + 'sustain', A1_time, 'sound_trig', A1_sound_id); + + + + switch value(training_stage) + + case 0 %% learning the reward sound association -left or right led on -> poke -> sound+reward + sma = add_state(sma, 'name', 'sideled_on', 'self_timer', SideLed_duration, ... + 'output_actions', {'DOut', SideLight}, ... + 'input_to_statechange',{'Tup','wait_for_collecting_reward'}); + + sma = add_state(sma, 'name', 'wait_for_collecting_reward', 'self_timer', RewardCollection_duration, ... + 'output_actions', {'DOut', SideLight}, ... + 'input_to_statechange',{HitEvent,'hit_state','Tup','wait_for_collecting_reward',ErrorEvent,'second_hit_state'}); + + sma = add_state(sma,'name','second_hit_state','self_timer',RewardCollection_duration,... + 'output_actions',{'DOut', SideLight},... + 'input_to_statechange',{'Tup','second_hit_state',HitEvent,'hit_state'}); + + sma = add_state(sma,'name','hit_state','self_timer',0.01,... + 'output_actions', {'DOut', SideLight,'SchedWaveTrig','reward_delivery','SoundOut',reward_sound_id},... + 'input_to_statechange',{'Tup','drink_state'}); + + + case 1 %% center led on -> poke in the center -> go cue -> reward light and sound -- waiting time grows slowlly -stimuli can be present + + sma = add_state(sma,'name','wait_for_cpoke','self_timer',CenterLed_duration, ... + 'output_actions', {'DOut', center1led, 'SchedWaveTrig','+TrigScope+TrigVideo'}, ... + 'input_to_statechange', {'Cin','cp';'Tup','timeout_state'}); + + if stimuli_on ==0 || n_done_trials <1 + % center poke starts: trigger center_poke scheduled wave, + % and when that ends go to side_led_on + sma = add_state(sma,'name','cp','self_timer', SettlingIn_time, ... + 'output_actions', {'SchedWaveTrig', 'center_poke + settling_period'}, ... + 'input_to_statechange', {'Tup', 'cp_legal_cbreak_period', ... + 'Cout','current_state+1', ... + 'center_poke_Out', 'wait_for_collecting_reward', ... + 'Rin', 'violation_state', ... + 'Rout', 'violation_state', ... + 'Lin', 'violation_state', ... + 'Lout', 'violation_state'}); + else + + % center poke starts: trigger center_poke scheduled wave, + % and when that ends go to side_led_on + sma = add_state(sma,'name','cp','self_timer', SettlingIn_time+0.00001, ... + 'output_actions', {'SchedWaveTrig', 'center_poke + settling_period +stimA1'}, ... + 'input_to_statechange', {'Tup', 'cp_legal_cbreak_period', ... + 'Cout','current_state+1', ... + 'center_poke_Out', 'wait_for_collecting_reward', ... + 'Rin', 'violation_state', ... + 'Rout', 'violation_state', ... + 'Lin', 'violation_state', ... + 'Lout', 'violation_state'}); + end + + + % nose is out and we're in "SettlingIn_time": + % if settling_legal_cbreak time elapses, go to violation state, + % if nose is put back in, go to copy of cp start + % when SettlingIn_time elapses (settling_period_In) "legal cbreaks" changes to usueal legal_cbreaks + sma = add_state(sma, 'self_timer', settling_legal_cbreak+0.00001, ... + 'output_actions', {'DOut', center1led*LED_during_settling_legal_cbreak}, ... + 'input_to_statechange', {'Tup', 'violation_state', ... + 'Cin', 'current_state+1', ... + 'settling_period_In', 'cp_legal_cbreak_period', ... + 'center_poke_Out', 'wait_for_collecting_reward', ... + 'Rin', 'violation_state', ... + 'Rout', 'violation_state', ... + 'Lin', 'violation_state', ... + 'Lout', 'violation_state'}); + + % center poke: + % A copy of two states above, but without triggering the + % start of the center_poke scheduled wave. + sma = add_state(sma, 'self_timer', 10000, ... + 'input_to_statechange', {'Cout', 'current_state-1', ... + 'settling_period_In','cp_legal_cbreak_period', ... + 'center_poke_Out', 'wait_for_collecting_reward', ... + 'Rin', 'violation_state', ... + 'Rout', 'violation_state', ... + 'Lin', 'violation_state', ... + 'Lout', 'violation_state'}); + + % SettlingIn_time elapsed and from now on cbreaks are treated given legal_cbreaks + sma = add_state(sma,'name','cp_legal_cbreak_period', 'self_timer', 10000, ... + 'input_to_statechange', {'Cout', 'current_state+1', ... + 'Clo', 'current_state+1', ... + 'center_poke_Out', 'wait_for_collecting_reward', ... + 'Rin', 'violation_state', ... + 'Rout', 'violation_state', ... + 'Lin', 'violation_state', ... + 'Lout', 'violation_state'}); + + % nose is out and we're still in legal_cbreak: + % if legal_cbreak time elapses, go to violation_state, + % if nose is put back in, go to copy of cp start + sma = add_state(sma, 'self_timer', legal_cbreak+0.00001, ... + 'output_actions', {'DOut', center1led*LED_during_legal_cbreak}, ... + 'input_to_statechange', {'Tup', 'violation_state', ... + 'Cin', 'current_state+1', ... + 'center_poke_Out', 'wait_for_collecting_reward', ... + 'Rin', 'violation_state', ... + 'Rout', 'violation_state', ... + 'Lin', 'violation_state', ... + 'Lout', 'violation_state'}); + + % center poke: + % A copy of two states above, but without triggering the + % start of the center_poke scheduled wave. + sma = add_state(sma, 'self_timer', 10000, ... + 'input_to_statechange', {'Cout', 'current_state-1', ... + 'center_poke_Out', 'wait_for_collecting_reward', ... + 'Rin', 'violation_state', ... + 'Rout', 'violation_state', ... + 'Lin', 'violation_state', ... + 'Lout', 'violation_state'}); + + + sma = add_state(sma, 'name', 'wait_for_collecting_reward', 'self_timer', 30000, ... + 'output_actions', {'DOut', LEDOn*SideLight}, ... + 'input_to_statechange',{HitEvent, HitState, ErrorEvent, 'second_hit_state'}); + + % The two states that make a LeftHit: + %with reward sound + + sma = add_state(sma,'name', 'lefthit','self_timer', reward_delay, ... + 'output_actions', {'DOut', SideLight', 'SoundOut', Lreward_sound_id}, ... + 'input_to_statechange', {'Tup', 'current_state+1'}); + + % without reward sound +% sma = add_state(sma,'name', 'lefthit','self_timer', reward_delay, ... +% 'output_actions', {'DOut', SideLight'}, ... +% 'input_to_statechange', {'Tup', 'current_state+1'}); +% + sma = add_state(sma, 'self_timer', LeftWValveTime, ... + 'output_actions', {'DOut', SideLight+left1water,},... + 'input_to_statechange',{'Tup','hit_state'}); + + % The two states that make a RightHit: + + %with reward sound + sma = add_state(sma,'name', 'righthit','self_timer', reward_delay, ... + 'output_actions', {'DOut', SideLight', 'SoundOut', Rreward_sound_id}, ... + 'input_to_statechange', {'Tup', 'current_state+1'}); + + % without reward sound +% sma = add_state(sma,'name', 'righthit','self_timer', reward_delay, ... +% 'output_actions', {'DOut', SideLight'}, ... +% 'input_to_statechange', {'Tup', 'current_state+1'}); +% + sma = add_state(sma, 'self_timer', RightWValveTime, ... + 'output_actions', {'DOut', SideLight+right1water,},... + 'input_to_statechange',{'Tup','hit_state'}); + + + sma = add_state(sma,'name','second_hit_state','self_timer', wait_for_second_hit,... + 'output_actions',{'DOut', LEDOn*SideLight},... + 'input_to_statechange',{HitEvent, SecondHitState,'Tup','check_next_trial_ready'}); + + + % The two states that make a SecondLeftHit: + sma = add_state(sma,'name', 'secondlefthit','self_timer', secondhit_delay, ... + 'input_to_statechange', {'Tup', 'current_state+1'}); + sma = add_state(sma, 'self_timer', reward_delay, ... + 'output_actions', {'DOut', AnyReward*SideLight', 'SoundOut', AnyReward*Lreward_sound_id}, ... + 'input_to_statechange', {'Tup', 'current_state+1'}); + sma = add_state(sma, 'self_timer', LeftWValveTime, ... + 'output_actions', {'DOut', AnyReward*(SideLight+left1water)},... + 'input_to_statechange',{'Tup','hit_state'}); + + % The two states that make a SecondRightHit: + sma = add_state(sma,'name', 'secondrighthit','self_timer', secondhit_delay, ... + 'input_to_statechange', {'Tup', 'current_state+1'}); + sma = add_state(sma, 'self_timer', reward_delay, ... + 'output_actions', {'DOut', AnyReward*SideLight', 'SoundOut', AnyReward*Rreward_sound_id}, ... + 'input_to_statechange', {'Tup', 'current_state+1'}); + sma = add_state(sma, 'self_timer', RightWValveTime, ... + 'output_actions', {'DOut', AnyReward*(SideLight+right1water)},... + 'input_to_statechange',{'Tup','hit_state'}); + + % and a common hit_state that we flick through + sma = add_state(sma, 'name', 'hit_state', 'self_timer', 0.0001, ... + 'input_to_statechange', {'Tup', 'drink_state'}); + + + end %end of swith for different training_stages + + +% sma = add_state(sma,'name','drink_state','self_timer',AnyReward*drink_time+error_iti,... +% 'input_to_statechange',{'Tup','check_next_trial_ready'}); + + sma = add_state(sma,'name','drink_state','self_timer',AnyReward*drink_time+error_iti,... + 'input_to_statechange',{'Tup','preclean_up_state'}); + + if stimuli_on ==0 + sma = add_state(sma,'name','violation_state','self_timer',viol_snd_duration,... + 'output_actions',{'SchedWaveTrig', '-center_poke', ... + 'SoundOut',viol_sound_id, 'DOut', center1led},... + 'input_to_statechange', {'Tup', 'current_state+1'}); + else + + sma = add_state(sma,'name','violation_state','self_timer',viol_snd_duration,... + 'output_actions',{'SchedWaveTrig', '-center_poke-stimA1', ... + 'SoundOut',viol_sound_id, 'DOut', center1led},... + 'input_to_statechange', {'Tup', 'current_state+1'}); + end + + sma = add_state(sma, 'self_timer', max(0.001, violation_iti-viol_snd_duration), ... + 'input_to_statechange',{'Tup','preclean_up_state'}); + + sma = add_state(sma,'name','timeout_state','self_timer', timeout_duration,... + 'output_actions',{'SoundOut',to_sound_id},... + 'input_to_statechange',{'Tup','preclean_up_state'}); + + + sma = add_state(sma,'name','preclean_up_state','self_timer',0.5,... + 'output_actions',{ 'SchedWaveTrig','-TrigScope-TrigVideo'},... + 'input_to_statechange',{'Tup','check_next_trial_ready'}); + + varargout{2} = {'check_next_trial_ready'}; + + varargout{1} = sma; + + % Not all 'prepare_next_trial_states' are defined in all training + % stages. So we send to dispatcher only those states that are + % defined. + state_names = get_labels(sma); state_names = state_names(:,1); + prepare_next_trial_states = {'lefthit', 'righthit', 'hit_state','second_hit_state', 'error_state', 'violation_state','timeout_state'}; + + sma = StimulatorSection(obj,'prepare_next_trial',sma); + dispatcher('send_assembler', sma, intersect(state_names, prepare_next_trial_states)); + + case 'get_state_colors', + varargout{1} = struct( ... + 'wait_for_cpoke', [0.68 1 0.63], ... + 'cp', [0.63 1 0.94], ... + 'cp_legal_cbreak_period', [0.63 1 0.94]*0.8, ... + 'sideled_on', [1 0.79 0.63], ... + 'wait_for_collecting_reward', [0.53 0.78 1.00],... + 'righthit', [0.3 0.9 0], ... + 'lefthit', [0 0.9 0.3], ... + 'hit_state', [0.77 0.60 0.48], ... + 'second_hit_state', [0.25 0.45 0.48], ... + 'drink_state', [0 1 0], ... + 'error_state', [1 0.54 0.54], ... + 'violation_state', [0.31 0.48 0.30], ... + 'timeout_state', 0.8*[0.31 0.48 0.30]); + % 'go_cue_on', [0.63 1 0.94]*0.6, ... + % 'prerw_postcs', [0.25 0.45 0.48], ... + % 'lefthit', [0.53 0.78 1.00], ... + % 'lefthit_pasound', [0.53 0.78 1.00]*0.7, ... + % 'righthit', [0.52 1.0 0.60], ... + % 'righthit_pasound', [0.52 1.0 0.60]*0.7, ... + % 'warning', [0.3 0 0], ... + % 'danger', [0.5 0.05 0.05], ... + % 'hit', [0 1 0] + + + + + case 'reinit', + currfig = double(gcf); + + % Delete all SoloParamHandles who belong to this object and whose + % fullname starts with the name of this mfile: + delete_sphandle('owner', ['^@' class(obj) '$'], ... + 'fullname', ['^' mfilename]); + + + % Reinitialise at the original GUI position and figure: + feval(mfilename, obj, 'init'); + + % Restore the current figure: + figure(currfig); + + otherwise + warning('do not know how to do %s',action); +end \ No newline at end of file diff --git a/Protocols/@SoundCatContinuous/SoundCatSMA.m b/Protocols/@SoundCatContinuous/SoundCatSMA.m new file mode 100644 index 00000000..265125ce --- /dev/null +++ b/Protocols/@SoundCatContinuous/SoundCatSMA.m @@ -0,0 +1,424 @@ + +function [varargout] = SoundCatSMA(obj, action) + +GetSoloFunctionArgs; + + +switch action + + case 'init' + + srate=SoundManagerSection(obj,'get_sample_rate'); + freq1=5; + dur1=1.5*1000; + Vol=1; + tw=Vol*(MakeBupperSwoop(srate,0, freq1 , freq1 , dur1/2 , dur1/2,0,0.1)); + SoundManagerSection(obj, 'declare_new_sound', 'LRewardSound', [tw ; zeros(1, length(tw))]) + SoundManagerSection(obj, 'declare_new_sound', 'RRewardSound', [zeros(1, length(tw));tw]) + SoundManagerSection(obj, 'send_not_yet_uploaded_sounds'); + + case 'prepare_next_trial', + + %% Setup water + min_time= 2.5E-4; % This is less than the minumum time allowed for a state transition. + + left1led = bSettings('get', 'DIOLINES', 'left1led'); + center1led = bSettings('get', 'DIOLINES', 'center1led'); + right1led = bSettings('get', 'DIOLINES', 'right1led'); + left1water = bSettings('get', 'DIOLINES', 'left1water'); + right1water = bSettings('get', 'DIOLINES', 'right1water'); + + + %% Setup sounds + sone_sound_id = SoundManagerSection(obj, 'get_sound_id', 'SOneSound'); + go_sound_id = SoundManagerSection(obj, 'get_sound_id', 'GoSound'); + go_cue_duration = value(time_go_cue); %SoundManagerSection(obj, 'get_sound_duration', 'GoSound'); + RLreward_sound_id = SoundManagerSection(obj, 'get_sound_id', 'RewardSound'); + err_sound_id = SoundManagerSection(obj, 'get_sound_id', 'ErrorSound'); + viol_sound_id = SoundManagerSection(obj, 'get_sound_id', 'ViolationSound'); + viol_snd_duration = SoundManagerSection(obj, 'get_sound_duration', 'ViolationSound'); + to_sound_id = SoundManagerSection(obj, 'get_sound_id', 'TimeoutSound'); + timeout_duration = SoundManagerSection(obj, 'get_sound_duration', 'TimeoutSound'); + Lreward_sound_id = SoundManagerSection(obj, 'get_sound_id', 'LRewardSound'); + Rreward_sound_id = SoundManagerSection(obj, 'get_sound_id', 'RRewardSound'); + + + A1_sound_id = SoundManagerSection(obj, 'get_sound_id', 'StimAUD1'); + + %% Declare variables + % These will get moved to other functions as SoloParamHandles. + + WaterAmount=maxasymp + (minasymp./(1+(n_done_trials/inflp).^slp).^assym); +% WaterValvesSection(obj, 'set_water_amounts', WaterAmount, WaterAmount); +% [LeftWValveTime RightWValveTime] = WaterValvesSection(obj, 'get_water_times'); + WValveTimes = GetValveTimes(WaterAmount, [2 3]); + LeftWValveTime = WValveTimes(1); + RightWValveTime = WValveTimes(2); + [LeftWMult RightWMult] = SideSection(obj, 'get_water_mult'); + LeftWValveTime=LeftWValveTime*LeftWMult; + RightWValveTime=RightWValveTime*RightWMult; + + side = SideSection(obj, 'get_current_side'); + if side == 'l' + HitEvent = 'Lin'; ErrorEvent = 'Rin'; + HitState = 'lefthit'; SideLight = left1led; + SecondHitState = 'secondlefthit'; + + else + HitEvent = 'Rin'; ErrorEvent = 'Lin'; + HitState = 'righthit'; SideLight = right1led; + SecondHitState = 'secondrighthit'; + end; + + + if strcmp(reward_type, 'Always') + LEDOn=1; + AnyReward=1; + wait_for_second_hit=30000; + error_iti=0; + else + LEDOn=0; + if strcmp(reward_type, 'NoReward') + AnyReward=0; + wait_for_second_hit=error_iti; + else + AnyReward=1; + wait_for_second_hit=30000; + error_iti=0; + end + end; + + + sma = StateMachineAssembler('full_trial_structure','use_happenings', 1); + + sma = add_scheduled_wave(sma, 'name', 'center_poke', 'preamble', CP_duration, ... + 'sustain', go_cue_duration, 'sound_trig', go_sound_id); + + sma = add_scheduled_wave(sma, 'name', 'settling_period', 'preamble', SettlingIn_time); + + + % to modify it for widefield imagine + if value(imaging)==1 && ~isnan(bSettings('get', 'DIOLINES', 'scope')); + trigscope = bSettings('get', 'DIOLINES', 'scope'); + else + trigscope = nan; + end + + if value(imaging)==1 + sma = add_scheduled_wave(sma, 'name', 'TrigScope', 'preamble', 0, 'sustain', ... + 0.5, 'DOut', trigscope, 'loop', 0); %for Ephys +% 99999, 'DOut', trigscope, 'loop', 0); %for Miniscope Camera + + else + sma = add_scheduled_wave(sma, 'name', 'TrigScope', 'preamble', 0, 'sustain', 0); %dummy wave. + end + + % for video imagine + if ~isnan(bSettings('get', 'DIOLINES', 'video')) + trigvideo = bSettings('get', 'DIOLINES', 'video'); + else + trigvideo = nan; + end + + sma = add_scheduled_wave(sma, 'name', 'TrigVideo', 'preamble', 0, 'sustain', 99999, 'DOut', trigvideo); %dummy wave. + + + % ---BEGIN: for training stage 0 only--- + if side=='l', + sma = add_scheduled_wave(sma, 'name', 'reward_delivery', 'preamble', reward_delay, ... + 'sustain', LeftWValveTime, 'DOut', left1water); + reward_sound_id=Lreward_sound_id; + else + sma = add_scheduled_wave(sma, 'name', 'reward_delivery', 'preamble', reward_delay, ... + 'sustain', RightWValveTime, 'DOut', right1water); + reward_sound_id=Rreward_sound_id; + end; + % ---END: for training stage 0 only--- + + sma = add_scheduled_wave(sma, 'name', 'stimA1', 'preamble', PreStim_time, ... + 'sustain', A1_time, 'sound_trig', A1_sound_id); + + + + switch value(training_stage) + + case 0 %% learning the reward sound association -left or right led on -> poke -> sound+reward + sma = add_state(sma, 'name', 'sideled_on', 'self_timer', SideLed_duration, ... + 'output_actions', {'DOut', SideLight}, ... + 'input_to_statechange',{'Tup','wait_for_collecting_reward'}); + + sma = add_state(sma, 'name', 'wait_for_collecting_reward', 'self_timer', RewardCollection_duration, ... + 'output_actions', {'DOut', SideLight}, ... + 'input_to_statechange',{HitEvent,'hit_state','Tup','wait_for_collecting_reward',ErrorEvent,'second_hit_state'}); + + sma = add_state(sma,'name','second_hit_state','self_timer',RewardCollection_duration,... + 'output_actions',{'DOut', SideLight},... + 'input_to_statechange',{'Tup','second_hit_state',HitEvent,'hit_state'}); + + sma = add_state(sma,'name','hit_state','self_timer',0.01,... + 'output_actions', {'DOut', SideLight,'SchedWaveTrig','reward_delivery','SoundOut',reward_sound_id},... + 'input_to_statechange',{'Tup','drink_state'}); + + + case 1 %% center led on -> poke in the center -> go cue -> reward light and sound -- waiting time grows slowlly -stimuli can be present + + sma = add_state(sma,'name','wait_for_cpoke','self_timer',CenterLed_duration, ... + 'output_actions', {'DOut', center1led, 'SchedWaveTrig','+TrigScope+TrigVideo'}, ... + 'input_to_statechange', {'Cin','cp';'Tup','timeout_state'}); + + if stimuli_on ==0 || n_done_trials <1 + % center poke starts: trigger center_poke scheduled wave, + % and when that ends go to side_led_on + sma = add_state(sma,'name','cp','self_timer', SettlingIn_time, ... + 'output_actions', {'SchedWaveTrig', 'center_poke + settling_period'}, ... + 'input_to_statechange', {'Tup', 'cp_legal_cbreak_period', ... + 'Cout','current_state+1', ... + 'center_poke_Out', 'wait_for_collecting_reward', ... + 'Rin', 'violation_state', ... + 'Rout', 'violation_state', ... + 'Lin', 'violation_state', ... + 'Lout', 'violation_state'}); + else + + % center poke starts: trigger center_poke scheduled wave, + % and when that ends go to side_led_on + sma = add_state(sma,'name','cp','self_timer', SettlingIn_time+0.00001, ... + 'output_actions', {'SchedWaveTrig', 'center_poke + settling_period +stimA1'}, ... + 'input_to_statechange', {'Tup', 'cp_legal_cbreak_period', ... + 'Cout','current_state+1', ... + 'center_poke_Out', 'wait_for_collecting_reward', ... + 'Rin', 'violation_state', ... + 'Rout', 'violation_state', ... + 'Lin', 'violation_state', ... + 'Lout', 'violation_state'}); + end + + + % nose is out and we're in "SettlingIn_time": + % if settling_legal_cbreak time elapses, go to violation state, + % if nose is put back in, go to copy of cp start + % when SettlingIn_time elapses (settling_period_In) "legal cbreaks" changes to usueal legal_cbreaks + sma = add_state(sma, 'self_timer', settling_legal_cbreak+0.00001, ... + 'output_actions', {'DOut', center1led*LED_during_settling_legal_cbreak}, ... + 'input_to_statechange', {'Tup', 'violation_state', ... + 'Cin', 'current_state+1', ... + 'settling_period_In', 'cp_legal_cbreak_period', ... + 'center_poke_Out', 'wait_for_collecting_reward', ... + 'Rin', 'violation_state', ... + 'Rout', 'violation_state', ... + 'Lin', 'violation_state', ... + 'Lout', 'violation_state'}); + + % center poke: + % A copy of two states above, but without triggering the + % start of the center_poke scheduled wave. + sma = add_state(sma, 'self_timer', 10000, ... + 'input_to_statechange', {'Cout', 'current_state-1', ... + 'settling_period_In','cp_legal_cbreak_period', ... + 'center_poke_Out', 'wait_for_collecting_reward', ... + 'Rin', 'violation_state', ... + 'Rout', 'violation_state', ... + 'Lin', 'violation_state', ... + 'Lout', 'violation_state'}); + + % SettlingIn_time elapsed and from now on cbreaks are treated given legal_cbreaks + sma = add_state(sma,'name','cp_legal_cbreak_period', 'self_timer', 10000, ... + 'input_to_statechange', {'Cout', 'current_state+1', ... + 'Clo', 'current_state+1', ... + 'center_poke_Out', 'wait_for_collecting_reward', ... + 'Rin', 'violation_state', ... + 'Rout', 'violation_state', ... + 'Lin', 'violation_state', ... + 'Lout', 'violation_state'}); + + % nose is out and we're still in legal_cbreak: + % if legal_cbreak time elapses, go to violation_state, + % if nose is put back in, go to copy of cp start + sma = add_state(sma, 'self_timer', legal_cbreak+0.00001, ... + 'output_actions', {'DOut', center1led*LED_during_legal_cbreak}, ... + 'input_to_statechange', {'Tup', 'violation_state', ... + 'Cin', 'current_state+1', ... + 'center_poke_Out', 'wait_for_collecting_reward', ... + 'Rin', 'violation_state', ... + 'Rout', 'violation_state', ... + 'Lin', 'violation_state', ... + 'Lout', 'violation_state'}); + + % center poke: + % A copy of two states above, but without triggering the + % start of the center_poke scheduled wave. + sma = add_state(sma, 'self_timer', 10000, ... + 'input_to_statechange', {'Cout', 'current_state-1', ... + 'center_poke_Out', 'wait_for_collecting_reward', ... + 'Rin', 'violation_state', ... + 'Rout', 'violation_state', ... + 'Lin', 'violation_state', ... + 'Lout', 'violation_state'}); + + + sma = add_state(sma, 'name', 'wait_for_collecting_reward', 'self_timer', 30000, ... + 'output_actions', {'DOut', LEDOn*SideLight}, ... + 'input_to_statechange',{HitEvent, HitState, ErrorEvent, 'second_hit_state'}); + + % The two states that make a LeftHit: + %with reward sound + +% sma = add_state(sma,'name', 'lefthit','self_timer', reward_delay, ... +% 'output_actions', {'DOut', SideLight', 'SoundOut', Lreward_sound_id}, ... +% 'input_to_statechange', {'Tup', 'current_state+1'}); + + % without reward sound + sma = add_state(sma,'name', 'lefthit','self_timer', reward_delay, ... + 'output_actions', {'DOut', SideLight'}, ... + 'input_to_statechange', {'Tup', 'current_state+1'}); +% + sma = add_state(sma, 'self_timer', LeftWValveTime, ... + 'output_actions', {'DOut', SideLight+left1water,},... + 'input_to_statechange',{'Tup','hit_state'}); + + % The two states that make a RightHit: + + %with reward sound +% sma = add_state(sma,'name', 'righthit','self_timer', reward_delay, ... +% 'output_actions', {'DOut', SideLight', 'SoundOut', Rreward_sound_id}, ... +% 'input_to_statechange', {'Tup', 'current_state+1'}); + + % without reward sound + sma = add_state(sma,'name', 'righthit','self_timer', reward_delay, ... + 'output_actions', {'DOut', SideLight'}, ... + 'input_to_statechange', {'Tup', 'current_state+1'}); +% + sma = add_state(sma, 'self_timer', RightWValveTime, ... + 'output_actions', {'DOut', SideLight+right1water,},... + 'input_to_statechange',{'Tup','hit_state'}); + + + sma = add_state(sma,'name','second_hit_state','self_timer', wait_for_second_hit,... + 'output_actions',{'DOut', LEDOn*SideLight},... + 'input_to_statechange',{HitEvent, SecondHitState,'Tup','check_next_trial_ready'}); + + + % The two states that make a SecondLeftHit: + sma = add_state(sma,'name', 'secondlefthit','self_timer', secondhit_delay, ... + 'input_to_statechange', {'Tup', 'current_state+1'}); +% sma = add_state(sma, 'self_timer', reward_delay, ... +% 'output_actions', {'DOut', AnyReward*SideLight', 'SoundOut', AnyReward*Lreward_sound_id}, ... +% 'input_to_statechange', {'Tup', 'current_state+1'}); + sma = add_state(sma, 'self_timer', reward_delay, ... + 'output_actions', {'DOut', AnyReward*SideLight'}, ... + 'input_to_statechange', {'Tup', 'current_state+1'}); + sma = add_state(sma, 'self_timer', LeftWValveTime, ... + 'output_actions', {'DOut', AnyReward*(SideLight+left1water)},... + 'input_to_statechange',{'Tup','hit_state'}); + + % The two states that make a SecondRightHit: + sma = add_state(sma,'name', 'secondrighthit','self_timer', secondhit_delay, ... + 'input_to_statechange', {'Tup', 'current_state+1'}); +% sma = add_state(sma, 'self_timer', reward_delay, ... +% 'output_actions', {'DOut', AnyReward*SideLight', 'SoundOut', AnyReward*Rreward_sound_id}, ... +% 'input_to_statechange', {'Tup', 'current_state+1'}); + sma = add_state(sma, 'self_timer', reward_delay, ... + 'output_actions', {'DOut', AnyReward*SideLight'}, ... + 'input_to_statechange', {'Tup', 'current_state+1'}); + sma = add_state(sma, 'self_timer', RightWValveTime, ... + 'output_actions', {'DOut', AnyReward*(SideLight+right1water)},... + 'input_to_statechange',{'Tup','hit_state'}); + + % and a common hit_state that we flick through + sma = add_state(sma, 'name', 'hit_state', 'self_timer', 0.0001, ... + 'input_to_statechange', {'Tup', 'drink_state'}); + + + end %end of swith for different training_stages + + +% sma = add_state(sma,'name','drink_state','self_timer',AnyReward*drink_time+error_iti,... +% 'input_to_statechange',{'Tup','check_next_trial_ready'}); + + sma = add_state(sma,'name','drink_state','self_timer',AnyReward*drink_time+error_iti,... + 'input_to_statechange',{'Tup','preclean_up_state'}); + + if stimuli_on ==0 + sma = add_state(sma,'name','violation_state','self_timer',viol_snd_duration,... + 'output_actions',{'SchedWaveTrig', '-center_poke', ... + 'SoundOut',viol_sound_id, 'DOut', center1led},... + 'input_to_statechange', {'Tup', 'current_state+1'}); + else + + sma = add_state(sma,'name','violation_state','self_timer',viol_snd_duration,... + 'output_actions',{'SchedWaveTrig', '-center_poke-stimA1', ... + 'SoundOut',viol_sound_id, 'DOut', center1led},... + 'input_to_statechange', {'Tup', 'current_state+1'}); + end + + sma = add_state(sma, 'self_timer', max(0.001, violation_iti-viol_snd_duration), ... + 'input_to_statechange',{'Tup','preclean_up_state'}); + + sma = add_state(sma,'name','timeout_state','self_timer', timeout_duration,... + 'output_actions',{'SoundOut',to_sound_id},... + 'input_to_statechange',{'Tup','preclean_up_state'}); + + + sma = add_state(sma,'name','preclean_up_state','self_timer',0.5,... + 'output_actions',{ 'SchedWaveTrig','-TrigScope-TrigVideo'},... + 'input_to_statechange',{'Tup','check_next_trial_ready'}); + + varargout{2} = {'check_next_trial_ready'}; + + varargout{1} = sma; + + % Not all 'prepare_next_trial_states' are defined in all training + % stages. So we send to dispatcher only those states that are + % defined. + state_names = get_labels(sma); state_names = state_names(:,1); + prepare_next_trial_states = {'lefthit', 'righthit', 'hit_state','second_hit_state', 'error_state', 'violation_state','timeout_state'}; + + sma = StimulatorSection(obj,'prepare_next_trial',sma); + dispatcher('send_assembler', sma, intersect(state_names, prepare_next_trial_states)); + + case 'get_state_colors', + varargout{1} = struct( ... + 'wait_for_cpoke', [0.68 1 0.63], ... + 'cp', [0.63 1 0.94], ... + 'cp_legal_cbreak_period', [0.63 1 0.94]*0.8, ... + 'sideled_on', [1 0.79 0.63], ... + 'wait_for_collecting_reward', [0.53 0.78 1.00],... + 'righthit', [0.3 0.9 0], ... + 'lefthit', [0 0.9 0.3], ... + 'hit_state', [0.77 0.60 0.48], ... + 'second_hit_state', [0.25 0.45 0.48], ... + 'drink_state', [0 1 0], ... + 'error_state', [1 0.54 0.54], ... + 'violation_state', [0.31 0.48 0.30], ... + 'timeout_state', 0.8*[0.31 0.48 0.30]); + % 'go_cue_on', [0.63 1 0.94]*0.6, ... + % 'prerw_postcs', [0.25 0.45 0.48], ... + % 'lefthit', [0.53 0.78 1.00], ... + % 'lefthit_pasound', [0.53 0.78 1.00]*0.7, ... + % 'righthit', [0.52 1.0 0.60], ... + % 'righthit_pasound', [0.52 1.0 0.60]*0.7, ... + % 'warning', [0.3 0 0], ... + % 'danger', [0.5 0.05 0.05], ... + % 'hit', [0 1 0] + + + + + case 'reinit', + currfig = double(gcf); + + % Delete all SoloParamHandles who belong to this object and whose + % fullname starts with the name of this mfile: + delete_sphandle('owner', ['^@' class(obj) '$'], ... + 'fullname', ['^' mfilename]); + + + % Reinitialise at the original GUI position and figure: + feval(mfilename, obj, 'init'); + + % Restore the current figure: + figure(currfig); + + otherwise + warning('do not know how to do %s',action); +end \ No newline at end of file diff --git a/Protocols/@SoundCatContinuous/SoundCatSMART.m b/Protocols/@SoundCatContinuous/SoundCatSMART.m new file mode 100644 index 00000000..542366b4 --- /dev/null +++ b/Protocols/@SoundCatContinuous/SoundCatSMART.m @@ -0,0 +1,413 @@ + +function [varargout] = SoundCatSMA(obj, action) + +GetSoloFunctionArgs; + + +switch action + + case 'init' + + srate=SoundManagerSection(obj,'get_sample_rate'); + freq1=5; + dur1=1.5*1000; + Vol=1; + tw=Vol*(MakeBupperSwoop(srate,0, freq1 , freq1 , dur1/2 , dur1/2,0,0.1)); + SoundManagerSection(obj, 'declare_new_sound', 'LRewardSound', [tw ; zeros(1, length(tw))]) + SoundManagerSection(obj, 'declare_new_sound', 'RRewardSound', [zeros(1, length(tw));tw]) + SoundManagerSection(obj, 'send_not_yet_uploaded_sounds'); + + case 'prepare_next_trial', + + %% Setup water + min_time= 2.5E-4; % This is less than the minumum time allowed for a state transition. + + left1led = bSettings('get', 'DIOLINES', 'left1led'); + center1led = bSettings('get', 'DIOLINES', 'center1led'); + right1led = bSettings('get', 'DIOLINES', 'right1led'); + left1water = bSettings('get', 'DIOLINES', 'left1water'); + right1water = bSettings('get', 'DIOLINES', 'right1water'); + + + %% Setup sounds + sone_sound_id = SoundManagerSection(obj, 'get_sound_id', 'SOneSound'); + go_sound_id = SoundManagerSection(obj, 'get_sound_id', 'GoSound'); + go_cue_duration = value(time_go_cue); %SoundManagerSection(obj, 'get_sound_duration', 'GoSound'); + RLreward_sound_id = SoundManagerSection(obj, 'get_sound_id', 'RewardSound'); + err_sound_id = SoundManagerSection(obj, 'get_sound_id', 'ErrorSound'); + viol_sound_id = SoundManagerSection(obj, 'get_sound_id', 'ViolationSound'); + viol_snd_duration = SoundManagerSection(obj, 'get_sound_duration', 'ViolationSound'); + to_sound_id = SoundManagerSection(obj, 'get_sound_id', 'TimeoutSound'); + timeout_duration = SoundManagerSection(obj, 'get_sound_duration', 'TimeoutSound'); + Lreward_sound_id = SoundManagerSection(obj, 'get_sound_id', 'LRewardSound'); + Rreward_sound_id = SoundManagerSection(obj, 'get_sound_id', 'RRewardSound'); + + + A1_sound_id = SoundManagerSection(obj, 'get_sound_id', 'StimAUD1'); + + %% Declare variables + % These will get moved to other functions as SoloParamHandles. + + WaterAmount=maxasymp + (minasymp./(1+(n_done_trials/inflp).^slp).^assym); +% WaterValvesSection(obj, 'set_water_amounts', WaterAmount, WaterAmount); +% [LeftWValveTime RightWValveTime] = WaterValvesSection(obj, 'get_water_times'); + WValveTimes = GetValveTimes(WaterAmount, [2 3]); + LeftWValveTime = WValveTimes(1); + RightWValveTime = WValveTimes(2); + [LeftWMult RightWMult] = SideSection(obj, 'get_water_mult'); + LeftWValveTime=LeftWValveTime*LeftWMult; + RightWValveTime=RightWValveTime*RightWMult; + + side = SideSection(obj, 'get_current_side'); + if side == 'l' + HitEvent = 'Lin'; ErrorEvent = 'Rin'; + HitState = 'lefthit'; SideLight = left1led; + SecondHitState = 'secondlefthit'; + + else + HitEvent = 'Rin'; ErrorEvent = 'Lin'; + HitState = 'righthit'; SideLight = right1led; + SecondHitState = 'secondrighthit'; + end; + + + if strcmp(reward_type, 'Always') + LEDOn=1; + AnyReward=1; + wait_for_second_hit=30000; + error_iti=0; + else + LEDOn=0; + if strcmp(reward_type, 'NoReward') + AnyReward=0; + wait_for_second_hit=error_iti; + else + AnyReward=1; + wait_for_second_hit=30000; + error_iti=0; + end + end; + + + sma = StateMachineAssembler('full_trial_structure','use_happenings', 1); + + sma = add_scheduled_wave(sma, 'name', 'center_poke', 'preamble', CP_duration, ... + 'sustain', go_cue_duration, 'sound_trig', go_sound_id); + + sma = add_scheduled_wave(sma, 'name', 'settling_period', 'preamble', SettlingIn_time); + + + % to modify it for widefield imagine + if value(imaging)==1 && ~isnan(bSettings('get', 'DIOLINES', 'scope')); + trigscope = bSettings('get', 'DIOLINES', 'scope'); + else + trigscope = nan; + end + + if value(imaging)==1 + sma = add_scheduled_wave(sma, 'name', 'TrigScope', 'preamble', 0, 'sustain', ... + 0.5, 'DOut', trigscope, 'loop', 0); %for Ephys +% 99999, 'DOut', trigscope, 'loop', 0); %for Miniscope Camera + + else + sma = add_scheduled_wave(sma, 'name', 'TrigScope', 'preamble', 0, 'sustain', 0); %dummy wave. + end + + % for video imagine + if ~isnan(bSettings('get', 'DIOLINES', 'video')) + trigvideo = bSettings('get', 'DIOLINES', 'video'); + else + trigvideo = nan; + end + + sma = add_scheduled_wave(sma, 'name', 'TrigVideo', 'preamble', 0, 'sustain', 99999, 'DOut', trigvideo); %dummy wave. + + + % ---BEGIN: for training stage 0 only--- + if side=='l', + sma = add_scheduled_wave(sma, 'name', 'reward_delivery', 'preamble', reward_delay, ... + 'sustain', LeftWValveTime, 'DOut', left1water); + reward_sound_id=Lreward_sound_id; + else + sma = add_scheduled_wave(sma, 'name', 'reward_delivery', 'preamble', reward_delay, ... + 'sustain', RightWValveTime, 'DOut', right1water); + reward_sound_id=Rreward_sound_id; + end; + % ---END: for training stage 0 only--- + + sma = add_scheduled_wave(sma, 'name', 'stimA1', 'preamble', PreStim_time, ... + 'sustain', A1_time, 'sound_trig', A1_sound_id); + + + + switch value(training_stage) + + case 0 %% learning the reward sound association -left or right led on -> poke -> sound+reward + sma = add_state(sma, 'name', 'sideled_on', 'self_timer', SideLed_duration, ... + 'output_actions', {'DOut', SideLight}, ... + 'input_to_statechange',{'Tup','wait_for_collecting_reward'}); + + sma = add_state(sma, 'name', 'wait_for_collecting_reward', 'self_timer', RewardCollection_duration, ... + 'output_actions', {'DOut', SideLight}, ... + 'input_to_statechange',{HitEvent,'hit_state','Tup','wait_for_collecting_reward',ErrorEvent,'second_hit_state'}); + + sma = add_state(sma,'name','second_hit_state','self_timer',RewardCollection_duration,... + 'output_actions',{'DOut', SideLight},... + 'input_to_statechange',{'Tup','second_hit_state',HitEvent,'hit_state'}); + + sma = add_state(sma,'name','hit_state','self_timer',0.01,... + 'output_actions', {'DOut', SideLight,'SchedWaveTrig','reward_delivery','SoundOut',reward_sound_id},... + 'input_to_statechange',{'Tup','drink_state'}); + + + case 1 %% center led on -> poke in the center -> go cue -> reward light and sound -- waiting time grows slowlly -stimuli can be present + + sma = add_state(sma,'name','wait_for_cpoke','self_timer',CenterLed_duration, ... + 'output_actions', {'DOut', center1led, 'SchedWaveTrig','+TrigScope+TrigVideo'}, ... + 'input_to_statechange', {'Cin','cp';'Tup','timeout_state'}); + + + if stimuli_on ==0 || n_done_trials <1 + % center poke starts: trigger center_poke scheduled wave, + % and when that ends go to side_led_on + sma = add_state(sma,'name','cp','self_timer', 30000, ... + 'output_actions', {'SchedWaveTrig', 'center_poke'}, ... + 'input_to_statechange', {'Cout','wait_for_collecting_reward'}); + else + + % center poke starts: trigger center_poke scheduled wave, + % and when that ends go to side_led_on + sma = add_state(sma,'name','cp','self_timer', 30000, ... + 'output_actions', {'SchedWaveTrig', 'center_poke+stimA1'}, ... + 'input_to_statechange', {'Cout','wait_for_collecting_reward'}); + end + + +% % nose is out and we're in "SettlingIn_time": +% % if settling_legal_cbreak time elapses, go to violation state, +% % if nose is put back in, go to copy of cp start +% % when SettlingIn_time elapses (settling_period_In) "legal cbreaks" changes to usueal legal_cbreaks +% sma = add_state(sma, 'self_timer', settling_legal_cbreak+0.00001, ... +% 'output_actions', {'DOut', center1led*LED_during_settling_legal_cbreak}, ... +% 'input_to_statechange', {'Tup', 'violation_state', ... +% 'Cin', 'current_state+1', ... +% 'settling_period_In', 'cp_legal_cbreak_period', ... +% 'center_poke_Out', 'wait_for_collecting_reward', ... +% 'Rin', 'violation_state', ... +% 'Rout', 'violation_state', ... +% 'Lin', 'violation_state', ... +% 'Lout', 'violation_state'}); +% +% % center poke: +% % A copy of two states above, but without triggering the +% % start of the center_poke scheduled wave. +% sma = add_state(sma, 'self_timer', 10000, ... +% 'input_to_statechange', {'Cout', 'current_state-1', ... +% 'settling_period_In','cp_legal_cbreak_period', ... +% 'center_poke_Out', 'wait_for_collecting_reward', ... +% 'Rin', 'violation_state', ... +% 'Rout', 'violation_state', ... +% 'Lin', 'violation_state', ... +% 'Lout', 'violation_state'}); +% +% % SettlingIn_time elapsed and from now on cbreaks are treated given legal_cbreaks +% sma = add_state(sma,'name','cp_legal_cbreak_period', 'self_timer', 10000, ... +% 'input_to_statechange', {'Cout', 'current_state+1', ... +% 'Clo', 'current_state+1', ... +% 'center_poke_Out', 'wait_for_collecting_reward', ... +% 'Rin', 'violation_state', ... +% 'Rout', 'violation_state', ... +% 'Lin', 'violation_state', ... +% 'Lout', 'violation_state'}); +% +% % nose is out and we're still in legal_cbreak: +% % if legal_cbreak time elapses, go to violation_state, +% % if nose is put back in, go to copy of cp start +% sma = add_state(sma, 'self_timer', legal_cbreak+0.00001, ... +% 'output_actions', {'DOut', center1led*LED_during_legal_cbreak}, ... +% 'input_to_statechange', {'Tup', 'violation_state', ... +% 'Cin', 'current_state+1', ... +% 'center_poke_Out', 'wait_for_collecting_reward', ... +% 'Rin', 'violation_state', ... +% 'Rout', 'violation_state', ... +% 'Lin', 'violation_state', ... +% 'Lout', 'violation_state'}); +% +% % center poke: +% % A copy of two states above, but without triggering the +% % start of the center_poke scheduled wave. +% sma = add_state(sma, 'self_timer', 10000, ... +% 'input_to_statechange', {'Cout', 'current_state-1', ... +% 'center_poke_Out', 'wait_for_collecting_reward', ... +% 'Rin', 'violation_state', ... +% 'Rout', 'violation_state', ... +% 'Lin', 'violation_state', ... +% 'Lout', 'violation_state'}); + + + sma = add_state(sma, 'name', 'wait_for_collecting_reward', 'self_timer', 30000, ... + 'output_actions', {'DOut', LEDOn*SideLight, 'SchedWaveTrig','-stimA1'}, ... + 'input_to_statechange',{HitEvent, HitState, ErrorEvent, 'second_hit_state'}); + + % The two states that make a LeftHit: + %with reward sound + +% sma = add_state(sma,'name', 'lefthit','self_timer', reward_delay, ... +% 'output_actions', {'DOut', SideLight', 'SoundOut', Lreward_sound_id}, ... +% 'input_to_statechange', {'Tup', 'current_state+1'}); + + % without reward sound + sma = add_state(sma,'name', 'lefthit','self_timer', reward_delay, ... + 'output_actions', {'DOut', SideLight'}, ... + 'input_to_statechange', {'Tup', 'current_state+1'}); +% + sma = add_state(sma, 'self_timer', LeftWValveTime, ... + 'output_actions', {'DOut', SideLight+left1water,},... + 'input_to_statechange',{'Tup','hit_state'}); + + % The two states that make a RightHit: + + %with reward sound +% sma = add_state(sma,'name', 'righthit','self_timer', reward_delay, ... +% 'output_actions', {'DOut', SideLight', 'SoundOut', Rreward_sound_id}, ... +% 'input_to_statechange', {'Tup', 'current_state+1'}); + + % without reward sound + sma = add_state(sma,'name', 'righthit','self_timer', reward_delay, ... + 'output_actions', {'DOut', SideLight'}, ... + 'input_to_statechange', {'Tup', 'current_state+1'}); +% + sma = add_state(sma, 'self_timer', RightWValveTime, ... + 'output_actions', {'DOut', SideLight+right1water,},... + 'input_to_statechange',{'Tup','hit_state'}); + + + sma = add_state(sma,'name','second_hit_state','self_timer', wait_for_second_hit,... + 'output_actions',{'DOut', LEDOn*SideLight},... + 'input_to_statechange',{HitEvent, SecondHitState,'Tup','check_next_trial_ready'}); + + + % The two states that make a SecondLeftHit: + sma = add_state(sma,'name', 'secondlefthit','self_timer', secondhit_delay, ... + 'input_to_statechange', {'Tup', 'current_state+1'}); +% sma = add_state(sma, 'self_timer', reward_delay, ... +% 'output_actions', {'DOut', AnyReward*SideLight', 'SoundOut', AnyReward*Lreward_sound_id}, ... +% 'input_to_statechange', {'Tup', 'current_state+1'}); + sma = add_state(sma, 'self_timer', reward_delay, ... + 'output_actions', {'DOut', AnyReward*SideLight'}, ... + 'input_to_statechange', {'Tup', 'current_state+1'}); + sma = add_state(sma, 'self_timer', LeftWValveTime, ... + 'output_actions', {'DOut', AnyReward*(SideLight+left1water)},... + 'input_to_statechange',{'Tup','hit_state'}); + + % The two states that make a SecondRightHit: + sma = add_state(sma,'name', 'secondrighthit','self_timer', secondhit_delay, ... + 'input_to_statechange', {'Tup', 'current_state+1'}); +% sma = add_state(sma, 'self_timer', reward_delay, ... +% 'output_actions', {'DOut', AnyReward*SideLight', 'SoundOut', AnyReward*Rreward_sound_id}, ... +% 'input_to_statechange', {'Tup', 'current_state+1'}); + sma = add_state(sma, 'self_timer', reward_delay, ... + 'output_actions', {'DOut', AnyReward*SideLight'}, ... + 'input_to_statechange', {'Tup', 'current_state+1'}); + sma = add_state(sma, 'self_timer', RightWValveTime, ... + 'output_actions', {'DOut', AnyReward*(SideLight+right1water)},... + 'input_to_statechange',{'Tup','hit_state'}); + + % and a common hit_state that we flick through + sma = add_state(sma, 'name', 'hit_state', 'self_timer', 0.0001, ... + 'input_to_statechange', {'Tup', 'drink_state'}); + + + end %end of swith for different training_stages + + +% sma = add_state(sma,'name','drink_state','self_timer',AnyReward*drink_time+error_iti,... +% 'input_to_statechange',{'Tup','check_next_trial_ready'}); + + sma = add_state(sma,'name','drink_state','self_timer',AnyReward*drink_time+error_iti,... + 'input_to_statechange',{'Tup','preclean_up_state'}); + + if stimuli_on ==0 + sma = add_state(sma,'name','violation_state','self_timer',viol_snd_duration,... + 'output_actions',{'SchedWaveTrig', '-center_poke', ... + 'SoundOut',viol_sound_id, 'DOut', center1led},... + 'input_to_statechange', {'Tup', 'current_state+1'}); + else + + sma = add_state(sma,'name','violation_state','self_timer',viol_snd_duration,... + 'output_actions',{'SchedWaveTrig', '-center_poke-stimA1', ... + 'SoundOut',viol_sound_id, 'DOut', center1led},... + 'input_to_statechange', {'Tup', 'current_state+1'}); + end + + sma = add_state(sma, 'self_timer', max(0.001, violation_iti-viol_snd_duration), ... + 'input_to_statechange',{'Tup','preclean_up_state'}); + + sma = add_state(sma,'name','timeout_state','self_timer', timeout_duration,... + 'output_actions',{'SoundOut',to_sound_id},... + 'input_to_statechange',{'Tup','preclean_up_state'}); + + + sma = add_state(sma,'name','preclean_up_state','self_timer',0.5,... + 'output_actions',{ 'SchedWaveTrig','-TrigScope-TrigVideo'},... + 'input_to_statechange',{'Tup','check_next_trial_ready'}); + + varargout{2} = {'check_next_trial_ready'}; + + varargout{1} = sma; + + % Not all 'prepare_next_trial_states' are defined in all training + % stages. So we send to dispatcher only those states that are + % defined. + state_names = get_labels(sma); state_names = state_names(:,1); + prepare_next_trial_states = {'lefthit', 'righthit', 'hit_state','second_hit_state', 'error_state', 'violation_state','timeout_state'}; + + sma = StimulatorSection(obj,'prepare_next_trial',sma); + dispatcher('send_assembler', sma, intersect(state_names, prepare_next_trial_states)); + + case 'get_state_colors', + varargout{1} = struct( ... + 'wait_for_cpoke', [0.68 1 0.63], ... + 'cp', [0.63 1 0.94], ... + 'cp_legal_cbreak_period', [0.63 1 0.94]*0.8, ... + 'sideled_on', [1 0.79 0.63], ... + 'wait_for_collecting_reward', [0.53 0.78 1.00],... + 'righthit', [0.3 0.9 0], ... + 'lefthit', [0 0.9 0.3], ... + 'hit_state', [0.77 0.60 0.48], ... + 'second_hit_state', [0.25 0.45 0.48], ... + 'drink_state', [0 1 0], ... + 'error_state', [1 0.54 0.54], ... + 'violation_state', [0.31 0.48 0.30], ... + 'timeout_state', 0.8*[0.31 0.48 0.30]); + % 'go_cue_on', [0.63 1 0.94]*0.6, ... + % 'prerw_postcs', [0.25 0.45 0.48], ... + % 'lefthit', [0.53 0.78 1.00], ... + % 'lefthit_pasound', [0.53 0.78 1.00]*0.7, ... + % 'righthit', [0.52 1.0 0.60], ... + % 'righthit_pasound', [0.52 1.0 0.60]*0.7, ... + % 'warning', [0.3 0 0], ... + % 'danger', [0.5 0.05 0.05], ... + % 'hit', [0 1 0] + + + + + case 'reinit', + currfig = double(gcf); + + % Delete all SoloParamHandles who belong to this object and whose + % fullname starts with the name of this mfile: + delete_sphandle('owner', ['^@' class(obj) '$'], ... + 'fullname', ['^' mfilename]); + + + % Reinitialise at the original GUI position and figure: + feval(mfilename, obj, 'init'); + + % Restore the current figure: + figure(currfig); + + otherwise + warning('do not know how to do %s',action); +end \ No newline at end of file diff --git a/Protocols/@SoundCatContinuous/SoundSection.m b/Protocols/@SoundCatContinuous/SoundSection.m new file mode 100644 index 00000000..4e49253d --- /dev/null +++ b/Protocols/@SoundCatContinuous/SoundSection.m @@ -0,0 +1,63 @@ + + +function [x, y] = SoundSection(obj, action, varargin) + +GetSoloFunctionArgs(obj); + +switch action, + + % ------------------------------------------------------------------ + % INIT + % ------------------------------------------------------------------ + + case 'init' + if length(varargin) < 2, + error('Need at least two arguments, x and y position, to initialize %s', mfilename); + end; + x = varargin{1}; y = varargin{2}; + + ToggleParam(obj, 'SoundsShow', 0, x, y, 'OnString', 'Sounds', ... + 'OffString', 'Sounds', 'TooltipString', 'Show/Hide Sounds panel'); + set_callback(SoundsShow, {mfilename, 'show_hide'}); %#ok (Defined just above) + next_row(y); + oldx=x; oldy=y; parentfig=double(gcf); + + SoloParamHandle(obj, 'myfig', 'value', figure('Position', [100 100 560 440], ... + 'closerequestfcn', [mfilename '(' class(obj) ', ''hide'');'], 'MenuBar', 'none', ... + 'Name', mfilename), 'saveable', 0); + set(double(gcf), 'Visible', 'off'); + x=10;y=10; + + [x,y]=SoundInterface(obj,'add','ViolationSound',x,y,'Volume',0.005,'Freq',9000,'Duration',0.5); +% [x,y]=SoundInterface(obj,'add','ViolationSound',x,y,'Style','WhiteNoise','Volume',0.01); + [x,y]=SoundInterface(obj,'add','ErrorSound',x,y,'Style','WhiteNoise','Volume',0.08); + [x,y]=SoundInterface(obj,'add','TimeoutSound',x,y,'Style','WhiteNoise','Volume',0.08,'Duration',0.5); + next_column(x); + y=10; + [x,y]=SoundInterface(obj,'add','GoSound',x,y,'Style','Tone','Volume',0.005,'Freq',3000,'Duration',0.2); + SoundInterface(obj, 'disable', 'GoSound', 'Dur1'); + + [x,y]=SoundInterface(obj,'add','RewardSound',x,y,'Style','Bups','Volume',1,'Freq',5,'Duration',1.5); + [x,y]=SoundInterface(obj,'add','SOneSound',x,y,'Style','Tone','Volume',0.005,'Freq',3000,'Duration',0.2); + SoundInterface(obj, 'disable', 'GoSound', 'Dur1'); + SoundInterface(obj, 'disable', 'GoSound', 'Freq1'); + [x,y]=SoundInterface(obj,'add','STwoSound',x,y,'Style','WhiteNoise','Volume',0.08); + + + + x=oldx; y=oldy; + figure(parentfig); + + case 'hide', + SoundsShow.value = 0; set(value(myfig), 'Visible', 'off'); + + case 'show', + SoundsShow.value = 1; set(value(myfig), 'Visible', 'on'); + + case 'show_hide', + if SoundsShow == 1, set(value(myfig), 'Visible', 'on'); %#ok (defined by GetSoloFunctionArgs) + else set(value(myfig), 'Visible', 'off'); + end; + +end + \ No newline at end of file diff --git a/Protocols/@SoundCatContinuous/StimulatorSection.m b/Protocols/@SoundCatContinuous/StimulatorSection.m new file mode 100644 index 00000000..71ad0760 --- /dev/null +++ b/Protocols/@SoundCatContinuous/StimulatorSection.m @@ -0,0 +1,325 @@ +% +% PARAMETERS: +% ----------- +% +% obj Default object argument. +% +% action One of: +% +% 'prepare_next_trial' +% +% 'init' +% +% +% RETURNS: +% -------- +% +% +% +% +% +% + + + +function [varargout] = StimulatorSection(obj, action, x, y) + +GetSoloFunctionArgs(obj); + +switch action + +%% init +% ---------------------------------------------------------------- +% +% INIT +% +% ---------------------------------------------------------------- + + case 'init', + %NumeditParam(obj,'LCB_onstim', 0,x,y,'position',[x y 100 20],'labelfraction',0.6); + %NumeditParam(obj,'LCB_nostim', 0,x,y,'position',[x+100 y 100 20],'labelfraction',0.6); next_row(y); + %SoloParamHandle(obj,'LegalCBrk_temp', 'value',0); + + MenuParam(obj,'StimInterval',{'WholeTrial','S1','DelayDur','GoCue'},1,x,y,'labelfraction',0.30); next_row(y); + set_callback(StimInterval, {mfilename, 'StimInterval'}); + MenuParam(obj,'StimOnSide',{'both','left','right'},1,x,y,'labelfraction',0.3); next_row(y); + + diolines = bSettings('get','DIOLINES', 'all'); + for i = 1:size(diolines,1); dionames{i} = diolines{i,1}; dionums(i) = diolines{i,2}; end %#ok + [dionums order] = sort(dionums); + dionames2 = cell(0); + for i = 1:length(dionums); if ~isnan(dionums(i)); dionames2{end+1} = dionames{order(i)}; end; end %#ok + dionames2 = cell(0); + dionames2{1} = 'opto'; + + MenuParam(obj,'StimLine',dionames2,1,x,y,'labelfraction',0.30); next_row(y); + + SC = state_colors(obj); + WC = wave_colors(obj); + states = fieldnames(SC); + waves = fieldnames(WC); + states(2:end+1) = states; + states{1} = 'cp'; + states(end+1:end+length(waves)) = waves; + + MenuParam(obj,'StimState',states,1,x,y,'labelfraction',0.30); next_row(y); + + NumeditParam(obj,'StimStates', 1,x,y,'position',[x y 100 20],'labelfraction',0.60); + NumeditParam(obj,'StimLines', 1,x,y,'position',[x+100 y 100 20],'labelfraction',0.60); next_row(y); + + NumeditParam(obj,'StartDelay', 0,x,y,'position',[x y 100 20],'labelfraction',0.60); + NumeditParam(obj,'StimFreq', 20,x,y,'position',[x+100 y 100 20],'labelfraction',0.60); next_row(y); + NumeditParam(obj,'PulseWidth',15,x,y,'position',[x y 100 20],'labelfraction',0.60); + NumeditParam(obj,'NumPulses', 1,x,y,'position',[x+100 y 100 20],'labelfraction',0.60); next_row(y); + + DispParam(obj,'SD',0 ,x,y,'position',[x y 50 20],'labelfraction',0.4); + DispParam(obj,'SF',20,x,y,'position',[x+50 y 50 20],'labelfraction',0.4); + DispParam(obj,'PW',15,x,y,'position',[x+100 y 50 20],'labelfraction',0.4); + DispParam(obj,'NP',1,x,y,'position',[x+150 y 50 20],'labelfraction',0.4); next_row(y); + + NumeditParam(obj,'StimProb', 0,x,y,'position',[x y 100 20],'labelfraction',0.65); + ToggleParam( obj,'ShuffleValues',0,x,y,'position',[x+100 y 100 20],'OnString','Shuffle','OffString','Lock'); next_row(y); + + SoloParamHandle(obj, 'stimulator_history', 'value', []); + + SubheaderParam(obj, 'title', 'Stimulator Section', x, y); next_row(y); + varargout{1} = x; + varargout{2} = y; + +%% update_values +% ----------------------------------------------------------------------- +% +% UPDATE_VALUES +% +% ----------------------------------------------------------------------- + + case 'update_values', + + StimulatorSection(obj,'StimInterval'); + sh = value(stimulator_history) %#ok + %if n_done_trials == 0 || sh(end) == 0 + % LegalCBrk_temp.value = value(LegalCBrk); %#ok + %end + + if ~dispatcher('is_running'); + %dispatcher is not running, last stim_hist not used, lop it off + sh = sh(1:end-1); + end + + if value(StimProb) == 0 + %LCB_nostim.value = value(LegalCBrk); %#ok + stimulator_history.value = [sh, 0]; + elseif rand(1) <= value(StimProb) %&& ((value(StimOnFree)==1 && strcmp(value(ThisTrial_Free),'FREE')) || value(StimOnFree)==0) + stimulator_history.value = [sh, 1]; + %LegalCBrk.value = value(LCB_onstim); + else + %LegalCBrk.value = value(LCB_nostim); %#ok + stimulator_history.value = [sh, 0]; + end + + + +%% prepare_next_trial +% ----------------------------------------------------------------------- +% +% PREPARE_NEXT_TRIAL +% +% ----------------------------------------------------------------------- + + case 'prepare_next_trial', + sh = value(stimulator_history); %#ok + + sma = x; + + sd = value(StartDelay); + sf = value(StimFreq); + pw = value(PulseWidth); + np = value(NumPulses); + ss = value(StimStates) + sl = value(StimLines) + + if value(ShuffleValues) == 1 + sd = sd(ceil(rand(1) * length(sd))); + sf = sf(ceil(rand(1) * length(sf))); + pw = pw(ceil(rand(1) * length(pw))); + np = np(ceil(rand(1) * length(np))); + ss = ss(ceil(rand(1) * length(ss))); + sl = sl(ceil(rand(1) * length(sl))); + else + if length(unique([length(sd) length(sf) length(pw) length(np) length(ss) length(sl)])) > 1 + disp('Warning: param values in StimulatorSection have different lengths. Only first value will be used.'); + temp = 1; + else + temp = ceil(rand(1) * length(sd)); + end + sd = sd(temp); sf = sf(temp); pw = pw(temp); np = np(temp); ss = ss(temp); sl = sl(temp); + end + + pss = get(get_ghandle(StimState),'String'); %#ok + psl = get(get_ghandle(StimLine), 'String'); %#ok + if ss > length(pss) + disp('StimState value greater than list of possible stim states'); + else + StimState.value = ss; + disp('test ss') + value(ss) + end + + if sl > length(psl) + slc = ['0',num2str(sl),'0']; + z = find(slc == '0'); + if length(z) > 2 + sln = []; + for i = 1:length(z)-1 + sln(end+1) = str2num(slc(z(i)+1:z(i+1)-1)); %#ok + end + if any(sln > length(psl)) + disp('StimLine value greater than list of possible stim lines'); + else + slname = psl{sln(1)}; + for i=2:length(sln) + slname = [slname,'+',psl{sln(i)}]; %#ok + end + if sum(strcmp(psl,slname)) == 0 + psl{end+1} = slname; + set(get_ghandle(StimLine),'String',psl) + end + StimLine.value = find(strcmp(psl,slname)==1,1,'first'); + sl = sln; + end + else + disp('StimLine value greater than list of possible stim lines'); + end + else + StimLine.value = sl; + end + + for i = 1:length(sl) + stimline = bSettings('get','DIOLINES',psl{sl(i)}); + + sma = add_scheduled_wave(sma,... + 'name', ['stimulator_wave',num2str(i)],... + 'preamble', (1/sf)-(pw/1000),... %%%% Remember: change it such that if this is negative makes it 0 + 'sustain' , pw/1000,... + 'DOut', stimline,... + 'loop', np-1); + + if sd ~= 0 + sma = add_scheduled_wave(sma,... + 'name',['stimulator_wave_pause',num2str(i)],... + 'preamble',sd,... + 'trigger_on_up',['stimulator_wave',num2str(i)]); + else + sma = add_scheduled_wave(sma,... + 'name',['stimulator_wave_pause',num2str(i)],... + 'preamble',1,... + 'trigger_on_up',['stimulator_wave',num2str(i)]); + end + end + + for i = 1:length(sl) + if sh(end) == 1 + if strcmp(value(StimState),'none') == 0 + if sd ~= 0 + sma = add_stimulus(sma,['stimulator_wave_pause',num2str(i)],value(StimState)); + else + sma = add_stimulus(sma,['stimulator_wave',num2str(i)],value(StimState)); + end + + SD.value = sd; SF.value = sf; PW.value = pw; NP.value = np; + end + else + SD.value = 0; SF.value = 0; PW.value = 0; NP.value = 0; + end + end + + varargout{1} = sma; + + + %% Case StimInterval + case 'StimInterval' + + if strcmp(StimInterval, 'WholeTrial'); + PulseWidth.value = Total_CP_duration*1000; + StimFreq.value = 1000/PulseWidth; + StartDelay.value = PreStim_time; + elseif strcmp(StimInterval, 'S1'); + PulseWidth.value = A1_time*1000; + StimFreq.value = 1000/PulseWidth; + StartDelay.value = PreStim_time; + elseif strcmp(StimInterval, 'DelayDur'); + PulseWidth.value = time_bet_aud1_gocue*1000; + StimFreq.value = 1000/PulseWidth; + StartDelay.value = PreStim_time + A1_time; + elseif strcmp(StimInterval, 'GoCue'); + PulseWidth.value = time_go_cue*1000; + StimFreq.value = 1000/PulseWidth; + StartDelay.value = PreStim_time + A1_time + time_bet_aud1_gocue; + + end +%% set +% ----------------------------------------------------------------------- +% +% SET +% +% ----------------------------------------------------------------------- + case 'set' + varname = x; + newval = y; + + try + temp = 'SoloParamHandle'; %#ok + eval(['test = isa(',varname,',temp);']); + if test == 1 + eval([varname,'.value = newval;']); + end + catch %#ok + showerror; + warning(['Unable to assign value: ',num2str(newval),' to SoloParamHandle: ',varname]); %#ok + end + + +%% get +% ----------------------------------------------------------------------- +% +% GET +% +% ----------------------------------------------------------------------- + case 'get' + varname = x; + + try + temp = 'SoloParamHandle'; %#ok + eval(['test = isa(',varname,',temp);']); + if test == 1 + eval(['varargout{1} = value(',varname,');']); + end + catch %#ok + showerror; + warning(['Unable to get value from SoloParamHandle: ',varname]); %#ok + end + + +%% reinit +% ----------------------------------------------------------------------- +% +% REINIT +% +% ----------------------------------------------------------------------- + case 'reinit', + currfig = double(gcf); + + % Delete all SoloParamHandles who belong to this object and whose + % fullname starts with the name of this mfile: + delete_sphandle('owner', ['^@' class(obj) '$'], ... + 'fullname', ['^' mfilename]); + + + % Reinitialise at the original GUI position and figure: + feval(mfilename, obj, 'init'); + + % Restore the current figure: + figure(currfig); +end; + + diff --git a/Protocols/@SoundCatContinuous/StimulusSection.m b/Protocols/@SoundCatContinuous/StimulusSection.m new file mode 100644 index 00000000..daaed21f --- /dev/null +++ b/Protocols/@SoundCatContinuous/StimulusSection.m @@ -0,0 +1,435 @@ + + +function [x, y] = StimulusSection(obj, action, varargin) + +GetSoloFunctionArgs(obj); + +switch action, + + % ------------------------------------------------------------------ + % INIT + % ------------------------------------------------------------------ + + case 'init' + if length(varargin) < 2, + error('Need at least two arguments, x and y position, to initialize %s', mfilename); + end; + x = varargin{1}; y = varargin{2}; + + ToggleParam(obj, 'StimulusShow', 0, x, y, 'OnString', 'Stimuli', ... + 'OffString', 'Stimuli', 'TooltipString', 'Show/Hide Stimulus panel'); + set_callback(StimulusShow, {mfilename, 'show_hide'}); %#ok (Defined just above) + next_row(y); + + SoloParamHandle(obj, 'myfig', 'value', figure('closerequestfcn', [mfilename '(' class(obj) ', ''hide'');'], 'MenuBar', 'none', ... + 'Name', mfilename), 'saveable', 0); + screen_size = get(0, 'ScreenSize'); + set(value(myfig),'Position',[1 screen_size(4)-740, 1000 1000]); % put fig at top right + set(double(gcf), 'Visible', 'off'); + x=10;y=10; + + SoloParamHandle(obj, 'ax', 'saveable', 0, ... + 'value', axes('Position', [0.01 0.5 0.45 0.45])); + ylabel('log_e A','FontSize',16,'FontName','Cambria Math'); + set(value(ax),'Fontsize',15) + xlabel('Sound Categorization','FontSize',16,'FontName','Cambria Math') + + SoloParamHandle(obj, 'axperf', 'saveable', 0, ... + 'value', axes('Position', [0.5 0.5 0.45 0.45])); + ylabel('log_e A','FontSize',16,'FontName','Cambria Math'); + set(value(axperf),'Fontsize',15) + xlabel('Sound Categorization','FontSize',16,'FontName','Cambria Math') + + SoundManagerSection(obj, 'declare_new_sound', 'StimAUD1') + SoloParamHandle(obj, 'thisstim', 'value', []); + SoloParamHandle(obj, 'thisstimlog', 'value', []); + SoloParamHandle(obj, 'h1', 'value', []); + + y=5; + + next_row(y); + next_row(y); + PushbuttonParam(obj, 'refresh_stimuli', x,y , 'TooltipString', 'Instantiates the stimuli given the new set of parameters'); + set_callback(refresh_stimuli, {mfilename, 'plot_stimuli'}); + + + next_row(y); + next_row(y); + MenuParam(obj, 'Rule', {'S2>S_boundry Left','S2>S_boundry Right'}, ... + 'S2>S_boundry Left', x, y, 'labelfraction', 0.35, 'TooltipString', sprintf(['\nThis bottom determines the rule\n', ... + '\n''S2>S_boundry Left'' means if Aud2 > Aud_boundry then reward will be delivered from the left water spout and if Aud2 < Aud_boundry then water comes form right\n',... + '\n''S2>S_boundry Right'' means if Aud2 < Aud_boundry then reward will be delivered from the left water spout and if Aud2 > Aud_boundry then water comes from right\n'])); + next_row(y, 1) + + next_column(x); + y=5; + MenuParam(obj, 'filter_type', {'GAUS','LPFIR', 'FIRLS','BUTTER','MOVAVRG','KAISER','EQUIRIP','HAMMING'}, ... + 'GAUS', x, y, 'labelfraction', 0.35, 'TooltipString', sprintf(['\nDifferent filters. ''LPFIR'': lowpass FIR ''FIRLS'': Least square linear-phase FIR filter design\n', ... + '\n''BUTTER'': IIR Butterworth lowpass filter ''GAUS'': Gaussian filter (window)\n', ... + '\n''MOVAVRG'': Moving average FIR filter ''KAISER'': Kaiser-window FIR filtering\n', ... + '\n''EQUIRIP'':Eqiripple FIR filter ''HAMMING'': Hamming-window based FIR'])); + next_row(y, 1) + NumeditParam(obj,'fcut',110,x,y,'label','fcut','TooltipString','Cut off frequency on the original white noise'); + next_row(y); + NumeditParam(obj,'lfreq',2000,x,y,'label','Modulator_LowFreq','TooltipString','Lower bound for the frequency modulator'); + next_row(y); + NumeditParam(obj,'hfreq',20000,x,y,'label','Modulator_HighFreq','TooltipString','Upper bound for the frequency modulator'); + next_row(y); + DispParam(obj, 'A1_sigma', 0.01, x,y,'label','A1_sigma','TooltipString','Sigma value for the first stimulus'); + next_row(y); + DispParam(obj, 'A1_freq', 0.01, x,y,'label','A1_freq','TooltipString','Sigma value for the first stimulus'); + next_row(y); + NumeditParam(obj,'minS1',0.007,x,y,'label','minS1','TooltipString','min sigma value for AUD1'); + next_row(y); + NumeditParam(obj,'maxS1',0.05,x,y,'label','maxS1','TooltipString','max sigma value for AUD1'); + next_row(y); + NumeditParam(obj,'minF1',1,x,y,'label','minF1','TooltipString','min frequency value for AUD1'); + next_row(y); + NumeditParam(obj,'maxF1',9,x,y,'label','maxF1','TooltipString','max frequency value for AUD1'); + next_row(y); + NumeditParam(obj,'boundary',0,x,y,'label','boundary','TooltipString','decision boundary for categorisation (log)'); + next_row(y); + MenuParam(obj, 'mu_location', {'center', 'side'}, ... + 'center', x, y, 'labelfraction', 0.35, 'TooltipString', sprintf(['\nLocation of boundary'])); + next_row(y); + ToggleParam(obj, 'frequency_categorization', 0, x,y,... + 'OnString', 'frequency ON',... + 'OffString', 'frequency OFF',... + 'TooltipString', sprintf('If on (black) then it enables the presentation of pure tones')); + next_row(y); + MenuParam(obj, 'DistributionType', {'uniform','unim-unif', 'unif-unim', 'unimodal', 'bimodal', 'asym-unif', 'Unim-Unif', 'Unif-Unim'}, ... + 'uniform', x, y, 'labelfraction', 0.35, 'TooltipString', sprintf(['\nDifferent distributions'])); + set_callback(DistributionType, {mfilename, 'DistributionType'}); + if frequency_categorization + boundary.value = (log(value(minF1)) + log(value(maxF1)))/2; + else + boundary.value = (log(value(minS1)) + log(value(maxS1)))/2; + end + if strcmp(mu_location,'center') + boundary.value = value(boundary); + elseif strcmp(mu_location,'side') + boundary.value = (log(value(minS1)) + value(boundary))/2; + end + StimulusSection(obj,'plot_stimuli'); + + set_callback(frequency_categorization, {mfilename, 'FrequencyCategorization'}); + + case 'prepare_next_trial' + + %% d or u? + + SideSection(obj,'get_current_side'); + StimulusSection(obj,'pick_current_stimulus'); + if frequency_categorization + A1_freq.value=exp(value(thisstim)); + A1_sigma.value=0; + A1 = log(value(A1_freq)); + else + A1_sigma.value=exp(value(thisstim)); + A1_freq.value=0; + A1 = log(value(A1_sigma)); + end + +% value(thisstimlog(n_done_trials+1)) - value(boundary) + if value(thisstimlog(n_done_trials+1)) > value(boundary)%value(numClass) + set(value(h1), 'YData', value(A1), 'color',[0.4 0.8 0.1],'markerfacecolor',[0.4 0.8 0.1]); + else + set(value(h1), 'YData', value(A1), 'color',[0.8 0.4 0.1],'markerfacecolor',[0.8 0.4 0.1]); + end + + % Plot current stimulus and move to saving stimulus history + + %% produce noise pattern + srate=SoundManagerSection(obj,'get_sample_rate'); + Fs=srate; + T=value(A1_time); + + if frequency_categorization + dur1 = A1_time*1000; + bal=0; + freq1=A1_freq*1000; + vol=0.001; + RVol=vol*min(1,(1+bal));LVol=vol*min(1,(1-bal)); + t=0:(1/srate):(dur1/1000); t = t(1:end-1); + tw=sin(t*2*pi*freq1); + RW=RVol*tw; + %w=[LW;RW]; + AUD1 = RW; + else + [rawA1 rawA2 normA1 normA2]=noisestim(1,1,T,value(fcut),Fs,value(filter_type)); + modulator=singlenoise(1,T,[value(lfreq) value(hfreq)],Fs,'BUTTER'); + AUD1=normA1(1:A1_time*srate).*modulator(1:A1_time*srate).*A1_sigma; + end + + if ~isempty(AUD1) + SoundManagerSection(obj, 'set_sound', 'StimAUD1', [AUD1'; AUD1']) + end + + + SoundManagerSection(obj, 'send_not_yet_uploaded_sounds'); + + if n_done_trials >0 + + if ~violation_history(n_done_trials) && ~timeout_history(n_done_trials) + StimulusSection(obj,'update_stimulus_history'); + else + StimulusSection(obj,'update_stimulus_history_nan'); + end + end + + + %% Case make_stimuli + case 'make_stimuli' + if nargout>0 + if frequency_categorization + stim_min_log = log(value(minF1)); + stim_max_log = log(value(maxF1)); + else + stim_min_log = log(value(minS1)); + stim_max_log = log(value(maxS1)); + end + + stim_range_log = stim_max_log - stim_min_log; + if strcmp(DistributionType,'uniform') + stim_i_log = stim_min_log + rand() * (stim_max_log - stim_min_log); + %stim_i = exp(stim_i_log) + elseif strcmp(DistributionType,'unimodal') + mu_1 = value(boundary); + sigma = 0.4; + stim_tot = []; + for i = 1:1000 + stim_i_log = stim_max_log +1; + while (stim_i_log > stim_max_log) ||(stim_i_log < stim_min_log) + stim_i_log = normrnd(mu_1,sigma); + end + stim_tot = [stim_tot stim_i_log]; + end + +% stim_tot = [] +% for i = 1:1000 +% stim_i_log = stim_min_log + rand() * (stim_max_log - stim_min_log); +% stim_i_log = stim_i_log + 0.4 * cos(2 * pi * 0.5 * (stim_i_log - stim_min_log)/(stim_max_log - stim_min_log)) +% stim_i_log = stim_i_log - 0.4; +% stim_i_log = stim_min_log + (stim_i_log - stim_min_log) * stim_range_log/(stim_range_log - 0.8) +% stim_tot = [stim_tot stim_i_log]; +% end + + elseif strcmp(DistributionType,'bimodal') + mu_1 = (stim_min_log + value(boundary))/2; + mu_2 = (stim_max_log + value(boundary))/2; + sigma = 0.2; +% stim_tot = []; +% for i = 1:1000 + stim_i_log = stim_max_log + 1; + while (stim_i_log > stim_max_log) ||(stim_i_log < stim_min_log) + if rand()>0.5 + stim_i_log = normrnd(mu_1,sigma); + else + stim_i_log = normrnd(mu_2,sigma); + end + end +% stim_tot = [stim_tot stim_i_log]; +% end +% stim_tot = [stim_tot stim_i_log]; + + +% stim_tot = [] +% for i = 1:1000 +% stim_i_log = stim_min_log + rand() * (stim_max_log - stim_min_log); +% stim_i_log = stim_i_log + 0.07 * cos(2 * pi * 1.5 * (stim_i_log - stim_min_log)/(stim_max_log - stim_min_log)) +% stim_i_log = stim_i_log - 0.07; +% stim_i_log = stim_min_log + (stim_i_log - stim_min_log) * stim_range_log/(stim_range_log - 0.14) +% stim_tot = [stim_tot stim_i_log]; +% end + + elseif strcmp(DistributionType,'unim-unif') + stim_i_log = stim_min_log + rand() * (stim_max_log - stim_min_log); + if stim_i_log < (stim_max_log + stim_min_log) / 2 + for i = 1:30 + stim_i_log = stim_i_log - 0.01 * sin(2 * pi * 0.5 * (stim_i_log - stim_min_log) / (stim_range_log/2)); + end + end + elseif strcmp(DistributionType,'unif-unim') + stim_i_log = stim_min_log + rand() * (stim_max_log - stim_min_log); + if stim_i_log > (stim_max_log + stim_min_log) / 2 + for i = 1:30 + stim_i_log = stim_i_log - 0.01 * sin(2 * pi * 0.5 * (stim_i_log - stim_min_log) / (stim_range_log/2)); + end + end + elseif strcmp(DistributionType,'Unim-Unif') + stim_i_log = stim_min_log + rand() * (stim_max_log - stim_min_log); + if stim_i_log < (stim_max_log + stim_min_log) / 2 + for i = 1:30 + stim_i_log = stim_i_log + 0.01 * sin(2 * pi * 0.5 * (stim_i_log - stim_min_log) / (stim_range_log/2)); + end + end + elseif strcmp(DistributionType,'Unif-Unim') + stim_i_log = stim_min_log + rand() * (stim_max_log - stim_min_log); + if stim_i_log > (stim_max_log + stim_min_log) / 2 + for i = 1:30 + stim_i_log = stim_i_log + 0.01 * sin(2 * pi * 0.5 * (stim_i_log - stim_min_log) / (stim_range_log/2)); + end + end + elseif strcmp(DistributionType,'asym-unif') +% stim_tot = []; + stim_half_log = value(boundary) - stim_min_log; +% for i = 1:1000 + stim_i_log = stim_min_log + rand() * (stim_max_log - stim_min_log); + if stim_i_log < (stim_min_log + value(boundary)) / 2 + for i = 1:30 + stim_i_log = stim_i_log - 0.01 * sin(2 * pi * 0.5 * (stim_i_log - stim_min_log) / (stim_half_log/2)); + end + end +% stim_tot = [stim_tot stim_i_log]; +% end + end + x = stim_i_log; + end + + + %% Case pick_current_stimulus + case 'pick_current_stimulus' + if frequency_categorization + stim_min_log = log(value(minF1)); + stim_max_log = log(value(maxF1)); + else + stim_min_log = log(value(minS1)); + stim_max_log = log(value(maxS1)); + end + if strcmp(Rule,'S2>S_boundry Left') + if strcmp(ThisTrial, 'LEFT') + stim_i_log = stim_min_log; + while stim_i_log <= value(boundary) + stim_i_log = StimulusSection(obj,'make_stimuli'); + end + %stim_i = exp(stim_i_log); + else + stim_i_log = stim_max_log; + while stim_i_log >= value(boundary) + stim_i_log = StimulusSection(obj,'make_stimuli'); + end + %stim_i = exp(stim_i_log); + end + elseif strcmp(Rule,'S2>S_boundry Right') + if strcmp(ThisTrial, 'LEFT') + stim_i_log = stim_max_log; + while stim_i_log >= value(boundary) + stim_i_log = StimulusSection(obj,'make_stimuli'); + end + %stim_i = exp(stim_i_log); + else + stim_i_log = stim_min_log; + while stim_i_log <= value(boundary) + stim_i_log = StimulusSection(obj,'make_stimuli'); + end + %stim_i = exp(stim_i_log); + end + end + thisstim.value=stim_i_log; + thisstimlog(n_done_trials+1)= stim_i_log; + %disp(exp(value(thisstimlog))) + + %% Case plot_pais + case 'plot_stimuli' + + %% plot the stimuli + if frequency_categorization + boundary.value = (log(value(minF1)) + log(value(maxF1)))/2; + stim_min_log = log(value(minF1)); + stim_max_log = log(value(maxF1)); + stim_min = value(minF1); + stim_max = value(maxF1); + + else + boundary.value = (log(value(minS1)) + log(value(maxS1)))/2; + if strcmp(mu_location,'center') + boundary.value = value(boundary); + elseif strcmp(mu_location,'side') + boundary.value = (log(value(minS1)) + value(boundary))/2; + end + stim_min_log = log(value(minS1)); + stim_max_log = log(value(maxS1)); + stim_min = value(minS1); + stim_max = value(maxS1); + end + cla(value(ax)) + xd=1; + axes(value(ax)); + plot(xd,stim_min_log,'s','MarkerSize',15,'MarkerEdgeColor',[0 0 0],'LineWidth',2) + hold on + plot(xd,stim_max_log,'s','MarkerSize',15,'MarkerEdgeColor',[0 0 0],'LineWidth',2) + line([0,2], [value(boundary),value(boundary)]); + axis square + set(value(ax),'ytick',([stim_min_log, stim_max_log]),'xtick',xd); + set(value(ax),'yticklabel',([stim_min, stim_max]),'xticklabel','S1'); + ylabel('\sigma_1 in log scale','FontSize',16,'FontName','Cambria Math'); + set(value(ax),'Fontsize',15) + xlabel('S1','FontSize',16,'FontName','Cambria Math') + + SideSection(obj,'get_current_side'); + StimulusSection(obj,'pick_current_stimulus'); + + A1 = value(thisstim); + + %% plot the stimulus; + if value(thisstim) > value(boundary)%value(numClass) + h1.value=plot(xd,value(A1),'s','color',[0.4 0.8 0.1],'markerfacecolor',[0.4 0.8 0.1],'MarkerSize',15,'LineWidth',3); + else + h1.value=plot(xd,value(A1),'s','color',[0.8 0.4 0.1],'markerfacecolor',[0.8 0.4 0.1],'MarkerSize',15,'LineWidth',3); + end + + + %% Case frequency ON + case 'FrequencyCategorization' + if frequency_categorization == 1 + enable(maxF1);enable(minF1); + StimulusSection(obj,'plot_stimuli'); + else + disable(maxF1);disable(minF1); + StimulusSection(obj,'plot_stimuli'); + end + + %% Case get_stimuli + case 'get_stimuli' + if nargout>0 + x=value(S1); + end + + + %% Case close + case 'close' + % Delete all SoloParamHandles who belong to this object and whose + % fullname starts with the name of this mfile: + if exist('myfig', 'var') && isa(myfig, 'SoloParamHandle') && ishandle(value(myfig)), %#ok + delete(value(myfig)); + end; + delete_sphandle('owner', ['^@' class(obj) '$'], ... + 'fullname', ['^' mfilename]); + + %% Case update_stimuli + case 'update_stimuli' + StimulusSection(obj,'plot_stimuli'); + + case 'update_stimulus_history' + ps=value(stimulus_history); + ps(n_done_trials)=value(thisstimlog(n_done_trials)); + stimulus_history.value=ps; + + case 'update_stimulus_history_nan' + ps=value(stimulus_history); + ps(n_done_trials)=value(thisstimlog(n_done_trials));%nan; + stimulus_history.value=ps; + + %% Case hide + case 'hide', + StimulusShow.value = 0; set(value(myfig), 'Visible', 'off'); + %% Case show + case 'show', + StimulusShow.value = 1; set(value(myfig), 'Visible', 'on'); + %% Case Show_hide + case 'show_hide', + if StimulusShow == 1, set(value(myfig), 'Visible', 'on'); %#ok (defined by GetSoloFunctionArgs) + else set(value(myfig), 'Visible', 'off'); + end; + end + \ No newline at end of file diff --git a/Protocols/@SoundCatContinuous/StimulusSection_.m b/Protocols/@SoundCatContinuous/StimulusSection_.m new file mode 100644 index 00000000..b605526f --- /dev/null +++ b/Protocols/@SoundCatContinuous/StimulusSection_.m @@ -0,0 +1,349 @@ + + +function [x, y] = StimulusSection(obj, action, varargin) + +GetSoloFunctionArgs(obj); + +switch action, + + % ------------------------------------------------------------------ + % INIT + % ------------------------------------------------------------------ + + case 'init' + if length(varargin) < 2, + error('Need at least two arguments, x and y position, to initialize %s', mfilename); + end; + x = varargin{1}; y = varargin{2}; + + ToggleParam(obj, 'StimulusShow', 0, x, y, 'OnString', 'Stimuli', ... + 'OffString', 'Stimuli', 'TooltipString', 'Show/Hide Stimulus panel'); + set_callback(StimulusShow, {mfilename, 'show_hide'}); %#ok (Defined just above) + next_row(y); + + SoloParamHandle(obj, 'myfig', 'value', figure('closerequestfcn', [mfilename '(' class(obj) ', ''hide'');'], 'MenuBar', 'none', ... + 'Name', mfilename), 'saveable', 0); + screen_size = get(0, 'ScreenSize'); + set(value(myfig),'Position',[1 screen_size(4)-740, 1000 1000]); % put fig at top right + set(double(gcf), 'Visible', 'off'); + x=10;y=10; + + SoloParamHandle(obj, 'ax', 'saveable', 0, ... + 'value', axes('Position', [0.01 0.5 0.45 0.45])); + ylabel('log_e A','FontSize',16,'FontName','Cambria Math'); + set(value(ax),'Fontsize',15) + xlabel('Sound Categorization','FontSize',16,'FontName','Cambria Math') + + SoloParamHandle(obj, 'axperf', 'saveable', 0, ... + 'value', axes('Position', [0.5 0.5 0.45 0.45])); + ylabel('log_e A','FontSize',16,'FontName','Cambria Math'); + set(value(axperf),'Fontsize',15) + xlabel('Sound Categorization','FontSize',16,'FontName','Cambria Math') + + SoundManagerSection(obj, 'declare_new_sound', 'StimAUD1') + SoloParamHandle(obj, 'thisstim', 'value', []); + SoloParamHandle(obj, 'thisstimlog', 'value', []); + SoloParamHandle(obj, 'h1', 'value', []); + + y=5; + + next_row(y); + next_row(y); + PushbuttonParam(obj, 'refresh_stimuli', x,y , 'TooltipString', 'Instantiates the stimuli given the new set of parameters'); + set_callback(refresh_stimuli, {mfilename, 'plot_stimuli'}); + + + next_row(y); + next_row(y); + MenuParam(obj, 'Rule', {'S2>S_boundry Left','S2>S_boundry Right'}, ... + 'S2>S_boundry Left', x, y, 'labelfraction', 0.35, 'TooltipString', sprintf(['\nThis bottom determines the rule\n', ... + '\n''S2>S_boundry Left'' means if Aud2 > Aud_boundry then reward will be delivered from the left water spout and if Aud2 < Aud_boundry then water comes form right\n',... + '\n''S2>S_boundry Right'' means if Aud2 < Aud_boundry then reward will be delivered from the left water spout and if Aud2 > Aud_boundry then water comes from right\n'])); + next_row(y, 1) + + next_column(x); + y=5; + MenuParam(obj, 'filter_type', {'GAUS','LPFIR', 'FIRLS','BUTTER','MOVAVRG','KAISER','EQUIRIP','HAMMING'}, ... + 'GAUS', x, y, 'labelfraction', 0.35, 'TooltipString', sprintf(['\nDifferent filters. ''LPFIR'': lowpass FIR ''FIRLS'': Least square linear-phase FIR filter design\n', ... + '\n''BUTTER'': IIR Butterworth lowpass filter ''GAUS'': Gaussian filter (window)\n', ... + '\n''MOVAVRG'': Moving average FIR filter ''KAISER'': Kaiser-window FIR filtering\n', ... + '\n''EQUIRIP'':Eqiripple FIR filter ''HAMMING'': Hamming-window based FIR'])); + next_row(y, 1) + NumeditParam(obj,'fcut',110,x,y,'label','fcut','TooltipString','Cut off frequency on the original white noise'); + next_row(y); + NumeditParam(obj,'lfreq',2000,x,y,'label','Modulator_LowFreq','TooltipString','Lower bound for the frequency modulator'); + next_row(y); + NumeditParam(obj,'hfreq',20000,x,y,'label','Modulator_HighFreq','TooltipString','Upper bound for the frequency modulator'); + next_row(y); + DispParam(obj, 'A1_sigma', 0.01, x,y,'label','A1_sigma','TooltipString','Sigma value for the first stimulus'); + next_row(y); + DispParam(obj, 'A1_freq', 0.01, x,y,'label','A1_freq','TooltipString','Sigma value for the first stimulus'); + next_row(y); + NumeditParam(obj,'minS1',0.007,x,y,'label','minS1','TooltipString','min sigma value for AUD1'); + next_row(y); + NumeditParam(obj,'maxS1',0.05,x,y,'label','maxS1','TooltipString','max sigma value for AUD1'); + next_row(y); + NumeditParam(obj,'minF1',1,x,y,'label','minF1','TooltipString','min frequency value for AUD1'); + next_row(y); + NumeditParam(obj,'maxF1',9,x,y,'label','maxF1','TooltipString','max frequency value for AUD1'); + next_row(y); + NumeditParam(obj,'boundary',0,x,y,'label','boundary','TooltipString','decision boundary for categorisation (log)'); + next_row(y); + NumeditParam(obj,'mu',0,x,y,'label','mu','TooltipString','mean of distribution'); + next_row(y); + ToggleParam(obj, 'frequency_categorization', 0, x,y,... + 'OnString', 'frequency ON',... + 'OffString', 'frequency OFF',... + 'TooltipString', sprintf('If on (black) then it enables the presentation of pure tones')); + next_row(y); + MenuParam(obj, 'DistributionType', {'uniform','unim-unif', 'unif-unim'}, ... + 'uniform', x, y, 'labelfraction', 0.35, 'TooltipString', sprintf(['\nDifferent distributions'])); + set_callback(DistributionType, {mfilename, 'DistributionType'}); + if frequency_categorization + boundary.value = (log(value(minF1)) + log(value(maxF1)))/2; + else + boundary.value = (log(value(minS1)) + log(value(maxS1)))/2; + end + StimulusSection(obj,'plot_stimuli'); + + set_callback(frequency_categorization, {mfilename, 'FrequencyCategorization'}); + + case 'prepare_next_trial' + + %% d or u? + + SideSection(obj,'get_current_side'); + StimulusSection(obj,'pick_current_stimulus'); + if frequency_categorization + A1_freq.value=exp(value(thisstim)); + A1_sigma.value=0; + A1 = log(value(A1_freq)); + else + A1_sigma.value=exp(value(thisstim)); + A1_freq.value=0; + A1 = log(value(A1_sigma)); + end + +% value(thisstimlog(n_done_trials+1)) - value(boundary) + if value(thisstimlog(n_done_trials+1)) > value(boundary)%value(numClass) + set(value(h1), 'YData', value(A1), 'color',[0.4 0.8 0.1],'markerfacecolor',[0.4 0.8 0.1]); + else + set(value(h1), 'YData', value(A1), 'color',[0.8 0.4 0.1],'markerfacecolor',[0.8 0.4 0.1]); + end + + % Plot current stimulus and move to saving stimulus history + + %% produce noise pattern + srate=SoundManagerSection(obj,'get_sample_rate'); + Fs=srate; + T=value(A1_time); + + if frequency_categorization + dur1 = A1_time*1000; + bal=0; + freq1=A1_freq*1000; + vol=0.002; + RVol=vol*min(1,(1+bal));LVol=vol*min(1,(1-bal)); + t=0:(1/srate):(dur1/1000); t = t(1:end-1); + tw=sin(t*2*pi*freq1); + RW=RVol*tw; + %w=[LW;RW]; + AUD1 = RW; + else + [rawA1 rawA2 normA1 normA2]=noisestim(1,1,T,value(fcut),Fs,value(filter_type)); + modulator=singlenoise(1,T,[value(lfreq) value(hfreq)],Fs,'BUTTER'); + AUD1=normA1(1:A1_time*srate).*modulator(1:A1_time*srate).*A1_sigma; + end + + if ~isempty(AUD1) + SoundManagerSection(obj, 'set_sound', 'StimAUD1', [AUD1'; AUD1']) + end + + + SoundManagerSection(obj, 'send_not_yet_uploaded_sounds'); + + if n_done_trials >0 + + if ~violation_history(n_done_trials) && ~timeout_history(n_done_trials) + StimulusSection(obj,'update_stimulus_history'); + else + StimulusSection(obj,'update_stimulus_history_nan'); + end + end + + + %% Case make_stimuli + case 'make_stimuli' + if nargout>0 + if frequency_categorization + stim_min_log = log(value(minF1)); + stim_max_log = log(value(maxF1)); + else + stim_min_log = log(value(minS1)); + stim_max_log = log(value(maxS1)); + end + + stim_range_log = stim_max_log - stim_min_log; + if strcmp(DistributionType,'uniform') + stim_i_log = stim_min_log + rand() * (stim_max_log - stim_min_log); + %stim_i = exp(stim_i_log) + elseif strcmp(DistributionType,'unim-unif') + stim_i_log = stim_min_log + rand() * (stim_max_log - stim_min_log); + if stim_i_log < (stim_max_log + stim_min_log) / 2 + for i = 1:30 + stim_i_log = stim_i_log - 0.01 * sin(2 * pi * 0.5 * (stim_i_log - stim_min_log) / (stim_range_log/2)); + end + end + elseif strcmp(DistributionType,'unif-unim') + stim_i_log = stim_min_log + rand() * (stim_max_log - stim_min_log); + if stim_i_log > (stim_max_log + stim_min_log) / 2 + for i = 1:30 + stim_i_log = stim_i_log - 0.01 * sin(2 * pi * 0.5 * (stim_i_log - stim_min_log) / (stim_range_log/2)); + end + end + end + x = stim_i_log; + end + + + %% Case pick_current_stimulus + case 'pick_current_stimulus' + if frequency_categorization + stim_min_log = log(value(minF1)); + stim_max_log = log(value(maxF1)); + else + stim_min_log = log(value(minS1)); + stim_max_log = log(value(maxS1)); + end + if strcmp(Rule,'S2>S_boundry Left') + if strcmp(ThisTrial, 'LEFT') + stim_i_log = stim_min_log; + while stim_i_log <= value(boundary) + stim_i_log = StimulusSection(obj,'make_stimuli'); + end + %stim_i = exp(stim_i_log); + else + stim_i_log = stim_max_log; + while stim_i_log >= value(boundary) + stim_i_log = StimulusSection(obj,'make_stimuli'); + end + %stim_i = exp(stim_i_log); + end + elseif strcmp(Rule,'S2>S_boundry Right') + if strcmp(ThisTrial, 'LEFT') + stim_i_log = stim_max_log; + while stim_i_log >= value(boundary) + stim_i_log = StimulusSection(obj,'make_stimuli'); + end + %stim_i = exp(stim_i_log); + else + stim_i_log = stim_min_log; + while stim_i_log <= value(boundary) + stim_i_log = StimulusSection(obj,'make_stimuli'); + end + %stim_i = exp(stim_i_log); + end + end + thisstim.value=stim_i_log; + thisstimlog(n_done_trials+1)= stim_i_log; + %disp(exp(value(thisstimlog))) + + %% Case plot_pais + case 'plot_stimuli' + + %% plot the stimuli + if frequency_categorization + boundary.value = (log(value(minF1)) + log(value(maxF1)))/2; + stim_min_log = log(value(minF1)); + stim_max_log = log(value(maxF1)); + stim_min = value(minF1); + stim_max = value(maxF1); + + else + boundary.value = (log(value(minS1)) + log(value(maxS1)))/2; + stim_min_log = log(value(minS1)); + stim_max_log = log(value(maxS1)); + stim_min = value(minS1); + stim_max = value(maxS1); + end + cla(value(ax)) + xd=1; + axes(value(ax)); + plot(xd,stim_min_log,'s','MarkerSize',15,'MarkerEdgeColor',[0 0 0],'LineWidth',2) + hold on + plot(xd,stim_max_log,'s','MarkerSize',15,'MarkerEdgeColor',[0 0 0],'LineWidth',2) + line([0,2], [value(boundary),value(boundary)]); + axis square + set(value(ax),'ytick',([stim_min_log, stim_max_log]),'xtick',xd); + set(value(ax),'yticklabel',([stim_min, stim_max]),'xticklabel','S1'); + ylabel('\sigma_1 in log scale','FontSize',16,'FontName','Cambria Math'); + set(value(ax),'Fontsize',15) + xlabel('S1','FontSize',16,'FontName','Cambria Math') + + SideSection(obj,'get_current_side'); + StimulusSection(obj,'pick_current_stimulus'); + + A1 = value(thisstim); + + %% plot the stimulus; + if value(thisstim) > value(boundary)%value(numClass) + h1.value=plot(xd,value(A1),'s','color',[0.4 0.8 0.1],'markerfacecolor',[0.4 0.8 0.1],'MarkerSize',15,'LineWidth',3); + else + h1.value=plot(xd,value(A1),'s','color',[0.8 0.4 0.1],'markerfacecolor',[0.8 0.4 0.1],'MarkerSize',15,'LineWidth',3); + end + + + %% Case frequency ON + case 'FrequencyCategorization' + if frequency_categorization == 1 + enable(maxF1);enable(minF1); + StimulusSection(obj,'plot_stimuli'); + else + disable(maxF1);disable(minF1); + StimulusSection(obj,'plot_stimuli'); + end + + %% Case get_stimuli + case 'get_stimuli' + if nargout>0 + x=value(S1); + end + + + %% Case close + case 'close' + % Delete all SoloParamHandles who belong to this object and whose + % fullname starts with the name of this mfile: + if exist('myfig', 'var') && isa(myfig, 'SoloParamHandle') && ishandle(value(myfig)), %#ok + delete(value(myfig)); + end; + delete_sphandle('owner', ['^@' class(obj) '$'], ... + 'fullname', ['^' mfilename]); + + %% Case update_stimuli + case 'update_stimuli' + StimulusSection(obj,'plot_stimuli'); + + case 'update_stimulus_history' + ps=value(stimulus_history); + ps(n_done_trials)=value(thisstimlog(n_done_trials)); + stimulus_history.value=ps; + + case 'update_stimulus_history_nan' + ps=value(stimulus_history); + ps(n_done_trials)=nan; + stimulus_history.value=ps; + + %% Case hide + case 'hide', + StimulusShow.value = 0; set(value(myfig), 'Visible', 'off'); + %% Case show + case 'show', + StimulusShow.value = 1; set(value(myfig), 'Visible', 'on'); + %% Case Show_hide + case 'show_hide', + if StimulusShow == 1, set(value(myfig), 'Visible', 'on'); %#ok (defined by GetSoloFunctionArgs) + else set(value(myfig), 'Visible', 'off'); + end; + end + \ No newline at end of file diff --git a/Protocols/@SoundCatContinuous/noiselib.m b/Protocols/@SoundCatContinuous/noiselib.m new file mode 100644 index 00000000..57dd5c7d --- /dev/null +++ b/Protocols/@SoundCatContinuous/noiselib.m @@ -0,0 +1,51 @@ +function [base,filtbase]=noiselib(num,T,fcut,Fs,filter_type,outband) +close all +clc + +%%%%%%%%%%%%%%%%% Determines of the type of filter used %%%%%%%%%%%%%%%%%%% +%'LPFIR': lowpass FIR%%%%%'FIRLS': Least square linear-phase FIR filter design +%'BUTTER': IIR Butterworth lowpass filter%%%%%%'GAUS': Gaussian filter (window) +%'MOVAVRG': Moving average FIR filter%%%%%%%%'KAISER': Kaiser-window FIR filtering +% 'EQUIRIP':Eqiripple FIR filter%%%%% 'HAMMING': Hamming-window based FIR +% T is duration of each signal in milisecond, fcut is the cut-off frequency +% Fs is the sampling frequency +% outband=40; + +for ii=1:num +s = RandStream('mcg16807','Seed',ii) +RandStream.setDefaultStream(s) + +replace=1; +L=T*Fs/1000; % Length of signal +t=L*1000*linspace(0,1,L)/Fs; % time in miliseconds +%%%%%%%%%%% produce position values %%%%%%% +pos1 = randn(Fs,1); +pos1(pos1>outband)=[]; +pos1(pos1<-outband)=[]; + +base(:,num)=pos1; +%base = randsample(pos1,L,replace); +%%%% Filter the original position values %%%%%% +filtbase(:,num)=filt(base,fcut,Fs,filter_type); + +end + +end + +%%%%%% plot the row and filtered position values %%%%%%%%% +% subplot(2,2,1) +% plot(t,base,'r'); +% ylabel('base') +% xlabel('Time (ms)') +% subplot(2,2,2) +% plot(t,target,'g'); +% ylabel('target') +% xlabel('Time (ms)') +% subplot(2,2,3) +% plot(t,filtbase) +% ylabel('filtbase') +% xlabel('Time (ms)') +% subplot(2,2,4) +% plot(t,filttarget) +% ylabel('filttarget') +% xlabel('Time (ms)') diff --git a/Protocols/@SoundCatContinuous/private/filt.m b/Protocols/@SoundCatContinuous/private/filt.m new file mode 100644 index 00000000..e4c572ff --- /dev/null +++ b/Protocols/@SoundCatContinuous/private/filt.m @@ -0,0 +1,60 @@ +function filtsignal=filt(signal,fcut,Fs,filter_type) +a=2; % wp/ws used in butterworth method and LS linear FIR method +N=200; % filter order used in lowpass FIR method +rp=3; % passband ripple in dB used in butterworth method +rs=60; % stopband attenuation in dB used in butterworth method +beta=0.1102*(rs-8.8); %used in Kaiser window to obtain sidelobe attenuation of rs dB +if strcmp(filter_type, 'GAUS') || strcmp(filter_type, 'MOVAVRG') +window = fix(Fs/fcut); % window size used in Gaussian and moving average methods +end +wp=2*fcut/Fs; % normalized passband corner frequency wp, the cutoff frequency +ws=a*wp; % normalized stopband corner frequency + + +switch filter_type + case 'BUTTER' %Butterworth IIR filter + if length(wp)>1 + ws(1)=2*(fcut(1)/2)/Fs; + ws(2)=2*(fcut(2)+fcut(1)/2)/Fs; + [n,wn]=buttord(wp,ws,rp,rs); + [b,a]=butter(n,wn,'bandpass'); + else + [n,wn]=buttord(wp,ws,rp,rs); + [b,a]=butter(n,wn,'low'); + end + filtsignal=filter(b,a,signal);%conventional filtering + case 'LPFIR' %Lowpass FIR filter + d=fdesign.lowpass('N,Fc',N,fcut,Fs); % Fc is the 6-dB down point, N is the filter order(N+1 filter coefficients) + Hd = design(d); + filtsignal=filter(Hd.Numerator,1,signal); %conventional filtering + case 'FIRLS' %Least square linear-phase FIR filter design + b=firls(255,[0 2*fcut/Fs a*2*fcut/Fs 1],[1 1 0 0]); + filtsignal=filter(b,1,signal); %conventional filtering + case 'EQUIRIP' %Eqiripple FIR filter + d=fdesign.lowpass('Fp,Fst,Ap,Ast',wp,ws,rp,rs); + Hd=design(d,'equiripple'); + filtsignal=filter(Hd.Numerator,1,signal); %conventional filtering + case 'MOVAVRG' % Moving average FIR filtering, Rectangular window + h = ones(window,1)/window; + b = fir1(window-1,wp,h); + filtsignal = filter(b, 1, signal); + case 'HAMMING' % Hamming-window based FIR filtering + b = fir1(150,wp); + filtsignal = filter(b, 1, signal); + filtsignal = filter(h, 1, signal); + case 'GAUS' % Gaussian-window FIR filtering + h = normpdf(1:window, 0, fix(window/2)); + b = fir1(window-1,wp,h); + filtsignal = filter(b, 1, signal); + case 'GAUS1' % Gaussian-window FIR filtering + b = fir1(window-1,wp,gausswin(window,2)/window); + filtsignal = filter(b, 1, signal); + case 'KAISER' %Kaiser-window FIR filtering + h=kaiser(window,beta); + b = fir1(window-1,wp,h); + filtsignal = filter(b, 1, signal); + + otherwise + sprintf('filter_type is wrong!! havaset kojast!!') +end + diff --git a/Protocols/@SoundCatContinuous/private/noisestim.m b/Protocols/@SoundCatContinuous/private/noisestim.m new file mode 100644 index 00000000..7f074ab0 --- /dev/null +++ b/Protocols/@SoundCatContinuous/private/noisestim.m @@ -0,0 +1,47 @@ +function [base,target,normbase,normtarget]=noisestim(sigma_1,sigma_2,T,fcut,Fs,filter_type) + +%%%%%%%%%%%%%%%%% Determines of the type of filter used %%%%%%%%%%%%%%%%%%% +%'LPFIR': lowpass FIR%%%%%'FIRLS': Least square linear-phase FIR filter design +%'BUTTER': IIR Butterworth lowpass filter%%%%%%'GAUS': Gaussian filter (window) +%'MOVAVRG': Moving average FIR filter%%%%%%%%'KAISER': Kaiser-window FIR filtering +% 'EQUIRIP':Eqiripple FIR filter%%%%% 'HAMMING': Hamming-window based FIR +% T is duration of each signal in milisecond, fcut is the cut-off frequency +% Fs is the sampling frequency +% outband=40; +replace=1; +L=floor(T*Fs); % Length of signal +t=L*linspace(0,1,L)/Fs; % time in miliseconds +%%%%%%%%%%% produce position values %%%%%%% +pos1 = sigma_1*randn(Fs,1); +% pos1(pos1>outband)=[]; +% pos1(pos1<-outband)=[]; + +pos2 =sigma_2*randn(Fs,1); +% pos2(pos2>outband)=[]; +% pos2(pos2<-outband)=[]; +base = randsample(pos1,L,replace); +target = randsample(pos2,L,replace); +%%%% Filter the original position values %%%%%% +filtbase=filt(base,fcut,Fs,filter_type); +filttarget=filt(target,fcut,Fs,filter_type); +normbase=filtbase./(max(abs(filtbase))); +normtarget=filttarget./(max(abs(filttarget))); +end + +%%%%%% plot the row and filtered position values %%%%%%%%% +% subplot(2,2,1) +% plot(t,base,'r'); +% ylabel('base') +% xlabel('Time (ms)') +% subplot(2,2,2) +% plot(t,target,'g'); +% ylabel('target') +% xlabel('Time (ms)') +% subplot(2,2,3) +% plot(t,filtbase) +% ylabel('filtbase') +% xlabel('Time (ms)') +% subplot(2,2,4) +% plot(t,filttarget) +% ylabel('filttarget') +% xlabel('Time (ms)') diff --git a/Protocols/@SoundCatContinuous/private/singlenoise.m b/Protocols/@SoundCatContinuous/private/singlenoise.m new file mode 100644 index 00000000..0cf201f3 --- /dev/null +++ b/Protocols/@SoundCatContinuous/private/singlenoise.m @@ -0,0 +1,32 @@ +function [normbase]=singlenoise(sigma_1,T,fcut,Fs,filter_type) +%GetSoloFunctionArgs(obj); + +%%%%%%%%%%%%%%%%% Determines of the type of filter used %%%%%%%%%%%%%%%%%%% +%'LPFIR': lowpass FIR%%%%%'FIRLS': Least square linear-phase FIR filter design +%'BUTTER': IIR Butterworth lowpass filter%%%%%%'GAUS': Gaussian filter (window) +%'MOVAVRG': Moving average FIR filter%%%%%%%%'KAISER': Kaiser-window FIR filtering +% 'EQUIRIP':Eqiripple FIR filter%%%%% 'HAMMING': Hamming-window based FIR +% T is duration of each signal in milisecond, fcut is the cut-off frequency +% Fs is the sampling frequency +% outband=40; +sigma_1=1; +%T=10000; +%fcut=[3000 4000]; +%Fs=200000; +filter_type='BUTTER'; +outband=60; +replace=1; +L=floor(T*Fs); % Length of signal +%%%%%%%%%%% produce position values %%%%%%% +pos1 = sigma_1*randn(Fs,1); +% pos1(pos1>outband)=[]; +% pos1(pos1<-outband)=[]; + +base = randsample(pos1,L,replace); +%%%% Filter the original position values %%%%%% +%filtbase=filt(base,fcut,Fs,filter_type); +hf = design(fdesign.bandpass('N,F3dB1,F3dB2',10,fcut(1),fcut(2),Fs)); +filtbase=filter(hf,base); +normbase=filtbase./(max(abs(filtbase))); +end + diff --git a/Protocols/@SoundCatContinuous/state_colors.m b/Protocols/@SoundCatContinuous/state_colors.m new file mode 100644 index 00000000..c6df1d67 --- /dev/null +++ b/Protocols/@SoundCatContinuous/state_colors.m @@ -0,0 +1,16 @@ +function SC = state_colors(obj) %#ok + +SC = struct( ... + 'wait_for_cpoke', [0.68 1 0.63], ... + 'cp', [0.63 1 0.94], ... + 'cp_legal_cbreak_period', [0.63 1 0.94]*0.8, ... + 'sideled_on', [1 0.79 0.63], ... + 'wait_for_collecting_reward', [0.53 0.78 1.00],... + 'righthit', [0.3 0.9 0], ... + 'lefthit', [0 0.9 0.3], ... + 'hit_state', [0.77 0.60 0.48], ... + 'second_hit_state', [0.25 0.45 0.48], ... + 'drink_state', [0 1 0], ... + 'error_state', [1 0.54 0.54], ... + 'violation_state', [0.31 0.48 0.30], ... + 'timeout_state', 0.8*[0.31 0.48 0.30]); \ No newline at end of file diff --git a/Protocols/@SoundCatContinuous/wave_colors.m b/Protocols/@SoundCatContinuous/wave_colors.m new file mode 100644 index 00000000..0d5a7339 --- /dev/null +++ b/Protocols/@SoundCatContinuous/wave_colors.m @@ -0,0 +1,11 @@ +function WC = wave_colors(obj) %#ok + +WC = struct(... + 'stim_wave', [1 1 1 ], ... + 'prestim_wave', [1 1 1 ], ... + 'stimulator_wave', [1 1 0 ], ... + 'stimulator_wave1', [1 1 0 ], ... + 'stimulator_wave2', [1 1 0 ], ... + 'stimulator_wave3', [1 1 0 ], ... + 'stimulator_wave4', [1 1 0 ], ... + 'stimulator_wave5', [1 1 0 ]); \ No newline at end of file From 84ef217f41dcd2c2f48d80ad0058e7151645f4c2 Mon Sep 17 00:00:00 2001 From: Viktor Plattner Date: Fri, 7 Feb 2025 16:17:27 +0000 Subject: [PATCH 014/164] updating sendsummary call in sound cat protocoll --- Protocols/@SoundCatContinuous/SoundCatContinuous.m | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Protocols/@SoundCatContinuous/SoundCatContinuous.m b/Protocols/@SoundCatContinuous/SoundCatContinuous.m index 63ebc611..2e3fb0e9 100644 --- a/Protocols/@SoundCatContinuous/SoundCatContinuous.m +++ b/Protocols/@SoundCatContinuous/SoundCatContinuous.m @@ -249,9 +249,9 @@ pd.stim1dur=stim1dur(:); %pd.stimul=stim_history(:); - - sendsummary(obj,'protocol_data',pd); - + + sendsummary(obj,'protocol_data','PLACEHOLDER it was just pd'); + %% otherwise otherwise, warning('Unknown action! "%s"\n', action); From d76bbda10a5a7e31a8e0ceb586393cca4bbc09a9 Mon Sep 17 00:00:00 2001 From: viktorpm <50667179+viktorpm@users.noreply.github.com> Date: Thu, 20 Feb 2025 11:09:05 +0000 Subject: [PATCH 015/164] Update README.md removing irrelevant windows 7 instructions --- README.md | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/README.md b/README.md index b30044b5..a0345758 100644 --- a/README.md +++ b/README.md @@ -11,15 +11,3 @@ This repository stores the BControl code for our high-throughput behavior traini - The `/ExperPort/Settings/Settings_Custom.conf` file contains rig-specific configurations. Instead, we provide `/ExperPort/Settings/_Settings_Custom.conf`, which is a template. After downloading, users should rename it to `Settings_Custom.conf` and add their rig-specific settings. - The `/PASSWORD_CONFIG-DO_NOT_VERSIONCONTROL.mat` file stores hostnames, users, and passwords. It is version-controlled with SVN and stored on our internal server. - - -## Setting Up Connection with GitHub on Windows 7 Machines - -If you're encountering an issue where TLS 1.2 is not available on your Windows 7 machine, follow these steps to manually enable TLS 1.2 via the registry: - -1. Press `Win + R`, type `regedit`, and press Enter to open the Registry Editor. -2. Navigate to `HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols`. -3. Create the following keys if they do not exist: `TLS 1.2\Client` and `TLS 1.2\Server`. -4. Under `TLS 1.2\Client`, create a DWORD value named `DisabledByDefault` and set its value to `0`. -5. Also under `TLS 1.2\Client`, create another DWORD value named `Enabled` and set its value to `1`. -6. Repeat the process for `TLS 1.2\Server` if needed. From 059f5b8d15dd85a91f13a0be584872cd0d51f9d7 Mon Sep 17 00:00:00 2001 From: viktorpm <50667179+viktorpm@users.noreply.github.com> Date: Wed, 26 Feb 2025 15:34:24 +0000 Subject: [PATCH 016/164] Soundcalibration update (#13) * modernize StateMachineAssembler - Replace hardcoded input mappings with dynamic configuration - Add support for variable number of input lines - Implement input validation and error handling - Update state matrix dimensions to accommodate flexible input count * add fifth sound channel support - Add new sound parameters (A5_sigma) - Implement SoundManagerSection integration for fifth sound - Add Toggle parameter for fifth sound channel - Add corresponding callback function implementation * improve GUI layout and code structure * Refactor GUI initialization logic for better organization * Implement dynamic GUI sizing based on monitor dimensions * Standardize indentation and spacing throughout the file * improving code structure * GUI aesthetic tweeks --- .../StateMachineAssembler.m | 133 ++-- .../@SoundCalibration/SoundCalibration.m | 626 ++++++++++-------- 2 files changed, 449 insertions(+), 310 deletions(-) diff --git a/ExperPort/Modules/@StateMachineAssembler/StateMachineAssembler.m b/ExperPort/Modules/@StateMachineAssembler/StateMachineAssembler.m index 899e7d9d..5c891d83 100644 --- a/ExperPort/Modules/@StateMachineAssembler/StateMachineAssembler.m +++ b/ExperPort/Modules/@StateMachineAssembler/StateMachineAssembler.m @@ -17,7 +17,7 @@ % % Written by Carlos Brody October 2006; edited by Sebastien Awwad 2007,2008 - +% Additional Input lines added December 2012 CDB function [sma] = StateMachineAssembler(varargin) @@ -36,30 +36,13 @@ sma_struct = []; end; - - fnames = {'name', 'detectorFunctionName', 'inputNumber', 'happId'}; - hs = { - 'Cin', 'line_in', 1, 1 ; ... - 'Cout', 'line_out', 1, 2 ; ... - 'Lin', 'line_in', 2, 3 ; ... - 'Lout', 'line_out', 2, 4 ; ... - 'Rin', 'line_in', 3, 5 ; ... - 'Rout', 'line_out', 3, 6 ; ... - 'Chi', 'line_high', 1, 7 ; ... - 'Clo', 'line_low', 1, 8 ; ... - 'Lhi', 'line_high', 2, 9 ; ... - 'Llo', 'line_low', 2, 10 ; ... - 'Rhi', 'line_high', 3, 11 ; ... - 'Rlo', 'line_low', 3, 12 ; ... - }; - - default_happSpec = cell2struct(hs, fnames, 2); - - + pairs = { ... 'default_DOut' 0 ; ... - 'default_happSpec' default_happSpec ; ... + 'default_happSpec' [] ; ... 'use_happenings' 0 ; ... + 'n_input_lines' 3 ; ... + 'line_names' 'CLRABDEFGHIJKL' ; ... }; singles = { ... 'no_dead_time_technology', 'inputarg', 'no_dead_time_technology', '' ; ... @@ -68,6 +51,58 @@ }; parseargs(varargin, pairs, singles); + if n_input_lines ~= 3 && ~use_happenings, + error('Can use n_input_lines different to 3 only if using happenings'); + end; + + if isempty(default_happSpec) %#ok<*NODEF> + fnames = {'name', 'detectorFunctionName', 'inputNumber', 'happId'}; + happnames = {'line_in', 'line_out', 'line_high', 'line_low'}; + happabbrev = {'in', 'out', 'hi', 'lo'}; + hid = 0; + hs = {}; + + for li = 1:3, + for hni = 1:2, + hid = hid + 1; + hs = [hs ; ... + {[line_names(li) happabbrev{hni}], happnames{hni}, li, hid}]; + end; + end; + for li = 1:3, + for hni = 1:2, + hid = hid + 1; + hs = [hs ; ... + {[line_names(li) happabbrev{hni+2}], happnames{hni+2}, li, hid}]; + end; + end; + + for li=4:n_input_lines, + for hni = 1:4, + hid = hid + 1; + hs = [hs ; ... + {[line_names(li) happabbrev{hni}], happnames{hni}, li, hid}]; + end; + end; + + %Chuck's code to try to remap inputs + inputs = bSettings('get','INPUTLINES','all'); + for i = 1:size(hs,1) + inputnum = find(strcmp(inputs(:,1),hs{i,1}(1)) == 1,1,'first'); + if isempty(inputnum) + error(['can''t find input channel ',hs{i,1}(1),' in INPUTLINES ']); + elseif numel(inputnum) > 1 + error(['found more than one instance of input channel ',hs{i,1}(1),' in INPUTLINES ']); + end + + hs{i,3} = inputs{inputnum,2}; + end + %end Chuck's code + + default_happSpec = cell2struct(hs, fnames, 2); + end; + + if isempty(default_DOut), error('you passed in an empty matrix as a default_DOut -- can''t do that'); end; @@ -75,41 +110,56 @@ % error('sorry, default_DOut different to zero not yet supported'); end; - default_input_map = { ... - 'Cin' 1 ; ... - 'Cout' 2 ; ... - 'Lin' 3 ; ...in - 'Lout' 4 ; ... - 'Rin' 5 ; ... - 'Rout' 6 ; ... - 'Tup' 7 ; ... - }; + ncols = 7; % Keeping track of total number of state matrix columns + input_line_names = line_names; + default_input_map = cell(0, 2); + for k=1:n_input_lines, + if k > numel(input_line_names), + error('Sorry, trying to have more input lines than I can handle!'); + end; + default_input_map = [default_input_map ; ... + {[input_line_names(k) 'in'] k*2-1 ; ... + [input_line_names(k) 'out'] k*2 ; ... + }]; + end; + ncols = n_input_lines*2+1; + default_input_map = [default_input_map ; {'Tup' ncols}]; + + % default_input_map = { ... +% 'Cin' 1 ; ... +% 'Cout' 2 ; ... +% 'Lin' 3 ; ... +% 'Lout' 4 ; ... +% 'Rin' 5 ; ... +% 'Rout' 6 ; ... +% 'Tup' ncols ; ... +% }; + default_self_timer_map = { ... - 'Timer' 8 ; ... + 'Timer' ncols+1 ; ... }; default_output_map = { ... - 'DOut' 9 ; ... - 'SoundOut' 10 ; ... + 'DOut' ncols+2 ; ... + 'SoundOut' ncols+3 ; ... }; - + ncols = ncols+3; - sma = struct( ... + sma = struct( ... + 'n_input_lines', n_input_lines, ... 'input_map', {default_input_map}, ... 'self_timer_map', {default_self_timer_map}, ... 'output_map', {default_output_map}, ... - 'use_happenings', 1, ... % added by Viktor - 'n_input_lines', 3, ... % added by Viktor - ...%'use_happenings', use_happenings, ... + 'use_happenings', use_happenings, ... 'happSpec', default_happSpec, ... 'happList', {cell(0, 1)}, ... 'state_name_list', {cell(0,3)}, ... 'current_state', 0, ... - 'states', zeros(0, 10), ... + 'states', zeros(0, ncols), ... 'default_actions', {cell(0, 1)}, ... 'current_iti_state', 0, ... - 'iti_states', zeros(0, 10), ... + 'iti_states', zeros(0, ncols), ... 'default_iti_actions', {cell(0, 1)}, ... 'dio_sched_wave_cols', 8, ... 'sched_waves', struct('name', {}, 'id', {}, 'in_column', {}, ... @@ -150,7 +200,6 @@ sma = class(sma, 'StateMachineAssembler'); return; end; - %clear classes %mod by Viktor sma = class(sma, 'StateMachineAssembler'); diff --git a/Protocols/@SoundCalibration/SoundCalibration.m b/Protocols/@SoundCalibration/SoundCalibration.m index bd1aef55..27d9156e 100644 --- a/Protocols/@SoundCalibration/SoundCalibration.m +++ b/Protocols/@SoundCalibration/SoundCalibration.m @@ -3,7 +3,7 @@ %through dispatcher. This is treated like any other protocol. It is %designed to be plastic, looking at a rigs settings files, determining %the input and output lines, and building a GUI specific to that rigs -%componants. +%componants. % %Written by Chuck 2017 @@ -23,20 +23,20 @@ %--------------------------------------------------------------- % If creating an empty object, return without further ado: -if nargin==0 || (nargin==1 && ischar(varargin{1}) && strcmp(varargin{1}, 'empty')), - return; +if nargin==0 || (nargin==1 && ischar(varargin{1}) && strcmp(varargin{1}, 'empty')), + return; end; -if isa(varargin{1}, mfilename), % If first arg is an object of this class itself, we are - % Most likely responding to a callback from - % a SoloParamHandle defined in this mfile. - if length(varargin) < 2 || ~ischar(varargin{2}), - error(['If called with a "%s" object as first arg, a second arg, a ' ... - 'string specifying the action, is required\n']); - else action = varargin{2}; varargin = varargin(3:end); %#ok - end; +if isa(varargin{1}, mfilename), % If first arg is an object of this class itself, we are + % Most likely responding to a callback from + % a SoloParamHandle defined in this mfile. + if length(varargin) < 2 || ~ischar(varargin{2}), + error(['If called with a "%s" object as first arg, a second arg, a ' ... + 'string specifying the action, is required\n']); + else action = varargin{2}; varargin = varargin(3:end); %#ok + end; else % Ok, regular call with first param being the action string. - action = varargin{1}; varargin = varargin(2:end); %#ok + action = varargin{1}; varargin = varargin(2:end); %#ok end; if ~ischar(action), error('The action parameter must be a string'); end; @@ -72,16 +72,16 @@ % protocol (see below). % % 'trial_completed' Called when 'state_0' is reached in the RTLSM, -% marking final completion of a trial (and the start of +% marking final completion of a trial (and the start of % the next). % % 'close' Called when the protocol is to be closed. % % -% VARIABLES THAT DISPATCHER WILL ALWAYS INSTANTIATE FOR YOU IN YOUR +% VARIABLES THAT DISPATCHER WILL ALWAYS INSTANTIATE FOR YOU IN YOUR % PROTOCOL: % -% (These variables will be instantiated as regular Matlab variables, +% (These variables will be instantiated as regular Matlab variables, % not SoloParamHandles. For any method in your protocol (i.e., an m-file % within the @your_protocol directory) that takes "obj" as its first argument, % calling "GetSoloFunctionArgs(obj)" will instantiate all the variables below.) @@ -116,203 +116,293 @@ % dispatcher.m. See the wiki documentation for information on how to access % those histories from within your protocol and for information. % -% +% switch action, - %--------------------------------------------------------------- - % CASE INIT - %--------------------------------------------------------------- - - case 'init' - - % Make default figure. We remember to make it non-saveable; on next run - % the handle to this figure might be different, and we don't want to - % overwrite it when someone does load_data and some old value of the - % fig handle was stored as SoloParamHandle "myfig" - SoloParamHandle(obj, 'myfig', 'saveable', 0); myfig.value = double(figure); - - % Make the title of the figure be the protocol name, and if someone tries - % to close this figure, call dispatcher's close_protocol function, so it'll know - % to take it off the list of open protocols. - name = mfilename; - set(value(myfig), 'Name', name, 'Tag', name, ... - 'closerequestfcn', [mfilename,'(''close'');'], 'MenuBar', 'none'); - - alldio = bSettings('get','DIOLINES','ALL'); - allinp = bSettings('get','INPUTLINES','ALL'); - dionums = cell2mat(alldio(:,2)); - inpnums = cell2mat(allinp(:,2)); - - for i = 1:16 - inp1 = find(inpnums == i,1,'first'); - dio1 = find(dionums == 2^(( i-1)*2), 1,'first'); - dio2 = find(dionums == 2^(((i-1)*2)+1),1,'first'); - - if isempty(inp1) && isempty(dio1) && isempty(dio2) - continue; - end - - if ~isempty(inp1) - linegroups{i}{1,1} = allinp{inp1,2}; %#ok - linegroups{i}{1,2} = allinp{inp1,1}; %#ok - else - linegroups{i}{1,1} = []; %#ok - linegroups{i}{1,2} = []; %#ok - end - if ~isempty(dio1) - linegroups{i}{2,1} = alldio{dio1,2}; %#ok - linegroups{i}{2,2} = alldio{dio1,1}; %#ok - else - linegroups{i}{2,1} = []; %#ok - linegroups{i}{2,2} = []; %#ok - end - if ~isempty(dio2) - linegroups{i}{3,1} = alldio{dio2,2}; %#ok - linegroups{i}{3,2} = alldio{dio2,1}; %#ok - else - linegroups{i}{3,1} = []; %#ok - linegroups{i}{3,2} = []; %#ok - end - end - - % Generate the sounds we need. - soundserver = bSettings('get','RIGS','sound_machine_server'); - if ~isempty(soundserver) - sr = SoundManagerSection(obj,'get_sample_rate'); - Fs=sr; - lfreq=2000; - hfreq=20000; - freq = 100; - T = 1; - fcut = 110; - filter_type = 'GAUS'; - A1_sigma = 0.0500; - A2_sigma = 0.0260; - A3_sigma = 0.0135; - A4_sigma = 0.0070; - [rawA1 rawA2 normA1 normA2]=noisestim(1,1,T,fcut,Fs,filter_type); - modulator=singlenoise(1,T,[lfreq hfreq],Fs,'BUTTER'); - AUD1=normA1(1:T*sr).*modulator(1:T*sr).*A1_sigma; - AUD2=normA1(1:T*sr).*modulator(1:T*sr).*A2_sigma; - AUD3=normA1(1:T*sr).*modulator(1:T*sr).*A3_sigma; - AUD4=normA1(1:T*sr).*modulator(1:T*sr).*A4_sigma; -% snd = MakeBupperSwoop(sr,0,freq,freq, len/2, len/2, 0, 0.1, 'F1_volume_factor',0.07,'F2_volume_factor',0.07); -% silence_length = 0; -% presound_silence = zeros(1,sr*silence_length/1000); -% snd = [presound_silence, snd]; -% -% SoundManagerSection(obj,'declare_new_sound','left_sound', [snd; zeros(1,size(snd,2))]); -% SoundManagerSection(obj,'declare_new_sound','right_sound', [zeros(1,size(snd,2)); snd]); -% -% SoundManagerSection(obj,'loop_sound','left_sound',1); -% SoundManagerSection(obj,'loop_sound','right_sound',1); -% -% SoundManagerSection(obj,'declare_new_sound','center_sound',[snd;snd([ceil((sr / freq) / 2):end,1:ceil((sr / freq) / 2)-1])]); -% SoundManagerSection(obj,'loop_sound','center_sound',1); -% - if ~isempty(AUD2) - SoundManagerSection(obj, 'declare_new_sound', 'left_sound', [AUD2'; AUD2']) - end - if ~isempty(AUD1) - SoundManagerSection(obj, 'declare_new_sound', 'center_sound', [AUD1'; AUD1']) - end - if ~isempty(AUD3) - SoundManagerSection(obj, 'declare_new_sound', 'right_sound', [AUD3'; AUD3']) - end - if ~isempty(AUD4) - SoundManagerSection(obj, 'declare_new_sound', 'fourth_sound', [AUD4'; AUD4']) - end - SoundManagerSection(obj, 'send_not_yet_uploaded_sounds'); - end - - MP = get(0,'MonitorPositions'); - groupwidth = floor(MP(3)/numel(linegroups)); - - % At this point we have one SoloParamHandle, myfig - % Let's put the figure where we want it and give it a reasonable size: - set(value(myfig), 'Position', [1,floor((MP(4)-600)/2),numel(linegroups)*groupwidth,600]); - - line_names = []; - for i = 1:numel(linegroups) - if ~isempty(linegroups{i}) && ~isempty(linegroups{i}{1,2}) - SubheaderParam(obj,['Input',linegroups{i}{1,2}],{linegroups{i}{1,2};0},0,0,'position',[((i-1)*groupwidth)+1, 300, groupwidth-2,290]); - set(get_ghandle(eval(['Input',linegroups{i}{1,2}])),'FontSize',32,'BackgroundColor',[0,1,0],'HorizontalAlignment','center'); - line_names(end+1) = linegroups{i}{1,2}; %#ok - - if strcmp(linegroups{i}{1,2},'L') && ~isempty(soundserver) - ToggleParam(obj,'LeftSound', 0,0,0,'position',[((i-1)*groupwidth)+1, 10, groupwidth-2,50],'OnString', 'Sound Two ON', 'OffString', 'Sound Two OFF'); - set_callback(LeftSound,{mfilename,'play_left_sound'}); - - elseif strcmp(linegroups{i}{1,2},'R') && ~isempty(soundserver) - ToggleParam(obj,'RightSound', 0,0,0,'position',[((i-1)*groupwidth)+1, 10, groupwidth-2,50],'OnString', 'Sound Three ON', 'OffString', 'Sound Three OFF'); - set_callback(RightSound,{mfilename,'play_right_sound'}); - - elseif strcmp(linegroups{i}{1,2},'C') && ~isempty(soundserver) - ToggleParam(obj,'CenterSound', 0,0,0,'position',[((i-1)*groupwidth)+1, 10, groupwidth-2,50],'OnString', 'Sound One ON', 'OffString', 'Sound One OFF'); - set_callback(CenterSound,{mfilename,'play_center_sound'}); - - elseif strcmp(linegroups{i}{1,2},'A') && ~isempty(soundserver) - ToggleParam(obj,'FourthSound', 0,0,0,'position',[((i-1)*groupwidth)+1, 10, groupwidth-2,50],'OnString', 'Sound Four ON', 'OffString', 'Sound Four OFF'); - set_callback(FourthSound,{mfilename,'play_fourth_sound'}); + %--------------------------------------------------------------- + % CASE INIT + %--------------------------------------------------------------- + + case 'init' + + % Make default figure. We remember to make it non-saveable; on next run + % the handle to this figure might be different, and we don't want to + % overwrite it when someone does load_data and some old value of the + % fig handle was stored as SoloParamHandle "myfig" + SoloParamHandle(obj, 'myfig', 'saveable', 0); myfig.value = double(figure); + + % Make the title of the figure be the protocol name, and if someone tries + % to close this figure, call dispatcher's close_protocol function, so it'll know + % to take it off the list of open protocols. + name = mfilename; + set(value(myfig), 'Name', name, 'Tag', name, ... + 'closerequestfcn', [mfilename,'(''close'');'], 'MenuBar', 'none'); + + alldio = bSettings('get','DIOLINES','ALL'); + allinp = bSettings('get','INPUTLINES','ALL'); + dionums = cell2mat(alldio(:,2)); + inpnums = cell2mat(allinp(:,2)); + + for i = 1:16 + inp1 = find(inpnums == i,1,'first'); + dio1 = find(dionums == 2^(( i-1)*2), 1,'first'); + dio2 = find(dionums == 2^(((i-1)*2)+1),1,'first'); + + if isempty(inp1) && isempty(dio1) && isempty(dio2) + continue; + end + + if ~isempty(inp1) + linegroups{i}{1,1} = allinp{inp1,2}; %#ok + linegroups{i}{1,2} = allinp{inp1,1}; %#ok + else + linegroups{i}{1,1} = []; %#ok + linegroups{i}{1,2} = []; %#ok + end + if ~isempty(dio1) + linegroups{i}{2,1} = alldio{dio1,2}; %#ok + linegroups{i}{2,2} = alldio{dio1,1}; %#ok + else + linegroups{i}{2,1} = []; %#ok + linegroups{i}{2,2} = []; %#ok + end + if ~isempty(dio2) + linegroups{i}{3,1} = alldio{dio2,2}; %#ok + linegroups{i}{3,2} = alldio{dio2,1}; %#ok + else + linegroups{i}{3,1} = []; %#ok + linegroups{i}{3,2} = []; %#ok end end - if ~isempty(linegroups{i}) && ~isempty(linegroups{i}{2,2}) - ToggleParam(obj,['DIO',num2str(i),'_1'],0,0,0,'position',[((i-1)*groupwidth)+1, 210, groupwidth-2,50],'OnString', [linegroups{i}{2,2},' ON'], 'OffString', [linegroups{i}{2,2},' OFF']); - set_callback(eval(['DIO',num2str(i),'_1']),{mfilename,['toggle',num2str(i),'_1']}); - - end - if ~isempty(linegroups{i}) && ~isempty(linegroups{i}{3,2}) - ToggleParam(obj,['DIO',num2str(i),'_2'],0,0,0,'position',[((i-1)*groupwidth)+1, 110, groupwidth-2,50],'OnString', [linegroups{i}{3,2},' ON'], 'OffString', [linegroups{i}{3,2},' OFF']); - set_callback(eval(['DIO',num2str(i),'_2']),{mfilename,['toggle',num2str(i),'_2']}); + + % Generate the sounds we need. + soundserver = bSettings('get','RIGS','sound_machine_server'); + if ~isempty(soundserver) + sr = SoundManagerSection(obj,'get_sample_rate'); + Fs=sr; + lfreq=2000; + hfreq=20000; + freq = 100; + T = 1; + fcut = 110; + filter_type = 'GAUS'; + A1_sigma = 0.3199; %0.0500; + A2_sigma = 0.1230;%0.0260; + A3_sigma = 0.0473;%0.0135; + A4_sigma = 0.0182;%0.0070; + A5_sigma = 0.0070; + [rawA1 rawA2 normA1 normA2]=noisestim(1,1,T,fcut,Fs,filter_type); + modulator=singlenoise(1,T,[lfreq hfreq],Fs,'BUTTER'); + AUD1=normA1(1:T*sr).*modulator(1:T*sr).*A1_sigma; + AUD2=normA1(1:T*sr).*modulator(1:T*sr).*A2_sigma; + AUD3=normA1(1:T*sr).*modulator(1:T*sr).*A3_sigma; + AUD4=normA1(1:T*sr).*modulator(1:T*sr).*A4_sigma; + AUD5=normA1(1:T*sr).*modulator(1:T*sr).*A5_sigma; + % snd = MakeBupperSwoop(sr,0,freq,freq, len/2, len/2, 0, 0.1, 'F1_volume_factor',0.07,'F2_volume_factor',0.07); + % silence_length = 0; + % presound_silence = zeros(1,sr*silence_length/1000); + % snd = [presound_silence, snd]; + % + % SoundManagerSection(obj,'declare_new_sound','left_sound', [snd; zeros(1,size(snd,2))]); + % SoundManagerSection(obj,'declare_new_sound','right_sound', [zeros(1,size(snd,2)); snd]); + % + % SoundManagerSection(obj,'loop_sound','left_sound',1); + % SoundManagerSection(obj,'loop_sound','right_sound',1); + % + % SoundManager Section(obj,'declare_new_sound','center_sound',[snd;snd([ceil((sr / freq) / 2):end,1:ceil((sr / freq) / 2)-1])]); + % SoundManagerSection(obj,'loop_sound','center_sound',1); + % + if ~isempty(AUD2) + SoundManagerSection(obj, 'declare_new_sound', 'left_sound', [AUD2'; AUD2']) + end + if ~isempty(AUD1) + SoundManagerSection(obj, 'declare_new_sound', 'center_sound', [AUD1'; AUD1']) + end + if ~isempty(AUD3) + SoundManagerSection(obj, 'declare_new_sound', 'right_sound', [AUD3'; AUD3']) + end + if ~isempty(AUD4) + SoundManagerSection(obj, 'declare_new_sound', 'fourth_sound', [AUD4'; AUD4']) + end + if ~isempty(AUD5) + SoundManagerSection(obj, 'declare_new_sound', 'fifth_sound', [AUD5'; AUD5']) + end + SoundManagerSection(obj, 'send_not_yet_uploaded_sounds'); end - end - - SoloParamHandle(obj,'LineGroups','value',linegroups); - SoloParamHandle(obj,'LineNames','value',line_names); - - scr = timer; - set(scr,'Period', 0.2,'ExecutionMode','FixedRate','TasksToExecute',Inf,... - 'BusyMode','drop','TimerFcn',[mfilename,'(''close_continued'')']); - SoloParamHandle(obj, 'stopping_complete_timer', 'value', scr); + + % Get monitor dimensions for dynamic GUI sizing + MP = get(0,'MonitorPositions'); + + % Calculate individual group width based on screen dimensions and number of groups + groupwidth = floor((MP(3)/2)/numel(linegroups)); + + padding = 10 % padding around GUI elements + + % Calculate total width needed for all groups combined + total_width = floor(numel(linegroups) * groupwidth+padding); + total_height = 400 % total height of GUI - SoundCalibration(obj,'prepare_next_trial'); - - dispatcher('Run'); + label_height = 140 % height of port label + button_height = 25 + + % Center the GUI horizontally on screen + left_pos = floor((MP(3) - total_width) / 2); + + % Set figure position with centered alignment and fixed height of 400 pixels + % position vector: [left-right, down-up, width, height], the origo is + % the bottom left corner + set(value(myfig), 'Position', [left_pos, floor((MP(4)-total_height)/2), total_width, total_height]); + + % Initialize array to store line names + line_names = []; + + % Iterate through each line group + for i = 1:numel(linegroups) + % Check if current group exists and has valid parameters + if ~isempty(linegroups{i}) && ~isempty(linegroups{i}{1,2}) + % port label position + SubheaderParam( ... + obj, ... + ['Input',linegroups{i}{1,2}], ... + {linegroups{i}{1,2};0},0,0, ... + 'position',[((i-1)*groupwidth)+padding, ... + total_height-padding-label_height, ... + groupwidth-padding, ... + label_height ... + ]); + + % port label aesthetics + set(get_ghandle(eval(['Input',linegroups{i}{1,2}])), ... + 'FontSize',32, ... % Large font size for visibility + 'BackgroundColor',[0,1,0], ... % Green background + 'HorizontalAlignment','center'); % Center align text + + % Store line name for later reference + line_names(end+1) = linegroups{i}{1,2}; %#ok + + % Add sound controls based on line identifier + if strcmp(linegroups{i}{1,2},'L') && ~isempty(soundserver) + % Left channel sound toggle + ToggleParam(obj, ... + 'LeftSound', 0,0,0, ... + 'position',[((i-1)*groupwidth)+padding, padding, groupwidth-padding,button_height], ... + 'OnString', 'Sound Two ON', ... + 'OffString', 'Sound Two OFF'); + set_callback(LeftSound,{mfilename,'play_left_sound'}); + + elseif strcmp(linegroups{i}{1,2},'R') && ~isempty(soundserver) + % Right channel sound toggle + ToggleParam(obj, ... + 'RightSound', 0,0,0, ... + 'position',[((i-1)*groupwidth)+padding, padding, groupwidth-padding,button_height], ... + 'OnString', 'Sound Three ON', ... + 'OffString', 'Sound Three OFF'); + set_callback(RightSound,{mfilename,'play_right_sound'}); + + elseif strcmp(linegroups{i}{1,2},'C') && ~isempty(soundserver) + % Center channel sound toggle + ToggleParam(obj, ... + 'CenterSound', 0,0,0, ... + 'position',[((i-1)*groupwidth)+padding, padding, groupwidth-padding,button_height], ... + 'OnString', 'Sound One ON', ... + 'OffString', 'Sound One OFF'); + set_callback(CenterSound,{mfilename,'play_center_sound'}); + + elseif strcmp(linegroups{i}{1,2},'A') && ~isempty(soundserver) + % Additional channel sound toggle + ToggleParam(obj, ... + 'FourthSound', 0,0,0, ... + 'position',[((i-1)*groupwidth)+padding, padding, groupwidth-padding,button_height], ... + 'OnString', 'Sound Four ON', ... + 'OffString', 'Sound Four OFF'); + set_callback(FourthSound,{mfilename,'play_fourth_sound'}); + end + + % Handle case for empty line identifier but existing group + elseif ~isempty(linegroups{i}) && isempty(linegroups{i}{1,2}) + ToggleParam(obj, ... + 'FifthSound', 0,0,0, ... + 'position',[((i-1)*groupwidth)+padding, padding, groupwidth-padding,button_height], ... + 'OnString', 'Sound Five ON', ... + 'OffString', 'Sound Five OFF'); + set_callback(FifthSound,{mfilename,'play_fifth_sound'}); + end + + % Add first digital I/O control if available + if ~isempty(linegroups{i}) && ~isempty(linegroups{i}{2,2}) + ToggleParam(obj, ... + ['DIO',num2str(i),'_1'],0,0,0, ... + 'position',[((i-1)*groupwidth)+padding, 2*button_height+padding, groupwidth-padding,button_height], ... + 'OnString', [linegroups{i}{2,2},' ON'], ... + 'OffString', [linegroups{i}{2,2},' OFF']); + set_callback(eval(['DIO',num2str(i),'_1']),{mfilename,['toggle',num2str(i),'_1']}); + end + + % Add second digital I/O control if available + if ~isempty(linegroups{i}) && ~isempty(linegroups{i}{3,2}) + ToggleParam(obj, ... + ['DIO',num2str(i),'_2'],0,0,0, ... + 'position',[((i-1)*groupwidth)+padding, button_height+padding, groupwidth-padding,button_height], ... + 'OnString', [linegroups{i}{3,2},' ON'], ... + 'OffString', [linegroups{i}{3,2},' OFF']); + set_callback(eval(['DIO',num2str(i),'_2']),{mfilename,['toggle',num2str(i),'_2']}); + end + end + + + SoloParamHandle(obj,'LineGroups','value',linegroups); + SoloParamHandle(obj,'LineNames','value',line_names); - - case 'play_left_sound' + scr = timer; + set(scr,'Period', 0.2,'ExecutionMode','FixedRate','TasksToExecute',Inf,... + 'BusyMode','drop','TimerFcn',[mfilename,'(''close_continued'')']); + SoloParamHandle(obj, 'stopping_complete_timer', 'value', scr); + + SoundCalibration(obj,'prepare_next_trial'); + + dispatcher('Run'); + + + case 'play_left_sound' %% play_left_sound if value(LeftSound) == 1 SoundManagerSection(obj,'play_sound','left_sound'); else SoundManagerSection(obj,'stop_sound','left_sound'); end - - case 'play_right_sound' + + case 'play_right_sound' %% play_right_sound if value(RightSound) == 1 SoundManagerSection(obj,'play_sound','right_sound'); else SoundManagerSection(obj,'stop_sound','right_sound'); - end - - case 'play_center_sound' + end + + case 'play_center_sound' %% play_center_sound if value(CenterSound) == 1 SoundManagerSection(obj,'play_sound','center_sound'); else SoundManagerSection(obj,'stop_sound','center_sound'); - end + end - case 'play_fourth_sound' + case 'play_fourth_sound' %% play_fourth_sound if value(FourthSound) == 1 SoundManagerSection(obj,'play_sound','fourth_sound'); else SoundManagerSection(obj,'stop_sound','fourth_sound'); - end + end + case 'play_fifth_sound' + %% play_fifth_sound + if value(FifthSound) == 1 + SoundManagerSection(obj,'play_sound','fifth_sound'); + else + SoundManagerSection(obj,'stop_sound','fifth_sound'); + end case 'toggle1_1' %% case toggle1_1 linegroups = value(LineGroups); @@ -322,7 +412,7 @@ %% case toggle1_2 linegroups = value(LineGroups); dispatcher('toggle_bypass',log2(linegroups{1}{3,1})); - + case 'toggle2_1' %% case toggle1_1 linegroups = value(LineGroups); @@ -332,7 +422,7 @@ %% case toggle1_2 linegroups = value(LineGroups); dispatcher('toggle_bypass',log2(linegroups{2}{3,1})); - + case 'toggle3_1' %% case toggle1_1 linegroups = value(LineGroups); @@ -342,7 +432,7 @@ %% case toggle1_2 linegroups = value(LineGroups); dispatcher('toggle_bypass',log2(linegroups{3}{3,1})); - + case 'toggle4_1' %% case toggle1_1 linegroups = value(LineGroups); @@ -352,7 +442,7 @@ %% case toggle1_2 linegroups = value(LineGroups); dispatcher('toggle_bypass',log2(linegroups{4}{3,1})); - + case 'toggle5_1' %% case toggle1_1 linegroups = value(LineGroups); @@ -362,7 +452,7 @@ %% case toggle1_2 linegroups = value(LineGroups); dispatcher('toggle_bypass',log2(linegroups{5}{3,1})); - + case 'toggle6_1' %% case toggle1_1 linegroups = value(LineGroups); @@ -372,7 +462,7 @@ %% case toggle1_2 linegroups = value(LineGroups); dispatcher('toggle_bypass',log2(linegroups{6}{3,1})); - + case 'toggle7_1' %% case toggle1_1 linegroups = value(LineGroups); @@ -382,7 +472,7 @@ %% case toggle1_2 linegroups = value(LineGroups); dispatcher('toggle_bypass',log2(linegroups{7}{3,1})); - + case 'toggle8_1' %% case toggle1_1 linegroups = value(LineGroups); @@ -391,8 +481,8 @@ case 'toggle8_2' %% case toggle1_2 linegroups = value(LineGroups); - dispatcher('toggle_bypass',log2(linegroups{8}{3,1})); - + dispatcher('toggle_bypass',log2(linegroups{8}{3,1})); + case 'toggle9_1' %% case toggle1_1 linegroups = value(LineGroups); @@ -401,8 +491,8 @@ case 'toggle9_2' %% case toggle1_2 linegroups = value(LineGroups); - dispatcher('toggle_bypass',log2(linegroups{9}{3,1})); - + dispatcher('toggle_bypass',log2(linegroups{9}{3,1})); + case 'toggle10_1' %% case toggle1_1 linegroups = value(LineGroups); @@ -411,8 +501,8 @@ case 'toggle10_2' %% case toggle1_2 linegroups = value(LineGroups); - dispatcher('toggle_bypass',log2(linegroups{10}{3,1})); - + dispatcher('toggle_bypass',log2(linegroups{10}{3,1})); + case 'toggle11_1' %% case toggle1_1 linegroups = value(LineGroups); @@ -421,8 +511,8 @@ case 'toggle11_2' %% case toggle1_2 linegroups = value(LineGroups); - dispatcher('toggle_bypass',log2(linegroups{11}{3,1})); - + dispatcher('toggle_bypass',log2(linegroups{11}{3,1})); + case 'toggle12_1' %% case toggle1_1 linegroups = value(LineGroups); @@ -431,8 +521,8 @@ case 'toggle12_2' %% case toggle1_2 linegroups = value(LineGroups); - dispatcher('toggle_bypass',log2(linegroups{12}{3,1})); - + dispatcher('toggle_bypass',log2(linegroups{12}{3,1})); + case 'toggle13_1' %% case toggle1_1 linegroups = value(LineGroups); @@ -441,8 +531,8 @@ case 'toggle13_2' %% case toggle1_2 linegroups = value(LineGroups); - dispatcher('toggle_bypass',log2(linegroups{13}{3,1})); - + dispatcher('toggle_bypass',log2(linegroups{13}{3,1})); + case 'toggle14_1' %% case toggle1_1 linegroups = value(LineGroups); @@ -451,8 +541,8 @@ case 'toggle14_2' %% case toggle1_2 linegroups = value(LineGroups); - dispatcher('toggle_bypass',log2(linegroups{14}{3,1})); - + dispatcher('toggle_bypass',log2(linegroups{14}{3,1})); + case 'toggle15_1' %% case toggle1_1 linegroups = value(LineGroups); @@ -461,8 +551,8 @@ case 'toggle15_2' %% case toggle1_2 linegroups = value(LineGroups); - dispatcher('toggle_bypass',log2(linegroups{15}{3,1})); - + dispatcher('toggle_bypass',log2(linegroups{15}{3,1})); + case 'toggle16_1' %% case toggle1_1 linegroups = value(LineGroups); @@ -471,70 +561,70 @@ case 'toggle16_2' %% case toggle1_2 linegroups = value(LineGroups); - dispatcher('toggle_bypass',log2(linegroups{16}{3,1})); - - %--------------------------------------------------------------- - % CASE PREPARE_NEXT_TRIAL - %--------------------------------------------------------------- - case 'prepare_next_trial' - line_names = value(LineNames); - sma = StateMachineAssembler('full_trial_structure','use_happenings',1,'n_input_lines',numel(line_names),'line_names',line_names); - sma = add_state(sma, 'name', 'the_only_state', 'self_timer',1e4, 'input_to_statechange', {'Tup', 'final_state'}); - sma = add_state(sma, 'name', 'final_state', 'self_timer',1e4, 'input_to_statechange', {'Tup', 'check_next_trial_ready'}); - dispatcher('send_assembler', sma, 'final_state'); - - %--------------------------------------------------------------- - % CASE TRIAL_COMPLETED - %--------------------------------------------------------------- - case 'trial_completed' - - - %--------------------------------------------------------------- - % CASE UPDATE - %--------------------------------------------------------------- - case 'update' - pe = parsed_events; %#ok - linenames = value(LineNames); - - for i = 1:numel(linenames) - poketimes = eval(['pe.pokes.',linenames(i)]); - if ~isempty(poketimes) && isnan(poketimes(end,2)) - set(get_ghandle(eval(['Input',linenames(i)])),'BackgroundColor',[1,0,0]); - - str = get(get_ghandle(eval(['Input',linenames(i)])),'string'); - str{2} = size(poketimes,1); - set(get_ghandle(eval(['Input',linenames(i)])),'string',str); - - else - set(get_ghandle(eval(['Input',linenames(i)])),'BackgroundColor',[0,1,0]); - end - end - - %--------------------------------------------------------------- - % CASE CLOSE - %--------------------------------------------------------------- - case 'close' - - dispatcher('Stop'); - - %Let's pause until we know dispatcher is done running - set(value(stopping_complete_timer),'TimerFcn',[mfilename,'(''close_continued'');']); - start(value(stopping_complete_timer)); - - case 'close_continued' - - if value(stopping_process_completed) - stop(value(stopping_complete_timer)); %Stop looping. - %dispatcher('set_protocol',''); - - if exist('myfig', 'var') && isa(myfig, 'SoloParamHandle') && ishandle(value(myfig)), %#ok - delete(value(myfig)); - end; - delete_sphandle('owner', ['^@' class(obj) '$']); - dispatcher('set_protocol',''); - end - otherwise, - warning('Unknown action! "%s"\n', action); %#ok + dispatcher('toggle_bypass',log2(linegroups{16}{3,1})); + + %--------------------------------------------------------------- + % CASE PREPARE_NEXT_TRIAL + %--------------------------------------------------------------- + case 'prepare_next_trial' + line_names = value(LineNames); + sma = StateMachineAssembler('full_trial_structure','use_happenings',1,'n_input_lines',numel(line_names),'line_names',line_names); + sma = add_state(sma, 'name', 'the_only_state', 'self_timer',1e4, 'input_to_statechange', {'Tup', 'final_state'}); + sma = add_state(sma, 'name', 'final_state', 'self_timer',1e4, 'input_to_statechange', {'Tup', 'check_next_trial_ready'}); + dispatcher('send_assembler', sma, 'final_state'); + + %--------------------------------------------------------------- + % CASE TRIAL_COMPLETED + %--------------------------------------------------------------- + case 'trial_completed' + + + %--------------------------------------------------------------- + % CASE UPDATE + %--------------------------------------------------------------- + case 'update' + pe = parsed_events; %#ok + linenames = value(LineNames); + + for i = 1:numel(linenames) + poketimes = eval(['pe.pokes.',linenames(i)]); + if ~isempty(poketimes) && isnan(poketimes(end,2)) + set(get_ghandle(eval(['Input',linenames(i)])),'BackgroundColor',[1,0,0]); + + str = get(get_ghandle(eval(['Input',linenames(i)])),'string'); + str{2} = size(poketimes,1); + set(get_ghandle(eval(['Input',linenames(i)])),'string',str); + + else + set(get_ghandle(eval(['Input',linenames(i)])),'BackgroundColor',[0,1,0]); + end + end + + %--------------------------------------------------------------- + % CASE CLOSE + %--------------------------------------------------------------- + case 'close' + + dispatcher('Stop'); + + %Let's pause until we know dispatcher is done running + set(value(stopping_complete_timer),'TimerFcn',[mfilename,'(''close_continued'');']); + start(value(stopping_complete_timer)); + + case 'close_continued' + + if value(stopping_process_completed) + stop(value(stopping_complete_timer)); %Stop looping. + %dispatcher('set_protocol',''); + + if exist('myfig', 'var') && isa(myfig, 'SoloParamHandle') && ishandle(value(myfig)), %#ok + delete(value(myfig)); + end; + delete_sphandle('owner', ['^@' class(obj) '$']); + dispatcher('set_protocol',''); + end + otherwise, + warning('Unknown action! "%s"\n', action); %#ok end; return; From 28919174b4788f9dc18af87e123f3c8ef28fdd98 Mon Sep 17 00:00:00 2001 From: viktorpm <50667179+viktorpm@users.noreply.github.com> Date: Mon, 3 Mar 2025 17:26:36 +0000 Subject: [PATCH 017/164] djust sigma values in SoundCalibration.m (#14) * djust sigma values in SoundCalibration.m - Updated A1_sigma to 0.007. - Modified A2_sigma to use geometric mean of 0.05 and 0.007. - Set A3_sigma, A4_sigma, and A5_sigma to 0.05. - Retained previous values as comments for reference. * Update calibration values max, min, mean and middle between mean&max and mean&min --------- Co-authored-by: ledapent Co-authored-by: Leda Pentousi <106737328+ledapent@users.noreply.github.com> --- Protocols/@SoundCalibration/SoundCalibration.m | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Protocols/@SoundCalibration/SoundCalibration.m b/Protocols/@SoundCalibration/SoundCalibration.m index 27d9156e..250ff610 100644 --- a/Protocols/@SoundCalibration/SoundCalibration.m +++ b/Protocols/@SoundCalibration/SoundCalibration.m @@ -188,10 +188,10 @@ T = 1; fcut = 110; filter_type = 'GAUS'; - A1_sigma = 0.3199; %0.0500; - A2_sigma = 0.1230;%0.0260; - A3_sigma = 0.0473;%0.0135; - A4_sigma = 0.0182;%0.0070; + A1_sigma = 0.0500; + A2_sigma = 0.0306 %0.1230;%0.0260; + A3_sigma = 0.0187 %0.0473;%0.0135; + A4_sigma = 0.0114 %0.0182;%0.0070; A5_sigma = 0.0070; [rawA1 rawA2 normA1 normA2]=noisestim(1,1,T,fcut,Fs,filter_type); modulator=singlenoise(1,T,[lfreq hfreq],Fs,'BUTTER'); From a4dfb4de56396dd5b6f6401657d0312cc039ffad Mon Sep 17 00:00:00 2001 From: ArpitAgarwal Date: Tue, 18 Mar 2025 15:59:51 +0000 Subject: [PATCH 018/164] ignoring mac specific files --- .gitignore | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.gitignore b/.gitignore index a71a258a..f3381d9c 100644 --- a/.gitignore +++ b/.gitignore @@ -6,5 +6,8 @@ ExperPort/Settings/Settings_Custom_v2_0.conf ExperPort/Settings/Settings_Custom.conf ExperPort/sendsummary_error_log.txt PASSWORD_CONFIG-DO_NOT_VERSIONCONTROL.mat +.gitattributes +.DS_Store +Screenshot 2025-01-22 at 11.22.30.png From b2b6463858937b53b47c1e6cdeccf794d3e523e9 Mon Sep 17 00:00:00 2001 From: ArpitAgarwal Date: Tue, 18 Mar 2025 16:04:57 +0000 Subject: [PATCH 019/164] adding new training protocol: 1 step for cenre poke --- .../Arpit_CentrePokeTraining.m | 262 ++++++++++ .../Arpit_CentrePokeTrainingSMA.m | 393 +++++++++++++++ .../Arpit_CentrePoke_TrainingStages.m | 141 ++++++ .../@Arpit_CentrePokeTraining/ParamsSection.m | 469 ++++++++++++++++++ 4 files changed, 1265 insertions(+) create mode 100644 Protocols/@Arpit_CentrePokeTraining/Arpit_CentrePokeTraining.m create mode 100644 Protocols/@Arpit_CentrePokeTraining/Arpit_CentrePokeTrainingSMA.m create mode 100644 Protocols/@Arpit_CentrePokeTraining/Arpit_CentrePoke_TrainingStages.m create mode 100644 Protocols/@Arpit_CentrePokeTraining/ParamsSection.m diff --git a/Protocols/@Arpit_CentrePokeTraining/Arpit_CentrePokeTraining.m b/Protocols/@Arpit_CentrePokeTraining/Arpit_CentrePokeTraining.m new file mode 100644 index 00000000..3bf0ddb3 --- /dev/null +++ b/Protocols/@Arpit_CentrePokeTraining/Arpit_CentrePokeTraining.m @@ -0,0 +1,262 @@ +% Arpit_CentrePokeTraining protocol +% Arpit, 12 March 2025 + +function [obj] = Arpit_CentrePokeTraining(varargin) + +% Default object is of our own class (mfilename); +% we inherit only from Plugins + +obj = class(struct, mfilename, pokesplot2, saveload, sessionmodel, soundmanager, soundui, antibias, ... + water, distribui, punishui, comments, soundtable, sqlsummary,reinforcement); + +%--------------------------------------------------------------- +% BEGIN SECTION COMMON TO ALL PROTOCOLS, DO NOT MODIFY +%--------------------------------------------------------------- + +% If creating an empty object, return without further ado: +if nargin==0 || (nargin==1 && ischar(varargin{1}) && strcmp(varargin{1}, 'empty')), + return; +end; + +if isa(varargin{1}, mfilename), % If first arg is an object of this class itself, we are + % Most likely responding to a callback from + % a SoloParamHandle defined in this mfile. + if length(varargin) < 2 || ~ischar(varargin{2}), + error(['If called with a "%s" object as first arg, a second arg, a ' ... + 'string specifying the action, is required\n']); + else action = varargin{2}; varargin = varargin(3:end); %#ok + end; +else % Ok, regular call with first param being the action string. + action = varargin{1}; varargin = varargin(2:end); %#ok +end; + +GetSoloFunctionArgs(obj); + +switch action, + + %% init + case 'init' + dispatcher('set_trialnum_indicator_flag'); + hackvar = 10; SoloFunctionAddVars('SessionModel', 'ro_args', 'hackvar'); %#ok + SoloParamHandle(obj, 'myfig', 'saveable', 0); myfig.value = figure; + + % Make the title of the figure be the protocol name, and if someone tries + % to close this figure, call dispatcher's close_protocol function, so it'll know + % to take it off the list of open protocols. + name = mfilename; + set(value(myfig), 'Name', name, 'Tag', name, ... + 'closerequestfcn', 'dispatcher(''close_protocol'')', 'MenuBar', 'none'); + % At this point we have one SoloParamHandle, myfig + % Let's put the figure where we want it and give it a reasonable size: + set(value(myfig), 'Position', [485 144 850 680]); + + SoloParamHandle(obj, 'nsessions_healthy_number_of_pokes', 'value', 0, 'save_with_settings', 1); + SoloParamHandle(obj, 'post_DelComp_protocol', 'value', '', 'save_with_settings', 1); + SoloParamHandle(obj, 'post_DelComp_settings_filename', 'value', '', 'save_with_settings', 1); + + + SoloParamHandle(obj, 'hit_history', 'value', []); + DeclareGlobals(obj, 'ro_args', {'hit_history'}); + SoloFunctionAddVars('ParamsSection', 'rw_args', 'hit_history'); + + %pair_history changed to stimulus_history (from AthenaDelayComp) + % SoloParamHandle(obj, 'stimulus_history', 'value', []); + % DeclareGlobals(obj, 'ro_args', {'stimulus_history'}); + % SoloFunctionAddVars('StimulusSection', 'rw_args', 'stimulus_history'); + + SoloParamHandle(obj, 'violation_history', 'value', []); + DeclareGlobals(obj, 'ro_args', {'violation_history'}); + SoloFunctionAddVars('ParamsSection', 'rw_args', 'violation_history'); + + SoloParamHandle(obj, 'timeout_history', 'value', []); + DeclareGlobals(obj, 'ro_args', {'timeout_history'}); + SoloFunctionAddVars('ParamsSection', 'rw_args', 'timeout_history'); + + + SoundManagerSection(obj, 'init'); + x = 5; y = 5; % Initial position on main GUI window + [x, y] = SavingSection(obj, 'init', x, y); + + %% slow ramp up of water amount + %%the water volume is controlled by a 5-parameter logistic function: WaterAmount(t) = maxasymp + (minasymp/(1+(t/inflp)^slp).^assym) + NumeditParam(obj, 'maxasymp', 38, x,y,'label','maxasymp','TooltipString',... + 'the water volume is controlled by a 5-parameter logistic function: WaterAmount(trialnum) = maxasymp + (minasymp/(1+(trialnum/inflp)^slp).^assym)'); + next_row(y); + NumeditParam(obj, 'slp', 3, x,y,'label','slp','TooltipString','Water Modulation: Slope of the logistic function'); + next_row(y); + NumeditParam(obj, 'inflp', 350, x,y,'label','inflp','TooltipString','Water Modulation: concentration at the inflection point'); + next_row(y); + NumeditParam(obj, 'minasymp', -21, x,y,'label','inflp','TooltipString','Water Modulation: minimum asymptote'); + next_row(y); + NumeditParam(obj, 'assym', 0.7, x,y,'label','assym','TooltipString','Water Modulation: asymmetry factor'); + next_row(y); + DispParam(obj, 'trial_1', 0, x, y, 'TooltipString', 'uL on first trial'); + next_row(y); + DispParam(obj, 'trial_150', 0, x, y, 'TooltipString', 'uL on trial 150'); + next_row(y); + DispParam(obj, 'trial_300', 0, x, y, 'TooltipString', 'uL on trial 300'); + next_row(y); + set_callback({maxasymp;slp;inflp;minasymp;assym}, {mfilename, 'change_water_modulation_params'}); + feval(mfilename, obj, 'change_water_modulation_params'); + + %AthenaSMA changed to SoundCatSMA (From AthenaDelayComp) + SoloFunctionAddVars('Arpit_CentrePokeTrainingSMA', 'ro_args', ... + {'maxasymp';'slp';'inflp';'minasymp';'assym'}); + [x, y] = WaterValvesSection(obj, 'init', x, y); + + % For plotting with the pokesplot plugin, we need to tell it what + % colors to plot with: + my_state_colors = Arpit_CentrePokeTrainingSMA(obj, 'get_state_colors'); + % In pokesplot, the poke colors have a default value, so we don't need + % to specify them, but here they are so you know how to change them. + my_poke_colors = struct( ... + 'L', 0.6*[1 0.66 0], ... + 'C', [0 0 0], ... + 'R', 0.9*[1 0.66 0]); + + [x, y] = PokesPlotSection(obj, 'init', x, y, ... + struct('states', my_state_colors, 'pokes', my_poke_colors)); next_row(y); + + [x, y] = CommentsSection(obj, 'init', x, y); + SessionDefinition(obj, 'init', x, y, value(myfig)); next_row(y, 2); %#ok + SessionDefinition(obj, 'set_old_style_parsing_flag',0); + % [x, y] = PunishmentSection(obj, 'init', x, y); %#ok + + next_column(x); y=5; + [x, y] = OverallPerformanceSection(obj, 'init', x, y); + [x, y] = StimulatorSection(obj, 'init', x, y); next_row(y, 1.3); + [x, y] = ParamsSection(obj, 'init', x, y); %#ok + [x, y] = SoundSection(obj,'init',x,y); +% [x, y] = PlayStimuli(obj,'init',x,y); + % [x, y] = StimulusSection(obj,'init',x,y); + % [x, y] = SoftPokeStayInterface(obj, 'add', 'soft_cp', x, y); + figpos = get(double(gcf), 'Position'); + [expmtr, rname]=SavingSection(obj, 'get_info'); + HeaderParam(obj, 'prot_title', [mfilename ': ' expmtr ', ' rname], x, y, 'position', [10 figpos(4)-25, 800 20]); + + Arpit_CentrePokeTrainingSMA(obj, 'init'); + + feval(mfilename, obj, 'prepare_next_trial'); + + %% change_water_modulation_params + case 'change_water_modulation_params' + display_guys = [1 150 300]; + for i=1:numel(display_guys) + t = display_guys(i); + + myvar = eval(sprintf('trial_%d', t)); + myvar.value = maxasymp + (minasymp/(1+(t/inflp)^slp).^assym); + end + + %% prepare next trial + case 'prepare_next_trial' + + ParamsSection(obj, 'prepare_next_trial'); + % Run SessionDefinition *after* ParamsSection so we know whether the + % trial was a violation or not + SessionDefinition(obj, 'next_trial'); + StimulatorSection(obj, 'update_values'); + OverallPerformanceSection(obj, 'evaluate'); + % StimulusSection(obj,'prepare_next_trial'); + SoundManagerSection(obj, 'send_not_yet_uploaded_sounds'); + + + nTrials.value = n_done_trials; + + [sma, prepare_next_trial_states] = Arpit_CentrePokeTrainingSMA(obj, 'prepare_next_trial'); + + % Default behavior of following call is that every 20 trials, the data + % gets saved, not interactive, no commit to CVS. + SavingSection(obj, 'autosave_data'); + + CommentsSection(obj, 'clear_history'); % Make sure we're not storing unnecessary history + if n_done_trials==1 % Auto-append date for convenience. + CommentsSection(obj, 'append_date'); CommentsSection(obj, 'append_line', ''); + end + + if n_done_trials==1 + [expmtr, rname]=SavingSection(obj, 'get_info'); + prot_title.value=[mfilename ' on rig ' get_hostname ' : ' expmtr ', ' rname '. Started at ' datestr(now, 'HH:MM')]; + end + + try send_n_done_trials(obj); end + + %% trial_completed + case 'trial_completed' + + % Do any updates in the protocol that need doing: + feval(mfilename, 'update'); + % And PokesPlot needs completing the trial: + PokesPlotSection(obj, 'trial_completed'); + %% update + case 'update' + PokesPlotSection(obj, 'update'); + + + %% close + case 'close' + PokesPlotSection(obj, 'close'); + %PunishmentSection(obj, 'close'); + ParamsSection(obj, 'close'); + StimulusSection(obj,'close'); + + if exist('myfig', 'var') && isa(myfig, 'SoloParamHandle') && ishandle(value(myfig)) %#ok + delete(value(myfig)); + end + delete_sphandle('owner', ['^@' class(obj) '$']); + + + %% end_session + case 'end_session' + prot_title.value = [value(prot_title) ', Ended at ' datestr(now, 'HH:MM')]; + + + %% pre_saving_settings + case 'pre_saving_settings' + + StimulusSection(obj,'hide'); + SessionDefinition(obj, 'run_eod_logic_without_saving'); + perf = OverallPerformanceSection(obj, 'evaluate'); + cp_durs = ParamsSection(obj, 'get_cp_history'); +% [classperf tot_perf]= StimulusSection(obj, 'get_class_perform'); + + %% + % YOU NEED TO CHANGE THIS +% [stimuli] = StimulusSection(obj,'get_stimuli'); + %% + + [stim1dur] = ParamsSection(obj,'get_stimdur_history'); + %stim_history = StimulatorSection(obj,'get_history'); + +% CommentsSection(obj, 'append_line', ... +% sprintf(['ntrials = %d, violations = %.2f, timeouts=%.2f, hits = %.2f\n', ... +% 'pre-Go cue went from %.3f to %.3f (delta=%.3f)\n', ... +% 'Low = %.2f, High = %.2f'], ... +% perf(1), perf(2), perf(3), perf(6), cp_durs(1), cp_durs(end), cp_durs(end)-cp_durs(1), classperf(1),classperf(2))); + + pd.hits=hit_history(:); + pd.sides=previous_sides(:); + pd.viols=violation_history(:); + pd.timeouts=timeout_history(:); +% pd.performance=tot_perf(:); + pd.cp_durs=cp_durs(:); + + +% pd.stimuli=stimuli(:); + % Athena: look into pair_history perhaps stimulus_history and stimuli + % are the same + %pd.stimulus=stimulus_history(:); + + pd.stim1dur=stim1dur(:); + + %pd.stimul=stim_history(:); + + sendsummary(obj,'protocol_data',pd); + + %% otherwise + otherwise + warning('Unknown action! "%s"\n', action); +end + +return; + diff --git a/Protocols/@Arpit_CentrePokeTraining/Arpit_CentrePokeTrainingSMA.m b/Protocols/@Arpit_CentrePokeTraining/Arpit_CentrePokeTrainingSMA.m new file mode 100644 index 00000000..86d84d27 --- /dev/null +++ b/Protocols/@Arpit_CentrePokeTraining/Arpit_CentrePokeTrainingSMA.m @@ -0,0 +1,393 @@ +function [varargout] = Arpit_CentrePokeTrainingSMA(obj, action) + +%%%%%%%%%%%%%%%%%%% TRAINING STAGES %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +%% Stage 1: Light Chasing to introduce the Reward Location +% The rat learns to associate the side pokes as the reward location. The +% light is always ON on the chosen side until the rat pokes and gets a reward immediately by playing +% reward sound and water delivery. On wrong side play punishment sound. + +%% Stage 2: Light Chasing continues, introducing Timeout Punishment +% The rat still gets reward on the side pokes but there is a timeout with sound and the +% rat is punished by additional time. The training continues as previous +% stage. + +%% Stage 3: Introduce Centre Nose Poke and play Reward Sound as Go Cue until settling in time +% Centre Poke LED on starting with 1 ms and increment by 1 percent until it reaches the max cp time. (should take around 700-800 trials). Take into +% account when rat removes poke before go Cue and dynamically change the cp_time if rats is failing many times. Timeout is now when the rat doesn't +% centre poke within the set time. Side lights still directs towards reward location. This stage is basically the settling_In stage +% where the time period is the entire length of the cp_time. This is further subdivided based upon the centre poke time. In sub-stage +% 1 until settling in time, there is no violation as the rat is fidgety intitially when starting to poke. + +%% Stage 4: Continue Centre Poke Training with increasing CP Time, and introduce violation +% Once the rat reliably holds centre poke for settling time, introduce +% violation (around after 200 ms) whereby if rat pokes out before the cp_time then its a violation. If many +% violations then continue training here till the performance in violation trial is below 20 percent for set trials. +% This stage continue till rat learns to poke for atleast CP = 1sec. + +%% Stage 5: Continue Centre Poke Training with violation and increasing CP Time, and introduce a fixed Sound between cp start and Go Cue +% In substage 3, which is CP > 1 sec, play a fixed sound between the poke start and Go Cue with the goal of conditioning the rat +% to expect a sound in between a Go Cue and poke start. This could help in later training stages by reducing Violations. +% In this stage the pre stim and stim time is fixed and only the delay between stim end and go cue increases. + +%% Stage 6: Continue Centre Poke Training with variable pre-stim time +% After reaching the max CP, vary the pre stim value and fixing the total CP. + +%% Stage 7: Continue Centre Poke Training with variable pre-stim time and variable delay time (both within a range). +% This variable time is introduced so that rat doesn't learn the expectancy +% time between each of the parameters + + +GetSoloFunctionArgs; + + +switch action + + case 'init' + + SoftPokeStayInterface(obj, 'add', 'soft_cp', x, y); + + case 'prepare_next_trial' + + %% Setup water + min_time= 2.5E-4; % This is less than the minumum time allowed for a state transition. + + left1led = bSettings('get', 'DIOLINES', 'left1led'); + center1led = bSettings('get', 'DIOLINES', 'center1led'); + right1led = bSettings('get', 'DIOLINES', 'right1led'); + left1water = bSettings('get', 'DIOLINES', 'left1water'); + right1water = bSettings('get', 'DIOLINES', 'right1water'); + + + %% Setup sounds + sone_sound_id = SoundManagerSection(obj, 'get_sound_id', 'SOneSound'); + sone_sound_duration = value(A1_time); % SoundManagerSection(obj, 'get_sound_duration', 'SOneSound'); + go_sound_id = SoundManagerSection(obj, 'get_sound_id', 'GoSound'); + go_cue_duration = value(time_go_cue); % SoundManagerSection(obj, 'get_sound_duration', 'GoSound'); + viol_sound_id = SoundManagerSection(obj, 'get_sound_id', 'ViolationSound'); + viol_snd_duration = SoundManagerSection(obj, 'get_sound_duration', 'ViolationSound'); + to_sound_id = SoundManagerSection(obj, 'get_sound_id', 'TimeoutSound'); + timeout_snd_duration = SoundManagerSection(obj, 'get_sound_duration', 'TimeoutSound'); + + %% Declare variables + % These will get moved to other functions as SoloParamHandles. + + WaterAmount=maxasymp + (minasymp./(1+(n_done_trials/inflp).^slp).^assym); + % WaterValvesSection(obj, 'set_water_amounts', WaterAmount, WaterAmount); + % [LeftWValveTime RightWValveTime] = WaterValvesSection(obj, 'get_water_times'); + WValveTimes = GetValveTimes(WaterAmount, [2 3]); + LeftWValveTime = WValveTimes(1); + RightWValveTime = WValveTimes(2); + [LeftWMult, RightWMult] = ParamsSection(obj, 'get_water_mult'); + LeftWValveTime=LeftWValveTime*LeftWMult; + RightWValveTime=RightWValveTime*RightWMult; + + side = ParamsSection(obj, 'get_current_side'); + + if side == 'l' + HitEvent = 'Lin'; + ErrorEvent = 'Rin'; + SideLight = left1led; + else + HitEvent = 'Rin'; + ErrorEvent = 'Lin'; + SideLight = right1led; + end + + + sma = StateMachineAssembler('full_trial_structure','use_happenings', 1); + + SoftPokeStayInterface(obj, 'set', 'soft_cp', 'Duration', CP_duration - SettlingIn_time,'Grace', legal_cbreak); % used in later stages, after stage 3 + + if side=='l' + sma = add_scheduled_wave(sma, 'name', 'reward_delivery', 'preamble', reward_delay, ... + 'sustain', LeftWValveTime, 'DOut', left1water); % % activating the left side water port + else + sma = add_scheduled_wave(sma, 'name', 'reward_delivery', 'preamble', reward_delay, ... + 'sustain', RightWValveTime, 'DOut', right1water); % activating the right side water port + end + + sma = add_scheduled_wave(sma, 'name', 'settling_period', 'preamble', SettlingIn_time); % intial fidgety period without violation + + sma = add_scheduled_wave(sma, 'name', 'stimA1', 'preamble', PreStim_time, ... + 'sustain', sone_sound_duration, 'sound_trig', sone_sound_id); % to play a sound before Go Cue + + sma = add_scheduled_wave(sma, 'name', 'Go_Cue', 'preamble', 0.001, ... + 'sustain', go_cue_duration, 'sound_trig', go_sound_id); % to play the Go Cue/Reward Sound + + sma = add_scheduled_wave(sma, 'name', 'reward_collection_dur', 'preamble', SideLed_duration + RewardCollection_duration); % time to collect the reward + + % For Training Stage 1 + + switch value(training_stage) + + case 1 % LEARNING THE REWARD SOUND ASSOCIATION -LEFT OR RIGHT LED ON -> POKE -> SOUND+REWARD GIVEN + % INFINITE TIME AND CHANCES TO SELF CORRECT + + sma = add_state(sma, 'name', 'side_led_wait_RewardCollection', 'self_timer', SideLed_duration + RewardCollection_duration, ... + 'output_actions', {'DOut', SideLight}, ... + 'input_to_statechange',{HitEvent,'hit_state';'Tup','side_led_wait_RewardCollection'; ErrorEvent,'second_hit_state'}); + + + sma = add_state(sma,'name','second_hit_state','self_timer',RewardCollection_duration,... + 'output_actions',{'DOut', SideLight},... + 'input_to_statechange',{'Tup','second_hit_state'; HitEvent,'hit_state'}); + + sma = add_state(sma,'name','hit_state','self_timer',0.01,... + 'output_actions', {'DOut', SideLight; 'SchedWaveTrig','reward_delivery + Go_Cue'},... + 'input_to_statechange',{'Tup','drink_state'}); + + sma = add_state(sma,'name','drink_state','self_timer',drink_time,... + 'input_to_statechange',{'Tup','preclean_up_state'}); + + sma = add_state(sma,'name','preclean_up_state','self_timer',0.5,... + 'input_to_statechange',{'Tup','check_next_trial_ready'}); + + % For Training Stage 2 + + case 2 %% STILL LEARNING THE REWARD SOUND ASSOCIATION -LEFT OR RIGHT LED ON -> POKE -> SOUND+REWARD BUT + % GIVEN LIMITED TIME TO SELF CORRECT OTHERWISE ITS A TIMEOUT AND A TIMEOUT SOUND IS PLAYED + + sma = add_state(sma, 'name', 'side_led_wait_RewardCollection', 'self_timer', SideLed_duration + RewardCollection_duration, ... + 'output_actions', {'DOut', SideLight; 'SchedWaveTrig', 'reward_collection_dur'}, ... + 'input_to_statechange',{HitEvent,'hit_state';'Tup','timeout_state'; ErrorEvent,'second_hit_state'}); + + sma = add_state(sma,'name','second_hit_state','self_timer',RewardCollection_duration,... + 'output_actions',{'DOut', SideLight},... + 'input_to_statechange',{'reward_collection_dur_In', 'timeout_state'; 'Tup','timeout_state'; HitEvent,'hit_state'}); + + sma = add_state(sma,'name','hit_state','self_timer',0.01,... + 'output_actions', {'DOut', SideLight; 'SchedWaveTrig','reward_delivery + Go_Cue'},... + 'input_to_statechange',{'Tup','drink_state'}); + + sma = add_state(sma,'name','drink_state','self_timer',drink_time,... + 'input_to_statechange',{'Tup','preclean_up_state'}); + + % For Timeout + + sma = add_state(sma,'name','timeout_state','self_timer',timeout_snd_duration,... + 'output_actions', {'SoundOut',to_sound_id; 'SchedWaveTrig', '-Go_Cue'},... + 'input_to_statechange',{'Tup','current_state+1'}); + sma = add_state(sma, 'self_timer', max(0.001, timeout_iti-timeout_snd_duration), ... + 'input_to_statechange',{'Tup','preclean_up_state'}); + + sma = add_state(sma,'name','preclean_up_state','self_timer',0.5,... + 'input_to_statechange',{'Tup','check_next_trial_ready'}); + + % Training Stage 3 + + case 3 % INTRODUCE CENTRE NOSE POKE AND PLAY REWARD SOUND AS GO CUE ONLY + % UNTIL CP_DUR <= SETTLING IN TIME (NO VIOLATIONS) + + sma = add_state(sma,'name','wait_for_cpoke','self_timer',cp_timeout, ... + 'output_actions', {'DOut', center1led}, ... + 'input_to_statechange', {'Cin','settling_in_state'; 'Tup','timeout_state'}); + + sma = add_state(sma,'name','settling_in_state','self_timer',CP_duration, ... + 'output_actions', {'DOut', center1led}, ... + 'input_to_statechange', {'Tup','side_led_wait_RewardCollection'}); + + sma = add_state(sma, 'name', 'side_led_wait_RewardCollection', 'self_timer', SideLed_duration + RewardCollection_duration, ... + 'output_actions', {'DOut', SideLight; 'SchedWaveTrig', 'reward_collection_dur + Go_Cue'}, ... + 'input_to_statechange',{HitEvent,'hit_state';'Tup','timeout_state'; ErrorEvent,'second_hit_state'}); + + sma = add_state(sma,'name','second_hit_state','self_timer',RewardCollection_duration,... + 'output_actions',{'DOut', SideLight},... + 'input_to_statechange',{'reward_collection_dur_In', 'timeout_state'; 'Tup','timeout_state'; HitEvent,'hit_state'}); + + sma = add_state(sma,'name','hit_state','self_timer',0.01,... + 'output_actions', {'DOut', SideLight; 'SchedWaveTrig','reward_delivery'},... + 'input_to_statechange',{'Tup','drink_state'}); + + sma = add_state(sma,'name','drink_state','self_timer',drink_time,... + 'input_to_statechange',{'Tup','preclean_up_state'}); + + % For Timeout + + sma = add_state(sma,'name','timeout_state','self_timer',timeout_snd_duration,... + 'output_actions', {'SoundOut',to_sound_id; 'SchedWaveTrig', '-Go_Cue'},... + 'input_to_statechange',{'Tup','current_state+1'}); + sma = add_state(sma, 'self_timer', max(0.001, timeout_iti-timeout_snd_duration), ... + 'input_to_statechange',{'Tup','preclean_up_state'}); + + sma = add_state(sma,'name','preclean_up_state','self_timer',0.5,... + 'input_to_statechange',{'Tup','check_next_trial_ready'}); + + + case {4,5,6,7} % STAGE 4 - LEARN TO NOSE POKE BEYOND SETTLING TIME WITH THE INTRODUCTION OF VIOLATION, THIS IS UNTIL CP = 1 SEC + % STAGE 5 ONWARDS - THE STIMULI IS INTRODUCED FROM THE STAGE 5 ONWARDS + + sma = add_state(sma,'name','wait_for_cpoke','self_timer',cp_timeout, ... + 'output_actions', {'DOut', center1led}, ... + 'input_to_statechange', {'Cin','settling_in_state';'Tup','timeout_state'}); + + % sma = add_state(sma,'name','settling_in_state','self_timer',SettlingIn_time, ... + % 'output_actions', {'DOut', center1led,'SchedWaveTrig', settling_period}, ... + % 'input_to_statechange', {'Cout','intermid_poke_out_state','Tup','soft_cp'}); + + % Before progressing check if its still centre poking or pokes within legal c_break other wise its a violation + + % This state is considering the poke is out at the end of settling time / at the start of this state + + % sma = add_state(sma,'name','intermid_poke_out_state','self_timer',SettlingIn_time, ... + % 'output_actions', {'DOut', center1led}, ... + % 'input_to_statechange', {'settling_period_In','legal_poke_start_state','Cin','intermid_poke_in_state','Tup','legal_poke_start_state',... + % 'Rin', 'violation_state','Rout', 'violation_state', 'Lin', 'violation_state','Lout', 'violation_state'}); + + % The other case is when the nose is still in then go directly to soft_cp + + % sma = add_state(sma,'name','intermid_poke_in_state','self_timer',SettlingIn_time, ... + % 'output_actions', {'DOut', center1led}, ... + % 'input_to_statechange', {'settling_period_In','soft_cp', 'Cout','intermid_poke_out_state','Tup','soft_cp',... + % 'Rin', 'violation_state','Rout', 'violation_state', 'Lin', 'violation_state','Lout', 'violation_state'}); + + % Before progressing check if its still centre poking or pokes within legal c_break other wise its a violation + + sma = add_state(sma,'name','settling_in_state','self_timer',SettlingIn_time, ... + 'output_actions', {'DOut', center1led; 'SchedWaveTrig', settling_period}, ... + 'input_to_statechange', {'Cout','current_state + 1';'Tup','soft_cp'}); + + % Instead using Intermediate State + + % This intermediate state is considering the poke is out before the end of settling time / at the start of this state + sma = add_state(sma,'self_timer',SettlingIn_time,'output_actions', {'DOut', center1led}, ... + 'input_to_statechange', {'settling_period_In','legal_poke_start_state';'Cin','current_state + 1';'Tup','legal_poke_start_state';... + 'Rin', 'violation_state';'Rout', 'violation_state'; 'Lin', 'violation_state';'Lout', 'violation_state'}); + + % The state jump to here when the nose is still in then go directly to soft_cp + sma = add_state(sma,'self_timer',SettlingIn_time, 'output_actions', {'DOut', center1led},... + 'input_to_statechange', {'settling_period_In','soft_cp'; 'Cout','current_state - 1';'Tup','soft_cp';... + 'Rin', 'violation_state'; 'Rout', 'violation_state'; 'Lin', 'violation_state'; 'Lout', 'violation_state'}); + + sma = add_state(sma,'name','legal_poke_start_state','self_timer',legal_cbreak, ... + 'output_actions', {'DOut', center1led}, ... + 'input_to_statechange', {'Cin','soft_cp'; 'Tup','violation_state';... + 'Rin', 'violation_state'; 'Rout', 'violation_state'; 'Lin', 'violation_state'; 'Lout', 'violation_state'}); + + if value(training_stage) == 4 + + % Soft poke with violation + sma = SoftPokeStayInterface(obj, 'add_sma_states', 'soft_cp', ... + {'success_exitstate_name', 'legal_poke_end_state'},{'abort_exitstate_name', 'violation_state'}, ...... + 'pokeid', 'C','ConstantDOut', center1led, 'triggertime', 0.0001); + + else % Soft poke with violation. The difference between this and previous stage is introduction of + % sound before the go cue + + sma = SoftPokeStayInterface(obj, 'add_sma_states', 'soft_cp', ... + {'success_exitstate_name', 'legal_poke_end_state'},{'abort_exitstate_name', 'violation_state'}, ...... + 'pokeid', 'C','ConstantDOut', center1led, 'triggertime', 0.0001,'InitialSchedWaveTrig', stimA1); + + end + + % Before giving the reward check if its still centre poking or pokes + % within legal c_break other wise its a violation + + sma = add_state(sma,'name','legal_poke_end_state','self_timer',legal_cbreak, ... + 'output_actions', {'DOut', center1led}, ... + 'input_to_statechange', {'Cin','side_led_wait_RewardCollection'; 'Tup','violation_state'}); + + sma = add_state(sma, 'name', 'side_led_wait_RewardCollection', 'self_timer', SideLed_duration + RewardCollection_duration, ... + 'output_actions', {'DOut', SideLight; 'SchedWaveTrig', 'reward_collection_dur + Go_Cue'}, ... + 'input_to_statechange',{HitEvent,'hit_state'; 'Tup','timeout_state'; ErrorEvent,'second_hit_state'}); + + sma = add_state(sma,'name','second_hit_state','self_timer',RewardCollection_duration,... + 'output_actions',{'DOut', SideLight},... + 'input_to_statechange',{'reward_collection_dur_In', 'timeout_state'; 'Tup','punish_state'; HitEvent,'hit_state'}); + + sma = add_state(sma,'name','hit_state','self_timer',0.01,... + 'output_actions', {'DOut', SideLight; 'SchedWaveTrig','reward_delivery'},... + 'input_to_statechange',{'Tup','drink_state'}); + + sma = add_state(sma,'name','drink_state','self_timer',drink_time,... + 'input_to_statechange',{'Tup','preclean_up_state'}); + + % For Timeout + + sma = add_state(sma,'name','timeout_state','self_timer',timeout_snd_duration,... + 'output_actions', {'SoundOut',to_sound_id; 'SchedWaveTrig', '-Go_Cue'},... + 'input_to_statechange',{'Tup','current_state+1'}); + sma = add_state(sma, 'self_timer', max(0.001, timeout_iti-timeout_snd_duration), ... + 'input_to_statechange',{'Tup','preclean_up_state'}); + + % For Violations + + sma = add_state(sma,'name','violation_state','self_timer',viol_snd_duration,... + 'output_actions',{'SoundOut',viol_sound_id; 'DOut', center1led; 'SchedWaveTrig', '-Go_Cue'},... + 'input_to_statechange', {'Tup', 'current_state+1'}); + sma = add_state(sma, 'self_timer', max(0.001, violation_iti-viol_snd_duration), ... + 'input_to_statechange',{'Tup','preclean_up_state'}); + + sma = add_state(sma,'name','preclean_up_state','self_timer',0.5,... + 'input_to_statechange',{'Tup','check_next_trial_ready'}); + + end + + + varargout{2} = {'check_next_trial_ready'}; + + varargout{1} = sma; + + % Not all 'prepare_next_trial_states' are defined in all training + % stages. So we send to dispatcher only those states that are + % defined. + state_names = get_labels(sma); state_names = state_names(:,1); + prepare_next_trial_states = {'side_led_wait_RewardCollection','hit_state','second_hit_state','drink_state', 'violation_state','timeout_state'}; + + dispatcher('send_assembler', sma, intersect(state_names, prepare_next_trial_states)); + + case 'get_state_colors' + varargout{1} = struct( ... + 'wait_for_cpoke', [0.68 1 0.63], ... + 'settling_in_state', [0.63 1 0.94], ... + 'legal_poke_start_state', [0.63 1 0.94]*0.8, ... + 'legal_poke_end_state', [1 0.79 0.63], ... + 'soft_cp', [0.3 0.9 0], ... + 'side_led_wait_RewardCollection', [0.53 0.78 1.00],... + 'hit_state', [0.77 0.60 0.48], ... + 'second_hit_state', [0.25 0.45 0.48], ... + 'drink_state', [0 1 0], ... + 'violation_state', [0.31 0.48 0.30], ... + 'timeout_state', 0.8*[0.31 0.48 0.30]); + + + % 'lefthit', [0 0.9 0.3], ... + % 'error_state', [1 0.54 0.54], ... + + + % 'go_cue_on', [0.63 1 0.94]*0.6, ... + % 'prerw_postcs', [0.25 0.45 0.48], ... + % 'lefthit', [0.53 0.78 1.00], ... + % 'lefthit_pasound', [0.53 0.78 1.00]*0.7, ... + % 'righthit', [0.52 1.0 0.60], ... + % 'righthit_pasound', [0.52 1.0 0.60]*0.7, ... + % 'warning', [0.3 0 0], ... + % 'danger', [0.5 0.05 0.05], ... + % 'hit', [0 1 0] + + + + + case 'reinit' + currfig = double(gcf); + + % Delete all SoloParamHandles who belong to this object and whose + % fullname starts with the name of this mfile: + delete_sphandle('owner', ['^@' class(obj) '$'], ... + 'fullname', ['^' mfilename]); + + + % Reinitialise at the original GUI position and figure: + feval(mfilename, obj, 'init'); + + % Restore the current figure: + figure(currfig); + + otherwise + warning('do not know how to do %s',action); +end + +end + + + diff --git a/Protocols/@Arpit_CentrePokeTraining/Arpit_CentrePoke_TrainingStages.m b/Protocols/@Arpit_CentrePokeTraining/Arpit_CentrePoke_TrainingStages.m new file mode 100644 index 00000000..dc39cf61 --- /dev/null +++ b/Protocols/@Arpit_CentrePokeTraining/Arpit_CentrePoke_TrainingStages.m @@ -0,0 +1,141 @@ +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% +% ------------ STAGE SEPARATOR ------- (do not edit this line) +% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% --- STAGE NAME: --- (do not edit this line) +Growing nose in center time + +% --- VAR NAMES: --- (do not edit this line) +% Maximum duration of center poke (including Go cue), in secs: +max_total_cp 6.2 forceinit=1 +% Standard Go cue duration +target_go_cue_duration 0.2 +% CP_duration reached at the end of the last session +last_session_total_cp 0 +% Fractional increment in center poke duration every time there is a non-cp-violation trial: +cp_fraction 0.001 +% Minimum increment (in secs) in center poke duration every time there is a non-cp-violation trial: +cp_minimum_increment 0.001 + + +% Initial startup trials over which to gradually grow cp duration +% Only do the initial startup stuff if cp_duration is longer than: +cp_duration_threshold_for_initial_trials 1.5 +% Number of initial trials over which to gradually grow cp duration: +n_initial_trials 10 +% Starting total center poke duration: +starting_total_cp 0.5 + + +% Minimum CP_duration at which a settling_in legal_cbreak is different to +% the regular legal_cbreak: +start_settling_in_at 1000 +% Once we've reached CP_duration > start_settling_in_at, the parameters of +% the settling in: +settling_in_time 0 +settling_in_legal_cbreak 0.05 + + + + +% --- TRAINING STRING: --- (do not edit this line) + +% Check to see whether we're doing the initial startup stuff. Assumes that +% we're NOT changing the Go cue duration. +if SideSection_Total_CP_duration+0.0001 < last_session_total_cp & ... + last_session_total_cp > cp_duration_threshold_for_initial_trials, + % Check to see whether we're done with initial stuff within numerical + % rounding error: + if abs(last_session_total_cp - SideSection_Total_CP_duration) < 0.0001, + SideSection_CP_duration.value = last_session_total_cp - SideSection_time_go_cue; + elseif ~violation_history(end), + increment = ... + (last_session_total_cp - starting_total_cp)/(n_initial_trials - 1); + SideSection_CP_duration.value = SideSection_CP_duration + increment; + end; +elseif ~violation_history(end) && SideSection_Total_CP_duration < max_total_cp, + % We're in regular increasing territory + increment = SideSection_Total_CP_duration*cp_fraction; + if increment < cp_minimum_increment, + increment = value(cp_minimum_increment); + end; + % If we're growing the CP duration, grow the Go cue duration first + % until it reaches its target; after that, grow CP_duration, the + % pre-Go cue time. + if SideSection_time_go_cue < target_go_cue_duration, + SideSection_time_go_cue.value = SideSection_time_go_cue + increment; + else + SideSection_CP_duration.value = SideSection_CP_duration + increment; + end; +end; +% make sure the total reflects all the changes: +callback(SideSection_CP_duration); + +% Double-check that we don't go over the desired max value: +if SideSection_Total_CP_duration > max_total_cp, + SideSection_CP_duration.value = max_total_cp - SideSection_time_go_cue; + % once again, make sure the total reflects any the changes: + callback(SideSection_CP_duration); +end; + +% Settling in code: +if SideSection_CP_duration >= start_settling_in_at + SideSection_SettlingIn_time.value = value(settling_in_time); + SideSection_settling_legal_cbreak.value = value(settling_in_legal_cbreak); +else + SideSection_SettlingIn_time.value = 0; + SideSection_settling_legal_cbreak.value = value(SideSection_legal_cbreak); +end; + + + + +% --- END-OF-DAY LOGIC: -- (do not edit this line) + +% Store the value of the total cp duration reached: +last_session_total_cp.value = value(SideSection_Total_CP_duration); + +% Check whether we're going to do the initial startup trials on the next +% day: +if SideSection_Total_CP_duration > cp_duration_threshold_for_initial_trials, + % Yup, doing initial startup. Set CP_duration to the necessary duration: + SideSection_CP_duration.value = starting_total_cp - SideSection_time_go_cue; + % Error check, make sure we don't set it to something nonsensical: + if SideSection_CP_duration < 0.001, + SideSection_CP_duration = 0.001; + end; + % Callback to make sure the calculation of Total_CP_duration is made. + callback(SideSection_CP_duration); +end; + + +% --- COMPLETION STRING: --- (do not edit this line) +1 + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% +% ------------ STAGE SEPARATOR ------- (do not edit this line) +% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% --- STAGE NAME: --- (do not edit this line) +Random training name + +% --- VAR NAMES: --- (do not edit this line) +raw_event = 1 +we = 45 +sam = 5 + +% --- TRAINING STRING: --- (do not edit this line) +if a > 5 +disp(a) +end + +% --- END-OF-DAY LOGIC: -- (do not edit this line) + + +% --- COMPLETION STRING: --- (do not edit this line) +1 + + diff --git a/Protocols/@Arpit_CentrePokeTraining/ParamsSection.m b/Protocols/@Arpit_CentrePokeTraining/ParamsSection.m new file mode 100644 index 00000000..7a57fe8a --- /dev/null +++ b/Protocols/@Arpit_CentrePokeTraining/ParamsSection.m @@ -0,0 +1,469 @@ +function [x, y] = ParamsSection(obj, action, x,y) + +GetSoloFunctionArgs(obj); + +switch action + + % ------------------------------------------------------------------ + % INIT + % ------------------------------------------------------------------ + + case 'init' + + SoloParamHandle(obj, 'my_gui_info', 'value', [x y double(gcf)], 'saveable', 0); + y0 = y; + + [x, y] = AntibiasSectionAthena(obj, 'init', x, y); + + + ToggleParam(obj, 'antibias_LRprob', 0, x,y,... + 'OnString', 'AB_Prob ON',... + 'OffString', 'AB_Prob OFF',... + 'TooltipString', sprintf(['If on (Yellow) then it enables the AntiBias algorithm\n'... + 'based on changing the probablity of Left vs Right'])); + + next_row(y); + NumeditParam(obj, 'LeftProb', 0.5, x, y); next_row(y); + set_callback(LeftProb, {mfilename, 'new_leftprob'}); + MenuParam(obj, 'MaxSame', {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, Inf}, Inf, x, y, ... + 'TooltipString', sprintf(['\nMaximum number of consecutive trials where correct\n' ... + 'response is on the same side. Overrides antibias. Thus, for\n' ... + 'example, if MaxSame=5 and there have been 5 Left trials, the\n' ... + 'next trial is guaranteed to be Right'])); next_row(y); + + DispParam(obj, 'ThisTrial', 'LEFT', x, y); next_row(y); + SoloParamHandle(obj, 'previous_sides', 'value', []); + DeclareGlobals(obj, 'ro_args', 'previous_sides'); + SubheaderParam(obj, 'title', 'Sides Section', x, y); + next_row(y, 1.5); + next_column(x); y = 5; + % Reward Collection + NumeditParam(obj, 'RewardCollection_duration', 10, x,y,'label','RewardCollection_duration','TooltipString','Wait until rat collects the reward else a timeout'); + next_row(y); + NumeditParam(obj, 'SideLed_duration', 0.5, x,y,'label','Side LED duration','TooltipString','Duration of SideLed'); + % Centre Poke + next_row(y); + NumeditParam(obj, 'legal_cbreak', 0.1, x,y, 'position', [x, y, 175 20], 'TooltipString','Time in sec for which it is ok to be outside the center port before a violation occurs.'); + next_row(y); + NumeditParam(obj, 'SettlingIn_time', 0.2, x,y, 'position', [x, y, 175 20], 'TooltipString','Initial settling period during which there is no violation'); + next_row(y); + NumeditParam(obj, 'A1_time_Min', 0.4, x,y,'label','Min time for AUD1','TooltipString','Min value to select the Duration of fixed stimulus'); + set_callback(A1_time_Min, {mfilename, 'new_CP_duration'}); + next_row(y); + NumeditParam(obj, 'A1_time', 0.4, x,y,'label','AUD1 on Time','TooltipString','Actual Duration of fixed stimulus'); + next_row(y); + set_callback(A1_time, {mfilename, 'new_CP_duration'}); + next_row(y); + NumeditParam(obj, 'A1_time_Max', 0.4, x,y,'label','Max time for AUD1','TooltipString','Max value to select the Duration of fixed stimulus'); + set_callback(A1_time_Max, {mfilename, 'new_CP_duration'}); + next_row(y); + NumeditParam(obj, 'PreStim_time_Min', 0.20, x,y,'label','Min time for Pre-Stim','TooltipString','Min Time in NIC before starting the stimulus'); + set_callback(PreStim_time_Min, {mfilename, 'new_CP_duration'}); + next_row(y); + NumeditParam(obj, 'PreStim_time', 0.20, x,y,'label','Pre-Stim NIC time','TooltipString','Actual Time in NIC before starting the stimulus'); + set_callback(PreStim_time, {mfilename, 'new_CP_duration'}); + next_row(y); + NumeditParam(obj, 'PreStim_time_Max', 0.40, x,y,'label','Max time for Pre-Stim','TooltipString','Max Time in NIC before starting the stimulus'); + set_callback(PreStim_time_Max, {mfilename, 'new_CP_duration'}); + next_row(y); + NumeditParam(obj, 'time_bet_aud1_gocue_Min', 0.2, x,y,'label','Min A1-GoCue time','TooltipString','Min time between the end of the stimulus and the go cue '); + set_callback(time_bet_aud1_gocue_Min, {mfilename, 'new_CP_duration'}); + next_row(y); + NumeditParam(obj, 'time_bet_aud1_gocue', 0.2, x,y,'label','A1-GoCue time','TooltipString','Actual time between the end of the stimulus and the go cue '); + next_row(y); + set_callback(time_bet_aud1_gocue, {mfilename, 'new_CP_duration'}); + next_row(y); + NumeditParam(obj, 'time_bet_aud1_gocue_Max', 2, x,y,'label','Max A1-GoCue time','TooltipString','Max time between the end of the stimulus and the go cue '); + set_callback(time_bet_aud1_gocue_Max, {mfilename, 'new_CP_duration'}); + next_row(y); + DispParam(obj, 'init_CP_duration', 0.01, x,y,'label','init_CP duration','TooltipString','Duration of Nose in Central Poke before Go cue starts (see Total_CP_duration)'); + next_row(y); + DispParam(obj, 'CP_duration', PreStim_time+A1_time+time_bet_aud1_gocue, x,y,'label','CP duration','TooltipString','Duration of Nose in Central Poke before Go cue starts (see Total_CP_duration)'); + set_callback(CP_duration, {mfilename, 'new_CP_duration'}); + next_row(y); + NumeditParam(obj, 'time_go_cue' ,0.1, x,y,'label','Go Cue Duration','TooltipString','duration of go cue (see Total_CP_duration)'); + set_callback(time_go_cue, {mfilename, 'new_time_go_cue'}); + next_row(y); + DispParam(obj, 'Total_CP_duration', CP_duration+time_go_cue, x, y, 'TooltipString', 'Total expected(rat can poke out anytime after Go cue onset) nose in center port time, in secs. Sum of CP_duration and Go Cue duration'); %#ok<*NODEF> + next_row(y); + ToggleParam(obj, 'warmup_on', 1, x,y,... + 'OnString', 'Warmup ON',... + 'OffString', 'Warmup OFF',... + 'TooltipString', sprintf(['If on (Yellow) then it applies the initial warming up phase, during which the\n',... + 'CP_duration starts small and gradually grows to last_session_max_cp_duration'])); + next_row(y); + + NumeditParam(obj, 'reward_delay', 0.01, x,y,'label','Reward Delay','TooltipString','Delay between side poke and reward delivery'); + next_row(y); + NumeditParam(obj, 'drink_time', 1, x,y,'label','Drink Time','TooltipString','waits to finish water delivery'); + next_row(y); + NumeditParam(obj, 'timeout_iti', 5, x,y,'label','Error Timeout','TooltipString','ITI on timeout trials'); + next_row(y); + NumeditParam(obj, 'violation_iti', 1, x,y,'label','Violation Timeout','TooltipString','Center poke violation duration'); + + next_row(y); + ToggleParam(obj, 'random_PreStim_time', 0, x,y,... + 'OnString', 'random PreStim_time ON',... + 'OffString', 'random PreStim_time OFF',... + 'TooltipString', sprintf('If on (black) then it enables the random time between the user given range')); + set_callback(random_PreStim_time, {mfilename, 'new_CP_duration'}); + next_row(y); + ToggleParam(obj, 'random_A1_time', 0, x,y,... + 'OnString', 'random A1_time ON',... + 'OffString', 'random A1_time OFF',... + 'TooltipString', sprintf('If on (black) then it enables the random sampling of A1_time')); + set_callback(random_A1_time, {mfilename, 'new_CP_duration'}); + next_row(y); + ToggleParam(obj, 'random_prego_time', 0, x,y,... + 'OnString', 'random prego_time ON',... + 'OffString', 'random prego_time OFF',... + 'TooltipString', sprintf('If on (black) then it enables the random sampling of time between the end of the stimulus and the go cue')); + set_callback(random_prego_time, {mfilename, 'new_CP_duration'}); + + next_column(x); + y=5; + NumeditParam(obj,'trials_in_stage',1,x,y,'label','Trial Counter'); + next_row(y); + NumeditParam(obj,'training_stage',1,x,y,'label','Training Stage'); + next_row(y); + ToggleParam(obj,'use_training',0,x,y,'OnString','Using Autotrain','OffString','Manual Settings'); + + next_row(y); + NumeditParam(obj, 'ntrial_correct_bias', 0, x, y, ... + 'TooltipString', 'antibias starts from trial=ntrial_correct_bias'); + next_row(y); + NumeditParam(obj, 'right_left_diff', .12, x, y, ... + 'TooltipString', 'antibias applies if difference between right and left sides is bigger than this number'); + next_row(y); + NumeditParam(obj, 'max_wtr_mult', 4, x, y, ... + 'TooltipString', 'wtr_mult will be min(max_wtr_mult,right_hit/left_hit)'); + next_row(y); + NumeditParam(obj, 'left_wtr_mult', 1, x, y, ... + 'TooltipString', 'all left reward times are multiplied by this number'); + next_row(y); + NumeditParam(obj, 'right_wtr_mult', 1, x, y, ... + 'TooltipString', 'all right reward times are multiplied by this number'); + next_row(y); + ToggleParam(obj, 'antibias_wtr_mult', 0, x,y,... + 'OnString', 'AB ON',... + 'OffString', 'AB OFF',... + 'TooltipString', sprintf(['If on (black) then it disables the wtr_mult entries\n'... + 'and uses hitfrac to adjust the water times'])); + + next_row(y); + SoloFunctionAddVars('Arpit_CentrePokeTrainingSMA', 'ro_args', ... + {'CP_duration';'SideLed_duration'; ... + 'RewardCollection_duration';'training_stage'; ... + 'legal_cbreak' ; 'SettlingIn_time'; 'time_go_cue'; ... + 'A1_time';'time_bet_aud1_gocue' ; 'PreStim_time'; + 'drink_time';'reward_delay';'reward_duration';'left_wtr_mult';... + 'right_wtr_mult';'antibias_wtr_mult';... + 'secondhit_delay';'timeout_iti';'violation_iti'}); + + % SoloFunctionAddVars('StimulatorSection', 'ro_args', ... + % {'A1_time';'time_bet_aud1_gocue';'time_go_cue'; ... + % 'PreStim_time';'CP_duration';'Total_CP_duration'}); + + % History of hit/miss: + SoloParamHandle(obj, 'deltaf_history', 'value', []); + + SoloFunctionAddVars('OverallPerformanceSection', 'ro_args', ... + {'training_stage'}); + + + SoloParamHandle(obj, 'previous_parameters', 'value', []); + + case 'new_leftprob' + AntibiasSectionAthena(obj, 'update_biashitfrac', value(LeftProb)); + + + case 'new_CP_duration' + + if random_PreStim_time == 1 && (PreStim_time.value < value(PreStim_time_Min) || PreStim_time.value > value(PreStim_time_Max)) + PreStim_time.value = value(PreStim_time_Min); + else + PreStim_time.value = value(PreStim_time); + end + + if random_A1_time == 1 && (A1_time.value < value(A1_time_Min) || A1_time.value > value(A1_time_Max)) + A1_time.value = value(A1_time_Min); + else + A1_time.value = value(A1_time); + end + + if random_time_bet_aud1_gocue == 1 && (time_bet_aud1_gocue.value < value(time_bet_aud1_gocue_Min) || time_bet_aud1_gocue.value > value(time_bet_aud1_gocue_Max)) + time_bet_aud1_gocue.value = value(time_bet_aud1_gocue_Min); + else + time_bet_aud1_gocue.value = value(time_bet_aud1_gocue); + end + + CP_duration.value= SettlingIn_time + PreStim_time + A1_time + time_bet_aud1_gocue; + Total_CP_duration.value = CP_duration + time_go_cue; %#ok<*NASGU> + + case 'new_time_go_cue' + Total_CP_duration.value = CP_duration + time_go_cue; + SoundInterface(obj, 'set', 'GoSound', 'Dur1', value(time_go_cue)); + + + case 'prepare_next_trial' + + delay_time.value=0; + trials_in_stage.value=0; + reward_delay.value=0.01; + left_prob.value=0.5; + right_prob.value=0.5; + + switch value(training_stage) + + case {1,2} %% learning the reward sound association -left or right led on -> poke -> sound+reward + + time_go_cue.value=0.200; + disable(settling_time); + disable(PreStim_time); + disable(A1_time); + disable(time_bet_aud1_gocue); + + + case {3,4} % Centre poke without the A1_Stim but has violation in 4 + enable(settling_time); + disable(PreStim_time); + disable(A1_time); + disable(time_bet_aud1_gocue); + + if n_done_trials <1 && warmup_on ==1 + CP_duration.value=value(init_CP_duration); + else + CP_duration.value=CP_duration; + end + Total_CP_duration.value = CP_duration + time_go_cue; %#ok<*NASGU> + + case {5,6,7} % + + enable(settling_time); + enable(PreStim_time); + enable(A1_time); + enable(time_bet_aud1_gocue); + + if random_prego_time == 1 + time_bet_aud1_gocue = randi([time_bet_aud1_gocue_Min, time_bet_aud1_gocue_Max],1,1); + end + + if random_A1_time == 1 + A1_time = randi([A1_time_Min, A1_time_Max],1,1); + end + + if random_PreStim_time == 1 + PreStim_time = randi([PreStim_time_Min, PreStim_time_Max],1,1); + end + + if n_done_trials <1 && warmup_on ==1 + CP_duration.value=value(init_CP_duration); + else + CP_duration.value=settling_time + A1_time + PreStim_time + time_bet_aud1_gocue; + end + Total_CP_duration.value = CP_duration + time_go_cue; %#ok<*NASGU> + + + end + + + %% update hit_history, previous_sides, etc + was_viol=false; + was_hit=false; + was_timeout=false; + if n_done_trials>0 + if ~isempty(parsed_events) + if isfield(parsed_events,'states') + if isfield(parsed_events.states,'timeout_state') + was_timeout=rows(parsed_events.states.timeout_state)>0; + end + if isfield(parsed_events.states,'violation_state') + was_viol=rows(parsed_events.states.violation_state)>0; + end + end + + end + + violation_history.value=[violation_history(:); was_viol]; + timeout_history.value=[timeout_history(:); was_timeout]; + + ParamsSection(obj,'update_side_history'); + + if ~was_viol && ~was_timeout + %was_hit=rows(parsed_events.states.hit_state)>0; + was_hit=rows(parsed_events.states.second_hit_state)==0; + hit_history.value=[hit_history(:); was_hit]; + + else + % There was a violation or timeout + hit_history.value=[hit_history(:); nan]; + end + + % Now calculate the deltaF and sides - this maybe interesting + % even in a violation or timeout case. + + fn=fieldnames(parsed_events.states); + led_states=find(strncmp('led',fn,3)); + deltaF=0; + n_l=0; + n_r=0; + for lx=1:numel(led_states) + lind=led_states(lx); + if rows(parsed_events.states.(fn{lind}))>0 + if fn{lind}(end)=='l' + deltaF=deltaF-1; + n_l=n_l+1; + elseif fn{lind}(end)=='r' + deltaF=deltaF+1; + n_r=n_r+1; + elseif fn{lind}(end)=='b' + n_l=n_l+1; + n_r=n_r+1; + + end + end + + end + + % if deltaF>0 then a right poke is a hit + % if deltaF<0 then a left poke is a hit + + deltaf_history.value=[deltaf_history(:); deltaF]; + + end + + if antibias_LRprob ==1 + if n_done_trials >ntrial_correct_bias && ~was_viol && ~was_timeout + nonan_hit_history=value(hit_history); + nonan_hit_history(isnan(nonan_hit_history))=[]; + nonan_previous_sides=value(previous_sides); + nan_history=value(hit_history); + nonan_previous_sides(isnan(nan_history))=[]; + AntibiasSectionAthena(obj, 'update', value(LeftProb), nonan_hit_history(:)',nonan_previous_sides(:)); % <~> Transposed hit history so that it is the expected column vector. (Antibias errors out otherwise.) 2007.09.05 01:39 + end + + + if ~isinf(MaxSame) && length(previous_sides) > MaxSame && ... + all(previous_sides(n_done_trials-MaxSame+1:n_done_trials) == previous_sides(n_done_trials)) %#ok + if previous_sides(end)=='l' + ThisTrial.value = 'RIGHT'; + else + ThisTrial.value = 'LEFT'; + end + else + choiceprobs = AntibiasSectionAthena(obj, 'get_posterior_probs'); + if rand(1) <= choiceprobs(1) + ThisTrial.value = 'LEFT'; + else + ThisTrial.value = 'RIGHT'; + end + end + + else + if (rand(1)<=LeftProb) + ThisTrial.value='LEFT'; + + else + ThisTrial.value='RIGHT'; + end + + end + + + + +% %% Do the anti-bias with changing reward delivery +% % reset anti-bias +% left_wtr_mult.value=1; +% right_wtr_mult.value=1; +% if n_done_trials>ntrial_correct_bias && antibias_wtr_mult==1 +% hh=hit_history(n_done_trials-ntrial_correct_bias:n_done_trials); +% ps=previous_sides(n_done_trials-ntrial_correct_bias:n_done_trials); +% +% right_hit=nanmean(hh(ps=='r')); +% left_hit=nanmean(hh(ps=='l')); +% +% if abs(right_hit-left_hit) + + case 'get_left_prob' + x = value(LeftProb); + + case 'get_cp_history' + x = cell2mat(get_history(CP_duration)); + + case 'get_stimdur_history' + x = cell2mat(get_history(A1_time)); + + case 'update_side_history' + if strcmp(ThisTrial, 'LEFT') + ps=value(previous_sides); + ps(n_done_trials)='l'; + previous_sides.value=ps; + + else + ps=value(previous_sides); + ps(n_done_trials)='r'; + previous_sides.value=ps; + end + + case 'get_current_side' + if strcmp(ThisTrial, 'LEFT') + x = 'l'; %#ok + else + x = 'r'; + end + + + case 'close' + % Delete all SoloParamHandles who belong to this object and whose + % fullname starts with the name of this mfile: + delete_sphandle('owner', ['^@' class(obj) '$'], ... + 'fullname', ['^' mfilename]); + + case 'reinit' + currfig = double(gcf); + + % Get the original GUI position and figure: + x = my_gui_info(1); y = my_gui_info(2); figure(my_gui_info(3)); + + % Delete all SoloParamHandles who belong to this object and whose + % fullname starts with the name of this mfile: + delete_sphandle('owner', ['^@' class(obj) '$'], ... + 'fullname', ['^' mfilename]); + + % Reinitialise at the original GUI position and figure: + [x, y] = feval(mfilename, obj, 'init', x, y); + + % Restore the current figure: + figure(currfig); + +end + + From 16eb6ab545b391d1f78f9cae9666505394b596e5 Mon Sep 17 00:00:00 2001 From: ArpitAgarwal Date: Tue, 18 Mar 2025 16:24:26 +0000 Subject: [PATCH 020/164] adding new files to Arpit_CentrePokeTraining protocol: 1 step for cenre poke --- .../AntibiasSectionAthena.m | 315 +++++++++++++++++ .../@Arpit_CentrePokeTraining/LOGplotPairs.m | 71 ++++ .../OverallPerformanceSection.m | 127 +++++++ .../@Arpit_CentrePokeTraining/PlayStimuli.m | 87 +++++ .../ProduceNoiseStimuli.m | 33 ++ .../PunishmentSection.m | 133 +++++++ .../RewardsSection.m | 237 +++++++++++++ .../@Arpit_CentrePokeTraining/SoundSection.m | 63 ++++ .../StimulatorSection.m | 325 ++++++++++++++++++ .../@Arpit_CentrePokeTraining/noiselib.m | 51 +++ .../@Arpit_CentrePokeTraining/private/filt.m | 60 ++++ .../private/noisestim.m | 47 +++ .../private/singlenoise.m | 32 ++ .../@Arpit_CentrePokeTraining/state_colors.m | 16 + .../@Arpit_CentrePokeTraining/wave_colors.m | 11 + 15 files changed, 1608 insertions(+) create mode 100644 Protocols/@Arpit_CentrePokeTraining/AntibiasSectionAthena.m create mode 100644 Protocols/@Arpit_CentrePokeTraining/LOGplotPairs.m create mode 100644 Protocols/@Arpit_CentrePokeTraining/OverallPerformanceSection.m create mode 100644 Protocols/@Arpit_CentrePokeTraining/PlayStimuli.m create mode 100644 Protocols/@Arpit_CentrePokeTraining/ProduceNoiseStimuli.m create mode 100644 Protocols/@Arpit_CentrePokeTraining/PunishmentSection.m create mode 100644 Protocols/@Arpit_CentrePokeTraining/RewardsSection.m create mode 100644 Protocols/@Arpit_CentrePokeTraining/SoundSection.m create mode 100644 Protocols/@Arpit_CentrePokeTraining/StimulatorSection.m create mode 100644 Protocols/@Arpit_CentrePokeTraining/noiselib.m create mode 100644 Protocols/@Arpit_CentrePokeTraining/private/filt.m create mode 100644 Protocols/@Arpit_CentrePokeTraining/private/noisestim.m create mode 100644 Protocols/@Arpit_CentrePokeTraining/private/singlenoise.m create mode 100644 Protocols/@Arpit_CentrePokeTraining/state_colors.m create mode 100644 Protocols/@Arpit_CentrePokeTraining/wave_colors.m diff --git a/Protocols/@Arpit_CentrePokeTraining/AntibiasSectionAthena.m b/Protocols/@Arpit_CentrePokeTraining/AntibiasSectionAthena.m new file mode 100644 index 00000000..cb34d14a --- /dev/null +++ b/Protocols/@Arpit_CentrePokeTraining/AntibiasSectionAthena.m @@ -0,0 +1,315 @@ +% [x, y] = AntibiasSection(obj, action, [arg1], [arg2], [arg3]) +% +% Section that calculates biases and calculates probability of choosing a stimulus +% given the previous history. +% +% Antibias assumes that trials are of two classes, Left desired answer +% and Right desired answer, and that their outcome is either Correct or +% Incorrect. Given the history of previous trial classes, and the history +% of previous corrects/incorrects, Antibias makes a local estimate of +% fraction correct for each class, combines that with a prior probability +% of making the next trial Left, and produces a recommended probability +% for choosing the next trial as Left. Antibias will tend to make the +% class with the smaller frac correct the one with the higher probability. +% The strength of that tendency is quantified by a parameter, beta. +% (See probabilistic_trial_selector.m for details on the math of how the +% tendency is generated.) +% +% Local estimates of fraction correct are computed using an exponential +% kernel, most recent trial the most strongly weighted. The tau of this +% kernel is a GUI parameter. Two different estimates are computed: one for +% use in computing Left probability and Right probability; and a second +% simply for GUI display purposes. The two estimates can have different +% taus for their kernels. +% +% GUI DISPLAY: When initialized, this plugin will put up two panels and a +% title. In each panel, there is a slider that controls the tau of the +% recent-trials exponential kernel. One panel will display the percent +% corrects for Right and Left, as computed with its kernel. The second panel +% will display the a posteriori probabilities of making the next trial a +% "Left" trial or making the next trial a "Right" trial. This second panel +% has its own tau slider, and it also has a GUI parameter, beta, that +% controls how strongly the history matters. If beta=0, history doesn't +% matter, and the a priori LeftProbability dominates. If beta=Inf, then +% history matters above all: the next trial will be of the type with lowest +% fraction correct, for sure. +% +% See the bottom of this help file for examples of usage. +% +% arg1, arg2, arg3 are optional and their meaning depends on action (see +% below). +% +% PARAMETERS: +% ----------- +% +% obj Default object argument. +% +% action One of: +% +% 'init' To initialise the plugin and set up the GUI for it. This +% action requires two more arguments: The bottom left +% x y position (in units of pixels) of where to start placing +% the GUI elements of this plugin. +% +% 'update' This call will recompute both local estimates of +% fraction correct, and will recompute the recommended +% LeftProb, p(Left). This action requires three more arguments: +% HitHist, LProb, a scalar b/w 0 and 1; HitHist, a vector of 1s +% SidesHist and 0s and of length n_done_trials where 1 represents +% correct and 0 represents incorrect, first element +% corresponds to first trial; and SidesHist, a vector of +% 'l's and 'r's and of length n_done_trials where 'l' +% represents 'left', 'r' represents 'right' first element +% corresponds to first trial. +% +% 'get_posterior_probs' Returns a vector with two components, +% [p(Left) p(Right)]. +% +% +% 'update_biashitfrac' This call will recompute the local estimate of fraction +% Left correct and fraction Right correct used for +% LeftProb, antibiasing, and will also recompute the recommended +% HitHist, Left probability. This action +% SidesHist, requires three more arguments: LProb, a scalar b/w 0 +% and 1; HitHist, a vector of 1s and 0s and of length +% n_done_trials where 1 represents correct and 0 +% represents incorrect, first element corresponds to +% first trial; and SidesHist, a vector of 'l's and 'r's +% and of length n_done_trials where 'l' represents +% 'left', 'r' represents 'right' first element +% corresponds to first trial. +% +% 'update_hitfrac' This call is not related to computing the posterior +% Left probability, but will recompute only the local estimate +% HitHist, of fraction correct that is not used for antibiasing. +% SidesHist This action requires two more arguments: HitHist, a +% vector of 1s and 0s and of length n_done_trials where 1 +% represents correct and 0 represents incorrect, first +% element corresponds to first trial; and SidesHist, a vector of +% 'l's and 'r's and of length n_done_trials where 'l' +% represents 'left', 'r' represents 'right' first element +% corresponds to first trial. +% +% 'get' Needs one extra parameter, either 'Beta' or +% 'antibias_tau', and returns the corresponding scalar. +% +% 'reinit' Delete all of this section's GUIs and data, +% and reinit, at the same position on the same +% figure as the original section GUI was placed. +% +% +% x, y Relevant to action = 'init'; they indicate the initial +% position to place the GUI at, in the current figure window +% +% RETURNS: +% -------- +% +% if action == 'init' : +% +% [x1, y1, w, h] When action == 'init', Antibias will put up GUIs and take +% up a certain amount of space of the figure that was current when +% AntiBiasSection(obj, 'init', x, y) was called. On return, [x1 y1] +% will be the top left corner of the space used; [x y] (as passed +% to Antibias in the init call) will be the bottom left corner; +% [x+w y1] will be the top right; and [x+w y] will be the bottom +% right. h = y1-y. All these are in units of pixels. +% +% +% if action == 'get_posterior_probs' : +% +% [L R] When action == 'get_posterior_probs', a two-component vector is +% returned, with p(Left) and p(Right). If beta=0, then p(Left) +% will be the same as the last LeftProb that was passed in. +% +% +% USAGE: +% ------ +% +% To use this plugin, the typical calls would be: +% +% 'init' : On initializing your protocol, call +% AntibiasSection(obj, 'init', x, y); +% +% 'update' : After a trial is completed, call +% AntibiasSection(obj, 'update', LeftProb, HitHist, SidesHist) +% +% 'get_posterior_probs' : After a trial is completed, and when you are +% deciding what kind of trial to make the next trial, get the plugins +% opinion on whether the next trial should be Left or Right by calling +% AntibiasSection(obj, 'get_posterior_probs') +% +% See PARAMETERS section above for the documentation of each of these calls. +% + + +function [x, y, w, h] = AntibiasSection(obj, action, varargin) + +GetSoloFunctionArgs(obj); + +switch action + + case 'init', % ------------ CASE INIT ---------------- + x = varargin{1}; y = varargin{2}; y0 = y; + % Save the figure and the position in the figure where we are + % going to start adding GUI elements: + SoloParamHandle(obj, 'my_gui_info', 'value', [x y double(gcf)]); + + LogsliderParam(obj, 'HitFracTau', 30, 10, 400, x, y, 'label', 'hits frac tau', ... + 'TooltipString', ... + sprintf(['\nnumber of trials back over which to compute fraction of correct trials.\n' ... + 'This is just for displaying info-- for the bias calculation, see BiasTau above'])); + set_callback(HitFracTau, {mfilename, 'update_hitfrac'}); + next_row(y); + DispParam(obj, 'LtHitFrac', 0, x, y); next_row(y); + DispParam(obj, 'RtHitFrac', 0, x, y); next_row(y); + DispParam(obj, 'HitFrac', 0, x, y); next_row(y); + + next_row(y, 0.5); + LogsliderParam(obj, 'BiasTau', 30, 10, 400, x, y, 'label', 'antibias tau', ... + 'TooltipString', ... + sprintf(['\nnumber of trials back over\nwhich to compute fraction of correct trials\n' ... + 'for the antibias function.'])); next_row(y); + NumeditParam(obj, 'Beta', 0, x, y, ... + 'TooltipString', ... + sprintf(['When this is 0, past performance doesn''t affect choice\n' ... + 'of next trial. When this is large, the next trial is ' ... + 'almost guaranteed\nto be the one with smallest %% correct'])); next_row(y); + set_callback({BiasTau, Beta}, {mfilename, 'update_biashitfrac'}); + DispParam(obj, 'LtProb', 0, x, y); next_row(y); + DispParam(obj, 'RtProb', 0, x, y); next_row(y); + SoloParamHandle(obj, 'BiasLtHitFrac', 'value', 0); + SoloParamHandle(obj, 'BiasRtHitFrac', 'value', 0); + + SoloParamHandle(obj, 'LocalLeftProb', 'value', 0.5); + SoloParamHandle(obj, 'LocalHitHistory', 'value', []); + SoloParamHandle(obj, 'LocalPrevSides', 'value', []); + + + SubheaderParam(obj, 'title', mfilename, x, y); + next_row(y, 1.5); + + w = gui_position('get_width'); + h = y-y0; + + + case 'update', % --- CASE UPDATE ------------------- + if length(varargin)>0, LocalLeftProb.value = varargin{1}; end; + if length(varargin)>1, LocalHitHistory.value = varargin{2}; end; + if length(varargin)>2, LocalPrevSides.value = varargin{3}; end; + % Protect against somebody passing in SPHs, not actual values, by mistake: + if isa(value(LocalLeftProb), 'SoloParamHandle'), LocalLeftProb.value = value(value(LocalLeftProb)); end; + if isa(value(LocalHitHistory), 'SoloParamHandle'), LocalHitHistory.value = value(value(LocalHitHistory)); end; + if isa(value(LocalPrevSides), 'SoloParamHandle'), LocalPrevSides.value = value(value(LocalPrevSides)); end; + + feval(mfilename, obj, 'update_hitfrac'); + feval(mfilename, obj, 'update_biashitfrac'); + + + + case 'update_biashitfrac', % ------- CASE UPDATE_BIASHITFRAC ------------- + if length(varargin)>0, LocalLeftProb.value = varargin{1}; end; + if length(varargin)>1, LocalHitHistory.value = varargin{2}; end; + if length(varargin)>2, LocalPrevSides.value = varargin{3}; end; + % Protect against somebody passing in SPHs, not actual values, by mistake: + if isa(value(LocalLeftProb), 'SoloParamHandle'), LocalLeftProb.value = value(value(LocalLeftProb)); end; + if isa(value(LocalHitHistory), 'SoloParamHandle'), LocalHitHistory.value = value(value(LocalHitHistory)); end; + if isa(value(LocalPrevSides), 'SoloParamHandle'), LocalPrevSides.value = value(value(LocalPrevSides)); end; + + LeftProb = value(LocalLeftProb); + hit_history = value(LocalHitHistory); hit_history = colvec(hit_history); + previous_sides = value(LocalPrevSides); + + kernel = exp(-(0:length(hit_history)-1)/BiasTau)'; + kernel = kernel(end:-1:1); + + prevs = previous_sides(1:length(hit_history))'; + ul = find(prevs == 'l'); + if isempty(ul), BiasLtHitFrac.value = 1; + else BiasLtHitFrac.value = sum(hit_history(ul) .* kernel(ul))/sum(kernel(ul)); + end; + + ur = find(prevs == 'r'); + if isempty(ur), BiasRtHitFrac.value = 1; + else BiasRtHitFrac.value = sum(hit_history(ur) .* kernel(ur))/sum(kernel(ur)); + end; + + if isempty(ul) && ~isempty(ur), BiasLtHitFrac.value = value(BiasRtHitFrac); end; + if isempty(ur) && ~isempty(ul), BiasRtHitFrac.value = value(BiasLtHitFrac); end; + + choices = probabilistic_trial_selector([value(BiasLtHitFrac), value(BiasRtHitFrac)], ... + [LeftProb, 1-LeftProb], value(Beta)); + LtProb.value = choices(1); + RtProb.value = choices(2); + + + case 'get_posterior_probs', % ------- CASE GET_POSTERIOR_PROBS ------------- + x = [value(LtProb) ; value(RtProb)]; %#ok + + + case 'update_hitfrac', % ------- CASE UPDATE_HITFRAC ------------- + if length(varargin)>0, LocalHitHistory.value = varargin{1}; end; + if length(varargin)>1, LocalPrevSides.value = varargin{2}; end; + % Protect against somebody passing in SPHs, not actual values, by mistake: + if isa(value(LocalHitHistory), 'SoloParamHandle'), LocalHitHistory.value = value(value(LocalHitHistory)); end; + if isa(value(LocalPrevSides), 'SoloParamHandle'), LocalPrevSides.value = value(value(LocalPrevSides)); end; + + hit_history = value(LocalHitHistory); hit_history = colvec(hit_history); + previous_sides = value(LocalPrevSides); + + + if length(hit_history)>0, + kernel = exp(-(0:length(hit_history)-1)/HitFracTau)'; + kernel = kernel(end:-1:1); + HitFrac.value = sum(hit_history .* kernel)/sum(kernel); + + prevs = previous_sides(1:length(hit_history))'; + u = find(prevs == 'l'); + if isempty(u), LtHitFrac.value = NaN; + else LtHitFrac.value = sum(hit_history(u) .* kernel(u))/sum(kernel(u)); + end; + + u = find(prevs == 'r'); + if isempty(u), RtHitFrac.value = NaN; + else RtHitFrac.value = sum(hit_history(u) .* kernel(u))/sum(kernel(u)); + end; + end; + + + case 'get', % ------- CASE GET ------------- + if length(varargin)~=1, + error('AntibiasSection:Invalid', '''get'' needs one extra param'); + end; + switch varargin{1}, + case 'Beta', + x = value(Beta); + case 'antibias_tau', + x = value(BiasTau); + otherwise + error('AntibiasSection:Invalid', 'Don''t know how to get %s', varargin{1}); + end; + + + case 'reinit', % ------- CASE REINIT ------------- + currfig = double(gcf); + + % Get the original GUI position and figure: + x = my_gui_info(1); y = my_gui_info(2); figure(my_gui_info(3)); + + % Delete all SoloParamHandles who belong to this object and whose + % fullname starts with the name of this mfile: + delete_sphandle('owner', ['^@' class(obj) '$'], ... + 'fullname', ['^' mfilename]); + + % Reinitialise at the original GUI position and figure: + [x, y] = feval(mfilename, obj, 'init', x, y); + + % Restore the current figure: + figure(currfig); +end; + + + + +function [x] = colvec(x) + if size(x,2) > size(x,1), x = x'; end; + \ No newline at end of file diff --git a/Protocols/@Arpit_CentrePokeTraining/LOGplotPairs.m b/Protocols/@Arpit_CentrePokeTraining/LOGplotPairs.m new file mode 100644 index 00000000..275f736e --- /dev/null +++ b/Protocols/@Arpit_CentrePokeTraining/LOGplotPairs.m @@ -0,0 +1,71 @@ +function LOGplotPairs(x,y,marker,markersize,markeredgecolor,thislinewidth,FONTSIZE) + + +% delete(gca) +% load('NEWHOT','HOTETOBOKHORAM') + +% position = [16 124 1019 761]; +% % +% figure('Position',position); + +% if nargin <8 +% LogOrLin='linear'; +% end + +if nargin <4 + marker='.'; +end + + +% Plot the points +hold on +x=log(x); +y=log(y); +for i=1:length(x) + +% loglog(x(i),y(i),marker,'color',map(in,:),'markerfacecolor',map(in,:),'MarkerSize',markersize) +% plot3(x(i),y(i),v(i),marker,'MarkerSize',markersize,'MarkerEdgeColor',markeredgecolor,'LineWidth',thislinewidth) + plot(x(i),y(i),marker,'MarkerSize',markersize,'MarkerEdgeColor',markeredgecolor,'LineWidth',thislinewidth) + +end + +xlim([min([x ;y])-min([x ;y])/2 max([x ;y])+min([x ;y])/2]) +ylim([min([x; y])-min([x ;y])/2 max([x; y])+-min([x ;y])/2]) +hold off + +% figure('Position',[1 scrsz(4)/2 scrsz(3)/2 scrsz(4)/2]) + +Ylabel('log_e \sigma_2','FontSize',FONTSIZE,'FontName','Cambria Math'); +set(double(gca),'Fontsize',15) +Xlabel('log_e \sigma_1','FontSize',FONTSIZE,'FontName','Cambria Math') + +% grid on +setyticklabels=1 + +if setyticklabels==1 + +Ytick=get(double(gca),'YtickLabel'); +Xtick=get(double(gca),'XtickLabel'); +% +% Ytick=num2str((3:0.5:6)'); +% Xtick=num2str((3:0.5:6)'); + +% set(gca,'ytick',[],'xtick',[]); +end +% +% axis square +% HUMANORRAT=2 +% if HUMANORRAT==2 +% ylim([2.5 6]) +% xlim([2.2 6.3]) +% else +% ylim([3.5 6]) +% xlim([3.5 6.5]) +% end +if setyticklabels==1 +set(double(gca),'ytick',str2num(Ytick),'xtick',str2num(Xtick)); +set(double(gca),'yticklabel',num2str(round(round(exp(str2num(Ytick)).*100)./100)),'xticklabel',num2str(round(round(exp(str2num(Xtick)).*100)./100))); +end + +% set(gca,'yticklabel',num2str(round(exp(str2num(Ytick)).*100)./100),'xticklabel',num2str(round(exp(str2num(Xtick)).*100)./100)); +view(2) diff --git a/Protocols/@Arpit_CentrePokeTraining/OverallPerformanceSection.m b/Protocols/@Arpit_CentrePokeTraining/OverallPerformanceSection.m new file mode 100644 index 00000000..b377fc98 --- /dev/null +++ b/Protocols/@Arpit_CentrePokeTraining/OverallPerformanceSection.m @@ -0,0 +1,127 @@ +% [x, y] = OverallPerformanceSection(obj, action, x,y) +% +% Reports overall performance. Uses training_stage from SideSection. +% +% PARAMETERS: +% ----------- +% +% action One of: +% 'init' To initialise the section and set up the GUI +% for it +% +% 'close' Delete all of this section's GUIs and data +% +% 'reinit' Delete all of this section's GUIs and data, +% and reinit, at the same position on the same +% figure as the original section GUI was placed. +% +% 'evalueta' Look at history and compute hit fraction, etc. +% +% x, y Only relevant to action = 'init'; they indicate the initial +% position to place the GUI at, in the current figure window +% +% RETURNS: +% -------- +% +% perf When action == 'init', returns x and y, pixel positions on +% the current figure, updated after placing of this section's GUI. +% When action == 'evaluate', returns a vector with elements +% [ntrials, violation_rate, left_hit_frac, right_hit_frac, hit_frac] +% +% + +% CDB, 23-March-2013 + +function [x, y] = OverallPerformanceSection(obj, action, x,y) + +GetSoloFunctionArgs(obj); + +switch action, + + % ------------------------------------------------------------------ + % INIT + % ------------------------------------------------------------------ + + case 'init' + + SoloParamHandle(obj, 'my_gui_info', 'value', [x y double(gcf)], 'saveable', 0); + + DispParam(obj, 'ntrials', 0, x, y); next_row(y); + DispParam(obj, 'violation_rate', 0, x, y, 'TooltipString', ... + 'Fraction of trials with a center poke violation'); next_row(y); + DispParam(obj, 'timeout_rate', 0, x, y, 'TooltipString', ... + 'Fraction of trials with timeout'); next_row(y); + DispParam(obj, 'Left_hit_frac', 0, x, y, 'TooltipString', ... + 'Fraction of correct Left trials'); next_row(y); + DispParam(obj, 'Right_hit_frac', 0, x, y, 'TooltipString', ... + 'Fraction of correct Right trials'); next_row(y); + DispParam(obj, 'hit_frac', 0, x, y, 'TooltipString', ... + 'Fraction of correct trials'); next_row(y); + + SubheaderParam(obj, 'title', 'Overall Performance', x, y); + next_row(y, 1.5); + SoloParamHandle(obj, 'previous_parameters', 'value', []); + + % ------------------------------------------------------------------ + % evaluate + % ------------------------------------------------------------------ + + case 'evaluate' + + switch value(training_stage) + case 1, %% center led on -> poke in the center -> go cue -> reward light and sound + if n_done_trials > 1, + ntrials.value = n_done_trials; + %violation_rate.value = numel(find(isnan(hit_history)))/n_done_trials; + violation_rate.value = numel(find(violation_history))/n_done_trials; + timeout_rate.value = numel(find(timeout_history))/n_done_trials; + goods = ~isnan(hit_history)'; + lefts = previous_sides(1:n_done_trials)=='l'; + rights = previous_sides(1:n_done_trials)=='r'; + Left_hit_frac.value = mean(hit_history(goods & lefts)); + Right_hit_frac.value = mean(hit_history(goods & rights)); + hit_frac.value = mean(hit_history(goods)); + end; + end + + if nargout > 0, + x = [n_done_trials, value(violation_rate), value(timeout_rate), value(Left_hit_frac), ... + value(Right_hit_frac), value(hit_frac)]; + end; + + + % ------------------------------------------------------------------ + % close + % ------------------------------------------------------------------ + + case 'close', + % Delete all SoloParamHandles who belong to this object and whose + % fullname starts with the name of this mfile: + delete_sphandle('owner', ['^@' class(obj) '$'], ... + 'fullname', ['^' mfilename]); + + + % ------------------------------------------------------------------ + % reinit + % ------------------------------------------------------------------ + + case 'reinit', + currfig = double(gcf); + + % Get the original GUI position and figure: + x = my_gui_info(1); y = my_gui_info(2); figure(my_gui_info(3)); + + % Delete all SoloParamHandles who belong to this object and whose + % fullname starts with the name of this mfile: + delete_sphandle('owner', ['^@' class(obj) '$'], ... + 'fullname', ['^' mfilename]); + + % Reinitialise at the original GUI position and figure: + [x, y] = feval(mfilename, obj, 'init', x, y); + + % Restore the current figure: + figure(currfig); + +end + + diff --git a/Protocols/@Arpit_CentrePokeTraining/PlayStimuli.m b/Protocols/@Arpit_CentrePokeTraining/PlayStimuli.m new file mode 100644 index 00000000..52ca7c8f --- /dev/null +++ b/Protocols/@Arpit_CentrePokeTraining/PlayStimuli.m @@ -0,0 +1,87 @@ + +function [x, y] = PlayStimuli(obj, action, varargin) +GetSoloFunctionArgs(obj); + +switch action, + + case 'init' + + if length(varargin) < 2, + error('Need at least two arguments, x and y position, to initialize %s', mfilename); + end; + x = varargin{1}; y = varargin{2}; + + ToggleParam(obj, 'StimuliPlayShow', 0, x, y, 'OnString', 'StimToPlay', ... + 'OffString', 'StimToPlay', 'TooltipString', 'Show/Hide Sounds panel'); + set_callback(StimuliPlayShow, {mfilename, 'show_hide'}); %#ok (Defined just above) + next_row(y); + oldx=x; oldy=y; parentfig=double(gcf); + + SoloParamHandle(obj, 'myfig', 'value', figure('Position', [100 100 560 440], ... + 'closerequestfcn', [mfilename '(' class(obj) ', ''hide'');'], 'MenuBar', 'none', ... + 'Name', mfilename), 'saveable', 0); + set(double(gcf), 'Visible', 'off'); + x=10;y=10; + + MenuParam(obj, 'filter_type', {'GAUS','LPFIR', 'FIRLS','BUTTER','MOVAVRG','KAISER','EQUIRIP','HAMMING'}, ... + 'GAUS', x, y, 'labelfraction', 0.35, 'TooltipString', sprintf(['\nDifferent filters. ''LPFIR'': lowpass FIR ''FIRLS'': Least square linear-phase FIR filter design\n', ... + '\n''BUTTER'': IIR Butterworth lowpass filter ''GAUS'': Gaussian filter (window)\n', ... + '\n''MOVAVRG'': Moving average FIR filter ''KAISER'': Kaiser-window FIR filtering\n', ... + '\n''EQUIRIP'':Eqiripple FIR filter ''HAMMING'': Hamming-window based FIR'])); + next_row(y, 1.3) + NumeditParam(obj, 'A1_sigma', 0.01, x,y,'label','A1_sigma','TooltipString','Sigma value for the stimulus'); + next_row(y); + NumeditParam(obj,'fcut',110,x,y,'label','fcut','TooltipString','Cut off frequency on the original white noise'); + next_row(y); + NumeditParam(obj,'lfreq',2000,x,y,'label','Modulator_LowFreq','TooltipString','Lower bound for the frequency modulator'); + next_row(y); + NumeditParam(obj,'hfreq',20000,x,y,'label','Modulator_HighFreq','TooltipString','Upper bound for the frequency modulator'); + next_row(y); + NumeditParam(obj,'dur',0.5,x,y,'label','dur','TooltipString','duration of stimulus in ms'); + next_row(y); + + sname='GaussNoise'; + + srate=SoundManagerSection(obj,'get_sample_rate'); + Fs=srate; + replace=1; + T=dur; + L=floor(T*Fs); % Length of signal + sigma_1=1; + + pos1 = sigma_1*randn(Fs,1); + base = randsample(pos1,L,replace); + filtbase=filt(base,fcut,Fs,value(filter_type)); + normbase=filtbase./(max(abs(filtbase))); + + mod1 = sigma_1*randn(Fs,1); + mod1 = randsample(mod1,L,replace); + hf = design(fdesign.bandpass('N,F3dB1,F3dB2',10,value(lfreq),value(hfreq),Fs)); + filtmod=filter(hf,mod1); + modulator=filtbase./(max(abs(filtmod))); + + AUD1=normbase(1:dur*srate).*modulator(1:dur*srate).*A1_sigma; + w=[AUD1'; AUD1']; + SoundManagerSection(obj, 'declare_new_sound', sname); + SoundManagerSection(obj, 'set_sound',sname,w) + + SubheaderParam(obj, [sname 'Head'], sname, x,y,'TooltipString',''); + PushbuttonParam(obj, [sname 'Play'], x,y, 'label', 'Play', 'position', [x y 30 20]); + set_callback(eval([sname 'Play']),{'SoundManagerSection', 'play_sound', sname}); + PushbuttonParam(obj, [sname 'Stop'], x,y, 'label', 'Stop', 'position', [x+30 y 30 20]); + set_callback(eval([sname 'Stop']),{'SoundManagerSection', 'stop_sound', sname}); + + x=oldx; y=oldy; + figure(parentfig); + + case 'hide', + StimuliPlayShow.value = 0; set(value(myfig), 'Visible', 'off'); + + case 'show', + StimuliPlayShow.value = 1; set(value(myfig), 'Visible', 'on'); + + case 'show_hide', + if StimuliPlayShow == 1, set(value(myfig), 'Visible', 'on'); %#ok (defined by GetSoloFunctionArgs) + else set(value(myfig), 'Visible', 'off'); + end; +end diff --git a/Protocols/@Arpit_CentrePokeTraining/ProduceNoiseStimuli.m b/Protocols/@Arpit_CentrePokeTraining/ProduceNoiseStimuli.m new file mode 100644 index 00000000..262d6a6d --- /dev/null +++ b/Protocols/@Arpit_CentrePokeTraining/ProduceNoiseStimuli.m @@ -0,0 +1,33 @@ +clear all + +T=1000; +fcut=110; +Fs=10000; +filter_type='GAUS'; +outband=55; +Ind=[0.4]; + +minS1_d=1; +maxS1_d=30; +numClass=8; + +S1_d(1)=minS1_d; +S2_d(1)=S1_d(1)*(1-Ind)/(1+Ind); +S1_u(1)=S1_d; +S2_u(1)=S1_u(1)*(1+Ind)/(1-(Ind)); +for ii=2:numClass +S1_d(ii)=S2_u(ii-1); +S2_d(ii)=S1_d(ii)*(1-Ind)/(1+Ind); +S1_u(ii)=S1_d(ii); +S2_u(ii)=S1_u(ii)*(1+Ind)/(1-Ind); +end + +[rawA rawB filtA filtB]=noise(Sigma1(ss),Sigma2(ss),T,fcut,Fs,filter_type,outband); + + +pairs=[]; +pairs(:,1)=[S1_d S1_u]; +pairs(:,2)=[S2_d S2_u]; +thesepairs=pairs(2:end-1,:) +LOGplotPairs(thesepairs(:,1),thesepairs(:,2),'s',18,'k',1,16) + diff --git a/Protocols/@Arpit_CentrePokeTraining/PunishmentSection.m b/Protocols/@Arpit_CentrePokeTraining/PunishmentSection.m new file mode 100644 index 00000000..5289350e --- /dev/null +++ b/Protocols/@Arpit_CentrePokeTraining/PunishmentSection.m @@ -0,0 +1,133 @@ +% Typical section code-- this file may be used as a template to be added +% on to. The code below stores the current figure and initial position when +% the action is 'init'; and, upon 'reinit', deletes all SoloParamHandles +% belonging to this section, then calls 'init' at the proper GUI position +% again. + + +% [x, y] = YOUR_SECTION_NAME(obj, action, x, y) +% +% Section that takes care of YOUR HELP DESCRIPTION +% +% PARAMETERS: +% ----------- +% +% obj Default object argument. +% +% action One of: +% 'init' To initialise the section and set up the GUI +% for it +% +% 'reinit' Delete all of this section's GUIs and data, +% and reinit, at the same position on the same +% figure as the original section GUI was placed. +% +% 'prepare_next_trial' Goes through the processing necessary +% to compute what the next trial's correct side +% should be. +% +% 'get_current_side' Returns either the string 'l' or the +% string 'r', for which side is the current trial's +% correcy side. +% +% +% x, y Relevant to action = 'init'; they indicate the initial +% position to place the GUI at, in the current figure window +% +% RETURNS: +% -------- +% +% [x, y] When action == 'init', returns x and y, pixel positions on +% the current figure, updated after placing of this section's GUI. +% +% +% x When action == 'get_current_side', returns either the string 'l' +% or the string 'r', for Left and Right, respectively. +% + + +function [x, y] = PunishmentSection(obj, action, x, y) + +GetSoloFunctionArgs(obj); + +switch action + case 'init', + % Save the figure and the position in the figure where we are + % going to start adding GUI elements: + SoloParamHandle(obj, 'my_gui_info', 'value', [x y double(gcf)], 'saveable', 0); + + NumeditParam(obj, 'DrinkTime', 20, x, y, 'TooltipString', sprintf('\nTime over which drinking is ok')); next_row(y); + ToggleParam(obj, 'WarningSoundPanel', 1, x, y, 'OnString', 'warn show', 'OffString', 'warn hide', 'position', [x y 80 20]); + NumeditParam(obj, 'WarnDur', 4, x, y, 'labelfraction', 0.6, 'TooltipString', 'Warning sound duration in secs', 'position', [x+80 y 60 20]); + NumeditParam(obj, 'DangerDur',15, x, y, 'labelfraction', 0.6, 'TooltipString', sprintf('\nDuration of post-drink period where poking is punished'), 'position', [x+140 y 60 20]); next_row(y); + set_callback(WarningSoundPanel, {mfilename, 'WarningSoundPanel'}); + % start subpanel + oldx = x; oldy = y; oldfigure = double(gcf); + SoloParamHandle(obj, 'WarningSoundPanelFigure', 'saveable', 0, 'value', figure('Position', [120 120 430 156])); + sfig = value(WarningSoundPanelFigure); + set(sfig, 'MenuBar', 'none', 'NumberTitle', 'off', ... + 'Name', 'Warning sound', 'CloseRequestFcn', 'Classical(classical, ''closeWarningSoundPanel'')'); + SoundInterface(obj, 'add', 'WarningSound', 10, 10); + SoundInterface(obj, 'set', 'WarningSound', 'Vol', 0.0002); + SoundInterface(obj, 'set', 'WarningSound', 'Vol2', 0.004); + SoundInterface(obj, 'set', 'WarningSound', 'Dur1', 4); + SoundInterface(obj, 'set', 'WarningSound', 'Loop', 0); + SoundInterface(obj, 'set', 'WarningSound', 'Style', 'WhiteNoiseRamp'); + + SoundInterface(obj, 'add', 'DangerSound', 215, 10); + SoundInterface(obj, 'set', 'DangerSound', 'Vol', 0.004); + SoundInterface(obj, 'set', 'DangerSound', 'Dur1', 1); + SoundInterface(obj, 'set', 'DangerSound', 'Loop', 1); + SoundInterface(obj, 'set', 'DangerSound', 'Style', 'WhiteNoise'); + + x = oldx; y = oldy; figure(oldfigure); + % end subpanel + SoloFunctionAddVars('SMASection', 'ro_args', {'DrinkTime', 'WarnDur', 'DangerDur'}); + + [x, y] = PunishInterface(obj, 'add', 'PostDrinkPun', x, y); %#ok + next_row(y); + + %--------------------------------------------------------------- + % WarningSoundPanel + %--------------------------------------------------------------- + + case 'WarningSoundPanel' + if WarningSoundPanel==0, set(value(WarningSoundPanelFigure), 'Visible', 'off'); + else set(value(WarningSoundPanelFigure), 'Visible', 'on'); + end; + + %--------------------------------------------------------------- + % CLOSE + %--------------------------------------------------------------- + + case 'close', + if exist('WarningSoundPanelFigure', 'var') && ishandle(value(WarningSoundPanelFigure)), + delete(value(WarningSoundPanelFigure)); + end; + + if exist('PostDrinkPun_SoundsPanel', 'var') && ishandle(value(PostDrinkPun_SoundsPanel)), + delete(value(PostDrinkPun_SoundsPanel)); + end + + %--------------------------------------------------------------- + % REINIT + %--------------------------------------------------------------- + case 'reinit', + currfig = double(gcf); + + % Get the original GUI position and figure: + x = my_gui_info(1); y = my_gui_info(2); figure(my_gui_info(3)); + + % Delete all SoloParamHandles who belong to this object and whose + % fullname starts with the name of this mfile: + delete_sphandle('owner', ['^@' class(obj) '$'], ... + 'fullname', ['^' mfilename '_']); + + % Reinitialise at the original GUI position and figure: + [x, y] = feval(mfilename, obj, 'init', x, y); + + % Restore the current figure: + figure(currfig); +end; + + diff --git a/Protocols/@Arpit_CentrePokeTraining/RewardsSection.m b/Protocols/@Arpit_CentrePokeTraining/RewardsSection.m new file mode 100644 index 00000000..65ead5c1 --- /dev/null +++ b/Protocols/@Arpit_CentrePokeTraining/RewardsSection.m @@ -0,0 +1,237 @@ +% Typical section code-- this file may be used as a template to be added +% on to. The code below stores the current figure and initial position when +% the action is 'init'; and, upon 'reinit', deletes all SoloParamHandles +% belonging to this section, then calls 'init' at the proper GUI position +% again. +% +% +% [x, y] = YOUR_SECTION_NAME(obj, action, x, y) +% +% Section that takes care of YOUR HELP DESCRIPTION +% +% PARAMETERS: +% ----------- +% +% obj Default object argument. +% +% action One of: +% 'init' To initialise the section and set up the GUI +% for it +% +% 'reinit' Delete all of this section's GUIs and data, +% and reinit, at the same position on the same +% figure as the original section GUI was placed. +% +% 'get_stimulus' Returns either (1) a structure with fields 'type' +% and 'duration', with the contents of 'type' being +% 'lights' and the contents of 'duration' the +% maximum duration, in secs, of the stimulus; or (2) a +% structure with the fields 'type', 'duration', and +% 'id', with contents 'sounds', duration of sound in +% secs, and integer sound_id, respectively. +% +% 'get_poked_trials' Returns a double, number of trials in +% which subject poked in the appropriate poke at +% some point. +% +% +% x, y Relevant to action = 'init'; they indicate the initial +% position to place the GUI at, in the current figure window +% +% RETURNS: +% -------- +% +% [x, y] When action == 'init', returns x and y, pixel positions on +% the current figure, updated after placing of this section's GUI. +% +% +% x When action == 'get_current_side', returns either the string 'l' +% or the string 'r', for Left and Right, respectively. +% + + +function [x, y] = RewardsSection(obj, action, x, y) + +GetSoloFunctionArgs(obj); + +switch action + case 'init', + % Save the figure and the position in the figure where we are + % going to start adding GUI elements: + SoloParamHandle(obj, 'my_gui_info', 'value', [x y double(gcf)]); + + + DispParam(obj, 'n_trials', 0, x, y, ... + 'TooltipString', 'total # of elapsed trials'); next_row(y); + DispParam(obj, 'poked_trials', 0, x, y, ... + 'TooltipString', '# of trials in which subject poked in the appropriate poke at some point'); next_row(y); + DispParam(obj, 'consec_p_trials', 0, x, y, ... + 'TooltipString', '# of consecutive "poked_trials"'); next_row(y); + DispParam(obj, 'consec_up_trials', 0, x, y, ... + 'TooltipString', '# of consecutive UN"poked_trials"'); + ToggleParam(obj, 'WaterBlock', 0, x, y, 'position', [x+185 y 15 15], 'label', '', 'TooltipString', ... + sprintf('\nif BLACK, no water delivery, in "direct" mode. If BROWN, normal water delivery'), ... + 'OnString', '', 'OffString', ''); next_row(y); + DispParam(obj, 'rewarded_trials', 0, x, y, ... + 'TooltipString', '# of trials in which subject poked and got water'); next_row(y); + DispParam(obj, 'consec_r_trials', 0, x, y, ... + 'TooltipString', '# of consecutive trials, ending in last trial, in which subject poked and got water'); next_row(y); + DispParam(obj, 'rt', 0, x, y, ... + 'TooltipString', 'reaction time'); next_row(y); + NumeditParam(obj, 'rtThreshold', 4, x, y, 'TooltipString', ... + sprintf('\nrt less than this defines a "quick" trial')); next_row(y); + DispParam(obj, 'consec_q_trials', 0, x, y, ... + 'TooltipString', '# of consecutive trials with rt less than rtThreshold'); next_row(y); + DispParam(obj, 'good_trials', 0, x, y, ... + 'TooltipString', '# of trials in which subject exceeded post CS/pre CS poke ratio'); next_row(y); + DispParam(obj, 'consec_g_trials', 0, x, y, ... + 'TooltipString', '# of consecutive trials, ending in last trial, in which subject exceeded post CS/pre CS poke ratio'); next_row(y); + + % ------- + + SoloParamHandle(obj, 'r_trials', 'value', []); + SoloParamHandle(obj, 'q_trials', 'value', []); + SoloParamHandle(obj, 'g_trials', 'value', []); + + SubheaderParam(obj, 'title', 'Rewards Section', x, y); + next_row(y, 1.5); + + SoloFunctionAddVars('SMASection', ... + 'ro_args', {'WaterDelivery', 'RewardTime', 'PokeMeasureTime', 'WaterBlock'}); + feval(mfilename, obj, 'WaterDelivery'); % Set whatever is appropriate for current WaterDelivery + + + % --------------------------------------------------------------------- + % + % CASE GET_POKED_TRIALS + % + % --------------------------------------------------------------------- + + + case 'get_poked_trials', + x = value(poked_trials); %#ok + return; + + case 'add_to_pd' + %% add to pd + x.reward_time=cell2mat(get_history(RewardTime)); + + % --------------------------------------------------------------------- + % + % CASE PREPARE_NEXT_TRIAL + % + % --------------------------------------------------------------------- + + + case 'prepare_next_trial', + if isempty(parsed_events), return; end; + if ~isempty(previous_sides), %#ok + previous_sides = previous_sides(:); + wdh = get_history(WaterDelivery); + if isempty(wdh), wdh{1}='direct'; end; % fix for wierd bug + switch value(wdh{end}), + case 'direct', csstate = 'direct_cs'; + case 'on correct poke', csstate = 'cs'; + case 'on correctly timed poke', csstate = 'rewardable_cs'; + otherwise + error('huh?'); + end; + cs_onset = parsed_events.states.(csstate)(1,1); + if isequal(StimulusSection(obj, 'get_last_stimulus_loc'), 'anti-loc'), + if previous_sides(end)=='l', poke = 'R'; else poke = 'L'; end; + else + if previous_sides(end)=='l', poke = 'L'; else poke = 'R'; end; + end; + mypokes = parsed_events.pokes.(poke)(:,1); + + if ~isempty(find(mypokes > cs_onset,1)) + poked_trials.value = poked_trials+1; %#ok + consec_p_trials.value = consec_p_trials+1; %#ok + consec_up_trials.value = 0; + else + consec_p_trials.value = 0; + consec_up_trials.value = consec_up_trials+1; %#ok + end + end; + + if rows(parsed_events.states.lefthit)>0 || rows(parsed_events.states.righthit)>0, + r_trials.value = [r_trials(1:n_done_trials-1) 1]; %#ok + rewarded_trials.value = rewarded_trials+1; %#ok + consec_r_trials.value = consec_r_trials+1; %#ok + else + r_trials.value = [r_trials(1:n_done_trials-1) 0]; %#ok + consec_r_trials.value = 0; + end; + + hit_history.value = value(r_trials); + + n_trials.value = n_trials+1; %#ok + + + % Compute reaction times only for non-direct delivery modes: + if strcmp(csstate, 'direct_cs'), + consec_q_trials.value = 0; + else + if rows(parsed_events.states.lefthit>0) + rt.value = parsed_events.states.lefthit(1,1) - parsed_events.states.(csstate)(1,1); + elseif rows(parsed_events.states.righthit>0) + rt.value = parsed_events.states.righthit(1,1) - parsed_events.states.(csstate)(1,1); + else + warning('CLASSICAL:No_hit_state', 'No lefthit or righthit -- not computing rt!'); + end; + + if rt < rtThreshold, consec_q_trials.value = consec_q_trials + 1; %#ok + else consec_q_trials.value = 0; + end; + end; + + % Now compute the good poke ratio stuff + if ~isempty(previous_sides), + preCSonset_pokes = ... + length(find(parsed_events.states.(csstate)(1,1)-PokeMeasureTime < mypokes & ... + mypokes < parsed_events.states.(csstate)(1,1))); + postCSonset_pokes = ... + length(find(parsed_events.states.(csstate)(1,1) < mypokes & ... + mypokes < parsed_events.states.(csstate)(1,1)+PokeMeasureTime)); + if preCSonset_pokes > 0, PokeRatio.value = postCSonset_pokes / preCSonset_pokes; + elseif postCSonset_pokes > 0, PokeRatio.value = Inf; + else PokeRatio.value = 0; + end; + if PokeRatio > PokeRatioThreshold, + good_trials.value = good_trials+1; %#ok + consec_g_trials.value = consec_g_trials+1; %#ok + else + consec_g_trials.value = 0; + end; + end; + + + + + % --------------------------------------------------------------------- + % + % CASE REINIT + % + % --------------------------------------------------------------------- + + case 'reinit', + currfig = double(gcf); + + % Get the original GUI position and figure: + x = my_gui_info(1); y = my_gui_info(2); figure(my_gui_info(3)); + + % Delete all SoloParamHandles who belong to this object and whose + % fullname starts with the name of this mfile: + delete_sphandle('owner', ['^@' class(obj) '$'], ... + 'fullname', ['^' mfilename]); + + + % Reinitialise at the original GUI position and figure: + [x, y] = feval(mfilename, obj, 'init', x, y); + + % Restore the current figure: + figure(currfig); +end + + + diff --git a/Protocols/@Arpit_CentrePokeTraining/SoundSection.m b/Protocols/@Arpit_CentrePokeTraining/SoundSection.m new file mode 100644 index 00000000..4e49253d --- /dev/null +++ b/Protocols/@Arpit_CentrePokeTraining/SoundSection.m @@ -0,0 +1,63 @@ + + +function [x, y] = SoundSection(obj, action, varargin) + +GetSoloFunctionArgs(obj); + +switch action, + + % ------------------------------------------------------------------ + % INIT + % ------------------------------------------------------------------ + + case 'init' + if length(varargin) < 2, + error('Need at least two arguments, x and y position, to initialize %s', mfilename); + end; + x = varargin{1}; y = varargin{2}; + + ToggleParam(obj, 'SoundsShow', 0, x, y, 'OnString', 'Sounds', ... + 'OffString', 'Sounds', 'TooltipString', 'Show/Hide Sounds panel'); + set_callback(SoundsShow, {mfilename, 'show_hide'}); %#ok (Defined just above) + next_row(y); + oldx=x; oldy=y; parentfig=double(gcf); + + SoloParamHandle(obj, 'myfig', 'value', figure('Position', [100 100 560 440], ... + 'closerequestfcn', [mfilename '(' class(obj) ', ''hide'');'], 'MenuBar', 'none', ... + 'Name', mfilename), 'saveable', 0); + set(double(gcf), 'Visible', 'off'); + x=10;y=10; + + [x,y]=SoundInterface(obj,'add','ViolationSound',x,y,'Volume',0.005,'Freq',9000,'Duration',0.5); +% [x,y]=SoundInterface(obj,'add','ViolationSound',x,y,'Style','WhiteNoise','Volume',0.01); + [x,y]=SoundInterface(obj,'add','ErrorSound',x,y,'Style','WhiteNoise','Volume',0.08); + [x,y]=SoundInterface(obj,'add','TimeoutSound',x,y,'Style','WhiteNoise','Volume',0.08,'Duration',0.5); + next_column(x); + y=10; + [x,y]=SoundInterface(obj,'add','GoSound',x,y,'Style','Tone','Volume',0.005,'Freq',3000,'Duration',0.2); + SoundInterface(obj, 'disable', 'GoSound', 'Dur1'); + + [x,y]=SoundInterface(obj,'add','RewardSound',x,y,'Style','Bups','Volume',1,'Freq',5,'Duration',1.5); + [x,y]=SoundInterface(obj,'add','SOneSound',x,y,'Style','Tone','Volume',0.005,'Freq',3000,'Duration',0.2); + SoundInterface(obj, 'disable', 'GoSound', 'Dur1'); + SoundInterface(obj, 'disable', 'GoSound', 'Freq1'); + [x,y]=SoundInterface(obj,'add','STwoSound',x,y,'Style','WhiteNoise','Volume',0.08); + + + + x=oldx; y=oldy; + figure(parentfig); + + case 'hide', + SoundsShow.value = 0; set(value(myfig), 'Visible', 'off'); + + case 'show', + SoundsShow.value = 1; set(value(myfig), 'Visible', 'on'); + + case 'show_hide', + if SoundsShow == 1, set(value(myfig), 'Visible', 'on'); %#ok (defined by GetSoloFunctionArgs) + else set(value(myfig), 'Visible', 'off'); + end; + +end + \ No newline at end of file diff --git a/Protocols/@Arpit_CentrePokeTraining/StimulatorSection.m b/Protocols/@Arpit_CentrePokeTraining/StimulatorSection.m new file mode 100644 index 00000000..71ad0760 --- /dev/null +++ b/Protocols/@Arpit_CentrePokeTraining/StimulatorSection.m @@ -0,0 +1,325 @@ +% +% PARAMETERS: +% ----------- +% +% obj Default object argument. +% +% action One of: +% +% 'prepare_next_trial' +% +% 'init' +% +% +% RETURNS: +% -------- +% +% +% +% +% +% + + + +function [varargout] = StimulatorSection(obj, action, x, y) + +GetSoloFunctionArgs(obj); + +switch action + +%% init +% ---------------------------------------------------------------- +% +% INIT +% +% ---------------------------------------------------------------- + + case 'init', + %NumeditParam(obj,'LCB_onstim', 0,x,y,'position',[x y 100 20],'labelfraction',0.6); + %NumeditParam(obj,'LCB_nostim', 0,x,y,'position',[x+100 y 100 20],'labelfraction',0.6); next_row(y); + %SoloParamHandle(obj,'LegalCBrk_temp', 'value',0); + + MenuParam(obj,'StimInterval',{'WholeTrial','S1','DelayDur','GoCue'},1,x,y,'labelfraction',0.30); next_row(y); + set_callback(StimInterval, {mfilename, 'StimInterval'}); + MenuParam(obj,'StimOnSide',{'both','left','right'},1,x,y,'labelfraction',0.3); next_row(y); + + diolines = bSettings('get','DIOLINES', 'all'); + for i = 1:size(diolines,1); dionames{i} = diolines{i,1}; dionums(i) = diolines{i,2}; end %#ok + [dionums order] = sort(dionums); + dionames2 = cell(0); + for i = 1:length(dionums); if ~isnan(dionums(i)); dionames2{end+1} = dionames{order(i)}; end; end %#ok + dionames2 = cell(0); + dionames2{1} = 'opto'; + + MenuParam(obj,'StimLine',dionames2,1,x,y,'labelfraction',0.30); next_row(y); + + SC = state_colors(obj); + WC = wave_colors(obj); + states = fieldnames(SC); + waves = fieldnames(WC); + states(2:end+1) = states; + states{1} = 'cp'; + states(end+1:end+length(waves)) = waves; + + MenuParam(obj,'StimState',states,1,x,y,'labelfraction',0.30); next_row(y); + + NumeditParam(obj,'StimStates', 1,x,y,'position',[x y 100 20],'labelfraction',0.60); + NumeditParam(obj,'StimLines', 1,x,y,'position',[x+100 y 100 20],'labelfraction',0.60); next_row(y); + + NumeditParam(obj,'StartDelay', 0,x,y,'position',[x y 100 20],'labelfraction',0.60); + NumeditParam(obj,'StimFreq', 20,x,y,'position',[x+100 y 100 20],'labelfraction',0.60); next_row(y); + NumeditParam(obj,'PulseWidth',15,x,y,'position',[x y 100 20],'labelfraction',0.60); + NumeditParam(obj,'NumPulses', 1,x,y,'position',[x+100 y 100 20],'labelfraction',0.60); next_row(y); + + DispParam(obj,'SD',0 ,x,y,'position',[x y 50 20],'labelfraction',0.4); + DispParam(obj,'SF',20,x,y,'position',[x+50 y 50 20],'labelfraction',0.4); + DispParam(obj,'PW',15,x,y,'position',[x+100 y 50 20],'labelfraction',0.4); + DispParam(obj,'NP',1,x,y,'position',[x+150 y 50 20],'labelfraction',0.4); next_row(y); + + NumeditParam(obj,'StimProb', 0,x,y,'position',[x y 100 20],'labelfraction',0.65); + ToggleParam( obj,'ShuffleValues',0,x,y,'position',[x+100 y 100 20],'OnString','Shuffle','OffString','Lock'); next_row(y); + + SoloParamHandle(obj, 'stimulator_history', 'value', []); + + SubheaderParam(obj, 'title', 'Stimulator Section', x, y); next_row(y); + varargout{1} = x; + varargout{2} = y; + +%% update_values +% ----------------------------------------------------------------------- +% +% UPDATE_VALUES +% +% ----------------------------------------------------------------------- + + case 'update_values', + + StimulatorSection(obj,'StimInterval'); + sh = value(stimulator_history) %#ok + %if n_done_trials == 0 || sh(end) == 0 + % LegalCBrk_temp.value = value(LegalCBrk); %#ok + %end + + if ~dispatcher('is_running'); + %dispatcher is not running, last stim_hist not used, lop it off + sh = sh(1:end-1); + end + + if value(StimProb) == 0 + %LCB_nostim.value = value(LegalCBrk); %#ok + stimulator_history.value = [sh, 0]; + elseif rand(1) <= value(StimProb) %&& ((value(StimOnFree)==1 && strcmp(value(ThisTrial_Free),'FREE')) || value(StimOnFree)==0) + stimulator_history.value = [sh, 1]; + %LegalCBrk.value = value(LCB_onstim); + else + %LegalCBrk.value = value(LCB_nostim); %#ok + stimulator_history.value = [sh, 0]; + end + + + +%% prepare_next_trial +% ----------------------------------------------------------------------- +% +% PREPARE_NEXT_TRIAL +% +% ----------------------------------------------------------------------- + + case 'prepare_next_trial', + sh = value(stimulator_history); %#ok + + sma = x; + + sd = value(StartDelay); + sf = value(StimFreq); + pw = value(PulseWidth); + np = value(NumPulses); + ss = value(StimStates) + sl = value(StimLines) + + if value(ShuffleValues) == 1 + sd = sd(ceil(rand(1) * length(sd))); + sf = sf(ceil(rand(1) * length(sf))); + pw = pw(ceil(rand(1) * length(pw))); + np = np(ceil(rand(1) * length(np))); + ss = ss(ceil(rand(1) * length(ss))); + sl = sl(ceil(rand(1) * length(sl))); + else + if length(unique([length(sd) length(sf) length(pw) length(np) length(ss) length(sl)])) > 1 + disp('Warning: param values in StimulatorSection have different lengths. Only first value will be used.'); + temp = 1; + else + temp = ceil(rand(1) * length(sd)); + end + sd = sd(temp); sf = sf(temp); pw = pw(temp); np = np(temp); ss = ss(temp); sl = sl(temp); + end + + pss = get(get_ghandle(StimState),'String'); %#ok + psl = get(get_ghandle(StimLine), 'String'); %#ok + if ss > length(pss) + disp('StimState value greater than list of possible stim states'); + else + StimState.value = ss; + disp('test ss') + value(ss) + end + + if sl > length(psl) + slc = ['0',num2str(sl),'0']; + z = find(slc == '0'); + if length(z) > 2 + sln = []; + for i = 1:length(z)-1 + sln(end+1) = str2num(slc(z(i)+1:z(i+1)-1)); %#ok + end + if any(sln > length(psl)) + disp('StimLine value greater than list of possible stim lines'); + else + slname = psl{sln(1)}; + for i=2:length(sln) + slname = [slname,'+',psl{sln(i)}]; %#ok + end + if sum(strcmp(psl,slname)) == 0 + psl{end+1} = slname; + set(get_ghandle(StimLine),'String',psl) + end + StimLine.value = find(strcmp(psl,slname)==1,1,'first'); + sl = sln; + end + else + disp('StimLine value greater than list of possible stim lines'); + end + else + StimLine.value = sl; + end + + for i = 1:length(sl) + stimline = bSettings('get','DIOLINES',psl{sl(i)}); + + sma = add_scheduled_wave(sma,... + 'name', ['stimulator_wave',num2str(i)],... + 'preamble', (1/sf)-(pw/1000),... %%%% Remember: change it such that if this is negative makes it 0 + 'sustain' , pw/1000,... + 'DOut', stimline,... + 'loop', np-1); + + if sd ~= 0 + sma = add_scheduled_wave(sma,... + 'name',['stimulator_wave_pause',num2str(i)],... + 'preamble',sd,... + 'trigger_on_up',['stimulator_wave',num2str(i)]); + else + sma = add_scheduled_wave(sma,... + 'name',['stimulator_wave_pause',num2str(i)],... + 'preamble',1,... + 'trigger_on_up',['stimulator_wave',num2str(i)]); + end + end + + for i = 1:length(sl) + if sh(end) == 1 + if strcmp(value(StimState),'none') == 0 + if sd ~= 0 + sma = add_stimulus(sma,['stimulator_wave_pause',num2str(i)],value(StimState)); + else + sma = add_stimulus(sma,['stimulator_wave',num2str(i)],value(StimState)); + end + + SD.value = sd; SF.value = sf; PW.value = pw; NP.value = np; + end + else + SD.value = 0; SF.value = 0; PW.value = 0; NP.value = 0; + end + end + + varargout{1} = sma; + + + %% Case StimInterval + case 'StimInterval' + + if strcmp(StimInterval, 'WholeTrial'); + PulseWidth.value = Total_CP_duration*1000; + StimFreq.value = 1000/PulseWidth; + StartDelay.value = PreStim_time; + elseif strcmp(StimInterval, 'S1'); + PulseWidth.value = A1_time*1000; + StimFreq.value = 1000/PulseWidth; + StartDelay.value = PreStim_time; + elseif strcmp(StimInterval, 'DelayDur'); + PulseWidth.value = time_bet_aud1_gocue*1000; + StimFreq.value = 1000/PulseWidth; + StartDelay.value = PreStim_time + A1_time; + elseif strcmp(StimInterval, 'GoCue'); + PulseWidth.value = time_go_cue*1000; + StimFreq.value = 1000/PulseWidth; + StartDelay.value = PreStim_time + A1_time + time_bet_aud1_gocue; + + end +%% set +% ----------------------------------------------------------------------- +% +% SET +% +% ----------------------------------------------------------------------- + case 'set' + varname = x; + newval = y; + + try + temp = 'SoloParamHandle'; %#ok + eval(['test = isa(',varname,',temp);']); + if test == 1 + eval([varname,'.value = newval;']); + end + catch %#ok + showerror; + warning(['Unable to assign value: ',num2str(newval),' to SoloParamHandle: ',varname]); %#ok + end + + +%% get +% ----------------------------------------------------------------------- +% +% GET +% +% ----------------------------------------------------------------------- + case 'get' + varname = x; + + try + temp = 'SoloParamHandle'; %#ok + eval(['test = isa(',varname,',temp);']); + if test == 1 + eval(['varargout{1} = value(',varname,');']); + end + catch %#ok + showerror; + warning(['Unable to get value from SoloParamHandle: ',varname]); %#ok + end + + +%% reinit +% ----------------------------------------------------------------------- +% +% REINIT +% +% ----------------------------------------------------------------------- + case 'reinit', + currfig = double(gcf); + + % Delete all SoloParamHandles who belong to this object and whose + % fullname starts with the name of this mfile: + delete_sphandle('owner', ['^@' class(obj) '$'], ... + 'fullname', ['^' mfilename]); + + + % Reinitialise at the original GUI position and figure: + feval(mfilename, obj, 'init'); + + % Restore the current figure: + figure(currfig); +end; + + diff --git a/Protocols/@Arpit_CentrePokeTraining/noiselib.m b/Protocols/@Arpit_CentrePokeTraining/noiselib.m new file mode 100644 index 00000000..57dd5c7d --- /dev/null +++ b/Protocols/@Arpit_CentrePokeTraining/noiselib.m @@ -0,0 +1,51 @@ +function [base,filtbase]=noiselib(num,T,fcut,Fs,filter_type,outband) +close all +clc + +%%%%%%%%%%%%%%%%% Determines of the type of filter used %%%%%%%%%%%%%%%%%%% +%'LPFIR': lowpass FIR%%%%%'FIRLS': Least square linear-phase FIR filter design +%'BUTTER': IIR Butterworth lowpass filter%%%%%%'GAUS': Gaussian filter (window) +%'MOVAVRG': Moving average FIR filter%%%%%%%%'KAISER': Kaiser-window FIR filtering +% 'EQUIRIP':Eqiripple FIR filter%%%%% 'HAMMING': Hamming-window based FIR +% T is duration of each signal in milisecond, fcut is the cut-off frequency +% Fs is the sampling frequency +% outband=40; + +for ii=1:num +s = RandStream('mcg16807','Seed',ii) +RandStream.setDefaultStream(s) + +replace=1; +L=T*Fs/1000; % Length of signal +t=L*1000*linspace(0,1,L)/Fs; % time in miliseconds +%%%%%%%%%%% produce position values %%%%%%% +pos1 = randn(Fs,1); +pos1(pos1>outband)=[]; +pos1(pos1<-outband)=[]; + +base(:,num)=pos1; +%base = randsample(pos1,L,replace); +%%%% Filter the original position values %%%%%% +filtbase(:,num)=filt(base,fcut,Fs,filter_type); + +end + +end + +%%%%%% plot the row and filtered position values %%%%%%%%% +% subplot(2,2,1) +% plot(t,base,'r'); +% ylabel('base') +% xlabel('Time (ms)') +% subplot(2,2,2) +% plot(t,target,'g'); +% ylabel('target') +% xlabel('Time (ms)') +% subplot(2,2,3) +% plot(t,filtbase) +% ylabel('filtbase') +% xlabel('Time (ms)') +% subplot(2,2,4) +% plot(t,filttarget) +% ylabel('filttarget') +% xlabel('Time (ms)') diff --git a/Protocols/@Arpit_CentrePokeTraining/private/filt.m b/Protocols/@Arpit_CentrePokeTraining/private/filt.m new file mode 100644 index 00000000..e4c572ff --- /dev/null +++ b/Protocols/@Arpit_CentrePokeTraining/private/filt.m @@ -0,0 +1,60 @@ +function filtsignal=filt(signal,fcut,Fs,filter_type) +a=2; % wp/ws used in butterworth method and LS linear FIR method +N=200; % filter order used in lowpass FIR method +rp=3; % passband ripple in dB used in butterworth method +rs=60; % stopband attenuation in dB used in butterworth method +beta=0.1102*(rs-8.8); %used in Kaiser window to obtain sidelobe attenuation of rs dB +if strcmp(filter_type, 'GAUS') || strcmp(filter_type, 'MOVAVRG') +window = fix(Fs/fcut); % window size used in Gaussian and moving average methods +end +wp=2*fcut/Fs; % normalized passband corner frequency wp, the cutoff frequency +ws=a*wp; % normalized stopband corner frequency + + +switch filter_type + case 'BUTTER' %Butterworth IIR filter + if length(wp)>1 + ws(1)=2*(fcut(1)/2)/Fs; + ws(2)=2*(fcut(2)+fcut(1)/2)/Fs; + [n,wn]=buttord(wp,ws,rp,rs); + [b,a]=butter(n,wn,'bandpass'); + else + [n,wn]=buttord(wp,ws,rp,rs); + [b,a]=butter(n,wn,'low'); + end + filtsignal=filter(b,a,signal);%conventional filtering + case 'LPFIR' %Lowpass FIR filter + d=fdesign.lowpass('N,Fc',N,fcut,Fs); % Fc is the 6-dB down point, N is the filter order(N+1 filter coefficients) + Hd = design(d); + filtsignal=filter(Hd.Numerator,1,signal); %conventional filtering + case 'FIRLS' %Least square linear-phase FIR filter design + b=firls(255,[0 2*fcut/Fs a*2*fcut/Fs 1],[1 1 0 0]); + filtsignal=filter(b,1,signal); %conventional filtering + case 'EQUIRIP' %Eqiripple FIR filter + d=fdesign.lowpass('Fp,Fst,Ap,Ast',wp,ws,rp,rs); + Hd=design(d,'equiripple'); + filtsignal=filter(Hd.Numerator,1,signal); %conventional filtering + case 'MOVAVRG' % Moving average FIR filtering, Rectangular window + h = ones(window,1)/window; + b = fir1(window-1,wp,h); + filtsignal = filter(b, 1, signal); + case 'HAMMING' % Hamming-window based FIR filtering + b = fir1(150,wp); + filtsignal = filter(b, 1, signal); + filtsignal = filter(h, 1, signal); + case 'GAUS' % Gaussian-window FIR filtering + h = normpdf(1:window, 0, fix(window/2)); + b = fir1(window-1,wp,h); + filtsignal = filter(b, 1, signal); + case 'GAUS1' % Gaussian-window FIR filtering + b = fir1(window-1,wp,gausswin(window,2)/window); + filtsignal = filter(b, 1, signal); + case 'KAISER' %Kaiser-window FIR filtering + h=kaiser(window,beta); + b = fir1(window-1,wp,h); + filtsignal = filter(b, 1, signal); + + otherwise + sprintf('filter_type is wrong!! havaset kojast!!') +end + diff --git a/Protocols/@Arpit_CentrePokeTraining/private/noisestim.m b/Protocols/@Arpit_CentrePokeTraining/private/noisestim.m new file mode 100644 index 00000000..7f074ab0 --- /dev/null +++ b/Protocols/@Arpit_CentrePokeTraining/private/noisestim.m @@ -0,0 +1,47 @@ +function [base,target,normbase,normtarget]=noisestim(sigma_1,sigma_2,T,fcut,Fs,filter_type) + +%%%%%%%%%%%%%%%%% Determines of the type of filter used %%%%%%%%%%%%%%%%%%% +%'LPFIR': lowpass FIR%%%%%'FIRLS': Least square linear-phase FIR filter design +%'BUTTER': IIR Butterworth lowpass filter%%%%%%'GAUS': Gaussian filter (window) +%'MOVAVRG': Moving average FIR filter%%%%%%%%'KAISER': Kaiser-window FIR filtering +% 'EQUIRIP':Eqiripple FIR filter%%%%% 'HAMMING': Hamming-window based FIR +% T is duration of each signal in milisecond, fcut is the cut-off frequency +% Fs is the sampling frequency +% outband=40; +replace=1; +L=floor(T*Fs); % Length of signal +t=L*linspace(0,1,L)/Fs; % time in miliseconds +%%%%%%%%%%% produce position values %%%%%%% +pos1 = sigma_1*randn(Fs,1); +% pos1(pos1>outband)=[]; +% pos1(pos1<-outband)=[]; + +pos2 =sigma_2*randn(Fs,1); +% pos2(pos2>outband)=[]; +% pos2(pos2<-outband)=[]; +base = randsample(pos1,L,replace); +target = randsample(pos2,L,replace); +%%%% Filter the original position values %%%%%% +filtbase=filt(base,fcut,Fs,filter_type); +filttarget=filt(target,fcut,Fs,filter_type); +normbase=filtbase./(max(abs(filtbase))); +normtarget=filttarget./(max(abs(filttarget))); +end + +%%%%%% plot the row and filtered position values %%%%%%%%% +% subplot(2,2,1) +% plot(t,base,'r'); +% ylabel('base') +% xlabel('Time (ms)') +% subplot(2,2,2) +% plot(t,target,'g'); +% ylabel('target') +% xlabel('Time (ms)') +% subplot(2,2,3) +% plot(t,filtbase) +% ylabel('filtbase') +% xlabel('Time (ms)') +% subplot(2,2,4) +% plot(t,filttarget) +% ylabel('filttarget') +% xlabel('Time (ms)') diff --git a/Protocols/@Arpit_CentrePokeTraining/private/singlenoise.m b/Protocols/@Arpit_CentrePokeTraining/private/singlenoise.m new file mode 100644 index 00000000..0cf201f3 --- /dev/null +++ b/Protocols/@Arpit_CentrePokeTraining/private/singlenoise.m @@ -0,0 +1,32 @@ +function [normbase]=singlenoise(sigma_1,T,fcut,Fs,filter_type) +%GetSoloFunctionArgs(obj); + +%%%%%%%%%%%%%%%%% Determines of the type of filter used %%%%%%%%%%%%%%%%%%% +%'LPFIR': lowpass FIR%%%%%'FIRLS': Least square linear-phase FIR filter design +%'BUTTER': IIR Butterworth lowpass filter%%%%%%'GAUS': Gaussian filter (window) +%'MOVAVRG': Moving average FIR filter%%%%%%%%'KAISER': Kaiser-window FIR filtering +% 'EQUIRIP':Eqiripple FIR filter%%%%% 'HAMMING': Hamming-window based FIR +% T is duration of each signal in milisecond, fcut is the cut-off frequency +% Fs is the sampling frequency +% outband=40; +sigma_1=1; +%T=10000; +%fcut=[3000 4000]; +%Fs=200000; +filter_type='BUTTER'; +outband=60; +replace=1; +L=floor(T*Fs); % Length of signal +%%%%%%%%%%% produce position values %%%%%%% +pos1 = sigma_1*randn(Fs,1); +% pos1(pos1>outband)=[]; +% pos1(pos1<-outband)=[]; + +base = randsample(pos1,L,replace); +%%%% Filter the original position values %%%%%% +%filtbase=filt(base,fcut,Fs,filter_type); +hf = design(fdesign.bandpass('N,F3dB1,F3dB2',10,fcut(1),fcut(2),Fs)); +filtbase=filter(hf,base); +normbase=filtbase./(max(abs(filtbase))); +end + diff --git a/Protocols/@Arpit_CentrePokeTraining/state_colors.m b/Protocols/@Arpit_CentrePokeTraining/state_colors.m new file mode 100644 index 00000000..c6df1d67 --- /dev/null +++ b/Protocols/@Arpit_CentrePokeTraining/state_colors.m @@ -0,0 +1,16 @@ +function SC = state_colors(obj) %#ok + +SC = struct( ... + 'wait_for_cpoke', [0.68 1 0.63], ... + 'cp', [0.63 1 0.94], ... + 'cp_legal_cbreak_period', [0.63 1 0.94]*0.8, ... + 'sideled_on', [1 0.79 0.63], ... + 'wait_for_collecting_reward', [0.53 0.78 1.00],... + 'righthit', [0.3 0.9 0], ... + 'lefthit', [0 0.9 0.3], ... + 'hit_state', [0.77 0.60 0.48], ... + 'second_hit_state', [0.25 0.45 0.48], ... + 'drink_state', [0 1 0], ... + 'error_state', [1 0.54 0.54], ... + 'violation_state', [0.31 0.48 0.30], ... + 'timeout_state', 0.8*[0.31 0.48 0.30]); \ No newline at end of file diff --git a/Protocols/@Arpit_CentrePokeTraining/wave_colors.m b/Protocols/@Arpit_CentrePokeTraining/wave_colors.m new file mode 100644 index 00000000..0d5a7339 --- /dev/null +++ b/Protocols/@Arpit_CentrePokeTraining/wave_colors.m @@ -0,0 +1,11 @@ +function WC = wave_colors(obj) %#ok + +WC = struct(... + 'stim_wave', [1 1 1 ], ... + 'prestim_wave', [1 1 1 ], ... + 'stimulator_wave', [1 1 0 ], ... + 'stimulator_wave1', [1 1 0 ], ... + 'stimulator_wave2', [1 1 0 ], ... + 'stimulator_wave3', [1 1 0 ], ... + 'stimulator_wave4', [1 1 0 ], ... + 'stimulator_wave5', [1 1 0 ]); \ No newline at end of file From 10c60bfdd0c36a226ea8fe691a7eabdd85ab21c4 Mon Sep 17 00:00:00 2001 From: ArpitAgarwal Date: Tue, 18 Mar 2025 17:12:44 +0000 Subject: [PATCH 021/164] changes made to Arpit_CentrePokeTraining protocol: 1 step for cenre poke --- .../Arpit_CentrePokeTraining.m | 20 +- .../Arpit_CentrePokeTrainingSMA.m | 1 - .../ParamsSection.asv | 532 ++++++++++++++++++ .../@Arpit_CentrePokeTraining/ParamsSection.m | 105 +++- 4 files changed, 628 insertions(+), 30 deletions(-) create mode 100644 Protocols/@Arpit_CentrePokeTraining/ParamsSection.asv diff --git a/Protocols/@Arpit_CentrePokeTraining/Arpit_CentrePokeTraining.m b/Protocols/@Arpit_CentrePokeTraining/Arpit_CentrePokeTraining.m index 3bf0ddb3..a582172e 100644 --- a/Protocols/@Arpit_CentrePokeTraining/Arpit_CentrePokeTraining.m +++ b/Protocols/@Arpit_CentrePokeTraining/Arpit_CentrePokeTraining.m @@ -7,32 +7,32 @@ % we inherit only from Plugins obj = class(struct, mfilename, pokesplot2, saveload, sessionmodel, soundmanager, soundui, antibias, ... - water, distribui, punishui, comments, soundtable, sqlsummary,reinforcement); + water, distribui, punishui, comments, soundtable, sqlsummary,reinforcement,softpokestay2); %--------------------------------------------------------------- % BEGIN SECTION COMMON TO ALL PROTOCOLS, DO NOT MODIFY %--------------------------------------------------------------- % If creating an empty object, return without further ado: -if nargin==0 || (nargin==1 && ischar(varargin{1}) && strcmp(varargin{1}, 'empty')), +if nargin==0 || (nargin==1 && ischar(varargin{1}) && strcmp(varargin{1}, 'empty')) return; -end; +end -if isa(varargin{1}, mfilename), % If first arg is an object of this class itself, we are +if isa(varargin{1}, mfilename) % If first arg is an object of this class itself, we are % Most likely responding to a callback from % a SoloParamHandle defined in this mfile. if length(varargin) < 2 || ~ischar(varargin{2}), error(['If called with a "%s" object as first arg, a second arg, a ' ... 'string specifying the action, is required\n']); else action = varargin{2}; varargin = varargin(3:end); %#ok - end; + end else % Ok, regular call with first param being the action string. action = varargin{1}; varargin = varargin(2:end); %#ok -end; +end GetSoloFunctionArgs(obj); -switch action, +switch action %% init case 'init' @@ -124,12 +124,12 @@ next_column(x); y=5; [x, y] = OverallPerformanceSection(obj, 'init', x, y); - [x, y] = StimulatorSection(obj, 'init', x, y); next_row(y, 1.3); + % [x, y] = StimulatorSection(obj, 'init', x, y); next_row(y, 1.3); [x, y] = ParamsSection(obj, 'init', x, y); %#ok [x, y] = SoundSection(obj,'init',x,y); % [x, y] = PlayStimuli(obj,'init',x,y); % [x, y] = StimulusSection(obj,'init',x,y); - % [x, y] = SoftPokeStayInterface(obj, 'add', 'soft_cp', x, y); + [x, y] = SoftPokeStayInterface(obj, 'add', 'soft_cp', x, y); figpos = get(double(gcf), 'Position'); [expmtr, rname]=SavingSection(obj, 'get_info'); HeaderParam(obj, 'prot_title', [mfilename ': ' expmtr ', ' rname], x, y, 'position', [10 figpos(4)-25, 800 20]); @@ -155,7 +155,7 @@ % Run SessionDefinition *after* ParamsSection so we know whether the % trial was a violation or not SessionDefinition(obj, 'next_trial'); - StimulatorSection(obj, 'update_values'); + % StimulatorSection(obj, 'update_values'); OverallPerformanceSection(obj, 'evaluate'); % StimulusSection(obj,'prepare_next_trial'); SoundManagerSection(obj, 'send_not_yet_uploaded_sounds'); diff --git a/Protocols/@Arpit_CentrePokeTraining/Arpit_CentrePokeTrainingSMA.m b/Protocols/@Arpit_CentrePokeTraining/Arpit_CentrePokeTrainingSMA.m index 86d84d27..824fbe57 100644 --- a/Protocols/@Arpit_CentrePokeTraining/Arpit_CentrePokeTrainingSMA.m +++ b/Protocols/@Arpit_CentrePokeTraining/Arpit_CentrePokeTrainingSMA.m @@ -45,7 +45,6 @@ case 'init' - SoftPokeStayInterface(obj, 'add', 'soft_cp', x, y); case 'prepare_next_trial' diff --git a/Protocols/@Arpit_CentrePokeTraining/ParamsSection.asv b/Protocols/@Arpit_CentrePokeTraining/ParamsSection.asv new file mode 100644 index 00000000..6904cef2 --- /dev/null +++ b/Protocols/@Arpit_CentrePokeTraining/ParamsSection.asv @@ -0,0 +1,532 @@ +function [x, y] = ParamsSection(obj, action, x,y) + +GetSoloFunctionArgs(obj); + +switch action + + % ------------------------------------------------------------------ + % INIT + % ------------------------------------------------------------------ + + case 'init' + + SoloParamHandle(obj, 'my_gui_info', 'value', [x y double(gcf)], 'saveable', 0); + y0 = y; + + [x, y] = AntibiasSectionAthena(obj, 'init', x, y); + + + ToggleParam(obj, 'antibias_LRprob', 0, x,y,... + 'OnString', 'AB_Prob ON',... + 'OffString', 'AB_Prob OFF',... + 'TooltipString', sprintf(['If on (Yellow) then it enables the AntiBias algorithm\n'... + 'based on changing the probablity of Left vs Right'])); + + next_row(y); + NumeditParam(obj, 'LeftProb', 0.5, x, y); next_row(y); + set_callback(LeftProb, {mfilename, 'new_leftprob'}); + MenuParam(obj, 'MaxSame', {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, Inf}, Inf, x, y, ... + 'TooltipString', sprintf(['\nMaximum number of consecutive trials where correct\n' ... + 'response is on the same side. Overrides antibias. Thus, for\n' ... + 'example, if MaxSame=5 and there have been 5 Left trials, the\n' ... + 'next trial is guaranteed to be Right'])); next_row(y); + + DispParam(obj, 'ThisTrial', 'LEFT', x, y); next_row(y); + SoloParamHandle(obj, 'previous_sides', 'value', []); + DeclareGlobals(obj, 'ro_args', 'previous_sides'); + SubheaderParam(obj, 'title', 'Sides Section', x, y); + next_row(y, 1.5); + next_column(x); y = 5; + % Reward Collection + NumeditParam(obj, 'RewardCollection_duration', 10, x,y,'label','RewardCollection_dur','TooltipString','Wait until rat collects the reward else a timeout'); + next_row(y); + NumeditParam(obj, 'SideLed_duration', 0.5, x,y,'label','Side LED duration','TooltipString','Duration of SideLed'); + % Centre Poke + next_row(y); + NumeditParam(obj, 'legal_cbreak', 0.1, x,y, 'position', [x, y, 175 20], 'TooltipString','Time in sec for which it is ok to be outside the center port before a violation occurs.'); + next_row(y); + NumeditParam(obj, 'SettlingIn_time', 0.2, x,y, 'position', [x, y, 175 20], 'TooltipString','Initial settling period during which there is no violation'); + next_row(y); + NumeditParam(obj, 'A1_time_Min', 0.4, x,y,'label','Min time AUD1','TooltipString','Min value to select the Duration of fixed stimulus'); + set_callback(A1_time_Min, {mfilename, 'new_CP_duration'}); + next_row(y); + NumeditParam(obj, 'A1_time', 0.4, x,y,'label','AUD1 Time','TooltipString','Actual Duration of fixed stimulus'); + next_row(y); + set_callback(A1_time, {mfilename, 'new_CP_duration'}); + next_row(y); + NumeditParam(obj, 'A1_time_Max', 0.4, x,y,'label','Max time AUD1','TooltipString','Max value to select the Duration of fixed stimulus'); + set_callback(A1_time_Max, {mfilename, 'new_CP_duration'}); + next_row(y); + NumeditParam(obj, 'PreStim_time_Min', 0.20, x,y,'label','Pre-Stim Min','TooltipString','Min Time in NIC before starting the stimulus'); + set_callback(PreStim_time_Min, {mfilename, 'new_CP_duration'}); + next_row(y); + NumeditParam(obj, 'PreStim_time', 0.20, x,y,'label','Pre-Stim time','TooltipString','Actual Time in NIC before starting the stimulus'); + set_callback(PreStim_time, {mfilename, 'new_CP_duration'}); + next_row(y); + NumeditParam(obj, 'PreStim_time_Max', 0.40, x,y,'label','Pre-Stim Max','TooltipString','Max Time in NIC before starting the stimulus'); + set_callback(PreStim_time_Max, {mfilename, 'new_CP_duration'}); + next_row(y); + NumeditParam(obj, 'time_bet_aud1_gocue_Min', 0.2, x,y,'label','Min A1-GoCue time','TooltipString','Min time between the end of the stimulus and the go cue '); + set_callback(time_bet_aud1_gocue_Min, {mfilename, 'new_CP_duration'}); + next_row(y); + NumeditParam(obj, 'time_bet_aud1_gocue', 0.2, x,y,'label','A1-GoCue time','TooltipString','Actual time between the end of the stimulus and the go cue '); + next_row(y); + set_callback(time_bet_aud1_gocue, {mfilename, 'new_CP_duration'}); + next_row(y); + NumeditParam(obj, 'time_bet_aud1_gocue_Max', 2, x,y,'label','Max A1-GoCue time','TooltipString','Max time between the end of the stimulus and the go cue '); + set_callback(time_bet_aud1_gocue_Max, {mfilename, 'new_CP_duration'}); + next_row(y); + DispParam(obj, 'init_CP_duration', 0.01, x,y,'label','init_CP duration','TooltipString','Duration of Nose in Central Poke before Go cue starts (see Total_CP_duration)'); + next_row(y); + DispParam(obj, 'CP_duration', PreStim_time+A1_time+time_bet_aud1_gocue, x,y,'label','CP duration','TooltipString','Duration of Nose in Central Poke before Go cue starts (see Total_CP_duration)'); + set_callback(CP_duration, {mfilename, 'new_CP_duration'}); + next_row(y); + NumeditParam(obj, 'time_go_cue' ,0.1, x,y,'label','Go Cue Duration','TooltipString','duration of go cue (see Total_CP_duration)'); + set_callback(time_go_cue, {mfilename, 'new_time_go_cue'}); + next_row(y); + DispParam(obj, 'Total_CP_duration', CP_duration+time_go_cue, x, y, 'TooltipString', 'Total expected(rat can poke out anytime after Go cue onset) nose in center port time, in secs. Sum of CP_duration and Go Cue duration'); %#ok<*NODEF> + next_row(y); + ToggleParam(obj, 'warmup_on', 1, x,y,... + 'OnString', 'Warmup ON',... + 'OffString', 'Warmup OFF',... + 'TooltipString', sprintf(['If on (Yellow) then it applies the initial warming up phase, during which the\n',... + 'CP_duration starts small and gradually grows to last_session_max_cp_duration'])); + next_row(y); + + NumeditParam(obj, 'reward_delay', 0.01, x,y,'label','Reward Delay','TooltipString','Delay between side poke and reward delivery'); + next_row(y); + NumeditParam(obj, 'drink_time', 1, x,y,'label','Drink Time','TooltipString','waits to finish water delivery'); + next_row(y); + NumeditParam(obj, 'timeout_iti', 5, x,y,'label','No Choice Timeout','TooltipString','ITI on timeout trials'); + next_row(y); + NumeditParam(obj, 'violation_iti', 1, x,y,'label','Violation Timeout','TooltipString','Center poke violation duration'); + + next_row(y); + ToggleParam(obj, 'random_PreStim_time', 0, x,y,... + 'OnString', 'random PreStim_time ON',... + 'OffString', 'random PreStim_time OFF',... + 'TooltipString', sprintf('If on (black) then it enables the random time between the user given range')); + set_callback(random_PreStim_time, {mfilename, 'new_CP_duration'}); + next_row(y); + ToggleParam(obj, 'random_A1_time', 0, x,y,... + 'OnString', 'random A1_time ON',... + 'OffString', 'random A1_time OFF',... + 'TooltipString', sprintf('If on (black) then it enables the random sampling of A1_time')); + set_callback(random_A1_time, {mfilename, 'new_CP_duration'}); + next_row(y); + ToggleParam(obj, 'random_prego_time', 0, x,y,... + 'OnString', 'random prego_time ON',... + 'OffString', 'random prego_time OFF',... + 'TooltipString', sprintf('If on (black) then it enables the random sampling of time between the end of the stimulus and the go cue')); + set_callback(random_prego_time, {mfilename, 'new_CP_duration'}); + + next_column(x); + y=5; + NumeditParam(obj,'trials_in_stage',1,x,y,'label','Trial Counter'); + next_row(y); + NumeditParam(obj,'training_stage',1,x,y,'label','Training Stage'); + set_callback(training_stage, {mfilename, 'Training_Stage'}); + next_row(y); + ToggleParam(obj,'use_training',0,x,y,'OnString','Using Autotrain','OffString','Manual Settings'); + set_callback(training_stage, {mfilename, 'Training_Stage'}); + + next_row(y); + NumeditParam(obj, 'ntrial_correct_bias', 0, x, y, ... + 'TooltipString', 'antibias starts from trial=ntrial_correct_bias'); + next_row(y); + NumeditParam(obj, 'right_left_diff', .12, x, y, ... + 'TooltipString', 'antibias applies if difference between right and left sides is bigger than this number'); + next_row(y); + NumeditParam(obj, 'max_wtr_mult', 4, x, y, ... + 'TooltipString', 'wtr_mult will be min(max_wtr_mult,right_hit/left_hit)'); + next_row(y); + NumeditParam(obj, 'left_wtr_mult', 1, x, y, ... + 'TooltipString', 'all left reward times are multiplied by this number'); + next_row(y); + NumeditParam(obj, 'right_wtr_mult', 1, x, y, ... + 'TooltipString', 'all right reward times are multiplied by this number'); + next_row(y); + ToggleParam(obj, 'antibias_wtr_mult', 0, x,y,... + 'OnString', 'AB ON',... + 'OffString', 'AB OFF',... + 'TooltipString', sprintf(['If on (black) then it disables the wtr_mult entries\n'... + 'and uses hitfrac to adjust the water times'])); + + next_row(y); + SoloFunctionAddVars('Arpit_CentrePokeTrainingSMA', 'ro_args', ... + {'CP_duration';'SideLed_duration'; ... + 'RewardCollection_duration';'training_stage'; ... + 'legal_cbreak' ; 'SettlingIn_time'; 'time_go_cue'; ... + 'A1_time';'time_bet_aud1_gocue' ; 'PreStim_time'; + 'drink_time';'reward_delay';'left_wtr_mult';... + 'right_wtr_mult';'antibias_wtr_mult';... + 'timeout_iti';'violation_iti'}); + + % SoloFunctionAddVars('StimulatorSection', 'ro_args', ... + % {'A1_time';'time_bet_aud1_gocue';'time_go_cue'; ... + % 'PreStim_time';'CP_duration';'Total_CP_duration'}); + + % History of hit/miss: + SoloParamHandle(obj, 'deltaf_history', 'value', []); + + SoloFunctionAddVars('OverallPerformanceSection', 'ro_args', ... + {'training_stage'}); + + + SoloParamHandle(obj, 'previous_parameters', 'value', []); + + case 'new_leftprob' + AntibiasSectionAthena(obj, 'update_biashitfrac', value(LeftProb)); + + + case 'new_CP_duration' + + if random_PreStim_time == 1 && (value(PreStim_time) < value(PreStim_time_Min) || value(PreStim_time) > value(PreStim_time_Max)) + PreStim_time.value = value(PreStim_time_Min); + else + PreStim_time.value = value(PreStim_time); + end + + if random_A1_time == 1 && (value(A1_time) < value(A1_time_Min) || value(A1_time) > value(A1_time_Max)) + A1_time.value = value(A1_time_Min); + else + A1_time.value = value(A1_time); + end + + if random_prego_time == 1 && (value(time_bet_aud1_gocue) < value(time_bet_aud1_gocue_Min) || value(time_bet_aud1_gocue) > value(time_bet_aud1_gocue_Max)) + time_bet_aud1_gocue.value = value(time_bet_aud1_gocue_Min); + else + time_bet_aud1_gocue.value = value(time_bet_aud1_gocue); + end + + CP_duration.value= SettlingIn_time + PreStim_time + A1_time + time_bet_aud1_gocue; + Total_CP_duration.value = CP_duration + time_go_cue; %#ok<*NASGU> + + case 'new_time_go_cue' + Total_CP_duration.value = CP_duration + time_go_cue; + SoundInterface(obj, 'set', 'GoSound', 'Dur1', value(time_go_cue)); + + case 'training_stage' + + delay_time.value=0; + trials_in_stage.value=0; + reward_delay.value=0.01; + left_prob.value=0.5; + right_prob.value=0.5; + + switch value(training_stage) + + case {1,2} %% learning the reward sound association -left or right led on -> poke -> sound+reward + + time_go_cue.value=0.200; + disable(SettlingIn_time); + disable(PreStim_time); + disable(A1_time); + disable(time_bet_aud1_gocue); + + + case {3,4} % Centre poke without the A1_Stim but has violation in 4 + enable(SettlingIn_time); + disable(PreStim_time); + disable(A1_time); + disable(time_bet_aud1_gocue); + + if n_done_trials <1 && warmup_on ==1 + CP_duration.value=value(init_CP_duration); + else + CP_duration.value=CP_duration; + end + Total_CP_duration.value = CP_duration + time_go_cue; %#ok<*NASGU> + + case {5,6,7} % + + enable(SettlingIn_time); + enable(PreStim_time); + enable(A1_time); + enable(time_bet_aud1_gocue); + + if random_prego_time == 1 + time_bet_aud1_gocue = randi([time_bet_aud1_gocue_Min, time_bet_aud1_gocue_Max],1,1); + end + + if random_A1_time == 1 + A1_time = randi([A1_time_Min, A1_time_Max],1,1); + end + + if random_PreStim_time == 1 + PreStim_time = randi([PreStim_time_Min, PreStim_time_Max],1,1); + end + + if n_done_trials <1 && warmup_on ==1 + CP_duration.value=value(init_CP_duration); + else + CP_duration.value=SettlingIn_time + A1_time + PreStim_time + time_bet_aud1_gocue; + end + Total_CP_duration.value = CP_duration + time_go_cue; %#ok<*NASGU> + + + end + + + case 'prepare_next_trial' + + delay_time.value=0; + trials_in_stage.value=0; + reward_delay.value=0.01; + left_prob.value=0.5; + right_prob.value=0.5; + + switch value(training_stage) + + case {1,2} %% learning the reward sound association -left or right led on -> poke -> sound+reward + + time_go_cue.value=0.200; + disable(SettlingIn_time); + disable(PreStim_time); + disable(A1_time); + disable(time_bet_aud1_gocue); + + + case {3,4} % Centre poke without the A1_Stim but has violation in 4 + enable(SettlingIn_time); + disable(PreStim_time); + disable(A1_time); + disable(time_bet_aud1_gocue); + + if n_done_trials <1 && warmup_on ==1 + CP_duration.value=value(init_CP_duration); + else + CP_duration.value=CP_duration; + end + Total_CP_duration.value = CP_duration + time_go_cue; %#ok<*NASGU> + + case {5,6,7} % + + enable(SettlingIn_time); + enable(PreStim_time); + enable(A1_time); + enable(time_bet_aud1_gocue); + + if random_prego_time == 1 + time_bet_aud1_gocue = randi([time_bet_aud1_gocue_Min, time_bet_aud1_gocue_Max],1,1); + end + + if random_A1_time == 1 + A1_time = randi([A1_time_Min, A1_time_Max],1,1); + end + + if random_PreStim_time == 1 + PreStim_time = randi([PreStim_time_Min, PreStim_time_Max],1,1); + end + + if n_done_trials <1 && warmup_on ==1 + CP_duration.value=value(init_CP_duration); + else + CP_duration.value=SettlingIn_time + A1_time + PreStim_time + time_bet_aud1_gocue; + end + Total_CP_duration.value = CP_duration + time_go_cue; %#ok<*NASGU> + + + end + + + %% update hit_history, previous_sides, etc + was_viol=false; + was_hit=false; + was_timeout=false; + if n_done_trials>0 + if ~isempty(parsed_events) + if isfield(parsed_events,'states') + if isfield(parsed_events.states,'timeout_state') + was_timeout=rows(parsed_events.states.timeout_state)>0; + end + if isfield(parsed_events.states,'violation_state') + was_viol=rows(parsed_events.states.violation_state)>0; + end + end + + end + + violation_history.value=[violation_history(:); was_viol]; + timeout_history.value=[timeout_history(:); was_timeout]; + + ParamsSection(obj,'update_side_history'); + + if ~was_viol && ~was_timeout + %was_hit=rows(parsed_events.states.hit_state)>0; + was_hit=rows(parsed_events.states.second_hit_state)==0; + hit_history.value=[hit_history(:); was_hit]; + + else + % There was a violation or timeout + hit_history.value=[hit_history(:); nan]; + end + + % Now calculate the deltaF and sides - this maybe interesting + % even in a violation or timeout case. + + fn=fieldnames(parsed_events.states); + led_states=find(strncmp('led',fn,3)); + deltaF=0; + n_l=0; + n_r=0; + for lx=1:numel(led_states) + lind=led_states(lx); + if rows(parsed_events.states.(fn{lind}))>0 + if fn{lind}(end)=='l' + deltaF=deltaF-1; + n_l=n_l+1; + elseif fn{lind}(end)=='r' + deltaF=deltaF+1; + n_r=n_r+1; + elseif fn{lind}(end)=='b' + n_l=n_l+1; + n_r=n_r+1; + + end + end + + end + + % if deltaF>0 then a right poke is a hit + % if deltaF<0 then a left poke is a hit + + deltaf_history.value=[deltaf_history(:); deltaF]; + + end + + if antibias_LRprob ==1 + if n_done_trials >ntrial_correct_bias && ~was_viol && ~was_timeout + nonan_hit_history=value(hit_history); + nonan_hit_history(isnan(nonan_hit_history))=[]; + nonan_previous_sides=value(previous_sides); + nan_history=value(hit_history); + nonan_previous_sides(isnan(nan_history))=[]; + AntibiasSectionAthena(obj, 'update', value(LeftProb), nonan_hit_history(:)',nonan_previous_sides(:)); % <~> Transposed hit history so that it is the expected column vector. (Antibias errors out otherwise.) 2007.09.05 01:39 + end + + + if ~isinf(MaxSame) && length(previous_sides) > MaxSame && ... + all(previous_sides(n_done_trials-MaxSame+1:n_done_trials) == previous_sides(n_done_trials)) %#ok + if previous_sides(end)=='l' + ThisTrial.value = 'RIGHT'; + else + ThisTrial.value = 'LEFT'; + end + else + choiceprobs = AntibiasSectionAthena(obj, 'get_posterior_probs'); + if rand(1) <= choiceprobs(1) + ThisTrial.value = 'LEFT'; + else + ThisTrial.value = 'RIGHT'; + end + end + + else + if (rand(1)<=LeftProb) + ThisTrial.value='LEFT'; + + else + ThisTrial.value='RIGHT'; + end + + end + + + + +% %% Do the anti-bias with changing reward delivery +% % reset anti-bias +% left_wtr_mult.value=1; +% right_wtr_mult.value=1; +% if n_done_trials>ntrial_correct_bias && antibias_wtr_mult==1 +% hh=hit_history(n_done_trials-ntrial_correct_bias:n_done_trials); +% ps=previous_sides(n_done_trials-ntrial_correct_bias:n_done_trials); +% +% right_hit=nanmean(hh(ps=='r')); +% left_hit=nanmean(hh(ps=='l')); +% +% if abs(right_hit-left_hit) + + case 'get_left_prob' + x = value(LeftProb); + + case 'get_cp_history' + x = cell2mat(get_history(CP_duration)); + + case 'get_stimdur_history' + x = cell2mat(get_history(A1_time)); + + case 'update_side_history' + if strcmp(ThisTrial, 'LEFT') + ps=value(previous_sides); + ps(n_done_trials)='l'; + previous_sides.value=ps; + + else + ps=value(previous_sides); + ps(n_done_trials)='r'; + previous_sides.value=ps; + end + + case 'get_current_side' + if strcmp(ThisTrial, 'LEFT') + x = 'l'; %#ok + else + x = 'r'; + end + + + case 'close' + % Delete all SoloParamHandles who belong to this object and whose + % fullname starts with the name of this mfile: + delete_sphandle('owner', ['^@' class(obj) '$'], ... + 'fullname', ['^' mfilename]); + + case 'reinit' + currfig = double(gcf); + + % Get the original GUI position and figure: + x = my_gui_info(1); y = my_gui_info(2); figure(my_gui_info(3)); + + % Delete all SoloParamHandles who belong to this object and whose + % fullname starts with the name of this mfile: + delete_sphandle('owner', ['^@' class(obj) '$'], ... + 'fullname', ['^' mfilename]); + + % Reinitialise at the original GUI position and figure: + [x, y] = feval(mfilename, obj, 'init', x, y); + + % Restore the current figure: + figure(currfig); + +end + + diff --git a/Protocols/@Arpit_CentrePokeTraining/ParamsSection.m b/Protocols/@Arpit_CentrePokeTraining/ParamsSection.m index 7a57fe8a..94f5ec25 100644 --- a/Protocols/@Arpit_CentrePokeTraining/ParamsSection.m +++ b/Protocols/@Arpit_CentrePokeTraining/ParamsSection.m @@ -38,7 +38,7 @@ next_row(y, 1.5); next_column(x); y = 5; % Reward Collection - NumeditParam(obj, 'RewardCollection_duration', 10, x,y,'label','RewardCollection_duration','TooltipString','Wait until rat collects the reward else a timeout'); + NumeditParam(obj, 'RewardCollection_duration', 10, x,y,'label','RewardCollection_dur','TooltipString','Wait until rat collects the reward else a timeout'); next_row(y); NumeditParam(obj, 'SideLed_duration', 0.5, x,y,'label','Side LED duration','TooltipString','Duration of SideLed'); % Centre Poke @@ -47,23 +47,23 @@ next_row(y); NumeditParam(obj, 'SettlingIn_time', 0.2, x,y, 'position', [x, y, 175 20], 'TooltipString','Initial settling period during which there is no violation'); next_row(y); - NumeditParam(obj, 'A1_time_Min', 0.4, x,y,'label','Min time for AUD1','TooltipString','Min value to select the Duration of fixed stimulus'); + NumeditParam(obj, 'A1_time_Min', 0.4, x,y,'label','Min time AUD1','TooltipString','Min value to select the Duration of fixed stimulus'); set_callback(A1_time_Min, {mfilename, 'new_CP_duration'}); next_row(y); - NumeditParam(obj, 'A1_time', 0.4, x,y,'label','AUD1 on Time','TooltipString','Actual Duration of fixed stimulus'); + NumeditParam(obj, 'A1_time', 0.4, x,y,'label','AUD1 Time','TooltipString','Actual Duration of fixed stimulus'); next_row(y); set_callback(A1_time, {mfilename, 'new_CP_duration'}); next_row(y); - NumeditParam(obj, 'A1_time_Max', 0.4, x,y,'label','Max time for AUD1','TooltipString','Max value to select the Duration of fixed stimulus'); + NumeditParam(obj, 'A1_time_Max', 0.4, x,y,'label','Max time AUD1','TooltipString','Max value to select the Duration of fixed stimulus'); set_callback(A1_time_Max, {mfilename, 'new_CP_duration'}); next_row(y); - NumeditParam(obj, 'PreStim_time_Min', 0.20, x,y,'label','Min time for Pre-Stim','TooltipString','Min Time in NIC before starting the stimulus'); + NumeditParam(obj, 'PreStim_time_Min', 0.20, x,y,'label','Pre-Stim Min','TooltipString','Min Time in NIC before starting the stimulus'); set_callback(PreStim_time_Min, {mfilename, 'new_CP_duration'}); next_row(y); - NumeditParam(obj, 'PreStim_time', 0.20, x,y,'label','Pre-Stim NIC time','TooltipString','Actual Time in NIC before starting the stimulus'); + NumeditParam(obj, 'PreStim_time', 0.20, x,y,'label','Pre-Stim time','TooltipString','Actual Time in NIC before starting the stimulus'); set_callback(PreStim_time, {mfilename, 'new_CP_duration'}); next_row(y); - NumeditParam(obj, 'PreStim_time_Max', 0.40, x,y,'label','Max time for Pre-Stim','TooltipString','Max Time in NIC before starting the stimulus'); + NumeditParam(obj, 'PreStim_time_Max', 0.40, x,y,'label','Pre-Stim Max','TooltipString','Max Time in NIC before starting the stimulus'); set_callback(PreStim_time_Max, {mfilename, 'new_CP_duration'}); next_row(y); NumeditParam(obj, 'time_bet_aud1_gocue_Min', 0.2, x,y,'label','Min A1-GoCue time','TooltipString','Min time between the end of the stimulus and the go cue '); @@ -97,7 +97,7 @@ next_row(y); NumeditParam(obj, 'drink_time', 1, x,y,'label','Drink Time','TooltipString','waits to finish water delivery'); next_row(y); - NumeditParam(obj, 'timeout_iti', 5, x,y,'label','Error Timeout','TooltipString','ITI on timeout trials'); + NumeditParam(obj, 'timeout_iti', 5, x,y,'label','No Choice Timeout','TooltipString','ITI on timeout trials'); next_row(y); NumeditParam(obj, 'violation_iti', 1, x,y,'label','Violation Timeout','TooltipString','Center poke violation duration'); @@ -125,9 +125,11 @@ NumeditParam(obj,'trials_in_stage',1,x,y,'label','Trial Counter'); next_row(y); NumeditParam(obj,'training_stage',1,x,y,'label','Training Stage'); + set_callback(training_stage, {mfilename, 'Training_Stage'}); next_row(y); ToggleParam(obj,'use_training',0,x,y,'OnString','Using Autotrain','OffString','Manual Settings'); - + set_callback(training_stage, {mfilename, 'Training_Stage'}); + next_row(y); NumeditParam(obj, 'ntrial_correct_bias', 0, x, y, ... 'TooltipString', 'antibias starts from trial=ntrial_correct_bias'); @@ -156,9 +158,9 @@ 'RewardCollection_duration';'training_stage'; ... 'legal_cbreak' ; 'SettlingIn_time'; 'time_go_cue'; ... 'A1_time';'time_bet_aud1_gocue' ; 'PreStim_time'; - 'drink_time';'reward_delay';'reward_duration';'left_wtr_mult';... + 'drink_time';'reward_delay';'left_wtr_mult';... 'right_wtr_mult';'antibias_wtr_mult';... - 'secondhit_delay';'timeout_iti';'violation_iti'}); + 'timeout_iti';'violation_iti'}); % SoloFunctionAddVars('StimulatorSection', 'ro_args', ... % {'A1_time';'time_bet_aud1_gocue';'time_go_cue'; ... @@ -179,19 +181,19 @@ case 'new_CP_duration' - if random_PreStim_time == 1 && (PreStim_time.value < value(PreStim_time_Min) || PreStim_time.value > value(PreStim_time_Max)) + if random_PreStim_time == 1 && (value(PreStim_time) < value(PreStim_time_Min) || value(PreStim_time) > value(PreStim_time_Max)) PreStim_time.value = value(PreStim_time_Min); else PreStim_time.value = value(PreStim_time); end - if random_A1_time == 1 && (A1_time.value < value(A1_time_Min) || A1_time.value > value(A1_time_Max)) + if random_A1_time == 1 && (value(A1_time) < value(A1_time_Min) || value(A1_time) > value(A1_time_Max)) A1_time.value = value(A1_time_Min); else A1_time.value = value(A1_time); end - if random_time_bet_aud1_gocue == 1 && (time_bet_aud1_gocue.value < value(time_bet_aud1_gocue_Min) || time_bet_aud1_gocue.value > value(time_bet_aud1_gocue_Max)) + if random_prego_time == 1 && (value(time_bet_aud1_gocue) < value(time_bet_aud1_gocue_Min) || value(time_bet_aud1_gocue) > value(time_bet_aud1_gocue_Max)) time_bet_aud1_gocue.value = value(time_bet_aud1_gocue_Min); else time_bet_aud1_gocue.value = value(time_bet_aud1_gocue); @@ -204,7 +206,72 @@ Total_CP_duration.value = CP_duration + time_go_cue; SoundInterface(obj, 'set', 'GoSound', 'Dur1', value(time_go_cue)); - + case 'training_stage' + + if value(use_training) == 1 + + disable(training_stage); % user cannot change the training stages + else + + enable(training_stage); % user can change the training stages + delay_time.value=0; + trials_in_stage.value=0; + reward_delay.value=0.01; + left_prob.value=0.5; + right_prob.value=0.5; + + switch value(training_stage) + + case {1,2} %% learning the reward sound association -left or right led on -> poke -> sound+reward + + time_go_cue.value=0.200; + disable(SettlingIn_time); + disable(PreStim_time); + disable(A1_time); + disable(time_bet_aud1_gocue); + + + case {3,4} % Centre poke without the A1_Stim but has violation in 4 + enable(SettlingIn_time); + disable(PreStim_time); + disable(A1_time); + disable(time_bet_aud1_gocue); + + if n_done_trials <1 && warmup_on ==1 + CP_duration.value=value(init_CP_duration); + else + CP_duration.value=CP_duration; + end + Total_CP_duration.value = CP_duration + time_go_cue; %#ok<*NASGU> + + case {5,6,7} % + + enable(SettlingIn_time); + enable(PreStim_time); + enable(A1_time); + enable(time_bet_aud1_gocue); + + if random_prego_time == 1 + time_bet_aud1_gocue = randi([time_bet_aud1_gocue_Min, time_bet_aud1_gocue_Max],1,1); + end + + if random_A1_time == 1 + A1_time = randi([A1_time_Min, A1_time_Max],1,1); + end + + if random_PreStim_time == 1 + PreStim_time = randi([PreStim_time_Min, PreStim_time_Max],1,1); + end + + if n_done_trials <1 && warmup_on ==1 + CP_duration.value=value(init_CP_duration); + else + CP_duration.value=SettlingIn_time + A1_time + PreStim_time + time_bet_aud1_gocue; + end + Total_CP_duration.value = CP_duration + time_go_cue; %#ok<*NASGU> + end + end + case 'prepare_next_trial' delay_time.value=0; @@ -218,14 +285,14 @@ case {1,2} %% learning the reward sound association -left or right led on -> poke -> sound+reward time_go_cue.value=0.200; - disable(settling_time); + disable(SettlingIn_time); disable(PreStim_time); disable(A1_time); disable(time_bet_aud1_gocue); case {3,4} % Centre poke without the A1_Stim but has violation in 4 - enable(settling_time); + enable(SettlingIn_time); disable(PreStim_time); disable(A1_time); disable(time_bet_aud1_gocue); @@ -239,7 +306,7 @@ case {5,6,7} % - enable(settling_time); + enable(SettlingIn_time); enable(PreStim_time); enable(A1_time); enable(time_bet_aud1_gocue); @@ -259,7 +326,7 @@ if n_done_trials <1 && warmup_on ==1 CP_duration.value=value(init_CP_duration); else - CP_duration.value=settling_time + A1_time + PreStim_time + time_bet_aud1_gocue; + CP_duration.value=SettlingIn_time + A1_time + PreStim_time + time_bet_aud1_gocue; end Total_CP_duration.value = CP_duration + time_go_cue; %#ok<*NASGU> From 96a04ad66322daf9ecf36f421d74956f73e46196 Mon Sep 17 00:00:00 2001 From: ArpitAgarwal Date: Wed, 19 Mar 2025 00:08:44 +0000 Subject: [PATCH 022/164] removed unused m files and updated the Arpit_CentrePokeTraining protocol: 1 step for cenre poke --- .../Arpit_CentrePokeTraining.m | 50 +- .../ParamsSection.asv | 532 ------------------ .../@Arpit_CentrePokeTraining/ParamsSection.m | 26 +- .../@Arpit_CentrePokeTraining/PlayStimuli.m | 87 --- .../StimulatorSection.m | 325 ----------- 5 files changed, 34 insertions(+), 986 deletions(-) delete mode 100644 Protocols/@Arpit_CentrePokeTraining/ParamsSection.asv delete mode 100644 Protocols/@Arpit_CentrePokeTraining/PlayStimuli.m delete mode 100644 Protocols/@Arpit_CentrePokeTraining/StimulatorSection.m diff --git a/Protocols/@Arpit_CentrePokeTraining/Arpit_CentrePokeTraining.m b/Protocols/@Arpit_CentrePokeTraining/Arpit_CentrePokeTraining.m index a582172e..e27c265c 100644 --- a/Protocols/@Arpit_CentrePokeTraining/Arpit_CentrePokeTraining.m +++ b/Protocols/@Arpit_CentrePokeTraining/Arpit_CentrePokeTraining.m @@ -6,11 +6,11 @@ % Default object is of our own class (mfilename); % we inherit only from Plugins -obj = class(struct, mfilename, pokesplot2, saveload, sessionmodel, soundmanager, soundui, antibias, ... +obj = class(struct, mfilename, pokesplot2, saveload, sessionmodel2, soundmanager, soundui, antibias, ... water, distribui, punishui, comments, soundtable, sqlsummary,reinforcement,softpokestay2); %--------------------------------------------------------------- -% BEGIN SECTION COMMON TO ALL PROTOCOLS, DO NOT MODIFY +% BEGIN SECTION COMMONSoundTableSection TO ALL PROTOCOLS, DO NOT MODIFY %--------------------------------------------------------------- % If creating an empty object, return without further ado: @@ -21,10 +21,11 @@ if isa(varargin{1}, mfilename) % If first arg is an object of this class itself, we are % Most likely responding to a callback from % a SoloParamHandle defined in this mfile. - if length(varargin) < 2 || ~ischar(varargin{2}), + if length(varargin) < 2 || ~ischar(varargin{2}) error(['If called with a "%s" object as first arg, a second arg, a ' ... 'string specifying the action, is required\n']); - else action = varargin{2}; varargin = varargin(3:end); %#ok + else + action = varargin{2}; varargin = varargin(3:end); %#ok end else % Ok, regular call with first param being the action string. action = varargin{1}; varargin = varargin(2:end); %#ok @@ -59,11 +60,6 @@ DeclareGlobals(obj, 'ro_args', {'hit_history'}); SoloFunctionAddVars('ParamsSection', 'rw_args', 'hit_history'); - %pair_history changed to stimulus_history (from AthenaDelayComp) - % SoloParamHandle(obj, 'stimulus_history', 'value', []); - % DeclareGlobals(obj, 'ro_args', {'stimulus_history'}); - % SoloFunctionAddVars('StimulusSection', 'rw_args', 'stimulus_history'); - SoloParamHandle(obj, 'violation_history', 'value', []); DeclareGlobals(obj, 'ro_args', {'violation_history'}); SoloFunctionAddVars('ParamsSection', 'rw_args', 'violation_history'); @@ -118,27 +114,27 @@ struct('states', my_state_colors, 'pokes', my_poke_colors)); next_row(y); [x, y] = CommentsSection(obj, 'init', x, y); - SessionDefinition(obj, 'init', x, y, value(myfig)); next_row(y, 2); %#ok - SessionDefinition(obj, 'set_old_style_parsing_flag',0); + % [x, y] = PunishmentSection(obj, 'init', x, y); %#ok next_column(x); y=5; [x, y] = OverallPerformanceSection(obj, 'init', x, y); - % [x, y] = StimulatorSection(obj, 'init', x, y); next_row(y, 1.3); [x, y] = ParamsSection(obj, 'init', x, y); %#ok [x, y] = SoundSection(obj,'init',x,y); -% [x, y] = PlayStimuli(obj,'init',x,y); - % [x, y] = StimulusSection(obj,'init',x,y); [x, y] = SoftPokeStayInterface(obj, 'add', 'soft_cp', x, y); + figpos = get(double(gcf), 'Position'); [expmtr, rname]=SavingSection(obj, 'get_info'); HeaderParam(obj, 'prot_title', [mfilename ': ' expmtr ', ' rname], x, y, 'position', [10 figpos(4)-25, 800 20]); Arpit_CentrePokeTrainingSMA(obj, 'init'); + SessionDefinition(obj, 'init', x, y, value(myfig)); next_row(y, 2); %#ok + SessionDefinition(obj, 'set_old_style_parsing_flag',0); + feval(mfilename, obj, 'prepare_next_trial'); - %% change_water_modulation_params + %% change_water_modulation_params case 'change_water_modulation_params' display_guys = [1 150 300]; for i=1:numel(display_guys) @@ -148,19 +144,16 @@ myvar.value = maxasymp + (minasymp/(1+(t/inflp)^slp).^assym); end - %% prepare next trial + %% prepare next trial case 'prepare_next_trial' ParamsSection(obj, 'prepare_next_trial'); % Run SessionDefinition *after* ParamsSection so we know whether the % trial was a violation or not SessionDefinition(obj, 'next_trial'); - % StimulatorSection(obj, 'update_values'); OverallPerformanceSection(obj, 'evaluate'); - % StimulusSection(obj,'prepare_next_trial'); SoundManagerSection(obj, 'send_not_yet_uploaded_sounds'); - nTrials.value = n_done_trials; [sma, prepare_next_trial_states] = Arpit_CentrePokeTrainingSMA(obj, 'prepare_next_trial'); @@ -179,24 +172,25 @@ prot_title.value=[mfilename ' on rig ' get_hostname ' : ' expmtr ', ' rname '. Started at ' datestr(now, 'HH:MM')]; end - try send_n_done_trials(obj); end + try send_n_done_trials(obj); + end - %% trial_completed + %% trial_completed case 'trial_completed' % Do any updates in the protocol that need doing: feval(mfilename, 'update'); % And PokesPlot needs completing the trial: PokesPlotSection(obj, 'trial_completed'); - %% update + + %% update case 'update' PokesPlotSection(obj, 'update'); - %% close + %% close case 'close' PokesPlotSection(obj, 'close'); - %PunishmentSection(obj, 'close'); ParamsSection(obj, 'close'); StimulusSection(obj,'close'); @@ -214,19 +208,11 @@ %% pre_saving_settings case 'pre_saving_settings' - StimulusSection(obj,'hide'); SessionDefinition(obj, 'run_eod_logic_without_saving'); perf = OverallPerformanceSection(obj, 'evaluate'); cp_durs = ParamsSection(obj, 'get_cp_history'); -% [classperf tot_perf]= StimulusSection(obj, 'get_class_perform'); - - %% - % YOU NEED TO CHANGE THIS -% [stimuli] = StimulusSection(obj,'get_stimuli'); - %% [stim1dur] = ParamsSection(obj,'get_stimdur_history'); - %stim_history = StimulatorSection(obj,'get_history'); % CommentsSection(obj, 'append_line', ... % sprintf(['ntrials = %d, violations = %.2f, timeouts=%.2f, hits = %.2f\n', ... diff --git a/Protocols/@Arpit_CentrePokeTraining/ParamsSection.asv b/Protocols/@Arpit_CentrePokeTraining/ParamsSection.asv deleted file mode 100644 index 6904cef2..00000000 --- a/Protocols/@Arpit_CentrePokeTraining/ParamsSection.asv +++ /dev/null @@ -1,532 +0,0 @@ -function [x, y] = ParamsSection(obj, action, x,y) - -GetSoloFunctionArgs(obj); - -switch action - - % ------------------------------------------------------------------ - % INIT - % ------------------------------------------------------------------ - - case 'init' - - SoloParamHandle(obj, 'my_gui_info', 'value', [x y double(gcf)], 'saveable', 0); - y0 = y; - - [x, y] = AntibiasSectionAthena(obj, 'init', x, y); - - - ToggleParam(obj, 'antibias_LRprob', 0, x,y,... - 'OnString', 'AB_Prob ON',... - 'OffString', 'AB_Prob OFF',... - 'TooltipString', sprintf(['If on (Yellow) then it enables the AntiBias algorithm\n'... - 'based on changing the probablity of Left vs Right'])); - - next_row(y); - NumeditParam(obj, 'LeftProb', 0.5, x, y); next_row(y); - set_callback(LeftProb, {mfilename, 'new_leftprob'}); - MenuParam(obj, 'MaxSame', {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, Inf}, Inf, x, y, ... - 'TooltipString', sprintf(['\nMaximum number of consecutive trials where correct\n' ... - 'response is on the same side. Overrides antibias. Thus, for\n' ... - 'example, if MaxSame=5 and there have been 5 Left trials, the\n' ... - 'next trial is guaranteed to be Right'])); next_row(y); - - DispParam(obj, 'ThisTrial', 'LEFT', x, y); next_row(y); - SoloParamHandle(obj, 'previous_sides', 'value', []); - DeclareGlobals(obj, 'ro_args', 'previous_sides'); - SubheaderParam(obj, 'title', 'Sides Section', x, y); - next_row(y, 1.5); - next_column(x); y = 5; - % Reward Collection - NumeditParam(obj, 'RewardCollection_duration', 10, x,y,'label','RewardCollection_dur','TooltipString','Wait until rat collects the reward else a timeout'); - next_row(y); - NumeditParam(obj, 'SideLed_duration', 0.5, x,y,'label','Side LED duration','TooltipString','Duration of SideLed'); - % Centre Poke - next_row(y); - NumeditParam(obj, 'legal_cbreak', 0.1, x,y, 'position', [x, y, 175 20], 'TooltipString','Time in sec for which it is ok to be outside the center port before a violation occurs.'); - next_row(y); - NumeditParam(obj, 'SettlingIn_time', 0.2, x,y, 'position', [x, y, 175 20], 'TooltipString','Initial settling period during which there is no violation'); - next_row(y); - NumeditParam(obj, 'A1_time_Min', 0.4, x,y,'label','Min time AUD1','TooltipString','Min value to select the Duration of fixed stimulus'); - set_callback(A1_time_Min, {mfilename, 'new_CP_duration'}); - next_row(y); - NumeditParam(obj, 'A1_time', 0.4, x,y,'label','AUD1 Time','TooltipString','Actual Duration of fixed stimulus'); - next_row(y); - set_callback(A1_time, {mfilename, 'new_CP_duration'}); - next_row(y); - NumeditParam(obj, 'A1_time_Max', 0.4, x,y,'label','Max time AUD1','TooltipString','Max value to select the Duration of fixed stimulus'); - set_callback(A1_time_Max, {mfilename, 'new_CP_duration'}); - next_row(y); - NumeditParam(obj, 'PreStim_time_Min', 0.20, x,y,'label','Pre-Stim Min','TooltipString','Min Time in NIC before starting the stimulus'); - set_callback(PreStim_time_Min, {mfilename, 'new_CP_duration'}); - next_row(y); - NumeditParam(obj, 'PreStim_time', 0.20, x,y,'label','Pre-Stim time','TooltipString','Actual Time in NIC before starting the stimulus'); - set_callback(PreStim_time, {mfilename, 'new_CP_duration'}); - next_row(y); - NumeditParam(obj, 'PreStim_time_Max', 0.40, x,y,'label','Pre-Stim Max','TooltipString','Max Time in NIC before starting the stimulus'); - set_callback(PreStim_time_Max, {mfilename, 'new_CP_duration'}); - next_row(y); - NumeditParam(obj, 'time_bet_aud1_gocue_Min', 0.2, x,y,'label','Min A1-GoCue time','TooltipString','Min time between the end of the stimulus and the go cue '); - set_callback(time_bet_aud1_gocue_Min, {mfilename, 'new_CP_duration'}); - next_row(y); - NumeditParam(obj, 'time_bet_aud1_gocue', 0.2, x,y,'label','A1-GoCue time','TooltipString','Actual time between the end of the stimulus and the go cue '); - next_row(y); - set_callback(time_bet_aud1_gocue, {mfilename, 'new_CP_duration'}); - next_row(y); - NumeditParam(obj, 'time_bet_aud1_gocue_Max', 2, x,y,'label','Max A1-GoCue time','TooltipString','Max time between the end of the stimulus and the go cue '); - set_callback(time_bet_aud1_gocue_Max, {mfilename, 'new_CP_duration'}); - next_row(y); - DispParam(obj, 'init_CP_duration', 0.01, x,y,'label','init_CP duration','TooltipString','Duration of Nose in Central Poke before Go cue starts (see Total_CP_duration)'); - next_row(y); - DispParam(obj, 'CP_duration', PreStim_time+A1_time+time_bet_aud1_gocue, x,y,'label','CP duration','TooltipString','Duration of Nose in Central Poke before Go cue starts (see Total_CP_duration)'); - set_callback(CP_duration, {mfilename, 'new_CP_duration'}); - next_row(y); - NumeditParam(obj, 'time_go_cue' ,0.1, x,y,'label','Go Cue Duration','TooltipString','duration of go cue (see Total_CP_duration)'); - set_callback(time_go_cue, {mfilename, 'new_time_go_cue'}); - next_row(y); - DispParam(obj, 'Total_CP_duration', CP_duration+time_go_cue, x, y, 'TooltipString', 'Total expected(rat can poke out anytime after Go cue onset) nose in center port time, in secs. Sum of CP_duration and Go Cue duration'); %#ok<*NODEF> - next_row(y); - ToggleParam(obj, 'warmup_on', 1, x,y,... - 'OnString', 'Warmup ON',... - 'OffString', 'Warmup OFF',... - 'TooltipString', sprintf(['If on (Yellow) then it applies the initial warming up phase, during which the\n',... - 'CP_duration starts small and gradually grows to last_session_max_cp_duration'])); - next_row(y); - - NumeditParam(obj, 'reward_delay', 0.01, x,y,'label','Reward Delay','TooltipString','Delay between side poke and reward delivery'); - next_row(y); - NumeditParam(obj, 'drink_time', 1, x,y,'label','Drink Time','TooltipString','waits to finish water delivery'); - next_row(y); - NumeditParam(obj, 'timeout_iti', 5, x,y,'label','No Choice Timeout','TooltipString','ITI on timeout trials'); - next_row(y); - NumeditParam(obj, 'violation_iti', 1, x,y,'label','Violation Timeout','TooltipString','Center poke violation duration'); - - next_row(y); - ToggleParam(obj, 'random_PreStim_time', 0, x,y,... - 'OnString', 'random PreStim_time ON',... - 'OffString', 'random PreStim_time OFF',... - 'TooltipString', sprintf('If on (black) then it enables the random time between the user given range')); - set_callback(random_PreStim_time, {mfilename, 'new_CP_duration'}); - next_row(y); - ToggleParam(obj, 'random_A1_time', 0, x,y,... - 'OnString', 'random A1_time ON',... - 'OffString', 'random A1_time OFF',... - 'TooltipString', sprintf('If on (black) then it enables the random sampling of A1_time')); - set_callback(random_A1_time, {mfilename, 'new_CP_duration'}); - next_row(y); - ToggleParam(obj, 'random_prego_time', 0, x,y,... - 'OnString', 'random prego_time ON',... - 'OffString', 'random prego_time OFF',... - 'TooltipString', sprintf('If on (black) then it enables the random sampling of time between the end of the stimulus and the go cue')); - set_callback(random_prego_time, {mfilename, 'new_CP_duration'}); - - next_column(x); - y=5; - NumeditParam(obj,'trials_in_stage',1,x,y,'label','Trial Counter'); - next_row(y); - NumeditParam(obj,'training_stage',1,x,y,'label','Training Stage'); - set_callback(training_stage, {mfilename, 'Training_Stage'}); - next_row(y); - ToggleParam(obj,'use_training',0,x,y,'OnString','Using Autotrain','OffString','Manual Settings'); - set_callback(training_stage, {mfilename, 'Training_Stage'}); - - next_row(y); - NumeditParam(obj, 'ntrial_correct_bias', 0, x, y, ... - 'TooltipString', 'antibias starts from trial=ntrial_correct_bias'); - next_row(y); - NumeditParam(obj, 'right_left_diff', .12, x, y, ... - 'TooltipString', 'antibias applies if difference between right and left sides is bigger than this number'); - next_row(y); - NumeditParam(obj, 'max_wtr_mult', 4, x, y, ... - 'TooltipString', 'wtr_mult will be min(max_wtr_mult,right_hit/left_hit)'); - next_row(y); - NumeditParam(obj, 'left_wtr_mult', 1, x, y, ... - 'TooltipString', 'all left reward times are multiplied by this number'); - next_row(y); - NumeditParam(obj, 'right_wtr_mult', 1, x, y, ... - 'TooltipString', 'all right reward times are multiplied by this number'); - next_row(y); - ToggleParam(obj, 'antibias_wtr_mult', 0, x,y,... - 'OnString', 'AB ON',... - 'OffString', 'AB OFF',... - 'TooltipString', sprintf(['If on (black) then it disables the wtr_mult entries\n'... - 'and uses hitfrac to adjust the water times'])); - - next_row(y); - SoloFunctionAddVars('Arpit_CentrePokeTrainingSMA', 'ro_args', ... - {'CP_duration';'SideLed_duration'; ... - 'RewardCollection_duration';'training_stage'; ... - 'legal_cbreak' ; 'SettlingIn_time'; 'time_go_cue'; ... - 'A1_time';'time_bet_aud1_gocue' ; 'PreStim_time'; - 'drink_time';'reward_delay';'left_wtr_mult';... - 'right_wtr_mult';'antibias_wtr_mult';... - 'timeout_iti';'violation_iti'}); - - % SoloFunctionAddVars('StimulatorSection', 'ro_args', ... - % {'A1_time';'time_bet_aud1_gocue';'time_go_cue'; ... - % 'PreStim_time';'CP_duration';'Total_CP_duration'}); - - % History of hit/miss: - SoloParamHandle(obj, 'deltaf_history', 'value', []); - - SoloFunctionAddVars('OverallPerformanceSection', 'ro_args', ... - {'training_stage'}); - - - SoloParamHandle(obj, 'previous_parameters', 'value', []); - - case 'new_leftprob' - AntibiasSectionAthena(obj, 'update_biashitfrac', value(LeftProb)); - - - case 'new_CP_duration' - - if random_PreStim_time == 1 && (value(PreStim_time) < value(PreStim_time_Min) || value(PreStim_time) > value(PreStim_time_Max)) - PreStim_time.value = value(PreStim_time_Min); - else - PreStim_time.value = value(PreStim_time); - end - - if random_A1_time == 1 && (value(A1_time) < value(A1_time_Min) || value(A1_time) > value(A1_time_Max)) - A1_time.value = value(A1_time_Min); - else - A1_time.value = value(A1_time); - end - - if random_prego_time == 1 && (value(time_bet_aud1_gocue) < value(time_bet_aud1_gocue_Min) || value(time_bet_aud1_gocue) > value(time_bet_aud1_gocue_Max)) - time_bet_aud1_gocue.value = value(time_bet_aud1_gocue_Min); - else - time_bet_aud1_gocue.value = value(time_bet_aud1_gocue); - end - - CP_duration.value= SettlingIn_time + PreStim_time + A1_time + time_bet_aud1_gocue; - Total_CP_duration.value = CP_duration + time_go_cue; %#ok<*NASGU> - - case 'new_time_go_cue' - Total_CP_duration.value = CP_duration + time_go_cue; - SoundInterface(obj, 'set', 'GoSound', 'Dur1', value(time_go_cue)); - - case 'training_stage' - - delay_time.value=0; - trials_in_stage.value=0; - reward_delay.value=0.01; - left_prob.value=0.5; - right_prob.value=0.5; - - switch value(training_stage) - - case {1,2} %% learning the reward sound association -left or right led on -> poke -> sound+reward - - time_go_cue.value=0.200; - disable(SettlingIn_time); - disable(PreStim_time); - disable(A1_time); - disable(time_bet_aud1_gocue); - - - case {3,4} % Centre poke without the A1_Stim but has violation in 4 - enable(SettlingIn_time); - disable(PreStim_time); - disable(A1_time); - disable(time_bet_aud1_gocue); - - if n_done_trials <1 && warmup_on ==1 - CP_duration.value=value(init_CP_duration); - else - CP_duration.value=CP_duration; - end - Total_CP_duration.value = CP_duration + time_go_cue; %#ok<*NASGU> - - case {5,6,7} % - - enable(SettlingIn_time); - enable(PreStim_time); - enable(A1_time); - enable(time_bet_aud1_gocue); - - if random_prego_time == 1 - time_bet_aud1_gocue = randi([time_bet_aud1_gocue_Min, time_bet_aud1_gocue_Max],1,1); - end - - if random_A1_time == 1 - A1_time = randi([A1_time_Min, A1_time_Max],1,1); - end - - if random_PreStim_time == 1 - PreStim_time = randi([PreStim_time_Min, PreStim_time_Max],1,1); - end - - if n_done_trials <1 && warmup_on ==1 - CP_duration.value=value(init_CP_duration); - else - CP_duration.value=SettlingIn_time + A1_time + PreStim_time + time_bet_aud1_gocue; - end - Total_CP_duration.value = CP_duration + time_go_cue; %#ok<*NASGU> - - - end - - - case 'prepare_next_trial' - - delay_time.value=0; - trials_in_stage.value=0; - reward_delay.value=0.01; - left_prob.value=0.5; - right_prob.value=0.5; - - switch value(training_stage) - - case {1,2} %% learning the reward sound association -left or right led on -> poke -> sound+reward - - time_go_cue.value=0.200; - disable(SettlingIn_time); - disable(PreStim_time); - disable(A1_time); - disable(time_bet_aud1_gocue); - - - case {3,4} % Centre poke without the A1_Stim but has violation in 4 - enable(SettlingIn_time); - disable(PreStim_time); - disable(A1_time); - disable(time_bet_aud1_gocue); - - if n_done_trials <1 && warmup_on ==1 - CP_duration.value=value(init_CP_duration); - else - CP_duration.value=CP_duration; - end - Total_CP_duration.value = CP_duration + time_go_cue; %#ok<*NASGU> - - case {5,6,7} % - - enable(SettlingIn_time); - enable(PreStim_time); - enable(A1_time); - enable(time_bet_aud1_gocue); - - if random_prego_time == 1 - time_bet_aud1_gocue = randi([time_bet_aud1_gocue_Min, time_bet_aud1_gocue_Max],1,1); - end - - if random_A1_time == 1 - A1_time = randi([A1_time_Min, A1_time_Max],1,1); - end - - if random_PreStim_time == 1 - PreStim_time = randi([PreStim_time_Min, PreStim_time_Max],1,1); - end - - if n_done_trials <1 && warmup_on ==1 - CP_duration.value=value(init_CP_duration); - else - CP_duration.value=SettlingIn_time + A1_time + PreStim_time + time_bet_aud1_gocue; - end - Total_CP_duration.value = CP_duration + time_go_cue; %#ok<*NASGU> - - - end - - - %% update hit_history, previous_sides, etc - was_viol=false; - was_hit=false; - was_timeout=false; - if n_done_trials>0 - if ~isempty(parsed_events) - if isfield(parsed_events,'states') - if isfield(parsed_events.states,'timeout_state') - was_timeout=rows(parsed_events.states.timeout_state)>0; - end - if isfield(parsed_events.states,'violation_state') - was_viol=rows(parsed_events.states.violation_state)>0; - end - end - - end - - violation_history.value=[violation_history(:); was_viol]; - timeout_history.value=[timeout_history(:); was_timeout]; - - ParamsSection(obj,'update_side_history'); - - if ~was_viol && ~was_timeout - %was_hit=rows(parsed_events.states.hit_state)>0; - was_hit=rows(parsed_events.states.second_hit_state)==0; - hit_history.value=[hit_history(:); was_hit]; - - else - % There was a violation or timeout - hit_history.value=[hit_history(:); nan]; - end - - % Now calculate the deltaF and sides - this maybe interesting - % even in a violation or timeout case. - - fn=fieldnames(parsed_events.states); - led_states=find(strncmp('led',fn,3)); - deltaF=0; - n_l=0; - n_r=0; - for lx=1:numel(led_states) - lind=led_states(lx); - if rows(parsed_events.states.(fn{lind}))>0 - if fn{lind}(end)=='l' - deltaF=deltaF-1; - n_l=n_l+1; - elseif fn{lind}(end)=='r' - deltaF=deltaF+1; - n_r=n_r+1; - elseif fn{lind}(end)=='b' - n_l=n_l+1; - n_r=n_r+1; - - end - end - - end - - % if deltaF>0 then a right poke is a hit - % if deltaF<0 then a left poke is a hit - - deltaf_history.value=[deltaf_history(:); deltaF]; - - end - - if antibias_LRprob ==1 - if n_done_trials >ntrial_correct_bias && ~was_viol && ~was_timeout - nonan_hit_history=value(hit_history); - nonan_hit_history(isnan(nonan_hit_history))=[]; - nonan_previous_sides=value(previous_sides); - nan_history=value(hit_history); - nonan_previous_sides(isnan(nan_history))=[]; - AntibiasSectionAthena(obj, 'update', value(LeftProb), nonan_hit_history(:)',nonan_previous_sides(:)); % <~> Transposed hit history so that it is the expected column vector. (Antibias errors out otherwise.) 2007.09.05 01:39 - end - - - if ~isinf(MaxSame) && length(previous_sides) > MaxSame && ... - all(previous_sides(n_done_trials-MaxSame+1:n_done_trials) == previous_sides(n_done_trials)) %#ok - if previous_sides(end)=='l' - ThisTrial.value = 'RIGHT'; - else - ThisTrial.value = 'LEFT'; - end - else - choiceprobs = AntibiasSectionAthena(obj, 'get_posterior_probs'); - if rand(1) <= choiceprobs(1) - ThisTrial.value = 'LEFT'; - else - ThisTrial.value = 'RIGHT'; - end - end - - else - if (rand(1)<=LeftProb) - ThisTrial.value='LEFT'; - - else - ThisTrial.value='RIGHT'; - end - - end - - - - -% %% Do the anti-bias with changing reward delivery -% % reset anti-bias -% left_wtr_mult.value=1; -% right_wtr_mult.value=1; -% if n_done_trials>ntrial_correct_bias && antibias_wtr_mult==1 -% hh=hit_history(n_done_trials-ntrial_correct_bias:n_done_trials); -% ps=previous_sides(n_done_trials-ntrial_correct_bias:n_done_trials); -% -% right_hit=nanmean(hh(ps=='r')); -% left_hit=nanmean(hh(ps=='l')); -% -% if abs(right_hit-left_hit) - - case 'get_left_prob' - x = value(LeftProb); - - case 'get_cp_history' - x = cell2mat(get_history(CP_duration)); - - case 'get_stimdur_history' - x = cell2mat(get_history(A1_time)); - - case 'update_side_history' - if strcmp(ThisTrial, 'LEFT') - ps=value(previous_sides); - ps(n_done_trials)='l'; - previous_sides.value=ps; - - else - ps=value(previous_sides); - ps(n_done_trials)='r'; - previous_sides.value=ps; - end - - case 'get_current_side' - if strcmp(ThisTrial, 'LEFT') - x = 'l'; %#ok - else - x = 'r'; - end - - - case 'close' - % Delete all SoloParamHandles who belong to this object and whose - % fullname starts with the name of this mfile: - delete_sphandle('owner', ['^@' class(obj) '$'], ... - 'fullname', ['^' mfilename]); - - case 'reinit' - currfig = double(gcf); - - % Get the original GUI position and figure: - x = my_gui_info(1); y = my_gui_info(2); figure(my_gui_info(3)); - - % Delete all SoloParamHandles who belong to this object and whose - % fullname starts with the name of this mfile: - delete_sphandle('owner', ['^@' class(obj) '$'], ... - 'fullname', ['^' mfilename]); - - % Reinitialise at the original GUI position and figure: - [x, y] = feval(mfilename, obj, 'init', x, y); - - % Restore the current figure: - figure(currfig); - -end - - diff --git a/Protocols/@Arpit_CentrePokeTraining/ParamsSection.m b/Protocols/@Arpit_CentrePokeTraining/ParamsSection.m index 94f5ec25..94b12ce7 100644 --- a/Protocols/@Arpit_CentrePokeTraining/ParamsSection.m +++ b/Protocols/@Arpit_CentrePokeTraining/ParamsSection.m @@ -34,7 +34,7 @@ DispParam(obj, 'ThisTrial', 'LEFT', x, y); next_row(y); SoloParamHandle(obj, 'previous_sides', 'value', []); DeclareGlobals(obj, 'ro_args', 'previous_sides'); - SubheaderParam(obj, 'title', 'Sides Section', x, y); + SubheaderParam(obj, 'title', 'Params Section', x, y); next_row(y, 1.5); next_column(x); y = 5; % Reward Collection @@ -128,7 +128,7 @@ set_callback(training_stage, {mfilename, 'Training_Stage'}); next_row(y); ToggleParam(obj,'use_training',0,x,y,'OnString','Using Autotrain','OffString','Manual Settings'); - set_callback(training_stage, {mfilename, 'Training_Stage'}); + set_callback(use_training, {mfilename, 'Training_Stage'}); next_row(y); NumeditParam(obj, 'ntrial_correct_bias', 0, x, y, ... @@ -147,8 +147,8 @@ 'TooltipString', 'all right reward times are multiplied by this number'); next_row(y); ToggleParam(obj, 'antibias_wtr_mult', 0, x,y,... - 'OnString', 'AB ON',... - 'OffString', 'AB OFF',... + 'OnString', 'AntiBias Water ON',... + 'OffString', 'AntiBias Water OFF',... 'TooltipString', sprintf(['If on (black) then it disables the wtr_mult entries\n'... 'and uses hitfrac to adjust the water times'])); @@ -232,6 +232,8 @@ case {3,4} % Centre poke without the A1_Stim but has violation in 4 + + time_go_cue.value=0.100; enable(SettlingIn_time); disable(PreStim_time); disable(A1_time); @@ -246,6 +248,7 @@ case {5,6,7} % + time_go_cue.value=0.100; enable(SettlingIn_time); enable(PreStim_time); enable(A1_time); @@ -292,20 +295,23 @@ case {3,4} % Centre poke without the A1_Stim but has violation in 4 - enable(SettlingIn_time); - disable(PreStim_time); - disable(A1_time); - disable(time_bet_aud1_gocue); - + + time_go_cue.value=0.100; + enable(SettlingIn_time); + disable(PreStim_time); + disable(A1_time); + disable(time_bet_aud1_gocue); + if n_done_trials <1 && warmup_on ==1 CP_duration.value=value(init_CP_duration); else - CP_duration.value=CP_duration; + CP_duration.value=CP_duration; end Total_CP_duration.value = CP_duration + time_go_cue; %#ok<*NASGU> case {5,6,7} % + time_go_cue.value=0.100; enable(SettlingIn_time); enable(PreStim_time); enable(A1_time); diff --git a/Protocols/@Arpit_CentrePokeTraining/PlayStimuli.m b/Protocols/@Arpit_CentrePokeTraining/PlayStimuli.m deleted file mode 100644 index 52ca7c8f..00000000 --- a/Protocols/@Arpit_CentrePokeTraining/PlayStimuli.m +++ /dev/null @@ -1,87 +0,0 @@ - -function [x, y] = PlayStimuli(obj, action, varargin) -GetSoloFunctionArgs(obj); - -switch action, - - case 'init' - - if length(varargin) < 2, - error('Need at least two arguments, x and y position, to initialize %s', mfilename); - end; - x = varargin{1}; y = varargin{2}; - - ToggleParam(obj, 'StimuliPlayShow', 0, x, y, 'OnString', 'StimToPlay', ... - 'OffString', 'StimToPlay', 'TooltipString', 'Show/Hide Sounds panel'); - set_callback(StimuliPlayShow, {mfilename, 'show_hide'}); %#ok (Defined just above) - next_row(y); - oldx=x; oldy=y; parentfig=double(gcf); - - SoloParamHandle(obj, 'myfig', 'value', figure('Position', [100 100 560 440], ... - 'closerequestfcn', [mfilename '(' class(obj) ', ''hide'');'], 'MenuBar', 'none', ... - 'Name', mfilename), 'saveable', 0); - set(double(gcf), 'Visible', 'off'); - x=10;y=10; - - MenuParam(obj, 'filter_type', {'GAUS','LPFIR', 'FIRLS','BUTTER','MOVAVRG','KAISER','EQUIRIP','HAMMING'}, ... - 'GAUS', x, y, 'labelfraction', 0.35, 'TooltipString', sprintf(['\nDifferent filters. ''LPFIR'': lowpass FIR ''FIRLS'': Least square linear-phase FIR filter design\n', ... - '\n''BUTTER'': IIR Butterworth lowpass filter ''GAUS'': Gaussian filter (window)\n', ... - '\n''MOVAVRG'': Moving average FIR filter ''KAISER'': Kaiser-window FIR filtering\n', ... - '\n''EQUIRIP'':Eqiripple FIR filter ''HAMMING'': Hamming-window based FIR'])); - next_row(y, 1.3) - NumeditParam(obj, 'A1_sigma', 0.01, x,y,'label','A1_sigma','TooltipString','Sigma value for the stimulus'); - next_row(y); - NumeditParam(obj,'fcut',110,x,y,'label','fcut','TooltipString','Cut off frequency on the original white noise'); - next_row(y); - NumeditParam(obj,'lfreq',2000,x,y,'label','Modulator_LowFreq','TooltipString','Lower bound for the frequency modulator'); - next_row(y); - NumeditParam(obj,'hfreq',20000,x,y,'label','Modulator_HighFreq','TooltipString','Upper bound for the frequency modulator'); - next_row(y); - NumeditParam(obj,'dur',0.5,x,y,'label','dur','TooltipString','duration of stimulus in ms'); - next_row(y); - - sname='GaussNoise'; - - srate=SoundManagerSection(obj,'get_sample_rate'); - Fs=srate; - replace=1; - T=dur; - L=floor(T*Fs); % Length of signal - sigma_1=1; - - pos1 = sigma_1*randn(Fs,1); - base = randsample(pos1,L,replace); - filtbase=filt(base,fcut,Fs,value(filter_type)); - normbase=filtbase./(max(abs(filtbase))); - - mod1 = sigma_1*randn(Fs,1); - mod1 = randsample(mod1,L,replace); - hf = design(fdesign.bandpass('N,F3dB1,F3dB2',10,value(lfreq),value(hfreq),Fs)); - filtmod=filter(hf,mod1); - modulator=filtbase./(max(abs(filtmod))); - - AUD1=normbase(1:dur*srate).*modulator(1:dur*srate).*A1_sigma; - w=[AUD1'; AUD1']; - SoundManagerSection(obj, 'declare_new_sound', sname); - SoundManagerSection(obj, 'set_sound',sname,w) - - SubheaderParam(obj, [sname 'Head'], sname, x,y,'TooltipString',''); - PushbuttonParam(obj, [sname 'Play'], x,y, 'label', 'Play', 'position', [x y 30 20]); - set_callback(eval([sname 'Play']),{'SoundManagerSection', 'play_sound', sname}); - PushbuttonParam(obj, [sname 'Stop'], x,y, 'label', 'Stop', 'position', [x+30 y 30 20]); - set_callback(eval([sname 'Stop']),{'SoundManagerSection', 'stop_sound', sname}); - - x=oldx; y=oldy; - figure(parentfig); - - case 'hide', - StimuliPlayShow.value = 0; set(value(myfig), 'Visible', 'off'); - - case 'show', - StimuliPlayShow.value = 1; set(value(myfig), 'Visible', 'on'); - - case 'show_hide', - if StimuliPlayShow == 1, set(value(myfig), 'Visible', 'on'); %#ok (defined by GetSoloFunctionArgs) - else set(value(myfig), 'Visible', 'off'); - end; -end diff --git a/Protocols/@Arpit_CentrePokeTraining/StimulatorSection.m b/Protocols/@Arpit_CentrePokeTraining/StimulatorSection.m deleted file mode 100644 index 71ad0760..00000000 --- a/Protocols/@Arpit_CentrePokeTraining/StimulatorSection.m +++ /dev/null @@ -1,325 +0,0 @@ -% -% PARAMETERS: -% ----------- -% -% obj Default object argument. -% -% action One of: -% -% 'prepare_next_trial' -% -% 'init' -% -% -% RETURNS: -% -------- -% -% -% -% -% -% - - - -function [varargout] = StimulatorSection(obj, action, x, y) - -GetSoloFunctionArgs(obj); - -switch action - -%% init -% ---------------------------------------------------------------- -% -% INIT -% -% ---------------------------------------------------------------- - - case 'init', - %NumeditParam(obj,'LCB_onstim', 0,x,y,'position',[x y 100 20],'labelfraction',0.6); - %NumeditParam(obj,'LCB_nostim', 0,x,y,'position',[x+100 y 100 20],'labelfraction',0.6); next_row(y); - %SoloParamHandle(obj,'LegalCBrk_temp', 'value',0); - - MenuParam(obj,'StimInterval',{'WholeTrial','S1','DelayDur','GoCue'},1,x,y,'labelfraction',0.30); next_row(y); - set_callback(StimInterval, {mfilename, 'StimInterval'}); - MenuParam(obj,'StimOnSide',{'both','left','right'},1,x,y,'labelfraction',0.3); next_row(y); - - diolines = bSettings('get','DIOLINES', 'all'); - for i = 1:size(diolines,1); dionames{i} = diolines{i,1}; dionums(i) = diolines{i,2}; end %#ok - [dionums order] = sort(dionums); - dionames2 = cell(0); - for i = 1:length(dionums); if ~isnan(dionums(i)); dionames2{end+1} = dionames{order(i)}; end; end %#ok - dionames2 = cell(0); - dionames2{1} = 'opto'; - - MenuParam(obj,'StimLine',dionames2,1,x,y,'labelfraction',0.30); next_row(y); - - SC = state_colors(obj); - WC = wave_colors(obj); - states = fieldnames(SC); - waves = fieldnames(WC); - states(2:end+1) = states; - states{1} = 'cp'; - states(end+1:end+length(waves)) = waves; - - MenuParam(obj,'StimState',states,1,x,y,'labelfraction',0.30); next_row(y); - - NumeditParam(obj,'StimStates', 1,x,y,'position',[x y 100 20],'labelfraction',0.60); - NumeditParam(obj,'StimLines', 1,x,y,'position',[x+100 y 100 20],'labelfraction',0.60); next_row(y); - - NumeditParam(obj,'StartDelay', 0,x,y,'position',[x y 100 20],'labelfraction',0.60); - NumeditParam(obj,'StimFreq', 20,x,y,'position',[x+100 y 100 20],'labelfraction',0.60); next_row(y); - NumeditParam(obj,'PulseWidth',15,x,y,'position',[x y 100 20],'labelfraction',0.60); - NumeditParam(obj,'NumPulses', 1,x,y,'position',[x+100 y 100 20],'labelfraction',0.60); next_row(y); - - DispParam(obj,'SD',0 ,x,y,'position',[x y 50 20],'labelfraction',0.4); - DispParam(obj,'SF',20,x,y,'position',[x+50 y 50 20],'labelfraction',0.4); - DispParam(obj,'PW',15,x,y,'position',[x+100 y 50 20],'labelfraction',0.4); - DispParam(obj,'NP',1,x,y,'position',[x+150 y 50 20],'labelfraction',0.4); next_row(y); - - NumeditParam(obj,'StimProb', 0,x,y,'position',[x y 100 20],'labelfraction',0.65); - ToggleParam( obj,'ShuffleValues',0,x,y,'position',[x+100 y 100 20],'OnString','Shuffle','OffString','Lock'); next_row(y); - - SoloParamHandle(obj, 'stimulator_history', 'value', []); - - SubheaderParam(obj, 'title', 'Stimulator Section', x, y); next_row(y); - varargout{1} = x; - varargout{2} = y; - -%% update_values -% ----------------------------------------------------------------------- -% -% UPDATE_VALUES -% -% ----------------------------------------------------------------------- - - case 'update_values', - - StimulatorSection(obj,'StimInterval'); - sh = value(stimulator_history) %#ok - %if n_done_trials == 0 || sh(end) == 0 - % LegalCBrk_temp.value = value(LegalCBrk); %#ok - %end - - if ~dispatcher('is_running'); - %dispatcher is not running, last stim_hist not used, lop it off - sh = sh(1:end-1); - end - - if value(StimProb) == 0 - %LCB_nostim.value = value(LegalCBrk); %#ok - stimulator_history.value = [sh, 0]; - elseif rand(1) <= value(StimProb) %&& ((value(StimOnFree)==1 && strcmp(value(ThisTrial_Free),'FREE')) || value(StimOnFree)==0) - stimulator_history.value = [sh, 1]; - %LegalCBrk.value = value(LCB_onstim); - else - %LegalCBrk.value = value(LCB_nostim); %#ok - stimulator_history.value = [sh, 0]; - end - - - -%% prepare_next_trial -% ----------------------------------------------------------------------- -% -% PREPARE_NEXT_TRIAL -% -% ----------------------------------------------------------------------- - - case 'prepare_next_trial', - sh = value(stimulator_history); %#ok - - sma = x; - - sd = value(StartDelay); - sf = value(StimFreq); - pw = value(PulseWidth); - np = value(NumPulses); - ss = value(StimStates) - sl = value(StimLines) - - if value(ShuffleValues) == 1 - sd = sd(ceil(rand(1) * length(sd))); - sf = sf(ceil(rand(1) * length(sf))); - pw = pw(ceil(rand(1) * length(pw))); - np = np(ceil(rand(1) * length(np))); - ss = ss(ceil(rand(1) * length(ss))); - sl = sl(ceil(rand(1) * length(sl))); - else - if length(unique([length(sd) length(sf) length(pw) length(np) length(ss) length(sl)])) > 1 - disp('Warning: param values in StimulatorSection have different lengths. Only first value will be used.'); - temp = 1; - else - temp = ceil(rand(1) * length(sd)); - end - sd = sd(temp); sf = sf(temp); pw = pw(temp); np = np(temp); ss = ss(temp); sl = sl(temp); - end - - pss = get(get_ghandle(StimState),'String'); %#ok - psl = get(get_ghandle(StimLine), 'String'); %#ok - if ss > length(pss) - disp('StimState value greater than list of possible stim states'); - else - StimState.value = ss; - disp('test ss') - value(ss) - end - - if sl > length(psl) - slc = ['0',num2str(sl),'0']; - z = find(slc == '0'); - if length(z) > 2 - sln = []; - for i = 1:length(z)-1 - sln(end+1) = str2num(slc(z(i)+1:z(i+1)-1)); %#ok - end - if any(sln > length(psl)) - disp('StimLine value greater than list of possible stim lines'); - else - slname = psl{sln(1)}; - for i=2:length(sln) - slname = [slname,'+',psl{sln(i)}]; %#ok - end - if sum(strcmp(psl,slname)) == 0 - psl{end+1} = slname; - set(get_ghandle(StimLine),'String',psl) - end - StimLine.value = find(strcmp(psl,slname)==1,1,'first'); - sl = sln; - end - else - disp('StimLine value greater than list of possible stim lines'); - end - else - StimLine.value = sl; - end - - for i = 1:length(sl) - stimline = bSettings('get','DIOLINES',psl{sl(i)}); - - sma = add_scheduled_wave(sma,... - 'name', ['stimulator_wave',num2str(i)],... - 'preamble', (1/sf)-(pw/1000),... %%%% Remember: change it such that if this is negative makes it 0 - 'sustain' , pw/1000,... - 'DOut', stimline,... - 'loop', np-1); - - if sd ~= 0 - sma = add_scheduled_wave(sma,... - 'name',['stimulator_wave_pause',num2str(i)],... - 'preamble',sd,... - 'trigger_on_up',['stimulator_wave',num2str(i)]); - else - sma = add_scheduled_wave(sma,... - 'name',['stimulator_wave_pause',num2str(i)],... - 'preamble',1,... - 'trigger_on_up',['stimulator_wave',num2str(i)]); - end - end - - for i = 1:length(sl) - if sh(end) == 1 - if strcmp(value(StimState),'none') == 0 - if sd ~= 0 - sma = add_stimulus(sma,['stimulator_wave_pause',num2str(i)],value(StimState)); - else - sma = add_stimulus(sma,['stimulator_wave',num2str(i)],value(StimState)); - end - - SD.value = sd; SF.value = sf; PW.value = pw; NP.value = np; - end - else - SD.value = 0; SF.value = 0; PW.value = 0; NP.value = 0; - end - end - - varargout{1} = sma; - - - %% Case StimInterval - case 'StimInterval' - - if strcmp(StimInterval, 'WholeTrial'); - PulseWidth.value = Total_CP_duration*1000; - StimFreq.value = 1000/PulseWidth; - StartDelay.value = PreStim_time; - elseif strcmp(StimInterval, 'S1'); - PulseWidth.value = A1_time*1000; - StimFreq.value = 1000/PulseWidth; - StartDelay.value = PreStim_time; - elseif strcmp(StimInterval, 'DelayDur'); - PulseWidth.value = time_bet_aud1_gocue*1000; - StimFreq.value = 1000/PulseWidth; - StartDelay.value = PreStim_time + A1_time; - elseif strcmp(StimInterval, 'GoCue'); - PulseWidth.value = time_go_cue*1000; - StimFreq.value = 1000/PulseWidth; - StartDelay.value = PreStim_time + A1_time + time_bet_aud1_gocue; - - end -%% set -% ----------------------------------------------------------------------- -% -% SET -% -% ----------------------------------------------------------------------- - case 'set' - varname = x; - newval = y; - - try - temp = 'SoloParamHandle'; %#ok - eval(['test = isa(',varname,',temp);']); - if test == 1 - eval([varname,'.value = newval;']); - end - catch %#ok - showerror; - warning(['Unable to assign value: ',num2str(newval),' to SoloParamHandle: ',varname]); %#ok - end - - -%% get -% ----------------------------------------------------------------------- -% -% GET -% -% ----------------------------------------------------------------------- - case 'get' - varname = x; - - try - temp = 'SoloParamHandle'; %#ok - eval(['test = isa(',varname,',temp);']); - if test == 1 - eval(['varargout{1} = value(',varname,');']); - end - catch %#ok - showerror; - warning(['Unable to get value from SoloParamHandle: ',varname]); %#ok - end - - -%% reinit -% ----------------------------------------------------------------------- -% -% REINIT -% -% ----------------------------------------------------------------------- - case 'reinit', - currfig = double(gcf); - - % Delete all SoloParamHandles who belong to this object and whose - % fullname starts with the name of this mfile: - delete_sphandle('owner', ['^@' class(obj) '$'], ... - 'fullname', ['^' mfilename]); - - - % Reinitialise at the original GUI position and figure: - feval(mfilename, obj, 'init'); - - % Restore the current figure: - figure(currfig); -end; - - From dbb2d7d5272974224d909c10b86df9c9023aa58a Mon Sep 17 00:00:00 2001 From: ArpitAgarwal Date: Wed, 19 Mar 2025 10:25:23 +0000 Subject: [PATCH 023/164] making the protocol Arpit_CentrePokeTraining protocol workable --- .../Arpit_CentrePokeTraining.asv | 264 +++++++++ .../Arpit_CentrePokeTraining.m | 18 +- .../ParamsSection.asv | 536 ++++++++++++++++++ .../@Arpit_CentrePokeTraining/ParamsSection.m | 24 +- 4 files changed, 824 insertions(+), 18 deletions(-) create mode 100644 Protocols/@Arpit_CentrePokeTraining/Arpit_CentrePokeTraining.asv create mode 100644 Protocols/@Arpit_CentrePokeTraining/ParamsSection.asv diff --git a/Protocols/@Arpit_CentrePokeTraining/Arpit_CentrePokeTraining.asv b/Protocols/@Arpit_CentrePokeTraining/Arpit_CentrePokeTraining.asv new file mode 100644 index 00000000..ab5a83fb --- /dev/null +++ b/Protocols/@Arpit_CentrePokeTraining/Arpit_CentrePokeTraining.asv @@ -0,0 +1,264 @@ +% Arpit_CentrePokeTraining protocol +% Arpit, 12 March 2025 + +function [obj] = Arpit_CentrePokeTraining(varargin) + +% Default object is of our own class (mfilename); +% we inherit only from Plugins + +obj = class(struct, mfilename, pokesplot2, saveload, sessionmodel2, soundmanager, soundui, antibias, ... + water, distribui, punishui, comments, soundtable, sqlsummary,reinforcement,softpokestay2); + +%--------------------------------------------------------------- +% BEGIN SECTION COMMONSoundTableSection TO ALL PROTOCOLS, DO NOT MODIFY +%--------------------------------------------------------------- + +% If creating an empty object, return without further ado: +if nargin==0 || (nargin==1 && ischar(varargin{1}) && strcmp(varargin{1}, 'empty')) + return; +end + +if isa(varargin{1}, mfilename) % If first arg is an object of this class itself, we are + % Most likely responding to a callback from + % a SoloParamHandle defined in this mfile. + if length(varargin) < 2 || ~ischar(varargin{2}) + error(['If called with a "%s" object as first arg, a second arg, a ' ... + 'string specifying the action, is required\n']); + else + action = varargin{2}; varargin = varargin(3:end); %#ok + end +else % Ok, regular call with first param being the action string. + action = varargin{1}; varargin = varargin(2:end); %#ok +end + +GetSoloFunctionArgs(obj); + +switch action + + %% init + case 'init' + dispatcher('set_trialnum_indicator_flag'); + hackvar = 10; SoloFunctionAddVars('SessionModel', 'ro_args', 'hackvar'); %#ok + SoloParamHandle(obj, 'myfig', 'saveable', 0); myfig.value = figure; + + % Make the title of the figure be the protocol name, and if someone tries + % to close this figure, call dispatcher's close_protocol function, so it'll know + % to take it off the list of open protocols. + name = mfilename; + set(value(myfig), 'Name', name, 'Tag', name, ... + 'closerequestfcn', 'dispatcher(''close_protocol'')', 'MenuBar', 'none'); + % At this point we have one SoloParamHandle, myfig + % Let's put the figure where we want it and give it a reasonable size: + set(value(myfig), 'Position', [485 144 850 680]); + + SoloParamHandle(obj, 'nsessions_healthy_number_of_pokes', 'value', 0, 'save_with_settings', 1); + SoloParamHandle(obj, 'post_DelComp_protocol', 'value', '', 'save_with_settings', 1); + SoloParamHandle(obj, 'post_DelComp_settings_filename', 'value', '', 'save_with_settings', 1); + + + SoloParamHandle(obj, 'hit_history', 'value', []); + DeclareGlobals(obj, 'ro_args', {'hit_history'}); + SoloFunctionAddVars('ParamsSection', 'rw_args', 'hit_history'); + + SoloParamHandle(obj, 'violation_history', 'value', []); + DeclareGlobals(obj, 'ro_args', {'violation_history'}); + SoloFunctionAddVars('ParamsSection', 'rw_args', 'violation_history'); + + SoloParamHandle(obj, 'timeout_history', 'value', []); + DeclareGlobals(obj, 'ro_args', {'timeout_history'}); + SoloFunctionAddVars('ParamsSection', 'rw_args', 'timeout_history'); + + SoloParamHandle(obj, 'Stage_Trial_Counter', 'value', zeros(1,7)); + DeclareGlobals(obj, 'ro_args', {'Stage_Trial_Counter'}); + SoloFunctionAddVars('ParamsSection', 'rw_args', 'Stage_Trial_Counter'); + + SoloParamHandle(obj, 'Stage_ValidTrial_Counter', 'value', zeros(1,7)); + DeclareGlobals(obj, 'ro_args', {'Stage_ValidTrial_Counter'}); + SoloFunctionAddVars('ParamsSection', 'rw_args', 'Stage_ValidTrial_Counter'); + + SoloParamHandle(obj, 'Stage_ViolationTrial_Counter', 'value', zeros(1,7)); + DeclareGlobals(obj, 'ro_args', {'Stage_ViolationTrial_Counter'}); + SoloFunctionAddVars('ParamsSection', 'rw_args', 'Stage_ViolationTrial_Counter'); + + SoloParamHandle(obj, 'Stage_TimeoutTrial_Counter', 'value', zeros(1,7)); + DeclareGlobals(obj, 'ro_args', {'Stage_TimeoutTrial_Counter'}); + SoloFunctionAddVars('ParamsSection', 'rw_args', 'Stage_TimeoutTrial_Counter'); + + + SoundManagerSection(obj, 'init'); + x = 5; y = 5; % Initial position on main GUI window + [x, y] = SavingSection(obj, 'init', x, y); + + %% slow ramp up of water amount + %%the water volume is controlled by a 5-parameter logistic function: WaterAmount(t) = maxasymp + (minasymp/(1+(t/inflp)^slp).^assym) + NumeditParam(obj, 'maxasymp', 38, x,y,'label','maxasymp','TooltipString',... + 'the water volume is controlled by a 5-parameter logistic function: WaterAmount(trialnum) = maxasymp + (minasymp/(1+(trialnum/inflp)^slp).^assym)'); + next_row(y); + NumeditParam(obj, 'slp', 3, x,y,'label','slp','TooltipString','Water Modulation: Slope of the logistic function'); + next_row(y); + NumeditParam(obj, 'inflp', 350, x,y,'label','inflp','TooltipString','Water Modulation: concentration at the inflection point'); + next_row(y); + NumeditParam(obj, 'minasymp', -21, x,y,'label','inflp','TooltipString','Water Modulation: minimum asymptote'); + next_row(y); + NumeditParam(obj, 'assym', 0.7, x,y,'label','assym','TooltipString','Water Modulation: asymmetry factor'); + next_row(y); + DispParam(obj, 'trial_1', 0, x, y, 'TooltipString', 'uL on first trial'); + next_row(y); + DispParam(obj, 'trial_150', 0, x, y, 'TooltipString', 'uL on trial 150'); + next_row(y); + DispParam(obj, 'trial_300', 0, x, y, 'TooltipString', 'uL on trial 300'); + next_row(y); + set_callback({maxasymp;slp;inflp;minasymp;assym}, {mfilename, 'change_water_modulation_params'}); + feval(mfilename, obj, 'change_water_modulation_params'); + + %AthenaSMA changed to SoundCatSMA (From AthenaDelayComp) + SoloFunctionAddVars('Arpit_CentrePokeTrainingSMA', 'ro_args', ... + {'maxasymp';'slp';'inflp';'minasymp';'assym'}); + [x, y] = WaterValvesSection(obj, 'init', x, y); + + % For plotting with the pokesplot plugin, we need to tell it what + % colors to plot with: + my_state_colors = Arpit_CentrePokeTrainingSMA(obj, 'get_state_colors'); + % In pokesplot, the poke colors have a default value, so we don't need + % to specify them, but here they are so you know how to change them. + my_poke_colors = struct( ... + 'L', 0.6*[1 0.66 0], ... + 'C', [0 0 0], ... + 'R', 0.9*[1 0.66 0]); + + [x, y] = PokesPlotSection(obj, 'init', x, y, ... + struct('states', my_state_colors, 'pokes', my_poke_colors)); next_row(y); + + [x, y] = CommentsSection(obj, 'init', x, y); + + % [x, y] = PunishmentSection(obj, 'init', x, y); %#ok + + next_column(x); y=5; + [x, y] = OverallPerformanceSection(obj, 'init', x, y); + [x, y] = ParamsSection(obj, 'init', x, y); %#ok + [x, y] = SoundSection(obj,'init',x,y); + [x, y] = SoftPokeStayInterface(obj, 'add', 'soft_cp', x, y); + + figpos = get(double(gcf), 'Position'); + [expmtr, rname]=SavingSection(obj, 'get_info'); + HeaderParam(obj, 'prot_title', [mfilename ': ' expmtr ', ' rname], x, y, 'position', [10 figpos(4)-25, 800 20]); + + Arpit_CentrePokeTrainingSMA(obj, 'init'); + + SessionDefinition(obj, 'init', x, y, value(myfig)); next_row(y, 2); %#ok + SessionDefinition(obj, 'set_old_style_parsing_flag',0); + + feval(mfilename, obj, 'prepare_next_trial'); + + %% change_water_modulation_params + case 'change_water_modulation_params' + display_guys = [1 150 300]; + for i=1:numel(display_guys) + t = display_guys(i); + + myvar = eval(sprintf('trial_%d', t)); + myvar.value = maxasymp + (minasymp/(1+(t/inflp)^slp).^assym); + end + + %% prepare next trial + case 'prepare_next_trial' + + ParamsSection(obj, 'prepare_next_trial'); + % Run SessionDefinition *after* ParamsSection so we know whether the + % trial was a violation or not + SessionDefinition(obj, 'next_trial'); + OverallPerformanceSection(obj, 'evaluate'); + SoundManagerSection(obj, 'send_not_yet_uploaded_sounds'); + + nTrials.value = n_done_trials; + + [sma, prepare_next_trial_states] = Arpit_CentrePokeTrainingSMA(obj, 'prepare_next_trial'); + + % Default behavior of following call is that every 20 trials, the data + % gets saved, not interactive, no commit to CVS. + SavingSection(obj, 'autosave_data'); + + CommentsSection(obj, 'clear_history'); % Make sure we're not storing unnecessary history + if n_done_trials==1 % Auto-append date for convenience. + CommentsSection(obj, 'append_date'); CommentsSection(obj, 'append_line', ''); + end + + if n_done_trials==1 + [expmtr, rname]=SavingSection(obj, 'get_info'); + prot_title.value=[mfilename ' on rig ' get_hostname ' : ' expmtr ', ' rname '. Started at ' datestr(now, 'HH:MM')]; + end + + try send_n_done_trials(obj); + end + + %% trial_completed + case 'trial_completed' + + % Do any updates in the protocol that need doing: + feval(mfilename, 'update'); + % And PokesPlot needs completing the trial: + PokesPlotSection(obj, 'trial_completed'); + + %% update + case 'update' + PokesPlotSection(obj, 'update'); + + + %% close + case 'close' + PokesPlotSection(obj, 'close'); + ParamsSection(obj, 'close'); + StimulusSection(obj,'close'); + + if exist('myfig', 'var') && isa(myfig, 'SoloParamHandle') && ishandle(value(myfig)) %#ok + delete(value(myfig)); + end + delete_sphandle('owner', ['^@' class(obj) '$']); + + + %% end_session + case 'end_session' + prot_title.value = [value(prot_title) ', Ended at ' datestr(now, 'HH:MM')]; + + + %% pre_saving_settings + case 'pre_saving_settings' + + SessionDefinition(obj, 'run_eod_logic_without_saving'); + perf = OverallPerformanceSection(obj, 'evaluate'); + cp_durs = ParamsSection(obj, 'get_cp_history'); + + [stim1dur] = ParamsSection(obj,'get_stimdur_history'); + +% CommentsSection(obj, 'append_line', ... +% sprintf(['ntrials = %d, violations = %.2f, timeouts=%.2f, hits = %.2f\n', ... +% 'pre-Go cue went from %.3f to %.3f (delta=%.3f)\n', ... +% 'Low = %.2f, High = %.2f'], ... +% perf(1), perf(2), perf(3), perf(6), cp_durs(1), cp_durs(end), cp_durs(end)-cp_durs(1), classperf(1),classperf(2))); + + pd.hits=hit_history(:); + pd.sides=previous_sides(:); + pd.viols=violation_history(:); + pd.timeouts=timeout_history(:); +% pd.performance=tot_perf(:); + pd.cp_durs=cp_durs(:); + + +% pd.stimuli=stimuli(:); + % Athena: look into pair_history perhaps stimulus_history and stimuli + % are the same + %pd.stimulus=stimulus_history(:); + + pd.stim1dur=stim1dur(:); + + %pd.stimul=stim_history(:); + + sendsummary(obj,'protocol_data',pd); + + %% otherwise + otherwise + warning('Unknown action! "%s"\n', action); +end + +return; + diff --git a/Protocols/@Arpit_CentrePokeTraining/Arpit_CentrePokeTraining.m b/Protocols/@Arpit_CentrePokeTraining/Arpit_CentrePokeTraining.m index e27c265c..a45e47f5 100644 --- a/Protocols/@Arpit_CentrePokeTraining/Arpit_CentrePokeTraining.m +++ b/Protocols/@Arpit_CentrePokeTraining/Arpit_CentrePokeTraining.m @@ -68,7 +68,23 @@ DeclareGlobals(obj, 'ro_args', {'timeout_history'}); SoloFunctionAddVars('ParamsSection', 'rw_args', 'timeout_history'); - + % SoloParamHandle(obj, 'Stage_Trial_Counter', 'value', zeros(1,7)); + % DeclareGlobals(obj, 'ro_args', {'Stage_Trial_Counter'}); + % SoloFunctionAddVars('ParamsSection', 'rw_args', 'Stage_Trial_Counter'); + % + % SoloParamHandle(obj, 'Stage_ValidTrial_Counter', 'value', zeros(1,7)); + % DeclareGlobals(obj, 'ro_args', {'Stage_ValidTrial_Counter'}); + % SoloFunctionAddVars('ParamsSection', 'rw_args', 'Stage_ValidTrial_Counter'); + % + % SoloParamHandle(obj, 'Stage_ViolationTrial_Counter', 'value', zeros(1,7)); + % DeclareGlobals(obj, 'ro_args', {'Stage_ViolationTrial_Counter'}); + % SoloFunctionAddVars('ParamsSection', 'rw_args', 'Stage_ViolationTrial_Counter'); + % + % SoloParamHandle(obj, 'Stage_TimeoutTrial_Counter', 'value', zeros(1,7)); + % DeclareGlobals(obj, 'ro_args', {'Stage_TimeoutTrial_Counter'}); + % SoloFunctionAddVars('ParamsSection', 'rw_args', 'Stage_TimeoutTrial_Counter'); + + SoundManagerSection(obj, 'init'); x = 5; y = 5; % Initial position on main GUI window [x, y] = SavingSection(obj, 'init', x, y); diff --git a/Protocols/@Arpit_CentrePokeTraining/ParamsSection.asv b/Protocols/@Arpit_CentrePokeTraining/ParamsSection.asv new file mode 100644 index 00000000..862acb04 --- /dev/null +++ b/Protocols/@Arpit_CentrePokeTraining/ParamsSection.asv @@ -0,0 +1,536 @@ +function [x, y] = ParamsSection(obj, action, x,y) + +GetSoloFunctionArgs(obj); + +switch action + + % ------------------------------------------------------------------ + % INIT + % ------------------------------------------------------------------ + + case 'init' + + SoloParamHandle(obj, 'my_gui_info', 'value', [x y double(gcf)], 'saveable', 0); + y0 = y; + + [x, y] = AntibiasSectionAthena(obj, 'init', x, y); + + + ToggleParam(obj, 'antibias_LRprob', 0, x,y,... + 'OnString', 'AB_Prob ON',... + 'OffString', 'AB_Prob OFF',... + 'TooltipString', sprintf(['If on (Yellow) then it enables the AntiBias algorithm\n'... + 'based on changing the probablity of Left vs Right'])); + + next_row(y); + NumeditParam(obj, 'LeftProb', 0.5, x, y); next_row(y); + set_callback(LeftProb, {mfilename, 'new_leftprob'}); + MenuParam(obj, 'MaxSame', {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, Inf}, Inf, x, y, ... + 'TooltipString', sprintf(['\nMaximum number of consecutive trials where correct\n' ... + 'response is on the same side. Overrides antibias. Thus, for\n' ... + 'example, if MaxSame=5 and there have been 5 Left trials, the\n' ... + 'next trial is guaranteed to be Right'])); next_row(y); + + DispParam(obj, 'ThisTrial', 'LEFT', x, y); next_row(y); + SoloParamHandle(obj, 'previous_sides', 'value', []); + DeclareGlobals(obj, 'ro_args', 'previous_sides'); + SubheaderParam(obj, 'title', 'Params Section', x, y); + next_row(y, 1.5); + next_column(x); y = 5; + % Reward Collection + NumeditParam(obj, 'RewardCollection_duration', 10, x,y,'label','RewardCollection_dur','TooltipString','Wait until rat collects the reward else a timeout'); + next_row(y); + NumeditParam(obj, 'SideLed_duration', 1, x,y,'label','Side LED duration','TooltipString','Duration of SideLed'); + % Centre Poke + next_row(y); + NumeditParam(obj, 'legal_cbreak', 0.1, x,y, 'position', [x, y, 175 20], 'TooltipString','Time in sec for which it is ok to be outside the center port before a violation occurs.'); + set_callback(legal_cbreak, {mfilename, 'new_legal_cbreak'}); + next_row(y); + NumeditParam(obj, 'SettlingIn_time', 0.2, x,y, 'position', [x, y, 175 20], 'TooltipString','Initial settling period during which there is no violation'); + next_row(y); + NumeditParam(obj, 'A1_time_Min', 0.4, x,y,'label','Min time AUD1','TooltipString','Min value to select the Duration of fixed stimulus'); + set_callback(A1_time_Min, {mfilename, 'new_CP_duration'}); + next_row(y); + NumeditParam(obj, 'A1_time', 0.4, x,y,'label','AUD1 Time','TooltipString','Actual Duration of fixed stimulus'); + next_row(y); + set_callback(A1_time, {mfilename, 'new_CP_duration'}); + next_row(y); + NumeditParam(obj, 'A1_time_Max', 0.4, x,y,'label','Max time AUD1','TooltipString','Max value to select the Duration of fixed stimulus'); + set_callback(A1_time_Max, {mfilename, 'new_CP_duration'}); + next_row(y); + NumeditParam(obj, 'PreStim_time_Min', 0.20, x,y,'label','Pre-Stim Min','TooltipString','Min Time in NIC before starting the stimulus'); + set_callback(PreStim_time_Min, {mfilename, 'new_CP_duration'}); + next_row(y); + NumeditParam(obj, 'PreStim_time', 0.20, x,y,'label','Pre-Stim time','TooltipString','Actual Time in NIC before starting the stimulus'); + set_callback(PreStim_time, {mfilename, 'new_CP_duration'}); + next_row(y); + NumeditParam(obj, 'PreStim_time_Max', 0.40, x,y,'label','Pre-Stim Max','TooltipString','Max Time in NIC before starting the stimulus'); + set_callback(PreStim_time_Max, {mfilename, 'new_CP_duration'}); + next_row(y); + NumeditParam(obj, 'time_bet_aud1_gocue_Min', 0.2, x,y,'label','Min A1-GoCue time','TooltipString','Min time between the end of the stimulus and the go cue '); + set_callback(time_bet_aud1_gocue_Min, {mfilename, 'new_CP_duration'}); + next_row(y); + NumeditParam(obj, 'time_bet_aud1_gocue', 0.2, x,y,'label','A1-GoCue time','TooltipString','Actual time between the end of the stimulus and the go cue '); + next_row(y); + set_callback(time_bet_aud1_gocue, {mfilename, 'new_CP_duration'}); + next_row(y); + NumeditParam(obj, 'time_bet_aud1_gocue_Max', 2, x,y,'label','Max A1-GoCue time','TooltipString','Max time between the end of the stimulus and the go cue '); + set_callback(time_bet_aud1_gocue_Max, {mfilename, 'new_CP_duration'}); + next_row(y); + DispParam(obj, 'init_CP_duration', 0.01, x,y,'label','init_CP duration','TooltipString','Duration of Nose in Central Poke before Go cue starts (see Total_CP_duration)'); + next_row(y); + DispParam(obj, 'CP_duration', PreStim_time+A1_time+time_bet_aud1_gocue, x,y,'label','CP duration','TooltipString','Duration of Nose in Central Poke before Go cue starts (see Total_CP_duration)'); + set_callback(CP_duration, {mfilename, 'new_CP_duration'}); + next_row(y); + NumeditParam(obj, 'time_go_cue' ,0.1, x,y,'label','Go Cue Duration','TooltipString','duration of go cue (see Total_CP_duration)'); + set_callback(time_go_cue, {mfilename, 'new_time_go_cue'}); + next_row(y); + DispParam(obj, 'Total_CP_duration', CP_duration+time_go_cue, x, y, 'TooltipString', 'Total expected(rat can poke out anytime after Go cue onset) nose in center port time, in secs. Sum of CP_duration and Go Cue duration'); %#ok<*NODEF> + next_row(y); + ToggleParam(obj, 'warmup_on', 1, x,y,... + 'OnString', 'Warmup ON',... + 'OffString', 'Warmup OFF',... + 'TooltipString', sprintf(['If on (Yellow) then it applies the initial warming up phase, during which the\n',... + 'CP_duration starts small and gradually grows to last_session_max_cp_duration'])); + next_row(y); + + NumeditParam(obj, 'reward_delay', 0.01, x,y,'label','Reward Delay','TooltipString','Delay between side poke and reward delivery'); + next_row(y); + NumeditParam(obj, 'drink_time', 1, x,y,'label','Drink Time','TooltipString','waits to finish water delivery'); + next_row(y); + NumeditParam(obj, 'timeout_iti', 5, x,y,'label','No Choice Timeout','TooltipString','ITI on timeout trials'); + next_row(y); + NumeditParam(obj, 'violation_iti', 1, x,y,'label','Violation Timeout','TooltipString','Center poke violation duration'); + + next_row(y); + ToggleParam(obj, 'random_PreStim_time', 0, x,y,... + 'OnString', 'random PreStim_time ON',... + 'OffString', 'random PreStim_time OFF',... + 'TooltipString', sprintf('If on (black) then it enables the random time between the user given range')); + set_callback(random_PreStim_time, {mfilename, 'new_CP_duration'}); + next_row(y); + ToggleParam(obj, 'random_A1_time', 0, x,y,... + 'OnString', 'random A1_time ON',... + 'OffString', 'random A1_time OFF',... + 'TooltipString', sprintf('If on (black) then it enables the random sampling of A1_time')); + set_callback(random_A1_time, {mfilename, 'new_CP_duration'}); + next_row(y); + ToggleParam(obj, 'random_prego_time', 0, x,y,... + 'OnString', 'random prego_time ON',... + 'OffString', 'random prego_time OFF',... + 'TooltipString', sprintf('If on (black) then it enables the random sampling of time between the end of the stimulus and the go cue')); + set_callback(random_prego_time, {mfilename, 'new_CP_duration'}); + + next_column(x); + y=5; + NumeditParam(obj,'trials_in_stage',1,x,y,'label','Trial Counter'); + next_row(y); + NumeditParam(obj,'training_stage',1,x,y,'label','Training Stage'); + set_callback(training_stage, {mfilename, 'Training_Stage'}); + next_row(y); + ToggleParam(obj,'use_training',0,x,y,'OnString','Using Autotrain','OffString','Manual Settings'); + set_callback(use_training, {mfilename, 'Training_Stage'}); + + next_row(y); + NumeditParam(obj, 'ntrial_correct_bias', 0, x, y, ... + 'TooltipString', 'antibias starts from trial=ntrial_correct_bias'); + next_row(y); + NumeditParam(obj, 'right_left_diff', .12, x, y, ... + 'TooltipString', 'antibias applies if difference between right and left sides is bigger than this number'); + next_row(y); + NumeditParam(obj, 'max_wtr_mult', 4, x, y, ... + 'TooltipString', 'wtr_mult will be min(max_wtr_mult,right_hit/left_hit)'); + next_row(y); + NumeditParam(obj, 'left_wtr_mult', 1, x, y, ... + 'TooltipString', 'all left reward times are multiplied by this number'); + next_row(y); + NumeditParam(obj, 'right_wtr_mult', 1, x, y, ... + 'TooltipString', 'all right reward times are multiplied by this number'); + next_row(y); + ToggleParam(obj, 'antibias_wtr_mult', 0, x,y,... + 'OnString', 'AntiBias Water ON',... + 'OffString', 'AntiBias Water OFF',... + 'TooltipString', sprintf(['If on (black) then it disables the wtr_mult entries\n'... + 'and uses hitfrac to adjust the water times'])); + + next_row(y); + SoloFunctionAddVars('Arpit_CentrePokeTrainingSMA', 'ro_args', ... + {'CP_duration';'SideLed_duration'; ... + 'RewardCollection_duration';'training_stage'; ... + 'legal_cbreak' ; 'SettlingIn_time'; 'time_go_cue'; ... + 'A1_time';'time_bet_aud1_gocue' ; 'PreStim_time'; + 'drink_time';'reward_delay';'left_wtr_mult';... + 'right_wtr_mult';'antibias_wtr_mult';... + 'timeout_iti';'violation_iti'}); + + % History of hit/miss: + SoloParamHandle(obj, 'deltaf_history', 'value', []); + + SoloFunctionAddVars('OverallPerformanceSection', 'ro_args', ... + {'training_stage'}); + + + SoloParamHandle(obj, 'previous_parameters', 'value', []); + + case 'new_leftprob' + AntibiasSectionAthena(obj, 'update_biashitfrac', value(LeftProb)); + + case 'new_legal_cbreak' + SoftPokeStayInterface(obj, 'set', 'soft_cp', 'Grace', legal_cbreak); + + case 'new_CP_duration' + + if random_PreStim_time == 1 && (value(PreStim_time) < value(PreStim_time_Min) || value(PreStim_time) > value(PreStim_time_Max)) + PreStim_time.value = value(PreStim_time_Min); + else + PreStim_time.value = value(PreStim_time); + end + + if random_A1_time == 1 && (value(A1_time) < value(A1_time_Min) || value(A1_time) > value(A1_time_Max)) + A1_time.value = value(A1_time_Min); + else + A1_time.value = value(A1_time); + end + + if random_prego_time == 1 && (value(time_bet_aud1_gocue) < value(time_bet_aud1_gocue_Min) || value(time_bet_aud1_gocue) > value(time_bet_aud1_gocue_Max)) + time_bet_aud1_gocue.value = value(time_bet_aud1_gocue_Min); + else + time_bet_aud1_gocue.value = value(time_bet_aud1_gocue); + end + + CP_duration.value= SettlingIn_time + PreStim_time + A1_time + time_bet_aud1_gocue; + Total_CP_duration.value = CP_duration + time_go_cue; %#ok<*NASGU> + SoftPokeStayInterface(obj, 'set', 'soft_cp', 'Duration', PreStim_time + A1_time + time_bet_aud1_gocue ); + + case 'new_time_go_cue' + Total_CP_duration.value = CP_duration + time_go_cue; + SoundInterface(obj, 'set', 'GoSound', 'Dur1', value(time_go_cue)); + + case 'training_stage' + + if value(use_training) == 1 + + disable(training_stage); % user cannot change the training stages + else + + enable(training_stage); % user can change the training stages + delay_time.value=0; + trials_in_stage.value=0; + + switch value(training_stage) + + case {1,2} %% learning the reward sound association -left or right led on -> poke -> sound+reward + + time_go_cue.value=0.200; + disable(SettlingIn_time); + disable(PreStim_time); + disable(A1_time); + disable(time_bet_aud1_gocue); + + + case {3,4} % Centre poke without the A1_Stim but has violation in 4 + + time_go_cue.value=0.100; + enable(SettlingIn_time); + disable(PreStim_time); + disable(A1_time); + disable(time_bet_aud1_gocue); + + if n_done_trials <1 && warmup_on ==1 + CP_duration.value=value(init_CP_duration); + else + CP_duration.value=CP_duration; + end + Total_CP_duration.value = CP_duration + time_go_cue; %#ok<*NASGU> + + case {5,6,7} % + + time_go_cue.value=0.100; + enable(SettlingIn_time); + enable(PreStim_time); + enable(A1_time); + enable(time_bet_aud1_gocue); + + if random_prego_time == 1 + time_bet_aud1_gocue = randi([time_bet_aud1_gocue_Min, time_bet_aud1_gocue_Max],1,1); + end + + if random_A1_time == 1 + A1_time = randi([A1_time_Min, A1_time_Max],1,1); + end + + if random_PreStim_time == 1 + PreStim_time = randi([PreStim_time_Min, PreStim_time_Max],1,1); + end + + if n_done_trials <1 && warmup_on ==1 + CP_duration.value=value(init_CP_duration); + else + CP_duration.value=SettlingIn_time + A1_time + PreStim_time + time_bet_aud1_gocue; + end + Total_CP_duration.value = CP_duration + time_go_cue; %#ok<*NASGU> + end + end + + case 'prepare_next_trial' + + delay_time.value=0; + trials_in_stage.value=0; + + switch value(training_stage) + + case {1,2} %% learning the reward sound association -left or right led on -> poke -> sound+reward + + time_go_cue.value=0.200; + disable(SettlingIn_time); + disable(PreStim_time); + disable(A1_time); + disable(time_bet_aud1_gocue); + + + case {3,4} % Centre poke without the A1_Stim but has violation in 4 + + time_go_cue.value=0.100; + enable(SettlingIn_time); + disable(PreStim_time); + disable(A1_time); + disable(time_bet_aud1_gocue); + + if n_done_trials <1 && warmup_on ==1 + CP_duration.value=value(init_CP_duration); + else + CP_duration.value=CP_duration; + end + Total_CP_duration.value = CP_duration + time_go_cue; %#ok<*NASGU> + + case {5,6,7} % + + time_go_cue.value=0.100; + enable(SettlingIn_time); + enable(PreStim_time); + enable(A1_time); + enable(time_bet_aud1_gocue); + + if random_prego_time == 1 + time_bet_aud1_gocue = randi([time_bet_aud1_gocue_Min, time_bet_aud1_gocue_Max],1,1); + end + + if random_A1_time == 1 + A1_time = randi([A1_time_Min, A1_time_Max],1,1); + end + + if random_PreStim_time == 1 + PreStim_time = randi([PreStim_time_Min, PreStim_time_Max],1,1); + end + + if n_done_trials <1 && warmup_on ==1 + CP_duration.value=value(init_CP_duration); + else + CP_duration.value=SettlingIn_time + A1_time + PreStim_time + time_bet_aud1_gocue; + end + Total_CP_duration.value = CP_duration + time_go_cue; %#ok<*NASGU> + + + end + + + %% update hit_history, previous_sides, etc + was_viol=false; + was_hit=false; + was_timeout=false; + if n_done_trials>0 + if ~isempty(parsed_events) + if isfield(parsed_events,'states') + if isfield(parsed_events.states,'timeout_state') + was_timeout=rows(parsed_events.states.timeout_state)>0; + end + if isfield(parsed_events.states,'violation_state') + was_viol=rows(parsed_events.states.violation_state)>0; + end + end + + end + + violation_history.value=[violation_history(:); was_viol]; + timeout_history.value=[timeout_history(:); was_timeout]; + + ParamsSection(obj,'update_side_history'); + + if ~was_viol && ~was_timeout + %was_hit=rows(parsed_events.states.hit_state)>0; + was_hit=rows(parsed_events.states.second_hit_state)==0; + hit_history.value=[hit_history(:); was_hit]; + + else + % There was a violation or timeout + hit_history.value=[hit_history(:); nan]; + end + + % Now calculate the deltaF and sides - this maybe interesting + % even in a violation or timeout case. + + fn=fieldnames(parsed_events.states); + led_states=find(strncmp('led',fn,3)); + deltaF=0; + n_l=0; + n_r=0; + for lx=1:numel(led_states) + lind=led_states(lx); + if rows(parsed_events.states.(fn{lind}))>0 + if fn{lind}(end)=='l' + deltaF=deltaF-1; + n_l=n_l+1; + elseif fn{lind}(end)=='r' + deltaF=deltaF+1; + n_r=n_r+1; + elseif fn{lind}(end)=='b' + n_l=n_l+1; + n_r=n_r+1; + + end + end + + end + + % if deltaF>0 then a right poke is a hit + % if deltaF<0 then a left poke is a hit + + deltaf_history.value=[deltaf_history(:); deltaF]; + + end + + if antibias_LRprob ==1 + if n_done_trials >ntrial_correct_bias && ~was_viol && ~was_timeout + nonan_hit_history=value(hit_history); + nonan_hit_history(isnan(nonan_hit_history))=[]; + nonan_previous_sides=value(previous_sides); + nan_history=value(hit_history); + nonan_previous_sides(isnan(nan_history))=[]; + AntibiasSectionAthena(obj, 'update', value(LeftProb), nonan_hit_history(:)',nonan_previous_sides(:)); % <~> Transposed hit history so that it is the expected column vector. (Antibias errors out otherwise.) 2007.09.05 01:39 + end + + + if ~isinf(MaxSame) && length(previous_sides) > MaxSame && ... + all(previous_sides(n_done_trials-MaxSame+1:n_done_trials) == previous_sides(n_done_trials)) %#ok + if previous_sides(end)=='l' + ThisTrial.value = 'RIGHT'; + else + ThisTrial.value = 'LEFT'; + end + else + choiceprobs = AntibiasSectionAthena(obj, 'get_posterior_probs'); + if rand(1) <= choiceprobs(1) + ThisTrial.value = 'LEFT'; + else + ThisTrial.value = 'RIGHT'; + end + end + + else + if (rand(1)<=LeftProb) + ThisTrial.value='LEFT'; + + else + ThisTrial.value='RIGHT'; + end + + end + + + + +% %% Do the anti-bias with changing reward delivery +% % reset anti-bias +% left_wtr_mult.value=1; +% right_wtr_mult.value=1; +% if n_done_trials>ntrial_correct_bias && antibias_wtr_mult==1 +% hh=hit_history(n_done_trials-ntrial_correct_bias:n_done_trials); +% ps=previous_sides(n_done_trials-ntrial_correct_bias:n_done_trials); +% +% right_hit=nanmean(hh(ps=='r')); +% left_hit=nanmean(hh(ps=='l')); +% +% if abs(right_hit-left_hit) + + case 'get_left_prob' + x = value(LeftProb); + + case 'get_cp_history' + x = cell2mat(get_history(CP_duration)); + + case 'get_stimdur_history' + x = cell2mat(get_history(A1_time)); + + case 'update_side_history' + if strcmp(ThisTrial, 'LEFT') + ps=value(previous_sides); + ps(n_done_trials)='l'; + previous_sides.value=ps; + + else + ps=value(previous_sides); + ps(n_done_trials)='r'; + previous_sides.value=ps; + end + + case 'get_current_side' + if strcmp(ThisTrial, 'LEFT') + x = 'l'; %#ok + else + x = 'r'; + end + + + case 'close' + % Delete all SoloParamHandles who belong to this object and whose + % fullname starts with the name of this mfile: + delete_sphandle('owner', ['^@' class(obj) '$'], ... + 'fullname', ['^' mfilename]); + + case 'reinit' + currfig = double(gcf); + + % Get the original GUI position and figure: + x = my_gui_info(1); y = my_gui_info(2); figure(my_gui_info(3)); + + % Delete all SoloParamHandles who belong to this object and whose + % fullname starts with the name of this mfile: + delete_sphandle('owner', ['^@' class(obj) '$'], ... + 'fullname', ['^' mfilename]); + + % Reinitialise at the original GUI position and figure: + [x, y] = feval(mfilename, obj, 'init', x, y); + + % Restore the current figure: + figure(currfig); + +end + + diff --git a/Protocols/@Arpit_CentrePokeTraining/ParamsSection.m b/Protocols/@Arpit_CentrePokeTraining/ParamsSection.m index 94b12ce7..846306cb 100644 --- a/Protocols/@Arpit_CentrePokeTraining/ParamsSection.m +++ b/Protocols/@Arpit_CentrePokeTraining/ParamsSection.m @@ -40,11 +40,12 @@ % Reward Collection NumeditParam(obj, 'RewardCollection_duration', 10, x,y,'label','RewardCollection_dur','TooltipString','Wait until rat collects the reward else a timeout'); next_row(y); - NumeditParam(obj, 'SideLed_duration', 0.5, x,y,'label','Side LED duration','TooltipString','Duration of SideLed'); + NumeditParam(obj, 'SideLed_duration', 1, x,y,'label','Side LED duration','TooltipString','Duration of SideLed'); % Centre Poke next_row(y); NumeditParam(obj, 'legal_cbreak', 0.1, x,y, 'position', [x, y, 175 20], 'TooltipString','Time in sec for which it is ok to be outside the center port before a violation occurs.'); - next_row(y); + set_callback(legal_cbreak, {mfilename, 'new_legal_cbreak'}); + next_row(y); NumeditParam(obj, 'SettlingIn_time', 0.2, x,y, 'position', [x, y, 175 20], 'TooltipString','Initial settling period during which there is no violation'); next_row(y); NumeditParam(obj, 'A1_time_Min', 0.4, x,y,'label','Min time AUD1','TooltipString','Min value to select the Duration of fixed stimulus'); @@ -161,10 +162,6 @@ 'drink_time';'reward_delay';'left_wtr_mult';... 'right_wtr_mult';'antibias_wtr_mult';... 'timeout_iti';'violation_iti'}); - - % SoloFunctionAddVars('StimulatorSection', 'ro_args', ... - % {'A1_time';'time_bet_aud1_gocue';'time_go_cue'; ... - % 'PreStim_time';'CP_duration';'Total_CP_duration'}); % History of hit/miss: SoloParamHandle(obj, 'deltaf_history', 'value', []); @@ -178,7 +175,9 @@ case 'new_leftprob' AntibiasSectionAthena(obj, 'update_biashitfrac', value(LeftProb)); - + case 'new_legal_cbreak' + SoftPokeStayInterface(obj, 'set', 'soft_cp', 'Grace', legal_cbreak); + case 'new_CP_duration' if random_PreStim_time == 1 && (value(PreStim_time) < value(PreStim_time_Min) || value(PreStim_time) > value(PreStim_time_Max)) @@ -201,6 +200,7 @@ CP_duration.value= SettlingIn_time + PreStim_time + A1_time + time_bet_aud1_gocue; Total_CP_duration.value = CP_duration + time_go_cue; %#ok<*NASGU> + SoftPokeStayInterface(obj, 'set', 'soft_cp', 'Duration', PreStim_time + A1_time + time_bet_aud1_gocue ); case 'new_time_go_cue' Total_CP_duration.value = CP_duration + time_go_cue; @@ -214,11 +214,7 @@ else enable(training_stage); % user can change the training stages - delay_time.value=0; trials_in_stage.value=0; - reward_delay.value=0.01; - left_prob.value=0.5; - right_prob.value=0.5; switch value(training_stage) @@ -277,12 +273,6 @@ case 'prepare_next_trial' - delay_time.value=0; - trials_in_stage.value=0; - reward_delay.value=0.01; - left_prob.value=0.5; - right_prob.value=0.5; - switch value(training_stage) case {1,2} %% learning the reward sound association -left or right led on -> poke -> sound+reward From 495533f67e93281212b580d1131a7e8fd97423fb Mon Sep 17 00:00:00 2001 From: ArpitAgarwal Date: Wed, 19 Mar 2025 11:12:20 +0000 Subject: [PATCH 024/164] still making the protocol Arpit_CentrePokeTraining protocol workable --- .../Arpit_CentrePokeTraining.asv | 264 ------------------ .../ParamsSection.asv | 52 ++-- .../@Arpit_CentrePokeTraining/ParamsSection.m | 50 ++-- 3 files changed, 49 insertions(+), 317 deletions(-) delete mode 100644 Protocols/@Arpit_CentrePokeTraining/Arpit_CentrePokeTraining.asv diff --git a/Protocols/@Arpit_CentrePokeTraining/Arpit_CentrePokeTraining.asv b/Protocols/@Arpit_CentrePokeTraining/Arpit_CentrePokeTraining.asv deleted file mode 100644 index ab5a83fb..00000000 --- a/Protocols/@Arpit_CentrePokeTraining/Arpit_CentrePokeTraining.asv +++ /dev/null @@ -1,264 +0,0 @@ -% Arpit_CentrePokeTraining protocol -% Arpit, 12 March 2025 - -function [obj] = Arpit_CentrePokeTraining(varargin) - -% Default object is of our own class (mfilename); -% we inherit only from Plugins - -obj = class(struct, mfilename, pokesplot2, saveload, sessionmodel2, soundmanager, soundui, antibias, ... - water, distribui, punishui, comments, soundtable, sqlsummary,reinforcement,softpokestay2); - -%--------------------------------------------------------------- -% BEGIN SECTION COMMONSoundTableSection TO ALL PROTOCOLS, DO NOT MODIFY -%--------------------------------------------------------------- - -% If creating an empty object, return without further ado: -if nargin==0 || (nargin==1 && ischar(varargin{1}) && strcmp(varargin{1}, 'empty')) - return; -end - -if isa(varargin{1}, mfilename) % If first arg is an object of this class itself, we are - % Most likely responding to a callback from - % a SoloParamHandle defined in this mfile. - if length(varargin) < 2 || ~ischar(varargin{2}) - error(['If called with a "%s" object as first arg, a second arg, a ' ... - 'string specifying the action, is required\n']); - else - action = varargin{2}; varargin = varargin(3:end); %#ok - end -else % Ok, regular call with first param being the action string. - action = varargin{1}; varargin = varargin(2:end); %#ok -end - -GetSoloFunctionArgs(obj); - -switch action - - %% init - case 'init' - dispatcher('set_trialnum_indicator_flag'); - hackvar = 10; SoloFunctionAddVars('SessionModel', 'ro_args', 'hackvar'); %#ok - SoloParamHandle(obj, 'myfig', 'saveable', 0); myfig.value = figure; - - % Make the title of the figure be the protocol name, and if someone tries - % to close this figure, call dispatcher's close_protocol function, so it'll know - % to take it off the list of open protocols. - name = mfilename; - set(value(myfig), 'Name', name, 'Tag', name, ... - 'closerequestfcn', 'dispatcher(''close_protocol'')', 'MenuBar', 'none'); - % At this point we have one SoloParamHandle, myfig - % Let's put the figure where we want it and give it a reasonable size: - set(value(myfig), 'Position', [485 144 850 680]); - - SoloParamHandle(obj, 'nsessions_healthy_number_of_pokes', 'value', 0, 'save_with_settings', 1); - SoloParamHandle(obj, 'post_DelComp_protocol', 'value', '', 'save_with_settings', 1); - SoloParamHandle(obj, 'post_DelComp_settings_filename', 'value', '', 'save_with_settings', 1); - - - SoloParamHandle(obj, 'hit_history', 'value', []); - DeclareGlobals(obj, 'ro_args', {'hit_history'}); - SoloFunctionAddVars('ParamsSection', 'rw_args', 'hit_history'); - - SoloParamHandle(obj, 'violation_history', 'value', []); - DeclareGlobals(obj, 'ro_args', {'violation_history'}); - SoloFunctionAddVars('ParamsSection', 'rw_args', 'violation_history'); - - SoloParamHandle(obj, 'timeout_history', 'value', []); - DeclareGlobals(obj, 'ro_args', {'timeout_history'}); - SoloFunctionAddVars('ParamsSection', 'rw_args', 'timeout_history'); - - SoloParamHandle(obj, 'Stage_Trial_Counter', 'value', zeros(1,7)); - DeclareGlobals(obj, 'ro_args', {'Stage_Trial_Counter'}); - SoloFunctionAddVars('ParamsSection', 'rw_args', 'Stage_Trial_Counter'); - - SoloParamHandle(obj, 'Stage_ValidTrial_Counter', 'value', zeros(1,7)); - DeclareGlobals(obj, 'ro_args', {'Stage_ValidTrial_Counter'}); - SoloFunctionAddVars('ParamsSection', 'rw_args', 'Stage_ValidTrial_Counter'); - - SoloParamHandle(obj, 'Stage_ViolationTrial_Counter', 'value', zeros(1,7)); - DeclareGlobals(obj, 'ro_args', {'Stage_ViolationTrial_Counter'}); - SoloFunctionAddVars('ParamsSection', 'rw_args', 'Stage_ViolationTrial_Counter'); - - SoloParamHandle(obj, 'Stage_TimeoutTrial_Counter', 'value', zeros(1,7)); - DeclareGlobals(obj, 'ro_args', {'Stage_TimeoutTrial_Counter'}); - SoloFunctionAddVars('ParamsSection', 'rw_args', 'Stage_TimeoutTrial_Counter'); - - - SoundManagerSection(obj, 'init'); - x = 5; y = 5; % Initial position on main GUI window - [x, y] = SavingSection(obj, 'init', x, y); - - %% slow ramp up of water amount - %%the water volume is controlled by a 5-parameter logistic function: WaterAmount(t) = maxasymp + (minasymp/(1+(t/inflp)^slp).^assym) - NumeditParam(obj, 'maxasymp', 38, x,y,'label','maxasymp','TooltipString',... - 'the water volume is controlled by a 5-parameter logistic function: WaterAmount(trialnum) = maxasymp + (minasymp/(1+(trialnum/inflp)^slp).^assym)'); - next_row(y); - NumeditParam(obj, 'slp', 3, x,y,'label','slp','TooltipString','Water Modulation: Slope of the logistic function'); - next_row(y); - NumeditParam(obj, 'inflp', 350, x,y,'label','inflp','TooltipString','Water Modulation: concentration at the inflection point'); - next_row(y); - NumeditParam(obj, 'minasymp', -21, x,y,'label','inflp','TooltipString','Water Modulation: minimum asymptote'); - next_row(y); - NumeditParam(obj, 'assym', 0.7, x,y,'label','assym','TooltipString','Water Modulation: asymmetry factor'); - next_row(y); - DispParam(obj, 'trial_1', 0, x, y, 'TooltipString', 'uL on first trial'); - next_row(y); - DispParam(obj, 'trial_150', 0, x, y, 'TooltipString', 'uL on trial 150'); - next_row(y); - DispParam(obj, 'trial_300', 0, x, y, 'TooltipString', 'uL on trial 300'); - next_row(y); - set_callback({maxasymp;slp;inflp;minasymp;assym}, {mfilename, 'change_water_modulation_params'}); - feval(mfilename, obj, 'change_water_modulation_params'); - - %AthenaSMA changed to SoundCatSMA (From AthenaDelayComp) - SoloFunctionAddVars('Arpit_CentrePokeTrainingSMA', 'ro_args', ... - {'maxasymp';'slp';'inflp';'minasymp';'assym'}); - [x, y] = WaterValvesSection(obj, 'init', x, y); - - % For plotting with the pokesplot plugin, we need to tell it what - % colors to plot with: - my_state_colors = Arpit_CentrePokeTrainingSMA(obj, 'get_state_colors'); - % In pokesplot, the poke colors have a default value, so we don't need - % to specify them, but here they are so you know how to change them. - my_poke_colors = struct( ... - 'L', 0.6*[1 0.66 0], ... - 'C', [0 0 0], ... - 'R', 0.9*[1 0.66 0]); - - [x, y] = PokesPlotSection(obj, 'init', x, y, ... - struct('states', my_state_colors, 'pokes', my_poke_colors)); next_row(y); - - [x, y] = CommentsSection(obj, 'init', x, y); - - % [x, y] = PunishmentSection(obj, 'init', x, y); %#ok - - next_column(x); y=5; - [x, y] = OverallPerformanceSection(obj, 'init', x, y); - [x, y] = ParamsSection(obj, 'init', x, y); %#ok - [x, y] = SoundSection(obj,'init',x,y); - [x, y] = SoftPokeStayInterface(obj, 'add', 'soft_cp', x, y); - - figpos = get(double(gcf), 'Position'); - [expmtr, rname]=SavingSection(obj, 'get_info'); - HeaderParam(obj, 'prot_title', [mfilename ': ' expmtr ', ' rname], x, y, 'position', [10 figpos(4)-25, 800 20]); - - Arpit_CentrePokeTrainingSMA(obj, 'init'); - - SessionDefinition(obj, 'init', x, y, value(myfig)); next_row(y, 2); %#ok - SessionDefinition(obj, 'set_old_style_parsing_flag',0); - - feval(mfilename, obj, 'prepare_next_trial'); - - %% change_water_modulation_params - case 'change_water_modulation_params' - display_guys = [1 150 300]; - for i=1:numel(display_guys) - t = display_guys(i); - - myvar = eval(sprintf('trial_%d', t)); - myvar.value = maxasymp + (minasymp/(1+(t/inflp)^slp).^assym); - end - - %% prepare next trial - case 'prepare_next_trial' - - ParamsSection(obj, 'prepare_next_trial'); - % Run SessionDefinition *after* ParamsSection so we know whether the - % trial was a violation or not - SessionDefinition(obj, 'next_trial'); - OverallPerformanceSection(obj, 'evaluate'); - SoundManagerSection(obj, 'send_not_yet_uploaded_sounds'); - - nTrials.value = n_done_trials; - - [sma, prepare_next_trial_states] = Arpit_CentrePokeTrainingSMA(obj, 'prepare_next_trial'); - - % Default behavior of following call is that every 20 trials, the data - % gets saved, not interactive, no commit to CVS. - SavingSection(obj, 'autosave_data'); - - CommentsSection(obj, 'clear_history'); % Make sure we're not storing unnecessary history - if n_done_trials==1 % Auto-append date for convenience. - CommentsSection(obj, 'append_date'); CommentsSection(obj, 'append_line', ''); - end - - if n_done_trials==1 - [expmtr, rname]=SavingSection(obj, 'get_info'); - prot_title.value=[mfilename ' on rig ' get_hostname ' : ' expmtr ', ' rname '. Started at ' datestr(now, 'HH:MM')]; - end - - try send_n_done_trials(obj); - end - - %% trial_completed - case 'trial_completed' - - % Do any updates in the protocol that need doing: - feval(mfilename, 'update'); - % And PokesPlot needs completing the trial: - PokesPlotSection(obj, 'trial_completed'); - - %% update - case 'update' - PokesPlotSection(obj, 'update'); - - - %% close - case 'close' - PokesPlotSection(obj, 'close'); - ParamsSection(obj, 'close'); - StimulusSection(obj,'close'); - - if exist('myfig', 'var') && isa(myfig, 'SoloParamHandle') && ishandle(value(myfig)) %#ok - delete(value(myfig)); - end - delete_sphandle('owner', ['^@' class(obj) '$']); - - - %% end_session - case 'end_session' - prot_title.value = [value(prot_title) ', Ended at ' datestr(now, 'HH:MM')]; - - - %% pre_saving_settings - case 'pre_saving_settings' - - SessionDefinition(obj, 'run_eod_logic_without_saving'); - perf = OverallPerformanceSection(obj, 'evaluate'); - cp_durs = ParamsSection(obj, 'get_cp_history'); - - [stim1dur] = ParamsSection(obj,'get_stimdur_history'); - -% CommentsSection(obj, 'append_line', ... -% sprintf(['ntrials = %d, violations = %.2f, timeouts=%.2f, hits = %.2f\n', ... -% 'pre-Go cue went from %.3f to %.3f (delta=%.3f)\n', ... -% 'Low = %.2f, High = %.2f'], ... -% perf(1), perf(2), perf(3), perf(6), cp_durs(1), cp_durs(end), cp_durs(end)-cp_durs(1), classperf(1),classperf(2))); - - pd.hits=hit_history(:); - pd.sides=previous_sides(:); - pd.viols=violation_history(:); - pd.timeouts=timeout_history(:); -% pd.performance=tot_perf(:); - pd.cp_durs=cp_durs(:); - - -% pd.stimuli=stimuli(:); - % Athena: look into pair_history perhaps stimulus_history and stimuli - % are the same - %pd.stimulus=stimulus_history(:); - - pd.stim1dur=stim1dur(:); - - %pd.stimul=stim_history(:); - - sendsummary(obj,'protocol_data',pd); - - %% otherwise - otherwise - warning('Unknown action! "%s"\n', action); -end - -return; - diff --git a/Protocols/@Arpit_CentrePokeTraining/ParamsSection.asv b/Protocols/@Arpit_CentrePokeTraining/ParamsSection.asv index 862acb04..5b01f33f 100644 --- a/Protocols/@Arpit_CentrePokeTraining/ParamsSection.asv +++ b/Protocols/@Arpit_CentrePokeTraining/ParamsSection.asv @@ -37,27 +37,27 @@ switch action SubheaderParam(obj, 'title', 'Params Section', x, y); next_row(y, 1.5); next_column(x); y = 5; + next_row(y); + NumeditParam(obj, 'reward_delay', 0.01, x,y,'label','Reward Delay','TooltipString','Delay between side poke and reward delivery'); + next_row(y); + NumeditParam(obj, 'drink_time', 1, x,y,'label','Drink Time','TooltipString','waits to finish water delivery'); + next_row(y); + NumeditParam(obj, 'timeout_iti', 5, x,y,'label','No Choice Timeout','TooltipString','ITI on timeout trials'); + next_row(y); + NumeditParam(obj, 'violation_iti', 1, x,y,'label','Violation Timeout','TooltipString','Center poke violation duration'); % Reward Collection + next_row(y); NumeditParam(obj, 'RewardCollection_duration', 10, x,y,'label','RewardCollection_dur','TooltipString','Wait until rat collects the reward else a timeout'); next_row(y); NumeditParam(obj, 'SideLed_duration', 1, x,y,'label','Side LED duration','TooltipString','Duration of SideLed'); % Centre Poke next_row(y); - NumeditParam(obj, 'legal_cbreak', 0.1, x,y, 'position', [x, y, 175 20], 'TooltipString','Time in sec for which it is ok to be outside the center port before a violation occurs.'); + NumeditParam(obj, 'legal_cbreak', 0.1, x,y, 'TooltipString','Time in sec for which it is ok to be outside the center port before a violation occurs.'); set_callback(legal_cbreak, {mfilename, 'new_legal_cbreak'}); next_row(y); - NumeditParam(obj, 'SettlingIn_time', 0.2, x,y, 'position', [x, y, 175 20], 'TooltipString','Initial settling period during which there is no violation'); - next_row(y); - NumeditParam(obj, 'A1_time_Min', 0.4, x,y,'label','Min time AUD1','TooltipString','Min value to select the Duration of fixed stimulus'); - set_callback(A1_time_Min, {mfilename, 'new_CP_duration'}); - next_row(y); - NumeditParam(obj, 'A1_time', 0.4, x,y,'label','AUD1 Time','TooltipString','Actual Duration of fixed stimulus'); - next_row(y); - set_callback(A1_time, {mfilename, 'new_CP_duration'}); - next_row(y); - NumeditParam(obj, 'A1_time_Max', 0.4, x,y,'label','Max time AUD1','TooltipString','Max value to select the Duration of fixed stimulus'); - set_callback(A1_time_Max, {mfilename, 'new_CP_duration'}); + NumeditParam(obj, 'SettlingIn_time', 0.2, x,y, 'TooltipString','Initial settling period during which there is no violation'); next_row(y); + % PreStim NumeditParam(obj, 'PreStim_time_Min', 0.20, x,y,'label','Pre-Stim Min','TooltipString','Min Time in NIC before starting the stimulus'); set_callback(PreStim_time_Min, {mfilename, 'new_CP_duration'}); next_row(y); @@ -67,11 +67,21 @@ switch action NumeditParam(obj, 'PreStim_time_Max', 0.40, x,y,'label','Pre-Stim Max','TooltipString','Max Time in NIC before starting the stimulus'); set_callback(PreStim_time_Max, {mfilename, 'new_CP_duration'}); next_row(y); + % A1 Time + NumeditParam(obj, 'A1_time_Min', 0.4, x,y,'label','Min time AUD1','TooltipString','Min value to select the Duration of fixed stimulus'); + set_callback(A1_time_Min, {mfilename, 'new_CP_duration'}); + next_row(y); + NumeditParam(obj, 'A1_time', 0.4, x,y,'label','AUD1 Time','TooltipString','Actual Duration of fixed stimulus'); + set_callback(A1_time, {mfilename, 'new_CP_duration'}); + next_row(y); + NumeditParam(obj, 'A1_time_Max', 0.4, x,y,'label','Max time AUD1','TooltipString','Max value to select the Duration of fixed stimulus'); + set_callback(A1_time_Max, {mfilename, 'new_CP_duration'}); + next_row(y); + % Time b/w stim and Go Cue NumeditParam(obj, 'time_bet_aud1_gocue_Min', 0.2, x,y,'label','Min A1-GoCue time','TooltipString','Min time between the end of the stimulus and the go cue '); set_callback(time_bet_aud1_gocue_Min, {mfilename, 'new_CP_duration'}); next_row(y); NumeditParam(obj, 'time_bet_aud1_gocue', 0.2, x,y,'label','A1-GoCue time','TooltipString','Actual time between the end of the stimulus and the go cue '); - next_row(y); set_callback(time_bet_aud1_gocue, {mfilename, 'new_CP_duration'}); next_row(y); NumeditParam(obj, 'time_bet_aud1_gocue_Max', 2, x,y,'label','Max A1-GoCue time','TooltipString','Max time between the end of the stimulus and the go cue '); @@ -82,7 +92,7 @@ switch action DispParam(obj, 'CP_duration', PreStim_time+A1_time+time_bet_aud1_gocue, x,y,'label','CP duration','TooltipString','Duration of Nose in Central Poke before Go cue starts (see Total_CP_duration)'); set_callback(CP_duration, {mfilename, 'new_CP_duration'}); next_row(y); - NumeditParam(obj, 'time_go_cue' ,0.1, x,y,'label','Go Cue Duration','TooltipString','duration of go cue (see Total_CP_duration)'); + NumeditParam(obj, 'time_go_cue' ,0.2, x,y,'label','Go Cue Duration','TooltipString','duration of go cue (see Total_CP_duration)'); set_callback(time_go_cue, {mfilename, 'new_time_go_cue'}); next_row(y); DispParam(obj, 'Total_CP_duration', CP_duration+time_go_cue, x, y, 'TooltipString', 'Total expected(rat can poke out anytime after Go cue onset) nose in center port time, in secs. Sum of CP_duration and Go Cue duration'); %#ok<*NODEF> @@ -93,16 +103,6 @@ switch action 'TooltipString', sprintf(['If on (Yellow) then it applies the initial warming up phase, during which the\n',... 'CP_duration starts small and gradually grows to last_session_max_cp_duration'])); next_row(y); - - NumeditParam(obj, 'reward_delay', 0.01, x,y,'label','Reward Delay','TooltipString','Delay between side poke and reward delivery'); - next_row(y); - NumeditParam(obj, 'drink_time', 1, x,y,'label','Drink Time','TooltipString','waits to finish water delivery'); - next_row(y); - NumeditParam(obj, 'timeout_iti', 5, x,y,'label','No Choice Timeout','TooltipString','ITI on timeout trials'); - next_row(y); - NumeditParam(obj, 'violation_iti', 1, x,y,'label','Violation Timeout','TooltipString','Center poke violation duration'); - - next_row(y); ToggleParam(obj, 'random_PreStim_time', 0, x,y,... 'OnString', 'random PreStim_time ON',... 'OffString', 'random PreStim_time OFF',... @@ -214,7 +214,6 @@ switch action else enable(training_stage); % user can change the training stages - delay_time.value=0; trials_in_stage.value=0; switch value(training_stage) @@ -274,9 +273,6 @@ switch action case 'prepare_next_trial' - delay_time.value=0; - trials_in_stage.value=0; - switch value(training_stage) case {1,2} %% learning the reward sound association -left or right led on -> poke -> sound+reward diff --git a/Protocols/@Arpit_CentrePokeTraining/ParamsSection.m b/Protocols/@Arpit_CentrePokeTraining/ParamsSection.m index 846306cb..b73d0b57 100644 --- a/Protocols/@Arpit_CentrePokeTraining/ParamsSection.m +++ b/Protocols/@Arpit_CentrePokeTraining/ParamsSection.m @@ -37,27 +37,27 @@ SubheaderParam(obj, 'title', 'Params Section', x, y); next_row(y, 1.5); next_column(x); y = 5; + next_row(y); + NumeditParam(obj, 'reward_delay', 0.01, x,y,'label','Reward Delay','TooltipString','Delay between side poke and reward delivery'); + next_row(y); + NumeditParam(obj, 'drink_time', 1, x,y,'label','Drink Time','TooltipString','waits to finish water delivery'); + next_row(y); + NumeditParam(obj, 'timeout_iti', 5, x,y,'label','No Choice Timeout','TooltipString','ITI on timeout trials'); + next_row(y); + NumeditParam(obj, 'violation_iti', 1, x,y,'label','Violation Timeout','TooltipString','Center poke violation duration'); % Reward Collection + next_row(y); NumeditParam(obj, 'RewardCollection_duration', 10, x,y,'label','RewardCollection_dur','TooltipString','Wait until rat collects the reward else a timeout'); next_row(y); NumeditParam(obj, 'SideLed_duration', 1, x,y,'label','Side LED duration','TooltipString','Duration of SideLed'); % Centre Poke next_row(y); - NumeditParam(obj, 'legal_cbreak', 0.1, x,y, 'position', [x, y, 175 20], 'TooltipString','Time in sec for which it is ok to be outside the center port before a violation occurs.'); + NumeditParam(obj, 'legal_cbreak', 0.1, x,y, 'TooltipString','Time in sec for which it is ok to be outside the center port before a violation occurs.'); set_callback(legal_cbreak, {mfilename, 'new_legal_cbreak'}); next_row(y); - NumeditParam(obj, 'SettlingIn_time', 0.2, x,y, 'position', [x, y, 175 20], 'TooltipString','Initial settling period during which there is no violation'); - next_row(y); - NumeditParam(obj, 'A1_time_Min', 0.4, x,y,'label','Min time AUD1','TooltipString','Min value to select the Duration of fixed stimulus'); - set_callback(A1_time_Min, {mfilename, 'new_CP_duration'}); - next_row(y); - NumeditParam(obj, 'A1_time', 0.4, x,y,'label','AUD1 Time','TooltipString','Actual Duration of fixed stimulus'); - next_row(y); - set_callback(A1_time, {mfilename, 'new_CP_duration'}); - next_row(y); - NumeditParam(obj, 'A1_time_Max', 0.4, x,y,'label','Max time AUD1','TooltipString','Max value to select the Duration of fixed stimulus'); - set_callback(A1_time_Max, {mfilename, 'new_CP_duration'}); + NumeditParam(obj, 'SettlingIn_time', 0.2, x,y, 'TooltipString','Initial settling period during which there is no violation'); next_row(y); + % PreStim NumeditParam(obj, 'PreStim_time_Min', 0.20, x,y,'label','Pre-Stim Min','TooltipString','Min Time in NIC before starting the stimulus'); set_callback(PreStim_time_Min, {mfilename, 'new_CP_duration'}); next_row(y); @@ -67,11 +67,21 @@ NumeditParam(obj, 'PreStim_time_Max', 0.40, x,y,'label','Pre-Stim Max','TooltipString','Max Time in NIC before starting the stimulus'); set_callback(PreStim_time_Max, {mfilename, 'new_CP_duration'}); next_row(y); + % A1 Time + NumeditParam(obj, 'A1_time_Min', 0.4, x,y,'label','Min time AUD1','TooltipString','Min value to select the Duration of fixed stimulus'); + set_callback(A1_time_Min, {mfilename, 'new_CP_duration'}); + next_row(y); + NumeditParam(obj, 'A1_time', 0.4, x,y,'label','AUD1 Time','TooltipString','Actual Duration of fixed stimulus'); + set_callback(A1_time, {mfilename, 'new_CP_duration'}); + next_row(y); + NumeditParam(obj, 'A1_time_Max', 0.4, x,y,'label','Max time AUD1','TooltipString','Max value to select the Duration of fixed stimulus'); + set_callback(A1_time_Max, {mfilename, 'new_CP_duration'}); + next_row(y); + % Time b/w stim and Go Cue NumeditParam(obj, 'time_bet_aud1_gocue_Min', 0.2, x,y,'label','Min A1-GoCue time','TooltipString','Min time between the end of the stimulus and the go cue '); set_callback(time_bet_aud1_gocue_Min, {mfilename, 'new_CP_duration'}); next_row(y); NumeditParam(obj, 'time_bet_aud1_gocue', 0.2, x,y,'label','A1-GoCue time','TooltipString','Actual time between the end of the stimulus and the go cue '); - next_row(y); set_callback(time_bet_aud1_gocue, {mfilename, 'new_CP_duration'}); next_row(y); NumeditParam(obj, 'time_bet_aud1_gocue_Max', 2, x,y,'label','Max A1-GoCue time','TooltipString','Max time between the end of the stimulus and the go cue '); @@ -82,7 +92,7 @@ DispParam(obj, 'CP_duration', PreStim_time+A1_time+time_bet_aud1_gocue, x,y,'label','CP duration','TooltipString','Duration of Nose in Central Poke before Go cue starts (see Total_CP_duration)'); set_callback(CP_duration, {mfilename, 'new_CP_duration'}); next_row(y); - NumeditParam(obj, 'time_go_cue' ,0.1, x,y,'label','Go Cue Duration','TooltipString','duration of go cue (see Total_CP_duration)'); + NumeditParam(obj, 'time_go_cue' ,0.2, x,y,'label','Go Cue Duration','TooltipString','duration of go cue (see Total_CP_duration)'); set_callback(time_go_cue, {mfilename, 'new_time_go_cue'}); next_row(y); DispParam(obj, 'Total_CP_duration', CP_duration+time_go_cue, x, y, 'TooltipString', 'Total expected(rat can poke out anytime after Go cue onset) nose in center port time, in secs. Sum of CP_duration and Go Cue duration'); %#ok<*NODEF> @@ -93,16 +103,6 @@ 'TooltipString', sprintf(['If on (Yellow) then it applies the initial warming up phase, during which the\n',... 'CP_duration starts small and gradually grows to last_session_max_cp_duration'])); next_row(y); - - NumeditParam(obj, 'reward_delay', 0.01, x,y,'label','Reward Delay','TooltipString','Delay between side poke and reward delivery'); - next_row(y); - NumeditParam(obj, 'drink_time', 1, x,y,'label','Drink Time','TooltipString','waits to finish water delivery'); - next_row(y); - NumeditParam(obj, 'timeout_iti', 5, x,y,'label','No Choice Timeout','TooltipString','ITI on timeout trials'); - next_row(y); - NumeditParam(obj, 'violation_iti', 1, x,y,'label','Violation Timeout','TooltipString','Center poke violation duration'); - - next_row(y); ToggleParam(obj, 'random_PreStim_time', 0, x,y,... 'OnString', 'random PreStim_time ON',... 'OffString', 'random PreStim_time OFF',... @@ -176,7 +176,7 @@ AntibiasSectionAthena(obj, 'update_biashitfrac', value(LeftProb)); case 'new_legal_cbreak' - SoftPokeStayInterface(obj, 'set', 'soft_cp', 'Grace', legal_cbreak); + SoftPokeStayInterface(obj, 'set', 'soft_cp', 'Grace', value(legal_cbreak)); case 'new_CP_duration' From ba92aca693033e02531f71833d3143789b96ff85 Mon Sep 17 00:00:00 2001 From: ArpitAgarwal Date: Wed, 19 Mar 2025 11:20:56 +0000 Subject: [PATCH 025/164] still making the protocol Arpit_CentrePokeTraining protocol workable --- Protocols/@Arpit_CentrePokeTraining/ParamsSection.m | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Protocols/@Arpit_CentrePokeTraining/ParamsSection.m b/Protocols/@Arpit_CentrePokeTraining/ParamsSection.m index b73d0b57..01f5ef35 100644 --- a/Protocols/@Arpit_CentrePokeTraining/ParamsSection.m +++ b/Protocols/@Arpit_CentrePokeTraining/ParamsSection.m @@ -126,10 +126,10 @@ NumeditParam(obj,'trials_in_stage',1,x,y,'label','Trial Counter'); next_row(y); NumeditParam(obj,'training_stage',1,x,y,'label','Training Stage'); - set_callback(training_stage, {mfilename, 'Training_Stage'}); + set_callback(training_stage, {mfilename, 'Changed_Training_Stage'}); next_row(y); ToggleParam(obj,'use_training',0,x,y,'OnString','Using Autotrain','OffString','Manual Settings'); - set_callback(use_training, {mfilename, 'Training_Stage'}); + set_callback(use_training, {mfilename, 'Changed_Training_Stage'}); next_row(y); NumeditParam(obj, 'ntrial_correct_bias', 0, x, y, ... @@ -206,7 +206,7 @@ Total_CP_duration.value = CP_duration + time_go_cue; SoundInterface(obj, 'set', 'GoSound', 'Dur1', value(time_go_cue)); - case 'training_stage' + case 'Changed_Training_Stage' if value(use_training) == 1 From c8c11f8674a8dfa2e527af54791255936c71447a Mon Sep 17 00:00:00 2001 From: ArpitAgarwal Date: Mon, 24 Mar 2025 17:48:37 +0000 Subject: [PATCH 026/164] removed anti bias, added another protocol to compare,added the automated training stages, renamed overall performance section to sessionperformance with some added data to be tracked over sessions and among others --- .../@sessionmodel2/SessionDefinition.m | 2 +- .../AntibiasSectionAthena.m | 315 ---- .../Arpit_CentrePokeTraining.m | 39 +- .../Arpit_CentrePokeTrainingSMA.m | 138 +- .../OverallPerformanceSection.m | 127 -- .../ParamsSection.asv | 532 ------- .../@Arpit_CentrePokeTraining/ParamsSection.m | 166 +- .../SessionPerformanceSection.m | 174 +++ .../@Arpit_CentrePokeTraining/SoundSection.m | 18 +- ...okeTraining_experimenter_ratname_250319c.m | 641 ++++++++ Protocols/@TaskSwitch4/HistorySection.m | 471 ++++++ Protocols/@TaskSwitch4/PPfilter.mat | Bin 0 -> 2184 bytes Protocols/@TaskSwitch4/SMA1.m | 332 ++++ Protocols/@TaskSwitch4/StimulusSection.m | 1377 +++++++++++++++++ Protocols/@TaskSwitch4/TaskSwitch4.m | 224 +++ Protocols/@TaskSwitch4/TrainingSection.m | 748 +++++++++ Protocols/@TaskSwitch4/get_variables_script.m | 11 + Protocols/@TaskSwitch4/make_pbup_mixed3.m | 206 +++ Protocols/@TaskSwitch4/parseargs.m | 187 +++ Protocols/@TaskSwitch4/singlebup_old.m | 59 + Protocols/@TaskSwitch4/state_colors.m | 26 + 21 files changed, 4618 insertions(+), 1175 deletions(-) delete mode 100644 Protocols/@Arpit_CentrePokeTraining/AntibiasSectionAthena.m delete mode 100644 Protocols/@Arpit_CentrePokeTraining/OverallPerformanceSection.m delete mode 100644 Protocols/@Arpit_CentrePokeTraining/ParamsSection.asv create mode 100644 Protocols/@Arpit_CentrePokeTraining/SessionPerformanceSection.m create mode 100644 Protocols/@Arpit_CentrePokeTraining/pipeline_Arpit_CentrePokeTraining_experimenter_ratname_250319c.m create mode 100644 Protocols/@TaskSwitch4/HistorySection.m create mode 100644 Protocols/@TaskSwitch4/PPfilter.mat create mode 100644 Protocols/@TaskSwitch4/SMA1.m create mode 100644 Protocols/@TaskSwitch4/StimulusSection.m create mode 100644 Protocols/@TaskSwitch4/TaskSwitch4.m create mode 100644 Protocols/@TaskSwitch4/TrainingSection.m create mode 100644 Protocols/@TaskSwitch4/get_variables_script.m create mode 100644 Protocols/@TaskSwitch4/make_pbup_mixed3.m create mode 100644 Protocols/@TaskSwitch4/parseargs.m create mode 100644 Protocols/@TaskSwitch4/singlebup_old.m create mode 100644 Protocols/@TaskSwitch4/state_colors.m diff --git a/ExperPort/Plugins/@sessionmodel2/SessionDefinition.m b/ExperPort/Plugins/@sessionmodel2/SessionDefinition.m index 3cffdeae..6ce32d45 100644 --- a/ExperPort/Plugins/@sessionmodel2/SessionDefinition.m +++ b/ExperPort/Plugins/@sessionmodel2/SessionDefinition.m @@ -2014,7 +2014,7 @@ if ~isequal(SavingSection(obj, 'get_settings_file_load_time'), value(TEMPORARY_SETTINGS_FILE_LOAD_TIME)) feval(mfilename, obj, 'init'); end - + if n_done_trials >= 1 && ~isempty(value(CURRENT_TRAINING_STAGES_FILE_NAME)) diff --git a/Protocols/@Arpit_CentrePokeTraining/AntibiasSectionAthena.m b/Protocols/@Arpit_CentrePokeTraining/AntibiasSectionAthena.m deleted file mode 100644 index cb34d14a..00000000 --- a/Protocols/@Arpit_CentrePokeTraining/AntibiasSectionAthena.m +++ /dev/null @@ -1,315 +0,0 @@ -% [x, y] = AntibiasSection(obj, action, [arg1], [arg2], [arg3]) -% -% Section that calculates biases and calculates probability of choosing a stimulus -% given the previous history. -% -% Antibias assumes that trials are of two classes, Left desired answer -% and Right desired answer, and that their outcome is either Correct or -% Incorrect. Given the history of previous trial classes, and the history -% of previous corrects/incorrects, Antibias makes a local estimate of -% fraction correct for each class, combines that with a prior probability -% of making the next trial Left, and produces a recommended probability -% for choosing the next trial as Left. Antibias will tend to make the -% class with the smaller frac correct the one with the higher probability. -% The strength of that tendency is quantified by a parameter, beta. -% (See probabilistic_trial_selector.m for details on the math of how the -% tendency is generated.) -% -% Local estimates of fraction correct are computed using an exponential -% kernel, most recent trial the most strongly weighted. The tau of this -% kernel is a GUI parameter. Two different estimates are computed: one for -% use in computing Left probability and Right probability; and a second -% simply for GUI display purposes. The two estimates can have different -% taus for their kernels. -% -% GUI DISPLAY: When initialized, this plugin will put up two panels and a -% title. In each panel, there is a slider that controls the tau of the -% recent-trials exponential kernel. One panel will display the percent -% corrects for Right and Left, as computed with its kernel. The second panel -% will display the a posteriori probabilities of making the next trial a -% "Left" trial or making the next trial a "Right" trial. This second panel -% has its own tau slider, and it also has a GUI parameter, beta, that -% controls how strongly the history matters. If beta=0, history doesn't -% matter, and the a priori LeftProbability dominates. If beta=Inf, then -% history matters above all: the next trial will be of the type with lowest -% fraction correct, for sure. -% -% See the bottom of this help file for examples of usage. -% -% arg1, arg2, arg3 are optional and their meaning depends on action (see -% below). -% -% PARAMETERS: -% ----------- -% -% obj Default object argument. -% -% action One of: -% -% 'init' To initialise the plugin and set up the GUI for it. This -% action requires two more arguments: The bottom left -% x y position (in units of pixels) of where to start placing -% the GUI elements of this plugin. -% -% 'update' This call will recompute both local estimates of -% fraction correct, and will recompute the recommended -% LeftProb, p(Left). This action requires three more arguments: -% HitHist, LProb, a scalar b/w 0 and 1; HitHist, a vector of 1s -% SidesHist and 0s and of length n_done_trials where 1 represents -% correct and 0 represents incorrect, first element -% corresponds to first trial; and SidesHist, a vector of -% 'l's and 'r's and of length n_done_trials where 'l' -% represents 'left', 'r' represents 'right' first element -% corresponds to first trial. -% -% 'get_posterior_probs' Returns a vector with two components, -% [p(Left) p(Right)]. -% -% -% 'update_biashitfrac' This call will recompute the local estimate of fraction -% Left correct and fraction Right correct used for -% LeftProb, antibiasing, and will also recompute the recommended -% HitHist, Left probability. This action -% SidesHist, requires three more arguments: LProb, a scalar b/w 0 -% and 1; HitHist, a vector of 1s and 0s and of length -% n_done_trials where 1 represents correct and 0 -% represents incorrect, first element corresponds to -% first trial; and SidesHist, a vector of 'l's and 'r's -% and of length n_done_trials where 'l' represents -% 'left', 'r' represents 'right' first element -% corresponds to first trial. -% -% 'update_hitfrac' This call is not related to computing the posterior -% Left probability, but will recompute only the local estimate -% HitHist, of fraction correct that is not used for antibiasing. -% SidesHist This action requires two more arguments: HitHist, a -% vector of 1s and 0s and of length n_done_trials where 1 -% represents correct and 0 represents incorrect, first -% element corresponds to first trial; and SidesHist, a vector of -% 'l's and 'r's and of length n_done_trials where 'l' -% represents 'left', 'r' represents 'right' first element -% corresponds to first trial. -% -% 'get' Needs one extra parameter, either 'Beta' or -% 'antibias_tau', and returns the corresponding scalar. -% -% 'reinit' Delete all of this section's GUIs and data, -% and reinit, at the same position on the same -% figure as the original section GUI was placed. -% -% -% x, y Relevant to action = 'init'; they indicate the initial -% position to place the GUI at, in the current figure window -% -% RETURNS: -% -------- -% -% if action == 'init' : -% -% [x1, y1, w, h] When action == 'init', Antibias will put up GUIs and take -% up a certain amount of space of the figure that was current when -% AntiBiasSection(obj, 'init', x, y) was called. On return, [x1 y1] -% will be the top left corner of the space used; [x y] (as passed -% to Antibias in the init call) will be the bottom left corner; -% [x+w y1] will be the top right; and [x+w y] will be the bottom -% right. h = y1-y. All these are in units of pixels. -% -% -% if action == 'get_posterior_probs' : -% -% [L R] When action == 'get_posterior_probs', a two-component vector is -% returned, with p(Left) and p(Right). If beta=0, then p(Left) -% will be the same as the last LeftProb that was passed in. -% -% -% USAGE: -% ------ -% -% To use this plugin, the typical calls would be: -% -% 'init' : On initializing your protocol, call -% AntibiasSection(obj, 'init', x, y); -% -% 'update' : After a trial is completed, call -% AntibiasSection(obj, 'update', LeftProb, HitHist, SidesHist) -% -% 'get_posterior_probs' : After a trial is completed, and when you are -% deciding what kind of trial to make the next trial, get the plugins -% opinion on whether the next trial should be Left or Right by calling -% AntibiasSection(obj, 'get_posterior_probs') -% -% See PARAMETERS section above for the documentation of each of these calls. -% - - -function [x, y, w, h] = AntibiasSection(obj, action, varargin) - -GetSoloFunctionArgs(obj); - -switch action - - case 'init', % ------------ CASE INIT ---------------- - x = varargin{1}; y = varargin{2}; y0 = y; - % Save the figure and the position in the figure where we are - % going to start adding GUI elements: - SoloParamHandle(obj, 'my_gui_info', 'value', [x y double(gcf)]); - - LogsliderParam(obj, 'HitFracTau', 30, 10, 400, x, y, 'label', 'hits frac tau', ... - 'TooltipString', ... - sprintf(['\nnumber of trials back over which to compute fraction of correct trials.\n' ... - 'This is just for displaying info-- for the bias calculation, see BiasTau above'])); - set_callback(HitFracTau, {mfilename, 'update_hitfrac'}); - next_row(y); - DispParam(obj, 'LtHitFrac', 0, x, y); next_row(y); - DispParam(obj, 'RtHitFrac', 0, x, y); next_row(y); - DispParam(obj, 'HitFrac', 0, x, y); next_row(y); - - next_row(y, 0.5); - LogsliderParam(obj, 'BiasTau', 30, 10, 400, x, y, 'label', 'antibias tau', ... - 'TooltipString', ... - sprintf(['\nnumber of trials back over\nwhich to compute fraction of correct trials\n' ... - 'for the antibias function.'])); next_row(y); - NumeditParam(obj, 'Beta', 0, x, y, ... - 'TooltipString', ... - sprintf(['When this is 0, past performance doesn''t affect choice\n' ... - 'of next trial. When this is large, the next trial is ' ... - 'almost guaranteed\nto be the one with smallest %% correct'])); next_row(y); - set_callback({BiasTau, Beta}, {mfilename, 'update_biashitfrac'}); - DispParam(obj, 'LtProb', 0, x, y); next_row(y); - DispParam(obj, 'RtProb', 0, x, y); next_row(y); - SoloParamHandle(obj, 'BiasLtHitFrac', 'value', 0); - SoloParamHandle(obj, 'BiasRtHitFrac', 'value', 0); - - SoloParamHandle(obj, 'LocalLeftProb', 'value', 0.5); - SoloParamHandle(obj, 'LocalHitHistory', 'value', []); - SoloParamHandle(obj, 'LocalPrevSides', 'value', []); - - - SubheaderParam(obj, 'title', mfilename, x, y); - next_row(y, 1.5); - - w = gui_position('get_width'); - h = y-y0; - - - case 'update', % --- CASE UPDATE ------------------- - if length(varargin)>0, LocalLeftProb.value = varargin{1}; end; - if length(varargin)>1, LocalHitHistory.value = varargin{2}; end; - if length(varargin)>2, LocalPrevSides.value = varargin{3}; end; - % Protect against somebody passing in SPHs, not actual values, by mistake: - if isa(value(LocalLeftProb), 'SoloParamHandle'), LocalLeftProb.value = value(value(LocalLeftProb)); end; - if isa(value(LocalHitHistory), 'SoloParamHandle'), LocalHitHistory.value = value(value(LocalHitHistory)); end; - if isa(value(LocalPrevSides), 'SoloParamHandle'), LocalPrevSides.value = value(value(LocalPrevSides)); end; - - feval(mfilename, obj, 'update_hitfrac'); - feval(mfilename, obj, 'update_biashitfrac'); - - - - case 'update_biashitfrac', % ------- CASE UPDATE_BIASHITFRAC ------------- - if length(varargin)>0, LocalLeftProb.value = varargin{1}; end; - if length(varargin)>1, LocalHitHistory.value = varargin{2}; end; - if length(varargin)>2, LocalPrevSides.value = varargin{3}; end; - % Protect against somebody passing in SPHs, not actual values, by mistake: - if isa(value(LocalLeftProb), 'SoloParamHandle'), LocalLeftProb.value = value(value(LocalLeftProb)); end; - if isa(value(LocalHitHistory), 'SoloParamHandle'), LocalHitHistory.value = value(value(LocalHitHistory)); end; - if isa(value(LocalPrevSides), 'SoloParamHandle'), LocalPrevSides.value = value(value(LocalPrevSides)); end; - - LeftProb = value(LocalLeftProb); - hit_history = value(LocalHitHistory); hit_history = colvec(hit_history); - previous_sides = value(LocalPrevSides); - - kernel = exp(-(0:length(hit_history)-1)/BiasTau)'; - kernel = kernel(end:-1:1); - - prevs = previous_sides(1:length(hit_history))'; - ul = find(prevs == 'l'); - if isempty(ul), BiasLtHitFrac.value = 1; - else BiasLtHitFrac.value = sum(hit_history(ul) .* kernel(ul))/sum(kernel(ul)); - end; - - ur = find(prevs == 'r'); - if isempty(ur), BiasRtHitFrac.value = 1; - else BiasRtHitFrac.value = sum(hit_history(ur) .* kernel(ur))/sum(kernel(ur)); - end; - - if isempty(ul) && ~isempty(ur), BiasLtHitFrac.value = value(BiasRtHitFrac); end; - if isempty(ur) && ~isempty(ul), BiasRtHitFrac.value = value(BiasLtHitFrac); end; - - choices = probabilistic_trial_selector([value(BiasLtHitFrac), value(BiasRtHitFrac)], ... - [LeftProb, 1-LeftProb], value(Beta)); - LtProb.value = choices(1); - RtProb.value = choices(2); - - - case 'get_posterior_probs', % ------- CASE GET_POSTERIOR_PROBS ------------- - x = [value(LtProb) ; value(RtProb)]; %#ok - - - case 'update_hitfrac', % ------- CASE UPDATE_HITFRAC ------------- - if length(varargin)>0, LocalHitHistory.value = varargin{1}; end; - if length(varargin)>1, LocalPrevSides.value = varargin{2}; end; - % Protect against somebody passing in SPHs, not actual values, by mistake: - if isa(value(LocalHitHistory), 'SoloParamHandle'), LocalHitHistory.value = value(value(LocalHitHistory)); end; - if isa(value(LocalPrevSides), 'SoloParamHandle'), LocalPrevSides.value = value(value(LocalPrevSides)); end; - - hit_history = value(LocalHitHistory); hit_history = colvec(hit_history); - previous_sides = value(LocalPrevSides); - - - if length(hit_history)>0, - kernel = exp(-(0:length(hit_history)-1)/HitFracTau)'; - kernel = kernel(end:-1:1); - HitFrac.value = sum(hit_history .* kernel)/sum(kernel); - - prevs = previous_sides(1:length(hit_history))'; - u = find(prevs == 'l'); - if isempty(u), LtHitFrac.value = NaN; - else LtHitFrac.value = sum(hit_history(u) .* kernel(u))/sum(kernel(u)); - end; - - u = find(prevs == 'r'); - if isempty(u), RtHitFrac.value = NaN; - else RtHitFrac.value = sum(hit_history(u) .* kernel(u))/sum(kernel(u)); - end; - end; - - - case 'get', % ------- CASE GET ------------- - if length(varargin)~=1, - error('AntibiasSection:Invalid', '''get'' needs one extra param'); - end; - switch varargin{1}, - case 'Beta', - x = value(Beta); - case 'antibias_tau', - x = value(BiasTau); - otherwise - error('AntibiasSection:Invalid', 'Don''t know how to get %s', varargin{1}); - end; - - - case 'reinit', % ------- CASE REINIT ------------- - currfig = double(gcf); - - % Get the original GUI position and figure: - x = my_gui_info(1); y = my_gui_info(2); figure(my_gui_info(3)); - - % Delete all SoloParamHandles who belong to this object and whose - % fullname starts with the name of this mfile: - delete_sphandle('owner', ['^@' class(obj) '$'], ... - 'fullname', ['^' mfilename]); - - % Reinitialise at the original GUI position and figure: - [x, y] = feval(mfilename, obj, 'init', x, y); - - % Restore the current figure: - figure(currfig); -end; - - - - -function [x] = colvec(x) - if size(x,2) > size(x,1), x = x'; end; - \ No newline at end of file diff --git a/Protocols/@Arpit_CentrePokeTraining/Arpit_CentrePokeTraining.m b/Protocols/@Arpit_CentrePokeTraining/Arpit_CentrePokeTraining.m index a45e47f5..22a24378 100644 --- a/Protocols/@Arpit_CentrePokeTraining/Arpit_CentrePokeTraining.m +++ b/Protocols/@Arpit_CentrePokeTraining/Arpit_CentrePokeTraining.m @@ -6,8 +6,8 @@ % Default object is of our own class (mfilename); % we inherit only from Plugins -obj = class(struct, mfilename, pokesplot2, saveload, sessionmodel2, soundmanager, soundui, antibias, ... - water, distribui, punishui, comments, soundtable, sqlsummary,reinforcement,softpokestay2); +obj = class(struct, mfilename, pokesplot2, saveload, sessionmodel2, soundmanager, soundui, ... + water, comments, soundtable, sqlsummary); %--------------------------------------------------------------- % BEGIN SECTION COMMONSoundTableSection TO ALL PROTOCOLS, DO NOT MODIFY @@ -37,7 +37,7 @@ %% init case 'init' - dispatcher('set_trialnum_indicator_flag'); + hackvar = 10; SoloFunctionAddVars('SessionModel', 'ro_args', 'hackvar'); %#ok SoloParamHandle(obj, 'myfig', 'saveable', 0); myfig.value = figure; @@ -54,11 +54,6 @@ SoloParamHandle(obj, 'nsessions_healthy_number_of_pokes', 'value', 0, 'save_with_settings', 1); SoloParamHandle(obj, 'post_DelComp_protocol', 'value', '', 'save_with_settings', 1); SoloParamHandle(obj, 'post_DelComp_settings_filename', 'value', '', 'save_with_settings', 1); - - - SoloParamHandle(obj, 'hit_history', 'value', []); - DeclareGlobals(obj, 'ro_args', {'hit_history'}); - SoloFunctionAddVars('ParamsSection', 'rw_args', 'hit_history'); SoloParamHandle(obj, 'violation_history', 'value', []); DeclareGlobals(obj, 'ro_args', {'violation_history'}); @@ -72,17 +67,6 @@ % DeclareGlobals(obj, 'ro_args', {'Stage_Trial_Counter'}); % SoloFunctionAddVars('ParamsSection', 'rw_args', 'Stage_Trial_Counter'); % - % SoloParamHandle(obj, 'Stage_ValidTrial_Counter', 'value', zeros(1,7)); - % DeclareGlobals(obj, 'ro_args', {'Stage_ValidTrial_Counter'}); - % SoloFunctionAddVars('ParamsSection', 'rw_args', 'Stage_ValidTrial_Counter'); - % - % SoloParamHandle(obj, 'Stage_ViolationTrial_Counter', 'value', zeros(1,7)); - % DeclareGlobals(obj, 'ro_args', {'Stage_ViolationTrial_Counter'}); - % SoloFunctionAddVars('ParamsSection', 'rw_args', 'Stage_ViolationTrial_Counter'); - % - % SoloParamHandle(obj, 'Stage_TimeoutTrial_Counter', 'value', zeros(1,7)); - % DeclareGlobals(obj, 'ro_args', {'Stage_TimeoutTrial_Counter'}); - % SoloFunctionAddVars('ParamsSection', 'rw_args', 'Stage_TimeoutTrial_Counter'); SoundManagerSection(obj, 'init'); @@ -134,10 +118,9 @@ % [x, y] = PunishmentSection(obj, 'init', x, y); %#ok next_column(x); y=5; - [x, y] = OverallPerformanceSection(obj, 'init', x, y); + [x, y] = SessionPerformanceSection(obj, 'init', x, y); [x, y] = ParamsSection(obj, 'init', x, y); %#ok [x, y] = SoundSection(obj,'init',x,y); - [x, y] = SoftPokeStayInterface(obj, 'add', 'soft_cp', x, y); figpos = get(double(gcf), 'Position'); [expmtr, rname]=SavingSection(obj, 'get_info'); @@ -167,7 +150,7 @@ % Run SessionDefinition *after* ParamsSection so we know whether the % trial was a violation or not SessionDefinition(obj, 'next_trial'); - OverallPerformanceSection(obj, 'evaluate'); + SessionPerformanceSection(obj, 'evaluate'); SoundManagerSection(obj, 'send_not_yet_uploaded_sounds'); nTrials.value = n_done_trials; @@ -225,7 +208,7 @@ case 'pre_saving_settings' SessionDefinition(obj, 'run_eod_logic_without_saving'); - perf = OverallPerformanceSection(obj, 'evaluate'); + perf = SessionPerformanceSection(obj, 'evaluate'); cp_durs = ParamsSection(obj, 'get_cp_history'); [stim1dur] = ParamsSection(obj,'get_stimdur_history'); @@ -236,22 +219,12 @@ % 'Low = %.2f, High = %.2f'], ... % perf(1), perf(2), perf(3), perf(6), cp_durs(1), cp_durs(end), cp_durs(end)-cp_durs(1), classperf(1),classperf(2))); - pd.hits=hit_history(:); pd.sides=previous_sides(:); pd.viols=violation_history(:); pd.timeouts=timeout_history(:); % pd.performance=tot_perf(:); pd.cp_durs=cp_durs(:); - -% pd.stimuli=stimuli(:); - % Athena: look into pair_history perhaps stimulus_history and stimuli - % are the same - %pd.stimulus=stimulus_history(:); - - pd.stim1dur=stim1dur(:); - - %pd.stimul=stim_history(:); sendsummary(obj,'protocol_data',pd); diff --git a/Protocols/@Arpit_CentrePokeTraining/Arpit_CentrePokeTrainingSMA.m b/Protocols/@Arpit_CentrePokeTraining/Arpit_CentrePokeTrainingSMA.m index 824fbe57..ee17beb3 100644 --- a/Protocols/@Arpit_CentrePokeTraining/Arpit_CentrePokeTrainingSMA.m +++ b/Protocols/@Arpit_CentrePokeTraining/Arpit_CentrePokeTrainingSMA.m @@ -49,8 +49,7 @@ case 'prepare_next_trial' %% Setup water - min_time= 2.5E-4; % This is less than the minumum time allowed for a state transition. - + left1led = bSettings('get', 'DIOLINES', 'left1led'); center1led = bSettings('get', 'DIOLINES', 'center1led'); right1led = bSettings('get', 'DIOLINES', 'right1led'); @@ -60,7 +59,8 @@ %% Setup sounds sone_sound_id = SoundManagerSection(obj, 'get_sound_id', 'SOneSound'); - sone_sound_duration = value(A1_time); % SoundManagerSection(obj, 'get_sound_duration', 'SOneSound'); + stwo_sound_id = SoundManagerSection(obj, 'get_sound_id', 'STwoSound'); + sound_duration = value(A1_time); % SoundManagerSection(obj, 'get_sound_duration', 'SOneSound'); go_sound_id = SoundManagerSection(obj, 'get_sound_id', 'GoSound'); go_cue_duration = value(time_go_cue); % SoundManagerSection(obj, 'get_sound_duration', 'GoSound'); viol_sound_id = SoundManagerSection(obj, 'get_sound_id', 'ViolationSound'); @@ -96,20 +96,29 @@ sma = StateMachineAssembler('full_trial_structure','use_happenings', 1); - SoftPokeStayInterface(obj, 'set', 'soft_cp', 'Duration', CP_duration - SettlingIn_time,'Grace', legal_cbreak); % used in later stages, after stage 3 - if side=='l' sma = add_scheduled_wave(sma, 'name', 'reward_delivery', 'preamble', reward_delay, ... 'sustain', LeftWValveTime, 'DOut', left1water); % % activating the left side water port + + sma = add_scheduled_wave(sma, 'name', 'stimplay', 'preamble', PreStim_time, ... + 'sustain', sound_duration, 'sound_trig', sone_sound_id); % to play a sound before Go Cue + else sma = add_scheduled_wave(sma, 'name', 'reward_delivery', 'preamble', reward_delay, ... 'sustain', RightWValveTime, 'DOut', right1water); % activating the right side water port + + sma = add_scheduled_wave(sma, 'name', 'stimplay', 'preamble', PreStim_time, ... + 'sustain', sound_duration, 'sound_trig', stwo_sound_id); % to play a sound before Go Cue + end sma = add_scheduled_wave(sma, 'name', 'settling_period', 'preamble', SettlingIn_time); % intial fidgety period without violation - sma = add_scheduled_wave(sma, 'name', 'stimA1', 'preamble', PreStim_time, ... - 'sustain', sone_sound_duration, 'sound_trig', sone_sound_id); % to play a sound before Go Cue + if value(training_stage) == 3 + sma = add_scheduled_wave(sma, 'name', 'CP_Duration_wave', 'preamble', CP_duration); % total length of centre poke to consider success + else + sma = add_scheduled_wave(sma, 'name', 'CP_Duration_wave', 'preamble', CP_duration - SettlingIn_time); % total length of centre poke minus the inital fidgety time to consider success + end sma = add_scheduled_wave(sma, 'name', 'Go_Cue', 'preamble', 0.001, ... 'sustain', go_cue_duration, 'sound_trig', go_sound_id); % to play the Go Cue/Reward Sound @@ -133,7 +142,7 @@ 'input_to_statechange',{'Tup','second_hit_state'; HitEvent,'hit_state'}); sma = add_state(sma,'name','hit_state','self_timer',0.01,... - 'output_actions', {'DOut', SideLight; 'SchedWaveTrig','reward_delivery + Go_Cue'},... + 'output_actions', {'SchedWaveTrig','reward_delivery + Go_Cue'},... 'input_to_statechange',{'Tup','drink_state'}); sma = add_state(sma,'name','drink_state','self_timer',drink_time,... @@ -156,7 +165,7 @@ 'input_to_statechange',{'reward_collection_dur_In', 'timeout_state'; 'Tup','timeout_state'; HitEvent,'hit_state'}); sma = add_state(sma,'name','hit_state','self_timer',0.01,... - 'output_actions', {'DOut', SideLight; 'SchedWaveTrig','reward_delivery + Go_Cue'},... + 'output_actions', {'SchedWaveTrig','reward_delivery + Go_Cue'},... 'input_to_statechange',{'Tup','drink_state'}); sma = add_state(sma,'name','drink_state','self_timer',drink_time,... @@ -183,8 +192,19 @@ 'input_to_statechange', {'Cin','settling_in_state'; 'Tup','timeout_state'}); sma = add_state(sma,'name','settling_in_state','self_timer',CP_duration, ... - 'output_actions', {'DOut', center1led}, ... - 'input_to_statechange', {'Tup','side_led_wait_RewardCollection'}); + 'output_actions', {'SchedWaveTrig', 'CP_Duration_wave'}, ... + 'input_to_statechange', {'Cout','current_state + 1';'Tup','side_led_wait_RewardCollection'}); + + % Intermediate State + + % This intermediate state is considering the poke is out before the end of settling time / at the start of this state + sma = add_state(sma,'self_timer',CP_duration,'output_actions', {'DOut', center1led}, ... + 'input_to_statechange', {'CP_Duration_wave_In','side_led_wait_RewardCollection';'Cin','current_state + 1';'Tup','side_led_wait_RewardCollection'}); + + % The state jump to here when the nose is still in then go + % directly to give reward + sma = add_state(sma,'self_timer',CP_duration,... + 'input_to_statechange', {'CP_Duration_wave_In','side_led_wait_RewardCollection'; 'Cout','current_state - 1';'Tup','side_led_wait_RewardCollection'}); sma = add_state(sma, 'name', 'side_led_wait_RewardCollection', 'self_timer', SideLed_duration + RewardCollection_duration, ... 'output_actions', {'DOut', SideLight; 'SchedWaveTrig', 'reward_collection_dur + Go_Cue'}, ... @@ -195,7 +215,7 @@ 'input_to_statechange',{'reward_collection_dur_In', 'timeout_state'; 'Tup','timeout_state'; HitEvent,'hit_state'}); sma = add_state(sma,'name','hit_state','self_timer',0.01,... - 'output_actions', {'DOut', SideLight; 'SchedWaveTrig','reward_delivery'},... + 'output_actions', {'SchedWaveTrig','reward_delivery'},... 'input_to_statechange',{'Tup','drink_state'}); sma = add_state(sma,'name','drink_state','self_timer',drink_time,... @@ -220,69 +240,95 @@ 'output_actions', {'DOut', center1led}, ... 'input_to_statechange', {'Cin','settling_in_state';'Tup','timeout_state'}); - % sma = add_state(sma,'name','settling_in_state','self_timer',SettlingIn_time, ... - % 'output_actions', {'DOut', center1led,'SchedWaveTrig', settling_period}, ... - % 'input_to_statechange', {'Cout','intermid_poke_out_state','Tup','soft_cp'}); - + %%%%%%%%%%%%% SETTLING IN STATE START %%%%%%%%%%%%%%%%%%%% % Before progressing check if its still centre poking or pokes within legal c_break other wise its a violation - % This state is considering the poke is out at the end of settling time / at the start of this state + if CP_duration <= SettlingIn_time + legal_cbreak % state machine during initial warm-up when starting a new session - % sma = add_state(sma,'name','intermid_poke_out_state','self_timer',SettlingIn_time, ... - % 'output_actions', {'DOut', center1led}, ... - % 'input_to_statechange', {'settling_period_In','legal_poke_start_state','Cin','intermid_poke_in_state','Tup','legal_poke_start_state',... - % 'Rin', 'violation_state','Rout', 'violation_state', 'Lin', 'violation_state','Lout', 'violation_state'}); + sma = add_state(sma,'name','settling_in_state','self_timer',CP_duration, ... + 'output_actions', {'SchedWaveTrig', 'CP_Duration_wave'}, ... + 'input_to_statechange', {'Cout','current_state + 1';'Tup','side_led_wait_RewardCollection'}); - % The other case is when the nose is still in then go directly to soft_cp + % Intermediate State - % sma = add_state(sma,'name','intermid_poke_in_state','self_timer',SettlingIn_time, ... - % 'output_actions', {'DOut', center1led}, ... - % 'input_to_statechange', {'settling_period_In','soft_cp', 'Cout','intermid_poke_out_state','Tup','soft_cp',... - % 'Rin', 'violation_state','Rout', 'violation_state', 'Lin', 'violation_state','Lout', 'violation_state'}); + % This intermediate state is considering the poke is out before the end of settling time / at the start of this state + sma = add_state(sma,'self_timer',CP_duration,'output_actions', {'DOut', center1led}, ... + 'input_to_statechange', {'CP_Duration_wave_In','side_led_wait_RewardCollection';'Cin','current_state + 1';'Tup','side_led_wait_RewardCollection'}); - % Before progressing check if its still centre poking or pokes within legal c_break other wise its a violation + % The state jump to here when the nose is still in then go + % directly to give reward + sma = add_state(sma,'self_timer',CP_duration,... + 'input_to_statechange', {'CP_Duration_wave_In','side_led_wait_RewardCollection'; 'Cout','current_state - 1';'Tup','side_led_wait_RewardCollection'}); - sma = add_state(sma,'name','settling_in_state','self_timer',SettlingIn_time, ... - 'output_actions', {'DOut', center1led; 'SchedWaveTrig', settling_period}, ... - 'input_to_statechange', {'Cout','current_state + 1';'Tup','soft_cp'}); + else % the usual state machine - % Instead using Intermediate State + sma = add_state(sma,'name','settling_in_state','self_timer',SettlingIn_time, ... + 'output_actions', {'SchedWaveTrig', settling_period}, ... + 'input_to_statechange', {'Cout','current_state + 1';'Tup','soft_cp'}); - % This intermediate state is considering the poke is out before the end of settling time / at the start of this state + % Intermediate State + + % This intermediate state is considering the poke is out before the end of settling time / at the start of this state sma = add_state(sma,'self_timer',SettlingIn_time,'output_actions', {'DOut', center1led}, ... - 'input_to_statechange', {'settling_period_In','legal_poke_start_state';'Cin','current_state + 1';'Tup','legal_poke_start_state';... + 'input_to_statechange', {'settling_period_In','legal_poke_start_state';'Cin','current_state + 1';'Tup','legal_poke_start_state';... 'Rin', 'violation_state';'Rout', 'violation_state'; 'Lin', 'violation_state';'Lout', 'violation_state'}); % The state jump to here when the nose is still in then go directly to soft_cp - sma = add_state(sma,'self_timer',SettlingIn_time, 'output_actions', {'DOut', center1led},... + sma = add_state(sma,'self_timer',SettlingIn_time,... 'input_to_statechange', {'settling_period_In','soft_cp'; 'Cout','current_state - 1';'Tup','soft_cp';... 'Rin', 'violation_state'; 'Rout', 'violation_state'; 'Lin', 'violation_state'; 'Lout', 'violation_state'}); - sma = add_state(sma,'name','legal_poke_start_state','self_timer',legal_cbreak, ... + end + + %%%%%%%%%%%%% SETTLING IN STATE END %%%%%%%%%%%%%%%%%%%%%% + + % STATE TO CHECK BEFORE START OF LEGAL POKE PERIOD + + sma = add_state(sma,'name','legal_poke_start_state','self_timer',legal_cbreak/2, ... 'output_actions', {'DOut', center1led}, ... 'input_to_statechange', {'Cin','soft_cp'; 'Tup','violation_state';... - 'Rin', 'violation_state'; 'Rout', 'violation_state'; 'Lin', 'violation_state'; 'Lout', 'violation_state'}); + 'Rin', 'violation_state'; 'Rout', 'violation_state'; 'Lin', 'violation_state'; 'Lout', 'violation_state'}); %more stringent by giving half the legal cp time + + + %%%%%%%%%%%% LEGAL SOFT POKE STATE START %%%%%%%%%%%%%%%%% if value(training_stage) == 4 % Soft poke with violation - sma = SoftPokeStayInterface(obj, 'add_sma_states', 'soft_cp', ... - {'success_exitstate_name', 'legal_poke_end_state'},{'abort_exitstate_name', 'violation_state'}, ...... - 'pokeid', 'C','ConstantDOut', center1led, 'triggertime', 0.0001); + + sma = add_state(sma,'name','soft_cp','self_timer',CP_duration - SettlingIn_time, ... CP_Duration_wave + 'output_actions', {'SchedWaveTrig', 'CP_Duration_wave'},... + 'input_to_statechange', {'Cout','current_state + 1'; 'Tup','legal_poke_end_state';... + 'Rin', 'violation_state'; 'Rout', 'violation_state'; 'Lin', 'violation_state'; 'Lout', 'violation_state'}); %more stringent by giving half the legal cp time else % Soft poke with violation. The difference between this and previous stage is introduction of - % sound before the go cue + % sound before the go cue so a new scheduled wave - sma = SoftPokeStayInterface(obj, 'add_sma_states', 'soft_cp', ... - {'success_exitstate_name', 'legal_poke_end_state'},{'abort_exitstate_name', 'violation_state'}, ...... - 'pokeid', 'C','ConstantDOut', center1led, 'triggertime', 0.0001,'InitialSchedWaveTrig', stimA1); + sma = add_state(sma,'name','soft_cp','self_timer',CP_duration - SettlingIn_time, ... CP_Duration_wave + 'output_actions', {'SchedWaveTrig', 'CP_Duration_wave + stimplay'},... + 'input_to_statechange', {'Cout','current_state + 1'; 'Tup','legal_poke_end_state';... + 'Rin', 'violation_state'; 'Rout', 'violation_state'; 'Lin', 'violation_state'; 'Lout', 'violation_state'}); %more stringent by giving half the legal cp time end + % Intermediate State + + % This intermediate state is considering the poke is out before the end of settling time / at the start of this state + sma = add_state(sma,'self_timer',legal_cbreak,'output_actions', {'DOut', center1led}, ... + 'input_to_statechange', {'CP_Duration_wave_In','legal_poke_end_state';'Cin','current_state + 1';'Tup','violation_state';... + 'Rin', 'violation_state';'Rout', 'violation_state'; 'Lin', 'violation_state';'Lout', 'violation_state'}); + + % The state jump to here when the nose is still in then go directly to soft_cp + sma = add_state(sma,'self_timer',CP_duration - SettlingIn_time, ... + 'input_to_statechange', {'CP_Duration_wave_In','side_led_wait_RewardCollection';'Cout','current_state - 1';'Tup','side_led_wait_RewardCollection';... + 'Rin', 'violation_state';'Rout', 'violation_state'; 'Lin', 'violation_state';'Lout', 'violation_state'}); + + %%%%%%%%%%%% LEGAL SOFT POKE STATE END %%%%%%%%%%%%%%%%% + % Before giving the reward check if its still centre poking or pokes % within legal c_break other wise its a violation - sma = add_state(sma,'name','legal_poke_end_state','self_timer',legal_cbreak, ... + sma = add_state(sma,'name','legal_poke_end_state','self_timer',legal_cbreak/2, ... 'output_actions', {'DOut', center1led}, ... 'input_to_statechange', {'Cin','side_led_wait_RewardCollection'; 'Tup','violation_state'}); @@ -295,7 +341,7 @@ 'input_to_statechange',{'reward_collection_dur_In', 'timeout_state'; 'Tup','punish_state'; HitEvent,'hit_state'}); sma = add_state(sma,'name','hit_state','self_timer',0.01,... - 'output_actions', {'DOut', SideLight; 'SchedWaveTrig','reward_delivery'},... + 'output_actions', {'SchedWaveTrig','reward_delivery'},... 'input_to_statechange',{'Tup','drink_state'}); sma = add_state(sma,'name','drink_state','self_timer',drink_time,... @@ -312,7 +358,7 @@ % For Violations sma = add_state(sma,'name','violation_state','self_timer',viol_snd_duration,... - 'output_actions',{'SoundOut',viol_sound_id; 'DOut', center1led; 'SchedWaveTrig', '-Go_Cue'},... + 'output_actions',{'SoundOut',viol_sound_id; 'DOut', center1led; 'SchedWaveTrig', '-Go_Cue -Stimplay -CP_Duration_wave'},... 'input_to_statechange', {'Tup', 'current_state+1'}); sma = add_state(sma, 'self_timer', max(0.001, violation_iti-viol_snd_duration), ... 'input_to_statechange',{'Tup','preclean_up_state'}); diff --git a/Protocols/@Arpit_CentrePokeTraining/OverallPerformanceSection.m b/Protocols/@Arpit_CentrePokeTraining/OverallPerformanceSection.m deleted file mode 100644 index b377fc98..00000000 --- a/Protocols/@Arpit_CentrePokeTraining/OverallPerformanceSection.m +++ /dev/null @@ -1,127 +0,0 @@ -% [x, y] = OverallPerformanceSection(obj, action, x,y) -% -% Reports overall performance. Uses training_stage from SideSection. -% -% PARAMETERS: -% ----------- -% -% action One of: -% 'init' To initialise the section and set up the GUI -% for it -% -% 'close' Delete all of this section's GUIs and data -% -% 'reinit' Delete all of this section's GUIs and data, -% and reinit, at the same position on the same -% figure as the original section GUI was placed. -% -% 'evalueta' Look at history and compute hit fraction, etc. -% -% x, y Only relevant to action = 'init'; they indicate the initial -% position to place the GUI at, in the current figure window -% -% RETURNS: -% -------- -% -% perf When action == 'init', returns x and y, pixel positions on -% the current figure, updated after placing of this section's GUI. -% When action == 'evaluate', returns a vector with elements -% [ntrials, violation_rate, left_hit_frac, right_hit_frac, hit_frac] -% -% - -% CDB, 23-March-2013 - -function [x, y] = OverallPerformanceSection(obj, action, x,y) - -GetSoloFunctionArgs(obj); - -switch action, - - % ------------------------------------------------------------------ - % INIT - % ------------------------------------------------------------------ - - case 'init' - - SoloParamHandle(obj, 'my_gui_info', 'value', [x y double(gcf)], 'saveable', 0); - - DispParam(obj, 'ntrials', 0, x, y); next_row(y); - DispParam(obj, 'violation_rate', 0, x, y, 'TooltipString', ... - 'Fraction of trials with a center poke violation'); next_row(y); - DispParam(obj, 'timeout_rate', 0, x, y, 'TooltipString', ... - 'Fraction of trials with timeout'); next_row(y); - DispParam(obj, 'Left_hit_frac', 0, x, y, 'TooltipString', ... - 'Fraction of correct Left trials'); next_row(y); - DispParam(obj, 'Right_hit_frac', 0, x, y, 'TooltipString', ... - 'Fraction of correct Right trials'); next_row(y); - DispParam(obj, 'hit_frac', 0, x, y, 'TooltipString', ... - 'Fraction of correct trials'); next_row(y); - - SubheaderParam(obj, 'title', 'Overall Performance', x, y); - next_row(y, 1.5); - SoloParamHandle(obj, 'previous_parameters', 'value', []); - - % ------------------------------------------------------------------ - % evaluate - % ------------------------------------------------------------------ - - case 'evaluate' - - switch value(training_stage) - case 1, %% center led on -> poke in the center -> go cue -> reward light and sound - if n_done_trials > 1, - ntrials.value = n_done_trials; - %violation_rate.value = numel(find(isnan(hit_history)))/n_done_trials; - violation_rate.value = numel(find(violation_history))/n_done_trials; - timeout_rate.value = numel(find(timeout_history))/n_done_trials; - goods = ~isnan(hit_history)'; - lefts = previous_sides(1:n_done_trials)=='l'; - rights = previous_sides(1:n_done_trials)=='r'; - Left_hit_frac.value = mean(hit_history(goods & lefts)); - Right_hit_frac.value = mean(hit_history(goods & rights)); - hit_frac.value = mean(hit_history(goods)); - end; - end - - if nargout > 0, - x = [n_done_trials, value(violation_rate), value(timeout_rate), value(Left_hit_frac), ... - value(Right_hit_frac), value(hit_frac)]; - end; - - - % ------------------------------------------------------------------ - % close - % ------------------------------------------------------------------ - - case 'close', - % Delete all SoloParamHandles who belong to this object and whose - % fullname starts with the name of this mfile: - delete_sphandle('owner', ['^@' class(obj) '$'], ... - 'fullname', ['^' mfilename]); - - - % ------------------------------------------------------------------ - % reinit - % ------------------------------------------------------------------ - - case 'reinit', - currfig = double(gcf); - - % Get the original GUI position and figure: - x = my_gui_info(1); y = my_gui_info(2); figure(my_gui_info(3)); - - % Delete all SoloParamHandles who belong to this object and whose - % fullname starts with the name of this mfile: - delete_sphandle('owner', ['^@' class(obj) '$'], ... - 'fullname', ['^' mfilename]); - - % Reinitialise at the original GUI position and figure: - [x, y] = feval(mfilename, obj, 'init', x, y); - - % Restore the current figure: - figure(currfig); - -end - - diff --git a/Protocols/@Arpit_CentrePokeTraining/ParamsSection.asv b/Protocols/@Arpit_CentrePokeTraining/ParamsSection.asv deleted file mode 100644 index 5b01f33f..00000000 --- a/Protocols/@Arpit_CentrePokeTraining/ParamsSection.asv +++ /dev/null @@ -1,532 +0,0 @@ -function [x, y] = ParamsSection(obj, action, x,y) - -GetSoloFunctionArgs(obj); - -switch action - - % ------------------------------------------------------------------ - % INIT - % ------------------------------------------------------------------ - - case 'init' - - SoloParamHandle(obj, 'my_gui_info', 'value', [x y double(gcf)], 'saveable', 0); - y0 = y; - - [x, y] = AntibiasSectionAthena(obj, 'init', x, y); - - - ToggleParam(obj, 'antibias_LRprob', 0, x,y,... - 'OnString', 'AB_Prob ON',... - 'OffString', 'AB_Prob OFF',... - 'TooltipString', sprintf(['If on (Yellow) then it enables the AntiBias algorithm\n'... - 'based on changing the probablity of Left vs Right'])); - - next_row(y); - NumeditParam(obj, 'LeftProb', 0.5, x, y); next_row(y); - set_callback(LeftProb, {mfilename, 'new_leftprob'}); - MenuParam(obj, 'MaxSame', {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, Inf}, Inf, x, y, ... - 'TooltipString', sprintf(['\nMaximum number of consecutive trials where correct\n' ... - 'response is on the same side. Overrides antibias. Thus, for\n' ... - 'example, if MaxSame=5 and there have been 5 Left trials, the\n' ... - 'next trial is guaranteed to be Right'])); next_row(y); - - DispParam(obj, 'ThisTrial', 'LEFT', x, y); next_row(y); - SoloParamHandle(obj, 'previous_sides', 'value', []); - DeclareGlobals(obj, 'ro_args', 'previous_sides'); - SubheaderParam(obj, 'title', 'Params Section', x, y); - next_row(y, 1.5); - next_column(x); y = 5; - next_row(y); - NumeditParam(obj, 'reward_delay', 0.01, x,y,'label','Reward Delay','TooltipString','Delay between side poke and reward delivery'); - next_row(y); - NumeditParam(obj, 'drink_time', 1, x,y,'label','Drink Time','TooltipString','waits to finish water delivery'); - next_row(y); - NumeditParam(obj, 'timeout_iti', 5, x,y,'label','No Choice Timeout','TooltipString','ITI on timeout trials'); - next_row(y); - NumeditParam(obj, 'violation_iti', 1, x,y,'label','Violation Timeout','TooltipString','Center poke violation duration'); - % Reward Collection - next_row(y); - NumeditParam(obj, 'RewardCollection_duration', 10, x,y,'label','RewardCollection_dur','TooltipString','Wait until rat collects the reward else a timeout'); - next_row(y); - NumeditParam(obj, 'SideLed_duration', 1, x,y,'label','Side LED duration','TooltipString','Duration of SideLed'); - % Centre Poke - next_row(y); - NumeditParam(obj, 'legal_cbreak', 0.1, x,y, 'TooltipString','Time in sec for which it is ok to be outside the center port before a violation occurs.'); - set_callback(legal_cbreak, {mfilename, 'new_legal_cbreak'}); - next_row(y); - NumeditParam(obj, 'SettlingIn_time', 0.2, x,y, 'TooltipString','Initial settling period during which there is no violation'); - next_row(y); - % PreStim - NumeditParam(obj, 'PreStim_time_Min', 0.20, x,y,'label','Pre-Stim Min','TooltipString','Min Time in NIC before starting the stimulus'); - set_callback(PreStim_time_Min, {mfilename, 'new_CP_duration'}); - next_row(y); - NumeditParam(obj, 'PreStim_time', 0.20, x,y,'label','Pre-Stim time','TooltipString','Actual Time in NIC before starting the stimulus'); - set_callback(PreStim_time, {mfilename, 'new_CP_duration'}); - next_row(y); - NumeditParam(obj, 'PreStim_time_Max', 0.40, x,y,'label','Pre-Stim Max','TooltipString','Max Time in NIC before starting the stimulus'); - set_callback(PreStim_time_Max, {mfilename, 'new_CP_duration'}); - next_row(y); - % A1 Time - NumeditParam(obj, 'A1_time_Min', 0.4, x,y,'label','Min time AUD1','TooltipString','Min value to select the Duration of fixed stimulus'); - set_callback(A1_time_Min, {mfilename, 'new_CP_duration'}); - next_row(y); - NumeditParam(obj, 'A1_time', 0.4, x,y,'label','AUD1 Time','TooltipString','Actual Duration of fixed stimulus'); - set_callback(A1_time, {mfilename, 'new_CP_duration'}); - next_row(y); - NumeditParam(obj, 'A1_time_Max', 0.4, x,y,'label','Max time AUD1','TooltipString','Max value to select the Duration of fixed stimulus'); - set_callback(A1_time_Max, {mfilename, 'new_CP_duration'}); - next_row(y); - % Time b/w stim and Go Cue - NumeditParam(obj, 'time_bet_aud1_gocue_Min', 0.2, x,y,'label','Min A1-GoCue time','TooltipString','Min time between the end of the stimulus and the go cue '); - set_callback(time_bet_aud1_gocue_Min, {mfilename, 'new_CP_duration'}); - next_row(y); - NumeditParam(obj, 'time_bet_aud1_gocue', 0.2, x,y,'label','A1-GoCue time','TooltipString','Actual time between the end of the stimulus and the go cue '); - set_callback(time_bet_aud1_gocue, {mfilename, 'new_CP_duration'}); - next_row(y); - NumeditParam(obj, 'time_bet_aud1_gocue_Max', 2, x,y,'label','Max A1-GoCue time','TooltipString','Max time between the end of the stimulus and the go cue '); - set_callback(time_bet_aud1_gocue_Max, {mfilename, 'new_CP_duration'}); - next_row(y); - DispParam(obj, 'init_CP_duration', 0.01, x,y,'label','init_CP duration','TooltipString','Duration of Nose in Central Poke before Go cue starts (see Total_CP_duration)'); - next_row(y); - DispParam(obj, 'CP_duration', PreStim_time+A1_time+time_bet_aud1_gocue, x,y,'label','CP duration','TooltipString','Duration of Nose in Central Poke before Go cue starts (see Total_CP_duration)'); - set_callback(CP_duration, {mfilename, 'new_CP_duration'}); - next_row(y); - NumeditParam(obj, 'time_go_cue' ,0.2, x,y,'label','Go Cue Duration','TooltipString','duration of go cue (see Total_CP_duration)'); - set_callback(time_go_cue, {mfilename, 'new_time_go_cue'}); - next_row(y); - DispParam(obj, 'Total_CP_duration', CP_duration+time_go_cue, x, y, 'TooltipString', 'Total expected(rat can poke out anytime after Go cue onset) nose in center port time, in secs. Sum of CP_duration and Go Cue duration'); %#ok<*NODEF> - next_row(y); - ToggleParam(obj, 'warmup_on', 1, x,y,... - 'OnString', 'Warmup ON',... - 'OffString', 'Warmup OFF',... - 'TooltipString', sprintf(['If on (Yellow) then it applies the initial warming up phase, during which the\n',... - 'CP_duration starts small and gradually grows to last_session_max_cp_duration'])); - next_row(y); - ToggleParam(obj, 'random_PreStim_time', 0, x,y,... - 'OnString', 'random PreStim_time ON',... - 'OffString', 'random PreStim_time OFF',... - 'TooltipString', sprintf('If on (black) then it enables the random time between the user given range')); - set_callback(random_PreStim_time, {mfilename, 'new_CP_duration'}); - next_row(y); - ToggleParam(obj, 'random_A1_time', 0, x,y,... - 'OnString', 'random A1_time ON',... - 'OffString', 'random A1_time OFF',... - 'TooltipString', sprintf('If on (black) then it enables the random sampling of A1_time')); - set_callback(random_A1_time, {mfilename, 'new_CP_duration'}); - next_row(y); - ToggleParam(obj, 'random_prego_time', 0, x,y,... - 'OnString', 'random prego_time ON',... - 'OffString', 'random prego_time OFF',... - 'TooltipString', sprintf('If on (black) then it enables the random sampling of time between the end of the stimulus and the go cue')); - set_callback(random_prego_time, {mfilename, 'new_CP_duration'}); - - next_column(x); - y=5; - NumeditParam(obj,'trials_in_stage',1,x,y,'label','Trial Counter'); - next_row(y); - NumeditParam(obj,'training_stage',1,x,y,'label','Training Stage'); - set_callback(training_stage, {mfilename, 'Training_Stage'}); - next_row(y); - ToggleParam(obj,'use_training',0,x,y,'OnString','Using Autotrain','OffString','Manual Settings'); - set_callback(use_training, {mfilename, 'Training_Stage'}); - - next_row(y); - NumeditParam(obj, 'ntrial_correct_bias', 0, x, y, ... - 'TooltipString', 'antibias starts from trial=ntrial_correct_bias'); - next_row(y); - NumeditParam(obj, 'right_left_diff', .12, x, y, ... - 'TooltipString', 'antibias applies if difference between right and left sides is bigger than this number'); - next_row(y); - NumeditParam(obj, 'max_wtr_mult', 4, x, y, ... - 'TooltipString', 'wtr_mult will be min(max_wtr_mult,right_hit/left_hit)'); - next_row(y); - NumeditParam(obj, 'left_wtr_mult', 1, x, y, ... - 'TooltipString', 'all left reward times are multiplied by this number'); - next_row(y); - NumeditParam(obj, 'right_wtr_mult', 1, x, y, ... - 'TooltipString', 'all right reward times are multiplied by this number'); - next_row(y); - ToggleParam(obj, 'antibias_wtr_mult', 0, x,y,... - 'OnString', 'AntiBias Water ON',... - 'OffString', 'AntiBias Water OFF',... - 'TooltipString', sprintf(['If on (black) then it disables the wtr_mult entries\n'... - 'and uses hitfrac to adjust the water times'])); - - next_row(y); - SoloFunctionAddVars('Arpit_CentrePokeTrainingSMA', 'ro_args', ... - {'CP_duration';'SideLed_duration'; ... - 'RewardCollection_duration';'training_stage'; ... - 'legal_cbreak' ; 'SettlingIn_time'; 'time_go_cue'; ... - 'A1_time';'time_bet_aud1_gocue' ; 'PreStim_time'; - 'drink_time';'reward_delay';'left_wtr_mult';... - 'right_wtr_mult';'antibias_wtr_mult';... - 'timeout_iti';'violation_iti'}); - - % History of hit/miss: - SoloParamHandle(obj, 'deltaf_history', 'value', []); - - SoloFunctionAddVars('OverallPerformanceSection', 'ro_args', ... - {'training_stage'}); - - - SoloParamHandle(obj, 'previous_parameters', 'value', []); - - case 'new_leftprob' - AntibiasSectionAthena(obj, 'update_biashitfrac', value(LeftProb)); - - case 'new_legal_cbreak' - SoftPokeStayInterface(obj, 'set', 'soft_cp', 'Grace', legal_cbreak); - - case 'new_CP_duration' - - if random_PreStim_time == 1 && (value(PreStim_time) < value(PreStim_time_Min) || value(PreStim_time) > value(PreStim_time_Max)) - PreStim_time.value = value(PreStim_time_Min); - else - PreStim_time.value = value(PreStim_time); - end - - if random_A1_time == 1 && (value(A1_time) < value(A1_time_Min) || value(A1_time) > value(A1_time_Max)) - A1_time.value = value(A1_time_Min); - else - A1_time.value = value(A1_time); - end - - if random_prego_time == 1 && (value(time_bet_aud1_gocue) < value(time_bet_aud1_gocue_Min) || value(time_bet_aud1_gocue) > value(time_bet_aud1_gocue_Max)) - time_bet_aud1_gocue.value = value(time_bet_aud1_gocue_Min); - else - time_bet_aud1_gocue.value = value(time_bet_aud1_gocue); - end - - CP_duration.value= SettlingIn_time + PreStim_time + A1_time + time_bet_aud1_gocue; - Total_CP_duration.value = CP_duration + time_go_cue; %#ok<*NASGU> - SoftPokeStayInterface(obj, 'set', 'soft_cp', 'Duration', PreStim_time + A1_time + time_bet_aud1_gocue ); - - case 'new_time_go_cue' - Total_CP_duration.value = CP_duration + time_go_cue; - SoundInterface(obj, 'set', 'GoSound', 'Dur1', value(time_go_cue)); - - case 'training_stage' - - if value(use_training) == 1 - - disable(training_stage); % user cannot change the training stages - else - - enable(training_stage); % user can change the training stages - trials_in_stage.value=0; - - switch value(training_stage) - - case {1,2} %% learning the reward sound association -left or right led on -> poke -> sound+reward - - time_go_cue.value=0.200; - disable(SettlingIn_time); - disable(PreStim_time); - disable(A1_time); - disable(time_bet_aud1_gocue); - - - case {3,4} % Centre poke without the A1_Stim but has violation in 4 - - time_go_cue.value=0.100; - enable(SettlingIn_time); - disable(PreStim_time); - disable(A1_time); - disable(time_bet_aud1_gocue); - - if n_done_trials <1 && warmup_on ==1 - CP_duration.value=value(init_CP_duration); - else - CP_duration.value=CP_duration; - end - Total_CP_duration.value = CP_duration + time_go_cue; %#ok<*NASGU> - - case {5,6,7} % - - time_go_cue.value=0.100; - enable(SettlingIn_time); - enable(PreStim_time); - enable(A1_time); - enable(time_bet_aud1_gocue); - - if random_prego_time == 1 - time_bet_aud1_gocue = randi([time_bet_aud1_gocue_Min, time_bet_aud1_gocue_Max],1,1); - end - - if random_A1_time == 1 - A1_time = randi([A1_time_Min, A1_time_Max],1,1); - end - - if random_PreStim_time == 1 - PreStim_time = randi([PreStim_time_Min, PreStim_time_Max],1,1); - end - - if n_done_trials <1 && warmup_on ==1 - CP_duration.value=value(init_CP_duration); - else - CP_duration.value=SettlingIn_time + A1_time + PreStim_time + time_bet_aud1_gocue; - end - Total_CP_duration.value = CP_duration + time_go_cue; %#ok<*NASGU> - end - end - - case 'prepare_next_trial' - - switch value(training_stage) - - case {1,2} %% learning the reward sound association -left or right led on -> poke -> sound+reward - - time_go_cue.value=0.200; - disable(SettlingIn_time); - disable(PreStim_time); - disable(A1_time); - disable(time_bet_aud1_gocue); - - - case {3,4} % Centre poke without the A1_Stim but has violation in 4 - - time_go_cue.value=0.100; - enable(SettlingIn_time); - disable(PreStim_time); - disable(A1_time); - disable(time_bet_aud1_gocue); - - if n_done_trials <1 && warmup_on ==1 - CP_duration.value=value(init_CP_duration); - else - CP_duration.value=CP_duration; - end - Total_CP_duration.value = CP_duration + time_go_cue; %#ok<*NASGU> - - case {5,6,7} % - - time_go_cue.value=0.100; - enable(SettlingIn_time); - enable(PreStim_time); - enable(A1_time); - enable(time_bet_aud1_gocue); - - if random_prego_time == 1 - time_bet_aud1_gocue = randi([time_bet_aud1_gocue_Min, time_bet_aud1_gocue_Max],1,1); - end - - if random_A1_time == 1 - A1_time = randi([A1_time_Min, A1_time_Max],1,1); - end - - if random_PreStim_time == 1 - PreStim_time = randi([PreStim_time_Min, PreStim_time_Max],1,1); - end - - if n_done_trials <1 && warmup_on ==1 - CP_duration.value=value(init_CP_duration); - else - CP_duration.value=SettlingIn_time + A1_time + PreStim_time + time_bet_aud1_gocue; - end - Total_CP_duration.value = CP_duration + time_go_cue; %#ok<*NASGU> - - - end - - - %% update hit_history, previous_sides, etc - was_viol=false; - was_hit=false; - was_timeout=false; - if n_done_trials>0 - if ~isempty(parsed_events) - if isfield(parsed_events,'states') - if isfield(parsed_events.states,'timeout_state') - was_timeout=rows(parsed_events.states.timeout_state)>0; - end - if isfield(parsed_events.states,'violation_state') - was_viol=rows(parsed_events.states.violation_state)>0; - end - end - - end - - violation_history.value=[violation_history(:); was_viol]; - timeout_history.value=[timeout_history(:); was_timeout]; - - ParamsSection(obj,'update_side_history'); - - if ~was_viol && ~was_timeout - %was_hit=rows(parsed_events.states.hit_state)>0; - was_hit=rows(parsed_events.states.second_hit_state)==0; - hit_history.value=[hit_history(:); was_hit]; - - else - % There was a violation or timeout - hit_history.value=[hit_history(:); nan]; - end - - % Now calculate the deltaF and sides - this maybe interesting - % even in a violation or timeout case. - - fn=fieldnames(parsed_events.states); - led_states=find(strncmp('led',fn,3)); - deltaF=0; - n_l=0; - n_r=0; - for lx=1:numel(led_states) - lind=led_states(lx); - if rows(parsed_events.states.(fn{lind}))>0 - if fn{lind}(end)=='l' - deltaF=deltaF-1; - n_l=n_l+1; - elseif fn{lind}(end)=='r' - deltaF=deltaF+1; - n_r=n_r+1; - elseif fn{lind}(end)=='b' - n_l=n_l+1; - n_r=n_r+1; - - end - end - - end - - % if deltaF>0 then a right poke is a hit - % if deltaF<0 then a left poke is a hit - - deltaf_history.value=[deltaf_history(:); deltaF]; - - end - - if antibias_LRprob ==1 - if n_done_trials >ntrial_correct_bias && ~was_viol && ~was_timeout - nonan_hit_history=value(hit_history); - nonan_hit_history(isnan(nonan_hit_history))=[]; - nonan_previous_sides=value(previous_sides); - nan_history=value(hit_history); - nonan_previous_sides(isnan(nan_history))=[]; - AntibiasSectionAthena(obj, 'update', value(LeftProb), nonan_hit_history(:)',nonan_previous_sides(:)); % <~> Transposed hit history so that it is the expected column vector. (Antibias errors out otherwise.) 2007.09.05 01:39 - end - - - if ~isinf(MaxSame) && length(previous_sides) > MaxSame && ... - all(previous_sides(n_done_trials-MaxSame+1:n_done_trials) == previous_sides(n_done_trials)) %#ok - if previous_sides(end)=='l' - ThisTrial.value = 'RIGHT'; - else - ThisTrial.value = 'LEFT'; - end - else - choiceprobs = AntibiasSectionAthena(obj, 'get_posterior_probs'); - if rand(1) <= choiceprobs(1) - ThisTrial.value = 'LEFT'; - else - ThisTrial.value = 'RIGHT'; - end - end - - else - if (rand(1)<=LeftProb) - ThisTrial.value='LEFT'; - - else - ThisTrial.value='RIGHT'; - end - - end - - - - -% %% Do the anti-bias with changing reward delivery -% % reset anti-bias -% left_wtr_mult.value=1; -% right_wtr_mult.value=1; -% if n_done_trials>ntrial_correct_bias && antibias_wtr_mult==1 -% hh=hit_history(n_done_trials-ntrial_correct_bias:n_done_trials); -% ps=previous_sides(n_done_trials-ntrial_correct_bias:n_done_trials); -% -% right_hit=nanmean(hh(ps=='r')); -% left_hit=nanmean(hh(ps=='l')); -% -% if abs(right_hit-left_hit) - - case 'get_left_prob' - x = value(LeftProb); - - case 'get_cp_history' - x = cell2mat(get_history(CP_duration)); - - case 'get_stimdur_history' - x = cell2mat(get_history(A1_time)); - - case 'update_side_history' - if strcmp(ThisTrial, 'LEFT') - ps=value(previous_sides); - ps(n_done_trials)='l'; - previous_sides.value=ps; - - else - ps=value(previous_sides); - ps(n_done_trials)='r'; - previous_sides.value=ps; - end - - case 'get_current_side' - if strcmp(ThisTrial, 'LEFT') - x = 'l'; %#ok - else - x = 'r'; - end - - - case 'close' - % Delete all SoloParamHandles who belong to this object and whose - % fullname starts with the name of this mfile: - delete_sphandle('owner', ['^@' class(obj) '$'], ... - 'fullname', ['^' mfilename]); - - case 'reinit' - currfig = double(gcf); - - % Get the original GUI position and figure: - x = my_gui_info(1); y = my_gui_info(2); figure(my_gui_info(3)); - - % Delete all SoloParamHandles who belong to this object and whose - % fullname starts with the name of this mfile: - delete_sphandle('owner', ['^@' class(obj) '$'], ... - 'fullname', ['^' mfilename]); - - % Reinitialise at the original GUI position and figure: - [x, y] = feval(mfilename, obj, 'init', x, y); - - % Restore the current figure: - figure(currfig); - -end - - diff --git a/Protocols/@Arpit_CentrePokeTraining/ParamsSection.m b/Protocols/@Arpit_CentrePokeTraining/ParamsSection.m index 01f5ef35..1100582a 100644 --- a/Protocols/@Arpit_CentrePokeTraining/ParamsSection.m +++ b/Protocols/@Arpit_CentrePokeTraining/ParamsSection.m @@ -13,15 +13,6 @@ SoloParamHandle(obj, 'my_gui_info', 'value', [x y double(gcf)], 'saveable', 0); y0 = y; - [x, y] = AntibiasSectionAthena(obj, 'init', x, y); - - - ToggleParam(obj, 'antibias_LRprob', 0, x,y,... - 'OnString', 'AB_Prob ON',... - 'OffString', 'AB_Prob OFF',... - 'TooltipString', sprintf(['If on (Yellow) then it enables the AntiBias algorithm\n'... - 'based on changing the probablity of Left vs Right'])); - next_row(y); NumeditParam(obj, 'LeftProb', 0.5, x, y); next_row(y); set_callback(LeftProb, {mfilename, 'new_leftprob'}); @@ -34,6 +25,8 @@ DispParam(obj, 'ThisTrial', 'LEFT', x, y); next_row(y); SoloParamHandle(obj, 'previous_sides', 'value', []); DeclareGlobals(obj, 'ro_args', 'previous_sides'); + + SubheaderParam(obj, 'title', 'Params Section', x, y); next_row(y, 1.5); next_column(x); y = 5; @@ -47,11 +40,14 @@ NumeditParam(obj, 'violation_iti', 1, x,y,'label','Violation Timeout','TooltipString','Center poke violation duration'); % Reward Collection next_row(y); - NumeditParam(obj, 'RewardCollection_duration', 10, x,y,'label','RewardCollection_dur','TooltipString','Wait until rat collects the reward else a timeout'); + NumeditParam(obj, 'RewardCollection_duration', 6, x,y,'label','RewardCollection_dur','TooltipString','Wait until rat collects the reward else a timeout'); next_row(y); NumeditParam(obj, 'SideLed_duration', 1, x,y,'label','Side LED duration','TooltipString','Duration of SideLed'); + next_row(y); % Centre Poke next_row(y); + NumeditParam(obj, 'cp_timeout', 5, x,y, 'TooltipString','Time from trial start for rat to centre poke else timeout'); + next_row(y); NumeditParam(obj, 'legal_cbreak', 0.1, x,y, 'TooltipString','Time in sec for which it is ok to be outside the center port before a violation occurs.'); set_callback(legal_cbreak, {mfilename, 'new_legal_cbreak'}); next_row(y); @@ -68,7 +64,7 @@ set_callback(PreStim_time_Max, {mfilename, 'new_CP_duration'}); next_row(y); % A1 Time - NumeditParam(obj, 'A1_time_Min', 0.4, x,y,'label','Min time AUD1','TooltipString','Min value to select the Duration of fixed stimulus'); + NumeditParam(obj, 'A1_time_Min', 0.1, x,y,'label','Min time AUD1','TooltipString','Min value to select the Duration of fixed stimulus'); set_callback(A1_time_Min, {mfilename, 'new_CP_duration'}); next_row(y); NumeditParam(obj, 'A1_time', 0.4, x,y,'label','AUD1 Time','TooltipString','Actual Duration of fixed stimulus'); @@ -123,9 +119,10 @@ next_column(x); y=5; - NumeditParam(obj,'trials_in_stage',1,x,y,'label','Trial Counter'); - next_row(y); - NumeditParam(obj,'training_stage',1,x,y,'label','Training Stage'); + MenuParam(obj, 'training_stage', {'Stage 1'; 'Stage 2'; 'Stage 3';... + 'Stage 4'; 'Stage 5'; 'Stage 6'; 'Stage 7'}, 1, x, y, ... + 'label', 'Active Stage', 'TooltipString', 'the current training stage'); + % NumeditParam(obj,'training_stage',1,x,y,'label','Training Stage'); set_callback(training_stage, {mfilename, 'Changed_Training_Stage'}); next_row(y); ToggleParam(obj,'use_training',0,x,y,'OnString','Using Autotrain','OffString','Manual Settings'); @@ -161,17 +158,18 @@ 'A1_time';'time_bet_aud1_gocue' ; 'PreStim_time'; 'drink_time';'reward_delay';'left_wtr_mult';... 'right_wtr_mult';'antibias_wtr_mult';... - 'timeout_iti';'violation_iti'}); + 'cp_timeout';'timeout_iti';'violation_iti'}); - % History of hit/miss: - SoloParamHandle(obj, 'deltaf_history', 'value', []); - - SoloFunctionAddVars('OverallPerformanceSection', 'ro_args', ... - {'training_stage'}); - + + SoloFunctionAddVars('SessionPerformanceSection', 'ro_args', ... + {'training_stage'}); SoloParamHandle(obj, 'previous_parameters', 'value', []); + + + %%%%%%%%% Switch b/w Actions within ParamSection %%%%%%%%%%%%% + case 'new_leftprob' AntibiasSectionAthena(obj, 'update_biashitfrac', value(LeftProb)); @@ -199,28 +197,27 @@ end CP_duration.value= SettlingIn_time + PreStim_time + A1_time + time_bet_aud1_gocue; - Total_CP_duration.value = CP_duration + time_go_cue; %#ok<*NASGU> - SoftPokeStayInterface(obj, 'set', 'soft_cp', 'Duration', PreStim_time + A1_time + time_bet_aud1_gocue ); + Total_CP_duration.value = CP_duration + time_go_cue; %#ok<*NASGU> case 'new_time_go_cue' Total_CP_duration.value = CP_duration + time_go_cue; - SoundInterface(obj, 'set', 'GoSound', 'Dur1', value(time_go_cue)); case 'Changed_Training_Stage' if value(use_training) == 1 disable(training_stage); % user cannot change the training stages + disable(PreStim_time); + disable(time_bet_aud1_gocue); else enable(training_stage); % user can change the training stages - trials_in_stage.value=0; + % trials_in_stage.value=0; switch value(training_stage) case {1,2} %% learning the reward sound association -left or right led on -> poke -> sound+reward - time_go_cue.value=0.200; disable(SettlingIn_time); disable(PreStim_time); disable(A1_time); @@ -229,11 +226,10 @@ case {3,4} % Centre poke without the A1_Stim but has violation in 4 - time_go_cue.value=0.100; enable(SettlingIn_time); disable(PreStim_time); disable(A1_time); - disable(time_bet_aud1_gocue); + enable(time_bet_aud1_gocue); if n_done_trials <1 && warmup_on ==1 CP_duration.value=value(init_CP_duration); @@ -244,28 +240,30 @@ case {5,6,7} % - time_go_cue.value=0.100; enable(SettlingIn_time); enable(PreStim_time); enable(A1_time); enable(time_bet_aud1_gocue); if random_prego_time == 1 - time_bet_aud1_gocue = randi([time_bet_aud1_gocue_Min, time_bet_aud1_gocue_Max],1,1); + time_range_go_cue = time_bet_aud1_gocue_Min:0.01:time_bet_aud1_gocue_Max; + time_bet_aud1_gocue = time_range_go_cue(randi([1, numel(time_range_go_cue)],1,1)); end if random_A1_time == 1 - A1_time = randi([A1_time_Min, A1_time_Max],1,1); + time_range_A1_time = A1_time_Min: 0.01 : A1_time_Max; + A1_time = time_range_A1_time(randi([1, numel(time_range_A1_time)],1,1)); end if random_PreStim_time == 1 - PreStim_time = randi([PreStim_time_Min, PreStim_time_Max],1,1); + time_range_PreStim_time = PreStim_time_Min : 0.01 : PreStim_time_Max; + PreStim_time = time_range_PreStim_time(randi([1, numel(time_range_PreStim_time)],1,1)); end if n_done_trials <1 && warmup_on ==1 - CP_duration.value=value(init_CP_duration); + CP_duration.value = value(init_CP_duration); else - CP_duration.value=SettlingIn_time + A1_time + PreStim_time + time_bet_aud1_gocue; + CP_duration.value = SettlingIn_time + A1_time + PreStim_time + time_bet_aud1_gocue; end Total_CP_duration.value = CP_duration + time_go_cue; %#ok<*NASGU> end @@ -290,7 +288,7 @@ enable(SettlingIn_time); disable(PreStim_time); disable(A1_time); - disable(time_bet_aud1_gocue); + enable(time_bet_aud1_gocue); if n_done_trials <1 && warmup_on ==1 CP_duration.value=value(init_CP_duration); @@ -330,9 +328,8 @@ end - %% update hit_history, previous_sides, etc + %% update violation, timeout, previous_sides, etc was_viol=false; - was_hit=false; was_timeout=false; if n_done_trials>0 if ~isempty(parsed_events) @@ -352,77 +349,26 @@ ParamsSection(obj,'update_side_history'); - if ~was_viol && ~was_timeout - %was_hit=rows(parsed_events.states.hit_state)>0; - was_hit=rows(parsed_events.states.second_hit_state)==0; - hit_history.value=[hit_history(:); was_hit]; - - else - % There was a violation or timeout - hit_history.value=[hit_history(:); nan]; - end - - % Now calculate the deltaF and sides - this maybe interesting - % even in a violation or timeout case. - - fn=fieldnames(parsed_events.states); - led_states=find(strncmp('led',fn,3)); - deltaF=0; - n_l=0; - n_r=0; - for lx=1:numel(led_states) - lind=led_states(lx); - if rows(parsed_events.states.(fn{lind}))>0 - if fn{lind}(end)=='l' - deltaF=deltaF-1; - n_l=n_l+1; - elseif fn{lind}(end)=='r' - deltaF=deltaF+1; - n_r=n_r+1; - elseif fn{lind}(end)=='b' - n_l=n_l+1; - n_r=n_r+1; - - end - end - - end - - % if deltaF>0 then a right poke is a hit - % if deltaF<0 then a left poke is a hit - - deltaf_history.value=[deltaf_history(:); deltaF]; - end - if antibias_LRprob ==1 - if n_done_trials >ntrial_correct_bias && ~was_viol && ~was_timeout - nonan_hit_history=value(hit_history); - nonan_hit_history(isnan(nonan_hit_history))=[]; - nonan_previous_sides=value(previous_sides); - nan_history=value(hit_history); - nonan_previous_sides(isnan(nan_history))=[]; - AntibiasSectionAthena(obj, 'update', value(LeftProb), nonan_hit_history(:)',nonan_previous_sides(:)); % <~> Transposed hit history so that it is the expected column vector. (Antibias errors out otherwise.) 2007.09.05 01:39 + %% Choose Side for the Next Trial + + if ~isinf(MaxSame) && length(previous_sides) > MaxSame && ... + all(previous_sides(n_done_trials-MaxSame+1:n_done_trials) == previous_sides(n_done_trials)) %#ok + if previous_sides(end)=='l' + ThisTrial.value = 'RIGHT'; + else + ThisTrial.value = 'LEFT'; end - - if ~isinf(MaxSame) && length(previous_sides) > MaxSame && ... - all(previous_sides(n_done_trials-MaxSame+1:n_done_trials) == previous_sides(n_done_trials)) %#ok - if previous_sides(end)=='l' - ThisTrial.value = 'RIGHT'; - else - ThisTrial.value = 'LEFT'; - end + if previous_sides(end)=='r' + ThisTrial.value = 'LEFT'; else - choiceprobs = AntibiasSectionAthena(obj, 'get_posterior_probs'); - if rand(1) <= choiceprobs(1) - ThisTrial.value = 'LEFT'; - else - ThisTrial.value = 'RIGHT'; - end + ThisTrial.value = 'RIGHT'; end else + if (rand(1)<=LeftProb) ThisTrial.value='LEFT'; @@ -432,7 +378,7 @@ end - + %% % %% Do the anti-bias with changing reward delivery @@ -485,15 +431,15 @@ x = cell2mat(get_history(A1_time)); case 'update_side_history' - if strcmp(ThisTrial, 'LEFT') - ps=value(previous_sides); - ps(n_done_trials)='l'; - previous_sides.value=ps; - - else - ps=value(previous_sides); - ps(n_done_trials)='r'; - previous_sides.value=ps; + if strcmp(ThisTrial, 'LEFT') + ps=value(previous_sides); + ps(n_done_trials)='l'; + previous_sides.value=ps; + + else + ps=value(previous_sides); + ps(n_done_trials)='r'; + previous_sides.value=ps; end case 'get_current_side' diff --git a/Protocols/@Arpit_CentrePokeTraining/SessionPerformanceSection.m b/Protocols/@Arpit_CentrePokeTraining/SessionPerformanceSection.m new file mode 100644 index 00000000..1e66df8c --- /dev/null +++ b/Protocols/@Arpit_CentrePokeTraining/SessionPerformanceSection.m @@ -0,0 +1,174 @@ +% [x, y] = SessionPerformanceSection(obj, action, x,y) +% +% Reports overall performance. Uses training_stage from SideSection. +% +% PARAMETERS: +% ----------- +% +% action One of: +% 'init' To initialise the section and set up the GUI +% for it +% +% 'close' Delete all of this section's GUIs and data +% +% 'reinit' Delete all of this section's GUIs and data, +% and reinit, at the same position on the same +% figure as the original section GUI was placed. +% +% 'evalueta' Look at history and compute hit fraction, etc. +% +% x, y Only relevant to action = 'init'; they indicate the initial +% position to place the GUI at, in the current figure window +% +% RETURNS: +% -------- +% +% perf When action == 'init', returns x and y, pixel positions on +% the current figure, updated after placing of this section's GUI. +% When action == 'evaluate', returns a vector with elements +% [ntrials, violation_rate, left_hit_frac, right_hit_frac, hit_frac] +% +% + +% CDB, 23-March-2013 + +function [x, y] = SessionPerformanceSection(obj, action, x,y) + +GetSoloFunctionArgs(obj); + +switch action + + % ------------------------------------------------------------------ + % INIT + % ------------------------------------------------------------------ + + case 'init' + + SoloParamHandle(obj, 'my_gui_info', 'value', [x y double(gcf)], 'saveable', 0); + + DispParam(obj, 'ntrials', 0, x, y,'label','Session Trial Counter', 'TooltipString', ... + 'trials done in this session'); + next_row(y); + DispParam(obj, 'ntrials_stage', 0, x, y,'label','Session Trial Counter', 'TooltipString', ... + 'trials completed in this training stage'); + next_row(y); + DispParam(obj, 'violation_rate', 0, x, y,'label','Session Violation Rate', 'TooltipString', ... + 'Fraction of trials with a center poke violation in this session'); + next_row(y); + DispParam(obj, 'timeout_rate', 0, x, y,'label','Session Timeout Rate', 'TooltipString', ... + 'Fraction of trials with timeout in this session'); + next_row(y); + DispParam(obj, 'violation_recent', 0, x, y,'label','Recent Violation Rate', 'TooltipString', ... + 'Fraction of trials with a center poke violation in the past 20 trials'); + next_row(y); + DispParam(obj, 'timeout_recent', 0, x, y,'label','Recent Timeout Rate', 'TooltipString', ... + 'Fraction of trials with a center poke violation in the past 20 trials'); + next_row(y); + DispParam(obj, 'violation_stage', 0, x, y,'label','Stage Violation Rate', 'TooltipString', ... + 'Fraction of violations in this training stage'); + next_row(y); + DispParam(obj, 'violation_stage', 0, x, y,'label','Stage Timeout Rate', 'TooltipString', ... + 'Fraction of timeouts in this training stage'); + next_row(y); + SubheaderParam(obj, 'title', 'Overall Performance', x, y); + next_row(y, 1.5); + SoloParamHandle(obj, 'previous_parameters', 'value', []); + + % ------------------------------------------------------------------ + % evaluate + % ------------------------------------------------------------------ + + case 'evaluate' + + switch value(training_stage) + case 1 %% center led on -> poke in the center -> go cue -> reward light and sound + if n_completed_trials > 1 + ntrials.value = n_completed_trials; + violation_rate.value = nan; + timeout_rate.value = nan; + end + violation_recent.value = nan; + timeout_recent.value = nan; + + violation_stage.value = nan; + timeout_stage.value = nan; + + case {2,3} %% center led on -> poke in the center -> go cue -> reward light and sound + if n_completed_trials > 1 + ntrials.value = n_completed_trials; + violation_rate.value = nan; + timeout_rate.value = numel(find(timeout_history))/n_completed_trials; + end + + violation_recent.value = nan; + violation_stage.value = nan; + + if n_completed_trails >= 20 + timeout_recent.value = numel(find(timeout_history(end-19:end)))/20; + else + timeout_recent.value = nan; + end + + timeout_stage.value = nan; + + + case {4,5,6,7} + + if n_completed_trials > 1 + ntrials.value = n_completed_trials; + violation_rate.value = numel(find(violation_history))/n_completed_trials; + timeout_rate.value = numel(find(timeout_history))/n_completed_trials; + end + + if n_completed_trails >= 20 + timeout_recent.value = numel(find(timeout_history(end-19:end)))/20; + violation_recent.value = numel(find(violation_history(end-19:end)))/20; + else + timeout_recent.value = nan; + violation_recent.value = nan; + end + + + end + + if nargout > 0 + x = [n_completed_trials, value(ntrials_stage), value(violation_rate), value(timeout_rate), value(violation_recent), ... + value(timeout_recent), value(violation_stage), value(timeout_stage)]; + end + + + % ------------------------------------------------------------------ + % close + % ------------------------------------------------------------------ + + case 'close' + % Delete all SoloParamHandles who belong to this object and whose + % fullname starts with the name of this mfile: + delete_sphandle('owner', ['^@' class(obj) '$'], ... + 'fullname', ['^' mfilename]); + + + % ------------------------------------------------------------------ + % reinit + % ------------------------------------------------------------------ + + case 'reinit' + currfig = double(gcf); + + % Get the original GUI position and figure: + x = my_gui_info(1); y = my_gui_info(2); figure(my_gui_info(3)); + + % Delete all SoloParamHandles who belong to this object and whose + % fullname starts with the name of this mfile: + delete_sphandle('owner', ['^@' class(obj) '$'], ... + 'fullname', ['^' mfilename]); + + % Reinitialise at the original GUI position and figure: + [x, y] = feval(mfilename, obj, 'init', x, y); + + % Restore the current figure: + figure(currfig); + +end + + diff --git a/Protocols/@Arpit_CentrePokeTraining/SoundSection.m b/Protocols/@Arpit_CentrePokeTraining/SoundSection.m index 4e49253d..5ecb8231 100644 --- a/Protocols/@Arpit_CentrePokeTraining/SoundSection.m +++ b/Protocols/@Arpit_CentrePokeTraining/SoundSection.m @@ -27,24 +27,20 @@ 'Name', mfilename), 'saveable', 0); set(double(gcf), 'Visible', 'off'); x=10;y=10; - - [x,y]=SoundInterface(obj,'add','ViolationSound',x,y,'Volume',0.005,'Freq',9000,'Duration',0.5); -% [x,y]=SoundInterface(obj,'add','ViolationSound',x,y,'Style','WhiteNoise','Volume',0.01); - [x,y]=SoundInterface(obj,'add','ErrorSound',x,y,'Style','WhiteNoise','Volume',0.08); + + [x,y]=SoundInterface(obj,'add','ViolationSound',x,y,'Volume',0.005,'Freq',1000,'Duration',0.5); +% [x,y]=SoundInterface(obj,'add','ViolationSound',x,y,'Style','WhiteNoise','Volume',0.01); [x,y]=SoundInterface(obj,'add','TimeoutSound',x,y,'Style','WhiteNoise','Volume',0.08,'Duration',0.5); + [x,y]=SoundInterface(obj,'add','RewardSound',x,y,'Style','Bups','Volume',1,'Freq',5,'Duration',1.5); + [x,y]=SoundInterface(obj,'add','ErrorSound',x,y,'Style','WhiteNoise','Volume',0.08); next_column(x); y=10; [x,y]=SoundInterface(obj,'add','GoSound',x,y,'Style','Tone','Volume',0.005,'Freq',3000,'Duration',0.2); - SoundInterface(obj, 'disable', 'GoSound', 'Dur1'); - - [x,y]=SoundInterface(obj,'add','RewardSound',x,y,'Style','Bups','Volume',1,'Freq',5,'Duration',1.5); - [x,y]=SoundInterface(obj,'add','SOneSound',x,y,'Style','Tone','Volume',0.005,'Freq',3000,'Duration',0.2); - SoundInterface(obj, 'disable', 'GoSound', 'Dur1'); + SoundInterface(obj, 'disable', 'GoSound', 'Dur1'); SoundInterface(obj, 'disable', 'GoSound', 'Freq1'); + [x,y]=SoundInterface(obj,'add','SOneSound',x,y,'Style','Tone','Volume',0.005,'Freq',3000,'Duration',0.2); [x,y]=SoundInterface(obj,'add','STwoSound',x,y,'Style','WhiteNoise','Volume',0.08); - - x=oldx; y=oldy; figure(parentfig); diff --git a/Protocols/@Arpit_CentrePokeTraining/pipeline_Arpit_CentrePokeTraining_experimenter_ratname_250319c.m b/Protocols/@Arpit_CentrePokeTraining/pipeline_Arpit_CentrePokeTraining_experimenter_ratname_250319c.m new file mode 100644 index 00000000..8033483a --- /dev/null +++ b/Protocols/@Arpit_CentrePokeTraining/pipeline_Arpit_CentrePokeTraining_experimenter_ratname_250319c.m @@ -0,0 +1,641 @@ +%Training stage file. +%Please use the session automator window exclusively +%to edit this file. + +function varargout = pipeline_Arpit_CentrePokeTraining_experimenter_ratname_250319c(obj, action, varargin) + +GetSoloFunctionArgs('func_owner', ['@' class(obj)], 'func_name', 'SessionModel'); + +pairs = {'helper_vars_eval', true; + 'stage_algorithm_eval', true; + 'completion_test_eval', false; + 'eod_logic_eval', false}; +parseargs(varargin, pairs); + +switch action + + +%% Familiarize with Reward Side Pokes + +% +case 'Familiarize with Reward Side Pokes' + if helper_vars_eval + GetSoloFunctionArgs(obj); + ClearHelperVarsNotOwned(obj); + % + stage1_trial_counter = 0; + stage1_trial_counter_today = 0; + stage1_trial_counter_oppSide = 0; + % + end + if stage_algorithm_eval + GetSoloFunctionArgs(obj); + ClearHelperVarsNotOwned(obj); + % + ParamsSection_MaxSame.value = 4; + ParamsSection_training_stage.value = 1; + stage1_trial_counter = stage1_trial_counter + 1; + stage1_trial_counter_today = stage1_trial_counter_today + 1; + if value(previous_sides(end)) ~= value(ParamsSection_ThisTrial) + stage1_trial_counter_oppSide = stage1_trial_counter_oppSide + 1; + end + % + end +if completion_test_eval +GetSoloFunctionArgs(obj); +ClearHelperVarsNotOwned(obj); +clear('ans'); +% +% only run it if its the start of the day, so number of trials the rat did +% is less +if n_completed_trial < 100 + if stage1_trial_counter > 300 && stage1_trial_counter_oppSide > 150 + sm = value(SessionDefinition_my_session_model); + SessionDefinition_my_session_model.value = jump(sm, 'offset',+1); + end +end +% +if exist('ans', 'var') +varargout{1}=logical(ans); clear('ans'); +else +varargout{1}=false; +end +end +if eod_logic_eval +GetSoloFunctionArgs(obj); +ClearHelperVarsNotOwned(obj); +% +stage1_trial_counter_today = 0; +if stage1_trial_counter > 300 && stage1_trial_counter_oppSide > 150 + sm = value(SessionDefinition_my_session_model); + SessionDefinition_my_session_model.value = jump(sm, 'offset',+1); +end +% +end +% + +%% Timeout Rewarded Side Pokes + +% +case 'Timeout Rewarded Side Pokes' +if helper_vars_eval +GetSoloFunctionArgs(obj); +ClearHelperVarsNotOwned(obj); +% + stage2_trial_counter = 0; + stage2_trial_counter_today = 0; + stage2_trial_counter_oppSide = 0; + opp_side_trials = 0; + stage2_timeout_percent = 0; + max_reward_collection_dur = 15; % this is the max I will allow + min_reward_collection_dur = 5; % this is the max I will allow +% +end +if stage_algorithm_eval +GetSoloFunctionArgs(obj); +ClearHelperVarsNotOwned(obj); +% +ParamsSection_MaxSame.value = 4; +ParamsSection_training_stage.value = 2; +stage2_trial_counter = stage2_trial_counter + 1; +stage2_trial_counter_today = stage2_trial_counter_today + 1; +if value(previous_sides(end)) ~= value(ParamsSection_ThisTrial) + stage2_trial_counter_oppSide = stage2_trial_counter_oppSide + 1; + opp_side_trials = opp_side_trials + 1; +end +% Update the reward collection time based upon behav +if size(timeout_history) > 5 + if all(value(timeout_history(end-1:end))) && opp_side_trials >= 2 + ParamsSection_RewardCollection_duration.value = min([value(ParamsSection_RewardCollection_duration) + 1,... + max_reward_collection_dur]); + opp_side_trials = 0; + end + + if ~any(value(timeout_history(end-1:end))) && opp_side_trials >= 2 + ParamsSection_RewardCollection_duration.value = max([value(ParamsSection_RewardCollection_duration) - 1,... + min_reward_collection_dur]); + opp_side_trials = 0; + end +end + +if size(timeout_history) > 20 + if all(value(timeout_history(end-19:end))) + ParamsSection_RewardCollection_duration.value = 30; + end +end + +stage2_timeout_percent = ((stage2_timeout_percent * stage2_trial_counter) + double(timeout_history(end)))/(stage2_trial_counter + 1); + +% +end +if completion_test_eval +GetSoloFunctionArgs(obj); +ClearHelperVarsNotOwned(obj); +clear('ans'); +% +if n_completed_trial > 50 + if stage2_trial_counter > 1000 && stage2_trial_counter_oppSide > 200 + sm = value(SessionDefinition_my_session_model); + SessionDefinition_my_session_model.value = jump(sm, 'offset',+1); + end +end +% +if exist('ans', 'var') +varargout{1}=logical(ans); clear('ans'); +else +varargout{1}=false; +end +end +if eod_logic_eval +GetSoloFunctionArgs(obj); +ClearHelperVarsNotOwned(obj); +% + +% +end +% + +%% Introduce Centre Poke + +% +case 'Introduce Centre Poke' +if helper_vars_eval +GetSoloFunctionArgs(obj); +ClearHelperVarsNotOwned(obj); +% +% Maximum & Minimum duration of center poke, in secs: +cp_max = value(ParamsSection_SettlingIn_time) + value(ParamsSection_legal_cbreak); +cp_min = value(ParamsSection_init_CP_duration); +% Fractional increment in center poke duration every time there is a non-cp-violation trial: +cp_fraction = 0.001; +% Minimum increment (in secs) in center poke duration every time there is a non-cp-violation trial: +cp_minimum_increment = 0.001; + +stage3_trial_counter = 0; +stage3_trial_counter_today = 0; +stage3_timeout_percent = 0; + +last_session_cp = 0; +% +end +if stage_algorithm_eval +GetSoloFunctionArgs(obj); +ClearHelperVarsNotOwned(obj); +% + +ParamsSection_training_stage.value = 3; +stage3_trial_counter = stage3_trial_counter + 1; +stage3_trial_counter_today = stage3_trial_counter_today + 1; +stage3_timeout_percent = ((stage3_timeout_percent * stage3_trial_counter) + double(timeout_history(end)))/(stage3_trial_counter + 1); + +% Change the value of CP Duration +if n_completed_trials < 1 + ParamsSection_CP_duration.value = value(ParamsSection_init_CP_duration); +else + if ~timeout_history(end) && value(ParamsSection_CP_duration) < cp_max + increment = value(ParamsSection_CP_duration)*cp_fraction; + if increment < cp_minimum_increment + increment = value(cp_minimum_increment); + end + ParamsSection_CP_duration.value = value(ParamsSection_CP_duration) + increment; + end +end +% +end +if completion_test_eval +GetSoloFunctionArgs(obj); +ClearHelperVarsNotOwned(obj); +clear('ans'); +% +if value(ParamsSection_CP_duration) >= cp_max + sm = value(SessionDefinition_my_session_model); + SessionDefinition_my_session_model.value = jump(sm, 'offset',+1); +end +% +if exist('ans', 'var') +varargout{1}=logical(ans); clear('ans'); +else +varargout{1}=false; +end +end +if eod_logic_eval +GetSoloFunctionArgs(obj); +ClearHelperVarsNotOwned(obj); +% +stage3_trial_counter_today = 0; +last_session_cp = value(ParamsSection_CP_duration); +% +end +% + +%% Introduce Violation for Centre Poke + +% +case 'Introduce Violation for Centre Poke' +if helper_vars_eval +GetSoloFunctionArgs(obj); +ClearHelperVarsNotOwned(obj); +% + +% Maximum & Minimum duration of center poke, in secs: +cp_min = value(ParamsSection_SettlingIn_time) + value(ParamsSection_legal_cbreak); +cp_max = 1.5; +% Fractional increment in center poke duration every time there is a non-cp-violation trial: +cp_fraction = 0.001; +% Minimum increment (in secs) in center poke duration every time there is a non-cp-violation trial: +cp_minimum_increment = 0.001; +last_session_cp = 0; +stage4_trial_counter = 0; +stage4_trial_counter_today = 0; +stage4_timeout_percent = 0; +stage4_violation_percent = 0; + +% +end +if stage_algorithm_eval +GetSoloFunctionArgs(obj); +ClearHelperVarsNotOwned(obj); +% +ParamsSection_training_stage.value = 4; +stage4_trial_counter = stage4_trial_counter + 1; +stage4_trial_counter_today = stage4_trial_counter_today + 1; +stage4_timeout_percent = ((stage4_timeout_percent * stage4_trial_counter) + double(timeout_history(end)))/(stage4_trial_counter + 1); +stage4_violation_percent = ((stage4_violation_percent * stage4_trial_counter) + double(violation_history(end)))/(stage4_trial_counter + 1); + +% Change the value of CP Duration +if n_completed_trials < 1 + ParamsSection_CP_duration.value = value(ParamsSection_init_CP_duration); +else + if ~violation_history(end) && ~timeout_history(end) && value(ParamsSection_CP_duration) < cp_max + increment = value(ParamsSection_CP_duration)*cp_fraction; + if increment < cp_minimum_increment + increment = value(cp_minimum_increment); + end + ParamsSection_CP_duration.value = value(ParamsSection_CP_duration) + increment; + end +end +if value(ParamsSection_CP_duration) > cp_max + ParamsSection_CP_duration.value = cp_max; +end +% +end +if completion_test_eval +GetSoloFunctionArgs(obj); +ClearHelperVarsNotOwned(obj); +clear('ans'); +% +if value(ParamsSection_CP_duration) >= cp_max && n_completed_trials > 100 +if SessionPerformanceSection_violation_recent < 0.1 && SessionPerformanceSection_timeout_recent < 0.1 && stage4_violation_percent < 0.35 + sm = value(SessionDefinition_my_session_model); + SessionDefinition_my_session_model.value = jump(sm, 'offset',+1); + last_session_cp = value(ParamsSection_CP_duration); +end +end +% +if exist('ans', 'var') +varargout{1}=logical(ans); clear('ans'); +else +varargout{1}=false; +end +end +if eod_logic_eval +GetSoloFunctionArgs(obj); +ClearHelperVarsNotOwned(obj); +% +% Store the value of the total cp duration reached: +stage4_trial_counter_today = 0; +last_session_cp = value(ParamsSection_CP_duration); +% +end +% + +%% Introduce Stimuli Sound during Centre Poke + +% +case 'Introduce Stimuli Sound during Centre Poke' +if helper_vars_eval +GetSoloFunctionArgs(obj); +ClearHelperVarsNotOwned(obj); +% +cp_max = 5; +cp_min = 1.5; +% Fractional increment in center poke duration every time there is a non-cp-violation trial: +cp_fraction = 0.002; +% Minimum increment (in secs) in center poke duration every time there is a non-cp-violation trial: +cp_minimum_increment = 0.001; +% cp value for last session +last_session_cp = 0; +% Starting total center poke duration: +starting_total_cp = 0.5; +% number of warm-up trials +n_trial_warmup = 10; +stage5_trial_counter = 0; +stage5_trial_counter_today = 0; +stage5_timeout_percent = 0; +stage5_violation_percent = 0; +% +end +if stage_algorithm_eval +GetSoloFunctionArgs(obj); +ClearHelperVarsNotOwned(obj); +% + +ParamsSection_training_stage.value = 5; +stage5_trial_counter = stage5_trial_counter + 1; +stage5_trial_counter_today = stage5_trial_counter_today + 1; +stage5_timeout_percent = ((stage5_timeout_percent * stage5_trial_counter) + double(timeout_history(end)))/(stage5_trial_counter + 1); +stage5_violation_percent = ((stage5_violation_percent * stage5_trial_counter) + double(violation_history(end)))/(stage5_trial_counter + 1); + +% Change the value of CP Duration + +% Since starting a new session then do a pre warm up to last saved cp +% duration else continue with learning with increased poke time +if n_completed_trials == 0 + ParamsSection_CP_duration.value = value(ParamsSection_init_CP_duration); +elseif n_completed_trials == 1 + ParamsSection_CP_duration.value = starting_total_cp; +else + if ~violation_history(end) && ~timeout_history(end) + if value(ParamsSection_CP_duration) < max([cp_min,last_session_cp]) % warm up stage + increment = (max([cp_min,last_session_cp]) - value(ParamsSection_CP_duration))/ (n_trial_warmup - 1); + else + if value(ParamsSection_CP_duration) >= last_session_cp && value(ParamsSection_CP_duration) <= cp_max % no warm up stage + increment = value(ParamsSection_CP_duration)*cp_fraction; + if increment < cp_minimum_increment + increment = value(cp_minimum_increment); + end + end + end + + ParamsSection_CP_duration.value = value(ParamsSection_CP_duration) + increment; + + % Check if the values are within the required range + if value(ParamsSection_CP_duration) < starting_total_cp + ParamsSection_CP_duration.value = starting_total_cp; + end + if value(ParamsSection_CP_duration) > cp_max + ParamsSection_CP_duration.value = cp_max; + end + + end +end + +if value(ParamsSection_CP_duration) >= 1 + ParamsSection_PreStim_time.value = 0.4; + if value(ParamsSection_CP_duration) < 2 + ParamsSection_A1_time.value = 0.1; + elseif value(ParamsSection_CP_duration) < 2.5 && value(ParamsSection_CP_duration) >= 2 + ParamsSection_A1_time.value = 0.2; + elseif value(ParamsSection_CP_duration) < 3 && value(ParamsSection_CP_duration) >= 2.5 + ParamsSection_A1_time.value = 0.3; + else + ParamsSection_A1_time.value = 0.4; + end +elseif value(ParamsSection_CP_duration) < 1 && value(ParamsSection_CP_duration) >= starting_total_cp + ParamsSection_SettlingIn_time.value = 0.2; + ParamsSection_PreStim_time.value = 0.1; + ParamsSection_A1_time.value = 0.1; +end + +% +end +if completion_test_eval +GetSoloFunctionArgs(obj); +ClearHelperVarsNotOwned(obj); +clear('ans'); +% +if value(ParamsSection_CP_duration) >= cp_max && stage5_trial_counter > 1000 + if SessionPerformanceSection_violation_recent < 0.1 && SessionPerformanceSection_timeout_recent < 0.1 && stage5_violation_percent < 0.3 && n_completed_trials > 100 + sm = value(SessionDefinition_my_session_model); + SessionDefinition_my_session_model.value = jump(sm, 'offset',+1); + last_session_cp = value(ParamsSection_CP_duration); + end +end +% +if exist('ans', 'var') +varargout{1}=logical(ans); clear('ans'); +else +varargout{1}=false; +end +end +if eod_logic_eval +GetSoloFunctionArgs(obj); +ClearHelperVarsNotOwned(obj); +% +stage5_trial_counter_today = 0; +last_session_cp = value(ParamsSection_CP_duration); +% +end +% + +%% Vary Stimuli location during Centre Poke + +% +case 'Vary Stimuli location during Centre Poke' +if helper_vars_eval +GetSoloFunctionArgs(obj); +ClearHelperVarsNotOwned(obj); +% +stage6_trial_counter = 0; +stage6_trial_counter_today = 0; +stage6_timeout_percent = 0; +stage6_violation_percent = 0; +cp_max = 5; +n_trial_warmup = 20; +starting_total_cp = 0.5; +prestim_min = 0.5; +prestim_max = 2; + +% +end +if stage_algorithm_eval +GetSoloFunctionArgs(obj); +ClearHelperVarsNotOwned(obj); +% +ParamsSection_training_stage.value = 6; +stage6_trial_counter = stage6_trial_counter + 1; +stage6_trial_counter_today = stage6_trial_counter_today + 1; +stage6_timeout_percent = ((stage6_timeout_percent * stage6_trial_counter) + double(timeout_history(end)))/(stage6_trial_counter + 1); +stage6_violation_percent = ((stage6_violation_percent * stage6_trial_counter) + double(violation_history(end)))/(stage6_trial_counter + 1); + +% Warm Up If starting a new session +if n_completed_trials == 0 + ParamsSection_CP_duration.value = value(ParamsSection_init_CP_duration); +elseif n_completed_trials == 1 + ParamsSection_CP_duration.value = starting_total_cp; +else + if value(ParamsSection_CP_duration) < cp_max % warm up stage + if ~violation_history(end) && ~timeout_history(end) + increment = (cp_max - value(ParamsSection_CP_duration))/ (n_trial_warmup - 1); + ParamsSection_CP_duration.value = value(ParamsSection_CP_duration) + increment; + % Check if the values are within the required range + if value(ParamsSection_CP_duration) < starting_total_cp + ParamsSection_CP_duration.value = starting_total_cp; + end + if value(ParamsSection_CP_duration) > cp_max + ParamsSection_CP_duration.value = cp_max; + end + end + end +end + +if value(ParamsSection_CP_duration) < 3 % during the warm up phase + ParamsSection_SettlingIn_time.value = 0.2; + ParamsSection_PreStim_time.value = 0.1; + ParamsSection_A1_time.value = 0.1; +else + ParamsSection_A1_time.value = 0.4; % actual training stage + time_range_PreStim_time = prestim_min : 0.01 : prestim_max; + ParamsSection_PreStim_time.value = time_range_PreStim_time(randi([1, numel(time_range_PreStim_time)],1,1)); +end + +% +end +if completion_test_eval +GetSoloFunctionArgs(obj); +ClearHelperVarsNotOwned(obj); +clear('ans'); +% +if stage6_trial_counter > 1500 + if SessionPerformanceSection_violation_recent < 0.15 && SessionPerformanceSection_timeout_recent < 0.15 && stage6_violation_percent < 0.25 + sm = value(SessionDefinition_my_session_model); + SessionDefinition_my_session_model.value = jump(sm, 'offset',+1); + end +end +% +if exist('ans', 'var') +varargout{1}=logical(ans); clear('ans'); +else +varargout{1}=false; +end +end +if eod_logic_eval +GetSoloFunctionArgs(obj); +ClearHelperVarsNotOwned(obj); +% +stage6_trial_counter_today = 0; +% +end +% + +%% Variable Stimuli Go Cue location during Centre Poke + +% +case 'Variable Stimuli Go Cue location during Centre Poke' +if helper_vars_eval +GetSoloFunctionArgs(obj); +ClearHelperVarsNotOwned(obj); +% +stage7_trial_counter = 0; +stage7_trial_counter_today = 0; +stage7_timeout_percent = 0; +stage7_violation_percent = 0; + +% Variables for warmup stage +cp_max = 5; +n_trial_warmup = 20; +starting_total_cp = 0.5; +warmup_completed = 0; +% +end +if stage_algorithm_eval +GetSoloFunctionArgs(obj); +ClearHelperVarsNotOwned(obj); +% +ParamsSection_training_stage.value = 7; +stage7_trial_counter = stage7_trial_counter + 1; +stage7_trial_counter_today = stage7_trial_counter_today + 1; +stage7_timeout_percent = ((stage7_timeout_percent * stage7_trial_counter) + double(timeout_history(end)))/(stage7_trial_counter + 1); +stage7_violation_percent = ((stage7_violation_percent * stage7_trial_counter) + double(violation_history(end)))/(stage7_trial_counter + 1); + +if stage7_trial_counter < 2000 + % the rat has been not been trained for good amount of sessions wait for the user + % to play around with the settings + ParamsSection_warmup_on.value = 1; + ParamsSection_random_PreStim_time.value = 1; + ParamsSection_random_prego_time.value = 1; + ParamsSection_random_A1_time.value = 0; + ParamsSection_PreStim_time_Min.value = 0.2; + ParamsSection_PreStim_time_Max.value = 2; + ParamsSection_time_bet_aud1_gocue_Min.value = 0.2; + ParamsSection_time_bet_aud1_gocue_Min.value = 2; + ParamsSection_A1_time.value = 0.4; +end + +% Warm Up If starting a new session +if ParamsSection_warmup_on == 1 + if n_completed_trials == 0 + ParamsSection_CP_duration.value = value(ParamsSection_init_CP_duration); + warmup_completed = 0; + elseif n_completed_trials == 1 + ParamsSection_CP_duration.value = starting_total_cp; + else + if value(ParamsSection_CP_duration) <= cp_max % warm up stage + if ~violation_history(end) && ~timeout_history(end) + increment = (cp_max - value(ParamsSection_CP_duration))/ (n_trial_warmup - 1); + ParamsSection_CP_duration.value = value(ParamsSection_CP_duration) + increment; + % Check if the values are within the required range + if value(ParamsSection_CP_duration) < starting_total_cp + ParamsSection_CP_duration.value = starting_total_cp; + end + if value(ParamsSection_CP_duration) >= cp_max + ParamsSection_CP_duration.value = cp_max; + warmup_completed = 1; + end + end + end + end +else + warmup_completed = 1; +end + +if warmup_completed == 1 + if value(ParamsSection_random_A1_time) + time_range_A1_time = ParamsSection_A1_time_Min : 0.1 : ParamsSection_A1_time_Max; + ParamsSection_A1_time.value = time_range_A1_time(randi([1, numel(time_range_A1_time)],1,1)); + end + if value(ParamsSection_random_PreStim_time) + time_range_PreStim_time = ParamsSection_PreStim_time_Min : 0.1 : ParamsSection_PreStim_time_Max; + ParamsSection_PreStim_time.value = time_range_PreStim_time(randi([1, numel(time_range_PreStim_time)],1,1)); + end + if value(ParamsSection_random_prego_time) + time_range_prego_time = ParamsSection_time_bet_aud1_gocue_Min : 0.1 : ParamsSection_time_bet_aud1_gocue_Min; + ParamsSection_time_bet_aud1_gocue.value = time_range_prego_time(randi([1, numel(time_range_prego_time)],1,1)); + end +end +% +end +if completion_test_eval +GetSoloFunctionArgs(obj); +ClearHelperVarsNotOwned(obj); +clear('ans'); +% + +% +if exist('ans', 'var') +varargout{1}=logical(ans); clear('ans'); +else +varargout{1}=false; +end +end +if eod_logic_eval +GetSoloFunctionArgs(obj); +ClearHelperVarsNotOwned(obj); +% +stage7_trial_counter_today = 0; +% +end +% + + +end + +end + +%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +% + +% + +%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% diff --git a/Protocols/@TaskSwitch4/HistorySection.m b/Protocols/@TaskSwitch4/HistorySection.m new file mode 100644 index 00000000..f8cd1e25 --- /dev/null +++ b/Protocols/@TaskSwitch4/HistorySection.m @@ -0,0 +1,471 @@ +function [x, y] = HistorySection(obj, action, varargin) + +GetSoloFunctionArgs(obj); + + +switch action, + + % ------------------------------------------------------------------ + % INIT + % ------------------------------------------------------------------ + + + %%% list all the way the rat can do well/bad : nose, perf etc. + + case 'init' + x=varargin{1}; + y=varargin{2}; + + %%% info about last trial + DispParam(obj, 'last_result','', x, y, 'labelfraction', 0.5,'position', [x y 200 20]); next_row(y,1.1); + + %%% percent correct coh/incoh + DispParam(obj, 'correct_coherent',0, x, y, 'labelfraction', 0.55,'label','%hit coh','position', [x y 100 20]); + DispParam(obj, 'correct_incoherent',0, x, y, 'labelfraction', 0.55,'label','%hit incoh','position', [x+100 y 100 20]);next_row(y); + + %%% percent correct left/right + DispParam(obj, 'left_correct',0, x, y, 'labelfraction', 0.55,'label','%hit left','position', [x y 100 20]); + DispParam(obj, 'right_correct',0, x, y, 'labelfraction', 0.55,'label','%hit right','position', [x+100 y 100 20]);next_row(y); + + %%% percent correct dir/freq + DispParam(obj, 'dir_correct',0, x, y, 'labelfraction', 0.55,'label','%hit dir','position', [x y 100 20]); + DispParam(obj, 'freq_correct',0, x, y, 'labelfraction', 0.55,'label','%hit freq','position', [x+100 y 100 20]);next_row(y,1.1); + + %%% percent correct + DispParam(obj, 'total_correct',0, x, y, 'labelfraction', 0.55,'label','%hit','position', [x y 100 20]); + %%% violations + DispParam(obj, 'percent_violations',0, x, y, 'labelfraction', 0.55,'label','%viol','position', [x+100 y 100 20]);next_row(y,1.1); + %%% total number of trials + DispParam(obj, 'nTrials',0, x, y, 'labelfraction', 0.55,'position', [x y 100 20]); + %%% number of valid trials + DispParam(obj, 'nValid',0, x, y, 'labelfraction', 0.55,'position', [x+100 y 100 20]);next_row(y); + + SubheaderParam(obj,'title',mfilename,x,y); next_row(y); + + + + + + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + %%%%%%%%%%%%% INTERNAL VARIBLES %%%%%%%%%%%%% + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + %trial variables + %binary + SoloParamHandle(obj, 'was_hit', 'value', 0); + SoloParamHandle(obj, 'was_err', 'value', 0); + SoloParamHandle(obj, 'was_nic_err', 'value', 0); + SoloParamHandle(obj, 'was_timeout', 'value', 0); + SoloParamHandle(obj, 'was_wait', 'value', 0); + + SoloParamHandle(obj, 'was_block_switch', 'value', 0); + + SoloParamHandle(obj, 'result', 'value', 0); + + + %history variables + SoloParamHandle(obj, 'hit_history', 'value',[]); + + SoloParamHandle(obj, 'side_history', 'value',[]); + SoloParamHandle(obj, 'quadrant_history', 'value',[]); + SoloParamHandle(obj, 'task_history', 'value',[]); + SoloParamHandle(obj, 'incoh_history', 'value',[]); + SoloParamHandle(obj, 'gammadir_history', 'value',[]); + SoloParamHandle(obj, 'gammafreq_history', 'value',[]); + + SoloParamHandle(obj, 'result_history', 'value',[]); + + + + %history within this block/task + SoloParamHandle(obj, 'hit_history_task', 'value',[]); + SoloParamHandle(obj, 'incoh_history_task', 'value',[]); + SoloParamHandle(obj, 'previous_task', 'value',[]); + + %previous training stage + SoloParamHandle(obj, 'previous_stage', 'value',[]); + + + + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + %%%%%%%%%%%%% SEND OUT VARIBLES %%%%%%%%%%%%% + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + +% +% %%% percent correct coh/incoh +% DispParam(obj, 'correct_coherent',0, x, y, 'labelfraction', 0.55,'label','%hit coh','position', [x y 100 20]); +% DispParam(obj, 'correct_incoherent',0, x, y, 'labelfraction', 0.55,'label','%hit incoh','position', [x+100 y 100 20]);next_row(y); +% +% %%% percent correct left/right +% DispParam(obj, 'left_correct',0, x, y, 'labelfraction', 0.55,'label','%hit left','position', [x y 100 20]); +% DispParam(obj, 'right_correct',0, x, y, 'labelfraction', 0.55,'label','%hit right','position', [x+100 y 100 20]);next_row(y); +% + + + + %to training section + SoloFunctionAddVars('TrainingSection', 'ro_args',{'was_hit';'was_block_switch';... + 'result';'nValid';'total_correct';'dir_correct';'freq_correct';'left_correct';... + 'right_correct';'correct_coherent';'correct_incoherent'}); + SoloFunctionAddVars('TrainingSection', 'rw_args',{'previous_stage'}); + + %to stimulus section + SoloFunctionAddVars('StimulusSection', 'ro_args',{'hit_history';'side_history';'quadrant_history';'task_history';... + 'incoh_history';'gammadir_history';'gammafreq_history';'result'}); + SoloFunctionAddVars('StimulusSection', 'rw_args',{'was_block_switch'}); + + + + + + + + + case 'next_trial', + + %%% if we haven't done any trial yet, no reason to save any history + if(n_done_trials==0 || isempty(parsed_events) || ~isfield(parsed_events,'states')) + + + %%% initialize stuff + previous_stage.value=value(training_stage); + + + + return; + end + + %%%% BINARY VARIABLES ABOUT TRIAL RESULT %%%% + if isfield(parsed_events.states,'hit_state') + was_hit.value=rows(parsed_events.states.hit_state)>0; + if(value(was_hit)==1) + result.value=1; + last_result.value='correct'; + end + end + if isfield(parsed_events.states,'error_state') + was_err.value=rows(parsed_events.states.error_state)>0; + if(value(was_err)==1) + result.value=2; + last_result.value='error'; + end + end + if isfield(parsed_events.states,'nic_error_state') + was_nic_err.value=rows(parsed_events.states.nic_error_state)>0; + if(value(was_nic_err)==1) + result.value=3; + last_result.value='nic_viol'; + end + end + if isfield(parsed_events.states,'timeout_state') + was_timeout.value=rows(parsed_events.states.timeout_state)>0; + if(value(was_timeout)==1) + result.value=4; + last_result.value='timeout viol'; + end + end + %%% rat got it wrong and then got it right + if isfield(parsed_events.states,'wait_state') + was_wait.value=rows(parsed_events.states.wait_state)>0; + %gets it wrong first and then gets it right + if(value(was_wait)==1 && value(was_hit)==1) + result.value=5; + last_result.value='wait->right'; + end + %gets it wrong first and then doesn't get it right (how?) + if(value(was_wait)==1 && value(was_hit)==0) + result.value=6; + last_result.value='wait->err'; + end + end + + + + + + + + %%% HISTORY VARIABLES %%% + res=value(result); + if(res==1) + hit_history.value=[value(hit_history) 1]; + nTrials.value = value(nTrials) + 1; + nValid.value = value(nValid) + 1; + elseif(res==2 || res==5 || res==6) + hit_history.value=[value(hit_history) 0]; + nTrials.value = value(nTrials) + 1; + nValid.value = value(nValid) + 1; + else + hit_history.value=[value(hit_history) NaN]; + nTrials.value = value(nTrials) + 1; + end + + + %%%% RESULT HISTORY %%%% + result_history.value=[value(result_history) res]; + + + %%%% SIDE HISTORY %%%% + if strcmp(value(ThisSide), 'LEFT'), + s = 'l'; + else + s = 'r'; + end + side_history.value=[value(side_history) s]; + + + %%%% QUADRANT HISTORY %%%% + quadrant_history.value=[value(quadrant_history) value(ThisQuadrant)]; + + + %%%% TASK HISTORY %%%% + if(strcmp(value(ThisTask),'Direction')) + t = 'd'; + else + t = 'f'; + end + task_history.value=[value(task_history) t]; + + + %%%% INCOH HISTORY %%%% + if(value(incoherent_trial)==1) + c = 1; + else + c = 0; + end + incoh_history.value=[value(incoh_history) c]; + + + %%%% GAMMA_DIR HISTORY %%%% + gammadir_history.value=[value(gammadir_history) value(ThisGamma_dir)]; + + + %%%% GAMMA_FREQ HISTORY %%%% + gammafreq_history.value=[value(gammafreq_history) value(ThisGamma_freq)]; + + + + + + + %%%%%%%% GENERAL PERFORMANCES %%%%%%%% + + + %%% save overall percent correct + vec_hit=value(hit_history); + total_correct.value = nanmean(vec_hit); + + %%% save NIC violations + vec_res=value(result_history); + num_violations=length(find(vec_res==3)); + num_total=length(vec_res); + percent_violations.value=num_violations/num_total; + + %%% save left/right percent correct + vec_side=value(side_history); + left_correct.value = nanmean(vec_hit(vec_side=='l')); + right_correct.value = nanmean(vec_hit(vec_side=='r')); + + %%% save dir/freq percent correct + vec_task=value(task_history); + dir_correct.value = nanmean(vec_hit(vec_task=='d')); + freq_correct.value = nanmean(vec_hit(vec_task=='f')); + + %%% save coh/incoh percent correct + vec_incoh=value(incoh_history); + correct_coherent.value = nanmean(vec_hit(vec_incoh==0)); + correct_incoherent.value = nanmean(vec_hit(vec_incoh==1)); + + + + + + + + + %%%%%%%% TASK PERFORMANCES %%%%%%%% + + + %new task? + if(~strcmp(value(previous_task),value(ThisTask))) + + nTrials_task.value=1; + + if(value(incoherent_trial)==1) + nTrials_incoh_task.value=1; + nTrials_coh_task.value=0; + else + nTrials_incoh_task.value=0; + nTrials_coh_task.value=1; + end + + hit_history_task.value=[]; + incoh_history_task.value=[]; + + total_correct_task.value=NaN; + total_correct_coherent_task.value=NaN; + total_correct_incoherent_task.value=NaN; + + previous_task.value=value(ThisTask); + + + else + + if(res==1) + nTrials_task.value = value(nTrials_task) + 1; + + hit_history_task.value=[value(hit_history_task) 1]; + + if(value(incoherent_trial)==1) + nTrials_incoh_task.value = value(nTrials_incoh_task) + 1; + incoh_history_task.value=[value(incoh_history_task) 1]; + else + nTrials_coh_task.value = value(nTrials_coh_task) + 1; + incoh_history_task.value=[value(incoh_history_task) 0]; + end + + + + elseif(res==2) + nTrials_task.value = value(nTrials_task) + 1; + + hit_history_task.value=[value(hit_history_task) 0]; + + if(value(incoherent_trial)==1) + nTrials_incoh_task.value = value(nTrials_incoh_task) + 1; + incoh_history_task.value=[value(incoh_history_task) 1]; + else + nTrials_coh_task.value = value(nTrials_coh_task) + 1; + incoh_history_task.value=[value(incoh_history_task) 0]; + end + + + else + hit_history_task.value=[value(hit_history_task) NaN]; + + if(value(incoherent_trial)==1) + incoh_history_task.value=[value(incoh_history_task) NaN]; + else + incoh_history_task.value=[value(incoh_history_task) NaN]; + end + + end + + + + + + %%% compute performances for current task block + + %total correct + vec_hit_task=value(hit_history_task); + vec=vec_hit_task; + vec=vec(~isnan(vec)); + + %%% kernel function: last few trials are the most important + kernel = exp(-(0:length(vec)-1)/5); + kernel = kernel(end:-1:1); + + if(~isempty(vec)) + total_correct_task.value = nansum(vec .* kernel)/sum(kernel); + else + total_correct_task.value = NaN; + end + + + + + %incoherent correct + vec_hit_task=value(hit_history_task); + vec_incoh_task=value(incoh_history_task); + vec=vec_hit_task(vec_incoh_task==1); + vec=vec(~isnan(vec)); + + %%% kernel function: last few trials are the most important + kernel = exp(-(0:length(vec)-1)/5); + kernel = kernel(end:-1:1); + + if(~isempty(vec)) + total_correct_incoherent_task.value = nansum(vec .* kernel)/sum(kernel); + else + total_correct_incoherent_task.value = NaN; + end + + + + + %coherent correct + vec_hit_task=value(hit_history_task); + vec_incoh_task=value(incoh_history_task); + vec=vec_hit_task(vec_incoh_task==0); + vec=vec(~isnan(vec)); + + %%% kernel function: last few trials are the most important + kernel = exp(-(0:length(vec)-1)/5); + kernel = kernel(end:-1:1); + + if(~isempty(vec)) + total_correct_coherent_task.value = nansum(vec .* kernel)/sum(kernel); + else + total_correct_coherent_task.value = NaN; + end + + + end + + + + + + %%%%%%%% STAGE PERFORMANCES %%%%%%%% + + %new stage? + if(n_done_trials>1 && ~strcmp(value(previous_stage),value(training_stage))) + nTrials_stage.value= 0; + nDays_stage.value= 1; + previous_stage.value=value(training_stage); + else + nTrials_stage.value = value(nTrials_stage) + 1; + end + + + + + + + + case 'end_session' + + CommentsSection(obj, 'append_line', [value(training_stage) ' ; ']); + CommentsSection(obj, 'append_line', ['days: ' num2str(value(nDays_stage)) ' ; ']); + CommentsSection(obj, 'append_line', ['valid: ' num2str(value(nValid)) ' ; ']); + CommentsSection(obj, 'append_line', ['dir: ' num2str(round(value(dir_correct)*100)/100) ' ; ']); + CommentsSection(obj, 'append_line', ['freq: ' num2str(round(value(freq_correct)*100)/100) ' ; ']); + CommentsSection(obj, 'append_line', ['coh: ' num2str(round(value(correct_coherent)*100)/100) ' ; ']); + CommentsSection(obj, 'append_line', ['incoh: ' num2str(round(value(correct_incoherent)*100)/100)]); + + + + + case 'get' + + val=varargin{1}; + + eval(['x=value(' val ');']); + + + + + case 'make_and_send_summary', + + pd.hits = value(hit_history); + pd.sides = value(side_history); + pd.tasks = value(task_history); + pd.stage = value(training_stage); + sendsummary(obj, 'sides', pd.sides, 'protocol_data', pd); + + +end + + diff --git a/Protocols/@TaskSwitch4/PPfilter.mat b/Protocols/@TaskSwitch4/PPfilter.mat new file mode 100644 index 0000000000000000000000000000000000000000..a98f32c42c74202f48f653b38e4d148c02d8353b GIT binary patch literal 2184 zcmbVNdr;GN68{lU-jIMoMJeGS0)j+>7Exd$w1~({bBOrh5fX_80s#el06xHoio8@5 zq!3yR@{(eyRKX2`q9P~COX+FFgO;3HMPV?*$^FvejdOGN$My4_o&E04erET(JF{Ou z7JD6QHGSC<2HlUvcHr>^Tzh(;AR>_`6vepG13fqT_}kMxMcjx)ZWLV@>q=+u;?n&h zM06&D&Tw*NE_HQg(480zXZrsQLdC~Vk07Bz5VQYK1Vw3+m_-WdCRF|{6e2KC=~pAv zXMA>~R+dSsNV3442?R&ToQRGNRBzLT zBNHhf6!Y|8PFdHbS3*6o(O%0LYS#m!wLcp4&DV$BdQWfOj@O4)UVKXPEq(aCEP@KP z&CQtudQ=cxRNehx1r@SY8dr5jQ6V;K%=+d@Di~=lox1)b6($QR?>7yTamNmnH&AJC znO3&1W+e@X@Vu#jFdDQOmTbMiqk;43VP;t_4J1Mjd{spQ!)>WLMb~IxQ7&``T>5eCwJMIJOn0tI)0e4&B1d%&#Q?HMAm3BZM)`uco#A2{9d{O95&U5b{?k6 z@>iFC>CL0D{Ch1voH3=y8g z@ypB0&AYT@WjH^wW6o^Wz-YRP&-w|-TIg@J{N`7(U|f`zly1Qi>V~QKZn2Uvwp1Mm zFj~&ybCS+!#MsK393>LAkj`qJ(Jvm1-g%r`0N4Dc`t3&JV3M<}Ic!S~)Td)V4$VA}k(xkORYABS~>8Z5XTn=!-2mcOW^Pk&W_Os2AvEv_I?)wSRX${Ha*_L5o_pOIbSbr!AYBz{e-U!gU3f%XIMX)`_@lo@tsDDLt06y*B7H*5iIM?Er@8|FIhKei8FPO=hFt%zS z@QH8-7G>--y`7Z_)}H&W?(j>8in)gx2UbVm=be}Ko16@R_eB)11GxvFk64ti%SeP9 zpXFu^sh+`*o)%imkU45_l0PIU>@mlAP<(!q5e8PMWcYBGA~CN=#WYA8b6lPKA2Ao9 zc%|=4$8blCBg*brC(T2fl@V^|7TaT|a9q5uhJnOxRjZZ@@2-no`9tQDPFQ{H%<&am z21=)@q;(lqh}8y>x7cIX3tol1dDO|o$>0p%xWo3?(Uf=b4aE`%bnKpnT3TUo_p?KS zyGyad%xc2XkBRARK3D2EOK?p{{XD-yTcmS)RVlB{F@4&sA(r*-I`#CmbM=PCSjtLq zmp?Sd@OPAlox>U!*Bw*6kne~LMW&IKnI*1KsW*PSnt`P{Ua$2PCg_#+Vx4ya`ObQ` zZ@MW95u2AgT%DeamkTBIfk7t5N#v(3gbsM=sC57Hmo|7UL-$Zm!fGhJ+4S~m<^ufB e|L(v2TgsjJac@${n{t=_8J=?g{u!P!_rCxvd?sW7 literal 0 HcmV?d00001 diff --git a/Protocols/@TaskSwitch4/SMA1.m b/Protocols/@TaskSwitch4/SMA1.m new file mode 100644 index 00000000..a7798492 --- /dev/null +++ b/Protocols/@TaskSwitch4/SMA1.m @@ -0,0 +1,332 @@ +function [] = SMA1(obj, action) + +GetSoloFunctionArgs; + + +switch action + + case 'init', + + feval(mfilename, obj, 'next_trial'); + + + case 'next_trial', + + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + %%%%%%%%%%%%%%%%%%%% SETUP THE HARDWARE %%%%%%%%%%%%%%%%%%%% + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + %%% minimum time + min_time= 2.5E-4; % This is less than the minumum time allowed for a state transition. + + %%% define LEDs and water lines + left1led = bSettings('get', 'DIOLINES', 'left1led'); + center1led = bSettings('get', 'DIOLINES', 'center1led'); + right1led = bSettings('get', 'DIOLINES', 'right1led'); + left1water = bSettings('get', 'DIOLINES', 'left1water'); + right1water = bSettings('get', 'DIOLINES', 'right1water'); + + + start_stop = bSettings('get', 'DIOLINES', 'start_stop'); + trialnum_indicator = bSettings('get', 'DIOLINES', 'trialnum_indicator'); + cerebro1 = bSettings('get', 'DIOLINES', 'cerebro1'); + cerebro2 = bSettings('get', 'DIOLINES', 'cerebro2'); + + %%% define state machine assembler + sma = StateMachineAssembler('full_trial_structure','use_happenings', 1); + + %%% get water valve opening times (based on calibration) + [LeftWValveTime RightWValveTime] = WaterValvesSection(obj, 'get_water_times'); + + + + + %%% set the water reward + if strcmp(value(ThisSide),'LEFT') + correct_response='Lhi'; + error_response='Rhi'; + hit_dio=left1water; + hit_led=left1led; + hit_valve_time=LeftWValveTime*value(total_water_multiplier); + elseif strcmp(value(ThisSide),'RIGHT') + correct_response='Rhi'; + error_response='Lhi'; + hit_dio=right1water; + hit_led=right1led; + hit_valve_time=RightWValveTime*value(total_water_multiplier); + else + error('!!!') + end + + + + + %%% Setup sounds + hit_sound_id = SoundManagerSection(obj, 'get_sound_id', 'HitSound'); + err_sound_id = SoundManagerSection(obj, 'get_sound_id', 'ErrorSound'); + + viol_sound_id = SoundManagerSection(obj, 'get_sound_id', 'ViolationSound'); + timeout_sound_id = SoundManagerSection(obj, 'get_sound_id', 'TimeoutSound'); + + task1_sound_id = SoundManagerSection(obj, 'get_sound_id', 'Task1Sound'); + task2_sound_id = SoundManagerSection(obj, 'get_sound_id', 'Task2Sound'); + + + + + + %%% setup variables according to current task + if(strcmp(value(ThisTask),'Direction')) + task_sound_id=task1_sound_id; + + helper_lights=0; + error_forgiveness=0; + wait_delay=0; + + centerlight=center1led+left1led+right1led; + centerlight2=center1led; + + wait_for_cpoke2='wait_for_cpoke_dir'; + + elseif(strcmp(value(ThisTask),'Frequency')) + task_sound_id=task2_sound_id; + helper_lights=value(helper_lights_freq); + error_forgiveness=value(error_forgiveness_freq); + wait_delay=value(wait_delay_freq); + + centerlight=center1led; + centerlight2=center1led; + + wait_for_cpoke2='wait_for_cpoke_freq'; + + else + error('what task?') + end + + + + %%% setup the stimulus + stimulus_sound_id = SoundManagerSection(obj, 'get_sound_id', 'StimulusSound'); + + + %%% setup the reward + sma = add_scheduled_wave(sma, 'name', 'direct_reward', 'preamble', value(reward_delay), ... + 'sustain', hit_valve_time, 'DOut', hit_dio); + + + + if(helper_lights) + sidelights=hit_led; + else + sidelights=left1led+right1led; + end + + + + + + %%%%%%%%%%%%%%%%%%%% WAIT FOR CPOKE %%%%%%%%%%%%%%%%%%%% + + + + + sma = add_state(sma,'name','wait_for_cpoke',... + 'self_timer',min_time,... + 'input_to_statechange',{'Clo','wait_for_cpoke1'}); %wait for poke out + + +% %%%spikegadgets setup +% if(~isnan(start_stop) && ~isnan(trialnum_indicator) && n_done_trials==0) +% +% sma = add_state(sma,'name','wait_for_cpoke1',... +% 'self_timer',0.5,... +% 'output_actions',{'DOut', start_stop },... %start recording (TTL up) +% 'input_to_statechange',{'Tup','wait_for_cpoke1b'}); +% +% sma = add_state(sma,'name','wait_for_cpoke1b',... +% 'self_timer',2,... +% 'output_actions',{'DOut', -start_stop },... %start recording (TTL down) +% 'input_to_statechange',{'Tup','wait_for_cpoke1c'}); +% +% sma = add_state(sma,'name','wait_for_cpoke1c',... +% 'self_timer',0.5,... +% 'output_actions',{'DOut', trialnum_indicator },... %trial pulse up +% 'input_to_statechange',{'Tup','wait_for_cpoke1d'}); +% +% sma = add_state(sma,'name','wait_for_cpoke1d',... +% 'self_timer',min_time,... +% 'output_actions',{'DOut', -trialnum_indicator },... %trial pulse down +% 'input_to_statechange',{'Tup',wait_for_cpoke2}); +% +% else + + + sma = add_state(sma,'name','wait_for_cpoke1',... + 'self_timer',min_time,... + 'input_to_statechange',{'Tup',wait_for_cpoke2}); + + +% end + + + sma = add_state(sma,'name','wait_for_cpoke_dir',... + 'self_timer',0.5,... + 'output_actions',{'SoundOut',task_sound_id;'DOut', centerlight },... + 'input_to_statechange',{'Tup','wait_for_cpoke_bis'}); + + + sma = add_state(sma,'name','wait_for_cpoke_freq',... + 'self_timer',0.5,... + 'output_actions',{'SoundOut',task_sound_id;'DOut', centerlight },... + 'input_to_statechange',{'Tup','wait_for_cpoke_bis'}); + + + + + + sma = add_state(sma,'name','wait_for_cpoke_bis',... + 'self_timer',value(wait_for_cpoke_timeout),... + 'output_actions',{'DOut', centerlight},... + 'input_to_statechange',{'Tup','timeout_state'; ... + 'Chi','nic_prestim'}); + + + + + + %%%%%%%%%%%%%%%%%%%% NIC PRESTIM %%%%%%%%%%%%%%%%%%%% + + % sma = add_state(sma,'name','nic_prestim','self_timer',value(settling_time),... + % 'output_actions',{'DOut', centerlight2 },... + % 'input_to_statechange',{'Clo','wait_for_cpoke';... + % 'Tup','cpoke'}); + + + sma = add_state(sma,'name','nic_prestim','self_timer',value(settling_time),... + 'output_actions',{'SoundOut',-task_sound_id;'DOut', centerlight2 },... + 'input_to_statechange',{'Clo','wait_for_cpoke';... + 'Tup','cpoke'}); + + + + %%%%%%%%%%%%%%%%%%%% CPOKE with legal breaks up to 0.1 %%%%%%%%%%%%%% + + legal_cbreak=0.1; + + sma = add_scheduled_wave(sma,'name','cpoke_timer','preamble', value(nose_in_center)+legal_cbreak); + + % trigger the start of the timer: + sma = add_state(sma, 'name', 'cpoke', 'self_timer', min_time, ... + 'output_actions', {'SoundOut',stimulus_sound_id; ... + 'SchedWaveTrig', 'cpoke_timer'; 'DOut', centerlight2}, ... + 'input_to_statechange', {'Cout', 'cpoke_out'; ... + 'Tup', 'cpoke_in'}); + + + sma = add_state(sma, 'name', 'cpoke_in', 'self_timer', 1000, ... + 'output_actions', {'DOut', centerlight2}, ... + 'input_to_statechange', {'Cout', 'cpoke_out'; ... + 'cpoke_timer_In', 'wait_for_cout'}); + + sma = add_state(sma, 'name', 'cpoke_out', 'self_timer', legal_cbreak, ... + 'output_actions', {'DOut', centerlight2}, ... + 'input_to_statechange', {'Cin', 'cpoke_in'; ... + 'cpoke_timer_In', 'wait_for_cout';... + 'Tup', 'nic_error_state'}); + + + + %%%%%%%%%%%%%%%%%%%% WAIT FOR COUT %%%%%%%%%%%%%%%%%%%% + + sma = add_state(sma,'name','wait_for_cout',... + 'input_to_statechange',{'Clo','wait_for_spoke'}); + + + + %%%%%%%%%%%%%%%%%%%% WAIT FOR SPOKE %%%%%%%%%%%%%%%%%%%% + + if error_forgiveness==1 + % stimulus sound keeps going after rat steps out of cpoke + sma = add_state(sma,'name','wait_for_spoke','self_timer',value(wait_for_spoke_timeout),... + 'output_actions',{'DOut',sidelights},... + 'input_to_statechange',{correct_response,'hit_state';... + error_response,'wait_state';... + 'Tup','timeout_state'}); + else + % as soon as rat steps out of cpoke, we stop stimulus sound + sma = add_state(sma,'name','wait_for_spoke','self_timer',value(wait_for_spoke_timeout),... + 'output_actions',{'SoundOut',-stimulus_sound_id;'DOut',sidelights},... + 'input_to_statechange',{correct_response,'hit_state';... + error_response,'error_state';... + 'Tup','timeout_state'}); + end + + + + %%%%%%%%%%%%%%%%%%%% HIT STATE %%%%%%%%%%%%%%%%%%%% + + % first we turn off stimulus sound, in case it didn't happen before + % (i.e. if error_forgiveness == 1) + sma = add_state(sma,'name','hit_state','self_timer',min_time,... + 'output_actions',{'SoundOut',-stimulus_sound_id},... + 'input_to_statechange',{'Tup','hit_state2'}); + + + % we do the operations for the hit state + sma = add_state(sma,'name','hit_state2','self_timer',value(reward_delay)+hit_valve_time+0.4,... + 'output_actions',{'SoundOut',hit_sound_id;'DOut',hit_led;... + 'SchedWaveTrig','direct_reward'},... + 'input_to_statechange',{'Tup','clean_up_state'}); + + + + + %%%%%%%%%%%%%%%%%%%% ERROR STATE %%%%%%%%%%%%%%%%%%%% + + sma = add_state(sma,'name','error_state','self_timer',value(total_error_delay),... + 'output_actions',{'SoundOut',err_sound_id},... + 'input_to_statechange',{'Tup','clean_up_state'}); + + + sma = add_state(sma,'name','wait_state','self_timer',wait_delay,... + 'input_to_statechange',{'Tup','wait_for_spoke'}); + + + + %%%%%%%%%%%%%%%%%%%% NIC ERROR STATE %%%%%%%%%%%%%%%%%%%% + + sma = add_multi_sounds_state(sma,[-stimulus_sound_id viol_sound_id],... + 'self_timer',value(nic_delay),... + 'state_name','nic_error_state','return_state','clean_up_state'); + + + %%%%%%%%%%%%%%%%%%%% TIMEOUT STATE %%%%%%%%%%%%%%%%%%%% + + sma = add_state(sma,'name','timeout_state','self_timer',value(timeout_delay),... + 'output_actions',{'SoundOut',timeout_sound_id},... + 'input_to_statechange',{'Tup','clean_up_state'}); + + + %%%%%%%%%%%%%%%%%%%% CLEAN UP STATE %%%%%%%%%%%%%%%%%%%% + + sma = add_multi_sounds_state(sma,[-stimulus_sound_id -timeout_sound_id -viol_sound_id -hit_sound_id -err_sound_id -task1_sound_id -task2_sound_id],... + 'state_name','clean_up_state','return_state','check_next_trial_ready'); + + + dispatcher('send_assembler', sma, {'hit_state2', 'error_state',... + 'nic_error_state','timeout_state'}); + + + case 'get' + + %val=varargin{1}; + % + %eval(['x=value(' val ');']); + + + otherwise + warning('do not know how to do %s',action); +end + + + diff --git a/Protocols/@TaskSwitch4/StimulusSection.m b/Protocols/@TaskSwitch4/StimulusSection.m new file mode 100644 index 00000000..f5b9846d --- /dev/null +++ b/Protocols/@TaskSwitch4/StimulusSection.m @@ -0,0 +1,1377 @@ +function [x, y] = StimulusSection(obj, action, varargin) + +GetSoloFunctionArgs(obj); + +switch action, + + % ------------------------------------------------------------------ + % INIT + % ------------------------------------------------------------------ + + case 'init' + x=varargin{1}; + y=varargin{2}; + + + + %%%% WATER INCREASE + + DispParam(obj, 'water_increase_multiplier',1, x, y, 'labelfraction', 0.65,... + 'TooltipString', 'water increase multiplier as a function of n_done_trials');next_row(y); + + NumeditParam(obj, 'WI_base', .95, x, y, 'labelfraction', 0.2,'label','B','position', [x y 66 20], ... + 'TooltipString', 'water increase base'); + + NumeditParam(obj, 'WI_ratio', .00025, x, y, 'labelfraction', 0.2,'label','R','position', [x+66 y 66 20], ... + 'TooltipString', 'water increase per trial'); + + NumeditParam(obj, 'WI_max', 1.2, x, y, 'labelfraction', 0.2,'label','M','position', [x+133 y 66 20], ... + 'TooltipString', 'water increase max');next_row(y); + + ToggleParam(obj, 'water_increase_toggle', 1, x,y,... + 'OnString', 'Water increase ON','OffString', 'Water increase OFF',... + 'TooltipString', sprintf('If on water increase is on'));next_row(y); + + + + + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + %%%%%%% TIMING PARAMETERS WINDOW %%%%%%%% + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + %%%% Separate window for stimulus parameters + ToggleParam(obj, 'TimingShow', 0, x, y, 'OnString', 'Parameters timing', ... + 'OffString', 'Parameters timing', 'TooltipString', 'Show/Hide Timing panel'); + set_callback(TimingShow, {mfilename, 'show_hide3'}); + next_row(y);oldx=x; oldy=y; parentfig=double(gcf); + SoloParamHandle(obj, 'myfig3', 'value', double(figure('Position', [300 100 280 220],'closerequestfcn',... + [mfilename '(' class(obj) ', ''hide3'');'], 'MenuBar', 'none','Name', mfilename)), 'saveable', 0); + + set(gcf, 'Visible', 'off'); + x=10;y=10; + + NumeditParam(obj, 'timeout_delay', 3, x,y,'label','Timeout delay','TooltipString','Delay after timeout at spoke, follows new trial'); + next_row(y); + + NumeditParam(obj, 'reward_delay', 0.001, x,y,'label','Reward Delay','TooltipString','Delay between side poke and reward delivery'); + next_row(y); + + NumeditParam(obj, 'wait_for_spoke_timeout', 60, x,y,'label','Timeout for spoke','TooltipString','Time after NIC to wait for a side poke'); + next_row(y); + + NumeditParam(obj, 'nic_delay', 3, x,y,'label','NIC violation delay','TooltipString','Delay after NIC violation, follows new trial'); + next_row(y); + + NumeditParam(obj, 'settling_time', 0.001, x,y,'label','Pre-stimulus delay','TooltipString','Time in NIC before starting stimulus'); + next_row(y); + + NumeditParam(obj, 'wait_for_cpoke_timeout', 120, x,y,'label','Timeout for cpoke','TooltipString','Timeout waiting for cpoke'); + next_row(y); + + SubheaderParam(obj,'title','Timing',x,y); next_row(y, 1.5); + + %%% back to the main window + x=oldx; y=oldy; + figure(parentfig); + + + + + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + %%%%%%% SOUND PARAMETERS WINDOW %%%%%%%%% + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + %%%% Separate window for sound parameters + ToggleParam(obj, 'SoundsShow', 0, x, y, 'OnString', 'Parameters other sounds', ... + 'OffString', 'Parameters other sounds', 'TooltipString', 'Show/Hide Sounds panel'); + set_callback(SoundsShow, {mfilename, 'show_hide2'});next_row(y); + oldx=x; oldy=y; parentfig=double(gcf); + SoloParamHandle(obj, 'myfig2', 'value', double(figure('Position', [100 100 560 440],'closerequestfcn',... + [mfilename '(' class(obj) ', ''hide2'');'], 'MenuBar', 'none','Name', mfilename)), 'saveable', 0); + set(gcf, 'Visible', 'off'); + x=10;y=10; + + %%%% Sound parameters + [x,y]=SoundInterface(obj,'add','ViolationSound',x,y,'Style','SpectrumNoise','Volume',0.0015,'Loop',1); + SoundInterface(obj,'set','ViolationSound','Freq1',8000,'Freq2',2222,'Dur1',1,'Sigma',14,'Cntrst',111,'CRatio',1); + + [x,y]=SoundInterface(obj,'add','ErrorSound',x,y,'Style','PClick','Volume',0.0035,'Loop',1); + SoundInterface(obj,'set','ErrorSound','Freq1',333,'Freq2',333,'Dur1',0.5,'Width',5); + + [x,y]=SoundInterface(obj,'add','Task1Sound',x,y,'Style','ToneFMWiggle','Volume',0.005); + SoundInterface(obj,'set','Task1Sound','Dur1',1,'Freq1',4000,'FMAmp',600,'FMFreq',5); + + next_column(x);y=10; + + [x,y]=SoundInterface(obj,'add','TimeoutSound',x,y,'Style','SpectrumNoise','Volume',0.0025,'Loop',1); + SoundInterface(obj,'set','TimeoutSound','Freq1',1000,'Freq2',1000,'Dur1',1,'Sigma',100,'Cntrst',500,'CRatio',5); + + [x,y]=SoundInterface(obj,'add','HitSound',x,y,'Style','ToneSweep','Volume',0.0025); + SoundInterface(obj,'set','HitSound','Freq1',1000,'Freq2',5000,'Dur1',0.1,'Dur2',0.3,'Tau',0.05); + + [x,y]=SoundInterface(obj,'add','Task2Sound',x,y,'Style','ToneFMWiggle','Volume',0.005); + SoundInterface(obj,'set','Task2Sound','Dur1',1,'Freq1',9000,'FMAmp',1800,'FMFreq',15); + + %%% back to the main window + x=oldx; y=oldy; + figure(parentfig); + + + + + + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + %%%%%%% STIM. PARAMETERS WINDOW %%%%%%%%% + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + %%%% Separate window for stimulus parameters + ToggleParam(obj, 'StimuluShow', 0, x, y, 'OnString', 'Parameters stimulus', ... + 'OffString', 'Parameters stimulus', 'TooltipString', 'Show/Hide Stimulus panel'); + set_callback(StimuluShow, {mfilename, 'show_hide'}); + next_row(y); + oldx=x; oldy=y; parentfig=double(gcf); + SoloParamHandle(obj, 'myfig', 'value', double(figure('Position', [300 100 280 220], 'closerequestfcn',... + [mfilename '(' class(obj) ', ''hide'');'], 'MenuBar', 'none','Name', mfilename)), 'saveable', 0); + set(gcf, 'Visible', 'off'); + x=10;y=10; + + %%%% Stimulus parameters + NumeditParam(obj, 'bup_width', 5, x, y, 'position', [x y 200 20], ... + 'label', 'bup_width (ms)', 'TooltipString', 'the bup width in units of msec');next_row(y); + NumeditParam(obj, 'bup_ramp', 2, x, y, 'position', [x y 200 20], ... + 'label', 'bup_ramp (ms)', 'TooltipString', 'the duration in units of msec of the upwards and downwards volume ramps for individual bups');next_row(y); + NumeditParam(obj, 'total_rate', 40, x, y, 'position', [x y 200 20], ... + 'TooltipString', 'the sum of left and right bup rates');next_row(y); + NumeditParam(obj, 'freq_lo', 6500, x, y, 'position', [x y 200 20], ... + 'TooltipString', 'low frequency (Hz)');next_row(y); + NumeditParam(obj, 'freq_hi', 14200, x, y, 'position', [x y 200 20], ... + 'TooltipString', 'high frequency (Hz)');next_row(y); + NumeditParam(obj, 'vol_low_freq', 1, x, y, 'position', [x y 200 20], ... + 'TooltipString', 'volume multiplier for clicks at low frequency');next_row(y); + NumeditParam(obj, 'vol_hi_freq', 1, x, y, 'position', [x y 200 20], ... + 'TooltipString', 'volume multiplier for clicks at high frequency');next_row(y); + %%% overall volume + NumeditParam(obj, 'vol', 0.15, x, y, 'position', [x y 200 20],'label','Overall volume multiplier', ... + 'labelfraction', 0.7,'TooltipString', 'volume multiplier for all sounds in the protocol');next_row(y); + + %%% back to the main window + x=oldx; y=oldy; + figure(parentfig); + + + + + + + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + %%%%%%%%% TASK SWITCHING PARAMETERS %%%%%%%%% + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + %%%% minimum performances required + NumeditParam(obj, 'task_switch_min_perf', .8, x, y, 'position', [x y 200 20], ... + 'label', 'Min perf', 'TooltipString', 'Minimum performance to allow switching');next_row(y); + + %%%% minimum number of trials + NumeditParam(obj, 'task_switch_mintrials', 30, x, y, 'position', [x y 200 20], ... + 'label', 'Min trials', 'TooltipString', 'Minimum number of trials before switching');next_row(y); + + %%%% switch on/off + ToggleParam(obj, 'task_switch_auto', 0, x, y, 'position', [x y 200 20], ... + 'OffString', 'Auto task switch OFF', 'OnString', 'Auto task switch ON', ... + 'TooltipString', 'If on, switches automatically between tasks');next_row(y); + + %%%% first task: direction or random? + ToggleParam(obj, 'randomize_first_task', 0, x, y, 'position', [x y 200 20], ... + 'OffString', 'Start with direction', 'OnString', 'Randomize first task', ... + 'TooltipString', 'If on, picks randomly the first task');next_row(y,2); + + %%%% are incongruent stimuli present now? + DispParam(obj, 'exist_incoherent',0, x, y,... + 'TooltipString', '1 if incoherent trials can be generated under current parameters'); + next_row(y); + + %%%% number of incoherent trials in this block + DispParam(obj, 'nTrials_incoh_task', 0, x, y,'labelfraction', 0.55,'label','nTri incoh','position', [x y 100 20]); + %%%% performance on incoherent trials in this block + DispParam(obj, 'total_correct_incoherent_task',0, x, y, 'labelfraction', 0.55,'label','%hit incoh','position', [x+100 y 100 20]);next_row(y); + + %%%% number of coherent trials in this block + DispParam(obj, 'nTrials_coh_task',0, x, y, 'labelfraction', 0.55,'label','nTri coh','position', [x y 100 20]); + %%%% performance on coherent trials in this block + DispParam(obj, 'total_correct_coherent_task',0, x, y, 'labelfraction', 0.55,'label','%hit coh','position', [x+100 y 100 20]);next_row(y); + + %%%% total number of trials in this block + DispParam(obj, 'nTrials_task',0, x, y, 'labelfraction', 0.55,'label','nTri all','position', [x y 100 20]); + %%%% overall performance in this block + DispParam(obj, 'total_correct_task', 0, x, y,'labelfraction', 0.55,'label','%hit all','position', [x+100 y 100 20]);next_row(y); + + %%%% current task + MenuParam(obj, 'ThisTask', {'Direction'; 'Frequency'}, 1, x, y, ... + 'TooltipString', 'the task of the present trial'); next_row(y); + + SubheaderParam(obj,'title','Current Task',x,y, 'position', [x y 200 18]); next_row(y,1.5); + + + + + + + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + %%%%%%%%%%%%%%%%%%% ANTIBIAS %%%%%%%%%%%%%%%%%% + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + %%% Ab_TAU: number of trials back for antibias + NumeditParam(obj, 'Ab_Tau', 50, x, y,'position', [x y 100 20], ... + 'TooltipString', 'Number of trials back over which to compute antibias'); + + %%% Ab_BETA: strenth of antibias + NumeditParam(obj, 'Ab_Beta', 5, x, y,'position', [x+100 y 100 20], ... + 'TooltipString', 'antibias strength');next_row(y); + + %%% antibias type + MenuParam(obj, 'antibias_type', {'No antibias'; 'Side antibias';... + 'Quadrant antibias'}, 1, x, y, 'label', 'Type', 'TooltipString',... + 'antibias type (depends on exist_incoherent)', 'labelfraction',... + 0.3333);next_row(y,1); + + %%% side antibias on/off + ToggleParam(obj, 'antibias_toggle', 1, x,y,... + 'OnString', 'Antibias ON','OffString', 'Antibias OFF',... + 'TooltipString', sprintf('If on antibias is on'));next_row(y,1.1); + + + + + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + %%%%%%%%%%%%% TRAINING PARAMETERS %%%%%%%%%%%%% + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + %%% mixing of frequency evidence + SliderParam(obj,'stimulus_mixing_freq',0,0,1,x, y,'label','%mixing freq', 'position', [x y 200 20]);next_row(y); + %%% mixing of direction evidence + SliderParam(obj,'stimulus_mixing_dir',0,0,1,x, y,'label','%mixing dir', 'position', [x y 200 20]);next_row(y,1.3); + + + %%% on trials w/ error forgiveness for frequency, wait delay + SliderParam(obj,'wait_delay_freq',0.2,0.2,3,x, y,'label','wait delay', 'position', [x y 200 20]);next_row(y); + + %%% helper lights on/off for frequency trials + ToggleParam(obj, 'helper_lights_freq', 1, x, y, ... + 'OffString', 'Freq. lights OFF', ... + 'OnString', 'Freq. lights ON', ... + 'TooltipString', 'If on (black), LED lights help indicate what to do; if off (brown), no helper LED lights','position', [x y 100 20]); + + %%% toggle error forgiveness on/off for frequency trials + ToggleParam(obj, 'error_forgiveness_freq', 1, x, y, ... + 'OffString', 'Forgive OFF', ... + 'OnString', 'Forgive ON', ... + 'TooltipString', 'If on (black), start new trial upon wrong side choice; if off (brown), can choose again',... + 'position', [x+100 y 100 20]);next_row(y,1.1); + + %%% required time with nose in center + SliderParam(obj,'nose_in_center',0.05,0.05,1.3,x, y,'label','min NIC', 'position', [x y 200 20]);next_row(y,1.1); + + SubheaderParam(obj,'title','Training parameters',x,y, 'position', [x y 200 18]); next_row(y); + + + + + + + + + %%%%%% COLUMN 3 %%%%%% + y=5; next_column(x); + + + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + %%%%%%%%%%%%% DIRECTION TASK %%%%%%%%%%%%% + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + %%% delay for each quadrant + DispParam(obj, 'Delay3Dir',4, x, y,'labelfraction',0.6,'position', [x y 100 20]); + DispParam(obj, 'Delay4Dir',4, x, y,'labelfraction',0.6,'position', [x+100 y 100 20]);next_row(y); + DispParam(obj, 'Delay2Dir',4, x, y,'labelfraction',0.6,'position', [x y 100 20]); + DispParam(obj, 'Delay1Dir',4, x, y,'labelfraction',0.6,'position', [x+100 y 100 20]);next_row(y,1.1); + + %%% water for each quadrant + DispParam(obj, 'Water3Dir',1, x, y,'labelfraction',0.6,'position', [x y 100 20]); + DispParam(obj, 'Water4Dir',1, x, y,'labelfraction',0.6,'position', [x+100 y 100 20]);next_row(y); + DispParam(obj, 'Water2Dir',1, x, y,'labelfraction',0.6,'position', [x y 100 20]); + DispParam(obj, 'Water1Dir',1, x, y,'labelfraction',0.6,'position', [x+100 y 100 20]);next_row(y,1.1); + + %%% probability for each quadrant + DispParam(obj, 'Prob3Dir',0.25, x, y,'labelfraction',0.6,'position', [x y 100 20]); + DispParam(obj, 'Prob4Dir',0.25, x, y,'labelfraction',0.6,'position', [x+100 y 100 20]);next_row(y); + DispParam(obj, 'Prob2Dir',0.25, x, y,'labelfraction',0.6,'position', [x y 100 20]); + DispParam(obj, 'Prob1Dir',0.25, x, y,'labelfraction',0.6,'position', [x+100 y 100 20]);next_row(y,1.1); + + %%% percent correct for each quadrant + DispParam(obj, 'Bias3Dir',0.25, x, y,'labelfraction',0.6,'position', [x y 100 20]); + DispParam(obj, 'Bias4Dir',0.25, x, y,'labelfraction',0.6,'position', [x+100 y 100 20]);next_row(y); + DispParam(obj, 'Bias2Dir',0.25, x, y,'labelfraction',0.6,'position', [x y 100 20]); + DispParam(obj, 'Bias1Dir',0.25, x, y,'labelfraction',0.6,'position', [x+100 y 100 20]);next_row(y,1.1); + + SubheaderParam(obj,'title','Quadrant Antibias',x,y, 'position', [x y 407 20]); next_row(y,1); + + + + DispParam(obj, 'WaterL',1, x, y,'position', [x y 100 20]); + DispParam(obj, 'WaterR',1, x, y,'position', [x+100 y 100 20]); + + next_column(x); + + DispParam(obj, 'DelayL',4, x, y,'position', [x y 100 20]); + DispParam(obj, 'DelayR',4, x, y,'position', [x+100 y 100 20]);next_row(y,1.1); + + next_column(x,-1); + + DispParam(obj, 'BiasL',0.5, x, y,'position', [x y 100 20]); + DispParam(obj, 'BiasR',0.5, x, y,'position', [x+100 y 100 20]); + + next_column(x); + + DispParam(obj, 'ProbL',0.5, x, y,'position', [x y 100 20]); + DispParam(obj, 'ProbR',0.5, x, y,'position', [x+100 y 100 20]);next_row(y,1.1); + + next_column(x,-1); + SubheaderParam(obj,'title','Side Antibias',x,y, 'position', [x y 407 20]); next_row(y,1.1); + + + %%%%%%%%%%% all the possible durations and gammas + NumeditParam(obj, 'durations_dir', [1.3], x, y, 'position', [x y 200 20], ... + 'label','Duration values','TooltipString', 'possible stimulus durations');next_row(y); + NumeditParam(obj, 'gamma_freq_values_dir', [0], x, y, 'position', [x y 200 20], ... + 'label','Gamma_freq values','TooltipString', 'possible gamma_freq values');next_row(y); + NumeditParam(obj, 'gamma_dir_values_dir', [4], x, y, 'position', [x y 200 20], ... + 'label','Gamma_dir values','TooltipString', 'possible gamma_dir values');next_row(y); + + + + + + %%%%%% COLUMN 3 %%%%%% + y=5; next_column(x); + + + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + %%%%%%%%%%%%% FREQUENCY TASK %%%%%%%%%%%%% + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + + %%% delay for each quadrant + DispParam(obj, 'Delay3Freq',4, x, y,'labelfraction',0.6,'position', [x y 100 20]); + DispParam(obj, 'Delay4Freq',4, x, y,'labelfraction',0.6,'position', [x+100 y 100 20]);next_row(y); + DispParam(obj, 'Delay2Freq',4, x, y,'labelfraction',0.6,'position', [x y 100 20]); + DispParam(obj, 'Delay1Freq',4, x, y,'labelfraction',0.6,'position', [x+100 y 100 20]);next_row(y,1.1); + + %%% water for each quadrant + DispParam(obj, 'Water3Freq',1, x, y,'labelfraction',0.6,'position', [x y 100 20]); + DispParam(obj, 'Water4Freq',1, x, y,'labelfraction',0.6,'position', [x+100 y 100 20]);next_row(y); + DispParam(obj, 'Water2Freq',1, x, y,'labelfraction',0.6,'position', [x y 100 20]); + DispParam(obj, 'Water1Freq',1, x, y,'labelfraction',0.6,'position', [x+100 y 100 20]);next_row(y,1.1); + + %%% probability for each quadrant + DispParam(obj, 'Prob3Freq',0.25, x, y,'labelfraction',0.6,'position', [x y 100 20]); + DispParam(obj, 'Prob4Freq',0.25, x, y,'labelfraction',0.6,'position', [x+100 y 100 20]);next_row(y); + DispParam(obj, 'Prob2Freq',0.25, x, y,'labelfraction',0.6,'position', [x y 100 20]); + DispParam(obj, 'Prob1Freq',0.25, x, y,'labelfraction',0.6,'position', [x+100 y 100 20]);next_row(y,1.1); + + %%% percent correct for each quadrant + DispParam(obj, 'Bias3Freq',0.25, x, y,'labelfraction',0.6,'position', [x y 100 20]); + DispParam(obj, 'Bias4Freq',0.25, x, y,'labelfraction',0.6,'position', [x+100 y 100 20]);next_row(y); + DispParam(obj, 'Bias2Freq',0.25, x, y,'labelfraction',0.6,'position', [x y 100 20]); + DispParam(obj, 'Bias1Freq',0.25, x, y,'labelfraction',0.6,'position', [x+100 y 100 20]);next_row(y,5.4); + + + + + %%%%%%%%%%% all the possible durations and gammas + NumeditParam(obj, 'durations_freq', [1.3], x, y, 'position', [x y 200 20], ... + 'label','Duration values','TooltipString', 'possible stimulus durations');next_row(y); + NumeditParam(obj, 'gamma_freq_values_freq', [4], x, y, 'position', [x y 200 20], ... + 'label','Gamma_freq values','TooltipString', 'possible gamma_freq values');next_row(y); + NumeditParam(obj, 'gamma_dir_values_freq', [0], x, y, 'position', [x y 200 20], ... + 'label','Gamma_dir values','TooltipString', 'possible gamma_dir values');next_row(y); + + + + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + %%%%%%%%%%%%%%% IMAGE PLOTS %%%%%%%%%%%%%% + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + %%% button to update all plots + PushbuttonParam(obj,'draw', x, y, 'position', [x+170 y 30 20],'label', 'draw'); + set_callback(draw, {mfilename, 'update_plot_button'}); + next_row(y); + + + %%% direction plot + newaxes = axes; + SoloParamHandle(obj, 'myaxesdir', 'saveable', 0,'value', double(newaxes)); + set(value(myaxesdir),'Position', [.52 .58 .21 .21]); + SoloParamHandle(obj, 'matrixdir', 'saveable',0); + + + %%% frequency plot + newaxes = axes; + SoloParamHandle(obj, 'myaxesfreq', 'saveable', 0,'value', double(newaxes)); + set(value(myaxesfreq),'Position', [.74 .58 .21 .21]); + SoloParamHandle(obj, 'matrixfreq', 'saveable',0); + + + feval(mfilename, obj, 'initialize_plots'); + + + + + + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + %%%%%%%%%%% STIMULUS VARIABLES %%%%%%%%%%% + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + + next_row(y,9.3); + next_column(x,-1); + + SubheaderParam(obj,'title','This',x,y, 'position', [x y 50 20]); + %%% current quadrant/side + DispParam(obj, 'ThisSide','RIGHT', x, y,'label','Side','position', [x+50 y 70 20]); + DispParam(obj, 'ThisQuadrant',4, x, y,'label','Quad','position', [x+120 y 70 20]); + %%% current duration + DispParam(obj, 'ThisDuration', 1.3, x, y,'label','Dur', 'position', [x+190 y 70 20], 'labelfraction', 0.6); + %%% current gammas + DispParam(obj, 'ThisGamma_dir',1, x, y,'label','Gdir','position', [x+260 y 70 20]); + DispParam(obj, 'ThisGamma_freq',1, x, y,'label','Gfreq','position', [x+330 y 70 20]);next_row(y); + + + + + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + %%%%%%%%%%%% LAST TRIALS PLOT %%%%%%%%%%%% + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + newaxes = axes; + SoloParamHandle(obj, 'myaxeshistory0', 'saveable', 0,'value', double(newaxes)); + set(value(myaxeshistory0),'Position', [.87 .83 .08 .08]); + set(value(myaxeshistory0),'XLim',[0.5 6.5],'YLim',[0.5 6.5]); + set(value(myaxeshistory0),'XTick',[],'YTick',[]); + set(value(myaxeshistory0),'Box','off'); + + newaxes = axes; + SoloParamHandle(obj, 'myaxeshistory1', 'saveable', 0,'value', double(newaxes)); + set(value(myaxeshistory1),'Position', [.775 .83 .08 .08]); + set(value(myaxeshistory0),'XLim',[0.5 6.5],'YLim',[0.5 6.5]); + set(value(myaxeshistory1),'XTick',[],'YTick',[]); + set(value(myaxeshistory1),'Box','off'); + + newaxes = axes; + SoloParamHandle(obj, 'myaxeshistory2', 'saveable', 0,'value', double(newaxes)); + set(value(myaxeshistory2),'Position', [.68 .83 .08 .08]); + set(value(myaxeshistory2),'XTick',[],'YTick',[]); + set(value(myaxeshistory2),'Box','off'); + + newaxes = axes; + SoloParamHandle(obj, 'myaxeshistory3', 'saveable', 0,'value', double(newaxes)); + set(value(myaxeshistory3),'Position', [.585 .83 .08 .08]); + set(value(myaxeshistory3),'XTick',[],'YTick',[]); + set(value(myaxeshistory3),'Box','off'); + + newaxes = axes; + SoloParamHandle(obj, 'myaxeshistory4', 'saveable', 0,'value', double(newaxes)); + set(value(myaxeshistory4),'Position', [.49 .83 .08 .08]); + set(value(myaxeshistory4),'XTick',[],'YTick',[]); + set(value(myaxeshistory4),'Box','off'); + + + + + + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + %%%%%%%%%%%%% INTERNAL VARIBLES %%%%%%%%%%%%% + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + %stimulus variables + SoloParamHandle(obj, 'ThisSideFrequency', 'value', 'RIGHT'); + SoloParamHandle(obj, 'ThisSideDirection', 'value', 'RIGHT'); + SoloParamHandle(obj, 'incoherent_trial', 'value', 1); + + %variables to send to the state matrix + SoloParamHandle(obj, 'total_error_delay', 'value', 0); + SoloParamHandle(obj, 'total_water_multiplier', 'value', 1); + + %information about current stimulus to be saved in the data file + SoloParamHandle(obj, 'ThisStimulus', 'value', []); + + + + + + + + + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + %%%%%%%%%%%%% SEND OUT VARIBLES %%%%%%%%%%%%% + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + %%% send to the state matrix section + SoloFunctionAddVars('SMA1', 'ro_args',{'ThisTask';'settling_time';... + 'reward_delay';'nose_in_center';'nic_delay';... + 'error_forgiveness_freq';'wait_delay_freq';... + 'wait_for_cpoke_timeout';'wait_for_spoke_timeout'; + 'timeout_delay';'ThisSide';'helper_lights_freq';... + 'total_error_delay';'total_water_multiplier'}); + + %%% send to the training section + SoloFunctionAddVars('TrainingSection', 'rw_args', {... + 'antibias_type';'gamma_dir_values_dir';'gamma_dir_values_freq';... + 'gamma_freq_values_dir';'gamma_freq_values_freq';... + 'durations_dir';'durations_freq';'stimulus_mixing_dir';... + 'stimulus_mixing_freq';'nose_in_center' ;'error_forgiveness_freq';... + 'wait_delay_freq';'helper_lights_freq';'ThisTask';... + 'randomize_first_task';'task_switch_auto';'task_switch_min_perf'}); + + %%% send to the history section + SoloFunctionAddVars('HistorySection', 'ro_args', {'ThisSide';... + 'ThisQuadrant';'incoherent_trial';... + 'ThisGamma_dir';'ThisGamma_freq';'ThisTask'}); + + SoloFunctionAddVars('HistorySection', 'rw_args', {'nTrials_task';... + 'nTrials_coh_task';'nTrials_incoh_task';'total_correct_task';... + 'total_correct_incoherent_task';... + 'total_correct_coherent_task'}); + + + + + + + case 'next_trial', + + + + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + %%%%%%%%% WHAT TASK ON THIS TRIAL? %%%%%%%%%% + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + was_block_switch.value=0; + + %%% choose task for the first trial in the session + if(n_done_trials<=1) + if(value(randomize_first_task)==1) + if(rand(1)>0.5) + ThisTask.value='Direction'; + else + ThisTask.value='Frequency'; + end + else + ThisTask.value='Direction'; + end + + %%% it's not the first trial: should we switch task? + elseif(value(task_switch_auto)==1) + + %%% requirement 1: at least N trials in this task + flag1=value(nTrials_task)>=value(task_switch_mintrials); + if(value(exist_incoherent)) + %%% requirement 2: performances in the last N trials coherent above min + flag2a=value(total_correct_coherent_task)>=value(task_switch_min_perf); + %%% requirement 3: performances in the last N trials incoherent above min + flag2b=value(total_correct_incoherent_task)>=value(task_switch_min_perf); + flag2=(flag2a && flag2b); + else + %%% requirement 2: performances in the last N trials above min + flag2=value(total_correct_task)>=value(task_switch_min_perf); + end + + if(flag1 && flag2) %%% switch task + was_block_switch.value=1; + if(strcmp(value(ThisTask),'Direction')) + %from direction to frequency + ThisTask.value='Frequency'; + else + %from frequency to direction + ThisTask.value='Direction'; + end + end + end + + %%% setup stimulus variables according to current task + if(strcmp(value(ThisTask),'Direction')) + durations=value(durations_dir); + gamma_dir_values=value(gamma_dir_values_dir); + gamma_freq_values=value(gamma_freq_values_dir); + crosstalk_dir=0; + crosstalk_freq=1-value(stimulus_mixing_dir); + elseif(strcmp(value(ThisTask),'Frequency')) + durations=value(durations_freq); + gamma_dir_values=value(gamma_dir_values_freq); + gamma_freq_values=value(gamma_freq_values_freq); + crosstalk_freq=0; + crosstalk_dir=1-value(stimulus_mixing_freq); + else + error('what task?') + end + + + + + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + %%%%%%%%%%%%%% CHOOSE ANTIBIAS %%%%%%%%%%%%%% + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + + %%% is it possible to have incoherent trials? + + %stimuli aren't mixed + flag1=value(stimulus_mixing_dir)<1; + flag2=value(stimulus_mixing_freq)<1; + %one of the dimensions is not modulated + flag3=(length(gamma_dir_values)==1 && gamma_dir_values(1)==0); + flag4=(length(gamma_freq_values)==1 && gamma_freq_values(1)==0); + + if(flag1 || flag2 || flag3 || flag4) + %there is no such thing as an incoherent trial + exist_incoherent.value=0; + else + exist_incoherent.value=1; + end + + + if(value(antibias_toggle)==1) + if(value(exist_incoherent)==1) + antibias_type.value='Quadrant antibias'; + else + antibias_type.value='Side antibias'; + end + else + antibias_type.value='No antibias'; + end + + + + + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + %%%%%%%%%%%%%%%% COMPUTE ANTIBIAS %%%%%%%%%%%%%%% + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + + vec_hit=value(hit_history); + %%% kernel function: last few trials are the most important, + %%% Ab_Tau decides how many trials back matter + kernel = exp(-(0:length(vec_hit)-1)/value(Ab_Tau)); + kernel = kernel(end:-1:1); + + + %%% SIDE ANTIBIAS + vec_sides=value(side_history); + vec_sides = vec_sides(1:length(vec_hit)); + %%%% compute bias + if(isempty(find(vec_sides=='l',1)) || isempty(find(vec_sides=='r',1))) + fracs=[0.5 0.5]; + else + bias_left = nansum(vec_hit(vec_sides=='l') .* kernel(vec_sides=='l'))/sum(kernel(vec_sides=='l')); + bias_right = nansum(vec_hit(vec_sides=='r') .* kernel(vec_sides=='r'))/sum(kernel(vec_sides=='r')); + fracs=[bias_left bias_right]; + end; + fracs=fracs./sum(fracs); + if(~isempty(find(isnan(fracs)))) + fracs=[0.5 0.5]; + end + BiasL.value=round(fracs(1)*100)/100; + BiasR.value=round(fracs(2)*100)/100; + %%% compute resulting probabilities, water and delay + p = exp(-fracs*value(Ab_Beta)); + p=p./sum(p); + ProbL.value=p(1); + ProbR.value=p(2); + WaterL.value=p(1)*2; + WaterR.value=p(2)*2; + DelayL.value=max(p(1)*8,2.5); + DelayR.value=max(p(2)*8,2.5); + + + + + %%% QUADRANT ANTIBIAS + vec_quad=value(quadrant_history); + vec_task=value(task_history); + vec_quad = vec_quad(1:length(vec_hit)); + vec_task = vec_task(1:length(vec_hit)); + vec_quadd=vec_quad(vec_task=='d'); + vec_hitd=vec_hit(vec_task=='d'); + vec_quadf=vec_quad(vec_task=='f'); + vec_hitf=vec_hit(vec_task=='f'); + %%%% compute bias for direction task + if(isempty(find(vec_quadd==1,1)) || isempty(find(vec_quadd==2,1)) ||... + isempty(find(vec_quadd==4,1)) || isempty(find(vec_quadd==3,1))) + fracsd=[0.25 0.25 0.25 0.25]; + else + fracsd=nan(1,4); + for i=1:4 + fracsd(i)=nansum(vec_hitd(vec_quadd==i) .* kernel(vec_quadd==i))/sum(kernel(vec_quadd==i)); + end + end; + fracsd=fracsd./sum(fracsd); + if(~isempty(find(isnan(fracsd)))) + fracsd=[0.25 0.25 0.25 0.25]; + end + Bias1Dir.value=round(fracsd(1)*100)/100; + Bias2Dir.value=round(fracsd(2)*100)/100; + Bias3Dir.value=round(fracsd(3)*100)/100; + Bias4Dir.value=round(fracsd(4)*100)/100; + %%% compute resulting probabilities, water and delay for direction task + p = exp(-fracsd*1.5*value(Ab_Beta)); + p=p./sum(p); + Prob1Dir.value=p(1); + Prob2Dir.value=p(2); + Prob3Dir.value=p(3); + Prob4Dir.value=p(4); + Water1Dir.value=p(1)*4; + Water2Dir.value=p(2)*4; + Water3Dir.value=p(3)*4; + Water4Dir.value=p(4)*4; + Delay1Dir.value=max(p(1)*16,2); + Delay2Dir.value=max(p(2)*16,2); + Delay3Dir.value=max(p(3)*16,2); + Delay4Dir.value=max(p(4)*16,2); + + %%%% compute bias for frequency task + if(isempty(find(vec_quadf==1,1)) || isempty(find(vec_quadf==2,1)) ||... + isempty(find(vec_quadf==4,1)) || isempty(find(vec_quadf==3,1))) + fracsf=[0.25 0.25 0.25 0.25]; + else + fracsf=nan(1,4); + for i=1:4 + fracsf(i)=nansum(vec_hitf(vec_quadf==i) .* kernel(vec_quadf==i))/sum(kernel(vec_quadf==i)); + end + end; + fracsf=fracsf./sum(fracsf); + if(~isempty(find(isnan(fracsf)))) + fracsf=[0.25 0.25 0.25 0.25]; + end + Bias1Freq.value=round(fracsf(1)*100)/100; + Bias2Freq.value=round(fracsf(2)*100)/100; + Bias3Freq.value=round(fracsf(3)*100)/100; + Bias4Freq.value=round(fracsf(4)*100)/100; + %%% compute resulting probabilities, water and delay for frequency task + p = exp(-fracsf*value(Ab_Beta)); + p=p./sum(p); + Prob1Freq.value=p(1); + Prob2Freq.value=p(2); + Prob3Freq.value=p(3); + Prob4Freq.value=p(4); + Water1Freq.value=p(1)*4; + Water2Freq.value=p(2)*4; + Water3Freq.value=p(3)*4; + Water4Freq.value=p(4)*4; + Delay1Freq.value=max(p(1)*16,2); + Delay2Freq.value=max(p(2)*16,2); + Delay3Freq.value=max(p(3)*16,2); + Delay4Freq.value=max(p(4)*16,2); + + + + + + + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + %%%%%%%%%%%% SELECT QUADRANT/SIDE %%%%%%%%%%% + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + + %INCOHERENT IS DEFINED -> CHOOSE QUADRANT + if(value(exist_incoherent)==1) + + %%%% DIRECTION TASK + if(strcmp(value(ThisTask),'Direction')) + %%%% CHOOSE THE QUADRANT + if(strcmp(value(antibias_type),'Quadrant antibias')) + ThisQuadrant.value=find(mnrnd(1,[value(Prob1Dir) value(Prob2Dir)... + value(Prob3Dir) value(Prob4Dir)])==1); + elseif(strcmp(value(antibias_type),'Side antibias')) + ThisQuadrant.value=find(mnrnd(1,[value(Prob1Dir) value(Prob2Dir)... + value(Prob3Dir) value(Prob4Dir)])==1); + elseif(strcmp(value(antibias_type),'No antibias')) + ThisQuadrant.value=find(mnrnd(1,[0.25 0.25 0.25 0.25])==1); + else + error('what antibias????') + end + %%%% ASSIGN THE SIDES + if(value(ThisQuadrant)==1) + ThisSide.value='RIGHT'; + ThisSideDirection.value='RIGHT'; + ThisSideFrequency.value='LEFT'; + incoherent_trial.value=1; + end + if(value(ThisQuadrant)==2) + ThisSide.value='LEFT'; + ThisSideDirection.value='LEFT'; + ThisSideFrequency.value='LEFT'; + incoherent_trial.value=0; + end + if(value(ThisQuadrant)==3) + ThisSide.value='LEFT'; + ThisSideDirection.value='LEFT'; + ThisSideFrequency.value='RIGHT'; + incoherent_trial.value=1; + end + if(value(ThisQuadrant)==4) + ThisSide.value='RIGHT'; + ThisSideDirection.value='RIGHT'; + ThisSideFrequency.value='RIGHT'; + incoherent_trial.value=0; + end + + %%%% FREQUENCY TASK + elseif(strcmp(value(ThisTask),'Frequency')) + %%%% CHOOSE THE QUADRANT + if(strcmp(value(antibias_type),'Quadrant antibias')) + ThisQuadrant.value=find(mnrnd(1,[value(Prob1Freq) value(Prob2Freq)... + value(Prob3Freq) value(Prob4Freq)])==1); + elseif(strcmp(value(antibias_type),'Side antibias')) + ThisQuadrant.value=find(mnrnd(1,[0 value(ProbL) 0 value(ProbR)])==1); + elseif(strcmp(value(antibias_type),'No antibias')) + ThisQuadrant.value=find(mnrnd(1,[0.25 0.25 0.25 0.25])==1); + else + error('what antibias????') + end + %%%% ASSIGN THE SIDES + if(value(ThisQuadrant)==1) + ThisSide.value='LEFT'; + ThisSideDirection.value='RIGHT'; + ThisSideFrequency.value='LEFT'; + incoherent_trial.value=1; + end + if(value(ThisQuadrant)==2) + ThisSide.value='LEFT'; + ThisSideDirection.value='LEFT'; + ThisSideFrequency.value='LEFT'; + incoherent_trial.value=0; + end + if(value(ThisQuadrant)==3) + ThisSide.value='RIGHT'; + ThisSideDirection.value='LEFT'; + ThisSideFrequency.value='RIGHT'; + incoherent_trial.value=1; + end + if(value(ThisQuadrant)==4) + ThisSide.value='RIGHT'; + ThisSideDirection.value='RIGHT'; + ThisSideFrequency.value='RIGHT'; + incoherent_trial.value=0; + end + else + error('what task????') + end + + %INCOHERENT NOT DEFINED -> ONLY CHOOSE THE SIDE + else + incoherent_trial.value=0; + ThisQuadrant.value=NaN; + %CHOOSE SIDE + sidevals={'LEFT','RIGHT'}; + if(strcmp(value(antibias_type),'Side antibias')) + ind=find(mnrnd(1,[value(ProbL) value(ProbR)])==1); + elseif(strcmp(value(antibias_type),'No antibias')) + ind=find(mnrnd(1,[0.5 0.5])==1); + else + error('what antibias???') + end + %%% ASSIGN THE SIDE + ThisSide.value=sidevals{ind}; + ThisSideDirection.value=sidevals{ind}; + ThisSideFrequency.value=sidevals{ind}; + end + + + + + + + + + + + + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + %%%%%%%%%%%%% ASSIGN WATER/DELAY %%%%%%%%%%%% + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + %water multiplier + if(value(water_increase_toggle)==1) + water_increase_multiplier.value=value(WI_base)+n_done_trials*value(WI_ratio); + water_increase_multiplier.value=min([value(WI_max),value(water_increase_multiplier)]); + else + water_increase_multiplier.value=1; + end + + + %%%% QUADRANT ANTIBIAS + if(strcmp(value(antibias_type),'Quadrant antibias')) + %%%% DIRECTION TASK + if(strcmp(value(ThisTask),'Direction')) + if(value(ThisQuadrant)==1) + total_water_multiplier.value=value(Water1Dir)*value(water_increase_multiplier); + total_error_delay.value=value(Delay1Dir); + end + if(value(ThisQuadrant)==2) + total_water_multiplier.value=value(Water2Dir)*value(water_increase_multiplier); + total_error_delay.value=value(Delay2Dir); + end + if(value(ThisQuadrant)==3) + total_water_multiplier.value=value(Water3Dir)*value(water_increase_multiplier); + total_error_delay.value=value(Delay3Dir); + end + if(value(ThisQuadrant)==4) + total_water_multiplier.value=value(Water4Dir)*value(water_increase_multiplier); + total_error_delay.value=value(Delay4Dir); + end + %%%% FREQUENCY TASK + elseif(strcmp(value(ThisTask),'Frequency')) + if(value(ThisQuadrant)==1) + total_water_multiplier.value=value(Water1Freq)*value(water_increase_multiplier); + total_error_delay.value=value(Delay1Freq); + end + if(value(ThisQuadrant)==2) + total_water_multiplier.value=value(Water2Freq)*value(water_increase_multiplier); + total_error_delay.value=value(Delay2Freq); + end + if(value(ThisQuadrant)==3) + total_water_multiplier.value=value(Water3Freq)*value(water_increase_multiplier); + total_error_delay.value=value(Delay3Freq); + end + if(value(ThisQuadrant)==4) + total_water_multiplier.value=value(Water4Freq)*value(water_increase_multiplier); + total_error_delay.value=value(Delay4Freq); + end + else + error('what task????') + end + %%%% SIDE ANTIBIAS + elseif(strcmp(value(antibias_type),'Side antibias')) + if(strcmp(value(ThisSide),'RIGHT')) + total_water_multiplier.value=value(WaterR)*value(water_increase_multiplier); + total_error_delay.value=value(DelayR); + elseif(strcmp(value(ThisSide),'LEFT')) + total_water_multiplier.value=value(WaterL)*value(water_increase_multiplier); + total_error_delay.value=value(DelayL); + else + error('what side???') + end + %%%% NO ANTIBIAS + elseif(strcmp(value(antibias_type),'No antibias')) + total_water_multiplier.value=1; + total_error_delay.value=4; + else + error('what antibias???') + end + + + + + + + + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + %%%%%%%%%%%%% GENERATE STIMULUS %%%%%%%%%%%% + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + + %%%% get sample rate + srate = SoundManagerSection(obj, 'get_sample_rate'); + + + %%% select duration for the current trial + vec=durations; + ra=randperm(length(vec)); + ThisDuration.value=vec(ra(1)); + + + %%% select gamma frequency for the current trial + vec=value(gamma_freq_values); + ra=randperm(length(vec)); + if(strcmp(value(ThisSideFrequency),'RIGHT')) + ThisGamma_freq.value=vec(ra(1)); + elseif(strcmp(value(ThisSideFrequency),'LEFT')) + ThisGamma_freq.value=-vec(ra(1)); + else + error('what?') + end + + + %%% select gamma direction for the current trial + vec=value(gamma_dir_values); + ra=randperm(length(vec)); + if(strcmp(value(ThisSideDirection),'RIGHT')) + ThisGamma_dir.value=vec(ra(1)); + elseif(strcmp(value(ThisSideDirection),'LEFT')) + ThisGamma_dir.value=-vec(ra(1)); + else + error('whattt?') + end + + + freq_vec=[value(freq_lo) value(freq_hi)]; + + + [snd data] = make_pbup_mixed3(value(total_rate),... + value(ThisGamma_dir),value(ThisGamma_freq), srate, value(ThisDuration), ... + 'bup_width',value(bup_width),'crosstalk_dir', crosstalk_dir,... + 'crosstalk_freq', crosstalk_freq,'freq_vec',freq_vec,'bup_ramp',... + value(bup_ramp),'vol_low',value(vol_low_freq),'vol_hi',value(vol_hi_freq)); + + + snd=snd*value(vol); + + + if ~SoundManagerSection(obj, 'sound_exists', 'StimulusSound'), + SoundManagerSection(obj, 'declare_new_sound', 'StimulusSound'); + SoundManagerSection(obj, 'set_sound', 'StimulusSound', snd); + else + snd_prev = SoundManagerSection(obj, 'get_sound', 'StimulusSound'); + if ~isequal(snd, snd_prev), + SoundManagerSection(obj, 'set_sound', 'StimulusSound', snd); + end; + end; + + bpt.freqs=freq_vec; + bpt.crosstalk_dir=crosstalk_dir; + bpt.crosstalk_freq=crosstalk_freq; + bpt.bup_width=value(bup_width); + bpt.bup_ramp=value(bup_ramp); + bpt.vol_low=value(vol_low_freq); + bpt.vol_hi=value(vol_hi_freq); + bpt.vol=value(vol); + bpt.gamma_dir = value(ThisGamma_dir); + bpt.gamma_freq = value(ThisGamma_freq); + bpt.duration = value(ThisDuration); + bpt.left_hi = data.left_hi; + bpt.right_hi = data.right_hi; + bpt.left_lo = data.left_lo; + bpt.right_lo = data.right_lo; + ThisStimulus.value = bpt; + push_history(ThisStimulus); + + + + + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + %%%%%%%%%%%% UPDATE PLOT BUTTON %%%%%%%%%%%% + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + case 'update_plot_button', + + %%% re-initialize the plots regardless of n_done_trials + feval(mfilename, obj, 'initialize_plots'); + + %populate the images + feval(mfilename, obj, 'update_plot'); + + + + + + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + %%%%%%%%%%%% UPDATE PLOT %%%%%%%%%%%% + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + case 'update_plot', + + if(n_done_trials==1) + %if it's the first trial, re-initialize plots + feval(mfilename, obj, 'initialize_plots'); + end + + %%% GET THE DATA + hits=value(hit_history); + side=value(side_history); + task=value(task_history); + task=task(1:length(hits)); + gdir=value(gammadir_history); + gdir=gdir(1:length(hits)); + gfreq=value(gammafreq_history); + gfreq=gfreq(1:length(hits)); + %remove violation trials + ind=find(~isnan(hits)); + hits=hits(ind); + side=side(ind); + task=task(ind); + gdir=gdir(ind); + gfreq=gfreq(ind); + %save choice (where the rat went) + choice=nan(1,length(side)); + for i=1:length(side) + if(hits(i)==1) + if(side(i)=='r') + choice(i)=1; + else + choice(i)=0; + end + else + if(side(i)=='r') + choice(i)=0; + else + choice(i)=1; + end + end + end + + + %%% POPULATE DIRECTION TASK + + %unique values of direction during direction trials + xvec=value(gamma_dir_values_dir); + xvec=unique([-xvec xvec]); + %unique values of frequency during direction trials + yvec=value(gamma_freq_values_dir); + yvec=unique([-yvec yvec]); + n1=length(xvec); + n2=length(yvec); + %subselect direction trials + curtask='d'; + ind=find(task==curtask); + gdir1=gdir(ind); + gfreq1=gfreq(ind); + choice1=choice(ind); + %populate matrix + matrice=nan(n2,n1); + for i=1:n2 + for j=1:n1 + indz=find(gfreq1==yvec(i) & gdir1==xvec(j)); + if(~isempty(indz)) + matrice(i,j)=mean(choice1(indz)); + else + matrice(i,j)=NaN; + end + end + end + %retrieve image handle + hdir=value(matrixdir); + %populate image + set(hdir,'CData',matrice); + + + %%% POPULATE FREQUENCY TASK + + %unique values of direction during frequency trials + xvec=value(gamma_dir_values_freq); + xvec=unique([-xvec xvec]); + %unique values of frequency during frequency trials + yvec=value(gamma_freq_values_freq); + yvec=unique([-yvec yvec]); + n1=length(xvec); + n2=length(yvec); + %subselect frequency trials + curtask='f'; + ind=find(task==curtask); + gdir1=gdir(ind); + gfreq1=gfreq(ind); + choice1=choice(ind); + %populate matrix + matrice=nan(n2,n1); + for i=1:n2 + for j=1:n1 + indz=find(gfreq1==yvec(i) & gdir1==xvec(j)); + if(~isempty(indz)) + matrice(i,j)=mean(choice1(indz)); + else + matrice(i,j)=NaN; + end + end + end + %retrieve image handle + hfreq=value(matrixfreq); + %populate image + set(hfreq,'CData',matrice); + + + + %%% POPULATE LAST TRIAL PLOT + + + hits=value(hit_history); + task=value(task_history); + task=task(1:length(hits)); + gdir=value(gammadir_history); + gdir=gdir(1:length(hits)); + gfreq=value(gammafreq_history); + gfreq=gfreq(1:length(hits)); + + if(~isempty(hits) && ~isnan(hits(end))) + + %%% copy over old plots + cla(value(myaxeshistory4)); + copyobj(get(value(myaxeshistory3),'Children'),value(myaxeshistory4)); + set(value(myaxeshistory4),'XLim',get(value(myaxeshistory3),'XLim')); + set(value(myaxeshistory4),'YLim',get(value(myaxeshistory3),'YLim')); + set(value(myaxeshistory4),'XTick',[],'YTick',[]); + set(value(myaxeshistory4),'Box','off'); + cla(value(myaxeshistory3)); + copyobj(get(value(myaxeshistory2),'Children'),value(myaxeshistory3)); + set(value(myaxeshistory3),'XLim',get(value(myaxeshistory2),'XLim')); + set(value(myaxeshistory3),'YLim',get(value(myaxeshistory2),'YLim')); + set(value(myaxeshistory3),'XTick',[],'YTick',[]); + set(value(myaxeshistory3),'Box','off'); + cla(value(myaxeshistory2)); + copyobj(get(value(myaxeshistory1),'Children'),value(myaxeshistory2)); + set(value(myaxeshistory2),'XLim',get(value(myaxeshistory1),'XLim')); + set(value(myaxeshistory2),'YLim',get(value(myaxeshistory1),'YLim')); + set(value(myaxeshistory2),'XTick',[],'YTick',[]); + set(value(myaxeshistory2),'Box','off'); + cla(value(myaxeshistory1)); + copyobj(get(value(myaxeshistory0),'Children'),value(myaxeshistory1)); + set(value(myaxeshistory1),'XLim',get(value(myaxeshistory0),'XLim')); + set(value(myaxeshistory1),'YLim',get(value(myaxeshistory0),'YLim')); + set(value(myaxeshistory1),'XTick',[],'YTick',[]); + set(value(myaxeshistory1),'Box','off'); + + %%% populate new plot + lasthit=hits(end); + lasttask=task(end); + lastgdir=gdir(end); + lastgfreq=gfreq(end); + cla(value(myaxeshistory0)); + hold(value(myaxeshistory0),'off'); + if(lasttask=='d') + xvec=value(gamma_dir_values_dir); + xvec=unique([-xvec xvec]); + yvec=value(gamma_freq_values_dir); + yvec=unique([-yvec yvec]); + yvec=yvec(end:-1:1); + n1=length(xvec); + n2=length(yvec); + plot(value(myaxeshistory0),mean(1:n1),mean(1:n2),'xb'); + elseif(lasttask=='f') + xvec=value(gamma_dir_values_freq); + xvec=unique([-xvec xvec]); + yvec=value(gamma_freq_values_freq); + yvec=unique([-yvec yvec]); + yvec=yvec(end:-1:1); + n1=length(xvec); + n2=length(yvec); + plot(value(myaxeshistory0),mean(1:n1),mean(1:n2),'or'); + end + hold(value(myaxeshistory0),'on'); + mat=repmat(1:n1,n2,1)'; + vec1=mat(:); + mat=repmat(1:n2,n1,1); + vec2=mat(:); + plot(value(myaxeshistory0),vec1,vec2,'.k') + xval=find(lastgdir==xvec); + yval=find(lastgfreq==yvec); + if(~isempty(xval) && ~isempty(yval)) + if(lasthit==1) + plot(value(myaxeshistory0),xval,yval,'.g','MarkerSize',20) + else + plot(value(myaxeshistory0),xval,yval,'.r','MarkerSize',20) + end + end + set(value(myaxeshistory0),'XLim',[0.5 n1+.5],'YLim',[.5 n2+.5]); + hold(value(myaxeshistory0),'off'); + set(value(myaxeshistory0),'XTick',[],'YTick',[]); + set(value(myaxeshistory0),'Box','off'); + + end + + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + %%%%%%%%%%%% INITIALIZE PLOTS %%%%%%%%%%%% + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + case 'initialize_plots', + + %%% generate colormap for imagesc plots + bu=nan(64,3); + bu(:,1)=[zeros(23,1); linspace(0,1,17)'; ones(15,1); linspace(1,0.5,9)']; + bu(:,2)=[zeros(7,1); linspace(0,1,17)'; ones(15,1); linspace(1,0,17)'; zeros(8,1)]; + bu(:,3)=[linspace(0.5625,1,8)'; ones(15,1); linspace(1,0,17)'; zeros(24,1)]; + bu=[1 1 1;bu]; + + %%% DIRECTION PLOT + + %unique values of direction during direction trials + xvec=value(gamma_dir_values_dir); + xvec=unique([xvec -xvec]); + %unique values of frequency during direction trials + yvec=value(gamma_freq_values_dir); + yvec=unique([yvec -yvec]); + n1=length(xvec); + n2=length(yvec); + %initialize image and save handle + axes(value(myaxesdir)); + hdir=imagesc(nan(n2,n1),[-0.0159 1]); + matrixdir.value=hdir; + colormap(bu); + colorbar; + axis image + set(value(myaxesdir),'XTick',[],'YTick',[]); + title('Direction') + + + %%% FREQUENCY PLOT + + %unique values of direction during frequency trials + xvec=value(gamma_dir_values_freq); + xvec=unique([xvec -xvec]); + %unique values of frequency during frequency trials + yvec=value(gamma_freq_values_freq); + yvec=unique([yvec -yvec]); + n1=length(xvec); + n2=length(yvec); + %initialize image and save handle + axes(value(myaxesfreq)); + hfreq=imagesc(nan(n2,n1),[-0.0159 1]); + matrixfreq.value=hfreq; + colormap(bu); + colorbar; + axis image + set(value(myaxesfreq),'XTick',[],'YTick',[]); + title('Frequency') + + + + + + case 'hide', + StimuluShow.value = 0; set(value(myfig), 'Visible', 'off'); + case 'show', + StimuluShow.value = 1; set(value(myfig), 'Visible', 'on'); + case 'show_hide', + if StimuluShow == 1, set(value(myfig), 'Visible', 'on'); + else set(value(myfig), 'Visible', 'off'); + end; + + + case 'hide2', + SoundsShow.value = 0; set(value(myfig2), 'Visible', 'off'); + case 'show2', + SoundsShow.value = 1; set(value(myfig2), 'Visible', 'on'); + case 'show_hide2', + if SoundsShow == 1, set(value(myfig2), 'Visible', 'on'); + else set(value(myfig2), 'Visible', 'off'); + end; + + + case 'hide3', + TimingShow.value = 0; set(value(myfig3), 'Visible', 'off'); + case 'show3', + TimingShow.value = 1; set(value(myfig3), 'Visible', 'on'); + case 'show_hide3', + if TimingShow == 1, set(value(myfig3), 'Visible', 'on'); + else set(value(myfig3), 'Visible', 'off'); + end; + + + case 'close', + delete(value(myfig)); + delete(value(myfig2)); + delete(value(myfig3)); + + + case 'get' + val=varargin{1}; + eval(['x=value(' val ');']); + +end + + diff --git a/Protocols/@TaskSwitch4/TaskSwitch4.m b/Protocols/@TaskSwitch4/TaskSwitch4.m new file mode 100644 index 00000000..8956695f --- /dev/null +++ b/Protocols/@TaskSwitch4/TaskSwitch4.m @@ -0,0 +1,224 @@ +% TaskSwitch4 protocol +% Marino Pagan, October 2015 + +function [obj] = TaskSwitch4(varargin) + +% Default object is of our own class (mfilename); +% we inherit only from Plugins + +obj = class(struct, mfilename, saveload, water, ... + pokesplot2, soundmanager, soundui, ... + distribui, comments, sqlsummary); + +%--------------------------------------------------------------- +% BEGIN SECTION COMMON TO ALL PROTOCOLS, DO NOT MODIFY +%--------------------------------------------------------------- + +% If creating an empty object, return without further ado: +if nargin==0 || (nargin==1 && ischar(varargin{1}) && strcmp(varargin{1}, 'empty')), + return; +end; + +if isa(varargin{1}, mfilename), % If first arg is an object of this class itself, we are + % Most likely responding to a callback from + % a SoloParamHandle defined in this mfile. + if length(varargin) < 2 || ~ischar(varargin{2}), + error(['If called with a "%s" object as first arg, a second arg, a ' ... + 'string specifying the action, is required\n']); + else action = varargin{2}; varargin = varargin(3:end); %#ok + end; +else % Ok, regular call with first param being the action string. + action = varargin{1}; varargin = varargin(2:end); %#ok +end; + +GetSoloFunctionArgs(obj); + +switch action, + + case 'init' + + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + %%%%%%%%%%%% INIT %%%%%%%%%%%% + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + + getSessID(obj); + dispatcher('set_trialnum_indicator_flag'); + % Make default figure + SoloParamHandle(obj, 'myfig', 'saveable', 0); myfig.value = double(figure); + % Make the title of the figure be the protocol name + name = mfilename;set(value(myfig), 'Name', name, 'Tag', name, ... + 'closerequestfcn', 'dispatcher(''close_protocol'')', 'MenuBar', 'none'); + % Hack Variable + hackvar = 10; SoloFunctionAddVars('SessionModel', 'ro_args', 'hackvar'); %#ok + % Put the figure where we want it and resize (x,y,width,height) + set(value(myfig), 'Position', [400 100 850 570]); + % From Plugins/@soundmanager: + SoundManagerSection(obj, 'init'); + + + % ---------------------- + % Set up the main GUI window + % ---------------------- + x = 5; y = 5; % Initial position on main GUI window + + + % From Plugins/@saveload: + [x, y] = SavingSection(obj, 'init', x, y); + + next_row(y,-0.4); + + % Comments + [x, y] = CommentsSection(obj, 'init', x, y); + + % From Plugins/@water: + [x, y] = WaterValvesSection(obj, 'init', x, y, 'streak_gui', 1); + + next_row(y,-0.4); + + % PokesPlot + SC = state_colors(obj); + [x, y] = PokesPlotSection(obj,'init',x,y,struct('states',SC)); + PokesPlotSection(obj, 'set_alignon', 'cpoke(1,1)'); + PokesPlotSection(obj, 'hide'); + next_row(y); + + + % History + [x, y] = HistorySection(obj, 'init', x, y); + + + % Training + [x, y] = TrainingSection(obj, 'init', x, y); + + + %%%%%%%%%%%%%%%%%%%%% NEXT COLUMN %%%%%%%%%%%%%%%%%%%%% + y=5; next_column(x); + + + % Stimulus + [x, y] = StimulusSection(obj, 'init', x, y); + + + + figpos = get(gcf, 'Position'); + [expmtr, rname]=SavingSection(obj, 'get_info'); + HeaderParam(obj, 'prot_title', ['TaskSwitch4: ' expmtr ', ' rname], ... + x, y, 'position', [10 figpos(4)-20, 807 20]); + + + % OK, start preparing the first trial + TaskSwitch4(obj, 'prepare_next_trial'); + + + + + case 'prepare_next_trial' + + + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + %%%%%%%%%%%% PREPARE NEXT TRIAL %%%%%%%%%%% + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + + %%% update history + HistorySection(obj, 'next_trial'); + + %%% update training stage + TrainingSection(obj, 'next_trial'); + + %%% set up stimulus for next trial + StimulusSection(obj, 'next_trial'); + + + SoundManagerSection(obj, 'send_not_yet_uploaded_sounds'); + + % invoke autosave + SavingSection(obj, 'autosave_data'); + if n_done_trials==1 + [expmtr, rname]=SavingSection(obj, 'get_info'); + prot_title.value=['TaskSwitch4 - on rig ' get_hostname ' : ' expmtr ', ' rname '. Started at ' datestr(now, 'HH:MM')]; + + + + end + + %%% prepare the actual state matrix + SMA1(obj,'next_trial'); + + + %%% this line updates the "training room" webpage on Zut + try send_n_done_trials(obj); end + + + + case 'trial_completed' + feval(mfilename, 'update'); + + StimulusSection(obj, 'update_plot'); + + % And PokesPlot needs completing the trial: + PokesPlotSection(obj, 'trial_completed'); + + if n_done_trials==1, + CommentsSection(obj, 'append_date'); + CommentsSection(obj, 'append_line', ''); + end; + CommentsSection(obj, 'clear_history'); % Make sure we're not storing unnecessary history + + + + case 'update' + PokesPlotSection(obj, 'update'); + + + + case 'close' + PokesPlotSection(obj, 'close'); + CommentsSection(obj, 'close'); + + if exist('myfig', 'var') && isa(myfig, 'SoloParamHandle') && ishandle(value(myfig)), + delete(value(myfig)); + end; + + if exist('myfig2', 'var') && isa(myfig2, 'SoloParamHandle') && ishandle(value(myfig2)), + delete(value(myfig2)); + end; + + if exist('myfig3', 'var') && isa(myfig3, 'SoloParamHandle') && ishandle(value(myfig3)), + delete(value(myfig3)); + end; + + try + delete_sphandle('owner', ['^@' class(obj) '$']); + catch + warning('Some SoloParams were not properly cleaned up'); + end + + + case 'end_session' + HistorySection(obj, 'end_session'); + TrainingSection(obj, 'end_session'); + prot_title.value = [value(prot_title) ', Ended at ' datestr(now, 'HH:MM')]; + + + case 'pre_saving_settings' + HistorySection(obj, 'make_and_send_summary'); + + + case 'get' + val=varargin{1}; + + eval(['x=value(' val ');']); + + + otherwise, + warning('Unknown action! "%s"\n', action); +end; + +return; + diff --git a/Protocols/@TaskSwitch4/TrainingSection.m b/Protocols/@TaskSwitch4/TrainingSection.m new file mode 100644 index 00000000..1b9993df --- /dev/null +++ b/Protocols/@TaskSwitch4/TrainingSection.m @@ -0,0 +1,748 @@ +function [x, y] = TrainingSection(obj, action, varargin) + +%%%% TODO: + +%%% training stages and substages : when nic grows etc. -> make explicit + +%%% add the possibility to go back in training (???) +%manual +%%% reward more the harder task / make it easier??? +%manual +%%% put some constant checks: bias -> update antibias ; motivation -> increase water ; incoherent trial performance is low -> pump up incoh delay+reward +%manual + +GetSoloFunctionArgs(obj); + + +switch action, + + % ------------------------------------------------------------------ + % INIT + % ------------------------------------------------------------------ + + case 'init' + x=varargin{1}; + y=varargin{2}; + + %%%% AUTO STAGE SWITCH PARAMETERS + + + + ToggleParam(obj, 'stage_switch_auto', 1, x, y, 'position', [x y 200 20], ... + 'OffString', 'Autotrain OFF', 'OnString', 'Autotrain ON', ... + 'TooltipString', 'If on, switches automatically between training stages'); + next_row(y,1); + + + + + NumeditParam(obj, 'nDays_stage',1, x, y, 'labelfraction', 0.55,'label','nDays','position', [x y 100 20]); + NumeditParam(obj, 'nTrials_stage',0, x, y, 'labelfraction', 0.55,'label','nTrials','position', [x+100 y 100 20]);next_row(y); + + + + %%%% MANUALLY SET THE STAGE + + DispParam(obj, 'stage_explanation', sprintf('Stage description'),... + x, y, 'label','','position', [x y 200 20], 'labelfraction', 0.01,... + 'TooltipString', 'Description of current stage');next_row(y); + + + MenuParam(obj, 'training_stage', {'Stage 1'; 'Stage 2'; 'Stage 3';... + 'Stage 4'; 'Stage 5'; 'Stage 6'; 'Stage 7'; 'Stage 8a'; 'Stage 8b';... + 'Stage 8'; 'Stage 9a'; 'Stage 9b'; 'Stage 9'; 'Stage 10a'; 'Stage 10b';... + 'Stage 10'; 'Stage grow NIC'}, 1, x, y, ... + 'label', 'Active Stage', 'TooltipString', 'the current training stage'); + + PushbuttonParam(obj,'update_active_stage', x, y, 'position', [x+170 y 30 20],'label', 'OK'); + set_callback(update_active_stage, {mfilename, 'update_stage_button'}); + next_row(y); + + SubheaderParam(obj,'title',mfilename,x,y); + next_row(y, 1.5); + + SoloFunctionAddVars('HistorySection', 'ro_args', {'training_stage';'nDays_stage'}); + SoloFunctionAddVars('HistorySection', 'rw_args', {'nTrials_stage'}); + + + + + case 'next_trial', + if(n_done_trials>1 && value(stage_switch_auto)==1) + feval(mfilename, obj, 'update_stage'); + end + + + + + case 'update_stage_button', + + %reboot stage + nTrials_stage.value= 0; + nDays_stage.value= 1; + previous_stage.value=value(training_stage); + + %set parameters + feval(mfilename, obj, 'update_stage'); + + + + + case 'update_stage', + + %new stage? + if(~strcmp(value(previous_stage),value(training_stage))) + nTrials_stage.value= 0; + nDays_stage.value= 1; + previous_stage.value=value(training_stage); + end + + + + switch value(training_stage) + + case 'Stage 1', + stage_explanation.value=sprintf('dir only: progressively grow NIC'); + %%% updated only on the first trial + if(value(nTrials_stage)==0) + %%% task parameters + ThisTask.value='Direction'; + randomize_first_task.value=0; + task_switch_auto.value=0; + %%% direction task parameters + stimulus_mixing_dir.value=0; + gamma_dir_values_dir.value=4; + gamma_freq_values_dir.value=0; + durations_dir.value=1.3; + %%% other parameters + nose_in_center.value=0.05; + antibias_type.value='Side antibias'; + end + %%% algorithm: grow NIC + if(value(nose_in_center)>=1.3) + nose_in_center.value=1.3; + training_stage.value='Stage 2'; + nTrials_stage.value= 0; + nDays_stage.value= 1; + elseif(value(nTrials_stage)>0 && value(nose_in_center)<1.3 && value(was_hit)==1) + nose_in_center.value=value(nose_in_center)+0.001; + end + + case 'Stage 2', + stage_explanation.value=sprintf('dir only: wait for good endpoints'); + %%% updated only on the first trial + if(value(nTrials_stage)==0) + %%% task parameters + ThisTask.value='Direction'; + randomize_first_task.value=0; + task_switch_auto.value=0; + %%% direction task parameters + stimulus_mixing_dir.value=0; + gamma_dir_values_dir.value=4; + gamma_freq_values_dir.value=0; + durations_dir.value=1.3; + %%% other parameters + nose_in_center.value=1.3; + antibias_type.value='Side antibias'; + end + %%% algorithm: wait for good endpoints + if(n_done_trials>150 && value(nValid)>150 && value(dir_correct)>0.75 ... + && value(left_correct)>0.7 && value(right_correct)>0.7 ... + && value(nDays_stage)>2 && value(nTrials_stage)>150) + training_stage.value='Stage 3'; + nTrials_stage.value= 0; + nDays_stage.value= 1; + end + + case 'Stage 3', + stage_explanation.value=sprintf('dir+freq: add easy frequency trials'); + %%% updated only on the first trial + if(value(nTrials_stage)==0) + %%% task parameters + ThisTask.value='Direction'; + randomize_first_task.value=0; + task_switch_auto.value=1; + task_switch_min_perf.value=0.8; + %%% direction task parameters + stimulus_mixing_dir.value=0; + gamma_dir_values_dir.value=4; + gamma_freq_values_dir.value=0; + durations_dir.value=1.3; + %%% frequency task parameters + stimulus_mixing_freq.value=0; + gamma_dir_values_freq.value=0; + gamma_freq_values_freq.value=4; + durations_freq.value=10; + %%% frequency task help parameters + helper_lights_freq.value=1; + error_forgiveness_freq.value=1; + wait_delay_freq.value=0.2; + %%% other parameters + nose_in_center.value=1.3; + antibias_type.value='Side antibias'; + end + %%% algorithm: turn lights off, increase wait delay + if(value(nTrials_stage)>30) + %%% after 30 trials turn off the helper lights + helper_lights_freq.value=0; + %%% slowly increase the wait delay + if(value(wait_delay_freq)<3) + if(strcmp(value(ThisTask),'Frequency') && value(result)==5) + wait_delay_freq.value=value(wait_delay_freq)+0.1; + end + else + wait_delay_freq.value=3; + error_forgiveness_freq.value=0; + durations_freq.value=1.3; + training_stage.value='Stage 4'; + nTrials_stage.value= 0; + nDays_stage.value= 1; + end + end + + case 'Stage 4', + stage_explanation.value=sprintf('dir+freq: wait for good endpoints'); + %%% updated only on the first trial + if(value(nTrials_stage)==0) + %%% task parameters + ThisTask.value='Direction'; + randomize_first_task.value=0; + task_switch_auto.value=1; + task_switch_min_perf.value=0.8; + %%% direction task parameters + stimulus_mixing_dir.value=0; + gamma_dir_values_dir.value=4; + gamma_freq_values_dir.value=0; + durations_dir.value=1.3; + %%% frequency task parameters + stimulus_mixing_freq.value=0; + gamma_dir_values_freq.value=0; + gamma_freq_values_freq.value=4; + durations_freq.value=1.3; + %%% frequency task help parameters + helper_lights_freq.value=0; + error_forgiveness_freq.value=0; + %%% other parameters + nose_in_center.value=1.3; + antibias_type.value='Side antibias'; + end + %%% algorithm: wait for good endpoints + if(n_done_trials>150 && value(nValid)>150 && value(dir_correct)>0.7 ... + && value(freq_correct)>0.7 ... + && value(left_correct)>0.7 && value(right_correct)>0.7 ... + && value(nDays_stage)>2 && value(nTrials_stage)>150) + training_stage.value='Stage 5'; + nTrials_stage.value= 0; + nDays_stage.value= 1; + end + + case 'Stage 5', + stage_explanation.value=sprintf('dir+freq: progressively mix stimuli'); + %%% updated only on the first trial + if(value(nTrials_stage)==0) + %%% task parameters + ThisTask.value='Direction'; + randomize_first_task.value=0; + task_switch_auto.value=1; + task_switch_min_perf.value=0.8; + %%% direction task parameters + gamma_dir_values_dir.value=4; + gamma_freq_values_dir.value=0; + durations_dir.value=1.3; + %%% frequency task parameters + gamma_dir_values_freq.value=0; + gamma_freq_values_freq.value=4; + durations_freq.value=1.3; + %%% frequency task help parameters + helper_lights_freq.value=0; + error_forgiveness_freq.value=0; + %%% other parameters + nose_in_center.value=1.3; + antibias_type.value='Side antibias'; + end + %%% algorithm: at the end of each block increase mixing + if(value(stimulus_mixing_dir)<0.99 || value(stimulus_mixing_freq)<0.99) + if(value(was_block_switch)==1) + if(strcmp(value(ThisTask),'Direction')) + stimulus_mixing_dir.value=value(stimulus_mixing_dir)+0.1; + if(value(stimulus_mixing_dir)>1) + stimulus_mixing_dir.value=1; + end + else + stimulus_mixing_freq.value=value(stimulus_mixing_freq)+0.1; + if(value(stimulus_mixing_freq)>1) + stimulus_mixing_freq.value=1; + end + end + end + else + training_stage.value='Stage 6'; + nTrials_stage.value= 0; + nDays_stage.value= 1; + end + + case 'Stage 6', + stage_explanation.value=sprintf('mixed stimuli: wait for endpoints'); + %%% updated only on the first trial + if(value(nTrials_stage)==0) + %%% task parameters + ThisTask.value='Direction'; + randomize_first_task.value=0; + task_switch_auto.value=1; + task_switch_min_perf.value=0.8; + %%% direction task parameters + stimulus_mixing_dir.value=1; + gamma_dir_values_dir.value=4; + gamma_freq_values_dir.value=0; + durations_dir.value=1.3; + %%% frequency task parameters + stimulus_mixing_freq.value=1; + gamma_dir_values_freq.value=0; + gamma_freq_values_freq.value=4; + durations_freq.value=1.3; + %%% frequency task help parameters + helper_lights_freq.value=0; + error_forgiveness_freq.value=0; + %%% other parameters + nose_in_center.value=1.3; + antibias_type.value='Side antibias'; + end + %%% algorithm: wait for good endpoints + if(n_done_trials>150 && value(nValid)>150 && value(dir_correct)>0.7 ... + && value(freq_correct)>0.7 ... + && value(left_correct)>0.7 && value(right_correct)>0.7 ... + && value(nDays_stage)>2 && value(nTrials_stage)>150) + training_stage.value='Stage 7'; + nTrials_stage.value= 0; + nDays_stage.value= 1; + end + + case 'Stage 7', + stage_explanation.value=sprintf('incongruent 1 (4; 1,4)'); + %%% updated only on the first trial + if(value(nTrials_stage)==0) + %%% task parameters + ThisTask.value='Direction'; + randomize_first_task.value=0; + task_switch_auto.value=1; + task_switch_min_perf.value=0.7; + %%% direction task parameters + stimulus_mixing_dir.value=1; + gamma_dir_values_dir.value=4; + gamma_freq_values_dir.value=[1 4]; + durations_dir.value=1.3; + %%% frequency task parameters + stimulus_mixing_freq.value=1; + gamma_dir_values_freq.value=[1 4]; + gamma_freq_values_freq.value=4; + durations_freq.value=1.3; + %%% frequency task help parameters + helper_lights_freq.value=0; + error_forgiveness_freq.value=0; + %%% other parameters + nose_in_center.value=1.3; + antibias_type.value='Quadrant antibias'; + end + %%% algorithm: wait for good endpoints + if(n_done_trials>150 && value(nValid)>150 && value(dir_correct)>0.68 ... + && value(freq_correct)>0.68 ... + && value(left_correct)>0.68 && value(right_correct)>0.68 ... + && value(correct_coherent)>0.68 && value(correct_incoherent)>0.63 ... + && value(nDays_stage)>2 && value(nTrials_stage)>150) + training_stage.value='Stage 8a'; + nTrials_stage.value= 0; + nDays_stage.value= 1; + end + + + case 'Stage 8a', + stage_explanation.value=sprintf('incongruent 2a (4; 1,1.5,3.5,4)'); + %%% updated only on the first trial + if(value(nTrials_stage)==0) + %%% task parameters + ThisTask.value='Direction'; + randomize_first_task.value=0; + task_switch_auto.value=1; + task_switch_min_perf.value=0.7; + %%% direction task parameters + stimulus_mixing_dir.value=1; + gamma_dir_values_dir.value=4; + gamma_freq_values_dir.value=[1 1.5 3.5 4]; + durations_dir.value=1.3; + %%% frequency task parameters + stimulus_mixing_freq.value=1; + gamma_dir_values_freq.value=[1 1.5 3.5 4]; + gamma_freq_values_freq.value=4; + durations_freq.value=1.3; + %%% frequency task help parameters + helper_lights_freq.value=0; + error_forgiveness_freq.value=0; + %%% other parameters + nose_in_center.value=1.3; + antibias_type.value='Quadrant antibias'; + end + %%% algorithm: wait for good endpoints + if(n_done_trials>150 && value(nValid)>150 && value(dir_correct)>0.68 ... + && value(freq_correct)>0.68 ... + && value(left_correct)>0.68 && value(right_correct)>0.68 ... + && value(correct_coherent)>0.68 && value(correct_incoherent)>0.63 ... + && value(nDays_stage)>2 && value(nTrials_stage)>150) + training_stage.value='Stage 8b'; + nTrials_stage.value= 0; + nDays_stage.value= 1; + end + + + case 'Stage 8b', + stage_explanation.value=sprintf('incongruent 2b (4; 1,2,3,4)'); + %%% updated only on the first trial + if(value(nTrials_stage)==0) + %%% task parameters + ThisTask.value='Direction'; + randomize_first_task.value=0; + task_switch_auto.value=1; + task_switch_min_perf.value=0.7; + %%% direction task parameters + stimulus_mixing_dir.value=1; + gamma_dir_values_dir.value=4; + gamma_freq_values_dir.value=[1 2 3 4]; + durations_dir.value=1.3; + %%% frequency task parameters + stimulus_mixing_freq.value=1; + gamma_dir_values_freq.value=[1 2 3 4]; + gamma_freq_values_freq.value=4; + durations_freq.value=1.3; + %%% frequency task help parameters + helper_lights_freq.value=0; + error_forgiveness_freq.value=0; + %%% other parameters + nose_in_center.value=1.3; + antibias_type.value='Quadrant antibias'; + end + %%% algorithm: wait for good endpoints + if(n_done_trials>150 && value(nValid)>150 && value(dir_correct)>0.68 ... + && value(freq_correct)>0.68 ... + && value(left_correct)>0.68 && value(right_correct)>0.68 ... + && value(correct_coherent)>0.68 && value(correct_incoherent)>0.63 ... + && value(nDays_stage)>2 && value(nTrials_stage)>150) + training_stage.value='Stage 8'; + nTrials_stage.value= 0; + nDays_stage.value= 1; + end + + + + + + case 'Stage 8', + stage_explanation.value=sprintf('incongruent 2 (4; 1,2.5,4)'); + %%% updated only on the first trial + if(value(nTrials_stage)==0) + %%% task parameters + ThisTask.value='Direction'; + randomize_first_task.value=0; + task_switch_auto.value=1; + task_switch_min_perf.value=0.7; + %%% direction task parameters + stimulus_mixing_dir.value=1; + gamma_dir_values_dir.value=4; + gamma_freq_values_dir.value=[1 2.5 4]; + durations_dir.value=1.3; + %%% frequency task parameters + stimulus_mixing_freq.value=1; + gamma_dir_values_freq.value=[1 2.5 4]; + gamma_freq_values_freq.value=4; + durations_freq.value=1.3; + %%% frequency task help parameters + helper_lights_freq.value=0; + error_forgiveness_freq.value=0; + %%% other parameters + nose_in_center.value=1.3; + antibias_type.value='Quadrant antibias'; + end + %%% algorithm: wait for good endpoints + if(n_done_trials>150 && value(nValid)>150 && value(dir_correct)>0.68 ... + && value(freq_correct)>0.68 ... + && value(left_correct)>0.68 && value(right_correct)>0.68 ... + && value(correct_coherent)>0.68 && value(correct_incoherent)>0.63 ... + && value(nDays_stage)>2 && value(nTrials_stage)>150) + training_stage.value='Stage 9a'; + nTrials_stage.value= 0; + nDays_stage.value= 1; + end + + + case 'Stage 9a', + stage_explanation.value=sprintf('harder 1a (3.5,4; 1,2.5,4)'); + %%% updated only on the first trial + if(value(nTrials_stage)==0) + %%% task parameters + ThisTask.value='Direction'; + randomize_first_task.value=0; + task_switch_auto.value=1; + task_switch_min_perf.value=0.7; + %%% direction task parameters + stimulus_mixing_dir.value=1; + gamma_dir_values_dir.value=[3.5 4]; + gamma_freq_values_dir.value=[1 2.5 4]; + durations_dir.value=1.3; + %%% frequency task parameters + stimulus_mixing_freq.value=1; + gamma_dir_values_freq.value=[1 2.5 4]; + gamma_freq_values_freq.value=[3.5 4]; + durations_freq.value=1.3; + %%% frequency task help parameters + helper_lights_freq.value=0; + error_forgiveness_freq.value=0; + %%% other parameters + nose_in_center.value=1.3; + antibias_type.value='Quadrant antibias'; + end + %%% algorithm: wait for good endpoints + if(n_done_trials>150 && value(nValid)>150 && value(dir_correct)>0.68 ... + && value(freq_correct)>0.68 ... + && value(left_correct)>0.68 && value(right_correct)>0.68 ... + && value(correct_coherent)>0.68 && value(correct_incoherent)>0.63 ... + && value(nDays_stage)>2 && value(nTrials_stage)>150) + training_stage.value='Stage 9b'; + nTrials_stage.value= 0; + nDays_stage.value= 1; + end + + + case 'Stage 9b', + stage_explanation.value=sprintf('harder 1b (3,4; 1,2.5,4)'); + %%% updated only on the first trial + if(value(nTrials_stage)==0) + %%% task parameters + ThisTask.value='Direction'; + randomize_first_task.value=0; + task_switch_auto.value=1; + task_switch_min_perf.value=0.7; + %%% direction task parameters + stimulus_mixing_dir.value=1; + gamma_dir_values_dir.value=[3 4]; + gamma_freq_values_dir.value=[1 2.5 4]; + durations_dir.value=1.3; + %%% frequency task parameters + stimulus_mixing_freq.value=1; + gamma_dir_values_freq.value=[1 2.5 4]; + gamma_freq_values_freq.value=[3 4]; + durations_freq.value=1.3; + %%% frequency task help parameters + helper_lights_freq.value=0; + error_forgiveness_freq.value=0; + %%% other parameters + nose_in_center.value=1.3; + antibias_type.value='Quadrant antibias'; + end + %%% algorithm: wait for good endpoints + if(n_done_trials>150 && value(nValid)>150 && value(dir_correct)>0.68 ... + && value(freq_correct)>0.68 ... + && value(left_correct)>0.68 && value(right_correct)>0.68 ... + && value(correct_coherent)>0.68 && value(correct_incoherent)>0.6 ... + && value(nDays_stage)>2 && value(nTrials_stage)>150) + training_stage.value='Stage 9'; + nTrials_stage.value= 0; + nDays_stage.value= 1; + end + + + + + case 'Stage 9', + stage_explanation.value=sprintf('harder 1 (2.5,4; 1,2.5,4)'); + %%% updated only on the first trial + if(value(nTrials_stage)==0) + %%% task parameters + ThisTask.value='Direction'; + randomize_first_task.value=0; + task_switch_auto.value=1; + task_switch_min_perf.value=0.7; + %%% direction task parameters + stimulus_mixing_dir.value=1; + gamma_dir_values_dir.value=[2.5 4]; + gamma_freq_values_dir.value=[1 2.5 4]; + durations_dir.value=1.3; + %%% frequency task parameters + stimulus_mixing_freq.value=1; + gamma_dir_values_freq.value=[1 2.5 4]; + gamma_freq_values_freq.value=[2.5 4]; + durations_freq.value=1.3; + %%% frequency task help parameters + helper_lights_freq.value=0; + error_forgiveness_freq.value=0; + %%% other parameters + nose_in_center.value=1.3; + antibias_type.value='Quadrant antibias'; + end + %%% algorithm: wait for good endpoints + if(n_done_trials>150 && value(nValid)>150 && value(dir_correct)>0.68 ... + && value(freq_correct)>0.68 ... + && value(left_correct)>0.68 && value(right_correct)>0.68 ... + && value(correct_coherent)>0.68 && value(correct_incoherent)>0.6 ... + && value(nDays_stage)>2 && value(nTrials_stage)>150) + training_stage.value='Stage 10a'; + nTrials_stage.value= 0; + nDays_stage.value= 1; + end + + + + case 'Stage 10a', + stage_explanation.value=sprintf('harder 2a (2,2.5,4; 1,2.5,4)'); + %%% updated only on the first trial + if(value(nTrials_stage)==0) + %%% task parameters + ThisTask.value='Direction'; + randomize_first_task.value=0; + task_switch_auto.value=1; + task_switch_min_perf.value=0.7; + %%% direction task parameters + stimulus_mixing_dir.value=1; + gamma_dir_values_dir.value=[2 2.5 4]; + gamma_freq_values_dir.value=[1 2.5 4]; + durations_dir.value=1.3; + %%% frequency task parameters + stimulus_mixing_freq.value=1; + gamma_dir_values_freq.value=[1 2.5 4]; + gamma_freq_values_freq.value=[2 2.5 4]; + durations_freq.value=1.3; + %%% frequency task help parameters + helper_lights_freq.value=0; + error_forgiveness_freq.value=0; + %%% other parameters + nose_in_center.value=1.3; + antibias_type.value='Quadrant antibias'; + end + %%% algorithm: wait for good endpoints + if(n_done_trials>150 && value(nValid)>150 && value(dir_correct)>0.68 ... + && value(freq_correct)>0.68 ... + && value(left_correct)>0.68 && value(right_correct)>0.68 ... + && value(correct_coherent)>0.68 && value(correct_incoherent)>0.6 ... + && value(nDays_stage)>2 && value(nTrials_stage)>150) + training_stage.value='Stage 10b'; + nTrials_stage.value= 0; + nDays_stage.value= 1; + end + + + case 'Stage 10b', + stage_explanation.value=sprintf('harder 2b (1.5,2.5,4; 1,2.5,4)'); + %%% updated only on the first trial + if(value(nTrials_stage)==0) + %%% task parameters + ThisTask.value='Direction'; + randomize_first_task.value=0; + task_switch_auto.value=1; + task_switch_min_perf.value=0.7; + %%% direction task parameters + stimulus_mixing_dir.value=1; + gamma_dir_values_dir.value=[1.5 2.5 4]; + gamma_freq_values_dir.value=[1 2.5 4]; + durations_dir.value=1.3; + %%% frequency task parameters + stimulus_mixing_freq.value=1; + gamma_dir_values_freq.value=[1 2.5 4]; + gamma_freq_values_freq.value=[1.5 2.5 4]; + durations_freq.value=1.3; + %%% frequency task help parameters + helper_lights_freq.value=0; + error_forgiveness_freq.value=0; + %%% other parameters + nose_in_center.value=1.3; + antibias_type.value='Quadrant antibias'; + end + %%% algorithm: wait for good endpoints + if(n_done_trials>150 && value(nValid)>150 && value(dir_correct)>0.68 ... + && value(freq_correct)>0.68 ... + && value(left_correct)>0.68 && value(right_correct)>0.68 ... + && value(correct_coherent)>0.68 && value(correct_incoherent)>0.6 ... + && value(nDays_stage)>2 && value(nTrials_stage)>150) + training_stage.value='Stage 10'; + nTrials_stage.value= 0; + nDays_stage.value= 1; + end + + + + + + case 'Stage 10', + stage_explanation.value=sprintf('harder 2 (1,2.5,4; 1,2.5,4)'); + %%% updated only on the first trial + if(value(nTrials_stage)==0) + %%% task parameters + ThisTask.value='Direction'; + randomize_first_task.value=0; + task_switch_auto.value=1; + task_switch_min_perf.value=0.7; + %%% direction task parameters + stimulus_mixing_dir.value=1; + gamma_dir_values_dir.value=[1 2.5 4]; + gamma_freq_values_dir.value=[1 2.5 4]; + durations_dir.value=1.3; + %%% frequency task parameters + stimulus_mixing_freq.value=1; + gamma_dir_values_freq.value=[1 2.5 4]; + gamma_freq_values_freq.value=[1 2.5 4]; + durations_freq.value=1.3; + %%% frequency task help parameters + helper_lights_freq.value=0; + error_forgiveness_freq.value=0; + %%% other parameters + nose_in_center.value=1.3; + antibias_type.value='Quadrant antibias'; + end + + case 'Stage grow NIC', + stage_explanation.value=sprintf('progressively grow NIC'); + %%% updated only on the first trial + if(value(nTrials_stage)==0) + %%% task parameters + nose_in_center.value=0.8; + end + %%% algorithm: grow NIC + if(value(nose_in_center)>=1.3) + nose_in_center.value=1.3; + elseif(value(nTrials_stage)>0 && value(nose_in_center)<1.3 && value(was_hit)==1) + nose_in_center.value=value(nose_in_center)+0.05; + end + + end + + + case 'end_session' + + + %%%%%%%%% HERE YOU MIGHT WANT TO IMPLEMENT CHECKS AND SWITCH STAGE + %%%%%%%%% IF NECESSARY!!! + +% +% if(value(stage_switch_auto)==1) +% +% feval(mfilename, obj, 'update_stage'); +% +% end + + + nDays_stage.value = value(nDays_stage) + 1; + + + + + case 'get' + + val=varargin{1}; + + eval(['x=value(' val ');']); + + + + + + + +end + + diff --git a/Protocols/@TaskSwitch4/get_variables_script.m b/Protocols/@TaskSwitch4/get_variables_script.m new file mode 100644 index 00000000..87a86f09 --- /dev/null +++ b/Protocols/@TaskSwitch4/get_variables_script.m @@ -0,0 +1,11 @@ +x = TaskSwitch4; + +% GetSoloFunctionArgs(x) + +hits=StimulusSection(x,'get','hit_history'); +side=StimulusSection(x,'get','side_history'); +task=StimulusSection(x,'get','task_history'); + + +% temp=StimulusSection(x,'get','hit_history') + diff --git a/Protocols/@TaskSwitch4/make_pbup_mixed3.m b/Protocols/@TaskSwitch4/make_pbup_mixed3.m new file mode 100644 index 00000000..141e3263 --- /dev/null +++ b/Protocols/@TaskSwitch4/make_pbup_mixed3.m @@ -0,0 +1,206 @@ +% [snd data] = make_pbup_mixed3(R, gamma_dir, gamma_freq, srate, T, varargin) +% +% Makes Poisson bups +% bup events from the left and right speakers are independent Poisson +% events +% +% ======= +% inputs: +% +% R total rate (in clicks/sec) of bups from both left and right +% speakers (r_L + r_R). +% +% gamma_dir the natural log ratio of right and left rates: log(r_R/r_L) +% +% gamma_freq the natural log ratio of high and low probabilities: log(p_H/p_L) +% +% srate sample rate +% +% T total time (in sec) of Poisson bup trains to be generated +% +% ========= +% varargin: +% +% bup_width +% width of a bup in msec (Default 3) +% +% bup_ramp +% the duration in msec of the upwards and downwards volume ramps +% for individual bups. The bup volume ramps up following a cos^2 +% function over this duration and it ramps down in an inverse +% fashion. +% +% crosstalk_dir +% between 0 and 1, determines volume of left clicks that are +% heard in the right channel, and vice versa. +% +% crosstalk_freq +% between 0 and 1, determines volume of hi clicks that are +% added to low clicks, and vice versa. +% +% vol_hi +% volume multiplier for clicks at high frequency +% +% vol_low +% volume multiplier for clicks at low frequency +% +% ======== +% outputs: +% +% snd a vector representing the sound generated + +% data a struct containing the actual bup times (in sec, centered in +% middle of every bup) in snd. +% data.left and data.right +% + +function [snd data] = make_pbup_mixed3(R, gamma_dir, gamma_freq, srate, T, varargin) + +pairs = {... + 'bup_width', 5; ... + 'bup_ramp', 2; ... + 'crosstalk_dir' 0; ... + 'crosstalk_freq' 0; ... + 'freq_vec', [6500 14200]; ... + 'vol_low', 1; ... + 'vol_hi', 1; ... + }; parseargs(varargin, pairs); + + +% rates of Poisson events on left and right +rrate = R/(exp(-gamma_dir)+1); +lrate = R - rrate; + + + +% rates of Poisson events on left and right +hirate = R/(exp(-gamma_freq)+1); +lorate = R - hirate; + + + +frac_hi=hirate./(hirate+lorate); +frac_lo=1-frac_hi; + + +rhirate=rrate*frac_hi; +rlorate=rrate*frac_lo; + + +lhirate=lrate*frac_hi; +llorate=lrate*frac_lo; + + + + +%t = linspace(0, T, srate*T); +lT = srate*T; %the length of what previously was the t vector + +% times of the bups are Poisson events +tp_rhi = find(rand(1,lT) < rhirate/srate); +tp_rlo = find(rand(1,lT) < rlorate/srate); + +tp_lhi = find(rand(1,lT) < lhirate/srate); +tp_llo = find(rand(1,lT) < llorate/srate); + + +data.right_hi = tp_rhi/srate; +data.right_lo = tp_rlo/srate; +data.left_hi = tp_lhi/srate; +data.left_lo = tp_llo/srate; + + + + + +buph = singlebup_old(srate, 0, 'ntones', 1, 'width', bup_width, 'basefreq', max(freq_vec), 'ramp', bup_ramp); +bupl = singlebup_old(srate, 0, 'ntones', 1, 'width', bup_width, 'basefreq', min(freq_vec), 'ramp', bup_ramp); + + +if(length(buph)/2==round(length(buph)/2)) + buph=[buph 0]; +end +if(length(bupl)/2==round(length(bupl)/2)) + bupl=[bupl 0]; +end + +buph=buph*vol_hi; +bupl=bupl*vol_low; + + + + + + +if crosstalk_freq > 0, % implement crosstalk_freq + + temp_buph = buph + crosstalk_freq*bupl; + temp_bupl = bupl + crosstalk_freq*buph; + + buph=temp_buph/(1+crosstalk_freq); + bupl=temp_bupl/(1+crosstalk_freq); + +end; + + + + + + + + +w = floor(length(buph)/2); + +snd = zeros(2, lT); + + +for i = 1:length(tp_rhi), % place hi-freq right bups + bup=buph; + if tp_rhi(i) > w && tp_rhi(i) < lT-w, + snd(2,tp_rhi(i)-w:tp_rhi(i)+w) = snd(2,tp_rhi(i)-w:tp_rhi(i)+w)+bup; + end; +end; + +for i = 1:length(tp_rlo), % place lo-freq right bups + bup=bupl; + if tp_rlo(i) > w && tp_rlo(i) < lT-w, + snd(2,tp_rlo(i)-w:tp_rlo(i)+w) = snd(2,tp_rlo(i)-w:tp_rlo(i)+w)+bup; + end; +end; + +for i = 1:length(tp_lhi), % place hi-freq left bups + bup=buph; + if tp_lhi(i) > w && tp_lhi(i) < lT-w, + snd(1,tp_lhi(i)-w:tp_lhi(i)+w) = snd(1,tp_lhi(i)-w:tp_lhi(i)+w)+bup; + end; +end; + +for i = 1:length(tp_llo), % place lo-freq left bups + bup=bupl; + if tp_llo(i) > w && tp_llo(i) < lT-w, + snd(1,tp_llo(i)-w:tp_llo(i)+w) = snd(1,tp_llo(i)-w:tp_llo(i)+w)+bup; + end; +end; + + + + +if crosstalk_dir > 0, % implement crosstalk_dir + temp_snd(1,:) = snd(1,:) + crosstalk_dir*snd(2,:); + temp_snd(2,:) = snd(2,:) + crosstalk_dir*snd(1,:); + + % normalize the sound so that the volume (summed across both + % speakers) is the same as the original snd before crosstalk + ftemp_snd = fft(temp_snd,2); + fsnd = fft(snd,2); + Ptemp_snd = ftemp_snd .* conj(ftemp_snd); + Psnd = fsnd .* conj(fsnd); + vol_scaling = sqrt(sum(Psnd(:))/sum(Ptemp_snd(:))); + + snd = real(ifft(ftemp_snd * vol_scaling)); +end; + +snd(snd>1) = 1; +snd(snd<-1) = -1; + + diff --git a/Protocols/@TaskSwitch4/parseargs.m b/Protocols/@TaskSwitch4/parseargs.m new file mode 100644 index 00000000..84bf42d9 --- /dev/null +++ b/Protocols/@TaskSwitch4/parseargs.m @@ -0,0 +1,187 @@ +%parseargs [opts] = parseargs(arguments, pairs, singles, ignore_unknowns) +% +% Variable argument parsing-- supersedes parseargs_example. This +% function is meant to be used in the context of other functions +% which have variable arguments. Typically, the function using +% variable argument parsing would be written with the following +% header: +% +% function myfunction(args, ..., varargin) +% +% and would define the variables "pairs" and "singles" (in a +% format described below), and would then include the line +% +% parseargs(varargin, pairs, singles); +% +% 'pairs' and 'singles' specify how the variable arguments should +% be parsed; their format is decribed below. It is best +% understood by looking at the example at the bottom of these help +% comments. +% +% varargin can be of two forms: +% 1) A cell array where odd entries are variable names and even entries are +% the corresponding values +% 2) A struct where the fieldnames are the variable names and the values of +% the fields are the values (for pairs) or the existence of the field +% triggers acts as a single. +% +% pairs can be of two forms: +% 1) an n x 2 cell array where the first column are the variable names and +% the 2nd column are the default values. +% 2) A struct where the fieldnames are the variable names and the values of +% the fields are the values. +% +% PARSEARGS DOES NOT RETURN ANY VALUES; INSTEAD, IT USES ASSIGNIN +% COMMANDS TO CHANGE OR SET VALUES OF VARIABLES IN THE CALLING +% FUNCTION'S SPACE. +% +% +% +% PARAMETERS: +% ----------- +% +% -arguments The varargin list, I.e. a row cell array. +% +% -pairs A cell array of all those arguments that are +% specified by argument-value pairs. First column +% of this cell array must indicate the variable +% names; the second column must indicate +% correponding default values. +% +% -singles A cell array of all those arguments that are +% specified by a single flag. The first column must +% indicate the flag; the second column must +% indicate the corresponding variable name that +% will be affected in the caller's workspace; the +% third column must indicate the value that that +% variable will take upon appearance of the flag; +% and the fourth column must indicate a default +% value for the variable. +% +% +% Example: +% -------- +% +% In "pairs", the first column defines both the variable name and the +% marker looked for in varargin, and the second column defines that +% variable's default value: +% +% pairs = {'thingy' 20 ; ... +% 'blob' 'that'}; +% +% In "singles", the first column is the flag to be looked for in varargin, +% the second column defines the variable name this flag affects, the third +% column defines the value the variable will take if the flag was found, and +% the last column defines the value the variable takes if the flag was NOT +% found in varargin. +% +% singles = {'no_plot' 'plot_fg' '0' '1'; ... +% {'plot' 'plot_fg' '1' '1'}; +% +% +% Now for the function call from the user function: +% +% parseargs({'blob', 'fuff!', 'no_plot'}, pairs, singles); +% +% This will set, in the caller space, thingy=20, blob='fuff!', and +% plot_fg=0. Since default values are in the second column of "pairs" +% and the fourth column of "singles", and in the call to +% parseargs 'thingy' was not specified, 'thingy' takes on its +% default value of 20. +% +% Note that the arguments to parseargs may be in any order-- the +% only ordering restriction is that whatever immediately follows +% pair names (e.g. 'blob') will be interpreted as the value to be +% assigned to them (e.g. 'blob' takes on the value 'fuff!'); +% +% If you never use singles, you can just call "parseargs(varargin, pairs)" +% without the singles argument. +% + + +function [varargout] = parseargs(arguments, pairs, singles,ignore_unknowns) + +if nargin < 3, singles = {}; end; +if nargin < 4, ignore_unknowns=false; end; + +% This assigns all the default values for pairs. +if isstruct(pairs) + out=pairs; + fn=fieldnames(pairs); + for fx=1:numel(fn) + assignin('caller',fn{fx}, pairs.(fn{fx})); + end + pairs=fn; +else + for i=1:size(pairs,1), + assignin('caller', pairs{i,1}, pairs{i,2}); + end; +end +% This assigns all the default values for singles. +for i=1:size(singles,1), + assignin('caller', singles{i,2}, singles{i,4}); +end; +if isempty(singles), singles = {'', '', [], []}; nosingles=true; else nosingles=false; end; +if isempty(pairs), pairs = {'', []}; nopairs=true; else nopairs=false; end; + + +% Now we assign the value to those passed by arguments. +if numel(arguments)==1 && isstruct(arguments{1}) + arguments=arguments{1}; + fn=fieldnames(arguments); + for arg=1:numel(fn) + switch fn{arg} + case pairs(:,1) + assignin('caller',fn{arg}, arguments.(fn{arg})); + out.(fn{arg})=arguments.(fn{arg}); + case singles(:,1) + u = find(strcmp(fn{arg}, singles(:,1))); + out.(fn{arg})=singles(:,1); + assignin('caller', singles{u,2}, singles{u,3}); + otherwise + if ~ignore_unknowns + fn{arg} + + mname = evalin('caller', 'mfilename'); + error([mname ' : Didn''t understand above parameter']); + end + end + end + +else + arg = 1; while arg <= length(arguments), + + switch arguments{arg}, + + case pairs(:,1), + if arg+1 <= length(arguments) + assignin('caller', arguments{arg}, arguments{arg+1}); + arg = arg+1; + end; + + case singles(:,1), + u = find(strcmp(arguments{arg}, singles(:,1))); + assignin('caller', singles{u,2}, singles{u,3}); + + otherwise + if ~ignore_unknowns + arguments{arg} + mname = evalin('caller', 'mfilename'); + error([mname ' : Didn''t understand above parameter']); + else + if nosingles + arg=arg+1; + elseif nopairs + % don't increment args. + else + error('Cannot use ignore_unknown and a mix of singles and pairs') + end + + end + end; + arg = arg+1; end; +end +if nargout>0 + varargout{1}=out; +end +return; diff --git a/Protocols/@TaskSwitch4/singlebup_old.m b/Protocols/@TaskSwitch4/singlebup_old.m new file mode 100644 index 00000000..049aeab9 --- /dev/null +++ b/Protocols/@TaskSwitch4/singlebup_old.m @@ -0,0 +1,59 @@ +% [snd] = singlebup_old(srate, att, { 'width', 5}, {'ramp', 2}, {'basefreq', 2000}, ... +% {'ntones' 5}, {'PPfilter_fname', ''}); ... + + +function [snd] = singlebup_old(srate, att, varargin) + + pairs = { ... + 'width', 5 ; ... + 'ramp', 2 ; ... + 'basefreq' 2000 ; ... + 'ntones' 5 ; ... + 'PPfilter_fname' '' ; ... + }; parseargs(varargin, pairs); + + + width = width/1000; + ramp = ramp/1000; + + if isempty(PPfilter_fname) + FilterPath=['Protocols' filesep 'PPfilter.mat']; + else + FilterPath = PPfilter_fname; + end; + PP = load(FilterPath); + PP=PP.PP; + + + t = 0:(1/srate):width; + + snd = zeros(size(t)); + for i=1:ntones, + f = basefreq*(2.^(i-1)); + attenuation = att - ppval(PP, log10(f)); + snd = snd + (10.^(-attenuation./20)) .* sin(2*pi*f*t); + end; + + if max(abs(snd)) >= 1, snd = snd/(1.01*max(abs(snd))); end; + + rampedge=MakeEdge(srate, ramp); ramplen = length(rampedge); + snd(1:ramplen) = snd(1:ramplen) .* fliplr(rampedge); + snd(end-ramplen+1:end) = snd(end-ramplen+1:end) .* rampedge; + + return; + + +% ------------------------------------------------------------- +% +% +% +% ------------------------------------------------------------- + + + +function [envelope] = MakeEdge(srate, coslen) + + t = (0:(1/srate):coslen)*pi/(2*coslen); + envelope = (cos(t)).^2; + return; + \ No newline at end of file diff --git a/Protocols/@TaskSwitch4/state_colors.m b/Protocols/@TaskSwitch4/state_colors.m new file mode 100644 index 00000000..4b49bc19 --- /dev/null +++ b/Protocols/@TaskSwitch4/state_colors.m @@ -0,0 +1,26 @@ +function SC = state_colors(obj) %#ok + + +% Colors that the various states take when plotting +SC = struct( ... + 'wait_for_cpoke', [210 105 205]/255, ... % orchid + 'wait_for_cpoke_dir', (255/210)*[210 105 205]/255, ... % orchid + 'wait_for_cpoke_freq', (105/255)*[210 105 205]/255, ... % orchid + 'cpoke', [0.52 0.76 1], ... + 'cpoke_in', [0.52 0.76 1], ... + 'cpoke_out', [0.52 0.76 1], ... + 'wait_for_cout', [0.52 0.6 0.8], ... + 'nic_error_state', [1 0.45 0.45], ... + 'wait_for_spoke', [132 111 255]/255, ... % slate blue + 'hit_state', [50 255 50]/255, ... % green + 'hit_state2', [50 255 50]/255, ... % green + 'error_state', [255 0 0]/255, ... % red + 'wait_state', [255 0 0]/255, ... % red + 'timeout_state', [255 236 139]/255, ... % light goldenrod + 'state_0', [1 1 1 ], ... + 'iti', [0.5 0.5 0.5], ... + 'check_next_trial_ready', [0.7 0.7 0.7]); + + + + From 0ab74061307f18fbf9ae01307b4e9860c3fe03ef Mon Sep 17 00:00:00 2001 From: ArpitAgarwal Date: Tue, 25 Mar 2025 13:48:13 +0000 Subject: [PATCH 027/164] completed the automated training stage file, moved reward water calculation from SMA to ParamsSection --- .../Arpit_CentrePokeTraining.m | 2 +- .../Arpit_CentrePokeTrainingSMA.m | 23 +-- .../@Arpit_CentrePokeTraining/ParamsSection.m | 29 ++-- ...okeTraining_experimenter_ratname_250319c.m | 159 +++++++++++++++--- 4 files changed, 158 insertions(+), 55 deletions(-) diff --git a/Protocols/@Arpit_CentrePokeTraining/Arpit_CentrePokeTraining.m b/Protocols/@Arpit_CentrePokeTraining/Arpit_CentrePokeTraining.m index 22a24378..e862ac49 100644 --- a/Protocols/@Arpit_CentrePokeTraining/Arpit_CentrePokeTraining.m +++ b/Protocols/@Arpit_CentrePokeTraining/Arpit_CentrePokeTraining.m @@ -96,7 +96,7 @@ feval(mfilename, obj, 'change_water_modulation_params'); %AthenaSMA changed to SoundCatSMA (From AthenaDelayComp) - SoloFunctionAddVars('Arpit_CentrePokeTrainingSMA', 'ro_args', ... + SoloFunctionAddVars('ParamsSection', 'ro_args', ... {'maxasymp';'slp';'inflp';'minasymp';'assym'}); [x, y] = WaterValvesSection(obj, 'init', x, y); diff --git a/Protocols/@Arpit_CentrePokeTraining/Arpit_CentrePokeTrainingSMA.m b/Protocols/@Arpit_CentrePokeTraining/Arpit_CentrePokeTrainingSMA.m index ee17beb3..a61cd8f4 100644 --- a/Protocols/@Arpit_CentrePokeTraining/Arpit_CentrePokeTrainingSMA.m +++ b/Protocols/@Arpit_CentrePokeTraining/Arpit_CentrePokeTrainingSMA.m @@ -68,19 +68,7 @@ to_sound_id = SoundManagerSection(obj, 'get_sound_id', 'TimeoutSound'); timeout_snd_duration = SoundManagerSection(obj, 'get_sound_duration', 'TimeoutSound'); - %% Declare variables - % These will get moved to other functions as SoloParamHandles. - - WaterAmount=maxasymp + (minasymp./(1+(n_done_trials/inflp).^slp).^assym); - % WaterValvesSection(obj, 'set_water_amounts', WaterAmount, WaterAmount); - % [LeftWValveTime RightWValveTime] = WaterValvesSection(obj, 'get_water_times'); - WValveTimes = GetValveTimes(WaterAmount, [2 3]); - LeftWValveTime = WValveTimes(1); - RightWValveTime = WValveTimes(2); - [LeftWMult, RightWMult] = ParamsSection(obj, 'get_water_mult'); - LeftWValveTime=LeftWValveTime*LeftWMult; - RightWValveTime=RightWValveTime*RightWMult; - + [LeftWValveTime,RightWValveTime] = ParamsSection(obj, 'get_water_amount'); side = ParamsSection(obj, 'get_current_side'); if side == 'l' @@ -114,7 +102,7 @@ sma = add_scheduled_wave(sma, 'name', 'settling_period', 'preamble', SettlingIn_time); % intial fidgety period without violation - if value(training_stage) == 3 + if CP_duration > (SettlingIn_time + legal_cbreak) sma = add_scheduled_wave(sma, 'name', 'CP_Duration_wave', 'preamble', CP_duration); % total length of centre poke to consider success else sma = add_scheduled_wave(sma, 'name', 'CP_Duration_wave', 'preamble', CP_duration - SettlingIn_time); % total length of centre poke minus the inital fidgety time to consider success @@ -151,6 +139,9 @@ sma = add_state(sma,'name','preclean_up_state','self_timer',0.5,... 'input_to_statechange',{'Tup','check_next_trial_ready'}); + sma = add_state(sma, 'name', 'timeout_state'); + sma = add_state(sma, 'name', 'violation_state'); + % For Training Stage 2 case 2 %% STILL LEARNING THE REWARD SOUND ASSOCIATION -LEFT OR RIGHT LED ON -> POKE -> SOUND+REWARD BUT @@ -182,6 +173,8 @@ sma = add_state(sma,'name','preclean_up_state','self_timer',0.5,... 'input_to_statechange',{'Tup','check_next_trial_ready'}); + sma = add_state(sma, 'name', 'violation_state'); + % Training Stage 3 case 3 % INTRODUCE CENTRE NOSE POKE AND PLAY REWARD SOUND AS GO CUE ONLY @@ -377,7 +370,7 @@ % stages. So we send to dispatcher only those states that are % defined. state_names = get_labels(sma); state_names = state_names(:,1); - prepare_next_trial_states = {'side_led_wait_RewardCollection','hit_state','second_hit_state','drink_state', 'violation_state','timeout_state'}; + prepare_next_trial_states = {'side_led_wait_RewardCollection','hit_state','second_hit_state','drink_state', 'violation_state','timeout_state','preclean_up_state'}; dispatcher('send_assembler', sma, intersect(state_names, prepare_next_trial_states)); diff --git a/Protocols/@Arpit_CentrePokeTraining/ParamsSection.m b/Protocols/@Arpit_CentrePokeTraining/ParamsSection.m index 1100582a..fcf88070 100644 --- a/Protocols/@Arpit_CentrePokeTraining/ParamsSection.m +++ b/Protocols/@Arpit_CentrePokeTraining/ParamsSection.m @@ -15,7 +15,6 @@ next_row(y); NumeditParam(obj, 'LeftProb', 0.5, x, y); next_row(y); - set_callback(LeftProb, {mfilename, 'new_leftprob'}); MenuParam(obj, 'MaxSame', {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, Inf}, Inf, x, y, ... 'TooltipString', sprintf(['\nMaximum number of consecutive trials where correct\n' ... 'response is on the same side. Overrides antibias. Thus, for\n' ... @@ -49,7 +48,6 @@ NumeditParam(obj, 'cp_timeout', 5, x,y, 'TooltipString','Time from trial start for rat to centre poke else timeout'); next_row(y); NumeditParam(obj, 'legal_cbreak', 0.1, x,y, 'TooltipString','Time in sec for which it is ok to be outside the center port before a violation occurs.'); - set_callback(legal_cbreak, {mfilename, 'new_legal_cbreak'}); next_row(y); NumeditParam(obj, 'SettlingIn_time', 0.2, x,y, 'TooltipString','Initial settling period during which there is no violation'); next_row(y); @@ -156,8 +154,7 @@ 'RewardCollection_duration';'training_stage'; ... 'legal_cbreak' ; 'SettlingIn_time'; 'time_go_cue'; ... 'A1_time';'time_bet_aud1_gocue' ; 'PreStim_time'; - 'drink_time';'reward_delay';'left_wtr_mult';... - 'right_wtr_mult';'antibias_wtr_mult';... + 'drink_time';'reward_delay';'antibias_wtr_mult';... 'cp_timeout';'timeout_iti';'violation_iti'}); @@ -170,12 +167,6 @@ %%%%%%%%% Switch b/w Actions within ParamSection %%%%%%%%%%%%% - case 'new_leftprob' - AntibiasSectionAthena(obj, 'update_biashitfrac', value(LeftProb)); - - case 'new_legal_cbreak' - SoftPokeStayInterface(obj, 'set', 'soft_cp', 'Grace', value(legal_cbreak)); - case 'new_CP_duration' if random_PreStim_time == 1 && (value(PreStim_time) < value(PreStim_time_Min) || value(PreStim_time) > value(PreStim_time_Max)) @@ -378,9 +369,7 @@ end - %% - - + % %% Do the anti-bias with changing reward delivery % % reset anti-bias % left_wtr_mult.value=1; @@ -402,9 +391,19 @@ % end % end - + case 'get_water_amount' + + %% Calculate the water amount for each side valve + WaterAmount=maxasymp + (minasymp./(1+(n_done_trials/inflp).^slp).^assym); + % WaterValvesSection(obj, 'set_water_amounts', WaterAmount, WaterAmount); + % [LeftWValveTime RightWValveTime] = WaterValvesSection(obj, 'get_water_times'); + WValveTimes = GetValveTimes(WaterAmount, [2 3]); + LeftWValveTime = WValveTimes(1); + RightWValveTime = WValveTimes(2); + [LeftWMult, RightWMult] = ParamsSection(obj, 'get_water_mult'); + LeftWValveTime=LeftWValveTime*LeftWMult; + RightWValveTime=RightWValveTime*RightWMult; - case 'get_water_mult' %% Modulating Water Time diff --git a/Protocols/@Arpit_CentrePokeTraining/pipeline_Arpit_CentrePokeTraining_experimenter_ratname_250319c.m b/Protocols/@Arpit_CentrePokeTraining/pipeline_Arpit_CentrePokeTraining_experimenter_ratname_250319c.m index 8033483a..162817e5 100644 --- a/Protocols/@Arpit_CentrePokeTraining/pipeline_Arpit_CentrePokeTraining_experimenter_ratname_250319c.m +++ b/Protocols/@Arpit_CentrePokeTraining/pipeline_Arpit_CentrePokeTraining_experimenter_ratname_250319c.m @@ -536,6 +536,20 @@ n_trial_warmup = 20; starting_total_cp = 0.5; warmup_completed = 0; +warm_up_on = 0; +random_prestim = 0; +random_prego = 0; +random_A1 = 0; +prestim_min = 0; +prestim_max = 0; +prestim_time = 0; +prego_min =0; +prego_max = 0; +prego_time = 0; +a1_time = 0; +a1_time_min = 0; +a1_time_max = 0; + % end if stage_algorithm_eval @@ -551,19 +565,37 @@ if stage7_trial_counter < 2000 % the rat has been not been trained for good amount of sessions wait for the user % to play around with the settings - ParamsSection_warmup_on.value = 1; - ParamsSection_random_PreStim_time.value = 1; - ParamsSection_random_prego_time.value = 1; - ParamsSection_random_A1_time.value = 0; - ParamsSection_PreStim_time_Min.value = 0.2; - ParamsSection_PreStim_time_Max.value = 2; - ParamsSection_time_bet_aud1_gocue_Min.value = 0.2; - ParamsSection_time_bet_aud1_gocue_Min.value = 2; - ParamsSection_A1_time.value = 0.4; + warm_up_on = 1; + random_prestim = 1; + random_prego = 1; + random_A1 = 0; + prestim_min = 0.2; + prestim_max = 2; + prestim_time = 0.2; + prego_min = 0.2; + prego_max = 2; + prego_time = 0.2; + a1_time = 0.4; + a1_time_min = 0.4; + a1_time_max = 0.4; +else + warm_up_on = value(ParamsSection_warmup_on); + random_prestim = value(ParamsSection_random_PreStim_time); + random_prego = value(ParamsSection_random_prego_time); + random_A1 = value(ParamsSection_random_A1_time); + prestim_min = value(ParamsSection_PreStim_time_Min); + prestim_max = value(ParamsSection_PreStim_time_Max); + prestim_time = value(ParamsSection_PreStim_time); + prego_min = value(ParamsSection_time_bet_aud1_gocue_Min); + prego_max = value(ParamsSection_time_bet_aud1_gocue_Max); + prego_time = value(ParamsSection_time_bet_aud1_gocue); + a1_time = value(ParamsSection_A1_time); + a1_time_min = value(ParamsSection_A1_time_Min); + a1_time_max = value(ParamsSection_A1_time_Max); end % Warm Up If starting a new session -if ParamsSection_warmup_on == 1 +if warm_up_on == 1 if n_completed_trials == 0 ParamsSection_CP_duration.value = value(ParamsSection_init_CP_duration); warmup_completed = 0; @@ -589,20 +621,10 @@ warmup_completed = 1; end -if warmup_completed == 1 - if value(ParamsSection_random_A1_time) - time_range_A1_time = ParamsSection_A1_time_Min : 0.1 : ParamsSection_A1_time_Max; - ParamsSection_A1_time.value = time_range_A1_time(randi([1, numel(time_range_A1_time)],1,1)); - end - if value(ParamsSection_random_PreStim_time) - time_range_PreStim_time = ParamsSection_PreStim_time_Min : 0.1 : ParamsSection_PreStim_time_Max; - ParamsSection_PreStim_time.value = time_range_PreStim_time(randi([1, numel(time_range_PreStim_time)],1,1)); - end - if value(ParamsSection_random_prego_time) - time_range_prego_time = ParamsSection_time_bet_aud1_gocue_Min : 0.1 : ParamsSection_time_bet_aud1_gocue_Min; - ParamsSection_time_bet_aud1_gocue.value = time_range_prego_time(randi([1, numel(time_range_prego_time)],1,1)); - end -end +[ParamsSection_PreStim_time.value ,ParamsSection_A1_time.value,ParamsSection_time_bet_aud1_gocue.value] = param_time_within_range(warmup_completed,... + cp_length,prestim_min,prestim_max, random_prestim, prestim_time,... + a1_time_min,a1_time_max, random_A1, a1_time,prego_min,prego_max, random_prego, prego_time); + % end if completion_test_eval @@ -636,6 +658,95 @@ % +function [prestim,A1,prego] = param_time_within_range(fixed_length,cp_length,range_min_prestim,range_max_prestim, is_random_prestim, provided_time_prestim,... + range_min_A1,range_max_A1, is_random_A1, provided_time_A1,range_min_prego,range_max_prego, is_random_prego, provided_time_prego) + +if fixed_length == 1 % warm up stage where cp length is increasing +% then calculate the range/typical value + if cp_length <= 0.3 + prestim = 0.1; + A1 = 0.1; + prego = 0.1; + else + range_size = round(0.3 * cp_length,1); + if range_size > 0.4 + step_size = 0.1; + else + step_size = 0.01; + end + + timerange = 0.1:step_size:range_size; + + if is_random_prestim == 1 + prestim = timerange(randi([1, numel(timerange)],1,1)); + else + if provided_time_prestim <= range_size + prestim = provided_time_prestim; + else + prestim = range_size; + end + + end + + if is_random_A1 == 1 + A1 = timerange(randi([1, numel(timerange)],1,1)); + else + if provided_time_A1 <= range_size + A1 = provided_time_A1; + else + A1 = range_size; + end + end + + prego = cp_length - prestim - A1; + + end + +else + + if is_random_prestim == 1 + range_size_prestim = range_max_prestim - range_min_prestim; + if range_size_prestim > 0.4 + step_size_prestim = 0.1; + else + step_size_prestim = 0.01; + end + time_range_prestim = range_min_prestim:step_size_prestim:range_max_prestim; + prestim = time_range_prestim(randi([1, numel(time_range_prestim)],1,1)); + else + prestim = provided_time_prestim; + end + + if is_random_A1 == 1 + range_size_A1 = range_max_A1 - range_min_A1; + if range_size_A1 > 0.4 + step_size_A1 = 0.1; + else + step_size_A1 = 0.01; + end + time_range_A1 = range_min_A1:step_size_A1:range_max_A1; + A1 = time_range_A1(randi([1, numel(time_range_A1)],1,1)); + else + A1 = provided_time_A1; + end + + if is_random_prego == 1 + range_size_prego = range_max_prego - range_min_prego; + if range_size_prego > 0.4 + step_size_prego = 0.1; + else + step_size_prego = 0.01; + end + time_range_prego = range_min_prego:step_size_prego:range_max_prego; + prego = time_range_prego(randi([1, numel(time_range_prego)],1,1)); + else + prego = provided_time_prego; + end + +end +end + + % %% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% From 0d43e8e8aabf3bbca33bbe5f9276583c5f5e35c8 Mon Sep 17 00:00:00 2001 From: viktorpm Date: Tue, 25 Mar 2025 14:03:30 +0000 Subject: [PATCH 028/164] changed ParamsSection to avoid error --- Protocols/@Arpit_CentrePokeTraining/ParamsSection.m | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Protocols/@Arpit_CentrePokeTraining/ParamsSection.m b/Protocols/@Arpit_CentrePokeTraining/ParamsSection.m index fcf88070..c125baff 100644 --- a/Protocols/@Arpit_CentrePokeTraining/ParamsSection.m +++ b/Protocols/@Arpit_CentrePokeTraining/ParamsSection.m @@ -401,8 +401,8 @@ LeftWValveTime = WValveTimes(1); RightWValveTime = WValveTimes(2); [LeftWMult, RightWMult] = ParamsSection(obj, 'get_water_mult'); - LeftWValveTime=LeftWValveTime*LeftWMult; - RightWValveTime=RightWValveTime*RightWMult; + x=LeftWValveTime*LeftWMult; + y=RightWValveTime*RightWMult; case 'get_water_mult' From 23b48e22ace9ca932fa3f000a567cde62739ebe6 Mon Sep 17 00:00:00 2001 From: ArpitAgarwal Date: Tue, 25 Mar 2025 14:16:15 +0000 Subject: [PATCH 029/164] removed another error preventing running of State Machine in ParamsSection --- Protocols/@Arpit_CentrePokeTraining/ParamsSection.m | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Protocols/@Arpit_CentrePokeTraining/ParamsSection.m b/Protocols/@Arpit_CentrePokeTraining/ParamsSection.m index c125baff..43b13d19 100644 --- a/Protocols/@Arpit_CentrePokeTraining/ParamsSection.m +++ b/Protocols/@Arpit_CentrePokeTraining/ParamsSection.m @@ -117,8 +117,8 @@ next_column(x); y=5; - MenuParam(obj, 'training_stage', {'Stage 1'; 'Stage 2'; 'Stage 3';... - 'Stage 4'; 'Stage 5'; 'Stage 6'; 'Stage 7'}, 1, x, y, ... + MenuParam(obj, 'training_stage', {'1'; '2'; '3';... + '4'; '5'; '6'; '7'}, 1, x, y, ... 'label', 'Active Stage', 'TooltipString', 'the current training stage'); % NumeditParam(obj,'training_stage',1,x,y,'label','Training Stage'); set_callback(training_stage, {mfilename, 'Changed_Training_Stage'}); From d0952c4fccc4ac7ff4fc3221dab24c6f0fcae078 Mon Sep 17 00:00:00 2001 From: ArpitAgarwal Date: Tue, 25 Mar 2025 15:28:32 +0000 Subject: [PATCH 030/164] working around error during testing --- .../@Arpit_CentrePokeTraining/ParamsSection.m | 75 ++++++++++--------- .../SessionPerformanceSection.m | 4 +- 2 files changed, 41 insertions(+), 38 deletions(-) diff --git a/Protocols/@Arpit_CentrePokeTraining/ParamsSection.m b/Protocols/@Arpit_CentrePokeTraining/ParamsSection.m index 43b13d19..23095f92 100644 --- a/Protocols/@Arpit_CentrePokeTraining/ParamsSection.m +++ b/Protocols/@Arpit_CentrePokeTraining/ParamsSection.m @@ -187,11 +187,11 @@ time_bet_aud1_gocue.value = value(time_bet_aud1_gocue); end - CP_duration.value= SettlingIn_time + PreStim_time + A1_time + time_bet_aud1_gocue; - Total_CP_duration.value = CP_duration + time_go_cue; %#ok<*NASGU> + CP_duration.value= value(SettlingIn_time) + value(PreStim_time) + value(A1_time) + value(time_bet_aud1_gocue); + Total_CP_duration.value = value(CP_duration) + value(time_go_cue); %#ok<*NASGU> case 'new_time_go_cue' - Total_CP_duration.value = CP_duration + time_go_cue; + Total_CP_duration.value = value(CP_duration) + value(time_go_cue); case 'Changed_Training_Stage' @@ -203,7 +203,7 @@ else enable(training_stage); % user can change the training stages - % trials_in_stage.value=0; + SessionDefinition_CURRENT_ACTIVE_STAGE = value(training_stage); switch value(training_stage) @@ -222,12 +222,12 @@ disable(A1_time); enable(time_bet_aud1_gocue); - if n_done_trials <1 && warmup_on ==1 - CP_duration.value=value(init_CP_duration); + if n_done_trials <1 && value(warmup_on) ==1 + CP_duration.value = value(init_CP_duration); else - CP_duration.value=CP_duration; + CP_duration.value = value(CP_duration); end - Total_CP_duration.value = CP_duration + time_go_cue; %#ok<*NASGU> + Total_CP_duration.value = value(CP_duration) + value(time_go_cue); %#ok<*NASGU> case {5,6,7} % @@ -237,26 +237,26 @@ enable(time_bet_aud1_gocue); if random_prego_time == 1 - time_range_go_cue = time_bet_aud1_gocue_Min:0.01:time_bet_aud1_gocue_Max; - time_bet_aud1_gocue = time_range_go_cue(randi([1, numel(time_range_go_cue)],1,1)); + time_range_go_cue = value(time_bet_aud1_gocue_Min):0.01:value(time_bet_aud1_gocue_Max); + time_bet_aud1_gocue.value = time_range_go_cue(randi([1, numel(time_range_go_cue)],1,1)); end if random_A1_time == 1 - time_range_A1_time = A1_time_Min: 0.01 : A1_time_Max; - A1_time = time_range_A1_time(randi([1, numel(time_range_A1_time)],1,1)); + time_range_A1_time = value(A1_time_Min): 0.01 : value(A1_time_Max); + A1_time.value = time_range_A1_time(randi([1, numel(time_range_A1_time)],1,1)); end if random_PreStim_time == 1 - time_range_PreStim_time = PreStim_time_Min : 0.01 : PreStim_time_Max; - PreStim_time = time_range_PreStim_time(randi([1, numel(time_range_PreStim_time)],1,1)); + time_range_PreStim_time = value(PreStim_time_Min) : 0.01 : value(PreStim_time_Max); + PreStim_time.value = time_range_PreStim_time(randi([1, numel(time_range_PreStim_time)],1,1)); end if n_done_trials <1 && warmup_on ==1 CP_duration.value = value(init_CP_duration); else - CP_duration.value = SettlingIn_time + A1_time + PreStim_time + time_bet_aud1_gocue; + CP_duration.value = value(SettlingIn_time) + value(A1_time) + value(PreStim_time) + value(time_bet_aud1_gocue); end - Total_CP_duration.value = CP_duration + time_go_cue; %#ok<*NASGU> + Total_CP_duration.value = value(CP_duration) + value(time_go_cue); %#ok<*NASGU> end end @@ -282,11 +282,11 @@ enable(time_bet_aud1_gocue); if n_done_trials <1 && warmup_on ==1 - CP_duration.value=value(init_CP_duration); + CP_duration.value = value(init_CP_duration); else - CP_duration.value=CP_duration; + CP_duration.value = value(CP_duration); end - Total_CP_duration.value = CP_duration + time_go_cue; %#ok<*NASGU> + Total_CP_duration.value = value(CP_duration) + value(time_go_cue); %#ok<*NASGU> case {5,6,7} % @@ -297,23 +297,26 @@ enable(time_bet_aud1_gocue); if random_prego_time == 1 - time_bet_aud1_gocue = randi([time_bet_aud1_gocue_Min, time_bet_aud1_gocue_Max],1,1); - end - - if random_A1_time == 1 - A1_time = randi([A1_time_Min, A1_time_Max],1,1); - end - - if random_PreStim_time == 1 - PreStim_time = randi([PreStim_time_Min, PreStim_time_Max],1,1); - end - - if n_done_trials <1 && warmup_on ==1 - CP_duration.value=value(init_CP_duration); - else - CP_duration.value=SettlingIn_time + A1_time + PreStim_time + time_bet_aud1_gocue; - end - Total_CP_duration.value = CP_duration + time_go_cue; %#ok<*NASGU> + time_range_go_cue = value(time_bet_aud1_gocue_Min):0.01:value(time_bet_aud1_gocue_Max); + time_bet_aud1_gocue.value = time_range_go_cue(randi([1, numel(time_range_go_cue)],1,1)); + end + + if random_A1_time == 1 + time_range_A1_time = value(A1_time_Min): 0.01 : value(A1_time_Max); + A1_time.value = time_range_A1_time(randi([1, numel(time_range_A1_time)],1,1)); + end + + if random_PreStim_time == 1 + time_range_PreStim_time = value(PreStim_time_Min) : 0.01 : value(PreStim_time_Max); + PreStim_time.value = time_range_PreStim_time(randi([1, numel(time_range_PreStim_time)],1,1)); + end + + if n_done_trials <1 && warmup_on ==1 + CP_duration.value = value(init_CP_duration); + else + CP_duration.value = value(SettlingIn_time) + value(A1_time) + value(PreStim_time) + value(time_bet_aud1_gocue); + end + Total_CP_duration.value = value(CP_duration) + value(time_go_cue); %#ok<*NASGU> end diff --git a/Protocols/@Arpit_CentrePokeTraining/SessionPerformanceSection.m b/Protocols/@Arpit_CentrePokeTraining/SessionPerformanceSection.m index 1e66df8c..c7cf1c75 100644 --- a/Protocols/@Arpit_CentrePokeTraining/SessionPerformanceSection.m +++ b/Protocols/@Arpit_CentrePokeTraining/SessionPerformanceSection.m @@ -103,7 +103,7 @@ violation_recent.value = nan; violation_stage.value = nan; - if n_completed_trails >= 20 + if n_completed_trials >= 20 timeout_recent.value = numel(find(timeout_history(end-19:end)))/20; else timeout_recent.value = nan; @@ -120,7 +120,7 @@ timeout_rate.value = numel(find(timeout_history))/n_completed_trials; end - if n_completed_trails >= 20 + if n_completed_trials >= 20 timeout_recent.value = numel(find(timeout_history(end-19:end)))/20; violation_recent.value = numel(find(violation_history(end-19:end)))/20; else From 8e9d6060a5bc18a5e50232141255ee6e27393c60 Mon Sep 17 00:00:00 2001 From: ArpitAgarwal Date: Tue, 25 Mar 2025 17:08:08 +0000 Subject: [PATCH 031/164] working around error during testing --- .../@Arpit_CentrePokeTraining/ParamsSection.m | 6 +- .../SessionPerformanceSection.asv | 177 ++++ .../SessionPerformanceSection.m | 19 +- ...eTraining_experimenter_ratname_250319c.asv | 785 ++++++++++++++++++ ...okeTraining_experimenter_ratname_250319c.m | 60 +- 5 files changed, 1024 insertions(+), 23 deletions(-) create mode 100644 Protocols/@Arpit_CentrePokeTraining/SessionPerformanceSection.asv create mode 100644 Protocols/@Arpit_CentrePokeTraining/pipeline_Arpit_CentrePokeTraining_experimenter_ratname_250319c.asv diff --git a/Protocols/@Arpit_CentrePokeTraining/ParamsSection.m b/Protocols/@Arpit_CentrePokeTraining/ParamsSection.m index 23095f92..c1bb872f 100644 --- a/Protocols/@Arpit_CentrePokeTraining/ParamsSection.m +++ b/Protocols/@Arpit_CentrePokeTraining/ParamsSection.m @@ -123,7 +123,7 @@ % NumeditParam(obj,'training_stage',1,x,y,'label','Training Stage'); set_callback(training_stage, {mfilename, 'Changed_Training_Stage'}); next_row(y); - ToggleParam(obj,'use_training',0,x,y,'OnString','Using Autotrain','OffString','Manual Settings'); + ToggleParam(obj,'use_auto_train',1,x,y,'OnString','Using Autotrain','OffString','Manual Settings'); set_callback(use_training, {mfilename, 'Changed_Training_Stage'}); next_row(y); @@ -195,7 +195,7 @@ case 'Changed_Training_Stage' - if value(use_training) == 1 + if value(use_auto_train) == 1 disable(training_stage); % user cannot change the training stages disable(PreStim_time); @@ -203,7 +203,7 @@ else enable(training_stage); % user can change the training stages - SessionDefinition_CURRENT_ACTIVE_STAGE = value(training_stage); + SessionDefinition(obj, 'jump_to_stage',value(training_stage)); switch value(training_stage) diff --git a/Protocols/@Arpit_CentrePokeTraining/SessionPerformanceSection.asv b/Protocols/@Arpit_CentrePokeTraining/SessionPerformanceSection.asv new file mode 100644 index 00000000..39e56717 --- /dev/null +++ b/Protocols/@Arpit_CentrePokeTraining/SessionPerformanceSection.asv @@ -0,0 +1,177 @@ +% [x, y] = SessionPerformanceSection(obj, action, x,y) +% +% Reports overall performance. Uses training_stage from SideSection. +% +% PARAMETERS: +% ----------- +% +% action One of: +% 'init' To initialise the section and set up the GUI +% for it +% +% 'close' Delete all of this section's GUIs and data +% +% 'reinit' Delete all of this section's GUIs and data, +% and reinit, at the same position on the same +% figure as the original section GUI was placed. +% +% 'evalueta' Look at history and compute hit fraction, etc. +% +% x, y Only relevant to action = 'init'; they indicate the initial +% position to place the GUI at, in the current figure window +% +% RETURNS: +% -------- +% +% perf When action == 'init', returns x and y, pixel positions on +% the current figure, updated after placing of this section's GUI. +% When action == 'evaluate', returns a vector with elements +% [ntrials, violation_rate, left_hit_frac, right_hit_frac, hit_frac] +% +% + +% CDB, 23-March-2013 + +function [x, y] = SessionPerformanceSection(obj, action, x,y) + +GetSoloFunctionArgs(obj); + +switch action + + % ------------------------------------------------------------------ + % INIT + % ------------------------------------------------------------------ + + case 'init' + + SoloParamHandle(obj, 'my_gui_info', 'value', [x y double(gcf)], 'saveable', 0); + + DispParam(obj, 'ntrials', 0, x, y,'label','Session Trials', 'TooltipString', ... + 'trials done in this session'); + next_row(y); + DispParam(obj, 'ntrials_stage', 0, x, y,'label','Stage Trials', 'TooltipString', ... + 'trials completed in this training stage'); + next_row(y); + DispParam(obj, 'ntrials_stage_today', 0, x, y,'label','Stage Trials Today', 'TooltipString', ... + 'trials completed in this training stage'); + next_row(y); + DispParam(obj, 'violation_rate', 0, x, y,'label','Session Violation', 'TooltipString', ... + 'Fraction of trials with a center poke violation in this session'); + next_row(y); + DispParam(obj, 'timeout_rate', 0, x, y,'label','Session Timeout', 'TooltipString', ... + 'Fraction of trials with timeout in this session'); + next_row(y); + DispParam(obj, 'violation_recent', 0, x, y,'label','Recent Violation', 'TooltipString', ... + 'Fraction of trials with a center poke violation in the past 20 trials'); + next_row(y); + DispParam(obj, 'timeout_recent', 0, x, y,'label','Recent Timeout', 'TooltipString', ... + 'Fraction of trials with a center poke violation in the past 20 trials'); + next_row(y); + DispParam(obj, 'violation_stage', 0, x, y,'label','Stage Violation', 'TooltipString', ... + 'Fraction of violations in this training stage'); + next_row(y); + DispParam(obj, 'violation_stage', 0, x, y,'label','Stage Timeout', 'TooltipString', ... + 'Fraction of timeouts in this training stage'); + next_row(y); + SubheaderParam(obj, 'title', 'Overall Performance', x, y); + next_row(y, 1.5); + SoloParamHandle(obj, 'previous_parameters', 'value', []); + + % ------------------------------------------------------------------ + % evaluate + % ------------------------------------------------------------------ + + case 'evaluate' + + switch value(training_stage) + case 1 %% center led on -> poke in the center -> go cue -> reward light and sound + if n_completed_trials > 1 + ntrials.value = n_completed_trials; + violation_rate.value = nan; + timeout_rate.value = nan; + end + violation_recent.value = nan; + timeout_recent.value = nan; + + violation_stage.value = nan; + timeout_stage.value = nan; + + case {2,3} %% center led on -> poke in the center -> go cue -> reward light and sound + if n_completed_trials > 1 + ntrials.value = n_completed_trials; + violation_rate.value = nan; + timeout_rate.value = numel(find(timeout_history))/n_completed_trials; + end + + violation_recent.value = nan; + violation_stage.value = nan; + + if n_completed_trials >= 20 + timeout_recent.value = numel(find(timeout_history(end-19:end)))/20; + else + timeout_recent.value = nan; + end + + timeout_stage.value = nan; + + + case {4,5,6,7} + + if n_completed_trials > 1 + ntrials.value = n_completed_trials; + violation_rate.value = numel(find(violation_history))/n_completed_trials; + timeout_rate.value = numel(find(timeout_history))/n_completed_trials; + end + + if n_completed_trials >= 20 + timeout_recent.value = numel(find(timeout_history(end-19:end)))/20; + violation_recent.value = numel(find(violation_history(end-19:end)))/20; + else + timeout_recent.value = nan; + violation_recent.value = nan; + end + + + end + + if nargout > 0 + x = [n_completed_trials, value(ntrials_stage), value(violation_rate), value(timeout_rate), value(violation_recent), ... + value(timeout_recent), value(violation_stage), value(timeout_stage)]; + end + + + % ------------------------------------------------------------------ + % close + % ------------------------------------------------------------------ + + case 'close' + % Delete all SoloParamHandles who belong to this object and whose + % fullname starts with the name of this mfile: + delete_sphandle('owner', ['^@' class(obj) '$'], ... + 'fullname', ['^' mfilename]); + + + % ------------------------------------------------------------------ + % reinit + % ------------------------------------------------------------------ + + case 'reinit' + currfig = double(gcf); + + % Get the original GUI position and figure: + x = my_gui_info(1); y = my_gui_info(2); figure(my_gui_info(3)); + + % Delete all SoloParamHandles who belong to this object and whose + % fullname starts with the name of this mfile: + delete_sphandle('owner', ['^@' class(obj) '$'], ... + 'fullname', ['^' mfilename]); + + % Reinitialise at the original GUI position and figure: + [x, y] = feval(mfilename, obj, 'init', x, y); + + % Restore the current figure: + figure(currfig); + +end + + diff --git a/Protocols/@Arpit_CentrePokeTraining/SessionPerformanceSection.m b/Protocols/@Arpit_CentrePokeTraining/SessionPerformanceSection.m index c7cf1c75..39e56717 100644 --- a/Protocols/@Arpit_CentrePokeTraining/SessionPerformanceSection.m +++ b/Protocols/@Arpit_CentrePokeTraining/SessionPerformanceSection.m @@ -46,28 +46,31 @@ SoloParamHandle(obj, 'my_gui_info', 'value', [x y double(gcf)], 'saveable', 0); - DispParam(obj, 'ntrials', 0, x, y,'label','Session Trial Counter', 'TooltipString', ... + DispParam(obj, 'ntrials', 0, x, y,'label','Session Trials', 'TooltipString', ... 'trials done in this session'); next_row(y); - DispParam(obj, 'ntrials_stage', 0, x, y,'label','Session Trial Counter', 'TooltipString', ... + DispParam(obj, 'ntrials_stage', 0, x, y,'label','Stage Trials', 'TooltipString', ... 'trials completed in this training stage'); next_row(y); - DispParam(obj, 'violation_rate', 0, x, y,'label','Session Violation Rate', 'TooltipString', ... + DispParam(obj, 'ntrials_stage_today', 0, x, y,'label','Stage Trials Today', 'TooltipString', ... + 'trials completed in this training stage'); + next_row(y); + DispParam(obj, 'violation_rate', 0, x, y,'label','Session Violation', 'TooltipString', ... 'Fraction of trials with a center poke violation in this session'); next_row(y); - DispParam(obj, 'timeout_rate', 0, x, y,'label','Session Timeout Rate', 'TooltipString', ... + DispParam(obj, 'timeout_rate', 0, x, y,'label','Session Timeout', 'TooltipString', ... 'Fraction of trials with timeout in this session'); next_row(y); - DispParam(obj, 'violation_recent', 0, x, y,'label','Recent Violation Rate', 'TooltipString', ... + DispParam(obj, 'violation_recent', 0, x, y,'label','Recent Violation', 'TooltipString', ... 'Fraction of trials with a center poke violation in the past 20 trials'); next_row(y); - DispParam(obj, 'timeout_recent', 0, x, y,'label','Recent Timeout Rate', 'TooltipString', ... + DispParam(obj, 'timeout_recent', 0, x, y,'label','Recent Timeout', 'TooltipString', ... 'Fraction of trials with a center poke violation in the past 20 trials'); next_row(y); - DispParam(obj, 'violation_stage', 0, x, y,'label','Stage Violation Rate', 'TooltipString', ... + DispParam(obj, 'violation_stage', 0, x, y,'label','Stage Violation', 'TooltipString', ... 'Fraction of violations in this training stage'); next_row(y); - DispParam(obj, 'violation_stage', 0, x, y,'label','Stage Timeout Rate', 'TooltipString', ... + DispParam(obj, 'violation_stage', 0, x, y,'label','Stage Timeout', 'TooltipString', ... 'Fraction of timeouts in this training stage'); next_row(y); SubheaderParam(obj, 'title', 'Overall Performance', x, y); diff --git a/Protocols/@Arpit_CentrePokeTraining/pipeline_Arpit_CentrePokeTraining_experimenter_ratname_250319c.asv b/Protocols/@Arpit_CentrePokeTraining/pipeline_Arpit_CentrePokeTraining_experimenter_ratname_250319c.asv new file mode 100644 index 00000000..cad5bb78 --- /dev/null +++ b/Protocols/@Arpit_CentrePokeTraining/pipeline_Arpit_CentrePokeTraining_experimenter_ratname_250319c.asv @@ -0,0 +1,785 @@ +%Training stage file. +%Please use the session automator window exclusively +%to edit this file. + +function varargout = pipeline_Arpit_CentrePokeTraining_experimenter_ratname_250319c(obj, action, varargin) + +GetSoloFunctionArgs('func_owner', ['@' class(obj)], 'func_name', 'SessionModel'); + +pairs = {'helper_vars_eval', true; + 'stage_algorithm_eval', true; + 'completion_test_eval', false; + 'eod_logic_eval', false}; +parseargs(varargin, pairs); + +switch action + + +%% Familiarize with Reward Side Pokes + +% +case 'Familiarize with Reward Side Pokes' + if helper_vars_eval + GetSoloFunctionArgs(obj); + ClearHelperVarsNotOwned(obj); + % + stage1_trial_counter = 0; + stage1_trial_counter_today = 0; + stage1_trial_counter_oppSide = 0; + % + end + if stage_algorithm_eval + GetSoloFunctionArgs(obj); + ClearHelperVarsNotOwned(obj); + % + ParamsSection_MaxSame.value = 4; + callback(ParamsSection_MaxSame); + ParamsSection_training_stage.value = 1; + callback(ParamsSection_training_stage); + stage1_trial_counter = stage1_trial_counter + 1; + stage1_trial_counter_today = stage1_trial_counter_today + 1; + if value(previous_sides(end)) ~= value(ParamsSection_ThisTrial) + stage1_trial_counter_oppSide = stage1_trial_counter_oppSide + 1; + end + % + end +if completion_test_eval +GetSoloFunctionArgs(obj); +ClearHelperVarsNotOwned(obj); +clear('ans'); +% +% only run it if its the start of the day, so number of trials the rat did +% is less +if n_completed_trial < 100 + if stage1_trial_counter > 300 && stage1_trial_counter_oppSide > 150 + sm = value(SessionDefinition_my_session_model); + SessionDefinition_my_session_model.value = jump(sm, 'offset',+1); + end +end +% +if exist('ans', 'var') +varargout{1}=logical(ans); clear('ans'); +else +varargout{1}=false; +end +end +if eod_logic_eval +GetSoloFunctionArgs(obj); +ClearHelperVarsNotOwned(obj); +% +stage1_trial_counter_today = 0; +if stage1_trial_counter > 300 && stage1_trial_counter_oppSide > 150 + sm = value(SessionDefinition_my_session_model); + SessionDefinition_my_session_model.value = jump(sm, 'offset',+1); +end +% +end +% + +%% Timeout Rewarded Side Pokes + +% +case 'Timeout Rewarded Side Pokes' +if helper_vars_eval +GetSoloFunctionArgs(obj); +ClearHelperVarsNotOwned(obj); +% +stage2_trial_counter = 0; +stage2_trial_counter_today = 0; +stage2_trial_counter_oppSide = 0; +opp_side_trials = 0; +stage2_timeout_percent = 0; +max_reward_collection_dur = 15; % this is the max I will allow +min_reward_collection_dur = 5; % this is the max I will allow +% +end +if stage_algorithm_eval +GetSoloFunctionArgs(obj); +ClearHelperVarsNotOwned(obj); +% +ParamsSection_MaxSame.value = 4; +callback(ParamsSection_MaxSame); +ParamsSection_training_stage.value = 2; +callback(ParamsSection_training_stage); +stage2_trial_counter = stage2_trial_counter + 1; +stage2_trial_counter_today = stage2_trial_counter_today + 1; +if value(previous_sides(end)) ~= value(ParamsSection_ThisTrial) + stage2_trial_counter_oppSide = stage2_trial_counter_oppSide + 1; + opp_side_trials = opp_side_trials + 1; +end +% Update the reward collection time based upon behav +if size(timeout_history) > 5 + if all(value(timeout_history(end-1:end))) && opp_side_trials >= 2 + ParamsSection_RewardCollection_duration.value = min([value(ParamsSection_RewardCollection_duration) + 1,... + max_reward_collection_dur]); + callback(ParamsSection_RewardCollection_duration); + opp_side_trials = 0; + end + + if ~any(value(timeout_history(end-1:end))) && opp_side_trials >= 2 + ParamsSection_RewardCollection_duration.value = max([value(ParamsSection_RewardCollection_duration) - 1,... + min_reward_collection_dur]); + callback(ParamsSection_RewardCollection_duration); + opp_side_trials = 0; + end +end + +if size(timeout_history) > 20 + if all(value(timeout_history(end-19:end))) + ParamsSection_RewardCollection_duration.value = 30; + callback(ParamsSection_RewardCollection_duration); + end +end + +stage2_timeout_percent = ((stage2_timeout_percent * stage2_trial_counter) + double(timeout_history(end)))/(stage2_trial_counter + 1); + +% +end +if completion_test_eval +GetSoloFunctionArgs(obj); +ClearHelperVarsNotOwned(obj); +clear('ans'); +% +if n_completed_trial > 50 + if stage2_trial_counter > 1000 && stage2_trial_counter_oppSide > 200 + sm = value(SessionDefinition_my_session_model); + SessionDefinition_my_session_model.value = jump(sm, 'offset',+1); + end +end +% +if exist('ans', 'var') +varargout{1}=logical(ans); clear('ans'); +else +varargout{1}=false; +end +end +if eod_logic_eval +GetSoloFunctionArgs(obj); +ClearHelperVarsNotOwned(obj); +% + +% +end +% + +%% Introduce Centre Poke + +% +case 'Introduce Centre Poke' +if helper_vars_eval +GetSoloFunctionArgs(obj); +ClearHelperVarsNotOwned(obj); +% +% Maximum & Minimum duration of center poke, in secs: +cp_max = value(ParamsSection_SettlingIn_time) + value(ParamsSection_legal_cbreak); +cp_min = value(ParamsSection_init_CP_duration); +% Fractional increment in center poke duration every time there is a non-cp-violation trial: +cp_fraction = 0.001; +% Minimum increment (in secs) in center poke duration every time there is a non-cp-violation trial: +cp_minimum_increment = 0.001; + +stage3_trial_counter = 0; +stage3_trial_counter_today = 0; +stage3_timeout_percent = 0; + +last_session_cp = 0; +% +end +if stage_algorithm_eval +GetSoloFunctionArgs(obj); +ClearHelperVarsNotOwned(obj); +% +ParamsSection_MaxSame.value = Inf; +callback(ParamsSection_MaxSame); +ParamsSection_training_stage.value = 3; +callback(ParamsSection_training_stage); +stage3_trial_counter = stage3_trial_counter + 1; +stage3_trial_counter_today = stage3_trial_counter_today + 1; +stage3_timeout_percent = ((stage3_timeout_percent * stage3_trial_counter) + double(timeout_history(end)))/(stage3_trial_counter + 1); + +% Change the value of CP Duration +if n_completed_trials < 1 + ParamsSection_CP_duration.value = value(ParamsSection_init_CP_duration); +else + if ~timeout_history(end) && value(ParamsSection_CP_duration) < cp_max + increment = value(ParamsSection_CP_duration)*cp_fraction; + if increment < cp_minimum_increment + increment = value(cp_minimum_increment); + end + ParamsSection_CP_duration.value = value(ParamsSection_CP_duration) + increment; + end +end +callback(ParamsSection_CP_duration); +% +end +if completion_test_eval +GetSoloFunctionArgs(obj); +ClearHelperVarsNotOwned(obj); +clear('ans'); +% +if value(ParamsSection_CP_duration) >= cp_max + sm = value(SessionDefinition_my_session_model); + SessionDefinition_my_session_model.value = jump(sm, 'offset',+1); +end +% +if exist('ans', 'var') +varargout{1}=logical(ans); clear('ans'); +else +varargout{1}=false; +end +end +if eod_logic_eval +GetSoloFunctionArgs(obj); +ClearHelperVarsNotOwned(obj); +% +stage3_trial_counter_today = 0; +last_session_cp = value(ParamsSection_CP_duration); +% +end +% + +%% Introduce Violation for Centre Poke + +% +case 'Introduce Violation for Centre Poke' +if helper_vars_eval +GetSoloFunctionArgs(obj); +ClearHelperVarsNotOwned(obj); +% + +% Maximum & Minimum duration of center poke, in secs: +cp_min = value(ParamsSection_SettlingIn_time) + value(ParamsSection_legal_cbreak); +cp_max = 1.5; +% Fractional increment in center poke duration every time there is a non-cp-violation trial: +cp_fraction = 0.001; +% Minimum increment (in secs) in center poke duration every time there is a non-cp-violation trial: +cp_minimum_increment = 0.001; +last_session_cp = 0; +stage4_trial_counter = 0; +stage4_trial_counter_today = 0; +stage4_timeout_percent = 0; +stage4_violation_percent = 0; + +% +end +if stage_algorithm_eval +GetSoloFunctionArgs(obj); +ClearHelperVarsNotOwned(obj); +% +ParamsSection_training_stage.value = 4; +callback(ParamsSection_training_stage); +stage4_trial_counter = stage4_trial_counter + 1; +stage4_trial_counter_today = stage4_trial_counter_today + 1; +stage4_timeout_percent = ((stage4_timeout_percent * stage4_trial_counter) + double(timeout_history(end)))/(stage4_trial_counter + 1); +stage4_violation_percent = ((stage4_violation_percent * stage4_trial_counter) + double(violation_history(end)))/(stage4_trial_counter + 1); + +% Change the value of CP Duration +if n_completed_trials < 1 + ParamsSection_CP_duration.value = value(ParamsSection_init_CP_duration); +else + if ~violation_history(end) && ~timeout_history(end) && value(ParamsSection_CP_duration) < cp_max + increment = value(ParamsSection_CP_duration)*cp_fraction; + if increment < cp_minimum_increment + increment = value(cp_minimum_increment); + end + ParamsSection_CP_duration.value = value(ParamsSection_CP_duration) + increment; + end +end +if value(ParamsSection_CP_duration) > cp_max + ParamsSection_CP_duration.value = cp_max; +end + +callback(ParamsSection_CP_duration); +% +end +if completion_test_eval +GetSoloFunctionArgs(obj); +ClearHelperVarsNotOwned(obj); +clear('ans'); +% +if value(ParamsSection_CP_duration) >= cp_max && n_completed_trials > 100 +if SessionPerformanceSection_violation_recent < 0.1 && SessionPerformanceSection_timeout_recent < 0.1 && stage4_violation_percent < 0.35 + sm = value(SessionDefinition_my_session_model); + SessionDefinition_my_session_model.value = jump(sm, 'offset',+1); + last_session_cp = value(ParamsSection_CP_duration); +end +end +% +if exist('ans', 'var') +varargout{1}=logical(ans); clear('ans'); +else +varargout{1}=false; +end +end +if eod_logic_eval +GetSoloFunctionArgs(obj); +ClearHelperVarsNotOwned(obj); +% +% Store the value of the total cp duration reached: +stage4_trial_counter_today = 0; +last_session_cp = value(ParamsSection_CP_duration); +% +end +% + +%% Introduce Stimuli Sound during Centre Poke + +% +case 'Introduce Stimuli Sound during Centre Poke' +if helper_vars_eval +GetSoloFunctionArgs(obj); +ClearHelperVarsNotOwned(obj); +% +cp_max = 5; +cp_min = 1.5; +% Fractional increment in center poke duration every time there is a non-cp-violation trial: +cp_fraction = 0.002; +% Minimum increment (in secs) in center poke duration every time there is a non-cp-violation trial: +cp_minimum_increment = 0.001; +% cp value for last session +last_session_cp = 0; +% Starting total center poke duration: +starting_total_cp = 0.5; +% number of warm-up trials +n_trial_warmup = 10; +stage5_trial_counter = 0; +stage5_trial_counter_today = 0; +stage5_timeout_percent = 0; +stage5_violation_percent = 0; +% +end +if stage_algorithm_eval +GetSoloFunctionArgs(obj); +ClearHelperVarsNotOwned(obj); +% + +ParamsSection_training_stage.value = 5; +callback(ParamsSection_training_stage); +stage5_trial_counter = stage5_trial_counter + 1; +stage5_trial_counter_today = stage5_trial_counter_today + 1; +stage5_timeout_percent = ((stage5_timeout_percent * stage5_trial_counter) + double(timeout_history(end)))/(stage5_trial_counter + 1); +stage5_violation_percent = ((stage5_violation_percent * stage5_trial_counter) + double(violation_history(end)))/(stage5_trial_counter + 1); + +% Change the value of CP Duration + +% Since starting a new session then do a pre warm up to last saved cp +% duration else continue with learning with increased poke time +if n_completed_trials == 0 + ParamsSection_CP_duration.value = value(ParamsSection_init_CP_duration); +elseif n_completed_trials == 1 + ParamsSection_CP_duration.value = starting_total_cp; +else + if ~violation_history(end) && ~timeout_history(end) + if value(ParamsSection_CP_duration) < max([cp_min,last_session_cp]) % warm up stage + increment = (max([cp_min,last_session_cp]) - value(ParamsSection_CP_duration))/ (n_trial_warmup - 1); + else + if value(ParamsSection_CP_duration) >= last_session_cp && value(ParamsSection_CP_duration) <= cp_max % no warm up stage + increment = value(ParamsSection_CP_duration)*cp_fraction; + if increment < cp_minimum_increment + increment = value(cp_minimum_increment); + end + end + end + + ParamsSection_CP_duration.value = value(ParamsSection_CP_duration) + increment; + + % Check if the values are within the required range + if value(ParamsSection_CP_duration) < starting_total_cp + ParamsSection_CP_duration.value = starting_total_cp; + end + if value(ParamsSection_CP_duration) > cp_max + ParamsSection_CP_duration.value = cp_max; + end + + end +end +callback(ParamsSection_CP_duration); + +if value(ParamsSection_CP_duration) >= 1 + ParamsSection_PreStim_time.value = 0.4; + if value(ParamsSection_CP_duration) < 2 + ParamsSection_A1_time.value = 0.1; + elseif value(ParamsSection_CP_duration) < 2.5 && value(ParamsSection_CP_duration) >= 2 + ParamsSection_A1_time.value = 0.2; + elseif value(ParamsSection_CP_duration) < 3 && value(ParamsSection_CP_duration) >= 2.5 + ParamsSection_A1_time.value = 0.3; + else + ParamsSection_A1_time.value = 0.4; + end +elseif value(ParamsSection_CP_duration) < 1 && value(ParamsSection_CP_duration) >= starting_total_cp + ParamsSection_SettlingIn_time.value = 0.2; + callback(ParamsSection_SettlingIn_time); + ParamsSection_PreStim_time.value = 0.1; + ParamsSection_A1_time.value = 0.1; +end + +if value(ParamsSection_CP_duration) >= starting_total_cp + callback(ParamsSection_PreStim_time); + callback(ParamsSection_A1_time); +end + + +% +end +if completion_test_eval +GetSoloFunctionArgs(obj); +ClearHelperVarsNotOwned(obj); +clear('ans'); +% +if value(ParamsSection_CP_duration) >= cp_max && stage5_trial_counter > 1000 + if SessionPerformanceSection_violation_recent < 0.1 && SessionPerformanceSection_timeout_recent < 0.1 && stage5_violation_percent < 0.3 && n_completed_trials > 100 + sm = value(SessionDefinition_my_session_model); + SessionDefinition_my_session_model.value = jump(sm, 'offset',+1); + last_session_cp = value(ParamsSection_CP_duration); + end +end +% +if exist('ans', 'var') +varargout{1}=logical(ans); clear('ans'); +else +varargout{1}=false; +end +end +if eod_logic_eval +GetSoloFunctionArgs(obj); +ClearHelperVarsNotOwned(obj); +% +stage5_trial_counter_today = 0; +last_session_cp = value(ParamsSection_CP_duration); +% +end +% + +%% Vary Stimuli location during Centre Poke + +% +case 'Vary Stimuli location during Centre Poke' +if helper_vars_eval +GetSoloFunctionArgs(obj); +ClearHelperVarsNotOwned(obj); +% +stage6_trial_counter = 0; +stage6_trial_counter_today = 0; +stage6_timeout_percent = 0; +stage6_violation_percent = 0; +cp_max = 5; +n_trial_warmup = 20; +starting_total_cp = 0.5; +prestim_min = 0.5; +prestim_max = 2; + +% +end +if stage_algorithm_eval +GetSoloFunctionArgs(obj); +ClearHelperVarsNotOwned(obj); +% +ParamsSection_training_stage.value = 6; +callback(ParamsSection_training_stage); +stage6_trial_counter = stage6_trial_counter + 1; +stage6_trial_counter_today = stage6_trial_counter_today + 1; +stage6_timeout_percent = ((stage6_timeout_percent * stage6_trial_counter) + double(timeout_history(end)))/(stage6_trial_counter + 1); +stage6_violation_percent = ((stage6_violation_percent * stage6_trial_counter) + double(violation_history(end)))/(stage6_trial_counter + 1); + +% Warm Up If starting a new session +if n_completed_trials == 0 + ParamsSection_CP_duration.value = value(ParamsSection_init_CP_duration); +elseif n_completed_trials == 1 + ParamsSection_CP_duration.value = starting_total_cp; +else + if value(ParamsSection_CP_duration) < cp_max % warm up stage + if ~violation_history(end) && ~timeout_history(end) + increment = (cp_max - value(ParamsSection_CP_duration))/ (n_trial_warmup - 1); + ParamsSection_CP_duration.value = value(ParamsSection_CP_duration) + increment; + % Check if the values are within the required range + if value(ParamsSection_CP_duration) < starting_total_cp + ParamsSection_CP_duration.value = starting_total_cp; + end + if value(ParamsSection_CP_duration) > cp_max + ParamsSection_CP_duration.value = cp_max; + end + end + end +end + +callback(ParamsSection_CP_duration); + +if value(ParamsSection_CP_duration) < 3 % during the warm up phase + ParamsSection_SettlingIn_time.value = 0.2; + callback(ParamsSection_SettlingIn_time); + ParamsSection_PreStim_time.value = 0.1; + ParamsSection_A1_time.value = 0.1; +else + ParamsSection_A1_time.value = 0.4; % actual training stage + time_range_PreStim_time = prestim_min : 0.01 : prestim_max; + ParamsSection_PreStim_time.value = time_range_PreStim_time(randi([1, numel(time_range_PreStim_time)],1,1)); +end +callback(ParamsSection_A1_time); +callback(ParamsSection_PreStim_time); + +% +end +if completion_test_eval +GetSoloFunctionArgs(obj); +ClearHelperVarsNotOwned(obj); +clear('ans'); +% +if stage6_trial_counter > 1500 + if SessionPerformanceSection_violation_recent < 0.15 && SessionPerformanceSection_timeout_recent < 0.15 && stage6_violation_percent < 0.25 + sm = value(SessionDefinition_my_session_model); + SessionDefinition_my_session_model.value = jump(sm, 'offset',+1); + end +end +% +if exist('ans', 'var') +varargout{1}=logical(ans); clear('ans'); +else +varargout{1}=false; +end +end +if eod_logic_eval +GetSoloFunctionArgs(obj); +ClearHelperVarsNotOwned(obj); +% +stage6_trial_counter_today = 0; +% +end +% + +%% Variable Stimuli Go Cue location during Centre Poke + +% +case 'Variable Stimuli Go Cue location during Centre Poke' +if helper_vars_eval +GetSoloFunctionArgs(obj); +ClearHelperVarsNotOwned(obj); +% +stage7_trial_counter = 0; +stage7_trial_counter_today = 0; +stage7_timeout_percent = 0; +stage7_violation_percent = 0; + +% Variables for warmup stage +cp_max = 5; +n_trial_warmup = 20; +starting_total_cp = 0.5; +warmup_completed = 0; +warm_up_on = 0; +random_prestim = 0; +random_prego = 0; +random_A1 = 0; +prestim_min = 0; +prestim_max = 0; +prestim_time = 0; +prego_min =0; +prego_max = 0; +prego_time = 0; +a1_time = 0; +a1_time_min = 0; +a1_time_max = 0; + +% +end +if stage_algorithm_eval +GetSoloFunctionArgs(obj); +ClearHelperVarsNotOwned(obj); +% +ParamsSection_training_stage.value = 7; +stage7_trial_counter = stage7_trial_counter + 1; +stage7_trial_counter_today = stage7_trial_counter_today + 1; +stage7_timeout_percent = ((stage7_timeout_percent * stage7_trial_counter) + double(timeout_history(end)))/(stage7_trial_counter + 1); +stage7_violation_percent = ((stage7_violation_percent * stage7_trial_counter) + double(violation_history(end)))/(stage7_trial_counter + 1); + +if stage7_trial_counter < 2000 + % the rat has been not been trained for good amount of sessions wait for the user + % to play around with the settings + warm_up_on = 1; + random_prestim = 1; + random_prego = 1; + random_A1 = 0; + prestim_min = 0.2; + prestim_max = 2; + prestim_time = 0.2; + prego_min = 0.2; + prego_max = 2; + prego_time = 0.2; + a1_time = 0.4; + a1_time_min = 0.4; + a1_time_max = 0.4; +else + warm_up_on = value(ParamsSection_warmup_on); + random_prestim = value(ParamsSection_random_PreStim_time); + random_prego = value(ParamsSection_random_prego_time); + random_A1 = value(ParamsSection_random_A1_time); + prestim_min = value(ParamsSection_PreStim_time_Min); + prestim_max = value(ParamsSection_PreStim_time_Max); + prestim_time = value(ParamsSection_PreStim_time); + prego_min = value(ParamsSection_time_bet_aud1_gocue_Min); + prego_max = value(ParamsSection_time_bet_aud1_gocue_Max); + prego_time = value(ParamsSection_time_bet_aud1_gocue); + a1_time = value(ParamsSection_A1_time); + a1_time_min = value(ParamsSection_A1_time_Min); + a1_time_max = value(ParamsSection_A1_time_Max); +end + +% Warm Up If starting a new session +if warm_up_on == 1 + if n_completed_trials == 0 + ParamsSection_CP_duration.value = value(ParamsSection_init_CP_duration); + warmup_completed = 0; + elseif n_completed_trials == 1 + ParamsSection_CP_duration.value = starting_total_cp; + else + if value(ParamsSection_CP_duration) <= cp_max % warm up stage + if ~violation_history(end) && ~timeout_history(end) + increment = (cp_max - value(ParamsSection_CP_duration))/ (n_trial_warmup - 1); + ParamsSection_CP_duration.value = value(ParamsSection_CP_duration) + increment; + % Check if the values are within the required range + if value(ParamsSection_CP_duration) < starting_total_cp + ParamsSection_CP_duration.value = starting_total_cp; + end + if value(ParamsSection_CP_duration) >= cp_max + ParamsSection_CP_duration.value = cp_max; + warmup_completed = 1; + end + end + end + end +else + warmup_completed = 1; +end + +if n_completed_trials >= 1 + cp_length = value(ParamsSection_CP_duration) - value(ParamsSection_Setting) +[ParamsSection_PreStim_time.value ,ParamsSection_A1_time.value,ParamsSection_time_bet_aud1_gocue.value] = param_time_within_range(warmup_completed,... + cp_length,prestim_min,prestim_max, random_prestim, prestim_time,... + a1_time_min,a1_time_max, random_A1, a1_time,prego_min,prego_max, random_prego, prego_time); + +callback(ParamsSection_PreStim_time); +end +callback(ParamsSection_CP_duration); +% +end +if completion_test_eval +GetSoloFunctionArgs(obj); +ClearHelperVarsNotOwned(obj); +clear('ans'); +% + +% +if exist('ans', 'var') +varargout{1}=logical(ans); clear('ans'); +else +varargout{1}=false; +end +end +if eod_logic_eval +GetSoloFunctionArgs(obj); +ClearHelperVarsNotOwned(obj); +% +stage7_trial_counter_today = 0; +% +end +% + + +end + +end + +%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +% + +function [prestim,A1,prego] = param_time_within_range(fixed_length,cp_length,range_min_prestim,range_max_prestim, is_random_prestim, provided_time_prestim,... + range_min_A1,range_max_A1, is_random_A1, provided_time_A1,range_min_prego,range_max_prego, is_random_prego, provided_time_prego) + +if fixed_length == 1 % warm up stage where cp length is increasing +% then calculate the range/typical value + if cp_length <= 0.3 + prestim = 0.1; + A1 = 0.1; + prego = 0.1; + else + range_size = round(0.3 * cp_length,1); + if range_size > 0.4 + step_size = 0.1; + else + step_size = 0.01; + end + + timerange = 0.1:step_size:range_size; + + if is_random_prestim == 1 + prestim = timerange(randi([1, numel(timerange)],1,1)); + else + if provided_time_prestim <= range_size + prestim = provided_time_prestim; + else + prestim = range_size; + end + + end + + if is_random_A1 == 1 + A1 = timerange(randi([1, numel(timerange)],1,1)); + else + if provided_time_A1 <= range_size + A1 = provided_time_A1; + else + A1 = range_size; + end + end + + prego = cp_length - prestim - A1; + + end + +else + + if is_random_prestim == 1 + range_size_prestim = range_max_prestim - range_min_prestim; + if range_size_prestim > 0.4 + step_size_prestim = 0.1; + else + step_size_prestim = 0.01; + end + time_range_prestim = range_min_prestim:step_size_prestim:range_max_prestim; + prestim = time_range_prestim(randi([1, numel(time_range_prestim)],1,1)); + else + prestim = provided_time_prestim; + end + + if is_random_A1 == 1 + range_size_A1 = range_max_A1 - range_min_A1; + if range_size_A1 > 0.4 + step_size_A1 = 0.1; + else + step_size_A1 = 0.01; + end + time_range_A1 = range_min_A1:step_size_A1:range_max_A1; + A1 = time_range_A1(randi([1, numel(time_range_A1)],1,1)); + else + A1 = provided_time_A1; + end + + if is_random_prego == 1 + range_size_prego = range_max_prego - range_min_prego; + if range_size_prego > 0.4 + step_size_prego = 0.1; + else + step_size_prego = 0.01; + end + time_range_prego = range_min_prego:step_size_prego:range_max_prego; + prego = time_range_prego(randi([1, numel(time_range_prego)],1,1)); + else + prego = provided_time_prego; + end + +end +end + + +% + +%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% diff --git a/Protocols/@Arpit_CentrePokeTraining/pipeline_Arpit_CentrePokeTraining_experimenter_ratname_250319c.m b/Protocols/@Arpit_CentrePokeTraining/pipeline_Arpit_CentrePokeTraining_experimenter_ratname_250319c.m index 162817e5..6bc87e46 100644 --- a/Protocols/@Arpit_CentrePokeTraining/pipeline_Arpit_CentrePokeTraining_experimenter_ratname_250319c.m +++ b/Protocols/@Arpit_CentrePokeTraining/pipeline_Arpit_CentrePokeTraining_experimenter_ratname_250319c.m @@ -33,7 +33,9 @@ ClearHelperVarsNotOwned(obj); % ParamsSection_MaxSame.value = 4; + callback(ParamsSection_MaxSame); ParamsSection_training_stage.value = 1; + callback(ParamsSection_training_stage); stage1_trial_counter = stage1_trial_counter + 1; stage1_trial_counter_today = stage1_trial_counter_today + 1; if value(previous_sides(end)) ~= value(ParamsSection_ThisTrial) @@ -82,13 +84,13 @@ GetSoloFunctionArgs(obj); ClearHelperVarsNotOwned(obj); % - stage2_trial_counter = 0; - stage2_trial_counter_today = 0; - stage2_trial_counter_oppSide = 0; - opp_side_trials = 0; - stage2_timeout_percent = 0; - max_reward_collection_dur = 15; % this is the max I will allow - min_reward_collection_dur = 5; % this is the max I will allow +stage2_trial_counter = 0; +stage2_trial_counter_today = 0; +stage2_trial_counter_oppSide = 0; +opp_side_trials = 0; +stage2_timeout_percent = 0; +max_reward_collection_dur = 15; % this is the max I will allow +min_reward_collection_dur = 5; % this is the max I will allow % end if stage_algorithm_eval @@ -96,7 +98,9 @@ ClearHelperVarsNotOwned(obj); % ParamsSection_MaxSame.value = 4; +callback(ParamsSection_MaxSame); ParamsSection_training_stage.value = 2; +callback(ParamsSection_training_stage); stage2_trial_counter = stage2_trial_counter + 1; stage2_trial_counter_today = stage2_trial_counter_today + 1; if value(previous_sides(end)) ~= value(ParamsSection_ThisTrial) @@ -108,12 +112,14 @@ if all(value(timeout_history(end-1:end))) && opp_side_trials >= 2 ParamsSection_RewardCollection_duration.value = min([value(ParamsSection_RewardCollection_duration) + 1,... max_reward_collection_dur]); + callback(ParamsSection_RewardCollection_duration); opp_side_trials = 0; end if ~any(value(timeout_history(end-1:end))) && opp_side_trials >= 2 ParamsSection_RewardCollection_duration.value = max([value(ParamsSection_RewardCollection_duration) - 1,... min_reward_collection_dur]); + callback(ParamsSection_RewardCollection_duration); opp_side_trials = 0; end end @@ -121,6 +127,7 @@ if size(timeout_history) > 20 if all(value(timeout_history(end-19:end))) ParamsSection_RewardCollection_duration.value = 30; + callback(ParamsSection_RewardCollection_duration); end end @@ -182,8 +189,10 @@ GetSoloFunctionArgs(obj); ClearHelperVarsNotOwned(obj); % - +ParamsSection_MaxSame.value = Inf; +callback(ParamsSection_MaxSame); ParamsSection_training_stage.value = 3; +callback(ParamsSection_training_stage); stage3_trial_counter = stage3_trial_counter + 1; stage3_trial_counter_today = stage3_trial_counter_today + 1; stage3_timeout_percent = ((stage3_timeout_percent * stage3_trial_counter) + double(timeout_history(end)))/(stage3_trial_counter + 1); @@ -200,6 +209,7 @@ ParamsSection_CP_duration.value = value(ParamsSection_CP_duration) + increment; end end +callback(ParamsSection_CP_duration); % end if completion_test_eval @@ -257,6 +267,7 @@ ClearHelperVarsNotOwned(obj); % ParamsSection_training_stage.value = 4; +callback(ParamsSection_training_stage); stage4_trial_counter = stage4_trial_counter + 1; stage4_trial_counter_today = stage4_trial_counter_today + 1; stage4_timeout_percent = ((stage4_timeout_percent * stage4_trial_counter) + double(timeout_history(end)))/(stage4_trial_counter + 1); @@ -277,6 +288,8 @@ if value(ParamsSection_CP_duration) > cp_max ParamsSection_CP_duration.value = cp_max; end + +callback(ParamsSection_CP_duration); % end if completion_test_eval @@ -341,6 +354,7 @@ % ParamsSection_training_stage.value = 5; +callback(ParamsSection_training_stage); stage5_trial_counter = stage5_trial_counter + 1; stage5_trial_counter_today = stage5_trial_counter_today + 1; stage5_timeout_percent = ((stage5_timeout_percent * stage5_trial_counter) + double(timeout_history(end)))/(stage5_trial_counter + 1); @@ -379,9 +393,10 @@ end end +callback(ParamsSection_CP_duration); if value(ParamsSection_CP_duration) >= 1 - ParamsSection_PreStim_time.value = 0.4; + ParamsSection_PreStim_time.value = 0.4; if value(ParamsSection_CP_duration) < 2 ParamsSection_A1_time.value = 0.1; elseif value(ParamsSection_CP_duration) < 2.5 && value(ParamsSection_CP_duration) >= 2 @@ -393,10 +408,17 @@ end elseif value(ParamsSection_CP_duration) < 1 && value(ParamsSection_CP_duration) >= starting_total_cp ParamsSection_SettlingIn_time.value = 0.2; + callback(ParamsSection_SettlingIn_time); ParamsSection_PreStim_time.value = 0.1; ParamsSection_A1_time.value = 0.1; end +if value(ParamsSection_CP_duration) >= starting_total_cp + callback(ParamsSection_PreStim_time); + callback(ParamsSection_A1_time); +end + + % end if completion_test_eval @@ -453,6 +475,7 @@ ClearHelperVarsNotOwned(obj); % ParamsSection_training_stage.value = 6; +callback(ParamsSection_training_stage); stage6_trial_counter = stage6_trial_counter + 1; stage6_trial_counter_today = stage6_trial_counter_today + 1; stage6_timeout_percent = ((stage6_timeout_percent * stage6_trial_counter) + double(timeout_history(end)))/(stage6_trial_counter + 1); @@ -479,8 +502,11 @@ end end +callback(ParamsSection_CP_duration); + if value(ParamsSection_CP_duration) < 3 % during the warm up phase ParamsSection_SettlingIn_time.value = 0.2; + callback(ParamsSection_SettlingIn_time); ParamsSection_PreStim_time.value = 0.1; ParamsSection_A1_time.value = 0.1; else @@ -488,6 +514,8 @@ time_range_PreStim_time = prestim_min : 0.01 : prestim_max; ParamsSection_PreStim_time.value = time_range_PreStim_time(randi([1, numel(time_range_PreStim_time)],1,1)); end +callback(ParamsSection_A1_time); +callback(ParamsSection_PreStim_time); % end @@ -621,10 +649,18 @@ warmup_completed = 1; end -[ParamsSection_PreStim_time.value ,ParamsSection_A1_time.value,ParamsSection_time_bet_aud1_gocue.value] = param_time_within_range(warmup_completed,... - cp_length,prestim_min,prestim_max, random_prestim, prestim_time,... - a1_time_min,a1_time_max, random_A1, a1_time,prego_min,prego_max, random_prego, prego_time); +if n_completed_trials >= 1 + cp_length = value(ParamsSection_CP_duration) - value(ParamsSection_SettlingIn_time); + [ParamsSection_PreStim_time.value ,ParamsSection_A1_time.value,ParamsSection_time_bet_aud1_gocue.value] = param_time_within_range(warmup_completed,... + cp_length,prestim_min,prestim_max, random_prestim, prestim_time,... + a1_time_min,a1_time_max, random_A1, a1_time,prego_min,prego_max, random_prego, prego_time); + callback(ParamsSection_PreStim_time); + callback(ParamsSection_A1_time); + callback(ParamsSection_time_bet_aud1_gocue); + ParamsSection_CP_duration = value(ParamsSection_SettlingIn_time) + value(ParamsSection_PreStim_time) + value(ParamsSection_A1_time) + value(ParamsSection_time_bet_aud1_gocue); +end +callback(ParamsSection_CP_duration); % end if completion_test_eval From db0800e9c44e8fe470a05d98c9ea37a2823e50bf Mon Sep 17 00:00:00 2001 From: viktorpm Date: Tue, 25 Mar 2025 17:17:22 +0000 Subject: [PATCH 032/164] corrected errors while testing --- Protocols/@Arpit_CentrePokeTraining/ParamsSection.m | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Protocols/@Arpit_CentrePokeTraining/ParamsSection.m b/Protocols/@Arpit_CentrePokeTraining/ParamsSection.m index c1bb872f..ea90394c 100644 --- a/Protocols/@Arpit_CentrePokeTraining/ParamsSection.m +++ b/Protocols/@Arpit_CentrePokeTraining/ParamsSection.m @@ -122,9 +122,10 @@ 'label', 'Active Stage', 'TooltipString', 'the current training stage'); % NumeditParam(obj,'training_stage',1,x,y,'label','Training Stage'); set_callback(training_stage, {mfilename, 'Changed_Training_Stage'}); + disable(training_stage); next_row(y); ToggleParam(obj,'use_auto_train',1,x,y,'OnString','Using Autotrain','OffString','Manual Settings'); - set_callback(use_training, {mfilename, 'Changed_Training_Stage'}); + set_callback(use_auto_train, {mfilename, 'Changed_Training_Stage'}); next_row(y); NumeditParam(obj, 'ntrial_correct_bias', 0, x, y, ... From 4eb7991ecdc7c412c406e8c605a05978e32c6083 Mon Sep 17 00:00:00 2001 From: ArpitAgarwal Date: Wed, 26 Mar 2025 14:26:40 +0000 Subject: [PATCH 033/164] added user controlled parameters for training stages as well as a new stage for running session with user setting. --- .../Arpit_CentrePokeTraining.m | 1 + .../Arpit_CentrePokeTrainingSMA.m | 2 +- .../@Arpit_CentrePokeTraining/ParamsSection.m | 14 +- .../Training_ParamsSection.m | 139 ++++ ...eTraining_experimenter_ratname_250319c.asv | 785 ------------------ ...okeTraining_experimenter_ratname_250319c.m | 279 +++++-- 6 files changed, 338 insertions(+), 882 deletions(-) create mode 100644 Protocols/@Arpit_CentrePokeTraining/Training_ParamsSection.m delete mode 100644 Protocols/@Arpit_CentrePokeTraining/pipeline_Arpit_CentrePokeTraining_experimenter_ratname_250319c.asv diff --git a/Protocols/@Arpit_CentrePokeTraining/Arpit_CentrePokeTraining.m b/Protocols/@Arpit_CentrePokeTraining/Arpit_CentrePokeTraining.m index e862ac49..a13e10e4 100644 --- a/Protocols/@Arpit_CentrePokeTraining/Arpit_CentrePokeTraining.m +++ b/Protocols/@Arpit_CentrePokeTraining/Arpit_CentrePokeTraining.m @@ -118,6 +118,7 @@ % [x, y] = PunishmentSection(obj, 'init', x, y); %#ok next_column(x); y=5; + [x,y] = Training_ParamsSection(obj, 'init', x, y); [x, y] = SessionPerformanceSection(obj, 'init', x, y); [x, y] = ParamsSection(obj, 'init', x, y); %#ok [x, y] = SoundSection(obj,'init',x,y); diff --git a/Protocols/@Arpit_CentrePokeTraining/Arpit_CentrePokeTrainingSMA.m b/Protocols/@Arpit_CentrePokeTraining/Arpit_CentrePokeTrainingSMA.m index a61cd8f4..cd595beb 100644 --- a/Protocols/@Arpit_CentrePokeTraining/Arpit_CentrePokeTrainingSMA.m +++ b/Protocols/@Arpit_CentrePokeTraining/Arpit_CentrePokeTrainingSMA.m @@ -226,7 +226,7 @@ 'input_to_statechange',{'Tup','check_next_trial_ready'}); - case {4,5,6,7} % STAGE 4 - LEARN TO NOSE POKE BEYOND SETTLING TIME WITH THE INTRODUCTION OF VIOLATION, THIS IS UNTIL CP = 1 SEC + case {4,5,6,7,8} % STAGE 4 - LEARN TO NOSE POKE BEYOND SETTLING TIME WITH THE INTRODUCTION OF VIOLATION, THIS IS UNTIL CP = 1 SEC % STAGE 5 ONWARDS - THE STIMULI IS INTRODUCED FROM THE STAGE 5 ONWARDS sma = add_state(sma,'name','wait_for_cpoke','self_timer',cp_timeout, ... diff --git a/Protocols/@Arpit_CentrePokeTraining/ParamsSection.m b/Protocols/@Arpit_CentrePokeTraining/ParamsSection.m index ea90394c..590fdc0c 100644 --- a/Protocols/@Arpit_CentrePokeTraining/ParamsSection.m +++ b/Protocols/@Arpit_CentrePokeTraining/ParamsSection.m @@ -118,7 +118,7 @@ next_column(x); y=5; MenuParam(obj, 'training_stage', {'1'; '2'; '3';... - '4'; '5'; '6'; '7'}, 1, x, y, ... + '4'; '5'; '6'; '7';'8'}, 1, x, y, ... 'label', 'Active Stage', 'TooltipString', 'the current training stage'); % NumeditParam(obj,'training_stage',1,x,y,'label','Training Stage'); set_callback(training_stage, {mfilename, 'Changed_Training_Stage'}); @@ -160,7 +160,10 @@ SoloFunctionAddVars('SessionPerformanceSection', 'ro_args', ... - {'training_stage'}); + {'training_stage'}); + + SoloFunctionAddVars('SessionPerformanceSection', 'ro_args', ... + {'training_stage'}); SoloParamHandle(obj, 'previous_parameters', 'value', []); @@ -195,7 +198,7 @@ Total_CP_duration.value = value(CP_duration) + value(time_go_cue); case 'Changed_Training_Stage' - + if value(use_auto_train) == 1 disable(training_stage); % user cannot change the training stages @@ -205,6 +208,7 @@ enable(training_stage); % user can change the training stages SessionDefinition(obj, 'jump_to_stage',value(training_stage)); + [x,y] = Training_ParamsSection(obj, 'init', x, y); % update the training params as well switch value(training_stage) @@ -230,7 +234,7 @@ end Total_CP_duration.value = value(CP_duration) + value(time_go_cue); %#ok<*NASGU> - case {5,6,7} % + case {5,6,7,8} % enable(SettlingIn_time); enable(PreStim_time); @@ -289,7 +293,7 @@ end Total_CP_duration.value = value(CP_duration) + value(time_go_cue); %#ok<*NASGU> - case {5,6,7} % + case {5,6,7,8} % time_go_cue.value=0.100; enable(SettlingIn_time); diff --git a/Protocols/@Arpit_CentrePokeTraining/Training_ParamsSection.m b/Protocols/@Arpit_CentrePokeTraining/Training_ParamsSection.m new file mode 100644 index 00000000..623517b1 --- /dev/null +++ b/Protocols/@Arpit_CentrePokeTraining/Training_ParamsSection.m @@ -0,0 +1,139 @@ +function [x, y] = Training_ParamsSection(obj, action, x,y) + +GetSoloFunctionArgs(obj); + +switch action + + % ------------------------------------------------------------------ + % INIT + % ------------------------------------------------------------------ + + case 'init' + + SoloParamHandle(obj, 'my_gui_info', 'value', [x y double(gcf)], 'saveable', 0); + + switch value(training_stage) + + + case 1 + % STAGE RUNNING PARAMETERS + SubheaderParam(obj, 'title', 'Stage Params', x, y); + next_row(y); + + % COMPLETION TEST PARAMETERS + SubheaderParam(obj, 'title', 'Completion Params', x, y); next_row(y); + NumeditParam(obj, 'total_trials', 300, x, y,'label','Trials','TooltipString','total trials in this stage for its completion'); next_row(y); + NumeditParam(obj, 'total_trials_opp', 150, x, y,'label','Trials_Opp','TooltipString','total trials with opposide side option in this stage for its completion'); next_row(y); + + + case 2 + % STAGE RUNNING PARAMETERS + SubheaderParam(obj, 'title', 'Stage Params', x, y); next_row(y); + NumeditParam(obj, 'max_rColl_dur', 15, x, y,'label','T_Max_RCollect','TooltipString','User given max water collect time(code will optimize for value below this)'); next_row(y); + NumeditParam(obj, 'min_rColl_dur', 5, x, y,'label','T_Min_RCollect','TooltipString','User given min water collect time(code will optimize for value above this)'); next_row(y); + + % COMPLETION TEST PARAMETERS + SubheaderParam(obj, 'title', 'Completion Params', x, y); + NumeditParam(obj, 'total_trials', 1000, x, y,'label','Trials','TooltipString','total trials in this stage for its completion'); next_row(y); + NumeditParam(obj, 'total_trials_opp', 400, x, y,'label','Trials_Opp','TooltipString','total trials with opposide side option in this stage for its completion'); next_row(y); + + + case 3 % no completion test required + % STAGE RUNNING PARAMETERS + SubheaderParam(obj, 'title', 'Stage Params', x, y); next_row(y); + DispParam(obj, 'max_CP', 0.3, x, y,'label','CP_Dur_Max','TooltipString','max CP duration being trained in this stage'); next_row(y); + NumeditParam(obj, 'CPfraction_inc', 0.001, x, y,'label','CP_frac_Increase','TooltipString','CP duration is increased by this fraction'); next_row(y); + + case 4 + % STAGE RUNNING PARAMETERS + SubheaderParam(obj, 'title', 'Stage Params', x, y); next_row(y); + NumeditParam(obj, 'max_CP', 1.5, x, y,'label','CP_Dur_Max','TooltipString','max CP duration being trained in this stage'); next_row(y); + NumeditParam(obj, 'CPfraction_inc', 0.001, x, y,'label','CP_frac_Increase','TooltipString','CP duration is increased by this fraction'); next_row(y); + + % COMPLETION TEST PARAMETERS + SubheaderParam(obj, 'title', 'Completion Params', x, y); + NumeditParam(obj, 'recent_violation', 0.15, x, y,'label','Recent_ViolateRate','TooltipString','violation rate for last 20 trials in this stage for its completion'); next_row(y); + NumeditParam(obj, 'recent_timeout', 0.15, x, y,'label','Recent_TimeoutRate','TooltipString','timeout rate for last 20 trials in this stage for its completion'); next_row(y); + NumeditParam(obj, 'stage_violation', 0.35, x, y,'label','Stage_ViolationRate','TooltipString','overall violation rate in this stage for its completion'); next_row(y); + + case 5 + % STAGE RUNNING PARAMETERS + SubheaderParam(obj, 'title', 'Stage Params', x, y); next_row(y); + NumeditParam(obj, 'max_CP', 1.5, x, y,'label','CP_Dur_Max','TooltipString','max CP duration being trained in this stage'); next_row(y); + NumeditParam(obj, 'CPfraction_inc', 0.002, x, y,'label','CP_frac_Increase','TooltipString','CP duration is increased by this fraction'); next_row(y); + NumeditParam(obj, 'min_CP', 1.5, x, y,'label','CP_Dur_Min','TooltipString','min CP duration being trained in this stage'); next_row(y); + NumeditParam(obj, 'starting_CP', 0.3, x, y,'TooltipString','min CP duration (minus the settling-in time) during warm up'); next_row(y); + NumeditParam(obj, 'warm_up_trials', 10, x, y,'label','Warmup Trials','TooltipString','N trials for warmup'); next_row(y); + NumeditParam(obj, 'stim_dur', 0.4, x, y,'label','A1 Dur','TooltipString','This stage A1 duration'); next_row(y); + + % COMPLETION TEST PARAMETERS + SubheaderParam(obj, 'title', 'Completion Params', x, y); + NumeditParam(obj, 'recent_violation', 0.15, x, y,'label','Recent_ViolateRate','TooltipString','violation rate for last 20 trials in this stage for its completion'); next_row(y); + NumeditParam(obj, 'recent_timeout', 0.15, x, y,'label','Recent_TimeoutRate','TooltipString','timeout rate for last 20 trials in this stage for its completion'); next_row(y); + NumeditParam(obj, 'stage_violation', 0.35, x, y,'label','Stage_ViolationRate','TooltipString','overall violation rate in this stage for its completion'); next_row(y); + + case 6 + % STAGE RUNNING PARAMETERS + SubheaderParam(obj, 'title', 'Stage Params', x, y); next_row(y); + NumeditParam(obj, 'max_CP', 5, x, y,'label','CP_Dur_Max','TooltipString','max CP duration being trained in this stage'); next_row(y); + NumeditParam(obj, 'starting_CP', 0.3, x, y,'TooltipString','min CP duration (minus the settling-in time) during warm up'); next_row(y); + NumeditParam(obj, 'warm_up_trials', 20, x, y,'label','Warmup Trials','TooltipString','N trials for warmup'); next_row(y); + NumeditParam(obj, 'max_prestim', 2, x, y,'label','Pre-Stim Max','TooltipString','This stage Max Time, before starting the stimulus'); next_row(y); + NumeditParam(obj, 'min_prestim', 0.2, x, y,'label','Pre-Stim Min','TooltipString','This stage Min Time, before starting the stimulus'); next_row(y); + NumeditParam(obj, 'stim_dur', 0.4, x, y,'label','A1 Dur','TooltipString','This stage A1 duration'); next_row(y); + + % COMPLETION TEST PARAMETERS + SubheaderParam(obj, 'title', 'Completion Params', x, y); + NumeditParam(obj, 'recent_violation', 0.15, x, y,'label','Recent_ViolateRate','TooltipString','violation rate for last 20 trials in this stage for its completion'); next_row(y); + NumeditParam(obj, 'recent_timeout', 0.15, x, y,'label','Recent_TimeoutRate','TooltipString','timeout rate for last 20 trials in this stage for its completion'); next_row(y); + NumeditParam(obj, 'stage_violation', 0.25, x, y,'label','Stage_ViolationRate','TooltipString','overall violation rate in this stage for its completion'); next_row(y); + NumeditParam(obj, 'total_trials', 1500, x, y,'label','Trials','TooltipString','total trials in this stage for its completion'); next_row(y); + + case 7 + % STAGE RUNNING PARAMETERS + SubheaderParam(obj, 'title', 'Stage Params', x, y); next_row(y); + NumeditParam(obj, 'max_CP', 5, x, y,'label','CP_Dur_Max','TooltipString','max CP duration being trained in this stage'); next_row(y); + NumeditParam(obj, 'starting_CP', 0.3, x, y,'TooltipString','min CP duration (minus the settling-in time) during warm up'); next_row(y); + NumeditParam(obj, 'warm_up_trials', 20, x, y,'label','Warmup Trials','TooltipString','N trials for warmup'); next_row(y); + NumeditParam(obj, 'max_prestim', 2, x, y,'label','Pre-Stim Max','TooltipString','This stage Max Time, before starting the stimulus'); next_row(y); + NumeditParam(obj, 'min_prestim', 0.2, x, y,'label','Pre-Stim Min','TooltipString','This stage Min Time, before starting the stimulus'); next_row(y); + NumeditParam(obj, 'max_prego', 2, x, y,'label','Max A1-GoCue time','TooltipString','This stage Max time, between the end of the stimulus and the go cue'); next_row(y); + NumeditParam(obj, 'min_prego', 0.2, x, y,'label','Min A1-GoCue time','TooltipString','This stage Min time, between the end of the stimulus and the go cue'); next_row(y); + NumeditParam(obj, 'stim_dur', 0.4, x, y,'label','A1 Dur','TooltipString','This stage A1 duration'); next_row(y); + + % COMPLETION TEST PARAMETERS + SubheaderParam(obj, 'title', 'Completion Params', x, y); + NumeditParam(obj, 'recent_violation', 0.15, x, y,'label','Recent_ViolateRate','TooltipString','violation rate for last 20 trials in this stage for its completion'); next_row(y); + NumeditParam(obj, 'recent_timeout', 0.15, x, y,'label','Recent_TimeoutRate','TooltipString','timeout rate for last 20 trials in this stage for its completion'); next_row(y); + NumeditParam(obj, 'stage_violation', 0.20, x, y,'label','Stage_ViolationRate','TooltipString','overall violation rate in this stage for its completion'); next_row(y); + NumeditParam(obj, 'total_trials', 2000, x, y,'label','Trials','TooltipString','total trials in this stage for its completion'); next_row(y); + + end + + case 'close' + % Delete all SoloParamHandles who belong to this object and whose + % fullname starts with the name of this mfile: + delete_sphandle('owner', ['^@' class(obj) '$'], ... + 'fullname', ['^' mfilename]); + + case 'reinit' + currfig = double(gcf); + + % Get the original GUI position and figure: + x = my_gui_info(1); y = my_gui_info(2); figure(my_gui_info(3)); + + % Delete all SoloParamHandles who belong to this object and whose + % fullname starts with the name of this mfile: + delete_sphandle('owner', ['^@' class(obj) '$'], ... + 'fullname', ['^' mfilename]); + + % Reinitialise at the original GUI position and figure: + [x, y] = feval(mfilename, obj, 'init', x, y); + + % Restore the current figure: + figure(currfig); + + +end + +end diff --git a/Protocols/@Arpit_CentrePokeTraining/pipeline_Arpit_CentrePokeTraining_experimenter_ratname_250319c.asv b/Protocols/@Arpit_CentrePokeTraining/pipeline_Arpit_CentrePokeTraining_experimenter_ratname_250319c.asv deleted file mode 100644 index cad5bb78..00000000 --- a/Protocols/@Arpit_CentrePokeTraining/pipeline_Arpit_CentrePokeTraining_experimenter_ratname_250319c.asv +++ /dev/null @@ -1,785 +0,0 @@ -%Training stage file. -%Please use the session automator window exclusively -%to edit this file. - -function varargout = pipeline_Arpit_CentrePokeTraining_experimenter_ratname_250319c(obj, action, varargin) - -GetSoloFunctionArgs('func_owner', ['@' class(obj)], 'func_name', 'SessionModel'); - -pairs = {'helper_vars_eval', true; - 'stage_algorithm_eval', true; - 'completion_test_eval', false; - 'eod_logic_eval', false}; -parseargs(varargin, pairs); - -switch action - - -%% Familiarize with Reward Side Pokes - -% -case 'Familiarize with Reward Side Pokes' - if helper_vars_eval - GetSoloFunctionArgs(obj); - ClearHelperVarsNotOwned(obj); - % - stage1_trial_counter = 0; - stage1_trial_counter_today = 0; - stage1_trial_counter_oppSide = 0; - % - end - if stage_algorithm_eval - GetSoloFunctionArgs(obj); - ClearHelperVarsNotOwned(obj); - % - ParamsSection_MaxSame.value = 4; - callback(ParamsSection_MaxSame); - ParamsSection_training_stage.value = 1; - callback(ParamsSection_training_stage); - stage1_trial_counter = stage1_trial_counter + 1; - stage1_trial_counter_today = stage1_trial_counter_today + 1; - if value(previous_sides(end)) ~= value(ParamsSection_ThisTrial) - stage1_trial_counter_oppSide = stage1_trial_counter_oppSide + 1; - end - % - end -if completion_test_eval -GetSoloFunctionArgs(obj); -ClearHelperVarsNotOwned(obj); -clear('ans'); -% -% only run it if its the start of the day, so number of trials the rat did -% is less -if n_completed_trial < 100 - if stage1_trial_counter > 300 && stage1_trial_counter_oppSide > 150 - sm = value(SessionDefinition_my_session_model); - SessionDefinition_my_session_model.value = jump(sm, 'offset',+1); - end -end -% -if exist('ans', 'var') -varargout{1}=logical(ans); clear('ans'); -else -varargout{1}=false; -end -end -if eod_logic_eval -GetSoloFunctionArgs(obj); -ClearHelperVarsNotOwned(obj); -% -stage1_trial_counter_today = 0; -if stage1_trial_counter > 300 && stage1_trial_counter_oppSide > 150 - sm = value(SessionDefinition_my_session_model); - SessionDefinition_my_session_model.value = jump(sm, 'offset',+1); -end -% -end -% - -%% Timeout Rewarded Side Pokes - -% -case 'Timeout Rewarded Side Pokes' -if helper_vars_eval -GetSoloFunctionArgs(obj); -ClearHelperVarsNotOwned(obj); -% -stage2_trial_counter = 0; -stage2_trial_counter_today = 0; -stage2_trial_counter_oppSide = 0; -opp_side_trials = 0; -stage2_timeout_percent = 0; -max_reward_collection_dur = 15; % this is the max I will allow -min_reward_collection_dur = 5; % this is the max I will allow -% -end -if stage_algorithm_eval -GetSoloFunctionArgs(obj); -ClearHelperVarsNotOwned(obj); -% -ParamsSection_MaxSame.value = 4; -callback(ParamsSection_MaxSame); -ParamsSection_training_stage.value = 2; -callback(ParamsSection_training_stage); -stage2_trial_counter = stage2_trial_counter + 1; -stage2_trial_counter_today = stage2_trial_counter_today + 1; -if value(previous_sides(end)) ~= value(ParamsSection_ThisTrial) - stage2_trial_counter_oppSide = stage2_trial_counter_oppSide + 1; - opp_side_trials = opp_side_trials + 1; -end -% Update the reward collection time based upon behav -if size(timeout_history) > 5 - if all(value(timeout_history(end-1:end))) && opp_side_trials >= 2 - ParamsSection_RewardCollection_duration.value = min([value(ParamsSection_RewardCollection_duration) + 1,... - max_reward_collection_dur]); - callback(ParamsSection_RewardCollection_duration); - opp_side_trials = 0; - end - - if ~any(value(timeout_history(end-1:end))) && opp_side_trials >= 2 - ParamsSection_RewardCollection_duration.value = max([value(ParamsSection_RewardCollection_duration) - 1,... - min_reward_collection_dur]); - callback(ParamsSection_RewardCollection_duration); - opp_side_trials = 0; - end -end - -if size(timeout_history) > 20 - if all(value(timeout_history(end-19:end))) - ParamsSection_RewardCollection_duration.value = 30; - callback(ParamsSection_RewardCollection_duration); - end -end - -stage2_timeout_percent = ((stage2_timeout_percent * stage2_trial_counter) + double(timeout_history(end)))/(stage2_trial_counter + 1); - -% -end -if completion_test_eval -GetSoloFunctionArgs(obj); -ClearHelperVarsNotOwned(obj); -clear('ans'); -% -if n_completed_trial > 50 - if stage2_trial_counter > 1000 && stage2_trial_counter_oppSide > 200 - sm = value(SessionDefinition_my_session_model); - SessionDefinition_my_session_model.value = jump(sm, 'offset',+1); - end -end -% -if exist('ans', 'var') -varargout{1}=logical(ans); clear('ans'); -else -varargout{1}=false; -end -end -if eod_logic_eval -GetSoloFunctionArgs(obj); -ClearHelperVarsNotOwned(obj); -% - -% -end -% - -%% Introduce Centre Poke - -% -case 'Introduce Centre Poke' -if helper_vars_eval -GetSoloFunctionArgs(obj); -ClearHelperVarsNotOwned(obj); -% -% Maximum & Minimum duration of center poke, in secs: -cp_max = value(ParamsSection_SettlingIn_time) + value(ParamsSection_legal_cbreak); -cp_min = value(ParamsSection_init_CP_duration); -% Fractional increment in center poke duration every time there is a non-cp-violation trial: -cp_fraction = 0.001; -% Minimum increment (in secs) in center poke duration every time there is a non-cp-violation trial: -cp_minimum_increment = 0.001; - -stage3_trial_counter = 0; -stage3_trial_counter_today = 0; -stage3_timeout_percent = 0; - -last_session_cp = 0; -% -end -if stage_algorithm_eval -GetSoloFunctionArgs(obj); -ClearHelperVarsNotOwned(obj); -% -ParamsSection_MaxSame.value = Inf; -callback(ParamsSection_MaxSame); -ParamsSection_training_stage.value = 3; -callback(ParamsSection_training_stage); -stage3_trial_counter = stage3_trial_counter + 1; -stage3_trial_counter_today = stage3_trial_counter_today + 1; -stage3_timeout_percent = ((stage3_timeout_percent * stage3_trial_counter) + double(timeout_history(end)))/(stage3_trial_counter + 1); - -% Change the value of CP Duration -if n_completed_trials < 1 - ParamsSection_CP_duration.value = value(ParamsSection_init_CP_duration); -else - if ~timeout_history(end) && value(ParamsSection_CP_duration) < cp_max - increment = value(ParamsSection_CP_duration)*cp_fraction; - if increment < cp_minimum_increment - increment = value(cp_minimum_increment); - end - ParamsSection_CP_duration.value = value(ParamsSection_CP_duration) + increment; - end -end -callback(ParamsSection_CP_duration); -% -end -if completion_test_eval -GetSoloFunctionArgs(obj); -ClearHelperVarsNotOwned(obj); -clear('ans'); -% -if value(ParamsSection_CP_duration) >= cp_max - sm = value(SessionDefinition_my_session_model); - SessionDefinition_my_session_model.value = jump(sm, 'offset',+1); -end -% -if exist('ans', 'var') -varargout{1}=logical(ans); clear('ans'); -else -varargout{1}=false; -end -end -if eod_logic_eval -GetSoloFunctionArgs(obj); -ClearHelperVarsNotOwned(obj); -% -stage3_trial_counter_today = 0; -last_session_cp = value(ParamsSection_CP_duration); -% -end -% - -%% Introduce Violation for Centre Poke - -% -case 'Introduce Violation for Centre Poke' -if helper_vars_eval -GetSoloFunctionArgs(obj); -ClearHelperVarsNotOwned(obj); -% - -% Maximum & Minimum duration of center poke, in secs: -cp_min = value(ParamsSection_SettlingIn_time) + value(ParamsSection_legal_cbreak); -cp_max = 1.5; -% Fractional increment in center poke duration every time there is a non-cp-violation trial: -cp_fraction = 0.001; -% Minimum increment (in secs) in center poke duration every time there is a non-cp-violation trial: -cp_minimum_increment = 0.001; -last_session_cp = 0; -stage4_trial_counter = 0; -stage4_trial_counter_today = 0; -stage4_timeout_percent = 0; -stage4_violation_percent = 0; - -% -end -if stage_algorithm_eval -GetSoloFunctionArgs(obj); -ClearHelperVarsNotOwned(obj); -% -ParamsSection_training_stage.value = 4; -callback(ParamsSection_training_stage); -stage4_trial_counter = stage4_trial_counter + 1; -stage4_trial_counter_today = stage4_trial_counter_today + 1; -stage4_timeout_percent = ((stage4_timeout_percent * stage4_trial_counter) + double(timeout_history(end)))/(stage4_trial_counter + 1); -stage4_violation_percent = ((stage4_violation_percent * stage4_trial_counter) + double(violation_history(end)))/(stage4_trial_counter + 1); - -% Change the value of CP Duration -if n_completed_trials < 1 - ParamsSection_CP_duration.value = value(ParamsSection_init_CP_duration); -else - if ~violation_history(end) && ~timeout_history(end) && value(ParamsSection_CP_duration) < cp_max - increment = value(ParamsSection_CP_duration)*cp_fraction; - if increment < cp_minimum_increment - increment = value(cp_minimum_increment); - end - ParamsSection_CP_duration.value = value(ParamsSection_CP_duration) + increment; - end -end -if value(ParamsSection_CP_duration) > cp_max - ParamsSection_CP_duration.value = cp_max; -end - -callback(ParamsSection_CP_duration); -% -end -if completion_test_eval -GetSoloFunctionArgs(obj); -ClearHelperVarsNotOwned(obj); -clear('ans'); -% -if value(ParamsSection_CP_duration) >= cp_max && n_completed_trials > 100 -if SessionPerformanceSection_violation_recent < 0.1 && SessionPerformanceSection_timeout_recent < 0.1 && stage4_violation_percent < 0.35 - sm = value(SessionDefinition_my_session_model); - SessionDefinition_my_session_model.value = jump(sm, 'offset',+1); - last_session_cp = value(ParamsSection_CP_duration); -end -end -% -if exist('ans', 'var') -varargout{1}=logical(ans); clear('ans'); -else -varargout{1}=false; -end -end -if eod_logic_eval -GetSoloFunctionArgs(obj); -ClearHelperVarsNotOwned(obj); -% -% Store the value of the total cp duration reached: -stage4_trial_counter_today = 0; -last_session_cp = value(ParamsSection_CP_duration); -% -end -% - -%% Introduce Stimuli Sound during Centre Poke - -% -case 'Introduce Stimuli Sound during Centre Poke' -if helper_vars_eval -GetSoloFunctionArgs(obj); -ClearHelperVarsNotOwned(obj); -% -cp_max = 5; -cp_min = 1.5; -% Fractional increment in center poke duration every time there is a non-cp-violation trial: -cp_fraction = 0.002; -% Minimum increment (in secs) in center poke duration every time there is a non-cp-violation trial: -cp_minimum_increment = 0.001; -% cp value for last session -last_session_cp = 0; -% Starting total center poke duration: -starting_total_cp = 0.5; -% number of warm-up trials -n_trial_warmup = 10; -stage5_trial_counter = 0; -stage5_trial_counter_today = 0; -stage5_timeout_percent = 0; -stage5_violation_percent = 0; -% -end -if stage_algorithm_eval -GetSoloFunctionArgs(obj); -ClearHelperVarsNotOwned(obj); -% - -ParamsSection_training_stage.value = 5; -callback(ParamsSection_training_stage); -stage5_trial_counter = stage5_trial_counter + 1; -stage5_trial_counter_today = stage5_trial_counter_today + 1; -stage5_timeout_percent = ((stage5_timeout_percent * stage5_trial_counter) + double(timeout_history(end)))/(stage5_trial_counter + 1); -stage5_violation_percent = ((stage5_violation_percent * stage5_trial_counter) + double(violation_history(end)))/(stage5_trial_counter + 1); - -% Change the value of CP Duration - -% Since starting a new session then do a pre warm up to last saved cp -% duration else continue with learning with increased poke time -if n_completed_trials == 0 - ParamsSection_CP_duration.value = value(ParamsSection_init_CP_duration); -elseif n_completed_trials == 1 - ParamsSection_CP_duration.value = starting_total_cp; -else - if ~violation_history(end) && ~timeout_history(end) - if value(ParamsSection_CP_duration) < max([cp_min,last_session_cp]) % warm up stage - increment = (max([cp_min,last_session_cp]) - value(ParamsSection_CP_duration))/ (n_trial_warmup - 1); - else - if value(ParamsSection_CP_duration) >= last_session_cp && value(ParamsSection_CP_duration) <= cp_max % no warm up stage - increment = value(ParamsSection_CP_duration)*cp_fraction; - if increment < cp_minimum_increment - increment = value(cp_minimum_increment); - end - end - end - - ParamsSection_CP_duration.value = value(ParamsSection_CP_duration) + increment; - - % Check if the values are within the required range - if value(ParamsSection_CP_duration) < starting_total_cp - ParamsSection_CP_duration.value = starting_total_cp; - end - if value(ParamsSection_CP_duration) > cp_max - ParamsSection_CP_duration.value = cp_max; - end - - end -end -callback(ParamsSection_CP_duration); - -if value(ParamsSection_CP_duration) >= 1 - ParamsSection_PreStim_time.value = 0.4; - if value(ParamsSection_CP_duration) < 2 - ParamsSection_A1_time.value = 0.1; - elseif value(ParamsSection_CP_duration) < 2.5 && value(ParamsSection_CP_duration) >= 2 - ParamsSection_A1_time.value = 0.2; - elseif value(ParamsSection_CP_duration) < 3 && value(ParamsSection_CP_duration) >= 2.5 - ParamsSection_A1_time.value = 0.3; - else - ParamsSection_A1_time.value = 0.4; - end -elseif value(ParamsSection_CP_duration) < 1 && value(ParamsSection_CP_duration) >= starting_total_cp - ParamsSection_SettlingIn_time.value = 0.2; - callback(ParamsSection_SettlingIn_time); - ParamsSection_PreStim_time.value = 0.1; - ParamsSection_A1_time.value = 0.1; -end - -if value(ParamsSection_CP_duration) >= starting_total_cp - callback(ParamsSection_PreStim_time); - callback(ParamsSection_A1_time); -end - - -% -end -if completion_test_eval -GetSoloFunctionArgs(obj); -ClearHelperVarsNotOwned(obj); -clear('ans'); -% -if value(ParamsSection_CP_duration) >= cp_max && stage5_trial_counter > 1000 - if SessionPerformanceSection_violation_recent < 0.1 && SessionPerformanceSection_timeout_recent < 0.1 && stage5_violation_percent < 0.3 && n_completed_trials > 100 - sm = value(SessionDefinition_my_session_model); - SessionDefinition_my_session_model.value = jump(sm, 'offset',+1); - last_session_cp = value(ParamsSection_CP_duration); - end -end -% -if exist('ans', 'var') -varargout{1}=logical(ans); clear('ans'); -else -varargout{1}=false; -end -end -if eod_logic_eval -GetSoloFunctionArgs(obj); -ClearHelperVarsNotOwned(obj); -% -stage5_trial_counter_today = 0; -last_session_cp = value(ParamsSection_CP_duration); -% -end -% - -%% Vary Stimuli location during Centre Poke - -% -case 'Vary Stimuli location during Centre Poke' -if helper_vars_eval -GetSoloFunctionArgs(obj); -ClearHelperVarsNotOwned(obj); -% -stage6_trial_counter = 0; -stage6_trial_counter_today = 0; -stage6_timeout_percent = 0; -stage6_violation_percent = 0; -cp_max = 5; -n_trial_warmup = 20; -starting_total_cp = 0.5; -prestim_min = 0.5; -prestim_max = 2; - -% -end -if stage_algorithm_eval -GetSoloFunctionArgs(obj); -ClearHelperVarsNotOwned(obj); -% -ParamsSection_training_stage.value = 6; -callback(ParamsSection_training_stage); -stage6_trial_counter = stage6_trial_counter + 1; -stage6_trial_counter_today = stage6_trial_counter_today + 1; -stage6_timeout_percent = ((stage6_timeout_percent * stage6_trial_counter) + double(timeout_history(end)))/(stage6_trial_counter + 1); -stage6_violation_percent = ((stage6_violation_percent * stage6_trial_counter) + double(violation_history(end)))/(stage6_trial_counter + 1); - -% Warm Up If starting a new session -if n_completed_trials == 0 - ParamsSection_CP_duration.value = value(ParamsSection_init_CP_duration); -elseif n_completed_trials == 1 - ParamsSection_CP_duration.value = starting_total_cp; -else - if value(ParamsSection_CP_duration) < cp_max % warm up stage - if ~violation_history(end) && ~timeout_history(end) - increment = (cp_max - value(ParamsSection_CP_duration))/ (n_trial_warmup - 1); - ParamsSection_CP_duration.value = value(ParamsSection_CP_duration) + increment; - % Check if the values are within the required range - if value(ParamsSection_CP_duration) < starting_total_cp - ParamsSection_CP_duration.value = starting_total_cp; - end - if value(ParamsSection_CP_duration) > cp_max - ParamsSection_CP_duration.value = cp_max; - end - end - end -end - -callback(ParamsSection_CP_duration); - -if value(ParamsSection_CP_duration) < 3 % during the warm up phase - ParamsSection_SettlingIn_time.value = 0.2; - callback(ParamsSection_SettlingIn_time); - ParamsSection_PreStim_time.value = 0.1; - ParamsSection_A1_time.value = 0.1; -else - ParamsSection_A1_time.value = 0.4; % actual training stage - time_range_PreStim_time = prestim_min : 0.01 : prestim_max; - ParamsSection_PreStim_time.value = time_range_PreStim_time(randi([1, numel(time_range_PreStim_time)],1,1)); -end -callback(ParamsSection_A1_time); -callback(ParamsSection_PreStim_time); - -% -end -if completion_test_eval -GetSoloFunctionArgs(obj); -ClearHelperVarsNotOwned(obj); -clear('ans'); -% -if stage6_trial_counter > 1500 - if SessionPerformanceSection_violation_recent < 0.15 && SessionPerformanceSection_timeout_recent < 0.15 && stage6_violation_percent < 0.25 - sm = value(SessionDefinition_my_session_model); - SessionDefinition_my_session_model.value = jump(sm, 'offset',+1); - end -end -% -if exist('ans', 'var') -varargout{1}=logical(ans); clear('ans'); -else -varargout{1}=false; -end -end -if eod_logic_eval -GetSoloFunctionArgs(obj); -ClearHelperVarsNotOwned(obj); -% -stage6_trial_counter_today = 0; -% -end -% - -%% Variable Stimuli Go Cue location during Centre Poke - -% -case 'Variable Stimuli Go Cue location during Centre Poke' -if helper_vars_eval -GetSoloFunctionArgs(obj); -ClearHelperVarsNotOwned(obj); -% -stage7_trial_counter = 0; -stage7_trial_counter_today = 0; -stage7_timeout_percent = 0; -stage7_violation_percent = 0; - -% Variables for warmup stage -cp_max = 5; -n_trial_warmup = 20; -starting_total_cp = 0.5; -warmup_completed = 0; -warm_up_on = 0; -random_prestim = 0; -random_prego = 0; -random_A1 = 0; -prestim_min = 0; -prestim_max = 0; -prestim_time = 0; -prego_min =0; -prego_max = 0; -prego_time = 0; -a1_time = 0; -a1_time_min = 0; -a1_time_max = 0; - -% -end -if stage_algorithm_eval -GetSoloFunctionArgs(obj); -ClearHelperVarsNotOwned(obj); -% -ParamsSection_training_stage.value = 7; -stage7_trial_counter = stage7_trial_counter + 1; -stage7_trial_counter_today = stage7_trial_counter_today + 1; -stage7_timeout_percent = ((stage7_timeout_percent * stage7_trial_counter) + double(timeout_history(end)))/(stage7_trial_counter + 1); -stage7_violation_percent = ((stage7_violation_percent * stage7_trial_counter) + double(violation_history(end)))/(stage7_trial_counter + 1); - -if stage7_trial_counter < 2000 - % the rat has been not been trained for good amount of sessions wait for the user - % to play around with the settings - warm_up_on = 1; - random_prestim = 1; - random_prego = 1; - random_A1 = 0; - prestim_min = 0.2; - prestim_max = 2; - prestim_time = 0.2; - prego_min = 0.2; - prego_max = 2; - prego_time = 0.2; - a1_time = 0.4; - a1_time_min = 0.4; - a1_time_max = 0.4; -else - warm_up_on = value(ParamsSection_warmup_on); - random_prestim = value(ParamsSection_random_PreStim_time); - random_prego = value(ParamsSection_random_prego_time); - random_A1 = value(ParamsSection_random_A1_time); - prestim_min = value(ParamsSection_PreStim_time_Min); - prestim_max = value(ParamsSection_PreStim_time_Max); - prestim_time = value(ParamsSection_PreStim_time); - prego_min = value(ParamsSection_time_bet_aud1_gocue_Min); - prego_max = value(ParamsSection_time_bet_aud1_gocue_Max); - prego_time = value(ParamsSection_time_bet_aud1_gocue); - a1_time = value(ParamsSection_A1_time); - a1_time_min = value(ParamsSection_A1_time_Min); - a1_time_max = value(ParamsSection_A1_time_Max); -end - -% Warm Up If starting a new session -if warm_up_on == 1 - if n_completed_trials == 0 - ParamsSection_CP_duration.value = value(ParamsSection_init_CP_duration); - warmup_completed = 0; - elseif n_completed_trials == 1 - ParamsSection_CP_duration.value = starting_total_cp; - else - if value(ParamsSection_CP_duration) <= cp_max % warm up stage - if ~violation_history(end) && ~timeout_history(end) - increment = (cp_max - value(ParamsSection_CP_duration))/ (n_trial_warmup - 1); - ParamsSection_CP_duration.value = value(ParamsSection_CP_duration) + increment; - % Check if the values are within the required range - if value(ParamsSection_CP_duration) < starting_total_cp - ParamsSection_CP_duration.value = starting_total_cp; - end - if value(ParamsSection_CP_duration) >= cp_max - ParamsSection_CP_duration.value = cp_max; - warmup_completed = 1; - end - end - end - end -else - warmup_completed = 1; -end - -if n_completed_trials >= 1 - cp_length = value(ParamsSection_CP_duration) - value(ParamsSection_Setting) -[ParamsSection_PreStim_time.value ,ParamsSection_A1_time.value,ParamsSection_time_bet_aud1_gocue.value] = param_time_within_range(warmup_completed,... - cp_length,prestim_min,prestim_max, random_prestim, prestim_time,... - a1_time_min,a1_time_max, random_A1, a1_time,prego_min,prego_max, random_prego, prego_time); - -callback(ParamsSection_PreStim_time); -end -callback(ParamsSection_CP_duration); -% -end -if completion_test_eval -GetSoloFunctionArgs(obj); -ClearHelperVarsNotOwned(obj); -clear('ans'); -% - -% -if exist('ans', 'var') -varargout{1}=logical(ans); clear('ans'); -else -varargout{1}=false; -end -end -if eod_logic_eval -GetSoloFunctionArgs(obj); -ClearHelperVarsNotOwned(obj); -% -stage7_trial_counter_today = 0; -% -end -% - - -end - -end - -%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - -% - -function [prestim,A1,prego] = param_time_within_range(fixed_length,cp_length,range_min_prestim,range_max_prestim, is_random_prestim, provided_time_prestim,... - range_min_A1,range_max_A1, is_random_A1, provided_time_A1,range_min_prego,range_max_prego, is_random_prego, provided_time_prego) - -if fixed_length == 1 % warm up stage where cp length is increasing -% then calculate the range/typical value - if cp_length <= 0.3 - prestim = 0.1; - A1 = 0.1; - prego = 0.1; - else - range_size = round(0.3 * cp_length,1); - if range_size > 0.4 - step_size = 0.1; - else - step_size = 0.01; - end - - timerange = 0.1:step_size:range_size; - - if is_random_prestim == 1 - prestim = timerange(randi([1, numel(timerange)],1,1)); - else - if provided_time_prestim <= range_size - prestim = provided_time_prestim; - else - prestim = range_size; - end - - end - - if is_random_A1 == 1 - A1 = timerange(randi([1, numel(timerange)],1,1)); - else - if provided_time_A1 <= range_size - A1 = provided_time_A1; - else - A1 = range_size; - end - end - - prego = cp_length - prestim - A1; - - end - -else - - if is_random_prestim == 1 - range_size_prestim = range_max_prestim - range_min_prestim; - if range_size_prestim > 0.4 - step_size_prestim = 0.1; - else - step_size_prestim = 0.01; - end - time_range_prestim = range_min_prestim:step_size_prestim:range_max_prestim; - prestim = time_range_prestim(randi([1, numel(time_range_prestim)],1,1)); - else - prestim = provided_time_prestim; - end - - if is_random_A1 == 1 - range_size_A1 = range_max_A1 - range_min_A1; - if range_size_A1 > 0.4 - step_size_A1 = 0.1; - else - step_size_A1 = 0.01; - end - time_range_A1 = range_min_A1:step_size_A1:range_max_A1; - A1 = time_range_A1(randi([1, numel(time_range_A1)],1,1)); - else - A1 = provided_time_A1; - end - - if is_random_prego == 1 - range_size_prego = range_max_prego - range_min_prego; - if range_size_prego > 0.4 - step_size_prego = 0.1; - else - step_size_prego = 0.01; - end - time_range_prego = range_min_prego:step_size_prego:range_max_prego; - prego = time_range_prego(randi([1, numel(time_range_prego)],1,1)); - else - prego = provided_time_prego; - end - -end -end - - -% - -%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% diff --git a/Protocols/@Arpit_CentrePokeTraining/pipeline_Arpit_CentrePokeTraining_experimenter_ratname_250319c.m b/Protocols/@Arpit_CentrePokeTraining/pipeline_Arpit_CentrePokeTraining_experimenter_ratname_250319c.m index 6bc87e46..0c08b919 100644 --- a/Protocols/@Arpit_CentrePokeTraining/pipeline_Arpit_CentrePokeTraining_experimenter_ratname_250319c.m +++ b/Protocols/@Arpit_CentrePokeTraining/pipeline_Arpit_CentrePokeTraining_experimenter_ratname_250319c.m @@ -37,7 +37,11 @@ ParamsSection_training_stage.value = 1; callback(ParamsSection_training_stage); stage1_trial_counter = stage1_trial_counter + 1; + SessionPerformanceSection_ntrials_stage = stage1_trial_counter; + callback(SessionPerformanceSection_ntrials_stage) stage1_trial_counter_today = stage1_trial_counter_today + 1; + SessionPerformanceSection_ntrials_stage_today.value = stage1_trial_counter_today; + callback(SessionPerformanceSection_ntrials_stage_today) if value(previous_sides(end)) ~= value(ParamsSection_ThisTrial) stage1_trial_counter_oppSide = stage1_trial_counter_oppSide + 1; end @@ -51,7 +55,7 @@ % only run it if its the start of the day, so number of trials the rat did % is less if n_completed_trial < 100 - if stage1_trial_counter > 300 && stage1_trial_counter_oppSide > 150 + if stage1_trial_counter > value(Training_ParamsSection_total_trials) && stage1_trial_counter_oppSide > value(Training_ParamsSection_total_trials_opp) sm = value(SessionDefinition_my_session_model); SessionDefinition_my_session_model.value = jump(sm, 'offset',+1); end @@ -68,7 +72,7 @@ ClearHelperVarsNotOwned(obj); % stage1_trial_counter_today = 0; -if stage1_trial_counter > 300 && stage1_trial_counter_oppSide > 150 +if stage1_trial_counter > value(Training_ParamsSection_total_trials) && stage1_trial_counter_oppSide > value(Training_ParamsSection_total_trials_opp) sm = value(SessionDefinition_my_session_model); SessionDefinition_my_session_model.value = jump(sm, 'offset',+1); end @@ -89,15 +93,13 @@ stage2_trial_counter_oppSide = 0; opp_side_trials = 0; stage2_timeout_percent = 0; -max_reward_collection_dur = 15; % this is the max I will allow -min_reward_collection_dur = 5; % this is the max I will allow % end if stage_algorithm_eval GetSoloFunctionArgs(obj); ClearHelperVarsNotOwned(obj); % -ParamsSection_MaxSame.value = 4; +ParamsSection_MaxSame.value = 3; callback(ParamsSection_MaxSame); ParamsSection_training_stage.value = 2; callback(ParamsSection_training_stage); @@ -111,14 +113,14 @@ if size(timeout_history) > 5 if all(value(timeout_history(end-1:end))) && opp_side_trials >= 2 ParamsSection_RewardCollection_duration.value = min([value(ParamsSection_RewardCollection_duration) + 1,... - max_reward_collection_dur]); + value(Training_ParamsSection_max_rColl_dur)]); callback(ParamsSection_RewardCollection_duration); opp_side_trials = 0; end if ~any(value(timeout_history(end-1:end))) && opp_side_trials >= 2 ParamsSection_RewardCollection_duration.value = max([value(ParamsSection_RewardCollection_duration) - 1,... - min_reward_collection_dur]); + value(Training_ParamsSection_min_rColl_dur)]); callback(ParamsSection_RewardCollection_duration); opp_side_trials = 0; end @@ -141,7 +143,7 @@ clear('ans'); % if n_completed_trial > 50 - if stage2_trial_counter > 1000 && stage2_trial_counter_oppSide > 200 + if stage2_trial_counter > value(Training_ParamsSection_total_trials) && stage2_trial_counter_oppSide > value(Training_ParamsSection_total_trials_opp) sm = value(SessionDefinition_my_session_model); SessionDefinition_my_session_model.value = jump(sm, 'offset',+1); end @@ -157,7 +159,7 @@ GetSoloFunctionArgs(obj); ClearHelperVarsNotOwned(obj); % - +stage2_trial_counter_today = 0; % end % @@ -173,8 +175,6 @@ % Maximum & Minimum duration of center poke, in secs: cp_max = value(ParamsSection_SettlingIn_time) + value(ParamsSection_legal_cbreak); cp_min = value(ParamsSection_init_CP_duration); -% Fractional increment in center poke duration every time there is a non-cp-violation trial: -cp_fraction = 0.001; % Minimum increment (in secs) in center poke duration every time there is a non-cp-violation trial: cp_minimum_increment = 0.001; @@ -202,8 +202,8 @@ ParamsSection_CP_duration.value = value(ParamsSection_init_CP_duration); else if ~timeout_history(end) && value(ParamsSection_CP_duration) < cp_max - increment = value(ParamsSection_CP_duration)*cp_fraction; - if increment < cp_minimum_increment + increment = value(ParamsSection_CP_duration) * value(Training_ParamsSection_CPfraction_inc); + if increment < value(cp_minimum_increment) increment = value(cp_minimum_increment); end ParamsSection_CP_duration.value = value(ParamsSection_CP_duration) + increment; @@ -249,9 +249,9 @@ % Maximum & Minimum duration of center poke, in secs: cp_min = value(ParamsSection_SettlingIn_time) + value(ParamsSection_legal_cbreak); -cp_max = 1.5; +cp_max = value(Training_ParamsSection_max_CP); % Fractional increment in center poke duration every time there is a non-cp-violation trial: -cp_fraction = 0.001; +cp_fraction = value(Training_ParamsSection_CPfraction_inc); % Minimum increment (in secs) in center poke duration every time there is a non-cp-violation trial: cp_minimum_increment = 0.001; last_session_cp = 0; @@ -298,7 +298,8 @@ clear('ans'); % if value(ParamsSection_CP_duration) >= cp_max && n_completed_trials > 100 -if SessionPerformanceSection_violation_recent < 0.1 && SessionPerformanceSection_timeout_recent < 0.1 && stage4_violation_percent < 0.35 +if SessionPerformanceSection_violation_recent < value(Training_ParamsSection_recent_violation) && SessionPerformanceSection_timeout_recent < value(Training_ParamsSection_recent_timeout) && ... + stage4_violation_percent < value(Training_ParamsSection_stage_violation) sm = value(SessionDefinition_my_session_model); SessionDefinition_my_session_model.value = jump(sm, 'offset',+1); last_session_cp = value(ParamsSection_CP_duration); @@ -330,18 +331,18 @@ GetSoloFunctionArgs(obj); ClearHelperVarsNotOwned(obj); % -cp_max = 5; -cp_min = 1.5; +cp_max = value(Training_ParamsSection_max_CP); +cp_min = value(Training_ParamsSection_min_CP); % Fractional increment in center poke duration every time there is a non-cp-violation trial: -cp_fraction = 0.002; +cp_fraction = value(Training_ParamsSection_CPfraction_inc); % Minimum increment (in secs) in center poke duration every time there is a non-cp-violation trial: cp_minimum_increment = 0.001; % cp value for last session last_session_cp = 0; % Starting total center poke duration: -starting_total_cp = 0.5; +starting_cp = value(Training_ParamsSection_starting_CP) + value(ParamsSection_SettlingIn_time); % number of warm-up trials -n_trial_warmup = 10; +n_trial_warmup = value(Training_ParamsSection_warm_up_trials); stage5_trial_counter = 0; stage5_trial_counter_today = 0; stage5_timeout_percent = 0; @@ -367,7 +368,7 @@ if n_completed_trials == 0 ParamsSection_CP_duration.value = value(ParamsSection_init_CP_duration); elseif n_completed_trials == 1 - ParamsSection_CP_duration.value = starting_total_cp; + ParamsSection_CP_duration.value = starting_cp; else if ~violation_history(end) && ~timeout_history(end) if value(ParamsSection_CP_duration) < max([cp_min,last_session_cp]) % warm up stage @@ -384,8 +385,8 @@ ParamsSection_CP_duration.value = value(ParamsSection_CP_duration) + increment; % Check if the values are within the required range - if value(ParamsSection_CP_duration) < starting_total_cp - ParamsSection_CP_duration.value = starting_total_cp; + if value(ParamsSection_CP_duration) < starting_cp + ParamsSection_CP_duration.value = starting_cp; end if value(ParamsSection_CP_duration) > cp_max ParamsSection_CP_duration.value = cp_max; @@ -406,14 +407,16 @@ else ParamsSection_A1_time.value = 0.4; end -elseif value(ParamsSection_CP_duration) < 1 && value(ParamsSection_CP_duration) >= starting_total_cp +elseif value(ParamsSection_CP_duration) < 1 && value(ParamsSection_CP_duration) >= starting_cp ParamsSection_SettlingIn_time.value = 0.2; callback(ParamsSection_SettlingIn_time); ParamsSection_PreStim_time.value = 0.1; ParamsSection_A1_time.value = 0.1; end -if value(ParamsSection_CP_duration) >= starting_total_cp +if value(ParamsSection_CP_duration) >= starting_cp + ParamsSection_time_bet_aud1_gocue.value = value(ParamsSection_CP_duration) - value(ParamsSection_SettlingIn_time) - value(ParamsSection_A1_time) - value(ParamsSection_PreStim_time); + callback(ParamsSection_time_bet_aud1_gocue) callback(ParamsSection_PreStim_time); callback(ParamsSection_A1_time); end @@ -427,7 +430,8 @@ clear('ans'); % if value(ParamsSection_CP_duration) >= cp_max && stage5_trial_counter > 1000 - if SessionPerformanceSection_violation_recent < 0.1 && SessionPerformanceSection_timeout_recent < 0.1 && stage5_violation_percent < 0.3 && n_completed_trials > 100 + if SessionPerformanceSection_violation_recent < value(Training_ParamsSection_recent_violation) && SessionPerformanceSection_timeout_recent < value(Training_ParamsSection_recent_timeout) && ... + stage5_violation_percent < value(Training_ParamsSection_stage_violation) && n_completed_trials > 100 sm = value(SessionDefinition_my_session_model); SessionDefinition_my_session_model.value = jump(sm, 'offset',+1); last_session_cp = value(ParamsSection_CP_duration); @@ -462,12 +466,14 @@ stage6_trial_counter_today = 0; stage6_timeout_percent = 0; stage6_violation_percent = 0; -cp_max = 5; -n_trial_warmup = 20; -starting_total_cp = 0.5; -prestim_min = 0.5; -prestim_max = 2; - +cp_max = value(Training_ParamsSection_max_CP); +% Starting total center poke duration: +starting_cp = value(Training_ParamsSection_starting_CP) + value(ParamsSection_SettlingIn_time); +% number of warm-up trials +n_trial_warmup = value(Training_ParamsSection_warm_up_trials); +prestim_min = value(Training_ParamsSection_min_prestim); +prestim_max = value(Training_ParamsSection_max_prestim); +stim_dur = value(Training_ParamsSection_stim_dur); % end if stage_algorithm_eval @@ -485,15 +491,15 @@ if n_completed_trials == 0 ParamsSection_CP_duration.value = value(ParamsSection_init_CP_duration); elseif n_completed_trials == 1 - ParamsSection_CP_duration.value = starting_total_cp; + ParamsSection_CP_duration.value = starting_cp; else if value(ParamsSection_CP_duration) < cp_max % warm up stage if ~violation_history(end) && ~timeout_history(end) increment = (cp_max - value(ParamsSection_CP_duration))/ (n_trial_warmup - 1); ParamsSection_CP_duration.value = value(ParamsSection_CP_duration) + increment; % Check if the values are within the required range - if value(ParamsSection_CP_duration) < starting_total_cp - ParamsSection_CP_duration.value = starting_total_cp; + if value(ParamsSection_CP_duration) < starting_cp + ParamsSection_CP_duration.value = starting_cp; end if value(ParamsSection_CP_duration) > cp_max ParamsSection_CP_duration.value = cp_max; @@ -504,18 +510,23 @@ callback(ParamsSection_CP_duration); -if value(ParamsSection_CP_duration) < 3 % during the warm up phase +if value(ParamsSection_CP_duration) < 3 && value(ParamsSection_CP_duration) >= starting_cp % during the warm up phase ParamsSection_SettlingIn_time.value = 0.2; callback(ParamsSection_SettlingIn_time); ParamsSection_PreStim_time.value = 0.1; ParamsSection_A1_time.value = 0.1; else - ParamsSection_A1_time.value = 0.4; % actual training stage + ParamsSection_A1_time.value = stim_dur; % actual training stage time_range_PreStim_time = prestim_min : 0.01 : prestim_max; ParamsSection_PreStim_time.value = time_range_PreStim_time(randi([1, numel(time_range_PreStim_time)],1,1)); end -callback(ParamsSection_A1_time); -callback(ParamsSection_PreStim_time); + +if value(ParamsSection_CP_duration) >= starting_cp + ParamsSection_time_bet_aud1_gocue.value = value(ParamsSection_CP_duration) - value(ParamsSection_SettlingIn_time) - value(ParamsSection_A1_time) - value(ParamsSection_PreStim_time); + callback(ParamsSection_time_bet_aud1_gocue) + callback(ParamsSection_PreStim_time); + callback(ParamsSection_A1_time); +end % end @@ -524,8 +535,9 @@ ClearHelperVarsNotOwned(obj); clear('ans'); % -if stage6_trial_counter > 1500 - if SessionPerformanceSection_violation_recent < 0.15 && SessionPerformanceSection_timeout_recent < 0.15 && stage6_violation_percent < 0.25 +if stage6_trial_counter > value(Training_ParamsSection_total_trials) + if SessionPerformanceSection_violation_recent < value(Training_ParamsSection_recent_violation) && SessionPerformanceSection_timeout_recent < value(Training_ParamsSection_recent_timeout) && ... + stage6_violation_percent < value(Training_ParamsSection_stage_violation) sm = value(SessionDefinition_my_session_model); SessionDefinition_my_session_model.value = jump(sm, 'offset',+1); end @@ -560,24 +572,25 @@ stage7_violation_percent = 0; % Variables for warmup stage -cp_max = 5; -n_trial_warmup = 20; -starting_total_cp = 0.5; +cp_max = value(Training_ParamsSection_max_CP); +% Starting total center poke duration: +starting_cp = value(Training_ParamsSection_starting_CP) + value(ParamsSection_SettlingIn_time); +% number of warm-up trials +n_trial_warmup = value(Training_ParamsSection_warm_up_trials); +prestim_min = value(Training_ParamsSection_min_prestim); +prestim_max = value(Training_ParamsSection_max_prestim); +prestim_time = value(Training_ParamsSection_min_prestim); +a1_time = value(Training_ParamsSection_stim_dur); +a1_time_min = value(Training_ParamsSection_stim_dur); +a1_time_max = value(Training_ParamsSection_stim_dur + 0.1); +prego_min = value(Training_ParamsSection_min_prego); +prego_max = value(Training_ParamsSection_max_prego); +prego_time = value(Training_ParamsSection_min_prego); warmup_completed = 0; -warm_up_on = 0; -random_prestim = 0; -random_prego = 0; +warm_up_on = 1; +random_prestim = 1; +random_prego = 1; random_A1 = 0; -prestim_min = 0; -prestim_max = 0; -prestim_time = 0; -prego_min =0; -prego_max = 0; -prego_time = 0; -a1_time = 0; -a1_time_min = 0; -a1_time_max = 0; - % end if stage_algorithm_eval @@ -585,58 +598,142 @@ ClearHelperVarsNotOwned(obj); % ParamsSection_training_stage.value = 7; +callback(ParamsSection_training_stage); stage7_trial_counter = stage7_trial_counter + 1; stage7_trial_counter_today = stage7_trial_counter_today + 1; stage7_timeout_percent = ((stage7_timeout_percent * stage7_trial_counter) + double(timeout_history(end)))/(stage7_trial_counter + 1); stage7_violation_percent = ((stage7_violation_percent * stage7_trial_counter) + double(violation_history(end)))/(stage7_trial_counter + 1); -if stage7_trial_counter < 2000 - % the rat has been not been trained for good amount of sessions wait for the user - % to play around with the settings - warm_up_on = 1; - random_prestim = 1; - random_prego = 1; - random_A1 = 0; - prestim_min = 0.2; - prestim_max = 2; - prestim_time = 0.2; - prego_min = 0.2; - prego_max = 2; - prego_time = 0.2; - a1_time = 0.4; - a1_time_min = 0.4; - a1_time_max = 0.4; +% Warm Up If starting a new session +if warm_up_on == 1 + if n_completed_trials == 0 + ParamsSection_CP_duration.value = value(ParamsSection_init_CP_duration); + warmup_completed = 0; + elseif n_completed_trials == 1 + ParamsSection_CP_duration.value = starting_cp; + else + if value(ParamsSection_CP_duration) <= cp_max % warm up stage + if ~violation_history(end) && ~timeout_history(end) + increment = (cp_max - value(ParamsSection_CP_duration))/ (n_trial_warmup - 1); + ParamsSection_CP_duration.value = value(ParamsSection_CP_duration) + increment; + % Check if the values are within the required range + if value(ParamsSection_CP_duration) < starting_cp + ParamsSection_CP_duration.value = starting_cp; + end + if value(ParamsSection_CP_duration) >= cp_max + ParamsSection_CP_duration.value = cp_max; + warmup_completed = 1; + end + end + end + end else - warm_up_on = value(ParamsSection_warmup_on); - random_prestim = value(ParamsSection_random_PreStim_time); - random_prego = value(ParamsSection_random_prego_time); - random_A1 = value(ParamsSection_random_A1_time); - prestim_min = value(ParamsSection_PreStim_time_Min); - prestim_max = value(ParamsSection_PreStim_time_Max); - prestim_time = value(ParamsSection_PreStim_time); - prego_min = value(ParamsSection_time_bet_aud1_gocue_Min); - prego_max = value(ParamsSection_time_bet_aud1_gocue_Max); - prego_time = value(ParamsSection_time_bet_aud1_gocue); - a1_time = value(ParamsSection_A1_time); - a1_time_min = value(ParamsSection_A1_time_Min); - a1_time_max = value(ParamsSection_A1_time_Max); + warmup_completed = 1; end +if n_completed_trials >= 1 + cp_length = value(ParamsSection_CP_duration) - value(ParamsSection_SettlingIn_time); + [ParamsSection_PreStim_time.value ,ParamsSection_A1_time.value,ParamsSection_time_bet_aud1_gocue.value] = param_time_within_range(warmup_completed,... + cp_length,prestim_min,prestim_max, random_prestim, prestim_time,... + a1_time_min,a1_time_max, random_A1, a1_time,prego_min,prego_max, random_prego, prego_time); + + callback(ParamsSection_PreStim_time); + callback(ParamsSection_A1_time); + callback(ParamsSection_time_bet_aud1_gocue); + ParamsSection_CP_duration = value(ParamsSection_SettlingIn_time) + value(ParamsSection_PreStim_time) + value(ParamsSection_A1_time) + value(ParamsSection_time_bet_aud1_gocue); +end +callback(ParamsSection_CP_duration); +% +end +if completion_test_eval +GetSoloFunctionArgs(obj); +ClearHelperVarsNotOwned(obj); +clear('ans'); +% +if stage7_trial_counter > value(Training_ParamsSection_total_trials) + if SessionPerformanceSection_violation_recent < value(Training_ParamsSection_recent_violation) && SessionPerformanceSection_timeout_recent < value(Training_ParamsSection_recent_timeout) && ... + stage7_violation_percent < value(Training_ParamsSection_stage_violation) + sm = value(SessionDefinition_my_session_model); + SessionDefinition_my_session_model.value = jump(sm, 'offset',+1); + end +end +% +if exist('ans', 'var') +varargout{1}=logical(ans); clear('ans'); +else +varargout{1}=false; +end +end +if eod_logic_eval +GetSoloFunctionArgs(obj); +ClearHelperVarsNotOwned(obj); +% +stage7_trial_counter_today = 0; +% +end +% + +%% User Setting + +% + case 'User Setting' +if helper_vars_eval +GetSoloFunctionArgs(obj); +ClearHelperVarsNotOwned(obj); +% +stage8_trial_counter = 0; +stage8_trial_counter_today = 0; +stage8_timeout_percent = 0; +stage8_violation_percent = 0; + +% Variables for warmup stage +cp_max = 5; +n_trial_warmup = 20; +starting_cp = 0.5; +warmup_completed = 0; +warm_up_on = value(ParamsSection_warmup_on); +random_prestim = value(ParamsSection_random_PreStim_time); +random_prego = value(ParamsSection_random_prego_time); +random_A1 = value(ParamsSection_random_A1_time); +prestim_min = value(ParamsSection_PreStim_time_Min); +prestim_max = value(ParamsSection_PreStim_time_Max); +prestim_time = value(ParamsSection_PreStim_time); +prego_min = value(ParamsSection_time_bet_aud1_gocue_Min); +prego_max = value(ParamsSection_time_bet_aud1_gocue_Max); +prego_time = value(ParamsSection_time_bet_aud1_gocue); +a1_time = value(ParamsSection_A1_time); +a1_time_min = value(ParamsSection_A1_time_Min); +a1_time_max = value(ParamsSection_A1_time_Max); + +% +end +if stage_algorithm_eval +GetSoloFunctionArgs(obj); +ClearHelperVarsNotOwned(obj); +% + +ParamsSection_training_stage.value = 8; +stage8_trial_counter = stage8_trial_counter + 1; +stage8_trial_counter_today = stage8_trial_counter_today + 1; +stage8_timeout_percent = ((stage8_timeout_percent * stage8_trial_counter) + double(timeout_history(end)))/(stage8_trial_counter + 1); +stage8_violation_percent = ((stage8_violation_percent * stage8_trial_counter) + double(violation_history(end)))/(stage8_trial_counter + 1); + + % Warm Up If starting a new session if warm_up_on == 1 if n_completed_trials == 0 ParamsSection_CP_duration.value = value(ParamsSection_init_CP_duration); warmup_completed = 0; elseif n_completed_trials == 1 - ParamsSection_CP_duration.value = starting_total_cp; + ParamsSection_CP_duration.value = starting_cp; else if value(ParamsSection_CP_duration) <= cp_max % warm up stage if ~violation_history(end) && ~timeout_history(end) increment = (cp_max - value(ParamsSection_CP_duration))/ (n_trial_warmup - 1); ParamsSection_CP_duration.value = value(ParamsSection_CP_duration) + increment; % Check if the values are within the required range - if value(ParamsSection_CP_duration) < starting_total_cp - ParamsSection_CP_duration.value = starting_total_cp; + if value(ParamsSection_CP_duration) < starting_cp + ParamsSection_CP_duration.value = starting_cp; end if value(ParamsSection_CP_duration) >= cp_max ParamsSection_CP_duration.value = cp_max; @@ -680,7 +777,7 @@ GetSoloFunctionArgs(obj); ClearHelperVarsNotOwned(obj); % -stage7_trial_counter_today = 0; +stage8_trial_counter_today = 0; % end % From 3aced503a34e6e3fdb7b026ec650a54726c3b582 Mon Sep 17 00:00:00 2001 From: viktorpm Date: Wed, 26 Mar 2025 16:25:21 +0000 Subject: [PATCH 034/164] changes to remove errors because of new addition during the previous commit --- .../Arpit_CentrePokeTraining.m | 13 +- .../@Arpit_CentrePokeTraining/ParamsSection.m | 4 +- .../SessionPerformanceSection.asv | 177 ------------------ .../SessionPerformanceSection.m | 4 +- .../Training_ParamsSection.m | 46 ++--- ...okeTraining_experimenter_ratname_250319c.m | 90 ++++++--- 6 files changed, 98 insertions(+), 236 deletions(-) delete mode 100644 Protocols/@Arpit_CentrePokeTraining/SessionPerformanceSection.asv diff --git a/Protocols/@Arpit_CentrePokeTraining/Arpit_CentrePokeTraining.m b/Protocols/@Arpit_CentrePokeTraining/Arpit_CentrePokeTraining.m index a13e10e4..aad46392 100644 --- a/Protocols/@Arpit_CentrePokeTraining/Arpit_CentrePokeTraining.m +++ b/Protocols/@Arpit_CentrePokeTraining/Arpit_CentrePokeTraining.m @@ -117,12 +117,19 @@ % [x, y] = PunishmentSection(obj, 'init', x, y); %#ok - next_column(x); y=5; - [x,y] = Training_ParamsSection(obj, 'init', x, y); + next_column(x); y=5; [x, y] = SessionPerformanceSection(obj, 'init', x, y); [x, y] = ParamsSection(obj, 'init', x, y); %#ok [x, y] = SoundSection(obj,'init',x,y); - + [stage_fig_x,stage_fig_y] = Training_ParamsSection(obj, 'init', x, y); + + SoloParamHandle(obj, 'stage_fig_x', 'value', stage_fig_x); + SoloFunctionAddVars('ParamsSection', 'rw_args', 'stage_fig_x'); + + SoloParamHandle(obj, 'stage_fig_y', 'value', stage_fig_y); + SoloFunctionAddVars('ParamsSection', 'rw_args', 'stage_fig_y'); + + figpos = get(double(gcf), 'Position'); [expmtr, rname]=SavingSection(obj, 'get_info'); HeaderParam(obj, 'prot_title', [mfilename ': ' expmtr ', ' rname], x, y, 'position', [10 figpos(4)-25, 800 20]); diff --git a/Protocols/@Arpit_CentrePokeTraining/ParamsSection.m b/Protocols/@Arpit_CentrePokeTraining/ParamsSection.m index 590fdc0c..a1cd860b 100644 --- a/Protocols/@Arpit_CentrePokeTraining/ParamsSection.m +++ b/Protocols/@Arpit_CentrePokeTraining/ParamsSection.m @@ -162,7 +162,7 @@ SoloFunctionAddVars('SessionPerformanceSection', 'ro_args', ... {'training_stage'}); - SoloFunctionAddVars('SessionPerformanceSection', 'ro_args', ... + SoloFunctionAddVars('Training_ParamsSection', 'ro_args', ... {'training_stage'}); SoloParamHandle(obj, 'previous_parameters', 'value', []); @@ -208,7 +208,7 @@ enable(training_stage); % user can change the training stages SessionDefinition(obj, 'jump_to_stage',value(training_stage)); - [x,y] = Training_ParamsSection(obj, 'init', x, y); % update the training params as well + [stage_fig_x,stage_fig_y] = Training_ParamsSection(obj, 'reinit', value(stage_fig_x),value(stage_fig_y)); % update the training params as well switch value(training_stage) diff --git a/Protocols/@Arpit_CentrePokeTraining/SessionPerformanceSection.asv b/Protocols/@Arpit_CentrePokeTraining/SessionPerformanceSection.asv deleted file mode 100644 index 39e56717..00000000 --- a/Protocols/@Arpit_CentrePokeTraining/SessionPerformanceSection.asv +++ /dev/null @@ -1,177 +0,0 @@ -% [x, y] = SessionPerformanceSection(obj, action, x,y) -% -% Reports overall performance. Uses training_stage from SideSection. -% -% PARAMETERS: -% ----------- -% -% action One of: -% 'init' To initialise the section and set up the GUI -% for it -% -% 'close' Delete all of this section's GUIs and data -% -% 'reinit' Delete all of this section's GUIs and data, -% and reinit, at the same position on the same -% figure as the original section GUI was placed. -% -% 'evalueta' Look at history and compute hit fraction, etc. -% -% x, y Only relevant to action = 'init'; they indicate the initial -% position to place the GUI at, in the current figure window -% -% RETURNS: -% -------- -% -% perf When action == 'init', returns x and y, pixel positions on -% the current figure, updated after placing of this section's GUI. -% When action == 'evaluate', returns a vector with elements -% [ntrials, violation_rate, left_hit_frac, right_hit_frac, hit_frac] -% -% - -% CDB, 23-March-2013 - -function [x, y] = SessionPerformanceSection(obj, action, x,y) - -GetSoloFunctionArgs(obj); - -switch action - - % ------------------------------------------------------------------ - % INIT - % ------------------------------------------------------------------ - - case 'init' - - SoloParamHandle(obj, 'my_gui_info', 'value', [x y double(gcf)], 'saveable', 0); - - DispParam(obj, 'ntrials', 0, x, y,'label','Session Trials', 'TooltipString', ... - 'trials done in this session'); - next_row(y); - DispParam(obj, 'ntrials_stage', 0, x, y,'label','Stage Trials', 'TooltipString', ... - 'trials completed in this training stage'); - next_row(y); - DispParam(obj, 'ntrials_stage_today', 0, x, y,'label','Stage Trials Today', 'TooltipString', ... - 'trials completed in this training stage'); - next_row(y); - DispParam(obj, 'violation_rate', 0, x, y,'label','Session Violation', 'TooltipString', ... - 'Fraction of trials with a center poke violation in this session'); - next_row(y); - DispParam(obj, 'timeout_rate', 0, x, y,'label','Session Timeout', 'TooltipString', ... - 'Fraction of trials with timeout in this session'); - next_row(y); - DispParam(obj, 'violation_recent', 0, x, y,'label','Recent Violation', 'TooltipString', ... - 'Fraction of trials with a center poke violation in the past 20 trials'); - next_row(y); - DispParam(obj, 'timeout_recent', 0, x, y,'label','Recent Timeout', 'TooltipString', ... - 'Fraction of trials with a center poke violation in the past 20 trials'); - next_row(y); - DispParam(obj, 'violation_stage', 0, x, y,'label','Stage Violation', 'TooltipString', ... - 'Fraction of violations in this training stage'); - next_row(y); - DispParam(obj, 'violation_stage', 0, x, y,'label','Stage Timeout', 'TooltipString', ... - 'Fraction of timeouts in this training stage'); - next_row(y); - SubheaderParam(obj, 'title', 'Overall Performance', x, y); - next_row(y, 1.5); - SoloParamHandle(obj, 'previous_parameters', 'value', []); - - % ------------------------------------------------------------------ - % evaluate - % ------------------------------------------------------------------ - - case 'evaluate' - - switch value(training_stage) - case 1 %% center led on -> poke in the center -> go cue -> reward light and sound - if n_completed_trials > 1 - ntrials.value = n_completed_trials; - violation_rate.value = nan; - timeout_rate.value = nan; - end - violation_recent.value = nan; - timeout_recent.value = nan; - - violation_stage.value = nan; - timeout_stage.value = nan; - - case {2,3} %% center led on -> poke in the center -> go cue -> reward light and sound - if n_completed_trials > 1 - ntrials.value = n_completed_trials; - violation_rate.value = nan; - timeout_rate.value = numel(find(timeout_history))/n_completed_trials; - end - - violation_recent.value = nan; - violation_stage.value = nan; - - if n_completed_trials >= 20 - timeout_recent.value = numel(find(timeout_history(end-19:end)))/20; - else - timeout_recent.value = nan; - end - - timeout_stage.value = nan; - - - case {4,5,6,7} - - if n_completed_trials > 1 - ntrials.value = n_completed_trials; - violation_rate.value = numel(find(violation_history))/n_completed_trials; - timeout_rate.value = numel(find(timeout_history))/n_completed_trials; - end - - if n_completed_trials >= 20 - timeout_recent.value = numel(find(timeout_history(end-19:end)))/20; - violation_recent.value = numel(find(violation_history(end-19:end)))/20; - else - timeout_recent.value = nan; - violation_recent.value = nan; - end - - - end - - if nargout > 0 - x = [n_completed_trials, value(ntrials_stage), value(violation_rate), value(timeout_rate), value(violation_recent), ... - value(timeout_recent), value(violation_stage), value(timeout_stage)]; - end - - - % ------------------------------------------------------------------ - % close - % ------------------------------------------------------------------ - - case 'close' - % Delete all SoloParamHandles who belong to this object and whose - % fullname starts with the name of this mfile: - delete_sphandle('owner', ['^@' class(obj) '$'], ... - 'fullname', ['^' mfilename]); - - - % ------------------------------------------------------------------ - % reinit - % ------------------------------------------------------------------ - - case 'reinit' - currfig = double(gcf); - - % Get the original GUI position and figure: - x = my_gui_info(1); y = my_gui_info(2); figure(my_gui_info(3)); - - % Delete all SoloParamHandles who belong to this object and whose - % fullname starts with the name of this mfile: - delete_sphandle('owner', ['^@' class(obj) '$'], ... - 'fullname', ['^' mfilename]); - - % Reinitialise at the original GUI position and figure: - [x, y] = feval(mfilename, obj, 'init', x, y); - - % Restore the current figure: - figure(currfig); - -end - - diff --git a/Protocols/@Arpit_CentrePokeTraining/SessionPerformanceSection.m b/Protocols/@Arpit_CentrePokeTraining/SessionPerformanceSection.m index 39e56717..f391df95 100644 --- a/Protocols/@Arpit_CentrePokeTraining/SessionPerformanceSection.m +++ b/Protocols/@Arpit_CentrePokeTraining/SessionPerformanceSection.m @@ -70,7 +70,7 @@ DispParam(obj, 'violation_stage', 0, x, y,'label','Stage Violation', 'TooltipString', ... 'Fraction of violations in this training stage'); next_row(y); - DispParam(obj, 'violation_stage', 0, x, y,'label','Stage Timeout', 'TooltipString', ... + DispParam(obj, 'timeout_stage', 0, x, y,'label','Stage Timeout', 'TooltipString', ... 'Fraction of timeouts in this training stage'); next_row(y); SubheaderParam(obj, 'title', 'Overall Performance', x, y); @@ -115,7 +115,7 @@ timeout_stage.value = nan; - case {4,5,6,7} + case {4,5,6,7,8} if n_completed_trials > 1 ntrials.value = n_completed_trials; diff --git a/Protocols/@Arpit_CentrePokeTraining/Training_ParamsSection.m b/Protocols/@Arpit_CentrePokeTraining/Training_ParamsSection.m index 623517b1..c2882e77 100644 --- a/Protocols/@Arpit_CentrePokeTraining/Training_ParamsSection.m +++ b/Protocols/@Arpit_CentrePokeTraining/Training_ParamsSection.m @@ -11,87 +11,79 @@ case 'init' SoloParamHandle(obj, 'my_gui_info', 'value', [x y double(gcf)], 'saveable', 0); - + next_row(y); next_row(y); next_row(y);next_row(y); switch value(training_stage) - case 1 % STAGE RUNNING PARAMETERS - SubheaderParam(obj, 'title', 'Stage Params', x, y); - next_row(y); - + % SubheaderParam(obj, 'title', 'Stage Params', x, y); + % next_row(y); % COMPLETION TEST PARAMETERS - SubheaderParam(obj, 'title', 'Completion Params', x, y); next_row(y); NumeditParam(obj, 'total_trials', 300, x, y,'label','Trials','TooltipString','total trials in this stage for its completion'); next_row(y); NumeditParam(obj, 'total_trials_opp', 150, x, y,'label','Trials_Opp','TooltipString','total trials with opposide side option in this stage for its completion'); next_row(y); - + SubheaderParam(obj, 'title', 'Completion Params', x, y); next_row(y); case 2 % STAGE RUNNING PARAMETERS - SubheaderParam(obj, 'title', 'Stage Params', x, y); next_row(y); NumeditParam(obj, 'max_rColl_dur', 15, x, y,'label','T_Max_RCollect','TooltipString','User given max water collect time(code will optimize for value below this)'); next_row(y); NumeditParam(obj, 'min_rColl_dur', 5, x, y,'label','T_Min_RCollect','TooltipString','User given min water collect time(code will optimize for value above this)'); next_row(y); - + SubheaderParam(obj, 'title', 'Stage Params', x, y); next_row(y); % COMPLETION TEST PARAMETERS - SubheaderParam(obj, 'title', 'Completion Params', x, y); NumeditParam(obj, 'total_trials', 1000, x, y,'label','Trials','TooltipString','total trials in this stage for its completion'); next_row(y); NumeditParam(obj, 'total_trials_opp', 400, x, y,'label','Trials_Opp','TooltipString','total trials with opposide side option in this stage for its completion'); next_row(y); - + SubheaderParam(obj, 'title', 'Completion Params', x, y);next_row(y); case 3 % no completion test required % STAGE RUNNING PARAMETERS - SubheaderParam(obj, 'title', 'Stage Params', x, y); next_row(y); DispParam(obj, 'max_CP', 0.3, x, y,'label','CP_Dur_Max','TooltipString','max CP duration being trained in this stage'); next_row(y); NumeditParam(obj, 'CPfraction_inc', 0.001, x, y,'label','CP_frac_Increase','TooltipString','CP duration is increased by this fraction'); next_row(y); - + SubheaderParam(obj, 'title', 'Stage Params', x, y); next_row(y); + case 4 % STAGE RUNNING PARAMETERS - SubheaderParam(obj, 'title', 'Stage Params', x, y); next_row(y); NumeditParam(obj, 'max_CP', 1.5, x, y,'label','CP_Dur_Max','TooltipString','max CP duration being trained in this stage'); next_row(y); NumeditParam(obj, 'CPfraction_inc', 0.001, x, y,'label','CP_frac_Increase','TooltipString','CP duration is increased by this fraction'); next_row(y); - + SubheaderParam(obj, 'title', 'Stage Params', x, y); next_row(y); % COMPLETION TEST PARAMETERS - SubheaderParam(obj, 'title', 'Completion Params', x, y); NumeditParam(obj, 'recent_violation', 0.15, x, y,'label','Recent_ViolateRate','TooltipString','violation rate for last 20 trials in this stage for its completion'); next_row(y); NumeditParam(obj, 'recent_timeout', 0.15, x, y,'label','Recent_TimeoutRate','TooltipString','timeout rate for last 20 trials in this stage for its completion'); next_row(y); NumeditParam(obj, 'stage_violation', 0.35, x, y,'label','Stage_ViolationRate','TooltipString','overall violation rate in this stage for its completion'); next_row(y); - + SubheaderParam(obj, 'title', 'Completion Params', x, y);next_row(y); + case 5 % STAGE RUNNING PARAMETERS - SubheaderParam(obj, 'title', 'Stage Params', x, y); next_row(y); NumeditParam(obj, 'max_CP', 1.5, x, y,'label','CP_Dur_Max','TooltipString','max CP duration being trained in this stage'); next_row(y); NumeditParam(obj, 'CPfraction_inc', 0.002, x, y,'label','CP_frac_Increase','TooltipString','CP duration is increased by this fraction'); next_row(y); NumeditParam(obj, 'min_CP', 1.5, x, y,'label','CP_Dur_Min','TooltipString','min CP duration being trained in this stage'); next_row(y); NumeditParam(obj, 'starting_CP', 0.3, x, y,'TooltipString','min CP duration (minus the settling-in time) during warm up'); next_row(y); NumeditParam(obj, 'warm_up_trials', 10, x, y,'label','Warmup Trials','TooltipString','N trials for warmup'); next_row(y); NumeditParam(obj, 'stim_dur', 0.4, x, y,'label','A1 Dur','TooltipString','This stage A1 duration'); next_row(y); - + SubheaderParam(obj, 'title', 'Stage Params', x, y); next_row(y); % COMPLETION TEST PARAMETERS - SubheaderParam(obj, 'title', 'Completion Params', x, y); NumeditParam(obj, 'recent_violation', 0.15, x, y,'label','Recent_ViolateRate','TooltipString','violation rate for last 20 trials in this stage for its completion'); next_row(y); NumeditParam(obj, 'recent_timeout', 0.15, x, y,'label','Recent_TimeoutRate','TooltipString','timeout rate for last 20 trials in this stage for its completion'); next_row(y); NumeditParam(obj, 'stage_violation', 0.35, x, y,'label','Stage_ViolationRate','TooltipString','overall violation rate in this stage for its completion'); next_row(y); + NumeditParam(obj, 'total_trials', 1200, x, y,'label','Trials','TooltipString','total trials in this stage for its completion'); next_row(y); + SubheaderParam(obj, 'title', 'Completion Params', x, y);next_row(y); case 6 % STAGE RUNNING PARAMETERS - SubheaderParam(obj, 'title', 'Stage Params', x, y); next_row(y); NumeditParam(obj, 'max_CP', 5, x, y,'label','CP_Dur_Max','TooltipString','max CP duration being trained in this stage'); next_row(y); NumeditParam(obj, 'starting_CP', 0.3, x, y,'TooltipString','min CP duration (minus the settling-in time) during warm up'); next_row(y); NumeditParam(obj, 'warm_up_trials', 20, x, y,'label','Warmup Trials','TooltipString','N trials for warmup'); next_row(y); NumeditParam(obj, 'max_prestim', 2, x, y,'label','Pre-Stim Max','TooltipString','This stage Max Time, before starting the stimulus'); next_row(y); NumeditParam(obj, 'min_prestim', 0.2, x, y,'label','Pre-Stim Min','TooltipString','This stage Min Time, before starting the stimulus'); next_row(y); NumeditParam(obj, 'stim_dur', 0.4, x, y,'label','A1 Dur','TooltipString','This stage A1 duration'); next_row(y); - + SubheaderParam(obj, 'title', 'Stage Params', x, y); next_row(y); % COMPLETION TEST PARAMETERS - SubheaderParam(obj, 'title', 'Completion Params', x, y); NumeditParam(obj, 'recent_violation', 0.15, x, y,'label','Recent_ViolateRate','TooltipString','violation rate for last 20 trials in this stage for its completion'); next_row(y); NumeditParam(obj, 'recent_timeout', 0.15, x, y,'label','Recent_TimeoutRate','TooltipString','timeout rate for last 20 trials in this stage for its completion'); next_row(y); NumeditParam(obj, 'stage_violation', 0.25, x, y,'label','Stage_ViolationRate','TooltipString','overall violation rate in this stage for its completion'); next_row(y); NumeditParam(obj, 'total_trials', 1500, x, y,'label','Trials','TooltipString','total trials in this stage for its completion'); next_row(y); + SubheaderParam(obj, 'title', 'Completion Params', x, y);next_row(y); case 7 % STAGE RUNNING PARAMETERS - SubheaderParam(obj, 'title', 'Stage Params', x, y); next_row(y); NumeditParam(obj, 'max_CP', 5, x, y,'label','CP_Dur_Max','TooltipString','max CP duration being trained in this stage'); next_row(y); NumeditParam(obj, 'starting_CP', 0.3, x, y,'TooltipString','min CP duration (minus the settling-in time) during warm up'); next_row(y); NumeditParam(obj, 'warm_up_trials', 20, x, y,'label','Warmup Trials','TooltipString','N trials for warmup'); next_row(y); @@ -100,15 +92,15 @@ NumeditParam(obj, 'max_prego', 2, x, y,'label','Max A1-GoCue time','TooltipString','This stage Max time, between the end of the stimulus and the go cue'); next_row(y); NumeditParam(obj, 'min_prego', 0.2, x, y,'label','Min A1-GoCue time','TooltipString','This stage Min time, between the end of the stimulus and the go cue'); next_row(y); NumeditParam(obj, 'stim_dur', 0.4, x, y,'label','A1 Dur','TooltipString','This stage A1 duration'); next_row(y); - + SubheaderParam(obj, 'title', 'Stage Params', x, y); next_row(y); % COMPLETION TEST PARAMETERS - SubheaderParam(obj, 'title', 'Completion Params', x, y); NumeditParam(obj, 'recent_violation', 0.15, x, y,'label','Recent_ViolateRate','TooltipString','violation rate for last 20 trials in this stage for its completion'); next_row(y); NumeditParam(obj, 'recent_timeout', 0.15, x, y,'label','Recent_TimeoutRate','TooltipString','timeout rate for last 20 trials in this stage for its completion'); next_row(y); NumeditParam(obj, 'stage_violation', 0.20, x, y,'label','Stage_ViolationRate','TooltipString','overall violation rate in this stage for its completion'); next_row(y); NumeditParam(obj, 'total_trials', 2000, x, y,'label','Trials','TooltipString','total trials in this stage for its completion'); next_row(y); - + SubheaderParam(obj, 'title', 'Completion Params', x, y); next_row(y); end + SubheaderParam(obj, 'title', 'AUTOMATED TRAINING STAGE', x, y); case 'close' % Delete all SoloParamHandles who belong to this object and whose diff --git a/Protocols/@Arpit_CentrePokeTraining/pipeline_Arpit_CentrePokeTraining_experimenter_ratname_250319c.m b/Protocols/@Arpit_CentrePokeTraining/pipeline_Arpit_CentrePokeTraining_experimenter_ratname_250319c.m index 0c08b919..e3bc6a22 100644 --- a/Protocols/@Arpit_CentrePokeTraining/pipeline_Arpit_CentrePokeTraining_experimenter_ratname_250319c.m +++ b/Protocols/@Arpit_CentrePokeTraining/pipeline_Arpit_CentrePokeTraining_experimenter_ratname_250319c.m @@ -37,7 +37,7 @@ ParamsSection_training_stage.value = 1; callback(ParamsSection_training_stage); stage1_trial_counter = stage1_trial_counter + 1; - SessionPerformanceSection_ntrials_stage = stage1_trial_counter; + SessionPerformanceSection_ntrials_stage.value = stage1_trial_counter; callback(SessionPerformanceSection_ntrials_stage) stage1_trial_counter_today = stage1_trial_counter_today + 1; SessionPerformanceSection_ntrials_stage_today.value = stage1_trial_counter_today; @@ -56,8 +56,7 @@ % is less if n_completed_trial < 100 if stage1_trial_counter > value(Training_ParamsSection_total_trials) && stage1_trial_counter_oppSide > value(Training_ParamsSection_total_trials_opp) - sm = value(SessionDefinition_my_session_model); - SessionDefinition_my_session_model.value = jump(sm, 'offset',+1); + SessionDefinition(obj, 'jump_to_stage', 'Timeout Rewarded Side Pokes'); end end % @@ -73,8 +72,7 @@ % stage1_trial_counter_today = 0; if stage1_trial_counter > value(Training_ParamsSection_total_trials) && stage1_trial_counter_oppSide > value(Training_ParamsSection_total_trials_opp) - sm = value(SessionDefinition_my_session_model); - SessionDefinition_my_session_model.value = jump(sm, 'offset',+1); + SessionDefinition(obj, 'jump_to_stage', 'Timeout Rewarded Side Pokes'); end % end @@ -104,7 +102,12 @@ ParamsSection_training_stage.value = 2; callback(ParamsSection_training_stage); stage2_trial_counter = stage2_trial_counter + 1; +SessionPerformanceSection_ntrials_stage.value = stage2_trial_counter; +callback(SessionPerformanceSection_ntrials_stage); stage2_trial_counter_today = stage2_trial_counter_today + 1; +SessionPerformanceSection_ntrials_stage_today.value = stage2_trial_counter_today; +callback(SessionPerformanceSection_ntrials_stage_today); + if value(previous_sides(end)) ~= value(ParamsSection_ThisTrial) stage2_trial_counter_oppSide = stage2_trial_counter_oppSide + 1; opp_side_trials = opp_side_trials + 1; @@ -134,7 +137,8 @@ end stage2_timeout_percent = ((stage2_timeout_percent * stage2_trial_counter) + double(timeout_history(end)))/(stage2_trial_counter + 1); - +SessionPerformanceSection_timeout_stage.value = stage2_timeout_percent; +callback(SessionPerformanceSection_timeout_stage); % end if completion_test_eval @@ -144,8 +148,7 @@ % if n_completed_trial > 50 if stage2_trial_counter > value(Training_ParamsSection_total_trials) && stage2_trial_counter_oppSide > value(Training_ParamsSection_total_trials_opp) - sm = value(SessionDefinition_my_session_model); - SessionDefinition_my_session_model.value = jump(sm, 'offset',+1); + SessionDefinition(obj, 'jump_to_stage', 'Introduce Centre Poke'); end end % @@ -177,11 +180,9 @@ cp_min = value(ParamsSection_init_CP_duration); % Minimum increment (in secs) in center poke duration every time there is a non-cp-violation trial: cp_minimum_increment = 0.001; - stage3_trial_counter = 0; stage3_trial_counter_today = 0; stage3_timeout_percent = 0; - last_session_cp = 0; % end @@ -194,8 +195,14 @@ ParamsSection_training_stage.value = 3; callback(ParamsSection_training_stage); stage3_trial_counter = stage3_trial_counter + 1; +SessionPerformanceSection_ntrials_stage.value = stage3_trial_counter; +callback(SessionPerformanceSection_ntrials_stage); stage3_trial_counter_today = stage3_trial_counter_today + 1; +SessionPerformanceSection_ntrials_stage_today.value = stage3_trial_counter_today; +callback(SessionPerformanceSection_ntrials_stage_today); stage3_timeout_percent = ((stage3_timeout_percent * stage3_trial_counter) + double(timeout_history(end)))/(stage3_trial_counter + 1); +SessionPerformanceSection_timeout_stage.value = stage3_timeout_percent; +callback(SessionPerformanceSection_timeout_stage); % Change the value of CP Duration if n_completed_trials < 1 @@ -218,8 +225,7 @@ clear('ans'); % if value(ParamsSection_CP_duration) >= cp_max - sm = value(SessionDefinition_my_session_model); - SessionDefinition_my_session_model.value = jump(sm, 'offset',+1); + SessionDefinition(obj, 'jump_to_stage', 'Introduce Violation for Centre Poke'); end % if exist('ans', 'var') @@ -269,10 +275,17 @@ ParamsSection_training_stage.value = 4; callback(ParamsSection_training_stage); stage4_trial_counter = stage4_trial_counter + 1; +SessionPerformanceSection_ntrials_stage.value = stage4_trial_counter; +callback(SessionPerformanceSection_ntrials_stage); stage4_trial_counter_today = stage4_trial_counter_today + 1; +SessionPerformanceSection_ntrials_stage_today.value = stage4_trial_counter_today; +callback(SessionPerformanceSection_ntrials_stage_today); stage4_timeout_percent = ((stage4_timeout_percent * stage4_trial_counter) + double(timeout_history(end)))/(stage4_trial_counter + 1); stage4_violation_percent = ((stage4_violation_percent * stage4_trial_counter) + double(violation_history(end)))/(stage4_trial_counter + 1); - +SessionPerformanceSection_timeout_stage.value = stage4_timeout_percent; +callback(SessionPerformanceSection_timeout_stage); +SessionPerformanceSection_violation_stage.value = stage4_violation_percent; +callback(SessionPerformanceSection_violation_stage); % Change the value of CP Duration if n_completed_trials < 1 ParamsSection_CP_duration.value = value(ParamsSection_init_CP_duration); @@ -300,8 +313,7 @@ if value(ParamsSection_CP_duration) >= cp_max && n_completed_trials > 100 if SessionPerformanceSection_violation_recent < value(Training_ParamsSection_recent_violation) && SessionPerformanceSection_timeout_recent < value(Training_ParamsSection_recent_timeout) && ... stage4_violation_percent < value(Training_ParamsSection_stage_violation) - sm = value(SessionDefinition_my_session_model); - SessionDefinition_my_session_model.value = jump(sm, 'offset',+1); + SessionDefinition(obj, 'jump_to_stage', 'Introduce Stimuli Sound during Centre Poke'); last_session_cp = value(ParamsSection_CP_duration); end end @@ -357,10 +369,17 @@ ParamsSection_training_stage.value = 5; callback(ParamsSection_training_stage); stage5_trial_counter = stage5_trial_counter + 1; +SessionPerformanceSection_ntrials_stage.value = stage5_trial_counter; +callback(SessionPerformanceSection_ntrials_stage); stage5_trial_counter_today = stage5_trial_counter_today + 1; +SessionPerformanceSection_ntrials_stage_today.value = stage5_trial_counter_today; +callback(SessionPerformanceSection_ntrials_stage_today); stage5_timeout_percent = ((stage5_timeout_percent * stage5_trial_counter) + double(timeout_history(end)))/(stage5_trial_counter + 1); stage5_violation_percent = ((stage5_violation_percent * stage5_trial_counter) + double(violation_history(end)))/(stage5_trial_counter + 1); - +SessionPerformanceSection_timeout_stage.value = stage5_timeout_percent; +callback(SessionPerformanceSection_timeout_stage); +SessionPerformanceSection_violation_stage.value = stage5_violation_percent; +callback(SessionPerformanceSection_violation_stage); % Change the value of CP Duration % Since starting a new session then do a pre warm up to last saved cp @@ -429,11 +448,10 @@ ClearHelperVarsNotOwned(obj); clear('ans'); % -if value(ParamsSection_CP_duration) >= cp_max && stage5_trial_counter > 1000 +if value(ParamsSection_CP_duration) >= cp_max && stage5_trial_counter > value(Training_ParamsSection_total_trials) if SessionPerformanceSection_violation_recent < value(Training_ParamsSection_recent_violation) && SessionPerformanceSection_timeout_recent < value(Training_ParamsSection_recent_timeout) && ... stage5_violation_percent < value(Training_ParamsSection_stage_violation) && n_completed_trials > 100 - sm = value(SessionDefinition_my_session_model); - SessionDefinition_my_session_model.value = jump(sm, 'offset',+1); + SessionDefinition(obj, 'jump_to_stage', 'Vary Stimuli location during Centre Poke'); last_session_cp = value(ParamsSection_CP_duration); end end @@ -483,10 +501,17 @@ ParamsSection_training_stage.value = 6; callback(ParamsSection_training_stage); stage6_trial_counter = stage6_trial_counter + 1; +SessionPerformanceSection_ntrials_stage.value = stage6_trial_counter; +callback(SessionPerformanceSection_ntrials_stage); stage6_trial_counter_today = stage6_trial_counter_today + 1; +SessionPerformanceSection_ntrials_stage_today.value = stage6_trial_counter_today; +callback(SessionPerformanceSection_ntrials_stage_today); stage6_timeout_percent = ((stage6_timeout_percent * stage6_trial_counter) + double(timeout_history(end)))/(stage6_trial_counter + 1); stage6_violation_percent = ((stage6_violation_percent * stage6_trial_counter) + double(violation_history(end)))/(stage6_trial_counter + 1); - +SessionPerformanceSection_timeout_stage.value = stage6_timeout_percent; +callback(SessionPerformanceSection_timeout_stage); +SessionPerformanceSection_violation_stage.value = stage6_violation_percent; +callback(SessionPerformanceSection_violation_stage); % Warm Up If starting a new session if n_completed_trials == 0 ParamsSection_CP_duration.value = value(ParamsSection_init_CP_duration); @@ -538,8 +563,7 @@ if stage6_trial_counter > value(Training_ParamsSection_total_trials) if SessionPerformanceSection_violation_recent < value(Training_ParamsSection_recent_violation) && SessionPerformanceSection_timeout_recent < value(Training_ParamsSection_recent_timeout) && ... stage6_violation_percent < value(Training_ParamsSection_stage_violation) - sm = value(SessionDefinition_my_session_model); - SessionDefinition_my_session_model.value = jump(sm, 'offset',+1); + SessionDefinition(obj, 'jump_to_stage', 'Variable Stimuli Go Cue location during Centre Poke'); end end % @@ -597,12 +621,21 @@ GetSoloFunctionArgs(obj); ClearHelperVarsNotOwned(obj); % + ParamsSection_training_stage.value = 7; callback(ParamsSection_training_stage); stage7_trial_counter = stage7_trial_counter + 1; +SessionPerformanceSection_ntrials_stage.value = stage7_trial_counter; +callback(SessionPerformanceSection_ntrials_stage); stage7_trial_counter_today = stage7_trial_counter_today + 1; +SessionPerformanceSection_ntrials_stage_today.value = stage7_trial_counter_today; +callback(SessionPerformanceSection_ntrials_stage_today); stage7_timeout_percent = ((stage7_timeout_percent * stage7_trial_counter) + double(timeout_history(end)))/(stage7_trial_counter + 1); stage7_violation_percent = ((stage7_violation_percent * stage7_trial_counter) + double(violation_history(end)))/(stage7_trial_counter + 1); +SessionPerformanceSection_timeout_stage.value = stage7_timeout_percent; +callback(SessionPerformanceSection_timeout_stage); +SessionPerformanceSection_violation_stage.value = stage7_violation_percent; +callback(SessionPerformanceSection_violation_stage); % Warm Up If starting a new session if warm_up_on == 1 @@ -653,8 +686,7 @@ if stage7_trial_counter > value(Training_ParamsSection_total_trials) if SessionPerformanceSection_violation_recent < value(Training_ParamsSection_recent_violation) && SessionPerformanceSection_timeout_recent < value(Training_ParamsSection_recent_timeout) && ... stage7_violation_percent < value(Training_ParamsSection_stage_violation) - sm = value(SessionDefinition_my_session_model); - SessionDefinition_my_session_model.value = jump(sm, 'offset',+1); + SessionDefinition(obj, 'jump_to_stage', 'User Setting'); end end % @@ -713,11 +745,19 @@ % ParamsSection_training_stage.value = 8; +callback(ParamsSection_training_stage); stage8_trial_counter = stage8_trial_counter + 1; +SessionPerformanceSection_ntrials_stage.value = stage8_trial_counter; +callback(SessionPerformanceSection_ntrials_stage); stage8_trial_counter_today = stage8_trial_counter_today + 1; +SessionPerformanceSection_ntrials_stage_today.value = stage8_trial_counter_today; +callback(SessionPerformanceSection_ntrials_stage_today); stage8_timeout_percent = ((stage8_timeout_percent * stage8_trial_counter) + double(timeout_history(end)))/(stage8_trial_counter + 1); stage8_violation_percent = ((stage8_violation_percent * stage8_trial_counter) + double(violation_history(end)))/(stage8_trial_counter + 1); - +SessionPerformanceSection_timeout_stage.value = stage8_timeout_percent; +callback(SessionPerformanceSection_timeout_stage); +SessionPerformanceSection_violation_stage.value = stage8_violation_percent; +callback(SessionPerformanceSection_violation_stage); % Warm Up If starting a new session if warm_up_on == 1 From fae1f7a2d30a48208de3d20d14c1fc4c8549f997 Mon Sep 17 00:00:00 2001 From: viktorpm Date: Wed, 26 Mar 2025 17:58:55 +0000 Subject: [PATCH 035/164] error debug --- .../Arpit_CentrePokeTraining.m | 2 +- .../Arpit_CentrePokeTrainingSMA.m | 16 ++++++++-------- .../@Arpit_CentrePokeTraining/ParamsSection.m | 1 + ...rePokeTraining_experimenter_ratname_250319c.m | 6 +++--- 4 files changed, 13 insertions(+), 12 deletions(-) diff --git a/Protocols/@Arpit_CentrePokeTraining/Arpit_CentrePokeTraining.m b/Protocols/@Arpit_CentrePokeTraining/Arpit_CentrePokeTraining.m index aad46392..9a8b28fc 100644 --- a/Protocols/@Arpit_CentrePokeTraining/Arpit_CentrePokeTraining.m +++ b/Protocols/@Arpit_CentrePokeTraining/Arpit_CentrePokeTraining.m @@ -137,7 +137,7 @@ Arpit_CentrePokeTrainingSMA(obj, 'init'); SessionDefinition(obj, 'init', x, y, value(myfig)); next_row(y, 2); %#ok - SessionDefinition(obj, 'set_old_style_parsing_flag',0); + % SessionDefinition(obj, 'set_old_style_parsing_flag',0); feval(mfilename, obj, 'prepare_next_trial'); diff --git a/Protocols/@Arpit_CentrePokeTraining/Arpit_CentrePokeTrainingSMA.m b/Protocols/@Arpit_CentrePokeTraining/Arpit_CentrePokeTrainingSMA.m index cd595beb..db6cf911 100644 --- a/Protocols/@Arpit_CentrePokeTraining/Arpit_CentrePokeTrainingSMA.m +++ b/Protocols/@Arpit_CentrePokeTraining/Arpit_CentrePokeTrainingSMA.m @@ -130,7 +130,7 @@ 'input_to_statechange',{'Tup','second_hit_state'; HitEvent,'hit_state'}); sma = add_state(sma,'name','hit_state','self_timer',0.01,... - 'output_actions', {'SchedWaveTrig','reward_delivery + Go_Cue'},... + 'output_actions', {'SchedWaveTrig','reward_delivery+Go_Cue'},... 'input_to_statechange',{'Tup','drink_state'}); sma = add_state(sma,'name','drink_state','self_timer',drink_time,... @@ -156,7 +156,7 @@ 'input_to_statechange',{'reward_collection_dur_In', 'timeout_state'; 'Tup','timeout_state'; HitEvent,'hit_state'}); sma = add_state(sma,'name','hit_state','self_timer',0.01,... - 'output_actions', {'SchedWaveTrig','reward_delivery + Go_Cue'},... + 'output_actions', {'SchedWaveTrig','reward_delivery+Go_Cue'},... 'input_to_statechange',{'Tup','drink_state'}); sma = add_state(sma,'name','drink_state','self_timer',drink_time,... @@ -200,7 +200,7 @@ 'input_to_statechange', {'CP_Duration_wave_In','side_led_wait_RewardCollection'; 'Cout','current_state - 1';'Tup','side_led_wait_RewardCollection'}); sma = add_state(sma, 'name', 'side_led_wait_RewardCollection', 'self_timer', SideLed_duration + RewardCollection_duration, ... - 'output_actions', {'DOut', SideLight; 'SchedWaveTrig', 'reward_collection_dur + Go_Cue'}, ... + 'output_actions', {'DOut', SideLight; 'SchedWaveTrig', 'reward_collection_dur+Go_Cue'}, ... 'input_to_statechange',{HitEvent,'hit_state';'Tup','timeout_state'; ErrorEvent,'second_hit_state'}); sma = add_state(sma,'name','second_hit_state','self_timer',RewardCollection_duration,... @@ -256,7 +256,7 @@ else % the usual state machine sma = add_state(sma,'name','settling_in_state','self_timer',SettlingIn_time, ... - 'output_actions', {'SchedWaveTrig', settling_period}, ... + 'output_actions', {'SchedWaveTrig', 'settling_period'}, ... 'input_to_statechange', {'Cout','current_state + 1';'Tup','soft_cp'}); % Intermediate State @@ -298,7 +298,7 @@ % sound before the go cue so a new scheduled wave sma = add_state(sma,'name','soft_cp','self_timer',CP_duration - SettlingIn_time, ... CP_Duration_wave - 'output_actions', {'SchedWaveTrig', 'CP_Duration_wave + stimplay'},... + 'output_actions', {'SchedWaveTrig', 'CP_Duration_wave+stimplay'},... 'input_to_statechange', {'Cout','current_state + 1'; 'Tup','legal_poke_end_state';... 'Rin', 'violation_state'; 'Rout', 'violation_state'; 'Lin', 'violation_state'; 'Lout', 'violation_state'}); %more stringent by giving half the legal cp time @@ -326,12 +326,12 @@ 'input_to_statechange', {'Cin','side_led_wait_RewardCollection'; 'Tup','violation_state'}); sma = add_state(sma, 'name', 'side_led_wait_RewardCollection', 'self_timer', SideLed_duration + RewardCollection_duration, ... - 'output_actions', {'DOut', SideLight; 'SchedWaveTrig', 'reward_collection_dur + Go_Cue'}, ... + 'output_actions', {'DOut', SideLight; 'SchedWaveTrig', 'reward_collection_dur+Go_Cue'}, ... 'input_to_statechange',{HitEvent,'hit_state'; 'Tup','timeout_state'; ErrorEvent,'second_hit_state'}); sma = add_state(sma,'name','second_hit_state','self_timer',RewardCollection_duration,... 'output_actions',{'DOut', SideLight},... - 'input_to_statechange',{'reward_collection_dur_In', 'timeout_state'; 'Tup','punish_state'; HitEvent,'hit_state'}); + 'input_to_statechange',{'reward_collection_dur_In', 'timeout_state'; 'Tup','timeout_state'; HitEvent,'hit_state'}); sma = add_state(sma,'name','hit_state','self_timer',0.01,... 'output_actions', {'SchedWaveTrig','reward_delivery'},... @@ -351,7 +351,7 @@ % For Violations sma = add_state(sma,'name','violation_state','self_timer',viol_snd_duration,... - 'output_actions',{'SoundOut',viol_sound_id; 'DOut', center1led; 'SchedWaveTrig', '-Go_Cue -Stimplay -CP_Duration_wave'},... + 'output_actions',{'SoundOut',viol_sound_id; 'DOut', center1led; 'SchedWaveTrig', '-Go_Cue-stimplay-CP_Duration_wave'},... 'input_to_statechange', {'Tup', 'current_state+1'}); sma = add_state(sma, 'self_timer', max(0.001, violation_iti-viol_snd_duration), ... 'input_to_statechange',{'Tup','preclean_up_state'}); diff --git a/Protocols/@Arpit_CentrePokeTraining/ParamsSection.m b/Protocols/@Arpit_CentrePokeTraining/ParamsSection.m index a1cd860b..5362daf0 100644 --- a/Protocols/@Arpit_CentrePokeTraining/ParamsSection.m +++ b/Protocols/@Arpit_CentrePokeTraining/ParamsSection.m @@ -209,6 +209,7 @@ enable(training_stage); % user can change the training stages SessionDefinition(obj, 'jump_to_stage',value(training_stage)); [stage_fig_x,stage_fig_y] = Training_ParamsSection(obj, 'reinit', value(stage_fig_x),value(stage_fig_y)); % update the training params as well + Arpit_CentrePokeTrainingSMA(obj,'reinit'); switch value(training_stage) diff --git a/Protocols/@Arpit_CentrePokeTraining/pipeline_Arpit_CentrePokeTraining_experimenter_ratname_250319c.m b/Protocols/@Arpit_CentrePokeTraining/pipeline_Arpit_CentrePokeTraining_experimenter_ratname_250319c.m index e3bc6a22..ebd0f689 100644 --- a/Protocols/@Arpit_CentrePokeTraining/pipeline_Arpit_CentrePokeTraining_experimenter_ratname_250319c.m +++ b/Protocols/@Arpit_CentrePokeTraining/pipeline_Arpit_CentrePokeTraining_experimenter_ratname_250319c.m @@ -54,7 +54,7 @@ % % only run it if its the start of the day, so number of trials the rat did % is less -if n_completed_trial < 100 +if n_completed_trials < 100 if stage1_trial_counter > value(Training_ParamsSection_total_trials) && stage1_trial_counter_oppSide > value(Training_ParamsSection_total_trials_opp) SessionDefinition(obj, 'jump_to_stage', 'Timeout Rewarded Side Pokes'); end @@ -146,7 +146,7 @@ ClearHelperVarsNotOwned(obj); clear('ans'); % -if n_completed_trial > 50 +if n_completed_trials > 50 if stage2_trial_counter > value(Training_ParamsSection_total_trials) && stage2_trial_counter_oppSide > value(Training_ParamsSection_total_trials_opp) SessionDefinition(obj, 'jump_to_stage', 'Introduce Centre Poke'); end @@ -448,7 +448,7 @@ ClearHelperVarsNotOwned(obj); clear('ans'); % -if value(ParamsSection_CP_duration) >= cp_max && stage5_trial_counter > value(Training_ParamsSection_total_trials) +if value(ParamsSection_CP_duration) >= value(Training_ParamsSection_max_CP) && stage5_trial_counter > value(Training_ParamsSection_total_trials) if SessionPerformanceSection_violation_recent < value(Training_ParamsSection_recent_violation) && SessionPerformanceSection_timeout_recent < value(Training_ParamsSection_recent_timeout) && ... stage5_violation_percent < value(Training_ParamsSection_stage_violation) && n_completed_trials > 100 SessionDefinition(obj, 'jump_to_stage', 'Vary Stimuli location during Centre Poke'); From b66b40030acbb41461673c30af3c16a656c2afb4 Mon Sep 17 00:00:00 2001 From: ArpitAgarwal Date: Fri, 28 Mar 2025 23:19:03 +0000 Subject: [PATCH 036/164] corrected state machine for error and edited stage 1 to run without user action. Need to remove the changes in stage 1. --- .../Arpit_CentrePokeTrainingSMA.m | 106 ++++++++++-------- ...okeTraining_experimenter_ratname_250319c.m | 94 ++++++++-------- 2 files changed, 106 insertions(+), 94 deletions(-) diff --git a/Protocols/@Arpit_CentrePokeTraining/Arpit_CentrePokeTrainingSMA.m b/Protocols/@Arpit_CentrePokeTraining/Arpit_CentrePokeTrainingSMA.m index db6cf911..01ffffbb 100644 --- a/Protocols/@Arpit_CentrePokeTraining/Arpit_CentrePokeTrainingSMA.m +++ b/Protocols/@Arpit_CentrePokeTraining/Arpit_CentrePokeTrainingSMA.m @@ -100,11 +100,10 @@ end - sma = add_scheduled_wave(sma, 'name', 'settling_period', 'preamble', SettlingIn_time); % intial fidgety period without violation - - if CP_duration > (SettlingIn_time + legal_cbreak) + if CP_duration <= (SettlingIn_time + legal_cbreak) sma = add_scheduled_wave(sma, 'name', 'CP_Duration_wave', 'preamble', CP_duration); % total length of centre poke to consider success else + sma = add_scheduled_wave(sma, 'name', 'settling_period', 'preamble', SettlingIn_time); % intial fidgety period without violation sma = add_scheduled_wave(sma, 'name', 'CP_Duration_wave', 'preamble', CP_duration - SettlingIn_time); % total length of centre poke minus the inital fidgety time to consider success end @@ -120,19 +119,30 @@ case 1 % LEARNING THE REWARD SOUND ASSOCIATION -LEFT OR RIGHT LED ON -> POKE -> SOUND+REWARD GIVEN % INFINITE TIME AND CHANCES TO SELF CORRECT - sma = add_state(sma, 'name', 'side_led_wait_RewardCollection', 'self_timer', SideLed_duration + RewardCollection_duration, ... + sma = add_state(sma, 'name', 'side_led_wait_RewardCollection', 'self_timer', 2, ... 'output_actions', {'DOut', SideLight}, ... - 'input_to_statechange',{HitEvent,'hit_state';'Tup','side_led_wait_RewardCollection'; ErrorEvent,'second_hit_state'}); - + 'input_to_statechange',{'Tup','hit_state'}); - sma = add_state(sma,'name','second_hit_state','self_timer',RewardCollection_duration,... + sma = add_state(sma,'name','second_hit_state','self_timer',3,... 'output_actions',{'DOut', SideLight},... - 'input_to_statechange',{'Tup','second_hit_state'; HitEvent,'hit_state'}); + 'input_to_statechange',{'Tup','hit_state'}); sma = add_state(sma,'name','hit_state','self_timer',0.01,... 'output_actions', {'SchedWaveTrig','reward_delivery+Go_Cue'},... 'input_to_statechange',{'Tup','drink_state'}); + % sma = add_state(sma, 'name', 'side_led_wait_RewardCollection', 'self_timer', SideLed_duration + RewardCollection_duration, ... + % 'output_actions', {'DOut', SideLight}, ... + % 'input_to_statechange',{HitEvent,'hit_state';'Tup','side_led_wait_RewardCollection'; ErrorEvent,'second_hit_state'}); + % + % sma = add_state(sma,'name','second_hit_state','self_timer',RewardCollection_duration,... + % 'output_actions',{'DOut', SideLight},... + % 'input_to_statechange',{'Tup','second_hit_state'; HitEvent,'hit_state'}); + % + % sma = add_state(sma,'name','hit_state','self_timer',0.01,... + % 'output_actions', {'SchedWaveTrig','reward_delivery+Go_Cue'},... + % 'input_to_statechange',{'Tup','drink_state'}); + sma = add_state(sma,'name','drink_state','self_timer',drink_time,... 'input_to_statechange',{'Tup','preclean_up_state'}); @@ -261,69 +271,71 @@ % Intermediate State - % This intermediate state is considering the poke is out before the end of settling time / at the start of this state - sma = add_state(sma,'self_timer',SettlingIn_time,'output_actions', {'DOut', center1led}, ... - 'input_to_statechange', {'settling_period_In','legal_poke_start_state';'Cin','current_state + 1';'Tup','legal_poke_start_state';... - 'Rin', 'violation_state';'Rout', 'violation_state'; 'Lin', 'violation_state';'Lout', 'violation_state'}); - - % The state jump to here when the nose is still in then go directly to soft_cp - sma = add_state(sma,'self_timer',SettlingIn_time,... - 'input_to_statechange', {'settling_period_In','soft_cp'; 'Cout','current_state - 1';'Tup','soft_cp';... - 'Rin', 'violation_state'; 'Rout', 'violation_state'; 'Lin', 'violation_state'; 'Lout', 'violation_state'}); + % This intermediate state is considering the poke is out before the end of settling time / at the start of this state + sma = add_state(sma,'self_timer',SettlingIn_time,'output_actions', {'DOut', center1led}, ... + 'input_to_statechange', {'settling_period_In','legal_poke_start_state';'Cin','current_state + 1';'Tup','legal_poke_start_state';... + 'Rin', 'violation_state';'Rout', 'violation_state'; 'Lin', 'violation_state';'Lout', 'violation_state'}); - end + % The state jump to here when the nose is still in then go directly to soft_cp + sma = add_state(sma,'self_timer',SettlingIn_time,... + 'input_to_statechange', {'settling_period_In','soft_cp'; 'Cout','current_state - 1';'Tup','soft_cp';... + 'Rin', 'violation_state'; 'Rout', 'violation_state'; 'Lin', 'violation_state'; 'Lout', 'violation_state'}); - %%%%%%%%%%%%% SETTLING IN STATE END %%%%%%%%%%%%%%%%%%%%%% + %%%%%%%%%%%%% SETTLING IN STATE END %%%%%%%%%%%%%%%%%%%%%% - % STATE TO CHECK BEFORE START OF LEGAL POKE PERIOD + % STATE TO CHECK BEFORE START OF LEGAL POKE PERIOD - sma = add_state(sma,'name','legal_poke_start_state','self_timer',legal_cbreak/2, ... - 'output_actions', {'DOut', center1led}, ... - 'input_to_statechange', {'Cin','soft_cp'; 'Tup','violation_state';... - 'Rin', 'violation_state'; 'Rout', 'violation_state'; 'Lin', 'violation_state'; 'Lout', 'violation_state'}); %more stringent by giving half the legal cp time + sma = add_state(sma,'name','legal_poke_start_state','self_timer',legal_cbreak/2, ... + 'output_actions', {'DOut', center1led}, ... + 'input_to_statechange', {'Cin','soft_cp'; 'Tup','violation_state';... + 'Rin', 'violation_state'; 'Rout', 'violation_state'; 'Lin', 'violation_state'; 'Lout', 'violation_state'}); %more stringent by giving half the legal cp time - %%%%%%%%%%%% LEGAL SOFT POKE STATE START %%%%%%%%%%%%%%%%% + %%%%%%%%%%%% LEGAL SOFT POKE STATE START %%%%%%%%%%%%%%%%% - if value(training_stage) == 4 + if value(training_stage) == 4 % Soft poke with violation - sma = add_state(sma,'name','soft_cp','self_timer',CP_duration - SettlingIn_time, ... CP_Duration_wave - 'output_actions', {'SchedWaveTrig', 'CP_Duration_wave'},... - 'input_to_statechange', {'Cout','current_state + 1'; 'Tup','legal_poke_end_state';... - 'Rin', 'violation_state'; 'Rout', 'violation_state'; 'Lin', 'violation_state'; 'Lout', 'violation_state'}); %more stringent by giving half the legal cp time + sma = add_state(sma,'name','soft_cp','self_timer',CP_duration - SettlingIn_time, ... CP_Duration_wave + 'output_actions', {'SchedWaveTrig', 'CP_Duration_wave'},... + 'input_to_statechange', {'Cout','current_state + 1'; 'Tup','legal_poke_end_state';... + 'Rin', 'violation_state'; 'Rout', 'violation_state'; 'Lin', 'violation_state'; 'Lout', 'violation_state'}); %more stringent by giving half the legal cp time - else % Soft poke with violation. The difference between this and previous stage is introduction of + else % Soft poke with violation. The difference between this and previous stage is introduction of % sound before the go cue so a new scheduled wave - sma = add_state(sma,'name','soft_cp','self_timer',CP_duration - SettlingIn_time, ... CP_Duration_wave - 'output_actions', {'SchedWaveTrig', 'CP_Duration_wave+stimplay'},... - 'input_to_statechange', {'Cout','current_state + 1'; 'Tup','legal_poke_end_state';... - 'Rin', 'violation_state'; 'Rout', 'violation_state'; 'Lin', 'violation_state'; 'Lout', 'violation_state'}); %more stringent by giving half the legal cp time + sma = add_state(sma,'name','soft_cp','self_timer',CP_duration - SettlingIn_time, ... CP_Duration_wave + 'output_actions', {'SchedWaveTrig', 'CP_Duration_wave+stimplay'},... + 'input_to_statechange', {'Cout','current_state + 1'; 'Tup','side_led_wait_RewardCollection';... + 'Rin', 'violation_state'; 'Rout', 'violation_state'; 'Lin', 'violation_state'; 'Lout', 'violation_state'}); %more stringent by giving half the legal cp time - end + end - % Intermediate State + % Intermediate State - % This intermediate state is considering the poke is out before the end of settling time / at the start of this state - sma = add_state(sma,'self_timer',legal_cbreak,'output_actions', {'DOut', center1led}, ... + % This intermediate state is considering the poke is out before the end of settling time / at the start of this state + sma = add_state(sma,'self_timer',legal_cbreak,'output_actions', {'DOut', center1led}, ... 'input_to_statechange', {'CP_Duration_wave_In','legal_poke_end_state';'Cin','current_state + 1';'Tup','violation_state';... 'Rin', 'violation_state';'Rout', 'violation_state'; 'Lin', 'violation_state';'Lout', 'violation_state'}); - % The state jump to here when the nose is still in then go directly to soft_cp - sma = add_state(sma,'self_timer',CP_duration - SettlingIn_time, ... + % The state jump to here when the nose is still in then go directly to soft_cp + sma = add_state(sma,'self_timer',CP_duration - SettlingIn_time, ... 'input_to_statechange', {'CP_Duration_wave_In','side_led_wait_RewardCollection';'Cout','current_state - 1';'Tup','side_led_wait_RewardCollection';... 'Rin', 'violation_state';'Rout', 'violation_state'; 'Lin', 'violation_state';'Lout', 'violation_state'}); - %%%%%%%%%%%% LEGAL SOFT POKE STATE END %%%%%%%%%%%%%%%%% + %%%%%%%%%%%% LEGAL SOFT POKE STATE END %%%%%%%%%%%%%%%%% - % Before giving the reward check if its still centre poking or pokes - % within legal c_break other wise its a violation + % Before giving the reward check if its still centre poking or pokes + % within legal c_break other wise its a violation - sma = add_state(sma,'name','legal_poke_end_state','self_timer',legal_cbreak/2, ... - 'output_actions', {'DOut', center1led}, ... - 'input_to_statechange', {'Cin','side_led_wait_RewardCollection'; 'Tup','violation_state'}); + sma = add_state(sma,'name','legal_poke_end_state','self_timer',legal_cbreak/2, ... + 'output_actions', {'DOut', center1led}, ... + 'input_to_statechange', {'Cin','side_led_wait_RewardCollection'; 'Tup','violation_state'}); + + end + + %%%%%%%%%%%%%%% REWARD COLLECTION STATE START %%%%%%%%%%%%%%% sma = add_state(sma, 'name', 'side_led_wait_RewardCollection', 'self_timer', SideLed_duration + RewardCollection_duration, ... 'output_actions', {'DOut', SideLight; 'SchedWaveTrig', 'reward_collection_dur+Go_Cue'}, ... diff --git a/Protocols/@Arpit_CentrePokeTraining/pipeline_Arpit_CentrePokeTraining_experimenter_ratname_250319c.m b/Protocols/@Arpit_CentrePokeTraining/pipeline_Arpit_CentrePokeTraining_experimenter_ratname_250319c.m index ebd0f689..61ecc199 100644 --- a/Protocols/@Arpit_CentrePokeTraining/pipeline_Arpit_CentrePokeTraining_experimenter_ratname_250319c.m +++ b/Protocols/@Arpit_CentrePokeTraining/pipeline_Arpit_CentrePokeTraining_experimenter_ratname_250319c.m @@ -23,9 +23,9 @@ GetSoloFunctionArgs(obj); ClearHelperVarsNotOwned(obj); % - stage1_trial_counter = 0; - stage1_trial_counter_today = 0; - stage1_trial_counter_oppSide = 0; + stage1_trial_counter.value = 0; + stage1_trial_counter_today.value = 0; + stage1_trial_counter_oppSide.value = 0; % end if stage_algorithm_eval @@ -36,14 +36,14 @@ callback(ParamsSection_MaxSame); ParamsSection_training_stage.value = 1; callback(ParamsSection_training_stage); - stage1_trial_counter = stage1_trial_counter + 1; - SessionPerformanceSection_ntrials_stage.value = stage1_trial_counter; + stage1_trial_counter.value = value(stage1_trial_counter) + 1; + SessionPerformanceSection_ntrials_stage.value = value(stage1_trial_counter); callback(SessionPerformanceSection_ntrials_stage) - stage1_trial_counter_today = stage1_trial_counter_today + 1; - SessionPerformanceSection_ntrials_stage_today.value = stage1_trial_counter_today; + stage1_trial_counter_today.value = value(stage1_trial_counter_today) + 1; + SessionPerformanceSection_ntrials_stage_today.value = value(stage1_trial_counter_today); callback(SessionPerformanceSection_ntrials_stage_today) if value(previous_sides(end)) ~= value(ParamsSection_ThisTrial) - stage1_trial_counter_oppSide = stage1_trial_counter_oppSide + 1; + stage1_trial_counter_oppSide.value = value(stage1_trial_counter_oppSide) + 1; end % end @@ -55,7 +55,7 @@ % only run it if its the start of the day, so number of trials the rat did % is less if n_completed_trials < 100 - if stage1_trial_counter > value(Training_ParamsSection_total_trials) && stage1_trial_counter_oppSide > value(Training_ParamsSection_total_trials_opp) + if value(stage1_trial_counter) > value(Training_ParamsSection_total_trials) && value(stage1_trial_counter_oppSide) > value(Training_ParamsSection_total_trials_opp) SessionDefinition(obj, 'jump_to_stage', 'Timeout Rewarded Side Pokes'); end end @@ -70,8 +70,8 @@ GetSoloFunctionArgs(obj); ClearHelperVarsNotOwned(obj); % -stage1_trial_counter_today = 0; -if stage1_trial_counter > value(Training_ParamsSection_total_trials) && stage1_trial_counter_oppSide > value(Training_ParamsSection_total_trials_opp) +stage1_trial_counter_today.value = 0; +if value(stage1_trial_counter) > value(Training_ParamsSection_total_trials) && value(stage1_trial_counter_oppSide) > value(Training_ParamsSection_total_trials_opp) SessionDefinition(obj, 'jump_to_stage', 'Timeout Rewarded Side Pokes'); end % @@ -86,11 +86,11 @@ GetSoloFunctionArgs(obj); ClearHelperVarsNotOwned(obj); % -stage2_trial_counter = 0; -stage2_trial_counter_today = 0; -stage2_trial_counter_oppSide = 0; -opp_side_trials = 0; -stage2_timeout_percent = 0; +stage2_trial_counter.value = 0; +stage2_trial_counter_today.value = 0; +stage2_trial_counter_oppSide.value = 0; +opp_side_trials.value = 0; +stage2_timeout_percent.value = 0; % end if stage_algorithm_eval @@ -101,31 +101,31 @@ callback(ParamsSection_MaxSame); ParamsSection_training_stage.value = 2; callback(ParamsSection_training_stage); -stage2_trial_counter = stage2_trial_counter + 1; -SessionPerformanceSection_ntrials_stage.value = stage2_trial_counter; +stage2_trial_counter.value = value(stage2_trial_counter) + 1; +SessionPerformanceSection_ntrials_stage.value = value(stage2_trial_counter); callback(SessionPerformanceSection_ntrials_stage); -stage2_trial_counter_today = stage2_trial_counter_today + 1; -SessionPerformanceSection_ntrials_stage_today.value = stage2_trial_counter_today; +stage2_trial_counter_today.value = value(stage2_trial_counter_today) + 1; +SessionPerformanceSection_ntrials_stage_today.value = value(stage2_trial_counter_today); callback(SessionPerformanceSection_ntrials_stage_today); if value(previous_sides(end)) ~= value(ParamsSection_ThisTrial) - stage2_trial_counter_oppSide = stage2_trial_counter_oppSide + 1; - opp_side_trials = opp_side_trials + 1; + stage2_trial_counter_oppSide.value = value(stage2_trial_counter_oppSide) + 1; + opp_side_trials.value = value(opp_side_trials) + 1; end % Update the reward collection time based upon behav if size(timeout_history) > 5 - if all(value(timeout_history(end-1:end))) && opp_side_trials >= 2 + if all(value(timeout_history(end-1:end))) && value(opp_side_trials) >= 2 ParamsSection_RewardCollection_duration.value = min([value(ParamsSection_RewardCollection_duration) + 1,... value(Training_ParamsSection_max_rColl_dur)]); callback(ParamsSection_RewardCollection_duration); - opp_side_trials = 0; + opp_side_trials.value = 0; end - if ~any(value(timeout_history(end-1:end))) && opp_side_trials >= 2 + if ~any(value(timeout_history(end-1:end))) && value(opp_side_trials) >= 2 ParamsSection_RewardCollection_duration.value = max([value(ParamsSection_RewardCollection_duration) - 1,... value(Training_ParamsSection_min_rColl_dur)]); callback(ParamsSection_RewardCollection_duration); - opp_side_trials = 0; + opp_side_trials.value = 0; end end @@ -136,8 +136,8 @@ end end -stage2_timeout_percent = ((stage2_timeout_percent * stage2_trial_counter) + double(timeout_history(end)))/(stage2_trial_counter + 1); -SessionPerformanceSection_timeout_stage.value = stage2_timeout_percent; +stage2_timeout_percent.value = ((value(stage2_timeout_percent) * value(stage2_trial_counter)) + double(timeout_history(end)))/(value(stage2_trial_counter) + 1); +SessionPerformanceSection_timeout_stage.value = value(stage2_timeout_percent); callback(SessionPerformanceSection_timeout_stage); % end @@ -147,7 +147,7 @@ clear('ans'); % if n_completed_trials > 50 - if stage2_trial_counter > value(Training_ParamsSection_total_trials) && stage2_trial_counter_oppSide > value(Training_ParamsSection_total_trials_opp) + if value(stage2_trial_counter) > value(Training_ParamsSection_total_trials) && value(stage2_trial_counter_oppSide) > value(Training_ParamsSection_total_trials_opp) SessionDefinition(obj, 'jump_to_stage', 'Introduce Centre Poke'); end end @@ -162,7 +162,7 @@ GetSoloFunctionArgs(obj); ClearHelperVarsNotOwned(obj); % -stage2_trial_counter_today = 0; +stage2_trial_counter_today.value = 0; % end % @@ -176,14 +176,14 @@ ClearHelperVarsNotOwned(obj); % % Maximum & Minimum duration of center poke, in secs: -cp_max = value(ParamsSection_SettlingIn_time) + value(ParamsSection_legal_cbreak); -cp_min = value(ParamsSection_init_CP_duration); +cp_max.value = value(ParamsSection_SettlingIn_time) + value(ParamsSection_legal_cbreak); +cp_min.value = value(ParamsSection_init_CP_duration); % Minimum increment (in secs) in center poke duration every time there is a non-cp-violation trial: -cp_minimum_increment = 0.001; -stage3_trial_counter = 0; -stage3_trial_counter_today = 0; -stage3_timeout_percent = 0; -last_session_cp = 0; +cp_minimum_increment.value = 0.001; +stage3_trial_counter.value = 0; +stage3_trial_counter_today.value = 0; +stage3_timeout_percent.value = 0; +last_session_cp.value = 0; % end if stage_algorithm_eval @@ -194,21 +194,21 @@ callback(ParamsSection_MaxSame); ParamsSection_training_stage.value = 3; callback(ParamsSection_training_stage); -stage3_trial_counter = stage3_trial_counter + 1; -SessionPerformanceSection_ntrials_stage.value = stage3_trial_counter; +stage3_trial_counter.value = value(stage3_trial_counter) + 1; +SessionPerformanceSection_ntrials_stage.value = value(stage3_trial_counter); callback(SessionPerformanceSection_ntrials_stage); -stage3_trial_counter_today = stage3_trial_counter_today + 1; -SessionPerformanceSection_ntrials_stage_today.value = stage3_trial_counter_today; +stage3_trial_counter_today.value = value(stage3_trial_counter_today) + 1; +SessionPerformanceSection_ntrials_stage_today.value = value(stage3_trial_counter_today); callback(SessionPerformanceSection_ntrials_stage_today); -stage3_timeout_percent = ((stage3_timeout_percent * stage3_trial_counter) + double(timeout_history(end)))/(stage3_trial_counter + 1); -SessionPerformanceSection_timeout_stage.value = stage3_timeout_percent; +stage3_timeout_percent.value = ((value(stage3_timeout_percent) * value(stage3_trial_counter)) + double(timeout_history(end)))/(value(stage3_trial_counter) + 1); +SessionPerformanceSection_timeout_stage.value = value(stage3_timeout_percent); callback(SessionPerformanceSection_timeout_stage); % Change the value of CP Duration if n_completed_trials < 1 ParamsSection_CP_duration.value = value(ParamsSection_init_CP_duration); else - if ~timeout_history(end) && value(ParamsSection_CP_duration) < cp_max + if ~timeout_history(end) && value(ParamsSection_CP_duration) < value(cp_max) increment = value(ParamsSection_CP_duration) * value(Training_ParamsSection_CPfraction_inc); if increment < value(cp_minimum_increment) increment = value(cp_minimum_increment); @@ -224,7 +224,7 @@ ClearHelperVarsNotOwned(obj); clear('ans'); % -if value(ParamsSection_CP_duration) >= cp_max +if value(ParamsSection_CP_duration) >= value(cp_max) SessionDefinition(obj, 'jump_to_stage', 'Introduce Violation for Centre Poke'); end % @@ -238,8 +238,8 @@ GetSoloFunctionArgs(obj); ClearHelperVarsNotOwned(obj); % -stage3_trial_counter_today = 0; -last_session_cp = value(ParamsSection_CP_duration); +stage3_trial_counter_today.value = 0; +last_session_cp.value = value(ParamsSection_CP_duration); % end % From 117067e46f177638ce7624f86536a28a1a8c57f7 Mon Sep 17 00:00:00 2001 From: viktorpm Date: Sat, 29 Mar 2025 11:30:03 +0000 Subject: [PATCH 037/164] changes made to create_helper_var function in sessionmodel2 because of runtime error of same name of varivale and function --- .../Plugins/@sessionmodel2/CreateHelperVar.m | 21 +- ...eTraining_experimenter_ratname_250319c.asv | 925 ++++++++++++++++++ ...okeTraining_experimenter_ratname_250319c.m | 26 +- 3 files changed, 954 insertions(+), 18 deletions(-) create mode 100644 Protocols/@Arpit_CentrePokeTraining/pipeline_Arpit_CentrePokeTraining_experimenter_ratname_250319c.asv diff --git a/ExperPort/Plugins/@sessionmodel2/CreateHelperVar.m b/ExperPort/Plugins/@sessionmodel2/CreateHelperVar.m index 08f163ad..ebc28586 100644 --- a/ExperPort/Plugins/@sessionmodel2/CreateHelperVar.m +++ b/ExperPort/Plugins/@sessionmodel2/CreateHelperVar.m @@ -7,8 +7,8 @@ function CreateHelperVar(obj, varname, varargin) % % 'value', varval: Sets the helper var to varval if the helper var does % not already exist. If this option is not specified, varval defaults to -% the empty matrix. -% +% the empty matrix. +% % 'force_init', true: Forces the helper var to be set to the specified % value. If this option is not specified, 'force_init' is assumed to be % false. @@ -21,11 +21,22 @@ function CreateHelperVar(obj, varname, varargin) elseif ~isvarname(varname) error('varname has to be a valid MATLAB variable name'); end - +%% ARPIT %% +% Changing the name from 'value' to 'var_value' because +% there already exist a function named value and cannot have a variable +% also named the same. +varargin_names = varargin(1:2:end); +value_idx = find(cellfun(@(x) contains(x,'value'),varargin_names)); +if ~isempty(value_idx) + varargin{(value_idx - 1) * 2 + 1} = 'var_value'; +end pairs = {'force_init', false; - 'value', []}; + 'var_value', []}; parseargs(varargin, pairs); -varval = value; clear('value'); +varval = var_value; +% clear('var_value'); + +%% try force_init = logical(force_init); %#ok diff --git a/Protocols/@Arpit_CentrePokeTraining/pipeline_Arpit_CentrePokeTraining_experimenter_ratname_250319c.asv b/Protocols/@Arpit_CentrePokeTraining/pipeline_Arpit_CentrePokeTraining_experimenter_ratname_250319c.asv new file mode 100644 index 00000000..a7200d5d --- /dev/null +++ b/Protocols/@Arpit_CentrePokeTraining/pipeline_Arpit_CentrePokeTraining_experimenter_ratname_250319c.asv @@ -0,0 +1,925 @@ +%Training stage file. +%Please use the session automator window exclusively +%to edit this file. + +function varargout = pipeline_Arpit_CentrePokeTraining_experimenter_ratname_250319c(obj, action, varargin) + +GetSoloFunctionArgs('func_owner', ['@' class(obj)], 'func_name', 'SessionModel'); + +pairs = {'helper_vars_eval', true; + 'stage_algorithm_eval', true; + 'completion_test_eval', false; + 'eod_logic_eval', false}; +parseargs(varargin, pairs); + +switch action + + +%% Familiarize with Reward Side Pokes + +% +case 'Familiarize with Reward Side Pokes' + if helper_vars_eval + GetSoloFunctionArgs(obj); + ClearHelperVarsNotOwned(obj); + % + CreateHelperVar(obj,'stage1_trial_counter','value',zeros(1,8)); + CreateHelperVar(obj,'stage1_trial_counter_today','value',zeros(1,8)); + CreateHelperVar(obj,'stage1_trial_counter_oppSide','value',zeros(1,8)); + % + end + if stage_algorithm_eval + GetSoloFunctionArgs(obj); + ClearHelperVarsNotOwned(obj); + % + ParamsSection_MaxSame.value = 4; + callback(ParamsSection_MaxSame); + ParamsSection_training_stage.value = 1; + callback(ParamsSection_training_stage); + stage1_trial_counter(1) = stage1_trial_counter(1) + 1; + SessionPerformanceSection_ntrials_stage.value = value(stage1_trial_counter); + callback(SessionPerformanceSection_ntrials_stage) + stage1_trial_counter_today = stage1_trial_counter_today + 1; + SessionPerformanceSection_ntrials_stage_today.value = value(stage1_trial_counter_today); + callback(SessionPerformanceSection_ntrials_stage_today) + if value(previous_sides(end)) ~= value(ParamsSection_ThisTrial) + stage1_trial_counter_oppSide = stage1_trial_counter_oppSide + 1; + end + % + end +if completion_test_eval +GetSoloFunctionArgs(obj); +ClearHelperVarsNotOwned(obj); +clear('ans'); +% +% only run it if its the start of the day, so number of trials the rat did +% is less +if n_completed_trials < 100 + if value(stage1_trial_counter) > value(Training_ParamsSection_total_trials) && value(stage1_trial_counter_oppSide) > value(Training_ParamsSection_total_trials_opp) + SessionDefinition(obj, 'jump_to_stage', 'Timeout Rewarded Side Pokes'); + end +end +% +if exist('ans', 'var') +varargout{1}=logical(ans); clear('ans'); +else +varargout{1}=false; +end +end +if eod_logic_eval +GetSoloFunctionArgs(obj); +ClearHelperVarsNotOwned(obj); +% +stage1_trial_counter_today.value = 0; +if value(stage1_trial_counter) > value(Training_ParamsSection_total_trials) && value(stage1_trial_counter_oppSide) > value(Training_ParamsSection_total_trials_opp) + SessionDefinition(obj, 'jump_to_stage', 'Timeout Rewarded Side Pokes'); +end +% +end +% + +%% Timeout Rewarded Side Pokes + +% +case 'Timeout Rewarded Side Pokes' +if helper_vars_eval +GetSoloFunctionArgs(obj); +ClearHelperVarsNotOwned(obj); +% +stage2_trial_counter.value = 0; +stage2_trial_counter_today.value = 0; +stage2_trial_counter_oppSide.value = 0; +opp_side_trials.value = 0; +stage2_timeout_percent.value = 0; +% +end +if stage_algorithm_eval +GetSoloFunctionArgs(obj); +ClearHelperVarsNotOwned(obj); +% +ParamsSection_MaxSame.value = 3; +callback(ParamsSection_MaxSame); +ParamsSection_training_stage.value = 2; +callback(ParamsSection_training_stage); +stage2_trial_counter.value = value(stage2_trial_counter) + 1; +SessionPerformanceSection_ntrials_stage.value = value(stage2_trial_counter); +callback(SessionPerformanceSection_ntrials_stage); +stage2_trial_counter_today.value = value(stage2_trial_counter_today) + 1; +SessionPerformanceSection_ntrials_stage_today.value = value(stage2_trial_counter_today); +callback(SessionPerformanceSection_ntrials_stage_today); + +if value(previous_sides(end)) ~= value(ParamsSection_ThisTrial) + stage2_trial_counter_oppSide.value = value(stage2_trial_counter_oppSide) + 1; + opp_side_trials.value = value(opp_side_trials) + 1; +end +% Update the reward collection time based upon behav +if size(timeout_history) > 5 + if all(value(timeout_history(end-1:end))) && value(opp_side_trials) >= 2 + ParamsSection_RewardCollection_duration.value = min([value(ParamsSection_RewardCollection_duration) + 1,... + value(Training_ParamsSection_max_rColl_dur)]); + callback(ParamsSection_RewardCollection_duration); + opp_side_trials.value = 0; + end + + if ~any(value(timeout_history(end-1:end))) && value(opp_side_trials) >= 2 + ParamsSection_RewardCollection_duration.value = max([value(ParamsSection_RewardCollection_duration) - 1,... + value(Training_ParamsSection_min_rColl_dur)]); + callback(ParamsSection_RewardCollection_duration); + opp_side_trials.value = 0; + end +end + +if size(timeout_history) > 20 + if all(value(timeout_history(end-19:end))) + ParamsSection_RewardCollection_duration.value = 30; + callback(ParamsSection_RewardCollection_duration); + end +end + +stage2_timeout_percent.value = ((value(stage2_timeout_percent) * value(stage2_trial_counter)) + double(timeout_history(end)))/(value(stage2_trial_counter) + 1); +SessionPerformanceSection_timeout_stage.value = value(stage2_timeout_percent); +callback(SessionPerformanceSection_timeout_stage); +% +end +if completion_test_eval +GetSoloFunctionArgs(obj); +ClearHelperVarsNotOwned(obj); +clear('ans'); +% +if n_completed_trials > 50 + if value(stage2_trial_counter) > value(Training_ParamsSection_total_trials) && value(stage2_trial_counter_oppSide) > value(Training_ParamsSection_total_trials_opp) + SessionDefinition(obj, 'jump_to_stage', 'Introduce Centre Poke'); + end +end +% +if exist('ans', 'var') +varargout{1}=logical(ans); clear('ans'); +else +varargout{1}=false; +end +end +if eod_logic_eval +GetSoloFunctionArgs(obj); +ClearHelperVarsNotOwned(obj); +% +stage2_trial_counter_today.value = 0; +% +end +% + +%% Introduce Centre Poke + +% +case 'Introduce Centre Poke' +if helper_vars_eval +GetSoloFunctionArgs(obj); +ClearHelperVarsNotOwned(obj); +% +% Maximum & Minimum duration of center poke, in secs: +cp_max = value(ParamsSection_SettlingIn_time) + value(ParamsSection_legal_cbreak); +cp_min = value(ParamsSection_init_CP_duration); +% Minimum increment (in secs) in center poke duration every time there is a non-cp-violation trial: +cp_minimum_increment = 0.001; +stage3_trial_counter = 0; +stage3_trial_counter_today = 0; +stage3_timeout_percent = 0; +last_session_cp = 0; +% +end +if stage_algorithm_eval +GetSoloFunctionArgs(obj); +ClearHelperVarsNotOwned(obj); +% +ParamsSection_MaxSame.value = Inf; +callback(ParamsSection_MaxSame); +ParamsSection_training_stage.value = 3; +callback(ParamsSection_training_stage); +stage3_trial_counter.value = value(stage3_trial_counter) + 1; +SessionPerformanceSection_ntrials_stage.value = value(stage3_trial_counter); +callback(SessionPerformanceSection_ntrials_stage); +stage3_trial_counter_today.value = value(stage3_trial_counter_today) + 1; +SessionPerformanceSection_ntrials_stage_today.value = value(stage3_trial_counter_today); +callback(SessionPerformanceSection_ntrials_stage_today); +stage3_timeout_percent.value = ((value(stage3_timeout_percent) * value(stage3_trial_counter)) + double(timeout_history(end)))/(value(stage3_trial_counter) + 1); +SessionPerformanceSection_timeout_stage.value = value(stage3_timeout_percent); +callback(SessionPerformanceSection_timeout_stage); + +% Change the value of CP Duration +if n_completed_trials < 1 + ParamsSection_CP_duration.value = value(ParamsSection_init_CP_duration); +else + if ~timeout_history(end) && value(ParamsSection_CP_duration) < value(cp_max) + increment = value(ParamsSection_CP_duration) * value(Training_ParamsSection_CPfraction_inc); + if increment < value(cp_minimum_increment) + increment = value(cp_minimum_increment); + end + ParamsSection_CP_duration.value = value(ParamsSection_CP_duration) + increment; + end +end +callback(ParamsSection_CP_duration); +% +end +if completion_test_eval +GetSoloFunctionArgs(obj); +ClearHelperVarsNotOwned(obj); +clear('ans'); +% +if value(ParamsSection_CP_duration) >= value(cp_max) + SessionDefinition(obj, 'jump_to_stage', 'Introduce Violation for Centre Poke'); +end +% +if exist('ans', 'var') +varargout{1}=logical(ans); clear('ans'); +else +varargout{1}=false; +end +end +if eod_logic_eval +GetSoloFunctionArgs(obj); +ClearHelperVarsNotOwned(obj); +% +stage3_trial_counter_today.value = 0; +last_session_cp.value = value(ParamsSection_CP_duration); +% +end +% + +%% Introduce Violation for Centre Poke + +% +case 'Introduce Violation for Centre Poke' +if helper_vars_eval +GetSoloFunctionArgs(obj); +ClearHelperVarsNotOwned(obj); +% + +% Maximum & Minimum duration of center poke, in secs: +cp_min = value(ParamsSection_SettlingIn_time) + value(ParamsSection_legal_cbreak); +cp_max = value(Training_ParamsSection_max_CP); +% Fractional increment in center poke duration every time there is a non-cp-violation trial: +cp_fraction = value(Training_ParamsSection_CPfraction_inc); +% Minimum increment (in secs) in center poke duration every time there is a non-cp-violation trial: +cp_minimum_increment = 0.001; +last_session_cp = 0; +stage4_trial_counter = 0; +stage4_trial_counter_today = 0; +stage4_timeout_percent = 0; +stage4_violation_percent = 0; + +% +end +if stage_algorithm_eval +GetSoloFunctionArgs(obj); +ClearHelperVarsNotOwned(obj); +% +ParamsSection_training_stage.value = 4; +callback(ParamsSection_training_stage); +stage4_trial_counter = stage4_trial_counter + 1; +SessionPerformanceSection_ntrials_stage.value = stage4_trial_counter; +callback(SessionPerformanceSection_ntrials_stage); +stage4_trial_counter_today = stage4_trial_counter_today + 1; +SessionPerformanceSection_ntrials_stage_today.value = stage4_trial_counter_today; +callback(SessionPerformanceSection_ntrials_stage_today); +stage4_timeout_percent = ((stage4_timeout_percent * stage4_trial_counter) + double(timeout_history(end)))/(stage4_trial_counter + 1); +stage4_violation_percent = ((stage4_violation_percent * stage4_trial_counter) + double(violation_history(end)))/(stage4_trial_counter + 1); +SessionPerformanceSection_timeout_stage.value = stage4_timeout_percent; +callback(SessionPerformanceSection_timeout_stage); +SessionPerformanceSection_violation_stage.value = stage4_violation_percent; +callback(SessionPerformanceSection_violation_stage); +% Change the value of CP Duration +if n_completed_trials < 1 + ParamsSection_CP_duration.value = value(ParamsSection_init_CP_duration); +else + if ~violation_history(end) && ~timeout_history(end) && value(ParamsSection_CP_duration) < cp_max + increment = value(ParamsSection_CP_duration)*cp_fraction; + if increment < cp_minimum_increment + increment = value(cp_minimum_increment); + end + ParamsSection_CP_duration.value = value(ParamsSection_CP_duration) + increment; + end +end +if value(ParamsSection_CP_duration) > cp_max + ParamsSection_CP_duration.value = cp_max; +end + +callback(ParamsSection_CP_duration); +% +end +if completion_test_eval +GetSoloFunctionArgs(obj); +ClearHelperVarsNotOwned(obj); +clear('ans'); +% +if value(ParamsSection_CP_duration) >= cp_max && n_completed_trials > 100 +if SessionPerformanceSection_violation_recent < value(Training_ParamsSection_recent_violation) && SessionPerformanceSection_timeout_recent < value(Training_ParamsSection_recent_timeout) && ... + stage4_violation_percent < value(Training_ParamsSection_stage_violation) + SessionDefinition(obj, 'jump_to_stage', 'Introduce Stimuli Sound during Centre Poke'); + last_session_cp = value(ParamsSection_CP_duration); +end +end +% +if exist('ans', 'var') +varargout{1}=logical(ans); clear('ans'); +else +varargout{1}=false; +end +end +if eod_logic_eval +GetSoloFunctionArgs(obj); +ClearHelperVarsNotOwned(obj); +% +% Store the value of the total cp duration reached: +stage4_trial_counter_today = 0; +last_session_cp = value(ParamsSection_CP_duration); +% +end +% + +%% Introduce Stimuli Sound during Centre Poke + +% +case 'Introduce Stimuli Sound during Centre Poke' +if helper_vars_eval +GetSoloFunctionArgs(obj); +ClearHelperVarsNotOwned(obj); +% +cp_max = value(Training_ParamsSection_max_CP); +cp_min = value(Training_ParamsSection_min_CP); +% Fractional increment in center poke duration every time there is a non-cp-violation trial: +cp_fraction = value(Training_ParamsSection_CPfraction_inc); +% Minimum increment (in secs) in center poke duration every time there is a non-cp-violation trial: +cp_minimum_increment = 0.001; +% cp value for last session +last_session_cp = 0; +% Starting total center poke duration: +starting_cp = value(Training_ParamsSection_starting_CP) + value(ParamsSection_SettlingIn_time); +% number of warm-up trials +n_trial_warmup = value(Training_ParamsSection_warm_up_trials); +stage5_trial_counter = 0; +stage5_trial_counter_today = 0; +stage5_timeout_percent = 0; +stage5_violation_percent = 0; +% +end +if stage_algorithm_eval +GetSoloFunctionArgs(obj); +ClearHelperVarsNotOwned(obj); +% + +ParamsSection_training_stage.value = 5; +callback(ParamsSection_training_stage); +stage5_trial_counter = stage5_trial_counter + 1; +SessionPerformanceSection_ntrials_stage.value = stage5_trial_counter; +callback(SessionPerformanceSection_ntrials_stage); +stage5_trial_counter_today = stage5_trial_counter_today + 1; +SessionPerformanceSection_ntrials_stage_today.value = stage5_trial_counter_today; +callback(SessionPerformanceSection_ntrials_stage_today); +stage5_timeout_percent = ((stage5_timeout_percent * stage5_trial_counter) + double(timeout_history(end)))/(stage5_trial_counter + 1); +stage5_violation_percent = ((stage5_violation_percent * stage5_trial_counter) + double(violation_history(end)))/(stage5_trial_counter + 1); +SessionPerformanceSection_timeout_stage.value = stage5_timeout_percent; +callback(SessionPerformanceSection_timeout_stage); +SessionPerformanceSection_violation_stage.value = stage5_violation_percent; +callback(SessionPerformanceSection_violation_stage); +% Change the value of CP Duration + +% Since starting a new session then do a pre warm up to last saved cp +% duration else continue with learning with increased poke time +if n_completed_trials == 0 + ParamsSection_CP_duration.value = value(ParamsSection_init_CP_duration); +elseif n_completed_trials == 1 + ParamsSection_CP_duration.value = starting_cp; +else + if ~violation_history(end) && ~timeout_history(end) + if value(ParamsSection_CP_duration) < max([cp_min,last_session_cp]) % warm up stage + increment = (max([cp_min,last_session_cp]) - value(ParamsSection_CP_duration))/ (n_trial_warmup - 1); + else + if value(ParamsSection_CP_duration) >= last_session_cp && value(ParamsSection_CP_duration) <= cp_max % no warm up stage + increment = value(ParamsSection_CP_duration)*cp_fraction; + if increment < cp_minimum_increment + increment = value(cp_minimum_increment); + end + end + end + + ParamsSection_CP_duration.value = value(ParamsSection_CP_duration) + increment; + + % Check if the values are within the required range + if value(ParamsSection_CP_duration) < starting_cp + ParamsSection_CP_duration.value = starting_cp; + end + if value(ParamsSection_CP_duration) > cp_max + ParamsSection_CP_duration.value = cp_max; + end + + end +end +callback(ParamsSection_CP_duration); + +if value(ParamsSection_CP_duration) >= 1 + ParamsSection_PreStim_time.value = 0.4; + if value(ParamsSection_CP_duration) < 2 + ParamsSection_A1_time.value = 0.1; + elseif value(ParamsSection_CP_duration) < 2.5 && value(ParamsSection_CP_duration) >= 2 + ParamsSection_A1_time.value = 0.2; + elseif value(ParamsSection_CP_duration) < 3 && value(ParamsSection_CP_duration) >= 2.5 + ParamsSection_A1_time.value = 0.3; + else + ParamsSection_A1_time.value = 0.4; + end +elseif value(ParamsSection_CP_duration) < 1 && value(ParamsSection_CP_duration) >= starting_cp + ParamsSection_SettlingIn_time.value = 0.2; + callback(ParamsSection_SettlingIn_time); + ParamsSection_PreStim_time.value = 0.1; + ParamsSection_A1_time.value = 0.1; +end + +if value(ParamsSection_CP_duration) >= starting_cp + ParamsSection_time_bet_aud1_gocue.value = value(ParamsSection_CP_duration) - value(ParamsSection_SettlingIn_time) - value(ParamsSection_A1_time) - value(ParamsSection_PreStim_time); + callback(ParamsSection_time_bet_aud1_gocue) + callback(ParamsSection_PreStim_time); + callback(ParamsSection_A1_time); +end + + +% +end +if completion_test_eval +GetSoloFunctionArgs(obj); +ClearHelperVarsNotOwned(obj); +clear('ans'); +% +if value(ParamsSection_CP_duration) >= value(Training_ParamsSection_max_CP) && stage5_trial_counter > value(Training_ParamsSection_total_trials) + if SessionPerformanceSection_violation_recent < value(Training_ParamsSection_recent_violation) && SessionPerformanceSection_timeout_recent < value(Training_ParamsSection_recent_timeout) && ... + stage5_violation_percent < value(Training_ParamsSection_stage_violation) && n_completed_trials > 100 + SessionDefinition(obj, 'jump_to_stage', 'Vary Stimuli location during Centre Poke'); + last_session_cp = value(ParamsSection_CP_duration); + end +end +% +if exist('ans', 'var') +varargout{1}=logical(ans); clear('ans'); +else +varargout{1}=false; +end +end +if eod_logic_eval +GetSoloFunctionArgs(obj); +ClearHelperVarsNotOwned(obj); +% +stage5_trial_counter_today = 0; +last_session_cp = value(ParamsSection_CP_duration); +% +end +% + +%% Vary Stimuli location during Centre Poke + +% +case 'Vary Stimuli location during Centre Poke' +if helper_vars_eval +GetSoloFunctionArgs(obj); +ClearHelperVarsNotOwned(obj); +% +stage6_trial_counter = 0; +stage6_trial_counter_today = 0; +stage6_timeout_percent = 0; +stage6_violation_percent = 0; +cp_max = value(Training_ParamsSection_max_CP); +% Starting total center poke duration: +starting_cp = value(Training_ParamsSection_starting_CP) + value(ParamsSection_SettlingIn_time); +% number of warm-up trials +n_trial_warmup = value(Training_ParamsSection_warm_up_trials); +prestim_min = value(Training_ParamsSection_min_prestim); +prestim_max = value(Training_ParamsSection_max_prestim); +stim_dur = value(Training_ParamsSection_stim_dur); +% +end +if stage_algorithm_eval +GetSoloFunctionArgs(obj); +ClearHelperVarsNotOwned(obj); +% +ParamsSection_training_stage.value = 6; +callback(ParamsSection_training_stage); +stage6_trial_counter = stage6_trial_counter + 1; +SessionPerformanceSection_ntrials_stage.value = stage6_trial_counter; +callback(SessionPerformanceSection_ntrials_stage); +stage6_trial_counter_today = stage6_trial_counter_today + 1; +SessionPerformanceSection_ntrials_stage_today.value = stage6_trial_counter_today; +callback(SessionPerformanceSection_ntrials_stage_today); +stage6_timeout_percent = ((stage6_timeout_percent * stage6_trial_counter) + double(timeout_history(end)))/(stage6_trial_counter + 1); +stage6_violation_percent = ((stage6_violation_percent * stage6_trial_counter) + double(violation_history(end)))/(stage6_trial_counter + 1); +SessionPerformanceSection_timeout_stage.value = stage6_timeout_percent; +callback(SessionPerformanceSection_timeout_stage); +SessionPerformanceSection_violation_stage.value = stage6_violation_percent; +callback(SessionPerformanceSection_violation_stage); +% Warm Up If starting a new session +if n_completed_trials == 0 + ParamsSection_CP_duration.value = value(ParamsSection_init_CP_duration); +elseif n_completed_trials == 1 + ParamsSection_CP_duration.value = starting_cp; +else + if value(ParamsSection_CP_duration) < cp_max % warm up stage + if ~violation_history(end) && ~timeout_history(end) + increment = (cp_max - value(ParamsSection_CP_duration))/ (n_trial_warmup - 1); + ParamsSection_CP_duration.value = value(ParamsSection_CP_duration) + increment; + % Check if the values are within the required range + if value(ParamsSection_CP_duration) < starting_cp + ParamsSection_CP_duration.value = starting_cp; + end + if value(ParamsSection_CP_duration) > cp_max + ParamsSection_CP_duration.value = cp_max; + end + end + end +end + +callback(ParamsSection_CP_duration); + +if value(ParamsSection_CP_duration) < 3 && value(ParamsSection_CP_duration) >= starting_cp % during the warm up phase + ParamsSection_SettlingIn_time.value = 0.2; + callback(ParamsSection_SettlingIn_time); + ParamsSection_PreStim_time.value = 0.1; + ParamsSection_A1_time.value = 0.1; +else + ParamsSection_A1_time.value = stim_dur; % actual training stage + time_range_PreStim_time = prestim_min : 0.01 : prestim_max; + ParamsSection_PreStim_time.value = time_range_PreStim_time(randi([1, numel(time_range_PreStim_time)],1,1)); +end + +if value(ParamsSection_CP_duration) >= starting_cp + ParamsSection_time_bet_aud1_gocue.value = value(ParamsSection_CP_duration) - value(ParamsSection_SettlingIn_time) - value(ParamsSection_A1_time) - value(ParamsSection_PreStim_time); + callback(ParamsSection_time_bet_aud1_gocue) + callback(ParamsSection_PreStim_time); + callback(ParamsSection_A1_time); +end + +% +end +if completion_test_eval +GetSoloFunctionArgs(obj); +ClearHelperVarsNotOwned(obj); +clear('ans'); +% +if stage6_trial_counter > value(Training_ParamsSection_total_trials) + if SessionPerformanceSection_violation_recent < value(Training_ParamsSection_recent_violation) && SessionPerformanceSection_timeout_recent < value(Training_ParamsSection_recent_timeout) && ... + stage6_violation_percent < value(Training_ParamsSection_stage_violation) + SessionDefinition(obj, 'jump_to_stage', 'Variable Stimuli Go Cue location during Centre Poke'); + end +end +% +if exist('ans', 'var') +varargout{1}=logical(ans); clear('ans'); +else +varargout{1}=false; +end +end +if eod_logic_eval +GetSoloFunctionArgs(obj); +ClearHelperVarsNotOwned(obj); +% +stage6_trial_counter_today = 0; +% +end +% + +%% Variable Stimuli Go Cue location during Centre Poke + +% +case 'Variable Stimuli Go Cue location during Centre Poke' +if helper_vars_eval +GetSoloFunctionArgs(obj); +ClearHelperVarsNotOwned(obj); +% +stage7_trial_counter = 0; +stage7_trial_counter_today = 0; +stage7_timeout_percent = 0; +stage7_violation_percent = 0; + +% Variables for warmup stage +cp_max = value(Training_ParamsSection_max_CP); +% Starting total center poke duration: +starting_cp = value(Training_ParamsSection_starting_CP) + value(ParamsSection_SettlingIn_time); +% number of warm-up trials +n_trial_warmup = value(Training_ParamsSection_warm_up_trials); +prestim_min = value(Training_ParamsSection_min_prestim); +prestim_max = value(Training_ParamsSection_max_prestim); +prestim_time = value(Training_ParamsSection_min_prestim); +a1_time = value(Training_ParamsSection_stim_dur); +a1_time_min = value(Training_ParamsSection_stim_dur); +a1_time_max = value(Training_ParamsSection_stim_dur + 0.1); +prego_min = value(Training_ParamsSection_min_prego); +prego_max = value(Training_ParamsSection_max_prego); +prego_time = value(Training_ParamsSection_min_prego); +warmup_completed = 0; +warm_up_on = 1; +random_prestim = 1; +random_prego = 1; +random_A1 = 0; +% +end +if stage_algorithm_eval +GetSoloFunctionArgs(obj); +ClearHelperVarsNotOwned(obj); +% + +ParamsSection_training_stage.value = 7; +callback(ParamsSection_training_stage); +stage7_trial_counter = stage7_trial_counter + 1; +SessionPerformanceSection_ntrials_stage.value = stage7_trial_counter; +callback(SessionPerformanceSection_ntrials_stage); +stage7_trial_counter_today = stage7_trial_counter_today + 1; +SessionPerformanceSection_ntrials_stage_today.value = stage7_trial_counter_today; +callback(SessionPerformanceSection_ntrials_stage_today); +stage7_timeout_percent = ((stage7_timeout_percent * stage7_trial_counter) + double(timeout_history(end)))/(stage7_trial_counter + 1); +stage7_violation_percent = ((stage7_violation_percent * stage7_trial_counter) + double(violation_history(end)))/(stage7_trial_counter + 1); +SessionPerformanceSection_timeout_stage.value = stage7_timeout_percent; +callback(SessionPerformanceSection_timeout_stage); +SessionPerformanceSection_violation_stage.value = stage7_violation_percent; +callback(SessionPerformanceSection_violation_stage); + +% Warm Up If starting a new session +if warm_up_on == 1 + if n_completed_trials == 0 + ParamsSection_CP_duration.value = value(ParamsSection_init_CP_duration); + warmup_completed = 0; + elseif n_completed_trials == 1 + ParamsSection_CP_duration.value = starting_cp; + else + if value(ParamsSection_CP_duration) <= cp_max % warm up stage + if ~violation_history(end) && ~timeout_history(end) + increment = (cp_max - value(ParamsSection_CP_duration))/ (n_trial_warmup - 1); + ParamsSection_CP_duration.value = value(ParamsSection_CP_duration) + increment; + % Check if the values are within the required range + if value(ParamsSection_CP_duration) < starting_cp + ParamsSection_CP_duration.value = starting_cp; + end + if value(ParamsSection_CP_duration) >= cp_max + ParamsSection_CP_duration.value = cp_max; + warmup_completed = 1; + end + end + end + end +else + warmup_completed = 1; +end + +if n_completed_trials >= 1 + cp_length = value(ParamsSection_CP_duration) - value(ParamsSection_SettlingIn_time); + [ParamsSection_PreStim_time.value ,ParamsSection_A1_time.value,ParamsSection_time_bet_aud1_gocue.value] = param_time_within_range(warmup_completed,... + cp_length,prestim_min,prestim_max, random_prestim, prestim_time,... + a1_time_min,a1_time_max, random_A1, a1_time,prego_min,prego_max, random_prego, prego_time); + + callback(ParamsSection_PreStim_time); + callback(ParamsSection_A1_time); + callback(ParamsSection_time_bet_aud1_gocue); + ParamsSection_CP_duration = value(ParamsSection_SettlingIn_time) + value(ParamsSection_PreStim_time) + value(ParamsSection_A1_time) + value(ParamsSection_time_bet_aud1_gocue); +end +callback(ParamsSection_CP_duration); +% +end +if completion_test_eval +GetSoloFunctionArgs(obj); +ClearHelperVarsNotOwned(obj); +clear('ans'); +% +if stage7_trial_counter > value(Training_ParamsSection_total_trials) + if SessionPerformanceSection_violation_recent < value(Training_ParamsSection_recent_violation) && SessionPerformanceSection_timeout_recent < value(Training_ParamsSection_recent_timeout) && ... + stage7_violation_percent < value(Training_ParamsSection_stage_violation) + SessionDefinition(obj, 'jump_to_stage', 'User Setting'); + end +end +% +if exist('ans', 'var') +varargout{1}=logical(ans); clear('ans'); +else +varargout{1}=false; +end +end +if eod_logic_eval +GetSoloFunctionArgs(obj); +ClearHelperVarsNotOwned(obj); +% +stage7_trial_counter_today = 0; +% +end +% + +%% User Setting + +% + case 'User Setting' +if helper_vars_eval +GetSoloFunctionArgs(obj); +ClearHelperVarsNotOwned(obj); +% +stage8_trial_counter = 0; +stage8_trial_counter_today = 0; +stage8_timeout_percent = 0; +stage8_violation_percent = 0; + +% Variables for warmup stage +cp_max = 5; +n_trial_warmup = 20; +starting_cp = 0.5; +warmup_completed = 0; +warm_up_on = value(ParamsSection_warmup_on); +random_prestim = value(ParamsSection_random_PreStim_time); +random_prego = value(ParamsSection_random_prego_time); +random_A1 = value(ParamsSection_random_A1_time); +prestim_min = value(ParamsSection_PreStim_time_Min); +prestim_max = value(ParamsSection_PreStim_time_Max); +prestim_time = value(ParamsSection_PreStim_time); +prego_min = value(ParamsSection_time_bet_aud1_gocue_Min); +prego_max = value(ParamsSection_time_bet_aud1_gocue_Max); +prego_time = value(ParamsSection_time_bet_aud1_gocue); +a1_time = value(ParamsSection_A1_time); +a1_time_min = value(ParamsSection_A1_time_Min); +a1_time_max = value(ParamsSection_A1_time_Max); + +% +end +if stage_algorithm_eval +GetSoloFunctionArgs(obj); +ClearHelperVarsNotOwned(obj); +% + +ParamsSection_training_stage.value = 8; +callback(ParamsSection_training_stage); +stage8_trial_counter = stage8_trial_counter + 1; +SessionPerformanceSection_ntrials_stage.value = stage8_trial_counter; +callback(SessionPerformanceSection_ntrials_stage); +stage8_trial_counter_today = stage8_trial_counter_today + 1; +SessionPerformanceSection_ntrials_stage_today.value = stage8_trial_counter_today; +callback(SessionPerformanceSection_ntrials_stage_today); +stage8_timeout_percent = ((stage8_timeout_percent * stage8_trial_counter) + double(timeout_history(end)))/(stage8_trial_counter + 1); +stage8_violation_percent = ((stage8_violation_percent * stage8_trial_counter) + double(violation_history(end)))/(stage8_trial_counter + 1); +SessionPerformanceSection_timeout_stage.value = stage8_timeout_percent; +callback(SessionPerformanceSection_timeout_stage); +SessionPerformanceSection_violation_stage.value = stage8_violation_percent; +callback(SessionPerformanceSection_violation_stage); + +% Warm Up If starting a new session +if warm_up_on == 1 + if n_completed_trials == 0 + ParamsSection_CP_duration.value = value(ParamsSection_init_CP_duration); + warmup_completed = 0; + elseif n_completed_trials == 1 + ParamsSection_CP_duration.value = starting_cp; + else + if value(ParamsSection_CP_duration) <= cp_max % warm up stage + if ~violation_history(end) && ~timeout_history(end) + increment = (cp_max - value(ParamsSection_CP_duration))/ (n_trial_warmup - 1); + ParamsSection_CP_duration.value = value(ParamsSection_CP_duration) + increment; + % Check if the values are within the required range + if value(ParamsSection_CP_duration) < starting_cp + ParamsSection_CP_duration.value = starting_cp; + end + if value(ParamsSection_CP_duration) >= cp_max + ParamsSection_CP_duration.value = cp_max; + warmup_completed = 1; + end + end + end + end +else + warmup_completed = 1; +end + +if n_completed_trials >= 1 + cp_length = value(ParamsSection_CP_duration) - value(ParamsSection_SettlingIn_time); + [ParamsSection_PreStim_time.value ,ParamsSection_A1_time.value,ParamsSection_time_bet_aud1_gocue.value] = param_time_within_range(warmup_completed,... + cp_length,prestim_min,prestim_max, random_prestim, prestim_time,... + a1_time_min,a1_time_max, random_A1, a1_time,prego_min,prego_max, random_prego, prego_time); + + callback(ParamsSection_PreStim_time); + callback(ParamsSection_A1_time); + callback(ParamsSection_time_bet_aud1_gocue); + ParamsSection_CP_duration = value(ParamsSection_SettlingIn_time) + value(ParamsSection_PreStim_time) + value(ParamsSection_A1_time) + value(ParamsSection_time_bet_aud1_gocue); +end +callback(ParamsSection_CP_duration); +% +end +if completion_test_eval +GetSoloFunctionArgs(obj); +ClearHelperVarsNotOwned(obj); +clear('ans'); +% + +% +if exist('ans', 'var') +varargout{1}=logical(ans); clear('ans'); +else +varargout{1}=false; +end +end +if eod_logic_eval +GetSoloFunctionArgs(obj); +ClearHelperVarsNotOwned(obj); +% +stage8_trial_counter_today = 0; +% +end +% + + +end + +end + +%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +% + +function [prestim,A1,prego] = param_time_within_range(fixed_length,cp_length,range_min_prestim,range_max_prestim, is_random_prestim, provided_time_prestim,... + range_min_A1,range_max_A1, is_random_A1, provided_time_A1,range_min_prego,range_max_prego, is_random_prego, provided_time_prego) + +if fixed_length == 1 % warm up stage where cp length is increasing +% then calculate the range/typical value + if cp_length <= 0.3 + prestim = 0.1; + A1 = 0.1; + prego = 0.1; + else + range_size = round(0.3 * cp_length,1); + if range_size > 0.4 + step_size = 0.1; + else + step_size = 0.01; + end + + timerange = 0.1:step_size:range_size; + + if is_random_prestim == 1 + prestim = timerange(randi([1, numel(timerange)],1,1)); + else + if provided_time_prestim <= range_size + prestim = provided_time_prestim; + else + prestim = range_size; + end + + end + + if is_random_A1 == 1 + A1 = timerange(randi([1, numel(timerange)],1,1)); + else + if provided_time_A1 <= range_size + A1 = provided_time_A1; + else + A1 = range_size; + end + end + + prego = cp_length - prestim - A1; + + end + +else + + if is_random_prestim == 1 + range_size_prestim = range_max_prestim - range_min_prestim; + if range_size_prestim > 0.4 + step_size_prestim = 0.1; + else + step_size_prestim = 0.01; + end + time_range_prestim = range_min_prestim:step_size_prestim:range_max_prestim; + prestim = time_range_prestim(randi([1, numel(time_range_prestim)],1,1)); + else + prestim = provided_time_prestim; + end + + if is_random_A1 == 1 + range_size_A1 = range_max_A1 - range_min_A1; + if range_size_A1 > 0.4 + step_size_A1 = 0.1; + else + step_size_A1 = 0.01; + end + time_range_A1 = range_min_A1:step_size_A1:range_max_A1; + A1 = time_range_A1(randi([1, numel(time_range_A1)],1,1)); + else + A1 = provided_time_A1; + end + + if is_random_prego == 1 + range_size_prego = range_max_prego - range_min_prego; + if range_size_prego > 0.4 + step_size_prego = 0.1; + else + step_size_prego = 0.01; + end + time_range_prego = range_min_prego:step_size_prego:range_max_prego; + prego = time_range_prego(randi([1, numel(time_range_prego)],1,1)); + else + prego = provided_time_prego; + end + +end +end + + +% + +%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% diff --git a/Protocols/@Arpit_CentrePokeTraining/pipeline_Arpit_CentrePokeTraining_experimenter_ratname_250319c.m b/Protocols/@Arpit_CentrePokeTraining/pipeline_Arpit_CentrePokeTraining_experimenter_ratname_250319c.m index 61ecc199..a7200d5d 100644 --- a/Protocols/@Arpit_CentrePokeTraining/pipeline_Arpit_CentrePokeTraining_experimenter_ratname_250319c.m +++ b/Protocols/@Arpit_CentrePokeTraining/pipeline_Arpit_CentrePokeTraining_experimenter_ratname_250319c.m @@ -23,9 +23,9 @@ GetSoloFunctionArgs(obj); ClearHelperVarsNotOwned(obj); % - stage1_trial_counter.value = 0; - stage1_trial_counter_today.value = 0; - stage1_trial_counter_oppSide.value = 0; + CreateHelperVar(obj,'stage1_trial_counter','value',zeros(1,8)); + CreateHelperVar(obj,'stage1_trial_counter_today','value',zeros(1,8)); + CreateHelperVar(obj,'stage1_trial_counter_oppSide','value',zeros(1,8)); % end if stage_algorithm_eval @@ -36,14 +36,14 @@ callback(ParamsSection_MaxSame); ParamsSection_training_stage.value = 1; callback(ParamsSection_training_stage); - stage1_trial_counter.value = value(stage1_trial_counter) + 1; + stage1_trial_counter(1) = stage1_trial_counter(1) + 1; SessionPerformanceSection_ntrials_stage.value = value(stage1_trial_counter); callback(SessionPerformanceSection_ntrials_stage) - stage1_trial_counter_today.value = value(stage1_trial_counter_today) + 1; + stage1_trial_counter_today = stage1_trial_counter_today + 1; SessionPerformanceSection_ntrials_stage_today.value = value(stage1_trial_counter_today); callback(SessionPerformanceSection_ntrials_stage_today) if value(previous_sides(end)) ~= value(ParamsSection_ThisTrial) - stage1_trial_counter_oppSide.value = value(stage1_trial_counter_oppSide) + 1; + stage1_trial_counter_oppSide = stage1_trial_counter_oppSide + 1; end % end @@ -176,14 +176,14 @@ ClearHelperVarsNotOwned(obj); % % Maximum & Minimum duration of center poke, in secs: -cp_max.value = value(ParamsSection_SettlingIn_time) + value(ParamsSection_legal_cbreak); -cp_min.value = value(ParamsSection_init_CP_duration); +cp_max = value(ParamsSection_SettlingIn_time) + value(ParamsSection_legal_cbreak); +cp_min = value(ParamsSection_init_CP_duration); % Minimum increment (in secs) in center poke duration every time there is a non-cp-violation trial: -cp_minimum_increment.value = 0.001; -stage3_trial_counter.value = 0; -stage3_trial_counter_today.value = 0; -stage3_timeout_percent.value = 0; -last_session_cp.value = 0; +cp_minimum_increment = 0.001; +stage3_trial_counter = 0; +stage3_trial_counter_today = 0; +stage3_timeout_percent = 0; +last_session_cp = 0; % end if stage_algorithm_eval From 1c492d1a36c43213dac9baeff4134d471d6e5f01 Mon Sep 17 00:00:00 2001 From: Arpit Date: Sat, 29 Mar 2025 17:23:55 +0000 Subject: [PATCH 038/164] changes to automated training protocol --- ...eTraining_experimenter_ratname_250319c.asv | 925 ------------------ ...okeTraining_experimenter_ratname_250319c.m | 608 ++++++++---- 2 files changed, 407 insertions(+), 1126 deletions(-) delete mode 100644 Protocols/@Arpit_CentrePokeTraining/pipeline_Arpit_CentrePokeTraining_experimenter_ratname_250319c.asv diff --git a/Protocols/@Arpit_CentrePokeTraining/pipeline_Arpit_CentrePokeTraining_experimenter_ratname_250319c.asv b/Protocols/@Arpit_CentrePokeTraining/pipeline_Arpit_CentrePokeTraining_experimenter_ratname_250319c.asv deleted file mode 100644 index a7200d5d..00000000 --- a/Protocols/@Arpit_CentrePokeTraining/pipeline_Arpit_CentrePokeTraining_experimenter_ratname_250319c.asv +++ /dev/null @@ -1,925 +0,0 @@ -%Training stage file. -%Please use the session automator window exclusively -%to edit this file. - -function varargout = pipeline_Arpit_CentrePokeTraining_experimenter_ratname_250319c(obj, action, varargin) - -GetSoloFunctionArgs('func_owner', ['@' class(obj)], 'func_name', 'SessionModel'); - -pairs = {'helper_vars_eval', true; - 'stage_algorithm_eval', true; - 'completion_test_eval', false; - 'eod_logic_eval', false}; -parseargs(varargin, pairs); - -switch action - - -%% Familiarize with Reward Side Pokes - -% -case 'Familiarize with Reward Side Pokes' - if helper_vars_eval - GetSoloFunctionArgs(obj); - ClearHelperVarsNotOwned(obj); - % - CreateHelperVar(obj,'stage1_trial_counter','value',zeros(1,8)); - CreateHelperVar(obj,'stage1_trial_counter_today','value',zeros(1,8)); - CreateHelperVar(obj,'stage1_trial_counter_oppSide','value',zeros(1,8)); - % - end - if stage_algorithm_eval - GetSoloFunctionArgs(obj); - ClearHelperVarsNotOwned(obj); - % - ParamsSection_MaxSame.value = 4; - callback(ParamsSection_MaxSame); - ParamsSection_training_stage.value = 1; - callback(ParamsSection_training_stage); - stage1_trial_counter(1) = stage1_trial_counter(1) + 1; - SessionPerformanceSection_ntrials_stage.value = value(stage1_trial_counter); - callback(SessionPerformanceSection_ntrials_stage) - stage1_trial_counter_today = stage1_trial_counter_today + 1; - SessionPerformanceSection_ntrials_stage_today.value = value(stage1_trial_counter_today); - callback(SessionPerformanceSection_ntrials_stage_today) - if value(previous_sides(end)) ~= value(ParamsSection_ThisTrial) - stage1_trial_counter_oppSide = stage1_trial_counter_oppSide + 1; - end - % - end -if completion_test_eval -GetSoloFunctionArgs(obj); -ClearHelperVarsNotOwned(obj); -clear('ans'); -% -% only run it if its the start of the day, so number of trials the rat did -% is less -if n_completed_trials < 100 - if value(stage1_trial_counter) > value(Training_ParamsSection_total_trials) && value(stage1_trial_counter_oppSide) > value(Training_ParamsSection_total_trials_opp) - SessionDefinition(obj, 'jump_to_stage', 'Timeout Rewarded Side Pokes'); - end -end -% -if exist('ans', 'var') -varargout{1}=logical(ans); clear('ans'); -else -varargout{1}=false; -end -end -if eod_logic_eval -GetSoloFunctionArgs(obj); -ClearHelperVarsNotOwned(obj); -% -stage1_trial_counter_today.value = 0; -if value(stage1_trial_counter) > value(Training_ParamsSection_total_trials) && value(stage1_trial_counter_oppSide) > value(Training_ParamsSection_total_trials_opp) - SessionDefinition(obj, 'jump_to_stage', 'Timeout Rewarded Side Pokes'); -end -% -end -% - -%% Timeout Rewarded Side Pokes - -% -case 'Timeout Rewarded Side Pokes' -if helper_vars_eval -GetSoloFunctionArgs(obj); -ClearHelperVarsNotOwned(obj); -% -stage2_trial_counter.value = 0; -stage2_trial_counter_today.value = 0; -stage2_trial_counter_oppSide.value = 0; -opp_side_trials.value = 0; -stage2_timeout_percent.value = 0; -% -end -if stage_algorithm_eval -GetSoloFunctionArgs(obj); -ClearHelperVarsNotOwned(obj); -% -ParamsSection_MaxSame.value = 3; -callback(ParamsSection_MaxSame); -ParamsSection_training_stage.value = 2; -callback(ParamsSection_training_stage); -stage2_trial_counter.value = value(stage2_trial_counter) + 1; -SessionPerformanceSection_ntrials_stage.value = value(stage2_trial_counter); -callback(SessionPerformanceSection_ntrials_stage); -stage2_trial_counter_today.value = value(stage2_trial_counter_today) + 1; -SessionPerformanceSection_ntrials_stage_today.value = value(stage2_trial_counter_today); -callback(SessionPerformanceSection_ntrials_stage_today); - -if value(previous_sides(end)) ~= value(ParamsSection_ThisTrial) - stage2_trial_counter_oppSide.value = value(stage2_trial_counter_oppSide) + 1; - opp_side_trials.value = value(opp_side_trials) + 1; -end -% Update the reward collection time based upon behav -if size(timeout_history) > 5 - if all(value(timeout_history(end-1:end))) && value(opp_side_trials) >= 2 - ParamsSection_RewardCollection_duration.value = min([value(ParamsSection_RewardCollection_duration) + 1,... - value(Training_ParamsSection_max_rColl_dur)]); - callback(ParamsSection_RewardCollection_duration); - opp_side_trials.value = 0; - end - - if ~any(value(timeout_history(end-1:end))) && value(opp_side_trials) >= 2 - ParamsSection_RewardCollection_duration.value = max([value(ParamsSection_RewardCollection_duration) - 1,... - value(Training_ParamsSection_min_rColl_dur)]); - callback(ParamsSection_RewardCollection_duration); - opp_side_trials.value = 0; - end -end - -if size(timeout_history) > 20 - if all(value(timeout_history(end-19:end))) - ParamsSection_RewardCollection_duration.value = 30; - callback(ParamsSection_RewardCollection_duration); - end -end - -stage2_timeout_percent.value = ((value(stage2_timeout_percent) * value(stage2_trial_counter)) + double(timeout_history(end)))/(value(stage2_trial_counter) + 1); -SessionPerformanceSection_timeout_stage.value = value(stage2_timeout_percent); -callback(SessionPerformanceSection_timeout_stage); -% -end -if completion_test_eval -GetSoloFunctionArgs(obj); -ClearHelperVarsNotOwned(obj); -clear('ans'); -% -if n_completed_trials > 50 - if value(stage2_trial_counter) > value(Training_ParamsSection_total_trials) && value(stage2_trial_counter_oppSide) > value(Training_ParamsSection_total_trials_opp) - SessionDefinition(obj, 'jump_to_stage', 'Introduce Centre Poke'); - end -end -% -if exist('ans', 'var') -varargout{1}=logical(ans); clear('ans'); -else -varargout{1}=false; -end -end -if eod_logic_eval -GetSoloFunctionArgs(obj); -ClearHelperVarsNotOwned(obj); -% -stage2_trial_counter_today.value = 0; -% -end -% - -%% Introduce Centre Poke - -% -case 'Introduce Centre Poke' -if helper_vars_eval -GetSoloFunctionArgs(obj); -ClearHelperVarsNotOwned(obj); -% -% Maximum & Minimum duration of center poke, in secs: -cp_max = value(ParamsSection_SettlingIn_time) + value(ParamsSection_legal_cbreak); -cp_min = value(ParamsSection_init_CP_duration); -% Minimum increment (in secs) in center poke duration every time there is a non-cp-violation trial: -cp_minimum_increment = 0.001; -stage3_trial_counter = 0; -stage3_trial_counter_today = 0; -stage3_timeout_percent = 0; -last_session_cp = 0; -% -end -if stage_algorithm_eval -GetSoloFunctionArgs(obj); -ClearHelperVarsNotOwned(obj); -% -ParamsSection_MaxSame.value = Inf; -callback(ParamsSection_MaxSame); -ParamsSection_training_stage.value = 3; -callback(ParamsSection_training_stage); -stage3_trial_counter.value = value(stage3_trial_counter) + 1; -SessionPerformanceSection_ntrials_stage.value = value(stage3_trial_counter); -callback(SessionPerformanceSection_ntrials_stage); -stage3_trial_counter_today.value = value(stage3_trial_counter_today) + 1; -SessionPerformanceSection_ntrials_stage_today.value = value(stage3_trial_counter_today); -callback(SessionPerformanceSection_ntrials_stage_today); -stage3_timeout_percent.value = ((value(stage3_timeout_percent) * value(stage3_trial_counter)) + double(timeout_history(end)))/(value(stage3_trial_counter) + 1); -SessionPerformanceSection_timeout_stage.value = value(stage3_timeout_percent); -callback(SessionPerformanceSection_timeout_stage); - -% Change the value of CP Duration -if n_completed_trials < 1 - ParamsSection_CP_duration.value = value(ParamsSection_init_CP_duration); -else - if ~timeout_history(end) && value(ParamsSection_CP_duration) < value(cp_max) - increment = value(ParamsSection_CP_duration) * value(Training_ParamsSection_CPfraction_inc); - if increment < value(cp_minimum_increment) - increment = value(cp_minimum_increment); - end - ParamsSection_CP_duration.value = value(ParamsSection_CP_duration) + increment; - end -end -callback(ParamsSection_CP_duration); -% -end -if completion_test_eval -GetSoloFunctionArgs(obj); -ClearHelperVarsNotOwned(obj); -clear('ans'); -% -if value(ParamsSection_CP_duration) >= value(cp_max) - SessionDefinition(obj, 'jump_to_stage', 'Introduce Violation for Centre Poke'); -end -% -if exist('ans', 'var') -varargout{1}=logical(ans); clear('ans'); -else -varargout{1}=false; -end -end -if eod_logic_eval -GetSoloFunctionArgs(obj); -ClearHelperVarsNotOwned(obj); -% -stage3_trial_counter_today.value = 0; -last_session_cp.value = value(ParamsSection_CP_duration); -% -end -% - -%% Introduce Violation for Centre Poke - -% -case 'Introduce Violation for Centre Poke' -if helper_vars_eval -GetSoloFunctionArgs(obj); -ClearHelperVarsNotOwned(obj); -% - -% Maximum & Minimum duration of center poke, in secs: -cp_min = value(ParamsSection_SettlingIn_time) + value(ParamsSection_legal_cbreak); -cp_max = value(Training_ParamsSection_max_CP); -% Fractional increment in center poke duration every time there is a non-cp-violation trial: -cp_fraction = value(Training_ParamsSection_CPfraction_inc); -% Minimum increment (in secs) in center poke duration every time there is a non-cp-violation trial: -cp_minimum_increment = 0.001; -last_session_cp = 0; -stage4_trial_counter = 0; -stage4_trial_counter_today = 0; -stage4_timeout_percent = 0; -stage4_violation_percent = 0; - -% -end -if stage_algorithm_eval -GetSoloFunctionArgs(obj); -ClearHelperVarsNotOwned(obj); -% -ParamsSection_training_stage.value = 4; -callback(ParamsSection_training_stage); -stage4_trial_counter = stage4_trial_counter + 1; -SessionPerformanceSection_ntrials_stage.value = stage4_trial_counter; -callback(SessionPerformanceSection_ntrials_stage); -stage4_trial_counter_today = stage4_trial_counter_today + 1; -SessionPerformanceSection_ntrials_stage_today.value = stage4_trial_counter_today; -callback(SessionPerformanceSection_ntrials_stage_today); -stage4_timeout_percent = ((stage4_timeout_percent * stage4_trial_counter) + double(timeout_history(end)))/(stage4_trial_counter + 1); -stage4_violation_percent = ((stage4_violation_percent * stage4_trial_counter) + double(violation_history(end)))/(stage4_trial_counter + 1); -SessionPerformanceSection_timeout_stage.value = stage4_timeout_percent; -callback(SessionPerformanceSection_timeout_stage); -SessionPerformanceSection_violation_stage.value = stage4_violation_percent; -callback(SessionPerformanceSection_violation_stage); -% Change the value of CP Duration -if n_completed_trials < 1 - ParamsSection_CP_duration.value = value(ParamsSection_init_CP_duration); -else - if ~violation_history(end) && ~timeout_history(end) && value(ParamsSection_CP_duration) < cp_max - increment = value(ParamsSection_CP_duration)*cp_fraction; - if increment < cp_minimum_increment - increment = value(cp_minimum_increment); - end - ParamsSection_CP_duration.value = value(ParamsSection_CP_duration) + increment; - end -end -if value(ParamsSection_CP_duration) > cp_max - ParamsSection_CP_duration.value = cp_max; -end - -callback(ParamsSection_CP_duration); -% -end -if completion_test_eval -GetSoloFunctionArgs(obj); -ClearHelperVarsNotOwned(obj); -clear('ans'); -% -if value(ParamsSection_CP_duration) >= cp_max && n_completed_trials > 100 -if SessionPerformanceSection_violation_recent < value(Training_ParamsSection_recent_violation) && SessionPerformanceSection_timeout_recent < value(Training_ParamsSection_recent_timeout) && ... - stage4_violation_percent < value(Training_ParamsSection_stage_violation) - SessionDefinition(obj, 'jump_to_stage', 'Introduce Stimuli Sound during Centre Poke'); - last_session_cp = value(ParamsSection_CP_duration); -end -end -% -if exist('ans', 'var') -varargout{1}=logical(ans); clear('ans'); -else -varargout{1}=false; -end -end -if eod_logic_eval -GetSoloFunctionArgs(obj); -ClearHelperVarsNotOwned(obj); -% -% Store the value of the total cp duration reached: -stage4_trial_counter_today = 0; -last_session_cp = value(ParamsSection_CP_duration); -% -end -% - -%% Introduce Stimuli Sound during Centre Poke - -% -case 'Introduce Stimuli Sound during Centre Poke' -if helper_vars_eval -GetSoloFunctionArgs(obj); -ClearHelperVarsNotOwned(obj); -% -cp_max = value(Training_ParamsSection_max_CP); -cp_min = value(Training_ParamsSection_min_CP); -% Fractional increment in center poke duration every time there is a non-cp-violation trial: -cp_fraction = value(Training_ParamsSection_CPfraction_inc); -% Minimum increment (in secs) in center poke duration every time there is a non-cp-violation trial: -cp_minimum_increment = 0.001; -% cp value for last session -last_session_cp = 0; -% Starting total center poke duration: -starting_cp = value(Training_ParamsSection_starting_CP) + value(ParamsSection_SettlingIn_time); -% number of warm-up trials -n_trial_warmup = value(Training_ParamsSection_warm_up_trials); -stage5_trial_counter = 0; -stage5_trial_counter_today = 0; -stage5_timeout_percent = 0; -stage5_violation_percent = 0; -% -end -if stage_algorithm_eval -GetSoloFunctionArgs(obj); -ClearHelperVarsNotOwned(obj); -% - -ParamsSection_training_stage.value = 5; -callback(ParamsSection_training_stage); -stage5_trial_counter = stage5_trial_counter + 1; -SessionPerformanceSection_ntrials_stage.value = stage5_trial_counter; -callback(SessionPerformanceSection_ntrials_stage); -stage5_trial_counter_today = stage5_trial_counter_today + 1; -SessionPerformanceSection_ntrials_stage_today.value = stage5_trial_counter_today; -callback(SessionPerformanceSection_ntrials_stage_today); -stage5_timeout_percent = ((stage5_timeout_percent * stage5_trial_counter) + double(timeout_history(end)))/(stage5_trial_counter + 1); -stage5_violation_percent = ((stage5_violation_percent * stage5_trial_counter) + double(violation_history(end)))/(stage5_trial_counter + 1); -SessionPerformanceSection_timeout_stage.value = stage5_timeout_percent; -callback(SessionPerformanceSection_timeout_stage); -SessionPerformanceSection_violation_stage.value = stage5_violation_percent; -callback(SessionPerformanceSection_violation_stage); -% Change the value of CP Duration - -% Since starting a new session then do a pre warm up to last saved cp -% duration else continue with learning with increased poke time -if n_completed_trials == 0 - ParamsSection_CP_duration.value = value(ParamsSection_init_CP_duration); -elseif n_completed_trials == 1 - ParamsSection_CP_duration.value = starting_cp; -else - if ~violation_history(end) && ~timeout_history(end) - if value(ParamsSection_CP_duration) < max([cp_min,last_session_cp]) % warm up stage - increment = (max([cp_min,last_session_cp]) - value(ParamsSection_CP_duration))/ (n_trial_warmup - 1); - else - if value(ParamsSection_CP_duration) >= last_session_cp && value(ParamsSection_CP_duration) <= cp_max % no warm up stage - increment = value(ParamsSection_CP_duration)*cp_fraction; - if increment < cp_minimum_increment - increment = value(cp_minimum_increment); - end - end - end - - ParamsSection_CP_duration.value = value(ParamsSection_CP_duration) + increment; - - % Check if the values are within the required range - if value(ParamsSection_CP_duration) < starting_cp - ParamsSection_CP_duration.value = starting_cp; - end - if value(ParamsSection_CP_duration) > cp_max - ParamsSection_CP_duration.value = cp_max; - end - - end -end -callback(ParamsSection_CP_duration); - -if value(ParamsSection_CP_duration) >= 1 - ParamsSection_PreStim_time.value = 0.4; - if value(ParamsSection_CP_duration) < 2 - ParamsSection_A1_time.value = 0.1; - elseif value(ParamsSection_CP_duration) < 2.5 && value(ParamsSection_CP_duration) >= 2 - ParamsSection_A1_time.value = 0.2; - elseif value(ParamsSection_CP_duration) < 3 && value(ParamsSection_CP_duration) >= 2.5 - ParamsSection_A1_time.value = 0.3; - else - ParamsSection_A1_time.value = 0.4; - end -elseif value(ParamsSection_CP_duration) < 1 && value(ParamsSection_CP_duration) >= starting_cp - ParamsSection_SettlingIn_time.value = 0.2; - callback(ParamsSection_SettlingIn_time); - ParamsSection_PreStim_time.value = 0.1; - ParamsSection_A1_time.value = 0.1; -end - -if value(ParamsSection_CP_duration) >= starting_cp - ParamsSection_time_bet_aud1_gocue.value = value(ParamsSection_CP_duration) - value(ParamsSection_SettlingIn_time) - value(ParamsSection_A1_time) - value(ParamsSection_PreStim_time); - callback(ParamsSection_time_bet_aud1_gocue) - callback(ParamsSection_PreStim_time); - callback(ParamsSection_A1_time); -end - - -% -end -if completion_test_eval -GetSoloFunctionArgs(obj); -ClearHelperVarsNotOwned(obj); -clear('ans'); -% -if value(ParamsSection_CP_duration) >= value(Training_ParamsSection_max_CP) && stage5_trial_counter > value(Training_ParamsSection_total_trials) - if SessionPerformanceSection_violation_recent < value(Training_ParamsSection_recent_violation) && SessionPerformanceSection_timeout_recent < value(Training_ParamsSection_recent_timeout) && ... - stage5_violation_percent < value(Training_ParamsSection_stage_violation) && n_completed_trials > 100 - SessionDefinition(obj, 'jump_to_stage', 'Vary Stimuli location during Centre Poke'); - last_session_cp = value(ParamsSection_CP_duration); - end -end -% -if exist('ans', 'var') -varargout{1}=logical(ans); clear('ans'); -else -varargout{1}=false; -end -end -if eod_logic_eval -GetSoloFunctionArgs(obj); -ClearHelperVarsNotOwned(obj); -% -stage5_trial_counter_today = 0; -last_session_cp = value(ParamsSection_CP_duration); -% -end -% - -%% Vary Stimuli location during Centre Poke - -% -case 'Vary Stimuli location during Centre Poke' -if helper_vars_eval -GetSoloFunctionArgs(obj); -ClearHelperVarsNotOwned(obj); -% -stage6_trial_counter = 0; -stage6_trial_counter_today = 0; -stage6_timeout_percent = 0; -stage6_violation_percent = 0; -cp_max = value(Training_ParamsSection_max_CP); -% Starting total center poke duration: -starting_cp = value(Training_ParamsSection_starting_CP) + value(ParamsSection_SettlingIn_time); -% number of warm-up trials -n_trial_warmup = value(Training_ParamsSection_warm_up_trials); -prestim_min = value(Training_ParamsSection_min_prestim); -prestim_max = value(Training_ParamsSection_max_prestim); -stim_dur = value(Training_ParamsSection_stim_dur); -% -end -if stage_algorithm_eval -GetSoloFunctionArgs(obj); -ClearHelperVarsNotOwned(obj); -% -ParamsSection_training_stage.value = 6; -callback(ParamsSection_training_stage); -stage6_trial_counter = stage6_trial_counter + 1; -SessionPerformanceSection_ntrials_stage.value = stage6_trial_counter; -callback(SessionPerformanceSection_ntrials_stage); -stage6_trial_counter_today = stage6_trial_counter_today + 1; -SessionPerformanceSection_ntrials_stage_today.value = stage6_trial_counter_today; -callback(SessionPerformanceSection_ntrials_stage_today); -stage6_timeout_percent = ((stage6_timeout_percent * stage6_trial_counter) + double(timeout_history(end)))/(stage6_trial_counter + 1); -stage6_violation_percent = ((stage6_violation_percent * stage6_trial_counter) + double(violation_history(end)))/(stage6_trial_counter + 1); -SessionPerformanceSection_timeout_stage.value = stage6_timeout_percent; -callback(SessionPerformanceSection_timeout_stage); -SessionPerformanceSection_violation_stage.value = stage6_violation_percent; -callback(SessionPerformanceSection_violation_stage); -% Warm Up If starting a new session -if n_completed_trials == 0 - ParamsSection_CP_duration.value = value(ParamsSection_init_CP_duration); -elseif n_completed_trials == 1 - ParamsSection_CP_duration.value = starting_cp; -else - if value(ParamsSection_CP_duration) < cp_max % warm up stage - if ~violation_history(end) && ~timeout_history(end) - increment = (cp_max - value(ParamsSection_CP_duration))/ (n_trial_warmup - 1); - ParamsSection_CP_duration.value = value(ParamsSection_CP_duration) + increment; - % Check if the values are within the required range - if value(ParamsSection_CP_duration) < starting_cp - ParamsSection_CP_duration.value = starting_cp; - end - if value(ParamsSection_CP_duration) > cp_max - ParamsSection_CP_duration.value = cp_max; - end - end - end -end - -callback(ParamsSection_CP_duration); - -if value(ParamsSection_CP_duration) < 3 && value(ParamsSection_CP_duration) >= starting_cp % during the warm up phase - ParamsSection_SettlingIn_time.value = 0.2; - callback(ParamsSection_SettlingIn_time); - ParamsSection_PreStim_time.value = 0.1; - ParamsSection_A1_time.value = 0.1; -else - ParamsSection_A1_time.value = stim_dur; % actual training stage - time_range_PreStim_time = prestim_min : 0.01 : prestim_max; - ParamsSection_PreStim_time.value = time_range_PreStim_time(randi([1, numel(time_range_PreStim_time)],1,1)); -end - -if value(ParamsSection_CP_duration) >= starting_cp - ParamsSection_time_bet_aud1_gocue.value = value(ParamsSection_CP_duration) - value(ParamsSection_SettlingIn_time) - value(ParamsSection_A1_time) - value(ParamsSection_PreStim_time); - callback(ParamsSection_time_bet_aud1_gocue) - callback(ParamsSection_PreStim_time); - callback(ParamsSection_A1_time); -end - -% -end -if completion_test_eval -GetSoloFunctionArgs(obj); -ClearHelperVarsNotOwned(obj); -clear('ans'); -% -if stage6_trial_counter > value(Training_ParamsSection_total_trials) - if SessionPerformanceSection_violation_recent < value(Training_ParamsSection_recent_violation) && SessionPerformanceSection_timeout_recent < value(Training_ParamsSection_recent_timeout) && ... - stage6_violation_percent < value(Training_ParamsSection_stage_violation) - SessionDefinition(obj, 'jump_to_stage', 'Variable Stimuli Go Cue location during Centre Poke'); - end -end -% -if exist('ans', 'var') -varargout{1}=logical(ans); clear('ans'); -else -varargout{1}=false; -end -end -if eod_logic_eval -GetSoloFunctionArgs(obj); -ClearHelperVarsNotOwned(obj); -% -stage6_trial_counter_today = 0; -% -end -% - -%% Variable Stimuli Go Cue location during Centre Poke - -% -case 'Variable Stimuli Go Cue location during Centre Poke' -if helper_vars_eval -GetSoloFunctionArgs(obj); -ClearHelperVarsNotOwned(obj); -% -stage7_trial_counter = 0; -stage7_trial_counter_today = 0; -stage7_timeout_percent = 0; -stage7_violation_percent = 0; - -% Variables for warmup stage -cp_max = value(Training_ParamsSection_max_CP); -% Starting total center poke duration: -starting_cp = value(Training_ParamsSection_starting_CP) + value(ParamsSection_SettlingIn_time); -% number of warm-up trials -n_trial_warmup = value(Training_ParamsSection_warm_up_trials); -prestim_min = value(Training_ParamsSection_min_prestim); -prestim_max = value(Training_ParamsSection_max_prestim); -prestim_time = value(Training_ParamsSection_min_prestim); -a1_time = value(Training_ParamsSection_stim_dur); -a1_time_min = value(Training_ParamsSection_stim_dur); -a1_time_max = value(Training_ParamsSection_stim_dur + 0.1); -prego_min = value(Training_ParamsSection_min_prego); -prego_max = value(Training_ParamsSection_max_prego); -prego_time = value(Training_ParamsSection_min_prego); -warmup_completed = 0; -warm_up_on = 1; -random_prestim = 1; -random_prego = 1; -random_A1 = 0; -% -end -if stage_algorithm_eval -GetSoloFunctionArgs(obj); -ClearHelperVarsNotOwned(obj); -% - -ParamsSection_training_stage.value = 7; -callback(ParamsSection_training_stage); -stage7_trial_counter = stage7_trial_counter + 1; -SessionPerformanceSection_ntrials_stage.value = stage7_trial_counter; -callback(SessionPerformanceSection_ntrials_stage); -stage7_trial_counter_today = stage7_trial_counter_today + 1; -SessionPerformanceSection_ntrials_stage_today.value = stage7_trial_counter_today; -callback(SessionPerformanceSection_ntrials_stage_today); -stage7_timeout_percent = ((stage7_timeout_percent * stage7_trial_counter) + double(timeout_history(end)))/(stage7_trial_counter + 1); -stage7_violation_percent = ((stage7_violation_percent * stage7_trial_counter) + double(violation_history(end)))/(stage7_trial_counter + 1); -SessionPerformanceSection_timeout_stage.value = stage7_timeout_percent; -callback(SessionPerformanceSection_timeout_stage); -SessionPerformanceSection_violation_stage.value = stage7_violation_percent; -callback(SessionPerformanceSection_violation_stage); - -% Warm Up If starting a new session -if warm_up_on == 1 - if n_completed_trials == 0 - ParamsSection_CP_duration.value = value(ParamsSection_init_CP_duration); - warmup_completed = 0; - elseif n_completed_trials == 1 - ParamsSection_CP_duration.value = starting_cp; - else - if value(ParamsSection_CP_duration) <= cp_max % warm up stage - if ~violation_history(end) && ~timeout_history(end) - increment = (cp_max - value(ParamsSection_CP_duration))/ (n_trial_warmup - 1); - ParamsSection_CP_duration.value = value(ParamsSection_CP_duration) + increment; - % Check if the values are within the required range - if value(ParamsSection_CP_duration) < starting_cp - ParamsSection_CP_duration.value = starting_cp; - end - if value(ParamsSection_CP_duration) >= cp_max - ParamsSection_CP_duration.value = cp_max; - warmup_completed = 1; - end - end - end - end -else - warmup_completed = 1; -end - -if n_completed_trials >= 1 - cp_length = value(ParamsSection_CP_duration) - value(ParamsSection_SettlingIn_time); - [ParamsSection_PreStim_time.value ,ParamsSection_A1_time.value,ParamsSection_time_bet_aud1_gocue.value] = param_time_within_range(warmup_completed,... - cp_length,prestim_min,prestim_max, random_prestim, prestim_time,... - a1_time_min,a1_time_max, random_A1, a1_time,prego_min,prego_max, random_prego, prego_time); - - callback(ParamsSection_PreStim_time); - callback(ParamsSection_A1_time); - callback(ParamsSection_time_bet_aud1_gocue); - ParamsSection_CP_duration = value(ParamsSection_SettlingIn_time) + value(ParamsSection_PreStim_time) + value(ParamsSection_A1_time) + value(ParamsSection_time_bet_aud1_gocue); -end -callback(ParamsSection_CP_duration); -% -end -if completion_test_eval -GetSoloFunctionArgs(obj); -ClearHelperVarsNotOwned(obj); -clear('ans'); -% -if stage7_trial_counter > value(Training_ParamsSection_total_trials) - if SessionPerformanceSection_violation_recent < value(Training_ParamsSection_recent_violation) && SessionPerformanceSection_timeout_recent < value(Training_ParamsSection_recent_timeout) && ... - stage7_violation_percent < value(Training_ParamsSection_stage_violation) - SessionDefinition(obj, 'jump_to_stage', 'User Setting'); - end -end -% -if exist('ans', 'var') -varargout{1}=logical(ans); clear('ans'); -else -varargout{1}=false; -end -end -if eod_logic_eval -GetSoloFunctionArgs(obj); -ClearHelperVarsNotOwned(obj); -% -stage7_trial_counter_today = 0; -% -end -% - -%% User Setting - -% - case 'User Setting' -if helper_vars_eval -GetSoloFunctionArgs(obj); -ClearHelperVarsNotOwned(obj); -% -stage8_trial_counter = 0; -stage8_trial_counter_today = 0; -stage8_timeout_percent = 0; -stage8_violation_percent = 0; - -% Variables for warmup stage -cp_max = 5; -n_trial_warmup = 20; -starting_cp = 0.5; -warmup_completed = 0; -warm_up_on = value(ParamsSection_warmup_on); -random_prestim = value(ParamsSection_random_PreStim_time); -random_prego = value(ParamsSection_random_prego_time); -random_A1 = value(ParamsSection_random_A1_time); -prestim_min = value(ParamsSection_PreStim_time_Min); -prestim_max = value(ParamsSection_PreStim_time_Max); -prestim_time = value(ParamsSection_PreStim_time); -prego_min = value(ParamsSection_time_bet_aud1_gocue_Min); -prego_max = value(ParamsSection_time_bet_aud1_gocue_Max); -prego_time = value(ParamsSection_time_bet_aud1_gocue); -a1_time = value(ParamsSection_A1_time); -a1_time_min = value(ParamsSection_A1_time_Min); -a1_time_max = value(ParamsSection_A1_time_Max); - -% -end -if stage_algorithm_eval -GetSoloFunctionArgs(obj); -ClearHelperVarsNotOwned(obj); -% - -ParamsSection_training_stage.value = 8; -callback(ParamsSection_training_stage); -stage8_trial_counter = stage8_trial_counter + 1; -SessionPerformanceSection_ntrials_stage.value = stage8_trial_counter; -callback(SessionPerformanceSection_ntrials_stage); -stage8_trial_counter_today = stage8_trial_counter_today + 1; -SessionPerformanceSection_ntrials_stage_today.value = stage8_trial_counter_today; -callback(SessionPerformanceSection_ntrials_stage_today); -stage8_timeout_percent = ((stage8_timeout_percent * stage8_trial_counter) + double(timeout_history(end)))/(stage8_trial_counter + 1); -stage8_violation_percent = ((stage8_violation_percent * stage8_trial_counter) + double(violation_history(end)))/(stage8_trial_counter + 1); -SessionPerformanceSection_timeout_stage.value = stage8_timeout_percent; -callback(SessionPerformanceSection_timeout_stage); -SessionPerformanceSection_violation_stage.value = stage8_violation_percent; -callback(SessionPerformanceSection_violation_stage); - -% Warm Up If starting a new session -if warm_up_on == 1 - if n_completed_trials == 0 - ParamsSection_CP_duration.value = value(ParamsSection_init_CP_duration); - warmup_completed = 0; - elseif n_completed_trials == 1 - ParamsSection_CP_duration.value = starting_cp; - else - if value(ParamsSection_CP_duration) <= cp_max % warm up stage - if ~violation_history(end) && ~timeout_history(end) - increment = (cp_max - value(ParamsSection_CP_duration))/ (n_trial_warmup - 1); - ParamsSection_CP_duration.value = value(ParamsSection_CP_duration) + increment; - % Check if the values are within the required range - if value(ParamsSection_CP_duration) < starting_cp - ParamsSection_CP_duration.value = starting_cp; - end - if value(ParamsSection_CP_duration) >= cp_max - ParamsSection_CP_duration.value = cp_max; - warmup_completed = 1; - end - end - end - end -else - warmup_completed = 1; -end - -if n_completed_trials >= 1 - cp_length = value(ParamsSection_CP_duration) - value(ParamsSection_SettlingIn_time); - [ParamsSection_PreStim_time.value ,ParamsSection_A1_time.value,ParamsSection_time_bet_aud1_gocue.value] = param_time_within_range(warmup_completed,... - cp_length,prestim_min,prestim_max, random_prestim, prestim_time,... - a1_time_min,a1_time_max, random_A1, a1_time,prego_min,prego_max, random_prego, prego_time); - - callback(ParamsSection_PreStim_time); - callback(ParamsSection_A1_time); - callback(ParamsSection_time_bet_aud1_gocue); - ParamsSection_CP_duration = value(ParamsSection_SettlingIn_time) + value(ParamsSection_PreStim_time) + value(ParamsSection_A1_time) + value(ParamsSection_time_bet_aud1_gocue); -end -callback(ParamsSection_CP_duration); -% -end -if completion_test_eval -GetSoloFunctionArgs(obj); -ClearHelperVarsNotOwned(obj); -clear('ans'); -% - -% -if exist('ans', 'var') -varargout{1}=logical(ans); clear('ans'); -else -varargout{1}=false; -end -end -if eod_logic_eval -GetSoloFunctionArgs(obj); -ClearHelperVarsNotOwned(obj); -% -stage8_trial_counter_today = 0; -% -end -% - - -end - -end - -%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - -% - -function [prestim,A1,prego] = param_time_within_range(fixed_length,cp_length,range_min_prestim,range_max_prestim, is_random_prestim, provided_time_prestim,... - range_min_A1,range_max_A1, is_random_A1, provided_time_A1,range_min_prego,range_max_prego, is_random_prego, provided_time_prego) - -if fixed_length == 1 % warm up stage where cp length is increasing -% then calculate the range/typical value - if cp_length <= 0.3 - prestim = 0.1; - A1 = 0.1; - prego = 0.1; - else - range_size = round(0.3 * cp_length,1); - if range_size > 0.4 - step_size = 0.1; - else - step_size = 0.01; - end - - timerange = 0.1:step_size:range_size; - - if is_random_prestim == 1 - prestim = timerange(randi([1, numel(timerange)],1,1)); - else - if provided_time_prestim <= range_size - prestim = provided_time_prestim; - else - prestim = range_size; - end - - end - - if is_random_A1 == 1 - A1 = timerange(randi([1, numel(timerange)],1,1)); - else - if provided_time_A1 <= range_size - A1 = provided_time_A1; - else - A1 = range_size; - end - end - - prego = cp_length - prestim - A1; - - end - -else - - if is_random_prestim == 1 - range_size_prestim = range_max_prestim - range_min_prestim; - if range_size_prestim > 0.4 - step_size_prestim = 0.1; - else - step_size_prestim = 0.01; - end - time_range_prestim = range_min_prestim:step_size_prestim:range_max_prestim; - prestim = time_range_prestim(randi([1, numel(time_range_prestim)],1,1)); - else - prestim = provided_time_prestim; - end - - if is_random_A1 == 1 - range_size_A1 = range_max_A1 - range_min_A1; - if range_size_A1 > 0.4 - step_size_A1 = 0.1; - else - step_size_A1 = 0.01; - end - time_range_A1 = range_min_A1:step_size_A1:range_max_A1; - A1 = time_range_A1(randi([1, numel(time_range_A1)],1,1)); - else - A1 = provided_time_A1; - end - - if is_random_prego == 1 - range_size_prego = range_max_prego - range_min_prego; - if range_size_prego > 0.4 - step_size_prego = 0.1; - else - step_size_prego = 0.01; - end - time_range_prego = range_min_prego:step_size_prego:range_max_prego; - prego = time_range_prego(randi([1, numel(time_range_prego)],1,1)); - else - prego = provided_time_prego; - end - -end -end - - -% - -%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% diff --git a/Protocols/@Arpit_CentrePokeTraining/pipeline_Arpit_CentrePokeTraining_experimenter_ratname_250319c.m b/Protocols/@Arpit_CentrePokeTraining/pipeline_Arpit_CentrePokeTraining_experimenter_ratname_250319c.m index a7200d5d..ad280798 100644 --- a/Protocols/@Arpit_CentrePokeTraining/pipeline_Arpit_CentrePokeTraining_experimenter_ratname_250319c.m +++ b/Protocols/@Arpit_CentrePokeTraining/pipeline_Arpit_CentrePokeTraining_experimenter_ratname_250319c.m @@ -19,43 +19,62 @@ % case 'Familiarize with Reward Side Pokes' - if helper_vars_eval - GetSoloFunctionArgs(obj); - ClearHelperVarsNotOwned(obj); - % - CreateHelperVar(obj,'stage1_trial_counter','value',zeros(1,8)); - CreateHelperVar(obj,'stage1_trial_counter_today','value',zeros(1,8)); - CreateHelperVar(obj,'stage1_trial_counter_oppSide','value',zeros(1,8)); - % - end - if stage_algorithm_eval - GetSoloFunctionArgs(obj); - ClearHelperVarsNotOwned(obj); - % - ParamsSection_MaxSame.value = 4; - callback(ParamsSection_MaxSame); - ParamsSection_training_stage.value = 1; - callback(ParamsSection_training_stage); - stage1_trial_counter(1) = stage1_trial_counter(1) + 1; - SessionPerformanceSection_ntrials_stage.value = value(stage1_trial_counter); - callback(SessionPerformanceSection_ntrials_stage) - stage1_trial_counter_today = stage1_trial_counter_today + 1; - SessionPerformanceSection_ntrials_stage_today.value = value(stage1_trial_counter_today); - callback(SessionPerformanceSection_ntrials_stage_today) - if value(previous_sides(end)) ~= value(ParamsSection_ThisTrial) - stage1_trial_counter_oppSide = stage1_trial_counter_oppSide + 1; - end - % - end + + +if helper_vars_eval +GetSoloFunctionArgs(obj); +ClearHelperVarsNotOwned(obj); +% +CreateHelperVar(obj,'stages_trial_counter','value',zeros(1,8)); +CreateHelperVar(obj,'stages_trial_counter_today','value',zeros(1,8)); +CreateHelperVar(obj,'stages_trial_counter_oppSide','value',zeros(1,8)); +CreateHelperVar(obj,'stages_timeout_rate','value',zeros(1,8)); +CreateHelperVar(obj,'stages_violation_rate','value',zeros(1,8)); +% +end + + +if stage_algorithm_eval +GetSoloFunctionArgs(obj); +ClearHelperVarsNotOwned(obj); + + +% +ParamsSection_MaxSame.value = 4; +callback(ParamsSection_MaxSame); +stage_no = value(SessionDefinition_CURRENT_ACTIVE_STAGE); +ParamsSection_training_stage.value = stage_no; +callback(ParamsSection_training_stage); +this_stage_trial_counter = value(stages_trial_counter); +this_stage_trial_counter(stage_no) = this_stage_trial_counter(stage_no) + 1; +SessionPerformanceSection_ntrials_stage.value = this_stage_trial_counter(stage_no); +callback(SessionPerformanceSection_ntrials_stage); +stages_trial_counter.value = this_stage_trial_counter; +this_stage_trial_counter_today = value(stages_trial_counter_today); +this_stage_trial_counter_today(stage_no) = this_stage_trial_counter_today(stage_no) + 1; +SessionPerformanceSection_ntrials_stage_today.value = this_stage_trial_counter_today(stage_no); +callback(SessionPerformanceSection_ntrials_stage_today); +stages_trial_counter_today.value = this_stage_trial_counter_today; +this_stage_trial_counter_oppSide = value(stages_trial_counter_oppSide); +if value(previous_sides(end)) ~= value(ParamsSection_ThisTrial) + this_stage_trial_counter_oppSide(stage_no) = this_stage_trial_counter_oppSide(stage_no) + 1; +end +stages_trial_counter_oppSide.value = this_stage_trial_counter_oppSide; +% +end + + if completion_test_eval GetSoloFunctionArgs(obj); ClearHelperVarsNotOwned(obj); clear('ans'); % -% only run it if its the start of the day, so number of trials the rat did -% is less +stage_no = value(SessionDefinition_CURRENT_ACTIVE_STAGE); +this_stage_trial_counter_oppSide = value(stages_trial_counter_oppSide); +this_stage_trial_counter = value(stages_trial_counter); +% only run it if its the start of the day, number of trials is small if n_completed_trials < 100 - if value(stage1_trial_counter) > value(Training_ParamsSection_total_trials) && value(stage1_trial_counter_oppSide) > value(Training_ParamsSection_total_trials_opp) + if this_stage_trial_counter(stage_no) > value(Training_ParamsSection_total_trials) && this_stage_trial_counter_oppSide(stage_no) > value(Training_ParamsSection_total_trials_opp) SessionDefinition(obj, 'jump_to_stage', 'Timeout Rewarded Side Pokes'); end end @@ -66,69 +85,94 @@ varargout{1}=false; end end + + if eod_logic_eval GetSoloFunctionArgs(obj); ClearHelperVarsNotOwned(obj); % -stage1_trial_counter_today.value = 0; -if value(stage1_trial_counter) > value(Training_ParamsSection_total_trials) && value(stage1_trial_counter_oppSide) > value(Training_ParamsSection_total_trials_opp) +stage_no = value(SessionDefinition_CURRENT_ACTIVE_STAGE); +this_stage_trial_counter_oppSide = value(stages_trial_counter_oppSide); +this_stage_trial_counter = value(stages_trial_counter); +stages_trial_counter_today.value = zeros(1,8); +if this_stage_trial_counter(stage_no) > value(Training_ParamsSection_total_trials) && this_stage_trial_counter_oppSide(stage_no) > value(Training_ParamsSection_total_trials_opp) SessionDefinition(obj, 'jump_to_stage', 'Timeout Rewarded Side Pokes'); end % end + % + + + + %% Timeout Rewarded Side Pokes % + case 'Timeout Rewarded Side Pokes' + + if helper_vars_eval GetSoloFunctionArgs(obj); ClearHelperVarsNotOwned(obj); % -stage2_trial_counter.value = 0; -stage2_trial_counter_today.value = 0; -stage2_trial_counter_oppSide.value = 0; -opp_side_trials.value = 0; -stage2_timeout_percent.value = 0; +CreateHelperVar(obj,'stages_trial_counter','value',zeros(1,8)); +CreateHelperVar(obj,'stages_trial_counter_today','value',zeros(1,8)); +CreateHelperVar(obj,'stages_trial_counter_oppSide','value',zeros(1,8)); +CreateHelperVar(obj,'stages_timeout_rate','value',zeros(1,8)); +CreateHelperVar(obj,'stages_violation_rate','value',zeros(1,8)); +CreateHelperVar(obj,'this_stage_opp_side_trials', 'value', '0', 'forceinit','true'); % end + + if stage_algorithm_eval GetSoloFunctionArgs(obj); ClearHelperVarsNotOwned(obj); % +% Change ParamsSection Vars ParamsSection_MaxSame.value = 3; callback(ParamsSection_MaxSame); -ParamsSection_training_stage.value = 2; +stage_no = value(SessionDefinition_CURRENT_ACTIVE_STAGE); +ParamsSection_training_stage.value = stage_no; callback(ParamsSection_training_stage); -stage2_trial_counter.value = value(stage2_trial_counter) + 1; -SessionPerformanceSection_ntrials_stage.value = value(stage2_trial_counter); +% Update the helper vars +this_stage_trial_counter = value(stages_trial_counter); +this_stage_trial_counter(stage_no) = this_stage_trial_counter(stage_no) + 1; +SessionPerformanceSection_ntrials_stage.value = this_stage_trial_counter(stage_no); callback(SessionPerformanceSection_ntrials_stage); -stage2_trial_counter_today.value = value(stage2_trial_counter_today) + 1; -SessionPerformanceSection_ntrials_stage_today.value = value(stage2_trial_counter_today); +stages_trial_counter.value = this_stage_trial_counter; + +this_stage_trial_counter_today = value(stages_trial_counter_today); +this_stage_trial_counter_today(stage_no) = this_stage_trial_counter_today(stage_no) + 1; +SessionPerformanceSection_ntrials_stage_today.value = this_stage_trial_counter_today(stage_no); callback(SessionPerformanceSection_ntrials_stage_today); +stages_trial_counter_today.value = this_stage_trial_counter_today; +this_stage_trial_counter_oppSide = value(stages_trial_counter_oppSide); if value(previous_sides(end)) ~= value(ParamsSection_ThisTrial) - stage2_trial_counter_oppSide.value = value(stage2_trial_counter_oppSide) + 1; - opp_side_trials.value = value(opp_side_trials) + 1; + this_stage_trial_counter_oppSide(stage_no) = this_stage_trial_counter_oppSide(stage_no) + 1; + this_stage_opp_side_trials.value = value(this_stage_opp_side_trials) + 1; end +stages_trial_counter_oppSide.value = this_stage_trial_counter_oppSide; + % Update the reward collection time based upon behav if size(timeout_history) > 5 - if all(value(timeout_history(end-1:end))) && value(opp_side_trials) >= 2 + if all(value(timeout_history(end-1:end))) && value(this_stage_opp_side_trials) >= 2 ParamsSection_RewardCollection_duration.value = min([value(ParamsSection_RewardCollection_duration) + 1,... value(Training_ParamsSection_max_rColl_dur)]); callback(ParamsSection_RewardCollection_duration); - opp_side_trials.value = 0; + this_stage_opp_side_trials.value = 0; end - - if ~any(value(timeout_history(end-1:end))) && value(opp_side_trials) >= 2 + if ~any(value(timeout_history(end-1:end))) && value(this_stage_opp_side_trials) >= 2 ParamsSection_RewardCollection_duration.value = max([value(ParamsSection_RewardCollection_duration) - 1,... value(Training_ParamsSection_min_rColl_dur)]); callback(ParamsSection_RewardCollection_duration); - opp_side_trials.value = 0; + this_stage_opp_side_trials.value = 0; end end - if size(timeout_history) > 20 if all(value(timeout_history(end-19:end))) ParamsSection_RewardCollection_duration.value = 30; @@ -136,21 +180,29 @@ end end -stage2_timeout_percent.value = ((value(stage2_timeout_percent) * value(stage2_trial_counter)) + double(timeout_history(end)))/(value(stage2_trial_counter) + 1); -SessionPerformanceSection_timeout_stage.value = value(stage2_timeout_percent); +this_stage_timeout_percent = value(stages_timeout_rate); +this_stage_timeout_percent(stage_no) = ((this_stage_timeout_percent(stage_no) * this_stage_trial_counter(stage_no)) + double(timeout_history(end)))/(this_stage_trial_counter(stage_no) + 1); +SessionPerformanceSection_timeout_stage.value = this_stage_timeout_percent(stage_no); callback(SessionPerformanceSection_timeout_stage); +stages_timeout_rate.value = this_stage_timeout_percent; % end + + if completion_test_eval GetSoloFunctionArgs(obj); ClearHelperVarsNotOwned(obj); clear('ans'); % +stage_no = value(SessionDefinition_CURRENT_ACTIVE_STAGE); +this_stage_trial_counter_oppSide = value(stages_trial_counter_oppSide); +this_stage_trial_counter = value(stages_trial_counter); +% only run it if its the start of the day, number of trials is small if n_completed_trials > 50 - if value(stage2_trial_counter) > value(Training_ParamsSection_total_trials) && value(stage2_trial_counter_oppSide) > value(Training_ParamsSection_total_trials_opp) - SessionDefinition(obj, 'jump_to_stage', 'Introduce Centre Poke'); + if this_stage_trial_counter(stage_no) > value(Training_ParamsSection_total_trials) && this_stage_trial_counter_oppSide(stage_no) > value(Training_ParamsSection_total_trials_opp) + SessionDefinition(obj, 'jump_to_stage', 'Introduce Centre Poke'); end -end +end % if exist('ans', 'var') varargout{1}=logical(ans); clear('ans'); @@ -158,15 +210,20 @@ varargout{1}=false; end end + + if eod_logic_eval GetSoloFunctionArgs(obj); ClearHelperVarsNotOwned(obj); % -stage2_trial_counter_today.value = 0; +stages_trial_counter_today.value = zeros(1,8); % end % + + + %% Introduce Centre Poke % @@ -175,43 +232,58 @@ GetSoloFunctionArgs(obj); ClearHelperVarsNotOwned(obj); % -% Maximum & Minimum duration of center poke, in secs: -cp_max = value(ParamsSection_SettlingIn_time) + value(ParamsSection_legal_cbreak); -cp_min = value(ParamsSection_init_CP_duration); -% Minimum increment (in secs) in center poke duration every time there is a non-cp-violation trial: -cp_minimum_increment = 0.001; -stage3_trial_counter = 0; -stage3_trial_counter_today = 0; -stage3_timeout_percent = 0; -last_session_cp = 0; +CreateHelperVar(obj,'stages_trial_counter','value',zeros(1,8)); +CreateHelperVar(obj,'stages_trial_counter_today','value',zeros(1,8)); +CreateHelperVar(obj,'stages_trial_counter_oppSide','value',zeros(1,8)); +CreateHelperVar(obj,'stages_timeout_rate','value',zeros(1,8)); +CreateHelperVar(obj,'stages_violation_rate','value',zeros(1,8)); +CreateHelperVar(obj,'last_session_CP','value',0); % end + + if stage_algorithm_eval GetSoloFunctionArgs(obj); ClearHelperVarsNotOwned(obj); % +% Maximum & Minimum duration of center poke, in secs: +cp_max = value(ParamsSection_SettlingIn_time) + value(ParamsSection_legal_cbreak); +cp_min = value(ParamsSection_init_CP_duration); +% Minimum increment (in secs) in center poke duration every time there is a non-cp-violation trial: +cp_minimum_increment = 0.001; ParamsSection_MaxSame.value = Inf; callback(ParamsSection_MaxSame); -ParamsSection_training_stage.value = 3; + +stage_no = value(SessionDefinition_CURRENT_ACTIVE_STAGE); +ParamsSection_training_stage.value = stage_no; callback(ParamsSection_training_stage); -stage3_trial_counter.value = value(stage3_trial_counter) + 1; -SessionPerformanceSection_ntrials_stage.value = value(stage3_trial_counter); +% Update the helper vars +this_stage_trial_counter = value(stages_trial_counter); +this_stage_trial_counter(stage_no) = this_stage_trial_counter(stage_no) + 1; +SessionPerformanceSection_ntrials_stage.value = this_stage_trial_counter(stage_no); callback(SessionPerformanceSection_ntrials_stage); -stage3_trial_counter_today.value = value(stage3_trial_counter_today) + 1; -SessionPerformanceSection_ntrials_stage_today.value = value(stage3_trial_counter_today); +stages_trial_counter.value = this_stage_trial_counter; + +this_stage_trial_counter_today = value(stages_trial_counter_today); +this_stage_trial_counter_today(stage_no) = this_stage_trial_counter_today(stage_no) + 1; +SessionPerformanceSection_ntrials_stage_today.value = this_stage_trial_counter_today(stage_no); callback(SessionPerformanceSection_ntrials_stage_today); -stage3_timeout_percent.value = ((value(stage3_timeout_percent) * value(stage3_trial_counter)) + double(timeout_history(end)))/(value(stage3_trial_counter) + 1); -SessionPerformanceSection_timeout_stage.value = value(stage3_timeout_percent); +stages_trial_counter_today.value = this_stage_trial_counter_today; + +this_stage_timeout_percent = value(stages_timeout_rate); +this_stage_timeout_percent(stage_no) = ((this_stage_timeout_percent(stage_no) * this_stage_trial_counter(stage_no)) + double(timeout_history(end)))/(this_stage_trial_counter(stage_no) + 1); +SessionPerformanceSection_timeout_stage.value = this_stage_timeout_percent(stage_no); callback(SessionPerformanceSection_timeout_stage); +stages_timeout_rate.value = this_stage_timeout_percent; % Change the value of CP Duration if n_completed_trials < 1 ParamsSection_CP_duration.value = value(ParamsSection_init_CP_duration); else - if ~timeout_history(end) && value(ParamsSection_CP_duration) < value(cp_max) + if ~timeout_history(end) && value(ParamsSection_CP_duration) < cp_max increment = value(ParamsSection_CP_duration) * value(Training_ParamsSection_CPfraction_inc); - if increment < value(cp_minimum_increment) - increment = value(cp_minimum_increment); + if increment < cp_minimum_increment + increment = cp_minimum_increment; end ParamsSection_CP_duration.value = value(ParamsSection_CP_duration) + increment; end @@ -219,12 +291,15 @@ callback(ParamsSection_CP_duration); % end + + if completion_test_eval GetSoloFunctionArgs(obj); ClearHelperVarsNotOwned(obj); clear('ans'); % -if value(ParamsSection_CP_duration) >= value(cp_max) +cp_max = value(ParamsSection_SettlingIn_time) + value(ParamsSection_legal_cbreak); +if value(ParamsSection_CP_duration) >= cp_max SessionDefinition(obj, 'jump_to_stage', 'Introduce Violation for Centre Poke'); end % @@ -234,12 +309,15 @@ varargout{1}=false; end end + + + if eod_logic_eval GetSoloFunctionArgs(obj); ClearHelperVarsNotOwned(obj); % -stage3_trial_counter_today.value = 0; -last_session_cp.value = value(ParamsSection_CP_duration); +stages_trial_counter_today.value = zeros(1,8); +last_session_CP.value = value(ParamsSection_CP_duration); % end % @@ -252,6 +330,21 @@ GetSoloFunctionArgs(obj); ClearHelperVarsNotOwned(obj); % +CreateHelperVar(obj,'stages_trial_counter','value',zeros(1,8)); +CreateHelperVar(obj,'stages_trial_counter_today','value',zeros(1,8)); +CreateHelperVar(obj,'stages_trial_counter_oppSide','value',zeros(1,8)); +CreateHelperVar(obj,'stages_timeout_rate','value',zeros(1,8)); +CreateHelperVar(obj,'stages_violation_rate','value',zeros(1,8)); +CreateHelperVar(obj,'last_session_CP','value',0); +% +end + + + +if stage_algorithm_eval +GetSoloFunctionArgs(obj); +ClearHelperVarsNotOwned(obj); +% % Maximum & Minimum duration of center poke, in secs: cp_min = value(ParamsSection_SettlingIn_time) + value(ParamsSection_legal_cbreak); @@ -260,40 +353,43 @@ cp_fraction = value(Training_ParamsSection_CPfraction_inc); % Minimum increment (in secs) in center poke duration every time there is a non-cp-violation trial: cp_minimum_increment = 0.001; -last_session_cp = 0; -stage4_trial_counter = 0; -stage4_trial_counter_today = 0; -stage4_timeout_percent = 0; -stage4_violation_percent = 0; -% -end -if stage_algorithm_eval -GetSoloFunctionArgs(obj); -ClearHelperVarsNotOwned(obj); -% -ParamsSection_training_stage.value = 4; +stage_no = value(SessionDefinition_CURRENT_ACTIVE_STAGE); +ParamsSection_training_stage.value = stage_no; callback(ParamsSection_training_stage); -stage4_trial_counter = stage4_trial_counter + 1; -SessionPerformanceSection_ntrials_stage.value = stage4_trial_counter; +% Update the helper vars +this_stage_trial_counter = value(stages_trial_counter); +this_stage_trial_counter(stage_no) = this_stage_trial_counter(stage_no) + 1; +SessionPerformanceSection_ntrials_stage.value = this_stage_trial_counter(stage_no); callback(SessionPerformanceSection_ntrials_stage); -stage4_trial_counter_today = stage4_trial_counter_today + 1; -SessionPerformanceSection_ntrials_stage_today.value = stage4_trial_counter_today; +stages_trial_counter.value = this_stage_trial_counter; + +this_stage_trial_counter_today = value(stages_trial_counter_today); +this_stage_trial_counter_today(stage_no) = this_stage_trial_counter_today(stage_no) + 1; +SessionPerformanceSection_ntrials_stage_today.value = this_stage_trial_counter_today(stage_no); callback(SessionPerformanceSection_ntrials_stage_today); -stage4_timeout_percent = ((stage4_timeout_percent * stage4_trial_counter) + double(timeout_history(end)))/(stage4_trial_counter + 1); -stage4_violation_percent = ((stage4_violation_percent * stage4_trial_counter) + double(violation_history(end)))/(stage4_trial_counter + 1); -SessionPerformanceSection_timeout_stage.value = stage4_timeout_percent; +stages_trial_counter_today.value = this_stage_trial_counter_today; + +this_stage_timeout_percent = value(stages_timeout_rate); +this_stage_timeout_percent(stage_no) = ((this_stage_timeout_percent(stage_no) * this_stage_trial_counter(stage_no)) + double(timeout_history(end)))/(this_stage_trial_counter(stage_no) + 1); +SessionPerformanceSection_timeout_stage.value = this_stage_timeout_percent(stage_no); callback(SessionPerformanceSection_timeout_stage); -SessionPerformanceSection_violation_stage.value = stage4_violation_percent; +stages_timeout_rate.value = this_stage_timeout_percent; + +this_stage_violation_percent = value(stages_violation_rate); +this_stage_violation_percent(stage_no) = ((this_stage_violation_percent(stage_no) * this_stage_trial_counter(stage_no)) + double(violation_history(end)))/(this_stage_trial_counter(stage_no) + 1); +SessionPerformanceSection_violation_stage.value = this_stage_violation_percent(stage_no); callback(SessionPerformanceSection_violation_stage); +stages_violation_rate.value = this_stage_violation_percent; + % Change the value of CP Duration if n_completed_trials < 1 ParamsSection_CP_duration.value = value(ParamsSection_init_CP_duration); else if ~violation_history(end) && ~timeout_history(end) && value(ParamsSection_CP_duration) < cp_max - increment = value(ParamsSection_CP_duration)*cp_fraction; + increment = value(ParamsSection_CP_duration) * cp_fraction; if increment < cp_minimum_increment - increment = value(cp_minimum_increment); + increment = cp_minimum_increment; end ParamsSection_CP_duration.value = value(ParamsSection_CP_duration) + increment; end @@ -305,16 +401,19 @@ callback(ParamsSection_CP_duration); % end + + if completion_test_eval GetSoloFunctionArgs(obj); ClearHelperVarsNotOwned(obj); clear('ans'); % +cp_max = value(Training_ParamsSection_max_CP); if value(ParamsSection_CP_duration) >= cp_max && n_completed_trials > 100 if SessionPerformanceSection_violation_recent < value(Training_ParamsSection_recent_violation) && SessionPerformanceSection_timeout_recent < value(Training_ParamsSection_recent_timeout) && ... - stage4_violation_percent < value(Training_ParamsSection_stage_violation) + SessionPerformanceSection_violation_stage < value(Training_ParamsSection_stage_violation) SessionDefinition(obj, 'jump_to_stage', 'Introduce Stimuli Sound during Centre Poke'); - last_session_cp = value(ParamsSection_CP_duration); + last_session_CP.value = value(ParamsSection_CP_duration); end end % @@ -324,17 +423,23 @@ varargout{1}=false; end end + + if eod_logic_eval GetSoloFunctionArgs(obj); ClearHelperVarsNotOwned(obj); % % Store the value of the total cp duration reached: -stage4_trial_counter_today = 0; -last_session_cp = value(ParamsSection_CP_duration); +stages_trial_counter_today.value = zeros(1,8); +last_session_CP.value = value(ParamsSection_CP_duration); % end % + + + + %% Introduce Stimuli Sound during Centre Poke % @@ -343,43 +448,59 @@ GetSoloFunctionArgs(obj); ClearHelperVarsNotOwned(obj); % +CreateHelperVar(obj,'stages_trial_counter','value',zeros(1,8)); +CreateHelperVar(obj,'stages_trial_counter_today','value',zeros(1,8)); +CreateHelperVar(obj,'stages_trial_counter_oppSide','value',zeros(1,8)); +CreateHelperVar(obj,'stages_timeout_rate','value',zeros(1,8)); +CreateHelperVar(obj,'stages_violation_rate','value',zeros(1,8)); +CreateHelperVar(obj,'last_session_CP','value',0); +% +end + + +if stage_algorithm_eval +GetSoloFunctionArgs(obj); +ClearHelperVarsNotOwned(obj); +% cp_max = value(Training_ParamsSection_max_CP); cp_min = value(Training_ParamsSection_min_CP); % Fractional increment in center poke duration every time there is a non-cp-violation trial: cp_fraction = value(Training_ParamsSection_CPfraction_inc); % Minimum increment (in secs) in center poke duration every time there is a non-cp-violation trial: cp_minimum_increment = 0.001; -% cp value for last session -last_session_cp = 0; % Starting total center poke duration: starting_cp = value(Training_ParamsSection_starting_CP) + value(ParamsSection_SettlingIn_time); % number of warm-up trials n_trial_warmup = value(Training_ParamsSection_warm_up_trials); -stage5_trial_counter = 0; -stage5_trial_counter_today = 0; -stage5_timeout_percent = 0; -stage5_violation_percent = 0; -% -end -if stage_algorithm_eval -GetSoloFunctionArgs(obj); -ClearHelperVarsNotOwned(obj); -% -ParamsSection_training_stage.value = 5; +stage_no = value(SessionDefinition_CURRENT_ACTIVE_STAGE); +ParamsSection_training_stage.value = stage_no; callback(ParamsSection_training_stage); -stage5_trial_counter = stage5_trial_counter + 1; -SessionPerformanceSection_ntrials_stage.value = stage5_trial_counter; +% Update the helper vars +this_stage_trial_counter = value(stages_trial_counter); +this_stage_trial_counter(stage_no) = this_stage_trial_counter(stage_no) + 1; +SessionPerformanceSection_ntrials_stage.value = this_stage_trial_counter(stage_no); callback(SessionPerformanceSection_ntrials_stage); -stage5_trial_counter_today = stage5_trial_counter_today + 1; -SessionPerformanceSection_ntrials_stage_today.value = stage5_trial_counter_today; +stages_trial_counter.value = this_stage_trial_counter; + +this_stage_trial_counter_today = value(stages_trial_counter_today); +this_stage_trial_counter_today(stage_no) = this_stage_trial_counter_today(stage_no) + 1; +SessionPerformanceSection_ntrials_stage_today.value = this_stage_trial_counter_today(stage_no); callback(SessionPerformanceSection_ntrials_stage_today); -stage5_timeout_percent = ((stage5_timeout_percent * stage5_trial_counter) + double(timeout_history(end)))/(stage5_trial_counter + 1); -stage5_violation_percent = ((stage5_violation_percent * stage5_trial_counter) + double(violation_history(end)))/(stage5_trial_counter + 1); -SessionPerformanceSection_timeout_stage.value = stage5_timeout_percent; +stages_trial_counter_today.value = this_stage_trial_counter_today; + +this_stage_timeout_percent = value(stages_timeout_rate); +this_stage_timeout_percent(stage_no) = ((this_stage_timeout_percent(stage_no) * this_stage_trial_counter(stage_no)) + double(timeout_history(end)))/(this_stage_trial_counter(stage_no) + 1); +SessionPerformanceSection_timeout_stage.value = this_stage_timeout_percent(stage_no); callback(SessionPerformanceSection_timeout_stage); -SessionPerformanceSection_violation_stage.value = stage5_violation_percent; +stages_timeout_rate.value = this_stage_timeout_percent; + +this_stage_violation_percent = value(stages_violation_rate); +this_stage_violation_percent(stage_no) = ((this_stage_violation_percent(stage_no) * this_stage_trial_counter(stage_no)) + double(violation_history(end)))/(this_stage_trial_counter(stage_no) + 1); +SessionPerformanceSection_violation_stage.value = this_stage_violation_percent(stage_no); callback(SessionPerformanceSection_violation_stage); +stages_violation_rate.value = this_stage_violation_percent; + % Change the value of CP Duration % Since starting a new session then do a pre warm up to last saved cp @@ -390,13 +511,13 @@ ParamsSection_CP_duration.value = starting_cp; else if ~violation_history(end) && ~timeout_history(end) - if value(ParamsSection_CP_duration) < max([cp_min,last_session_cp]) % warm up stage - increment = (max([cp_min,last_session_cp]) - value(ParamsSection_CP_duration))/ (n_trial_warmup - 1); + if value(ParamsSection_CP_duration) < max([cp_min,value(last_session_CP)]) % warm up stage + increment = (max([cp_min,value(last_session_CP)]) - value(ParamsSection_CP_duration))/ (n_trial_warmup - 1); else - if value(ParamsSection_CP_duration) >= last_session_cp && value(ParamsSection_CP_duration) <= cp_max % no warm up stage + if value(ParamsSection_CP_duration) >= value(last_session_CP) && value(ParamsSection_CP_duration) <= cp_max % no warm up stage increment = value(ParamsSection_CP_duration)*cp_fraction; if increment < cp_minimum_increment - increment = value(cp_minimum_increment); + increment = cp_minimum_increment; end end end @@ -439,20 +560,23 @@ callback(ParamsSection_PreStim_time); callback(ParamsSection_A1_time); end - - % end + + + if completion_test_eval GetSoloFunctionArgs(obj); ClearHelperVarsNotOwned(obj); clear('ans'); % -if value(ParamsSection_CP_duration) >= value(Training_ParamsSection_max_CP) && stage5_trial_counter > value(Training_ParamsSection_total_trials) +stage_no = value(SessionDefinition_CURRENT_ACTIVE_STAGE); +this_stage_trial_counter = value(stages_trial_counter); +if value(ParamsSection_CP_duration) >= value(Training_ParamsSection_max_CP) && this_stage_trial_counter(stage_no) > value(Training_ParamsSection_total_trials) if SessionPerformanceSection_violation_recent < value(Training_ParamsSection_recent_violation) && SessionPerformanceSection_timeout_recent < value(Training_ParamsSection_recent_timeout) && ... - stage5_violation_percent < value(Training_ParamsSection_stage_violation) && n_completed_trials > 100 + SessionPerformanceSection_violation_stage < value(Training_ParamsSection_stage_violation) && n_completed_trials > 100 SessionDefinition(obj, 'jump_to_stage', 'Vary Stimuli location during Centre Poke'); - last_session_cp = value(ParamsSection_CP_duration); + last_session_CP.value = value(ParamsSection_CP_duration); end end % @@ -462,14 +586,20 @@ varargout{1}=false; end end + + + if eod_logic_eval GetSoloFunctionArgs(obj); ClearHelperVarsNotOwned(obj); % -stage5_trial_counter_today = 0; -last_session_cp = value(ParamsSection_CP_duration); +stages_trial_counter_today.value = zeros(1,8); +last_session_CP.value = value(ParamsSection_CP_duration); % end + + + % %% Vary Stimuli location during Centre Poke @@ -480,10 +610,21 @@ GetSoloFunctionArgs(obj); ClearHelperVarsNotOwned(obj); % -stage6_trial_counter = 0; -stage6_trial_counter_today = 0; -stage6_timeout_percent = 0; -stage6_violation_percent = 0; +CreateHelperVar(obj,'stages_trial_counter','value',zeros(1,8)); +CreateHelperVar(obj,'stages_trial_counter_today','value',zeros(1,8)); +CreateHelperVar(obj,'stages_trial_counter_oppSide','value',zeros(1,8)); +CreateHelperVar(obj,'stages_timeout_rate','value',zeros(1,8)); +CreateHelperVar(obj,'stages_violation_rate','value',zeros(1,8)); +CreateHelperVar(obj,'last_session_CP','value',0); +% +end + + + +if stage_algorithm_eval +GetSoloFunctionArgs(obj); +ClearHelperVarsNotOwned(obj); +% cp_max = value(Training_ParamsSection_max_CP); % Starting total center poke duration: starting_cp = value(Training_ParamsSection_starting_CP) + value(ParamsSection_SettlingIn_time); @@ -492,26 +633,35 @@ prestim_min = value(Training_ParamsSection_min_prestim); prestim_max = value(Training_ParamsSection_max_prestim); stim_dur = value(Training_ParamsSection_stim_dur); -% -end -if stage_algorithm_eval -GetSoloFunctionArgs(obj); -ClearHelperVarsNotOwned(obj); -% -ParamsSection_training_stage.value = 6; + +stage_no = value(SessionDefinition_CURRENT_ACTIVE_STAGE); +ParamsSection_training_stage.value = stage_no; callback(ParamsSection_training_stage); -stage6_trial_counter = stage6_trial_counter + 1; -SessionPerformanceSection_ntrials_stage.value = stage6_trial_counter; +% Update the helper vars +this_stage_trial_counter = value(stages_trial_counter); +this_stage_trial_counter(stage_no) = this_stage_trial_counter(stage_no) + 1; +SessionPerformanceSection_ntrials_stage.value = this_stage_trial_counter(stage_no); callback(SessionPerformanceSection_ntrials_stage); -stage6_trial_counter_today = stage6_trial_counter_today + 1; -SessionPerformanceSection_ntrials_stage_today.value = stage6_trial_counter_today; +stages_trial_counter.value = this_stage_trial_counter; + +this_stage_trial_counter_today = value(stages_trial_counter_today); +this_stage_trial_counter_today(stage_no) = this_stage_trial_counter_today(stage_no) + 1; +SessionPerformanceSection_ntrials_stage_today.value = this_stage_trial_counter_today(stage_no); callback(SessionPerformanceSection_ntrials_stage_today); -stage6_timeout_percent = ((stage6_timeout_percent * stage6_trial_counter) + double(timeout_history(end)))/(stage6_trial_counter + 1); -stage6_violation_percent = ((stage6_violation_percent * stage6_trial_counter) + double(violation_history(end)))/(stage6_trial_counter + 1); -SessionPerformanceSection_timeout_stage.value = stage6_timeout_percent; +stages_trial_counter_today.value = this_stage_trial_counter_today; + +this_stage_timeout_percent = value(stages_timeout_rate); +this_stage_timeout_percent(stage_no) = ((this_stage_timeout_percent(stage_no) * this_stage_trial_counter(stage_no)) + double(timeout_history(end)))/(this_stage_trial_counter(stage_no) + 1); +SessionPerformanceSection_timeout_stage.value = this_stage_timeout_percent(stage_no); callback(SessionPerformanceSection_timeout_stage); -SessionPerformanceSection_violation_stage.value = stage6_violation_percent; +stages_timeout_rate.value = this_stage_timeout_percent; + +this_stage_violation_percent = value(stages_violation_rate); +this_stage_violation_percent(stage_no) = ((this_stage_violation_percent(stage_no) * this_stage_trial_counter(stage_no)) + double(violation_history(end)))/(this_stage_trial_counter(stage_no) + 1); +SessionPerformanceSection_violation_stage.value = this_stage_violation_percent(stage_no); callback(SessionPerformanceSection_violation_stage); +stages_violation_rate.value = this_stage_violation_percent; + % Warm Up If starting a new session if n_completed_trials == 0 ParamsSection_CP_duration.value = value(ParamsSection_init_CP_duration); @@ -560,9 +710,11 @@ ClearHelperVarsNotOwned(obj); clear('ans'); % -if stage6_trial_counter > value(Training_ParamsSection_total_trials) +stage_no = value(SessionDefinition_CURRENT_ACTIVE_STAGE); +this_stage_trial_counter = value(stages_trial_counter); +if this_stage_trial_counter(stage_no) > value(Training_ParamsSection_total_trials) if SessionPerformanceSection_violation_recent < value(Training_ParamsSection_recent_violation) && SessionPerformanceSection_timeout_recent < value(Training_ParamsSection_recent_timeout) && ... - stage6_violation_percent < value(Training_ParamsSection_stage_violation) + SessionPerformanceSection_violation_stage < value(Training_ParamsSection_stage_violation) && n_completed_trials > 100 SessionDefinition(obj, 'jump_to_stage', 'Variable Stimuli Go Cue location during Centre Poke'); end end @@ -573,15 +725,20 @@ varargout{1}=false; end end + + if eod_logic_eval GetSoloFunctionArgs(obj); ClearHelperVarsNotOwned(obj); % -stage6_trial_counter_today = 0; +stages_trial_counter_today.value = zeros(1,8); % end % + + + %% Variable Stimuli Go Cue location during Centre Poke % @@ -590,11 +747,20 @@ GetSoloFunctionArgs(obj); ClearHelperVarsNotOwned(obj); % -stage7_trial_counter = 0; -stage7_trial_counter_today = 0; -stage7_timeout_percent = 0; -stage7_violation_percent = 0; +CreateHelperVar(obj,'stages_trial_counter','value',zeros(1,8)); +CreateHelperVar(obj,'stages_trial_counter_today','value',zeros(1,8)); +CreateHelperVar(obj,'stages_trial_counter_oppSide','value',zeros(1,8)); +CreateHelperVar(obj,'stages_timeout_rate','value',zeros(1,8)); +CreateHelperVar(obj,'stages_violation_rate','value',zeros(1,8)); +CreateHelperVar(obj,'last_session_CP','value',0); +% +end + +if stage_algorithm_eval +GetSoloFunctionArgs(obj); +ClearHelperVarsNotOwned(obj); +% % Variables for warmup stage cp_max = value(Training_ParamsSection_max_CP); % Starting total center poke duration: @@ -615,27 +781,34 @@ random_prestim = 1; random_prego = 1; random_A1 = 0; -% -end -if stage_algorithm_eval -GetSoloFunctionArgs(obj); -ClearHelperVarsNotOwned(obj); -% -ParamsSection_training_stage.value = 7; +stage_no = value(SessionDefinition_CURRENT_ACTIVE_STAGE); +ParamsSection_training_stage.value = stage_no; callback(ParamsSection_training_stage); -stage7_trial_counter = stage7_trial_counter + 1; -SessionPerformanceSection_ntrials_stage.value = stage7_trial_counter; +% Update the helper vars +this_stage_trial_counter = value(stages_trial_counter); +this_stage_trial_counter(stage_no) = this_stage_trial_counter(stage_no) + 1; +SessionPerformanceSection_ntrials_stage.value = this_stage_trial_counter(stage_no); callback(SessionPerformanceSection_ntrials_stage); -stage7_trial_counter_today = stage7_trial_counter_today + 1; -SessionPerformanceSection_ntrials_stage_today.value = stage7_trial_counter_today; +stages_trial_counter.value = this_stage_trial_counter; + +this_stage_trial_counter_today = value(stages_trial_counter_today); +this_stage_trial_counter_today(stage_no) = this_stage_trial_counter_today(stage_no) + 1; +SessionPerformanceSection_ntrials_stage_today.value = this_stage_trial_counter_today(stage_no); callback(SessionPerformanceSection_ntrials_stage_today); -stage7_timeout_percent = ((stage7_timeout_percent * stage7_trial_counter) + double(timeout_history(end)))/(stage7_trial_counter + 1); -stage7_violation_percent = ((stage7_violation_percent * stage7_trial_counter) + double(violation_history(end)))/(stage7_trial_counter + 1); -SessionPerformanceSection_timeout_stage.value = stage7_timeout_percent; +stages_trial_counter_today.value = this_stage_trial_counter_today; + +this_stage_timeout_percent = value(stages_timeout_rate); +this_stage_timeout_percent(stage_no) = ((this_stage_timeout_percent(stage_no) * this_stage_trial_counter(stage_no)) + double(timeout_history(end)))/(this_stage_trial_counter(stage_no) + 1); +SessionPerformanceSection_timeout_stage.value = this_stage_timeout_percent(stage_no); callback(SessionPerformanceSection_timeout_stage); -SessionPerformanceSection_violation_stage.value = stage7_violation_percent; +stages_timeout_rate.value = this_stage_timeout_percent; + +this_stage_violation_percent = value(stages_violation_rate); +this_stage_violation_percent(stage_no) = ((this_stage_violation_percent(stage_no) * this_stage_trial_counter(stage_no)) + double(violation_history(end)))/(this_stage_trial_counter(stage_no) + 1); +SessionPerformanceSection_violation_stage.value = this_stage_violation_percent(stage_no); callback(SessionPerformanceSection_violation_stage); +stages_violation_rate.value = this_stage_violation_percent; % Warm Up If starting a new session if warm_up_on == 1 @@ -678,14 +851,19 @@ callback(ParamsSection_CP_duration); % end + + + if completion_test_eval GetSoloFunctionArgs(obj); ClearHelperVarsNotOwned(obj); clear('ans'); % -if stage7_trial_counter > value(Training_ParamsSection_total_trials) +stage_no = value(SessionDefinition_CURRENT_ACTIVE_STAGE); +this_stage_trial_counter = value(stages_trial_counter); +if this_stage_trial_counter(stage_no) > value(Training_ParamsSection_total_trials) if SessionPerformanceSection_violation_recent < value(Training_ParamsSection_recent_violation) && SessionPerformanceSection_timeout_recent < value(Training_ParamsSection_recent_timeout) && ... - stage7_violation_percent < value(Training_ParamsSection_stage_violation) + SessionPerformanceSection_violation_stage < value(Training_ParamsSection_stage_violation) SessionDefinition(obj, 'jump_to_stage', 'User Setting'); end end @@ -696,11 +874,14 @@ varargout{1}=false; end end + + + if eod_logic_eval GetSoloFunctionArgs(obj); ClearHelperVarsNotOwned(obj); % -stage7_trial_counter_today = 0; +stages_trial_counter_today.value = zeros(1,8); % end % @@ -709,15 +890,27 @@ % case 'User Setting' + + if helper_vars_eval GetSoloFunctionArgs(obj); ClearHelperVarsNotOwned(obj); % -stage8_trial_counter = 0; -stage8_trial_counter_today = 0; -stage8_timeout_percent = 0; -stage8_violation_percent = 0; +CreateHelperVar(obj,'stages_trial_counter','value',zeros(1,8)); +CreateHelperVar(obj,'stages_trial_counter_today','value',zeros(1,8)); +CreateHelperVar(obj,'stages_trial_counter_oppSide','value',zeros(1,8)); +CreateHelperVar(obj,'stages_timeout_rate','value',zeros(1,8)); +CreateHelperVar(obj,'stages_violation_rate','value',zeros(1,8)); +CreateHelperVar(obj,'last_session_CP','value',0); +% +end + + +if stage_algorithm_eval +GetSoloFunctionArgs(obj); +ClearHelperVarsNotOwned(obj); +% % Variables for warmup stage cp_max = 5; n_trial_warmup = 20; @@ -737,27 +930,34 @@ a1_time_min = value(ParamsSection_A1_time_Min); a1_time_max = value(ParamsSection_A1_time_Max); -% -end -if stage_algorithm_eval -GetSoloFunctionArgs(obj); -ClearHelperVarsNotOwned(obj); -% -ParamsSection_training_stage.value = 8; +stage_no = value(SessionDefinition_CURRENT_ACTIVE_STAGE); +ParamsSection_training_stage.value = stage_no; callback(ParamsSection_training_stage); -stage8_trial_counter = stage8_trial_counter + 1; -SessionPerformanceSection_ntrials_stage.value = stage8_trial_counter; +% Update the helper vars +this_stage_trial_counter = value(stages_trial_counter); +this_stage_trial_counter(stage_no) = this_stage_trial_counter(stage_no) + 1; +SessionPerformanceSection_ntrials_stage.value = this_stage_trial_counter(stage_no); callback(SessionPerformanceSection_ntrials_stage); -stage8_trial_counter_today = stage8_trial_counter_today + 1; -SessionPerformanceSection_ntrials_stage_today.value = stage8_trial_counter_today; +stages_trial_counter.value = this_stage_trial_counter; + +this_stage_trial_counter_today = value(stages_trial_counter_today); +this_stage_trial_counter_today(stage_no) = this_stage_trial_counter_today(stage_no) + 1; +SessionPerformanceSection_ntrials_stage_today.value = this_stage_trial_counter_today(stage_no); callback(SessionPerformanceSection_ntrials_stage_today); -stage8_timeout_percent = ((stage8_timeout_percent * stage8_trial_counter) + double(timeout_history(end)))/(stage8_trial_counter + 1); -stage8_violation_percent = ((stage8_violation_percent * stage8_trial_counter) + double(violation_history(end)))/(stage8_trial_counter + 1); -SessionPerformanceSection_timeout_stage.value = stage8_timeout_percent; +stages_trial_counter_today.value = this_stage_trial_counter_today; + +this_stage_timeout_percent = value(stages_timeout_rate); +this_stage_timeout_percent(stage_no) = ((this_stage_timeout_percent(stage_no) * this_stage_trial_counter(stage_no)) + double(timeout_history(end)))/(this_stage_trial_counter(stage_no) + 1); +SessionPerformanceSection_timeout_stage.value = this_stage_timeout_percent(stage_no); callback(SessionPerformanceSection_timeout_stage); -SessionPerformanceSection_violation_stage.value = stage8_violation_percent; +stages_timeout_rate.value = this_stage_timeout_percent; + +this_stage_violation_percent = value(stages_violation_rate); +this_stage_violation_percent(stage_no) = ((this_stage_violation_percent(stage_no) * this_stage_trial_counter(stage_no)) + double(violation_history(end)))/(this_stage_trial_counter(stage_no) + 1); +SessionPerformanceSection_violation_stage.value = this_stage_violation_percent(stage_no); callback(SessionPerformanceSection_violation_stage); +stages_violation_rate.value = this_stage_violation_percent; % Warm Up If starting a new session if warm_up_on == 1 @@ -800,6 +1000,9 @@ callback(ParamsSection_CP_duration); % end + + + if completion_test_eval GetSoloFunctionArgs(obj); ClearHelperVarsNotOwned(obj); @@ -813,11 +1016,14 @@ varargout{1}=false; end end + + + if eod_logic_eval GetSoloFunctionArgs(obj); ClearHelperVarsNotOwned(obj); % -stage8_trial_counter_today = 0; +stages_trial_counter_today.value = zeros(1,8); % end % From 10e0095051ca3c0ddf53d1e3efbbf70e50565613 Mon Sep 17 00:00:00 2001 From: Arpit Date: Mon, 31 Mar 2025 10:54:48 +0100 Subject: [PATCH 039/164] added stimuli presentation in addition to fixed sound during CP. Created a new file for loading the automated training. --- .../Arpit_CentrePokeTraining.m | 17 +- .../Arpit_CentrePokeTrainingSMA.m | 31 +- ...essionDefinition_AutomatedTrainingStages.m | 1150 +++++++++++++++++ .../Arpit_CentrePoke_TrainingStages.m | 141 -- .../@Arpit_CentrePokeTraining/ParamsSection.m | 13 +- .../StimulusSection.m | 442 +++++++ ...okeTraining_experimenter_ratname_250319c.m | 381 +++--- 7 files changed, 1829 insertions(+), 346 deletions(-) create mode 100644 Protocols/@Arpit_CentrePokeTraining/Arpit_CentrePokeTraining_SessionDefinition_AutomatedTrainingStages.m delete mode 100644 Protocols/@Arpit_CentrePokeTraining/Arpit_CentrePoke_TrainingStages.m create mode 100644 Protocols/@Arpit_CentrePokeTraining/StimulusSection.m diff --git a/Protocols/@Arpit_CentrePokeTraining/Arpit_CentrePokeTraining.m b/Protocols/@Arpit_CentrePokeTraining/Arpit_CentrePokeTraining.m index 9a8b28fc..f6bdc08e 100644 --- a/Protocols/@Arpit_CentrePokeTraining/Arpit_CentrePokeTraining.m +++ b/Protocols/@Arpit_CentrePokeTraining/Arpit_CentrePokeTraining.m @@ -63,11 +63,10 @@ DeclareGlobals(obj, 'ro_args', {'timeout_history'}); SoloFunctionAddVars('ParamsSection', 'rw_args', 'timeout_history'); - % SoloParamHandle(obj, 'Stage_Trial_Counter', 'value', zeros(1,7)); - % DeclareGlobals(obj, 'ro_args', {'Stage_Trial_Counter'}); - % SoloFunctionAddVars('ParamsSection', 'rw_args', 'Stage_Trial_Counter'); - % - + SoloParamHandle(obj, 'stimulus_history', 'value', []); + DeclareGlobals(obj, 'ro_args', {'stimulus_history'}); + SoloFunctionAddVars('StimulusSection', 'rw_args', 'stimulus_history'); + SoundManagerSection(obj, 'init'); x = 5; y = 5; % Initial position on main GUI window @@ -121,6 +120,7 @@ [x, y] = SessionPerformanceSection(obj, 'init', x, y); [x, y] = ParamsSection(obj, 'init', x, y); %#ok [x, y] = SoundSection(obj,'init',x,y); + [x, y] = StimulusSection(obj,'init',x,y); [stage_fig_x,stage_fig_y] = Training_ParamsSection(obj, 'init', x, y); SoloParamHandle(obj, 'stage_fig_x', 'value', stage_fig_x); @@ -160,8 +160,7 @@ SessionDefinition(obj, 'next_trial'); SessionPerformanceSection(obj, 'evaluate'); SoundManagerSection(obj, 'send_not_yet_uploaded_sounds'); - - nTrials.value = n_done_trials; + StimulusSection(obj,'prepare_next_trial'); [sma, prepare_next_trial_states] = Arpit_CentrePokeTrainingSMA(obj, 'prepare_next_trial'); @@ -214,7 +213,8 @@ %% pre_saving_settings case 'pre_saving_settings' - + + StimulusSection(obj,'hide'); SessionDefinition(obj, 'run_eod_logic_without_saving'); perf = SessionPerformanceSection(obj, 'evaluate'); cp_durs = ParamsSection(obj, 'get_cp_history'); @@ -232,6 +232,7 @@ pd.timeouts=timeout_history(:); % pd.performance=tot_perf(:); pd.cp_durs=cp_durs(:); + pd.stim1dur=stim1dur(:); sendsummary(obj,'protocol_data',pd); diff --git a/Protocols/@Arpit_CentrePokeTraining/Arpit_CentrePokeTrainingSMA.m b/Protocols/@Arpit_CentrePokeTraining/Arpit_CentrePokeTrainingSMA.m index 01ffffbb..b752f707 100644 --- a/Protocols/@Arpit_CentrePokeTraining/Arpit_CentrePokeTrainingSMA.m +++ b/Protocols/@Arpit_CentrePokeTraining/Arpit_CentrePokeTrainingSMA.m @@ -60,6 +60,7 @@ %% Setup sounds sone_sound_id = SoundManagerSection(obj, 'get_sound_id', 'SOneSound'); stwo_sound_id = SoundManagerSection(obj, 'get_sound_id', 'STwoSound'); + A1_sound_id = SoundManagerSection(obj, 'get_sound_id', 'StimAUD1'); sound_duration = value(A1_time); % SoundManagerSection(obj, 'get_sound_duration', 'SOneSound'); go_sound_id = SoundManagerSection(obj, 'get_sound_id', 'GoSound'); go_cue_duration = value(time_go_cue); % SoundManagerSection(obj, 'get_sound_duration', 'GoSound'); @@ -74,32 +75,31 @@ if side == 'l' HitEvent = 'Lin'; ErrorEvent = 'Rin'; + sound_id = sone_sound_id; SideLight = left1led; + WValveTime = LeftWValveTime; + WValveSide = left1water; else HitEvent = 'Rin'; ErrorEvent = 'Lin'; + sound_id = stwo_sound_id; SideLight = right1led; + WValveTime = RightWValveTime; + WValveSide = right1water; end sma = StateMachineAssembler('full_trial_structure','use_happenings', 1); - if side=='l' - sma = add_scheduled_wave(sma, 'name', 'reward_delivery', 'preamble', reward_delay, ... - 'sustain', LeftWValveTime, 'DOut', left1water); % % activating the left side water port - + % scheduled wave for stimuli/fixed sound, could be based upon side + if stimuli_on sma = add_scheduled_wave(sma, 'name', 'stimplay', 'preamble', PreStim_time, ... - 'sustain', sound_duration, 'sound_trig', sone_sound_id); % to play a sound before Go Cue - + 'sustain', sound_duration, 'sound_trig', A1_sound_id); % to play a sound before Go Cue else - sma = add_scheduled_wave(sma, 'name', 'reward_delivery', 'preamble', reward_delay, ... - 'sustain', RightWValveTime, 'DOut', right1water); % activating the right side water port - sma = add_scheduled_wave(sma, 'name', 'stimplay', 'preamble', PreStim_time, ... - 'sustain', sound_duration, 'sound_trig', stwo_sound_id); % to play a sound before Go Cue - + 'sustain', sound_duration, 'sound_trig', sound_id); % to play a sound before Go Cue end - + % Scheduled wave for CP Duration if CP_duration <= (SettlingIn_time + legal_cbreak) sma = add_scheduled_wave(sma, 'name', 'CP_Duration_wave', 'preamble', CP_duration); % total length of centre poke to consider success else @@ -110,12 +110,15 @@ sma = add_scheduled_wave(sma, 'name', 'Go_Cue', 'preamble', 0.001, ... 'sustain', go_cue_duration, 'sound_trig', go_sound_id); % to play the Go Cue/Reward Sound + % scheduled wave for rewarded side either of the side + sma = add_scheduled_wave(sma, 'name', 'reward_delivery', 'preamble', reward_delay, ... + 'sustain', WValveTime, 'DOut', WValveSide); % water delivery side sma = add_scheduled_wave(sma, 'name', 'reward_collection_dur', 'preamble', SideLed_duration + RewardCollection_duration); % time to collect the reward - % For Training Stage 1 - switch value(training_stage) + % For Training Stage 1 + case 1 % LEARNING THE REWARD SOUND ASSOCIATION -LEFT OR RIGHT LED ON -> POKE -> SOUND+REWARD GIVEN % INFINITE TIME AND CHANCES TO SELF CORRECT diff --git a/Protocols/@Arpit_CentrePokeTraining/Arpit_CentrePokeTraining_SessionDefinition_AutomatedTrainingStages.m b/Protocols/@Arpit_CentrePokeTraining/Arpit_CentrePokeTraining_SessionDefinition_AutomatedTrainingStages.m new file mode 100644 index 00000000..61f5667f --- /dev/null +++ b/Protocols/@Arpit_CentrePokeTraining/Arpit_CentrePokeTraining_SessionDefinition_AutomatedTrainingStages.m @@ -0,0 +1,1150 @@ +%Training stage file. +%Please use the session automator window exclusively +%to edit this file. + +function varargout = Arpit_CentrePokeTraining_SessionDefinition_AutomatedTrainingStages(obj, action, varargin) + +GetSoloFunctionArgs('func_owner', ['@' class(obj)], 'func_name', 'SessionModel'); + +pairs = {'helper_vars_eval', true; + 'stage_algorithm_eval', true; + 'completion_test_eval', false; + 'eod_logic_eval', false}; +parseargs(varargin, pairs); + +switch action + + +%% Familiarize with Reward Side Pokes + +% +case 'Familiarize with Reward Side Pokes' + + +if helper_vars_eval +GetSoloFunctionArgs(obj); +ClearHelperVarsNotOwned(obj); +% +CreateHelperVar(obj,'stages_trial_counter','value',zeros(1,8)); +CreateHelperVar(obj,'stages_trial_counter_today','value',zeros(1,8)); +CreateHelperVar(obj,'stages_trial_counter_oppSide','value',zeros(1,8)); +CreateHelperVar(obj,'stages_timeout_rate','value',zeros(1,8)); +CreateHelperVar(obj,'stages_violation_rate','value',zeros(1,8)); +CreateHelperVar(obj,'stage_start_completed_trial','value',n_completed_trials,'forceinit',true); +% +end + + +if stage_algorithm_eval +GetSoloFunctionArgs(obj); +ClearHelperVarsNotOwned(obj); + + +% +ParamsSection_MaxSame.value = 4; +callback(ParamsSection_MaxSame); +stage_no = value(SessionDefinition_CURRENT_ACTIVE_STAGE); +ParamsSection_training_stage.value = stage_no; +callback(ParamsSection_training_stage); +if n_completed_trials > value(stage_start_completed_trial) + this_stage_trial_counter = value(stages_trial_counter); + this_stage_trial_counter(stage_no) = this_stage_trial_counter(stage_no) + 1; + SessionPerformanceSection_ntrials_stage.value = this_stage_trial_counter(stage_no); + callback(SessionPerformanceSection_ntrials_stage); + stages_trial_counter.value = this_stage_trial_counter; + this_stage_trial_counter_today = value(stages_trial_counter_today); + this_stage_trial_counter_today(stage_no) = this_stage_trial_counter_today(stage_no) + 1; + SessionPerformanceSection_ntrials_stage_today.value = this_stage_trial_counter_today(stage_no); + callback(SessionPerformanceSection_ntrials_stage_today); + stages_trial_counter_today.value = this_stage_trial_counter_today; + this_stage_trial_counter_oppSide = value(stages_trial_counter_oppSide); + if value(previous_sides(end)) ~= value(ParamsSection_ThisTrial) + this_stage_trial_counter_oppSide(stage_no) = this_stage_trial_counter_oppSide(stage_no) + 1; + end + stages_trial_counter_oppSide.value = this_stage_trial_counter_oppSide; +end +% +end + + +if completion_test_eval +GetSoloFunctionArgs(obj); +ClearHelperVarsNotOwned(obj); +clear('ans'); +% +stage_no = value(SessionDefinition_CURRENT_ACTIVE_STAGE); +this_stage_trial_counter_oppSide = value(stages_trial_counter_oppSide); +this_stage_trial_counter = value(stages_trial_counter); +% only run it if its the start of the day, number of trials is small +if n_completed_trials < 100 + if this_stage_trial_counter(stage_no) > value(Training_ParamsSection_total_trials) && this_stage_trial_counter_oppSide(stage_no) > value(Training_ParamsSection_total_trials_opp) + SessionDefinition(obj, 'jump_to_stage', 'Timeout Rewarded Side Pokes'); + end +end +% +if exist('ans', 'var') +varargout{1}=logical(ans); clear('ans'); +else +varargout{1}=false; +end +end + + +if eod_logic_eval +GetSoloFunctionArgs(obj); +ClearHelperVarsNotOwned(obj); +% +stage_no = value(SessionDefinition_CURRENT_ACTIVE_STAGE); +this_stage_trial_counter_oppSide = value(stages_trial_counter_oppSide); +this_stage_trial_counter = value(stages_trial_counter); +stages_trial_counter_today.value = zeros(1,8); +if this_stage_trial_counter(stage_no) > value(Training_ParamsSection_total_trials) && this_stage_trial_counter_oppSide(stage_no) > value(Training_ParamsSection_total_trials_opp) + SessionDefinition(obj, 'jump_to_stage', 'Timeout Rewarded Side Pokes'); +end +% +end + +% + + + + + +%% Timeout Rewarded Side Pokes + +% + +case 'Timeout Rewarded Side Pokes' + + +if helper_vars_eval +GetSoloFunctionArgs(obj); +ClearHelperVarsNotOwned(obj); +% +CreateHelperVar(obj,'stages_trial_counter','value',zeros(1,8)); +CreateHelperVar(obj,'stages_trial_counter_today','value',zeros(1,8)); +CreateHelperVar(obj,'stages_trial_counter_oppSide','value',zeros(1,8)); +CreateHelperVar(obj,'stages_timeout_rate','value',zeros(1,8)); +CreateHelperVar(obj,'stages_violation_rate','value',zeros(1,8)); +CreateHelperVar(obj,'this_stage_opp_side_trials', 'value', '0', 'forceinit','true'); +CreateHelperVar(obj,'stage_start_completed_trial','value',n_completed_trials,'forceinit',true); +% +end + + +if stage_algorithm_eval +GetSoloFunctionArgs(obj); +ClearHelperVarsNotOwned(obj); +% +% Change ParamsSection Vars +ParamsSection_MaxSame.value = 3; +callback(ParamsSection_MaxSame); +stage_no = value(SessionDefinition_CURRENT_ACTIVE_STAGE); +ParamsSection_training_stage.value = stage_no; +callback(ParamsSection_training_stage); +if n_completed_trials > value(stage_start_completed_trial) + % Update the helper vars + this_stage_trial_counter = value(stages_trial_counter); + this_stage_trial_counter(stage_no) = this_stage_trial_counter(stage_no) + 1; + SessionPerformanceSection_ntrials_stage.value = this_stage_trial_counter(stage_no); + callback(SessionPerformanceSection_ntrials_stage); + stages_trial_counter.value = this_stage_trial_counter; + + this_stage_trial_counter_today = value(stages_trial_counter_today); + this_stage_trial_counter_today(stage_no) = this_stage_trial_counter_today(stage_no) + 1; + SessionPerformanceSection_ntrials_stage_today.value = this_stage_trial_counter_today(stage_no); + callback(SessionPerformanceSection_ntrials_stage_today); + stages_trial_counter_today.value = this_stage_trial_counter_today; + + this_stage_trial_counter_oppSide = value(stages_trial_counter_oppSide); + if value(previous_sides(end)) ~= value(ParamsSection_ThisTrial) + this_stage_trial_counter_oppSide(stage_no) = this_stage_trial_counter_oppSide(stage_no) + 1; + this_stage_opp_side_trials.value = value(this_stage_opp_side_trials) + 1; + end + stages_trial_counter_oppSide.value = this_stage_trial_counter_oppSide; + + this_stage_timeout_percent = value(stages_timeout_rate); + this_stage_timeout_percent(stage_no) = ((this_stage_timeout_percent(stage_no) * this_stage_trial_counter(stage_no)) + double(timeout_history(end)))/(this_stage_trial_counter(stage_no) + 1); + SessionPerformanceSection_timeout_stage.value = this_stage_timeout_percent(stage_no); + callback(SessionPerformanceSection_timeout_stage); + stages_timeout_rate.value = this_stage_timeout_percent; +end +% Update the reward collection time based upon behav +if size(timeout_history) > 5 + if all(value(timeout_history(end-1:end))) && value(this_stage_opp_side_trials) >= 2 + ParamsSection_RewardCollection_duration.value = min([value(ParamsSection_RewardCollection_duration) + 1,... + value(Training_ParamsSection_max_rColl_dur)]); + callback(ParamsSection_RewardCollection_duration); + this_stage_opp_side_trials.value = 0; + end + if ~any(value(timeout_history(end-1:end))) && value(this_stage_opp_side_trials) >= 2 + ParamsSection_RewardCollection_duration.value = max([value(ParamsSection_RewardCollection_duration) - 1,... + value(Training_ParamsSection_min_rColl_dur)]); + callback(ParamsSection_RewardCollection_duration); + this_stage_opp_side_trials.value = 0; + end +end +if size(timeout_history) > 20 + if all(value(timeout_history(end-19:end))) + ParamsSection_RewardCollection_duration.value = 30; + callback(ParamsSection_RewardCollection_duration); + end +end + +% +end + + +if completion_test_eval +GetSoloFunctionArgs(obj); +ClearHelperVarsNotOwned(obj); +clear('ans'); +% +stage_no = value(SessionDefinition_CURRENT_ACTIVE_STAGE); +this_stage_trial_counter_oppSide = value(stages_trial_counter_oppSide); +this_stage_trial_counter = value(stages_trial_counter); +% only run it if its the start of the day, number of trials is small +if n_completed_trials > 50 + if this_stage_trial_counter(stage_no) > value(Training_ParamsSection_total_trials) && this_stage_trial_counter_oppSide(stage_no) > value(Training_ParamsSection_total_trials_opp) + SessionDefinition(obj, 'jump_to_stage', 'Introduce Centre Poke'); + end +end +% +if exist('ans', 'var') +varargout{1}=logical(ans); clear('ans'); +else +varargout{1}=false; +end +end + + +if eod_logic_eval +GetSoloFunctionArgs(obj); +ClearHelperVarsNotOwned(obj); +% +stages_trial_counter_today.value = zeros(1,8); +% +end +% + + + + +%% Introduce Centre Poke + +% +case 'Introduce Centre Poke' +if helper_vars_eval +GetSoloFunctionArgs(obj); +ClearHelperVarsNotOwned(obj); +% +CreateHelperVar(obj,'stages_trial_counter','value',zeros(1,8)); +CreateHelperVar(obj,'stages_trial_counter_today','value',zeros(1,8)); +CreateHelperVar(obj,'stages_trial_counter_oppSide','value',zeros(1,8)); +CreateHelperVar(obj,'stages_timeout_rate','value',zeros(1,8)); +CreateHelperVar(obj,'stages_violation_rate','value',zeros(1,8)); +CreateHelperVar(obj,'last_session_CP','value',0); +CreateHelperVar(obj,'stage_start_completed_trial','value',n_completed_trials,'forceinit',true); +% +end + + +if stage_algorithm_eval +GetSoloFunctionArgs(obj); +ClearHelperVarsNotOwned(obj); +% +% Maximum & Minimum duration of center poke, in secs: +cp_max = value(ParamsSection_SettlingIn_time) + value(ParamsSection_legal_cbreak); +cp_min = value(ParamsSection_init_CP_duration); +% Minimum increment (in secs) in center poke duration every time there is a non-cp-violation trial: +cp_minimum_increment = 0.001; +ParamsSection_MaxSame.value = Inf; +callback(ParamsSection_MaxSame); + +stage_no = value(SessionDefinition_CURRENT_ACTIVE_STAGE); +ParamsSection_training_stage.value = stage_no; +callback(ParamsSection_training_stage); +if n_completed_trials > value(stage_start_completed_trial) + % Update the helper vars + this_stage_trial_counter = value(stages_trial_counter); + this_stage_trial_counter(stage_no) = this_stage_trial_counter(stage_no) + 1; + SessionPerformanceSection_ntrials_stage.value = this_stage_trial_counter(stage_no); + callback(SessionPerformanceSection_ntrials_stage); + stages_trial_counter.value = this_stage_trial_counter; + + this_stage_trial_counter_today = value(stages_trial_counter_today); + this_stage_trial_counter_today(stage_no) = this_stage_trial_counter_today(stage_no) + 1; + SessionPerformanceSection_ntrials_stage_today.value = this_stage_trial_counter_today(stage_no); + callback(SessionPerformanceSection_ntrials_stage_today); + stages_trial_counter_today.value = this_stage_trial_counter_today; + + this_stage_timeout_percent = value(stages_timeout_rate); + this_stage_timeout_percent(stage_no) = ((this_stage_timeout_percent(stage_no) * this_stage_trial_counter(stage_no)) + double(timeout_history(end)))/(this_stage_trial_counter(stage_no) + 1); + SessionPerformanceSection_timeout_stage.value = this_stage_timeout_percent(stage_no); + callback(SessionPerformanceSection_timeout_stage); + stages_timeout_rate.value = this_stage_timeout_percent; +end +% Change the value of CP Duration +if n_completed_trials < 1 + ParamsSection_CP_duration.value = value(ParamsSection_init_CP_duration); +else + if ~timeout_history(end) && value(ParamsSection_CP_duration) < cp_max + increment = value(ParamsSection_CP_duration) * value(Training_ParamsSection_CPfraction_inc); + if increment < cp_minimum_increment + increment = cp_minimum_increment; + end + ParamsSection_CP_duration.value = value(ParamsSection_CP_duration) + increment; + end +end +callback(ParamsSection_CP_duration); +% +end + + +if completion_test_eval +GetSoloFunctionArgs(obj); +ClearHelperVarsNotOwned(obj); +clear('ans'); +% +cp_max = value(ParamsSection_SettlingIn_time) + value(ParamsSection_legal_cbreak); +if value(ParamsSection_CP_duration) >= cp_max + SessionDefinition(obj, 'jump_to_stage', 'Introduce Violation for Centre Poke'); +end +% +if exist('ans', 'var') +varargout{1}=logical(ans); clear('ans'); +else +varargout{1}=false; +end +end + + + +if eod_logic_eval +GetSoloFunctionArgs(obj); +ClearHelperVarsNotOwned(obj); +% +stages_trial_counter_today.value = zeros(1,8); +last_session_CP.value = value(ParamsSection_CP_duration); +% +end +% + + +%% Introduce Violation for Centre Poke + +% +case 'Introduce Violation for Centre Poke' +if helper_vars_eval +GetSoloFunctionArgs(obj); +ClearHelperVarsNotOwned(obj); +% +CreateHelperVar(obj,'stages_trial_counter','value',zeros(1,8)); +CreateHelperVar(obj,'stages_trial_counter_today','value',zeros(1,8)); +CreateHelperVar(obj,'stages_trial_counter_oppSide','value',zeros(1,8)); +CreateHelperVar(obj,'stages_timeout_rate','value',zeros(1,8)); +CreateHelperVar(obj,'stages_violation_rate','value',zeros(1,8)); +CreateHelperVar(obj,'last_session_CP','value',0); +CreateHelperVar(obj,'stage_start_completed_trial','value',n_completed_trials,'forceinit',true); +% +end + + + +if stage_algorithm_eval +GetSoloFunctionArgs(obj); +ClearHelperVarsNotOwned(obj); +% + +% Maximum & Minimum duration of center poke, in secs: +cp_min = value(ParamsSection_SettlingIn_time) + value(ParamsSection_legal_cbreak); +cp_max = value(Training_ParamsSection_max_CP); +% Fractional increment in center poke duration every time there is a non-cp-violation trial: +cp_fraction = value(Training_ParamsSection_CPfraction_inc); +% Minimum increment (in secs) in center poke duration every time there is a non-cp-violation trial: +cp_minimum_increment = 0.001; + +stage_no = value(SessionDefinition_CURRENT_ACTIVE_STAGE); +ParamsSection_training_stage.value = stage_no; +callback(ParamsSection_training_stage); +if n_completed_trials > value(stage_start_completed_trial) + % Update the helper vars + this_stage_trial_counter = value(stages_trial_counter); + this_stage_trial_counter(stage_no) = this_stage_trial_counter(stage_no) + 1; + SessionPerformanceSection_ntrials_stage.value = this_stage_trial_counter(stage_no); + callback(SessionPerformanceSection_ntrials_stage); + stages_trial_counter.value = this_stage_trial_counter; + + this_stage_trial_counter_today = value(stages_trial_counter_today); + this_stage_trial_counter_today(stage_no) = this_stage_trial_counter_today(stage_no) + 1; + SessionPerformanceSection_ntrials_stage_today.value = this_stage_trial_counter_today(stage_no); + callback(SessionPerformanceSection_ntrials_stage_today); + stages_trial_counter_today.value = this_stage_trial_counter_today; + + this_stage_timeout_percent = value(stages_timeout_rate); + this_stage_timeout_percent(stage_no) = ((this_stage_timeout_percent(stage_no) * this_stage_trial_counter(stage_no)) + double(timeout_history(end)))/(this_stage_trial_counter(stage_no) + 1); + SessionPerformanceSection_timeout_stage.value = this_stage_timeout_percent(stage_no); + callback(SessionPerformanceSection_timeout_stage); + stages_timeout_rate.value = this_stage_timeout_percent; + + this_stage_violation_percent = value(stages_violation_rate); + this_stage_violation_percent(stage_no) = ((this_stage_violation_percent(stage_no) * this_stage_trial_counter(stage_no)) + double(violation_history(end)))/(this_stage_trial_counter(stage_no) + 1); + SessionPerformanceSection_violation_stage.value = this_stage_violation_percent(stage_no); + callback(SessionPerformanceSection_violation_stage); + stages_violation_rate.value = this_stage_violation_percent; +end +% Change the value of CP Duration +if n_completed_trials < 1 + ParamsSection_CP_duration.value = value(ParamsSection_init_CP_duration); +else + if ~violation_history(end) && ~timeout_history(end) && value(ParamsSection_CP_duration) < cp_max + increment = value(ParamsSection_CP_duration) * cp_fraction; + if increment < cp_minimum_increment + increment = cp_minimum_increment; + end + ParamsSection_CP_duration.value = value(ParamsSection_CP_duration) + increment; + end +end +if value(ParamsSection_CP_duration) > cp_max + ParamsSection_CP_duration.value = cp_max; +end + +callback(ParamsSection_CP_duration); +% +end + + +if completion_test_eval +GetSoloFunctionArgs(obj); +ClearHelperVarsNotOwned(obj); +clear('ans'); +% +cp_max = value(Training_ParamsSection_max_CP); +if value(ParamsSection_CP_duration) >= cp_max && n_completed_trials > 100 +if SessionPerformanceSection_violation_recent < value(Training_ParamsSection_recent_violation) && SessionPerformanceSection_timeout_recent < value(Training_ParamsSection_recent_timeout) && ... + SessionPerformanceSection_violation_stage < value(Training_ParamsSection_stage_violation) + SessionDefinition(obj, 'jump_to_stage', 'Introduce Stimuli Sound during Centre Poke'); + last_session_CP.value = value(ParamsSection_CP_duration); +end +end +% +if exist('ans', 'var') +varargout{1}=logical(ans); clear('ans'); +else +varargout{1}=false; +end +end + + +if eod_logic_eval +GetSoloFunctionArgs(obj); +ClearHelperVarsNotOwned(obj); +% +% Store the value of the total cp duration reached: +stages_trial_counter_today.value = zeros(1,8); +last_session_CP.value = value(ParamsSection_CP_duration); +% +end +% + + + + + +%% Introduce Stimuli Sound during Centre Poke + +% +case 'Introduce Stimuli Sound during Centre Poke' +if helper_vars_eval +GetSoloFunctionArgs(obj); +ClearHelperVarsNotOwned(obj); +% +CreateHelperVar(obj,'stages_trial_counter','value',zeros(1,8)); +CreateHelperVar(obj,'stages_trial_counter_today','value',zeros(1,8)); +CreateHelperVar(obj,'stages_trial_counter_oppSide','value',zeros(1,8)); +CreateHelperVar(obj,'stages_timeout_rate','value',zeros(1,8)); +CreateHelperVar(obj,'stages_violation_rate','value',zeros(1,8)); +CreateHelperVar(obj,'last_session_CP','value',0); +CreateHelperVar(obj,'stage_start_completed_trial','value',n_completed_trials,'forceinit',true); +% +end + + +if stage_algorithm_eval +GetSoloFunctionArgs(obj); +ClearHelperVarsNotOwned(obj); +% +cp_max = value(Training_ParamsSection_max_CP); +cp_min = value(Training_ParamsSection_min_CP); +% Fractional increment in center poke duration every time there is a non-cp-violation trial: +cp_fraction = value(Training_ParamsSection_CPfraction_inc); +% Minimum increment (in secs) in center poke duration every time there is a non-cp-violation trial: +cp_minimum_increment = 0.001; +% Starting total center poke duration: +starting_cp = value(Training_ParamsSection_starting_CP) + value(ParamsSection_SettlingIn_time); +% number of warm-up trials +n_trial_warmup = value(Training_ParamsSection_warm_up_trials); + +stage_no = value(SessionDefinition_CURRENT_ACTIVE_STAGE); +ParamsSection_training_stage.value = stage_no; +callback(ParamsSection_training_stage); +if n_completed_trials > value(stage_start_completed_trial) + % Update the helper vars + this_stage_trial_counter = value(stages_trial_counter); + this_stage_trial_counter(stage_no) = this_stage_trial_counter(stage_no) + 1; + SessionPerformanceSection_ntrials_stage.value = this_stage_trial_counter(stage_no); + callback(SessionPerformanceSection_ntrials_stage); + stages_trial_counter.value = this_stage_trial_counter; + + this_stage_trial_counter_today = value(stages_trial_counter_today); + this_stage_trial_counter_today(stage_no) = this_stage_trial_counter_today(stage_no) + 1; + SessionPerformanceSection_ntrials_stage_today.value = this_stage_trial_counter_today(stage_no); + callback(SessionPerformanceSection_ntrials_stage_today); + stages_trial_counter_today.value = this_stage_trial_counter_today; + + this_stage_timeout_percent = value(stages_timeout_rate); + this_stage_timeout_percent(stage_no) = ((this_stage_timeout_percent(stage_no) * this_stage_trial_counter(stage_no)) + double(timeout_history(end)))/(this_stage_trial_counter(stage_no) + 1); + SessionPerformanceSection_timeout_stage.value = this_stage_timeout_percent(stage_no); + callback(SessionPerformanceSection_timeout_stage); + stages_timeout_rate.value = this_stage_timeout_percent; + + this_stage_violation_percent = value(stages_violation_rate); + this_stage_violation_percent(stage_no) = ((this_stage_violation_percent(stage_no) * this_stage_trial_counter(stage_no)) + double(violation_history(end)))/(this_stage_trial_counter(stage_no) + 1); + SessionPerformanceSection_violation_stage.value = this_stage_violation_percent(stage_no); + callback(SessionPerformanceSection_violation_stage); + stages_violation_rate.value = this_stage_violation_percent; +end +% Change the value of CP Duration + +% Since starting a new session then do a pre warm up to last saved cp +% duration else continue with learning with increased poke time +if n_completed_trials == 0 + ParamsSection_CP_duration.value = value(ParamsSection_init_CP_duration); +elseif n_completed_trials == 1 + ParamsSection_CP_duration.value = starting_cp; +else + if ~violation_history(end) && ~timeout_history(end) + if value(ParamsSection_CP_duration) < max([cp_min,value(last_session_CP)]) % warm up stage + increment = (max([cp_min,value(last_session_CP)]) - value(ParamsSection_CP_duration))/ (n_trial_warmup - 1); + else + if value(ParamsSection_CP_duration) >= value(last_session_CP) && value(ParamsSection_CP_duration) <= cp_max % no warm up stage + increment = value(ParamsSection_CP_duration)*cp_fraction; + if increment < cp_minimum_increment + increment = cp_minimum_increment; + end + end + end + + ParamsSection_CP_duration.value = value(ParamsSection_CP_duration) + increment; + + % Check if the values are within the required range + if value(ParamsSection_CP_duration) < starting_cp + ParamsSection_CP_duration.value = starting_cp; + end + if value(ParamsSection_CP_duration) > cp_max + ParamsSection_CP_duration.value = cp_max; + end + + end +end +callback(ParamsSection_CP_duration); + +if value(ParamsSection_CP_duration) >= 1 + ParamsSection_PreStim_time.value = 0.4; + if value(ParamsSection_CP_duration) < 2 + ParamsSection_A1_time.value = 0.1; + elseif value(ParamsSection_CP_duration) < 2.5 && value(ParamsSection_CP_duration) >= 2 + ParamsSection_A1_time.value = 0.2; + elseif value(ParamsSection_CP_duration) < 3 && value(ParamsSection_CP_duration) >= 2.5 + ParamsSection_A1_time.value = 0.3; + else + ParamsSection_A1_time.value = 0.4; + end +elseif value(ParamsSection_CP_duration) < 1 && value(ParamsSection_CP_duration) >= starting_cp + ParamsSection_SettlingIn_time.value = 0.2; + callback(ParamsSection_SettlingIn_time); + ParamsSection_PreStim_time.value = 0.1; + ParamsSection_A1_time.value = 0.1; +end + +if value(ParamsSection_CP_duration) >= starting_cp + ParamsSection_time_bet_aud1_gocue.value = value(ParamsSection_CP_duration) - value(ParamsSection_SettlingIn_time) - value(ParamsSection_A1_time) - value(ParamsSection_PreStim_time); + callback(ParamsSection_time_bet_aud1_gocue) + callback(ParamsSection_PreStim_time); + callback(ParamsSection_A1_time); +end +% +end + + + +if completion_test_eval +GetSoloFunctionArgs(obj); +ClearHelperVarsNotOwned(obj); +clear('ans'); +% +stage_no = value(SessionDefinition_CURRENT_ACTIVE_STAGE); +this_stage_trial_counter = value(stages_trial_counter); +if value(ParamsSection_CP_duration) >= value(Training_ParamsSection_max_CP) && this_stage_trial_counter(stage_no) > value(Training_ParamsSection_total_trials) + if SessionPerformanceSection_violation_recent < value(Training_ParamsSection_recent_violation) && SessionPerformanceSection_timeout_recent < value(Training_ParamsSection_recent_timeout) && ... + SessionPerformanceSection_violation_stage < value(Training_ParamsSection_stage_violation) && n_completed_trials > 100 + SessionDefinition(obj, 'jump_to_stage', 'Vary Stimuli location during Centre Poke'); + last_session_CP.value = value(ParamsSection_CP_duration); + end +end +% +if exist('ans', 'var') +varargout{1}=logical(ans); clear('ans'); +else +varargout{1}=false; +end +end + + + +if eod_logic_eval +GetSoloFunctionArgs(obj); +ClearHelperVarsNotOwned(obj); +% +stages_trial_counter_today.value = zeros(1,8); +last_session_CP.value = value(ParamsSection_CP_duration); +% +end + + + +% + +%% Vary Stimuli location during Centre Poke + +% +case 'Vary Stimuli location during Centre Poke' +if helper_vars_eval +GetSoloFunctionArgs(obj); +ClearHelperVarsNotOwned(obj); +% +CreateHelperVar(obj,'stages_trial_counter','value',zeros(1,8)); +CreateHelperVar(obj,'stages_trial_counter_today','value',zeros(1,8)); +CreateHelperVar(obj,'stages_trial_counter_oppSide','value',zeros(1,8)); +CreateHelperVar(obj,'stages_timeout_rate','value',zeros(1,8)); +CreateHelperVar(obj,'stages_violation_rate','value',zeros(1,8)); +CreateHelperVar(obj,'last_session_CP','value',0); +CreateHelperVar(obj,'stage_start_completed_trial','value',n_completed_trials,'forceinit',true); +% +end + + + +if stage_algorithm_eval +GetSoloFunctionArgs(obj); +ClearHelperVarsNotOwned(obj); +% +cp_max = value(Training_ParamsSection_max_CP); +% Starting total center poke duration: +starting_cp = value(Training_ParamsSection_starting_CP) + value(ParamsSection_SettlingIn_time); +% number of warm-up trials +n_trial_warmup = value(Training_ParamsSection_warm_up_trials); +prestim_min = value(Training_ParamsSection_min_prestim); +prestim_max = value(Training_ParamsSection_max_prestim); +stim_dur = value(Training_ParamsSection_stim_dur); + +stage_no = value(SessionDefinition_CURRENT_ACTIVE_STAGE); +ParamsSection_training_stage.value = stage_no; +callback(ParamsSection_training_stage); +if n_completed_trials > value(stage_start_completed_trial) + % Update the helper vars + this_stage_trial_counter = value(stages_trial_counter); + this_stage_trial_counter(stage_no) = this_stage_trial_counter(stage_no) + 1; + SessionPerformanceSection_ntrials_stage.value = this_stage_trial_counter(stage_no); + callback(SessionPerformanceSection_ntrials_stage); + stages_trial_counter.value = this_stage_trial_counter; + + this_stage_trial_counter_today = value(stages_trial_counter_today); + this_stage_trial_counter_today(stage_no) = this_stage_trial_counter_today(stage_no) + 1; + SessionPerformanceSection_ntrials_stage_today.value = this_stage_trial_counter_today(stage_no); + callback(SessionPerformanceSection_ntrials_stage_today); + stages_trial_counter_today.value = this_stage_trial_counter_today; + + this_stage_timeout_percent = value(stages_timeout_rate); + this_stage_timeout_percent(stage_no) = ((this_stage_timeout_percent(stage_no) * this_stage_trial_counter(stage_no)) + double(timeout_history(end)))/(this_stage_trial_counter(stage_no) + 1); + SessionPerformanceSection_timeout_stage.value = this_stage_timeout_percent(stage_no); + callback(SessionPerformanceSection_timeout_stage); + stages_timeout_rate.value = this_stage_timeout_percent; + + this_stage_violation_percent = value(stages_violation_rate); + this_stage_violation_percent(stage_no) = ((this_stage_violation_percent(stage_no) * this_stage_trial_counter(stage_no)) + double(violation_history(end)))/(this_stage_trial_counter(stage_no) + 1); + SessionPerformanceSection_violation_stage.value = this_stage_violation_percent(stage_no); + callback(SessionPerformanceSection_violation_stage); + stages_violation_rate.value = this_stage_violation_percent; +end +% Warm Up If starting a new session +if n_completed_trials == 0 + ParamsSection_CP_duration.value = value(ParamsSection_init_CP_duration); +elseif n_completed_trials == 1 + ParamsSection_CP_duration.value = starting_cp; +else + if value(ParamsSection_CP_duration) < cp_max % warm up stage + if ~violation_history(end) && ~timeout_history(end) + increment = (cp_max - value(ParamsSection_CP_duration))/ (n_trial_warmup - 1); + ParamsSection_CP_duration.value = value(ParamsSection_CP_duration) + increment; + % Check if the values are within the required range + if value(ParamsSection_CP_duration) < starting_cp + ParamsSection_CP_duration.value = starting_cp; + end + if value(ParamsSection_CP_duration) > cp_max + ParamsSection_CP_duration.value = cp_max; + end + end + end +end + +callback(ParamsSection_CP_duration); + +if value(ParamsSection_CP_duration) < 3 && value(ParamsSection_CP_duration) >= starting_cp % during the warm up phase + ParamsSection_SettlingIn_time.value = 0.2; + callback(ParamsSection_SettlingIn_time); + ParamsSection_PreStim_time.value = 0.1; + ParamsSection_A1_time.value = 0.1; +else + ParamsSection_A1_time.value = stim_dur; % actual training stage + time_range_PreStim_time = prestim_min : 0.01 : prestim_max; + ParamsSection_PreStim_time.value = time_range_PreStim_time(randi([1, numel(time_range_PreStim_time)],1,1)); +end + +if value(ParamsSection_CP_duration) >= starting_cp + ParamsSection_time_bet_aud1_gocue.value = value(ParamsSection_CP_duration) - value(ParamsSection_SettlingIn_time) - value(ParamsSection_A1_time) - value(ParamsSection_PreStim_time); + callback(ParamsSection_time_bet_aud1_gocue) + callback(ParamsSection_PreStim_time); + callback(ParamsSection_A1_time); +end + +% +end +if completion_test_eval +GetSoloFunctionArgs(obj); +ClearHelperVarsNotOwned(obj); +clear('ans'); +% +stage_no = value(SessionDefinition_CURRENT_ACTIVE_STAGE); +this_stage_trial_counter = value(stages_trial_counter); +if this_stage_trial_counter(stage_no) > value(Training_ParamsSection_total_trials) + if SessionPerformanceSection_violation_recent < value(Training_ParamsSection_recent_violation) && SessionPerformanceSection_timeout_recent < value(Training_ParamsSection_recent_timeout) && ... + SessionPerformanceSection_violation_stage < value(Training_ParamsSection_stage_violation) && n_completed_trials > 100 + SessionDefinition(obj, 'jump_to_stage', 'Variable Stimuli Go Cue location during Centre Poke'); + end +end +% +if exist('ans', 'var') +varargout{1}=logical(ans); clear('ans'); +else +varargout{1}=false; +end +end + + +if eod_logic_eval +GetSoloFunctionArgs(obj); +ClearHelperVarsNotOwned(obj); +% +stages_trial_counter_today.value = zeros(1,8); +% +end +% + + + + +%% Variable Stimuli Go Cue location during Centre Poke + +% +case 'Variable Stimuli Go Cue location during Centre Poke' +if helper_vars_eval +GetSoloFunctionArgs(obj); +ClearHelperVarsNotOwned(obj); +% +CreateHelperVar(obj,'stages_trial_counter','value',zeros(1,8)); +CreateHelperVar(obj,'stages_trial_counter_today','value',zeros(1,8)); +CreateHelperVar(obj,'stages_trial_counter_oppSide','value',zeros(1,8)); +CreateHelperVar(obj,'stages_timeout_rate','value',zeros(1,8)); +CreateHelperVar(obj,'stages_violation_rate','value',zeros(1,8)); +CreateHelperVar(obj,'last_session_CP','value',0); +CreateHelperVar(obj,'stage_start_completed_trial','value',n_completed_trials,'forceinit',true); +% +end + + +if stage_algorithm_eval +GetSoloFunctionArgs(obj); +ClearHelperVarsNotOwned(obj); +% +% Variables for warmup stage +cp_max = value(Training_ParamsSection_max_CP); +% Starting total center poke duration: +starting_cp = value(Training_ParamsSection_starting_CP) + value(ParamsSection_SettlingIn_time); +% number of warm-up trials +n_trial_warmup = value(Training_ParamsSection_warm_up_trials); +prestim_min = value(Training_ParamsSection_min_prestim); +prestim_max = value(Training_ParamsSection_max_prestim); +prestim_time = value(Training_ParamsSection_min_prestim); +a1_time = value(Training_ParamsSection_stim_dur); +a1_time_min = value(Training_ParamsSection_stim_dur); +a1_time_max = value(Training_ParamsSection_stim_dur + 0.1); +prego_min = value(Training_ParamsSection_min_prego); +prego_max = value(Training_ParamsSection_max_prego); +prego_time = value(Training_ParamsSection_min_prego); +warmup_completed = 0; +warm_up_on = 1; +random_prestim = 1; +random_prego = 1; +random_A1 = 0; + +stage_no = value(SessionDefinition_CURRENT_ACTIVE_STAGE); +ParamsSection_training_stage.value = stage_no; +callback(ParamsSection_training_stage); +if n_completed_trials > value(stage_start_completed_trial) + % Update the helper vars + this_stage_trial_counter = value(stages_trial_counter); + this_stage_trial_counter(stage_no) = this_stage_trial_counter(stage_no) + 1; + SessionPerformanceSection_ntrials_stage.value = this_stage_trial_counter(stage_no); + callback(SessionPerformanceSection_ntrials_stage); + stages_trial_counter.value = this_stage_trial_counter; + + this_stage_trial_counter_today = value(stages_trial_counter_today); + this_stage_trial_counter_today(stage_no) = this_stage_trial_counter_today(stage_no) + 1; + SessionPerformanceSection_ntrials_stage_today.value = this_stage_trial_counter_today(stage_no); + callback(SessionPerformanceSection_ntrials_stage_today); + stages_trial_counter_today.value = this_stage_trial_counter_today; + + this_stage_timeout_percent = value(stages_timeout_rate); + this_stage_timeout_percent(stage_no) = ((this_stage_timeout_percent(stage_no) * this_stage_trial_counter(stage_no)) + double(timeout_history(end)))/(this_stage_trial_counter(stage_no) + 1); + SessionPerformanceSection_timeout_stage.value = this_stage_timeout_percent(stage_no); + callback(SessionPerformanceSection_timeout_stage); + stages_timeout_rate.value = this_stage_timeout_percent; + + this_stage_violation_percent = value(stages_violation_rate); + this_stage_violation_percent(stage_no) = ((this_stage_violation_percent(stage_no) * this_stage_trial_counter(stage_no)) + double(violation_history(end)))/(this_stage_trial_counter(stage_no) + 1); + SessionPerformanceSection_violation_stage.value = this_stage_violation_percent(stage_no); + callback(SessionPerformanceSection_violation_stage); + stages_violation_rate.value = this_stage_violation_percent; +end +% Warm Up If starting a new session +if warm_up_on == 1 + if n_completed_trials == 0 + ParamsSection_CP_duration.value = value(ParamsSection_init_CP_duration); + warmup_completed = 0; + elseif n_completed_trials == 1 + ParamsSection_CP_duration.value = starting_cp; + else + if value(ParamsSection_CP_duration) <= cp_max % warm up stage + if ~violation_history(end) && ~timeout_history(end) + increment = (cp_max - value(ParamsSection_CP_duration))/ (n_trial_warmup - 1); + ParamsSection_CP_duration.value = value(ParamsSection_CP_duration) + increment; + % Check if the values are within the required range + if value(ParamsSection_CP_duration) < starting_cp + ParamsSection_CP_duration.value = starting_cp; + end + if value(ParamsSection_CP_duration) >= cp_max + ParamsSection_CP_duration.value = cp_max; + warmup_completed = 1; + end + end + end + end +else + warmup_completed = 1; +end + +if n_completed_trials >= 1 + cp_length = value(ParamsSection_CP_duration) - value(ParamsSection_SettlingIn_time); + [ParamsSection_PreStim_time.value ,ParamsSection_A1_time.value,ParamsSection_time_bet_aud1_gocue.value] = param_time_within_range(warmup_completed,... + cp_length,prestim_min,prestim_max, random_prestim, prestim_time,... + a1_time_min,a1_time_max, random_A1, a1_time,prego_min,prego_max, random_prego, prego_time); + + callback(ParamsSection_PreStim_time); + callback(ParamsSection_A1_time); + callback(ParamsSection_time_bet_aud1_gocue); + ParamsSection_CP_duration = value(ParamsSection_SettlingIn_time) + value(ParamsSection_PreStim_time) + value(ParamsSection_A1_time) + value(ParamsSection_time_bet_aud1_gocue); +end +callback(ParamsSection_CP_duration); +% +end + + + +if completion_test_eval +GetSoloFunctionArgs(obj); +ClearHelperVarsNotOwned(obj); +clear('ans'); +% +stage_no = value(SessionDefinition_CURRENT_ACTIVE_STAGE); +this_stage_trial_counter = value(stages_trial_counter); +if this_stage_trial_counter(stage_no) > value(Training_ParamsSection_total_trials) + if SessionPerformanceSection_violation_recent < value(Training_ParamsSection_recent_violation) && SessionPerformanceSection_timeout_recent < value(Training_ParamsSection_recent_timeout) && ... + SessionPerformanceSection_violation_stage < value(Training_ParamsSection_stage_violation) + SessionDefinition(obj, 'jump_to_stage', 'User Setting'); + end +end +% +if exist('ans', 'var') +varargout{1}=logical(ans); clear('ans'); +else +varargout{1}=false; +end +end + + + +if eod_logic_eval +GetSoloFunctionArgs(obj); +ClearHelperVarsNotOwned(obj); +% +stages_trial_counter_today.value = zeros(1,8); +% +end +% + +%% User Setting + +% + case 'User Setting' + + +if helper_vars_eval +GetSoloFunctionArgs(obj); +ClearHelperVarsNotOwned(obj); +% +CreateHelperVar(obj,'stages_trial_counter','value',zeros(1,8)); +CreateHelperVar(obj,'stages_trial_counter_today','value',zeros(1,8)); +CreateHelperVar(obj,'stages_trial_counter_oppSide','value',zeros(1,8)); +CreateHelperVar(obj,'stages_timeout_rate','value',zeros(1,8)); +CreateHelperVar(obj,'stages_violation_rate','value',zeros(1,8)); +CreateHelperVar(obj,'last_session_CP','value',0); +CreateHelperVar(obj,'stage_start_completed_trial','value',n_completed_trials,'forceinit',true); +% +end + + + +if stage_algorithm_eval +GetSoloFunctionArgs(obj); +ClearHelperVarsNotOwned(obj); +% +% Variables for warmup stage +cp_max = 5; +n_trial_warmup = 20; +starting_cp = 0.5; +warmup_completed = 0; +warm_up_on = value(ParamsSection_warmup_on); +random_prestim = value(ParamsSection_random_PreStim_time); +random_prego = value(ParamsSection_random_prego_time); +random_A1 = value(ParamsSection_random_A1_time); +prestim_min = value(ParamsSection_PreStim_time_Min); +prestim_max = value(ParamsSection_PreStim_time_Max); +prestim_time = value(ParamsSection_PreStim_time); +prego_min = value(ParamsSection_time_bet_aud1_gocue_Min); +prego_max = value(ParamsSection_time_bet_aud1_gocue_Max); +prego_time = value(ParamsSection_time_bet_aud1_gocue); +a1_time = value(ParamsSection_A1_time); +a1_time_min = value(ParamsSection_A1_time_Min); +a1_time_max = value(ParamsSection_A1_time_Max); + +stage_no = value(SessionDefinition_CURRENT_ACTIVE_STAGE); +ParamsSection_training_stage.value = stage_no; +callback(ParamsSection_training_stage); + +if n_completed_trials > value(stage_start_completed_trial) + % Update the helper vars + this_stage_trial_counter = value(stages_trial_counter); + this_stage_trial_counter(stage_no) = this_stage_trial_counter(stage_no) + 1; + SessionPerformanceSection_ntrials_stage.value = this_stage_trial_counter(stage_no); + callback(SessionPerformanceSection_ntrials_stage); + stages_trial_counter.value = this_stage_trial_counter; + + this_stage_trial_counter_today = value(stages_trial_counter_today); + this_stage_trial_counter_today(stage_no) = this_stage_trial_counter_today(stage_no) + 1; + SessionPerformanceSection_ntrials_stage_today.value = this_stage_trial_counter_today(stage_no); + callback(SessionPerformanceSection_ntrials_stage_today); + stages_trial_counter_today.value = this_stage_trial_counter_today; + + this_stage_timeout_percent = value(stages_timeout_rate); + this_stage_timeout_percent(stage_no) = ((this_stage_timeout_percent(stage_no) * this_stage_trial_counter(stage_no)) + double(timeout_history(end)))/(this_stage_trial_counter(stage_no) + 1); + SessionPerformanceSection_timeout_stage.value = this_stage_timeout_percent(stage_no); + callback(SessionPerformanceSection_timeout_stage); + stages_timeout_rate.value = this_stage_timeout_percent; + + this_stage_violation_percent = value(stages_violation_rate); + this_stage_violation_percent(stage_no) = ((this_stage_violation_percent(stage_no) * this_stage_trial_counter(stage_no)) + double(violation_history(end)))/(this_stage_trial_counter(stage_no) + 1); + SessionPerformanceSection_violation_stage.value = this_stage_violation_percent(stage_no); + callback(SessionPerformanceSection_violation_stage); + stages_violation_rate.value = this_stage_violation_percent; +end +% Warm Up If starting a new session +if warm_up_on == 1 + if n_completed_trials == 0 + ParamsSection_CP_duration.value = value(ParamsSection_init_CP_duration); + warmup_completed = 0; + elseif n_completed_trials == 1 + ParamsSection_CP_duration.value = starting_cp; + else + if value(ParamsSection_CP_duration) <= cp_max % warm up stage + if ~violation_history(end) && ~timeout_history(end) + increment = (cp_max - value(ParamsSection_CP_duration))/ (n_trial_warmup - 1); + ParamsSection_CP_duration.value = value(ParamsSection_CP_duration) + increment; + % Check if the values are within the required range + if value(ParamsSection_CP_duration) < starting_cp + ParamsSection_CP_duration.value = starting_cp; + end + if value(ParamsSection_CP_duration) >= cp_max + ParamsSection_CP_duration.value = cp_max; + warmup_completed = 1; + end + end + end + end +else + warmup_completed = 1; +end + +if n_completed_trials >= 1 + cp_length = value(ParamsSection_CP_duration) - value(ParamsSection_SettlingIn_time); + [ParamsSection_PreStim_time.value ,ParamsSection_A1_time.value,ParamsSection_time_bet_aud1_gocue.value] = param_time_within_range(warmup_completed,... + cp_length,prestim_min,prestim_max, random_prestim, prestim_time,... + a1_time_min,a1_time_max, random_A1, a1_time,prego_min,prego_max, random_prego, prego_time); + + callback(ParamsSection_PreStim_time); + callback(ParamsSection_A1_time); + callback(ParamsSection_time_bet_aud1_gocue); + ParamsSection_CP_duration = value(ParamsSection_SettlingIn_time) + value(ParamsSection_PreStim_time) + value(ParamsSection_A1_time) + value(ParamsSection_time_bet_aud1_gocue); +end +callback(ParamsSection_CP_duration); +% +end + + + +if completion_test_eval +GetSoloFunctionArgs(obj); +ClearHelperVarsNotOwned(obj); +clear('ans'); +% + +% +if exist('ans', 'var') +varargout{1}=logical(ans); clear('ans'); +else +varargout{1}=false; +end +end + + + +if eod_logic_eval +GetSoloFunctionArgs(obj); +ClearHelperVarsNotOwned(obj); +% +stages_trial_counter_today.value = zeros(1,8); +% +end +% + + +end + +end + +%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +% + +function [prestim,A1,prego] = param_time_within_range(fixed_length,cp_length,range_min_prestim,range_max_prestim, is_random_prestim, provided_time_prestim,... + range_min_A1,range_max_A1, is_random_A1, provided_time_A1,range_min_prego,range_max_prego, is_random_prego, provided_time_prego) + +if fixed_length == 1 % warm up stage where cp length is increasing +% then calculate the range/typical value + if cp_length <= 0.3 + prestim = 0.1; + A1 = 0.1; + prego = 0.1; + else + range_size = round(0.3 * cp_length,1); + if range_size > 0.4 + step_size = 0.1; + else + step_size = 0.01; + end + + timerange = 0.1:step_size:range_size; + + if is_random_prestim == 1 + prestim = timerange(randi([1, numel(timerange)],1,1)); + else + if provided_time_prestim <= range_size + prestim = provided_time_prestim; + else + prestim = range_size; + end + + end + + if is_random_A1 == 1 + A1 = timerange(randi([1, numel(timerange)],1,1)); + else + if provided_time_A1 <= range_size + A1 = provided_time_A1; + else + A1 = range_size; + end + end + + prego = cp_length - prestim - A1; + + end + +else + + if is_random_prestim == 1 + range_size_prestim = range_max_prestim - range_min_prestim; + if range_size_prestim > 0.4 + step_size_prestim = 0.1; + else + step_size_prestim = 0.01; + end + time_range_prestim = range_min_prestim:step_size_prestim:range_max_prestim; + prestim = time_range_prestim(randi([1, numel(time_range_prestim)],1,1)); + else + prestim = provided_time_prestim; + end + + if is_random_A1 == 1 + range_size_A1 = range_max_A1 - range_min_A1; + if range_size_A1 > 0.4 + step_size_A1 = 0.1; + else + step_size_A1 = 0.01; + end + time_range_A1 = range_min_A1:step_size_A1:range_max_A1; + A1 = time_range_A1(randi([1, numel(time_range_A1)],1,1)); + else + A1 = provided_time_A1; + end + + if is_random_prego == 1 + range_size_prego = range_max_prego - range_min_prego; + if range_size_prego > 0.4 + step_size_prego = 0.1; + else + step_size_prego = 0.01; + end + time_range_prego = range_min_prego:step_size_prego:range_max_prego; + prego = time_range_prego(randi([1, numel(time_range_prego)],1,1)); + else + prego = provided_time_prego; + end + +end +end + + +% + +%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% diff --git a/Protocols/@Arpit_CentrePokeTraining/Arpit_CentrePoke_TrainingStages.m b/Protocols/@Arpit_CentrePokeTraining/Arpit_CentrePoke_TrainingStages.m deleted file mode 100644 index dc39cf61..00000000 --- a/Protocols/@Arpit_CentrePokeTraining/Arpit_CentrePoke_TrainingStages.m +++ /dev/null @@ -1,141 +0,0 @@ -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -% -% ------------ STAGE SEPARATOR ------- (do not edit this line) -% -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -% --- STAGE NAME: --- (do not edit this line) -Growing nose in center time - -% --- VAR NAMES: --- (do not edit this line) -% Maximum duration of center poke (including Go cue), in secs: -max_total_cp 6.2 forceinit=1 -% Standard Go cue duration -target_go_cue_duration 0.2 -% CP_duration reached at the end of the last session -last_session_total_cp 0 -% Fractional increment in center poke duration every time there is a non-cp-violation trial: -cp_fraction 0.001 -% Minimum increment (in secs) in center poke duration every time there is a non-cp-violation trial: -cp_minimum_increment 0.001 - - -% Initial startup trials over which to gradually grow cp duration -% Only do the initial startup stuff if cp_duration is longer than: -cp_duration_threshold_for_initial_trials 1.5 -% Number of initial trials over which to gradually grow cp duration: -n_initial_trials 10 -% Starting total center poke duration: -starting_total_cp 0.5 - - -% Minimum CP_duration at which a settling_in legal_cbreak is different to -% the regular legal_cbreak: -start_settling_in_at 1000 -% Once we've reached CP_duration > start_settling_in_at, the parameters of -% the settling in: -settling_in_time 0 -settling_in_legal_cbreak 0.05 - - - - -% --- TRAINING STRING: --- (do not edit this line) - -% Check to see whether we're doing the initial startup stuff. Assumes that -% we're NOT changing the Go cue duration. -if SideSection_Total_CP_duration+0.0001 < last_session_total_cp & ... - last_session_total_cp > cp_duration_threshold_for_initial_trials, - % Check to see whether we're done with initial stuff within numerical - % rounding error: - if abs(last_session_total_cp - SideSection_Total_CP_duration) < 0.0001, - SideSection_CP_duration.value = last_session_total_cp - SideSection_time_go_cue; - elseif ~violation_history(end), - increment = ... - (last_session_total_cp - starting_total_cp)/(n_initial_trials - 1); - SideSection_CP_duration.value = SideSection_CP_duration + increment; - end; -elseif ~violation_history(end) && SideSection_Total_CP_duration < max_total_cp, - % We're in regular increasing territory - increment = SideSection_Total_CP_duration*cp_fraction; - if increment < cp_minimum_increment, - increment = value(cp_minimum_increment); - end; - % If we're growing the CP duration, grow the Go cue duration first - % until it reaches its target; after that, grow CP_duration, the - % pre-Go cue time. - if SideSection_time_go_cue < target_go_cue_duration, - SideSection_time_go_cue.value = SideSection_time_go_cue + increment; - else - SideSection_CP_duration.value = SideSection_CP_duration + increment; - end; -end; -% make sure the total reflects all the changes: -callback(SideSection_CP_duration); - -% Double-check that we don't go over the desired max value: -if SideSection_Total_CP_duration > max_total_cp, - SideSection_CP_duration.value = max_total_cp - SideSection_time_go_cue; - % once again, make sure the total reflects any the changes: - callback(SideSection_CP_duration); -end; - -% Settling in code: -if SideSection_CP_duration >= start_settling_in_at - SideSection_SettlingIn_time.value = value(settling_in_time); - SideSection_settling_legal_cbreak.value = value(settling_in_legal_cbreak); -else - SideSection_SettlingIn_time.value = 0; - SideSection_settling_legal_cbreak.value = value(SideSection_legal_cbreak); -end; - - - - -% --- END-OF-DAY LOGIC: -- (do not edit this line) - -% Store the value of the total cp duration reached: -last_session_total_cp.value = value(SideSection_Total_CP_duration); - -% Check whether we're going to do the initial startup trials on the next -% day: -if SideSection_Total_CP_duration > cp_duration_threshold_for_initial_trials, - % Yup, doing initial startup. Set CP_duration to the necessary duration: - SideSection_CP_duration.value = starting_total_cp - SideSection_time_go_cue; - % Error check, make sure we don't set it to something nonsensical: - if SideSection_CP_duration < 0.001, - SideSection_CP_duration = 0.001; - end; - % Callback to make sure the calculation of Total_CP_duration is made. - callback(SideSection_CP_duration); -end; - - -% --- COMPLETION STRING: --- (do not edit this line) -1 - - -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -% -% ------------ STAGE SEPARATOR ------- (do not edit this line) -% -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -% --- STAGE NAME: --- (do not edit this line) -Random training name - -% --- VAR NAMES: --- (do not edit this line) -raw_event = 1 -we = 45 -sam = 5 - -% --- TRAINING STRING: --- (do not edit this line) -if a > 5 -disp(a) -end - -% --- END-OF-DAY LOGIC: -- (do not edit this line) - - -% --- COMPLETION STRING: --- (do not edit this line) -1 - - diff --git a/Protocols/@Arpit_CentrePokeTraining/ParamsSection.m b/Protocols/@Arpit_CentrePokeTraining/ParamsSection.m index 5362daf0..6d6e45c4 100644 --- a/Protocols/@Arpit_CentrePokeTraining/ParamsSection.m +++ b/Protocols/@Arpit_CentrePokeTraining/ParamsSection.m @@ -90,7 +90,13 @@ set_callback(time_go_cue, {mfilename, 'new_time_go_cue'}); next_row(y); DispParam(obj, 'Total_CP_duration', CP_duration+time_go_cue, x, y, 'TooltipString', 'Total expected(rat can poke out anytime after Go cue onset) nose in center port time, in secs. Sum of CP_duration and Go Cue duration'); %#ok<*NODEF> - next_row(y); + + next_row(y); + ToggleParam(obj, 'stimuli_on', 0, x,y,... + 'OnString', 'Use Stimuli',... + 'OffString', 'Fixed Sound',... + 'TooltipString', sprintf('If on (black) then it enables training with stimuli else a fixed sound')); + next_row(y); ToggleParam(obj, 'warmup_on', 1, x,y,... 'OnString', 'Warmup ON',... 'OffString', 'Warmup OFF',... @@ -151,7 +157,7 @@ next_row(y); SoloFunctionAddVars('Arpit_CentrePokeTrainingSMA', 'ro_args', ... - {'CP_duration';'SideLed_duration'; ... + {'CP_duration';'SideLed_duration'; 'stimuli_on';... 'RewardCollection_duration';'training_stage'; ... 'legal_cbreak' ; 'SettlingIn_time'; 'time_go_cue'; ... 'A1_time';'time_bet_aud1_gocue' ; 'PreStim_time'; @@ -162,6 +168,9 @@ SoloFunctionAddVars('SessionPerformanceSection', 'ro_args', ... {'training_stage'}); + SoloFunctionAddVars('StimulusSection', 'ro_args', ... + {'training_stage','stimuli_on'}); + SoloFunctionAddVars('Training_ParamsSection', 'ro_args', ... {'training_stage'}); diff --git a/Protocols/@Arpit_CentrePokeTraining/StimulusSection.m b/Protocols/@Arpit_CentrePokeTraining/StimulusSection.m new file mode 100644 index 00000000..29e6051c --- /dev/null +++ b/Protocols/@Arpit_CentrePokeTraining/StimulusSection.m @@ -0,0 +1,442 @@ + + +function [x, y] = StimulusSection(obj, action, varargin) + +GetSoloFunctionArgs(obj); + +switch action + + % ------------------------------------------------------------------ + % INIT + % ------------------------------------------------------------------ + + case 'init' + if length(varargin) < 2 + error('Need at least two arguments, x and y position, to initialize %s', mfilename); + end + x = varargin{1}; y = varargin{2}; + + ToggleParam(obj, 'StimulusShow', 0, x, y, 'OnString', 'Stimuli', ... + 'OffString', 'Stimuli', 'TooltipString', 'Show/Hide Stimulus panel'); + set_callback(StimulusShow, {mfilename, 'show_hide'}); %#ok (Defined just above) + next_row(y); + + SoloParamHandle(obj, 'myfig', 'value', figure('closerequestfcn', [mfilename '(' class(obj) ', ''hide'');'], 'MenuBar', 'none', ... + 'Name', mfilename), 'saveable', 0); + screen_size = get(0, 'ScreenSize'); + set(value(myfig),'Position',[1 screen_size(4)-740, 1000 1000]); % put fig at top right + set(double(gcf), 'Visible', 'off'); + x=10;y=10; + + SoloParamHandle(obj, 'ax', 'saveable', 0, ... + 'value', axes('Position', [0.01 0.5 0.45 0.45])); + ylabel('log_e A','FontSize',16,'FontName','Cambria Math'); + set(value(ax),'Fontsize',15) + xlabel('Sound Categorization','FontSize',16,'FontName','Cambria Math') + + SoloParamHandle(obj, 'axperf', 'saveable', 0, ... + 'value', axes('Position', [0.5 0.5 0.45 0.45])); + ylabel('log_e A','FontSize',16,'FontName','Cambria Math'); + set(value(axperf),'Fontsize',15) + xlabel('Sound Categorization','FontSize',16,'FontName','Cambria Math') + + SoundManagerSection(obj, 'declare_new_sound', 'StimAUD1') + SoloParamHandle(obj, 'thisstim', 'value', []); + SoloParamHandle(obj, 'thisstimlog', 'value', []); + SoloParamHandle(obj, 'h1', 'value', []); + + y=5; + + next_row(y); + next_row(y); + PushbuttonParam(obj, 'refresh_stimuli', x,y , 'TooltipString', 'Instantiates the stimuli given the new set of parameters'); + set_callback(refresh_stimuli, {mfilename, 'plot_stimuli'}); + + + next_row(y); + next_row(y); + MenuParam(obj, 'Rule', {'S2>S_boundry Left','S2>S_boundry Right'}, ... + 'S2>S_boundry Left', x, y, 'labelfraction', 0.35, 'TooltipString', sprintf(['\nThis bottom determines the rule\n', ... + '\n''S2>S_boundry Left'' means if Aud2 > Aud_boundry then reward will be delivered from the left water spout and if Aud2 < Aud_boundry then water comes form right\n',... + '\n''S2>S_boundry Right'' means if Aud2 < Aud_boundry then reward will be delivered from the left water spout and if Aud2 > Aud_boundry then water comes from right\n'])); + next_row(y, 1) + + next_column(x); + y=5; + MenuParam(obj, 'filter_type', {'GAUS','LPFIR', 'FIRLS','BUTTER','MOVAVRG','KAISER','EQUIRIP','HAMMING'}, ... + 'GAUS', x, y, 'labelfraction', 0.35, 'TooltipString', sprintf(['\nDifferent filters. ''LPFIR'': lowpass FIR ''FIRLS'': Least square linear-phase FIR filter design\n', ... + '\n''BUTTER'': IIR Butterworth lowpass filter ''GAUS'': Gaussian filter (window)\n', ... + '\n''MOVAVRG'': Moving average FIR filter ''KAISER'': Kaiser-window FIR filtering\n', ... + '\n''EQUIRIP'':Eqiripple FIR filter ''HAMMING'': Hamming-window based FIR'])); + next_row(y, 1) + NumeditParam(obj,'fcut',110,x,y,'label','fcut','TooltipString','Cut off frequency on the original white noise'); + next_row(y); + NumeditParam(obj,'lfreq',2000,x,y,'label','Modulator_LowFreq','TooltipString','Lower bound for the frequency modulator'); + next_row(y); + NumeditParam(obj,'hfreq',20000,x,y,'label','Modulator_HighFreq','TooltipString','Upper bound for the frequency modulator'); + next_row(y); + DispParam(obj, 'A1_sigma', 0.01, x,y,'label','A1_sigma','TooltipString','Sigma value for the first stimulus'); + next_row(y); + DispParam(obj, 'A1_freq', 0.01, x,y,'label','A1_freq','TooltipString','Sigma value for the first stimulus'); + next_row(y); + NumeditParam(obj,'minS1',0.007,x,y,'label','minS1','TooltipString','min sigma value for AUD1'); + set_callback(minS1, {mfilename, 'Cal_Boundary'}); + next_row(y); + NumeditParam(obj,'maxS1',0.05,x,y,'label','maxS1','TooltipString','max sigma value for AUD1'); + set_callback(maxS1, {mfilename, 'Cal_Boundary'}); + next_row(y); + NumeditParam(obj,'minF1',4,x,y,'label','minF1','TooltipString','min frequency value for AUD1'); + set_callback(minF1, {mfilename, 'Cal_Boundary'}); + next_row(y); + NumeditParam(obj,'maxF1',10,x,y,'label','maxF1','TooltipString','max frequency value for AUD1'); + set_callback(maxF1, {mfilename, 'Cal_Boundary'}); + next_row(y); + DispParam(obj,'boundary',-3.9,x,y,'label','boundary','TooltipString','decision boundary for categorisation (log)'); + next_row(y); + MenuParam(obj, 'mu_location', {'center', 'side'}, ... + 'center', x, y, 'labelfraction', 0.35, 'TooltipString', sprintf('\nLocation of boundary')); + set_callback(mu_location, {mfilename, 'Cal_Boundary'}); + next_row(y); + ToggleParam(obj, 'frequency_categorization', 0, x,y,... + 'OnString', 'Frequency(Tone)',... + 'OffString', 'Amplitude(Noise)',... + 'TooltipString', sprintf('If on (black) then it enables the presentation of pure tones')); + set_callback(frequency_categorization, {mfilename, 'Cal_Boundary'}); + set_callback(frequency_categorization, {mfilename, 'FrequencyCategorization'}); + next_row(y); + MenuParam(obj, 'DistributionType', {'uniform','unim-unif', 'unif-unim', 'unimodal', 'bimodal', 'asym-unif', 'Unim-Unif', 'Unif-Unim'}, ... + 'uniform', x, y, 'labelfraction', 0.35, 'TooltipString', sprintf('\nDifferent distributions')); + + StimulusSection(obj,'plot_stimuli'); + + case 'prepare_next_trial' + + ParamsSection(obj,'get_current_side'); + StimulusSection(obj,'pick_current_stimulus'); + srate=SoundManagerSection(obj,'get_sample_rate'); + Fs=srate; + T=value(A1_time); + + if frequency_categorization + % produce the tone + A1_freq.value=exp(value(thisstim)); + A1_sigma.value=0; + A1 = value(thisstim); + dur1 = A1_time*1000; + bal=0; + freq1=A1_freq*1000; + vol=0.001; + RVol=vol*min(1,(1+bal)); + LVol=vol*min(1,(1-bal)); + t=0:(1/srate):(dur1/1000); + t = t(1:end-1); + tw=sin(t*2*pi*freq1); + RW=RVol*tw; + %w=[LW;RW]; + AUD1 = RW; + else + % produce noise pattern + A1_sigma.value=exp(value(thisstim)); + A1_freq.value=0; + A1 = value(thisstim); + [rawA1, rawA2, normA1, normA2]=noisestim(1,1,T,value(fcut),Fs,value(filter_type)); + modulator=singlenoise(1,T,[value(lfreq) value(hfreq)],Fs,'BUTTER'); + AUD1=normA1(1:A1_time*srate).*modulator(1:A1_time*srate).*A1_sigma; + end + + if ~isempty(AUD1) + SoundManagerSection(obj, 'set_sound', 'StimAUD1', [AUD1'; AUD1']) + end + + SoundManagerSection(obj, 'send_not_yet_uploaded_sounds'); + + % Plot current stimulus and move to saving stimulus history + + if value(thisstimlog(n_completed_trials+1)) > value(boundary)%value(numClass) + set(value(h1), 'YData', value(A1), 'color',[0.4 0.8 0.1],'markerfacecolor',[0.4 0.8 0.1]); + else + set(value(h1), 'YData', value(A1), 'color',[0.8 0.4 0.1],'markerfacecolor',[0.8 0.4 0.1]); + end + + if n_completed_trials >0 + if ~violation_history(n_completed_trials) && ~timeout_history(n_completed_trials) + StimulusSection(obj,'update_stimulus_history'); + else + StimulusSection(obj,'update_stimulus_history_nan'); + end + end + + + %% Case make_stimuli + case 'make_stimuli' + if nargout>0 + if frequency_categorization + stim_min_log = log(value(minF1)); + stim_max_log = log(value(maxF1)); + else + stim_min_log = log(value(minS1)); + stim_max_log = log(value(maxS1)); + end + + stim_range_log = stim_max_log - stim_min_log; + if strcmp(DistributionType,'uniform') + stim_i_log = stim_min_log + rand() * (stim_max_log - stim_min_log); + + elseif strcmp(DistributionType,'unimodal') + mu_1 = value(boundary); + sigma = 0.4; + stim_tot = []; + for i = 1:1000 + stim_i_log = stim_max_log +1; + while (stim_i_log > stim_max_log) ||(stim_i_log < stim_min_log) + stim_i_log = normrnd(mu_1,sigma); + end + stim_tot = [stim_tot stim_i_log]; + end + + % stim_tot = [] + % for i = 1:1000 + % stim_i_log = stim_min_log + rand() * (stim_max_log - stim_min_log); + % stim_i_log = stim_i_log + 0.4 * cos(2 * pi * 0.5 * (stim_i_log - stim_min_log)/(stim_max_log - stim_min_log)) + % stim_i_log = stim_i_log - 0.4; + % stim_i_log = stim_min_log + (stim_i_log - stim_min_log) * stim_range_log/(stim_range_log - 0.8) + % stim_tot = [stim_tot stim_i_log]; + % end + + elseif strcmp(DistributionType,'bimodal') + mu_1 = (stim_min_log + value(boundary))/2; + mu_2 = (stim_max_log + value(boundary))/2; + sigma = 0.2; + % stim_tot = []; + % for i = 1:1000 + stim_i_log = stim_max_log + 1; + while (stim_i_log > stim_max_log) ||(stim_i_log < stim_min_log) + if rand()>0.5 + stim_i_log = normrnd(mu_1,sigma); + else + stim_i_log = normrnd(mu_2,sigma); + end + end + % stim_tot = [stim_tot stim_i_log]; + % end + % stim_tot = [stim_tot stim_i_log]; + + + % stim_tot = [] + % for i = 1:1000 + % stim_i_log = stim_min_log + rand() * (stim_max_log - stim_min_log); + % stim_i_log = stim_i_log + 0.07 * cos(2 * pi * 1.5 * (stim_i_log - stim_min_log)/(stim_max_log - stim_min_log)) + % stim_i_log = stim_i_log - 0.07; + % stim_i_log = stim_min_log + (stim_i_log - stim_min_log) * stim_range_log/(stim_range_log - 0.14) + % stim_tot = [stim_tot stim_i_log]; + % end + + elseif strcmp(DistributionType,'unim-unif') + stim_i_log = stim_min_log + rand() * (stim_max_log - stim_min_log); + if stim_i_log < (stim_max_log + stim_min_log) / 2 + for i = 1:30 + stim_i_log = stim_i_log - 0.01 * sin(2 * pi * 0.5 * (stim_i_log - stim_min_log) / (stim_range_log/2)); + end + end + elseif strcmp(DistributionType,'unif-unim') + stim_i_log = stim_min_log + rand() * (stim_max_log - stim_min_log); + if stim_i_log > (stim_max_log + stim_min_log) / 2 + for i = 1:30 + stim_i_log = stim_i_log - 0.01 * sin(2 * pi * 0.5 * (stim_i_log - stim_min_log) / (stim_range_log/2)); + end + end + elseif strcmp(DistributionType,'Unim-Unif') + stim_i_log = stim_min_log + rand() * (stim_max_log - stim_min_log); + if stim_i_log < (stim_max_log + stim_min_log) / 2 + for i = 1:30 + stim_i_log = stim_i_log + 0.01 * sin(2 * pi * 0.5 * (stim_i_log - stim_min_log) / (stim_range_log/2)); + end + end + elseif strcmp(DistributionType,'Unif-Unim') + stim_i_log = stim_min_log + rand() * (stim_max_log - stim_min_log); + if stim_i_log > (stim_max_log + stim_min_log) / 2 + for i = 1:30 + stim_i_log = stim_i_log + 0.01 * sin(2 * pi * 0.5 * (stim_i_log - stim_min_log) / (stim_range_log/2)); + end + end + elseif strcmp(DistributionType,'asym-unif') + % stim_tot = []; + stim_half_log = value(boundary) - stim_min_log; + % for i = 1:1000 + stim_i_log = stim_min_log + rand() * (stim_max_log - stim_min_log); + if stim_i_log < (stim_min_log + value(boundary)) / 2 + for i = 1:30 + stim_i_log = stim_i_log - 0.01 * sin(2 * pi * 0.5 * (stim_i_log - stim_min_log) / (stim_half_log/2)); + end + end + % stim_tot = [stim_tot stim_i_log]; + % end + end + x = stim_i_log; + end + + + %% Case pick_current_stimulus + case 'pick_current_stimulus' + if frequency_categorization + stim_min_log = log(value(minF1)); + stim_max_log = log(value(maxF1)); + else + stim_min_log = log(value(minS1)); + stim_max_log = log(value(maxS1)); + end + if strcmp(Rule,'S2>S_boundry Left') + if strcmp(ThisTrial, 'LEFT') + stim_i_log = stim_min_log; + while stim_i_log <= value(boundary) + stim_i_log = StimulusSection(obj,'make_stimuli'); + end + else + stim_i_log = stim_max_log; + while stim_i_log >= value(boundary) + stim_i_log = StimulusSection(obj,'make_stimuli'); + end + end + elseif strcmp(Rule,'S2>S_boundry Right') + if strcmp(ThisTrial, 'LEFT') + stim_i_log = stim_max_log; + while stim_i_log >= value(boundary) + stim_i_log = StimulusSection(obj,'make_stimuli'); + end + else + stim_i_log = stim_min_log; + while stim_i_log <= value(boundary) + stim_i_log = StimulusSection(obj,'make_stimuli'); + end + end + end + thisstim.value=stim_i_log; + thisstimlog(n_completed_trials+1)= stim_i_log; + + %% Case plot_pais + case 'plot_stimuli' + + %% plot the stimuli + if frequency_categorization + boundary.value = (log(value(minF1)) + log(value(maxF1)))/2; + stim_min_log = log(value(minF1)); + stim_max_log = log(value(maxF1)); + stim_min = value(minF1); + stim_max = value(maxF1); + else + boundary.value = (log(value(minS1)) + log(value(maxS1)))/2; + if strcmp(mu_location,'center') + boundary.value = value(boundary); + elseif strcmp(mu_location,'side') + boundary.value = (log(value(minS1)) + value(boundary))/2; + end + stim_min_log = log(value(minS1)); + stim_max_log = log(value(maxS1)); + stim_min = value(minS1); + stim_max = value(maxS1); + end + cla(value(ax)) + xd=1; + axes(value(ax)); + plot(xd,stim_min_log,'s','MarkerSize',15,'MarkerEdgeColor',[0 0 0],'LineWidth',2) + hold on + plot(xd,stim_max_log,'s','MarkerSize',15,'MarkerEdgeColor',[0 0 0],'LineWidth',2) + line([0,2], [value(boundary),value(boundary)]); + axis square + set(value(ax),'ytick',([stim_min_log, stim_max_log]),'xtick',xd); + set(value(ax),'yticklabel',([stim_min, stim_max]),'xticklabel','S1'); + ylabel('\sigma_1 in log scale','FontSize',16,'FontName','Cambria Math'); + set(value(ax),'Fontsize',15) + xlabel('S1','FontSize',16,'FontName','Cambria Math') + + ParamsSection(obj,'get_current_side'); + StimulusSection(obj,'pick_current_stimulus'); + + A1 = value(thisstim); + + %% plot the stimulus; + if value(thisstim) > value(boundary)%value(numClass) + h1.value=plot(xd,value(A1),'s','color',[0.4 0.8 0.1],'markerfacecolor',[0.4 0.8 0.1],'MarkerSize',15,'LineWidth',3); + else + h1.value=plot(xd,value(A1),'s','color',[0.8 0.4 0.1],'markerfacecolor',[0.8 0.4 0.1],'MarkerSize',15,'LineWidth',3); + end + + %% Boundary Calculate + case 'Cal_Boundary' + if frequency_categorization + val_boundary = (log(value(minF1)) + log(value(maxF1)))/2; + min_val = log(value(minF1)); + else + val_boundary = (log(value(minS1)) + log(value(maxS1)))/2; + min_val = log(value(minS1)); + end + if strcmp(mu_location,'center') + boundary.value = val_boundary; + elseif strcmp(mu_location,'side') + boundary.value = (min_val + val_boundary)/2; + end + + %% Case frequency ON + case 'FrequencyCategorization' + if frequency_categorization == 1 + make_visible(maxF1);make_visible(minF1);make_visible(A1_freq); + make_invisible(maxS1);make_invisible(minS1);make_invisible(A1_sigma); + StimulusSection(obj,'plot_stimuli'); + else + make_visible(maxS1);make_visible(minS1);make_visible(A1_sigma); + make_invisible(maxF1);make_invisible(minF1);make_invisible(A1_freq); + StimulusSection(obj,'plot_stimuli'); + end + + %% Case get_stimuli + % case 'get_stimuli' + % if nargout>0 + % x=value(S1); + % end + + + %% Case close + case 'close' + % Delete all SoloParamHandles who belong to this object and whose + % fullname starts with the name of this mfile: + if exist('myfig', 'var') && isa(myfig, 'SoloParamHandle') && ishandle(value(myfig)) %#ok + delete(value(myfig)); + end + delete_sphandle('owner', ['^@' class(obj) '$'], ... + 'fullname', ['^' mfilename]); + + %% Case update_stimuli + case 'update_stimuli' + StimulusSection(obj,'plot_stimuli'); + + case 'update_stimulus_history' + ps=value(stimulus_history); + ps(n_completed_trials)=value(thisstimlog(n_completed_trials)); + stimulus_history.value=ps; + + case 'update_stimulus_history_nan' + ps=value(stimulus_history); + ps(n_completed_trials)=value(thisstimlog(n_completed_trials));%nan; + stimulus_history.value=ps; + + %% Case hide + case 'hide' + StimulusShow.value = 0; + set(value(myfig), 'Visible', 'off'); + + %% Case show + case 'show' + StimulusShow.value = 1; + set(value(myfig), 'Visible', 'on'); + + %% Case Show_hide + case 'show_hide' + if StimulusShow == 1 + set(value(myfig), 'Visible', 'on'); %#ok (defined by GetSoloFunctionArgs) + else + set(value(myfig), 'Visible', 'off'); + end + +end + +end diff --git a/Protocols/@Arpit_CentrePokeTraining/pipeline_Arpit_CentrePokeTraining_experimenter_ratname_250319c.m b/Protocols/@Arpit_CentrePokeTraining/pipeline_Arpit_CentrePokeTraining_experimenter_ratname_250319c.m index ad280798..93f2ede0 100644 --- a/Protocols/@Arpit_CentrePokeTraining/pipeline_Arpit_CentrePokeTraining_experimenter_ratname_250319c.m +++ b/Protocols/@Arpit_CentrePokeTraining/pipeline_Arpit_CentrePokeTraining_experimenter_ratname_250319c.m @@ -30,6 +30,7 @@ CreateHelperVar(obj,'stages_trial_counter_oppSide','value',zeros(1,8)); CreateHelperVar(obj,'stages_timeout_rate','value',zeros(1,8)); CreateHelperVar(obj,'stages_violation_rate','value',zeros(1,8)); +CreateHelperVar(obj,'stage_start_completed_trial','value',n_completed_trials,'forceinit',true); % end @@ -45,21 +46,23 @@ stage_no = value(SessionDefinition_CURRENT_ACTIVE_STAGE); ParamsSection_training_stage.value = stage_no; callback(ParamsSection_training_stage); -this_stage_trial_counter = value(stages_trial_counter); -this_stage_trial_counter(stage_no) = this_stage_trial_counter(stage_no) + 1; -SessionPerformanceSection_ntrials_stage.value = this_stage_trial_counter(stage_no); -callback(SessionPerformanceSection_ntrials_stage); -stages_trial_counter.value = this_stage_trial_counter; -this_stage_trial_counter_today = value(stages_trial_counter_today); -this_stage_trial_counter_today(stage_no) = this_stage_trial_counter_today(stage_no) + 1; -SessionPerformanceSection_ntrials_stage_today.value = this_stage_trial_counter_today(stage_no); -callback(SessionPerformanceSection_ntrials_stage_today); -stages_trial_counter_today.value = this_stage_trial_counter_today; -this_stage_trial_counter_oppSide = value(stages_trial_counter_oppSide); -if value(previous_sides(end)) ~= value(ParamsSection_ThisTrial) - this_stage_trial_counter_oppSide(stage_no) = this_stage_trial_counter_oppSide(stage_no) + 1; +if n_completed_trials > value(stage_start_completed_trial) + this_stage_trial_counter = value(stages_trial_counter); + this_stage_trial_counter(stage_no) = this_stage_trial_counter(stage_no) + 1; + SessionPerformanceSection_ntrials_stage.value = this_stage_trial_counter(stage_no); + callback(SessionPerformanceSection_ntrials_stage); + stages_trial_counter.value = this_stage_trial_counter; + this_stage_trial_counter_today = value(stages_trial_counter_today); + this_stage_trial_counter_today(stage_no) = this_stage_trial_counter_today(stage_no) + 1; + SessionPerformanceSection_ntrials_stage_today.value = this_stage_trial_counter_today(stage_no); + callback(SessionPerformanceSection_ntrials_stage_today); + stages_trial_counter_today.value = this_stage_trial_counter_today; + this_stage_trial_counter_oppSide = value(stages_trial_counter_oppSide); + if value(previous_sides(end)) ~= value(ParamsSection_ThisTrial) + this_stage_trial_counter_oppSide(stage_no) = this_stage_trial_counter_oppSide(stage_no) + 1; + end + stages_trial_counter_oppSide.value = this_stage_trial_counter_oppSide; end -stages_trial_counter_oppSide.value = this_stage_trial_counter_oppSide; % end @@ -124,6 +127,7 @@ CreateHelperVar(obj,'stages_timeout_rate','value',zeros(1,8)); CreateHelperVar(obj,'stages_violation_rate','value',zeros(1,8)); CreateHelperVar(obj,'this_stage_opp_side_trials', 'value', '0', 'forceinit','true'); +CreateHelperVar(obj,'stage_start_completed_trial','value',n_completed_trials,'forceinit',true); % end @@ -138,26 +142,33 @@ stage_no = value(SessionDefinition_CURRENT_ACTIVE_STAGE); ParamsSection_training_stage.value = stage_no; callback(ParamsSection_training_stage); -% Update the helper vars -this_stage_trial_counter = value(stages_trial_counter); -this_stage_trial_counter(stage_no) = this_stage_trial_counter(stage_no) + 1; -SessionPerformanceSection_ntrials_stage.value = this_stage_trial_counter(stage_no); -callback(SessionPerformanceSection_ntrials_stage); -stages_trial_counter.value = this_stage_trial_counter; - -this_stage_trial_counter_today = value(stages_trial_counter_today); -this_stage_trial_counter_today(stage_no) = this_stage_trial_counter_today(stage_no) + 1; -SessionPerformanceSection_ntrials_stage_today.value = this_stage_trial_counter_today(stage_no); -callback(SessionPerformanceSection_ntrials_stage_today); -stages_trial_counter_today.value = this_stage_trial_counter_today; +if n_completed_trials > value(stage_start_completed_trial) + % Update the helper vars + this_stage_trial_counter = value(stages_trial_counter); + this_stage_trial_counter(stage_no) = this_stage_trial_counter(stage_no) + 1; + SessionPerformanceSection_ntrials_stage.value = this_stage_trial_counter(stage_no); + callback(SessionPerformanceSection_ntrials_stage); + stages_trial_counter.value = this_stage_trial_counter; + + this_stage_trial_counter_today = value(stages_trial_counter_today); + this_stage_trial_counter_today(stage_no) = this_stage_trial_counter_today(stage_no) + 1; + SessionPerformanceSection_ntrials_stage_today.value = this_stage_trial_counter_today(stage_no); + callback(SessionPerformanceSection_ntrials_stage_today); + stages_trial_counter_today.value = this_stage_trial_counter_today; + + this_stage_trial_counter_oppSide = value(stages_trial_counter_oppSide); + if value(previous_sides(end)) ~= value(ParamsSection_ThisTrial) + this_stage_trial_counter_oppSide(stage_no) = this_stage_trial_counter_oppSide(stage_no) + 1; + this_stage_opp_side_trials.value = value(this_stage_opp_side_trials) + 1; + end + stages_trial_counter_oppSide.value = this_stage_trial_counter_oppSide; -this_stage_trial_counter_oppSide = value(stages_trial_counter_oppSide); -if value(previous_sides(end)) ~= value(ParamsSection_ThisTrial) - this_stage_trial_counter_oppSide(stage_no) = this_stage_trial_counter_oppSide(stage_no) + 1; - this_stage_opp_side_trials.value = value(this_stage_opp_side_trials) + 1; + this_stage_timeout_percent = value(stages_timeout_rate); + this_stage_timeout_percent(stage_no) = ((this_stage_timeout_percent(stage_no) * this_stage_trial_counter(stage_no)) + double(timeout_history(end)))/(this_stage_trial_counter(stage_no) + 1); + SessionPerformanceSection_timeout_stage.value = this_stage_timeout_percent(stage_no); + callback(SessionPerformanceSection_timeout_stage); + stages_timeout_rate.value = this_stage_timeout_percent; end -stages_trial_counter_oppSide.value = this_stage_trial_counter_oppSide; - % Update the reward collection time based upon behav if size(timeout_history) > 5 if all(value(timeout_history(end-1:end))) && value(this_stage_opp_side_trials) >= 2 @@ -180,11 +191,6 @@ end end -this_stage_timeout_percent = value(stages_timeout_rate); -this_stage_timeout_percent(stage_no) = ((this_stage_timeout_percent(stage_no) * this_stage_trial_counter(stage_no)) + double(timeout_history(end)))/(this_stage_trial_counter(stage_no) + 1); -SessionPerformanceSection_timeout_stage.value = this_stage_timeout_percent(stage_no); -callback(SessionPerformanceSection_timeout_stage); -stages_timeout_rate.value = this_stage_timeout_percent; % end @@ -238,6 +244,7 @@ CreateHelperVar(obj,'stages_timeout_rate','value',zeros(1,8)); CreateHelperVar(obj,'stages_violation_rate','value',zeros(1,8)); CreateHelperVar(obj,'last_session_CP','value',0); +CreateHelperVar(obj,'stage_start_completed_trial','value',n_completed_trials,'forceinit',true); % end @@ -257,25 +264,26 @@ stage_no = value(SessionDefinition_CURRENT_ACTIVE_STAGE); ParamsSection_training_stage.value = stage_no; callback(ParamsSection_training_stage); -% Update the helper vars -this_stage_trial_counter = value(stages_trial_counter); -this_stage_trial_counter(stage_no) = this_stage_trial_counter(stage_no) + 1; -SessionPerformanceSection_ntrials_stage.value = this_stage_trial_counter(stage_no); -callback(SessionPerformanceSection_ntrials_stage); -stages_trial_counter.value = this_stage_trial_counter; - -this_stage_trial_counter_today = value(stages_trial_counter_today); -this_stage_trial_counter_today(stage_no) = this_stage_trial_counter_today(stage_no) + 1; -SessionPerformanceSection_ntrials_stage_today.value = this_stage_trial_counter_today(stage_no); -callback(SessionPerformanceSection_ntrials_stage_today); -stages_trial_counter_today.value = this_stage_trial_counter_today; - -this_stage_timeout_percent = value(stages_timeout_rate); -this_stage_timeout_percent(stage_no) = ((this_stage_timeout_percent(stage_no) * this_stage_trial_counter(stage_no)) + double(timeout_history(end)))/(this_stage_trial_counter(stage_no) + 1); -SessionPerformanceSection_timeout_stage.value = this_stage_timeout_percent(stage_no); -callback(SessionPerformanceSection_timeout_stage); -stages_timeout_rate.value = this_stage_timeout_percent; - +if n_completed_trials > value(stage_start_completed_trial) + % Update the helper vars + this_stage_trial_counter = value(stages_trial_counter); + this_stage_trial_counter(stage_no) = this_stage_trial_counter(stage_no) + 1; + SessionPerformanceSection_ntrials_stage.value = this_stage_trial_counter(stage_no); + callback(SessionPerformanceSection_ntrials_stage); + stages_trial_counter.value = this_stage_trial_counter; + + this_stage_trial_counter_today = value(stages_trial_counter_today); + this_stage_trial_counter_today(stage_no) = this_stage_trial_counter_today(stage_no) + 1; + SessionPerformanceSection_ntrials_stage_today.value = this_stage_trial_counter_today(stage_no); + callback(SessionPerformanceSection_ntrials_stage_today); + stages_trial_counter_today.value = this_stage_trial_counter_today; + + this_stage_timeout_percent = value(stages_timeout_rate); + this_stage_timeout_percent(stage_no) = ((this_stage_timeout_percent(stage_no) * this_stage_trial_counter(stage_no)) + double(timeout_history(end)))/(this_stage_trial_counter(stage_no) + 1); + SessionPerformanceSection_timeout_stage.value = this_stage_timeout_percent(stage_no); + callback(SessionPerformanceSection_timeout_stage); + stages_timeout_rate.value = this_stage_timeout_percent; +end % Change the value of CP Duration if n_completed_trials < 1 ParamsSection_CP_duration.value = value(ParamsSection_init_CP_duration); @@ -322,6 +330,7 @@ end % + %% Introduce Violation for Centre Poke % @@ -336,6 +345,7 @@ CreateHelperVar(obj,'stages_timeout_rate','value',zeros(1,8)); CreateHelperVar(obj,'stages_violation_rate','value',zeros(1,8)); CreateHelperVar(obj,'last_session_CP','value',0); +CreateHelperVar(obj,'stage_start_completed_trial','value',n_completed_trials,'forceinit',true); % end @@ -357,31 +367,32 @@ stage_no = value(SessionDefinition_CURRENT_ACTIVE_STAGE); ParamsSection_training_stage.value = stage_no; callback(ParamsSection_training_stage); -% Update the helper vars -this_stage_trial_counter = value(stages_trial_counter); -this_stage_trial_counter(stage_no) = this_stage_trial_counter(stage_no) + 1; -SessionPerformanceSection_ntrials_stage.value = this_stage_trial_counter(stage_no); -callback(SessionPerformanceSection_ntrials_stage); -stages_trial_counter.value = this_stage_trial_counter; - -this_stage_trial_counter_today = value(stages_trial_counter_today); -this_stage_trial_counter_today(stage_no) = this_stage_trial_counter_today(stage_no) + 1; -SessionPerformanceSection_ntrials_stage_today.value = this_stage_trial_counter_today(stage_no); -callback(SessionPerformanceSection_ntrials_stage_today); -stages_trial_counter_today.value = this_stage_trial_counter_today; - -this_stage_timeout_percent = value(stages_timeout_rate); -this_stage_timeout_percent(stage_no) = ((this_stage_timeout_percent(stage_no) * this_stage_trial_counter(stage_no)) + double(timeout_history(end)))/(this_stage_trial_counter(stage_no) + 1); -SessionPerformanceSection_timeout_stage.value = this_stage_timeout_percent(stage_no); -callback(SessionPerformanceSection_timeout_stage); -stages_timeout_rate.value = this_stage_timeout_percent; - -this_stage_violation_percent = value(stages_violation_rate); -this_stage_violation_percent(stage_no) = ((this_stage_violation_percent(stage_no) * this_stage_trial_counter(stage_no)) + double(violation_history(end)))/(this_stage_trial_counter(stage_no) + 1); -SessionPerformanceSection_violation_stage.value = this_stage_violation_percent(stage_no); -callback(SessionPerformanceSection_violation_stage); -stages_violation_rate.value = this_stage_violation_percent; - +if n_completed_trials > value(stage_start_completed_trial) + % Update the helper vars + this_stage_trial_counter = value(stages_trial_counter); + this_stage_trial_counter(stage_no) = this_stage_trial_counter(stage_no) + 1; + SessionPerformanceSection_ntrials_stage.value = this_stage_trial_counter(stage_no); + callback(SessionPerformanceSection_ntrials_stage); + stages_trial_counter.value = this_stage_trial_counter; + + this_stage_trial_counter_today = value(stages_trial_counter_today); + this_stage_trial_counter_today(stage_no) = this_stage_trial_counter_today(stage_no) + 1; + SessionPerformanceSection_ntrials_stage_today.value = this_stage_trial_counter_today(stage_no); + callback(SessionPerformanceSection_ntrials_stage_today); + stages_trial_counter_today.value = this_stage_trial_counter_today; + + this_stage_timeout_percent = value(stages_timeout_rate); + this_stage_timeout_percent(stage_no) = ((this_stage_timeout_percent(stage_no) * this_stage_trial_counter(stage_no)) + double(timeout_history(end)))/(this_stage_trial_counter(stage_no) + 1); + SessionPerformanceSection_timeout_stage.value = this_stage_timeout_percent(stage_no); + callback(SessionPerformanceSection_timeout_stage); + stages_timeout_rate.value = this_stage_timeout_percent; + + this_stage_violation_percent = value(stages_violation_rate); + this_stage_violation_percent(stage_no) = ((this_stage_violation_percent(stage_no) * this_stage_trial_counter(stage_no)) + double(violation_history(end)))/(this_stage_trial_counter(stage_no) + 1); + SessionPerformanceSection_violation_stage.value = this_stage_violation_percent(stage_no); + callback(SessionPerformanceSection_violation_stage); + stages_violation_rate.value = this_stage_violation_percent; +end % Change the value of CP Duration if n_completed_trials < 1 ParamsSection_CP_duration.value = value(ParamsSection_init_CP_duration); @@ -454,6 +465,7 @@ CreateHelperVar(obj,'stages_timeout_rate','value',zeros(1,8)); CreateHelperVar(obj,'stages_violation_rate','value',zeros(1,8)); CreateHelperVar(obj,'last_session_CP','value',0); +CreateHelperVar(obj,'stage_start_completed_trial','value',n_completed_trials,'forceinit',true); % end @@ -476,31 +488,32 @@ stage_no = value(SessionDefinition_CURRENT_ACTIVE_STAGE); ParamsSection_training_stage.value = stage_no; callback(ParamsSection_training_stage); -% Update the helper vars -this_stage_trial_counter = value(stages_trial_counter); -this_stage_trial_counter(stage_no) = this_stage_trial_counter(stage_no) + 1; -SessionPerformanceSection_ntrials_stage.value = this_stage_trial_counter(stage_no); -callback(SessionPerformanceSection_ntrials_stage); -stages_trial_counter.value = this_stage_trial_counter; - -this_stage_trial_counter_today = value(stages_trial_counter_today); -this_stage_trial_counter_today(stage_no) = this_stage_trial_counter_today(stage_no) + 1; -SessionPerformanceSection_ntrials_stage_today.value = this_stage_trial_counter_today(stage_no); -callback(SessionPerformanceSection_ntrials_stage_today); -stages_trial_counter_today.value = this_stage_trial_counter_today; - -this_stage_timeout_percent = value(stages_timeout_rate); -this_stage_timeout_percent(stage_no) = ((this_stage_timeout_percent(stage_no) * this_stage_trial_counter(stage_no)) + double(timeout_history(end)))/(this_stage_trial_counter(stage_no) + 1); -SessionPerformanceSection_timeout_stage.value = this_stage_timeout_percent(stage_no); -callback(SessionPerformanceSection_timeout_stage); -stages_timeout_rate.value = this_stage_timeout_percent; - -this_stage_violation_percent = value(stages_violation_rate); -this_stage_violation_percent(stage_no) = ((this_stage_violation_percent(stage_no) * this_stage_trial_counter(stage_no)) + double(violation_history(end)))/(this_stage_trial_counter(stage_no) + 1); -SessionPerformanceSection_violation_stage.value = this_stage_violation_percent(stage_no); -callback(SessionPerformanceSection_violation_stage); -stages_violation_rate.value = this_stage_violation_percent; - +if n_completed_trials > value(stage_start_completed_trial) + % Update the helper vars + this_stage_trial_counter = value(stages_trial_counter); + this_stage_trial_counter(stage_no) = this_stage_trial_counter(stage_no) + 1; + SessionPerformanceSection_ntrials_stage.value = this_stage_trial_counter(stage_no); + callback(SessionPerformanceSection_ntrials_stage); + stages_trial_counter.value = this_stage_trial_counter; + + this_stage_trial_counter_today = value(stages_trial_counter_today); + this_stage_trial_counter_today(stage_no) = this_stage_trial_counter_today(stage_no) + 1; + SessionPerformanceSection_ntrials_stage_today.value = this_stage_trial_counter_today(stage_no); + callback(SessionPerformanceSection_ntrials_stage_today); + stages_trial_counter_today.value = this_stage_trial_counter_today; + + this_stage_timeout_percent = value(stages_timeout_rate); + this_stage_timeout_percent(stage_no) = ((this_stage_timeout_percent(stage_no) * this_stage_trial_counter(stage_no)) + double(timeout_history(end)))/(this_stage_trial_counter(stage_no) + 1); + SessionPerformanceSection_timeout_stage.value = this_stage_timeout_percent(stage_no); + callback(SessionPerformanceSection_timeout_stage); + stages_timeout_rate.value = this_stage_timeout_percent; + + this_stage_violation_percent = value(stages_violation_rate); + this_stage_violation_percent(stage_no) = ((this_stage_violation_percent(stage_no) * this_stage_trial_counter(stage_no)) + double(violation_history(end)))/(this_stage_trial_counter(stage_no) + 1); + SessionPerformanceSection_violation_stage.value = this_stage_violation_percent(stage_no); + callback(SessionPerformanceSection_violation_stage); + stages_violation_rate.value = this_stage_violation_percent; +end % Change the value of CP Duration % Since starting a new session then do a pre warm up to last saved cp @@ -616,6 +629,7 @@ CreateHelperVar(obj,'stages_timeout_rate','value',zeros(1,8)); CreateHelperVar(obj,'stages_violation_rate','value',zeros(1,8)); CreateHelperVar(obj,'last_session_CP','value',0); +CreateHelperVar(obj,'stage_start_completed_trial','value',n_completed_trials,'forceinit',true); % end @@ -637,31 +651,32 @@ stage_no = value(SessionDefinition_CURRENT_ACTIVE_STAGE); ParamsSection_training_stage.value = stage_no; callback(ParamsSection_training_stage); -% Update the helper vars -this_stage_trial_counter = value(stages_trial_counter); -this_stage_trial_counter(stage_no) = this_stage_trial_counter(stage_no) + 1; -SessionPerformanceSection_ntrials_stage.value = this_stage_trial_counter(stage_no); -callback(SessionPerformanceSection_ntrials_stage); -stages_trial_counter.value = this_stage_trial_counter; - -this_stage_trial_counter_today = value(stages_trial_counter_today); -this_stage_trial_counter_today(stage_no) = this_stage_trial_counter_today(stage_no) + 1; -SessionPerformanceSection_ntrials_stage_today.value = this_stage_trial_counter_today(stage_no); -callback(SessionPerformanceSection_ntrials_stage_today); -stages_trial_counter_today.value = this_stage_trial_counter_today; - -this_stage_timeout_percent = value(stages_timeout_rate); -this_stage_timeout_percent(stage_no) = ((this_stage_timeout_percent(stage_no) * this_stage_trial_counter(stage_no)) + double(timeout_history(end)))/(this_stage_trial_counter(stage_no) + 1); -SessionPerformanceSection_timeout_stage.value = this_stage_timeout_percent(stage_no); -callback(SessionPerformanceSection_timeout_stage); -stages_timeout_rate.value = this_stage_timeout_percent; - -this_stage_violation_percent = value(stages_violation_rate); -this_stage_violation_percent(stage_no) = ((this_stage_violation_percent(stage_no) * this_stage_trial_counter(stage_no)) + double(violation_history(end)))/(this_stage_trial_counter(stage_no) + 1); -SessionPerformanceSection_violation_stage.value = this_stage_violation_percent(stage_no); -callback(SessionPerformanceSection_violation_stage); -stages_violation_rate.value = this_stage_violation_percent; - +if n_completed_trials > value(stage_start_completed_trial) + % Update the helper vars + this_stage_trial_counter = value(stages_trial_counter); + this_stage_trial_counter(stage_no) = this_stage_trial_counter(stage_no) + 1; + SessionPerformanceSection_ntrials_stage.value = this_stage_trial_counter(stage_no); + callback(SessionPerformanceSection_ntrials_stage); + stages_trial_counter.value = this_stage_trial_counter; + + this_stage_trial_counter_today = value(stages_trial_counter_today); + this_stage_trial_counter_today(stage_no) = this_stage_trial_counter_today(stage_no) + 1; + SessionPerformanceSection_ntrials_stage_today.value = this_stage_trial_counter_today(stage_no); + callback(SessionPerformanceSection_ntrials_stage_today); + stages_trial_counter_today.value = this_stage_trial_counter_today; + + this_stage_timeout_percent = value(stages_timeout_rate); + this_stage_timeout_percent(stage_no) = ((this_stage_timeout_percent(stage_no) * this_stage_trial_counter(stage_no)) + double(timeout_history(end)))/(this_stage_trial_counter(stage_no) + 1); + SessionPerformanceSection_timeout_stage.value = this_stage_timeout_percent(stage_no); + callback(SessionPerformanceSection_timeout_stage); + stages_timeout_rate.value = this_stage_timeout_percent; + + this_stage_violation_percent = value(stages_violation_rate); + this_stage_violation_percent(stage_no) = ((this_stage_violation_percent(stage_no) * this_stage_trial_counter(stage_no)) + double(violation_history(end)))/(this_stage_trial_counter(stage_no) + 1); + SessionPerformanceSection_violation_stage.value = this_stage_violation_percent(stage_no); + callback(SessionPerformanceSection_violation_stage); + stages_violation_rate.value = this_stage_violation_percent; +end % Warm Up If starting a new session if n_completed_trials == 0 ParamsSection_CP_duration.value = value(ParamsSection_init_CP_duration); @@ -753,6 +768,7 @@ CreateHelperVar(obj,'stages_timeout_rate','value',zeros(1,8)); CreateHelperVar(obj,'stages_violation_rate','value',zeros(1,8)); CreateHelperVar(obj,'last_session_CP','value',0); +CreateHelperVar(obj,'stage_start_completed_trial','value',n_completed_trials,'forceinit',true); % end @@ -785,31 +801,32 @@ stage_no = value(SessionDefinition_CURRENT_ACTIVE_STAGE); ParamsSection_training_stage.value = stage_no; callback(ParamsSection_training_stage); -% Update the helper vars -this_stage_trial_counter = value(stages_trial_counter); -this_stage_trial_counter(stage_no) = this_stage_trial_counter(stage_no) + 1; -SessionPerformanceSection_ntrials_stage.value = this_stage_trial_counter(stage_no); -callback(SessionPerformanceSection_ntrials_stage); -stages_trial_counter.value = this_stage_trial_counter; - -this_stage_trial_counter_today = value(stages_trial_counter_today); -this_stage_trial_counter_today(stage_no) = this_stage_trial_counter_today(stage_no) + 1; -SessionPerformanceSection_ntrials_stage_today.value = this_stage_trial_counter_today(stage_no); -callback(SessionPerformanceSection_ntrials_stage_today); -stages_trial_counter_today.value = this_stage_trial_counter_today; - -this_stage_timeout_percent = value(stages_timeout_rate); -this_stage_timeout_percent(stage_no) = ((this_stage_timeout_percent(stage_no) * this_stage_trial_counter(stage_no)) + double(timeout_history(end)))/(this_stage_trial_counter(stage_no) + 1); -SessionPerformanceSection_timeout_stage.value = this_stage_timeout_percent(stage_no); -callback(SessionPerformanceSection_timeout_stage); -stages_timeout_rate.value = this_stage_timeout_percent; - -this_stage_violation_percent = value(stages_violation_rate); -this_stage_violation_percent(stage_no) = ((this_stage_violation_percent(stage_no) * this_stage_trial_counter(stage_no)) + double(violation_history(end)))/(this_stage_trial_counter(stage_no) + 1); -SessionPerformanceSection_violation_stage.value = this_stage_violation_percent(stage_no); -callback(SessionPerformanceSection_violation_stage); -stages_violation_rate.value = this_stage_violation_percent; - +if n_completed_trials > value(stage_start_completed_trial) + % Update the helper vars + this_stage_trial_counter = value(stages_trial_counter); + this_stage_trial_counter(stage_no) = this_stage_trial_counter(stage_no) + 1; + SessionPerformanceSection_ntrials_stage.value = this_stage_trial_counter(stage_no); + callback(SessionPerformanceSection_ntrials_stage); + stages_trial_counter.value = this_stage_trial_counter; + + this_stage_trial_counter_today = value(stages_trial_counter_today); + this_stage_trial_counter_today(stage_no) = this_stage_trial_counter_today(stage_no) + 1; + SessionPerformanceSection_ntrials_stage_today.value = this_stage_trial_counter_today(stage_no); + callback(SessionPerformanceSection_ntrials_stage_today); + stages_trial_counter_today.value = this_stage_trial_counter_today; + + this_stage_timeout_percent = value(stages_timeout_rate); + this_stage_timeout_percent(stage_no) = ((this_stage_timeout_percent(stage_no) * this_stage_trial_counter(stage_no)) + double(timeout_history(end)))/(this_stage_trial_counter(stage_no) + 1); + SessionPerformanceSection_timeout_stage.value = this_stage_timeout_percent(stage_no); + callback(SessionPerformanceSection_timeout_stage); + stages_timeout_rate.value = this_stage_timeout_percent; + + this_stage_violation_percent = value(stages_violation_rate); + this_stage_violation_percent(stage_no) = ((this_stage_violation_percent(stage_no) * this_stage_trial_counter(stage_no)) + double(violation_history(end)))/(this_stage_trial_counter(stage_no) + 1); + SessionPerformanceSection_violation_stage.value = this_stage_violation_percent(stage_no); + callback(SessionPerformanceSection_violation_stage); + stages_violation_rate.value = this_stage_violation_percent; +end % Warm Up If starting a new session if warm_up_on == 1 if n_completed_trials == 0 @@ -902,6 +919,7 @@ CreateHelperVar(obj,'stages_timeout_rate','value',zeros(1,8)); CreateHelperVar(obj,'stages_violation_rate','value',zeros(1,8)); CreateHelperVar(obj,'last_session_CP','value',0); +CreateHelperVar(obj,'stage_start_completed_trial','value',n_completed_trials,'forceinit',true); % end @@ -930,35 +948,36 @@ a1_time_min = value(ParamsSection_A1_time_Min); a1_time_max = value(ParamsSection_A1_time_Max); - stage_no = value(SessionDefinition_CURRENT_ACTIVE_STAGE); ParamsSection_training_stage.value = stage_no; callback(ParamsSection_training_stage); -% Update the helper vars -this_stage_trial_counter = value(stages_trial_counter); -this_stage_trial_counter(stage_no) = this_stage_trial_counter(stage_no) + 1; -SessionPerformanceSection_ntrials_stage.value = this_stage_trial_counter(stage_no); -callback(SessionPerformanceSection_ntrials_stage); -stages_trial_counter.value = this_stage_trial_counter; - -this_stage_trial_counter_today = value(stages_trial_counter_today); -this_stage_trial_counter_today(stage_no) = this_stage_trial_counter_today(stage_no) + 1; -SessionPerformanceSection_ntrials_stage_today.value = this_stage_trial_counter_today(stage_no); -callback(SessionPerformanceSection_ntrials_stage_today); -stages_trial_counter_today.value = this_stage_trial_counter_today; - -this_stage_timeout_percent = value(stages_timeout_rate); -this_stage_timeout_percent(stage_no) = ((this_stage_timeout_percent(stage_no) * this_stage_trial_counter(stage_no)) + double(timeout_history(end)))/(this_stage_trial_counter(stage_no) + 1); -SessionPerformanceSection_timeout_stage.value = this_stage_timeout_percent(stage_no); -callback(SessionPerformanceSection_timeout_stage); -stages_timeout_rate.value = this_stage_timeout_percent; - -this_stage_violation_percent = value(stages_violation_rate); -this_stage_violation_percent(stage_no) = ((this_stage_violation_percent(stage_no) * this_stage_trial_counter(stage_no)) + double(violation_history(end)))/(this_stage_trial_counter(stage_no) + 1); -SessionPerformanceSection_violation_stage.value = this_stage_violation_percent(stage_no); -callback(SessionPerformanceSection_violation_stage); -stages_violation_rate.value = this_stage_violation_percent; +if n_completed_trials > value(stage_start_completed_trial) + % Update the helper vars + this_stage_trial_counter = value(stages_trial_counter); + this_stage_trial_counter(stage_no) = this_stage_trial_counter(stage_no) + 1; + SessionPerformanceSection_ntrials_stage.value = this_stage_trial_counter(stage_no); + callback(SessionPerformanceSection_ntrials_stage); + stages_trial_counter.value = this_stage_trial_counter; + + this_stage_trial_counter_today = value(stages_trial_counter_today); + this_stage_trial_counter_today(stage_no) = this_stage_trial_counter_today(stage_no) + 1; + SessionPerformanceSection_ntrials_stage_today.value = this_stage_trial_counter_today(stage_no); + callback(SessionPerformanceSection_ntrials_stage_today); + stages_trial_counter_today.value = this_stage_trial_counter_today; + + this_stage_timeout_percent = value(stages_timeout_rate); + this_stage_timeout_percent(stage_no) = ((this_stage_timeout_percent(stage_no) * this_stage_trial_counter(stage_no)) + double(timeout_history(end)))/(this_stage_trial_counter(stage_no) + 1); + SessionPerformanceSection_timeout_stage.value = this_stage_timeout_percent(stage_no); + callback(SessionPerformanceSection_timeout_stage); + stages_timeout_rate.value = this_stage_timeout_percent; + + this_stage_violation_percent = value(stages_violation_rate); + this_stage_violation_percent(stage_no) = ((this_stage_violation_percent(stage_no) * this_stage_trial_counter(stage_no)) + double(violation_history(end)))/(this_stage_trial_counter(stage_no) + 1); + SessionPerformanceSection_violation_stage.value = this_stage_violation_percent(stage_no); + callback(SessionPerformanceSection_violation_stage); + stages_violation_rate.value = this_stage_violation_percent; +end % Warm Up If starting a new session if warm_up_on == 1 if n_completed_trials == 0 From ba4e871be653e8da86b555ec9bcc52a8cd8b5b92 Mon Sep 17 00:00:00 2001 From: viktorpm Date: Mon, 31 Mar 2025 13:45:17 +0100 Subject: [PATCH 040/164] error debug during trial run --- .../Arpit_CentrePokeTraining.m | 18 +++-- .../Arpit_CentrePokeTrainingSMA.m | 2 +- ...essionDefinition_AutomatedTrainingStages.m | 72 ++++++++++++------- .../@Arpit_CentrePokeTraining/ParamsSection.m | 16 ++--- .../@Arpit_CentrePokeTraining/SoundSection.m | 31 ++++---- .../StimulusSection.m | 34 +++++---- 6 files changed, 100 insertions(+), 73 deletions(-) diff --git a/Protocols/@Arpit_CentrePokeTraining/Arpit_CentrePokeTraining.m b/Protocols/@Arpit_CentrePokeTraining/Arpit_CentrePokeTraining.m index f6bdc08e..ad888ccf 100644 --- a/Protocols/@Arpit_CentrePokeTraining/Arpit_CentrePokeTraining.m +++ b/Protocols/@Arpit_CentrePokeTraining/Arpit_CentrePokeTraining.m @@ -113,32 +113,30 @@ struct('states', my_state_colors, 'pokes', my_poke_colors)); next_row(y); [x, y] = CommentsSection(obj, 'init', x, y); - % [x, y] = PunishmentSection(obj, 'init', x, y); %#ok next_column(x); y=5; [x, y] = SessionPerformanceSection(obj, 'init', x, y); [x, y] = ParamsSection(obj, 'init', x, y); %#ok - [x, y] = SoundSection(obj,'init',x,y); - [x, y] = StimulusSection(obj,'init',x,y); + + [x, y] = SoundSection(obj,'init',x,y); [stage_fig_x,stage_fig_y] = Training_ParamsSection(obj, 'init', x, y); - SoloParamHandle(obj, 'stage_fig_x', 'value', stage_fig_x); SoloFunctionAddVars('ParamsSection', 'rw_args', 'stage_fig_x'); - SoloParamHandle(obj, 'stage_fig_y', 'value', stage_fig_y); SoloFunctionAddVars('ParamsSection', 'rw_args', 'stage_fig_y'); - - + figpos = get(double(gcf), 'Position'); [expmtr, rname]=SavingSection(obj, 'get_info'); HeaderParam(obj, 'prot_title', [mfilename ': ' expmtr ', ' rname], x, y, 'position', [10 figpos(4)-25, 800 20]); + + [x, y] = StimulusSection(obj,'init',x,y); Arpit_CentrePokeTrainingSMA(obj, 'init'); - + + next_row(y); next_row(y); SessionDefinition(obj, 'init', x, y, value(myfig)); next_row(y, 2); %#ok - % SessionDefinition(obj, 'set_old_style_parsing_flag',0); - + feval(mfilename, obj, 'prepare_next_trial'); %% change_water_modulation_params diff --git a/Protocols/@Arpit_CentrePokeTraining/Arpit_CentrePokeTrainingSMA.m b/Protocols/@Arpit_CentrePokeTraining/Arpit_CentrePokeTrainingSMA.m index b752f707..67399b74 100644 --- a/Protocols/@Arpit_CentrePokeTraining/Arpit_CentrePokeTrainingSMA.m +++ b/Protocols/@Arpit_CentrePokeTraining/Arpit_CentrePokeTrainingSMA.m @@ -92,7 +92,7 @@ sma = StateMachineAssembler('full_trial_structure','use_happenings', 1); % scheduled wave for stimuli/fixed sound, could be based upon side - if stimuli_on + if stimuli_on && value(training_stage) > 4 sma = add_scheduled_wave(sma, 'name', 'stimplay', 'preamble', PreStim_time, ... 'sustain', sound_duration, 'sound_trig', A1_sound_id); % to play a sound before Go Cue else diff --git a/Protocols/@Arpit_CentrePokeTraining/Arpit_CentrePokeTraining_SessionDefinition_AutomatedTrainingStages.m b/Protocols/@Arpit_CentrePokeTraining/Arpit_CentrePokeTraining_SessionDefinition_AutomatedTrainingStages.m index 61f5667f..831a9d1d 100644 --- a/Protocols/@Arpit_CentrePokeTraining/Arpit_CentrePokeTraining_SessionDefinition_AutomatedTrainingStages.m +++ b/Protocols/@Arpit_CentrePokeTraining/Arpit_CentrePokeTraining_SessionDefinition_AutomatedTrainingStages.m @@ -30,7 +30,7 @@ CreateHelperVar(obj,'stages_trial_counter_oppSide','value',zeros(1,8)); CreateHelperVar(obj,'stages_timeout_rate','value',zeros(1,8)); CreateHelperVar(obj,'stages_violation_rate','value',zeros(1,8)); -CreateHelperVar(obj,'stage_start_completed_trial','value',n_completed_trials,'forceinit',true); +CreateHelperVar(obj,'stage_start_completed_trial','value',n_completed_trials,'force_init',true); % end @@ -44,8 +44,8 @@ ParamsSection_MaxSame.value = 4; callback(ParamsSection_MaxSame); stage_no = value(SessionDefinition_CURRENT_ACTIVE_STAGE); -ParamsSection_training_stage.value = stage_no; -callback(ParamsSection_training_stage); +% ParamsSection_training_stage.value = stage_no; +% callback(ParamsSection_training_stage); if n_completed_trials > value(stage_start_completed_trial) this_stage_trial_counter = value(stages_trial_counter); this_stage_trial_counter(stage_no) = this_stage_trial_counter(stage_no) + 1; @@ -78,6 +78,9 @@ % only run it if its the start of the day, number of trials is small if n_completed_trials < 100 if this_stage_trial_counter(stage_no) > value(Training_ParamsSection_total_trials) && this_stage_trial_counter_oppSide(stage_no) > value(Training_ParamsSection_total_trials_opp) + ParamsSection_training_stage.value = stage_no + 1; + callback(ParamsSection_training_stage); + ParamsSection(obj, 'Changed_Training_Stage'); SessionDefinition(obj, 'jump_to_stage', 'Timeout Rewarded Side Pokes'); end end @@ -99,7 +102,10 @@ this_stage_trial_counter = value(stages_trial_counter); stages_trial_counter_today.value = zeros(1,8); if this_stage_trial_counter(stage_no) > value(Training_ParamsSection_total_trials) && this_stage_trial_counter_oppSide(stage_no) > value(Training_ParamsSection_total_trials_opp) - SessionDefinition(obj, 'jump_to_stage', 'Timeout Rewarded Side Pokes'); + ParamsSection_training_stage.value = stage_no + 1; + callback(ParamsSection_training_stage); + ParamsSection(obj, 'Changed_Training_Stage'); + SessionDefinition(obj, 'jump_to_stage', 'Timeout Rewarded Side Pokes'); end % end @@ -126,8 +132,8 @@ CreateHelperVar(obj,'stages_trial_counter_oppSide','value',zeros(1,8)); CreateHelperVar(obj,'stages_timeout_rate','value',zeros(1,8)); CreateHelperVar(obj,'stages_violation_rate','value',zeros(1,8)); -CreateHelperVar(obj,'this_stage_opp_side_trials', 'value', '0', 'forceinit','true'); -CreateHelperVar(obj,'stage_start_completed_trial','value',n_completed_trials,'forceinit',true); +CreateHelperVar(obj,'this_stage_opp_side_trials', 'value', '0', 'force_init',true); +CreateHelperVar(obj,'stage_start_completed_trial','value',n_completed_trials,'force_init',true); % end @@ -140,8 +146,8 @@ ParamsSection_MaxSame.value = 3; callback(ParamsSection_MaxSame); stage_no = value(SessionDefinition_CURRENT_ACTIVE_STAGE); -ParamsSection_training_stage.value = stage_no; -callback(ParamsSection_training_stage); +% ParamsSection_training_stage.value = stage_no; +% callback(ParamsSection_training_stage); if n_completed_trials > value(stage_start_completed_trial) % Update the helper vars this_stage_trial_counter = value(stages_trial_counter); @@ -206,6 +212,9 @@ % only run it if its the start of the day, number of trials is small if n_completed_trials > 50 if this_stage_trial_counter(stage_no) > value(Training_ParamsSection_total_trials) && this_stage_trial_counter_oppSide(stage_no) > value(Training_ParamsSection_total_trials_opp) + ParamsSection_training_stage.value = stage_no + 1; + callback(ParamsSection_training_stage); + ParamsSection(obj, 'Changed_Training_Stage'); SessionDefinition(obj, 'jump_to_stage', 'Introduce Centre Poke'); end end @@ -244,7 +253,7 @@ CreateHelperVar(obj,'stages_timeout_rate','value',zeros(1,8)); CreateHelperVar(obj,'stages_violation_rate','value',zeros(1,8)); CreateHelperVar(obj,'last_session_CP','value',0); -CreateHelperVar(obj,'stage_start_completed_trial','value',n_completed_trials,'forceinit',true); +CreateHelperVar(obj,'stage_start_completed_trial','value',n_completed_trials,'force_init',true); % end @@ -262,8 +271,8 @@ callback(ParamsSection_MaxSame); stage_no = value(SessionDefinition_CURRENT_ACTIVE_STAGE); -ParamsSection_training_stage.value = stage_no; -callback(ParamsSection_training_stage); +% ParamsSection_training_stage.value = stage_no; +% callback(ParamsSection_training_stage); if n_completed_trials > value(stage_start_completed_trial) % Update the helper vars this_stage_trial_counter = value(stages_trial_counter); @@ -308,6 +317,9 @@ % cp_max = value(ParamsSection_SettlingIn_time) + value(ParamsSection_legal_cbreak); if value(ParamsSection_CP_duration) >= cp_max + ParamsSection_training_stage.value = 4; + callback(ParamsSection_training_stage); + ParamsSection(obj, 'Changed_Training_Stage'); SessionDefinition(obj, 'jump_to_stage', 'Introduce Violation for Centre Poke'); end % @@ -345,7 +357,7 @@ CreateHelperVar(obj,'stages_timeout_rate','value',zeros(1,8)); CreateHelperVar(obj,'stages_violation_rate','value',zeros(1,8)); CreateHelperVar(obj,'last_session_CP','value',0); -CreateHelperVar(obj,'stage_start_completed_trial','value',n_completed_trials,'forceinit',true); +CreateHelperVar(obj,'stage_start_completed_trial','value',n_completed_trials,'force_init',true); % end @@ -365,8 +377,8 @@ cp_minimum_increment = 0.001; stage_no = value(SessionDefinition_CURRENT_ACTIVE_STAGE); -ParamsSection_training_stage.value = stage_no; -callback(ParamsSection_training_stage); +% ParamsSection_training_stage.value = stage_no; +% callback(ParamsSection_training_stage); if n_completed_trials > value(stage_start_completed_trial) % Update the helper vars this_stage_trial_counter = value(stages_trial_counter); @@ -423,6 +435,9 @@ if value(ParamsSection_CP_duration) >= cp_max && n_completed_trials > 100 if SessionPerformanceSection_violation_recent < value(Training_ParamsSection_recent_violation) && SessionPerformanceSection_timeout_recent < value(Training_ParamsSection_recent_timeout) && ... SessionPerformanceSection_violation_stage < value(Training_ParamsSection_stage_violation) + ParamsSection_training_stage.value = 5; + callback(ParamsSection_training_stage); + ParamsSection(obj, 'Changed_Training_Stage'); SessionDefinition(obj, 'jump_to_stage', 'Introduce Stimuli Sound during Centre Poke'); last_session_CP.value = value(ParamsSection_CP_duration); end @@ -465,7 +480,7 @@ CreateHelperVar(obj,'stages_timeout_rate','value',zeros(1,8)); CreateHelperVar(obj,'stages_violation_rate','value',zeros(1,8)); CreateHelperVar(obj,'last_session_CP','value',0); -CreateHelperVar(obj,'stage_start_completed_trial','value',n_completed_trials,'forceinit',true); +CreateHelperVar(obj,'stage_start_completed_trial','value',n_completed_trials,'force_init',true); % end @@ -486,8 +501,8 @@ n_trial_warmup = value(Training_ParamsSection_warm_up_trials); stage_no = value(SessionDefinition_CURRENT_ACTIVE_STAGE); -ParamsSection_training_stage.value = stage_no; -callback(ParamsSection_training_stage); +% ParamsSection_training_stage.value = stage_no; +% callback(ParamsSection_training_stage); if n_completed_trials > value(stage_start_completed_trial) % Update the helper vars this_stage_trial_counter = value(stages_trial_counter); @@ -588,6 +603,9 @@ if value(ParamsSection_CP_duration) >= value(Training_ParamsSection_max_CP) && this_stage_trial_counter(stage_no) > value(Training_ParamsSection_total_trials) if SessionPerformanceSection_violation_recent < value(Training_ParamsSection_recent_violation) && SessionPerformanceSection_timeout_recent < value(Training_ParamsSection_recent_timeout) && ... SessionPerformanceSection_violation_stage < value(Training_ParamsSection_stage_violation) && n_completed_trials > 100 + ParamsSection_training_stage.value = stage_no + 1; + callback(ParamsSection_training_stage); + ParamsSection(obj, 'Changed_Training_Stage'); SessionDefinition(obj, 'jump_to_stage', 'Vary Stimuli location during Centre Poke'); last_session_CP.value = value(ParamsSection_CP_duration); end @@ -629,7 +647,7 @@ CreateHelperVar(obj,'stages_timeout_rate','value',zeros(1,8)); CreateHelperVar(obj,'stages_violation_rate','value',zeros(1,8)); CreateHelperVar(obj,'last_session_CP','value',0); -CreateHelperVar(obj,'stage_start_completed_trial','value',n_completed_trials,'forceinit',true); +CreateHelperVar(obj,'stage_start_completed_trial','value',n_completed_trials,'force_init',true); % end @@ -649,8 +667,8 @@ stim_dur = value(Training_ParamsSection_stim_dur); stage_no = value(SessionDefinition_CURRENT_ACTIVE_STAGE); -ParamsSection_training_stage.value = stage_no; -callback(ParamsSection_training_stage); +% ParamsSection_training_stage.value = stage_no; +% callback(ParamsSection_training_stage); if n_completed_trials > value(stage_start_completed_trial) % Update the helper vars this_stage_trial_counter = value(stages_trial_counter); @@ -730,6 +748,9 @@ if this_stage_trial_counter(stage_no) > value(Training_ParamsSection_total_trials) if SessionPerformanceSection_violation_recent < value(Training_ParamsSection_recent_violation) && SessionPerformanceSection_timeout_recent < value(Training_ParamsSection_recent_timeout) && ... SessionPerformanceSection_violation_stage < value(Training_ParamsSection_stage_violation) && n_completed_trials > 100 + ParamsSection_training_stage.value = stage_no + 1; + callback(ParamsSection_training_stage); + ParamsSection(obj, 'Changed_Training_Stage'); SessionDefinition(obj, 'jump_to_stage', 'Variable Stimuli Go Cue location during Centre Poke'); end end @@ -768,7 +789,7 @@ CreateHelperVar(obj,'stages_timeout_rate','value',zeros(1,8)); CreateHelperVar(obj,'stages_violation_rate','value',zeros(1,8)); CreateHelperVar(obj,'last_session_CP','value',0); -CreateHelperVar(obj,'stage_start_completed_trial','value',n_completed_trials,'forceinit',true); +CreateHelperVar(obj,'stage_start_completed_trial','value',n_completed_trials,'force_init',true); % end @@ -799,8 +820,8 @@ random_A1 = 0; stage_no = value(SessionDefinition_CURRENT_ACTIVE_STAGE); -ParamsSection_training_stage.value = stage_no; -callback(ParamsSection_training_stage); +% ParamsSection_training_stage.value = stage_no; +% callback(ParamsSection_training_stage); if n_completed_trials > value(stage_start_completed_trial) % Update the helper vars this_stage_trial_counter = value(stages_trial_counter); @@ -881,6 +902,9 @@ if this_stage_trial_counter(stage_no) > value(Training_ParamsSection_total_trials) if SessionPerformanceSection_violation_recent < value(Training_ParamsSection_recent_violation) && SessionPerformanceSection_timeout_recent < value(Training_ParamsSection_recent_timeout) && ... SessionPerformanceSection_violation_stage < value(Training_ParamsSection_stage_violation) + ParamsSection_training_stage.value = stage_no + 1; + callback(ParamsSection_training_stage); + ParamsSection(obj, 'Changed_Training_Stage'); SessionDefinition(obj, 'jump_to_stage', 'User Setting'); end end @@ -919,7 +943,7 @@ CreateHelperVar(obj,'stages_timeout_rate','value',zeros(1,8)); CreateHelperVar(obj,'stages_violation_rate','value',zeros(1,8)); CreateHelperVar(obj,'last_session_CP','value',0); -CreateHelperVar(obj,'stage_start_completed_trial','value',n_completed_trials,'forceinit',true); +CreateHelperVar(obj,'stage_start_completed_trial','value',n_completed_trials,'force_init',true); % end diff --git a/Protocols/@Arpit_CentrePokeTraining/ParamsSection.m b/Protocols/@Arpit_CentrePokeTraining/ParamsSection.m index 6d6e45c4..6414cd7a 100644 --- a/Protocols/@Arpit_CentrePokeTraining/ParamsSection.m +++ b/Protocols/@Arpit_CentrePokeTraining/ParamsSection.m @@ -95,7 +95,7 @@ ToggleParam(obj, 'stimuli_on', 0, x,y,... 'OnString', 'Use Stimuli',... 'OffString', 'Fixed Sound',... - 'TooltipString', sprintf('If on (black) then it enables training with stimuli else a fixed sound')); + 'TooltipString', sprintf('If on (black) then it enables training with stimuli else using a fixed sound from Stage 5')); next_row(y); ToggleParam(obj, 'warmup_on', 1, x,y,... 'OnString', 'Warmup ON',... @@ -169,8 +169,9 @@ {'training_stage'}); SoloFunctionAddVars('StimulusSection', 'ro_args', ... - {'training_stage','stimuli_on'}); - + {'training_stage';'stimuli_on';'ThisTrial';'A1_time';... + 'time_bet_aud1_gocue' ;'PreStim_time'}); + SoloFunctionAddVars('Training_ParamsSection', 'ro_args', ... {'training_stage'}); @@ -209,14 +210,12 @@ case 'Changed_Training_Stage' if value(use_auto_train) == 1 - - disable(training_stage); % user cannot change the training stages - disable(PreStim_time); - disable(time_bet_aud1_gocue); + disable(training_stage); % user cannot change the training stages else - enable(training_stage); % user can change the training stages SessionDefinition(obj, 'jump_to_stage',value(training_stage)); + end + [stage_fig_x,stage_fig_y] = Training_ParamsSection(obj, 'reinit', value(stage_fig_x),value(stage_fig_y)); % update the training params as well Arpit_CentrePokeTrainingSMA(obj,'reinit'); @@ -273,7 +272,6 @@ end Total_CP_duration.value = value(CP_duration) + value(time_go_cue); %#ok<*NASGU> end - end case 'prepare_next_trial' diff --git a/Protocols/@Arpit_CentrePokeTraining/SoundSection.m b/Protocols/@Arpit_CentrePokeTraining/SoundSection.m index 5ecb8231..fcb2a535 100644 --- a/Protocols/@Arpit_CentrePokeTraining/SoundSection.m +++ b/Protocols/@Arpit_CentrePokeTraining/SoundSection.m @@ -4,16 +4,16 @@ GetSoloFunctionArgs(obj); -switch action, +switch action % ------------------------------------------------------------------ % INIT % ------------------------------------------------------------------ case 'init' - if length(varargin) < 2, + if length(varargin) < 2 error('Need at least two arguments, x and y position, to initialize %s', mfilename); - end; + end x = varargin{1}; y = varargin{2}; ToggleParam(obj, 'SoundsShow', 0, x, y, 'OnString', 'Sounds', ... @@ -37,23 +37,26 @@ y=10; [x,y]=SoundInterface(obj,'add','GoSound',x,y,'Style','Tone','Volume',0.005,'Freq',3000,'Duration',0.2); SoundInterface(obj, 'disable', 'GoSound', 'Dur1'); - SoundInterface(obj, 'disable', 'GoSound', 'Freq1'); [x,y]=SoundInterface(obj,'add','SOneSound',x,y,'Style','Tone','Volume',0.005,'Freq',3000,'Duration',0.2); [x,y]=SoundInterface(obj,'add','STwoSound',x,y,'Style','WhiteNoise','Volume',0.08); x=oldx; y=oldy; figure(parentfig); - case 'hide', - SoundsShow.value = 0; set(value(myfig), 'Visible', 'off'); - - case 'show', - SoundsShow.value = 1; set(value(myfig), 'Visible', 'on'); - - case 'show_hide', - if SoundsShow == 1, set(value(myfig), 'Visible', 'on'); %#ok (defined by GetSoloFunctionArgs) - else set(value(myfig), 'Visible', 'off'); - end; + case 'hide' + SoundsShow.value = 0; + set(value(myfig), 'Visible', 'off'); + + case 'show' + SoundsShow.value = 1; + set(value(myfig), 'Visible', 'on'); + + case 'show_hide' + if SoundsShow == 1 + set(value(myfig), 'Visible', 'on'); % (defined by GetSoloFunctionArgs) + else + set(value(myfig), 'Visible', 'off'); + end end \ No newline at end of file diff --git a/Protocols/@Arpit_CentrePokeTraining/StimulusSection.m b/Protocols/@Arpit_CentrePokeTraining/StimulusSection.m index 29e6051c..eb9f2b18 100644 --- a/Protocols/@Arpit_CentrePokeTraining/StimulusSection.m +++ b/Protocols/@Arpit_CentrePokeTraining/StimulusSection.m @@ -16,8 +16,8 @@ end x = varargin{1}; y = varargin{2}; - ToggleParam(obj, 'StimulusShow', 0, x, y, 'OnString', 'Stimuli', ... - 'OffString', 'Stimuli', 'TooltipString', 'Show/Hide Stimulus panel'); + ToggleParam(obj, 'StimulusShow', 1, x, y, 'OnString', 'Stimuli Show', ... + 'OffString', 'Stimuli Hidden', 'TooltipString', 'Show/Hide Stimulus panel'); set_callback(StimulusShow, {mfilename, 'show_hide'}); %#ok (Defined just above) next_row(y); @@ -34,11 +34,11 @@ set(value(ax),'Fontsize',15) xlabel('Sound Categorization','FontSize',16,'FontName','Cambria Math') - SoloParamHandle(obj, 'axperf', 'saveable', 0, ... - 'value', axes('Position', [0.5 0.5 0.45 0.45])); - ylabel('log_e A','FontSize',16,'FontName','Cambria Math'); - set(value(axperf),'Fontsize',15) - xlabel('Sound Categorization','FontSize',16,'FontName','Cambria Math') + % SoloParamHandle(obj, 'axperf', 'saveable', 0, ... + % 'value', axes('Position', [0.5 0.5 0.45 0.45])); + % ylabel('log_e A','FontSize',16,'FontName','Cambria Math'); + % set(value(axperf),'Fontsize',15) + % xlabel('Sound Categorization','FontSize',16,'FontName','Cambria Math') SoundManagerSection(obj, 'declare_new_sound', 'StimAUD1') SoloParamHandle(obj, 'thisstim', 'value', []); @@ -63,35 +63,36 @@ next_column(x); y=5; + next_row(y, 1) MenuParam(obj, 'filter_type', {'GAUS','LPFIR', 'FIRLS','BUTTER','MOVAVRG','KAISER','EQUIRIP','HAMMING'}, ... 'GAUS', x, y, 'labelfraction', 0.35, 'TooltipString', sprintf(['\nDifferent filters. ''LPFIR'': lowpass FIR ''FIRLS'': Least square linear-phase FIR filter design\n', ... '\n''BUTTER'': IIR Butterworth lowpass filter ''GAUS'': Gaussian filter (window)\n', ... '\n''MOVAVRG'': Moving average FIR filter ''KAISER'': Kaiser-window FIR filtering\n', ... '\n''EQUIRIP'':Eqiripple FIR filter ''HAMMING'': Hamming-window based FIR'])); - next_row(y, 1) + next_row(y); NumeditParam(obj,'fcut',110,x,y,'label','fcut','TooltipString','Cut off frequency on the original white noise'); next_row(y); NumeditParam(obj,'lfreq',2000,x,y,'label','Modulator_LowFreq','TooltipString','Lower bound for the frequency modulator'); next_row(y); NumeditParam(obj,'hfreq',20000,x,y,'label','Modulator_HighFreq','TooltipString','Upper bound for the frequency modulator'); next_row(y); - DispParam(obj, 'A1_sigma', 0.01, x,y,'label','A1_sigma','TooltipString','Sigma value for the first stimulus'); - next_row(y); - DispParam(obj, 'A1_freq', 0.01, x,y,'label','A1_freq','TooltipString','Sigma value for the first stimulus'); - next_row(y); - NumeditParam(obj,'minS1',0.007,x,y,'label','minS1','TooltipString','min sigma value for AUD1'); + NumeditParam(obj,'minS1',0.007,x,y,'label','minS1','TooltipString','min sigma value for AUD1'); set_callback(minS1, {mfilename, 'Cal_Boundary'}); next_row(y); NumeditParam(obj,'maxS1',0.05,x,y,'label','maxS1','TooltipString','max sigma value for AUD1'); set_callback(maxS1, {mfilename, 'Cal_Boundary'}); next_row(y); + DispParam(obj, 'A1_sigma', 0.01, x,y,'label','A1_sigma','TooltipString','Sigma value for the first stimulus'); + next_row(y); NumeditParam(obj,'minF1',4,x,y,'label','minF1','TooltipString','min frequency value for AUD1'); set_callback(minF1, {mfilename, 'Cal_Boundary'}); next_row(y); NumeditParam(obj,'maxF1',10,x,y,'label','maxF1','TooltipString','max frequency value for AUD1'); set_callback(maxF1, {mfilename, 'Cal_Boundary'}); - next_row(y); - DispParam(obj,'boundary',-3.9,x,y,'label','boundary','TooltipString','decision boundary for categorisation (log)'); + next_row(y); + DispParam(obj, 'A1_freq', 0.01, x,y,'label','A1_freq','TooltipString','Sigma value for the first stimulus'); + next_row(y); + DispParam(obj,'boundary',-3.9,x,y,'label','boundary(log)','TooltipString','decision boundary for categorisation (log)'); next_row(y); MenuParam(obj, 'mu_location', {'center', 'side'}, ... 'center', x, y, 'labelfraction', 0.35, 'TooltipString', sprintf('\nLocation of boundary')); @@ -103,6 +104,7 @@ 'TooltipString', sprintf('If on (black) then it enables the presentation of pure tones')); set_callback(frequency_categorization, {mfilename, 'Cal_Boundary'}); set_callback(frequency_categorization, {mfilename, 'FrequencyCategorization'}); + make_invisible(maxF1);make_invisible(minF1);make_invisible(A1_freq); next_row(y); MenuParam(obj, 'DistributionType', {'uniform','unim-unif', 'unif-unim', 'unimodal', 'bimodal', 'asym-unif', 'Unim-Unif', 'Unif-Unim'}, ... 'uniform', x, y, 'labelfraction', 0.35, 'TooltipString', sprintf('\nDifferent distributions')); @@ -381,9 +383,11 @@ if frequency_categorization == 1 make_visible(maxF1);make_visible(minF1);make_visible(A1_freq); make_invisible(maxS1);make_invisible(minS1);make_invisible(A1_sigma); + make_invisible(fcut);make_invisible(lfreq);make_invisible(hfreq); make_invisible(filter_type); StimulusSection(obj,'plot_stimuli'); else make_visible(maxS1);make_visible(minS1);make_visible(A1_sigma); + make_visible(fcut);make_visible(lfreq);make_visible(hfreq); make_visible(filter_type); make_invisible(maxF1);make_invisible(minF1);make_invisible(A1_freq); StimulusSection(obj,'plot_stimuli'); end From 12beb29d1e0d882fc97f1756d2b020203058343f Mon Sep 17 00:00:00 2001 From: Viktor Plattner Date: Mon, 31 Mar 2025 14:05:23 +0100 Subject: [PATCH 041/164] Add pull request template --- .github/PULL_REQUEST_TEMPLATE.md | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 .github/PULL_REQUEST_TEMPLATE.md diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 00000000..c960675b --- /dev/null +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,24 @@ +### Description + +Please include a summary of the changes made. Include any relevant context, screenshots, or details. + +### Type of Change + +- [ ] Bug fix +- [ ] New feature +- [ ] Documentation update +- [ ] Breaking change +- [ ] Other (please specify): + +### Checklist + +- [ ] I have tested the changes locally. +- [ ] I have updated the documentation accordingly. +- [ ] I have added necessary tests to cover the changes. +- [ ] I have followed the code style guidelines. +- [ ] I have verified that the code works in all target environments. + +### Related Issues + +Closes # (issue number) + From 88e08abbd1f34179253d20717bc15fdd55d81107 Mon Sep 17 00:00:00 2001 From: Arpit Date: Mon, 31 Mar 2025 15:22:43 +0100 Subject: [PATCH 042/164] minor fix with file name changes --- .../Arpit_CentrePokeTrainingSMA.m | 39 +- ...ng_SessionDefinition_AutoTrainingStages.m} | 2 +- .../PunishmentSection.m | 133 -- .../RewardsSection.m | 237 ---- ...okeTraining_experimenter_ratname_250319c.m | 1150 ----------------- 5 files changed, 21 insertions(+), 1540 deletions(-) rename Protocols/@Arpit_CentrePokeTraining/{Arpit_CentrePokeTraining_SessionDefinition_AutomatedTrainingStages.m => Arpit_CentrePokeTraining_SessionDefinition_AutoTrainingStages.m} (99%) delete mode 100644 Protocols/@Arpit_CentrePokeTraining/PunishmentSection.m delete mode 100644 Protocols/@Arpit_CentrePokeTraining/RewardsSection.m delete mode 100644 Protocols/@Arpit_CentrePokeTraining/pipeline_Arpit_CentrePokeTraining_experimenter_ratname_250319c.m diff --git a/Protocols/@Arpit_CentrePokeTraining/Arpit_CentrePokeTrainingSMA.m b/Protocols/@Arpit_CentrePokeTraining/Arpit_CentrePokeTrainingSMA.m index 67399b74..d762923e 100644 --- a/Protocols/@Arpit_CentrePokeTraining/Arpit_CentrePokeTrainingSMA.m +++ b/Protocols/@Arpit_CentrePokeTraining/Arpit_CentrePokeTrainingSMA.m @@ -33,10 +33,11 @@ %% Stage 6: Continue Centre Poke Training with variable pre-stim time % After reaching the max CP, vary the pre stim value and fixing the total CP. -%% Stage 7: Continue Centre Poke Training with variable pre-stim time and variable delay time (both within a range). -% This variable time is introduced so that rat doesn't learn the expectancy +%% Stage 7: Continue Centre Poke Training with variable pre-stim time and variable delay time (both within a range of [0.2 - 2 secs]). +% This variable time is introduced so that rat doesn't learn the % time between each of the parameters +%% Stage 8: Same as previous Stage but now using user defined values. GetSoloFunctionArgs; @@ -91,7 +92,7 @@ sma = StateMachineAssembler('full_trial_structure','use_happenings', 1); - % scheduled wave for stimuli/fixed sound, could be based upon side + % scheduled wave for stimuli/fixed sound, based upon side if stimuli_on && value(training_stage) > 4 sma = add_scheduled_wave(sma, 'name', 'stimplay', 'preamble', PreStim_time, ... 'sustain', sound_duration, 'sound_trig', A1_sound_id); % to play a sound before Go Cue @@ -122,30 +123,30 @@ case 1 % LEARNING THE REWARD SOUND ASSOCIATION -LEFT OR RIGHT LED ON -> POKE -> SOUND+REWARD GIVEN % INFINITE TIME AND CHANCES TO SELF CORRECT - sma = add_state(sma, 'name', 'side_led_wait_RewardCollection', 'self_timer', 2, ... - 'output_actions', {'DOut', SideLight}, ... - 'input_to_statechange',{'Tup','hit_state'}); - - sma = add_state(sma,'name','second_hit_state','self_timer',3,... - 'output_actions',{'DOut', SideLight},... - 'input_to_statechange',{'Tup','hit_state'}); - - sma = add_state(sma,'name','hit_state','self_timer',0.01,... - 'output_actions', {'SchedWaveTrig','reward_delivery+Go_Cue'},... - 'input_to_statechange',{'Tup','drink_state'}); - - % sma = add_state(sma, 'name', 'side_led_wait_RewardCollection', 'self_timer', SideLed_duration + RewardCollection_duration, ... + % sma = add_state(sma, 'name', 'side_led_wait_RewardCollection', 'self_timer', 2, ... % 'output_actions', {'DOut', SideLight}, ... - % 'input_to_statechange',{HitEvent,'hit_state';'Tup','side_led_wait_RewardCollection'; ErrorEvent,'second_hit_state'}); + % 'input_to_statechange',{'Tup','hit_state'}); % - % sma = add_state(sma,'name','second_hit_state','self_timer',RewardCollection_duration,... + % sma = add_state(sma,'name','second_hit_state','self_timer',3,... % 'output_actions',{'DOut', SideLight},... - % 'input_to_statechange',{'Tup','second_hit_state'; HitEvent,'hit_state'}); + % 'input_to_statechange',{'Tup','hit_state'}); % % sma = add_state(sma,'name','hit_state','self_timer',0.01,... % 'output_actions', {'SchedWaveTrig','reward_delivery+Go_Cue'},... % 'input_to_statechange',{'Tup','drink_state'}); + sma = add_state(sma, 'name', 'side_led_wait_RewardCollection', 'self_timer', SideLed_duration + RewardCollection_duration, ... + 'output_actions', {'DOut', SideLight}, ... + 'input_to_statechange',{HitEvent,'hit_state';'Tup','side_led_wait_RewardCollection'; ErrorEvent,'second_hit_state'}); + + sma = add_state(sma,'name','second_hit_state','self_timer',RewardCollection_duration,... + 'output_actions',{'DOut', SideLight},... + 'input_to_statechange',{'Tup','second_hit_state'; HitEvent,'hit_state'}); + + sma = add_state(sma,'name','hit_state','self_timer',0.01,... + 'output_actions', {'SchedWaveTrig','reward_delivery+Go_Cue'},... + 'input_to_statechange',{'Tup','drink_state'}); + sma = add_state(sma,'name','drink_state','self_timer',drink_time,... 'input_to_statechange',{'Tup','preclean_up_state'}); diff --git a/Protocols/@Arpit_CentrePokeTraining/Arpit_CentrePokeTraining_SessionDefinition_AutomatedTrainingStages.m b/Protocols/@Arpit_CentrePokeTraining/Arpit_CentrePokeTraining_SessionDefinition_AutoTrainingStages.m similarity index 99% rename from Protocols/@Arpit_CentrePokeTraining/Arpit_CentrePokeTraining_SessionDefinition_AutomatedTrainingStages.m rename to Protocols/@Arpit_CentrePokeTraining/Arpit_CentrePokeTraining_SessionDefinition_AutoTrainingStages.m index 831a9d1d..db4cf26f 100644 --- a/Protocols/@Arpit_CentrePokeTraining/Arpit_CentrePokeTraining_SessionDefinition_AutomatedTrainingStages.m +++ b/Protocols/@Arpit_CentrePokeTraining/Arpit_CentrePokeTraining_SessionDefinition_AutoTrainingStages.m @@ -2,7 +2,7 @@ %Please use the session automator window exclusively %to edit this file. -function varargout = Arpit_CentrePokeTraining_SessionDefinition_AutomatedTrainingStages(obj, action, varargin) +function varargout = Arpit_CentrePokeTraining_SessionDefinition_AutoTrainingStages(obj, action, varargin) GetSoloFunctionArgs('func_owner', ['@' class(obj)], 'func_name', 'SessionModel'); diff --git a/Protocols/@Arpit_CentrePokeTraining/PunishmentSection.m b/Protocols/@Arpit_CentrePokeTraining/PunishmentSection.m deleted file mode 100644 index 5289350e..00000000 --- a/Protocols/@Arpit_CentrePokeTraining/PunishmentSection.m +++ /dev/null @@ -1,133 +0,0 @@ -% Typical section code-- this file may be used as a template to be added -% on to. The code below stores the current figure and initial position when -% the action is 'init'; and, upon 'reinit', deletes all SoloParamHandles -% belonging to this section, then calls 'init' at the proper GUI position -% again. - - -% [x, y] = YOUR_SECTION_NAME(obj, action, x, y) -% -% Section that takes care of YOUR HELP DESCRIPTION -% -% PARAMETERS: -% ----------- -% -% obj Default object argument. -% -% action One of: -% 'init' To initialise the section and set up the GUI -% for it -% -% 'reinit' Delete all of this section's GUIs and data, -% and reinit, at the same position on the same -% figure as the original section GUI was placed. -% -% 'prepare_next_trial' Goes through the processing necessary -% to compute what the next trial's correct side -% should be. -% -% 'get_current_side' Returns either the string 'l' or the -% string 'r', for which side is the current trial's -% correcy side. -% -% -% x, y Relevant to action = 'init'; they indicate the initial -% position to place the GUI at, in the current figure window -% -% RETURNS: -% -------- -% -% [x, y] When action == 'init', returns x and y, pixel positions on -% the current figure, updated after placing of this section's GUI. -% -% -% x When action == 'get_current_side', returns either the string 'l' -% or the string 'r', for Left and Right, respectively. -% - - -function [x, y] = PunishmentSection(obj, action, x, y) - -GetSoloFunctionArgs(obj); - -switch action - case 'init', - % Save the figure and the position in the figure where we are - % going to start adding GUI elements: - SoloParamHandle(obj, 'my_gui_info', 'value', [x y double(gcf)], 'saveable', 0); - - NumeditParam(obj, 'DrinkTime', 20, x, y, 'TooltipString', sprintf('\nTime over which drinking is ok')); next_row(y); - ToggleParam(obj, 'WarningSoundPanel', 1, x, y, 'OnString', 'warn show', 'OffString', 'warn hide', 'position', [x y 80 20]); - NumeditParam(obj, 'WarnDur', 4, x, y, 'labelfraction', 0.6, 'TooltipString', 'Warning sound duration in secs', 'position', [x+80 y 60 20]); - NumeditParam(obj, 'DangerDur',15, x, y, 'labelfraction', 0.6, 'TooltipString', sprintf('\nDuration of post-drink period where poking is punished'), 'position', [x+140 y 60 20]); next_row(y); - set_callback(WarningSoundPanel, {mfilename, 'WarningSoundPanel'}); - % start subpanel - oldx = x; oldy = y; oldfigure = double(gcf); - SoloParamHandle(obj, 'WarningSoundPanelFigure', 'saveable', 0, 'value', figure('Position', [120 120 430 156])); - sfig = value(WarningSoundPanelFigure); - set(sfig, 'MenuBar', 'none', 'NumberTitle', 'off', ... - 'Name', 'Warning sound', 'CloseRequestFcn', 'Classical(classical, ''closeWarningSoundPanel'')'); - SoundInterface(obj, 'add', 'WarningSound', 10, 10); - SoundInterface(obj, 'set', 'WarningSound', 'Vol', 0.0002); - SoundInterface(obj, 'set', 'WarningSound', 'Vol2', 0.004); - SoundInterface(obj, 'set', 'WarningSound', 'Dur1', 4); - SoundInterface(obj, 'set', 'WarningSound', 'Loop', 0); - SoundInterface(obj, 'set', 'WarningSound', 'Style', 'WhiteNoiseRamp'); - - SoundInterface(obj, 'add', 'DangerSound', 215, 10); - SoundInterface(obj, 'set', 'DangerSound', 'Vol', 0.004); - SoundInterface(obj, 'set', 'DangerSound', 'Dur1', 1); - SoundInterface(obj, 'set', 'DangerSound', 'Loop', 1); - SoundInterface(obj, 'set', 'DangerSound', 'Style', 'WhiteNoise'); - - x = oldx; y = oldy; figure(oldfigure); - % end subpanel - SoloFunctionAddVars('SMASection', 'ro_args', {'DrinkTime', 'WarnDur', 'DangerDur'}); - - [x, y] = PunishInterface(obj, 'add', 'PostDrinkPun', x, y); %#ok - next_row(y); - - %--------------------------------------------------------------- - % WarningSoundPanel - %--------------------------------------------------------------- - - case 'WarningSoundPanel' - if WarningSoundPanel==0, set(value(WarningSoundPanelFigure), 'Visible', 'off'); - else set(value(WarningSoundPanelFigure), 'Visible', 'on'); - end; - - %--------------------------------------------------------------- - % CLOSE - %--------------------------------------------------------------- - - case 'close', - if exist('WarningSoundPanelFigure', 'var') && ishandle(value(WarningSoundPanelFigure)), - delete(value(WarningSoundPanelFigure)); - end; - - if exist('PostDrinkPun_SoundsPanel', 'var') && ishandle(value(PostDrinkPun_SoundsPanel)), - delete(value(PostDrinkPun_SoundsPanel)); - end - - %--------------------------------------------------------------- - % REINIT - %--------------------------------------------------------------- - case 'reinit', - currfig = double(gcf); - - % Get the original GUI position and figure: - x = my_gui_info(1); y = my_gui_info(2); figure(my_gui_info(3)); - - % Delete all SoloParamHandles who belong to this object and whose - % fullname starts with the name of this mfile: - delete_sphandle('owner', ['^@' class(obj) '$'], ... - 'fullname', ['^' mfilename '_']); - - % Reinitialise at the original GUI position and figure: - [x, y] = feval(mfilename, obj, 'init', x, y); - - % Restore the current figure: - figure(currfig); -end; - - diff --git a/Protocols/@Arpit_CentrePokeTraining/RewardsSection.m b/Protocols/@Arpit_CentrePokeTraining/RewardsSection.m deleted file mode 100644 index 65ead5c1..00000000 --- a/Protocols/@Arpit_CentrePokeTraining/RewardsSection.m +++ /dev/null @@ -1,237 +0,0 @@ -% Typical section code-- this file may be used as a template to be added -% on to. The code below stores the current figure and initial position when -% the action is 'init'; and, upon 'reinit', deletes all SoloParamHandles -% belonging to this section, then calls 'init' at the proper GUI position -% again. -% -% -% [x, y] = YOUR_SECTION_NAME(obj, action, x, y) -% -% Section that takes care of YOUR HELP DESCRIPTION -% -% PARAMETERS: -% ----------- -% -% obj Default object argument. -% -% action One of: -% 'init' To initialise the section and set up the GUI -% for it -% -% 'reinit' Delete all of this section's GUIs and data, -% and reinit, at the same position on the same -% figure as the original section GUI was placed. -% -% 'get_stimulus' Returns either (1) a structure with fields 'type' -% and 'duration', with the contents of 'type' being -% 'lights' and the contents of 'duration' the -% maximum duration, in secs, of the stimulus; or (2) a -% structure with the fields 'type', 'duration', and -% 'id', with contents 'sounds', duration of sound in -% secs, and integer sound_id, respectively. -% -% 'get_poked_trials' Returns a double, number of trials in -% which subject poked in the appropriate poke at -% some point. -% -% -% x, y Relevant to action = 'init'; they indicate the initial -% position to place the GUI at, in the current figure window -% -% RETURNS: -% -------- -% -% [x, y] When action == 'init', returns x and y, pixel positions on -% the current figure, updated after placing of this section's GUI. -% -% -% x When action == 'get_current_side', returns either the string 'l' -% or the string 'r', for Left and Right, respectively. -% - - -function [x, y] = RewardsSection(obj, action, x, y) - -GetSoloFunctionArgs(obj); - -switch action - case 'init', - % Save the figure and the position in the figure where we are - % going to start adding GUI elements: - SoloParamHandle(obj, 'my_gui_info', 'value', [x y double(gcf)]); - - - DispParam(obj, 'n_trials', 0, x, y, ... - 'TooltipString', 'total # of elapsed trials'); next_row(y); - DispParam(obj, 'poked_trials', 0, x, y, ... - 'TooltipString', '# of trials in which subject poked in the appropriate poke at some point'); next_row(y); - DispParam(obj, 'consec_p_trials', 0, x, y, ... - 'TooltipString', '# of consecutive "poked_trials"'); next_row(y); - DispParam(obj, 'consec_up_trials', 0, x, y, ... - 'TooltipString', '# of consecutive UN"poked_trials"'); - ToggleParam(obj, 'WaterBlock', 0, x, y, 'position', [x+185 y 15 15], 'label', '', 'TooltipString', ... - sprintf('\nif BLACK, no water delivery, in "direct" mode. If BROWN, normal water delivery'), ... - 'OnString', '', 'OffString', ''); next_row(y); - DispParam(obj, 'rewarded_trials', 0, x, y, ... - 'TooltipString', '# of trials in which subject poked and got water'); next_row(y); - DispParam(obj, 'consec_r_trials', 0, x, y, ... - 'TooltipString', '# of consecutive trials, ending in last trial, in which subject poked and got water'); next_row(y); - DispParam(obj, 'rt', 0, x, y, ... - 'TooltipString', 'reaction time'); next_row(y); - NumeditParam(obj, 'rtThreshold', 4, x, y, 'TooltipString', ... - sprintf('\nrt less than this defines a "quick" trial')); next_row(y); - DispParam(obj, 'consec_q_trials', 0, x, y, ... - 'TooltipString', '# of consecutive trials with rt less than rtThreshold'); next_row(y); - DispParam(obj, 'good_trials', 0, x, y, ... - 'TooltipString', '# of trials in which subject exceeded post CS/pre CS poke ratio'); next_row(y); - DispParam(obj, 'consec_g_trials', 0, x, y, ... - 'TooltipString', '# of consecutive trials, ending in last trial, in which subject exceeded post CS/pre CS poke ratio'); next_row(y); - - % ------- - - SoloParamHandle(obj, 'r_trials', 'value', []); - SoloParamHandle(obj, 'q_trials', 'value', []); - SoloParamHandle(obj, 'g_trials', 'value', []); - - SubheaderParam(obj, 'title', 'Rewards Section', x, y); - next_row(y, 1.5); - - SoloFunctionAddVars('SMASection', ... - 'ro_args', {'WaterDelivery', 'RewardTime', 'PokeMeasureTime', 'WaterBlock'}); - feval(mfilename, obj, 'WaterDelivery'); % Set whatever is appropriate for current WaterDelivery - - - % --------------------------------------------------------------------- - % - % CASE GET_POKED_TRIALS - % - % --------------------------------------------------------------------- - - - case 'get_poked_trials', - x = value(poked_trials); %#ok - return; - - case 'add_to_pd' - %% add to pd - x.reward_time=cell2mat(get_history(RewardTime)); - - % --------------------------------------------------------------------- - % - % CASE PREPARE_NEXT_TRIAL - % - % --------------------------------------------------------------------- - - - case 'prepare_next_trial', - if isempty(parsed_events), return; end; - if ~isempty(previous_sides), %#ok - previous_sides = previous_sides(:); - wdh = get_history(WaterDelivery); - if isempty(wdh), wdh{1}='direct'; end; % fix for wierd bug - switch value(wdh{end}), - case 'direct', csstate = 'direct_cs'; - case 'on correct poke', csstate = 'cs'; - case 'on correctly timed poke', csstate = 'rewardable_cs'; - otherwise - error('huh?'); - end; - cs_onset = parsed_events.states.(csstate)(1,1); - if isequal(StimulusSection(obj, 'get_last_stimulus_loc'), 'anti-loc'), - if previous_sides(end)=='l', poke = 'R'; else poke = 'L'; end; - else - if previous_sides(end)=='l', poke = 'L'; else poke = 'R'; end; - end; - mypokes = parsed_events.pokes.(poke)(:,1); - - if ~isempty(find(mypokes > cs_onset,1)) - poked_trials.value = poked_trials+1; %#ok - consec_p_trials.value = consec_p_trials+1; %#ok - consec_up_trials.value = 0; - else - consec_p_trials.value = 0; - consec_up_trials.value = consec_up_trials+1; %#ok - end - end; - - if rows(parsed_events.states.lefthit)>0 || rows(parsed_events.states.righthit)>0, - r_trials.value = [r_trials(1:n_done_trials-1) 1]; %#ok - rewarded_trials.value = rewarded_trials+1; %#ok - consec_r_trials.value = consec_r_trials+1; %#ok - else - r_trials.value = [r_trials(1:n_done_trials-1) 0]; %#ok - consec_r_trials.value = 0; - end; - - hit_history.value = value(r_trials); - - n_trials.value = n_trials+1; %#ok - - - % Compute reaction times only for non-direct delivery modes: - if strcmp(csstate, 'direct_cs'), - consec_q_trials.value = 0; - else - if rows(parsed_events.states.lefthit>0) - rt.value = parsed_events.states.lefthit(1,1) - parsed_events.states.(csstate)(1,1); - elseif rows(parsed_events.states.righthit>0) - rt.value = parsed_events.states.righthit(1,1) - parsed_events.states.(csstate)(1,1); - else - warning('CLASSICAL:No_hit_state', 'No lefthit or righthit -- not computing rt!'); - end; - - if rt < rtThreshold, consec_q_trials.value = consec_q_trials + 1; %#ok - else consec_q_trials.value = 0; - end; - end; - - % Now compute the good poke ratio stuff - if ~isempty(previous_sides), - preCSonset_pokes = ... - length(find(parsed_events.states.(csstate)(1,1)-PokeMeasureTime < mypokes & ... - mypokes < parsed_events.states.(csstate)(1,1))); - postCSonset_pokes = ... - length(find(parsed_events.states.(csstate)(1,1) < mypokes & ... - mypokes < parsed_events.states.(csstate)(1,1)+PokeMeasureTime)); - if preCSonset_pokes > 0, PokeRatio.value = postCSonset_pokes / preCSonset_pokes; - elseif postCSonset_pokes > 0, PokeRatio.value = Inf; - else PokeRatio.value = 0; - end; - if PokeRatio > PokeRatioThreshold, - good_trials.value = good_trials+1; %#ok - consec_g_trials.value = consec_g_trials+1; %#ok - else - consec_g_trials.value = 0; - end; - end; - - - - - % --------------------------------------------------------------------- - % - % CASE REINIT - % - % --------------------------------------------------------------------- - - case 'reinit', - currfig = double(gcf); - - % Get the original GUI position and figure: - x = my_gui_info(1); y = my_gui_info(2); figure(my_gui_info(3)); - - % Delete all SoloParamHandles who belong to this object and whose - % fullname starts with the name of this mfile: - delete_sphandle('owner', ['^@' class(obj) '$'], ... - 'fullname', ['^' mfilename]); - - - % Reinitialise at the original GUI position and figure: - [x, y] = feval(mfilename, obj, 'init', x, y); - - % Restore the current figure: - figure(currfig); -end - - - diff --git a/Protocols/@Arpit_CentrePokeTraining/pipeline_Arpit_CentrePokeTraining_experimenter_ratname_250319c.m b/Protocols/@Arpit_CentrePokeTraining/pipeline_Arpit_CentrePokeTraining_experimenter_ratname_250319c.m deleted file mode 100644 index 93f2ede0..00000000 --- a/Protocols/@Arpit_CentrePokeTraining/pipeline_Arpit_CentrePokeTraining_experimenter_ratname_250319c.m +++ /dev/null @@ -1,1150 +0,0 @@ -%Training stage file. -%Please use the session automator window exclusively -%to edit this file. - -function varargout = pipeline_Arpit_CentrePokeTraining_experimenter_ratname_250319c(obj, action, varargin) - -GetSoloFunctionArgs('func_owner', ['@' class(obj)], 'func_name', 'SessionModel'); - -pairs = {'helper_vars_eval', true; - 'stage_algorithm_eval', true; - 'completion_test_eval', false; - 'eod_logic_eval', false}; -parseargs(varargin, pairs); - -switch action - - -%% Familiarize with Reward Side Pokes - -% -case 'Familiarize with Reward Side Pokes' - - -if helper_vars_eval -GetSoloFunctionArgs(obj); -ClearHelperVarsNotOwned(obj); -% -CreateHelperVar(obj,'stages_trial_counter','value',zeros(1,8)); -CreateHelperVar(obj,'stages_trial_counter_today','value',zeros(1,8)); -CreateHelperVar(obj,'stages_trial_counter_oppSide','value',zeros(1,8)); -CreateHelperVar(obj,'stages_timeout_rate','value',zeros(1,8)); -CreateHelperVar(obj,'stages_violation_rate','value',zeros(1,8)); -CreateHelperVar(obj,'stage_start_completed_trial','value',n_completed_trials,'forceinit',true); -% -end - - -if stage_algorithm_eval -GetSoloFunctionArgs(obj); -ClearHelperVarsNotOwned(obj); - - -% -ParamsSection_MaxSame.value = 4; -callback(ParamsSection_MaxSame); -stage_no = value(SessionDefinition_CURRENT_ACTIVE_STAGE); -ParamsSection_training_stage.value = stage_no; -callback(ParamsSection_training_stage); -if n_completed_trials > value(stage_start_completed_trial) - this_stage_trial_counter = value(stages_trial_counter); - this_stage_trial_counter(stage_no) = this_stage_trial_counter(stage_no) + 1; - SessionPerformanceSection_ntrials_stage.value = this_stage_trial_counter(stage_no); - callback(SessionPerformanceSection_ntrials_stage); - stages_trial_counter.value = this_stage_trial_counter; - this_stage_trial_counter_today = value(stages_trial_counter_today); - this_stage_trial_counter_today(stage_no) = this_stage_trial_counter_today(stage_no) + 1; - SessionPerformanceSection_ntrials_stage_today.value = this_stage_trial_counter_today(stage_no); - callback(SessionPerformanceSection_ntrials_stage_today); - stages_trial_counter_today.value = this_stage_trial_counter_today; - this_stage_trial_counter_oppSide = value(stages_trial_counter_oppSide); - if value(previous_sides(end)) ~= value(ParamsSection_ThisTrial) - this_stage_trial_counter_oppSide(stage_no) = this_stage_trial_counter_oppSide(stage_no) + 1; - end - stages_trial_counter_oppSide.value = this_stage_trial_counter_oppSide; -end -% -end - - -if completion_test_eval -GetSoloFunctionArgs(obj); -ClearHelperVarsNotOwned(obj); -clear('ans'); -% -stage_no = value(SessionDefinition_CURRENT_ACTIVE_STAGE); -this_stage_trial_counter_oppSide = value(stages_trial_counter_oppSide); -this_stage_trial_counter = value(stages_trial_counter); -% only run it if its the start of the day, number of trials is small -if n_completed_trials < 100 - if this_stage_trial_counter(stage_no) > value(Training_ParamsSection_total_trials) && this_stage_trial_counter_oppSide(stage_no) > value(Training_ParamsSection_total_trials_opp) - SessionDefinition(obj, 'jump_to_stage', 'Timeout Rewarded Side Pokes'); - end -end -% -if exist('ans', 'var') -varargout{1}=logical(ans); clear('ans'); -else -varargout{1}=false; -end -end - - -if eod_logic_eval -GetSoloFunctionArgs(obj); -ClearHelperVarsNotOwned(obj); -% -stage_no = value(SessionDefinition_CURRENT_ACTIVE_STAGE); -this_stage_trial_counter_oppSide = value(stages_trial_counter_oppSide); -this_stage_trial_counter = value(stages_trial_counter); -stages_trial_counter_today.value = zeros(1,8); -if this_stage_trial_counter(stage_no) > value(Training_ParamsSection_total_trials) && this_stage_trial_counter_oppSide(stage_no) > value(Training_ParamsSection_total_trials_opp) - SessionDefinition(obj, 'jump_to_stage', 'Timeout Rewarded Side Pokes'); -end -% -end - -% - - - - - -%% Timeout Rewarded Side Pokes - -% - -case 'Timeout Rewarded Side Pokes' - - -if helper_vars_eval -GetSoloFunctionArgs(obj); -ClearHelperVarsNotOwned(obj); -% -CreateHelperVar(obj,'stages_trial_counter','value',zeros(1,8)); -CreateHelperVar(obj,'stages_trial_counter_today','value',zeros(1,8)); -CreateHelperVar(obj,'stages_trial_counter_oppSide','value',zeros(1,8)); -CreateHelperVar(obj,'stages_timeout_rate','value',zeros(1,8)); -CreateHelperVar(obj,'stages_violation_rate','value',zeros(1,8)); -CreateHelperVar(obj,'this_stage_opp_side_trials', 'value', '0', 'forceinit','true'); -CreateHelperVar(obj,'stage_start_completed_trial','value',n_completed_trials,'forceinit',true); -% -end - - -if stage_algorithm_eval -GetSoloFunctionArgs(obj); -ClearHelperVarsNotOwned(obj); -% -% Change ParamsSection Vars -ParamsSection_MaxSame.value = 3; -callback(ParamsSection_MaxSame); -stage_no = value(SessionDefinition_CURRENT_ACTIVE_STAGE); -ParamsSection_training_stage.value = stage_no; -callback(ParamsSection_training_stage); -if n_completed_trials > value(stage_start_completed_trial) - % Update the helper vars - this_stage_trial_counter = value(stages_trial_counter); - this_stage_trial_counter(stage_no) = this_stage_trial_counter(stage_no) + 1; - SessionPerformanceSection_ntrials_stage.value = this_stage_trial_counter(stage_no); - callback(SessionPerformanceSection_ntrials_stage); - stages_trial_counter.value = this_stage_trial_counter; - - this_stage_trial_counter_today = value(stages_trial_counter_today); - this_stage_trial_counter_today(stage_no) = this_stage_trial_counter_today(stage_no) + 1; - SessionPerformanceSection_ntrials_stage_today.value = this_stage_trial_counter_today(stage_no); - callback(SessionPerformanceSection_ntrials_stage_today); - stages_trial_counter_today.value = this_stage_trial_counter_today; - - this_stage_trial_counter_oppSide = value(stages_trial_counter_oppSide); - if value(previous_sides(end)) ~= value(ParamsSection_ThisTrial) - this_stage_trial_counter_oppSide(stage_no) = this_stage_trial_counter_oppSide(stage_no) + 1; - this_stage_opp_side_trials.value = value(this_stage_opp_side_trials) + 1; - end - stages_trial_counter_oppSide.value = this_stage_trial_counter_oppSide; - - this_stage_timeout_percent = value(stages_timeout_rate); - this_stage_timeout_percent(stage_no) = ((this_stage_timeout_percent(stage_no) * this_stage_trial_counter(stage_no)) + double(timeout_history(end)))/(this_stage_trial_counter(stage_no) + 1); - SessionPerformanceSection_timeout_stage.value = this_stage_timeout_percent(stage_no); - callback(SessionPerformanceSection_timeout_stage); - stages_timeout_rate.value = this_stage_timeout_percent; -end -% Update the reward collection time based upon behav -if size(timeout_history) > 5 - if all(value(timeout_history(end-1:end))) && value(this_stage_opp_side_trials) >= 2 - ParamsSection_RewardCollection_duration.value = min([value(ParamsSection_RewardCollection_duration) + 1,... - value(Training_ParamsSection_max_rColl_dur)]); - callback(ParamsSection_RewardCollection_duration); - this_stage_opp_side_trials.value = 0; - end - if ~any(value(timeout_history(end-1:end))) && value(this_stage_opp_side_trials) >= 2 - ParamsSection_RewardCollection_duration.value = max([value(ParamsSection_RewardCollection_duration) - 1,... - value(Training_ParamsSection_min_rColl_dur)]); - callback(ParamsSection_RewardCollection_duration); - this_stage_opp_side_trials.value = 0; - end -end -if size(timeout_history) > 20 - if all(value(timeout_history(end-19:end))) - ParamsSection_RewardCollection_duration.value = 30; - callback(ParamsSection_RewardCollection_duration); - end -end - -% -end - - -if completion_test_eval -GetSoloFunctionArgs(obj); -ClearHelperVarsNotOwned(obj); -clear('ans'); -% -stage_no = value(SessionDefinition_CURRENT_ACTIVE_STAGE); -this_stage_trial_counter_oppSide = value(stages_trial_counter_oppSide); -this_stage_trial_counter = value(stages_trial_counter); -% only run it if its the start of the day, number of trials is small -if n_completed_trials > 50 - if this_stage_trial_counter(stage_no) > value(Training_ParamsSection_total_trials) && this_stage_trial_counter_oppSide(stage_no) > value(Training_ParamsSection_total_trials_opp) - SessionDefinition(obj, 'jump_to_stage', 'Introduce Centre Poke'); - end -end -% -if exist('ans', 'var') -varargout{1}=logical(ans); clear('ans'); -else -varargout{1}=false; -end -end - - -if eod_logic_eval -GetSoloFunctionArgs(obj); -ClearHelperVarsNotOwned(obj); -% -stages_trial_counter_today.value = zeros(1,8); -% -end -% - - - - -%% Introduce Centre Poke - -% -case 'Introduce Centre Poke' -if helper_vars_eval -GetSoloFunctionArgs(obj); -ClearHelperVarsNotOwned(obj); -% -CreateHelperVar(obj,'stages_trial_counter','value',zeros(1,8)); -CreateHelperVar(obj,'stages_trial_counter_today','value',zeros(1,8)); -CreateHelperVar(obj,'stages_trial_counter_oppSide','value',zeros(1,8)); -CreateHelperVar(obj,'stages_timeout_rate','value',zeros(1,8)); -CreateHelperVar(obj,'stages_violation_rate','value',zeros(1,8)); -CreateHelperVar(obj,'last_session_CP','value',0); -CreateHelperVar(obj,'stage_start_completed_trial','value',n_completed_trials,'forceinit',true); -% -end - - -if stage_algorithm_eval -GetSoloFunctionArgs(obj); -ClearHelperVarsNotOwned(obj); -% -% Maximum & Minimum duration of center poke, in secs: -cp_max = value(ParamsSection_SettlingIn_time) + value(ParamsSection_legal_cbreak); -cp_min = value(ParamsSection_init_CP_duration); -% Minimum increment (in secs) in center poke duration every time there is a non-cp-violation trial: -cp_minimum_increment = 0.001; -ParamsSection_MaxSame.value = Inf; -callback(ParamsSection_MaxSame); - -stage_no = value(SessionDefinition_CURRENT_ACTIVE_STAGE); -ParamsSection_training_stage.value = stage_no; -callback(ParamsSection_training_stage); -if n_completed_trials > value(stage_start_completed_trial) - % Update the helper vars - this_stage_trial_counter = value(stages_trial_counter); - this_stage_trial_counter(stage_no) = this_stage_trial_counter(stage_no) + 1; - SessionPerformanceSection_ntrials_stage.value = this_stage_trial_counter(stage_no); - callback(SessionPerformanceSection_ntrials_stage); - stages_trial_counter.value = this_stage_trial_counter; - - this_stage_trial_counter_today = value(stages_trial_counter_today); - this_stage_trial_counter_today(stage_no) = this_stage_trial_counter_today(stage_no) + 1; - SessionPerformanceSection_ntrials_stage_today.value = this_stage_trial_counter_today(stage_no); - callback(SessionPerformanceSection_ntrials_stage_today); - stages_trial_counter_today.value = this_stage_trial_counter_today; - - this_stage_timeout_percent = value(stages_timeout_rate); - this_stage_timeout_percent(stage_no) = ((this_stage_timeout_percent(stage_no) * this_stage_trial_counter(stage_no)) + double(timeout_history(end)))/(this_stage_trial_counter(stage_no) + 1); - SessionPerformanceSection_timeout_stage.value = this_stage_timeout_percent(stage_no); - callback(SessionPerformanceSection_timeout_stage); - stages_timeout_rate.value = this_stage_timeout_percent; -end -% Change the value of CP Duration -if n_completed_trials < 1 - ParamsSection_CP_duration.value = value(ParamsSection_init_CP_duration); -else - if ~timeout_history(end) && value(ParamsSection_CP_duration) < cp_max - increment = value(ParamsSection_CP_duration) * value(Training_ParamsSection_CPfraction_inc); - if increment < cp_minimum_increment - increment = cp_minimum_increment; - end - ParamsSection_CP_duration.value = value(ParamsSection_CP_duration) + increment; - end -end -callback(ParamsSection_CP_duration); -% -end - - -if completion_test_eval -GetSoloFunctionArgs(obj); -ClearHelperVarsNotOwned(obj); -clear('ans'); -% -cp_max = value(ParamsSection_SettlingIn_time) + value(ParamsSection_legal_cbreak); -if value(ParamsSection_CP_duration) >= cp_max - SessionDefinition(obj, 'jump_to_stage', 'Introduce Violation for Centre Poke'); -end -% -if exist('ans', 'var') -varargout{1}=logical(ans); clear('ans'); -else -varargout{1}=false; -end -end - - - -if eod_logic_eval -GetSoloFunctionArgs(obj); -ClearHelperVarsNotOwned(obj); -% -stages_trial_counter_today.value = zeros(1,8); -last_session_CP.value = value(ParamsSection_CP_duration); -% -end -% - - -%% Introduce Violation for Centre Poke - -% -case 'Introduce Violation for Centre Poke' -if helper_vars_eval -GetSoloFunctionArgs(obj); -ClearHelperVarsNotOwned(obj); -% -CreateHelperVar(obj,'stages_trial_counter','value',zeros(1,8)); -CreateHelperVar(obj,'stages_trial_counter_today','value',zeros(1,8)); -CreateHelperVar(obj,'stages_trial_counter_oppSide','value',zeros(1,8)); -CreateHelperVar(obj,'stages_timeout_rate','value',zeros(1,8)); -CreateHelperVar(obj,'stages_violation_rate','value',zeros(1,8)); -CreateHelperVar(obj,'last_session_CP','value',0); -CreateHelperVar(obj,'stage_start_completed_trial','value',n_completed_trials,'forceinit',true); -% -end - - - -if stage_algorithm_eval -GetSoloFunctionArgs(obj); -ClearHelperVarsNotOwned(obj); -% - -% Maximum & Minimum duration of center poke, in secs: -cp_min = value(ParamsSection_SettlingIn_time) + value(ParamsSection_legal_cbreak); -cp_max = value(Training_ParamsSection_max_CP); -% Fractional increment in center poke duration every time there is a non-cp-violation trial: -cp_fraction = value(Training_ParamsSection_CPfraction_inc); -% Minimum increment (in secs) in center poke duration every time there is a non-cp-violation trial: -cp_minimum_increment = 0.001; - -stage_no = value(SessionDefinition_CURRENT_ACTIVE_STAGE); -ParamsSection_training_stage.value = stage_no; -callback(ParamsSection_training_stage); -if n_completed_trials > value(stage_start_completed_trial) - % Update the helper vars - this_stage_trial_counter = value(stages_trial_counter); - this_stage_trial_counter(stage_no) = this_stage_trial_counter(stage_no) + 1; - SessionPerformanceSection_ntrials_stage.value = this_stage_trial_counter(stage_no); - callback(SessionPerformanceSection_ntrials_stage); - stages_trial_counter.value = this_stage_trial_counter; - - this_stage_trial_counter_today = value(stages_trial_counter_today); - this_stage_trial_counter_today(stage_no) = this_stage_trial_counter_today(stage_no) + 1; - SessionPerformanceSection_ntrials_stage_today.value = this_stage_trial_counter_today(stage_no); - callback(SessionPerformanceSection_ntrials_stage_today); - stages_trial_counter_today.value = this_stage_trial_counter_today; - - this_stage_timeout_percent = value(stages_timeout_rate); - this_stage_timeout_percent(stage_no) = ((this_stage_timeout_percent(stage_no) * this_stage_trial_counter(stage_no)) + double(timeout_history(end)))/(this_stage_trial_counter(stage_no) + 1); - SessionPerformanceSection_timeout_stage.value = this_stage_timeout_percent(stage_no); - callback(SessionPerformanceSection_timeout_stage); - stages_timeout_rate.value = this_stage_timeout_percent; - - this_stage_violation_percent = value(stages_violation_rate); - this_stage_violation_percent(stage_no) = ((this_stage_violation_percent(stage_no) * this_stage_trial_counter(stage_no)) + double(violation_history(end)))/(this_stage_trial_counter(stage_no) + 1); - SessionPerformanceSection_violation_stage.value = this_stage_violation_percent(stage_no); - callback(SessionPerformanceSection_violation_stage); - stages_violation_rate.value = this_stage_violation_percent; -end -% Change the value of CP Duration -if n_completed_trials < 1 - ParamsSection_CP_duration.value = value(ParamsSection_init_CP_duration); -else - if ~violation_history(end) && ~timeout_history(end) && value(ParamsSection_CP_duration) < cp_max - increment = value(ParamsSection_CP_duration) * cp_fraction; - if increment < cp_minimum_increment - increment = cp_minimum_increment; - end - ParamsSection_CP_duration.value = value(ParamsSection_CP_duration) + increment; - end -end -if value(ParamsSection_CP_duration) > cp_max - ParamsSection_CP_duration.value = cp_max; -end - -callback(ParamsSection_CP_duration); -% -end - - -if completion_test_eval -GetSoloFunctionArgs(obj); -ClearHelperVarsNotOwned(obj); -clear('ans'); -% -cp_max = value(Training_ParamsSection_max_CP); -if value(ParamsSection_CP_duration) >= cp_max && n_completed_trials > 100 -if SessionPerformanceSection_violation_recent < value(Training_ParamsSection_recent_violation) && SessionPerformanceSection_timeout_recent < value(Training_ParamsSection_recent_timeout) && ... - SessionPerformanceSection_violation_stage < value(Training_ParamsSection_stage_violation) - SessionDefinition(obj, 'jump_to_stage', 'Introduce Stimuli Sound during Centre Poke'); - last_session_CP.value = value(ParamsSection_CP_duration); -end -end -% -if exist('ans', 'var') -varargout{1}=logical(ans); clear('ans'); -else -varargout{1}=false; -end -end - - -if eod_logic_eval -GetSoloFunctionArgs(obj); -ClearHelperVarsNotOwned(obj); -% -% Store the value of the total cp duration reached: -stages_trial_counter_today.value = zeros(1,8); -last_session_CP.value = value(ParamsSection_CP_duration); -% -end -% - - - - - -%% Introduce Stimuli Sound during Centre Poke - -% -case 'Introduce Stimuli Sound during Centre Poke' -if helper_vars_eval -GetSoloFunctionArgs(obj); -ClearHelperVarsNotOwned(obj); -% -CreateHelperVar(obj,'stages_trial_counter','value',zeros(1,8)); -CreateHelperVar(obj,'stages_trial_counter_today','value',zeros(1,8)); -CreateHelperVar(obj,'stages_trial_counter_oppSide','value',zeros(1,8)); -CreateHelperVar(obj,'stages_timeout_rate','value',zeros(1,8)); -CreateHelperVar(obj,'stages_violation_rate','value',zeros(1,8)); -CreateHelperVar(obj,'last_session_CP','value',0); -CreateHelperVar(obj,'stage_start_completed_trial','value',n_completed_trials,'forceinit',true); -% -end - - -if stage_algorithm_eval -GetSoloFunctionArgs(obj); -ClearHelperVarsNotOwned(obj); -% -cp_max = value(Training_ParamsSection_max_CP); -cp_min = value(Training_ParamsSection_min_CP); -% Fractional increment in center poke duration every time there is a non-cp-violation trial: -cp_fraction = value(Training_ParamsSection_CPfraction_inc); -% Minimum increment (in secs) in center poke duration every time there is a non-cp-violation trial: -cp_minimum_increment = 0.001; -% Starting total center poke duration: -starting_cp = value(Training_ParamsSection_starting_CP) + value(ParamsSection_SettlingIn_time); -% number of warm-up trials -n_trial_warmup = value(Training_ParamsSection_warm_up_trials); - -stage_no = value(SessionDefinition_CURRENT_ACTIVE_STAGE); -ParamsSection_training_stage.value = stage_no; -callback(ParamsSection_training_stage); -if n_completed_trials > value(stage_start_completed_trial) - % Update the helper vars - this_stage_trial_counter = value(stages_trial_counter); - this_stage_trial_counter(stage_no) = this_stage_trial_counter(stage_no) + 1; - SessionPerformanceSection_ntrials_stage.value = this_stage_trial_counter(stage_no); - callback(SessionPerformanceSection_ntrials_stage); - stages_trial_counter.value = this_stage_trial_counter; - - this_stage_trial_counter_today = value(stages_trial_counter_today); - this_stage_trial_counter_today(stage_no) = this_stage_trial_counter_today(stage_no) + 1; - SessionPerformanceSection_ntrials_stage_today.value = this_stage_trial_counter_today(stage_no); - callback(SessionPerformanceSection_ntrials_stage_today); - stages_trial_counter_today.value = this_stage_trial_counter_today; - - this_stage_timeout_percent = value(stages_timeout_rate); - this_stage_timeout_percent(stage_no) = ((this_stage_timeout_percent(stage_no) * this_stage_trial_counter(stage_no)) + double(timeout_history(end)))/(this_stage_trial_counter(stage_no) + 1); - SessionPerformanceSection_timeout_stage.value = this_stage_timeout_percent(stage_no); - callback(SessionPerformanceSection_timeout_stage); - stages_timeout_rate.value = this_stage_timeout_percent; - - this_stage_violation_percent = value(stages_violation_rate); - this_stage_violation_percent(stage_no) = ((this_stage_violation_percent(stage_no) * this_stage_trial_counter(stage_no)) + double(violation_history(end)))/(this_stage_trial_counter(stage_no) + 1); - SessionPerformanceSection_violation_stage.value = this_stage_violation_percent(stage_no); - callback(SessionPerformanceSection_violation_stage); - stages_violation_rate.value = this_stage_violation_percent; -end -% Change the value of CP Duration - -% Since starting a new session then do a pre warm up to last saved cp -% duration else continue with learning with increased poke time -if n_completed_trials == 0 - ParamsSection_CP_duration.value = value(ParamsSection_init_CP_duration); -elseif n_completed_trials == 1 - ParamsSection_CP_duration.value = starting_cp; -else - if ~violation_history(end) && ~timeout_history(end) - if value(ParamsSection_CP_duration) < max([cp_min,value(last_session_CP)]) % warm up stage - increment = (max([cp_min,value(last_session_CP)]) - value(ParamsSection_CP_duration))/ (n_trial_warmup - 1); - else - if value(ParamsSection_CP_duration) >= value(last_session_CP) && value(ParamsSection_CP_duration) <= cp_max % no warm up stage - increment = value(ParamsSection_CP_duration)*cp_fraction; - if increment < cp_minimum_increment - increment = cp_minimum_increment; - end - end - end - - ParamsSection_CP_duration.value = value(ParamsSection_CP_duration) + increment; - - % Check if the values are within the required range - if value(ParamsSection_CP_duration) < starting_cp - ParamsSection_CP_duration.value = starting_cp; - end - if value(ParamsSection_CP_duration) > cp_max - ParamsSection_CP_duration.value = cp_max; - end - - end -end -callback(ParamsSection_CP_duration); - -if value(ParamsSection_CP_duration) >= 1 - ParamsSection_PreStim_time.value = 0.4; - if value(ParamsSection_CP_duration) < 2 - ParamsSection_A1_time.value = 0.1; - elseif value(ParamsSection_CP_duration) < 2.5 && value(ParamsSection_CP_duration) >= 2 - ParamsSection_A1_time.value = 0.2; - elseif value(ParamsSection_CP_duration) < 3 && value(ParamsSection_CP_duration) >= 2.5 - ParamsSection_A1_time.value = 0.3; - else - ParamsSection_A1_time.value = 0.4; - end -elseif value(ParamsSection_CP_duration) < 1 && value(ParamsSection_CP_duration) >= starting_cp - ParamsSection_SettlingIn_time.value = 0.2; - callback(ParamsSection_SettlingIn_time); - ParamsSection_PreStim_time.value = 0.1; - ParamsSection_A1_time.value = 0.1; -end - -if value(ParamsSection_CP_duration) >= starting_cp - ParamsSection_time_bet_aud1_gocue.value = value(ParamsSection_CP_duration) - value(ParamsSection_SettlingIn_time) - value(ParamsSection_A1_time) - value(ParamsSection_PreStim_time); - callback(ParamsSection_time_bet_aud1_gocue) - callback(ParamsSection_PreStim_time); - callback(ParamsSection_A1_time); -end -% -end - - - -if completion_test_eval -GetSoloFunctionArgs(obj); -ClearHelperVarsNotOwned(obj); -clear('ans'); -% -stage_no = value(SessionDefinition_CURRENT_ACTIVE_STAGE); -this_stage_trial_counter = value(stages_trial_counter); -if value(ParamsSection_CP_duration) >= value(Training_ParamsSection_max_CP) && this_stage_trial_counter(stage_no) > value(Training_ParamsSection_total_trials) - if SessionPerformanceSection_violation_recent < value(Training_ParamsSection_recent_violation) && SessionPerformanceSection_timeout_recent < value(Training_ParamsSection_recent_timeout) && ... - SessionPerformanceSection_violation_stage < value(Training_ParamsSection_stage_violation) && n_completed_trials > 100 - SessionDefinition(obj, 'jump_to_stage', 'Vary Stimuli location during Centre Poke'); - last_session_CP.value = value(ParamsSection_CP_duration); - end -end -% -if exist('ans', 'var') -varargout{1}=logical(ans); clear('ans'); -else -varargout{1}=false; -end -end - - - -if eod_logic_eval -GetSoloFunctionArgs(obj); -ClearHelperVarsNotOwned(obj); -% -stages_trial_counter_today.value = zeros(1,8); -last_session_CP.value = value(ParamsSection_CP_duration); -% -end - - - -% - -%% Vary Stimuli location during Centre Poke - -% -case 'Vary Stimuli location during Centre Poke' -if helper_vars_eval -GetSoloFunctionArgs(obj); -ClearHelperVarsNotOwned(obj); -% -CreateHelperVar(obj,'stages_trial_counter','value',zeros(1,8)); -CreateHelperVar(obj,'stages_trial_counter_today','value',zeros(1,8)); -CreateHelperVar(obj,'stages_trial_counter_oppSide','value',zeros(1,8)); -CreateHelperVar(obj,'stages_timeout_rate','value',zeros(1,8)); -CreateHelperVar(obj,'stages_violation_rate','value',zeros(1,8)); -CreateHelperVar(obj,'last_session_CP','value',0); -CreateHelperVar(obj,'stage_start_completed_trial','value',n_completed_trials,'forceinit',true); -% -end - - - -if stage_algorithm_eval -GetSoloFunctionArgs(obj); -ClearHelperVarsNotOwned(obj); -% -cp_max = value(Training_ParamsSection_max_CP); -% Starting total center poke duration: -starting_cp = value(Training_ParamsSection_starting_CP) + value(ParamsSection_SettlingIn_time); -% number of warm-up trials -n_trial_warmup = value(Training_ParamsSection_warm_up_trials); -prestim_min = value(Training_ParamsSection_min_prestim); -prestim_max = value(Training_ParamsSection_max_prestim); -stim_dur = value(Training_ParamsSection_stim_dur); - -stage_no = value(SessionDefinition_CURRENT_ACTIVE_STAGE); -ParamsSection_training_stage.value = stage_no; -callback(ParamsSection_training_stage); -if n_completed_trials > value(stage_start_completed_trial) - % Update the helper vars - this_stage_trial_counter = value(stages_trial_counter); - this_stage_trial_counter(stage_no) = this_stage_trial_counter(stage_no) + 1; - SessionPerformanceSection_ntrials_stage.value = this_stage_trial_counter(stage_no); - callback(SessionPerformanceSection_ntrials_stage); - stages_trial_counter.value = this_stage_trial_counter; - - this_stage_trial_counter_today = value(stages_trial_counter_today); - this_stage_trial_counter_today(stage_no) = this_stage_trial_counter_today(stage_no) + 1; - SessionPerformanceSection_ntrials_stage_today.value = this_stage_trial_counter_today(stage_no); - callback(SessionPerformanceSection_ntrials_stage_today); - stages_trial_counter_today.value = this_stage_trial_counter_today; - - this_stage_timeout_percent = value(stages_timeout_rate); - this_stage_timeout_percent(stage_no) = ((this_stage_timeout_percent(stage_no) * this_stage_trial_counter(stage_no)) + double(timeout_history(end)))/(this_stage_trial_counter(stage_no) + 1); - SessionPerformanceSection_timeout_stage.value = this_stage_timeout_percent(stage_no); - callback(SessionPerformanceSection_timeout_stage); - stages_timeout_rate.value = this_stage_timeout_percent; - - this_stage_violation_percent = value(stages_violation_rate); - this_stage_violation_percent(stage_no) = ((this_stage_violation_percent(stage_no) * this_stage_trial_counter(stage_no)) + double(violation_history(end)))/(this_stage_trial_counter(stage_no) + 1); - SessionPerformanceSection_violation_stage.value = this_stage_violation_percent(stage_no); - callback(SessionPerformanceSection_violation_stage); - stages_violation_rate.value = this_stage_violation_percent; -end -% Warm Up If starting a new session -if n_completed_trials == 0 - ParamsSection_CP_duration.value = value(ParamsSection_init_CP_duration); -elseif n_completed_trials == 1 - ParamsSection_CP_duration.value = starting_cp; -else - if value(ParamsSection_CP_duration) < cp_max % warm up stage - if ~violation_history(end) && ~timeout_history(end) - increment = (cp_max - value(ParamsSection_CP_duration))/ (n_trial_warmup - 1); - ParamsSection_CP_duration.value = value(ParamsSection_CP_duration) + increment; - % Check if the values are within the required range - if value(ParamsSection_CP_duration) < starting_cp - ParamsSection_CP_duration.value = starting_cp; - end - if value(ParamsSection_CP_duration) > cp_max - ParamsSection_CP_duration.value = cp_max; - end - end - end -end - -callback(ParamsSection_CP_duration); - -if value(ParamsSection_CP_duration) < 3 && value(ParamsSection_CP_duration) >= starting_cp % during the warm up phase - ParamsSection_SettlingIn_time.value = 0.2; - callback(ParamsSection_SettlingIn_time); - ParamsSection_PreStim_time.value = 0.1; - ParamsSection_A1_time.value = 0.1; -else - ParamsSection_A1_time.value = stim_dur; % actual training stage - time_range_PreStim_time = prestim_min : 0.01 : prestim_max; - ParamsSection_PreStim_time.value = time_range_PreStim_time(randi([1, numel(time_range_PreStim_time)],1,1)); -end - -if value(ParamsSection_CP_duration) >= starting_cp - ParamsSection_time_bet_aud1_gocue.value = value(ParamsSection_CP_duration) - value(ParamsSection_SettlingIn_time) - value(ParamsSection_A1_time) - value(ParamsSection_PreStim_time); - callback(ParamsSection_time_bet_aud1_gocue) - callback(ParamsSection_PreStim_time); - callback(ParamsSection_A1_time); -end - -% -end -if completion_test_eval -GetSoloFunctionArgs(obj); -ClearHelperVarsNotOwned(obj); -clear('ans'); -% -stage_no = value(SessionDefinition_CURRENT_ACTIVE_STAGE); -this_stage_trial_counter = value(stages_trial_counter); -if this_stage_trial_counter(stage_no) > value(Training_ParamsSection_total_trials) - if SessionPerformanceSection_violation_recent < value(Training_ParamsSection_recent_violation) && SessionPerformanceSection_timeout_recent < value(Training_ParamsSection_recent_timeout) && ... - SessionPerformanceSection_violation_stage < value(Training_ParamsSection_stage_violation) && n_completed_trials > 100 - SessionDefinition(obj, 'jump_to_stage', 'Variable Stimuli Go Cue location during Centre Poke'); - end -end -% -if exist('ans', 'var') -varargout{1}=logical(ans); clear('ans'); -else -varargout{1}=false; -end -end - - -if eod_logic_eval -GetSoloFunctionArgs(obj); -ClearHelperVarsNotOwned(obj); -% -stages_trial_counter_today.value = zeros(1,8); -% -end -% - - - - -%% Variable Stimuli Go Cue location during Centre Poke - -% -case 'Variable Stimuli Go Cue location during Centre Poke' -if helper_vars_eval -GetSoloFunctionArgs(obj); -ClearHelperVarsNotOwned(obj); -% -CreateHelperVar(obj,'stages_trial_counter','value',zeros(1,8)); -CreateHelperVar(obj,'stages_trial_counter_today','value',zeros(1,8)); -CreateHelperVar(obj,'stages_trial_counter_oppSide','value',zeros(1,8)); -CreateHelperVar(obj,'stages_timeout_rate','value',zeros(1,8)); -CreateHelperVar(obj,'stages_violation_rate','value',zeros(1,8)); -CreateHelperVar(obj,'last_session_CP','value',0); -CreateHelperVar(obj,'stage_start_completed_trial','value',n_completed_trials,'forceinit',true); -% -end - - -if stage_algorithm_eval -GetSoloFunctionArgs(obj); -ClearHelperVarsNotOwned(obj); -% -% Variables for warmup stage -cp_max = value(Training_ParamsSection_max_CP); -% Starting total center poke duration: -starting_cp = value(Training_ParamsSection_starting_CP) + value(ParamsSection_SettlingIn_time); -% number of warm-up trials -n_trial_warmup = value(Training_ParamsSection_warm_up_trials); -prestim_min = value(Training_ParamsSection_min_prestim); -prestim_max = value(Training_ParamsSection_max_prestim); -prestim_time = value(Training_ParamsSection_min_prestim); -a1_time = value(Training_ParamsSection_stim_dur); -a1_time_min = value(Training_ParamsSection_stim_dur); -a1_time_max = value(Training_ParamsSection_stim_dur + 0.1); -prego_min = value(Training_ParamsSection_min_prego); -prego_max = value(Training_ParamsSection_max_prego); -prego_time = value(Training_ParamsSection_min_prego); -warmup_completed = 0; -warm_up_on = 1; -random_prestim = 1; -random_prego = 1; -random_A1 = 0; - -stage_no = value(SessionDefinition_CURRENT_ACTIVE_STAGE); -ParamsSection_training_stage.value = stage_no; -callback(ParamsSection_training_stage); -if n_completed_trials > value(stage_start_completed_trial) - % Update the helper vars - this_stage_trial_counter = value(stages_trial_counter); - this_stage_trial_counter(stage_no) = this_stage_trial_counter(stage_no) + 1; - SessionPerformanceSection_ntrials_stage.value = this_stage_trial_counter(stage_no); - callback(SessionPerformanceSection_ntrials_stage); - stages_trial_counter.value = this_stage_trial_counter; - - this_stage_trial_counter_today = value(stages_trial_counter_today); - this_stage_trial_counter_today(stage_no) = this_stage_trial_counter_today(stage_no) + 1; - SessionPerformanceSection_ntrials_stage_today.value = this_stage_trial_counter_today(stage_no); - callback(SessionPerformanceSection_ntrials_stage_today); - stages_trial_counter_today.value = this_stage_trial_counter_today; - - this_stage_timeout_percent = value(stages_timeout_rate); - this_stage_timeout_percent(stage_no) = ((this_stage_timeout_percent(stage_no) * this_stage_trial_counter(stage_no)) + double(timeout_history(end)))/(this_stage_trial_counter(stage_no) + 1); - SessionPerformanceSection_timeout_stage.value = this_stage_timeout_percent(stage_no); - callback(SessionPerformanceSection_timeout_stage); - stages_timeout_rate.value = this_stage_timeout_percent; - - this_stage_violation_percent = value(stages_violation_rate); - this_stage_violation_percent(stage_no) = ((this_stage_violation_percent(stage_no) * this_stage_trial_counter(stage_no)) + double(violation_history(end)))/(this_stage_trial_counter(stage_no) + 1); - SessionPerformanceSection_violation_stage.value = this_stage_violation_percent(stage_no); - callback(SessionPerformanceSection_violation_stage); - stages_violation_rate.value = this_stage_violation_percent; -end -% Warm Up If starting a new session -if warm_up_on == 1 - if n_completed_trials == 0 - ParamsSection_CP_duration.value = value(ParamsSection_init_CP_duration); - warmup_completed = 0; - elseif n_completed_trials == 1 - ParamsSection_CP_duration.value = starting_cp; - else - if value(ParamsSection_CP_duration) <= cp_max % warm up stage - if ~violation_history(end) && ~timeout_history(end) - increment = (cp_max - value(ParamsSection_CP_duration))/ (n_trial_warmup - 1); - ParamsSection_CP_duration.value = value(ParamsSection_CP_duration) + increment; - % Check if the values are within the required range - if value(ParamsSection_CP_duration) < starting_cp - ParamsSection_CP_duration.value = starting_cp; - end - if value(ParamsSection_CP_duration) >= cp_max - ParamsSection_CP_duration.value = cp_max; - warmup_completed = 1; - end - end - end - end -else - warmup_completed = 1; -end - -if n_completed_trials >= 1 - cp_length = value(ParamsSection_CP_duration) - value(ParamsSection_SettlingIn_time); - [ParamsSection_PreStim_time.value ,ParamsSection_A1_time.value,ParamsSection_time_bet_aud1_gocue.value] = param_time_within_range(warmup_completed,... - cp_length,prestim_min,prestim_max, random_prestim, prestim_time,... - a1_time_min,a1_time_max, random_A1, a1_time,prego_min,prego_max, random_prego, prego_time); - - callback(ParamsSection_PreStim_time); - callback(ParamsSection_A1_time); - callback(ParamsSection_time_bet_aud1_gocue); - ParamsSection_CP_duration = value(ParamsSection_SettlingIn_time) + value(ParamsSection_PreStim_time) + value(ParamsSection_A1_time) + value(ParamsSection_time_bet_aud1_gocue); -end -callback(ParamsSection_CP_duration); -% -end - - - -if completion_test_eval -GetSoloFunctionArgs(obj); -ClearHelperVarsNotOwned(obj); -clear('ans'); -% -stage_no = value(SessionDefinition_CURRENT_ACTIVE_STAGE); -this_stage_trial_counter = value(stages_trial_counter); -if this_stage_trial_counter(stage_no) > value(Training_ParamsSection_total_trials) - if SessionPerformanceSection_violation_recent < value(Training_ParamsSection_recent_violation) && SessionPerformanceSection_timeout_recent < value(Training_ParamsSection_recent_timeout) && ... - SessionPerformanceSection_violation_stage < value(Training_ParamsSection_stage_violation) - SessionDefinition(obj, 'jump_to_stage', 'User Setting'); - end -end -% -if exist('ans', 'var') -varargout{1}=logical(ans); clear('ans'); -else -varargout{1}=false; -end -end - - - -if eod_logic_eval -GetSoloFunctionArgs(obj); -ClearHelperVarsNotOwned(obj); -% -stages_trial_counter_today.value = zeros(1,8); -% -end -% - -%% User Setting - -% - case 'User Setting' - - -if helper_vars_eval -GetSoloFunctionArgs(obj); -ClearHelperVarsNotOwned(obj); -% -CreateHelperVar(obj,'stages_trial_counter','value',zeros(1,8)); -CreateHelperVar(obj,'stages_trial_counter_today','value',zeros(1,8)); -CreateHelperVar(obj,'stages_trial_counter_oppSide','value',zeros(1,8)); -CreateHelperVar(obj,'stages_timeout_rate','value',zeros(1,8)); -CreateHelperVar(obj,'stages_violation_rate','value',zeros(1,8)); -CreateHelperVar(obj,'last_session_CP','value',0); -CreateHelperVar(obj,'stage_start_completed_trial','value',n_completed_trials,'forceinit',true); -% -end - - - -if stage_algorithm_eval -GetSoloFunctionArgs(obj); -ClearHelperVarsNotOwned(obj); -% -% Variables for warmup stage -cp_max = 5; -n_trial_warmup = 20; -starting_cp = 0.5; -warmup_completed = 0; -warm_up_on = value(ParamsSection_warmup_on); -random_prestim = value(ParamsSection_random_PreStim_time); -random_prego = value(ParamsSection_random_prego_time); -random_A1 = value(ParamsSection_random_A1_time); -prestim_min = value(ParamsSection_PreStim_time_Min); -prestim_max = value(ParamsSection_PreStim_time_Max); -prestim_time = value(ParamsSection_PreStim_time); -prego_min = value(ParamsSection_time_bet_aud1_gocue_Min); -prego_max = value(ParamsSection_time_bet_aud1_gocue_Max); -prego_time = value(ParamsSection_time_bet_aud1_gocue); -a1_time = value(ParamsSection_A1_time); -a1_time_min = value(ParamsSection_A1_time_Min); -a1_time_max = value(ParamsSection_A1_time_Max); - -stage_no = value(SessionDefinition_CURRENT_ACTIVE_STAGE); -ParamsSection_training_stage.value = stage_no; -callback(ParamsSection_training_stage); - -if n_completed_trials > value(stage_start_completed_trial) - % Update the helper vars - this_stage_trial_counter = value(stages_trial_counter); - this_stage_trial_counter(stage_no) = this_stage_trial_counter(stage_no) + 1; - SessionPerformanceSection_ntrials_stage.value = this_stage_trial_counter(stage_no); - callback(SessionPerformanceSection_ntrials_stage); - stages_trial_counter.value = this_stage_trial_counter; - - this_stage_trial_counter_today = value(stages_trial_counter_today); - this_stage_trial_counter_today(stage_no) = this_stage_trial_counter_today(stage_no) + 1; - SessionPerformanceSection_ntrials_stage_today.value = this_stage_trial_counter_today(stage_no); - callback(SessionPerformanceSection_ntrials_stage_today); - stages_trial_counter_today.value = this_stage_trial_counter_today; - - this_stage_timeout_percent = value(stages_timeout_rate); - this_stage_timeout_percent(stage_no) = ((this_stage_timeout_percent(stage_no) * this_stage_trial_counter(stage_no)) + double(timeout_history(end)))/(this_stage_trial_counter(stage_no) + 1); - SessionPerformanceSection_timeout_stage.value = this_stage_timeout_percent(stage_no); - callback(SessionPerformanceSection_timeout_stage); - stages_timeout_rate.value = this_stage_timeout_percent; - - this_stage_violation_percent = value(stages_violation_rate); - this_stage_violation_percent(stage_no) = ((this_stage_violation_percent(stage_no) * this_stage_trial_counter(stage_no)) + double(violation_history(end)))/(this_stage_trial_counter(stage_no) + 1); - SessionPerformanceSection_violation_stage.value = this_stage_violation_percent(stage_no); - callback(SessionPerformanceSection_violation_stage); - stages_violation_rate.value = this_stage_violation_percent; -end -% Warm Up If starting a new session -if warm_up_on == 1 - if n_completed_trials == 0 - ParamsSection_CP_duration.value = value(ParamsSection_init_CP_duration); - warmup_completed = 0; - elseif n_completed_trials == 1 - ParamsSection_CP_duration.value = starting_cp; - else - if value(ParamsSection_CP_duration) <= cp_max % warm up stage - if ~violation_history(end) && ~timeout_history(end) - increment = (cp_max - value(ParamsSection_CP_duration))/ (n_trial_warmup - 1); - ParamsSection_CP_duration.value = value(ParamsSection_CP_duration) + increment; - % Check if the values are within the required range - if value(ParamsSection_CP_duration) < starting_cp - ParamsSection_CP_duration.value = starting_cp; - end - if value(ParamsSection_CP_duration) >= cp_max - ParamsSection_CP_duration.value = cp_max; - warmup_completed = 1; - end - end - end - end -else - warmup_completed = 1; -end - -if n_completed_trials >= 1 - cp_length = value(ParamsSection_CP_duration) - value(ParamsSection_SettlingIn_time); - [ParamsSection_PreStim_time.value ,ParamsSection_A1_time.value,ParamsSection_time_bet_aud1_gocue.value] = param_time_within_range(warmup_completed,... - cp_length,prestim_min,prestim_max, random_prestim, prestim_time,... - a1_time_min,a1_time_max, random_A1, a1_time,prego_min,prego_max, random_prego, prego_time); - - callback(ParamsSection_PreStim_time); - callback(ParamsSection_A1_time); - callback(ParamsSection_time_bet_aud1_gocue); - ParamsSection_CP_duration = value(ParamsSection_SettlingIn_time) + value(ParamsSection_PreStim_time) + value(ParamsSection_A1_time) + value(ParamsSection_time_bet_aud1_gocue); -end -callback(ParamsSection_CP_duration); -% -end - - - -if completion_test_eval -GetSoloFunctionArgs(obj); -ClearHelperVarsNotOwned(obj); -clear('ans'); -% - -% -if exist('ans', 'var') -varargout{1}=logical(ans); clear('ans'); -else -varargout{1}=false; -end -end - - - -if eod_logic_eval -GetSoloFunctionArgs(obj); -ClearHelperVarsNotOwned(obj); -% -stages_trial_counter_today.value = zeros(1,8); -% -end -% - - -end - -end - -%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - -% - -function [prestim,A1,prego] = param_time_within_range(fixed_length,cp_length,range_min_prestim,range_max_prestim, is_random_prestim, provided_time_prestim,... - range_min_A1,range_max_A1, is_random_A1, provided_time_A1,range_min_prego,range_max_prego, is_random_prego, provided_time_prego) - -if fixed_length == 1 % warm up stage where cp length is increasing -% then calculate the range/typical value - if cp_length <= 0.3 - prestim = 0.1; - A1 = 0.1; - prego = 0.1; - else - range_size = round(0.3 * cp_length,1); - if range_size > 0.4 - step_size = 0.1; - else - step_size = 0.01; - end - - timerange = 0.1:step_size:range_size; - - if is_random_prestim == 1 - prestim = timerange(randi([1, numel(timerange)],1,1)); - else - if provided_time_prestim <= range_size - prestim = provided_time_prestim; - else - prestim = range_size; - end - - end - - if is_random_A1 == 1 - A1 = timerange(randi([1, numel(timerange)],1,1)); - else - if provided_time_A1 <= range_size - A1 = provided_time_A1; - else - A1 = range_size; - end - end - - prego = cp_length - prestim - A1; - - end - -else - - if is_random_prestim == 1 - range_size_prestim = range_max_prestim - range_min_prestim; - if range_size_prestim > 0.4 - step_size_prestim = 0.1; - else - step_size_prestim = 0.01; - end - time_range_prestim = range_min_prestim:step_size_prestim:range_max_prestim; - prestim = time_range_prestim(randi([1, numel(time_range_prestim)],1,1)); - else - prestim = provided_time_prestim; - end - - if is_random_A1 == 1 - range_size_A1 = range_max_A1 - range_min_A1; - if range_size_A1 > 0.4 - step_size_A1 = 0.1; - else - step_size_A1 = 0.01; - end - time_range_A1 = range_min_A1:step_size_A1:range_max_A1; - A1 = time_range_A1(randi([1, numel(time_range_A1)],1,1)); - else - A1 = provided_time_A1; - end - - if is_random_prego == 1 - range_size_prego = range_max_prego - range_min_prego; - if range_size_prego > 0.4 - step_size_prego = 0.1; - else - step_size_prego = 0.01; - end - time_range_prego = range_min_prego:step_size_prego:range_max_prego; - prego = time_range_prego(randi([1, numel(time_range_prego)],1,1)); - else - prego = provided_time_prego; - end - -end -end - - -% - -%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% From 8497d992b5458d8c2656d7de885df1293a8b60ba Mon Sep 17 00:00:00 2001 From: viktorpm Date: Mon, 31 Mar 2025 17:26:14 +0100 Subject: [PATCH 043/164] runtime error debugging on rig 31 --- ...ing_SessionDefinition_AutoTrainingStages.m | 27 ++++++++++++------- .../SessionPerformanceSection.m | 5 +--- 2 files changed, 19 insertions(+), 13 deletions(-) diff --git a/Protocols/@Arpit_CentrePokeTraining/Arpit_CentrePokeTraining_SessionDefinition_AutoTrainingStages.m b/Protocols/@Arpit_CentrePokeTraining/Arpit_CentrePokeTraining_SessionDefinition_AutoTrainingStages.m index db4cf26f..16088a96 100644 --- a/Protocols/@Arpit_CentrePokeTraining/Arpit_CentrePokeTraining_SessionDefinition_AutoTrainingStages.m +++ b/Protocols/@Arpit_CentrePokeTraining/Arpit_CentrePokeTraining_SessionDefinition_AutoTrainingStages.m @@ -146,11 +146,11 @@ ParamsSection_MaxSame.value = 3; callback(ParamsSection_MaxSame); stage_no = value(SessionDefinition_CURRENT_ACTIVE_STAGE); +this_stage_trial_counter = value(stages_trial_counter); % ParamsSection_training_stage.value = stage_no; % callback(ParamsSection_training_stage); if n_completed_trials > value(stage_start_completed_trial) - % Update the helper vars - this_stage_trial_counter = value(stages_trial_counter); + % Update the helper vars this_stage_trial_counter(stage_no) = this_stage_trial_counter(stage_no) + 1; SessionPerformanceSection_ntrials_stage.value = this_stage_trial_counter(stage_no); callback(SessionPerformanceSection_ntrials_stage); @@ -271,11 +271,11 @@ callback(ParamsSection_MaxSame); stage_no = value(SessionDefinition_CURRENT_ACTIVE_STAGE); +this_stage_trial_counter = value(stages_trial_counter); % ParamsSection_training_stage.value = stage_no; % callback(ParamsSection_training_stage); if n_completed_trials > value(stage_start_completed_trial) - % Update the helper vars - this_stage_trial_counter = value(stages_trial_counter); + % Update the helper vars this_stage_trial_counter(stage_no) = this_stage_trial_counter(stage_no) + 1; SessionPerformanceSection_ntrials_stage.value = this_stage_trial_counter(stage_no); callback(SessionPerformanceSection_ntrials_stage); @@ -294,7 +294,11 @@ stages_timeout_rate.value = this_stage_timeout_percent; end % Change the value of CP Duration -if n_completed_trials < 1 +if value(last_session_CP) == 0 && this_stage_trial_counter(stage_no) < 2 + ParamsSection_CP_duration.value = cp_min; % initialize to min_CP +end + +if n_completed_trials < 1 % intialize to min value at the start of each session/day ParamsSection_CP_duration.value = value(ParamsSection_init_CP_duration); else if ~timeout_history(end) && value(ParamsSection_CP_duration) < cp_max @@ -321,6 +325,7 @@ callback(ParamsSection_training_stage); ParamsSection(obj, 'Changed_Training_Stage'); SessionDefinition(obj, 'jump_to_stage', 'Introduce Violation for Centre Poke'); + last_session_CP.value = value(ParamsSection_CP_duration); end % if exist('ans', 'var') @@ -377,17 +382,17 @@ cp_minimum_increment = 0.001; stage_no = value(SessionDefinition_CURRENT_ACTIVE_STAGE); +this_stage_trial_counter = value(stages_trial_counter); +this_stage_trial_counter_today = value(stages_trial_counter_today); % ParamsSection_training_stage.value = stage_no; % callback(ParamsSection_training_stage); if n_completed_trials > value(stage_start_completed_trial) - % Update the helper vars - this_stage_trial_counter = value(stages_trial_counter); + % Update the helper vars this_stage_trial_counter(stage_no) = this_stage_trial_counter(stage_no) + 1; SessionPerformanceSection_ntrials_stage.value = this_stage_trial_counter(stage_no); callback(SessionPerformanceSection_ntrials_stage); stages_trial_counter.value = this_stage_trial_counter; - - this_stage_trial_counter_today = value(stages_trial_counter_today); + this_stage_trial_counter_today(stage_no) = this_stage_trial_counter_today(stage_no) + 1; SessionPerformanceSection_ntrials_stage_today.value = this_stage_trial_counter_today(stage_no); callback(SessionPerformanceSection_ntrials_stage_today); @@ -406,6 +411,10 @@ stages_violation_rate.value = this_stage_violation_percent; end % Change the value of CP Duration +if this_stage_trial_counter(stage_no) < 2 + ParamsSection_CP_duration.value = cp_min; % initialize to min_CP +end + if n_completed_trials < 1 ParamsSection_CP_duration.value = value(ParamsSection_init_CP_duration); else diff --git a/Protocols/@Arpit_CentrePokeTraining/SessionPerformanceSection.m b/Protocols/@Arpit_CentrePokeTraining/SessionPerformanceSection.m index f391df95..6e2049a6 100644 --- a/Protocols/@Arpit_CentrePokeTraining/SessionPerformanceSection.m +++ b/Protocols/@Arpit_CentrePokeTraining/SessionPerformanceSection.m @@ -112,8 +112,6 @@ timeout_recent.value = nan; end - timeout_stage.value = nan; - case {4,5,6,7,8} @@ -130,8 +128,7 @@ timeout_recent.value = nan; violation_recent.value = nan; end - - + end if nargout > 0 From b131f0f70c7609110cc329c86c63cf7bfd1f2008 Mon Sep 17 00:00:00 2001 From: Arpit Date: Wed, 2 Apr 2025 17:07:28 +0100 Subject: [PATCH 044/164] changed how stimuli distribution is generated --- .../Arpit_CentrePokeTraining.m | 14 +- .../CreateSamples_from_Distribution.m | 142 +++++++ .../@Arpit_CentrePokeTraining/ParamsSection.m | 11 +- .../StimulusSection.m | 369 ++++++++++-------- 4 files changed, 377 insertions(+), 159 deletions(-) create mode 100644 Protocols/@Arpit_CentrePokeTraining/CreateSamples_from_Distribution.m diff --git a/Protocols/@Arpit_CentrePokeTraining/Arpit_CentrePokeTraining.m b/Protocols/@Arpit_CentrePokeTraining/Arpit_CentrePokeTraining.m index ad888ccf..eb483d1a 100644 --- a/Protocols/@Arpit_CentrePokeTraining/Arpit_CentrePokeTraining.m +++ b/Protocols/@Arpit_CentrePokeTraining/Arpit_CentrePokeTraining.m @@ -67,6 +67,9 @@ DeclareGlobals(obj, 'ro_args', {'stimulus_history'}); SoloFunctionAddVars('StimulusSection', 'rw_args', 'stimulus_history'); + SoloParamHandle(obj, 'hit_history', 'value', []); + DeclareGlobals(obj, 'ro_args', {'hit_history'}); + SoloFunctionAddVars('SideSection', 'rw_args', 'hit_history'); SoundManagerSection(obj, 'init'); x = 5; y = 5; % Initial position on main GUI window @@ -155,11 +158,11 @@ ParamsSection(obj, 'prepare_next_trial'); % Run SessionDefinition *after* ParamsSection so we know whether the % trial was a violation or not + push_helper_vars_tosql(obj,n_done_trials); SessionDefinition(obj, 'next_trial'); SessionPerformanceSection(obj, 'evaluate'); - SoundManagerSection(obj, 'send_not_yet_uploaded_sounds'); StimulusSection(obj,'prepare_next_trial'); - + SoundManagerSection(obj, 'send_not_yet_uploaded_sounds'); [sma, prepare_next_trial_states] = Arpit_CentrePokeTrainingSMA(obj, 'prepare_next_trial'); % Default behavior of following call is that every 20 trials, the data @@ -176,7 +179,8 @@ prot_title.value=[mfilename ' on rig ' get_hostname ' : ' expmtr ', ' rname '. Started at ' datestr(now, 'HH:MM')]; end - try send_n_done_trials(obj); + try + send_n_done_trials(obj); end %% trial_completed @@ -224,11 +228,11 @@ % 'pre-Go cue went from %.3f to %.3f (delta=%.3f)\n', ... % 'Low = %.2f, High = %.2f'], ... % perf(1), perf(2), perf(3), perf(6), cp_durs(1), cp_durs(end), cp_durs(end)-cp_durs(1), classperf(1),classperf(2))); - + + pd.hits=hit_history(:); pd.sides=previous_sides(:); pd.viols=violation_history(:); pd.timeouts=timeout_history(:); -% pd.performance=tot_perf(:); pd.cp_durs=cp_durs(:); pd.stim1dur=stim1dur(:); diff --git a/Protocols/@Arpit_CentrePokeTraining/CreateSamples_from_Distribution.m b/Protocols/@Arpit_CentrePokeTraining/CreateSamples_from_Distribution.m new file mode 100644 index 00000000..23cdd90b --- /dev/null +++ b/Protocols/@Arpit_CentrePokeTraining/CreateSamples_from_Distribution.m @@ -0,0 +1,142 @@ +function samples = CreateSamples_from_Distribution(distributiontype,mu_val,sigma_val,range_min,range_max,n_samples) +%% + + +% samples = CreateSamples_from_Distribution('Anti_Half_Normal',log(4),log(4),log(10),1000,) + +% pairs = { ... +% 'Distribution_Type' 'Sinusoidal'; ... +% 'Range' [log(0.007) log(0.05)] ; ... +% 'Mean_val' mean([log(0.007),log(0.05)]) ; ... +% 'N_Samples' 1; ... +% 'sigma_val' (log(0.05) - log(0.007))/3 +% }; parseargs(varargin, pairs); + +%% + + +% Ensure the range is valid +if range_max <= range_min + error('Upper bound range_max must be greater than lower bound range_min.'); +end + +if contains(distributiontype,'normal','IgnoreCase',true) + if mu_val == range_min + distributiontype = 'Anti_Half_Normal'; + else + distributiontype = 'Half_Normal'; + end + +elseif contains(distributiontype,'sinusoidal','IgnoreCase',true) + if mu_val == range_min + distributiontype = 'Anti_Sinusoidal'; + else + distributiontype = 'Sinusoidal'; + end +end + +switch distributiontype + + case 'Sinusoidal' + + pdf_func = @(x) sin((pi / 2) * ((x - range_min) / (range_max - range_min))); % Define the PDF (Peak at max value) + A_val = 1 / integral(pdf_func, range_min, range_max); % Compute Normalization Constant A + pdf_func = @(x) A_val * sin((pi / 2) * ((x - range_min) / (range_max - range_min))); % the normalized PDF + cdf_func = @(x) integral(@(t) pdf_func(t), range_min, x); % Define the CDF as the cumulative integral of the normalized PDF + inv_cdf_func = @(u) range_min + (range_max - range_min) * (2/pi) * acos(1 - u); % the Inverse CDF + sample_single_point = @() inv_cdf_func(rand()); % Function for Sampling a Single Point + samples = arrayfun(@(x) sample_single_point(), 1:n_samples);% Sample Random Point + + + case 'Anti_Sinusoidal' + + pdf_func = @(x) sin((pi / 2) * ((range_max - x) / (range_max - range_min))); % Define the PDF (Peak at min value) + A_val = 1 / integral(pdf_func, range_min, range_max); % Compute Normalization Constant A + pdf_func = @(x) A_val * sin((pi / 2) * ((range_max - x) / (range_max - range_min))); % Define the normalized PDF + cdf_func = @(x) integral(@(t) pdf_func(t), range_min, x); % Define the CDF as the cumulative integral of the normalized PDF + inv_cdf_func = @(u) range_max - (range_max - range_min) * (2/pi) * acos(1 - u); % the Inverse CDF + sample_single_point = @() inv_cdf_func(rand()); % Function for Sampling a Single Point + samples = arrayfun(@(x) sample_single_point(), 1:n_samples); % Sample Random Points + + + case 'Anti_Half_Normal' + + pdf_func = @(x, A) A * exp(- (x - mu_val).^2 / (2 * sigma_val^2)); % Define the Half-Normal PDF (Peak at min value) + A_den = integral(@(x) exp(- (x - mu_val).^2 / (2 * sigma_val^2)), range_min, range_max); % Compute Normalization Constant A + A_sol = 1 / A_den; % Ensuring total probability integrates to 1 + + pdf_func = @(x) A_sol * exp(- (x - mu_val).^2 / (2 * sigma_val^2)); % Define the normalized PDF function + + cdf_func = @(x) (erf((x - mu_val) / (sqrt(2) * sigma_val)) - erf((range_min - mu_val) / (sqrt(2) * sigma_val))) ... % CDF formula for truncated normal distribution + / (erf((range_max - mu_val) / (sqrt(2) * sigma_val)) - erf((range_min - mu_val) / (sqrt(2) * sigma_val))); + + inv_cdf_func = @(u) mu_val + sqrt(2) * sigma_val * erfinv( ... % Inverse CDF function (solving for x in terms of U) + u * (erf((range_max - mu_val) / (sqrt(2) * sigma_val)) - erf((range_min - mu_val) / (sqrt(2) * sigma_val))) ... + + erf((range_min - mu_val) / (sqrt(2) * sigma_val))); + + sample_single_point = @() inv_cdf_func(rand()); % Function for Sampling a Single Point + samples = arrayfun(@(x) sample_single_point(), 1:n_samples); % Sample Random Points + + + case 'Half_Normal' + + pdf_func = @(x, A) A * exp(- (range_max - x).^2 / (2 * sigma_val^2)); % Define the Half-Normal PDF (Peak at max value) + A_den = integral(@(x) exp(- (range_max - x).^2 / (2 * sigma_val^2)), range_min, range_max); % Compute Normalization Constant A + A_sol = 1 / A_den; % Ensuring total probability integrates to 1 + + pdf_func = @(x) A_sol * exp(- (range_max - x).^2 / (2 * sigma_val^2)); % Define the final normalized PDF function + + cdf_func = @(x) (erf((x - range_max) / (sqrt(2) * sigma_val)) - erf((range_min - range_max) / (sqrt(2) * sigma_val))) ... % CDF formula for truncated normal distribution + / (erf((range_max - range_max) / (sqrt(2) * sigma_val)) - erf((range_min - range_max) / (sqrt(2) * sigma_val))); + + inv_cdf_func = @(u) range_max - sqrt(2) * sigma_val * erfinv( ... % Inverse CDF function (solving for x in terms of U) + u * (erf((range_max - range_min) / (sqrt(2) * sigma_val)))); + + sample_single_point = @() inv_cdf_func(rand()); % Function for Sampling a Single Point + samples = arrayfun(@(x) sample_single_point(), 1:n_samples); % Sample Random Points + +end + +% check all the samples are within the range (excluding the range) +out_of_range_samples = find(samples <= range_min & samples >= range_max); +if length(out_of_range_samples) >= 1 + samples_replacement = arrayfun(@(x) sample_single_point(), 1:length(out_of_range_samples)); % + samples(out_of_range_samples) = samples_replacement; +end +% Recheck and if its again out of range then replace it in while loop ( was +% trying to avoid it) +out_of_range_samples_new = find(samples <= range_min & samples >= range_max); +if length(out_of_range_samples) >= 1 + for i = length(out_of_range_samples_new) + sample_new = arrayfun(@(x) sample_single_point(), 1); + while sample_new <= range_min || sample_new >= range_max + sample_new = arrayfun(@(x) sample_single_point(), 1); + end + samples(out_of_range_samples_new(i)) = sample_new; + end +end + +% %% Debugging - Validate CDF and Inverse CDF + + % % Create a fine grid of x values + % x_vals = linspace(range_min, range_max, 1000); + % % Evaluate PDF and CDF + % pdf_vals = pdf_func(x_vals); + % cdf_vals = cdf_func(x_vals); + % % Generate uniform random samples and apply inverse CDF + % U = rand(10000,1); + % sampled_x = inv_cdf_func(U); + % %% Plot PDF and CDF + % figure; + % % PDF Plot + % subplot(2,1,1); + % plot(x_vals, pdf_vals, 'b', 'LineWidth', 2); + % xlabel('x'); ylabel('PDF'); + % title('Half-Normal PDF'); + % grid on; + % % CDF Plot + % subplot(2,1,2); + % plot(x_vals, cdf_vals, 'r', 'LineWidth', 2); + % xlabel('x'); ylabel('CDF'); + % title('Cumulative Distribution Function (CDF)'); + % grid on; \ No newline at end of file diff --git a/Protocols/@Arpit_CentrePokeTraining/ParamsSection.m b/Protocols/@Arpit_CentrePokeTraining/ParamsSection.m index 6414cd7a..5e4755eb 100644 --- a/Protocols/@Arpit_CentrePokeTraining/ParamsSection.m +++ b/Protocols/@Arpit_CentrePokeTraining/ParamsSection.m @@ -338,6 +338,7 @@ %% update violation, timeout, previous_sides, etc was_viol=false; was_timeout=false; + was_hit=false; if n_done_trials>0 if ~isempty(parsed_events) if isfield(parsed_events,'states') @@ -355,7 +356,15 @@ timeout_history.value=[timeout_history(:); was_timeout]; ParamsSection(obj,'update_side_history'); - + % Update Hit History + if ~was_viol && ~was_timeout + was_hit=rows(parsed_events.states.second_hit_state)==0; + hit_history.value=[hit_history(:); was_hit]; + + else + % There was a violation or timeout + hit_history.value=[hit_history(:); nan]; + end end %% Choose Side for the Next Trial diff --git a/Protocols/@Arpit_CentrePokeTraining/StimulusSection.m b/Protocols/@Arpit_CentrePokeTraining/StimulusSection.m index eb9f2b18..524b7f11 100644 --- a/Protocols/@Arpit_CentrePokeTraining/StimulusSection.m +++ b/Protocols/@Arpit_CentrePokeTraining/StimulusSection.m @@ -55,12 +55,45 @@ next_row(y); next_row(y); - MenuParam(obj, 'Rule', {'S2>S_boundry Left','S2>S_boundry Right'}, ... - 'S2>S_boundry Left', x, y, 'labelfraction', 0.35, 'TooltipString', sprintf(['\nThis bottom determines the rule\n', ... - '\n''S2>S_boundry Left'' means if Aud2 > Aud_boundry then reward will be delivered from the left water spout and if Aud2 < Aud_boundry then water comes form right\n',... - '\n''S2>S_boundry Right'' means if Aud2 < Aud_boundry then reward will be delivered from the left water spout and if Aud2 > Aud_boundry then water comes from right\n'])); + MenuParam(obj, 'Rule', {'S1>S_boundary Left','S1>S_boundary Right'}, ... + 'S1>S_boundary Left', x, y, 'labelfraction', 0.35, 'TooltipString', sprintf(['\nThis buttom determines the rule\n', ... + '\n''S1>S_boundary Left'' means if Aud1 > Aud_boundry then reward will be delivered from the left water spout and if Aud1 < Aud_boundry then water comes from right\n',... + '\n''S1>S_boundary Right'' means if Aud1 < Aud_boundry then reward will be delivered from the left water spout and if Aud1 > Aud_boundry then water comes from right\n'])); next_row(y, 1) + MenuParam(obj, 'Prob_Dist_Left', {'Uniform','Half Normal','Normal','Sinusoidal','Anti Half Normal','Anti Sinusoidal','Monotonic Increase'}, ... + 'Uniform', x, y, 'labelfraction', 0.35, 'TooltipString', sprintf(['\n Different Probability Distributions for Category A.\n', ... + '\n''Normal - the mean is at mid point of range. Half Normal - truncated normal with mean at boundary.\n',... + '\n''Anti Half Normal - the mean/max is at the side edge of the range.\n',... + '\n''Sinosidal - using sine function instead of half normal and Anti Sinusoidal is when max is at the edge, same as anti half normal.\n',... + '\n''Monotonic - increases and decreases in steps from min to max (removing the random'])); + set_callback(Prob_Dist_Left, {mfilename, 'Cal_Mean'}); + next_row(y); + DispParam(obj, 'mean_Left', 0.01, x,y,'label','μ Left','TooltipString','mean/max log stim value for the left side distribution'); + next_row(y); + DispParam(obj, 'sigma_Left', 0.01, x,y,'label','μ Left','TooltipString','mean/max log stim value for the left side distribution'); + next_row(y); + NumeditParam(obj, 'sigma_range_Left', 1, x,y,'label','3σ Left','TooltipString',sprintf(['\n A way to reduce the range and increase more distribution towards mean\n', ... + '\n''A value b/w range [0.2 - 1] is acceptable, signifying 3 Sigma (99.7%) value for the left side distribution'])); + set_callback(sigma_range_Left, {mfilename, 'Cal_Sigma'}); + next_row(y); + + MenuParam(obj, 'Prob_Dist_Right', {'Uniform','Half Normal','Normal','Sinusoidal','Anti Half Normal','Anti Sinusoidal'}, ... + 'Uniform', x, y, 'labelfraction', 0.35, 'TooltipString', sprintf(['\n Different Probability Distributions for Category A.\n', ... + '\n''Normal - the mean is at mid point of range (side edge - boundary). Half Normal - truncated normal with mean at boundary.\n',... + '\n''Anti Half Normal - the mean/max is at the side edge of the range.\n',... + '\n''Sinosidal - using sine function instead of half normal and Anti Sinusoidal is when max is at the edge, same as anti half normal'])); + set_callback(Prob_Dist_Right, {mfilename, 'Cal_Mean'}); + next_row(y); + DispParam(obj, 'mean_Right', 0.01, x,y,'label','μ Right','TooltipString','mean/max log stim value for the right side distribution'); + next_row(y); + DispParam(obj, 'sigma_Right', 0.01, x,y,'label','μ Left','TooltipString','mean/max log stim value for the left side distribution'); + next_row(y); + DispParam(obj, 'sigma_range_Right', 1, x,y,'label','3σ Right','TooltipString',sprintf(['\n A way to reduce the range and increase more distribution towards mean\n', ... + '\n''A value b/w range [0.2 - 1] is acceptable, signifying 3 Sigma (99.7%) value for the right side distribution'])); + set_callback(sigma_range_Right, {mfilename, 'Cal_Sigma'}); + next_row(y); + next_column(x); y=5; next_row(y, 1) @@ -78,17 +111,21 @@ next_row(y); NumeditParam(obj,'minS1',0.007,x,y,'label','minS1','TooltipString','min sigma value for AUD1'); set_callback(minS1, {mfilename, 'Cal_Boundary'}); + set_callback(minS1, {mfilename, 'Cal_Mean'}); next_row(y); NumeditParam(obj,'maxS1',0.05,x,y,'label','maxS1','TooltipString','max sigma value for AUD1'); set_callback(maxS1, {mfilename, 'Cal_Boundary'}); + set_callback(maxS1, {mfilename, 'Cal_Mean'}); next_row(y); DispParam(obj, 'A1_sigma', 0.01, x,y,'label','A1_sigma','TooltipString','Sigma value for the first stimulus'); next_row(y); NumeditParam(obj,'minF1',4,x,y,'label','minF1','TooltipString','min frequency value for AUD1'); set_callback(minF1, {mfilename, 'Cal_Boundary'}); + set_callback(minF1, {mfilename, 'Cal_Mean'}); next_row(y); NumeditParam(obj,'maxF1',10,x,y,'label','maxF1','TooltipString','max frequency value for AUD1'); set_callback(maxF1, {mfilename, 'Cal_Boundary'}); + set_callback(maxF1, {mfilename, 'Cal_Mean'}); next_row(y); DispParam(obj, 'A1_freq', 0.01, x,y,'label','A1_freq','TooltipString','Sigma value for the first stimulus'); next_row(y); @@ -97,6 +134,7 @@ MenuParam(obj, 'mu_location', {'center', 'side'}, ... 'center', x, y, 'labelfraction', 0.35, 'TooltipString', sprintf('\nLocation of boundary')); set_callback(mu_location, {mfilename, 'Cal_Boundary'}); + set_callback(mu_location, {mfilename, 'Cal_Mean'}); next_row(y); ToggleParam(obj, 'frequency_categorization', 0, x,y,... 'OnString', 'Frequency(Tone)',... @@ -104,16 +142,14 @@ 'TooltipString', sprintf('If on (black) then it enables the presentation of pure tones')); set_callback(frequency_categorization, {mfilename, 'Cal_Boundary'}); set_callback(frequency_categorization, {mfilename, 'FrequencyCategorization'}); + set_callback(frequency_categorization, {mfilename, 'Cal_Mean'}); make_invisible(maxF1);make_invisible(minF1);make_invisible(A1_freq); next_row(y); - MenuParam(obj, 'DistributionType', {'uniform','unim-unif', 'unif-unim', 'unimodal', 'bimodal', 'asym-unif', 'Unim-Unif', 'Unif-Unim'}, ... - 'uniform', x, y, 'labelfraction', 0.35, 'TooltipString', sprintf('\nDifferent distributions')); StimulusSection(obj,'plot_stimuli'); case 'prepare_next_trial' - ParamsSection(obj,'get_current_side'); StimulusSection(obj,'pick_current_stimulus'); srate=SoundManagerSection(obj,'get_sample_rate'); Fs=srate; @@ -121,9 +157,8 @@ if frequency_categorization % produce the tone - A1_freq.value=exp(value(thisstim)); - A1_sigma.value=0; - A1 = value(thisstim); + A1_freq.value = value(thisstim); + A1 = value(thisstimlog(n_completed_trials+1)); dur1 = A1_time*1000; bal=0; freq1=A1_freq*1000; @@ -138,9 +173,8 @@ AUD1 = RW; else % produce noise pattern - A1_sigma.value=exp(value(thisstim)); - A1_freq.value=0; - A1 = value(thisstim); + A1_sigma.value = value(thisstim); + A1 = value(thisstimlog(n_completed_trials+1)); [rawA1, rawA2, normA1, normA2]=noisestim(1,1,T,value(fcut),Fs,value(filter_type)); modulator=singlenoise(1,T,[value(lfreq) value(hfreq)],Fs,'BUTTER'); AUD1=normA1(1:A1_time*srate).*modulator(1:A1_time*srate).*A1_sigma; @@ -160,7 +194,7 @@ set(value(h1), 'YData', value(A1), 'color',[0.8 0.4 0.1],'markerfacecolor',[0.8 0.4 0.1]); end - if n_completed_trials >0 + if n_completed_trials > 0 if ~violation_history(n_completed_trials) && ~timeout_history(n_completed_trials) StimulusSection(obj,'update_stimulus_history'); else @@ -168,116 +202,6 @@ end end - - %% Case make_stimuli - case 'make_stimuli' - if nargout>0 - if frequency_categorization - stim_min_log = log(value(minF1)); - stim_max_log = log(value(maxF1)); - else - stim_min_log = log(value(minS1)); - stim_max_log = log(value(maxS1)); - end - - stim_range_log = stim_max_log - stim_min_log; - if strcmp(DistributionType,'uniform') - stim_i_log = stim_min_log + rand() * (stim_max_log - stim_min_log); - - elseif strcmp(DistributionType,'unimodal') - mu_1 = value(boundary); - sigma = 0.4; - stim_tot = []; - for i = 1:1000 - stim_i_log = stim_max_log +1; - while (stim_i_log > stim_max_log) ||(stim_i_log < stim_min_log) - stim_i_log = normrnd(mu_1,sigma); - end - stim_tot = [stim_tot stim_i_log]; - end - - % stim_tot = [] - % for i = 1:1000 - % stim_i_log = stim_min_log + rand() * (stim_max_log - stim_min_log); - % stim_i_log = stim_i_log + 0.4 * cos(2 * pi * 0.5 * (stim_i_log - stim_min_log)/(stim_max_log - stim_min_log)) - % stim_i_log = stim_i_log - 0.4; - % stim_i_log = stim_min_log + (stim_i_log - stim_min_log) * stim_range_log/(stim_range_log - 0.8) - % stim_tot = [stim_tot stim_i_log]; - % end - - elseif strcmp(DistributionType,'bimodal') - mu_1 = (stim_min_log + value(boundary))/2; - mu_2 = (stim_max_log + value(boundary))/2; - sigma = 0.2; - % stim_tot = []; - % for i = 1:1000 - stim_i_log = stim_max_log + 1; - while (stim_i_log > stim_max_log) ||(stim_i_log < stim_min_log) - if rand()>0.5 - stim_i_log = normrnd(mu_1,sigma); - else - stim_i_log = normrnd(mu_2,sigma); - end - end - % stim_tot = [stim_tot stim_i_log]; - % end - % stim_tot = [stim_tot stim_i_log]; - - - % stim_tot = [] - % for i = 1:1000 - % stim_i_log = stim_min_log + rand() * (stim_max_log - stim_min_log); - % stim_i_log = stim_i_log + 0.07 * cos(2 * pi * 1.5 * (stim_i_log - stim_min_log)/(stim_max_log - stim_min_log)) - % stim_i_log = stim_i_log - 0.07; - % stim_i_log = stim_min_log + (stim_i_log - stim_min_log) * stim_range_log/(stim_range_log - 0.14) - % stim_tot = [stim_tot stim_i_log]; - % end - - elseif strcmp(DistributionType,'unim-unif') - stim_i_log = stim_min_log + rand() * (stim_max_log - stim_min_log); - if stim_i_log < (stim_max_log + stim_min_log) / 2 - for i = 1:30 - stim_i_log = stim_i_log - 0.01 * sin(2 * pi * 0.5 * (stim_i_log - stim_min_log) / (stim_range_log/2)); - end - end - elseif strcmp(DistributionType,'unif-unim') - stim_i_log = stim_min_log + rand() * (stim_max_log - stim_min_log); - if stim_i_log > (stim_max_log + stim_min_log) / 2 - for i = 1:30 - stim_i_log = stim_i_log - 0.01 * sin(2 * pi * 0.5 * (stim_i_log - stim_min_log) / (stim_range_log/2)); - end - end - elseif strcmp(DistributionType,'Unim-Unif') - stim_i_log = stim_min_log + rand() * (stim_max_log - stim_min_log); - if stim_i_log < (stim_max_log + stim_min_log) / 2 - for i = 1:30 - stim_i_log = stim_i_log + 0.01 * sin(2 * pi * 0.5 * (stim_i_log - stim_min_log) / (stim_range_log/2)); - end - end - elseif strcmp(DistributionType,'Unif-Unim') - stim_i_log = stim_min_log + rand() * (stim_max_log - stim_min_log); - if stim_i_log > (stim_max_log + stim_min_log) / 2 - for i = 1:30 - stim_i_log = stim_i_log + 0.01 * sin(2 * pi * 0.5 * (stim_i_log - stim_min_log) / (stim_range_log/2)); - end - end - elseif strcmp(DistributionType,'asym-unif') - % stim_tot = []; - stim_half_log = value(boundary) - stim_min_log; - % for i = 1:1000 - stim_i_log = stim_min_log + rand() * (stim_max_log - stim_min_log); - if stim_i_log < (stim_min_log + value(boundary)) / 2 - for i = 1:30 - stim_i_log = stim_i_log - 0.01 * sin(2 * pi * 0.5 * (stim_i_log - stim_min_log) / (stim_half_log/2)); - end - end - % stim_tot = [stim_tot stim_i_log]; - % end - end - x = stim_i_log; - end - - %% Case pick_current_stimulus case 'pick_current_stimulus' if frequency_categorization @@ -287,33 +211,72 @@ stim_min_log = log(value(minS1)); stim_max_log = log(value(maxS1)); end - if strcmp(Rule,'S2>S_boundry Left') - if strcmp(ThisTrial, 'LEFT') - stim_i_log = stim_min_log; - while stim_i_log <= value(boundary) - stim_i_log = StimulusSection(obj,'make_stimuli'); - end - else - stim_i_log = stim_max_log; - while stim_i_log >= value(boundary) - stim_i_log = StimulusSection(obj,'make_stimuli'); - end + + if strcmpi(ThisTrial, 'LEFT') + dist_type = value(Prob_Dist_Left); + dist_mean = value(mean_Left); + dist_sigma = value(sigma_Left); + dist_range_multiplier = value(sigma_range_Left); + if strcmp(Rule,'S1>S_boundary Left') + edge_max = stim_max_log; + edge_min = value(boundary); + edge_max = edge_min + dist_range_multiplier * (edge_max - edge_min); + else % the rule is S1>S_boundary Right + edge_min = stim_min_log; + edge_max = value(boundary); + edge_min = edge_max - dist_range_multiplier * (edge_max - edge_min); + end + + else % trial is Right + dist_type = value(Prob_Dist_Right); + dist_mean = value(mean_Right); + dist_sigma = value(sigma_Right); + dist_range_multiplier = value(sigma_range_Right); + if strcmp(Rule,'S1>S_boundary Right') + edge_max = stim_max_log; + edge_min = value(boundary); + edge_max = edge_min + dist_range_multiplier * (edge_max - edge_min); + else % the rule is S1>S_boundary Left + edge_min = stim_min_log; + edge_max = value(boundary); + edge_min = edge_max - dist_range_multiplier * (edge_max - edge_min); end - elseif strcmp(Rule,'S2>S_boundry Right') - if strcmp(ThisTrial, 'LEFT') - stim_i_log = stim_max_log; - while stim_i_log >= value(boundary) - stim_i_log = StimulusSection(obj,'make_stimuli'); + end + + % Create a Stimuli with the selected Distribution and Side + switch dist_type + + case 'Uniform' % uniform distribution + stim_i_log = random('Uniform',edge_min,edge_max); + + case 'Half Normal' + if edge_min == value(boundary) + stim_i_log = random('Half Normal',dist_mean,dist_sigma); + while stim_i_log < edge_min || stim_i_log > edge_max + stim_i_log = random('Half Normal',dist_mean,dist_sigma); + end + else + stim_i_log = CreateSamples_from_Distribution('normal',dist_mean,dist_sigma,edge_min,edge_max,1); end - else - stim_i_log = stim_min_log; - while stim_i_log <= value(boundary) - stim_i_log = StimulusSection(obj,'make_stimuli'); + + case 'Anti Half Normal' + + stim_i_log = CreateSamples_from_Distribution('normal',dist_mean,dist_sigma,edge_min,edge_max,1); + + case 'Normal' + stim_i_log = random('Normal',dist_mean,dist_sigma); + while stim_i_log < edge_min || stim_i_log > edge_max + stim_i_log = random('Normal',dist_mean,dist_sigma); end - end + + case 'Sinusoidal' | 'Anti Sinusoidal' + + stim_i_log = CreateSamples_from_Distribution('Sinusoidal',dist_mean,dist_sigma,edge_min,edge_max,1); + end - thisstim.value=stim_i_log; - thisstimlog(n_completed_trials+1)= stim_i_log; + + thisstim.value=exp(stim_i_log); + thisstimlog(n_completed_trials+1) = stim_i_log; %% Case plot_pais case 'plot_stimuli' @@ -351,9 +314,7 @@ set(value(ax),'Fontsize',15) xlabel('S1','FontSize',16,'FontName','Cambria Math') - ParamsSection(obj,'get_current_side'); StimulusSection(obj,'pick_current_stimulus'); - A1 = value(thisstim); %% plot the stimulus; @@ -377,6 +338,112 @@ elseif strcmp(mu_location,'side') boundary.value = (min_val + val_boundary)/2; end + + %% Updated Mean/Max for Each Side based upon Distribution Selected + case 'Cal_Mean' + + if frequency_categorization + edge_max = log(value(maxF1)); + edge_min = log(value(minF1)); + else + edge_max = log(value(maxS1)); + edge_min = log(value(minS1)); + end + + % Calculation for Left Side + + % Sigma + + dist_sigma_multiplier = value(sigma_range_Left); + if dist_sigma_multiplier < 0.2 + dist_sigma_multiplier = 0.2; + end + if dist_sigma_multiplier > 1 + dist_sigma_multiplier = 1; + end + sigma_Left.value = (dist_sigma_multiplier * (edge_max - edge_min)) / 3; % as we asked user to provide 3 sigma + + % Mean + if matches(Prob_Dist_Left,{'Uniform','Half Normal','Sinusoidal'}) + mean_Left.value = value(boundary); + else + if strcmp(Rule,'S1>S_boundary Left') + if matches(Prob_Dist_Left,{'Anti Half Normal','Anti Sinusoidal'}) + mean_Left.value = edge_max; + elseif matches(Prob_Dist_Left,'Normal') + mean_Left.value = (edge_max + value(boundary))/2; + end + else + if matches(Prob_Dist_Left,{'Anti Half Normal','Anti Sinusoidal'}) + mean_Left.value = edge_min; + elseif matches(Prob_Dist_Left,'Normal') + mean_Left.value = (edge_min + value(boundary))/2; + end + end + end + + % Calculation for Right Side + + % Sigma + dist_sigma_multiplier = value(sigma_range_Right); + if dist_sigma_multiplier < 0.2 + dist_sigma_multiplier = 0.2; + end + if dist_sigma_multiplier > 1 + dist_sigma_multiplier = 1; + end + sigma_Right.value = (dist_sigma_multiplier * (edge_max - edge_min)) / 3; % as we asked user to provide 3 sigma + + % Mean + if matches(Prob_Dist_Right,{'Uniform','Half Normal','Sinusoidal'}) + mean_Right.value = value(boundary); + else + if strcmp(Rule,'S1>S_boundary Right') + if matches(Prob_Dist_Right,{'Anti Half Normal','Anti Sinusoidal'}) + mean_Right.value = edge_max; + elseif matches(Prob_Dist_Right,'Normal') + mean_Right.value = (edge_max + value(boundary))/2; + end + else + if matches(Prob_Dist_Right,{'Anti Half Normal','Anti Sinusoidal'}) + mean_Right.value = edge_min; + elseif matches(Prob_Dist_Right,'Normal') + mean_Right.value = (edge_min + value(boundary))/2; + end + end + end + + %% Calculate Sigma + case 'Cal_Sigma' + + if frequency_categorization + edge_max = log(value(maxF1)); + edge_min = log(value(minF1)); + else + edge_max = log(value(maxS1)); + edge_min = log(value(minS1)); + end + + % Calculation for Left Side Sigma + dist_sigma_multiplier = value(sigma_range_Left); + if dist_sigma_multiplier < 0.2 + dist_sigma_multiplier = 0.2; + end + if dist_sigma_multiplier > 1 + dist_sigma_multiplier = 1; + end + sigma_Left.value = (dist_sigma_multiplier * (edge_max - edge_min)) / 3; % as we asked user to provide 3 sigma + + % Calculation for Right Side Sigma + dist_sigma_multiplier = value(sigma_range_Right); + if dist_sigma_multiplier < 0.2 + dist_sigma_multiplier = 0.2; + end + if dist_sigma_multiplier > 1 + dist_sigma_multiplier = 1; + end + sigma_Right.value = (dist_sigma_multiplier * (edge_max - edge_min)) / 3; % as we asked user to provide 3 sigma + %% Case frequency ON case 'FrequencyCategorization' @@ -409,10 +476,6 @@ delete_sphandle('owner', ['^@' class(obj) '$'], ... 'fullname', ['^' mfilename]); - %% Case update_stimuli - case 'update_stimuli' - StimulusSection(obj,'plot_stimuli'); - case 'update_stimulus_history' ps=value(stimulus_history); ps(n_completed_trials)=value(thisstimlog(n_completed_trials)); From ae1f165e4f38eb2c907de0e4a36d58ac74cbd47a Mon Sep 17 00:00:00 2001 From: viktorpm Date: Wed, 2 Apr 2025 17:37:46 +0100 Subject: [PATCH 045/164] debug --- .../StimulusSection.m | 30 +++++++++---------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/Protocols/@Arpit_CentrePokeTraining/StimulusSection.m b/Protocols/@Arpit_CentrePokeTraining/StimulusSection.m index 524b7f11..0b588c35 100644 --- a/Protocols/@Arpit_CentrePokeTraining/StimulusSection.m +++ b/Protocols/@Arpit_CentrePokeTraining/StimulusSection.m @@ -59,10 +59,10 @@ 'S1>S_boundary Left', x, y, 'labelfraction', 0.35, 'TooltipString', sprintf(['\nThis buttom determines the rule\n', ... '\n''S1>S_boundary Left'' means if Aud1 > Aud_boundry then reward will be delivered from the left water spout and if Aud1 < Aud_boundry then water comes from right\n',... '\n''S1>S_boundary Right'' means if Aud1 < Aud_boundry then reward will be delivered from the left water spout and if Aud1 > Aud_boundry then water comes from right\n'])); - next_row(y, 1) + next_row(y, 1);next_row(y, 1); - MenuParam(obj, 'Prob_Dist_Left', {'Uniform','Half Normal','Normal','Sinusoidal','Anti Half Normal','Anti Sinusoidal','Monotonic Increase'}, ... - 'Uniform', x, y, 'labelfraction', 0.35, 'TooltipString', sprintf(['\n Different Probability Distributions for Category A.\n', ... + MenuParam(obj, 'Prob_Dist_Left', {'Uniform','Half Normal','Normal','Sinusoidal','Anti Half Normal','Anti Sinusoidal','Monotonic Increase'}, ... + 'Uniform', x, y,'label','Left Dist', 'labelfraction', 0.35, 'TooltipString', sprintf(['\n Different Probability Distributions for Category A.\n', ... '\n''Normal - the mean is at mid point of range. Half Normal - truncated normal with mean at boundary.\n',... '\n''Anti Half Normal - the mean/max is at the side edge of the range.\n',... '\n''Sinosidal - using sine function instead of half normal and Anti Sinusoidal is when max is at the edge, same as anti half normal.\n',... @@ -71,15 +71,15 @@ next_row(y); DispParam(obj, 'mean_Left', 0.01, x,y,'label','μ Left','TooltipString','mean/max log stim value for the left side distribution'); next_row(y); - DispParam(obj, 'sigma_Left', 0.01, x,y,'label','μ Left','TooltipString','mean/max log stim value for the left side distribution'); + DispParam(obj, 'sigma_Left', 0.01, x,y,'label','σ Left','TooltipString','mean/max log stim value for the left side distribution'); next_row(y); NumeditParam(obj, 'sigma_range_Left', 1, x,y,'label','3σ Left','TooltipString',sprintf(['\n A way to reduce the range and increase more distribution towards mean\n', ... '\n''A value b/w range [0.2 - 1] is acceptable, signifying 3 Sigma (99.7%) value for the left side distribution'])); set_callback(sigma_range_Left, {mfilename, 'Cal_Sigma'}); - next_row(y); + next_row(y); next_row(y); MenuParam(obj, 'Prob_Dist_Right', {'Uniform','Half Normal','Normal','Sinusoidal','Anti Half Normal','Anti Sinusoidal'}, ... - 'Uniform', x, y, 'labelfraction', 0.35, 'TooltipString', sprintf(['\n Different Probability Distributions for Category A.\n', ... + 'Uniform', x, y, 'label','Right Dist', 'labelfraction', 0.35, 'TooltipString', sprintf(['\n Different Probability Distributions for Category A.\n', ... '\n''Normal - the mean is at mid point of range (side edge - boundary). Half Normal - truncated normal with mean at boundary.\n',... '\n''Anti Half Normal - the mean/max is at the side edge of the range.\n',... '\n''Sinosidal - using sine function instead of half normal and Anti Sinusoidal is when max is at the edge, same as anti half normal'])); @@ -87,9 +87,9 @@ next_row(y); DispParam(obj, 'mean_Right', 0.01, x,y,'label','μ Right','TooltipString','mean/max log stim value for the right side distribution'); next_row(y); - DispParam(obj, 'sigma_Right', 0.01, x,y,'label','μ Left','TooltipString','mean/max log stim value for the left side distribution'); + DispParam(obj, 'sigma_Right', 0.01, x,y,'label','σ Right','TooltipString','mean/max log stim value for the left side distribution'); next_row(y); - DispParam(obj, 'sigma_range_Right', 1, x,y,'label','3σ Right','TooltipString',sprintf(['\n A way to reduce the range and increase more distribution towards mean\n', ... + NumeditParam(obj, 'sigma_range_Right', 1, x,y,'label','3σ Right','TooltipString',sprintf(['\n A way to reduce the range and increase more distribution towards mean\n', ... '\n''A value b/w range [0.2 - 1] is acceptable, signifying 3 Sigma (99.7%) value for the right side distribution'])); set_callback(sigma_range_Right, {mfilename, 'Cal_Sigma'}); next_row(y); @@ -364,7 +364,7 @@ sigma_Left.value = (dist_sigma_multiplier * (edge_max - edge_min)) / 3; % as we asked user to provide 3 sigma % Mean - if matches(Prob_Dist_Left,{'Uniform','Half Normal','Sinusoidal'}) + if matches(value(Prob_Dist_Left),{'Uniform','Half Normal','Sinusoidal'}) mean_Left.value = value(boundary); else if strcmp(Rule,'S1>S_boundary Left') @@ -374,9 +374,9 @@ mean_Left.value = (edge_max + value(boundary))/2; end else - if matches(Prob_Dist_Left,{'Anti Half Normal','Anti Sinusoidal'}) + if matches(value(Prob_Dist_Left),{'Anti Half Normal','Anti Sinusoidal'}) mean_Left.value = edge_min; - elseif matches(Prob_Dist_Left,'Normal') + elseif matches(value(Prob_Dist_Left),'Normal') mean_Left.value = (edge_min + value(boundary))/2; end end @@ -395,17 +395,17 @@ sigma_Right.value = (dist_sigma_multiplier * (edge_max - edge_min)) / 3; % as we asked user to provide 3 sigma % Mean - if matches(Prob_Dist_Right,{'Uniform','Half Normal','Sinusoidal'}) + if matches(value(Prob_Dist_Right),{'Uniform','Half Normal','Sinusoidal'}) mean_Right.value = value(boundary); else if strcmp(Rule,'S1>S_boundary Right') - if matches(Prob_Dist_Right,{'Anti Half Normal','Anti Sinusoidal'}) + if matches(value(Prob_Dist_Right),{'Anti Half Normal','Anti Sinusoidal'}) mean_Right.value = edge_max; - elseif matches(Prob_Dist_Right,'Normal') + elseif matches(value(Prob_Dist_Right),'Normal') mean_Right.value = (edge_max + value(boundary))/2; end else - if matches(Prob_Dist_Right,{'Anti Half Normal','Anti Sinusoidal'}) + if matches(value(Prob_Dist_Right),{'Anti Half Normal','Anti Sinusoidal'}) mean_Right.value = edge_min; elseif matches(Prob_Dist_Right,'Normal') mean_Right.value = (edge_min + value(boundary))/2; From 9732fe4b8a9ca816755dee9340a7543d427b31d2 Mon Sep 17 00:00:00 2001 From: Arpit Date: Wed, 2 Apr 2025 21:07:04 +0100 Subject: [PATCH 046/164] bug fix StimulusSection --- .../StimulusSection.asv | 512 ++++++++++++++++++ .../StimulusSection.m | 14 +- 2 files changed, 518 insertions(+), 8 deletions(-) create mode 100644 Protocols/@Arpit_CentrePokeTraining/StimulusSection.asv diff --git a/Protocols/@Arpit_CentrePokeTraining/StimulusSection.asv b/Protocols/@Arpit_CentrePokeTraining/StimulusSection.asv new file mode 100644 index 00000000..ee29363a --- /dev/null +++ b/Protocols/@Arpit_CentrePokeTraining/StimulusSection.asv @@ -0,0 +1,512 @@ + + +function [x, y] = StimulusSection(obj, action, varargin) + +GetSoloFunctionArgs(obj); + +switch action + + % ------------------------------------------------------------------ + % INIT + % ------------------------------------------------------------------ + + case 'init' + if length(varargin) < 2 + error('Need at least two arguments, x and y position, to initialize %s', mfilename); + end + x = varargin{1}; y = varargin{2}; + + ToggleParam(obj, 'StimulusShow', 1, x, y, 'OnString', 'Stimuli Show', ... + 'OffString', 'Stimuli Hidden', 'TooltipString', 'Show/Hide Stimulus panel'); + set_callback(StimulusShow, {mfilename, 'show_hide'}); %#ok (Defined just above) + next_row(y); + + SoloParamHandle(obj, 'myfig', 'value', figure('closerequestfcn', [mfilename '(' class(obj) ', ''hide'');'], 'MenuBar', 'none', ... + 'Name', mfilename), 'saveable', 0); + screen_size = get(0, 'ScreenSize'); + set(value(myfig),'Position',[1 screen_size(4)-740, 1000 1000]); % put fig at top right + set(double(gcf), 'Visible', 'off'); + x=10;y=10; + + SoloParamHandle(obj, 'ax', 'saveable', 0, ... + 'value', axes('Position', [0.01 0.5 0.45 0.45])); + ylabel('log_e A','FontSize',16,'FontName','Cambria Math'); + set(value(ax),'Fontsize',15) + xlabel('Sound Categorization','FontSize',16,'FontName','Cambria Math') + + % SoloParamHandle(obj, 'axperf', 'saveable', 0, ... + % 'value', axes('Position', [0.5 0.5 0.45 0.45])); + % ylabel('log_e A','FontSize',16,'FontName','Cambria Math'); + % set(value(axperf),'Fontsize',15) + % xlabel('Sound Categorization','FontSize',16,'FontName','Cambria Math') + + SoundManagerSection(obj, 'declare_new_sound', 'StimAUD1') + SoloParamHandle(obj, 'thisstim', 'value', []); + SoloParamHandle(obj, 'thisstimlog', 'value', []); + SoloParamHandle(obj, 'h1', 'value', []); + + y=5; + + next_row(y); + next_row(y); + PushbuttonParam(obj, 'refresh_stimuli', x,y , 'TooltipString', 'Instantiates the stimuli given the new set of parameters'); + set_callback(refresh_stimuli, {mfilename, 'plot_stimuli'}); + + + next_row(y); + next_row(y); + MenuParam(obj, 'Rule', {'S1>S_boundary Left','S1>S_boundary Right'}, ... + 'S1>S_boundary Left', x, y, 'labelfraction', 0.35, 'TooltipString', sprintf(['\nThis buttom determines the rule\n', ... + '\n''S1>S_boundary Left'' means if Aud1 > Aud_boundry then reward will be delivered from the left water spout and if Aud1 < Aud_boundry then water comes from right\n',... + '\n''S1>S_boundary Right'' means if Aud1 < Aud_boundry then reward will be delivered from the left water spout and if Aud1 > Aud_boundry then water comes from right\n'])); + next_row(y, 1);next_row(y, 1); + + MenuParam(obj, 'Prob_Dist_Left', {'Uniform','Half Normal','Normal','Sinusoidal','Anti Half Normal','Anti Sinusoidal','Monotonic Increase'}, ... + 'Uniform', x, y,'label','Left Dist', 'labelfraction', 0.35, 'TooltipString', sprintf(['\n Different Probability Distributions for Category A.\n', ... + '\n''Normal - the mean is at mid point of range. Half Normal - truncated normal with mean at boundary.\n',... + '\n''Anti Half Normal - the mean/max is at the side edge of the range.\n',... + '\n''Sinosidal - using sine function instead of half normal and Anti Sinusoidal is when max is at the edge, same as anti half normal.\n',... + '\n''Monotonic - increases and decreases in steps from min to max (removing the random'])); + set_callback(Prob_Dist_Left, {mfilename, 'Cal_Mean'}); + next_row(y); + DispParam(obj, 'mean_Left', 0.01, x,y,'label','μ Left','TooltipString','mean/max log stim value for the left side distribution'); + next_row(y); + DispParam(obj, 'sigma_Left', 0.01, x,y,'label','σ Left','TooltipString','mean/max log stim value for the left side distribution'); + next_row(y); + NumeditParam(obj, 'sigma_range_Left', 1, x,y,'label','3σ Left','TooltipString',sprintf(['\n A way to reduce the range and increase more distribution towards mean\n', ... + '\n''A value b/w range [0.2 - 1] is acceptable, signifying 3 Sigma (99.7%) value for the left side distribution'])); + set_callback(sigma_range_Left, {mfilename, 'Cal_Sigma'}); + next_row(y); next_row(y); + + MenuParam(obj, 'Prob_Dist_Right', {'Uniform','Half Normal','Normal','Sinusoidal','Anti Half Normal','Anti Sinusoidal'}, ... + 'Uniform', x, y, 'label','Right Dist', 'labelfraction', 0.35, 'TooltipString', sprintf(['\n Different Probability Distributions for Category A.\n', ... + '\n''Normal - the mean is at mid point of range (side edge - boundary). Half Normal - truncated normal with mean at boundary.\n',... + '\n''Anti Half Normal - the mean/max is at the side edge of the range.\n',... + '\n''Sinosidal - using sine function instead of half normal and Anti Sinusoidal is when max is at the edge, same as anti half normal'])); + set_callback(Prob_Dist_Right, {mfilename, 'Cal_Mean'}); + next_row(y); + DispParam(obj, 'mean_Right', 0.01, x,y,'label','μ Right','TooltipString','mean/max log stim value for the right side distribution'); + next_row(y); + DispParam(obj, 'sigma_Right', 0.01, x,y,'label','σ Right','TooltipString','mean/max log stim value for the left side distribution'); + next_row(y); + NumeditParam(obj, 'sigma_range_Right', 1, x,y,'label','3σ Right','TooltipString',sprintf(['\n A way to reduce the range and increase more distribution towards mean\n', ... + '\n''A value b/w range [0.2 - 1] is acceptable, signifying 3 Sigma (99.7%) value for the right side distribution'])); + set_callback(sigma_range_Right, {mfilename, 'Cal_Sigma'}); + next_row(y); + + next_column(x); + y=5; + next_row(y, 1) + MenuParam(obj, 'filter_type', {'GAUS','LPFIR', 'FIRLS','BUTTER','MOVAVRG','KAISER','EQUIRIP','HAMMING'}, ... + 'GAUS', x, y, 'labelfraction', 0.35, 'TooltipString', sprintf(['\nDifferent filters. ''LPFIR'': lowpass FIR ''FIRLS'': Least square linear-phase FIR filter design\n', ... + '\n''BUTTER'': IIR Butterworth lowpass filter ''GAUS'': Gaussian filter (window)\n', ... + '\n''MOVAVRG'': Moving average FIR filter ''KAISER'': Kaiser-window FIR filtering\n', ... + '\n''EQUIRIP'':Eqiripple FIR filter ''HAMMING'': Hamming-window based FIR'])); + next_row(y); + NumeditParam(obj,'fcut',110,x,y,'label','fcut','TooltipString','Cut off frequency on the original white noise'); + next_row(y); + NumeditParam(obj,'lfreq',2000,x,y,'label','Modulator_LowFreq','TooltipString','Lower bound for the frequency modulator'); + next_row(y); + NumeditParam(obj,'hfreq',20000,x,y,'label','Modulator_HighFreq','TooltipString','Upper bound for the frequency modulator'); + next_row(y); + NumeditParam(obj,'minS1',0.007,x,y,'label','minS1','TooltipString','min sigma value for AUD1'); + set_callback(minS1, {mfilename, 'Cal_Boundary'}); + set_callback(minS1, {mfilename, 'Cal_Mean'}); + next_row(y); + NumeditParam(obj,'maxS1',0.05,x,y,'label','maxS1','TooltipString','max sigma value for AUD1'); + set_callback(maxS1, {mfilename, 'Cal_Boundary'}); + set_callback(maxS1, {mfilename, 'Cal_Mean'}); + next_row(y); + DispParam(obj, 'A1_sigma', 0.01, x,y,'label','A1_sigma','TooltipString','Sigma value for the first stimulus'); + next_row(y); + NumeditParam(obj,'minF1',4,x,y,'label','minF1','TooltipString','min frequency value for AUD1'); + set_callback(minF1, {mfilename, 'Cal_Boundary'}); + set_callback(minF1, {mfilename, 'Cal_Mean'}); + next_row(y); + NumeditParam(obj,'maxF1',10,x,y,'label','maxF1','TooltipString','max frequency value for AUD1'); + set_callback(maxF1, {mfilename, 'Cal_Boundary'}); + set_callback(maxF1, {mfilename, 'Cal_Mean'}); + next_row(y); + DispParam(obj, 'A1_freq', 0.01, x,y,'label','A1_freq','TooltipString','Sigma value for the first stimulus'); + next_row(y); + DispParam(obj,'boundary',-3.9,x,y,'label','boundary(log)','TooltipString','decision boundary for categorisation (log)'); + next_row(y); + MenuParam(obj, 'mu_location', {'center', 'side'}, ... + 'center', x, y, 'labelfraction', 0.35, 'TooltipString', sprintf('\nLocation of boundary')); + set_callback(mu_location, {mfilename, 'Cal_Boundary'}); + next_row(y); + ToggleParam(obj, 'frequency_categorization', 0, x,y,... + 'OnString', 'Frequency(Tone)',... + 'OffString', 'Amplitude(Noise)',... + 'TooltipString', sprintf('If on (black) then it enables the presentation of pure tones')); + set_callback(frequency_categorization, {mfilename, 'FrequencyCategorization'}); + make_invisible(maxF1);make_invisible(minF1);make_invisible(A1_freq); + next_row(y); + + StimulusSection(obj,'plot_stimuli'); + + case 'prepare_next_trial' + + StimulusSection(obj,'pick_current_stimulus'); + srate=SoundManagerSection(obj,'get_sample_rate'); + Fs=srate; + T=value(A1_time); + + if frequency_categorization + % produce the tone + A1_freq.value = value(thisstim); + A1 = value(thisstimlog(n_completed_trials+1)); + dur1 = A1_time*1000; + bal=0; + freq1=A1_freq*1000; + vol=0.001; + RVol=vol*min(1,(1+bal)); + LVol=vol*min(1,(1-bal)); + t=0:(1/srate):(dur1/1000); + t = t(1:end-1); + tw=sin(t*2*pi*freq1); + RW=RVol*tw; + %w=[LW;RW]; + AUD1 = RW; + else + % produce noise pattern + A1_sigma.value = value(thisstim); + A1 = value(thisstimlog(n_completed_trials+1)); + [rawA1, rawA2, normA1, normA2]=noisestim(1,1,T,value(fcut),Fs,value(filter_type)); + modulator=singlenoise(1,T,[value(lfreq) value(hfreq)],Fs,'BUTTER'); + AUD1=normA1(1:A1_time*srate).*modulator(1:A1_time*srate).*A1_sigma; + end + + if ~isempty(AUD1) + SoundManagerSection(obj, 'set_sound', 'StimAUD1', [AUD1'; AUD1']) + end + + SoundManagerSection(obj, 'send_not_yet_uploaded_sounds'); + + % Plot current stimulus and move to saving stimulus history + + if value(thisstimlog(n_completed_trials+1)) > value(boundary)%value(numClass) + set(value(h1), 'YData', value(A1), 'color',[0.4 0.8 0.1],'markerfacecolor',[0.4 0.8 0.1]); + else + set(value(h1), 'YData', value(A1), 'color',[0.8 0.4 0.1],'markerfacecolor',[0.8 0.4 0.1]); + end + + if n_completed_trials > 0 + if ~violation_history(n_completed_trials) && ~timeout_history(n_completed_trials) + StimulusSection(obj,'update_stimulus_history'); + else + StimulusSection(obj,'update_stimulus_history_nan'); + end + end + + %% Case pick_current_stimulus + case 'pick_current_stimulus' + if frequency_categorization + stim_min_log = log(value(minF1)); + stim_max_log = log(value(maxF1)); + else + stim_min_log = log(value(minS1)); + stim_max_log = log(value(maxS1)); + end + + if strcmpi(ThisTrial, 'LEFT') + dist_type = value(Prob_Dist_Left); + dist_mean = value(mean_Left); + dist_sigma = value(sigma_Left); + dist_range_multiplier = value(sigma_range_Left); + if strcmp(Rule,'S1>S_boundary Left') + edge_max = stim_max_log; + edge_min = value(boundary); + edge_max = edge_min + dist_range_multiplier * (edge_max - edge_min); + else % the rule is S1>S_boundary Right + edge_min = stim_min_log; + edge_max = value(boundary); + edge_min = edge_max - dist_range_multiplier * (edge_max - edge_min); + end + + else % trial is Right + dist_type = value(Prob_Dist_Right); + dist_mean = value(mean_Right); + dist_sigma = value(sigma_Right); + dist_range_multiplier = value(sigma_range_Right); + if strcmp(Rule,'S1>S_boundary Right') + edge_max = stim_max_log; + edge_min = value(boundary); + edge_max = edge_min + dist_range_multiplier * (edge_max - edge_min); + else % the rule is S1>S_boundary Left + edge_min = stim_min_log; + edge_max = value(boundary); + edge_min = edge_max - dist_range_multiplier * (edge_max - edge_min); + end + end + + % Create a Stimuli with the selected Distribution and Side + switch dist_type + + case 'Uniform' % uniform distribution + stim_i_log = random('Uniform',edge_min,edge_max); + + case 'Half Normal' + if edge_min == value(boundary) + stim_i_log = random('Half Normal',dist_mean,dist_sigma); + while stim_i_log < edge_min || stim_i_log > edge_max + stim_i_log = random('Half Normal',dist_mean,dist_sigma); + end + else + stim_i_log = CreateSamples_from_Distribution('normal',dist_mean,dist_sigma,edge_min,edge_max,1); + end + + case 'Anti Half Normal' + + stim_i_log = CreateSamples_from_Distribution('normal',dist_mean,dist_sigma,edge_min,edge_max,1); + + case 'Normal' + stim_i_log = random('Normal',dist_mean,dist_sigma); + while stim_i_log < edge_min || stim_i_log > edge_max + stim_i_log = random('Normal',dist_mean,dist_sigma); + end + + case 'Sinusoidal' | 'Anti Sinusoidal' + + stim_i_log = CreateSamples_from_Distribution('Sinusoidal',dist_mean,dist_sigma,edge_min,edge_max,1); + + end + + thisstim.value=exp(stim_i_log); + thisstimlog(n_completed_trials+1) = stim_i_log; + + %% Case plot_pais + case 'plot_stimuli' + + %% plot the stimuli + if frequency_categorization + boundary.value = (log(value(minF1)) + log(value(maxF1)))/2; + stim_min_log = log(value(minF1)); + stim_max_log = log(value(maxF1)); + stim_min = value(minF1); + stim_max = value(maxF1); + else + boundary.value = (log(value(minS1)) + log(value(maxS1)))/2; + if strcmp(mu_location,'center') + boundary.value = value(boundary); + elseif strcmp(mu_location,'side') + boundary.value = (log(value(minS1)) + value(boundary))/2; + end + stim_min_log = log(value(minS1)); + stim_max_log = log(value(maxS1)); + stim_min = value(minS1); + stim_max = value(maxS1); + end + cla(value(ax)) + xd=1; + axes(value(ax)); + plot(xd,stim_min_log,'s','MarkerSize',15,'MarkerEdgeColor',[0 0 0],'LineWidth',2) + hold on + plot(xd,stim_max_log,'s','MarkerSize',15,'MarkerEdgeColor',[0 0 0],'LineWidth',2) + line([0,2], [value(boundary),value(boundary)]); + axis square + set(value(ax),'ytick',([stim_min_log, stim_max_log]),'xtick',xd); + set(value(ax),'yticklabel',([stim_min, stim_max]),'xticklabel','S1'); + ylabel('\sigma_1 in log scale','FontSize',16,'FontName','Cambria Math'); + set(value(ax),'Fontsize',15) + xlabel('S1','FontSize',16,'FontName','Cambria Math') + + StimulusSection(obj,'pick_current_stimulus'); + A1 = value(thisstim); + + %% plot the stimulus; + if value(thisstim) > value(boundary)%value(numClass) + h1.value=plot(xd,value(A1),'s','color',[0.4 0.8 0.1],'markerfacecolor',[0.4 0.8 0.1],'MarkerSize',15,'LineWidth',3); + else + h1.value=plot(xd,value(A1),'s','color',[0.8 0.4 0.1],'markerfacecolor',[0.8 0.4 0.1],'MarkerSize',15,'LineWidth',3); + end + + %% Boundary Calculate + case 'Cal_Boundary' + if frequency_categorization + val_boundary = (log(value(minF1)) + log(value(maxF1)))/2; + min_val = log(value(minF1)); + else + val_boundary = (log(value(minS1)) + log(value(maxS1)))/2; + min_val = log(value(minS1)); + end + if strcmp(mu_location,'center') + boundary.value = val_boundary; + elseif strcmp(mu_location,'side') + boundary.value = (min_val + val_boundary)/2; + end + + StimulusSection(obj,'Cal_Mean'); % update the mean and sigma values for each side + + %% Updated Mean/Max for Each Side based upon Distribution Selected + case 'Cal_Mean' + + if frequency_categorization + edge_max = log(value(maxF1)); + edge_min = log(value(minF1)); + else + edge_max = log(value(maxS1)); + edge_min = log(value(minS1)); + end + + % Calculation for Left Side + + % Sigma + + dist_sigma_multiplier = value(sigma_range_Left); + if dist_sigma_multiplier < 0.2 + dist_sigma_multiplier = 0.2; + end + if dist_sigma_multiplier > 1 + dist_sigma_multiplier = 1; + end + sigma_Left.value = (dist_sigma_multiplier * (edge_max - edge_min)) / 3; % as we asked user to provide 3 sigma + + % Mean + if matches(value(Prob_Dist_Left),{'Uniform','Half Normal','Sinusoidal'}) + mean_Left.value = value(boundary); + else + if strcmp(Rule,'S1>S_boundary Left') + if matches(Prob_Dist_Left,{'Anti Half Normal','Anti Sinusoidal'}) + mean_Left.value = edge_max; + elseif matches(Prob_Dist_Left,'Normal') + mean_Left.value = (edge_max + value(boundary))/2; + end + else + if matches(value(Prob_Dist_Left),{'Anti Half Normal','Anti Sinusoidal'}) + mean_Left.value = edge_min; + elseif matches(value(Prob_Dist_Left),'Normal') + mean_Left.value = (edge_min + value(boundary))/2; + end + end + end + + % Calculation for Right Side + + % Sigma + dist_sigma_multiplier = value(sigma_range_Right); + if dist_sigma_multiplier < 0.2 + dist_sigma_multiplier = 0.2; + end + if dist_sigma_multiplier > 1 + dist_sigma_multiplier = 1; + end + sigma_Right.value = (dist_sigma_multiplier * (edge_max - edge_min)) / 3; % as we asked user to provide 3 sigma + + % Mean + if matches(value(Prob_Dist_Right),{'Uniform','Half Normal','Sinusoidal'}) + mean_Right.value = value(boundary); + else + if strcmp(Rule,'S1>S_boundary Right') + if matches(value(Prob_Dist_Right),{'Anti Half Normal','Anti Sinusoidal'}) + mean_Right.value = edge_max; + elseif matches(value(Prob_Dist_Right),'Normal') + mean_Right.value = (edge_max + value(boundary))/2; + end + else + if matches(value(Prob_Dist_Right),{'Anti Half Normal','Anti Sinusoidal'}) + mean_Right.value = edge_min; + elseif matches(Prob_Dist_Right,'Normal') + mean_Right.value = (edge_min + value(boundary))/2; + end + end + end + + %% Calculate Sigma + case 'Cal_Sigma' + + if frequency_categorization + edge_max = log(value(maxF1)); + edge_min = log(value(minF1)); + else + edge_max = log(value(maxS1)); + edge_min = log(value(minS1)); + end + + % Calculation for Left Side Sigma + dist_sigma_multiplier = value(sigma_range_Left); + if dist_sigma_multiplier < 0.2 + dist_sigma_multiplier = 0.2; + end + if dist_sigma_multiplier > 1 + dist_sigma_multiplier = 1; + end + sigma_Left.value = (dist_sigma_multiplier * (edge_max - edge_min)) / 3; % as we asked user to provide 3 sigma + + % Calculation for Right Side Sigma + dist_sigma_multiplier = value(sigma_range_Right); + if dist_sigma_multiplier < 0.2 + dist_sigma_multiplier = 0.2; + end + if dist_sigma_multiplier > 1 + dist_sigma_multiplier = 1; + end + sigma_Right.value = (dist_sigma_multiplier * (edge_max - edge_min)) / 3; % as we asked user to provide 3 sigma + + + %% Case frequency ON + case 'FrequencyCategorization' + if frequency_categorization == 1 + make_visible(maxF1);make_visible(minF1);make_visible(A1_freq); + make_invisible(maxS1);make_invisible(minS1);make_invisible(A1_sigma); + make_invisible(fcut);make_invisible(lfreq);make_invisible(hfreq); make_invisible(filter_type); + StimulusSection(obj,'plot_stimuli'); + else + make_visible(maxS1);make_visible(minS1);make_visible(A1_sigma); + make_visible(fcut);make_visible(lfreq);make_visible(hfreq); make_visible(filter_type); + make_invisible(maxF1);make_invisible(minF1);make_invisible(A1_freq); + StimulusSection(obj,'plot_stimuli'); + end + + StimulusSection(obj,'Cal_Boundary'); % update the boundary + StimulusSection(obj,'Cal_Mean'); % update the mean and sigma values for each side + + + %% Case get_stimuli + % case 'get_stimuli' + % if nargout>0 + % x=value(S1); + % end + + + %% Case close + case 'close' + % Delete all SoloParamHandles who belong to this object and whose + % fullname starts with the name of this mfile: + if exist('myfig', 'var') && isa(myfig, 'SoloParamHandle') && ishandle(value(myfig)) %#ok + delete(value(myfig)); + end + delete_sphandle('owner', ['^@' class(obj) '$'], ... + 'fullname', ['^' mfilename]); + + case 'update_stimulus_history' + ps=value(stimulus_history); + ps(n_completed_trials)=value(thisstimlog(n_completed_trials)); + stimulus_history.value=ps; + + case 'update_stimulus_history_nan' + ps=value(stimulus_history); + ps(n_completed_trials)=value(thisstimlog(n_completed_trials));%nan; + stimulus_history.value=ps; + + %% Case hide + case 'hide' + StimulusShow.value = 0; + set(value(myfig), 'Visible', 'off'); + + %% Case show + case 'show' + StimulusShow.value = 1; + set(value(myfig), 'Visible', 'on'); + + %% Case Show_hide + case 'show_hide' + if StimulusShow == 1 + set(value(myfig), 'Visible', 'on'); %#ok (defined by GetSoloFunctionArgs) + else + set(value(myfig), 'Visible', 'off'); + end + +end + +end diff --git a/Protocols/@Arpit_CentrePokeTraining/StimulusSection.m b/Protocols/@Arpit_CentrePokeTraining/StimulusSection.m index 0b588c35..2123d455 100644 --- a/Protocols/@Arpit_CentrePokeTraining/StimulusSection.m +++ b/Protocols/@Arpit_CentrePokeTraining/StimulusSection.m @@ -111,21 +111,17 @@ next_row(y); NumeditParam(obj,'minS1',0.007,x,y,'label','minS1','TooltipString','min sigma value for AUD1'); set_callback(minS1, {mfilename, 'Cal_Boundary'}); - set_callback(minS1, {mfilename, 'Cal_Mean'}); next_row(y); NumeditParam(obj,'maxS1',0.05,x,y,'label','maxS1','TooltipString','max sigma value for AUD1'); set_callback(maxS1, {mfilename, 'Cal_Boundary'}); - set_callback(maxS1, {mfilename, 'Cal_Mean'}); next_row(y); DispParam(obj, 'A1_sigma', 0.01, x,y,'label','A1_sigma','TooltipString','Sigma value for the first stimulus'); next_row(y); NumeditParam(obj,'minF1',4,x,y,'label','minF1','TooltipString','min frequency value for AUD1'); set_callback(minF1, {mfilename, 'Cal_Boundary'}); - set_callback(minF1, {mfilename, 'Cal_Mean'}); next_row(y); NumeditParam(obj,'maxF1',10,x,y,'label','maxF1','TooltipString','max frequency value for AUD1'); set_callback(maxF1, {mfilename, 'Cal_Boundary'}); - set_callback(maxF1, {mfilename, 'Cal_Mean'}); next_row(y); DispParam(obj, 'A1_freq', 0.01, x,y,'label','A1_freq','TooltipString','Sigma value for the first stimulus'); next_row(y); @@ -134,15 +130,12 @@ MenuParam(obj, 'mu_location', {'center', 'side'}, ... 'center', x, y, 'labelfraction', 0.35, 'TooltipString', sprintf('\nLocation of boundary')); set_callback(mu_location, {mfilename, 'Cal_Boundary'}); - set_callback(mu_location, {mfilename, 'Cal_Mean'}); next_row(y); ToggleParam(obj, 'frequency_categorization', 0, x,y,... 'OnString', 'Frequency(Tone)',... 'OffString', 'Amplitude(Noise)',... 'TooltipString', sprintf('If on (black) then it enables the presentation of pure tones')); - set_callback(frequency_categorization, {mfilename, 'Cal_Boundary'}); set_callback(frequency_categorization, {mfilename, 'FrequencyCategorization'}); - set_callback(frequency_categorization, {mfilename, 'Cal_Mean'}); make_invisible(maxF1);make_invisible(minF1);make_invisible(A1_freq); next_row(y); @@ -338,7 +331,9 @@ elseif strcmp(mu_location,'side') boundary.value = (min_val + val_boundary)/2; end - + + StimulusSection(obj,'Cal_Mean'); % update the mean and sigma values for each side + %% Updated Mean/Max for Each Side based upon Distribution Selected case 'Cal_Mean' @@ -459,6 +454,9 @@ StimulusSection(obj,'plot_stimuli'); end + StimulusSection(obj,'Cal_Boundary'); % update the boundary + + %% Case get_stimuli % case 'get_stimuli' % if nargout>0 From e71b2651f30166efc2a30b9a9c184ea09334a4dc Mon Sep 17 00:00:00 2001 From: viktorpm Date: Thu, 3 Apr 2025 12:47:08 +0100 Subject: [PATCH 047/164] debug --- .../Arpit_CentrePokeTraining.m | 12 +- .../Arpit_CentrePokeTrainingSMA.m | 3 +- .../@Arpit_CentrePokeTraining/ParamsSection.m | 71 +-- .../StimulusSection.asv | 512 ------------------ .../StimulusSection.m | 131 +++-- 5 files changed, 109 insertions(+), 620 deletions(-) delete mode 100644 Protocols/@Arpit_CentrePokeTraining/StimulusSection.asv diff --git a/Protocols/@Arpit_CentrePokeTraining/Arpit_CentrePokeTraining.m b/Protocols/@Arpit_CentrePokeTraining/Arpit_CentrePokeTraining.m index eb483d1a..3e6f70ac 100644 --- a/Protocols/@Arpit_CentrePokeTraining/Arpit_CentrePokeTraining.m +++ b/Protocols/@Arpit_CentrePokeTraining/Arpit_CentrePokeTraining.m @@ -156,9 +156,9 @@ case 'prepare_next_trial' ParamsSection(obj, 'prepare_next_trial'); - % Run SessionDefinition *after* ParamsSection so we know whether the - % trial was a violation or not - push_helper_vars_tosql(obj,n_done_trials); + % Run SessionDefinition *after* ParamsSection so we know whether the trial was a violation or not + + % push_helper_vars_tosql(obj,n_done_trials); SessionDefinition(obj, 'next_trial'); SessionPerformanceSection(obj, 'evaluate'); StimulusSection(obj,'prepare_next_trial'); @@ -179,9 +179,9 @@ prot_title.value=[mfilename ' on rig ' get_hostname ' : ' expmtr ', ' rname '. Started at ' datestr(now, 'HH:MM')]; end - try - send_n_done_trials(obj); - end + % try + % send_n_done_trials(obj); + % end %% trial_completed case 'trial_completed' diff --git a/Protocols/@Arpit_CentrePokeTraining/Arpit_CentrePokeTrainingSMA.m b/Protocols/@Arpit_CentrePokeTraining/Arpit_CentrePokeTrainingSMA.m index d762923e..5e37dabb 100644 --- a/Protocols/@Arpit_CentrePokeTraining/Arpit_CentrePokeTrainingSMA.m +++ b/Protocols/@Arpit_CentrePokeTraining/Arpit_CentrePokeTrainingSMA.m @@ -420,7 +420,8 @@ % 'hit', [0 1 0] - + case 'close' + case 'reinit' currfig = double(gcf); diff --git a/Protocols/@Arpit_CentrePokeTraining/ParamsSection.m b/Protocols/@Arpit_CentrePokeTraining/ParamsSection.m index 5e4755eb..c7017195 100644 --- a/Protocols/@Arpit_CentrePokeTraining/ParamsSection.m +++ b/Protocols/@Arpit_CentrePokeTraining/ParamsSection.m @@ -275,41 +275,42 @@ case 'prepare_next_trial' - switch value(training_stage) - - case {1,2} %% learning the reward sound association -left or right led on -> poke -> sound+reward - - time_go_cue.value=0.200; - disable(SettlingIn_time); - disable(PreStim_time); - disable(A1_time); - disable(time_bet_aud1_gocue); - - - case {3,4} % Centre poke without the A1_Stim but has violation in 4 - - time_go_cue.value=0.100; - enable(SettlingIn_time); - disable(PreStim_time); - disable(A1_time); - enable(time_bet_aud1_gocue); - - if n_done_trials <1 && warmup_on ==1 - CP_duration.value = value(init_CP_duration); - else - CP_duration.value = value(CP_duration); - end - Total_CP_duration.value = value(CP_duration) + value(time_go_cue); %#ok<*NASGU> + if value(use_auto_train) == 1 + switch value(training_stage) + + case {1,2} %% learning the reward sound association -left or right led on -> poke -> sound+reward + + time_go_cue.value=0.200; + disable(SettlingIn_time); + disable(PreStim_time); + disable(A1_time); + disable(time_bet_aud1_gocue); + + + case {3,4} % Centre poke without the A1_Stim but has violation in 4 + + time_go_cue.value=0.100; + enable(SettlingIn_time); + disable(PreStim_time); + disable(A1_time); + enable(time_bet_aud1_gocue); + + if n_done_trials <1 && warmup_on ==1 + CP_duration.value = value(init_CP_duration); + else + CP_duration.value = value(CP_duration); + end + Total_CP_duration.value = value(CP_duration) + value(time_go_cue); %#ok<*NASGU> - case {5,6,7,8} % - - time_go_cue.value=0.100; - enable(SettlingIn_time); - enable(PreStim_time); - enable(A1_time); - enable(time_bet_aud1_gocue); + case {5,6,7,8} % - if random_prego_time == 1 + time_go_cue.value=0.100; + enable(SettlingIn_time); + enable(PreStim_time); + enable(A1_time); + enable(time_bet_aud1_gocue); + + if random_prego_time == 1 time_range_go_cue = value(time_bet_aud1_gocue_Min):0.01:value(time_bet_aud1_gocue_Max); time_bet_aud1_gocue.value = time_range_go_cue(randi([1, numel(time_range_go_cue)],1,1)); end @@ -331,8 +332,8 @@ end Total_CP_duration.value = value(CP_duration) + value(time_go_cue); %#ok<*NASGU> - - end + end + end %% update violation, timeout, previous_sides, etc diff --git a/Protocols/@Arpit_CentrePokeTraining/StimulusSection.asv b/Protocols/@Arpit_CentrePokeTraining/StimulusSection.asv deleted file mode 100644 index ee29363a..00000000 --- a/Protocols/@Arpit_CentrePokeTraining/StimulusSection.asv +++ /dev/null @@ -1,512 +0,0 @@ - - -function [x, y] = StimulusSection(obj, action, varargin) - -GetSoloFunctionArgs(obj); - -switch action - - % ------------------------------------------------------------------ - % INIT - % ------------------------------------------------------------------ - - case 'init' - if length(varargin) < 2 - error('Need at least two arguments, x and y position, to initialize %s', mfilename); - end - x = varargin{1}; y = varargin{2}; - - ToggleParam(obj, 'StimulusShow', 1, x, y, 'OnString', 'Stimuli Show', ... - 'OffString', 'Stimuli Hidden', 'TooltipString', 'Show/Hide Stimulus panel'); - set_callback(StimulusShow, {mfilename, 'show_hide'}); %#ok (Defined just above) - next_row(y); - - SoloParamHandle(obj, 'myfig', 'value', figure('closerequestfcn', [mfilename '(' class(obj) ', ''hide'');'], 'MenuBar', 'none', ... - 'Name', mfilename), 'saveable', 0); - screen_size = get(0, 'ScreenSize'); - set(value(myfig),'Position',[1 screen_size(4)-740, 1000 1000]); % put fig at top right - set(double(gcf), 'Visible', 'off'); - x=10;y=10; - - SoloParamHandle(obj, 'ax', 'saveable', 0, ... - 'value', axes('Position', [0.01 0.5 0.45 0.45])); - ylabel('log_e A','FontSize',16,'FontName','Cambria Math'); - set(value(ax),'Fontsize',15) - xlabel('Sound Categorization','FontSize',16,'FontName','Cambria Math') - - % SoloParamHandle(obj, 'axperf', 'saveable', 0, ... - % 'value', axes('Position', [0.5 0.5 0.45 0.45])); - % ylabel('log_e A','FontSize',16,'FontName','Cambria Math'); - % set(value(axperf),'Fontsize',15) - % xlabel('Sound Categorization','FontSize',16,'FontName','Cambria Math') - - SoundManagerSection(obj, 'declare_new_sound', 'StimAUD1') - SoloParamHandle(obj, 'thisstim', 'value', []); - SoloParamHandle(obj, 'thisstimlog', 'value', []); - SoloParamHandle(obj, 'h1', 'value', []); - - y=5; - - next_row(y); - next_row(y); - PushbuttonParam(obj, 'refresh_stimuli', x,y , 'TooltipString', 'Instantiates the stimuli given the new set of parameters'); - set_callback(refresh_stimuli, {mfilename, 'plot_stimuli'}); - - - next_row(y); - next_row(y); - MenuParam(obj, 'Rule', {'S1>S_boundary Left','S1>S_boundary Right'}, ... - 'S1>S_boundary Left', x, y, 'labelfraction', 0.35, 'TooltipString', sprintf(['\nThis buttom determines the rule\n', ... - '\n''S1>S_boundary Left'' means if Aud1 > Aud_boundry then reward will be delivered from the left water spout and if Aud1 < Aud_boundry then water comes from right\n',... - '\n''S1>S_boundary Right'' means if Aud1 < Aud_boundry then reward will be delivered from the left water spout and if Aud1 > Aud_boundry then water comes from right\n'])); - next_row(y, 1);next_row(y, 1); - - MenuParam(obj, 'Prob_Dist_Left', {'Uniform','Half Normal','Normal','Sinusoidal','Anti Half Normal','Anti Sinusoidal','Monotonic Increase'}, ... - 'Uniform', x, y,'label','Left Dist', 'labelfraction', 0.35, 'TooltipString', sprintf(['\n Different Probability Distributions for Category A.\n', ... - '\n''Normal - the mean is at mid point of range. Half Normal - truncated normal with mean at boundary.\n',... - '\n''Anti Half Normal - the mean/max is at the side edge of the range.\n',... - '\n''Sinosidal - using sine function instead of half normal and Anti Sinusoidal is when max is at the edge, same as anti half normal.\n',... - '\n''Monotonic - increases and decreases in steps from min to max (removing the random'])); - set_callback(Prob_Dist_Left, {mfilename, 'Cal_Mean'}); - next_row(y); - DispParam(obj, 'mean_Left', 0.01, x,y,'label','μ Left','TooltipString','mean/max log stim value for the left side distribution'); - next_row(y); - DispParam(obj, 'sigma_Left', 0.01, x,y,'label','σ Left','TooltipString','mean/max log stim value for the left side distribution'); - next_row(y); - NumeditParam(obj, 'sigma_range_Left', 1, x,y,'label','3σ Left','TooltipString',sprintf(['\n A way to reduce the range and increase more distribution towards mean\n', ... - '\n''A value b/w range [0.2 - 1] is acceptable, signifying 3 Sigma (99.7%) value for the left side distribution'])); - set_callback(sigma_range_Left, {mfilename, 'Cal_Sigma'}); - next_row(y); next_row(y); - - MenuParam(obj, 'Prob_Dist_Right', {'Uniform','Half Normal','Normal','Sinusoidal','Anti Half Normal','Anti Sinusoidal'}, ... - 'Uniform', x, y, 'label','Right Dist', 'labelfraction', 0.35, 'TooltipString', sprintf(['\n Different Probability Distributions for Category A.\n', ... - '\n''Normal - the mean is at mid point of range (side edge - boundary). Half Normal - truncated normal with mean at boundary.\n',... - '\n''Anti Half Normal - the mean/max is at the side edge of the range.\n',... - '\n''Sinosidal - using sine function instead of half normal and Anti Sinusoidal is when max is at the edge, same as anti half normal'])); - set_callback(Prob_Dist_Right, {mfilename, 'Cal_Mean'}); - next_row(y); - DispParam(obj, 'mean_Right', 0.01, x,y,'label','μ Right','TooltipString','mean/max log stim value for the right side distribution'); - next_row(y); - DispParam(obj, 'sigma_Right', 0.01, x,y,'label','σ Right','TooltipString','mean/max log stim value for the left side distribution'); - next_row(y); - NumeditParam(obj, 'sigma_range_Right', 1, x,y,'label','3σ Right','TooltipString',sprintf(['\n A way to reduce the range and increase more distribution towards mean\n', ... - '\n''A value b/w range [0.2 - 1] is acceptable, signifying 3 Sigma (99.7%) value for the right side distribution'])); - set_callback(sigma_range_Right, {mfilename, 'Cal_Sigma'}); - next_row(y); - - next_column(x); - y=5; - next_row(y, 1) - MenuParam(obj, 'filter_type', {'GAUS','LPFIR', 'FIRLS','BUTTER','MOVAVRG','KAISER','EQUIRIP','HAMMING'}, ... - 'GAUS', x, y, 'labelfraction', 0.35, 'TooltipString', sprintf(['\nDifferent filters. ''LPFIR'': lowpass FIR ''FIRLS'': Least square linear-phase FIR filter design\n', ... - '\n''BUTTER'': IIR Butterworth lowpass filter ''GAUS'': Gaussian filter (window)\n', ... - '\n''MOVAVRG'': Moving average FIR filter ''KAISER'': Kaiser-window FIR filtering\n', ... - '\n''EQUIRIP'':Eqiripple FIR filter ''HAMMING'': Hamming-window based FIR'])); - next_row(y); - NumeditParam(obj,'fcut',110,x,y,'label','fcut','TooltipString','Cut off frequency on the original white noise'); - next_row(y); - NumeditParam(obj,'lfreq',2000,x,y,'label','Modulator_LowFreq','TooltipString','Lower bound for the frequency modulator'); - next_row(y); - NumeditParam(obj,'hfreq',20000,x,y,'label','Modulator_HighFreq','TooltipString','Upper bound for the frequency modulator'); - next_row(y); - NumeditParam(obj,'minS1',0.007,x,y,'label','minS1','TooltipString','min sigma value for AUD1'); - set_callback(minS1, {mfilename, 'Cal_Boundary'}); - set_callback(minS1, {mfilename, 'Cal_Mean'}); - next_row(y); - NumeditParam(obj,'maxS1',0.05,x,y,'label','maxS1','TooltipString','max sigma value for AUD1'); - set_callback(maxS1, {mfilename, 'Cal_Boundary'}); - set_callback(maxS1, {mfilename, 'Cal_Mean'}); - next_row(y); - DispParam(obj, 'A1_sigma', 0.01, x,y,'label','A1_sigma','TooltipString','Sigma value for the first stimulus'); - next_row(y); - NumeditParam(obj,'minF1',4,x,y,'label','minF1','TooltipString','min frequency value for AUD1'); - set_callback(minF1, {mfilename, 'Cal_Boundary'}); - set_callback(minF1, {mfilename, 'Cal_Mean'}); - next_row(y); - NumeditParam(obj,'maxF1',10,x,y,'label','maxF1','TooltipString','max frequency value for AUD1'); - set_callback(maxF1, {mfilename, 'Cal_Boundary'}); - set_callback(maxF1, {mfilename, 'Cal_Mean'}); - next_row(y); - DispParam(obj, 'A1_freq', 0.01, x,y,'label','A1_freq','TooltipString','Sigma value for the first stimulus'); - next_row(y); - DispParam(obj,'boundary',-3.9,x,y,'label','boundary(log)','TooltipString','decision boundary for categorisation (log)'); - next_row(y); - MenuParam(obj, 'mu_location', {'center', 'side'}, ... - 'center', x, y, 'labelfraction', 0.35, 'TooltipString', sprintf('\nLocation of boundary')); - set_callback(mu_location, {mfilename, 'Cal_Boundary'}); - next_row(y); - ToggleParam(obj, 'frequency_categorization', 0, x,y,... - 'OnString', 'Frequency(Tone)',... - 'OffString', 'Amplitude(Noise)',... - 'TooltipString', sprintf('If on (black) then it enables the presentation of pure tones')); - set_callback(frequency_categorization, {mfilename, 'FrequencyCategorization'}); - make_invisible(maxF1);make_invisible(minF1);make_invisible(A1_freq); - next_row(y); - - StimulusSection(obj,'plot_stimuli'); - - case 'prepare_next_trial' - - StimulusSection(obj,'pick_current_stimulus'); - srate=SoundManagerSection(obj,'get_sample_rate'); - Fs=srate; - T=value(A1_time); - - if frequency_categorization - % produce the tone - A1_freq.value = value(thisstim); - A1 = value(thisstimlog(n_completed_trials+1)); - dur1 = A1_time*1000; - bal=0; - freq1=A1_freq*1000; - vol=0.001; - RVol=vol*min(1,(1+bal)); - LVol=vol*min(1,(1-bal)); - t=0:(1/srate):(dur1/1000); - t = t(1:end-1); - tw=sin(t*2*pi*freq1); - RW=RVol*tw; - %w=[LW;RW]; - AUD1 = RW; - else - % produce noise pattern - A1_sigma.value = value(thisstim); - A1 = value(thisstimlog(n_completed_trials+1)); - [rawA1, rawA2, normA1, normA2]=noisestim(1,1,T,value(fcut),Fs,value(filter_type)); - modulator=singlenoise(1,T,[value(lfreq) value(hfreq)],Fs,'BUTTER'); - AUD1=normA1(1:A1_time*srate).*modulator(1:A1_time*srate).*A1_sigma; - end - - if ~isempty(AUD1) - SoundManagerSection(obj, 'set_sound', 'StimAUD1', [AUD1'; AUD1']) - end - - SoundManagerSection(obj, 'send_not_yet_uploaded_sounds'); - - % Plot current stimulus and move to saving stimulus history - - if value(thisstimlog(n_completed_trials+1)) > value(boundary)%value(numClass) - set(value(h1), 'YData', value(A1), 'color',[0.4 0.8 0.1],'markerfacecolor',[0.4 0.8 0.1]); - else - set(value(h1), 'YData', value(A1), 'color',[0.8 0.4 0.1],'markerfacecolor',[0.8 0.4 0.1]); - end - - if n_completed_trials > 0 - if ~violation_history(n_completed_trials) && ~timeout_history(n_completed_trials) - StimulusSection(obj,'update_stimulus_history'); - else - StimulusSection(obj,'update_stimulus_history_nan'); - end - end - - %% Case pick_current_stimulus - case 'pick_current_stimulus' - if frequency_categorization - stim_min_log = log(value(minF1)); - stim_max_log = log(value(maxF1)); - else - stim_min_log = log(value(minS1)); - stim_max_log = log(value(maxS1)); - end - - if strcmpi(ThisTrial, 'LEFT') - dist_type = value(Prob_Dist_Left); - dist_mean = value(mean_Left); - dist_sigma = value(sigma_Left); - dist_range_multiplier = value(sigma_range_Left); - if strcmp(Rule,'S1>S_boundary Left') - edge_max = stim_max_log; - edge_min = value(boundary); - edge_max = edge_min + dist_range_multiplier * (edge_max - edge_min); - else % the rule is S1>S_boundary Right - edge_min = stim_min_log; - edge_max = value(boundary); - edge_min = edge_max - dist_range_multiplier * (edge_max - edge_min); - end - - else % trial is Right - dist_type = value(Prob_Dist_Right); - dist_mean = value(mean_Right); - dist_sigma = value(sigma_Right); - dist_range_multiplier = value(sigma_range_Right); - if strcmp(Rule,'S1>S_boundary Right') - edge_max = stim_max_log; - edge_min = value(boundary); - edge_max = edge_min + dist_range_multiplier * (edge_max - edge_min); - else % the rule is S1>S_boundary Left - edge_min = stim_min_log; - edge_max = value(boundary); - edge_min = edge_max - dist_range_multiplier * (edge_max - edge_min); - end - end - - % Create a Stimuli with the selected Distribution and Side - switch dist_type - - case 'Uniform' % uniform distribution - stim_i_log = random('Uniform',edge_min,edge_max); - - case 'Half Normal' - if edge_min == value(boundary) - stim_i_log = random('Half Normal',dist_mean,dist_sigma); - while stim_i_log < edge_min || stim_i_log > edge_max - stim_i_log = random('Half Normal',dist_mean,dist_sigma); - end - else - stim_i_log = CreateSamples_from_Distribution('normal',dist_mean,dist_sigma,edge_min,edge_max,1); - end - - case 'Anti Half Normal' - - stim_i_log = CreateSamples_from_Distribution('normal',dist_mean,dist_sigma,edge_min,edge_max,1); - - case 'Normal' - stim_i_log = random('Normal',dist_mean,dist_sigma); - while stim_i_log < edge_min || stim_i_log > edge_max - stim_i_log = random('Normal',dist_mean,dist_sigma); - end - - case 'Sinusoidal' | 'Anti Sinusoidal' - - stim_i_log = CreateSamples_from_Distribution('Sinusoidal',dist_mean,dist_sigma,edge_min,edge_max,1); - - end - - thisstim.value=exp(stim_i_log); - thisstimlog(n_completed_trials+1) = stim_i_log; - - %% Case plot_pais - case 'plot_stimuli' - - %% plot the stimuli - if frequency_categorization - boundary.value = (log(value(minF1)) + log(value(maxF1)))/2; - stim_min_log = log(value(minF1)); - stim_max_log = log(value(maxF1)); - stim_min = value(minF1); - stim_max = value(maxF1); - else - boundary.value = (log(value(minS1)) + log(value(maxS1)))/2; - if strcmp(mu_location,'center') - boundary.value = value(boundary); - elseif strcmp(mu_location,'side') - boundary.value = (log(value(minS1)) + value(boundary))/2; - end - stim_min_log = log(value(minS1)); - stim_max_log = log(value(maxS1)); - stim_min = value(minS1); - stim_max = value(maxS1); - end - cla(value(ax)) - xd=1; - axes(value(ax)); - plot(xd,stim_min_log,'s','MarkerSize',15,'MarkerEdgeColor',[0 0 0],'LineWidth',2) - hold on - plot(xd,stim_max_log,'s','MarkerSize',15,'MarkerEdgeColor',[0 0 0],'LineWidth',2) - line([0,2], [value(boundary),value(boundary)]); - axis square - set(value(ax),'ytick',([stim_min_log, stim_max_log]),'xtick',xd); - set(value(ax),'yticklabel',([stim_min, stim_max]),'xticklabel','S1'); - ylabel('\sigma_1 in log scale','FontSize',16,'FontName','Cambria Math'); - set(value(ax),'Fontsize',15) - xlabel('S1','FontSize',16,'FontName','Cambria Math') - - StimulusSection(obj,'pick_current_stimulus'); - A1 = value(thisstim); - - %% plot the stimulus; - if value(thisstim) > value(boundary)%value(numClass) - h1.value=plot(xd,value(A1),'s','color',[0.4 0.8 0.1],'markerfacecolor',[0.4 0.8 0.1],'MarkerSize',15,'LineWidth',3); - else - h1.value=plot(xd,value(A1),'s','color',[0.8 0.4 0.1],'markerfacecolor',[0.8 0.4 0.1],'MarkerSize',15,'LineWidth',3); - end - - %% Boundary Calculate - case 'Cal_Boundary' - if frequency_categorization - val_boundary = (log(value(minF1)) + log(value(maxF1)))/2; - min_val = log(value(minF1)); - else - val_boundary = (log(value(minS1)) + log(value(maxS1)))/2; - min_val = log(value(minS1)); - end - if strcmp(mu_location,'center') - boundary.value = val_boundary; - elseif strcmp(mu_location,'side') - boundary.value = (min_val + val_boundary)/2; - end - - StimulusSection(obj,'Cal_Mean'); % update the mean and sigma values for each side - - %% Updated Mean/Max for Each Side based upon Distribution Selected - case 'Cal_Mean' - - if frequency_categorization - edge_max = log(value(maxF1)); - edge_min = log(value(minF1)); - else - edge_max = log(value(maxS1)); - edge_min = log(value(minS1)); - end - - % Calculation for Left Side - - % Sigma - - dist_sigma_multiplier = value(sigma_range_Left); - if dist_sigma_multiplier < 0.2 - dist_sigma_multiplier = 0.2; - end - if dist_sigma_multiplier > 1 - dist_sigma_multiplier = 1; - end - sigma_Left.value = (dist_sigma_multiplier * (edge_max - edge_min)) / 3; % as we asked user to provide 3 sigma - - % Mean - if matches(value(Prob_Dist_Left),{'Uniform','Half Normal','Sinusoidal'}) - mean_Left.value = value(boundary); - else - if strcmp(Rule,'S1>S_boundary Left') - if matches(Prob_Dist_Left,{'Anti Half Normal','Anti Sinusoidal'}) - mean_Left.value = edge_max; - elseif matches(Prob_Dist_Left,'Normal') - mean_Left.value = (edge_max + value(boundary))/2; - end - else - if matches(value(Prob_Dist_Left),{'Anti Half Normal','Anti Sinusoidal'}) - mean_Left.value = edge_min; - elseif matches(value(Prob_Dist_Left),'Normal') - mean_Left.value = (edge_min + value(boundary))/2; - end - end - end - - % Calculation for Right Side - - % Sigma - dist_sigma_multiplier = value(sigma_range_Right); - if dist_sigma_multiplier < 0.2 - dist_sigma_multiplier = 0.2; - end - if dist_sigma_multiplier > 1 - dist_sigma_multiplier = 1; - end - sigma_Right.value = (dist_sigma_multiplier * (edge_max - edge_min)) / 3; % as we asked user to provide 3 sigma - - % Mean - if matches(value(Prob_Dist_Right),{'Uniform','Half Normal','Sinusoidal'}) - mean_Right.value = value(boundary); - else - if strcmp(Rule,'S1>S_boundary Right') - if matches(value(Prob_Dist_Right),{'Anti Half Normal','Anti Sinusoidal'}) - mean_Right.value = edge_max; - elseif matches(value(Prob_Dist_Right),'Normal') - mean_Right.value = (edge_max + value(boundary))/2; - end - else - if matches(value(Prob_Dist_Right),{'Anti Half Normal','Anti Sinusoidal'}) - mean_Right.value = edge_min; - elseif matches(Prob_Dist_Right,'Normal') - mean_Right.value = (edge_min + value(boundary))/2; - end - end - end - - %% Calculate Sigma - case 'Cal_Sigma' - - if frequency_categorization - edge_max = log(value(maxF1)); - edge_min = log(value(minF1)); - else - edge_max = log(value(maxS1)); - edge_min = log(value(minS1)); - end - - % Calculation for Left Side Sigma - dist_sigma_multiplier = value(sigma_range_Left); - if dist_sigma_multiplier < 0.2 - dist_sigma_multiplier = 0.2; - end - if dist_sigma_multiplier > 1 - dist_sigma_multiplier = 1; - end - sigma_Left.value = (dist_sigma_multiplier * (edge_max - edge_min)) / 3; % as we asked user to provide 3 sigma - - % Calculation for Right Side Sigma - dist_sigma_multiplier = value(sigma_range_Right); - if dist_sigma_multiplier < 0.2 - dist_sigma_multiplier = 0.2; - end - if dist_sigma_multiplier > 1 - dist_sigma_multiplier = 1; - end - sigma_Right.value = (dist_sigma_multiplier * (edge_max - edge_min)) / 3; % as we asked user to provide 3 sigma - - - %% Case frequency ON - case 'FrequencyCategorization' - if frequency_categorization == 1 - make_visible(maxF1);make_visible(minF1);make_visible(A1_freq); - make_invisible(maxS1);make_invisible(minS1);make_invisible(A1_sigma); - make_invisible(fcut);make_invisible(lfreq);make_invisible(hfreq); make_invisible(filter_type); - StimulusSection(obj,'plot_stimuli'); - else - make_visible(maxS1);make_visible(minS1);make_visible(A1_sigma); - make_visible(fcut);make_visible(lfreq);make_visible(hfreq); make_visible(filter_type); - make_invisible(maxF1);make_invisible(minF1);make_invisible(A1_freq); - StimulusSection(obj,'plot_stimuli'); - end - - StimulusSection(obj,'Cal_Boundary'); % update the boundary - StimulusSection(obj,'Cal_Mean'); % update the mean and sigma values for each side - - - %% Case get_stimuli - % case 'get_stimuli' - % if nargout>0 - % x=value(S1); - % end - - - %% Case close - case 'close' - % Delete all SoloParamHandles who belong to this object and whose - % fullname starts with the name of this mfile: - if exist('myfig', 'var') && isa(myfig, 'SoloParamHandle') && ishandle(value(myfig)) %#ok - delete(value(myfig)); - end - delete_sphandle('owner', ['^@' class(obj) '$'], ... - 'fullname', ['^' mfilename]); - - case 'update_stimulus_history' - ps=value(stimulus_history); - ps(n_completed_trials)=value(thisstimlog(n_completed_trials)); - stimulus_history.value=ps; - - case 'update_stimulus_history_nan' - ps=value(stimulus_history); - ps(n_completed_trials)=value(thisstimlog(n_completed_trials));%nan; - stimulus_history.value=ps; - - %% Case hide - case 'hide' - StimulusShow.value = 0; - set(value(myfig), 'Visible', 'off'); - - %% Case show - case 'show' - StimulusShow.value = 1; - set(value(myfig), 'Visible', 'on'); - - %% Case Show_hide - case 'show_hide' - if StimulusShow == 1 - set(value(myfig), 'Visible', 'on'); %#ok (defined by GetSoloFunctionArgs) - else - set(value(myfig), 'Visible', 'off'); - end - -end - -end diff --git a/Protocols/@Arpit_CentrePokeTraining/StimulusSection.m b/Protocols/@Arpit_CentrePokeTraining/StimulusSection.m index 2123d455..e86efe62 100644 --- a/Protocols/@Arpit_CentrePokeTraining/StimulusSection.m +++ b/Protocols/@Arpit_CentrePokeTraining/StimulusSection.m @@ -24,28 +24,16 @@ SoloParamHandle(obj, 'myfig', 'value', figure('closerequestfcn', [mfilename '(' class(obj) ', ''hide'');'], 'MenuBar', 'none', ... 'Name', mfilename), 'saveable', 0); screen_size = get(0, 'ScreenSize'); - set(value(myfig),'Position',[1 screen_size(4)-740, 1000 1000]); % put fig at top right - set(double(gcf), 'Visible', 'off'); - x=10;y=10; - - SoloParamHandle(obj, 'ax', 'saveable', 0, ... - 'value', axes('Position', [0.01 0.5 0.45 0.45])); - ylabel('log_e A','FontSize',16,'FontName','Cambria Math'); - set(value(ax),'Fontsize',15) - xlabel('Sound Categorization','FontSize',16,'FontName','Cambria Math') - - % SoloParamHandle(obj, 'axperf', 'saveable', 0, ... - % 'value', axes('Position', [0.5 0.5 0.45 0.45])); - % ylabel('log_e A','FontSize',16,'FontName','Cambria Math'); - % set(value(axperf),'Fontsize',15) - % xlabel('Sound Categorization','FontSize',16,'FontName','Cambria Math') + set(value(myfig),'Position',[1 screen_size(4)-740, 300 300]); % put fig at top right + % set(double(gcf), 'Visible', 'off'); + % x=10;y=10; SoundManagerSection(obj, 'declare_new_sound', 'StimAUD1') SoloParamHandle(obj, 'thisstim', 'value', []); SoloParamHandle(obj, 'thisstimlog', 'value', []); SoloParamHandle(obj, 'h1', 'value', []); - y=5; + x = 10; y=5; next_row(y); next_row(y); @@ -71,10 +59,11 @@ next_row(y); DispParam(obj, 'mean_Left', 0.01, x,y,'label','μ Left','TooltipString','mean/max log stim value for the left side distribution'); next_row(y); - DispParam(obj, 'sigma_Left', 0.01, x,y,'label','σ Left','TooltipString','mean/max log stim value for the left side distribution'); + DispParam(obj, 'sigma_Left', 0.01, x,y,'label','σ Left','TooltipString','sigma value(log) for normal distribution for the left side distribution'); next_row(y); NumeditParam(obj, 'sigma_range_Left', 1, x,y,'label','3σ Left','TooltipString',sprintf(['\n A way to reduce the range and increase more distribution towards mean\n', ... - '\n''A value b/w range [0.2 - 1] is acceptable, signifying 3 Sigma (99.7%) value for the left side distribution'])); + '\n''signifying 3 Sigma (99.7%%) value for the left side distribution, \n',... + '\n''A value b/w range [0.2 - 1] is acceptable.'])); set_callback(sigma_range_Left, {mfilename, 'Cal_Sigma'}); next_row(y); next_row(y); @@ -87,10 +76,11 @@ next_row(y); DispParam(obj, 'mean_Right', 0.01, x,y,'label','μ Right','TooltipString','mean/max log stim value for the right side distribution'); next_row(y); - DispParam(obj, 'sigma_Right', 0.01, x,y,'label','σ Right','TooltipString','mean/max log stim value for the left side distribution'); + DispParam(obj, 'sigma_Right', 0.01, x,y,'label','σ Right','TooltipString','sigma value (log) for normal distribution for the right side distribution'); next_row(y); NumeditParam(obj, 'sigma_range_Right', 1, x,y,'label','3σ Right','TooltipString',sprintf(['\n A way to reduce the range and increase more distribution towards mean\n', ... - '\n''A value b/w range [0.2 - 1] is acceptable, signifying 3 Sigma (99.7%) value for the right side distribution'])); + '\n''signifying 3 Sigma (99.7 %%) value for the right side distribution, \n',... + '\n''A value b/w range [0.2 - 1] is acceptable.'])); set_callback(sigma_range_Right, {mfilename, 'Cal_Sigma'}); next_row(y); @@ -139,62 +129,71 @@ make_invisible(maxF1);make_invisible(minF1);make_invisible(A1_freq); next_row(y); + % next_column(y) + ax = axes(gcf,'Position',[x y 200 200]); + set(gca, 'Visible', 'on'); + plot(ax,randi(1,10)); + ylabel('log_e A','FontSize',16,'FontName','Cambria Math'); + set(ax,'Fontsize',15) + xlabel('Sound Categorization','FontSize',16,'FontName','Cambria Math') + SoloParamHandle(obj, 'ax', 'saveable', 0, 'value', ax); + StimulusSection(obj,'plot_stimuli'); case 'prepare_next_trial' + if value(training_stage) > 4 && stimuli_on + StimulusSection(obj,'pick_current_stimulus'); + srate=SoundManagerSection(obj,'get_sample_rate'); + Fs=srate; + T=value(A1_time); + + if frequency_categorization + % produce the tone + A1_freq.value = value(thisstim); + A1 = value(thisstimlog(n_completed_trials+1)); + dur1 = A1_time*1000; + bal=0; + freq1=A1_freq*1000; + vol=0.001; + RVol=vol*min(1,(1+bal)); + LVol=vol*min(1,(1-bal)); + t=0:(1/srate):(dur1/1000); + t = t(1:end-1); + tw=sin(t*2*pi*freq1); + RW=RVol*tw; + %w=[LW;RW]; + AUD1 = RW; + else + % produce noise pattern + A1_sigma.value = value(thisstim); + A1 = value(thisstimlog(n_completed_trials+1)); + [rawA1, rawA2, normA1, normA2]=noisestim(1,1,T,value(fcut),Fs,value(filter_type)); + modulator=singlenoise(1,T,[value(lfreq) value(hfreq)],Fs,'BUTTER'); + AUD1=normA1(1:A1_time*srate).*modulator(1:A1_time*srate).*A1_sigma; + end - StimulusSection(obj,'pick_current_stimulus'); - srate=SoundManagerSection(obj,'get_sample_rate'); - Fs=srate; - T=value(A1_time); - - if frequency_categorization - % produce the tone - A1_freq.value = value(thisstim); - A1 = value(thisstimlog(n_completed_trials+1)); - dur1 = A1_time*1000; - bal=0; - freq1=A1_freq*1000; - vol=0.001; - RVol=vol*min(1,(1+bal)); - LVol=vol*min(1,(1-bal)); - t=0:(1/srate):(dur1/1000); - t = t(1:end-1); - tw=sin(t*2*pi*freq1); - RW=RVol*tw; - %w=[LW;RW]; - AUD1 = RW; - else - % produce noise pattern - A1_sigma.value = value(thisstim); - A1 = value(thisstimlog(n_completed_trials+1)); - [rawA1, rawA2, normA1, normA2]=noisestim(1,1,T,value(fcut),Fs,value(filter_type)); - modulator=singlenoise(1,T,[value(lfreq) value(hfreq)],Fs,'BUTTER'); - AUD1=normA1(1:A1_time*srate).*modulator(1:A1_time*srate).*A1_sigma; - end - - if ~isempty(AUD1) - SoundManagerSection(obj, 'set_sound', 'StimAUD1', [AUD1'; AUD1']) - end + if ~isempty(AUD1) + SoundManagerSection(obj, 'set_sound', 'StimAUD1', [AUD1'; AUD1']) + end - SoundManagerSection(obj, 'send_not_yet_uploaded_sounds'); + SoundManagerSection(obj, 'send_not_yet_uploaded_sounds'); - % Plot current stimulus and move to saving stimulus history + % Plot current stimulus and move to saving stimulus history - if value(thisstimlog(n_completed_trials+1)) > value(boundary)%value(numClass) - set(value(h1), 'YData', value(A1), 'color',[0.4 0.8 0.1],'markerfacecolor',[0.4 0.8 0.1]); - else - set(value(h1), 'YData', value(A1), 'color',[0.8 0.4 0.1],'markerfacecolor',[0.8 0.4 0.1]); - end - - if n_completed_trials > 0 - if ~violation_history(n_completed_trials) && ~timeout_history(n_completed_trials) - StimulusSection(obj,'update_stimulus_history'); + if value(thisstimlog(n_completed_trials+1)) > value(boundary)%value(numClass) + set(value(h1), 'YData', value(A1), 'color',[0.4 0.8 0.1],'markerfacecolor',[0.4 0.8 0.1]); else - StimulusSection(obj,'update_stimulus_history_nan'); + set(value(h1), 'YData', value(A1), 'color',[0.8 0.4 0.1],'markerfacecolor',[0.8 0.4 0.1]); end - end + if n_completed_trials > 0 + if ~violation_history(n_completed_trials) && ~timeout_history(n_completed_trials) + StimulusSection(obj,'update_stimulus_history'); + else + StimulusSection(obj,'update_stimulus_history_nan'); + end + end + end %% Case pick_current_stimulus case 'pick_current_stimulus' if frequency_categorization From 1fb03b0458e9dc7a7c83210fbc546c559ae84b79 Mon Sep 17 00:00:00 2001 From: Arpit Date: Thu, 3 Apr 2025 13:27:54 +0100 Subject: [PATCH 048/164] state error fix in SMA for stage 4 and above. --- .../Arpit_CentrePokeTrainingSMA.m | 2 +- Protocols/@Arpit_CentrePokeTraining/ParamsSection.m | 2 +- Protocols/@Arpit_CentrePokeTraining/StimulusSection.m | 10 +++++----- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/Protocols/@Arpit_CentrePokeTraining/Arpit_CentrePokeTrainingSMA.m b/Protocols/@Arpit_CentrePokeTraining/Arpit_CentrePokeTrainingSMA.m index 5e37dabb..a03bb050 100644 --- a/Protocols/@Arpit_CentrePokeTraining/Arpit_CentrePokeTrainingSMA.m +++ b/Protocols/@Arpit_CentrePokeTraining/Arpit_CentrePokeTrainingSMA.m @@ -303,7 +303,7 @@ sma = add_state(sma,'name','soft_cp','self_timer',CP_duration - SettlingIn_time, ... CP_Duration_wave 'output_actions', {'SchedWaveTrig', 'CP_Duration_wave'},... - 'input_to_statechange', {'Cout','current_state + 1'; 'Tup','legal_poke_end_state';... + 'input_to_statechange', {'Cout','current_state + 1'; 'Tup','side_led_wait_RewardCollection';... 'Rin', 'violation_state'; 'Rout', 'violation_state'; 'Lin', 'violation_state'; 'Lout', 'violation_state'}); %more stringent by giving half the legal cp time else % Soft poke with violation. The difference between this and previous stage is introduction of diff --git a/Protocols/@Arpit_CentrePokeTraining/ParamsSection.m b/Protocols/@Arpit_CentrePokeTraining/ParamsSection.m index c7017195..e41df53a 100644 --- a/Protocols/@Arpit_CentrePokeTraining/ParamsSection.m +++ b/Protocols/@Arpit_CentrePokeTraining/ParamsSection.m @@ -275,7 +275,7 @@ case 'prepare_next_trial' - if value(use_auto_train) == 1 + if value(use_auto_train) == 0 switch value(training_stage) case {1,2} %% learning the reward sound association -left or right led on -> poke -> sound+reward diff --git a/Protocols/@Arpit_CentrePokeTraining/StimulusSection.m b/Protocols/@Arpit_CentrePokeTraining/StimulusSection.m index e86efe62..bd96f84f 100644 --- a/Protocols/@Arpit_CentrePokeTraining/StimulusSection.m +++ b/Protocols/@Arpit_CentrePokeTraining/StimulusSection.m @@ -180,11 +180,11 @@ % Plot current stimulus and move to saving stimulus history - if value(thisstimlog(n_completed_trials+1)) > value(boundary)%value(numClass) - set(value(h1), 'YData', value(A1), 'color',[0.4 0.8 0.1],'markerfacecolor',[0.4 0.8 0.1]); - else - set(value(h1), 'YData', value(A1), 'color',[0.8 0.4 0.1],'markerfacecolor',[0.8 0.4 0.1]); - end + % if value(thisstimlog(n_completed_trials+1)) > value(boundary)%value(numClass) + % set(value(h1), 'YData', value(A1), 'color',[0.4 0.8 0.1],'markerfacecolor',[0.4 0.8 0.1]); + % else + % set(value(h1), 'YData', value(A1), 'color',[0.8 0.4 0.1],'markerfacecolor',[0.8 0.4 0.1]); + % end if n_completed_trials > 0 if ~violation_history(n_completed_trials) && ~timeout_history(n_completed_trials) From 072ce9be805da4f599a1861cf55e999f3b4a7960 Mon Sep 17 00:00:00 2001 From: viktorpm Date: Thu, 3 Apr 2025 15:39:25 +0100 Subject: [PATCH 049/164] bug fixes to get a working version of protocol --- .../@Arpit_CentrePokeTraining/Arpit_CentrePokeTraining.m | 1 + ...entrePokeTraining_SessionDefinition_AutoTrainingStages.m | 6 +++--- Protocols/@Arpit_CentrePokeTraining/ParamsSection.m | 2 +- .../@Arpit_CentrePokeTraining/Training_ParamsSection.m | 2 +- 4 files changed, 6 insertions(+), 5 deletions(-) diff --git a/Protocols/@Arpit_CentrePokeTraining/Arpit_CentrePokeTraining.m b/Protocols/@Arpit_CentrePokeTraining/Arpit_CentrePokeTraining.m index 3e6f70ac..47297187 100644 --- a/Protocols/@Arpit_CentrePokeTraining/Arpit_CentrePokeTraining.m +++ b/Protocols/@Arpit_CentrePokeTraining/Arpit_CentrePokeTraining.m @@ -159,6 +159,7 @@ % Run SessionDefinition *after* ParamsSection so we know whether the trial was a violation or not % push_helper_vars_tosql(obj,n_done_trials); + SessionDefinition(obj, 'next_trial'); SessionPerformanceSection(obj, 'evaluate'); StimulusSection(obj,'prepare_next_trial'); diff --git a/Protocols/@Arpit_CentrePokeTraining/Arpit_CentrePokeTraining_SessionDefinition_AutoTrainingStages.m b/Protocols/@Arpit_CentrePokeTraining/Arpit_CentrePokeTraining_SessionDefinition_AutoTrainingStages.m index 16088a96..7b15f4a9 100644 --- a/Protocols/@Arpit_CentrePokeTraining/Arpit_CentrePokeTraining_SessionDefinition_AutoTrainingStages.m +++ b/Protocols/@Arpit_CentrePokeTraining/Arpit_CentrePokeTraining_SessionDefinition_AutoTrainingStages.m @@ -132,7 +132,7 @@ CreateHelperVar(obj,'stages_trial_counter_oppSide','value',zeros(1,8)); CreateHelperVar(obj,'stages_timeout_rate','value',zeros(1,8)); CreateHelperVar(obj,'stages_violation_rate','value',zeros(1,8)); -CreateHelperVar(obj,'this_stage_opp_side_trials', 'value', '0', 'force_init',true); +CreateHelperVar(obj,'this_stage_opp_side_trials', 'value', 0, 'force_init',true); CreateHelperVar(obj,'stage_start_completed_trial','value',n_completed_trials,'force_init',true); % end @@ -176,7 +176,7 @@ stages_timeout_rate.value = this_stage_timeout_percent; end % Update the reward collection time based upon behav -if size(timeout_history) > 5 +if length(timeout_history) > 5 if all(value(timeout_history(end-1:end))) && value(this_stage_opp_side_trials) >= 2 ParamsSection_RewardCollection_duration.value = min([value(ParamsSection_RewardCollection_duration) + 1,... value(Training_ParamsSection_max_rColl_dur)]); @@ -190,7 +190,7 @@ this_stage_opp_side_trials.value = 0; end end -if size(timeout_history) > 20 +if length(timeout_history) > 20 if all(value(timeout_history(end-19:end))) ParamsSection_RewardCollection_duration.value = 30; callback(ParamsSection_RewardCollection_duration); diff --git a/Protocols/@Arpit_CentrePokeTraining/ParamsSection.m b/Protocols/@Arpit_CentrePokeTraining/ParamsSection.m index e41df53a..c5b74363 100644 --- a/Protocols/@Arpit_CentrePokeTraining/ParamsSection.m +++ b/Protocols/@Arpit_CentrePokeTraining/ParamsSection.m @@ -218,7 +218,7 @@ [stage_fig_x,stage_fig_y] = Training_ParamsSection(obj, 'reinit', value(stage_fig_x),value(stage_fig_y)); % update the training params as well Arpit_CentrePokeTrainingSMA(obj,'reinit'); - + SessionPerformanceSection(obj, 'evaluate'); switch value(training_stage) case {1,2} %% learning the reward sound association -left or right led on -> poke -> sound+reward diff --git a/Protocols/@Arpit_CentrePokeTraining/Training_ParamsSection.m b/Protocols/@Arpit_CentrePokeTraining/Training_ParamsSection.m index c2882e77..82ccf177 100644 --- a/Protocols/@Arpit_CentrePokeTraining/Training_ParamsSection.m +++ b/Protocols/@Arpit_CentrePokeTraining/Training_ParamsSection.m @@ -52,7 +52,7 @@ case 5 % STAGE RUNNING PARAMETERS - NumeditParam(obj, 'max_CP', 1.5, x, y,'label','CP_Dur_Max','TooltipString','max CP duration being trained in this stage'); next_row(y); + NumeditParam(obj, 'max_CP', 5, x, y,'label','CP_Dur_Max','TooltipString','max CP duration being trained in this stage'); next_row(y); NumeditParam(obj, 'CPfraction_inc', 0.002, x, y,'label','CP_frac_Increase','TooltipString','CP duration is increased by this fraction'); next_row(y); NumeditParam(obj, 'min_CP', 1.5, x, y,'label','CP_Dur_Min','TooltipString','min CP duration being trained in this stage'); next_row(y); NumeditParam(obj, 'starting_CP', 0.3, x, y,'TooltipString','min CP duration (minus the settling-in time) during warm up'); next_row(y); From 453faff64589ffe4f6dacddc4c08b5d4046501d2 Mon Sep 17 00:00:00 2001 From: Arpit Date: Thu, 3 Apr 2025 17:45:02 +0100 Subject: [PATCH 050/164] paramSection updated to change the visibility of various parameters based upon stage. --- ...ing_SessionDefinition_AutoTrainingStages.m | 172 ++++++++++-------- .../@Arpit_CentrePokeTraining/ParamsSection.m | 129 ++++++------- 2 files changed, 158 insertions(+), 143 deletions(-) diff --git a/Protocols/@Arpit_CentrePokeTraining/Arpit_CentrePokeTraining_SessionDefinition_AutoTrainingStages.m b/Protocols/@Arpit_CentrePokeTraining/Arpit_CentrePokeTraining_SessionDefinition_AutoTrainingStages.m index 7b15f4a9..236d2e37 100644 --- a/Protocols/@Arpit_CentrePokeTraining/Arpit_CentrePokeTraining_SessionDefinition_AutoTrainingStages.m +++ b/Protocols/@Arpit_CentrePokeTraining/Arpit_CentrePokeTraining_SessionDefinition_AutoTrainingStages.m @@ -44,8 +44,10 @@ ParamsSection_MaxSame.value = 4; callback(ParamsSection_MaxSame); stage_no = value(SessionDefinition_CURRENT_ACTIVE_STAGE); -% ParamsSection_training_stage.value = stage_no; -% callback(ParamsSection_training_stage); +if stage_no ~= value(ParamsSection_training_stage) + ParamsSection_training_stage.value = stage_no; + callback(ParamsSection_training_stage); +end if n_completed_trials > value(stage_start_completed_trial) this_stage_trial_counter = value(stages_trial_counter); this_stage_trial_counter(stage_no) = this_stage_trial_counter(stage_no) + 1; @@ -72,16 +74,18 @@ ClearHelperVarsNotOwned(obj); clear('ans'); % -stage_no = value(SessionDefinition_CURRENT_ACTIVE_STAGE); -this_stage_trial_counter_oppSide = value(stages_trial_counter_oppSide); -this_stage_trial_counter = value(stages_trial_counter); -% only run it if its the start of the day, number of trials is small -if n_completed_trials < 100 - if this_stage_trial_counter(stage_no) > value(Training_ParamsSection_total_trials) && this_stage_trial_counter_oppSide(stage_no) > value(Training_ParamsSection_total_trials_opp) - ParamsSection_training_stage.value = stage_no + 1; - callback(ParamsSection_training_stage); - ParamsSection(obj, 'Changed_Training_Stage'); - SessionDefinition(obj, 'jump_to_stage', 'Timeout Rewarded Side Pokes'); +if ParamsSection_use_auto_train % do completion check if auto training + stage_no = value(SessionDefinition_CURRENT_ACTIVE_STAGE); + this_stage_trial_counter_oppSide = value(stages_trial_counter_oppSide); + this_stage_trial_counter = value(stages_trial_counter); + % only run it if its the start of the day, number of trials is small + if n_completed_trials < 100 + if this_stage_trial_counter(stage_no) > value(Training_ParamsSection_total_trials) && this_stage_trial_counter_oppSide(stage_no) > value(Training_ParamsSection_total_trials_opp) + ParamsSection_training_stage.value = stage_no + 1; + callback(ParamsSection_training_stage); + ParamsSection(obj, 'Changed_Training_Stage'); + SessionDefinition(obj, 'jump_to_stage', 'Timeout Rewarded Side Pokes'); + end end end % @@ -147,8 +151,10 @@ callback(ParamsSection_MaxSame); stage_no = value(SessionDefinition_CURRENT_ACTIVE_STAGE); this_stage_trial_counter = value(stages_trial_counter); -% ParamsSection_training_stage.value = stage_no; -% callback(ParamsSection_training_stage); +if stage_no ~= value(ParamsSection_training_stage) + ParamsSection_training_stage.value = stage_no; + callback(ParamsSection_training_stage); +end if n_completed_trials > value(stage_start_completed_trial) % Update the helper vars this_stage_trial_counter(stage_no) = this_stage_trial_counter(stage_no) + 1; @@ -206,18 +212,20 @@ ClearHelperVarsNotOwned(obj); clear('ans'); % -stage_no = value(SessionDefinition_CURRENT_ACTIVE_STAGE); -this_stage_trial_counter_oppSide = value(stages_trial_counter_oppSide); -this_stage_trial_counter = value(stages_trial_counter); -% only run it if its the start of the day, number of trials is small -if n_completed_trials > 50 - if this_stage_trial_counter(stage_no) > value(Training_ParamsSection_total_trials) && this_stage_trial_counter_oppSide(stage_no) > value(Training_ParamsSection_total_trials_opp) - ParamsSection_training_stage.value = stage_no + 1; - callback(ParamsSection_training_stage); - ParamsSection(obj, 'Changed_Training_Stage'); - SessionDefinition(obj, 'jump_to_stage', 'Introduce Centre Poke'); +if ParamsSection_use_auto_train % do completion check if auto training + stage_no = value(SessionDefinition_CURRENT_ACTIVE_STAGE); + this_stage_trial_counter_oppSide = value(stages_trial_counter_oppSide); + this_stage_trial_counter = value(stages_trial_counter); + % only run it if its the start of the day, number of trials is small + if n_completed_trials > 50 + if this_stage_trial_counter(stage_no) > value(Training_ParamsSection_total_trials) && this_stage_trial_counter_oppSide(stage_no) > value(Training_ParamsSection_total_trials_opp) + ParamsSection_training_stage.value = stage_no + 1; + callback(ParamsSection_training_stage); + ParamsSection(obj, 'Changed_Training_Stage'); + SessionDefinition(obj, 'jump_to_stage', 'Introduce Centre Poke'); + end end -end +end % if exist('ans', 'var') varargout{1}=logical(ans); clear('ans'); @@ -272,8 +280,10 @@ stage_no = value(SessionDefinition_CURRENT_ACTIVE_STAGE); this_stage_trial_counter = value(stages_trial_counter); -% ParamsSection_training_stage.value = stage_no; -% callback(ParamsSection_training_stage); +if stage_no ~= value(ParamsSection_training_stage) + ParamsSection_training_stage.value = stage_no; + callback(ParamsSection_training_stage); +end if n_completed_trials > value(stage_start_completed_trial) % Update the helper vars this_stage_trial_counter(stage_no) = this_stage_trial_counter(stage_no) + 1; @@ -319,13 +329,15 @@ ClearHelperVarsNotOwned(obj); clear('ans'); % -cp_max = value(ParamsSection_SettlingIn_time) + value(ParamsSection_legal_cbreak); -if value(ParamsSection_CP_duration) >= cp_max - ParamsSection_training_stage.value = 4; - callback(ParamsSection_training_stage); - ParamsSection(obj, 'Changed_Training_Stage'); - SessionDefinition(obj, 'jump_to_stage', 'Introduce Violation for Centre Poke'); - last_session_CP.value = value(ParamsSection_CP_duration); +if ParamsSection_use_auto_train % do completion check if auto training + cp_max = value(ParamsSection_SettlingIn_time) + value(ParamsSection_legal_cbreak); + if value(ParamsSection_CP_duration) >= cp_max + ParamsSection_training_stage.value = 4; + callback(ParamsSection_training_stage); + ParamsSection(obj, 'Changed_Training_Stage'); + SessionDefinition(obj, 'jump_to_stage', 'Introduce Violation for Centre Poke'); + last_session_CP.value = value(ParamsSection_CP_duration); + end end % if exist('ans', 'var') @@ -384,8 +396,10 @@ stage_no = value(SessionDefinition_CURRENT_ACTIVE_STAGE); this_stage_trial_counter = value(stages_trial_counter); this_stage_trial_counter_today = value(stages_trial_counter_today); -% ParamsSection_training_stage.value = stage_no; -% callback(ParamsSection_training_stage); +if stage_no ~= value(ParamsSection_training_stage) + ParamsSection_training_stage.value = stage_no; + callback(ParamsSection_training_stage); +end if n_completed_trials > value(stage_start_completed_trial) % Update the helper vars this_stage_trial_counter(stage_no) = this_stage_trial_counter(stage_no) + 1; @@ -440,16 +454,16 @@ ClearHelperVarsNotOwned(obj); clear('ans'); % -cp_max = value(Training_ParamsSection_max_CP); -if value(ParamsSection_CP_duration) >= cp_max && n_completed_trials > 100 -if SessionPerformanceSection_violation_recent < value(Training_ParamsSection_recent_violation) && SessionPerformanceSection_timeout_recent < value(Training_ParamsSection_recent_timeout) && ... - SessionPerformanceSection_violation_stage < value(Training_ParamsSection_stage_violation) - ParamsSection_training_stage.value = 5; - callback(ParamsSection_training_stage); - ParamsSection(obj, 'Changed_Training_Stage'); - SessionDefinition(obj, 'jump_to_stage', 'Introduce Stimuli Sound during Centre Poke'); - last_session_CP.value = value(ParamsSection_CP_duration); -end +if ParamsSection_use_auto_train % do completion check if auto training + cp_max = value(Training_ParamsSection_max_CP); + if value(ParamsSection_CP_duration) >= cp_max && n_completed_trials > 100 && SessionPerformanceSection_violation_recent < value(Training_ParamsSection_recent_violation) && SessionPerformanceSection_timeout_recent < value(Training_ParamsSection_recent_timeout) && ... + SessionPerformanceSection_violation_stage < value(Training_ParamsSection_stage_violation) + ParamsSection_training_stage.value = 5; + callback(ParamsSection_training_stage); + ParamsSection(obj, 'Changed_Training_Stage'); + SessionDefinition(obj, 'jump_to_stage', 'Introduce Stimuli Sound during Centre Poke'); + last_session_CP.value = value(ParamsSection_CP_duration); + end end % if exist('ans', 'var') @@ -510,8 +524,10 @@ n_trial_warmup = value(Training_ParamsSection_warm_up_trials); stage_no = value(SessionDefinition_CURRENT_ACTIVE_STAGE); -% ParamsSection_training_stage.value = stage_no; -% callback(ParamsSection_training_stage); +if stage_no ~= value(ParamsSection_training_stage) + ParamsSection_training_stage.value = stage_no; + callback(ParamsSection_training_stage); +end if n_completed_trials > value(stage_start_completed_trial) % Update the helper vars this_stage_trial_counter = value(stages_trial_counter); @@ -607,16 +623,18 @@ ClearHelperVarsNotOwned(obj); clear('ans'); % -stage_no = value(SessionDefinition_CURRENT_ACTIVE_STAGE); -this_stage_trial_counter = value(stages_trial_counter); -if value(ParamsSection_CP_duration) >= value(Training_ParamsSection_max_CP) && this_stage_trial_counter(stage_no) > value(Training_ParamsSection_total_trials) - if SessionPerformanceSection_violation_recent < value(Training_ParamsSection_recent_violation) && SessionPerformanceSection_timeout_recent < value(Training_ParamsSection_recent_timeout) && ... - SessionPerformanceSection_violation_stage < value(Training_ParamsSection_stage_violation) && n_completed_trials > 100 - ParamsSection_training_stage.value = stage_no + 1; - callback(ParamsSection_training_stage); - ParamsSection(obj, 'Changed_Training_Stage'); - SessionDefinition(obj, 'jump_to_stage', 'Vary Stimuli location during Centre Poke'); - last_session_CP.value = value(ParamsSection_CP_duration); +if ParamsSection_use_auto_train % do completion check if auto training + stage_no = value(SessionDefinition_CURRENT_ACTIVE_STAGE); + this_stage_trial_counter = value(stages_trial_counter); + if value(ParamsSection_CP_duration) >= value(Training_ParamsSection_max_CP) && this_stage_trial_counter(stage_no) > value(Training_ParamsSection_total_trials) + if SessionPerformanceSection_violation_recent < value(Training_ParamsSection_recent_violation) && SessionPerformanceSection_timeout_recent < value(Training_ParamsSection_recent_timeout) && ... + SessionPerformanceSection_violation_stage < value(Training_ParamsSection_stage_violation) && n_completed_trials > 100 + ParamsSection_training_stage.value = stage_no + 1; + callback(ParamsSection_training_stage); + ParamsSection(obj, 'Changed_Training_Stage'); + SessionDefinition(obj, 'jump_to_stage', 'Vary Stimuli location during Centre Poke'); + last_session_CP.value = value(ParamsSection_CP_duration); + end end end % @@ -676,8 +694,10 @@ stim_dur = value(Training_ParamsSection_stim_dur); stage_no = value(SessionDefinition_CURRENT_ACTIVE_STAGE); -% ParamsSection_training_stage.value = stage_no; -% callback(ParamsSection_training_stage); +if stage_no ~= value(ParamsSection_training_stage) + ParamsSection_training_stage.value = stage_no; + callback(ParamsSection_training_stage); +end if n_completed_trials > value(stage_start_completed_trial) % Update the helper vars this_stage_trial_counter = value(stages_trial_counter); @@ -752,15 +772,17 @@ ClearHelperVarsNotOwned(obj); clear('ans'); % -stage_no = value(SessionDefinition_CURRENT_ACTIVE_STAGE); -this_stage_trial_counter = value(stages_trial_counter); -if this_stage_trial_counter(stage_no) > value(Training_ParamsSection_total_trials) - if SessionPerformanceSection_violation_recent < value(Training_ParamsSection_recent_violation) && SessionPerformanceSection_timeout_recent < value(Training_ParamsSection_recent_timeout) && ... - SessionPerformanceSection_violation_stage < value(Training_ParamsSection_stage_violation) && n_completed_trials > 100 - ParamsSection_training_stage.value = stage_no + 1; - callback(ParamsSection_training_stage); - ParamsSection(obj, 'Changed_Training_Stage'); - SessionDefinition(obj, 'jump_to_stage', 'Variable Stimuli Go Cue location during Centre Poke'); +if ParamsSection_use_auto_train % do completion check if auto training + stage_no = value(SessionDefinition_CURRENT_ACTIVE_STAGE); + this_stage_trial_counter = value(stages_trial_counter); + if this_stage_trial_counter(stage_no) > value(Training_ParamsSection_total_trials) + if SessionPerformanceSection_violation_recent < value(Training_ParamsSection_recent_violation) && SessionPerformanceSection_timeout_recent < value(Training_ParamsSection_recent_timeout) && ... + SessionPerformanceSection_violation_stage < value(Training_ParamsSection_stage_violation) && n_completed_trials > 100 + ParamsSection_training_stage.value = stage_no + 1; + callback(ParamsSection_training_stage); + ParamsSection(obj, 'Changed_Training_Stage'); + SessionDefinition(obj, 'jump_to_stage', 'Variable Stimuli Go Cue location during Centre Poke'); + end end end % @@ -829,8 +851,10 @@ random_A1 = 0; stage_no = value(SessionDefinition_CURRENT_ACTIVE_STAGE); -% ParamsSection_training_stage.value = stage_no; -% callback(ParamsSection_training_stage); +if stage_no ~= value(ParamsSection_training_stage) + ParamsSection_training_stage.value = stage_no; + callback(ParamsSection_training_stage); +end if n_completed_trials > value(stage_start_completed_trial) % Update the helper vars this_stage_trial_counter = value(stages_trial_counter); @@ -982,8 +1006,10 @@ a1_time_max = value(ParamsSection_A1_time_Max); stage_no = value(SessionDefinition_CURRENT_ACTIVE_STAGE); -ParamsSection_training_stage.value = stage_no; -callback(ParamsSection_training_stage); +if stage_no ~= value(ParamsSection_training_stage) + ParamsSection_training_stage.value = stage_no; + callback(ParamsSection_training_stage); +end if n_completed_trials > value(stage_start_completed_trial) % Update the helper vars diff --git a/Protocols/@Arpit_CentrePokeTraining/ParamsSection.m b/Protocols/@Arpit_CentrePokeTraining/ParamsSection.m index c5b74363..4599b80a 100644 --- a/Protocols/@Arpit_CentrePokeTraining/ParamsSection.m +++ b/Protocols/@Arpit_CentrePokeTraining/ParamsSection.m @@ -84,7 +84,7 @@ DispParam(obj, 'init_CP_duration', 0.01, x,y,'label','init_CP duration','TooltipString','Duration of Nose in Central Poke before Go cue starts (see Total_CP_duration)'); next_row(y); DispParam(obj, 'CP_duration', PreStim_time+A1_time+time_bet_aud1_gocue, x,y,'label','CP duration','TooltipString','Duration of Nose in Central Poke before Go cue starts (see Total_CP_duration)'); - set_callback(CP_duration, {mfilename, 'new_CP_duration'}); + % set_callback(CP_duration, {mfilename, 'new_CP_duration'}); next_row(y); NumeditParam(obj, 'time_go_cue' ,0.2, x,y,'label','Go Cue Duration','TooltipString','duration of go cue (see Total_CP_duration)'); set_callback(time_go_cue, {mfilename, 'new_time_go_cue'}); @@ -219,96 +219,52 @@ [stage_fig_x,stage_fig_y] = Training_ParamsSection(obj, 'reinit', value(stage_fig_x),value(stage_fig_y)); % update the training params as well Arpit_CentrePokeTrainingSMA(obj,'reinit'); SessionPerformanceSection(obj, 'evaluate'); + switch value(training_stage) case {1,2} %% learning the reward sound association -left or right led on -> poke -> sound+reward - disable(SettlingIn_time); - disable(PreStim_time); - disable(A1_time); - disable(time_bet_aud1_gocue); - + make_invisible(SettlingIn_time); make_invisible(legal_cbreak); make_invisible(cp_timeout); + make_invisible(PreStim_time); make_invisible(PreStim_time_Min); make_invisible(PreStim_time_Max); + make_invisible(A1_time); make_invisible(A1_time_Min); make_invisible(A1_time_Max); + make_invisible(time_bet_aud1_gocue);make_invisible(time_bet_aud1_gocue_Min);make_invisible(time_bet_aud1_gocue_Max); + make_invisible(CP_duration); make_invisible(Total_CP_duration); make_invisible(init_CP_duration); case {3,4} % Centre poke without the A1_Stim but has violation in 4 - enable(SettlingIn_time); - disable(PreStim_time); - disable(A1_time); - enable(time_bet_aud1_gocue); + make_visible(SettlingIn_time); make_visible(legal_cbreak); make_visible(cp_timeout); + make_invisible(PreStim_time); make_invisible(PreStim_time_Min); make_invisible(PreStim_time_Max); + make_invisible(A1_time); make_invisible(A1_time_Min); make_invisible(A1_time_Max); + make_invisible(time_bet_aud1_gocue);make_invisible(time_bet_aud1_gocue_Min);make_invisible(time_bet_aud1_gocue_Max); + make_visible(CP_duration); make_visible(Total_CP_duration); make_visible(init_CP_duration); if n_done_trials <1 && value(warmup_on) ==1 CP_duration.value = value(init_CP_duration); - else - CP_duration.value = value(CP_duration); end - Total_CP_duration.value = value(CP_duration) + value(time_go_cue); %#ok<*NASGU> - - case {5,6,7,8} % - enable(SettlingIn_time); - enable(PreStim_time); - enable(A1_time); - enable(time_bet_aud1_gocue); - - if random_prego_time == 1 - time_range_go_cue = value(time_bet_aud1_gocue_Min):0.01:value(time_bet_aud1_gocue_Max); - time_bet_aud1_gocue.value = time_range_go_cue(randi([1, numel(time_range_go_cue)],1,1)); - end - - if random_A1_time == 1 - time_range_A1_time = value(A1_time_Min): 0.01 : value(A1_time_Max); - A1_time.value = time_range_A1_time(randi([1, numel(time_range_A1_time)],1,1)); - end - - if random_PreStim_time == 1 - time_range_PreStim_time = value(PreStim_time_Min) : 0.01 : value(PreStim_time_Max); - PreStim_time.value = time_range_PreStim_time(randi([1, numel(time_range_PreStim_time)],1,1)); - end - - if n_done_trials <1 && warmup_on ==1 - CP_duration.value = value(init_CP_duration); - else - CP_duration.value = value(SettlingIn_time) + value(A1_time) + value(PreStim_time) + value(time_bet_aud1_gocue); - end Total_CP_duration.value = value(CP_duration) + value(time_go_cue); %#ok<*NASGU> - end - - case 'prepare_next_trial' - - if value(use_auto_train) == 0 - switch value(training_stage) - - case {1,2} %% learning the reward sound association -left or right led on -> poke -> sound+reward - time_go_cue.value=0.200; - disable(SettlingIn_time); - disable(PreStim_time); - disable(A1_time); - disable(time_bet_aud1_gocue); + case {5,6,7} % - - case {3,4} % Centre poke without the A1_Stim but has violation in 4 - - time_go_cue.value=0.100; - enable(SettlingIn_time); - disable(PreStim_time); - disable(A1_time); - enable(time_bet_aud1_gocue); + make_visible(SettlingIn_time); make_visible(legal_cbreak); make_visible(cp_timeout); + make_visible(PreStim_time); make_invisible(PreStim_time_Min); make_invisible(PreStim_time_Max); + make_visible(A1_time); make_invisible(A1_time_Min); make_invisible(A1_time_Max); + make_visible(time_bet_aud1_gocue);make_invisible(time_bet_aud1_gocue_Min);make_invisible(time_bet_aud1_gocue_Max); + make_visible(CP_duration); make_visible(Total_CP_duration); make_visible(init_CP_duration); if n_done_trials <1 && warmup_on ==1 CP_duration.value = value(init_CP_duration); - else - CP_duration.value = value(CP_duration); end + Total_CP_duration.value = value(CP_duration) + value(time_go_cue); %#ok<*NASGU> - case {5,6,7,8} % + case 8 - time_go_cue.value=0.100; - enable(SettlingIn_time); - enable(PreStim_time); - enable(A1_time); - enable(time_bet_aud1_gocue); + make_visible(SettlingIn_time); make_visible(legal_cbreak); make_visible(cp_timeout); + make_visible(PreStim_time); make_visible(PreStim_time_Min); make_visible(PreStim_time_Max); + make_visible(A1_time); make_visible(A1_time_Min); make_visible(A1_time_Max); + make_visible(time_bet_aud1_gocue);make_visible(time_bet_aud1_gocue_Min);make_visible(time_bet_aud1_gocue_Max); + make_visible(CP_duration); make_visible(Total_CP_duration); make_visible(init_CP_duration); if random_prego_time == 1 time_range_go_cue = value(time_bet_aud1_gocue_Min):0.01:value(time_bet_aud1_gocue_Max); @@ -333,9 +289,42 @@ Total_CP_duration.value = value(CP_duration) + value(time_go_cue); %#ok<*NASGU> end + + case 'prepare_next_trial' + + if value(training_stage) == 8 % user setting + + make_visible(SettlingIn_time); + make_visible(PreStim_time); make_visible(PreStim_time_Min); make_visible(PreStim_time_Max); + make_visible(A1_time); make_visible(A1_time_Min); make_visible(A1_time_Max); + make_visible(time_bet_aud1_gocue);make_visible(time_bet_aud1_gocue_Min);make_visible(time_bet_aud1_gocue_Max); + make_visible(CP_duration); make_visible(Total_CP_duration); make_visible(init_CP_duration); + + if random_prego_time == 1 + time_range_go_cue = value(time_bet_aud1_gocue_Min):0.01:value(time_bet_aud1_gocue_Max); + time_bet_aud1_gocue.value = time_range_go_cue(randi([1, numel(time_range_go_cue)],1,1)); + end + + if random_A1_time == 1 + time_range_A1_time = value(A1_time_Min): 0.01 : value(A1_time_Max); + A1_time.value = time_range_A1_time(randi([1, numel(time_range_A1_time)],1,1)); + end + + if random_PreStim_time == 1 + time_range_PreStim_time = value(PreStim_time_Min) : 0.01 : value(PreStim_time_Max); + PreStim_time.value = time_range_PreStim_time(randi([1, numel(time_range_PreStim_time)],1,1)); + end + + if n_done_trials <1 && warmup_on ==1 + CP_duration.value = value(init_CP_duration); + else + CP_duration.value = value(SettlingIn_time) + value(A1_time) + value(PreStim_time) + value(time_bet_aud1_gocue); + end + + Total_CP_duration.value = value(CP_duration) + value(time_go_cue); %#ok<*NASGU> + end - %% update violation, timeout, previous_sides, etc was_viol=false; was_timeout=false; From 5a25da56f48d77ddaefa6e2eb1844252311136fc Mon Sep 17 00:00:00 2001 From: Arpit Date: Thu, 3 Apr 2025 18:19:33 +0100 Subject: [PATCH 051/164] stimulus plot --- Protocols/@Arpit_CentrePokeTraining/StimulusSection.m | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/Protocols/@Arpit_CentrePokeTraining/StimulusSection.m b/Protocols/@Arpit_CentrePokeTraining/StimulusSection.m index bd96f84f..d93927b6 100644 --- a/Protocols/@Arpit_CentrePokeTraining/StimulusSection.m +++ b/Protocols/@Arpit_CentrePokeTraining/StimulusSection.m @@ -24,9 +24,8 @@ SoloParamHandle(obj, 'myfig', 'value', figure('closerequestfcn', [mfilename '(' class(obj) ', ''hide'');'], 'MenuBar', 'none', ... 'Name', mfilename), 'saveable', 0); screen_size = get(0, 'ScreenSize'); - set(value(myfig),'Position',[1 screen_size(4)-740, 300 300]); % put fig at top right - % set(double(gcf), 'Visible', 'off'); - % x=10;y=10; + set(value(myfig),'Position',[1 screen_size(4)-740, 400 400]); % put fig at top right + % set(gcf, 'Visible', 'off'); SoundManagerSection(obj, 'declare_new_sound', 'StimAUD1') SoloParamHandle(obj, 'thisstim', 'value', []); @@ -130,8 +129,10 @@ next_row(y); % next_column(y) - ax = axes(gcf,'Position',[x y 200 200]); - set(gca, 'Visible', 'on'); + stim_dist_fig = figure; + SoloParamHandle(obj, 'stim_dist_fig', 'value', figure('closerequestfcn', [mfilename '(' class(obj) ', ''hide'');'], 'MenuBar', 'none', ... + 'Name', mfilename), 'saveable', 0); + ax = axes(stim_dist_fig,'Position',[0.1 0.1 0.9 0.9]); plot(ax,randi(1,10)); ylabel('log_e A','FontSize',16,'FontName','Cambria Math'); set(ax,'Fontsize',15) From acd883c5288d5e2876d7e710d73c8a6c9420783a Mon Sep 17 00:00:00 2001 From: viktorpm Date: Thu, 3 Apr 2025 18:30:49 +0100 Subject: [PATCH 052/164] debug stimuli figure --- Protocols/@Arpit_CentrePokeTraining/StimulusSection.m | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/Protocols/@Arpit_CentrePokeTraining/StimulusSection.m b/Protocols/@Arpit_CentrePokeTraining/StimulusSection.m index d93927b6..37588619 100644 --- a/Protocols/@Arpit_CentrePokeTraining/StimulusSection.m +++ b/Protocols/@Arpit_CentrePokeTraining/StimulusSection.m @@ -129,10 +129,9 @@ next_row(y); % next_column(y) - stim_dist_fig = figure; SoloParamHandle(obj, 'stim_dist_fig', 'value', figure('closerequestfcn', [mfilename '(' class(obj) ', ''hide'');'], 'MenuBar', 'none', ... - 'Name', mfilename), 'saveable', 0); - ax = axes(stim_dist_fig,'Position',[0.1 0.1 0.9 0.9]); + 'Name', 'StimulusPlot'), 'saveable', 0); + ax = axes(value(stim_dist_fig),'Position',[0.1 0.1 0.9 0.9]); plot(ax,randi(1,10)); ylabel('log_e A','FontSize',16,'FontName','Cambria Math'); set(ax,'Fontsize',15) @@ -488,18 +487,22 @@ case 'hide' StimulusShow.value = 0; set(value(myfig), 'Visible', 'off'); + set(value(stim_dist_fig), 'Visible', 'off'); %% Case show case 'show' StimulusShow.value = 1; set(value(myfig), 'Visible', 'on'); + set(value(stim_dist_fig), 'Visible', 'on'); %% Case Show_hide case 'show_hide' if StimulusShow == 1 - set(value(myfig), 'Visible', 'on'); %#ok (defined by GetSoloFunctionArgs) + set(value(myfig), 'Visible', 'on'); + set(value(stim_dist_fig), 'Visible', 'on');%#ok (defined by GetSoloFunctionArgs) else set(value(myfig), 'Visible', 'off'); + set(value(stim_dist_fig), 'Visible', 'off'); end end From 6d4a4440a080c1bd6dcbce445c5c54c5fba3f3ed Mon Sep 17 00:00:00 2001 From: Arpit Date: Tue, 8 Apr 2025 11:01:09 +0100 Subject: [PATCH 053/164] chnages to plot the stim distribution, added USB based camera to Runrat --- ExperPort/Modules/@runrats/runrats.m | 40 +++++++ .../Arpit_CentrePokeTraining.m | 68 ++++++++++- .../Arpit_CentrePokeTrainingSMA.m | 2 +- ...ing_SessionDefinition_AutoTrainingStages.m | 4 +- .../SessionPerformanceSection.m | 11 ++ .../StimulusSection.m | 110 ++++++++++++------ .../plot_stimuliDistribution.m | 100 ++++++++++++++++ 7 files changed, 295 insertions(+), 40 deletions(-) create mode 100644 Protocols/@Arpit_CentrePokeTraining/plot_stimuliDistribution.m diff --git a/ExperPort/Modules/@runrats/runrats.m b/ExperPort/Modules/@runrats/runrats.m index 0f635491..32c9b9d0 100644 --- a/ExperPort/Modules/@runrats/runrats.m +++ b/ExperPort/Modules/@runrats/runrats.m @@ -145,6 +145,10 @@ SoloParamHandle(obj, 'curProtocol', 'value', ''); %Current Protocol SoloParamHandle(obj,'schedDay','value',[]); + % For Live Webcam Feed + SoloParamHandle(obj,'Camera_Fig_window','value',[]); + SoloParamHandle(obj,'Camera_Obj','value',[]); + SoloParamHandle(obj,'Camera_Image','value',[]); %Let's make the menus try @@ -1414,6 +1418,31 @@ catch disp('failed to start pi camera') end + + % If using USB Webcam, then try using it + try + disp('Connecting to USB HD Camera') + webcam_connected = webcamlist; + webcam_idx = find(contains(webcam_connected,'USB')); + if ~isempty(webcam_idx) % USB Camera connected + cam = webcam(webcam_connected{webcam_idx}); + fig = figure('NumberTitle','off','MenuBar','none'); + fig.Name = 'My Camera'; + ax = axes(fig); + frame = snapshot(cam); + im = image(ax,zeros(size(frame),'uint8')); + axis(ax,'image'); + preview(cam,im) + Camera_Fig_window.value = fig; + Camera_Obj.value = cam; + Camera_Image.value = im; + else + disp('No USB camera connected') + end + catch + disp('failed to connect to USB camera') + end + %Enable the Multi button so the user can stop the session enable(Multi); @@ -1443,6 +1472,7 @@ runrats(obj,'updatelog','runend'); runrats(obj,'disable_all'); set(get_ghandle(Multi),'String','Saving...','Fontsize',32); + %Stop raspberry pi_camera try disp('stopping camera') @@ -1451,6 +1481,16 @@ disp('failed to stop pi camera') end + % Stop USB Camera + try + closePreview(value(Camera_Obj),value(Camera_Image)) + clear(value(Camera_Obj)); + close(value(Camera_Fig_window)); + disp('USB camera stopped') + catch + disp('failed to stop USB camera') + end + %Stop dispatcher and wait for it to respond dispatcher(value(dispobj),'Stop'); %#ok diff --git a/Protocols/@Arpit_CentrePokeTraining/Arpit_CentrePokeTraining.m b/Protocols/@Arpit_CentrePokeTraining/Arpit_CentrePokeTraining.m index 47297187..208197b8 100644 --- a/Protocols/@Arpit_CentrePokeTraining/Arpit_CentrePokeTraining.m +++ b/Protocols/@Arpit_CentrePokeTraining/Arpit_CentrePokeTraining.m @@ -10,7 +10,7 @@ water, comments, soundtable, sqlsummary); %--------------------------------------------------------------- -% BEGIN SECTION COMMONSoundTableSection TO ALL PROTOCOLS, DO NOT MODIFY +% BEGIN SECTION COMMON TO ALL PROTOCOLS, DO NOT MODIFY %--------------------------------------------------------------- % If creating an empty object, return without further ado: @@ -33,6 +33,72 @@ GetSoloFunctionArgs(obj); + +%--------------------------------------------------------------- +% END OF SECTION COMMON TO ALL PROTOCOLS, MODIFY AFTER THIS LINE +%--------------------------------------------------------------- + +% ---- From here on is where you can put the code you like. +% +% Your protocol will be called, at the appropriate times, with the +% following possible actions: +% +% 'init' To initialize -- make figure windows, variables, etc. +% +% 'update' Called periodically within a trial +% +% 'prepare_next_trial' Called when a trial has ended and your protocol is expected +% to produce the StateMachine diagram for the next trial; +% i.e., somewhere in your protocol's response to this call, it +% should call "dispatcher('send_assembler', sma, +% prepare_next_trial_set);" where sma is the +% StateMachineAssembler object that you have prepared and +% prepare_next_trial_set is either a single string or a cell +% with elements that are all strings. These strings should +% correspond to names of states in sma. +% Note that after the prepare_next_trial call, further +% events may still occur while your protocol is thinking, +% before the new StateMachine diagram gets sent. These events +% will be available to you when 'state0' is called on your +% protocol (see below). +% +% 'trial_completed' Called when the any of the prepare_next_trial set +% of states is reached. +% +% 'close' Called when the protocol is to be closed. +% +% +% VARIABLES THAT DISPATCHER WILL ALWAYS INSTANTIATE FOR YOU AS READ_ONLY +% GLOBALS IN YOUR PROTOCOL: +% +% n_done_trials How many trials have been finished; when a trial reaches +% one of the prepare_next_trial states for the first +% time, this variable is incremented by 1. +% +% n_started_trials How many trials have been started. This variable gets +% incremented by 1 every time the state machine goes +% through state 0. +% +% parsed_events The result of running disassemble.m, with the +% parsed_structure flag set to 1, on all events from the +% start of the current trial to now. +% +% latest_parsed_events The result of running disassemble.m, with the +% parsed_structure flag set to 1, on all new events from +% the last time 'update' was called to now. +% +% raw_events All the events obtained in the current trial, not parsed +% or disassembled, but raw as gotten from the State +% Machine object. +% +% current_assembler The StateMachineAssembler object that was used to +% generate the State Machine diagram in effect in the +% current trial. +% +% Trial-by-trial history of parsed_events, raw_events, and +% current_assembler, are automatically stored for you in your protocol by +% dispatcher.m. + switch action %% init diff --git a/Protocols/@Arpit_CentrePokeTraining/Arpit_CentrePokeTrainingSMA.m b/Protocols/@Arpit_CentrePokeTraining/Arpit_CentrePokeTrainingSMA.m index a03bb050..9f696f14 100644 --- a/Protocols/@Arpit_CentrePokeTraining/Arpit_CentrePokeTrainingSMA.m +++ b/Protocols/@Arpit_CentrePokeTraining/Arpit_CentrePokeTrainingSMA.m @@ -93,7 +93,7 @@ sma = StateMachineAssembler('full_trial_structure','use_happenings', 1); % scheduled wave for stimuli/fixed sound, based upon side - if stimuli_on && value(training_stage) > 4 + if stimuli_on && value(training_stage) > 5 sma = add_scheduled_wave(sma, 'name', 'stimplay', 'preamble', PreStim_time, ... 'sustain', sound_duration, 'sound_trig', A1_sound_id); % to play a sound before Go Cue else diff --git a/Protocols/@Arpit_CentrePokeTraining/Arpit_CentrePokeTraining_SessionDefinition_AutoTrainingStages.m b/Protocols/@Arpit_CentrePokeTraining/Arpit_CentrePokeTraining_SessionDefinition_AutoTrainingStages.m index 236d2e37..92a9c6be 100644 --- a/Protocols/@Arpit_CentrePokeTraining/Arpit_CentrePokeTraining_SessionDefinition_AutoTrainingStages.m +++ b/Protocols/@Arpit_CentrePokeTraining/Arpit_CentrePokeTraining_SessionDefinition_AutoTrainingStages.m @@ -917,7 +917,7 @@ callback(ParamsSection_PreStim_time); callback(ParamsSection_A1_time); callback(ParamsSection_time_bet_aud1_gocue); - ParamsSection_CP_duration = value(ParamsSection_SettlingIn_time) + value(ParamsSection_PreStim_time) + value(ParamsSection_A1_time) + value(ParamsSection_time_bet_aud1_gocue); + ParamsSection_CP_duration.value = value(ParamsSection_SettlingIn_time) + value(ParamsSection_PreStim_time) + value(ParamsSection_A1_time) + value(ParamsSection_time_bet_aud1_gocue); end callback(ParamsSection_CP_duration); % @@ -1073,7 +1073,7 @@ callback(ParamsSection_PreStim_time); callback(ParamsSection_A1_time); callback(ParamsSection_time_bet_aud1_gocue); - ParamsSection_CP_duration = value(ParamsSection_SettlingIn_time) + value(ParamsSection_PreStim_time) + value(ParamsSection_A1_time) + value(ParamsSection_time_bet_aud1_gocue); + ParamsSection_CP_duration.value = value(ParamsSection_SettlingIn_time) + value(ParamsSection_PreStim_time) + value(ParamsSection_A1_time) + value(ParamsSection_time_bet_aud1_gocue); end callback(ParamsSection_CP_duration); % diff --git a/Protocols/@Arpit_CentrePokeTraining/SessionPerformanceSection.m b/Protocols/@Arpit_CentrePokeTraining/SessionPerformanceSection.m index 6e2049a6..bb96054f 100644 --- a/Protocols/@Arpit_CentrePokeTraining/SessionPerformanceSection.m +++ b/Protocols/@Arpit_CentrePokeTraining/SessionPerformanceSection.m @@ -83,6 +83,16 @@ case 'evaluate' + this_stage_trial_counter_today = value(stages_trial_counter_today); + this_stage_trial_counter = value(stages_trial_counter); + ntrials_stage.value = this_stage_trial_counter(value(training_stage)); + ntrials_stage_today.value = this_stage_trial_counter_today(value(training_stage)); + + this_stage_timeout_percent = value(stages_timeout_rate); + this_stage_violation_percent = value(stages_violation_rate); + violation_stage.value = this_stage_violation_percent(value(training_stage)); + timeout_stage.value = this_stage_timeout_percent(value(training_stage)); + switch value(training_stage) case 1 %% center led on -> poke in the center -> go cue -> reward light and sound if n_completed_trials > 1 @@ -96,6 +106,7 @@ violation_stage.value = nan; timeout_stage.value = nan; + case {2,3} %% center led on -> poke in the center -> go cue -> reward light and sound if n_completed_trials > 1 ntrials.value = n_completed_trials; diff --git a/Protocols/@Arpit_CentrePokeTraining/StimulusSection.m b/Protocols/@Arpit_CentrePokeTraining/StimulusSection.m index 37588619..fd3b2ff0 100644 --- a/Protocols/@Arpit_CentrePokeTraining/StimulusSection.m +++ b/Protocols/@Arpit_CentrePokeTraining/StimulusSection.m @@ -48,7 +48,7 @@ '\n''S1>S_boundary Right'' means if Aud1 < Aud_boundry then reward will be delivered from the left water spout and if Aud1 > Aud_boundry then water comes from right\n'])); next_row(y, 1);next_row(y, 1); - MenuParam(obj, 'Prob_Dist_Left', {'Uniform','Half Normal','Normal','Sinusoidal','Anti Half Normal','Anti Sinusoidal','Monotonic Increase'}, ... + MenuParam(obj, 'Prob_Dist_Left', {'Uniform','Exponential','Half Normal','Normal','Sinusoidal','Anti Exponential','Anti Half Normal','Anti Sinusoidal','Monotonic Increase'}, ... 'Uniform', x, y,'label','Left Dist', 'labelfraction', 0.35, 'TooltipString', sprintf(['\n Different Probability Distributions for Category A.\n', ... '\n''Normal - the mean is at mid point of range. Half Normal - truncated normal with mean at boundary.\n',... '\n''Anti Half Normal - the mean/max is at the side edge of the range.\n',... @@ -66,7 +66,7 @@ set_callback(sigma_range_Left, {mfilename, 'Cal_Sigma'}); next_row(y); next_row(y); - MenuParam(obj, 'Prob_Dist_Right', {'Uniform','Half Normal','Normal','Sinusoidal','Anti Half Normal','Anti Sinusoidal'}, ... + MenuParam(obj, 'Prob_Dist_Right', {'Uniform','Exponential','Half Normal','Normal','Sinusoidal','Anti Exponential','Anti Half Normal','Anti Sinusoidal','Monotonic Increase'}, ... 'Uniform', x, y, 'label','Right Dist', 'labelfraction', 0.35, 'TooltipString', sprintf(['\n Different Probability Distributions for Category A.\n', ... '\n''Normal - the mean is at mid point of range (side edge - boundary). Half Normal - truncated normal with mean at boundary.\n',... '\n''Anti Half Normal - the mean/max is at the side edge of the range.\n',... @@ -132,7 +132,6 @@ SoloParamHandle(obj, 'stim_dist_fig', 'value', figure('closerequestfcn', [mfilename '(' class(obj) ', ''hide'');'], 'MenuBar', 'none', ... 'Name', 'StimulusPlot'), 'saveable', 0); ax = axes(value(stim_dist_fig),'Position',[0.1 0.1 0.9 0.9]); - plot(ax,randi(1,10)); ylabel('log_e A','FontSize',16,'FontName','Cambria Math'); set(ax,'Fontsize',15) xlabel('Sound Categorization','FontSize',16,'FontName','Cambria Math') @@ -141,7 +140,7 @@ StimulusSection(obj,'plot_stimuli'); case 'prepare_next_trial' - if value(training_stage) > 4 && stimuli_on + if value(training_stage) > 5 && stimuli_on StimulusSection(obj,'pick_current_stimulus'); srate=SoundManagerSection(obj,'get_sample_rate'); Fs=srate; @@ -270,31 +269,51 @@ thisstim.value=exp(stim_i_log); thisstimlog(n_completed_trials+1) = stim_i_log; - %% Case plot_pais + %% Case plot stimuli distribution case 'plot_stimuli' - %% plot the stimuli - if frequency_categorization - boundary.value = (log(value(minF1)) + log(value(maxF1)))/2; + if frequency_categorization stim_min_log = log(value(minF1)); stim_max_log = log(value(maxF1)); - stim_min = value(minF1); - stim_max = value(maxF1); else - boundary.value = (log(value(minS1)) + log(value(maxS1)))/2; - if strcmp(mu_location,'center') - boundary.value = value(boundary); - elseif strcmp(mu_location,'side') - boundary.value = (log(value(minS1)) + value(boundary))/2; - end stim_min_log = log(value(minS1)); stim_max_log = log(value(maxS1)); - stim_min = value(minS1); - stim_max = value(maxS1); end + + dist_type_left = value(Prob_Dist_Left); + dist_mean_left = value(mean_Left); + dist_sigma_left = value(sigma_Left); + dist_type_right = value(Prob_Dist_Right); + dist_mean_right = value(mean_Right); + dist_sigma_right = value(sigma_Right); + dist_range_multiplier_left = value(sigma_range_Left); + dist_range_multiplier_right = value(sigma_range_Right); + + if strcmp(Rule,'S1>S_boundary Left') + edge_max_left = stim_max_log; + edge_min_left = value(boundary); + edge_max_left = edge_min_left + dist_range_multiplier_left * (edge_max_left - edge_min_left); + edge_max_right = value(boundary); + edge_min_right = stim_min_log; + edge_min_right = edge_max_right - dist_range_multiplier_right * (edge_max_right - edge_min_right); + + else % the rule is S1>S_boundary Right + + edge_min_left = stim_min_log; + edge_max_left = value(boundary); + edge_min_left = edge_max_left - dist_range_multiplier_left * (edge_max_left - edge_min_left); + edge_max_right = stim_max_log; + edge_min_right = value(boundary); + edge_max_right = edge_min_right + dist_range_multiplier_right * (edge_max_right - edge_min_right); + end + + cla(value(ax)) - xd=1; - axes(value(ax)); + ax_axes = axes(value(ax)); + + plot_stimuliDistribution(ax_axes,[edge_min, value(boundary), edge_max], dist_type_left,dist_mean_left,dist_sigma_left,... + [edge_min_left edge_max_left],dist_type_right,dist_mean_right,dist_sigma_right,[edge_min_right edge_max_right]); + plot(xd,stim_min_log,'s','MarkerSize',15,'MarkerEdgeColor',[0 0 0],'LineWidth',2) hold on plot(xd,stim_max_log,'s','MarkerSize',15,'MarkerEdgeColor',[0 0 0],'LineWidth',2) @@ -355,23 +374,32 @@ if dist_sigma_multiplier > 1 dist_sigma_multiplier = 1; end - sigma_Left.value = (dist_sigma_multiplier * (edge_max - edge_min)) / 3; % as we asked user to provide 3 sigma + + if strcmp(Rule,'S1>S_boundary Left') + edge_min_left = value(boundary); + edge_max_left = edge_min_left + dist_sigma_multiplier * (edge_max - edge_min_left); + else % the rule is S1>S_boundary Right + edge_max_left = value(boundary); + edge_min_left = edge_max_left - dist_sigma_multiplier * (edge_max_left - edge_min); + end + + sigma_Left.value = (edge_max_left - edge_min_left) / 3; % as we asked user to provide 3 sigma % Mean - if matches(value(Prob_Dist_Left),{'Uniform','Half Normal','Sinusoidal'}) + if matches(value(Prob_Dist_Left),{'Uniform','Half Normal','Sinusoidal','Exponential'}) mean_Left.value = value(boundary); else if strcmp(Rule,'S1>S_boundary Left') - if matches(Prob_Dist_Left,{'Anti Half Normal','Anti Sinusoidal'}) - mean_Left.value = edge_max; + if matches(Prob_Dist_Left,{'Anti Half Normal','Anti Sinusoidal','Anti Exponential'}) + mean_Left.value = edge_max_left; elseif matches(Prob_Dist_Left,'Normal') - mean_Left.value = (edge_max + value(boundary))/2; + mean_Left.value = (edge_max_left + value(boundary))/2; end else - if matches(value(Prob_Dist_Left),{'Anti Half Normal','Anti Sinusoidal'}) - mean_Left.value = edge_min; + if matches(value(Prob_Dist_Left),{'Anti Half Normal','Anti Sinusoidal','Anti Exponential'}) + mean_Left.value = edge_min_left; elseif matches(value(Prob_Dist_Left),'Normal') - mean_Left.value = (edge_min + value(boundary))/2; + mean_Left.value = (edge_min_left + value(boundary))/2; end end end @@ -386,23 +414,33 @@ if dist_sigma_multiplier > 1 dist_sigma_multiplier = 1; end - sigma_Right.value = (dist_sigma_multiplier * (edge_max - edge_min)) / 3; % as we asked user to provide 3 sigma + + if strcmp(Rule,'S1>S_boundary Right') + edge_min_right = value(boundary); + edge_max_right = edge_min_right + dist_sigma_multiplier * (edge_max - edge_min_right); + else % the rule is S1>S_boundary Right + edge_max_right = value(boundary); + edge_min_right = edge_max_right - dist_sigma_multiplier * (edge_max_right - edge_min); + end + + sigma_Right.value = (edge_max_right - edge_min_right) / 3; % as we asked user to provide 3 sigma + % Mean - if matches(value(Prob_Dist_Right),{'Uniform','Half Normal','Sinusoidal'}) + if matches(value(Prob_Dist_Right),{'Uniform','Half Normal','Sinusoidal','Exponential'}) mean_Right.value = value(boundary); else if strcmp(Rule,'S1>S_boundary Right') - if matches(value(Prob_Dist_Right),{'Anti Half Normal','Anti Sinusoidal'}) - mean_Right.value = edge_max; + if matches(value(Prob_Dist_Right),{'Anti Half Normal','Anti Sinusoidal','Anti Exponential'}) + mean_Right.value = edge_max_right; elseif matches(value(Prob_Dist_Right),'Normal') - mean_Right.value = (edge_max + value(boundary))/2; + mean_Right.value = (edge_max_right + value(boundary))/2; end else - if matches(value(Prob_Dist_Right),{'Anti Half Normal','Anti Sinusoidal'}) - mean_Right.value = edge_min; + if matches(value(Prob_Dist_Right),{'Anti Half Normal','Anti Sinusoidal','Anti Exponential'}) + mean_Right.value = edge_min_right; elseif matches(Prob_Dist_Right,'Normal') - mean_Right.value = (edge_min + value(boundary))/2; + mean_Right.value = (edge_min_right + value(boundary))/2; end end end diff --git a/Protocols/@Arpit_CentrePokeTraining/plot_stimuliDistribution.m b/Protocols/@Arpit_CentrePokeTraining/plot_stimuliDistribution.m new file mode 100644 index 00000000..084edf86 --- /dev/null +++ b/Protocols/@Arpit_CentrePokeTraining/plot_stimuliDistribution.m @@ -0,0 +1,100 @@ +function plot_stimuliDistribution(ax, plot_x_range, distribution_left,mean_left,sigma_left,... + range_left,distribution_right,mean_right,sigma_right,range_right) + +x_left = plot_x_range(1):.01:plot_x_range(2); +x_right = plot_x_range(2):.01:plot_x_range(3); + +switch distribution_left + + case 'Uniform' + + pdf_funct = makedist('Uniform','lower',range_left(1),'upper',range_left(2)); + pd_left = pdf(pdf_funct,x_left); + + case 'Half Normal' + + pdf_funct_init = @(x, A) A * exp(- (range_left(2) - x).^2 / (2 * sigma_left^2)); % Define the Half-Normal PDF (Peak at max value) + A_den = integral(@(x) exp(- (range_left(2) - x).^2 / (2 * sigma_left^2)), range_left(1), range_left(2)); % Compute Normalization Constant A + A_sol = 1 / A_den; % Ensuring total probability integrates to 1 + pdf_funct = @(x) A_sol * exp(- (range_left(2) - x).^2 / (2 * sigma_left^2)); % Define the final normalized PDF function + pd_left = pdf_funct(x_left); + + case 'Normal' + + pdf_funct = makedist('Normal','mu',mean_left,'sigma',sigma_left); + pd_left = pdf(pdf_funct,x_left); + + case 'Sinusoidal' + + pdf_funct_init = @(x) sin((pi / 2) * ((x - range_left(1)) / (range_left(2) - range_left(1)))); + A_val = 1 / integral(pdf_funct_init, range_left(1), range_left(2)); % Compute Normalization Constant A + pdf_funct = @(x) A_val * sin((pi / 2) * ((x - range_left(1)) / (range_left(2) - range_left(1)))); % the normalized PDF + pd_left = pdf_funct(x_left); + + case 'Anti Half Normal' + + pdf_funct = makedist('HalfNormal','mu',mean_left,'sigma',sigma_left); + pd_left = pdf(pdf_funct,x_left); + + case 'Anti Sinusoidal' + + pdf_funct_init = @(x) sin((pi / 2) * ((range_left(2) - x) / (range_left(2) - range_left(1)))); % Define the PDF (Peak at min value) + A_val = 1 / integral(pdf_funct_init, range_left(1), range_left(2)); % Compute Normalization Constant A + pdf_funct = @(x) A_val * sin((pi / 2) * ((range_left(2) - x) / (range_left(2) - range_left(1)))); % Define the normalized PDF + pd_left = pdf_funct(x_left); + + case 'Monotonic Increase' + +end + +switch distribution_right + + case 'Uniform' + + pdf_funct = makedist('Uniform','lower',range_right(1),'upper',range_right(2)); + pd_right = pdf(pdf_funct,x_right); + + case 'Half Normal' + + pdf_funct = makedist('HalfNormal','mu',mean_right,'sigma',sigma_right); + pd_right = pdf(pdf_funct,x_right); + + case 'Normal' + + pdf_funct = makedist('Normal','mu',mean_right,'sigma',sigma_right); + pd_right = pdf(pdf_funct,x_right); + + case 'Sinusoidal' + + pdf_funct_init = @(x) sin((pi / 2) * ((range_right(2) - x) / (range_right(2) - range_right(1)))); % Define the PDF (Peak at min value) + A_val = 1 / integral(pdf_funct_init, range_right(1), range_right(2)); % Compute Normalization Constant A + pdf_funct = @(x) A_val * sin((pi / 2) * ((range_right(2) - x) / (range_right(2) - range_right(1)))); % Define the normalized PDF + pd_right = pdf(pdf_funct,x_right); + + case 'Anti Half Normal' + + pdf_funct_init = @(x, A) A * exp(- (range_right(2) - x).^2 / (2 * sigma_right^2)); % Define the Half-Normal PDF (Peak at max value) + A_den = integral(@(x) exp(- (range_right(2) - x).^2 / (2 * sigma_right^2)), range_right(1), range_right(2)); % Compute Normalization Constant A + A_sol = 1 / A_den; % Ensuring total probability integrates to 1 + pdf_funct = @(x) A_sol * exp(- (range_right(2) - x).^2 / (2 * sigma_right^2)); % Define the final normalized PDF function + pd_right = pdf(pdf_funct,x_right); + + case 'Anti Sinusoidal' + + pdf_funct_init = @(x) sin((pi / 2) * ((x - range_right(1)) / (range_right(2) - range_right(1)))); + A_val = 1 / integral(pdf_funct_init, range_right(1), range_right(2)); % Compute Normalization Constant A + pdf_funct = @(x) A_val * sin((pi / 2) * ((x - range_right(1)) / (range_right(2) - range_right(1)))); % the normalized PDF + pd_right = pdf(pdf_funct,x_right); + + case 'Monotonic Increase' + +end + +% Plot the distribution + +plot_x = [x_left,x_right]; +plot_y = [pd_left,pd_right]; + +plot(ax,plot_x,plot_y,'b','LineWidth', 2); + +end \ No newline at end of file From b08fe8b78b89a5e6548859341f1facfc2ddd405f Mon Sep 17 00:00:00 2001 From: ledapent Date: Tue, 8 Apr 2025 11:14:41 +0100 Subject: [PATCH 054/164] increasing sound length --- Protocols/@SoundCalibration/SoundCalibration.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Protocols/@SoundCalibration/SoundCalibration.m b/Protocols/@SoundCalibration/SoundCalibration.m index 250ff610..06b4b3d1 100644 --- a/Protocols/@SoundCalibration/SoundCalibration.m +++ b/Protocols/@SoundCalibration/SoundCalibration.m @@ -185,7 +185,7 @@ lfreq=2000; hfreq=20000; freq = 100; - T = 1; + T = 5; fcut = 110; filter_type = 'GAUS'; A1_sigma = 0.0500; From 043a0b133c3009ba733832453f4c4cde5c29ab45 Mon Sep 17 00:00:00 2001 From: viktorpm Date: Tue, 8 Apr 2025 13:00:04 +0100 Subject: [PATCH 055/164] error debug --- ExperPort/Modules/@runrats/runrats.m | 27 ++++---- .../SessionPerformanceSection.m | 20 +++--- ...tribution.m => StimuliDistribution_plot.m} | 2 +- .../StimulusSection.m | 65 ++++++++++--------- .../@CalibrationMeister/CalibrationMeister.m | 28 ++++---- 5 files changed, 74 insertions(+), 68 deletions(-) rename Protocols/@Arpit_CentrePokeTraining/{plot_stimuliDistribution.m => StimuliDistribution_plot.m} (97%) diff --git a/ExperPort/Modules/@runrats/runrats.m b/ExperPort/Modules/@runrats/runrats.m index 32c9b9d0..778ba7ce 100644 --- a/ExperPort/Modules/@runrats/runrats.m +++ b/ExperPort/Modules/@runrats/runrats.m @@ -1412,12 +1412,12 @@ pause(5); %Start raspberry pi_camera - try - disp('trying camera') - start_camera(value(RigID),value(RatMenu),value(CurrProtocol),'start') - catch - disp('failed to start pi camera') - end + % try + % disp('trying camera') + % start_camera(value(RigID),value(RatMenu),value(CurrProtocol),'start') + % catch + % disp('failed to start pi camera') + % end % If using USB Webcam, then try using it try @@ -1474,16 +1474,17 @@ set(get_ghandle(Multi),'String','Saving...','Fontsize',32); %Stop raspberry pi_camera - try - disp('stopping camera') - start_camera(value(RigID),value(RatMenu),value(CurrProtocol),'stop') - catch - disp('failed to stop pi camera') - end + % try + % disp('stopping camera') + % start_camera(value(RigID),value(RatMenu),value(CurrProtocol),'stop') + % catch + % disp('failed to stop pi camera') + % end % Stop USB Camera try - closePreview(value(Camera_Obj),value(Camera_Image)) + closePreview(value(Camera_Obj)) + clear(value(Camera_Image)) clear(value(Camera_Obj)); close(value(Camera_Fig_window)); disp('USB camera stopped') diff --git a/Protocols/@Arpit_CentrePokeTraining/SessionPerformanceSection.m b/Protocols/@Arpit_CentrePokeTraining/SessionPerformanceSection.m index bb96054f..2d8ce66e 100644 --- a/Protocols/@Arpit_CentrePokeTraining/SessionPerformanceSection.m +++ b/Protocols/@Arpit_CentrePokeTraining/SessionPerformanceSection.m @@ -83,15 +83,17 @@ case 'evaluate' - this_stage_trial_counter_today = value(stages_trial_counter_today); - this_stage_trial_counter = value(stages_trial_counter); - ntrials_stage.value = this_stage_trial_counter(value(training_stage)); - ntrials_stage_today.value = this_stage_trial_counter_today(value(training_stage)); - - this_stage_timeout_percent = value(stages_timeout_rate); - this_stage_violation_percent = value(stages_violation_rate); - violation_stage.value = this_stage_violation_percent(value(training_stage)); - timeout_stage.value = this_stage_timeout_percent(value(training_stage)); + if n_completed_trials >= 1 + this_stage_trial_counter_today = value(stages_trial_counter_today); + this_stage_trial_counter = value(stages_trial_counter); + ntrials_stage.value = this_stage_trial_counter(value(training_stage)); + ntrials_stage_today.value = this_stage_trial_counter_today(value(training_stage)); + + this_stage_timeout_percent = value(stages_timeout_rate); + this_stage_violation_percent = value(stages_violation_rate); + violation_stage.value = this_stage_violation_percent(value(training_stage)); + timeout_stage.value = this_stage_timeout_percent(value(training_stage)); + end switch value(training_stage) case 1 %% center led on -> poke in the center -> go cue -> reward light and sound diff --git a/Protocols/@Arpit_CentrePokeTraining/plot_stimuliDistribution.m b/Protocols/@Arpit_CentrePokeTraining/StimuliDistribution_plot.m similarity index 97% rename from Protocols/@Arpit_CentrePokeTraining/plot_stimuliDistribution.m rename to Protocols/@Arpit_CentrePokeTraining/StimuliDistribution_plot.m index 084edf86..4cc1fec5 100644 --- a/Protocols/@Arpit_CentrePokeTraining/plot_stimuliDistribution.m +++ b/Protocols/@Arpit_CentrePokeTraining/StimuliDistribution_plot.m @@ -1,4 +1,4 @@ -function plot_stimuliDistribution(ax, plot_x_range, distribution_left,mean_left,sigma_left,... +function StimuliDistribution_plot(obj,ax,plot_x_range, distribution_left,mean_left,sigma_left,... range_left,distribution_right,mean_right,sigma_right,range_right) x_left = plot_x_range(1):.01:plot_x_range(2); diff --git a/Protocols/@Arpit_CentrePokeTraining/StimulusSection.m b/Protocols/@Arpit_CentrePokeTraining/StimulusSection.m index fd3b2ff0..eb49126f 100644 --- a/Protocols/@Arpit_CentrePokeTraining/StimulusSection.m +++ b/Protocols/@Arpit_CentrePokeTraining/StimulusSection.m @@ -280,12 +280,12 @@ stim_max_log = log(value(maxS1)); end - dist_type_left = value(Prob_Dist_Left); - dist_mean_left = value(mean_Left); - dist_sigma_left = value(sigma_Left); - dist_type_right = value(Prob_Dist_Right); - dist_mean_right = value(mean_Right); - dist_sigma_right = value(sigma_Right); + % dist_type_left = value(Prob_Dist_Left); + % dist_mean_left = value(mean_Left); + % dist_sigma_left = value(sigma_Left); + % dist_type_right = value(Prob_Dist_Right); + % dist_mean_right = value(mean_Right); + % dist_sigma_right = value(sigma_Right); dist_range_multiplier_left = value(sigma_range_Left); dist_range_multiplier_right = value(sigma_range_Right); @@ -309,31 +309,34 @@ cla(value(ax)) - ax_axes = axes(value(ax)); - - plot_stimuliDistribution(ax_axes,[edge_min, value(boundary), edge_max], dist_type_left,dist_mean_left,dist_sigma_left,... - [edge_min_left edge_max_left],dist_type_right,dist_mean_right,dist_sigma_right,[edge_min_right edge_max_right]); - - plot(xd,stim_min_log,'s','MarkerSize',15,'MarkerEdgeColor',[0 0 0],'LineWidth',2) - hold on - plot(xd,stim_max_log,'s','MarkerSize',15,'MarkerEdgeColor',[0 0 0],'LineWidth',2) - line([0,2], [value(boundary),value(boundary)]); - axis square - set(value(ax),'ytick',([stim_min_log, stim_max_log]),'xtick',xd); - set(value(ax),'yticklabel',([stim_min, stim_max]),'xticklabel','S1'); - ylabel('\sigma_1 in log scale','FontSize',16,'FontName','Cambria Math'); - set(value(ax),'Fontsize',15) - xlabel('S1','FontSize',16,'FontName','Cambria Math') - - StimulusSection(obj,'pick_current_stimulus'); - A1 = value(thisstim); - - %% plot the stimulus; - if value(thisstim) > value(boundary)%value(numClass) - h1.value=plot(xd,value(A1),'s','color',[0.4 0.8 0.1],'markerfacecolor',[0.4 0.8 0.1],'MarkerSize',15,'LineWidth',3); - else - h1.value=plot(xd,value(A1),'s','color',[0.8 0.4 0.1],'markerfacecolor',[0.8 0.4 0.1],'MarkerSize',15,'LineWidth',3); - end + x_range = [stim_min_log, value(boundary), stim_max_log]; + left_range = [edge_min_left edge_max_left]; + right_range = [edge_min_right edge_max_right]; + stim_plot_axes = value(ax); + + StimuliDistribution_plot(obj,stim_plot_axes,x_range,value(Prob_Dist_Left),value(mean_Left),value(sigma_Left),left_range,... + value(Prob_Dist_Right),value(mean_Right),value(sigma_Right),right_range); + + % plot(xd,stim_min_log,'s','MarkerSize',15,'MarkerEdgeColor',[0 0 0],'LineWidth',2) + % hold on + % plot(xd,stim_max_log,'s','MarkerSize',15,'MarkerEdgeColor',[0 0 0],'LineWidth',2) + % line([0,2], [value(boundary),value(boundary)]); + % axis square + % set(value(ax),'ytick',([stim_min_log, stim_max_log]),'xtick',xd); + % set(value(ax),'yticklabel',([stim_min, stim_max]),'xticklabel','S1'); + % ylabel('\sigma_1 in log scale','FontSize',16,'FontName','Cambria Math'); + % set(value(ax),'Fontsize',15) + % xlabel('S1','FontSize',16,'FontName','Cambria Math') + + % StimulusSection(obj,'pick_current_stimulus'); + % A1 = value(thisstim); + % + % %% plot the stimulus; + % if value(thisstim) > value(boundary)%value(numClass) + % h1.value=plot(xd,value(A1),'s','color',[0.4 0.8 0.1],'markerfacecolor',[0.4 0.8 0.1],'MarkerSize',15,'LineWidth',3); + % else + % h1.value=plot(xd,value(A1),'s','color',[0.8 0.4 0.1],'markerfacecolor',[0.8 0.4 0.1],'MarkerSize',15,'LineWidth',3); + % end %% Boundary Calculate case 'Cal_Boundary' diff --git a/Protocols/@CalibrationMeister/CalibrationMeister.m b/Protocols/@CalibrationMeister/CalibrationMeister.m index ee51c314..30c24f43 100644 --- a/Protocols/@CalibrationMeister/CalibrationMeister.m +++ b/Protocols/@CalibrationMeister/CalibrationMeister.m @@ -291,32 +291,32 @@ set(mh,'Position', [main_header_pos main_header_size],'FontSize', main_header_font_size,... 'BackgroundColor', main_header_bgcolor,'ForegroundColor',main_header_fgcolor); - SubHeaderParam(obj, 'sub_header_3',sub_header_3_string,20,20,'TooltipString','Change them at your own risk!!!'); + SubheaderParam(obj, 'sub_header_3',sub_header_3_string,20,20,'TooltipString','Change them at your own risk!!!'); mh=get_ghandle(sub_header_3); set(mh,'Position', [sub_header_3_pos sub_header_3_size],'FontSize', sub_header_3_font_size,... 'BackgroundColor', sub_header_3_bgcolor,'ForegroundColor',sub_header_3_fgcolor); - SubHeaderParam(obj, 'sub_header_2',sub_header_2_string,20,20); + SubheaderParam(obj, 'sub_header_2',sub_header_2_string,20,20); mh=get_ghandle(sub_header_2); set(mh,'Position', [sub_header_2_pos sub_header_2_size],'FontSize', sub_header_2_font_size,... 'BackgroundColor', sub_header_2_bgcolor,'ForegroundColor',sub_header_2_fgcolor); - SubHeaderParam(obj, 'sub_header_1',sub_header_1_string,20,20); + SubheaderParam(obj, 'sub_header_1',sub_header_1_string,20,20); mh=get_ghandle(sub_header_1); set(mh,'Position', [sub_header_1_pos sub_header_1_size],'FontSize', sub_header_1_font_size,... 'BackgroundColor', sub_header_1_bgcolor,'ForegroundColor',sub_header_1_fgcolor); - SubHeaderParam(obj, 'sub_header_4',sub_header_4_string,20,20); + SubheaderParam(obj, 'sub_header_4',sub_header_4_string,20,20); mh=get_ghandle(sub_header_4); set(mh,'Position', [sub_header_4_pos sub_header_4_size],'FontSize', sub_header_4_font_size,... 'BackgroundColor', sub_header_4_bgcolor,'ForegroundColor',sub_header_4_fgcolor); - SubHeaderParam(obj, 'sub_header_5',sub_header_5_string,20,20,'TooltipString','These are the estimates of pulse times which will be used to achieve the next target under consideration'); + SubheaderParam(obj, 'sub_header_5',sub_header_5_string,20,20,'TooltipString','These are the estimates of pulse times which will be used to achieve the next target under consideration'); mh=get_ghandle(sub_header_5); set(mh,'Position', [sub_header_5_pos sub_header_5_size],'FontSize', sub_header_5_font_size,... 'BackgroundColor', sub_header_5_bgcolor,'ForegroundColor',sub_header_5_fgcolor); - SubHeaderParam(obj, 'sub_header_6',sub_header_6_string,20,20,'TooltipString','Tells the current status of calibration for this rig'); + SubheaderParam(obj, 'sub_header_6',sub_header_6_string,20,20,'TooltipString','Tells the current status of calibration for this rig'); mh=get_ghandle(sub_header_6); set(mh,'Position', [sub_header_6_pos sub_header_6_size],'FontSize', sub_header_6_font_size,'FontWeight',sub_header_6_font_wieght,... 'BackgroundColor', sub_header_6_bgcolor,'ForegroundColor',sub_header_6_fgcolor); @@ -370,45 +370,45 @@ % Buttons and their callbacks: Start % Settings: Start - NumEditParam(obj, 'inter_valve_pause',value(inter_valve_pause_default),10,200,'label',INTER_VALVE_PAUSE_LABEL,'labelfraction',universal_label_fraction); + NumeditParam(obj, 'inter_valve_pause',value(inter_valve_pause_default),10,200,'label',INTER_VALVE_PAUSE_LABEL,'labelfraction',universal_label_fraction); lh=get_glhandle(inter_valve_pause); set(lh(2),'FontSize', universal_label_font_size, 'BackgroundColor', universal_label_color,'Position',[60 200 220 20]); set_callback(inter_valve_pause,{mfilename,'verify_inter_valve_pause'}); - NumEditParam(obj, 'num_pulses',value(num_pulses_default),10,180,'label',NUM_PULSES_LABEL,'labelfraction',universal_label_fraction); + NumeditParam(obj, 'num_pulses',value(num_pulses_default),10,180,'label',NUM_PULSES_LABEL,'labelfraction',universal_label_fraction); lh=get_glhandle(num_pulses); set(lh(2),'FontSize', universal_label_font_size, 'BackgroundColor', universal_label_color,'Position',[60 180 220 20]); set_callback(num_pulses,{mfilename,'verify_num_pulses'}); - NumEditParam(obj, 'left_pulse_time',value(pulse_time_default),10,160,'label',LEFT_PULSE_TIME_LABEL,'labelfraction',universal_label_fraction); + NumeditParam(obj, 'left_pulse_time',value(pulse_time_default),10,160,'label',LEFT_PULSE_TIME_LABEL,'labelfraction',universal_label_fraction); lh=get_glhandle(left_pulse_time); set(lh(2),'FontSize', universal_label_font_size, 'BackgroundColor', universal_label_color,'Position',[60 160 220 20]); set_callback(left_pulse_time,{mfilename,'verify_pulse_times'}); if ~valves_used(1); disable(left_pulse_time); end; - NumEditParam(obj, 'center_pulse_time',value(pulse_time_default),10,140,'label',CENTER_PULSE_TIME_LABEL,'labelfraction',universal_label_fraction); + NumeditParam(obj, 'center_pulse_time',value(pulse_time_default),10,140,'label',CENTER_PULSE_TIME_LABEL,'labelfraction',universal_label_fraction); lh=get_glhandle(center_pulse_time); set(lh(2),'FontSize', universal_label_font_size, 'BackgroundColor', universal_label_color,'Position',[60 140 220 20]); set_callback(center_pulse_time,{mfilename,'verify_pulse_times'}); if ~valves_used(2); disable(center_pulse_time); end; - NumEditParam(obj, 'right_pulse_time',value(pulse_time_default),10,120,'label',RIGHT_PULSE_TIME_LABEL,'labelfraction',universal_label_fraction); + NumeditParam(obj, 'right_pulse_time',value(pulse_time_default),10,120,'label',RIGHT_PULSE_TIME_LABEL,'labelfraction',universal_label_fraction); lh=get_glhandle(right_pulse_time); set(lh(2),'FontSize', universal_label_font_size, 'BackgroundColor', universal_label_color,'Position',[60 120 220 20]); set_callback(right_pulse_time,{mfilename,'verify_pulse_times'}); if ~valves_used(3); disable(right_pulse_time); end; - NumEditParam(obj, 'low_target_dispense',value(low_target_dispense_default),10,100,'label',LOW_TARGET_LABEL,'labelfraction',universal_label_fraction); + NumeditParam(obj, 'low_target_dispense',value(low_target_dispense_default),10,100,'label',LOW_TARGET_LABEL,'labelfraction',universal_label_fraction); lh=get_glhandle(low_target_dispense); set(lh(2),'FontSize', universal_label_font_size, 'BackgroundColor', universal_label_color,'Position',[60 100 220 20]); set_callback(low_target_dispense,{mfilename,'verify_target_dispense'}); - NumEditParam(obj, 'high_target_dispense',value(high_target_dispense_default),10,80,'label',HIGH_TARGET_LABEL,'labelfraction',universal_label_fraction); + NumeditParam(obj, 'high_target_dispense',value(high_target_dispense_default),10,80,'label',HIGH_TARGET_LABEL,'labelfraction',universal_label_fraction); lh=get_glhandle(high_target_dispense); set(lh(2),'FontSize', universal_label_font_size, 'BackgroundColor', universal_label_color,'Position',[60 80 220 20]); set_callback(high_target_dispense,{mfilename,'verify_target_dispense'}); - NumEditParam(obj, 'error_tolerance',value(error_tolerance_default),10,60,'label',ERROR_TOLERANCE_LABEL,'labelfraction',universal_label_fraction); + NumeditParam(obj, 'error_tolerance',value(error_tolerance_default),10,60,'label',ERROR_TOLERANCE_LABEL,'labelfraction',universal_label_fraction); lh=get_glhandle(error_tolerance); set(lh(2),'FontSize', universal_label_font_size, 'BackgroundColor', universal_label_color,'Position',[60 60 220 20]); set_callback(error_tolerance,{mfilename,'verify_error_tolerance'}); From 1f482416be496b7cffca526fc6f0dab23766d290 Mon Sep 17 00:00:00 2001 From: Arpit Date: Tue, 8 Apr 2025 14:04:43 +0100 Subject: [PATCH 056/164] commented USB camera in runrats as its crashing MATLAB --- ExperPort/Modules/@runrats/runrats.m | 62 ++++++++++++++-------------- 1 file changed, 31 insertions(+), 31 deletions(-) diff --git a/ExperPort/Modules/@runrats/runrats.m b/ExperPort/Modules/@runrats/runrats.m index 778ba7ce..ed41a535 100644 --- a/ExperPort/Modules/@runrats/runrats.m +++ b/ExperPort/Modules/@runrats/runrats.m @@ -1420,28 +1420,28 @@ % end % If using USB Webcam, then try using it - try - disp('Connecting to USB HD Camera') - webcam_connected = webcamlist; - webcam_idx = find(contains(webcam_connected,'USB')); - if ~isempty(webcam_idx) % USB Camera connected - cam = webcam(webcam_connected{webcam_idx}); - fig = figure('NumberTitle','off','MenuBar','none'); - fig.Name = 'My Camera'; - ax = axes(fig); - frame = snapshot(cam); - im = image(ax,zeros(size(frame),'uint8')); - axis(ax,'image'); - preview(cam,im) - Camera_Fig_window.value = fig; - Camera_Obj.value = cam; - Camera_Image.value = im; - else - disp('No USB camera connected') - end - catch - disp('failed to connect to USB camera') - end + % try + % disp('Connecting to USB HD Camera') + % webcam_connected = webcamlist; + % webcam_idx = find(contains(webcam_connected,'USB')); + % if ~isempty(webcam_idx) % USB Camera connected + % cam = webcam(webcam_connected{webcam_idx}); + % fig = figure('NumberTitle','off','MenuBar','none'); + % fig.Name = 'My Camera'; + % ax = axes(fig); + % frame = snapshot(cam); + % im = image(ax,zeros(size(frame),'uint8')); + % axis(ax,'image'); + % preview(cam,im) + % Camera_Fig_window.value = fig; + % Camera_Obj.value = cam; + % Camera_Image.value = im; + % else + % disp('No USB camera connected') + % end + % catch + % disp('failed to connect to USB camera') + % end %Enable the Multi button so the user can stop the session enable(Multi); @@ -1482,15 +1482,15 @@ % end % Stop USB Camera - try - closePreview(value(Camera_Obj)) - clear(value(Camera_Image)) - clear(value(Camera_Obj)); - close(value(Camera_Fig_window)); - disp('USB camera stopped') - catch - disp('failed to stop USB camera') - end + % try + % closePreview(value(Camera_Obj)) + % clear(value(Camera_Image)) + % clear(value(Camera_Obj)); + % close(value(Camera_Fig_window)); + % disp('USB camera stopped') + % catch + % disp('failed to stop USB camera') + % end %Stop dispatcher and wait for it to respond dispatcher(value(dispobj),'Stop'); %#ok From 3d27e041a3bafa4456ab87a715457e880a926cbb Mon Sep 17 00:00:00 2001 From: Arpit Date: Tue, 22 Apr 2025 15:37:26 +0100 Subject: [PATCH 057/164] added support for bonsai camera control --- .../Arpit_CentrePokeTraining.m | 32 +- .../Arpit_CentrePokeTrainingSMA.m | 9 +- .../Connect_Bonsai_Camera.m | 220 ++++++ .../@Arpit_CentrePokeTraining/ParamsSection.m | 2 +- .../ProduceNoiseStimuli.m | 33 - .../StimulusSection.m | 172 +++-- .../@Arpit_CentrePokeTraining/noiselib.m | 51 -- .../CreateSamples_from_Distribution.m | 0 .../{ => private}/StimuliDistribution_plot.m | 2 +- .../Arpit_SoundCalibration.m | 707 ++++++++++++++++++ .../Arpit_SoundCatContinuous.m | 212 ++++++ .../@Arpit_SoundCatContinuous/SideSection.m | 505 +++++++++++++ .../StimulatorSection.asv | 325 ++++++++ .../StimulatorSection.m | 317 ++++++++ 14 files changed, 2422 insertions(+), 165 deletions(-) create mode 100644 Protocols/@Arpit_CentrePokeTraining/Connect_Bonsai_Camera.m delete mode 100644 Protocols/@Arpit_CentrePokeTraining/ProduceNoiseStimuli.m delete mode 100644 Protocols/@Arpit_CentrePokeTraining/noiselib.m rename Protocols/@Arpit_CentrePokeTraining/{ => private}/CreateSamples_from_Distribution.m (100%) rename Protocols/@Arpit_CentrePokeTraining/{ => private}/StimuliDistribution_plot.m (97%) create mode 100644 Protocols/@Arpit_SoundCalibration/Arpit_SoundCalibration.m create mode 100644 Protocols/@Arpit_SoundCatContinuous/Arpit_SoundCatContinuous.m create mode 100644 Protocols/@Arpit_SoundCatContinuous/SideSection.m create mode 100644 Protocols/@Arpit_SoundCatContinuous/StimulatorSection.asv create mode 100644 Protocols/@Arpit_SoundCatContinuous/StimulatorSection.m diff --git a/Protocols/@Arpit_CentrePokeTraining/Arpit_CentrePokeTraining.m b/Protocols/@Arpit_CentrePokeTraining/Arpit_CentrePokeTraining.m index 208197b8..ca35b377 100644 --- a/Protocols/@Arpit_CentrePokeTraining/Arpit_CentrePokeTraining.m +++ b/Protocols/@Arpit_CentrePokeTraining/Arpit_CentrePokeTraining.m @@ -206,6 +206,33 @@ next_row(y); next_row(y); SessionDefinition(obj, 'init', x, y, value(myfig)); next_row(y, 2); %#ok + %% Before preparing the trial, start with the Bonsai app to control the USB based Camera + % Declare the folder location for saving the video files + current_dir = cd; + ratter_dir = extractBefore(current_dir,'ratter'); + main_dir_video = [ratter_dir '\ratter_Videos']; + date_str = regexprep(char(datetime('today','Format','yyyy-MM-dd')), '[^0-9]', ''); + video_foldername = sprintf('video_@%s_%s_%s_%s',name,expmtr,ratname,date_str); + rat_dir = sprintf('%s\\%s\\%s',main_dir_video,expmtr,ratname); + video_save_dir = sprintf('%s\\%s\\%s\\%s',main_dir_video,expmtr,ratname,video_foldername); + % We have the general structure of folder save location, now need to + % check if there is any other folder for same date. We will add a + % alphabet in the end based upon the no. of files present. + if exist('rat_dir','folder') == 7 + folderNames_rat_dir = {listing(find([listing.isdir])).name}; + folderNames_rat_dir = folderNames_rat_dir(~ismember(folderNames_rat_dir,{'.','..'})); % Remove the '.' and '..' entries (current and parent directories) + sessions_today = length(find(contains(folderNames_rat_dir,video_foldername))); % number of folders containing the video foldername + video_save_dir = [video_save_dir char(sessions_today + 97)]; + else + video_save_dir = [video_save_dir char(97)]; + end + makedir(video_save_dir); + SoloParamHandle(obj, 'Video_Saving_Folder', 'value', video_save_dir); + + Connect_Bonsai_Camera(obj,'init'); + + %% + feval(mfilename, obj, 'prepare_next_trial'); %% change_water_modulation_params @@ -227,7 +254,7 @@ % push_helper_vars_tosql(obj,n_done_trials); SessionDefinition(obj, 'next_trial'); - SessionPerformanceSection(obj, 'evaluate'); + StimulusSection(obj,'prepare_next_trial'); SoundManagerSection(obj, 'send_not_yet_uploaded_sounds'); [sma, prepare_next_trial_states] = Arpit_CentrePokeTrainingSMA(obj, 'prepare_next_trial'); @@ -252,7 +279,8 @@ %% trial_completed case 'trial_completed' - + % Update the Metrics Calculated + SessionPerformanceSection(obj, 'evaluate'); % Do any updates in the protocol that need doing: feval(mfilename, 'update'); % And PokesPlot needs completing the trial: diff --git a/Protocols/@Arpit_CentrePokeTraining/Arpit_CentrePokeTrainingSMA.m b/Protocols/@Arpit_CentrePokeTraining/Arpit_CentrePokeTrainingSMA.m index 9f696f14..01118875 100644 --- a/Protocols/@Arpit_CentrePokeTraining/Arpit_CentrePokeTrainingSMA.m +++ b/Protocols/@Arpit_CentrePokeTraining/Arpit_CentrePokeTrainingSMA.m @@ -59,9 +59,10 @@ %% Setup sounds - sone_sound_id = SoundManagerSection(obj, 'get_sound_id', 'SOneSound'); - stwo_sound_id = SoundManagerSection(obj, 'get_sound_id', 'STwoSound'); - A1_sound_id = SoundManagerSection(obj, 'get_sound_id', 'StimAUD1'); + + sone_sound_id = SoundManagerSection(obj, 'get_sound_id', 'SOneSound'); % fixed sound for 1 side + stwo_sound_id = SoundManagerSection(obj, 'get_sound_id', 'STwoSound'); % fixed sound for other side + A1_sound_id = SoundManagerSection(obj, 'get_sound_id', 'StimAUD1'); % distribution based sound sound_duration = value(A1_time); % SoundManagerSection(obj, 'get_sound_duration', 'SOneSound'); go_sound_id = SoundManagerSection(obj, 'get_sound_id', 'GoSound'); go_cue_duration = value(time_go_cue); % SoundManagerSection(obj, 'get_sound_duration', 'GoSound'); @@ -93,7 +94,7 @@ sma = StateMachineAssembler('full_trial_structure','use_happenings', 1); % scheduled wave for stimuli/fixed sound, based upon side - if stimuli_on && value(training_stage) > 5 + if stimuli_on sma = add_scheduled_wave(sma, 'name', 'stimplay', 'preamble', PreStim_time, ... 'sustain', sound_duration, 'sound_trig', A1_sound_id); % to play a sound before Go Cue else diff --git a/Protocols/@Arpit_CentrePokeTraining/Connect_Bonsai_Camera.m b/Protocols/@Arpit_CentrePokeTraining/Connect_Bonsai_Camera.m new file mode 100644 index 00000000..11ee4c20 --- /dev/null +++ b/Protocols/@Arpit_CentrePokeTraining/Connect_Bonsai_Camera.m @@ -0,0 +1,220 @@ +function Connect_Bonsai_Camera(obj,action) + +GetSoloFunctionArgs(obj); + + +% C:\RatterVideos\ExpName\RateName\Videos_Protocolname_ExpName_RatName_date +% (the same format as Data file) + +%%%%%%%%% DO NOT CHANGE THEM UNLESS YOU MAKE THE SAME CHANGES IN BONSAI %%%%%%%%% + +% UDP Connection Parameters to connect to Bonsai +bonsaiComputerIP = '127.0.0.1'; % IP address of the PC running Bonsai (use '127.0.0.1' if same PC) +bonsaiComputer_name = 'Receiver'; +bonsaiUdpPort = 9090; % Port configured in Bonsai's UdpReceiver +% Sending the Command to Turn on the Camera. Remember to use this +% as in Bonsai I am using the address as /camera and using +% condition to compare the arriving message. The message could only +% be 'start' or 'stop' with '/camera' address for bonsai to react +startCommand = "start"; % Use string type. MUST MATCH Bonsai Filter Value +stopCommand = "stop"; % Use string type. MUST MATCH Bonsai Filter Value +camera_command_address = "/camera"; % Use string type. MUST MATCH Bonsai Address Value +recording_command_address = "/record"; % Use string type. MUST MATCH Bonsai Address Value +bonsai_path = 'C:\Users\Turin\Downloads\Bonsai\Bonsai.exe'; % Path of Bonsai App +file_path = 'C:\Users\Turin\Desktop\bonsai_script.bonsai'; % Path for .bonsai file + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +switch action + + case 'init' + + % --- Create UDP Port Object --- + % This object can send to any destination without prior connection + udpSender = udpport("datagram","IPV4",'LocalHost',bonsaiComputerIP,... + 'LocalPort',9091,'Tag','MATLABSender'); + + SoloParamHandle(obj, 'UDPSender', 'value', udpSender); + + % Now that we have created the UPD connection, we can send commands + % over OSC. Before that we need to open the Bonsai App + + command = sprintf('"%s" "%s" --start', bonsai_path, file_path); + system([command, ' &']); + + % Before starting the streaming of Camera, I need to send the + % file directory for saving the file otherwise Bonsai can run into + % error as it will try saving files in the predefined folder in + % bonsai and that can conflict with file already present there. + + oscMsg_file_directory = createOSCMessage(recording_command_address, [value(Video_Saving_Folder) '\Trial.avi']); + write(udpSender, oscMsg_file_directory, "uint8", bonsaiComputerIP,bonsaiUdpPort); + + % OSC message to start the camera + oscMsg_Camera_start = createOSCMessage(camera_command_address, startCommand); + % the command to send message to Bonsai + write(udpSender, oscMsg_Camera_start, "uint8", bonsaiComputerIP,bonsaiUdpPort); + + % NOTE: Ideally I should start saving the trials once the experimenter presses + % Run either on dispatcher or Runrats. But, I dont want to make the changes there + % so would start recording as soon as the protocol is loaded and camera starts streaming + + case 'next_trial' + + % in this I send a command to bonsai so that it creates a new file + % for each trial + + filename = sprintf('%s//%s//%s',work_dir,exp_name,rat_name); + oscMsg_filename = createOSCMessage(recording_command_address, filename); + write(value(udpSender), oscMsg_filename, "uint8", bonsaiComputerIP,bonsaiUdpPort); + + case 'close' + + % this would stop the workflow + oscMsg_Camera_stop = createOSCMessage("/camera", stopCommand); + write(value(udpSender), oscMsg_Camera_stop, "uint8", bonsaiComputerIP,bonsaiUdpPort); + + % this is to kill the Bonsai app + system('taskkill /F /IM Bonsai.exe'); + + +end + +end + +%%%%%%%%%%%%%%% ADD 0N Functions %%%%%%%%%%%%%%%%%%%% + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +function oscPacket = createOSCMessage(address, arg) + % Ensure address and arg are char (not string) + if isstring(address) + address = char(address); + end + if isstring(arg) + arg = char(arg); + end + + % Helper to pad with nulls to 4-byte alignment + function bytes = padNulls(str) + strBytes = uint8([str, 0]); % null-terminated + padding = mod(4 - mod(length(strBytes), 4), 4); + bytes = [strBytes, zeros(1, padding, 'uint8')]; + end + + % Address (e.g., "/camera", "/record") + addrBytes = padNulls(address); + + % Type Tag String (e.g., ",s" for a single string argument) + typeTag = ',s'; + tagBytes = padNulls(typeTag); + + % Argument (e.g., "start") + argBytes = padNulls(arg); + + % Combine all parts + oscPacket = [addrBytes, tagBytes, argBytes]; +end + + +function runBonsaiWorkflow(workflowPath, addOptArray, bonsaiExePath, external) + +%% addOptArray = {'field', 'val', 'field2', 'val2'}; +%% check the input +switch nargin + case 1 + bonsaiExePath = bonsaiPath(64); + addOptArray = ''; + addFlag = 0; + external = 1; + case 2 + bonsaiExePath = bonsaiPath(64); + addFlag = 1; + external = 1; + case 3 + addFlag = 1; + external = 1; + case 4 + if isempty(bonsaiExePath) + bonsaiExePath = bonsaiPath(64); + end + addFlag = 1; + + otherwise + error('Too many variables'); +end + + + +%% write the command +optSuffix = '-p:'; +startFlag = '--start'; +noEditorFlag = '--noeditor'; %use this instead of startFlag to start Bonsai without showing the editor +cmdsep = ' '; +if external +command = [['"' bonsaiExePath '"'] cmdsep ['"' workflowPath '"'] cmdsep startFlag]; +else +command = [['"' bonsaiExePath '"'] cmdsep ['"' workflowPath '"'] cmdsep noEditorFlag]; +end +ii = 1; +commandAppend = ''; +if addFlag + while ii < size(addOptArray,2) + commandAppend = [commandAppend cmdsep optSuffix addOptArray{ii} '="' num2str(addOptArray{ii+1}) '"' ]; + ii = ii+2; + end +end + +if external + command = [command commandAppend ' &']; +else + command = [command commandAppend]; +end +%% run the command +[sysecho] = system(command); +end + + +%% parse the addOptArray +function out = parseAddOptArray(addOptArray) +addOpt = cell(length(addOptArray)/2,2); +for ii = 1:length(addOpt) + addOpt{ii,1} = addOptArray{2*ii-1}; %gets the propriety + addOpt{ii,2} = addOptArray{2*ii}; %gets the value +end +out = addOpt; +end + + +function pathout = bonsaiPath(x64x86) + +%%version of bonsai +if nargin <1; x64x86 = 64; end +switch x64x86 + case {'64', 64, 'x64', 1, '64bit'} + bonsaiEXE = 'Bonsai64.exe'; + case {32, '32', 'x86', 0, '32bit'} + bonsaiEXE = 'Bonsai.exe'; +end + +dirs = {'appdata', 'localappdata', 'programfiles', 'programfiles(x86)',... + 'C:\Software\Bonsai.Packages\Externals\Bonsai\Bonsai.Editor\bin\x86\Release',... + 'C:\Software\Bonsai.Packages\Externals\Bonsai\Bonsai.Editor\bin\x64\Release'... + }; +foundBonsai = 0; +dirIDX = 1; +while ~foundBonsai && (dirIDX <= length(dirs)) + pathout = fullfile(getenv(dirs{dirIDX}),'Bonsai', bonsaiEXE); + foundBonsai = exist(pathout, 'file'); + dirIDX = dirIDX +1; +end + +if ~foundBonsai + warning('could not find bonsai executable, please insert it manually'); + [fname fpath] = uigetfile( '*.exe', 'Provide the path to Bonsai executable'); + pathout = fullfile(fpath, fname); +end + +end diff --git a/Protocols/@Arpit_CentrePokeTraining/ParamsSection.m b/Protocols/@Arpit_CentrePokeTraining/ParamsSection.m index 4599b80a..702af0ab 100644 --- a/Protocols/@Arpit_CentrePokeTraining/ParamsSection.m +++ b/Protocols/@Arpit_CentrePokeTraining/ParamsSection.m @@ -45,7 +45,7 @@ next_row(y); % Centre Poke next_row(y); - NumeditParam(obj, 'cp_timeout', 5, x,y, 'TooltipString','Time from trial start for rat to centre poke else timeout'); + NumeditParam(obj, 'cp_timeout', 120, x,y, 'TooltipString','Time from trial start for rat to centre poke else timeout'); next_row(y); NumeditParam(obj, 'legal_cbreak', 0.1, x,y, 'TooltipString','Time in sec for which it is ok to be outside the center port before a violation occurs.'); next_row(y); diff --git a/Protocols/@Arpit_CentrePokeTraining/ProduceNoiseStimuli.m b/Protocols/@Arpit_CentrePokeTraining/ProduceNoiseStimuli.m deleted file mode 100644 index 262d6a6d..00000000 --- a/Protocols/@Arpit_CentrePokeTraining/ProduceNoiseStimuli.m +++ /dev/null @@ -1,33 +0,0 @@ -clear all - -T=1000; -fcut=110; -Fs=10000; -filter_type='GAUS'; -outband=55; -Ind=[0.4]; - -minS1_d=1; -maxS1_d=30; -numClass=8; - -S1_d(1)=minS1_d; -S2_d(1)=S1_d(1)*(1-Ind)/(1+Ind); -S1_u(1)=S1_d; -S2_u(1)=S1_u(1)*(1+Ind)/(1-(Ind)); -for ii=2:numClass -S1_d(ii)=S2_u(ii-1); -S2_d(ii)=S1_d(ii)*(1-Ind)/(1+Ind); -S1_u(ii)=S1_d(ii); -S2_u(ii)=S1_u(ii)*(1+Ind)/(1-Ind); -end - -[rawA rawB filtA filtB]=noise(Sigma1(ss),Sigma2(ss),T,fcut,Fs,filter_type,outband); - - -pairs=[]; -pairs(:,1)=[S1_d S1_u]; -pairs(:,2)=[S2_d S2_u]; -thesepairs=pairs(2:end-1,:) -LOGplotPairs(thesepairs(:,1),thesepairs(:,2),'s',18,'k',1,16) - diff --git a/Protocols/@Arpit_CentrePokeTraining/StimulusSection.m b/Protocols/@Arpit_CentrePokeTraining/StimulusSection.m index eb49126f..0beb8a25 100644 --- a/Protocols/@Arpit_CentrePokeTraining/StimulusSection.m +++ b/Protocols/@Arpit_CentrePokeTraining/StimulusSection.m @@ -48,12 +48,11 @@ '\n''S1>S_boundary Right'' means if Aud1 < Aud_boundry then reward will be delivered from the left water spout and if Aud1 > Aud_boundry then water comes from right\n'])); next_row(y, 1);next_row(y, 1); - MenuParam(obj, 'Prob_Dist_Left', {'Uniform','Exponential','Half Normal','Normal','Sinusoidal','Anti Exponential','Anti Half Normal','Anti Sinusoidal','Monotonic Increase'}, ... + MenuParam(obj, 'Prob_Dist_Left', {'Uniform','Exponential','Half Normal','Normal','Sinusoidal','Anti Exponential','Anti Half Normal','Anti Sinusoidal'}, ... 'Uniform', x, y,'label','Left Dist', 'labelfraction', 0.35, 'TooltipString', sprintf(['\n Different Probability Distributions for Category A.\n', ... '\n''Normal - the mean is at mid point of range. Half Normal - truncated normal with mean at boundary.\n',... '\n''Anti Half Normal - the mean/max is at the side edge of the range.\n',... - '\n''Sinosidal - using sine function instead of half normal and Anti Sinusoidal is when max is at the edge, same as anti half normal.\n',... - '\n''Monotonic - increases and decreases in steps from min to max (removing the random'])); + '\n''Sinosidal - using sine function instead of half normal and Anti Sinusoidal is when max is at the edge, same as anti half normal.\n'])); set_callback(Prob_Dist_Left, {mfilename, 'Cal_Mean'}); next_row(y); DispParam(obj, 'mean_Left', 0.01, x,y,'label','μ Left','TooltipString','mean/max log stim value for the left side distribution'); @@ -66,7 +65,7 @@ set_callback(sigma_range_Left, {mfilename, 'Cal_Sigma'}); next_row(y); next_row(y); - MenuParam(obj, 'Prob_Dist_Right', {'Uniform','Exponential','Half Normal','Normal','Sinusoidal','Anti Exponential','Anti Half Normal','Anti Sinusoidal','Monotonic Increase'}, ... + MenuParam(obj, 'Prob_Dist_Right', {'Uniform','Exponential','Half Normal','Normal','Sinusoidal','Anti Exponential','Anti Half Normal','Anti Sinusoidal'}, ... 'Uniform', x, y, 'label','Right Dist', 'labelfraction', 0.35, 'TooltipString', sprintf(['\n Different Probability Distributions for Category A.\n', ... '\n''Normal - the mean is at mid point of range (side edge - boundary). Half Normal - truncated normal with mean at boundary.\n',... '\n''Anti Half Normal - the mean/max is at the side edge of the range.\n',... @@ -131,7 +130,7 @@ % next_column(y) SoloParamHandle(obj, 'stim_dist_fig', 'value', figure('closerequestfcn', [mfilename '(' class(obj) ', ''hide'');'], 'MenuBar', 'none', ... 'Name', 'StimulusPlot'), 'saveable', 0); - ax = axes(value(stim_dist_fig),'Position',[0.1 0.1 0.9 0.9]); + ax = axes(value(stim_dist_fig),'Position',[0.1 0.1 0.8 0.8]); ylabel('log_e A','FontSize',16,'FontName','Cambria Math'); set(ax,'Fontsize',15) xlabel('Sound Categorization','FontSize',16,'FontName','Cambria Math') @@ -140,7 +139,7 @@ StimulusSection(obj,'plot_stimuli'); case 'prepare_next_trial' - if value(training_stage) > 5 && stimuli_on + if value(training_stage) > 4 && stimuli_on StimulusSection(obj,'pick_current_stimulus'); srate=SoundManagerSection(obj,'get_sample_rate'); Fs=srate; @@ -203,67 +202,105 @@ stim_max_log = log(value(maxS1)); end - if strcmpi(ThisTrial, 'LEFT') - dist_type = value(Prob_Dist_Left); - dist_mean = value(mean_Left); - dist_sigma = value(sigma_Left); - dist_range_multiplier = value(sigma_range_Left); - if strcmp(Rule,'S1>S_boundary Left') - edge_max = stim_max_log; - edge_min = value(boundary); - edge_max = edge_min + dist_range_multiplier * (edge_max - edge_min); - else % the rule is S1>S_boundary Right - edge_min = stim_min_log; - edge_max = value(boundary); - edge_min = edge_max - dist_range_multiplier * (edge_max - edge_min); + if value(training_stage) == 4 % playing fixed sound during this stage + if (strcmpi(ThisTrial, 'LEFT') & strcmp(Rule,'S1>S_boundary Left')) | ... + (strcmpi(ThisTrial, 'RIGHT') & strcmp(Rule,'S1>S_boundary Right')) + + stim_i_log = stim_min_log; + else + stim_i_log = stim_max_log; end + + else % will be playing stimuli from the distribution + + if strcmpi(ThisTrial, 'LEFT') + dist_type = value(Prob_Dist_Left); + dist_mean = value(mean_Left); + dist_sigma = value(sigma_Left); + dist_range_multiplier = value(sigma_range_Left); + if strcmp(Rule,'S1>S_boundary Left') + edge_max = stim_max_log; + edge_min = value(boundary); + edge_max = edge_min + dist_range_multiplier * (edge_max - edge_min); + else % the rule is S1>S_boundary Right + edge_min = stim_min_log; + edge_max = value(boundary); + edge_min = edge_max - dist_range_multiplier * (edge_max - edge_min); + end - else % trial is Right - dist_type = value(Prob_Dist_Right); - dist_mean = value(mean_Right); - dist_sigma = value(sigma_Right); - dist_range_multiplier = value(sigma_range_Right); - if strcmp(Rule,'S1>S_boundary Right') - edge_max = stim_max_log; - edge_min = value(boundary); - edge_max = edge_min + dist_range_multiplier * (edge_max - edge_min); - else % the rule is S1>S_boundary Left - edge_min = stim_min_log; - edge_max = value(boundary); - edge_min = edge_max - dist_range_multiplier * (edge_max - edge_min); + else % trial is Right + dist_type = value(Prob_Dist_Right); + dist_mean = value(mean_Right); + dist_sigma = value(sigma_Right); + dist_range_multiplier = value(sigma_range_Right); + if strcmp(Rule,'S1>S_boundary Right') + edge_max = stim_max_log; + edge_min = value(boundary); + edge_max = edge_min + dist_range_multiplier * (edge_max - edge_min); + else % the rule is S1>S_boundary Left + edge_min = stim_min_log; + edge_max = value(boundary); + edge_min = edge_max - dist_range_multiplier * (edge_max - edge_min); + end end - end - % Create a Stimuli with the selected Distribution and Side - switch dist_type + % Create a Stimuli with the selected Distribution and Side + switch dist_type - case 'Uniform' % uniform distribution - stim_i_log = random('Uniform',edge_min,edge_max); + case 'Uniform' % uniform distribution + stim_i_log = random('Uniform',edge_min,edge_max); - case 'Half Normal' - if edge_min == value(boundary) - stim_i_log = random('Half Normal',dist_mean,dist_sigma); + case 'Exponential' + + lambda = 2.153 * (edge_max - edge_min); % In mice they are using 2.153 for normalized stim range [0 1] + stim_i_log = edge_min - 1; % preinitialize for while loop while stim_i_log < edge_min || stim_i_log > edge_max + U = rand(1); + if edge_min == value(boundary) % exponentially decreasing + stim_i_log = edge_min + (-(1/lambda)*log(U)); % the distribution would be between range [0 1], so added the edge_min + else + stim_i_log = edge_max - (-(1/lambda)*log(U)); + end + end + + case 'Anti Exponential' + + lambda = 2.153 * (edge_max - edge_min); % In mice they are using 2.153 for normalized stim range [0 1] + stim_i_log = edge_min - 1; % preinitialize for while loop + while stim_i_log < edge_min || stim_i_log > edge_max + U = rand(1); + if edge_min == value(boundary) % exponentially decreasing + stim_i_log = edge_max - (-(1/lambda)*log(U)); % the distribution would be between range [0 1], so added the edge_min + else + stim_i_log = edge_min - ((1/lambda)*log(U)); + end + end + + case 'Half Normal' + if edge_min == value(boundary) stim_i_log = random('Half Normal',dist_mean,dist_sigma); + while stim_i_log < edge_min || stim_i_log > edge_max + stim_i_log = random('Half Normal',dist_mean,dist_sigma); + end + else + stim_i_log = CreateSamples_from_Distribution('normal',dist_mean,dist_sigma,edge_min,edge_max,1); end - else - stim_i_log = CreateSamples_from_Distribution('normal',dist_mean,dist_sigma,edge_min,edge_max,1); - end - case 'Anti Half Normal' + case 'Anti Half Normal' - stim_i_log = CreateSamples_from_Distribution('normal',dist_mean,dist_sigma,edge_min,edge_max,1); + stim_i_log = CreateSamples_from_Distribution('normal',dist_mean,dist_sigma,edge_min,edge_max,1); - case 'Normal' - stim_i_log = random('Normal',dist_mean,dist_sigma); - while stim_i_log < edge_min || stim_i_log > edge_max + case 'Normal' stim_i_log = random('Normal',dist_mean,dist_sigma); - end + while stim_i_log < edge_min || stim_i_log > edge_max + stim_i_log = random('Normal',dist_mean,dist_sigma); + end - case 'Sinusoidal' | 'Anti Sinusoidal' + case 'Sinusoidal' | 'Anti Sinusoidal' - stim_i_log = CreateSamples_from_Distribution('Sinusoidal',dist_mean,dist_sigma,edge_min,edge_max,1); - + stim_i_log = CreateSamples_from_Distribution('Sinusoidal',dist_mean,dist_sigma,edge_min,edge_max,1); + + end end thisstim.value=exp(stim_i_log); @@ -280,12 +317,6 @@ stim_max_log = log(value(maxS1)); end - % dist_type_left = value(Prob_Dist_Left); - % dist_mean_left = value(mean_Left); - % dist_sigma_left = value(sigma_Left); - % dist_type_right = value(Prob_Dist_Right); - % dist_mean_right = value(mean_Right); - % dist_sigma_right = value(sigma_Right); dist_range_multiplier_left = value(sigma_range_Left); dist_range_multiplier_right = value(sigma_range_Right); @@ -309,13 +340,17 @@ cla(value(ax)) - x_range = [stim_min_log, value(boundary), stim_max_log]; - left_range = [edge_min_left edge_max_left]; - right_range = [edge_min_right edge_max_right]; - stim_plot_axes = value(ax); - StimuliDistribution_plot(obj,stim_plot_axes,x_range,value(Prob_Dist_Left),value(mean_Left),value(sigma_Left),left_range,... - value(Prob_Dist_Right),value(mean_Right),value(sigma_Right),right_range); + StimuliDistribution_plot(value(ax),[stim_min_log, value(boundary), stim_max_log], ... + value(Prob_Dist_Left),value(mean_Left),value(sigma_Left),[edge_min_left edge_max_left], ... + value(Prob_Dist_Right),value(mean_Right),value(sigma_Right),[edge_min_right edge_max_right]); + + hold (value(ax),'on') + xline([stim_min_log value(boundary) stim_max_log],'-',{'Stim Min','Boundary','Stim Max'}); + + ylabel('log_e A','FontSize',16,'FontName','Cambria Math'); + set(ax,'Fontsize',15) + xlabel('Sound Categorization','FontSize',16,'FontName','Cambria Math') % plot(xd,stim_min_log,'s','MarkerSize',15,'MarkerEdgeColor',[0 0 0],'LineWidth',2) % hold on @@ -328,15 +363,6 @@ % set(value(ax),'Fontsize',15) % xlabel('S1','FontSize',16,'FontName','Cambria Math') - % StimulusSection(obj,'pick_current_stimulus'); - % A1 = value(thisstim); - % - % %% plot the stimulus; - % if value(thisstim) > value(boundary)%value(numClass) - % h1.value=plot(xd,value(A1),'s','color',[0.4 0.8 0.1],'markerfacecolor',[0.4 0.8 0.1],'MarkerSize',15,'LineWidth',3); - % else - % h1.value=plot(xd,value(A1),'s','color',[0.8 0.4 0.1],'markerfacecolor',[0.8 0.4 0.1],'MarkerSize',15,'LineWidth',3); - % end %% Boundary Calculate case 'Cal_Boundary' diff --git a/Protocols/@Arpit_CentrePokeTraining/noiselib.m b/Protocols/@Arpit_CentrePokeTraining/noiselib.m deleted file mode 100644 index 57dd5c7d..00000000 --- a/Protocols/@Arpit_CentrePokeTraining/noiselib.m +++ /dev/null @@ -1,51 +0,0 @@ -function [base,filtbase]=noiselib(num,T,fcut,Fs,filter_type,outband) -close all -clc - -%%%%%%%%%%%%%%%%% Determines of the type of filter used %%%%%%%%%%%%%%%%%%% -%'LPFIR': lowpass FIR%%%%%'FIRLS': Least square linear-phase FIR filter design -%'BUTTER': IIR Butterworth lowpass filter%%%%%%'GAUS': Gaussian filter (window) -%'MOVAVRG': Moving average FIR filter%%%%%%%%'KAISER': Kaiser-window FIR filtering -% 'EQUIRIP':Eqiripple FIR filter%%%%% 'HAMMING': Hamming-window based FIR -% T is duration of each signal in milisecond, fcut is the cut-off frequency -% Fs is the sampling frequency -% outband=40; - -for ii=1:num -s = RandStream('mcg16807','Seed',ii) -RandStream.setDefaultStream(s) - -replace=1; -L=T*Fs/1000; % Length of signal -t=L*1000*linspace(0,1,L)/Fs; % time in miliseconds -%%%%%%%%%%% produce position values %%%%%%% -pos1 = randn(Fs,1); -pos1(pos1>outband)=[]; -pos1(pos1<-outband)=[]; - -base(:,num)=pos1; -%base = randsample(pos1,L,replace); -%%%% Filter the original position values %%%%%% -filtbase(:,num)=filt(base,fcut,Fs,filter_type); - -end - -end - -%%%%%% plot the row and filtered position values %%%%%%%%% -% subplot(2,2,1) -% plot(t,base,'r'); -% ylabel('base') -% xlabel('Time (ms)') -% subplot(2,2,2) -% plot(t,target,'g'); -% ylabel('target') -% xlabel('Time (ms)') -% subplot(2,2,3) -% plot(t,filtbase) -% ylabel('filtbase') -% xlabel('Time (ms)') -% subplot(2,2,4) -% plot(t,filttarget) -% ylabel('filttarget') -% xlabel('Time (ms)') diff --git a/Protocols/@Arpit_CentrePokeTraining/CreateSamples_from_Distribution.m b/Protocols/@Arpit_CentrePokeTraining/private/CreateSamples_from_Distribution.m similarity index 100% rename from Protocols/@Arpit_CentrePokeTraining/CreateSamples_from_Distribution.m rename to Protocols/@Arpit_CentrePokeTraining/private/CreateSamples_from_Distribution.m diff --git a/Protocols/@Arpit_CentrePokeTraining/StimuliDistribution_plot.m b/Protocols/@Arpit_CentrePokeTraining/private/StimuliDistribution_plot.m similarity index 97% rename from Protocols/@Arpit_CentrePokeTraining/StimuliDistribution_plot.m rename to Protocols/@Arpit_CentrePokeTraining/private/StimuliDistribution_plot.m index 4cc1fec5..9214f16e 100644 --- a/Protocols/@Arpit_CentrePokeTraining/StimuliDistribution_plot.m +++ b/Protocols/@Arpit_CentrePokeTraining/private/StimuliDistribution_plot.m @@ -1,4 +1,4 @@ -function StimuliDistribution_plot(obj,ax,plot_x_range, distribution_left,mean_left,sigma_left,... +function StimuliDistribution_plot(ax,plot_x_range, distribution_left,mean_left,sigma_left,... range_left,distribution_right,mean_right,sigma_right,range_right) x_left = plot_x_range(1):.01:plot_x_range(2); diff --git a/Protocols/@Arpit_SoundCalibration/Arpit_SoundCalibration.m b/Protocols/@Arpit_SoundCalibration/Arpit_SoundCalibration.m new file mode 100644 index 00000000..9521c087 --- /dev/null +++ b/Protocols/@Arpit_SoundCalibration/Arpit_SoundCalibration.m @@ -0,0 +1,707 @@ + +%This is a new version of the old stand alone rigtester.m did not run +%through dispatcher. This is treated like any other protocol. It is +%designed to be plastic, looking at a rigs settings files, determining +%the input and output lines, and building a GUI specific to that rigs +%componants. +% +%Written by Chuck 2017 + +% Make sure you ran newstartup, then dispatcher('init'), and you're good to +% go! +% + +function [obj] = Arpit_SoundCalibration(varargin) + +% Default object is of our own class (mfilename); in this simplest of +% protocols, we inherit only from Plugins/@pokesplot + +obj = class(struct, mfilename, soundmanager, soundui); + +%--------------------------------------------------------------- +% BEGIN SECTION COMMON TO ALL PROTOCOLS, DO NOT MODIFY +%--------------------------------------------------------------- + +% If creating an empty object, return without further ado: +if nargin==0 || (nargin==1 && ischar(varargin{1}) && strcmp(varargin{1}, 'empty')), + return; +end; + +if isa(varargin{1}, mfilename), % If first arg is an object of this class itself, we are + % Most likely responding to a callback from + % a SoloParamHandle defined in this mfile. + if length(varargin) < 2 || ~ischar(varargin{2}), + error(['If called with a "%s" object as first arg, a second arg, a ' ... + 'string specifying the action, is required\n']); + else action = varargin{2}; varargin = varargin(3:end); %#ok + end; +else % Ok, regular call with first param being the action string. + action = varargin{1}; varargin = varargin(2:end); %#ok +end; +if ~ischar(action), error('The action parameter must be a string'); end; + +GetSoloFunctionArgs(obj); + +%--------------------------------------------------------------- +% END OF SECTION COMMON TO ALL PROTOCOLS, MODIFY AFTER THIS LINE +%--------------------------------------------------------------- + + +% ---- From here on is where you can put the code you like. +% +% Your protocol will be called, at the appropriate times, with the +% following possible actions: +% +% 'init' To initialize -- make figure windows, variables, etc. +% +% 'update' Called periodically within a trial +% +% 'prepare_next_trial' Called when a trial has ended and your protocol +% is expected to produce the StateMachine diagram for the next +% trial; i.e., somewhere in your protocol's response to this +% call, it should call "dispatcher('send_assembler', sma, +% prepare_next_trial_set);" where sma is the +% StateMachineAssembler object that you have prepared and +% prepare_next_trial_set is either a single string or a cell +% with elements that are all strings. These strings should +% correspond to names of states in sma. +% Note that after the 'prepare_next_trial' call, further +% events may still occur in the RTLSM while your protocol is thinking, +% before the new StateMachine diagram gets sent. These events +% will be available to you when 'trial_completed' is called on your +% protocol (see below). +% +% 'trial_completed' Called when 'state_0' is reached in the RTLSM, +% marking final completion of a trial (and the start of +% the next). +% +% 'close' Called when the protocol is to be closed. +% +% +% VARIABLES THAT DISPATCHER WILL ALWAYS INSTANTIATE FOR YOU IN YOUR +% PROTOCOL: +% +% (These variables will be instantiated as regular Matlab variables, +% not SoloParamHandles. For any method in your protocol (i.e., an m-file +% within the @your_protocol directory) that takes "obj" as its first argument, +% calling "GetSoloFunctionArgs(obj)" will instantiate all the variables below.) +% +% +% n_done_trials How many trials have been finished; when a trial reaches +% one of the prepare_next_trial states for the first +% time, this variable is incremented by 1. +% +% n_started trials How many trials have been started. This variable gets +% incremented by 1 every time the state machine goes +% through state 0. +% +% parsed_events The result of running disassemble.m, with the +% parsed_structure flag set to 1, on all events from the +% start of the current trial to now. +% +% latest_events The result of running disassemble.m, with the +% parsed_structure flag set to 1, on all new events from +% the last time 'update' was called to now. +% +% raw_events All the events obtained in the current trial, not parsed +% or disassembled, but raw as gotten from the State +% Machine object. +% +% current_assembler The StateMachineAssembler object that was used to +% generate the State Machine diagram in effect in the +% current trial. +% +% Trial-by-trial history of parsed_events, raw_events, and +% current_assembler, are automatically stored for you in your protocol by +% dispatcher.m. See the wiki documentation for information on how to access +% those histories from within your protocol and for information. +% +% + + +switch action, + + %--------------------------------------------------------------- + % CASE INIT + %--------------------------------------------------------------- + + case 'init' + + % Make default figure. We remember to make it non-saveable; on next run + % the handle to this figure might be different, and we don't want to + % overwrite it when someone does load_data and some old value of the + % fig handle was stored as SoloParamHandle "myfig" + SoloParamHandle(obj, 'myfig', 'saveable', 0); myfig.value = double(figure); + + % Make the title of the figure be the protocol name, and if someone tries + % to close this figure, call dispatcher's close_protocol function, so it'll know + % to take it off the list of open protocols. + name = mfilename; + set(value(myfig), 'Name', name, 'Tag', name, ... + 'closerequestfcn', [mfilename,'(''close'');'], 'MenuBar', 'none'); + + alldio = bSettings('get','DIOLINES','ALL'); + allinp = bSettings('get','INPUTLINES','ALL'); + dionums = cell2mat(alldio(:,2)); + inpnums = cell2mat(allinp(:,2)); + + for i = 1:16 + inp1 = find(inpnums == i,1,'first'); + dio1 = find(dionums == 2^(( i-1)*2), 1,'first'); + dio2 = find(dionums == 2^(((i-1)*2)+1),1,'first'); + + if isempty(inp1) && isempty(dio1) && isempty(dio2) + continue; + end + + if ~isempty(inp1) + linegroups{i}{1,1} = allinp{inp1,2}; %#ok + linegroups{i}{1,2} = allinp{inp1,1}; %#ok + else + linegroups{i}{1,1} = []; %#ok + linegroups{i}{1,2} = []; %#ok + end + if ~isempty(dio1) + linegroups{i}{2,1} = alldio{dio1,2}; %#ok + linegroups{i}{2,2} = alldio{dio1,1}; %#ok + else + linegroups{i}{2,1} = []; %#ok + linegroups{i}{2,2} = []; %#ok + end + if ~isempty(dio2) + linegroups{i}{3,1} = alldio{dio2,2}; %#ok + linegroups{i}{3,2} = alldio{dio2,1}; %#ok + else + linegroups{i}{3,1} = []; %#ok + linegroups{i}{3,2} = []; %#ok + end + end + + % Generate the sounds we need. + soundserver = bSettings('get','RIGS','sound_machine_server'); + if ~isempty(soundserver) + sr = SoundManagerSection(obj,'get_sample_rate'); + Fs=sr; + lfreq=2000; + hfreq=20000; + freq = 100; + T = 5; + fcut = 110; + filter_type = 'GAUS'; + A1_sigma = 0.0500; + A2_sigma = 0.0306 %0.1230;%0.0260; + A3_sigma = 0.0187 %0.0473;%0.0135; + A4_sigma = 0.0114 %0.0182;%0.0070; + A5_sigma = 0.0070; + [rawA1 rawA2 normA1 normA2]=noisestim(1,1,T,fcut,Fs,filter_type); + modulator=singlenoise(1,T,[lfreq hfreq],Fs,'BUTTER'); + AUD1=normA1(1:T*sr).*modulator(1:T*sr).*A1_sigma; + AUD2=normA1(1:T*sr).*modulator(1:T*sr).*A2_sigma; + AUD3=normA1(1:T*sr).*modulator(1:T*sr).*A3_sigma; + AUD4=normA1(1:T*sr).*modulator(1:T*sr).*A4_sigma; + AUD5=normA1(1:T*sr).*modulator(1:T*sr).*A5_sigma; + % snd = MakeBupperSwoop(sr,0,freq,freq, len/2, len/2, 0, 0.1, 'F1_volume_factor',0.07,'F2_volume_factor',0.07); + % silence_length = 0; + % presound_silence = zeros(1,sr*silence_length/1000); + % snd = [presound_silence, snd]; + % + % SoundManagerSection(obj,'declare_new_sound','left_sound', [snd; zeros(1,size(snd,2))]); + % SoundManagerSection(obj,'declare_new_sound','right_sound', [zeros(1,size(snd,2)); snd]); + % + % SoundManagerSection(obj,'loop_sound','left_sound',1); + % SoundManagerSection(obj,'loop_sound','right_sound',1); + % + % SoundManager Section(obj,'declare_new_sound','center_sound',[snd;snd([ceil((sr / freq) / 2):end,1:ceil((sr / freq) / 2)-1])]); + % SoundManagerSection(obj,'loop_sound','center_sound',1); + % + if ~isempty(AUD2) + SoundManagerSection(obj, 'declare_new_sound', 'left_sound', [AUD2'; AUD2']) + end + if ~isempty(AUD1) + SoundManagerSection(obj, 'declare_new_sound', 'center_sound', [AUD1'; AUD1']) + end + if ~isempty(AUD3) + SoundManagerSection(obj, 'declare_new_sound', 'right_sound', [AUD3'; AUD3']) + end + if ~isempty(AUD4) + SoundManagerSection(obj, 'declare_new_sound', 'fourth_sound', [AUD4'; AUD4']) + end + if ~isempty(AUD5) + SoundManagerSection(obj, 'declare_new_sound', 'fifth_sound', [AUD5'; AUD5']) + end + SoundManagerSection(obj, 'send_not_yet_uploaded_sounds'); + end + + % Get monitor dimensions for dynamic GUI sizing + MP = get(0,'MonitorPositions'); + + % Calculate individual group width based on screen dimensions and number of groups + groupwidth = floor((MP(3)/2)/numel(linegroups)); + + padding = 10 % padding around GUI elements + + % Calculate total width needed for all groups combined + total_width = floor(numel(linegroups) * groupwidth+padding); + total_height = 400 % total height of GUI + + label_height = 140 % height of port label + button_height = 25 + + % Center the GUI horizontally on screen + left_pos = floor((MP(3) - total_width) / 2); + + % Set figure position with centered alignment and fixed height of 400 pixels + % position vector: [left-right, down-up, width, height], the origo is + % the bottom left corner + set(value(myfig), 'Position', [left_pos, floor((MP(4)-total_height)/2), total_width, total_height]); + + % Initialize array to store line names + line_names = []; + + % Iterate through each line group + for i = 1:numel(linegroups) + % Check if current group exists and has valid parameters + if ~isempty(linegroups{i}) && ~isempty(linegroups{i}{1,2}) + % port label position + SubheaderParam( ... + obj, ... + ['Input',linegroups{i}{1,2}], ... + {linegroups{i}{1,2};0},0,0, ... + 'position',[((i-1)*groupwidth)+padding, ... + total_height-padding-label_height, ... + groupwidth-padding, ... + label_height ... + ]); + + % port label aesthetics + set(get_ghandle(eval(['Input',linegroups{i}{1,2}])), ... + 'FontSize',32, ... % Large font size for visibility + 'BackgroundColor',[0,1,0], ... % Green background + 'HorizontalAlignment','center'); % Center align text + + % Store line name for later reference + line_names(end+1) = linegroups{i}{1,2}; %#ok + + % Add sound controls based on line identifier + if strcmp(linegroups{i}{1,2},'L') && ~isempty(soundserver) + % Left channel sound toggle + ToggleParam(obj, ... + 'LeftSound', 0,0,0, ... + 'position',[((i-1)*groupwidth)+padding, padding, groupwidth-padding,button_height], ... + 'OnString', 'Sound Two ON', ... + 'OffString', 'Sound Two OFF'); + set_callback(LeftSound,{mfilename,'play_left_sound'}); + + elseif strcmp(linegroups{i}{1,2},'R') && ~isempty(soundserver) + % Right channel sound toggle + ToggleParam(obj, ... + 'RightSound', 0,0,0, ... + 'position',[((i-1)*groupwidth)+padding, padding, groupwidth-padding,button_height], ... + 'OnString', 'Sound Three ON', ... + 'OffString', 'Sound Three OFF'); + set_callback(RightSound,{mfilename,'play_right_sound'}); + + elseif strcmp(linegroups{i}{1,2},'C') && ~isempty(soundserver) + % Center channel sound toggle + ToggleParam(obj, ... + 'CenterSound', 0,0,0, ... + 'position',[((i-1)*groupwidth)+padding, padding, groupwidth-padding,button_height], ... + 'OnString', 'Sound One ON', ... + 'OffString', 'Sound One OFF'); + set_callback(CenterSound,{mfilename,'play_center_sound'}); + + elseif strcmp(linegroups{i}{1,2},'A') && ~isempty(soundserver) + % Additional channel sound toggle + ToggleParam(obj, ... + 'FourthSound', 0,0,0, ... + 'position',[((i-1)*groupwidth)+padding, padding, groupwidth-padding,button_height], ... + 'OnString', 'Sound Four ON', ... + 'OffString', 'Sound Four OFF'); + set_callback(FourthSound,{mfilename,'play_fourth_sound'}); + end + + % Handle case for empty line identifier but existing group + elseif ~isempty(linegroups{i}) && isempty(linegroups{i}{1,2}) + ToggleParam(obj, ... + 'FifthSound', 0,0,0, ... + 'position',[((i-1)*groupwidth)+padding, padding, groupwidth-padding,button_height], ... + 'OnString', 'Sound Five ON', ... + 'OffString', 'Sound Five OFF'); + set_callback(FifthSound,{mfilename,'play_fifth_sound'}); + end + + % Add first digital I/O control if available + if ~isempty(linegroups{i}) && ~isempty(linegroups{i}{2,2}) + ToggleParam(obj, ... + ['DIO',num2str(i),'_1'],0,0,0, ... + 'position',[((i-1)*groupwidth)+padding, 2*button_height+padding, groupwidth-padding,button_height], ... + 'OnString', [linegroups{i}{2,2},' ON'], ... + 'OffString', [linegroups{i}{2,2},' OFF']); + set_callback(eval(['DIO',num2str(i),'_1']),{mfilename,['toggle',num2str(i),'_1']}); + end + + % Add second digital I/O control if available + if ~isempty(linegroups{i}) && ~isempty(linegroups{i}{3,2}) + ToggleParam(obj, ... + ['DIO',num2str(i),'_2'],0,0,0, ... + 'position',[((i-1)*groupwidth)+padding, button_height+padding, groupwidth-padding,button_height], ... + 'OnString', [linegroups{i}{3,2},' ON'], ... + 'OffString', [linegroups{i}{3,2},' OFF']); + set_callback(eval(['DIO',num2str(i),'_2']),{mfilename,['toggle',num2str(i),'_2']}); + end + end + + + SoloParamHandle(obj,'LineGroups','value',linegroups); + SoloParamHandle(obj,'LineNames','value',line_names); + + scr = timer; + set(scr,'Period', 0.2,'ExecutionMode','FixedRate','TasksToExecute',Inf,... + 'BusyMode','drop','TimerFcn',[mfilename,'(''close_continued'')']); + SoloParamHandle(obj, 'stopping_complete_timer', 'value', scr); + + SoundCalibration(obj,'prepare_next_trial'); + + dispatcher('Run'); + + + case 'play_left_sound' + %% play_left_sound + if value(LeftSound) == 1 + SoundManagerSection(obj,'play_sound','left_sound'); + else + SoundManagerSection(obj,'stop_sound','left_sound'); + end + + case 'play_right_sound' + %% play_right_sound + if value(RightSound) == 1 + SoundManagerSection(obj,'play_sound','right_sound'); + else + SoundManagerSection(obj,'stop_sound','right_sound'); + end + + case 'play_center_sound' + %% play_center_sound + if value(CenterSound) == 1 + SoundManagerSection(obj,'play_sound','center_sound'); + else + SoundManagerSection(obj,'stop_sound','center_sound'); + end + + case 'play_fourth_sound' + %% play_fourth_sound + if value(FourthSound) == 1 + SoundManagerSection(obj,'play_sound','fourth_sound'); + else + SoundManagerSection(obj,'stop_sound','fourth_sound'); + end + case 'play_fifth_sound' + %% play_fifth_sound + if value(FifthSound) == 1 + SoundManagerSection(obj,'play_sound','fifth_sound'); + else + SoundManagerSection(obj,'stop_sound','fifth_sound'); + end + case 'toggle1_1' + %% case toggle1_1 + linegroups = value(LineGroups); + dispatcher('toggle_bypass',log2(linegroups{1}{2,1})); + + case 'toggle1_2' + %% case toggle1_2 + linegroups = value(LineGroups); + dispatcher('toggle_bypass',log2(linegroups{1}{3,1})); + + case 'toggle2_1' + %% case toggle1_1 + linegroups = value(LineGroups); + dispatcher('toggle_bypass',log2(linegroups{2}{2,1})); + + case 'toggle2_2' + %% case toggle1_2 + linegroups = value(LineGroups); + dispatcher('toggle_bypass',log2(linegroups{2}{3,1})); + + case 'toggle3_1' + %% case toggle1_1 + linegroups = value(LineGroups); + dispatcher('toggle_bypass',log2(linegroups{3}{2,1})); + + case 'toggle3_2' + %% case toggle1_2 + linegroups = value(LineGroups); + dispatcher('toggle_bypass',log2(linegroups{3}{3,1})); + + case 'toggle4_1' + %% case toggle1_1 + linegroups = value(LineGroups); + dispatcher('toggle_bypass',log2(linegroups{4}{2,1})); + + case 'toggle4_2' + %% case toggle1_2 + linegroups = value(LineGroups); + dispatcher('toggle_bypass',log2(linegroups{4}{3,1})); + + case 'toggle5_1' + %% case toggle1_1 + linegroups = value(LineGroups); + dispatcher('toggle_bypass',log2(linegroups{5}{2,1})); + + case 'toggle5_2' + %% case toggle1_2 + linegroups = value(LineGroups); + dispatcher('toggle_bypass',log2(linegroups{5}{3,1})); + + case 'toggle6_1' + %% case toggle1_1 + linegroups = value(LineGroups); + dispatcher('toggle_bypass',log2(linegroups{6}{2,1})); + + case 'toggle6_2' + %% case toggle1_2 + linegroups = value(LineGroups); + dispatcher('toggle_bypass',log2(linegroups{6}{3,1})); + + case 'toggle7_1' + %% case toggle1_1 + linegroups = value(LineGroups); + dispatcher('toggle_bypass',log2(linegroups{7}{2,1})); + + case 'toggle7_2' + %% case toggle1_2 + linegroups = value(LineGroups); + dispatcher('toggle_bypass',log2(linegroups{7}{3,1})); + + case 'toggle8_1' + %% case toggle1_1 + linegroups = value(LineGroups); + dispatcher('toggle_bypass',log2(linegroups{8}{2,1})); + + case 'toggle8_2' + %% case toggle1_2 + linegroups = value(LineGroups); + dispatcher('toggle_bypass',log2(linegroups{8}{3,1})); + + case 'toggle9_1' + %% case toggle1_1 + linegroups = value(LineGroups); + dispatcher('toggle_bypass',log2(linegroups{9}{2,1})); + + case 'toggle9_2' + %% case toggle1_2 + linegroups = value(LineGroups); + dispatcher('toggle_bypass',log2(linegroups{9}{3,1})); + + case 'toggle10_1' + %% case toggle1_1 + linegroups = value(LineGroups); + dispatcher('toggle_bypass',log2(linegroups{10}{2,1})); + + case 'toggle10_2' + %% case toggle1_2 + linegroups = value(LineGroups); + dispatcher('toggle_bypass',log2(linegroups{10}{3,1})); + + case 'toggle11_1' + %% case toggle1_1 + linegroups = value(LineGroups); + dispatcher('toggle_bypass',log2(linegroups{11}{2,1})); + + case 'toggle11_2' + %% case toggle1_2 + linegroups = value(LineGroups); + dispatcher('toggle_bypass',log2(linegroups{11}{3,1})); + + case 'toggle12_1' + %% case toggle1_1 + linegroups = value(LineGroups); + dispatcher('toggle_bypass',log2(linegroups{12}{2,1})); + + case 'toggle12_2' + %% case toggle1_2 + linegroups = value(LineGroups); + dispatcher('toggle_bypass',log2(linegroups{12}{3,1})); + + case 'toggle13_1' + %% case toggle1_1 + linegroups = value(LineGroups); + dispatcher('toggle_bypass',log2(linegroups{13}{2,1})); + + case 'toggle13_2' + %% case toggle1_2 + linegroups = value(LineGroups); + dispatcher('toggle_bypass',log2(linegroups{13}{3,1})); + + case 'toggle14_1' + %% case toggle1_1 + linegroups = value(LineGroups); + dispatcher('toggle_bypass',log2(linegroups{14}{2,1})); + + case 'toggle14_2' + %% case toggle1_2 + linegroups = value(LineGroups); + dispatcher('toggle_bypass',log2(linegroups{14}{3,1})); + + case 'toggle15_1' + %% case toggle1_1 + linegroups = value(LineGroups); + dispatcher('toggle_bypass',log2(linegroups{15}{2,1})); + + case 'toggle15_2' + %% case toggle1_2 + linegroups = value(LineGroups); + dispatcher('toggle_bypass',log2(linegroups{15}{3,1})); + + case 'toggle16_1' + %% case toggle1_1 + linegroups = value(LineGroups); + dispatcher('toggle_bypass',log2(linegroups{16}{2,1})); + + case 'toggle16_2' + %% case toggle1_2 + linegroups = value(LineGroups); + dispatcher('toggle_bypass',log2(linegroups{16}{3,1})); + + %--------------------------------------------------------------- + % CASE PREPARE_NEXT_TRIAL + %--------------------------------------------------------------- + case 'prepare_next_trial' + line_names = value(LineNames); + sma = StateMachineAssembler('full_trial_structure','use_happenings',1,'n_input_lines',numel(line_names),'line_names',line_names); + sma = add_state(sma, 'name', 'the_only_state', 'self_timer',1e4, 'input_to_statechange', {'Tup', 'final_state'}); + sma = add_state(sma, 'name', 'final_state', 'self_timer',1e4, 'input_to_statechange', {'Tup', 'check_next_trial_ready'}); + dispatcher('send_assembler', sma, 'final_state'); + + %--------------------------------------------------------------- + % CASE TRIAL_COMPLETED + %--------------------------------------------------------------- + case 'trial_completed' + + + %--------------------------------------------------------------- + % CASE UPDATE + %--------------------------------------------------------------- + case 'update' + pe = parsed_events; %#ok + linenames = value(LineNames); + + for i = 1:numel(linenames) + poketimes = eval(['pe.pokes.',linenames(i)]); + if ~isempty(poketimes) && isnan(poketimes(end,2)) + set(get_ghandle(eval(['Input',linenames(i)])),'BackgroundColor',[1,0,0]); + + str = get(get_ghandle(eval(['Input',linenames(i)])),'string'); + str{2} = size(poketimes,1); + set(get_ghandle(eval(['Input',linenames(i)])),'string',str); + + else + set(get_ghandle(eval(['Input',linenames(i)])),'BackgroundColor',[0,1,0]); + end + end + + %--------------------------------------------------------------- + % CASE CLOSE + %--------------------------------------------------------------- + case 'close' + + dispatcher('Stop'); + + %Let's pause until we know dispatcher is done running + set(value(stopping_complete_timer),'TimerFcn',[mfilename,'(''close_continued'');']); + start(value(stopping_complete_timer)); + + case 'close_continued' + + if value(stopping_process_completed) + stop(value(stopping_complete_timer)); %Stop looping. + %dispatcher('set_protocol',''); + + if exist('myfig', 'var') && isa(myfig, 'SoloParamHandle') && ishandle(value(myfig)), %#ok + delete(value(myfig)); + end; + delete_sphandle('owner', ['^@' class(obj) '$']); + dispatcher('set_protocol',''); + end + otherwise, + warning('Unknown action! "%s"\n', action); %#ok +end; + +return; + +end + +function calculate_soundlevel + +% --- Configuration --- +serialPort = '/dev/ttyACM0'; % Replace with the actual serial port of your Raspberry Pi +baudRate = 9600; % Or your desired baud rate +matlabOutputValues = 0:10:100; % Example range of MATLAB output values +calibrationData = []; + +% --- Establish Serial Connection --- +try + s = serial(serialPort, 'BaudRate', baudRate); + fopen(s); + disp(['Opened serial port: ', serialPort, ' at ', num2str(baudRate), ' bps']); + + % --- Calibration Loop --- + for i = 1:length(matlabOutputValues) + outputValue = matlabOutputValues(i); + disp(['Sending MATLAB output value: ', num2str(outputValue)]); + + % --- Send command to set speaker level (replace with your actual command) --- + setSpeakerLevel(outputValue); + + % --- Send command to Raspberry Pi to measure SPL --- + fprintf(s, 'measure_spl\n'); % Send command over serial + + % --- Receive SPL value from Raspberry Pi --- + receivedData = fgetl(s); + if ~isempty(receivedData) + splValue = str2double(receivedData); + if ~isnan(splValue) + disp(['Received SPL: ', num2str(splValue), ' dB']); + calibrationData = [calibrationData; outputValue, splValue]; + pause(2); % Allow time for sound to stabilize and measurement + else + warning('Received non-numeric SPL value.'); + end + else + warning('No data received from Raspberry Pi.'); + end + end + + % --- Close Serial Connection --- + fclose(s); + delete(s); + clear s; + + % --- Display Calibration Data --- + disp('Calibration Data (MATLAB Value, dB SPL):'); + disp(calibrationData); + + % --- Create Calibration Mapping (Example: Linear Fit) --- + p = polyfit(calibrationData(:,1), calibrationData(:,2), 1); + disp('Calibration Polynomial (p(1) * MATLAB_Value + p(2)):'); + disp(p); + % Now you can use this polynomial 'p' to estimate dB SPL from your MATLAB values + % estimatedSPL = polyval(p, yourMatlabValue); + +catch ME + disp(['Error occurred: ', ME.message]); + if exist('s', 'var') && isvalid(s) + fclose(s); + delete(s); + clear s; + end +end + +% --- Your function to control the speaker level in MATLAB --- +function setSpeakerLevel(value) + % Replace this with your actual commands or functions to control the speaker. + disp(['(Simulating setting speaker level to: ', num2str(value), ')']); + pause(1); % Simulate speaker level change +end + +end \ No newline at end of file diff --git a/Protocols/@Arpit_SoundCatContinuous/Arpit_SoundCatContinuous.m b/Protocols/@Arpit_SoundCatContinuous/Arpit_SoundCatContinuous.m new file mode 100644 index 00000000..1523d8d2 --- /dev/null +++ b/Protocols/@Arpit_SoundCatContinuous/Arpit_SoundCatContinuous.m @@ -0,0 +1,212 @@ +% AltSoundCatCatch protocol +% EM, October 2020 + +function [obj] = Arpit_SoundCatContinuous(varargin) + +% Default object is of our own class (mfilename); +% we inherit only from Plugins + +obj = class(struct, mfilename, pokesplot2, saveload, sessionmodel2, soundmanager, soundui, antibias, ... + water, soundtable, comments, sqlsummary); + +%--------------------------------------------------------------- +% BEGIN SECTION COMMON TO ALL PROTOCOLS, DO NOT MODIFY +%--------------------------------------------------------------- + +% If creating an empty object, return without further ado: +if nargin==0 || (nargin==1 && ischar(varargin{1}) && strcmp(varargin{1}, 'empty')) + return; +end + +if isa(varargin{1}, mfilename) % If first arg is an object of this class itself, we are + % Most likely responding to a callback from + % a SoloParamHandle defined in this mfile. + if length(varargin) < 2 || ~ischar(varargin{2}) + error(['If called with a "%s" object as first arg, a second arg, a ' ... + 'string specifying the action, is required\n']); + else action = varargin{2}; varargin = varargin(3:end); %#ok + end +else % Ok, regular call with first param being the action string. + action = varargin{1}; varargin = varargin(2:end); %#ok +end + +GetSoloFunctionArgs(obj); + +switch action + + %% init + case 'init' + dispatcher('set_trialnum_indicator_flag'); + hackvar = 10; SoloFunctionAddVars('SessionModel', 'ro_args', 'hackvar'); %#ok + SoloParamHandle(obj, 'myfig', 'saveable', 0); myfig.value = figure; + + % Make the title of the figure be the protocol name, and if someone tries + % to close this figure, call dispatcher's close_protocol function, so it'll know + % to take it off the list of open protocols. + name = mfilename; + set(value(myfig), 'Name', name, 'Tag', name, ... + 'closerequestfcn', 'dispatcher(''close_protocol'')', 'MenuBar', 'none'); + % At this point we have one SoloParamHandle, myfig + % Let's put the figure where we want it and give it a reasonable size: + set(value(myfig), 'Position', [485 144 850 680]); + + SoloParamHandle(obj, 'nsessions_healthy_number_of_pokes', 'value', 0, 'save_with_settings', 1); + SoloParamHandle(obj, 'post_DelComp_protocol', 'value', '', 'save_with_settings', 1); + SoloParamHandle(obj, 'post_DelComp_settings_filename', 'value', '', 'save_with_settings', 1); + + + SoloParamHandle(obj, 'hit_history', 'value', []); + DeclareGlobals(obj, 'ro_args', {'hit_history'}); + SoloFunctionAddVars('SideSection', 'rw_args', 'hit_history'); + + %pair_history changed to stimulus_history (from AthenaDelayComp) + SoloParamHandle(obj, 'stimulus_history', 'value', []); + DeclareGlobals(obj, 'ro_args', {'stimulus_history'}); + SoloFunctionAddVars('StimulusSection', 'rw_args', 'stimulus_history'); + + SoloParamHandle(obj, 'violation_history', 'value', []); + DeclareGlobals(obj, 'ro_args', {'violation_history'}); + SoloFunctionAddVars('SideSection', 'rw_args', 'violation_history'); + + SoloParamHandle(obj, 'timeout_history', 'value', []); + DeclareGlobals(obj, 'ro_args', {'timeout_history'}); + SoloFunctionAddVars('SideSection', 'rw_args', 'timeout_history'); + + + SoundManagerSection(obj, 'init'); + + x = 5; y = 5; % Initial position on main GUI window + + [x, y] = SavingSection(obj, 'init', x, y); + [x, y] = WaterValvesSection(obj, 'init', x, y); + + % For plotting with the pokesplot plugin, we need to tell it what + % colors to plot with: + my_state_colors = SoundCatSMA(obj, 'get_state_colors'); + % In pokesplot, the poke colors have a default value, so we don't need + % to specify them, but here they are so you know how to change them. + my_poke_colors = struct( ... + 'L', 0.6*[1 0.66 0], ... + 'C', [0 0 0], ... + 'R', 0.9*[1 0.66 0]); + + [x, y] = PokesPlotSection(obj, 'init', x, y, ... + struct('states', my_state_colors, 'pokes', my_poke_colors)); next_row(y); + + [x, y] = CommentsSection(obj, 'init', x, y); + SessionDefinition(obj, 'init', x, y, value(myfig)); next_row(y, 2); %#ok + + next_column(x); y=5; + [x, y] = OverallPerformanceSection(obj, 'init', x, y); + [x, y] = StimulatorSection(obj, 'init', x, y); next_row(y, 1.3); + [x, y] = SideSection(obj, 'init', x, y); %#ok + [x, y] = SoundSection(obj,'init',x,y); + [x, y] = StimulusSection(obj,'init',x,y); + + figpos = get(double(gcf), 'Position'); + [expmtr, rname]=SavingSection(obj, 'get_info'); + HeaderParam(obj, 'prot_title', [mfilename ': ' expmtr ', ' rname], x, y, 'position', [10 figpos(4)-25, 800 20]); + + SoundCatSMA(obj, 'init'); + feval(mfilename, obj, 'prepare_next_trial'); + + + %% prepare next trial + case 'prepare_next_trial' + + SideSection(obj, 'prepare_next_trial'); + % Run SessionDefinition *after* SideSection so we know whether the + % trial was a violation or not + SessionDefinition(obj, 'next_trial'); + StimulatorSection(obj, 'update_values'); + OverallPerformanceSection(obj, 'evaluate'); + StimulusSection(obj,'prepare_next_trial'); + SoundManagerSection(obj, 'send_not_yet_uploaded_sounds'); + + nTrials.value = n_done_trials; + + [sma, prepare_next_trial_states] = SoundCatSMA(obj, 'prepare_next_trial'); + + % Default behavior of following call is that every 20 trials, the data + % gets saved, not interactive, no commit to CVS. + SavingSection(obj, 'autosave_data'); + + CommentsSection(obj, 'clear_history'); % Make sure we're not storing unnecessary history + if n_done_trials==1 % Auto-append date for convenience. + CommentsSection(obj, 'append_date'); CommentsSection(obj, 'append_line', ''); + end + + if n_done_trials==1 + [expmtr, rname]=SavingSection(obj, 'get_info'); + prot_title.value=[mfilename ' on rig ' get_hostname ' : ' expmtr ', ' rname '. Started at ' datestr(now, 'HH:MM')]; + end + + try send_n_done_trials(obj); end + + %% trial_completed + case 'trial_completed' + + % Do any updates in the protocol that need doing: + feval(mfilename, 'update'); + % And PokesPlot needs completing the trial: + PokesPlotSection(obj, 'trial_completed'); + %% update + case 'update' + PokesPlotSection(obj, 'update'); + + + %% close + case 'close' + PokesPlotSection(obj, 'close'); + %PunishmentSection(obj, 'close'); + SideSection(obj, 'close'); + StimulusSection(obj,'close'); + + if exist('myfig', 'var') && isa(myfig, 'SoloParamHandle') && ishandle(value(myfig)), %#ok + delete(value(myfig)); + end + delete_sphandle('owner', ['^@' class(obj) '$']); + + + %% end_session + case 'end_session' + prot_title.value = [value(prot_title) ', Ended at ' datestr(now, 'HH:MM')]; + + + %% pre_saving_settings + case 'pre_saving_settings' + + StimulusSection(obj,'hide'); + SessionDefinition(obj, 'run_eod_logic_without_saving'); + perf = OverallPerformanceSection(obj, 'evaluate'); + cp_durs = SideSection(obj, 'get_cp_history'); + + [stim1dur] = SideSection(obj,'get_stimdur_history'); + %stim_history = StimulatorSection(obj,'get_history'); + + pd.hits=hit_history(:); + pd.sides=previous_sides(:); + pd.viols=violation_history(:); + pd.timeouts=timeout_history(:); +% pd.performance=tot_perf(:); + pd.cp_durs=cp_durs(:); + + +% pd.stimuli=stimuli(:); + % Athena: look into pair_history perhaps stimulus_history and stimuli + % are the same + %pd.stimulus=stimulus_history(:); + + pd.stim1dur=stim1dur(:); + + %pd.stimul=stim_history(:); + + sendsummary(obj,'protocol_data',pd); + + %% otherwise + otherwise + warning('Unknown action! "%s"\n', action); +end + +return; + diff --git a/Protocols/@Arpit_SoundCatContinuous/SideSection.m b/Protocols/@Arpit_SoundCatContinuous/SideSection.m new file mode 100644 index 00000000..c46b644f --- /dev/null +++ b/Protocols/@Arpit_SoundCatContinuous/SideSection.m @@ -0,0 +1,505 @@ + + +function [x, y] = SideSection(obj, action, x,y) + +GetSoloFunctionArgs(obj); + +switch action + + % ------------------------------------------------------------------ + % INIT + % ------------------------------------------------------------------ + + case 'init' + + SoloParamHandle(obj, 'my_gui_info', 'value', [x y double(gcf)], 'saveable', 0); + y0 = y; + + [x, y] = AntibiasSectionAthena(obj, 'init', x, y); + + + ToggleParam(obj, 'antibias_LRprob', 0, x,y,... + 'OnString', 'AB_Prob ON',... + 'OffString', 'AB_Prob OFF',... + 'TooltipString', sprintf(['If on (Yellow) then it enables the AntiBias algorithm\n'... + 'based on changing the probablity of Left vs Right'])); + + next_row(y); + NumeditParam(obj, 'LeftProb', 0.5, x, y); next_row(y); + set_callback(LeftProb, {mfilename, 'new_leftprob'}); + MenuParam(obj, 'MaxSame', {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, Inf}, Inf, x, y, ... + 'TooltipString', sprintf(['\nMaximum number of consecutive trials where correct\n' ... + 'response is on the same side. Overrides antibias. Thus, for\n' ... + 'example, if MaxSame=5 and there have been 5 Left trials, the\n' ... + 'next trial is guaranteed to be Right'])); next_row(y); + + DispParam(obj, 'ThisTrial', 'LEFT', x, y); next_row(y); + SoloParamHandle(obj, 'previous_sides', 'value', []); + DeclareGlobals(obj, 'ro_args', 'previous_sides'); + SubheaderParam(obj, 'title', 'Sides Section', x, y); + next_row(y, 1.5); + %% slow ramp up of water amount + %%the water volume is controlled by a 5-parameter logistic function: WaterAmount(t) = maxasymp + (minasymp/(1+(t/inflp)^slp).^assym) + NumeditParam(obj, 'maxasymp', 38, x,y,'label','maxasymp','TooltipString',... + 'the water volume is controlled by a 5-parameter logistic function: WaterAmount(trialnum) = maxasymp + (minasymp/(1+(trialnum/inflp)^slp).^assym)'); + next_row(y); + NumeditParam(obj, 'slp', 3, x,y,'label','slp','TooltipString','Water Modulation: Slope of the logistic function'); + next_row(y); + NumeditParam(obj, 'inflp', 350, x,y,'label','inflp','TooltipString','Water Modulation: concentration at the inflection point'); + next_row(y); + NumeditParam(obj, 'minasymp', -21, x,y,'label','inflp','TooltipString','Water Modulation: minimum asymptote'); + next_row(y); + NumeditParam(obj, 'assym', 0.7, x,y,'label','assym','TooltipString','Water Modulation: asymmetry factor'); + next_row(y); + DispParam(obj, 'trial_1', 0, x, y, 'TooltipString', 'uL on first trial'); + next_row(y); + DispParam(obj, 'trial_150', 0, x, y, 'TooltipString', 'uL on trial 150'); + next_row(y); + DispParam(obj, 'trial_300', 0, x, y, 'TooltipString', 'uL on trial 300'); + next_row(y); + set_callback({maxasymp;slp;inflp;minasymp;assym}, {mfilename, 'change_water_modulation_params'}); + feval(mfilename, obj, 'change_water_modulation_params'); + + next_column(x); y = 5; + NumeditParam(obj, 'RewardCollection_duration', 10, x,y,'label','RewardCollection_duration','TooltipString','Wait until rat collects the reward'); + next_row(y); + NumeditParam(obj, 'CenterLed_duration', 200, x,y,'label','Central LED duration','TooltipString','Duration of Center Led'); + next_row(y); + NumeditParam(obj, 'SideLed_duration', 0.5, x,y,'label','Side LED duration','TooltipString','Duration of SideLed'); + next_row(y); + NumeditParam(obj, 'legal_cbreak', 0.1, x,y, 'position', [x, y, 175 20], 'TooltipString','Time in sec for which it is ok to be outside the center port before a violation occurs.'); + ToggleParam(obj, 'LED_during_legal_cbreak', 1, x, y, 'OnString', 'LED ON LcB', 'OffString', 'LED off LcB', ... + 'position', [x+180 y 20 20], 'TooltipString', ... + 'If 1 (black), turn center port LED back on during legal_cbreak; if 0 (brown), leave LED off'); + next_row(y); + NumeditParam(obj, 'SettlingIn_time', 0.2, x,y, 'position', [x, y, 175 20], 'TooltipString','Initial settling period during which "legal cbreak period" can be longer than the usual "legal_cbreak"'); + next_row(y); + NumeditParam(obj, 'settling_legal_cbreak', 0.1, x,y, 'position', [x, y, 175 20], 'TooltipString','Time in sec for which it is ok during the "SettlingIn_time" to be outside the center port before a violation occurs.'); + ToggleParam(obj, 'LED_during_settling_legal_cbreak', 0, x, y, 'OnString', 'LED ON SetLcB', 'OffString', 'LED OFF setLcB', ... + 'position', [x+180 y 20 20], 'TooltipString', ... + 'If 1 (black), turn center port LED back on during settling_legal_cbreak; if 0 (brown), leave LED off'); + next_row(y); + MenuParam(obj, 'side_lights' ,{'none','both','correct side','anti side'},1, x,y,'label','Side Lights','TooltipString','Controls the side LEDs during wait_for_spoke'); + next_row(y); + + + NumeditParam(obj, 'A1_time', 0.4, x,y,'label','AUD1 on Time','TooltipString','Duration of first stimulus'); + next_row(y); + set_callback(A1_time, {mfilename, 'new_CP_duration'}); + NumeditParam(obj, 'PreStim_time', 0.20, x,y,'label','Pre-Stim NIC time','TooltipString','Time in NIC before starting the stimulus'); + next_row(y); + set_callback(PreStim_time, {mfilename, 'new_CP_duration'}); + NumeditParam(obj, 'time_bet_aud1_gocue', 0, x,y,'label','A1-GoCue time','TooltipString','time between the end of the stimulus and the go cue '); + next_row(y); + set_callback(time_bet_aud1_gocue, {mfilename, 'new_CP_duration'}); + DispParam(obj, 'init_CP_duration', 0.01, x,y,'label','init_CP duration','TooltipString','Duration of Nose in Central Poke before Go cue starts (see Total_CP_duration)'); + next_row(y); + DispParam(obj, 'CP_duration', PreStim_time+A1_time+time_bet_aud1_gocue, x,y,'label','CP duration','TooltipString','Duration of Nose in Central Poke before Go cue starts (see Total_CP_duration)'); + set_callback(CP_duration, {mfilename, 'new_CP_duration'}); + next_row(y); + NumeditParam(obj, 'time_go_cue' ,0, x,y,'label','Go Cue Duration','TooltipString','duration of go cue (see Total_CP_duration)'); + set_callback(time_go_cue, {mfilename, 'new_time_go_cue'}); + next_row(y); + DispParam(obj, 'Total_CP_duration', CP_duration+time_go_cue, x, y, 'TooltipString', 'Total nose in center port time, in secs. Sum of CP_duration and Go Cue duration'); %#ok<*NODEF> + next_row(y); + ToggleParam(obj, 'warmup_on', 1, x,y,... + 'OnString', 'Warmup ON',... + 'OffString', 'Warmup OFF',... + 'TooltipString', sprintf(['If on (Yellow) then it applies the initial warming up phase, during which the\n',... + 'CP_duration starts small and gradually grows to last_session_max_cp_duration'])); + next_row(y); + % to modify it for widefield imagine + NumeditParam(obj,'imaging',0,x,y,'label','Scope Trigger'); + next_row(y); + NumeditParam(obj, 'reward_delay', 0.01, x,y,'label','Reward Delay','TooltipString','Delay between side poke and reward delivery'); + next_row(y); + NumeditParam(obj, 'reward_duration', 0.1, x,y,'label','Reward Duration','TooltipString','Duration of reward sound'); + next_row(y); + NumeditParam(obj, 'drink_time', 1, x,y,'label','Drink Time','TooltipString','waits to finish water delivery'); + next_row(y); + NumeditParam(obj, 'error_iti', 5, x,y,'label','Error Timeout','TooltipString','ITI on error trials'); + next_row(y); + NumeditParam(obj, 'violation_iti', 1, x,y,'label','Violation Timeout','TooltipString','Center poke violation duration'); + next_row(y); + MenuParam(obj, 'reward_type', {'Always','DelayedReward', 'NoReward'}, ... + 'Always', x, y, 'labelfraction', 0.35, 'TooltipString', sprintf(['\nThis menu is to determine the Reward delivery on wrong-hit trials\n',... + '\nIf ''Always'': reward will be available on each trial no matter which side rat goes first\n',... + '\n If rat pokes first on the wrong side, then reward will be delivered with a delay (if DelayedReward) or not delivered at all (if NoReward)'])); + set_callback(reward_type, {mfilename, 'new_reward_type'}); + next_row(y); + NumeditParam(obj,'secondhit_delay',0,x,y,'label','SecondHit Delay','TooltipString','Reward will be delayed with this amount if reward_type=DelayedReward'); + + next_row(y); + ToggleParam(obj, 'random_A1_time', 0, x,y,... + 'OnString', 'random A1_time ON',... + 'OffString', 'random A1_time OFF',... + 'TooltipString', sprintf('If on (black) then it enables the random sampling of A1_time')); + next_row(y); + ToggleParam(obj, 'random_prego_time', 0, x,y,... + 'OnString', 'random prego_time ON',... + 'OffString', 'random prego_time OFF',... + 'TooltipString', sprintf('If on (black) then it enables the random sampling of time between the end of the stimulus and the go cue')); + + next_column(x); + y=5; + NumeditParam(obj,'trials_in_stage',1,x,y,'label','Trial Counter'); + next_row(y); + NumeditParam(obj,'training_stage',1,x,y,'label','Training Stage'); + next_row(y); + ToggleParam(obj,'use_training',0,x,y,'OnString','Using Autotrain','OffString','Manual Settings'); + + next_row(y); + NumeditParam(obj, 'ntrial_correct_bias', 0, x, y, ... + 'TooltipString', 'antibias starts from trial=ntrial_correct_bias'); + next_row(y); + NumeditParam(obj, 'right_left_diff', .12, x, y, ... + 'TooltipString', 'antibias applies if difference between right and left sides is bigger than this number'); + next_row(y); + NumeditParam(obj, 'max_wtr_mult', 4, x, y, ... + 'TooltipString', 'wtr_mult will be min(max_wtr_mult,right_hit/left_hit)'); + next_row(y); + NumeditParam(obj, 'left_wtr_mult', 1, x, y, ... + 'TooltipString', 'all left reward times are multiplied by this number'); + next_row(y); + NumeditParam(obj, 'right_wtr_mult', 1, x, y, ... + 'TooltipString', 'all right reward times are multiplied by this number'); + next_row(y); + ToggleParam(obj, 'antibias_wtr_mult', 0, x,y,... + 'OnString', 'AB ON',... + 'OffString', 'AB OFF',... + 'TooltipString', sprintf(['If on (black) then it disables the wtr_mult entries\n'... + 'and uses hitfrac to adjust the water times'])); + + next_row(y); + ToggleParam(obj, 'stimuli_on', 1, x,y,... + 'OnString', 'Stimuli ON',... + 'OffString', 'Stimuli OFF',... + 'TooltipString', sprintf('If on (black) then it disable the presentation of sound stimuli during nose poke')); + set_callback(stimuli_on, {mfilename, 'new_CP_duration'}); + + next_row(y); + SoloFunctionAddVars('SoundCatSMA', 'ro_args', ... + {'CP_duration';'SideLed_duration';'CenterLed_duration';'side_lights' ; ... + 'RewardCollection_duration';'training_stage'; ... + 'legal_cbreak' ; 'LED_during_legal_cbreak' ; ... + 'SettlingIn_time';'settling_legal_cbreak' ; 'LED_during_settling_legal_cbreak' ; ... + 'time_go_cue'; ... + 'stimuli_on';'A1_time';'time_bet_aud1_gocue' ; ... + 'PreStim_time';'warmup_on' + 'drink_time';'reward_delay';'reward_duration';'left_wtr_mult';... + 'right_wtr_mult';'antibias_wtr_mult';... + 'reward_type';'secondhit_delay';'error_iti';'violation_iti';'imaging'}); + + SoloFunctionAddVars('StimulusSection', 'ro_args', ... + {'ThisTrial';'A1_time';'time_bet_aud1_gocue' ; ... + 'PreStim_time'}); + SoloFunctionAddVars('StimulatorSection', 'ro_args', ... + {'A1_time';'time_bet_aud1_gocue';'time_go_cue'; ... + 'PreStim_time';'CP_duration';'Total_CP_duration'}); + + % History of hit/miss: + SoloParamHandle(obj, 'deltaf_history', 'value', []); + + SoloFunctionAddVars('OverallPerformanceSection', 'ro_args', ... + {'training_stage'}); + + SoloFunctionAddVars('SoundCatSMA', 'ro_args', ... + {'maxasymp';'slp';'inflp';'minasymp';'assym'}); + + SoloParamHandle(obj, 'previous_parameters', 'value', []); + + + %% change_water_modulation_params + case 'change_water_modulation_params', + display_guys = [1 150 300]; + for i=1:numel(display_guys), + t = display_guys(i); + + myvar = eval(sprintf('trial_%d', t)); + myvar.value = maxasymp + (minasymp/(1+(t/inflp)^slp).^assym); + end; + + + case 'new_leftprob' + AntibiasSectionAthena(obj, 'update_biashitfrac', value(LeftProb)); + + + case 'new_CP_duration' + if stimuli_on == 0 + PreStim_time.value=0; + A1_time.value=0; + time_bet_aud1_gocue.value=0; + disable(PreStim_time); + disable(A1_time); + disable(time_bet_aud1_gocue); + else + enable(PreStim_time); + enable(A1_time); + enable(time_bet_aud1_gocue); + end + CP_duration.value=PreStim_time + A1_time + time_bet_aud1_gocue; + Total_CP_duration.value = CP_duration + time_go_cue; %#ok<*NASGU> + + case 'new_time_go_cue' + Total_CP_duration.value = CP_duration + time_go_cue; + SoundInterface(obj, 'set', 'GoSound', 'Dur1', value(time_go_cue)); + + case 'new_reward_type' + if strcmp(reward_type,'DelayedReward') + enable(secondhit_delay) + else + secondhit_delay=0; + disable(secondhit_delay) + + end + + + case 'prepare_next_trial' + + + switch value(training_stage) + case 0 %% learning the reward sound association -left or right led on -> poke -> sound+reward + settling_time.value=0.01; + delay_time.value=0; + allow_nic_breaks.value=1; + side_lights.value=3; + trials_in_stage.value=0; + reward_delay.value=0.01; + left_prob.value=0.5; + right_prob.value=0.5; + time_go_cue.value=0.200; + reward_duration=0.200; + + + case 1 %% center led on -> poke in the center -> go cue -> reward light and sound + %what A1_time and time before go cue? + if random_A1_time + A1_times = [0.2, 0.4]; + randomix = randi(length(A1_times), 1); + A1_time.value = A1_times(randomix); + end + + if random_prego_time + prego_times = [0.2, 0.4, 0.6, 0.8, 1]; + randomixx = randi(length(prego_times), 1); + time_bet_aud1_gocue.value = prego_times(randomixx); + end + settling_time.value=0.25; + delay_time.value=0; + allow_nic_breaks.value=1; + side_lights.value=3; + training_stage.value=1; + trials_in_stage.value=0; + reward_delay.value=0.01; + left_prob.value=0.5; + right_prob.value=0.5; + if n_done_trials <1 && warmup_on ==1 + CP_duration.value=value(init_CP_duration); + else + CP_duration.value=PreStim_time + A1_time + time_bet_aud1_gocue; + end + Total_CP_duration.value = CP_duration + time_go_cue; %#ok<*NASGU> + + + case 3 % like stage 2, now passive exposure to the stimuli - reward comes anyway + case 4 %% now reward comes only if rat goes to the correct side + + end + + + %% update hit_history, previous_sides, etc + was_viol=false; + was_hit=false; + was_timeout=false; + if n_done_trials>0 + if ~isempty(parsed_events) + if isfield(parsed_events,'states') + if isfield(parsed_events.states,'timeout_state') + was_timeout=rows(parsed_events.states.timeout_state)>0; + end + if isfield(parsed_events.states,'violation_state') + was_viol=rows(parsed_events.states.violation_state)>0; + end + end + + end + + violation_history.value=[violation_history(:); was_viol]; + timeout_history.value=[timeout_history(:); was_timeout]; + + SideSection(obj,'update_side_history'); + + if ~was_viol && ~was_timeout + %was_hit=rows(parsed_events.states.hit_state)>0; + was_hit=rows(parsed_events.states.second_hit_state)==0; + hit_history.value=[hit_history(:); was_hit]; + + else + % There was a violation or timeout + hit_history.value=[hit_history(:); nan]; + end + + % Now calculate the deltaF and sides - this maybe interesting + % even in a violation or timeout case. + + fn=fieldnames(parsed_events.states); + led_states=find(strncmp('led',fn,3)); + deltaF=0; + n_l=0; + n_r=0; + for lx=1:numel(led_states) + lind=led_states(lx); + if rows(parsed_events.states.(fn{lind}))>0 + if fn{lind}(end)=='l' + deltaF=deltaF-1; + n_l=n_l+1; + elseif fn{lind}(end)=='r' + deltaF=deltaF+1; + n_r=n_r+1; + elseif fn{lind}(end)=='b' + n_l=n_l+1; + n_r=n_r+1; + + end + end + + end + + % if deltaF>0 then a right poke is a hit + % if deltaF<0 then a left poke is a hit + + deltaf_history.value=[deltaf_history(:); deltaF]; + + end + + if antibias_LRprob ==1 + if n_done_trials >ntrial_correct_bias && ~was_viol && ~was_timeout + nonan_hit_history=value(hit_history); + nonan_hit_history(isnan(nonan_hit_history))=[]; + nonan_previous_sides=value(previous_sides); + nan_history=value(hit_history); + nonan_previous_sides(isnan(nan_history))=[]; + AntibiasSectionAthena(obj, 'update', value(LeftProb), nonan_hit_history(:)',nonan_previous_sides(:)); % <~> Transposed hit history so that it is the expected column vector. (Antibias errors out otherwise.) 2007.09.05 01:39 + end + + + if ~isinf(MaxSame) && length(previous_sides) > MaxSame && ... + all(previous_sides(n_done_trials-MaxSame+1:n_done_trials) == previous_sides(n_done_trials)), %#ok + if previous_sides(end)=='l', ThisTrial.value = 'RIGHT'; + else ThisTrial.value = 'LEFT'; + end; + else + choiceprobs = AntibiasSectionAthena(obj, 'get_posterior_probs'); + if rand(1) <= choiceprobs(1), ThisTrial.value = 'LEFT'; + else ThisTrial.value = 'RIGHT'; + end; + end; + + else + if (rand(1)<=LeftProb) + ThisTrial.value='LEFT'; + + else + ThisTrial.value='RIGHT'; + end + + end + + + + +% %% Do the anti-bias with changing reward delivery +% % reset anti-bias +% left_wtr_mult.value=1; +% right_wtr_mult.value=1; +% if n_done_trials>ntrial_correct_bias && antibias_wtr_mult==1 +% hh=hit_history(n_done_trials-ntrial_correct_bias:n_done_trials); +% ps=previous_sides(n_done_trials-ntrial_correct_bias:n_done_trials); +% +% right_hit=nanmean(hh(ps=='r')); +% left_hit=nanmean(hh(ps=='l')); +% +% if abs(right_hit-left_hit) + + case 'get_left_prob' + x = value(LeftProb); + + case 'get_cp_history' + x = cell2mat(get_history(CP_duration)); + + case 'get_stimdur_history' + x = cell2mat(get_history(A1_time)); + + case 'update_side_history' + if strcmp(ThisTrial, 'LEFT') + ps=value(previous_sides); + ps(n_done_trials)='l'; + previous_sides.value=ps; + + else + ps=value(previous_sides); + ps(n_done_trials)='r'; + previous_sides.value=ps; + end; + + case 'get_current_side' + if strcmp(ThisTrial, 'LEFT') + x = 'l'; %#ok + else + x = 'r'; + end; + + + case 'close' + % Delete all SoloParamHandles who belong to this object and whose + % fullname starts with the name of this mfile: + delete_sphandle('owner', ['^@' class(obj) '$'], ... + 'fullname', ['^' mfilename]); + + case 'reinit', + currfig = double(gcf); + + % Get the original GUI position and figure: + x = my_gui_info(1); y = my_gui_info(2); figure(my_gui_info(3)); + + % Delete all SoloParamHandles who belong to this object and whose + % fullname starts with the name of this mfile: + delete_sphandle('owner', ['^@' class(obj) '$'], ... + 'fullname', ['^' mfilename]); + + % Reinitialise at the original GUI position and figure: + [x, y] = feval(mfilename, obj, 'init', x, y); + + % Restore the current figure: + figure(currfig); + +end + + diff --git a/Protocols/@Arpit_SoundCatContinuous/StimulatorSection.asv b/Protocols/@Arpit_SoundCatContinuous/StimulatorSection.asv new file mode 100644 index 00000000..051237fe --- /dev/null +++ b/Protocols/@Arpit_SoundCatContinuous/StimulatorSection.asv @@ -0,0 +1,325 @@ +% +% PARAMETERS: +% ----------- +% +% obj Default object argument. +% +% action One of: +% +% 'prepare_next_trial' +% +% 'init' +% +% +% RETURNS: +% -------- +% +% +% +% +% +% + + + +function [varargout] = StimulatorSection(obj, action, x, y) + +GetSoloFunctionArgs(obj); + +switch action + +%% init +% ---------------------------------------------------------------- +% +% INIT +% +% ---------------------------------------------------------------- + + case 'init', + %NumeditParam(obj,'LCB_onstim', 0,x,y,'position',[x y 100 20],'labelfraction',0.6); + %NumeditParam(obj,'LCB_nostim', 0,x,y,'position',[x+100 y 100 20],'labelfraction',0.6); next_row(y); + %SoloParamHandle(obj,'LegalCBrk_temp', 'value',0); + + MenuParam(obj,'StimInterval',{'WholeTrial','S1','DelayDur','GoCue'},1,x,y,'labelfraction',0.30); next_row(y); + set_callback(StimInterval, {mfilename, 'StimInterval'}); + MenuParam(obj,'StimOnSide',{'both','left','right'},1,x,y,'labelfraction',0.3); next_row(y); + + diolines = bSettings('get','DIOLINES', 'all'); + for i = 1:size(diolines,1); dionames{i} = diolines{i,1}; dionums(i) = diolines{i,2}; end %#ok + [dionums order] = sort(dionums); + dionames2 = cell(0); + for i = 1:length(dionums); if ~isnan(dionums(i)); dionames2{end+1} = dionames{order(i)}; end; end %#ok + dionames2 = cell(0); + dionames2{1} = 'opto'; + + MenuParam(obj,'StimLine',dionames2,1,x,y,'labelfraction',0.30); next_row(y); + + SC = state_colors(obj); + WC = wave_colors(obj); + states = fieldnames(SC); + waves = fieldnames(WC); + states(2:end+1) = states; + states{1} = 'cp'; + states(end+1:end+length(waves)) = waves; + + MenuParam(obj,'StimState',states,1,x,y,'labelfraction',0.30); next_row(y); + + NumeditParam(obj,'StimStates', 1,x,y,'position',[x y 100 20],'labelfraction',0.60); + NumeditParam(obj,'StimLines', 1,x,y,'position',[x+100 y 100 20],'labelfraction',0.60); next_row(y); + + NumeditParam(obj,'StartDelay', 0,x,y,'position',[x y 100 20],'labelfraction',0.60); + NumeditParam(obj,'StimFreq', 20,x,y,'position',[x+100 y 100 20],'labelfraction',0.60); next_row(y); + NumeditParam(obj,'PulseWidth',15,x,y,'position',[x y 100 20],'labelfraction',0.60); + NumeditParam(obj,'NumPulses', 1,x,y,'position',[x+100 y 100 20],'labelfraction',0.60); next_row(y); + + DispParam(obj,'SD',0 ,x,y,'position',[x y 50 20],'labelfraction',0.4); + DispParam(obj,'SF',20,x,y,'position',[x+50 y 50 20],'labelfraction',0.4); + DispParam(obj,'PW',15,x,y,'position',[x+100 y 50 20],'labelfraction',0.4); + DispParam(obj,'NP',1,x,y,'position',[x+150 y 50 20],'labelfraction',0.4); next_row(y); + + NumeditParam(obj,'StimProb', 0,x,y,'position',[x y 100 20],'labelfraction',0.65); + ToggleParam( obj,'ShuffleValues',0,x,y,'position',[x+100 y 100 20],'OnString','Shuffle','OffString','Lock'); next_row(y); + + SoloParamHandle(obj, 'stimulator_history', 'value', []); + + SubheaderParam(obj, 'title', 'Stimulator Section', x, y); next_row(y); + varargout{1} = x; + varargout{2} = y; + +%% update_values +% ----------------------------------------------------------------------- +% +% UPDATE_VALUES +% +% ----------------------------------------------------------------------- + + case 'update_values', + + StimulatorSection(obj,'StimInterval'); + sh = value(stimulator_history) %#ok + %if n_done_trials == 0 || sh(end) == 0 + % LegalCBrk_temp.value = value(LegalCBrk); %#ok + %end + + if ~dispatcher('is_running'); + %dispatcher is not running, last stim_hist not used, lop it off + sh = sh(1:end-1); + end + + if value(StimProb) == 0 + %LCB_nostim.value = value(LegalCBrk); %#ok + stimulator_history.value = [sh, 0]; + elseif rand(1) <= value(StimProb) %&& ((value(StimOnFree)==1 && strcmp(value(ThisTrial_Free),'FREE')) || value(StimOnFree)==0) + stimulator_history.value = [sh, 1]; + %LegalCBrk.value = value(LCB_onstim); + else + %LegalCBrk.value = value(LCB_nostim); %#ok + stimulator_history.value = [sh, 0]; + end + + + +%% prepare_next_trial +% ----------------------------------------------------------------------- +% +% PREPARE_NEXT_TRIAL +% +% ----------------------------------------------------------------------- + + case 'prepare_next_trial', + sh = value(stimulator_history); %#ok + + sma = x; + + sd = value(StartDelay); + sf = value(StimFreq); + pw = value(PulseWidth); + np = value(NumPulses); + ss = value(StimStates) + sl = value(StimLines) + + if value(ShuffleValues) == 1 + sd = sd(ceil(rand(1) * length(sd))); + sf = sf(ceil(rand(1) * length(sf))); + pw = pw(ceil(rand(1) * length(pw))); + np = np(ceil(rand(1) * length(np))); + ss = ss(ceil(rand(1) * length(ss))); + sl = sl(ceil(rand(1) * length(sl))); + else + if length(unique([length(sd) length(sf) length(pw) length(np) length(ss) length(sl)])) > 1 + disp('Warning: param values in StimulatorSection have different lengths. Only first value will be used.'); + temp = 1; + else + temp = ceil(rand(1) * length(sd)); + end + sd = sd(temp); sf = sf(temp); pw = pw(temp); np = np(temp); ss = ss(temp); sl = sl(temp); + end + + pss = get(get_ghandle(StimState),'String'); %#ok + psl = get(get_ghandle(StimLine), 'String'); %#ok + if ss > length(pss) + disp('StimState value greater than list of possible stim states'); + else + StimState.value = ss; + disp('test ss') + value(ss) + end + + if sl > length(psl) + slc = ['0',num2str(sl),'0']; + z = find(slc == '0'); + if length(z) > 2 + sln = []; + for i = 1:length(z)-1 + sln(end+1) = str2num(slc(z(i)+1:z(i+1)-1)); %#ok + end + if any(sln > length(psl)) + disp('StimLine value greater than list of possible stim lines'); + else + slname = psl{sln(1)}; + for i=2:length(sln) + slname = [slname,'+',psl{sln(i)}]; %#ok + end + if sum(strcmp(psl,slname)) == 0 + psl{end+1} = slname; + set(get_ghandle(StimLine),'String',psl) + end + StimLine.value = find(strcmp(psl,slname)==1,1,'first'); + sl = sln; + end + else + disp('StimLine value greater than list of possible stim lines'); + end + else + StimLine.value = sl; + end + + for i = 1:length(sl) + stimline = bSettings('get','DIOLINES',psl{sl(i)}); + + sma = add_scheduled_wave(sma,... + 'name', ['stimulator_wave',num2str(i)],... + 'preamble', (1/sf)-(pw/1000),... %%%% Remember: change it such that if this is negative makes it 0 + 'sustain' , pw/1000,... + 'DOut', stimline,... + 'loop', np-1); + + if sd ~= 0 + sma = add_scheduled_wave(sma,... + 'name',['stimulator_wave_pause',num2str(i)],... + 'preamble',sd,... + 'trigger_on_up',['stimulator_wave',num2str(i)]); + else + sma = add_scheduled_wave(sma,... + 'name',['stimulator_wave_pause',num2str(i)],... + 'preamble',1,... + 'trigger_on_up',['stimulator_wave',num2str(i)]); + end + end + + for i = 1:length(sl) + if sh(end) == 1 + if strcmp(value(StimState),'none') == 0 + if sd ~= 0 + sma = add_stimulus(sma,['stimulator_wave_pause',num2str(i)],value(StimState)); + else + sma = add_stimulus(sma,['stimulator_wave',num2str(i)],value(StimState)); + end + + SD.value = sd; SF.value = sf; PW.value = pw; NP.value = np; + end + else + SD.value = 0; SF.value = 0; PW.value = 0; NP.value = 0; + end + end + + varargout{1} = sma; + + + %% Case StimInterval + case 'StimInterval' + + if strcmp(StimInterval, 'WholeTrial') + PulseWidth.value = Total_CP_duration*1000; + StimFreq.value = 1000/PulseWidth; + StartDelay.value = PreStim_time; + elseif strcmp(StimInterval, 'S1') + PulseWidth.value = A1_time*1000; + StimFreq.value = 1000/PulseWidth; + StartDelay.value = PreStim_time; + elseif strcmp(StimInterval, 'DelayDur') + PulseWidth.value = time_bet_aud1_gocue*1000; + StimFreq.value = 1000/PulseWidth; + StartDelay.value = PreStim_time + A1_time; + elseif strcmp(StimInterval, 'GoCue') + PulseWidth.value = time_go_cue*1000; + StimFreq.value = 1000/PulseWidth; + StartDelay.value = PreStim_time + A1_time + time_bet_aud1_gocue; + + end +%% set +% ----------------------------------------------------------------------- +% +% SET +% +% ----------------------------------------------------------------------- + case 'set' + varname = x; + newval = y; + + try + temp = 'SoloParamHandle'; %#ok + eval(['test = isa(',varname,',temp);']); + if test == 1 + eval([varname,'.value = newval;']); + end + catch %#ok + showerror; + warning(['Unable to assign value: ',num2str(newval),' to SoloParamHandle: ',varname]); %#ok + end + + +%% get +% ----------------------------------------------------------------------- +% +% GET +% +% ----------------------------------------------------------------------- + case 'get' + varname = x; + + try + temp = 'SoloParamHandle'; %#ok + eval(['test = isa(',varname,',temp);']); + if test == 1 + eval(['varargout{1} = value(',varname,');']); + end + catch %#ok + showerror; + warning(['Unable to get value from SoloParamHandle: ',varname]); %#ok + end + + +%% reinit +% ----------------------------------------------------------------------- +% +% REINIT +% +% ----------------------------------------------------------------------- + case 'reinit' + currfig = double(gcf); + + % Delete all SoloParamHandles who belong to this object and whose + % fullname starts with the name of this mfile: + delete_sphandle('owner', ['^@' class(obj) '$'], ... + 'fullname', ['^' mfilename]); + + + % Reinitialise at the original GUI position and figure: + feval(mfilename, obj, 'init'); + + % Restore the current figure: + figure(currfig); +end + + diff --git a/Protocols/@Arpit_SoundCatContinuous/StimulatorSection.m b/Protocols/@Arpit_SoundCatContinuous/StimulatorSection.m new file mode 100644 index 00000000..35c40f30 --- /dev/null +++ b/Protocols/@Arpit_SoundCatContinuous/StimulatorSection.m @@ -0,0 +1,317 @@ +% +% PARAMETERS: +% ----------- +% +% obj Default object argument. +% +% action One of: +% +% 'prepare_next_trial' +% +% 'init' +% +% +% RETURNS: +% -------- + +function [varargout] = StimulatorSection(obj, action, x, y) + +GetSoloFunctionArgs(obj); + +switch action + +%% init +% ---------------------------------------------------------------- +% +% INIT +% +% ---------------------------------------------------------------- + + case 'init' + %NumeditParam(obj,'LCB_onstim', 0,x,y,'position',[x y 100 20],'labelfraction',0.6); + %NumeditParam(obj,'LCB_nostim', 0,x,y,'position',[x+100 y 100 20],'labelfraction',0.6); next_row(y); + %SoloParamHandle(obj,'LegalCBrk_temp', 'value',0); + + MenuParam(obj,'StimInterval',{'WholeTrial','S1','DelayDur','GoCue'},1,x,y,'labelfraction',0.30); next_row(y); + set_callback(StimInterval, {mfilename, 'StimInterval'}); + MenuParam(obj,'StimOnSide',{'both','left','right'},1,x,y,'labelfraction',0.3); next_row(y); + + diolines = bSettings('get','DIOLINES', 'all'); + for i = 1:size(diolines,1); dionames{i} = diolines{i,1}; dionums(i) = diolines{i,2}; end %#ok + [dionums order] = sort(dionums); + dionames2 = cell(0); + for i = 1:length(dionums); if ~isnan(dionums(i)); dionames2{end+1} = dionames{order(i)}; end; end %#ok + dionames2 = cell(0); + dionames2{1} = 'opto'; + + MenuParam(obj,'StimLine',dionames2,1,x,y,'labelfraction',0.30); next_row(y); + + SC = state_colors(obj); + WC = wave_colors(obj); + states = fieldnames(SC); + waves = fieldnames(WC); + states(2:end+1) = states; + states{1} = 'cp'; + states(end+1:end+length(waves)) = waves; + + MenuParam(obj,'StimState',states,1,x,y,'labelfraction',0.30); next_row(y); + + NumeditParam(obj,'StimStates', 1,x,y,'position',[x y 100 20],'labelfraction',0.60); + NumeditParam(obj,'StimLines', 1,x,y,'position',[x+100 y 100 20],'labelfraction',0.60); next_row(y); + + NumeditParam(obj,'StartDelay', 0,x,y,'position',[x y 100 20],'labelfraction',0.60); + NumeditParam(obj,'StimFreq', 20,x,y,'position',[x+100 y 100 20],'labelfraction',0.60); next_row(y); + NumeditParam(obj,'PulseWidth',15,x,y,'position',[x y 100 20],'labelfraction',0.60); + NumeditParam(obj,'NumPulses', 1,x,y,'position',[x+100 y 100 20],'labelfraction',0.60); next_row(y); + + DispParam(obj,'SD',0 ,x,y,'position',[x y 50 20],'labelfraction',0.4); + DispParam(obj,'SF',20,x,y,'position',[x+50 y 50 20],'labelfraction',0.4); + DispParam(obj,'PW',15,x,y,'position',[x+100 y 50 20],'labelfraction',0.4); + DispParam(obj,'NP',1,x,y,'position',[x+150 y 50 20],'labelfraction',0.4); next_row(y); + + NumeditParam(obj,'StimProb', 0,x,y,'position',[x y 100 20],'labelfraction',0.65); + ToggleParam( obj,'ShuffleValues',0,x,y,'position',[x+100 y 100 20],'OnString','Shuffle','OffString','Lock'); next_row(y); + + SoloParamHandle(obj, 'stimulator_history', 'value', []); + + SubheaderParam(obj, 'title', 'Stimulator Section', x, y); next_row(y); + varargout{1} = x; + varargout{2} = y; + +%% update_values +% ----------------------------------------------------------------------- +% +% UPDATE_VALUES +% +% ----------------------------------------------------------------------- + + case 'update_values' + + StimulatorSection(obj,'StimInterval'); + sh = value(stimulator_history); %#ok + %if n_done_trials == 0 || sh(end) == 0 + % LegalCBrk_temp.value = value(LegalCBrk); %#ok + %end + + if ~dispatcher('is_running'); + %dispatcher is not running, last stim_hist not used, lop it off + sh = sh(1:end-1); + end + + if value(StimProb) == 0 + %LCB_nostim.value = value(LegalCBrk); %#ok + stimulator_history.value = [sh, 0]; + elseif rand(1) <= value(StimProb) %&& ((value(StimOnFree)==1 && strcmp(value(ThisTrial_Free),'FREE')) || value(StimOnFree)==0) + stimulator_history.value = [sh, 1]; + %LegalCBrk.value = value(LCB_onstim); + else + %LegalCBrk.value = value(LCB_nostim); %#ok + stimulator_history.value = [sh, 0]; + end + + + +%% prepare_next_trial +% ----------------------------------------------------------------------- +% +% PREPARE_NEXT_TRIAL +% +% ----------------------------------------------------------------------- + + case 'prepare_next_trial' + sh = value(stimulator_history); %#ok + + sma = x; + + sd = value(StartDelay); + sf = value(StimFreq); + pw = value(PulseWidth); + np = value(NumPulses); + ss = value(StimStates); + sl = value(StimLines); + + if value(ShuffleValues) == 1 + sd = sd(ceil(rand(1) * length(sd))); + sf = sf(ceil(rand(1) * length(sf))); + pw = pw(ceil(rand(1) * length(pw))); + np = np(ceil(rand(1) * length(np))); + ss = ss(ceil(rand(1) * length(ss))); + sl = sl(ceil(rand(1) * length(sl))); + else + if length(unique([length(sd) length(sf) length(pw) length(np) length(ss) length(sl)])) > 1 + disp('Warning: param values in StimulatorSection have different lengths. Only first value will be used.'); + temp = 1; + else + temp = ceil(rand(1) * length(sd)); + end + sd = sd(temp); sf = sf(temp); pw = pw(temp); np = np(temp); ss = ss(temp); sl = sl(temp); + end + + pss = get(get_ghandle(StimState),'String'); %#ok + psl = get(get_ghandle(StimLine), 'String'); %#ok + if ss > length(pss) + disp('StimState value greater than list of possible stim states'); + else + StimState.value = ss; + disp('test ss') + value(ss) + end + + if sl > length(psl) + slc = ['0',num2str(sl),'0']; + z = find(slc == '0'); + if length(z) > 2 + sln = []; + for i = 1:length(z)-1 + sln(end+1) = str2num(slc(z(i)+1:z(i+1)-1)); %#ok + end + if any(sln > length(psl)) + disp('StimLine value greater than list of possible stim lines'); + else + slname = psl{sln(1)}; + for i=2:length(sln) + slname = [slname,'+',psl{sln(i)}]; %#ok + end + if sum(strcmp(psl,slname)) == 0 + psl{end+1} = slname; + set(get_ghandle(StimLine),'String',psl) + end + StimLine.value = find(strcmp(psl,slname)==1,1,'first'); + sl = sln; + end + else + disp('StimLine value greater than list of possible stim lines'); + end + else + StimLine.value = sl; + end + + for i = 1:length(sl) + stimline = bSettings('get','DIOLINES',psl{sl(i)}); + + sma = add_scheduled_wave(sma,... + 'name', ['stimulator_wave',num2str(i)],... + 'preamble', (1/sf)-(pw/1000),... %%%% Remember: change it such that if this is negative makes it 0 + 'sustain' , pw/1000,... + 'DOut', stimline,... + 'loop', np-1); + + if sd ~= 0 + sma = add_scheduled_wave(sma,... + 'name',['stimulator_wave_pause',num2str(i)],... + 'preamble',sd,... + 'trigger_on_up',['stimulator_wave',num2str(i)]); + else + sma = add_scheduled_wave(sma,... + 'name',['stimulator_wave_pause',num2str(i)],... + 'preamble',1,... + 'trigger_on_up',['stimulator_wave',num2str(i)]); + end + end + + for i = 1:length(sl) + if sh(end) == 1 + if strcmp(value(StimState),'none') == 0 + if sd ~= 0 + sma = add_stimulus(sma,['stimulator_wave_pause',num2str(i)],value(StimState)); + else + sma = add_stimulus(sma,['stimulator_wave',num2str(i)],value(StimState)); + end + + SD.value = sd; SF.value = sf; PW.value = pw; NP.value = np; + end + else + SD.value = 0; SF.value = 0; PW.value = 0; NP.value = 0; + end + end + + varargout{1} = sma; + + + %% Case StimInterval + case 'StimInterval' + + if strcmp(StimInterval, 'WholeTrial') + PulseWidth.value = Total_CP_duration*1000; + StimFreq.value = 1000/PulseWidth; + StartDelay.value = PreStim_time; + elseif strcmp(StimInterval, 'S1') + PulseWidth.value = A1_time*1000; + StimFreq.value = 1000/PulseWidth; + StartDelay.value = PreStim_time; + elseif strcmp(StimInterval, 'DelayDur') + PulseWidth.value = time_bet_aud1_gocue*1000; + StimFreq.value = 1000/PulseWidth; + StartDelay.value = PreStim_time + A1_time; + elseif strcmp(StimInterval, 'GoCue') + PulseWidth.value = time_go_cue*1000; + StimFreq.value = 1000/PulseWidth; + StartDelay.value = PreStim_time + A1_time + time_bet_aud1_gocue; + + end +%% set +% ----------------------------------------------------------------------- +% +% SET +% +% ----------------------------------------------------------------------- + case 'set' + varname = x; + newval = y; + + try + temp = 'SoloParamHandle'; %#ok + eval(['test = isa(',varname,',temp);']); + if test == 1 + eval([varname,'.value = newval;']); + end + catch %#ok + showerror; + warning(['Unable to assign value: ',num2str(newval),' to SoloParamHandle: ',varname]); %#ok + end + + +%% get +% ----------------------------------------------------------------------- +% +% GET +% +% ----------------------------------------------------------------------- + case 'get' + varname = x; + + try + temp = 'SoloParamHandle'; %#ok + eval(['test = isa(',varname,',temp);']); + if test == 1 + eval(['varargout{1} = value(',varname,');']); + end + catch %#ok + showerror; + warning(['Unable to get value from SoloParamHandle: ',varname]); %#ok + end + + +%% reinit +% ----------------------------------------------------------------------- +% +% REINIT +% +% ----------------------------------------------------------------------- + case 'reinit' + currfig = double(gcf); + + % Delete all SoloParamHandles who belong to this object and whose + % fullname starts with the name of this mfile: + delete_sphandle('owner', ['^@' class(obj) '$'], ... + 'fullname', ['^' mfilename]); + + + % Reinitialise at the original GUI position and figure: + feval(mfilename, obj, 'init'); + + % Restore the current figure: + figure(currfig); +end + + From 59998b84eecf2abdb8c7edc80dfae83d7cdf1ae3 Mon Sep 17 00:00:00 2001 From: viktorpm Date: Tue, 22 Apr 2025 19:51:24 +0100 Subject: [PATCH 058/164] camera debug --- .../Arpit_CentrePokeTraining.m | 19 ++- .../Connect_Bonsai_Camera.m | 55 ++++-- .../StimulusSection.m | 4 +- .../private/StimuliDistribution_plot.m | 158 +++++++++++++----- 4 files changed, 164 insertions(+), 72 deletions(-) diff --git a/Protocols/@Arpit_CentrePokeTraining/Arpit_CentrePokeTraining.m b/Protocols/@Arpit_CentrePokeTraining/Arpit_CentrePokeTraining.m index ca35b377..36eb8300 100644 --- a/Protocols/@Arpit_CentrePokeTraining/Arpit_CentrePokeTraining.m +++ b/Protocols/@Arpit_CentrePokeTraining/Arpit_CentrePokeTraining.m @@ -210,15 +210,15 @@ % Declare the folder location for saving the video files current_dir = cd; ratter_dir = extractBefore(current_dir,'ratter'); - main_dir_video = [ratter_dir '\ratter_Videos']; + main_dir_video = [ratter_dir 'ratter_Videos']; date_str = regexprep(char(datetime('today','Format','yyyy-MM-dd')), '[^0-9]', ''); - video_foldername = sprintf('video_@%s_%s_%s_%s',name,expmtr,ratname,date_str); - rat_dir = sprintf('%s\\%s\\%s',main_dir_video,expmtr,ratname); - video_save_dir = sprintf('%s\\%s\\%s\\%s',main_dir_video,expmtr,ratname,video_foldername); + video_foldername = sprintf('video_@%s_%s_%s_%s',name,expmtr,rname,date_str); + rat_dir = sprintf('%s\\%s\\%s',main_dir_video,expmtr,rname); + video_save_dir = sprintf('%s\\%s\\%s\\%s',main_dir_video,expmtr,rname,video_foldername); % We have the general structure of folder save location, now need to % check if there is any other folder for same date. We will add a % alphabet in the end based upon the no. of files present. - if exist('rat_dir','folder') == 7 + if exist('rat_dir','dir') == 7 folderNames_rat_dir = {listing(find([listing.isdir])).name}; folderNames_rat_dir = folderNames_rat_dir(~ismember(folderNames_rat_dir,{'.','..'})); % Remove the '.' and '..' entries (current and parent directories) sessions_today = length(find(contains(folderNames_rat_dir,video_foldername))); % number of folders containing the video foldername @@ -226,9 +226,10 @@ else video_save_dir = [video_save_dir char(97)]; end - makedir(video_save_dir); + mkdir(video_save_dir); SoloParamHandle(obj, 'Video_Saving_Folder', 'value', video_save_dir); - + SoloFunctionAddVars('Connect_Bonsai_Camera', 'ro_args', ... + {'Video_Saving_Folder'}); Connect_Bonsai_Camera(obj,'init'); %% @@ -279,6 +280,8 @@ %% trial_completed case 'trial_completed' + % Change the video trial + Connect_Bonsai_Camera(obj,'next_trial'); % Update the Metrics Calculated SessionPerformanceSection(obj, 'evaluate'); % Do any updates in the protocol that need doing: @@ -296,7 +299,7 @@ PokesPlotSection(obj, 'close'); ParamsSection(obj, 'close'); StimulusSection(obj,'close'); - + Connect_Bonsai_Camera(obj,'close'); if exist('myfig', 'var') && isa(myfig, 'SoloParamHandle') && ishandle(value(myfig)) %#ok delete(value(myfig)); end diff --git a/Protocols/@Arpit_CentrePokeTraining/Connect_Bonsai_Camera.m b/Protocols/@Arpit_CentrePokeTraining/Connect_Bonsai_Camera.m index 11ee4c20..4b749eda 100644 --- a/Protocols/@Arpit_CentrePokeTraining/Connect_Bonsai_Camera.m +++ b/Protocols/@Arpit_CentrePokeTraining/Connect_Bonsai_Camera.m @@ -20,8 +20,18 @@ function Connect_Bonsai_Camera(obj,action) stopCommand = "stop"; % Use string type. MUST MATCH Bonsai Filter Value camera_command_address = "/camera"; % Use string type. MUST MATCH Bonsai Address Value recording_command_address = "/record"; % Use string type. MUST MATCH Bonsai Address Value -bonsai_path = 'C:\Users\Turin\Downloads\Bonsai\Bonsai.exe'; % Path of Bonsai App -file_path = 'C:\Users\Turin\Desktop\bonsai_script.bonsai'; % Path for .bonsai file + +% bonsai_path = 'C:\Users\Turin\Downloads\Bonsai\Bonsai.exe'; % Path of Bonsai App +scriptFullPath = mfilename('fullpath'); % Path of running the current script +scriptDirectory = fileparts(scriptFullPath); +bonsai_workflow_Path = fullfile(scriptDirectory,'Bonsai','Camera_Control.bonsai'); +foundworkflow = exist("bonsai_workflow_Path",'file'); +% if ~foundworkflow +% warning('could not find bonsai executable, please insert it manually'); +% [fname fpath] = uigetfile( '*.exe', 'Provide the path to Bonsai executable'); +% pathout = fullfile(fpath, fname); +% end +% file_path = 'C:\Users\Turin\Desktop\bonsai_script.bonsai'; % Path for .bonsai file %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -38,9 +48,12 @@ function Connect_Bonsai_Camera(obj,action) % Now that we have created the UPD connection, we can send commands % over OSC. Before that we need to open the Bonsai App + + % bonsai_path = bonsaiPath(32); + % command = sprintf('"%s" "%s" --start', bonsai_path, bonsai_file_path); + % system([command, ' &']); - command = sprintf('"%s" "%s" --start', bonsai_path, file_path); - system([command, ' &']); + runBonsaiWorkflow(bonsai_workflow_Path); % Before starting the streaming of Camera, I need to send the % file directory for saving the file otherwise Bonsai can run into @@ -48,31 +61,37 @@ function Connect_Bonsai_Camera(obj,action) % bonsai and that can conflict with file already present there. oscMsg_file_directory = createOSCMessage(recording_command_address, [value(Video_Saving_Folder) '\Trial.avi']); - write(udpSender, oscMsg_file_directory, "uint8", bonsaiComputerIP,bonsaiUdpPort); + % write(udpSender, oscMsg_file_directory, "uint8", bonsaiComputerIP,bonsaiUdpPort); % OSC message to start the camera oscMsg_Camera_start = createOSCMessage(camera_command_address, startCommand); % the command to send message to Bonsai write(udpSender, oscMsg_Camera_start, "uint8", bonsaiComputerIP,bonsaiUdpPort); + pause(2); + write(udpSender, oscMsg_Camera_start, "uint8", bonsaiComputerIP,bonsaiUdpPort); + pause(5); % NOTE: Ideally I should start saving the trials once the experimenter presses % Run either on dispatcher or Runrats. But, I dont want to make the changes there % so would start recording as soon as the protocol is loaded and camera starts streaming + write(udpSender, oscMsg_file_directory, "uint8", bonsaiComputerIP,bonsaiUdpPort); + case 'next_trial' % in this I send a command to bonsai so that it creates a new file % for each trial - filename = sprintf('%s//%s//%s',work_dir,exp_name,rat_name); - oscMsg_filename = createOSCMessage(recording_command_address, filename); - write(value(udpSender), oscMsg_filename, "uint8", bonsaiComputerIP,bonsaiUdpPort); + oscMsg_file_directory = createOSCMessage(recording_command_address, [value(Video_Saving_Folder) '\Trial.avi']); + write(value(UDPSender), oscMsg_file_directory, "uint8", bonsaiComputerIP,bonsaiUdpPort); case 'close' % this would stop the workflow oscMsg_Camera_stop = createOSCMessage("/camera", stopCommand); - write(value(udpSender), oscMsg_Camera_stop, "uint8", bonsaiComputerIP,bonsaiUdpPort); + write(value(UDPSender), oscMsg_Camera_stop, "uint8", bonsaiComputerIP,bonsaiUdpPort); + + delete(value(UDPSender)); % delete the UDP Port % this is to kill the Bonsai app system('taskkill /F /IM Bonsai.exe'); @@ -125,12 +144,12 @@ function runBonsaiWorkflow(workflowPath, addOptArray, bonsaiExePath, external) %% check the input switch nargin case 1 - bonsaiExePath = bonsaiPath(64); + bonsaiExePath = bonsaiPath(32); addOptArray = ''; addFlag = 0; external = 1; case 2 - bonsaiExePath = bonsaiPath(64); + bonsaiExePath = bonsaiPath(32); addFlag = 1; external = 1; case 3 @@ -138,7 +157,7 @@ function runBonsaiWorkflow(workflowPath, addOptArray, bonsaiExePath, external) external = 1; case 4 if isempty(bonsaiExePath) - bonsaiExePath = bonsaiPath(64); + bonsaiExePath = bonsaiPath(32); end addFlag = 1; @@ -199,18 +218,20 @@ function runBonsaiWorkflow(workflowPath, addOptArray, bonsaiExePath, external) bonsaiEXE = 'Bonsai.exe'; end -dirs = {'appdata', 'localappdata', 'programfiles', 'programfiles(x86)',... - 'C:\Software\Bonsai.Packages\Externals\Bonsai\Bonsai.Editor\bin\x86\Release',... - 'C:\Software\Bonsai.Packages\Externals\Bonsai\Bonsai.Editor\bin\x64\Release'... - }; +dirs = {'appdata', 'localappdata', 'programfiles', 'programfiles(x86)','USERPROFILE'}; foundBonsai = 0; dirIDX = 1; while ~foundBonsai && (dirIDX <= length(dirs)) - pathout = fullfile(getenv(dirs{dirIDX}),'Bonsai', bonsaiEXE); + pathout = fullfile(getenv(dirs{dirIDX}),'Bonsai', bonsaiEXE); foundBonsai = exist(pathout, 'file'); dirIDX = dirIDX +1; end +if ~foundBonsai + pathout = fullfile(getenv(dirs{5}),'Desktop',bonsaiEXE); + foundBonsai = exist(pathout, 'file'); +end + if ~foundBonsai warning('could not find bonsai executable, please insert it manually'); [fname fpath] = uigetfile( '*.exe', 'Provide the path to Bonsai executable'); diff --git a/Protocols/@Arpit_CentrePokeTraining/StimulusSection.m b/Protocols/@Arpit_CentrePokeTraining/StimulusSection.m index 0beb8a25..1629884e 100644 --- a/Protocols/@Arpit_CentrePokeTraining/StimulusSection.m +++ b/Protocols/@Arpit_CentrePokeTraining/StimulusSection.m @@ -341,7 +341,7 @@ cla(value(ax)) - StimuliDistribution_plot(value(ax),[stim_min_log, value(boundary), stim_max_log], ... + StimuliDistribution_plot(value(ax),[stim_min_log, value(boundary), stim_max_log], Rule, ... value(Prob_Dist_Left),value(mean_Left),value(sigma_Left),[edge_min_left edge_max_left], ... value(Prob_Dist_Right),value(mean_Right),value(sigma_Right),[edge_min_right edge_max_right]); @@ -349,7 +349,7 @@ xline([stim_min_log value(boundary) stim_max_log],'-',{'Stim Min','Boundary','Stim Max'}); ylabel('log_e A','FontSize',16,'FontName','Cambria Math'); - set(ax,'Fontsize',15) + set(value(ax),'Fontsize',15) xlabel('Sound Categorization','FontSize',16,'FontName','Cambria Math') % plot(xd,stim_min_log,'s','MarkerSize',15,'MarkerEdgeColor',[0 0 0],'LineWidth',2) diff --git a/Protocols/@Arpit_CentrePokeTraining/private/StimuliDistribution_plot.m b/Protocols/@Arpit_CentrePokeTraining/private/StimuliDistribution_plot.m index 9214f16e..e6723f6c 100644 --- a/Protocols/@Arpit_CentrePokeTraining/private/StimuliDistribution_plot.m +++ b/Protocols/@Arpit_CentrePokeTraining/private/StimuliDistribution_plot.m @@ -1,47 +1,82 @@ -function StimuliDistribution_plot(ax,plot_x_range, distribution_left,mean_left,sigma_left,... +function StimuliDistribution_plot(ax,plot_x_range, Rule, distribution_left,mean_left,sigma_left,... range_left,distribution_right,mean_right,sigma_right,range_right) -x_left = plot_x_range(1):.01:plot_x_range(2); -x_right = plot_x_range(2):.01:plot_x_range(3); +if strcmp(Rule,'S1>S_boundary Right') + x_left = plot_x_range(1):.01:plot_x_range(2); + x_right = plot_x_range(2):.01:plot_x_range(3); +else + x_right = plot_x_range(1):.01:plot_x_range(2); + x_left = plot_x_range(2):.01:plot_x_range(3); +end switch distribution_left case 'Uniform' + + pdf_funct_left = makedist('Uniform','lower',range_left(1),'upper',range_left(2)); + pd_left = pdf(pdf_funct_left,x_left); + + case 'Exponential' - pdf_funct = makedist('Uniform','lower',range_left(1),'upper',range_left(2)); - pd_left = pdf(pdf_funct,x_left); + lambda = 2.153 * (range_left(2) - range_left(1)); + lambda = 1/lambda; + if range_left(1) == plot_x_range(2) % decreasing function + Z = exp(-lambda * range_left(1)) - exp(-lambda * range_left(2)); % Define the normalization constant + pdf_func_left = @(x) (lambda * exp(-lambda * x)) / Z; % Normalized truncated exponential PDF + else + Z = exp(lambda * range_left(2)) - exp(lambda * range_left(1));% Normalization constant + A = lambda / Z; + pdf_func_left = @(x) A * exp(lambda * x); + end + + pd_left = pdf_func_left(x_left); + + case 'Normal' + + pdf_funct_left = makedist('Normal','mu',mean_left,'sigma',sigma_left); + pd_left = pdf(pdf_funct_left,x_left); case 'Half Normal' pdf_funct_init = @(x, A) A * exp(- (range_left(2) - x).^2 / (2 * sigma_left^2)); % Define the Half-Normal PDF (Peak at max value) A_den = integral(@(x) exp(- (range_left(2) - x).^2 / (2 * sigma_left^2)), range_left(1), range_left(2)); % Compute Normalization Constant A - A_sol = 1 / A_den; % Ensuring total probability integrates to 1 - pdf_funct = @(x) A_sol * exp(- (range_left(2) - x).^2 / (2 * sigma_left^2)); % Define the final normalized PDF function - pd_left = pdf_funct(x_left); + A_sol = 1 / A_den; % Ensuring total probability integrates to 1 + pdf_funct_left = @(x) A_sol * exp(- (range_left(2) - x).^2 / (2 * sigma_left^2)); % Define the final normalized PDF function + pd_left = pdf_funct_left(x_left); - case 'Normal' - - pdf_funct = makedist('Normal','mu',mean_left,'sigma',sigma_left); - pd_left = pdf(pdf_funct,x_left); + case 'Anti Exponential' - case 'Sinusoidal' + lambda = 2.153 * (range_left(2) - range_left(1)); + + if range_left(2) == plot_x_range(2) % decreasing function + Z = exp(-lambda * range_left(1)) - exp(-lambda * range_left(2)); % Define the normalization constant + pdf_func_left = @(x) (lambda * exp(-lambda * x)) / Z; % Normalized truncated exponential PDF + else + Z = exp(lambda * range_left(2)) - exp(lambda * range_left(1));% Normalization constant + A = lambda / Z; + pdf_func_left = @(x) A * exp(lambda * x); + end - pdf_funct_init = @(x) sin((pi / 2) * ((x - range_left(1)) / (range_left(2) - range_left(1)))); - A_val = 1 / integral(pdf_funct_init, range_left(1), range_left(2)); % Compute Normalization Constant A - pdf_funct = @(x) A_val * sin((pi / 2) * ((x - range_left(1)) / (range_left(2) - range_left(1)))); % the normalized PDF - pd_left = pdf_funct(x_left); + pd_left = pdf_func_left(x_left); case 'Anti Half Normal' - pdf_funct = makedist('HalfNormal','mu',mean_left,'sigma',sigma_left); - pd_left = pdf(pdf_funct,x_left); + pdf_funct_left = makedist('HalfNormal','mu',mean_left,'sigma',sigma_left); + pd_left = pdf(pdf_funct_left,x_left); case 'Anti Sinusoidal' + pdf_funct_init = @(x) sin((pi / 2) * ((x - range_left(1)) / (range_left(2) - range_left(1)))); + A_val = 1 / integral(pdf_funct_init, range_left(1), range_left(2)); % Compute Normalization Constant A + pdf_funct_left = @(x) A_val * sin((pi / 2) * ((x - range_left(1)) / (range_left(2) - range_left(1)))); % the normalized PDF + pd_left = pdf_funct_left(x_left); + + case 'Sinusoidal' + pdf_funct_init = @(x) sin((pi / 2) * ((range_left(2) - x) / (range_left(2) - range_left(1)))); % Define the PDF (Peak at min value) A_val = 1 / integral(pdf_funct_init, range_left(1), range_left(2)); % Compute Normalization Constant A - pdf_funct = @(x) A_val * sin((pi / 2) * ((range_left(2) - x) / (range_left(2) - range_left(1)))); % Define the normalized PDF - pd_left = pdf_funct(x_left); + pdf_funct_left = @(x) A_val * sin((pi / 2) * ((range_left(2) - x) / (range_left(2) - range_left(1)))); % Define the normalized PDF + pd_left = pdf_funct_left(x_left); case 'Monotonic Increase' @@ -50,50 +85,83 @@ function StimuliDistribution_plot(ax,plot_x_range, distribution_left,mean_left,s switch distribution_right case 'Uniform' - - pdf_funct = makedist('Uniform','lower',range_right(1),'upper',range_right(2)); - pd_right = pdf(pdf_funct,x_right); - case 'Half Normal' + pdf_funct_right = makedist('Uniform','lower',range_right(1),'upper',range_right(2)); + pd_right = pdf(pdf_funct_right,x_right); - pdf_funct = makedist('HalfNormal','mu',mean_right,'sigma',sigma_right); - pd_right = pdf(pdf_funct,x_right); + case 'Exponential' + + lambda = 2.153 * (range_right(2) - range_right(1)); + lambda = 1/lambda; + if range_right(1) == plot_x_range(2) % decreasing function + Z = exp(-lambda * range_right(1)) - exp(-lambda * range_right(2)); % Define the normalization constant + pdf_func_right = @(x) (lambda * exp(-lambda * x)) / Z; % Normalized truncated exponential PDF + else + Z = exp(lambda * range_right(2)) - exp(lambda * range_right(1));% Normalization constant + A = lambda / Z; + pdf_func_right = @(x) A * exp(lambda * x); + end + + pd_right = pdf_func_right(x_right); case 'Normal' - pdf_funct = makedist('Normal','mu',mean_right,'sigma',sigma_right); - pd_right = pdf(pdf_funct,x_right); + pdf_funct_right = makedist('Normal','mu',mean_right,'sigma',sigma_right); + pd_right = pdf(pdf_funct_right,x_right); - case 'Sinusoidal' + case 'Half Normal' - pdf_funct_init = @(x) sin((pi / 2) * ((range_right(2) - x) / (range_right(2) - range_right(1)))); % Define the PDF (Peak at min value) - A_val = 1 / integral(pdf_funct_init, range_right(1), range_right(2)); % Compute Normalization Constant A - pdf_funct = @(x) A_val * sin((pi / 2) * ((range_right(2) - x) / (range_right(2) - range_right(1)))); % Define the normalized PDF - pd_right = pdf(pdf_funct,x_right); + pdf_funct_right = makedist('HalfNormal','mu',mean_right,'sigma',sigma_right); + pd_right = pdf(pdf_funct_right,x_right); case 'Anti Half Normal' pdf_funct_init = @(x, A) A * exp(- (range_right(2) - x).^2 / (2 * sigma_right^2)); % Define the Half-Normal PDF (Peak at max value) A_den = integral(@(x) exp(- (range_right(2) - x).^2 / (2 * sigma_right^2)), range_right(1), range_right(2)); % Compute Normalization Constant A - A_sol = 1 / A_den; % Ensuring total probability integrates to 1 - pdf_funct = @(x) A_sol * exp(- (range_right(2) - x).^2 / (2 * sigma_right^2)); % Define the final normalized PDF function - pd_right = pdf(pdf_funct,x_right); + A_sol = 1 / A_den; % Ensuring total probability integrates to 1 + pdf_funct_right = @(x) A_sol * exp(- (range_right(2) - x).^2 / (2 * sigma_right^2)); % Define the final normalized PDF function + pd_right = pdf_funct_right(x_right); + + case 'Anti Exponential' + + lambda = 2.153 * (range_right(2) - range_right(1)); + + if range_right(2) == plot_x_range(2) % decreasing function + Z = exp(-lambda * range_right(1)) - exp(-lambda * range_right(2)); % Define the normalization constant + pdf_func_right = @(x) (lambda * exp(-lambda * x)) / Z; % Normalized truncated exponential PDF + else + Z = exp(lambda * range_right(2)) - exp(lambda * range_right(1));% Normalization constant + A = lambda / Z; + pdf_func_right = @(x) A * exp(lambda * x); + end + + pd_right = pdf_func_right(x_right); case 'Anti Sinusoidal' - pdf_funct_init = @(x) sin((pi / 2) * ((x - range_right(1)) / (range_right(2) - range_right(1)))); + pdf_funct_init = @(x) sin((pi / 2) * ((range_right(2) - x) / (range_right(2) - range_right(1)))); % Define the PDF (Peak at min value) A_val = 1 / integral(pdf_funct_init, range_right(1), range_right(2)); % Compute Normalization Constant A - pdf_funct = @(x) A_val * sin((pi / 2) * ((x - range_right(1)) / (range_right(2) - range_right(1)))); % the normalized PDF - pd_right = pdf(pdf_funct,x_right); + pdf_funct_right = @(x) A_val * sin((pi / 2) * ((range_right(2) - x) / (range_right(2) - range_right(1)))); % Define the normalized PDF + pd_right = pdf_funct_right(x_right); - case 'Monotonic Increase' -end + case 'Sinusoidal' -% Plot the distribution + pdf_funct_init = @(x) sin((pi / 2) * ((x - range_right(1)) / (range_right(2) - range_right(1)))); + A_val = 1 / integral(pdf_funct_init, range_right(1), range_right(2)); % Compute Normalization Constant A + pdf_funct_right = @(x) A_val * sin((pi / 2) * ((x - range_right(1)) / (range_right(2) - range_right(1)))); % the normalized PDF + pd_right = pdf_funct_right(x_right); -plot_x = [x_left,x_right]; -plot_y = [pd_left,pd_right]; +end + +% Plot the distribution +if strcmp(Rule,'S1>S_boundary Right') + plot_x = [x_left,x_right]; + plot_y = [pd_left,pd_right]; +else + plot_x = [x_right,x_left]; + plot_y = [pd_right,pd_left]; +end plot(ax,plot_x,plot_y,'b','LineWidth', 2); From 7131b5c67b3bd415e503805f491403f90c24aa57 Mon Sep 17 00:00:00 2001 From: viktorpm Date: Wed, 23 Apr 2025 11:10:07 +0100 Subject: [PATCH 059/164] camera debug --- .../Arpit_CentrePokeTraining.asv | 346 ++++++++ .../Arpit_CentrePokeTraining.m | 5 +- .../Connect_Bonsai_Camera.asv | 246 ++++++ .../Connect_Bonsai_Camera.m | 6 + .../Arpit_SoundCalibration.asv | 769 ++++++++++++++++++ .../Arpit_SoundCalibration.m | 155 +++- 6 files changed, 1519 insertions(+), 8 deletions(-) create mode 100644 Protocols/@Arpit_CentrePokeTraining/Arpit_CentrePokeTraining.asv create mode 100644 Protocols/@Arpit_CentrePokeTraining/Connect_Bonsai_Camera.asv create mode 100644 Protocols/@Arpit_SoundCalibration/Arpit_SoundCalibration.asv diff --git a/Protocols/@Arpit_CentrePokeTraining/Arpit_CentrePokeTraining.asv b/Protocols/@Arpit_CentrePokeTraining/Arpit_CentrePokeTraining.asv new file mode 100644 index 00000000..e12319a4 --- /dev/null +++ b/Protocols/@Arpit_CentrePokeTraining/Arpit_CentrePokeTraining.asv @@ -0,0 +1,346 @@ +% Arpit_CentrePokeTraining protocol +% Arpit, 12 March 2025 + +function [obj] = Arpit_CentrePokeTraining(varargin) + +% Default object is of our own class (mfilename); +% we inherit only from Plugins + +obj = class(struct, mfilename, pokesplot2, saveload, sessionmodel2, soundmanager, soundui, ... + water, comments, soundtable, sqlsummary); + +%--------------------------------------------------------------- +% BEGIN SECTION COMMON TO ALL PROTOCOLS, DO NOT MODIFY +%--------------------------------------------------------------- + +% If creating an empty object, return without further ado: +if nargin==0 || (nargin==1 && ischar(varargin{1}) && strcmp(varargin{1}, 'empty')) + return; +end + +if isa(varargin{1}, mfilename) % If first arg is an object of this class itself, we are + % Most likely responding to a callback from + % a SoloParamHandle defined in this mfile. + if length(varargin) < 2 || ~ischar(varargin{2}) + error(['If called with a "%s" object as first arg, a second arg, a ' ... + 'string specifying the action, is required\n']); + else + action = varargin{2}; varargin = varargin(3:end); %#ok + end +else % Ok, regular call with first param being the action string. + action = varargin{1}; varargin = varargin(2:end); %#ok +end + +GetSoloFunctionArgs(obj); + + +%--------------------------------------------------------------- +% END OF SECTION COMMON TO ALL PROTOCOLS, MODIFY AFTER THIS LINE +%--------------------------------------------------------------- + +% ---- From here on is where you can put the code you like. +% +% Your protocol will be called, at the appropriate times, with the +% following possible actions: +% +% 'init' To initialize -- make figure windows, variables, etc. +% +% 'update' Called periodically within a trial +% +% 'prepare_next_trial' Called when a trial has ended and your protocol is expected +% to produce the StateMachine diagram for the next trial; +% i.e., somewhere in your protocol's response to this call, it +% should call "dispatcher('send_assembler', sma, +% prepare_next_trial_set);" where sma is the +% StateMachineAssembler object that you have prepared and +% prepare_next_trial_set is either a single string or a cell +% with elements that are all strings. These strings should +% correspond to names of states in sma. +% Note that after the prepare_next_trial call, further +% events may still occur while your protocol is thinking, +% before the new StateMachine diagram gets sent. These events +% will be available to you when 'state0' is called on your +% protocol (see below). +% +% 'trial_completed' Called when the any of the prepare_next_trial set +% of states is reached. +% +% 'close' Called when the protocol is to be closed. +% +% +% VARIABLES THAT DISPATCHER WILL ALWAYS INSTANTIATE FOR YOU AS READ_ONLY +% GLOBALS IN YOUR PROTOCOL: +% +% n_done_trials How many trials have been finished; when a trial reaches +% one of the prepare_next_trial states for the first +% time, this variable is incremented by 1. +% +% n_started_trials How many trials have been started. This variable gets +% incremented by 1 every time the state machine goes +% through state 0. +% +% parsed_events The result of running disassemble.m, with the +% parsed_structure flag set to 1, on all events from the +% start of the current trial to now. +% +% latest_parsed_events The result of running disassemble.m, with the +% parsed_structure flag set to 1, on all new events from +% the last time 'update' was called to now. +% +% raw_events All the events obtained in the current trial, not parsed +% or disassembled, but raw as gotten from the State +% Machine object. +% +% current_assembler The StateMachineAssembler object that was used to +% generate the State Machine diagram in effect in the +% current trial. +% +% Trial-by-trial history of parsed_events, raw_events, and +% current_assembler, are automatically stored for you in your protocol by +% dispatcher.m. + +switch action + + %% init + case 'init' + + hackvar = 10; SoloFunctionAddVars('SessionModel', 'ro_args', 'hackvar'); %#ok + SoloParamHandle(obj, 'myfig', 'saveable', 0); myfig.value = figure; + + % Make the title of the figure be the protocol name, and if someone tries + % to close this figure, call dispatcher's close_protocol function, so it'll know + % to take it off the list of open protocols. + name = mfilename; + set(value(myfig), 'Name', name, 'Tag', name, ... + 'closerequestfcn', 'dispatcher(''close_protocol'')', 'MenuBar', 'none'); + % At this point we have one SoloParamHandle, myfig + % Let's put the figure where we want it and give it a reasonable size: + set(value(myfig), 'Position', [485 144 850 680]); + + SoloParamHandle(obj, 'nsessions_healthy_number_of_pokes', 'value', 0, 'save_with_settings', 1); + SoloParamHandle(obj, 'post_DelComp_protocol', 'value', '', 'save_with_settings', 1); + SoloParamHandle(obj, 'post_DelComp_settings_filename', 'value', '', 'save_with_settings', 1); + + SoloParamHandle(obj, 'violation_history', 'value', []); + DeclareGlobals(obj, 'ro_args', {'violation_history'}); + SoloFunctionAddVars('ParamsSection', 'rw_args', 'violation_history'); + + SoloParamHandle(obj, 'timeout_history', 'value', []); + DeclareGlobals(obj, 'ro_args', {'timeout_history'}); + SoloFunctionAddVars('ParamsSection', 'rw_args', 'timeout_history'); + + SoloParamHandle(obj, 'stimulus_history', 'value', []); + DeclareGlobals(obj, 'ro_args', {'stimulus_history'}); + SoloFunctionAddVars('StimulusSection', 'rw_args', 'stimulus_history'); + + SoloParamHandle(obj, 'hit_history', 'value', []); + DeclareGlobals(obj, 'ro_args', {'hit_history'}); + SoloFunctionAddVars('SideSection', 'rw_args', 'hit_history'); + + SoundManagerSection(obj, 'init'); + x = 5; y = 5; % Initial position on main GUI window + [x, y] = SavingSection(obj, 'init', x, y); + + %% slow ramp up of water amount + %%the water volume is controlled by a 5-parameter logistic function: WaterAmount(t) = maxasymp + (minasymp/(1+(t/inflp)^slp).^assym) + NumeditParam(obj, 'maxasymp', 38, x,y,'label','maxasymp','TooltipString',... + 'the water volume is controlled by a 5-parameter logistic function: WaterAmount(trialnum) = maxasymp + (minasymp/(1+(trialnum/inflp)^slp).^assym)'); + next_row(y); + NumeditParam(obj, 'slp', 3, x,y,'label','slp','TooltipString','Water Modulation: Slope of the logistic function'); + next_row(y); + NumeditParam(obj, 'inflp', 350, x,y,'label','inflp','TooltipString','Water Modulation: concentration at the inflection point'); + next_row(y); + NumeditParam(obj, 'minasymp', -21, x,y,'label','inflp','TooltipString','Water Modulation: minimum asymptote'); + next_row(y); + NumeditParam(obj, 'assym', 0.7, x,y,'label','assym','TooltipString','Water Modulation: asymmetry factor'); + next_row(y); + DispParam(obj, 'trial_1', 0, x, y, 'TooltipString', 'uL on first trial'); + next_row(y); + DispParam(obj, 'trial_150', 0, x, y, 'TooltipString', 'uL on trial 150'); + next_row(y); + DispParam(obj, 'trial_300', 0, x, y, 'TooltipString', 'uL on trial 300'); + next_row(y); + set_callback({maxasymp;slp;inflp;minasymp;assym}, {mfilename, 'change_water_modulation_params'}); + feval(mfilename, obj, 'change_water_modulation_params'); + + %AthenaSMA changed to SoundCatSMA (From AthenaDelayComp) + SoloFunctionAddVars('ParamsSection', 'ro_args', ... + {'maxasymp';'slp';'inflp';'minasymp';'assym'}); + [x, y] = WaterValvesSection(obj, 'init', x, y); + + % For plotting with the pokesplot plugin, we need to tell it what + % colors to plot with: + my_state_colors = Arpit_CentrePokeTrainingSMA(obj, 'get_state_colors'); + % In pokesplot, the poke colors have a default value, so we don't need + % to specify them, but here they are so you know how to change them. + my_poke_colors = struct( ... + 'L', 0.6*[1 0.66 0], ... + 'C', [0 0 0], ... + 'R', 0.9*[1 0.66 0]); + + [x, y] = PokesPlotSection(obj, 'init', x, y, ... + struct('states', my_state_colors, 'pokes', my_poke_colors)); next_row(y); + + [x, y] = CommentsSection(obj, 'init', x, y); + % [x, y] = PunishmentSection(obj, 'init', x, y); %#ok + + next_column(x); y=5; + [x, y] = SessionPerformanceSection(obj, 'init', x, y); + [x, y] = ParamsSection(obj, 'init', x, y); %#ok + + [x, y] = SoundSection(obj,'init',x,y); + [stage_fig_x,stage_fig_y] = Training_ParamsSection(obj, 'init', x, y); + SoloParamHandle(obj, 'stage_fig_x', 'value', stage_fig_x); + SoloFunctionAddVars('ParamsSection', 'rw_args', 'stage_fig_x'); + SoloParamHandle(obj, 'stage_fig_y', 'value', stage_fig_y); + SoloFunctionAddVars('ParamsSection', 'rw_args', 'stage_fig_y'); + + figpos = get(double(gcf), 'Position'); + [expmtr, rname]=SavingSection(obj, 'get_info'); + HeaderParam(obj, 'prot_title', [mfilename ': ' expmtr ', ' rname], x, y, 'position', [10 figpos(4)-25, 800 20]); + + [x, y] = StimulusSection(obj,'init',x,y); + + Arpit_CentrePokeTrainingSMA(obj, 'init'); + + next_row(y); next_row(y); + SessionDefinition(obj, 'init', x, y, value(myfig)); next_row(y, 2); %#ok + + %% Before preparing the trial, start with the Bonsai app to control the USB based Camera + % Declare the folder location for saving the video files + current_dir = cd; + ratter_dir = extractBefore(current_dir,'ratter'); + main_dir_video = [ratter_dir 'ratter_Videos']; + date_str = regexprep(char(datetime('today','Format','yyyy-MM-dd')), '[^0-9]', ''); + video_foldername = sprintf('video_@%s_%s_%s_%s',name,expmtr,rname,date_str); + rat_dir = sprintf('%s\\%s\\%s',main_dir_video,expmtr,rname); + video_save_dir = sprintf('%s\\%s\\%s\\%s',main_dir_video,expmtr,rname,video_foldername); + % We have the general structure of folder save location, now need to + % check if there is any other folder for same date. We will add a + % alphabet in the end based upon the no. of files present. + if exist(rat_dir,'dir') == 7 + folderNames_rat_dir = {listing(find([listing.isdir])).name}; + folderNames_rat_dir = folderNames_rat_dir(~ismember(folderNames_rat_dir,{'.','..'})); % Remove the '.' and '..' entries (current and parent directories) + sessions_today = length(find(contains(folderNames_rat_dir,video_foldername))); % number of folders containing the video foldername + video_save_dir = [video_save_dir char(sessions_today + 97)]; + else + video_save_dir = [video_save_dir char(97)]; + end + mkdir(video_save_dir); + SoloParamHandle(obj, 'Video_Saving_Folder', 'value', video_save_dir); + SoloFunctionAddVars('Connect_Bonsai_Camera', 'ro_args', ... + {'Video_Saving_Folder'}); + Connect_Bonsai_Camera(obj,'init'); + + %% + + feval(mfilename, obj, 'prepare_next_trial'); + + %% change_water_modulation_params + case 'change_water_modulation_params' + display_guys = [1 150 300]; + for i=1:numel(display_guys) + t = display_guys(i); + + myvar = eval(sprintf('trial_%d', t)); + myvar.value = maxasymp + (minasymp/(1+(t/inflp)^slp).^assym); + end + + %% prepare next trial + case 'prepare_next_trial' + + ParamsSection(obj, 'prepare_next_trial'); + % Run SessionDefinition *after* ParamsSection so we know whether the trial was a violation or not + + % push_helper_vars_tosql(obj,n_done_trials); + + SessionDefinition(obj, 'next_trial'); + + StimulusSection(obj,'prepare_next_trial'); + SoundManagerSection(obj, 'send_not_yet_uploaded_sounds'); + [sma, prepare_next_trial_states] = Arpit_CentrePokeTrainingSMA(obj, 'prepare_next_trial'); + + % Default behavior of following call is that every 20 trials, the data + % gets saved, not interactive, no commit to CVS. + SavingSection(obj, 'autosave_data'); + + CommentsSection(obj, 'clear_history'); % Make sure we're not storing unnecessary history + if n_done_trials==1 % Auto-append date for convenience. + CommentsSection(obj, 'append_date'); CommentsSection(obj, 'append_line', ''); + end + + if n_done_trials==1 + [expmtr, rname]=SavingSection(obj, 'get_info'); + prot_title.value=[mfilename ' on rig ' get_hostname ' : ' expmtr ', ' rname '. Started at ' datestr(now, 'HH:MM')]; + end + + % try + % send_n_done_trials(obj); + % end + + %% trial_completed + case 'trial_completed' + % Change the video trial + Connect_Bonsai_Camera(obj,'next_trial'); + % Update the Metrics Calculated + SessionPerformanceSection(obj, 'evaluate'); + % Do any updates in the protocol that need doing: + feval(mfilename, 'update'); + % And PokesPlot needs completing the trial: + PokesPlotSection(obj, 'trial_completed'); + + %% update + case 'update' + PokesPlotSection(obj, 'update'); + + + %% close + case 'close' + PokesPlotSection(obj, 'close'); + ParamsSection(obj, 'close'); + StimulusSection(obj,'close'); + Connect_Bonsai_Camera(obj,'close'); + if exist('myfig', 'var') && isa(myfig, 'SoloParamHandle') && ishandle(value(myfig)) %#ok + delete(value(myfig)); + end + delete_sphandle('owner', ['^@' class(obj) '$']); + + + %% end_session + case 'end_session' + prot_title.value = [value(prot_title) ', Ended at ' datestr(now, 'HH:MM')]; + + + %% pre_saving_settings + case 'pre_saving_settings' + + StimulusSection(obj,'hide'); + SessionDefinition(obj, 'run_eod_logic_without_saving'); + perf = SessionPerformanceSection(obj, 'evaluate'); + cp_durs = ParamsSection(obj, 'get_cp_history'); + + [stim1dur] = ParamsSection(obj,'get_stimdur_history'); + +% CommentsSection(obj, 'append_line', ... +% sprintf(['ntrials = %d, violations = %.2f, timeouts=%.2f, hits = %.2f\n', ... +% 'pre-Go cue went from %.3f to %.3f (delta=%.3f)\n', ... +% 'Low = %.2f, High = %.2f'], ... +% perf(1), perf(2), perf(3), perf(6), cp_durs(1), cp_durs(end), cp_durs(end)-cp_durs(1), classperf(1),classperf(2))); + + pd.hits=hit_history(:); + pd.sides=previous_sides(:); + pd.viols=violation_history(:); + pd.timeouts=timeout_history(:); + pd.cp_durs=cp_durs(:); + pd.stim1dur=stim1dur(:); + + + sendsummary(obj,'protocol_data',pd); + + %% otherwise + otherwise + warning('Unknown action! "%s"\n', action); +end + +return; + diff --git a/Protocols/@Arpit_CentrePokeTraining/Arpit_CentrePokeTraining.m b/Protocols/@Arpit_CentrePokeTraining/Arpit_CentrePokeTraining.m index 36eb8300..9f9cc839 100644 --- a/Protocols/@Arpit_CentrePokeTraining/Arpit_CentrePokeTraining.m +++ b/Protocols/@Arpit_CentrePokeTraining/Arpit_CentrePokeTraining.m @@ -218,7 +218,8 @@ % We have the general structure of folder save location, now need to % check if there is any other folder for same date. We will add a % alphabet in the end based upon the no. of files present. - if exist('rat_dir','dir') == 7 + if exist(rat_dir,'dir') == 7 + listing = dir(rat_dir); folderNames_rat_dir = {listing(find([listing.isdir])).name}; folderNames_rat_dir = folderNames_rat_dir(~ismember(folderNames_rat_dir,{'.','..'})); % Remove the '.' and '..' entries (current and parent directories) sessions_today = length(find(contains(folderNames_rat_dir,video_foldername))); % number of folders containing the video foldername @@ -309,7 +310,7 @@ %% end_session case 'end_session' prot_title.value = [value(prot_title) ', Ended at ' datestr(now, 'HH:MM')]; - + Connect_Bonsai_Camera(obj,'stop'); % Stopping the cameras %% pre_saving_settings case 'pre_saving_settings' diff --git a/Protocols/@Arpit_CentrePokeTraining/Connect_Bonsai_Camera.asv b/Protocols/@Arpit_CentrePokeTraining/Connect_Bonsai_Camera.asv new file mode 100644 index 00000000..4ec91744 --- /dev/null +++ b/Protocols/@Arpit_CentrePokeTraining/Connect_Bonsai_Camera.asv @@ -0,0 +1,246 @@ +function Connect_Bonsai_Camera(obj,action) + +GetSoloFunctionArgs(obj); + + +% C:\RatterVideos\ExpName\RateName\Videos_Protocolname_ExpName_RatName_date +% (the same format as Data file) + +%%%%%%%%% DO NOT CHANGE THEM UNLESS YOU MAKE THE SAME CHANGES IN BONSAI %%%%%%%%% + +% UDP Connection Parameters to connect to Bonsai +bonsaiComputerIP = '127.0.0.1'; % IP address of the PC running Bonsai (use '127.0.0.1' if same PC) +bonsaiComputer_name = 'Receiver'; +bonsaiUdpPort = 9090; % Port configured in Bonsai's UdpReceiver +% Sending the Command to Turn on the Camera. Remember to use this +% as in Bonsai I am using the address as /camera and using +% condition to compare the arriving message. The message could only +% be 'start' or 'stop' with '/camera' address for bonsai to react +startCommand = "start"; % Use string type. MUST MATCH Bonsai Filter Value +stopCommand = "stop"; % Use string type. MUST MATCH Bonsai Filter Value +camera_command_address = "/camera"; % Use string type. MUST MATCH Bonsai Address Value +recording_command_address = "/record"; % Use string type. MUST MATCH Bonsai Address Value + +% bonsai_path = 'C:\Users\Turin\Downloads\Bonsai\Bonsai.exe'; % Path of Bonsai App +scriptFullPath = mfilename('fullpath'); % Path of running the current script +scriptDirectory = fileparts(scriptFullPath); +bonsai_workflow_Path = fullfile(scriptDirectory,'Bonsai','Camera_Control.bonsai'); +foundworkflow = exist("bonsai_workflow_Path",'file'); +% if ~foundworkflow +% warning('could not find bonsai executable, please insert it manually'); +% [fname fpath] = uigetfile( '*.exe', 'Provide the path to Bonsai executable'); +% pathout = fullfile(fpath, fname); +% end +% file_path = 'C:\Users\Turin\Desktop\bonsai_script.bonsai'; % Path for .bonsai file + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +switch action + + case 'init' + + % --- Create UDP Port Object --- + % This object can send to any destination without prior connection + udpSender = udpport("datagram","IPV4",'LocalHost',bonsaiComputerIP,... + 'LocalPort',9091,'Tag','MATLABSender'); + + SoloParamHandle(obj, 'UDPSender', 'value', udpSender); + + % Now that we have created the UPD connection, we can send commands + % over OSC. Before that we need to open the Bonsai App + + % bonsai_path = bonsaiPath(32); + % command = sprintf('"%s" "%s" --start', bonsai_path, bonsai_file_path); + % system([command, ' &']); + + runBonsaiWorkflow(bonsai_workflow_Path); + + % Before starting the streaming of Camera, I need to send the + % file directory for saving the file otherwise Bonsai can run into + % error as it will try saving files in the predefined folder in + % bonsai and that can conflict with file already present there. + + oscMsg_file_directory = createOSCMessage(recording_command_address, [value(Video_Saving_Folder) '\Trial.avi']); + % write(udpSender, oscMsg_file_directory, "uint8", bonsaiComputerIP,bonsaiUdpPort); + + % OSC message to start the camera + oscMsg_Camera_start = createOSCMessage(camera_command_address, startCommand); + % the command to send message to Bonsai + write(udpSender, oscMsg_Camera_start, "uint8", bonsaiComputerIP,bonsaiUdpPort); + pause(2); + write(udpSender, oscMsg_Camera_start, "uint8", bonsaiComputerIP,bonsaiUdpPort); + + pause(5); + % NOTE: Ideally I should start saving the trials once the experimenter presses + % Run either on dispatcher or Runrats. But, I dont want to make the changes there + % so would start recording as soon as the protocol is loaded and camera starts streaming + + write(udpSender, oscMsg_file_directory, "uint8", bonsaiComputerIP,bonsaiUdpPort); + + case 'next_trial' + + % in this I send a command to bonsai so that it creates a new file + % for each trial + + oscMsg_file_directory = createOSCMessage(recording_command_address, [value(Video_Saving_Folder) '\Trial.avi']); + write(value(UDPSender), oscMsg_file_directory, "uint8", bonsaiComputerIP,bonsaiUdpPort); + + case 'stop' + % this stops saving and streaming of the camera + oscMsg_Camera_stop = createOSCMessage("/camera", stopCommand); + write(value(UDPSender), oscMsg_Camera_stop, "uint8", bonsaiComputerIP,bonsaiUdpPort); + + case 'close' + + % this would stop the workflow + oscMsg_Camera_stop = createOSCMessage("/camera", stopCommand); + write(value(UDPSender), oscMsg_Camera_stop, "uint8", bonsaiComputerIP,bonsaiUdpPort); + + delete(value(UDPSender)); % delete the UDP Port + + % this is to kill the Bonsai app + system('taskkill /F /IM Bonsai.exe'); + + +end + +end + +%%%%%%%%%%%%%%% ADD 0N Functions %%%%%%%%%%%%%%%%%%%% + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +function oscPacket = createOSCMessage(address, arg) + % Ensure address and arg are char (not string) + if isstring(address) + address = char(address); + end + if isstring(arg) + arg = char(arg); + end + + % Helper to pad with nulls to 4-byte alignment + function bytes = padNulls(str) + strBytes = uint8([str, 0]); % null-terminated + padding = mod(4 - mod(length(strBytes), 4), 4); + bytes = [strBytes, zeros(1, padding, 'uint8')]; + end + + % Address (e.g., "/camera", "/record") + addrBytes = padNulls(address); + + % Type Tag String (e.g., ",s" for a single string argument) + typeTag = ',s'; + tagBytes = padNulls(typeTag); + + % Argument (e.g., "start") + argBytes = padNulls(arg); + + % Combine all parts + oscPacket = [addrBytes, tagBytes, argBytes]; +end + + +function runBonsaiWorkflow(workflowPath, addOptArray, bonsaiExePath, external) + +%% addOptArray = {'field', 'val', 'field2', 'val2'}; +%% check the input +switch nargin + case 1 + bonsaiExePath = bonsaiPath(32); + addOptArray = ''; + addFlag = 0; + external = 1; + case 2 + bonsaiExePath = bonsaiPath(32); + addFlag = 1; + external = 1; + case 3 + addFlag = 1; + external = 1; + case 4 + if isempty(bonsaiExePath) + bonsaiExePath = bonsaiPath(32); + end + addFlag = 1; + + otherwise + error('Too many variables'); +end + + + +%% write the command +optSuffix = '-p:'; +startFlag = '--start'; +noEditorFlag = '--noeditor'; %use this instead of startFlag to start Bonsai without showing the editor +cmdsep = ' '; +if external +command = [['"' bonsaiExePath '"'] cmdsep ['"' workflowPath '"'] cmdsep startFlag]; +else +command = [['"' bonsaiExePath '"'] cmdsep ['"' workflowPath '"'] cmdsep noEditorFlag]; +end +ii = 1; +commandAppend = ''; +if addFlag + while ii < size(addOptArray,2) + commandAppend = [commandAppend cmdsep optSuffix addOptArray{ii} '="' num2str(addOptArray{ii+1}) '"' ]; + ii = ii+2; + end +end + +if external + command = [command commandAppend ' &']; +else + command = [command commandAppend]; +end +%% run the command +[sysecho] = system(command); +end + + +%% parse the addOptArray +function out = parseAddOptArray(addOptArray) +addOpt = cell(length(addOptArray)/2,2); +for ii = 1:length(addOpt) + addOpt{ii,1} = addOptArray{2*ii-1}; %gets the propriety + addOpt{ii,2} = addOptArray{2*ii}; %gets the value +end +out = addOpt; +end + + +function pathout = bonsaiPath(x64x86) + +%%version of bonsai +if nargin <1; x64x86 = 64; end +switch x64x86 + case {'64', 64, 'x64', 1, '64bit'} + bonsaiEXE = 'Bonsai64.exe'; + case {32, '32', 'x86', 0, '32bit'} + bonsaiEXE = 'Bonsai.exe'; +end + +dirs = {'appdata', 'localappdata', 'programfiles', 'programfiles(x86)','USERPROFILE'}; +foundBonsai = 0; +dirIDX = 1; +while ~foundBonsai && (dirIDX <= length(dirs)) + pathout = fullfile(getenv(dirs{dirIDX}),'Bonsai', bonsaiEXE); + foundBonsai = exist(pathout, 'file'); + dirIDX = dirIDX +1; +end + +if ~foundBonsai + pathout = fullfile(getenv(dirs{5}),'Desktop',bonsaiEXE); + foundBonsai = exist(pathout, 'file'); +end + +if ~foundBonsai + warning('could not find bonsai executable, please insert it manually'); + [fname fpath] = uigetfile( '*.exe', 'Provide the path to Bonsai executable'); + pathout = fullfile(fpath, fname); +end + +end diff --git a/Protocols/@Arpit_CentrePokeTraining/Connect_Bonsai_Camera.m b/Protocols/@Arpit_CentrePokeTraining/Connect_Bonsai_Camera.m index 4b749eda..a6b41822 100644 --- a/Protocols/@Arpit_CentrePokeTraining/Connect_Bonsai_Camera.m +++ b/Protocols/@Arpit_CentrePokeTraining/Connect_Bonsai_Camera.m @@ -85,6 +85,12 @@ function Connect_Bonsai_Camera(obj,action) oscMsg_file_directory = createOSCMessage(recording_command_address, [value(Video_Saving_Folder) '\Trial.avi']); write(value(UDPSender), oscMsg_file_directory, "uint8", bonsaiComputerIP,bonsaiUdpPort); + case 'stop' + + % this stops saving and streaming of the camera + oscMsg_Camera_stop = createOSCMessage("/camera", stopCommand); + write(value(UDPSender), oscMsg_Camera_stop, "uint8", bonsaiComputerIP,bonsaiUdpPort); + case 'close' % this would stop the workflow diff --git a/Protocols/@Arpit_SoundCalibration/Arpit_SoundCalibration.asv b/Protocols/@Arpit_SoundCalibration/Arpit_SoundCalibration.asv new file mode 100644 index 00000000..3f61bebe --- /dev/null +++ b/Protocols/@Arpit_SoundCalibration/Arpit_SoundCalibration.asv @@ -0,0 +1,769 @@ + +%This is a new version of the old stand alone rigtester.m did not run +%through dispatcher. This is treated like any other protocol. It is +%designed to be plastic, looking at a rigs settings files, determining +%the input and output lines, and building a GUI specific to that rigs +%componants. +% +%Written by Chuck 2017 + +% Make sure you ran newstartup, then dispatcher('init'), and you're good to +% go! +% + +function [obj] = Arpit_SoundCalibration(varargin) + +% Default object is of our own class (mfilename); in this simplest of +% protocols, we inherit only from Plugins/@pokesplot + +obj = class(struct, mfilename, soundmanager, soundui); + +%--------------------------------------------------------------- +% BEGIN SECTION COMMON TO ALL PROTOCOLS, DO NOT MODIFY +%--------------------------------------------------------------- + +% If creating an empty object, return without further ado: +if nargin==0 || (nargin==1 && ischar(varargin{1}) && strcmp(varargin{1}, 'empty')), + return; +end; + +if isa(varargin{1}, mfilename), % If first arg is an object of this class itself, we are + % Most likely responding to a callback from + % a SoloParamHandle defined in this mfile. + if length(varargin) < 2 || ~ischar(varargin{2}), + error(['If called with a "%s" object as first arg, a second arg, a ' ... + 'string specifying the action, is required\n']); + else action = varargin{2}; varargin = varargin(3:end); %#ok + end; +else % Ok, regular call with first param being the action string. + action = varargin{1}; varargin = varargin(2:end); %#ok +end; +if ~ischar(action), error('The action parameter must be a string'); end; + +GetSoloFunctionArgs(obj); + +%--------------------------------------------------------------- +% END OF SECTION COMMON TO ALL PROTOCOLS, MODIFY AFTER THIS LINE +%--------------------------------------------------------------- + + +% ---- From here on is where you can put the code you like. +% +% Your protocol will be called, at the appropriate times, with the +% following possible actions: +% +% 'init' To initialize -- make figure windows, variables, etc. +% +% 'update' Called periodically within a trial +% +% 'prepare_next_trial' Called when a trial has ended and your protocol +% is expected to produce the StateMachine diagram for the next +% trial; i.e., somewhere in your protocol's response to this +% call, it should call "dispatcher('send_assembler', sma, +% prepare_next_trial_set);" where sma is the +% StateMachineAssembler object that you have prepared and +% prepare_next_trial_set is either a single string or a cell +% with elements that are all strings. These strings should +% correspond to names of states in sma. +% Note that after the 'prepare_next_trial' call, further +% events may still occur in the RTLSM while your protocol is thinking, +% before the new StateMachine diagram gets sent. These events +% will be available to you when 'trial_completed' is called on your +% protocol (see below). +% +% 'trial_completed' Called when 'state_0' is reached in the RTLSM, +% marking final completion of a trial (and the start of +% the next). +% +% 'close' Called when the protocol is to be closed. +% +% +% VARIABLES THAT DISPATCHER WILL ALWAYS INSTANTIATE FOR YOU IN YOUR +% PROTOCOL: +% +% (These variables will be instantiated as regular Matlab variables, +% not SoloParamHandles. For any method in your protocol (i.e., an m-file +% within the @your_protocol directory) that takes "obj" as its first argument, +% calling "GetSoloFunctionArgs(obj)" will instantiate all the variables below.) +% +% +% n_done_trials How many trials have been finished; when a trial reaches +% one of the prepare_next_trial states for the first +% time, this variable is incremented by 1. +% +% n_started trials How many trials have been started. This variable gets +% incremented by 1 every time the state machine goes +% through state 0. +% +% parsed_events The result of running disassemble.m, with the +% parsed_structure flag set to 1, on all events from the +% start of the current trial to now. +% +% latest_events The result of running disassemble.m, with the +% parsed_structure flag set to 1, on all new events from +% the last time 'update' was called to now. +% +% raw_events All the events obtained in the current trial, not parsed +% or disassembled, but raw as gotten from the State +% Machine object. +% +% current_assembler The StateMachineAssembler object that was used to +% generate the State Machine diagram in effect in the +% current trial. +% +% Trial-by-trial history of parsed_events, raw_events, and +% current_assembler, are automatically stored for you in your protocol by +% dispatcher.m. See the wiki documentation for information on how to access +% those histories from within your protocol and for information. +% +% + + +switch action, + + %--------------------------------------------------------------- + % CASE INIT + %--------------------------------------------------------------- + + case 'init' + + % Make default figure. We remember to make it non-saveable; on next run + % the handle to this figure might be different, and we don't want to + % overwrite it when someone does load_data and some old value of the + % fig handle was stored as SoloParamHandle "myfig" + SoloParamHandle(obj, 'myfig', 'saveable', 0); myfig.value = double(figure); + + % Make the title of the figure be the protocol name, and if someone tries + % to close this figure, call dispatcher's close_protocol function, so it'll know + % to take it off the list of open protocols. + name = mfilename; + set(value(myfig), 'Name', name, 'Tag', name, ... + 'closerequestfcn', [mfilename,'(''close'');'], 'MenuBar', 'none'); + + alldio = bSettings('get','DIOLINES','ALL'); + allinp = bSettings('get','INPUTLINES','ALL'); + dionums = cell2mat(alldio(:,2)); + inpnums = cell2mat(allinp(:,2)); + + for i = 1:16 + inp1 = find(inpnums == i,1,'first'); + dio1 = find(dionums == 2^(( i-1)*2), 1,'first'); + dio2 = find(dionums == 2^(((i-1)*2)+1),1,'first'); + + if isempty(inp1) && isempty(dio1) && isempty(dio2) + continue; + end + + if ~isempty(inp1) + linegroups{i}{1,1} = allinp{inp1,2}; %#ok + linegroups{i}{1,2} = allinp{inp1,1}; %#ok + else + linegroups{i}{1,1} = []; %#ok + linegroups{i}{1,2} = []; %#ok + end + if ~isempty(dio1) + linegroups{i}{2,1} = alldio{dio1,2}; %#ok + linegroups{i}{2,2} = alldio{dio1,1}; %#ok + else + linegroups{i}{2,1} = []; %#ok + linegroups{i}{2,2} = []; %#ok + end + if ~isempty(dio2) + linegroups{i}{3,1} = alldio{dio2,2}; %#ok + linegroups{i}{3,2} = alldio{dio2,1}; %#ok + else + linegroups{i}{3,1} = []; %#ok + linegroups{i}{3,2} = []; %#ok + end + end + + % Generate the sounds we need. + soundserver = bSettings('get','RIGS','sound_machine_server'); + if ~isempty(soundserver) + sr = SoundManagerSection(obj,'get_sample_rate'); + Fs=sr; + lfreq=2000; + hfreq=20000; + freq = 100; + T = 5; + fcut = 110; + filter_type = 'GAUS'; + A1_sigma = 0.0500; + A2_sigma = 0.0306; %0.1230;%0.0260; + A3_sigma = 0.0187; %0.0473;%0.0135; + A4_sigma = 0.0114; %0.0182;%0.0070; + A5_sigma = 0.0070; + [rawA1 rawA2 normA1 normA2]=noisestim(1,1,T,fcut,Fs,filter_type); + modulator=singlenoise(1,T,[lfreq hfreq],Fs,'BUTTER'); + AUD1=normA1(1:T*sr).*modulator(1:T*sr).*A1_sigma; + AUD2=normA1(1:T*sr).*modulator(1:T*sr).*A2_sigma; + AUD3=normA1(1:T*sr).*modulator(1:T*sr).*A3_sigma; + AUD4=normA1(1:T*sr).*modulator(1:T*sr).*A4_sigma; + AUD5=normA1(1:T*sr).*modulator(1:T*sr).*A5_sigma; + % snd = MakeBupperSwoop(sr,0,freq,freq, len/2, len/2, 0, 0.1, 'F1_volume_factor',0.07,'F2_volume_factor',0.07); + % silence_length = 0; + % presound_silence = zeros(1,sr*silence_length/1000); + % snd = [presound_silence, snd]; + % + % SoundManagerSection(obj,'declare_new_sound','left_sound', [snd; zeros(1,size(snd,2))]); + % SoundManagerSection(obj,'declare_new_sound','right_sound', [zeros(1,size(snd,2)); snd]); + % + % SoundManagerSection(obj,'loop_sound','left_sound',1); + % SoundManagerSection(obj,'loop_sound','right_sound',1); + % + % SoundManager Section(obj,'declare_new_sound','center_sound',[snd;snd([ceil((sr / freq) / 2):end,1:ceil((sr / freq) / 2)-1])]); + % SoundManagerSection(obj,'loop_sound','center_sound',1); + % + if ~isempty(AUD2) + SoundManagerSection(obj, 'declare_new_sound', 'left_sound', [AUD2'; AUD2']) + end + if ~isempty(AUD1) + SoundManagerSection(obj, 'declare_new_sound', 'center_sound', [AUD1'; AUD1']) + end + if ~isempty(AUD3) + SoundManagerSection(obj, 'declare_new_sound', 'right_sound', [AUD3'; AUD3']) + end + if ~isempty(AUD4) + SoundManagerSection(obj, 'declare_new_sound', 'fourth_sound', [AUD4'; AUD4']) + end + if ~isempty(AUD5) + SoundManagerSection(obj, 'declare_new_sound', 'fifth_sound', [AUD5'; AUD5']) + end + SoundManagerSection(obj, 'send_not_yet_uploaded_sounds'); + end + + % Get monitor dimensions for dynamic GUI sizing + MP = get(0,'MonitorPositions'); + + % Calculate individual group width based on screen dimensions and number of groups + groupwidth = floor((MP(3)/2)/numel(linegroups)); + + padding = 10 % padding around GUI elements + + % Calculate total width needed for all groups combined + total_width = floor(numel(linegroups) * groupwidth+padding); + total_height = 400 % total height of GUI + + label_height = 140 % height of port label + button_height = 25 + + % Center the GUI horizontally on screen + left_pos = floor((MP(3) - total_width) / 2); + + % Set figure position with centered alignment and fixed height of 400 pixels + % position vector: [left-right, down-up, width, height], the origo is + % the bottom left corner + set(value(myfig), 'Position', [left_pos, floor((MP(4)-total_height)/2), total_width, total_height]); + + % Initialize array to store line names + line_names = []; + + % Iterate through each line group + for i = 1:numel(linegroups) + % Check if current group exists and has valid parameters + if ~isempty(linegroups{i}) && ~isempty(linegroups{i}{1,2}) + % port label position + SubheaderParam( ... + obj, ... + ['Input',linegroups{i}{1,2}], ... + {linegroups{i}{1,2};0},0,0, ... + 'position',[((i-1)*groupwidth)+padding, ... + total_height-padding-label_height, ... + groupwidth-padding, ... + label_height ... + ]); + + % port label aesthetics + set(get_ghandle(eval(['Input',linegroups{i}{1,2}])), ... + 'FontSize',32, ... % Large font size for visibility + 'BackgroundColor',[0,1,0], ... % Green background + 'HorizontalAlignment','center'); % Center align text + + % Store line name for later reference + line_names(end+1) = linegroups{i}{1,2}; %#ok + + % Add sound controls based on line identifier + if strcmp(linegroups{i}{1,2},'L') && ~isempty(soundserver) + % Left channel sound toggle + ToggleParam(obj, ... + 'LeftSound', 0,0,0, ... + 'position',[((i-1)*groupwidth)+padding, padding, groupwidth-padding,button_height], ... + 'OnString', 'Sound Two ON', ... + 'OffString', 'Sound Two OFF'); + set_callback(LeftSound,{mfilename,'play_left_sound'}); + + elseif strcmp(linegroups{i}{1,2},'R') && ~isempty(soundserver) + % Right channel sound toggle + ToggleParam(obj, ... + 'RightSound', 0,0,0, ... + 'position',[((i-1)*groupwidth)+padding, padding, groupwidth-padding,button_height], ... + 'OnString', 'Sound Three ON', ... + 'OffString', 'Sound Three OFF'); + set_callback(RightSound,{mfilename,'play_right_sound'}); + + elseif strcmp(linegroups{i}{1,2},'C') && ~isempty(soundserver) + % Center channel sound toggle + ToggleParam(obj, ... + 'CenterSound', 0,0,0, ... + 'position',[((i-1)*groupwidth)+padding, padding, groupwidth-padding,button_height], ... + 'OnString', 'Sound One ON', ... + 'OffString', 'Sound One OFF'); + set_callback(CenterSound,{mfilename,'play_center_sound'}); + + elseif strcmp(linegroups{i}{1,2},'A') && ~isempty(soundserver) + % Additional channel sound toggle + ToggleParam(obj, ... + 'FourthSound', 0,0,0, ... + 'position',[((i-1)*groupwidth)+padding, padding, groupwidth-padding,button_height], ... + 'OnString', 'Sound Four ON', ... + 'OffString', 'Sound Four OFF'); + set_callback(FourthSound,{mfilename,'play_fourth_sound'}); + end + + % Handle case for empty line identifier but existing group + elseif ~isempty(linegroups{i}) && isempty(linegroups{i}{1,2}) + ToggleParam(obj, ... + 'FifthSound', 0,0,0, ... + 'position',[((i-1)*groupwidth)+padding, padding, groupwidth-padding,button_height], ... + 'OnString', 'Sound Five ON', ... + 'OffString', 'Sound Five OFF'); + set_callback(FifthSound,{mfilename,'play_fifth_sound'}); + end + + % Add first digital I/O control if available + if ~isempty(linegroups{i}) && ~isempty(linegroups{i}{2,2}) + ToggleParam(obj, ... + ['DIO',num2str(i),'_1'],0,0,0, ... + 'position',[((i-1)*groupwidth)+padding, 2*button_height+padding, groupwidth-padding,button_height], ... + 'OnString', [linegroups{i}{2,2},' ON'], ... + 'OffString', [linegroups{i}{2,2},' OFF']); + set_callback(eval(['DIO',num2str(i),'_1']),{mfilename,['toggle',num2str(i),'_1']}); + end + + % Add second digital I/O control if available + if ~isempty(linegroups{i}) && ~isempty(linegroups{i}{3,2}) + ToggleParam(obj, ... + ['DIO',num2str(i),'_2'],0,0,0, ... + 'position',[((i-1)*groupwidth)+padding, button_height+padding, groupwidth-padding,button_height], ... + 'OnString', [linegroups{i}{3,2},' ON'], ... + 'OffString', [linegroups{i}{3,2},' OFF']); + set_callback(eval(['DIO',num2str(i),'_2']),{mfilename,['toggle',num2str(i),'_2']}); + end + end + + + SoloParamHandle(obj,'LineGroups','value',linegroups); + SoloParamHandle(obj,'LineNames','value',line_names); + + scr = timer; + set(scr,'Period', 0.2,'ExecutionMode','FixedRate','TasksToExecute',Inf,... + 'BusyMode','drop','TimerFcn',[mfilename,'(''close_continued'')']); + SoloParamHandle(obj, 'stopping_complete_timer', 'value', scr); + + SoundCalibration(obj,'prepare_next_trial'); + + dispatcher('Run'); + + + case 'play_left_sound' + %% play_left_sound + if value(LeftSound) == 1 + SoundManagerSection(obj,'play_sound','left_sound'); + else + SoundManagerSection(obj,'stop_sound','left_sound'); + end + + case 'play_right_sound' + %% play_right_sound + if value(RightSound) == 1 + SoundManagerSection(obj,'play_sound','right_sound'); + else + SoundManagerSection(obj,'stop_sound','right_sound'); + end + + case 'play_center_sound' + %% play_center_sound + if value(CenterSound) == 1 + SoundManagerSection(obj,'play_sound','center_sound'); + else + SoundManagerSection(obj,'stop_sound','center_sound'); + end + + case 'play_fourth_sound' + %% play_fourth_sound + if value(FourthSound) == 1 + SoundManagerSection(obj,'play_sound','fourth_sound'); + else + SoundManagerSection(obj,'stop_sound','fourth_sound'); + end + case 'play_fifth_sound' + %% play_fifth_sound + if value(FifthSound) == 1 + SoundManagerSection(obj,'play_sound','fifth_sound'); + else + SoundManagerSection(obj,'stop_sound','fifth_sound'); + end + case 'toggle1_1' + %% case toggle1_1 + linegroups = value(LineGroups); + dispatcher('toggle_bypass',log2(linegroups{1}{2,1})); + + case 'toggle1_2' + %% case toggle1_2 + linegroups = value(LineGroups); + dispatcher('toggle_bypass',log2(linegroups{1}{3,1})); + + case 'toggle2_1' + %% case toggle1_1 + linegroups = value(LineGroups); + dispatcher('toggle_bypass',log2(linegroups{2}{2,1})); + + case 'toggle2_2' + %% case toggle1_2 + linegroups = value(LineGroups); + dispatcher('toggle_bypass',log2(linegroups{2}{3,1})); + + case 'toggle3_1' + %% case toggle1_1 + linegroups = value(LineGroups); + dispatcher('toggle_bypass',log2(linegroups{3}{2,1})); + + case 'toggle3_2' + %% case toggle1_2 + linegroups = value(LineGroups); + dispatcher('toggle_bypass',log2(linegroups{3}{3,1})); + + case 'toggle4_1' + %% case toggle1_1 + linegroups = value(LineGroups); + dispatcher('toggle_bypass',log2(linegroups{4}{2,1})); + + case 'toggle4_2' + %% case toggle1_2 + linegroups = value(LineGroups); + dispatcher('toggle_bypass',log2(linegroups{4}{3,1})); + + case 'toggle5_1' + %% case toggle1_1 + linegroups = value(LineGroups); + dispatcher('toggle_bypass',log2(linegroups{5}{2,1})); + + case 'toggle5_2' + %% case toggle1_2 + linegroups = value(LineGroups); + dispatcher('toggle_bypass',log2(linegroups{5}{3,1})); + + case 'toggle6_1' + %% case toggle1_1 + linegroups = value(LineGroups); + dispatcher('toggle_bypass',log2(linegroups{6}{2,1})); + + case 'toggle6_2' + %% case toggle1_2 + linegroups = value(LineGroups); + dispatcher('toggle_bypass',log2(linegroups{6}{3,1})); + + case 'toggle7_1' + %% case toggle1_1 + linegroups = value(LineGroups); + dispatcher('toggle_bypass',log2(linegroups{7}{2,1})); + + case 'toggle7_2' + %% case toggle1_2 + linegroups = value(LineGroups); + dispatcher('toggle_bypass',log2(linegroups{7}{3,1})); + + case 'toggle8_1' + %% case toggle1_1 + linegroups = value(LineGroups); + dispatcher('toggle_bypass',log2(linegroups{8}{2,1})); + + case 'toggle8_2' + %% case toggle1_2 + linegroups = value(LineGroups); + dispatcher('toggle_bypass',log2(linegroups{8}{3,1})); + + case 'toggle9_1' + %% case toggle1_1 + linegroups = value(LineGroups); + dispatcher('toggle_bypass',log2(linegroups{9}{2,1})); + + case 'toggle9_2' + %% case toggle1_2 + linegroups = value(LineGroups); + dispatcher('toggle_bypass',log2(linegroups{9}{3,1})); + + case 'toggle10_1' + %% case toggle1_1 + linegroups = value(LineGroups); + dispatcher('toggle_bypass',log2(linegroups{10}{2,1})); + + case 'toggle10_2' + %% case toggle1_2 + linegroups = value(LineGroups); + dispatcher('toggle_bypass',log2(linegroups{10}{3,1})); + + case 'toggle11_1' + %% case toggle1_1 + linegroups = value(LineGroups); + dispatcher('toggle_bypass',log2(linegroups{11}{2,1})); + + case 'toggle11_2' + %% case toggle1_2 + linegroups = value(LineGroups); + dispatcher('toggle_bypass',log2(linegroups{11}{3,1})); + + case 'toggle12_1' + %% case toggle1_1 + linegroups = value(LineGroups); + dispatcher('toggle_bypass',log2(linegroups{12}{2,1})); + + case 'toggle12_2' + %% case toggle1_2 + linegroups = value(LineGroups); + dispatcher('toggle_bypass',log2(linegroups{12}{3,1})); + + case 'toggle13_1' + %% case toggle1_1 + linegroups = value(LineGroups); + dispatcher('toggle_bypass',log2(linegroups{13}{2,1})); + + case 'toggle13_2' + %% case toggle1_2 + linegroups = value(LineGroups); + dispatcher('toggle_bypass',log2(linegroups{13}{3,1})); + + case 'toggle14_1' + %% case toggle1_1 + linegroups = value(LineGroups); + dispatcher('toggle_bypass',log2(linegroups{14}{2,1})); + + case 'toggle14_2' + %% case toggle1_2 + linegroups = value(LineGroups); + dispatcher('toggle_bypass',log2(linegroups{14}{3,1})); + + case 'toggle15_1' + %% case toggle1_1 + linegroups = value(LineGroups); + dispatcher('toggle_bypass',log2(linegroups{15}{2,1})); + + case 'toggle15_2' + %% case toggle1_2 + linegroups = value(LineGroups); + dispatcher('toggle_bypass',log2(linegroups{15}{3,1})); + + case 'toggle16_1' + %% case toggle1_1 + linegroups = value(LineGroups); + dispatcher('toggle_bypass',log2(linegroups{16}{2,1})); + + case 'toggle16_2' + %% case toggle1_2 + linegroups = value(LineGroups); + dispatcher('toggle_bypass',log2(linegroups{16}{3,1})); + + %--------------------------------------------------------------- + % CASE PREPARE_NEXT_TRIAL + %--------------------------------------------------------------- + case 'prepare_next_trial' + line_names = value(LineNames); + sma = StateMachineAssembler('full_trial_structure','use_happenings',1,'n_input_lines',numel(line_names),'line_names',line_names); + sma = add_state(sma, 'name', 'the_only_state', 'self_timer',1e4, 'input_to_statechange', {'Tup', 'final_state'}); + sma = add_state(sma, 'name', 'final_state', 'self_timer',1e4, 'input_to_statechange', {'Tup', 'check_next_trial_ready'}); + dispatcher('send_assembler', sma, 'final_state'); + + %--------------------------------------------------------------- + % CASE TRIAL_COMPLETED + %--------------------------------------------------------------- + case 'trial_completed' + + + %--------------------------------------------------------------- + % CASE UPDATE + %--------------------------------------------------------------- + case 'update' + pe = parsed_events; %#ok + linenames = value(LineNames); + + for i = 1:numel(linenames) + poketimes = eval(['pe.pokes.',linenames(i)]); + if ~isempty(poketimes) && isnan(poketimes(end,2)) + set(get_ghandle(eval(['Input',linenames(i)])),'BackgroundColor',[1,0,0]); + + str = get(get_ghandle(eval(['Input',linenames(i)])),'string'); + str{2} = size(poketimes,1); + set(get_ghandle(eval(['Input',linenames(i)])),'string',str); + + else + set(get_ghandle(eval(['Input',linenames(i)])),'BackgroundColor',[0,1,0]); + end + end + + %--------------------------------------------------------------- + % CASE CLOSE + %--------------------------------------------------------------- + case 'close' + + dispatcher('Stop'); + + %Let's pause until we know dispatcher is done running + set(value(stopping_complete_timer),'TimerFcn',[mfilename,'(''close_continued'');']); + start(value(stopping_complete_timer)); + + case 'close_continued' + + if value(stopping_process_completed) + stop(value(stopping_complete_timer)); %Stop looping. + %dispatcher('set_protocol',''); + + if exist('myfig', 'var') && isa(myfig, 'SoloParamHandle') && ishandle(value(myfig)), %#ok + delete(value(myfig)); + end; + delete_sphandle('owner', ['^@' class(obj) '$']); + dispatcher('set_protocol',''); + end + otherwise, + warning('Unknown action! "%s"\n', action); %#ok +end; + +return; + +end + +function calculate_soundlevel + +% --- Configuration --- +serialPort = '/dev/ttyACM0'; % Replace with the actual serial port of your Raspberry Pi +baudRate = 9600; % Or your desired baud rate +matlabOutputValues = 0:10:100; % Example range of MATLAB output values +calibrationData = []; + +% --- Establish Serial Connection --- +try + s = serial(serialPort, 'BaudRate', baudRate); + fopen(s); + disp(['Opened serial port: ', serialPort, ' at ', num2str(baudRate), ' bps']); + + % --- Calibration Loop --- + for i = 1:length(matlabOutputValues) + outputValue = matlabOutputValues(i); + disp(['Sending MATLAB output value: ', num2str(outputValue)]); + + % --- Send command to set speaker level (replace with your actual command) --- + setSpeakerLevel(outputValue); + + % --- Send command to Raspberry Pi to measure SPL --- + fprintf(s, 'measure_spl\n'); % Send command over serial + + % --- Receive SPL value from Raspberry Pi --- + receivedData = fgetl(s); + if ~isempty(receivedData) + splValue = str2double(receivedData); + if ~isnan(splValue) + disp(['Received SPL: ', num2str(splValue), ' dB']); + calibrationData = [calibrationData; outputValue, splValue]; + pause(2); % Allow time for sound to stabilize and measurement + else + warning('Received non-numeric SPL value.'); + end + else + warning('No data received from Raspberry Pi.'); + end + end + + % --- Close Serial Connection --- + fclose(s); + delete(s); + clear s; + + % --- Display Calibration Data --- + disp('Calibration Data (MATLAB Value, dB SPL):'); + disp(calibrationData); + + % --- Create Calibration Mapping (Example: Linear Fit) --- + p = polyfit(calibrationData(:,1), calibrationData(:,2), 1); + disp('Calibration Polynomial (p(1) * MATLAB_Value + p(2)):'); + disp(p); + % Now you can use this polynomial 'p' to estimate dB SPL from your MATLAB values + % estimatedSPL = polyval(p, yourMatlabValue); + +catch ME + disp(['Error occurred: ', ME.message]); + if exist('s', 'var') && isvalid(s) + fclose(s); + delete(s); + clear s; + end +end + +% --- Your function to control the speaker level in MATLAB --- +function setSpeakerLevel(value) + % Replace this with your actual commands or functions to control the speaker. + disp(['(Simulating setting speaker level to: ', num2str(value), ')']); + pause(1); % Simulate speaker level change +end + +end + + +function [normbase]=singlenoise(sigma_1,T,fcut,Fs,filter_type) + +%%%%%%%%%%%%%%%%% Determines of the type of filter used %%%%%%%%%%%%%%%%%%% +%'LPFIR': lowpass FIR%%%%%'FIRLS': Least square linear-phase FIR filter design +%'BUTTER': IIR Butterworth lowpass filter%%%%%%'GAUS': Gaussian filter (window) +%'MOVAVRG': Moving average FIR filter%%%%%%%%'KAISER': Kaiser-window FIR filtering +% 'EQUIRIP':Eqiripple FIR filter%%%%% 'HAMMING': Hamming-window based FIR +% T is duration of each signal in milisecond, fcut is the cut-off frequency +% Fs is the sampling frequency +% outband=40; + +filter_type='BUTTER'; +outband=60; +replace=1; +L=floor(T*Fs);% Length of signal + +%%%%%%%%%%% produce position values %%%%%%% +pos1 = sigma_1 * randn(Fs,1); + +% pos1(pos1>outband)=[]; +% pos1(pos1<-outband)=[]; + +base = randsample(pos1,L,replace); +%%%% Filter the original position values %%%%%% +%filtbase=filt(base,fcut,Fs,filter_type); +hf = design(fdesign.bandpass('N,F3dB1,F3dB2',10,fcut(1),fcut(2),Fs)); +filtbase=filter(hf,base); +normbase=filtbase./(max(abs(filtbase))); +end + + +function [base,target,normbase,normtarget]=noisestim(sigma_1,sigma_2,T,fcut,Fs,filter_type) + +%%%%%%%%%%%%%%%%% Determines of the type of filter used %%%%%%%%%%%%%%%%%%% +%'LPFIR': lowpass FIR%%%%%'FIRLS': Least square linear-phase FIR filter design +%'BUTTER': IIR Butterworth lowpass filter%%%%%%'GAUS': Gaussian filter (window) +%'MOVAVRG': Moving average FIR filter%%%%%%%%'KAISER': Kaiser-window FIR filtering +% 'EQUIRIP':Eqiripple FIR filter%%%%% 'HAMMING': Hamming-window based FIR +% T is duration of each signal in milisecond, fcut is the cut-off frequency +% Fs is the sampling frequency +% outband=40; +replace=1; +L=floor(T*Fs); % Length of signal +% t=L*linspace(0,1,L)/Fs; % time in miliseconds +%%%%%%%%%%% produce position values %%%%%%% +pos1 = sigma_1*randn(Fs,1); +% pos1(pos1>outband)=[]; +% pos1(pos1<-outband)=[]; + +pos2 =sigma_2*randn(Fs,1); +% pos2(pos2>outband)=[]; +% pos2(pos2<-outband)=[]; +base = randsample(pos1,L,replace); +target = randsample(pos2,L,replace); +%%%% Filter the original position values %%%%%% +filtbase=filt(base,fcut,Fs,filter_type); +filttarget=filt(target,fcut,Fs,filter_type); +normbase=filtbase./(max(abs(filtbase))); +normtarget=filttarget./(max(abs(filttarget))); +end diff --git a/Protocols/@Arpit_SoundCalibration/Arpit_SoundCalibration.m b/Protocols/@Arpit_SoundCalibration/Arpit_SoundCalibration.m index 9521c087..4979e981 100644 --- a/Protocols/@Arpit_SoundCalibration/Arpit_SoundCalibration.m +++ b/Protocols/@Arpit_SoundCalibration/Arpit_SoundCalibration.m @@ -189,9 +189,9 @@ fcut = 110; filter_type = 'GAUS'; A1_sigma = 0.0500; - A2_sigma = 0.0306 %0.1230;%0.0260; - A3_sigma = 0.0187 %0.0473;%0.0135; - A4_sigma = 0.0114 %0.0182;%0.0070; + A2_sigma = 0.0306; %0.1230;%0.0260; + A3_sigma = 0.0187; %0.0473;%0.0135; + A4_sigma = 0.0114; %0.0182;%0.0070; A5_sigma = 0.0070; [rawA1 rawA2 normA1 normA2]=noisestim(1,1,T,fcut,Fs,filter_type); modulator=singlenoise(1,T,[lfreq hfreq],Fs,'BUTTER'); @@ -360,7 +360,7 @@ 'BusyMode','drop','TimerFcn',[mfilename,'(''close_continued'')']); SoloParamHandle(obj, 'stopping_complete_timer', 'value', scr); - SoundCalibration(obj,'prepare_next_trial'); + Arpit_SoundCalibration(obj,'prepare_next_trial'); dispatcher('Run'); @@ -568,7 +568,7 @@ %--------------------------------------------------------------- case 'prepare_next_trial' line_names = value(LineNames); - sma = StateMachineAssembler('full_trial_structure','use_happenings',1,'n_input_lines',numel(line_names),'line_names',line_names); + sma = StateMachineAssembler('full_trial_structure','use_happenings',1); %,'n_input_lines',numel(line_names),'line_names',line_names); sma = add_state(sma, 'name', 'the_only_state', 'self_timer',1e4, 'input_to_statechange', {'Tup', 'final_state'}); sma = add_state(sma, 'name', 'final_state', 'self_timer',1e4, 'input_to_statechange', {'Tup', 'check_next_trial_ready'}); dispatcher('send_assembler', sma, 'final_state'); @@ -631,6 +631,21 @@ end + + + + + + + + + + + + + + + function calculate_soundlevel % --- Configuration --- @@ -704,4 +719,132 @@ function setSpeakerLevel(value) pause(1); % Simulate speaker level change end -end \ No newline at end of file +end + + +function [normbase]=singlenoise(sigma_1,T,fcut,Fs,filter_type) + +%%%%%%%%%%%%%%%%% Determines of the type of filter used %%%%%%%%%%%%%%%%%%% +%'LPFIR': lowpass FIR%%%%%'FIRLS': Least square linear-phase FIR filter design +%'BUTTER': IIR Butterworth lowpass filter%%%%%%'GAUS': Gaussian filter (window) +%'MOVAVRG': Moving average FIR filter%%%%%%%%'KAISER': Kaiser-window FIR filtering +% 'EQUIRIP':Eqiripple FIR filter%%%%% 'HAMMING': Hamming-window based FIR +% T is duration of each signal in milisecond, fcut is the cut-off frequency +% Fs is the sampling frequency +% outband=40; +% filter_type='BUTTER'; + +outband=60; +replace=1; +L=floor(T*Fs);% Length of signal + +%%%%%%%%%%% produce position values %%%%%%% +pos1 = sigma_1 * randn(Fs,1); + +% pos1(pos1>outband)=[]; +% pos1(pos1<-outband)=[]; + +base = randsample(pos1,L,replace); +%%%% Filter the original position values %%%%%% +%filtbase=filt(base,fcut,Fs,filter_type); +hf = design(fdesign.bandpass('N,F3dB1,F3dB2',10,fcut(1),fcut(2),Fs)); +filtbase=filter(hf,base); +normbase=filtbase./(max(abs(filtbase))); + +end + + +function [base,target,normbase,normtarget]=noisestim(sigma_1,sigma_2,T,fcut,Fs,filter_type) + +%%%%%%%%%%%%%%%%% Determines of the type of filter used %%%%%%%%%%%%%%%%%%% +%'LPFIR': lowpass FIR%%%%%'FIRLS': Least square linear-phase FIR filter design +%'BUTTER': IIR Butterworth lowpass filter%%%%%%'GAUS': Gaussian filter (window) +%'MOVAVRG': Moving average FIR filter%%%%%%%%'KAISER': Kaiser-window FIR filtering +% 'EQUIRIP':Eqiripple FIR filter%%%%% 'HAMMING': Hamming-window based FIR +% T is duration of each signal in milisecond, fcut is the cut-off frequency +% Fs is the sampling frequency +% outband=40; +replace=1; +L=floor(T*Fs); % Length of signal +% t=L*linspace(0,1,L)/Fs; % time in miliseconds +%%%%%%%%%%% produce position values %%%%%%% +pos1 = sigma_1*randn(Fs,1); +% pos1(pos1>outband)=[]; +% pos1(pos1<-outband)=[]; + +pos2 =sigma_2*randn(Fs,1); +% pos2(pos2>outband)=[]; +% pos2(pos2<-outband)=[]; +base = randsample(pos1,L,replace); +target = randsample(pos2,L,replace); +%%%% Filter the original position values %%%%%% +filtbase=filt(base,fcut,Fs,filter_type); +filttarget=filt(target,fcut,Fs,filter_type); +normbase=filtbase./(max(abs(filtbase))); +normtarget=filttarget./(max(abs(filttarget))); + +end + + + + +function filtsignal=filt(signal,fcut,Fs,filter_type) +a=2; % wp/ws used in butterworth method and LS linear FIR method +N=200; % filter order used in lowpass FIR method +rp=3; % passband ripple in dB used in butterworth method +rs=60; % stopband attenuation in dB used in butterworth method +beta=0.1102*(rs-8.8); %used in Kaiser window to obtain sidelobe attenuation of rs dB +if strcmp(filter_type, 'GAUS') || strcmp(filter_type, 'MOVAVRG') +window = fix(Fs/fcut); % window size used in Gaussian and moving average methods +end +wp=2*fcut/Fs; % normalized passband corner frequency wp, the cutoff frequency +ws=a*wp; % normalized stopband corner frequency + +switch filter_type + case 'BUTTER' %Butterworth IIR filter + if length(wp)>1 + ws(1)=2*(fcut(1)/2)/Fs; + ws(2)=2*(fcut(2)+fcut(1)/2)/Fs; + [n,wn]=buttord(wp,ws,rp,rs); + [b,a]=butter(n,wn,'bandpass'); + else + [n,wn]=buttord(wp,ws,rp,rs); + [b,a]=butter(n,wn,'low'); + end + filtsignal=filter(b,a,signal);%conventional filtering + case 'LPFIR' %Lowpass FIR filter + d=fdesign.lowpass('N,Fc',N,fcut,Fs); % Fc is the 6-dB down point, N is the filter order(N+1 filter coefficients) + Hd = design(d); + filtsignal=filter(Hd.Numerator,1,signal); %conventional filtering + case 'FIRLS' %Least square linear-phase FIR filter design + b=firls(255,[0 2*fcut/Fs a*2*fcut/Fs 1],[1 1 0 0]); + filtsignal=filter(b,1,signal); %conventional filtering + case 'EQUIRIP' %Eqiripple FIR filter + d=fdesign.lowpass('Fp,Fst,Ap,Ast',wp,ws,rp,rs); + Hd=design(d,'equiripple'); + filtsignal=filter(Hd.Numerator,1,signal); %conventional filtering + case 'MOVAVRG' % Moving average FIR filtering, Rectangular window + h = ones(window,1)/window; + b = fir1(window-1,wp,h); + filtsignal = filter(b, 1, signal); + case 'HAMMING' % Hamming-window based FIR filtering + b = fir1(150,wp); + filtsignal = filter(b, 1, signal); + filtsignal = filter(h, 1, signal); + case 'GAUS' % Gaussian-window FIR filtering + h = normpdf(1:window, 0, fix(window/2)); + b = fir1(window-1,wp,h); + filtsignal = filter(b, 1, signal); + case 'GAUS1' % Gaussian-window FIR filtering + b = fir1(window-1,wp,gausswin(window,2)/window); + filtsignal = filter(b, 1, signal); + case 'KAISER' %Kaiser-window FIR filtering + h=kaiser(window,beta); + b = fir1(window-1,wp,h); + filtsignal = filter(b, 1, signal); + + otherwise + sprintf('filter_type is wrong!! havaset kojast!!') +end +end + From b88e3f30185026767fdd65aa45adeb232e862c78 Mon Sep 17 00:00:00 2001 From: viktorpm Date: Wed, 23 Apr 2025 17:23:49 +0100 Subject: [PATCH 060/164] renamed protocol to remove underscore in name. Unable to rename one of them. --- .../ArpitSoundCalibration.m} | 0 .../ArpitSoundCatContinuous.m} | 0 .../SideSection.m | 0 .../StimulatorSection.asv | 0 .../StimulatorSection.m | 0 ...keTraining.m => ArpitCentrePokeTraining.m} | 0 ...ningSMA.m => ArpitCentrePokeTrainingSMA.m} | 0 ...ng_SessionDefinition_AutoTrainingStages.m} | 0 .../Arpit_CentrePokeTraining.asv | 346 -------- .../Connect_Bonsai_Camera.asv | 246 ------ .../Connect_Bonsai_Camera.m | 9 +- .../StimulusSection.m | 5 + .../private/StimuliDistribution_plot.m | 2 + .../Arpit_SoundCalibration.asv | 769 ------------------ 14 files changed, 11 insertions(+), 1366 deletions(-) rename Protocols/{@Arpit_SoundCalibration/Arpit_SoundCalibration.m => @ArpitSoundCalibration/ArpitSoundCalibration.m} (100%) rename Protocols/{@Arpit_SoundCatContinuous/Arpit_SoundCatContinuous.m => @ArpitSoundCatContinuous/ArpitSoundCatContinuous.m} (100%) rename Protocols/{@Arpit_SoundCatContinuous => @ArpitSoundCatContinuous}/SideSection.m (100%) rename Protocols/{@Arpit_SoundCatContinuous => @ArpitSoundCatContinuous}/StimulatorSection.asv (100%) rename Protocols/{@Arpit_SoundCatContinuous => @ArpitSoundCatContinuous}/StimulatorSection.m (100%) rename Protocols/@Arpit_CentrePokeTraining/{Arpit_CentrePokeTraining.m => ArpitCentrePokeTraining.m} (100%) rename Protocols/@Arpit_CentrePokeTraining/{Arpit_CentrePokeTrainingSMA.m => ArpitCentrePokeTrainingSMA.m} (100%) rename Protocols/@Arpit_CentrePokeTraining/{Arpit_CentrePokeTraining_SessionDefinition_AutoTrainingStages.m => ArpitCentrePokeTraining_SessionDefinition_AutoTrainingStages.m} (100%) delete mode 100644 Protocols/@Arpit_CentrePokeTraining/Arpit_CentrePokeTraining.asv delete mode 100644 Protocols/@Arpit_CentrePokeTraining/Connect_Bonsai_Camera.asv delete mode 100644 Protocols/@Arpit_SoundCalibration/Arpit_SoundCalibration.asv diff --git a/Protocols/@Arpit_SoundCalibration/Arpit_SoundCalibration.m b/Protocols/@ArpitSoundCalibration/ArpitSoundCalibration.m similarity index 100% rename from Protocols/@Arpit_SoundCalibration/Arpit_SoundCalibration.m rename to Protocols/@ArpitSoundCalibration/ArpitSoundCalibration.m diff --git a/Protocols/@Arpit_SoundCatContinuous/Arpit_SoundCatContinuous.m b/Protocols/@ArpitSoundCatContinuous/ArpitSoundCatContinuous.m similarity index 100% rename from Protocols/@Arpit_SoundCatContinuous/Arpit_SoundCatContinuous.m rename to Protocols/@ArpitSoundCatContinuous/ArpitSoundCatContinuous.m diff --git a/Protocols/@Arpit_SoundCatContinuous/SideSection.m b/Protocols/@ArpitSoundCatContinuous/SideSection.m similarity index 100% rename from Protocols/@Arpit_SoundCatContinuous/SideSection.m rename to Protocols/@ArpitSoundCatContinuous/SideSection.m diff --git a/Protocols/@Arpit_SoundCatContinuous/StimulatorSection.asv b/Protocols/@ArpitSoundCatContinuous/StimulatorSection.asv similarity index 100% rename from Protocols/@Arpit_SoundCatContinuous/StimulatorSection.asv rename to Protocols/@ArpitSoundCatContinuous/StimulatorSection.asv diff --git a/Protocols/@Arpit_SoundCatContinuous/StimulatorSection.m b/Protocols/@ArpitSoundCatContinuous/StimulatorSection.m similarity index 100% rename from Protocols/@Arpit_SoundCatContinuous/StimulatorSection.m rename to Protocols/@ArpitSoundCatContinuous/StimulatorSection.m diff --git a/Protocols/@Arpit_CentrePokeTraining/Arpit_CentrePokeTraining.m b/Protocols/@Arpit_CentrePokeTraining/ArpitCentrePokeTraining.m similarity index 100% rename from Protocols/@Arpit_CentrePokeTraining/Arpit_CentrePokeTraining.m rename to Protocols/@Arpit_CentrePokeTraining/ArpitCentrePokeTraining.m diff --git a/Protocols/@Arpit_CentrePokeTraining/Arpit_CentrePokeTrainingSMA.m b/Protocols/@Arpit_CentrePokeTraining/ArpitCentrePokeTrainingSMA.m similarity index 100% rename from Protocols/@Arpit_CentrePokeTraining/Arpit_CentrePokeTrainingSMA.m rename to Protocols/@Arpit_CentrePokeTraining/ArpitCentrePokeTrainingSMA.m diff --git a/Protocols/@Arpit_CentrePokeTraining/Arpit_CentrePokeTraining_SessionDefinition_AutoTrainingStages.m b/Protocols/@Arpit_CentrePokeTraining/ArpitCentrePokeTraining_SessionDefinition_AutoTrainingStages.m similarity index 100% rename from Protocols/@Arpit_CentrePokeTraining/Arpit_CentrePokeTraining_SessionDefinition_AutoTrainingStages.m rename to Protocols/@Arpit_CentrePokeTraining/ArpitCentrePokeTraining_SessionDefinition_AutoTrainingStages.m diff --git a/Protocols/@Arpit_CentrePokeTraining/Arpit_CentrePokeTraining.asv b/Protocols/@Arpit_CentrePokeTraining/Arpit_CentrePokeTraining.asv deleted file mode 100644 index e12319a4..00000000 --- a/Protocols/@Arpit_CentrePokeTraining/Arpit_CentrePokeTraining.asv +++ /dev/null @@ -1,346 +0,0 @@ -% Arpit_CentrePokeTraining protocol -% Arpit, 12 March 2025 - -function [obj] = Arpit_CentrePokeTraining(varargin) - -% Default object is of our own class (mfilename); -% we inherit only from Plugins - -obj = class(struct, mfilename, pokesplot2, saveload, sessionmodel2, soundmanager, soundui, ... - water, comments, soundtable, sqlsummary); - -%--------------------------------------------------------------- -% BEGIN SECTION COMMON TO ALL PROTOCOLS, DO NOT MODIFY -%--------------------------------------------------------------- - -% If creating an empty object, return without further ado: -if nargin==0 || (nargin==1 && ischar(varargin{1}) && strcmp(varargin{1}, 'empty')) - return; -end - -if isa(varargin{1}, mfilename) % If first arg is an object of this class itself, we are - % Most likely responding to a callback from - % a SoloParamHandle defined in this mfile. - if length(varargin) < 2 || ~ischar(varargin{2}) - error(['If called with a "%s" object as first arg, a second arg, a ' ... - 'string specifying the action, is required\n']); - else - action = varargin{2}; varargin = varargin(3:end); %#ok - end -else % Ok, regular call with first param being the action string. - action = varargin{1}; varargin = varargin(2:end); %#ok -end - -GetSoloFunctionArgs(obj); - - -%--------------------------------------------------------------- -% END OF SECTION COMMON TO ALL PROTOCOLS, MODIFY AFTER THIS LINE -%--------------------------------------------------------------- - -% ---- From here on is where you can put the code you like. -% -% Your protocol will be called, at the appropriate times, with the -% following possible actions: -% -% 'init' To initialize -- make figure windows, variables, etc. -% -% 'update' Called periodically within a trial -% -% 'prepare_next_trial' Called when a trial has ended and your protocol is expected -% to produce the StateMachine diagram for the next trial; -% i.e., somewhere in your protocol's response to this call, it -% should call "dispatcher('send_assembler', sma, -% prepare_next_trial_set);" where sma is the -% StateMachineAssembler object that you have prepared and -% prepare_next_trial_set is either a single string or a cell -% with elements that are all strings. These strings should -% correspond to names of states in sma. -% Note that after the prepare_next_trial call, further -% events may still occur while your protocol is thinking, -% before the new StateMachine diagram gets sent. These events -% will be available to you when 'state0' is called on your -% protocol (see below). -% -% 'trial_completed' Called when the any of the prepare_next_trial set -% of states is reached. -% -% 'close' Called when the protocol is to be closed. -% -% -% VARIABLES THAT DISPATCHER WILL ALWAYS INSTANTIATE FOR YOU AS READ_ONLY -% GLOBALS IN YOUR PROTOCOL: -% -% n_done_trials How many trials have been finished; when a trial reaches -% one of the prepare_next_trial states for the first -% time, this variable is incremented by 1. -% -% n_started_trials How many trials have been started. This variable gets -% incremented by 1 every time the state machine goes -% through state 0. -% -% parsed_events The result of running disassemble.m, with the -% parsed_structure flag set to 1, on all events from the -% start of the current trial to now. -% -% latest_parsed_events The result of running disassemble.m, with the -% parsed_structure flag set to 1, on all new events from -% the last time 'update' was called to now. -% -% raw_events All the events obtained in the current trial, not parsed -% or disassembled, but raw as gotten from the State -% Machine object. -% -% current_assembler The StateMachineAssembler object that was used to -% generate the State Machine diagram in effect in the -% current trial. -% -% Trial-by-trial history of parsed_events, raw_events, and -% current_assembler, are automatically stored for you in your protocol by -% dispatcher.m. - -switch action - - %% init - case 'init' - - hackvar = 10; SoloFunctionAddVars('SessionModel', 'ro_args', 'hackvar'); %#ok - SoloParamHandle(obj, 'myfig', 'saveable', 0); myfig.value = figure; - - % Make the title of the figure be the protocol name, and if someone tries - % to close this figure, call dispatcher's close_protocol function, so it'll know - % to take it off the list of open protocols. - name = mfilename; - set(value(myfig), 'Name', name, 'Tag', name, ... - 'closerequestfcn', 'dispatcher(''close_protocol'')', 'MenuBar', 'none'); - % At this point we have one SoloParamHandle, myfig - % Let's put the figure where we want it and give it a reasonable size: - set(value(myfig), 'Position', [485 144 850 680]); - - SoloParamHandle(obj, 'nsessions_healthy_number_of_pokes', 'value', 0, 'save_with_settings', 1); - SoloParamHandle(obj, 'post_DelComp_protocol', 'value', '', 'save_with_settings', 1); - SoloParamHandle(obj, 'post_DelComp_settings_filename', 'value', '', 'save_with_settings', 1); - - SoloParamHandle(obj, 'violation_history', 'value', []); - DeclareGlobals(obj, 'ro_args', {'violation_history'}); - SoloFunctionAddVars('ParamsSection', 'rw_args', 'violation_history'); - - SoloParamHandle(obj, 'timeout_history', 'value', []); - DeclareGlobals(obj, 'ro_args', {'timeout_history'}); - SoloFunctionAddVars('ParamsSection', 'rw_args', 'timeout_history'); - - SoloParamHandle(obj, 'stimulus_history', 'value', []); - DeclareGlobals(obj, 'ro_args', {'stimulus_history'}); - SoloFunctionAddVars('StimulusSection', 'rw_args', 'stimulus_history'); - - SoloParamHandle(obj, 'hit_history', 'value', []); - DeclareGlobals(obj, 'ro_args', {'hit_history'}); - SoloFunctionAddVars('SideSection', 'rw_args', 'hit_history'); - - SoundManagerSection(obj, 'init'); - x = 5; y = 5; % Initial position on main GUI window - [x, y] = SavingSection(obj, 'init', x, y); - - %% slow ramp up of water amount - %%the water volume is controlled by a 5-parameter logistic function: WaterAmount(t) = maxasymp + (minasymp/(1+(t/inflp)^slp).^assym) - NumeditParam(obj, 'maxasymp', 38, x,y,'label','maxasymp','TooltipString',... - 'the water volume is controlled by a 5-parameter logistic function: WaterAmount(trialnum) = maxasymp + (minasymp/(1+(trialnum/inflp)^slp).^assym)'); - next_row(y); - NumeditParam(obj, 'slp', 3, x,y,'label','slp','TooltipString','Water Modulation: Slope of the logistic function'); - next_row(y); - NumeditParam(obj, 'inflp', 350, x,y,'label','inflp','TooltipString','Water Modulation: concentration at the inflection point'); - next_row(y); - NumeditParam(obj, 'minasymp', -21, x,y,'label','inflp','TooltipString','Water Modulation: minimum asymptote'); - next_row(y); - NumeditParam(obj, 'assym', 0.7, x,y,'label','assym','TooltipString','Water Modulation: asymmetry factor'); - next_row(y); - DispParam(obj, 'trial_1', 0, x, y, 'TooltipString', 'uL on first trial'); - next_row(y); - DispParam(obj, 'trial_150', 0, x, y, 'TooltipString', 'uL on trial 150'); - next_row(y); - DispParam(obj, 'trial_300', 0, x, y, 'TooltipString', 'uL on trial 300'); - next_row(y); - set_callback({maxasymp;slp;inflp;minasymp;assym}, {mfilename, 'change_water_modulation_params'}); - feval(mfilename, obj, 'change_water_modulation_params'); - - %AthenaSMA changed to SoundCatSMA (From AthenaDelayComp) - SoloFunctionAddVars('ParamsSection', 'ro_args', ... - {'maxasymp';'slp';'inflp';'minasymp';'assym'}); - [x, y] = WaterValvesSection(obj, 'init', x, y); - - % For plotting with the pokesplot plugin, we need to tell it what - % colors to plot with: - my_state_colors = Arpit_CentrePokeTrainingSMA(obj, 'get_state_colors'); - % In pokesplot, the poke colors have a default value, so we don't need - % to specify them, but here they are so you know how to change them. - my_poke_colors = struct( ... - 'L', 0.6*[1 0.66 0], ... - 'C', [0 0 0], ... - 'R', 0.9*[1 0.66 0]); - - [x, y] = PokesPlotSection(obj, 'init', x, y, ... - struct('states', my_state_colors, 'pokes', my_poke_colors)); next_row(y); - - [x, y] = CommentsSection(obj, 'init', x, y); - % [x, y] = PunishmentSection(obj, 'init', x, y); %#ok - - next_column(x); y=5; - [x, y] = SessionPerformanceSection(obj, 'init', x, y); - [x, y] = ParamsSection(obj, 'init', x, y); %#ok - - [x, y] = SoundSection(obj,'init',x,y); - [stage_fig_x,stage_fig_y] = Training_ParamsSection(obj, 'init', x, y); - SoloParamHandle(obj, 'stage_fig_x', 'value', stage_fig_x); - SoloFunctionAddVars('ParamsSection', 'rw_args', 'stage_fig_x'); - SoloParamHandle(obj, 'stage_fig_y', 'value', stage_fig_y); - SoloFunctionAddVars('ParamsSection', 'rw_args', 'stage_fig_y'); - - figpos = get(double(gcf), 'Position'); - [expmtr, rname]=SavingSection(obj, 'get_info'); - HeaderParam(obj, 'prot_title', [mfilename ': ' expmtr ', ' rname], x, y, 'position', [10 figpos(4)-25, 800 20]); - - [x, y] = StimulusSection(obj,'init',x,y); - - Arpit_CentrePokeTrainingSMA(obj, 'init'); - - next_row(y); next_row(y); - SessionDefinition(obj, 'init', x, y, value(myfig)); next_row(y, 2); %#ok - - %% Before preparing the trial, start with the Bonsai app to control the USB based Camera - % Declare the folder location for saving the video files - current_dir = cd; - ratter_dir = extractBefore(current_dir,'ratter'); - main_dir_video = [ratter_dir 'ratter_Videos']; - date_str = regexprep(char(datetime('today','Format','yyyy-MM-dd')), '[^0-9]', ''); - video_foldername = sprintf('video_@%s_%s_%s_%s',name,expmtr,rname,date_str); - rat_dir = sprintf('%s\\%s\\%s',main_dir_video,expmtr,rname); - video_save_dir = sprintf('%s\\%s\\%s\\%s',main_dir_video,expmtr,rname,video_foldername); - % We have the general structure of folder save location, now need to - % check if there is any other folder for same date. We will add a - % alphabet in the end based upon the no. of files present. - if exist(rat_dir,'dir') == 7 - folderNames_rat_dir = {listing(find([listing.isdir])).name}; - folderNames_rat_dir = folderNames_rat_dir(~ismember(folderNames_rat_dir,{'.','..'})); % Remove the '.' and '..' entries (current and parent directories) - sessions_today = length(find(contains(folderNames_rat_dir,video_foldername))); % number of folders containing the video foldername - video_save_dir = [video_save_dir char(sessions_today + 97)]; - else - video_save_dir = [video_save_dir char(97)]; - end - mkdir(video_save_dir); - SoloParamHandle(obj, 'Video_Saving_Folder', 'value', video_save_dir); - SoloFunctionAddVars('Connect_Bonsai_Camera', 'ro_args', ... - {'Video_Saving_Folder'}); - Connect_Bonsai_Camera(obj,'init'); - - %% - - feval(mfilename, obj, 'prepare_next_trial'); - - %% change_water_modulation_params - case 'change_water_modulation_params' - display_guys = [1 150 300]; - for i=1:numel(display_guys) - t = display_guys(i); - - myvar = eval(sprintf('trial_%d', t)); - myvar.value = maxasymp + (minasymp/(1+(t/inflp)^slp).^assym); - end - - %% prepare next trial - case 'prepare_next_trial' - - ParamsSection(obj, 'prepare_next_trial'); - % Run SessionDefinition *after* ParamsSection so we know whether the trial was a violation or not - - % push_helper_vars_tosql(obj,n_done_trials); - - SessionDefinition(obj, 'next_trial'); - - StimulusSection(obj,'prepare_next_trial'); - SoundManagerSection(obj, 'send_not_yet_uploaded_sounds'); - [sma, prepare_next_trial_states] = Arpit_CentrePokeTrainingSMA(obj, 'prepare_next_trial'); - - % Default behavior of following call is that every 20 trials, the data - % gets saved, not interactive, no commit to CVS. - SavingSection(obj, 'autosave_data'); - - CommentsSection(obj, 'clear_history'); % Make sure we're not storing unnecessary history - if n_done_trials==1 % Auto-append date for convenience. - CommentsSection(obj, 'append_date'); CommentsSection(obj, 'append_line', ''); - end - - if n_done_trials==1 - [expmtr, rname]=SavingSection(obj, 'get_info'); - prot_title.value=[mfilename ' on rig ' get_hostname ' : ' expmtr ', ' rname '. Started at ' datestr(now, 'HH:MM')]; - end - - % try - % send_n_done_trials(obj); - % end - - %% trial_completed - case 'trial_completed' - % Change the video trial - Connect_Bonsai_Camera(obj,'next_trial'); - % Update the Metrics Calculated - SessionPerformanceSection(obj, 'evaluate'); - % Do any updates in the protocol that need doing: - feval(mfilename, 'update'); - % And PokesPlot needs completing the trial: - PokesPlotSection(obj, 'trial_completed'); - - %% update - case 'update' - PokesPlotSection(obj, 'update'); - - - %% close - case 'close' - PokesPlotSection(obj, 'close'); - ParamsSection(obj, 'close'); - StimulusSection(obj,'close'); - Connect_Bonsai_Camera(obj,'close'); - if exist('myfig', 'var') && isa(myfig, 'SoloParamHandle') && ishandle(value(myfig)) %#ok - delete(value(myfig)); - end - delete_sphandle('owner', ['^@' class(obj) '$']); - - - %% end_session - case 'end_session' - prot_title.value = [value(prot_title) ', Ended at ' datestr(now, 'HH:MM')]; - - - %% pre_saving_settings - case 'pre_saving_settings' - - StimulusSection(obj,'hide'); - SessionDefinition(obj, 'run_eod_logic_without_saving'); - perf = SessionPerformanceSection(obj, 'evaluate'); - cp_durs = ParamsSection(obj, 'get_cp_history'); - - [stim1dur] = ParamsSection(obj,'get_stimdur_history'); - -% CommentsSection(obj, 'append_line', ... -% sprintf(['ntrials = %d, violations = %.2f, timeouts=%.2f, hits = %.2f\n', ... -% 'pre-Go cue went from %.3f to %.3f (delta=%.3f)\n', ... -% 'Low = %.2f, High = %.2f'], ... -% perf(1), perf(2), perf(3), perf(6), cp_durs(1), cp_durs(end), cp_durs(end)-cp_durs(1), classperf(1),classperf(2))); - - pd.hits=hit_history(:); - pd.sides=previous_sides(:); - pd.viols=violation_history(:); - pd.timeouts=timeout_history(:); - pd.cp_durs=cp_durs(:); - pd.stim1dur=stim1dur(:); - - - sendsummary(obj,'protocol_data',pd); - - %% otherwise - otherwise - warning('Unknown action! "%s"\n', action); -end - -return; - diff --git a/Protocols/@Arpit_CentrePokeTraining/Connect_Bonsai_Camera.asv b/Protocols/@Arpit_CentrePokeTraining/Connect_Bonsai_Camera.asv deleted file mode 100644 index 4ec91744..00000000 --- a/Protocols/@Arpit_CentrePokeTraining/Connect_Bonsai_Camera.asv +++ /dev/null @@ -1,246 +0,0 @@ -function Connect_Bonsai_Camera(obj,action) - -GetSoloFunctionArgs(obj); - - -% C:\RatterVideos\ExpName\RateName\Videos_Protocolname_ExpName_RatName_date -% (the same format as Data file) - -%%%%%%%%% DO NOT CHANGE THEM UNLESS YOU MAKE THE SAME CHANGES IN BONSAI %%%%%%%%% - -% UDP Connection Parameters to connect to Bonsai -bonsaiComputerIP = '127.0.0.1'; % IP address of the PC running Bonsai (use '127.0.0.1' if same PC) -bonsaiComputer_name = 'Receiver'; -bonsaiUdpPort = 9090; % Port configured in Bonsai's UdpReceiver -% Sending the Command to Turn on the Camera. Remember to use this -% as in Bonsai I am using the address as /camera and using -% condition to compare the arriving message. The message could only -% be 'start' or 'stop' with '/camera' address for bonsai to react -startCommand = "start"; % Use string type. MUST MATCH Bonsai Filter Value -stopCommand = "stop"; % Use string type. MUST MATCH Bonsai Filter Value -camera_command_address = "/camera"; % Use string type. MUST MATCH Bonsai Address Value -recording_command_address = "/record"; % Use string type. MUST MATCH Bonsai Address Value - -% bonsai_path = 'C:\Users\Turin\Downloads\Bonsai\Bonsai.exe'; % Path of Bonsai App -scriptFullPath = mfilename('fullpath'); % Path of running the current script -scriptDirectory = fileparts(scriptFullPath); -bonsai_workflow_Path = fullfile(scriptDirectory,'Bonsai','Camera_Control.bonsai'); -foundworkflow = exist("bonsai_workflow_Path",'file'); -% if ~foundworkflow -% warning('could not find bonsai executable, please insert it manually'); -% [fname fpath] = uigetfile( '*.exe', 'Provide the path to Bonsai executable'); -% pathout = fullfile(fpath, fname); -% end -% file_path = 'C:\Users\Turin\Desktop\bonsai_script.bonsai'; % Path for .bonsai file - -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - -switch action - - case 'init' - - % --- Create UDP Port Object --- - % This object can send to any destination without prior connection - udpSender = udpport("datagram","IPV4",'LocalHost',bonsaiComputerIP,... - 'LocalPort',9091,'Tag','MATLABSender'); - - SoloParamHandle(obj, 'UDPSender', 'value', udpSender); - - % Now that we have created the UPD connection, we can send commands - % over OSC. Before that we need to open the Bonsai App - - % bonsai_path = bonsaiPath(32); - % command = sprintf('"%s" "%s" --start', bonsai_path, bonsai_file_path); - % system([command, ' &']); - - runBonsaiWorkflow(bonsai_workflow_Path); - - % Before starting the streaming of Camera, I need to send the - % file directory for saving the file otherwise Bonsai can run into - % error as it will try saving files in the predefined folder in - % bonsai and that can conflict with file already present there. - - oscMsg_file_directory = createOSCMessage(recording_command_address, [value(Video_Saving_Folder) '\Trial.avi']); - % write(udpSender, oscMsg_file_directory, "uint8", bonsaiComputerIP,bonsaiUdpPort); - - % OSC message to start the camera - oscMsg_Camera_start = createOSCMessage(camera_command_address, startCommand); - % the command to send message to Bonsai - write(udpSender, oscMsg_Camera_start, "uint8", bonsaiComputerIP,bonsaiUdpPort); - pause(2); - write(udpSender, oscMsg_Camera_start, "uint8", bonsaiComputerIP,bonsaiUdpPort); - - pause(5); - % NOTE: Ideally I should start saving the trials once the experimenter presses - % Run either on dispatcher or Runrats. But, I dont want to make the changes there - % so would start recording as soon as the protocol is loaded and camera starts streaming - - write(udpSender, oscMsg_file_directory, "uint8", bonsaiComputerIP,bonsaiUdpPort); - - case 'next_trial' - - % in this I send a command to bonsai so that it creates a new file - % for each trial - - oscMsg_file_directory = createOSCMessage(recording_command_address, [value(Video_Saving_Folder) '\Trial.avi']); - write(value(UDPSender), oscMsg_file_directory, "uint8", bonsaiComputerIP,bonsaiUdpPort); - - case 'stop' - % this stops saving and streaming of the camera - oscMsg_Camera_stop = createOSCMessage("/camera", stopCommand); - write(value(UDPSender), oscMsg_Camera_stop, "uint8", bonsaiComputerIP,bonsaiUdpPort); - - case 'close' - - % this would stop the workflow - oscMsg_Camera_stop = createOSCMessage("/camera", stopCommand); - write(value(UDPSender), oscMsg_Camera_stop, "uint8", bonsaiComputerIP,bonsaiUdpPort); - - delete(value(UDPSender)); % delete the UDP Port - - % this is to kill the Bonsai app - system('taskkill /F /IM Bonsai.exe'); - - -end - -end - -%%%%%%%%%%%%%%% ADD 0N Functions %%%%%%%%%%%%%%%%%%%% - - - -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - -function oscPacket = createOSCMessage(address, arg) - % Ensure address and arg are char (not string) - if isstring(address) - address = char(address); - end - if isstring(arg) - arg = char(arg); - end - - % Helper to pad with nulls to 4-byte alignment - function bytes = padNulls(str) - strBytes = uint8([str, 0]); % null-terminated - padding = mod(4 - mod(length(strBytes), 4), 4); - bytes = [strBytes, zeros(1, padding, 'uint8')]; - end - - % Address (e.g., "/camera", "/record") - addrBytes = padNulls(address); - - % Type Tag String (e.g., ",s" for a single string argument) - typeTag = ',s'; - tagBytes = padNulls(typeTag); - - % Argument (e.g., "start") - argBytes = padNulls(arg); - - % Combine all parts - oscPacket = [addrBytes, tagBytes, argBytes]; -end - - -function runBonsaiWorkflow(workflowPath, addOptArray, bonsaiExePath, external) - -%% addOptArray = {'field', 'val', 'field2', 'val2'}; -%% check the input -switch nargin - case 1 - bonsaiExePath = bonsaiPath(32); - addOptArray = ''; - addFlag = 0; - external = 1; - case 2 - bonsaiExePath = bonsaiPath(32); - addFlag = 1; - external = 1; - case 3 - addFlag = 1; - external = 1; - case 4 - if isempty(bonsaiExePath) - bonsaiExePath = bonsaiPath(32); - end - addFlag = 1; - - otherwise - error('Too many variables'); -end - - - -%% write the command -optSuffix = '-p:'; -startFlag = '--start'; -noEditorFlag = '--noeditor'; %use this instead of startFlag to start Bonsai without showing the editor -cmdsep = ' '; -if external -command = [['"' bonsaiExePath '"'] cmdsep ['"' workflowPath '"'] cmdsep startFlag]; -else -command = [['"' bonsaiExePath '"'] cmdsep ['"' workflowPath '"'] cmdsep noEditorFlag]; -end -ii = 1; -commandAppend = ''; -if addFlag - while ii < size(addOptArray,2) - commandAppend = [commandAppend cmdsep optSuffix addOptArray{ii} '="' num2str(addOptArray{ii+1}) '"' ]; - ii = ii+2; - end -end - -if external - command = [command commandAppend ' &']; -else - command = [command commandAppend]; -end -%% run the command -[sysecho] = system(command); -end - - -%% parse the addOptArray -function out = parseAddOptArray(addOptArray) -addOpt = cell(length(addOptArray)/2,2); -for ii = 1:length(addOpt) - addOpt{ii,1} = addOptArray{2*ii-1}; %gets the propriety - addOpt{ii,2} = addOptArray{2*ii}; %gets the value -end -out = addOpt; -end - - -function pathout = bonsaiPath(x64x86) - -%%version of bonsai -if nargin <1; x64x86 = 64; end -switch x64x86 - case {'64', 64, 'x64', 1, '64bit'} - bonsaiEXE = 'Bonsai64.exe'; - case {32, '32', 'x86', 0, '32bit'} - bonsaiEXE = 'Bonsai.exe'; -end - -dirs = {'appdata', 'localappdata', 'programfiles', 'programfiles(x86)','USERPROFILE'}; -foundBonsai = 0; -dirIDX = 1; -while ~foundBonsai && (dirIDX <= length(dirs)) - pathout = fullfile(getenv(dirs{dirIDX}),'Bonsai', bonsaiEXE); - foundBonsai = exist(pathout, 'file'); - dirIDX = dirIDX +1; -end - -if ~foundBonsai - pathout = fullfile(getenv(dirs{5}),'Desktop',bonsaiEXE); - foundBonsai = exist(pathout, 'file'); -end - -if ~foundBonsai - warning('could not find bonsai executable, please insert it manually'); - [fname fpath] = uigetfile( '*.exe', 'Provide the path to Bonsai executable'); - pathout = fullfile(fpath, fname); -end - -end diff --git a/Protocols/@Arpit_CentrePokeTraining/Connect_Bonsai_Camera.m b/Protocols/@Arpit_CentrePokeTraining/Connect_Bonsai_Camera.m index a6b41822..03b03499 100644 --- a/Protocols/@Arpit_CentrePokeTraining/Connect_Bonsai_Camera.m +++ b/Protocols/@Arpit_CentrePokeTraining/Connect_Bonsai_Camera.m @@ -54,7 +54,7 @@ function Connect_Bonsai_Camera(obj,action) % system([command, ' &']); runBonsaiWorkflow(bonsai_workflow_Path); - + pause(5); % Before starting the streaming of Camera, I need to send the % file directory for saving the file otherwise Bonsai can run into % error as it will try saving files in the predefined folder in @@ -67,9 +67,7 @@ function Connect_Bonsai_Camera(obj,action) oscMsg_Camera_start = createOSCMessage(camera_command_address, startCommand); % the command to send message to Bonsai write(udpSender, oscMsg_Camera_start, "uint8", bonsaiComputerIP,bonsaiUdpPort); - pause(2); - write(udpSender, oscMsg_Camera_start, "uint8", bonsaiComputerIP,bonsaiUdpPort); - + pause(5); % NOTE: Ideally I should start saving the trials once the experimenter presses % Run either on dispatcher or Runrats. But, I dont want to make the changes there @@ -99,8 +97,9 @@ function Connect_Bonsai_Camera(obj,action) delete(value(UDPSender)); % delete the UDP Port - % this is to kill the Bonsai app + % this is to kill the Bonsai app and cmd system('taskkill /F /IM Bonsai.exe'); + system('taskkill /F /IM cmd.exe'); end diff --git a/Protocols/@Arpit_CentrePokeTraining/StimulusSection.m b/Protocols/@Arpit_CentrePokeTraining/StimulusSection.m index 1629884e..c6776a0e 100644 --- a/Protocols/@Arpit_CentrePokeTraining/StimulusSection.m +++ b/Protocols/@Arpit_CentrePokeTraining/StimulusSection.m @@ -532,11 +532,16 @@ %% Case close case 'close' + set(value(myfig), 'Visible', 'off'); + set(value(stim_dist_fig), 'Visible', 'off'); % Delete all SoloParamHandles who belong to this object and whose % fullname starts with the name of this mfile: if exist('myfig', 'var') && isa(myfig, 'SoloParamHandle') && ishandle(value(myfig)) %#ok delete(value(myfig)); end + if exist('stim_dist_fig', 'var') && isa(stim_dist_fig, 'SoloParamHandle') && ishandle(value(stim_dist_fig)) %#ok + delete(value(stim_dist_fig)); + end delete_sphandle('owner', ['^@' class(obj) '$'], ... 'fullname', ['^' mfilename]); diff --git a/Protocols/@Arpit_CentrePokeTraining/private/StimuliDistribution_plot.m b/Protocols/@Arpit_CentrePokeTraining/private/StimuliDistribution_plot.m index e6723f6c..8c3c100d 100644 --- a/Protocols/@Arpit_CentrePokeTraining/private/StimuliDistribution_plot.m +++ b/Protocols/@Arpit_CentrePokeTraining/private/StimuliDistribution_plot.m @@ -164,5 +164,7 @@ function StimuliDistribution_plot(ax,plot_x_range, Rule, distribution_left,mean_ end plot(ax,plot_x,plot_y,'b','LineWidth', 2); +ylim(ax,[0 max(plot_y)]); +xlim(ax,[min(plot_x) max(plot_x)]) end \ No newline at end of file diff --git a/Protocols/@Arpit_SoundCalibration/Arpit_SoundCalibration.asv b/Protocols/@Arpit_SoundCalibration/Arpit_SoundCalibration.asv deleted file mode 100644 index 3f61bebe..00000000 --- a/Protocols/@Arpit_SoundCalibration/Arpit_SoundCalibration.asv +++ /dev/null @@ -1,769 +0,0 @@ - -%This is a new version of the old stand alone rigtester.m did not run -%through dispatcher. This is treated like any other protocol. It is -%designed to be plastic, looking at a rigs settings files, determining -%the input and output lines, and building a GUI specific to that rigs -%componants. -% -%Written by Chuck 2017 - -% Make sure you ran newstartup, then dispatcher('init'), and you're good to -% go! -% - -function [obj] = Arpit_SoundCalibration(varargin) - -% Default object is of our own class (mfilename); in this simplest of -% protocols, we inherit only from Plugins/@pokesplot - -obj = class(struct, mfilename, soundmanager, soundui); - -%--------------------------------------------------------------- -% BEGIN SECTION COMMON TO ALL PROTOCOLS, DO NOT MODIFY -%--------------------------------------------------------------- - -% If creating an empty object, return without further ado: -if nargin==0 || (nargin==1 && ischar(varargin{1}) && strcmp(varargin{1}, 'empty')), - return; -end; - -if isa(varargin{1}, mfilename), % If first arg is an object of this class itself, we are - % Most likely responding to a callback from - % a SoloParamHandle defined in this mfile. - if length(varargin) < 2 || ~ischar(varargin{2}), - error(['If called with a "%s" object as first arg, a second arg, a ' ... - 'string specifying the action, is required\n']); - else action = varargin{2}; varargin = varargin(3:end); %#ok - end; -else % Ok, regular call with first param being the action string. - action = varargin{1}; varargin = varargin(2:end); %#ok -end; -if ~ischar(action), error('The action parameter must be a string'); end; - -GetSoloFunctionArgs(obj); - -%--------------------------------------------------------------- -% END OF SECTION COMMON TO ALL PROTOCOLS, MODIFY AFTER THIS LINE -%--------------------------------------------------------------- - - -% ---- From here on is where you can put the code you like. -% -% Your protocol will be called, at the appropriate times, with the -% following possible actions: -% -% 'init' To initialize -- make figure windows, variables, etc. -% -% 'update' Called periodically within a trial -% -% 'prepare_next_trial' Called when a trial has ended and your protocol -% is expected to produce the StateMachine diagram for the next -% trial; i.e., somewhere in your protocol's response to this -% call, it should call "dispatcher('send_assembler', sma, -% prepare_next_trial_set);" where sma is the -% StateMachineAssembler object that you have prepared and -% prepare_next_trial_set is either a single string or a cell -% with elements that are all strings. These strings should -% correspond to names of states in sma. -% Note that after the 'prepare_next_trial' call, further -% events may still occur in the RTLSM while your protocol is thinking, -% before the new StateMachine diagram gets sent. These events -% will be available to you when 'trial_completed' is called on your -% protocol (see below). -% -% 'trial_completed' Called when 'state_0' is reached in the RTLSM, -% marking final completion of a trial (and the start of -% the next). -% -% 'close' Called when the protocol is to be closed. -% -% -% VARIABLES THAT DISPATCHER WILL ALWAYS INSTANTIATE FOR YOU IN YOUR -% PROTOCOL: -% -% (These variables will be instantiated as regular Matlab variables, -% not SoloParamHandles. For any method in your protocol (i.e., an m-file -% within the @your_protocol directory) that takes "obj" as its first argument, -% calling "GetSoloFunctionArgs(obj)" will instantiate all the variables below.) -% -% -% n_done_trials How many trials have been finished; when a trial reaches -% one of the prepare_next_trial states for the first -% time, this variable is incremented by 1. -% -% n_started trials How many trials have been started. This variable gets -% incremented by 1 every time the state machine goes -% through state 0. -% -% parsed_events The result of running disassemble.m, with the -% parsed_structure flag set to 1, on all events from the -% start of the current trial to now. -% -% latest_events The result of running disassemble.m, with the -% parsed_structure flag set to 1, on all new events from -% the last time 'update' was called to now. -% -% raw_events All the events obtained in the current trial, not parsed -% or disassembled, but raw as gotten from the State -% Machine object. -% -% current_assembler The StateMachineAssembler object that was used to -% generate the State Machine diagram in effect in the -% current trial. -% -% Trial-by-trial history of parsed_events, raw_events, and -% current_assembler, are automatically stored for you in your protocol by -% dispatcher.m. See the wiki documentation for information on how to access -% those histories from within your protocol and for information. -% -% - - -switch action, - - %--------------------------------------------------------------- - % CASE INIT - %--------------------------------------------------------------- - - case 'init' - - % Make default figure. We remember to make it non-saveable; on next run - % the handle to this figure might be different, and we don't want to - % overwrite it when someone does load_data and some old value of the - % fig handle was stored as SoloParamHandle "myfig" - SoloParamHandle(obj, 'myfig', 'saveable', 0); myfig.value = double(figure); - - % Make the title of the figure be the protocol name, and if someone tries - % to close this figure, call dispatcher's close_protocol function, so it'll know - % to take it off the list of open protocols. - name = mfilename; - set(value(myfig), 'Name', name, 'Tag', name, ... - 'closerequestfcn', [mfilename,'(''close'');'], 'MenuBar', 'none'); - - alldio = bSettings('get','DIOLINES','ALL'); - allinp = bSettings('get','INPUTLINES','ALL'); - dionums = cell2mat(alldio(:,2)); - inpnums = cell2mat(allinp(:,2)); - - for i = 1:16 - inp1 = find(inpnums == i,1,'first'); - dio1 = find(dionums == 2^(( i-1)*2), 1,'first'); - dio2 = find(dionums == 2^(((i-1)*2)+1),1,'first'); - - if isempty(inp1) && isempty(dio1) && isempty(dio2) - continue; - end - - if ~isempty(inp1) - linegroups{i}{1,1} = allinp{inp1,2}; %#ok - linegroups{i}{1,2} = allinp{inp1,1}; %#ok - else - linegroups{i}{1,1} = []; %#ok - linegroups{i}{1,2} = []; %#ok - end - if ~isempty(dio1) - linegroups{i}{2,1} = alldio{dio1,2}; %#ok - linegroups{i}{2,2} = alldio{dio1,1}; %#ok - else - linegroups{i}{2,1} = []; %#ok - linegroups{i}{2,2} = []; %#ok - end - if ~isempty(dio2) - linegroups{i}{3,1} = alldio{dio2,2}; %#ok - linegroups{i}{3,2} = alldio{dio2,1}; %#ok - else - linegroups{i}{3,1} = []; %#ok - linegroups{i}{3,2} = []; %#ok - end - end - - % Generate the sounds we need. - soundserver = bSettings('get','RIGS','sound_machine_server'); - if ~isempty(soundserver) - sr = SoundManagerSection(obj,'get_sample_rate'); - Fs=sr; - lfreq=2000; - hfreq=20000; - freq = 100; - T = 5; - fcut = 110; - filter_type = 'GAUS'; - A1_sigma = 0.0500; - A2_sigma = 0.0306; %0.1230;%0.0260; - A3_sigma = 0.0187; %0.0473;%0.0135; - A4_sigma = 0.0114; %0.0182;%0.0070; - A5_sigma = 0.0070; - [rawA1 rawA2 normA1 normA2]=noisestim(1,1,T,fcut,Fs,filter_type); - modulator=singlenoise(1,T,[lfreq hfreq],Fs,'BUTTER'); - AUD1=normA1(1:T*sr).*modulator(1:T*sr).*A1_sigma; - AUD2=normA1(1:T*sr).*modulator(1:T*sr).*A2_sigma; - AUD3=normA1(1:T*sr).*modulator(1:T*sr).*A3_sigma; - AUD4=normA1(1:T*sr).*modulator(1:T*sr).*A4_sigma; - AUD5=normA1(1:T*sr).*modulator(1:T*sr).*A5_sigma; - % snd = MakeBupperSwoop(sr,0,freq,freq, len/2, len/2, 0, 0.1, 'F1_volume_factor',0.07,'F2_volume_factor',0.07); - % silence_length = 0; - % presound_silence = zeros(1,sr*silence_length/1000); - % snd = [presound_silence, snd]; - % - % SoundManagerSection(obj,'declare_new_sound','left_sound', [snd; zeros(1,size(snd,2))]); - % SoundManagerSection(obj,'declare_new_sound','right_sound', [zeros(1,size(snd,2)); snd]); - % - % SoundManagerSection(obj,'loop_sound','left_sound',1); - % SoundManagerSection(obj,'loop_sound','right_sound',1); - % - % SoundManager Section(obj,'declare_new_sound','center_sound',[snd;snd([ceil((sr / freq) / 2):end,1:ceil((sr / freq) / 2)-1])]); - % SoundManagerSection(obj,'loop_sound','center_sound',1); - % - if ~isempty(AUD2) - SoundManagerSection(obj, 'declare_new_sound', 'left_sound', [AUD2'; AUD2']) - end - if ~isempty(AUD1) - SoundManagerSection(obj, 'declare_new_sound', 'center_sound', [AUD1'; AUD1']) - end - if ~isempty(AUD3) - SoundManagerSection(obj, 'declare_new_sound', 'right_sound', [AUD3'; AUD3']) - end - if ~isempty(AUD4) - SoundManagerSection(obj, 'declare_new_sound', 'fourth_sound', [AUD4'; AUD4']) - end - if ~isempty(AUD5) - SoundManagerSection(obj, 'declare_new_sound', 'fifth_sound', [AUD5'; AUD5']) - end - SoundManagerSection(obj, 'send_not_yet_uploaded_sounds'); - end - - % Get monitor dimensions for dynamic GUI sizing - MP = get(0,'MonitorPositions'); - - % Calculate individual group width based on screen dimensions and number of groups - groupwidth = floor((MP(3)/2)/numel(linegroups)); - - padding = 10 % padding around GUI elements - - % Calculate total width needed for all groups combined - total_width = floor(numel(linegroups) * groupwidth+padding); - total_height = 400 % total height of GUI - - label_height = 140 % height of port label - button_height = 25 - - % Center the GUI horizontally on screen - left_pos = floor((MP(3) - total_width) / 2); - - % Set figure position with centered alignment and fixed height of 400 pixels - % position vector: [left-right, down-up, width, height], the origo is - % the bottom left corner - set(value(myfig), 'Position', [left_pos, floor((MP(4)-total_height)/2), total_width, total_height]); - - % Initialize array to store line names - line_names = []; - - % Iterate through each line group - for i = 1:numel(linegroups) - % Check if current group exists and has valid parameters - if ~isempty(linegroups{i}) && ~isempty(linegroups{i}{1,2}) - % port label position - SubheaderParam( ... - obj, ... - ['Input',linegroups{i}{1,2}], ... - {linegroups{i}{1,2};0},0,0, ... - 'position',[((i-1)*groupwidth)+padding, ... - total_height-padding-label_height, ... - groupwidth-padding, ... - label_height ... - ]); - - % port label aesthetics - set(get_ghandle(eval(['Input',linegroups{i}{1,2}])), ... - 'FontSize',32, ... % Large font size for visibility - 'BackgroundColor',[0,1,0], ... % Green background - 'HorizontalAlignment','center'); % Center align text - - % Store line name for later reference - line_names(end+1) = linegroups{i}{1,2}; %#ok - - % Add sound controls based on line identifier - if strcmp(linegroups{i}{1,2},'L') && ~isempty(soundserver) - % Left channel sound toggle - ToggleParam(obj, ... - 'LeftSound', 0,0,0, ... - 'position',[((i-1)*groupwidth)+padding, padding, groupwidth-padding,button_height], ... - 'OnString', 'Sound Two ON', ... - 'OffString', 'Sound Two OFF'); - set_callback(LeftSound,{mfilename,'play_left_sound'}); - - elseif strcmp(linegroups{i}{1,2},'R') && ~isempty(soundserver) - % Right channel sound toggle - ToggleParam(obj, ... - 'RightSound', 0,0,0, ... - 'position',[((i-1)*groupwidth)+padding, padding, groupwidth-padding,button_height], ... - 'OnString', 'Sound Three ON', ... - 'OffString', 'Sound Three OFF'); - set_callback(RightSound,{mfilename,'play_right_sound'}); - - elseif strcmp(linegroups{i}{1,2},'C') && ~isempty(soundserver) - % Center channel sound toggle - ToggleParam(obj, ... - 'CenterSound', 0,0,0, ... - 'position',[((i-1)*groupwidth)+padding, padding, groupwidth-padding,button_height], ... - 'OnString', 'Sound One ON', ... - 'OffString', 'Sound One OFF'); - set_callback(CenterSound,{mfilename,'play_center_sound'}); - - elseif strcmp(linegroups{i}{1,2},'A') && ~isempty(soundserver) - % Additional channel sound toggle - ToggleParam(obj, ... - 'FourthSound', 0,0,0, ... - 'position',[((i-1)*groupwidth)+padding, padding, groupwidth-padding,button_height], ... - 'OnString', 'Sound Four ON', ... - 'OffString', 'Sound Four OFF'); - set_callback(FourthSound,{mfilename,'play_fourth_sound'}); - end - - % Handle case for empty line identifier but existing group - elseif ~isempty(linegroups{i}) && isempty(linegroups{i}{1,2}) - ToggleParam(obj, ... - 'FifthSound', 0,0,0, ... - 'position',[((i-1)*groupwidth)+padding, padding, groupwidth-padding,button_height], ... - 'OnString', 'Sound Five ON', ... - 'OffString', 'Sound Five OFF'); - set_callback(FifthSound,{mfilename,'play_fifth_sound'}); - end - - % Add first digital I/O control if available - if ~isempty(linegroups{i}) && ~isempty(linegroups{i}{2,2}) - ToggleParam(obj, ... - ['DIO',num2str(i),'_1'],0,0,0, ... - 'position',[((i-1)*groupwidth)+padding, 2*button_height+padding, groupwidth-padding,button_height], ... - 'OnString', [linegroups{i}{2,2},' ON'], ... - 'OffString', [linegroups{i}{2,2},' OFF']); - set_callback(eval(['DIO',num2str(i),'_1']),{mfilename,['toggle',num2str(i),'_1']}); - end - - % Add second digital I/O control if available - if ~isempty(linegroups{i}) && ~isempty(linegroups{i}{3,2}) - ToggleParam(obj, ... - ['DIO',num2str(i),'_2'],0,0,0, ... - 'position',[((i-1)*groupwidth)+padding, button_height+padding, groupwidth-padding,button_height], ... - 'OnString', [linegroups{i}{3,2},' ON'], ... - 'OffString', [linegroups{i}{3,2},' OFF']); - set_callback(eval(['DIO',num2str(i),'_2']),{mfilename,['toggle',num2str(i),'_2']}); - end - end - - - SoloParamHandle(obj,'LineGroups','value',linegroups); - SoloParamHandle(obj,'LineNames','value',line_names); - - scr = timer; - set(scr,'Period', 0.2,'ExecutionMode','FixedRate','TasksToExecute',Inf,... - 'BusyMode','drop','TimerFcn',[mfilename,'(''close_continued'')']); - SoloParamHandle(obj, 'stopping_complete_timer', 'value', scr); - - SoundCalibration(obj,'prepare_next_trial'); - - dispatcher('Run'); - - - case 'play_left_sound' - %% play_left_sound - if value(LeftSound) == 1 - SoundManagerSection(obj,'play_sound','left_sound'); - else - SoundManagerSection(obj,'stop_sound','left_sound'); - end - - case 'play_right_sound' - %% play_right_sound - if value(RightSound) == 1 - SoundManagerSection(obj,'play_sound','right_sound'); - else - SoundManagerSection(obj,'stop_sound','right_sound'); - end - - case 'play_center_sound' - %% play_center_sound - if value(CenterSound) == 1 - SoundManagerSection(obj,'play_sound','center_sound'); - else - SoundManagerSection(obj,'stop_sound','center_sound'); - end - - case 'play_fourth_sound' - %% play_fourth_sound - if value(FourthSound) == 1 - SoundManagerSection(obj,'play_sound','fourth_sound'); - else - SoundManagerSection(obj,'stop_sound','fourth_sound'); - end - case 'play_fifth_sound' - %% play_fifth_sound - if value(FifthSound) == 1 - SoundManagerSection(obj,'play_sound','fifth_sound'); - else - SoundManagerSection(obj,'stop_sound','fifth_sound'); - end - case 'toggle1_1' - %% case toggle1_1 - linegroups = value(LineGroups); - dispatcher('toggle_bypass',log2(linegroups{1}{2,1})); - - case 'toggle1_2' - %% case toggle1_2 - linegroups = value(LineGroups); - dispatcher('toggle_bypass',log2(linegroups{1}{3,1})); - - case 'toggle2_1' - %% case toggle1_1 - linegroups = value(LineGroups); - dispatcher('toggle_bypass',log2(linegroups{2}{2,1})); - - case 'toggle2_2' - %% case toggle1_2 - linegroups = value(LineGroups); - dispatcher('toggle_bypass',log2(linegroups{2}{3,1})); - - case 'toggle3_1' - %% case toggle1_1 - linegroups = value(LineGroups); - dispatcher('toggle_bypass',log2(linegroups{3}{2,1})); - - case 'toggle3_2' - %% case toggle1_2 - linegroups = value(LineGroups); - dispatcher('toggle_bypass',log2(linegroups{3}{3,1})); - - case 'toggle4_1' - %% case toggle1_1 - linegroups = value(LineGroups); - dispatcher('toggle_bypass',log2(linegroups{4}{2,1})); - - case 'toggle4_2' - %% case toggle1_2 - linegroups = value(LineGroups); - dispatcher('toggle_bypass',log2(linegroups{4}{3,1})); - - case 'toggle5_1' - %% case toggle1_1 - linegroups = value(LineGroups); - dispatcher('toggle_bypass',log2(linegroups{5}{2,1})); - - case 'toggle5_2' - %% case toggle1_2 - linegroups = value(LineGroups); - dispatcher('toggle_bypass',log2(linegroups{5}{3,1})); - - case 'toggle6_1' - %% case toggle1_1 - linegroups = value(LineGroups); - dispatcher('toggle_bypass',log2(linegroups{6}{2,1})); - - case 'toggle6_2' - %% case toggle1_2 - linegroups = value(LineGroups); - dispatcher('toggle_bypass',log2(linegroups{6}{3,1})); - - case 'toggle7_1' - %% case toggle1_1 - linegroups = value(LineGroups); - dispatcher('toggle_bypass',log2(linegroups{7}{2,1})); - - case 'toggle7_2' - %% case toggle1_2 - linegroups = value(LineGroups); - dispatcher('toggle_bypass',log2(linegroups{7}{3,1})); - - case 'toggle8_1' - %% case toggle1_1 - linegroups = value(LineGroups); - dispatcher('toggle_bypass',log2(linegroups{8}{2,1})); - - case 'toggle8_2' - %% case toggle1_2 - linegroups = value(LineGroups); - dispatcher('toggle_bypass',log2(linegroups{8}{3,1})); - - case 'toggle9_1' - %% case toggle1_1 - linegroups = value(LineGroups); - dispatcher('toggle_bypass',log2(linegroups{9}{2,1})); - - case 'toggle9_2' - %% case toggle1_2 - linegroups = value(LineGroups); - dispatcher('toggle_bypass',log2(linegroups{9}{3,1})); - - case 'toggle10_1' - %% case toggle1_1 - linegroups = value(LineGroups); - dispatcher('toggle_bypass',log2(linegroups{10}{2,1})); - - case 'toggle10_2' - %% case toggle1_2 - linegroups = value(LineGroups); - dispatcher('toggle_bypass',log2(linegroups{10}{3,1})); - - case 'toggle11_1' - %% case toggle1_1 - linegroups = value(LineGroups); - dispatcher('toggle_bypass',log2(linegroups{11}{2,1})); - - case 'toggle11_2' - %% case toggle1_2 - linegroups = value(LineGroups); - dispatcher('toggle_bypass',log2(linegroups{11}{3,1})); - - case 'toggle12_1' - %% case toggle1_1 - linegroups = value(LineGroups); - dispatcher('toggle_bypass',log2(linegroups{12}{2,1})); - - case 'toggle12_2' - %% case toggle1_2 - linegroups = value(LineGroups); - dispatcher('toggle_bypass',log2(linegroups{12}{3,1})); - - case 'toggle13_1' - %% case toggle1_1 - linegroups = value(LineGroups); - dispatcher('toggle_bypass',log2(linegroups{13}{2,1})); - - case 'toggle13_2' - %% case toggle1_2 - linegroups = value(LineGroups); - dispatcher('toggle_bypass',log2(linegroups{13}{3,1})); - - case 'toggle14_1' - %% case toggle1_1 - linegroups = value(LineGroups); - dispatcher('toggle_bypass',log2(linegroups{14}{2,1})); - - case 'toggle14_2' - %% case toggle1_2 - linegroups = value(LineGroups); - dispatcher('toggle_bypass',log2(linegroups{14}{3,1})); - - case 'toggle15_1' - %% case toggle1_1 - linegroups = value(LineGroups); - dispatcher('toggle_bypass',log2(linegroups{15}{2,1})); - - case 'toggle15_2' - %% case toggle1_2 - linegroups = value(LineGroups); - dispatcher('toggle_bypass',log2(linegroups{15}{3,1})); - - case 'toggle16_1' - %% case toggle1_1 - linegroups = value(LineGroups); - dispatcher('toggle_bypass',log2(linegroups{16}{2,1})); - - case 'toggle16_2' - %% case toggle1_2 - linegroups = value(LineGroups); - dispatcher('toggle_bypass',log2(linegroups{16}{3,1})); - - %--------------------------------------------------------------- - % CASE PREPARE_NEXT_TRIAL - %--------------------------------------------------------------- - case 'prepare_next_trial' - line_names = value(LineNames); - sma = StateMachineAssembler('full_trial_structure','use_happenings',1,'n_input_lines',numel(line_names),'line_names',line_names); - sma = add_state(sma, 'name', 'the_only_state', 'self_timer',1e4, 'input_to_statechange', {'Tup', 'final_state'}); - sma = add_state(sma, 'name', 'final_state', 'self_timer',1e4, 'input_to_statechange', {'Tup', 'check_next_trial_ready'}); - dispatcher('send_assembler', sma, 'final_state'); - - %--------------------------------------------------------------- - % CASE TRIAL_COMPLETED - %--------------------------------------------------------------- - case 'trial_completed' - - - %--------------------------------------------------------------- - % CASE UPDATE - %--------------------------------------------------------------- - case 'update' - pe = parsed_events; %#ok - linenames = value(LineNames); - - for i = 1:numel(linenames) - poketimes = eval(['pe.pokes.',linenames(i)]); - if ~isempty(poketimes) && isnan(poketimes(end,2)) - set(get_ghandle(eval(['Input',linenames(i)])),'BackgroundColor',[1,0,0]); - - str = get(get_ghandle(eval(['Input',linenames(i)])),'string'); - str{2} = size(poketimes,1); - set(get_ghandle(eval(['Input',linenames(i)])),'string',str); - - else - set(get_ghandle(eval(['Input',linenames(i)])),'BackgroundColor',[0,1,0]); - end - end - - %--------------------------------------------------------------- - % CASE CLOSE - %--------------------------------------------------------------- - case 'close' - - dispatcher('Stop'); - - %Let's pause until we know dispatcher is done running - set(value(stopping_complete_timer),'TimerFcn',[mfilename,'(''close_continued'');']); - start(value(stopping_complete_timer)); - - case 'close_continued' - - if value(stopping_process_completed) - stop(value(stopping_complete_timer)); %Stop looping. - %dispatcher('set_protocol',''); - - if exist('myfig', 'var') && isa(myfig, 'SoloParamHandle') && ishandle(value(myfig)), %#ok - delete(value(myfig)); - end; - delete_sphandle('owner', ['^@' class(obj) '$']); - dispatcher('set_protocol',''); - end - otherwise, - warning('Unknown action! "%s"\n', action); %#ok -end; - -return; - -end - -function calculate_soundlevel - -% --- Configuration --- -serialPort = '/dev/ttyACM0'; % Replace with the actual serial port of your Raspberry Pi -baudRate = 9600; % Or your desired baud rate -matlabOutputValues = 0:10:100; % Example range of MATLAB output values -calibrationData = []; - -% --- Establish Serial Connection --- -try - s = serial(serialPort, 'BaudRate', baudRate); - fopen(s); - disp(['Opened serial port: ', serialPort, ' at ', num2str(baudRate), ' bps']); - - % --- Calibration Loop --- - for i = 1:length(matlabOutputValues) - outputValue = matlabOutputValues(i); - disp(['Sending MATLAB output value: ', num2str(outputValue)]); - - % --- Send command to set speaker level (replace with your actual command) --- - setSpeakerLevel(outputValue); - - % --- Send command to Raspberry Pi to measure SPL --- - fprintf(s, 'measure_spl\n'); % Send command over serial - - % --- Receive SPL value from Raspberry Pi --- - receivedData = fgetl(s); - if ~isempty(receivedData) - splValue = str2double(receivedData); - if ~isnan(splValue) - disp(['Received SPL: ', num2str(splValue), ' dB']); - calibrationData = [calibrationData; outputValue, splValue]; - pause(2); % Allow time for sound to stabilize and measurement - else - warning('Received non-numeric SPL value.'); - end - else - warning('No data received from Raspberry Pi.'); - end - end - - % --- Close Serial Connection --- - fclose(s); - delete(s); - clear s; - - % --- Display Calibration Data --- - disp('Calibration Data (MATLAB Value, dB SPL):'); - disp(calibrationData); - - % --- Create Calibration Mapping (Example: Linear Fit) --- - p = polyfit(calibrationData(:,1), calibrationData(:,2), 1); - disp('Calibration Polynomial (p(1) * MATLAB_Value + p(2)):'); - disp(p); - % Now you can use this polynomial 'p' to estimate dB SPL from your MATLAB values - % estimatedSPL = polyval(p, yourMatlabValue); - -catch ME - disp(['Error occurred: ', ME.message]); - if exist('s', 'var') && isvalid(s) - fclose(s); - delete(s); - clear s; - end -end - -% --- Your function to control the speaker level in MATLAB --- -function setSpeakerLevel(value) - % Replace this with your actual commands or functions to control the speaker. - disp(['(Simulating setting speaker level to: ', num2str(value), ')']); - pause(1); % Simulate speaker level change -end - -end - - -function [normbase]=singlenoise(sigma_1,T,fcut,Fs,filter_type) - -%%%%%%%%%%%%%%%%% Determines of the type of filter used %%%%%%%%%%%%%%%%%%% -%'LPFIR': lowpass FIR%%%%%'FIRLS': Least square linear-phase FIR filter design -%'BUTTER': IIR Butterworth lowpass filter%%%%%%'GAUS': Gaussian filter (window) -%'MOVAVRG': Moving average FIR filter%%%%%%%%'KAISER': Kaiser-window FIR filtering -% 'EQUIRIP':Eqiripple FIR filter%%%%% 'HAMMING': Hamming-window based FIR -% T is duration of each signal in milisecond, fcut is the cut-off frequency -% Fs is the sampling frequency -% outband=40; - -filter_type='BUTTER'; -outband=60; -replace=1; -L=floor(T*Fs);% Length of signal - -%%%%%%%%%%% produce position values %%%%%%% -pos1 = sigma_1 * randn(Fs,1); - -% pos1(pos1>outband)=[]; -% pos1(pos1<-outband)=[]; - -base = randsample(pos1,L,replace); -%%%% Filter the original position values %%%%%% -%filtbase=filt(base,fcut,Fs,filter_type); -hf = design(fdesign.bandpass('N,F3dB1,F3dB2',10,fcut(1),fcut(2),Fs)); -filtbase=filter(hf,base); -normbase=filtbase./(max(abs(filtbase))); -end - - -function [base,target,normbase,normtarget]=noisestim(sigma_1,sigma_2,T,fcut,Fs,filter_type) - -%%%%%%%%%%%%%%%%% Determines of the type of filter used %%%%%%%%%%%%%%%%%%% -%'LPFIR': lowpass FIR%%%%%'FIRLS': Least square linear-phase FIR filter design -%'BUTTER': IIR Butterworth lowpass filter%%%%%%'GAUS': Gaussian filter (window) -%'MOVAVRG': Moving average FIR filter%%%%%%%%'KAISER': Kaiser-window FIR filtering -% 'EQUIRIP':Eqiripple FIR filter%%%%% 'HAMMING': Hamming-window based FIR -% T is duration of each signal in milisecond, fcut is the cut-off frequency -% Fs is the sampling frequency -% outband=40; -replace=1; -L=floor(T*Fs); % Length of signal -% t=L*linspace(0,1,L)/Fs; % time in miliseconds -%%%%%%%%%%% produce position values %%%%%%% -pos1 = sigma_1*randn(Fs,1); -% pos1(pos1>outband)=[]; -% pos1(pos1<-outband)=[]; - -pos2 =sigma_2*randn(Fs,1); -% pos2(pos2>outband)=[]; -% pos2(pos2<-outband)=[]; -base = randsample(pos1,L,replace); -target = randsample(pos2,L,replace); -%%%% Filter the original position values %%%%%% -filtbase=filt(base,fcut,Fs,filter_type); -filttarget=filt(target,fcut,Fs,filter_type); -normbase=filtbase./(max(abs(filtbase))); -normtarget=filttarget./(max(abs(filttarget))); -end From fec7bbfaac340f7d6656632d87d9d0ac90d349e1 Mon Sep 17 00:00:00 2001 From: Arpit Date: Wed, 23 Apr 2025 17:27:06 +0100 Subject: [PATCH 061/164] changed the folder name in another machine --- .../ArpitCentrePokeTraining.m | 0 .../ArpitCentrePokeTrainingSMA.m | 0 ...ArpitCentrePokeTraining_SessionDefinition_AutoTrainingStages.m | 0 .../Connect_Bonsai_Camera.m | 0 .../LOGplotPairs.m | 0 .../ParamsSection.m | 0 .../SessionPerformanceSection.m | 0 .../SoundSection.m | 0 .../StimulusSection.m | 0 .../Training_ParamsSection.m | 0 .../private/CreateSamples_from_Distribution.m | 0 .../private/StimuliDistribution_plot.m | 0 .../private/filt.m | 0 .../private/noisestim.m | 0 .../private/singlenoise.m | 0 .../state_colors.m | 0 .../wave_colors.m | 0 17 files changed, 0 insertions(+), 0 deletions(-) rename Protocols/{@Arpit_CentrePokeTraining => @ArpitCentrePokeTraining}/ArpitCentrePokeTraining.m (100%) rename Protocols/{@Arpit_CentrePokeTraining => @ArpitCentrePokeTraining}/ArpitCentrePokeTrainingSMA.m (100%) rename Protocols/{@Arpit_CentrePokeTraining => @ArpitCentrePokeTraining}/ArpitCentrePokeTraining_SessionDefinition_AutoTrainingStages.m (100%) rename Protocols/{@Arpit_CentrePokeTraining => @ArpitCentrePokeTraining}/Connect_Bonsai_Camera.m (100%) rename Protocols/{@Arpit_CentrePokeTraining => @ArpitCentrePokeTraining}/LOGplotPairs.m (100%) rename Protocols/{@Arpit_CentrePokeTraining => @ArpitCentrePokeTraining}/ParamsSection.m (100%) rename Protocols/{@Arpit_CentrePokeTraining => @ArpitCentrePokeTraining}/SessionPerformanceSection.m (100%) rename Protocols/{@Arpit_CentrePokeTraining => @ArpitCentrePokeTraining}/SoundSection.m (100%) rename Protocols/{@Arpit_CentrePokeTraining => @ArpitCentrePokeTraining}/StimulusSection.m (100%) rename Protocols/{@Arpit_CentrePokeTraining => @ArpitCentrePokeTraining}/Training_ParamsSection.m (100%) rename Protocols/{@Arpit_CentrePokeTraining => @ArpitCentrePokeTraining}/private/CreateSamples_from_Distribution.m (100%) rename Protocols/{@Arpit_CentrePokeTraining => @ArpitCentrePokeTraining}/private/StimuliDistribution_plot.m (100%) rename Protocols/{@Arpit_CentrePokeTraining => @ArpitCentrePokeTraining}/private/filt.m (100%) rename Protocols/{@Arpit_CentrePokeTraining => @ArpitCentrePokeTraining}/private/noisestim.m (100%) rename Protocols/{@Arpit_CentrePokeTraining => @ArpitCentrePokeTraining}/private/singlenoise.m (100%) rename Protocols/{@Arpit_CentrePokeTraining => @ArpitCentrePokeTraining}/state_colors.m (100%) rename Protocols/{@Arpit_CentrePokeTraining => @ArpitCentrePokeTraining}/wave_colors.m (100%) diff --git a/Protocols/@Arpit_CentrePokeTraining/ArpitCentrePokeTraining.m b/Protocols/@ArpitCentrePokeTraining/ArpitCentrePokeTraining.m similarity index 100% rename from Protocols/@Arpit_CentrePokeTraining/ArpitCentrePokeTraining.m rename to Protocols/@ArpitCentrePokeTraining/ArpitCentrePokeTraining.m diff --git a/Protocols/@Arpit_CentrePokeTraining/ArpitCentrePokeTrainingSMA.m b/Protocols/@ArpitCentrePokeTraining/ArpitCentrePokeTrainingSMA.m similarity index 100% rename from Protocols/@Arpit_CentrePokeTraining/ArpitCentrePokeTrainingSMA.m rename to Protocols/@ArpitCentrePokeTraining/ArpitCentrePokeTrainingSMA.m diff --git a/Protocols/@Arpit_CentrePokeTraining/ArpitCentrePokeTraining_SessionDefinition_AutoTrainingStages.m b/Protocols/@ArpitCentrePokeTraining/ArpitCentrePokeTraining_SessionDefinition_AutoTrainingStages.m similarity index 100% rename from Protocols/@Arpit_CentrePokeTraining/ArpitCentrePokeTraining_SessionDefinition_AutoTrainingStages.m rename to Protocols/@ArpitCentrePokeTraining/ArpitCentrePokeTraining_SessionDefinition_AutoTrainingStages.m diff --git a/Protocols/@Arpit_CentrePokeTraining/Connect_Bonsai_Camera.m b/Protocols/@ArpitCentrePokeTraining/Connect_Bonsai_Camera.m similarity index 100% rename from Protocols/@Arpit_CentrePokeTraining/Connect_Bonsai_Camera.m rename to Protocols/@ArpitCentrePokeTraining/Connect_Bonsai_Camera.m diff --git a/Protocols/@Arpit_CentrePokeTraining/LOGplotPairs.m b/Protocols/@ArpitCentrePokeTraining/LOGplotPairs.m similarity index 100% rename from Protocols/@Arpit_CentrePokeTraining/LOGplotPairs.m rename to Protocols/@ArpitCentrePokeTraining/LOGplotPairs.m diff --git a/Protocols/@Arpit_CentrePokeTraining/ParamsSection.m b/Protocols/@ArpitCentrePokeTraining/ParamsSection.m similarity index 100% rename from Protocols/@Arpit_CentrePokeTraining/ParamsSection.m rename to Protocols/@ArpitCentrePokeTraining/ParamsSection.m diff --git a/Protocols/@Arpit_CentrePokeTraining/SessionPerformanceSection.m b/Protocols/@ArpitCentrePokeTraining/SessionPerformanceSection.m similarity index 100% rename from Protocols/@Arpit_CentrePokeTraining/SessionPerformanceSection.m rename to Protocols/@ArpitCentrePokeTraining/SessionPerformanceSection.m diff --git a/Protocols/@Arpit_CentrePokeTraining/SoundSection.m b/Protocols/@ArpitCentrePokeTraining/SoundSection.m similarity index 100% rename from Protocols/@Arpit_CentrePokeTraining/SoundSection.m rename to Protocols/@ArpitCentrePokeTraining/SoundSection.m diff --git a/Protocols/@Arpit_CentrePokeTraining/StimulusSection.m b/Protocols/@ArpitCentrePokeTraining/StimulusSection.m similarity index 100% rename from Protocols/@Arpit_CentrePokeTraining/StimulusSection.m rename to Protocols/@ArpitCentrePokeTraining/StimulusSection.m diff --git a/Protocols/@Arpit_CentrePokeTraining/Training_ParamsSection.m b/Protocols/@ArpitCentrePokeTraining/Training_ParamsSection.m similarity index 100% rename from Protocols/@Arpit_CentrePokeTraining/Training_ParamsSection.m rename to Protocols/@ArpitCentrePokeTraining/Training_ParamsSection.m diff --git a/Protocols/@Arpit_CentrePokeTraining/private/CreateSamples_from_Distribution.m b/Protocols/@ArpitCentrePokeTraining/private/CreateSamples_from_Distribution.m similarity index 100% rename from Protocols/@Arpit_CentrePokeTraining/private/CreateSamples_from_Distribution.m rename to Protocols/@ArpitCentrePokeTraining/private/CreateSamples_from_Distribution.m diff --git a/Protocols/@Arpit_CentrePokeTraining/private/StimuliDistribution_plot.m b/Protocols/@ArpitCentrePokeTraining/private/StimuliDistribution_plot.m similarity index 100% rename from Protocols/@Arpit_CentrePokeTraining/private/StimuliDistribution_plot.m rename to Protocols/@ArpitCentrePokeTraining/private/StimuliDistribution_plot.m diff --git a/Protocols/@Arpit_CentrePokeTraining/private/filt.m b/Protocols/@ArpitCentrePokeTraining/private/filt.m similarity index 100% rename from Protocols/@Arpit_CentrePokeTraining/private/filt.m rename to Protocols/@ArpitCentrePokeTraining/private/filt.m diff --git a/Protocols/@Arpit_CentrePokeTraining/private/noisestim.m b/Protocols/@ArpitCentrePokeTraining/private/noisestim.m similarity index 100% rename from Protocols/@Arpit_CentrePokeTraining/private/noisestim.m rename to Protocols/@ArpitCentrePokeTraining/private/noisestim.m diff --git a/Protocols/@Arpit_CentrePokeTraining/private/singlenoise.m b/Protocols/@ArpitCentrePokeTraining/private/singlenoise.m similarity index 100% rename from Protocols/@Arpit_CentrePokeTraining/private/singlenoise.m rename to Protocols/@ArpitCentrePokeTraining/private/singlenoise.m diff --git a/Protocols/@Arpit_CentrePokeTraining/state_colors.m b/Protocols/@ArpitCentrePokeTraining/state_colors.m similarity index 100% rename from Protocols/@Arpit_CentrePokeTraining/state_colors.m rename to Protocols/@ArpitCentrePokeTraining/state_colors.m diff --git a/Protocols/@Arpit_CentrePokeTraining/wave_colors.m b/Protocols/@ArpitCentrePokeTraining/wave_colors.m similarity index 100% rename from Protocols/@Arpit_CentrePokeTraining/wave_colors.m rename to Protocols/@ArpitCentrePokeTraining/wave_colors.m From cc7dffc6655705423a0033fe7952e725aa12af91 Mon Sep 17 00:00:00 2001 From: viktorpm Date: Wed, 23 Apr 2025 17:52:16 +0100 Subject: [PATCH 062/164] error debug caused by filename change --- ...okeTraining_experimenter_ratname_250423a.mat | Bin 0 -> 15234 bytes .../ArpitCentrePokeTraining.m | 10 +++++----- .../ArpitCentrePokeTrainingSMA.m | 2 +- ...ining_SessionDefinition_AutoTrainingStages.m | 2 +- .../Connect_Bonsai_Camera.m | 4 ++-- .../@ArpitCentrePokeTraining/ParamsSection.m | 4 ++-- .../@ArpitCentrePokeTraining/SoundSection.m | 8 ++++---- 7 files changed, 15 insertions(+), 15 deletions(-) create mode 100644 ExperPort/settings_@ArpitCentrePokeTraining_experimenter_ratname_250423a.mat diff --git a/ExperPort/settings_@ArpitCentrePokeTraining_experimenter_ratname_250423a.mat b/ExperPort/settings_@ArpitCentrePokeTraining_experimenter_ratname_250423a.mat new file mode 100644 index 0000000000000000000000000000000000000000..4087ad24ce85a75a0e91a889fa49a70341cbfd01 GIT binary patch literal 15234 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 Fe*rC~`D_3H literal 0 HcmV?d00001 diff --git a/Protocols/@ArpitCentrePokeTraining/ArpitCentrePokeTraining.m b/Protocols/@ArpitCentrePokeTraining/ArpitCentrePokeTraining.m index 9f9cc839..56a32fa3 100644 --- a/Protocols/@ArpitCentrePokeTraining/ArpitCentrePokeTraining.m +++ b/Protocols/@ArpitCentrePokeTraining/ArpitCentrePokeTraining.m @@ -1,7 +1,7 @@ -% Arpit_CentrePokeTraining protocol +% ArpitCentrePokeTraining protocol % Arpit, 12 March 2025 -function [obj] = Arpit_CentrePokeTraining(varargin) +function [obj] = ArpitCentrePokeTraining(varargin) % Default object is of our own class (mfilename); % we inherit only from Plugins @@ -170,7 +170,7 @@ % For plotting with the pokesplot plugin, we need to tell it what % colors to plot with: - my_state_colors = Arpit_CentrePokeTrainingSMA(obj, 'get_state_colors'); + my_state_colors = ArpitCentrePokeTrainingSMA(obj, 'get_state_colors'); % In pokesplot, the poke colors have a default value, so we don't need % to specify them, but here they are so you know how to change them. my_poke_colors = struct( ... @@ -201,7 +201,7 @@ [x, y] = StimulusSection(obj,'init',x,y); - Arpit_CentrePokeTrainingSMA(obj, 'init'); + ArpitCentrePokeTrainingSMA(obj, 'init'); next_row(y); next_row(y); SessionDefinition(obj, 'init', x, y, value(myfig)); next_row(y, 2); %#ok @@ -259,7 +259,7 @@ StimulusSection(obj,'prepare_next_trial'); SoundManagerSection(obj, 'send_not_yet_uploaded_sounds'); - [sma, prepare_next_trial_states] = Arpit_CentrePokeTrainingSMA(obj, 'prepare_next_trial'); + [sma, prepare_next_trial_states] = ArpitCentrePokeTrainingSMA(obj, 'prepare_next_trial'); % Default behavior of following call is that every 20 trials, the data % gets saved, not interactive, no commit to CVS. diff --git a/Protocols/@ArpitCentrePokeTraining/ArpitCentrePokeTrainingSMA.m b/Protocols/@ArpitCentrePokeTraining/ArpitCentrePokeTrainingSMA.m index 01118875..914586c5 100644 --- a/Protocols/@ArpitCentrePokeTraining/ArpitCentrePokeTrainingSMA.m +++ b/Protocols/@ArpitCentrePokeTraining/ArpitCentrePokeTrainingSMA.m @@ -1,4 +1,4 @@ -function [varargout] = Arpit_CentrePokeTrainingSMA(obj, action) +function [varargout] = ArpitCentrePokeTrainingSMA(obj, action) %%%%%%%%%%%%%%%%%%% TRAINING STAGES %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% diff --git a/Protocols/@ArpitCentrePokeTraining/ArpitCentrePokeTraining_SessionDefinition_AutoTrainingStages.m b/Protocols/@ArpitCentrePokeTraining/ArpitCentrePokeTraining_SessionDefinition_AutoTrainingStages.m index 92a9c6be..bc35643b 100644 --- a/Protocols/@ArpitCentrePokeTraining/ArpitCentrePokeTraining_SessionDefinition_AutoTrainingStages.m +++ b/Protocols/@ArpitCentrePokeTraining/ArpitCentrePokeTraining_SessionDefinition_AutoTrainingStages.m @@ -2,7 +2,7 @@ %Please use the session automator window exclusively %to edit this file. -function varargout = Arpit_CentrePokeTraining_SessionDefinition_AutoTrainingStages(obj, action, varargin) +function varargout = ArpitCentrePokeTraining_SessionDefinition_AutoTrainingStages(obj, action, varargin) GetSoloFunctionArgs('func_owner', ['@' class(obj)], 'func_name', 'SessionModel'); diff --git a/Protocols/@ArpitCentrePokeTraining/Connect_Bonsai_Camera.m b/Protocols/@ArpitCentrePokeTraining/Connect_Bonsai_Camera.m index 03b03499..c866d2b8 100644 --- a/Protocols/@ArpitCentrePokeTraining/Connect_Bonsai_Camera.m +++ b/Protocols/@ArpitCentrePokeTraining/Connect_Bonsai_Camera.m @@ -54,7 +54,7 @@ function Connect_Bonsai_Camera(obj,action) % system([command, ' &']); runBonsaiWorkflow(bonsai_workflow_Path); - pause(5); + pause(3); % Before starting the streaming of Camera, I need to send the % file directory for saving the file otherwise Bonsai can run into % error as it will try saving files in the predefined folder in @@ -68,7 +68,7 @@ function Connect_Bonsai_Camera(obj,action) % the command to send message to Bonsai write(udpSender, oscMsg_Camera_start, "uint8", bonsaiComputerIP,bonsaiUdpPort); - pause(5); + pause(3); % NOTE: Ideally I should start saving the trials once the experimenter presses % Run either on dispatcher or Runrats. But, I dont want to make the changes there % so would start recording as soon as the protocol is loaded and camera starts streaming diff --git a/Protocols/@ArpitCentrePokeTraining/ParamsSection.m b/Protocols/@ArpitCentrePokeTraining/ParamsSection.m index 702af0ab..d5f16703 100644 --- a/Protocols/@ArpitCentrePokeTraining/ParamsSection.m +++ b/Protocols/@ArpitCentrePokeTraining/ParamsSection.m @@ -156,7 +156,7 @@ 'and uses hitfrac to adjust the water times'])); next_row(y); - SoloFunctionAddVars('Arpit_CentrePokeTrainingSMA', 'ro_args', ... + SoloFunctionAddVars('ArpitCentrePokeTrainingSMA', 'ro_args', ... {'CP_duration';'SideLed_duration'; 'stimuli_on';... 'RewardCollection_duration';'training_stage'; ... 'legal_cbreak' ; 'SettlingIn_time'; 'time_go_cue'; ... @@ -217,7 +217,7 @@ end [stage_fig_x,stage_fig_y] = Training_ParamsSection(obj, 'reinit', value(stage_fig_x),value(stage_fig_y)); % update the training params as well - Arpit_CentrePokeTrainingSMA(obj,'reinit'); + ArpitCentrePokeTrainingSMA(obj,'reinit'); SessionPerformanceSection(obj, 'evaluate'); switch value(training_stage) diff --git a/Protocols/@ArpitCentrePokeTraining/SoundSection.m b/Protocols/@ArpitCentrePokeTraining/SoundSection.m index fcb2a535..0fd22e60 100644 --- a/Protocols/@ArpitCentrePokeTraining/SoundSection.m +++ b/Protocols/@ArpitCentrePokeTraining/SoundSection.m @@ -28,17 +28,17 @@ set(double(gcf), 'Visible', 'off'); x=10;y=10; - [x,y]=SoundInterface(obj,'add','ViolationSound',x,y,'Volume',0.005,'Freq',1000,'Duration',0.5); + [x,y]=SoundInterface(obj,'add','ViolationSound',x,y,'Volume',0.01,'Freq',1000,'Duration',0.5); % [x,y]=SoundInterface(obj,'add','ViolationSound',x,y,'Style','WhiteNoise','Volume',0.01); [x,y]=SoundInterface(obj,'add','TimeoutSound',x,y,'Style','WhiteNoise','Volume',0.08,'Duration',0.5); [x,y]=SoundInterface(obj,'add','RewardSound',x,y,'Style','Bups','Volume',1,'Freq',5,'Duration',1.5); [x,y]=SoundInterface(obj,'add','ErrorSound',x,y,'Style','WhiteNoise','Volume',0.08); next_column(x); y=10; - [x,y]=SoundInterface(obj,'add','GoSound',x,y,'Style','Tone','Volume',0.005,'Freq',3000,'Duration',0.2); + [x,y]=SoundInterface(obj,'add','GoSound',x,y,'Style','Tone','Volume',0.01,'Freq',3000,'Duration',0.2); SoundInterface(obj, 'disable', 'GoSound', 'Dur1'); - [x,y]=SoundInterface(obj,'add','SOneSound',x,y,'Style','Tone','Volume',0.005,'Freq',3000,'Duration',0.2); - [x,y]=SoundInterface(obj,'add','STwoSound',x,y,'Style','WhiteNoise','Volume',0.08); + [x,y]=SoundInterface(obj,'add','SOneSound',x,y,'Style','WhiteNoise','Volume',0.007); + [x,y]=SoundInterface(obj,'add','STwoSound',x,y,'Style','WhiteNoise','Volume',0.07); x=oldx; y=oldy; figure(parentfig); From 2c4c9ef4bd54aa8ed6fce0d4acdcea12b33641bf Mon Sep 17 00:00:00 2001 From: Viktor Plattner Date: Thu, 24 Apr 2025 13:48:56 +0100 Subject: [PATCH 063/164] updated PR template --- .github/PULL_REQUEST_TEMPLATE.md | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index c960675b..5d2497da 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -17,6 +17,7 @@ Please include a summary of the changes made. Include any relevant context, scre - [ ] I have added necessary tests to cover the changes. - [ ] I have followed the code style guidelines. - [ ] I have verified that the code works in all target environments. +- [ ] I have made sure the technicians are comfortable using the new feature (if applicable). ### Related Issues From bc9cbd2260c699980ddc825b080401defcd504c3 Mon Sep 17 00:00:00 2001 From: Arpit Date: Fri, 25 Apr 2025 10:47:55 +0100 Subject: [PATCH 064/164] changed bonsai folder name as it was part of gitignore --- .../Camera_Control.bonsai | 190 ++++++++++ .../Camera_Control.bonsai.layout | 333 ++++++++++++++++++ .../Connect_Bonsai_Camera.m | 2 +- 3 files changed, 524 insertions(+), 1 deletion(-) create mode 100644 Protocols/@ArpitCentrePokeTraining/Bonsai_Camera_Control/Camera_Control.bonsai create mode 100644 Protocols/@ArpitCentrePokeTraining/Bonsai_Camera_Control/Camera_Control.bonsai.layout diff --git a/Protocols/@ArpitCentrePokeTraining/Bonsai_Camera_Control/Camera_Control.bonsai b/Protocols/@ArpitCentrePokeTraining/Bonsai_Camera_Control/Camera_Control.bonsai new file mode 100644 index 00000000..2e796c2a --- /dev/null +++ b/Protocols/@ArpitCentrePokeTraining/Bonsai_Camera_Control/Camera_Control.bonsai @@ -0,0 +1,190 @@ + + + + + + + Receiver + 9090 + MATLABSender + 0 + + + + + tcp://localhost:5557 + XSubscriber + + + + /camera + s + Receiver + + + Camera_Control + + + Camera + + + + jpg + + + + + + + + + tcp://localhost:5557 + + + + + 0 + + + + + Camera_Control + + + + + + Source1 + + + + start + + + + + + + + + + + + + + + Camera + + + Camera_Control + + + + + + Source1 + + + + stop + + + + + + + + + + + + + + + + Camera + + + /record + s + Receiver + + + + PT1S + + + + + + + + + + + + + + RecordTrials + + + + Source1 + + + + + + + C:\Users\Public\Public Videos\Trial.avi + FileCount + true + false + FMP4 + 30 + + 0 + 0 + + NearestNeighbor + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Protocols/@ArpitCentrePokeTraining/Bonsai_Camera_Control/Camera_Control.bonsai.layout b/Protocols/@ArpitCentrePokeTraining/Bonsai_Camera_Control/Camera_Control.bonsai.layout new file mode 100644 index 00000000..5f01c6c4 --- /dev/null +++ b/Protocols/@ArpitCentrePokeTraining/Bonsai_Camera_Control/Camera_Control.bonsai.layout @@ -0,0 +1,333 @@ + + + + false + + 0 + 0 + + + 0 + 0 + + Normal + + + false + + 0 + 0 + + + 0 + 0 + + Normal + + + true + + 94 + 95 + + + 333 + 63 + + Normal + Bonsai.Design.ObjectTextVisualizer + + + + + + false + + 0 + 0 + + + 0 + 0 + + Normal + + + false + + 0 + 0 + + + 0 + 0 + + Normal + + + false + + 0 + 0 + + + 0 + 0 + + Normal + + + false + + 0 + 0 + + + 0 + 0 + + Normal + + + false + + 0 + 0 + + + 0 + 0 + + Normal + + + false + + 636 + 274 + + + 333 + 277 + + Normal + + + true + + 472 + 106 + + + 333 + 63 + + Normal + Bonsai.Design.ObjectTextVisualizer + + + + + + false + + 0 + 0 + + + 0 + 0 + + Normal + + false + + 245 + 255 + + + 313 + 237 + + Normal + + + + true + + 15 + 705 + + + 333 + 277 + + Normal + Bonsai.Vision.Design.IplImageVisualizer + + + + + + false + + 0 + 0 + + + 0 + 0 + + Normal + + + true + + 95 + 246 + + + 333 + 177 + + Normal + Bonsai.Design.ObjectTextVisualizer + + + + + + false + + 98 + 102 + + + 333 + 63 + + Normal + + false + + 270 + 280 + + + 313 + 237 + + Normal + + + + false + + 0 + 0 + + + 0 + 0 + + Normal + + + false + + 0 + 0 + + + 0 + 0 + + Normal + + + false + + 0 + 0 + + + 0 + 0 + + Normal + + + false + + 0 + 0 + + + 0 + 0 + + Normal + + + false + + 0 + 0 + + + 0 + 0 + + Normal + + + false + + 0 + 0 + + + 0 + 0 + + Normal + + + false + + 0 + 0 + + + 0 + 0 + + Normal + + + false + + 0 + 0 + + + 0 + 0 + + Normal + + false + + 220 + 229 + + + 313 + 237 + + Normal + + + \ No newline at end of file diff --git a/Protocols/@ArpitCentrePokeTraining/Connect_Bonsai_Camera.m b/Protocols/@ArpitCentrePokeTraining/Connect_Bonsai_Camera.m index c866d2b8..dbb83eea 100644 --- a/Protocols/@ArpitCentrePokeTraining/Connect_Bonsai_Camera.m +++ b/Protocols/@ArpitCentrePokeTraining/Connect_Bonsai_Camera.m @@ -24,7 +24,7 @@ function Connect_Bonsai_Camera(obj,action) % bonsai_path = 'C:\Users\Turin\Downloads\Bonsai\Bonsai.exe'; % Path of Bonsai App scriptFullPath = mfilename('fullpath'); % Path of running the current script scriptDirectory = fileparts(scriptFullPath); -bonsai_workflow_Path = fullfile(scriptDirectory,'Bonsai','Camera_Control.bonsai'); +bonsai_workflow_Path = fullfile(scriptDirectory,'Bonsai_Camera_Control','Camera_Control.bonsai'); foundworkflow = exist("bonsai_workflow_Path",'file'); % if ~foundworkflow % warning('could not find bonsai executable, please insert it manually'); From 64a4a9ab2bb6e8ae3197be12e0721c392ddf1cf9 Mon Sep 17 00:00:00 2001 From: Arpit Date: Fri, 25 Apr 2025 14:56:35 +0100 Subject: [PATCH 065/164] test code --- .../private/LoadSettingGUI.m | 114 ++++++++++++++++++ 1 file changed, 114 insertions(+) create mode 100644 Protocols/@ArpitCentrePokeTraining/private/LoadSettingGUI.m diff --git a/Protocols/@ArpitCentrePokeTraining/private/LoadSettingGUI.m b/Protocols/@ArpitCentrePokeTraining/private/LoadSettingGUI.m new file mode 100644 index 00000000..5c7ceb0e --- /dev/null +++ b/Protocols/@ArpitCentrePokeTraining/private/LoadSettingGUI.m @@ -0,0 +1,114 @@ + +function LoadSettingGUI() + +% REMOVE THE SETTINGS/PRE OPENED GUI +try + flush +catch +end + +% START FRESH +% FIRST SET THE REQUIRED DIRECTORY +current_dir = cd; +ratter_dir = extractBefore(current_dir,'ratter'); +ratter_modules_dir = fullfile(ratter_dir, 'ratter', 'ExperPort'); +cd(ratter_modules_dir); + +% Initial option sets +% Let's identify all the experimenter and their respective rats +try + Experimenter_Name = bdata('select distinct experimenter from rats where extant=1 order by experimenter'); +catch %#ok + disp('ERROR: Unable to connect to MySQL Server'); + Experimenter_Name = ''; +end + +if ~isempty(Experimenter_Name) + for n_exp = 1:numel(Experimenter_Name) + ratnames = bdata(['select ratname from rats where experimenter="',Experimenter_Name{n_exp},'" and extant=1']); + Rat_Name{n_exp} = sortrows(strtrim(ratnames)); + end +end + +Exp_Rat_Map = containers.Map(Experimenter_Name, Rat_Name); + +% Create figure +fig = figure('Name', 'Dynamic Button GUI', ... + 'Position', [500, 300, 600, 400], ... + 'MenuBar', 'none', ... + 'NumberTitle', 'off', ... + 'Color', [0.9 0.9 0.9]); + +% Create main buttons with resize callback +buttonHandles = createButtons(fig, mainOptions, @(src, ~) mainButtonCallback(src, Exp_Rat_Map, fig)); + + % Setup resize behavior + set(fig, 'ResizeFcn', @(src, ~) resizeButtons(src, buttonHandles)); + + % === Callback for first-level buttons === + function mainButtonCallback(src, map, figHandle) + experimenter = src.String; + subOptions = map(experimenter); + clf(figHandle); % Clear previous buttons + + % Create new buttons and assign second-level callback + newButtons = createButtons(figHandle, subOptions, @(src2, ~) subButtonCallback(src2,experimenter)); + + % Update resize function + set(figHandle, 'ResizeFcn', @(src, ~) resizeButtons(src, newButtons)); + end + + % === Final action when second-level button is clicked === + function subButtonCallback(src,exp_name) + rat_name = src.String; + msgbox(['You selected: ' rat_name], 'Choice'); + % You can call a custom function here instead of msgbox + Run_Runrats(exp_name, rat_name) + end +end + +% === Create buttons dynamically and return handles === +function btns = createButtons(figHandle, optionList, callbackFcn) + delete(findall(figHandle, 'Type', 'uicontrol')); % Clear any existing buttons + + n = numel(optionList); + btns = gobjects(1, n); + + for i = 1:n + btns(i) = uicontrol(figHandle, ... + 'Style', 'pushbutton', ... + 'Units', 'normalized', ... + 'String', optionList{i}, ... + 'FontSize', 14, ... + 'FontWeight', 'bold', ... + 'BackgroundColor', [0.7 0.8 1], ... + 'Callback', callbackFcn); + end + + resizeButtons(figHandle, btns); % Initial layout +end + +% === Resize button layout responsively === +function resizeButtons(figHandle, buttons) + n = numel(buttons); + spacing = 0.02; + totalSpacing = spacing * (n + 1); + btnHeight = (1 - totalSpacing) / n; + btnWidth = 0.8; + x = (1 - btnWidth) / 2; + + for i = 1:n + y = 1 - spacing - i * (btnHeight + spacing) + spacing; + set(buttons(i), 'Position', [x, y, btnWidth, btnHeight]); + end +end + + + +function Run_Runrats(experimenter_name, rat_name) + +bpod(''); +newstartup; +runrats('init'); + +end From 9fba683bd22e4c1a463e69cf2952047d7de940b7 Mon Sep 17 00:00:00 2001 From: Arpit Date: Fri, 25 Apr 2025 16:47:23 +0100 Subject: [PATCH 066/164] debug --- ExperPort/Modules/@runrats/runrats.m | 15 +++- .../private/LoadSettingGUI.m | 71 +++++++++++++++++-- 2 files changed, 78 insertions(+), 8 deletions(-) diff --git a/ExperPort/Modules/@runrats/runrats.m b/ExperPort/Modules/@runrats/runrats.m index ed41a535..9c7fb530 100644 --- a/ExperPort/Modules/@runrats/runrats.m +++ b/ExperPort/Modules/@runrats/runrats.m @@ -747,7 +747,20 @@ %runrats(obj,'updatelog','update_exprat'); - +%% SPECIAL CASE ADDED BY ARPIT FOR CLICK AND SELECT +% doesn't effect the running of the other cases/functions + case 'update exp_rat_userclick' + + ExpMenu.value = selected_exp; + runrats(obj,'update_ratmenu',selectedrat); + %If the rat has changed, let's update it. + if ~strcmp(value(RatMenu),selectedrat) + runrats(obj,'update_rat',value(InLiveLoop)); %#ok + else + %Stay in the loop if we haven't changed anything + InLiveLoop.value = 1; + end +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% case 'update_tech_instructions' %% update_tech_instructions %Posts the tech instructions for the active rat on the screen diff --git a/Protocols/@ArpitCentrePokeTraining/private/LoadSettingGUI.m b/Protocols/@ArpitCentrePokeTraining/private/LoadSettingGUI.m index 5c7ceb0e..8743cb59 100644 --- a/Protocols/@ArpitCentrePokeTraining/private/LoadSettingGUI.m +++ b/Protocols/@ArpitCentrePokeTraining/private/LoadSettingGUI.m @@ -40,7 +40,7 @@ function LoadSettingGUI() 'Color', [0.9 0.9 0.9]); % Create main buttons with resize callback -buttonHandles = createButtons(fig, mainOptions, @(src, ~) mainButtonCallback(src, Exp_Rat_Map, fig)); +buttonHandles = createButtons(fig, Experimenter_Name, @(src, ~) mainButtonCallback(src, Exp_Rat_Map, fig)); % Setup resize behavior set(fig, 'ResizeFcn', @(src, ~) resizeButtons(src, buttonHandles)); @@ -52,17 +52,16 @@ function mainButtonCallback(src, map, figHandle) clf(figHandle); % Clear previous buttons % Create new buttons and assign second-level callback - newButtons = createButtons(figHandle, subOptions, @(src2, ~) subButtonCallback(src2,experimenter)); + newButtons = createButtons(figHandle, subOptions, @(src2, ~) subButtonCallback(src2,experimenter,fig)); % Update resize function set(figHandle, 'ResizeFcn', @(src, ~) resizeButtons(src, newButtons)); end % === Final action when second-level button is clicked === - function subButtonCallback(src,exp_name) + function subButtonCallback(src,exp_name,figHandle) rat_name = src.String; - msgbox(['You selected: ' rat_name], 'Choice'); - % You can call a custom function here instead of msgbox + close(figHandle); Run_Runrats(exp_name, rat_name) end end @@ -106,9 +105,67 @@ function resizeButtons(figHandle, buttons) function Run_Runrats(experimenter_name, rat_name) - -bpod(''); +% Identify the bpod port before starting +bpodPort = getOrSetCOMPort(); +disp(['Using COM Port: ' bpodPort]); +bpod(bpodPort); newstartup; runrats('init'); +runrats('update exp_rat_userclick',experimenter_name,rat_name); end + +function comPort = getOrSetCOMPort() + configFile = fullfile(fileparts(mfilename('fullpath')), 'com_config.mat'); + maxAttempts = 3; + success = false; + + % Try existing config or prompt + if exist(configFile, 'file') + data = load(configFile, 'comPort'); + comPort = data.comPort; + else + comPort = promptForPort(); + end + + % Try to validate and possibly retry + for attempt = 1:maxAttempts + try + s = serialport(comPort, 9600); % test connection + clear s; % close it immediately + success = true; + break; + catch + fprintf('[Warning] Failed to open %s. Please select a different COM port.\n', comPort); + comPort = promptForPort(); + end + end + + if ~success + error('Failed to find a valid COM port after %d attempts.', maxAttempts); + end + + % Save the working port + save(configFile, 'comPort'); +end + +function comPort = promptForPort() + availablePorts = serialportlist("available"); + + if isempty(availablePorts) + warning('No serial ports detected. Enter manually.'); + comPort = input('Enter COM port manually (e.g., COM3): ', 's'); + else + fprintf('Available COM ports:\n'); + for i = 1:numel(availablePorts) + fprintf(' %d: %s\n', i, availablePorts(i)); + end + idx = input('Select COM port number: '); + if isnumeric(idx) && idx >= 1 && idx <= numel(availablePorts) + comPort = availablePorts(idx); + else + comPort = input('Enter COM port manually (e.g., COM3): ', 's'); + end + end +end + From 08c7f5ea0c2423867e9ff8aa11151e7e745e2a38 Mon Sep 17 00:00:00 2001 From: Arpit Date: Fri, 25 Apr 2025 17:33:41 +0100 Subject: [PATCH 067/164] test --- .../private/LoadSettingGUI.m | 213 +++++++++--------- 1 file changed, 105 insertions(+), 108 deletions(-) diff --git a/Protocols/@ArpitCentrePokeTraining/private/LoadSettingGUI.m b/Protocols/@ArpitCentrePokeTraining/private/LoadSettingGUI.m index 8743cb59..48624aa5 100644 --- a/Protocols/@ArpitCentrePokeTraining/private/LoadSettingGUI.m +++ b/Protocols/@ArpitCentrePokeTraining/private/LoadSettingGUI.m @@ -1,20 +1,29 @@ - -function LoadSettingGUI() - % REMOVE THE SETTINGS/PRE OPENED GUI try flush catch end -% START FRESH -% FIRST SET THE REQUIRED DIRECTORY +% START FRESH & SET THE REQUIRED DIRECTORY +getenv('USERPROFILE'); +getenv('HOMEDRIVE'); +getenv("SYSTEMROOT"); + current_dir = cd; ratter_dir = extractBefore(current_dir,'ratter'); ratter_modules_dir = fullfile(ratter_dir, 'ratter', 'ExperPort'); cd(ratter_modules_dir); -% Initial option sets +% START BPOD + +% Identify the bpod port before starting +bpodPort = getOrSetCOMPort(); +disp(['Using COM Port: ' bpodPort]); +bpod(bpodPort); +newstartup; + +% PRESENT THE USER WITH THE CHOICE OF EXPERIMENTER/RAT + % Let's identify all the experimenter and their respective rats try Experimenter_Name = bdata('select distinct experimenter from rats where extant=1 order by experimenter'); @@ -41,131 +50,119 @@ function LoadSettingGUI() % Create main buttons with resize callback buttonHandles = createButtons(fig, Experimenter_Name, @(src, ~) mainButtonCallback(src, Exp_Rat_Map, fig)); - - % Setup resize behavior - set(fig, 'ResizeFcn', @(src, ~) resizeButtons(src, buttonHandles)); - - % === Callback for first-level buttons === - function mainButtonCallback(src, map, figHandle) - experimenter = src.String; - subOptions = map(experimenter); - clf(figHandle); % Clear previous buttons - - % Create new buttons and assign second-level callback - newButtons = createButtons(figHandle, subOptions, @(src2, ~) subButtonCallback(src2,experimenter,fig)); - - % Update resize function - set(figHandle, 'ResizeFcn', @(src, ~) resizeButtons(src, newButtons)); - end - % === Final action when second-level button is clicked === - function subButtonCallback(src,exp_name,figHandle) - rat_name = src.String; - close(figHandle); - Run_Runrats(exp_name, rat_name) - end +% Setup resize behavior +set(fig, 'ResizeFcn', @(src, ~) resizeButtons(src, buttonHandles)); + +% === Callback for first-level buttons === +function mainButtonCallback(src, map, figHandle) +experimenter = src.String; +subOptions = map(experimenter); +clf(figHandle); % Clear previous buttons + +% Create new buttons and assign second-level callback +newButtons = createButtons(figHandle, subOptions, @(src2, ~) subButtonCallback(src2,experimenter,fig)); + +% Update resize function +set(figHandle, 'ResizeFcn', @(src, ~) resizeButtons(src, newButtons)); +end + +% === Final action when second-level button is clicked === +function subButtonCallback(src,experimenter_name,figHandle) +rat_name = src.String; +close(figHandle); +runrats('init'); +runrats('update exp_rat_userclick',experimenter_name,rat_name); end % === Create buttons dynamically and return handles === function btns = createButtons(figHandle, optionList, callbackFcn) - delete(findall(figHandle, 'Type', 'uicontrol')); % Clear any existing buttons - - n = numel(optionList); - btns = gobjects(1, n); - - for i = 1:n - btns(i) = uicontrol(figHandle, ... - 'Style', 'pushbutton', ... - 'Units', 'normalized', ... - 'String', optionList{i}, ... - 'FontSize', 14, ... - 'FontWeight', 'bold', ... - 'BackgroundColor', [0.7 0.8 1], ... - 'Callback', callbackFcn); - end +delete(findall(figHandle, 'Type', 'uicontrol')); % Clear any existing buttons + +n = numel(optionList); +btns = gobjects(1, n); + +for i = 1:n + btns(i) = uicontrol(figHandle, ... + 'Style', 'pushbutton', ... + 'Units', 'normalized', ... + 'String', optionList{i}, ... + 'FontSize', 14, ... + 'FontWeight', 'bold', ... + 'BackgroundColor', [0.7 0.8 1], ... + 'Callback', callbackFcn); +end - resizeButtons(figHandle, btns); % Initial layout +resizeButtons(figHandle, btns); % Initial layout end % === Resize button layout responsively === function resizeButtons(figHandle, buttons) - n = numel(buttons); - spacing = 0.02; - totalSpacing = spacing * (n + 1); - btnHeight = (1 - totalSpacing) / n; - btnWidth = 0.8; - x = (1 - btnWidth) / 2; - - for i = 1:n - y = 1 - spacing - i * (btnHeight + spacing) + spacing; - set(buttons(i), 'Position', [x, y, btnWidth, btnHeight]); - end +n = numel(buttons); +spacing = 0.02; +totalSpacing = spacing * (n + 1); +btnHeight = (1 - totalSpacing) / n; +btnWidth = 0.8; +x = (1 - btnWidth) / 2; + +for i = 1:n + y = 1 - spacing - i * (btnHeight + spacing) + spacing; + set(buttons(i), 'Position', [x, y, btnWidth, btnHeight]); +end end - -function Run_Runrats(experimenter_name, rat_name) -% Identify the bpod port before starting -bpodPort = getOrSetCOMPort(); -disp(['Using COM Port: ' bpodPort]); -bpod(bpodPort); -newstartup; -runrats('init'); -runrats('update exp_rat_userclick',experimenter_name,rat_name); - +function comPort = getOrSetCOMPort() +configFile = fullfile(fileparts(mfilename('fullpath')), 'com_config.mat'); +maxAttempts = 3; +success = false; + +% Try existing config or prompt +if exist(configFile, 'file') + data = load(configFile, 'comPort'); + comPort = data.comPort; +else + comPort = promptForPort(); end -function comPort = getOrSetCOMPort() - configFile = fullfile(fileparts(mfilename('fullpath')), 'com_config.mat'); - maxAttempts = 3; - success = false; - - % Try existing config or prompt - if exist(configFile, 'file') - data = load(configFile, 'comPort'); - comPort = data.comPort; - else +% Try to validate and possibly retry +for attempt = 1:maxAttempts + try + s = serialport(comPort, 9600); % test connection + clear s; % close it immediately + success = true; + break; + catch + fprintf('[Warning] Failed to open %s. Please select a different COM port.\n', comPort); comPort = promptForPort(); end +end - % Try to validate and possibly retry - for attempt = 1:maxAttempts - try - s = serialport(comPort, 9600); % test connection - clear s; % close it immediately - success = true; - break; - catch - fprintf('[Warning] Failed to open %s. Please select a different COM port.\n', comPort); - comPort = promptForPort(); - end - end - - if ~success - error('Failed to find a valid COM port after %d attempts.', maxAttempts); - end +if ~success + error('Failed to find a valid COM port after %d attempts.', maxAttempts); +end - % Save the working port - save(configFile, 'comPort'); +% Save the working port +save(configFile, 'comPort'); end function comPort = promptForPort() - availablePorts = serialportlist("available"); - - if isempty(availablePorts) - warning('No serial ports detected. Enter manually.'); - comPort = input('Enter COM port manually (e.g., COM3): ', 's'); +availablePorts = serialportlist("available"); + +if isempty(availablePorts) + warning('No serial ports detected. Enter manually.'); + comPort = input('Enter COM port manually (e.g., COM3): ', 's'); +else + fprintf('Available COM ports:\n'); + for i = 1:numel(availablePorts) + fprintf(' %d: %s\n', i, availablePorts(i)); + end + idx = input('Select COM port number: '); + if isnumeric(idx) && idx >= 1 && idx <= numel(availablePorts) + comPort = availablePorts(idx); else - fprintf('Available COM ports:\n'); - for i = 1:numel(availablePorts) - fprintf(' %d: %s\n', i, availablePorts(i)); - end - idx = input('Select COM port number: '); - if isnumeric(idx) && idx >= 1 && idx <= numel(availablePorts) - comPort = availablePorts(idx); - else - comPort = input('Enter COM port manually (e.g., COM3): ', 's'); - end + comPort = input('Enter COM port manually (e.g., COM3): ', 's'); end end +end From 1e8bb60ed216736483cd433e20a83fad96aa0e89 Mon Sep 17 00:00:00 2001 From: viktorpm Date: Fri, 25 Apr 2025 18:45:06 +0100 Subject: [PATCH 068/164] rigtest oneclick startup --- ExperPort/Modules/@runrats/runrats.m | 8 +++-- ...eTraining_experimenter_ratname_250423b.mat | Bin 0 -> 16109 bytes .../private/LoadSettingGUI.m | 28 ++++++++++-------- 3 files changed, 20 insertions(+), 16 deletions(-) create mode 100644 ExperPort/settings_@ArpitCentrePokeTraining_experimenter_ratname_250423b.mat diff --git a/ExperPort/Modules/@runrats/runrats.m b/ExperPort/Modules/@runrats/runrats.m index 9c7fb530..443a5f3c 100644 --- a/ExperPort/Modules/@runrats/runrats.m +++ b/ExperPort/Modules/@runrats/runrats.m @@ -751,15 +751,17 @@ % doesn't effect the running of the other cases/functions case 'update exp_rat_userclick' - ExpMenu.value = selected_exp; - runrats(obj,'update_ratmenu',selectedrat); + ExpMenu.value = varargin{1}; + runrats(obj,'update_ratmenu',varargin{2}); %If the rat has changed, let's update it. - if ~strcmp(value(RatMenu),selectedrat) + if ~strcmp(value(RatMenu),varargin{2}) runrats(obj,'update_rat',value(InLiveLoop)); %#ok else %Stay in the loop if we haven't changed anything InLiveLoop.value = 1; end + + runrats(obj,'begin_load_protocol'); %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% case 'update_tech_instructions' %% update_tech_instructions diff --git a/ExperPort/settings_@ArpitCentrePokeTraining_experimenter_ratname_250423b.mat b/ExperPort/settings_@ArpitCentrePokeTraining_experimenter_ratname_250423b.mat new file mode 100644 index 0000000000000000000000000000000000000000..5da9b2ccb9745c8df2533df4339e312c16008eed GIT binary patch literal 16109 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∓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 K`FOsJ_ Date: Sun, 27 Apr 2025 00:23:45 +0100 Subject: [PATCH 069/164] pokesplot debug --- .../ArpitCentrePokeTraining.m | 24 +- .../@ArpitCentrePokeTraining/poke_colors.m | 8 + .../private/LoadSettingGUI.m | 2 +- .../@ArpitCentrePokeTraining/state_colors.m | 24 +- .../ArpitSoundCalibration.asv | 733 ++++++++++++++++++ .../ArpitSoundCalibration.m | 509 +++++------- 6 files changed, 954 insertions(+), 346 deletions(-) create mode 100644 Protocols/@ArpitCentrePokeTraining/poke_colors.m create mode 100644 Protocols/@ArpitSoundCalibration/ArpitSoundCalibration.asv diff --git a/Protocols/@ArpitCentrePokeTraining/ArpitCentrePokeTraining.m b/Protocols/@ArpitCentrePokeTraining/ArpitCentrePokeTraining.m index 56a32fa3..53ff96a2 100644 --- a/Protocols/@ArpitCentrePokeTraining/ArpitCentrePokeTraining.m +++ b/Protocols/@ArpitCentrePokeTraining/ArpitCentrePokeTraining.m @@ -166,28 +166,14 @@ %AthenaSMA changed to SoundCatSMA (From AthenaDelayComp) SoloFunctionAddVars('ParamsSection', 'ro_args', ... {'maxasymp';'slp';'inflp';'minasymp';'assym'}); - [x, y] = WaterValvesSection(obj, 'init', x, y); - - % For plotting with the pokesplot plugin, we need to tell it what - % colors to plot with: - my_state_colors = ArpitCentrePokeTrainingSMA(obj, 'get_state_colors'); - % In pokesplot, the poke colors have a default value, so we don't need - % to specify them, but here they are so you know how to change them. - my_poke_colors = struct( ... - 'L', 0.6*[1 0.66 0], ... - 'C', [0 0 0], ... - 'R', 0.9*[1 0.66 0]); - - [x, y] = PokesPlotSection(obj, 'init', x, y, ... - struct('states', my_state_colors, 'pokes', my_poke_colors)); next_row(y); - - [x, y] = CommentsSection(obj, 'init', x, y); - % [x, y] = PunishmentSection(obj, 'init', x, y); %#ok + [x, y] = WaterValvesSection(obj, 'init', x, y); + [x, y] = PokesPlotSection(obj, 'init', x, y); + next_row(y); + [x, y] = CommentsSection(obj, 'init', x, y); next_column(x); y=5; [x, y] = SessionPerformanceSection(obj, 'init', x, y); - [x, y] = ParamsSection(obj, 'init', x, y); %#ok - + [x, y] = ParamsSection(obj, 'init', x, y); %#ok [x, y] = SoundSection(obj,'init',x,y); [stage_fig_x,stage_fig_y] = Training_ParamsSection(obj, 'init', x, y); SoloParamHandle(obj, 'stage_fig_x', 'value', stage_fig_x); diff --git a/Protocols/@ArpitCentrePokeTraining/poke_colors.m b/Protocols/@ArpitCentrePokeTraining/poke_colors.m new file mode 100644 index 00000000..a235c9b5 --- /dev/null +++ b/Protocols/@ArpitCentrePokeTraining/poke_colors.m @@ -0,0 +1,8 @@ +function PC = poke_colors(obj) + + +PC = struct( ... + 'L', 0.6*[1 0.66 0], ... + 'C', [0 0 0], ... + 'R', 0.9*[1 0.66 0]); + diff --git a/Protocols/@ArpitCentrePokeTraining/private/LoadSettingGUI.m b/Protocols/@ArpitCentrePokeTraining/private/LoadSettingGUI.m index 80b71f0d..e1c36ff0 100644 --- a/Protocols/@ArpitCentrePokeTraining/private/LoadSettingGUI.m +++ b/Protocols/@ArpitCentrePokeTraining/private/LoadSettingGUI.m @@ -19,8 +19,8 @@ % Identify the bpod port before starting % bpodPort = getOrSetCOMPort(); % disp(['Using COM Port: ' bpodPort]); -Bpod; % Bpod(bpodPort); +Bpod; newstartup; % PRESENT THE USER WITH THE CHOICE OF EXPERIMENTER/RAT diff --git a/Protocols/@ArpitCentrePokeTraining/state_colors.m b/Protocols/@ArpitCentrePokeTraining/state_colors.m index c6df1d67..1e3b1737 100644 --- a/Protocols/@ArpitCentrePokeTraining/state_colors.m +++ b/Protocols/@ArpitCentrePokeTraining/state_colors.m @@ -1,16 +1,14 @@ function SC = state_colors(obj) %#ok SC = struct( ... - 'wait_for_cpoke', [0.68 1 0.63], ... - 'cp', [0.63 1 0.94], ... - 'cp_legal_cbreak_period', [0.63 1 0.94]*0.8, ... - 'sideled_on', [1 0.79 0.63], ... - 'wait_for_collecting_reward', [0.53 0.78 1.00],... - 'righthit', [0.3 0.9 0], ... - 'lefthit', [0 0.9 0.3], ... - 'hit_state', [0.77 0.60 0.48], ... - 'second_hit_state', [0.25 0.45 0.48], ... - 'drink_state', [0 1 0], ... - 'error_state', [1 0.54 0.54], ... - 'violation_state', [0.31 0.48 0.30], ... - 'timeout_state', 0.8*[0.31 0.48 0.30]); \ No newline at end of file + 'wait_for_cpoke', [0.68 1 0.63], ... + 'settling_in_state', [0.63 1 0.94], ... + 'legal_poke_start_state', [0.63 1 0.94]*0.8, ... + 'legal_poke_end_state', [1 0.79 0.63], ... + 'soft_cp', [0.3 0.9 0], ... + 'side_led_wait_RewardCollection', [0.53 0.78 1.00],... + 'hit_state', [0.77 0.60 0.48], ... + 'second_hit_state', [0.25 0.45 0.48], ... + 'drink_state', [0 1 0], ... + 'violation_state', [0.31 0.48 0.30], ... + 'timeout_state', 0.8*[0.31 0.48 0.30]); diff --git a/Protocols/@ArpitSoundCalibration/ArpitSoundCalibration.asv b/Protocols/@ArpitSoundCalibration/ArpitSoundCalibration.asv new file mode 100644 index 00000000..69ddc99c --- /dev/null +++ b/Protocols/@ArpitSoundCalibration/ArpitSoundCalibration.asv @@ -0,0 +1,733 @@ + +% MATLAB script for calibrating sound pressure level using Arduino and serial communication in real-time. +% +% This script automates the process of calibrating a speaker using an Arduino and a sound level meter. +% It communicates with an Arduino to control speaker output and read sound pressure level (SPL) measurements. +% The script performs the following steps: +% 1. Establishes serial communication with the Arduino. +% 2. Creates a figure window with a table to display real-time SPL readings. +% 3. Iterates through a set of speaker output values, sending each value to the speaker (via a user-defined function). +% 4. At each speaker output level, the script reads the corresponding SPL from the Arduino. +% 5. The measured SPL values are displayed in the table in real-time. +% 6. After completing the measurements, the script displays the final calibration data and optionally plots the +% relationship between speaker output and SPL, including a polynomial fit. +% 7. Includes error handling to ensure robust communication with the Arduino. + +% Written by Arpit 2024 + +% Make sure you ran newstartup, then dispatcher('init'), and you're good to +% go! +% + +function [obj] = Arpit_SoundCalibration(varargin) + +% Default object is of our own class (mfilename); in this simplest of +% protocols, we inherit only from Plugins/@pokesplot + +obj = class(struct, mfilename, soundmanager, soundui); + +%--------------------------------------------------------------- +% BEGIN SECTION COMMON TO ALL PROTOCOLS, DO NOT MODIFY +%--------------------------------------------------------------- + +% If creating an empty object, return without further ado: +if nargin==0 || (nargin==1 && ischar(varargin{1}) && strcmp(varargin{1}, 'empty')), + return; +end; + +if isa(varargin{1}, mfilename), % If first arg is an object of this class itself, we are + % Most likely responding to a callback from + % a SoloParamHandle defined in this mfile. + if length(varargin) < 2 || ~ischar(varargin{2}), + error(['If called with a "%s" object as first arg, a second arg, a ' ... + 'string specifying the action, is required\n']); + else action = varargin{2}; varargin = varargin(3:end); %#ok + end; +else % Ok, regular call with first param being the action string. + action = varargin{1}; varargin = varargin(2:end); %#ok +end; +if ~ischar(action), error('The action parameter must be a string'); end; + +GetSoloFunctionArgs(obj); + +%--------------------------------------------------------------- +% END OF SECTION COMMON TO ALL PROTOCOLS, MODIFY AFTER THIS LINE +%--------------------------------------------------------------- + + +% ---- From here on is where you can put the code you like. +% +% Your protocol will be called, at the appropriate times, with the +% following possible actions: +% +% 'init' To initialize -- make figure windows, variables, etc. +% +% 'update' Called periodically within a trial +% +% 'prepare_next_trial' Called when a trial has ended and your protocol +% is expected to produce the StateMachine diagram for the next +% trial; i.e., somewhere in your protocol's response to this +% call, it should call "dispatcher('send_assembler', sma, +% prepare_next_trial_set);" where sma is the +% StateMachineAssembler object that you have prepared and +% prepare_next_trial_set is either a single string or a cell +% with elements that are all strings. These strings should +% correspond to names of states in sma. +% Note that after the 'prepare_next_trial' call, further +% events may still occur in the RTLSM while your protocol is thinking, +% before the new StateMachine diagram gets sent. These events +% will be available to you when 'trial_completed' is called on your +% protocol (see below). +% +% 'trial_completed' Called when 'state_0' is reached in the RTLSM, +% marking final completion of a trial (and the start of +% the next). +% +% 'close' Called when the protocol is to be closed. +% +% +% VARIABLES THAT DISPATCHER WILL ALWAYS INSTANTIATE FOR YOU IN YOUR +% PROTOCOL: +% +% (These variables will be instantiated as regular Matlab variables, +% not SoloParamHandles. For any method in your protocol (i.e., an m-file +% within the @your_protocol directory) that takes "obj" as its first argument, +% calling "GetSoloFunctionArgs(obj)" will instantiate all the variables below.) +% +% +% n_done_trials How many trials have been finished; when a trial reaches +% one of the prepare_next_trial states for the first +% time, this variable is incremented by 1. +% +% n_started trials How many trials have been started. This variable gets +% incremented by 1 every time the state machine goes +% through state 0. +% +% parsed_events The result of running disassemble.m, with the +% parsed_structure flag set to 1, on all events from the +% start of the current trial to now. +% +% latest_events The result of running disassemble.m, with the +% parsed_structure flag set to 1, on all new events from +% the last time 'update' was called to now. +% +% raw_events All the events obtained in the current trial, not parsed +% or disassembled, but raw as gotten from the State +% Machine object. +% +% current_assembler The StateMachineAssembler object that was used to +% generate the State Machine diagram in effect in the +% current trial. +% +% Trial-by-trial history of parsed_events, raw_events, and +% current_assembler, are automatically stored for you in your protocol by +% dispatcher.m. See the wiki documentation for information on how to access +% those histories from within your protocol and for information. +% +% + + +switch action + + %--------------------------------------------------------------- + % CASE INIT + %--------------------------------------------------------------- + + case 'init' + + % Make default figure. We remember to make it non-saveable; on next run + % the handle to this figure might be different, and we don't want to + % overwrite it when someone does load_data and some old value of the + % fig handle was stored as SoloParamHandle "myfig" + SoloParamHandle(obj, 'myfig', 'saveable', 0); myfig.value = double(figure); + + % Make the title of the figure be the protocol name, and if someone tries + % to close this figure, call dispatcher's close_protocol function, so it'll know + % to take it off the list of open protocols. + name = mfilename; + set(value(myfig), 'Name', name, 'Tag', name, ... + 'closerequestfcn', [mfilename,'(''close'');'], 'MenuBar', 'none'); + + + % Generate the sounds we need. + soundserver = bSettings('get','RIGS','sound_machine_server'); + if ~isempty(soundserver) + sr = SoundManagerSection(obj,'get_sample_rate'); + Fs=sr; + lfreq=2000; + hfreq=20000; + freq = 100; + T = 5; + fcut = 110; + filter_type = 'GAUS'; + A1_sigma = 0.0500; + A2_sigma = 0.0306; %0.1230;%0.0260; + A3_sigma = 0.0187; %0.0473;%0.0135; + A4_sigma = 0.0114; %0.0182;%0.0070; + A5_sigma = 0.0070; + [rawA1 rawA2 normA1 normA2]=noisestim(1,1,T,fcut,Fs,filter_type); + modulator=singlenoise(1,T,[lfreq hfreq],Fs,'BUTTER'); + AUD1=normA1(1:T*sr).*modulator(1:T*sr).*A1_sigma; + AUD2=normA1(1:T*sr).*modulator(1:T*sr).*A2_sigma; + AUD3=normA1(1:T*sr).*modulator(1:T*sr).*A3_sigma; + AUD4=normA1(1:T*sr).*modulator(1:T*sr).*A4_sigma; + AUD5=normA1(1:T*sr).*modulator(1:T*sr).*A5_sigma; + + if ~isempty(AUD2) + SoundManagerSection(obj, 'declare_new_sound', 'left_sound', [AUD2'; AUD2']) + end + if ~isempty(AUD1) + SoundManagerSection(obj, 'declare_new_sound', 'center_sound', [AUD1'; AUD1']) + end + if ~isempty(AUD3) + SoundManagerSection(obj, 'declare_new_sound', 'right_sound', [AUD3'; AUD3']) + end + if ~isempty(AUD4) + SoundManagerSection(obj, 'declare_new_sound', 'fourth_sound', [AUD4'; AUD4']) + end + if ~isempty(AUD5) + SoundManagerSection(obj, 'declare_new_sound', 'fifth_sound', [AUD5'; AUD5']) + end + SoundManagerSection(obj, 'send_not_yet_uploaded_sounds'); + end + + % Get monitor dimensions for dynamic GUI sizing + MP = get(0,'MonitorPositions'); + + % Calculate individual group width based on screen dimensions and number of groups + groupwidth = floor((MP(3)/2)/numel(linegroups)); + + padding = 10 % padding around GUI elements + + % Calculate total width needed for all groups combined + total_width = floor(numel(linegroups) * groupwidth+padding); + total_height = 400 % total height of GUI + + label_height = 140 % height of port label + button_height = 25 + + % Center the GUI horizontally on screen + left_pos = floor((MP(3) - total_width) / 2); + + % Set figure position with centered alignment and fixed height of 400 pixels + % position vector: [left-right, down-up, width, height], the origo is + % the bottom left corner + set(value(myfig), 'Position', [left_pos, floor((MP(4)-total_height)/2), total_width, total_height]); + + % Initialize array to store line names + line_names = []; + + % Iterate through each line group + for i = 1:numel(linegroups) + % Check if current group exists and has valid parameters + if ~isempty(linegroups{i}) && ~isempty(linegroups{i}{1,2}) + % port label position + SubheaderParam( ... + obj, ... + ['Input',linegroups{i}{1,2}], ... + {linegroups{i}{1,2};0},0,0, ... + 'position',[((i-1)*groupwidth)+padding, ... + total_height-padding-label_height, ... + groupwidth-padding, ... + label_height ... + ]); + + % port label aesthetics + set(get_ghandle(eval(['Input',linegroups{i}{1,2}])), ... + 'FontSize',32, ... % Large font size for visibility + 'BackgroundColor',[0,1,0], ... % Green background + 'HorizontalAlignment','center'); % Center align text + + % Store line name for later reference + line_names(end+1) = linegroups{i}{1,2}; %#ok + + % Add sound controls based on line identifier + if strcmp(linegroups{i}{1,2},'L') && ~isempty(soundserver) + % Left channel sound toggle + ToggleParam(obj, ... + 'LeftSound', 0,0,0, ... + 'position',[((i-1)*groupwidth)+padding, padding, groupwidth-padding,button_height], ... + 'OnString', 'Sound Two ON', ... + 'OffString', 'Sound Two OFF'); + set_callback(LeftSound,{mfilename,'play_left_sound'}); + + elseif strcmp(linegroups{i}{1,2},'R') && ~isempty(soundserver) + % Right channel sound toggle + ToggleParam(obj, ... + 'RightSound', 0,0,0, ... + 'position',[((i-1)*groupwidth)+padding, padding, groupwidth-padding,button_height], ... + 'OnString', 'Sound Three ON', ... + 'OffString', 'Sound Three OFF'); + set_callback(RightSound,{mfilename,'play_right_sound'}); + + elseif strcmp(linegroups{i}{1,2},'C') && ~isempty(soundserver) + % Center channel sound toggle + ToggleParam(obj, ... + 'CenterSound', 0,0,0, ... + 'position',[((i-1)*groupwidth)+padding, padding, groupwidth-padding,button_height], ... + 'OnString', 'Sound One ON', ... + 'OffString', 'Sound One OFF'); + set_callback(CenterSound,{mfilename,'play_center_sound'}); + + elseif strcmp(linegroups{i}{1,2},'A') && ~isempty(soundserver) + % Additional channel sound toggle + ToggleParam(obj, ... + 'FourthSound', 0,0,0, ... + 'position',[((i-1)*groupwidth)+padding, padding, groupwidth-padding,button_height], ... + 'OnString', 'Sound Four ON', ... + 'OffString', 'Sound Four OFF'); + set_callback(FourthSound,{mfilename,'play_fourth_sound'}); + end + + % Handle case for empty line identifier but existing group + elseif ~isempty(linegroups{i}) && isempty(linegroups{i}{1,2}) + ToggleParam(obj, ... + 'FifthSound', 0,0,0, ... + 'position',[((i-1)*groupwidth)+padding, padding, groupwidth-padding,button_height], ... + 'OnString', 'Sound Five ON', ... + 'OffString', 'Sound Five OFF'); + set_callback(FifthSound,{mfilename,'play_fifth_sound'}); + end + + + + SoloParamHandle(obj,'LineGroups','value',linegroups); + SoloParamHandle(obj,'LineNames','value',line_names); + + scr = timer; + set(scr,'Period', 0.2,'ExecutionMode','FixedRate','TasksToExecute',Inf,... + 'BusyMode','drop','TimerFcn',[mfilename,'(''close_continued'')']); + SoloParamHandle(obj, 'stopping_complete_timer', 'value', scr); + + Arpit_SoundCalibration(obj,'prepare_next_trial'); + + dispatcher('Run'); + + + case 'play_left_sound' + %% play_left_sound + if value(LeftSound) == 1 + SoundManagerSection(obj,'play_sound','left_sound'); + else + SoundManagerSection(obj,'stop_sound','left_sound'); + end + + case 'play_right_sound' + %% play_right_sound + if value(RightSound) == 1 + SoundManagerSection(obj,'play_sound','right_sound'); + else + SoundManagerSection(obj,'stop_sound','right_sound'); + end + + case 'play_center_sound' + %% play_center_sound + if value(CenterSound) == 1 + SoundManagerSection(obj,'play_sound','center_sound'); + else + SoundManagerSection(obj,'stop_sound','center_sound'); + end + + case 'play_fourth_sound' + %% play_fourth_sound + if value(FourthSound) == 1 + SoundManagerSection(obj,'play_sound','fourth_sound'); + else + SoundManagerSection(obj,'stop_sound','fourth_sound'); + end + case 'play_fifth_sound' + %% play_fifth_sound + if value(FifthSound) == 1 + SoundManagerSection(obj,'play_sound','fifth_sound'); + else + SoundManagerSection(obj,'stop_sound','fifth_sound'); + end + + case 'toggle16_2' + %% case toggle1_2 + linegroups = value(LineGroups); + dispatcher('toggle_bypass',log2(linegroups{16}{3,1})); + + %--------------------------------------------------------------- + % CASE PREPARE_NEXT_TRIAL + %--------------------------------------------------------------- + case 'prepare_next_trial' + line_names = value(LineNames); + sma = StateMachineAssembler('full_trial_structure','use_happenings',1); %,'n_input_lines',numel(line_names),'line_names',line_names); + sma = add_state(sma, 'name', 'the_only_state', 'self_timer',1e4, 'input_to_statechange', {'Tup', 'final_state'}); + sma = add_state(sma, 'name', 'final_state', 'self_timer',1e4, 'input_to_statechange', {'Tup', 'check_next_trial_ready'}); + dispatcher('send_assembler', sma, 'final_state'); + + %--------------------------------------------------------------- + % CASE TRIAL_COMPLETED + %--------------------------------------------------------------- + case 'trial_completed' + + + %--------------------------------------------------------------- + % CASE UPDATE + %--------------------------------------------------------------- + case 'update' + pe = parsed_events; %#ok + linenames = value(LineNames); + + for i = 1:numel(linenames) + poketimes = eval(['pe.pokes.',linenames(i)]); + if ~isempty(poketimes) && isnan(poketimes(end,2)) + set(get_ghandle(eval(['Input',linenames(i)])),'BackgroundColor',[1,0,0]); + + str = get(get_ghandle(eval(['Input',linenames(i)])),'string'); + str{2} = size(poketimes,1); + set(get_ghandle(eval(['Input',linenames(i)])),'string',str); + + else + set(get_ghandle(eval(['Input',linenames(i)])),'BackgroundColor',[0,1,0]); + end + end + + %--------------------------------------------------------------- + % CASE CLOSE + %--------------------------------------------------------------- + case 'close' + + dispatcher('Stop'); + + %Let's pause until we know dispatcher is done running + set(value(stopping_complete_timer),'TimerFcn',[mfilename,'(''close_continued'');']); + start(value(stopping_complete_timer)); + + case 'close_continued' + + if value(stopping_process_completed) + stop(value(stopping_complete_timer)); %Stop looping. + %dispatcher('set_protocol',''); + + if exist('myfig', 'var') && isa(myfig, 'SoloParamHandle') && ishandle(value(myfig)), %#ok + delete(value(myfig)); + end + delete_sphandle('owner', ['^@' class(obj) '$']); + dispatcher('set_protocol',''); + end + + otherwise + + warning('Unknown action! "%s"\n', action); %#ok + end + +return; + +end + + + + + + + + + + + + + + + + +function [normbase]=singlenoise(sigma_1,T,fcut,Fs,filter_type) + +%%%%%%%%%%%%%%%%% Determines of the type of filter used %%%%%%%%%%%%%%%%%%% +%'LPFIR': lowpass FIR%%%%%'FIRLS': Least square linear-phase FIR filter design +%'BUTTER': IIR Butterworth lowpass filter%%%%%%'GAUS': Gaussian filter (window) +%'MOVAVRG': Moving average FIR filter%%%%%%%%'KAISER': Kaiser-window FIR filtering +% 'EQUIRIP':Eqiripple FIR filter%%%%% 'HAMMING': Hamming-window based FIR +% T is duration of each signal in milisecond, fcut is the cut-off frequency +% Fs is the sampling frequency +% outband=40; +% filter_type='BUTTER'; + +outband=60; +replace=1; +L=floor(T*Fs);% Length of signal + +%%%%%%%%%%% produce position values %%%%%%% +pos1 = sigma_1 * randn(Fs,1); + +% pos1(pos1>outband)=[]; +% pos1(pos1<-outband)=[]; + +base = randsample(pos1,L,replace); +%%%% Filter the original position values %%%%%% +%filtbase=filt(base,fcut,Fs,filter_type); +hf = design(fdesign.bandpass('N,F3dB1,F3dB2',10,fcut(1),fcut(2),Fs)); +filtbase=filter(hf,base); +normbase=filtbase./(max(abs(filtbase))); + +end + + +function [base,target,normbase,normtarget]=noisestim(sigma_1,sigma_2,T,fcut,Fs,filter_type) + +%%%%%%%%%%%%%%%%% Determines of the type of filter used %%%%%%%%%%%%%%%%%%% +%'LPFIR': lowpass FIR%%%%%'FIRLS': Least square linear-phase FIR filter design +%'BUTTER': IIR Butterworth lowpass filter%%%%%%'GAUS': Gaussian filter (window) +%'MOVAVRG': Moving average FIR filter%%%%%%%%'KAISER': Kaiser-window FIR filtering +% 'EQUIRIP':Eqiripple FIR filter%%%%% 'HAMMING': Hamming-window based FIR +% T is duration of each signal in milisecond, fcut is the cut-off frequency +% Fs is the sampling frequency +% outband=40; +replace=1; +L=floor(T*Fs); % Length of signal +% t=L*linspace(0,1,L)/Fs; % time in miliseconds +%%%%%%%%%%% produce position values %%%%%%% +pos1 = sigma_1*randn(Fs,1); +% pos1(pos1>outband)=[]; +% pos1(pos1<-outband)=[]; + +pos2 =sigma_2*randn(Fs,1); +% pos2(pos2>outband)=[]; +% pos2(pos2<-outband)=[]; +base = randsample(pos1,L,replace); +target = randsample(pos2,L,replace); +%%%% Filter the original position values %%%%%% +filtbase=filt(base,fcut,Fs,filter_type); +filttarget=filt(target,fcut,Fs,filter_type); +normbase=filtbase./(max(abs(filtbase))); +normtarget=filttarget./(max(abs(filttarget))); + +end + + + + +function filtsignal=filt(signal,fcut,Fs,filter_type) +a=2; % wp/ws used in butterworth method and LS linear FIR method +N=200; % filter order used in lowpass FIR method +rp=3; % passband ripple in dB used in butterworth method +rs=60; % stopband attenuation in dB used in butterworth method +beta=0.1102*(rs-8.8); %used in Kaiser window to obtain sidelobe attenuation of rs dB +if strcmp(filter_type, 'GAUS') || strcmp(filter_type, 'MOVAVRG') +window = fix(Fs/fcut); % window size used in Gaussian and moving average methods +end +wp=2*fcut/Fs; % normalized passband corner frequency wp, the cutoff frequency +ws=a*wp; % normalized stopband corner frequency + +switch filter_type + case 'BUTTER' %Butterworth IIR filter + if length(wp)>1 + ws(1)=2*(fcut(1)/2)/Fs; + ws(2)=2*(fcut(2)+fcut(1)/2)/Fs; + [n,wn]=buttord(wp,ws,rp,rs); + [b,a]=butter(n,wn,'bandpass'); + else + [n,wn]=buttord(wp,ws,rp,rs); + [b,a]=butter(n,wn,'low'); + end + filtsignal=filter(b,a,signal);%conventional filtering + case 'LPFIR' %Lowpass FIR filter + d=fdesign.lowpass('N,Fc',N,fcut,Fs); % Fc is the 6-dB down point, N is the filter order(N+1 filter coefficients) + Hd = design(d); + filtsignal=filter(Hd.Numerator,1,signal); %conventional filtering + case 'FIRLS' %Least square linear-phase FIR filter design + b=firls(255,[0 2*fcut/Fs a*2*fcut/Fs 1],[1 1 0 0]); + filtsignal=filter(b,1,signal); %conventional filtering + case 'EQUIRIP' %Eqiripple FIR filter + d=fdesign.lowpass('Fp,Fst,Ap,Ast',wp,ws,rp,rs); + Hd=design(d,'equiripple'); + filtsignal=filter(Hd.Numerator,1,signal); %conventional filtering + case 'MOVAVRG' % Moving average FIR filtering, Rectangular window + h = ones(window,1)/window; + b = fir1(window-1,wp,h); + filtsignal = filter(b, 1, signal); + case 'HAMMING' % Hamming-window based FIR filtering + b = fir1(150,wp); + filtsignal = filter(b, 1, signal); + filtsignal = filter(h, 1, signal); + case 'GAUS' % Gaussian-window FIR filtering + h = normpdf(1:window, 0, fix(window/2)); + b = fir1(window-1,wp,h); + filtsignal = filter(b, 1, signal); + case 'GAUS1' % Gaussian-window FIR filtering + b = fir1(window-1,wp,gausswin(window,2)/window); + filtsignal = filter(b, 1, signal); + case 'KAISER' %Kaiser-window FIR filtering + h=kaiser(window,beta); + b = fir1(window-1,wp,h); + filtsignal = filter(b, 1, signal); + + otherwise + sprintf('filter_type is wrong!! havaset kojast!!') +end + +end + + + +% MATLAB script for calibrating sound pressure level using Arduino and serial communication in real-time + +% --- Configuration --- +arduinoPort = 'COM3'; % Replace with the actual COM port of your Arduino +baudRate = 115200; +speakerOutputValues = 0:10:100; % Example range of speaker output values +numRepetitions = 3; % Number of times to repeat each measurement +tableTitle = 'Speaker Calibration Data'; % Title for the table + +% --- Initialize Serial Communication with Arduino --- +try + % Create serial port object + arduinoSerial = serial(arduinoPort, 'BaudRate', baudRate); + % Open the serial port + fopen(arduinoSerial); + % Set a timeout to prevent MATLAB from waiting indefinitely + arduinoSerial.Timeout = 10; % in seconds + % Read the "Arduino Ready" message + arduinoReady = fgetl(arduinoSerial); + if ~strcmp(arduinoReady, 'Arduino Ready') + error('MATLAB:ArduinoNotReady', 'Arduino did not send the "Ready" signal. Check the Arduino code and connection.'); + end + disp('Arduino connected and ready.'); + + % --- Create Figure and Table --- + fig = figure('Name', tableTitle, 'NumberTitle', 'off'); + % Create an empty table in the figure + hTable = uitable(fig, 'Data', zeros(0, numRepetitions + 1), ... + 'ColumnName', [{'SpeakerValue'}, arrayfun(@(i) ['SPL_Rep' num2str(i)], 1:numRepetitions, 'UniformOutput', false)], ... + 'RowName', [], ... + 'Position', [20 20 400 300]); % Adjust position as needed + + % --- Preallocate Data Storage (for efficiency) --- + numValues = length(speakerOutputValues); + dataMatrix = zeros(numValues * numRepetitions, numRepetitions + 1); % +1 for speakerValue + + % --- Calibration Loop --- + disp('Starting calibration...'); + for i = 1:numValues + speakerValue = speakerOutputValues(i); + disp(['Setting speaker output to: ' num2str(speakerValue)]); + + % Store the speaker value in the data matrix + dataMatrix((i - 1) * numRepetitions + 1:i * numRepetitions, 1) = speakerValue; + + for j = 1:numRepetitions + fprintf(' Repetition %d: ', j); + % *** Replace this with your function to control the speaker in MATLAB *** + setSpeakerLevel(speakerValue); % Call the speaker control function + % ---------------------------------------------------------------------- + + % Read the SPL value from Arduino + try + splString = fgetl(arduinoSerial); + splValue = str2double(splString); + if isnan(splValue) + error('MATLAB:InvalidSPLValue', 'Received non-numeric SPL value from Arduino.'); + end + disp(['Received SPL: ' num2str(splValue) ' dB']); + dataMatrix((i - 1) * numRepetitions + j, j + 1) = splValue; % Store in matrix + + catch ME + % Handle errors, such as timeout or non-numeric data + disp(['Error: ' ME.message]); + if (strcmp(ME.identifier, 'MATLAB:serial:fread:timeout')) + disp(' Timeout occurred while waiting for data from Arduino.'); + elseif (strcmp(ME.identifier, 'MATLAB:InvalidSPLValue')) + disp(' Non-numeric data received from Arduino.'); + end + % Consider adding a 'continue' here to proceed to the next repetition + % even if one fails. Otherwise, the script will stop. + continue; % Add this to continue to the next iteration + end + pause(0.5); % Short pause + + % --- Update the Table in the Figure --- + % Create a temporary table and update the figure's table + tempTable = array2table(dataMatrix(1:i * numRepetitions, :), 'VariableNames', [{'SpeakerValue'}, arrayfun(@(i) ['SPL_Rep' num2str(i)], 1:numRepetitions, 'UniformOutput', false)]); + set(hTable, 'Data', tempTable); + drawnow; % Force the figure to update + end + end + + % --- Close Serial Port --- + fclose(arduinoSerial); + delete(arduinoSerial); + clear arduinoSerial; + + % --- Display Results in a Table --- + calibrationTable = array2table(dataMatrix, 'VariableNames', [{'SpeakerValue'}, arrayfun(@(i) ['SPL_Rep' num2str(i)], 1:numRepetitions, 'UniformOutput', false)]); + disp(tableTitle); + disp(calibrationTable); + + % --- Optional: Plot Calibration Data --- + figure; % Create a new figure for the plot + plot(calibrationTable.SpeakerValue, calibrationTable{:, 2:end}, '-o'); % Plot all repetitions + xlabel('Speaker Output Value'); + ylabel('SPL (dB)'); + title('Speaker Calibration Curve'); + legend([columnNames{2:end}]); + grid on; + + % --- Optional: Polynomial Fit and Display Equation --- + degree = 2; % You can change the degree of the polynomial + p = polyfit(calibrationTable.SpeakerValue, mean(calibrationTable{:, 2:end}, 2), degree); % Fit to the *mean* SPL + fittedSPL = polyval(p, calibrationTable.SpeakerValue); + hold on; + plot(calibrationTable.SpeakerValue, fittedSPL, 'r-', 'LineWidth', 2); % Plot the fit + hold off; + % Display the polynomial equation + equationString = poly2str(p, 'x'); % Use a helper function (defined below) + disp(['Polynomial fit equation: SPL = ' equationString]); + +catch ME + % Handle any errors that occur during the process + disp(['An error occurred: ' ME.message]); + % Clean up the serial port if it was opened + if exist('arduinoSerial', 'var') && isvalid(arduinoSerial) + fclose(arduinoSerial); + delete(arduinoSerial); + clear arduinoSerial; + end +end + +% --- Function to simulate setting the speaker level (REPLACE THIS) --- +function setSpeakerLevel(value) + % Replace this with the actual commands or functions to control the speaker. + % This is just a placeholder for your specific speaker control mechanism. + disp(['(Simulating setting speaker level to: ' num2str(value), ')']); + pause(1); % Simulate speaker level change +end + +% --- Helper function to convert polynomial coefficients to a string --- +function equationString = poly2str(p, varName) + % Converts a polynomial coefficient vector (as returned by polyfit) + % to a string representation of the polynomial equation. + % + % Example: + % p = [1 2 3]; % Coefficients for 1x^2 + 2x + 3 + % varName = 'x'; + % equationString = poly2str(p, varName); % Returns '1x^2 + 2x + 3' + + n = length(p); + equationString = ''; + for i = 1:n + coeff = p(i); + if coeff ~= 0 + if ~isempty(equationString) + equationString = [equationString, ' + ']; % Use '+' sign (except for the first term) + end + if coeff == 1 && i < n %suppress 1 + % equationString = [equationString, varName]; + elseif coeff ~= 1 + equationString = [equationString, num2str(coeff)]; + end + + if i < n + equationString = [equationString, varName]; + if i < n - 1 + equationString = [equationString, '^', num2str(n - i)]; + end + elseif coeff ~= 0 + equationString = [equationString, num2str(coeff)]; + end + end + end + % Replace "+ -" with "-" + equationString = strrep(equationString, '+ -', '-'); +end diff --git a/Protocols/@ArpitSoundCalibration/ArpitSoundCalibration.m b/Protocols/@ArpitSoundCalibration/ArpitSoundCalibration.m index 4979e981..de805b7b 100644 --- a/Protocols/@ArpitSoundCalibration/ArpitSoundCalibration.m +++ b/Protocols/@ArpitSoundCalibration/ArpitSoundCalibration.m @@ -1,11 +1,19 @@ -%This is a new version of the old stand alone rigtester.m did not run -%through dispatcher. This is treated like any other protocol. It is -%designed to be plastic, looking at a rigs settings files, determining -%the input and output lines, and building a GUI specific to that rigs -%componants. +% MATLAB script for calibrating sound pressure level using Arduino and serial communication in real-time. % -%Written by Chuck 2017 +% This script automates the process of calibrating a speaker using an Arduino and a sound level meter. +% It communicates with an Arduino to control speaker output and read sound pressure level (SPL) measurements. +% The script performs the following steps: +% 1. Establishes serial communication with the Arduino. +% 2. Creates a figure window with a table to display real-time SPL readings. +% 3. Iterates through a set of speaker output values, sending each value to the speaker (via a user-defined function). +% 4. At each speaker output level, the script reads the corresponding SPL from the Arduino. +% 5. The measured SPL values are displayed in the table in real-time. +% 6. After completing the measurements, the script displays the final calibration data and optionally plots the +% relationship between speaker output and SPL, including a polynomial fit. +% 7. Includes error handling to ensure robust communication with the Arduino. + +% Written by Arpit 2024 % Make sure you ran newstartup, then dispatcher('init'), and you're good to % go! @@ -119,7 +127,7 @@ % -switch action, +switch action %--------------------------------------------------------------- % CASE INIT @@ -140,42 +148,6 @@ set(value(myfig), 'Name', name, 'Tag', name, ... 'closerequestfcn', [mfilename,'(''close'');'], 'MenuBar', 'none'); - alldio = bSettings('get','DIOLINES','ALL'); - allinp = bSettings('get','INPUTLINES','ALL'); - dionums = cell2mat(alldio(:,2)); - inpnums = cell2mat(allinp(:,2)); - - for i = 1:16 - inp1 = find(inpnums == i,1,'first'); - dio1 = find(dionums == 2^(( i-1)*2), 1,'first'); - dio2 = find(dionums == 2^(((i-1)*2)+1),1,'first'); - - if isempty(inp1) && isempty(dio1) && isempty(dio2) - continue; - end - - if ~isempty(inp1) - linegroups{i}{1,1} = allinp{inp1,2}; %#ok - linegroups{i}{1,2} = allinp{inp1,1}; %#ok - else - linegroups{i}{1,1} = []; %#ok - linegroups{i}{1,2} = []; %#ok - end - if ~isempty(dio1) - linegroups{i}{2,1} = alldio{dio1,2}; %#ok - linegroups{i}{2,2} = alldio{dio1,1}; %#ok - else - linegroups{i}{2,1} = []; %#ok - linegroups{i}{2,2} = []; %#ok - end - if ~isempty(dio2) - linegroups{i}{3,1} = alldio{dio2,2}; %#ok - linegroups{i}{3,2} = alldio{dio2,1}; %#ok - else - linegroups{i}{3,1} = []; %#ok - linegroups{i}{3,2} = []; %#ok - end - end % Generate the sounds we need. soundserver = bSettings('get','RIGS','sound_machine_server'); @@ -200,20 +172,7 @@ AUD3=normA1(1:T*sr).*modulator(1:T*sr).*A3_sigma; AUD4=normA1(1:T*sr).*modulator(1:T*sr).*A4_sigma; AUD5=normA1(1:T*sr).*modulator(1:T*sr).*A5_sigma; - % snd = MakeBupperSwoop(sr,0,freq,freq, len/2, len/2, 0, 0.1, 'F1_volume_factor',0.07,'F2_volume_factor',0.07); - % silence_length = 0; - % presound_silence = zeros(1,sr*silence_length/1000); - % snd = [presound_silence, snd]; - % - % SoundManagerSection(obj,'declare_new_sound','left_sound', [snd; zeros(1,size(snd,2))]); - % SoundManagerSection(obj,'declare_new_sound','right_sound', [zeros(1,size(snd,2)); snd]); - % - % SoundManagerSection(obj,'loop_sound','left_sound',1); - % SoundManagerSection(obj,'loop_sound','right_sound',1); - % - % SoundManager Section(obj,'declare_new_sound','center_sound',[snd;snd([ceil((sr / freq) / 2):end,1:ceil((sr / freq) / 2)-1])]); - % SoundManagerSection(obj,'loop_sound','center_sound',1); - % + if ~isempty(AUD2) SoundManagerSection(obj, 'declare_new_sound', 'left_sound', [AUD2'; AUD2']) end @@ -259,7 +218,7 @@ line_names = []; % Iterate through each line group - for i = 1:numel(linegroups) + % Check if current group exists and has valid parameters if ~isempty(linegroups{i}) && ~isempty(linegroups{i}{1,2}) % port label position @@ -330,27 +289,7 @@ set_callback(FifthSound,{mfilename,'play_fifth_sound'}); end - % Add first digital I/O control if available - if ~isempty(linegroups{i}) && ~isempty(linegroups{i}{2,2}) - ToggleParam(obj, ... - ['DIO',num2str(i),'_1'],0,0,0, ... - 'position',[((i-1)*groupwidth)+padding, 2*button_height+padding, groupwidth-padding,button_height], ... - 'OnString', [linegroups{i}{2,2},' ON'], ... - 'OffString', [linegroups{i}{2,2},' OFF']); - set_callback(eval(['DIO',num2str(i),'_1']),{mfilename,['toggle',num2str(i),'_1']}); - end - - % Add second digital I/O control if available - if ~isempty(linegroups{i}) && ~isempty(linegroups{i}{3,2}) - ToggleParam(obj, ... - ['DIO',num2str(i),'_2'],0,0,0, ... - 'position',[((i-1)*groupwidth)+padding, button_height+padding, groupwidth-padding,button_height], ... - 'OnString', [linegroups{i}{3,2},' ON'], ... - 'OffString', [linegroups{i}{3,2},' OFF']); - set_callback(eval(['DIO',num2str(i),'_2']),{mfilename,['toggle',num2str(i),'_2']}); - end - end - + SoloParamHandle(obj,'LineGroups','value',linegroups); SoloParamHandle(obj,'LineNames','value',line_names); @@ -403,161 +342,7 @@ else SoundManagerSection(obj,'stop_sound','fifth_sound'); end - case 'toggle1_1' - %% case toggle1_1 - linegroups = value(LineGroups); - dispatcher('toggle_bypass',log2(linegroups{1}{2,1})); - - case 'toggle1_2' - %% case toggle1_2 - linegroups = value(LineGroups); - dispatcher('toggle_bypass',log2(linegroups{1}{3,1})); - - case 'toggle2_1' - %% case toggle1_1 - linegroups = value(LineGroups); - dispatcher('toggle_bypass',log2(linegroups{2}{2,1})); - - case 'toggle2_2' - %% case toggle1_2 - linegroups = value(LineGroups); - dispatcher('toggle_bypass',log2(linegroups{2}{3,1})); - - case 'toggle3_1' - %% case toggle1_1 - linegroups = value(LineGroups); - dispatcher('toggle_bypass',log2(linegroups{3}{2,1})); - - case 'toggle3_2' - %% case toggle1_2 - linegroups = value(LineGroups); - dispatcher('toggle_bypass',log2(linegroups{3}{3,1})); - - case 'toggle4_1' - %% case toggle1_1 - linegroups = value(LineGroups); - dispatcher('toggle_bypass',log2(linegroups{4}{2,1})); - - case 'toggle4_2' - %% case toggle1_2 - linegroups = value(LineGroups); - dispatcher('toggle_bypass',log2(linegroups{4}{3,1})); - - case 'toggle5_1' - %% case toggle1_1 - linegroups = value(LineGroups); - dispatcher('toggle_bypass',log2(linegroups{5}{2,1})); - - case 'toggle5_2' - %% case toggle1_2 - linegroups = value(LineGroups); - dispatcher('toggle_bypass',log2(linegroups{5}{3,1})); - - case 'toggle6_1' - %% case toggle1_1 - linegroups = value(LineGroups); - dispatcher('toggle_bypass',log2(linegroups{6}{2,1})); - - case 'toggle6_2' - %% case toggle1_2 - linegroups = value(LineGroups); - dispatcher('toggle_bypass',log2(linegroups{6}{3,1})); - - case 'toggle7_1' - %% case toggle1_1 - linegroups = value(LineGroups); - dispatcher('toggle_bypass',log2(linegroups{7}{2,1})); - - case 'toggle7_2' - %% case toggle1_2 - linegroups = value(LineGroups); - dispatcher('toggle_bypass',log2(linegroups{7}{3,1})); - - case 'toggle8_1' - %% case toggle1_1 - linegroups = value(LineGroups); - dispatcher('toggle_bypass',log2(linegroups{8}{2,1})); - - case 'toggle8_2' - %% case toggle1_2 - linegroups = value(LineGroups); - dispatcher('toggle_bypass',log2(linegroups{8}{3,1})); - - case 'toggle9_1' - %% case toggle1_1 - linegroups = value(LineGroups); - dispatcher('toggle_bypass',log2(linegroups{9}{2,1})); - - case 'toggle9_2' - %% case toggle1_2 - linegroups = value(LineGroups); - dispatcher('toggle_bypass',log2(linegroups{9}{3,1})); - - case 'toggle10_1' - %% case toggle1_1 - linegroups = value(LineGroups); - dispatcher('toggle_bypass',log2(linegroups{10}{2,1})); - - case 'toggle10_2' - %% case toggle1_2 - linegroups = value(LineGroups); - dispatcher('toggle_bypass',log2(linegroups{10}{3,1})); - - case 'toggle11_1' - %% case toggle1_1 - linegroups = value(LineGroups); - dispatcher('toggle_bypass',log2(linegroups{11}{2,1})); - - case 'toggle11_2' - %% case toggle1_2 - linegroups = value(LineGroups); - dispatcher('toggle_bypass',log2(linegroups{11}{3,1})); - - case 'toggle12_1' - %% case toggle1_1 - linegroups = value(LineGroups); - dispatcher('toggle_bypass',log2(linegroups{12}{2,1})); - - case 'toggle12_2' - %% case toggle1_2 - linegroups = value(LineGroups); - dispatcher('toggle_bypass',log2(linegroups{12}{3,1})); - - case 'toggle13_1' - %% case toggle1_1 - linegroups = value(LineGroups); - dispatcher('toggle_bypass',log2(linegroups{13}{2,1})); - - case 'toggle13_2' - %% case toggle1_2 - linegroups = value(LineGroups); - dispatcher('toggle_bypass',log2(linegroups{13}{3,1})); - - case 'toggle14_1' - %% case toggle1_1 - linegroups = value(LineGroups); - dispatcher('toggle_bypass',log2(linegroups{14}{2,1})); - - case 'toggle14_2' - %% case toggle1_2 - linegroups = value(LineGroups); - dispatcher('toggle_bypass',log2(linegroups{14}{3,1})); - - case 'toggle15_1' - %% case toggle1_1 - linegroups = value(LineGroups); - dispatcher('toggle_bypass',log2(linegroups{15}{2,1})); - - case 'toggle15_2' - %% case toggle1_2 - linegroups = value(LineGroups); - dispatcher('toggle_bypass',log2(linegroups{15}{3,1})); - - case 'toggle16_1' - %% case toggle1_1 - linegroups = value(LineGroups); - dispatcher('toggle_bypass',log2(linegroups{16}{2,1})); - + case 'toggle16_2' %% case toggle1_2 linegroups = value(LineGroups); @@ -619,13 +404,15 @@ if exist('myfig', 'var') && isa(myfig, 'SoloParamHandle') && ishandle(value(myfig)), %#ok delete(value(myfig)); - end; + end delete_sphandle('owner', ['^@' class(obj) '$']); dispatcher('set_protocol',''); end - otherwise, + + otherwise + warning('Unknown action! "%s"\n', action); %#ok -end; + end return; @@ -646,82 +433,6 @@ -function calculate_soundlevel - -% --- Configuration --- -serialPort = '/dev/ttyACM0'; % Replace with the actual serial port of your Raspberry Pi -baudRate = 9600; % Or your desired baud rate -matlabOutputValues = 0:10:100; % Example range of MATLAB output values -calibrationData = []; - -% --- Establish Serial Connection --- -try - s = serial(serialPort, 'BaudRate', baudRate); - fopen(s); - disp(['Opened serial port: ', serialPort, ' at ', num2str(baudRate), ' bps']); - - % --- Calibration Loop --- - for i = 1:length(matlabOutputValues) - outputValue = matlabOutputValues(i); - disp(['Sending MATLAB output value: ', num2str(outputValue)]); - - % --- Send command to set speaker level (replace with your actual command) --- - setSpeakerLevel(outputValue); - - % --- Send command to Raspberry Pi to measure SPL --- - fprintf(s, 'measure_spl\n'); % Send command over serial - - % --- Receive SPL value from Raspberry Pi --- - receivedData = fgetl(s); - if ~isempty(receivedData) - splValue = str2double(receivedData); - if ~isnan(splValue) - disp(['Received SPL: ', num2str(splValue), ' dB']); - calibrationData = [calibrationData; outputValue, splValue]; - pause(2); % Allow time for sound to stabilize and measurement - else - warning('Received non-numeric SPL value.'); - end - else - warning('No data received from Raspberry Pi.'); - end - end - - % --- Close Serial Connection --- - fclose(s); - delete(s); - clear s; - - % --- Display Calibration Data --- - disp('Calibration Data (MATLAB Value, dB SPL):'); - disp(calibrationData); - - % --- Create Calibration Mapping (Example: Linear Fit) --- - p = polyfit(calibrationData(:,1), calibrationData(:,2), 1); - disp('Calibration Polynomial (p(1) * MATLAB_Value + p(2)):'); - disp(p); - % Now you can use this polynomial 'p' to estimate dB SPL from your MATLAB values - % estimatedSPL = polyval(p, yourMatlabValue); - -catch ME - disp(['Error occurred: ', ME.message]); - if exist('s', 'var') && isvalid(s) - fclose(s); - delete(s); - clear s; - end -end - -% --- Your function to control the speaker level in MATLAB --- -function setSpeakerLevel(value) - % Replace this with your actual commands or functions to control the speaker. - disp(['(Simulating setting speaker level to: ', num2str(value), ')']); - pause(1); % Simulate speaker level change -end - -end - - function [normbase]=singlenoise(sigma_1,T,fcut,Fs,filter_type) %%%%%%%%%%%%%%%%% Determines of the type of filter used %%%%%%%%%%%%%%%%%%% @@ -846,5 +557,177 @@ function setSpeakerLevel(value) otherwise sprintf('filter_type is wrong!! havaset kojast!!') end + end + + +% MATLAB script for calibrating sound pressure level using Arduino and serial communication in real-time + +% --- Configuration --- +arduinoPort = 'COM3'; % Replace with the actual COM port of your Arduino +baudRate = 115200; +speakerOutputValues = 0:10:100; % Example range of speaker output values +numRepetitions = 3; % Number of times to repeat each measurement +tableTitle = 'Speaker Calibration Data'; % Title for the table + +% --- Initialize Serial Communication with Arduino --- +try + % Create serial port object + arduinoSerial = serial(arduinoPort, 'BaudRate', baudRate); + % Open the serial port + fopen(arduinoSerial); + % Set a timeout to prevent MATLAB from waiting indefinitely + arduinoSerial.Timeout = 10; % in seconds + % Read the "Arduino Ready" message + arduinoReady = fgetl(arduinoSerial); + if ~strcmp(arduinoReady, 'Arduino Ready') + error('MATLAB:ArduinoNotReady', 'Arduino did not send the "Ready" signal. Check the Arduino code and connection.'); + end + disp('Arduino connected and ready.'); + + % --- Create Figure and Table --- + fig = figure('Name', tableTitle, 'NumberTitle', 'off'); + % Create an empty table in the figure + hTable = uitable(fig, 'Data', zeros(0, numRepetitions + 1), ... + 'ColumnName', [{'SpeakerValue'}, arrayfun(@(i) ['SPL_Rep' num2str(i)], 1:numRepetitions, 'UniformOutput', false)], ... + 'RowName', [], ... + 'Position', [20 20 400 300]); % Adjust position as needed + + % --- Preallocate Data Storage (for efficiency) --- + numValues = length(speakerOutputValues); + dataMatrix = zeros(numValues * numRepetitions, numRepetitions + 1); % +1 for speakerValue + + % --- Calibration Loop --- + disp('Starting calibration...'); + for i = 1:numValues + speakerValue = speakerOutputValues(i); + disp(['Setting speaker output to: ' num2str(speakerValue)]); + + % Store the speaker value in the data matrix + dataMatrix((i - 1) * numRepetitions + 1:i * numRepetitions, 1) = speakerValue; + + for j = 1:numRepetitions + fprintf(' Repetition %d: ', j); + % *** Replace this with your function to control the speaker in MATLAB *** + setSpeakerLevel(speakerValue); % Call the speaker control function + % ---------------------------------------------------------------------- + + % Read the SPL value from Arduino + try + splString = fgetl(arduinoSerial); + splValue = str2double(splString); + if isnan(splValue) + error('MATLAB:InvalidSPLValue', 'Received non-numeric SPL value from Arduino.'); + end + disp(['Received SPL: ' num2str(splValue) ' dB']); + dataMatrix((i - 1) * numRepetitions + j, j + 1) = splValue; % Store in matrix + + catch ME + % Handle errors, such as timeout or non-numeric data + disp(['Error: ' ME.message]); + if (strcmp(ME.identifier, 'MATLAB:serial:fread:timeout')) + disp(' Timeout occurred while waiting for data from Arduino.'); + elseif (strcmp(ME.identifier, 'MATLAB:InvalidSPLValue')) + disp(' Non-numeric data received from Arduino.'); + end + % Consider adding a 'continue' here to proceed to the next repetition + % even if one fails. Otherwise, the script will stop. + continue; % Add this to continue to the next iteration + end + pause(0.5); % Short pause + + % --- Update the Table in the Figure --- + % Create a temporary table and update the figure's table + tempTable = array2table(dataMatrix(1:i * numRepetitions, :), 'VariableNames', [{'SpeakerValue'}, arrayfun(@(i) ['SPL_Rep' num2str(i)], 1:numRepetitions, 'UniformOutput', false)]); + set(hTable, 'Data', tempTable); + drawnow; % Force the figure to update + end + end + + % --- Close Serial Port --- + fclose(arduinoSerial); + delete(arduinoSerial); + clear arduinoSerial; + + % --- Display Results in a Table --- + calibrationTable = array2table(dataMatrix, 'VariableNames', [{'SpeakerValue'}, arrayfun(@(i) ['SPL_Rep' num2str(i)], 1:numRepetitions, 'UniformOutput', false)]); + disp(tableTitle); + disp(calibrationTable); + + % --- Optional: Plot Calibration Data --- + figure; % Create a new figure for the plot + plot(calibrationTable.SpeakerValue, calibrationTable{:, 2:end}, '-o'); % Plot all repetitions + xlabel('Speaker Output Value'); + ylabel('SPL (dB)'); + title('Speaker Calibration Curve'); + legend([columnNames{2:end}]); + grid on; + + % --- Optional: Polynomial Fit and Display Equation --- + degree = 2; % You can change the degree of the polynomial + p = polyfit(calibrationTable.SpeakerValue, mean(calibrationTable{:, 2:end}, 2), degree); % Fit to the *mean* SPL + fittedSPL = polyval(p, calibrationTable.SpeakerValue); + hold on; + plot(calibrationTable.SpeakerValue, fittedSPL, 'r-', 'LineWidth', 2); % Plot the fit + hold off; + % Display the polynomial equation + equationString = poly2str(p, 'x'); % Use a helper function (defined below) + disp(['Polynomial fit equation: SPL = ' equationString]); + +catch ME + % Handle any errors that occur during the process + disp(['An error occurred: ' ME.message]); + % Clean up the serial port if it was opened + if exist('arduinoSerial', 'var') && isvalid(arduinoSerial) + fclose(arduinoSerial); + delete(arduinoSerial); + clear arduinoSerial; + end +end + +% --- Function to simulate setting the speaker level (REPLACE THIS) --- +function setSpeakerLevel(value) + % Replace this with the actual commands or functions to control the speaker. + % This is just a placeholder for your specific speaker control mechanism. + disp(['(Simulating setting speaker level to: ' num2str(value), ')']); + pause(1); % Simulate speaker level change +end + +% --- Helper function to convert polynomial coefficients to a string --- +function equationString = poly2str(p, varName) + % Converts a polynomial coefficient vector (as returned by polyfit) + % to a string representation of the polynomial equation. + % + % Example: + % p = [1 2 3]; % Coefficients for 1x^2 + 2x + 3 + % varName = 'x'; + % equationString = poly2str(p, varName); % Returns '1x^2 + 2x + 3' + + n = length(p); + equationString = ''; + for i = 1:n + coeff = p(i); + if coeff ~= 0 + if ~isempty(equationString) + equationString = [equationString, ' + ']; % Use '+' sign (except for the first term) + end + if coeff == 1 && i < n %suppress 1 + % equationString = [equationString, varName]; + elseif coeff ~= 1 + equationString = [equationString, num2str(coeff)]; + end + + if i < n + equationString = [equationString, varName]; + if i < n - 1 + equationString = [equationString, '^', num2str(n - i)]; + end + elseif coeff ~= 0 + equationString = [equationString, num2str(coeff)]; + end + end + end + % Replace "+ -" with "-" + equationString = strrep(equationString, '+ -', '-'); +end From 59ccb4098a045bd38986263cbaff531d73de3037 Mon Sep 17 00:00:00 2001 From: Arpit Date: Sun, 27 Apr 2025 17:39:45 +0100 Subject: [PATCH 070/164] save setting change --- ExperPort/Plugins/@saveload/SavingSection.m | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ExperPort/Plugins/@saveload/SavingSection.m b/ExperPort/Plugins/@saveload/SavingSection.m index 840ebda8..f8a28b7d 100644 --- a/ExperPort/Plugins/@saveload/SavingSection.m +++ b/ExperPort/Plugins/@saveload/SavingSection.m @@ -106,7 +106,9 @@ SoloParamHandle(obj, 'settings_file_load_time', 'value', settings_file_load_time_num); EditParam(obj, 'experimenter', 'experimenter', x, y); next_row(y, 1.5); + set_callback(experimenter, {mfilename, 'set'}); EditParam(obj, 'ratname', 'ratname', x, y); next_row(y, 1.5); + set_callback(ratname, {mfilename, 'set'}); PushbuttonParam(obj, 'loadsets', x, y, 'label', 'Load Settings'); set_callback(loadsets, {mfilename, 'loadsets'}); From 5963fc7ad02050795b51102db5f8d00861e4ceda Mon Sep 17 00:00:00 2001 From: viktorpm Date: Sun, 27 Apr 2025 18:38:52 +0100 Subject: [PATCH 071/164] added ratname and exp name while loading protocol --- ExperPort/Modules/@runrats/runrats.asv | 2031 +++++++++++++++++ ExperPort/Modules/@runrats/runrats.m | 5 + ExperPort/Plugins/@saveload/SavingSection.asv | 369 +++ ExperPort/Plugins/@saveload/SavingSection.m | 17 +- ...itCentrePokeTraining_Arpit_AR1_250427a.mat | Bin 0 -> 15252 bytes 5 files changed, 2413 insertions(+), 9 deletions(-) create mode 100644 ExperPort/Modules/@runrats/runrats.asv create mode 100644 ExperPort/Plugins/@saveload/SavingSection.asv create mode 100644 ExperPort/settings_@ArpitCentrePokeTraining_Arpit_AR1_250427a.mat diff --git a/ExperPort/Modules/@runrats/runrats.asv b/ExperPort/Modules/@runrats/runrats.asv new file mode 100644 index 00000000..eb91cb95 --- /dev/null +++ b/ExperPort/Modules/@runrats/runrats.asv @@ -0,0 +1,2031 @@ +% RunRats(varargin) +% This is the GUI for technicians in the lab to use for day to day running +% of rats. The logic of the code follows that of dispatcher. + +% The original RunRats was written bu Jeff Erlich with modifications by +% Sebastien Awwad, 2007-2008 and Sundeep Tuteja, 2009 +% +% This is a complete rewrite written by Chuck Kopec 2011 +% Maintenance features added by Chuck Kopec 2012 +% 9 shift expansion by Chuck Kopec 2013 +% being changed for Akrami Lab Sharbat 2019 + + +function [obj, varargout]=runrats(varargin) + +%Argument handling +obj = class(struct, mfilename, sqlsummary); +varargout = {}; +if nargin==0 || nargin==1 && ischar(varargin{1}) && strcmp(varargin{1}, 'empty'), + return; +end; + +% This line is required for the use of SoloParams +GetSoloFunctionArgs; + +if nargin>=2 && isa(varargin{1}, class(obj)), action = varargin{2}; varargin = varargin(3:end); +else action = varargin{1}; varargin = varargin(2:end); +end; +if ~ischar(action) + error('Runrats expects to be called with a string as the first argument specifying the action to perform. E.g.: runrats(''init'');'); +end + +%Make the action case insensitive +action = lower(action); + +switch action + + case 'physinit' + %% physinit + SoloParamHandle(obj,'phys','value',1); + runrats(obj,'init'); + + case 'init' + %% init + display('test'); + %If we are starting from a forced reboot from runrats itself we + %need to make sure the do_on_reboot.bat file is set back to nothing + try %#ok + p = pwd; + cd('\ratter\Rigscripts') + + !del do_on_reboot.bat + !copy nothing.bat do_on_reboot.bat /Y + cd(p); + end + + %If a dispatcher is already open, let's close it so we don't ever have + %more than 1 + if exist('myfig', 'var') + if isa(myfig, 'SoloParamHandle') && ishandle(value(myfig)), delete(value(myfig)); end; + end + + %Let's see if the rig is broken + isbroken = bdata(['select isbroken from rig_maintenance where rigid="',num2str(bSettings('get','RIGS','Rig_ID')),'" and isbroken=1']); + if isempty(isbroken); isbroken = 0; + else isbroken = 1; + end + SoloParamHandle(obj,'IsBroken','value',isbroken); % + + %Let's see how long it's been since the last calibration + SoloParamHandle(obj,'CalibAge','value',Inf); %Age of the calibration in days + SoloParamHandle(obj,'CalibExp','value',100); + % runrats(obj,'check_calibration'); %we use bpod now, ignore. + + %If the calibration is too old, let's open WaterCalibration for the + %tech, if we are getting near needing a new calibration let's warn them + %If the rig is broken ignore calibration + + techmessage = ''; + % if dispatcher('is_running'); dispatcher('close'); end + % if bSettings('get','WATER','skip_calibration_now') ~= 1 && isbroken == 0 + % if value(CalibAge) >= value(CalibExp) %#ok + % dispatcher('init'); set(double(gcf),'visible','off'); + % dispatcher('set_protocol','WaterCalibration'); + % return; + % elseif value(CalibAge) >= value(CalibExp)-1 + % techmessage = 'WARNING Calibration will expire in 1 day!'; + % elseif value(CalibAge) >= value(CalibExp)-2 + % techmessage = 'Calibration will expire in 2 days,'; + % elseif value(CalibAge) >= value(CalibExp)-3 + % techmessage = 'Calibration will expire in 3 days,'; + % end + % end + % + % Start and hide the dispatcher. + + dispobj=dispatcher('init'); + h=get_sphandle('owner','dispatcher','name','myfig'); + set(value(h{1}), 'Visible','Off'); + SoloParamHandle(obj, 'dispobj','value',dispobj); + + %SoloParamHandle storing the settings file + SoloParamHandle(obj, 'settings_file_sph', 'value', ''); + SoloParamHandle(obj, 'settings_file_load_time', 'value', 0 ); + + + % The main RunRats window should appear in the center of the screen + sr = get(0,'MonitorPositions'); + wh = [650 250]; + pos = [(sr(3)-wh(1))/2,(sr(4)-wh(2))/2,wh(1),wh(2)]; + + fig = figure('Position',pos,'MenuBar','none','ToolBar','none', ... + 'NumberTitle','off','Name','RunRats V2.2 ','Resize','off',... + 'closerequestfcn', [mfilename '(''close'')']); + SoloParamHandle(obj,'myfig', 'value',fig); + + try + set(myfig, 'WindowStyle', 'modal'); + pause(0.1); + + catch %#ok + + disp('WARNING: Failed to keep runrats on top'); + end + + + %Create the non-gui variables we will need + SoloParamHandle(obj,'RigID', 'value',bSettings('get','RIGS','Rig_ID')); %The rig ID + SoloParamHandle(obj,'RatSch', 'value',cell(10,1)); %The rats that run in this rig in session order + SoloParamHandle(obj,'CurrSession', 'value',1); %The session that we should prep to load + SoloParamHandle(obj,'CurrProtocol', 'value',1); %The protocol that the rat runs under + SoloParamHandle(obj,'WasFlushed', 'value',0); %Was the rig flused today + SoloParamHandle(obj,'InLiveLoop', 'value',0); %Let's us know if we should break out of the loop + SoloParamHandle(obj,'CurrRat', 'value',''); %The name of the current rat + SoloParamHandle(obj,'PanelBack', 'value',[]); %handles to all the uicontrols that share the background color + SoloParamHandle(obj,'RigIsBroken', 'value',0); %1 if the rig is broken, 0 if it's not + SoloParamHandle(obj,'DoingMaintenance','value',0); %1 if we are entering a maintenance note + SoloParamHandle(obj,'do_full_restart', 'value',1); %1 if we want to restart matlab between each session + SoloParamHandle(obj,'SafetyMode', 'value',''); %empty for no safety, B for before, A for after + SoloParamHandle(obj,'OptoPlugColor', 'value',[]); %figure handle for opto plug color panel + + if ~exist('phys','var'); SoloParamHandle(obj,'phys','value',0); end + + + SoloParamHandle(obj, 'curProtocol', 'value', ''); %Current Protocol + SoloParamHandle(obj,'schedDay','value',[]); + + % For Live Webcam Feed + SoloParamHandle(obj,'Camera_Fig_window','value',[]); + SoloParamHandle(obj,'Camera_Obj','value',[]); + SoloParamHandle(obj,'Camera_Image','value',[]); + + %Let's make the menus + try + all_experimenters = bdata('select distinct experimenter from rats where extant=1 order by experimenter'); + catch %#ok + disp('ERROR: Unable to connect to MySQL Server'); + all_experimenters = ''; + end + all_experimenters = [{''};all_experimenters]; + + MenuParam(obj,'ExpMenu',all_experimenters,1,0,0,'position',[10 204 200 35],'labelfraction',0.01); + MenuParam(obj,'RatMenu',{''}, 1,0,0,'position',[10 162 200 35],'labelfraction',0.01); + MenuParam(obj,'SchList',{'Not Active'}, 1,0,0,'position',[10 120 200 35],'labelfraction',0.01); + + SubheaderParam(obj,'ExpLbl','Experimenter',0,0,'position',[215 204 170 35]); + SubheaderParam(obj,'RatLbl','Rat', 0,0,'position',[215 162 170 35]); + SubheaderParam(obj,'SchLbl','Schedule', 0,0,'position',[215 120 170 35]); + + set(get_ghandle(ExpMenu),'FontSize',20,'BackgroundColor',[1 1 1]); %#ok + set(get_ghandle(RatMenu),'FontSize',20,'BackgroundColor',[1 1 1]); %#ok + set(get_ghandle(SchList),'FontSize',16,'BackgroundColor',[1 1 1],'FontName','Courier','FontWeight','bold'); %#ok + + set(get_ghandle(ExpLbl),'FontSize',20,'BackgroundColor',[1,0.8,0.6],'HorizontalAlignment','left'); + set(get_ghandle(RatLbl),'FontSize',20,'BackgroundColor',[1,0.8,0.6],'HorizontalAlignment','left'); + set(get_ghandle(SchLbl),'FontSize',20,'BackgroundColor',[1,0.8,0.6],'HorizontalAlignment','left'); + + set_callback(ExpMenu,{mfilename,'expmenu_callback'}); + set_callback(RatMenu,{mfilename,'ratmenu_callback'}); + set_callback(SchList,{mfilename,'schlist_callback'}); + + + %Let's make the buttons + PushbuttonParam(obj,'FlushValves',0,0,'position',[ 10,50,140, 25],'BackgroundColor',[0.6 1 1 ],'label','Flush Valves'); + PushbuttonParam(obj,'TestImplant',0,0,'position',[155,50,140, 25],'BackgroundColor',[1 0.6 1 ],'label','Test Implant'); + PushbuttonParam(obj,'UpdateMode' ,0,0,'position',[300,50,170, 25],'BackgroundColor',[0.6 1 0.6],'label','Live Update On'); + PushbuttonParam(obj,'Maintenance',0,0,'position',[475,50,165, 25],'BackgroundColor',[1 0.3 0.1],'label','Maintenance'); + PushbuttonParam(obj,'Multi', 0,0,'position',[400,85,240,155],'BackgroundColor',[1 1 0.4],'label','Load Protocol'); + + set(get_ghandle(FlushValves),'Fontsize',16); + set(get_ghandle(TestImplant),'Fontsize',16); + set(get_ghandle(UpdateMode), 'Fontsize',16); + set(get_ghandle(Maintenance),'Fontsize',16); + set(get_ghandle(Multi), 'Fontsize',24); + + set_callback(FlushValves,{mfilename,'flush_valves'}); + set_callback(TestImplant,{mfilename,'test_implant'}); + set_callback(UpdateMode, {mfilename,'updatemode_button'}); + set_callback(Maintenance,{mfilename,'rig_maintenance'}); + set_callback(Multi, {mfilename,'multi_button'}); + + %Hide the Test Implant button if this rig isn't set up to do this + alldio=bSettings('get','DIOLINES','ALL'); + if isempty(alldio) || (sum(strcmp(alldio(:,1),'stim1')) == 0 && sum(strcmp(alldio(:,1),'LASER')) == 0) &&... + (sum(strcmp(alldio(:,1),'BLUE')) == 0 && sum(strcmp(alldio(:,1),'GREEN')) == 0) &&... + (sum(strcmp(alldio(:,1),'YELLOW')) == 0 && sum(strcmp(alldio(:,1),'RED')) == 0) + set(get_ghandle(TestImplant),'visible','off'); + end + + + %Let's make the display areas + DispParam(obj,'Instructions','', 0,0,'position',[10,80,380,40],'label','','labelfraction',0.005); + DispParam(obj,'StatusBar', techmessage,0,0,'position',[ 1, 1,650,50],'label','','labelfraction',0.003); + + %This is intended to be a copy of the instructions field which will + %require the tech to click on if the experimenter wants it + PushbuttonParam(obj,'Safety', 0,0,'position',[10,80,380,40],'label',''); + set_callback(Safety,{mfilename,'safety_button'}); + + set(get_ghandle(Instructions),'Fontsize',14,'BackgroundColor',[1,0.8,0.6],'HorizontalAlignment','left','FontWeight','bold'); %#ok + set(get_ghandle(StatusBar), 'Fontsize',14,'BackgroundColor',[0.9,0.9,1],'HorizontalAlignment','center'); %#ok + set(get_ghandle(Safety), 'Fontsize',14,'BackgroundColor',[1,0.8,0.6],'HorizontalAlignment','left','FontWeight','bold','visible','off'); + + + c = get(double(gcf),'children'); + set(c(end),'backgroundcolor',[1,0.8,0.6]); + PanelBack.value = [c(end),get_ghandle(ExpLbl),get_ghandle(RatLbl),get_ghandle(SchLbl),get_ghandle(Instructions)]; + + + %Time to make the rig maintenance screen + confirmnote = {'If you repaired the rig, enter','your name and click FIXED.'}; + schedulenote = 'Run these rats in different rigs'; + try %#ok + labmembers = bdata('select experimenter from contacts where is_alumni=0'); + labmembers = unique(strtrim(labmembers)); + labmembers(2:end+1) = labmembers; + end + labmembers{1} = 'Select Name'; + + DispParam(obj,'MaintenanceTitle','Rig is Broken',0,0,'position',[220,190,430, 60],'label','','labelfraction',0.01); + DispParam(obj,'ScheduleNote',schedulenote, 0,0,'position',[ 1,220,220, 30],'label','','labelfraction',0.01); + DispParam(obj,'Schedule','', 0,0,'position',[ 1, 40,220,180],'label','','labelfraction',0.01); + DispParam(obj,'RigTechNote','', 0,0,'position',[220, 40,430,150],'label','','labelfraction',0.01); + DispParam(obj,'ConfirmNote',confirmnote, 0,0,'position',[ 1, 1,220, 40],'label','','labelfraction',0.01); + MenuParam(obj,'LabMembersMenu',labmembers, 1,0,0,'position',[220, 1,283, 40],'labelfraction',0.01); + PushbuttonParam(obj,'Fixed', 0,0,'position',[500, 1,150, 40],'label','Fixed'); + + set(get_ghandle(MaintenanceTitle),'Fontsize',32,'visible','off','BackgroundColor',[1,0,0], 'HorizontalAlignment','center'); + set(get_ghandle(ScheduleNote), 'Fontsize',12,'visible','off','BackgroundColor',[1,0.7,0.7], 'HorizontalAlignment','center'); + set(get_ghandle(Schedule), 'Fontsize',12,'visible','off','BackgroundColor',[1,0.7,0.7], 'HorizontalAlignment','left'); + set(get_ghandle(RigTechNote), 'Fontsize',12,'visible','off','BackgroundColor',[1,1,1], 'HorizontalAlignment','center'); + set(get_ghandle(ConfirmNote), 'Fontsize',12,'visible','off','BackgroundColor',[0.9,0.9,0.9],'HorizontalAlignment','center'); + set(get_ghandle(LabMembersMenu), 'Fontsize',24,'visible','off','BackgroundColor',[1,1,1]); %#ok + set(get_ghandle(Fixed), 'Fontsize',24,'visible','off','BackgroundColor',[0,1,0],'enable','off'); + + set_callback(LabMembersMenu,{mfilename,'select_labmember'}); + set_callback(Fixed, {mfilename,'flag_rig_fixed'}); + + %Let's make a fix comments screen + EditParam(obj, 'RepairNote','',0,0,'position',[ 1, 40,650,150],'label','','HorizontalAlignment','left','labelfraction',0.003); + PushbuttonParam(obj,'Submit', 0,0,'position',[ 1, 1,650, 40],'label','Submit'); + + set(get_ghandle(RepairNote),'Fontsize',16,'visible','off','BackgroundColor',[1,1,1],'max',2); + set(get_ghandle(Submit), 'Fontsize',24,'visible','off','BackgroundColor',[0,1,0],'enable','off'); + + set_callback(RepairNote,{mfilename,'enter_repairnote'}); + set_callback(Submit, {mfilename,'submit_rig_fixed'}); + + + %Let's clean up the unused labels + temp = get_glhandle(Instructions); set(temp(2),'visible','off'); + temp = get_glhandle(StatusBar); set(temp(2),'visible','off'); + temp = get_glhandle(ConfirmNote); set(temp(2),'visible','off'); + temp = get_glhandle(ScheduleNote); set(temp(2),'visible','off'); + temp = get_glhandle(Schedule); set(temp(2),'visible','off'); + temp = get_glhandle(LabMembersMenu); set(temp(2),'visible','off'); + + %Last thing we need to make is a timer, boooooo, I don't like them + scr = timer; + set(scr,'Period', 0.2,'ExecutionMode','FixedRate','TasksToExecute',Inf,... + 'BusyMode','drop','TimerFcn',[mfilename,'(''End_Continued'')']); + SoloParamHandle(obj, 'stopping_complete_timer', 'value', scr); + + % Update the rig info table + update_riginfo(); + runrats(obj,'updatelog','startup'); + + %We are done building the GUI, let's update the session menu, figure + %out what session we are in and load up that rat + + runrats(obj,'update_schedule'); + runrats(obj,'estimate_current_session'); + + %Now that we know the schedule for this rig and what session we should + %prepare to load, let's do it. + runrats(obj,'update_exprat'); + runrats(obj,'check_rig_flushed'); + runrats(obj,'live_loop'); + + + case 'send_empty_state_machine' + %% send_empty_state_machine + display('reached sesm'); + + state_machine_server = bSettings('get','RIGS','state_machine_server'); + + server_slot = bSettings('get','RIGS','server_slot'); + if isnan(server_slot); server_slot = 0; end + + card_slot = bSettings('get', 'RIGS', 'card_slot'); + if isnan(card_slot); card_slot = 0; end + + sm = BpodSM(state_machine_server, 3333,server_slot); + sm = Initialize(sm); + + [inL,outL] = MachinesSection(dispatcher,'determine_io_maps'); + + sma = StateMachineAssembler('full_trial_structure'); + sma = add_state(sma,'name','vapid_state_in_vapid_matrix'); + + send(sma,sm,'run_trial_asap',0,'input_lines',inL,'dout_lines',outL,'sound_card_slot', int2str(card_slot)); + + + case 'check_calibration' + %% check_calibration + + lastcalib = check_calibration; + if ~isnan(lastcalib); calibage = now - datenum(lastcalib,'yyyy-mm-dd HH:MM:SS'); + else calibage = Inf; + end + + CalibAge.value = calibage; + days = floor(value(CalibExp) - calibage); + hours = floor((value(CalibExp) - days - calibage) * 24); + + if exist('StatusBar','var') && calibage > value(CalibExp) && bSettings('get','WATER','skip_calibration_now') ~= 1 + %If calibration has expired close runrats and open calibration protocol + dispatcher('set_protocol','WaterCalibration'); + runrats(obj,'close_gui_only'); + return; + + elseif exist('StatusBar','var') && (calibage > value(CalibExp)- 6) && bSettings('get','WATER','skip_calibration_now') ~= 1 + %Calibration is not expired but old, so let's warn the tech it + %will expire soon + + techmessage = value(StatusBar); %#ok + tempF = strfind(techmessage,'flush'); + + %Only update message if it's not already telling the tech to + %flush the valves + if isempty(tempF) + tempC = strfind(techmessage,'Calibration will expire in'); + + if ~isempty(tempC); techmessage(min(tempC):end) = ''; end + + lastchar = find(techmessage ~= ' ',1,'last'); + if ~isempty(lastchar) && lastchar < numel(techmessage) + techmessage(lastchar + 1:end) = ''; + end + + techmessage = [techmessage,' Calibration will expire in ',... + num2str(days),' days ',num2str(hours),' hours']; + + StatusBar.value = techmessage; + end + end + + + + case 'live_loop' + %% live_loop + %Here we loop, updated the schedule and menus accordingly. + + %We do this so the DIO lines work + display('reached live loop'); + runrats(obj,'send_empty_state_machine'); + + InLiveLoop.value = 1; + strt = now; + donefirst = 0; + while value(InLiveLoop) == 1 + %We only want to update every 30 seconds so we don't clog the + %network but we want the loop to go fast to be responsive. + if ((now - strt) * 24 * 60) > 0.5 || donefirst == 0 + disp(['RunRats Live Update at ',datestr(now,'HH:MM:SS')]); + runrats(obj,'update_schedule'); + runrats(obj,'estimate_current_session'); + runrats(obj,'update_exprat'); + runrats(obj,'check_rig_flushed'); + runrats(obj,'check_rig_broken'); + + + if strcmp(get(get_ghandle(MaintenanceTitle),'string'),'Please wait while RunRats updates...') + set(get_ghandle(MaintenanceTitle),'string','Rig is Broken','BackgroundColor',[1 0 0]); + % elseif value(IsBroken) == 0 + %Only check for calibration if the rig is not broken + % runrats(obj,'check_calibration'); + end + + strt = now; + donefirst = 1; + end + pause(rand(1)); + end + if runrats('is_running') + %disp(get(get_ghandle(Multi),'string')); + if isempty(strfind(get(get_ghandle(Multi),'string'),'Run')) &&... + isempty(strfind(get(get_ghandle(Multi),'string'),'Unloading Test')) + runrats(obj,'updatemode_button',0); + end + end + + + case 'updatemode_button' + %% updatemode_button + %The user has clicked the updatemode button, this will either take + %us in or out of the live loop, we also need to change colors + + if nargin > 2; forceto = varargin{1}; + elseif strcmp(get(get_ghandle(UpdateMode),'string'),'Live Update On'); forceto = 0; + else forceto = 1; + end + + if forceto == 0 + set(get_ghandle(UpdateMode),'String','Update Paused','BackgroundColor',[1 0 0],'ForegroundColor',[0 0 0]); + h = value(PanelBack); %#ok + for i = 1:length(h); + set(h(i),'BackgroundColor',[1,0.2,0.2]); + end + set(get_ghandle(Safety),'BackgroundColor',[1,0.2,0.2]); + InLiveLoop.value = 0; + else + h = value(PanelBack); %#ok + for i = 1:length(h); + set(h(i),'BackgroundColor',[1,0.8,0.6]); + end + set(get_ghandle(Safety),'BackgroundColor',[1,0.8,0.6]); + set(get_ghandle(UpdateMode),'String','Live Update On','BackgroundColor',[0.6 1 0.6],'ForegroundColor',[0 0 0]); + runrats(obj,'live_loop'); + end + + + case 'expmenu_callback' + %% expmenu_callback + %The user has selected a different experimenter from the menu + + InLiveLoop.value = 0; + runrats(obj,'update_ratmenu'); + + + case 'ratmenu_callback' + %% ratmenu_callback + %The user has selected a different rat from the menu + + InLiveLoop.value = 0; + runrats(obj,'update_rat',1); + + + case 'schlist_callback' + %% schlist_callback + %The user has selected a different session from the schedule + + InLiveLoop.value = 0; + runrats(obj,'update_exprat'); + + + case 'check_rig_flushed' + %% check_rig_flushed + %Check if the rig has been flushed today + % + % if ~isnan(value(RigID)) + % wf = bdata(['select wasflushed from rigflush where rig=',... + % num2str(value(RigID)),' and dateval="',datestr(now,'yyyy-mm-dd'),'"']); + % if isempty(wf) || wf == 0 + % %The rig has not been flushed + % StatusBar.value = 'Please flush the rig before loading the first rat today.'; + % set(get_ghandle(Multi),'enable','off'); + % wf = 0; + % end + % else + % wf = nan; + % end + % WasFlushed.value = wf; + + %As of 2015-09-02 I am turning this feature off. Possibly leading to + %rats losing motivation in session 1 if they can drink it. -Chuck + + WasFlushed.value = 1; + + + case 'check_rig_broken' + %% check_rig_broken + %Here we determine if the rig has been flagged as broken. If it is + %we bring up the maintenance screen, if not we keep it as normal, + %however, if we are entering a maintenance note we should skip this + + if value(DoingMaintenance) == 0 && ~isnan(value(RigID)) %#ok + [note,isbroken,tech,notedate] = bdata('select note, isbroken, broke_person, broke_date from rig_maintenance where rigid="{S}"',value(RigID)); + + fullnote = ''; + if ~isempty(note) + recent_break = find(isbroken == 1,1,'last'); + if ~isempty(recent_break) + note = note{recent_break}; + isbroken = isbroken(recent_break); + tech = tech{recent_break}; + notedate = notedate{recent_break}; + try %#ok + fullnote = {['Note entered by ',tech,' on ',notedate],char(note)'}; + end + else + isbroken = 0; + end + + set(get_ghandle(Fixed),'enable','off'); + + if isbroken == 1; s = {'on','off'}; + else s = {'off','on'}; + end + else s = {'off','on'}; + end + + set(get_ghandle(MaintenanceTitle),'visible',s{1}); + set(get_ghandle(RigTechNote), 'visible',s{1},'string',fullnote); + set(get_ghandle(ConfirmNote), 'visible',s{1}); + set(get_ghandle(LabMembersMenu), 'visible',s{1}); %#ok + set(get_ghandle(Fixed), 'visible',s{1}); + set(get_ghandle(ScheduleNote), 'visible',s{1}); + set(get_ghandle(Schedule), 'visible',s{1}); + + set(get_ghandle(Instructions), 'visible',s{2}); %#ok + set(get_ghandle(StatusBar), 'visible',s{2}); %#ok + set(get_ghandle(FlushValves), 'visible',s{2}); + set(get_ghandle(TestImplant), 'visible',s{2}); + set(get_ghandle(UpdateMode), 'visible',s{2}); + set(get_ghandle(Maintenance), 'visible',s{2}); + set(get_ghandle(Multi), 'visible',s{2}); + set(get_ghandle(ExpMenu), 'visible',s{2}); %#ok + set(get_ghandle(RatMenu), 'visible',s{2}); %#ok + set(get_ghandle(SchList), 'visible',s{2}); %#ok + set(get_ghandle(ExpLbl), 'visible',s{2}); + set(get_ghandle(RatLbl), 'visible',s{2}); + set(get_ghandle(SchLbl), 'visible',s{2}); + + + %Hide the Test Implant button if this rig isn't set up to do this + alldio=bSettings('get','DIOLINES','ALL'); + if isempty(alldio) || (sum(strcmp(alldio(:,1),'stim1')) == 0 && sum(strcmp(alldio(:,1),'LASER')) == 0) + set(get_ghandle(TestImplant),'visible','off'); + end + + IsBroken.value = isbroken; + end + + case 'select_labmember' + %% select_labmember + %If the user has selected their name we enable the fixed button + + if strcmp(value(LabMembersMenu),'Select Name') == 0 %#ok + set(get_ghandle(Fixed),'enable','on'); + else + set(get_ghandle(Fixed),'enable','off'); + end + + if ~strcmp(value(RepairNote),'') && ~strcmp(value(LabMembersMenu),'Select Name') + set(get_ghandle(Submit),'enable','on'); + end + + + case 'enter_repairnote' + %% enter_repairnote + %If the user has entered a note we can enable the Submit button + + if strcmp(value(RepairNote),'') || strcmp(value(LabMembersMenu),'Select Name') %#ok + set(get_ghandle(Submit),'enable','off'); + else + set(get_ghandle(Submit),'enable','on'); + end + + + + case 'flag_rig_fixed' + %% flag_rig_fixed + %The user has confirmed that the rig is fixed. Now we make them + %enter comments about how they fixed it before going back to + %runrats normal mode + + set(get_ghandle(MaintenanceTitle),'string','Enter how you fixed the rig, then click SUBMIT',... + 'BackgroundColor',[0.7,1,0.7],'FontSize',20); + set(get_ghandle(RepairNote),'visible','on','string',''); + set(get_ghandle(Submit), 'visible','on','enable','off'); + + + + case 'submit_rig_fixed' + %% submit_rig_fixed + %The user has entered comments. Update the MySQL table and return + %runrats to normal operation + + [id isbroken] = bdata(['select maintenance_id, isbroken from rig_maintenance where rigid=',num2str(value(RigID))]); + if ~isempty(id) && sum(isbroken) ~= 0 + %The rig was broken, let's add info to that MySQL entry + temp = find(isbroken == 1,1,'last'); + id = id(temp); + bdata('call mark_rigfixed("{S}","{S}","{S}","{S}")',id,value(LabMembersMenu),... + datestr(now,'yyyy-mm-dd HH:MM'),value(RepairNote)); %#ok + InLiveLoop.value = 1; + else + %We just did maintenance, make a new MySQL entry + bdata(['insert into rig_maintenance set fix_person="',value(LabMembersMenu),... + '", fix_note="',value(RepairNote),'", fix_date="',datestr(now,'yyyy-mm-dd HH:MM'),... + '", rigid=',num2str(value(RigID))]); %#ok + end + + set(get_ghandle(LabMembersMenu),'position',[220, 1,283, 40]); + set(get_ghandle(Submit), 'position',[ 1, 1,650, 40]); + + set(get_ghandle(RepairNote), 'visible','off','string',''); + set(get_ghandle(Submit), 'visible','off'); + set(get_ghandle(LabMembersMenu),'visible','off'); + + set(get_ghandle(MaintenanceTitle),'string','Please wait while RunRats updates...',... + 'BackgroundColor',[0.7 0.7 0.7],'FontSize',20); + + DoingMaintenance.value = 0; + LabMembersMenu.value = 1; + + runrats(obj,'check_rig_broken'); + + + + case 'update_rat' + %% udpate_rat + %Here we take the selected rat and checkout their settings if + %necessary, update their settings, and update the tech instructions + + if isempty(value(RatMenu)) || isempty(value(ExpMenu)); %#ok + return; + end + + runrats(obj,'disable_all'); + set(get_ghandle(Multi),'string',['Updating ',value(RatMenu)],... + 'Backgroundcolor',[0.8 0.8 0.8],'fontsize',24); + pause(0.1); + + dirCurrent = cd; + [dirData errtmp] = bSettings('get','GENERAL','Main_Data_Directory'); + if errtmp || ~ischar(dirData) || isempty(dirData); + return; + end + + if ~exist(dirData,'dir') + %We need to check out SoloData + end + cd(dirData); + + dirSettings = [dirData ,filesep,'Settings']; + dirExp = [dirSettings,filesep,value(ExpMenu)]; + dirRat = [dirExp ,filesep,value(RatMenu)]; + + if ~exist(dirSettings,'dir') + mkdir('Settings'); + system('svn add Settings'); + end + cd(dirSettings); + + if ~exist(dirExp,'dir') + mkdir(value(ExpMenu)); + system(['svn add ' value(ExpMenu)]); + end + cd(dirExp); + + if ~exist(dirRat,'dir'); + mkdir(value(RatMenu)); + system(['svn add ' value(RatMenu)]); + end; + cd(dirRat); + + update_folder(pwd,'svn'); + + cd(dirCurrent); + runrats(obj,'enable_all'); + set(get_ghandle(Multi),'string','Load Protocol','BackgroundColor',[1,1,0.4],'FontSize',24); + + if strcmp(value(CurrRat),value(RatMenu)) %#ok + if nargin < 3 || varargin{1} == 1 + InLiveLoop.value = 1; + end + else + CurrRat.value = value(RatMenu); + + %Let's see if this is a new rat, if so, warn the tech + ratM = bdata(['select mass from mass_log where animal_id="',value(RatMenu),'"']); + if length(ratM) < 14 + StatusBar.value = 'WARNING: New Rat!'; + set(get_ghandle(StatusBar),'fontweight','bold'); + else + StatusBar.value = ['Ready to load ',value(RatMenu)]; + set(get_ghandle(StatusBar),'fontweight','normal'); + end + end + + runrats(obj,'update_tech_instructions'); + runrats(obj,'check_rig_flushed'); + + + case 'update_exprat' + %% update_exprat + %Here we update the Experimenter and Rat Menus to reflect the + %selected entry in the Schedule List + + CurrSession.value = get(get_ghandle(SchList),'value'); %#ok + currrat = value(RatMenu); %#ok + + ratsch = value(RatSch); %#ok + if ~isnan(value(CurrSession)) + if value(CurrSession) <= 9 + SchList.value = value(CurrSession); + StatusBar.value = ['Ready to load session ',num2str(value(CurrSession))]; + else + SchList.value = 10; + StatusBar.value = 'All training session are done for today.'; + end + activerat = ratsch{value(CurrSession)}; + else + activerat = ''; + end + + %Find the rat's experimenter, and set the experimenter and rat menus accordingly + if ~isempty(activerat) + exp = bdata(['select experimenter from rats where ratname="',activerat,'"']); + ExpMenu.value = exp{1}; + else + ExpMenu.value = ''; + end + + runrats(obj,'update_ratmenu',activerat); + runrats(obj,'update_tech_instructions'); + + %If the rat has changed, let's update it. + if ~strcmp(value(RatMenu),currrat) + runrats(obj,'update_rat',value(InLiveLoop)); %#ok + else + %Stay in the loop if we haven't changed anything + InLiveLoop.value = 1; + end + + %runrats(obj,'updatelog','update_exprat'); + +%% SPECIAL CASE ADDED BY ARPIT FOR CLICK AND SELECT +% doesn't effect the running of the other cases/functions + case 'update exp_rat_userclick' + + ExpMenu.value = varargin{1}; + runrats(obj,'update_ratmenu',varargin{2}); + %If the rat has changed, let's update it. + if ~strcmp(value(RatMenu),varargin{2}) + runrats(obj,'update_rat',value(InLiveLoop)); %#ok + else + %Stay in the loop if we haven't changed anything + InLiveLoop.value = 1; + end + + runrats(obj,'begin_load_protocol'); + + % Added to send the details about the experimenter and rat + case 'exp_rat_names' + x = value(ExpMenu.value) +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + case 'update_tech_instructions' + %% update_tech_instructions + %Posts the tech instructions for the active rat on the screen + + if ~isempty(value(OptoPlugColor)) %#ok + try delete(value(OptoPlugColor)); end %#ok + end + + if ~strcmp(value(RatMenu),'') %#ok + [ti,sess] = bdata(['select instructions, timeslot from scheduler where ratname="',value(RatMenu),... + '" and date="',datestr(now,'yyyy-mm-dd'),'"']); + if iscell(ti) && numel(ti) > 1 % More than one session is scheduled for this rat. Grab the appropriate tech instruction + runrats(obj,'estimate_current_session'); + if isempty(value(CurrSession)) + ti = ti{1}; + elseif sum(sess==value(CurrSession)) == 1 + ti = ti{sess==value(CurrSession)}; + else + ti = ''; + end + elseif iscell(ti) && ~isempty(ti); ti = ti{1}; + else ti = ''; + end + try + if sum(ti=='#') >= 2 && rem(sum(ti=='#'),2)==0 + %This may be a day specific message. Let's try to parse it + + if strcmp(datestr(now,'ddd'),'Mon'); dayltr = 'm'; + elseif strcmp(datestr(now,'ddd'),'Tue'); dayltr = 't'; + elseif strcmp(datestr(now,'ddd'),'Wed'); dayltr = 'w'; + elseif strcmp(datestr(now,'ddd'),'Thu'); dayltr = 'r'; + elseif strcmp(datestr(now,'ddd'),'Fri'); dayltr = 'f'; + elseif strcmp(datestr(now,'ddd'),'Sat'); dayltr = 's'; + else strcmp(datestr(now,'ddd'),'Sun'); dayltr = 'u'; + end + + p = find(ti == '#'); + + TI = ti(1:p(1)-1); + + for i = 1:2:numel(p)-1 + d = ti(p(i)+1:p(i+1)-1); + if ~isempty(strfind(lower(d),dayltr)) + %This portion of the instructions should be + %displayed today + msg = ''; + if i+1==numel(p) + if numel(ti)>p(end); msg = ti(p(end)+1:end); end + else + msg = ti(p(i+1)+1:p(i+2)-1); + end + + TI = [TI,' ',msg]; %#ok + end + end + + fstltr = find(TI ~= ' ',1,'first'); + if fstltr > 1; TI(1:fstltr-1) = ''; end + ti = TI; + + end + catch %#ok + disp('Error while parsing tech instructions. Displaying everything.') + end + + %Now let's check the instructions for the safety code + d = find(ti == '$'); + s = ''; + if numel(d) > 1 + for i = 1:2:numel(d) - 1 + temp = ti(d(i)+1:d(i+1)-1); + s(end+1:end+numel(temp)) = temp; + end + end + if ~isempty(strfind(s,'A')) && ~isempty(strfind(s,'B')) > 0; SafetyMode.value = 'AB'; + elseif ~isempty(strfind(s,'A')) && isempty(strfind(s,'B')) == 0; SafetyMode.value = 'A'; + elseif isempty(strfind(s,'A')) && ~isempty(strfind(s,'B')) > 0; SafetyMode.value = 'B'; + else SafetyMode.value = ''; + end + + bad = []; + if numel(d) > 1 + for i = 1:2:numel(d) - 1 + bad(end+1:end+numel(d(i):d(i+1))) = d(i):d(i+1); + end + end + ti(bad) = []; + + Instructions.value = ti; + try %#ok + OptoPlugColor.value = parse_optonote(ti); + end + else + Instructions.value = ''; + SafetyMode.value = ''; + end + + + + case 'disable_all' + %% disable_all + %Disable all the buttons. + + set(get_ghandle(ExpMenu), 'enable','off'); %#ok + set(get_ghandle(RatMenu), 'enable','off'); %#ok + set(get_ghandle(SchList), 'enable','off'); %#ok + set(get_ghandle(FlushValves),'enable','off'); + set(get_ghandle(TestImplant),'enable','off'); + set(get_ghandle(UpdateMode), 'enable','off'); + set(get_ghandle(Maintenance),'enable','off'); + set(get_ghandle(Multi), 'enable','off'); + + + + case 'enable_all' + %% enable_all + %Enable all the buttons. + + set(get_ghandle(ExpMenu), 'enable','on'); %#ok + set(get_ghandle(RatMenu), 'enable','on'); %#ok + set(get_ghandle(SchList), 'enable','on'); %#ok + set(get_ghandle(FlushValves),'enable','on'); + set(get_ghandle(TestImplant),'enable','on'); + set(get_ghandle(UpdateMode), 'enable','on'); + set(get_ghandle(Maintenance),'enable','on'); + set(get_ghandle(Multi), 'enable','on'); + + + + case 'flush_valves' + %% flush_valves + %This will open each water valve sequentially for 10 seconds + + runrats(obj,'disable_all'); + oldval=value(StatusBar); %#ok + StatusBar.value='Flushing Each Valve for 10 seconds'; + + %Let's find all the water valves + alldio=bSettings('get','DIOLINES','ALL'); + diolist=[]; + skip_center = bSettings('get','WATER','skip_center_flush'); + if isnan(skip_center) || isempty(skip_center); skip_center = 0; end + + for di=1:size(alldio,1) + if ~isempty(strfind(alldio{di,1},'water')) && ~isnan(alldio{di,2}) + if ~isempty(strfind(alldio{di,1},'center')) && skip_center == 1; + continue; + end + diolist=[diolist alldio{di,2}]; %#ok + end + end + %Dispatcher needs the log of each DIO line + diolist = log2(diolist); + + %Cycle through the list opening each for 10 seconds + for i = 1:length(diolist) + dispatcher('toggle_bypass',diolist(i)); + pause(10); + dispatcher('toggle_bypass',diolist(i)); + end + + if strcmp(oldval,'Please flush the rig before loading the first rat today.') + oldval = ''; + end + StatusBar.value=oldval; + runrats(obj,'enable_all'); + + + %Update MySQL to show this rig has been flushed today + if ~isnan(value(RigID)) + id = bdata(['select id from rigflush where rig=',... + num2str(value(RigID)),' and dateval="',datestr(now,'yyyy-mm-dd'),'"']); + + + if isempty(id) || ~isnan(id) + if isempty(id) + bdata(['insert into rigflush set rig=',num2str(value(RigID)),... + ', wasflushed=1, dateval="',datestr(now,'yyyy-mm-dd'),'"']); + end + end + end + WasFlushed.value = 1; + + + + case 'test_implant' + %% test_implant + %This will toggle all the stim and laser lines to test a rats + %implant before running + + %Check to see if the multi button is already disabled + if strcmp(get(get_ghandle(Multi),'enable'),'off') + disablemulti = 1; + else + disablemulti = 0; + end + + runrats(obj,'disable_all'); + oldval=value(StatusBar); %#ok + StatusBar.value='Testing Implant. Please Observe Rat'; + + %Let's find all the stim lines + alldio=bSettings('get','DIOLINES','ALL'); + diolist=[]; + for di=1:size(alldio,1) + if (~isempty(strfind(alldio{di,1},'LASER')) ||... + ~isempty(strfind(alldio{di,1},'GREEN')) ||... + ~isempty(strfind(alldio{di,1},'BLUE')) ||... + ~isempty(strfind(alldio{di,1},'YELLOW')) ||... + ~isempty(strfind(alldio{di,1},'RED'))) &&... + ~isnan(alldio{di,2}) + diolist=[diolist alldio{di,2}]; %#ok + end + end + %Dispatcher needs the log of each DIO line + diolist = log2(diolist); + + %Call dispatcher to toggle the stim lines in succession + pause(3); + for i = 1:length(diolist) + dispatcher('toggle_bypass',diolist(i)); + pause(0.2); + dispatcher('toggle_bypass',diolist(i)); + pause(0.5); + end + + StatusBar.value=oldval; + runrats(obj,'enable_all'); + runrats(obj,'check_rig_flushed'); + + if disablemulti == 1 + set(get_ghandle(Multi),'enable','off'); + end + + runrats(obj,'updatelog','testimplant'); + + + case 'rig_maintenance' + %% rig_maintenance + %The user wants to enter some comments about maintenance they + %performed on the rig. + + set(get_ghandle(StatusBar),'visible','off'); %#ok + + set(get_ghandle(MaintenanceTitle),'visible','on','string','Enter a description of the maintenance performed:','BackgroundColor',[1 0.3 0.1],'Fontsize',20); + set(get_ghandle(RepairNote), 'visible','on'); + set(get_ghandle(Submit), 'visible','on','position',[325,1,325,40]); + set(get_ghandle(LabMembersMenu), 'visible','on','position',[ 1,1,325,40]); %#ok + DoingMaintenance.value = 1; + + + + case 'update_ratmenu' + %% update_ratmenu + %Here we get the list of extant rats for the selected experimenter + %and populate the rat menu with it + + exp = value(ExpMenu); %#ok + currats = get(get_ghandle(RatMenu),'string'); %#ok + + if strcmp(exp,'') + ratnames = {''}; + else + ratnames = bdata(['select ratname from rats where experimenter="',exp,'" and extant=1']); + ratnames = sortrows(strtrim(ratnames)); + end + + %If we've changed things, update the menus accordingly + if length(currats) ~= length(ratnames) || ~strcmp(currats{1},ratnames{1}) ||... + (nargin > 2 && ~strcmp(varargin{1},value(RatMenu))) + set(get_ghandle(RatMenu),'string',ratnames,'value',1); + + %If we passed in a rat name, set the manu to it + if nargin > 2; RatMenu.value = varargin{1}; + else RatMenu.value = ratnames{1}; + end + + runrats(obj,'update_rat'); + else + %Stay in the loop if we haven't changed anything + InLiveLoop.value = 1; + end + + + + case 'update_schedule' + %% update_schedule + %Here we grab the current schedule for this rig + + if ~isnan(value(RigID)); + [rats slots] = bdata(['select ratname, timeslot from scheduler where date="',... + datestr(now,'yyyy-mm-dd'),'" and rig=',num2str(value(RigID))]); + end + + %Let's populate the 5 training sessions (changed by sharbat, with + %approx times guesse for Akrami lab + ratsch = cell(6,1); for i=1:6; ratsch{i} = ''; end + sch = cell(6,1); + + + sch{1} = ' 8-10am: '; + sch{2} = '10-12am: '; + sch{3} = '12- 2pm: '; + sch{4} = ' 2- 4pm: '; + sch{5} = ' 4- 6pm: '; + sch{6} = ''; + + if ~isnan(value(RigID)) + for i = 1:5 + temp = slots==i; + if sum(temp) == 1 + ratsch{i} = rats{temp}; + sch{i} = [sch{i},ratsch{i}]; + end + end + end + set(get_ghandle(Schedule),'string',sch(1:5)); + set(get_ghandle(SchList),'string',sch); %#ok + RatSch.value = ratsch; + + + case 'estimate_current_session' + %% estimate_current_session + %Here we will try to estimate which session is currently training + + CS = value(CurrSession); + + [ratSCH slots] = bdata(['select ratname, timeslot from scheduler where date="',... + datestr(now,'yyyy-mm-dd'),'"']); + ratSES = bdata(['select ratname from sessions where sessiondate="',datestr(now,'yyyy-mm-dd'),'"']); + [ratSS ST] = bdata(['select ratname, starttime from sess_started where sessiondate="',... + datestr(now,'yyyy-mm-dd'),'"']); + + %Let's cycle through each of the 6 training sessions and see if the + %rats are started, done, or neither + COMP = zeros(5,1); + for i = 1:5 + currats = ratSCH(slots == i); + currats(strcmp(currats,'')) = []; + comp = zeros(size(currats)); + st = zeros(size(currats)); st(:) = nan; + for j = 1:length(currats) + if sum(strcmp(ratSES,currats{j})) > 0; comp(j) = 2; %rat finished + elseif sum(strcmp(ratSS, currats{j})) > 0; comp(j) = 1; %rat running + st(j) = datenum(ST{find(strcmp(ratSS, currats{j})==1,1,'first')},'HH:MM:SS'); + end + end + + if sum(comp == 2) >= numel(comp)/2; COMP(i) = 2; %session is likely completed + elseif sum(comp > 0) >= numel(comp)/2; COMP(i) = 1; %session is likely running + else COMP(i) = 0; %session not yet started + end + + %If the session is still running, let's see how long they've been in. + if COMP(i) == 1 + st = (datenum(datestr(now,'HH:MM:SS')) - nanmean(st)) * 24 * 60; + if st > 60; COMP(i) = 2; end + end + end + + lastcomp = find(COMP == 2,1,'last'); + if isempty(lastcomp); CurrSession.value = 1; + elseif lastcomp == numel(COMP); CurrSession.value = []; + else CurrSession.value = lastcomp + 1; + end + + %In the event the system thinks we've finished 9 but there are + %sessions earlier that aren't completed, let's find the first not + %completed session and make that the current session. + %careful with no of sessions + if isempty(value(CurrSession)) + notcomp = find(COMP ~= 2,1,'first'); + if ~isempty(notcomp) + CurrSession.value = notcomp; + end + end + % WARNING : dummy value added by sharbat + % CurrSession.value = 4 + % WARNING : dummy value added by sharbat + CurrSession.value = find(COMP ~= 2,1,'first'); + SchList.value = value(CurrSession); + + if isempty(value(CurrSession)) || value(CurrSession) ~= CS + disp('Updating Current Session'); + + if ~isempty(value(CurrSession)) + runrats(obj,'updatelog','update_session'); + end + + if (isempty(value(CurrSession)) && ~isempty(CS) && CS == 5) %|| (value(CurrSession) == 7 && CS == 6) + %Training for today is nearing its end but this rig is + %likely not running a rat in session 9 or it's already out. + %Let's do the daily reboot for this rig here. + runrats(obj,'reboot') + end + + end + + + case 'safety_button' + %% safety_button + %The user had clicked the safety button which will unlock the multi + %button + + set(get_ghandle(Multi),'enable','on'); + + + case 'multi_button' + %% multi_button + %The user has clicked the Multi purpose button, depending on what + %state runrats is in, this button will do different things. Let's + %figure out what to do here. + + if strcmp(get(get_ghandle(Multi),'string'),'Load Protocol') + runrats(obj,'begin_load_protocol'); + + elseif ~isempty(strfind(get(get_ghandle(Multi),'string'),'Run')) + runrats(obj,'run'); + + elseif ~isempty(strfind(get(get_ghandle(Multi),'string'),'End Session')) + runrats(obj,'end'); + + elseif ~isempty(strfind(get(get_ghandle(Multi),'string'),'Crashed')) + runrats(obj,'crash_cleanup'); + + end + + + case 'begin_load_protocol' + %% begin_load_protocol + %Let's unload any protocols from dispatcher and either do the + %manual test or skip right to loading the protocol depending on the + %rig's settings + + %dammy add here + + InLiveLoop.value = 0; + runrats(obj,'disable_all'); + + set(get_ghandle(Multi),'string','Unloading...','fontsize',28); + + x = ''; + try x = dispatcher('get_protocol_object'); end %#ok + if ~isempty(x) + %There was a protocol previously open. Let's not trust that + %their close section is working properly. + try %#ok + %rigscripts does not exist currently, try Protocols (ask + %Athena) -sharbat + p = bSettings('get','GENERAL','Main_Code_Directory'); + p(strfind(p,'ExperPort'):end) = ''; + p = [p,'Rigscripts']; + cd(p); + if ispc == 1 + system('restart_runrats.bat'); + end + end + end + + dispatcher('set_protocol',''); + if bSettings('get','RUNRATS','skip_manual_test') == 1 + runrats(obj,'load_protocol'); + else + runrats(obj,'manual_test'); + end + + + case 'manual_test' + %% manual_test + %Load and run the manual test protocol + + %Reset the values that GCS sees for trials and performance + try send_n_done_trials(obj,'reset'); end %#ok + + set(get_ghandle(Multi),'string','Loading Test','fontsize',28); + + r = rand(1); + if r < 0.0015 && value(RigID) < 40 + runrats(obj,'updatelog','manualtest_leftfail'); + dispatcher('set_protocol','Rigtest_singletrial_leftfail'); + elseif r < 0.003 && value(RigID) < 40 + runrats(obj,'updatelog','manualtest_rightfail'); + dispatcher('set_protocol','Rigtest_singletrial_rightfail'); + else + runrats(obj,'updatelog','manualtest'); + dispatcher('set_protocol','Rigtest_singletrial'); + end + %Hide protocol window. + h=get_sphandle('owner','Rigtest_singletrial','name', 'myfig'); + for i=1:numel(h); set(value(h{i}),'Visible','Off'); end + + set(get_ghandle(Multi),'String','Manual Test','Fontsize',28); + StatusBar.value='Please test the rig by poking in the lit pokes.'; + + %Begin execution of the manual test protocol. + dispatcher(value(dispobj),'Run'); %#ok + + %RigTest_singletrial will call runrats('rigtest_singletrial_is_complete') + %after it finishes one trial at which point we continue on + + + case 'rigtest_singletrial_is_complete' + %% rigtest_singletrial_is_complete + %The manual test is complete, let's clear out the protocol and move + %on to load the rat's protocol and settings + + set(get_ghandle(Multi),'String','Unloading Test','Fontsize',24); + StatusBar.value='Completing Test. Please be patient!'; + + dispatcher(value(dispobj),'Stop'); %#ok + + %Let's pause until we know dispatcher is done running + set(value(stopping_complete_timer),'TimerFcn',[mfilename,'(''rigtest_singletrial_is_complete_continued'');']); + start(value(stopping_complete_timer)); + + + case 'rigtest_singletrial_is_complete_continued', + %% rigtest_singletrial_is_complete_continued + if value(stopping_process_completed) %This is provided by dispatcher to runrats + stop(value(stopping_complete_timer)); %Stop looping. + dispatcher('set_protocol',''); + runrats(obj,'load_protocol'); + end + + + + case 'load_protocol' + %% load_protocol + %Okay, we are finally ready to load the protocol and the rats + %settings, runrats will then wait for the tech to click Run + + %Let's make sure we have the most up-to-date settings + runrats(obj,'update_rat',0); + + set(get_ghandle(Multi),'String','Loading...','BackgroundColor', [0.8 0.8 0.6],'Fontsize',30); + StatusBar.value='Loading protocol and settings. Please be patient!'; + pause(0.1); + + %Let's also make sure we have the most up-to-date code + CurrDir = pwd; + pname = bSettings('get','GENERAL','Main_Code_Directory'); + if ~isempty(pname) && ischar(pname) + update_folder(pname,'svn'); + end + + %And finally we make sure the protocols are up-to-date + pname = bSettings('get','GENERAL','Protocols_Directory'); + if ~isempty(pname) && ischar(pname) + update_folder(pname,'svn'); + end + cd(CurrDir); + + %Let's get the protocol for the rat and load it + CurrProtocol.value = getProtocol(value(ExpMenu),value(RatMenu)); %#ok + if isempty(value(CurrProtocol)) + StatusBar.value = ['No Settings for ',value(RatMenu)]; + runrats(obj,'enable_all'); + set(get_ghandle(Multi),'string','Load Protocol','BackgroundColor',[1,1,0.4],'FontSize',24); + InLiveLoop.value = 1; + + %Let's notify the experimenter that the load failed, pause to + %let the tech see the note, then go back to live loop + runrats(obj,'email_experimenter','no settings'); + + runrats(obj,'updatelog','nosettings'); + + pause(10); + runrats(obj,'live_loop'); + return; + end + + try + dispatcher(value(dispobj),'set_protocol',value(CurrProtocol)); %#ok + catch %#ok + StatusBar.value = ['Failed to load ',value(CurrProtocol),' for ',value(RatMenu)]; + runrats(obj,'enable_all'); + set(get_ghandle(Multi),'string','Load Protocol','BackgroundColor',[1,1,0.4],'FontSize',24); + InLiveLoop.value = 1; + + %Let's notify the experimenter that the load failed, pause to + %let the tech see the note, then go back to live loop + runrats(obj,'email_experimenter','protocol fail'); + + runrats(obj,'updatelog','failload protocol'); + + pause(10); + runrats(obj,'live_loop'); + return; + end + + rath=get_sphandle('name','ratname','owner',value(CurrProtocol)); + exph=get_sphandle('name','experimenter','owner',value(CurrProtocol)); + rath{1}.value=value(RatMenu); %#ok + exph{1}.value=value(ExpMenu); %#ok + + try + protobj=eval(value(CurrProtocol)); + + [out, sfile]=load_solouiparamvalues(value(RatMenu),'experimenter',value(ExpMenu),... + 'owner',class(protobj),'interactive',0); + settings_file_sph.value = sfile; + settings_file_load_time.value = now; + if ~dispatcher('is_running') + pop_history(class(protobj), 'include_non_gui', 1); + feval(value(CurrProtocol), protobj, 'prepare_next_trial'); + end + catch %#ok + StatusBar.value = 'Failed to load Settings file.'; + runrats(obj,'enable_all'); + set(get_ghandle(Multi),'string','Load Protocol','BackgroundColor',[1,1,0.4],'FontSize',24); + InLiveLoop.value = 1; + + %Let's notify the experimenter that the load failed, pause to + %let the tech see the note, then go back to live loop + runrats(obj,'email_experimenter','settings fail'); + + runrats(obj,'updatelog','failload settings'); + + pause(10); + runrats(obj,'live_loop'); + return; + end + + set(get_ghandle(Multi),'String',['Run: ',value(RatMenu)],'BackgroundColor',[0.3,1,0.3],'Fontsize',32); + [pname,fname,ext] = fileparts(sfile); %#ok + + StatusBar.value=['Using settings file: ',fname]; + if value(phys)==1 + create_phys_session(eval(value(CurrProtocol))) + end + + runrats(obj,'enable_all'); + figure(value(myfig)); + InLiveLoop.value = 0; + + %Check to see if the experimenter wants to enable the safety before + if ~isempty(strfind(value(SafetyMode),'B')) %#ok + set(get_ghandle(Multi),'enable','off'); + set(get_ghandle(Safety),'visible','on','string',value(Instructions)); %#ok + else + set(get_ghandle(Multi),'enable','on'); + set(get_ghandle(Safety),'visible','off','string',''); + end + + + case 'run' + %% run + %Here we start the protocol running + runrats(obj,'updatelog','runstart'); + runrats(obj,'disable_all'); + set(get_ghandle(Multi),'String','End Session','BackgroundColor',[1,0.2,0.2],'Fontsize',28); + StatusBar.value = ['Start Time: ',datestr(now,'HH:MM PM'),'. ',value(StatusBar)]; %#ok + + try + sendstarttime(eval(value(CurrProtocol))); %#ok + catch %#ok + disp('ERROR: Failed to add the start time to the MySQL table.'); + end; + + %Let's make everything unresponsive for 5 more seconds to prevent + %double clicks from stopping the session + pause(5); + + %Start raspberry pi_camera + % try + % disp('trying camera') + % start_camera(value(RigID),value(RatMenu),value(CurrProtocol),'start') + % catch + % disp('failed to start pi camera') + % end + + % If using USB Webcam, then try using it + % try + % disp('Connecting to USB HD Camera') + % webcam_connected = webcamlist; + % webcam_idx = find(contains(webcam_connected,'USB')); + % if ~isempty(webcam_idx) % USB Camera connected + % cam = webcam(webcam_connected{webcam_idx}); + % fig = figure('NumberTitle','off','MenuBar','none'); + % fig.Name = 'My Camera'; + % ax = axes(fig); + % frame = snapshot(cam); + % im = image(ax,zeros(size(frame),'uint8')); + % axis(ax,'image'); + % preview(cam,im) + % Camera_Fig_window.value = fig; + % Camera_Obj.value = cam; + % Camera_Image.value = im; + % else + % disp('No USB camera connected') + % end + % catch + % disp('failed to connect to USB camera') + % end + + %Enable the Multi button so the user can stop the session + enable(Multi); + + %Check to see if the experimenter wants to enable the safety before + if ~isempty(strfind(value(SafetyMode),'A')) %#ok + set(get_ghandle(Multi),'enable','off'); + set(get_ghandle(Safety),'visible','on','string',value(Instructions)); %#ok + else + set(get_ghandle(Multi),'enable','on'); + set(get_ghandle(Safety),'visible','off','string',''); + end + + dispatcher(value(dispobj),'Run'); %#ok + + + case 'flicker_multibutton' + %% flicker_multibutton + %Called by dispatcher while running to invert the color of the + %multi button, let's the tech know if the rig is running + clr = get(get_ghandle(Multi),'BackgroundColor'); + set(get_ghandle(Multi),'BackgroundColor',1 - clr); + + + case 'end' + %% end + %Ends the current protocol being run through dispatcher + runrats(obj,'updatelog','runend'); + runrats(obj,'disable_all'); + set(get_ghandle(Multi),'String','Saving...','Fontsize',32); + + %Stop raspberry pi_camera + % try + % disp('stopping camera') + % start_camera(value(RigID),value(RatMenu),value(CurrProtocol),'stop') + % catch + % disp('failed to stop pi camera') + % end + + % Stop USB Camera + % try + % closePreview(value(Camera_Obj)) + % clear(value(Camera_Image)) + % clear(value(Camera_Obj)); + % close(value(Camera_Fig_window)); + % disp('USB camera stopped') + % catch + % disp('failed to stop USB camera') + % end + + %Stop dispatcher and wait for it to respond + dispatcher(value(dispobj),'Stop'); %#ok + + %Let's pause until we know dispatcher is done running + set(value(stopping_complete_timer),'TimerFcn',[mfilename,'(''end_continued'');']); + start(value(stopping_complete_timer)); + + + case 'end_continued' + %% end_continued + if value(stopping_process_completed) %This is provided by dispatcher to runrats + stop(value(stopping_complete_timer)); %Stop looping. + + %Now that everything is stopped let's send an empty state + %matrix to the Linux machine. This will reset all the lines + %and sounds to be off. + runrats(obj,'send_empty_state_machine') + + protobj=eval(value(CurrProtocol)); %#ok + feval(value(CurrProtocol), protobj, 'end_session'); + sfile=SavingSection(protobj,'savedata','interactive',0); + + %if the protocol has a pre_saving_settings section, call it + try + feval(value(CurrProtocol),protobj,'pre_saving_settings'); + catch %#ok + disp('Protocol does not appeat to have a pre_saving_settings') + end + + + SavingSection(protobj,'savesets','interactive',0); + + [pname,fname] = fileparts(sfile); + + configFilePath = '..\PASSWORD_CONFIG-DO_NOT_VERSIONCONTROL.mat'; + load(configFilePath); + svnusername = svn_user; + svnpsswd = svn_password; + logmsg = 'saved data and settings file'; + + cd(pname); + cmdstr = char(strcat('svn add', {' '}, fname, '.mat',{'@'})); %the @ calls for a peg revision, ref to + % https://stackoverflow.com/questions/27312188/how-to-move-rename-a-file-in-subversion-with-characters-in-it + system(cmdstr); + cmdstr2 = sprintf('svn ci --username="%s" --password="%s" -m "%s"',char(svnusername), char(svnpsswd), char(logmsg)); + if ~(system(cmdstr2)) + display('SoloData data files added!') + end + + [setpname, setfname] = fileparts(value(settings_file_sph)); + cd(setpname); + cmdstr3 = char(strcat('svn add', {' '}, setfname, '.mat',{'@'})); %the @ calls for a peg revision, ref to + % https://stackoverflow.com/questions/27312188/how-to-move-rename-a-file-in-subversion-with-characters-in-it + try + system(cmdstr3); + catch + display('SoloData settings files seem to be there!') + end + + cmdstr4 = sprintf('svn ci --username="%s" --password="%s" -m "%s"',char(svnusername), char(svnpsswd), char(logmsg)); + if ~(system(cmdstr4)) + display('SoloData settings files added!') + end + StatusBar.value=['Saved data and settings file: ',fname]; + + set(get_ghandle(Multi),'String','Reloading'); + dispatcher('set_protocol',''); + + %Let's reset the Multi button and hop back in the live loop + set(get_ghandle(Multi),'ForegroundColor',[0,0,0],'BackgroundColor',... + [1,1,0.4],'string','Load Protocol','FontSize',24); + InLiveLoop.value = 1; + runrats(obj,'enable_all'); + + %We need to turn RunRats back to live mode + h = value(PanelBack); %#ok + for i = 1:length(h); + set(h(i),'BackgroundColor',[1,0.8,0.6]); + end + set(get_ghandle(UpdateMode),'String','Live Update On','BackgroundColor',[0.6 1 0.6],'ForegroundColor',[0 0 0]); + + %Another option is to now kill MatLab completely and restart + %runrats. This ensures windows don't pile up, and code can get + %updated before each session + + if value(do_full_restart) == 1 + + %Close the MySQL connection + try bdata('close'); end %#ok + + if value(CurrSession) == 9 %|| value(CurrSession) == 6 %#ok + %We just finished session 9 so let's do a full reboot + runrats(obj,'reboot') + else + %This is an earlier session during the day, let's simply + %restart Matlab + % + pause(1); + drawnow; + + p = bSettings('get','GENERAL','Main_Code_Directory'); + p(strfind(p,'ExperPort'):end) = ''; + p = [p,'Rigscripts']; + cd(p); + + try %#ok + if ispc == 1 + system('restart_runrats.bat') + else + system('./start_runrats.sh') + end + end + end + end + + %And now we hop back in the loop + runrats(obj,'live_loop'); + end + + + case 'reboot' + %% reboot + %This is designed to execute once each day, either when the session + %9 rat is done training or if this rig is not running a rat in + %session 9, when it sees that session 9 is nearning completion. + runrats(obj,'updatelog','reboot'); + try + try + %Once a week try to email me the datalogs so I can see if + %things are working. I'll deactivate this once I know all + %is working properly. + if strcmp(datestr(now,'ddd'),'Mon') + file = which('runrats_datalog.txt'); + path = bSettings('get','GENERAL','Main_Data_Directory'); + newfile = [path,filesep,'Data',filesep,'RunRats',filesep,'Rig',sprintf('%03.0f',value(RigID)),filesep,... + datestr(now,'yymmdd'),'_','Rig',sprintf('%03.0f',value(RigID)),'_runrats_datalog.txt']; + system(['echo f | xcopy "',file,'" "',newfile,'"']); + add_and_commit(newfile); + end + end + cd('\ratter\Rigscripts') + + !del do_on_reboot.bat + !copy start_runrats.bat do_on_reboot.bat /Y + pause(1) + system('shutdown -r -f -t 1'); + pause(20); + %!copy nothing.bat do_on_reboot.bat /Y + end + + + case 'crashed' + %% crashed + %Dispatcher crashed while running the protocol. Let's notify the + %tech that we've crashed and send an email with the last error + %message to the rat's owner. We will also update a MySQL table that + %the GCS reads to post that a crash happened + runrats(obj,'updatelog','crashed'); + set(get_ghandle(Multi),'String','Crashed','BackgroundColor',[0,0,0],... + 'ForegroundColor',[1,1,1],'Fontsize',40); + enable(Multi); + + if ~isempty(varargin) && iscell(varargin) && strcmp(class(varargin{1}),'MException') + lsterr = varargin{1}; + else + lsterr = lasterror; %#ok + end + + %Let's stop dispatcher and clean it up + RunningSection(value(dispobj),'RunStop'); %#ok + dispatcher('set_protocol',''); + + %Now we can email the rat's owner a crash report + try %#ok + message = cell(0); + message{end+1} = ['Rig ',num2str(value(RigID)),' crashed while running ',value(RatMenu),' at ',datestr(now,13)]; %#ok + message{end+1} = ''; + message{end+1} = lsterr.message; + message{end+1} = ''; + + for i = 1:length(lsterr.stack) + message{end+1} = [lsterr.stack(i).name,' at ',num2str(lsterr.stack(i).line)]; %#ok + end + + IP = get_network_info; + message{end+1} = ' '; + if ischar(IP); message{end+1} = ['Email generated by ',IP]; + else message{end+1} = 'Email generated by an unknown computer!!!'; + end + message{end+1} = 'ratter\ExperPort\Modules\@runrats\runrats.m'; + + %setpref('Internet','SMTP_Server','brodyfs2.princeton.edu'); + %setpref('Internet','E_mail',['RunRats',datestr(now,'yymm'),'@Princeton.EDU']); + set_email_sender + + owner = bdata(['select contact from rats where ratname="',value(RatMenu),'"']); + if ~isempty(owner) + owner = owner{1}; + owner = [',',owner,',']; + owner(owner == ' ') = ''; + cms = find(owner == ','); + + for i = 1:length(cms)-1 + exp = owner(cms(i)+1:cms(i+1)-1); + sendmail([exp,'@princeton.edu'],[value(RatMenu),' Crashed'],message); + end + end + end + + %Let's update the MySQL table to indicate a crash has happened + id = bdata(['select sessid from sess_started where ratname="',value(RatMenu),... + '" and was_ended=0 and sessiondate="',datestr(now,'yyyy-mm-dd'),'"']); + if ~isempty(id) + id = id(end); + bdata('call mark_crashed("{S}")',id); + end + + + + + case 'crash_cleanup' + %% crash_cleanup + %The tech has acknowledged the crash. Let's jump back in the loop + + runrats(obj,'enable_all'); + set(get_ghandle(Multi),'ForegroundColor',[0,0,0],'BackgroundColor',... + [1,1,0.4],'string','Load Protocol','FontSize',24); + + InLiveLoop.value = 1; + runrats(obj,'live_loop'); + + case 'updatelog' + + try + Exp = value(ExpMenu); + Rat = value(RatMenu); + Sch = value(SchList); + + time = datestr(now,'yymmdd HH:MM:SS'); + + str = [time,' ',varargin{1},' ',Exp,' ',Rat,' ',Sch,char(10)]; + file = which('runrats_datalog.txt'); + f = fopen(file,'a+t'); + fseek(f,0,'eof'); + fprintf(f,str,'char'); + fclose(f); + + %runrats_datalog{end+1} = str; + %save(file,'runrats_datalog'); + end + + + case 'email_experimenter' + %% email_experimenter + %Something has gone wrong during the load phase and we should email + %the experimenter if we can + + try %#ok + %setpref('Internet','SMTP_Server','brodyfs2.princeton.edu'); + %setpref('Internet','E_mail',['RunRats',datestr(now,'yymm'),'@Princeton.EDU']); + set_email_sender + + message{1} = ['Load failed for ',value(RatMenu)]; %#ok + if strcmp(varargin{1},'no settings') + subject = ['No settings for ',value(RatMenu)]; + message{2} = ['Please check that there are settings for ',value(RatMenu)]; + elseif strcmp(varargin{1},'protocol fail') + subject = ['Failed to load ',value(CurrProtocol)]; %#ok + message{2} = ['Protocol ',value(CurrProtocol),' could not be laoded for ',value(RatMenu)]; + else + subject = ['Failed to load settings for ',value(RatMenu)]; + message{2} = ['The settings file for ',value(RatMenu),' failed to load']; + end + + IP = get_network_info; + message{end+1} = ' '; + if ischar(IP); message{end+1} = ['Email generated by ',IP]; + else message{end+1} = 'Email generated by an unknown computer!!!'; + end + message{end+1} = 'ratter\ExperPort\Modules\@runrats\runrats.m'; + + contact = bdata(['select contact from rats where ratname="',value(RatMenu),'"']); + if ~isempty(contact) + contact = [',',contact{1},',']; + contact(contact == ' ') = ''; + cms = find(contact == ','); + for i = 1:length(cms)-1 + email = contact(cms(i)+1:cms(i+1)-1); + sendmail([email,'@princeton.edu'],subject,message); + end + end + end + + + + + case 'is_running', + %% is_running + %If the Multi button exists, runrats has been loaded + if exist('Multi','var'), obj = 1; else obj = 0; end + + + case 'get_settings_file_load_time' + %% get_settings_file_load_time + %This is called by SavingSection, let's not modify it + + if exist('settings_file_load_time', 'var') && isa(settings_file_load_time, 'SoloParamHandle') %#ok + varargout{1} = value(settings_file_load_time); + else + varargout{1} = 0; + end + + + case 'get_settings_file_path' + %% get_setting_file_path + %This is called by SavingSection, let's not modify it + + if exist('settings_file_sph', 'var') && isa(settings_file_sph, 'SoloParamHandle') %#ok + varargout{1} = value(settings_file_sph); + else + varargout{1} = ''; + end + + + case 'send_empty_state_machine' + %% send_empty_state_machine + %Sends an empty state matrix. This allows us to toggle DIO lines + + state_machine_server = bSettings('get','RIGS','state_machine_server'); + + server_slot = bSettings('get','RIGS','server_slot'); + if isnan(server_slot); server_slot = 0; end + + card_slot = bSettings('get', 'RIGS', 'card_slot'); + if isnan(card_slot); card_slot = 0; end + + sm = BPodSM(state_machine_server, 3333,server_slot); + sm = Initialize(sm); + + [inL outL] = MachinesSection(dispatcher,'determine_io_maps'); + + sma = StateMachineAssembler('full_trial_structure'); + sma = add_state(sma,'name','vapid_state_in_vapid_matrix'); + + send(sma,sm,'run_trial_asap',0,'input_lines',inL,'dout_lines',outL,'sound_card_slot', int2str(card_slot)); + + + case 'close' + %% close + %Closes runrats + + runrats(obj,'close_gui_only'); + + try + dispatcher(value(dispobj),'close'); %#ok + catch %#ok + disp('WARNING: Dispatcher close attempt in runrats failed.'); + end + + + case 'close_gui_only' + %% close_gui_only + + try + StatusBar.value='Cleaning up......'; + runrats(obj,'disable_all'); + catch %#ok + disp('WARNING: Close attempt in runrats failed.'); + end + delete(value(myfig)); + + delete_sphandle('owner', ['^@', mfilename '$']); + obj = []; + + + otherwise + warning('Unknown action " %s" !', action);%#ok +end; + +return; + + +function p=getProtocol(exprmtr,rat) + +olddir=cd; +p=''; +try %#ok + dd=bSettings('get','GENERAL','Main_Data_Directory'); + + if isnan(dd); dd='../SoloData'; end + if dd(end)~=filesep; dd(end+1)=filesep; end + %changed filesep, sharbat + dd=[dd,'Settings',filesep]; + cd([dd,exprmtr,filesep,rat]); + fn=dir('settings_*_*_*_*.mat'); + for xi=1:numel(fn) + s=fn(xi).name; + tc=textscan(s,'%s','Delimiter','_'); + prt{xi}=tc{1}{2}; %#ok + r=tc{1}{end}; + % Must have had 5 fields (settings, prot, exprtr, rat, date), the + % date must be 11 chars long (7 of date plus '.mat'), the first six + % of those must be numbers, not letters: + if length(tc{1}) == 5 && length(r) == 11 && all(~isletter(r(1:6))), + setdate{xi}=r(1:7); %#ok + else % not a file we want, give it a really early date, 2000: + setdate{xi}='000101a'; %#ok + end; + end + + [srtdsets, sdi]=sort(setdate); + + % Look only at settings that are not later than today + ymd = str2double(yearmonthday); keeps = ones(size(sdi)); + for i=1:length(sdi), if str2double(srtdsets{i}(1:6)) > ymd, keeps(i) = 0; end; end; + srtdsets = srtdsets(find(keeps)); sdi = sdi(find(keeps)); %#ok + + p=prt{sdi(end)}; + if p(1)=='@' + p=p(2:end); + end + +end +cd(olddir) + + +function update_folder(pname,vn) + +try + currdir = pwd; + cd(pname); + if strcmp(vn,'cvs') + failed1 = 0; + [failed2 message2] = system('cvs up -d -P -A'); + elseif strcmp(vn,'svn') + [failed1 message1] = system('svn cleanup'); + [failed2 message2] = system('svn update'); + end + cd(currdir); + + rig = bSettings('get','RIGS','Rig_ID'); + if ~ischar(rig); rig = num2str(rig); end + + if failed1 == 1 || failed2 == 1 + %setpref('Internet','SMTP_Server','brodyfs2.princeton.edu'); + %setpref('Internet','E_mail',['RunRats',datestr(now,'yymm'),'@Princeton.EDU']); + set_email_sender + + if pname(1) ~= filesep; pname = [filesep,pname]; end + if pname(end) ~= filesep; pname = [pname,filesep]; end + fs = find(pname == filesep); + + contact = ''; + for i = 1:length(fs)-1 + temp = pname(fs(i)+1:fs(i+1)-1); + if length(temp) == 4 && ~isempty(str2num(temp(2:4))) %#ok + %This is a rat, let's email the owner + contact = bdata(['select contact from rats where ratname="',temp,'"']); + elseif strcmpi(temp,'experport') || strcmpi(temp,'protocols') + contact = {'ckopec'}; + end + end + + if failed1 == 1 + message = cell(0); + message{1} = ['SVN cleanup failed in ',pname]; + message{2} = ' '; + message{3} = message1; + + IP = get_network_info; + message{end+1} = ' '; + if ischar(IP); message{end+1} = ['Email generated by ',IP]; + else message{end+1} = 'Email generated by an unknown computer!!!'; + end + message{end+1} = 'ratter\ExperPort\Modules\@runrats\runrats.m'; + + ctemp = [',',contact{1},',']; + ctemp(ctemp == ' ') = ''; + cms = find(ctemp == ','); + for i = 1:length(cms)-1 + email = ctemp(cms(i)+1:cms(i+1)-1); + sendmail([email,'@princeton.edu'],['SVN Cleanup FAILED on Rig ',rig],message); + end + end + + if failed2 == 1 + message = cell(0); + message{1} = [vn,' update failed in ',pname]; + message{2} = ' '; + message{3} = message2; + if strcmp(vn,'cvs'); subject = ['SVN Update FAILED on Rig ',rig]; + elseif strcmp(vn,'svn'); subject = ['SVN Update FAILED on Rig ',rig]; + else subject = ''; + end + + IP = get_network_info; + message{end+1} = ' '; + if ischar(IP); message{end+1} = ['Email generated by ',IP]; + else message{end+1} = 'Email generated by an unknown computer!!!'; + end + message{end+1} = 'ratter\ExperPort\Modules\@runrats\runrats.m'; + + ctemp = [',',contact{1},',']; + ctemp(ctemp == ' ') = ''; + cms = find(ctemp == ','); + for i = 1:length(cms)-1 + email = ctemp(cms(i)+1:cms(i+1)-1); + sendmail([email,'@ucl.ac.uk'],subject,message); + end + end + end +catch %#ok + senderror_report; +end + + + + diff --git a/ExperPort/Modules/@runrats/runrats.m b/ExperPort/Modules/@runrats/runrats.m index 443a5f3c..40873be6 100644 --- a/ExperPort/Modules/@runrats/runrats.m +++ b/ExperPort/Modules/@runrats/runrats.m @@ -762,6 +762,11 @@ end runrats(obj,'begin_load_protocol'); + + % Added to send the details about the experimenter and rat + case 'exp_rat_names' + varargout{1} = value(ExpMenu); + varargout{2} = value(RatMenu); %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% case 'update_tech_instructions' %% update_tech_instructions diff --git a/ExperPort/Plugins/@saveload/SavingSection.asv b/ExperPort/Plugins/@saveload/SavingSection.asv new file mode 100644 index 00000000..6e4a599d --- /dev/null +++ b/ExperPort/Plugins/@saveload/SavingSection.asv @@ -0,0 +1,369 @@ +% [x, y] = SavingSection(obj, action, x, y) +% +% Section that takes care of saving/loading data and settings. +% +% PARAMETERS: +% ----------- +% +% obj Default object argument. +% +% action One of: +% 'init' To initialise the section and set up the GUI +% for it +% +% 'reinit' Delete all of this section's GUIs and data, +% and reinit, at the same position on the same +% figure as the original section GUI was placed. +% +% 'savesets' Save GUI settings only to a file +% +% 'loadsets' Load GUI settings from a file +% +% 'savedata' Save all SoloParamHandles to a file. +% Also deletes previous _ASV.mat files from same +% protocol, experimenter, ratname, date. (They are +% presumably now unnecessary.) Does not erase any +% other _ASV.mat files. +% +% 'autosave_data' Every autosave_frequency calls with this +% action string, save a data file with _ASV.mat suffix, +% no commit, non-interactive. autosave_frequency is +% 20 by default; it can be changed by a call with +% 'set_autosave_frequency' as its action. +% Typically, you might do an 'autosave_data' call +% after every trial, and that way every +% autosave_frequency trials, the data gets saved. +% If a regular 'savedata' with the same filename +% is completed, the _ASV.mat file is deemed +% unnecessary and is deleted. +% +% 'set_autosave_frequency' n Requires one more extra +% parameter, n, a scalar positive integer. Ssts +% autosave_frequency to n. +% +% 'get_autosave_frequency' Returns the current value of +% autosave_frequency. +% +% 'loaddata' Load all SoloParamHandles from a file +% +% 'get_info' Returns the experimenter, rat name, and date +% when this data was saved (yymmdd string). Date +% string is '.' if this data was saved before date +% information started being stored. +% +% 'set_info' experimenter ratname. Takes two strings and sets +% the experimenter and ratname to thos values. +% +% x, y Only relevant to action = 'init'; they indicate the initial +% position to place the GUI at, in the current figure window +% +% RETURNS: +% -------- +% +% [x,y,z] When action == 'init', returns x and y, pixel positions on +% the current figure, updated after placing of this section's GUI. +% When action == 'get_info', returns experimenter, rat name, and +% date when this data was saved (string yymmdd). +% + +function [x, y, z] = SavingSection(obj, action, x, y, varargin) + z = '.'; % added to allow for the extra return val without complaint + + GetSoloFunctionArgs(obj); + + + % Why would you put this here instead of in the saving cases? + % No. The variable is now updated below, ON DATA SAVE ONLY (i.e. + % not on settings save). Naturally, it then corresponds to + % the saved date from loaded data as well. + % + % if exist('SaveTime', 'var') && isa(SaveTime, 'SoloParamHandle'), + % SaveTime.value = datestr(now); + % end; + + switch action + case 'init', % ------------ CASE INIT -------------------- + % Save the figure and the position in the figure where we are + % going to start adding GUI elements: + SoloParamHandle(obj, 'my_gui_info', 'value', [x y double(gcf)]); + SoloParamHandle(obj, 'data_file', 'value', ''); + + %Sundeep Tuteja, 22nd December, 2009: Adding a SoloParamHandle called + %settings_file to store the full path to the currently loaded settings file. + try + [experimenter_name, rat_name] = runrats('exp_rat_names'); + [dummy, settings_file_str] = runrats('get_settings_file_path'); + clear('dummy'); + catch %#ok + settings_file_str = ''; + experimenter_name = 'experimenter' + end + SoloParamHandle(obj, 'settings_file', 'value', settings_file_str); + try + [dummy, settings_file_load_time_num] = runrats('get_settings_file_load_time'); + clear('dummy'); + catch %#ok + settings_file_load_time_num = 0; + end + SoloParamHandle(obj, 'settings_file_load_time', 'value', settings_file_load_time_num); + + EditParam(obj, 'experimenter', 'experimenter', x, y); next_row(y, 1.5); + set_callback(experimenter, {mfilename, 'set'}); + EditParam(obj, 'ratname', 'ratname', x, y); next_row(y, 1.5); + set_callback(ratname, {mfilename, 'set'}); + + PushbuttonParam(obj, 'loadsets', x, y, 'label', 'Load Settings'); + set_callback(loadsets, {mfilename, 'loadsets'}); + next_row(y); + PushbuttonParam(obj, 'savesets', x, y, 'label', 'Save Settings'); + set_callback(savesets, {mfilename, 'savesets'}); + next_row(y, 1.5); + + PushbuttonParam(obj, 'loaddata', x, y, 'label', 'Load Data'); + set_callback(loaddata, {mfilename, 'loaddata'}); + next_row(y); + PushbuttonParam(obj, 'savedata', x, y, 'label', 'Save Data'); + set_callback(savedata, {mfilename, 'savedata'}); + next_row(y); + %--- JS 2017, to permit running B-control outside of Brody lab + try + usingBdata = bSettings('get', 'GENERAL', 'use_bdata'); + catch + usingBdata = 1; + end + if usingBdata == 1 + SoloParamHandle(obj, 'hostname', 'value', get_hostname); + else + SoloParamHandle(obj, 'hostname', 'value', 'localhost'); + end + %--- end JS 2017 + SoloParamHandle(obj, 'SaveTime', 'value', '_'); % Changed from init val datestr(now) to init val '.'. The time this is initialized is NOT the time data was saved. This default value should be something more reasonable, perhaps, but any changes should be mirrored in the documentation at the top of this file and in sensitive code in PokesPlot and dispatcher script hook code. + + SoloParamHandle(obj, 'n_autosave_calls', 'value', 0); % How many 'autosave_data' calls have been done since init + SoloParamHandle(obj, 'autosave_frequency', 'value', 20); % Every autosave_frequency 'autosave_data' calls, save the data with _ASV.mat suffix, no commit, not interactive + + SoloParamHandle(obj, 'save_all_data_to_sql','value',0); + % <~> Addition of new interactivity toggle to make life a little + % faster for experimenters and technicians. The banner has also + % been shrunk horizontally. Oct 25 2007, -s + SubheaderParam(obj, 'title', mfilename, x, y, 'width',110); + ToggleParam(obj, 'interactive_by_default',1,x+111,y, ... + 'position', [x+111,y,88,20], ... + 'OnString', 'interactive', ... + 'OffString', 'noninteractive', ... + 'TooltipString', sprintf(['When this is "interactive",\n' ... + ' a save dialog pops up when save is pressed.\n' ... + 'When this is "noninteractive",\n' ... + ' the default filename is used without confirmation.'])); + next_row(y, 1.5); + + + return; + + + case 'set' + parname = x; parval = y; + switch parname + case 'ratname', + ratname.value = parval; + case 'experimenter', + experimenter.value = parval; + case 'data_file', + data_file.value = parval; + case 'save_all_data_to_sql' + save_all_data_to_sql.value=parval; + + otherwise, + warning('SAVELOAD:InvalidParam', 'Don''t know how to set "%s", not doing anything', parname); + end; + + + % ------------ CASE GET_ALL_INFO -------------------- + %Sundeep Tuteja, 22nd December, 2009: Adding a case to get + %experimenter name, rat name, settings file loaded, if any, and data file. Case + %'get_info' preserved + + case 'get_all_info' + x = struct([]); + x(1).experimenter = value(experimenter); + x(1).ratname = value(ratname); + [dummy, settings_file_from_runrats] = runrats('get_settings_file_path'); clear('dummy'); + %Giving preference to runrats (this should be changed, since it + %is possible to load a settings file even while running runrats. + if ~isempty(settings_file_from_runrats) + settings_file.value = settings_file_from_runrats; + end + x(1).settings_file = value(settings_file); + x(1).data_file = value(data_file); + y = []; + z = []; + return; + + + case 'get_info', % ------------ CASE GET_INFO -------------------- + y=value(ratname); + x=value(experimenter); + z=value(SaveTime); %#ok (This line OK. SPH initialized above) + return; + + case 'set_info', % ------------ CASE SET_INFO -------------------- + ratname.value=y; %#ok + experimenter.value=x; %#ok + return; + + case 'savesets', % ------------ CASE SAVESETS -------------------- + if nargin == 3, varargin = {x}; + elseif nargin == 4, varargin = {x y}; + elseif nargin >= 5, varargin = [{x y} varargin]; + end; + pairs = { ... + 'interactive' value(interactive_by_default) ; ... + 'commit' 1 ; ... % <~> turned back on (settings>>server) + }; parseargs(varargin, pairs); + + % <~> New functionality for saving settings for multiple rats at + % once by accepting a comma-separated list of rat names in the + % ratname field. 25 Oct 2007, -s + full_rat_str = value(ratname); + commas_in_ratname = strfind(full_rat_str,','); + commas_in_ratname = [0, commas_in_ratname, length(full_rat_str)+1]; + for i=2:length(commas_in_ratname), + this_ratname = strtrim(full_rat_str(commas_in_ratname(i-1)+1:commas_in_ratname(i)-1)); + if ~isempty(this_ratname), + ratname.value = this_ratname; + save_solouiparamvalues( this_ratname, ... + 'experimenter', value(experimenter), ... + 'interactive', interactive, ... + 'owner', class(obj), ... + 'commit', commit); + ratname.value = full_rat_str; + end; + end; + + return; + + case 'loadsets', % ------------ CASE LOADSETS -------------------- + % Disallow starting to run until settings finish loading: + dispatcher('runstart_disable'); + [sets_were_loaded, settings_file.value]= load_solouiparamvalues(value(ratname), 'experimenter', value(experimenter), 'owner', class(obj)); + % TESTING + if sets_were_loaded && ~dispatcher('is_running'), + settings_file_load_time.value = now; + % If we're not yet running, then current stored values for this + % trial will be overriden by the settings that are being loaded + % before the trial starts. Pop the history. Added by CDB to fix + % bug introduced by 'prepare_next_trial' below. + pop_history(class(obj), 'include_non_gui', 1); + feval(class(obj), obj, 'prepare_next_trial'); + % added by jerlich to flush the default SM loaded at 'init'. + end; % If we *are* already running a trial, then prepare_next_trial will be run when the trial ends; + % and that is the right time to run it. + + % end TESTING + dispatcher('runstart_enable'); + + return; + + case 'get_settings_file_load_time' + [dummy, x1] = runrats('get_settings_file_load_time'); clear('dummy'); + x2 = value(settings_file_load_time); + x = max(x1, x2); settings_file_load_time.value = x; + y = []; + z = []; + return; + + + case 'savedata', % ------------ CASE SAVEDATA -------------------- + if nargin == 3, varargin = {x}; + elseif nargin == 4, varargin = {x y}; + elseif nargin >= 5, varargin = [{x y} varargin]; + end; + pairs = { ... + 'interactive' value(interactive_by_default) ; ... + 'commit' 1 ; ... + 'asv' 0 ; ... + }; parseargs(varargin, pairs); + + SaveTime.value = datestr(now); % <~> added 2007.08.08 + + x=save_soloparamvalues(value(ratname), 'experimenter', value(experimenter), ... + 'interactive', interactive, ... + 'owner', class(obj), ... + 'commit', commit, 'asv', asv); + data_file.value=x; + + return; + + + case 'autosave_data', % ------------ CASE AUTOSAVE_DATA -------------------- + n_autosave_calls.value = n_autosave_calls + 1; + if rem(n_autosave_calls(1), autosave_frequency(1)) == 0, + SavingSection(obj, 'savedata', 'interactive', 0, 'commit', 0, 'asv', 1); + end; + + case 'set_autosave_frequency', % ------------ CASE SET_AUTOSAVE_FREQUENCY -------------------- + if nargin < 3, + warning('%s : %s : need an extra argument, \na scalar positive integer\nautosave_frequency not changed.', mfilename, action); %#ok + return; + end; + arg = x; + if isscalar(arg) && isnumeric(arg) && arg>=1, + autosave_frequency.value = ceil(arg); + return; + end; + + warning('%s : %s : argument must be numeric,\na scalar positive integer\nautosave_frequency not changed.', mfilename, action); %#ok + + + case 'get_autosave_frequency', % ------------ CASE GET_AUTOSAVE_FREQUENCY -------------------- + x = value(autosave_frequency); + + + case 'get_data_file', % ------------ CASE GET_AUTOSAVE_FREQUENCY -------------------- + x = value(data_file); + + case 'loaddata', % ------------ CASE LOADDATA -------------------- + if nargin == 3, varargin = {x}; + elseif nargin == 4, varargin = {x y}; + elseif nargin >= 5, varargin = [{x y} varargin]; + end; + pairs = { ... + 'interactive' 1 ; ... + }; parseargs(varargin, pairs); + % Although this shouldn't happen, we should be consistent and + % restrict running here, too. + % Disallow starting to run until data finish loading: + dispatcher('runstart_disable'); + load_soloparamvalues(value(ratname), 'experimenter', value(experimenter) ,... + 'owner', class(obj), 'interactive', interactive); + dispatcher('runstart_enable'); + + return; + + + case 'check_autosave', + if rem(n_done_trials,19) == 0 && n_done_trials>1, + SavingSection(obj, 'savedata', 'interactive', 0, 'commit', 0, ... + 'asv', 1); + end; + + case 'reinit', % ------------ CASE REINIT -------------------- + currfig = double(gcf); + + % Get the original GUI position and figure: + x = my_gui_info(1); y = my_gui_info(2); figure(my_gui_info(3)); + + % Delete all SoloParamHandles who belong to this object and whose + % fullname starts with the name of this mfile: + delete_sphandle('owner', ['^@' class(obj) '$'], ... + 'fullname', ['^' mfilename]); + + % Reinitialise at the original GUI position and figure: + [x, y] = feval(mfilename, obj, 'init', x, y); + + % Restore the current figure: + figure(currfig); + end; + + + \ No newline at end of file diff --git a/ExperPort/Plugins/@saveload/SavingSection.m b/ExperPort/Plugins/@saveload/SavingSection.m index f8a28b7d..1a6efb43 100644 --- a/ExperPort/Plugins/@saveload/SavingSection.m +++ b/ExperPort/Plugins/@saveload/SavingSection.m @@ -91,24 +91,23 @@ %Sundeep Tuteja, 22nd December, 2009: Adding a SoloParamHandle called %settings_file to store the full path to the currently loaded settings file. try - [dummy, settings_file_str] = runrats('get_settings_file_path'); - clear('dummy'); + [~,experimenter_name, rat_name] = runrats('exp_rat_names'); + [~, settings_file_str] = runrats('get_settings_file_path'); catch %#ok settings_file_str = ''; + experimenter_name = 'experimenter'; + rat_name = 'ratname'; end SoloParamHandle(obj, 'settings_file', 'value', settings_file_str); try - [dummy, settings_file_load_time_num] = runrats('get_settings_file_load_time'); - clear('dummy'); + [~, settings_file_load_time_num] = runrats('get_settings_file_load_time'); catch %#ok settings_file_load_time_num = 0; end SoloParamHandle(obj, 'settings_file_load_time', 'value', settings_file_load_time_num); - EditParam(obj, 'experimenter', 'experimenter', x, y); next_row(y, 1.5); - set_callback(experimenter, {mfilename, 'set'}); - EditParam(obj, 'ratname', 'ratname', x, y); next_row(y, 1.5); - set_callback(ratname, {mfilename, 'set'}); + EditParam(obj, 'experimenter', experimenter_name, x, y); next_row(y, 1.5); + EditParam(obj, 'ratname', rat_name, x, y); next_row(y, 1.5); PushbuttonParam(obj, 'loadsets', x, y, 'label', 'Load Settings'); set_callback(loadsets, {mfilename, 'loadsets'}); @@ -158,7 +157,7 @@ return; - + case 'set' parname = x; parval = y; switch parname diff --git a/ExperPort/settings_@ArpitCentrePokeTraining_Arpit_AR1_250427a.mat b/ExperPort/settings_@ArpitCentrePokeTraining_Arpit_AR1_250427a.mat new file mode 100644 index 0000000000000000000000000000000000000000..0414150f656e2e68660a8cb175f6d0e28a8f9fc7 GIT binary patch literal 15252 zcmb`O1yG#L)8GjaG6N9lxU6jKEjj7c>`mRQ zoPZ9Z^lH+&iYkI{IO(N#}^4emrg@G!}J^h3$n=srN;< zvJvU#zZuIl&g{4?CRW*X*(*MC1(GDGm0^DZtdBpmzuLI1)Btwe{En? zC?mcifPoXYO$d(CR+@elk}$Doq{Q=@pQNBHF^7v6*{YxUw7ZNTy%^ag_rz){nKcb} z6FP!3*C-zoTUFA3HK}*7@}^{>T5TLpqByi%?CiH5$4KI|4phSG%uig}yK##cUpnTH z0aq>UKbd!*_as&;D89sbOJAjdc|7H zVhFr4wZkWf9gO#~eFN_%t9k!ZDsB-Lk>30_*yYZVfwzl~ijKgZ#m#Mu=1UE_Z%V0r zThF&!Wj_KN=1Z-nMj=E`-(=2r!t87=UL7BbfIdw!wZxq!OCMZI+FTR!znD8sk_G?Z z)uSk&{Rxcg+gn>OY>C5Wd6tAxz$|tGNV3m2bico#1OmGSI14ZQlicQ|zCI$Xw^uPz z_JGHv_+jAafzB-tc$0eMG~a#U)!Z-GPyV+5Bmu7o=k>Sz(_z!KD~HV;ul$aVA>bR8 zS8V-OX3(?5qaV*bQ<~RQQ9s$ePtT>eP-x{!e*709qH1$|;NPe2f zL9Urv15e^ruws@;2z^S}n-Vl>hCIgXt6QbduHNBIQK2(=9V(s{{o_e*K#7|h<3~RV z@k8sD>hy=V1w<>yXxTqnN>wMOM19yv7{%-|KM$>lH>&b2_TBv0?vZJ!ynWU3@UrNe zH>34Kgyraal~dJdJ#d%Ru>x%Qtb3Rhh@NR`?k_tGr2yiEB|XUkm6A!dv)(wjQ`@-K z!w}{}poR0e!fkE4P2?g81;FPgBNKZIoh(sZ)e+Muye9c4r9FJ6rwD zwCZHsh2R0FO2Ds)XJ;Lk49hfS(*m5Tg8AZk+j45+;cv0X48L09=I5)WMW3MfL*!+% zlT_Coe`I}Q`OLcS*#Ro5ms~*Ee3e(!+E6Fb_DYd?LCdWEk9r?&-dn^}dB#R0 zW;nCB)fQCOU6GQUU;V1@sp}b z8_}B_f2KDSDhIsbxXerbbz@b-6N83kwb(=YNi~2G-|z>e*{<-rcBE8EXbf;-IQ{-0 zVQpEZOZ<(j{oy!56nSy5&}I0wGv0xFB7eeMAGx$&cDKLKjh-(1U~rS2$lmSU@^ix- zbRnNEcuH4e_(4=>A{nKr#BS}bGJYJ*I3n+v(YmsEkr}^3qfeI310W>^=WrR}xxBw2 zVU%FyrdxIs0^NyM`(b5wWNVR{wtFUz+`#Yij2I+aj)*p3y5Cvz4m%Z)p(5e~=aDesSKa^;c1yV2cDchI7O1|bJ{0~^-oXlt6xBY%^WrcVL zhlaPXzlOJ9OQ}$9*W5^hfU*->h%5Wfu;Y!DX+>(dD@Uq)i4N^Vf)=f0zJ|A#ZZT}} zWy#-Q;GF~ED;{sH<=E5r?dL`|LR}okzdTD;uOD)(w%TVL{dAXO=~Co=-f~z7c6D=D zZ6WvhsQ@_!*?-#xw3d!vkBU_P!uJ>z!uimCeh~a5RJS@9u{I^XX)!k=4L<$HLlIn5K z?`WhZOLHH`e-4zH+%MU?V!22&jzR}rOc~(s*U&c0sQY~Pw-yCsx=dyhlR>WWHaJhP0wySHeG?#-LJ+fEf;d7i%zJ?O)^y6 zp^g>_)INUrYQ!Ix^DJr_2(XmLRMn^mTvsA-a$m&@J3Psg|Aww0W6?tA*Q+az%|FBj zC9S`DyO?#7uea^7?un^T_h=(VKSSwi{7r-s-`Z7lg6~_=9z6BsW~OATYK`aON2Dfu zZ}(JWkY5?}T!0tK_M>>O6yv(sYkW8L3BfxXwbbchK%1i^a~m(Ry)<*k=a|+*{)-nI z%^IyUX8}3M^*=>3_NF*MuqP%9F}HI0zBxbr31&GgsoYEUFyVZe6_hb!OhtWU+VJt6jYrFJ8JbB776*J_C_dIs9$9|7PnwJKTZzNRs%43V!(- z;R$WN>Ok$YpS1IKZfPdb+wN%(hp+Ep`t1$gp74j=-L@`w&%>V~E8e>x(ro#Wg3p;f zpqA#kn#T^i(G5(#ZB%XJ@mRkLb8)`rYr|Mo*W!M$mE~U7eU?C-RxfuM&BAg|IbH)4 zGruJqy1CpBwSN!SB^`QEQB>(&4CF>#6j68Q?1RKNY3=sU8!DAgx>E*L2pQhM=EfWBy^d+206rPABmj3sB;(I+EkkZB2OLf>VqyGy>&g4ysI1G3^#dzN{v9h3&D zfM?s_P&L5*OYLK?^XbXSm%Bw+1Im)(VLiR9c7944X0AqHA{Kt>{{XRcDKJVj$yApligTsU2 zAK)58s`%TbCcv#1DJR{$cz&XVC6YRPmp_jfS!iFbe~}QlNIr11;=9j&SK38v@mreP zX+Kl-S#u7QPk+L^E`A2M_9$LVsB5&T2EZJdYz6pg~yseRZKiV`6%X9PO#3)H)AuZ{NMoh!}3HGTR!1!G-|3)K#7aT{>KLnKH*|VjRz;4@gSeLn_ua#mF z%6cU~dM{P%L_gxMAfCt64v}8~7>?f4KptzJ^c~UAw9wJ+NP@PmNS|EP72V+XdrpRv zz>0Q|>(fHZHFP;UNCjK4ZkHDS_D4;A{jR?qgsuM5!5>Y7kN%n4`!gmTR|Ls>b_A8P zX~Rt4t~42Gwme-RwF`eD8|@L^RJq0chzv8TrO>!TGa+(pYp zOWzP01g4ndVw~9Vo0&w$NDrngQ+@Pz?_{B;MO~K*PBH|9Wy`q*?NGCXQ6T*OD3(&b0k9rp8g1bibl*|&9h0blZ1qVibU zd>HyctrTCluhjYSNp|`iWTD66{+rrpgAmp6Y*`m)qYabE4f!qmANhlX;Sx+oGmrZ; zcjFsx5F`(shLU*C9_X`37-=4iz6J_~^PENCCFz%KfwQquR$N*=H>;Ud6kA`pI;4HT8(b2!RKmmIbE}3)hxNbZ=+b!S zP36)*b~{8m<1$)3<2>m3-NglW zC(Bn{7g5Gvr(veJ1D92;0mR@*x%1nS4wRx{VA?6{Qr;WTkCj)#oe3Nm@ohJx%yS`03}VcG_9($ucVlg}uX7tK%aX8mTf%mI?axs2XswvCAyw}; z8_s@nM$6VAFD7oo;Pzg)Hxots)jo^Ryc+uIf&VKCw^!c3Pkl3HCws8#vFMR~)xY6BJ#v}1o|p7)7zR+uC%T)HqmyF?} zOj3ZRZlJoY2s;qtpz9MdcQIpm7SGG|7$e#rv{i;+pwS8iX5ho`6rbzm9{(z5=mIc4 z$E7yJ4=Tr%^M6Qm#liS;i=hNH9$ax2 zckmf%NcgeaZC+ujM(b1PoImGM#mTZ&@*en%b5cV4TD5j_&qojco6!SLuDNyqR{#U( zJ*|zlXQd(aG%xpo3Kwv5z)&(PT7K#Ve1*l1NEu|@WVxRCxZgDF$U|}xEn%3sA6KsG z!^Bhv=zkr#4CWHJLy>1Mom()s7@uHe;MAK8yYorjNI{5M$r_E7CxjciB&bnXS@*f>f`V=&>TpG`Mar@1L4z-8x3~4Pql)Xwt7! zvh^6A?CNmd=AJI%qwU}mE){YV2nV}Lx&OA0BpnS3`<1nllJ3L`p}K@nS8`1b-fYh< zAaU4LUtQbCl@FF)*;RbjH0t79G4a1qaO=V{@s&B0G}?i6<92o+ zK6M5kkf@MCQap%#+&4`@N{0sNOsI%Y`Aj~?%%Ei2T=l$nmycll!xAFD)a5kdUr5Az zfJP~+Hf{GcnbZ0GZwl7$pX&#V%GU>S$$Xq#z{6>h%?`;uSRD*<< zR0$}JjqWGs(UmxbWxCj?7a2U2#jwBxmo>Jnj>qx*L?)Xb#lhq#z9I%n414D8w1DPy zIc@aM?GtU+@3>jDlR2ZvRcWn*w*AV_Xiv_z*R0tySQUm^l;(^_o6oz(FJ+Iu1)(2n z>Eb)Uhs;!nUOVkS|23i#eh7~#5&xAcxpHy5BHreq>((YCiFM|g)Ba|Z>h;lw)r0Ml z(8tJxwp(B}OVlWD(cx!XlR+m}A#wMpBG#|tsJ7nDu0CaFn}>x%xFAou8s^6*Xx6Ov z&FbBBjy%I)o510am2U2QcJW?Nt8(;W?e418YDaLkuY*hFN|)p6u2a5llSrAfVivYW z`ja=MBD2)-atl4uG_wuyaQ_Zf9i;#Ez2EFUzHb@}qEce?QgFf`>sLip^i}+96D-qB zyvjhys;L%S>bL}m+i_E$ys^l>QNo)CqyE{2b!4o>XA6`3(z9t_E}NIU3Q(_IsZ1^P z|IPngC0OR&6Q*TJZ^*jhccL*$Ywr6)FeK?aMljoi+&g~y@l$@N$b;Xj>)`%SV#>as z5O~j&wl6WH{DBxQn+XI1digKY<1jI zY63L!m@oLS^z+NE!6BFKj0w|h>w$e3Ee~^UdXp%zDKT{`X;9kn7pZk0lS?=kKd%_v zV-hlDyq_EO;p|)LgQvW)h`C&`H3*Qegs^}}~=6=iI zcNY8|<#YpF>(xD+q=BM?f+^6dAS+=LcbGjEyFR4|ZQ&Od!5Gt+aQp8E(^tl}AZW3! zOzQhpIz`cVTlcQ>MMEGkQkWYIaNJl+ZS|xAnU>GUPcpB;U#$f>CSh88K2o`vI2EL$ zRk*`Y_QSyu)!D5)u8R3kYnJ@lr^LH+U9`adw9gv{QQhyq(%-a|Qj|eFO=M2ZoWVu# z;kl*BiJ~YX%Z)nfopf#$WYRHs>t*B*2Hhnm{BT?&Hsrm^W|!UWt`I8&z@kr}7Enq3 zXRS$ZMP!56b`RXOPVniB%>FnYg~SsY)TADkd*ER?zSXV5G$4QB&7OBleeO`%`VgKp z(f2CXTcb46%4AnD0uQD-2?>_&b;4-r@*BVIX`*gp_>c=X`^6==pRuq~%h$(yKHLMY6F$6j-IBur97>Wb0$-!n9iV`ooC@%4hUqoBK!#F41t zG&=wGwNAOp#uHs4gti!&&cg5Pz$buD3Xp*+NZ$R;l#WT0D~gnkHbvLGri@`uWR={v zChxLrfH}#gy-njxcYIy(7tA9AI`&3y1(vKP2t1k)+!Uc z?ceM}Hkq$nXycq+AL|;REBZ;j0Hc&P0b(QZvOr4*TjKNVvJRl#+nXQ@1jBvdMWV7#uS5Z?|y_M2el&599}N0N=$70VIUp0BuMT8*|Kj@48p}TbhrKWFYZTkY#v#q*zWy_VyG1 z*EzTp5AuQcVJTeaZ|_PUI(3Br{gsm{{r6rBHG#n$)<{q znKY;=y-H)>jl$tA&HH3cXX`{5wU%$3fQv-EKA60y6W_mn`5P_Vkx>Y57Avj=<8m&G zrOWHjwfEtfgil%9ts%&y?PlDG!7m%h^Uv(lm}%fN`<5hBiamm*-_F2CUHkHu1oarR zSk{(4igBIm<9TzFbH`5)txM>Zh3B!CbWUAHY~zURLRpc>AWQP_1kRI3xN9YvIQ_h&cJa@MJ`K*BK;t-MZ>4(tXWYpMu^-L zelj22&21WjSqx!#1HPV#}9&ay~P7MN4Cz9rQ)>1 zLbp&N^$*($w`qpSL(?G9M8@OjCSCUU};bfb-H2lSRK+jNyGzna5C|Q zR^XR3NW46C3k?0k5b|_wEG)}bdbWhVNO}k_AR2}G5fwbRh$v~(5`oqfn z`Q9XxdoeWpg>Jj()kAr^NB}_DOdhPq;&Ft8FnQS5C>|}|X7$Gz)&3Fu`6Fqn)}}S@ z>2wwl%^UUi5R-o!(iskRE4&0j5{thiW2bqzE;>sZOL!9QLIj!Z3MdNjj=Q{#3z^KC zQ3MOj|Od6S{*g0hPxM%U$KiMGe<} zBM3nGF)M095(@Kw?j6AtJ@}B|ebVDBOK=t%&8nA^e~uKmqv?TGtV2s&q9ay*0X-ji zwgj&nuZHq7dEz@G*g>lq_YRVQMuhl!^7l#CcX|WTv2C*b4#X}-OrBKEEgY@TdkWDm zLHKpv4P#$*Hkoyhug*_@3-f(0x+P9PdUGQS_~8vg)7naHMw5j`$~qnsHIO zYc^3lL5MYBLcgxd^rTewdDq?VqCval7Ba~sO(wE)zwxwsg%U}hLyT5YAI#S9dB{XE z4`^tzY<Enk00TT||=Pj#RG+e-@O~UPIR$6V^50&zS=}MOo ztqu$|AJpU<4+zJst9{68L1>U@TW&20f101yO)x^|CYb3a5UVwP9+EmD&x`*@T|mBG zrv*E^?jZ9g!wwY{?=fMO^YIL8nYV=BeX9eW821T0zNE`q-p=-canY+*1ij)#9*0M{#g&c+$J$lfl3Hsc9@IH=@E0NkP>0d<-7s9KIa@OsiHfj>}VIHEg%w3ipI5V2Q)e(a8L4LM>o2u zXPm#h<1`%+z6reytrZ?Xp18D`o;fah`TV5yp5y2F2?rwsyrl;0MvGRSoi@i}2+KNc zDB)$bHoBaWL&OeSSu7|=!wv@ml5Q_q6m#$Q>v@u?)k#SWe$}o<^YghI?n#Frrh2!& z5gGL>kZkoEK%Qp4@?tLVyJ^}M#M7ZU;AwrBZm=Y*s66Ndn8BecNyeO%kCN5!^B zjpWqH#ruG^qroP>H|B3tzuD31w}#7DBw!;hmtpds37Pw?0?V3D&U7ho!GB z-1n>lK2DnWfRG@wjjlepbO(Wa4*$t4-G>vlUC|ApvvBi(yQ=yv4N1?6GSV7$loMY2 zf`k~)1Lte;U$yJHksGg}=);fli?B%JZmh&Gz^=)g*uRNdvY#PJbH&1VPaxI?UL|jw z($P;zmuL1FRyjrzQYRoDaK7g9eiQl|1natu$-y(6+lDm7ngHq(@w{vEHTAhvGa zG4gHHyEF@u;=<}iX%!D+elIY#algiAHSQdmkOocX(EbiRZSZAA98d%!KtNc3Vo@X_ zhHuBm-TiT@!Ar~+&nypRN-<(IaVo{6h0LLA=asFS2r@9YdfCh2VCqF|Np!v*% z{EqDS&dR;08BBEqlCG4l)eBZ>2n(AN|p zEB>Z^w;E?~#kxC5m@a(qHi5P1XH-$wp+6E^!i}%C#h1=Q%TH_hQDPMhRUXTGRU?(# zMKXcF`GkN?_4KN|P4zQCbhx#IEVM)Ph!VjPmdB!dn8=$x7*te9cJi|JP4^^R4IaW9 z##%<;py4ETy+7lh5h4FFTbnQ?=m#k7z5d!N%KVy=C6s7vn1$S!fC6829UcCeygcD~bnfdr`25f7ab!{VGt}e1 zw|jG$lbS1XB!dI-NzG%t)UzMjHC4@gr_^$aLl@tgQe!B*T9LB_~!S4GvI@){s z1nF>TJc+AC%`((Gr}4mPK+)hG)@rIw{ar#+{kPc#e%cN*gnyS4bj82@Du*7^0hagj zG=A7uAJc@c?&^tm;LnE{u((}1?VP&2P~EZbXS!~6>gRR}GXgQoR}>y!LDU|A#~wEA z7eC@WI4A1_Oa{s$hX<#a4&{#nM(Yfo{jFlPL8!GnADDH(ad)tecy~y?A0FjO!H8?{TtcxZG>0}EPX5PyA-w*3 zB=WVh^{AXOoiziSQ%GMnhWIUa_1yHAXc{KdNk+Mf+|19Q)FnWhY4)_xnuI}i zh0pWL$W2SK{+6!i+eV1o3G|dX$Qaj@<(MtiPg9EO;?n6d9ko5VBJqs8e5(!r6xiLg zk|>m&o7B~(RV)K?7e`)nw0b4ply&pUj zvoO9KlWdwkRlEuPJj~hh;}!pCRe5tG$@S$IbA-eXWZsfxv%In$^GFN3 zzBMt#)SDB(_UYu+&<{1;7pvWq;~8e9`@h-YjHJTsC?B)esA7CVHhv87264Y^Q^-h&*{+_DY+BGzx1;)eA5`?_F&_#Y_9Rr z;$?aW3|owO^UNuiu|^moxZW%Rkr6N)osHBf6i31dr(F8oelRcm!UJ_?MU`x_3UKOt z4%bZLsYtXl;(RPIias=((_0zqcdk0xeog(nchp_5j@El`h^uFHvp;|C9nfn`QWnE# zQ5-e4(T#qm3%Ul?pXr0ov`8jKd8m!pr%K||<6mY|v9i8KiP*mQZNQb0@A3n<`(fGN zRVyz}04o-?&>1g;ul%iutsjw}Fza~+azx6Iygt%VxUbNW*FMD~gi?^ih~HE^>UxDc z5&!391YaSr(-n9RxiB1whP(1Uc8*B+G)gMjo#_GtiL7|Yc{BehbKYmo-IE+XDy_HTzEGqJ`5GeXLFI}? z9$Nc}+QHMG0~6ymk6On_W2t{UOF=Sk8IZtqI2-ZwQl%7NyWa>)aMo_SeHxkhrCPI= zrSkr_=9%ge(<+1cQ6!KMAZ4+WSeE60M|(+w%o%GXO)4CWj*c!c33R^m!MO7o+yo>@ z`3<_*ppu!Qiyyx6TR?4Ci(?F02(NHN!3n12)+Emj%J;$u;M0} zt*zYqlMeS4N2LND2Z(NZF#*!kCEwSn0Wn_W7S`WaEFGHrByWc^l+Dtt0(zDP`SY9w zBQhTn6*4g~un?Mr&t9PDo&-KVuR6yYo$wlZ_VAgP;)X(3IxYM({3!q9(B4r9Aj|nH zis}oBrdQYe6_3cEl%djD8MobWx3m4(34MncrK%BdKm+8aRL~?A_hOAJ)Dvg@a+z6+ zLKXW|u(#demP`gsW8+?7-Qi^iSL~p?4J}HuH-K*G^l_loi^M2ZL(x>3Waq_%(Vnha z9jlhrIT!I)UMDZ}e1qg`1iszsO`v|nuzdBU+gQVBJ$TPgOQsp*;xD~!EZ+)#R1;NQ z9f(gb5u%UM2Zabe7)iH+zXfjmh=Er6@29Lor9FIuOqvG$q^w_ij*7I+^O3;~=K+;Y z5^^5#G>p)gHAy?{`bhLU?OQ%Wa3wfp`ZKHC^tGm=+=j)Nd@L+`*QDJ+@BO&S04i*~ zWqgt!eGhGe`kVLA$8iVWn|=$`8l46hGG*QFWf$otzj;eSn>lg5Lw`f8SJZ>T&NN={ zQpKgGWH%eYiQz7LQB`mBa1LuAaLwN$_rYPVV%gq3k{y!KbIbtYjER6Dq zgx&AmO^rsdHj)~LPxm7BK>Ybo@9(?@gWR;u?g~3t(!%zc`QM%h=>?KZ$es==TY#V3 zeXaTaZnFowkuZ`U{H!UcoQDGCj72d zeu5$QWTRT4d*Yx((AXPZ-Izl*&yvURP!8DX08|LsIA1*6z zOK(r4`w;|ZnfHpo-wPHYNX!jMVA{N|JLQGIBpG?HX-Z)I3x|4qb)51D`^@u{{N_LD zjW2M#jgH5{rNu$s8WvxpOE(I|>%me^iF8h1=$?vPg-^YeYFxiN>u^`gHRjo^TSxI7 zq6D}P{Lkx6J(ub>4CWoR0f$EqjCU~!xs-IE`~FG3ybIMr^^2_Yb5jP54 zPy7KmuIB{c+<;i4pbuP8bnB<_XE)8l#?WrbK==uq*pE}}EUJFNX4DALei{We=|%yK zNYYCQ9eY#x{VMXWmw5|9m(?vnP4759AaWF20t+gug&=C3CC~5vw_pPi#C_`XzK%l8 z$8ib8#YfASJT$FxH?DoL{l1%ys`wPPyENsq%Q zBSyB|YI3Y=P8nhq(@6A1O{3f)IM#<_PQfYjDPGgf5PcTD5^bx*(^fkM-+PMx2G~&e ze+L_C|7WlP`d`5Y)qlVStN$mkK?Tb{BXOzh{{z^7fOGh$mbw8|7d&y=43%rks`ig9 z-mfJaX>S*y%i=?U3KvNR%RP5z4OTY`yJbp=inn^ww$-bW2jxW?Lm>(RQm*;SOG7SoYUNyAo25`l$75huD zkR4@iFP!@W6=o%+}ORUKM9#hW>n~i!XcFzqCpYJD6~j^soq-Or=73nb#W@10;H#?S+P{ z?7Tf;?tTLVu#eomM3~6bnd#V1=ay&R^H-aVZy$esZ{PCRzIQDvz$0v7TC;$BNpDm} zdTqDBKj|m!L^w0Xt2XUy#nW*r5x6|3fe!H2S%0kUO2AG&2^@x!mP;fa!lgRo zc>nM-!Id|KX=+TM6LVR1cyMJ$Av-y>n@)P7DbACGxmKSTI#@b%J&i;AQXO>9niOF) zE|&<79=*=|%;!|}^4%~7m%QB2N=#v7fR9IUi@LQ7jMzgkXs?=V`b0Rk#?-t1X8W8; zD0oxk$@|uIc24u_yU=<=H&i}_IQw#nAG&AA{1XW&X~piXahlZ2x(8Jb@%M%Q0Wq-t z!4v+f2x85D=NwcM7dJ#nUn*^!tN$dSos?ACNcN5n#eAv$RdrcLtua5Mx2!`pXFx$~ zlES?6#5uc} zL2ba?QkhW8#SVkLzdd|^)kf(9K;$mvw5C%*Z#yuxo3aAd?#V%IoaWl?Y2)tWx}iT) z2h6&InrVqtxZ2j3P^(LVR)z1v&>hD`&uU=!P`qX8K->uChIn~z$f(nb|u zq@A+lX042Re{}lz-m2VKfTt@gCUK8nNBi@;?|@^?)L^dz0_MDw;OkMC!TZ;%GXF_5 z{0nc`&I$H1WZ!hDh$v%z8e{lUN*u%y?8WCF>?PTfrux&hB4W=@Uy>$lZ@YhXb!L3? z4+^oXL-5x*^G{oE$*1SrHJF>3(m8*l@QjvuQT^o*{s0UAq8I)sTdwT?LLBl^b9nxf zcSy(n3qR~yH7vHuHa_)oS$yiAKWRsS#B zpvXoatgFC|e;TDltCFvkH(tG;tzu%kvy@f(-vt}ecgzA017YQjRM1q9e%72Ac244 zASw8$pn&{87(~07;KNbE{B_OTFPE*BD=i!s+faudMlyo}@|7kRJv%~yV^kuixW?rqg-h{zT6Lv}i2`VMN zV0g!6m&?-iO|4RmvF~(13hJLj$kCjEY9Bob+9&0?Yl}O!TjEN~^kT;k6~4^h#fq3x zRKAJcU(PsW{r0KzhixPzkY?3l;HA%KDCBa!F~~3*cxOWsT`~mGb1hKWEn)92NoCj@ zbLRTi_mDYQoLa_oTfWbU_2VHR^hcsT?G>~jeCfNFU92~Q4N?EFo1oQCb3UV_N{LtZ z77AB_3JPB9jndp^^*IyyHmD5SCIja_*E8#1d Date: Sun, 27 Apr 2025 21:22:39 +0100 Subject: [PATCH 072/164] last commit again --- ExperPort/Plugins/@saveload/SavingSection.asv | 369 ------------------ 1 file changed, 369 deletions(-) delete mode 100644 ExperPort/Plugins/@saveload/SavingSection.asv diff --git a/ExperPort/Plugins/@saveload/SavingSection.asv b/ExperPort/Plugins/@saveload/SavingSection.asv deleted file mode 100644 index 6e4a599d..00000000 --- a/ExperPort/Plugins/@saveload/SavingSection.asv +++ /dev/null @@ -1,369 +0,0 @@ -% [x, y] = SavingSection(obj, action, x, y) -% -% Section that takes care of saving/loading data and settings. -% -% PARAMETERS: -% ----------- -% -% obj Default object argument. -% -% action One of: -% 'init' To initialise the section and set up the GUI -% for it -% -% 'reinit' Delete all of this section's GUIs and data, -% and reinit, at the same position on the same -% figure as the original section GUI was placed. -% -% 'savesets' Save GUI settings only to a file -% -% 'loadsets' Load GUI settings from a file -% -% 'savedata' Save all SoloParamHandles to a file. -% Also deletes previous _ASV.mat files from same -% protocol, experimenter, ratname, date. (They are -% presumably now unnecessary.) Does not erase any -% other _ASV.mat files. -% -% 'autosave_data' Every autosave_frequency calls with this -% action string, save a data file with _ASV.mat suffix, -% no commit, non-interactive. autosave_frequency is -% 20 by default; it can be changed by a call with -% 'set_autosave_frequency' as its action. -% Typically, you might do an 'autosave_data' call -% after every trial, and that way every -% autosave_frequency trials, the data gets saved. -% If a regular 'savedata' with the same filename -% is completed, the _ASV.mat file is deemed -% unnecessary and is deleted. -% -% 'set_autosave_frequency' n Requires one more extra -% parameter, n, a scalar positive integer. Ssts -% autosave_frequency to n. -% -% 'get_autosave_frequency' Returns the current value of -% autosave_frequency. -% -% 'loaddata' Load all SoloParamHandles from a file -% -% 'get_info' Returns the experimenter, rat name, and date -% when this data was saved (yymmdd string). Date -% string is '.' if this data was saved before date -% information started being stored. -% -% 'set_info' experimenter ratname. Takes two strings and sets -% the experimenter and ratname to thos values. -% -% x, y Only relevant to action = 'init'; they indicate the initial -% position to place the GUI at, in the current figure window -% -% RETURNS: -% -------- -% -% [x,y,z] When action == 'init', returns x and y, pixel positions on -% the current figure, updated after placing of this section's GUI. -% When action == 'get_info', returns experimenter, rat name, and -% date when this data was saved (string yymmdd). -% - -function [x, y, z] = SavingSection(obj, action, x, y, varargin) - z = '.'; % added to allow for the extra return val without complaint - - GetSoloFunctionArgs(obj); - - - % Why would you put this here instead of in the saving cases? - % No. The variable is now updated below, ON DATA SAVE ONLY (i.e. - % not on settings save). Naturally, it then corresponds to - % the saved date from loaded data as well. - % - % if exist('SaveTime', 'var') && isa(SaveTime, 'SoloParamHandle'), - % SaveTime.value = datestr(now); - % end; - - switch action - case 'init', % ------------ CASE INIT -------------------- - % Save the figure and the position in the figure where we are - % going to start adding GUI elements: - SoloParamHandle(obj, 'my_gui_info', 'value', [x y double(gcf)]); - SoloParamHandle(obj, 'data_file', 'value', ''); - - %Sundeep Tuteja, 22nd December, 2009: Adding a SoloParamHandle called - %settings_file to store the full path to the currently loaded settings file. - try - [experimenter_name, rat_name] = runrats('exp_rat_names'); - [dummy, settings_file_str] = runrats('get_settings_file_path'); - clear('dummy'); - catch %#ok - settings_file_str = ''; - experimenter_name = 'experimenter' - end - SoloParamHandle(obj, 'settings_file', 'value', settings_file_str); - try - [dummy, settings_file_load_time_num] = runrats('get_settings_file_load_time'); - clear('dummy'); - catch %#ok - settings_file_load_time_num = 0; - end - SoloParamHandle(obj, 'settings_file_load_time', 'value', settings_file_load_time_num); - - EditParam(obj, 'experimenter', 'experimenter', x, y); next_row(y, 1.5); - set_callback(experimenter, {mfilename, 'set'}); - EditParam(obj, 'ratname', 'ratname', x, y); next_row(y, 1.5); - set_callback(ratname, {mfilename, 'set'}); - - PushbuttonParam(obj, 'loadsets', x, y, 'label', 'Load Settings'); - set_callback(loadsets, {mfilename, 'loadsets'}); - next_row(y); - PushbuttonParam(obj, 'savesets', x, y, 'label', 'Save Settings'); - set_callback(savesets, {mfilename, 'savesets'}); - next_row(y, 1.5); - - PushbuttonParam(obj, 'loaddata', x, y, 'label', 'Load Data'); - set_callback(loaddata, {mfilename, 'loaddata'}); - next_row(y); - PushbuttonParam(obj, 'savedata', x, y, 'label', 'Save Data'); - set_callback(savedata, {mfilename, 'savedata'}); - next_row(y); - %--- JS 2017, to permit running B-control outside of Brody lab - try - usingBdata = bSettings('get', 'GENERAL', 'use_bdata'); - catch - usingBdata = 1; - end - if usingBdata == 1 - SoloParamHandle(obj, 'hostname', 'value', get_hostname); - else - SoloParamHandle(obj, 'hostname', 'value', 'localhost'); - end - %--- end JS 2017 - SoloParamHandle(obj, 'SaveTime', 'value', '_'); % Changed from init val datestr(now) to init val '.'. The time this is initialized is NOT the time data was saved. This default value should be something more reasonable, perhaps, but any changes should be mirrored in the documentation at the top of this file and in sensitive code in PokesPlot and dispatcher script hook code. - - SoloParamHandle(obj, 'n_autosave_calls', 'value', 0); % How many 'autosave_data' calls have been done since init - SoloParamHandle(obj, 'autosave_frequency', 'value', 20); % Every autosave_frequency 'autosave_data' calls, save the data with _ASV.mat suffix, no commit, not interactive - - SoloParamHandle(obj, 'save_all_data_to_sql','value',0); - % <~> Addition of new interactivity toggle to make life a little - % faster for experimenters and technicians. The banner has also - % been shrunk horizontally. Oct 25 2007, -s - SubheaderParam(obj, 'title', mfilename, x, y, 'width',110); - ToggleParam(obj, 'interactive_by_default',1,x+111,y, ... - 'position', [x+111,y,88,20], ... - 'OnString', 'interactive', ... - 'OffString', 'noninteractive', ... - 'TooltipString', sprintf(['When this is "interactive",\n' ... - ' a save dialog pops up when save is pressed.\n' ... - 'When this is "noninteractive",\n' ... - ' the default filename is used without confirmation.'])); - next_row(y, 1.5); - - - return; - - - case 'set' - parname = x; parval = y; - switch parname - case 'ratname', - ratname.value = parval; - case 'experimenter', - experimenter.value = parval; - case 'data_file', - data_file.value = parval; - case 'save_all_data_to_sql' - save_all_data_to_sql.value=parval; - - otherwise, - warning('SAVELOAD:InvalidParam', 'Don''t know how to set "%s", not doing anything', parname); - end; - - - % ------------ CASE GET_ALL_INFO -------------------- - %Sundeep Tuteja, 22nd December, 2009: Adding a case to get - %experimenter name, rat name, settings file loaded, if any, and data file. Case - %'get_info' preserved - - case 'get_all_info' - x = struct([]); - x(1).experimenter = value(experimenter); - x(1).ratname = value(ratname); - [dummy, settings_file_from_runrats] = runrats('get_settings_file_path'); clear('dummy'); - %Giving preference to runrats (this should be changed, since it - %is possible to load a settings file even while running runrats. - if ~isempty(settings_file_from_runrats) - settings_file.value = settings_file_from_runrats; - end - x(1).settings_file = value(settings_file); - x(1).data_file = value(data_file); - y = []; - z = []; - return; - - - case 'get_info', % ------------ CASE GET_INFO -------------------- - y=value(ratname); - x=value(experimenter); - z=value(SaveTime); %#ok (This line OK. SPH initialized above) - return; - - case 'set_info', % ------------ CASE SET_INFO -------------------- - ratname.value=y; %#ok - experimenter.value=x; %#ok - return; - - case 'savesets', % ------------ CASE SAVESETS -------------------- - if nargin == 3, varargin = {x}; - elseif nargin == 4, varargin = {x y}; - elseif nargin >= 5, varargin = [{x y} varargin]; - end; - pairs = { ... - 'interactive' value(interactive_by_default) ; ... - 'commit' 1 ; ... % <~> turned back on (settings>>server) - }; parseargs(varargin, pairs); - - % <~> New functionality for saving settings for multiple rats at - % once by accepting a comma-separated list of rat names in the - % ratname field. 25 Oct 2007, -s - full_rat_str = value(ratname); - commas_in_ratname = strfind(full_rat_str,','); - commas_in_ratname = [0, commas_in_ratname, length(full_rat_str)+1]; - for i=2:length(commas_in_ratname), - this_ratname = strtrim(full_rat_str(commas_in_ratname(i-1)+1:commas_in_ratname(i)-1)); - if ~isempty(this_ratname), - ratname.value = this_ratname; - save_solouiparamvalues( this_ratname, ... - 'experimenter', value(experimenter), ... - 'interactive', interactive, ... - 'owner', class(obj), ... - 'commit', commit); - ratname.value = full_rat_str; - end; - end; - - return; - - case 'loadsets', % ------------ CASE LOADSETS -------------------- - % Disallow starting to run until settings finish loading: - dispatcher('runstart_disable'); - [sets_were_loaded, settings_file.value]= load_solouiparamvalues(value(ratname), 'experimenter', value(experimenter), 'owner', class(obj)); - % TESTING - if sets_were_loaded && ~dispatcher('is_running'), - settings_file_load_time.value = now; - % If we're not yet running, then current stored values for this - % trial will be overriden by the settings that are being loaded - % before the trial starts. Pop the history. Added by CDB to fix - % bug introduced by 'prepare_next_trial' below. - pop_history(class(obj), 'include_non_gui', 1); - feval(class(obj), obj, 'prepare_next_trial'); - % added by jerlich to flush the default SM loaded at 'init'. - end; % If we *are* already running a trial, then prepare_next_trial will be run when the trial ends; - % and that is the right time to run it. - - % end TESTING - dispatcher('runstart_enable'); - - return; - - case 'get_settings_file_load_time' - [dummy, x1] = runrats('get_settings_file_load_time'); clear('dummy'); - x2 = value(settings_file_load_time); - x = max(x1, x2); settings_file_load_time.value = x; - y = []; - z = []; - return; - - - case 'savedata', % ------------ CASE SAVEDATA -------------------- - if nargin == 3, varargin = {x}; - elseif nargin == 4, varargin = {x y}; - elseif nargin >= 5, varargin = [{x y} varargin]; - end; - pairs = { ... - 'interactive' value(interactive_by_default) ; ... - 'commit' 1 ; ... - 'asv' 0 ; ... - }; parseargs(varargin, pairs); - - SaveTime.value = datestr(now); % <~> added 2007.08.08 - - x=save_soloparamvalues(value(ratname), 'experimenter', value(experimenter), ... - 'interactive', interactive, ... - 'owner', class(obj), ... - 'commit', commit, 'asv', asv); - data_file.value=x; - - return; - - - case 'autosave_data', % ------------ CASE AUTOSAVE_DATA -------------------- - n_autosave_calls.value = n_autosave_calls + 1; - if rem(n_autosave_calls(1), autosave_frequency(1)) == 0, - SavingSection(obj, 'savedata', 'interactive', 0, 'commit', 0, 'asv', 1); - end; - - case 'set_autosave_frequency', % ------------ CASE SET_AUTOSAVE_FREQUENCY -------------------- - if nargin < 3, - warning('%s : %s : need an extra argument, \na scalar positive integer\nautosave_frequency not changed.', mfilename, action); %#ok - return; - end; - arg = x; - if isscalar(arg) && isnumeric(arg) && arg>=1, - autosave_frequency.value = ceil(arg); - return; - end; - - warning('%s : %s : argument must be numeric,\na scalar positive integer\nautosave_frequency not changed.', mfilename, action); %#ok - - - case 'get_autosave_frequency', % ------------ CASE GET_AUTOSAVE_FREQUENCY -------------------- - x = value(autosave_frequency); - - - case 'get_data_file', % ------------ CASE GET_AUTOSAVE_FREQUENCY -------------------- - x = value(data_file); - - case 'loaddata', % ------------ CASE LOADDATA -------------------- - if nargin == 3, varargin = {x}; - elseif nargin == 4, varargin = {x y}; - elseif nargin >= 5, varargin = [{x y} varargin]; - end; - pairs = { ... - 'interactive' 1 ; ... - }; parseargs(varargin, pairs); - % Although this shouldn't happen, we should be consistent and - % restrict running here, too. - % Disallow starting to run until data finish loading: - dispatcher('runstart_disable'); - load_soloparamvalues(value(ratname), 'experimenter', value(experimenter) ,... - 'owner', class(obj), 'interactive', interactive); - dispatcher('runstart_enable'); - - return; - - - case 'check_autosave', - if rem(n_done_trials,19) == 0 && n_done_trials>1, - SavingSection(obj, 'savedata', 'interactive', 0, 'commit', 0, ... - 'asv', 1); - end; - - case 'reinit', % ------------ CASE REINIT -------------------- - currfig = double(gcf); - - % Get the original GUI position and figure: - x = my_gui_info(1); y = my_gui_info(2); figure(my_gui_info(3)); - - % Delete all SoloParamHandles who belong to this object and whose - % fullname starts with the name of this mfile: - delete_sphandle('owner', ['^@' class(obj) '$'], ... - 'fullname', ['^' mfilename]); - - % Reinitialise at the original GUI position and figure: - [x, y] = feval(mfilename, obj, 'init', x, y); - - % Restore the current figure: - figure(currfig); - end; - - - \ No newline at end of file From 89c8cb453b8012baec7f8f571629b7d4ddff8c64 Mon Sep 17 00:00:00 2001 From: Arpit Date: Fri, 2 May 2025 11:38:52 +0100 Subject: [PATCH 073/164] error debug session definition --- ExperPort/Plugins/@sessionmodel2/SessionDefinition.m | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/ExperPort/Plugins/@sessionmodel2/SessionDefinition.m b/ExperPort/Plugins/@sessionmodel2/SessionDefinition.m index 6ce32d45..fc9a49ec 100644 --- a/ExperPort/Plugins/@sessionmodel2/SessionDefinition.m +++ b/ExperPort/Plugins/@sessionmodel2/SessionDefinition.m @@ -1956,10 +1956,13 @@ return_structure = value(GLOBAL_HELPER_VAR_NAME_LIST); if ~isempty(return_structure) - for ctr = 1:length(return_structure) - assert(logical(exist(return_structure(ctr).var_name, 'var')) && isa(eval(return_structure(ctr).var_name), 'SoloParamHandle')) - return_structure(ctr).current_value = value(eval(return_structure(ctr).var_name)); - return_structure(ctr).history = get_history(eval(return_structure(ctr).var_name)); + try + for ctr = 1:length(return_structure) + assert(logical(exist(return_structure(ctr).var_name, 'var')) && isa(eval(return_structure(ctr).var_name), 'SoloParamHandle')) + return_structure(ctr).current_value = value(eval(return_structure(ctr).var_name)); + return_structure(ctr).history = get_history(eval(return_structure(ctr).var_name)); + end + catch end end varargout{1} = return_structure; From 6c2d0c00b726c76c502727496a6118c76f0c30b0 Mon Sep 17 00:00:00 2001 From: Arpit Date: Fri, 2 May 2025 15:53:27 +0100 Subject: [PATCH 074/164] added control button for camera --- .../ArpitCentrePokeTraining.m | 16 +++++++++++++++ .../Connect_Bonsai_Camera.m | 20 ++++++++++++++++++- .../@ArpitCentrePokeTraining/ParamsSection.m | 2 +- .../Training_ParamsSection.m | 4 ++-- 4 files changed, 38 insertions(+), 4 deletions(-) diff --git a/Protocols/@ArpitCentrePokeTraining/ArpitCentrePokeTraining.m b/Protocols/@ArpitCentrePokeTraining/ArpitCentrePokeTraining.m index 53ff96a2..f2c1131a 100644 --- a/Protocols/@ArpitCentrePokeTraining/ArpitCentrePokeTraining.m +++ b/Protocols/@ArpitCentrePokeTraining/ArpitCentrePokeTraining.m @@ -186,6 +186,13 @@ HeaderParam(obj, 'prot_title', [mfilename ': ' expmtr ', ' rname], x, y, 'position', [10 figpos(4)-25, 800 20]); [x, y] = StimulusSection(obj,'init',x,y); + + ToggleParam(obj, 'Connect_Camera', 1, x,y,... + 'OnString', 'Camera On',... + 'OffString', 'Camera Off',... + 'TooltipString', sprintf('If on (black) then it enables to start the camera \n',... + 'Only press if camera does not start on its own.')); + set_callback(Connect_Camera, {mfilename, 'camera_control'}); ArpitCentrePokeTrainingSMA(obj, 'init'); @@ -194,6 +201,7 @@ %% Before preparing the trial, start with the Bonsai app to control the USB based Camera % Declare the folder location for saving the video files + current_dir = cd; ratter_dir = extractBefore(current_dir,'ratter'); main_dir_video = [ratter_dir 'ratter_Videos']; @@ -224,6 +232,14 @@ feval(mfilename, obj, 'prepare_next_trial'); %% change_water_modulation_params + case 'camera_control' + + if value(Connect_Camera) == 1 + Connect_Bonsai_Camera(obj,'start'); + else + Connect_Bonsai_Camera(obj,'stop'); + end + case 'change_water_modulation_params' display_guys = [1 150 300]; for i=1:numel(display_guys) diff --git a/Protocols/@ArpitCentrePokeTraining/Connect_Bonsai_Camera.m b/Protocols/@ArpitCentrePokeTraining/Connect_Bonsai_Camera.m index dbb83eea..ade83d68 100644 --- a/Protocols/@ArpitCentrePokeTraining/Connect_Bonsai_Camera.m +++ b/Protocols/@ArpitCentrePokeTraining/Connect_Bonsai_Camera.m @@ -54,7 +54,8 @@ function Connect_Bonsai_Camera(obj,action) % system([command, ' &']); runBonsaiWorkflow(bonsai_workflow_Path); - pause(3); + + pause(5); % Before starting the streaming of Camera, I need to send the % file directory for saving the file otherwise Bonsai can run into % error as it will try saving files in the predefined folder in @@ -83,6 +84,23 @@ function Connect_Bonsai_Camera(obj,action) oscMsg_file_directory = createOSCMessage(recording_command_address, [value(Video_Saving_Folder) '\Trial.avi']); write(value(UDPSender), oscMsg_file_directory, "uint8", bonsaiComputerIP,bonsaiUdpPort); + case 'start' + + oscMsg_file_directory = createOSCMessage(recording_command_address, [value(Video_Saving_Folder) '\Trial.avi']); + % write(udpSender, oscMsg_file_directory, "uint8", bonsaiComputerIP,bonsaiUdpPort); + + % OSC message to start the camera + oscMsg_Camera_start = createOSCMessage(camera_command_address, startCommand); + % the command to send message to Bonsai + write(udpSender, oscMsg_Camera_start, "uint8", bonsaiComputerIP,bonsaiUdpPort); + + pause(3); + % NOTE: Ideally I should start saving the trials once the experimenter presses + % Run either on dispatcher or Runrats. But, I dont want to make the changes there + % so would start recording as soon as the protocol is loaded and camera starts streaming + + write(udpSender, oscMsg_file_directory, "uint8", bonsaiComputerIP,bonsaiUdpPort); + case 'stop' % this stops saving and streaming of the camera diff --git a/Protocols/@ArpitCentrePokeTraining/ParamsSection.m b/Protocols/@ArpitCentrePokeTraining/ParamsSection.m index d5f16703..0490a75a 100644 --- a/Protocols/@ArpitCentrePokeTraining/ParamsSection.m +++ b/Protocols/@ArpitCentrePokeTraining/ParamsSection.m @@ -39,7 +39,7 @@ NumeditParam(obj, 'violation_iti', 1, x,y,'label','Violation Timeout','TooltipString','Center poke violation duration'); % Reward Collection next_row(y); - NumeditParam(obj, 'RewardCollection_duration', 6, x,y,'label','RewardCollection_dur','TooltipString','Wait until rat collects the reward else a timeout'); + NumeditParam(obj, 'RewardCollection_duration', 300, x,y,'label','RewardCollection_dur','TooltipString','Wait until rat collects the reward else a timeout'); next_row(y); NumeditParam(obj, 'SideLed_duration', 1, x,y,'label','Side LED duration','TooltipString','Duration of SideLed'); next_row(y); diff --git a/Protocols/@ArpitCentrePokeTraining/Training_ParamsSection.m b/Protocols/@ArpitCentrePokeTraining/Training_ParamsSection.m index 82ccf177..7cbc3a8d 100644 --- a/Protocols/@ArpitCentrePokeTraining/Training_ParamsSection.m +++ b/Protocols/@ArpitCentrePokeTraining/Training_ParamsSection.m @@ -25,8 +25,8 @@ case 2 % STAGE RUNNING PARAMETERS - NumeditParam(obj, 'max_rColl_dur', 15, x, y,'label','T_Max_RCollect','TooltipString','User given max water collect time(code will optimize for value below this)'); next_row(y); - NumeditParam(obj, 'min_rColl_dur', 5, x, y,'label','T_Min_RCollect','TooltipString','User given min water collect time(code will optimize for value above this)'); next_row(y); + NumeditParam(obj, 'max_rColl_dur', 300, x, y,'label','T_Max_RCollect','TooltipString','User given max water collect time(code will optimize for value below this)'); next_row(y); + NumeditParam(obj, 'min_rColl_dur', 100, x, y,'label','T_Min_RCollect','TooltipString','User given min water collect time(code will optimize for value above this)'); next_row(y); SubheaderParam(obj, 'title', 'Stage Params', x, y); next_row(y); % COMPLETION TEST PARAMETERS NumeditParam(obj, 'total_trials', 1000, x, y,'label','Trials','TooltipString','total trials in this stage for its completion'); next_row(y); From f08662fe3ace9d385087bb0d7d54dc406eac6115 Mon Sep 17 00:00:00 2001 From: Arpit Date: Fri, 2 May 2025 16:00:33 +0100 Subject: [PATCH 075/164] debug --- .../ArpitCentrePokeTraining.m | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/Protocols/@ArpitCentrePokeTraining/ArpitCentrePokeTraining.m b/Protocols/@ArpitCentrePokeTraining/ArpitCentrePokeTraining.m index f2c1131a..eeb1bddc 100644 --- a/Protocols/@ArpitCentrePokeTraining/ArpitCentrePokeTraining.m +++ b/Protocols/@ArpitCentrePokeTraining/ArpitCentrePokeTraining.m @@ -171,6 +171,14 @@ [x, y] = PokesPlotSection(obj, 'init', x, y); next_row(y); [x, y] = CommentsSection(obj, 'init', x, y); + next_row(y); + ToggleParam(obj, 'Connect_Camera', 1, x,y,... + 'OnString', 'Camera On',... + 'OffString', 'Camera Off',... + 'TooltipString', sprintf('If on (black) then it enables to start the camera \n',... + 'Only press if camera does not start on its own.')); + set_callback(Connect_Camera, {mfilename, 'camera_control'}); + next_column(x); y=5; [x, y] = SessionPerformanceSection(obj, 'init', x, y); [x, y] = ParamsSection(obj, 'init', x, y); %#ok @@ -187,13 +195,6 @@ [x, y] = StimulusSection(obj,'init',x,y); - ToggleParam(obj, 'Connect_Camera', 1, x,y,... - 'OnString', 'Camera On',... - 'OffString', 'Camera Off',... - 'TooltipString', sprintf('If on (black) then it enables to start the camera \n',... - 'Only press if camera does not start on its own.')); - set_callback(Connect_Camera, {mfilename, 'camera_control'}); - ArpitCentrePokeTrainingSMA(obj, 'init'); next_row(y); next_row(y); From 97da425fc79d600185550aaa6eb4d37e2993b158 Mon Sep 17 00:00:00 2001 From: Arpit Date: Fri, 2 May 2025 16:21:57 +0100 Subject: [PATCH 076/164] debug --- Protocols/@ArpitCentrePokeTraining/Connect_Bonsai_Camera.m | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Protocols/@ArpitCentrePokeTraining/Connect_Bonsai_Camera.m b/Protocols/@ArpitCentrePokeTraining/Connect_Bonsai_Camera.m index ade83d68..bfe913ac 100644 --- a/Protocols/@ArpitCentrePokeTraining/Connect_Bonsai_Camera.m +++ b/Protocols/@ArpitCentrePokeTraining/Connect_Bonsai_Camera.m @@ -92,14 +92,14 @@ function Connect_Bonsai_Camera(obj,action) % OSC message to start the camera oscMsg_Camera_start = createOSCMessage(camera_command_address, startCommand); % the command to send message to Bonsai - write(udpSender, oscMsg_Camera_start, "uint8", bonsaiComputerIP,bonsaiUdpPort); + write(value(UDPSender), oscMsg_Camera_start, "uint8", bonsaiComputerIP,bonsaiUdpPort); pause(3); % NOTE: Ideally I should start saving the trials once the experimenter presses % Run either on dispatcher or Runrats. But, I dont want to make the changes there % so would start recording as soon as the protocol is loaded and camera starts streaming - write(udpSender, oscMsg_file_directory, "uint8", bonsaiComputerIP,bonsaiUdpPort); + write(value(UDPSender), oscMsg_file_directory, "uint8", bonsaiComputerIP,bonsaiUdpPort); case 'stop' From 96e77bee4d5fba12c0b61d435731d5738fa143ef Mon Sep 17 00:00:00 2001 From: Arpit Date: Fri, 2 May 2025 17:17:55 +0100 Subject: [PATCH 077/164] added control for reward sound in early training stages --- .../ArpitCentrePokeTrainingSMA.m | 9 +++++++-- Protocols/@ArpitCentrePokeTraining/ParamsSection.m | 12 +++++++++++- 2 files changed, 18 insertions(+), 3 deletions(-) diff --git a/Protocols/@ArpitCentrePokeTraining/ArpitCentrePokeTrainingSMA.m b/Protocols/@ArpitCentrePokeTraining/ArpitCentrePokeTrainingSMA.m index 914586c5..59a18e56 100644 --- a/Protocols/@ArpitCentrePokeTraining/ArpitCentrePokeTrainingSMA.m +++ b/Protocols/@ArpitCentrePokeTraining/ArpitCentrePokeTrainingSMA.m @@ -109,8 +109,13 @@ sma = add_scheduled_wave(sma, 'name', 'CP_Duration_wave', 'preamble', CP_duration - SettlingIn_time); % total length of centre poke minus the inital fidgety time to consider success end - sma = add_scheduled_wave(sma, 'name', 'Go_Cue', 'preamble', 0.001, ... - 'sustain', go_cue_duration, 'sound_trig', go_sound_id); % to play the Go Cue/Reward Sound + if value(Go_Sound) == 1 + sma = add_scheduled_wave(sma, 'name', 'Go_Cue', 'preamble', 0.001, ... + 'sustain', go_cue_duration, 'sound_trig', go_sound_id); % to play the Go Cue/Reward Sound + else + sma = add_scheduled_wave(sma, 'name', 'Go_Cue', 'preamble', 0.001, ... + 'sustain', go_cue_duration); % to play the Go Cue/Reward Sound + end % scheduled wave for rewarded side either of the side sma = add_scheduled_wave(sma, 'name', 'reward_delivery', 'preamble', reward_delay, ... diff --git a/Protocols/@ArpitCentrePokeTraining/ParamsSection.m b/Protocols/@ArpitCentrePokeTraining/ParamsSection.m index 0490a75a..157a18bf 100644 --- a/Protocols/@ArpitCentrePokeTraining/ParamsSection.m +++ b/Protocols/@ArpitCentrePokeTraining/ParamsSection.m @@ -92,6 +92,9 @@ DispParam(obj, 'Total_CP_duration', CP_duration+time_go_cue, x, y, 'TooltipString', 'Total expected(rat can poke out anytime after Go cue onset) nose in center port time, in secs. Sum of CP_duration and Go Cue duration'); %#ok<*NODEF> next_row(y); + ToggleParam(obj, 'Go_Sound', 1, x, y, 'OnString', 'Play Reward Sound', 'OffString', 'No Reward Sound','TooltipString', ... + 'If 1 (black), sound is played for intial 2 stages of light chasing; if 0 (brown), leave sound off'); + next_row(y); ToggleParam(obj, 'stimuli_on', 0, x,y,... 'OnString', 'Use Stimuli',... 'OffString', 'Fixed Sound',... @@ -162,7 +165,7 @@ 'legal_cbreak' ; 'SettlingIn_time'; 'time_go_cue'; ... 'A1_time';'time_bet_aud1_gocue' ; 'PreStim_time'; 'drink_time';'reward_delay';'antibias_wtr_mult';... - 'cp_timeout';'timeout_iti';'violation_iti'}); + 'cp_timeout';'timeout_iti';'violation_iti';'Go_Sound'}); SoloFunctionAddVars('SessionPerformanceSection', 'ro_args', ... @@ -291,6 +294,13 @@ end case 'prepare_next_trial' + + % change the reward collection duration once we start with centre + % poke + if value(training_stage) >= 3 + RewardCollection_duration.value = 6; + Go_Sound.value = 1; + end if value(training_stage) == 8 % user setting From 415382cc4f6e3ebed3dda6dda32fa4145c7380f3 Mon Sep 17 00:00:00 2001 From: Arpit Date: Sun, 4 May 2025 13:25:18 +0100 Subject: [PATCH 078/164] error debug --- .../ArpitCentrePokeTraining.m | 11 +++-------- .../SessionPerformanceSection.m | 2 +- 2 files changed, 4 insertions(+), 9 deletions(-) diff --git a/Protocols/@ArpitCentrePokeTraining/ArpitCentrePokeTraining.m b/Protocols/@ArpitCentrePokeTraining/ArpitCentrePokeTraining.m index eeb1bddc..5857e167 100644 --- a/Protocols/@ArpitCentrePokeTraining/ArpitCentrePokeTraining.m +++ b/Protocols/@ArpitCentrePokeTraining/ArpitCentrePokeTraining.m @@ -117,10 +117,6 @@ % Let's put the figure where we want it and give it a reasonable size: set(value(myfig), 'Position', [485 144 850 680]); - SoloParamHandle(obj, 'nsessions_healthy_number_of_pokes', 'value', 0, 'save_with_settings', 1); - SoloParamHandle(obj, 'post_DelComp_protocol', 'value', '', 'save_with_settings', 1); - SoloParamHandle(obj, 'post_DelComp_settings_filename', 'value', '', 'save_with_settings', 1); - SoloParamHandle(obj, 'violation_history', 'value', []); DeclareGlobals(obj, 'ro_args', {'violation_history'}); SoloFunctionAddVars('ParamsSection', 'rw_args', 'violation_history'); @@ -135,7 +131,7 @@ SoloParamHandle(obj, 'hit_history', 'value', []); DeclareGlobals(obj, 'ro_args', {'hit_history'}); - SoloFunctionAddVars('SideSection', 'rw_args', 'hit_history'); + SoloFunctionAddVars('ParamsSection', 'rw_args', 'hit_history'); SoundManagerSection(obj, 'init'); x = 5; y = 5; % Initial position on main GUI window @@ -254,9 +250,8 @@ case 'prepare_next_trial' ParamsSection(obj, 'prepare_next_trial'); - % Run SessionDefinition *after* ParamsSection so we know whether the trial was a violation or not - - % push_helper_vars_tosql(obj,n_done_trials); + + % push_helper_vars_tosql(obj,n_completed_trials); SessionDefinition(obj, 'next_trial'); diff --git a/Protocols/@ArpitCentrePokeTraining/SessionPerformanceSection.m b/Protocols/@ArpitCentrePokeTraining/SessionPerformanceSection.m index 2d8ce66e..1f9b8c6c 100644 --- a/Protocols/@ArpitCentrePokeTraining/SessionPerformanceSection.m +++ b/Protocols/@ArpitCentrePokeTraining/SessionPerformanceSection.m @@ -1,6 +1,6 @@ % [x, y] = SessionPerformanceSection(obj, action, x,y) % -% Reports overall performance. Uses training_stage from SideSection. +% Reports overall performance. Uses training_stage from ParamsSection. % % PARAMETERS: % ----------- From 375f44ba83711ac458a60afe38fe02b14b8b437c Mon Sep 17 00:00:00 2001 From: Arpit Date: Mon, 5 May 2025 18:25:39 +0100 Subject: [PATCH 079/164] added new window to see training performance --- .../Plugins/@sessionmodel2/CreateHelperVar.m | 7 +- .../ArpitCentrePokeTraining.m | 12 +- .../ArpitCentrePokeTrainingSMA.m | 14 +-- .../@ArpitCentrePokeTraining/ParamsSection.m | 4 +- .../Training_ParamsSection.m | 9 +- .../Training_Performance_Summary.m | 116 ++++++++++++++++++ 6 files changed, 140 insertions(+), 22 deletions(-) create mode 100644 Protocols/@ArpitCentrePokeTraining/Training_Performance_Summary.m diff --git a/ExperPort/Plugins/@sessionmodel2/CreateHelperVar.m b/ExperPort/Plugins/@sessionmodel2/CreateHelperVar.m index ebc28586..51a6a937 100644 --- a/ExperPort/Plugins/@sessionmodel2/CreateHelperVar.m +++ b/ExperPort/Plugins/@sessionmodel2/CreateHelperVar.m @@ -34,7 +34,12 @@ function CreateHelperVar(obj, varname, varargin) 'var_value', []}; parseargs(varargin, pairs); varval = var_value; -% clear('var_value'); + +% OLD CODE +% pairs = {'force_init', false; +% 'value', []}; +% parseargs(varargin, pairs); +% varval = value; clear('value'); %% diff --git a/Protocols/@ArpitCentrePokeTraining/ArpitCentrePokeTraining.m b/Protocols/@ArpitCentrePokeTraining/ArpitCentrePokeTraining.m index 5857e167..3fe3079e 100644 --- a/Protocols/@ArpitCentrePokeTraining/ArpitCentrePokeTraining.m +++ b/Protocols/@ArpitCentrePokeTraining/ArpitCentrePokeTraining.m @@ -176,6 +176,7 @@ set_callback(Connect_Camera, {mfilename, 'camera_control'}); next_column(x); y=5; + [x,y] = Training_Performance_Summary(obj, 'init', x, y); [x, y] = SessionPerformanceSection(obj, 'init', x, y); [x, y] = ParamsSection(obj, 'init', x, y); %#ok [x, y] = SoundSection(obj,'init',x,y); @@ -226,7 +227,8 @@ %% - feval(mfilename, obj, 'prepare_next_trial'); + % feval(mfilename, obj, 'prepare_next_trial'); % Commented out because + %%% it is being also run by Runrats(while loading the protocol) %% change_water_modulation_params case 'camera_control' @@ -285,12 +287,10 @@ SessionPerformanceSection(obj, 'evaluate'); % Do any updates in the protocol that need doing: feval(mfilename, 'update'); - % And PokesPlot needs completing the trial: - PokesPlotSection(obj, 'trial_completed'); - + %% update case 'update' - PokesPlotSection(obj, 'update'); + % PokesPlotSection(obj, 'update'); %% close @@ -315,7 +315,7 @@ StimulusSection(obj,'hide'); SessionDefinition(obj, 'run_eod_logic_without_saving'); - perf = SessionPerformanceSection(obj, 'evaluate'); + % perf = SessionPerformanceSection(obj, 'evaluate'); cp_durs = ParamsSection(obj, 'get_cp_history'); [stim1dur] = ParamsSection(obj,'get_stimdur_history'); diff --git a/Protocols/@ArpitCentrePokeTraining/ArpitCentrePokeTrainingSMA.m b/Protocols/@ArpitCentrePokeTraining/ArpitCentrePokeTrainingSMA.m index 59a18e56..bcd9f8b8 100644 --- a/Protocols/@ArpitCentrePokeTraining/ArpitCentrePokeTrainingSMA.m +++ b/Protocols/@ArpitCentrePokeTraining/ArpitCentrePokeTrainingSMA.m @@ -129,18 +129,6 @@ case 1 % LEARNING THE REWARD SOUND ASSOCIATION -LEFT OR RIGHT LED ON -> POKE -> SOUND+REWARD GIVEN % INFINITE TIME AND CHANCES TO SELF CORRECT - % sma = add_state(sma, 'name', 'side_led_wait_RewardCollection', 'self_timer', 2, ... - % 'output_actions', {'DOut', SideLight}, ... - % 'input_to_statechange',{'Tup','hit_state'}); - % - % sma = add_state(sma,'name','second_hit_state','self_timer',3,... - % 'output_actions',{'DOut', SideLight},... - % 'input_to_statechange',{'Tup','hit_state'}); - % - % sma = add_state(sma,'name','hit_state','self_timer',0.01,... - % 'output_actions', {'SchedWaveTrig','reward_delivery+Go_Cue'},... - % 'input_to_statechange',{'Tup','drink_state'}); - sma = add_state(sma, 'name', 'side_led_wait_RewardCollection', 'self_timer', SideLed_duration + RewardCollection_duration, ... 'output_actions', {'DOut', SideLight}, ... 'input_to_statechange',{HitEvent,'hit_state';'Tup','side_led_wait_RewardCollection'; ErrorEvent,'second_hit_state'}); @@ -246,6 +234,8 @@ 'input_to_statechange',{'Tup','check_next_trial_ready'}); + sma = add_state(sma, 'name', 'violation_state'); + case {4,5,6,7,8} % STAGE 4 - LEARN TO NOSE POKE BEYOND SETTLING TIME WITH THE INTRODUCTION OF VIOLATION, THIS IS UNTIL CP = 1 SEC % STAGE 5 ONWARDS - THE STIMULI IS INTRODUCED FROM THE STAGE 5 ONWARDS diff --git a/Protocols/@ArpitCentrePokeTraining/ParamsSection.m b/Protocols/@ArpitCentrePokeTraining/ParamsSection.m index 157a18bf..132ded70 100644 --- a/Protocols/@ArpitCentrePokeTraining/ParamsSection.m +++ b/Protocols/@ArpitCentrePokeTraining/ParamsSection.m @@ -83,13 +83,13 @@ next_row(y); DispParam(obj, 'init_CP_duration', 0.01, x,y,'label','init_CP duration','TooltipString','Duration of Nose in Central Poke before Go cue starts (see Total_CP_duration)'); next_row(y); - DispParam(obj, 'CP_duration', PreStim_time+A1_time+time_bet_aud1_gocue, x,y,'label','CP duration','TooltipString','Duration of Nose in Central Poke before Go cue starts (see Total_CP_duration)'); + NumeditParam(obj, 'CP_duration', PreStim_time+A1_time+time_bet_aud1_gocue, x,y,'label','CP duration', 'TooltipString','Duration of Nose in Central Poke before Go cue starts (see Total_CP_duration)'); % set_callback(CP_duration, {mfilename, 'new_CP_duration'}); next_row(y); NumeditParam(obj, 'time_go_cue' ,0.2, x,y,'label','Go Cue Duration','TooltipString','duration of go cue (see Total_CP_duration)'); set_callback(time_go_cue, {mfilename, 'new_time_go_cue'}); next_row(y); - DispParam(obj, 'Total_CP_duration', CP_duration+time_go_cue, x, y, 'TooltipString', 'Total expected(rat can poke out anytime after Go cue onset) nose in center port time, in secs. Sum of CP_duration and Go Cue duration'); %#ok<*NODEF> + DispParam(obj, 'Total_CP_duration', CP_duration+time_go_cue, x, y,'save_with_settings', 1, 'TooltipString', 'Total expected(rat can poke out anytime after Go cue onset) nose in center port time, in secs. Sum of CP_duration and Go Cue duration'); %#ok<*NODEF> next_row(y); ToggleParam(obj, 'Go_Sound', 1, x, y, 'OnString', 'Play Reward Sound', 'OffString', 'No Reward Sound','TooltipString', ... diff --git a/Protocols/@ArpitCentrePokeTraining/Training_ParamsSection.m b/Protocols/@ArpitCentrePokeTraining/Training_ParamsSection.m index 7cbc3a8d..8b6c059e 100644 --- a/Protocols/@ArpitCentrePokeTraining/Training_ParamsSection.m +++ b/Protocols/@ArpitCentrePokeTraining/Training_ParamsSection.m @@ -16,7 +16,8 @@ case 1 % STAGE RUNNING PARAMETERS - % SubheaderParam(obj, 'title', 'Stage Params', x, y); + DispParam(obj, 'stages_trial_counter_oppSide', 0, x, y,'save_with_settings', 1,'TooltipString','total trials in opposide side this stage'); next_row(y); + SubheaderParam(obj, 'title', 'Stage Params', x, y); % next_row(y); % COMPLETION TEST PARAMETERS NumeditParam(obj, 'total_trials', 300, x, y,'label','Trials','TooltipString','total trials in this stage for its completion'); next_row(y); @@ -25,6 +26,7 @@ case 2 % STAGE RUNNING PARAMETERS + DispParam(obj, 'stages_trial_counter_oppSide', 0, x, y,'save_with_settings', 1,'TooltipString','total trials in opposide side this stage'); next_row(y); NumeditParam(obj, 'max_rColl_dur', 300, x, y,'label','T_Max_RCollect','TooltipString','User given max water collect time(code will optimize for value below this)'); next_row(y); NumeditParam(obj, 'min_rColl_dur', 100, x, y,'label','T_Min_RCollect','TooltipString','User given min water collect time(code will optimize for value above this)'); next_row(y); SubheaderParam(obj, 'title', 'Stage Params', x, y); next_row(y); @@ -35,12 +37,14 @@ case 3 % no completion test required % STAGE RUNNING PARAMETERS + DispParam(obj, 'last_session_CP', 0, x, y,'save_with_settings', 1,'TooltipString','total cp reached by last session'); next_row(y); DispParam(obj, 'max_CP', 0.3, x, y,'label','CP_Dur_Max','TooltipString','max CP duration being trained in this stage'); next_row(y); NumeditParam(obj, 'CPfraction_inc', 0.001, x, y,'label','CP_frac_Increase','TooltipString','CP duration is increased by this fraction'); next_row(y); SubheaderParam(obj, 'title', 'Stage Params', x, y); next_row(y); case 4 % STAGE RUNNING PARAMETERS + DispParam(obj, 'last_session_CP', 0, x, y,'save_with_settings', 1,'TooltipString','total cp reached by last session'); next_row(y); NumeditParam(obj, 'max_CP', 1.5, x, y,'label','CP_Dur_Max','TooltipString','max CP duration being trained in this stage'); next_row(y); NumeditParam(obj, 'CPfraction_inc', 0.001, x, y,'label','CP_frac_Increase','TooltipString','CP duration is increased by this fraction'); next_row(y); SubheaderParam(obj, 'title', 'Stage Params', x, y); next_row(y); @@ -52,6 +56,7 @@ case 5 % STAGE RUNNING PARAMETERS + DispParam(obj, 'last_session_CP', 0, x, y,'save_with_settings', 1,'TooltipString','total cp reached by last session'); next_row(y); NumeditParam(obj, 'max_CP', 5, x, y,'label','CP_Dur_Max','TooltipString','max CP duration being trained in this stage'); next_row(y); NumeditParam(obj, 'CPfraction_inc', 0.002, x, y,'label','CP_frac_Increase','TooltipString','CP duration is increased by this fraction'); next_row(y); NumeditParam(obj, 'min_CP', 1.5, x, y,'label','CP_Dur_Min','TooltipString','min CP duration being trained in this stage'); next_row(y); @@ -68,6 +73,7 @@ case 6 % STAGE RUNNING PARAMETERS + DispParam(obj, 'last_session_CP', 0, x, y,'save_with_settings', 1,'TooltipString','total cp reached by last session'); next_row(y); NumeditParam(obj, 'max_CP', 5, x, y,'label','CP_Dur_Max','TooltipString','max CP duration being trained in this stage'); next_row(y); NumeditParam(obj, 'starting_CP', 0.3, x, y,'TooltipString','min CP duration (minus the settling-in time) during warm up'); next_row(y); NumeditParam(obj, 'warm_up_trials', 20, x, y,'label','Warmup Trials','TooltipString','N trials for warmup'); next_row(y); @@ -84,6 +90,7 @@ case 7 % STAGE RUNNING PARAMETERS + DispParam(obj, 'last_session_CP', 0, x, y,'save_with_settings', 1,'TooltipString','total cp reached by last session'); next_row(y); NumeditParam(obj, 'max_CP', 5, x, y,'label','CP_Dur_Max','TooltipString','max CP duration being trained in this stage'); next_row(y); NumeditParam(obj, 'starting_CP', 0.3, x, y,'TooltipString','min CP duration (minus the settling-in time) during warm up'); next_row(y); NumeditParam(obj, 'warm_up_trials', 20, x, y,'label','Warmup Trials','TooltipString','N trials for warmup'); next_row(y); diff --git a/Protocols/@ArpitCentrePokeTraining/Training_Performance_Summary.m b/Protocols/@ArpitCentrePokeTraining/Training_Performance_Summary.m new file mode 100644 index 00000000..ed0e7084 --- /dev/null +++ b/Protocols/@ArpitCentrePokeTraining/Training_Performance_Summary.m @@ -0,0 +1,116 @@ +function [x, y] = Training_Performance_Summary(obj, action, varargin) + +GetSoloFunctionArgs(obj); + +switch action + + % ------------------------------------------------------------------ + % INIT + % ------------------------------------------------------------------ + + case 'init' + if length(varargin) < 2 + error('Need at least two arguments, x and y position, to initialize %s', mfilename); + end + x = varargin{1}; y = varargin{2}; + + ToggleParam(obj, 'SummaryShow', 1, x, y, 'OnString', 'Summary Show', ... + 'OffString', 'Summary Hidden', 'TooltipString', 'Show/Hide Summary panel'); + set_callback(SummaryShow, {mfilename, 'show_hide'}); %#ok (Defined just above) + next_row(y); + + SoloParamHandle(obj, 'myfig', 'value', figure('closerequestfcn', [mfilename '(' class(obj) ', ''hide'');'], 'MenuBar', 'none', ... + 'Name', mfilename), 'saveable', 0); + screen_size = get(0, 'ScreenSize'); + set(value(myfig),'Position',[1 screen_size(4)-740, 400 400]); % put fig at top right + % set(gcf, 'Visible', 'off'); + + SoloParamHandle(obj, 'h1', 'value', []); + + x = 10; y=5; + + % Create stage names + N_Stages = 8; + for i = 1:N_Stages + rowNames{i, 1} = sprintf('Stage %d', i); + end + % Create column names + N_params = 5; + columnNames = {'Stage/Param','Trials','TrialsToday','TrialsValid','ViolationRate','TimeoutRate'}; + + % Create Variable Names for each Edit Box + count = 0; + for j = 1:N_params + for i = 1:N_Stages + count = count + 1; + variable_names(count) = sprintf('stage_%i_%s',i,columnNames{j+1}); + end + end + + count = 0; + + for column_n = 1 : N_params + 1 + for rows_n = 1 : N_Stages + 1 + if column_n == 1 && rows_n ~= N_Stages + 1 + SubheaderParam(obj, 'title', sprintf('Stage %i',(N_stages - column_n + 1)), x, y); + next_row(y); + end + if column_n == 1 && rows_n == N_Stages + 1 + SubheaderParam(obj, 'title', columnNames{1}, x, y); + next_row(y); + end + if column_n ~= 1 && rows_n ~= N_Stages + 1 + count = count + 1; + NumeditParam(obj, variable_names{count}, 0, x, y); + next_row(y); + end + if rows_n == N_Stages + 1 + SubheaderParam(obj, 'title', columnNames{column_n+1}, x, y); + next_row(y); + end + end + next_column(x); + end + + +%% Case close + case 'close' + set(value(myfig), 'Visible', 'off'); + set(value(stim_dist_fig), 'Visible', 'off'); + % Delete all SoloParamHandles who belong to this object and whose + % fullname starts with the name of this mfile: + if exist('myfig', 'var') && isa(myfig, 'SoloParamHandle') && ishandle(value(myfig)) %#ok + delete(value(myfig)); + end + if exist('stim_dist_fig', 'var') && isa(stim_dist_fig, 'SoloParamHandle') && ishandle(value(stim_dist_fig)) %#ok + delete(value(stim_dist_fig)); + end + delete_sphandle('owner', ['^@' class(obj) '$'], ... + 'fullname', ['^' mfilename]); + + %% Case hide + case 'hide' + SummaryShow.value = 0; + set(value(myfig), 'Visible', 'off'); + set(value(stim_dist_fig), 'Visible', 'off'); + + %% Case show + case 'show' + SummaryShow.value = 1; + set(value(myfig), 'Visible', 'on'); + set(value(stim_dist_fig), 'Visible', 'on'); + + %% Case Show_hide + case 'show_hide' + if SummaryShow == 1 + set(value(myfig), 'Visible', 'on'); + set(value(stim_dist_fig), 'Visible', 'on');%#ok (defined by GetSoloFunctionArgs) + else + set(value(myfig), 'Visible', 'off'); + set(value(stim_dist_fig), 'Visible', 'off'); + end + +end + +end + From f2956f32466dd562bf53f45c5a5e56b3299b036c Mon Sep 17 00:00:00 2001 From: viktorpm Date: Tue, 6 May 2025 10:03:11 +0100 Subject: [PATCH 080/164] working version of summary section --- .../ArpitCentrePokeTraining.m | 37 ++++++++------ .../@ArpitCentrePokeTraining/ParamsSection.m | 10 ++-- .../StimulusSection.m | 14 +++-- .../Training_Performance_Summary.m | 51 +++++++++---------- 4 files changed, 60 insertions(+), 52 deletions(-) diff --git a/Protocols/@ArpitCentrePokeTraining/ArpitCentrePokeTraining.m b/Protocols/@ArpitCentrePokeTraining/ArpitCentrePokeTraining.m index 3fe3079e..f812a9e3 100644 --- a/Protocols/@ArpitCentrePokeTraining/ArpitCentrePokeTraining.m +++ b/Protocols/@ArpitCentrePokeTraining/ArpitCentrePokeTraining.m @@ -162,40 +162,47 @@ %AthenaSMA changed to SoundCatSMA (From AthenaDelayComp) SoloFunctionAddVars('ParamsSection', 'ro_args', ... {'maxasymp';'slp';'inflp';'minasymp';'assym'}); + + figpos = get(double(gcf), 'Position'); + [expmtr, rname]=SavingSection(obj, 'get_info'); + HeaderParam(obj, 'prot_title', [mfilename ': ' expmtr ', ' rname], x, y, 'position', [10 figpos(4)-25, 800 20]); - [x, y] = WaterValvesSection(obj, 'init', x, y); + [x, y] = WaterValvesSection(obj, 'init', x, y); next_row(y); [x, y] = PokesPlotSection(obj, 'init', x, y); next_row(y); [x, y] = CommentsSection(obj, 'init', x, y); next_row(y); - ToggleParam(obj, 'Connect_Camera', 1, x,y,... + oldx=x; oldy=y; + + next_column(x); y=5; + + [x, y] = ParamsSection(obj, 'init', x, y); %#ok + [x, y] = SoundSection(obj,'init',x,y); + [x, y] = StimulusSection(obj,'init',x,y); + + next_row(y);next_row(y); + [x, y] = SessionPerformanceSection(obj, 'init', x, y); + [x, y] = Training_Performance_Summary(obj, 'init', x, y); + + next_row(y);next_row(y); + ToggleParam(obj, 'Connect_Camera', 1, x,y,... 'OnString', 'Camera On',... 'OffString', 'Camera Off',... 'TooltipString', sprintf('If on (black) then it enables to start the camera \n',... 'Only press if camera does not start on its own.')); set_callback(Connect_Camera, {mfilename, 'camera_control'}); - next_column(x); y=5; - [x,y] = Training_Performance_Summary(obj, 'init', x, y); - [x, y] = SessionPerformanceSection(obj, 'init', x, y); - [x, y] = ParamsSection(obj, 'init', x, y); %#ok - [x, y] = SoundSection(obj,'init',x,y); + next_column(x); y=5; [stage_fig_x,stage_fig_y] = Training_ParamsSection(obj, 'init', x, y); SoloParamHandle(obj, 'stage_fig_x', 'value', stage_fig_x); SoloFunctionAddVars('ParamsSection', 'rw_args', 'stage_fig_x'); SoloParamHandle(obj, 'stage_fig_y', 'value', stage_fig_y); SoloFunctionAddVars('ParamsSection', 'rw_args', 'stage_fig_y'); - figpos = get(double(gcf), 'Position'); - [expmtr, rname]=SavingSection(obj, 'get_info'); - HeaderParam(obj, 'prot_title', [mfilename ': ' expmtr ', ' rname], x, y, 'position', [10 figpos(4)-25, 800 20]); - - [x, y] = StimulusSection(obj,'init',x,y); - ArpitCentrePokeTrainingSMA(obj, 'init'); - next_row(y); next_row(y); - SessionDefinition(obj, 'init', x, y, value(myfig)); next_row(y, 2); %#ok + x=oldx; y=oldy; + SessionDefinition(obj, 'init', x, y, value(myfig)); %#ok %% Before preparing the trial, start with the Bonsai app to control the USB based Camera % Declare the folder location for saving the video files diff --git a/Protocols/@ArpitCentrePokeTraining/ParamsSection.m b/Protocols/@ArpitCentrePokeTraining/ParamsSection.m index 132ded70..5068f463 100644 --- a/Protocols/@ArpitCentrePokeTraining/ParamsSection.m +++ b/Protocols/@ArpitCentrePokeTraining/ParamsSection.m @@ -12,8 +12,6 @@ SoloParamHandle(obj, 'my_gui_info', 'value', [x y double(gcf)], 'saveable', 0); y0 = y; - - next_row(y); NumeditParam(obj, 'LeftProb', 0.5, x, y); next_row(y); MenuParam(obj, 'MaxSame', {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, Inf}, Inf, x, y, ... 'TooltipString', sprintf(['\nMaximum number of consecutive trials where correct\n' ... @@ -27,9 +25,9 @@ SubheaderParam(obj, 'title', 'Params Section', x, y); - next_row(y, 1.5); - next_column(x); y = 5; - next_row(y); + % next_row(y, 1.5); + % next_column(x); y = 5; + % next_row(y); NumeditParam(obj, 'reward_delay', 0.01, x,y,'label','Reward Delay','TooltipString','Delay between side poke and reward delivery'); next_row(y); NumeditParam(obj, 'drink_time', 1, x,y,'label','Drink Time','TooltipString','waits to finish water delivery'); @@ -298,7 +296,7 @@ % change the reward collection duration once we start with centre % poke if value(training_stage) >= 3 - RewardCollection_duration.value = 6; + RewardCollection_duration.value = 30; Go_Sound.value = 1; end diff --git a/Protocols/@ArpitCentrePokeTraining/StimulusSection.m b/Protocols/@ArpitCentrePokeTraining/StimulusSection.m index c6776a0e..8dd8a940 100644 --- a/Protocols/@ArpitCentrePokeTraining/StimulusSection.m +++ b/Protocols/@ArpitCentrePokeTraining/StimulusSection.m @@ -16,16 +16,18 @@ end x = varargin{1}; y = varargin{2}; - ToggleParam(obj, 'StimulusShow', 1, x, y, 'OnString', 'Stimuli Show', ... + ToggleParam(obj, 'StimulusShow', 0, x, y, 'OnString', 'Stimuli Show', ... 'OffString', 'Stimuli Hidden', 'TooltipString', 'Show/Hide Stimulus panel'); set_callback(StimulusShow, {mfilename, 'show_hide'}); %#ok (Defined just above) next_row(y); - SoloParamHandle(obj, 'myfig', 'value', figure('closerequestfcn', [mfilename '(' class(obj) ', ''hide'');'], 'MenuBar', 'none', ... - 'Name', mfilename), 'saveable', 0); + oldx=x; oldy=y; parentfig=double(gcf); + + SoloParamHandle(obj, 'myfig', 'value', figure('closerequestfcn', [mfilename '(' class(obj) ', ''hide'');'],... + 'MenuBar', 'none', 'Name', mfilename), 'saveable', 0); screen_size = get(0, 'ScreenSize'); set(value(myfig),'Position',[1 screen_size(4)-740, 400 400]); % put fig at top right - % set(gcf, 'Visible', 'off'); + set(double(gcf), 'Visible', 'off'); SoundManagerSection(obj, 'declare_new_sound', 'StimAUD1') SoloParamHandle(obj, 'thisstim', 'value', []); @@ -130,6 +132,7 @@ % next_column(y) SoloParamHandle(obj, 'stim_dist_fig', 'value', figure('closerequestfcn', [mfilename '(' class(obj) ', ''hide'');'], 'MenuBar', 'none', ... 'Name', 'StimulusPlot'), 'saveable', 0); + set(double(gcf), 'Visible', 'off'); ax = axes(value(stim_dist_fig),'Position',[0.1 0.1 0.8 0.8]); ylabel('log_e A','FontSize',16,'FontName','Cambria Math'); set(ax,'Fontsize',15) @@ -137,6 +140,9 @@ SoloParamHandle(obj, 'ax', 'saveable', 0, 'value', ax); StimulusSection(obj,'plot_stimuli'); + + x=oldx; y=oldy; + figure(parentfig); case 'prepare_next_trial' if value(training_stage) > 4 && stimuli_on diff --git a/Protocols/@ArpitCentrePokeTraining/Training_Performance_Summary.m b/Protocols/@ArpitCentrePokeTraining/Training_Performance_Summary.m index ed0e7084..beecda45 100644 --- a/Protocols/@ArpitCentrePokeTraining/Training_Performance_Summary.m +++ b/Protocols/@ArpitCentrePokeTraining/Training_Performance_Summary.m @@ -14,21 +14,20 @@ end x = varargin{1}; y = varargin{2}; - ToggleParam(obj, 'SummaryShow', 1, x, y, 'OnString', 'Summary Show', ... + % SoloParamHandle(obj, 'my_xyfig', 'value', [x y double(gcf)]); + + ToggleParam(obj, 'SummaryShow', 0, x, y, 'OnString', 'Summary Show', ... 'OffString', 'Summary Hidden', 'TooltipString', 'Show/Hide Summary panel'); set_callback(SummaryShow, {mfilename, 'show_hide'}); %#ok (Defined just above) next_row(y); + oldx=x; oldy=y; parentfig=double(gcf); - SoloParamHandle(obj, 'myfig', 'value', figure('closerequestfcn', [mfilename '(' class(obj) ', ''hide'');'], 'MenuBar', 'none', ... + SoloParamHandle(obj, 'myfig', 'value', figure('Position', [ 226 122 1406 400], ... + 'closerequestfcn', [mfilename '(' class(obj) ', ''hide'');'], 'MenuBar', 'none', ... 'Name', mfilename), 'saveable', 0); - screen_size = get(0, 'ScreenSize'); - set(value(myfig),'Position',[1 screen_size(4)-740, 400 400]); % put fig at top right - % set(gcf, 'Visible', 'off'); + set(double(gcf), 'Visible', 'off'); - SoloParamHandle(obj, 'h1', 'value', []); - - x = 10; y=5; - + % Create stage names N_Stages = 8; for i = 1:N_Stages @@ -40,51 +39,53 @@ % Create Variable Names for each Edit Box count = 0; + variable_names = cell(1,N_params * N_Stages); for j = 1:N_params for i = 1:N_Stages count = count + 1; - variable_names(count) = sprintf('stage_%i_%s',i,columnNames{j+1}); + variable_names(count) = {sprintf('stage_%i_%s',N_Stages - i + 1,columnNames{j+1})}; end end count = 0; - - for column_n = 1 : N_params + 1 + x = 100; y=100; + + for column_n = 1 : N_params + 1 for rows_n = 1 : N_Stages + 1 - if column_n == 1 && rows_n ~= N_Stages + 1 - SubheaderParam(obj, 'title', sprintf('Stage %i',(N_stages - column_n + 1)), x, y); + if column_n == 1 && rows_n < N_Stages + 1 + SubheaderParam(obj, 'title', sprintf('Stage%i',(N_Stages - rows_n + 1)), x, y); next_row(y); end if column_n == 1 && rows_n == N_Stages + 1 SubheaderParam(obj, 'title', columnNames{1}, x, y); next_row(y); end - if column_n ~= 1 && rows_n ~= N_Stages + 1 + if column_n > 1 && rows_n < N_Stages + 1 count = count + 1; NumeditParam(obj, variable_names{count}, 0, x, y); next_row(y); end - if rows_n == N_Stages + 1 - SubheaderParam(obj, 'title', columnNames{column_n+1}, x, y); + if rows_n == N_Stages + 1 && column_n > 1 + SubheaderParam(obj, 'title', columnNames{column_n}, x, y); next_row(y); end end - next_column(x); + next_column(x); y=100; end + % + x=oldx; y=oldy; + figure(parentfig); %% Case close case 'close' set(value(myfig), 'Visible', 'off'); - set(value(stim_dist_fig), 'Visible', 'off'); % Delete all SoloParamHandles who belong to this object and whose % fullname starts with the name of this mfile: if exist('myfig', 'var') && isa(myfig, 'SoloParamHandle') && ishandle(value(myfig)) %#ok delete(value(myfig)); end - if exist('stim_dist_fig', 'var') && isa(stim_dist_fig, 'SoloParamHandle') && ishandle(value(stim_dist_fig)) %#ok - delete(value(stim_dist_fig)); - end + delete_sphandle('owner', ['^@' class(obj) '$'], ... 'fullname', ['^' mfilename]); @@ -92,22 +93,18 @@ case 'hide' SummaryShow.value = 0; set(value(myfig), 'Visible', 'off'); - set(value(stim_dist_fig), 'Visible', 'off'); %% Case show case 'show' SummaryShow.value = 1; set(value(myfig), 'Visible', 'on'); - set(value(stim_dist_fig), 'Visible', 'on'); %% Case Show_hide case 'show_hide' if SummaryShow == 1 - set(value(myfig), 'Visible', 'on'); - set(value(stim_dist_fig), 'Visible', 'on');%#ok (defined by GetSoloFunctionArgs) + set(value(myfig), 'Visible', 'on'); else set(value(myfig), 'Visible', 'off'); - set(value(stim_dist_fig), 'Visible', 'off'); end end From c08eb20334e762be9c3468cf97591865a7645426 Mon Sep 17 00:00:00 2001 From: Arpit Date: Tue, 6 May 2025 17:20:03 +0100 Subject: [PATCH 081/164] updated the protocol to calculate the training performance --- .../ArpitCentrePokeTraining.m | 1 + ...ing_SessionDefinition_AutoTrainingStages.m | 476 +++++------- .../@ArpitCentrePokeTraining/ParamsSection.m | 3 + .../SessionPerformanceSection.m | 19 +- .../Training_ParamsSection.m | 9 +- .../Training_Performance_Summary.m | 165 ++++ .../ArpitSoundCalibration.asv | 733 ------------------ 7 files changed, 364 insertions(+), 1042 deletions(-) delete mode 100644 Protocols/@ArpitSoundCalibration/ArpitSoundCalibration.asv diff --git a/Protocols/@ArpitCentrePokeTraining/ArpitCentrePokeTraining.m b/Protocols/@ArpitCentrePokeTraining/ArpitCentrePokeTraining.m index f812a9e3..a7597c6c 100644 --- a/Protocols/@ArpitCentrePokeTraining/ArpitCentrePokeTraining.m +++ b/Protocols/@ArpitCentrePokeTraining/ArpitCentrePokeTraining.m @@ -291,6 +291,7 @@ % Change the video trial Connect_Bonsai_Camera(obj,'next_trial'); % Update the Metrics Calculated + Training_Performance_Summary(obj,'evaluate'); SessionPerformanceSection(obj, 'evaluate'); % Do any updates in the protocol that need doing: feval(mfilename, 'update'); diff --git a/Protocols/@ArpitCentrePokeTraining/ArpitCentrePokeTraining_SessionDefinition_AutoTrainingStages.m b/Protocols/@ArpitCentrePokeTraining/ArpitCentrePokeTraining_SessionDefinition_AutoTrainingStages.m index bc35643b..95c60b82 100644 --- a/Protocols/@ArpitCentrePokeTraining/ArpitCentrePokeTraining_SessionDefinition_AutoTrainingStages.m +++ b/Protocols/@ArpitCentrePokeTraining/ArpitCentrePokeTraining_SessionDefinition_AutoTrainingStages.m @@ -25,11 +25,6 @@ GetSoloFunctionArgs(obj); ClearHelperVarsNotOwned(obj); % -CreateHelperVar(obj,'stages_trial_counter','value',zeros(1,8)); -CreateHelperVar(obj,'stages_trial_counter_today','value',zeros(1,8)); -CreateHelperVar(obj,'stages_trial_counter_oppSide','value',zeros(1,8)); -CreateHelperVar(obj,'stages_timeout_rate','value',zeros(1,8)); -CreateHelperVar(obj,'stages_violation_rate','value',zeros(1,8)); CreateHelperVar(obj,'stage_start_completed_trial','value',n_completed_trials,'force_init',true); % end @@ -48,23 +43,6 @@ ParamsSection_training_stage.value = stage_no; callback(ParamsSection_training_stage); end -if n_completed_trials > value(stage_start_completed_trial) - this_stage_trial_counter = value(stages_trial_counter); - this_stage_trial_counter(stage_no) = this_stage_trial_counter(stage_no) + 1; - SessionPerformanceSection_ntrials_stage.value = this_stage_trial_counter(stage_no); - callback(SessionPerformanceSection_ntrials_stage); - stages_trial_counter.value = this_stage_trial_counter; - this_stage_trial_counter_today = value(stages_trial_counter_today); - this_stage_trial_counter_today(stage_no) = this_stage_trial_counter_today(stage_no) + 1; - SessionPerformanceSection_ntrials_stage_today.value = this_stage_trial_counter_today(stage_no); - callback(SessionPerformanceSection_ntrials_stage_today); - stages_trial_counter_today.value = this_stage_trial_counter_today; - this_stage_trial_counter_oppSide = value(stages_trial_counter_oppSide); - if value(previous_sides(end)) ~= value(ParamsSection_ThisTrial) - this_stage_trial_counter_oppSide(stage_no) = this_stage_trial_counter_oppSide(stage_no) + 1; - end - stages_trial_counter_oppSide.value = this_stage_trial_counter_oppSide; -end % end @@ -76,11 +54,10 @@ % if ParamsSection_use_auto_train % do completion check if auto training stage_no = value(SessionDefinition_CURRENT_ACTIVE_STAGE); - this_stage_trial_counter_oppSide = value(stages_trial_counter_oppSide); - this_stage_trial_counter = value(stages_trial_counter); % only run it if its the start of the day, number of trials is small if n_completed_trials < 100 - if this_stage_trial_counter(stage_no) > value(Training_ParamsSection_total_trials) && this_stage_trial_counter_oppSide(stage_no) > value(Training_ParamsSection_total_trials_opp) + if value(Training_Performance_Summary_stage_1_TrialsValid) > value(Training_ParamsSection_total_trials) && ... + value(Training_ParamsSection_trial_oppSide) > value(Training_ParamsSection_total_trials_opp) ParamsSection_training_stage.value = stage_no + 1; callback(ParamsSection_training_stage); ParamsSection(obj, 'Changed_Training_Stage'); @@ -102,15 +79,29 @@ ClearHelperVarsNotOwned(obj); % stage_no = value(SessionDefinition_CURRENT_ACTIVE_STAGE); -this_stage_trial_counter_oppSide = value(stages_trial_counter_oppSide); -this_stage_trial_counter = value(stages_trial_counter); -stages_trial_counter_today.value = zeros(1,8); -if this_stage_trial_counter(stage_no) > value(Training_ParamsSection_total_trials) && this_stage_trial_counter_oppSide(stage_no) > value(Training_ParamsSection_total_trials_opp) +if value(Training_Performance_Summary_stage_1_TrialsValid) > value(Training_ParamsSection_total_trials) && ... + value(Training_ParamsSection_trial_oppSide) > value(Training_ParamsSection_total_trials_opp) ParamsSection_training_stage.value = stage_no + 1; callback(ParamsSection_training_stage); ParamsSection(obj, 'Changed_Training_Stage'); SessionDefinition(obj, 'jump_to_stage', 'Timeout Rewarded Side Pokes'); end +Training_Performance_Summary_stage_1_TrialsToday.value = 0; +callback(Training_Performance_Summary_stage_1_TrialsToday); +Training_Performance_Summary_stage_2_TrialsToday.value = 0; +callback(Training_Performance_Summary_stage_2_TrialsToday); +Training_Performance_Summary_stage_3_TrialsToday.value = 0; +callback(Training_Performance_Summary_stage_3_TrialsToday); +Training_Performance_Summary_stage_4_TrialsToday.value = 0; +callback(Training_Performance_Summary_stage_4_TrialsToday); +Training_Performance_Summary_stage_5_TrialsToday.value = 0; +callback(Training_Performance_Summary_stage_5_TrialsToday); +Training_Performance_Summary_stage_6_TrialsToday.value = 0; +callback(Training_Performance_Summary_stage_6_TrialsToday); +Training_Performance_Summary_stage_7_TrialsToday.value = 0; +callback(Training_Performance_Summary_stage_7_TrialsToday); +Training_Performance_Summary_stage_8_TrialsToday.value = 0; +callback(Training_Performance_Summary_stage_8_TrialsToday); % end @@ -131,13 +122,7 @@ GetSoloFunctionArgs(obj); ClearHelperVarsNotOwned(obj); % -CreateHelperVar(obj,'stages_trial_counter','value',zeros(1,8)); -CreateHelperVar(obj,'stages_trial_counter_today','value',zeros(1,8)); -CreateHelperVar(obj,'stages_trial_counter_oppSide','value',zeros(1,8)); -CreateHelperVar(obj,'stages_timeout_rate','value',zeros(1,8)); -CreateHelperVar(obj,'stages_violation_rate','value',zeros(1,8)); CreateHelperVar(obj,'this_stage_opp_side_trials', 'value', 0, 'force_init',true); -CreateHelperVar(obj,'stage_start_completed_trial','value',n_completed_trials,'force_init',true); % end @@ -150,37 +135,10 @@ ParamsSection_MaxSame.value = 3; callback(ParamsSection_MaxSame); stage_no = value(SessionDefinition_CURRENT_ACTIVE_STAGE); -this_stage_trial_counter = value(stages_trial_counter); if stage_no ~= value(ParamsSection_training_stage) ParamsSection_training_stage.value = stage_no; callback(ParamsSection_training_stage); end -if n_completed_trials > value(stage_start_completed_trial) - % Update the helper vars - this_stage_trial_counter(stage_no) = this_stage_trial_counter(stage_no) + 1; - SessionPerformanceSection_ntrials_stage.value = this_stage_trial_counter(stage_no); - callback(SessionPerformanceSection_ntrials_stage); - stages_trial_counter.value = this_stage_trial_counter; - - this_stage_trial_counter_today = value(stages_trial_counter_today); - this_stage_trial_counter_today(stage_no) = this_stage_trial_counter_today(stage_no) + 1; - SessionPerformanceSection_ntrials_stage_today.value = this_stage_trial_counter_today(stage_no); - callback(SessionPerformanceSection_ntrials_stage_today); - stages_trial_counter_today.value = this_stage_trial_counter_today; - - this_stage_trial_counter_oppSide = value(stages_trial_counter_oppSide); - if value(previous_sides(end)) ~= value(ParamsSection_ThisTrial) - this_stage_trial_counter_oppSide(stage_no) = this_stage_trial_counter_oppSide(stage_no) + 1; - this_stage_opp_side_trials.value = value(this_stage_opp_side_trials) + 1; - end - stages_trial_counter_oppSide.value = this_stage_trial_counter_oppSide; - - this_stage_timeout_percent = value(stages_timeout_rate); - this_stage_timeout_percent(stage_no) = ((this_stage_timeout_percent(stage_no) * this_stage_trial_counter(stage_no)) + double(timeout_history(end)))/(this_stage_trial_counter(stage_no) + 1); - SessionPerformanceSection_timeout_stage.value = this_stage_timeout_percent(stage_no); - callback(SessionPerformanceSection_timeout_stage); - stages_timeout_rate.value = this_stage_timeout_percent; -end % Update the reward collection time based upon behav if length(timeout_history) > 5 if all(value(timeout_history(end-1:end))) && value(this_stage_opp_side_trials) >= 2 @@ -198,7 +156,7 @@ end if length(timeout_history) > 20 if all(value(timeout_history(end-19:end))) - ParamsSection_RewardCollection_duration.value = 30; + ParamsSection_RewardCollection_duration.value = 120; callback(ParamsSection_RewardCollection_duration); end end @@ -214,11 +172,10 @@ % if ParamsSection_use_auto_train % do completion check if auto training stage_no = value(SessionDefinition_CURRENT_ACTIVE_STAGE); - this_stage_trial_counter_oppSide = value(stages_trial_counter_oppSide); - this_stage_trial_counter = value(stages_trial_counter); % only run it if its the start of the day, number of trials is small if n_completed_trials > 50 - if this_stage_trial_counter(stage_no) > value(Training_ParamsSection_total_trials) && this_stage_trial_counter_oppSide(stage_no) > value(Training_ParamsSection_total_trials_opp) + if value(Training_Performance_Summary_stage_2_TrialsValid) > value(Training_ParamsSection_total_trials) && ... + value(Training_ParamsSection_trial_oppSide) > value(Training_ParamsSection_total_trials_opp) ParamsSection_training_stage.value = stage_no + 1; callback(ParamsSection_training_stage); ParamsSection(obj, 'Changed_Training_Stage'); @@ -239,7 +196,22 @@ GetSoloFunctionArgs(obj); ClearHelperVarsNotOwned(obj); % -stages_trial_counter_today.value = zeros(1,8); +Training_Performance_Summary_stage_1_TrialsToday.value = 0; +callback(Training_Performance_Summary_stage_1_TrialsToday); +Training_Performance_Summary_stage_2_TrialsToday.value = 0; +callback(Training_Performance_Summary_stage_2_TrialsToday); +Training_Performance_Summary_stage_3_TrialsToday.value = 0; +callback(Training_Performance_Summary_stage_3_TrialsToday); +Training_Performance_Summary_stage_4_TrialsToday.value = 0; +callback(Training_Performance_Summary_stage_4_TrialsToday); +Training_Performance_Summary_stage_5_TrialsToday.value = 0; +callback(Training_Performance_Summary_stage_5_TrialsToday); +Training_Performance_Summary_stage_6_TrialsToday.value = 0; +callback(Training_Performance_Summary_stage_6_TrialsToday); +Training_Performance_Summary_stage_7_TrialsToday.value = 0; +callback(Training_Performance_Summary_stage_7_TrialsToday); +Training_Performance_Summary_stage_8_TrialsToday.value = 0; +callback(Training_Performance_Summary_stage_8_TrialsToday); % end % @@ -255,13 +227,7 @@ GetSoloFunctionArgs(obj); ClearHelperVarsNotOwned(obj); % -CreateHelperVar(obj,'stages_trial_counter','value',zeros(1,8)); -CreateHelperVar(obj,'stages_trial_counter_today','value',zeros(1,8)); -CreateHelperVar(obj,'stages_trial_counter_oppSide','value',zeros(1,8)); -CreateHelperVar(obj,'stages_timeout_rate','value',zeros(1,8)); -CreateHelperVar(obj,'stages_violation_rate','value',zeros(1,8)); -CreateHelperVar(obj,'last_session_CP','value',0); -CreateHelperVar(obj,'stage_start_completed_trial','value',n_completed_trials,'force_init',true); + % end @@ -279,32 +245,12 @@ callback(ParamsSection_MaxSame); stage_no = value(SessionDefinition_CURRENT_ACTIVE_STAGE); -this_stage_trial_counter = value(stages_trial_counter); if stage_no ~= value(ParamsSection_training_stage) ParamsSection_training_stage.value = stage_no; callback(ParamsSection_training_stage); end -if n_completed_trials > value(stage_start_completed_trial) - % Update the helper vars - this_stage_trial_counter(stage_no) = this_stage_trial_counter(stage_no) + 1; - SessionPerformanceSection_ntrials_stage.value = this_stage_trial_counter(stage_no); - callback(SessionPerformanceSection_ntrials_stage); - stages_trial_counter.value = this_stage_trial_counter; - - this_stage_trial_counter_today = value(stages_trial_counter_today); - this_stage_trial_counter_today(stage_no) = this_stage_trial_counter_today(stage_no) + 1; - SessionPerformanceSection_ntrials_stage_today.value = this_stage_trial_counter_today(stage_no); - callback(SessionPerformanceSection_ntrials_stage_today); - stages_trial_counter_today.value = this_stage_trial_counter_today; - - this_stage_timeout_percent = value(stages_timeout_rate); - this_stage_timeout_percent(stage_no) = ((this_stage_timeout_percent(stage_no) * this_stage_trial_counter(stage_no)) + double(timeout_history(end)))/(this_stage_trial_counter(stage_no) + 1); - SessionPerformanceSection_timeout_stage.value = this_stage_timeout_percent(stage_no); - callback(SessionPerformanceSection_timeout_stage); - stages_timeout_rate.value = this_stage_timeout_percent; -end % Change the value of CP Duration -if value(last_session_CP) == 0 && this_stage_trial_counter(stage_no) < 2 +if value(Training_ParamsSection_last_session_CP) == 0 && value(Training_Performance_Summary_stage_3_Trials) < 2 ParamsSection_CP_duration.value = cp_min; % initialize to min_CP end @@ -332,11 +278,12 @@ if ParamsSection_use_auto_train % do completion check if auto training cp_max = value(ParamsSection_SettlingIn_time) + value(ParamsSection_legal_cbreak); if value(ParamsSection_CP_duration) >= cp_max + Training_ParamsSection_last_session_CP.value = value(ParamsSection_CP_duration); + callback(Training_ParamsSection_last_session_CP); ParamsSection_training_stage.value = 4; callback(ParamsSection_training_stage); ParamsSection(obj, 'Changed_Training_Stage'); SessionDefinition(obj, 'jump_to_stage', 'Introduce Violation for Centre Poke'); - last_session_CP.value = value(ParamsSection_CP_duration); end end % @@ -353,8 +300,26 @@ GetSoloFunctionArgs(obj); ClearHelperVarsNotOwned(obj); % -stages_trial_counter_today.value = zeros(1,8); -last_session_CP.value = value(ParamsSection_CP_duration); +% Update the CP duration reached in this session +Training_ParamsSection_last_session_CP.value = value(ParamsSection_CP_duration); +callback(Training_ParamsSection_last_session_CP); +% Reset the number of trials done today for this stage +Training_Performance_Summary_stage_1_TrialsToday.value = 0; +callback(Training_Performance_Summary_stage_1_TrialsToday); +Training_Performance_Summary_stage_2_TrialsToday.value = 0; +callback(Training_Performance_Summary_stage_2_TrialsToday); +Training_Performance_Summary_stage_3_TrialsToday.value = 0; +callback(Training_Performance_Summary_stage_3_TrialsToday); +Training_Performance_Summary_stage_4_TrialsToday.value = 0; +callback(Training_Performance_Summary_stage_4_TrialsToday); +Training_Performance_Summary_stage_5_TrialsToday.value = 0; +callback(Training_Performance_Summary_stage_5_TrialsToday); +Training_Performance_Summary_stage_6_TrialsToday.value = 0; +callback(Training_Performance_Summary_stage_6_TrialsToday); +Training_Performance_Summary_stage_7_TrialsToday.value = 0; +callback(Training_Performance_Summary_stage_7_TrialsToday); +Training_Performance_Summary_stage_8_TrialsToday.value = 0; +callback(Training_Performance_Summary_stage_8_TrialsToday); % end % @@ -368,13 +333,7 @@ GetSoloFunctionArgs(obj); ClearHelperVarsNotOwned(obj); % -CreateHelperVar(obj,'stages_trial_counter','value',zeros(1,8)); -CreateHelperVar(obj,'stages_trial_counter_today','value',zeros(1,8)); -CreateHelperVar(obj,'stages_trial_counter_oppSide','value',zeros(1,8)); -CreateHelperVar(obj,'stages_timeout_rate','value',zeros(1,8)); -CreateHelperVar(obj,'stages_violation_rate','value',zeros(1,8)); -CreateHelperVar(obj,'last_session_CP','value',0); -CreateHelperVar(obj,'stage_start_completed_trial','value',n_completed_trials,'force_init',true); + % end @@ -394,38 +353,13 @@ cp_minimum_increment = 0.001; stage_no = value(SessionDefinition_CURRENT_ACTIVE_STAGE); -this_stage_trial_counter = value(stages_trial_counter); -this_stage_trial_counter_today = value(stages_trial_counter_today); if stage_no ~= value(ParamsSection_training_stage) ParamsSection_training_stage.value = stage_no; callback(ParamsSection_training_stage); end -if n_completed_trials > value(stage_start_completed_trial) - % Update the helper vars - this_stage_trial_counter(stage_no) = this_stage_trial_counter(stage_no) + 1; - SessionPerformanceSection_ntrials_stage.value = this_stage_trial_counter(stage_no); - callback(SessionPerformanceSection_ntrials_stage); - stages_trial_counter.value = this_stage_trial_counter; - - this_stage_trial_counter_today(stage_no) = this_stage_trial_counter_today(stage_no) + 1; - SessionPerformanceSection_ntrials_stage_today.value = this_stage_trial_counter_today(stage_no); - callback(SessionPerformanceSection_ntrials_stage_today); - stages_trial_counter_today.value = this_stage_trial_counter_today; - - this_stage_timeout_percent = value(stages_timeout_rate); - this_stage_timeout_percent(stage_no) = ((this_stage_timeout_percent(stage_no) * this_stage_trial_counter(stage_no)) + double(timeout_history(end)))/(this_stage_trial_counter(stage_no) + 1); - SessionPerformanceSection_timeout_stage.value = this_stage_timeout_percent(stage_no); - callback(SessionPerformanceSection_timeout_stage); - stages_timeout_rate.value = this_stage_timeout_percent; - - this_stage_violation_percent = value(stages_violation_rate); - this_stage_violation_percent(stage_no) = ((this_stage_violation_percent(stage_no) * this_stage_trial_counter(stage_no)) + double(violation_history(end)))/(this_stage_trial_counter(stage_no) + 1); - SessionPerformanceSection_violation_stage.value = this_stage_violation_percent(stage_no); - callback(SessionPerformanceSection_violation_stage); - stages_violation_rate.value = this_stage_violation_percent; -end + % Change the value of CP Duration -if this_stage_trial_counter(stage_no) < 2 +if value(Training_Performance_Summary_stage_4_Trials) < 2 ParamsSection_CP_duration.value = cp_min; % initialize to min_CP end @@ -456,13 +390,17 @@ % if ParamsSection_use_auto_train % do completion check if auto training cp_max = value(Training_ParamsSection_max_CP); - if value(ParamsSection_CP_duration) >= cp_max && n_completed_trials > 100 && SessionPerformanceSection_violation_recent < value(Training_ParamsSection_recent_violation) && SessionPerformanceSection_timeout_recent < value(Training_ParamsSection_recent_timeout) && ... - SessionPerformanceSection_violation_stage < value(Training_ParamsSection_stage_violation) + if value(ParamsSection_CP_duration) >= cp_max && n_completed_trials > 100 && ... + value(SessionPerformanceSection_violation_recent) < value(Training_ParamsSection_recent_violation) && ... + value(SessionPerformanceSection_timeout_recent) < value(Training_ParamsSection_recent_timeout) && ... + value(SessionPerformanceSection_violation_stage) < value(Training_ParamsSection_stage_violation) + ParamsSection_training_stage.value = 5; callback(ParamsSection_training_stage); ParamsSection(obj, 'Changed_Training_Stage'); SessionDefinition(obj, 'jump_to_stage', 'Introduce Stimuli Sound during Centre Poke'); - last_session_CP.value = value(ParamsSection_CP_duration); + Training_ParamsSection_last_session_CP.value = value(ParamsSection_CP_duration); + callback(Training_ParamsSection_last_session_CP); end end % @@ -478,9 +416,26 @@ GetSoloFunctionArgs(obj); ClearHelperVarsNotOwned(obj); % -% Store the value of the total cp duration reached: -stages_trial_counter_today.value = zeros(1,8); -last_session_CP.value = value(ParamsSection_CP_duration); +% Update the CP duration reached in this session +Training_ParamsSection_last_session_CP.value = value(ParamsSection_CP_duration); +callback(Training_ParamsSection_last_session_CP); +% Reset the number of trials done today for this stage +Training_Performance_Summary_stage_1_TrialsToday.value = 0; +callback(Training_Performance_Summary_stage_1_TrialsToday); +Training_Performance_Summary_stage_2_TrialsToday.value = 0; +callback(Training_Performance_Summary_stage_2_TrialsToday); +Training_Performance_Summary_stage_3_TrialsToday.value = 0; +callback(Training_Performance_Summary_stage_3_TrialsToday); +Training_Performance_Summary_stage_4_TrialsToday.value = 0; +callback(Training_Performance_Summary_stage_4_TrialsToday); +Training_Performance_Summary_stage_5_TrialsToday.value = 0; +callback(Training_Performance_Summary_stage_5_TrialsToday); +Training_Performance_Summary_stage_6_TrialsToday.value = 0; +callback(Training_Performance_Summary_stage_6_TrialsToday); +Training_Performance_Summary_stage_7_TrialsToday.value = 0; +callback(Training_Performance_Summary_stage_7_TrialsToday); +Training_Performance_Summary_stage_8_TrialsToday.value = 0; +callback(Training_Performance_Summary_stage_8_TrialsToday); % end % @@ -497,13 +452,7 @@ GetSoloFunctionArgs(obj); ClearHelperVarsNotOwned(obj); % -CreateHelperVar(obj,'stages_trial_counter','value',zeros(1,8)); -CreateHelperVar(obj,'stages_trial_counter_today','value',zeros(1,8)); -CreateHelperVar(obj,'stages_trial_counter_oppSide','value',zeros(1,8)); -CreateHelperVar(obj,'stages_timeout_rate','value',zeros(1,8)); -CreateHelperVar(obj,'stages_violation_rate','value',zeros(1,8)); -CreateHelperVar(obj,'last_session_CP','value',0); -CreateHelperVar(obj,'stage_start_completed_trial','value',n_completed_trials,'force_init',true); + % end @@ -528,34 +477,7 @@ ParamsSection_training_stage.value = stage_no; callback(ParamsSection_training_stage); end -if n_completed_trials > value(stage_start_completed_trial) - % Update the helper vars - this_stage_trial_counter = value(stages_trial_counter); - this_stage_trial_counter(stage_no) = this_stage_trial_counter(stage_no) + 1; - SessionPerformanceSection_ntrials_stage.value = this_stage_trial_counter(stage_no); - callback(SessionPerformanceSection_ntrials_stage); - stages_trial_counter.value = this_stage_trial_counter; - - this_stage_trial_counter_today = value(stages_trial_counter_today); - this_stage_trial_counter_today(stage_no) = this_stage_trial_counter_today(stage_no) + 1; - SessionPerformanceSection_ntrials_stage_today.value = this_stage_trial_counter_today(stage_no); - callback(SessionPerformanceSection_ntrials_stage_today); - stages_trial_counter_today.value = this_stage_trial_counter_today; - - this_stage_timeout_percent = value(stages_timeout_rate); - this_stage_timeout_percent(stage_no) = ((this_stage_timeout_percent(stage_no) * this_stage_trial_counter(stage_no)) + double(timeout_history(end)))/(this_stage_trial_counter(stage_no) + 1); - SessionPerformanceSection_timeout_stage.value = this_stage_timeout_percent(stage_no); - callback(SessionPerformanceSection_timeout_stage); - stages_timeout_rate.value = this_stage_timeout_percent; - - this_stage_violation_percent = value(stages_violation_rate); - this_stage_violation_percent(stage_no) = ((this_stage_violation_percent(stage_no) * this_stage_trial_counter(stage_no)) + double(violation_history(end)))/(this_stage_trial_counter(stage_no) + 1); - SessionPerformanceSection_violation_stage.value = this_stage_violation_percent(stage_no); - callback(SessionPerformanceSection_violation_stage); - stages_violation_rate.value = this_stage_violation_percent; -end % Change the value of CP Duration - % Since starting a new session then do a pre warm up to last saved cp % duration else continue with learning with increased poke time if n_completed_trials == 0 @@ -564,10 +486,10 @@ ParamsSection_CP_duration.value = starting_cp; else if ~violation_history(end) && ~timeout_history(end) - if value(ParamsSection_CP_duration) < max([cp_min,value(last_session_CP)]) % warm up stage - increment = (max([cp_min,value(last_session_CP)]) - value(ParamsSection_CP_duration))/ (n_trial_warmup - 1); + if value(ParamsSection_CP_duration) < max([cp_min,value(Training_ParamsSection_last_session_CP)]) % warm up stage + increment = (max([cp_min,value(Training_ParamsSection_last_session_CP)]) - value(ParamsSection_CP_duration))/ (n_trial_warmup - 1); else - if value(ParamsSection_CP_duration) >= value(last_session_CP) && value(ParamsSection_CP_duration) <= cp_max % no warm up stage + if value(ParamsSection_CP_duration) >= value(Training_ParamsSection_last_session_CP) && value(ParamsSection_CP_duration) <= cp_max % no warm up stage increment = value(ParamsSection_CP_duration)*cp_fraction; if increment < cp_minimum_increment increment = cp_minimum_increment; @@ -625,15 +547,14 @@ % if ParamsSection_use_auto_train % do completion check if auto training stage_no = value(SessionDefinition_CURRENT_ACTIVE_STAGE); - this_stage_trial_counter = value(stages_trial_counter); - if value(ParamsSection_CP_duration) >= value(Training_ParamsSection_max_CP) && this_stage_trial_counter(stage_no) > value(Training_ParamsSection_total_trials) - if SessionPerformanceSection_violation_recent < value(Training_ParamsSection_recent_violation) && SessionPerformanceSection_timeout_recent < value(Training_ParamsSection_recent_timeout) && ... - SessionPerformanceSection_violation_stage < value(Training_ParamsSection_stage_violation) && n_completed_trials > 100 + if value(ParamsSection_CP_duration) >= value(Training_ParamsSection_max_CP) && value(Training_Performance_Summary_stage_5_TrialsValid) > value(Training_ParamsSection_total_trials) + if value(SessionPerformanceSection_violation_recent) < value(Training_ParamsSection_recent_violation) && value(SessionPerformanceSection_timeout_recent) < value(Training_ParamsSection_recent_timeout) && ... + value(SessionPerformanceSection_violation_stage) < value(Training_ParamsSection_stage_violation) && n_completed_trials > 100 ParamsSection_training_stage.value = stage_no + 1; callback(ParamsSection_training_stage); ParamsSection(obj, 'Changed_Training_Stage'); SessionDefinition(obj, 'jump_to_stage', 'Vary Stimuli location during Centre Poke'); - last_session_CP.value = value(ParamsSection_CP_duration); + Training_ParamsSection_last_session_CP.value = value(ParamsSection_CP_duration); end end end @@ -651,8 +572,26 @@ GetSoloFunctionArgs(obj); ClearHelperVarsNotOwned(obj); % -stages_trial_counter_today.value = zeros(1,8); -last_session_CP.value = value(ParamsSection_CP_duration); +% Update the CP duration reached in this session +Training_ParamsSection_last_session_CP.value = value(ParamsSection_CP_duration); +callback(Training_ParamsSection_last_session_CP); +% Reset the number of trials done today for this stage +Training_Performance_Summary_stage_1_TrialsToday.value = 0; +callback(Training_Performance_Summary_stage_1_TrialsToday); +Training_Performance_Summary_stage_2_TrialsToday.value = 0; +callback(Training_Performance_Summary_stage_2_TrialsToday); +Training_Performance_Summary_stage_3_TrialsToday.value = 0; +callback(Training_Performance_Summary_stage_3_TrialsToday); +Training_Performance_Summary_stage_4_TrialsToday.value = 0; +callback(Training_Performance_Summary_stage_4_TrialsToday); +Training_Performance_Summary_stage_5_TrialsToday.value = 0; +callback(Training_Performance_Summary_stage_5_TrialsToday); +Training_Performance_Summary_stage_6_TrialsToday.value = 0; +callback(Training_Performance_Summary_stage_6_TrialsToday); +Training_Performance_Summary_stage_7_TrialsToday.value = 0; +callback(Training_Performance_Summary_stage_7_TrialsToday); +Training_Performance_Summary_stage_8_TrialsToday.value = 0; +callback(Training_Performance_Summary_stage_8_TrialsToday); % end @@ -668,13 +607,7 @@ GetSoloFunctionArgs(obj); ClearHelperVarsNotOwned(obj); % -CreateHelperVar(obj,'stages_trial_counter','value',zeros(1,8)); -CreateHelperVar(obj,'stages_trial_counter_today','value',zeros(1,8)); -CreateHelperVar(obj,'stages_trial_counter_oppSide','value',zeros(1,8)); -CreateHelperVar(obj,'stages_timeout_rate','value',zeros(1,8)); -CreateHelperVar(obj,'stages_violation_rate','value',zeros(1,8)); -CreateHelperVar(obj,'last_session_CP','value',0); -CreateHelperVar(obj,'stage_start_completed_trial','value',n_completed_trials,'force_init',true); + % end @@ -698,32 +631,7 @@ ParamsSection_training_stage.value = stage_no; callback(ParamsSection_training_stage); end -if n_completed_trials > value(stage_start_completed_trial) - % Update the helper vars - this_stage_trial_counter = value(stages_trial_counter); - this_stage_trial_counter(stage_no) = this_stage_trial_counter(stage_no) + 1; - SessionPerformanceSection_ntrials_stage.value = this_stage_trial_counter(stage_no); - callback(SessionPerformanceSection_ntrials_stage); - stages_trial_counter.value = this_stage_trial_counter; - - this_stage_trial_counter_today = value(stages_trial_counter_today); - this_stage_trial_counter_today(stage_no) = this_stage_trial_counter_today(stage_no) + 1; - SessionPerformanceSection_ntrials_stage_today.value = this_stage_trial_counter_today(stage_no); - callback(SessionPerformanceSection_ntrials_stage_today); - stages_trial_counter_today.value = this_stage_trial_counter_today; - - this_stage_timeout_percent = value(stages_timeout_rate); - this_stage_timeout_percent(stage_no) = ((this_stage_timeout_percent(stage_no) * this_stage_trial_counter(stage_no)) + double(timeout_history(end)))/(this_stage_trial_counter(stage_no) + 1); - SessionPerformanceSection_timeout_stage.value = this_stage_timeout_percent(stage_no); - callback(SessionPerformanceSection_timeout_stage); - stages_timeout_rate.value = this_stage_timeout_percent; - - this_stage_violation_percent = value(stages_violation_rate); - this_stage_violation_percent(stage_no) = ((this_stage_violation_percent(stage_no) * this_stage_trial_counter(stage_no)) + double(violation_history(end)))/(this_stage_trial_counter(stage_no) + 1); - SessionPerformanceSection_violation_stage.value = this_stage_violation_percent(stage_no); - callback(SessionPerformanceSection_violation_stage); - stages_violation_rate.value = this_stage_violation_percent; -end + % Warm Up If starting a new session if n_completed_trials == 0 ParamsSection_CP_duration.value = value(ParamsSection_init_CP_duration); @@ -732,7 +640,7 @@ else if value(ParamsSection_CP_duration) < cp_max % warm up stage if ~violation_history(end) && ~timeout_history(end) - increment = (cp_max - value(ParamsSection_CP_duration))/ (n_trial_warmup - 1); + increment = (cp_max - value(ParamsSection_CP_duration)) / (n_trial_warmup - 1); ParamsSection_CP_duration.value = value(ParamsSection_CP_duration) + increment; % Check if the values are within the required range if value(ParamsSection_CP_duration) < starting_cp @@ -774,10 +682,9 @@ % if ParamsSection_use_auto_train % do completion check if auto training stage_no = value(SessionDefinition_CURRENT_ACTIVE_STAGE); - this_stage_trial_counter = value(stages_trial_counter); - if this_stage_trial_counter(stage_no) > value(Training_ParamsSection_total_trials) - if SessionPerformanceSection_violation_recent < value(Training_ParamsSection_recent_violation) && SessionPerformanceSection_timeout_recent < value(Training_ParamsSection_recent_timeout) && ... - SessionPerformanceSection_violation_stage < value(Training_ParamsSection_stage_violation) && n_completed_trials > 100 + if value(Training_Performance_Summary_stage_6_TrialsValid) > value(Training_ParamsSection_total_trials) + if value(SessionPerformanceSection_violation_recent) < value(Training_ParamsSection_recent_violation) && value(SessionPerformanceSection_timeout_recent) < value(Training_ParamsSection_recent_timeout) && ... + value(SessionPerformanceSection_violation_stage) < value(Training_ParamsSection_stage_violation) && n_completed_trials > 100 ParamsSection_training_stage.value = stage_no + 1; callback(ParamsSection_training_stage); ParamsSection(obj, 'Changed_Training_Stage'); @@ -798,7 +705,23 @@ GetSoloFunctionArgs(obj); ClearHelperVarsNotOwned(obj); % -stages_trial_counter_today.value = zeros(1,8); +% Reset the number of trials done today for this stage +Training_Performance_Summary_stage_1_TrialsToday.value = 0; +callback(Training_Performance_Summary_stage_1_TrialsToday); +Training_Performance_Summary_stage_2_TrialsToday.value = 0; +callback(Training_Performance_Summary_stage_2_TrialsToday); +Training_Performance_Summary_stage_3_TrialsToday.value = 0; +callback(Training_Performance_Summary_stage_3_TrialsToday); +Training_Performance_Summary_stage_4_TrialsToday.value = 0; +callback(Training_Performance_Summary_stage_4_TrialsToday); +Training_Performance_Summary_stage_5_TrialsToday.value = 0; +callback(Training_Performance_Summary_stage_5_TrialsToday); +Training_Performance_Summary_stage_6_TrialsToday.value = 0; +callback(Training_Performance_Summary_stage_6_TrialsToday); +Training_Performance_Summary_stage_7_TrialsToday.value = 0; +callback(Training_Performance_Summary_stage_7_TrialsToday); +Training_Performance_Summary_stage_8_TrialsToday.value = 0; +callback(Training_Performance_Summary_stage_8_TrialsToday); % end % @@ -814,13 +737,7 @@ GetSoloFunctionArgs(obj); ClearHelperVarsNotOwned(obj); % -CreateHelperVar(obj,'stages_trial_counter','value',zeros(1,8)); -CreateHelperVar(obj,'stages_trial_counter_today','value',zeros(1,8)); -CreateHelperVar(obj,'stages_trial_counter_oppSide','value',zeros(1,8)); -CreateHelperVar(obj,'stages_timeout_rate','value',zeros(1,8)); -CreateHelperVar(obj,'stages_violation_rate','value',zeros(1,8)); -CreateHelperVar(obj,'last_session_CP','value',0); -CreateHelperVar(obj,'stage_start_completed_trial','value',n_completed_trials,'force_init',true); + % end @@ -855,32 +772,7 @@ ParamsSection_training_stage.value = stage_no; callback(ParamsSection_training_stage); end -if n_completed_trials > value(stage_start_completed_trial) - % Update the helper vars - this_stage_trial_counter = value(stages_trial_counter); - this_stage_trial_counter(stage_no) = this_stage_trial_counter(stage_no) + 1; - SessionPerformanceSection_ntrials_stage.value = this_stage_trial_counter(stage_no); - callback(SessionPerformanceSection_ntrials_stage); - stages_trial_counter.value = this_stage_trial_counter; - - this_stage_trial_counter_today = value(stages_trial_counter_today); - this_stage_trial_counter_today(stage_no) = this_stage_trial_counter_today(stage_no) + 1; - SessionPerformanceSection_ntrials_stage_today.value = this_stage_trial_counter_today(stage_no); - callback(SessionPerformanceSection_ntrials_stage_today); - stages_trial_counter_today.value = this_stage_trial_counter_today; - - this_stage_timeout_percent = value(stages_timeout_rate); - this_stage_timeout_percent(stage_no) = ((this_stage_timeout_percent(stage_no) * this_stage_trial_counter(stage_no)) + double(timeout_history(end)))/(this_stage_trial_counter(stage_no) + 1); - SessionPerformanceSection_timeout_stage.value = this_stage_timeout_percent(stage_no); - callback(SessionPerformanceSection_timeout_stage); - stages_timeout_rate.value = this_stage_timeout_percent; - - this_stage_violation_percent = value(stages_violation_rate); - this_stage_violation_percent(stage_no) = ((this_stage_violation_percent(stage_no) * this_stage_trial_counter(stage_no)) + double(violation_history(end)))/(this_stage_trial_counter(stage_no) + 1); - SessionPerformanceSection_violation_stage.value = this_stage_violation_percent(stage_no); - callback(SessionPerformanceSection_violation_stage); - stages_violation_rate.value = this_stage_violation_percent; -end + % Warm Up If starting a new session if warm_up_on == 1 if n_completed_trials == 0 @@ -931,10 +823,10 @@ clear('ans'); % stage_no = value(SessionDefinition_CURRENT_ACTIVE_STAGE); -this_stage_trial_counter = value(stages_trial_counter); -if this_stage_trial_counter(stage_no) > value(Training_ParamsSection_total_trials) - if SessionPerformanceSection_violation_recent < value(Training_ParamsSection_recent_violation) && SessionPerformanceSection_timeout_recent < value(Training_ParamsSection_recent_timeout) && ... - SessionPerformanceSection_violation_stage < value(Training_ParamsSection_stage_violation) + +if value(Training_Performance_Summary_stage_7_TrialsValid) > value(Training_ParamsSection_total_trials) + if value(SessionPerformanceSection_violation_recent) < value(Training_ParamsSection_recent_violation) && value(SessionPerformanceSection_timeout_recent) < value(Training_ParamsSection_recent_timeout) && ... + value(SessionPerformanceSection_violation_stage) < value(Training_ParamsSection_stage_violation) ParamsSection_training_stage.value = stage_no + 1; callback(ParamsSection_training_stage); ParamsSection(obj, 'Changed_Training_Stage'); @@ -955,7 +847,22 @@ GetSoloFunctionArgs(obj); ClearHelperVarsNotOwned(obj); % -stages_trial_counter_today.value = zeros(1,8); +Training_Performance_Summary_stage_1_TrialsToday.value = 0; +callback(Training_Performance_Summary_stage_1_TrialsToday); +Training_Performance_Summary_stage_2_TrialsToday.value = 0; +callback(Training_Performance_Summary_stage_2_TrialsToday); +Training_Performance_Summary_stage_3_TrialsToday.value = 0; +callback(Training_Performance_Summary_stage_3_TrialsToday); +Training_Performance_Summary_stage_4_TrialsToday.value = 0; +callback(Training_Performance_Summary_stage_4_TrialsToday); +Training_Performance_Summary_stage_5_TrialsToday.value = 0; +callback(Training_Performance_Summary_stage_5_TrialsToday); +Training_Performance_Summary_stage_6_TrialsToday.value = 0; +callback(Training_Performance_Summary_stage_6_TrialsToday); +Training_Performance_Summary_stage_7_TrialsToday.value = 0; +callback(Training_Performance_Summary_stage_7_TrialsToday); +Training_Performance_Summary_stage_8_TrialsToday.value = 0; +callback(Training_Performance_Summary_stage_8_TrialsToday); % end % @@ -970,13 +877,7 @@ GetSoloFunctionArgs(obj); ClearHelperVarsNotOwned(obj); % -CreateHelperVar(obj,'stages_trial_counter','value',zeros(1,8)); -CreateHelperVar(obj,'stages_trial_counter_today','value',zeros(1,8)); -CreateHelperVar(obj,'stages_trial_counter_oppSide','value',zeros(1,8)); -CreateHelperVar(obj,'stages_timeout_rate','value',zeros(1,8)); -CreateHelperVar(obj,'stages_violation_rate','value',zeros(1,8)); -CreateHelperVar(obj,'last_session_CP','value',0); -CreateHelperVar(obj,'stage_start_completed_trial','value',n_completed_trials,'force_init',true); + % end @@ -1011,32 +912,6 @@ callback(ParamsSection_training_stage); end -if n_completed_trials > value(stage_start_completed_trial) - % Update the helper vars - this_stage_trial_counter = value(stages_trial_counter); - this_stage_trial_counter(stage_no) = this_stage_trial_counter(stage_no) + 1; - SessionPerformanceSection_ntrials_stage.value = this_stage_trial_counter(stage_no); - callback(SessionPerformanceSection_ntrials_stage); - stages_trial_counter.value = this_stage_trial_counter; - - this_stage_trial_counter_today = value(stages_trial_counter_today); - this_stage_trial_counter_today(stage_no) = this_stage_trial_counter_today(stage_no) + 1; - SessionPerformanceSection_ntrials_stage_today.value = this_stage_trial_counter_today(stage_no); - callback(SessionPerformanceSection_ntrials_stage_today); - stages_trial_counter_today.value = this_stage_trial_counter_today; - - this_stage_timeout_percent = value(stages_timeout_rate); - this_stage_timeout_percent(stage_no) = ((this_stage_timeout_percent(stage_no) * this_stage_trial_counter(stage_no)) + double(timeout_history(end)))/(this_stage_trial_counter(stage_no) + 1); - SessionPerformanceSection_timeout_stage.value = this_stage_timeout_percent(stage_no); - callback(SessionPerformanceSection_timeout_stage); - stages_timeout_rate.value = this_stage_timeout_percent; - - this_stage_violation_percent = value(stages_violation_rate); - this_stage_violation_percent(stage_no) = ((this_stage_violation_percent(stage_no) * this_stage_trial_counter(stage_no)) + double(violation_history(end)))/(this_stage_trial_counter(stage_no) + 1); - SessionPerformanceSection_violation_stage.value = this_stage_violation_percent(stage_no); - callback(SessionPerformanceSection_violation_stage); - stages_violation_rate.value = this_stage_violation_percent; -end % Warm Up If starting a new session if warm_up_on == 1 if n_completed_trials == 0 @@ -1101,7 +976,22 @@ GetSoloFunctionArgs(obj); ClearHelperVarsNotOwned(obj); % -stages_trial_counter_today.value = zeros(1,8); +Training_Performance_Summary_stage_1_TrialsToday.value = 0; +callback(Training_Performance_Summary_stage_1_TrialsToday); +Training_Performance_Summary_stage_2_TrialsToday.value = 0; +callback(Training_Performance_Summary_stage_2_TrialsToday); +Training_Performance_Summary_stage_3_TrialsToday.value = 0; +callback(Training_Performance_Summary_stage_3_TrialsToday); +Training_Performance_Summary_stage_4_TrialsToday.value = 0; +callback(Training_Performance_Summary_stage_4_TrialsToday); +Training_Performance_Summary_stage_5_TrialsToday.value = 0; +callback(Training_Performance_Summary_stage_5_TrialsToday); +Training_Performance_Summary_stage_6_TrialsToday.value = 0; +callback(Training_Performance_Summary_stage_6_TrialsToday); +Training_Performance_Summary_stage_7_TrialsToday.value = 0; +callback(Training_Performance_Summary_stage_7_TrialsToday); +Training_Performance_Summary_stage_8_TrialsToday.value = 0; +callback(Training_Performance_Summary_stage_8_TrialsToday); % end % diff --git a/Protocols/@ArpitCentrePokeTraining/ParamsSection.m b/Protocols/@ArpitCentrePokeTraining/ParamsSection.m index 5068f463..00e2fcf7 100644 --- a/Protocols/@ArpitCentrePokeTraining/ParamsSection.m +++ b/Protocols/@ArpitCentrePokeTraining/ParamsSection.m @@ -175,6 +175,9 @@ SoloFunctionAddVars('Training_ParamsSection', 'ro_args', ... {'training_stage'}); + + SoloFunctionAddVars('Training_Performance_Summary', 'ro_args', ... + {'training_stage';'ThisTrial'}); SoloParamHandle(obj, 'previous_parameters', 'value', []); diff --git a/Protocols/@ArpitCentrePokeTraining/SessionPerformanceSection.m b/Protocols/@ArpitCentrePokeTraining/SessionPerformanceSection.m index 1f9b8c6c..b01c05b1 100644 --- a/Protocols/@ArpitCentrePokeTraining/SessionPerformanceSection.m +++ b/Protocols/@ArpitCentrePokeTraining/SessionPerformanceSection.m @@ -75,26 +75,17 @@ next_row(y); SubheaderParam(obj, 'title', 'Overall Performance', x, y); next_row(y, 1.5); - SoloParamHandle(obj, 'previous_parameters', 'value', []); + % SoloParamHandle(obj, 'previous_parameters', 'value', []); + + SoloFunctionAddVars('Training_Performance_Summary', 'ro_args', ... + {'ntrials_stage';'ntrials_stage_today';'violation_stage';'timeout_stage'}); % ------------------------------------------------------------------ % evaluate % ------------------------------------------------------------------ case 'evaluate' - - if n_completed_trials >= 1 - this_stage_trial_counter_today = value(stages_trial_counter_today); - this_stage_trial_counter = value(stages_trial_counter); - ntrials_stage.value = this_stage_trial_counter(value(training_stage)); - ntrials_stage_today.value = this_stage_trial_counter_today(value(training_stage)); - - this_stage_timeout_percent = value(stages_timeout_rate); - this_stage_violation_percent = value(stages_violation_rate); - violation_stage.value = this_stage_violation_percent(value(training_stage)); - timeout_stage.value = this_stage_timeout_percent(value(training_stage)); - end - + switch value(training_stage) case 1 %% center led on -> poke in the center -> go cue -> reward light and sound if n_completed_trials > 1 diff --git a/Protocols/@ArpitCentrePokeTraining/Training_ParamsSection.m b/Protocols/@ArpitCentrePokeTraining/Training_ParamsSection.m index 8b6c059e..124eb506 100644 --- a/Protocols/@ArpitCentrePokeTraining/Training_ParamsSection.m +++ b/Protocols/@ArpitCentrePokeTraining/Training_ParamsSection.m @@ -12,11 +12,12 @@ SoloParamHandle(obj, 'my_gui_info', 'value', [x y double(gcf)], 'saveable', 0); next_row(y); next_row(y); next_row(y);next_row(y); + switch value(training_stage) case 1 % STAGE RUNNING PARAMETERS - DispParam(obj, 'stages_trial_counter_oppSide', 0, x, y,'save_with_settings', 1,'TooltipString','total trials in opposide side this stage'); next_row(y); + DispParam(obj, 'trial_oppSide', 0, x, y,'save_with_settings', 1,'TooltipString','total trials in opposide side this stage'); next_row(y); SubheaderParam(obj, 'title', 'Stage Params', x, y); % next_row(y); % COMPLETION TEST PARAMETERS @@ -24,9 +25,11 @@ NumeditParam(obj, 'total_trials_opp', 150, x, y,'label','Trials_Opp','TooltipString','total trials with opposide side option in this stage for its completion'); next_row(y); SubheaderParam(obj, 'title', 'Completion Params', x, y); next_row(y); + SoloFunctionAddVars('Training_Performance_Summary', 'ro_args', {'trial_oppSide'}); + case 2 % STAGE RUNNING PARAMETERS - DispParam(obj, 'stages_trial_counter_oppSide', 0, x, y,'save_with_settings', 1,'TooltipString','total trials in opposide side this stage'); next_row(y); + DispParam(obj, 'trial_oppSide', 0, x, y,'save_with_settings', 1,'TooltipString','total trials in opposide side this stage'); next_row(y); NumeditParam(obj, 'max_rColl_dur', 300, x, y,'label','T_Max_RCollect','TooltipString','User given max water collect time(code will optimize for value below this)'); next_row(y); NumeditParam(obj, 'min_rColl_dur', 100, x, y,'label','T_Min_RCollect','TooltipString','User given min water collect time(code will optimize for value above this)'); next_row(y); SubheaderParam(obj, 'title', 'Stage Params', x, y); next_row(y); @@ -34,6 +37,8 @@ NumeditParam(obj, 'total_trials', 1000, x, y,'label','Trials','TooltipString','total trials in this stage for its completion'); next_row(y); NumeditParam(obj, 'total_trials_opp', 400, x, y,'label','Trials_Opp','TooltipString','total trials with opposide side option in this stage for its completion'); next_row(y); SubheaderParam(obj, 'title', 'Completion Params', x, y);next_row(y); + + SoloFunctionAddVars('Training_Performance_Summary', 'ro_args', {'trial_oppSide'}); case 3 % no completion test required % STAGE RUNNING PARAMETERS diff --git a/Protocols/@ArpitCentrePokeTraining/Training_Performance_Summary.m b/Protocols/@ArpitCentrePokeTraining/Training_Performance_Summary.m index beecda45..096809e1 100644 --- a/Protocols/@ArpitCentrePokeTraining/Training_Performance_Summary.m +++ b/Protocols/@ArpitCentrePokeTraining/Training_Performance_Summary.m @@ -77,6 +77,171 @@ x=oldx; y=oldy; figure(parentfig); +%% Evaluate + case evaluate + + switch value(training_stage) + + case 1 + + if n_completed_trials > 0 + + stage_1_Trials.value = value(stage_1_Trials) + 1; + stage_1_TrialsToday.value = value(stage_1_TrialsToday) + 1; + if value(previous_sides(end)) ~= value(ThisTrial) + trial_oppSide.value = value(trial_oppSide) + 1; % updating value for variable in TrainingParams_Section + end + stage_1_ViolationRate.value = nan; + stage_1_TimeoutRate.value = nan; + if value(hit_history(end)) == 1 + stage_1_TrialsValid.value = value(stage_1_TrialsToday) + 1; + end + % Updating Disp Values for SessionPeformance Section as + % well + ntrials_stage.value = value(stage_1_Trials); + ntrials_stage_today.value = value(stage_1_TrialsToday); + violation_stage.value = value(stage_1_ViolationRate); + timeout_stage.value = value(stage_1_TimeoutRate); + end + + case 2 + + if n_completed_trials > 0 + + stage_2_Trials.value = value(stage_2_Trials) + 1; + stage_2_TrialsToday.value = value(stage_2_TrialsToday) + 1; + if value(previous_sides(end)) ~= value(ThisTrial) && all(hit_history(end-1:end)) % last and present trials should also be a valid trial + trial_oppSide.value = value(trial_oppSide) + 1; % updating value for variable in TrainingParams_Section + end + stage_2_ViolationRate.value = nan; + stage_2_TimeoutRate.value = ((value(stage_2_TimeoutRate) * (value(stage_2_Trials) - 1)) + double(timeout_history(end))) / value(stage_2_Trials); + if value(hit_history(end)) == 1 + stage_2_TrialsValid.value = value(stage_2_TrialsToday) + 1; + end + % Updating Disp Values for SessionPeformance Section as + % well + ntrials_stage.value = value(stage_2_Trials); + ntrials_stage_today.value = value(stage_2_TrialsToday); + violation_stage.value = value(stage_2_ViolationRate); + timeout_stage.value = value(stage_2_TimeoutRate); + end + + case 3 + + if n_completed_trials > 0 + + stage_3_Trials.value = value(stage_3_Trials) + 1; + stage_1_TrialsToday.value = value(stage_3_TrialsToday) + 1; + stage_3_ViolationRate.value = ((value(stage_3_ViolationRate) * (value(stage_3_Trials) - 1)) + double(violation_history(end))) / value(stage_3_Trials); + stage_3_TimeoutRate.value = ((value(stage_3_TimeoutRate) * (value(stage_3_Trials) - 1)) + double(timeout_history(end))) / value(stage_3_Trials); + if value(hit_history(end)) == 1 + stage_3_TrialsValid.value = value(stage_3_TrialsToday) + 1; + end + % Updating Disp Values for SessionPeformance Section as + % well + ntrials_stage.value = value(stage_3_Trials); + ntrials_stage_today.value = value(stage_3_TrialsToday); + violation_stage.value = value(stage_3_ViolationRate); + timeout_stage.value = value(stage_3_TimeoutRate); + end + + case 4 + + if n_completed_trials > 0 + + stage_4_Trials.value = value(stage_4_Trials) + 1; + stage_4_TrialsToday.value = value(stage_4_TrialsToday) + 1; + stage_4_ViolationRate.value = ((value(stage_4_ViolationRate) * (value(stage_4_Trials) - 1)) + double(violation_history(end))) / value(stage_4_Trials); + stage_4_TimeoutRate.value = ((value(stage_4_TimeoutRate) * (value(stage_4_Trials) - 1)) + double(timeout_history(end))) / value(stage_4_Trials); + if value(hit_history(end)) == 1 + stage_4_TrialsValid.value = value(stage_4_TrialsToday) + 1; + end + % Updating Disp Values for SessionPeformance Section as + % well + ntrials_stage.value = value(stage_4_Trials); + ntrials_stage_today.value = value(stage_4_TrialsToday); + violation_stage.value = value(stage_4_ViolationRate); + timeout_stage.value = value(stage_4_TimeoutRate); + end + + case 5 + + if n_completed_trials > 0 + + stage_5_Trials.value = value(stage_5_Trials) + 1; + stage_5_TrialsToday.value = value(stage_5_TrialsToday) + 1; + stage_5_ViolationRate.value = ((value(stage_5_ViolationRate) * (value(stage_5_Trials) - 1)) + double(violation_history(end))) / value(stage_5_Trials); + stage_5_TimeoutRate.value = ((value(stage_5_TimeoutRate) * (value(stage_5_Trials) - 1)) + double(timeout_history(end))) / value(stage_5_Trials); + if value(hit_history(end)) == 1 + stage_5_TrialsValid.value = value(stage_5_TrialsToday) + 1; + end + % Updating Disp Values for SessionPeformance Section as + % well + ntrials_stage.value = value(stage_5_Trials); + ntrials_stage_today.value = value(stage_5_TrialsToday); + violation_stage.value = value(stage_5_ViolationRate); + timeout_stage.value = value(stage_5_TimeoutRate); + end + + case 6 + + if n_completed_trials > 0 + + stage_6_Trials.value = value(stage_6_Trials) + 1; + stage_6_TrialsToday.value = value(stage_6_TrialsToday) + 1; + stage_6_ViolationRate.value = ((value(stage_6_ViolationRate) * (value(stage_6_Trials) - 1)) + double(violation_history(end))) / value(stage_6_Trials); + stage_6_TimeoutRate.value = ((value(stage_6_TimeoutRate) * (value(stage_6_Trials) - 1)) + double(timeout_history(end))) / value(stage_6_Trials); + if value(hit_history(end)) == 1 + stage_6_TrialsValid.value = value(stage_6_TrialsToday) + 1; + end + % Updating Disp Values for SessionPeformance Section as + % well + ntrials_stage.value = value(stage_6_Trials); + ntrials_stage_today.value = value(stage_6_TrialsToday); + violation_stage.value = value(stage_6_ViolationRate); + timeout_stage.value = value(stage_6_TimeoutRate); + end + + case 7 + + if n_completed_trials > 0 + + stage_7_Trials.value = value(stage_7_Trials) + 1; + stage_7_TrialsToday.value = value(stage_7_TrialsToday) + 1; + stage_7_ViolationRate.value = ((value(stage_7_ViolationRate) * (value(stage_7_Trials) - 1)) + double(violation_history(end))) / value(stage_7_Trials); + stage_7_TimeoutRate.value = ((value(stage_7_TimeoutRate) * (value(stage_7_Trials) - 1)) + double(timeout_history(end))) / value(stage_7_Trials); + if value(hit_history(end)) == 1 + stage_7_TrialsValid.value = value(stage_7_TrialsToday) + 1; + end + % Updating Disp Values for SessionPeformance Section as + % well + ntrials_stage.value = value(stage_7_Trials); + ntrials_stage_today.value = value(stage_7_TrialsToday); + violation_stage.value = value(stage_7_ViolationRate); + timeout_stage.value = value(stage_7_TimeoutRate); + end + + case 8 + + if n_completed_trials > 0 + + stage_8_Trials.value = value(stage_8_Trials) + 1; + stage_8_TrialsToday.value = value(stage_8_TrialsToday) + 1; + stage_8_ViolationRate.value = ((value(stage_8_ViolationRate) * (value(stage_8_Trials) - 1)) + double(violation_history(end))) / value(stage_8_Trials); + stage_8_TimeoutRate.value = ((value(stage_8_TimeoutRate) * (value(stage_8_Trials) - 1)) + double(timeout_history(end))) / value(stage_8_Trials); + if value(hit_history(end)) == 1 + stage_8_TrialsValid.value = value(stage_8_TrialsToday) + 1; + end + % Updating Disp Values for SessionPeformance Section as + % well + ntrials_stage.value = value(stage_8_Trials); + ntrials_stage_today.value = value(stage_8_TrialsToday); + violation_stage.value = value(stage_8_ViolationRate); + timeout_stage.value = value(stage_8_TimeoutRate); + end + end + + %% Case close case 'close' set(value(myfig), 'Visible', 'off'); diff --git a/Protocols/@ArpitSoundCalibration/ArpitSoundCalibration.asv b/Protocols/@ArpitSoundCalibration/ArpitSoundCalibration.asv deleted file mode 100644 index 69ddc99c..00000000 --- a/Protocols/@ArpitSoundCalibration/ArpitSoundCalibration.asv +++ /dev/null @@ -1,733 +0,0 @@ - -% MATLAB script for calibrating sound pressure level using Arduino and serial communication in real-time. -% -% This script automates the process of calibrating a speaker using an Arduino and a sound level meter. -% It communicates with an Arduino to control speaker output and read sound pressure level (SPL) measurements. -% The script performs the following steps: -% 1. Establishes serial communication with the Arduino. -% 2. Creates a figure window with a table to display real-time SPL readings. -% 3. Iterates through a set of speaker output values, sending each value to the speaker (via a user-defined function). -% 4. At each speaker output level, the script reads the corresponding SPL from the Arduino. -% 5. The measured SPL values are displayed in the table in real-time. -% 6. After completing the measurements, the script displays the final calibration data and optionally plots the -% relationship between speaker output and SPL, including a polynomial fit. -% 7. Includes error handling to ensure robust communication with the Arduino. - -% Written by Arpit 2024 - -% Make sure you ran newstartup, then dispatcher('init'), and you're good to -% go! -% - -function [obj] = Arpit_SoundCalibration(varargin) - -% Default object is of our own class (mfilename); in this simplest of -% protocols, we inherit only from Plugins/@pokesplot - -obj = class(struct, mfilename, soundmanager, soundui); - -%--------------------------------------------------------------- -% BEGIN SECTION COMMON TO ALL PROTOCOLS, DO NOT MODIFY -%--------------------------------------------------------------- - -% If creating an empty object, return without further ado: -if nargin==0 || (nargin==1 && ischar(varargin{1}) && strcmp(varargin{1}, 'empty')), - return; -end; - -if isa(varargin{1}, mfilename), % If first arg is an object of this class itself, we are - % Most likely responding to a callback from - % a SoloParamHandle defined in this mfile. - if length(varargin) < 2 || ~ischar(varargin{2}), - error(['If called with a "%s" object as first arg, a second arg, a ' ... - 'string specifying the action, is required\n']); - else action = varargin{2}; varargin = varargin(3:end); %#ok - end; -else % Ok, regular call with first param being the action string. - action = varargin{1}; varargin = varargin(2:end); %#ok -end; -if ~ischar(action), error('The action parameter must be a string'); end; - -GetSoloFunctionArgs(obj); - -%--------------------------------------------------------------- -% END OF SECTION COMMON TO ALL PROTOCOLS, MODIFY AFTER THIS LINE -%--------------------------------------------------------------- - - -% ---- From here on is where you can put the code you like. -% -% Your protocol will be called, at the appropriate times, with the -% following possible actions: -% -% 'init' To initialize -- make figure windows, variables, etc. -% -% 'update' Called periodically within a trial -% -% 'prepare_next_trial' Called when a trial has ended and your protocol -% is expected to produce the StateMachine diagram for the next -% trial; i.e., somewhere in your protocol's response to this -% call, it should call "dispatcher('send_assembler', sma, -% prepare_next_trial_set);" where sma is the -% StateMachineAssembler object that you have prepared and -% prepare_next_trial_set is either a single string or a cell -% with elements that are all strings. These strings should -% correspond to names of states in sma. -% Note that after the 'prepare_next_trial' call, further -% events may still occur in the RTLSM while your protocol is thinking, -% before the new StateMachine diagram gets sent. These events -% will be available to you when 'trial_completed' is called on your -% protocol (see below). -% -% 'trial_completed' Called when 'state_0' is reached in the RTLSM, -% marking final completion of a trial (and the start of -% the next). -% -% 'close' Called when the protocol is to be closed. -% -% -% VARIABLES THAT DISPATCHER WILL ALWAYS INSTANTIATE FOR YOU IN YOUR -% PROTOCOL: -% -% (These variables will be instantiated as regular Matlab variables, -% not SoloParamHandles. For any method in your protocol (i.e., an m-file -% within the @your_protocol directory) that takes "obj" as its first argument, -% calling "GetSoloFunctionArgs(obj)" will instantiate all the variables below.) -% -% -% n_done_trials How many trials have been finished; when a trial reaches -% one of the prepare_next_trial states for the first -% time, this variable is incremented by 1. -% -% n_started trials How many trials have been started. This variable gets -% incremented by 1 every time the state machine goes -% through state 0. -% -% parsed_events The result of running disassemble.m, with the -% parsed_structure flag set to 1, on all events from the -% start of the current trial to now. -% -% latest_events The result of running disassemble.m, with the -% parsed_structure flag set to 1, on all new events from -% the last time 'update' was called to now. -% -% raw_events All the events obtained in the current trial, not parsed -% or disassembled, but raw as gotten from the State -% Machine object. -% -% current_assembler The StateMachineAssembler object that was used to -% generate the State Machine diagram in effect in the -% current trial. -% -% Trial-by-trial history of parsed_events, raw_events, and -% current_assembler, are automatically stored for you in your protocol by -% dispatcher.m. See the wiki documentation for information on how to access -% those histories from within your protocol and for information. -% -% - - -switch action - - %--------------------------------------------------------------- - % CASE INIT - %--------------------------------------------------------------- - - case 'init' - - % Make default figure. We remember to make it non-saveable; on next run - % the handle to this figure might be different, and we don't want to - % overwrite it when someone does load_data and some old value of the - % fig handle was stored as SoloParamHandle "myfig" - SoloParamHandle(obj, 'myfig', 'saveable', 0); myfig.value = double(figure); - - % Make the title of the figure be the protocol name, and if someone tries - % to close this figure, call dispatcher's close_protocol function, so it'll know - % to take it off the list of open protocols. - name = mfilename; - set(value(myfig), 'Name', name, 'Tag', name, ... - 'closerequestfcn', [mfilename,'(''close'');'], 'MenuBar', 'none'); - - - % Generate the sounds we need. - soundserver = bSettings('get','RIGS','sound_machine_server'); - if ~isempty(soundserver) - sr = SoundManagerSection(obj,'get_sample_rate'); - Fs=sr; - lfreq=2000; - hfreq=20000; - freq = 100; - T = 5; - fcut = 110; - filter_type = 'GAUS'; - A1_sigma = 0.0500; - A2_sigma = 0.0306; %0.1230;%0.0260; - A3_sigma = 0.0187; %0.0473;%0.0135; - A4_sigma = 0.0114; %0.0182;%0.0070; - A5_sigma = 0.0070; - [rawA1 rawA2 normA1 normA2]=noisestim(1,1,T,fcut,Fs,filter_type); - modulator=singlenoise(1,T,[lfreq hfreq],Fs,'BUTTER'); - AUD1=normA1(1:T*sr).*modulator(1:T*sr).*A1_sigma; - AUD2=normA1(1:T*sr).*modulator(1:T*sr).*A2_sigma; - AUD3=normA1(1:T*sr).*modulator(1:T*sr).*A3_sigma; - AUD4=normA1(1:T*sr).*modulator(1:T*sr).*A4_sigma; - AUD5=normA1(1:T*sr).*modulator(1:T*sr).*A5_sigma; - - if ~isempty(AUD2) - SoundManagerSection(obj, 'declare_new_sound', 'left_sound', [AUD2'; AUD2']) - end - if ~isempty(AUD1) - SoundManagerSection(obj, 'declare_new_sound', 'center_sound', [AUD1'; AUD1']) - end - if ~isempty(AUD3) - SoundManagerSection(obj, 'declare_new_sound', 'right_sound', [AUD3'; AUD3']) - end - if ~isempty(AUD4) - SoundManagerSection(obj, 'declare_new_sound', 'fourth_sound', [AUD4'; AUD4']) - end - if ~isempty(AUD5) - SoundManagerSection(obj, 'declare_new_sound', 'fifth_sound', [AUD5'; AUD5']) - end - SoundManagerSection(obj, 'send_not_yet_uploaded_sounds'); - end - - % Get monitor dimensions for dynamic GUI sizing - MP = get(0,'MonitorPositions'); - - % Calculate individual group width based on screen dimensions and number of groups - groupwidth = floor((MP(3)/2)/numel(linegroups)); - - padding = 10 % padding around GUI elements - - % Calculate total width needed for all groups combined - total_width = floor(numel(linegroups) * groupwidth+padding); - total_height = 400 % total height of GUI - - label_height = 140 % height of port label - button_height = 25 - - % Center the GUI horizontally on screen - left_pos = floor((MP(3) - total_width) / 2); - - % Set figure position with centered alignment and fixed height of 400 pixels - % position vector: [left-right, down-up, width, height], the origo is - % the bottom left corner - set(value(myfig), 'Position', [left_pos, floor((MP(4)-total_height)/2), total_width, total_height]); - - % Initialize array to store line names - line_names = []; - - % Iterate through each line group - for i = 1:numel(linegroups) - % Check if current group exists and has valid parameters - if ~isempty(linegroups{i}) && ~isempty(linegroups{i}{1,2}) - % port label position - SubheaderParam( ... - obj, ... - ['Input',linegroups{i}{1,2}], ... - {linegroups{i}{1,2};0},0,0, ... - 'position',[((i-1)*groupwidth)+padding, ... - total_height-padding-label_height, ... - groupwidth-padding, ... - label_height ... - ]); - - % port label aesthetics - set(get_ghandle(eval(['Input',linegroups{i}{1,2}])), ... - 'FontSize',32, ... % Large font size for visibility - 'BackgroundColor',[0,1,0], ... % Green background - 'HorizontalAlignment','center'); % Center align text - - % Store line name for later reference - line_names(end+1) = linegroups{i}{1,2}; %#ok - - % Add sound controls based on line identifier - if strcmp(linegroups{i}{1,2},'L') && ~isempty(soundserver) - % Left channel sound toggle - ToggleParam(obj, ... - 'LeftSound', 0,0,0, ... - 'position',[((i-1)*groupwidth)+padding, padding, groupwidth-padding,button_height], ... - 'OnString', 'Sound Two ON', ... - 'OffString', 'Sound Two OFF'); - set_callback(LeftSound,{mfilename,'play_left_sound'}); - - elseif strcmp(linegroups{i}{1,2},'R') && ~isempty(soundserver) - % Right channel sound toggle - ToggleParam(obj, ... - 'RightSound', 0,0,0, ... - 'position',[((i-1)*groupwidth)+padding, padding, groupwidth-padding,button_height], ... - 'OnString', 'Sound Three ON', ... - 'OffString', 'Sound Three OFF'); - set_callback(RightSound,{mfilename,'play_right_sound'}); - - elseif strcmp(linegroups{i}{1,2},'C') && ~isempty(soundserver) - % Center channel sound toggle - ToggleParam(obj, ... - 'CenterSound', 0,0,0, ... - 'position',[((i-1)*groupwidth)+padding, padding, groupwidth-padding,button_height], ... - 'OnString', 'Sound One ON', ... - 'OffString', 'Sound One OFF'); - set_callback(CenterSound,{mfilename,'play_center_sound'}); - - elseif strcmp(linegroups{i}{1,2},'A') && ~isempty(soundserver) - % Additional channel sound toggle - ToggleParam(obj, ... - 'FourthSound', 0,0,0, ... - 'position',[((i-1)*groupwidth)+padding, padding, groupwidth-padding,button_height], ... - 'OnString', 'Sound Four ON', ... - 'OffString', 'Sound Four OFF'); - set_callback(FourthSound,{mfilename,'play_fourth_sound'}); - end - - % Handle case for empty line identifier but existing group - elseif ~isempty(linegroups{i}) && isempty(linegroups{i}{1,2}) - ToggleParam(obj, ... - 'FifthSound', 0,0,0, ... - 'position',[((i-1)*groupwidth)+padding, padding, groupwidth-padding,button_height], ... - 'OnString', 'Sound Five ON', ... - 'OffString', 'Sound Five OFF'); - set_callback(FifthSound,{mfilename,'play_fifth_sound'}); - end - - - - SoloParamHandle(obj,'LineGroups','value',linegroups); - SoloParamHandle(obj,'LineNames','value',line_names); - - scr = timer; - set(scr,'Period', 0.2,'ExecutionMode','FixedRate','TasksToExecute',Inf,... - 'BusyMode','drop','TimerFcn',[mfilename,'(''close_continued'')']); - SoloParamHandle(obj, 'stopping_complete_timer', 'value', scr); - - Arpit_SoundCalibration(obj,'prepare_next_trial'); - - dispatcher('Run'); - - - case 'play_left_sound' - %% play_left_sound - if value(LeftSound) == 1 - SoundManagerSection(obj,'play_sound','left_sound'); - else - SoundManagerSection(obj,'stop_sound','left_sound'); - end - - case 'play_right_sound' - %% play_right_sound - if value(RightSound) == 1 - SoundManagerSection(obj,'play_sound','right_sound'); - else - SoundManagerSection(obj,'stop_sound','right_sound'); - end - - case 'play_center_sound' - %% play_center_sound - if value(CenterSound) == 1 - SoundManagerSection(obj,'play_sound','center_sound'); - else - SoundManagerSection(obj,'stop_sound','center_sound'); - end - - case 'play_fourth_sound' - %% play_fourth_sound - if value(FourthSound) == 1 - SoundManagerSection(obj,'play_sound','fourth_sound'); - else - SoundManagerSection(obj,'stop_sound','fourth_sound'); - end - case 'play_fifth_sound' - %% play_fifth_sound - if value(FifthSound) == 1 - SoundManagerSection(obj,'play_sound','fifth_sound'); - else - SoundManagerSection(obj,'stop_sound','fifth_sound'); - end - - case 'toggle16_2' - %% case toggle1_2 - linegroups = value(LineGroups); - dispatcher('toggle_bypass',log2(linegroups{16}{3,1})); - - %--------------------------------------------------------------- - % CASE PREPARE_NEXT_TRIAL - %--------------------------------------------------------------- - case 'prepare_next_trial' - line_names = value(LineNames); - sma = StateMachineAssembler('full_trial_structure','use_happenings',1); %,'n_input_lines',numel(line_names),'line_names',line_names); - sma = add_state(sma, 'name', 'the_only_state', 'self_timer',1e4, 'input_to_statechange', {'Tup', 'final_state'}); - sma = add_state(sma, 'name', 'final_state', 'self_timer',1e4, 'input_to_statechange', {'Tup', 'check_next_trial_ready'}); - dispatcher('send_assembler', sma, 'final_state'); - - %--------------------------------------------------------------- - % CASE TRIAL_COMPLETED - %--------------------------------------------------------------- - case 'trial_completed' - - - %--------------------------------------------------------------- - % CASE UPDATE - %--------------------------------------------------------------- - case 'update' - pe = parsed_events; %#ok - linenames = value(LineNames); - - for i = 1:numel(linenames) - poketimes = eval(['pe.pokes.',linenames(i)]); - if ~isempty(poketimes) && isnan(poketimes(end,2)) - set(get_ghandle(eval(['Input',linenames(i)])),'BackgroundColor',[1,0,0]); - - str = get(get_ghandle(eval(['Input',linenames(i)])),'string'); - str{2} = size(poketimes,1); - set(get_ghandle(eval(['Input',linenames(i)])),'string',str); - - else - set(get_ghandle(eval(['Input',linenames(i)])),'BackgroundColor',[0,1,0]); - end - end - - %--------------------------------------------------------------- - % CASE CLOSE - %--------------------------------------------------------------- - case 'close' - - dispatcher('Stop'); - - %Let's pause until we know dispatcher is done running - set(value(stopping_complete_timer),'TimerFcn',[mfilename,'(''close_continued'');']); - start(value(stopping_complete_timer)); - - case 'close_continued' - - if value(stopping_process_completed) - stop(value(stopping_complete_timer)); %Stop looping. - %dispatcher('set_protocol',''); - - if exist('myfig', 'var') && isa(myfig, 'SoloParamHandle') && ishandle(value(myfig)), %#ok - delete(value(myfig)); - end - delete_sphandle('owner', ['^@' class(obj) '$']); - dispatcher('set_protocol',''); - end - - otherwise - - warning('Unknown action! "%s"\n', action); %#ok - end - -return; - -end - - - - - - - - - - - - - - - - -function [normbase]=singlenoise(sigma_1,T,fcut,Fs,filter_type) - -%%%%%%%%%%%%%%%%% Determines of the type of filter used %%%%%%%%%%%%%%%%%%% -%'LPFIR': lowpass FIR%%%%%'FIRLS': Least square linear-phase FIR filter design -%'BUTTER': IIR Butterworth lowpass filter%%%%%%'GAUS': Gaussian filter (window) -%'MOVAVRG': Moving average FIR filter%%%%%%%%'KAISER': Kaiser-window FIR filtering -% 'EQUIRIP':Eqiripple FIR filter%%%%% 'HAMMING': Hamming-window based FIR -% T is duration of each signal in milisecond, fcut is the cut-off frequency -% Fs is the sampling frequency -% outband=40; -% filter_type='BUTTER'; - -outband=60; -replace=1; -L=floor(T*Fs);% Length of signal - -%%%%%%%%%%% produce position values %%%%%%% -pos1 = sigma_1 * randn(Fs,1); - -% pos1(pos1>outband)=[]; -% pos1(pos1<-outband)=[]; - -base = randsample(pos1,L,replace); -%%%% Filter the original position values %%%%%% -%filtbase=filt(base,fcut,Fs,filter_type); -hf = design(fdesign.bandpass('N,F3dB1,F3dB2',10,fcut(1),fcut(2),Fs)); -filtbase=filter(hf,base); -normbase=filtbase./(max(abs(filtbase))); - -end - - -function [base,target,normbase,normtarget]=noisestim(sigma_1,sigma_2,T,fcut,Fs,filter_type) - -%%%%%%%%%%%%%%%%% Determines of the type of filter used %%%%%%%%%%%%%%%%%%% -%'LPFIR': lowpass FIR%%%%%'FIRLS': Least square linear-phase FIR filter design -%'BUTTER': IIR Butterworth lowpass filter%%%%%%'GAUS': Gaussian filter (window) -%'MOVAVRG': Moving average FIR filter%%%%%%%%'KAISER': Kaiser-window FIR filtering -% 'EQUIRIP':Eqiripple FIR filter%%%%% 'HAMMING': Hamming-window based FIR -% T is duration of each signal in milisecond, fcut is the cut-off frequency -% Fs is the sampling frequency -% outband=40; -replace=1; -L=floor(T*Fs); % Length of signal -% t=L*linspace(0,1,L)/Fs; % time in miliseconds -%%%%%%%%%%% produce position values %%%%%%% -pos1 = sigma_1*randn(Fs,1); -% pos1(pos1>outband)=[]; -% pos1(pos1<-outband)=[]; - -pos2 =sigma_2*randn(Fs,1); -% pos2(pos2>outband)=[]; -% pos2(pos2<-outband)=[]; -base = randsample(pos1,L,replace); -target = randsample(pos2,L,replace); -%%%% Filter the original position values %%%%%% -filtbase=filt(base,fcut,Fs,filter_type); -filttarget=filt(target,fcut,Fs,filter_type); -normbase=filtbase./(max(abs(filtbase))); -normtarget=filttarget./(max(abs(filttarget))); - -end - - - - -function filtsignal=filt(signal,fcut,Fs,filter_type) -a=2; % wp/ws used in butterworth method and LS linear FIR method -N=200; % filter order used in lowpass FIR method -rp=3; % passband ripple in dB used in butterworth method -rs=60; % stopband attenuation in dB used in butterworth method -beta=0.1102*(rs-8.8); %used in Kaiser window to obtain sidelobe attenuation of rs dB -if strcmp(filter_type, 'GAUS') || strcmp(filter_type, 'MOVAVRG') -window = fix(Fs/fcut); % window size used in Gaussian and moving average methods -end -wp=2*fcut/Fs; % normalized passband corner frequency wp, the cutoff frequency -ws=a*wp; % normalized stopband corner frequency - -switch filter_type - case 'BUTTER' %Butterworth IIR filter - if length(wp)>1 - ws(1)=2*(fcut(1)/2)/Fs; - ws(2)=2*(fcut(2)+fcut(1)/2)/Fs; - [n,wn]=buttord(wp,ws,rp,rs); - [b,a]=butter(n,wn,'bandpass'); - else - [n,wn]=buttord(wp,ws,rp,rs); - [b,a]=butter(n,wn,'low'); - end - filtsignal=filter(b,a,signal);%conventional filtering - case 'LPFIR' %Lowpass FIR filter - d=fdesign.lowpass('N,Fc',N,fcut,Fs); % Fc is the 6-dB down point, N is the filter order(N+1 filter coefficients) - Hd = design(d); - filtsignal=filter(Hd.Numerator,1,signal); %conventional filtering - case 'FIRLS' %Least square linear-phase FIR filter design - b=firls(255,[0 2*fcut/Fs a*2*fcut/Fs 1],[1 1 0 0]); - filtsignal=filter(b,1,signal); %conventional filtering - case 'EQUIRIP' %Eqiripple FIR filter - d=fdesign.lowpass('Fp,Fst,Ap,Ast',wp,ws,rp,rs); - Hd=design(d,'equiripple'); - filtsignal=filter(Hd.Numerator,1,signal); %conventional filtering - case 'MOVAVRG' % Moving average FIR filtering, Rectangular window - h = ones(window,1)/window; - b = fir1(window-1,wp,h); - filtsignal = filter(b, 1, signal); - case 'HAMMING' % Hamming-window based FIR filtering - b = fir1(150,wp); - filtsignal = filter(b, 1, signal); - filtsignal = filter(h, 1, signal); - case 'GAUS' % Gaussian-window FIR filtering - h = normpdf(1:window, 0, fix(window/2)); - b = fir1(window-1,wp,h); - filtsignal = filter(b, 1, signal); - case 'GAUS1' % Gaussian-window FIR filtering - b = fir1(window-1,wp,gausswin(window,2)/window); - filtsignal = filter(b, 1, signal); - case 'KAISER' %Kaiser-window FIR filtering - h=kaiser(window,beta); - b = fir1(window-1,wp,h); - filtsignal = filter(b, 1, signal); - - otherwise - sprintf('filter_type is wrong!! havaset kojast!!') -end - -end - - - -% MATLAB script for calibrating sound pressure level using Arduino and serial communication in real-time - -% --- Configuration --- -arduinoPort = 'COM3'; % Replace with the actual COM port of your Arduino -baudRate = 115200; -speakerOutputValues = 0:10:100; % Example range of speaker output values -numRepetitions = 3; % Number of times to repeat each measurement -tableTitle = 'Speaker Calibration Data'; % Title for the table - -% --- Initialize Serial Communication with Arduino --- -try - % Create serial port object - arduinoSerial = serial(arduinoPort, 'BaudRate', baudRate); - % Open the serial port - fopen(arduinoSerial); - % Set a timeout to prevent MATLAB from waiting indefinitely - arduinoSerial.Timeout = 10; % in seconds - % Read the "Arduino Ready" message - arduinoReady = fgetl(arduinoSerial); - if ~strcmp(arduinoReady, 'Arduino Ready') - error('MATLAB:ArduinoNotReady', 'Arduino did not send the "Ready" signal. Check the Arduino code and connection.'); - end - disp('Arduino connected and ready.'); - - % --- Create Figure and Table --- - fig = figure('Name', tableTitle, 'NumberTitle', 'off'); - % Create an empty table in the figure - hTable = uitable(fig, 'Data', zeros(0, numRepetitions + 1), ... - 'ColumnName', [{'SpeakerValue'}, arrayfun(@(i) ['SPL_Rep' num2str(i)], 1:numRepetitions, 'UniformOutput', false)], ... - 'RowName', [], ... - 'Position', [20 20 400 300]); % Adjust position as needed - - % --- Preallocate Data Storage (for efficiency) --- - numValues = length(speakerOutputValues); - dataMatrix = zeros(numValues * numRepetitions, numRepetitions + 1); % +1 for speakerValue - - % --- Calibration Loop --- - disp('Starting calibration...'); - for i = 1:numValues - speakerValue = speakerOutputValues(i); - disp(['Setting speaker output to: ' num2str(speakerValue)]); - - % Store the speaker value in the data matrix - dataMatrix((i - 1) * numRepetitions + 1:i * numRepetitions, 1) = speakerValue; - - for j = 1:numRepetitions - fprintf(' Repetition %d: ', j); - % *** Replace this with your function to control the speaker in MATLAB *** - setSpeakerLevel(speakerValue); % Call the speaker control function - % ---------------------------------------------------------------------- - - % Read the SPL value from Arduino - try - splString = fgetl(arduinoSerial); - splValue = str2double(splString); - if isnan(splValue) - error('MATLAB:InvalidSPLValue', 'Received non-numeric SPL value from Arduino.'); - end - disp(['Received SPL: ' num2str(splValue) ' dB']); - dataMatrix((i - 1) * numRepetitions + j, j + 1) = splValue; % Store in matrix - - catch ME - % Handle errors, such as timeout or non-numeric data - disp(['Error: ' ME.message]); - if (strcmp(ME.identifier, 'MATLAB:serial:fread:timeout')) - disp(' Timeout occurred while waiting for data from Arduino.'); - elseif (strcmp(ME.identifier, 'MATLAB:InvalidSPLValue')) - disp(' Non-numeric data received from Arduino.'); - end - % Consider adding a 'continue' here to proceed to the next repetition - % even if one fails. Otherwise, the script will stop. - continue; % Add this to continue to the next iteration - end - pause(0.5); % Short pause - - % --- Update the Table in the Figure --- - % Create a temporary table and update the figure's table - tempTable = array2table(dataMatrix(1:i * numRepetitions, :), 'VariableNames', [{'SpeakerValue'}, arrayfun(@(i) ['SPL_Rep' num2str(i)], 1:numRepetitions, 'UniformOutput', false)]); - set(hTable, 'Data', tempTable); - drawnow; % Force the figure to update - end - end - - % --- Close Serial Port --- - fclose(arduinoSerial); - delete(arduinoSerial); - clear arduinoSerial; - - % --- Display Results in a Table --- - calibrationTable = array2table(dataMatrix, 'VariableNames', [{'SpeakerValue'}, arrayfun(@(i) ['SPL_Rep' num2str(i)], 1:numRepetitions, 'UniformOutput', false)]); - disp(tableTitle); - disp(calibrationTable); - - % --- Optional: Plot Calibration Data --- - figure; % Create a new figure for the plot - plot(calibrationTable.SpeakerValue, calibrationTable{:, 2:end}, '-o'); % Plot all repetitions - xlabel('Speaker Output Value'); - ylabel('SPL (dB)'); - title('Speaker Calibration Curve'); - legend([columnNames{2:end}]); - grid on; - - % --- Optional: Polynomial Fit and Display Equation --- - degree = 2; % You can change the degree of the polynomial - p = polyfit(calibrationTable.SpeakerValue, mean(calibrationTable{:, 2:end}, 2), degree); % Fit to the *mean* SPL - fittedSPL = polyval(p, calibrationTable.SpeakerValue); - hold on; - plot(calibrationTable.SpeakerValue, fittedSPL, 'r-', 'LineWidth', 2); % Plot the fit - hold off; - % Display the polynomial equation - equationString = poly2str(p, 'x'); % Use a helper function (defined below) - disp(['Polynomial fit equation: SPL = ' equationString]); - -catch ME - % Handle any errors that occur during the process - disp(['An error occurred: ' ME.message]); - % Clean up the serial port if it was opened - if exist('arduinoSerial', 'var') && isvalid(arduinoSerial) - fclose(arduinoSerial); - delete(arduinoSerial); - clear arduinoSerial; - end -end - -% --- Function to simulate setting the speaker level (REPLACE THIS) --- -function setSpeakerLevel(value) - % Replace this with the actual commands or functions to control the speaker. - % This is just a placeholder for your specific speaker control mechanism. - disp(['(Simulating setting speaker level to: ' num2str(value), ')']); - pause(1); % Simulate speaker level change -end - -% --- Helper function to convert polynomial coefficients to a string --- -function equationString = poly2str(p, varName) - % Converts a polynomial coefficient vector (as returned by polyfit) - % to a string representation of the polynomial equation. - % - % Example: - % p = [1 2 3]; % Coefficients for 1x^2 + 2x + 3 - % varName = 'x'; - % equationString = poly2str(p, varName); % Returns '1x^2 + 2x + 3' - - n = length(p); - equationString = ''; - for i = 1:n - coeff = p(i); - if coeff ~= 0 - if ~isempty(equationString) - equationString = [equationString, ' + ']; % Use '+' sign (except for the first term) - end - if coeff == 1 && i < n %suppress 1 - % equationString = [equationString, varName]; - elseif coeff ~= 1 - equationString = [equationString, num2str(coeff)]; - end - - if i < n - equationString = [equationString, varName]; - if i < n - 1 - equationString = [equationString, '^', num2str(n - i)]; - end - elseif coeff ~= 0 - equationString = [equationString, num2str(coeff)]; - end - end - end - % Replace "+ -" with "-" - equationString = strrep(equationString, '+ -', '-'); -end From 02bd09b83c27b564d4e0b366f8175e1c43052cc9 Mon Sep 17 00:00:00 2001 From: viktorpm Date: Tue, 6 May 2025 18:21:53 +0100 Subject: [PATCH 082/164] debug --- .../ArpitCentrePokeTraining.m | 6 +- .../Training_Performance_Summary.m | 94 +++++++++--------- ...eTraining_experimenter_ratname_250506a.mat | Bin 0 -> 15767 bytes 3 files changed, 52 insertions(+), 48 deletions(-) create mode 100644 Protocols/settings_@ArpitCentrePokeTraining_experimenter_ratname_250506a.mat diff --git a/Protocols/@ArpitCentrePokeTraining/ArpitCentrePokeTraining.m b/Protocols/@ArpitCentrePokeTraining/ArpitCentrePokeTraining.m index a7597c6c..e4e86785 100644 --- a/Protocols/@ArpitCentrePokeTraining/ArpitCentrePokeTraining.m +++ b/Protocols/@ArpitCentrePokeTraining/ArpitCentrePokeTraining.m @@ -181,9 +181,9 @@ [x, y] = StimulusSection(obj,'init',x,y); next_row(y);next_row(y); - [x, y] = SessionPerformanceSection(obj, 'init', x, y); - [x, y] = Training_Performance_Summary(obj, 'init', x, y); - + + [x, y] = Training_Performance_Summary(obj, 'init', x, y);next_row(y); + [x, y] = SessionPerformanceSection(obj, 'init', x, y); next_row(y);next_row(y); ToggleParam(obj, 'Connect_Camera', 1, x,y,... 'OnString', 'Camera On',... diff --git a/Protocols/@ArpitCentrePokeTraining/Training_Performance_Summary.m b/Protocols/@ArpitCentrePokeTraining/Training_Performance_Summary.m index 096809e1..f7af8aa4 100644 --- a/Protocols/@ArpitCentrePokeTraining/Training_Performance_Summary.m +++ b/Protocols/@ArpitCentrePokeTraining/Training_Performance_Summary.m @@ -78,7 +78,7 @@ figure(parentfig); %% Evaluate - case evaluate + case 'evaluate' switch value(training_stage) @@ -89,19 +89,19 @@ stage_1_Trials.value = value(stage_1_Trials) + 1; stage_1_TrialsToday.value = value(stage_1_TrialsToday) + 1; if value(previous_sides(end)) ~= value(ThisTrial) - trial_oppSide.value = value(trial_oppSide) + 1; % updating value for variable in TrainingParams_Section + trial_oppSide = trial_oppSide + 1; % updating value for variable in TrainingParams_Section end stage_1_ViolationRate.value = nan; stage_1_TimeoutRate.value = nan; if value(hit_history(end)) == 1 - stage_1_TrialsValid.value = value(stage_1_TrialsToday) + 1; + stage_1_TrialsValid.value = value(stage_1_TrialsValid) + 1; end % Updating Disp Values for SessionPeformance Section as % well - ntrials_stage.value = value(stage_1_Trials); - ntrials_stage_today.value = value(stage_1_TrialsToday); - violation_stage.value = value(stage_1_ViolationRate); - timeout_stage.value = value(stage_1_TimeoutRate); + ntrials_stage = value(stage_1_Trials); + ntrials_stage_today = value(stage_1_TrialsToday); + violation_stage = value(stage_1_ViolationRate); + timeout_stage = value(stage_1_TimeoutRate); end case 2 @@ -111,19 +111,19 @@ stage_2_Trials.value = value(stage_2_Trials) + 1; stage_2_TrialsToday.value = value(stage_2_TrialsToday) + 1; if value(previous_sides(end)) ~= value(ThisTrial) && all(hit_history(end-1:end)) % last and present trials should also be a valid trial - trial_oppSide.value = value(trial_oppSide) + 1; % updating value for variable in TrainingParams_Section + trial_oppSide = value(trial_oppSide) + 1; % updating value for variable in TrainingParams_Section end stage_2_ViolationRate.value = nan; stage_2_TimeoutRate.value = ((value(stage_2_TimeoutRate) * (value(stage_2_Trials) - 1)) + double(timeout_history(end))) / value(stage_2_Trials); if value(hit_history(end)) == 1 - stage_2_TrialsValid.value = value(stage_2_TrialsToday) + 1; + stage_2_TrialsValid.value = value(stage_2_TrialsValid) + 1; end % Updating Disp Values for SessionPeformance Section as % well - ntrials_stage.value = value(stage_2_Trials); - ntrials_stage_today.value = value(stage_2_TrialsToday); - violation_stage.value = value(stage_2_ViolationRate); - timeout_stage.value = value(stage_2_TimeoutRate); + ntrials_stage = value(stage_2_Trials); + ntrials_stage_today = value(stage_2_TrialsToday); + violation_stage = value(stage_2_ViolationRate); + timeout_stage = value(stage_2_TimeoutRate); end case 3 @@ -131,18 +131,22 @@ if n_completed_trials > 0 stage_3_Trials.value = value(stage_3_Trials) + 1; - stage_1_TrialsToday.value = value(stage_3_TrialsToday) + 1; + stage_3_TrialsToday.value = value(stage_3_TrialsToday) + 1; stage_3_ViolationRate.value = ((value(stage_3_ViolationRate) * (value(stage_3_Trials) - 1)) + double(violation_history(end))) / value(stage_3_Trials); stage_3_TimeoutRate.value = ((value(stage_3_TimeoutRate) * (value(stage_3_Trials) - 1)) + double(timeout_history(end))) / value(stage_3_Trials); if value(hit_history(end)) == 1 - stage_3_TrialsValid.value = value(stage_3_TrialsToday) + 1; + stage_3_TrialsValid.value = value(stage_3_TrialsValid) + 1; end % Updating Disp Values for SessionPeformance Section as % well - ntrials_stage.value = value(stage_3_Trials); - ntrials_stage_today.value = value(stage_3_TrialsToday); - violation_stage.value = value(stage_3_ViolationRate); - timeout_stage.value = value(stage_3_TimeoutRate); + ntrials_stage = value(stage_3_Trials); + callback(ntrials_stage); + ntrials_stage_today = value(stage_3_TrialsToday); + callback(ntrials_stage_today); + violation_stage = value(stage_3_ViolationRate); + callback(violation_stage); + timeout_stage = value(stage_3_TimeoutRate); + callback(timeout_stage); end case 4 @@ -154,14 +158,14 @@ stage_4_ViolationRate.value = ((value(stage_4_ViolationRate) * (value(stage_4_Trials) - 1)) + double(violation_history(end))) / value(stage_4_Trials); stage_4_TimeoutRate.value = ((value(stage_4_TimeoutRate) * (value(stage_4_Trials) - 1)) + double(timeout_history(end))) / value(stage_4_Trials); if value(hit_history(end)) == 1 - stage_4_TrialsValid.value = value(stage_4_TrialsToday) + 1; + stage_4_TrialsValid.value = value(stage_4_TrialsValid) + 1; end % Updating Disp Values for SessionPeformance Section as % well - ntrials_stage.value = value(stage_4_Trials); - ntrials_stage_today.value = value(stage_4_TrialsToday); - violation_stage.value = value(stage_4_ViolationRate); - timeout_stage.value = value(stage_4_TimeoutRate); + ntrials_stage = value(stage_4_Trials); + ntrials_stage_today = value(stage_4_TrialsToday); + violation_stage = value(stage_4_ViolationRate); + timeout_stage = value(stage_4_TimeoutRate); end case 5 @@ -173,14 +177,14 @@ stage_5_ViolationRate.value = ((value(stage_5_ViolationRate) * (value(stage_5_Trials) - 1)) + double(violation_history(end))) / value(stage_5_Trials); stage_5_TimeoutRate.value = ((value(stage_5_TimeoutRate) * (value(stage_5_Trials) - 1)) + double(timeout_history(end))) / value(stage_5_Trials); if value(hit_history(end)) == 1 - stage_5_TrialsValid.value = value(stage_5_TrialsToday) + 1; + stage_5_TrialsValid.value = value(stage_5_TrialsValid) + 1; end % Updating Disp Values for SessionPeformance Section as % well - ntrials_stage.value = value(stage_5_Trials); - ntrials_stage_today.value = value(stage_5_TrialsToday); - violation_stage.value = value(stage_5_ViolationRate); - timeout_stage.value = value(stage_5_TimeoutRate); + ntrials_stage = value(stage_5_Trials); + ntrials_stage_today = value(stage_5_TrialsToday); + violation_stage = value(stage_5_ViolationRate); + timeout_stage = value(stage_5_TimeoutRate); end case 6 @@ -192,14 +196,14 @@ stage_6_ViolationRate.value = ((value(stage_6_ViolationRate) * (value(stage_6_Trials) - 1)) + double(violation_history(end))) / value(stage_6_Trials); stage_6_TimeoutRate.value = ((value(stage_6_TimeoutRate) * (value(stage_6_Trials) - 1)) + double(timeout_history(end))) / value(stage_6_Trials); if value(hit_history(end)) == 1 - stage_6_TrialsValid.value = value(stage_6_TrialsToday) + 1; + stage_6_TrialsValid.value = value(stage_6_TrialsValid) + 1; end % Updating Disp Values for SessionPeformance Section as % well - ntrials_stage.value = value(stage_6_Trials); - ntrials_stage_today.value = value(stage_6_TrialsToday); - violation_stage.value = value(stage_6_ViolationRate); - timeout_stage.value = value(stage_6_TimeoutRate); + ntrials_stage = value(stage_6_Trials); + ntrials_stage_today = value(stage_6_TrialsToday); + violation_stage = value(stage_6_ViolationRate); + timeout_stage = value(stage_6_TimeoutRate); end case 7 @@ -211,14 +215,14 @@ stage_7_ViolationRate.value = ((value(stage_7_ViolationRate) * (value(stage_7_Trials) - 1)) + double(violation_history(end))) / value(stage_7_Trials); stage_7_TimeoutRate.value = ((value(stage_7_TimeoutRate) * (value(stage_7_Trials) - 1)) + double(timeout_history(end))) / value(stage_7_Trials); if value(hit_history(end)) == 1 - stage_7_TrialsValid.value = value(stage_7_TrialsToday) + 1; + stage_7_TrialsValid.value = value(stage_7_TrialsValid) + 1; end % Updating Disp Values for SessionPeformance Section as % well - ntrials_stage.value = value(stage_7_Trials); - ntrials_stage_today.value = value(stage_7_TrialsToday); - violation_stage.value = value(stage_7_ViolationRate); - timeout_stage.value = value(stage_7_TimeoutRate); + ntrials_stage = value(stage_7_Trials); + ntrials_stage_today = value(stage_7_TrialsToday); + violation_stage = value(stage_7_ViolationRate); + timeout_stage = value(stage_7_TimeoutRate); end case 8 @@ -230,14 +234,14 @@ stage_8_ViolationRate.value = ((value(stage_8_ViolationRate) * (value(stage_8_Trials) - 1)) + double(violation_history(end))) / value(stage_8_Trials); stage_8_TimeoutRate.value = ((value(stage_8_TimeoutRate) * (value(stage_8_Trials) - 1)) + double(timeout_history(end))) / value(stage_8_Trials); if value(hit_history(end)) == 1 - stage_8_TrialsValid.value = value(stage_8_TrialsToday) + 1; + stage_8_TrialsValid.value = value(stage_8_TrialsValid) + 1; end % Updating Disp Values for SessionPeformance Section as % well - ntrials_stage.value = value(stage_8_Trials); - ntrials_stage_today.value = value(stage_8_TrialsToday); - violation_stage.value = value(stage_8_ViolationRate); - timeout_stage.value = value(stage_8_TimeoutRate); + ntrials_stage = value(stage_8_Trials); + ntrials_stage_today = value(stage_8_TrialsToday); + violation_stage = value(stage_8_ViolationRate); + timeout_stage = value(stage_8_TimeoutRate); end end @@ -266,7 +270,7 @@ %% Case Show_hide case 'show_hide' - if SummaryShow == 1 + if value(SummaryShow) == 1 set(value(myfig), 'Visible', 'on'); else set(value(myfig), 'Visible', 'off'); diff --git a/Protocols/settings_@ArpitCentrePokeTraining_experimenter_ratname_250506a.mat b/Protocols/settings_@ArpitCentrePokeTraining_experimenter_ratname_250506a.mat new file mode 100644 index 0000000000000000000000000000000000000000..24c98418a8569453708ae83132c41b50048f0075 GIT binary patch literal 15767 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 Date: Tue, 6 May 2025 18:52:02 +0100 Subject: [PATCH 083/164] debug --- .../@ArpitCentrePokeTraining/ParamsSection.m | 4 +- .../SessionPerformanceSection.m | 9 ++- .../Training_ParamsSection.m | 4 -- .../Training_Performance_Summary.m | 61 +++---------------- 4 files changed, 16 insertions(+), 62 deletions(-) diff --git a/Protocols/@ArpitCentrePokeTraining/ParamsSection.m b/Protocols/@ArpitCentrePokeTraining/ParamsSection.m index 00e2fcf7..f354bee7 100644 --- a/Protocols/@ArpitCentrePokeTraining/ParamsSection.m +++ b/Protocols/@ArpitCentrePokeTraining/ParamsSection.m @@ -177,9 +177,9 @@ {'training_stage'}); SoloFunctionAddVars('Training_Performance_Summary', 'ro_args', ... - {'training_stage';'ThisTrial'}); + {'training_stage'}); - SoloParamHandle(obj, 'previous_parameters', 'value', []); + % SoloParamHandle(obj, 'previous_parameters', 'value', []); diff --git a/Protocols/@ArpitCentrePokeTraining/SessionPerformanceSection.m b/Protocols/@ArpitCentrePokeTraining/SessionPerformanceSection.m index b01c05b1..5375c7eb 100644 --- a/Protocols/@ArpitCentrePokeTraining/SessionPerformanceSection.m +++ b/Protocols/@ArpitCentrePokeTraining/SessionPerformanceSection.m @@ -15,7 +15,7 @@ % and reinit, at the same position on the same % figure as the original section GUI was placed. % -% 'evalueta' Look at history and compute hit fraction, etc. +% 'evaluate' Look at history and compute hit fraction, etc. % % x, y Only relevant to action = 'init'; they indicate the initial % position to place the GUI at, in the current figure window @@ -85,8 +85,14 @@ % ------------------------------------------------------------------ case 'evaluate' + + eval(sprintf('ntrials_stage.value = value(stage_%i_Trials);',value(training_stage))); + eval(sprintf('ntrials_stage_today.value = value(stage_%i_TrialsToday);',value(training_stage))); + eval(sprintf('ntrials_violation_stage.value = value(stage_%i_ViolationRate);',value(training_stage))); + eval(sprintf('ntrials_violation_stage.value = value(stage_%i_TimeoutRate);',value(training_stage))); switch value(training_stage) + case 1 %% center led on -> poke in the center -> go cue -> reward light and sound if n_completed_trials > 1 ntrials.value = n_completed_trials; @@ -98,7 +104,6 @@ violation_stage.value = nan; timeout_stage.value = nan; - case {2,3} %% center led on -> poke in the center -> go cue -> reward light and sound if n_completed_trials > 1 diff --git a/Protocols/@ArpitCentrePokeTraining/Training_ParamsSection.m b/Protocols/@ArpitCentrePokeTraining/Training_ParamsSection.m index 124eb506..e6bf8204 100644 --- a/Protocols/@ArpitCentrePokeTraining/Training_ParamsSection.m +++ b/Protocols/@ArpitCentrePokeTraining/Training_ParamsSection.m @@ -25,8 +25,6 @@ NumeditParam(obj, 'total_trials_opp', 150, x, y,'label','Trials_Opp','TooltipString','total trials with opposide side option in this stage for its completion'); next_row(y); SubheaderParam(obj, 'title', 'Completion Params', x, y); next_row(y); - SoloFunctionAddVars('Training_Performance_Summary', 'ro_args', {'trial_oppSide'}); - case 2 % STAGE RUNNING PARAMETERS DispParam(obj, 'trial_oppSide', 0, x, y,'save_with_settings', 1,'TooltipString','total trials in opposide side this stage'); next_row(y); @@ -37,8 +35,6 @@ NumeditParam(obj, 'total_trials', 1000, x, y,'label','Trials','TooltipString','total trials in this stage for its completion'); next_row(y); NumeditParam(obj, 'total_trials_opp', 400, x, y,'label','Trials_Opp','TooltipString','total trials with opposide side option in this stage for its completion'); next_row(y); SubheaderParam(obj, 'title', 'Completion Params', x, y);next_row(y); - - SoloFunctionAddVars('Training_Performance_Summary', 'ro_args', {'trial_oppSide'}); case 3 % no completion test required % STAGE RUNNING PARAMETERS diff --git a/Protocols/@ArpitCentrePokeTraining/Training_Performance_Summary.m b/Protocols/@ArpitCentrePokeTraining/Training_Performance_Summary.m index f7af8aa4..79ad4c8f 100644 --- a/Protocols/@ArpitCentrePokeTraining/Training_Performance_Summary.m +++ b/Protocols/@ArpitCentrePokeTraining/Training_Performance_Summary.m @@ -77,6 +77,9 @@ x=oldx; y=oldy; figure(parentfig); + SoloFunctionAddVars('SessionPerformanceSection', 'ro_args', ... + {variable_names}); + %% Evaluate case 'evaluate' @@ -87,10 +90,7 @@ if n_completed_trials > 0 stage_1_Trials.value = value(stage_1_Trials) + 1; - stage_1_TrialsToday.value = value(stage_1_TrialsToday) + 1; - if value(previous_sides(end)) ~= value(ThisTrial) - trial_oppSide = trial_oppSide + 1; % updating value for variable in TrainingParams_Section - end + stage_1_TrialsToday.value = value(stage_1_TrialsToday) + 1; stage_1_ViolationRate.value = nan; stage_1_TimeoutRate.value = nan; if value(hit_history(end)) == 1 @@ -98,10 +98,7 @@ end % Updating Disp Values for SessionPeformance Section as % well - ntrials_stage = value(stage_1_Trials); - ntrials_stage_today = value(stage_1_TrialsToday); - violation_stage = value(stage_1_ViolationRate); - timeout_stage = value(stage_1_TimeoutRate); + end case 2 @@ -118,12 +115,7 @@ if value(hit_history(end)) == 1 stage_2_TrialsValid.value = value(stage_2_TrialsValid) + 1; end - % Updating Disp Values for SessionPeformance Section as - % well - ntrials_stage = value(stage_2_Trials); - ntrials_stage_today = value(stage_2_TrialsToday); - violation_stage = value(stage_2_ViolationRate); - timeout_stage = value(stage_2_TimeoutRate); + end case 3 @@ -137,16 +129,6 @@ if value(hit_history(end)) == 1 stage_3_TrialsValid.value = value(stage_3_TrialsValid) + 1; end - % Updating Disp Values for SessionPeformance Section as - % well - ntrials_stage = value(stage_3_Trials); - callback(ntrials_stage); - ntrials_stage_today = value(stage_3_TrialsToday); - callback(ntrials_stage_today); - violation_stage = value(stage_3_ViolationRate); - callback(violation_stage); - timeout_stage = value(stage_3_TimeoutRate); - callback(timeout_stage); end case 4 @@ -160,12 +142,6 @@ if value(hit_history(end)) == 1 stage_4_TrialsValid.value = value(stage_4_TrialsValid) + 1; end - % Updating Disp Values for SessionPeformance Section as - % well - ntrials_stage = value(stage_4_Trials); - ntrials_stage_today = value(stage_4_TrialsToday); - violation_stage = value(stage_4_ViolationRate); - timeout_stage = value(stage_4_TimeoutRate); end case 5 @@ -179,12 +155,6 @@ if value(hit_history(end)) == 1 stage_5_TrialsValid.value = value(stage_5_TrialsValid) + 1; end - % Updating Disp Values for SessionPeformance Section as - % well - ntrials_stage = value(stage_5_Trials); - ntrials_stage_today = value(stage_5_TrialsToday); - violation_stage = value(stage_5_ViolationRate); - timeout_stage = value(stage_5_TimeoutRate); end case 6 @@ -198,12 +168,6 @@ if value(hit_history(end)) == 1 stage_6_TrialsValid.value = value(stage_6_TrialsValid) + 1; end - % Updating Disp Values for SessionPeformance Section as - % well - ntrials_stage = value(stage_6_Trials); - ntrials_stage_today = value(stage_6_TrialsToday); - violation_stage = value(stage_6_ViolationRate); - timeout_stage = value(stage_6_TimeoutRate); end case 7 @@ -217,12 +181,7 @@ if value(hit_history(end)) == 1 stage_7_TrialsValid.value = value(stage_7_TrialsValid) + 1; end - % Updating Disp Values for SessionPeformance Section as - % well - ntrials_stage = value(stage_7_Trials); - ntrials_stage_today = value(stage_7_TrialsToday); - violation_stage = value(stage_7_ViolationRate); - timeout_stage = value(stage_7_TimeoutRate); + end case 8 @@ -236,12 +195,6 @@ if value(hit_history(end)) == 1 stage_8_TrialsValid.value = value(stage_8_TrialsValid) + 1; end - % Updating Disp Values for SessionPeformance Section as - % well - ntrials_stage = value(stage_8_Trials); - ntrials_stage_today = value(stage_8_TrialsToday); - violation_stage = value(stage_8_ViolationRate); - timeout_stage = value(stage_8_TimeoutRate); end end From eb8813720800eb8a13183b1a8dd6b60db59c1db1 Mon Sep 17 00:00:00 2001 From: Arpit Date: Tue, 6 May 2025 18:57:38 +0100 Subject: [PATCH 084/164] debug --- .../Training_Performance_Summary.asv | 242 ++++++++++++++++++ .../Training_Performance_Summary.m | 9 +- 2 files changed, 250 insertions(+), 1 deletion(-) create mode 100644 Protocols/@ArpitCentrePokeTraining/Training_Performance_Summary.asv diff --git a/Protocols/@ArpitCentrePokeTraining/Training_Performance_Summary.asv b/Protocols/@ArpitCentrePokeTraining/Training_Performance_Summary.asv new file mode 100644 index 00000000..b9a69788 --- /dev/null +++ b/Protocols/@ArpitCentrePokeTraining/Training_Performance_Summary.asv @@ -0,0 +1,242 @@ +function [x, y] = Training_Performance_Summary(obj, action, varargin) + +GetSoloFunctionArgs(obj); + +switch action + + % ------------------------------------------------------------------ + % INIT + % ------------------------------------------------------------------ + + case 'init' + if length(varargin) < 2 + error('Need at least two arguments, x and y position, to initialize %s', mfilename); + end + x = varargin{1}; y = varargin{2}; + + % SoloParamHandle(obj, 'my_xyfig', 'value', [x y double(gcf)]); + + ToggleParam(obj, 'SummaryShow', 0, x, y, 'OnString', 'Summary Show', ... + 'OffString', 'Summary Hidden', 'TooltipString', 'Show/Hide Summary panel'); + set_callback(SummaryShow, {mfilename, 'show_hide'}); %#ok (Defined just above) + next_row(y); + oldx=x; oldy=y; parentfig=double(gcf); + + SoloParamHandle(obj, 'myfig', 'value', figure('Position', [ 226 122 1406 400], ... + 'closerequestfcn', [mfilename '(' class(obj) ', ''hide'');'], 'MenuBar', 'none', ... + 'Name', mfilename), 'saveable', 0); + set(double(gcf), 'Visible', 'off'); + + + % Create stage names + N_Stages = 8; + for i = 1:N_Stages + rowNames{i, 1} = sprintf('Stage %d', i); + end + % Create column names + N_params = 5; + columnNames = {'Stage/Param','Trials','TrialsToday','TrialsValid','ViolationRate','TimeoutRate'}; + + % Create Variable Names for each Edit Box + count = 0; + variable_names = cell(1,N_params * N_Stages); + for j = 1:N_params + for i = 1:N_Stages + count = count + 1; + variable_names(count) = {sprintf('stage_%i_%s',N_Stages - i + 1,columnNames{j+1})}; + end + end + + count = 0; + x = 100; y=100; + + for column_n = 1 : N_params + 1 + for rows_n = 1 : N_Stages + 1 + if column_n == 1 && rows_n < N_Stages + 1 + SubheaderParam(obj, 'title', sprintf('Stage%i',(N_Stages - rows_n + 1)), x, y); + next_row(y); + end + if column_n == 1 && rows_n == N_Stages + 1 + SubheaderParam(obj, 'title', columnNames{1}, x, y); + next_row(y); + end + if column_n > 1 && rows_n < N_Stages + 1 + count = count + 1; + NumeditParam(obj, variable_names{count}, 0, x, y); + next_row(y); + end + if rows_n == N_Stages + 1 && column_n > 1 + SubheaderParam(obj, 'title', columnNames{column_n}, x, y); + next_row(y); + end + end + next_column(x); y=100; + end + + % + x=oldx; y=oldy; + figure(parentfig); + + SoloFunctionAddVars('SessionPerformanceSection', 'ro_args', ... + {'stage_1_Trials';'stage_1_TrialsToday';'stage_1_ViolationRate';'stage_1_TimeoutRate'... + 'stage_1_Trials';'stage_1_TrialsToday';'stage_1_ViolationRate';'stage_1_TimeoutRate' ... + 'stage_1_Trials';'stage_1_TrialsToday';'stage_1_ViolationRate';'stage_1_TimeoutRate'... + 'stage_1_Trials';'stage_1_TrialsToday';'stage_1_ViolationRate';'stage_1_TimeoutRate'... + 'stage_1_Trials';'stage_1_TrialsToday';'stage_1_ViolationRate';'stage_1_TimeoutRate'... + 'stage_1_Trials';'stage_1_TrialsToday';'stage_1_ViolationRate';'stage_1_TimeoutRate'... + 'stage_1_Trials';'stage_1_TrialsToday';'stage_1_ViolationRate';'stage_1_TimeoutRate'... + 'stage_1_Trials';'stage_1_TrialsToday';'stage_1_ViolationRate';'stage_1_TimeoutRate'}); + +%% Evaluate + case 'evaluate' + + switch value(training_stage) + + case 1 + + if n_completed_trials > 0 + + stage_1_Trials.value = value(stage_1_Trials) + 1; + stage_1_TrialsToday.value = value(stage_1_TrialsToday) + 1; + stage_1_ViolationRate.value = nan; + stage_1_TimeoutRate.value = nan; + if value(hit_history(end)) == 1 + stage_1_TrialsValid.value = value(stage_1_TrialsValid) + 1; + end + % Updating Disp Values for SessionPeformance Section as + % well + + end + + case 2 + + if n_completed_trials > 0 + + stage_2_Trials.value = value(stage_2_Trials) + 1; + stage_2_TrialsToday.value = value(stage_2_TrialsToday) + 1; + if value(previous_sides(end)) ~= value(ThisTrial) && all(hit_history(end-1:end)) % last and present trials should also be a valid trial + trial_oppSide = value(trial_oppSide) + 1; % updating value for variable in TrainingParams_Section + end + stage_2_ViolationRate.value = nan; + stage_2_TimeoutRate.value = ((value(stage_2_TimeoutRate) * (value(stage_2_Trials) - 1)) + double(timeout_history(end))) / value(stage_2_Trials); + if value(hit_history(end)) == 1 + stage_2_TrialsValid.value = value(stage_2_TrialsValid) + 1; + end + + end + + case 3 + + if n_completed_trials > 0 + + stage_3_Trials.value = value(stage_3_Trials) + 1; + stage_3_TrialsToday.value = value(stage_3_TrialsToday) + 1; + stage_3_ViolationRate.value = ((value(stage_3_ViolationRate) * (value(stage_3_Trials) - 1)) + double(violation_history(end))) / value(stage_3_Trials); + stage_3_TimeoutRate.value = ((value(stage_3_TimeoutRate) * (value(stage_3_Trials) - 1)) + double(timeout_history(end))) / value(stage_3_Trials); + if value(hit_history(end)) == 1 + stage_3_TrialsValid.value = value(stage_3_TrialsValid) + 1; + end + end + + case 4 + + if n_completed_trials > 0 + + stage_4_Trials.value = value(stage_4_Trials) + 1; + stage_4_TrialsToday.value = value(stage_4_TrialsToday) + 1; + stage_4_ViolationRate.value = ((value(stage_4_ViolationRate) * (value(stage_4_Trials) - 1)) + double(violation_history(end))) / value(stage_4_Trials); + stage_4_TimeoutRate.value = ((value(stage_4_TimeoutRate) * (value(stage_4_Trials) - 1)) + double(timeout_history(end))) / value(stage_4_Trials); + if value(hit_history(end)) == 1 + stage_4_TrialsValid.value = value(stage_4_TrialsValid) + 1; + end + end + + case 5 + + if n_completed_trials > 0 + + stage_5_Trials.value = value(stage_5_Trials) + 1; + stage_5_TrialsToday.value = value(stage_5_TrialsToday) + 1; + stage_5_ViolationRate.value = ((value(stage_5_ViolationRate) * (value(stage_5_Trials) - 1)) + double(violation_history(end))) / value(stage_5_Trials); + stage_5_TimeoutRate.value = ((value(stage_5_TimeoutRate) * (value(stage_5_Trials) - 1)) + double(timeout_history(end))) / value(stage_5_Trials); + if value(hit_history(end)) == 1 + stage_5_TrialsValid.value = value(stage_5_TrialsValid) + 1; + end + end + + case 6 + + if n_completed_trials > 0 + + stage_6_Trials.value = value(stage_6_Trials) + 1; + stage_6_TrialsToday.value = value(stage_6_TrialsToday) + 1; + stage_6_ViolationRate.value = ((value(stage_6_ViolationRate) * (value(stage_6_Trials) - 1)) + double(violation_history(end))) / value(stage_6_Trials); + stage_6_TimeoutRate.value = ((value(stage_6_TimeoutRate) * (value(stage_6_Trials) - 1)) + double(timeout_history(end))) / value(stage_6_Trials); + if value(hit_history(end)) == 1 + stage_6_TrialsValid.value = value(stage_6_TrialsValid) + 1; + end + end + + case 7 + + if n_completed_trials > 0 + + stage_7_Trials.value = value(stage_7_Trials) + 1; + stage_7_TrialsToday.value = value(stage_7_TrialsToday) + 1; + stage_7_ViolationRate.value = ((value(stage_7_ViolationRate) * (value(stage_7_Trials) - 1)) + double(violation_history(end))) / value(stage_7_Trials); + stage_7_TimeoutRate.value = ((value(stage_7_TimeoutRate) * (value(stage_7_Trials) - 1)) + double(timeout_history(end))) / value(stage_7_Trials); + if value(hit_history(end)) == 1 + stage_7_TrialsValid.value = value(stage_7_TrialsValid) + 1; + end + + end + + case 8 + + if n_completed_trials > 0 + + stage_8_Trials.value = value(stage_8_Trials) + 1; + stage_8_TrialsToday.value = value(stage_8_TrialsToday) + 1; + stage_8_ViolationRate.value = ((value(stage_8_ViolationRate) * (value(stage_8_Trials) - 1)) + double(violation_history(end))) / value(stage_8_Trials); + stage_8_TimeoutRate.value = ((value(stage_8_TimeoutRate) * (value(stage_8_Trials) - 1)) + double(timeout_history(end))) / value(stage_8_Trials); + if value(hit_history(end)) == 1 + stage_8_TrialsValid.value = value(stage_8_TrialsValid) + 1; + end + end + end + + +%% Case close + case 'close' + set(value(myfig), 'Visible', 'off'); + % Delete all SoloParamHandles who belong to this object and whose + % fullname starts with the name of this mfile: + if exist('myfig', 'var') && isa(myfig, 'SoloParamHandle') && ishandle(value(myfig)) %#ok + delete(value(myfig)); + end + + delete_sphandle('owner', ['^@' class(obj) '$'], ... + 'fullname', ['^' mfilename]); + + %% Case hide + case 'hide' + SummaryShow.value = 0; + set(value(myfig), 'Visible', 'off'); + + %% Case show + case 'show' + SummaryShow.value = 1; + set(value(myfig), 'Visible', 'on'); + + %% Case Show_hide + case 'show_hide' + if value(SummaryShow) == 1 + set(value(myfig), 'Visible', 'on'); + else + set(value(myfig), 'Visible', 'off'); + end + +end + +end + diff --git a/Protocols/@ArpitCentrePokeTraining/Training_Performance_Summary.m b/Protocols/@ArpitCentrePokeTraining/Training_Performance_Summary.m index 79ad4c8f..f28db682 100644 --- a/Protocols/@ArpitCentrePokeTraining/Training_Performance_Summary.m +++ b/Protocols/@ArpitCentrePokeTraining/Training_Performance_Summary.m @@ -78,7 +78,14 @@ figure(parentfig); SoloFunctionAddVars('SessionPerformanceSection', 'ro_args', ... - {variable_names}); + {'stage_1_Trials';'stage_1_TrialsToday';'stage_1_ViolationRate';'stage_1_TimeoutRate'... + 'stage_2_Trials';'stage_2_TrialsToday';'stage_2_ViolationRate';'stage_2_TimeoutRate' ... + 'stage_3_Trials';'stage_3_TrialsToday';'stage_3_ViolationRate';'stage_3_TimeoutRate'... + 'stage_4_Trials';'stage_4_TrialsToday';'stage_4_ViolationRate';'stage_4_TimeoutRate'... + 'stage_5_Trials';'stage_5_TrialsToday';'stage_5_ViolationRate';'stage_5_TimeoutRate'... + 'stage_6_Trials';'stage_6_TrialsToday';'stage_6_ViolationRate';'stage_6_TimeoutRate'... + 'stage_7_Trials';'stage_7_TrialsToday';'stage_7_ViolationRate';'stage_7_TimeoutRate'... + 'stage_8_Trials';'stage_8_TrialsToday';'stage_8_ViolationRate';'stage_8_TimeoutRate'}); %% Evaluate case 'evaluate' From a9afa0c6865857d3219e635311ed61067fa895a0 Mon Sep 17 00:00:00 2001 From: Arpit Date: Wed, 7 May 2025 16:51:07 +0100 Subject: [PATCH 085/164] added camera as plugin and updating session performance by session definition --- ExperPort/Modules/@runrats/runrats.asv | 2031 ----------------- .../@bonsaicamera/BonsaiCameraInterface.m | 189 +- .../@bonsaicamera}/Camera_Control.bonsai | 0 .../Camera_Control.bonsai.layout | 0 .../Plugins/@bonsaicamera/bonsaicamera.m | 4 + ...itCentrePokeTraining_Arpit_AR1_250427a.mat | Bin 15252 -> 0 bytes ...eTraining_experimenter_ratname_250423a.mat | Bin 15234 -> 0 bytes ...eTraining_experimenter_ratname_250423b.mat | Bin 16109 -> 0 bytes .../ArpitCentrePokeTraining.m | 93 +- ...ing_SessionDefinition_AutoTrainingStages.m | 358 ++- .../@ArpitCentrePokeTraining/ParamsSection.m | 4 +- .../SessionPerformanceSection.m | 60 +- .../Training_Performance_Summary.asv | 242 -- .../Training_Performance_Summary.m | 135 +- 14 files changed, 555 insertions(+), 2561 deletions(-) delete mode 100644 ExperPort/Modules/@runrats/runrats.asv rename Protocols/@ArpitCentrePokeTraining/Connect_Bonsai_Camera.m => ExperPort/Plugins/@bonsaicamera/BonsaiCameraInterface.m (51%) rename {Protocols/@ArpitCentrePokeTraining/Bonsai_Camera_Control => ExperPort/Plugins/@bonsaicamera}/Camera_Control.bonsai (100%) rename {Protocols/@ArpitCentrePokeTraining/Bonsai_Camera_Control => ExperPort/Plugins/@bonsaicamera}/Camera_Control.bonsai.layout (100%) create mode 100644 ExperPort/Plugins/@bonsaicamera/bonsaicamera.m delete mode 100644 ExperPort/settings_@ArpitCentrePokeTraining_Arpit_AR1_250427a.mat delete mode 100644 ExperPort/settings_@ArpitCentrePokeTraining_experimenter_ratname_250423a.mat delete mode 100644 ExperPort/settings_@ArpitCentrePokeTraining_experimenter_ratname_250423b.mat delete mode 100644 Protocols/@ArpitCentrePokeTraining/Training_Performance_Summary.asv diff --git a/ExperPort/Modules/@runrats/runrats.asv b/ExperPort/Modules/@runrats/runrats.asv deleted file mode 100644 index eb91cb95..00000000 --- a/ExperPort/Modules/@runrats/runrats.asv +++ /dev/null @@ -1,2031 +0,0 @@ -% RunRats(varargin) -% This is the GUI for technicians in the lab to use for day to day running -% of rats. The logic of the code follows that of dispatcher. - -% The original RunRats was written bu Jeff Erlich with modifications by -% Sebastien Awwad, 2007-2008 and Sundeep Tuteja, 2009 -% -% This is a complete rewrite written by Chuck Kopec 2011 -% Maintenance features added by Chuck Kopec 2012 -% 9 shift expansion by Chuck Kopec 2013 -% being changed for Akrami Lab Sharbat 2019 - - -function [obj, varargout]=runrats(varargin) - -%Argument handling -obj = class(struct, mfilename, sqlsummary); -varargout = {}; -if nargin==0 || nargin==1 && ischar(varargin{1}) && strcmp(varargin{1}, 'empty'), - return; -end; - -% This line is required for the use of SoloParams -GetSoloFunctionArgs; - -if nargin>=2 && isa(varargin{1}, class(obj)), action = varargin{2}; varargin = varargin(3:end); -else action = varargin{1}; varargin = varargin(2:end); -end; -if ~ischar(action) - error('Runrats expects to be called with a string as the first argument specifying the action to perform. E.g.: runrats(''init'');'); -end - -%Make the action case insensitive -action = lower(action); - -switch action - - case 'physinit' - %% physinit - SoloParamHandle(obj,'phys','value',1); - runrats(obj,'init'); - - case 'init' - %% init - display('test'); - %If we are starting from a forced reboot from runrats itself we - %need to make sure the do_on_reboot.bat file is set back to nothing - try %#ok - p = pwd; - cd('\ratter\Rigscripts') - - !del do_on_reboot.bat - !copy nothing.bat do_on_reboot.bat /Y - cd(p); - end - - %If a dispatcher is already open, let's close it so we don't ever have - %more than 1 - if exist('myfig', 'var') - if isa(myfig, 'SoloParamHandle') && ishandle(value(myfig)), delete(value(myfig)); end; - end - - %Let's see if the rig is broken - isbroken = bdata(['select isbroken from rig_maintenance where rigid="',num2str(bSettings('get','RIGS','Rig_ID')),'" and isbroken=1']); - if isempty(isbroken); isbroken = 0; - else isbroken = 1; - end - SoloParamHandle(obj,'IsBroken','value',isbroken); % - - %Let's see how long it's been since the last calibration - SoloParamHandle(obj,'CalibAge','value',Inf); %Age of the calibration in days - SoloParamHandle(obj,'CalibExp','value',100); - % runrats(obj,'check_calibration'); %we use bpod now, ignore. - - %If the calibration is too old, let's open WaterCalibration for the - %tech, if we are getting near needing a new calibration let's warn them - %If the rig is broken ignore calibration - - techmessage = ''; - % if dispatcher('is_running'); dispatcher('close'); end - % if bSettings('get','WATER','skip_calibration_now') ~= 1 && isbroken == 0 - % if value(CalibAge) >= value(CalibExp) %#ok - % dispatcher('init'); set(double(gcf),'visible','off'); - % dispatcher('set_protocol','WaterCalibration'); - % return; - % elseif value(CalibAge) >= value(CalibExp)-1 - % techmessage = 'WARNING Calibration will expire in 1 day!'; - % elseif value(CalibAge) >= value(CalibExp)-2 - % techmessage = 'Calibration will expire in 2 days,'; - % elseif value(CalibAge) >= value(CalibExp)-3 - % techmessage = 'Calibration will expire in 3 days,'; - % end - % end - % - % Start and hide the dispatcher. - - dispobj=dispatcher('init'); - h=get_sphandle('owner','dispatcher','name','myfig'); - set(value(h{1}), 'Visible','Off'); - SoloParamHandle(obj, 'dispobj','value',dispobj); - - %SoloParamHandle storing the settings file - SoloParamHandle(obj, 'settings_file_sph', 'value', ''); - SoloParamHandle(obj, 'settings_file_load_time', 'value', 0 ); - - - % The main RunRats window should appear in the center of the screen - sr = get(0,'MonitorPositions'); - wh = [650 250]; - pos = [(sr(3)-wh(1))/2,(sr(4)-wh(2))/2,wh(1),wh(2)]; - - fig = figure('Position',pos,'MenuBar','none','ToolBar','none', ... - 'NumberTitle','off','Name','RunRats V2.2 ','Resize','off',... - 'closerequestfcn', [mfilename '(''close'')']); - SoloParamHandle(obj,'myfig', 'value',fig); - - try - set(myfig, 'WindowStyle', 'modal'); - pause(0.1); - - catch %#ok - - disp('WARNING: Failed to keep runrats on top'); - end - - - %Create the non-gui variables we will need - SoloParamHandle(obj,'RigID', 'value',bSettings('get','RIGS','Rig_ID')); %The rig ID - SoloParamHandle(obj,'RatSch', 'value',cell(10,1)); %The rats that run in this rig in session order - SoloParamHandle(obj,'CurrSession', 'value',1); %The session that we should prep to load - SoloParamHandle(obj,'CurrProtocol', 'value',1); %The protocol that the rat runs under - SoloParamHandle(obj,'WasFlushed', 'value',0); %Was the rig flused today - SoloParamHandle(obj,'InLiveLoop', 'value',0); %Let's us know if we should break out of the loop - SoloParamHandle(obj,'CurrRat', 'value',''); %The name of the current rat - SoloParamHandle(obj,'PanelBack', 'value',[]); %handles to all the uicontrols that share the background color - SoloParamHandle(obj,'RigIsBroken', 'value',0); %1 if the rig is broken, 0 if it's not - SoloParamHandle(obj,'DoingMaintenance','value',0); %1 if we are entering a maintenance note - SoloParamHandle(obj,'do_full_restart', 'value',1); %1 if we want to restart matlab between each session - SoloParamHandle(obj,'SafetyMode', 'value',''); %empty for no safety, B for before, A for after - SoloParamHandle(obj,'OptoPlugColor', 'value',[]); %figure handle for opto plug color panel - - if ~exist('phys','var'); SoloParamHandle(obj,'phys','value',0); end - - - SoloParamHandle(obj, 'curProtocol', 'value', ''); %Current Protocol - SoloParamHandle(obj,'schedDay','value',[]); - - % For Live Webcam Feed - SoloParamHandle(obj,'Camera_Fig_window','value',[]); - SoloParamHandle(obj,'Camera_Obj','value',[]); - SoloParamHandle(obj,'Camera_Image','value',[]); - - %Let's make the menus - try - all_experimenters = bdata('select distinct experimenter from rats where extant=1 order by experimenter'); - catch %#ok - disp('ERROR: Unable to connect to MySQL Server'); - all_experimenters = ''; - end - all_experimenters = [{''};all_experimenters]; - - MenuParam(obj,'ExpMenu',all_experimenters,1,0,0,'position',[10 204 200 35],'labelfraction',0.01); - MenuParam(obj,'RatMenu',{''}, 1,0,0,'position',[10 162 200 35],'labelfraction',0.01); - MenuParam(obj,'SchList',{'Not Active'}, 1,0,0,'position',[10 120 200 35],'labelfraction',0.01); - - SubheaderParam(obj,'ExpLbl','Experimenter',0,0,'position',[215 204 170 35]); - SubheaderParam(obj,'RatLbl','Rat', 0,0,'position',[215 162 170 35]); - SubheaderParam(obj,'SchLbl','Schedule', 0,0,'position',[215 120 170 35]); - - set(get_ghandle(ExpMenu),'FontSize',20,'BackgroundColor',[1 1 1]); %#ok - set(get_ghandle(RatMenu),'FontSize',20,'BackgroundColor',[1 1 1]); %#ok - set(get_ghandle(SchList),'FontSize',16,'BackgroundColor',[1 1 1],'FontName','Courier','FontWeight','bold'); %#ok - - set(get_ghandle(ExpLbl),'FontSize',20,'BackgroundColor',[1,0.8,0.6],'HorizontalAlignment','left'); - set(get_ghandle(RatLbl),'FontSize',20,'BackgroundColor',[1,0.8,0.6],'HorizontalAlignment','left'); - set(get_ghandle(SchLbl),'FontSize',20,'BackgroundColor',[1,0.8,0.6],'HorizontalAlignment','left'); - - set_callback(ExpMenu,{mfilename,'expmenu_callback'}); - set_callback(RatMenu,{mfilename,'ratmenu_callback'}); - set_callback(SchList,{mfilename,'schlist_callback'}); - - - %Let's make the buttons - PushbuttonParam(obj,'FlushValves',0,0,'position',[ 10,50,140, 25],'BackgroundColor',[0.6 1 1 ],'label','Flush Valves'); - PushbuttonParam(obj,'TestImplant',0,0,'position',[155,50,140, 25],'BackgroundColor',[1 0.6 1 ],'label','Test Implant'); - PushbuttonParam(obj,'UpdateMode' ,0,0,'position',[300,50,170, 25],'BackgroundColor',[0.6 1 0.6],'label','Live Update On'); - PushbuttonParam(obj,'Maintenance',0,0,'position',[475,50,165, 25],'BackgroundColor',[1 0.3 0.1],'label','Maintenance'); - PushbuttonParam(obj,'Multi', 0,0,'position',[400,85,240,155],'BackgroundColor',[1 1 0.4],'label','Load Protocol'); - - set(get_ghandle(FlushValves),'Fontsize',16); - set(get_ghandle(TestImplant),'Fontsize',16); - set(get_ghandle(UpdateMode), 'Fontsize',16); - set(get_ghandle(Maintenance),'Fontsize',16); - set(get_ghandle(Multi), 'Fontsize',24); - - set_callback(FlushValves,{mfilename,'flush_valves'}); - set_callback(TestImplant,{mfilename,'test_implant'}); - set_callback(UpdateMode, {mfilename,'updatemode_button'}); - set_callback(Maintenance,{mfilename,'rig_maintenance'}); - set_callback(Multi, {mfilename,'multi_button'}); - - %Hide the Test Implant button if this rig isn't set up to do this - alldio=bSettings('get','DIOLINES','ALL'); - if isempty(alldio) || (sum(strcmp(alldio(:,1),'stim1')) == 0 && sum(strcmp(alldio(:,1),'LASER')) == 0) &&... - (sum(strcmp(alldio(:,1),'BLUE')) == 0 && sum(strcmp(alldio(:,1),'GREEN')) == 0) &&... - (sum(strcmp(alldio(:,1),'YELLOW')) == 0 && sum(strcmp(alldio(:,1),'RED')) == 0) - set(get_ghandle(TestImplant),'visible','off'); - end - - - %Let's make the display areas - DispParam(obj,'Instructions','', 0,0,'position',[10,80,380,40],'label','','labelfraction',0.005); - DispParam(obj,'StatusBar', techmessage,0,0,'position',[ 1, 1,650,50],'label','','labelfraction',0.003); - - %This is intended to be a copy of the instructions field which will - %require the tech to click on if the experimenter wants it - PushbuttonParam(obj,'Safety', 0,0,'position',[10,80,380,40],'label',''); - set_callback(Safety,{mfilename,'safety_button'}); - - set(get_ghandle(Instructions),'Fontsize',14,'BackgroundColor',[1,0.8,0.6],'HorizontalAlignment','left','FontWeight','bold'); %#ok - set(get_ghandle(StatusBar), 'Fontsize',14,'BackgroundColor',[0.9,0.9,1],'HorizontalAlignment','center'); %#ok - set(get_ghandle(Safety), 'Fontsize',14,'BackgroundColor',[1,0.8,0.6],'HorizontalAlignment','left','FontWeight','bold','visible','off'); - - - c = get(double(gcf),'children'); - set(c(end),'backgroundcolor',[1,0.8,0.6]); - PanelBack.value = [c(end),get_ghandle(ExpLbl),get_ghandle(RatLbl),get_ghandle(SchLbl),get_ghandle(Instructions)]; - - - %Time to make the rig maintenance screen - confirmnote = {'If you repaired the rig, enter','your name and click FIXED.'}; - schedulenote = 'Run these rats in different rigs'; - try %#ok - labmembers = bdata('select experimenter from contacts where is_alumni=0'); - labmembers = unique(strtrim(labmembers)); - labmembers(2:end+1) = labmembers; - end - labmembers{1} = 'Select Name'; - - DispParam(obj,'MaintenanceTitle','Rig is Broken',0,0,'position',[220,190,430, 60],'label','','labelfraction',0.01); - DispParam(obj,'ScheduleNote',schedulenote, 0,0,'position',[ 1,220,220, 30],'label','','labelfraction',0.01); - DispParam(obj,'Schedule','', 0,0,'position',[ 1, 40,220,180],'label','','labelfraction',0.01); - DispParam(obj,'RigTechNote','', 0,0,'position',[220, 40,430,150],'label','','labelfraction',0.01); - DispParam(obj,'ConfirmNote',confirmnote, 0,0,'position',[ 1, 1,220, 40],'label','','labelfraction',0.01); - MenuParam(obj,'LabMembersMenu',labmembers, 1,0,0,'position',[220, 1,283, 40],'labelfraction',0.01); - PushbuttonParam(obj,'Fixed', 0,0,'position',[500, 1,150, 40],'label','Fixed'); - - set(get_ghandle(MaintenanceTitle),'Fontsize',32,'visible','off','BackgroundColor',[1,0,0], 'HorizontalAlignment','center'); - set(get_ghandle(ScheduleNote), 'Fontsize',12,'visible','off','BackgroundColor',[1,0.7,0.7], 'HorizontalAlignment','center'); - set(get_ghandle(Schedule), 'Fontsize',12,'visible','off','BackgroundColor',[1,0.7,0.7], 'HorizontalAlignment','left'); - set(get_ghandle(RigTechNote), 'Fontsize',12,'visible','off','BackgroundColor',[1,1,1], 'HorizontalAlignment','center'); - set(get_ghandle(ConfirmNote), 'Fontsize',12,'visible','off','BackgroundColor',[0.9,0.9,0.9],'HorizontalAlignment','center'); - set(get_ghandle(LabMembersMenu), 'Fontsize',24,'visible','off','BackgroundColor',[1,1,1]); %#ok - set(get_ghandle(Fixed), 'Fontsize',24,'visible','off','BackgroundColor',[0,1,0],'enable','off'); - - set_callback(LabMembersMenu,{mfilename,'select_labmember'}); - set_callback(Fixed, {mfilename,'flag_rig_fixed'}); - - %Let's make a fix comments screen - EditParam(obj, 'RepairNote','',0,0,'position',[ 1, 40,650,150],'label','','HorizontalAlignment','left','labelfraction',0.003); - PushbuttonParam(obj,'Submit', 0,0,'position',[ 1, 1,650, 40],'label','Submit'); - - set(get_ghandle(RepairNote),'Fontsize',16,'visible','off','BackgroundColor',[1,1,1],'max',2); - set(get_ghandle(Submit), 'Fontsize',24,'visible','off','BackgroundColor',[0,1,0],'enable','off'); - - set_callback(RepairNote,{mfilename,'enter_repairnote'}); - set_callback(Submit, {mfilename,'submit_rig_fixed'}); - - - %Let's clean up the unused labels - temp = get_glhandle(Instructions); set(temp(2),'visible','off'); - temp = get_glhandle(StatusBar); set(temp(2),'visible','off'); - temp = get_glhandle(ConfirmNote); set(temp(2),'visible','off'); - temp = get_glhandle(ScheduleNote); set(temp(2),'visible','off'); - temp = get_glhandle(Schedule); set(temp(2),'visible','off'); - temp = get_glhandle(LabMembersMenu); set(temp(2),'visible','off'); - - %Last thing we need to make is a timer, boooooo, I don't like them - scr = timer; - set(scr,'Period', 0.2,'ExecutionMode','FixedRate','TasksToExecute',Inf,... - 'BusyMode','drop','TimerFcn',[mfilename,'(''End_Continued'')']); - SoloParamHandle(obj, 'stopping_complete_timer', 'value', scr); - - % Update the rig info table - update_riginfo(); - runrats(obj,'updatelog','startup'); - - %We are done building the GUI, let's update the session menu, figure - %out what session we are in and load up that rat - - runrats(obj,'update_schedule'); - runrats(obj,'estimate_current_session'); - - %Now that we know the schedule for this rig and what session we should - %prepare to load, let's do it. - runrats(obj,'update_exprat'); - runrats(obj,'check_rig_flushed'); - runrats(obj,'live_loop'); - - - case 'send_empty_state_machine' - %% send_empty_state_machine - display('reached sesm'); - - state_machine_server = bSettings('get','RIGS','state_machine_server'); - - server_slot = bSettings('get','RIGS','server_slot'); - if isnan(server_slot); server_slot = 0; end - - card_slot = bSettings('get', 'RIGS', 'card_slot'); - if isnan(card_slot); card_slot = 0; end - - sm = BpodSM(state_machine_server, 3333,server_slot); - sm = Initialize(sm); - - [inL,outL] = MachinesSection(dispatcher,'determine_io_maps'); - - sma = StateMachineAssembler('full_trial_structure'); - sma = add_state(sma,'name','vapid_state_in_vapid_matrix'); - - send(sma,sm,'run_trial_asap',0,'input_lines',inL,'dout_lines',outL,'sound_card_slot', int2str(card_slot)); - - - case 'check_calibration' - %% check_calibration - - lastcalib = check_calibration; - if ~isnan(lastcalib); calibage = now - datenum(lastcalib,'yyyy-mm-dd HH:MM:SS'); - else calibage = Inf; - end - - CalibAge.value = calibage; - days = floor(value(CalibExp) - calibage); - hours = floor((value(CalibExp) - days - calibage) * 24); - - if exist('StatusBar','var') && calibage > value(CalibExp) && bSettings('get','WATER','skip_calibration_now') ~= 1 - %If calibration has expired close runrats and open calibration protocol - dispatcher('set_protocol','WaterCalibration'); - runrats(obj,'close_gui_only'); - return; - - elseif exist('StatusBar','var') && (calibage > value(CalibExp)- 6) && bSettings('get','WATER','skip_calibration_now') ~= 1 - %Calibration is not expired but old, so let's warn the tech it - %will expire soon - - techmessage = value(StatusBar); %#ok - tempF = strfind(techmessage,'flush'); - - %Only update message if it's not already telling the tech to - %flush the valves - if isempty(tempF) - tempC = strfind(techmessage,'Calibration will expire in'); - - if ~isempty(tempC); techmessage(min(tempC):end) = ''; end - - lastchar = find(techmessage ~= ' ',1,'last'); - if ~isempty(lastchar) && lastchar < numel(techmessage) - techmessage(lastchar + 1:end) = ''; - end - - techmessage = [techmessage,' Calibration will expire in ',... - num2str(days),' days ',num2str(hours),' hours']; - - StatusBar.value = techmessage; - end - end - - - - case 'live_loop' - %% live_loop - %Here we loop, updated the schedule and menus accordingly. - - %We do this so the DIO lines work - display('reached live loop'); - runrats(obj,'send_empty_state_machine'); - - InLiveLoop.value = 1; - strt = now; - donefirst = 0; - while value(InLiveLoop) == 1 - %We only want to update every 30 seconds so we don't clog the - %network but we want the loop to go fast to be responsive. - if ((now - strt) * 24 * 60) > 0.5 || donefirst == 0 - disp(['RunRats Live Update at ',datestr(now,'HH:MM:SS')]); - runrats(obj,'update_schedule'); - runrats(obj,'estimate_current_session'); - runrats(obj,'update_exprat'); - runrats(obj,'check_rig_flushed'); - runrats(obj,'check_rig_broken'); - - - if strcmp(get(get_ghandle(MaintenanceTitle),'string'),'Please wait while RunRats updates...') - set(get_ghandle(MaintenanceTitle),'string','Rig is Broken','BackgroundColor',[1 0 0]); - % elseif value(IsBroken) == 0 - %Only check for calibration if the rig is not broken - % runrats(obj,'check_calibration'); - end - - strt = now; - donefirst = 1; - end - pause(rand(1)); - end - if runrats('is_running') - %disp(get(get_ghandle(Multi),'string')); - if isempty(strfind(get(get_ghandle(Multi),'string'),'Run')) &&... - isempty(strfind(get(get_ghandle(Multi),'string'),'Unloading Test')) - runrats(obj,'updatemode_button',0); - end - end - - - case 'updatemode_button' - %% updatemode_button - %The user has clicked the updatemode button, this will either take - %us in or out of the live loop, we also need to change colors - - if nargin > 2; forceto = varargin{1}; - elseif strcmp(get(get_ghandle(UpdateMode),'string'),'Live Update On'); forceto = 0; - else forceto = 1; - end - - if forceto == 0 - set(get_ghandle(UpdateMode),'String','Update Paused','BackgroundColor',[1 0 0],'ForegroundColor',[0 0 0]); - h = value(PanelBack); %#ok - for i = 1:length(h); - set(h(i),'BackgroundColor',[1,0.2,0.2]); - end - set(get_ghandle(Safety),'BackgroundColor',[1,0.2,0.2]); - InLiveLoop.value = 0; - else - h = value(PanelBack); %#ok - for i = 1:length(h); - set(h(i),'BackgroundColor',[1,0.8,0.6]); - end - set(get_ghandle(Safety),'BackgroundColor',[1,0.8,0.6]); - set(get_ghandle(UpdateMode),'String','Live Update On','BackgroundColor',[0.6 1 0.6],'ForegroundColor',[0 0 0]); - runrats(obj,'live_loop'); - end - - - case 'expmenu_callback' - %% expmenu_callback - %The user has selected a different experimenter from the menu - - InLiveLoop.value = 0; - runrats(obj,'update_ratmenu'); - - - case 'ratmenu_callback' - %% ratmenu_callback - %The user has selected a different rat from the menu - - InLiveLoop.value = 0; - runrats(obj,'update_rat',1); - - - case 'schlist_callback' - %% schlist_callback - %The user has selected a different session from the schedule - - InLiveLoop.value = 0; - runrats(obj,'update_exprat'); - - - case 'check_rig_flushed' - %% check_rig_flushed - %Check if the rig has been flushed today - % - % if ~isnan(value(RigID)) - % wf = bdata(['select wasflushed from rigflush where rig=',... - % num2str(value(RigID)),' and dateval="',datestr(now,'yyyy-mm-dd'),'"']); - % if isempty(wf) || wf == 0 - % %The rig has not been flushed - % StatusBar.value = 'Please flush the rig before loading the first rat today.'; - % set(get_ghandle(Multi),'enable','off'); - % wf = 0; - % end - % else - % wf = nan; - % end - % WasFlushed.value = wf; - - %As of 2015-09-02 I am turning this feature off. Possibly leading to - %rats losing motivation in session 1 if they can drink it. -Chuck - - WasFlushed.value = 1; - - - case 'check_rig_broken' - %% check_rig_broken - %Here we determine if the rig has been flagged as broken. If it is - %we bring up the maintenance screen, if not we keep it as normal, - %however, if we are entering a maintenance note we should skip this - - if value(DoingMaintenance) == 0 && ~isnan(value(RigID)) %#ok - [note,isbroken,tech,notedate] = bdata('select note, isbroken, broke_person, broke_date from rig_maintenance where rigid="{S}"',value(RigID)); - - fullnote = ''; - if ~isempty(note) - recent_break = find(isbroken == 1,1,'last'); - if ~isempty(recent_break) - note = note{recent_break}; - isbroken = isbroken(recent_break); - tech = tech{recent_break}; - notedate = notedate{recent_break}; - try %#ok - fullnote = {['Note entered by ',tech,' on ',notedate],char(note)'}; - end - else - isbroken = 0; - end - - set(get_ghandle(Fixed),'enable','off'); - - if isbroken == 1; s = {'on','off'}; - else s = {'off','on'}; - end - else s = {'off','on'}; - end - - set(get_ghandle(MaintenanceTitle),'visible',s{1}); - set(get_ghandle(RigTechNote), 'visible',s{1},'string',fullnote); - set(get_ghandle(ConfirmNote), 'visible',s{1}); - set(get_ghandle(LabMembersMenu), 'visible',s{1}); %#ok - set(get_ghandle(Fixed), 'visible',s{1}); - set(get_ghandle(ScheduleNote), 'visible',s{1}); - set(get_ghandle(Schedule), 'visible',s{1}); - - set(get_ghandle(Instructions), 'visible',s{2}); %#ok - set(get_ghandle(StatusBar), 'visible',s{2}); %#ok - set(get_ghandle(FlushValves), 'visible',s{2}); - set(get_ghandle(TestImplant), 'visible',s{2}); - set(get_ghandle(UpdateMode), 'visible',s{2}); - set(get_ghandle(Maintenance), 'visible',s{2}); - set(get_ghandle(Multi), 'visible',s{2}); - set(get_ghandle(ExpMenu), 'visible',s{2}); %#ok - set(get_ghandle(RatMenu), 'visible',s{2}); %#ok - set(get_ghandle(SchList), 'visible',s{2}); %#ok - set(get_ghandle(ExpLbl), 'visible',s{2}); - set(get_ghandle(RatLbl), 'visible',s{2}); - set(get_ghandle(SchLbl), 'visible',s{2}); - - - %Hide the Test Implant button if this rig isn't set up to do this - alldio=bSettings('get','DIOLINES','ALL'); - if isempty(alldio) || (sum(strcmp(alldio(:,1),'stim1')) == 0 && sum(strcmp(alldio(:,1),'LASER')) == 0) - set(get_ghandle(TestImplant),'visible','off'); - end - - IsBroken.value = isbroken; - end - - case 'select_labmember' - %% select_labmember - %If the user has selected their name we enable the fixed button - - if strcmp(value(LabMembersMenu),'Select Name') == 0 %#ok - set(get_ghandle(Fixed),'enable','on'); - else - set(get_ghandle(Fixed),'enable','off'); - end - - if ~strcmp(value(RepairNote),'') && ~strcmp(value(LabMembersMenu),'Select Name') - set(get_ghandle(Submit),'enable','on'); - end - - - case 'enter_repairnote' - %% enter_repairnote - %If the user has entered a note we can enable the Submit button - - if strcmp(value(RepairNote),'') || strcmp(value(LabMembersMenu),'Select Name') %#ok - set(get_ghandle(Submit),'enable','off'); - else - set(get_ghandle(Submit),'enable','on'); - end - - - - case 'flag_rig_fixed' - %% flag_rig_fixed - %The user has confirmed that the rig is fixed. Now we make them - %enter comments about how they fixed it before going back to - %runrats normal mode - - set(get_ghandle(MaintenanceTitle),'string','Enter how you fixed the rig, then click SUBMIT',... - 'BackgroundColor',[0.7,1,0.7],'FontSize',20); - set(get_ghandle(RepairNote),'visible','on','string',''); - set(get_ghandle(Submit), 'visible','on','enable','off'); - - - - case 'submit_rig_fixed' - %% submit_rig_fixed - %The user has entered comments. Update the MySQL table and return - %runrats to normal operation - - [id isbroken] = bdata(['select maintenance_id, isbroken from rig_maintenance where rigid=',num2str(value(RigID))]); - if ~isempty(id) && sum(isbroken) ~= 0 - %The rig was broken, let's add info to that MySQL entry - temp = find(isbroken == 1,1,'last'); - id = id(temp); - bdata('call mark_rigfixed("{S}","{S}","{S}","{S}")',id,value(LabMembersMenu),... - datestr(now,'yyyy-mm-dd HH:MM'),value(RepairNote)); %#ok - InLiveLoop.value = 1; - else - %We just did maintenance, make a new MySQL entry - bdata(['insert into rig_maintenance set fix_person="',value(LabMembersMenu),... - '", fix_note="',value(RepairNote),'", fix_date="',datestr(now,'yyyy-mm-dd HH:MM'),... - '", rigid=',num2str(value(RigID))]); %#ok - end - - set(get_ghandle(LabMembersMenu),'position',[220, 1,283, 40]); - set(get_ghandle(Submit), 'position',[ 1, 1,650, 40]); - - set(get_ghandle(RepairNote), 'visible','off','string',''); - set(get_ghandle(Submit), 'visible','off'); - set(get_ghandle(LabMembersMenu),'visible','off'); - - set(get_ghandle(MaintenanceTitle),'string','Please wait while RunRats updates...',... - 'BackgroundColor',[0.7 0.7 0.7],'FontSize',20); - - DoingMaintenance.value = 0; - LabMembersMenu.value = 1; - - runrats(obj,'check_rig_broken'); - - - - case 'update_rat' - %% udpate_rat - %Here we take the selected rat and checkout their settings if - %necessary, update their settings, and update the tech instructions - - if isempty(value(RatMenu)) || isempty(value(ExpMenu)); %#ok - return; - end - - runrats(obj,'disable_all'); - set(get_ghandle(Multi),'string',['Updating ',value(RatMenu)],... - 'Backgroundcolor',[0.8 0.8 0.8],'fontsize',24); - pause(0.1); - - dirCurrent = cd; - [dirData errtmp] = bSettings('get','GENERAL','Main_Data_Directory'); - if errtmp || ~ischar(dirData) || isempty(dirData); - return; - end - - if ~exist(dirData,'dir') - %We need to check out SoloData - end - cd(dirData); - - dirSettings = [dirData ,filesep,'Settings']; - dirExp = [dirSettings,filesep,value(ExpMenu)]; - dirRat = [dirExp ,filesep,value(RatMenu)]; - - if ~exist(dirSettings,'dir') - mkdir('Settings'); - system('svn add Settings'); - end - cd(dirSettings); - - if ~exist(dirExp,'dir') - mkdir(value(ExpMenu)); - system(['svn add ' value(ExpMenu)]); - end - cd(dirExp); - - if ~exist(dirRat,'dir'); - mkdir(value(RatMenu)); - system(['svn add ' value(RatMenu)]); - end; - cd(dirRat); - - update_folder(pwd,'svn'); - - cd(dirCurrent); - runrats(obj,'enable_all'); - set(get_ghandle(Multi),'string','Load Protocol','BackgroundColor',[1,1,0.4],'FontSize',24); - - if strcmp(value(CurrRat),value(RatMenu)) %#ok - if nargin < 3 || varargin{1} == 1 - InLiveLoop.value = 1; - end - else - CurrRat.value = value(RatMenu); - - %Let's see if this is a new rat, if so, warn the tech - ratM = bdata(['select mass from mass_log where animal_id="',value(RatMenu),'"']); - if length(ratM) < 14 - StatusBar.value = 'WARNING: New Rat!'; - set(get_ghandle(StatusBar),'fontweight','bold'); - else - StatusBar.value = ['Ready to load ',value(RatMenu)]; - set(get_ghandle(StatusBar),'fontweight','normal'); - end - end - - runrats(obj,'update_tech_instructions'); - runrats(obj,'check_rig_flushed'); - - - case 'update_exprat' - %% update_exprat - %Here we update the Experimenter and Rat Menus to reflect the - %selected entry in the Schedule List - - CurrSession.value = get(get_ghandle(SchList),'value'); %#ok - currrat = value(RatMenu); %#ok - - ratsch = value(RatSch); %#ok - if ~isnan(value(CurrSession)) - if value(CurrSession) <= 9 - SchList.value = value(CurrSession); - StatusBar.value = ['Ready to load session ',num2str(value(CurrSession))]; - else - SchList.value = 10; - StatusBar.value = 'All training session are done for today.'; - end - activerat = ratsch{value(CurrSession)}; - else - activerat = ''; - end - - %Find the rat's experimenter, and set the experimenter and rat menus accordingly - if ~isempty(activerat) - exp = bdata(['select experimenter from rats where ratname="',activerat,'"']); - ExpMenu.value = exp{1}; - else - ExpMenu.value = ''; - end - - runrats(obj,'update_ratmenu',activerat); - runrats(obj,'update_tech_instructions'); - - %If the rat has changed, let's update it. - if ~strcmp(value(RatMenu),currrat) - runrats(obj,'update_rat',value(InLiveLoop)); %#ok - else - %Stay in the loop if we haven't changed anything - InLiveLoop.value = 1; - end - - %runrats(obj,'updatelog','update_exprat'); - -%% SPECIAL CASE ADDED BY ARPIT FOR CLICK AND SELECT -% doesn't effect the running of the other cases/functions - case 'update exp_rat_userclick' - - ExpMenu.value = varargin{1}; - runrats(obj,'update_ratmenu',varargin{2}); - %If the rat has changed, let's update it. - if ~strcmp(value(RatMenu),varargin{2}) - runrats(obj,'update_rat',value(InLiveLoop)); %#ok - else - %Stay in the loop if we haven't changed anything - InLiveLoop.value = 1; - end - - runrats(obj,'begin_load_protocol'); - - % Added to send the details about the experimenter and rat - case 'exp_rat_names' - x = value(ExpMenu.value) -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - case 'update_tech_instructions' - %% update_tech_instructions - %Posts the tech instructions for the active rat on the screen - - if ~isempty(value(OptoPlugColor)) %#ok - try delete(value(OptoPlugColor)); end %#ok - end - - if ~strcmp(value(RatMenu),'') %#ok - [ti,sess] = bdata(['select instructions, timeslot from scheduler where ratname="',value(RatMenu),... - '" and date="',datestr(now,'yyyy-mm-dd'),'"']); - if iscell(ti) && numel(ti) > 1 % More than one session is scheduled for this rat. Grab the appropriate tech instruction - runrats(obj,'estimate_current_session'); - if isempty(value(CurrSession)) - ti = ti{1}; - elseif sum(sess==value(CurrSession)) == 1 - ti = ti{sess==value(CurrSession)}; - else - ti = ''; - end - elseif iscell(ti) && ~isempty(ti); ti = ti{1}; - else ti = ''; - end - try - if sum(ti=='#') >= 2 && rem(sum(ti=='#'),2)==0 - %This may be a day specific message. Let's try to parse it - - if strcmp(datestr(now,'ddd'),'Mon'); dayltr = 'm'; - elseif strcmp(datestr(now,'ddd'),'Tue'); dayltr = 't'; - elseif strcmp(datestr(now,'ddd'),'Wed'); dayltr = 'w'; - elseif strcmp(datestr(now,'ddd'),'Thu'); dayltr = 'r'; - elseif strcmp(datestr(now,'ddd'),'Fri'); dayltr = 'f'; - elseif strcmp(datestr(now,'ddd'),'Sat'); dayltr = 's'; - else strcmp(datestr(now,'ddd'),'Sun'); dayltr = 'u'; - end - - p = find(ti == '#'); - - TI = ti(1:p(1)-1); - - for i = 1:2:numel(p)-1 - d = ti(p(i)+1:p(i+1)-1); - if ~isempty(strfind(lower(d),dayltr)) - %This portion of the instructions should be - %displayed today - msg = ''; - if i+1==numel(p) - if numel(ti)>p(end); msg = ti(p(end)+1:end); end - else - msg = ti(p(i+1)+1:p(i+2)-1); - end - - TI = [TI,' ',msg]; %#ok - end - end - - fstltr = find(TI ~= ' ',1,'first'); - if fstltr > 1; TI(1:fstltr-1) = ''; end - ti = TI; - - end - catch %#ok - disp('Error while parsing tech instructions. Displaying everything.') - end - - %Now let's check the instructions for the safety code - d = find(ti == '$'); - s = ''; - if numel(d) > 1 - for i = 1:2:numel(d) - 1 - temp = ti(d(i)+1:d(i+1)-1); - s(end+1:end+numel(temp)) = temp; - end - end - if ~isempty(strfind(s,'A')) && ~isempty(strfind(s,'B')) > 0; SafetyMode.value = 'AB'; - elseif ~isempty(strfind(s,'A')) && isempty(strfind(s,'B')) == 0; SafetyMode.value = 'A'; - elseif isempty(strfind(s,'A')) && ~isempty(strfind(s,'B')) > 0; SafetyMode.value = 'B'; - else SafetyMode.value = ''; - end - - bad = []; - if numel(d) > 1 - for i = 1:2:numel(d) - 1 - bad(end+1:end+numel(d(i):d(i+1))) = d(i):d(i+1); - end - end - ti(bad) = []; - - Instructions.value = ti; - try %#ok - OptoPlugColor.value = parse_optonote(ti); - end - else - Instructions.value = ''; - SafetyMode.value = ''; - end - - - - case 'disable_all' - %% disable_all - %Disable all the buttons. - - set(get_ghandle(ExpMenu), 'enable','off'); %#ok - set(get_ghandle(RatMenu), 'enable','off'); %#ok - set(get_ghandle(SchList), 'enable','off'); %#ok - set(get_ghandle(FlushValves),'enable','off'); - set(get_ghandle(TestImplant),'enable','off'); - set(get_ghandle(UpdateMode), 'enable','off'); - set(get_ghandle(Maintenance),'enable','off'); - set(get_ghandle(Multi), 'enable','off'); - - - - case 'enable_all' - %% enable_all - %Enable all the buttons. - - set(get_ghandle(ExpMenu), 'enable','on'); %#ok - set(get_ghandle(RatMenu), 'enable','on'); %#ok - set(get_ghandle(SchList), 'enable','on'); %#ok - set(get_ghandle(FlushValves),'enable','on'); - set(get_ghandle(TestImplant),'enable','on'); - set(get_ghandle(UpdateMode), 'enable','on'); - set(get_ghandle(Maintenance),'enable','on'); - set(get_ghandle(Multi), 'enable','on'); - - - - case 'flush_valves' - %% flush_valves - %This will open each water valve sequentially for 10 seconds - - runrats(obj,'disable_all'); - oldval=value(StatusBar); %#ok - StatusBar.value='Flushing Each Valve for 10 seconds'; - - %Let's find all the water valves - alldio=bSettings('get','DIOLINES','ALL'); - diolist=[]; - skip_center = bSettings('get','WATER','skip_center_flush'); - if isnan(skip_center) || isempty(skip_center); skip_center = 0; end - - for di=1:size(alldio,1) - if ~isempty(strfind(alldio{di,1},'water')) && ~isnan(alldio{di,2}) - if ~isempty(strfind(alldio{di,1},'center')) && skip_center == 1; - continue; - end - diolist=[diolist alldio{di,2}]; %#ok - end - end - %Dispatcher needs the log of each DIO line - diolist = log2(diolist); - - %Cycle through the list opening each for 10 seconds - for i = 1:length(diolist) - dispatcher('toggle_bypass',diolist(i)); - pause(10); - dispatcher('toggle_bypass',diolist(i)); - end - - if strcmp(oldval,'Please flush the rig before loading the first rat today.') - oldval = ''; - end - StatusBar.value=oldval; - runrats(obj,'enable_all'); - - - %Update MySQL to show this rig has been flushed today - if ~isnan(value(RigID)) - id = bdata(['select id from rigflush where rig=',... - num2str(value(RigID)),' and dateval="',datestr(now,'yyyy-mm-dd'),'"']); - - - if isempty(id) || ~isnan(id) - if isempty(id) - bdata(['insert into rigflush set rig=',num2str(value(RigID)),... - ', wasflushed=1, dateval="',datestr(now,'yyyy-mm-dd'),'"']); - end - end - end - WasFlushed.value = 1; - - - - case 'test_implant' - %% test_implant - %This will toggle all the stim and laser lines to test a rats - %implant before running - - %Check to see if the multi button is already disabled - if strcmp(get(get_ghandle(Multi),'enable'),'off') - disablemulti = 1; - else - disablemulti = 0; - end - - runrats(obj,'disable_all'); - oldval=value(StatusBar); %#ok - StatusBar.value='Testing Implant. Please Observe Rat'; - - %Let's find all the stim lines - alldio=bSettings('get','DIOLINES','ALL'); - diolist=[]; - for di=1:size(alldio,1) - if (~isempty(strfind(alldio{di,1},'LASER')) ||... - ~isempty(strfind(alldio{di,1},'GREEN')) ||... - ~isempty(strfind(alldio{di,1},'BLUE')) ||... - ~isempty(strfind(alldio{di,1},'YELLOW')) ||... - ~isempty(strfind(alldio{di,1},'RED'))) &&... - ~isnan(alldio{di,2}) - diolist=[diolist alldio{di,2}]; %#ok - end - end - %Dispatcher needs the log of each DIO line - diolist = log2(diolist); - - %Call dispatcher to toggle the stim lines in succession - pause(3); - for i = 1:length(diolist) - dispatcher('toggle_bypass',diolist(i)); - pause(0.2); - dispatcher('toggle_bypass',diolist(i)); - pause(0.5); - end - - StatusBar.value=oldval; - runrats(obj,'enable_all'); - runrats(obj,'check_rig_flushed'); - - if disablemulti == 1 - set(get_ghandle(Multi),'enable','off'); - end - - runrats(obj,'updatelog','testimplant'); - - - case 'rig_maintenance' - %% rig_maintenance - %The user wants to enter some comments about maintenance they - %performed on the rig. - - set(get_ghandle(StatusBar),'visible','off'); %#ok - - set(get_ghandle(MaintenanceTitle),'visible','on','string','Enter a description of the maintenance performed:','BackgroundColor',[1 0.3 0.1],'Fontsize',20); - set(get_ghandle(RepairNote), 'visible','on'); - set(get_ghandle(Submit), 'visible','on','position',[325,1,325,40]); - set(get_ghandle(LabMembersMenu), 'visible','on','position',[ 1,1,325,40]); %#ok - DoingMaintenance.value = 1; - - - - case 'update_ratmenu' - %% update_ratmenu - %Here we get the list of extant rats for the selected experimenter - %and populate the rat menu with it - - exp = value(ExpMenu); %#ok - currats = get(get_ghandle(RatMenu),'string'); %#ok - - if strcmp(exp,'') - ratnames = {''}; - else - ratnames = bdata(['select ratname from rats where experimenter="',exp,'" and extant=1']); - ratnames = sortrows(strtrim(ratnames)); - end - - %If we've changed things, update the menus accordingly - if length(currats) ~= length(ratnames) || ~strcmp(currats{1},ratnames{1}) ||... - (nargin > 2 && ~strcmp(varargin{1},value(RatMenu))) - set(get_ghandle(RatMenu),'string',ratnames,'value',1); - - %If we passed in a rat name, set the manu to it - if nargin > 2; RatMenu.value = varargin{1}; - else RatMenu.value = ratnames{1}; - end - - runrats(obj,'update_rat'); - else - %Stay in the loop if we haven't changed anything - InLiveLoop.value = 1; - end - - - - case 'update_schedule' - %% update_schedule - %Here we grab the current schedule for this rig - - if ~isnan(value(RigID)); - [rats slots] = bdata(['select ratname, timeslot from scheduler where date="',... - datestr(now,'yyyy-mm-dd'),'" and rig=',num2str(value(RigID))]); - end - - %Let's populate the 5 training sessions (changed by sharbat, with - %approx times guesse for Akrami lab - ratsch = cell(6,1); for i=1:6; ratsch{i} = ''; end - sch = cell(6,1); - - - sch{1} = ' 8-10am: '; - sch{2} = '10-12am: '; - sch{3} = '12- 2pm: '; - sch{4} = ' 2- 4pm: '; - sch{5} = ' 4- 6pm: '; - sch{6} = ''; - - if ~isnan(value(RigID)) - for i = 1:5 - temp = slots==i; - if sum(temp) == 1 - ratsch{i} = rats{temp}; - sch{i} = [sch{i},ratsch{i}]; - end - end - end - set(get_ghandle(Schedule),'string',sch(1:5)); - set(get_ghandle(SchList),'string',sch); %#ok - RatSch.value = ratsch; - - - case 'estimate_current_session' - %% estimate_current_session - %Here we will try to estimate which session is currently training - - CS = value(CurrSession); - - [ratSCH slots] = bdata(['select ratname, timeslot from scheduler where date="',... - datestr(now,'yyyy-mm-dd'),'"']); - ratSES = bdata(['select ratname from sessions where sessiondate="',datestr(now,'yyyy-mm-dd'),'"']); - [ratSS ST] = bdata(['select ratname, starttime from sess_started where sessiondate="',... - datestr(now,'yyyy-mm-dd'),'"']); - - %Let's cycle through each of the 6 training sessions and see if the - %rats are started, done, or neither - COMP = zeros(5,1); - for i = 1:5 - currats = ratSCH(slots == i); - currats(strcmp(currats,'')) = []; - comp = zeros(size(currats)); - st = zeros(size(currats)); st(:) = nan; - for j = 1:length(currats) - if sum(strcmp(ratSES,currats{j})) > 0; comp(j) = 2; %rat finished - elseif sum(strcmp(ratSS, currats{j})) > 0; comp(j) = 1; %rat running - st(j) = datenum(ST{find(strcmp(ratSS, currats{j})==1,1,'first')},'HH:MM:SS'); - end - end - - if sum(comp == 2) >= numel(comp)/2; COMP(i) = 2; %session is likely completed - elseif sum(comp > 0) >= numel(comp)/2; COMP(i) = 1; %session is likely running - else COMP(i) = 0; %session not yet started - end - - %If the session is still running, let's see how long they've been in. - if COMP(i) == 1 - st = (datenum(datestr(now,'HH:MM:SS')) - nanmean(st)) * 24 * 60; - if st > 60; COMP(i) = 2; end - end - end - - lastcomp = find(COMP == 2,1,'last'); - if isempty(lastcomp); CurrSession.value = 1; - elseif lastcomp == numel(COMP); CurrSession.value = []; - else CurrSession.value = lastcomp + 1; - end - - %In the event the system thinks we've finished 9 but there are - %sessions earlier that aren't completed, let's find the first not - %completed session and make that the current session. - %careful with no of sessions - if isempty(value(CurrSession)) - notcomp = find(COMP ~= 2,1,'first'); - if ~isempty(notcomp) - CurrSession.value = notcomp; - end - end - % WARNING : dummy value added by sharbat - % CurrSession.value = 4 - % WARNING : dummy value added by sharbat - CurrSession.value = find(COMP ~= 2,1,'first'); - SchList.value = value(CurrSession); - - if isempty(value(CurrSession)) || value(CurrSession) ~= CS - disp('Updating Current Session'); - - if ~isempty(value(CurrSession)) - runrats(obj,'updatelog','update_session'); - end - - if (isempty(value(CurrSession)) && ~isempty(CS) && CS == 5) %|| (value(CurrSession) == 7 && CS == 6) - %Training for today is nearing its end but this rig is - %likely not running a rat in session 9 or it's already out. - %Let's do the daily reboot for this rig here. - runrats(obj,'reboot') - end - - end - - - case 'safety_button' - %% safety_button - %The user had clicked the safety button which will unlock the multi - %button - - set(get_ghandle(Multi),'enable','on'); - - - case 'multi_button' - %% multi_button - %The user has clicked the Multi purpose button, depending on what - %state runrats is in, this button will do different things. Let's - %figure out what to do here. - - if strcmp(get(get_ghandle(Multi),'string'),'Load Protocol') - runrats(obj,'begin_load_protocol'); - - elseif ~isempty(strfind(get(get_ghandle(Multi),'string'),'Run')) - runrats(obj,'run'); - - elseif ~isempty(strfind(get(get_ghandle(Multi),'string'),'End Session')) - runrats(obj,'end'); - - elseif ~isempty(strfind(get(get_ghandle(Multi),'string'),'Crashed')) - runrats(obj,'crash_cleanup'); - - end - - - case 'begin_load_protocol' - %% begin_load_protocol - %Let's unload any protocols from dispatcher and either do the - %manual test or skip right to loading the protocol depending on the - %rig's settings - - %dammy add here - - InLiveLoop.value = 0; - runrats(obj,'disable_all'); - - set(get_ghandle(Multi),'string','Unloading...','fontsize',28); - - x = ''; - try x = dispatcher('get_protocol_object'); end %#ok - if ~isempty(x) - %There was a protocol previously open. Let's not trust that - %their close section is working properly. - try %#ok - %rigscripts does not exist currently, try Protocols (ask - %Athena) -sharbat - p = bSettings('get','GENERAL','Main_Code_Directory'); - p(strfind(p,'ExperPort'):end) = ''; - p = [p,'Rigscripts']; - cd(p); - if ispc == 1 - system('restart_runrats.bat'); - end - end - end - - dispatcher('set_protocol',''); - if bSettings('get','RUNRATS','skip_manual_test') == 1 - runrats(obj,'load_protocol'); - else - runrats(obj,'manual_test'); - end - - - case 'manual_test' - %% manual_test - %Load and run the manual test protocol - - %Reset the values that GCS sees for trials and performance - try send_n_done_trials(obj,'reset'); end %#ok - - set(get_ghandle(Multi),'string','Loading Test','fontsize',28); - - r = rand(1); - if r < 0.0015 && value(RigID) < 40 - runrats(obj,'updatelog','manualtest_leftfail'); - dispatcher('set_protocol','Rigtest_singletrial_leftfail'); - elseif r < 0.003 && value(RigID) < 40 - runrats(obj,'updatelog','manualtest_rightfail'); - dispatcher('set_protocol','Rigtest_singletrial_rightfail'); - else - runrats(obj,'updatelog','manualtest'); - dispatcher('set_protocol','Rigtest_singletrial'); - end - %Hide protocol window. - h=get_sphandle('owner','Rigtest_singletrial','name', 'myfig'); - for i=1:numel(h); set(value(h{i}),'Visible','Off'); end - - set(get_ghandle(Multi),'String','Manual Test','Fontsize',28); - StatusBar.value='Please test the rig by poking in the lit pokes.'; - - %Begin execution of the manual test protocol. - dispatcher(value(dispobj),'Run'); %#ok - - %RigTest_singletrial will call runrats('rigtest_singletrial_is_complete') - %after it finishes one trial at which point we continue on - - - case 'rigtest_singletrial_is_complete' - %% rigtest_singletrial_is_complete - %The manual test is complete, let's clear out the protocol and move - %on to load the rat's protocol and settings - - set(get_ghandle(Multi),'String','Unloading Test','Fontsize',24); - StatusBar.value='Completing Test. Please be patient!'; - - dispatcher(value(dispobj),'Stop'); %#ok - - %Let's pause until we know dispatcher is done running - set(value(stopping_complete_timer),'TimerFcn',[mfilename,'(''rigtest_singletrial_is_complete_continued'');']); - start(value(stopping_complete_timer)); - - - case 'rigtest_singletrial_is_complete_continued', - %% rigtest_singletrial_is_complete_continued - if value(stopping_process_completed) %This is provided by dispatcher to runrats - stop(value(stopping_complete_timer)); %Stop looping. - dispatcher('set_protocol',''); - runrats(obj,'load_protocol'); - end - - - - case 'load_protocol' - %% load_protocol - %Okay, we are finally ready to load the protocol and the rats - %settings, runrats will then wait for the tech to click Run - - %Let's make sure we have the most up-to-date settings - runrats(obj,'update_rat',0); - - set(get_ghandle(Multi),'String','Loading...','BackgroundColor', [0.8 0.8 0.6],'Fontsize',30); - StatusBar.value='Loading protocol and settings. Please be patient!'; - pause(0.1); - - %Let's also make sure we have the most up-to-date code - CurrDir = pwd; - pname = bSettings('get','GENERAL','Main_Code_Directory'); - if ~isempty(pname) && ischar(pname) - update_folder(pname,'svn'); - end - - %And finally we make sure the protocols are up-to-date - pname = bSettings('get','GENERAL','Protocols_Directory'); - if ~isempty(pname) && ischar(pname) - update_folder(pname,'svn'); - end - cd(CurrDir); - - %Let's get the protocol for the rat and load it - CurrProtocol.value = getProtocol(value(ExpMenu),value(RatMenu)); %#ok - if isempty(value(CurrProtocol)) - StatusBar.value = ['No Settings for ',value(RatMenu)]; - runrats(obj,'enable_all'); - set(get_ghandle(Multi),'string','Load Protocol','BackgroundColor',[1,1,0.4],'FontSize',24); - InLiveLoop.value = 1; - - %Let's notify the experimenter that the load failed, pause to - %let the tech see the note, then go back to live loop - runrats(obj,'email_experimenter','no settings'); - - runrats(obj,'updatelog','nosettings'); - - pause(10); - runrats(obj,'live_loop'); - return; - end - - try - dispatcher(value(dispobj),'set_protocol',value(CurrProtocol)); %#ok - catch %#ok - StatusBar.value = ['Failed to load ',value(CurrProtocol),' for ',value(RatMenu)]; - runrats(obj,'enable_all'); - set(get_ghandle(Multi),'string','Load Protocol','BackgroundColor',[1,1,0.4],'FontSize',24); - InLiveLoop.value = 1; - - %Let's notify the experimenter that the load failed, pause to - %let the tech see the note, then go back to live loop - runrats(obj,'email_experimenter','protocol fail'); - - runrats(obj,'updatelog','failload protocol'); - - pause(10); - runrats(obj,'live_loop'); - return; - end - - rath=get_sphandle('name','ratname','owner',value(CurrProtocol)); - exph=get_sphandle('name','experimenter','owner',value(CurrProtocol)); - rath{1}.value=value(RatMenu); %#ok - exph{1}.value=value(ExpMenu); %#ok - - try - protobj=eval(value(CurrProtocol)); - - [out, sfile]=load_solouiparamvalues(value(RatMenu),'experimenter',value(ExpMenu),... - 'owner',class(protobj),'interactive',0); - settings_file_sph.value = sfile; - settings_file_load_time.value = now; - if ~dispatcher('is_running') - pop_history(class(protobj), 'include_non_gui', 1); - feval(value(CurrProtocol), protobj, 'prepare_next_trial'); - end - catch %#ok - StatusBar.value = 'Failed to load Settings file.'; - runrats(obj,'enable_all'); - set(get_ghandle(Multi),'string','Load Protocol','BackgroundColor',[1,1,0.4],'FontSize',24); - InLiveLoop.value = 1; - - %Let's notify the experimenter that the load failed, pause to - %let the tech see the note, then go back to live loop - runrats(obj,'email_experimenter','settings fail'); - - runrats(obj,'updatelog','failload settings'); - - pause(10); - runrats(obj,'live_loop'); - return; - end - - set(get_ghandle(Multi),'String',['Run: ',value(RatMenu)],'BackgroundColor',[0.3,1,0.3],'Fontsize',32); - [pname,fname,ext] = fileparts(sfile); %#ok - - StatusBar.value=['Using settings file: ',fname]; - if value(phys)==1 - create_phys_session(eval(value(CurrProtocol))) - end - - runrats(obj,'enable_all'); - figure(value(myfig)); - InLiveLoop.value = 0; - - %Check to see if the experimenter wants to enable the safety before - if ~isempty(strfind(value(SafetyMode),'B')) %#ok - set(get_ghandle(Multi),'enable','off'); - set(get_ghandle(Safety),'visible','on','string',value(Instructions)); %#ok - else - set(get_ghandle(Multi),'enable','on'); - set(get_ghandle(Safety),'visible','off','string',''); - end - - - case 'run' - %% run - %Here we start the protocol running - runrats(obj,'updatelog','runstart'); - runrats(obj,'disable_all'); - set(get_ghandle(Multi),'String','End Session','BackgroundColor',[1,0.2,0.2],'Fontsize',28); - StatusBar.value = ['Start Time: ',datestr(now,'HH:MM PM'),'. ',value(StatusBar)]; %#ok - - try - sendstarttime(eval(value(CurrProtocol))); %#ok - catch %#ok - disp('ERROR: Failed to add the start time to the MySQL table.'); - end; - - %Let's make everything unresponsive for 5 more seconds to prevent - %double clicks from stopping the session - pause(5); - - %Start raspberry pi_camera - % try - % disp('trying camera') - % start_camera(value(RigID),value(RatMenu),value(CurrProtocol),'start') - % catch - % disp('failed to start pi camera') - % end - - % If using USB Webcam, then try using it - % try - % disp('Connecting to USB HD Camera') - % webcam_connected = webcamlist; - % webcam_idx = find(contains(webcam_connected,'USB')); - % if ~isempty(webcam_idx) % USB Camera connected - % cam = webcam(webcam_connected{webcam_idx}); - % fig = figure('NumberTitle','off','MenuBar','none'); - % fig.Name = 'My Camera'; - % ax = axes(fig); - % frame = snapshot(cam); - % im = image(ax,zeros(size(frame),'uint8')); - % axis(ax,'image'); - % preview(cam,im) - % Camera_Fig_window.value = fig; - % Camera_Obj.value = cam; - % Camera_Image.value = im; - % else - % disp('No USB camera connected') - % end - % catch - % disp('failed to connect to USB camera') - % end - - %Enable the Multi button so the user can stop the session - enable(Multi); - - %Check to see if the experimenter wants to enable the safety before - if ~isempty(strfind(value(SafetyMode),'A')) %#ok - set(get_ghandle(Multi),'enable','off'); - set(get_ghandle(Safety),'visible','on','string',value(Instructions)); %#ok - else - set(get_ghandle(Multi),'enable','on'); - set(get_ghandle(Safety),'visible','off','string',''); - end - - dispatcher(value(dispobj),'Run'); %#ok - - - case 'flicker_multibutton' - %% flicker_multibutton - %Called by dispatcher while running to invert the color of the - %multi button, let's the tech know if the rig is running - clr = get(get_ghandle(Multi),'BackgroundColor'); - set(get_ghandle(Multi),'BackgroundColor',1 - clr); - - - case 'end' - %% end - %Ends the current protocol being run through dispatcher - runrats(obj,'updatelog','runend'); - runrats(obj,'disable_all'); - set(get_ghandle(Multi),'String','Saving...','Fontsize',32); - - %Stop raspberry pi_camera - % try - % disp('stopping camera') - % start_camera(value(RigID),value(RatMenu),value(CurrProtocol),'stop') - % catch - % disp('failed to stop pi camera') - % end - - % Stop USB Camera - % try - % closePreview(value(Camera_Obj)) - % clear(value(Camera_Image)) - % clear(value(Camera_Obj)); - % close(value(Camera_Fig_window)); - % disp('USB camera stopped') - % catch - % disp('failed to stop USB camera') - % end - - %Stop dispatcher and wait for it to respond - dispatcher(value(dispobj),'Stop'); %#ok - - %Let's pause until we know dispatcher is done running - set(value(stopping_complete_timer),'TimerFcn',[mfilename,'(''end_continued'');']); - start(value(stopping_complete_timer)); - - - case 'end_continued' - %% end_continued - if value(stopping_process_completed) %This is provided by dispatcher to runrats - stop(value(stopping_complete_timer)); %Stop looping. - - %Now that everything is stopped let's send an empty state - %matrix to the Linux machine. This will reset all the lines - %and sounds to be off. - runrats(obj,'send_empty_state_machine') - - protobj=eval(value(CurrProtocol)); %#ok - feval(value(CurrProtocol), protobj, 'end_session'); - sfile=SavingSection(protobj,'savedata','interactive',0); - - %if the protocol has a pre_saving_settings section, call it - try - feval(value(CurrProtocol),protobj,'pre_saving_settings'); - catch %#ok - disp('Protocol does not appeat to have a pre_saving_settings') - end - - - SavingSection(protobj,'savesets','interactive',0); - - [pname,fname] = fileparts(sfile); - - configFilePath = '..\PASSWORD_CONFIG-DO_NOT_VERSIONCONTROL.mat'; - load(configFilePath); - svnusername = svn_user; - svnpsswd = svn_password; - logmsg = 'saved data and settings file'; - - cd(pname); - cmdstr = char(strcat('svn add', {' '}, fname, '.mat',{'@'})); %the @ calls for a peg revision, ref to - % https://stackoverflow.com/questions/27312188/how-to-move-rename-a-file-in-subversion-with-characters-in-it - system(cmdstr); - cmdstr2 = sprintf('svn ci --username="%s" --password="%s" -m "%s"',char(svnusername), char(svnpsswd), char(logmsg)); - if ~(system(cmdstr2)) - display('SoloData data files added!') - end - - [setpname, setfname] = fileparts(value(settings_file_sph)); - cd(setpname); - cmdstr3 = char(strcat('svn add', {' '}, setfname, '.mat',{'@'})); %the @ calls for a peg revision, ref to - % https://stackoverflow.com/questions/27312188/how-to-move-rename-a-file-in-subversion-with-characters-in-it - try - system(cmdstr3); - catch - display('SoloData settings files seem to be there!') - end - - cmdstr4 = sprintf('svn ci --username="%s" --password="%s" -m "%s"',char(svnusername), char(svnpsswd), char(logmsg)); - if ~(system(cmdstr4)) - display('SoloData settings files added!') - end - StatusBar.value=['Saved data and settings file: ',fname]; - - set(get_ghandle(Multi),'String','Reloading'); - dispatcher('set_protocol',''); - - %Let's reset the Multi button and hop back in the live loop - set(get_ghandle(Multi),'ForegroundColor',[0,0,0],'BackgroundColor',... - [1,1,0.4],'string','Load Protocol','FontSize',24); - InLiveLoop.value = 1; - runrats(obj,'enable_all'); - - %We need to turn RunRats back to live mode - h = value(PanelBack); %#ok - for i = 1:length(h); - set(h(i),'BackgroundColor',[1,0.8,0.6]); - end - set(get_ghandle(UpdateMode),'String','Live Update On','BackgroundColor',[0.6 1 0.6],'ForegroundColor',[0 0 0]); - - %Another option is to now kill MatLab completely and restart - %runrats. This ensures windows don't pile up, and code can get - %updated before each session - - if value(do_full_restart) == 1 - - %Close the MySQL connection - try bdata('close'); end %#ok - - if value(CurrSession) == 9 %|| value(CurrSession) == 6 %#ok - %We just finished session 9 so let's do a full reboot - runrats(obj,'reboot') - else - %This is an earlier session during the day, let's simply - %restart Matlab - % - pause(1); - drawnow; - - p = bSettings('get','GENERAL','Main_Code_Directory'); - p(strfind(p,'ExperPort'):end) = ''; - p = [p,'Rigscripts']; - cd(p); - - try %#ok - if ispc == 1 - system('restart_runrats.bat') - else - system('./start_runrats.sh') - end - end - end - end - - %And now we hop back in the loop - runrats(obj,'live_loop'); - end - - - case 'reboot' - %% reboot - %This is designed to execute once each day, either when the session - %9 rat is done training or if this rig is not running a rat in - %session 9, when it sees that session 9 is nearning completion. - runrats(obj,'updatelog','reboot'); - try - try - %Once a week try to email me the datalogs so I can see if - %things are working. I'll deactivate this once I know all - %is working properly. - if strcmp(datestr(now,'ddd'),'Mon') - file = which('runrats_datalog.txt'); - path = bSettings('get','GENERAL','Main_Data_Directory'); - newfile = [path,filesep,'Data',filesep,'RunRats',filesep,'Rig',sprintf('%03.0f',value(RigID)),filesep,... - datestr(now,'yymmdd'),'_','Rig',sprintf('%03.0f',value(RigID)),'_runrats_datalog.txt']; - system(['echo f | xcopy "',file,'" "',newfile,'"']); - add_and_commit(newfile); - end - end - cd('\ratter\Rigscripts') - - !del do_on_reboot.bat - !copy start_runrats.bat do_on_reboot.bat /Y - pause(1) - system('shutdown -r -f -t 1'); - pause(20); - %!copy nothing.bat do_on_reboot.bat /Y - end - - - case 'crashed' - %% crashed - %Dispatcher crashed while running the protocol. Let's notify the - %tech that we've crashed and send an email with the last error - %message to the rat's owner. We will also update a MySQL table that - %the GCS reads to post that a crash happened - runrats(obj,'updatelog','crashed'); - set(get_ghandle(Multi),'String','Crashed','BackgroundColor',[0,0,0],... - 'ForegroundColor',[1,1,1],'Fontsize',40); - enable(Multi); - - if ~isempty(varargin) && iscell(varargin) && strcmp(class(varargin{1}),'MException') - lsterr = varargin{1}; - else - lsterr = lasterror; %#ok - end - - %Let's stop dispatcher and clean it up - RunningSection(value(dispobj),'RunStop'); %#ok - dispatcher('set_protocol',''); - - %Now we can email the rat's owner a crash report - try %#ok - message = cell(0); - message{end+1} = ['Rig ',num2str(value(RigID)),' crashed while running ',value(RatMenu),' at ',datestr(now,13)]; %#ok - message{end+1} = ''; - message{end+1} = lsterr.message; - message{end+1} = ''; - - for i = 1:length(lsterr.stack) - message{end+1} = [lsterr.stack(i).name,' at ',num2str(lsterr.stack(i).line)]; %#ok - end - - IP = get_network_info; - message{end+1} = ' '; - if ischar(IP); message{end+1} = ['Email generated by ',IP]; - else message{end+1} = 'Email generated by an unknown computer!!!'; - end - message{end+1} = 'ratter\ExperPort\Modules\@runrats\runrats.m'; - - %setpref('Internet','SMTP_Server','brodyfs2.princeton.edu'); - %setpref('Internet','E_mail',['RunRats',datestr(now,'yymm'),'@Princeton.EDU']); - set_email_sender - - owner = bdata(['select contact from rats where ratname="',value(RatMenu),'"']); - if ~isempty(owner) - owner = owner{1}; - owner = [',',owner,',']; - owner(owner == ' ') = ''; - cms = find(owner == ','); - - for i = 1:length(cms)-1 - exp = owner(cms(i)+1:cms(i+1)-1); - sendmail([exp,'@princeton.edu'],[value(RatMenu),' Crashed'],message); - end - end - end - - %Let's update the MySQL table to indicate a crash has happened - id = bdata(['select sessid from sess_started where ratname="',value(RatMenu),... - '" and was_ended=0 and sessiondate="',datestr(now,'yyyy-mm-dd'),'"']); - if ~isempty(id) - id = id(end); - bdata('call mark_crashed("{S}")',id); - end - - - - - case 'crash_cleanup' - %% crash_cleanup - %The tech has acknowledged the crash. Let's jump back in the loop - - runrats(obj,'enable_all'); - set(get_ghandle(Multi),'ForegroundColor',[0,0,0],'BackgroundColor',... - [1,1,0.4],'string','Load Protocol','FontSize',24); - - InLiveLoop.value = 1; - runrats(obj,'live_loop'); - - case 'updatelog' - - try - Exp = value(ExpMenu); - Rat = value(RatMenu); - Sch = value(SchList); - - time = datestr(now,'yymmdd HH:MM:SS'); - - str = [time,' ',varargin{1},' ',Exp,' ',Rat,' ',Sch,char(10)]; - file = which('runrats_datalog.txt'); - f = fopen(file,'a+t'); - fseek(f,0,'eof'); - fprintf(f,str,'char'); - fclose(f); - - %runrats_datalog{end+1} = str; - %save(file,'runrats_datalog'); - end - - - case 'email_experimenter' - %% email_experimenter - %Something has gone wrong during the load phase and we should email - %the experimenter if we can - - try %#ok - %setpref('Internet','SMTP_Server','brodyfs2.princeton.edu'); - %setpref('Internet','E_mail',['RunRats',datestr(now,'yymm'),'@Princeton.EDU']); - set_email_sender - - message{1} = ['Load failed for ',value(RatMenu)]; %#ok - if strcmp(varargin{1},'no settings') - subject = ['No settings for ',value(RatMenu)]; - message{2} = ['Please check that there are settings for ',value(RatMenu)]; - elseif strcmp(varargin{1},'protocol fail') - subject = ['Failed to load ',value(CurrProtocol)]; %#ok - message{2} = ['Protocol ',value(CurrProtocol),' could not be laoded for ',value(RatMenu)]; - else - subject = ['Failed to load settings for ',value(RatMenu)]; - message{2} = ['The settings file for ',value(RatMenu),' failed to load']; - end - - IP = get_network_info; - message{end+1} = ' '; - if ischar(IP); message{end+1} = ['Email generated by ',IP]; - else message{end+1} = 'Email generated by an unknown computer!!!'; - end - message{end+1} = 'ratter\ExperPort\Modules\@runrats\runrats.m'; - - contact = bdata(['select contact from rats where ratname="',value(RatMenu),'"']); - if ~isempty(contact) - contact = [',',contact{1},',']; - contact(contact == ' ') = ''; - cms = find(contact == ','); - for i = 1:length(cms)-1 - email = contact(cms(i)+1:cms(i+1)-1); - sendmail([email,'@princeton.edu'],subject,message); - end - end - end - - - - - case 'is_running', - %% is_running - %If the Multi button exists, runrats has been loaded - if exist('Multi','var'), obj = 1; else obj = 0; end - - - case 'get_settings_file_load_time' - %% get_settings_file_load_time - %This is called by SavingSection, let's not modify it - - if exist('settings_file_load_time', 'var') && isa(settings_file_load_time, 'SoloParamHandle') %#ok - varargout{1} = value(settings_file_load_time); - else - varargout{1} = 0; - end - - - case 'get_settings_file_path' - %% get_setting_file_path - %This is called by SavingSection, let's not modify it - - if exist('settings_file_sph', 'var') && isa(settings_file_sph, 'SoloParamHandle') %#ok - varargout{1} = value(settings_file_sph); - else - varargout{1} = ''; - end - - - case 'send_empty_state_machine' - %% send_empty_state_machine - %Sends an empty state matrix. This allows us to toggle DIO lines - - state_machine_server = bSettings('get','RIGS','state_machine_server'); - - server_slot = bSettings('get','RIGS','server_slot'); - if isnan(server_slot); server_slot = 0; end - - card_slot = bSettings('get', 'RIGS', 'card_slot'); - if isnan(card_slot); card_slot = 0; end - - sm = BPodSM(state_machine_server, 3333,server_slot); - sm = Initialize(sm); - - [inL outL] = MachinesSection(dispatcher,'determine_io_maps'); - - sma = StateMachineAssembler('full_trial_structure'); - sma = add_state(sma,'name','vapid_state_in_vapid_matrix'); - - send(sma,sm,'run_trial_asap',0,'input_lines',inL,'dout_lines',outL,'sound_card_slot', int2str(card_slot)); - - - case 'close' - %% close - %Closes runrats - - runrats(obj,'close_gui_only'); - - try - dispatcher(value(dispobj),'close'); %#ok - catch %#ok - disp('WARNING: Dispatcher close attempt in runrats failed.'); - end - - - case 'close_gui_only' - %% close_gui_only - - try - StatusBar.value='Cleaning up......'; - runrats(obj,'disable_all'); - catch %#ok - disp('WARNING: Close attempt in runrats failed.'); - end - delete(value(myfig)); - - delete_sphandle('owner', ['^@', mfilename '$']); - obj = []; - - - otherwise - warning('Unknown action " %s" !', action);%#ok -end; - -return; - - -function p=getProtocol(exprmtr,rat) - -olddir=cd; -p=''; -try %#ok - dd=bSettings('get','GENERAL','Main_Data_Directory'); - - if isnan(dd); dd='../SoloData'; end - if dd(end)~=filesep; dd(end+1)=filesep; end - %changed filesep, sharbat - dd=[dd,'Settings',filesep]; - cd([dd,exprmtr,filesep,rat]); - fn=dir('settings_*_*_*_*.mat'); - for xi=1:numel(fn) - s=fn(xi).name; - tc=textscan(s,'%s','Delimiter','_'); - prt{xi}=tc{1}{2}; %#ok - r=tc{1}{end}; - % Must have had 5 fields (settings, prot, exprtr, rat, date), the - % date must be 11 chars long (7 of date plus '.mat'), the first six - % of those must be numbers, not letters: - if length(tc{1}) == 5 && length(r) == 11 && all(~isletter(r(1:6))), - setdate{xi}=r(1:7); %#ok - else % not a file we want, give it a really early date, 2000: - setdate{xi}='000101a'; %#ok - end; - end - - [srtdsets, sdi]=sort(setdate); - - % Look only at settings that are not later than today - ymd = str2double(yearmonthday); keeps = ones(size(sdi)); - for i=1:length(sdi), if str2double(srtdsets{i}(1:6)) > ymd, keeps(i) = 0; end; end; - srtdsets = srtdsets(find(keeps)); sdi = sdi(find(keeps)); %#ok - - p=prt{sdi(end)}; - if p(1)=='@' - p=p(2:end); - end - -end -cd(olddir) - - -function update_folder(pname,vn) - -try - currdir = pwd; - cd(pname); - if strcmp(vn,'cvs') - failed1 = 0; - [failed2 message2] = system('cvs up -d -P -A'); - elseif strcmp(vn,'svn') - [failed1 message1] = system('svn cleanup'); - [failed2 message2] = system('svn update'); - end - cd(currdir); - - rig = bSettings('get','RIGS','Rig_ID'); - if ~ischar(rig); rig = num2str(rig); end - - if failed1 == 1 || failed2 == 1 - %setpref('Internet','SMTP_Server','brodyfs2.princeton.edu'); - %setpref('Internet','E_mail',['RunRats',datestr(now,'yymm'),'@Princeton.EDU']); - set_email_sender - - if pname(1) ~= filesep; pname = [filesep,pname]; end - if pname(end) ~= filesep; pname = [pname,filesep]; end - fs = find(pname == filesep); - - contact = ''; - for i = 1:length(fs)-1 - temp = pname(fs(i)+1:fs(i+1)-1); - if length(temp) == 4 && ~isempty(str2num(temp(2:4))) %#ok - %This is a rat, let's email the owner - contact = bdata(['select contact from rats where ratname="',temp,'"']); - elseif strcmpi(temp,'experport') || strcmpi(temp,'protocols') - contact = {'ckopec'}; - end - end - - if failed1 == 1 - message = cell(0); - message{1} = ['SVN cleanup failed in ',pname]; - message{2} = ' '; - message{3} = message1; - - IP = get_network_info; - message{end+1} = ' '; - if ischar(IP); message{end+1} = ['Email generated by ',IP]; - else message{end+1} = 'Email generated by an unknown computer!!!'; - end - message{end+1} = 'ratter\ExperPort\Modules\@runrats\runrats.m'; - - ctemp = [',',contact{1},',']; - ctemp(ctemp == ' ') = ''; - cms = find(ctemp == ','); - for i = 1:length(cms)-1 - email = ctemp(cms(i)+1:cms(i+1)-1); - sendmail([email,'@princeton.edu'],['SVN Cleanup FAILED on Rig ',rig],message); - end - end - - if failed2 == 1 - message = cell(0); - message{1} = [vn,' update failed in ',pname]; - message{2} = ' '; - message{3} = message2; - if strcmp(vn,'cvs'); subject = ['SVN Update FAILED on Rig ',rig]; - elseif strcmp(vn,'svn'); subject = ['SVN Update FAILED on Rig ',rig]; - else subject = ''; - end - - IP = get_network_info; - message{end+1} = ' '; - if ischar(IP); message{end+1} = ['Email generated by ',IP]; - else message{end+1} = 'Email generated by an unknown computer!!!'; - end - message{end+1} = 'ratter\ExperPort\Modules\@runrats\runrats.m'; - - ctemp = [',',contact{1},',']; - ctemp(ctemp == ' ') = ''; - cms = find(ctemp == ','); - for i = 1:length(cms)-1 - email = ctemp(cms(i)+1:cms(i+1)-1); - sendmail([email,'@ucl.ac.uk'],subject,message); - end - end - end -catch %#ok - senderror_report; -end - - - - diff --git a/Protocols/@ArpitCentrePokeTraining/Connect_Bonsai_Camera.m b/ExperPort/Plugins/@bonsaicamera/BonsaiCameraInterface.m similarity index 51% rename from Protocols/@ArpitCentrePokeTraining/Connect_Bonsai_Camera.m rename to ExperPort/Plugins/@bonsaicamera/BonsaiCameraInterface.m index bfe913ac..752eca17 100644 --- a/Protocols/@ArpitCentrePokeTraining/Connect_Bonsai_Camera.m +++ b/ExperPort/Plugins/@bonsaicamera/BonsaiCameraInterface.m @@ -1,11 +1,27 @@ -function Connect_Bonsai_Camera(obj,action) +function [varargout] = BonsaiCameraInterface(obj,action,varargin) + +% If creating an empty object, return without further ado: +if nargin==0 || (nargin==1 && ischar(varargin{1}) && strcmp(varargin{1}, 'empty')) + return; +end GetSoloFunctionArgs(obj); +if isa(varargin{1}, mfilename) % If first arg is an object of this class itself, we are + % Most likely responding to a callback from a SoloParamHandle defined in this mfile. + if length(varargin) < 2 || ~ischar(varargin{2}) + error(['If called with a "%s" object as first arg, a second arg, a ' ... + 'string specifying the action, is required\n']); + else + action = varargin{2}; varargin = varargin(3:end); %#ok + end +else % Ok, regular call with first param being the action string. + action = varargin{1}; varargin = varargin(2:end); %#ok +end -% C:\RatterVideos\ExpName\RateName\Videos_Protocolname_ExpName_RatName_date -% (the same format as Data file) +GetSoloFunctionArgs(obj); +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%%%%%%%% DO NOT CHANGE THEM UNLESS YOU MAKE THE SAME CHANGES IN BONSAI %%%%%%%%% % UDP Connection Parameters to connect to Bonsai @@ -21,92 +37,151 @@ function Connect_Bonsai_Camera(obj,action) camera_command_address = "/camera"; % Use string type. MUST MATCH Bonsai Address Value recording_command_address = "/record"; % Use string type. MUST MATCH Bonsai Address Value -% bonsai_path = 'C:\Users\Turin\Downloads\Bonsai\Bonsai.exe'; % Path of Bonsai App +% The location of Bonsai workflow in the system. In this case it is saved +% within the ratter > ExpertPort > Plugins > @bonsaicamera folder scriptFullPath = mfilename('fullpath'); % Path of running the current script scriptDirectory = fileparts(scriptFullPath); bonsai_workflow_Path = fullfile(scriptDirectory,'Bonsai_Camera_Control','Camera_Control.bonsai'); foundworkflow = exist("bonsai_workflow_Path",'file'); -% if ~foundworkflow -% warning('could not find bonsai executable, please insert it manually'); -% [fname fpath] = uigetfile( '*.exe', 'Provide the path to Bonsai executable'); -% pathout = fullfile(fpath, fname); -% end -% file_path = 'C:\Users\Turin\Desktop\bonsai_script.bonsai'; % Path for .bonsai file +if ~foundworkflow + warning('could not find bonsai executable, please insert it manually'); + [bonsai_fname bonsai_fpath] = uigetfile( '*.exe', 'Provide the path to Bonsai executable'); + bonsai_workflow_Path = fullfile(bonsai_fpath, bonsai_fname); +end %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + +% Make the action case insensitive +action = lower(action); switch action case 'init' + if length(varargin) < 2 + error('Need at least two arguments, x and y position, to initialize %s', mfilename); + end + + x = varargin{1}; + y = varargin{2}; + varargout{1} = x; + varargout{2} = y; + + ToggleParam(obj, 'CameraControl', 1, x, y, 'OnString', 'Camera ON', ... + 'OffString', 'Camera OFF', 'TooltipString', 'Turn On/Off the streaming and Saving of Camera Feed'); + set_callback(CameraControl, {mfilename, 'camera_connection'}); %#ok (Defined just above) + next_row(y); + + %% STEP 1: Start the Bonsai app to control the USB based Camera + + % Run the Bonsai App with the workflow + + % bonsai_path = bonsaiPath(32); + % command = sprintf('"%s" "%s" --start', bonsai_path, bonsai_file_path); + % system([command, ' &']); + + % Bonsai app and the workflow is being run by helper function + + runBonsaiWorkflow(bonsai_workflow_Path); + pause(5); % wait few seconds for software to open before continuing + + + + %% STEP 2: DECLARING AND FOLDER LOCATION FOR SAVING THE VIDEOS + + % The video files are saved in the folder format declared below + % C:\RatterVideos\ExpName\RateName\Videos_Protocolname_ExpName_RatName_date + % (the same format as Data file) + + current_dir = cd; + ratter_dir = extractBefore(current_dir,'ratter'); + main_dir_video = [ratter_dir 'ratter_Videos']; + date_str = regexprep(char(datetime('today','Format','yyyy-MM-dd')), '[^0-9]', ''); + video_foldername = sprintf('video_@%s_%s_%s_%s',name,expmtr,rname,date_str); + rat_dir = sprintf('%s\\%s\\%s',main_dir_video,expmtr,rname); + video_save_dir = sprintf('%s\\%s\\%s\\%s',main_dir_video,expmtr,rname,video_foldername); + % We have the general structure of folder save location, now need to + % check if there is any other folder for same date. We will add a + % alphabet in the end based upon the no. of files present. + if exist(rat_dir,'dir') == 7 + listing = dir(rat_dir); + folderNames_rat_dir = {listing(find([listing.isdir])).name}; + folderNames_rat_dir = folderNames_rat_dir(~ismember(folderNames_rat_dir,{'.','..'})); % Remove the '.' and '..' entries (current and parent directories) + sessions_today = length(find(contains(folderNames_rat_dir,video_foldername))); % number of folders containing the video foldername + video_save_dir = [video_save_dir char(sessions_today + 97)]; + else + video_save_dir = [video_save_dir char(97)]; + end + mkdir(video_save_dir); + SoloParamHandle(obj, 'Video_Saving_Folder', 'value', video_save_dir); + % --- Create UDP Port Object --- % This object can send to any destination without prior connection udpSender = udpport("datagram","IPV4",'LocalHost',bonsaiComputerIP,... 'LocalPort',9091,'Tag','MATLABSender'); - SoloParamHandle(obj, 'UDPSender', 'value', udpSender); - - % Now that we have created the UPD connection, we can send commands - % over OSC. Before that we need to open the Bonsai App + SoloParamHandle(obj, 'UDPSender', 'value', udpSender, 'saveable', 0); - % bonsai_path = bonsaiPath(32); - % command = sprintf('"%s" "%s" --start', bonsai_path, bonsai_file_path); - % system([command, ' &']); - runBonsaiWorkflow(bonsai_workflow_Path); + + %% STEP 3: START STREAMING AND SAVING OF THE VIDEO - pause(5); + BonsaiCameraInterface(obj,'camera_connection'); + + + + + %% Camera connection On/Off + case 'camera_connection' + + if CameraControl == 1 % User Selected to Reconnect & Restart the feed from the Camera + + % Now that we have created the UPD connection, we can send commands + % over OSC. % Before starting the streaming of Camera, I need to send the % file directory for saving the file otherwise Bonsai can run into % error as it will try saving files in the predefined folder in % bonsai and that can conflict with file already present there. - oscMsg_file_directory = createOSCMessage(recording_command_address, [value(Video_Saving_Folder) '\Trial.avi']); - % write(udpSender, oscMsg_file_directory, "uint8", bonsaiComputerIP,bonsaiUdpPort); + oscMsg_file_directory = createOSCMessage(recording_command_address, [value(Video_Saving_Folder) '\Trial.avi']); + % write(udpSender, oscMsg_file_directory, "uint8", bonsaiComputerIP,bonsaiUdpPort); - % OSC message to start the camera - oscMsg_Camera_start = createOSCMessage(camera_command_address, startCommand); - % the command to send message to Bonsai - write(udpSender, oscMsg_Camera_start, "uint8", bonsaiComputerIP,bonsaiUdpPort); - - pause(3); - % NOTE: Ideally I should start saving the trials once the experimenter presses - % Run either on dispatcher or Runrats. But, I dont want to make the changes there - % so would start recording as soon as the protocol is loaded and camera starts streaming + % OSC message to start the camera + oscMsg_Camera_start = createOSCMessage(camera_command_address, startCommand); + % the command to send message to Bonsai + write(value(UDPSender), oscMsg_Camera_start, "uint8", bonsaiComputerIP,bonsaiUdpPort); - write(udpSender, oscMsg_file_directory, "uint8", bonsaiComputerIP,bonsaiUdpPort); + pause(3); + + % NOTE: Ideally I should start saving the trials once the experimenter presses + % Run either on dispatcher or Runrats. But, I dont want to make the changes there + % so would start recording as soon as the protocol is loaded and camera starts streaming - case 'next_trial' - - % in this I send a command to bonsai so that it creates a new file - % for each trial + write(value(UDPSender), oscMsg_file_directory, "uint8", bonsaiComputerIP,bonsaiUdpPort); - oscMsg_file_directory = createOSCMessage(recording_command_address, [value(Video_Saving_Folder) '\Trial.avi']); - write(value(UDPSender), oscMsg_file_directory, "uint8", bonsaiComputerIP,bonsaiUdpPort); + else % User Stopped the streaming and saving OF VIDEO - case 'start' + % this stops saving and streaming of the camera + oscMsg_Camera_stop = createOSCMessage("/camera", stopCommand); + write(value(UDPSender), oscMsg_Camera_stop, "uint8", bonsaiComputerIP,bonsaiUdpPort); - oscMsg_file_directory = createOSCMessage(recording_command_address, [value(Video_Saving_Folder) '\Trial.avi']); - % write(udpSender, oscMsg_file_directory, "uint8", bonsaiComputerIP,bonsaiUdpPort); + end - % OSC message to start the camera - oscMsg_Camera_start = createOSCMessage(camera_command_address, startCommand); - % the command to send message to Bonsai - write(value(UDPSender), oscMsg_Camera_start, "uint8", bonsaiComputerIP,bonsaiUdpPort); + + %% next trial + % SEND STRING TO BONSAI AT THE END OF THE TRIAL TO SAVE NEXT TRIAL IN + % NEW VIDEO FILE + case 'next_trial' - pause(3); - % NOTE: Ideally I should start saving the trials once the experimenter presses - % Run either on dispatcher or Runrats. But, I dont want to make the changes there - % so would start recording as soon as the protocol is loaded and camera starts streaming + % in this I send a command to bonsai so that it creates a new file + % for each trial + oscMsg_file_directory = createOSCMessage(recording_command_address, [value(Video_Saving_Folder) '\Trial.avi']); write(value(UDPSender), oscMsg_file_directory, "uint8", bonsaiComputerIP,bonsaiUdpPort); - - case 'stop' - - % this stops saving and streaming of the camera - oscMsg_Camera_stop = createOSCMessage("/camera", stopCommand); - write(value(UDPSender), oscMsg_Camera_stop, "uint8", bonsaiComputerIP,bonsaiUdpPort); - + + %% close bonsai and command window case 'close' % this would stop the workflow @@ -122,7 +197,6 @@ function Connect_Bonsai_Camera(obj,action) end -end %%%%%%%%%%%%%%% ADD 0N Functions %%%%%%%%%%%%%%%%%%%% @@ -262,3 +336,6 @@ function runBonsaiWorkflow(workflowPath, addOptArray, bonsaiExePath, external) end end + + +end \ No newline at end of file diff --git a/Protocols/@ArpitCentrePokeTraining/Bonsai_Camera_Control/Camera_Control.bonsai b/ExperPort/Plugins/@bonsaicamera/Camera_Control.bonsai similarity index 100% rename from Protocols/@ArpitCentrePokeTraining/Bonsai_Camera_Control/Camera_Control.bonsai rename to ExperPort/Plugins/@bonsaicamera/Camera_Control.bonsai diff --git a/Protocols/@ArpitCentrePokeTraining/Bonsai_Camera_Control/Camera_Control.bonsai.layout b/ExperPort/Plugins/@bonsaicamera/Camera_Control.bonsai.layout similarity index 100% rename from Protocols/@ArpitCentrePokeTraining/Bonsai_Camera_Control/Camera_Control.bonsai.layout rename to ExperPort/Plugins/@bonsaicamera/Camera_Control.bonsai.layout diff --git a/ExperPort/Plugins/@bonsaicamera/bonsaicamera.m b/ExperPort/Plugins/@bonsaicamera/bonsaicamera.m new file mode 100644 index 00000000..c6b2a557 --- /dev/null +++ b/ExperPort/Plugins/@bonsaicamera/bonsaicamera.m @@ -0,0 +1,4 @@ +function [obj] = bonsaicamera(varargin) + +%Argument handling +obj = class(struct, mfilename, saveload); \ No newline at end of file diff --git a/ExperPort/settings_@ArpitCentrePokeTraining_Arpit_AR1_250427a.mat b/ExperPort/settings_@ArpitCentrePokeTraining_Arpit_AR1_250427a.mat deleted file mode 100644 index 0414150f656e2e68660a8cb175f6d0e28a8f9fc7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 15252 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 Fe*rC~`D_3H diff --git a/ExperPort/settings_@ArpitCentrePokeTraining_experimenter_ratname_250423b.mat b/ExperPort/settings_@ArpitCentrePokeTraining_experimenter_ratname_250423b.mat deleted file mode 100644 index 5da9b2ccb9745c8df2533df4339e312c16008eed..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 16109 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∓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 K`FOsJ_ - - %% Before preparing the trial, start with the Bonsai app to control the USB based Camera - % Declare the folder location for saving the video files + [x, y] = BonsaiCameraInterface(obj,'init',x,y); - current_dir = cd; - ratter_dir = extractBefore(current_dir,'ratter'); - main_dir_video = [ratter_dir 'ratter_Videos']; - date_str = regexprep(char(datetime('today','Format','yyyy-MM-dd')), '[^0-9]', ''); - video_foldername = sprintf('video_@%s_%s_%s_%s',name,expmtr,rname,date_str); - rat_dir = sprintf('%s\\%s\\%s',main_dir_video,expmtr,rname); - video_save_dir = sprintf('%s\\%s\\%s\\%s',main_dir_video,expmtr,rname,video_foldername); - % We have the general structure of folder save location, now need to - % check if there is any other folder for same date. We will add a - % alphabet in the end based upon the no. of files present. - if exist(rat_dir,'dir') == 7 - listing = dir(rat_dir); - folderNames_rat_dir = {listing(find([listing.isdir])).name}; - folderNames_rat_dir = folderNames_rat_dir(~ismember(folderNames_rat_dir,{'.','..'})); % Remove the '.' and '..' entries (current and parent directories) - sessions_today = length(find(contains(folderNames_rat_dir,video_foldername))); % number of folders containing the video foldername - video_save_dir = [video_save_dir char(sessions_today + 97)]; - else - video_save_dir = [video_save_dir char(97)]; - end - mkdir(video_save_dir); - SoloParamHandle(obj, 'Video_Saving_Folder', 'value', video_save_dir); - SoloFunctionAddVars('Connect_Bonsai_Camera', 'ro_args', ... - {'Video_Saving_Folder'}); - Connect_Bonsai_Camera(obj,'init'); + % %% Before preparing the trial, start with the Bonsai app to control the USB based Camera + % % Declare the folder location for saving the video files + % + % current_dir = cd; + % ratter_dir = extractBefore(current_dir,'ratter'); + % main_dir_video = [ratter_dir 'ratter_Videos']; + % date_str = regexprep(char(datetime('today','Format','yyyy-MM-dd')), '[^0-9]', ''); + % video_foldername = sprintf('video_@%s_%s_%s_%s',name,expmtr,rname,date_str); + % rat_dir = sprintf('%s\\%s\\%s',main_dir_video,expmtr,rname); + % video_save_dir = sprintf('%s\\%s\\%s\\%s',main_dir_video,expmtr,rname,video_foldername); + % % We have the general structure of folder save location, now need to + % % check if there is any other folder for same date. We will add a + % % alphabet in the end based upon the no. of files present. + % if exist(rat_dir,'dir') == 7 + % listing = dir(rat_dir); + % folderNames_rat_dir = {listing(find([listing.isdir])).name}; + % folderNames_rat_dir = folderNames_rat_dir(~ismember(folderNames_rat_dir,{'.','..'})); % Remove the '.' and '..' entries (current and parent directories) + % sessions_today = length(find(contains(folderNames_rat_dir,video_foldername))); % number of folders containing the video foldername + % video_save_dir = [video_save_dir char(sessions_today + 97)]; + % else + % video_save_dir = [video_save_dir char(97)]; + % end + % mkdir(video_save_dir); + % SoloParamHandle(obj, 'Video_Saving_Folder', 'value', video_save_dir); + % SoloFunctionAddVars('Connect_Bonsai_Camera', 'ro_args', ... + % {'Video_Saving_Folder'}); + % Connect_Bonsai_Camera(obj,'init'); %% + % feval(mfilename, obj, 'prepare_next_trial'); % Commented out because it is also run by Runrats(while loading the protocol) + + %%% + % case 'camera_control' + % + % if value(Connect_Camera) == 1 + % Connect_Bonsai_Camera(obj,'start'); + % else + % Connect_Bonsai_Camera(obj,'stop'); + % end - % feval(mfilename, obj, 'prepare_next_trial'); % Commented out because - %%% it is being also run by Runrats(while loading the protocol) - %% change_water_modulation_params - case 'camera_control' - - if value(Connect_Camera) == 1 - Connect_Bonsai_Camera(obj,'start'); - else - Connect_Bonsai_Camera(obj,'stop'); - end - case 'change_water_modulation_params' display_guys = [1 150 300]; for i=1:numel(display_guys) @@ -290,9 +290,12 @@ case 'trial_completed' % Change the video trial Connect_Bonsai_Camera(obj,'next_trial'); - % Update the Metrics Calculated - Training_Performance_Summary(obj,'evaluate'); - SessionPerformanceSection(obj, 'evaluate'); + + % Update the Metrics Calculated, Instead being Calculated in Session + % Definition and commented out + + % Training_Performance_Summary(obj,'evaluate'); + % SessionPerformanceSection(obj, 'evaluate'); % Do any updates in the protocol that need doing: feval(mfilename, 'update'); diff --git a/Protocols/@ArpitCentrePokeTraining/ArpitCentrePokeTraining_SessionDefinition_AutoTrainingStages.m b/Protocols/@ArpitCentrePokeTraining/ArpitCentrePokeTraining_SessionDefinition_AutoTrainingStages.m index 95c60b82..3807d3e1 100644 --- a/Protocols/@ArpitCentrePokeTraining/ArpitCentrePokeTraining_SessionDefinition_AutoTrainingStages.m +++ b/Protocols/@ArpitCentrePokeTraining/ArpitCentrePokeTraining_SessionDefinition_AutoTrainingStages.m @@ -36,6 +36,8 @@ % + +% Update ParamsSection ParamsSection_MaxSame.value = 4; callback(ParamsSection_MaxSame); stage_no = value(SessionDefinition_CURRENT_ACTIVE_STAGE); @@ -43,6 +45,50 @@ ParamsSection_training_stage.value = stage_no; callback(ParamsSection_training_stage); end + +% Update Training_ParamsSection +if value(previous_sides(end)) ~= value(previous_sides(end-1)) && all(hit_history(end-1:end)) % last and present trials should also be a valid trial + Training_ParamsSection_trial_oppSide = value(Training_ParamsSection_trial_oppSide) + 1; % updating value for variable in TrainingParams_Section + callback(Training_ParamsSection_trial_oppSide); +end + +% Updating Disp Values for Training_Peformance_Summary +if n_completed_trials > 0 + Training_Performance_Summary_stage_1_Trials.value = value(Training_Performance_Summary_stage_1_Trials) + 1; + Training_Performance_Summary_stage_1_TrialsToday.value = value(Training_Performance_Summary_stage_1_TrialsToday) + 1; + Training_Performance_Summary_stage_1_ViolationRate.value = nan; + Training_Performance_Summary_stage_1_TimeoutRate.value = nan; + if ~isnan(value(hit_history(end))) + Training_Performance_Summary_stage_1_TrialsValid.value = value(Training_Performance_Summary_stage_1_TrialsValid) + 1; + end + callback(Training_Performance_Summary_stage_1_Trials); + callback(Training_Performance_Summary_stage_1_TrialsToday) + callback(Training_Performance_Summary_stage_1_ViolationRate); + callback(Training_Performance_Summary_stage_1_TimeoutRate); + callback(Training_Performance_Summary_stage_1_TrialsValid); + + % Updating Disp Values for Training_Peformance_Summary + SessionPerformanceSection_ntrials.value = n_completed_trials; + SessionPerformanceSection_violation_rate.value = nan; + SessionPerformanceSection_timeout_rate.value = nan; + SessionPerformanceSection_violation_recent.value = nan; + SessionPerformanceSection_timeout_recent.value = nan; + SessionPerformanceSection_ntrials_stage.value = value(Training_Performance_Summary_stage_1_Trials); + SessionPerformanceSection_ntrials_stage_today.value = value(Training_Performance_Summary_stage_1_TrialsToday); + SessionPerformanceSection_timeout_stage.value = nan; + SessionPerformanceSection_violation_stage.value = nan; + + callback(SessionPerformanceSection_ntrials); + callback(SessionPerformanceSection_ntrials_stage); + callback(SessionPerformanceSection_ntrials_stage_today) + callback(SessionPerformanceSection_violation_rate); + callback(SessionPerformanceSection_timeout_rate); + callback(SessionPerformanceSection_violation_recent); + callback(SessionPerformanceSection_timeout_recent); + callback(SessionPerformanceSection_violation_stage); + callback(SessionPerformanceSection_timeout_stage); + +end % end @@ -132,7 +178,7 @@ ClearHelperVarsNotOwned(obj); % % Change ParamsSection Vars -ParamsSection_MaxSame.value = 3; +ParamsSection_MaxSame.value = 4; callback(ParamsSection_MaxSame); stage_no = value(SessionDefinition_CURRENT_ACTIVE_STAGE); if stage_no ~= value(ParamsSection_training_stage) @@ -161,6 +207,55 @@ end end +% Update Training_ParamsSection +if value(previous_sides(end)) ~= value(previous_sides(end-1)) && all(hit_history(end-1:end)) % last and present trials should also be a valid trial + Training_ParamsSection_trial_oppSide = value(Training_ParamsSection_trial_oppSide) + 1; % updating value for variable in TrainingParams_Section + callback(Training_ParamsSection_trial_oppSide); +end + + +% Updating Disp Values for Training_Peformance_Summary +if n_completed_trials > 0 + Training_Performance_Summary_stage_2_Trials.value = value(Training_Performance_Summary_stage_2_Trials) + 1; + Training_Performance_Summary_stage_2_TrialsToday.value = value(Training_Performance_Summary_stage_2_TrialsToday) + 1; + Training_Performance_Summary_stage_2_ViolationRate.value = nan; + Training_Performance_Summary_stage_2_TimeoutRate.value = ((value(Training_Performance_Summary_stage_2_TimeoutRate) * (value(Training_Performance_Summary_stage_2_Trials) - 1)) + double(timeout_history(end))) / value(Training_Performance_Summary_stage_2_Trials); + if ~isnan(value(hit_history(end))) + Training_Performance_Summary_stage_2_TrialsValid.value = value(Training_Performance_Summary_stage_2_TrialsValid) + 1; + end + + callback(Training_Performance_Summary_stage_2_Trials); + callback(Training_Performance_Summary_stage_2_TrialsToday) + callback(Training_Performance_Summary_stage_2_ViolationRate); + callback(Training_Performance_Summary_stage_2_TimeoutRate); + callback(Training_Performance_Summary_stage_2_TrialsValid); + + % Updating Disp Values for Training_Peformance_Summary + SessionPerformanceSection_ntrials.value = n_completed_trials; + SessionPerformanceSection_violation_rate.value = nan; + SessionPerformanceSection_timeout_rate.value = numel(find(timeout_history))/n_completed_trials; + SessionPerformanceSection_violation_recent.value = nan; + if n_completed_trials >= 20 + SessionPerformanceSection_timeout_recent.value = numel(find(timeout_history(end-19:end)))/20; + else + SessionPerformanceSection_timeout_recent.value = nan; + end + SessionPerformanceSection_violation_stage.value = nan; + SessionPerformanceSection_ntrials_stage.value = value(Training_Performance_Summary_stage_2_Trials); + SessionPerformanceSection_ntrials_stage_today.value = value(Training_Performance_Summary_stage_2_TrialsToday); + SessionPerformanceSection_timeout_stage.value = value(Training_Performance_Summary_stage_2_TimeoutRate); + + callback(SessionPerformanceSection_ntrials); + callback(SessionPerformanceSection_ntrials_stage); + callback(SessionPerformanceSection_ntrials_stage_today) + callback(SessionPerformanceSection_violation_rate); + callback(SessionPerformanceSection_timeout_rate); + callback(SessionPerformanceSection_violation_recent); + callback(SessionPerformanceSection_timeout_recent); + callback(SessionPerformanceSection_violation_stage); + callback(SessionPerformanceSection_timeout_stage); +end + % end @@ -266,6 +361,49 @@ end end callback(ParamsSection_CP_duration); + +if n_completed_trials > 0 + % Updating Disp Values for Training_Peformance_Summary + Training_Performance_Summary_stage_3_Trials.value = value(Training_Performance_Summary_stage_3_Trials) + 1; + Training_Performance_Summary_stage_3_TrialsToday.value = value(Training_Performance_Summary_stage_3_TrialsToday) + 1; + Training_Performance_Summary_stage_3_ViolationRate.value = nan; + Training_Performance_Summary_stage_3_TimeoutRate.value = ((value(Training_Performance_Summary_stage_3_TimeoutRate) * (value(Training_Performance_Summary_stage_3_Trials) - 1)) + double(timeout_history(end))) / value(Training_Performance_Summary_stage_3_Trials); + if ~isnan(value(hit_history(end))) + Training_Performance_Summary_stage_3_TrialsValid.value = value(Training_Performance_Summary_stage_3_TrialsValid) + 1; + end + + callback(Training_Performance_Summary_stage_3_Trials); + callback(Training_Performance_Summary_stage_3_TrialsToday) + callback(Training_Performance_Summary_stage_3_ViolationRate); + callback(Training_Performance_Summary_stage_3_TimeoutRate); + callback(Training_Performance_Summary_stage_3_TrialsValid); + + % Updating Disp Values for Training_Peformance_Summary + SessionPerformanceSection_ntrials.value = n_completed_trials; + SessionPerformanceSection_violation_rate.value = nan; + SessionPerformanceSection_timeout_rate.value = numel(find(timeout_history))/n_completed_trials; + SessionPerformanceSection_violation_recent.value = nan; + if n_completed_trials >= 20 + SessionPerformanceSection_timeout_recent.value = numel(find(timeout_history(end-19:end)))/20; + else + SessionPerformanceSection_timeout_recent.value = nan; + end + SessionPerformanceSection_violation_stage.value = nan; + SessionPerformanceSection_ntrials_stage.value = value(Training_Performance_Summary_stage_3_Trials); + SessionPerformanceSection_ntrials_stage_today.value = value(Training_Performance_Summary_stage_3_TrialsToday); + SessionPerformanceSection_timeout_stage.value = value(Training_Performance_Summary_stage_3_TimeoutRate); + + callback(SessionPerformanceSection_ntrials); + callback(SessionPerformanceSection_ntrials_stage); + callback(SessionPerformanceSection_ntrials_stage_today) + callback(SessionPerformanceSection_violation_rate); + callback(SessionPerformanceSection_timeout_rate); + callback(SessionPerformanceSection_violation_recent); + callback(SessionPerformanceSection_timeout_recent); + callback(SessionPerformanceSection_violation_stage); + callback(SessionPerformanceSection_timeout_stage); +end + % end @@ -379,6 +517,49 @@ end callback(ParamsSection_CP_duration); + +if n_completed_trials > 0 + % Updating Disp Values for Training_Peformance_Summary + Training_Performance_Summary_stage_4_Trials.value = value(Training_Performance_Summary_stage_4_Trials) + 1; + Training_Performance_Summary_stage_4_TrialsToday.value = value(Training_Performance_Summary_stage_4_TrialsToday) + 1; + Training_Performance_Summary_stage_4_ViolationRate.value = ((value(Training_Performance_Summary_stage_4_ViolationRate) * (value(Training_Performance_Summary_stage_4_Trials) - 1)) + double(violation_history(end))) / value(Training_Performance_Summary_stage_4_Trials); + Training_Performance_Summary_stage_4_TimeoutRate.value = ((value(Training_Performance_Summary_stage_4_TimeoutRate) * (value(Training_Performance_Summary_stage_4_Trials) - 1)) + double(timeout_history(end))) / value(Training_Performance_Summary_stage_4_Trials); + if ~isnan(value(hit_history(end))) + Training_Performance_Summary_stage_4_TrialsValid.value = value(Training_Performance_Summary_stage_4_TrialsValid) + 1; + end + + callback(Training_Performance_Summary_stage_4_Trials); + callback(Training_Performance_Summary_stage_4_TrialsToday) + callback(Training_Performance_Summary_stage_4_ViolationRate); + callback(Training_Performance_Summary_stage_4_TimeoutRate); + callback(Training_Performance_Summary_stage_4_TrialsValid); + + % Updating Disp Values for Training_Peformance_Summary + SessionPerformanceSection_ntrials.value = n_completed_trials; + SessionPerformanceSection_violation_rate.value = numel(find(violation_history))/n_completed_trials; + SessionPerformanceSection_timeout_rate.value = numel(find(timeout_history))/n_completed_trials; + if n_completed_trials >= 20 + SessionPerformanceSection_violation_recent.value = numel(find(violation_history(end-19:end)))/20; + SessionPerformanceSection_timeout_recent.value = numel(find(timeout_history(end-19:end)))/20; + else + SessionPerformanceSection_timeout_recent.value = nan; + SessionPerformanceSection_violation_recent.value = nan; + end + SessionPerformanceSection_violation_stage.value = value(Training_Performance_Summary_stage_4_ViolationRate); + SessionPerformanceSection_ntrials_stage.value = value(Training_Performance_Summary_stage_4_Trials); + SessionPerformanceSection_ntrials_stage_today.value = value(Training_Performance_Summary_stage_4_TrialsToday); + SessionPerformanceSection_timeout_stage.value = value(Training_Performance_Summary_stage_4_TimeoutRate); + + callback(SessionPerformanceSection_ntrials); + callback(SessionPerformanceSection_ntrials_stage); + callback(SessionPerformanceSection_ntrials_stage_today) + callback(SessionPerformanceSection_violation_rate); + callback(SessionPerformanceSection_timeout_rate); + callback(SessionPerformanceSection_violation_recent); + callback(SessionPerformanceSection_timeout_recent); + callback(SessionPerformanceSection_violation_stage); + callback(SessionPerformanceSection_timeout_stage); +end % end @@ -535,6 +716,50 @@ callback(ParamsSection_PreStim_time); callback(ParamsSection_A1_time); end + +if n_completed_trials > 0 + % Updating Disp Values for Training_Peformance_Summary + Training_Performance_Summary_stage_5_Trials.value = value(Training_Performance_Summary_stage_5_Trials) + 1; + Training_Performance_Summary_stage_5_TrialsToday.value = value(Training_Performance_Summary_stage_5_TrialsToday) + 1; + Training_Performance_Summary_stage_5_ViolationRate.value = ((value(Training_Performance_Summary_stage_5_ViolationRate) * (value(Training_Performance_Summary_stage_5_Trials) - 1)) + double(violation_history(end))) / value(Training_Performance_Summary_stage_5_Trials); + Training_Performance_Summary_stage_5_TimeoutRate.value = ((value(Training_Performance_Summary_stage_5_TimeoutRate) * (value(Training_Performance_Summary_stage_5_Trials) - 1)) + double(timeout_history(end))) / value(Training_Performance_Summary_stage_5_Trials); + if ~isnan(value(hit_history(end))) + Training_Performance_Summary_stage_5_TrialsValid.value = value(Training_Performance_Summary_stage_5_TrialsValid) + 1; + end + + callback(Training_Performance_Summary_stage_5_Trials); + callback(Training_Performance_Summary_stage_5_TrialsToday) + callback(Training_Performance_Summary_stage_5_ViolationRate); + callback(Training_Performance_Summary_stage_5_TimeoutRate); + callback(Training_Performance_Summary_stage_5_TrialsValid); + + % Updating Disp Values for Training_Peformance_Summary + SessionPerformanceSection_ntrials.value = n_completed_trials; + SessionPerformanceSection_violation_rate.value = numel(find(violation_history))/n_completed_trials; + SessionPerformanceSection_timeout_rate.value = numel(find(timeout_history))/n_completed_trials; + if n_completed_trials >= 20 + SessionPerformanceSection_violation_recent.value = numel(find(violation_history(end-19:end)))/20; + SessionPerformanceSection_timeout_recent.value = numel(find(timeout_history(end-19:end)))/20; + else + SessionPerformanceSection_timeout_recent.value = nan; + SessionPerformanceSection_violation_recent.value = nan; + end + SessionPerformanceSection_violation_stage.value = value(Training_Performance_Summary_stage_5_ViolationRate); + SessionPerformanceSection_ntrials_stage.value = value(Training_Performance_Summary_stage_5_Trials); + SessionPerformanceSection_ntrials_stage_today.value = value(Training_Performance_Summary_stage_5_TrialsToday); + SessionPerformanceSection_timeout_stage.value = value(Training_Performance_Summary_stage_5_TimeoutRate); + + callback(SessionPerformanceSection_ntrials); + callback(SessionPerformanceSection_ntrials_stage); + callback(SessionPerformanceSection_ntrials_stage_today) + callback(SessionPerformanceSection_violation_rate); + callback(SessionPerformanceSection_timeout_rate); + callback(SessionPerformanceSection_violation_recent); + callback(SessionPerformanceSection_timeout_recent); + callback(SessionPerformanceSection_violation_stage); + callback(SessionPerformanceSection_timeout_stage); +end + % end @@ -673,6 +898,49 @@ callback(ParamsSection_A1_time); end +if n_completed_trials > 0 + % Updating Disp Values for Training_Peformance_Summary + Training_Performance_Summary_stage_6_Trials.value = value(Training_Performance_Summary_stage_6_Trials) + 1; + Training_Performance_Summary_stage_6_TrialsToday.value = value(Training_Performance_Summary_stage_6_TrialsToday) + 1; + Training_Performance_Summary_stage_6_ViolationRate.value = ((value(Training_Performance_Summary_stage_6_ViolationRate) * (value(Training_Performance_Summary_stage_6_Trials) - 1)) + double(violation_history(end))) / value(Training_Performance_Summary_stage_6_Trials); + Training_Performance_Summary_stage_6_TimeoutRate.value = ((value(Training_Performance_Summary_stage_6_TimeoutRate) * (value(Training_Performance_Summary_stage_6_Trials) - 1)) + double(timeout_history(end))) / value(Training_Performance_Summary_stage_6_Trials); + if ~isnan(value(hit_history(end))) + Training_Performance_Summary_stage_6_TrialsValid.value = value(Training_Performance_Summary_stage_6_TrialsValid) + 1; + end + + callback(Training_Performance_Summary_stage_6_Trials); + callback(Training_Performance_Summary_stage_6_TrialsToday) + callback(Training_Performance_Summary_stage_6_ViolationRate); + callback(Training_Performance_Summary_stage_6_TimeoutRate); + callback(Training_Performance_Summary_stage_6_TrialsValid); + + % Updating Disp Values for SessionPerformanceSection + SessionPerformanceSection_ntrials.value = n_completed_trials; + SessionPerformanceSection_violation_rate.value = numel(find(violation_history))/n_completed_trials; + SessionPerformanceSection_timeout_rate.value = numel(find(timeout_history))/n_completed_trials; + if n_completed_trials >= 20 + SessionPerformanceSection_violation_recent.value = numel(find(violation_history(end-19:end)))/20; + SessionPerformanceSection_timeout_recent.value = numel(find(timeout_history(end-19:end)))/20; + else + SessionPerformanceSection_timeout_recent.value = nan; + SessionPerformanceSection_violation_recent.value = nan; + end + SessionPerformanceSection_violation_stage.value = value(Training_Performance_Summary_stage_6_ViolationRate); + SessionPerformanceSection_ntrials_stage.value = value(Training_Performance_Summary_stage_6_Trials); + SessionPerformanceSection_ntrials_stage_today.value = value(Training_Performance_Summary_stage_6_TrialsToday); + SessionPerformanceSection_timeout_stage.value = value(Training_Performance_Summary_stage_6_TimeoutRate); + + callback(SessionPerformanceSection_ntrials); + callback(SessionPerformanceSection_ntrials_stage); + callback(SessionPerformanceSection_ntrials_stage_today) + callback(SessionPerformanceSection_violation_rate); + callback(SessionPerformanceSection_timeout_rate); + callback(SessionPerformanceSection_violation_recent); + callback(SessionPerformanceSection_timeout_recent); + callback(SessionPerformanceSection_violation_stage); + callback(SessionPerformanceSection_timeout_stage); +end + % end if completion_test_eval @@ -812,6 +1080,50 @@ ParamsSection_CP_duration.value = value(ParamsSection_SettlingIn_time) + value(ParamsSection_PreStim_time) + value(ParamsSection_A1_time) + value(ParamsSection_time_bet_aud1_gocue); end callback(ParamsSection_CP_duration); + +if n_completed_trials > 0 + % Updating Disp Values for Training_Peformance_Summary + Training_Performance_Summary_stage_7_Trials.value = value(Training_Performance_Summary_stage_7_Trials) + 1; + Training_Performance_Summary_stage_7_TrialsToday.value = value(Training_Performance_Summary_stage_7_TrialsToday) + 1; + Training_Performance_Summary_stage_7_ViolationRate.value = ((value(Training_Performance_Summary_stage_7_ViolationRate) * (value(Training_Performance_Summary_stage_7_Trials) - 1)) + double(violation_history(end))) / value(Training_Performance_Summary_stage_7_Trials); + Training_Performance_Summary_stage_7_TimeoutRate.value = ((value(Training_Performance_Summary_stage_7_TimeoutRate) * (value(Training_Performance_Summary_stage_7_Trials) - 1)) + double(timeout_history(end))) / value(Training_Performance_Summary_stage_7_Trials); + if ~isnan(value(hit_history(end))) + Training_Performance_Summary_stage_7_TrialsValid.value = value(Training_Performance_Summary_stage_7_TrialsValid) + 1; + end + + callback(Training_Performance_Summary_stage_7_Trials); + callback(Training_Performance_Summary_stage_7_TrialsToday) + callback(Training_Performance_Summary_stage_7_ViolationRate); + callback(Training_Performance_Summary_stage_7_TimeoutRate); + callback(Training_Performance_Summary_stage_7_TrialsValid); + + % Updating Disp Values for SessionPerformanceSection + SessionPerformanceSection_ntrials.value = n_completed_trials; + SessionPerformanceSection_violation_rate.value = numel(find(violation_history))/n_completed_trials; + SessionPerformanceSection_timeout_rate.value = numel(find(timeout_history))/n_completed_trials; + if n_completed_trials >= 20 + SessionPerformanceSection_violation_recent.value = numel(find(violation_history(end-19:end)))/20; + SessionPerformanceSection_timeout_recent.value = numel(find(timeout_history(end-19:end)))/20; + else + SessionPerformanceSection_timeout_recent.value = nan; + SessionPerformanceSection_violation_recent.value = nan; + end + SessionPerformanceSection_violation_stage.value = value(Training_Performance_Summary_stage_7_ViolationRate); + SessionPerformanceSection_ntrials_stage.value = value(Training_Performance_Summary_stage_7_Trials); + SessionPerformanceSection_ntrials_stage_today.value = value(Training_Performance_Summary_stage_7_TrialsToday); + SessionPerformanceSection_timeout_stage.value = value(Training_Performance_Summary_stage_7_TimeoutRate); + + callback(SessionPerformanceSection_ntrials); + callback(SessionPerformanceSection_ntrials_stage); + callback(SessionPerformanceSection_ntrials_stage_today) + callback(SessionPerformanceSection_violation_rate); + callback(SessionPerformanceSection_timeout_rate); + callback(SessionPerformanceSection_violation_recent); + callback(SessionPerformanceSection_timeout_recent); + callback(SessionPerformanceSection_violation_stage); + callback(SessionPerformanceSection_timeout_stage); +end + % end @@ -951,6 +1263,50 @@ ParamsSection_CP_duration.value = value(ParamsSection_SettlingIn_time) + value(ParamsSection_PreStim_time) + value(ParamsSection_A1_time) + value(ParamsSection_time_bet_aud1_gocue); end callback(ParamsSection_CP_duration); + +if n_completed_trials > 0 + % Updating Disp Values for Training_Peformance_Summary + Training_Performance_Summary_stage_8_Trials.value = value(Training_Performance_Summary_stage_8_Trials) + 1; + Training_Performance_Summary_stage_8_TrialsToday.value = value(Training_Performance_Summary_stage_8_TrialsToday) + 1; + Training_Performance_Summary_stage_8_ViolationRate.value = ((value(Training_Performance_Summary_stage_8_ViolationRate) * (value(Training_Performance_Summary_stage_8_Trials) - 1)) + double(violation_history(end))) / value(Training_Performance_Summary_stage_8_Trials); + Training_Performance_Summary_stage_8_TimeoutRate.value = ((value(Training_Performance_Summary_stage_8_TimeoutRate) * (value(Training_Performance_Summary_stage_8_Trials) - 1)) + double(timeout_history(end))) / value(Training_Performance_Summary_stage_8_Trials); + if ~isnan(value(hit_history(end))) + Training_Performance_Summary_stage_8_TrialsValid.value = value(Training_Performance_Summary_stage_8_TrialsValid) + 1; + end + + callback(Training_Performance_Summary_stage_8_Trials); + callback(Training_Performance_Summary_stage_8_TrialsToday) + callback(Training_Performance_Summary_stage_8_ViolationRate); + callback(Training_Performance_Summary_stage_8_TimeoutRate); + callback(Training_Performance_Summary_stage_8_TrialsValid); + + % Updating Disp Values for SessionPerformanceSection + SessionPerformanceSection_ntrials.value = n_completed_trials; + SessionPerformanceSection_violation_rate.value = numel(find(violation_history))/n_completed_trials; + SessionPerformanceSection_timeout_rate.value = numel(find(timeout_history))/n_completed_trials; + if n_completed_trials >= 20 + SessionPerformanceSection_violation_recent.value = numel(find(violation_history(end-19:end)))/20; + SessionPerformanceSection_timeout_recent.value = numel(find(timeout_history(end-19:end)))/20; + else + SessionPerformanceSection_timeout_recent.value = nan; + SessionPerformanceSection_violation_recent.value = nan; + end + SessionPerformanceSection_violation_stage.value = value(Training_Performance_Summary_stage_8_ViolationRate); + SessionPerformanceSection_ntrials_stage.value = value(Training_Performance_Summary_stage_8_Trials); + SessionPerformanceSection_ntrials_stage_today.value = value(Training_Performance_Summary_stage_8_TrialsToday); + SessionPerformanceSection_timeout_stage.value = value(Training_Performance_Summary_stage_8_TimeoutRate); + + callback(SessionPerformanceSection_ntrials); + callback(SessionPerformanceSection_ntrials_stage); + callback(SessionPerformanceSection_ntrials_stage_today) + callback(SessionPerformanceSection_violation_rate); + callback(SessionPerformanceSection_timeout_rate); + callback(SessionPerformanceSection_violation_recent); + callback(SessionPerformanceSection_timeout_recent); + callback(SessionPerformanceSection_violation_stage); + callback(SessionPerformanceSection_timeout_stage); +end + % end diff --git a/Protocols/@ArpitCentrePokeTraining/ParamsSection.m b/Protocols/@ArpitCentrePokeTraining/ParamsSection.m index f354bee7..81d27a05 100644 --- a/Protocols/@ArpitCentrePokeTraining/ParamsSection.m +++ b/Protocols/@ArpitCentrePokeTraining/ParamsSection.m @@ -32,7 +32,7 @@ next_row(y); NumeditParam(obj, 'drink_time', 1, x,y,'label','Drink Time','TooltipString','waits to finish water delivery'); next_row(y); - NumeditParam(obj, 'timeout_iti', 5, x,y,'label','No Choice Timeout','TooltipString','ITI on timeout trials'); + NumeditParam(obj, 'timeout_iti', 1, x,y,'label','No Choice Timeout','TooltipString','ITI on timeout trials'); next_row(y); NumeditParam(obj, 'violation_iti', 1, x,y,'label','Violation Timeout','TooltipString','Center poke violation duration'); % Reward Collection @@ -299,7 +299,7 @@ % change the reward collection duration once we start with centre % poke if value(training_stage) >= 3 - RewardCollection_duration.value = 30; + RewardCollection_duration.value = 100; Go_Sound.value = 1; end diff --git a/Protocols/@ArpitCentrePokeTraining/SessionPerformanceSection.m b/Protocols/@ArpitCentrePokeTraining/SessionPerformanceSection.m index 5375c7eb..bdcf0cf6 100644 --- a/Protocols/@ArpitCentrePokeTraining/SessionPerformanceSection.m +++ b/Protocols/@ArpitCentrePokeTraining/SessionPerformanceSection.m @@ -77,74 +77,16 @@ next_row(y, 1.5); % SoloParamHandle(obj, 'previous_parameters', 'value', []); - SoloFunctionAddVars('Training_Performance_Summary', 'ro_args', ... - {'ntrials_stage';'ntrials_stage_today';'violation_stage';'timeout_stage'}); - % ------------------------------------------------------------------ % evaluate % ------------------------------------------------------------------ case 'evaluate' - - eval(sprintf('ntrials_stage.value = value(stage_%i_Trials);',value(training_stage))); - eval(sprintf('ntrials_stage_today.value = value(stage_%i_TrialsToday);',value(training_stage))); - eval(sprintf('ntrials_violation_stage.value = value(stage_%i_ViolationRate);',value(training_stage))); - eval(sprintf('ntrials_violation_stage.value = value(stage_%i_TimeoutRate);',value(training_stage))); - - switch value(training_stage) - - case 1 %% center led on -> poke in the center -> go cue -> reward light and sound - if n_completed_trials > 1 - ntrials.value = n_completed_trials; - violation_rate.value = nan; - timeout_rate.value = nan; - end - violation_recent.value = nan; - timeout_recent.value = nan; - - violation_stage.value = nan; - timeout_stage.value = nan; - - case {2,3} %% center led on -> poke in the center -> go cue -> reward light and sound - if n_completed_trials > 1 - ntrials.value = n_completed_trials; - violation_rate.value = nan; - timeout_rate.value = numel(find(timeout_history))/n_completed_trials; - end - - violation_recent.value = nan; - violation_stage.value = nan; - - if n_completed_trials >= 20 - timeout_recent.value = numel(find(timeout_history(end-19:end)))/20; - else - timeout_recent.value = nan; - end - - - case {4,5,6,7,8} - - if n_completed_trials > 1 - ntrials.value = n_completed_trials; - violation_rate.value = numel(find(violation_history))/n_completed_trials; - timeout_rate.value = numel(find(timeout_history))/n_completed_trials; - end - - if n_completed_trials >= 20 - timeout_recent.value = numel(find(timeout_history(end-19:end)))/20; - violation_recent.value = numel(find(violation_history(end-19:end)))/20; - else - timeout_recent.value = nan; - violation_recent.value = nan; - end - - end - + if nargout > 0 x = [n_completed_trials, value(ntrials_stage), value(violation_rate), value(timeout_rate), value(violation_recent), ... value(timeout_recent), value(violation_stage), value(timeout_stage)]; end - % ------------------------------------------------------------------ % close diff --git a/Protocols/@ArpitCentrePokeTraining/Training_Performance_Summary.asv b/Protocols/@ArpitCentrePokeTraining/Training_Performance_Summary.asv deleted file mode 100644 index b9a69788..00000000 --- a/Protocols/@ArpitCentrePokeTraining/Training_Performance_Summary.asv +++ /dev/null @@ -1,242 +0,0 @@ -function [x, y] = Training_Performance_Summary(obj, action, varargin) - -GetSoloFunctionArgs(obj); - -switch action - - % ------------------------------------------------------------------ - % INIT - % ------------------------------------------------------------------ - - case 'init' - if length(varargin) < 2 - error('Need at least two arguments, x and y position, to initialize %s', mfilename); - end - x = varargin{1}; y = varargin{2}; - - % SoloParamHandle(obj, 'my_xyfig', 'value', [x y double(gcf)]); - - ToggleParam(obj, 'SummaryShow', 0, x, y, 'OnString', 'Summary Show', ... - 'OffString', 'Summary Hidden', 'TooltipString', 'Show/Hide Summary panel'); - set_callback(SummaryShow, {mfilename, 'show_hide'}); %#ok (Defined just above) - next_row(y); - oldx=x; oldy=y; parentfig=double(gcf); - - SoloParamHandle(obj, 'myfig', 'value', figure('Position', [ 226 122 1406 400], ... - 'closerequestfcn', [mfilename '(' class(obj) ', ''hide'');'], 'MenuBar', 'none', ... - 'Name', mfilename), 'saveable', 0); - set(double(gcf), 'Visible', 'off'); - - - % Create stage names - N_Stages = 8; - for i = 1:N_Stages - rowNames{i, 1} = sprintf('Stage %d', i); - end - % Create column names - N_params = 5; - columnNames = {'Stage/Param','Trials','TrialsToday','TrialsValid','ViolationRate','TimeoutRate'}; - - % Create Variable Names for each Edit Box - count = 0; - variable_names = cell(1,N_params * N_Stages); - for j = 1:N_params - for i = 1:N_Stages - count = count + 1; - variable_names(count) = {sprintf('stage_%i_%s',N_Stages - i + 1,columnNames{j+1})}; - end - end - - count = 0; - x = 100; y=100; - - for column_n = 1 : N_params + 1 - for rows_n = 1 : N_Stages + 1 - if column_n == 1 && rows_n < N_Stages + 1 - SubheaderParam(obj, 'title', sprintf('Stage%i',(N_Stages - rows_n + 1)), x, y); - next_row(y); - end - if column_n == 1 && rows_n == N_Stages + 1 - SubheaderParam(obj, 'title', columnNames{1}, x, y); - next_row(y); - end - if column_n > 1 && rows_n < N_Stages + 1 - count = count + 1; - NumeditParam(obj, variable_names{count}, 0, x, y); - next_row(y); - end - if rows_n == N_Stages + 1 && column_n > 1 - SubheaderParam(obj, 'title', columnNames{column_n}, x, y); - next_row(y); - end - end - next_column(x); y=100; - end - - % - x=oldx; y=oldy; - figure(parentfig); - - SoloFunctionAddVars('SessionPerformanceSection', 'ro_args', ... - {'stage_1_Trials';'stage_1_TrialsToday';'stage_1_ViolationRate';'stage_1_TimeoutRate'... - 'stage_1_Trials';'stage_1_TrialsToday';'stage_1_ViolationRate';'stage_1_TimeoutRate' ... - 'stage_1_Trials';'stage_1_TrialsToday';'stage_1_ViolationRate';'stage_1_TimeoutRate'... - 'stage_1_Trials';'stage_1_TrialsToday';'stage_1_ViolationRate';'stage_1_TimeoutRate'... - 'stage_1_Trials';'stage_1_TrialsToday';'stage_1_ViolationRate';'stage_1_TimeoutRate'... - 'stage_1_Trials';'stage_1_TrialsToday';'stage_1_ViolationRate';'stage_1_TimeoutRate'... - 'stage_1_Trials';'stage_1_TrialsToday';'stage_1_ViolationRate';'stage_1_TimeoutRate'... - 'stage_1_Trials';'stage_1_TrialsToday';'stage_1_ViolationRate';'stage_1_TimeoutRate'}); - -%% Evaluate - case 'evaluate' - - switch value(training_stage) - - case 1 - - if n_completed_trials > 0 - - stage_1_Trials.value = value(stage_1_Trials) + 1; - stage_1_TrialsToday.value = value(stage_1_TrialsToday) + 1; - stage_1_ViolationRate.value = nan; - stage_1_TimeoutRate.value = nan; - if value(hit_history(end)) == 1 - stage_1_TrialsValid.value = value(stage_1_TrialsValid) + 1; - end - % Updating Disp Values for SessionPeformance Section as - % well - - end - - case 2 - - if n_completed_trials > 0 - - stage_2_Trials.value = value(stage_2_Trials) + 1; - stage_2_TrialsToday.value = value(stage_2_TrialsToday) + 1; - if value(previous_sides(end)) ~= value(ThisTrial) && all(hit_history(end-1:end)) % last and present trials should also be a valid trial - trial_oppSide = value(trial_oppSide) + 1; % updating value for variable in TrainingParams_Section - end - stage_2_ViolationRate.value = nan; - stage_2_TimeoutRate.value = ((value(stage_2_TimeoutRate) * (value(stage_2_Trials) - 1)) + double(timeout_history(end))) / value(stage_2_Trials); - if value(hit_history(end)) == 1 - stage_2_TrialsValid.value = value(stage_2_TrialsValid) + 1; - end - - end - - case 3 - - if n_completed_trials > 0 - - stage_3_Trials.value = value(stage_3_Trials) + 1; - stage_3_TrialsToday.value = value(stage_3_TrialsToday) + 1; - stage_3_ViolationRate.value = ((value(stage_3_ViolationRate) * (value(stage_3_Trials) - 1)) + double(violation_history(end))) / value(stage_3_Trials); - stage_3_TimeoutRate.value = ((value(stage_3_TimeoutRate) * (value(stage_3_Trials) - 1)) + double(timeout_history(end))) / value(stage_3_Trials); - if value(hit_history(end)) == 1 - stage_3_TrialsValid.value = value(stage_3_TrialsValid) + 1; - end - end - - case 4 - - if n_completed_trials > 0 - - stage_4_Trials.value = value(stage_4_Trials) + 1; - stage_4_TrialsToday.value = value(stage_4_TrialsToday) + 1; - stage_4_ViolationRate.value = ((value(stage_4_ViolationRate) * (value(stage_4_Trials) - 1)) + double(violation_history(end))) / value(stage_4_Trials); - stage_4_TimeoutRate.value = ((value(stage_4_TimeoutRate) * (value(stage_4_Trials) - 1)) + double(timeout_history(end))) / value(stage_4_Trials); - if value(hit_history(end)) == 1 - stage_4_TrialsValid.value = value(stage_4_TrialsValid) + 1; - end - end - - case 5 - - if n_completed_trials > 0 - - stage_5_Trials.value = value(stage_5_Trials) + 1; - stage_5_TrialsToday.value = value(stage_5_TrialsToday) + 1; - stage_5_ViolationRate.value = ((value(stage_5_ViolationRate) * (value(stage_5_Trials) - 1)) + double(violation_history(end))) / value(stage_5_Trials); - stage_5_TimeoutRate.value = ((value(stage_5_TimeoutRate) * (value(stage_5_Trials) - 1)) + double(timeout_history(end))) / value(stage_5_Trials); - if value(hit_history(end)) == 1 - stage_5_TrialsValid.value = value(stage_5_TrialsValid) + 1; - end - end - - case 6 - - if n_completed_trials > 0 - - stage_6_Trials.value = value(stage_6_Trials) + 1; - stage_6_TrialsToday.value = value(stage_6_TrialsToday) + 1; - stage_6_ViolationRate.value = ((value(stage_6_ViolationRate) * (value(stage_6_Trials) - 1)) + double(violation_history(end))) / value(stage_6_Trials); - stage_6_TimeoutRate.value = ((value(stage_6_TimeoutRate) * (value(stage_6_Trials) - 1)) + double(timeout_history(end))) / value(stage_6_Trials); - if value(hit_history(end)) == 1 - stage_6_TrialsValid.value = value(stage_6_TrialsValid) + 1; - end - end - - case 7 - - if n_completed_trials > 0 - - stage_7_Trials.value = value(stage_7_Trials) + 1; - stage_7_TrialsToday.value = value(stage_7_TrialsToday) + 1; - stage_7_ViolationRate.value = ((value(stage_7_ViolationRate) * (value(stage_7_Trials) - 1)) + double(violation_history(end))) / value(stage_7_Trials); - stage_7_TimeoutRate.value = ((value(stage_7_TimeoutRate) * (value(stage_7_Trials) - 1)) + double(timeout_history(end))) / value(stage_7_Trials); - if value(hit_history(end)) == 1 - stage_7_TrialsValid.value = value(stage_7_TrialsValid) + 1; - end - - end - - case 8 - - if n_completed_trials > 0 - - stage_8_Trials.value = value(stage_8_Trials) + 1; - stage_8_TrialsToday.value = value(stage_8_TrialsToday) + 1; - stage_8_ViolationRate.value = ((value(stage_8_ViolationRate) * (value(stage_8_Trials) - 1)) + double(violation_history(end))) / value(stage_8_Trials); - stage_8_TimeoutRate.value = ((value(stage_8_TimeoutRate) * (value(stage_8_Trials) - 1)) + double(timeout_history(end))) / value(stage_8_Trials); - if value(hit_history(end)) == 1 - stage_8_TrialsValid.value = value(stage_8_TrialsValid) + 1; - end - end - end - - -%% Case close - case 'close' - set(value(myfig), 'Visible', 'off'); - % Delete all SoloParamHandles who belong to this object and whose - % fullname starts with the name of this mfile: - if exist('myfig', 'var') && isa(myfig, 'SoloParamHandle') && ishandle(value(myfig)) %#ok - delete(value(myfig)); - end - - delete_sphandle('owner', ['^@' class(obj) '$'], ... - 'fullname', ['^' mfilename]); - - %% Case hide - case 'hide' - SummaryShow.value = 0; - set(value(myfig), 'Visible', 'off'); - - %% Case show - case 'show' - SummaryShow.value = 1; - set(value(myfig), 'Visible', 'on'); - - %% Case Show_hide - case 'show_hide' - if value(SummaryShow) == 1 - set(value(myfig), 'Visible', 'on'); - else - set(value(myfig), 'Visible', 'off'); - end - -end - -end - diff --git a/Protocols/@ArpitCentrePokeTraining/Training_Performance_Summary.m b/Protocols/@ArpitCentrePokeTraining/Training_Performance_Summary.m index f28db682..01c3de6a 100644 --- a/Protocols/@ArpitCentrePokeTraining/Training_Performance_Summary.m +++ b/Protocols/@ArpitCentrePokeTraining/Training_Performance_Summary.m @@ -77,134 +77,19 @@ x=oldx; y=oldy; figure(parentfig); - SoloFunctionAddVars('SessionPerformanceSection', 'ro_args', ... - {'stage_1_Trials';'stage_1_TrialsToday';'stage_1_ViolationRate';'stage_1_TimeoutRate'... - 'stage_2_Trials';'stage_2_TrialsToday';'stage_2_ViolationRate';'stage_2_TimeoutRate' ... - 'stage_3_Trials';'stage_3_TrialsToday';'stage_3_ViolationRate';'stage_3_TimeoutRate'... - 'stage_4_Trials';'stage_4_TrialsToday';'stage_4_ViolationRate';'stage_4_TimeoutRate'... - 'stage_5_Trials';'stage_5_TrialsToday';'stage_5_ViolationRate';'stage_5_TimeoutRate'... - 'stage_6_Trials';'stage_6_TrialsToday';'stage_6_ViolationRate';'stage_6_TimeoutRate'... - 'stage_7_Trials';'stage_7_TrialsToday';'stage_7_ViolationRate';'stage_7_TimeoutRate'... - 'stage_8_Trials';'stage_8_TrialsToday';'stage_8_ViolationRate';'stage_8_TimeoutRate'}); - %% Evaluate case 'evaluate' - switch value(training_stage) - - case 1 - - if n_completed_trials > 0 - - stage_1_Trials.value = value(stage_1_Trials) + 1; - stage_1_TrialsToday.value = value(stage_1_TrialsToday) + 1; - stage_1_ViolationRate.value = nan; - stage_1_TimeoutRate.value = nan; - if value(hit_history(end)) == 1 - stage_1_TrialsValid.value = value(stage_1_TrialsValid) + 1; - end - % Updating Disp Values for SessionPeformance Section as - % well - - end - - case 2 - - if n_completed_trials > 0 - - stage_2_Trials.value = value(stage_2_Trials) + 1; - stage_2_TrialsToday.value = value(stage_2_TrialsToday) + 1; - if value(previous_sides(end)) ~= value(ThisTrial) && all(hit_history(end-1:end)) % last and present trials should also be a valid trial - trial_oppSide = value(trial_oppSide) + 1; % updating value for variable in TrainingParams_Section - end - stage_2_ViolationRate.value = nan; - stage_2_TimeoutRate.value = ((value(stage_2_TimeoutRate) * (value(stage_2_Trials) - 1)) + double(timeout_history(end))) / value(stage_2_Trials); - if value(hit_history(end)) == 1 - stage_2_TrialsValid.value = value(stage_2_TrialsValid) + 1; - end - - end - - case 3 - - if n_completed_trials > 0 - - stage_3_Trials.value = value(stage_3_Trials) + 1; - stage_3_TrialsToday.value = value(stage_3_TrialsToday) + 1; - stage_3_ViolationRate.value = ((value(stage_3_ViolationRate) * (value(stage_3_Trials) - 1)) + double(violation_history(end))) / value(stage_3_Trials); - stage_3_TimeoutRate.value = ((value(stage_3_TimeoutRate) * (value(stage_3_Trials) - 1)) + double(timeout_history(end))) / value(stage_3_Trials); - if value(hit_history(end)) == 1 - stage_3_TrialsValid.value = value(stage_3_TrialsValid) + 1; - end - end - - case 4 - - if n_completed_trials > 0 - - stage_4_Trials.value = value(stage_4_Trials) + 1; - stage_4_TrialsToday.value = value(stage_4_TrialsToday) + 1; - stage_4_ViolationRate.value = ((value(stage_4_ViolationRate) * (value(stage_4_Trials) - 1)) + double(violation_history(end))) / value(stage_4_Trials); - stage_4_TimeoutRate.value = ((value(stage_4_TimeoutRate) * (value(stage_4_Trials) - 1)) + double(timeout_history(end))) / value(stage_4_Trials); - if value(hit_history(end)) == 1 - stage_4_TrialsValid.value = value(stage_4_TrialsValid) + 1; - end - end - - case 5 - - if n_completed_trials > 0 - - stage_5_Trials.value = value(stage_5_Trials) + 1; - stage_5_TrialsToday.value = value(stage_5_TrialsToday) + 1; - stage_5_ViolationRate.value = ((value(stage_5_ViolationRate) * (value(stage_5_Trials) - 1)) + double(violation_history(end))) / value(stage_5_Trials); - stage_5_TimeoutRate.value = ((value(stage_5_TimeoutRate) * (value(stage_5_Trials) - 1)) + double(timeout_history(end))) / value(stage_5_Trials); - if value(hit_history(end)) == 1 - stage_5_TrialsValid.value = value(stage_5_TrialsValid) + 1; - end - end - - case 6 - - if n_completed_trials > 0 - - stage_6_Trials.value = value(stage_6_Trials) + 1; - stage_6_TrialsToday.value = value(stage_6_TrialsToday) + 1; - stage_6_ViolationRate.value = ((value(stage_6_ViolationRate) * (value(stage_6_Trials) - 1)) + double(violation_history(end))) / value(stage_6_Trials); - stage_6_TimeoutRate.value = ((value(stage_6_TimeoutRate) * (value(stage_6_Trials) - 1)) + double(timeout_history(end))) / value(stage_6_Trials); - if value(hit_history(end)) == 1 - stage_6_TrialsValid.value = value(stage_6_TrialsValid) + 1; - end - end - - case 7 - - if n_completed_trials > 0 - - stage_7_Trials.value = value(stage_7_Trials) + 1; - stage_7_TrialsToday.value = value(stage_7_TrialsToday) + 1; - stage_7_ViolationRate.value = ((value(stage_7_ViolationRate) * (value(stage_7_Trials) - 1)) + double(violation_history(end))) / value(stage_7_Trials); - stage_7_TimeoutRate.value = ((value(stage_7_TimeoutRate) * (value(stage_7_Trials) - 1)) + double(timeout_history(end))) / value(stage_7_Trials); - if value(hit_history(end)) == 1 - stage_7_TrialsValid.value = value(stage_7_TrialsValid) + 1; - end - - end - - case 8 - - if n_completed_trials > 0 - - stage_8_Trials.value = value(stage_8_Trials) + 1; - stage_8_TrialsToday.value = value(stage_8_TrialsToday) + 1; - stage_8_ViolationRate.value = ((value(stage_8_ViolationRate) * (value(stage_8_Trials) - 1)) + double(violation_history(end))) / value(stage_8_Trials); - stage_8_TimeoutRate.value = ((value(stage_8_TimeoutRate) * (value(stage_8_Trials) - 1)) + double(timeout_history(end))) / value(stage_8_Trials); - if value(hit_history(end)) == 1 - stage_8_TrialsValid.value = value(stage_8_TrialsValid) + 1; - end - end - end - + % if value(training_stage) == 8 && n_completed_trials > 0 + % stage_8_Trials.value = value(stage_8_Trials) + 1; + % stage_8_TrialsToday.value = value(stage_8_TrialsToday) + 1; + % stage_8_ViolationRate.value = ((value(stage_8_ViolationRate) * (value(stage_8_Trials) - 1)) + double(violation_history(end))) / value(stage_8_Trials); + % stage_8_TimeoutRate.value = ((value(stage_8_TimeoutRate) * (value(stage_8_Trials) - 1)) + double(timeout_history(end))) / value(stage_8_Trials); + % if value(hit_history(end)) == 1 + % stage_8_TrialsValid.value = value(stage_8_TrialsValid) + 1; + % end + % end + % %% Case close case 'close' From a73f2932a1472adaeab9ce13353cec143dd629b7 Mon Sep 17 00:00:00 2001 From: Arpit Date: Wed, 7 May 2025 17:20:05 +0100 Subject: [PATCH 086/164] error debug camera plugin --- .../@bonsaicamera/BonsaiCameraInterface.m | 143 +----------------- .../Plugins/@bonsaicamera/createOSCMessage.m | 40 +++++ .../Plugins/@bonsaicamera/runBonsaiWorkflow.m | 92 +++++++++++ 3 files changed, 133 insertions(+), 142 deletions(-) create mode 100644 ExperPort/Plugins/@bonsaicamera/createOSCMessage.m create mode 100644 ExperPort/Plugins/@bonsaicamera/runBonsaiWorkflow.m diff --git a/ExperPort/Plugins/@bonsaicamera/BonsaiCameraInterface.m b/ExperPort/Plugins/@bonsaicamera/BonsaiCameraInterface.m index 752eca17..3ce4804a 100644 --- a/ExperPort/Plugins/@bonsaicamera/BonsaiCameraInterface.m +++ b/ExperPort/Plugins/@bonsaicamera/BonsaiCameraInterface.m @@ -197,145 +197,4 @@ end - -%%%%%%%%%%%%%%% ADD 0N Functions %%%%%%%%%%%%%%%%%%%% - - - -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - -function oscPacket = createOSCMessage(address, arg) - % Ensure address and arg are char (not string) - if isstring(address) - address = char(address); - end - if isstring(arg) - arg = char(arg); - end - - % Helper to pad with nulls to 4-byte alignment - function bytes = padNulls(str) - strBytes = uint8([str, 0]); % null-terminated - padding = mod(4 - mod(length(strBytes), 4), 4); - bytes = [strBytes, zeros(1, padding, 'uint8')]; - end - - % Address (e.g., "/camera", "/record") - addrBytes = padNulls(address); - - % Type Tag String (e.g., ",s" for a single string argument) - typeTag = ',s'; - tagBytes = padNulls(typeTag); - - % Argument (e.g., "start") - argBytes = padNulls(arg); - - % Combine all parts - oscPacket = [addrBytes, tagBytes, argBytes]; -end - - -function runBonsaiWorkflow(workflowPath, addOptArray, bonsaiExePath, external) - -%% addOptArray = {'field', 'val', 'field2', 'val2'}; -%% check the input -switch nargin - case 1 - bonsaiExePath = bonsaiPath(32); - addOptArray = ''; - addFlag = 0; - external = 1; - case 2 - bonsaiExePath = bonsaiPath(32); - addFlag = 1; - external = 1; - case 3 - addFlag = 1; - external = 1; - case 4 - if isempty(bonsaiExePath) - bonsaiExePath = bonsaiPath(32); - end - addFlag = 1; - - otherwise - error('Too many variables'); -end - - - -%% write the command -optSuffix = '-p:'; -startFlag = '--start'; -noEditorFlag = '--noeditor'; %use this instead of startFlag to start Bonsai without showing the editor -cmdsep = ' '; -if external -command = [['"' bonsaiExePath '"'] cmdsep ['"' workflowPath '"'] cmdsep startFlag]; -else -command = [['"' bonsaiExePath '"'] cmdsep ['"' workflowPath '"'] cmdsep noEditorFlag]; -end -ii = 1; -commandAppend = ''; -if addFlag - while ii < size(addOptArray,2) - commandAppend = [commandAppend cmdsep optSuffix addOptArray{ii} '="' num2str(addOptArray{ii+1}) '"' ]; - ii = ii+2; - end -end - -if external - command = [command commandAppend ' &']; -else - command = [command commandAppend]; -end -%% run the command -[sysecho] = system(command); -end - - -%% parse the addOptArray -function out = parseAddOptArray(addOptArray) -addOpt = cell(length(addOptArray)/2,2); -for ii = 1:length(addOpt) - addOpt{ii,1} = addOptArray{2*ii-1}; %gets the propriety - addOpt{ii,2} = addOptArray{2*ii}; %gets the value -end -out = addOpt; -end - - -function pathout = bonsaiPath(x64x86) - -%%version of bonsai -if nargin <1; x64x86 = 64; end -switch x64x86 - case {'64', 64, 'x64', 1, '64bit'} - bonsaiEXE = 'Bonsai64.exe'; - case {32, '32', 'x86', 0, '32bit'} - bonsaiEXE = 'Bonsai.exe'; -end - -dirs = {'appdata', 'localappdata', 'programfiles', 'programfiles(x86)','USERPROFILE'}; -foundBonsai = 0; -dirIDX = 1; -while ~foundBonsai && (dirIDX <= length(dirs)) - pathout = fullfile(getenv(dirs{dirIDX}),'Bonsai', bonsaiEXE); - foundBonsai = exist(pathout, 'file'); - dirIDX = dirIDX +1; -end - -if ~foundBonsai - pathout = fullfile(getenv(dirs{5}),'Desktop',bonsaiEXE); - foundBonsai = exist(pathout, 'file'); -end - -if ~foundBonsai - warning('could not find bonsai executable, please insert it manually'); - [fname fpath] = uigetfile( '*.exe', 'Provide the path to Bonsai executable'); - pathout = fullfile(fpath, fname); -end - -end - - -end \ No newline at end of file +return diff --git a/ExperPort/Plugins/@bonsaicamera/createOSCMessage.m b/ExperPort/Plugins/@bonsaicamera/createOSCMessage.m new file mode 100644 index 00000000..fd14cd6e --- /dev/null +++ b/ExperPort/Plugins/@bonsaicamera/createOSCMessage.m @@ -0,0 +1,40 @@ +function oscPacket = createOSCMessage(address, arg) +% Ensure address and arg are char (not string) +if isstring(address) + address = char(address); +end +if isstring(arg) + arg = char(arg); +end + +% Helper to pad with nulls to 4-byte alignment + function bytes = padNulls(str) + strBytes = uint8([str, 0]); % null-terminated + padding = mod(4 - mod(length(strBytes), 4), 4); + bytes = [strBytes, zeros(1, padding, 'uint8')]; + end + +% Address (e.g., "/camera", "/record") +addrBytes = padNulls(address); + +% Type Tag String (e.g., ",s" for a single string argument) +typeTag = ',s'; +tagBytes = padNulls(typeTag); + +% Argument (e.g., "start") +argBytes = padNulls(arg); + +% Combine all parts +oscPacket = [addrBytes, tagBytes, argBytes]; + +%% parse the addOptArray + function out = parseAddOptArray(addOptArray) + addOpt = cell(length(addOptArray)/2,2); + for ii = 1:length(addOpt) + addOpt{ii,1} = addOptArray{2*ii-1}; %gets the propriety + addOpt{ii,2} = addOptArray{2*ii}; %gets the value + end + out = addOpt; + end + +end diff --git a/ExperPort/Plugins/@bonsaicamera/runBonsaiWorkflow.m b/ExperPort/Plugins/@bonsaicamera/runBonsaiWorkflow.m new file mode 100644 index 00000000..22cb3aeb --- /dev/null +++ b/ExperPort/Plugins/@bonsaicamera/runBonsaiWorkflow.m @@ -0,0 +1,92 @@ +function runBonsaiWorkflow(workflowPath, addOptArray, bonsaiExePath, external) + +%% addOptArray = {'field', 'val', 'field2', 'val2'}; +%% check the input +switch nargin + case 1 + bonsaiExePath = bonsaiPath(32); + addOptArray = ''; + addFlag = 0; + external = 1; + case 2 + bonsaiExePath = bonsaiPath(32); + addFlag = 1; + external = 1; + case 3 + addFlag = 1; + external = 1; + case 4 + if isempty(bonsaiExePath) + bonsaiExePath = bonsaiPath(32); + end + addFlag = 1; + + otherwise + error('Too many variables'); +end + + + +%% write the command +optSuffix = '-p:'; +startFlag = '--start'; +noEditorFlag = '--noeditor'; %use this instead of startFlag to start Bonsai without showing the editor +cmdsep = ' '; +if external + command = [['"' bonsaiExePath '"'] cmdsep ['"' workflowPath '"'] cmdsep startFlag]; +else + command = [['"' bonsaiExePath '"'] cmdsep ['"' workflowPath '"'] cmdsep noEditorFlag]; +end +ii = 1; +commandAppend = ''; +if addFlag + while ii < size(addOptArray,2) + commandAppend = [commandAppend cmdsep optSuffix addOptArray{ii} '="' num2str(addOptArray{ii+1}) '"' ]; + ii = ii+2; + end +end + +if external + command = [command commandAppend ' &']; +else + command = [command commandAppend]; +end +%% run the command +[sysecho] = system(command); + + + + function pathout = bonsaiPath(x64x86) + + %%version of bonsai + if nargin <1; x64x86 = 64; end + switch x64x86 + case {'64', 64, 'x64', 1, '64bit'} + bonsaiEXE = 'Bonsai64.exe'; + case {32, '32', 'x86', 0, '32bit'} + bonsaiEXE = 'Bonsai.exe'; + end + + dirs = {'appdata', 'localappdata', 'programfiles', 'programfiles(x86)','USERPROFILE'}; + foundBonsai = 0; + dirIDX = 1; + while ~foundBonsai && (dirIDX <= length(dirs)) + pathout = fullfile(getenv(dirs{dirIDX}),'Bonsai', bonsaiEXE); + foundBonsai = exist(pathout, 'file'); + dirIDX = dirIDX +1; + end + + if ~foundBonsai + pathout = fullfile(getenv(dirs{5}),'Desktop',bonsaiEXE); + foundBonsai = exist(pathout, 'file'); + end + + if ~foundBonsai + warning('could not find bonsai executable, please insert it manually'); + [fname fpath] = uigetfile( '*.exe', 'Provide the path to Bonsai executable'); + pathout = fullfile(fpath, fname); + end + + end + +end \ No newline at end of file From 2a9ab96e296a3384cf9418a302360fe04c4637ad Mon Sep 17 00:00:00 2001 From: viktorpm Date: Wed, 7 May 2025 18:20:27 +0100 Subject: [PATCH 087/164] debug --- .../@bonsaicamera/BonsaiCameraInterface.m | 38 ++++++++++--------- .../{ => private}/createOSCMessage.m | 10 ----- .../{ => private}/runBonsaiWorkflow.m | 11 ++++++ .../ArpitCentrePokeTraining.m | 12 ++---- 4 files changed, 36 insertions(+), 35 deletions(-) rename ExperPort/Plugins/@bonsaicamera/{ => private}/createOSCMessage.m (69%) rename ExperPort/Plugins/@bonsaicamera/{ => private}/runBonsaiWorkflow.m (88%) diff --git a/ExperPort/Plugins/@bonsaicamera/BonsaiCameraInterface.m b/ExperPort/Plugins/@bonsaicamera/BonsaiCameraInterface.m index 3ce4804a..62bd961b 100644 --- a/ExperPort/Plugins/@bonsaicamera/BonsaiCameraInterface.m +++ b/ExperPort/Plugins/@bonsaicamera/BonsaiCameraInterface.m @@ -7,17 +7,17 @@ GetSoloFunctionArgs(obj); -if isa(varargin{1}, mfilename) % If first arg is an object of this class itself, we are - % Most likely responding to a callback from a SoloParamHandle defined in this mfile. - if length(varargin) < 2 || ~ischar(varargin{2}) - error(['If called with a "%s" object as first arg, a second arg, a ' ... - 'string specifying the action, is required\n']); - else - action = varargin{2}; varargin = varargin(3:end); %#ok - end -else % Ok, regular call with first param being the action string. - action = varargin{1}; varargin = varargin(2:end); %#ok -end +% if isa(varargin{1}, mfilename) % If first arg is an object of this class itself, we are +% % Most likely responding to a callback from a SoloParamHandle defined in this mfile. +% if length(varargin) < 2 || ~ischar(varargin{2}) +% error(['If called with a "%s" object as first arg, a second arg, a ' ... +% 'string specifying the action, is required\n']); +% else +% action = varargin{2}; varargin = varargin(3:end); %#ok +% end +% else % Ok, regular call with first param being the action string. +% action = varargin{1}; varargin = varargin(2:end); %#ok +% end GetSoloFunctionArgs(obj); @@ -41,11 +41,11 @@ % within the ratter > ExpertPort > Plugins > @bonsaicamera folder scriptFullPath = mfilename('fullpath'); % Path of running the current script scriptDirectory = fileparts(scriptFullPath); -bonsai_workflow_Path = fullfile(scriptDirectory,'Bonsai_Camera_Control','Camera_Control.bonsai'); -foundworkflow = exist("bonsai_workflow_Path",'file'); +bonsai_workflow_Path = fullfile(scriptDirectory,'Camera_Control.bonsai'); +foundworkflow = isfile(bonsai_workflow_Path); if ~foundworkflow warning('could not find bonsai executable, please insert it manually'); - [bonsai_fname bonsai_fpath] = uigetfile( '*.exe', 'Provide the path to Bonsai executable'); + [bonsai_fname, bonsai_fpath] = uigetfile( '*.bonsai', 'Provide the path to Bonsai executable'); bonsai_workflow_Path = fullfile(bonsai_fpath, bonsai_fname); end @@ -95,13 +95,17 @@ % C:\RatterVideos\ExpName\RateName\Videos_Protocolname_ExpName_RatName_date % (the same format as Data file) + protocol_name = varargin{3}; + experimenter_name = varargin{4}; + rat_name = varargin{5}; + current_dir = cd; ratter_dir = extractBefore(current_dir,'ratter'); main_dir_video = [ratter_dir 'ratter_Videos']; date_str = regexprep(char(datetime('today','Format','yyyy-MM-dd')), '[^0-9]', ''); - video_foldername = sprintf('video_@%s_%s_%s_%s',name,expmtr,rname,date_str); - rat_dir = sprintf('%s\\%s\\%s',main_dir_video,expmtr,rname); - video_save_dir = sprintf('%s\\%s\\%s\\%s',main_dir_video,expmtr,rname,video_foldername); + video_foldername = sprintf('video_@%s_%s_%s_%s',protocol_name,experimenter_name,rat_name,date_str); + rat_dir = sprintf('%s\\%s\\%s',main_dir_video,experimenter_name,rat_name); + video_save_dir = sprintf('%s\\%s\\%s\\%s',main_dir_video,experimenter_name,rat_name,video_foldername); % We have the general structure of folder save location, now need to % check if there is any other folder for same date. We will add a % alphabet in the end based upon the no. of files present. diff --git a/ExperPort/Plugins/@bonsaicamera/createOSCMessage.m b/ExperPort/Plugins/@bonsaicamera/private/createOSCMessage.m similarity index 69% rename from ExperPort/Plugins/@bonsaicamera/createOSCMessage.m rename to ExperPort/Plugins/@bonsaicamera/private/createOSCMessage.m index fd14cd6e..0ddee6c1 100644 --- a/ExperPort/Plugins/@bonsaicamera/createOSCMessage.m +++ b/ExperPort/Plugins/@bonsaicamera/private/createOSCMessage.m @@ -27,14 +27,4 @@ % Combine all parts oscPacket = [addrBytes, tagBytes, argBytes]; -%% parse the addOptArray - function out = parseAddOptArray(addOptArray) - addOpt = cell(length(addOptArray)/2,2); - for ii = 1:length(addOpt) - addOpt{ii,1} = addOptArray{2*ii-1}; %gets the propriety - addOpt{ii,2} = addOptArray{2*ii}; %gets the value - end - out = addOpt; - end - end diff --git a/ExperPort/Plugins/@bonsaicamera/runBonsaiWorkflow.m b/ExperPort/Plugins/@bonsaicamera/private/runBonsaiWorkflow.m similarity index 88% rename from ExperPort/Plugins/@bonsaicamera/runBonsaiWorkflow.m rename to ExperPort/Plugins/@bonsaicamera/private/runBonsaiWorkflow.m index 22cb3aeb..2eb56086 100644 --- a/ExperPort/Plugins/@bonsaicamera/runBonsaiWorkflow.m +++ b/ExperPort/Plugins/@bonsaicamera/private/runBonsaiWorkflow.m @@ -56,6 +56,17 @@ function runBonsaiWorkflow(workflowPath, addOptArray, bonsaiExePath, external) +%% parse the addOptArray + function out = parseAddOptArray(addOptArray) + addOpt = cell(length(addOptArray)/2,2); + for ii = 1:length(addOpt) + addOpt{ii,1} = addOptArray{2*ii-1}; %gets the propriety + addOpt{ii,2} = addOptArray{2*ii}; %gets the value + end + out = addOpt; + end + + function pathout = bonsaiPath(x64x86) %%version of bonsai diff --git a/Protocols/@ArpitCentrePokeTraining/ArpitCentrePokeTraining.m b/Protocols/@ArpitCentrePokeTraining/ArpitCentrePokeTraining.m index 26c59242..c3d02eaf 100644 --- a/Protocols/@ArpitCentrePokeTraining/ArpitCentrePokeTraining.m +++ b/Protocols/@ArpitCentrePokeTraining/ArpitCentrePokeTraining.m @@ -184,12 +184,8 @@ [x, y] = Training_Performance_Summary(obj, 'init', x, y);next_row(y); [x, y] = SessionPerformanceSection(obj, 'init', x, y); next_row(y);next_row(y); - ToggleParam(obj, 'Connect_Camera', 1, x,y,... - 'OnString', 'Camera On',... - 'OffString', 'Camera Off',... - 'TooltipString', sprintf('If on (black) then it enables to start the camera \n',... - 'Only press if camera does not start on its own.')); - set_callback(Connect_Camera, {mfilename, 'camera_control'}); + + [x, y] = BonsaiCameraInterface(obj,'init',x,y,name,expmtr,rname); next_column(x); y=5; [stage_fig_x,stage_fig_y] = Training_ParamsSection(obj, 'init', x, y); @@ -203,7 +199,7 @@ x=oldx; y=oldy; SessionDefinition(obj, 'init', x, y, value(myfig)); %#ok - [x, y] = BonsaiCameraInterface(obj,'init',x,y); + % %% Before preparing the trial, start with the Bonsai app to control the USB based Camera % % Declare the folder location for saving the video files @@ -309,7 +305,7 @@ PokesPlotSection(obj, 'close'); ParamsSection(obj, 'close'); StimulusSection(obj,'close'); - Connect_Bonsai_Camera(obj,'close'); + BonsaiCameraInterface(obj,'close'); if exist('myfig', 'var') && isa(myfig, 'SoloParamHandle') && ishandle(value(myfig)) %#ok delete(value(myfig)); end From d75966558278b06ba7bd028af451f8477dcc4936 Mon Sep 17 00:00:00 2001 From: Arpit Date: Wed, 7 May 2025 21:58:56 +0100 Subject: [PATCH 088/164] added description for bonsaicamera plugin --- .../@bonsaicamera/BonsaiCameraInterface.m | 82 ++++++++++++++----- .../ArpitCentrePokeTraining.m | 41 +--------- 2 files changed, 66 insertions(+), 57 deletions(-) diff --git a/ExperPort/Plugins/@bonsaicamera/BonsaiCameraInterface.m b/ExperPort/Plugins/@bonsaicamera/BonsaiCameraInterface.m index 62bd961b..bded6df8 100644 --- a/ExperPort/Plugins/@bonsaicamera/BonsaiCameraInterface.m +++ b/ExperPort/Plugins/@bonsaicamera/BonsaiCameraInterface.m @@ -1,3 +1,47 @@ +%% BonsaiCameraInterface - MATLAB class interface to control USB camera via Bonsai using OSC messages. +% +% This class enables communication between MATLAB and a Bonsai workflow for controlling a USB-based camera. +% It uses Open Sound Control (OSC) messages transmitted over UDP to start and stop the camera feed, as well +% as to specify the location where video recordings should be saved. +% +% Dependencies: +% - Camera_Control.bonsai: Bonsai workflow file (must be saved in the same folder as this class file) +% - private/createOSCMessage.m: Helper function to format OSC messages +% - private/runBonsaiWorkflow.m: Helper function to launch Bonsai with a specific workflow +% +% Initialization ('init' action): +% Initialization ('init' action): +% - Requires exactly five input arguments in the following order: +% 1. x-position of the camera control toggle button (UI element in Solo GUI) +% 2. y-position of the camera control toggle button +% 3. Protocol name +% 4. Experimenter name +% 5. Rat name +% - If any of these are missing or fewer than five arguments are provided, the initialization will result in an error. + +% - Launches the Bonsai workflow using runBonsaiWorkflow.m This function is used to start a Bonsai workflow (.bonsai file) from within MATLAB. +% It builds and executes a command to launch the Bonsai executable with the selected workflow, +% allowing automated and optionally parameterized workflow startup. + +% - Sets up a UDP sender object to communicate via OSC. + +% - Automatically creates a structured directory for saving video files in the format: +% C:\ratter_Videos\\\video_@___[a-z] +% A suffix is appended if there are multiple sessions on the same date. + +% - Sends initial OSC messages to set the video save path and begin streaming. +% +% Switch-case structure: +% +% 'init' : Initializes UI toggle, starts Bonsai workflow, sets up save directory and UDP sender. +% 'camera_connection' : Sends OSC messages to start or stop the camera feed. Also sends the video path to Bonsai. +% 'next_trial' : Sends a new video save path to Bonsai to separate trial recordings. +% 'close' : Stops the camera, deletes the UDP sender object, and terminates Bonsai and CMD processes. +% +% Notes: +% - Uses IP 127.0.0.1 and port 9090 for sending OSC messages to Bonsai. +% - Messages use the OSC addresses "/camera" and "/record" with "start"/"stop" commands. + function [varargout] = BonsaiCameraInterface(obj,action,varargin) % If creating an empty object, return without further ado: @@ -7,19 +51,6 @@ GetSoloFunctionArgs(obj); -% if isa(varargin{1}, mfilename) % If first arg is an object of this class itself, we are -% % Most likely responding to a callback from a SoloParamHandle defined in this mfile. -% if length(varargin) < 2 || ~ischar(varargin{2}) -% error(['If called with a "%s" object as first arg, a second arg, a ' ... -% 'string specifying the action, is required\n']); -% else -% action = varargin{2}; varargin = varargin(3:end); %#ok -% end -% else % Ok, regular call with first param being the action string. -% action = varargin{1}; varargin = varargin(2:end); %#ok -% end - -GetSoloFunctionArgs(obj); %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%%%%%%%% DO NOT CHANGE THEM UNLESS YOU MAKE THE SAME CHANGES IN BONSAI %%%%%%%%% @@ -87,18 +118,31 @@ runBonsaiWorkflow(bonsai_workflow_Path); pause(5); % wait few seconds for software to open before continuing - - %% STEP 2: DECLARING AND FOLDER LOCATION FOR SAVING THE VIDEOS % The video files are saved in the folder format declared below % C:\RatterVideos\ExpName\RateName\Videos_Protocolname_ExpName_RatName_date % (the same format as Data file) - protocol_name = varargin{3}; - experimenter_name = varargin{4}; - rat_name = varargin{5}; - + switch length(varargin) + case 2 + protocol_name = 'protocol_name'; + experimenter_name = 'experimenter'; + rat_name = 'ratname'; + case 3 + protocol_name = varargin{3}; + experimenter_name = 'experimenter'; + rat_name = 'ratname'; + case 4 + protocol_name = varargin{3}; + experimenter_name = varargin{4}; + rat_name = 'ratname'; + case 5 + protocol_name = varargin{3}; + experimenter_name = varargin{4}; + rat_name = varargin{5}; + end + current_dir = cd; ratter_dir = extractBefore(current_dir,'ratter'); main_dir_video = [ratter_dir 'ratter_Videos']; diff --git a/Protocols/@ArpitCentrePokeTraining/ArpitCentrePokeTraining.m b/Protocols/@ArpitCentrePokeTraining/ArpitCentrePokeTraining.m index c3d02eaf..8d020cd4 100644 --- a/Protocols/@ArpitCentrePokeTraining/ArpitCentrePokeTraining.m +++ b/Protocols/@ArpitCentrePokeTraining/ArpitCentrePokeTraining.m @@ -183,9 +183,10 @@ [x, y] = Training_Performance_Summary(obj, 'init', x, y);next_row(y); [x, y] = SessionPerformanceSection(obj, 'init', x, y); - next_row(y);next_row(y); - [x, y] = BonsaiCameraInterface(obj,'init',x,y,name,expmtr,rname); + next_row(y);next_row(y); + + [x, y] = BonsaiCameraInterface(obj,'init',x,y,name,expmtr,rname); next_column(x); y=5; [stage_fig_x,stage_fig_y] = Training_ParamsSection(obj, 'init', x, y); @@ -200,46 +201,10 @@ x=oldx; y=oldy; SessionDefinition(obj, 'init', x, y, value(myfig)); %#ok - - % %% Before preparing the trial, start with the Bonsai app to control the USB based Camera - % % Declare the folder location for saving the video files - % - % current_dir = cd; - % ratter_dir = extractBefore(current_dir,'ratter'); - % main_dir_video = [ratter_dir 'ratter_Videos']; - % date_str = regexprep(char(datetime('today','Format','yyyy-MM-dd')), '[^0-9]', ''); - % video_foldername = sprintf('video_@%s_%s_%s_%s',name,expmtr,rname,date_str); - % rat_dir = sprintf('%s\\%s\\%s',main_dir_video,expmtr,rname); - % video_save_dir = sprintf('%s\\%s\\%s\\%s',main_dir_video,expmtr,rname,video_foldername); - % % We have the general structure of folder save location, now need to - % % check if there is any other folder for same date. We will add a - % % alphabet in the end based upon the no. of files present. - % if exist(rat_dir,'dir') == 7 - % listing = dir(rat_dir); - % folderNames_rat_dir = {listing(find([listing.isdir])).name}; - % folderNames_rat_dir = folderNames_rat_dir(~ismember(folderNames_rat_dir,{'.','..'})); % Remove the '.' and '..' entries (current and parent directories) - % sessions_today = length(find(contains(folderNames_rat_dir,video_foldername))); % number of folders containing the video foldername - % video_save_dir = [video_save_dir char(sessions_today + 97)]; - % else - % video_save_dir = [video_save_dir char(97)]; - % end - % mkdir(video_save_dir); - % SoloParamHandle(obj, 'Video_Saving_Folder', 'value', video_save_dir); - % SoloFunctionAddVars('Connect_Bonsai_Camera', 'ro_args', ... - % {'Video_Saving_Folder'}); - % Connect_Bonsai_Camera(obj,'init'); - %% % feval(mfilename, obj, 'prepare_next_trial'); % Commented out because it is also run by Runrats(while loading the protocol) %%% - % case 'camera_control' - % - % if value(Connect_Camera) == 1 - % Connect_Bonsai_Camera(obj,'start'); - % else - % Connect_Bonsai_Camera(obj,'stop'); - % end %% change_water_modulation_params case 'change_water_modulation_params' From cccb5770698869192036bd63738c08851d5829c9 Mon Sep 17 00:00:00 2001 From: Arpit Date: Thu, 8 May 2025 07:23:28 +0100 Subject: [PATCH 089/164] changed m files names to follow bcontrol convention for loading setting --- .../@bonsaicamera/BonsaiCameraInterface.m | 8 +- .../ArpitCentrePokeTraining.m | 15 +- ...ing_SessionDefinition_AutoTrainingStages.m | 610 +++++++++--------- .../@ArpitCentrePokeTraining/ParamsSection.m | 4 +- ..._Summary.m => PerformanceSummarySection.m} | 2 +- ...Section.m => TrainingStageParamsSection.m} | 2 +- 6 files changed, 324 insertions(+), 317 deletions(-) rename Protocols/@ArpitCentrePokeTraining/{Training_Performance_Summary.m => PerformanceSummarySection.m} (98%) rename Protocols/@ArpitCentrePokeTraining/{Training_ParamsSection.m => TrainingStageParamsSection.m} (99%) diff --git a/ExperPort/Plugins/@bonsaicamera/BonsaiCameraInterface.m b/ExperPort/Plugins/@bonsaicamera/BonsaiCameraInterface.m index bded6df8..5dcbe829 100644 --- a/ExperPort/Plugins/@bonsaicamera/BonsaiCameraInterface.m +++ b/ExperPort/Plugins/@bonsaicamera/BonsaiCameraInterface.m @@ -37,7 +37,7 @@ % 'camera_connection' : Sends OSC messages to start or stop the camera feed. Also sends the video path to Bonsai. % 'next_trial' : Sends a new video save path to Bonsai to separate trial recordings. % 'close' : Stops the camera, deletes the UDP sender object, and terminates Bonsai and CMD processes. -% +% 'stop' : Sending OSC message to stop streaming and save last trial video % Notes: % - Uses IP 127.0.0.1 and port 9090 for sending OSC messages to Bonsai. % - Messages use the OSC addresses "/camera" and "/record" with "start"/"stop" commands. @@ -218,6 +218,12 @@ end + case 'stop' % Stopping the streaming and saving last trial video + + % this stops saving and streaming of the camera + oscMsg_Camera_stop = createOSCMessage("/camera", stopCommand); + write(value(UDPSender), oscMsg_Camera_stop, "uint8", bonsaiComputerIP,bonsaiUdpPort); + %% next trial % SEND STRING TO BONSAI AT THE END OF THE TRIAL TO SAVE NEXT TRIAL IN % NEW VIDEO FILE diff --git a/Protocols/@ArpitCentrePokeTraining/ArpitCentrePokeTraining.m b/Protocols/@ArpitCentrePokeTraining/ArpitCentrePokeTraining.m index 8d020cd4..4cf54b9a 100644 --- a/Protocols/@ArpitCentrePokeTraining/ArpitCentrePokeTraining.m +++ b/Protocols/@ArpitCentrePokeTraining/ArpitCentrePokeTraining.m @@ -181,7 +181,7 @@ next_row(y);next_row(y); - [x, y] = Training_Performance_Summary(obj, 'init', x, y);next_row(y); + [x, y] = PerformanceSummarySection(obj, 'init', x, y);next_row(y); [x, y] = SessionPerformanceSection(obj, 'init', x, y); next_row(y);next_row(y); @@ -189,7 +189,7 @@ [x, y] = BonsaiCameraInterface(obj,'init',x,y,name,expmtr,rname); next_column(x); y=5; - [stage_fig_x,stage_fig_y] = Training_ParamsSection(obj, 'init', x, y); + [stage_fig_x,stage_fig_y] = TrainingStageParamsSection(obj, 'init', x, y); SoloParamHandle(obj, 'stage_fig_x', 'value', stage_fig_x); SoloFunctionAddVars('ParamsSection', 'rw_args', 'stage_fig_x'); SoloParamHandle(obj, 'stage_fig_y', 'value', stage_fig_y); @@ -249,16 +249,17 @@ %% trial_completed case 'trial_completed' - % Change the video trial - Connect_Bonsai_Camera(obj,'next_trial'); + + % Change the video trial + BonsaiCameraInterface(obj,'next_trial'); % Update the Metrics Calculated, Instead being Calculated in Session % Definition and commented out - % Training_Performance_Summary(obj,'evaluate'); + % PerformanceSummarySection(obj,'evaluate'); % SessionPerformanceSection(obj, 'evaluate'); % Do any updates in the protocol that need doing: - feval(mfilename, 'update'); + feval(mfilename, 'update'); %% update case 'update' @@ -280,7 +281,7 @@ %% end_session case 'end_session' prot_title.value = [value(prot_title) ', Ended at ' datestr(now, 'HH:MM')]; - Connect_Bonsai_Camera(obj,'stop'); % Stopping the cameras + BonsaiCameraInterface(obj,'stop') % Stopping the cameras %% pre_saving_settings case 'pre_saving_settings' diff --git a/Protocols/@ArpitCentrePokeTraining/ArpitCentrePokeTraining_SessionDefinition_AutoTrainingStages.m b/Protocols/@ArpitCentrePokeTraining/ArpitCentrePokeTraining_SessionDefinition_AutoTrainingStages.m index 3807d3e1..5713f46d 100644 --- a/Protocols/@ArpitCentrePokeTraining/ArpitCentrePokeTraining_SessionDefinition_AutoTrainingStages.m +++ b/Protocols/@ArpitCentrePokeTraining/ArpitCentrePokeTraining_SessionDefinition_AutoTrainingStages.m @@ -46,26 +46,26 @@ callback(ParamsSection_training_stage); end -% Update Training_ParamsSection +% Update TrainingStageParamsSection if value(previous_sides(end)) ~= value(previous_sides(end-1)) && all(hit_history(end-1:end)) % last and present trials should also be a valid trial - Training_ParamsSection_trial_oppSide = value(Training_ParamsSection_trial_oppSide) + 1; % updating value for variable in TrainingParams_Section - callback(Training_ParamsSection_trial_oppSide); + TrainingStageParamsSection_trial_oppSide = value(TrainingStageParamsSection_trial_oppSide) + 1; % updating value for variable in TrainingParams_Section + callback(TrainingStageParamsSection_trial_oppSide); end % Updating Disp Values for Training_Peformance_Summary if n_completed_trials > 0 - Training_Performance_Summary_stage_1_Trials.value = value(Training_Performance_Summary_stage_1_Trials) + 1; - Training_Performance_Summary_stage_1_TrialsToday.value = value(Training_Performance_Summary_stage_1_TrialsToday) + 1; - Training_Performance_Summary_stage_1_ViolationRate.value = nan; - Training_Performance_Summary_stage_1_TimeoutRate.value = nan; + PerformanceSummarySection_stage_1_Trials.value = value(PerformanceSummarySection_stage_1_Trials) + 1; + PerformanceSummarySection_stage_1_TrialsToday.value = value(PerformanceSummarySection_stage_1_TrialsToday) + 1; + PerformanceSummarySection_stage_1_ViolationRate.value = nan; + PerformanceSummarySection_stage_1_TimeoutRate.value = nan; if ~isnan(value(hit_history(end))) - Training_Performance_Summary_stage_1_TrialsValid.value = value(Training_Performance_Summary_stage_1_TrialsValid) + 1; + PerformanceSummarySection_stage_1_TrialsValid.value = value(PerformanceSummarySection_stage_1_TrialsValid) + 1; end - callback(Training_Performance_Summary_stage_1_Trials); - callback(Training_Performance_Summary_stage_1_TrialsToday) - callback(Training_Performance_Summary_stage_1_ViolationRate); - callback(Training_Performance_Summary_stage_1_TimeoutRate); - callback(Training_Performance_Summary_stage_1_TrialsValid); + callback(PerformanceSummarySection_stage_1_Trials); + callback(PerformanceSummarySection_stage_1_TrialsToday) + callback(PerformanceSummarySection_stage_1_ViolationRate); + callback(PerformanceSummarySection_stage_1_TimeoutRate); + callback(PerformanceSummarySection_stage_1_TrialsValid); % Updating Disp Values for Training_Peformance_Summary SessionPerformanceSection_ntrials.value = n_completed_trials; @@ -73,8 +73,8 @@ SessionPerformanceSection_timeout_rate.value = nan; SessionPerformanceSection_violation_recent.value = nan; SessionPerformanceSection_timeout_recent.value = nan; - SessionPerformanceSection_ntrials_stage.value = value(Training_Performance_Summary_stage_1_Trials); - SessionPerformanceSection_ntrials_stage_today.value = value(Training_Performance_Summary_stage_1_TrialsToday); + SessionPerformanceSection_ntrials_stage.value = value(PerformanceSummarySection_stage_1_Trials); + SessionPerformanceSection_ntrials_stage_today.value = value(PerformanceSummarySection_stage_1_TrialsToday); SessionPerformanceSection_timeout_stage.value = nan; SessionPerformanceSection_violation_stage.value = nan; @@ -102,8 +102,8 @@ stage_no = value(SessionDefinition_CURRENT_ACTIVE_STAGE); % only run it if its the start of the day, number of trials is small if n_completed_trials < 100 - if value(Training_Performance_Summary_stage_1_TrialsValid) > value(Training_ParamsSection_total_trials) && ... - value(Training_ParamsSection_trial_oppSide) > value(Training_ParamsSection_total_trials_opp) + if value(PerformanceSummarySection_stage_1_TrialsValid) > value(TrainingStageParamsSection_total_trials) && ... + value(TrainingStageParamsSection_trial_oppSide) > value(TrainingStageParamsSection_total_trials_opp) ParamsSection_training_stage.value = stage_no + 1; callback(ParamsSection_training_stage); ParamsSection(obj, 'Changed_Training_Stage'); @@ -125,29 +125,29 @@ ClearHelperVarsNotOwned(obj); % stage_no = value(SessionDefinition_CURRENT_ACTIVE_STAGE); -if value(Training_Performance_Summary_stage_1_TrialsValid) > value(Training_ParamsSection_total_trials) && ... - value(Training_ParamsSection_trial_oppSide) > value(Training_ParamsSection_total_trials_opp) +if value(PerformanceSummarySection_stage_1_TrialsValid) > value(TrainingStageParamsSection_total_trials) && ... + value(TrainingStageParamsSection_trial_oppSide) > value(TrainingStageParamsSection_total_trials_opp) ParamsSection_training_stage.value = stage_no + 1; callback(ParamsSection_training_stage); ParamsSection(obj, 'Changed_Training_Stage'); SessionDefinition(obj, 'jump_to_stage', 'Timeout Rewarded Side Pokes'); end -Training_Performance_Summary_stage_1_TrialsToday.value = 0; -callback(Training_Performance_Summary_stage_1_TrialsToday); -Training_Performance_Summary_stage_2_TrialsToday.value = 0; -callback(Training_Performance_Summary_stage_2_TrialsToday); -Training_Performance_Summary_stage_3_TrialsToday.value = 0; -callback(Training_Performance_Summary_stage_3_TrialsToday); -Training_Performance_Summary_stage_4_TrialsToday.value = 0; -callback(Training_Performance_Summary_stage_4_TrialsToday); -Training_Performance_Summary_stage_5_TrialsToday.value = 0; -callback(Training_Performance_Summary_stage_5_TrialsToday); -Training_Performance_Summary_stage_6_TrialsToday.value = 0; -callback(Training_Performance_Summary_stage_6_TrialsToday); -Training_Performance_Summary_stage_7_TrialsToday.value = 0; -callback(Training_Performance_Summary_stage_7_TrialsToday); -Training_Performance_Summary_stage_8_TrialsToday.value = 0; -callback(Training_Performance_Summary_stage_8_TrialsToday); +PerformanceSummarySection_stage_1_TrialsToday.value = 0; +callback(PerformanceSummarySection_stage_1_TrialsToday); +PerformanceSummarySection_stage_2_TrialsToday.value = 0; +callback(PerformanceSummarySection_stage_2_TrialsToday); +PerformanceSummarySection_stage_3_TrialsToday.value = 0; +callback(PerformanceSummarySection_stage_3_TrialsToday); +PerformanceSummarySection_stage_4_TrialsToday.value = 0; +callback(PerformanceSummarySection_stage_4_TrialsToday); +PerformanceSummarySection_stage_5_TrialsToday.value = 0; +callback(PerformanceSummarySection_stage_5_TrialsToday); +PerformanceSummarySection_stage_6_TrialsToday.value = 0; +callback(PerformanceSummarySection_stage_6_TrialsToday); +PerformanceSummarySection_stage_7_TrialsToday.value = 0; +callback(PerformanceSummarySection_stage_7_TrialsToday); +PerformanceSummarySection_stage_8_TrialsToday.value = 0; +callback(PerformanceSummarySection_stage_8_TrialsToday); % end @@ -189,13 +189,13 @@ if length(timeout_history) > 5 if all(value(timeout_history(end-1:end))) && value(this_stage_opp_side_trials) >= 2 ParamsSection_RewardCollection_duration.value = min([value(ParamsSection_RewardCollection_duration) + 1,... - value(Training_ParamsSection_max_rColl_dur)]); + value(TrainingStageParamsSection_max_rColl_dur)]); callback(ParamsSection_RewardCollection_duration); this_stage_opp_side_trials.value = 0; end if ~any(value(timeout_history(end-1:end))) && value(this_stage_opp_side_trials) >= 2 ParamsSection_RewardCollection_duration.value = max([value(ParamsSection_RewardCollection_duration) - 1,... - value(Training_ParamsSection_min_rColl_dur)]); + value(TrainingStageParamsSection_min_rColl_dur)]); callback(ParamsSection_RewardCollection_duration); this_stage_opp_side_trials.value = 0; end @@ -207,28 +207,28 @@ end end -% Update Training_ParamsSection +% Update TrainingStageParamsSection if value(previous_sides(end)) ~= value(previous_sides(end-1)) && all(hit_history(end-1:end)) % last and present trials should also be a valid trial - Training_ParamsSection_trial_oppSide = value(Training_ParamsSection_trial_oppSide) + 1; % updating value for variable in TrainingParams_Section - callback(Training_ParamsSection_trial_oppSide); + TrainingStageParamsSection_trial_oppSide = value(TrainingStageParamsSection_trial_oppSide) + 1; % updating value for variable in TrainingParams_Section + callback(TrainingStageParamsSection_trial_oppSide); end % Updating Disp Values for Training_Peformance_Summary if n_completed_trials > 0 - Training_Performance_Summary_stage_2_Trials.value = value(Training_Performance_Summary_stage_2_Trials) + 1; - Training_Performance_Summary_stage_2_TrialsToday.value = value(Training_Performance_Summary_stage_2_TrialsToday) + 1; - Training_Performance_Summary_stage_2_ViolationRate.value = nan; - Training_Performance_Summary_stage_2_TimeoutRate.value = ((value(Training_Performance_Summary_stage_2_TimeoutRate) * (value(Training_Performance_Summary_stage_2_Trials) - 1)) + double(timeout_history(end))) / value(Training_Performance_Summary_stage_2_Trials); + PerformanceSummarySection_stage_2_Trials.value = value(PerformanceSummarySection_stage_2_Trials) + 1; + PerformanceSummarySection_stage_2_TrialsToday.value = value(PerformanceSummarySection_stage_2_TrialsToday) + 1; + PerformanceSummarySection_stage_2_ViolationRate.value = nan; + PerformanceSummarySection_stage_2_TimeoutRate.value = ((value(PerformanceSummarySection_stage_2_TimeoutRate) * (value(PerformanceSummarySection_stage_2_Trials) - 1)) + double(timeout_history(end))) / value(PerformanceSummarySection_stage_2_Trials); if ~isnan(value(hit_history(end))) - Training_Performance_Summary_stage_2_TrialsValid.value = value(Training_Performance_Summary_stage_2_TrialsValid) + 1; + PerformanceSummarySection_stage_2_TrialsValid.value = value(PerformanceSummarySection_stage_2_TrialsValid) + 1; end - callback(Training_Performance_Summary_stage_2_Trials); - callback(Training_Performance_Summary_stage_2_TrialsToday) - callback(Training_Performance_Summary_stage_2_ViolationRate); - callback(Training_Performance_Summary_stage_2_TimeoutRate); - callback(Training_Performance_Summary_stage_2_TrialsValid); + callback(PerformanceSummarySection_stage_2_Trials); + callback(PerformanceSummarySection_stage_2_TrialsToday) + callback(PerformanceSummarySection_stage_2_ViolationRate); + callback(PerformanceSummarySection_stage_2_TimeoutRate); + callback(PerformanceSummarySection_stage_2_TrialsValid); % Updating Disp Values for Training_Peformance_Summary SessionPerformanceSection_ntrials.value = n_completed_trials; @@ -241,9 +241,9 @@ SessionPerformanceSection_timeout_recent.value = nan; end SessionPerformanceSection_violation_stage.value = nan; - SessionPerformanceSection_ntrials_stage.value = value(Training_Performance_Summary_stage_2_Trials); - SessionPerformanceSection_ntrials_stage_today.value = value(Training_Performance_Summary_stage_2_TrialsToday); - SessionPerformanceSection_timeout_stage.value = value(Training_Performance_Summary_stage_2_TimeoutRate); + SessionPerformanceSection_ntrials_stage.value = value(PerformanceSummarySection_stage_2_Trials); + SessionPerformanceSection_ntrials_stage_today.value = value(PerformanceSummarySection_stage_2_TrialsToday); + SessionPerformanceSection_timeout_stage.value = value(PerformanceSummarySection_stage_2_TimeoutRate); callback(SessionPerformanceSection_ntrials); callback(SessionPerformanceSection_ntrials_stage); @@ -269,8 +269,8 @@ stage_no = value(SessionDefinition_CURRENT_ACTIVE_STAGE); % only run it if its the start of the day, number of trials is small if n_completed_trials > 50 - if value(Training_Performance_Summary_stage_2_TrialsValid) > value(Training_ParamsSection_total_trials) && ... - value(Training_ParamsSection_trial_oppSide) > value(Training_ParamsSection_total_trials_opp) + if value(PerformanceSummarySection_stage_2_TrialsValid) > value(TrainingStageParamsSection_total_trials) && ... + value(TrainingStageParamsSection_trial_oppSide) > value(TrainingStageParamsSection_total_trials_opp) ParamsSection_training_stage.value = stage_no + 1; callback(ParamsSection_training_stage); ParamsSection(obj, 'Changed_Training_Stage'); @@ -291,22 +291,22 @@ GetSoloFunctionArgs(obj); ClearHelperVarsNotOwned(obj); % -Training_Performance_Summary_stage_1_TrialsToday.value = 0; -callback(Training_Performance_Summary_stage_1_TrialsToday); -Training_Performance_Summary_stage_2_TrialsToday.value = 0; -callback(Training_Performance_Summary_stage_2_TrialsToday); -Training_Performance_Summary_stage_3_TrialsToday.value = 0; -callback(Training_Performance_Summary_stage_3_TrialsToday); -Training_Performance_Summary_stage_4_TrialsToday.value = 0; -callback(Training_Performance_Summary_stage_4_TrialsToday); -Training_Performance_Summary_stage_5_TrialsToday.value = 0; -callback(Training_Performance_Summary_stage_5_TrialsToday); -Training_Performance_Summary_stage_6_TrialsToday.value = 0; -callback(Training_Performance_Summary_stage_6_TrialsToday); -Training_Performance_Summary_stage_7_TrialsToday.value = 0; -callback(Training_Performance_Summary_stage_7_TrialsToday); -Training_Performance_Summary_stage_8_TrialsToday.value = 0; -callback(Training_Performance_Summary_stage_8_TrialsToday); +PerformanceSummarySection_stage_1_TrialsToday.value = 0; +callback(PerformanceSummarySection_stage_1_TrialsToday); +PerformanceSummarySection_stage_2_TrialsToday.value = 0; +callback(PerformanceSummarySection_stage_2_TrialsToday); +PerformanceSummarySection_stage_3_TrialsToday.value = 0; +callback(PerformanceSummarySection_stage_3_TrialsToday); +PerformanceSummarySection_stage_4_TrialsToday.value = 0; +callback(PerformanceSummarySection_stage_4_TrialsToday); +PerformanceSummarySection_stage_5_TrialsToday.value = 0; +callback(PerformanceSummarySection_stage_5_TrialsToday); +PerformanceSummarySection_stage_6_TrialsToday.value = 0; +callback(PerformanceSummarySection_stage_6_TrialsToday); +PerformanceSummarySection_stage_7_TrialsToday.value = 0; +callback(PerformanceSummarySection_stage_7_TrialsToday); +PerformanceSummarySection_stage_8_TrialsToday.value = 0; +callback(PerformanceSummarySection_stage_8_TrialsToday); % end % @@ -345,7 +345,7 @@ callback(ParamsSection_training_stage); end % Change the value of CP Duration -if value(Training_ParamsSection_last_session_CP) == 0 && value(Training_Performance_Summary_stage_3_Trials) < 2 +if value(TrainingStageParamsSection_last_session_CP) == 0 && value(PerformanceSummarySection_stage_3_Trials) < 2 ParamsSection_CP_duration.value = cp_min; % initialize to min_CP end @@ -353,7 +353,7 @@ ParamsSection_CP_duration.value = value(ParamsSection_init_CP_duration); else if ~timeout_history(end) && value(ParamsSection_CP_duration) < cp_max - increment = value(ParamsSection_CP_duration) * value(Training_ParamsSection_CPfraction_inc); + increment = value(ParamsSection_CP_duration) * value(TrainingStageParamsSection_CPfraction_inc); if increment < cp_minimum_increment increment = cp_minimum_increment; end @@ -364,19 +364,19 @@ if n_completed_trials > 0 % Updating Disp Values for Training_Peformance_Summary - Training_Performance_Summary_stage_3_Trials.value = value(Training_Performance_Summary_stage_3_Trials) + 1; - Training_Performance_Summary_stage_3_TrialsToday.value = value(Training_Performance_Summary_stage_3_TrialsToday) + 1; - Training_Performance_Summary_stage_3_ViolationRate.value = nan; - Training_Performance_Summary_stage_3_TimeoutRate.value = ((value(Training_Performance_Summary_stage_3_TimeoutRate) * (value(Training_Performance_Summary_stage_3_Trials) - 1)) + double(timeout_history(end))) / value(Training_Performance_Summary_stage_3_Trials); + PerformanceSummarySection_stage_3_Trials.value = value(PerformanceSummarySection_stage_3_Trials) + 1; + PerformanceSummarySection_stage_3_TrialsToday.value = value(PerformanceSummarySection_stage_3_TrialsToday) + 1; + PerformanceSummarySection_stage_3_ViolationRate.value = nan; + PerformanceSummarySection_stage_3_TimeoutRate.value = ((value(PerformanceSummarySection_stage_3_TimeoutRate) * (value(PerformanceSummarySection_stage_3_Trials) - 1)) + double(timeout_history(end))) / value(PerformanceSummarySection_stage_3_Trials); if ~isnan(value(hit_history(end))) - Training_Performance_Summary_stage_3_TrialsValid.value = value(Training_Performance_Summary_stage_3_TrialsValid) + 1; + PerformanceSummarySection_stage_3_TrialsValid.value = value(PerformanceSummarySection_stage_3_TrialsValid) + 1; end - callback(Training_Performance_Summary_stage_3_Trials); - callback(Training_Performance_Summary_stage_3_TrialsToday) - callback(Training_Performance_Summary_stage_3_ViolationRate); - callback(Training_Performance_Summary_stage_3_TimeoutRate); - callback(Training_Performance_Summary_stage_3_TrialsValid); + callback(PerformanceSummarySection_stage_3_Trials); + callback(PerformanceSummarySection_stage_3_TrialsToday) + callback(PerformanceSummarySection_stage_3_ViolationRate); + callback(PerformanceSummarySection_stage_3_TimeoutRate); + callback(PerformanceSummarySection_stage_3_TrialsValid); % Updating Disp Values for Training_Peformance_Summary SessionPerformanceSection_ntrials.value = n_completed_trials; @@ -389,9 +389,9 @@ SessionPerformanceSection_timeout_recent.value = nan; end SessionPerformanceSection_violation_stage.value = nan; - SessionPerformanceSection_ntrials_stage.value = value(Training_Performance_Summary_stage_3_Trials); - SessionPerformanceSection_ntrials_stage_today.value = value(Training_Performance_Summary_stage_3_TrialsToday); - SessionPerformanceSection_timeout_stage.value = value(Training_Performance_Summary_stage_3_TimeoutRate); + SessionPerformanceSection_ntrials_stage.value = value(PerformanceSummarySection_stage_3_Trials); + SessionPerformanceSection_ntrials_stage_today.value = value(PerformanceSummarySection_stage_3_TrialsToday); + SessionPerformanceSection_timeout_stage.value = value(PerformanceSummarySection_stage_3_TimeoutRate); callback(SessionPerformanceSection_ntrials); callback(SessionPerformanceSection_ntrials_stage); @@ -416,8 +416,8 @@ if ParamsSection_use_auto_train % do completion check if auto training cp_max = value(ParamsSection_SettlingIn_time) + value(ParamsSection_legal_cbreak); if value(ParamsSection_CP_duration) >= cp_max - Training_ParamsSection_last_session_CP.value = value(ParamsSection_CP_duration); - callback(Training_ParamsSection_last_session_CP); + TrainingStageParamsSection_last_session_CP.value = value(ParamsSection_CP_duration); + callback(TrainingStageParamsSection_last_session_CP); ParamsSection_training_stage.value = 4; callback(ParamsSection_training_stage); ParamsSection(obj, 'Changed_Training_Stage'); @@ -439,25 +439,25 @@ ClearHelperVarsNotOwned(obj); % % Update the CP duration reached in this session -Training_ParamsSection_last_session_CP.value = value(ParamsSection_CP_duration); -callback(Training_ParamsSection_last_session_CP); +TrainingStageParamsSection_last_session_CP.value = value(ParamsSection_CP_duration); +callback(TrainingStageParamsSection_last_session_CP); % Reset the number of trials done today for this stage -Training_Performance_Summary_stage_1_TrialsToday.value = 0; -callback(Training_Performance_Summary_stage_1_TrialsToday); -Training_Performance_Summary_stage_2_TrialsToday.value = 0; -callback(Training_Performance_Summary_stage_2_TrialsToday); -Training_Performance_Summary_stage_3_TrialsToday.value = 0; -callback(Training_Performance_Summary_stage_3_TrialsToday); -Training_Performance_Summary_stage_4_TrialsToday.value = 0; -callback(Training_Performance_Summary_stage_4_TrialsToday); -Training_Performance_Summary_stage_5_TrialsToday.value = 0; -callback(Training_Performance_Summary_stage_5_TrialsToday); -Training_Performance_Summary_stage_6_TrialsToday.value = 0; -callback(Training_Performance_Summary_stage_6_TrialsToday); -Training_Performance_Summary_stage_7_TrialsToday.value = 0; -callback(Training_Performance_Summary_stage_7_TrialsToday); -Training_Performance_Summary_stage_8_TrialsToday.value = 0; -callback(Training_Performance_Summary_stage_8_TrialsToday); +PerformanceSummarySection_stage_1_TrialsToday.value = 0; +callback(PerformanceSummarySection_stage_1_TrialsToday); +PerformanceSummarySection_stage_2_TrialsToday.value = 0; +callback(PerformanceSummarySection_stage_2_TrialsToday); +PerformanceSummarySection_stage_3_TrialsToday.value = 0; +callback(PerformanceSummarySection_stage_3_TrialsToday); +PerformanceSummarySection_stage_4_TrialsToday.value = 0; +callback(PerformanceSummarySection_stage_4_TrialsToday); +PerformanceSummarySection_stage_5_TrialsToday.value = 0; +callback(PerformanceSummarySection_stage_5_TrialsToday); +PerformanceSummarySection_stage_6_TrialsToday.value = 0; +callback(PerformanceSummarySection_stage_6_TrialsToday); +PerformanceSummarySection_stage_7_TrialsToday.value = 0; +callback(PerformanceSummarySection_stage_7_TrialsToday); +PerformanceSummarySection_stage_8_TrialsToday.value = 0; +callback(PerformanceSummarySection_stage_8_TrialsToday); % end % @@ -484,9 +484,9 @@ % Maximum & Minimum duration of center poke, in secs: cp_min = value(ParamsSection_SettlingIn_time) + value(ParamsSection_legal_cbreak); -cp_max = value(Training_ParamsSection_max_CP); +cp_max = value(TrainingStageParamsSection_max_CP); % Fractional increment in center poke duration every time there is a non-cp-violation trial: -cp_fraction = value(Training_ParamsSection_CPfraction_inc); +cp_fraction = value(TrainingStageParamsSection_CPfraction_inc); % Minimum increment (in secs) in center poke duration every time there is a non-cp-violation trial: cp_minimum_increment = 0.001; @@ -497,7 +497,7 @@ end % Change the value of CP Duration -if value(Training_Performance_Summary_stage_4_Trials) < 2 +if value(PerformanceSummarySection_stage_4_Trials) < 2 ParamsSection_CP_duration.value = cp_min; % initialize to min_CP end @@ -520,19 +520,19 @@ if n_completed_trials > 0 % Updating Disp Values for Training_Peformance_Summary - Training_Performance_Summary_stage_4_Trials.value = value(Training_Performance_Summary_stage_4_Trials) + 1; - Training_Performance_Summary_stage_4_TrialsToday.value = value(Training_Performance_Summary_stage_4_TrialsToday) + 1; - Training_Performance_Summary_stage_4_ViolationRate.value = ((value(Training_Performance_Summary_stage_4_ViolationRate) * (value(Training_Performance_Summary_stage_4_Trials) - 1)) + double(violation_history(end))) / value(Training_Performance_Summary_stage_4_Trials); - Training_Performance_Summary_stage_4_TimeoutRate.value = ((value(Training_Performance_Summary_stage_4_TimeoutRate) * (value(Training_Performance_Summary_stage_4_Trials) - 1)) + double(timeout_history(end))) / value(Training_Performance_Summary_stage_4_Trials); + PerformanceSummarySection_stage_4_Trials.value = value(PerformanceSummarySection_stage_4_Trials) + 1; + PerformanceSummarySection_stage_4_TrialsToday.value = value(PerformanceSummarySection_stage_4_TrialsToday) + 1; + PerformanceSummarySection_stage_4_ViolationRate.value = ((value(PerformanceSummarySection_stage_4_ViolationRate) * (value(PerformanceSummarySection_stage_4_Trials) - 1)) + double(violation_history(end))) / value(PerformanceSummarySection_stage_4_Trials); + PerformanceSummarySection_stage_4_TimeoutRate.value = ((value(PerformanceSummarySection_stage_4_TimeoutRate) * (value(PerformanceSummarySection_stage_4_Trials) - 1)) + double(timeout_history(end))) / value(PerformanceSummarySection_stage_4_Trials); if ~isnan(value(hit_history(end))) - Training_Performance_Summary_stage_4_TrialsValid.value = value(Training_Performance_Summary_stage_4_TrialsValid) + 1; + PerformanceSummarySection_stage_4_TrialsValid.value = value(PerformanceSummarySection_stage_4_TrialsValid) + 1; end - callback(Training_Performance_Summary_stage_4_Trials); - callback(Training_Performance_Summary_stage_4_TrialsToday) - callback(Training_Performance_Summary_stage_4_ViolationRate); - callback(Training_Performance_Summary_stage_4_TimeoutRate); - callback(Training_Performance_Summary_stage_4_TrialsValid); + callback(PerformanceSummarySection_stage_4_Trials); + callback(PerformanceSummarySection_stage_4_TrialsToday) + callback(PerformanceSummarySection_stage_4_ViolationRate); + callback(PerformanceSummarySection_stage_4_TimeoutRate); + callback(PerformanceSummarySection_stage_4_TrialsValid); % Updating Disp Values for Training_Peformance_Summary SessionPerformanceSection_ntrials.value = n_completed_trials; @@ -545,10 +545,10 @@ SessionPerformanceSection_timeout_recent.value = nan; SessionPerformanceSection_violation_recent.value = nan; end - SessionPerformanceSection_violation_stage.value = value(Training_Performance_Summary_stage_4_ViolationRate); - SessionPerformanceSection_ntrials_stage.value = value(Training_Performance_Summary_stage_4_Trials); - SessionPerformanceSection_ntrials_stage_today.value = value(Training_Performance_Summary_stage_4_TrialsToday); - SessionPerformanceSection_timeout_stage.value = value(Training_Performance_Summary_stage_4_TimeoutRate); + SessionPerformanceSection_violation_stage.value = value(PerformanceSummarySection_stage_4_ViolationRate); + SessionPerformanceSection_ntrials_stage.value = value(PerformanceSummarySection_stage_4_Trials); + SessionPerformanceSection_ntrials_stage_today.value = value(PerformanceSummarySection_stage_4_TrialsToday); + SessionPerformanceSection_timeout_stage.value = value(PerformanceSummarySection_stage_4_TimeoutRate); callback(SessionPerformanceSection_ntrials); callback(SessionPerformanceSection_ntrials_stage); @@ -570,18 +570,18 @@ clear('ans'); % if ParamsSection_use_auto_train % do completion check if auto training - cp_max = value(Training_ParamsSection_max_CP); + cp_max = value(TrainingStageParamsSection_max_CP); if value(ParamsSection_CP_duration) >= cp_max && n_completed_trials > 100 && ... - value(SessionPerformanceSection_violation_recent) < value(Training_ParamsSection_recent_violation) && ... - value(SessionPerformanceSection_timeout_recent) < value(Training_ParamsSection_recent_timeout) && ... - value(SessionPerformanceSection_violation_stage) < value(Training_ParamsSection_stage_violation) + value(SessionPerformanceSection_violation_recent) < value(TrainingStageParamsSection_recent_violation) && ... + value(SessionPerformanceSection_timeout_recent) < value(TrainingStageParamsSection_recent_timeout) && ... + value(SessionPerformanceSection_violation_stage) < value(TrainingStageParamsSection_stage_violation) ParamsSection_training_stage.value = 5; callback(ParamsSection_training_stage); ParamsSection(obj, 'Changed_Training_Stage'); SessionDefinition(obj, 'jump_to_stage', 'Introduce Stimuli Sound during Centre Poke'); - Training_ParamsSection_last_session_CP.value = value(ParamsSection_CP_duration); - callback(Training_ParamsSection_last_session_CP); + TrainingStageParamsSection_last_session_CP.value = value(ParamsSection_CP_duration); + callback(TrainingStageParamsSection_last_session_CP); end end % @@ -598,25 +598,25 @@ ClearHelperVarsNotOwned(obj); % % Update the CP duration reached in this session -Training_ParamsSection_last_session_CP.value = value(ParamsSection_CP_duration); -callback(Training_ParamsSection_last_session_CP); +TrainingStageParamsSection_last_session_CP.value = value(ParamsSection_CP_duration); +callback(TrainingStageParamsSection_last_session_CP); % Reset the number of trials done today for this stage -Training_Performance_Summary_stage_1_TrialsToday.value = 0; -callback(Training_Performance_Summary_stage_1_TrialsToday); -Training_Performance_Summary_stage_2_TrialsToday.value = 0; -callback(Training_Performance_Summary_stage_2_TrialsToday); -Training_Performance_Summary_stage_3_TrialsToday.value = 0; -callback(Training_Performance_Summary_stage_3_TrialsToday); -Training_Performance_Summary_stage_4_TrialsToday.value = 0; -callback(Training_Performance_Summary_stage_4_TrialsToday); -Training_Performance_Summary_stage_5_TrialsToday.value = 0; -callback(Training_Performance_Summary_stage_5_TrialsToday); -Training_Performance_Summary_stage_6_TrialsToday.value = 0; -callback(Training_Performance_Summary_stage_6_TrialsToday); -Training_Performance_Summary_stage_7_TrialsToday.value = 0; -callback(Training_Performance_Summary_stage_7_TrialsToday); -Training_Performance_Summary_stage_8_TrialsToday.value = 0; -callback(Training_Performance_Summary_stage_8_TrialsToday); +PerformanceSummarySection_stage_1_TrialsToday.value = 0; +callback(PerformanceSummarySection_stage_1_TrialsToday); +PerformanceSummarySection_stage_2_TrialsToday.value = 0; +callback(PerformanceSummarySection_stage_2_TrialsToday); +PerformanceSummarySection_stage_3_TrialsToday.value = 0; +callback(PerformanceSummarySection_stage_3_TrialsToday); +PerformanceSummarySection_stage_4_TrialsToday.value = 0; +callback(PerformanceSummarySection_stage_4_TrialsToday); +PerformanceSummarySection_stage_5_TrialsToday.value = 0; +callback(PerformanceSummarySection_stage_5_TrialsToday); +PerformanceSummarySection_stage_6_TrialsToday.value = 0; +callback(PerformanceSummarySection_stage_6_TrialsToday); +PerformanceSummarySection_stage_7_TrialsToday.value = 0; +callback(PerformanceSummarySection_stage_7_TrialsToday); +PerformanceSummarySection_stage_8_TrialsToday.value = 0; +callback(PerformanceSummarySection_stage_8_TrialsToday); % end % @@ -642,16 +642,16 @@ GetSoloFunctionArgs(obj); ClearHelperVarsNotOwned(obj); % -cp_max = value(Training_ParamsSection_max_CP); -cp_min = value(Training_ParamsSection_min_CP); +cp_max = value(TrainingStageParamsSection_max_CP); +cp_min = value(TrainingStageParamsSection_min_CP); % Fractional increment in center poke duration every time there is a non-cp-violation trial: -cp_fraction = value(Training_ParamsSection_CPfraction_inc); +cp_fraction = value(TrainingStageParamsSection_CPfraction_inc); % Minimum increment (in secs) in center poke duration every time there is a non-cp-violation trial: cp_minimum_increment = 0.001; % Starting total center poke duration: -starting_cp = value(Training_ParamsSection_starting_CP) + value(ParamsSection_SettlingIn_time); +starting_cp = value(TrainingStageParamsSection_starting_CP) + value(ParamsSection_SettlingIn_time); % number of warm-up trials -n_trial_warmup = value(Training_ParamsSection_warm_up_trials); +n_trial_warmup = value(TrainingStageParamsSection_warm_up_trials); stage_no = value(SessionDefinition_CURRENT_ACTIVE_STAGE); if stage_no ~= value(ParamsSection_training_stage) @@ -667,10 +667,10 @@ ParamsSection_CP_duration.value = starting_cp; else if ~violation_history(end) && ~timeout_history(end) - if value(ParamsSection_CP_duration) < max([cp_min,value(Training_ParamsSection_last_session_CP)]) % warm up stage - increment = (max([cp_min,value(Training_ParamsSection_last_session_CP)]) - value(ParamsSection_CP_duration))/ (n_trial_warmup - 1); + if value(ParamsSection_CP_duration) < max([cp_min,value(TrainingStageParamsSection_last_session_CP)]) % warm up stage + increment = (max([cp_min,value(TrainingStageParamsSection_last_session_CP)]) - value(ParamsSection_CP_duration))/ (n_trial_warmup - 1); else - if value(ParamsSection_CP_duration) >= value(Training_ParamsSection_last_session_CP) && value(ParamsSection_CP_duration) <= cp_max % no warm up stage + if value(ParamsSection_CP_duration) >= value(TrainingStageParamsSection_last_session_CP) && value(ParamsSection_CP_duration) <= cp_max % no warm up stage increment = value(ParamsSection_CP_duration)*cp_fraction; if increment < cp_minimum_increment increment = cp_minimum_increment; @@ -719,19 +719,19 @@ if n_completed_trials > 0 % Updating Disp Values for Training_Peformance_Summary - Training_Performance_Summary_stage_5_Trials.value = value(Training_Performance_Summary_stage_5_Trials) + 1; - Training_Performance_Summary_stage_5_TrialsToday.value = value(Training_Performance_Summary_stage_5_TrialsToday) + 1; - Training_Performance_Summary_stage_5_ViolationRate.value = ((value(Training_Performance_Summary_stage_5_ViolationRate) * (value(Training_Performance_Summary_stage_5_Trials) - 1)) + double(violation_history(end))) / value(Training_Performance_Summary_stage_5_Trials); - Training_Performance_Summary_stage_5_TimeoutRate.value = ((value(Training_Performance_Summary_stage_5_TimeoutRate) * (value(Training_Performance_Summary_stage_5_Trials) - 1)) + double(timeout_history(end))) / value(Training_Performance_Summary_stage_5_Trials); + PerformanceSummarySection_stage_5_Trials.value = value(PerformanceSummarySection_stage_5_Trials) + 1; + PerformanceSummarySection_stage_5_TrialsToday.value = value(PerformanceSummarySection_stage_5_TrialsToday) + 1; + PerformanceSummarySection_stage_5_ViolationRate.value = ((value(PerformanceSummarySection_stage_5_ViolationRate) * (value(PerformanceSummarySection_stage_5_Trials) - 1)) + double(violation_history(end))) / value(PerformanceSummarySection_stage_5_Trials); + PerformanceSummarySection_stage_5_TimeoutRate.value = ((value(PerformanceSummarySection_stage_5_TimeoutRate) * (value(PerformanceSummarySection_stage_5_Trials) - 1)) + double(timeout_history(end))) / value(PerformanceSummarySection_stage_5_Trials); if ~isnan(value(hit_history(end))) - Training_Performance_Summary_stage_5_TrialsValid.value = value(Training_Performance_Summary_stage_5_TrialsValid) + 1; + PerformanceSummarySection_stage_5_TrialsValid.value = value(PerformanceSummarySection_stage_5_TrialsValid) + 1; end - callback(Training_Performance_Summary_stage_5_Trials); - callback(Training_Performance_Summary_stage_5_TrialsToday) - callback(Training_Performance_Summary_stage_5_ViolationRate); - callback(Training_Performance_Summary_stage_5_TimeoutRate); - callback(Training_Performance_Summary_stage_5_TrialsValid); + callback(PerformanceSummarySection_stage_5_Trials); + callback(PerformanceSummarySection_stage_5_TrialsToday) + callback(PerformanceSummarySection_stage_5_ViolationRate); + callback(PerformanceSummarySection_stage_5_TimeoutRate); + callback(PerformanceSummarySection_stage_5_TrialsValid); % Updating Disp Values for Training_Peformance_Summary SessionPerformanceSection_ntrials.value = n_completed_trials; @@ -744,10 +744,10 @@ SessionPerformanceSection_timeout_recent.value = nan; SessionPerformanceSection_violation_recent.value = nan; end - SessionPerformanceSection_violation_stage.value = value(Training_Performance_Summary_stage_5_ViolationRate); - SessionPerformanceSection_ntrials_stage.value = value(Training_Performance_Summary_stage_5_Trials); - SessionPerformanceSection_ntrials_stage_today.value = value(Training_Performance_Summary_stage_5_TrialsToday); - SessionPerformanceSection_timeout_stage.value = value(Training_Performance_Summary_stage_5_TimeoutRate); + SessionPerformanceSection_violation_stage.value = value(PerformanceSummarySection_stage_5_ViolationRate); + SessionPerformanceSection_ntrials_stage.value = value(PerformanceSummarySection_stage_5_Trials); + SessionPerformanceSection_ntrials_stage_today.value = value(PerformanceSummarySection_stage_5_TrialsToday); + SessionPerformanceSection_timeout_stage.value = value(PerformanceSummarySection_stage_5_TimeoutRate); callback(SessionPerformanceSection_ntrials); callback(SessionPerformanceSection_ntrials_stage); @@ -772,14 +772,14 @@ % if ParamsSection_use_auto_train % do completion check if auto training stage_no = value(SessionDefinition_CURRENT_ACTIVE_STAGE); - if value(ParamsSection_CP_duration) >= value(Training_ParamsSection_max_CP) && value(Training_Performance_Summary_stage_5_TrialsValid) > value(Training_ParamsSection_total_trials) - if value(SessionPerformanceSection_violation_recent) < value(Training_ParamsSection_recent_violation) && value(SessionPerformanceSection_timeout_recent) < value(Training_ParamsSection_recent_timeout) && ... - value(SessionPerformanceSection_violation_stage) < value(Training_ParamsSection_stage_violation) && n_completed_trials > 100 + if value(ParamsSection_CP_duration) >= value(TrainingStageParamsSection_max_CP) && value(PerformanceSummarySection_stage_5_TrialsValid) > value(TrainingStageParamsSection_total_trials) + if value(SessionPerformanceSection_violation_recent) < value(TrainingStageParamsSection_recent_violation) && value(SessionPerformanceSection_timeout_recent) < value(TrainingStageParamsSection_recent_timeout) && ... + value(SessionPerformanceSection_violation_stage) < value(TrainingStageParamsSection_stage_violation) && n_completed_trials > 100 ParamsSection_training_stage.value = stage_no + 1; callback(ParamsSection_training_stage); ParamsSection(obj, 'Changed_Training_Stage'); SessionDefinition(obj, 'jump_to_stage', 'Vary Stimuli location during Centre Poke'); - Training_ParamsSection_last_session_CP.value = value(ParamsSection_CP_duration); + TrainingStageParamsSection_last_session_CP.value = value(ParamsSection_CP_duration); end end end @@ -798,25 +798,25 @@ ClearHelperVarsNotOwned(obj); % % Update the CP duration reached in this session -Training_ParamsSection_last_session_CP.value = value(ParamsSection_CP_duration); -callback(Training_ParamsSection_last_session_CP); +TrainingStageParamsSection_last_session_CP.value = value(ParamsSection_CP_duration); +callback(TrainingStageParamsSection_last_session_CP); % Reset the number of trials done today for this stage -Training_Performance_Summary_stage_1_TrialsToday.value = 0; -callback(Training_Performance_Summary_stage_1_TrialsToday); -Training_Performance_Summary_stage_2_TrialsToday.value = 0; -callback(Training_Performance_Summary_stage_2_TrialsToday); -Training_Performance_Summary_stage_3_TrialsToday.value = 0; -callback(Training_Performance_Summary_stage_3_TrialsToday); -Training_Performance_Summary_stage_4_TrialsToday.value = 0; -callback(Training_Performance_Summary_stage_4_TrialsToday); -Training_Performance_Summary_stage_5_TrialsToday.value = 0; -callback(Training_Performance_Summary_stage_5_TrialsToday); -Training_Performance_Summary_stage_6_TrialsToday.value = 0; -callback(Training_Performance_Summary_stage_6_TrialsToday); -Training_Performance_Summary_stage_7_TrialsToday.value = 0; -callback(Training_Performance_Summary_stage_7_TrialsToday); -Training_Performance_Summary_stage_8_TrialsToday.value = 0; -callback(Training_Performance_Summary_stage_8_TrialsToday); +PerformanceSummarySection_stage_1_TrialsToday.value = 0; +callback(PerformanceSummarySection_stage_1_TrialsToday); +PerformanceSummarySection_stage_2_TrialsToday.value = 0; +callback(PerformanceSummarySection_stage_2_TrialsToday); +PerformanceSummarySection_stage_3_TrialsToday.value = 0; +callback(PerformanceSummarySection_stage_3_TrialsToday); +PerformanceSummarySection_stage_4_TrialsToday.value = 0; +callback(PerformanceSummarySection_stage_4_TrialsToday); +PerformanceSummarySection_stage_5_TrialsToday.value = 0; +callback(PerformanceSummarySection_stage_5_TrialsToday); +PerformanceSummarySection_stage_6_TrialsToday.value = 0; +callback(PerformanceSummarySection_stage_6_TrialsToday); +PerformanceSummarySection_stage_7_TrialsToday.value = 0; +callback(PerformanceSummarySection_stage_7_TrialsToday); +PerformanceSummarySection_stage_8_TrialsToday.value = 0; +callback(PerformanceSummarySection_stage_8_TrialsToday); % end @@ -842,14 +842,14 @@ GetSoloFunctionArgs(obj); ClearHelperVarsNotOwned(obj); % -cp_max = value(Training_ParamsSection_max_CP); +cp_max = value(TrainingStageParamsSection_max_CP); % Starting total center poke duration: -starting_cp = value(Training_ParamsSection_starting_CP) + value(ParamsSection_SettlingIn_time); +starting_cp = value(TrainingStageParamsSection_starting_CP) + value(ParamsSection_SettlingIn_time); % number of warm-up trials -n_trial_warmup = value(Training_ParamsSection_warm_up_trials); -prestim_min = value(Training_ParamsSection_min_prestim); -prestim_max = value(Training_ParamsSection_max_prestim); -stim_dur = value(Training_ParamsSection_stim_dur); +n_trial_warmup = value(TrainingStageParamsSection_warm_up_trials); +prestim_min = value(TrainingStageParamsSection_min_prestim); +prestim_max = value(TrainingStageParamsSection_max_prestim); +stim_dur = value(TrainingStageParamsSection_stim_dur); stage_no = value(SessionDefinition_CURRENT_ACTIVE_STAGE); if stage_no ~= value(ParamsSection_training_stage) @@ -900,19 +900,19 @@ if n_completed_trials > 0 % Updating Disp Values for Training_Peformance_Summary - Training_Performance_Summary_stage_6_Trials.value = value(Training_Performance_Summary_stage_6_Trials) + 1; - Training_Performance_Summary_stage_6_TrialsToday.value = value(Training_Performance_Summary_stage_6_TrialsToday) + 1; - Training_Performance_Summary_stage_6_ViolationRate.value = ((value(Training_Performance_Summary_stage_6_ViolationRate) * (value(Training_Performance_Summary_stage_6_Trials) - 1)) + double(violation_history(end))) / value(Training_Performance_Summary_stage_6_Trials); - Training_Performance_Summary_stage_6_TimeoutRate.value = ((value(Training_Performance_Summary_stage_6_TimeoutRate) * (value(Training_Performance_Summary_stage_6_Trials) - 1)) + double(timeout_history(end))) / value(Training_Performance_Summary_stage_6_Trials); + PerformanceSummarySection_stage_6_Trials.value = value(PerformanceSummarySection_stage_6_Trials) + 1; + PerformanceSummarySection_stage_6_TrialsToday.value = value(PerformanceSummarySection_stage_6_TrialsToday) + 1; + PerformanceSummarySection_stage_6_ViolationRate.value = ((value(PerformanceSummarySection_stage_6_ViolationRate) * (value(PerformanceSummarySection_stage_6_Trials) - 1)) + double(violation_history(end))) / value(PerformanceSummarySection_stage_6_Trials); + PerformanceSummarySection_stage_6_TimeoutRate.value = ((value(PerformanceSummarySection_stage_6_TimeoutRate) * (value(PerformanceSummarySection_stage_6_Trials) - 1)) + double(timeout_history(end))) / value(PerformanceSummarySection_stage_6_Trials); if ~isnan(value(hit_history(end))) - Training_Performance_Summary_stage_6_TrialsValid.value = value(Training_Performance_Summary_stage_6_TrialsValid) + 1; + PerformanceSummarySection_stage_6_TrialsValid.value = value(PerformanceSummarySection_stage_6_TrialsValid) + 1; end - callback(Training_Performance_Summary_stage_6_Trials); - callback(Training_Performance_Summary_stage_6_TrialsToday) - callback(Training_Performance_Summary_stage_6_ViolationRate); - callback(Training_Performance_Summary_stage_6_TimeoutRate); - callback(Training_Performance_Summary_stage_6_TrialsValid); + callback(PerformanceSummarySection_stage_6_Trials); + callback(PerformanceSummarySection_stage_6_TrialsToday) + callback(PerformanceSummarySection_stage_6_ViolationRate); + callback(PerformanceSummarySection_stage_6_TimeoutRate); + callback(PerformanceSummarySection_stage_6_TrialsValid); % Updating Disp Values for SessionPerformanceSection SessionPerformanceSection_ntrials.value = n_completed_trials; @@ -925,10 +925,10 @@ SessionPerformanceSection_timeout_recent.value = nan; SessionPerformanceSection_violation_recent.value = nan; end - SessionPerformanceSection_violation_stage.value = value(Training_Performance_Summary_stage_6_ViolationRate); - SessionPerformanceSection_ntrials_stage.value = value(Training_Performance_Summary_stage_6_Trials); - SessionPerformanceSection_ntrials_stage_today.value = value(Training_Performance_Summary_stage_6_TrialsToday); - SessionPerformanceSection_timeout_stage.value = value(Training_Performance_Summary_stage_6_TimeoutRate); + SessionPerformanceSection_violation_stage.value = value(PerformanceSummarySection_stage_6_ViolationRate); + SessionPerformanceSection_ntrials_stage.value = value(PerformanceSummarySection_stage_6_Trials); + SessionPerformanceSection_ntrials_stage_today.value = value(PerformanceSummarySection_stage_6_TrialsToday); + SessionPerformanceSection_timeout_stage.value = value(PerformanceSummarySection_stage_6_TimeoutRate); callback(SessionPerformanceSection_ntrials); callback(SessionPerformanceSection_ntrials_stage); @@ -950,9 +950,9 @@ % if ParamsSection_use_auto_train % do completion check if auto training stage_no = value(SessionDefinition_CURRENT_ACTIVE_STAGE); - if value(Training_Performance_Summary_stage_6_TrialsValid) > value(Training_ParamsSection_total_trials) - if value(SessionPerformanceSection_violation_recent) < value(Training_ParamsSection_recent_violation) && value(SessionPerformanceSection_timeout_recent) < value(Training_ParamsSection_recent_timeout) && ... - value(SessionPerformanceSection_violation_stage) < value(Training_ParamsSection_stage_violation) && n_completed_trials > 100 + if value(PerformanceSummarySection_stage_6_TrialsValid) > value(TrainingStageParamsSection_total_trials) + if value(SessionPerformanceSection_violation_recent) < value(TrainingStageParamsSection_recent_violation) && value(SessionPerformanceSection_timeout_recent) < value(TrainingStageParamsSection_recent_timeout) && ... + value(SessionPerformanceSection_violation_stage) < value(TrainingStageParamsSection_stage_violation) && n_completed_trials > 100 ParamsSection_training_stage.value = stage_no + 1; callback(ParamsSection_training_stage); ParamsSection(obj, 'Changed_Training_Stage'); @@ -974,22 +974,22 @@ ClearHelperVarsNotOwned(obj); % % Reset the number of trials done today for this stage -Training_Performance_Summary_stage_1_TrialsToday.value = 0; -callback(Training_Performance_Summary_stage_1_TrialsToday); -Training_Performance_Summary_stage_2_TrialsToday.value = 0; -callback(Training_Performance_Summary_stage_2_TrialsToday); -Training_Performance_Summary_stage_3_TrialsToday.value = 0; -callback(Training_Performance_Summary_stage_3_TrialsToday); -Training_Performance_Summary_stage_4_TrialsToday.value = 0; -callback(Training_Performance_Summary_stage_4_TrialsToday); -Training_Performance_Summary_stage_5_TrialsToday.value = 0; -callback(Training_Performance_Summary_stage_5_TrialsToday); -Training_Performance_Summary_stage_6_TrialsToday.value = 0; -callback(Training_Performance_Summary_stage_6_TrialsToday); -Training_Performance_Summary_stage_7_TrialsToday.value = 0; -callback(Training_Performance_Summary_stage_7_TrialsToday); -Training_Performance_Summary_stage_8_TrialsToday.value = 0; -callback(Training_Performance_Summary_stage_8_TrialsToday); +PerformanceSummarySection_stage_1_TrialsToday.value = 0; +callback(PerformanceSummarySection_stage_1_TrialsToday); +PerformanceSummarySection_stage_2_TrialsToday.value = 0; +callback(PerformanceSummarySection_stage_2_TrialsToday); +PerformanceSummarySection_stage_3_TrialsToday.value = 0; +callback(PerformanceSummarySection_stage_3_TrialsToday); +PerformanceSummarySection_stage_4_TrialsToday.value = 0; +callback(PerformanceSummarySection_stage_4_TrialsToday); +PerformanceSummarySection_stage_5_TrialsToday.value = 0; +callback(PerformanceSummarySection_stage_5_TrialsToday); +PerformanceSummarySection_stage_6_TrialsToday.value = 0; +callback(PerformanceSummarySection_stage_6_TrialsToday); +PerformanceSummarySection_stage_7_TrialsToday.value = 0; +callback(PerformanceSummarySection_stage_7_TrialsToday); +PerformanceSummarySection_stage_8_TrialsToday.value = 0; +callback(PerformanceSummarySection_stage_8_TrialsToday); % end % @@ -1015,20 +1015,20 @@ ClearHelperVarsNotOwned(obj); % % Variables for warmup stage -cp_max = value(Training_ParamsSection_max_CP); +cp_max = value(TrainingStageParamsSection_max_CP); % Starting total center poke duration: -starting_cp = value(Training_ParamsSection_starting_CP) + value(ParamsSection_SettlingIn_time); +starting_cp = value(TrainingStageParamsSection_starting_CP) + value(ParamsSection_SettlingIn_time); % number of warm-up trials -n_trial_warmup = value(Training_ParamsSection_warm_up_trials); -prestim_min = value(Training_ParamsSection_min_prestim); -prestim_max = value(Training_ParamsSection_max_prestim); -prestim_time = value(Training_ParamsSection_min_prestim); -a1_time = value(Training_ParamsSection_stim_dur); -a1_time_min = value(Training_ParamsSection_stim_dur); -a1_time_max = value(Training_ParamsSection_stim_dur + 0.1); -prego_min = value(Training_ParamsSection_min_prego); -prego_max = value(Training_ParamsSection_max_prego); -prego_time = value(Training_ParamsSection_min_prego); +n_trial_warmup = value(TrainingStageParamsSection_warm_up_trials); +prestim_min = value(TrainingStageParamsSection_min_prestim); +prestim_max = value(TrainingStageParamsSection_max_prestim); +prestim_time = value(TrainingStageParamsSection_min_prestim); +a1_time = value(TrainingStageParamsSection_stim_dur); +a1_time_min = value(TrainingStageParamsSection_stim_dur); +a1_time_max = value(TrainingStageParamsSection_stim_dur + 0.1); +prego_min = value(TrainingStageParamsSection_min_prego); +prego_max = value(TrainingStageParamsSection_max_prego); +prego_time = value(TrainingStageParamsSection_min_prego); warmup_completed = 0; warm_up_on = 1; random_prestim = 1; @@ -1083,19 +1083,19 @@ if n_completed_trials > 0 % Updating Disp Values for Training_Peformance_Summary - Training_Performance_Summary_stage_7_Trials.value = value(Training_Performance_Summary_stage_7_Trials) + 1; - Training_Performance_Summary_stage_7_TrialsToday.value = value(Training_Performance_Summary_stage_7_TrialsToday) + 1; - Training_Performance_Summary_stage_7_ViolationRate.value = ((value(Training_Performance_Summary_stage_7_ViolationRate) * (value(Training_Performance_Summary_stage_7_Trials) - 1)) + double(violation_history(end))) / value(Training_Performance_Summary_stage_7_Trials); - Training_Performance_Summary_stage_7_TimeoutRate.value = ((value(Training_Performance_Summary_stage_7_TimeoutRate) * (value(Training_Performance_Summary_stage_7_Trials) - 1)) + double(timeout_history(end))) / value(Training_Performance_Summary_stage_7_Trials); + PerformanceSummarySection_stage_7_Trials.value = value(PerformanceSummarySection_stage_7_Trials) + 1; + PerformanceSummarySection_stage_7_TrialsToday.value = value(PerformanceSummarySection_stage_7_TrialsToday) + 1; + PerformanceSummarySection_stage_7_ViolationRate.value = ((value(PerformanceSummarySection_stage_7_ViolationRate) * (value(PerformanceSummarySection_stage_7_Trials) - 1)) + double(violation_history(end))) / value(PerformanceSummarySection_stage_7_Trials); + PerformanceSummarySection_stage_7_TimeoutRate.value = ((value(PerformanceSummarySection_stage_7_TimeoutRate) * (value(PerformanceSummarySection_stage_7_Trials) - 1)) + double(timeout_history(end))) / value(PerformanceSummarySection_stage_7_Trials); if ~isnan(value(hit_history(end))) - Training_Performance_Summary_stage_7_TrialsValid.value = value(Training_Performance_Summary_stage_7_TrialsValid) + 1; + PerformanceSummarySection_stage_7_TrialsValid.value = value(PerformanceSummarySection_stage_7_TrialsValid) + 1; end - callback(Training_Performance_Summary_stage_7_Trials); - callback(Training_Performance_Summary_stage_7_TrialsToday) - callback(Training_Performance_Summary_stage_7_ViolationRate); - callback(Training_Performance_Summary_stage_7_TimeoutRate); - callback(Training_Performance_Summary_stage_7_TrialsValid); + callback(PerformanceSummarySection_stage_7_Trials); + callback(PerformanceSummarySection_stage_7_TrialsToday) + callback(PerformanceSummarySection_stage_7_ViolationRate); + callback(PerformanceSummarySection_stage_7_TimeoutRate); + callback(PerformanceSummarySection_stage_7_TrialsValid); % Updating Disp Values for SessionPerformanceSection SessionPerformanceSection_ntrials.value = n_completed_trials; @@ -1108,10 +1108,10 @@ SessionPerformanceSection_timeout_recent.value = nan; SessionPerformanceSection_violation_recent.value = nan; end - SessionPerformanceSection_violation_stage.value = value(Training_Performance_Summary_stage_7_ViolationRate); - SessionPerformanceSection_ntrials_stage.value = value(Training_Performance_Summary_stage_7_Trials); - SessionPerformanceSection_ntrials_stage_today.value = value(Training_Performance_Summary_stage_7_TrialsToday); - SessionPerformanceSection_timeout_stage.value = value(Training_Performance_Summary_stage_7_TimeoutRate); + SessionPerformanceSection_violation_stage.value = value(PerformanceSummarySection_stage_7_ViolationRate); + SessionPerformanceSection_ntrials_stage.value = value(PerformanceSummarySection_stage_7_Trials); + SessionPerformanceSection_ntrials_stage_today.value = value(PerformanceSummarySection_stage_7_TrialsToday); + SessionPerformanceSection_timeout_stage.value = value(PerformanceSummarySection_stage_7_TimeoutRate); callback(SessionPerformanceSection_ntrials); callback(SessionPerformanceSection_ntrials_stage); @@ -1136,9 +1136,9 @@ % stage_no = value(SessionDefinition_CURRENT_ACTIVE_STAGE); -if value(Training_Performance_Summary_stage_7_TrialsValid) > value(Training_ParamsSection_total_trials) - if value(SessionPerformanceSection_violation_recent) < value(Training_ParamsSection_recent_violation) && value(SessionPerformanceSection_timeout_recent) < value(Training_ParamsSection_recent_timeout) && ... - value(SessionPerformanceSection_violation_stage) < value(Training_ParamsSection_stage_violation) +if value(PerformanceSummarySection_stage_7_TrialsValid) > value(TrainingStageParamsSection_total_trials) + if value(SessionPerformanceSection_violation_recent) < value(TrainingStageParamsSection_recent_violation) && value(SessionPerformanceSection_timeout_recent) < value(TrainingStageParamsSection_recent_timeout) && ... + value(SessionPerformanceSection_violation_stage) < value(TrainingStageParamsSection_stage_violation) ParamsSection_training_stage.value = stage_no + 1; callback(ParamsSection_training_stage); ParamsSection(obj, 'Changed_Training_Stage'); @@ -1159,22 +1159,22 @@ GetSoloFunctionArgs(obj); ClearHelperVarsNotOwned(obj); % -Training_Performance_Summary_stage_1_TrialsToday.value = 0; -callback(Training_Performance_Summary_stage_1_TrialsToday); -Training_Performance_Summary_stage_2_TrialsToday.value = 0; -callback(Training_Performance_Summary_stage_2_TrialsToday); -Training_Performance_Summary_stage_3_TrialsToday.value = 0; -callback(Training_Performance_Summary_stage_3_TrialsToday); -Training_Performance_Summary_stage_4_TrialsToday.value = 0; -callback(Training_Performance_Summary_stage_4_TrialsToday); -Training_Performance_Summary_stage_5_TrialsToday.value = 0; -callback(Training_Performance_Summary_stage_5_TrialsToday); -Training_Performance_Summary_stage_6_TrialsToday.value = 0; -callback(Training_Performance_Summary_stage_6_TrialsToday); -Training_Performance_Summary_stage_7_TrialsToday.value = 0; -callback(Training_Performance_Summary_stage_7_TrialsToday); -Training_Performance_Summary_stage_8_TrialsToday.value = 0; -callback(Training_Performance_Summary_stage_8_TrialsToday); +PerformanceSummarySection_stage_1_TrialsToday.value = 0; +callback(PerformanceSummarySection_stage_1_TrialsToday); +PerformanceSummarySection_stage_2_TrialsToday.value = 0; +callback(PerformanceSummarySection_stage_2_TrialsToday); +PerformanceSummarySection_stage_3_TrialsToday.value = 0; +callback(PerformanceSummarySection_stage_3_TrialsToday); +PerformanceSummarySection_stage_4_TrialsToday.value = 0; +callback(PerformanceSummarySection_stage_4_TrialsToday); +PerformanceSummarySection_stage_5_TrialsToday.value = 0; +callback(PerformanceSummarySection_stage_5_TrialsToday); +PerformanceSummarySection_stage_6_TrialsToday.value = 0; +callback(PerformanceSummarySection_stage_6_TrialsToday); +PerformanceSummarySection_stage_7_TrialsToday.value = 0; +callback(PerformanceSummarySection_stage_7_TrialsToday); +PerformanceSummarySection_stage_8_TrialsToday.value = 0; +callback(PerformanceSummarySection_stage_8_TrialsToday); % end % @@ -1266,19 +1266,19 @@ if n_completed_trials > 0 % Updating Disp Values for Training_Peformance_Summary - Training_Performance_Summary_stage_8_Trials.value = value(Training_Performance_Summary_stage_8_Trials) + 1; - Training_Performance_Summary_stage_8_TrialsToday.value = value(Training_Performance_Summary_stage_8_TrialsToday) + 1; - Training_Performance_Summary_stage_8_ViolationRate.value = ((value(Training_Performance_Summary_stage_8_ViolationRate) * (value(Training_Performance_Summary_stage_8_Trials) - 1)) + double(violation_history(end))) / value(Training_Performance_Summary_stage_8_Trials); - Training_Performance_Summary_stage_8_TimeoutRate.value = ((value(Training_Performance_Summary_stage_8_TimeoutRate) * (value(Training_Performance_Summary_stage_8_Trials) - 1)) + double(timeout_history(end))) / value(Training_Performance_Summary_stage_8_Trials); + PerformanceSummarySection_stage_8_Trials.value = value(PerformanceSummarySection_stage_8_Trials) + 1; + PerformanceSummarySection_stage_8_TrialsToday.value = value(PerformanceSummarySection_stage_8_TrialsToday) + 1; + PerformanceSummarySection_stage_8_ViolationRate.value = ((value(PerformanceSummarySection_stage_8_ViolationRate) * (value(PerformanceSummarySection_stage_8_Trials) - 1)) + double(violation_history(end))) / value(PerformanceSummarySection_stage_8_Trials); + PerformanceSummarySection_stage_8_TimeoutRate.value = ((value(PerformanceSummarySection_stage_8_TimeoutRate) * (value(PerformanceSummarySection_stage_8_Trials) - 1)) + double(timeout_history(end))) / value(PerformanceSummarySection_stage_8_Trials); if ~isnan(value(hit_history(end))) - Training_Performance_Summary_stage_8_TrialsValid.value = value(Training_Performance_Summary_stage_8_TrialsValid) + 1; + PerformanceSummarySection_stage_8_TrialsValid.value = value(PerformanceSummarySection_stage_8_TrialsValid) + 1; end - callback(Training_Performance_Summary_stage_8_Trials); - callback(Training_Performance_Summary_stage_8_TrialsToday) - callback(Training_Performance_Summary_stage_8_ViolationRate); - callback(Training_Performance_Summary_stage_8_TimeoutRate); - callback(Training_Performance_Summary_stage_8_TrialsValid); + callback(PerformanceSummarySection_stage_8_Trials); + callback(PerformanceSummarySection_stage_8_TrialsToday) + callback(PerformanceSummarySection_stage_8_ViolationRate); + callback(PerformanceSummarySection_stage_8_TimeoutRate); + callback(PerformanceSummarySection_stage_8_TrialsValid); % Updating Disp Values for SessionPerformanceSection SessionPerformanceSection_ntrials.value = n_completed_trials; @@ -1291,10 +1291,10 @@ SessionPerformanceSection_timeout_recent.value = nan; SessionPerformanceSection_violation_recent.value = nan; end - SessionPerformanceSection_violation_stage.value = value(Training_Performance_Summary_stage_8_ViolationRate); - SessionPerformanceSection_ntrials_stage.value = value(Training_Performance_Summary_stage_8_Trials); - SessionPerformanceSection_ntrials_stage_today.value = value(Training_Performance_Summary_stage_8_TrialsToday); - SessionPerformanceSection_timeout_stage.value = value(Training_Performance_Summary_stage_8_TimeoutRate); + SessionPerformanceSection_violation_stage.value = value(PerformanceSummarySection_stage_8_ViolationRate); + SessionPerformanceSection_ntrials_stage.value = value(PerformanceSummarySection_stage_8_Trials); + SessionPerformanceSection_ntrials_stage_today.value = value(PerformanceSummarySection_stage_8_TrialsToday); + SessionPerformanceSection_timeout_stage.value = value(PerformanceSummarySection_stage_8_TimeoutRate); callback(SessionPerformanceSection_ntrials); callback(SessionPerformanceSection_ntrials_stage); @@ -1332,22 +1332,22 @@ GetSoloFunctionArgs(obj); ClearHelperVarsNotOwned(obj); % -Training_Performance_Summary_stage_1_TrialsToday.value = 0; -callback(Training_Performance_Summary_stage_1_TrialsToday); -Training_Performance_Summary_stage_2_TrialsToday.value = 0; -callback(Training_Performance_Summary_stage_2_TrialsToday); -Training_Performance_Summary_stage_3_TrialsToday.value = 0; -callback(Training_Performance_Summary_stage_3_TrialsToday); -Training_Performance_Summary_stage_4_TrialsToday.value = 0; -callback(Training_Performance_Summary_stage_4_TrialsToday); -Training_Performance_Summary_stage_5_TrialsToday.value = 0; -callback(Training_Performance_Summary_stage_5_TrialsToday); -Training_Performance_Summary_stage_6_TrialsToday.value = 0; -callback(Training_Performance_Summary_stage_6_TrialsToday); -Training_Performance_Summary_stage_7_TrialsToday.value = 0; -callback(Training_Performance_Summary_stage_7_TrialsToday); -Training_Performance_Summary_stage_8_TrialsToday.value = 0; -callback(Training_Performance_Summary_stage_8_TrialsToday); +PerformanceSummarySection_stage_1_TrialsToday.value = 0; +callback(PerformanceSummarySection_stage_1_TrialsToday); +PerformanceSummarySection_stage_2_TrialsToday.value = 0; +callback(PerformanceSummarySection_stage_2_TrialsToday); +PerformanceSummarySection_stage_3_TrialsToday.value = 0; +callback(PerformanceSummarySection_stage_3_TrialsToday); +PerformanceSummarySection_stage_4_TrialsToday.value = 0; +callback(PerformanceSummarySection_stage_4_TrialsToday); +PerformanceSummarySection_stage_5_TrialsToday.value = 0; +callback(PerformanceSummarySection_stage_5_TrialsToday); +PerformanceSummarySection_stage_6_TrialsToday.value = 0; +callback(PerformanceSummarySection_stage_6_TrialsToday); +PerformanceSummarySection_stage_7_TrialsToday.value = 0; +callback(PerformanceSummarySection_stage_7_TrialsToday); +PerformanceSummarySection_stage_8_TrialsToday.value = 0; +callback(PerformanceSummarySection_stage_8_TrialsToday); % end % diff --git a/Protocols/@ArpitCentrePokeTraining/ParamsSection.m b/Protocols/@ArpitCentrePokeTraining/ParamsSection.m index 81d27a05..d07ce3bf 100644 --- a/Protocols/@ArpitCentrePokeTraining/ParamsSection.m +++ b/Protocols/@ArpitCentrePokeTraining/ParamsSection.m @@ -173,10 +173,10 @@ {'training_stage';'stimuli_on';'ThisTrial';'A1_time';... 'time_bet_aud1_gocue' ;'PreStim_time'}); - SoloFunctionAddVars('Training_ParamsSection', 'ro_args', ... + SoloFunctionAddVars('TrainingStageParamsSection', 'ro_args', ... {'training_stage'}); - SoloFunctionAddVars('Training_Performance_Summary', 'ro_args', ... + SoloFunctionAddVars('PerformanceSummarySection', 'ro_args', ... {'training_stage'}); % SoloParamHandle(obj, 'previous_parameters', 'value', []); diff --git a/Protocols/@ArpitCentrePokeTraining/Training_Performance_Summary.m b/Protocols/@ArpitCentrePokeTraining/PerformanceSummarySection.m similarity index 98% rename from Protocols/@ArpitCentrePokeTraining/Training_Performance_Summary.m rename to Protocols/@ArpitCentrePokeTraining/PerformanceSummarySection.m index 01c3de6a..d30dea68 100644 --- a/Protocols/@ArpitCentrePokeTraining/Training_Performance_Summary.m +++ b/Protocols/@ArpitCentrePokeTraining/PerformanceSummarySection.m @@ -1,4 +1,4 @@ -function [x, y] = Training_Performance_Summary(obj, action, varargin) +function [x, y] = PerformanceSummarySection(obj, action, varargin) GetSoloFunctionArgs(obj); diff --git a/Protocols/@ArpitCentrePokeTraining/Training_ParamsSection.m b/Protocols/@ArpitCentrePokeTraining/TrainingStageParamsSection.m similarity index 99% rename from Protocols/@ArpitCentrePokeTraining/Training_ParamsSection.m rename to Protocols/@ArpitCentrePokeTraining/TrainingStageParamsSection.m index e6bf8204..222885b1 100644 --- a/Protocols/@ArpitCentrePokeTraining/Training_ParamsSection.m +++ b/Protocols/@ArpitCentrePokeTraining/TrainingStageParamsSection.m @@ -1,4 +1,4 @@ -function [x, y] = Training_ParamsSection(obj, action, x,y) +function [x, y] = TrainingStageParamsSection(obj, action, x,y) GetSoloFunctionArgs(obj); From 224c676888395dbb04f1970ea3ee131a745af75d Mon Sep 17 00:00:00 2001 From: Arpit Date: Thu, 8 May 2025 07:51:07 +0100 Subject: [PATCH 090/164] debug --- Protocols/@ArpitCentrePokeTraining/ParamsSection.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Protocols/@ArpitCentrePokeTraining/ParamsSection.m b/Protocols/@ArpitCentrePokeTraining/ParamsSection.m index d07ce3bf..ea3abc92 100644 --- a/Protocols/@ArpitCentrePokeTraining/ParamsSection.m +++ b/Protocols/@ArpitCentrePokeTraining/ParamsSection.m @@ -220,7 +220,7 @@ SessionDefinition(obj, 'jump_to_stage',value(training_stage)); end - [stage_fig_x,stage_fig_y] = Training_ParamsSection(obj, 'reinit', value(stage_fig_x),value(stage_fig_y)); % update the training params as well + [stage_fig_x,stage_fig_y] = TrainingStageParamsSection(obj, 'reinit', value(stage_fig_x),value(stage_fig_y)); % update the training params as well ArpitCentrePokeTrainingSMA(obj,'reinit'); SessionPerformanceSection(obj, 'evaluate'); From 1b06da4eeb750a135bc18cb7380c88c8205d97fd Mon Sep 17 00:00:00 2001 From: Arpit Date: Thu, 8 May 2025 16:39:08 +0100 Subject: [PATCH 091/164] error debug --- .../@bonsaicamera/BonsaiCameraInterface.m | 40 ++++++++++++++++--- .../ArpitCentrePokeTraining.m | 7 ++-- ...ing_SessionDefinition_AutoTrainingStages.m | 30 +++++++------- .../TrainingStageParamsSection.m | 10 ++--- 4 files changed, 58 insertions(+), 29 deletions(-) diff --git a/ExperPort/Plugins/@bonsaicamera/BonsaiCameraInterface.m b/ExperPort/Plugins/@bonsaicamera/BonsaiCameraInterface.m index 5dcbe829..3b18c626 100644 --- a/ExperPort/Plugins/@bonsaicamera/BonsaiCameraInterface.m +++ b/ExperPort/Plugins/@bonsaicamera/BonsaiCameraInterface.m @@ -126,17 +126,45 @@ switch length(varargin) case 2 - protocol_name = 'protocol_name'; - experimenter_name = 'experimenter'; - rat_name = 'ratname'; + protocol_name = lower(class(obj)); + experimenter_name_handle = get_sphandle('fullname', 'SavingSection_experimenter'); + if isempty(experimenter_name_handle) + experimenter_name = 'experimenter'; + else + experimenter_name = value(experimenter_name_handle{1}); + end + rat_name_handle = get_sphandle('fullname', 'SavingSection_ratname'); + if isempty(rat_name_handle) + rat_name = 'ratname'; + else + rat_name = value(rat_name_handle{1}); + end + case 3 protocol_name = varargin{3}; - experimenter_name = 'experimenter'; - rat_name = 'ratname'; + experimenter_name_handle = get_sphandle('fullname', 'SavingSection_experimenter'); + if isempty(experimenter_name_handle) + experimenter_name = 'experimenter'; + else + experimenter_name = value(experimenter_name_handle{1}); + end + rat_name_handle = get_sphandle('fullname', 'SavingSection_ratname'); + if isempty(rat_name_handle) + rat_name = 'ratname'; + else + rat_name = value(rat_name_handle{1}); + end + case 4 protocol_name = varargin{3}; experimenter_name = varargin{4}; - rat_name = 'ratname'; + rat_name_handle = get_sphandle('fullname', 'SavingSection_ratname'); + if isempty(rat_name_handle) + rat_name = 'ratname'; + else + rat_name = value(rat_name_handle{1}); + end + case 5 protocol_name = varargin{3}; experimenter_name = varargin{4}; diff --git a/Protocols/@ArpitCentrePokeTraining/ArpitCentrePokeTraining.m b/Protocols/@ArpitCentrePokeTraining/ArpitCentrePokeTraining.m index 4cf54b9a..ef90745a 100644 --- a/Protocols/@ArpitCentrePokeTraining/ArpitCentrePokeTraining.m +++ b/Protocols/@ArpitCentrePokeTraining/ArpitCentrePokeTraining.m @@ -243,9 +243,10 @@ prot_title.value=[mfilename ' on rig ' get_hostname ' : ' expmtr ', ' rname '. Started at ' datestr(now, 'HH:MM')]; end - % try - % send_n_done_trials(obj); - % end + try + send_n_done_trials(obj,'update'); + catch + end %% trial_completed case 'trial_completed' diff --git a/Protocols/@ArpitCentrePokeTraining/ArpitCentrePokeTraining_SessionDefinition_AutoTrainingStages.m b/Protocols/@ArpitCentrePokeTraining/ArpitCentrePokeTraining_SessionDefinition_AutoTrainingStages.m index 5713f46d..2477e038 100644 --- a/Protocols/@ArpitCentrePokeTraining/ArpitCentrePokeTraining_SessionDefinition_AutoTrainingStages.m +++ b/Protocols/@ArpitCentrePokeTraining/ArpitCentrePokeTraining_SessionDefinition_AutoTrainingStages.m @@ -47,8 +47,8 @@ end % Update TrainingStageParamsSection -if value(previous_sides(end)) ~= value(previous_sides(end-1)) && all(hit_history(end-1:end)) % last and present trials should also be a valid trial - TrainingStageParamsSection_trial_oppSide = value(TrainingStageParamsSection_trial_oppSide) + 1; % updating value for variable in TrainingParams_Section +if previous_sides(end) ~= previous_sides(end-1) && all(hit_history(end-1:end)) % last and present trials should also be a valid trial + TrainingStageParamsSection_trial_oppSide.value = value(TrainingStageParamsSection_trial_oppSide) + 1; % updating value for variable in TrainingParams_Section callback(TrainingStageParamsSection_trial_oppSide); end @@ -58,7 +58,7 @@ PerformanceSummarySection_stage_1_TrialsToday.value = value(PerformanceSummarySection_stage_1_TrialsToday) + 1; PerformanceSummarySection_stage_1_ViolationRate.value = nan; PerformanceSummarySection_stage_1_TimeoutRate.value = nan; - if ~isnan(value(hit_history(end))) + if ~isnan(hit_history(end)) PerformanceSummarySection_stage_1_TrialsValid.value = value(PerformanceSummarySection_stage_1_TrialsValid) + 1; end callback(PerformanceSummarySection_stage_1_Trials); @@ -187,13 +187,13 @@ end % Update the reward collection time based upon behav if length(timeout_history) > 5 - if all(value(timeout_history(end-1:end))) && value(this_stage_opp_side_trials) >= 2 + if all(timeout_history(end-1:end)) && value(this_stage_opp_side_trials) >= 2 ParamsSection_RewardCollection_duration.value = min([value(ParamsSection_RewardCollection_duration) + 1,... value(TrainingStageParamsSection_max_rColl_dur)]); callback(ParamsSection_RewardCollection_duration); this_stage_opp_side_trials.value = 0; end - if ~any(value(timeout_history(end-1:end))) && value(this_stage_opp_side_trials) >= 2 + if ~any(timeout_history(end-1:end)) && value(this_stage_opp_side_trials) >= 2 ParamsSection_RewardCollection_duration.value = max([value(ParamsSection_RewardCollection_duration) - 1,... value(TrainingStageParamsSection_min_rColl_dur)]); callback(ParamsSection_RewardCollection_duration); @@ -201,15 +201,15 @@ end end if length(timeout_history) > 20 - if all(value(timeout_history(end-19:end))) + if all(timeout_history(end-19:end)) ParamsSection_RewardCollection_duration.value = 120; callback(ParamsSection_RewardCollection_duration); end end % Update TrainingStageParamsSection -if value(previous_sides(end)) ~= value(previous_sides(end-1)) && all(hit_history(end-1:end)) % last and present trials should also be a valid trial - TrainingStageParamsSection_trial_oppSide = value(TrainingStageParamsSection_trial_oppSide) + 1; % updating value for variable in TrainingParams_Section +if previous_sides(end) ~= previous_sides(end-1) && all(hit_history(end-1:end)) % last and present trials should also be a valid trial + TrainingStageParamsSection_trial_oppSide.value = value(TrainingStageParamsSection_trial_oppSide) + 1; % updating value for variable in TrainingParams_Section callback(TrainingStageParamsSection_trial_oppSide); end @@ -220,7 +220,7 @@ PerformanceSummarySection_stage_2_TrialsToday.value = value(PerformanceSummarySection_stage_2_TrialsToday) + 1; PerformanceSummarySection_stage_2_ViolationRate.value = nan; PerformanceSummarySection_stage_2_TimeoutRate.value = ((value(PerformanceSummarySection_stage_2_TimeoutRate) * (value(PerformanceSummarySection_stage_2_Trials) - 1)) + double(timeout_history(end))) / value(PerformanceSummarySection_stage_2_Trials); - if ~isnan(value(hit_history(end))) + if ~isnan(hit_history(end)) PerformanceSummarySection_stage_2_TrialsValid.value = value(PerformanceSummarySection_stage_2_TrialsValid) + 1; end @@ -368,7 +368,7 @@ PerformanceSummarySection_stage_3_TrialsToday.value = value(PerformanceSummarySection_stage_3_TrialsToday) + 1; PerformanceSummarySection_stage_3_ViolationRate.value = nan; PerformanceSummarySection_stage_3_TimeoutRate.value = ((value(PerformanceSummarySection_stage_3_TimeoutRate) * (value(PerformanceSummarySection_stage_3_Trials) - 1)) + double(timeout_history(end))) / value(PerformanceSummarySection_stage_3_Trials); - if ~isnan(value(hit_history(end))) + if ~isnan(hit_history(end)) PerformanceSummarySection_stage_3_TrialsValid.value = value(PerformanceSummarySection_stage_3_TrialsValid) + 1; end @@ -524,7 +524,7 @@ PerformanceSummarySection_stage_4_TrialsToday.value = value(PerformanceSummarySection_stage_4_TrialsToday) + 1; PerformanceSummarySection_stage_4_ViolationRate.value = ((value(PerformanceSummarySection_stage_4_ViolationRate) * (value(PerformanceSummarySection_stage_4_Trials) - 1)) + double(violation_history(end))) / value(PerformanceSummarySection_stage_4_Trials); PerformanceSummarySection_stage_4_TimeoutRate.value = ((value(PerformanceSummarySection_stage_4_TimeoutRate) * (value(PerformanceSummarySection_stage_4_Trials) - 1)) + double(timeout_history(end))) / value(PerformanceSummarySection_stage_4_Trials); - if ~isnan(value(hit_history(end))) + if ~isnan(hit_history(end)) PerformanceSummarySection_stage_4_TrialsValid.value = value(PerformanceSummarySection_stage_4_TrialsValid) + 1; end @@ -723,7 +723,7 @@ PerformanceSummarySection_stage_5_TrialsToday.value = value(PerformanceSummarySection_stage_5_TrialsToday) + 1; PerformanceSummarySection_stage_5_ViolationRate.value = ((value(PerformanceSummarySection_stage_5_ViolationRate) * (value(PerformanceSummarySection_stage_5_Trials) - 1)) + double(violation_history(end))) / value(PerformanceSummarySection_stage_5_Trials); PerformanceSummarySection_stage_5_TimeoutRate.value = ((value(PerformanceSummarySection_stage_5_TimeoutRate) * (value(PerformanceSummarySection_stage_5_Trials) - 1)) + double(timeout_history(end))) / value(PerformanceSummarySection_stage_5_Trials); - if ~isnan(value(hit_history(end))) + if ~isnan(hit_history(end)) PerformanceSummarySection_stage_5_TrialsValid.value = value(PerformanceSummarySection_stage_5_TrialsValid) + 1; end @@ -904,7 +904,7 @@ PerformanceSummarySection_stage_6_TrialsToday.value = value(PerformanceSummarySection_stage_6_TrialsToday) + 1; PerformanceSummarySection_stage_6_ViolationRate.value = ((value(PerformanceSummarySection_stage_6_ViolationRate) * (value(PerformanceSummarySection_stage_6_Trials) - 1)) + double(violation_history(end))) / value(PerformanceSummarySection_stage_6_Trials); PerformanceSummarySection_stage_6_TimeoutRate.value = ((value(PerformanceSummarySection_stage_6_TimeoutRate) * (value(PerformanceSummarySection_stage_6_Trials) - 1)) + double(timeout_history(end))) / value(PerformanceSummarySection_stage_6_Trials); - if ~isnan(value(hit_history(end))) + if ~isnan(hit_history(end)) PerformanceSummarySection_stage_6_TrialsValid.value = value(PerformanceSummarySection_stage_6_TrialsValid) + 1; end @@ -1087,7 +1087,7 @@ PerformanceSummarySection_stage_7_TrialsToday.value = value(PerformanceSummarySection_stage_7_TrialsToday) + 1; PerformanceSummarySection_stage_7_ViolationRate.value = ((value(PerformanceSummarySection_stage_7_ViolationRate) * (value(PerformanceSummarySection_stage_7_Trials) - 1)) + double(violation_history(end))) / value(PerformanceSummarySection_stage_7_Trials); PerformanceSummarySection_stage_7_TimeoutRate.value = ((value(PerformanceSummarySection_stage_7_TimeoutRate) * (value(PerformanceSummarySection_stage_7_Trials) - 1)) + double(timeout_history(end))) / value(PerformanceSummarySection_stage_7_Trials); - if ~isnan(value(hit_history(end))) + if ~isnan(hit_history(end)) PerformanceSummarySection_stage_7_TrialsValid.value = value(PerformanceSummarySection_stage_7_TrialsValid) + 1; end @@ -1270,7 +1270,7 @@ PerformanceSummarySection_stage_8_TrialsToday.value = value(PerformanceSummarySection_stage_8_TrialsToday) + 1; PerformanceSummarySection_stage_8_ViolationRate.value = ((value(PerformanceSummarySection_stage_8_ViolationRate) * (value(PerformanceSummarySection_stage_8_Trials) - 1)) + double(violation_history(end))) / value(PerformanceSummarySection_stage_8_Trials); PerformanceSummarySection_stage_8_TimeoutRate.value = ((value(PerformanceSummarySection_stage_8_TimeoutRate) * (value(PerformanceSummarySection_stage_8_Trials) - 1)) + double(timeout_history(end))) / value(PerformanceSummarySection_stage_8_Trials); - if ~isnan(value(hit_history(end))) + if ~isnan(hit_history(end)) PerformanceSummarySection_stage_8_TrialsValid.value = value(PerformanceSummarySection_stage_8_TrialsValid) + 1; end diff --git a/Protocols/@ArpitCentrePokeTraining/TrainingStageParamsSection.m b/Protocols/@ArpitCentrePokeTraining/TrainingStageParamsSection.m index 222885b1..c830d983 100644 --- a/Protocols/@ArpitCentrePokeTraining/TrainingStageParamsSection.m +++ b/Protocols/@ArpitCentrePokeTraining/TrainingStageParamsSection.m @@ -38,14 +38,14 @@ case 3 % no completion test required % STAGE RUNNING PARAMETERS - DispParam(obj, 'last_session_CP', 0, x, y,'save_with_settings', 1,'TooltipString','total cp reached by last session'); next_row(y); + NumeditParam(obj, 'last_session_CP', 0, x, y,'TooltipString','total cp reached by last session'); next_row(y); DispParam(obj, 'max_CP', 0.3, x, y,'label','CP_Dur_Max','TooltipString','max CP duration being trained in this stage'); next_row(y); NumeditParam(obj, 'CPfraction_inc', 0.001, x, y,'label','CP_frac_Increase','TooltipString','CP duration is increased by this fraction'); next_row(y); SubheaderParam(obj, 'title', 'Stage Params', x, y); next_row(y); case 4 % STAGE RUNNING PARAMETERS - DispParam(obj, 'last_session_CP', 0, x, y,'save_with_settings', 1,'TooltipString','total cp reached by last session'); next_row(y); + NumeditParam(obj, 'last_session_CP', 0, x, y,'TooltipString','total cp reached by last session'); next_row(y); NumeditParam(obj, 'max_CP', 1.5, x, y,'label','CP_Dur_Max','TooltipString','max CP duration being trained in this stage'); next_row(y); NumeditParam(obj, 'CPfraction_inc', 0.001, x, y,'label','CP_frac_Increase','TooltipString','CP duration is increased by this fraction'); next_row(y); SubheaderParam(obj, 'title', 'Stage Params', x, y); next_row(y); @@ -57,7 +57,7 @@ case 5 % STAGE RUNNING PARAMETERS - DispParam(obj, 'last_session_CP', 0, x, y,'save_with_settings', 1,'TooltipString','total cp reached by last session'); next_row(y); + NumeditParam(obj, 'last_session_CP', 0, x, y,'TooltipString','total cp reached by last session'); next_row(y); NumeditParam(obj, 'max_CP', 5, x, y,'label','CP_Dur_Max','TooltipString','max CP duration being trained in this stage'); next_row(y); NumeditParam(obj, 'CPfraction_inc', 0.002, x, y,'label','CP_frac_Increase','TooltipString','CP duration is increased by this fraction'); next_row(y); NumeditParam(obj, 'min_CP', 1.5, x, y,'label','CP_Dur_Min','TooltipString','min CP duration being trained in this stage'); next_row(y); @@ -74,7 +74,7 @@ case 6 % STAGE RUNNING PARAMETERS - DispParam(obj, 'last_session_CP', 0, x, y,'save_with_settings', 1,'TooltipString','total cp reached by last session'); next_row(y); + NumeditParam(obj, 'last_session_CP', 0, x, y,'TooltipString','total cp reached by last session'); next_row(y); NumeditParam(obj, 'max_CP', 5, x, y,'label','CP_Dur_Max','TooltipString','max CP duration being trained in this stage'); next_row(y); NumeditParam(obj, 'starting_CP', 0.3, x, y,'TooltipString','min CP duration (minus the settling-in time) during warm up'); next_row(y); NumeditParam(obj, 'warm_up_trials', 20, x, y,'label','Warmup Trials','TooltipString','N trials for warmup'); next_row(y); @@ -91,7 +91,7 @@ case 7 % STAGE RUNNING PARAMETERS - DispParam(obj, 'last_session_CP', 0, x, y,'save_with_settings', 1,'TooltipString','total cp reached by last session'); next_row(y); + NumeditParam(obj, 'last_session_CP', 0, x, y,'TooltipString','total cp reached by last session'); next_row(y); NumeditParam(obj, 'max_CP', 5, x, y,'label','CP_Dur_Max','TooltipString','max CP duration being trained in this stage'); next_row(y); NumeditParam(obj, 'starting_CP', 0.3, x, y,'TooltipString','min CP duration (minus the settling-in time) during warm up'); next_row(y); NumeditParam(obj, 'warm_up_trials', 20, x, y,'label','Warmup Trials','TooltipString','N trials for warmup'); next_row(y); From e3cac6e5d3b8a13ee1e78a7de65e168058849a47 Mon Sep 17 00:00:00 2001 From: Arpit Date: Thu, 8 May 2025 17:19:53 +0100 Subject: [PATCH 092/164] debug session table entry --- .../ArpitCentrePokeTraining.m | 26 ++++++++++--------- ...ing_SessionDefinition_AutoTrainingStages.m | 16 +++++++----- 2 files changed, 24 insertions(+), 18 deletions(-) diff --git a/Protocols/@ArpitCentrePokeTraining/ArpitCentrePokeTraining.m b/Protocols/@ArpitCentrePokeTraining/ArpitCentrePokeTraining.m index ef90745a..a3f216d6 100644 --- a/Protocols/@ArpitCentrePokeTraining/ArpitCentrePokeTraining.m +++ b/Protocols/@ArpitCentrePokeTraining/ArpitCentrePokeTraining.m @@ -289,10 +289,20 @@ StimulusSection(obj,'hide'); SessionDefinition(obj, 'run_eod_logic_without_saving'); - % perf = SessionPerformanceSection(obj, 'evaluate'); - cp_durs = ParamsSection(obj, 'get_cp_history'); + + % Sending protocol data as structure is causing error in saving bdata, + % instead not sending it asif required can be taken from session data + sendsummary(obj); - [stim1dur] = ParamsSection(obj,'get_stimdur_history'); + % perf = SessionPerformanceSection(obj, 'evaluate'); + % cp_durs = ParamsSection(obj, 'get_cp_history'); + % [stim1dur] = ParamsSection(obj,'get_stimdur_history'); + % pd.hits=hit_history(:); + % pd.sides=previous_sides(:); + % pd.viols=violation_history(:); + % pd.timeouts=timeout_history(:); + % pd.cp_durs=cp_durs(:); + % pd.stim1dur=stim1dur(:); % CommentsSection(obj, 'append_line', ... % sprintf(['ntrials = %d, violations = %.2f, timeouts=%.2f, hits = %.2f\n', ... @@ -300,15 +310,7 @@ % 'Low = %.2f, High = %.2f'], ... % perf(1), perf(2), perf(3), perf(6), cp_durs(1), cp_durs(end), cp_durs(end)-cp_durs(1), classperf(1),classperf(2))); - pd.hits=hit_history(:); - pd.sides=previous_sides(:); - pd.viols=violation_history(:); - pd.timeouts=timeout_history(:); - pd.cp_durs=cp_durs(:); - pd.stim1dur=stim1dur(:); - - - sendsummary(obj,'protocol_data',pd); + % sendsummary(obj,'protocol_data',pd); %% otherwise otherwise diff --git a/Protocols/@ArpitCentrePokeTraining/ArpitCentrePokeTraining_SessionDefinition_AutoTrainingStages.m b/Protocols/@ArpitCentrePokeTraining/ArpitCentrePokeTraining_SessionDefinition_AutoTrainingStages.m index 2477e038..c18ab4b5 100644 --- a/Protocols/@ArpitCentrePokeTraining/ArpitCentrePokeTraining_SessionDefinition_AutoTrainingStages.m +++ b/Protocols/@ArpitCentrePokeTraining/ArpitCentrePokeTraining_SessionDefinition_AutoTrainingStages.m @@ -47,9 +47,11 @@ end % Update TrainingStageParamsSection -if previous_sides(end) ~= previous_sides(end-1) && all(hit_history(end-1:end)) % last and present trials should also be a valid trial - TrainingStageParamsSection_trial_oppSide.value = value(TrainingStageParamsSection_trial_oppSide) + 1; % updating value for variable in TrainingParams_Section - callback(TrainingStageParamsSection_trial_oppSide); +if n_completed_trials >= 2 + if previous_sides(end) ~= previous_sides(end-1) && all(hit_history(end-1:end)) % last and present trials should also be a valid trial + TrainingStageParamsSection_trial_oppSide.value = value(TrainingStageParamsSection_trial_oppSide) + 1; % updating value for variable in TrainingParams_Section + callback(TrainingStageParamsSection_trial_oppSide); + end end % Updating Disp Values for Training_Peformance_Summary @@ -208,9 +210,11 @@ end % Update TrainingStageParamsSection -if previous_sides(end) ~= previous_sides(end-1) && all(hit_history(end-1:end)) % last and present trials should also be a valid trial - TrainingStageParamsSection_trial_oppSide.value = value(TrainingStageParamsSection_trial_oppSide) + 1; % updating value for variable in TrainingParams_Section - callback(TrainingStageParamsSection_trial_oppSide); +if n_completed_trials >= 2 + if previous_sides(end) ~= previous_sides(end-1) && all(hit_history(end-1:end)) % last and present trials should also be a valid trial + TrainingStageParamsSection_trial_oppSide.value = value(TrainingStageParamsSection_trial_oppSide) + 1; % updating value for variable in TrainingParams_Section + callback(TrainingStageParamsSection_trial_oppSide); + end end From 55836b64b084ddb0db8f3f7b1e0732877638ce3f Mon Sep 17 00:00:00 2001 From: Arpit Date: Thu, 8 May 2025 17:33:41 +0100 Subject: [PATCH 093/164] debug --- .../@ArpitCentrePokeTraining/TrainingStageParamsSection.m | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Protocols/@ArpitCentrePokeTraining/TrainingStageParamsSection.m b/Protocols/@ArpitCentrePokeTraining/TrainingStageParamsSection.m index c830d983..37dc4338 100644 --- a/Protocols/@ArpitCentrePokeTraining/TrainingStageParamsSection.m +++ b/Protocols/@ArpitCentrePokeTraining/TrainingStageParamsSection.m @@ -17,7 +17,7 @@ case 1 % STAGE RUNNING PARAMETERS - DispParam(obj, 'trial_oppSide', 0, x, y,'save_with_settings', 1,'TooltipString','total trials in opposide side this stage'); next_row(y); + NumeditParam(obj, 'trial_oppSide', 0, x, y,'TooltipString','total trials in opposide side this stage'); next_row(y); SubheaderParam(obj, 'title', 'Stage Params', x, y); % next_row(y); % COMPLETION TEST PARAMETERS @@ -27,7 +27,7 @@ case 2 % STAGE RUNNING PARAMETERS - DispParam(obj, 'trial_oppSide', 0, x, y,'save_with_settings', 1,'TooltipString','total trials in opposide side this stage'); next_row(y); + NumeditParam(obj, 'trial_oppSide', 0, x, y,'TooltipString','total trials in opposide side this stage'); next_row(y); NumeditParam(obj, 'max_rColl_dur', 300, x, y,'label','T_Max_RCollect','TooltipString','User given max water collect time(code will optimize for value below this)'); next_row(y); NumeditParam(obj, 'min_rColl_dur', 100, x, y,'label','T_Min_RCollect','TooltipString','User given min water collect time(code will optimize for value above this)'); next_row(y); SubheaderParam(obj, 'title', 'Stage Params', x, y); next_row(y); From 0f40deb49e3d3c0a20ca62fa88aa4fadc0c8eeb3 Mon Sep 17 00:00:00 2001 From: Arpit Date: Fri, 9 May 2025 12:50:43 +0100 Subject: [PATCH 094/164] edited to remove database error --- .../ArpitCentrePokeTraining.m | 21 +++++++------------ 1 file changed, 7 insertions(+), 14 deletions(-) diff --git a/Protocols/@ArpitCentrePokeTraining/ArpitCentrePokeTraining.m b/Protocols/@ArpitCentrePokeTraining/ArpitCentrePokeTraining.m index a3f216d6..d7bc822e 100644 --- a/Protocols/@ArpitCentrePokeTraining/ArpitCentrePokeTraining.m +++ b/Protocols/@ArpitCentrePokeTraining/ArpitCentrePokeTraining.m @@ -227,7 +227,7 @@ StimulusSection(obj,'prepare_next_trial'); SoundManagerSection(obj, 'send_not_yet_uploaded_sounds'); - [sma, prepare_next_trial_states] = ArpitCentrePokeTrainingSMA(obj, 'prepare_next_trial'); + [~, ~] = ArpitCentrePokeTrainingSMA(obj, 'prepare_next_trial'); % Default behavior of following call is that every 20 trials, the data % gets saved, not interactive, no commit to CVS. @@ -238,34 +238,27 @@ CommentsSection(obj, 'append_date'); CommentsSection(obj, 'append_line', ''); end - if n_done_trials==1 - [expmtr, rname]=SavingSection(obj, 'get_info'); - prot_title.value=[mfilename ' on rig ' get_hostname ' : ' expmtr ', ' rname '. Started at ' datestr(now, 'HH:MM')]; - end - - try - send_n_done_trials(obj,'update'); - catch - end - %% trial_completed case 'trial_completed' % Change the video trial BonsaiCameraInterface(obj,'next_trial'); - % Update the Metrics Calculated, Instead being Calculated in Session - % Definition and commented out + % Update the Metrics Calculated, Instead being Calculated in SessionDefinition and commented out % PerformanceSummarySection(obj,'evaluate'); % SessionPerformanceSection(obj, 'evaluate'); + % Do any updates in the protocol that need doing: feval(mfilename, 'update'); %% update case 'update' % PokesPlotSection(obj, 'update'); - + if n_done_trials==1 + [expmtr, rname]=SavingSection(obj, 'get_info'); + prot_title.value=[mfilename ' on rig ' get_hostname ' : ' expmtr ', ' rname '. Started at ' datestr(now, 'HH:MM')]; + end %% close case 'close' From e7e24fa5ac3a3ea13e99344e7bd2e9f6890670f9 Mon Sep 17 00:00:00 2001 From: Arpit Date: Fri, 9 May 2025 13:53:49 +0100 Subject: [PATCH 095/164] added description to saving section --- ExperPort/Plugins/@saveload/SavingSection.m | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/ExperPort/Plugins/@saveload/SavingSection.m b/ExperPort/Plugins/@saveload/SavingSection.m index 1a6efb43..cec0a615 100644 --- a/ExperPort/Plugins/@saveload/SavingSection.m +++ b/ExperPort/Plugins/@saveload/SavingSection.m @@ -88,8 +88,11 @@ SoloParamHandle(obj, 'my_gui_info', 'value', [x y double(gcf)]); SoloParamHandle(obj, 'data_file', 'value', ''); - %Sundeep Tuteja, 22nd December, 2009: Adding a SoloParamHandle called - %settings_file to store the full path to the currently loaded settings file. + %Arpit, 09th May, 2025: Getting the experimenter name and rat name + %from runrats instead of initializing it. This is required to create + %the folder to save video files. If it runss into error or runrat is + %not running then as a default it sets values as 'experimenter and + %'ratname' try [~,experimenter_name, rat_name] = runrats('exp_rat_names'); [~, settings_file_str] = runrats('get_settings_file_path'); From 60591e28b451cc1ffd2053a25a12f5305c716e16 Mon Sep 17 00:00:00 2001 From: Arpit Date: Fri, 9 May 2025 15:39:33 +0100 Subject: [PATCH 096/164] added code to load setting file during intialization --- .../ArpitCentrePokeTraining.m | 16 ++++++++++++++++ ...aining_SessionDefinition_AutoTrainingStages.m | 6 +++++- .../@ArpitCentrePokeTraining/ParamsSection.m | 4 ++-- 3 files changed, 23 insertions(+), 3 deletions(-) diff --git a/Protocols/@ArpitCentrePokeTraining/ArpitCentrePokeTraining.m b/Protocols/@ArpitCentrePokeTraining/ArpitCentrePokeTraining.m index d7bc822e..d13d91dd 100644 --- a/Protocols/@ArpitCentrePokeTraining/ArpitCentrePokeTraining.m +++ b/Protocols/@ArpitCentrePokeTraining/ArpitCentrePokeTraining.m @@ -201,6 +201,22 @@ x=oldx; y=oldy; SessionDefinition(obj, 'init', x, y, value(myfig)); %#ok + %%% + +% Problem: Loading settings in later stages fails because the stage-dependent +% TrainingStageParamsSection's solohandles are not visible after initial setup. +% Solution: This section handles a specific scenario for loading setting files during initialization. +% Although also invoked in Runrats, calling it here is crucial. The +% TrainingStageParamsSection's parameters are initially set at stage 1 and are visible. +% However, they become stage-dependent. Subsequent stages lack visible solohandles for +% these parameters, preventing proper loading. This initialization provides a workaround +% to avoid significant changes required to load stage-specific solohandles and maintain +% broader compatibility. + try + [~, ~]=load_solouiparamvalues(rname,'experimenter',expmtr,... + 'owner',name,'interactive',0); + catch + end %% % feval(mfilename, obj, 'prepare_next_trial'); % Commented out because it is also run by Runrats(while loading the protocol) diff --git a/Protocols/@ArpitCentrePokeTraining/ArpitCentrePokeTraining_SessionDefinition_AutoTrainingStages.m b/Protocols/@ArpitCentrePokeTraining/ArpitCentrePokeTraining_SessionDefinition_AutoTrainingStages.m index c18ab4b5..b7d307dd 100644 --- a/Protocols/@ArpitCentrePokeTraining/ArpitCentrePokeTraining_SessionDefinition_AutoTrainingStages.m +++ b/Protocols/@ArpitCentrePokeTraining/ArpitCentrePokeTraining_SessionDefinition_AutoTrainingStages.m @@ -355,6 +355,8 @@ if n_completed_trials < 1 % intialize to min value at the start of each session/day ParamsSection_CP_duration.value = value(ParamsSection_init_CP_duration); +elseif n_completed_trials == 1 + ParamsSection_CP_duration.value = value(TrainingStageParamsSection_last_session_CP); else if ~timeout_history(end) && value(ParamsSection_CP_duration) < cp_max increment = value(ParamsSection_CP_duration) * value(TrainingStageParamsSection_CPfraction_inc); @@ -505,8 +507,10 @@ ParamsSection_CP_duration.value = cp_min; % initialize to min_CP end -if n_completed_trials < 1 +if n_completed_trials == 0 ParamsSection_CP_duration.value = value(ParamsSection_init_CP_duration); +elseif n_completed_trials == 1 + ParamsSection_CP_duration.value = value(TrainingStageParamsSection_last_session_CP); else if ~violation_history(end) && ~timeout_history(end) && value(ParamsSection_CP_duration) < cp_max increment = value(ParamsSection_CP_duration) * cp_fraction; diff --git a/Protocols/@ArpitCentrePokeTraining/ParamsSection.m b/Protocols/@ArpitCentrePokeTraining/ParamsSection.m index ea3abc92..8c3c6ab4 100644 --- a/Protocols/@ArpitCentrePokeTraining/ParamsSection.m +++ b/Protocols/@ArpitCentrePokeTraining/ParamsSection.m @@ -32,9 +32,9 @@ next_row(y); NumeditParam(obj, 'drink_time', 1, x,y,'label','Drink Time','TooltipString','waits to finish water delivery'); next_row(y); - NumeditParam(obj, 'timeout_iti', 1, x,y,'label','No Choice Timeout','TooltipString','ITI on timeout trials'); + NumeditParam(obj, 'timeout_iti', 1, x,y,'label','Timeout ITI','TooltipString','ITI on timeout trials'); next_row(y); - NumeditParam(obj, 'violation_iti', 1, x,y,'label','Violation Timeout','TooltipString','Center poke violation duration'); + NumeditParam(obj, 'violation_iti', 1, x,y,'label','Violation ITI','TooltipString','Center poke violation duration'); % Reward Collection next_row(y); NumeditParam(obj, 'RewardCollection_duration', 300, x,y,'label','RewardCollection_dur','TooltipString','Wait until rat collects the reward else a timeout'); From 5e35e0ebb113e237b446db3c6d30de79edb14b22 Mon Sep 17 00:00:00 2001 From: Arpit Date: Mon, 12 May 2025 23:07:23 +0100 Subject: [PATCH 097/164] fixing email sending functionality in runrats --- ExperPort/Modules/@runrats/runrats.m | 85 ++++++++++++++++------------ 1 file changed, 48 insertions(+), 37 deletions(-) diff --git a/ExperPort/Modules/@runrats/runrats.m b/ExperPort/Modules/@runrats/runrats.m index 40873be6..25ed6517 100644 --- a/ExperPort/Modules/@runrats/runrats.m +++ b/ExperPort/Modules/@runrats/runrats.m @@ -146,9 +146,9 @@ SoloParamHandle(obj,'schedDay','value',[]); % For Live Webcam Feed - SoloParamHandle(obj,'Camera_Fig_window','value',[]); - SoloParamHandle(obj,'Camera_Obj','value',[]); - SoloParamHandle(obj,'Camera_Image','value',[]); + % SoloParamHandle(obj,'Camera_Fig_window','value',[]); + % SoloParamHandle(obj,'Camera_Obj','value',[]); + % SoloParamHandle(obj,'Camera_Image','value',[]); %Let's make the menus try @@ -1705,7 +1705,7 @@ %setpref('Internet','SMTP_Server','brodyfs2.princeton.edu'); %setpref('Internet','E_mail',['RunRats',datestr(now,'yymm'),'@Princeton.EDU']); - set_email_sender + % set_email_sender owner = bdata(['select contact from rats where ratname="',value(RatMenu),'"']); if ~isempty(owner) @@ -1716,7 +1716,8 @@ for i = 1:length(cms)-1 exp = owner(cms(i)+1:cms(i+1)-1); - sendmail([exp,'@princeton.edu'],[value(RatMenu),' Crashed'],message); + % sendmail([exp,'@ucl.ac.uk'],[value(RatMenu),' Crashed'],message); + gmail_SMTP([exp,'@ucl.ac.uk'],[value(RatMenu),' Crashed'],message); end end end @@ -1772,7 +1773,7 @@ try %#ok %setpref('Internet','SMTP_Server','brodyfs2.princeton.edu'); %setpref('Internet','E_mail',['RunRats',datestr(now,'yymm'),'@Princeton.EDU']); - set_email_sender + % set_email_sender message{1} = ['Load failed for ',value(RatMenu)]; %#ok if strcmp(varargin{1},'no settings') @@ -1800,7 +1801,8 @@ cms = find(contact == ','); for i = 1:length(cms)-1 email = contact(cms(i)+1:cms(i+1)-1); - sendmail([email,'@princeton.edu'],subject,message); + % sendmail([email,'@princeton.edu'],subject,message); + gmail_SMTP([email,'@ucl.ac.uk'],subject,message); end end end @@ -1808,7 +1810,7 @@ - case 'is_running', + case 'is_running' %% is_running %If the Multi button exists, runrats has been loaded if exist('Multi','var'), obj = 1; else obj = 0; end @@ -1834,30 +1836,7 @@ else varargout{1} = ''; end - - - case 'send_empty_state_machine' - %% send_empty_state_machine - %Sends an empty state matrix. This allows us to toggle DIO lines - - state_machine_server = bSettings('get','RIGS','state_machine_server'); - - server_slot = bSettings('get','RIGS','server_slot'); - if isnan(server_slot); server_slot = 0; end - - card_slot = bSettings('get', 'RIGS', 'card_slot'); - if isnan(card_slot); card_slot = 0; end - - sm = BPodSM(state_machine_server, 3333,server_slot); - sm = Initialize(sm); - - [inL outL] = MachinesSection(dispatcher,'determine_io_maps'); - - sma = StateMachineAssembler('full_trial_structure'); - sma = add_state(sma,'name','vapid_state_in_vapid_matrix'); - - send(sma,sm,'run_trial_asap',0,'input_lines',inL,'dout_lines',outL,'sound_card_slot', int2str(card_slot)); - + case 'close' %% close @@ -1889,7 +1868,7 @@ otherwise warning('Unknown action " %s" !', action);%#ok -end; +end return; @@ -1919,7 +1898,7 @@ setdate{xi}=r(1:7); %#ok else % not a file we want, give it a really early date, 2000: setdate{xi}='000101a'; %#ok - end; + end end [srtdsets, sdi]=sort(setdate); @@ -1958,7 +1937,7 @@ function update_folder(pname,vn) if failed1 == 1 || failed2 == 1 %setpref('Internet','SMTP_Server','brodyfs2.princeton.edu'); %setpref('Internet','E_mail',['RunRats',datestr(now,'yymm'),'@Princeton.EDU']); - set_email_sender + % set_email_sender if pname(1) ~= filesep; pname = [filesep,pname]; end if pname(end) ~= filesep; pname = [pname,filesep]; end @@ -1993,7 +1972,8 @@ function update_folder(pname,vn) cms = find(ctemp == ','); for i = 1:length(cms)-1 email = ctemp(cms(i)+1:cms(i+1)-1); - sendmail([email,'@princeton.edu'],['SVN Cleanup FAILED on Rig ',rig],message); + % sendmail([email,'@princeton.edu'],['SVN Cleanup FAILED on Rig ',rig],message); + gmail_SMTP([email,'@ucl.ac.uk'],['SVN Cleanup FAILED on Rig ',rig],message); end end @@ -2019,7 +1999,8 @@ function update_folder(pname,vn) cms = find(ctemp == ','); for i = 1:length(cms)-1 email = ctemp(cms(i)+1:cms(i+1)-1); - sendmail([email,'@ucl.ac.uk'],subject,message); + % sendmail([email,'@ucl.ac.uk'],subject,message); % + gmail_SMTP([email,'@ucl.ac.uk'],subject,message); % Using gmail instead of brody smtp end end end @@ -2029,4 +2010,34 @@ function update_folder(pname,vn) +function gmail_SMTP(recipient_email,subject_line,email_body) + +smtp_server = 'smtp.gmail.com'; +smtp_port = '587'; % Use TLS +email_address = 'behav.akramilab@gmail.com'; +email_password = 'fakc mdbw woef lqmq'; % IMPORTANT: this is set in setting of gmail + +% recipient_email = 'arpit.agarwal@ucl.ac.uk'; +% subject_line = 'Test Email from MATLAB via Gmail'; +% email_body = 'This email was sent using Gmail SMTP from MATLAB.'; + +% --- Set MATLAB Email Preferences --- +setpref('Internet','SMTP_Server',smtp_server); +setpref('Internet','E_mail',email_address); +setpref('Internet','SMTP_Username',email_address); +setpref('Internet','SMTP_Password',email_password); + +% Set server properties +props = java.lang.System.getProperties; +props.setProperty('mail.smtp.auth','true'); +props.setProperty('mail.smtp.starttls.enable','true'); +props.setProperty('mail.smtp.port',smtp_port); + +% --- Send the Email --- +try + sendmail(recipient_email, subject_line, email_body); + disp('Email sent successfully via Gmail SMTP.'); +catch ME + disp(['Error sending email: ' ME.message]); +end From 5979ea213e8c075e65a58696027f3c10fcb67adb Mon Sep 17 00:00:00 2001 From: Arpit Date: Tue, 13 May 2025 14:23:07 +0100 Subject: [PATCH 098/164] fixing scheduler to work and also adding the same to LoadSettingGUI --- ExperPort/Modules/@runrats/runrats.m | 24 ++++++++-- .../private/LoadSettingGUI.m | 46 +++++++++++++++---- 2 files changed, 57 insertions(+), 13 deletions(-) diff --git a/ExperPort/Modules/@runrats/runrats.m b/ExperPort/Modules/@runrats/runrats.m index 25ed6517..da9f8c40 100644 --- a/ExperPort/Modules/@runrats/runrats.m +++ b/ExperPort/Modules/@runrats/runrats.m @@ -767,6 +767,8 @@ case 'exp_rat_names' varargout{1} = value(ExpMenu); varargout{2} = value(RatMenu); + + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% case 'update_tech_instructions' %% update_tech_instructions @@ -1055,11 +1057,18 @@ case 'update_schedule' %% update_schedule + %Here we grab the current schedule for this rig - if ~isnan(value(RigID)); - [rats slots] = bdata(['select ratname, timeslot from scheduler where date="',... - datestr(now,'yyyy-mm-dd'),'" and rig=',num2str(value(RigID))]); + % if ~isnan(value(RigID)); + % [rats slots] = bdata(['select ratname, timeslot from scheduler where date="',... + % datestr(now,'yyyy-mm-dd'),'" and rig=',num2str(value(RigID))]); + % end + + % Updated by Arpit - instead of taking the date, we will look for + % if the rats which are in training in scheduler table. + if ~isnan(value(RigID)) + [rats,slots] = bdata(['select ratname, timeslot from scheduler where in_training="1" and rig=',num2str(value(RigID))]); end %Let's populate the 5 training sessions (changed by sharbat, with @@ -1593,6 +1602,15 @@ end set(get_ghandle(UpdateMode),'String','Live Update On','BackgroundColor',[0.6 1 0.6],'ForegroundColor',[0 0 0]); + %%%%%%%%%%%%%%Removing the part of full restart %%%%%%%%%%%%%% + %%%%%%%%%%%%% ARPIT %%%%%%%%%%%%%%%%%%%%% + + do_full_restart.value = 0; + p = bSettings('get','GENERAL','Main_Code_Directory'); + cd(p); + + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + %Another option is to now kill MatLab completely and restart %runrats. This ensures windows don't pile up, and code can get %updated before each session diff --git a/Protocols/@ArpitCentrePokeTraining/private/LoadSettingGUI.m b/Protocols/@ArpitCentrePokeTraining/private/LoadSettingGUI.m index e1c36ff0..84cf7e1a 100644 --- a/Protocols/@ArpitCentrePokeTraining/private/LoadSettingGUI.m +++ b/Protocols/@ArpitCentrePokeTraining/private/LoadSettingGUI.m @@ -1,5 +1,9 @@ % REMOVE THE SETTINGS/PRE OPENED GUI - flush + if exist('BpodSystem','var') == 1 + if ~isempty(BpodSystem) + flush + end + end % START FRESH & SET THE REQUIRED DIRECTORY @@ -15,12 +19,17 @@ end % START BPOD +try + Bpod('COM5') +catch + + % Identify the bpod port before starting + bpodPort = getOrSetCOMPort(); + disp(['Using COM Port: ' bpodPort]); + % Bpod(); + Bpod(bpodPort); +end -% Identify the bpod port before starting -% bpodPort = getOrSetCOMPort(); -% disp(['Using COM Port: ' bpodPort]); -% Bpod(bpodPort); -Bpod; newstartup; % PRESENT THE USER WITH THE CHOICE OF EXPERIMENTER/RAT @@ -33,14 +42,25 @@ Experimenter_Name = ''; end +rig_id = bSettings('get','RIGS','Rig_ID'); % the ID of this rig + if ~isempty(Experimenter_Name) for n_exp = 1:numel(Experimenter_Name) - ratnames = bdata(['select ratname from rats where experimenter="',Experimenter_Name{n_exp},'" and extant=1']); - Rat_Name{n_exp} = sortrows(strtrim(ratnames)); + % Finding all training rat for this experimenter + % ratnames = bdata(['select ratname from rats where experimenter="',Experimenter_Name{n_exp},'" and extant=1']); + ratnames = bdata(['select ratname from rats where experimenter="',Experimenter_Name{n_exp},'" and in_training=1']); + Rat_Name_all{n_exp} = sortrows(strtrim(ratnames)); + + % Additionaally finding rats for this experimenter with its schedule on this rig + [rats,slots] = bdata(['select ratname, timeslot from scheduler where experimenter="',... + Experimenter_Name{n_exp},'" and rig="',num2str(rig_id),'"']); + + end end -Exp_Rat_Map = containers.Map(Experimenter_Name, Rat_Name); +Exp_Rat_Map = containers.Map(Experimenter_Name, Rat_Name_all); + % Create figure fig = figure('Name', 'Dynamic Button GUI', ... @@ -77,6 +97,13 @@ function subButtonCallback(src,experimenter_name,figHandle) runrats('update exp_rat_userclick',experimenter_name,rat_name); end + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +%% HELPER FUNCTIONS + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + % === Create buttons dynamically and return handles === function btns = createButtons(figHandle, optionList, callbackFcn) delete(findall(figHandle, 'Type', 'uicontrol')); % Clear any existing buttons @@ -167,4 +194,3 @@ function resizeButtons(figHandle, buttons) end end end - From f54559452caac7fab0ff341f77554c7d24a8b820 Mon Sep 17 00:00:00 2001 From: Arpit Date: Tue, 13 May 2025 15:47:57 +0100 Subject: [PATCH 099/164] adding rerun after crash in runrats --- ExperPort/Modules/@runrats/runrats.m | 42 ++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/ExperPort/Modules/@runrats/runrats.m b/ExperPort/Modules/@runrats/runrats.m index da9f8c40..18166b67 100644 --- a/ExperPort/Modules/@runrats/runrats.m +++ b/ExperPort/Modules/@runrats/runrats.m @@ -138,6 +138,7 @@ SoloParamHandle(obj,'do_full_restart', 'value',1); %1 if we want to restart matlab between each session SoloParamHandle(obj,'SafetyMode', 'value',''); %empty for no safety, B for before, A for after SoloParamHandle(obj,'OptoPlugColor', 'value',[]); %figure handle for opto plug color panel + SoloParamHandle(obj,'Rerun_AfterCrash', 'value',1); % should load the previous protocol if runrats/dispatcher crashed if ~exist('phys','var'); SoloParamHandle(obj,'phys','value',0); end @@ -1746,11 +1747,52 @@ if ~isempty(id) id = id(end); bdata('call mark_crashed("{S}")',id); + + end +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + %% Added by Arpit + %% Lets try and rerun the protocol and only do it if the animal is training + is_rat_training = bdata(['select is_training from rats where experimenter="',value(ExpMenu),'" and ratname="', value(RatMenu),'"']); + + if value(Rerun_AfterCrash) == 1 && is_rat_training == 1 + runrats(obj,'rerun'); + end + + + case 'rerun' % called in 'crash' and made by combining 'begin_load_protocol' , 'load_protocol' and 'run' + + InLiveLoop.value = 0; + runrats(obj,'disable_all'); + + set(get_ghandle(Multi),'string','Unloading...','fontsize',28); + + x = ''; + try x = dispatcher('get_protocol_object'); end %#ok + if ~isempty(x) + %There was a protocol previously open. Let's not trust that + %their close section is working properly. + try %#ok + %rigscripts does not exist currently, try Protocols (ask + %Athena) -sharbat + p = bSettings('get','GENERAL','Main_Code_Directory'); + p(strfind(p,'ExperPort'):end) = ''; + p = [p,'Rigscripts']; + cd(p); + if ispc == 1 + system('restart_runrats.bat'); + end + end end + dispatcher('set_protocol',''); + % Loading the protocol and setting file + runrats(obj,'load_protocol') + % Running the protocol + runrats(obj,'run') +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% case 'crash_cleanup' %% crash_cleanup %The tech has acknowledged the crash. Let's jump back in the loop From bb3cda30cac9303c5b6ff730dfe920f30888352c Mon Sep 17 00:00:00 2001 From: Arpit Date: Tue, 13 May 2025 16:36:29 +0100 Subject: [PATCH 100/164] removed redundant code from runrat to update the codes from svn as we are using github --- ExperPort/Modules/@runrats/runrats.m | 31 +++++++++++++++++----------- 1 file changed, 19 insertions(+), 12 deletions(-) diff --git a/ExperPort/Modules/@runrats/runrats.m b/ExperPort/Modules/@runrats/runrats.m index 18166b67..c7842008 100644 --- a/ExperPort/Modules/@runrats/runrats.m +++ b/ExperPort/Modules/@runrats/runrats.m @@ -677,7 +677,7 @@ end; cd(dirRat); - update_folder(pwd,'svn'); + % update_folder(pwd,'svn'); cd(dirCurrent); runrats(obj,'enable_all'); @@ -1316,19 +1316,26 @@ StatusBar.value='Loading protocol and settings. Please be patient!'; pause(0.1); + %%%%%%%%%%%% ARPIT %%%%%%%%%%%%%%%%%%%%%%%%%%%% + + % NOT REQUIRED AS INSTEAD OF SVN WE ARE USING GITHUB + %Let's also make sure we have the most up-to-date code - CurrDir = pwd; - pname = bSettings('get','GENERAL','Main_Code_Directory'); - if ~isempty(pname) && ischar(pname) - update_folder(pname,'svn'); - end + + % CurrDir = pwd; + % pname = bSettings('get','GENERAL','Main_Code_Directory'); + % if ~isempty(pname) && ischar(pname) + % update_folder(pname,'svn'); + % end + % + % %And finally we make sure the protocols are up-to-date + % pname = bSettings('get','GENERAL','Protocols_Directory'); + % if ~isempty(pname) && ischar(pname) + % update_folder(pname,'svn'); + % end + % cd(CurrDir); - %And finally we make sure the protocols are up-to-date - pname = bSettings('get','GENERAL','Protocols_Directory'); - if ~isempty(pname) && ischar(pname) - update_folder(pname,'svn'); - end - cd(CurrDir); + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %Let's get the protocol for the rat and load it CurrProtocol.value = getProtocol(value(ExpMenu),value(RatMenu)); %#ok From e5d5e2f6f75eb006d4d3d19c7bfff49f29430928 Mon Sep 17 00:00:00 2001 From: Arpit Date: Wed, 14 May 2025 17:38:35 +0100 Subject: [PATCH 101/164] removed live loop in runrats as it was redundant and causing error in running flush --- ExperPort/Modules/@runrats/runrats.m | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/ExperPort/Modules/@runrats/runrats.m b/ExperPort/Modules/@runrats/runrats.m index c7842008..072c40fa 100644 --- a/ExperPort/Modules/@runrats/runrats.m +++ b/ExperPort/Modules/@runrats/runrats.m @@ -296,7 +296,7 @@ %prepare to load, let's do it. runrats(obj,'update_exprat'); runrats(obj,'check_rig_flushed'); - runrats(obj,'live_loop'); + % runrats(obj,'live_loop'); case 'send_empty_state_machine' @@ -1600,7 +1600,7 @@ %Let's reset the Multi button and hop back in the live loop set(get_ghandle(Multi),'ForegroundColor',[0,0,0],'BackgroundColor',... [1,1,0.4],'string','Load Protocol','FontSize',24); - InLiveLoop.value = 1; + InLiveLoop.value = 0; % Changed by Arpit as the timer function is preventing 'flush' to run runrats(obj,'enable_all'); %We need to turn RunRats back to live mode @@ -1654,7 +1654,7 @@ end %And now we hop back in the loop - runrats(obj,'live_loop'); + % runrats(obj,'live_loop'); end @@ -1809,7 +1809,7 @@ [1,1,0.4],'string','Load Protocol','FontSize',24); InLiveLoop.value = 1; - runrats(obj,'live_loop'); + % runrats(obj,'live_loop'); case 'updatelog' From a43315decc8b47cac164fc08b79e38e8c362bdc5 Mon Sep 17 00:00:00 2001 From: Arpit Date: Thu, 15 May 2025 14:10:18 +0100 Subject: [PATCH 102/164] error debug and adding video record start on user pressing run in runrat --- ExperPort/Modules/@runrats/runrats.m | 10 ++++- .../@bonsaicamera/BonsaiCameraInterface.m | 43 +++++++++++-------- .../ArpitCentrePokeTraining.m | 5 +++ .../TrainingStageParamsSection.m | 8 ++-- 4 files changed, 43 insertions(+), 23 deletions(-) diff --git a/ExperPort/Modules/@runrats/runrats.m b/ExperPort/Modules/@runrats/runrats.m index 072c40fa..ab1f51c3 100644 --- a/ExperPort/Modules/@runrats/runrats.m +++ b/ExperPort/Modules/@runrats/runrats.m @@ -1442,7 +1442,7 @@ sendstarttime(eval(value(CurrProtocol))); %#ok catch %#ok disp('ERROR: Failed to add the start time to the MySQL table.'); - end; + end %Let's make everything unresponsive for 5 more seconds to prevent %double clicks from stopping the session @@ -1491,7 +1491,13 @@ set(get_ghandle(Multi),'enable','on'); set(get_ghandle(Safety),'visible','off','string',''); end + + % Let start recording the videos by sending the command to protocol + % itself + protobj=eval(value(CurrProtocol)); + feval(value(CurrProtocol), protobj, 'start_recording'); + % Now ready to run with dispatcher dispatcher(value(dispobj),'Run'); %#ok @@ -1759,7 +1765,7 @@ %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% Added by Arpit %% Lets try and rerun the protocol and only do it if the animal is training - is_rat_training = bdata(['select is_training from rats where experimenter="',value(ExpMenu),'" and ratname="', value(RatMenu),'"']); + is_rat_training = bdata(['select in_training from rats where experimenter="',value(ExpMenu),'" and ratname="', value(RatMenu),'"']); if value(Rerun_AfterCrash) == 1 && is_rat_training == 1 runrats(obj,'rerun'); diff --git a/ExperPort/Plugins/@bonsaicamera/BonsaiCameraInterface.m b/ExperPort/Plugins/@bonsaicamera/BonsaiCameraInterface.m index 3b18c626..995d5d7a 100644 --- a/ExperPort/Plugins/@bonsaicamera/BonsaiCameraInterface.m +++ b/ExperPort/Plugins/@bonsaicamera/BonsaiCameraInterface.m @@ -118,7 +118,7 @@ runBonsaiWorkflow(bonsai_workflow_Path); pause(5); % wait few seconds for software to open before continuing - %% STEP 2: DECLARING AND FOLDER LOCATION FOR SAVING THE VIDEOS + %% STEP 2: DECLARING FOLDER LOCATION FOR SAVING THE VIDEOS % The video files are saved in the folder format declared below % C:\RatterVideos\ExpName\RateName\Videos_Protocolname_ExpName_RatName_date @@ -190,7 +190,7 @@ else video_save_dir = [video_save_dir char(97)]; end - mkdir(video_save_dir); + % mkdir(video_save_dir); SoloParamHandle(obj, 'Video_Saving_Folder', 'value', video_save_dir); % --- Create UDP Port Object --- @@ -215,27 +215,19 @@ if CameraControl == 1 % User Selected to Reconnect & Restart the feed from the Camera % Now that we have created the UPD connection, we can send commands - % over OSC. - % Before starting the streaming of Camera, I need to send the - % file directory for saving the file otherwise Bonsai can run into - % error as it will try saving files in the predefined folder in - % bonsai and that can conflict with file already present there. - - oscMsg_file_directory = createOSCMessage(recording_command_address, [value(Video_Saving_Folder) '\Trial.avi']); - % write(udpSender, oscMsg_file_directory, "uint8", bonsaiComputerIP,bonsaiUdpPort); - + % over OSC. + % OSC message to start the camera oscMsg_Camera_start = createOSCMessage(camera_command_address, startCommand); % the command to send message to Bonsai write(value(UDPSender), oscMsg_Camera_start, "uint8", bonsaiComputerIP,bonsaiUdpPort); - - pause(3); - + % NOTE: Ideally I should start saving the trials once the experimenter presses % Run either on dispatcher or Runrats. But, I dont want to make the changes there % so would start recording as soon as the protocol is loaded and camera starts streaming - - write(value(UDPSender), oscMsg_file_directory, "uint8", bonsaiComputerIP,bonsaiUdpPort); + %% Fixed in runrats + % pause(3); + % write(value(UDPSender), oscMsg_file_directory, "uint8", bonsaiComputerIP,bonsaiUdpPort); else % User Stopped the streaming and saving OF VIDEO @@ -245,7 +237,24 @@ end - + %% User pressed Run on runrats, so create the video direcctory folder and start recording + case 'record_start' + + % create the folder to save the videos + mkdir(value(Video_Saving_Folder)); + pause(3); + % To be on safer side that the last streaming message reached lets + % resend it + % OSC message to start the camera + oscMsg_Camera_start = createOSCMessage(camera_command_address, startCommand); + % the command to send message to Bonsai + write(value(UDPSender), oscMsg_Camera_start, "uint8", bonsaiComputerIP,bonsaiUdpPort); + pause(2); + % Send the message to start recording + oscMsg_file_directory = createOSCMessage(recording_command_address, [value(Video_Saving_Folder) '\Trial.avi']); + write(value(UDPSender), oscMsg_file_directory, "uint8", bonsaiComputerIP,bonsaiUdpPort); + + case 'stop' % Stopping the streaming and saving last trial video % this stops saving and streaming of the camera diff --git a/Protocols/@ArpitCentrePokeTraining/ArpitCentrePokeTraining.m b/Protocols/@ArpitCentrePokeTraining/ArpitCentrePokeTraining.m index d13d91dd..a72f9649 100644 --- a/Protocols/@ArpitCentrePokeTraining/ArpitCentrePokeTraining.m +++ b/Protocols/@ArpitCentrePokeTraining/ArpitCentrePokeTraining.m @@ -232,6 +232,11 @@ myvar.value = maxasymp + (minasymp/(1+(t/inflp)^slp).^assym); end + %% when user presses run on runrats then then is called + case 'start_recording' + + BonsaiCameraInterface(obj,'record_start'); + %% prepare next trial case 'prepare_next_trial' diff --git a/Protocols/@ArpitCentrePokeTraining/TrainingStageParamsSection.m b/Protocols/@ArpitCentrePokeTraining/TrainingStageParamsSection.m index 37dc4338..6552ccf6 100644 --- a/Protocols/@ArpitCentrePokeTraining/TrainingStageParamsSection.m +++ b/Protocols/@ArpitCentrePokeTraining/TrainingStageParamsSection.m @@ -21,8 +21,8 @@ SubheaderParam(obj, 'title', 'Stage Params', x, y); % next_row(y); % COMPLETION TEST PARAMETERS - NumeditParam(obj, 'total_trials', 300, x, y,'label','Trials','TooltipString','total trials in this stage for its completion'); next_row(y); - NumeditParam(obj, 'total_trials_opp', 150, x, y,'label','Trials_Opp','TooltipString','total trials with opposide side option in this stage for its completion'); next_row(y); + NumeditParam(obj, 'total_trials', 1000, x, y,'label','Trials','TooltipString','total trials in this stage for its completion'); next_row(y); + NumeditParam(obj, 'total_trials_opp', 300, x, y,'label','Trials_Opp','TooltipString','total trials with opposide side option in this stage for its completion'); next_row(y); SubheaderParam(obj, 'title', 'Completion Params', x, y); next_row(y); case 2 @@ -32,8 +32,8 @@ NumeditParam(obj, 'min_rColl_dur', 100, x, y,'label','T_Min_RCollect','TooltipString','User given min water collect time(code will optimize for value above this)'); next_row(y); SubheaderParam(obj, 'title', 'Stage Params', x, y); next_row(y); % COMPLETION TEST PARAMETERS - NumeditParam(obj, 'total_trials', 1000, x, y,'label','Trials','TooltipString','total trials in this stage for its completion'); next_row(y); - NumeditParam(obj, 'total_trials_opp', 400, x, y,'label','Trials_Opp','TooltipString','total trials with opposide side option in this stage for its completion'); next_row(y); + NumeditParam(obj, 'total_trials', 700, x, y,'label','Trials','TooltipString','total trials in this stage for its completion'); next_row(y); + NumeditParam(obj, 'total_trials_opp', 200, x, y,'label','Trials_Opp','TooltipString','total trials with opposide side option in this stage for its completion'); next_row(y); SubheaderParam(obj, 'title', 'Completion Params', x, y);next_row(y); case 3 % no completion test required From abcd1108b223b8aa1af5159afd8a417a1719ca19 Mon Sep 17 00:00:00 2001 From: Arpit Date: Fri, 16 May 2025 17:39:05 +0100 Subject: [PATCH 103/164] added detailed error report on crash, changed filename of trial avi file, edited soundcatcontinuous to display on command window --- ExperPort/Modules/@runrats/runrats.m | 55 ++++++++++++------- .../@bonsaicamera/BonsaiCameraInterface.m | 8 ++- .../@bonsaicamera/Camera_Control.bonsai | 0 .../Camera_Control.bonsai.layout | 6 +- .../@SoundCatContinuous/SoundCatContinuous.m | 37 +++++++------ .../@SoundCatContinuous/StimulatorSection.m | 28 +++++----- 6 files changed, 77 insertions(+), 57 deletions(-) mode change 100644 => 100755 ExperPort/Plugins/@bonsaicamera/Camera_Control.bonsai mode change 100644 => 100755 ExperPort/Plugins/@bonsaicamera/Camera_Control.bonsai.layout diff --git a/ExperPort/Modules/@runrats/runrats.m b/ExperPort/Modules/@runrats/runrats.m index ab1f51c3..6ebf0cb3 100644 --- a/ExperPort/Modules/@runrats/runrats.m +++ b/ExperPort/Modules/@runrats/runrats.m @@ -1105,10 +1105,10 @@ CS = value(CurrSession); - [ratSCH slots] = bdata(['select ratname, timeslot from scheduler where date="',... + [ratSCH, slots] = bdata(['select ratname, timeslot from scheduler where date="',... datestr(now,'yyyy-mm-dd'),'"']); ratSES = bdata(['select ratname from sessions where sessiondate="',datestr(now,'yyyy-mm-dd'),'"']); - [ratSS ST] = bdata(['select ratname, starttime from sess_started where sessiondate="',... + [ratSS, ST] = bdata(['select ratname, starttime from sess_started where sessiondate="',... datestr(now,'yyyy-mm-dd'),'"']); %Let's cycle through each of the 6 training sessions and see if the @@ -1456,7 +1456,7 @@ % disp('failed to start pi camera') % end - % If using USB Webcam, then try using it + % If using USB Webcam, then try using it instead using Bonsai % try % disp('Connecting to USB HD Camera') % webcam_connected = webcamlist; @@ -1493,7 +1493,7 @@ end % Let start recording the videos by sending the command to protocol - % itself + % itself instead of the plugin bonsaicamera protobj=eval(value(CurrProtocol)); feval(value(CurrProtocol), protobj, 'start_recording'); @@ -1716,16 +1716,24 @@ RunningSection(value(dispobj),'RunStop'); %#ok dispatcher('set_protocol',''); - %Now we can email the rat's owner a crash report + %Now we can email the rat's owner a detailed crash report try %#ok + error_message = lsterr.message; + error_message = strrep(error_message, '\', '\\'); + error_message = strrep(error_message, '"', '\"'); message = cell(0); message{end+1} = ['Rig ',num2str(value(RigID)),' crashed while running ',value(RatMenu),' at ',datestr(now,13)]; %#ok message{end+1} = ''; - message{end+1} = lsterr.message; + message{end+1} = lsterr.identifier; + message{end+1} = error_message; + file_path = lsterr.stack(1).file; + message{end+1} = strrep(file_path, '\', '\\'); + message{end+1} = lsterr.stack(1).name; + message{end+1} = num2str(lsterr.stack(1).line); message{end+1} = ''; - + for i = 1:length(lsterr.stack) - message{end+1} = [lsterr.stack(i).name,' at ',num2str(lsterr.stack(i).line)]; %#ok + message{end+1} = ['Line ' num2str(lsterr.stack(i).line) ', File ' lsterr.stack(i).file ', Function ' lsterr.stack(i).name]; %#ok end IP = get_network_info; @@ -1754,19 +1762,28 @@ end end + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + %% Modified/Added by Arpit + %Let's update the MySQL table to indicate a crash has happened - id = bdata(['select sessid from sess_started where ratname="',value(RatMenu),... - '" and was_ended=0 and sessiondate="',datestr(now,'yyyy-mm-dd'),'"']); - if ~isempty(id) - id = id(end); - bdata('call mark_crashed("{S}")',id); - - end -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - %% Added by Arpit - %% Lets try and rerun the protocol and only do it if the animal is training - is_rat_training = bdata(['select in_training from rats where experimenter="',value(ExpMenu),'" and ratname="', value(RatMenu),'"']); + %Since this sql table is missing, the same information can be + % obtained from sess_started table where was_ended will stay 0. + % id = bdata(['select sessid from sess_started where ratname="',value(RatMenu),... + % '" and was_ended=0 and sessiondate="',datestr(now,'yyyy-mm-dd'),'"']); + % if ~isempty(id) + % id = id(end); + % bdata('call mark_crashed("{S}")',id); + % end + + %% Lets try and rerun the protocol and only do it if the animal is training + try + is_rat_training = bdata(['select in_training from rats where experimenter="',value(ExpMenu),'" and ratname="', value(RatMenu),'"']); + catch + is_rat_training = 1; % if couldn't find then try rerun of the protocol + end + if value(Rerun_AfterCrash) == 1 && is_rat_training == 1 runrats(obj,'rerun'); end diff --git a/ExperPort/Plugins/@bonsaicamera/BonsaiCameraInterface.m b/ExperPort/Plugins/@bonsaicamera/BonsaiCameraInterface.m index 995d5d7a..d61efa88 100644 --- a/ExperPort/Plugins/@bonsaicamera/BonsaiCameraInterface.m +++ b/ExperPort/Plugins/@bonsaicamera/BonsaiCameraInterface.m @@ -34,7 +34,9 @@ % Switch-case structure: % % 'init' : Initializes UI toggle, starts Bonsai workflow, sets up save directory and UDP sender. -% 'camera_connection' : Sends OSC messages to start or stop the camera feed. Also sends the video path to Bonsai. +% 'camera_connection' : Sends OSC messages to start or stop the camera feed. +% 'record_start' : creates the directory to save videos initially declared +% in 'init' and then send the message to Bonsai to start recording. % 'next_trial' : Sends a new video save path to Bonsai to separate trial recordings. % 'close' : Stops the camera, deletes the UDP sender object, and terminates Bonsai and CMD processes. % 'stop' : Sending OSC message to stop streaming and save last trial video @@ -251,7 +253,7 @@ write(value(UDPSender), oscMsg_Camera_start, "uint8", bonsaiComputerIP,bonsaiUdpPort); pause(2); % Send the message to start recording - oscMsg_file_directory = createOSCMessage(recording_command_address, [value(Video_Saving_Folder) '\Trial.avi']); + oscMsg_file_directory = createOSCMessage(recording_command_address,sprintf('%s\BControlTrial%i_bonsaiTrial.avi',value(Video_Saving_Folder),n_completed_trial)); write(value(UDPSender), oscMsg_file_directory, "uint8", bonsaiComputerIP,bonsaiUdpPort); @@ -269,7 +271,7 @@ % in this I send a command to bonsai so that it creates a new file % for each trial - oscMsg_file_directory = createOSCMessage(recording_command_address, [value(Video_Saving_Folder) '\Trial.avi']); + oscMsg_file_directory = createOSCMessage(recording_command_address,sprintf('%s\BControlTrial%i_bonsaiTrial.avi',value(Video_Saving_Folder),n_completed_trial)); write(value(UDPSender), oscMsg_file_directory, "uint8", bonsaiComputerIP,bonsaiUdpPort); %% close bonsai and command window diff --git a/ExperPort/Plugins/@bonsaicamera/Camera_Control.bonsai b/ExperPort/Plugins/@bonsaicamera/Camera_Control.bonsai old mode 100644 new mode 100755 diff --git a/ExperPort/Plugins/@bonsaicamera/Camera_Control.bonsai.layout b/ExperPort/Plugins/@bonsaicamera/Camera_Control.bonsai.layout old mode 100644 new mode 100755 index 5f01c6c4..f260048f --- a/ExperPort/Plugins/@bonsaicamera/Camera_Control.bonsai.layout +++ b/ExperPort/Plugins/@bonsaicamera/Camera_Control.bonsai.layout @@ -27,7 +27,7 @@ Normal - true + false 94 95 @@ -115,7 +115,7 @@ Normal - true + false 472 106 @@ -183,7 +183,7 @@ Normal - true + false 95 246 diff --git a/Protocols/@SoundCatContinuous/SoundCatContinuous.m b/Protocols/@SoundCatContinuous/SoundCatContinuous.m index 2e3fb0e9..7e9c669c 100644 --- a/Protocols/@SoundCatContinuous/SoundCatContinuous.m +++ b/Protocols/@SoundCatContinuous/SoundCatContinuous.m @@ -14,25 +14,26 @@ %--------------------------------------------------------------- % If creating an empty object, return without further ado: -if nargin==0 || (nargin==1 && ischar(varargin{1}) && strcmp(varargin{1}, 'empty')), +if nargin==0 || (nargin==1 && ischar(varargin{1}) && strcmp(varargin{1}, 'empty')) return; -end; +end -if isa(varargin{1}, mfilename), % If first arg is an object of this class itself, we are +if isa(varargin{1}, mfilename) % If first arg is an object of this class itself, we are % Most likely responding to a callback from % a SoloParamHandle defined in this mfile. - if length(varargin) < 2 || ~ischar(varargin{2}), + if length(varargin) < 2 || ~ischar(varargin{2}) error(['If called with a "%s" object as first arg, a second arg, a ' ... 'string specifying the action, is required\n']); - else action = varargin{2}; varargin = varargin(3:end); %#ok - end; + else + action = varargin{2}; varargin = varargin(3:end); %#ok + end else % Ok, regular call with first param being the action string. action = varargin{1}; varargin = varargin(2:end); %#ok -end; +end GetSoloFunctionArgs(obj); -switch action, +switch action %% init case 'init' @@ -138,14 +139,14 @@ feval(mfilename, obj, 'prepare_next_trial'); %% change_water_modulation_params - case 'change_water_modulation_params', + case 'change_water_modulation_params' display_guys = [1 150 300]; - for i=1:numel(display_guys), + for i=1:numel(display_guys) t = display_guys(i); myvar = eval(sprintf('trial_%d', t)); myvar.value = maxasymp + (minasymp/(1+(t/inflp)^slp).^assym); - end; + end %% prepare next trial case 'prepare_next_trial' @@ -169,16 +170,16 @@ SavingSection(obj, 'autosave_data'); CommentsSection(obj, 'clear_history'); % Make sure we're not storing unnecessary history - if n_done_trials==1, % Auto-append date for convenience. + if n_done_trials==1 % Auto-append date for convenience. CommentsSection(obj, 'append_date'); CommentsSection(obj, 'append_line', ''); - end; + end if n_done_trials==1 [expmtr, rname]=SavingSection(obj, 'get_info'); prot_title.value=[mfilename ' on rig ' get_hostname ' : ' expmtr ', ' rname '. Started at ' datestr(now, 'HH:MM')]; end - try send_n_done_trials(obj); end + % try send_n_done_trials(obj); end %% trial_completed case 'trial_completed' @@ -199,9 +200,9 @@ SideSection(obj, 'close'); StimulusSection(obj,'close'); - if exist('myfig', 'var') && isa(myfig, 'SoloParamHandle') && ishandle(value(myfig)), %#ok + if exist('myfig', 'var') && isa(myfig, 'SoloParamHandle') && ishandle(value(myfig)) %#ok delete(value(myfig)); - end; + end delete_sphandle('owner', ['^@' class(obj) '$']); @@ -253,9 +254,9 @@ sendsummary(obj,'protocol_data','PLACEHOLDER it was just pd'); %% otherwise - otherwise, + otherwise warning('Unknown action! "%s"\n', action); -end; +end return; diff --git a/Protocols/@SoundCatContinuous/StimulatorSection.m b/Protocols/@SoundCatContinuous/StimulatorSection.m index 71ad0760..ff91fbe0 100644 --- a/Protocols/@SoundCatContinuous/StimulatorSection.m +++ b/Protocols/@SoundCatContinuous/StimulatorSection.m @@ -35,7 +35,7 @@ % % ---------------------------------------------------------------- - case 'init', + case 'init' %NumeditParam(obj,'LCB_onstim', 0,x,y,'position',[x y 100 20],'labelfraction',0.6); %NumeditParam(obj,'LCB_nostim', 0,x,y,'position',[x+100 y 100 20],'labelfraction',0.6); next_row(y); %SoloParamHandle(obj,'LegalCBrk_temp', 'value',0); @@ -46,7 +46,7 @@ diolines = bSettings('get','DIOLINES', 'all'); for i = 1:size(diolines,1); dionames{i} = diolines{i,1}; dionums(i) = diolines{i,2}; end %#ok - [dionums order] = sort(dionums); + [dionums,order] = sort(dionums); dionames2 = cell(0); for i = 1:length(dionums); if ~isnan(dionums(i)); dionames2{end+1} = dionames{order(i)}; end; end %#ok dionames2 = cell(0); @@ -93,15 +93,15 @@ % % ----------------------------------------------------------------------- - case 'update_values', + case 'update_values' StimulatorSection(obj,'StimInterval'); - sh = value(stimulator_history) %#ok + sh = value(stimulator_history); %#ok %if n_done_trials == 0 || sh(end) == 0 % LegalCBrk_temp.value = value(LegalCBrk); %#ok %end - if ~dispatcher('is_running'); + if ~dispatcher('is_running') %dispatcher is not running, last stim_hist not used, lop it off sh = sh(1:end-1); end @@ -126,7 +126,7 @@ % % ----------------------------------------------------------------------- - case 'prepare_next_trial', + case 'prepare_next_trial' sh = value(stimulator_history); %#ok sma = x; @@ -135,8 +135,8 @@ sf = value(StimFreq); pw = value(PulseWidth); np = value(NumPulses); - ss = value(StimStates) - sl = value(StimLines) + ss = value(StimStates); + sl = value(StimLines); if value(ShuffleValues) == 1 sd = sd(ceil(rand(1) * length(sd))); @@ -239,19 +239,19 @@ %% Case StimInterval case 'StimInterval' - if strcmp(StimInterval, 'WholeTrial'); + if strcmp(StimInterval, 'WholeTrial') PulseWidth.value = Total_CP_duration*1000; StimFreq.value = 1000/PulseWidth; StartDelay.value = PreStim_time; - elseif strcmp(StimInterval, 'S1'); + elseif strcmp(StimInterval, 'S1') PulseWidth.value = A1_time*1000; StimFreq.value = 1000/PulseWidth; StartDelay.value = PreStim_time; - elseif strcmp(StimInterval, 'DelayDur'); + elseif strcmp(StimInterval, 'DelayDur') PulseWidth.value = time_bet_aud1_gocue*1000; StimFreq.value = 1000/PulseWidth; StartDelay.value = PreStim_time + A1_time; - elseif strcmp(StimInterval, 'GoCue'); + elseif strcmp(StimInterval, 'GoCue') PulseWidth.value = time_go_cue*1000; StimFreq.value = 1000/PulseWidth; StartDelay.value = PreStim_time + A1_time + time_bet_aud1_gocue; @@ -306,7 +306,7 @@ % REINIT % % ----------------------------------------------------------------------- - case 'reinit', + case 'reinit' currfig = double(gcf); % Delete all SoloParamHandles who belong to this object and whose @@ -320,6 +320,6 @@ % Restore the current figure: figure(currfig); -end; +end From dd4db3c378b593d7ac3062b38c118503b72586e5 Mon Sep 17 00:00:00 2001 From: Arpit Date: Fri, 16 May 2025 23:06:06 +0100 Subject: [PATCH 104/164] automatically create setting file for new rat for ArpitCentrePokeTraining --- ...eate_ArpitCentrePokeTraining_SettingFile.m | 91 ++ ...rpitCentrePokeTraining_arpit_AR05_250516.m | 1379 +++++++++++++++++ ...tCentrePokeTraining_arpit_AR05_250516a.mat | Bin 0 -> 16329 bytes 3 files changed, 1470 insertions(+) create mode 100644 Protocols/@ArpitCentrePokeTraining/private/create_ArpitCentrePokeTraining_SettingFile.m create mode 100644 Protocols/@ArpitCentrePokeTraining/private/pipeline_ArpitCentrePokeTraining_arpit_AR05_250516.m create mode 100644 Protocols/@ArpitCentrePokeTraining/private/settings_@ArpitCentrePokeTraining_arpit_AR05_250516a.mat diff --git a/Protocols/@ArpitCentrePokeTraining/private/create_ArpitCentrePokeTraining_SettingFile.m b/Protocols/@ArpitCentrePokeTraining/private/create_ArpitCentrePokeTraining_SettingFile.m new file mode 100644 index 00000000..83976046 --- /dev/null +++ b/Protocols/@ArpitCentrePokeTraining/private/create_ArpitCentrePokeTraining_SettingFile.m @@ -0,0 +1,91 @@ +function create_ArpitCentrePokeTraining_SettingFile() + +templateDir = 'C:/ratter/'; +templateDir = '/Users/AkramiLab_Arpit/Documents/AkramiLabScripts/ratter'; + +templatePath = [templateDir '/Protocols/@ArpitCentrePokeTraining/private/pipeline_ArpitCentrePokeTraining_arpit_AR05_250516.m']; +outputRootDir = [templateDir '/SoloData/Settings/']; + +experimenter = input('Enter experimenter name: ', 's'); +ratname = input('Enter rat name: ', 's'); + +% Get today's date +newDate = datestr(now, 'yymmdd'); + +% Read the template file +if ~isfile(templatePath) + error('Template file not found: %s', templatePath); +end +code = fileread(templatePath); +lines = splitlines(code); + +% Find and update the function definition line +newFuncName = ''; +for i = 1:length(lines) + line = strtrim(lines{i}); + if startsWith(line, 'function') && contains(line, 'pipeline_') + % Extract argument list + pattern = 'function\s+varargout\s*=\s*pipeline_.*?\((.*?)\)'; + tokens = regexp(line, pattern, 'tokens'); + if ~isempty(tokens) + args = tokens{1}{1}; + newFuncName = sprintf('pipeline_ArpitCentrePokeTraining_%s_%s_%s', ... + experimenter, ratname, newDate); + lines{i} = sprintf('function varargout = %s(%s)', newFuncName, args); + break; + end + end +end + +if isempty(newFuncName) + error('Could not find valid function definition line in template.'); +end + +% Create output directory if needed +outputDir = fullfile(outputRootDir, experimenter, ratname); +if ~exist(outputDir, 'dir') + mkdir(outputDir); +end + +% Build full new script path +newScriptPath = fullfile(outputDir, [newFuncName, '.m']); + +% Save modified content +newCode = strjoin(lines, newline); +fid = fopen(newScriptPath, 'w'); +if fid == -1 + error('Failed to open new file for writing: %s', newScriptPath); +end +fwrite(fid, newCode); +fclose(fid); + +fprintf('New Session Definition file saved to: %s\n', newScriptPath); + +% Load and modify .mat file +[templateFolder, ~, ~] = fileparts(templatePath); +matTemplatePath = fullfile(templateFolder, 'settings_@ArpitCentrePokeTraining_arpit_AR05_250516a.mat'); + +if ~isfile(matTemplatePath) + warning('No setting_file found in template directory: %s', matTemplatePath); + newMatPath = ''; + return; +end + +load(matTemplatePath,'saved','saved_autoset','fig_position'); % Load struct + +saved.SavingSection_experimenter = experimenter; +saved.SavingSection_ratname = ratname; +saved.SessionDefinition_textTrainingStageFile = newScriptPath; +saved.ArpitCentrePokeTraining_prot_title = sprintf('ArpitCentrePokeTraining: %s, %s',experimenter,ratname); +saved.PokesPlotSection_textHeader = sprintf('PokesPlotSection(%s, %s',experimenter,ratname); +saved.SessionDefinition_textHeader = sprintf('SESSION AUTOMATOR WINDOW: %s, %s',experimenter,ratname); + +new_MATfile = sprintf('settings_@ArpitCentrePokeTraining__%s_%s_%sa.mat', ... + experimenter, ratname, newDate); + +newMatPath = fullfile(outputDir, new_MATfile); + +save(newMatPath,'saved','saved_autoset','fig_position'); +fprintf('New setting file saved to: %s\n', newMatPath); + +end diff --git a/Protocols/@ArpitCentrePokeTraining/private/pipeline_ArpitCentrePokeTraining_arpit_AR05_250516.m b/Protocols/@ArpitCentrePokeTraining/private/pipeline_ArpitCentrePokeTraining_arpit_AR05_250516.m new file mode 100644 index 00000000..dda1012f --- /dev/null +++ b/Protocols/@ArpitCentrePokeTraining/private/pipeline_ArpitCentrePokeTraining_arpit_AR05_250516.m @@ -0,0 +1,1379 @@ +%Training stage file. +%Please use the session automator window exclusively +%to edit this file. + +function varargout = pipeline_ArpitCentrePokeTraining_experimenter_ratname_250516(obj, action, varargin) + +GetSoloFunctionArgs('func_owner', ['@' class(obj)], 'func_name', 'SessionModel'); + +pairs = {'helper_vars_eval', true; + 'stage_algorithm_eval', true; + 'completion_test_eval', false; + 'eod_logic_eval', false}; +parseargs(varargin, pairs); + +switch action + + +%% Familiarize with Reward Side Pokes + +% +case 'Familiarize with Reward Side Pokes' +if helper_vars_eval +GetSoloFunctionArgs(obj); +ClearHelperVarsNotOwned(obj); +% +CreateHelperVar(obj,'stage_start_completed_trial','value',n_completed_trials,'force_init',true); +% +end +if stage_algorithm_eval +GetSoloFunctionArgs(obj); +ClearHelperVarsNotOwned(obj); +% + +% Update ParamsSection +ParamsSection_MaxSame.value = 4; +callback(ParamsSection_MaxSame); +stage_no = value(SessionDefinition_CURRENT_ACTIVE_STAGE); +if stage_no ~= value(ParamsSection_training_stage) + ParamsSection_training_stage.value = stage_no; + callback(ParamsSection_training_stage); +end + +% Update TrainingStageParamsSection +if n_completed_trials >= 2 + if previous_sides(end) ~= previous_sides(end-1) && all(hit_history(end-1:end)) % last and present trials should also be a valid trial + TrainingStageParamsSection_trial_oppSide.value = value(TrainingStageParamsSection_trial_oppSide) + 1; % updating value for variable in TrainingParams_Section + callback(TrainingStageParamsSection_trial_oppSide); + end +end + +% Updating Disp Values for Training_Peformance_Summary +if n_completed_trials > 0 + PerformanceSummarySection_stage_1_Trials.value = value(PerformanceSummarySection_stage_1_Trials) + 1; + PerformanceSummarySection_stage_1_TrialsToday.value = value(PerformanceSummarySection_stage_1_TrialsToday) + 1; + PerformanceSummarySection_stage_1_ViolationRate.value = nan; + PerformanceSummarySection_stage_1_TimeoutRate.value = nan; + if ~isnan(hit_history(end)) + PerformanceSummarySection_stage_1_TrialsValid.value = value(PerformanceSummarySection_stage_1_TrialsValid) + 1; + end + callback(PerformanceSummarySection_stage_1_Trials); + callback(PerformanceSummarySection_stage_1_TrialsToday) + callback(PerformanceSummarySection_stage_1_ViolationRate); + callback(PerformanceSummarySection_stage_1_TimeoutRate); + callback(PerformanceSummarySection_stage_1_TrialsValid); + + % Updating Disp Values for Training_Peformance_Summary + SessionPerformanceSection_ntrials.value = n_completed_trials; + SessionPerformanceSection_violation_rate.value = nan; + SessionPerformanceSection_timeout_rate.value = nan; + SessionPerformanceSection_violation_recent.value = nan; + SessionPerformanceSection_timeout_recent.value = nan; + SessionPerformanceSection_ntrials_stage.value = value(PerformanceSummarySection_stage_1_Trials); + SessionPerformanceSection_ntrials_stage_today.value = value(PerformanceSummarySection_stage_1_TrialsToday); + SessionPerformanceSection_timeout_stage.value = nan; + SessionPerformanceSection_violation_stage.value = nan; + + callback(SessionPerformanceSection_ntrials); + callback(SessionPerformanceSection_ntrials_stage); + callback(SessionPerformanceSection_ntrials_stage_today) + callback(SessionPerformanceSection_violation_rate); + callback(SessionPerformanceSection_timeout_rate); + callback(SessionPerformanceSection_violation_recent); + callback(SessionPerformanceSection_timeout_recent); + callback(SessionPerformanceSection_violation_stage); + callback(SessionPerformanceSection_timeout_stage); + +end +% +end +if completion_test_eval +GetSoloFunctionArgs(obj); +ClearHelperVarsNotOwned(obj); +clear('ans'); +% +if ParamsSection_use_auto_train % do completion check if auto training + stage_no = value(SessionDefinition_CURRENT_ACTIVE_STAGE); + % only run it if its the start of the day, number of trials is small + if n_completed_trials < 100 + if value(PerformanceSummarySection_stage_1_TrialsValid) > value(TrainingStageParamsSection_total_trials) && ... + value(TrainingStageParamsSection_trial_oppSide) > value(TrainingStageParamsSection_total_trials_opp) + ParamsSection_training_stage.value = stage_no + 1; + callback(ParamsSection_training_stage); + ParamsSection(obj, 'Changed_Training_Stage'); + SessionDefinition(obj, 'jump_to_stage', 'Timeout Rewarded Side Pokes'); + end + end +end +% +if exist('ans', 'var') +varargout{1}=logical(ans); clear('ans'); +else +varargout{1}=false; +end +end +if eod_logic_eval +GetSoloFunctionArgs(obj); +ClearHelperVarsNotOwned(obj); +% +stage_no = value(SessionDefinition_CURRENT_ACTIVE_STAGE); +if value(PerformanceSummarySection_stage_1_TrialsValid) > value(TrainingStageParamsSection_total_trials) && ... + value(TrainingStageParamsSection_trial_oppSide) > value(TrainingStageParamsSection_total_trials_opp) + ParamsSection_training_stage.value = stage_no + 1; + callback(ParamsSection_training_stage); + ParamsSection(obj, 'Changed_Training_Stage'); + SessionDefinition(obj, 'jump_to_stage', 'Timeout Rewarded Side Pokes'); +end +PerformanceSummarySection_stage_1_TrialsToday.value = 0; +callback(PerformanceSummarySection_stage_1_TrialsToday); +PerformanceSummarySection_stage_2_TrialsToday.value = 0; +callback(PerformanceSummarySection_stage_2_TrialsToday); +PerformanceSummarySection_stage_3_TrialsToday.value = 0; +callback(PerformanceSummarySection_stage_3_TrialsToday); +PerformanceSummarySection_stage_4_TrialsToday.value = 0; +callback(PerformanceSummarySection_stage_4_TrialsToday); +PerformanceSummarySection_stage_5_TrialsToday.value = 0; +callback(PerformanceSummarySection_stage_5_TrialsToday); +PerformanceSummarySection_stage_6_TrialsToday.value = 0; +callback(PerformanceSummarySection_stage_6_TrialsToday); +PerformanceSummarySection_stage_7_TrialsToday.value = 0; +callback(PerformanceSummarySection_stage_7_TrialsToday); +PerformanceSummarySection_stage_8_TrialsToday.value = 0; +callback(PerformanceSummarySection_stage_8_TrialsToday); +% +end +% + +%% Timeout Rewarded Side Pokes + +% +case 'Timeout Rewarded Side Pokes' +if helper_vars_eval +GetSoloFunctionArgs(obj); +ClearHelperVarsNotOwned(obj); +% +CreateHelperVar(obj,'this_stage_opp_side_trials', 'value', 0, 'force_init',true); +% +end +if stage_algorithm_eval +GetSoloFunctionArgs(obj); +ClearHelperVarsNotOwned(obj); +% +% Change ParamsSection Vars +ParamsSection_MaxSame.value = 4; +callback(ParamsSection_MaxSame); +stage_no = value(SessionDefinition_CURRENT_ACTIVE_STAGE); +if stage_no ~= value(ParamsSection_training_stage) + ParamsSection_training_stage.value = stage_no; + callback(ParamsSection_training_stage); +end +% Update the reward collection time based upon behav +if length(timeout_history) > 5 + if all(timeout_history(end-1:end)) && value(this_stage_opp_side_trials) >= 2 + ParamsSection_RewardCollection_duration.value = min([value(ParamsSection_RewardCollection_duration) + 1,... + value(TrainingStageParamsSection_max_rColl_dur)]); + callback(ParamsSection_RewardCollection_duration); + this_stage_opp_side_trials.value = 0; + end + if ~any(timeout_history(end-1:end)) && value(this_stage_opp_side_trials) >= 2 + ParamsSection_RewardCollection_duration.value = max([value(ParamsSection_RewardCollection_duration) - 1,... + value(TrainingStageParamsSection_min_rColl_dur)]); + callback(ParamsSection_RewardCollection_duration); + this_stage_opp_side_trials.value = 0; + end +end +if length(timeout_history) > 20 + if all(timeout_history(end-19:end)) + ParamsSection_RewardCollection_duration.value = 120; + callback(ParamsSection_RewardCollection_duration); + end +end + +% Update TrainingStageParamsSection +if n_completed_trials >= 2 + if previous_sides(end) ~= previous_sides(end-1) && all(hit_history(end-1:end)) % last and present trials should also be a valid trial + TrainingStageParamsSection_trial_oppSide.value = value(TrainingStageParamsSection_trial_oppSide) + 1; % updating value for variable in TrainingParams_Section + callback(TrainingStageParamsSection_trial_oppSide); + end +end + + +% Updating Disp Values for Training_Peformance_Summary +if n_completed_trials > 0 + PerformanceSummarySection_stage_2_Trials.value = value(PerformanceSummarySection_stage_2_Trials) + 1; + PerformanceSummarySection_stage_2_TrialsToday.value = value(PerformanceSummarySection_stage_2_TrialsToday) + 1; + PerformanceSummarySection_stage_2_ViolationRate.value = nan; + PerformanceSummarySection_stage_2_TimeoutRate.value = ((value(PerformanceSummarySection_stage_2_TimeoutRate) * (value(PerformanceSummarySection_stage_2_Trials) - 1)) + double(timeout_history(end))) / value(PerformanceSummarySection_stage_2_Trials); + if ~isnan(hit_history(end)) + PerformanceSummarySection_stage_2_TrialsValid.value = value(PerformanceSummarySection_stage_2_TrialsValid) + 1; + end + + callback(PerformanceSummarySection_stage_2_Trials); + callback(PerformanceSummarySection_stage_2_TrialsToday) + callback(PerformanceSummarySection_stage_2_ViolationRate); + callback(PerformanceSummarySection_stage_2_TimeoutRate); + callback(PerformanceSummarySection_stage_2_TrialsValid); + + % Updating Disp Values for Training_Peformance_Summary + SessionPerformanceSection_ntrials.value = n_completed_trials; + SessionPerformanceSection_violation_rate.value = nan; + SessionPerformanceSection_timeout_rate.value = numel(find(timeout_history))/n_completed_trials; + SessionPerformanceSection_violation_recent.value = nan; + if n_completed_trials >= 20 + SessionPerformanceSection_timeout_recent.value = numel(find(timeout_history(end-19:end)))/20; + else + SessionPerformanceSection_timeout_recent.value = nan; + end + SessionPerformanceSection_violation_stage.value = nan; + SessionPerformanceSection_ntrials_stage.value = value(PerformanceSummarySection_stage_2_Trials); + SessionPerformanceSection_ntrials_stage_today.value = value(PerformanceSummarySection_stage_2_TrialsToday); + SessionPerformanceSection_timeout_stage.value = value(PerformanceSummarySection_stage_2_TimeoutRate); + + callback(SessionPerformanceSection_ntrials); + callback(SessionPerformanceSection_ntrials_stage); + callback(SessionPerformanceSection_ntrials_stage_today) + callback(SessionPerformanceSection_violation_rate); + callback(SessionPerformanceSection_timeout_rate); + callback(SessionPerformanceSection_violation_recent); + callback(SessionPerformanceSection_timeout_recent); + callback(SessionPerformanceSection_violation_stage); + callback(SessionPerformanceSection_timeout_stage); +end + +% +end +if completion_test_eval +GetSoloFunctionArgs(obj); +ClearHelperVarsNotOwned(obj); +clear('ans'); +% +if ParamsSection_use_auto_train % do completion check if auto training + stage_no = value(SessionDefinition_CURRENT_ACTIVE_STAGE); + % only run it if its the start of the day, number of trials is small + if n_completed_trials > 50 + if value(PerformanceSummarySection_stage_2_TrialsValid) > value(TrainingStageParamsSection_total_trials) && ... + value(TrainingStageParamsSection_trial_oppSide) > value(TrainingStageParamsSection_total_trials_opp) + ParamsSection_training_stage.value = stage_no + 1; + callback(ParamsSection_training_stage); + ParamsSection(obj, 'Changed_Training_Stage'); + SessionDefinition(obj, 'jump_to_stage', 'Introduce Centre Poke'); + end + end +end +% +if exist('ans', 'var') +varargout{1}=logical(ans); clear('ans'); +else +varargout{1}=false; +end +end +if eod_logic_eval +GetSoloFunctionArgs(obj); +ClearHelperVarsNotOwned(obj); +% +PerformanceSummarySection_stage_1_TrialsToday.value = 0; +callback(PerformanceSummarySection_stage_1_TrialsToday); +PerformanceSummarySection_stage_2_TrialsToday.value = 0; +callback(PerformanceSummarySection_stage_2_TrialsToday); +PerformanceSummarySection_stage_3_TrialsToday.value = 0; +callback(PerformanceSummarySection_stage_3_TrialsToday); +PerformanceSummarySection_stage_4_TrialsToday.value = 0; +callback(PerformanceSummarySection_stage_4_TrialsToday); +PerformanceSummarySection_stage_5_TrialsToday.value = 0; +callback(PerformanceSummarySection_stage_5_TrialsToday); +PerformanceSummarySection_stage_6_TrialsToday.value = 0; +callback(PerformanceSummarySection_stage_6_TrialsToday); +PerformanceSummarySection_stage_7_TrialsToday.value = 0; +callback(PerformanceSummarySection_stage_7_TrialsToday); +PerformanceSummarySection_stage_8_TrialsToday.value = 0; +callback(PerformanceSummarySection_stage_8_TrialsToday); +% +end +% + +%% Introduce Centre Poke + +% +case 'Introduce Centre Poke' +if helper_vars_eval +GetSoloFunctionArgs(obj); +ClearHelperVarsNotOwned(obj); +% + +% +end +if stage_algorithm_eval +GetSoloFunctionArgs(obj); +ClearHelperVarsNotOwned(obj); +% +% Maximum & Minimum duration of center poke, in secs: +cp_max = value(ParamsSection_SettlingIn_time) + value(ParamsSection_legal_cbreak); +cp_min = value(ParamsSection_init_CP_duration); +% Minimum increment (in secs) in center poke duration every time there is a non-cp-violation trial: +cp_minimum_increment = 0.001; +ParamsSection_MaxSame.value = Inf; +callback(ParamsSection_MaxSame); + +stage_no = value(SessionDefinition_CURRENT_ACTIVE_STAGE); +if stage_no ~= value(ParamsSection_training_stage) + ParamsSection_training_stage.value = stage_no; + callback(ParamsSection_training_stage); +end +% Change the value of CP Duration +if value(TrainingStageParamsSection_last_session_CP) == 0 && value(PerformanceSummarySection_stage_3_Trials) < 2 + ParamsSection_CP_duration.value = cp_min; % initialize to min_CP +end + +if n_completed_trials < 1 % intialize to min value at the start of each session/day + ParamsSection_CP_duration.value = value(ParamsSection_init_CP_duration); +elseif n_completed_trials == 1 + ParamsSection_CP_duration.value = value(TrainingStageParamsSection_last_session_CP); +else + if ~timeout_history(end) && value(ParamsSection_CP_duration) < cp_max + increment = value(ParamsSection_CP_duration) * value(TrainingStageParamsSection_CPfraction_inc); + if increment < cp_minimum_increment + increment = cp_minimum_increment; + end + ParamsSection_CP_duration.value = value(ParamsSection_CP_duration) + increment; + end +end +callback(ParamsSection_CP_duration); + +if n_completed_trials > 0 + % Updating Disp Values for Training_Peformance_Summary + PerformanceSummarySection_stage_3_Trials.value = value(PerformanceSummarySection_stage_3_Trials) + 1; + PerformanceSummarySection_stage_3_TrialsToday.value = value(PerformanceSummarySection_stage_3_TrialsToday) + 1; + PerformanceSummarySection_stage_3_ViolationRate.value = nan; + PerformanceSummarySection_stage_3_TimeoutRate.value = ((value(PerformanceSummarySection_stage_3_TimeoutRate) * (value(PerformanceSummarySection_stage_3_Trials) - 1)) + double(timeout_history(end))) / value(PerformanceSummarySection_stage_3_Trials); + if ~isnan(hit_history(end)) + PerformanceSummarySection_stage_3_TrialsValid.value = value(PerformanceSummarySection_stage_3_TrialsValid) + 1; + end + + callback(PerformanceSummarySection_stage_3_Trials); + callback(PerformanceSummarySection_stage_3_TrialsToday) + callback(PerformanceSummarySection_stage_3_ViolationRate); + callback(PerformanceSummarySection_stage_3_TimeoutRate); + callback(PerformanceSummarySection_stage_3_TrialsValid); + + % Updating Disp Values for Training_Peformance_Summary + SessionPerformanceSection_ntrials.value = n_completed_trials; + SessionPerformanceSection_violation_rate.value = nan; + SessionPerformanceSection_timeout_rate.value = numel(find(timeout_history))/n_completed_trials; + SessionPerformanceSection_violation_recent.value = nan; + if n_completed_trials >= 20 + SessionPerformanceSection_timeout_recent.value = numel(find(timeout_history(end-19:end)))/20; + else + SessionPerformanceSection_timeout_recent.value = nan; + end + SessionPerformanceSection_violation_stage.value = nan; + SessionPerformanceSection_ntrials_stage.value = value(PerformanceSummarySection_stage_3_Trials); + SessionPerformanceSection_ntrials_stage_today.value = value(PerformanceSummarySection_stage_3_TrialsToday); + SessionPerformanceSection_timeout_stage.value = value(PerformanceSummarySection_stage_3_TimeoutRate); + + callback(SessionPerformanceSection_ntrials); + callback(SessionPerformanceSection_ntrials_stage); + callback(SessionPerformanceSection_ntrials_stage_today) + callback(SessionPerformanceSection_violation_rate); + callback(SessionPerformanceSection_timeout_rate); + callback(SessionPerformanceSection_violation_recent); + callback(SessionPerformanceSection_timeout_recent); + callback(SessionPerformanceSection_violation_stage); + callback(SessionPerformanceSection_timeout_stage); +end + +% +end +if completion_test_eval +GetSoloFunctionArgs(obj); +ClearHelperVarsNotOwned(obj); +clear('ans'); +% +if ParamsSection_use_auto_train % do completion check if auto training + cp_max = value(ParamsSection_SettlingIn_time) + value(ParamsSection_legal_cbreak); + if value(ParamsSection_CP_duration) >= cp_max + TrainingStageParamsSection_last_session_CP.value = value(ParamsSection_CP_duration); + callback(TrainingStageParamsSection_last_session_CP); + ParamsSection_training_stage.value = 4; + callback(ParamsSection_training_stage); + ParamsSection(obj, 'Changed_Training_Stage'); + SessionDefinition(obj, 'jump_to_stage', 'Introduce Violation for Centre Poke'); + end +end +% +if exist('ans', 'var') +varargout{1}=logical(ans); clear('ans'); +else +varargout{1}=false; +end +end +if eod_logic_eval +GetSoloFunctionArgs(obj); +ClearHelperVarsNotOwned(obj); +% +% Update the CP duration reached in this session +TrainingStageParamsSection_last_session_CP.value = value(ParamsSection_CP_duration); +callback(TrainingStageParamsSection_last_session_CP); +% Reset the number of trials done today for this stage +PerformanceSummarySection_stage_1_TrialsToday.value = 0; +callback(PerformanceSummarySection_stage_1_TrialsToday); +PerformanceSummarySection_stage_2_TrialsToday.value = 0; +callback(PerformanceSummarySection_stage_2_TrialsToday); +PerformanceSummarySection_stage_3_TrialsToday.value = 0; +callback(PerformanceSummarySection_stage_3_TrialsToday); +PerformanceSummarySection_stage_4_TrialsToday.value = 0; +callback(PerformanceSummarySection_stage_4_TrialsToday); +PerformanceSummarySection_stage_5_TrialsToday.value = 0; +callback(PerformanceSummarySection_stage_5_TrialsToday); +PerformanceSummarySection_stage_6_TrialsToday.value = 0; +callback(PerformanceSummarySection_stage_6_TrialsToday); +PerformanceSummarySection_stage_7_TrialsToday.value = 0; +callback(PerformanceSummarySection_stage_7_TrialsToday); +PerformanceSummarySection_stage_8_TrialsToday.value = 0; +callback(PerformanceSummarySection_stage_8_TrialsToday); +% +end +% + +%% Introduce Violation for Centre Poke + +% +case 'Introduce Violation for Centre Poke' +if helper_vars_eval +GetSoloFunctionArgs(obj); +ClearHelperVarsNotOwned(obj); +% + +% +end +if stage_algorithm_eval +GetSoloFunctionArgs(obj); +ClearHelperVarsNotOwned(obj); +% + +% Maximum & Minimum duration of center poke, in secs: +cp_min = value(ParamsSection_SettlingIn_time) + value(ParamsSection_legal_cbreak); +cp_max = value(TrainingStageParamsSection_max_CP); +% Fractional increment in center poke duration every time there is a non-cp-violation trial: +cp_fraction = value(TrainingStageParamsSection_CPfraction_inc); +% Minimum increment (in secs) in center poke duration every time there is a non-cp-violation trial: +cp_minimum_increment = 0.001; + +stage_no = value(SessionDefinition_CURRENT_ACTIVE_STAGE); +if stage_no ~= value(ParamsSection_training_stage) + ParamsSection_training_stage.value = stage_no; + callback(ParamsSection_training_stage); +end + +% Change the value of CP Duration +if value(PerformanceSummarySection_stage_4_Trials) < 2 + ParamsSection_CP_duration.value = cp_min; % initialize to min_CP +end + +if n_completed_trials == 0 + ParamsSection_CP_duration.value = value(ParamsSection_init_CP_duration); +elseif n_completed_trials == 1 + ParamsSection_CP_duration.value = value(TrainingStageParamsSection_last_session_CP); +else + if ~violation_history(end) && ~timeout_history(end) && value(ParamsSection_CP_duration) < cp_max + increment = value(ParamsSection_CP_duration) * cp_fraction; + if increment < cp_minimum_increment + increment = cp_minimum_increment; + end + ParamsSection_CP_duration.value = value(ParamsSection_CP_duration) + increment; + end +end +if value(ParamsSection_CP_duration) > cp_max + ParamsSection_CP_duration.value = cp_max; +end + +callback(ParamsSection_CP_duration); + +if n_completed_trials > 0 + % Updating Disp Values for Training_Peformance_Summary + PerformanceSummarySection_stage_4_Trials.value = value(PerformanceSummarySection_stage_4_Trials) + 1; + PerformanceSummarySection_stage_4_TrialsToday.value = value(PerformanceSummarySection_stage_4_TrialsToday) + 1; + PerformanceSummarySection_stage_4_ViolationRate.value = ((value(PerformanceSummarySection_stage_4_ViolationRate) * (value(PerformanceSummarySection_stage_4_Trials) - 1)) + double(violation_history(end))) / value(PerformanceSummarySection_stage_4_Trials); + PerformanceSummarySection_stage_4_TimeoutRate.value = ((value(PerformanceSummarySection_stage_4_TimeoutRate) * (value(PerformanceSummarySection_stage_4_Trials) - 1)) + double(timeout_history(end))) / value(PerformanceSummarySection_stage_4_Trials); + if ~isnan(hit_history(end)) + PerformanceSummarySection_stage_4_TrialsValid.value = value(PerformanceSummarySection_stage_4_TrialsValid) + 1; + end + + callback(PerformanceSummarySection_stage_4_Trials); + callback(PerformanceSummarySection_stage_4_TrialsToday) + callback(PerformanceSummarySection_stage_4_ViolationRate); + callback(PerformanceSummarySection_stage_4_TimeoutRate); + callback(PerformanceSummarySection_stage_4_TrialsValid); + + % Updating Disp Values for Training_Peformance_Summary + SessionPerformanceSection_ntrials.value = n_completed_trials; + SessionPerformanceSection_violation_rate.value = numel(find(violation_history))/n_completed_trials; + SessionPerformanceSection_timeout_rate.value = numel(find(timeout_history))/n_completed_trials; + if n_completed_trials >= 20 + SessionPerformanceSection_violation_recent.value = numel(find(violation_history(end-19:end)))/20; + SessionPerformanceSection_timeout_recent.value = numel(find(timeout_history(end-19:end)))/20; + else + SessionPerformanceSection_timeout_recent.value = nan; + SessionPerformanceSection_violation_recent.value = nan; + end + SessionPerformanceSection_violation_stage.value = value(PerformanceSummarySection_stage_4_ViolationRate); + SessionPerformanceSection_ntrials_stage.value = value(PerformanceSummarySection_stage_4_Trials); + SessionPerformanceSection_ntrials_stage_today.value = value(PerformanceSummarySection_stage_4_TrialsToday); + SessionPerformanceSection_timeout_stage.value = value(PerformanceSummarySection_stage_4_TimeoutRate); + + callback(SessionPerformanceSection_ntrials); + callback(SessionPerformanceSection_ntrials_stage); + callback(SessionPerformanceSection_ntrials_stage_today) + callback(SessionPerformanceSection_violation_rate); + callback(SessionPerformanceSection_timeout_rate); + callback(SessionPerformanceSection_violation_recent); + callback(SessionPerformanceSection_timeout_recent); + callback(SessionPerformanceSection_violation_stage); + callback(SessionPerformanceSection_timeout_stage); +end +% +end +if completion_test_eval +GetSoloFunctionArgs(obj); +ClearHelperVarsNotOwned(obj); +clear('ans'); +% +if ParamsSection_use_auto_train % do completion check if auto training + cp_max = value(TrainingStageParamsSection_max_CP); + if value(ParamsSection_CP_duration) >= cp_max && n_completed_trials > 100 && ... + value(SessionPerformanceSection_violation_recent) < value(TrainingStageParamsSection_recent_violation) && ... + value(SessionPerformanceSection_timeout_recent) < value(TrainingStageParamsSection_recent_timeout) && ... + value(SessionPerformanceSection_violation_stage) < value(TrainingStageParamsSection_stage_violation) + + ParamsSection_training_stage.value = 5; + callback(ParamsSection_training_stage); + ParamsSection(obj, 'Changed_Training_Stage'); + SessionDefinition(obj, 'jump_to_stage', 'Introduce Stimuli Sound during Centre Poke'); + TrainingStageParamsSection_last_session_CP.value = value(ParamsSection_CP_duration); + callback(TrainingStageParamsSection_last_session_CP); + end +end +% +if exist('ans', 'var') +varargout{1}=logical(ans); clear('ans'); +else +varargout{1}=false; +end +end +if eod_logic_eval +GetSoloFunctionArgs(obj); +ClearHelperVarsNotOwned(obj); +% +% Update the CP duration reached in this session +TrainingStageParamsSection_last_session_CP.value = value(ParamsSection_CP_duration); +callback(TrainingStageParamsSection_last_session_CP); +% Reset the number of trials done today for this stage +PerformanceSummarySection_stage_1_TrialsToday.value = 0; +callback(PerformanceSummarySection_stage_1_TrialsToday); +PerformanceSummarySection_stage_2_TrialsToday.value = 0; +callback(PerformanceSummarySection_stage_2_TrialsToday); +PerformanceSummarySection_stage_3_TrialsToday.value = 0; +callback(PerformanceSummarySection_stage_3_TrialsToday); +PerformanceSummarySection_stage_4_TrialsToday.value = 0; +callback(PerformanceSummarySection_stage_4_TrialsToday); +PerformanceSummarySection_stage_5_TrialsToday.value = 0; +callback(PerformanceSummarySection_stage_5_TrialsToday); +PerformanceSummarySection_stage_6_TrialsToday.value = 0; +callback(PerformanceSummarySection_stage_6_TrialsToday); +PerformanceSummarySection_stage_7_TrialsToday.value = 0; +callback(PerformanceSummarySection_stage_7_TrialsToday); +PerformanceSummarySection_stage_8_TrialsToday.value = 0; +callback(PerformanceSummarySection_stage_8_TrialsToday); +% +end +% + +%% Introduce Stimuli Sound during Centre Poke + +% +case 'Introduce Stimuli Sound during Centre Poke' +if helper_vars_eval +GetSoloFunctionArgs(obj); +ClearHelperVarsNotOwned(obj); +% + +% +end +if stage_algorithm_eval +GetSoloFunctionArgs(obj); +ClearHelperVarsNotOwned(obj); +% +cp_max = value(TrainingStageParamsSection_max_CP); +cp_min = value(TrainingStageParamsSection_min_CP); +% Fractional increment in center poke duration every time there is a non-cp-violation trial: +cp_fraction = value(TrainingStageParamsSection_CPfraction_inc); +% Minimum increment (in secs) in center poke duration every time there is a non-cp-violation trial: +cp_minimum_increment = 0.001; +% Starting total center poke duration: +starting_cp = value(TrainingStageParamsSection_starting_CP) + value(ParamsSection_SettlingIn_time); +% number of warm-up trials +n_trial_warmup = value(TrainingStageParamsSection_warm_up_trials); + +stage_no = value(SessionDefinition_CURRENT_ACTIVE_STAGE); +if stage_no ~= value(ParamsSection_training_stage) + ParamsSection_training_stage.value = stage_no; + callback(ParamsSection_training_stage); +end +% Change the value of CP Duration +% Since starting a new session then do a pre warm up to last saved cp +% duration else continue with learning with increased poke time +if n_completed_trials == 0 + ParamsSection_CP_duration.value = value(ParamsSection_init_CP_duration); +elseif n_completed_trials == 1 + ParamsSection_CP_duration.value = starting_cp; +else + if ~violation_history(end) && ~timeout_history(end) + if value(ParamsSection_CP_duration) < max([cp_min,value(TrainingStageParamsSection_last_session_CP)]) % warm up stage + increment = (max([cp_min,value(TrainingStageParamsSection_last_session_CP)]) - value(ParamsSection_CP_duration))/ (n_trial_warmup - 1); + else + if value(ParamsSection_CP_duration) >= value(TrainingStageParamsSection_last_session_CP) && value(ParamsSection_CP_duration) <= cp_max % no warm up stage + increment = value(ParamsSection_CP_duration)*cp_fraction; + if increment < cp_minimum_increment + increment = cp_minimum_increment; + end + end + end + + ParamsSection_CP_duration.value = value(ParamsSection_CP_duration) + increment; + + % Check if the values are within the required range + if value(ParamsSection_CP_duration) < starting_cp + ParamsSection_CP_duration.value = starting_cp; + end + if value(ParamsSection_CP_duration) > cp_max + ParamsSection_CP_duration.value = cp_max; + end + + end +end +callback(ParamsSection_CP_duration); + +if value(ParamsSection_CP_duration) >= 1 + ParamsSection_PreStim_time.value = 0.4; + if value(ParamsSection_CP_duration) < 2 + ParamsSection_A1_time.value = 0.1; + elseif value(ParamsSection_CP_duration) < 2.5 && value(ParamsSection_CP_duration) >= 2 + ParamsSection_A1_time.value = 0.2; + elseif value(ParamsSection_CP_duration) < 3 && value(ParamsSection_CP_duration) >= 2.5 + ParamsSection_A1_time.value = 0.3; + else + ParamsSection_A1_time.value = 0.4; + end +elseif value(ParamsSection_CP_duration) < 1 && value(ParamsSection_CP_duration) >= starting_cp + ParamsSection_SettlingIn_time.value = 0.2; + callback(ParamsSection_SettlingIn_time); + ParamsSection_PreStim_time.value = 0.1; + ParamsSection_A1_time.value = 0.1; +end + +if value(ParamsSection_CP_duration) >= starting_cp + ParamsSection_time_bet_aud1_gocue.value = value(ParamsSection_CP_duration) - value(ParamsSection_SettlingIn_time) - value(ParamsSection_A1_time) - value(ParamsSection_PreStim_time); + callback(ParamsSection_time_bet_aud1_gocue) + callback(ParamsSection_PreStim_time); + callback(ParamsSection_A1_time); +end + +if n_completed_trials > 0 + % Updating Disp Values for Training_Peformance_Summary + PerformanceSummarySection_stage_5_Trials.value = value(PerformanceSummarySection_stage_5_Trials) + 1; + PerformanceSummarySection_stage_5_TrialsToday.value = value(PerformanceSummarySection_stage_5_TrialsToday) + 1; + PerformanceSummarySection_stage_5_ViolationRate.value = ((value(PerformanceSummarySection_stage_5_ViolationRate) * (value(PerformanceSummarySection_stage_5_Trials) - 1)) + double(violation_history(end))) / value(PerformanceSummarySection_stage_5_Trials); + PerformanceSummarySection_stage_5_TimeoutRate.value = ((value(PerformanceSummarySection_stage_5_TimeoutRate) * (value(PerformanceSummarySection_stage_5_Trials) - 1)) + double(timeout_history(end))) / value(PerformanceSummarySection_stage_5_Trials); + if ~isnan(hit_history(end)) + PerformanceSummarySection_stage_5_TrialsValid.value = value(PerformanceSummarySection_stage_5_TrialsValid) + 1; + end + + callback(PerformanceSummarySection_stage_5_Trials); + callback(PerformanceSummarySection_stage_5_TrialsToday) + callback(PerformanceSummarySection_stage_5_ViolationRate); + callback(PerformanceSummarySection_stage_5_TimeoutRate); + callback(PerformanceSummarySection_stage_5_TrialsValid); + + % Updating Disp Values for Training_Peformance_Summary + SessionPerformanceSection_ntrials.value = n_completed_trials; + SessionPerformanceSection_violation_rate.value = numel(find(violation_history))/n_completed_trials; + SessionPerformanceSection_timeout_rate.value = numel(find(timeout_history))/n_completed_trials; + if n_completed_trials >= 20 + SessionPerformanceSection_violation_recent.value = numel(find(violation_history(end-19:end)))/20; + SessionPerformanceSection_timeout_recent.value = numel(find(timeout_history(end-19:end)))/20; + else + SessionPerformanceSection_timeout_recent.value = nan; + SessionPerformanceSection_violation_recent.value = nan; + end + SessionPerformanceSection_violation_stage.value = value(PerformanceSummarySection_stage_5_ViolationRate); + SessionPerformanceSection_ntrials_stage.value = value(PerformanceSummarySection_stage_5_Trials); + SessionPerformanceSection_ntrials_stage_today.value = value(PerformanceSummarySection_stage_5_TrialsToday); + SessionPerformanceSection_timeout_stage.value = value(PerformanceSummarySection_stage_5_TimeoutRate); + + callback(SessionPerformanceSection_ntrials); + callback(SessionPerformanceSection_ntrials_stage); + callback(SessionPerformanceSection_ntrials_stage_today) + callback(SessionPerformanceSection_violation_rate); + callback(SessionPerformanceSection_timeout_rate); + callback(SessionPerformanceSection_violation_recent); + callback(SessionPerformanceSection_timeout_recent); + callback(SessionPerformanceSection_violation_stage); + callback(SessionPerformanceSection_timeout_stage); +end + +% +end +if completion_test_eval +GetSoloFunctionArgs(obj); +ClearHelperVarsNotOwned(obj); +clear('ans'); +% +if ParamsSection_use_auto_train % do completion check if auto training + stage_no = value(SessionDefinition_CURRENT_ACTIVE_STAGE); + if value(ParamsSection_CP_duration) >= value(TrainingStageParamsSection_max_CP) && value(PerformanceSummarySection_stage_5_TrialsValid) > value(TrainingStageParamsSection_total_trials) + if value(SessionPerformanceSection_violation_recent) < value(TrainingStageParamsSection_recent_violation) && value(SessionPerformanceSection_timeout_recent) < value(TrainingStageParamsSection_recent_timeout) && ... + value(SessionPerformanceSection_violation_stage) < value(TrainingStageParamsSection_stage_violation) && n_completed_trials > 100 + ParamsSection_training_stage.value = stage_no + 1; + callback(ParamsSection_training_stage); + ParamsSection(obj, 'Changed_Training_Stage'); + SessionDefinition(obj, 'jump_to_stage', 'Vary Stimuli location during Centre Poke'); + TrainingStageParamsSection_last_session_CP.value = value(ParamsSection_CP_duration); + end + end +end +% +if exist('ans', 'var') +varargout{1}=logical(ans); clear('ans'); +else +varargout{1}=false; +end +end +if eod_logic_eval +GetSoloFunctionArgs(obj); +ClearHelperVarsNotOwned(obj); +% +% Update the CP duration reached in this session +TrainingStageParamsSection_last_session_CP.value = value(ParamsSection_CP_duration); +callback(TrainingStageParamsSection_last_session_CP); +% Reset the number of trials done today for this stage +PerformanceSummarySection_stage_1_TrialsToday.value = 0; +callback(PerformanceSummarySection_stage_1_TrialsToday); +PerformanceSummarySection_stage_2_TrialsToday.value = 0; +callback(PerformanceSummarySection_stage_2_TrialsToday); +PerformanceSummarySection_stage_3_TrialsToday.value = 0; +callback(PerformanceSummarySection_stage_3_TrialsToday); +PerformanceSummarySection_stage_4_TrialsToday.value = 0; +callback(PerformanceSummarySection_stage_4_TrialsToday); +PerformanceSummarySection_stage_5_TrialsToday.value = 0; +callback(PerformanceSummarySection_stage_5_TrialsToday); +PerformanceSummarySection_stage_6_TrialsToday.value = 0; +callback(PerformanceSummarySection_stage_6_TrialsToday); +PerformanceSummarySection_stage_7_TrialsToday.value = 0; +callback(PerformanceSummarySection_stage_7_TrialsToday); +PerformanceSummarySection_stage_8_TrialsToday.value = 0; +callback(PerformanceSummarySection_stage_8_TrialsToday); +% +end +% + +%% Vary Stimuli location during Centre Poke + +% +case 'Vary Stimuli location during Centre Poke' +if helper_vars_eval +GetSoloFunctionArgs(obj); +ClearHelperVarsNotOwned(obj); +% + +% +end +if stage_algorithm_eval +GetSoloFunctionArgs(obj); +ClearHelperVarsNotOwned(obj); +% +cp_max = value(TrainingStageParamsSection_max_CP); +% Starting total center poke duration: +starting_cp = value(TrainingStageParamsSection_starting_CP) + value(ParamsSection_SettlingIn_time); +% number of warm-up trials +n_trial_warmup = value(TrainingStageParamsSection_warm_up_trials); +prestim_min = value(TrainingStageParamsSection_min_prestim); +prestim_max = value(TrainingStageParamsSection_max_prestim); +stim_dur = value(TrainingStageParamsSection_stim_dur); + +stage_no = value(SessionDefinition_CURRENT_ACTIVE_STAGE); +if stage_no ~= value(ParamsSection_training_stage) + ParamsSection_training_stage.value = stage_no; + callback(ParamsSection_training_stage); +end + +% Warm Up If starting a new session +if n_completed_trials == 0 + ParamsSection_CP_duration.value = value(ParamsSection_init_CP_duration); +elseif n_completed_trials == 1 + ParamsSection_CP_duration.value = starting_cp; +else + if value(ParamsSection_CP_duration) < cp_max % warm up stage + if ~violation_history(end) && ~timeout_history(end) + increment = (cp_max - value(ParamsSection_CP_duration)) / (n_trial_warmup - 1); + ParamsSection_CP_duration.value = value(ParamsSection_CP_duration) + increment; + % Check if the values are within the required range + if value(ParamsSection_CP_duration) < starting_cp + ParamsSection_CP_duration.value = starting_cp; + end + if value(ParamsSection_CP_duration) > cp_max + ParamsSection_CP_duration.value = cp_max; + end + end + end +end + +callback(ParamsSection_CP_duration); + +if value(ParamsSection_CP_duration) < 3 && value(ParamsSection_CP_duration) >= starting_cp % during the warm up phase + ParamsSection_SettlingIn_time.value = 0.2; + callback(ParamsSection_SettlingIn_time); + ParamsSection_PreStim_time.value = 0.1; + ParamsSection_A1_time.value = 0.1; +else + ParamsSection_A1_time.value = stim_dur; % actual training stage + time_range_PreStim_time = prestim_min : 0.01 : prestim_max; + ParamsSection_PreStim_time.value = time_range_PreStim_time(randi([1, numel(time_range_PreStim_time)],1,1)); +end + +if value(ParamsSection_CP_duration) >= starting_cp + ParamsSection_time_bet_aud1_gocue.value = value(ParamsSection_CP_duration) - value(ParamsSection_SettlingIn_time) - value(ParamsSection_A1_time) - value(ParamsSection_PreStim_time); + callback(ParamsSection_time_bet_aud1_gocue) + callback(ParamsSection_PreStim_time); + callback(ParamsSection_A1_time); +end + +if n_completed_trials > 0 + % Updating Disp Values for Training_Peformance_Summary + PerformanceSummarySection_stage_6_Trials.value = value(PerformanceSummarySection_stage_6_Trials) + 1; + PerformanceSummarySection_stage_6_TrialsToday.value = value(PerformanceSummarySection_stage_6_TrialsToday) + 1; + PerformanceSummarySection_stage_6_ViolationRate.value = ((value(PerformanceSummarySection_stage_6_ViolationRate) * (value(PerformanceSummarySection_stage_6_Trials) - 1)) + double(violation_history(end))) / value(PerformanceSummarySection_stage_6_Trials); + PerformanceSummarySection_stage_6_TimeoutRate.value = ((value(PerformanceSummarySection_stage_6_TimeoutRate) * (value(PerformanceSummarySection_stage_6_Trials) - 1)) + double(timeout_history(end))) / value(PerformanceSummarySection_stage_6_Trials); + if ~isnan(hit_history(end)) + PerformanceSummarySection_stage_6_TrialsValid.value = value(PerformanceSummarySection_stage_6_TrialsValid) + 1; + end + + callback(PerformanceSummarySection_stage_6_Trials); + callback(PerformanceSummarySection_stage_6_TrialsToday) + callback(PerformanceSummarySection_stage_6_ViolationRate); + callback(PerformanceSummarySection_stage_6_TimeoutRate); + callback(PerformanceSummarySection_stage_6_TrialsValid); + + % Updating Disp Values for SessionPerformanceSection + SessionPerformanceSection_ntrials.value = n_completed_trials; + SessionPerformanceSection_violation_rate.value = numel(find(violation_history))/n_completed_trials; + SessionPerformanceSection_timeout_rate.value = numel(find(timeout_history))/n_completed_trials; + if n_completed_trials >= 20 + SessionPerformanceSection_violation_recent.value = numel(find(violation_history(end-19:end)))/20; + SessionPerformanceSection_timeout_recent.value = numel(find(timeout_history(end-19:end)))/20; + else + SessionPerformanceSection_timeout_recent.value = nan; + SessionPerformanceSection_violation_recent.value = nan; + end + SessionPerformanceSection_violation_stage.value = value(PerformanceSummarySection_stage_6_ViolationRate); + SessionPerformanceSection_ntrials_stage.value = value(PerformanceSummarySection_stage_6_Trials); + SessionPerformanceSection_ntrials_stage_today.value = value(PerformanceSummarySection_stage_6_TrialsToday); + SessionPerformanceSection_timeout_stage.value = value(PerformanceSummarySection_stage_6_TimeoutRate); + + callback(SessionPerformanceSection_ntrials); + callback(SessionPerformanceSection_ntrials_stage); + callback(SessionPerformanceSection_ntrials_stage_today) + callback(SessionPerformanceSection_violation_rate); + callback(SessionPerformanceSection_timeout_rate); + callback(SessionPerformanceSection_violation_recent); + callback(SessionPerformanceSection_timeout_recent); + callback(SessionPerformanceSection_violation_stage); + callback(SessionPerformanceSection_timeout_stage); +end + +% +end +if completion_test_eval +GetSoloFunctionArgs(obj); +ClearHelperVarsNotOwned(obj); +clear('ans'); +% +if ParamsSection_use_auto_train % do completion check if auto training + stage_no = value(SessionDefinition_CURRENT_ACTIVE_STAGE); + if value(PerformanceSummarySection_stage_6_TrialsValid) > value(TrainingStageParamsSection_total_trials) + if value(SessionPerformanceSection_violation_recent) < value(TrainingStageParamsSection_recent_violation) && value(SessionPerformanceSection_timeout_recent) < value(TrainingStageParamsSection_recent_timeout) && ... + value(SessionPerformanceSection_violation_stage) < value(TrainingStageParamsSection_stage_violation) && n_completed_trials > 100 + ParamsSection_training_stage.value = stage_no + 1; + callback(ParamsSection_training_stage); + ParamsSection(obj, 'Changed_Training_Stage'); + SessionDefinition(obj, 'jump_to_stage', 'Variable Stimuli Go Cue location during Centre Poke'); + end + end +end +% +if exist('ans', 'var') +varargout{1}=logical(ans); clear('ans'); +else +varargout{1}=false; +end +end +if eod_logic_eval +GetSoloFunctionArgs(obj); +ClearHelperVarsNotOwned(obj); +% +% Reset the number of trials done today for this stage +PerformanceSummarySection_stage_1_TrialsToday.value = 0; +callback(PerformanceSummarySection_stage_1_TrialsToday); +PerformanceSummarySection_stage_2_TrialsToday.value = 0; +callback(PerformanceSummarySection_stage_2_TrialsToday); +PerformanceSummarySection_stage_3_TrialsToday.value = 0; +callback(PerformanceSummarySection_stage_3_TrialsToday); +PerformanceSummarySection_stage_4_TrialsToday.value = 0; +callback(PerformanceSummarySection_stage_4_TrialsToday); +PerformanceSummarySection_stage_5_TrialsToday.value = 0; +callback(PerformanceSummarySection_stage_5_TrialsToday); +PerformanceSummarySection_stage_6_TrialsToday.value = 0; +callback(PerformanceSummarySection_stage_6_TrialsToday); +PerformanceSummarySection_stage_7_TrialsToday.value = 0; +callback(PerformanceSummarySection_stage_7_TrialsToday); +PerformanceSummarySection_stage_8_TrialsToday.value = 0; +callback(PerformanceSummarySection_stage_8_TrialsToday); +% +end +% + +%% Variable Stimuli Go Cue location during Centre Poke + +% +case 'Variable Stimuli Go Cue location during Centre Poke' +if helper_vars_eval +GetSoloFunctionArgs(obj); +ClearHelperVarsNotOwned(obj); +% + +% +end +if stage_algorithm_eval +GetSoloFunctionArgs(obj); +ClearHelperVarsNotOwned(obj); +% +% Variables for warmup stage +cp_max = value(TrainingStageParamsSection_max_CP); +% Starting total center poke duration: +starting_cp = value(TrainingStageParamsSection_starting_CP) + value(ParamsSection_SettlingIn_time); +% number of warm-up trials +n_trial_warmup = value(TrainingStageParamsSection_warm_up_trials); +prestim_min = value(TrainingStageParamsSection_min_prestim); +prestim_max = value(TrainingStageParamsSection_max_prestim); +prestim_time = value(TrainingStageParamsSection_min_prestim); +a1_time = value(TrainingStageParamsSection_stim_dur); +a1_time_min = value(TrainingStageParamsSection_stim_dur); +a1_time_max = value(TrainingStageParamsSection_stim_dur + 0.1); +prego_min = value(TrainingStageParamsSection_min_prego); +prego_max = value(TrainingStageParamsSection_max_prego); +prego_time = value(TrainingStageParamsSection_min_prego); +warmup_completed = 0; +warm_up_on = 1; +random_prestim = 1; +random_prego = 1; +random_A1 = 0; + +stage_no = value(SessionDefinition_CURRENT_ACTIVE_STAGE); +if stage_no ~= value(ParamsSection_training_stage) + ParamsSection_training_stage.value = stage_no; + callback(ParamsSection_training_stage); +end + +% Warm Up If starting a new session +if warm_up_on == 1 + if n_completed_trials == 0 + ParamsSection_CP_duration.value = value(ParamsSection_init_CP_duration); + warmup_completed = 0; + elseif n_completed_trials == 1 + ParamsSection_CP_duration.value = starting_cp; + else + if value(ParamsSection_CP_duration) <= cp_max % warm up stage + if ~violation_history(end) && ~timeout_history(end) + increment = (cp_max - value(ParamsSection_CP_duration))/ (n_trial_warmup - 1); + ParamsSection_CP_duration.value = value(ParamsSection_CP_duration) + increment; + % Check if the values are within the required range + if value(ParamsSection_CP_duration) < starting_cp + ParamsSection_CP_duration.value = starting_cp; + end + if value(ParamsSection_CP_duration) >= cp_max + ParamsSection_CP_duration.value = cp_max; + warmup_completed = 1; + end + end + end + end +else + warmup_completed = 1; +end + +if n_completed_trials >= 1 + cp_length = value(ParamsSection_CP_duration) - value(ParamsSection_SettlingIn_time); + [ParamsSection_PreStim_time.value ,ParamsSection_A1_time.value,ParamsSection_time_bet_aud1_gocue.value] = param_time_within_range(warmup_completed,... + cp_length,prestim_min,prestim_max, random_prestim, prestim_time,... + a1_time_min,a1_time_max, random_A1, a1_time,prego_min,prego_max, random_prego, prego_time); + + callback(ParamsSection_PreStim_time); + callback(ParamsSection_A1_time); + callback(ParamsSection_time_bet_aud1_gocue); + ParamsSection_CP_duration.value = value(ParamsSection_SettlingIn_time) + value(ParamsSection_PreStim_time) + value(ParamsSection_A1_time) + value(ParamsSection_time_bet_aud1_gocue); +end +callback(ParamsSection_CP_duration); + +if n_completed_trials > 0 + % Updating Disp Values for Training_Peformance_Summary + PerformanceSummarySection_stage_7_Trials.value = value(PerformanceSummarySection_stage_7_Trials) + 1; + PerformanceSummarySection_stage_7_TrialsToday.value = value(PerformanceSummarySection_stage_7_TrialsToday) + 1; + PerformanceSummarySection_stage_7_ViolationRate.value = ((value(PerformanceSummarySection_stage_7_ViolationRate) * (value(PerformanceSummarySection_stage_7_Trials) - 1)) + double(violation_history(end))) / value(PerformanceSummarySection_stage_7_Trials); + PerformanceSummarySection_stage_7_TimeoutRate.value = ((value(PerformanceSummarySection_stage_7_TimeoutRate) * (value(PerformanceSummarySection_stage_7_Trials) - 1)) + double(timeout_history(end))) / value(PerformanceSummarySection_stage_7_Trials); + if ~isnan(hit_history(end)) + PerformanceSummarySection_stage_7_TrialsValid.value = value(PerformanceSummarySection_stage_7_TrialsValid) + 1; + end + + callback(PerformanceSummarySection_stage_7_Trials); + callback(PerformanceSummarySection_stage_7_TrialsToday) + callback(PerformanceSummarySection_stage_7_ViolationRate); + callback(PerformanceSummarySection_stage_7_TimeoutRate); + callback(PerformanceSummarySection_stage_7_TrialsValid); + + % Updating Disp Values for SessionPerformanceSection + SessionPerformanceSection_ntrials.value = n_completed_trials; + SessionPerformanceSection_violation_rate.value = numel(find(violation_history))/n_completed_trials; + SessionPerformanceSection_timeout_rate.value = numel(find(timeout_history))/n_completed_trials; + if n_completed_trials >= 20 + SessionPerformanceSection_violation_recent.value = numel(find(violation_history(end-19:end)))/20; + SessionPerformanceSection_timeout_recent.value = numel(find(timeout_history(end-19:end)))/20; + else + SessionPerformanceSection_timeout_recent.value = nan; + SessionPerformanceSection_violation_recent.value = nan; + end + SessionPerformanceSection_violation_stage.value = value(PerformanceSummarySection_stage_7_ViolationRate); + SessionPerformanceSection_ntrials_stage.value = value(PerformanceSummarySection_stage_7_Trials); + SessionPerformanceSection_ntrials_stage_today.value = value(PerformanceSummarySection_stage_7_TrialsToday); + SessionPerformanceSection_timeout_stage.value = value(PerformanceSummarySection_stage_7_TimeoutRate); + + callback(SessionPerformanceSection_ntrials); + callback(SessionPerformanceSection_ntrials_stage); + callback(SessionPerformanceSection_ntrials_stage_today) + callback(SessionPerformanceSection_violation_rate); + callback(SessionPerformanceSection_timeout_rate); + callback(SessionPerformanceSection_violation_recent); + callback(SessionPerformanceSection_timeout_recent); + callback(SessionPerformanceSection_violation_stage); + callback(SessionPerformanceSection_timeout_stage); +end + +% +end +if completion_test_eval +GetSoloFunctionArgs(obj); +ClearHelperVarsNotOwned(obj); +clear('ans'); +% +stage_no = value(SessionDefinition_CURRENT_ACTIVE_STAGE); + +if value(PerformanceSummarySection_stage_7_TrialsValid) > value(TrainingStageParamsSection_total_trials) + if value(SessionPerformanceSection_violation_recent) < value(TrainingStageParamsSection_recent_violation) && value(SessionPerformanceSection_timeout_recent) < value(TrainingStageParamsSection_recent_timeout) && ... + value(SessionPerformanceSection_violation_stage) < value(TrainingStageParamsSection_stage_violation) + ParamsSection_training_stage.value = stage_no + 1; + callback(ParamsSection_training_stage); + ParamsSection(obj, 'Changed_Training_Stage'); + SessionDefinition(obj, 'jump_to_stage', 'User Setting'); + end +end +% +if exist('ans', 'var') +varargout{1}=logical(ans); clear('ans'); +else +varargout{1}=false; +end +end +if eod_logic_eval +GetSoloFunctionArgs(obj); +ClearHelperVarsNotOwned(obj); +% +PerformanceSummarySection_stage_1_TrialsToday.value = 0; +callback(PerformanceSummarySection_stage_1_TrialsToday); +PerformanceSummarySection_stage_2_TrialsToday.value = 0; +callback(PerformanceSummarySection_stage_2_TrialsToday); +PerformanceSummarySection_stage_3_TrialsToday.value = 0; +callback(PerformanceSummarySection_stage_3_TrialsToday); +PerformanceSummarySection_stage_4_TrialsToday.value = 0; +callback(PerformanceSummarySection_stage_4_TrialsToday); +PerformanceSummarySection_stage_5_TrialsToday.value = 0; +callback(PerformanceSummarySection_stage_5_TrialsToday); +PerformanceSummarySection_stage_6_TrialsToday.value = 0; +callback(PerformanceSummarySection_stage_6_TrialsToday); +PerformanceSummarySection_stage_7_TrialsToday.value = 0; +callback(PerformanceSummarySection_stage_7_TrialsToday); +PerformanceSummarySection_stage_8_TrialsToday.value = 0; +callback(PerformanceSummarySection_stage_8_TrialsToday); +% +end +% + +%% User Setting + +% +case 'User Setting' +if helper_vars_eval +GetSoloFunctionArgs(obj); +ClearHelperVarsNotOwned(obj); +% + +% +end +if stage_algorithm_eval +GetSoloFunctionArgs(obj); +ClearHelperVarsNotOwned(obj); +% +% Variables for warmup stage +cp_max = 5; +n_trial_warmup = 20; +starting_cp = 0.5; +warmup_completed = 0; +warm_up_on = value(ParamsSection_warmup_on); +random_prestim = value(ParamsSection_random_PreStim_time); +random_prego = value(ParamsSection_random_prego_time); +random_A1 = value(ParamsSection_random_A1_time); +prestim_min = value(ParamsSection_PreStim_time_Min); +prestim_max = value(ParamsSection_PreStim_time_Max); +prestim_time = value(ParamsSection_PreStim_time); +prego_min = value(ParamsSection_time_bet_aud1_gocue_Min); +prego_max = value(ParamsSection_time_bet_aud1_gocue_Max); +prego_time = value(ParamsSection_time_bet_aud1_gocue); +a1_time = value(ParamsSection_A1_time); +a1_time_min = value(ParamsSection_A1_time_Min); +a1_time_max = value(ParamsSection_A1_time_Max); + +stage_no = value(SessionDefinition_CURRENT_ACTIVE_STAGE); +if stage_no ~= value(ParamsSection_training_stage) + ParamsSection_training_stage.value = stage_no; + callback(ParamsSection_training_stage); +end + +% Warm Up If starting a new session +if warm_up_on == 1 + if n_completed_trials == 0 + ParamsSection_CP_duration.value = value(ParamsSection_init_CP_duration); + warmup_completed = 0; + elseif n_completed_trials == 1 + ParamsSection_CP_duration.value = starting_cp; + else + if value(ParamsSection_CP_duration) <= cp_max % warm up stage + if ~violation_history(end) && ~timeout_history(end) + increment = (cp_max - value(ParamsSection_CP_duration))/ (n_trial_warmup - 1); + ParamsSection_CP_duration.value = value(ParamsSection_CP_duration) + increment; + % Check if the values are within the required range + if value(ParamsSection_CP_duration) < starting_cp + ParamsSection_CP_duration.value = starting_cp; + end + if value(ParamsSection_CP_duration) >= cp_max + ParamsSection_CP_duration.value = cp_max; + warmup_completed = 1; + end + end + end + end +else + warmup_completed = 1; +end + +if n_completed_trials >= 1 + cp_length = value(ParamsSection_CP_duration) - value(ParamsSection_SettlingIn_time); + [ParamsSection_PreStim_time.value ,ParamsSection_A1_time.value,ParamsSection_time_bet_aud1_gocue.value] = param_time_within_range(warmup_completed,... + cp_length,prestim_min,prestim_max, random_prestim, prestim_time,... + a1_time_min,a1_time_max, random_A1, a1_time,prego_min,prego_max, random_prego, prego_time); + + callback(ParamsSection_PreStim_time); + callback(ParamsSection_A1_time); + callback(ParamsSection_time_bet_aud1_gocue); + ParamsSection_CP_duration.value = value(ParamsSection_SettlingIn_time) + value(ParamsSection_PreStim_time) + value(ParamsSection_A1_time) + value(ParamsSection_time_bet_aud1_gocue); +end +callback(ParamsSection_CP_duration); + +if n_completed_trials > 0 + % Updating Disp Values for Training_Peformance_Summary + PerformanceSummarySection_stage_8_Trials.value = value(PerformanceSummarySection_stage_8_Trials) + 1; + PerformanceSummarySection_stage_8_TrialsToday.value = value(PerformanceSummarySection_stage_8_TrialsToday) + 1; + PerformanceSummarySection_stage_8_ViolationRate.value = ((value(PerformanceSummarySection_stage_8_ViolationRate) * (value(PerformanceSummarySection_stage_8_Trials) - 1)) + double(violation_history(end))) / value(PerformanceSummarySection_stage_8_Trials); + PerformanceSummarySection_stage_8_TimeoutRate.value = ((value(PerformanceSummarySection_stage_8_TimeoutRate) * (value(PerformanceSummarySection_stage_8_Trials) - 1)) + double(timeout_history(end))) / value(PerformanceSummarySection_stage_8_Trials); + if ~isnan(hit_history(end)) + PerformanceSummarySection_stage_8_TrialsValid.value = value(PerformanceSummarySection_stage_8_TrialsValid) + 1; + end + + callback(PerformanceSummarySection_stage_8_Trials); + callback(PerformanceSummarySection_stage_8_TrialsToday) + callback(PerformanceSummarySection_stage_8_ViolationRate); + callback(PerformanceSummarySection_stage_8_TimeoutRate); + callback(PerformanceSummarySection_stage_8_TrialsValid); + + % Updating Disp Values for SessionPerformanceSection + SessionPerformanceSection_ntrials.value = n_completed_trials; + SessionPerformanceSection_violation_rate.value = numel(find(violation_history))/n_completed_trials; + SessionPerformanceSection_timeout_rate.value = numel(find(timeout_history))/n_completed_trials; + if n_completed_trials >= 20 + SessionPerformanceSection_violation_recent.value = numel(find(violation_history(end-19:end)))/20; + SessionPerformanceSection_timeout_recent.value = numel(find(timeout_history(end-19:end)))/20; + else + SessionPerformanceSection_timeout_recent.value = nan; + SessionPerformanceSection_violation_recent.value = nan; + end + SessionPerformanceSection_violation_stage.value = value(PerformanceSummarySection_stage_8_ViolationRate); + SessionPerformanceSection_ntrials_stage.value = value(PerformanceSummarySection_stage_8_Trials); + SessionPerformanceSection_ntrials_stage_today.value = value(PerformanceSummarySection_stage_8_TrialsToday); + SessionPerformanceSection_timeout_stage.value = value(PerformanceSummarySection_stage_8_TimeoutRate); + + callback(SessionPerformanceSection_ntrials); + callback(SessionPerformanceSection_ntrials_stage); + callback(SessionPerformanceSection_ntrials_stage_today) + callback(SessionPerformanceSection_violation_rate); + callback(SessionPerformanceSection_timeout_rate); + callback(SessionPerformanceSection_violation_recent); + callback(SessionPerformanceSection_timeout_recent); + callback(SessionPerformanceSection_violation_stage); + callback(SessionPerformanceSection_timeout_stage); +end + +% +end +if completion_test_eval +GetSoloFunctionArgs(obj); +ClearHelperVarsNotOwned(obj); +clear('ans'); +% + +% +if exist('ans', 'var') +varargout{1}=logical(ans); clear('ans'); +else +varargout{1}=false; +end +end +if eod_logic_eval +GetSoloFunctionArgs(obj); +ClearHelperVarsNotOwned(obj); +% +PerformanceSummarySection_stage_1_TrialsToday.value = 0; +callback(PerformanceSummarySection_stage_1_TrialsToday); +PerformanceSummarySection_stage_2_TrialsToday.value = 0; +callback(PerformanceSummarySection_stage_2_TrialsToday); +PerformanceSummarySection_stage_3_TrialsToday.value = 0; +callback(PerformanceSummarySection_stage_3_TrialsToday); +PerformanceSummarySection_stage_4_TrialsToday.value = 0; +callback(PerformanceSummarySection_stage_4_TrialsToday); +PerformanceSummarySection_stage_5_TrialsToday.value = 0; +callback(PerformanceSummarySection_stage_5_TrialsToday); +PerformanceSummarySection_stage_6_TrialsToday.value = 0; +callback(PerformanceSummarySection_stage_6_TrialsToday); +PerformanceSummarySection_stage_7_TrialsToday.value = 0; +callback(PerformanceSummarySection_stage_7_TrialsToday); +PerformanceSummarySection_stage_8_TrialsToday.value = 0; +callback(PerformanceSummarySection_stage_8_TrialsToday); +% +end +% + + +end + +end + +%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +% + +function [prestim,A1,prego] = param_time_within_range(fixed_length,cp_length,range_min_prestim,range_max_prestim, is_random_prestim, provided_time_prestim,... + range_min_A1,range_max_A1, is_random_A1, provided_time_A1,range_min_prego,range_max_prego, is_random_prego, provided_time_prego) + +if fixed_length == 1 % warm up stage where cp length is increasing +% then calculate the range/typical value + if cp_length <= 0.3 + prestim = 0.1; + A1 = 0.1; + prego = 0.1; + else + range_size = round(0.3 * cp_length,1); + if range_size > 0.4 + step_size = 0.1; + else + step_size = 0.01; + end + + timerange = 0.1:step_size:range_size; + + if is_random_prestim == 1 + prestim = timerange(randi([1, numel(timerange)],1,1)); + else + if provided_time_prestim <= range_size + prestim = provided_time_prestim; + else + prestim = range_size; + end + + end + + if is_random_A1 == 1 + A1 = timerange(randi([1, numel(timerange)],1,1)); + else + if provided_time_A1 <= range_size + A1 = provided_time_A1; + else + A1 = range_size; + end + end + + prego = cp_length - prestim - A1; + + end + +else + + if is_random_prestim == 1 + range_size_prestim = range_max_prestim - range_min_prestim; + if range_size_prestim > 0.4 + step_size_prestim = 0.1; + else + step_size_prestim = 0.01; + end + time_range_prestim = range_min_prestim:step_size_prestim:range_max_prestim; + prestim = time_range_prestim(randi([1, numel(time_range_prestim)],1,1)); + else + prestim = provided_time_prestim; + end + + if is_random_A1 == 1 + range_size_A1 = range_max_A1 - range_min_A1; + if range_size_A1 > 0.4 + step_size_A1 = 0.1; + else + step_size_A1 = 0.01; + end + time_range_A1 = range_min_A1:step_size_A1:range_max_A1; + A1 = time_range_A1(randi([1, numel(time_range_A1)],1,1)); + else + A1 = provided_time_A1; + end + + if is_random_prego == 1 + range_size_prego = range_max_prego - range_min_prego; + if range_size_prego > 0.4 + step_size_prego = 0.1; + else + step_size_prego = 0.01; + end + time_range_prego = range_min_prego:step_size_prego:range_max_prego; + prego = time_range_prego(randi([1, numel(time_range_prego)],1,1)); + else + prego = provided_time_prego; + end + +end +end + + +% + +%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% diff --git a/Protocols/@ArpitCentrePokeTraining/private/settings_@ArpitCentrePokeTraining_arpit_AR05_250516a.mat b/Protocols/@ArpitCentrePokeTraining/private/settings_@ArpitCentrePokeTraining_arpit_AR05_250516a.mat new file mode 100644 index 0000000000000000000000000000000000000000..7e70e094972354707ab4d569647e26f9141390cf GIT binary patch literal 16329 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 LamD$f`qBRa*3e*P literal 0 HcmV?d00001 From aa76a725e0ffdc8ef2a3076199236a771fa1b122 Mon Sep 17 00:00:00 2001 From: Arpit Date: Sat, 17 May 2025 11:11:46 +0100 Subject: [PATCH 105/164] camera error --- ExperPort/Plugins/@bonsaicamera/BonsaiCameraInterface.m | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ExperPort/Plugins/@bonsaicamera/BonsaiCameraInterface.m b/ExperPort/Plugins/@bonsaicamera/BonsaiCameraInterface.m index d61efa88..64010b2c 100644 --- a/ExperPort/Plugins/@bonsaicamera/BonsaiCameraInterface.m +++ b/ExperPort/Plugins/@bonsaicamera/BonsaiCameraInterface.m @@ -253,7 +253,7 @@ write(value(UDPSender), oscMsg_Camera_start, "uint8", bonsaiComputerIP,bonsaiUdpPort); pause(2); % Send the message to start recording - oscMsg_file_directory = createOSCMessage(recording_command_address,sprintf('%s\BControlTrial%i_bonsaiTrial.avi',value(Video_Saving_Folder),n_completed_trial)); + oscMsg_file_directory = createOSCMessage(recording_command_address,sprintf('%s\BControlTrial%i_bonsaiTrial.avi',value(Video_Saving_Folder),n_completed_trials)); write(value(UDPSender), oscMsg_file_directory, "uint8", bonsaiComputerIP,bonsaiUdpPort); @@ -271,7 +271,7 @@ % in this I send a command to bonsai so that it creates a new file % for each trial - oscMsg_file_directory = createOSCMessage(recording_command_address,sprintf('%s\BControlTrial%i_bonsaiTrial.avi',value(Video_Saving_Folder),n_completed_trial)); + oscMsg_file_directory = createOSCMessage(recording_command_address,sprintf('%s\BControlTrial%i_bonsaiTrial.avi',value(Video_Saving_Folder),n_completed_trials)); write(value(UDPSender), oscMsg_file_directory, "uint8", bonsaiComputerIP,bonsaiUdpPort); %% close bonsai and command window From 95c042b3571e1aa83f440680db7b46d3521ac8c8 Mon Sep 17 00:00:00 2001 From: Arpit Date: Sat, 17 May 2025 12:22:55 +0100 Subject: [PATCH 106/164] camera filename error correction --- ExperPort/Plugins/@bonsaicamera/BonsaiCameraInterface.m | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ExperPort/Plugins/@bonsaicamera/BonsaiCameraInterface.m b/ExperPort/Plugins/@bonsaicamera/BonsaiCameraInterface.m index 64010b2c..7ab24617 100644 --- a/ExperPort/Plugins/@bonsaicamera/BonsaiCameraInterface.m +++ b/ExperPort/Plugins/@bonsaicamera/BonsaiCameraInterface.m @@ -253,7 +253,7 @@ write(value(UDPSender), oscMsg_Camera_start, "uint8", bonsaiComputerIP,bonsaiUdpPort); pause(2); % Send the message to start recording - oscMsg_file_directory = createOSCMessage(recording_command_address,sprintf('%s\BControlTrial%i_bonsaiTrial.avi',value(Video_Saving_Folder),n_completed_trials)); + oscMsg_file_directory = createOSCMessage(recording_command_address,sprintf('%s\\BControlTrial%i_bonsaiTrial.avi',value(Video_Saving_Folder),n_completed_trials)); write(value(UDPSender), oscMsg_file_directory, "uint8", bonsaiComputerIP,bonsaiUdpPort); @@ -271,7 +271,7 @@ % in this I send a command to bonsai so that it creates a new file % for each trial - oscMsg_file_directory = createOSCMessage(recording_command_address,sprintf('%s\BControlTrial%i_bonsaiTrial.avi',value(Video_Saving_Folder),n_completed_trials)); + oscMsg_file_directory = createOSCMessage(recording_command_address,sprintf('%s\\BControlTrial%i_bonsaiTrial.avi',value(Video_Saving_Folder),n_completed_trials)); write(value(UDPSender), oscMsg_file_directory, "uint8", bonsaiComputerIP,bonsaiUdpPort); %% close bonsai and command window From f86f726a207f061df9693d073e127c372d354187 Mon Sep 17 00:00:00 2001 From: Arpit Date: Sat, 17 May 2025 23:47:59 +0100 Subject: [PATCH 107/164] camera filename error correction --- ExperPort/Plugins/@bonsaicamera/BonsaiCameraInterface.m | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ExperPort/Plugins/@bonsaicamera/BonsaiCameraInterface.m b/ExperPort/Plugins/@bonsaicamera/BonsaiCameraInterface.m index 7ab24617..6d4fc194 100644 --- a/ExperPort/Plugins/@bonsaicamera/BonsaiCameraInterface.m +++ b/ExperPort/Plugins/@bonsaicamera/BonsaiCameraInterface.m @@ -253,7 +253,7 @@ write(value(UDPSender), oscMsg_Camera_start, "uint8", bonsaiComputerIP,bonsaiUdpPort); pause(2); % Send the message to start recording - oscMsg_file_directory = createOSCMessage(recording_command_address,sprintf('%s\\BControlTrial%i_bonsaiTrial.avi',value(Video_Saving_Folder),n_completed_trials)); + oscMsg_file_directory = createOSCMessage(recording_command_address,sprintf('%s\\BControlTrial%i_bonsaiTrial.avi',value(Video_Saving_Folder),n_done_trials+1)); write(value(UDPSender), oscMsg_file_directory, "uint8", bonsaiComputerIP,bonsaiUdpPort); @@ -271,7 +271,7 @@ % in this I send a command to bonsai so that it creates a new file % for each trial - oscMsg_file_directory = createOSCMessage(recording_command_address,sprintf('%s\\BControlTrial%i_bonsaiTrial.avi',value(Video_Saving_Folder),n_completed_trials)); + oscMsg_file_directory = createOSCMessage(recording_command_address,sprintf('%s\\BControlTrial%i_bonsaiTrial.avi',value(Video_Saving_Folder),n_done_trials+1)); write(value(UDPSender), oscMsg_file_directory, "uint8", bonsaiComputerIP,bonsaiUdpPort); %% close bonsai and command window From 9fbbe2f3a9c713b3999d668d141eae8d0f19a9e1 Mon Sep 17 00:00:00 2001 From: Arpit Date: Tue, 20 May 2025 13:58:10 +0100 Subject: [PATCH 108/164] changed n_completed_trials to n_done_trials in all functions --- .../ArpitCentrePokeTraining.m | 30 +- ...ing_SessionDefinition_AutoTrainingStages.m | 412 ++++++++++-------- .../PerformanceSummarySection.m | 2 +- .../SessionPerformanceSection.m | 2 +- ...rpitCentrePokeTraining_arpit_AR05_250516.m | 114 ++--- .../set_training_stage_last_setting_file.m | 39 ++ .../ArpitSoundCalibration.m | 8 +- 7 files changed, 348 insertions(+), 259 deletions(-) create mode 100644 Protocols/@ArpitCentrePokeTraining/private/set_training_stage_last_setting_file.m diff --git a/Protocols/@ArpitCentrePokeTraining/ArpitCentrePokeTraining.m b/Protocols/@ArpitCentrePokeTraining/ArpitCentrePokeTraining.m index a72f9649..7779eb08 100644 --- a/Protocols/@ArpitCentrePokeTraining/ArpitCentrePokeTraining.m +++ b/Protocols/@ArpitCentrePokeTraining/ArpitCentrePokeTraining.m @@ -188,6 +188,20 @@ [x, y] = BonsaiCameraInterface(obj,'init',x,y,name,expmtr,rname); + % Before Loading the TrainingStageParamsSection, let me first check if + % the setting file exist for this rat and only load the training stage + % Problem: Loading settings in later stages fails because the stage-dependent + % TrainingStageParamsSection's solohandles are not visible after initial setup. + % Solution: This section handles a specific scenario for loading setting files during initialization. + % Although also invoked in Runrats, calling it here is crucial. The + % TrainingStageParamsSection's parameters are initially set at stage 1 and are visible. + % However, they become stage-dependent. Subsequent stages lack visible solohandles for + % these parameters, preventing proper loading. This initialization provides a workaround + % to avoid significant changes required to load stage-specific solohandles and maintain + % broader compatibility. + + set_training_stage_last_setting_file(name,expmtr,rname) + next_column(x); y=5; [stage_fig_x,stage_fig_y] = TrainingStageParamsSection(obj, 'init', x, y); SoloParamHandle(obj, 'stage_fig_x', 'value', stage_fig_x); @@ -212,16 +226,18 @@ % these parameters, preventing proper loading. This initialization provides a workaround % to avoid significant changes required to load stage-specific solohandles and maintain % broader compatibility. - try - [~, ~]=load_solouiparamvalues(rname,'experimenter',expmtr,... - 'owner',name,'interactive',0); - catch - end - %% + +% try +% [~, ~]=load_solouiparamvalues(rname,'experimenter',expmtr,... +% 'owner',name,'interactive',0); +% catch +% end + %% % feval(mfilename, obj, 'prepare_next_trial'); % Commented out because it is also run by Runrats(while loading the protocol) %%% + %% change_water_modulation_params case 'change_water_modulation_params' display_guys = [1 150 300]; @@ -242,7 +258,7 @@ ParamsSection(obj, 'prepare_next_trial'); - % push_helper_vars_tosql(obj,n_completed_trials); + % push_helper_vars_tosql(obj,n_done_trials); SessionDefinition(obj, 'next_trial'); diff --git a/Protocols/@ArpitCentrePokeTraining/ArpitCentrePokeTraining_SessionDefinition_AutoTrainingStages.m b/Protocols/@ArpitCentrePokeTraining/ArpitCentrePokeTraining_SessionDefinition_AutoTrainingStages.m index b7d307dd..e1b0cbf5 100644 --- a/Protocols/@ArpitCentrePokeTraining/ArpitCentrePokeTraining_SessionDefinition_AutoTrainingStages.m +++ b/Protocols/@ArpitCentrePokeTraining/ArpitCentrePokeTraining_SessionDefinition_AutoTrainingStages.m @@ -25,7 +25,7 @@ GetSoloFunctionArgs(obj); ClearHelperVarsNotOwned(obj); % -CreateHelperVar(obj,'stage_start_completed_trial','value',n_completed_trials,'force_init',true); +CreateHelperVar(obj,'stage_start_completed_trial','value',n_done_trials,'force_init',true); % end @@ -46,8 +46,9 @@ callback(ParamsSection_training_stage); end + % Update TrainingStageParamsSection -if n_completed_trials >= 2 +if n_done_trials >= 2 if previous_sides(end) ~= previous_sides(end-1) && all(hit_history(end-1:end)) % last and present trials should also be a valid trial TrainingStageParamsSection_trial_oppSide.value = value(TrainingStageParamsSection_trial_oppSide) + 1; % updating value for variable in TrainingParams_Section callback(TrainingStageParamsSection_trial_oppSide); @@ -55,7 +56,27 @@ end % Updating Disp Values for Training_Peformance_Summary -if n_completed_trials > 0 +if n_done_trials > 0 + + if n_done_trials == 1 + PerformanceSummarySection_stage_1_TrialsToday.value = 0; + callback(PerformanceSummarySection_stage_1_TrialsToday); + PerformanceSummarySection_stage_2_TrialsToday.value = 0; + callback(PerformanceSummarySection_stage_2_TrialsToday); + PerformanceSummarySection_stage_3_TrialsToday.value = 0; + callback(PerformanceSummarySection_stage_3_TrialsToday); + PerformanceSummarySection_stage_4_TrialsToday.value = 0; + callback(PerformanceSummarySection_stage_4_TrialsToday); + PerformanceSummarySection_stage_5_TrialsToday.value = 0; + callback(PerformanceSummarySection_stage_5_TrialsToday); + PerformanceSummarySection_stage_6_TrialsToday.value = 0; + callback(PerformanceSummarySection_stage_6_TrialsToday); + PerformanceSummarySection_stage_7_TrialsToday.value = 0; + callback(PerformanceSummarySection_stage_7_TrialsToday); + PerformanceSummarySection_stage_8_TrialsToday.value = 0; + callback(PerformanceSummarySection_stage_8_TrialsToday); + end + PerformanceSummarySection_stage_1_Trials.value = value(PerformanceSummarySection_stage_1_Trials) + 1; PerformanceSummarySection_stage_1_TrialsToday.value = value(PerformanceSummarySection_stage_1_TrialsToday) + 1; PerformanceSummarySection_stage_1_ViolationRate.value = nan; @@ -70,7 +91,7 @@ callback(PerformanceSummarySection_stage_1_TrialsValid); % Updating Disp Values for Training_Peformance_Summary - SessionPerformanceSection_ntrials.value = n_completed_trials; + SessionPerformanceSection_ntrials.value = n_done_trials; SessionPerformanceSection_violation_rate.value = nan; SessionPerformanceSection_timeout_rate.value = nan; SessionPerformanceSection_violation_recent.value = nan; @@ -103,7 +124,7 @@ if ParamsSection_use_auto_train % do completion check if auto training stage_no = value(SessionDefinition_CURRENT_ACTIVE_STAGE); % only run it if its the start of the day, number of trials is small - if n_completed_trials < 100 + if n_done_trials < 100 if value(PerformanceSummarySection_stage_1_TrialsValid) > value(TrainingStageParamsSection_total_trials) && ... value(TrainingStageParamsSection_trial_oppSide) > value(TrainingStageParamsSection_total_trials_opp) ParamsSection_training_stage.value = stage_no + 1; @@ -134,22 +155,7 @@ ParamsSection(obj, 'Changed_Training_Stage'); SessionDefinition(obj, 'jump_to_stage', 'Timeout Rewarded Side Pokes'); end -PerformanceSummarySection_stage_1_TrialsToday.value = 0; -callback(PerformanceSummarySection_stage_1_TrialsToday); -PerformanceSummarySection_stage_2_TrialsToday.value = 0; -callback(PerformanceSummarySection_stage_2_TrialsToday); -PerformanceSummarySection_stage_3_TrialsToday.value = 0; -callback(PerformanceSummarySection_stage_3_TrialsToday); -PerformanceSummarySection_stage_4_TrialsToday.value = 0; -callback(PerformanceSummarySection_stage_4_TrialsToday); -PerformanceSummarySection_stage_5_TrialsToday.value = 0; -callback(PerformanceSummarySection_stage_5_TrialsToday); -PerformanceSummarySection_stage_6_TrialsToday.value = 0; -callback(PerformanceSummarySection_stage_6_TrialsToday); -PerformanceSummarySection_stage_7_TrialsToday.value = 0; -callback(PerformanceSummarySection_stage_7_TrialsToday); -PerformanceSummarySection_stage_8_TrialsToday.value = 0; -callback(PerformanceSummarySection_stage_8_TrialsToday); + % end @@ -210,7 +216,7 @@ end % Update TrainingStageParamsSection -if n_completed_trials >= 2 +if n_done_trials >= 2 if previous_sides(end) ~= previous_sides(end-1) && all(hit_history(end-1:end)) % last and present trials should also be a valid trial TrainingStageParamsSection_trial_oppSide.value = value(TrainingStageParamsSection_trial_oppSide) + 1; % updating value for variable in TrainingParams_Section callback(TrainingStageParamsSection_trial_oppSide); @@ -219,7 +225,27 @@ % Updating Disp Values for Training_Peformance_Summary -if n_completed_trials > 0 +if n_done_trials > 0 + + if n_done_trials == 1 + PerformanceSummarySection_stage_1_TrialsToday.value = 0; + callback(PerformanceSummarySection_stage_1_TrialsToday); + PerformanceSummarySection_stage_2_TrialsToday.value = 0; + callback(PerformanceSummarySection_stage_2_TrialsToday); + PerformanceSummarySection_stage_3_TrialsToday.value = 0; + callback(PerformanceSummarySection_stage_3_TrialsToday); + PerformanceSummarySection_stage_4_TrialsToday.value = 0; + callback(PerformanceSummarySection_stage_4_TrialsToday); + PerformanceSummarySection_stage_5_TrialsToday.value = 0; + callback(PerformanceSummarySection_stage_5_TrialsToday); + PerformanceSummarySection_stage_6_TrialsToday.value = 0; + callback(PerformanceSummarySection_stage_6_TrialsToday); + PerformanceSummarySection_stage_7_TrialsToday.value = 0; + callback(PerformanceSummarySection_stage_7_TrialsToday); + PerformanceSummarySection_stage_8_TrialsToday.value = 0; + callback(PerformanceSummarySection_stage_8_TrialsToday); + end + PerformanceSummarySection_stage_2_Trials.value = value(PerformanceSummarySection_stage_2_Trials) + 1; PerformanceSummarySection_stage_2_TrialsToday.value = value(PerformanceSummarySection_stage_2_TrialsToday) + 1; PerformanceSummarySection_stage_2_ViolationRate.value = nan; @@ -235,11 +261,11 @@ callback(PerformanceSummarySection_stage_2_TrialsValid); % Updating Disp Values for Training_Peformance_Summary - SessionPerformanceSection_ntrials.value = n_completed_trials; + SessionPerformanceSection_ntrials.value = n_done_trials; SessionPerformanceSection_violation_rate.value = nan; - SessionPerformanceSection_timeout_rate.value = numel(find(timeout_history))/n_completed_trials; + SessionPerformanceSection_timeout_rate.value = numel(find(timeout_history))/n_done_trials; SessionPerformanceSection_violation_recent.value = nan; - if n_completed_trials >= 20 + if n_done_trials >= 20 SessionPerformanceSection_timeout_recent.value = numel(find(timeout_history(end-19:end)))/20; else SessionPerformanceSection_timeout_recent.value = nan; @@ -272,7 +298,7 @@ if ParamsSection_use_auto_train % do completion check if auto training stage_no = value(SessionDefinition_CURRENT_ACTIVE_STAGE); % only run it if its the start of the day, number of trials is small - if n_completed_trials > 50 + if n_done_trials > 50 if value(PerformanceSummarySection_stage_2_TrialsValid) > value(TrainingStageParamsSection_total_trials) && ... value(TrainingStageParamsSection_trial_oppSide) > value(TrainingStageParamsSection_total_trials_opp) ParamsSection_training_stage.value = stage_no + 1; @@ -295,22 +321,7 @@ GetSoloFunctionArgs(obj); ClearHelperVarsNotOwned(obj); % -PerformanceSummarySection_stage_1_TrialsToday.value = 0; -callback(PerformanceSummarySection_stage_1_TrialsToday); -PerformanceSummarySection_stage_2_TrialsToday.value = 0; -callback(PerformanceSummarySection_stage_2_TrialsToday); -PerformanceSummarySection_stage_3_TrialsToday.value = 0; -callback(PerformanceSummarySection_stage_3_TrialsToday); -PerformanceSummarySection_stage_4_TrialsToday.value = 0; -callback(PerformanceSummarySection_stage_4_TrialsToday); -PerformanceSummarySection_stage_5_TrialsToday.value = 0; -callback(PerformanceSummarySection_stage_5_TrialsToday); -PerformanceSummarySection_stage_6_TrialsToday.value = 0; -callback(PerformanceSummarySection_stage_6_TrialsToday); -PerformanceSummarySection_stage_7_TrialsToday.value = 0; -callback(PerformanceSummarySection_stage_7_TrialsToday); -PerformanceSummarySection_stage_8_TrialsToday.value = 0; -callback(PerformanceSummarySection_stage_8_TrialsToday); + % end % @@ -353,9 +364,9 @@ ParamsSection_CP_duration.value = cp_min; % initialize to min_CP end -if n_completed_trials < 1 % intialize to min value at the start of each session/day +if n_done_trials < 1 % intialize to min value at the start of each session/day ParamsSection_CP_duration.value = value(ParamsSection_init_CP_duration); -elseif n_completed_trials == 1 +elseif n_done_trials == 1 ParamsSection_CP_duration.value = value(TrainingStageParamsSection_last_session_CP); else if ~timeout_history(end) && value(ParamsSection_CP_duration) < cp_max @@ -368,7 +379,27 @@ end callback(ParamsSection_CP_duration); -if n_completed_trials > 0 +if n_done_trials > 0 + + if n_done_trials == 1 + PerformanceSummarySection_stage_1_TrialsToday.value = 0; + callback(PerformanceSummarySection_stage_1_TrialsToday); + PerformanceSummarySection_stage_2_TrialsToday.value = 0; + callback(PerformanceSummarySection_stage_2_TrialsToday); + PerformanceSummarySection_stage_3_TrialsToday.value = 0; + callback(PerformanceSummarySection_stage_3_TrialsToday); + PerformanceSummarySection_stage_4_TrialsToday.value = 0; + callback(PerformanceSummarySection_stage_4_TrialsToday); + PerformanceSummarySection_stage_5_TrialsToday.value = 0; + callback(PerformanceSummarySection_stage_5_TrialsToday); + PerformanceSummarySection_stage_6_TrialsToday.value = 0; + callback(PerformanceSummarySection_stage_6_TrialsToday); + PerformanceSummarySection_stage_7_TrialsToday.value = 0; + callback(PerformanceSummarySection_stage_7_TrialsToday); + PerformanceSummarySection_stage_8_TrialsToday.value = 0; + callback(PerformanceSummarySection_stage_8_TrialsToday); + end + % Updating Disp Values for Training_Peformance_Summary PerformanceSummarySection_stage_3_Trials.value = value(PerformanceSummarySection_stage_3_Trials) + 1; PerformanceSummarySection_stage_3_TrialsToday.value = value(PerformanceSummarySection_stage_3_TrialsToday) + 1; @@ -385,11 +416,11 @@ callback(PerformanceSummarySection_stage_3_TrialsValid); % Updating Disp Values for Training_Peformance_Summary - SessionPerformanceSection_ntrials.value = n_completed_trials; + SessionPerformanceSection_ntrials.value = n_done_trials; SessionPerformanceSection_violation_rate.value = nan; - SessionPerformanceSection_timeout_rate.value = numel(find(timeout_history))/n_completed_trials; + SessionPerformanceSection_timeout_rate.value = numel(find(timeout_history))/n_done_trials; SessionPerformanceSection_violation_recent.value = nan; - if n_completed_trials >= 20 + if n_done_trials >= 20 SessionPerformanceSection_timeout_recent.value = numel(find(timeout_history(end-19:end)))/20; else SessionPerformanceSection_timeout_recent.value = nan; @@ -447,23 +478,6 @@ % Update the CP duration reached in this session TrainingStageParamsSection_last_session_CP.value = value(ParamsSection_CP_duration); callback(TrainingStageParamsSection_last_session_CP); -% Reset the number of trials done today for this stage -PerformanceSummarySection_stage_1_TrialsToday.value = 0; -callback(PerformanceSummarySection_stage_1_TrialsToday); -PerformanceSummarySection_stage_2_TrialsToday.value = 0; -callback(PerformanceSummarySection_stage_2_TrialsToday); -PerformanceSummarySection_stage_3_TrialsToday.value = 0; -callback(PerformanceSummarySection_stage_3_TrialsToday); -PerformanceSummarySection_stage_4_TrialsToday.value = 0; -callback(PerformanceSummarySection_stage_4_TrialsToday); -PerformanceSummarySection_stage_5_TrialsToday.value = 0; -callback(PerformanceSummarySection_stage_5_TrialsToday); -PerformanceSummarySection_stage_6_TrialsToday.value = 0; -callback(PerformanceSummarySection_stage_6_TrialsToday); -PerformanceSummarySection_stage_7_TrialsToday.value = 0; -callback(PerformanceSummarySection_stage_7_TrialsToday); -PerformanceSummarySection_stage_8_TrialsToday.value = 0; -callback(PerformanceSummarySection_stage_8_TrialsToday); % end % @@ -507,9 +521,9 @@ ParamsSection_CP_duration.value = cp_min; % initialize to min_CP end -if n_completed_trials == 0 +if n_done_trials == 0 ParamsSection_CP_duration.value = value(ParamsSection_init_CP_duration); -elseif n_completed_trials == 1 +elseif n_done_trials == 1 ParamsSection_CP_duration.value = value(TrainingStageParamsSection_last_session_CP); else if ~violation_history(end) && ~timeout_history(end) && value(ParamsSection_CP_duration) < cp_max @@ -526,7 +540,27 @@ callback(ParamsSection_CP_duration); -if n_completed_trials > 0 +if n_done_trials > 0 + + if n_done_trials == 1 + PerformanceSummarySection_stage_1_TrialsToday.value = 0; + callback(PerformanceSummarySection_stage_1_TrialsToday); + PerformanceSummarySection_stage_2_TrialsToday.value = 0; + callback(PerformanceSummarySection_stage_2_TrialsToday); + PerformanceSummarySection_stage_3_TrialsToday.value = 0; + callback(PerformanceSummarySection_stage_3_TrialsToday); + PerformanceSummarySection_stage_4_TrialsToday.value = 0; + callback(PerformanceSummarySection_stage_4_TrialsToday); + PerformanceSummarySection_stage_5_TrialsToday.value = 0; + callback(PerformanceSummarySection_stage_5_TrialsToday); + PerformanceSummarySection_stage_6_TrialsToday.value = 0; + callback(PerformanceSummarySection_stage_6_TrialsToday); + PerformanceSummarySection_stage_7_TrialsToday.value = 0; + callback(PerformanceSummarySection_stage_7_TrialsToday); + PerformanceSummarySection_stage_8_TrialsToday.value = 0; + callback(PerformanceSummarySection_stage_8_TrialsToday); + end + % Updating Disp Values for Training_Peformance_Summary PerformanceSummarySection_stage_4_Trials.value = value(PerformanceSummarySection_stage_4_Trials) + 1; PerformanceSummarySection_stage_4_TrialsToday.value = value(PerformanceSummarySection_stage_4_TrialsToday) + 1; @@ -543,10 +577,10 @@ callback(PerformanceSummarySection_stage_4_TrialsValid); % Updating Disp Values for Training_Peformance_Summary - SessionPerformanceSection_ntrials.value = n_completed_trials; - SessionPerformanceSection_violation_rate.value = numel(find(violation_history))/n_completed_trials; - SessionPerformanceSection_timeout_rate.value = numel(find(timeout_history))/n_completed_trials; - if n_completed_trials >= 20 + SessionPerformanceSection_ntrials.value = n_done_trials; + SessionPerformanceSection_violation_rate.value = numel(find(violation_history))/n_done_trials; + SessionPerformanceSection_timeout_rate.value = numel(find(timeout_history))/n_done_trials; + if n_done_trials >= 20 SessionPerformanceSection_violation_recent.value = numel(find(violation_history(end-19:end)))/20; SessionPerformanceSection_timeout_recent.value = numel(find(timeout_history(end-19:end)))/20; else @@ -579,7 +613,7 @@ % if ParamsSection_use_auto_train % do completion check if auto training cp_max = value(TrainingStageParamsSection_max_CP); - if value(ParamsSection_CP_duration) >= cp_max && n_completed_trials > 100 && ... + if value(ParamsSection_CP_duration) >= cp_max && n_done_trials > 100 && ... value(SessionPerformanceSection_violation_recent) < value(TrainingStageParamsSection_recent_violation) && ... value(SessionPerformanceSection_timeout_recent) < value(TrainingStageParamsSection_recent_timeout) && ... value(SessionPerformanceSection_violation_stage) < value(TrainingStageParamsSection_stage_violation) @@ -608,23 +642,6 @@ % Update the CP duration reached in this session TrainingStageParamsSection_last_session_CP.value = value(ParamsSection_CP_duration); callback(TrainingStageParamsSection_last_session_CP); -% Reset the number of trials done today for this stage -PerformanceSummarySection_stage_1_TrialsToday.value = 0; -callback(PerformanceSummarySection_stage_1_TrialsToday); -PerformanceSummarySection_stage_2_TrialsToday.value = 0; -callback(PerformanceSummarySection_stage_2_TrialsToday); -PerformanceSummarySection_stage_3_TrialsToday.value = 0; -callback(PerformanceSummarySection_stage_3_TrialsToday); -PerformanceSummarySection_stage_4_TrialsToday.value = 0; -callback(PerformanceSummarySection_stage_4_TrialsToday); -PerformanceSummarySection_stage_5_TrialsToday.value = 0; -callback(PerformanceSummarySection_stage_5_TrialsToday); -PerformanceSummarySection_stage_6_TrialsToday.value = 0; -callback(PerformanceSummarySection_stage_6_TrialsToday); -PerformanceSummarySection_stage_7_TrialsToday.value = 0; -callback(PerformanceSummarySection_stage_7_TrialsToday); -PerformanceSummarySection_stage_8_TrialsToday.value = 0; -callback(PerformanceSummarySection_stage_8_TrialsToday); % end % @@ -669,9 +686,9 @@ % Change the value of CP Duration % Since starting a new session then do a pre warm up to last saved cp % duration else continue with learning with increased poke time -if n_completed_trials == 0 +if n_done_trials == 0 ParamsSection_CP_duration.value = value(ParamsSection_init_CP_duration); -elseif n_completed_trials == 1 +elseif n_done_trials == 1 ParamsSection_CP_duration.value = starting_cp; else if ~violation_history(end) && ~timeout_history(end) @@ -725,7 +742,27 @@ callback(ParamsSection_A1_time); end -if n_completed_trials > 0 +if n_done_trials > 0 + + if n_done_trials == 1 + PerformanceSummarySection_stage_1_TrialsToday.value = 0; + callback(PerformanceSummarySection_stage_1_TrialsToday); + PerformanceSummarySection_stage_2_TrialsToday.value = 0; + callback(PerformanceSummarySection_stage_2_TrialsToday); + PerformanceSummarySection_stage_3_TrialsToday.value = 0; + callback(PerformanceSummarySection_stage_3_TrialsToday); + PerformanceSummarySection_stage_4_TrialsToday.value = 0; + callback(PerformanceSummarySection_stage_4_TrialsToday); + PerformanceSummarySection_stage_5_TrialsToday.value = 0; + callback(PerformanceSummarySection_stage_5_TrialsToday); + PerformanceSummarySection_stage_6_TrialsToday.value = 0; + callback(PerformanceSummarySection_stage_6_TrialsToday); + PerformanceSummarySection_stage_7_TrialsToday.value = 0; + callback(PerformanceSummarySection_stage_7_TrialsToday); + PerformanceSummarySection_stage_8_TrialsToday.value = 0; + callback(PerformanceSummarySection_stage_8_TrialsToday); + end + % Updating Disp Values for Training_Peformance_Summary PerformanceSummarySection_stage_5_Trials.value = value(PerformanceSummarySection_stage_5_Trials) + 1; PerformanceSummarySection_stage_5_TrialsToday.value = value(PerformanceSummarySection_stage_5_TrialsToday) + 1; @@ -742,10 +779,10 @@ callback(PerformanceSummarySection_stage_5_TrialsValid); % Updating Disp Values for Training_Peformance_Summary - SessionPerformanceSection_ntrials.value = n_completed_trials; - SessionPerformanceSection_violation_rate.value = numel(find(violation_history))/n_completed_trials; - SessionPerformanceSection_timeout_rate.value = numel(find(timeout_history))/n_completed_trials; - if n_completed_trials >= 20 + SessionPerformanceSection_ntrials.value = n_done_trials; + SessionPerformanceSection_violation_rate.value = numel(find(violation_history))/n_done_trials; + SessionPerformanceSection_timeout_rate.value = numel(find(timeout_history))/n_done_trials; + if n_done_trials >= 20 SessionPerformanceSection_violation_recent.value = numel(find(violation_history(end-19:end)))/20; SessionPerformanceSection_timeout_recent.value = numel(find(timeout_history(end-19:end)))/20; else @@ -782,7 +819,7 @@ stage_no = value(SessionDefinition_CURRENT_ACTIVE_STAGE); if value(ParamsSection_CP_duration) >= value(TrainingStageParamsSection_max_CP) && value(PerformanceSummarySection_stage_5_TrialsValid) > value(TrainingStageParamsSection_total_trials) if value(SessionPerformanceSection_violation_recent) < value(TrainingStageParamsSection_recent_violation) && value(SessionPerformanceSection_timeout_recent) < value(TrainingStageParamsSection_recent_timeout) && ... - value(SessionPerformanceSection_violation_stage) < value(TrainingStageParamsSection_stage_violation) && n_completed_trials > 100 + value(SessionPerformanceSection_violation_stage) < value(TrainingStageParamsSection_stage_violation) && n_done_trials > 100 ParamsSection_training_stage.value = stage_no + 1; callback(ParamsSection_training_stage); ParamsSection(obj, 'Changed_Training_Stage'); @@ -808,23 +845,6 @@ % Update the CP duration reached in this session TrainingStageParamsSection_last_session_CP.value = value(ParamsSection_CP_duration); callback(TrainingStageParamsSection_last_session_CP); -% Reset the number of trials done today for this stage -PerformanceSummarySection_stage_1_TrialsToday.value = 0; -callback(PerformanceSummarySection_stage_1_TrialsToday); -PerformanceSummarySection_stage_2_TrialsToday.value = 0; -callback(PerformanceSummarySection_stage_2_TrialsToday); -PerformanceSummarySection_stage_3_TrialsToday.value = 0; -callback(PerformanceSummarySection_stage_3_TrialsToday); -PerformanceSummarySection_stage_4_TrialsToday.value = 0; -callback(PerformanceSummarySection_stage_4_TrialsToday); -PerformanceSummarySection_stage_5_TrialsToday.value = 0; -callback(PerformanceSummarySection_stage_5_TrialsToday); -PerformanceSummarySection_stage_6_TrialsToday.value = 0; -callback(PerformanceSummarySection_stage_6_TrialsToday); -PerformanceSummarySection_stage_7_TrialsToday.value = 0; -callback(PerformanceSummarySection_stage_7_TrialsToday); -PerformanceSummarySection_stage_8_TrialsToday.value = 0; -callback(PerformanceSummarySection_stage_8_TrialsToday); % end @@ -866,9 +886,9 @@ end % Warm Up If starting a new session -if n_completed_trials == 0 +if n_done_trials == 0 ParamsSection_CP_duration.value = value(ParamsSection_init_CP_duration); -elseif n_completed_trials == 1 +elseif n_done_trials == 1 ParamsSection_CP_duration.value = starting_cp; else if value(ParamsSection_CP_duration) < cp_max % warm up stage @@ -906,7 +926,27 @@ callback(ParamsSection_A1_time); end -if n_completed_trials > 0 +if n_done_trials > 0 + + if n_done_trials == 1 + PerformanceSummarySection_stage_1_TrialsToday.value = 0; + callback(PerformanceSummarySection_stage_1_TrialsToday); + PerformanceSummarySection_stage_2_TrialsToday.value = 0; + callback(PerformanceSummarySection_stage_2_TrialsToday); + PerformanceSummarySection_stage_3_TrialsToday.value = 0; + callback(PerformanceSummarySection_stage_3_TrialsToday); + PerformanceSummarySection_stage_4_TrialsToday.value = 0; + callback(PerformanceSummarySection_stage_4_TrialsToday); + PerformanceSummarySection_stage_5_TrialsToday.value = 0; + callback(PerformanceSummarySection_stage_5_TrialsToday); + PerformanceSummarySection_stage_6_TrialsToday.value = 0; + callback(PerformanceSummarySection_stage_6_TrialsToday); + PerformanceSummarySection_stage_7_TrialsToday.value = 0; + callback(PerformanceSummarySection_stage_7_TrialsToday); + PerformanceSummarySection_stage_8_TrialsToday.value = 0; + callback(PerformanceSummarySection_stage_8_TrialsToday); + end + % Updating Disp Values for Training_Peformance_Summary PerformanceSummarySection_stage_6_Trials.value = value(PerformanceSummarySection_stage_6_Trials) + 1; PerformanceSummarySection_stage_6_TrialsToday.value = value(PerformanceSummarySection_stage_6_TrialsToday) + 1; @@ -923,10 +963,10 @@ callback(PerformanceSummarySection_stage_6_TrialsValid); % Updating Disp Values for SessionPerformanceSection - SessionPerformanceSection_ntrials.value = n_completed_trials; - SessionPerformanceSection_violation_rate.value = numel(find(violation_history))/n_completed_trials; - SessionPerformanceSection_timeout_rate.value = numel(find(timeout_history))/n_completed_trials; - if n_completed_trials >= 20 + SessionPerformanceSection_ntrials.value = n_done_trials; + SessionPerformanceSection_violation_rate.value = numel(find(violation_history))/n_done_trials; + SessionPerformanceSection_timeout_rate.value = numel(find(timeout_history))/n_done_trials; + if n_done_trials >= 20 SessionPerformanceSection_violation_recent.value = numel(find(violation_history(end-19:end)))/20; SessionPerformanceSection_timeout_recent.value = numel(find(timeout_history(end-19:end)))/20; else @@ -960,7 +1000,7 @@ stage_no = value(SessionDefinition_CURRENT_ACTIVE_STAGE); if value(PerformanceSummarySection_stage_6_TrialsValid) > value(TrainingStageParamsSection_total_trials) if value(SessionPerformanceSection_violation_recent) < value(TrainingStageParamsSection_recent_violation) && value(SessionPerformanceSection_timeout_recent) < value(TrainingStageParamsSection_recent_timeout) && ... - value(SessionPerformanceSection_violation_stage) < value(TrainingStageParamsSection_stage_violation) && n_completed_trials > 100 + value(SessionPerformanceSection_violation_stage) < value(TrainingStageParamsSection_stage_violation) && n_done_trials > 100 ParamsSection_training_stage.value = stage_no + 1; callback(ParamsSection_training_stage); ParamsSection(obj, 'Changed_Training_Stage'); @@ -981,23 +1021,7 @@ GetSoloFunctionArgs(obj); ClearHelperVarsNotOwned(obj); % -% Reset the number of trials done today for this stage -PerformanceSummarySection_stage_1_TrialsToday.value = 0; -callback(PerformanceSummarySection_stage_1_TrialsToday); -PerformanceSummarySection_stage_2_TrialsToday.value = 0; -callback(PerformanceSummarySection_stage_2_TrialsToday); -PerformanceSummarySection_stage_3_TrialsToday.value = 0; -callback(PerformanceSummarySection_stage_3_TrialsToday); -PerformanceSummarySection_stage_4_TrialsToday.value = 0; -callback(PerformanceSummarySection_stage_4_TrialsToday); -PerformanceSummarySection_stage_5_TrialsToday.value = 0; -callback(PerformanceSummarySection_stage_5_TrialsToday); -PerformanceSummarySection_stage_6_TrialsToday.value = 0; -callback(PerformanceSummarySection_stage_6_TrialsToday); -PerformanceSummarySection_stage_7_TrialsToday.value = 0; -callback(PerformanceSummarySection_stage_7_TrialsToday); -PerformanceSummarySection_stage_8_TrialsToday.value = 0; -callback(PerformanceSummarySection_stage_8_TrialsToday); + % end % @@ -1051,10 +1075,10 @@ % Warm Up If starting a new session if warm_up_on == 1 - if n_completed_trials == 0 + if n_done_trials == 0 ParamsSection_CP_duration.value = value(ParamsSection_init_CP_duration); warmup_completed = 0; - elseif n_completed_trials == 1 + elseif n_done_trials == 1 ParamsSection_CP_duration.value = starting_cp; else if value(ParamsSection_CP_duration) <= cp_max % warm up stage @@ -1076,7 +1100,7 @@ warmup_completed = 1; end -if n_completed_trials >= 1 +if n_done_trials >= 1 cp_length = value(ParamsSection_CP_duration) - value(ParamsSection_SettlingIn_time); [ParamsSection_PreStim_time.value ,ParamsSection_A1_time.value,ParamsSection_time_bet_aud1_gocue.value] = param_time_within_range(warmup_completed,... cp_length,prestim_min,prestim_max, random_prestim, prestim_time,... @@ -1089,7 +1113,27 @@ end callback(ParamsSection_CP_duration); -if n_completed_trials > 0 +if n_done_trials > 0 + + if n_done_trials == 1 + PerformanceSummarySection_stage_1_TrialsToday.value = 0; + callback(PerformanceSummarySection_stage_1_TrialsToday); + PerformanceSummarySection_stage_2_TrialsToday.value = 0; + callback(PerformanceSummarySection_stage_2_TrialsToday); + PerformanceSummarySection_stage_3_TrialsToday.value = 0; + callback(PerformanceSummarySection_stage_3_TrialsToday); + PerformanceSummarySection_stage_4_TrialsToday.value = 0; + callback(PerformanceSummarySection_stage_4_TrialsToday); + PerformanceSummarySection_stage_5_TrialsToday.value = 0; + callback(PerformanceSummarySection_stage_5_TrialsToday); + PerformanceSummarySection_stage_6_TrialsToday.value = 0; + callback(PerformanceSummarySection_stage_6_TrialsToday); + PerformanceSummarySection_stage_7_TrialsToday.value = 0; + callback(PerformanceSummarySection_stage_7_TrialsToday); + PerformanceSummarySection_stage_8_TrialsToday.value = 0; + callback(PerformanceSummarySection_stage_8_TrialsToday); + end + % Updating Disp Values for Training_Peformance_Summary PerformanceSummarySection_stage_7_Trials.value = value(PerformanceSummarySection_stage_7_Trials) + 1; PerformanceSummarySection_stage_7_TrialsToday.value = value(PerformanceSummarySection_stage_7_TrialsToday) + 1; @@ -1106,10 +1150,10 @@ callback(PerformanceSummarySection_stage_7_TrialsValid); % Updating Disp Values for SessionPerformanceSection - SessionPerformanceSection_ntrials.value = n_completed_trials; - SessionPerformanceSection_violation_rate.value = numel(find(violation_history))/n_completed_trials; - SessionPerformanceSection_timeout_rate.value = numel(find(timeout_history))/n_completed_trials; - if n_completed_trials >= 20 + SessionPerformanceSection_ntrials.value = n_done_trials; + SessionPerformanceSection_violation_rate.value = numel(find(violation_history))/n_done_trials; + SessionPerformanceSection_timeout_rate.value = numel(find(timeout_history))/n_done_trials; + if n_done_trials >= 20 SessionPerformanceSection_violation_recent.value = numel(find(violation_history(end-19:end)))/20; SessionPerformanceSection_timeout_recent.value = numel(find(timeout_history(end-19:end)))/20; else @@ -1167,22 +1211,7 @@ GetSoloFunctionArgs(obj); ClearHelperVarsNotOwned(obj); % -PerformanceSummarySection_stage_1_TrialsToday.value = 0; -callback(PerformanceSummarySection_stage_1_TrialsToday); -PerformanceSummarySection_stage_2_TrialsToday.value = 0; -callback(PerformanceSummarySection_stage_2_TrialsToday); -PerformanceSummarySection_stage_3_TrialsToday.value = 0; -callback(PerformanceSummarySection_stage_3_TrialsToday); -PerformanceSummarySection_stage_4_TrialsToday.value = 0; -callback(PerformanceSummarySection_stage_4_TrialsToday); -PerformanceSummarySection_stage_5_TrialsToday.value = 0; -callback(PerformanceSummarySection_stage_5_TrialsToday); -PerformanceSummarySection_stage_6_TrialsToday.value = 0; -callback(PerformanceSummarySection_stage_6_TrialsToday); -PerformanceSummarySection_stage_7_TrialsToday.value = 0; -callback(PerformanceSummarySection_stage_7_TrialsToday); -PerformanceSummarySection_stage_8_TrialsToday.value = 0; -callback(PerformanceSummarySection_stage_8_TrialsToday); + % end % @@ -1234,10 +1263,10 @@ % Warm Up If starting a new session if warm_up_on == 1 - if n_completed_trials == 0 + if n_done_trials == 0 ParamsSection_CP_duration.value = value(ParamsSection_init_CP_duration); warmup_completed = 0; - elseif n_completed_trials == 1 + elseif n_done_trials == 1 ParamsSection_CP_duration.value = starting_cp; else if value(ParamsSection_CP_duration) <= cp_max % warm up stage @@ -1259,7 +1288,7 @@ warmup_completed = 1; end -if n_completed_trials >= 1 +if n_done_trials >= 1 cp_length = value(ParamsSection_CP_duration) - value(ParamsSection_SettlingIn_time); [ParamsSection_PreStim_time.value ,ParamsSection_A1_time.value,ParamsSection_time_bet_aud1_gocue.value] = param_time_within_range(warmup_completed,... cp_length,prestim_min,prestim_max, random_prestim, prestim_time,... @@ -1272,7 +1301,27 @@ end callback(ParamsSection_CP_duration); -if n_completed_trials > 0 +if n_done_trials > 0 + + if n_done_trials == 1 + PerformanceSummarySection_stage_1_TrialsToday.value = 0; + callback(PerformanceSummarySection_stage_1_TrialsToday); + PerformanceSummarySection_stage_2_TrialsToday.value = 0; + callback(PerformanceSummarySection_stage_2_TrialsToday); + PerformanceSummarySection_stage_3_TrialsToday.value = 0; + callback(PerformanceSummarySection_stage_3_TrialsToday); + PerformanceSummarySection_stage_4_TrialsToday.value = 0; + callback(PerformanceSummarySection_stage_4_TrialsToday); + PerformanceSummarySection_stage_5_TrialsToday.value = 0; + callback(PerformanceSummarySection_stage_5_TrialsToday); + PerformanceSummarySection_stage_6_TrialsToday.value = 0; + callback(PerformanceSummarySection_stage_6_TrialsToday); + PerformanceSummarySection_stage_7_TrialsToday.value = 0; + callback(PerformanceSummarySection_stage_7_TrialsToday); + PerformanceSummarySection_stage_8_TrialsToday.value = 0; + callback(PerformanceSummarySection_stage_8_TrialsToday); + end + % Updating Disp Values for Training_Peformance_Summary PerformanceSummarySection_stage_8_Trials.value = value(PerformanceSummarySection_stage_8_Trials) + 1; PerformanceSummarySection_stage_8_TrialsToday.value = value(PerformanceSummarySection_stage_8_TrialsToday) + 1; @@ -1289,10 +1338,10 @@ callback(PerformanceSummarySection_stage_8_TrialsValid); % Updating Disp Values for SessionPerformanceSection - SessionPerformanceSection_ntrials.value = n_completed_trials; - SessionPerformanceSection_violation_rate.value = numel(find(violation_history))/n_completed_trials; - SessionPerformanceSection_timeout_rate.value = numel(find(timeout_history))/n_completed_trials; - if n_completed_trials >= 20 + SessionPerformanceSection_ntrials.value = n_done_trials; + SessionPerformanceSection_violation_rate.value = numel(find(violation_history))/n_done_trials; + SessionPerformanceSection_timeout_rate.value = numel(find(timeout_history))/n_done_trials; + if n_done_trials >= 20 SessionPerformanceSection_violation_recent.value = numel(find(violation_history(end-19:end)))/20; SessionPerformanceSection_timeout_recent.value = numel(find(timeout_history(end-19:end)))/20; else @@ -1340,22 +1389,7 @@ GetSoloFunctionArgs(obj); ClearHelperVarsNotOwned(obj); % -PerformanceSummarySection_stage_1_TrialsToday.value = 0; -callback(PerformanceSummarySection_stage_1_TrialsToday); -PerformanceSummarySection_stage_2_TrialsToday.value = 0; -callback(PerformanceSummarySection_stage_2_TrialsToday); -PerformanceSummarySection_stage_3_TrialsToday.value = 0; -callback(PerformanceSummarySection_stage_3_TrialsToday); -PerformanceSummarySection_stage_4_TrialsToday.value = 0; -callback(PerformanceSummarySection_stage_4_TrialsToday); -PerformanceSummarySection_stage_5_TrialsToday.value = 0; -callback(PerformanceSummarySection_stage_5_TrialsToday); -PerformanceSummarySection_stage_6_TrialsToday.value = 0; -callback(PerformanceSummarySection_stage_6_TrialsToday); -PerformanceSummarySection_stage_7_TrialsToday.value = 0; -callback(PerformanceSummarySection_stage_7_TrialsToday); -PerformanceSummarySection_stage_8_TrialsToday.value = 0; -callback(PerformanceSummarySection_stage_8_TrialsToday); + % end % diff --git a/Protocols/@ArpitCentrePokeTraining/PerformanceSummarySection.m b/Protocols/@ArpitCentrePokeTraining/PerformanceSummarySection.m index d30dea68..47838551 100644 --- a/Protocols/@ArpitCentrePokeTraining/PerformanceSummarySection.m +++ b/Protocols/@ArpitCentrePokeTraining/PerformanceSummarySection.m @@ -80,7 +80,7 @@ %% Evaluate case 'evaluate' - % if value(training_stage) == 8 && n_completed_trials > 0 + % if value(training_stage) == 8 && n_done_trials > 0 % stage_8_Trials.value = value(stage_8_Trials) + 1; % stage_8_TrialsToday.value = value(stage_8_TrialsToday) + 1; % stage_8_ViolationRate.value = ((value(stage_8_ViolationRate) * (value(stage_8_Trials) - 1)) + double(violation_history(end))) / value(stage_8_Trials); diff --git a/Protocols/@ArpitCentrePokeTraining/SessionPerformanceSection.m b/Protocols/@ArpitCentrePokeTraining/SessionPerformanceSection.m index bdcf0cf6..b8fcb77b 100644 --- a/Protocols/@ArpitCentrePokeTraining/SessionPerformanceSection.m +++ b/Protocols/@ArpitCentrePokeTraining/SessionPerformanceSection.m @@ -84,7 +84,7 @@ case 'evaluate' if nargout > 0 - x = [n_completed_trials, value(ntrials_stage), value(violation_rate), value(timeout_rate), value(violation_recent), ... + x = [n_done_trials, value(ntrials_stage), value(violation_rate), value(timeout_rate), value(violation_recent), ... value(timeout_recent), value(violation_stage), value(timeout_stage)]; end diff --git a/Protocols/@ArpitCentrePokeTraining/private/pipeline_ArpitCentrePokeTraining_arpit_AR05_250516.m b/Protocols/@ArpitCentrePokeTraining/private/pipeline_ArpitCentrePokeTraining_arpit_AR05_250516.m index dda1012f..dc414f6f 100644 --- a/Protocols/@ArpitCentrePokeTraining/private/pipeline_ArpitCentrePokeTraining_arpit_AR05_250516.m +++ b/Protocols/@ArpitCentrePokeTraining/private/pipeline_ArpitCentrePokeTraining_arpit_AR05_250516.m @@ -23,7 +23,7 @@ GetSoloFunctionArgs(obj); ClearHelperVarsNotOwned(obj); % -CreateHelperVar(obj,'stage_start_completed_trial','value',n_completed_trials,'force_init',true); +CreateHelperVar(obj,'stage_start_completed_trial','value',n_done_trials,'force_init',true); % end if stage_algorithm_eval @@ -41,7 +41,7 @@ end % Update TrainingStageParamsSection -if n_completed_trials >= 2 +if n_done_trials >= 2 if previous_sides(end) ~= previous_sides(end-1) && all(hit_history(end-1:end)) % last and present trials should also be a valid trial TrainingStageParamsSection_trial_oppSide.value = value(TrainingStageParamsSection_trial_oppSide) + 1; % updating value for variable in TrainingParams_Section callback(TrainingStageParamsSection_trial_oppSide); @@ -49,7 +49,7 @@ end % Updating Disp Values for Training_Peformance_Summary -if n_completed_trials > 0 +if n_done_trials > 0 PerformanceSummarySection_stage_1_Trials.value = value(PerformanceSummarySection_stage_1_Trials) + 1; PerformanceSummarySection_stage_1_TrialsToday.value = value(PerformanceSummarySection_stage_1_TrialsToday) + 1; PerformanceSummarySection_stage_1_ViolationRate.value = nan; @@ -64,7 +64,7 @@ callback(PerformanceSummarySection_stage_1_TrialsValid); % Updating Disp Values for Training_Peformance_Summary - SessionPerformanceSection_ntrials.value = n_completed_trials; + SessionPerformanceSection_ntrials.value = n_done_trials; SessionPerformanceSection_violation_rate.value = nan; SessionPerformanceSection_timeout_rate.value = nan; SessionPerformanceSection_violation_recent.value = nan; @@ -95,7 +95,7 @@ if ParamsSection_use_auto_train % do completion check if auto training stage_no = value(SessionDefinition_CURRENT_ACTIVE_STAGE); % only run it if its the start of the day, number of trials is small - if n_completed_trials < 100 + if n_done_trials < 100 if value(PerformanceSummarySection_stage_1_TrialsValid) > value(TrainingStageParamsSection_total_trials) && ... value(TrainingStageParamsSection_trial_oppSide) > value(TrainingStageParamsSection_total_trials_opp) ParamsSection_training_stage.value = stage_no + 1; @@ -190,7 +190,7 @@ end % Update TrainingStageParamsSection -if n_completed_trials >= 2 +if n_done_trials >= 2 if previous_sides(end) ~= previous_sides(end-1) && all(hit_history(end-1:end)) % last and present trials should also be a valid trial TrainingStageParamsSection_trial_oppSide.value = value(TrainingStageParamsSection_trial_oppSide) + 1; % updating value for variable in TrainingParams_Section callback(TrainingStageParamsSection_trial_oppSide); @@ -199,7 +199,7 @@ % Updating Disp Values for Training_Peformance_Summary -if n_completed_trials > 0 +if n_done_trials > 0 PerformanceSummarySection_stage_2_Trials.value = value(PerformanceSummarySection_stage_2_Trials) + 1; PerformanceSummarySection_stage_2_TrialsToday.value = value(PerformanceSummarySection_stage_2_TrialsToday) + 1; PerformanceSummarySection_stage_2_ViolationRate.value = nan; @@ -215,11 +215,11 @@ callback(PerformanceSummarySection_stage_2_TrialsValid); % Updating Disp Values for Training_Peformance_Summary - SessionPerformanceSection_ntrials.value = n_completed_trials; + SessionPerformanceSection_ntrials.value = n_done_trials; SessionPerformanceSection_violation_rate.value = nan; - SessionPerformanceSection_timeout_rate.value = numel(find(timeout_history))/n_completed_trials; + SessionPerformanceSection_timeout_rate.value = numel(find(timeout_history))/n_done_trials; SessionPerformanceSection_violation_recent.value = nan; - if n_completed_trials >= 20 + if n_done_trials >= 20 SessionPerformanceSection_timeout_recent.value = numel(find(timeout_history(end-19:end)))/20; else SessionPerformanceSection_timeout_recent.value = nan; @@ -250,7 +250,7 @@ if ParamsSection_use_auto_train % do completion check if auto training stage_no = value(SessionDefinition_CURRENT_ACTIVE_STAGE); % only run it if its the start of the day, number of trials is small - if n_completed_trials > 50 + if n_done_trials > 50 if value(PerformanceSummarySection_stage_2_TrialsValid) > value(TrainingStageParamsSection_total_trials) && ... value(TrainingStageParamsSection_trial_oppSide) > value(TrainingStageParamsSection_total_trials_opp) ParamsSection_training_stage.value = stage_no + 1; @@ -324,9 +324,9 @@ ParamsSection_CP_duration.value = cp_min; % initialize to min_CP end -if n_completed_trials < 1 % intialize to min value at the start of each session/day +if n_done_trials < 1 % intialize to min value at the start of each session/day ParamsSection_CP_duration.value = value(ParamsSection_init_CP_duration); -elseif n_completed_trials == 1 +elseif n_done_trials == 1 ParamsSection_CP_duration.value = value(TrainingStageParamsSection_last_session_CP); else if ~timeout_history(end) && value(ParamsSection_CP_duration) < cp_max @@ -339,7 +339,7 @@ end callback(ParamsSection_CP_duration); -if n_completed_trials > 0 +if n_done_trials > 0 % Updating Disp Values for Training_Peformance_Summary PerformanceSummarySection_stage_3_Trials.value = value(PerformanceSummarySection_stage_3_Trials) + 1; PerformanceSummarySection_stage_3_TrialsToday.value = value(PerformanceSummarySection_stage_3_TrialsToday) + 1; @@ -356,11 +356,11 @@ callback(PerformanceSummarySection_stage_3_TrialsValid); % Updating Disp Values for Training_Peformance_Summary - SessionPerformanceSection_ntrials.value = n_completed_trials; + SessionPerformanceSection_ntrials.value = n_done_trials; SessionPerformanceSection_violation_rate.value = nan; - SessionPerformanceSection_timeout_rate.value = numel(find(timeout_history))/n_completed_trials; + SessionPerformanceSection_timeout_rate.value = numel(find(timeout_history))/n_done_trials; SessionPerformanceSection_violation_recent.value = nan; - if n_completed_trials >= 20 + if n_done_trials >= 20 SessionPerformanceSection_timeout_recent.value = numel(find(timeout_history(end-19:end)))/20; else SessionPerformanceSection_timeout_recent.value = nan; @@ -469,9 +469,9 @@ ParamsSection_CP_duration.value = cp_min; % initialize to min_CP end -if n_completed_trials == 0 +if n_done_trials == 0 ParamsSection_CP_duration.value = value(ParamsSection_init_CP_duration); -elseif n_completed_trials == 1 +elseif n_done_trials == 1 ParamsSection_CP_duration.value = value(TrainingStageParamsSection_last_session_CP); else if ~violation_history(end) && ~timeout_history(end) && value(ParamsSection_CP_duration) < cp_max @@ -488,7 +488,7 @@ callback(ParamsSection_CP_duration); -if n_completed_trials > 0 +if n_done_trials > 0 % Updating Disp Values for Training_Peformance_Summary PerformanceSummarySection_stage_4_Trials.value = value(PerformanceSummarySection_stage_4_Trials) + 1; PerformanceSummarySection_stage_4_TrialsToday.value = value(PerformanceSummarySection_stage_4_TrialsToday) + 1; @@ -505,10 +505,10 @@ callback(PerformanceSummarySection_stage_4_TrialsValid); % Updating Disp Values for Training_Peformance_Summary - SessionPerformanceSection_ntrials.value = n_completed_trials; - SessionPerformanceSection_violation_rate.value = numel(find(violation_history))/n_completed_trials; - SessionPerformanceSection_timeout_rate.value = numel(find(timeout_history))/n_completed_trials; - if n_completed_trials >= 20 + SessionPerformanceSection_ntrials.value = n_done_trials; + SessionPerformanceSection_violation_rate.value = numel(find(violation_history))/n_done_trials; + SessionPerformanceSection_timeout_rate.value = numel(find(timeout_history))/n_done_trials; + if n_done_trials >= 20 SessionPerformanceSection_violation_recent.value = numel(find(violation_history(end-19:end)))/20; SessionPerformanceSection_timeout_recent.value = numel(find(timeout_history(end-19:end)))/20; else @@ -539,7 +539,7 @@ % if ParamsSection_use_auto_train % do completion check if auto training cp_max = value(TrainingStageParamsSection_max_CP); - if value(ParamsSection_CP_duration) >= cp_max && n_completed_trials > 100 && ... + if value(ParamsSection_CP_duration) >= cp_max && n_done_trials > 100 && ... value(SessionPerformanceSection_violation_recent) < value(TrainingStageParamsSection_recent_violation) && ... value(SessionPerformanceSection_timeout_recent) < value(TrainingStageParamsSection_recent_timeout) && ... value(SessionPerformanceSection_violation_stage) < value(TrainingStageParamsSection_stage_violation) @@ -621,9 +621,9 @@ % Change the value of CP Duration % Since starting a new session then do a pre warm up to last saved cp % duration else continue with learning with increased poke time -if n_completed_trials == 0 +if n_done_trials == 0 ParamsSection_CP_duration.value = value(ParamsSection_init_CP_duration); -elseif n_completed_trials == 1 +elseif n_done_trials == 1 ParamsSection_CP_duration.value = starting_cp; else if ~violation_history(end) && ~timeout_history(end) @@ -677,7 +677,7 @@ callback(ParamsSection_A1_time); end -if n_completed_trials > 0 +if n_done_trials > 0 % Updating Disp Values for Training_Peformance_Summary PerformanceSummarySection_stage_5_Trials.value = value(PerformanceSummarySection_stage_5_Trials) + 1; PerformanceSummarySection_stage_5_TrialsToday.value = value(PerformanceSummarySection_stage_5_TrialsToday) + 1; @@ -694,10 +694,10 @@ callback(PerformanceSummarySection_stage_5_TrialsValid); % Updating Disp Values for Training_Peformance_Summary - SessionPerformanceSection_ntrials.value = n_completed_trials; - SessionPerformanceSection_violation_rate.value = numel(find(violation_history))/n_completed_trials; - SessionPerformanceSection_timeout_rate.value = numel(find(timeout_history))/n_completed_trials; - if n_completed_trials >= 20 + SessionPerformanceSection_ntrials.value = n_done_trials; + SessionPerformanceSection_violation_rate.value = numel(find(violation_history))/n_done_trials; + SessionPerformanceSection_timeout_rate.value = numel(find(timeout_history))/n_done_trials; + if n_done_trials >= 20 SessionPerformanceSection_violation_recent.value = numel(find(violation_history(end-19:end)))/20; SessionPerformanceSection_timeout_recent.value = numel(find(timeout_history(end-19:end)))/20; else @@ -731,7 +731,7 @@ stage_no = value(SessionDefinition_CURRENT_ACTIVE_STAGE); if value(ParamsSection_CP_duration) >= value(TrainingStageParamsSection_max_CP) && value(PerformanceSummarySection_stage_5_TrialsValid) > value(TrainingStageParamsSection_total_trials) if value(SessionPerformanceSection_violation_recent) < value(TrainingStageParamsSection_recent_violation) && value(SessionPerformanceSection_timeout_recent) < value(TrainingStageParamsSection_recent_timeout) && ... - value(SessionPerformanceSection_violation_stage) < value(TrainingStageParamsSection_stage_violation) && n_completed_trials > 100 + value(SessionPerformanceSection_violation_stage) < value(TrainingStageParamsSection_stage_violation) && n_done_trials > 100 ParamsSection_training_stage.value = stage_no + 1; callback(ParamsSection_training_stage); ParamsSection(obj, 'Changed_Training_Stage'); @@ -806,9 +806,9 @@ end % Warm Up If starting a new session -if n_completed_trials == 0 +if n_done_trials == 0 ParamsSection_CP_duration.value = value(ParamsSection_init_CP_duration); -elseif n_completed_trials == 1 +elseif n_done_trials == 1 ParamsSection_CP_duration.value = starting_cp; else if value(ParamsSection_CP_duration) < cp_max % warm up stage @@ -846,7 +846,7 @@ callback(ParamsSection_A1_time); end -if n_completed_trials > 0 +if n_done_trials > 0 % Updating Disp Values for Training_Peformance_Summary PerformanceSummarySection_stage_6_Trials.value = value(PerformanceSummarySection_stage_6_Trials) + 1; PerformanceSummarySection_stage_6_TrialsToday.value = value(PerformanceSummarySection_stage_6_TrialsToday) + 1; @@ -863,10 +863,10 @@ callback(PerformanceSummarySection_stage_6_TrialsValid); % Updating Disp Values for SessionPerformanceSection - SessionPerformanceSection_ntrials.value = n_completed_trials; - SessionPerformanceSection_violation_rate.value = numel(find(violation_history))/n_completed_trials; - SessionPerformanceSection_timeout_rate.value = numel(find(timeout_history))/n_completed_trials; - if n_completed_trials >= 20 + SessionPerformanceSection_ntrials.value = n_done_trials; + SessionPerformanceSection_violation_rate.value = numel(find(violation_history))/n_done_trials; + SessionPerformanceSection_timeout_rate.value = numel(find(timeout_history))/n_done_trials; + if n_done_trials >= 20 SessionPerformanceSection_violation_recent.value = numel(find(violation_history(end-19:end)))/20; SessionPerformanceSection_timeout_recent.value = numel(find(timeout_history(end-19:end)))/20; else @@ -900,7 +900,7 @@ stage_no = value(SessionDefinition_CURRENT_ACTIVE_STAGE); if value(PerformanceSummarySection_stage_6_TrialsValid) > value(TrainingStageParamsSection_total_trials) if value(SessionPerformanceSection_violation_recent) < value(TrainingStageParamsSection_recent_violation) && value(SessionPerformanceSection_timeout_recent) < value(TrainingStageParamsSection_recent_timeout) && ... - value(SessionPerformanceSection_violation_stage) < value(TrainingStageParamsSection_stage_violation) && n_completed_trials > 100 + value(SessionPerformanceSection_violation_stage) < value(TrainingStageParamsSection_stage_violation) && n_done_trials > 100 ParamsSection_training_stage.value = stage_no + 1; callback(ParamsSection_training_stage); ParamsSection(obj, 'Changed_Training_Stage'); @@ -984,10 +984,10 @@ % Warm Up If starting a new session if warm_up_on == 1 - if n_completed_trials == 0 + if n_done_trials == 0 ParamsSection_CP_duration.value = value(ParamsSection_init_CP_duration); warmup_completed = 0; - elseif n_completed_trials == 1 + elseif n_done_trials == 1 ParamsSection_CP_duration.value = starting_cp; else if value(ParamsSection_CP_duration) <= cp_max % warm up stage @@ -1009,7 +1009,7 @@ warmup_completed = 1; end -if n_completed_trials >= 1 +if n_done_trials >= 1 cp_length = value(ParamsSection_CP_duration) - value(ParamsSection_SettlingIn_time); [ParamsSection_PreStim_time.value ,ParamsSection_A1_time.value,ParamsSection_time_bet_aud1_gocue.value] = param_time_within_range(warmup_completed,... cp_length,prestim_min,prestim_max, random_prestim, prestim_time,... @@ -1022,7 +1022,7 @@ end callback(ParamsSection_CP_duration); -if n_completed_trials > 0 +if n_done_trials > 0 % Updating Disp Values for Training_Peformance_Summary PerformanceSummarySection_stage_7_Trials.value = value(PerformanceSummarySection_stage_7_Trials) + 1; PerformanceSummarySection_stage_7_TrialsToday.value = value(PerformanceSummarySection_stage_7_TrialsToday) + 1; @@ -1039,10 +1039,10 @@ callback(PerformanceSummarySection_stage_7_TrialsValid); % Updating Disp Values for SessionPerformanceSection - SessionPerformanceSection_ntrials.value = n_completed_trials; - SessionPerformanceSection_violation_rate.value = numel(find(violation_history))/n_completed_trials; - SessionPerformanceSection_timeout_rate.value = numel(find(timeout_history))/n_completed_trials; - if n_completed_trials >= 20 + SessionPerformanceSection_ntrials.value = n_done_trials; + SessionPerformanceSection_violation_rate.value = numel(find(violation_history))/n_done_trials; + SessionPerformanceSection_timeout_rate.value = numel(find(timeout_history))/n_done_trials; + if n_done_trials >= 20 SessionPerformanceSection_violation_recent.value = numel(find(violation_history(end-19:end)))/20; SessionPerformanceSection_timeout_recent.value = numel(find(timeout_history(end-19:end)))/20; else @@ -1156,10 +1156,10 @@ % Warm Up If starting a new session if warm_up_on == 1 - if n_completed_trials == 0 + if n_done_trials == 0 ParamsSection_CP_duration.value = value(ParamsSection_init_CP_duration); warmup_completed = 0; - elseif n_completed_trials == 1 + elseif n_done_trials == 1 ParamsSection_CP_duration.value = starting_cp; else if value(ParamsSection_CP_duration) <= cp_max % warm up stage @@ -1181,7 +1181,7 @@ warmup_completed = 1; end -if n_completed_trials >= 1 +if n_done_trials >= 1 cp_length = value(ParamsSection_CP_duration) - value(ParamsSection_SettlingIn_time); [ParamsSection_PreStim_time.value ,ParamsSection_A1_time.value,ParamsSection_time_bet_aud1_gocue.value] = param_time_within_range(warmup_completed,... cp_length,prestim_min,prestim_max, random_prestim, prestim_time,... @@ -1194,7 +1194,7 @@ end callback(ParamsSection_CP_duration); -if n_completed_trials > 0 +if n_done_trials > 0 % Updating Disp Values for Training_Peformance_Summary PerformanceSummarySection_stage_8_Trials.value = value(PerformanceSummarySection_stage_8_Trials) + 1; PerformanceSummarySection_stage_8_TrialsToday.value = value(PerformanceSummarySection_stage_8_TrialsToday) + 1; @@ -1211,10 +1211,10 @@ callback(PerformanceSummarySection_stage_8_TrialsValid); % Updating Disp Values for SessionPerformanceSection - SessionPerformanceSection_ntrials.value = n_completed_trials; - SessionPerformanceSection_violation_rate.value = numel(find(violation_history))/n_completed_trials; - SessionPerformanceSection_timeout_rate.value = numel(find(timeout_history))/n_completed_trials; - if n_completed_trials >= 20 + SessionPerformanceSection_ntrials.value = n_done_trials; + SessionPerformanceSection_violation_rate.value = numel(find(violation_history))/n_done_trials; + SessionPerformanceSection_timeout_rate.value = numel(find(timeout_history))/n_done_trials; + if n_done_trials >= 20 SessionPerformanceSection_violation_recent.value = numel(find(violation_history(end-19:end)))/20; SessionPerformanceSection_timeout_recent.value = numel(find(timeout_history(end-19:end)))/20; else diff --git a/Protocols/@ArpitCentrePokeTraining/private/set_training_stage_last_setting_file.m b/Protocols/@ArpitCentrePokeTraining/private/set_training_stage_last_setting_file.m new file mode 100644 index 00000000..29e4b3b1 --- /dev/null +++ b/Protocols/@ArpitCentrePokeTraining/private/set_training_stage_last_setting_file.m @@ -0,0 +1,39 @@ +function set_training_stage_last_setting_file(owner,experimenter,ratname) + +global Solo_datadir; + + +% Get the latest setting file + +u = dir([Solo_datadir 'settings_@' owner '_' experimenter '_' ratname '*.mat']); +if ~isempty(u) + [filenames{1:length(u)}] = deal(u.name); + filenames = sort(filenames'); %#ok (can't use dimension argument with cell sort) + today_date_str = regexprep(char(datetime('today','Format','yy-MM-dd')), '[^0-9]', ''); + for i=length(u):-1:1 % search from the end back + file_date_num = textscan(filenames{i},[sets_or_data '_' owner '_' experimenter '_' ratname '_%n%*c.mat']); + + if ~isempty(file_date_num{1}) && file_date_num{1} <= str2double(today_date_str) + fullname = [rat_dir filenames{i}]; % We've found it. + break + end + end +end + +try + loaded_data = load(fullname); +catch + return; +end + +% Lets find the Handle for Training Stage +handles = get_sphandle('owner', owner); +get_ghandle('ParamsSection_training_stage') +for hi= 1:length(handles) + sph_fullname=get_fullname(handles{hi}); + handles{hi}.value = loaded_data.saved.(sph_fullname); + get_callback(updated_handles{i}); +end + + +end \ No newline at end of file diff --git a/Protocols/@ArpitSoundCalibration/ArpitSoundCalibration.m b/Protocols/@ArpitSoundCalibration/ArpitSoundCalibration.m index de805b7b..70b8c595 100644 --- a/Protocols/@ArpitSoundCalibration/ArpitSoundCalibration.m +++ b/Protocols/@ArpitSoundCalibration/ArpitSoundCalibration.m @@ -197,14 +197,14 @@ % Calculate individual group width based on screen dimensions and number of groups groupwidth = floor((MP(3)/2)/numel(linegroups)); - padding = 10 % padding around GUI elements + padding = 10; % padding around GUI elements % Calculate total width needed for all groups combined total_width = floor(numel(linegroups) * groupwidth+padding); - total_height = 400 % total height of GUI + total_height = 400; % total height of GUI - label_height = 140 % height of port label - button_height = 25 + label_height = 140; % height of port label + button_height = 25; % Center the GUI horizontally on screen left_pos = floor((MP(3) - total_width) / 2); From b9d01f53c326a53a1219850bf5f08b191ef9e78a Mon Sep 17 00:00:00 2001 From: Arpit Date: Tue, 20 May 2025 14:44:14 +0100 Subject: [PATCH 109/164] loading training stage during init in ArpitCentrePokeTraining --- .../ArpitCentrePokeTraining.m | 3 ++- .../set_training_stage_last_setting_file.m | 24 ++++++++++++++----- 2 files changed, 20 insertions(+), 7 deletions(-) diff --git a/Protocols/@ArpitCentrePokeTraining/ArpitCentrePokeTraining.m b/Protocols/@ArpitCentrePokeTraining/ArpitCentrePokeTraining.m index 7779eb08..72c767ed 100644 --- a/Protocols/@ArpitCentrePokeTraining/ArpitCentrePokeTraining.m +++ b/Protocols/@ArpitCentrePokeTraining/ArpitCentrePokeTraining.m @@ -188,8 +188,9 @@ [x, y] = BonsaiCameraInterface(obj,'init',x,y,name,expmtr,rname); - % Before Loading the TrainingStageParamsSection, let me first check if + % Before the TrainingStageParamsSection, let's first check if % the setting file exist for this rat and only load the training stage + % from there. % Problem: Loading settings in later stages fails because the stage-dependent % TrainingStageParamsSection's solohandles are not visible after initial setup. % Solution: This section handles a specific scenario for loading setting files during initialization. diff --git a/Protocols/@ArpitCentrePokeTraining/private/set_training_stage_last_setting_file.m b/Protocols/@ArpitCentrePokeTraining/private/set_training_stage_last_setting_file.m index 29e4b3b1..92dd3cd0 100644 --- a/Protocols/@ArpitCentrePokeTraining/private/set_training_stage_last_setting_file.m +++ b/Protocols/@ArpitCentrePokeTraining/private/set_training_stage_last_setting_file.m @@ -1,17 +1,19 @@ function set_training_stage_last_setting_file(owner,experimenter,ratname) -global Solo_datadir; +try +global Solo_datadir; +rat_dir = sprintf('%s\\Settings\\%s\\%s\\',Solo_datadir,experimenter,ratname); % Get the latest setting file -u = dir([Solo_datadir 'settings_@' owner '_' experimenter '_' ratname '*.mat']); +u = dir([rat_dir 'settings_@' owner '_' experimenter '_' ratname '*.mat']); if ~isempty(u) [filenames{1:length(u)}] = deal(u.name); filenames = sort(filenames'); %#ok (can't use dimension argument with cell sort) today_date_str = regexprep(char(datetime('today','Format','yy-MM-dd')), '[^0-9]', ''); for i=length(u):-1:1 % search from the end back - file_date_num = textscan(filenames{i},[sets_or_data '_' owner '_' experimenter '_' ratname '_%n%*c.mat']); + file_date_num = textscan(filenames{i},['settings_@' owner '_' experimenter '_' ratname '_%n%*c.mat']); if ~isempty(file_date_num{1}) && file_date_num{1} <= str2double(today_date_str) fullname = [rat_dir filenames{i}]; % We've found it. @@ -28,12 +30,22 @@ function set_training_stage_last_setting_file(owner,experimenter,ratname) % Lets find the Handle for Training Stage handles = get_sphandle('owner', owner); -get_ghandle('ParamsSection_training_stage') +stage_handle_name = 'ParamsSection_training_stage'; for hi= 1:length(handles) sph_fullname=get_fullname(handles{hi}); - handles{hi}.value = loaded_data.saved.(sph_fullname); - get_callback(updated_handles{i}); + if strcmpi(sph_fullname,stage_handle_name) + handles{hi}.value = loaded_data.saved.(sph_fullname); + callback(handles{i}); + break + end end +return + +catch + + return + +end end \ No newline at end of file From 71a575b6f0646326c40948a32a0d00e72533703d Mon Sep 17 00:00:00 2001 From: Arpit Date: Tue, 20 May 2025 20:06:27 +0100 Subject: [PATCH 110/164] added data to sqltable --- .../@sqlsummary/CentrePoketrainingsummary.m | 296 ++++++++++++++++++ .../ArpitCentrePokeTraining.m | 8 +- .../PerformanceSummarySection.m | 72 ++++- .../SessionPerformanceSection.m | 3 +- 4 files changed, 362 insertions(+), 17 deletions(-) create mode 100644 ExperPort/Plugins/@sqlsummary/CentrePoketrainingsummary.m diff --git a/ExperPort/Plugins/@sqlsummary/CentrePoketrainingsummary.m b/ExperPort/Plugins/@sqlsummary/CentrePoketrainingsummary.m new file mode 100644 index 00000000..8f6d9dcc --- /dev/null +++ b/ExperPort/Plugins/@sqlsummary/CentrePoketrainingsummary.m @@ -0,0 +1,296 @@ +function [err] = CentrePoketrainingsummary(obj, varargin) + %% Function Description + % Sends session data to the bdata.sessions table + % By default, it tries to get data from standard plugins + % See the pairs structure below for details + + %% Initialize Error Logging + diary('sendsummary_error_log.txt'); + + try + %% Define Default Parameters + pairs = { + 'force_send', 0;... + 'hits', get_val('hit_history');... + 'sides', get_val('previous_sides');... + 'savetime', get_savetime(obj);... + 'endtime', get_endtime(obj);... + 'sessiondate', get_sessiondate(obj);... + 'hostname', get_rigid;... + 'IP_addr', get_network_info;... + 'experimenter', get_val('SavingSection_experimenter');... + 'ratname', get_val('SavingSection_ratname');... + 'n_done_trials', get_val('n_completed_trials');... + 'protocol', class(obj);... + 'protocol_data', 'NULL';... + 'peh', get_parsed_events;... + 'last_comment', cleanup(CommentsSection(obj,'get_latest'));... + 'data_file', SavingSection(obj,'get_data_file');... + 'technotes', get_val('TechComment')... % Added technician comments field + }; + parseargs(varargin, pairs); + + %% Validate Rig ID + [rigID, e, m] = bSettings('get','RIGS','Rig_ID'); + if ~force_send && isnan(rigID) + err = 42; + return + end + + %% Handle Tech Notes + if ~ischar(technotes) || isempty(technotes) + technotes = ''; + end + + %% Extract Path Information + [pth, fl] = extract_path(data_file); + + %% Calculate Performance Metrics + hits = hits(1:n_done_trials); + sides = sides(1:n_done_trials); + total_correct = nanmean(hits); + + try + right_correct = nanmean(hits(sides=='r')); + left_correct = nanmean(hits(sides=='l')); + catch ME + fprintf(2, 'Error calculating correct pokes\n'); + disp(ME.message); + disp(ME.stack); + right_correct = -1; + left_correct = -1; + end + + %% Calculate Violation Percentage + if strncmpi('pbups', protocol, 5) && isfield(protocol_data, 'violations') + percent_violations = mean(protocol_data.violations); + else + percent_violations = mean(isnan(hits)); + end + + %% Calculate Poke Counts + left_pokes = 0; + center_pokes = 0; + right_pokes = 0; + for px = 1:numel(peh) + left_pokes = left_pokes + numel(peh(px).pokes.L); + center_pokes = center_pokes + numel(peh(px).pokes.C); + right_pokes = right_pokes + numel(peh(px).pokes.R); + end + + %% Get Session ID and Start Time + sessid = getSessID(obj); + starttime = get_starttime(sessid); % added 20091214 + + if isempty(starttime) + % Compute start time if not found in sess_started table + starttime = datestr(datenum(savetime)-sess_length(obj)/60/60/24, 13); + else + % Update sess_started table indicating session end + bdata('call set_sess_ended("{Si}", "{Si}")', sessid, 1); + end + + %% Define SQL columns and placeholders + colstr = [ + 'sessid, ', ... + 'ratname, ', ... + 'hostname, ', ... + 'experimenter, ', ... + 'endtime, ', ... + 'starttime, ', ... + 'sessiondate, ', ... + 'protocol, ', ... + 'n_done_trials, ', ... + 'total_correct, ', ... + 'right_correct, ', ... + 'left_correct, ', ... + 'percent_violations, ', ... + 'protocol_data, ', ... + 'comments, ', ... + 'data_file, ', ... + 'data_path, ', ... + 'left_pokes, ', ... + 'center_pokes, ', ... + 'right_pokes, ', ... + 'technotes, ', ... + 'IP_addr' + ]; + + valstr = [ + '"{Si}",', ... % sessid + '"{S}",', ... % ratname + '"{S}",', ... % hostname + '"{S}",', ... % experimenter + '"{S}",', ... % endtime + '"{S}",', ... % starttime + '"{S}",', ... % sessiondate + '"{S}",', ... % protocol + '"{S}",', ... % n_done_trials + '"{S}",', ... % total_correct + '"{S}",', ... % right_correct + '"{S}",', ... % left_correct + '"{S}",', ... % percent_violations + '"{S}",', ... % protocol_data + '"{S}",', ... % comments + '"{S}",', ... % data_file + '"{S}",', ... % data_path + '"{S}",', ... % left_pokes + '"{S}",', ... % center_pokes + '"{S}",', ... % right_pokes + '"{S}",', ... % technotes + '"{S}"' % IP_addr + ]; + + + + %% Construct SQL string + sqlstr = ['insert into sessions (' strtrim(colstr) ') values (' strtrim(valstr) ')']; + + + %% Execute SQL Query + bdata(sqlstr, ... + sessid, ... + ratname, ... + hostname, ... + experimenter, ... + endtime, ... + starttime, ... + sessiondate, ... + protocol, ... + n_done_trials, ... + total_correct, ... + right_correct, ... + left_correct, ... + percent_violations, ... + protocol_data, ... + last_comment, ... + fl, ... + pth, ... + left_pokes, ... + center_pokes, ... + right_pokes, ... + technotes, ... + IP_addr ... + ); + + %% Insert Parsed Events + bdata('insert into parsed_events values ("{S}", "{M}")', sessid, peh); + + err = 0; + + % Log successful execution + fprintf('No errors encountered during sendsummary execution.\n'); + + + catch ME + fprintf(2, 'Failed to send summary to sql\n'); + disp(ME.message); + disp(ME.stack); + err = 1; + + % Log error details + fprintf('Error occurred during sendsummary execution:\n'); + fprintf('%s\n', ME.message); + fprintf('%s\n', ME.stack); + end + + diary off; +end + +%% Helper Functions +function y = get_val(x) + y = get_sphandle('fullname', x); + if isempty(y) + y = ''; + else + y = value(y{1}); + end +end + +function y = get_parsed_events + y = get_sphandle('fullname', 'ProtocolsSection_parsed_events'); + y = cell2mat(get_history(y{1})); +end + +function y = sess_length(obj) + % Estimate session length + GetSoloFunctionArgs(obj); + + try + st = parsed_events_history{1}.states; %#ok + ss = st.starting_state; + es = st.starting_state; + eval(['ST = min(min(st.', ss, '));']); + eval(['ET = max(max(st.', es, '));']); + + D1 = round(ET - ST); + + pt = get_sphandle('name', 'prot_title'); + [Ts, Te] = get_times_from_prottitle(value(pt{1})); + Ts = [Ts, ':00']; + Te = [Te, ':00']; + + Dt = timediff(Ts, Te, 2); + y = Dt + D1; + catch ME + showerror; % Assuming showerror is a function that displays errors + fprintf(2, 'Error calculating session length\n'); + disp(ME.message); + disp(ME.stack); + end +end + +function y = cleanup(M) + try + y = strtrim(sprintf('%s', M')); + catch + y = ''; + end +end + +function [p, f] = extract_path(s) + last_fs = find(s == filesep, 1, 'last' ); + p = s(1:last_fs); + f = s(last_fs+1:end); +end + +function y = get_savetime(obj) + [x, x, y] = SavingSection(obj, 'get_info'); + if y == '_' + y = datestr(now); + end +end + +function y = get_endtime(obj) + [x, x, savetime] = SavingSection(obj, 'get_info'); + if savetime == '_' + y = datestr(now, 13); + else + y = datestr(savetime, 13); + end +end + +function y = get_starttime(sessid) + y = bdata('select starttime from sess_started where sessid="{Si}"', sessid); + if ~isempty(y) + y = y{1}; + end +end + +function y = get_sessiondate(obj) + [x, x, savetime] = SavingSection(obj, 'get_info'); + if savetime == '_' + y = datestr(now, 29); + else + y = datestr(savetime, 29); + end +end + +function y = get_rigid + y = getRigID; + if isnan(y) + y = 'Unknown'; + elseif isnumeric(y) + y = sprintf('Rig%02d', y); + end +end diff --git a/Protocols/@ArpitCentrePokeTraining/ArpitCentrePokeTraining.m b/Protocols/@ArpitCentrePokeTraining/ArpitCentrePokeTraining.m index 72c767ed..d3d5e649 100644 --- a/Protocols/@ArpitCentrePokeTraining/ArpitCentrePokeTraining.m +++ b/Protocols/@ArpitCentrePokeTraining/ArpitCentrePokeTraining.m @@ -323,9 +323,13 @@ % Sending protocol data as structure is causing error in saving bdata, % instead not sending it asif required can be taken from session data - sendsummary(obj); - % perf = SessionPerformanceSection(obj, 'evaluate'); + % sendsummary(obj); + perf = struct([]); + perf = PerformanceSummarySection(obj, 'evaluate'); + [perf.violation_rate,perf.timeout_rate] = SessionPerformanceSection(obj, 'evaluate'); + + CentrePoketrainingsummary(obj,perf); % cp_durs = ParamsSection(obj, 'get_cp_history'); % [stim1dur] = ParamsSection(obj,'get_stimdur_history'); % pd.hits=hit_history(:); diff --git a/Protocols/@ArpitCentrePokeTraining/PerformanceSummarySection.m b/Protocols/@ArpitCentrePokeTraining/PerformanceSummarySection.m index 47838551..b5ef9d0e 100644 --- a/Protocols/@ArpitCentrePokeTraining/PerformanceSummarySection.m +++ b/Protocols/@ArpitCentrePokeTraining/PerformanceSummarySection.m @@ -77,19 +77,65 @@ x=oldx; y=oldy; figure(parentfig); -%% Evaluate - case 'evaluate' - - % if value(training_stage) == 8 && n_done_trials > 0 - % stage_8_Trials.value = value(stage_8_Trials) + 1; - % stage_8_TrialsToday.value = value(stage_8_TrialsToday) + 1; - % stage_8_ViolationRate.value = ((value(stage_8_ViolationRate) * (value(stage_8_Trials) - 1)) + double(violation_history(end))) / value(stage_8_Trials); - % stage_8_TimeoutRate.value = ((value(stage_8_TimeoutRate) * (value(stage_8_Trials) - 1)) + double(timeout_history(end))) / value(stage_8_Trials); - % if value(hit_history(end)) == 1 - % stage_8_TrialsValid.value = value(stage_8_TrialsValid) + 1; - % end - % end - % + % ------------------------------------------------------------------ + % evaluate + % ------------------------------------------------------------------ + + case 'evaluate' + + pd.stage1_trials_total = value(stage_1_Trials); + pd.stage1_trials_today = value(stage_1_TrialsToday); + pd.stage1_trials_valid = value(stage_1_TrialsValid); + pd.stage1_violationrate = value(stage_1_ViolationRate); + pd.stage1_timeoutrate = value(stage_1_TimeoutRate); + + pd.stage2_trials_total = value(stage_2_Trials); + pd.stage2_trials_today = value(stage_2_TrialsToday); + pd.stage2_trials_valid = value(stage_2_TrialsValid); + pd.stage2_violationrate = value(stage_2_ViolationRate); + pd.stage2_timeoutrate = value(stage_2_TimeoutRate); + + pd.stage3_trials_total = value(stage_3_Trials); + pd.stage3_trials_today = value(stage_3_TrialsToday); + pd.stage3_trials_valid = value(stage_3_TrialsValid); + pd.stage3_violationrate = value(stage_3_ViolationRate); + pd.stage3_timeoutrate = value(stage_3_TimeoutRate); + + pd.stage4_trials_total = value(stage_4_Trials); + pd.stage4_trials_today = value(stage_4_TrialsToday); + pd.stage4_trials_valid = value(stage_4_TrialsValid); + pd.stage4_violationrate = value(stage_4_ViolationRate); + pd.stage4_timeoutrate = value(stage_4_TimeoutRate); + + pd.stage5_trials_total = value(stage_5_Trials); + pd.stage5_trials_today = value(stage_5_TrialsToday); + pd.stage5_trials_valid = value(stage_5_TrialsValid); + pd.stage5_violationrate = value(stage_5_ViolationRate); + pd.stage5_timeoutrate = value(stage_5_TimeoutRate); + + pd.stage6_trials_total = value(stage_6_Trials); + pd.stage6_trials_today = value(stage_6_TrialsToday); + pd.stage6_trials_valid = value(stage_6_TrialsValid); + pd.stage6_violationrate = value(stage_6_ViolationRate); + pd.stage6_timeoutrate = value(stage_6_TimeoutRate); + + pd.stage7_trials_total = value(stage_7_Trials); + pd.stage7_trials_today = value(stage_7_TrialsToday); + pd.stage7_trials_valid = value(stage_7_TrialsValid); + pd.stage7_violationrate = value(stage_7_ViolationRate); + pd.stage7_timeoutrate = value(stage_7_TimeoutRate); + + pd.stage8_trials_total = value(stage_8_Trials); + pd.stage8_trials_today = value(stage_8_TrialsToday); + pd.stage8_trials_valid = value(stage_8_TrialsValid); + pd.stage8_violationrate = value(stage_8_ViolationRate); + pd.stage8_timeoutrate = value(stage_8_TimeoutRate); + + + if nargout > 0 + x = pd; + end + %% Case close case 'close' diff --git a/Protocols/@ArpitCentrePokeTraining/SessionPerformanceSection.m b/Protocols/@ArpitCentrePokeTraining/SessionPerformanceSection.m index b8fcb77b..717c4447 100644 --- a/Protocols/@ArpitCentrePokeTraining/SessionPerformanceSection.m +++ b/Protocols/@ArpitCentrePokeTraining/SessionPerformanceSection.m @@ -84,8 +84,7 @@ case 'evaluate' if nargout > 0 - x = [n_done_trials, value(ntrials_stage), value(violation_rate), value(timeout_rate), value(violation_recent), ... - value(timeout_recent), value(violation_stage), value(timeout_stage)]; + x = [value(violation_rate), value(timeout_rate)]; end % ------------------------------------------------------------------ From 3864c6cdec42b93f9bc05fcb3443c3225905c0ec Mon Sep 17 00:00:00 2001 From: Arpit Date: Tue, 20 May 2025 22:27:47 +0100 Subject: [PATCH 111/164] continued data addition to sql table --- .../@sqlsummary/CentrePoketrainingsummary.asv | 315 ++++++++++++++++++ .../@sqlsummary/CentrePoketrainingsummary.m | 160 ++++++--- .../ArpitCentrePokeTraining.m | 29 +- .../@ArpitCentrePokeTraining/ParamsSection.m | 6 +- 4 files changed, 446 insertions(+), 64 deletions(-) create mode 100644 ExperPort/Plugins/@sqlsummary/CentrePoketrainingsummary.asv diff --git a/ExperPort/Plugins/@sqlsummary/CentrePoketrainingsummary.asv b/ExperPort/Plugins/@sqlsummary/CentrePoketrainingsummary.asv new file mode 100644 index 00000000..ad199b5b --- /dev/null +++ b/ExperPort/Plugins/@sqlsummary/CentrePoketrainingsummary.asv @@ -0,0 +1,315 @@ +function [err] = CentrePoketrainingsummary(obj, varargin) + %% Function Description + % Sends session data to the bdata.sessions table + % By default, it tries to get data from standard plugins + % See the pairs structure below for details + + %% Initialize Error Logging + diary('sendsummary_error_log.txt'); + + try + %% Define Default Parameters + pairs = { + 'force_send', 0;... + 'savetime', get_savetime(obj);... + 'endtime', get_endtime(obj);... + 'sessiondate', get_sessiondate(obj);... + 'hostname', get_rigid;... + 'IP_addr', get_network_info;... + 'experimenter', get_val('SavingSection_experimenter');... + 'ratname', get_val('SavingSection_ratname');... + 'n_done_trials', get_val('n_completed_trials');... + 'protocol', class(obj);... + 'protocol_data', 'NULL';... + 'peh', get_parsed_events;... + 'last_comment', cleanup(CommentsSection(obj,'get_latest'));... + 'data_file', SavingSection(obj,'get_data_file');... + 'technotes', get_val('TechComment')... % Added technician comments field + }; + parseargs(varargin, pairs); + + %% Validate Rig ID + [rigID, e, m] = bSettings('get','RIGS','Rig_ID'); + if ~force_send && isnan(rigID) + err = 42; + return + end + + %% Handle Tech Notes + if ~ischar(technotes) || isempty(technotes) + technotes = ''; + end + + %% Extract Path Information + [pth, fl] = extract_path(data_file); + + %% Calculate Violation Percentage + if isfield(protocol_data, 'violation_rate') + percent_violations = perf.violation_rate; + else + percent_violations = []; + end + + %% Calculate Poke Counts + left_pokes = 0; + center_pokes = 0; + right_pokes = 0; + for px = 1:numel(peh) + left_pokes = left_pokes + numel(peh(px).pokes.L); + center_pokes = center_pokes + numel(peh(px).pokes.C); + right_pokes = right_pokes + numel(peh(px).pokes.R); + end + + %% Get Session ID and Start Time + sessid = getSessID(obj); + starttime = get_starttime(sessid); % added 20091214 + + if isempty(starttime) + % Compute start time if not found in sess_started table + starttime = datestr(datenum(savetime)-sess_length(obj)/60/60/24, 13); + else + % Update sess_started table indicating session end + bdata('call set_sess_ended("{Si}", "{Si}")', sessid, 1); + end + + %% Define SQL columns and placeholders + colstr = [ + 'sessid',... + 'sessiondate'... DATE + 'starttime'... TIME + 'endtime'... TIME + 'ratname'... VARCHAR + 'experimenter'... VARCHAR + 'protocol'... VARCHAR + 'hostname'... VARCHAR + 'IP_address'... VARCHAR + 'training_stage_no'... INT + 'training_stage_name'... VARCHAR + 'n_done_trials'... INT + 'percent_violations'... VARCHAR + 'percent_timeout'... VARCHAR + 'stage1_trials_total'... INT + 'stage1_trials_today'... INT + 'stage1_trials_valid'... INT + 'stage1_percent_violation'... VARCHAR + 'stage1_percent_timeout'... VARCHAR + 'stage2_trials_total'... INT + 'stage2_trials_today'... INT + 'stage2_trials_valid'... INT + 'stage2_percent_violation'... VARCHAR + 'stage2_percent_timeout'... VARCHAR + 'stage3_trials_total'... INT + 'stage3_trials_today'... INT + 'stage3_trials_valid'... INT + 'stage3_percent_violation'... VARCHAR + 'stage3_percent_timeout'... VARCHAR + 'stage4_trials_total'... INT + 'stage4_trials_today'... INT + 'stage4_trials_valid'... INT + 'stage4_percent_violation'... VARCHAR + 'stage4_percent_timeout'... VARCHAR + 'stage5_trials_total'... INT + 'stage5_trials_today'... INT + 'stage5_trials_valid'... INT + 'stage5_percent_violation'... VARCHAR + 'stage5_percent_timeout'... VARCHAR + 'stage6_trials_total'... INT + 'stage6_trials_today'... INT + 'stage6_trials_valid'... INT + 'stage6_percent_violation'... VARCHAR + 'stage6_percent_timeout'... VARCHAR + 'stage7_trials_total'... INT + 'stage7_trials_today'... INT + 'stage7_trials_valid'... INT + 'stage7_percent_violation'... VARCHAR + 'stage7_percent_timeout'... VARCHAR + 'stage8_trials_total'... INT + 'stage8_trials_today'... INT + 'stage8_trials_valid'... INT + 'stage8_percent_violation'... VARCHAR + 'stage8_percent_timeout'... VARCHAR + 'datafile'... VARCHAR + 'datapath'... VARCHAR + 'videofile'... VARCHAR + 'videopath'... VARCHAR + 'centre_poke'... VARCHAR + 'left_poke'... VARCHAR + 'right_poke'... VARCHAR + 'comments'... VARCHAR + 'tech_notes']; + + + + valstr = [ + '"{Si}",', ... % sessid + '"{S}",', ... % ratname + '"{S}",', ... % hostname + '"{S}",', ... % experimenter + '"{S}",', ... % endtime + '"{S}",', ... % starttime + '"{S}",', ... % sessiondate + '"{S}",', ... % protocol + '"{S}",', ... % n_done_trials + '"{S}",', ... % total_correct + '"{S}",', ... % right_correct + '"{S}",', ... % left_correct + '"{S}",', ... % percent_violations + '"{S}",', ... % protocol_data + '"{S}",', ... % comments + '"{S}",', ... % data_file + '"{S}",', ... % data_path + '"{S}",', ... % left_pokes + '"{S}",', ... % center_pokes + '"{S}",', ... % right_pokes + '"{S}",', ... % technotes + '"{S}"' % IP_addr + ]; + + + + %% Construct SQL string + sqlstr = ['insert into sessions (' strtrim(colstr) ') values (' strtrim(valstr) ')']; + + + %% Execute SQL Query + bdata(sqlstr, ... + sessid, ... + ratname, ... + hostname, ... + experimenter, ... + endtime, ... + starttime, ... + sessiondate, ... + protocol, ... + n_done_trials, ... + total_correct, ... + right_correct, ... + left_correct, ... + percent_violations, ... + protocol_data, ... + last_comment, ... + fl, ... + pth, ... + left_pokes, ... + center_pokes, ... + right_pokes, ... + technotes, ... + IP_addr ... + ); + + % Log successful execution + fprintf('No errors encountered during sendsummary execution.\n'); + + + catch ME + fprintf(2, 'Failed to send summary to sql\n'); + disp(ME.message); + disp(ME.stack); + err = 1; + + % Log error details + fprintf('Error occurred during sendsummary execution:\n'); + fprintf('%s\n', ME.message); + fprintf('%s\n', ME.stack); + end + + diary off; +end + +%% Helper Functions +function y = get_val(x) + y = get_sphandle('fullname', x); + if isempty(y) + y = ''; + else + y = value(y{1}); + end +end + +function y = get_parsed_events + y = get_sphandle('fullname', 'ProtocolsSection_parsed_events'); + y = cell2mat(get_history(y{1})); +end + +function y = sess_length(obj) + % Estimate session length + GetSoloFunctionArgs(obj); + + try + st = parsed_events_history{1}.states; %#ok + ss = st.starting_state; + es = st.starting_state; + eval(['ST = min(min(st.', ss, '));']); + eval(['ET = max(max(st.', es, '));']); + + D1 = round(ET - ST); + + pt = get_sphandle('name', 'prot_title'); + [Ts, Te] = get_times_from_prottitle(value(pt{1})); + Ts = [Ts, ':00']; + Te = [Te, ':00']; + + Dt = timediff(Ts, Te, 2); + y = Dt + D1; + catch ME + showerror; % Assuming showerror is a function that displays errors + fprintf(2, 'Error calculating session length\n'); + disp(ME.message); + disp(ME.stack); + end +end + +function y = cleanup(M) + try + y = strtrim(sprintf('%s', M')); + catch + y = ''; + end +end + +function [p, f] = extract_path(s) + last_fs = find(s == filesep, 1, 'last' ); + p = s(1:last_fs); + f = s(last_fs+1:end); +end + +function y = get_savetime(obj) + [x, x, y] = SavingSection(obj, 'get_info'); + if y == '_' + y = datestr(now); + end +end + +function y = get_endtime(obj) + [x, x, savetime] = SavingSection(obj, 'get_info'); + if savetime == '_' + y = datestr(now, 13); + else + y = datestr(savetime, 13); + end +end + +function y = get_starttime(sessid) + y = bdata('select starttime from sess_started where sessid="{Si}"', sessid); + if ~isempty(y) + y = y{1}; + end +end + +function y = get_sessiondate(obj) + [x, x, savetime] = SavingSection(obj, 'get_info'); + if savetime == '_' + y = datestr(now, 29); + else + y = datestr(savetime, 29); + end +end + +function y = get_rigid + y = getRigID; + if isnan(y) + y = 'Unknown'; + elseif isnumeric(y) + y = sprintf('Rig%02d', y); + end +end diff --git a/ExperPort/Plugins/@sqlsummary/CentrePoketrainingsummary.m b/ExperPort/Plugins/@sqlsummary/CentrePoketrainingsummary.m index 8f6d9dcc..21b7ebf0 100644 --- a/ExperPort/Plugins/@sqlsummary/CentrePoketrainingsummary.m +++ b/ExperPort/Plugins/@sqlsummary/CentrePoketrainingsummary.m @@ -11,8 +11,6 @@ %% Define Default Parameters pairs = { 'force_send', 0;... - 'hits', get_val('hit_history');... - 'sides', get_val('previous_sides');... 'savetime', get_savetime(obj);... 'endtime', get_endtime(obj);... 'sessiondate', get_sessiondate(obj);... @@ -45,27 +43,11 @@ %% Extract Path Information [pth, fl] = extract_path(data_file); - %% Calculate Performance Metrics - hits = hits(1:n_done_trials); - sides = sides(1:n_done_trials); - total_correct = nanmean(hits); - - try - right_correct = nanmean(hits(sides=='r')); - left_correct = nanmean(hits(sides=='l')); - catch ME - fprintf(2, 'Error calculating correct pokes\n'); - disp(ME.message); - disp(ME.stack); - right_correct = -1; - left_correct = -1; - end - %% Calculate Violation Percentage - if strncmpi('pbups', protocol, 5) && isfield(protocol_data, 'violations') - percent_violations = mean(protocol_data.violations); + if isfield(protocol_data, 'violation_rate') + percent_violations = perf.violation_rate; else - percent_violations = mean(isnan(hits)); + percent_violations = []; end %% Calculate Poke Counts @@ -92,29 +74,71 @@ %% Define SQL columns and placeholders colstr = [ - 'sessid, ', ... - 'ratname, ', ... - 'hostname, ', ... - 'experimenter, ', ... - 'endtime, ', ... - 'starttime, ', ... - 'sessiondate, ', ... - 'protocol, ', ... - 'n_done_trials, ', ... - 'total_correct, ', ... - 'right_correct, ', ... - 'left_correct, ', ... - 'percent_violations, ', ... - 'protocol_data, ', ... - 'comments, ', ... - 'data_file, ', ... - 'data_path, ', ... - 'left_pokes, ', ... - 'center_pokes, ', ... - 'right_pokes, ', ... - 'technotes, ', ... - 'IP_addr' - ]; + 'sessid',... + 'sessiondate'... DATE + 'starttime'... TIME + 'endtime'... TIME + 'ratname'... VARCHAR + 'experimenter'... VARCHAR + 'protocol'... VARCHAR + 'hostname'... VARCHAR + 'IP_address'... VARCHAR + 'training_stage_no'... INT + 'training_stage_name'... VARCHAR + 'n_done_trials'... INT + 'percent_violations'... VARCHAR + 'percent_timeout'... VARCHAR + 'stage1_trials_total'... INT + 'stage1_trials_today'... INT + 'stage1_trials_valid'... INT + 'stage1_percent_violation'... VARCHAR + 'stage1_percent_timeout'... VARCHAR + 'stage2_trials_total'... INT + 'stage2_trials_today'... INT + 'stage2_trials_valid'... INT + 'stage2_percent_violation'... VARCHAR + 'stage2_percent_timeout'... VARCHAR + 'stage3_trials_total'... INT + 'stage3_trials_today'... INT + 'stage3_trials_valid'... INT + 'stage3_percent_violation'... VARCHAR + 'stage3_percent_timeout'... VARCHAR + 'stage4_trials_total'... INT + 'stage4_trials_today'... INT + 'stage4_trials_valid'... INT + 'stage4_percent_violation'... VARCHAR + 'stage4_percent_timeout'... VARCHAR + 'stage5_trials_total'... INT + 'stage5_trials_today'... INT + 'stage5_trials_valid'... INT + 'stage5_percent_violation'... VARCHAR + 'stage5_percent_timeout'... VARCHAR + 'stage6_trials_total'... INT + 'stage6_trials_today'... INT + 'stage6_trials_valid'... INT + 'stage6_percent_violation'... VARCHAR + 'stage6_percent_timeout'... VARCHAR + 'stage7_trials_total'... INT + 'stage7_trials_today'... INT + 'stage7_trials_valid'... INT + 'stage7_percent_violation'... VARCHAR + 'stage7_percent_timeout'... VARCHAR + 'stage8_trials_total'... INT + 'stage8_trials_today'... INT + 'stage8_trials_valid'... INT + 'stage8_percent_violation'... VARCHAR + 'stage8_percent_timeout'... VARCHAR + 'datafile'... VARCHAR + 'datapath'... VARCHAR + 'videofile'... VARCHAR + 'videopath'... VARCHAR + 'centre_poke'... VARCHAR + 'left_poke'... VARCHAR + 'right_poke'... VARCHAR + 'comments'... VARCHAR + 'tech_notes']; % total 63 columns + + valstr = [ '"{Si}",', ... % sessid @@ -139,12 +163,53 @@ '"{S}",', ... % right_pokes '"{S}",', ... % technotes '"{S}"' % IP_addr + '"{S}",', ... % ratname + '"{S}",', ... % hostname + '"{S}",', ... % experimenter + '"{S}",', ... % endtime + '"{S}",', ... % starttime + '"{S}",', ... % sessiondate + '"{S}",', ... % protocol + '"{S}",', ... % n_done_trials + '"{S}",', ... % total_correct + '"{S}",', ... % right_correct + '"{S}",', ... % left_correct + '"{S}",', ... % percent_violations + '"{S}",', ... % protocol_data + '"{S}",', ... % comments + '"{S}",', ... % data_file + '"{S}",', ... % data_path + '"{S}",', ... % left_pokes + '"{S}",', ... % center_pokes + '"{S}",', ... % right_pokes + '"{S}",', ... % technotes + '"{S}"' % IP_addr + '"{S}",', ... % ratname + '"{S}",', ... % hostname + '"{S}",', ... % experimenter + '"{S}",', ... % endtime + '"{S}",', ... % starttime + '"{S}",', ... % sessiondate + '"{S}",', ... % protocol + '"{S}",', ... % n_done_trials + '"{S}",', ... % total_correct + '"{S}",', ... % right_correct + '"{S}",', ... % left_correct + '"{S}",', ... % percent_violations + '"{S}",', ... % protocol_data + '"{S}",', ... % comments + '"{S}",', ... % data_file + '"{S}",', ... % data_path + '"{S}",', ... % left_pokes + '"{S}",', ... % center_pokes + '"{S}",', ... % right_pokes + '"{S}",', ... % technotes ]; %% Construct SQL string - sqlstr = ['insert into sessions (' strtrim(colstr) ') values (' strtrim(valstr) ')']; + sqlstr = ['insert into CentrePokeTraining (' strtrim(colstr) ') values (' strtrim(valstr) ')']; %% Execute SQL Query @@ -173,11 +238,6 @@ IP_addr ... ); - %% Insert Parsed Events - bdata('insert into parsed_events values ("{S}", "{M}")', sessid, peh); - - err = 0; - % Log successful execution fprintf('No errors encountered during sendsummary execution.\n'); diff --git a/Protocols/@ArpitCentrePokeTraining/ArpitCentrePokeTraining.m b/Protocols/@ArpitCentrePokeTraining/ArpitCentrePokeTraining.m index d3d5e649..a34765cf 100644 --- a/Protocols/@ArpitCentrePokeTraining/ArpitCentrePokeTraining.m +++ b/Protocols/@ArpitCentrePokeTraining/ArpitCentrePokeTraining.m @@ -327,25 +327,28 @@ % sendsummary(obj); perf = struct([]); perf = PerformanceSummarySection(obj, 'evaluate'); - [perf.violation_rate,perf.timeout_rate] = SessionPerformanceSection(obj, 'evaluate'); - - CentrePoketrainingsummary(obj,perf); - % cp_durs = ParamsSection(obj, 'get_cp_history'); - % [stim1dur] = ParamsSection(obj,'get_stimdur_history'); - % pd.hits=hit_history(:); - % pd.sides=previous_sides(:); - % pd.viols=violation_history(:); - % pd.timeouts=timeout_history(:); - % pd.cp_durs=cp_durs(:); - % pd.stim1dur=stim1dur(:); + [violation_rate,timeout_rate] = SessionPerformanceSection(obj, 'evaluate'); + perf.violation_rate = violation_rate; + perf.timeout_rate = timeout_rate; + + stage = ParamsSection(obj,'get_stage'); + perf.stage_no = stage; + + stage_name_list = {'Familiarize with Reward Side Pokes','Timeout Rewarded Side Pokes'... + 'Introduce Centre Poke','Introduce Violation for Centre Poke',... + 'Introduce Stimuli Sound during Centre Poke','Vary Stimuli location during Centre Poke'... + 'Variable Stimuli Go Cue location during Centre Poke','User Setting'}; + + perf.stage_name = stage_name_list{stage}; + perf.video_filepath = value(Video_Saving_Folder); + + CentrePoketrainingsummary(obj,'protocol_data',perf); % CommentsSection(obj, 'append_line', ... % sprintf(['ntrials = %d, violations = %.2f, timeouts=%.2f, hits = %.2f\n', ... % 'pre-Go cue went from %.3f to %.3f (delta=%.3f)\n', ... % 'Low = %.2f, High = %.2f'], ... % perf(1), perf(2), perf(3), perf(6), cp_durs(1), cp_durs(end), cp_durs(end)-cp_durs(1), classperf(1),classperf(2))); - - % sendsummary(obj,'protocol_data',pd); %% otherwise otherwise diff --git a/Protocols/@ArpitCentrePokeTraining/ParamsSection.m b/Protocols/@ArpitCentrePokeTraining/ParamsSection.m index 8c3c6ab4..45abb262 100644 --- a/Protocols/@ArpitCentrePokeTraining/ParamsSection.m +++ b/Protocols/@ArpitCentrePokeTraining/ParamsSection.m @@ -442,7 +442,11 @@ % right_wtr_mult.value=maxasymp + (minasymp./(1+(n_done_trials/inflp).^slp).^assym); x=left_wtr_mult+0; y=right_wtr_mult+0; - + + case 'get_stage' + + x = value(training_stage); + case 'get_previous_sides' x = value(previous_sides); %#ok From ec7e849276626c5f6d84557301da77a5b2985794 Mon Sep 17 00:00:00 2001 From: Arpit Date: Tue, 20 May 2025 23:17:45 +0100 Subject: [PATCH 112/164] same as last --- .../@bonsaicamera/BonsaiCameraInterface.m | 5 ++ .../ArpitCentrePokeTraining.m | 14 ++-- ...ing_SessionDefinition_AutoTrainingStages.m | 64 +++++++++---------- .../@ArpitCentrePokeTraining/ParamsSection.m | 1 + .../SessionPerformanceSection.m | 11 ++-- 5 files changed, 50 insertions(+), 45 deletions(-) diff --git a/ExperPort/Plugins/@bonsaicamera/BonsaiCameraInterface.m b/ExperPort/Plugins/@bonsaicamera/BonsaiCameraInterface.m index 6d4fc194..c4c93eb9 100644 --- a/ExperPort/Plugins/@bonsaicamera/BonsaiCameraInterface.m +++ b/ExperPort/Plugins/@bonsaicamera/BonsaiCameraInterface.m @@ -274,6 +274,11 @@ oscMsg_file_directory = createOSCMessage(recording_command_address,sprintf('%s\\BControlTrial%i_bonsaiTrial.avi',value(Video_Saving_Folder),n_done_trials+1)); write(value(UDPSender), oscMsg_file_directory, "uint8", bonsaiComputerIP,bonsaiUdpPort); + + case 'video_filepath' + + varargout{1} = value(Video_Saving_Folder); + %% close bonsai and command window case 'close' diff --git a/Protocols/@ArpitCentrePokeTraining/ArpitCentrePokeTraining.m b/Protocols/@ArpitCentrePokeTraining/ArpitCentrePokeTraining.m index a34765cf..ec8cc79b 100644 --- a/Protocols/@ArpitCentrePokeTraining/ArpitCentrePokeTraining.m +++ b/Protocols/@ArpitCentrePokeTraining/ArpitCentrePokeTraining.m @@ -327,20 +327,16 @@ % sendsummary(obj); perf = struct([]); perf = PerformanceSummarySection(obj, 'evaluate'); - [violation_rate,timeout_rate] = SessionPerformanceSection(obj, 'evaluate'); - perf.violation_rate = violation_rate; - perf.timeout_rate = timeout_rate; - - stage = ParamsSection(obj,'get_stage'); - perf.stage_no = stage; + [perf.violation_rate,perf.timeout_rate] = SessionPerformanceSection(obj, 'evaluate'); + [perf.stage_no,perf.CP_Duration] = ParamsSection(obj,'get_stage'); stage_name_list = {'Familiarize with Reward Side Pokes','Timeout Rewarded Side Pokes'... 'Introduce Centre Poke','Introduce Violation for Centre Poke',... 'Introduce Stimuli Sound during Centre Poke','Vary Stimuli location during Centre Poke'... 'Variable Stimuli Go Cue location during Centre Poke','User Setting'}; - - perf.stage_name = stage_name_list{stage}; - perf.video_filepath = value(Video_Saving_Folder); + perf.stage_name = stage_name_list{perf.stage_no}; + + perf.video_filepath = BonsaiCameraInterface(obj,'video_filepath'); CentrePoketrainingsummary(obj,'protocol_data',perf); diff --git a/Protocols/@ArpitCentrePokeTraining/ArpitCentrePokeTraining_SessionDefinition_AutoTrainingStages.m b/Protocols/@ArpitCentrePokeTraining/ArpitCentrePokeTraining_SessionDefinition_AutoTrainingStages.m index e1b0cbf5..1cd66448 100644 --- a/Protocols/@ArpitCentrePokeTraining/ArpitCentrePokeTraining_SessionDefinition_AutoTrainingStages.m +++ b/Protocols/@ArpitCentrePokeTraining/ArpitCentrePokeTraining_SessionDefinition_AutoTrainingStages.m @@ -92,8 +92,8 @@ % Updating Disp Values for Training_Peformance_Summary SessionPerformanceSection_ntrials.value = n_done_trials; - SessionPerformanceSection_violation_rate.value = nan; - SessionPerformanceSection_timeout_rate.value = nan; + SessionPerformanceSection_violation_percent.value = nan; + SessionPerformanceSection_timeout_percent.value = nan; SessionPerformanceSection_violation_recent.value = nan; SessionPerformanceSection_timeout_recent.value = nan; SessionPerformanceSection_ntrials_stage.value = value(PerformanceSummarySection_stage_1_Trials); @@ -104,8 +104,8 @@ callback(SessionPerformanceSection_ntrials); callback(SessionPerformanceSection_ntrials_stage); callback(SessionPerformanceSection_ntrials_stage_today) - callback(SessionPerformanceSection_violation_rate); - callback(SessionPerformanceSection_timeout_rate); + callback(SessionPerformanceSection_violation_percent); + callback(SessionPerformanceSection_timeout_percent); callback(SessionPerformanceSection_violation_recent); callback(SessionPerformanceSection_timeout_recent); callback(SessionPerformanceSection_violation_stage); @@ -262,8 +262,8 @@ % Updating Disp Values for Training_Peformance_Summary SessionPerformanceSection_ntrials.value = n_done_trials; - SessionPerformanceSection_violation_rate.value = nan; - SessionPerformanceSection_timeout_rate.value = numel(find(timeout_history))/n_done_trials; + SessionPerformanceSection_violation_percent.value = nan; + SessionPerformanceSection_timeout_percent.value = numel(find(timeout_history))/n_done_trials; SessionPerformanceSection_violation_recent.value = nan; if n_done_trials >= 20 SessionPerformanceSection_timeout_recent.value = numel(find(timeout_history(end-19:end)))/20; @@ -278,8 +278,8 @@ callback(SessionPerformanceSection_ntrials); callback(SessionPerformanceSection_ntrials_stage); callback(SessionPerformanceSection_ntrials_stage_today) - callback(SessionPerformanceSection_violation_rate); - callback(SessionPerformanceSection_timeout_rate); + callback(SessionPerformanceSection_violation_percent); + callback(SessionPerformanceSection_timeout_percent); callback(SessionPerformanceSection_violation_recent); callback(SessionPerformanceSection_timeout_recent); callback(SessionPerformanceSection_violation_stage); @@ -417,8 +417,8 @@ % Updating Disp Values for Training_Peformance_Summary SessionPerformanceSection_ntrials.value = n_done_trials; - SessionPerformanceSection_violation_rate.value = nan; - SessionPerformanceSection_timeout_rate.value = numel(find(timeout_history))/n_done_trials; + SessionPerformanceSection_violation_percent.value = nan; + SessionPerformanceSection_timeout_percent.value = numel(find(timeout_history))/n_done_trials; SessionPerformanceSection_violation_recent.value = nan; if n_done_trials >= 20 SessionPerformanceSection_timeout_recent.value = numel(find(timeout_history(end-19:end)))/20; @@ -433,8 +433,8 @@ callback(SessionPerformanceSection_ntrials); callback(SessionPerformanceSection_ntrials_stage); callback(SessionPerformanceSection_ntrials_stage_today) - callback(SessionPerformanceSection_violation_rate); - callback(SessionPerformanceSection_timeout_rate); + callback(SessionPerformanceSection_violation_percent); + callback(SessionPerformanceSection_timeout_percent); callback(SessionPerformanceSection_violation_recent); callback(SessionPerformanceSection_timeout_recent); callback(SessionPerformanceSection_violation_stage); @@ -578,8 +578,8 @@ % Updating Disp Values for Training_Peformance_Summary SessionPerformanceSection_ntrials.value = n_done_trials; - SessionPerformanceSection_violation_rate.value = numel(find(violation_history))/n_done_trials; - SessionPerformanceSection_timeout_rate.value = numel(find(timeout_history))/n_done_trials; + SessionPerformanceSection_violation_percent.value = numel(find(violation_history))/n_done_trials; + SessionPerformanceSection_timeout_percent.value = numel(find(timeout_history))/n_done_trials; if n_done_trials >= 20 SessionPerformanceSection_violation_recent.value = numel(find(violation_history(end-19:end)))/20; SessionPerformanceSection_timeout_recent.value = numel(find(timeout_history(end-19:end)))/20; @@ -595,8 +595,8 @@ callback(SessionPerformanceSection_ntrials); callback(SessionPerformanceSection_ntrials_stage); callback(SessionPerformanceSection_ntrials_stage_today) - callback(SessionPerformanceSection_violation_rate); - callback(SessionPerformanceSection_timeout_rate); + callback(SessionPerformanceSection_violation_percent); + callback(SessionPerformanceSection_timeout_percent); callback(SessionPerformanceSection_violation_recent); callback(SessionPerformanceSection_timeout_recent); callback(SessionPerformanceSection_violation_stage); @@ -780,8 +780,8 @@ % Updating Disp Values for Training_Peformance_Summary SessionPerformanceSection_ntrials.value = n_done_trials; - SessionPerformanceSection_violation_rate.value = numel(find(violation_history))/n_done_trials; - SessionPerformanceSection_timeout_rate.value = numel(find(timeout_history))/n_done_trials; + SessionPerformanceSection_violation_percent.value = numel(find(violation_history))/n_done_trials; + SessionPerformanceSection_timeout_percent.value = numel(find(timeout_history))/n_done_trials; if n_done_trials >= 20 SessionPerformanceSection_violation_recent.value = numel(find(violation_history(end-19:end)))/20; SessionPerformanceSection_timeout_recent.value = numel(find(timeout_history(end-19:end)))/20; @@ -797,8 +797,8 @@ callback(SessionPerformanceSection_ntrials); callback(SessionPerformanceSection_ntrials_stage); callback(SessionPerformanceSection_ntrials_stage_today) - callback(SessionPerformanceSection_violation_rate); - callback(SessionPerformanceSection_timeout_rate); + callback(SessionPerformanceSection_violation_percent); + callback(SessionPerformanceSection_timeout_percent); callback(SessionPerformanceSection_violation_recent); callback(SessionPerformanceSection_timeout_recent); callback(SessionPerformanceSection_violation_stage); @@ -964,8 +964,8 @@ % Updating Disp Values for SessionPerformanceSection SessionPerformanceSection_ntrials.value = n_done_trials; - SessionPerformanceSection_violation_rate.value = numel(find(violation_history))/n_done_trials; - SessionPerformanceSection_timeout_rate.value = numel(find(timeout_history))/n_done_trials; + SessionPerformanceSection_violation_percent.value = numel(find(violation_history))/n_done_trials; + SessionPerformanceSection_timeout_percent.value = numel(find(timeout_history))/n_done_trials; if n_done_trials >= 20 SessionPerformanceSection_violation_recent.value = numel(find(violation_history(end-19:end)))/20; SessionPerformanceSection_timeout_recent.value = numel(find(timeout_history(end-19:end)))/20; @@ -981,8 +981,8 @@ callback(SessionPerformanceSection_ntrials); callback(SessionPerformanceSection_ntrials_stage); callback(SessionPerformanceSection_ntrials_stage_today) - callback(SessionPerformanceSection_violation_rate); - callback(SessionPerformanceSection_timeout_rate); + callback(SessionPerformanceSection_violation_percent); + callback(SessionPerformanceSection_timeout_percent); callback(SessionPerformanceSection_violation_recent); callback(SessionPerformanceSection_timeout_recent); callback(SessionPerformanceSection_violation_stage); @@ -1151,8 +1151,8 @@ % Updating Disp Values for SessionPerformanceSection SessionPerformanceSection_ntrials.value = n_done_trials; - SessionPerformanceSection_violation_rate.value = numel(find(violation_history))/n_done_trials; - SessionPerformanceSection_timeout_rate.value = numel(find(timeout_history))/n_done_trials; + SessionPerformanceSection_violation_percent.value = numel(find(violation_history))/n_done_trials; + SessionPerformanceSection_timeout_percent.value = numel(find(timeout_history))/n_done_trials; if n_done_trials >= 20 SessionPerformanceSection_violation_recent.value = numel(find(violation_history(end-19:end)))/20; SessionPerformanceSection_timeout_recent.value = numel(find(timeout_history(end-19:end)))/20; @@ -1168,8 +1168,8 @@ callback(SessionPerformanceSection_ntrials); callback(SessionPerformanceSection_ntrials_stage); callback(SessionPerformanceSection_ntrials_stage_today) - callback(SessionPerformanceSection_violation_rate); - callback(SessionPerformanceSection_timeout_rate); + callback(SessionPerformanceSection_violation_percent); + callback(SessionPerformanceSection_timeout_percent); callback(SessionPerformanceSection_violation_recent); callback(SessionPerformanceSection_timeout_recent); callback(SessionPerformanceSection_violation_stage); @@ -1339,8 +1339,8 @@ % Updating Disp Values for SessionPerformanceSection SessionPerformanceSection_ntrials.value = n_done_trials; - SessionPerformanceSection_violation_rate.value = numel(find(violation_history))/n_done_trials; - SessionPerformanceSection_timeout_rate.value = numel(find(timeout_history))/n_done_trials; + SessionPerformanceSection_violation_percent.value = numel(find(violation_history))/n_done_trials; + SessionPerformanceSection_timeout_percent.value = numel(find(timeout_history))/n_done_trials; if n_done_trials >= 20 SessionPerformanceSection_violation_recent.value = numel(find(violation_history(end-19:end)))/20; SessionPerformanceSection_timeout_recent.value = numel(find(timeout_history(end-19:end)))/20; @@ -1356,8 +1356,8 @@ callback(SessionPerformanceSection_ntrials); callback(SessionPerformanceSection_ntrials_stage); callback(SessionPerformanceSection_ntrials_stage_today) - callback(SessionPerformanceSection_violation_rate); - callback(SessionPerformanceSection_timeout_rate); + callback(SessionPerformanceSection_violation_percent); + callback(SessionPerformanceSection_timeout_percent); callback(SessionPerformanceSection_violation_recent); callback(SessionPerformanceSection_timeout_recent); callback(SessionPerformanceSection_violation_stage); diff --git a/Protocols/@ArpitCentrePokeTraining/ParamsSection.m b/Protocols/@ArpitCentrePokeTraining/ParamsSection.m index 45abb262..29e83d55 100644 --- a/Protocols/@ArpitCentrePokeTraining/ParamsSection.m +++ b/Protocols/@ArpitCentrePokeTraining/ParamsSection.m @@ -446,6 +446,7 @@ case 'get_stage' x = value(training_stage); + y = value(CP_Duration); case 'get_previous_sides' x = value(previous_sides); %#ok diff --git a/Protocols/@ArpitCentrePokeTraining/SessionPerformanceSection.m b/Protocols/@ArpitCentrePokeTraining/SessionPerformanceSection.m index 717c4447..f938825b 100644 --- a/Protocols/@ArpitCentrePokeTraining/SessionPerformanceSection.m +++ b/Protocols/@ArpitCentrePokeTraining/SessionPerformanceSection.m @@ -32,7 +32,7 @@ % CDB, 23-March-2013 -function [x, y] = SessionPerformanceSection(obj, action, x,y) +function [x, y] = SessionPerformanceSection(obj, action, varargin) GetSoloFunctionArgs(obj); @@ -44,6 +44,8 @@ case 'init' + x = varargin{1}; y = varargin{2}; + SoloParamHandle(obj, 'my_gui_info', 'value', [x y double(gcf)], 'saveable', 0); DispParam(obj, 'ntrials', 0, x, y,'label','Session Trials', 'TooltipString', ... @@ -55,10 +57,10 @@ DispParam(obj, 'ntrials_stage_today', 0, x, y,'label','Stage Trials Today', 'TooltipString', ... 'trials completed in this training stage'); next_row(y); - DispParam(obj, 'violation_rate', 0, x, y,'label','Session Violation', 'TooltipString', ... + DispParam(obj, 'violation_percent', 0, x, y,'label','Session Violation', 'TooltipString', ... 'Fraction of trials with a center poke violation in this session'); next_row(y); - DispParam(obj, 'timeout_rate', 0, x, y,'label','Session Timeout', 'TooltipString', ... + DispParam(obj, 'timeout_percent', 0, x, y,'label','Session Timeout', 'TooltipString', ... 'Fraction of trials with timeout in this session'); next_row(y); DispParam(obj, 'violation_recent', 0, x, y,'label','Recent Violation', 'TooltipString', ... @@ -84,7 +86,8 @@ case 'evaluate' if nargout > 0 - x = [value(violation_rate), value(timeout_rate)]; + x = value(violation_percent); + y = value(timeout_percent); end % ------------------------------------------------------------------ From 67ecbc1037182d1f066eea49ab4f19d99cf19133 Mon Sep 17 00:00:00 2001 From: Arpit Date: Wed, 21 May 2025 10:19:19 +0100 Subject: [PATCH 113/164] same as previous --- .../@sqlsummary/CentrePoketrainingsummary.asv | 315 ------------------ .../@sqlsummary/CentrePoketrainingsummary.m | 311 +++++++++-------- .../ArpitCentrePokeTraining.m | 2 +- .../@ArpitCentrePokeTraining/ParamsSection.m | 2 +- 4 files changed, 177 insertions(+), 453 deletions(-) delete mode 100644 ExperPort/Plugins/@sqlsummary/CentrePoketrainingsummary.asv diff --git a/ExperPort/Plugins/@sqlsummary/CentrePoketrainingsummary.asv b/ExperPort/Plugins/@sqlsummary/CentrePoketrainingsummary.asv deleted file mode 100644 index ad199b5b..00000000 --- a/ExperPort/Plugins/@sqlsummary/CentrePoketrainingsummary.asv +++ /dev/null @@ -1,315 +0,0 @@ -function [err] = CentrePoketrainingsummary(obj, varargin) - %% Function Description - % Sends session data to the bdata.sessions table - % By default, it tries to get data from standard plugins - % See the pairs structure below for details - - %% Initialize Error Logging - diary('sendsummary_error_log.txt'); - - try - %% Define Default Parameters - pairs = { - 'force_send', 0;... - 'savetime', get_savetime(obj);... - 'endtime', get_endtime(obj);... - 'sessiondate', get_sessiondate(obj);... - 'hostname', get_rigid;... - 'IP_addr', get_network_info;... - 'experimenter', get_val('SavingSection_experimenter');... - 'ratname', get_val('SavingSection_ratname');... - 'n_done_trials', get_val('n_completed_trials');... - 'protocol', class(obj);... - 'protocol_data', 'NULL';... - 'peh', get_parsed_events;... - 'last_comment', cleanup(CommentsSection(obj,'get_latest'));... - 'data_file', SavingSection(obj,'get_data_file');... - 'technotes', get_val('TechComment')... % Added technician comments field - }; - parseargs(varargin, pairs); - - %% Validate Rig ID - [rigID, e, m] = bSettings('get','RIGS','Rig_ID'); - if ~force_send && isnan(rigID) - err = 42; - return - end - - %% Handle Tech Notes - if ~ischar(technotes) || isempty(technotes) - technotes = ''; - end - - %% Extract Path Information - [pth, fl] = extract_path(data_file); - - %% Calculate Violation Percentage - if isfield(protocol_data, 'violation_rate') - percent_violations = perf.violation_rate; - else - percent_violations = []; - end - - %% Calculate Poke Counts - left_pokes = 0; - center_pokes = 0; - right_pokes = 0; - for px = 1:numel(peh) - left_pokes = left_pokes + numel(peh(px).pokes.L); - center_pokes = center_pokes + numel(peh(px).pokes.C); - right_pokes = right_pokes + numel(peh(px).pokes.R); - end - - %% Get Session ID and Start Time - sessid = getSessID(obj); - starttime = get_starttime(sessid); % added 20091214 - - if isempty(starttime) - % Compute start time if not found in sess_started table - starttime = datestr(datenum(savetime)-sess_length(obj)/60/60/24, 13); - else - % Update sess_started table indicating session end - bdata('call set_sess_ended("{Si}", "{Si}")', sessid, 1); - end - - %% Define SQL columns and placeholders - colstr = [ - 'sessid',... - 'sessiondate'... DATE - 'starttime'... TIME - 'endtime'... TIME - 'ratname'... VARCHAR - 'experimenter'... VARCHAR - 'protocol'... VARCHAR - 'hostname'... VARCHAR - 'IP_address'... VARCHAR - 'training_stage_no'... INT - 'training_stage_name'... VARCHAR - 'n_done_trials'... INT - 'percent_violations'... VARCHAR - 'percent_timeout'... VARCHAR - 'stage1_trials_total'... INT - 'stage1_trials_today'... INT - 'stage1_trials_valid'... INT - 'stage1_percent_violation'... VARCHAR - 'stage1_percent_timeout'... VARCHAR - 'stage2_trials_total'... INT - 'stage2_trials_today'... INT - 'stage2_trials_valid'... INT - 'stage2_percent_violation'... VARCHAR - 'stage2_percent_timeout'... VARCHAR - 'stage3_trials_total'... INT - 'stage3_trials_today'... INT - 'stage3_trials_valid'... INT - 'stage3_percent_violation'... VARCHAR - 'stage3_percent_timeout'... VARCHAR - 'stage4_trials_total'... INT - 'stage4_trials_today'... INT - 'stage4_trials_valid'... INT - 'stage4_percent_violation'... VARCHAR - 'stage4_percent_timeout'... VARCHAR - 'stage5_trials_total'... INT - 'stage5_trials_today'... INT - 'stage5_trials_valid'... INT - 'stage5_percent_violation'... VARCHAR - 'stage5_percent_timeout'... VARCHAR - 'stage6_trials_total'... INT - 'stage6_trials_today'... INT - 'stage6_trials_valid'... INT - 'stage6_percent_violation'... VARCHAR - 'stage6_percent_timeout'... VARCHAR - 'stage7_trials_total'... INT - 'stage7_trials_today'... INT - 'stage7_trials_valid'... INT - 'stage7_percent_violation'... VARCHAR - 'stage7_percent_timeout'... VARCHAR - 'stage8_trials_total'... INT - 'stage8_trials_today'... INT - 'stage8_trials_valid'... INT - 'stage8_percent_violation'... VARCHAR - 'stage8_percent_timeout'... VARCHAR - 'datafile'... VARCHAR - 'datapath'... VARCHAR - 'videofile'... VARCHAR - 'videopath'... VARCHAR - 'centre_poke'... VARCHAR - 'left_poke'... VARCHAR - 'right_poke'... VARCHAR - 'comments'... VARCHAR - 'tech_notes']; - - - - valstr = [ - '"{Si}",', ... % sessid - '"{S}",', ... % ratname - '"{S}",', ... % hostname - '"{S}",', ... % experimenter - '"{S}",', ... % endtime - '"{S}",', ... % starttime - '"{S}",', ... % sessiondate - '"{S}",', ... % protocol - '"{S}",', ... % n_done_trials - '"{S}",', ... % total_correct - '"{S}",', ... % right_correct - '"{S}",', ... % left_correct - '"{S}",', ... % percent_violations - '"{S}",', ... % protocol_data - '"{S}",', ... % comments - '"{S}",', ... % data_file - '"{S}",', ... % data_path - '"{S}",', ... % left_pokes - '"{S}",', ... % center_pokes - '"{S}",', ... % right_pokes - '"{S}",', ... % technotes - '"{S}"' % IP_addr - ]; - - - - %% Construct SQL string - sqlstr = ['insert into sessions (' strtrim(colstr) ') values (' strtrim(valstr) ')']; - - - %% Execute SQL Query - bdata(sqlstr, ... - sessid, ... - ratname, ... - hostname, ... - experimenter, ... - endtime, ... - starttime, ... - sessiondate, ... - protocol, ... - n_done_trials, ... - total_correct, ... - right_correct, ... - left_correct, ... - percent_violations, ... - protocol_data, ... - last_comment, ... - fl, ... - pth, ... - left_pokes, ... - center_pokes, ... - right_pokes, ... - technotes, ... - IP_addr ... - ); - - % Log successful execution - fprintf('No errors encountered during sendsummary execution.\n'); - - - catch ME - fprintf(2, 'Failed to send summary to sql\n'); - disp(ME.message); - disp(ME.stack); - err = 1; - - % Log error details - fprintf('Error occurred during sendsummary execution:\n'); - fprintf('%s\n', ME.message); - fprintf('%s\n', ME.stack); - end - - diary off; -end - -%% Helper Functions -function y = get_val(x) - y = get_sphandle('fullname', x); - if isempty(y) - y = ''; - else - y = value(y{1}); - end -end - -function y = get_parsed_events - y = get_sphandle('fullname', 'ProtocolsSection_parsed_events'); - y = cell2mat(get_history(y{1})); -end - -function y = sess_length(obj) - % Estimate session length - GetSoloFunctionArgs(obj); - - try - st = parsed_events_history{1}.states; %#ok - ss = st.starting_state; - es = st.starting_state; - eval(['ST = min(min(st.', ss, '));']); - eval(['ET = max(max(st.', es, '));']); - - D1 = round(ET - ST); - - pt = get_sphandle('name', 'prot_title'); - [Ts, Te] = get_times_from_prottitle(value(pt{1})); - Ts = [Ts, ':00']; - Te = [Te, ':00']; - - Dt = timediff(Ts, Te, 2); - y = Dt + D1; - catch ME - showerror; % Assuming showerror is a function that displays errors - fprintf(2, 'Error calculating session length\n'); - disp(ME.message); - disp(ME.stack); - end -end - -function y = cleanup(M) - try - y = strtrim(sprintf('%s', M')); - catch - y = ''; - end -end - -function [p, f] = extract_path(s) - last_fs = find(s == filesep, 1, 'last' ); - p = s(1:last_fs); - f = s(last_fs+1:end); -end - -function y = get_savetime(obj) - [x, x, y] = SavingSection(obj, 'get_info'); - if y == '_' - y = datestr(now); - end -end - -function y = get_endtime(obj) - [x, x, savetime] = SavingSection(obj, 'get_info'); - if savetime == '_' - y = datestr(now, 13); - else - y = datestr(savetime, 13); - end -end - -function y = get_starttime(sessid) - y = bdata('select starttime from sess_started where sessid="{Si}"', sessid); - if ~isempty(y) - y = y{1}; - end -end - -function y = get_sessiondate(obj) - [x, x, savetime] = SavingSection(obj, 'get_info'); - if savetime == '_' - y = datestr(now, 29); - else - y = datestr(savetime, 29); - end -end - -function y = get_rigid - y = getRigID; - if isnan(y) - y = 'Unknown'; - elseif isnumeric(y) - y = sprintf('Rig%02d', y); - end -end diff --git a/ExperPort/Plugins/@sqlsummary/CentrePoketrainingsummary.m b/ExperPort/Plugins/@sqlsummary/CentrePoketrainingsummary.m index 21b7ebf0..88baaf00 100644 --- a/ExperPort/Plugins/@sqlsummary/CentrePoketrainingsummary.m +++ b/ExperPort/Plugins/@sqlsummary/CentrePoketrainingsummary.m @@ -44,12 +44,12 @@ [pth, fl] = extract_path(data_file); %% Calculate Violation Percentage - if isfield(protocol_data, 'violation_rate') - percent_violations = perf.violation_rate; - else - percent_violations = []; - end - + % if isfield(protocol_data, 'violation_rate') + % percent_violations = perf.violation_rate; + % else + % percent_violations = []; + % end + % %% Calculate Poke Counts left_pokes = 0; center_pokes = 0; @@ -74,140 +74,138 @@ %% Define SQL columns and placeholders colstr = [ - 'sessid',... - 'sessiondate'... DATE - 'starttime'... TIME - 'endtime'... TIME - 'ratname'... VARCHAR - 'experimenter'... VARCHAR - 'protocol'... VARCHAR - 'hostname'... VARCHAR - 'IP_address'... VARCHAR - 'training_stage_no'... INT - 'training_stage_name'... VARCHAR - 'n_done_trials'... INT - 'percent_violations'... VARCHAR - 'percent_timeout'... VARCHAR - 'stage1_trials_total'... INT - 'stage1_trials_today'... INT - 'stage1_trials_valid'... INT - 'stage1_percent_violation'... VARCHAR - 'stage1_percent_timeout'... VARCHAR - 'stage2_trials_total'... INT - 'stage2_trials_today'... INT - 'stage2_trials_valid'... INT - 'stage2_percent_violation'... VARCHAR - 'stage2_percent_timeout'... VARCHAR - 'stage3_trials_total'... INT - 'stage3_trials_today'... INT - 'stage3_trials_valid'... INT - 'stage3_percent_violation'... VARCHAR - 'stage3_percent_timeout'... VARCHAR - 'stage4_trials_total'... INT - 'stage4_trials_today'... INT - 'stage4_trials_valid'... INT - 'stage4_percent_violation'... VARCHAR - 'stage4_percent_timeout'... VARCHAR - 'stage5_trials_total'... INT - 'stage5_trials_today'... INT - 'stage5_trials_valid'... INT - 'stage5_percent_violation'... VARCHAR - 'stage5_percent_timeout'... VARCHAR - 'stage6_trials_total'... INT - 'stage6_trials_today'... INT - 'stage6_trials_valid'... INT - 'stage6_percent_violation'... VARCHAR - 'stage6_percent_timeout'... VARCHAR - 'stage7_trials_total'... INT - 'stage7_trials_today'... INT - 'stage7_trials_valid'... INT - 'stage7_percent_violation'... VARCHAR - 'stage7_percent_timeout'... VARCHAR - 'stage8_trials_total'... INT - 'stage8_trials_today'... INT - 'stage8_trials_valid'... INT - 'stage8_percent_violation'... VARCHAR - 'stage8_percent_timeout'... VARCHAR - 'datafile'... VARCHAR - 'datapath'... VARCHAR - 'videofile'... VARCHAR - 'videopath'... VARCHAR - 'centre_poke'... VARCHAR - 'left_poke'... VARCHAR - 'right_poke'... VARCHAR - 'comments'... VARCHAR + 'sessid, ',... + 'sessiondate, '... DATE + 'starttime, '... TIME + 'endtime, '... TIME + 'ratname, '... VARCHAR + 'experimenter, '... VARCHAR + 'protocol, '... VARCHAR + 'hostname, '... VARCHAR + 'IP_address, '... VARCHAR + 'training_stage_no, '... INT + 'training_stage_name, '... VARCHAR + 'n_done_trials, '... INT + 'percent_violations, '... VARCHAR + 'percent_timeout, '... VARCHAR + 'stage1_trials_total, '... INT + 'stage1_trials_today, '... INT + 'stage1_trials_valid, '... INT + 'stage1_percent_violation, '... VARCHAR + 'stage1_percent_timeout, '... VARCHAR + 'stage2_trials_total, '... INT + 'stage2_trials_today, '... INT + 'stage2_trials_valid, '... INT + 'stage2_percent_violation, '... VARCHAR + 'stage2_percent_timeout, '... VARCHAR + 'stage3_trials_total, '... INT + 'stage3_trials_today, '... INT + 'stage3_trials_valid, '... INT + 'stage3_percent_violation, '... VARCHAR + 'stage3_percent_timeout, '... VARCHAR + 'stage4_trials_total, '... INT + 'stage4_trials_today, '... INT + 'stage4_trials_valid, '... INT + 'stage4_percent_violation, '... VARCHAR + 'stage4_percent_timeout, '... VARCHAR + 'stage5_trials_total, '... INT + 'stage5_trials_today, '... INT + 'stage5_trials_valid, '... INT + 'stage5_percent_violation, '... VARCHAR + 'stage5_percent_timeout, '... VARCHAR + 'stage6_trials_total, '... INT + 'stage6_trials_today, '... INT + 'stage6_trials_valid, '... INT + 'stage6_percent_violation, '... VARCHAR + 'stage6_percent_timeout, '... VARCHAR + 'stage7_trials_total, '... INT + 'stage7_trials_today, '... INT + 'stage7_trials_valid, '... INT + 'stage7_percent_violation, '... VARCHAR + 'stage7_percent_timeout, '... VARCHAR + 'stage8_trials_total, '... INT + 'stage8_trials_today, '... INT + 'stage8_trials_valid, '... INT + 'stage8_percent_violation, '... VARCHAR + 'stage8_percent_timeout, '... VARCHAR + 'datafile, '... VARCHAR + 'datapath, '... VARCHAR + 'videofile, '... VARCHAR + 'videopath, '... VARCHAR + 'centre_poke, '... VARCHAR + 'left_poke, '... VARCHAR + 'right_poke, '... VARCHAR + 'comments, '... VARCHAR 'tech_notes']; % total 63 columns - - valstr = [ +valstr = [ '"{Si}",', ... % sessid - '"{S}",', ... % ratname - '"{S}",', ... % hostname - '"{S}",', ... % experimenter - '"{S}",', ... % endtime - '"{S}",', ... % starttime '"{S}",', ... % sessiondate - '"{S}",', ... % protocol - '"{S}",', ... % n_done_trials - '"{S}",', ... % total_correct - '"{S}",', ... % right_correct - '"{S}",', ... % left_correct - '"{S}",', ... % percent_violations - '"{S}",', ... % protocol_data - '"{S}",', ... % comments - '"{S}",', ... % data_file - '"{S}",', ... % data_path - '"{S}",', ... % left_pokes - '"{S}",', ... % center_pokes - '"{S}",', ... % right_pokes - '"{S}",', ... % technotes - '"{S}"' % IP_addr - '"{S}",', ... % ratname - '"{S}",', ... % hostname - '"{S}",', ... % experimenter - '"{S}",', ... % endtime '"{S}",', ... % starttime - '"{S}",', ... % sessiondate - '"{S}",', ... % protocol - '"{S}",', ... % n_done_trials - '"{S}",', ... % total_correct - '"{S}",', ... % right_correct - '"{S}",', ... % left_correct - '"{S}",', ... % percent_violations - '"{S}",', ... % protocol_data - '"{S}",', ... % comments - '"{S}",', ... % data_file - '"{S}",', ... % data_path - '"{S}",', ... % left_pokes - '"{S}",', ... % center_pokes - '"{S}",', ... % right_pokes - '"{S}",', ... % technotes - '"{S}"' % IP_addr + '"{S}",', ... % endtime '"{S}",', ... % ratname - '"{S}",', ... % hostname '"{S}",', ... % experimenter - '"{S}",', ... % endtime - '"{S}",', ... % starttime - '"{S}",', ... % sessiondate '"{S}",', ... % protocol + '"{S}",', ... % hostname + '"{S}",', ... % IP_address + '"{S}",', ... % training_stage_no + '"{S}",', ... % training_stage_name '"{S}",', ... % n_done_trials - '"{S}",', ... % total_correct - '"{S}",', ... % right_correct - '"{S}",', ... % left_correct '"{S}",', ... % percent_violations - '"{S}",', ... % protocol_data - '"{S}",', ... % comments - '"{S}",', ... % data_file - '"{S}",', ... % data_path + '"{S}",', ... % percent_timeout + '"{S}",', ... % stage1_trials_total + '"{S}",', ... % stage1_trials_today + '"{S}",', ... % stage1_trials_valid + '"{S}",', ... % stage1_percent_violation + '"{S}",', ... % stage1_percent_timeout + '"{S}",', ... % stage2_trials_total + '"{S}",', ... % stage2_trials_today + '"{S}",', ... % stage2_trials_valid + '"{S}",', ... % stage2_percent_violation + '"{S}",', ... % stage2_percent_timeout + '"{S}",', ... % stage3_trials_total + '"{S}",', ... % stage3_trials_today + '"{S}",', ... % stage3_trials_valid + '"{S}",', ... % stage3_percent_violation + '"{S}",', ... % stage3_percent_timeout + '"{S}",', ... % stage4_trials_total + '"{S}",', ... % stage4_trials_today + '"{S}",', ... % stage4_trials_valid + '"{S}",', ... % stage4_percent_violation + '"{S}",', ... % stage4_percent_timeout + '"{S}",', ... % stage5_trials_total + '"{S}",', ... % stage5_trials_today + '"{S}",', ... % stage5_trials_valid + '"{S}",', ... % stage5_percent_violation + '"{S}",', ... % stage5_percent_timeout + '"{S}",', ... % stage6_trials_total + '"{S}",', ... % stage6_trials_today + '"{S}",', ... % stage6_trials_valid + '"{S}",', ... % stage6_percent_violation + '"{S}",', ... % stage6_percent_timeout + '"{S}",', ... % stage7_trials_total + '"{S}",', ... % stage7_trials_today + '"{S}",', ... % stage7_trials_valid + '"{S}",', ... % stage7_percent_violation + '"{S}",', ... % stage7_percent_timeout + '"{S}",', ... % stage8_trials_total + '"{S}",', ... % stage8_trials_today + '"{S}",', ... % stage8_trials_valid + '"{S}",', ... % stage8_percent_violation + '"{S}",', ... % stage8_percent_timeout + '"{S}",', ... % datafile + '"{S}",', ... % datapath + '"{S}",', ... % video_path + '"{S}",', ... % videofile '"{S}",', ... % left_pokes '"{S}",', ... % center_pokes '"{S}",', ... % right_pokes + '"{S}",', ... % comments '"{S}",', ... % technotes ]; - %% Construct SQL string sqlstr = ['insert into CentrePokeTraining (' strtrim(colstr) ') values (' strtrim(valstr) ')']; @@ -215,28 +213,69 @@ %% Execute SQL Query bdata(sqlstr, ... sessid, ... + sessiondate, ... + starttime, ... + endtime, ... ratname, ... - hostname, ... experimenter, ... - endtime, ... - starttime, ... - sessiondate, ... protocol, ... + hostname, ... + IP_addr, ... + protocol_data.stage_no, ... + protocol_data.stage_name, ... n_done_trials, ... - total_correct, ... - right_correct, ... - left_correct, ... - percent_violations, ... - protocol_data, ... - last_comment, ... - fl, ... + protocol_data.violation_percent, ... + protocol_data.timeout_percent, ... + protocol_data.stage1_trials_total, ... + protocol_data.stage1_trials_today, ... + protocol_data.stage1_trials_valid, ... + protocol_data.stage1_violationrate, ... + protocol_data.stage1_timeoutrate, ... + protocol_data.stage2_trials_total, ... + protocol_data.stage2_trials_today, ... + protocol_data.stage2_trials_valid, ... + protocol_data.stage2_violationrate, ... + protocol_data.stage2_timeoutrate, ... + protocol_data.stage3_trials_total, ... + protocol_data.stage3_trials_today, ... + protocol_data.stage3_trials_valid, ... + protocol_data.stage3_violationrate, ... + protocol_data.stage3_timeoutrate, ... + protocol_data.stage4_trials_total, ... + protocol_data.stage4_trials_today, ... + protocol_data.stage4_trials_valid, ... + protocol_data.stage4_violationrate, ... + protocol_data.stage4_timeoutrate, ... + protocol_data.stage5_trials_total, ... + protocol_data.stage5_trials_today, ... + protocol_data.stage5_trials_valid, ... + protocol_data.stage5_violationrate, ... + protocol_data.stage5_timeoutrate, ... + protocol_data.stage6_trials_total, ... + protocol_data.stage6_trials_today, ... + protocol_data.stage6_trials_valid, ... + protocol_data.stage6_violationrate, ... + protocol_data.stage6_timeoutrate, ... + protocol_data.stage7_trials_total, ... + protocol_data.stage7_trials_today, ... + protocol_data.stage7_trials_valid, ... + protocol_data.stage7_violationrate, ... + protocol_data.stage7_timeoutrate, ... + protocol_data.stage8_trials_total, ... + protocol_data.stage8_trials_today, ... + protocol_data.stage8_trials_valid, ... + protocol_data.stage8_violationrate, ... + protocol_data.stage8_timeoutrate, ... pth, ... + fl, ... + perf.video_filepath, ... + perf.CP_Duration, .... left_pokes, ... center_pokes, ... right_pokes, ... - technotes, ... - IP_addr ... - ); + last_comment, ... + technotes... + ); % Log successful execution fprintf('No errors encountered during sendsummary execution.\n'); diff --git a/Protocols/@ArpitCentrePokeTraining/ArpitCentrePokeTraining.m b/Protocols/@ArpitCentrePokeTraining/ArpitCentrePokeTraining.m index ec8cc79b..66e16188 100644 --- a/Protocols/@ArpitCentrePokeTraining/ArpitCentrePokeTraining.m +++ b/Protocols/@ArpitCentrePokeTraining/ArpitCentrePokeTraining.m @@ -327,7 +327,7 @@ % sendsummary(obj); perf = struct([]); perf = PerformanceSummarySection(obj, 'evaluate'); - [perf.violation_rate,perf.timeout_rate] = SessionPerformanceSection(obj, 'evaluate'); + [perf.violation_percent,perf.timeout_percent] = SessionPerformanceSection(obj, 'evaluate'); [perf.stage_no,perf.CP_Duration] = ParamsSection(obj,'get_stage'); stage_name_list = {'Familiarize with Reward Side Pokes','Timeout Rewarded Side Pokes'... diff --git a/Protocols/@ArpitCentrePokeTraining/ParamsSection.m b/Protocols/@ArpitCentrePokeTraining/ParamsSection.m index 29e83d55..9281371e 100644 --- a/Protocols/@ArpitCentrePokeTraining/ParamsSection.m +++ b/Protocols/@ArpitCentrePokeTraining/ParamsSection.m @@ -446,7 +446,7 @@ case 'get_stage' x = value(training_stage); - y = value(CP_Duration); + y = value(CP_duration); case 'get_previous_sides' x = value(previous_sides); %#ok From c4e5d6f6726ac4c627f8f2058d5cf568eba8d0d7 Mon Sep 17 00:00:00 2001 From: Arpit Date: Wed, 21 May 2025 18:16:02 +0100 Subject: [PATCH 114/164] updated automated training file --- .../@sqlsummary/CentrePoketrainingsummary.m | 12 +- ...ing_SessionDefinition_AutoTrainingStages.m | 761 +++++---- ...te_ArpitCentrePokeTraining_SettingFile.asv | 92 ++ ...eate_ArpitCentrePokeTraining_SettingFile.m | 4 +- ...rpitCentrePokeTraining_arpit_AR05_250516.m | 1379 ----------------- 5 files changed, 461 insertions(+), 1787 deletions(-) create mode 100644 Protocols/@ArpitCentrePokeTraining/private/create_ArpitCentrePokeTraining_SettingFile.asv delete mode 100644 Protocols/@ArpitCentrePokeTraining/private/pipeline_ArpitCentrePokeTraining_arpit_AR05_250516.m diff --git a/ExperPort/Plugins/@sqlsummary/CentrePoketrainingsummary.m b/ExperPort/Plugins/@sqlsummary/CentrePoketrainingsummary.m index 88baaf00..e7e330f1 100644 --- a/ExperPort/Plugins/@sqlsummary/CentrePoketrainingsummary.m +++ b/ExperPort/Plugins/@sqlsummary/CentrePoketrainingsummary.m @@ -130,8 +130,8 @@ 'stage8_percent_timeout, '... VARCHAR 'datafile, '... VARCHAR 'datapath, '... VARCHAR - 'videofile, '... VARCHAR - 'videopath, '... VARCHAR + 'videopath, '... VARCHAR + 'CP_Dur_reached, '... VARCHAR 'centre_poke, '... VARCHAR 'left_poke, '... VARCHAR 'right_poke, '... VARCHAR @@ -202,7 +202,7 @@ '"{S}",', ... % center_pokes '"{S}",', ... % right_pokes '"{S}",', ... % comments - '"{S}",', ... % technotes + '"{S}"', ... % technotes ]; @@ -268,13 +268,13 @@ protocol_data.stage8_timeoutrate, ... pth, ... fl, ... - perf.video_filepath, ... - perf.CP_Duration, .... + protocol_data.video_filepath, ... + protocol_data.CP_Duration, .... left_pokes, ... center_pokes, ... right_pokes, ... last_comment, ... - technotes... + technotes ... ); % Log successful execution diff --git a/Protocols/@ArpitCentrePokeTraining/ArpitCentrePokeTraining_SessionDefinition_AutoTrainingStages.m b/Protocols/@ArpitCentrePokeTraining/ArpitCentrePokeTraining_SessionDefinition_AutoTrainingStages.m index 1cd66448..7857d07d 100644 --- a/Protocols/@ArpitCentrePokeTraining/ArpitCentrePokeTraining_SessionDefinition_AutoTrainingStages.m +++ b/Protocols/@ArpitCentrePokeTraining/ArpitCentrePokeTraining_SessionDefinition_AutoTrainingStages.m @@ -56,61 +56,61 @@ end % Updating Disp Values for Training_Peformance_Summary +% Performance section updates if n_done_trials > 0 - if n_done_trials == 1 - PerformanceSummarySection_stage_1_TrialsToday.value = 0; - callback(PerformanceSummarySection_stage_1_TrialsToday); - PerformanceSummarySection_stage_2_TrialsToday.value = 0; - callback(PerformanceSummarySection_stage_2_TrialsToday); - PerformanceSummarySection_stage_3_TrialsToday.value = 0; - callback(PerformanceSummarySection_stage_3_TrialsToday); - PerformanceSummarySection_stage_4_TrialsToday.value = 0; - callback(PerformanceSummarySection_stage_4_TrialsToday); - PerformanceSummarySection_stage_5_TrialsToday.value = 0; - callback(PerformanceSummarySection_stage_5_TrialsToday); - PerformanceSummarySection_stage_6_TrialsToday.value = 0; - callback(PerformanceSummarySection_stage_6_TrialsToday); - PerformanceSummarySection_stage_7_TrialsToday.value = 0; - callback(PerformanceSummarySection_stage_7_TrialsToday); - PerformanceSummarySection_stage_8_TrialsToday.value = 0; - callback(PerformanceSummarySection_stage_8_TrialsToday); + for k = 1:8 + eval(sprintf('PerformanceSummarySection_stage_%d_TrialsToday.value = 0;', k)); + eval(sprintf('callback(PerformanceSummarySection_stage_%d_TrialsToday);', k)); + end end PerformanceSummarySection_stage_1_Trials.value = value(PerformanceSummarySection_stage_1_Trials) + 1; PerformanceSummarySection_stage_1_TrialsToday.value = value(PerformanceSummarySection_stage_1_TrialsToday) + 1; - PerformanceSummarySection_stage_1_ViolationRate.value = nan; - PerformanceSummarySection_stage_1_TimeoutRate.value = nan; + PerformanceSummarySection_stage_1_ViolationRate.value = ... + ((value(PerformanceSummarySection_stage_1_ViolationRate) * (value(PerformanceSummarySection_stage_1_Trials) - 1)) + double(violation_history(end))) ... + / value(PerformanceSummarySection_stage_1_Trials); + PerformanceSummarySection_stage_1_TimeoutRate.value = ... + ((value(PerformanceSummarySection_stage_1_TimeoutRate) * (value(PerformanceSummarySection_stage_1_Trials) - 1)) + double(timeout_history(end))) ... + / value(PerformanceSummarySection_stage_1_Trials); + if ~isnan(hit_history(end)) PerformanceSummarySection_stage_1_TrialsValid.value = value(PerformanceSummarySection_stage_1_TrialsValid) + 1; end - callback(PerformanceSummarySection_stage_1_Trials); - callback(PerformanceSummarySection_stage_1_TrialsToday) - callback(PerformanceSummarySection_stage_1_ViolationRate); - callback(PerformanceSummarySection_stage_1_TimeoutRate); - callback(PerformanceSummarySection_stage_1_TrialsValid); - - % Updating Disp Values for Training_Peformance_Summary - SessionPerformanceSection_ntrials.value = n_done_trials; - SessionPerformanceSection_violation_percent.value = nan; - SessionPerformanceSection_timeout_percent.value = nan; - SessionPerformanceSection_violation_recent.value = nan; - SessionPerformanceSection_timeout_recent.value = nan; - SessionPerformanceSection_ntrials_stage.value = value(PerformanceSummarySection_stage_1_Trials); - SessionPerformanceSection_ntrials_stage_today.value = value(PerformanceSummarySection_stage_1_TrialsToday); - SessionPerformanceSection_timeout_stage.value = nan; - SessionPerformanceSection_violation_stage.value = nan; - - callback(SessionPerformanceSection_ntrials); - callback(SessionPerformanceSection_ntrials_stage); - callback(SessionPerformanceSection_ntrials_stage_today) - callback(SessionPerformanceSection_violation_percent); - callback(SessionPerformanceSection_timeout_percent); - callback(SessionPerformanceSection_violation_recent); - callback(SessionPerformanceSection_timeout_recent); - callback(SessionPerformanceSection_violation_stage); - callback(SessionPerformanceSection_timeout_stage); + callback(PerformanceSummarySection_stage_1_Trials); + callback(PerformanceSummarySection_stage_1_TrialsToday); + callback(PerformanceSummarySection_stage_1_ViolationRate); + callback(PerformanceSummarySection_stage_1_TimeoutRate); + callback(PerformanceSummarySection_stage_1_TrialsValid); + + % Session-wide stats + SessionPerformanceSection_ntrials.value = n_done_trials; + SessionPerformanceSection_violation_percent.value = numel(find(violation_history)) / n_done_trials; + SessionPerformanceSection_timeout_percent.value = numel(find(timeout_history)) / n_done_trials; + + if n_done_trials >= 20 + SessionPerformanceSection_violation_recent.value = numel(find(violation_history(end-19:end))) / 20; + SessionPerformanceSection_timeout_recent.value = numel(find(timeout_history(end-19:end))) / 20; + else + SessionPerformanceSection_violation_recent.value = nan; + SessionPerformanceSection_timeout_recent.value = nan; + end + + SessionPerformanceSection_violation_stage.value = value(PerformanceSummarySection_stage_1_ViolationRate); + SessionPerformanceSection_ntrials_stage.value = value(PerformanceSummarySection_stage_1_Trials); + SessionPerformanceSection_ntrials_stage_today.value = value(PerformanceSummarySection_stage_1_TrialsToday); + SessionPerformanceSection_timeout_stage.value = value(PerformanceSummarySection_stage_1_TimeoutRate); + + callback(SessionPerformanceSection_ntrials); + callback(SessionPerformanceSection_ntrials_stage); + callback(SessionPerformanceSection_ntrials_stage_today); + callback(SessionPerformanceSection_violation_percent); + callback(SessionPerformanceSection_timeout_percent); + callback(SessionPerformanceSection_violation_recent); + callback(SessionPerformanceSection_timeout_recent); + callback(SessionPerformanceSection_violation_stage); + callback(SessionPerformanceSection_timeout_stage); end % end @@ -225,65 +225,61 @@ % Updating Disp Values for Training_Peformance_Summary +% Performance section updates if n_done_trials > 0 - if n_done_trials == 1 - PerformanceSummarySection_stage_1_TrialsToday.value = 0; - callback(PerformanceSummarySection_stage_1_TrialsToday); - PerformanceSummarySection_stage_2_TrialsToday.value = 0; - callback(PerformanceSummarySection_stage_2_TrialsToday); - PerformanceSummarySection_stage_3_TrialsToday.value = 0; - callback(PerformanceSummarySection_stage_3_TrialsToday); - PerformanceSummarySection_stage_4_TrialsToday.value = 0; - callback(PerformanceSummarySection_stage_4_TrialsToday); - PerformanceSummarySection_stage_5_TrialsToday.value = 0; - callback(PerformanceSummarySection_stage_5_TrialsToday); - PerformanceSummarySection_stage_6_TrialsToday.value = 0; - callback(PerformanceSummarySection_stage_6_TrialsToday); - PerformanceSummarySection_stage_7_TrialsToday.value = 0; - callback(PerformanceSummarySection_stage_7_TrialsToday); - PerformanceSummarySection_stage_8_TrialsToday.value = 0; - callback(PerformanceSummarySection_stage_8_TrialsToday); + for k = 1:8 + eval(sprintf('PerformanceSummarySection_stage_%d_TrialsToday.value = 0;', k)); + eval(sprintf('callback(PerformanceSummarySection_stage_%d_TrialsToday);', k)); + end end PerformanceSummarySection_stage_2_Trials.value = value(PerformanceSummarySection_stage_2_Trials) + 1; PerformanceSummarySection_stage_2_TrialsToday.value = value(PerformanceSummarySection_stage_2_TrialsToday) + 1; - PerformanceSummarySection_stage_2_ViolationRate.value = nan; - PerformanceSummarySection_stage_2_TimeoutRate.value = ((value(PerformanceSummarySection_stage_2_TimeoutRate) * (value(PerformanceSummarySection_stage_2_Trials) - 1)) + double(timeout_history(end))) / value(PerformanceSummarySection_stage_2_Trials); + PerformanceSummarySection_stage_2_ViolationRate.value = ... + ((value(PerformanceSummarySection_stage_2_ViolationRate) * (value(PerformanceSummarySection_stage_2_Trials) - 1)) + double(violation_history(end))) ... + / value(PerformanceSummarySection_stage_2_Trials); + PerformanceSummarySection_stage_2_TimeoutRate.value = ... + ((value(PerformanceSummarySection_stage_2_TimeoutRate) * (value(PerformanceSummarySection_stage_2_Trials) - 1)) + double(timeout_history(end))) ... + / value(PerformanceSummarySection_stage_2_Trials); + if ~isnan(hit_history(end)) PerformanceSummarySection_stage_2_TrialsValid.value = value(PerformanceSummarySection_stage_2_TrialsValid) + 1; end - - callback(PerformanceSummarySection_stage_2_Trials); - callback(PerformanceSummarySection_stage_2_TrialsToday) - callback(PerformanceSummarySection_stage_2_ViolationRate); - callback(PerformanceSummarySection_stage_2_TimeoutRate); - callback(PerformanceSummarySection_stage_2_TrialsValid); - % Updating Disp Values for Training_Peformance_Summary - SessionPerformanceSection_ntrials.value = n_done_trials; - SessionPerformanceSection_violation_percent.value = nan; - SessionPerformanceSection_timeout_percent.value = numel(find(timeout_history))/n_done_trials; - SessionPerformanceSection_violation_recent.value = nan; - if n_done_trials >= 20 - SessionPerformanceSection_timeout_recent.value = numel(find(timeout_history(end-19:end)))/20; - else - SessionPerformanceSection_timeout_recent.value = nan; - end - SessionPerformanceSection_violation_stage.value = nan; - SessionPerformanceSection_ntrials_stage.value = value(PerformanceSummarySection_stage_2_Trials); - SessionPerformanceSection_ntrials_stage_today.value = value(PerformanceSummarySection_stage_2_TrialsToday); - SessionPerformanceSection_timeout_stage.value = value(PerformanceSummarySection_stage_2_TimeoutRate); - - callback(SessionPerformanceSection_ntrials); - callback(SessionPerformanceSection_ntrials_stage); - callback(SessionPerformanceSection_ntrials_stage_today) - callback(SessionPerformanceSection_violation_percent); - callback(SessionPerformanceSection_timeout_percent); - callback(SessionPerformanceSection_violation_recent); - callback(SessionPerformanceSection_timeout_recent); - callback(SessionPerformanceSection_violation_stage); - callback(SessionPerformanceSection_timeout_stage); + callback(PerformanceSummarySection_stage_2_Trials); + callback(PerformanceSummarySection_stage_2_TrialsToday); + callback(PerformanceSummarySection_stage_2_ViolationRate); + callback(PerformanceSummarySection_stage_2_TimeoutRate); + callback(PerformanceSummarySection_stage_2_TrialsValid); + + % Session-wide stats + SessionPerformanceSection_ntrials.value = n_done_trials; + SessionPerformanceSection_violation_percent.value = numel(find(violation_history)) / n_done_trials; + SessionPerformanceSection_timeout_percent.value = numel(find(timeout_history)) / n_done_trials; + + if n_done_trials >= 20 + SessionPerformanceSection_violation_recent.value = numel(find(violation_history(end-19:end))) / 20; + SessionPerformanceSection_timeout_recent.value = numel(find(timeout_history(end-19:end))) / 20; + else + SessionPerformanceSection_violation_recent.value = nan; + SessionPerformanceSection_timeout_recent.value = nan; + end + + SessionPerformanceSection_violation_stage.value = value(PerformanceSummarySection_stage_2_ViolationRate); + SessionPerformanceSection_ntrials_stage.value = value(PerformanceSummarySection_stage_2_Trials); + SessionPerformanceSection_ntrials_stage_today.value = value(PerformanceSummarySection_stage_2_TrialsToday); + SessionPerformanceSection_timeout_stage.value = value(PerformanceSummarySection_stage_2_TimeoutRate); + + callback(SessionPerformanceSection_ntrials); + callback(SessionPerformanceSection_ntrials_stage); + callback(SessionPerformanceSection_ntrials_stage_today); + callback(SessionPerformanceSection_violation_percent); + callback(SessionPerformanceSection_timeout_percent); + callback(SessionPerformanceSection_violation_recent); + callback(SessionPerformanceSection_timeout_recent); + callback(SessionPerformanceSection_violation_stage); + callback(SessionPerformanceSection_timeout_stage); end % @@ -379,66 +375,61 @@ end callback(ParamsSection_CP_duration); +% Performance section updates if n_done_trials > 0 - if n_done_trials == 1 - PerformanceSummarySection_stage_1_TrialsToday.value = 0; - callback(PerformanceSummarySection_stage_1_TrialsToday); - PerformanceSummarySection_stage_2_TrialsToday.value = 0; - callback(PerformanceSummarySection_stage_2_TrialsToday); - PerformanceSummarySection_stage_3_TrialsToday.value = 0; - callback(PerformanceSummarySection_stage_3_TrialsToday); - PerformanceSummarySection_stage_4_TrialsToday.value = 0; - callback(PerformanceSummarySection_stage_4_TrialsToday); - PerformanceSummarySection_stage_5_TrialsToday.value = 0; - callback(PerformanceSummarySection_stage_5_TrialsToday); - PerformanceSummarySection_stage_6_TrialsToday.value = 0; - callback(PerformanceSummarySection_stage_6_TrialsToday); - PerformanceSummarySection_stage_7_TrialsToday.value = 0; - callback(PerformanceSummarySection_stage_7_TrialsToday); - PerformanceSummarySection_stage_8_TrialsToday.value = 0; - callback(PerformanceSummarySection_stage_8_TrialsToday); + for k = 1:8 + eval(sprintf('PerformanceSummarySection_stage_%d_TrialsToday.value = 0;', k)); + eval(sprintf('callback(PerformanceSummarySection_stage_%d_TrialsToday);', k)); + end end - % Updating Disp Values for Training_Peformance_Summary PerformanceSummarySection_stage_3_Trials.value = value(PerformanceSummarySection_stage_3_Trials) + 1; PerformanceSummarySection_stage_3_TrialsToday.value = value(PerformanceSummarySection_stage_3_TrialsToday) + 1; - PerformanceSummarySection_stage_3_ViolationRate.value = nan; - PerformanceSummarySection_stage_3_TimeoutRate.value = ((value(PerformanceSummarySection_stage_3_TimeoutRate) * (value(PerformanceSummarySection_stage_3_Trials) - 1)) + double(timeout_history(end))) / value(PerformanceSummarySection_stage_3_Trials); + PerformanceSummarySection_stage_3_ViolationRate.value = ... + ((value(PerformanceSummarySection_stage_3_ViolationRate) * (value(PerformanceSummarySection_stage_3_Trials) - 1)) + double(violation_history(end))) ... + / value(PerformanceSummarySection_stage_3_Trials); + PerformanceSummarySection_stage_3_TimeoutRate.value = ... + ((value(PerformanceSummarySection_stage_3_TimeoutRate) * (value(PerformanceSummarySection_stage_3_Trials) - 1)) + double(timeout_history(end))) ... + / value(PerformanceSummarySection_stage_3_Trials); + if ~isnan(hit_history(end)) PerformanceSummarySection_stage_3_TrialsValid.value = value(PerformanceSummarySection_stage_3_TrialsValid) + 1; end callback(PerformanceSummarySection_stage_3_Trials); - callback(PerformanceSummarySection_stage_3_TrialsToday) + callback(PerformanceSummarySection_stage_3_TrialsToday); callback(PerformanceSummarySection_stage_3_ViolationRate); callback(PerformanceSummarySection_stage_3_TimeoutRate); callback(PerformanceSummarySection_stage_3_TrialsValid); - - % Updating Disp Values for Training_Peformance_Summary - SessionPerformanceSection_ntrials.value = n_done_trials; - SessionPerformanceSection_violation_percent.value = nan; - SessionPerformanceSection_timeout_percent.value = numel(find(timeout_history))/n_done_trials; - SessionPerformanceSection_violation_recent.value = nan; - if n_done_trials >= 20 - SessionPerformanceSection_timeout_recent.value = numel(find(timeout_history(end-19:end)))/20; - else - SessionPerformanceSection_timeout_recent.value = nan; - end - SessionPerformanceSection_violation_stage.value = nan; - SessionPerformanceSection_ntrials_stage.value = value(PerformanceSummarySection_stage_3_Trials); - SessionPerformanceSection_ntrials_stage_today.value = value(PerformanceSummarySection_stage_3_TrialsToday); - SessionPerformanceSection_timeout_stage.value = value(PerformanceSummarySection_stage_3_TimeoutRate); - callback(SessionPerformanceSection_ntrials); - callback(SessionPerformanceSection_ntrials_stage); - callback(SessionPerformanceSection_ntrials_stage_today) - callback(SessionPerformanceSection_violation_percent); - callback(SessionPerformanceSection_timeout_percent); - callback(SessionPerformanceSection_violation_recent); - callback(SessionPerformanceSection_timeout_recent); - callback(SessionPerformanceSection_violation_stage); - callback(SessionPerformanceSection_timeout_stage); + % Session-wide stats + SessionPerformanceSection_ntrials.value = n_done_trials; + SessionPerformanceSection_violation_percent.value = numel(find(violation_history)) / n_done_trials; + SessionPerformanceSection_timeout_percent.value = numel(find(timeout_history)) / n_done_trials; + + if n_done_trials >= 20 + SessionPerformanceSection_violation_recent.value = numel(find(violation_history(end-19:end))) / 20; + SessionPerformanceSection_timeout_recent.value = numel(find(timeout_history(end-19:end))) / 20; + else + SessionPerformanceSection_violation_recent.value = nan; + SessionPerformanceSection_timeout_recent.value = nan; + end + + SessionPerformanceSection_violation_stage.value = value(PerformanceSummarySection_stage_3_ViolationRate); + SessionPerformanceSection_ntrials_stage.value = value(PerformanceSummarySection_stage_3_Trials); + SessionPerformanceSection_ntrials_stage_today.value = value(PerformanceSummarySection_stage_3_TrialsToday); + SessionPerformanceSection_timeout_stage.value = value(PerformanceSummarySection_stage_3_TimeoutRate); + + callback(SessionPerformanceSection_ntrials); + callback(SessionPerformanceSection_ntrials_stage); + callback(SessionPerformanceSection_ntrials_stage_today); + callback(SessionPerformanceSection_violation_percent); + callback(SessionPerformanceSection_timeout_percent); + callback(SessionPerformanceSection_violation_recent); + callback(SessionPerformanceSection_timeout_recent); + callback(SessionPerformanceSection_violation_stage); + callback(SessionPerformanceSection_timeout_stage); end % @@ -540,68 +531,63 @@ callback(ParamsSection_CP_duration); +% Performance section updates if n_done_trials > 0 - if n_done_trials == 1 - PerformanceSummarySection_stage_1_TrialsToday.value = 0; - callback(PerformanceSummarySection_stage_1_TrialsToday); - PerformanceSummarySection_stage_2_TrialsToday.value = 0; - callback(PerformanceSummarySection_stage_2_TrialsToday); - PerformanceSummarySection_stage_3_TrialsToday.value = 0; - callback(PerformanceSummarySection_stage_3_TrialsToday); - PerformanceSummarySection_stage_4_TrialsToday.value = 0; - callback(PerformanceSummarySection_stage_4_TrialsToday); - PerformanceSummarySection_stage_5_TrialsToday.value = 0; - callback(PerformanceSummarySection_stage_5_TrialsToday); - PerformanceSummarySection_stage_6_TrialsToday.value = 0; - callback(PerformanceSummarySection_stage_6_TrialsToday); - PerformanceSummarySection_stage_7_TrialsToday.value = 0; - callback(PerformanceSummarySection_stage_7_TrialsToday); - PerformanceSummarySection_stage_8_TrialsToday.value = 0; - callback(PerformanceSummarySection_stage_8_TrialsToday); + for k = 1:8 + eval(sprintf('PerformanceSummarySection_stage_%d_TrialsToday.value = 0;', k)); + eval(sprintf('callback(PerformanceSummarySection_stage_%d_TrialsToday);', k)); + end end - % Updating Disp Values for Training_Peformance_Summary PerformanceSummarySection_stage_4_Trials.value = value(PerformanceSummarySection_stage_4_Trials) + 1; PerformanceSummarySection_stage_4_TrialsToday.value = value(PerformanceSummarySection_stage_4_TrialsToday) + 1; - PerformanceSummarySection_stage_4_ViolationRate.value = ((value(PerformanceSummarySection_stage_4_ViolationRate) * (value(PerformanceSummarySection_stage_4_Trials) - 1)) + double(violation_history(end))) / value(PerformanceSummarySection_stage_4_Trials); - PerformanceSummarySection_stage_4_TimeoutRate.value = ((value(PerformanceSummarySection_stage_4_TimeoutRate) * (value(PerformanceSummarySection_stage_4_Trials) - 1)) + double(timeout_history(end))) / value(PerformanceSummarySection_stage_4_Trials); + PerformanceSummarySection_stage_4_ViolationRate.value = ... + ((value(PerformanceSummarySection_stage_4_ViolationRate) * (value(PerformanceSummarySection_stage_4_Trials) - 1)) + double(violation_history(end))) ... + / value(PerformanceSummarySection_stage_4_Trials); + PerformanceSummarySection_stage_4_TimeoutRate.value = ... + ((value(PerformanceSummarySection_stage_4_TimeoutRate) * (value(PerformanceSummarySection_stage_4_Trials) - 1)) + double(timeout_history(end))) ... + / value(PerformanceSummarySection_stage_4_Trials); + if ~isnan(hit_history(end)) PerformanceSummarySection_stage_4_TrialsValid.value = value(PerformanceSummarySection_stage_4_TrialsValid) + 1; end callback(PerformanceSummarySection_stage_4_Trials); - callback(PerformanceSummarySection_stage_4_TrialsToday) + callback(PerformanceSummarySection_stage_4_TrialsToday); callback(PerformanceSummarySection_stage_4_ViolationRate); callback(PerformanceSummarySection_stage_4_TimeoutRate); callback(PerformanceSummarySection_stage_4_TrialsValid); - - % Updating Disp Values for Training_Peformance_Summary - SessionPerformanceSection_ntrials.value = n_done_trials; - SessionPerformanceSection_violation_percent.value = numel(find(violation_history))/n_done_trials; - SessionPerformanceSection_timeout_percent.value = numel(find(timeout_history))/n_done_trials; - if n_done_trials >= 20 - SessionPerformanceSection_violation_recent.value = numel(find(violation_history(end-19:end)))/20; - SessionPerformanceSection_timeout_recent.value = numel(find(timeout_history(end-19:end)))/20; - else - SessionPerformanceSection_timeout_recent.value = nan; - SessionPerformanceSection_violation_recent.value = nan; - end - SessionPerformanceSection_violation_stage.value = value(PerformanceSummarySection_stage_4_ViolationRate); - SessionPerformanceSection_ntrials_stage.value = value(PerformanceSummarySection_stage_4_Trials); - SessionPerformanceSection_ntrials_stage_today.value = value(PerformanceSummarySection_stage_4_TrialsToday); - SessionPerformanceSection_timeout_stage.value = value(PerformanceSummarySection_stage_4_TimeoutRate); - callback(SessionPerformanceSection_ntrials); - callback(SessionPerformanceSection_ntrials_stage); - callback(SessionPerformanceSection_ntrials_stage_today) - callback(SessionPerformanceSection_violation_percent); - callback(SessionPerformanceSection_timeout_percent); - callback(SessionPerformanceSection_violation_recent); - callback(SessionPerformanceSection_timeout_recent); - callback(SessionPerformanceSection_violation_stage); - callback(SessionPerformanceSection_timeout_stage); + % Session-wide stats + SessionPerformanceSection_ntrials.value = n_done_trials; + SessionPerformanceSection_violation_percent.value = numel(find(violation_history)) / n_done_trials; + SessionPerformanceSection_timeout_percent.value = numel(find(timeout_history)) / n_done_trials; + + if n_done_trials >= 20 + SessionPerformanceSection_violation_recent.value = numel(find(violation_history(end-19:end))) / 20; + SessionPerformanceSection_timeout_recent.value = numel(find(timeout_history(end-19:end))) / 20; + else + SessionPerformanceSection_violation_recent.value = nan; + SessionPerformanceSection_timeout_recent.value = nan; + end + + SessionPerformanceSection_violation_stage.value = value(PerformanceSummarySection_stage_4_ViolationRate); + SessionPerformanceSection_ntrials_stage.value = value(PerformanceSummarySection_stage_4_Trials); + SessionPerformanceSection_ntrials_stage_today.value = value(PerformanceSummarySection_stage_4_TrialsToday); + SessionPerformanceSection_timeout_stage.value = value(PerformanceSummarySection_stage_4_TimeoutRate); + + callback(SessionPerformanceSection_ntrials); + callback(SessionPerformanceSection_ntrials_stage); + callback(SessionPerformanceSection_ntrials_stage_today); + callback(SessionPerformanceSection_violation_percent); + callback(SessionPerformanceSection_timeout_percent); + callback(SessionPerformanceSection_violation_recent); + callback(SessionPerformanceSection_timeout_recent); + callback(SessionPerformanceSection_violation_stage); + callback(SessionPerformanceSection_timeout_stage); end + % end @@ -667,144 +653,133 @@ GetSoloFunctionArgs(obj); ClearHelperVarsNotOwned(obj); % +% Initial parameter fetch cp_max = value(TrainingStageParamsSection_max_CP); cp_min = value(TrainingStageParamsSection_min_CP); -% Fractional increment in center poke duration every time there is a non-cp-violation trial: cp_fraction = value(TrainingStageParamsSection_CPfraction_inc); -% Minimum increment (in secs) in center poke duration every time there is a non-cp-violation trial: cp_minimum_increment = 0.001; -% Starting total center poke duration: starting_cp = value(TrainingStageParamsSection_starting_CP) + value(ParamsSection_SettlingIn_time); -% number of warm-up trials n_trial_warmup = value(TrainingStageParamsSection_warm_up_trials); +cp_range = cp_max - cp_min; + +a1_min = 0.1; +a1_max = 0.4; stage_no = value(SessionDefinition_CURRENT_ACTIVE_STAGE); if stage_no ~= value(ParamsSection_training_stage) ParamsSection_training_stage.value = stage_no; callback(ParamsSection_training_stage); end -% Change the value of CP Duration -% Since starting a new session then do a pre warm up to last saved cp -% duration else continue with learning with increased poke time + +% CP duration logic +init_CP_duration = value(ParamsSection_init_CP_duration); +last_CP = value(TrainingStageParamsSection_last_session_CP); +curr_CP = value(ParamsSection_CP_duration); + if n_done_trials == 0 - ParamsSection_CP_duration.value = value(ParamsSection_init_CP_duration); -elseif n_done_trials == 1 - ParamsSection_CP_duration.value = starting_cp; + new_CP = init_CP_duration; + +elseif n_done_trials <= n_trial_warmup && n_done_trials > 0 + cp_delta = (last_CP - init_CP_duration) / n_trial_warmup; + new_CP = init_CP_duration + cp_delta * n_done_trials; + if new_CP < starting_cp + new_CP = starting_cp; + end else if ~violation_history(end) && ~timeout_history(end) - if value(ParamsSection_CP_duration) < max([cp_min,value(TrainingStageParamsSection_last_session_CP)]) % warm up stage - increment = (max([cp_min,value(TrainingStageParamsSection_last_session_CP)]) - value(ParamsSection_CP_duration))/ (n_trial_warmup - 1); - else - if value(ParamsSection_CP_duration) >= value(TrainingStageParamsSection_last_session_CP) && value(ParamsSection_CP_duration) <= cp_max % no warm up stage - increment = value(ParamsSection_CP_duration)*cp_fraction; - if increment < cp_minimum_increment - increment = cp_minimum_increment; - end - end - end - - ParamsSection_CP_duration.value = value(ParamsSection_CP_duration) + increment; - - % Check if the values are within the required range - if value(ParamsSection_CP_duration) < starting_cp - ParamsSection_CP_duration.value = starting_cp; - end - if value(ParamsSection_CP_duration) > cp_max - ParamsSection_CP_duration.value = cp_max; + increment = curr_CP * cp_fraction; + if increment < cp_minimum_increment + increment = cp_minimum_increment; end - + new_CP = curr_CP + increment; end end +ParamsSection_CP_duration.value = min(new_CP, cp_max); callback(ParamsSection_CP_duration); -if value(ParamsSection_CP_duration) >= 1 - ParamsSection_PreStim_time.value = 0.4; - if value(ParamsSection_CP_duration) < 2 - ParamsSection_A1_time.value = 0.1; - elseif value(ParamsSection_CP_duration) < 2.5 && value(ParamsSection_CP_duration) >= 2 - ParamsSection_A1_time.value = 0.2; - elseif value(ParamsSection_CP_duration) < 3 && value(ParamsSection_CP_duration) >= 2.5 - ParamsSection_A1_time.value = 0.3; +% Timing parameter adjustments based on CP_duration +if new_CP >= starting_cp + if new_CP >= cp_min + % Scale A1_time linearly between 0.1 to 0.4 + scale = (new_CP - cp_min) / cp_range; + scale = min(max(scale, 0), 1); + ParamsSection_A1_time.value = a1_min + scale * (a1_max - a1_min); else - ParamsSection_A1_time.value = 0.4; + ParamsSection_A1_time.value = a1_min; end -elseif value(ParamsSection_CP_duration) < 1 && value(ParamsSection_CP_duration) >= starting_cp - ParamsSection_SettlingIn_time.value = 0.2; - callback(ParamsSection_SettlingIn_time); - ParamsSection_PreStim_time.value = 0.1; - ParamsSection_A1_time.value = 0.1; -end -if value(ParamsSection_CP_duration) >= starting_cp - ParamsSection_time_bet_aud1_gocue.value = value(ParamsSection_CP_duration) - value(ParamsSection_SettlingIn_time) - value(ParamsSection_A1_time) - value(ParamsSection_PreStim_time); - callback(ParamsSection_time_bet_aud1_gocue) + if new_CP >= 1 + ParamsSection_PreStim_time.value = 0.4; + else + ParamsSection_PreStim_time.value = 0.1; + end + + ParamsSection_SettlingIn_time.value = 0.2; callback(ParamsSection_PreStim_time); callback(ParamsSection_A1_time); + callback(ParamsSection_SettlingIn_time); + + % Update time between Auditory and GoCue + ParamsSection_time_bet_aud1_gocue.value = new_CP - value(ParamsSection_SettlingIn_time) - value(ParamsSection_A1_time) - value(ParamsSection_PreStim_time); + callback(ParamsSection_time_bet_aud1_gocue); end +% Performance section updates if n_done_trials > 0 - if n_done_trials == 1 - PerformanceSummarySection_stage_1_TrialsToday.value = 0; - callback(PerformanceSummarySection_stage_1_TrialsToday); - PerformanceSummarySection_stage_2_TrialsToday.value = 0; - callback(PerformanceSummarySection_stage_2_TrialsToday); - PerformanceSummarySection_stage_3_TrialsToday.value = 0; - callback(PerformanceSummarySection_stage_3_TrialsToday); - PerformanceSummarySection_stage_4_TrialsToday.value = 0; - callback(PerformanceSummarySection_stage_4_TrialsToday); - PerformanceSummarySection_stage_5_TrialsToday.value = 0; - callback(PerformanceSummarySection_stage_5_TrialsToday); - PerformanceSummarySection_stage_6_TrialsToday.value = 0; - callback(PerformanceSummarySection_stage_6_TrialsToday); - PerformanceSummarySection_stage_7_TrialsToday.value = 0; - callback(PerformanceSummarySection_stage_7_TrialsToday); - PerformanceSummarySection_stage_8_TrialsToday.value = 0; - callback(PerformanceSummarySection_stage_8_TrialsToday); + for k = 1:8 + eval(sprintf('PerformanceSummarySection_stage_%d_TrialsToday.value = 0;', k)); + eval(sprintf('callback(PerformanceSummarySection_stage_%d_TrialsToday);', k)); + end end - % Updating Disp Values for Training_Peformance_Summary PerformanceSummarySection_stage_5_Trials.value = value(PerformanceSummarySection_stage_5_Trials) + 1; PerformanceSummarySection_stage_5_TrialsToday.value = value(PerformanceSummarySection_stage_5_TrialsToday) + 1; - PerformanceSummarySection_stage_5_ViolationRate.value = ((value(PerformanceSummarySection_stage_5_ViolationRate) * (value(PerformanceSummarySection_stage_5_Trials) - 1)) + double(violation_history(end))) / value(PerformanceSummarySection_stage_5_Trials); - PerformanceSummarySection_stage_5_TimeoutRate.value = ((value(PerformanceSummarySection_stage_5_TimeoutRate) * (value(PerformanceSummarySection_stage_5_Trials) - 1)) + double(timeout_history(end))) / value(PerformanceSummarySection_stage_5_Trials); + PerformanceSummarySection_stage_5_ViolationRate.value = ... + ((value(PerformanceSummarySection_stage_5_ViolationRate) * (value(PerformanceSummarySection_stage_5_Trials) - 1)) + double(violation_history(end))) ... + / value(PerformanceSummarySection_stage_5_Trials); + PerformanceSummarySection_stage_5_TimeoutRate.value = ... + ((value(PerformanceSummarySection_stage_5_TimeoutRate) * (value(PerformanceSummarySection_stage_5_Trials) - 1)) + double(timeout_history(end))) ... + / value(PerformanceSummarySection_stage_5_Trials); + if ~isnan(hit_history(end)) PerformanceSummarySection_stage_5_TrialsValid.value = value(PerformanceSummarySection_stage_5_TrialsValid) + 1; end callback(PerformanceSummarySection_stage_5_Trials); - callback(PerformanceSummarySection_stage_5_TrialsToday) + callback(PerformanceSummarySection_stage_5_TrialsToday); callback(PerformanceSummarySection_stage_5_ViolationRate); callback(PerformanceSummarySection_stage_5_TimeoutRate); callback(PerformanceSummarySection_stage_5_TrialsValid); - - % Updating Disp Values for Training_Peformance_Summary - SessionPerformanceSection_ntrials.value = n_done_trials; - SessionPerformanceSection_violation_percent.value = numel(find(violation_history))/n_done_trials; - SessionPerformanceSection_timeout_percent.value = numel(find(timeout_history))/n_done_trials; - if n_done_trials >= 20 - SessionPerformanceSection_violation_recent.value = numel(find(violation_history(end-19:end)))/20; - SessionPerformanceSection_timeout_recent.value = numel(find(timeout_history(end-19:end)))/20; - else - SessionPerformanceSection_timeout_recent.value = nan; - SessionPerformanceSection_violation_recent.value = nan; - end - SessionPerformanceSection_violation_stage.value = value(PerformanceSummarySection_stage_5_ViolationRate); - SessionPerformanceSection_ntrials_stage.value = value(PerformanceSummarySection_stage_5_Trials); - SessionPerformanceSection_ntrials_stage_today.value = value(PerformanceSummarySection_stage_5_TrialsToday); - SessionPerformanceSection_timeout_stage.value = value(PerformanceSummarySection_stage_5_TimeoutRate); - callback(SessionPerformanceSection_ntrials); - callback(SessionPerformanceSection_ntrials_stage); - callback(SessionPerformanceSection_ntrials_stage_today) - callback(SessionPerformanceSection_violation_percent); - callback(SessionPerformanceSection_timeout_percent); - callback(SessionPerformanceSection_violation_recent); - callback(SessionPerformanceSection_timeout_recent); - callback(SessionPerformanceSection_violation_stage); - callback(SessionPerformanceSection_timeout_stage); -end + % Session-wide stats + SessionPerformanceSection_ntrials.value = n_done_trials; + SessionPerformanceSection_violation_percent.value = numel(find(violation_history)) / n_done_trials; + SessionPerformanceSection_timeout_percent.value = numel(find(timeout_history)) / n_done_trials; + + if n_done_trials >= 20 + SessionPerformanceSection_violation_recent.value = numel(find(violation_history(end-19:end))) / 20; + SessionPerformanceSection_timeout_recent.value = numel(find(timeout_history(end-19:end))) / 20; + else + SessionPerformanceSection_violation_recent.value = nan; + SessionPerformanceSection_timeout_recent.value = nan; + end + SessionPerformanceSection_violation_stage.value = value(PerformanceSummarySection_stage_5_ViolationRate); + SessionPerformanceSection_ntrials_stage.value = value(PerformanceSummarySection_stage_5_Trials); + SessionPerformanceSection_ntrials_stage_today.value = value(PerformanceSummarySection_stage_5_TrialsToday); + SessionPerformanceSection_timeout_stage.value = value(PerformanceSummarySection_stage_5_TimeoutRate); + + callback(SessionPerformanceSection_ntrials); + callback(SessionPerformanceSection_ntrials_stage); + callback(SessionPerformanceSection_ntrials_stage_today); + callback(SessionPerformanceSection_violation_percent); + callback(SessionPerformanceSection_timeout_percent); + callback(SessionPerformanceSection_violation_recent); + callback(SessionPerformanceSection_timeout_recent); + callback(SessionPerformanceSection_violation_stage); + callback(SessionPerformanceSection_timeout_stage); +end % end @@ -885,108 +860,100 @@ callback(ParamsSection_training_stage); end -% Warm Up If starting a new session if n_done_trials == 0 - ParamsSection_CP_duration.value = value(ParamsSection_init_CP_duration); -elseif n_done_trials == 1 - ParamsSection_CP_duration.value = starting_cp; -else - if value(ParamsSection_CP_duration) < cp_max % warm up stage - if ~violation_history(end) && ~timeout_history(end) - increment = (cp_max - value(ParamsSection_CP_duration)) / (n_trial_warmup - 1); - ParamsSection_CP_duration.value = value(ParamsSection_CP_duration) + increment; - % Check if the values are within the required range - if value(ParamsSection_CP_duration) < starting_cp - ParamsSection_CP_duration.value = starting_cp; - end - if value(ParamsSection_CP_duration) > cp_max - ParamsSection_CP_duration.value = cp_max; - end - end + new_CP = init_CP_duration; +elseif n_done_trials <= n_trial_warmup + cp_delta = (cp_max - init_CP_duration) / n_trial_warmup; + new_CP = init_CP_duration + cp_delta * n_done_trials; + if new_CP < starting_cp + new_CP = starting_cp; end + if new_CP > cp_max + new_CP = cp_max; + end +else + new_CP = cp_max; end +ParamsSection_CP_duration.value = new_CP; callback(ParamsSection_CP_duration); -if value(ParamsSection_CP_duration) < 3 && value(ParamsSection_CP_duration) >= starting_cp % during the warm up phase +if new_CP >= starting_cp + ParamsSection_SettlingIn_time.value = 0.2; callback(ParamsSection_SettlingIn_time); - ParamsSection_PreStim_time.value = 0.1; - ParamsSection_A1_time.value = 0.1; -else - ParamsSection_A1_time.value = stim_dur; % actual training stage - time_range_PreStim_time = prestim_min : 0.01 : prestim_max; - ParamsSection_PreStim_time.value = time_range_PreStim_time(randi([1, numel(time_range_PreStim_time)],1,1)); -end -if value(ParamsSection_CP_duration) >= starting_cp + if value(ParamsSection_CP_duration) < 3 % during the warm up phase + ParamsSection_PreStim_time.value = 0.1; + ParamsSection_A1_time.value = 0.1; + else + ParamsSection_A1_time.value = stim_dur; % actual training stage + time_range_PreStim_time = prestim_min : 0.01 : prestim_max; + ParamsSection_PreStim_time.value = time_range_PreStim_time(randi([1, numel(time_range_PreStim_time)],1,1)); + end + ParamsSection_time_bet_aud1_gocue.value = value(ParamsSection_CP_duration) - value(ParamsSection_SettlingIn_time) - value(ParamsSection_A1_time) - value(ParamsSection_PreStim_time); callback(ParamsSection_time_bet_aud1_gocue) callback(ParamsSection_PreStim_time); callback(ParamsSection_A1_time); end +% --- Performance Logging --- if n_done_trials > 0 - if n_done_trials == 1 - PerformanceSummarySection_stage_1_TrialsToday.value = 0; - callback(PerformanceSummarySection_stage_1_TrialsToday); - PerformanceSummarySection_stage_2_TrialsToday.value = 0; - callback(PerformanceSummarySection_stage_2_TrialsToday); - PerformanceSummarySection_stage_3_TrialsToday.value = 0; - callback(PerformanceSummarySection_stage_3_TrialsToday); - PerformanceSummarySection_stage_4_TrialsToday.value = 0; - callback(PerformanceSummarySection_stage_4_TrialsToday); - PerformanceSummarySection_stage_5_TrialsToday.value = 0; - callback(PerformanceSummarySection_stage_5_TrialsToday); - PerformanceSummarySection_stage_6_TrialsToday.value = 0; - callback(PerformanceSummarySection_stage_6_TrialsToday); - PerformanceSummarySection_stage_7_TrialsToday.value = 0; - callback(PerformanceSummarySection_stage_7_TrialsToday); - PerformanceSummarySection_stage_8_TrialsToday.value = 0; - callback(PerformanceSummarySection_stage_8_TrialsToday); + for i = 1:8 + eval(sprintf('PerformanceSummarySection_stage_%d_TrialsToday.value = 0;', i)); + eval(sprintf('callback(PerformanceSummarySection_stage_%d_TrialsToday);', i)); + end end - % Updating Disp Values for Training_Peformance_Summary + % Example using stage 6 PerformanceSummarySection_stage_6_Trials.value = value(PerformanceSummarySection_stage_6_Trials) + 1; PerformanceSummarySection_stage_6_TrialsToday.value = value(PerformanceSummarySection_stage_6_TrialsToday) + 1; - PerformanceSummarySection_stage_6_ViolationRate.value = ((value(PerformanceSummarySection_stage_6_ViolationRate) * (value(PerformanceSummarySection_stage_6_Trials) - 1)) + double(violation_history(end))) / value(PerformanceSummarySection_stage_6_Trials); - PerformanceSummarySection_stage_6_TimeoutRate.value = ((value(PerformanceSummarySection_stage_6_TimeoutRate) * (value(PerformanceSummarySection_stage_6_Trials) - 1)) + double(timeout_history(end))) / value(PerformanceSummarySection_stage_6_Trials); + PerformanceSummarySection_stage_6_ViolationRate.value = ... + ((value(PerformanceSummarySection_stage_6_ViolationRate) * (value(PerformanceSummarySection_stage_6_Trials) - 1)) + ... + double(violation_history(end))) / value(PerformanceSummarySection_stage_6_Trials); + PerformanceSummarySection_stage_6_TimeoutRate.value = ... + ((value(PerformanceSummarySection_stage_6_TimeoutRate) * (value(PerformanceSummarySection_stage_6_Trials) - 1)) + ... + double(timeout_history(end))) / value(PerformanceSummarySection_stage_6_Trials); + if ~isnan(hit_history(end)) PerformanceSummarySection_stage_6_TrialsValid.value = value(PerformanceSummarySection_stage_6_TrialsValid) + 1; end callback(PerformanceSummarySection_stage_6_Trials); - callback(PerformanceSummarySection_stage_6_TrialsToday) + callback(PerformanceSummarySection_stage_6_TrialsToday); callback(PerformanceSummarySection_stage_6_ViolationRate); callback(PerformanceSummarySection_stage_6_TimeoutRate); callback(PerformanceSummarySection_stage_6_TrialsValid); - - % Updating Disp Values for SessionPerformanceSection - SessionPerformanceSection_ntrials.value = n_done_trials; - SessionPerformanceSection_violation_percent.value = numel(find(violation_history))/n_done_trials; - SessionPerformanceSection_timeout_percent.value = numel(find(timeout_history))/n_done_trials; - if n_done_trials >= 20 - SessionPerformanceSection_violation_recent.value = numel(find(violation_history(end-19:end)))/20; - SessionPerformanceSection_timeout_recent.value = numel(find(timeout_history(end-19:end)))/20; - else - SessionPerformanceSection_timeout_recent.value = nan; - SessionPerformanceSection_violation_recent.value = nan; - end - SessionPerformanceSection_violation_stage.value = value(PerformanceSummarySection_stage_6_ViolationRate); - SessionPerformanceSection_ntrials_stage.value = value(PerformanceSummarySection_stage_6_Trials); - SessionPerformanceSection_ntrials_stage_today.value = value(PerformanceSummarySection_stage_6_TrialsToday); - SessionPerformanceSection_timeout_stage.value = value(PerformanceSummarySection_stage_6_TimeoutRate); - callback(SessionPerformanceSection_ntrials); - callback(SessionPerformanceSection_ntrials_stage); - callback(SessionPerformanceSection_ntrials_stage_today) - callback(SessionPerformanceSection_violation_percent); - callback(SessionPerformanceSection_timeout_percent); - callback(SessionPerformanceSection_violation_recent); - callback(SessionPerformanceSection_timeout_recent); - callback(SessionPerformanceSection_violation_stage); - callback(SessionPerformanceSection_timeout_stage); + % SessionPerformance updates + SessionPerformanceSection_ntrials.value = n_done_trials; + SessionPerformanceSection_violation_percent.value = mean(violation_history); + SessionPerformanceSection_timeout_percent.value = mean(timeout_history); + + if n_done_trials >= 20 + SessionPerformanceSection_violation_recent.value = mean(violation_history(end-19:end)); + SessionPerformanceSection_timeout_recent.value = mean(timeout_history(end-19:end)); + else + SessionPerformanceSection_violation_recent.value = nan; + SessionPerformanceSection_timeout_recent.value = nan; + end + + SessionPerformanceSection_violation_stage.value = value(PerformanceSummarySection_stage_6_ViolationRate); + SessionPerformanceSection_ntrials_stage.value = value(PerformanceSummarySection_stage_6_Trials); + SessionPerformanceSection_ntrials_stage_today.value = value(PerformanceSummarySection_stage_6_TrialsToday); + SessionPerformanceSection_timeout_stage.value = value(PerformanceSummarySection_stage_6_TimeoutRate); + + callback(SessionPerformanceSection_ntrials); + callback(SessionPerformanceSection_ntrials_stage); + callback(SessionPerformanceSection_ntrials_stage_today); + callback(SessionPerformanceSection_violation_percent); + callback(SessionPerformanceSection_timeout_percent); + callback(SessionPerformanceSection_violation_recent); + callback(SessionPerformanceSection_timeout_recent); + callback(SessionPerformanceSection_violation_stage); + callback(SessionPerformanceSection_timeout_stage); end % @@ -1083,7 +1050,7 @@ else if value(ParamsSection_CP_duration) <= cp_max % warm up stage if ~violation_history(end) && ~timeout_history(end) - increment = (cp_max - value(ParamsSection_CP_duration))/ (n_trial_warmup - 1); + increment = (cp_max - value(ParamsSection_init_CP_duration))/ (n_trial_warmup - 1); ParamsSection_CP_duration.value = value(ParamsSection_CP_duration) + increment; % Check if the values are within the required range if value(ParamsSection_CP_duration) < starting_cp @@ -1113,67 +1080,61 @@ end callback(ParamsSection_CP_duration); +% Performance section updates if n_done_trials > 0 - if n_done_trials == 1 - PerformanceSummarySection_stage_1_TrialsToday.value = 0; - callback(PerformanceSummarySection_stage_1_TrialsToday); - PerformanceSummarySection_stage_2_TrialsToday.value = 0; - callback(PerformanceSummarySection_stage_2_TrialsToday); - PerformanceSummarySection_stage_3_TrialsToday.value = 0; - callback(PerformanceSummarySection_stage_3_TrialsToday); - PerformanceSummarySection_stage_4_TrialsToday.value = 0; - callback(PerformanceSummarySection_stage_4_TrialsToday); - PerformanceSummarySection_stage_5_TrialsToday.value = 0; - callback(PerformanceSummarySection_stage_5_TrialsToday); - PerformanceSummarySection_stage_6_TrialsToday.value = 0; - callback(PerformanceSummarySection_stage_6_TrialsToday); - PerformanceSummarySection_stage_7_TrialsToday.value = 0; - callback(PerformanceSummarySection_stage_7_TrialsToday); - PerformanceSummarySection_stage_8_TrialsToday.value = 0; - callback(PerformanceSummarySection_stage_8_TrialsToday); + for k = 1:8 + eval(sprintf('PerformanceSummarySection_stage_%d_TrialsToday.value = 0;', k)); + eval(sprintf('callback(PerformanceSummarySection_stage_%d_TrialsToday);', k)); + end end - % Updating Disp Values for Training_Peformance_Summary PerformanceSummarySection_stage_7_Trials.value = value(PerformanceSummarySection_stage_7_Trials) + 1; PerformanceSummarySection_stage_7_TrialsToday.value = value(PerformanceSummarySection_stage_7_TrialsToday) + 1; - PerformanceSummarySection_stage_7_ViolationRate.value = ((value(PerformanceSummarySection_stage_7_ViolationRate) * (value(PerformanceSummarySection_stage_7_Trials) - 1)) + double(violation_history(end))) / value(PerformanceSummarySection_stage_7_Trials); - PerformanceSummarySection_stage_7_TimeoutRate.value = ((value(PerformanceSummarySection_stage_7_TimeoutRate) * (value(PerformanceSummarySection_stage_7_Trials) - 1)) + double(timeout_history(end))) / value(PerformanceSummarySection_stage_7_Trials); + PerformanceSummarySection_stage_7_ViolationRate.value = ... + ((value(PerformanceSummarySection_stage_7_ViolationRate) * (value(PerformanceSummarySection_stage_7_Trials) - 1)) + double(violation_history(end))) ... + / value(PerformanceSummarySection_stage_7_Trials); + PerformanceSummarySection_stage_7_TimeoutRate.value = ... + ((value(PerformanceSummarySection_stage_7_TimeoutRate) * (value(PerformanceSummarySection_stage_7_Trials) - 1)) + double(timeout_history(end))) ... + / value(PerformanceSummarySection_stage_7_Trials); + if ~isnan(hit_history(end)) PerformanceSummarySection_stage_7_TrialsValid.value = value(PerformanceSummarySection_stage_7_TrialsValid) + 1; end callback(PerformanceSummarySection_stage_7_Trials); - callback(PerformanceSummarySection_stage_7_TrialsToday) + callback(PerformanceSummarySection_stage_7_TrialsToday); callback(PerformanceSummarySection_stage_7_ViolationRate); callback(PerformanceSummarySection_stage_7_TimeoutRate); callback(PerformanceSummarySection_stage_7_TrialsValid); - - % Updating Disp Values for SessionPerformanceSection - SessionPerformanceSection_ntrials.value = n_done_trials; - SessionPerformanceSection_violation_percent.value = numel(find(violation_history))/n_done_trials; - SessionPerformanceSection_timeout_percent.value = numel(find(timeout_history))/n_done_trials; - if n_done_trials >= 20 - SessionPerformanceSection_violation_recent.value = numel(find(violation_history(end-19:end)))/20; - SessionPerformanceSection_timeout_recent.value = numel(find(timeout_history(end-19:end)))/20; - else - SessionPerformanceSection_timeout_recent.value = nan; - SessionPerformanceSection_violation_recent.value = nan; - end - SessionPerformanceSection_violation_stage.value = value(PerformanceSummarySection_stage_7_ViolationRate); - SessionPerformanceSection_ntrials_stage.value = value(PerformanceSummarySection_stage_7_Trials); - SessionPerformanceSection_ntrials_stage_today.value = value(PerformanceSummarySection_stage_7_TrialsToday); - SessionPerformanceSection_timeout_stage.value = value(PerformanceSummarySection_stage_7_TimeoutRate); - callback(SessionPerformanceSection_ntrials); - callback(SessionPerformanceSection_ntrials_stage); - callback(SessionPerformanceSection_ntrials_stage_today) - callback(SessionPerformanceSection_violation_percent); - callback(SessionPerformanceSection_timeout_percent); - callback(SessionPerformanceSection_violation_recent); - callback(SessionPerformanceSection_timeout_recent); - callback(SessionPerformanceSection_violation_stage); - callback(SessionPerformanceSection_timeout_stage); + % Session-wide stats + SessionPerformanceSection_ntrials.value = n_done_trials; + SessionPerformanceSection_violation_percent.value = numel(find(violation_history)) / n_done_trials; + SessionPerformanceSection_timeout_percent.value = numel(find(timeout_history)) / n_done_trials; + + if n_done_trials >= 20 + SessionPerformanceSection_violation_recent.value = numel(find(violation_history(end-19:end))) / 20; + SessionPerformanceSection_timeout_recent.value = numel(find(timeout_history(end-19:end))) / 20; + else + SessionPerformanceSection_violation_recent.value = nan; + SessionPerformanceSection_timeout_recent.value = nan; + end + + SessionPerformanceSection_violation_stage.value = value(PerformanceSummarySection_stage_7_ViolationRate); + SessionPerformanceSection_ntrials_stage.value = value(PerformanceSummarySection_stage_7_Trials); + SessionPerformanceSection_ntrials_stage_today.value = value(PerformanceSummarySection_stage_7_TrialsToday); + SessionPerformanceSection_timeout_stage.value = value(PerformanceSummarySection_stage_7_TimeoutRate); + + callback(SessionPerformanceSection_ntrials); + callback(SessionPerformanceSection_ntrials_stage); + callback(SessionPerformanceSection_ntrials_stage_today); + callback(SessionPerformanceSection_violation_percent); + callback(SessionPerformanceSection_timeout_percent); + callback(SessionPerformanceSection_violation_recent); + callback(SessionPerformanceSection_timeout_recent); + callback(SessionPerformanceSection_violation_stage); + callback(SessionPerformanceSection_timeout_stage); end % @@ -1271,7 +1232,7 @@ else if value(ParamsSection_CP_duration) <= cp_max % warm up stage if ~violation_history(end) && ~timeout_history(end) - increment = (cp_max - value(ParamsSection_CP_duration))/ (n_trial_warmup - 1); + increment = (cp_max - value(ParamsSection_init_CP_duration))/ (n_trial_warmup - 1); ParamsSection_CP_duration.value = value(ParamsSection_CP_duration) + increment; % Check if the values are within the required range if value(ParamsSection_CP_duration) < starting_cp diff --git a/Protocols/@ArpitCentrePokeTraining/private/create_ArpitCentrePokeTraining_SettingFile.asv b/Protocols/@ArpitCentrePokeTraining/private/create_ArpitCentrePokeTraining_SettingFile.asv new file mode 100644 index 00000000..4ce0e27c --- /dev/null +++ b/Protocols/@ArpitCentrePokeTraining/private/create_ArpitCentrePokeTraining_SettingFile.asv @@ -0,0 +1,92 @@ +function create_ArpitCentrePokeTraining_SettingFile() + +templateDir = 'C:/ratter/'; +templateDir = '/Users/AkramiLab_Arpit/Documents/AkramiLabScripts/ratter'; + +templatePath = [templateDir '/Protocols/@ArpitCentrePokeTraining/private/pipeline_ArpitCentrePokeTraining_arpit_AR05_250516.m']; +setting_filename = 'settings_@ArpitCentrePokeTraining_arpit_AR05_250516a.mat'; +outputRootDir = [templateDir '/SoloData/Settings/']; + +experimenter = input('Enter experimenter name: ', 's'); +ratname = input('Enter rat name: ', 's'); + +% Get today's date +newDate = datestr(now, 'yymmdd'); + +% Read the template file +if ~isfile(templatePath) + error('Template file not found: %s', templatePath); +end +code = fileread(templatePath); +lines = splitlines(code); + +% Find and update the function definition line +newFuncName = ''; +for i = 1:length(lines) + line = strtrim(lines{i}); + if startsWith(line, 'function') && contains(line, 'pipeline_') + % Extract argument list + pattern = 'function\s+varargout\s*=\s*pipeline_.*?\((.*?)\)'; + tokens = regexp(line, pattern, 'tokens'); + if ~isempty(tokens) + args = tokens{1}{1}; + newFuncName = sprintf('pipeline_ArpitCentrePokeTraining_%s_%s_%s', ... + experimenter, ratname, newDate); + lines{i} = sprintf('function varargout = %s(%s)', newFuncName, args); + break; + end + end +end + +if isempty(newFuncName) + error('Could not find valid function definition line in template.'); +end + +% Create output directory if needed +outputDir = fullfile(outputRootDir, experimenter, ratname); +if ~exist(outputDir, 'dir') + mkdir(outputDir); +end + +% Build full new script path +newScriptPath = fullfile(outputDir, [newFuncName, '.m']); + +% Save modified content +newCode = strjoin(lines, newline); +fid = fopen(newScriptPath, 'w'); +if fid == -1 + error('Failed to open new file for writing: %s', newScriptPath); +end +fwrite(fid, newCode); +fclose(fid); + +fprintf('New Session Definition file saved to: %s\n', newScriptPath); + +% Load and modify .mat file +[templateFolder, ~, ~] = fileparts(templatePath); +matTemplatePath = fullfile(templateFolder, setting_filename); + +if ~isfile(matTemplatePath) + warning('No setting_file found in template directory: %s', matTemplatePath); + newMatPath = ''; + return; +end + +load(matTemplatePath,'saved','saved_autoset','fig_position'); % Load struct + +saved.SavingSection_experimenter = experimenter; +saved.SavingSection_ratname = ratname; +saved.SessionDefinition_textTrainingStageFile = newScriptPath; +saved.ArpitCentrePokeTraining_prot_title = sprintf('ArpitCentrePokeTraining: %s, %s',experimenter,ratname); +saved.PokesPlotSection_textHeader = sprintf('PokesPlotSection(%s, %s',experimenter,ratname); +saved.SessionDefinition_textHeader = sprintf('SESSION AUTOMATOR WINDOW: %s, %s',experimenter,ratname); + +new_MATfile = sprintf('settings_@ArpitCentrePokeTraining__%s_%s_%sa.mat', ... + experimenter, ratname, newDate); + +newMatPath = fullfile(outputDir, new_MATfile); + +save(newMatPath,'saved','saved_autoset','fig_position'); +fprintf('New setting file saved to: %s\n', newMatPath); + +end diff --git a/Protocols/@ArpitCentrePokeTraining/private/create_ArpitCentrePokeTraining_SettingFile.m b/Protocols/@ArpitCentrePokeTraining/private/create_ArpitCentrePokeTraining_SettingFile.m index 83976046..05a556d4 100644 --- a/Protocols/@ArpitCentrePokeTraining/private/create_ArpitCentrePokeTraining_SettingFile.m +++ b/Protocols/@ArpitCentrePokeTraining/private/create_ArpitCentrePokeTraining_SettingFile.m @@ -1,9 +1,9 @@ function create_ArpitCentrePokeTraining_SettingFile() templateDir = 'C:/ratter/'; -templateDir = '/Users/AkramiLab_Arpit/Documents/AkramiLabScripts/ratter'; templatePath = [templateDir '/Protocols/@ArpitCentrePokeTraining/private/pipeline_ArpitCentrePokeTraining_arpit_AR05_250516.m']; +setting_filename = 'settings_@ArpitCentrePokeTraining_arpit_AR05_250516a.mat'; outputRootDir = [templateDir '/SoloData/Settings/']; experimenter = input('Enter experimenter name: ', 's'); @@ -63,7 +63,7 @@ function create_ArpitCentrePokeTraining_SettingFile() % Load and modify .mat file [templateFolder, ~, ~] = fileparts(templatePath); -matTemplatePath = fullfile(templateFolder, 'settings_@ArpitCentrePokeTraining_arpit_AR05_250516a.mat'); +matTemplatePath = fullfile(templateFolder, setting_filename); if ~isfile(matTemplatePath) warning('No setting_file found in template directory: %s', matTemplatePath); diff --git a/Protocols/@ArpitCentrePokeTraining/private/pipeline_ArpitCentrePokeTraining_arpit_AR05_250516.m b/Protocols/@ArpitCentrePokeTraining/private/pipeline_ArpitCentrePokeTraining_arpit_AR05_250516.m deleted file mode 100644 index dc414f6f..00000000 --- a/Protocols/@ArpitCentrePokeTraining/private/pipeline_ArpitCentrePokeTraining_arpit_AR05_250516.m +++ /dev/null @@ -1,1379 +0,0 @@ -%Training stage file. -%Please use the session automator window exclusively -%to edit this file. - -function varargout = pipeline_ArpitCentrePokeTraining_experimenter_ratname_250516(obj, action, varargin) - -GetSoloFunctionArgs('func_owner', ['@' class(obj)], 'func_name', 'SessionModel'); - -pairs = {'helper_vars_eval', true; - 'stage_algorithm_eval', true; - 'completion_test_eval', false; - 'eod_logic_eval', false}; -parseargs(varargin, pairs); - -switch action - - -%% Familiarize with Reward Side Pokes - -% -case 'Familiarize with Reward Side Pokes' -if helper_vars_eval -GetSoloFunctionArgs(obj); -ClearHelperVarsNotOwned(obj); -% -CreateHelperVar(obj,'stage_start_completed_trial','value',n_done_trials,'force_init',true); -% -end -if stage_algorithm_eval -GetSoloFunctionArgs(obj); -ClearHelperVarsNotOwned(obj); -% - -% Update ParamsSection -ParamsSection_MaxSame.value = 4; -callback(ParamsSection_MaxSame); -stage_no = value(SessionDefinition_CURRENT_ACTIVE_STAGE); -if stage_no ~= value(ParamsSection_training_stage) - ParamsSection_training_stage.value = stage_no; - callback(ParamsSection_training_stage); -end - -% Update TrainingStageParamsSection -if n_done_trials >= 2 - if previous_sides(end) ~= previous_sides(end-1) && all(hit_history(end-1:end)) % last and present trials should also be a valid trial - TrainingStageParamsSection_trial_oppSide.value = value(TrainingStageParamsSection_trial_oppSide) + 1; % updating value for variable in TrainingParams_Section - callback(TrainingStageParamsSection_trial_oppSide); - end -end - -% Updating Disp Values for Training_Peformance_Summary -if n_done_trials > 0 - PerformanceSummarySection_stage_1_Trials.value = value(PerformanceSummarySection_stage_1_Trials) + 1; - PerformanceSummarySection_stage_1_TrialsToday.value = value(PerformanceSummarySection_stage_1_TrialsToday) + 1; - PerformanceSummarySection_stage_1_ViolationRate.value = nan; - PerformanceSummarySection_stage_1_TimeoutRate.value = nan; - if ~isnan(hit_history(end)) - PerformanceSummarySection_stage_1_TrialsValid.value = value(PerformanceSummarySection_stage_1_TrialsValid) + 1; - end - callback(PerformanceSummarySection_stage_1_Trials); - callback(PerformanceSummarySection_stage_1_TrialsToday) - callback(PerformanceSummarySection_stage_1_ViolationRate); - callback(PerformanceSummarySection_stage_1_TimeoutRate); - callback(PerformanceSummarySection_stage_1_TrialsValid); - - % Updating Disp Values for Training_Peformance_Summary - SessionPerformanceSection_ntrials.value = n_done_trials; - SessionPerformanceSection_violation_rate.value = nan; - SessionPerformanceSection_timeout_rate.value = nan; - SessionPerformanceSection_violation_recent.value = nan; - SessionPerformanceSection_timeout_recent.value = nan; - SessionPerformanceSection_ntrials_stage.value = value(PerformanceSummarySection_stage_1_Trials); - SessionPerformanceSection_ntrials_stage_today.value = value(PerformanceSummarySection_stage_1_TrialsToday); - SessionPerformanceSection_timeout_stage.value = nan; - SessionPerformanceSection_violation_stage.value = nan; - - callback(SessionPerformanceSection_ntrials); - callback(SessionPerformanceSection_ntrials_stage); - callback(SessionPerformanceSection_ntrials_stage_today) - callback(SessionPerformanceSection_violation_rate); - callback(SessionPerformanceSection_timeout_rate); - callback(SessionPerformanceSection_violation_recent); - callback(SessionPerformanceSection_timeout_recent); - callback(SessionPerformanceSection_violation_stage); - callback(SessionPerformanceSection_timeout_stage); - -end -% -end -if completion_test_eval -GetSoloFunctionArgs(obj); -ClearHelperVarsNotOwned(obj); -clear('ans'); -% -if ParamsSection_use_auto_train % do completion check if auto training - stage_no = value(SessionDefinition_CURRENT_ACTIVE_STAGE); - % only run it if its the start of the day, number of trials is small - if n_done_trials < 100 - if value(PerformanceSummarySection_stage_1_TrialsValid) > value(TrainingStageParamsSection_total_trials) && ... - value(TrainingStageParamsSection_trial_oppSide) > value(TrainingStageParamsSection_total_trials_opp) - ParamsSection_training_stage.value = stage_no + 1; - callback(ParamsSection_training_stage); - ParamsSection(obj, 'Changed_Training_Stage'); - SessionDefinition(obj, 'jump_to_stage', 'Timeout Rewarded Side Pokes'); - end - end -end -% -if exist('ans', 'var') -varargout{1}=logical(ans); clear('ans'); -else -varargout{1}=false; -end -end -if eod_logic_eval -GetSoloFunctionArgs(obj); -ClearHelperVarsNotOwned(obj); -% -stage_no = value(SessionDefinition_CURRENT_ACTIVE_STAGE); -if value(PerformanceSummarySection_stage_1_TrialsValid) > value(TrainingStageParamsSection_total_trials) && ... - value(TrainingStageParamsSection_trial_oppSide) > value(TrainingStageParamsSection_total_trials_opp) - ParamsSection_training_stage.value = stage_no + 1; - callback(ParamsSection_training_stage); - ParamsSection(obj, 'Changed_Training_Stage'); - SessionDefinition(obj, 'jump_to_stage', 'Timeout Rewarded Side Pokes'); -end -PerformanceSummarySection_stage_1_TrialsToday.value = 0; -callback(PerformanceSummarySection_stage_1_TrialsToday); -PerformanceSummarySection_stage_2_TrialsToday.value = 0; -callback(PerformanceSummarySection_stage_2_TrialsToday); -PerformanceSummarySection_stage_3_TrialsToday.value = 0; -callback(PerformanceSummarySection_stage_3_TrialsToday); -PerformanceSummarySection_stage_4_TrialsToday.value = 0; -callback(PerformanceSummarySection_stage_4_TrialsToday); -PerformanceSummarySection_stage_5_TrialsToday.value = 0; -callback(PerformanceSummarySection_stage_5_TrialsToday); -PerformanceSummarySection_stage_6_TrialsToday.value = 0; -callback(PerformanceSummarySection_stage_6_TrialsToday); -PerformanceSummarySection_stage_7_TrialsToday.value = 0; -callback(PerformanceSummarySection_stage_7_TrialsToday); -PerformanceSummarySection_stage_8_TrialsToday.value = 0; -callback(PerformanceSummarySection_stage_8_TrialsToday); -% -end -% - -%% Timeout Rewarded Side Pokes - -% -case 'Timeout Rewarded Side Pokes' -if helper_vars_eval -GetSoloFunctionArgs(obj); -ClearHelperVarsNotOwned(obj); -% -CreateHelperVar(obj,'this_stage_opp_side_trials', 'value', 0, 'force_init',true); -% -end -if stage_algorithm_eval -GetSoloFunctionArgs(obj); -ClearHelperVarsNotOwned(obj); -% -% Change ParamsSection Vars -ParamsSection_MaxSame.value = 4; -callback(ParamsSection_MaxSame); -stage_no = value(SessionDefinition_CURRENT_ACTIVE_STAGE); -if stage_no ~= value(ParamsSection_training_stage) - ParamsSection_training_stage.value = stage_no; - callback(ParamsSection_training_stage); -end -% Update the reward collection time based upon behav -if length(timeout_history) > 5 - if all(timeout_history(end-1:end)) && value(this_stage_opp_side_trials) >= 2 - ParamsSection_RewardCollection_duration.value = min([value(ParamsSection_RewardCollection_duration) + 1,... - value(TrainingStageParamsSection_max_rColl_dur)]); - callback(ParamsSection_RewardCollection_duration); - this_stage_opp_side_trials.value = 0; - end - if ~any(timeout_history(end-1:end)) && value(this_stage_opp_side_trials) >= 2 - ParamsSection_RewardCollection_duration.value = max([value(ParamsSection_RewardCollection_duration) - 1,... - value(TrainingStageParamsSection_min_rColl_dur)]); - callback(ParamsSection_RewardCollection_duration); - this_stage_opp_side_trials.value = 0; - end -end -if length(timeout_history) > 20 - if all(timeout_history(end-19:end)) - ParamsSection_RewardCollection_duration.value = 120; - callback(ParamsSection_RewardCollection_duration); - end -end - -% Update TrainingStageParamsSection -if n_done_trials >= 2 - if previous_sides(end) ~= previous_sides(end-1) && all(hit_history(end-1:end)) % last and present trials should also be a valid trial - TrainingStageParamsSection_trial_oppSide.value = value(TrainingStageParamsSection_trial_oppSide) + 1; % updating value for variable in TrainingParams_Section - callback(TrainingStageParamsSection_trial_oppSide); - end -end - - -% Updating Disp Values for Training_Peformance_Summary -if n_done_trials > 0 - PerformanceSummarySection_stage_2_Trials.value = value(PerformanceSummarySection_stage_2_Trials) + 1; - PerformanceSummarySection_stage_2_TrialsToday.value = value(PerformanceSummarySection_stage_2_TrialsToday) + 1; - PerformanceSummarySection_stage_2_ViolationRate.value = nan; - PerformanceSummarySection_stage_2_TimeoutRate.value = ((value(PerformanceSummarySection_stage_2_TimeoutRate) * (value(PerformanceSummarySection_stage_2_Trials) - 1)) + double(timeout_history(end))) / value(PerformanceSummarySection_stage_2_Trials); - if ~isnan(hit_history(end)) - PerformanceSummarySection_stage_2_TrialsValid.value = value(PerformanceSummarySection_stage_2_TrialsValid) + 1; - end - - callback(PerformanceSummarySection_stage_2_Trials); - callback(PerformanceSummarySection_stage_2_TrialsToday) - callback(PerformanceSummarySection_stage_2_ViolationRate); - callback(PerformanceSummarySection_stage_2_TimeoutRate); - callback(PerformanceSummarySection_stage_2_TrialsValid); - - % Updating Disp Values for Training_Peformance_Summary - SessionPerformanceSection_ntrials.value = n_done_trials; - SessionPerformanceSection_violation_rate.value = nan; - SessionPerformanceSection_timeout_rate.value = numel(find(timeout_history))/n_done_trials; - SessionPerformanceSection_violation_recent.value = nan; - if n_done_trials >= 20 - SessionPerformanceSection_timeout_recent.value = numel(find(timeout_history(end-19:end)))/20; - else - SessionPerformanceSection_timeout_recent.value = nan; - end - SessionPerformanceSection_violation_stage.value = nan; - SessionPerformanceSection_ntrials_stage.value = value(PerformanceSummarySection_stage_2_Trials); - SessionPerformanceSection_ntrials_stage_today.value = value(PerformanceSummarySection_stage_2_TrialsToday); - SessionPerformanceSection_timeout_stage.value = value(PerformanceSummarySection_stage_2_TimeoutRate); - - callback(SessionPerformanceSection_ntrials); - callback(SessionPerformanceSection_ntrials_stage); - callback(SessionPerformanceSection_ntrials_stage_today) - callback(SessionPerformanceSection_violation_rate); - callback(SessionPerformanceSection_timeout_rate); - callback(SessionPerformanceSection_violation_recent); - callback(SessionPerformanceSection_timeout_recent); - callback(SessionPerformanceSection_violation_stage); - callback(SessionPerformanceSection_timeout_stage); -end - -% -end -if completion_test_eval -GetSoloFunctionArgs(obj); -ClearHelperVarsNotOwned(obj); -clear('ans'); -% -if ParamsSection_use_auto_train % do completion check if auto training - stage_no = value(SessionDefinition_CURRENT_ACTIVE_STAGE); - % only run it if its the start of the day, number of trials is small - if n_done_trials > 50 - if value(PerformanceSummarySection_stage_2_TrialsValid) > value(TrainingStageParamsSection_total_trials) && ... - value(TrainingStageParamsSection_trial_oppSide) > value(TrainingStageParamsSection_total_trials_opp) - ParamsSection_training_stage.value = stage_no + 1; - callback(ParamsSection_training_stage); - ParamsSection(obj, 'Changed_Training_Stage'); - SessionDefinition(obj, 'jump_to_stage', 'Introduce Centre Poke'); - end - end -end -% -if exist('ans', 'var') -varargout{1}=logical(ans); clear('ans'); -else -varargout{1}=false; -end -end -if eod_logic_eval -GetSoloFunctionArgs(obj); -ClearHelperVarsNotOwned(obj); -% -PerformanceSummarySection_stage_1_TrialsToday.value = 0; -callback(PerformanceSummarySection_stage_1_TrialsToday); -PerformanceSummarySection_stage_2_TrialsToday.value = 0; -callback(PerformanceSummarySection_stage_2_TrialsToday); -PerformanceSummarySection_stage_3_TrialsToday.value = 0; -callback(PerformanceSummarySection_stage_3_TrialsToday); -PerformanceSummarySection_stage_4_TrialsToday.value = 0; -callback(PerformanceSummarySection_stage_4_TrialsToday); -PerformanceSummarySection_stage_5_TrialsToday.value = 0; -callback(PerformanceSummarySection_stage_5_TrialsToday); -PerformanceSummarySection_stage_6_TrialsToday.value = 0; -callback(PerformanceSummarySection_stage_6_TrialsToday); -PerformanceSummarySection_stage_7_TrialsToday.value = 0; -callback(PerformanceSummarySection_stage_7_TrialsToday); -PerformanceSummarySection_stage_8_TrialsToday.value = 0; -callback(PerformanceSummarySection_stage_8_TrialsToday); -% -end -% - -%% Introduce Centre Poke - -% -case 'Introduce Centre Poke' -if helper_vars_eval -GetSoloFunctionArgs(obj); -ClearHelperVarsNotOwned(obj); -% - -% -end -if stage_algorithm_eval -GetSoloFunctionArgs(obj); -ClearHelperVarsNotOwned(obj); -% -% Maximum & Minimum duration of center poke, in secs: -cp_max = value(ParamsSection_SettlingIn_time) + value(ParamsSection_legal_cbreak); -cp_min = value(ParamsSection_init_CP_duration); -% Minimum increment (in secs) in center poke duration every time there is a non-cp-violation trial: -cp_minimum_increment = 0.001; -ParamsSection_MaxSame.value = Inf; -callback(ParamsSection_MaxSame); - -stage_no = value(SessionDefinition_CURRENT_ACTIVE_STAGE); -if stage_no ~= value(ParamsSection_training_stage) - ParamsSection_training_stage.value = stage_no; - callback(ParamsSection_training_stage); -end -% Change the value of CP Duration -if value(TrainingStageParamsSection_last_session_CP) == 0 && value(PerformanceSummarySection_stage_3_Trials) < 2 - ParamsSection_CP_duration.value = cp_min; % initialize to min_CP -end - -if n_done_trials < 1 % intialize to min value at the start of each session/day - ParamsSection_CP_duration.value = value(ParamsSection_init_CP_duration); -elseif n_done_trials == 1 - ParamsSection_CP_duration.value = value(TrainingStageParamsSection_last_session_CP); -else - if ~timeout_history(end) && value(ParamsSection_CP_duration) < cp_max - increment = value(ParamsSection_CP_duration) * value(TrainingStageParamsSection_CPfraction_inc); - if increment < cp_minimum_increment - increment = cp_minimum_increment; - end - ParamsSection_CP_duration.value = value(ParamsSection_CP_duration) + increment; - end -end -callback(ParamsSection_CP_duration); - -if n_done_trials > 0 - % Updating Disp Values for Training_Peformance_Summary - PerformanceSummarySection_stage_3_Trials.value = value(PerformanceSummarySection_stage_3_Trials) + 1; - PerformanceSummarySection_stage_3_TrialsToday.value = value(PerformanceSummarySection_stage_3_TrialsToday) + 1; - PerformanceSummarySection_stage_3_ViolationRate.value = nan; - PerformanceSummarySection_stage_3_TimeoutRate.value = ((value(PerformanceSummarySection_stage_3_TimeoutRate) * (value(PerformanceSummarySection_stage_3_Trials) - 1)) + double(timeout_history(end))) / value(PerformanceSummarySection_stage_3_Trials); - if ~isnan(hit_history(end)) - PerformanceSummarySection_stage_3_TrialsValid.value = value(PerformanceSummarySection_stage_3_TrialsValid) + 1; - end - - callback(PerformanceSummarySection_stage_3_Trials); - callback(PerformanceSummarySection_stage_3_TrialsToday) - callback(PerformanceSummarySection_stage_3_ViolationRate); - callback(PerformanceSummarySection_stage_3_TimeoutRate); - callback(PerformanceSummarySection_stage_3_TrialsValid); - - % Updating Disp Values for Training_Peformance_Summary - SessionPerformanceSection_ntrials.value = n_done_trials; - SessionPerformanceSection_violation_rate.value = nan; - SessionPerformanceSection_timeout_rate.value = numel(find(timeout_history))/n_done_trials; - SessionPerformanceSection_violation_recent.value = nan; - if n_done_trials >= 20 - SessionPerformanceSection_timeout_recent.value = numel(find(timeout_history(end-19:end)))/20; - else - SessionPerformanceSection_timeout_recent.value = nan; - end - SessionPerformanceSection_violation_stage.value = nan; - SessionPerformanceSection_ntrials_stage.value = value(PerformanceSummarySection_stage_3_Trials); - SessionPerformanceSection_ntrials_stage_today.value = value(PerformanceSummarySection_stage_3_TrialsToday); - SessionPerformanceSection_timeout_stage.value = value(PerformanceSummarySection_stage_3_TimeoutRate); - - callback(SessionPerformanceSection_ntrials); - callback(SessionPerformanceSection_ntrials_stage); - callback(SessionPerformanceSection_ntrials_stage_today) - callback(SessionPerformanceSection_violation_rate); - callback(SessionPerformanceSection_timeout_rate); - callback(SessionPerformanceSection_violation_recent); - callback(SessionPerformanceSection_timeout_recent); - callback(SessionPerformanceSection_violation_stage); - callback(SessionPerformanceSection_timeout_stage); -end - -% -end -if completion_test_eval -GetSoloFunctionArgs(obj); -ClearHelperVarsNotOwned(obj); -clear('ans'); -% -if ParamsSection_use_auto_train % do completion check if auto training - cp_max = value(ParamsSection_SettlingIn_time) + value(ParamsSection_legal_cbreak); - if value(ParamsSection_CP_duration) >= cp_max - TrainingStageParamsSection_last_session_CP.value = value(ParamsSection_CP_duration); - callback(TrainingStageParamsSection_last_session_CP); - ParamsSection_training_stage.value = 4; - callback(ParamsSection_training_stage); - ParamsSection(obj, 'Changed_Training_Stage'); - SessionDefinition(obj, 'jump_to_stage', 'Introduce Violation for Centre Poke'); - end -end -% -if exist('ans', 'var') -varargout{1}=logical(ans); clear('ans'); -else -varargout{1}=false; -end -end -if eod_logic_eval -GetSoloFunctionArgs(obj); -ClearHelperVarsNotOwned(obj); -% -% Update the CP duration reached in this session -TrainingStageParamsSection_last_session_CP.value = value(ParamsSection_CP_duration); -callback(TrainingStageParamsSection_last_session_CP); -% Reset the number of trials done today for this stage -PerformanceSummarySection_stage_1_TrialsToday.value = 0; -callback(PerformanceSummarySection_stage_1_TrialsToday); -PerformanceSummarySection_stage_2_TrialsToday.value = 0; -callback(PerformanceSummarySection_stage_2_TrialsToday); -PerformanceSummarySection_stage_3_TrialsToday.value = 0; -callback(PerformanceSummarySection_stage_3_TrialsToday); -PerformanceSummarySection_stage_4_TrialsToday.value = 0; -callback(PerformanceSummarySection_stage_4_TrialsToday); -PerformanceSummarySection_stage_5_TrialsToday.value = 0; -callback(PerformanceSummarySection_stage_5_TrialsToday); -PerformanceSummarySection_stage_6_TrialsToday.value = 0; -callback(PerformanceSummarySection_stage_6_TrialsToday); -PerformanceSummarySection_stage_7_TrialsToday.value = 0; -callback(PerformanceSummarySection_stage_7_TrialsToday); -PerformanceSummarySection_stage_8_TrialsToday.value = 0; -callback(PerformanceSummarySection_stage_8_TrialsToday); -% -end -% - -%% Introduce Violation for Centre Poke - -% -case 'Introduce Violation for Centre Poke' -if helper_vars_eval -GetSoloFunctionArgs(obj); -ClearHelperVarsNotOwned(obj); -% - -% -end -if stage_algorithm_eval -GetSoloFunctionArgs(obj); -ClearHelperVarsNotOwned(obj); -% - -% Maximum & Minimum duration of center poke, in secs: -cp_min = value(ParamsSection_SettlingIn_time) + value(ParamsSection_legal_cbreak); -cp_max = value(TrainingStageParamsSection_max_CP); -% Fractional increment in center poke duration every time there is a non-cp-violation trial: -cp_fraction = value(TrainingStageParamsSection_CPfraction_inc); -% Minimum increment (in secs) in center poke duration every time there is a non-cp-violation trial: -cp_minimum_increment = 0.001; - -stage_no = value(SessionDefinition_CURRENT_ACTIVE_STAGE); -if stage_no ~= value(ParamsSection_training_stage) - ParamsSection_training_stage.value = stage_no; - callback(ParamsSection_training_stage); -end - -% Change the value of CP Duration -if value(PerformanceSummarySection_stage_4_Trials) < 2 - ParamsSection_CP_duration.value = cp_min; % initialize to min_CP -end - -if n_done_trials == 0 - ParamsSection_CP_duration.value = value(ParamsSection_init_CP_duration); -elseif n_done_trials == 1 - ParamsSection_CP_duration.value = value(TrainingStageParamsSection_last_session_CP); -else - if ~violation_history(end) && ~timeout_history(end) && value(ParamsSection_CP_duration) < cp_max - increment = value(ParamsSection_CP_duration) * cp_fraction; - if increment < cp_minimum_increment - increment = cp_minimum_increment; - end - ParamsSection_CP_duration.value = value(ParamsSection_CP_duration) + increment; - end -end -if value(ParamsSection_CP_duration) > cp_max - ParamsSection_CP_duration.value = cp_max; -end - -callback(ParamsSection_CP_duration); - -if n_done_trials > 0 - % Updating Disp Values for Training_Peformance_Summary - PerformanceSummarySection_stage_4_Trials.value = value(PerformanceSummarySection_stage_4_Trials) + 1; - PerformanceSummarySection_stage_4_TrialsToday.value = value(PerformanceSummarySection_stage_4_TrialsToday) + 1; - PerformanceSummarySection_stage_4_ViolationRate.value = ((value(PerformanceSummarySection_stage_4_ViolationRate) * (value(PerformanceSummarySection_stage_4_Trials) - 1)) + double(violation_history(end))) / value(PerformanceSummarySection_stage_4_Trials); - PerformanceSummarySection_stage_4_TimeoutRate.value = ((value(PerformanceSummarySection_stage_4_TimeoutRate) * (value(PerformanceSummarySection_stage_4_Trials) - 1)) + double(timeout_history(end))) / value(PerformanceSummarySection_stage_4_Trials); - if ~isnan(hit_history(end)) - PerformanceSummarySection_stage_4_TrialsValid.value = value(PerformanceSummarySection_stage_4_TrialsValid) + 1; - end - - callback(PerformanceSummarySection_stage_4_Trials); - callback(PerformanceSummarySection_stage_4_TrialsToday) - callback(PerformanceSummarySection_stage_4_ViolationRate); - callback(PerformanceSummarySection_stage_4_TimeoutRate); - callback(PerformanceSummarySection_stage_4_TrialsValid); - - % Updating Disp Values for Training_Peformance_Summary - SessionPerformanceSection_ntrials.value = n_done_trials; - SessionPerformanceSection_violation_rate.value = numel(find(violation_history))/n_done_trials; - SessionPerformanceSection_timeout_rate.value = numel(find(timeout_history))/n_done_trials; - if n_done_trials >= 20 - SessionPerformanceSection_violation_recent.value = numel(find(violation_history(end-19:end)))/20; - SessionPerformanceSection_timeout_recent.value = numel(find(timeout_history(end-19:end)))/20; - else - SessionPerformanceSection_timeout_recent.value = nan; - SessionPerformanceSection_violation_recent.value = nan; - end - SessionPerformanceSection_violation_stage.value = value(PerformanceSummarySection_stage_4_ViolationRate); - SessionPerformanceSection_ntrials_stage.value = value(PerformanceSummarySection_stage_4_Trials); - SessionPerformanceSection_ntrials_stage_today.value = value(PerformanceSummarySection_stage_4_TrialsToday); - SessionPerformanceSection_timeout_stage.value = value(PerformanceSummarySection_stage_4_TimeoutRate); - - callback(SessionPerformanceSection_ntrials); - callback(SessionPerformanceSection_ntrials_stage); - callback(SessionPerformanceSection_ntrials_stage_today) - callback(SessionPerformanceSection_violation_rate); - callback(SessionPerformanceSection_timeout_rate); - callback(SessionPerformanceSection_violation_recent); - callback(SessionPerformanceSection_timeout_recent); - callback(SessionPerformanceSection_violation_stage); - callback(SessionPerformanceSection_timeout_stage); -end -% -end -if completion_test_eval -GetSoloFunctionArgs(obj); -ClearHelperVarsNotOwned(obj); -clear('ans'); -% -if ParamsSection_use_auto_train % do completion check if auto training - cp_max = value(TrainingStageParamsSection_max_CP); - if value(ParamsSection_CP_duration) >= cp_max && n_done_trials > 100 && ... - value(SessionPerformanceSection_violation_recent) < value(TrainingStageParamsSection_recent_violation) && ... - value(SessionPerformanceSection_timeout_recent) < value(TrainingStageParamsSection_recent_timeout) && ... - value(SessionPerformanceSection_violation_stage) < value(TrainingStageParamsSection_stage_violation) - - ParamsSection_training_stage.value = 5; - callback(ParamsSection_training_stage); - ParamsSection(obj, 'Changed_Training_Stage'); - SessionDefinition(obj, 'jump_to_stage', 'Introduce Stimuli Sound during Centre Poke'); - TrainingStageParamsSection_last_session_CP.value = value(ParamsSection_CP_duration); - callback(TrainingStageParamsSection_last_session_CP); - end -end -% -if exist('ans', 'var') -varargout{1}=logical(ans); clear('ans'); -else -varargout{1}=false; -end -end -if eod_logic_eval -GetSoloFunctionArgs(obj); -ClearHelperVarsNotOwned(obj); -% -% Update the CP duration reached in this session -TrainingStageParamsSection_last_session_CP.value = value(ParamsSection_CP_duration); -callback(TrainingStageParamsSection_last_session_CP); -% Reset the number of trials done today for this stage -PerformanceSummarySection_stage_1_TrialsToday.value = 0; -callback(PerformanceSummarySection_stage_1_TrialsToday); -PerformanceSummarySection_stage_2_TrialsToday.value = 0; -callback(PerformanceSummarySection_stage_2_TrialsToday); -PerformanceSummarySection_stage_3_TrialsToday.value = 0; -callback(PerformanceSummarySection_stage_3_TrialsToday); -PerformanceSummarySection_stage_4_TrialsToday.value = 0; -callback(PerformanceSummarySection_stage_4_TrialsToday); -PerformanceSummarySection_stage_5_TrialsToday.value = 0; -callback(PerformanceSummarySection_stage_5_TrialsToday); -PerformanceSummarySection_stage_6_TrialsToday.value = 0; -callback(PerformanceSummarySection_stage_6_TrialsToday); -PerformanceSummarySection_stage_7_TrialsToday.value = 0; -callback(PerformanceSummarySection_stage_7_TrialsToday); -PerformanceSummarySection_stage_8_TrialsToday.value = 0; -callback(PerformanceSummarySection_stage_8_TrialsToday); -% -end -% - -%% Introduce Stimuli Sound during Centre Poke - -% -case 'Introduce Stimuli Sound during Centre Poke' -if helper_vars_eval -GetSoloFunctionArgs(obj); -ClearHelperVarsNotOwned(obj); -% - -% -end -if stage_algorithm_eval -GetSoloFunctionArgs(obj); -ClearHelperVarsNotOwned(obj); -% -cp_max = value(TrainingStageParamsSection_max_CP); -cp_min = value(TrainingStageParamsSection_min_CP); -% Fractional increment in center poke duration every time there is a non-cp-violation trial: -cp_fraction = value(TrainingStageParamsSection_CPfraction_inc); -% Minimum increment (in secs) in center poke duration every time there is a non-cp-violation trial: -cp_minimum_increment = 0.001; -% Starting total center poke duration: -starting_cp = value(TrainingStageParamsSection_starting_CP) + value(ParamsSection_SettlingIn_time); -% number of warm-up trials -n_trial_warmup = value(TrainingStageParamsSection_warm_up_trials); - -stage_no = value(SessionDefinition_CURRENT_ACTIVE_STAGE); -if stage_no ~= value(ParamsSection_training_stage) - ParamsSection_training_stage.value = stage_no; - callback(ParamsSection_training_stage); -end -% Change the value of CP Duration -% Since starting a new session then do a pre warm up to last saved cp -% duration else continue with learning with increased poke time -if n_done_trials == 0 - ParamsSection_CP_duration.value = value(ParamsSection_init_CP_duration); -elseif n_done_trials == 1 - ParamsSection_CP_duration.value = starting_cp; -else - if ~violation_history(end) && ~timeout_history(end) - if value(ParamsSection_CP_duration) < max([cp_min,value(TrainingStageParamsSection_last_session_CP)]) % warm up stage - increment = (max([cp_min,value(TrainingStageParamsSection_last_session_CP)]) - value(ParamsSection_CP_duration))/ (n_trial_warmup - 1); - else - if value(ParamsSection_CP_duration) >= value(TrainingStageParamsSection_last_session_CP) && value(ParamsSection_CP_duration) <= cp_max % no warm up stage - increment = value(ParamsSection_CP_duration)*cp_fraction; - if increment < cp_minimum_increment - increment = cp_minimum_increment; - end - end - end - - ParamsSection_CP_duration.value = value(ParamsSection_CP_duration) + increment; - - % Check if the values are within the required range - if value(ParamsSection_CP_duration) < starting_cp - ParamsSection_CP_duration.value = starting_cp; - end - if value(ParamsSection_CP_duration) > cp_max - ParamsSection_CP_duration.value = cp_max; - end - - end -end -callback(ParamsSection_CP_duration); - -if value(ParamsSection_CP_duration) >= 1 - ParamsSection_PreStim_time.value = 0.4; - if value(ParamsSection_CP_duration) < 2 - ParamsSection_A1_time.value = 0.1; - elseif value(ParamsSection_CP_duration) < 2.5 && value(ParamsSection_CP_duration) >= 2 - ParamsSection_A1_time.value = 0.2; - elseif value(ParamsSection_CP_duration) < 3 && value(ParamsSection_CP_duration) >= 2.5 - ParamsSection_A1_time.value = 0.3; - else - ParamsSection_A1_time.value = 0.4; - end -elseif value(ParamsSection_CP_duration) < 1 && value(ParamsSection_CP_duration) >= starting_cp - ParamsSection_SettlingIn_time.value = 0.2; - callback(ParamsSection_SettlingIn_time); - ParamsSection_PreStim_time.value = 0.1; - ParamsSection_A1_time.value = 0.1; -end - -if value(ParamsSection_CP_duration) >= starting_cp - ParamsSection_time_bet_aud1_gocue.value = value(ParamsSection_CP_duration) - value(ParamsSection_SettlingIn_time) - value(ParamsSection_A1_time) - value(ParamsSection_PreStim_time); - callback(ParamsSection_time_bet_aud1_gocue) - callback(ParamsSection_PreStim_time); - callback(ParamsSection_A1_time); -end - -if n_done_trials > 0 - % Updating Disp Values for Training_Peformance_Summary - PerformanceSummarySection_stage_5_Trials.value = value(PerformanceSummarySection_stage_5_Trials) + 1; - PerformanceSummarySection_stage_5_TrialsToday.value = value(PerformanceSummarySection_stage_5_TrialsToday) + 1; - PerformanceSummarySection_stage_5_ViolationRate.value = ((value(PerformanceSummarySection_stage_5_ViolationRate) * (value(PerformanceSummarySection_stage_5_Trials) - 1)) + double(violation_history(end))) / value(PerformanceSummarySection_stage_5_Trials); - PerformanceSummarySection_stage_5_TimeoutRate.value = ((value(PerformanceSummarySection_stage_5_TimeoutRate) * (value(PerformanceSummarySection_stage_5_Trials) - 1)) + double(timeout_history(end))) / value(PerformanceSummarySection_stage_5_Trials); - if ~isnan(hit_history(end)) - PerformanceSummarySection_stage_5_TrialsValid.value = value(PerformanceSummarySection_stage_5_TrialsValid) + 1; - end - - callback(PerformanceSummarySection_stage_5_Trials); - callback(PerformanceSummarySection_stage_5_TrialsToday) - callback(PerformanceSummarySection_stage_5_ViolationRate); - callback(PerformanceSummarySection_stage_5_TimeoutRate); - callback(PerformanceSummarySection_stage_5_TrialsValid); - - % Updating Disp Values for Training_Peformance_Summary - SessionPerformanceSection_ntrials.value = n_done_trials; - SessionPerformanceSection_violation_rate.value = numel(find(violation_history))/n_done_trials; - SessionPerformanceSection_timeout_rate.value = numel(find(timeout_history))/n_done_trials; - if n_done_trials >= 20 - SessionPerformanceSection_violation_recent.value = numel(find(violation_history(end-19:end)))/20; - SessionPerformanceSection_timeout_recent.value = numel(find(timeout_history(end-19:end)))/20; - else - SessionPerformanceSection_timeout_recent.value = nan; - SessionPerformanceSection_violation_recent.value = nan; - end - SessionPerformanceSection_violation_stage.value = value(PerformanceSummarySection_stage_5_ViolationRate); - SessionPerformanceSection_ntrials_stage.value = value(PerformanceSummarySection_stage_5_Trials); - SessionPerformanceSection_ntrials_stage_today.value = value(PerformanceSummarySection_stage_5_TrialsToday); - SessionPerformanceSection_timeout_stage.value = value(PerformanceSummarySection_stage_5_TimeoutRate); - - callback(SessionPerformanceSection_ntrials); - callback(SessionPerformanceSection_ntrials_stage); - callback(SessionPerformanceSection_ntrials_stage_today) - callback(SessionPerformanceSection_violation_rate); - callback(SessionPerformanceSection_timeout_rate); - callback(SessionPerformanceSection_violation_recent); - callback(SessionPerformanceSection_timeout_recent); - callback(SessionPerformanceSection_violation_stage); - callback(SessionPerformanceSection_timeout_stage); -end - -% -end -if completion_test_eval -GetSoloFunctionArgs(obj); -ClearHelperVarsNotOwned(obj); -clear('ans'); -% -if ParamsSection_use_auto_train % do completion check if auto training - stage_no = value(SessionDefinition_CURRENT_ACTIVE_STAGE); - if value(ParamsSection_CP_duration) >= value(TrainingStageParamsSection_max_CP) && value(PerformanceSummarySection_stage_5_TrialsValid) > value(TrainingStageParamsSection_total_trials) - if value(SessionPerformanceSection_violation_recent) < value(TrainingStageParamsSection_recent_violation) && value(SessionPerformanceSection_timeout_recent) < value(TrainingStageParamsSection_recent_timeout) && ... - value(SessionPerformanceSection_violation_stage) < value(TrainingStageParamsSection_stage_violation) && n_done_trials > 100 - ParamsSection_training_stage.value = stage_no + 1; - callback(ParamsSection_training_stage); - ParamsSection(obj, 'Changed_Training_Stage'); - SessionDefinition(obj, 'jump_to_stage', 'Vary Stimuli location during Centre Poke'); - TrainingStageParamsSection_last_session_CP.value = value(ParamsSection_CP_duration); - end - end -end -% -if exist('ans', 'var') -varargout{1}=logical(ans); clear('ans'); -else -varargout{1}=false; -end -end -if eod_logic_eval -GetSoloFunctionArgs(obj); -ClearHelperVarsNotOwned(obj); -% -% Update the CP duration reached in this session -TrainingStageParamsSection_last_session_CP.value = value(ParamsSection_CP_duration); -callback(TrainingStageParamsSection_last_session_CP); -% Reset the number of trials done today for this stage -PerformanceSummarySection_stage_1_TrialsToday.value = 0; -callback(PerformanceSummarySection_stage_1_TrialsToday); -PerformanceSummarySection_stage_2_TrialsToday.value = 0; -callback(PerformanceSummarySection_stage_2_TrialsToday); -PerformanceSummarySection_stage_3_TrialsToday.value = 0; -callback(PerformanceSummarySection_stage_3_TrialsToday); -PerformanceSummarySection_stage_4_TrialsToday.value = 0; -callback(PerformanceSummarySection_stage_4_TrialsToday); -PerformanceSummarySection_stage_5_TrialsToday.value = 0; -callback(PerformanceSummarySection_stage_5_TrialsToday); -PerformanceSummarySection_stage_6_TrialsToday.value = 0; -callback(PerformanceSummarySection_stage_6_TrialsToday); -PerformanceSummarySection_stage_7_TrialsToday.value = 0; -callback(PerformanceSummarySection_stage_7_TrialsToday); -PerformanceSummarySection_stage_8_TrialsToday.value = 0; -callback(PerformanceSummarySection_stage_8_TrialsToday); -% -end -% - -%% Vary Stimuli location during Centre Poke - -% -case 'Vary Stimuli location during Centre Poke' -if helper_vars_eval -GetSoloFunctionArgs(obj); -ClearHelperVarsNotOwned(obj); -% - -% -end -if stage_algorithm_eval -GetSoloFunctionArgs(obj); -ClearHelperVarsNotOwned(obj); -% -cp_max = value(TrainingStageParamsSection_max_CP); -% Starting total center poke duration: -starting_cp = value(TrainingStageParamsSection_starting_CP) + value(ParamsSection_SettlingIn_time); -% number of warm-up trials -n_trial_warmup = value(TrainingStageParamsSection_warm_up_trials); -prestim_min = value(TrainingStageParamsSection_min_prestim); -prestim_max = value(TrainingStageParamsSection_max_prestim); -stim_dur = value(TrainingStageParamsSection_stim_dur); - -stage_no = value(SessionDefinition_CURRENT_ACTIVE_STAGE); -if stage_no ~= value(ParamsSection_training_stage) - ParamsSection_training_stage.value = stage_no; - callback(ParamsSection_training_stage); -end - -% Warm Up If starting a new session -if n_done_trials == 0 - ParamsSection_CP_duration.value = value(ParamsSection_init_CP_duration); -elseif n_done_trials == 1 - ParamsSection_CP_duration.value = starting_cp; -else - if value(ParamsSection_CP_duration) < cp_max % warm up stage - if ~violation_history(end) && ~timeout_history(end) - increment = (cp_max - value(ParamsSection_CP_duration)) / (n_trial_warmup - 1); - ParamsSection_CP_duration.value = value(ParamsSection_CP_duration) + increment; - % Check if the values are within the required range - if value(ParamsSection_CP_duration) < starting_cp - ParamsSection_CP_duration.value = starting_cp; - end - if value(ParamsSection_CP_duration) > cp_max - ParamsSection_CP_duration.value = cp_max; - end - end - end -end - -callback(ParamsSection_CP_duration); - -if value(ParamsSection_CP_duration) < 3 && value(ParamsSection_CP_duration) >= starting_cp % during the warm up phase - ParamsSection_SettlingIn_time.value = 0.2; - callback(ParamsSection_SettlingIn_time); - ParamsSection_PreStim_time.value = 0.1; - ParamsSection_A1_time.value = 0.1; -else - ParamsSection_A1_time.value = stim_dur; % actual training stage - time_range_PreStim_time = prestim_min : 0.01 : prestim_max; - ParamsSection_PreStim_time.value = time_range_PreStim_time(randi([1, numel(time_range_PreStim_time)],1,1)); -end - -if value(ParamsSection_CP_duration) >= starting_cp - ParamsSection_time_bet_aud1_gocue.value = value(ParamsSection_CP_duration) - value(ParamsSection_SettlingIn_time) - value(ParamsSection_A1_time) - value(ParamsSection_PreStim_time); - callback(ParamsSection_time_bet_aud1_gocue) - callback(ParamsSection_PreStim_time); - callback(ParamsSection_A1_time); -end - -if n_done_trials > 0 - % Updating Disp Values for Training_Peformance_Summary - PerformanceSummarySection_stage_6_Trials.value = value(PerformanceSummarySection_stage_6_Trials) + 1; - PerformanceSummarySection_stage_6_TrialsToday.value = value(PerformanceSummarySection_stage_6_TrialsToday) + 1; - PerformanceSummarySection_stage_6_ViolationRate.value = ((value(PerformanceSummarySection_stage_6_ViolationRate) * (value(PerformanceSummarySection_stage_6_Trials) - 1)) + double(violation_history(end))) / value(PerformanceSummarySection_stage_6_Trials); - PerformanceSummarySection_stage_6_TimeoutRate.value = ((value(PerformanceSummarySection_stage_6_TimeoutRate) * (value(PerformanceSummarySection_stage_6_Trials) - 1)) + double(timeout_history(end))) / value(PerformanceSummarySection_stage_6_Trials); - if ~isnan(hit_history(end)) - PerformanceSummarySection_stage_6_TrialsValid.value = value(PerformanceSummarySection_stage_6_TrialsValid) + 1; - end - - callback(PerformanceSummarySection_stage_6_Trials); - callback(PerformanceSummarySection_stage_6_TrialsToday) - callback(PerformanceSummarySection_stage_6_ViolationRate); - callback(PerformanceSummarySection_stage_6_TimeoutRate); - callback(PerformanceSummarySection_stage_6_TrialsValid); - - % Updating Disp Values for SessionPerformanceSection - SessionPerformanceSection_ntrials.value = n_done_trials; - SessionPerformanceSection_violation_rate.value = numel(find(violation_history))/n_done_trials; - SessionPerformanceSection_timeout_rate.value = numel(find(timeout_history))/n_done_trials; - if n_done_trials >= 20 - SessionPerformanceSection_violation_recent.value = numel(find(violation_history(end-19:end)))/20; - SessionPerformanceSection_timeout_recent.value = numel(find(timeout_history(end-19:end)))/20; - else - SessionPerformanceSection_timeout_recent.value = nan; - SessionPerformanceSection_violation_recent.value = nan; - end - SessionPerformanceSection_violation_stage.value = value(PerformanceSummarySection_stage_6_ViolationRate); - SessionPerformanceSection_ntrials_stage.value = value(PerformanceSummarySection_stage_6_Trials); - SessionPerformanceSection_ntrials_stage_today.value = value(PerformanceSummarySection_stage_6_TrialsToday); - SessionPerformanceSection_timeout_stage.value = value(PerformanceSummarySection_stage_6_TimeoutRate); - - callback(SessionPerformanceSection_ntrials); - callback(SessionPerformanceSection_ntrials_stage); - callback(SessionPerformanceSection_ntrials_stage_today) - callback(SessionPerformanceSection_violation_rate); - callback(SessionPerformanceSection_timeout_rate); - callback(SessionPerformanceSection_violation_recent); - callback(SessionPerformanceSection_timeout_recent); - callback(SessionPerformanceSection_violation_stage); - callback(SessionPerformanceSection_timeout_stage); -end - -% -end -if completion_test_eval -GetSoloFunctionArgs(obj); -ClearHelperVarsNotOwned(obj); -clear('ans'); -% -if ParamsSection_use_auto_train % do completion check if auto training - stage_no = value(SessionDefinition_CURRENT_ACTIVE_STAGE); - if value(PerformanceSummarySection_stage_6_TrialsValid) > value(TrainingStageParamsSection_total_trials) - if value(SessionPerformanceSection_violation_recent) < value(TrainingStageParamsSection_recent_violation) && value(SessionPerformanceSection_timeout_recent) < value(TrainingStageParamsSection_recent_timeout) && ... - value(SessionPerformanceSection_violation_stage) < value(TrainingStageParamsSection_stage_violation) && n_done_trials > 100 - ParamsSection_training_stage.value = stage_no + 1; - callback(ParamsSection_training_stage); - ParamsSection(obj, 'Changed_Training_Stage'); - SessionDefinition(obj, 'jump_to_stage', 'Variable Stimuli Go Cue location during Centre Poke'); - end - end -end -% -if exist('ans', 'var') -varargout{1}=logical(ans); clear('ans'); -else -varargout{1}=false; -end -end -if eod_logic_eval -GetSoloFunctionArgs(obj); -ClearHelperVarsNotOwned(obj); -% -% Reset the number of trials done today for this stage -PerformanceSummarySection_stage_1_TrialsToday.value = 0; -callback(PerformanceSummarySection_stage_1_TrialsToday); -PerformanceSummarySection_stage_2_TrialsToday.value = 0; -callback(PerformanceSummarySection_stage_2_TrialsToday); -PerformanceSummarySection_stage_3_TrialsToday.value = 0; -callback(PerformanceSummarySection_stage_3_TrialsToday); -PerformanceSummarySection_stage_4_TrialsToday.value = 0; -callback(PerformanceSummarySection_stage_4_TrialsToday); -PerformanceSummarySection_stage_5_TrialsToday.value = 0; -callback(PerformanceSummarySection_stage_5_TrialsToday); -PerformanceSummarySection_stage_6_TrialsToday.value = 0; -callback(PerformanceSummarySection_stage_6_TrialsToday); -PerformanceSummarySection_stage_7_TrialsToday.value = 0; -callback(PerformanceSummarySection_stage_7_TrialsToday); -PerformanceSummarySection_stage_8_TrialsToday.value = 0; -callback(PerformanceSummarySection_stage_8_TrialsToday); -% -end -% - -%% Variable Stimuli Go Cue location during Centre Poke - -% -case 'Variable Stimuli Go Cue location during Centre Poke' -if helper_vars_eval -GetSoloFunctionArgs(obj); -ClearHelperVarsNotOwned(obj); -% - -% -end -if stage_algorithm_eval -GetSoloFunctionArgs(obj); -ClearHelperVarsNotOwned(obj); -% -% Variables for warmup stage -cp_max = value(TrainingStageParamsSection_max_CP); -% Starting total center poke duration: -starting_cp = value(TrainingStageParamsSection_starting_CP) + value(ParamsSection_SettlingIn_time); -% number of warm-up trials -n_trial_warmup = value(TrainingStageParamsSection_warm_up_trials); -prestim_min = value(TrainingStageParamsSection_min_prestim); -prestim_max = value(TrainingStageParamsSection_max_prestim); -prestim_time = value(TrainingStageParamsSection_min_prestim); -a1_time = value(TrainingStageParamsSection_stim_dur); -a1_time_min = value(TrainingStageParamsSection_stim_dur); -a1_time_max = value(TrainingStageParamsSection_stim_dur + 0.1); -prego_min = value(TrainingStageParamsSection_min_prego); -prego_max = value(TrainingStageParamsSection_max_prego); -prego_time = value(TrainingStageParamsSection_min_prego); -warmup_completed = 0; -warm_up_on = 1; -random_prestim = 1; -random_prego = 1; -random_A1 = 0; - -stage_no = value(SessionDefinition_CURRENT_ACTIVE_STAGE); -if stage_no ~= value(ParamsSection_training_stage) - ParamsSection_training_stage.value = stage_no; - callback(ParamsSection_training_stage); -end - -% Warm Up If starting a new session -if warm_up_on == 1 - if n_done_trials == 0 - ParamsSection_CP_duration.value = value(ParamsSection_init_CP_duration); - warmup_completed = 0; - elseif n_done_trials == 1 - ParamsSection_CP_duration.value = starting_cp; - else - if value(ParamsSection_CP_duration) <= cp_max % warm up stage - if ~violation_history(end) && ~timeout_history(end) - increment = (cp_max - value(ParamsSection_CP_duration))/ (n_trial_warmup - 1); - ParamsSection_CP_duration.value = value(ParamsSection_CP_duration) + increment; - % Check if the values are within the required range - if value(ParamsSection_CP_duration) < starting_cp - ParamsSection_CP_duration.value = starting_cp; - end - if value(ParamsSection_CP_duration) >= cp_max - ParamsSection_CP_duration.value = cp_max; - warmup_completed = 1; - end - end - end - end -else - warmup_completed = 1; -end - -if n_done_trials >= 1 - cp_length = value(ParamsSection_CP_duration) - value(ParamsSection_SettlingIn_time); - [ParamsSection_PreStim_time.value ,ParamsSection_A1_time.value,ParamsSection_time_bet_aud1_gocue.value] = param_time_within_range(warmup_completed,... - cp_length,prestim_min,prestim_max, random_prestim, prestim_time,... - a1_time_min,a1_time_max, random_A1, a1_time,prego_min,prego_max, random_prego, prego_time); - - callback(ParamsSection_PreStim_time); - callback(ParamsSection_A1_time); - callback(ParamsSection_time_bet_aud1_gocue); - ParamsSection_CP_duration.value = value(ParamsSection_SettlingIn_time) + value(ParamsSection_PreStim_time) + value(ParamsSection_A1_time) + value(ParamsSection_time_bet_aud1_gocue); -end -callback(ParamsSection_CP_duration); - -if n_done_trials > 0 - % Updating Disp Values for Training_Peformance_Summary - PerformanceSummarySection_stage_7_Trials.value = value(PerformanceSummarySection_stage_7_Trials) + 1; - PerformanceSummarySection_stage_7_TrialsToday.value = value(PerformanceSummarySection_stage_7_TrialsToday) + 1; - PerformanceSummarySection_stage_7_ViolationRate.value = ((value(PerformanceSummarySection_stage_7_ViolationRate) * (value(PerformanceSummarySection_stage_7_Trials) - 1)) + double(violation_history(end))) / value(PerformanceSummarySection_stage_7_Trials); - PerformanceSummarySection_stage_7_TimeoutRate.value = ((value(PerformanceSummarySection_stage_7_TimeoutRate) * (value(PerformanceSummarySection_stage_7_Trials) - 1)) + double(timeout_history(end))) / value(PerformanceSummarySection_stage_7_Trials); - if ~isnan(hit_history(end)) - PerformanceSummarySection_stage_7_TrialsValid.value = value(PerformanceSummarySection_stage_7_TrialsValid) + 1; - end - - callback(PerformanceSummarySection_stage_7_Trials); - callback(PerformanceSummarySection_stage_7_TrialsToday) - callback(PerformanceSummarySection_stage_7_ViolationRate); - callback(PerformanceSummarySection_stage_7_TimeoutRate); - callback(PerformanceSummarySection_stage_7_TrialsValid); - - % Updating Disp Values for SessionPerformanceSection - SessionPerformanceSection_ntrials.value = n_done_trials; - SessionPerformanceSection_violation_rate.value = numel(find(violation_history))/n_done_trials; - SessionPerformanceSection_timeout_rate.value = numel(find(timeout_history))/n_done_trials; - if n_done_trials >= 20 - SessionPerformanceSection_violation_recent.value = numel(find(violation_history(end-19:end)))/20; - SessionPerformanceSection_timeout_recent.value = numel(find(timeout_history(end-19:end)))/20; - else - SessionPerformanceSection_timeout_recent.value = nan; - SessionPerformanceSection_violation_recent.value = nan; - end - SessionPerformanceSection_violation_stage.value = value(PerformanceSummarySection_stage_7_ViolationRate); - SessionPerformanceSection_ntrials_stage.value = value(PerformanceSummarySection_stage_7_Trials); - SessionPerformanceSection_ntrials_stage_today.value = value(PerformanceSummarySection_stage_7_TrialsToday); - SessionPerformanceSection_timeout_stage.value = value(PerformanceSummarySection_stage_7_TimeoutRate); - - callback(SessionPerformanceSection_ntrials); - callback(SessionPerformanceSection_ntrials_stage); - callback(SessionPerformanceSection_ntrials_stage_today) - callback(SessionPerformanceSection_violation_rate); - callback(SessionPerformanceSection_timeout_rate); - callback(SessionPerformanceSection_violation_recent); - callback(SessionPerformanceSection_timeout_recent); - callback(SessionPerformanceSection_violation_stage); - callback(SessionPerformanceSection_timeout_stage); -end - -% -end -if completion_test_eval -GetSoloFunctionArgs(obj); -ClearHelperVarsNotOwned(obj); -clear('ans'); -% -stage_no = value(SessionDefinition_CURRENT_ACTIVE_STAGE); - -if value(PerformanceSummarySection_stage_7_TrialsValid) > value(TrainingStageParamsSection_total_trials) - if value(SessionPerformanceSection_violation_recent) < value(TrainingStageParamsSection_recent_violation) && value(SessionPerformanceSection_timeout_recent) < value(TrainingStageParamsSection_recent_timeout) && ... - value(SessionPerformanceSection_violation_stage) < value(TrainingStageParamsSection_stage_violation) - ParamsSection_training_stage.value = stage_no + 1; - callback(ParamsSection_training_stage); - ParamsSection(obj, 'Changed_Training_Stage'); - SessionDefinition(obj, 'jump_to_stage', 'User Setting'); - end -end -% -if exist('ans', 'var') -varargout{1}=logical(ans); clear('ans'); -else -varargout{1}=false; -end -end -if eod_logic_eval -GetSoloFunctionArgs(obj); -ClearHelperVarsNotOwned(obj); -% -PerformanceSummarySection_stage_1_TrialsToday.value = 0; -callback(PerformanceSummarySection_stage_1_TrialsToday); -PerformanceSummarySection_stage_2_TrialsToday.value = 0; -callback(PerformanceSummarySection_stage_2_TrialsToday); -PerformanceSummarySection_stage_3_TrialsToday.value = 0; -callback(PerformanceSummarySection_stage_3_TrialsToday); -PerformanceSummarySection_stage_4_TrialsToday.value = 0; -callback(PerformanceSummarySection_stage_4_TrialsToday); -PerformanceSummarySection_stage_5_TrialsToday.value = 0; -callback(PerformanceSummarySection_stage_5_TrialsToday); -PerformanceSummarySection_stage_6_TrialsToday.value = 0; -callback(PerformanceSummarySection_stage_6_TrialsToday); -PerformanceSummarySection_stage_7_TrialsToday.value = 0; -callback(PerformanceSummarySection_stage_7_TrialsToday); -PerformanceSummarySection_stage_8_TrialsToday.value = 0; -callback(PerformanceSummarySection_stage_8_TrialsToday); -% -end -% - -%% User Setting - -% -case 'User Setting' -if helper_vars_eval -GetSoloFunctionArgs(obj); -ClearHelperVarsNotOwned(obj); -% - -% -end -if stage_algorithm_eval -GetSoloFunctionArgs(obj); -ClearHelperVarsNotOwned(obj); -% -% Variables for warmup stage -cp_max = 5; -n_trial_warmup = 20; -starting_cp = 0.5; -warmup_completed = 0; -warm_up_on = value(ParamsSection_warmup_on); -random_prestim = value(ParamsSection_random_PreStim_time); -random_prego = value(ParamsSection_random_prego_time); -random_A1 = value(ParamsSection_random_A1_time); -prestim_min = value(ParamsSection_PreStim_time_Min); -prestim_max = value(ParamsSection_PreStim_time_Max); -prestim_time = value(ParamsSection_PreStim_time); -prego_min = value(ParamsSection_time_bet_aud1_gocue_Min); -prego_max = value(ParamsSection_time_bet_aud1_gocue_Max); -prego_time = value(ParamsSection_time_bet_aud1_gocue); -a1_time = value(ParamsSection_A1_time); -a1_time_min = value(ParamsSection_A1_time_Min); -a1_time_max = value(ParamsSection_A1_time_Max); - -stage_no = value(SessionDefinition_CURRENT_ACTIVE_STAGE); -if stage_no ~= value(ParamsSection_training_stage) - ParamsSection_training_stage.value = stage_no; - callback(ParamsSection_training_stage); -end - -% Warm Up If starting a new session -if warm_up_on == 1 - if n_done_trials == 0 - ParamsSection_CP_duration.value = value(ParamsSection_init_CP_duration); - warmup_completed = 0; - elseif n_done_trials == 1 - ParamsSection_CP_duration.value = starting_cp; - else - if value(ParamsSection_CP_duration) <= cp_max % warm up stage - if ~violation_history(end) && ~timeout_history(end) - increment = (cp_max - value(ParamsSection_CP_duration))/ (n_trial_warmup - 1); - ParamsSection_CP_duration.value = value(ParamsSection_CP_duration) + increment; - % Check if the values are within the required range - if value(ParamsSection_CP_duration) < starting_cp - ParamsSection_CP_duration.value = starting_cp; - end - if value(ParamsSection_CP_duration) >= cp_max - ParamsSection_CP_duration.value = cp_max; - warmup_completed = 1; - end - end - end - end -else - warmup_completed = 1; -end - -if n_done_trials >= 1 - cp_length = value(ParamsSection_CP_duration) - value(ParamsSection_SettlingIn_time); - [ParamsSection_PreStim_time.value ,ParamsSection_A1_time.value,ParamsSection_time_bet_aud1_gocue.value] = param_time_within_range(warmup_completed,... - cp_length,prestim_min,prestim_max, random_prestim, prestim_time,... - a1_time_min,a1_time_max, random_A1, a1_time,prego_min,prego_max, random_prego, prego_time); - - callback(ParamsSection_PreStim_time); - callback(ParamsSection_A1_time); - callback(ParamsSection_time_bet_aud1_gocue); - ParamsSection_CP_duration.value = value(ParamsSection_SettlingIn_time) + value(ParamsSection_PreStim_time) + value(ParamsSection_A1_time) + value(ParamsSection_time_bet_aud1_gocue); -end -callback(ParamsSection_CP_duration); - -if n_done_trials > 0 - % Updating Disp Values for Training_Peformance_Summary - PerformanceSummarySection_stage_8_Trials.value = value(PerformanceSummarySection_stage_8_Trials) + 1; - PerformanceSummarySection_stage_8_TrialsToday.value = value(PerformanceSummarySection_stage_8_TrialsToday) + 1; - PerformanceSummarySection_stage_8_ViolationRate.value = ((value(PerformanceSummarySection_stage_8_ViolationRate) * (value(PerformanceSummarySection_stage_8_Trials) - 1)) + double(violation_history(end))) / value(PerformanceSummarySection_stage_8_Trials); - PerformanceSummarySection_stage_8_TimeoutRate.value = ((value(PerformanceSummarySection_stage_8_TimeoutRate) * (value(PerformanceSummarySection_stage_8_Trials) - 1)) + double(timeout_history(end))) / value(PerformanceSummarySection_stage_8_Trials); - if ~isnan(hit_history(end)) - PerformanceSummarySection_stage_8_TrialsValid.value = value(PerformanceSummarySection_stage_8_TrialsValid) + 1; - end - - callback(PerformanceSummarySection_stage_8_Trials); - callback(PerformanceSummarySection_stage_8_TrialsToday) - callback(PerformanceSummarySection_stage_8_ViolationRate); - callback(PerformanceSummarySection_stage_8_TimeoutRate); - callback(PerformanceSummarySection_stage_8_TrialsValid); - - % Updating Disp Values for SessionPerformanceSection - SessionPerformanceSection_ntrials.value = n_done_trials; - SessionPerformanceSection_violation_rate.value = numel(find(violation_history))/n_done_trials; - SessionPerformanceSection_timeout_rate.value = numel(find(timeout_history))/n_done_trials; - if n_done_trials >= 20 - SessionPerformanceSection_violation_recent.value = numel(find(violation_history(end-19:end)))/20; - SessionPerformanceSection_timeout_recent.value = numel(find(timeout_history(end-19:end)))/20; - else - SessionPerformanceSection_timeout_recent.value = nan; - SessionPerformanceSection_violation_recent.value = nan; - end - SessionPerformanceSection_violation_stage.value = value(PerformanceSummarySection_stage_8_ViolationRate); - SessionPerformanceSection_ntrials_stage.value = value(PerformanceSummarySection_stage_8_Trials); - SessionPerformanceSection_ntrials_stage_today.value = value(PerformanceSummarySection_stage_8_TrialsToday); - SessionPerformanceSection_timeout_stage.value = value(PerformanceSummarySection_stage_8_TimeoutRate); - - callback(SessionPerformanceSection_ntrials); - callback(SessionPerformanceSection_ntrials_stage); - callback(SessionPerformanceSection_ntrials_stage_today) - callback(SessionPerformanceSection_violation_rate); - callback(SessionPerformanceSection_timeout_rate); - callback(SessionPerformanceSection_violation_recent); - callback(SessionPerformanceSection_timeout_recent); - callback(SessionPerformanceSection_violation_stage); - callback(SessionPerformanceSection_timeout_stage); -end - -% -end -if completion_test_eval -GetSoloFunctionArgs(obj); -ClearHelperVarsNotOwned(obj); -clear('ans'); -% - -% -if exist('ans', 'var') -varargout{1}=logical(ans); clear('ans'); -else -varargout{1}=false; -end -end -if eod_logic_eval -GetSoloFunctionArgs(obj); -ClearHelperVarsNotOwned(obj); -% -PerformanceSummarySection_stage_1_TrialsToday.value = 0; -callback(PerformanceSummarySection_stage_1_TrialsToday); -PerformanceSummarySection_stage_2_TrialsToday.value = 0; -callback(PerformanceSummarySection_stage_2_TrialsToday); -PerformanceSummarySection_stage_3_TrialsToday.value = 0; -callback(PerformanceSummarySection_stage_3_TrialsToday); -PerformanceSummarySection_stage_4_TrialsToday.value = 0; -callback(PerformanceSummarySection_stage_4_TrialsToday); -PerformanceSummarySection_stage_5_TrialsToday.value = 0; -callback(PerformanceSummarySection_stage_5_TrialsToday); -PerformanceSummarySection_stage_6_TrialsToday.value = 0; -callback(PerformanceSummarySection_stage_6_TrialsToday); -PerformanceSummarySection_stage_7_TrialsToday.value = 0; -callback(PerformanceSummarySection_stage_7_TrialsToday); -PerformanceSummarySection_stage_8_TrialsToday.value = 0; -callback(PerformanceSummarySection_stage_8_TrialsToday); -% -end -% - - -end - -end - -%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - -% - -function [prestim,A1,prego] = param_time_within_range(fixed_length,cp_length,range_min_prestim,range_max_prestim, is_random_prestim, provided_time_prestim,... - range_min_A1,range_max_A1, is_random_A1, provided_time_A1,range_min_prego,range_max_prego, is_random_prego, provided_time_prego) - -if fixed_length == 1 % warm up stage where cp length is increasing -% then calculate the range/typical value - if cp_length <= 0.3 - prestim = 0.1; - A1 = 0.1; - prego = 0.1; - else - range_size = round(0.3 * cp_length,1); - if range_size > 0.4 - step_size = 0.1; - else - step_size = 0.01; - end - - timerange = 0.1:step_size:range_size; - - if is_random_prestim == 1 - prestim = timerange(randi([1, numel(timerange)],1,1)); - else - if provided_time_prestim <= range_size - prestim = provided_time_prestim; - else - prestim = range_size; - end - - end - - if is_random_A1 == 1 - A1 = timerange(randi([1, numel(timerange)],1,1)); - else - if provided_time_A1 <= range_size - A1 = provided_time_A1; - else - A1 = range_size; - end - end - - prego = cp_length - prestim - A1; - - end - -else - - if is_random_prestim == 1 - range_size_prestim = range_max_prestim - range_min_prestim; - if range_size_prestim > 0.4 - step_size_prestim = 0.1; - else - step_size_prestim = 0.01; - end - time_range_prestim = range_min_prestim:step_size_prestim:range_max_prestim; - prestim = time_range_prestim(randi([1, numel(time_range_prestim)],1,1)); - else - prestim = provided_time_prestim; - end - - if is_random_A1 == 1 - range_size_A1 = range_max_A1 - range_min_A1; - if range_size_A1 > 0.4 - step_size_A1 = 0.1; - else - step_size_A1 = 0.01; - end - time_range_A1 = range_min_A1:step_size_A1:range_max_A1; - A1 = time_range_A1(randi([1, numel(time_range_A1)],1,1)); - else - A1 = provided_time_A1; - end - - if is_random_prego == 1 - range_size_prego = range_max_prego - range_min_prego; - if range_size_prego > 0.4 - step_size_prego = 0.1; - else - step_size_prego = 0.01; - end - time_range_prego = range_min_prego:step_size_prego:range_max_prego; - prego = time_range_prego(randi([1, numel(time_range_prego)],1,1)); - else - prego = provided_time_prego; - end - -end -end - - -% - -%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% From 11eb87d0f6a536d113d91ee715e6e2f34b213eb6 Mon Sep 17 00:00:00 2001 From: Arpit Date: Thu, 22 May 2025 18:26:44 +0100 Subject: [PATCH 115/164] error debug --- ...ing_SessionDefinition_AutoTrainingStages.m | 5 +- .../StimulusSection.m | 16 ++-- ...te_ArpitCentrePokeTraining_SettingFile.asv | 92 ------------------- 3 files changed, 11 insertions(+), 102 deletions(-) delete mode 100644 Protocols/@ArpitCentrePokeTraining/private/create_ArpitCentrePokeTraining_SettingFile.asv diff --git a/Protocols/@ArpitCentrePokeTraining/ArpitCentrePokeTraining_SessionDefinition_AutoTrainingStages.m b/Protocols/@ArpitCentrePokeTraining/ArpitCentrePokeTraining_SessionDefinition_AutoTrainingStages.m index 7857d07d..88aad042 100644 --- a/Protocols/@ArpitCentrePokeTraining/ArpitCentrePokeTraining_SessionDefinition_AutoTrainingStages.m +++ b/Protocols/@ArpitCentrePokeTraining/ArpitCentrePokeTraining_SessionDefinition_AutoTrainingStages.m @@ -661,7 +661,6 @@ starting_cp = value(TrainingStageParamsSection_starting_CP) + value(ParamsSection_SettlingIn_time); n_trial_warmup = value(TrainingStageParamsSection_warm_up_trials); cp_range = cp_max - cp_min; - a1_min = 0.1; a1_max = 0.4; @@ -679,7 +678,7 @@ if n_done_trials == 0 new_CP = init_CP_duration; -elseif n_done_trials <= n_trial_warmup && n_done_trials > 0 +elseif n_done_trials <= n_trial_warmup cp_delta = (last_CP - init_CP_duration) / n_trial_warmup; new_CP = init_CP_duration + cp_delta * n_done_trials; if new_CP < starting_cp @@ -692,6 +691,8 @@ increment = cp_minimum_increment; end new_CP = curr_CP + increment; + else + new_CP = curr_CP; end end ParamsSection_CP_duration.value = min(new_CP, cp_max); diff --git a/Protocols/@ArpitCentrePokeTraining/StimulusSection.m b/Protocols/@ArpitCentrePokeTraining/StimulusSection.m index 8dd8a940..125aaaad 100644 --- a/Protocols/@ArpitCentrePokeTraining/StimulusSection.m +++ b/Protocols/@ArpitCentrePokeTraining/StimulusSection.m @@ -112,7 +112,9 @@ next_row(y); NumeditParam(obj,'maxF1',10,x,y,'label','maxF1','TooltipString','max frequency value for AUD1'); set_callback(maxF1, {mfilename, 'Cal_Boundary'}); - next_row(y); + next_row(y); + NumeditParam(obj,'volumeF1',0.007,x,y,'label','VolumeF1','TooltipString','volume of tone for AUD1'); + next_row(y); DispParam(obj, 'A1_freq', 0.01, x,y,'label','A1_freq','TooltipString','Sigma value for the first stimulus'); next_row(y); DispParam(obj,'boundary',-3.9,x,y,'label','boundary(log)','TooltipString','decision boundary for categorisation (log)'); @@ -158,7 +160,7 @@ dur1 = A1_time*1000; bal=0; freq1=A1_freq*1000; - vol=0.001; + vol=value(volumeF1); RVol=vol*min(1,(1+bal)); LVol=vol*min(1,(1-bal)); t=0:(1/srate):(dur1/1000); @@ -515,19 +517,17 @@ %% Case frequency ON case 'FrequencyCategorization' if frequency_categorization == 1 - make_visible(maxF1);make_visible(minF1);make_visible(A1_freq); + make_visible(maxF1);make_visible(minF1);make_visible(A1_freq);make_visible(volumeF1); make_invisible(maxS1);make_invisible(minS1);make_invisible(A1_sigma); - make_invisible(fcut);make_invisible(lfreq);make_invisible(hfreq); make_invisible(filter_type); - StimulusSection(obj,'plot_stimuli'); + make_invisible(fcut);make_invisible(lfreq);make_invisible(hfreq); make_invisible(filter_type); else make_visible(maxS1);make_visible(minS1);make_visible(A1_sigma); make_visible(fcut);make_visible(lfreq);make_visible(hfreq); make_visible(filter_type); - make_invisible(maxF1);make_invisible(minF1);make_invisible(A1_freq); - StimulusSection(obj,'plot_stimuli'); + make_invisible(maxF1);make_invisible(minF1);make_invisible(A1_freq); make_visible(volumeF1); end StimulusSection(obj,'Cal_Boundary'); % update the boundary - + StimulusSection(obj,'plot_stimuli'); %% Case get_stimuli % case 'get_stimuli' diff --git a/Protocols/@ArpitCentrePokeTraining/private/create_ArpitCentrePokeTraining_SettingFile.asv b/Protocols/@ArpitCentrePokeTraining/private/create_ArpitCentrePokeTraining_SettingFile.asv deleted file mode 100644 index 4ce0e27c..00000000 --- a/Protocols/@ArpitCentrePokeTraining/private/create_ArpitCentrePokeTraining_SettingFile.asv +++ /dev/null @@ -1,92 +0,0 @@ -function create_ArpitCentrePokeTraining_SettingFile() - -templateDir = 'C:/ratter/'; -templateDir = '/Users/AkramiLab_Arpit/Documents/AkramiLabScripts/ratter'; - -templatePath = [templateDir '/Protocols/@ArpitCentrePokeTraining/private/pipeline_ArpitCentrePokeTraining_arpit_AR05_250516.m']; -setting_filename = 'settings_@ArpitCentrePokeTraining_arpit_AR05_250516a.mat'; -outputRootDir = [templateDir '/SoloData/Settings/']; - -experimenter = input('Enter experimenter name: ', 's'); -ratname = input('Enter rat name: ', 's'); - -% Get today's date -newDate = datestr(now, 'yymmdd'); - -% Read the template file -if ~isfile(templatePath) - error('Template file not found: %s', templatePath); -end -code = fileread(templatePath); -lines = splitlines(code); - -% Find and update the function definition line -newFuncName = ''; -for i = 1:length(lines) - line = strtrim(lines{i}); - if startsWith(line, 'function') && contains(line, 'pipeline_') - % Extract argument list - pattern = 'function\s+varargout\s*=\s*pipeline_.*?\((.*?)\)'; - tokens = regexp(line, pattern, 'tokens'); - if ~isempty(tokens) - args = tokens{1}{1}; - newFuncName = sprintf('pipeline_ArpitCentrePokeTraining_%s_%s_%s', ... - experimenter, ratname, newDate); - lines{i} = sprintf('function varargout = %s(%s)', newFuncName, args); - break; - end - end -end - -if isempty(newFuncName) - error('Could not find valid function definition line in template.'); -end - -% Create output directory if needed -outputDir = fullfile(outputRootDir, experimenter, ratname); -if ~exist(outputDir, 'dir') - mkdir(outputDir); -end - -% Build full new script path -newScriptPath = fullfile(outputDir, [newFuncName, '.m']); - -% Save modified content -newCode = strjoin(lines, newline); -fid = fopen(newScriptPath, 'w'); -if fid == -1 - error('Failed to open new file for writing: %s', newScriptPath); -end -fwrite(fid, newCode); -fclose(fid); - -fprintf('New Session Definition file saved to: %s\n', newScriptPath); - -% Load and modify .mat file -[templateFolder, ~, ~] = fileparts(templatePath); -matTemplatePath = fullfile(templateFolder, setting_filename); - -if ~isfile(matTemplatePath) - warning('No setting_file found in template directory: %s', matTemplatePath); - newMatPath = ''; - return; -end - -load(matTemplatePath,'saved','saved_autoset','fig_position'); % Load struct - -saved.SavingSection_experimenter = experimenter; -saved.SavingSection_ratname = ratname; -saved.SessionDefinition_textTrainingStageFile = newScriptPath; -saved.ArpitCentrePokeTraining_prot_title = sprintf('ArpitCentrePokeTraining: %s, %s',experimenter,ratname); -saved.PokesPlotSection_textHeader = sprintf('PokesPlotSection(%s, %s',experimenter,ratname); -saved.SessionDefinition_textHeader = sprintf('SESSION AUTOMATOR WINDOW: %s, %s',experimenter,ratname); - -new_MATfile = sprintf('settings_@ArpitCentrePokeTraining__%s_%s_%sa.mat', ... - experimenter, ratname, newDate); - -newMatPath = fullfile(outputDir, new_MATfile); - -save(newMatPath,'saved','saved_autoset','fig_position'); -fprintf('New setting file saved to: %s\n', newMatPath); - -end From 1d15d06b4b0d501b63aa31f8b568c724391fc510 Mon Sep 17 00:00:00 2001 From: Arpit Date: Fri, 23 May 2025 23:58:07 +0100 Subject: [PATCH 116/164] error debug --- ...ing_SessionDefinition_AutoTrainingStages.m | 4 +- .../StimulusSection.m | 18 +- .../ArpitSoundCatContinuous.m | 11 +- .../PerformanceSection.m | 127 +++++++ .../StimulatorSection.asv | 325 ------------------ 5 files changed, 142 insertions(+), 343 deletions(-) create mode 100644 Protocols/@ArpitSoundCatContinuous/PerformanceSection.m delete mode 100644 Protocols/@ArpitSoundCatContinuous/StimulatorSection.asv diff --git a/Protocols/@ArpitCentrePokeTraining/ArpitCentrePokeTraining_SessionDefinition_AutoTrainingStages.m b/Protocols/@ArpitCentrePokeTraining/ArpitCentrePokeTraining_SessionDefinition_AutoTrainingStages.m index 88aad042..ea43e957 100644 --- a/Protocols/@ArpitCentrePokeTraining/ArpitCentrePokeTraining_SessionDefinition_AutoTrainingStages.m +++ b/Protocols/@ArpitCentrePokeTraining/ArpitCentrePokeTraining_SessionDefinition_AutoTrainingStages.m @@ -49,7 +49,7 @@ % Update TrainingStageParamsSection if n_done_trials >= 2 - if previous_sides(end) ~= previous_sides(end-1) && all(hit_history(end-1:end)) % last and present trials should also be a valid trial + if previous_sides(end) ~= previous_sides(end-1) % last and present trials should also be a valid trial TrainingStageParamsSection_trial_oppSide.value = value(TrainingStageParamsSection_trial_oppSide) + 1; % updating value for variable in TrainingParams_Section callback(TrainingStageParamsSection_trial_oppSide); end @@ -217,7 +217,7 @@ % Update TrainingStageParamsSection if n_done_trials >= 2 - if previous_sides(end) ~= previous_sides(end-1) && all(hit_history(end-1:end)) % last and present trials should also be a valid trial + if previous_sides(end) ~= previous_sides(end-1) && all(~isnan(hit_history(end-1:end)))% last and present trials should also be a valid trial TrainingStageParamsSection_trial_oppSide.value = value(TrainingStageParamsSection_trial_oppSide) + 1; % updating value for variable in TrainingParams_Section callback(TrainingStageParamsSection_trial_oppSide); end diff --git a/Protocols/@ArpitCentrePokeTraining/StimulusSection.m b/Protocols/@ArpitCentrePokeTraining/StimulusSection.m index 125aaaad..6ac08a0d 100644 --- a/Protocols/@ArpitCentrePokeTraining/StimulusSection.m +++ b/Protocols/@ArpitCentrePokeTraining/StimulusSection.m @@ -128,7 +128,7 @@ 'OffString', 'Amplitude(Noise)',... 'TooltipString', sprintf('If on (black) then it enables the presentation of pure tones')); set_callback(frequency_categorization, {mfilename, 'FrequencyCategorization'}); - make_invisible(maxF1);make_invisible(minF1);make_invisible(A1_freq); + make_invisible(maxF1);make_invisible(minF1);make_invisible(A1_freq);make_invisible(volumeF1); next_row(y); % next_column(y) @@ -156,7 +156,7 @@ if frequency_categorization % produce the tone A1_freq.value = value(thisstim); - A1 = value(thisstimlog(n_completed_trials+1)); + A1 = value(thisstimlog(n_done_trials+1)); dur1 = A1_time*1000; bal=0; freq1=A1_freq*1000; @@ -172,7 +172,7 @@ else % produce noise pattern A1_sigma.value = value(thisstim); - A1 = value(thisstimlog(n_completed_trials+1)); + A1 = value(thisstimlog(n_done_trials+1)); [rawA1, rawA2, normA1, normA2]=noisestim(1,1,T,value(fcut),Fs,value(filter_type)); modulator=singlenoise(1,T,[value(lfreq) value(hfreq)],Fs,'BUTTER'); AUD1=normA1(1:A1_time*srate).*modulator(1:A1_time*srate).*A1_sigma; @@ -186,14 +186,14 @@ % Plot current stimulus and move to saving stimulus history - % if value(thisstimlog(n_completed_trials+1)) > value(boundary)%value(numClass) + % if value(thisstimlog(n_done_trials+1)) > value(boundary)%value(numClass) % set(value(h1), 'YData', value(A1), 'color',[0.4 0.8 0.1],'markerfacecolor',[0.4 0.8 0.1]); % else % set(value(h1), 'YData', value(A1), 'color',[0.8 0.4 0.1],'markerfacecolor',[0.8 0.4 0.1]); % end - if n_completed_trials > 0 - if ~violation_history(n_completed_trials) && ~timeout_history(n_completed_trials) + if n_done_trials > 0 + if ~violation_history(n_done_trials) && ~timeout_history(n_done_trials) StimulusSection(obj,'update_stimulus_history'); else StimulusSection(obj,'update_stimulus_history_nan'); @@ -312,7 +312,7 @@ end thisstim.value=exp(stim_i_log); - thisstimlog(n_completed_trials+1) = stim_i_log; + thisstimlog(n_done_trials+1) = stim_i_log; %% Case plot stimuli distribution case 'plot_stimuli' @@ -553,12 +553,12 @@ case 'update_stimulus_history' ps=value(stimulus_history); - ps(n_completed_trials)=value(thisstimlog(n_completed_trials)); + ps(n_done_trials)=value(thisstimlog(n_done_trials)); stimulus_history.value=ps; case 'update_stimulus_history_nan' ps=value(stimulus_history); - ps(n_completed_trials)=value(thisstimlog(n_completed_trials));%nan; + ps(n_done_trials)=value(thisstimlog(n_done_trials));%nan; stimulus_history.value=ps; %% Case hide diff --git a/Protocols/@ArpitSoundCatContinuous/ArpitSoundCatContinuous.m b/Protocols/@ArpitSoundCatContinuous/ArpitSoundCatContinuous.m index 1523d8d2..c3c75873 100644 --- a/Protocols/@ArpitSoundCatContinuous/ArpitSoundCatContinuous.m +++ b/Protocols/@ArpitSoundCatContinuous/ArpitSoundCatContinuous.m @@ -97,7 +97,7 @@ SessionDefinition(obj, 'init', x, y, value(myfig)); next_row(y, 2); %#ok next_column(x); y=5; - [x, y] = OverallPerformanceSection(obj, 'init', x, y); + [x, y] = PerformanceSection(obj, 'init', x, y); [x, y] = StimulatorSection(obj, 'init', x, y); next_row(y, 1.3); [x, y] = SideSection(obj, 'init', x, y); %#ok [x, y] = SoundSection(obj,'init',x,y); @@ -119,13 +119,12 @@ % trial was a violation or not SessionDefinition(obj, 'next_trial'); StimulatorSection(obj, 'update_values'); - OverallPerformanceSection(obj, 'evaluate'); + PerformanceSection(obj, 'evaluate'); StimulusSection(obj,'prepare_next_trial'); SoundManagerSection(obj, 'send_not_yet_uploaded_sounds'); - nTrials.value = n_done_trials; - [sma, prepare_next_trial_states] = SoundCatSMA(obj, 'prepare_next_trial'); + sma = add_trialnum_indicator(sma, n_done_trials); % Default behavior of following call is that every 20 trials, the data % gets saved, not interactive, no commit to CVS. @@ -141,8 +140,6 @@ prot_title.value=[mfilename ' on rig ' get_hostname ' : ' expmtr ', ' rname '. Started at ' datestr(now, 'HH:MM')]; end - try send_n_done_trials(obj); end - %% trial_completed case 'trial_completed' @@ -178,7 +175,7 @@ StimulusSection(obj,'hide'); SessionDefinition(obj, 'run_eod_logic_without_saving'); - perf = OverallPerformanceSection(obj, 'evaluate'); + perf = PerformanceSection(obj, 'evaluate'); cp_durs = SideSection(obj, 'get_cp_history'); [stim1dur] = SideSection(obj,'get_stimdur_history'); diff --git a/Protocols/@ArpitSoundCatContinuous/PerformanceSection.m b/Protocols/@ArpitSoundCatContinuous/PerformanceSection.m new file mode 100644 index 00000000..5b9a0084 --- /dev/null +++ b/Protocols/@ArpitSoundCatContinuous/PerformanceSection.m @@ -0,0 +1,127 @@ +% [x, y] = PerformanceSection(obj, action, x,y) +% +% Reports overall performance. Uses training_stage from SideSection. +% +% PARAMETERS: +% ----------- +% +% action One of: +% 'init' To initialise the section and set up the GUI +% for it +% +% 'close' Delete all of this section's GUIs and data +% +% 'reinit' Delete all of this section's GUIs and data, +% and reinit, at the same position on the same +% figure as the original section GUI was placed. +% +% 'evalueta' Look at history and compute hit fraction, etc. +% +% x, y Only relevant to action = 'init'; they indicate the initial +% position to place the GUI at, in the current figure window +% +% RETURNS: +% -------- +% +% perf When action == 'init', returns x and y, pixel positions on +% the current figure, updated after placing of this section's GUI. +% When action == 'evaluate', returns a vector with elements +% [ntrials, violation_percent, left_hit_frac, right_hit_frac, hit_frac] +% +% + +% CDB, 23-March-2013 + +function [x, y] = PerformanceSection(obj, action, x,y) + +GetSoloFunctionArgs(obj); + +switch action + + % ------------------------------------------------------------------ + % INIT + % ------------------------------------------------------------------ + + case 'init' + + SoloParamHandle(obj, 'my_gui_info', 'value', [x y double(gcf)], 'saveable', 0); + + DispParam(obj, 'ntrials', 0, x, y); next_row(y); + DispParam(obj, 'ntrials_valid', 0, x, y); next_row(y); + DispParam(obj, 'violation_percent', 0, x, y, 'TooltipString', ... + 'Fraction of trials with a center poke violation'); next_row(y); + DispParam(obj, 'timeout_percent', 0, x, y, 'TooltipString', ... + 'Fraction of trials with timeout'); next_row(y); + DispParam(obj, 'Left_hit_frac', 0, x, y, 'TooltipString', ... + 'Fraction of correct Left trials'); next_row(y); + DispParam(obj, 'Right_hit_frac', 0, x, y, 'TooltipString', ... + 'Fraction of correct Right trials'); next_row(y); + DispParam(obj, 'hit_frac', 0, x, y, 'TooltipString', ... + 'Fraction of correct trials'); next_row(y); + + SubheaderParam(obj, 'title', 'Overall Performance', x, y); + next_row(y, 1.5); + SoloParamHandle(obj, 'previous_parameters', 'value', []); + + % ------------------------------------------------------------------ + % evaluate + % ------------------------------------------------------------------ + + case 'evaluate' + + + if n_done_trials > 1 + + ntrials.value = n_done_trials; + n_trials_valid.value = numel(find(~isnan(hit_history))); + violation_percent.value = numel(find(violation_history))/n_done_trials; + timeout_percent.value = numel(find(timeout_history))/n_done_trials; + goods = ~isnan(hit_history)'; + lefts = previous_sides(1:n_done_trials)=='l'; + rights = previous_sides(1:n_done_trials)=='r'; + Left_hit_frac.value = mean(hit_history(goods & lefts)); + Right_hit_frac.value = mean(hit_history(goods & rights)); + hit_frac.value = mean(hit_history(goods)); + end + + if nargout > 0 + x = [n_done_trials, value(violation_percent), value(timeout_percent), value(Left_hit_frac), ... + value(Right_hit_frac), value(hit_frac)]; + end + + + % ------------------------------------------------------------------ + % close + % ------------------------------------------------------------------ + + case 'close' + % Delete all SoloParamHandles who belong to this object and whose + % fullname starts with the name of this mfile: + delete_sphandle('owner', ['^@' class(obj) '$'], ... + 'fullname', ['^' mfilename]); + + + % ------------------------------------------------------------------ + % reinit + % ------------------------------------------------------------------ + + case 'reinit' + currfig = double(gcf); + + % Get the original GUI position and figure: + x = my_gui_info(1); y = my_gui_info(2); figure(my_gui_info(3)); + + % Delete all SoloParamHandles who belong to this object and whose + % fullname starts with the name of this mfile: + delete_sphandle('owner', ['^@' class(obj) '$'], ... + 'fullname', ['^' mfilename]); + + % Reinitialise at the original GUI position and figure: + [x, y] = feval(mfilename, obj, 'init', x, y); + + % Restore the current figure: + figure(currfig); + +end + + diff --git a/Protocols/@ArpitSoundCatContinuous/StimulatorSection.asv b/Protocols/@ArpitSoundCatContinuous/StimulatorSection.asv deleted file mode 100644 index 051237fe..00000000 --- a/Protocols/@ArpitSoundCatContinuous/StimulatorSection.asv +++ /dev/null @@ -1,325 +0,0 @@ -% -% PARAMETERS: -% ----------- -% -% obj Default object argument. -% -% action One of: -% -% 'prepare_next_trial' -% -% 'init' -% -% -% RETURNS: -% -------- -% -% -% -% -% -% - - - -function [varargout] = StimulatorSection(obj, action, x, y) - -GetSoloFunctionArgs(obj); - -switch action - -%% init -% ---------------------------------------------------------------- -% -% INIT -% -% ---------------------------------------------------------------- - - case 'init', - %NumeditParam(obj,'LCB_onstim', 0,x,y,'position',[x y 100 20],'labelfraction',0.6); - %NumeditParam(obj,'LCB_nostim', 0,x,y,'position',[x+100 y 100 20],'labelfraction',0.6); next_row(y); - %SoloParamHandle(obj,'LegalCBrk_temp', 'value',0); - - MenuParam(obj,'StimInterval',{'WholeTrial','S1','DelayDur','GoCue'},1,x,y,'labelfraction',0.30); next_row(y); - set_callback(StimInterval, {mfilename, 'StimInterval'}); - MenuParam(obj,'StimOnSide',{'both','left','right'},1,x,y,'labelfraction',0.3); next_row(y); - - diolines = bSettings('get','DIOLINES', 'all'); - for i = 1:size(diolines,1); dionames{i} = diolines{i,1}; dionums(i) = diolines{i,2}; end %#ok - [dionums order] = sort(dionums); - dionames2 = cell(0); - for i = 1:length(dionums); if ~isnan(dionums(i)); dionames2{end+1} = dionames{order(i)}; end; end %#ok - dionames2 = cell(0); - dionames2{1} = 'opto'; - - MenuParam(obj,'StimLine',dionames2,1,x,y,'labelfraction',0.30); next_row(y); - - SC = state_colors(obj); - WC = wave_colors(obj); - states = fieldnames(SC); - waves = fieldnames(WC); - states(2:end+1) = states; - states{1} = 'cp'; - states(end+1:end+length(waves)) = waves; - - MenuParam(obj,'StimState',states,1,x,y,'labelfraction',0.30); next_row(y); - - NumeditParam(obj,'StimStates', 1,x,y,'position',[x y 100 20],'labelfraction',0.60); - NumeditParam(obj,'StimLines', 1,x,y,'position',[x+100 y 100 20],'labelfraction',0.60); next_row(y); - - NumeditParam(obj,'StartDelay', 0,x,y,'position',[x y 100 20],'labelfraction',0.60); - NumeditParam(obj,'StimFreq', 20,x,y,'position',[x+100 y 100 20],'labelfraction',0.60); next_row(y); - NumeditParam(obj,'PulseWidth',15,x,y,'position',[x y 100 20],'labelfraction',0.60); - NumeditParam(obj,'NumPulses', 1,x,y,'position',[x+100 y 100 20],'labelfraction',0.60); next_row(y); - - DispParam(obj,'SD',0 ,x,y,'position',[x y 50 20],'labelfraction',0.4); - DispParam(obj,'SF',20,x,y,'position',[x+50 y 50 20],'labelfraction',0.4); - DispParam(obj,'PW',15,x,y,'position',[x+100 y 50 20],'labelfraction',0.4); - DispParam(obj,'NP',1,x,y,'position',[x+150 y 50 20],'labelfraction',0.4); next_row(y); - - NumeditParam(obj,'StimProb', 0,x,y,'position',[x y 100 20],'labelfraction',0.65); - ToggleParam( obj,'ShuffleValues',0,x,y,'position',[x+100 y 100 20],'OnString','Shuffle','OffString','Lock'); next_row(y); - - SoloParamHandle(obj, 'stimulator_history', 'value', []); - - SubheaderParam(obj, 'title', 'Stimulator Section', x, y); next_row(y); - varargout{1} = x; - varargout{2} = y; - -%% update_values -% ----------------------------------------------------------------------- -% -% UPDATE_VALUES -% -% ----------------------------------------------------------------------- - - case 'update_values', - - StimulatorSection(obj,'StimInterval'); - sh = value(stimulator_history) %#ok - %if n_done_trials == 0 || sh(end) == 0 - % LegalCBrk_temp.value = value(LegalCBrk); %#ok - %end - - if ~dispatcher('is_running'); - %dispatcher is not running, last stim_hist not used, lop it off - sh = sh(1:end-1); - end - - if value(StimProb) == 0 - %LCB_nostim.value = value(LegalCBrk); %#ok - stimulator_history.value = [sh, 0]; - elseif rand(1) <= value(StimProb) %&& ((value(StimOnFree)==1 && strcmp(value(ThisTrial_Free),'FREE')) || value(StimOnFree)==0) - stimulator_history.value = [sh, 1]; - %LegalCBrk.value = value(LCB_onstim); - else - %LegalCBrk.value = value(LCB_nostim); %#ok - stimulator_history.value = [sh, 0]; - end - - - -%% prepare_next_trial -% ----------------------------------------------------------------------- -% -% PREPARE_NEXT_TRIAL -% -% ----------------------------------------------------------------------- - - case 'prepare_next_trial', - sh = value(stimulator_history); %#ok - - sma = x; - - sd = value(StartDelay); - sf = value(StimFreq); - pw = value(PulseWidth); - np = value(NumPulses); - ss = value(StimStates) - sl = value(StimLines) - - if value(ShuffleValues) == 1 - sd = sd(ceil(rand(1) * length(sd))); - sf = sf(ceil(rand(1) * length(sf))); - pw = pw(ceil(rand(1) * length(pw))); - np = np(ceil(rand(1) * length(np))); - ss = ss(ceil(rand(1) * length(ss))); - sl = sl(ceil(rand(1) * length(sl))); - else - if length(unique([length(sd) length(sf) length(pw) length(np) length(ss) length(sl)])) > 1 - disp('Warning: param values in StimulatorSection have different lengths. Only first value will be used.'); - temp = 1; - else - temp = ceil(rand(1) * length(sd)); - end - sd = sd(temp); sf = sf(temp); pw = pw(temp); np = np(temp); ss = ss(temp); sl = sl(temp); - end - - pss = get(get_ghandle(StimState),'String'); %#ok - psl = get(get_ghandle(StimLine), 'String'); %#ok - if ss > length(pss) - disp('StimState value greater than list of possible stim states'); - else - StimState.value = ss; - disp('test ss') - value(ss) - end - - if sl > length(psl) - slc = ['0',num2str(sl),'0']; - z = find(slc == '0'); - if length(z) > 2 - sln = []; - for i = 1:length(z)-1 - sln(end+1) = str2num(slc(z(i)+1:z(i+1)-1)); %#ok - end - if any(sln > length(psl)) - disp('StimLine value greater than list of possible stim lines'); - else - slname = psl{sln(1)}; - for i=2:length(sln) - slname = [slname,'+',psl{sln(i)}]; %#ok - end - if sum(strcmp(psl,slname)) == 0 - psl{end+1} = slname; - set(get_ghandle(StimLine),'String',psl) - end - StimLine.value = find(strcmp(psl,slname)==1,1,'first'); - sl = sln; - end - else - disp('StimLine value greater than list of possible stim lines'); - end - else - StimLine.value = sl; - end - - for i = 1:length(sl) - stimline = bSettings('get','DIOLINES',psl{sl(i)}); - - sma = add_scheduled_wave(sma,... - 'name', ['stimulator_wave',num2str(i)],... - 'preamble', (1/sf)-(pw/1000),... %%%% Remember: change it such that if this is negative makes it 0 - 'sustain' , pw/1000,... - 'DOut', stimline,... - 'loop', np-1); - - if sd ~= 0 - sma = add_scheduled_wave(sma,... - 'name',['stimulator_wave_pause',num2str(i)],... - 'preamble',sd,... - 'trigger_on_up',['stimulator_wave',num2str(i)]); - else - sma = add_scheduled_wave(sma,... - 'name',['stimulator_wave_pause',num2str(i)],... - 'preamble',1,... - 'trigger_on_up',['stimulator_wave',num2str(i)]); - end - end - - for i = 1:length(sl) - if sh(end) == 1 - if strcmp(value(StimState),'none') == 0 - if sd ~= 0 - sma = add_stimulus(sma,['stimulator_wave_pause',num2str(i)],value(StimState)); - else - sma = add_stimulus(sma,['stimulator_wave',num2str(i)],value(StimState)); - end - - SD.value = sd; SF.value = sf; PW.value = pw; NP.value = np; - end - else - SD.value = 0; SF.value = 0; PW.value = 0; NP.value = 0; - end - end - - varargout{1} = sma; - - - %% Case StimInterval - case 'StimInterval' - - if strcmp(StimInterval, 'WholeTrial') - PulseWidth.value = Total_CP_duration*1000; - StimFreq.value = 1000/PulseWidth; - StartDelay.value = PreStim_time; - elseif strcmp(StimInterval, 'S1') - PulseWidth.value = A1_time*1000; - StimFreq.value = 1000/PulseWidth; - StartDelay.value = PreStim_time; - elseif strcmp(StimInterval, 'DelayDur') - PulseWidth.value = time_bet_aud1_gocue*1000; - StimFreq.value = 1000/PulseWidth; - StartDelay.value = PreStim_time + A1_time; - elseif strcmp(StimInterval, 'GoCue') - PulseWidth.value = time_go_cue*1000; - StimFreq.value = 1000/PulseWidth; - StartDelay.value = PreStim_time + A1_time + time_bet_aud1_gocue; - - end -%% set -% ----------------------------------------------------------------------- -% -% SET -% -% ----------------------------------------------------------------------- - case 'set' - varname = x; - newval = y; - - try - temp = 'SoloParamHandle'; %#ok - eval(['test = isa(',varname,',temp);']); - if test == 1 - eval([varname,'.value = newval;']); - end - catch %#ok - showerror; - warning(['Unable to assign value: ',num2str(newval),' to SoloParamHandle: ',varname]); %#ok - end - - -%% get -% ----------------------------------------------------------------------- -% -% GET -% -% ----------------------------------------------------------------------- - case 'get' - varname = x; - - try - temp = 'SoloParamHandle'; %#ok - eval(['test = isa(',varname,',temp);']); - if test == 1 - eval(['varargout{1} = value(',varname,');']); - end - catch %#ok - showerror; - warning(['Unable to get value from SoloParamHandle: ',varname]); %#ok - end - - -%% reinit -% ----------------------------------------------------------------------- -% -% REINIT -% -% ----------------------------------------------------------------------- - case 'reinit' - currfig = double(gcf); - - % Delete all SoloParamHandles who belong to this object and whose - % fullname starts with the name of this mfile: - delete_sphandle('owner', ['^@' class(obj) '$'], ... - 'fullname', ['^' mfilename]); - - - % Reinitialise at the original GUI position and figure: - feval(mfilename, obj, 'init'); - - % Restore the current figure: - figure(currfig); -end - - From 3143109da2d8427cc08551d004d147bf68c4a853 Mon Sep 17 00:00:00 2001 From: Arpit Date: Sat, 24 May 2025 00:07:37 +0100 Subject: [PATCH 117/164] error debug --- ...pitCentrePokeTraining_SessionDefinition_AutoTrainingStages.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Protocols/@ArpitCentrePokeTraining/ArpitCentrePokeTraining_SessionDefinition_AutoTrainingStages.m b/Protocols/@ArpitCentrePokeTraining/ArpitCentrePokeTraining_SessionDefinition_AutoTrainingStages.m index ea43e957..33cf16bc 100644 --- a/Protocols/@ArpitCentrePokeTraining/ArpitCentrePokeTraining_SessionDefinition_AutoTrainingStages.m +++ b/Protocols/@ArpitCentrePokeTraining/ArpitCentrePokeTraining_SessionDefinition_AutoTrainingStages.m @@ -699,7 +699,7 @@ callback(ParamsSection_CP_duration); % Timing parameter adjustments based on CP_duration -if new_CP >= starting_cp +if new_CP >= starting_cp && new_CP ~= curr_CP if new_CP >= cp_min % Scale A1_time linearly between 0.1 to 0.4 scale = (new_CP - cp_min) / cp_range; From 7790fa473301afaa6377eb1f503a89a61aaaeef6 Mon Sep 17 00:00:00 2001 From: Arpit Date: Tue, 27 May 2025 14:26:00 +0100 Subject: [PATCH 118/164] error debug and minor improvements --- ...ing_SessionDefinition_AutoTrainingStages.m | 19 +++++++++++++++++++ .../@ArpitCentrePokeTraining/ParamsSection.m | 5 +++-- .../TrainingStageParamsSection.m | 1 + 3 files changed, 23 insertions(+), 2 deletions(-) diff --git a/Protocols/@ArpitCentrePokeTraining/ArpitCentrePokeTraining_SessionDefinition_AutoTrainingStages.m b/Protocols/@ArpitCentrePokeTraining/ArpitCentrePokeTraining_SessionDefinition_AutoTrainingStages.m index 33cf16bc..065f37ee 100644 --- a/Protocols/@ArpitCentrePokeTraining/ArpitCentrePokeTraining_SessionDefinition_AutoTrainingStages.m +++ b/Protocols/@ArpitCentrePokeTraining/ArpitCentrePokeTraining_SessionDefinition_AutoTrainingStages.m @@ -46,6 +46,20 @@ callback(ParamsSection_training_stage); end +% Introduce Go/Reward Sound intensity after the rat did some trials and also increase +% it gradually until the max sound played at around 0.05. We will start +% with 0.001 +if value(PerformanceSummarySection_stage_1_TrialsValid) < value(TrainingStageParamsSection_Go_Sound_Start) + ParamsSection_Go_Sound.Value = 0; +elseif value(PerformanceSummarySection_stage_1_TrialsValid) == value(TrainingStageParamsSection_Go_Sound_Start)% start gradual increase + ParamsSection_Go_Sound.Value = 1; + SoundInterface_GoSoundVol.value = 0.001; +else + ParamsSection_Go_Sound.Value = 1; + SoundInterface_GoSoundVol.value = value(SoundInterface_GoSoundVol) + ((0.05 - 0.001) / (value(TrainingStageParamsSection_total_trials) - value(TrainingStageParamsSection_Go_Sound_Start))); +end +callback(SoundInterface_GoSoundVol); +callback(ParamsSection_Go_Sound); % Update TrainingStageParamsSection if n_done_trials >= 2 @@ -219,6 +233,7 @@ if n_done_trials >= 2 if previous_sides(end) ~= previous_sides(end-1) && all(~isnan(hit_history(end-1:end)))% last and present trials should also be a valid trial TrainingStageParamsSection_trial_oppSide.value = value(TrainingStageParamsSection_trial_oppSide) + 1; % updating value for variable in TrainingParams_Section + this_stage_opp_side_trials.value = value(this_stage_opp_side_trials) + 1; % updating value to change the reward_Collection_Dur callback(TrainingStageParamsSection_trial_oppSide); end end @@ -297,6 +312,8 @@ if n_done_trials > 50 if value(PerformanceSummarySection_stage_2_TrialsValid) > value(TrainingStageParamsSection_total_trials) && ... value(TrainingStageParamsSection_trial_oppSide) > value(TrainingStageParamsSection_total_trials_opp) + ParamsSection_RewardCollection_duration.value = 40; + callback(ParamsSection_RewardCollection_duration); ParamsSection_training_stage.value = stage_no + 1; callback(ParamsSection_training_stage); ParamsSection(obj, 'Changed_Training_Stage'); @@ -445,6 +462,8 @@ cp_max = value(ParamsSection_SettlingIn_time) + value(ParamsSection_legal_cbreak); if value(ParamsSection_CP_duration) >= cp_max TrainingStageParamsSection_last_session_CP.value = value(ParamsSection_CP_duration); + ParamsSection_RewardCollection_duration.value = 8; + callback(ParamsSection_RewardCollection_duration); callback(TrainingStageParamsSection_last_session_CP); ParamsSection_training_stage.value = 4; callback(ParamsSection_training_stage); diff --git a/Protocols/@ArpitCentrePokeTraining/ParamsSection.m b/Protocols/@ArpitCentrePokeTraining/ParamsSection.m index 9281371e..0861bfe2 100644 --- a/Protocols/@ArpitCentrePokeTraining/ParamsSection.m +++ b/Protocols/@ArpitCentrePokeTraining/ParamsSection.m @@ -90,7 +90,7 @@ DispParam(obj, 'Total_CP_duration', CP_duration+time_go_cue, x, y,'save_with_settings', 1, 'TooltipString', 'Total expected(rat can poke out anytime after Go cue onset) nose in center port time, in secs. Sum of CP_duration and Go Cue duration'); %#ok<*NODEF> next_row(y); - ToggleParam(obj, 'Go_Sound', 1, x, y, 'OnString', 'Play Reward Sound', 'OffString', 'No Reward Sound','TooltipString', ... + ToggleParam(obj, 'Go_Sound', 0, x, y, 'OnString', 'Play Reward Sound', 'OffString', 'No Reward Sound','TooltipString', ... 'If 1 (black), sound is played for intial 2 stages of light chasing; if 0 (brown), leave sound off'); next_row(y); ToggleParam(obj, 'stimuli_on', 0, x,y,... @@ -299,8 +299,9 @@ % change the reward collection duration once we start with centre % poke if value(training_stage) >= 3 - RewardCollection_duration.value = 100; Go_Sound.value = 1; + else + CP_duration.value = 0; end if value(training_stage) == 8 % user setting diff --git a/Protocols/@ArpitCentrePokeTraining/TrainingStageParamsSection.m b/Protocols/@ArpitCentrePokeTraining/TrainingStageParamsSection.m index 6552ccf6..ed3becb9 100644 --- a/Protocols/@ArpitCentrePokeTraining/TrainingStageParamsSection.m +++ b/Protocols/@ArpitCentrePokeTraining/TrainingStageParamsSection.m @@ -18,6 +18,7 @@ case 1 % STAGE RUNNING PARAMETERS NumeditParam(obj, 'trial_oppSide', 0, x, y,'TooltipString','total trials in opposide side this stage'); next_row(y); + NumeditParam(obj, 'Go_Sound_Start', 400, x, y,'TooltipString','trials after which Go/Reward Sound should start'); next_row(y); SubheaderParam(obj, 'title', 'Stage Params', x, y); % next_row(y); % COMPLETION TEST PARAMETERS From 52c61eed3346ce45af81e6b5748ae1ea8231d5c1 Mon Sep 17 00:00:00 2001 From: Arpit Date: Wed, 4 Jun 2025 16:43:20 +0100 Subject: [PATCH 119/164] making ArpitPokeTraining more efficient --- ...ing_SessionDefinition_AutoTrainingStages.m | 8 +- .../TrainingStageParamsSection.m | 30 +- .../AntibiasSection.asv | 358 ++++++++++++ .../AntibiasSection.m | 408 ++++++++++++++ .../ArpitSoundCatContinuous.m | 6 - .../ArpitSoundCatContinuousSMA.asv | 281 ++++++++++ .../ArpitSoundCatContinuousSMA.m | 281 ++++++++++ .../@ArpitSoundCatContinuous/SideSection.asv | 510 ++++++++++++++++++ .../@ArpitSoundCatContinuous/SideSection.m | 251 ++++----- 9 files changed, 1975 insertions(+), 158 deletions(-) create mode 100644 Protocols/@ArpitSoundCatContinuous/AntibiasSection.asv create mode 100644 Protocols/@ArpitSoundCatContinuous/AntibiasSection.m create mode 100644 Protocols/@ArpitSoundCatContinuous/ArpitSoundCatContinuousSMA.asv create mode 100644 Protocols/@ArpitSoundCatContinuous/ArpitSoundCatContinuousSMA.m create mode 100644 Protocols/@ArpitSoundCatContinuous/SideSection.asv diff --git a/Protocols/@ArpitCentrePokeTraining/ArpitCentrePokeTraining_SessionDefinition_AutoTrainingStages.m b/Protocols/@ArpitCentrePokeTraining/ArpitCentrePokeTraining_SessionDefinition_AutoTrainingStages.m index 065f37ee..3e305443 100644 --- a/Protocols/@ArpitCentrePokeTraining/ArpitCentrePokeTraining_SessionDefinition_AutoTrainingStages.m +++ b/Protocols/@ArpitCentrePokeTraining/ArpitCentrePokeTraining_SessionDefinition_AutoTrainingStages.m @@ -625,6 +625,8 @@ ParamsSection_training_stage.value = 5; callback(ParamsSection_training_stage); + ParamsSection_RewardCollection_duration.value = 8; % Although done in previous stage as well but still to be sure + callback(ParamsSection_RewardCollection_duration); ParamsSection(obj, 'Changed_Training_Stage'); SessionDefinition(obj, 'jump_to_stage', 'Introduce Stimuli Sound during Centre Poke'); TrainingStageParamsSection_last_session_CP.value = value(ParamsSection_CP_duration); @@ -873,7 +875,7 @@ prestim_min = value(TrainingStageParamsSection_min_prestim); prestim_max = value(TrainingStageParamsSection_max_prestim); stim_dur = value(TrainingStageParamsSection_stim_dur); - +init_CP_duration = value(ParamsSection_init_CP_duration); stage_no = value(SessionDefinition_CURRENT_ACTIVE_STAGE); if stage_no ~= value(ParamsSection_training_stage) ParamsSection_training_stage.value = stage_no; @@ -903,7 +905,7 @@ ParamsSection_SettlingIn_time.value = 0.2; callback(ParamsSection_SettlingIn_time); - if value(ParamsSection_CP_duration) < 3 % during the warm up phase + if n_done_trials <= n_trial_warmup % during the warm up phase ParamsSection_PreStim_time.value = 0.1; ParamsSection_A1_time.value = 0.1; else @@ -1218,7 +1220,7 @@ ClearHelperVarsNotOwned(obj); % % Variables for warmup stage -cp_max = 5; +cp_max = 3; n_trial_warmup = 20; starting_cp = 0.5; warmup_completed = 0; diff --git a/Protocols/@ArpitCentrePokeTraining/TrainingStageParamsSection.m b/Protocols/@ArpitCentrePokeTraining/TrainingStageParamsSection.m index ed3becb9..0f81cf81 100644 --- a/Protocols/@ArpitCentrePokeTraining/TrainingStageParamsSection.m +++ b/Protocols/@ArpitCentrePokeTraining/TrainingStageParamsSection.m @@ -18,22 +18,22 @@ case 1 % STAGE RUNNING PARAMETERS NumeditParam(obj, 'trial_oppSide', 0, x, y,'TooltipString','total trials in opposide side this stage'); next_row(y); - NumeditParam(obj, 'Go_Sound_Start', 400, x, y,'TooltipString','trials after which Go/Reward Sound should start'); next_row(y); + NumeditParam(obj, 'Go_Sound_Start', 200, x, y,'TooltipString','trials after which Go/Reward Sound should start'); next_row(y); SubheaderParam(obj, 'title', 'Stage Params', x, y); % next_row(y); % COMPLETION TEST PARAMETERS - NumeditParam(obj, 'total_trials', 1000, x, y,'label','Trials','TooltipString','total trials in this stage for its completion'); next_row(y); - NumeditParam(obj, 'total_trials_opp', 300, x, y,'label','Trials_Opp','TooltipString','total trials with opposide side option in this stage for its completion'); next_row(y); + NumeditParam(obj, 'total_trials', 600, x, y,'label','Trials','TooltipString','total trials in this stage for its completion'); next_row(y); + NumeditParam(obj, 'total_trials_opp', 200, x, y,'label','Trials_Opp','TooltipString','total trials with opposide side option in this stage for its completion'); next_row(y); SubheaderParam(obj, 'title', 'Completion Params', x, y); next_row(y); case 2 % STAGE RUNNING PARAMETERS NumeditParam(obj, 'trial_oppSide', 0, x, y,'TooltipString','total trials in opposide side this stage'); next_row(y); - NumeditParam(obj, 'max_rColl_dur', 300, x, y,'label','T_Max_RCollect','TooltipString','User given max water collect time(code will optimize for value below this)'); next_row(y); - NumeditParam(obj, 'min_rColl_dur', 100, x, y,'label','T_Min_RCollect','TooltipString','User given min water collect time(code will optimize for value above this)'); next_row(y); + NumeditParam(obj, 'max_rColl_dur', 200, x, y,'label','T_Max_RCollect','TooltipString','User given max water collect time(code will optimize for value below this)'); next_row(y); + NumeditParam(obj, 'min_rColl_dur', 60, x, y,'label','T_Min_RCollect','TooltipString','User given min water collect time(code will optimize for value above this)'); next_row(y); SubheaderParam(obj, 'title', 'Stage Params', x, y); next_row(y); % COMPLETION TEST PARAMETERS - NumeditParam(obj, 'total_trials', 700, x, y,'label','Trials','TooltipString','total trials in this stage for its completion'); next_row(y); + NumeditParam(obj, 'total_trials', 600, x, y,'label','Trials','TooltipString','total trials in this stage for its completion'); next_row(y); NumeditParam(obj, 'total_trials_opp', 200, x, y,'label','Trials_Opp','TooltipString','total trials with opposide side option in this stage for its completion'); next_row(y); SubheaderParam(obj, 'title', 'Completion Params', x, y);next_row(y); @@ -59,7 +59,7 @@ case 5 % STAGE RUNNING PARAMETERS NumeditParam(obj, 'last_session_CP', 0, x, y,'TooltipString','total cp reached by last session'); next_row(y); - NumeditParam(obj, 'max_CP', 5, x, y,'label','CP_Dur_Max','TooltipString','max CP duration being trained in this stage'); next_row(y); + NumeditParam(obj, 'max_CP', 3, x, y,'label','CP_Dur_Max','TooltipString','max CP duration being trained in this stage'); next_row(y); NumeditParam(obj, 'CPfraction_inc', 0.002, x, y,'label','CP_frac_Increase','TooltipString','CP duration is increased by this fraction'); next_row(y); NumeditParam(obj, 'min_CP', 1.5, x, y,'label','CP_Dur_Min','TooltipString','min CP duration being trained in this stage'); next_row(y); NumeditParam(obj, 'starting_CP', 0.3, x, y,'TooltipString','min CP duration (minus the settling-in time) during warm up'); next_row(y); @@ -70,16 +70,16 @@ NumeditParam(obj, 'recent_violation', 0.15, x, y,'label','Recent_ViolateRate','TooltipString','violation rate for last 20 trials in this stage for its completion'); next_row(y); NumeditParam(obj, 'recent_timeout', 0.15, x, y,'label','Recent_TimeoutRate','TooltipString','timeout rate for last 20 trials in this stage for its completion'); next_row(y); NumeditParam(obj, 'stage_violation', 0.35, x, y,'label','Stage_ViolationRate','TooltipString','overall violation rate in this stage for its completion'); next_row(y); - NumeditParam(obj, 'total_trials', 1200, x, y,'label','Trials','TooltipString','total trials in this stage for its completion'); next_row(y); + NumeditParam(obj, 'total_trials', 800, x, y,'label','Trials','TooltipString','total trials in this stage for its completion'); next_row(y); SubheaderParam(obj, 'title', 'Completion Params', x, y);next_row(y); case 6 % STAGE RUNNING PARAMETERS NumeditParam(obj, 'last_session_CP', 0, x, y,'TooltipString','total cp reached by last session'); next_row(y); - NumeditParam(obj, 'max_CP', 5, x, y,'label','CP_Dur_Max','TooltipString','max CP duration being trained in this stage'); next_row(y); + NumeditParam(obj, 'max_CP', 3, x, y,'label','CP_Dur_Max','TooltipString','max CP duration being trained in this stage'); next_row(y); NumeditParam(obj, 'starting_CP', 0.3, x, y,'TooltipString','min CP duration (minus the settling-in time) during warm up'); next_row(y); NumeditParam(obj, 'warm_up_trials', 20, x, y,'label','Warmup Trials','TooltipString','N trials for warmup'); next_row(y); - NumeditParam(obj, 'max_prestim', 2, x, y,'label','Pre-Stim Max','TooltipString','This stage Max Time, before starting the stimulus'); next_row(y); + NumeditParam(obj, 'max_prestim', 1, x, y,'label','Pre-Stim Max','TooltipString','This stage Max Time, before starting the stimulus'); next_row(y); NumeditParam(obj, 'min_prestim', 0.2, x, y,'label','Pre-Stim Min','TooltipString','This stage Min Time, before starting the stimulus'); next_row(y); NumeditParam(obj, 'stim_dur', 0.4, x, y,'label','A1 Dur','TooltipString','This stage A1 duration'); next_row(y); SubheaderParam(obj, 'title', 'Stage Params', x, y); next_row(y); @@ -87,18 +87,18 @@ NumeditParam(obj, 'recent_violation', 0.15, x, y,'label','Recent_ViolateRate','TooltipString','violation rate for last 20 trials in this stage for its completion'); next_row(y); NumeditParam(obj, 'recent_timeout', 0.15, x, y,'label','Recent_TimeoutRate','TooltipString','timeout rate for last 20 trials in this stage for its completion'); next_row(y); NumeditParam(obj, 'stage_violation', 0.25, x, y,'label','Stage_ViolationRate','TooltipString','overall violation rate in this stage for its completion'); next_row(y); - NumeditParam(obj, 'total_trials', 1500, x, y,'label','Trials','TooltipString','total trials in this stage for its completion'); next_row(y); + NumeditParam(obj, 'total_trials', 800, x, y,'label','Trials','TooltipString','total trials in this stage for its completion'); next_row(y); SubheaderParam(obj, 'title', 'Completion Params', x, y);next_row(y); case 7 % STAGE RUNNING PARAMETERS NumeditParam(obj, 'last_session_CP', 0, x, y,'TooltipString','total cp reached by last session'); next_row(y); - NumeditParam(obj, 'max_CP', 5, x, y,'label','CP_Dur_Max','TooltipString','max CP duration being trained in this stage'); next_row(y); + NumeditParam(obj, 'max_CP', 3, x, y,'label','CP_Dur_Max','TooltipString','max CP duration being trained in this stage'); next_row(y); NumeditParam(obj, 'starting_CP', 0.3, x, y,'TooltipString','min CP duration (minus the settling-in time) during warm up'); next_row(y); NumeditParam(obj, 'warm_up_trials', 20, x, y,'label','Warmup Trials','TooltipString','N trials for warmup'); next_row(y); - NumeditParam(obj, 'max_prestim', 2, x, y,'label','Pre-Stim Max','TooltipString','This stage Max Time, before starting the stimulus'); next_row(y); + NumeditParam(obj, 'max_prestim', 1, x, y,'label','Pre-Stim Max','TooltipString','This stage Max Time, before starting the stimulus'); next_row(y); NumeditParam(obj, 'min_prestim', 0.2, x, y,'label','Pre-Stim Min','TooltipString','This stage Min Time, before starting the stimulus'); next_row(y); - NumeditParam(obj, 'max_prego', 2, x, y,'label','Max A1-GoCue time','TooltipString','This stage Max time, between the end of the stimulus and the go cue'); next_row(y); + NumeditParam(obj, 'max_prego', 1, x, y,'label','Max A1-GoCue time','TooltipString','This stage Max time, between the end of the stimulus and the go cue'); next_row(y); NumeditParam(obj, 'min_prego', 0.2, x, y,'label','Min A1-GoCue time','TooltipString','This stage Min time, between the end of the stimulus and the go cue'); next_row(y); NumeditParam(obj, 'stim_dur', 0.4, x, y,'label','A1 Dur','TooltipString','This stage A1 duration'); next_row(y); SubheaderParam(obj, 'title', 'Stage Params', x, y); next_row(y); @@ -106,7 +106,7 @@ NumeditParam(obj, 'recent_violation', 0.15, x, y,'label','Recent_ViolateRate','TooltipString','violation rate for last 20 trials in this stage for its completion'); next_row(y); NumeditParam(obj, 'recent_timeout', 0.15, x, y,'label','Recent_TimeoutRate','TooltipString','timeout rate for last 20 trials in this stage for its completion'); next_row(y); NumeditParam(obj, 'stage_violation', 0.20, x, y,'label','Stage_ViolationRate','TooltipString','overall violation rate in this stage for its completion'); next_row(y); - NumeditParam(obj, 'total_trials', 2000, x, y,'label','Trials','TooltipString','total trials in this stage for its completion'); next_row(y); + NumeditParam(obj, 'total_trials', 800, x, y,'label','Trials','TooltipString','total trials in this stage for its completion'); next_row(y); SubheaderParam(obj, 'title', 'Completion Params', x, y); next_row(y); end SubheaderParam(obj, 'title', 'AUTOMATED TRAINING STAGE', x, y); diff --git a/Protocols/@ArpitSoundCatContinuous/AntibiasSection.asv b/Protocols/@ArpitSoundCatContinuous/AntibiasSection.asv new file mode 100644 index 00000000..90fbfd67 --- /dev/null +++ b/Protocols/@ArpitSoundCatContinuous/AntibiasSection.asv @@ -0,0 +1,358 @@ +% [x, y] = AntibiasSection(obj, action, [arg1], [arg2], [arg3]) +% +% Section that calculates biases and calculates probability of choosing a stimulus +% given the previous history. +% +% Antibias assumes that trials are of two classes, Left desired answer +% and Right desired answer, and that their outcome is either Correct or +% Incorrect. Given the history of previous trial classes, and the history +% of previous corrects/incorrects, Antibias makes a local estimate of +% fraction correct for each class, combines that with a prior probability +% of making the next trial Left, and produces a recommended probability +% for choosing the next trial as Left. Antibias will tend to make the +% class with the smaller frac correct the one with the higher probability. +% The strength of that tendency is quantified by a parameter, beta. +% (See probabilistic_trial_selector.m for details on the math of how the +% tendency is generated.) +% +% Local estimates of fraction correct are computed using an exponential +% kernel, most recent trial the most strongly weighted. The tau of this +% kernel is a GUI parameter. Two different estimates are computed: one for +% use in computing Left probability and Right probability; and a second +% simply for GUI display purposes. The two estimates can have different +% taus for their kernels. +% +% GUI DISPLAY: When initialized, this plugin will put up two panels and a +% title. In each panel, there is a slider that controls the tau of the +% recent-trials exponential kernel. One panel will display the percent +% corrects for Right and Left, as computed with its kernel. The second panel +% will display the a posteriori probabilities of making the next trial a +% "Left" trial or making the next trial a "Right" trial. This second panel +% has its own tau slider, and it also has a GUI parameter, beta, that +% controls how strongly the history matters. If beta=0, history doesn't +% matter, and the a priori LeftProbability dominates. If beta=Inf, then +% history matters above all: the next trial will be of the type with lowest +% fraction correct, for sure. +% +% See the bottom of this help file for examples of usage. +% +% arg1, arg2, arg3 are optional and their meaning depends on action (see +% below). +% +% PARAMETERS: +% ----------- +% +% obj Default object argument. +% +% action One of: +% +% 'init' To initialise the plugin and set up the GUI for it. This +% action requires two more arguments: The bottom left +% x y position (in units of pixels) of where to start placing +% the GUI elements of this plugin. +% +% 'update' This call will recompute both local estimates of +% fraction correct, and will recompute the recommended +% LeftProb, p(Left). This action requires three more arguments: +% HitHist, LProb, a scalar b/w 0 and 1; HitHist, a vector of 1s +% SidesHist and 0s and of length n_done_trials where 1 represents +% correct and 0 represents incorrect, first element +% corresponds to first trial; and SidesHist, a vector of +% 'l's and 'r's and of length n_done_trials where 'l' +% represents 'left', 'r' represents 'right' first element +% corresponds to first trial. +% +% 'get_posterior_probs' Returns a vector with two components, +% [p(Left) p(Right)]. +% +% +% 'update_biashitfrac' This call will recompute the local estimate of fraction +% Left correct and fraction Right correct used for +% LeftProb, antibiasing, and will also recompute the recommended +% HitHist, Left probability. This action +% SidesHist, requires three more arguments: LProb, a scalar b/w 0 +% and 1; HitHist, a vector of 1s and 0s and of length +% n_done_trials where 1 represents correct and 0 +% represents incorrect, first element corresponds to +% first trial; and SidesHist, a vector of 'l's and 'r's +% and of length n_done_trials where 'l' represents +% 'left', 'r' represents 'right' first element +% corresponds to first trial. +% +% 'update_hitfrac' This call is not related to computing the posterior +% Left probability, but will recompute only the local estimate +% HitHist, of fraction correct that is not used for antibiasing. +% SidesHist This action requires two more arguments: HitHist, a +% vector of 1s and 0s and of length n_done_trials where 1 +% represents correct and 0 represents incorrect, first +% element corresponds to first trial; and SidesHist, a vector of +% 'l's and 'r's and of length n_done_trials where 'l' +% represents 'left', 'r' represents 'right' first element +% corresponds to first trial. +% +% 'get' Needs one extra parameter, either 'Beta' or +% 'antibias_tau', and returns the corresponding scalar. +% +% 'reinit' Delete all of this section's GUIs and data, +% and reinit, at the same position on the same +% figure as the original section GUI was placed. +% +% +% x, y Relevant to action = 'init'; they indicate the initial +% position to place the GUI at, in the current figure window +% +% RETURNS: +% -------- +% +% if action == 'init' : +% +% [x1, y1, w, h] When action == 'init', Antibias will put up GUIs and take +% up a certain amount of space of the figure that was current when +% AntiBiasSection(obj, 'init', x, y) was called. On return, [x1 y1] +% will be the top left corner of the space used; [x y] (as passed +% to Antibias in the init call) will be the bottom left corner; +% [x+w y1] will be the top right; and [x+w y] will be the bottom +% right. h = y1-y. All these are in units of pixels. +% +% +% if action == 'get_posterior_probs' : +% +% [L R] When action == 'get_posterior_probs', a two-component vector is +% returned, with p(Left) and p(Right). If beta=0, then p(Left) +% will be the same as the last LeftProb that was passed in. +% +% +% USAGE: +% ------ +% +% To use this plugin, the typical calls would be: +% +% 'init' : On initializing your protocol, call +% AntibiasSection(obj, 'init', x, y); +% +% 'update' : After a trial is completed, call +% AntibiasSection(obj, 'update', LeftProb, HitHist, SidesHist) +% +% 'get_posterior_probs' : After a trial is completed, and when you are +% deciding what kind of trial to make the next trial, get the plugins +% opinion on whether the next trial should be Left or Right by calling +% AntibiasSection(obj, 'get_posterior_probs') +% +% See PARAMETERS section above for the documentation of each of these calls. +% + + +function [x, y, w, h] = AntibiasSection(obj, action, varargin) + +GetSoloFunctionArgs(obj); + +switch action + + case 'init' % ------------ CASE INIT ---------------- + x = varargin{1}; y = varargin{2}; y0 = y; + % Save the figure and the position in the figure where we are + % going to start adding GUI elements: + SoloParamHandle(obj, 'my_gui_info', 'value', [x y double(gcf)]); + + LogsliderParam(obj, 'HitFracTau', 30, 10, 400, x, y, 'label', 'hits frac tau', ... + 'TooltipString', ... + sprintf(['\nnumber of trials back over which to compute fraction of correct trials.\n' ... + 'This is just for displaying info-- for the bias calculation, see BiasTau above'])); + set_callback(HitFracTau, {mfilename, 'update_hitfrac'}); + next_row(y); + DispParam(obj, 'LtHitFrac', 0, x, y); next_row(y); + DispParam(obj, 'RtHitFrac', 0, x, y); next_row(y); + DispParam(obj, 'HitFrac', 0, x, y); next_row(y); + + next_row(y, 0.5); + + LogsliderParam(obj, 'BiasTau', 30, 10, 400, x, y, 'label', 'antibias tau', ... + 'TooltipString', ... + sprintf(['\nnumber of trials back over\nwhich to compute fraction of correct trials\n' ... + 'for the antibias function.'])); next_row(y); + NumeditParam(obj, 'Beta', 0, x, y, ... + 'TooltipString', ... + sprintf(['When this is 0, past performance doesn''t affect choice\n' ... + 'of next trial. When this is large, the next trial is ' ... + 'almost guaranteed\nto be the one with smallest %% correct'])); next_row(y); + set_callback({BiasTau, Beta}, {mfilename, 'update_biashitfrac'}); + DispParam(obj, 'LtProb', 0, x, y); next_row(y); + DispParam(obj, 'RtProb', 0, x, y); next_row(y); + SoloParamHandle(obj, 'BiasLtHitFrac', 'value', 0); + SoloParamHandle(obj, 'BiasRtHitFrac', 'value', 0); + + SoloParamHandle(obj, 'LocalLeftProb', 'value', 0.5); + SoloParamHandle(obj, 'LocalHitHistory', 'value', []); + SoloParamHandle(obj, 'LocalPrevSides', 'value', []); + + + SubheaderParam(obj, 'title', mfilename, x, y); + next_row(y, 1.5); + + w = gui_position('get_width'); + h = y-y0; + + + case 'update' % --- CASE UPDATE ------------------- + if ~isempty(varargin) + LocalLeftProb.value = varargin{1}; + end + if length(varargin)>1 + LocalHitHistory.value = varargin{2}; + end + if length(varargin)>2 + LocalPrevSides.value = varargin{3}; + end + % Protect against somebody passing in SPHs, not actual values, by mistake: + if isa(value(LocalLeftProb), 'SoloParamHandle') + LocalLeftProb.value = value(value(LocalLeftProb)); + end + if isa(value(LocalHitHistory), 'SoloParamHandle') + LocalHitHistory.value = value(value(LocalHitHistory)); + end + if isa(value(LocalPrevSides), 'SoloParamHandle') + LocalPrevSides.value = value(value(LocalPrevSides)); + end + + feval(mfilename, obj, 'update_hitfrac'); + feval(mfilename, obj, 'update_biashitfrac'); + + + + case 'update_biashitfrac' % ------- CASE UPDATE_BIASHITFRAC ------------- + if ~isempty(varargin) + LocalLeftProb.value = varargin{1}; + end + if length(varargin)>1 + LocalHitHistory.value = varargin{2}; + end + if length(varargin)>2 + LocalPrevSides.value = varargin{3}; + end + + % Protect against somebody passing in SPHs, not actual values, by mistake: + if isa(value(LocalLeftProb), 'SoloParamHandle') + LocalLeftProb.value = value(value(LocalLeftProb)); + end + if isa(value(LocalHitHistory), 'SoloParamHandle') + LocalHitHistory.value = value(value(LocalHitHistory)); + end + if isa(value(LocalPrevSides), 'SoloParamHandle') + LocalPrevSides.value = value(value(LocalPrevSides)); + end + + LeftProb = value(LocalLeftProb); + hit_history = value(LocalHitHistory); + hit_history = colvec(hit_history); + previous_sides = value(LocalPrevSides); + + kernel = exp(-(0:length(hit_history)-1)/BiasTau)'; + kernel = kernel(end:-1:1); + + prevs = previous_sides(1:length(hit_history))'; + ul = find(prevs == 'l'); + if isempty(ul), BiasLtHitFrac.value = 1; + else + BiasLtHitFrac.value = sum(hit_history(ul) .* kernel(ul))/sum(kernel(ul)); + end + + ur = find(prevs == 'r'); + if isempty(ur), BiasRtHitFrac.value = 1; + else + BiasRtHitFrac.value = sum(hit_history(ur) .* kernel(ur))/sum(kernel(ur)); + end + + if isempty(ul) && ~isempty(ur) + BiasLtHitFrac.value = value(BiasRtHitFrac); + end + if isempty(ur) && ~isempty(ul) + BiasRtHitFrac.value = value(BiasLtHitFrac); + end + + choices = probabilistic_trial_selector([value(BiasLtHitFrac), value(BiasRtHitFrac)], ... + [LeftProb, 1-LeftProb], value(Beta)); + LtProb.value = choices(1); + RtProb.value = choices(2); + + + case 'get_posterior_probs' % ------- CASE GET_POSTERIOR_PROBS ------------- + x = [value(LtProb) ; value(RtProb)]; %#ok + + + case 'update_hitfrac' % ------- CASE UPDATE_HITFRAC ------------- + if ~isempty(varargin), LocalHitHistory.value = varargin{1}; end + if length(varargin)>1, LocalPrevSides.value = varargin{2}; end + % Protect against somebody passing in SPHs, not actual values, by mistake: + if isa(value(LocalHitHistory), 'SoloParamHandle'), LocalHitHistory.value = value(value(LocalHitHistory)); end + if isa(value(LocalPrevSides), 'SoloParamHandle'), LocalPrevSides.value = value(value(LocalPrevSides)); end + + hit_history = value(LocalHitHistory); hit_history = colvec(hit_history); + previous_sides = value(LocalPrevSides); + + + if ~isempty(hit_history) + kernel = exp(-(0:length(hit_history)-1)/HitFracTau)'; + kernel = kernel(end:-1:1); + HitFrac.value = sum(hit_history .* kernel)/sum(kernel); + + prevs = previous_sides(1:length(hit_history))'; + u = find(prevs == 'l'); + + if isempty(u) + LtHitFrac.value = NaN; + else + LtHitFrac.value = sum(hit_history(u) .* kernel(u))/sum(kernel(u)); + end + + u = find(prevs == 'r'); + + if isempty(u) + RtHitFrac.value = NaN; + else + RtHitFrac.value = sum(hit_history(u) .* kernel(u))/sum(kernel(u)); + end + + end + + + case 'get' % ------- CASE GET ------------- + if length(varargin)~=1 + error('AntibiasSection:Invalid', '''get'' needs one extra param'); + end + switch varargin{1} + case 'Beta' + x = value(Beta); + case 'antibias_tau' + x = value(BiasTau); + otherwise + error('AntibiasSection:Invalid', 'Don''t know how to get %s', varargin{1}); + end + + + case 'reinit' % ------- CASE REINIT ------------- + currfig = double(gcf); + + % Get the original GUI position and figure: + x = my_gui_info(1); y = my_gui_info(2); figure(my_gui_info(3)); + + % Delete all SoloParamHandles who belong to this object and whose + % fullname starts with the name of this mfile: + delete_sphandle('owner', ['^@' class(obj) '$'], ... + 'fullname', ['^' mfilename]); + + % Reinitialise at the original GUI position and figure: + [x, y] = feval(mfilename, obj, 'init', x, y); + + % Restore the current figure: + figure(currfig); +end + + + + +function [x] = colvec(x) + + if size(x,2) > size(x,1) + x = x'; + end + \ No newline at end of file diff --git a/Protocols/@ArpitSoundCatContinuous/AntibiasSection.m b/Protocols/@ArpitSoundCatContinuous/AntibiasSection.m new file mode 100644 index 00000000..30af9f02 --- /dev/null +++ b/Protocols/@ArpitSoundCatContinuous/AntibiasSection.m @@ -0,0 +1,408 @@ +% [x, y] = AntibiasSection(obj, action, [arg1], [arg2], [arg3]) +% +% Section that calculates biases and calculates probability of choosing a stimulus +% given the previous history. +% +% Antibias assumes that trials are of two classes, Left desired answer +% and Right desired answer, and that their outcome is either Correct or +% Incorrect. Given the history of previous trial classes, and the history +% of previous corrects/incorrects, Antibias makes a local estimate of +% fraction correct for each class, combines that with a prior probability +% of making the next trial Left, and produces a recommended probability +% for choosing the next trial as Left. Antibias will tend to make the +% class with the smaller frac correct the one with the higher probability. +% The strength of that tendency is quantified by a parameter, beta. +% (See probabilistic_trial_selector.m for details on the math of how the +% tendency is generated.) +% +% Local estimates of fraction correct are computed using an exponential +% kernel, most recent trial the most strongly weighted. The tau of this +% kernel is a GUI parameter. Two different estimates are computed: one for +% use in computing Left probability and Right probability; and a second +% simply for GUI display purposes. The two estimates can have different +% taus for their kernels. +% +% GUI DISPLAY: When initialized, this plugin will put up two panels and a +% title. In each panel, there is a slider that controls the tau of the +% recent-trials exponential kernel. One panel will display the percent +% corrects for Right and Left, as computed with its kernel. The second panel +% will display the a posteriori probabilities of making the next trial a +% "Left" trial or making the next trial a "Right" trial. This second panel +% has its own tau slider, and it also has a GUI parameter, beta, that +% controls how strongly the history matters. If beta=0, history doesn't +% matter, and the a priori LeftProbability dominates. If beta=Inf, then +% history matters above all: the next trial will be of the type with lowest +% fraction correct, for sure. +% +% See the bottom of this help file for examples of usage. +% +% arg1, arg2, arg3 are optional and their meaning depends on action (see +% below). +% +% PARAMETERS: +% ----------- +% +% obj Default object argument. +% +% action One of: +% +% 'init' To initialise the plugin and set up the GUI for it. This +% action requires two more arguments: The bottom left +% x y position (in units of pixels) of where to start placing +% the GUI elements of this plugin. +% +% 'update' This call will recompute both local estimates of +% fraction correct, and will recompute the recommended +% LeftProb, p(Left). This action requires three more arguments: +% HitHist, LProb, a scalar b/w 0 and 1; HitHist, a vector of 1s +% SidesHist and 0s and of length n_done_trials where 1 represents +% correct and 0 represents incorrect, first element +% corresponds to first trial; and SidesHist, a vector of +% 'l's and 'r's and of length n_done_trials where 'l' +% represents 'left', 'r' represents 'right' first element +% corresponds to first trial. +% +% 'get_posterior_probs' Returns a vector with two components, +% [p(Left) p(Right)]. +% +% +% 'update_biashitfrac' This call will recompute the local estimate of fraction +% Left correct and fraction Right correct used for +% LeftProb, antibiasing, and will also recompute the recommended +% HitHist, Left probability. This action +% SidesHist, requires three more arguments: LProb, a scalar b/w 0 +% and 1; HitHist, a vector of 1s and 0s and of length +% n_done_trials where 1 represents correct and 0 +% represents incorrect, first element corresponds to +% first trial; and SidesHist, a vector of 'l's and 'r's +% and of length n_done_trials where 'l' represents +% 'left', 'r' represents 'right' first element +% corresponds to first trial. +% +% 'update_hitfrac' This call is not related to computing the posterior +% Left probability, but will recompute only the local estimate +% HitHist, of fraction correct that is not used for antibiasing. +% SidesHist This action requires two more arguments: HitHist, a +% vector of 1s and 0s and of length n_done_trials where 1 +% represents correct and 0 represents incorrect, first +% element corresponds to first trial; and SidesHist, a vector of +% 'l's and 'r's and of length n_done_trials where 'l' +% represents 'left', 'r' represents 'right' first element +% corresponds to first trial. +% +% 'get' Needs one extra parameter, either 'Beta' or +% 'antibias_tau', and returns the corresponding scalar. +% +% 'reinit' Delete all of this section's GUIs and data, +% and reinit, at the same position on the same +% figure as the original section GUI was placed. +% +% +% x, y Relevant to action = 'init'; they indicate the initial +% position to place the GUI at, in the current figure window +% +% RETURNS: +% -------- +% +% if action == 'init' : +% +% [x1, y1, w, h] When action == 'init', Antibias will put up GUIs and take +% up a certain amount of space of the figure that was current when +% AntiBiasSection(obj, 'init', x, y) was called. On return, [x1 y1] +% will be the top left corner of the space used; [x y] (as passed +% to Antibias in the init call) will be the bottom left corner; +% [x+w y1] will be the top right; and [x+w y] will be the bottom +% right. h = y1-y. All these are in units of pixels. +% +% +% if action == 'get_posterior_probs' : +% +% [L R] When action == 'get_posterior_probs', a two-component vector is +% returned, with p(Left) and p(Right). If beta=0, then p(Left) +% will be the same as the last LeftProb that was passed in. +% +% +% USAGE: +% ------ +% +% To use this plugin, the typical calls would be: +% +% 'init' : On initializing your protocol, call +% AntibiasSection(obj, 'init', x, y); +% +% 'update' : After a trial is completed, call +% AntibiasSection(obj, 'update', LeftProb, HitHist, SidesHist) +% +% 'get_posterior_probs' : After a trial is completed, and when you are +% deciding what kind of trial to make the next trial, get the plugins +% opinion on whether the next trial should be Left or Right by calling +% AntibiasSection(obj, 'get_posterior_probs') +% +% See PARAMETERS section above for the documentation of each of these calls. +% + + +function [x, y, w, h] = AntibiasSection(obj, action, varargin) + +GetSoloFunctionArgs(obj); + +switch action + + case 'init' % ------------ CASE INIT ---------------- + x = varargin{1}; y = varargin{2}; y0 = y; + % Save the figure and the position in the figure where we are + % going to start adding GUI elements: + SoloParamHandle(obj, 'my_gui_info', 'value', [x y double(gcf)]); + + LogsliderParam(obj, 'HitFracTau', 30, 10, 400, x, y, 'label', 'hits frac tau', ... + 'TooltipString', ... + sprintf(['\nnumber of trials back over which to compute fraction of correct trials.\n' ... + 'This is just for displaying info-- for the bias calculation, see BiasTau above'])); + set_callback(HitFracTau, {mfilename, 'update_hitfrac'}); + next_row(y); + DispParam(obj, 'LtHitFrac', 0, x, y); next_row(y); + DispParam(obj, 'RtHitFrac', 0, x, y); next_row(y); + DispParam(obj, 'HitFrac', 0, x, y); next_row(y); + + next_row(y, 0.5); + + LogsliderParam(obj, 'BiasTau', 30, 10, 400, x, y, 'label', 'antibias tau', ... + 'TooltipString', ... + sprintf(['\nnumber of trials back over\nwhich to compute fraction of correct trials\n' ... + 'for the antibias function.'])); next_row(y); + NumeditParam(obj, 'Beta', 0, x, y, ... + 'TooltipString', ... + sprintf(['When this is 0, past performance doesn''t affect choice\n' ... + 'of next trial. When this is large, the next trial is ' ... + 'almost guaranteed\nto be the one with smallest %% correct'])); next_row(y); + set_callback({BiasTau, Beta}, {mfilename, 'update_biashitfrac'}); + DispParam(obj, 'LtProb', 0, x, y); next_row(y); + DispParam(obj, 'RtProb', 0, x, y); next_row(y); + SoloParamHandle(obj, 'BiasLtHitFrac', 'value', 0); + SoloParamHandle(obj, 'BiasRtHitFrac', 'value', 0); + + SoloParamHandle(obj, 'LocalLeftProb', 'value', 0.5); + SoloParamHandle(obj, 'LocalHitHistory', 'value', []); + SoloParamHandle(obj, 'LocalPrevSides', 'value', []); + + + SubheaderParam(obj, 'title', mfilename, x, y); + next_row(y, 1.5); + + w = gui_position('get_width'); + h = y-y0; + + + case 'update' % --- CASE UPDATE ------------------- + if ~isempty(varargin) + LocalLeftProb.value = varargin{1}; + end + if length(varargin)>1 + LocalHitHistory.value = varargin{2}; + end + if length(varargin)>2 + LocalPrevSides.value = varargin{3}; + end + % Protect against somebody passing in SPHs, not actual values, by mistake: + if isa(value(LocalLeftProb), 'SoloParamHandle') + LocalLeftProb.value = value(value(LocalLeftProb)); + end + if isa(value(LocalHitHistory), 'SoloParamHandle') + LocalHitHistory.value = value(value(LocalHitHistory)); + end + if isa(value(LocalPrevSides), 'SoloParamHandle') + LocalPrevSides.value = value(value(LocalPrevSides)); + end + + feval(mfilename, obj, 'update_hitfrac'); + feval(mfilename, obj, 'update_biashitfrac'); + + + case 'update_hitfrac' % ------- CASE UPDATE_HITFRAC ------------- + if ~isempty(varargin), LocalHitHistory.value = varargin{1}; end + if length(varargin)>1, LocalPrevSides.value = varargin{2}; end + % Protect against somebody passing in SPHs, not actual values, by mistake: + if isa(value(LocalHitHistory), 'SoloParamHandle') + LocalHitHistory.value = value(value(LocalHitHistory)); + end + if isa(value(LocalPrevSides), 'SoloParamHandle') + LocalPrevSides.value = value(value(LocalPrevSides)); + end + + hit_history = value(LocalHitHistory); + hit_history = colvec(hit_history); + previous_sides = value(LocalPrevSides); + + + if ~isempty(hit_history) + kernel = exp(-(0:length(hit_history)-1)/HitFracTau)'; + kernel = kernel(end:-1:1); + HitFrac.value = sum(hit_history .* kernel)/sum(kernel); + + prevs = previous_sides(1:length(hit_history))'; + u = find(prevs == 'l'); + + if isempty(u) + LtHitFrac.value = NaN; + else + LtHitFrac.value = sum(hit_history(u) .* kernel(u))/sum(kernel(u)); + end + + u = find(prevs == 'r'); + + if isempty(u) + RtHitFrac.value = NaN; + else + RtHitFrac.value = sum(hit_history(u) .* kernel(u))/sum(kernel(u)); + end + + end + + + + case 'update_biashitfrac' % ------- CASE UPDATE_BIASHITFRAC ------------- + if ~isempty(varargin) + LocalLeftProb.value = varargin{1}; + end + if length(varargin)>1 + LocalHitHistory.value = varargin{2}; + end + if length(varargin)>2 + LocalPrevSides.value = varargin{3}; + end + + % Protect against somebody passing in SPHs, not actual values, by mistake: + if isa(value(LocalLeftProb), 'SoloParamHandle') + LocalLeftProb.value = value(value(LocalLeftProb)); + end + if isa(value(LocalHitHistory), 'SoloParamHandle') + LocalHitHistory.value = value(value(LocalHitHistory)); + end + if isa(value(LocalPrevSides), 'SoloParamHandle') + LocalPrevSides.value = value(value(LocalPrevSides)); + end + + LeftProb = value(LocalLeftProb); + hit_history = value(LocalHitHistory); + hit_history = colvec(hit_history); + previous_sides = value(LocalPrevSides); + + kernel = exp(-(0:length(hit_history)-1)/BiasTau)'; + kernel = kernel(end:-1:1); + + prevs = previous_sides(1:length(hit_history))'; + ul = find(prevs == 'l'); + if isempty(ul), BiasLtHitFrac.value = 1; + else + BiasLtHitFrac.value = sum(hit_history(ul) .* kernel(ul))/sum(kernel(ul)); + end + + ur = find(prevs == 'r'); + if isempty(ur), BiasRtHitFrac.value = 1; + else + BiasRtHitFrac.value = sum(hit_history(ur) .* kernel(ur))/sum(kernel(ur)); + end + + if isempty(ul) && ~isempty(ur) + BiasLtHitFrac.value = value(BiasRtHitFrac); + end + if isempty(ur) && ~isempty(ul) + BiasRtHitFrac.value = value(BiasLtHitFrac); + end + + choices = probabilistic_trial_selector([value(BiasLtHitFrac), value(BiasRtHitFrac)], ... + [LeftProb, 1-LeftProb], value(Beta)); + LtProb.value = choices(1); + RtProb.value = choices(2); + + + case 'get_posterior_probs' % ------- CASE GET_POSTERIOR_PROBS ------------- + x = [value(LtProb) ; value(RtProb)]; %#ok + + + case 'get' % ------- CASE GET ------------- + if length(varargin)~=1 + error('AntibiasSection:Invalid', '''get'' needs one extra param'); + end + switch varargin{1} + case 'Beta' + x = value(Beta); + case 'antibias_tau' + x = value(BiasTau); + otherwise + error('AntibiasSection:Invalid', 'Don''t know how to get %s', varargin{1}); + end + + + case 'reinit' % ------- CASE REINIT ------------- + currfig = double(gcf); + + % Get the original GUI position and figure: + x = my_gui_info(1); y = my_gui_info(2); figure(my_gui_info(3)); + + % Delete all SoloParamHandles who belong to this object and whose + % fullname starts with the name of this mfile: + delete_sphandle('owner', ['^@' class(obj) '$'], ... + 'fullname', ['^' mfilename]); + + % Reinitialise at the original GUI position and figure: + [x, y] = feval(mfilename, obj, 'init', x, y); + + % Restore the current figure: + figure(currfig); +end + + + + +function [x] = colvec(x) + + if size(x,2) > size(x,1) + x = x'; + end + + +function [p] = probabilistic_trial_selector(frac, priors, beta) + +%[probs] = probabilistic_trial_selector(fractions_correct, priors, beta) +% +% Softmax trial selection, based on performance and prior distribution. +% +% Given a vector representing the fraction of correct trials (each element +% of the vector represents one stimulus type), and a prior distribution of +% desired probabilities, computes the probabilities for choosing the next +% stimulus trial. When beta=0, the probabilities returned are just the +% priors, regardless of performance. When beta is large (much bigger than +% 1), the trial type with the lowest per cent correct gets higher, all the +% others get lower. (A prior of 0, however, ensures a prob of 0). This +% function is most useful, then, for values of beta within the range 0 to +% 10. beta around 2 or 3 seems reasonable +% +% To get a single sample from probs, use Matlab's randsample.m like this: +% randsample(1:length(probs), 1, true, probs) +% This will return an index from 1 to length(probs) sampled according +% to the indicated probability. +% +% +% EXAMPLES: +% --------- +% +% >> probabilistic_trial_selector([1, 0.67, 0.3], [0.33 0.67 0], 0) +% probs = [0.33 0.67 0] +% +% >> probabilistic_trial_selector([1, 0.67, 0.3], [0.33 0.67 0], 0.5) +% probs = [0.295 0.705 0] +% +% >> probabilistic_trial_selector([1, 0.67, 0.3], [0.33 0.67 0], 10) +% probs = [0.017 0.982 0] +% + + if rows(frac)==cols(priors) + priors = priors'; + end + + p = exp(-frac*beta); + + p = p.*priors; + + p = p./sum(p); \ No newline at end of file diff --git a/Protocols/@ArpitSoundCatContinuous/ArpitSoundCatContinuous.m b/Protocols/@ArpitSoundCatContinuous/ArpitSoundCatContinuous.m index c3c75873..c41c6de4 100644 --- a/Protocols/@ArpitSoundCatContinuous/ArpitSoundCatContinuous.m +++ b/Protocols/@ArpitSoundCatContinuous/ArpitSoundCatContinuous.m @@ -188,12 +188,6 @@ % pd.performance=tot_perf(:); pd.cp_durs=cp_durs(:); - -% pd.stimuli=stimuli(:); - % Athena: look into pair_history perhaps stimulus_history and stimuli - % are the same - %pd.stimulus=stimulus_history(:); - pd.stim1dur=stim1dur(:); %pd.stimul=stim_history(:); diff --git a/Protocols/@ArpitSoundCatContinuous/ArpitSoundCatContinuousSMA.asv b/Protocols/@ArpitSoundCatContinuous/ArpitSoundCatContinuousSMA.asv new file mode 100644 index 00000000..9335b724 --- /dev/null +++ b/Protocols/@ArpitSoundCatContinuous/ArpitSoundCatContinuousSMA.asv @@ -0,0 +1,281 @@ +function [varargout] = ArpitSoundCatContinuousSMA(obj, action) + +GetSoloFunctionArgs; + + +switch action + + case 'init' + + + case 'prepare_next_trial' + + %% Setup water + + left1led = bSettings('get', 'DIOLINES', 'left1led'); + center1led = bSettings('get', 'DIOLINES', 'center1led'); + right1led = bSettings('get', 'DIOLINES', 'right1led'); + left1water = bSettings('get', 'DIOLINES', 'left1water'); + right1water = bSettings('get', 'DIOLINES', 'right1water'); + + + %% Setup sounds + + A1_sound_id = SoundManagerSection(obj, 'get_sound_id', 'StimAUD1'); % distribution based sound + sound_duration = value(A1_time); % SoundManagerSection(obj, 'get_sound_duration', 'SOneSound'); + go_sound_id = SoundManagerSection(obj, 'get_sound_id', 'GoSound'); + go_cue_duration = value(time_go_cue); % SoundManagerSection(obj, 'get_sound_duration', 'GoSound'); + viol_sound_id = SoundManagerSection(obj, 'get_sound_id', 'ViolationSound'); + viol_snd_duration = SoundManagerSection(obj, 'get_sound_duration', 'ViolationSound'); + to_sound_id = SoundManagerSection(obj, 'get_sound_id', 'TimeoutSound'); + timeout_snd_duration = SoundManagerSection(obj, 'get_sound_duration', 'TimeoutSound'); + + [LeftWValveTime,RightWValveTime] = ParamsSection(obj, 'get_water_amount'); + side = ParamsSection(obj, 'get_current_side'); + + if side == 'l' + HitEvent = 'Lin'; + ErrorEvent = 'Rin'; + sound_id = sone_sound_id; + SideLight = left1led; + WValveTime = LeftWValveTime; + WValveSide = left1water; + else + HitEvent = 'Rin'; + ErrorEvent = 'Lin'; + sound_id = stwo_sound_id; + SideLight = right1led; + WValveTime = RightWValveTime; + WValveSide = right1water; + end + + + sma = StateMachineAssembler('full_trial_structure','use_happenings', 1); + + %%%%%%%%%%%%%%%% SCHEDULED WAVES %%%%%%%%%%%%%%%%%%%%%%% + + % scheduled wave for stimuli / fixed(No) sound, based upon side + if value(stimuli_on) + sma = add_scheduled_wave(sma, 'name', 'stimplay', 'preamble', PreStim_time, ... + 'sustain', sound_duration, 'sound_trig', A1_sound_id); % to play a sound before Go Cue + else + % sma = add_scheduled_wave(sma, 'name', 'stimplay', 'preamble', PreStim_time, ... + % 'sustain', sound_duration, 'sound_trig', sound_id); % to play a fixed sound before Go Cue + sma = add_scheduled_wave(sma, 'name', 'stimplay', 'preamble', PreStim_time, ... + 'sustain', sound_duration); % to play No sound before Go Cue + end + + % Scheduled Wave for Go Sound + if value(Go_Sound) == 1 + sma = add_scheduled_wave(sma, 'name', 'Go_Cue', 'preamble', 0.001, ... + 'sustain', go_cue_duration, 'sound_trig', go_sound_id); % to play the Go Cue/Reward Sound + else + sma = add_scheduled_wave(sma, 'name', 'Go_Cue', 'preamble', 0.001, ... + 'sustain', go_cue_duration); % to play the Go Cue/Reward Sound + end + + % Scheduled wave for CP Duration + if CP_duration <= (SettlingIn_time + legal_cbreak) + sma = add_scheduled_wave(sma, 'name', 'CP_Duration_wave', 'preamble', CP_duration); % total length of centre poke to consider success + else + sma = add_scheduled_wave(sma, 'name', 'settling_period', 'preamble', SettlingIn_time); % intial fidgety period without violation + sma = add_scheduled_wave(sma, 'name', 'CP_Duration_wave', 'preamble', CP_duration - SettlingIn_time); % total length of centre poke minus the inital fidgety time to consider success + end + + % scheduled wave for rewarded side either of the side + sma = add_scheduled_wave(sma, 'name', 'reward_delivery', 'preamble', reward_delay, ... + 'sustain', WValveTime, 'DOut', WValveSide); % water delivery side + sma = add_scheduled_wave(sma, 'name', 'reward_collection_dur', 'preamble', SideLed_duration + RewardCollection_duration); % time to collect the reward + + %% + %%%%%%%%%%% % *STATES* %%%%%%%%%%%%%%%%%%% + + sma = add_state(sma,'name','wait_for_cpoke','self_timer',cp_timeout, ... + 'output_actions', {'DOut', center1led}, ... + 'input_to_statechange', {'Cin','settling_in_state';'Tup','timeout_state'}); + + %%%%%%%%%%%%% SETTLING IN STATE START %%%%%%%%%%%%%%%%%%%% + % Before progressing check if its still centre poking or pokes within legal c_break other wise its a violation + + if CP_duration <= SettlingIn_time + legal_cbreak % state machine during initial warm-up when starting a new session + + sma = add_state(sma,'name','settling_in_state','self_timer',CP_duration, ... + 'output_actions', {'SchedWaveTrig', 'CP_Duration_wave'}, ... + 'input_to_statechange', {'Cout','current_state + 1';'Tup','side_led_wait_RewardCollection'}); + + % Intermediate State + + % This intermediate state is considering the poke is out before the end of settling time / at the start of this state + sma = add_state(sma,'self_timer',CP_duration,'output_actions', {'DOut', center1led}, ... + 'input_to_statechange', {'CP_Duration_wave_In','side_led_wait_RewardCollection';'Cin','current_state + 1';'Tup','side_led_wait_RewardCollection'}); + + % The state jump to here when the nose is still in then go + % directly to give reward + sma = add_state(sma,'self_timer',CP_duration,... + 'input_to_statechange', {'CP_Duration_wave_In','side_led_wait_RewardCollection'; 'Cout','current_state - 1';'Tup','side_led_wait_RewardCollection'}); + + else % the usual state machine + + sma = add_state(sma,'name','settling_in_state','self_timer',SettlingIn_time, ... + 'output_actions', {'SchedWaveTrig', 'settling_period'}, ... + 'input_to_statechange', {'Cout','current_state + 1';'Tup','soft_cp'}); + + % Intermediate State + + % This intermediate state is considering the poke is out before the end of settling time / at the start of this state + sma = add_state(sma,'self_timer',SettlingIn_time,'output_actions', {'DOut', center1led}, ... + 'input_to_statechange', {'settling_period_In','legal_poke_start_state';'Cin','current_state + 1';'Tup','legal_poke_start_state';... + 'Rin', 'violation_state';'Rout', 'violation_state'; 'Lin', 'violation_state';'Lout', 'violation_state'}); + + % The state jump to here when the nose is still in then go directly to soft_cp + sma = add_state(sma,'self_timer',SettlingIn_time,... + 'input_to_statechange', {'settling_period_In','soft_cp'; 'Cout','current_state - 1';'Tup','soft_cp';... + 'Rin', 'violation_state'; 'Rout', 'violation_state'; 'Lin', 'violation_state'; 'Lout', 'violation_state'}); + + %%%%%%%%%%%%% SETTLING IN STATE END %%%%%%%%%%%%%%%%%%%%%% + + % STATE TO CHECK BEFORE START OF LEGAL POKE PERIOD + + sma = add_state(sma,'name','legal_poke_start_state','self_timer',legal_cbreak/2, ... + 'output_actions', {'DOut', center1led}, ... + 'input_to_statechange', {'Cin','soft_cp'; 'Tup','violation_state';... + 'Rin', 'violation_state'; 'Rout', 'violation_state'; 'Lin', 'violation_state'; 'Lout', 'violation_state'}); %more stringent by giving half the legal cp time + + %%%%%%%%%%%% LEGAL SOFT POKE STATE START %%%%%%%%%%%%%%%%% + + sma = add_state(sma,'name','soft_cp','self_timer',CP_duration - SettlingIn_time, ... CP_Duration_wave + 'output_actions', {'SchedWaveTrig', 'CP_Duration_wave+stimplay'},... + 'input_to_statechange', {'Cout','current_state + 1'; 'Tup','side_led_wait_RewardCollection';... + 'Rin', 'violation_state'; 'Rout', 'violation_state'; 'Lin', 'violation_state'; 'Lout', 'violation_state'}); %more stringent by giving half the legal cp time + + end + + % Intermediate State + + % This intermediate state is considering the poke is out before the end of settling time / at the start of this state + sma = add_state(sma,'self_timer',legal_cbreak,'output_actions', {'DOut', center1led}, ... + 'input_to_statechange', {'CP_Duration_wave_In','legal_poke_end_state';'Cin','current_state + 1';'Tup','violation_state';... + 'Rin', 'violation_state';'Rout', 'violation_state'; 'Lin', 'violation_state';'Lout', 'violation_state'}); + + % The state jump to here when the nose is still in then go directly to soft_cp + sma = add_state(sma,'self_timer',CP_duration - SettlingIn_time, ... + 'input_to_statechange', {'CP_Duration_wave_In','side_led_wait_RewardCollection';'Cout','current_state - 1';'Tup','side_led_wait_RewardCollection';... + 'Rin', 'violation_state';'Rout', 'violation_state'; 'Lin', 'violation_state';'Lout', 'violation_state'}); + + %%%%%%%%%%%% LEGAL SOFT POKE STATE END %%%%%%%%%%%%%%%%% + + % Before giving the reward check if its still centre poking or pokes + % within legal c_break other wise its a violation + + sma = add_state(sma,'name','legal_poke_end_state','self_timer',legal_cbreak/2, ... + 'output_actions', {'DOut', center1led}, ... + 'input_to_statechange', {'Cin','side_led_wait_RewardCollection'; 'Tup','violation_state'}); + + + %%%%%%%%%%%%%%% REWARD COLLECTION STATE START %%%%%%%%%%%%%%% + + sma = add_state(sma, 'name', 'side_led_wait_RewardCollection', 'self_timer', SideLed_duration + RewardCollection_duration, ... + 'output_actions', {'DOut', SideLight; 'SchedWaveTrig', 'reward_collection_dur+Go_Cue'}, ... + 'input_to_statechange',{HitEvent,'hit_state'; 'Tup','timeout_state'; ErrorEvent,'second_hit_state'}); + + sma = add_state(sma,'name','second_hit_state','self_timer',RewardCollection_duration,... + 'output_actions',{'DOut', SideLight},... + 'input_to_statechange',{'reward_collection_dur_In', 'timeout_state'; 'Tup','timeout_state'; HitEvent,'hit_state'}); + + sma = add_state(sma,'name','hit_state','self_timer',0.01,... + 'output_actions', {'SchedWaveTrig','reward_delivery'},... + 'input_to_statechange',{'Tup','drink_state'}); + + sma = add_state(sma,'name','drink_state','self_timer',drink_time,... + 'input_to_statechange',{'Tup','preclean_up_state'}); + + %%%%%%%%%%%%%%% FAILURE TO CENTRE POKE %%%%%%%%%%%%%%%%%%%%%% + + % For Timeout + + sma = add_state(sma,'name','timeout_state','self_timer',timeout_snd_duration,... + 'output_actions', {'SoundOut',to_sound_id; 'SchedWaveTrig', '-Go_Cue'},... + 'input_to_statechange',{'Tup','current_state+1'}); + sma = add_state(sma, 'self_timer', max(0.001, timeout_iti-timeout_snd_duration), ... + 'input_to_statechange',{'Tup','preclean_up_state'}); + + % For Violations + + sma = add_state(sma,'name','violation_state','self_timer',viol_snd_duration,... + 'output_actions',{'SoundOut',viol_sound_id; 'DOut', center1led; 'SchedWaveTrig', '-Go_Cue-stimplay-CP_Duration_wave'},... + 'input_to_statechange', {'Tup', 'current_state+1'}); + sma = add_state(sma, 'self_timer', max(0.001, violation_iti-viol_snd_duration), ... + 'input_to_statechange',{'Tup','preclean_up_state'}); + + sma = add_state(sma,'name','preclean_up_state','self_timer',0.5,... + 'input_to_statechange',{'Tup','check_next_trial_ready'}); + + varargout{2} = {'check_next_trial_ready'}; + + varargout{1} = sma; + + % Not all 'prepare_next_trial_states' are defined in all training + % stages. So we send to dispatcher only those states that are + % defined. + state_names = get_labels(sma); state_names = state_names(:,1); + prepare_next_trial_states = {'side_led_wait_RewardCollection','hit_state','second_hit_state','drink_state', 'violation_state','timeout_state','preclean_up_state'}; + + dispatcher('send_assembler', sma, intersect(state_names, prepare_next_trial_states)); + + case 'get_state_colors' + varargout{1} = struct( ... + 'wait_for_cpoke', [0.68 1 0.63], ... + 'settling_in_state', [0.63 1 0.94], ... + 'legal_poke_start_state', [0.63 1 0.94]*0.8, ... + 'legal_poke_end_state', [1 0.79 0.63], ... + 'soft_cp', [0.3 0.9 0], ... + 'side_led_wait_RewardCollection', [0.53 0.78 1.00],... + 'hit_state', [0.77 0.60 0.48], ... + 'second_hit_state', [0.25 0.45 0.48], ... + 'drink_state', [0 1 0], ... + 'violation_state', [0.31 0.48 0.30], ... + 'timeout_state', 0.8*[0.31 0.48 0.30]); + + + % 'lefthit', [0 0.9 0.3], ... + % 'error_state', [1 0.54 0.54], ... + + + % 'go_cue_on', [0.63 1 0.94]*0.6, ... + % 'prerw_postcs', [0.25 0.45 0.48], ... + % 'lefthit', [0.53 0.78 1.00], ... + % 'lefthit_pasound', [0.53 0.78 1.00]*0.7, ... + % 'righthit', [0.52 1.0 0.60], ... + % 'righthit_pasound', [0.52 1.0 0.60]*0.7, ... + % 'warning', [0.3 0 0], ... + % 'danger', [0.5 0.05 0.05], ... + % 'hit', [0 1 0] + + + case 'close' + + + case 'reinit' + currfig = double(gcf); + + % Delete all SoloParamHandles who belong to this object and whose + % fullname starts with the name of this mfile: + delete_sphandle('owner', ['^@' class(obj) '$'], ... + 'fullname', ['^' mfilename]); + + + % Reinitialise at the original GUI position and figure: + feval(mfilename, obj, 'init'); + + % Restore the current figure: + figure(currfig); + + otherwise + warning('do not know how to do %s',action); +end + + +end + + + diff --git a/Protocols/@ArpitSoundCatContinuous/ArpitSoundCatContinuousSMA.m b/Protocols/@ArpitSoundCatContinuous/ArpitSoundCatContinuousSMA.m new file mode 100644 index 00000000..996a62cf --- /dev/null +++ b/Protocols/@ArpitSoundCatContinuous/ArpitSoundCatContinuousSMA.m @@ -0,0 +1,281 @@ +function [varargout] = ArpitSoundCatContinuousSMA(obj, action) + +GetSoloFunctionArgs; + + +switch action + + case 'init' + + + case 'prepare_next_trial' + + %% Setup water + + left1led = bSettings('get', 'DIOLINES', 'left1led'); + center1led = bSettings('get', 'DIOLINES', 'center1led'); + right1led = bSettings('get', 'DIOLINES', 'right1led'); + left1water = bSettings('get', 'DIOLINES', 'left1water'); + right1water = bSettings('get', 'DIOLINES', 'right1water'); + + + %% Setup sounds + + A1_sound_id = SoundManagerSection(obj, 'get_sound_id', 'StimAUD1'); % distribution based sound + sound_duration = value(A1_time); % SoundManagerSection(obj, 'get_sound_duration', 'SOneSound'); + go_sound_id = SoundManagerSection(obj, 'get_sound_id', 'GoSound'); + go_cue_duration = value(time_go_cue); % SoundManagerSection(obj, 'get_sound_duration', 'GoSound'); + viol_sound_id = SoundManagerSection(obj, 'get_sound_id', 'ViolationSound'); + viol_snd_duration = SoundManagerSection(obj, 'get_sound_duration', 'ViolationSound'); + to_sound_id = SoundManagerSection(obj, 'get_sound_id', 'TimeoutSound'); + timeout_snd_duration = SoundManagerSection(obj, 'get_sound_duration', 'TimeoutSound'); + + [LeftWValveTime,RightWValveTime] = ParamsSection(obj, 'get_water_amount'); + side = ParamsSection(obj, 'get_current_side'); + + if side == 'l' + HitEvent = 'Lin'; + ErrorEvent = 'Rin'; + % sound_id = sone_sound_id; + SideLight = left1led; + WValveTime = LeftWValveTime; + WValveSide = left1water; + else + HitEvent = 'Rin'; + ErrorEvent = 'Lin'; + % sound_id = stwo_sound_id; + SideLight = right1led; + WValveTime = RightWValveTime; + WValveSide = right1water; + end + + + sma = StateMachineAssembler('full_trial_structure','use_happenings', 1); + + %%%%%%%%%%%%%%%% SCHEDULED WAVES %%%%%%%%%%%%%%%%%%%%%%% + + % scheduled wave for stimuli / fixed (No) sound, based upon side + if value(stimuli_on) + sma = add_scheduled_wave(sma, 'name', 'stimplay', 'preamble', PreStim_time, ... + 'sustain', sound_duration, 'sound_trig', A1_sound_id); % to play a sound before Go Cue + else + % sma = add_scheduled_wave(sma, 'name', 'stimplay', 'preamble', PreStim_time, ... + % 'sustain', sound_duration, 'sound_trig', sound_id); % to play a fixed sound before Go Cue + sma = add_scheduled_wave(sma, 'name', 'stimplay', 'preamble', PreStim_time, ... + 'sustain', sound_duration); % to play No sound before Go Cue + end + + % Scheduled Wave for Go Sound + if value(Go_Sound) == 1 + sma = add_scheduled_wave(sma, 'name', 'Go_Cue', 'preamble', 0.001, ... + 'sustain', go_cue_duration, 'sound_trig', go_sound_id); % to play the Go Cue/Reward Sound + else + sma = add_scheduled_wave(sma, 'name', 'Go_Cue', 'preamble', 0.001, ... + 'sustain', go_cue_duration); % to play the Go Cue/Reward Sound + end + + % Scheduled wave for CP Duration + if CP_duration <= (SettlingIn_time + legal_cbreak) + sma = add_scheduled_wave(sma, 'name', 'CP_Duration_wave', 'preamble', CP_duration); % total length of centre poke to consider success + else + sma = add_scheduled_wave(sma, 'name', 'settling_period', 'preamble', SettlingIn_time); % intial fidgety period without violation + sma = add_scheduled_wave(sma, 'name', 'CP_Duration_wave', 'preamble', CP_duration - SettlingIn_time); % total length of centre poke minus the inital fidgety time to consider success + end + + % scheduled wave for rewarded side either of the side + sma = add_scheduled_wave(sma, 'name', 'reward_delivery', 'preamble', reward_delay, ... + 'sustain', WValveTime, 'DOut', WValveSide); % water delivery side + sma = add_scheduled_wave(sma, 'name', 'reward_collection_dur', 'preamble', SideLed_duration + RewardCollection_duration); % time to collect the reward + + %% + %%%%%%%%%%% % *STATES* %%%%%%%%%%%%%%%%%%% + + sma = add_state(sma,'name','wait_for_cpoke','self_timer',cp_timeout, ... + 'output_actions', {'DOut', center1led}, ... + 'input_to_statechange', {'Cin','settling_in_state';'Tup','timeout_state'}); + + %%%%%%%%%%%%% SETTLING IN STATE START %%%%%%%%%%%%%%%%%%%% + % Before progressing check if its still centre poking or pokes within legal c_break other wise its a violation + + if CP_duration <= SettlingIn_time + legal_cbreak % state machine during initial warm-up when starting a new session + + sma = add_state(sma,'name','settling_in_state','self_timer',CP_duration, ... + 'output_actions', {'SchedWaveTrig', 'CP_Duration_wave'}, ... + 'input_to_statechange', {'Cout','current_state + 1';'Tup','side_led_wait_RewardCollection'}); + + % Intermediate State + + % This intermediate state is considering the poke is out before the end of settling time / at the start of this state + sma = add_state(sma,'self_timer',CP_duration,'output_actions', {'DOut', center1led}, ... + 'input_to_statechange', {'CP_Duration_wave_In','side_led_wait_RewardCollection';'Cin','current_state + 1';'Tup','side_led_wait_RewardCollection'}); + + % The state jump to here when the nose is still in then go + % directly to give reward + sma = add_state(sma,'self_timer',CP_duration,... + 'input_to_statechange', {'CP_Duration_wave_In','side_led_wait_RewardCollection'; 'Cout','current_state - 1';'Tup','side_led_wait_RewardCollection'}); + + else % the usual state machine + + sma = add_state(sma,'name','settling_in_state','self_timer',SettlingIn_time, ... + 'output_actions', {'SchedWaveTrig', 'settling_period'}, ... + 'input_to_statechange', {'Cout','current_state + 1';'Tup','soft_cp'}); + + % Intermediate State + + % This intermediate state is considering the poke is out before the end of settling time / at the start of this state + sma = add_state(sma,'self_timer',SettlingIn_time,'output_actions', {'DOut', center1led}, ... + 'input_to_statechange', {'settling_period_In','legal_poke_start_state';'Cin','current_state + 1';'Tup','legal_poke_start_state';... + 'Rin', 'violation_state';'Rout', 'violation_state'; 'Lin', 'violation_state';'Lout', 'violation_state'}); + + % The state jump to here when the nose is still in then go directly to soft_cp + sma = add_state(sma,'self_timer',SettlingIn_time,... + 'input_to_statechange', {'settling_period_In','soft_cp'; 'Cout','current_state - 1';'Tup','soft_cp';... + 'Rin', 'violation_state'; 'Rout', 'violation_state'; 'Lin', 'violation_state'; 'Lout', 'violation_state'}); + + %%%%%%%%%%%%% SETTLING IN STATE END %%%%%%%%%%%%%%%%%%%%%% + + % STATE TO CHECK BEFORE START OF LEGAL POKE PERIOD + + sma = add_state(sma,'name','legal_poke_start_state','self_timer',legal_cbreak/2, ... + 'output_actions', {'DOut', center1led}, ... + 'input_to_statechange', {'Cin','soft_cp'; 'Tup','violation_state';... + 'Rin', 'violation_state'; 'Rout', 'violation_state'; 'Lin', 'violation_state'; 'Lout', 'violation_state'}); %more stringent by giving half the legal cp time + + %%%%%%%%%%%% LEGAL SOFT POKE STATE START %%%%%%%%%%%%%%%%% + + sma = add_state(sma,'name','soft_cp','self_timer',CP_duration - SettlingIn_time, ... CP_Duration_wave + 'output_actions', {'SchedWaveTrig', 'CP_Duration_wave+stimplay'},... + 'input_to_statechange', {'Cout','current_state + 1'; 'Tup','side_led_wait_RewardCollection';... + 'Rin', 'violation_state'; 'Rout', 'violation_state'; 'Lin', 'violation_state'; 'Lout', 'violation_state'}); %more stringent by giving half the legal cp time + + end + + % Intermediate State + + % This intermediate state is considering the poke is out before the end of settling time / at the start of this state + sma = add_state(sma,'self_timer',legal_cbreak,'output_actions', {'DOut', center1led}, ... + 'input_to_statechange', {'CP_Duration_wave_In','legal_poke_end_state';'Cin','current_state + 1';'Tup','violation_state';... + 'Rin', 'violation_state';'Rout', 'violation_state'; 'Lin', 'violation_state';'Lout', 'violation_state'}); + + % The state jump to here when the nose is still in then go directly to soft_cp + sma = add_state(sma,'self_timer',CP_duration - SettlingIn_time, ... + 'input_to_statechange', {'CP_Duration_wave_In','side_led_wait_RewardCollection';'Cout','current_state - 1';'Tup','side_led_wait_RewardCollection';... + 'Rin', 'violation_state';'Rout', 'violation_state'; 'Lin', 'violation_state';'Lout', 'violation_state'}); + + %%%%%%%%%%%% LEGAL SOFT POKE STATE END %%%%%%%%%%%%%%%%% + + % Before giving the reward check if its still centre poking or pokes + % within legal c_break other wise its a violation + + sma = add_state(sma,'name','legal_poke_end_state','self_timer',legal_cbreak/2, ... + 'output_actions', {'DOut', center1led}, ... + 'input_to_statechange', {'Cin','side_led_wait_RewardCollection'; 'Tup','violation_state'}); + + + %%%%%%%%%%%%%%% REWARD COLLECTION STATE START %%%%%%%%%%%%%%% + + sma = add_state(sma, 'name', 'side_led_wait_RewardCollection', 'self_timer', SideLed_duration + RewardCollection_duration, ... + 'output_actions', {'DOut', SideLight; 'SchedWaveTrig', 'reward_collection_dur+Go_Cue'}, ... + 'input_to_statechange',{HitEvent,'hit_state'; 'Tup','timeout_state'; ErrorEvent,'second_hit_state'}); + + sma = add_state(sma,'name','second_hit_state','self_timer',RewardCollection_duration,... + 'output_actions',{'DOut', SideLight},... + 'input_to_statechange',{'reward_collection_dur_In', 'timeout_state'; 'Tup','timeout_state'; HitEvent,'hit_state'}); + + sma = add_state(sma,'name','hit_state','self_timer',0.01,... + 'output_actions', {'SchedWaveTrig','reward_delivery'},... + 'input_to_statechange',{'Tup','drink_state'}); + + sma = add_state(sma,'name','drink_state','self_timer',drink_time,... + 'input_to_statechange',{'Tup','preclean_up_state'}); + + %%%%%%%%%%%%%%% FAILURE TO CENTRE POKE %%%%%%%%%%%%%%%%%%%%%% + + % For Timeout + + sma = add_state(sma,'name','timeout_state','self_timer',timeout_snd_duration,... + 'output_actions', {'SoundOut',to_sound_id; 'SchedWaveTrig', '-Go_Cue'},... + 'input_to_statechange',{'Tup','current_state+1'}); + sma = add_state(sma, 'self_timer', max(0.001, timeout_iti-timeout_snd_duration), ... + 'input_to_statechange',{'Tup','preclean_up_state'}); + + % For Violations + + sma = add_state(sma,'name','violation_state','self_timer',viol_snd_duration,... + 'output_actions',{'SoundOut',viol_sound_id; 'DOut', center1led; 'SchedWaveTrig', '-Go_Cue-stimplay-CP_Duration_wave'},... + 'input_to_statechange', {'Tup', 'current_state+1'}); + sma = add_state(sma, 'self_timer', max(0.001, violation_iti-viol_snd_duration), ... + 'input_to_statechange',{'Tup','preclean_up_state'}); + + sma = add_state(sma,'name','preclean_up_state','self_timer',0.5,... + 'input_to_statechange',{'Tup','check_next_trial_ready'}); + + varargout{2} = {'check_next_trial_ready'}; + + varargout{1} = sma; + + % Not all 'prepare_next_trial_states' are defined in all training + % stages. So we send to dispatcher only those states that are + % defined. + state_names = get_labels(sma); state_names = state_names(:,1); + prepare_next_trial_states = {'side_led_wait_RewardCollection','hit_state','second_hit_state','drink_state', 'violation_state','timeout_state','preclean_up_state'}; + + dispatcher('send_assembler', sma, intersect(state_names, prepare_next_trial_states)); + + case 'get_state_colors' + varargout{1} = struct( ... + 'wait_for_cpoke', [0.68 1 0.63], ... + 'settling_in_state', [0.63 1 0.94], ... + 'legal_poke_start_state', [0.63 1 0.94]*0.8, ... + 'legal_poke_end_state', [1 0.79 0.63], ... + 'soft_cp', [0.3 0.9 0], ... + 'side_led_wait_RewardCollection', [0.53 0.78 1.00],... + 'hit_state', [0.77 0.60 0.48], ... + 'second_hit_state', [0.25 0.45 0.48], ... + 'drink_state', [0 1 0], ... + 'violation_state', [0.31 0.48 0.30], ... + 'timeout_state', 0.8*[0.31 0.48 0.30]); + + + % 'lefthit', [0 0.9 0.3], ... + % 'error_state', [1 0.54 0.54], ... + + + % 'go_cue_on', [0.63 1 0.94]*0.6, ... + % 'prerw_postcs', [0.25 0.45 0.48], ... + % 'lefthit', [0.53 0.78 1.00], ... + % 'lefthit_pasound', [0.53 0.78 1.00]*0.7, ... + % 'righthit', [0.52 1.0 0.60], ... + % 'righthit_pasound', [0.52 1.0 0.60]*0.7, ... + % 'warning', [0.3 0 0], ... + % 'danger', [0.5 0.05 0.05], ... + % 'hit', [0 1 0] + + + case 'close' + + + case 'reinit' + currfig = double(gcf); + + % Delete all SoloParamHandles who belong to this object and whose + % fullname starts with the name of this mfile: + delete_sphandle('owner', ['^@' class(obj) '$'], ... + 'fullname', ['^' mfilename]); + + + % Reinitialise at the original GUI position and figure: + feval(mfilename, obj, 'init'); + + % Restore the current figure: + figure(currfig); + + otherwise + warning('do not know how to do %s',action); +end + + +end + + + diff --git a/Protocols/@ArpitSoundCatContinuous/SideSection.asv b/Protocols/@ArpitSoundCatContinuous/SideSection.asv new file mode 100644 index 00000000..7ad41418 --- /dev/null +++ b/Protocols/@ArpitSoundCatContinuous/SideSection.asv @@ -0,0 +1,510 @@ + + +function [x, y] = SideSection(obj, action, x,y) + +GetSoloFunctionArgs(obj); + +switch action + + % ------------------------------------------------------------------ + % INIT + % ------------------------------------------------------------------ + + case 'init' + + SoloParamHandle(obj, 'my_gui_info', 'value', [x y double(gcf)], 'saveable', 0); + y0 = y; + + [x, y] = AntibiasSection(obj, 'init', x, y); + + + ToggleParam(obj, 'antibias_LRprob', 0, x,y,... + 'OnString', 'AB_Prob ON',... + 'OffString', 'AB_Prob OFF',... + 'TooltipString', sprintf(['If on (Yellow) then it enables the AntiBias algorithm\n'... + 'based on changing the probablity of Left vs Right'])); + + next_row(y); + NumeditParam(obj, 'LeftProb', 0.5, x, y); next_row(y); + set_callback(LeftProb, {mfilename, 'new_leftprob'}); + MenuParam(obj, 'MaxSame', {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, Inf}, Inf, x, y, ... + 'TooltipString', sprintf(['\nMaximum number of consecutive trials where correct\n' ... + 'response is on the same side. Overrides antibias. Thus, for\n' ... + 'example, if MaxSame=5 and there have been 5 Left trials, the\n' ... + 'next trial is guaranteed to be Right'])); next_row(y); + + next_row(y); + NumeditParam(obj, 'ntrial_correct_bias', 0, x, y, ... + 'TooltipString', 'antibias starts from trial=ntrial_correct_bias'); + next_row(y); + NumeditParam(obj, 'right_left_diff', .12, x, y, ... + 'TooltipString', 'antibias applies if difference between right and left sides is bigger than this number'); + next_row(y); + NumeditParam(obj, 'max_wtr_mult', 4, x, y, ... + 'TooltipString', 'wtr_mult will be min(max_wtr_mult,right_hit/left_hit)'); + next_row(y); + NumeditParam(obj, 'left_wtr_mult', 1, x, y, ... + 'TooltipString', 'all left reward times are multiplied by this number'); + next_row(y); + NumeditParam(obj, 'right_wtr_mult', 1, x, y, ... + 'TooltipString', 'all right reward times are multiplied by this number'); + next_row(y); + ToggleParam(obj, 'antibias_wtr_mult', 0, x,y,... + 'OnString', 'AB ON',... + 'OffString', 'AB OFF',... + 'TooltipString', sprintf(['If on (black) then it disables the wtr_mult entries\n'... + 'and uses hitfrac to adjust the water times'])); + + next_row(y); + + DispParam(obj, 'ThisTrial', 'LEFT', x, y); next_row(y); + SoloParamHandle(obj, 'previous_sides', 'value', []); + DeclareGlobals(obj, 'ro_args', 'previous_sides'); + SubheaderParam(obj, 'title', 'Sides Section', x, y); + next_row(y, 1.5); + %% slow ramp up of water amount + %%the water volume is controlled by a 5-parameter logistic function: WaterAmount(t) = maxasymp + (minasymp/(1+(t/inflp)^slp).^assym) + NumeditParam(obj, 'maxasymp', 38, x,y,'label','maxasymp','TooltipString',... + 'the water volume is controlled by a 5-parameter logistic function: WaterAmount(trialnum) = maxasymp + (minasymp/(1+(trialnum/inflp)^slp).^assym)'); + next_row(y); + NumeditParam(obj, 'slp', 3, x,y,'label','slp','TooltipString','Water Modulation: Slope of the logistic function'); + next_row(y); + NumeditParam(obj, 'inflp', 350, x,y,'label','inflp','TooltipString','Water Modulation: concentration at the inflection point'); + next_row(y); + NumeditParam(obj, 'minasymp', -21, x,y,'label','inflp','TooltipString','Water Modulation: minimum asymptote'); + next_row(y); + NumeditParam(obj, 'assym', 0.7, x,y,'label','assym','TooltipString','Water Modulation: asymmetry factor'); + next_row(y); + DispParam(obj, 'trial_1', 0, x, y, 'TooltipString', 'uL on first trial'); + next_row(y); + DispParam(obj, 'trial_150', 0, x, y, 'TooltipString', 'uL on trial 150'); + next_row(y); + DispParam(obj, 'trial_300', 0, x, y, 'TooltipString', 'uL on trial 300'); + next_row(y); + set_callback({maxasymp;slp;inflp;minasymp;assym}, {mfilename, 'change_water_modulation_params'}); + feval(mfilename, obj, 'change_water_modulation_params'); + + next_column(x); y = 5; + NumeditParam(obj, 'RewardCollection_duration', 10, x,y,'label','RewardCollection_duration','TooltipString','Wait until rat collects the reward'); + next_row(y); + NumeditParam(obj, 'CenterLed_duration', 200, x,y,'label','Central LED duration','TooltipString','Duration of Center Led'); + next_row(y); + NumeditParam(obj, 'SideLed_duration', 0.5, x,y,'label','Side LED duration','TooltipString','Duration of SideLed'); + next_row(y); + NumeditParam(obj, 'legal_cbreak', 0.1, x,y, 'position', [x, y, 175 20], 'TooltipString','Time in sec for which it is ok to be outside the center port before a violation occurs.'); + ToggleParam(obj, 'LED_during_legal_cbreak', 1, x, y, 'OnString', 'LED ON LcB', 'OffString', 'LED off LcB', ... + 'position', [x+180 y 20 20], 'TooltipString', ... + 'If 1 (black), turn center port LED back on during legal_cbreak; if 0 (brown), leave LED off'); + next_row(y); + NumeditParam(obj, 'SettlingIn_time', 0.2, x,y, 'position', [x, y, 175 20], 'TooltipString','Initial settling period during which "legal cbreak period" can be longer than the usual "legal_cbreak"'); + next_row(y); + NumeditParam(obj, 'settling_legal_cbreak', 0.1, x,y, 'position', [x, y, 175 20], 'TooltipString','Time in sec for which it is ok during the "SettlingIn_time" to be outside the center port before a violation occurs.'); + ToggleParam(obj, 'LED_during_settling_legal_cbreak', 0, x, y, 'OnString', 'LED ON SetLcB', 'OffString', 'LED OFF setLcB', ... + 'position', [x+180 y 20 20], 'TooltipString', ... + 'If 1 (black), turn center port LED back on during settling_legal_cbreak; if 0 (brown), leave LED off'); + next_row(y); + MenuParam(obj, 'side_lights' ,{'none','both','correct side','anti side'},1, x,y,'label','Side Lights','TooltipString','Controls the side LEDs during wait_for_spoke'); + next_row(y); + + + NumeditParam(obj, 'A1_time', 0.4, x,y,'label','AUD1 on Time','TooltipString','Duration of first stimulus'); + next_row(y); + set_callback(A1_time, {mfilename, 'new_CP_duration'}); + NumeditParam(obj, 'PreStim_time', 0.20, x,y,'label','Pre-Stim NIC time','TooltipString','Time in NIC before starting the stimulus'); + next_row(y); + set_callback(PreStim_time, {mfilename, 'new_CP_duration'}); + NumeditParam(obj, 'time_bet_aud1_gocue', 0, x,y,'label','A1-GoCue time','TooltipString','time between the end of the stimulus and the go cue '); + next_row(y); + set_callback(time_bet_aud1_gocue, {mfilename, 'new_CP_duration'}); + DispParam(obj, 'init_CP_duration', 0.01, x,y,'label','init_CP duration','TooltipString','Duration of Nose in Central Poke before Go cue starts (see Total_CP_duration)'); + next_row(y); + DispParam(obj, 'CP_duration', PreStim_time+A1_time+time_bet_aud1_gocue, x,y,'label','CP duration','TooltipString','Duration of Nose in Central Poke before Go cue starts (see Total_CP_duration)'); + set_callback(CP_duration, {mfilename, 'new_CP_duration'}); + next_row(y); + NumeditParam(obj, 'time_go_cue' ,0, x,y,'label','Go Cue Duration','TooltipString','duration of go cue (see Total_CP_duration)'); + set_callback(time_go_cue, {mfilename, 'new_time_go_cue'}); + next_row(y); + DispParam(obj, 'Total_CP_duration', CP_duration+time_go_cue, x, y, 'TooltipString', 'Total nose in center port time, in secs. Sum of CP_duration and Go Cue duration'); %#ok<*NODEF> + next_row(y); + ToggleParam(obj, 'warmup_on', 1, x,y,... + 'OnString', 'Warmup ON',... + 'OffString', 'Warmup OFF',... + 'TooltipString', sprintf(['If on (Yellow) then it applies the initial warming up phase, during which the\n',... + 'CP_duration starts small and gradually grows to last_session_max_cp_duration'])); + next_row(y); + % to modify it for widefield imagine + NumeditParam(obj,'imaging',0,x,y,'label','Scope Trigger'); + next_row(y); + NumeditParam(obj, 'reward_delay', 0.01, x,y,'label','Reward Delay','TooltipString','Delay between side poke and reward delivery'); + next_row(y); + NumeditParam(obj, 'reward_duration', 0.1, x,y,'label','Reward Duration','TooltipString','Duration of reward sound'); + next_row(y); + NumeditParam(obj, 'drink_time', 1, x,y,'label','Drink Time','TooltipString','waits to finish water delivery'); + next_row(y); + NumeditParam(obj, 'error_iti', 5, x,y,'label','Error Timeout','TooltipString','ITI on error trials'); + next_row(y); + NumeditParam(obj, 'violation_iti', 1, x,y,'label','Violation Timeout','TooltipString','Center poke violation duration'); + next_row(y); + MenuParam(obj, 'reward_type', {'Always','DelayedReward', 'NoReward'}, ... + 'Always', x, y, 'labelfraction', 0.35, 'TooltipString', sprintf(['\nThis menu is to determine the Reward delivery on wrong-hit trials\n',... + '\nIf ''Always'': reward will be available on each trial no matter which side rat goes first\n',... + '\n If rat pokes first on the wrong side, then reward will be delivered with a delay (if DelayedReward) or not delivered at all (if NoReward)'])); + set_callback(reward_type, {mfilename, 'new_reward_type'}); + next_row(y); + NumeditParam(obj,'secondhit_delay',0,x,y,'label','SecondHit Delay','TooltipString','Reward will be delayed with this amount if reward_type=DelayedReward'); + + next_row(y); + ToggleParam(obj, 'random_A1_time', 0, x,y,... + 'OnString', 'random A1_time ON',... + 'OffString', 'random A1_time OFF',... + 'TooltipString', sprintf('If on (black) then it enables the random sampling of A1_time')); + next_row(y); + ToggleParam(obj, 'random_prego_time', 0, x,y,... + 'OnString', 'random prego_time ON',... + 'OffString', 'random prego_time OFF',... + 'TooltipString', sprintf('If on (black) then it enables the random sampling of time between the end of the stimulus and the go cue')); + + next_column(x); + y=5; + NumeditParam(obj,'trials_in_stage',1,x,y,'label','Trial Counter'); + next_row(y); + NumeditParam(obj,'training_stage',1,x,y,'label','Training Stage'); + next_row(y); + ToggleParam(obj,'use_training',0,x,y,'OnString','Using Autotrain','OffString','Manual Settings'); + + + ToggleParam(obj, 'stimuli_on', 1, x,y,... + 'OnString', 'Stimuli ON',... + 'OffString', 'Stimuli OFF',... + 'TooltipString', sprintf('If on (black) then it disable the presentation of sound stimuli during nose poke')); + set_callback(stimuli_on, {mfilename, 'new_CP_duration'}); + + next_row(y); + SoloFunctionAddVars('SoundCatSMA', 'ro_args', ... + {'CP_duration';'SideLed_duration';'CenterLed_duration';'side_lights' ; ... + 'RewardCollection_duration';'training_stage'; ... + 'legal_cbreak' ; 'LED_during_legal_cbreak' ; ... + 'SettlingIn_time';'settling_legal_cbreak' ; 'LED_during_settling_legal_cbreak' ; ... + 'time_go_cue'; ... + 'stimuli_on';'A1_time';'time_bet_aud1_gocue' ; ... + 'PreStim_time';'warmup_on' + 'drink_time';'reward_delay';'reward_duration';'left_wtr_mult';... + 'right_wtr_mult';'antibias_wtr_mult';... + 'reward_type';'secondhit_delay';'error_iti';'violation_iti';'imaging'}); + + SoloFunctionAddVars('StimulusSection', 'ro_args', ... + {'ThisTrial';'A1_time';'time_bet_aud1_gocue' ; ... + 'PreStim_time'}); + SoloFunctionAddVars('StimulatorSection', 'ro_args', ... + {'A1_time';'time_bet_aud1_gocue';'time_go_cue'; ... + 'PreStim_time';'CP_duration';'Total_CP_duration'}); + + % History of hit/miss: + SoloParamHandle(obj, 'deltaf_history', 'value', []); + + SoloFunctionAddVars('OverallPerformanceSection', 'ro_args', ... + {'training_stage'}); + + SoloFunctionAddVars('SoundCatSMA', 'ro_args', ... + {'maxasymp';'slp';'inflp';'minasymp';'assym'}); + + SoloParamHandle(obj, 'previous_parameters', 'value', []); + + + %% change_water_modulation_params + case 'change_water_modulation_params', + display_guys = [1 150 300]; + for i=1:numel(display_guys), + t = display_guys(i); + + myvar = eval(sprintf('trial_%d', t)); + myvar.value = maxasymp + (minasymp/(1+(t/inflp)^slp).^assym); + end; + + + case 'new_leftprob' + AntibiasSectionAthena(obj, 'update_biashitfrac', value(LeftProb)); + + + case 'new_CP_duration' + if stimuli_on == 0 + PreStim_time.value=0; + A1_time.value=0; + time_bet_aud1_gocue.value=0; + disable(PreStim_time); + disable(A1_time); + disable(time_bet_aud1_gocue); + else + enable(PreStim_time); + enable(A1_time); + enable(time_bet_aud1_gocue); + end + CP_duration.value=PreStim_time + A1_time + time_bet_aud1_gocue; + Total_CP_duration.value = CP_duration + time_go_cue; %#ok<*NASGU> + + case 'new_time_go_cue' + Total_CP_duration.value = CP_duration + time_go_cue; + SoundInterface(obj, 'set', 'GoSound', 'Dur1', value(time_go_cue)); + + case 'new_reward_type' + if strcmp(reward_type,'DelayedReward') + enable(secondhit_delay) + else + secondhit_delay=0; + disable(secondhit_delay) + + end + + + case 'prepare_next_trial' + + + switch value(training_stage) + case 0 %% learning the reward sound association -left or right led on -> poke -> sound+reward + settling_time.value=0.01; + delay_time.value=0; + allow_nic_breaks.value=1; + side_lights.value=3; + trials_in_stage.value=0; + reward_delay.value=0.01; + left_prob.value=0.5; + right_prob.value=0.5; + time_go_cue.value=0.200; + reward_duration=0.200; + + + case 1 %% center led on -> poke in the center -> go cue -> reward light and sound + %what A1_time and time before go cue? + if random_A1_time + A1_times = [0.2, 0.4]; + randomix = randi(length(A1_times), 1); + A1_time.value = A1_times(randomix); + end + + if random_prego_time + prego_times = [0.2, 0.4, 0.6, 0.8, 1]; + randomixx = randi(length(prego_times), 1); + time_bet_aud1_gocue.value = prego_times(randomixx); + end + settling_time.value=0.25; + delay_time.value=0; + allow_nic_breaks.value=1; + side_lights.value=3; + training_stage.value=1; + trials_in_stage.value=0; + reward_delay.value=0.01; + left_prob.value=0.5; + right_prob.value=0.5; + if n_done_trials <1 && warmup_on ==1 + CP_duration.value=value(init_CP_duration); + else + CP_duration.value=PreStim_time + A1_time + time_bet_aud1_gocue; + end + Total_CP_duration.value = CP_duration + time_go_cue; %#ok<*NASGU> + + + case 3 % like stage 2, now passive exposure to the stimuli - reward comes anyway + case 4 %% now reward comes only if rat goes to the correct side + + end + + + %% update hit_history, previous_sides, etc + was_viol=false; + was_hit=false; + was_timeout=false; + if n_done_trials>0 + if ~isempty(parsed_events) + if isfield(parsed_events,'states') + if isfield(parsed_events.states,'timeout_state') + was_timeout=rows(parsed_events.states.timeout_state)>0; + end + if isfield(parsed_events.states,'violation_state') + was_viol=rows(parsed_events.states.violation_state)>0; + end + end + + end + + violation_history.value=[violation_history(:); was_viol]; + timeout_history.value=[timeout_history(:); was_timeout]; + + SideSection(obj,'update_side_history'); + + if ~was_viol && ~was_timeout + %was_hit=rows(parsed_events.states.hit_state)>0; + was_hit=rows(parsed_events.states.second_hit_state)==0; + hit_history.value=[hit_history(:); was_hit]; + + else + % There was a violation or timeout + hit_history.value=[hit_history(:); nan]; + end + + % Now calculate the deltaF and sides - this maybe interesting + % even in a violation or timeout case. + + fn=fieldnames(parsed_events.states); + led_states=find(strncmp('led',fn,3)); + deltaF=0; + n_l=0; + n_r=0; + for lx=1:numel(led_states) + lind=led_states(lx); + if rows(parsed_events.states.(fn{lind}))>0 + if fn{lind}(end)=='l' + deltaF=deltaF-1; + n_l=n_l+1; + elseif fn{lind}(end)=='r' + deltaF=deltaF+1; + n_r=n_r+1; + elseif fn{lind}(end)=='b' + n_l=n_l+1; + n_r=n_r+1; + + end + end + + end + + % if deltaF>0 then a right poke is a hit + % if deltaF<0 then a left poke is a hit + + deltaf_history.value=[deltaf_history(:); deltaF]; + + end + + if antibias_LRprob ==1 + if n_done_trials >ntrial_correct_bias && ~was_viol && ~was_timeout + nonan_hit_history=value(hit_history); + nonan_hit_history(isnan(nonan_hit_history))=[]; + nonan_previous_sides=value(previous_sides); + nan_history=value(hit_history); + nonan_previous_sides(isnan(nan_history))=[]; + AntibiasSection(obj, 'update', value(LeftProb), nonan_hit_history(:)',nonan_previous_sides(:)); % <~> Transposed hit history so that it is the expected column vector. (Antibias errors out otherwise.) 2007.09.05 01:39 + end + + + if ~isinf(MaxSame) && length(previous_sides) > MaxSame && ... + all(previous_sides(n_done_trials-MaxSame+1:n_done_trials) == previous_sides(n_done_trials)) %#ok + if previous_sides(end)=='l' + ThisTrial.value = 'RIGHT'; + else + ThisTrial.value = 'LEFT'; + end + else + choiceprobs = AntibiasSectionAthena(obj, 'get_posterior_probs'); + if rand(1) <= choiceprobs(1) + ThisTrial.value = 'LEFT'; + else + ThisTrial.value = 'RIGHT'; + end + end + + else + if (rand(1)<=LeftProb) + ThisTrial.value='LEFT'; + + else + ThisTrial.value='RIGHT'; + end + + end + + + + + %% Do the anti-bias with changing reward delivery + % reset anti-bias + left_wtr_mult.value=1; + right_wtr_mult.value=1; + if n_done_trials>ntrial_correct_bias && antibias_wtr_mult==1 + hh=hit_history(n_done_trials-ntrial_correct_bias:n_done_trials); + ps=previous_sides(n_done_trials-ntrial_correct_bias:n_done_trials); + + right_hit=nanmean(hh(ps=='r')); + left_hit=nanmean(hh(ps=='l')); + + if abs(right_hit-left_hit) + + case 'get_left_prob' + x = value(LeftProb); + + case 'get_cp_history' + x = cell2mat(get_history(CP_duration)); + + case 'get_stimdur_history' + x = cell2mat(get_history(A1_time)); + + case 'update_side_history' + if strcmp(ThisTrial, 'LEFT') + ps=value(previous_sides); + ps(n_done_trials)='l'; + previous_sides.value=ps; + + else + ps=value(previous_sides); + ps(n_done_trials)='r'; + previous_sides.value=ps; + end; + + case 'get_current_side' + if strcmp(ThisTrial, 'LEFT') + x = 'l'; %#ok + else + x = 'r'; + end; + + + case 'close' + % Delete all SoloParamHandles who belong to this object and whose + % fullname starts with the name of this mfile: + delete_sphandle('owner', ['^@' class(obj) '$'], ... + 'fullname', ['^' mfilename]); + + case 'reinit', + currfig = double(gcf); + + % Get the original GUI position and figure: + x = my_gui_info(1); y = my_gui_info(2); figure(my_gui_info(3)); + + % Delete all SoloParamHandles who belong to this object and whose + % fullname starts with the name of this mfile: + delete_sphandle('owner', ['^@' class(obj) '$'], ... + 'fullname', ['^' mfilename]); + + % Reinitialise at the original GUI position and figure: + [x, y] = feval(mfilename, obj, 'init', x, y); + + % Restore the current figure: + figure(currfig); + +end + + diff --git a/Protocols/@ArpitSoundCatContinuous/SideSection.m b/Protocols/@ArpitSoundCatContinuous/SideSection.m index c46b644f..ab8ded75 100644 --- a/Protocols/@ArpitSoundCatContinuous/SideSection.m +++ b/Protocols/@ArpitSoundCatContinuous/SideSection.m @@ -15,7 +15,7 @@ SoloParamHandle(obj, 'my_gui_info', 'value', [x y double(gcf)], 'saveable', 0); y0 = y; - [x, y] = AntibiasSectionAthena(obj, 'init', x, y); + [x, y] = AntibiasSection(obj, 'init', x, y); ToggleParam(obj, 'antibias_LRprob', 0, x,y,... @@ -32,6 +32,30 @@ 'response is on the same side. Overrides antibias. Thus, for\n' ... 'example, if MaxSame=5 and there have been 5 Left trials, the\n' ... 'next trial is guaranteed to be Right'])); next_row(y); + + next_row(y); + NumeditParam(obj, 'ntrial_correct_bias', 0, x, y, ... + 'TooltipString', 'antibias starts from trial=ntrial_correct_bias'); + next_row(y); + NumeditParam(obj, 'right_left_diff', .12, x, y, ... + 'TooltipString', 'antibias applies if difference between right and left sides is bigger than this number'); + next_row(y); + NumeditParam(obj, 'max_wtr_mult', 4, x, y, ... + 'TooltipString', 'wtr_mult will be min(max_wtr_mult,right_hit/left_hit)'); + next_row(y); + NumeditParam(obj, 'left_wtr_mult', 1, x, y, ... + 'TooltipString', 'all left reward times are multiplied by this number'); + next_row(y); + NumeditParam(obj, 'right_wtr_mult', 1, x, y, ... + 'TooltipString', 'all right reward times are multiplied by this number'); + next_row(y); + ToggleParam(obj, 'antibias_wtr_mult', 0, x,y,... + 'OnString', 'AB ON',... + 'OffString', 'AB OFF',... + 'TooltipString', sprintf(['If on (black) then it disables the wtr_mult entries\n'... + 'and uses hitfrac to adjust the water times'])); + + next_row(y); DispParam(obj, 'ThisTrial', 'LEFT', x, y); next_row(y); SoloParamHandle(obj, 'previous_sides', 'value', []); @@ -148,29 +172,7 @@ next_row(y); ToggleParam(obj,'use_training',0,x,y,'OnString','Using Autotrain','OffString','Manual Settings'); - next_row(y); - NumeditParam(obj, 'ntrial_correct_bias', 0, x, y, ... - 'TooltipString', 'antibias starts from trial=ntrial_correct_bias'); - next_row(y); - NumeditParam(obj, 'right_left_diff', .12, x, y, ... - 'TooltipString', 'antibias applies if difference between right and left sides is bigger than this number'); - next_row(y); - NumeditParam(obj, 'max_wtr_mult', 4, x, y, ... - 'TooltipString', 'wtr_mult will be min(max_wtr_mult,right_hit/left_hit)'); - next_row(y); - NumeditParam(obj, 'left_wtr_mult', 1, x, y, ... - 'TooltipString', 'all left reward times are multiplied by this number'); - next_row(y); - NumeditParam(obj, 'right_wtr_mult', 1, x, y, ... - 'TooltipString', 'all right reward times are multiplied by this number'); - next_row(y); - ToggleParam(obj, 'antibias_wtr_mult', 0, x,y,... - 'OnString', 'AB ON',... - 'OffString', 'AB OFF',... - 'TooltipString', sprintf(['If on (black) then it disables the wtr_mult entries\n'... - 'and uses hitfrac to adjust the water times'])); - - next_row(y); + ToggleParam(obj, 'stimuli_on', 1, x,y,... 'OnString', 'Stimuli ON',... 'OffString', 'Stimuli OFF',... @@ -256,79 +258,57 @@ case 'prepare_next_trial' - - switch value(training_stage) - case 0 %% learning the reward sound association -left or right led on -> poke -> sound+reward - settling_time.value=0.01; - delay_time.value=0; - allow_nic_breaks.value=1; - side_lights.value=3; - trials_in_stage.value=0; - reward_delay.value=0.01; - left_prob.value=0.5; - right_prob.value=0.5; - time_go_cue.value=0.200; - reward_duration=0.200; - - - case 1 %% center led on -> poke in the center -> go cue -> reward light and sound - %what A1_time and time before go cue? - if random_A1_time - A1_times = [0.2, 0.4]; - randomix = randi(length(A1_times), 1); - A1_time.value = A1_times(randomix); - end - - if random_prego_time - prego_times = [0.2, 0.4, 0.6, 0.8, 1]; - randomixx = randi(length(prego_times), 1); - time_bet_aud1_gocue.value = prego_times(randomixx); - end - settling_time.value=0.25; - delay_time.value=0; - allow_nic_breaks.value=1; - side_lights.value=3; - training_stage.value=1; - trials_in_stage.value=0; - reward_delay.value=0.01; - left_prob.value=0.5; - right_prob.value=0.5; - if n_done_trials <1 && warmup_on ==1 - CP_duration.value=value(init_CP_duration); - else - CP_duration.value=PreStim_time + A1_time + time_bet_aud1_gocue; - end - Total_CP_duration.value = CP_duration + time_go_cue; %#ok<*NASGU> - + if random_A1_time + A1_times = [0.2, 0.4]; + randomix = randi(length(A1_times), 1); + A1_time.value = A1_times(randomix); + end + + if random_prego_time + prego_times = [0.2, 0.4, 0.6, 0.8, 1]; + randomixx = randi(length(prego_times), 1); + time_bet_aud1_gocue.value = prego_times(randomixx); + end + settling_time.value=0.25; + delay_time.value=0; + allow_nic_breaks.value=1; + side_lights.value=3; + training_stage.value=1; + trials_in_stage.value=0; + reward_delay.value=0.01; + left_prob.value=0.5; + right_prob.value=0.5; + if n_done_trials <1 && warmup_on ==1 + CP_duration.value=value(init_CP_duration); + else + CP_duration.value=PreStim_time + A1_time + time_bet_aud1_gocue; + end + Total_CP_duration.value = CP_duration + time_go_cue; %#ok<*NASGU> + - case 3 % like stage 2, now passive exposure to the stimuli - reward comes anyway - case 4 %% now reward comes only if rat goes to the correct side - - end - %% update hit_history, previous_sides, etc was_viol=false; was_hit=false; was_timeout=false; - if n_done_trials>0 - if ~isempty(parsed_events) - if isfield(parsed_events,'states') - if isfield(parsed_events.states,'timeout_state') - was_timeout=rows(parsed_events.states.timeout_state)>0; + if n_done_trials>0 + if ~isempty(parsed_events) + if isfield(parsed_events,'states') + if isfield(parsed_events.states,'timeout_state') + was_timeout=rows(parsed_events.states.timeout_state)>0; end if isfield(parsed_events.states,'violation_state') - was_viol=rows(parsed_events.states.violation_state)>0; - end - end - - end - - violation_history.value=[violation_history(:); was_viol]; - timeout_history.value=[timeout_history(:); was_timeout]; + was_viol=rows(parsed_events.states.violation_state)>0; + end + end + + end + + violation_history.value=[violation_history(:); was_viol]; + timeout_history.value=[timeout_history(:); was_timeout]; + + SideSection(obj,'update_side_history'); - SideSection(obj,'update_side_history'); - if ~was_viol && ~was_timeout %was_hit=rows(parsed_events.states.hit_state)>0; was_hit=rows(parsed_events.states.second_hit_state)==0; @@ -374,31 +354,35 @@ if antibias_LRprob ==1 if n_done_trials >ntrial_correct_bias && ~was_viol && ~was_timeout - nonan_hit_history=value(hit_history); - nonan_hit_history(isnan(nonan_hit_history))=[]; - nonan_previous_sides=value(previous_sides); - nan_history=value(hit_history); - nonan_previous_sides(isnan(nan_history))=[]; - AntibiasSectionAthena(obj, 'update', value(LeftProb), nonan_hit_history(:)',nonan_previous_sides(:)); % <~> Transposed hit history so that it is the expected column vector. (Antibias errors out otherwise.) 2007.09.05 01:39 + nonan_hit_history=value(hit_history); + nonan_hit_history(isnan(nonan_hit_history))=[]; + nonan_previous_sides=value(previous_sides); + nan_history=value(hit_history); + nonan_previous_sides(isnan(nan_history))=[]; + AntibiasSection(obj, 'update', value(LeftProb), nonan_hit_history(:)',nonan_previous_sides(:)); % <~> Transposed hit history so that it is the expected column vector. (Antibias errors out otherwise.) 2007.09.05 01:39 end - - + + if ~isinf(MaxSame) && length(previous_sides) > MaxSame && ... - all(previous_sides(n_done_trials-MaxSame+1:n_done_trials) == previous_sides(n_done_trials)), %#ok - if previous_sides(end)=='l', ThisTrial.value = 'RIGHT'; - else ThisTrial.value = 'LEFT'; - end; + all(previous_sides(n_done_trials-MaxSame+1:n_done_trials) == previous_sides(n_done_trials)) %#ok + if previous_sides(end)=='l' + ThisTrial.value = 'RIGHT'; + else + ThisTrial.value = 'LEFT'; + end else choiceprobs = AntibiasSectionAthena(obj, 'get_posterior_probs'); - if rand(1) <= choiceprobs(1), ThisTrial.value = 'LEFT'; - else ThisTrial.value = 'RIGHT'; - end; - end; - + if rand(1) <= choiceprobs(1) + ThisTrial.value = 'LEFT'; + else + ThisTrial.value = 'RIGHT'; + end + end + else if (rand(1)<=LeftProb) ThisTrial.value='LEFT'; - + else ThisTrial.value='RIGHT'; end @@ -408,26 +392,25 @@ -% %% Do the anti-bias with changing reward delivery -% % reset anti-bias -% left_wtr_mult.value=1; -% right_wtr_mult.value=1; -% if n_done_trials>ntrial_correct_bias && antibias_wtr_mult==1 -% hh=hit_history(n_done_trials-ntrial_correct_bias:n_done_trials); -% ps=previous_sides(n_done_trials-ntrial_correct_bias:n_done_trials); -% -% right_hit=nanmean(hh(ps=='r')); -% left_hit=nanmean(hh(ps=='l')); -% -% if abs(right_hit-left_hit)ntrial_correct_bias && antibias_wtr_mult==1 + hh=hit_history(n_done_trials-ntrial_correct_bias:n_done_trials); + ps=previous_sides(n_done_trials-ntrial_correct_bias:n_done_trials); + + right_hit=nanmean(hh(ps=='r')); + left_hit=nanmean(hh(ps=='l')); + + if abs(right_hit-left_hit) case 'get_left_prob' @@ -467,14 +450,14 @@ ps=value(previous_sides); ps(n_done_trials)='r'; previous_sides.value=ps; - end; + end case 'get_current_side' - if strcmp(ThisTrial, 'LEFT') - x = 'l'; %#ok - else - x = 'r'; - end; + if strcmp(ThisTrial, 'LEFT') + x = 'l'; %#ok + else + x = 'r'; + end case 'close' @@ -483,7 +466,7 @@ delete_sphandle('owner', ['^@' class(obj) '$'], ... 'fullname', ['^' mfilename]); - case 'reinit', + case 'reinit' currfig = double(gcf); % Get the original GUI position and figure: From 4d83f16b7acb73d5545336d5eaac5949dd154c00 Mon Sep 17 00:00:00 2001 From: Arpit Date: Wed, 4 Jun 2025 16:51:30 +0100 Subject: [PATCH 120/164] changed video save directory --- .../@bonsaicamera/BonsaiCameraInterface.m | 9 +- .../AntibiasSection.asv | 358 ------------ .../ArpitSoundCatContinuousSMA.asv | 281 ---------- .../@ArpitSoundCatContinuous/SideSection.asv | 510 ------------------ 4 files changed, 7 insertions(+), 1151 deletions(-) delete mode 100644 Protocols/@ArpitSoundCatContinuous/AntibiasSection.asv delete mode 100644 Protocols/@ArpitSoundCatContinuous/ArpitSoundCatContinuousSMA.asv delete mode 100644 Protocols/@ArpitSoundCatContinuous/SideSection.asv diff --git a/ExperPort/Plugins/@bonsaicamera/BonsaiCameraInterface.m b/ExperPort/Plugins/@bonsaicamera/BonsaiCameraInterface.m index c4c93eb9..b3dbc404 100644 --- a/ExperPort/Plugins/@bonsaicamera/BonsaiCameraInterface.m +++ b/ExperPort/Plugins/@bonsaicamera/BonsaiCameraInterface.m @@ -173,9 +173,14 @@ rat_name = varargin{5}; end + + % Changing the Video file save location from C:\ratter_Videos to + % C:\ ratter\training_videos + + % ratter_dir = extractBefore(current_dir,'ratter'); + % main_dir_video = [current_dir 'ratter_Videos']; current_dir = cd; - ratter_dir = extractBefore(current_dir,'ratter'); - main_dir_video = [ratter_dir 'ratter_Videos']; + main_dir_video = [current_dir '\training_videos']; date_str = regexprep(char(datetime('today','Format','yyyy-MM-dd')), '[^0-9]', ''); video_foldername = sprintf('video_@%s_%s_%s_%s',protocol_name,experimenter_name,rat_name,date_str); rat_dir = sprintf('%s\\%s\\%s',main_dir_video,experimenter_name,rat_name); diff --git a/Protocols/@ArpitSoundCatContinuous/AntibiasSection.asv b/Protocols/@ArpitSoundCatContinuous/AntibiasSection.asv deleted file mode 100644 index 90fbfd67..00000000 --- a/Protocols/@ArpitSoundCatContinuous/AntibiasSection.asv +++ /dev/null @@ -1,358 +0,0 @@ -% [x, y] = AntibiasSection(obj, action, [arg1], [arg2], [arg3]) -% -% Section that calculates biases and calculates probability of choosing a stimulus -% given the previous history. -% -% Antibias assumes that trials are of two classes, Left desired answer -% and Right desired answer, and that their outcome is either Correct or -% Incorrect. Given the history of previous trial classes, and the history -% of previous corrects/incorrects, Antibias makes a local estimate of -% fraction correct for each class, combines that with a prior probability -% of making the next trial Left, and produces a recommended probability -% for choosing the next trial as Left. Antibias will tend to make the -% class with the smaller frac correct the one with the higher probability. -% The strength of that tendency is quantified by a parameter, beta. -% (See probabilistic_trial_selector.m for details on the math of how the -% tendency is generated.) -% -% Local estimates of fraction correct are computed using an exponential -% kernel, most recent trial the most strongly weighted. The tau of this -% kernel is a GUI parameter. Two different estimates are computed: one for -% use in computing Left probability and Right probability; and a second -% simply for GUI display purposes. The two estimates can have different -% taus for their kernels. -% -% GUI DISPLAY: When initialized, this plugin will put up two panels and a -% title. In each panel, there is a slider that controls the tau of the -% recent-trials exponential kernel. One panel will display the percent -% corrects for Right and Left, as computed with its kernel. The second panel -% will display the a posteriori probabilities of making the next trial a -% "Left" trial or making the next trial a "Right" trial. This second panel -% has its own tau slider, and it also has a GUI parameter, beta, that -% controls how strongly the history matters. If beta=0, history doesn't -% matter, and the a priori LeftProbability dominates. If beta=Inf, then -% history matters above all: the next trial will be of the type with lowest -% fraction correct, for sure. -% -% See the bottom of this help file for examples of usage. -% -% arg1, arg2, arg3 are optional and their meaning depends on action (see -% below). -% -% PARAMETERS: -% ----------- -% -% obj Default object argument. -% -% action One of: -% -% 'init' To initialise the plugin and set up the GUI for it. This -% action requires two more arguments: The bottom left -% x y position (in units of pixels) of where to start placing -% the GUI elements of this plugin. -% -% 'update' This call will recompute both local estimates of -% fraction correct, and will recompute the recommended -% LeftProb, p(Left). This action requires three more arguments: -% HitHist, LProb, a scalar b/w 0 and 1; HitHist, a vector of 1s -% SidesHist and 0s and of length n_done_trials where 1 represents -% correct and 0 represents incorrect, first element -% corresponds to first trial; and SidesHist, a vector of -% 'l's and 'r's and of length n_done_trials where 'l' -% represents 'left', 'r' represents 'right' first element -% corresponds to first trial. -% -% 'get_posterior_probs' Returns a vector with two components, -% [p(Left) p(Right)]. -% -% -% 'update_biashitfrac' This call will recompute the local estimate of fraction -% Left correct and fraction Right correct used for -% LeftProb, antibiasing, and will also recompute the recommended -% HitHist, Left probability. This action -% SidesHist, requires three more arguments: LProb, a scalar b/w 0 -% and 1; HitHist, a vector of 1s and 0s and of length -% n_done_trials where 1 represents correct and 0 -% represents incorrect, first element corresponds to -% first trial; and SidesHist, a vector of 'l's and 'r's -% and of length n_done_trials where 'l' represents -% 'left', 'r' represents 'right' first element -% corresponds to first trial. -% -% 'update_hitfrac' This call is not related to computing the posterior -% Left probability, but will recompute only the local estimate -% HitHist, of fraction correct that is not used for antibiasing. -% SidesHist This action requires two more arguments: HitHist, a -% vector of 1s and 0s and of length n_done_trials where 1 -% represents correct and 0 represents incorrect, first -% element corresponds to first trial; and SidesHist, a vector of -% 'l's and 'r's and of length n_done_trials where 'l' -% represents 'left', 'r' represents 'right' first element -% corresponds to first trial. -% -% 'get' Needs one extra parameter, either 'Beta' or -% 'antibias_tau', and returns the corresponding scalar. -% -% 'reinit' Delete all of this section's GUIs and data, -% and reinit, at the same position on the same -% figure as the original section GUI was placed. -% -% -% x, y Relevant to action = 'init'; they indicate the initial -% position to place the GUI at, in the current figure window -% -% RETURNS: -% -------- -% -% if action == 'init' : -% -% [x1, y1, w, h] When action == 'init', Antibias will put up GUIs and take -% up a certain amount of space of the figure that was current when -% AntiBiasSection(obj, 'init', x, y) was called. On return, [x1 y1] -% will be the top left corner of the space used; [x y] (as passed -% to Antibias in the init call) will be the bottom left corner; -% [x+w y1] will be the top right; and [x+w y] will be the bottom -% right. h = y1-y. All these are in units of pixels. -% -% -% if action == 'get_posterior_probs' : -% -% [L R] When action == 'get_posterior_probs', a two-component vector is -% returned, with p(Left) and p(Right). If beta=0, then p(Left) -% will be the same as the last LeftProb that was passed in. -% -% -% USAGE: -% ------ -% -% To use this plugin, the typical calls would be: -% -% 'init' : On initializing your protocol, call -% AntibiasSection(obj, 'init', x, y); -% -% 'update' : After a trial is completed, call -% AntibiasSection(obj, 'update', LeftProb, HitHist, SidesHist) -% -% 'get_posterior_probs' : After a trial is completed, and when you are -% deciding what kind of trial to make the next trial, get the plugins -% opinion on whether the next trial should be Left or Right by calling -% AntibiasSection(obj, 'get_posterior_probs') -% -% See PARAMETERS section above for the documentation of each of these calls. -% - - -function [x, y, w, h] = AntibiasSection(obj, action, varargin) - -GetSoloFunctionArgs(obj); - -switch action - - case 'init' % ------------ CASE INIT ---------------- - x = varargin{1}; y = varargin{2}; y0 = y; - % Save the figure and the position in the figure where we are - % going to start adding GUI elements: - SoloParamHandle(obj, 'my_gui_info', 'value', [x y double(gcf)]); - - LogsliderParam(obj, 'HitFracTau', 30, 10, 400, x, y, 'label', 'hits frac tau', ... - 'TooltipString', ... - sprintf(['\nnumber of trials back over which to compute fraction of correct trials.\n' ... - 'This is just for displaying info-- for the bias calculation, see BiasTau above'])); - set_callback(HitFracTau, {mfilename, 'update_hitfrac'}); - next_row(y); - DispParam(obj, 'LtHitFrac', 0, x, y); next_row(y); - DispParam(obj, 'RtHitFrac', 0, x, y); next_row(y); - DispParam(obj, 'HitFrac', 0, x, y); next_row(y); - - next_row(y, 0.5); - - LogsliderParam(obj, 'BiasTau', 30, 10, 400, x, y, 'label', 'antibias tau', ... - 'TooltipString', ... - sprintf(['\nnumber of trials back over\nwhich to compute fraction of correct trials\n' ... - 'for the antibias function.'])); next_row(y); - NumeditParam(obj, 'Beta', 0, x, y, ... - 'TooltipString', ... - sprintf(['When this is 0, past performance doesn''t affect choice\n' ... - 'of next trial. When this is large, the next trial is ' ... - 'almost guaranteed\nto be the one with smallest %% correct'])); next_row(y); - set_callback({BiasTau, Beta}, {mfilename, 'update_biashitfrac'}); - DispParam(obj, 'LtProb', 0, x, y); next_row(y); - DispParam(obj, 'RtProb', 0, x, y); next_row(y); - SoloParamHandle(obj, 'BiasLtHitFrac', 'value', 0); - SoloParamHandle(obj, 'BiasRtHitFrac', 'value', 0); - - SoloParamHandle(obj, 'LocalLeftProb', 'value', 0.5); - SoloParamHandle(obj, 'LocalHitHistory', 'value', []); - SoloParamHandle(obj, 'LocalPrevSides', 'value', []); - - - SubheaderParam(obj, 'title', mfilename, x, y); - next_row(y, 1.5); - - w = gui_position('get_width'); - h = y-y0; - - - case 'update' % --- CASE UPDATE ------------------- - if ~isempty(varargin) - LocalLeftProb.value = varargin{1}; - end - if length(varargin)>1 - LocalHitHistory.value = varargin{2}; - end - if length(varargin)>2 - LocalPrevSides.value = varargin{3}; - end - % Protect against somebody passing in SPHs, not actual values, by mistake: - if isa(value(LocalLeftProb), 'SoloParamHandle') - LocalLeftProb.value = value(value(LocalLeftProb)); - end - if isa(value(LocalHitHistory), 'SoloParamHandle') - LocalHitHistory.value = value(value(LocalHitHistory)); - end - if isa(value(LocalPrevSides), 'SoloParamHandle') - LocalPrevSides.value = value(value(LocalPrevSides)); - end - - feval(mfilename, obj, 'update_hitfrac'); - feval(mfilename, obj, 'update_biashitfrac'); - - - - case 'update_biashitfrac' % ------- CASE UPDATE_BIASHITFRAC ------------- - if ~isempty(varargin) - LocalLeftProb.value = varargin{1}; - end - if length(varargin)>1 - LocalHitHistory.value = varargin{2}; - end - if length(varargin)>2 - LocalPrevSides.value = varargin{3}; - end - - % Protect against somebody passing in SPHs, not actual values, by mistake: - if isa(value(LocalLeftProb), 'SoloParamHandle') - LocalLeftProb.value = value(value(LocalLeftProb)); - end - if isa(value(LocalHitHistory), 'SoloParamHandle') - LocalHitHistory.value = value(value(LocalHitHistory)); - end - if isa(value(LocalPrevSides), 'SoloParamHandle') - LocalPrevSides.value = value(value(LocalPrevSides)); - end - - LeftProb = value(LocalLeftProb); - hit_history = value(LocalHitHistory); - hit_history = colvec(hit_history); - previous_sides = value(LocalPrevSides); - - kernel = exp(-(0:length(hit_history)-1)/BiasTau)'; - kernel = kernel(end:-1:1); - - prevs = previous_sides(1:length(hit_history))'; - ul = find(prevs == 'l'); - if isempty(ul), BiasLtHitFrac.value = 1; - else - BiasLtHitFrac.value = sum(hit_history(ul) .* kernel(ul))/sum(kernel(ul)); - end - - ur = find(prevs == 'r'); - if isempty(ur), BiasRtHitFrac.value = 1; - else - BiasRtHitFrac.value = sum(hit_history(ur) .* kernel(ur))/sum(kernel(ur)); - end - - if isempty(ul) && ~isempty(ur) - BiasLtHitFrac.value = value(BiasRtHitFrac); - end - if isempty(ur) && ~isempty(ul) - BiasRtHitFrac.value = value(BiasLtHitFrac); - end - - choices = probabilistic_trial_selector([value(BiasLtHitFrac), value(BiasRtHitFrac)], ... - [LeftProb, 1-LeftProb], value(Beta)); - LtProb.value = choices(1); - RtProb.value = choices(2); - - - case 'get_posterior_probs' % ------- CASE GET_POSTERIOR_PROBS ------------- - x = [value(LtProb) ; value(RtProb)]; %#ok - - - case 'update_hitfrac' % ------- CASE UPDATE_HITFRAC ------------- - if ~isempty(varargin), LocalHitHistory.value = varargin{1}; end - if length(varargin)>1, LocalPrevSides.value = varargin{2}; end - % Protect against somebody passing in SPHs, not actual values, by mistake: - if isa(value(LocalHitHistory), 'SoloParamHandle'), LocalHitHistory.value = value(value(LocalHitHistory)); end - if isa(value(LocalPrevSides), 'SoloParamHandle'), LocalPrevSides.value = value(value(LocalPrevSides)); end - - hit_history = value(LocalHitHistory); hit_history = colvec(hit_history); - previous_sides = value(LocalPrevSides); - - - if ~isempty(hit_history) - kernel = exp(-(0:length(hit_history)-1)/HitFracTau)'; - kernel = kernel(end:-1:1); - HitFrac.value = sum(hit_history .* kernel)/sum(kernel); - - prevs = previous_sides(1:length(hit_history))'; - u = find(prevs == 'l'); - - if isempty(u) - LtHitFrac.value = NaN; - else - LtHitFrac.value = sum(hit_history(u) .* kernel(u))/sum(kernel(u)); - end - - u = find(prevs == 'r'); - - if isempty(u) - RtHitFrac.value = NaN; - else - RtHitFrac.value = sum(hit_history(u) .* kernel(u))/sum(kernel(u)); - end - - end - - - case 'get' % ------- CASE GET ------------- - if length(varargin)~=1 - error('AntibiasSection:Invalid', '''get'' needs one extra param'); - end - switch varargin{1} - case 'Beta' - x = value(Beta); - case 'antibias_tau' - x = value(BiasTau); - otherwise - error('AntibiasSection:Invalid', 'Don''t know how to get %s', varargin{1}); - end - - - case 'reinit' % ------- CASE REINIT ------------- - currfig = double(gcf); - - % Get the original GUI position and figure: - x = my_gui_info(1); y = my_gui_info(2); figure(my_gui_info(3)); - - % Delete all SoloParamHandles who belong to this object and whose - % fullname starts with the name of this mfile: - delete_sphandle('owner', ['^@' class(obj) '$'], ... - 'fullname', ['^' mfilename]); - - % Reinitialise at the original GUI position and figure: - [x, y] = feval(mfilename, obj, 'init', x, y); - - % Restore the current figure: - figure(currfig); -end - - - - -function [x] = colvec(x) - - if size(x,2) > size(x,1) - x = x'; - end - \ No newline at end of file diff --git a/Protocols/@ArpitSoundCatContinuous/ArpitSoundCatContinuousSMA.asv b/Protocols/@ArpitSoundCatContinuous/ArpitSoundCatContinuousSMA.asv deleted file mode 100644 index 9335b724..00000000 --- a/Protocols/@ArpitSoundCatContinuous/ArpitSoundCatContinuousSMA.asv +++ /dev/null @@ -1,281 +0,0 @@ -function [varargout] = ArpitSoundCatContinuousSMA(obj, action) - -GetSoloFunctionArgs; - - -switch action - - case 'init' - - - case 'prepare_next_trial' - - %% Setup water - - left1led = bSettings('get', 'DIOLINES', 'left1led'); - center1led = bSettings('get', 'DIOLINES', 'center1led'); - right1led = bSettings('get', 'DIOLINES', 'right1led'); - left1water = bSettings('get', 'DIOLINES', 'left1water'); - right1water = bSettings('get', 'DIOLINES', 'right1water'); - - - %% Setup sounds - - A1_sound_id = SoundManagerSection(obj, 'get_sound_id', 'StimAUD1'); % distribution based sound - sound_duration = value(A1_time); % SoundManagerSection(obj, 'get_sound_duration', 'SOneSound'); - go_sound_id = SoundManagerSection(obj, 'get_sound_id', 'GoSound'); - go_cue_duration = value(time_go_cue); % SoundManagerSection(obj, 'get_sound_duration', 'GoSound'); - viol_sound_id = SoundManagerSection(obj, 'get_sound_id', 'ViolationSound'); - viol_snd_duration = SoundManagerSection(obj, 'get_sound_duration', 'ViolationSound'); - to_sound_id = SoundManagerSection(obj, 'get_sound_id', 'TimeoutSound'); - timeout_snd_duration = SoundManagerSection(obj, 'get_sound_duration', 'TimeoutSound'); - - [LeftWValveTime,RightWValveTime] = ParamsSection(obj, 'get_water_amount'); - side = ParamsSection(obj, 'get_current_side'); - - if side == 'l' - HitEvent = 'Lin'; - ErrorEvent = 'Rin'; - sound_id = sone_sound_id; - SideLight = left1led; - WValveTime = LeftWValveTime; - WValveSide = left1water; - else - HitEvent = 'Rin'; - ErrorEvent = 'Lin'; - sound_id = stwo_sound_id; - SideLight = right1led; - WValveTime = RightWValveTime; - WValveSide = right1water; - end - - - sma = StateMachineAssembler('full_trial_structure','use_happenings', 1); - - %%%%%%%%%%%%%%%% SCHEDULED WAVES %%%%%%%%%%%%%%%%%%%%%%% - - % scheduled wave for stimuli / fixed(No) sound, based upon side - if value(stimuli_on) - sma = add_scheduled_wave(sma, 'name', 'stimplay', 'preamble', PreStim_time, ... - 'sustain', sound_duration, 'sound_trig', A1_sound_id); % to play a sound before Go Cue - else - % sma = add_scheduled_wave(sma, 'name', 'stimplay', 'preamble', PreStim_time, ... - % 'sustain', sound_duration, 'sound_trig', sound_id); % to play a fixed sound before Go Cue - sma = add_scheduled_wave(sma, 'name', 'stimplay', 'preamble', PreStim_time, ... - 'sustain', sound_duration); % to play No sound before Go Cue - end - - % Scheduled Wave for Go Sound - if value(Go_Sound) == 1 - sma = add_scheduled_wave(sma, 'name', 'Go_Cue', 'preamble', 0.001, ... - 'sustain', go_cue_duration, 'sound_trig', go_sound_id); % to play the Go Cue/Reward Sound - else - sma = add_scheduled_wave(sma, 'name', 'Go_Cue', 'preamble', 0.001, ... - 'sustain', go_cue_duration); % to play the Go Cue/Reward Sound - end - - % Scheduled wave for CP Duration - if CP_duration <= (SettlingIn_time + legal_cbreak) - sma = add_scheduled_wave(sma, 'name', 'CP_Duration_wave', 'preamble', CP_duration); % total length of centre poke to consider success - else - sma = add_scheduled_wave(sma, 'name', 'settling_period', 'preamble', SettlingIn_time); % intial fidgety period without violation - sma = add_scheduled_wave(sma, 'name', 'CP_Duration_wave', 'preamble', CP_duration - SettlingIn_time); % total length of centre poke minus the inital fidgety time to consider success - end - - % scheduled wave for rewarded side either of the side - sma = add_scheduled_wave(sma, 'name', 'reward_delivery', 'preamble', reward_delay, ... - 'sustain', WValveTime, 'DOut', WValveSide); % water delivery side - sma = add_scheduled_wave(sma, 'name', 'reward_collection_dur', 'preamble', SideLed_duration + RewardCollection_duration); % time to collect the reward - - %% - %%%%%%%%%%% % *STATES* %%%%%%%%%%%%%%%%%%% - - sma = add_state(sma,'name','wait_for_cpoke','self_timer',cp_timeout, ... - 'output_actions', {'DOut', center1led}, ... - 'input_to_statechange', {'Cin','settling_in_state';'Tup','timeout_state'}); - - %%%%%%%%%%%%% SETTLING IN STATE START %%%%%%%%%%%%%%%%%%%% - % Before progressing check if its still centre poking or pokes within legal c_break other wise its a violation - - if CP_duration <= SettlingIn_time + legal_cbreak % state machine during initial warm-up when starting a new session - - sma = add_state(sma,'name','settling_in_state','self_timer',CP_duration, ... - 'output_actions', {'SchedWaveTrig', 'CP_Duration_wave'}, ... - 'input_to_statechange', {'Cout','current_state + 1';'Tup','side_led_wait_RewardCollection'}); - - % Intermediate State - - % This intermediate state is considering the poke is out before the end of settling time / at the start of this state - sma = add_state(sma,'self_timer',CP_duration,'output_actions', {'DOut', center1led}, ... - 'input_to_statechange', {'CP_Duration_wave_In','side_led_wait_RewardCollection';'Cin','current_state + 1';'Tup','side_led_wait_RewardCollection'}); - - % The state jump to here when the nose is still in then go - % directly to give reward - sma = add_state(sma,'self_timer',CP_duration,... - 'input_to_statechange', {'CP_Duration_wave_In','side_led_wait_RewardCollection'; 'Cout','current_state - 1';'Tup','side_led_wait_RewardCollection'}); - - else % the usual state machine - - sma = add_state(sma,'name','settling_in_state','self_timer',SettlingIn_time, ... - 'output_actions', {'SchedWaveTrig', 'settling_period'}, ... - 'input_to_statechange', {'Cout','current_state + 1';'Tup','soft_cp'}); - - % Intermediate State - - % This intermediate state is considering the poke is out before the end of settling time / at the start of this state - sma = add_state(sma,'self_timer',SettlingIn_time,'output_actions', {'DOut', center1led}, ... - 'input_to_statechange', {'settling_period_In','legal_poke_start_state';'Cin','current_state + 1';'Tup','legal_poke_start_state';... - 'Rin', 'violation_state';'Rout', 'violation_state'; 'Lin', 'violation_state';'Lout', 'violation_state'}); - - % The state jump to here when the nose is still in then go directly to soft_cp - sma = add_state(sma,'self_timer',SettlingIn_time,... - 'input_to_statechange', {'settling_period_In','soft_cp'; 'Cout','current_state - 1';'Tup','soft_cp';... - 'Rin', 'violation_state'; 'Rout', 'violation_state'; 'Lin', 'violation_state'; 'Lout', 'violation_state'}); - - %%%%%%%%%%%%% SETTLING IN STATE END %%%%%%%%%%%%%%%%%%%%%% - - % STATE TO CHECK BEFORE START OF LEGAL POKE PERIOD - - sma = add_state(sma,'name','legal_poke_start_state','self_timer',legal_cbreak/2, ... - 'output_actions', {'DOut', center1led}, ... - 'input_to_statechange', {'Cin','soft_cp'; 'Tup','violation_state';... - 'Rin', 'violation_state'; 'Rout', 'violation_state'; 'Lin', 'violation_state'; 'Lout', 'violation_state'}); %more stringent by giving half the legal cp time - - %%%%%%%%%%%% LEGAL SOFT POKE STATE START %%%%%%%%%%%%%%%%% - - sma = add_state(sma,'name','soft_cp','self_timer',CP_duration - SettlingIn_time, ... CP_Duration_wave - 'output_actions', {'SchedWaveTrig', 'CP_Duration_wave+stimplay'},... - 'input_to_statechange', {'Cout','current_state + 1'; 'Tup','side_led_wait_RewardCollection';... - 'Rin', 'violation_state'; 'Rout', 'violation_state'; 'Lin', 'violation_state'; 'Lout', 'violation_state'}); %more stringent by giving half the legal cp time - - end - - % Intermediate State - - % This intermediate state is considering the poke is out before the end of settling time / at the start of this state - sma = add_state(sma,'self_timer',legal_cbreak,'output_actions', {'DOut', center1led}, ... - 'input_to_statechange', {'CP_Duration_wave_In','legal_poke_end_state';'Cin','current_state + 1';'Tup','violation_state';... - 'Rin', 'violation_state';'Rout', 'violation_state'; 'Lin', 'violation_state';'Lout', 'violation_state'}); - - % The state jump to here when the nose is still in then go directly to soft_cp - sma = add_state(sma,'self_timer',CP_duration - SettlingIn_time, ... - 'input_to_statechange', {'CP_Duration_wave_In','side_led_wait_RewardCollection';'Cout','current_state - 1';'Tup','side_led_wait_RewardCollection';... - 'Rin', 'violation_state';'Rout', 'violation_state'; 'Lin', 'violation_state';'Lout', 'violation_state'}); - - %%%%%%%%%%%% LEGAL SOFT POKE STATE END %%%%%%%%%%%%%%%%% - - % Before giving the reward check if its still centre poking or pokes - % within legal c_break other wise its a violation - - sma = add_state(sma,'name','legal_poke_end_state','self_timer',legal_cbreak/2, ... - 'output_actions', {'DOut', center1led}, ... - 'input_to_statechange', {'Cin','side_led_wait_RewardCollection'; 'Tup','violation_state'}); - - - %%%%%%%%%%%%%%% REWARD COLLECTION STATE START %%%%%%%%%%%%%%% - - sma = add_state(sma, 'name', 'side_led_wait_RewardCollection', 'self_timer', SideLed_duration + RewardCollection_duration, ... - 'output_actions', {'DOut', SideLight; 'SchedWaveTrig', 'reward_collection_dur+Go_Cue'}, ... - 'input_to_statechange',{HitEvent,'hit_state'; 'Tup','timeout_state'; ErrorEvent,'second_hit_state'}); - - sma = add_state(sma,'name','second_hit_state','self_timer',RewardCollection_duration,... - 'output_actions',{'DOut', SideLight},... - 'input_to_statechange',{'reward_collection_dur_In', 'timeout_state'; 'Tup','timeout_state'; HitEvent,'hit_state'}); - - sma = add_state(sma,'name','hit_state','self_timer',0.01,... - 'output_actions', {'SchedWaveTrig','reward_delivery'},... - 'input_to_statechange',{'Tup','drink_state'}); - - sma = add_state(sma,'name','drink_state','self_timer',drink_time,... - 'input_to_statechange',{'Tup','preclean_up_state'}); - - %%%%%%%%%%%%%%% FAILURE TO CENTRE POKE %%%%%%%%%%%%%%%%%%%%%% - - % For Timeout - - sma = add_state(sma,'name','timeout_state','self_timer',timeout_snd_duration,... - 'output_actions', {'SoundOut',to_sound_id; 'SchedWaveTrig', '-Go_Cue'},... - 'input_to_statechange',{'Tup','current_state+1'}); - sma = add_state(sma, 'self_timer', max(0.001, timeout_iti-timeout_snd_duration), ... - 'input_to_statechange',{'Tup','preclean_up_state'}); - - % For Violations - - sma = add_state(sma,'name','violation_state','self_timer',viol_snd_duration,... - 'output_actions',{'SoundOut',viol_sound_id; 'DOut', center1led; 'SchedWaveTrig', '-Go_Cue-stimplay-CP_Duration_wave'},... - 'input_to_statechange', {'Tup', 'current_state+1'}); - sma = add_state(sma, 'self_timer', max(0.001, violation_iti-viol_snd_duration), ... - 'input_to_statechange',{'Tup','preclean_up_state'}); - - sma = add_state(sma,'name','preclean_up_state','self_timer',0.5,... - 'input_to_statechange',{'Tup','check_next_trial_ready'}); - - varargout{2} = {'check_next_trial_ready'}; - - varargout{1} = sma; - - % Not all 'prepare_next_trial_states' are defined in all training - % stages. So we send to dispatcher only those states that are - % defined. - state_names = get_labels(sma); state_names = state_names(:,1); - prepare_next_trial_states = {'side_led_wait_RewardCollection','hit_state','second_hit_state','drink_state', 'violation_state','timeout_state','preclean_up_state'}; - - dispatcher('send_assembler', sma, intersect(state_names, prepare_next_trial_states)); - - case 'get_state_colors' - varargout{1} = struct( ... - 'wait_for_cpoke', [0.68 1 0.63], ... - 'settling_in_state', [0.63 1 0.94], ... - 'legal_poke_start_state', [0.63 1 0.94]*0.8, ... - 'legal_poke_end_state', [1 0.79 0.63], ... - 'soft_cp', [0.3 0.9 0], ... - 'side_led_wait_RewardCollection', [0.53 0.78 1.00],... - 'hit_state', [0.77 0.60 0.48], ... - 'second_hit_state', [0.25 0.45 0.48], ... - 'drink_state', [0 1 0], ... - 'violation_state', [0.31 0.48 0.30], ... - 'timeout_state', 0.8*[0.31 0.48 0.30]); - - - % 'lefthit', [0 0.9 0.3], ... - % 'error_state', [1 0.54 0.54], ... - - - % 'go_cue_on', [0.63 1 0.94]*0.6, ... - % 'prerw_postcs', [0.25 0.45 0.48], ... - % 'lefthit', [0.53 0.78 1.00], ... - % 'lefthit_pasound', [0.53 0.78 1.00]*0.7, ... - % 'righthit', [0.52 1.0 0.60], ... - % 'righthit_pasound', [0.52 1.0 0.60]*0.7, ... - % 'warning', [0.3 0 0], ... - % 'danger', [0.5 0.05 0.05], ... - % 'hit', [0 1 0] - - - case 'close' - - - case 'reinit' - currfig = double(gcf); - - % Delete all SoloParamHandles who belong to this object and whose - % fullname starts with the name of this mfile: - delete_sphandle('owner', ['^@' class(obj) '$'], ... - 'fullname', ['^' mfilename]); - - - % Reinitialise at the original GUI position and figure: - feval(mfilename, obj, 'init'); - - % Restore the current figure: - figure(currfig); - - otherwise - warning('do not know how to do %s',action); -end - - -end - - - diff --git a/Protocols/@ArpitSoundCatContinuous/SideSection.asv b/Protocols/@ArpitSoundCatContinuous/SideSection.asv deleted file mode 100644 index 7ad41418..00000000 --- a/Protocols/@ArpitSoundCatContinuous/SideSection.asv +++ /dev/null @@ -1,510 +0,0 @@ - - -function [x, y] = SideSection(obj, action, x,y) - -GetSoloFunctionArgs(obj); - -switch action - - % ------------------------------------------------------------------ - % INIT - % ------------------------------------------------------------------ - - case 'init' - - SoloParamHandle(obj, 'my_gui_info', 'value', [x y double(gcf)], 'saveable', 0); - y0 = y; - - [x, y] = AntibiasSection(obj, 'init', x, y); - - - ToggleParam(obj, 'antibias_LRprob', 0, x,y,... - 'OnString', 'AB_Prob ON',... - 'OffString', 'AB_Prob OFF',... - 'TooltipString', sprintf(['If on (Yellow) then it enables the AntiBias algorithm\n'... - 'based on changing the probablity of Left vs Right'])); - - next_row(y); - NumeditParam(obj, 'LeftProb', 0.5, x, y); next_row(y); - set_callback(LeftProb, {mfilename, 'new_leftprob'}); - MenuParam(obj, 'MaxSame', {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, Inf}, Inf, x, y, ... - 'TooltipString', sprintf(['\nMaximum number of consecutive trials where correct\n' ... - 'response is on the same side. Overrides antibias. Thus, for\n' ... - 'example, if MaxSame=5 and there have been 5 Left trials, the\n' ... - 'next trial is guaranteed to be Right'])); next_row(y); - - next_row(y); - NumeditParam(obj, 'ntrial_correct_bias', 0, x, y, ... - 'TooltipString', 'antibias starts from trial=ntrial_correct_bias'); - next_row(y); - NumeditParam(obj, 'right_left_diff', .12, x, y, ... - 'TooltipString', 'antibias applies if difference between right and left sides is bigger than this number'); - next_row(y); - NumeditParam(obj, 'max_wtr_mult', 4, x, y, ... - 'TooltipString', 'wtr_mult will be min(max_wtr_mult,right_hit/left_hit)'); - next_row(y); - NumeditParam(obj, 'left_wtr_mult', 1, x, y, ... - 'TooltipString', 'all left reward times are multiplied by this number'); - next_row(y); - NumeditParam(obj, 'right_wtr_mult', 1, x, y, ... - 'TooltipString', 'all right reward times are multiplied by this number'); - next_row(y); - ToggleParam(obj, 'antibias_wtr_mult', 0, x,y,... - 'OnString', 'AB ON',... - 'OffString', 'AB OFF',... - 'TooltipString', sprintf(['If on (black) then it disables the wtr_mult entries\n'... - 'and uses hitfrac to adjust the water times'])); - - next_row(y); - - DispParam(obj, 'ThisTrial', 'LEFT', x, y); next_row(y); - SoloParamHandle(obj, 'previous_sides', 'value', []); - DeclareGlobals(obj, 'ro_args', 'previous_sides'); - SubheaderParam(obj, 'title', 'Sides Section', x, y); - next_row(y, 1.5); - %% slow ramp up of water amount - %%the water volume is controlled by a 5-parameter logistic function: WaterAmount(t) = maxasymp + (minasymp/(1+(t/inflp)^slp).^assym) - NumeditParam(obj, 'maxasymp', 38, x,y,'label','maxasymp','TooltipString',... - 'the water volume is controlled by a 5-parameter logistic function: WaterAmount(trialnum) = maxasymp + (minasymp/(1+(trialnum/inflp)^slp).^assym)'); - next_row(y); - NumeditParam(obj, 'slp', 3, x,y,'label','slp','TooltipString','Water Modulation: Slope of the logistic function'); - next_row(y); - NumeditParam(obj, 'inflp', 350, x,y,'label','inflp','TooltipString','Water Modulation: concentration at the inflection point'); - next_row(y); - NumeditParam(obj, 'minasymp', -21, x,y,'label','inflp','TooltipString','Water Modulation: minimum asymptote'); - next_row(y); - NumeditParam(obj, 'assym', 0.7, x,y,'label','assym','TooltipString','Water Modulation: asymmetry factor'); - next_row(y); - DispParam(obj, 'trial_1', 0, x, y, 'TooltipString', 'uL on first trial'); - next_row(y); - DispParam(obj, 'trial_150', 0, x, y, 'TooltipString', 'uL on trial 150'); - next_row(y); - DispParam(obj, 'trial_300', 0, x, y, 'TooltipString', 'uL on trial 300'); - next_row(y); - set_callback({maxasymp;slp;inflp;minasymp;assym}, {mfilename, 'change_water_modulation_params'}); - feval(mfilename, obj, 'change_water_modulation_params'); - - next_column(x); y = 5; - NumeditParam(obj, 'RewardCollection_duration', 10, x,y,'label','RewardCollection_duration','TooltipString','Wait until rat collects the reward'); - next_row(y); - NumeditParam(obj, 'CenterLed_duration', 200, x,y,'label','Central LED duration','TooltipString','Duration of Center Led'); - next_row(y); - NumeditParam(obj, 'SideLed_duration', 0.5, x,y,'label','Side LED duration','TooltipString','Duration of SideLed'); - next_row(y); - NumeditParam(obj, 'legal_cbreak', 0.1, x,y, 'position', [x, y, 175 20], 'TooltipString','Time in sec for which it is ok to be outside the center port before a violation occurs.'); - ToggleParam(obj, 'LED_during_legal_cbreak', 1, x, y, 'OnString', 'LED ON LcB', 'OffString', 'LED off LcB', ... - 'position', [x+180 y 20 20], 'TooltipString', ... - 'If 1 (black), turn center port LED back on during legal_cbreak; if 0 (brown), leave LED off'); - next_row(y); - NumeditParam(obj, 'SettlingIn_time', 0.2, x,y, 'position', [x, y, 175 20], 'TooltipString','Initial settling period during which "legal cbreak period" can be longer than the usual "legal_cbreak"'); - next_row(y); - NumeditParam(obj, 'settling_legal_cbreak', 0.1, x,y, 'position', [x, y, 175 20], 'TooltipString','Time in sec for which it is ok during the "SettlingIn_time" to be outside the center port before a violation occurs.'); - ToggleParam(obj, 'LED_during_settling_legal_cbreak', 0, x, y, 'OnString', 'LED ON SetLcB', 'OffString', 'LED OFF setLcB', ... - 'position', [x+180 y 20 20], 'TooltipString', ... - 'If 1 (black), turn center port LED back on during settling_legal_cbreak; if 0 (brown), leave LED off'); - next_row(y); - MenuParam(obj, 'side_lights' ,{'none','both','correct side','anti side'},1, x,y,'label','Side Lights','TooltipString','Controls the side LEDs during wait_for_spoke'); - next_row(y); - - - NumeditParam(obj, 'A1_time', 0.4, x,y,'label','AUD1 on Time','TooltipString','Duration of first stimulus'); - next_row(y); - set_callback(A1_time, {mfilename, 'new_CP_duration'}); - NumeditParam(obj, 'PreStim_time', 0.20, x,y,'label','Pre-Stim NIC time','TooltipString','Time in NIC before starting the stimulus'); - next_row(y); - set_callback(PreStim_time, {mfilename, 'new_CP_duration'}); - NumeditParam(obj, 'time_bet_aud1_gocue', 0, x,y,'label','A1-GoCue time','TooltipString','time between the end of the stimulus and the go cue '); - next_row(y); - set_callback(time_bet_aud1_gocue, {mfilename, 'new_CP_duration'}); - DispParam(obj, 'init_CP_duration', 0.01, x,y,'label','init_CP duration','TooltipString','Duration of Nose in Central Poke before Go cue starts (see Total_CP_duration)'); - next_row(y); - DispParam(obj, 'CP_duration', PreStim_time+A1_time+time_bet_aud1_gocue, x,y,'label','CP duration','TooltipString','Duration of Nose in Central Poke before Go cue starts (see Total_CP_duration)'); - set_callback(CP_duration, {mfilename, 'new_CP_duration'}); - next_row(y); - NumeditParam(obj, 'time_go_cue' ,0, x,y,'label','Go Cue Duration','TooltipString','duration of go cue (see Total_CP_duration)'); - set_callback(time_go_cue, {mfilename, 'new_time_go_cue'}); - next_row(y); - DispParam(obj, 'Total_CP_duration', CP_duration+time_go_cue, x, y, 'TooltipString', 'Total nose in center port time, in secs. Sum of CP_duration and Go Cue duration'); %#ok<*NODEF> - next_row(y); - ToggleParam(obj, 'warmup_on', 1, x,y,... - 'OnString', 'Warmup ON',... - 'OffString', 'Warmup OFF',... - 'TooltipString', sprintf(['If on (Yellow) then it applies the initial warming up phase, during which the\n',... - 'CP_duration starts small and gradually grows to last_session_max_cp_duration'])); - next_row(y); - % to modify it for widefield imagine - NumeditParam(obj,'imaging',0,x,y,'label','Scope Trigger'); - next_row(y); - NumeditParam(obj, 'reward_delay', 0.01, x,y,'label','Reward Delay','TooltipString','Delay between side poke and reward delivery'); - next_row(y); - NumeditParam(obj, 'reward_duration', 0.1, x,y,'label','Reward Duration','TooltipString','Duration of reward sound'); - next_row(y); - NumeditParam(obj, 'drink_time', 1, x,y,'label','Drink Time','TooltipString','waits to finish water delivery'); - next_row(y); - NumeditParam(obj, 'error_iti', 5, x,y,'label','Error Timeout','TooltipString','ITI on error trials'); - next_row(y); - NumeditParam(obj, 'violation_iti', 1, x,y,'label','Violation Timeout','TooltipString','Center poke violation duration'); - next_row(y); - MenuParam(obj, 'reward_type', {'Always','DelayedReward', 'NoReward'}, ... - 'Always', x, y, 'labelfraction', 0.35, 'TooltipString', sprintf(['\nThis menu is to determine the Reward delivery on wrong-hit trials\n',... - '\nIf ''Always'': reward will be available on each trial no matter which side rat goes first\n',... - '\n If rat pokes first on the wrong side, then reward will be delivered with a delay (if DelayedReward) or not delivered at all (if NoReward)'])); - set_callback(reward_type, {mfilename, 'new_reward_type'}); - next_row(y); - NumeditParam(obj,'secondhit_delay',0,x,y,'label','SecondHit Delay','TooltipString','Reward will be delayed with this amount if reward_type=DelayedReward'); - - next_row(y); - ToggleParam(obj, 'random_A1_time', 0, x,y,... - 'OnString', 'random A1_time ON',... - 'OffString', 'random A1_time OFF',... - 'TooltipString', sprintf('If on (black) then it enables the random sampling of A1_time')); - next_row(y); - ToggleParam(obj, 'random_prego_time', 0, x,y,... - 'OnString', 'random prego_time ON',... - 'OffString', 'random prego_time OFF',... - 'TooltipString', sprintf('If on (black) then it enables the random sampling of time between the end of the stimulus and the go cue')); - - next_column(x); - y=5; - NumeditParam(obj,'trials_in_stage',1,x,y,'label','Trial Counter'); - next_row(y); - NumeditParam(obj,'training_stage',1,x,y,'label','Training Stage'); - next_row(y); - ToggleParam(obj,'use_training',0,x,y,'OnString','Using Autotrain','OffString','Manual Settings'); - - - ToggleParam(obj, 'stimuli_on', 1, x,y,... - 'OnString', 'Stimuli ON',... - 'OffString', 'Stimuli OFF',... - 'TooltipString', sprintf('If on (black) then it disable the presentation of sound stimuli during nose poke')); - set_callback(stimuli_on, {mfilename, 'new_CP_duration'}); - - next_row(y); - SoloFunctionAddVars('SoundCatSMA', 'ro_args', ... - {'CP_duration';'SideLed_duration';'CenterLed_duration';'side_lights' ; ... - 'RewardCollection_duration';'training_stage'; ... - 'legal_cbreak' ; 'LED_during_legal_cbreak' ; ... - 'SettlingIn_time';'settling_legal_cbreak' ; 'LED_during_settling_legal_cbreak' ; ... - 'time_go_cue'; ... - 'stimuli_on';'A1_time';'time_bet_aud1_gocue' ; ... - 'PreStim_time';'warmup_on' - 'drink_time';'reward_delay';'reward_duration';'left_wtr_mult';... - 'right_wtr_mult';'antibias_wtr_mult';... - 'reward_type';'secondhit_delay';'error_iti';'violation_iti';'imaging'}); - - SoloFunctionAddVars('StimulusSection', 'ro_args', ... - {'ThisTrial';'A1_time';'time_bet_aud1_gocue' ; ... - 'PreStim_time'}); - SoloFunctionAddVars('StimulatorSection', 'ro_args', ... - {'A1_time';'time_bet_aud1_gocue';'time_go_cue'; ... - 'PreStim_time';'CP_duration';'Total_CP_duration'}); - - % History of hit/miss: - SoloParamHandle(obj, 'deltaf_history', 'value', []); - - SoloFunctionAddVars('OverallPerformanceSection', 'ro_args', ... - {'training_stage'}); - - SoloFunctionAddVars('SoundCatSMA', 'ro_args', ... - {'maxasymp';'slp';'inflp';'minasymp';'assym'}); - - SoloParamHandle(obj, 'previous_parameters', 'value', []); - - - %% change_water_modulation_params - case 'change_water_modulation_params', - display_guys = [1 150 300]; - for i=1:numel(display_guys), - t = display_guys(i); - - myvar = eval(sprintf('trial_%d', t)); - myvar.value = maxasymp + (minasymp/(1+(t/inflp)^slp).^assym); - end; - - - case 'new_leftprob' - AntibiasSectionAthena(obj, 'update_biashitfrac', value(LeftProb)); - - - case 'new_CP_duration' - if stimuli_on == 0 - PreStim_time.value=0; - A1_time.value=0; - time_bet_aud1_gocue.value=0; - disable(PreStim_time); - disable(A1_time); - disable(time_bet_aud1_gocue); - else - enable(PreStim_time); - enable(A1_time); - enable(time_bet_aud1_gocue); - end - CP_duration.value=PreStim_time + A1_time + time_bet_aud1_gocue; - Total_CP_duration.value = CP_duration + time_go_cue; %#ok<*NASGU> - - case 'new_time_go_cue' - Total_CP_duration.value = CP_duration + time_go_cue; - SoundInterface(obj, 'set', 'GoSound', 'Dur1', value(time_go_cue)); - - case 'new_reward_type' - if strcmp(reward_type,'DelayedReward') - enable(secondhit_delay) - else - secondhit_delay=0; - disable(secondhit_delay) - - end - - - case 'prepare_next_trial' - - - switch value(training_stage) - case 0 %% learning the reward sound association -left or right led on -> poke -> sound+reward - settling_time.value=0.01; - delay_time.value=0; - allow_nic_breaks.value=1; - side_lights.value=3; - trials_in_stage.value=0; - reward_delay.value=0.01; - left_prob.value=0.5; - right_prob.value=0.5; - time_go_cue.value=0.200; - reward_duration=0.200; - - - case 1 %% center led on -> poke in the center -> go cue -> reward light and sound - %what A1_time and time before go cue? - if random_A1_time - A1_times = [0.2, 0.4]; - randomix = randi(length(A1_times), 1); - A1_time.value = A1_times(randomix); - end - - if random_prego_time - prego_times = [0.2, 0.4, 0.6, 0.8, 1]; - randomixx = randi(length(prego_times), 1); - time_bet_aud1_gocue.value = prego_times(randomixx); - end - settling_time.value=0.25; - delay_time.value=0; - allow_nic_breaks.value=1; - side_lights.value=3; - training_stage.value=1; - trials_in_stage.value=0; - reward_delay.value=0.01; - left_prob.value=0.5; - right_prob.value=0.5; - if n_done_trials <1 && warmup_on ==1 - CP_duration.value=value(init_CP_duration); - else - CP_duration.value=PreStim_time + A1_time + time_bet_aud1_gocue; - end - Total_CP_duration.value = CP_duration + time_go_cue; %#ok<*NASGU> - - - case 3 % like stage 2, now passive exposure to the stimuli - reward comes anyway - case 4 %% now reward comes only if rat goes to the correct side - - end - - - %% update hit_history, previous_sides, etc - was_viol=false; - was_hit=false; - was_timeout=false; - if n_done_trials>0 - if ~isempty(parsed_events) - if isfield(parsed_events,'states') - if isfield(parsed_events.states,'timeout_state') - was_timeout=rows(parsed_events.states.timeout_state)>0; - end - if isfield(parsed_events.states,'violation_state') - was_viol=rows(parsed_events.states.violation_state)>0; - end - end - - end - - violation_history.value=[violation_history(:); was_viol]; - timeout_history.value=[timeout_history(:); was_timeout]; - - SideSection(obj,'update_side_history'); - - if ~was_viol && ~was_timeout - %was_hit=rows(parsed_events.states.hit_state)>0; - was_hit=rows(parsed_events.states.second_hit_state)==0; - hit_history.value=[hit_history(:); was_hit]; - - else - % There was a violation or timeout - hit_history.value=[hit_history(:); nan]; - end - - % Now calculate the deltaF and sides - this maybe interesting - % even in a violation or timeout case. - - fn=fieldnames(parsed_events.states); - led_states=find(strncmp('led',fn,3)); - deltaF=0; - n_l=0; - n_r=0; - for lx=1:numel(led_states) - lind=led_states(lx); - if rows(parsed_events.states.(fn{lind}))>0 - if fn{lind}(end)=='l' - deltaF=deltaF-1; - n_l=n_l+1; - elseif fn{lind}(end)=='r' - deltaF=deltaF+1; - n_r=n_r+1; - elseif fn{lind}(end)=='b' - n_l=n_l+1; - n_r=n_r+1; - - end - end - - end - - % if deltaF>0 then a right poke is a hit - % if deltaF<0 then a left poke is a hit - - deltaf_history.value=[deltaf_history(:); deltaF]; - - end - - if antibias_LRprob ==1 - if n_done_trials >ntrial_correct_bias && ~was_viol && ~was_timeout - nonan_hit_history=value(hit_history); - nonan_hit_history(isnan(nonan_hit_history))=[]; - nonan_previous_sides=value(previous_sides); - nan_history=value(hit_history); - nonan_previous_sides(isnan(nan_history))=[]; - AntibiasSection(obj, 'update', value(LeftProb), nonan_hit_history(:)',nonan_previous_sides(:)); % <~> Transposed hit history so that it is the expected column vector. (Antibias errors out otherwise.) 2007.09.05 01:39 - end - - - if ~isinf(MaxSame) && length(previous_sides) > MaxSame && ... - all(previous_sides(n_done_trials-MaxSame+1:n_done_trials) == previous_sides(n_done_trials)) %#ok - if previous_sides(end)=='l' - ThisTrial.value = 'RIGHT'; - else - ThisTrial.value = 'LEFT'; - end - else - choiceprobs = AntibiasSectionAthena(obj, 'get_posterior_probs'); - if rand(1) <= choiceprobs(1) - ThisTrial.value = 'LEFT'; - else - ThisTrial.value = 'RIGHT'; - end - end - - else - if (rand(1)<=LeftProb) - ThisTrial.value='LEFT'; - - else - ThisTrial.value='RIGHT'; - end - - end - - - - - %% Do the anti-bias with changing reward delivery - % reset anti-bias - left_wtr_mult.value=1; - right_wtr_mult.value=1; - if n_done_trials>ntrial_correct_bias && antibias_wtr_mult==1 - hh=hit_history(n_done_trials-ntrial_correct_bias:n_done_trials); - ps=previous_sides(n_done_trials-ntrial_correct_bias:n_done_trials); - - right_hit=nanmean(hh(ps=='r')); - left_hit=nanmean(hh(ps=='l')); - - if abs(right_hit-left_hit) - - case 'get_left_prob' - x = value(LeftProb); - - case 'get_cp_history' - x = cell2mat(get_history(CP_duration)); - - case 'get_stimdur_history' - x = cell2mat(get_history(A1_time)); - - case 'update_side_history' - if strcmp(ThisTrial, 'LEFT') - ps=value(previous_sides); - ps(n_done_trials)='l'; - previous_sides.value=ps; - - else - ps=value(previous_sides); - ps(n_done_trials)='r'; - previous_sides.value=ps; - end; - - case 'get_current_side' - if strcmp(ThisTrial, 'LEFT') - x = 'l'; %#ok - else - x = 'r'; - end; - - - case 'close' - % Delete all SoloParamHandles who belong to this object and whose - % fullname starts with the name of this mfile: - delete_sphandle('owner', ['^@' class(obj) '$'], ... - 'fullname', ['^' mfilename]); - - case 'reinit', - currfig = double(gcf); - - % Get the original GUI position and figure: - x = my_gui_info(1); y = my_gui_info(2); figure(my_gui_info(3)); - - % Delete all SoloParamHandles who belong to this object and whose - % fullname starts with the name of this mfile: - delete_sphandle('owner', ['^@' class(obj) '$'], ... - 'fullname', ['^' mfilename]); - - % Reinitialise at the original GUI position and figure: - [x, y] = feval(mfilename, obj, 'init', x, y); - - % Restore the current figure: - figure(currfig); - -end - - From 471fbce98ba0156452ce59458b2055317a53f2d3 Mon Sep 17 00:00:00 2001 From: Arpit Date: Thu, 5 Jun 2025 14:38:49 +0100 Subject: [PATCH 121/164] new protocol arpitsoundcatcomtinous --- .../@ArpitCentrePokeTraining/ParamsSection.m | 7 +- .../TrainingStageParamsSection.m | 10 +- .../ArpitSoundCatContinuousSMA.m | 51 +++-- .../@ArpitSoundCatContinuous/SideSection.m | 194 ++++++++++-------- 4 files changed, 159 insertions(+), 103 deletions(-) diff --git a/Protocols/@ArpitCentrePokeTraining/ParamsSection.m b/Protocols/@ArpitCentrePokeTraining/ParamsSection.m index 0861bfe2..8d7610a8 100644 --- a/Protocols/@ArpitCentrePokeTraining/ParamsSection.m +++ b/Protocols/@ArpitCentrePokeTraining/ParamsSection.m @@ -216,8 +216,13 @@ if value(use_auto_train) == 1 disable(training_stage); % user cannot change the training stages else + Training_Stages_List = {'Familiarize with Reward Side Pokes','Timeout Rewarded Side Pokes'... + 'Introduce Centre Poke','Introduce Violation for Centre Poke',... + 'Introduce Stimuli Sound during Centre Poke','Vary Stimuli location during Centre Poke'... + 'Variable Stimuli Go Cue location during Centre Poke','User Setting'}; + stage_name = Training_Stages_List{value(training_stage)}; enable(training_stage); % user can change the training stages - SessionDefinition(obj, 'jump_to_stage',value(training_stage)); + SessionDefinition(obj, 'jump_to_stage',stage_name); end [stage_fig_x,stage_fig_y] = TrainingStageParamsSection(obj, 'reinit', value(stage_fig_x),value(stage_fig_y)); % update the training params as well diff --git a/Protocols/@ArpitCentrePokeTraining/TrainingStageParamsSection.m b/Protocols/@ArpitCentrePokeTraining/TrainingStageParamsSection.m index 0f81cf81..f8870d8f 100644 --- a/Protocols/@ArpitCentrePokeTraining/TrainingStageParamsSection.m +++ b/Protocols/@ArpitCentrePokeTraining/TrainingStageParamsSection.m @@ -22,7 +22,7 @@ SubheaderParam(obj, 'title', 'Stage Params', x, y); % next_row(y); % COMPLETION TEST PARAMETERS - NumeditParam(obj, 'total_trials', 600, x, y,'label','Trials','TooltipString','total trials in this stage for its completion'); next_row(y); + NumeditParam(obj, 'total_trials', 400, x, y,'label','Trials','TooltipString','total trials in this stage for its completion'); next_row(y); NumeditParam(obj, 'total_trials_opp', 200, x, y,'label','Trials_Opp','TooltipString','total trials with opposide side option in this stage for its completion'); next_row(y); SubheaderParam(obj, 'title', 'Completion Params', x, y); next_row(y); @@ -33,7 +33,7 @@ NumeditParam(obj, 'min_rColl_dur', 60, x, y,'label','T_Min_RCollect','TooltipString','User given min water collect time(code will optimize for value above this)'); next_row(y); SubheaderParam(obj, 'title', 'Stage Params', x, y); next_row(y); % COMPLETION TEST PARAMETERS - NumeditParam(obj, 'total_trials', 600, x, y,'label','Trials','TooltipString','total trials in this stage for its completion'); next_row(y); + NumeditParam(obj, 'total_trials', 400, x, y,'label','Trials','TooltipString','total trials in this stage for its completion'); next_row(y); NumeditParam(obj, 'total_trials_opp', 200, x, y,'label','Trials_Opp','TooltipString','total trials with opposide side option in this stage for its completion'); next_row(y); SubheaderParam(obj, 'title', 'Completion Params', x, y);next_row(y); @@ -70,7 +70,7 @@ NumeditParam(obj, 'recent_violation', 0.15, x, y,'label','Recent_ViolateRate','TooltipString','violation rate for last 20 trials in this stage for its completion'); next_row(y); NumeditParam(obj, 'recent_timeout', 0.15, x, y,'label','Recent_TimeoutRate','TooltipString','timeout rate for last 20 trials in this stage for its completion'); next_row(y); NumeditParam(obj, 'stage_violation', 0.35, x, y,'label','Stage_ViolationRate','TooltipString','overall violation rate in this stage for its completion'); next_row(y); - NumeditParam(obj, 'total_trials', 800, x, y,'label','Trials','TooltipString','total trials in this stage for its completion'); next_row(y); + NumeditParam(obj, 'total_trials', 600, x, y,'label','Trials','TooltipString','total trials in this stage for its completion'); next_row(y); SubheaderParam(obj, 'title', 'Completion Params', x, y);next_row(y); case 6 @@ -87,7 +87,7 @@ NumeditParam(obj, 'recent_violation', 0.15, x, y,'label','Recent_ViolateRate','TooltipString','violation rate for last 20 trials in this stage for its completion'); next_row(y); NumeditParam(obj, 'recent_timeout', 0.15, x, y,'label','Recent_TimeoutRate','TooltipString','timeout rate for last 20 trials in this stage for its completion'); next_row(y); NumeditParam(obj, 'stage_violation', 0.25, x, y,'label','Stage_ViolationRate','TooltipString','overall violation rate in this stage for its completion'); next_row(y); - NumeditParam(obj, 'total_trials', 800, x, y,'label','Trials','TooltipString','total trials in this stage for its completion'); next_row(y); + NumeditParam(obj, 'total_trials', 600, x, y,'label','Trials','TooltipString','total trials in this stage for its completion'); next_row(y); SubheaderParam(obj, 'title', 'Completion Params', x, y);next_row(y); case 7 @@ -106,7 +106,7 @@ NumeditParam(obj, 'recent_violation', 0.15, x, y,'label','Recent_ViolateRate','TooltipString','violation rate for last 20 trials in this stage for its completion'); next_row(y); NumeditParam(obj, 'recent_timeout', 0.15, x, y,'label','Recent_TimeoutRate','TooltipString','timeout rate for last 20 trials in this stage for its completion'); next_row(y); NumeditParam(obj, 'stage_violation', 0.20, x, y,'label','Stage_ViolationRate','TooltipString','overall violation rate in this stage for its completion'); next_row(y); - NumeditParam(obj, 'total_trials', 800, x, y,'label','Trials','TooltipString','total trials in this stage for its completion'); next_row(y); + NumeditParam(obj, 'total_trials', 600, x, y,'label','Trials','TooltipString','total trials in this stage for its completion'); next_row(y); SubheaderParam(obj, 'title', 'Completion Params', x, y); next_row(y); end SubheaderParam(obj, 'title', 'AUTOMATED TRAINING STAGE', x, y); diff --git a/Protocols/@ArpitSoundCatContinuous/ArpitSoundCatContinuousSMA.m b/Protocols/@ArpitSoundCatContinuous/ArpitSoundCatContinuousSMA.m index 996a62cf..a262bc07 100644 --- a/Protocols/@ArpitSoundCatContinuous/ArpitSoundCatContinuousSMA.m +++ b/Protocols/@ArpitSoundCatContinuous/ArpitSoundCatContinuousSMA.m @@ -83,14 +83,14 @@ end % scheduled wave for rewarded side either of the side - sma = add_scheduled_wave(sma, 'name', 'reward_delivery', 'preamble', reward_delay, ... + sma = add_scheduled_wave(sma, 'name', 'reward_delivery', 'preamble', 0.01, ... 'sustain', WValveTime, 'DOut', WValveSide); % water delivery side sma = add_scheduled_wave(sma, 'name', 'reward_collection_dur', 'preamble', SideLed_duration + RewardCollection_duration); % time to collect the reward %% %%%%%%%%%%% % *STATES* %%%%%%%%%%%%%%%%%%% - sma = add_state(sma,'name','wait_for_cpoke','self_timer',cp_timeout, ... + sma = add_state(sma,'name','wait_for_cpoke','self_timer',CenterLed_duration, ... 'output_actions', {'DOut', center1led}, ... 'input_to_statechange', {'Cin','settling_in_state';'Tup','timeout_state'}); @@ -106,7 +106,7 @@ % Intermediate State % This intermediate state is considering the poke is out before the end of settling time / at the start of this state - sma = add_state(sma,'self_timer',CP_duration,'output_actions', {'DOut', center1led}, ... + sma = add_state(sma,'self_timer',CP_duration,'output_actions', {'DOut', center1led * LED_during_settling_legal_cbreak}, ... 'input_to_statechange', {'CP_Duration_wave_In','side_led_wait_RewardCollection';'Cin','current_state + 1';'Tup','side_led_wait_RewardCollection'}); % The state jump to here when the nose is still in then go @@ -123,7 +123,7 @@ % Intermediate State % This intermediate state is considering the poke is out before the end of settling time / at the start of this state - sma = add_state(sma,'self_timer',SettlingIn_time,'output_actions', {'DOut', center1led}, ... + sma = add_state(sma,'self_timer',SettlingIn_time,'output_actions', {'DOut', center1led * LED_during_settling_legal_cbreak}, ... 'input_to_statechange', {'settling_period_In','legal_poke_start_state';'Cin','current_state + 1';'Tup','legal_poke_start_state';... 'Rin', 'violation_state';'Rout', 'violation_state'; 'Lin', 'violation_state';'Lout', 'violation_state'}); @@ -137,7 +137,7 @@ % STATE TO CHECK BEFORE START OF LEGAL POKE PERIOD sma = add_state(sma,'name','legal_poke_start_state','self_timer',legal_cbreak/2, ... - 'output_actions', {'DOut', center1led}, ... + 'output_actions', {'DOut', center1led * LED_during_legal_cbreak}, ... 'input_to_statechange', {'Cin','soft_cp'; 'Tup','violation_state';... 'Rin', 'violation_state'; 'Rout', 'violation_state'; 'Lin', 'violation_state'; 'Lout', 'violation_state'}); %more stringent by giving half the legal cp time @@ -153,7 +153,7 @@ % Intermediate State % This intermediate state is considering the poke is out before the end of settling time / at the start of this state - sma = add_state(sma,'self_timer',legal_cbreak,'output_actions', {'DOut', center1led}, ... + sma = add_state(sma,'self_timer',legal_cbreak,'output_actions', {'DOut', center1led * LED_during_legal_cbreak}, ... 'input_to_statechange', {'CP_Duration_wave_In','legal_poke_end_state';'Cin','current_state + 1';'Tup','violation_state';... 'Rin', 'violation_state';'Rout', 'violation_state'; 'Lin', 'violation_state';'Lout', 'violation_state'}); @@ -168,22 +168,44 @@ % within legal c_break other wise its a violation sma = add_state(sma,'name','legal_poke_end_state','self_timer',legal_cbreak/2, ... - 'output_actions', {'DOut', center1led}, ... + 'output_actions', {'DOut', center1led * LED_during_legal_cbreak}, ... 'input_to_statechange', {'Cin','side_led_wait_RewardCollection'; 'Tup','violation_state'}); %%%%%%%%%%%%%%% REWARD COLLECTION STATE START %%%%%%%%%%%%%%% sma = add_state(sma, 'name', 'side_led_wait_RewardCollection', 'self_timer', SideLed_duration + RewardCollection_duration, ... - 'output_actions', {'DOut', SideLight; 'SchedWaveTrig', 'reward_collection_dur+Go_Cue'}, ... + 'output_actions', {'DOut', SideLight * Side_LED; 'SchedWaveTrig', 'reward_collection_dur+Go_Cue'}, ... 'input_to_statechange',{HitEvent,'hit_state'; 'Tup','timeout_state'; ErrorEvent,'second_hit_state'}); + + % Based upon whether the user has selected reward to be given + % always or with delay or no reward at all for error event + + if strcmp(reward_type, 'Always') + + sma = add_state(sma,'name','second_hit_state','self_timer',RewardCollection_duration,... + 'output_actions',{'DOut', SideLight},... + 'input_to_statechange',{'reward_collection_dur_In', 'timeout_state'; 'Tup','timeout_state'; HitEvent,'hit_state'}); + + elseif strcmp(reward_type, 'DelayedReward') + + sma = add_state(sma,'name','second_hit_state','self_timer',secondhit_delay,... + 'input_to_statechange',{'Tup','current_state + 1';}); - sma = add_state(sma,'name','second_hit_state','self_timer',RewardCollection_duration,... - 'output_actions',{'DOut', SideLight},... - 'input_to_statechange',{'reward_collection_dur_In', 'timeout_state'; 'Tup','timeout_state'; HitEvent,'hit_state'}); + sma = add_state(sma,'self_timer',RewardCollection_duration,... + 'output_actions',{'DOut', SideLight},... + 'input_to_statechange',{'Tup','timeout_state'; HitEvent,'hit_state'}); - sma = add_state(sma,'name','hit_state','self_timer',0.01,... - 'output_actions', {'SchedWaveTrig','reward_delivery'},... + else % no reward but a punishment iti + sma = add_state(sma,'name','second_hit_state','self_timer',error_iti,... + 'input_to_statechange',{'reward_collection_dur_In', 'timeout_state'; 'Tup','preclean_up_state'}); + sma = add_state(sma, 'name', 'hit_state'); + sma = add_state(sma, 'name', 'drink_state'); + + end + + sma = add_state(sma,'name','hit_state','self_timer',reward_delay,... + 'output_actions', {'DOut', SideLight; 'SchedWaveTrig','reward_delivery'},... 'input_to_statechange',{'Tup','drink_state'}); sma = add_state(sma,'name','drink_state','self_timer',drink_time,... @@ -219,8 +241,9 @@ % defined. state_names = get_labels(sma); state_names = state_names(:,1); prepare_next_trial_states = {'side_led_wait_RewardCollection','hit_state','second_hit_state','drink_state', 'violation_state','timeout_state','preclean_up_state'}; - + sma = StimulatorSection(obj,'prepare_next_trial',sma); dispatcher('send_assembler', sma, intersect(state_names, prepare_next_trial_states)); + case 'get_state_colors' varargout{1} = struct( ... diff --git a/Protocols/@ArpitSoundCatContinuous/SideSection.m b/Protocols/@ArpitSoundCatContinuous/SideSection.m index ab8ded75..dd4262b8 100644 --- a/Protocols/@ArpitSoundCatContinuous/SideSection.m +++ b/Protocols/@ArpitSoundCatContinuous/SideSection.m @@ -85,37 +85,80 @@ feval(mfilename, obj, 'change_water_modulation_params'); next_column(x); y = 5; + + % Reward Collection + MenuParam(obj, 'reward_type', {'Always','DelayedReward', 'NoReward'}, ... + 'Always', x, y, 'labelfraction', 0.35, 'TooltipString', sprintf(['\nThis menu is to determine the Reward delivery on wrong-hit trials\n',... + '\nIf ''Always'': reward will be available on each trial no matter which side rat goes first\n',... + '\n If rat pokes first on the wrong side, then reward will be delivered with a delay (if DelayedReward) or not delivered at all (if NoReward)'])); + set_callback(reward_type, {mfilename, 'new_reward_type'}); + next_row(y); + NumeditParam(obj,'secondhit_delay',0,x,y,'label','SecondHit Delay','TooltipString','Reward will be delayed with this amount if reward_type=DelayedReward'); + next_row(y); NumeditParam(obj, 'RewardCollection_duration', 10, x,y,'label','RewardCollection_duration','TooltipString','Wait until rat collects the reward'); + next_row(y); + NumeditParam(obj, 'SideLed_duration', 0.5, x,y,'label','Side LED duration','TooltipString','Duration of SideLed'); + next_row(y); + MenuParam(obj, 'side_lights' ,{'none','both','correct side','anti side'},1, x,y,'label','Side Lights','TooltipString','Controls the side LEDs during wait_for_spoke'); + next_row(y); + NumeditParam(obj, 'reward_delay', 0.01, x,y,'label','Reward Delay','TooltipString','Delay between side poke and reward delivery'); + next_row(y); + NumeditParam(obj, 'drink_time', 1, x,y,'label','Drink Time','TooltipString','waits to finish water delivery'); next_row(y); - NumeditParam(obj, 'CenterLed_duration', 200, x,y,'label','Central LED duration','TooltipString','Duration of Center Led'); + NumeditParam(obj, 'error_iti', 3, x,y,'label','Error Timeout','TooltipString','ITI on error trials(valid only for NoReward Condition)'); next_row(y); - NumeditParam(obj, 'SideLed_duration', 0.5, x,y,'label','Side LED duration','TooltipString','Duration of SideLed'); + + % Centre Poke + NumeditParam(obj, 'CenterLed_duration', 200, x,y,'label','Central LED duration','TooltipString','Duration of poke before Timeout'); + next_row(y); + NumeditParam(obj, 'violation_iti', 1, x,y,'label','Violation Timeout','TooltipString','Center poke violation duration'); next_row(y); - NumeditParam(obj, 'legal_cbreak', 0.1, x,y, 'position', [x, y, 175 20], 'TooltipString','Time in sec for which it is ok to be outside the center port before a violation occurs.'); + NumeditParam(obj, 'timeout_iti', 1, x,y,'label','Timeout ITI','TooltipString','ITI on timeout trials'); + next_row(y); + NumeditParam(obj, 'legal_cbreak', 0.1, x,y, 'position', [x, y, 175 20], 'TooltipString','Time in sec for which it is ok to be outside the center port before a violation occurs.'); ToggleParam(obj, 'LED_during_legal_cbreak', 1, x, y, 'OnString', 'LED ON LcB', 'OffString', 'LED off LcB', ... 'position', [x+180 y 20 20], 'TooltipString', ... 'If 1 (black), turn center port LED back on during legal_cbreak; if 0 (brown), leave LED off'); next_row(y); NumeditParam(obj, 'SettlingIn_time', 0.2, x,y, 'position', [x, y, 175 20], 'TooltipString','Initial settling period during which "legal cbreak period" can be longer than the usual "legal_cbreak"'); - next_row(y); - NumeditParam(obj, 'settling_legal_cbreak', 0.1, x,y, 'position', [x, y, 175 20], 'TooltipString','Time in sec for which it is ok during the "SettlingIn_time" to be outside the center port before a violation occurs.'); - ToggleParam(obj, 'LED_during_settling_legal_cbreak', 0, x, y, 'OnString', 'LED ON SetLcB', 'OffString', 'LED OFF setLcB', ... + ToggleParam(obj, 'LED_during_settling_legal_cbreak', 0, x, y, 'OnString', 'LED ON SetLcB', 'OffString', 'LED OFF setLcB', ... 'position', [x+180 y 20 20], 'TooltipString', ... 'If 1 (black), turn center port LED back on during settling_legal_cbreak; if 0 (brown), leave LED off'); next_row(y); - MenuParam(obj, 'side_lights' ,{'none','both','correct side','anti side'},1, x,y,'label','Side Lights','TooltipString','Controls the side LEDs during wait_for_spoke'); - next_row(y); - - NumeditParam(obj, 'A1_time', 0.4, x,y,'label','AUD1 on Time','TooltipString','Duration of first stimulus'); + % PreStim + NumeditParam(obj, 'PreStim_time_Min', 0.20, x,y,'label','Pre-Stim Min','TooltipString','Min Time in NIC before starting the stimulus'); + set_callback(PreStim_time_Min, {mfilename, 'new_CP_duration'}); next_row(y); + NumeditParam(obj, 'PreStim_time', 0.20, x,y,'label','Pre-Stim time','TooltipString','Actual Time in NIC before starting the stimulus'); + set_callback(PreStim_time, {mfilename, 'new_CP_duration'}); + next_row(y); + NumeditParam(obj, 'PreStim_time_Max', 0.40, x,y,'label','Pre-Stim Max','TooltipString','Max Time in NIC before starting the stimulus'); + set_callback(PreStim_time_Max, {mfilename, 'new_CP_duration'}); + next_row(y); + % A1 Time + NumeditParam(obj, 'A1_time_Min', 0.1, x,y,'label','Min time AUD1','TooltipString','Min value to select the Duration of fixed stimulus'); + set_callback(A1_time_Min, {mfilename, 'new_CP_duration'}); + next_row(y); + NumeditParam(obj, 'A1_time', 0.4, x,y,'label','AUD1 Time','TooltipString','Actual Duration of fixed stimulus'); set_callback(A1_time, {mfilename, 'new_CP_duration'}); - NumeditParam(obj, 'PreStim_time', 0.20, x,y,'label','Pre-Stim NIC time','TooltipString','Time in NIC before starting the stimulus'); next_row(y); - set_callback(PreStim_time, {mfilename, 'new_CP_duration'}); - NumeditParam(obj, 'time_bet_aud1_gocue', 0, x,y,'label','A1-GoCue time','TooltipString','time between the end of the stimulus and the go cue '); + NumeditParam(obj, 'A1_time_Max', 0.4, x,y,'label','Max time AUD1','TooltipString','Max value to select the Duration of fixed stimulus'); + set_callback(A1_time_Max, {mfilename, 'new_CP_duration'}); + next_row(y); + % Time b/w stim and Go Cue + NumeditParam(obj, 'time_bet_aud1_gocue_Min', 0.2, x,y,'label','Min A1-GoCue time','TooltipString','Min time between the end of the stimulus and the go cue '); + set_callback(time_bet_aud1_gocue_Min, {mfilename, 'new_CP_duration'}); + next_row(y); + NumeditParam(obj, 'time_bet_aud1_gocue', 0.2, x,y,'label','A1-GoCue time','TooltipString','Actual time between the end of the stimulus and the go cue '); + set_callback(time_bet_aud1_gocue, {mfilename, 'new_CP_duration'}); + next_row(y); + NumeditParam(obj, 'time_bet_aud1_gocue_Max', 2, x,y,'label','Max A1-GoCue time','TooltipString','Max time between the end of the stimulus and the go cue '); + set_callback(time_bet_aud1_gocue_Max, {mfilename, 'new_CP_duration'}); next_row(y); set_callback(time_bet_aud1_gocue, {mfilename, 'new_CP_duration'}); + + DispParam(obj, 'init_CP_duration', 0.01, x,y,'label','init_CP duration','TooltipString','Duration of Nose in Central Poke before Go cue starts (see Total_CP_duration)'); next_row(y); DispParam(obj, 'CP_duration', PreStim_time+A1_time+time_bet_aud1_gocue, x,y,'label','CP duration','TooltipString','Duration of Nose in Central Poke before Go cue starts (see Total_CP_duration)'); @@ -126,61 +169,44 @@ next_row(y); DispParam(obj, 'Total_CP_duration', CP_duration+time_go_cue, x, y, 'TooltipString', 'Total nose in center port time, in secs. Sum of CP_duration and Go Cue duration'); %#ok<*NODEF> next_row(y); + + % Toggle Buttons To Control Parameters ToggleParam(obj, 'warmup_on', 1, x,y,... 'OnString', 'Warmup ON',... 'OffString', 'Warmup OFF',... 'TooltipString', sprintf(['If on (Yellow) then it applies the initial warming up phase, during which the\n',... - 'CP_duration starts small and gradually grows to last_session_max_cp_duration'])); + 'CP_duration starts small and gradually grows to last_session_max_cp_duration'])); next_row(y); - % to modify it for widefield imagine - NumeditParam(obj,'imaging',0,x,y,'label','Scope Trigger'); + ToggleParam(obj, 'stimuli_on', 0, x,y,... + 'OnString', 'Use Stimuli',... + 'OffString', 'Fixed Sound',... + 'TooltipString', sprintf('If on (black) then it enables training with stimuli else using a fixed sound from Stage 5')); next_row(y); - NumeditParam(obj, 'reward_delay', 0.01, x,y,'label','Reward Delay','TooltipString','Delay between side poke and reward delivery'); - next_row(y); - NumeditParam(obj, 'reward_duration', 0.1, x,y,'label','Reward Duration','TooltipString','Duration of reward sound'); - next_row(y); - NumeditParam(obj, 'drink_time', 1, x,y,'label','Drink Time','TooltipString','waits to finish water delivery'); - next_row(y); - NumeditParam(obj, 'error_iti', 5, x,y,'label','Error Timeout','TooltipString','ITI on error trials'); - next_row(y); - NumeditParam(obj, 'violation_iti', 1, x,y,'label','Violation Timeout','TooltipString','Center poke violation duration'); - next_row(y); - MenuParam(obj, 'reward_type', {'Always','DelayedReward', 'NoReward'}, ... - 'Always', x, y, 'labelfraction', 0.35, 'TooltipString', sprintf(['\nThis menu is to determine the Reward delivery on wrong-hit trials\n',... - '\nIf ''Always'': reward will be available on each trial no matter which side rat goes first\n',... - '\n If rat pokes first on the wrong side, then reward will be delivered with a delay (if DelayedReward) or not delivered at all (if NoReward)'])); - set_callback(reward_type, {mfilename, 'new_reward_type'}); - next_row(y); - NumeditParam(obj,'secondhit_delay',0,x,y,'label','SecondHit Delay','TooltipString','Reward will be delayed with this amount if reward_type=DelayedReward'); - + ToggleParam(obj, 'random_PreStim_time', 0, x,y,... + 'OnString', 'random PreStim_time ON',... + 'OffString', 'random PreStim_time OFF',... + 'TooltipString', sprintf('If on (black) then it enables the random time between the user given range')); + set_callback(random_PreStim_time, {mfilename, 'new_CP_duration'}); next_row(y); ToggleParam(obj, 'random_A1_time', 0, x,y,... 'OnString', 'random A1_time ON',... 'OffString', 'random A1_time OFF',... 'TooltipString', sprintf('If on (black) then it enables the random sampling of A1_time')); + set_callback(random_A1_time, {mfilename, 'new_CP_duration'}); next_row(y); ToggleParam(obj, 'random_prego_time', 0, x,y,... 'OnString', 'random prego_time ON',... 'OffString', 'random prego_time OFF',... 'TooltipString', sprintf('If on (black) then it enables the random sampling of time between the end of the stimulus and the go cue')); - + set_callback(random_prego_time, {mfilename, 'new_CP_duration'}); next_column(x); y=5; - NumeditParam(obj,'trials_in_stage',1,x,y,'label','Trial Counter'); - next_row(y); - NumeditParam(obj,'training_stage',1,x,y,'label','Training Stage'); + next_row(y); ToggleParam(obj,'use_training',0,x,y,'OnString','Using Autotrain','OffString','Manual Settings'); - - ToggleParam(obj, 'stimuli_on', 1, x,y,... - 'OnString', 'Stimuli ON',... - 'OffString', 'Stimuli OFF',... - 'TooltipString', sprintf('If on (black) then it disable the presentation of sound stimuli during nose poke')); - set_callback(stimuli_on, {mfilename, 'new_CP_duration'}); - next_row(y); - SoloFunctionAddVars('SoundCatSMA', 'ro_args', ... + SoloFunctionAddVars('ArpitSoundCatContinuousSMA', 'ro_args', ... {'CP_duration';'SideLed_duration';'CenterLed_duration';'side_lights' ; ... 'RewardCollection_duration';'training_stage'; ... 'legal_cbreak' ; 'LED_during_legal_cbreak' ; ... @@ -212,38 +238,45 @@ %% change_water_modulation_params - case 'change_water_modulation_params', + case 'change_water_modulation_params' display_guys = [1 150 300]; - for i=1:numel(display_guys), + for i=1:numel(display_guys) t = display_guys(i); myvar = eval(sprintf('trial_%d', t)); myvar.value = maxasymp + (minasymp/(1+(t/inflp)^slp).^assym); - end; + end case 'new_leftprob' AntibiasSectionAthena(obj, 'update_biashitfrac', value(LeftProb)); - case 'new_CP_duration' - if stimuli_on == 0 - PreStim_time.value=0; - A1_time.value=0; - time_bet_aud1_gocue.value=0; - disable(PreStim_time); - disable(A1_time); - disable(time_bet_aud1_gocue); + ccase 'new_CP_duration' + + if random_PreStim_time == 1 && (value(PreStim_time) < value(PreStim_time_Min) || value(PreStim_time) > value(PreStim_time_Max)) + PreStim_time.value = value(PreStim_time_Min); else - enable(PreStim_time); - enable(A1_time); - enable(time_bet_aud1_gocue); + PreStim_time.value = value(PreStim_time); end - CP_duration.value=PreStim_time + A1_time + time_bet_aud1_gocue; - Total_CP_duration.value = CP_duration + time_go_cue; %#ok<*NASGU> + + if random_A1_time == 1 && (value(A1_time) < value(A1_time_Min) || value(A1_time) > value(A1_time_Max)) + A1_time.value = value(A1_time_Min); + else + A1_time.value = value(A1_time); + end + + if random_prego_time == 1 && (value(time_bet_aud1_gocue) < value(time_bet_aud1_gocue_Min) || value(time_bet_aud1_gocue) > value(time_bet_aud1_gocue_Max)) + time_bet_aud1_gocue.value = value(time_bet_aud1_gocue_Min); + else + time_bet_aud1_gocue.value = value(time_bet_aud1_gocue); + end + + CP_duration.value= value(SettlingIn_time) + value(PreStim_time) + value(A1_time) + value(time_bet_aud1_gocue); + Total_CP_duration.value = value(CP_duration) + value(time_go_cue); %#ok<*NASGU> case 'new_time_go_cue' - Total_CP_duration.value = CP_duration + time_go_cue; + Total_CP_duration.value = value(CP_duration) + value(time_go_cue); SoundInterface(obj, 'set', 'GoSound', 'Dur1', value(time_go_cue)); case 'new_reward_type' @@ -258,33 +291,28 @@ case 'prepare_next_trial' - if random_A1_time - A1_times = [0.2, 0.4]; - randomix = randi(length(A1_times), 1); - A1_time.value = A1_times(randomix); + if random_prego_time == 1 + time_range_go_cue = value(time_bet_aud1_gocue_Min):0.01:value(time_bet_aud1_gocue_Max); + time_bet_aud1_gocue.value = time_range_go_cue(randi([1, numel(time_range_go_cue)],1,1)); + end + + if random_A1_time == 1 + time_range_A1_time = value(A1_time_Min): 0.01 : value(A1_time_Max); + A1_time.value = time_range_A1_time(randi([1, numel(time_range_A1_time)],1,1)); end - if random_prego_time - prego_times = [0.2, 0.4, 0.6, 0.8, 1]; - randomixx = randi(length(prego_times), 1); - time_bet_aud1_gocue.value = prego_times(randomixx); + if random_PreStim_time == 1 + time_range_PreStim_time = value(PreStim_time_Min) : 0.01 : value(PreStim_time_Max); + PreStim_time.value = time_range_PreStim_time(randi([1, numel(time_range_PreStim_time)],1,1)); end - settling_time.value=0.25; - delay_time.value=0; - allow_nic_breaks.value=1; - side_lights.value=3; - training_stage.value=1; - trials_in_stage.value=0; - reward_delay.value=0.01; - left_prob.value=0.5; - right_prob.value=0.5; + if n_done_trials <1 && warmup_on ==1 - CP_duration.value=value(init_CP_duration); + CP_duration.value = value(init_CP_duration); else - CP_duration.value=PreStim_time + A1_time + time_bet_aud1_gocue; + CP_duration.value = value(SettlingIn_time) + value(A1_time) + value(PreStim_time) + value(time_bet_aud1_gocue); end - Total_CP_duration.value = CP_duration + time_go_cue; %#ok<*NASGU> + Total_CP_duration.value = value(CP_duration) + value(time_go_cue); %#ok<*NASGU> %% update hit_history, previous_sides, etc From 5bbf68b5495bbe700213273de9c7892ad1c7074c Mon Sep 17 00:00:00 2001 From: Arpit Date: Thu, 5 Jun 2025 14:51:05 +0100 Subject: [PATCH 122/164] error debug --- .../ArpitCentrePokeTraining.m | 1 - .../ArpitSoundCatContinuous.m | 15 +-------------- Protocols/@ArpitSoundCatContinuous/state_colors.m | 14 ++++++++++++++ Protocols/@ArpitSoundCatContinuous/wave_colors.m | 11 +++++++++++ 4 files changed, 26 insertions(+), 15 deletions(-) create mode 100644 Protocols/@ArpitSoundCatContinuous/state_colors.m create mode 100644 Protocols/@ArpitSoundCatContinuous/wave_colors.m diff --git a/Protocols/@ArpitCentrePokeTraining/ArpitCentrePokeTraining.m b/Protocols/@ArpitCentrePokeTraining/ArpitCentrePokeTraining.m index 66e16188..7f6b6b94 100644 --- a/Protocols/@ArpitCentrePokeTraining/ArpitCentrePokeTraining.m +++ b/Protocols/@ArpitCentrePokeTraining/ArpitCentrePokeTraining.m @@ -158,7 +158,6 @@ set_callback({maxasymp;slp;inflp;minasymp;assym}, {mfilename, 'change_water_modulation_params'}); feval(mfilename, obj, 'change_water_modulation_params'); - %AthenaSMA changed to SoundCatSMA (From AthenaDelayComp) SoloFunctionAddVars('ParamsSection', 'ro_args', ... {'maxasymp';'slp';'inflp';'minasymp';'assym'}); diff --git a/Protocols/@ArpitSoundCatContinuous/ArpitSoundCatContinuous.m b/Protocols/@ArpitSoundCatContinuous/ArpitSoundCatContinuous.m index c41c6de4..a3d2aa70 100644 --- a/Protocols/@ArpitSoundCatContinuous/ArpitSoundCatContinuous.m +++ b/Protocols/@ArpitSoundCatContinuous/ArpitSoundCatContinuous.m @@ -79,20 +79,7 @@ [x, y] = SavingSection(obj, 'init', x, y); [x, y] = WaterValvesSection(obj, 'init', x, y); - - % For plotting with the pokesplot plugin, we need to tell it what - % colors to plot with: - my_state_colors = SoundCatSMA(obj, 'get_state_colors'); - % In pokesplot, the poke colors have a default value, so we don't need - % to specify them, but here they are so you know how to change them. - my_poke_colors = struct( ... - 'L', 0.6*[1 0.66 0], ... - 'C', [0 0 0], ... - 'R', 0.9*[1 0.66 0]); - - [x, y] = PokesPlotSection(obj, 'init', x, y, ... - struct('states', my_state_colors, 'pokes', my_poke_colors)); next_row(y); - + [x, y] = PokesPlotSection(obj, 'init', x, y); [x, y] = CommentsSection(obj, 'init', x, y); SessionDefinition(obj, 'init', x, y, value(myfig)); next_row(y, 2); %#ok diff --git a/Protocols/@ArpitSoundCatContinuous/state_colors.m b/Protocols/@ArpitSoundCatContinuous/state_colors.m new file mode 100644 index 00000000..1e3b1737 --- /dev/null +++ b/Protocols/@ArpitSoundCatContinuous/state_colors.m @@ -0,0 +1,14 @@ +function SC = state_colors(obj) %#ok + +SC = struct( ... + 'wait_for_cpoke', [0.68 1 0.63], ... + 'settling_in_state', [0.63 1 0.94], ... + 'legal_poke_start_state', [0.63 1 0.94]*0.8, ... + 'legal_poke_end_state', [1 0.79 0.63], ... + 'soft_cp', [0.3 0.9 0], ... + 'side_led_wait_RewardCollection', [0.53 0.78 1.00],... + 'hit_state', [0.77 0.60 0.48], ... + 'second_hit_state', [0.25 0.45 0.48], ... + 'drink_state', [0 1 0], ... + 'violation_state', [0.31 0.48 0.30], ... + 'timeout_state', 0.8*[0.31 0.48 0.30]); diff --git a/Protocols/@ArpitSoundCatContinuous/wave_colors.m b/Protocols/@ArpitSoundCatContinuous/wave_colors.m new file mode 100644 index 00000000..0d5a7339 --- /dev/null +++ b/Protocols/@ArpitSoundCatContinuous/wave_colors.m @@ -0,0 +1,11 @@ +function WC = wave_colors(obj) %#ok + +WC = struct(... + 'stim_wave', [1 1 1 ], ... + 'prestim_wave', [1 1 1 ], ... + 'stimulator_wave', [1 1 0 ], ... + 'stimulator_wave1', [1 1 0 ], ... + 'stimulator_wave2', [1 1 0 ], ... + 'stimulator_wave3', [1 1 0 ], ... + 'stimulator_wave4', [1 1 0 ], ... + 'stimulator_wave5', [1 1 0 ]); \ No newline at end of file From 57d13a947b9f153fb33360f2ff2b68e8d7f6255b Mon Sep 17 00:00:00 2001 From: Arpit Date: Thu, 5 Jun 2025 15:04:56 +0100 Subject: [PATCH 123/164] changing the gif layout of ArpitSoundCatContinuous --- .../ArpitSoundCatContinuous.m | 32 +++++++++++++++++-- .../@ArpitSoundCatContinuous/SideSection.m | 25 ++------------- 2 files changed, 31 insertions(+), 26 deletions(-) diff --git a/Protocols/@ArpitSoundCatContinuous/ArpitSoundCatContinuous.m b/Protocols/@ArpitSoundCatContinuous/ArpitSoundCatContinuous.m index a3d2aa70..b8a1a86c 100644 --- a/Protocols/@ArpitSoundCatContinuous/ArpitSoundCatContinuous.m +++ b/Protocols/@ArpitSoundCatContinuous/ArpitSoundCatContinuous.m @@ -77,6 +77,31 @@ x = 5; y = 5; % Initial position on main GUI window + %% slow ramp up of water amount + %%the water volume is controlled by a 5-parameter logistic function: WaterAmount(t) = maxasymp + (minasymp/(1+(t/inflp)^slp).^assym) + NumeditParam(obj, 'maxasymp', 38, x,y,'label','maxasymp','TooltipString',... + 'the water volume is controlled by a 5-parameter logistic function: WaterAmount(trialnum) = maxasymp + (minasymp/(1+(trialnum/inflp)^slp).^assym)'); + next_row(y); + NumeditParam(obj, 'slp', 3, x,y,'label','slp','TooltipString','Water Modulation: Slope of the logistic function'); + next_row(y); + NumeditParam(obj, 'inflp', 350, x,y,'label','inflp','TooltipString','Water Modulation: concentration at the inflection point'); + next_row(y); + NumeditParam(obj, 'minasymp', -21, x,y,'label','inflp','TooltipString','Water Modulation: minimum asymptote'); + next_row(y); + NumeditParam(obj, 'assym', 0.7, x,y,'label','assym','TooltipString','Water Modulation: asymmetry factor'); + next_row(y); + DispParam(obj, 'trial_1', 0, x, y, 'TooltipString', 'uL on first trial'); + next_row(y); + DispParam(obj, 'trial_150', 0, x, y, 'TooltipString', 'uL on trial 150'); + next_row(y); + DispParam(obj, 'trial_300', 0, x, y, 'TooltipString', 'uL on trial 300'); + next_row(y); + set_callback({maxasymp;slp;inflp;minasymp;assym}, {mfilename, 'change_water_modulation_params'}); + feval(mfilename, obj, 'change_water_modulation_params'); + + SoloFunctionAddVars('SideSection', 'ro_args', ... + {'maxasymp';'slp';'inflp';'minasymp';'assym'}); + [x, y] = SavingSection(obj, 'init', x, y); [x, y] = WaterValvesSection(obj, 'init', x, y); [x, y] = PokesPlotSection(obj, 'init', x, y); @@ -84,12 +109,13 @@ SessionDefinition(obj, 'init', x, y, value(myfig)); next_row(y, 2); %#ok next_column(x); y=5; - [x, y] = PerformanceSection(obj, 'init', x, y); - [x, y] = StimulatorSection(obj, 'init', x, y); next_row(y, 1.3); + [x, y] = SideSection(obj, 'init', x, y); %#ok [x, y] = SoundSection(obj,'init',x,y); [x, y] = StimulusSection(obj,'init',x,y); - + + [x, y] = PerformanceSection(obj, 'init', x, y); + [x, y] = StimulatorSection(obj, 'init', x, y); next_row(y, 1.3); figpos = get(double(gcf), 'Position'); [expmtr, rname]=SavingSection(obj, 'get_info'); HeaderParam(obj, 'prot_title', [mfilename ': ' expmtr ', ' rname], x, y, 'position', [10 figpos(4)-25, 800 20]); diff --git a/Protocols/@ArpitSoundCatContinuous/SideSection.m b/Protocols/@ArpitSoundCatContinuous/SideSection.m index dd4262b8..3ec3ea90 100644 --- a/Protocols/@ArpitSoundCatContinuous/SideSection.m +++ b/Protocols/@ArpitSoundCatContinuous/SideSection.m @@ -62,28 +62,7 @@ DeclareGlobals(obj, 'ro_args', 'previous_sides'); SubheaderParam(obj, 'title', 'Sides Section', x, y); next_row(y, 1.5); - %% slow ramp up of water amount - %%the water volume is controlled by a 5-parameter logistic function: WaterAmount(t) = maxasymp + (minasymp/(1+(t/inflp)^slp).^assym) - NumeditParam(obj, 'maxasymp', 38, x,y,'label','maxasymp','TooltipString',... - 'the water volume is controlled by a 5-parameter logistic function: WaterAmount(trialnum) = maxasymp + (minasymp/(1+(trialnum/inflp)^slp).^assym)'); - next_row(y); - NumeditParam(obj, 'slp', 3, x,y,'label','slp','TooltipString','Water Modulation: Slope of the logistic function'); - next_row(y); - NumeditParam(obj, 'inflp', 350, x,y,'label','inflp','TooltipString','Water Modulation: concentration at the inflection point'); - next_row(y); - NumeditParam(obj, 'minasymp', -21, x,y,'label','inflp','TooltipString','Water Modulation: minimum asymptote'); - next_row(y); - NumeditParam(obj, 'assym', 0.7, x,y,'label','assym','TooltipString','Water Modulation: asymmetry factor'); - next_row(y); - DispParam(obj, 'trial_1', 0, x, y, 'TooltipString', 'uL on first trial'); - next_row(y); - DispParam(obj, 'trial_150', 0, x, y, 'TooltipString', 'uL on trial 150'); - next_row(y); - DispParam(obj, 'trial_300', 0, x, y, 'TooltipString', 'uL on trial 300'); - next_row(y); - set_callback({maxasymp;slp;inflp;minasymp;assym}, {mfilename, 'change_water_modulation_params'}); - feval(mfilename, obj, 'change_water_modulation_params'); - + next_column(x); y = 5; % Reward Collection @@ -164,7 +143,7 @@ DispParam(obj, 'CP_duration', PreStim_time+A1_time+time_bet_aud1_gocue, x,y,'label','CP duration','TooltipString','Duration of Nose in Central Poke before Go cue starts (see Total_CP_duration)'); set_callback(CP_duration, {mfilename, 'new_CP_duration'}); next_row(y); - NumeditParam(obj, 'time_go_cue' ,0, x,y,'label','Go Cue Duration','TooltipString','duration of go cue (see Total_CP_duration)'); + NumeditParam(obj, 'time_go_cue' ,0.2, x,y,'label','Go Cue Duration','TooltipString','duration of go cue (see Total_CP_duration)'); set_callback(time_go_cue, {mfilename, 'new_time_go_cue'}); next_row(y); DispParam(obj, 'Total_CP_duration', CP_duration+time_go_cue, x, y, 'TooltipString', 'Total nose in center port time, in secs. Sum of CP_duration and Go Cue duration'); %#ok<*NODEF> From 39d2255ca4832ccd0b48576f5285ea84a9c92223 Mon Sep 17 00:00:00 2001 From: Arpit Date: Thu, 5 Jun 2025 15:14:42 +0100 Subject: [PATCH 124/164] error debug --- .../ArpitSoundCatContinuous.m | 8 ++--- .../@ArpitSoundCatContinuous/SideSection.m | 30 +++++++++---------- 2 files changed, 18 insertions(+), 20 deletions(-) diff --git a/Protocols/@ArpitSoundCatContinuous/ArpitSoundCatContinuous.m b/Protocols/@ArpitSoundCatContinuous/ArpitSoundCatContinuous.m index b8a1a86c..af27eda6 100644 --- a/Protocols/@ArpitSoundCatContinuous/ArpitSoundCatContinuous.m +++ b/Protocols/@ArpitSoundCatContinuous/ArpitSoundCatContinuous.m @@ -76,7 +76,8 @@ SoundManagerSection(obj, 'init'); x = 5; y = 5; % Initial position on main GUI window - + [x, y] = SavingSection(obj, 'init', x, y); + %% slow ramp up of water amount %%the water volume is controlled by a 5-parameter logistic function: WaterAmount(t) = maxasymp + (minasymp/(1+(t/inflp)^slp).^assym) NumeditParam(obj, 'maxasymp', 38, x,y,'label','maxasymp','TooltipString',... @@ -101,8 +102,7 @@ SoloFunctionAddVars('SideSection', 'ro_args', ... {'maxasymp';'slp';'inflp';'minasymp';'assym'}); - - [x, y] = SavingSection(obj, 'init', x, y); + [x, y] = WaterValvesSection(obj, 'init', x, y); [x, y] = PokesPlotSection(obj, 'init', x, y); [x, y] = CommentsSection(obj, 'init', x, y); @@ -113,7 +113,7 @@ [x, y] = SideSection(obj, 'init', x, y); %#ok [x, y] = SoundSection(obj,'init',x,y); [x, y] = StimulusSection(obj,'init',x,y); - + [x, y] = PerformanceSection(obj, 'init', x, y); [x, y] = StimulatorSection(obj, 'init', x, y); next_row(y, 1.3); figpos = get(double(gcf), 'Position'); diff --git a/Protocols/@ArpitSoundCatContinuous/SideSection.m b/Protocols/@ArpitSoundCatContinuous/SideSection.m index 3ec3ea90..1be67444 100644 --- a/Protocols/@ArpitSoundCatContinuous/SideSection.m +++ b/Protocols/@ArpitSoundCatContinuous/SideSection.m @@ -16,14 +16,12 @@ y0 = y; [x, y] = AntibiasSection(obj, 'init', x, y); - - + ToggleParam(obj, 'antibias_LRprob', 0, x,y,... 'OnString', 'AB_Prob ON',... 'OffString', 'AB_Prob OFF',... 'TooltipString', sprintf(['If on (Yellow) then it enables the AntiBias algorithm\n'... 'based on changing the probablity of Left vs Right'])); - next_row(y); NumeditParam(obj, 'LeftProb', 0.5, x, y); next_row(y); set_callback(LeftProb, {mfilename, 'new_leftprob'}); @@ -50,15 +48,15 @@ 'TooltipString', 'all right reward times are multiplied by this number'); next_row(y); ToggleParam(obj, 'antibias_wtr_mult', 0, x,y,... - 'OnString', 'AB ON',... - 'OffString', 'AB OFF',... + 'OnString', 'AB_Water ON',... + 'OffString', 'AB_Water OFF',... 'TooltipString', sprintf(['If on (black) then it disables the wtr_mult entries\n'... - 'and uses hitfrac to adjust the water times'])); - - next_row(y); - + 'and uses hitfrac to adjust the water times'])); + next_row(y); DispParam(obj, 'ThisTrial', 'LEFT', x, y); next_row(y); - SoloParamHandle(obj, 'previous_sides', 'value', []); + + + SoloParamHandle(obj, 'previous_sides', 'value', []); DeclareGlobals(obj, 'ro_args', 'previous_sides'); SubheaderParam(obj, 'title', 'Sides Section', x, y); next_row(y, 1.5); @@ -74,7 +72,7 @@ next_row(y); NumeditParam(obj,'secondhit_delay',0,x,y,'label','SecondHit Delay','TooltipString','Reward will be delayed with this amount if reward_type=DelayedReward'); next_row(y); - NumeditParam(obj, 'RewardCollection_duration', 10, x,y,'label','RewardCollection_duration','TooltipString','Wait until rat collects the reward'); + NumeditParam(obj, 'RewardCollection_duration', 10, x,y,'label','RColl_dur','TooltipString','Wait until rat collects the reward'); next_row(y); NumeditParam(obj, 'SideLed_duration', 0.5, x,y,'label','Side LED duration','TooltipString','Duration of SideLed'); next_row(y); @@ -88,7 +86,7 @@ next_row(y); % Centre Poke - NumeditParam(obj, 'CenterLed_duration', 200, x,y,'label','Central LED duration','TooltipString','Duration of poke before Timeout'); + NumeditParam(obj, 'CenterLed_duration', 200, x,y,'label','C_LED dur','TooltipString','Duration of poke before Timeout'); next_row(y); NumeditParam(obj, 'violation_iti', 1, x,y,'label','Violation Timeout','TooltipString','Center poke violation duration'); next_row(y); @@ -187,15 +185,15 @@ next_row(y); SoloFunctionAddVars('ArpitSoundCatContinuousSMA', 'ro_args', ... {'CP_duration';'SideLed_duration';'CenterLed_duration';'side_lights' ; ... - 'RewardCollection_duration';'training_stage'; ... + 'RewardCollection_duration'; ... 'legal_cbreak' ; 'LED_during_legal_cbreak' ; ... - 'SettlingIn_time';'settling_legal_cbreak' ; 'LED_during_settling_legal_cbreak' ; ... + 'SettlingIn_time' ; 'LED_during_settling_legal_cbreak' ; ... 'time_go_cue'; ... 'stimuli_on';'A1_time';'time_bet_aud1_gocue' ; ... 'PreStim_time';'warmup_on' - 'drink_time';'reward_delay';'reward_duration';'left_wtr_mult';... + 'drink_time';'reward_delay';'left_wtr_mult';... 'right_wtr_mult';'antibias_wtr_mult';... - 'reward_type';'secondhit_delay';'error_iti';'violation_iti';'imaging'}); + 'reward_type';'secondhit_delay';'error_iti';'violation_iti'}); SoloFunctionAddVars('StimulusSection', 'ro_args', ... {'ThisTrial';'A1_time';'time_bet_aud1_gocue' ; ... From cc551ebb4e47923eb9c0581e649a5a56beccd1d2 Mon Sep 17 00:00:00 2001 From: Arpit Date: Thu, 5 Jun 2025 15:21:14 +0100 Subject: [PATCH 125/164] error debug --- .../@ArpitSoundCatContinuous/SideSection.m | 6 +- .../@ArpitSoundCatContinuous/SoundSection.m | 62 ++ .../StimulusSection.m | 588 ++++++++++++++++++ 3 files changed, 652 insertions(+), 4 deletions(-) create mode 100644 Protocols/@ArpitSoundCatContinuous/SoundSection.m create mode 100644 Protocols/@ArpitSoundCatContinuous/StimulusSection.m diff --git a/Protocols/@ArpitSoundCatContinuous/SideSection.m b/Protocols/@ArpitSoundCatContinuous/SideSection.m index 1be67444..2bf17465 100644 --- a/Protocols/@ArpitSoundCatContinuous/SideSection.m +++ b/Protocols/@ArpitSoundCatContinuous/SideSection.m @@ -155,8 +155,8 @@ 'CP_duration starts small and gradually grows to last_session_max_cp_duration'])); next_row(y); ToggleParam(obj, 'stimuli_on', 0, x,y,... - 'OnString', 'Use Stimuli',... - 'OffString', 'Fixed Sound',... + 'OnString', 'Stimuli ON',... + 'OffString', 'Stimuli OFF',... 'TooltipString', sprintf('If on (black) then it enables training with stimuli else using a fixed sound from Stage 5')); next_row(y); ToggleParam(obj, 'random_PreStim_time', 0, x,y,... @@ -205,8 +205,6 @@ % History of hit/miss: SoloParamHandle(obj, 'deltaf_history', 'value', []); - SoloFunctionAddVars('OverallPerformanceSection', 'ro_args', ... - {'training_stage'}); SoloFunctionAddVars('SoundCatSMA', 'ro_args', ... {'maxasymp';'slp';'inflp';'minasymp';'assym'}); diff --git a/Protocols/@ArpitSoundCatContinuous/SoundSection.m b/Protocols/@ArpitSoundCatContinuous/SoundSection.m new file mode 100644 index 00000000..0fd22e60 --- /dev/null +++ b/Protocols/@ArpitSoundCatContinuous/SoundSection.m @@ -0,0 +1,62 @@ + + +function [x, y] = SoundSection(obj, action, varargin) + +GetSoloFunctionArgs(obj); + +switch action + + % ------------------------------------------------------------------ + % INIT + % ------------------------------------------------------------------ + + case 'init' + if length(varargin) < 2 + error('Need at least two arguments, x and y position, to initialize %s', mfilename); + end + x = varargin{1}; y = varargin{2}; + + ToggleParam(obj, 'SoundsShow', 0, x, y, 'OnString', 'Sounds', ... + 'OffString', 'Sounds', 'TooltipString', 'Show/Hide Sounds panel'); + set_callback(SoundsShow, {mfilename, 'show_hide'}); %#ok (Defined just above) + next_row(y); + oldx=x; oldy=y; parentfig=double(gcf); + + SoloParamHandle(obj, 'myfig', 'value', figure('Position', [100 100 560 440], ... + 'closerequestfcn', [mfilename '(' class(obj) ', ''hide'');'], 'MenuBar', 'none', ... + 'Name', mfilename), 'saveable', 0); + set(double(gcf), 'Visible', 'off'); + x=10;y=10; + + [x,y]=SoundInterface(obj,'add','ViolationSound',x,y,'Volume',0.01,'Freq',1000,'Duration',0.5); +% [x,y]=SoundInterface(obj,'add','ViolationSound',x,y,'Style','WhiteNoise','Volume',0.01); + [x,y]=SoundInterface(obj,'add','TimeoutSound',x,y,'Style','WhiteNoise','Volume',0.08,'Duration',0.5); + [x,y]=SoundInterface(obj,'add','RewardSound',x,y,'Style','Bups','Volume',1,'Freq',5,'Duration',1.5); + [x,y]=SoundInterface(obj,'add','ErrorSound',x,y,'Style','WhiteNoise','Volume',0.08); + next_column(x); + y=10; + [x,y]=SoundInterface(obj,'add','GoSound',x,y,'Style','Tone','Volume',0.01,'Freq',3000,'Duration',0.2); + SoundInterface(obj, 'disable', 'GoSound', 'Dur1'); + [x,y]=SoundInterface(obj,'add','SOneSound',x,y,'Style','WhiteNoise','Volume',0.007); + [x,y]=SoundInterface(obj,'add','STwoSound',x,y,'Style','WhiteNoise','Volume',0.07); + + x=oldx; y=oldy; + figure(parentfig); + + case 'hide' + SoundsShow.value = 0; + set(value(myfig), 'Visible', 'off'); + + case 'show' + SoundsShow.value = 1; + set(value(myfig), 'Visible', 'on'); + + case 'show_hide' + if SoundsShow == 1 + set(value(myfig), 'Visible', 'on'); % (defined by GetSoloFunctionArgs) + else + set(value(myfig), 'Visible', 'off'); + end + +end + \ No newline at end of file diff --git a/Protocols/@ArpitSoundCatContinuous/StimulusSection.m b/Protocols/@ArpitSoundCatContinuous/StimulusSection.m new file mode 100644 index 00000000..6ac08a0d --- /dev/null +++ b/Protocols/@ArpitSoundCatContinuous/StimulusSection.m @@ -0,0 +1,588 @@ + + +function [x, y] = StimulusSection(obj, action, varargin) + +GetSoloFunctionArgs(obj); + +switch action + + % ------------------------------------------------------------------ + % INIT + % ------------------------------------------------------------------ + + case 'init' + if length(varargin) < 2 + error('Need at least two arguments, x and y position, to initialize %s', mfilename); + end + x = varargin{1}; y = varargin{2}; + + ToggleParam(obj, 'StimulusShow', 0, x, y, 'OnString', 'Stimuli Show', ... + 'OffString', 'Stimuli Hidden', 'TooltipString', 'Show/Hide Stimulus panel'); + set_callback(StimulusShow, {mfilename, 'show_hide'}); %#ok (Defined just above) + next_row(y); + + oldx=x; oldy=y; parentfig=double(gcf); + + SoloParamHandle(obj, 'myfig', 'value', figure('closerequestfcn', [mfilename '(' class(obj) ', ''hide'');'],... + 'MenuBar', 'none', 'Name', mfilename), 'saveable', 0); + screen_size = get(0, 'ScreenSize'); + set(value(myfig),'Position',[1 screen_size(4)-740, 400 400]); % put fig at top right + set(double(gcf), 'Visible', 'off'); + + SoundManagerSection(obj, 'declare_new_sound', 'StimAUD1') + SoloParamHandle(obj, 'thisstim', 'value', []); + SoloParamHandle(obj, 'thisstimlog', 'value', []); + SoloParamHandle(obj, 'h1', 'value', []); + + x = 10; y=5; + + next_row(y); + next_row(y); + PushbuttonParam(obj, 'refresh_stimuli', x,y , 'TooltipString', 'Instantiates the stimuli given the new set of parameters'); + set_callback(refresh_stimuli, {mfilename, 'plot_stimuli'}); + + + next_row(y); + next_row(y); + MenuParam(obj, 'Rule', {'S1>S_boundary Left','S1>S_boundary Right'}, ... + 'S1>S_boundary Left', x, y, 'labelfraction', 0.35, 'TooltipString', sprintf(['\nThis buttom determines the rule\n', ... + '\n''S1>S_boundary Left'' means if Aud1 > Aud_boundry then reward will be delivered from the left water spout and if Aud1 < Aud_boundry then water comes from right\n',... + '\n''S1>S_boundary Right'' means if Aud1 < Aud_boundry then reward will be delivered from the left water spout and if Aud1 > Aud_boundry then water comes from right\n'])); + next_row(y, 1);next_row(y, 1); + + MenuParam(obj, 'Prob_Dist_Left', {'Uniform','Exponential','Half Normal','Normal','Sinusoidal','Anti Exponential','Anti Half Normal','Anti Sinusoidal'}, ... + 'Uniform', x, y,'label','Left Dist', 'labelfraction', 0.35, 'TooltipString', sprintf(['\n Different Probability Distributions for Category A.\n', ... + '\n''Normal - the mean is at mid point of range. Half Normal - truncated normal with mean at boundary.\n',... + '\n''Anti Half Normal - the mean/max is at the side edge of the range.\n',... + '\n''Sinosidal - using sine function instead of half normal and Anti Sinusoidal is when max is at the edge, same as anti half normal.\n'])); + set_callback(Prob_Dist_Left, {mfilename, 'Cal_Mean'}); + next_row(y); + DispParam(obj, 'mean_Left', 0.01, x,y,'label','μ Left','TooltipString','mean/max log stim value for the left side distribution'); + next_row(y); + DispParam(obj, 'sigma_Left', 0.01, x,y,'label','σ Left','TooltipString','sigma value(log) for normal distribution for the left side distribution'); + next_row(y); + NumeditParam(obj, 'sigma_range_Left', 1, x,y,'label','3σ Left','TooltipString',sprintf(['\n A way to reduce the range and increase more distribution towards mean\n', ... + '\n''signifying 3 Sigma (99.7%%) value for the left side distribution, \n',... + '\n''A value b/w range [0.2 - 1] is acceptable.'])); + set_callback(sigma_range_Left, {mfilename, 'Cal_Sigma'}); + next_row(y); next_row(y); + + MenuParam(obj, 'Prob_Dist_Right', {'Uniform','Exponential','Half Normal','Normal','Sinusoidal','Anti Exponential','Anti Half Normal','Anti Sinusoidal'}, ... + 'Uniform', x, y, 'label','Right Dist', 'labelfraction', 0.35, 'TooltipString', sprintf(['\n Different Probability Distributions for Category A.\n', ... + '\n''Normal - the mean is at mid point of range (side edge - boundary). Half Normal - truncated normal with mean at boundary.\n',... + '\n''Anti Half Normal - the mean/max is at the side edge of the range.\n',... + '\n''Sinosidal - using sine function instead of half normal and Anti Sinusoidal is when max is at the edge, same as anti half normal'])); + set_callback(Prob_Dist_Right, {mfilename, 'Cal_Mean'}); + next_row(y); + DispParam(obj, 'mean_Right', 0.01, x,y,'label','μ Right','TooltipString','mean/max log stim value for the right side distribution'); + next_row(y); + DispParam(obj, 'sigma_Right', 0.01, x,y,'label','σ Right','TooltipString','sigma value (log) for normal distribution for the right side distribution'); + next_row(y); + NumeditParam(obj, 'sigma_range_Right', 1, x,y,'label','3σ Right','TooltipString',sprintf(['\n A way to reduce the range and increase more distribution towards mean\n', ... + '\n''signifying 3 Sigma (99.7 %%) value for the right side distribution, \n',... + '\n''A value b/w range [0.2 - 1] is acceptable.'])); + set_callback(sigma_range_Right, {mfilename, 'Cal_Sigma'}); + next_row(y); + + next_column(x); + y=5; + next_row(y, 1) + MenuParam(obj, 'filter_type', {'GAUS','LPFIR', 'FIRLS','BUTTER','MOVAVRG','KAISER','EQUIRIP','HAMMING'}, ... + 'GAUS', x, y, 'labelfraction', 0.35, 'TooltipString', sprintf(['\nDifferent filters. ''LPFIR'': lowpass FIR ''FIRLS'': Least square linear-phase FIR filter design\n', ... + '\n''BUTTER'': IIR Butterworth lowpass filter ''GAUS'': Gaussian filter (window)\n', ... + '\n''MOVAVRG'': Moving average FIR filter ''KAISER'': Kaiser-window FIR filtering\n', ... + '\n''EQUIRIP'':Eqiripple FIR filter ''HAMMING'': Hamming-window based FIR'])); + next_row(y); + NumeditParam(obj,'fcut',110,x,y,'label','fcut','TooltipString','Cut off frequency on the original white noise'); + next_row(y); + NumeditParam(obj,'lfreq',2000,x,y,'label','Modulator_LowFreq','TooltipString','Lower bound for the frequency modulator'); + next_row(y); + NumeditParam(obj,'hfreq',20000,x,y,'label','Modulator_HighFreq','TooltipString','Upper bound for the frequency modulator'); + next_row(y); + NumeditParam(obj,'minS1',0.007,x,y,'label','minS1','TooltipString','min sigma value for AUD1'); + set_callback(minS1, {mfilename, 'Cal_Boundary'}); + next_row(y); + NumeditParam(obj,'maxS1',0.05,x,y,'label','maxS1','TooltipString','max sigma value for AUD1'); + set_callback(maxS1, {mfilename, 'Cal_Boundary'}); + next_row(y); + DispParam(obj, 'A1_sigma', 0.01, x,y,'label','A1_sigma','TooltipString','Sigma value for the first stimulus'); + next_row(y); + NumeditParam(obj,'minF1',4,x,y,'label','minF1','TooltipString','min frequency value for AUD1'); + set_callback(minF1, {mfilename, 'Cal_Boundary'}); + next_row(y); + NumeditParam(obj,'maxF1',10,x,y,'label','maxF1','TooltipString','max frequency value for AUD1'); + set_callback(maxF1, {mfilename, 'Cal_Boundary'}); + next_row(y); + NumeditParam(obj,'volumeF1',0.007,x,y,'label','VolumeF1','TooltipString','volume of tone for AUD1'); + next_row(y); + DispParam(obj, 'A1_freq', 0.01, x,y,'label','A1_freq','TooltipString','Sigma value for the first stimulus'); + next_row(y); + DispParam(obj,'boundary',-3.9,x,y,'label','boundary(log)','TooltipString','decision boundary for categorisation (log)'); + next_row(y); + MenuParam(obj, 'mu_location', {'center', 'side'}, ... + 'center', x, y, 'labelfraction', 0.35, 'TooltipString', sprintf('\nLocation of boundary')); + set_callback(mu_location, {mfilename, 'Cal_Boundary'}); + next_row(y); + ToggleParam(obj, 'frequency_categorization', 0, x,y,... + 'OnString', 'Frequency(Tone)',... + 'OffString', 'Amplitude(Noise)',... + 'TooltipString', sprintf('If on (black) then it enables the presentation of pure tones')); + set_callback(frequency_categorization, {mfilename, 'FrequencyCategorization'}); + make_invisible(maxF1);make_invisible(minF1);make_invisible(A1_freq);make_invisible(volumeF1); + next_row(y); + + % next_column(y) + SoloParamHandle(obj, 'stim_dist_fig', 'value', figure('closerequestfcn', [mfilename '(' class(obj) ', ''hide'');'], 'MenuBar', 'none', ... + 'Name', 'StimulusPlot'), 'saveable', 0); + set(double(gcf), 'Visible', 'off'); + ax = axes(value(stim_dist_fig),'Position',[0.1 0.1 0.8 0.8]); + ylabel('log_e A','FontSize',16,'FontName','Cambria Math'); + set(ax,'Fontsize',15) + xlabel('Sound Categorization','FontSize',16,'FontName','Cambria Math') + SoloParamHandle(obj, 'ax', 'saveable', 0, 'value', ax); + + StimulusSection(obj,'plot_stimuli'); + + x=oldx; y=oldy; + figure(parentfig); + + case 'prepare_next_trial' + if value(training_stage) > 4 && stimuli_on + StimulusSection(obj,'pick_current_stimulus'); + srate=SoundManagerSection(obj,'get_sample_rate'); + Fs=srate; + T=value(A1_time); + + if frequency_categorization + % produce the tone + A1_freq.value = value(thisstim); + A1 = value(thisstimlog(n_done_trials+1)); + dur1 = A1_time*1000; + bal=0; + freq1=A1_freq*1000; + vol=value(volumeF1); + RVol=vol*min(1,(1+bal)); + LVol=vol*min(1,(1-bal)); + t=0:(1/srate):(dur1/1000); + t = t(1:end-1); + tw=sin(t*2*pi*freq1); + RW=RVol*tw; + %w=[LW;RW]; + AUD1 = RW; + else + % produce noise pattern + A1_sigma.value = value(thisstim); + A1 = value(thisstimlog(n_done_trials+1)); + [rawA1, rawA2, normA1, normA2]=noisestim(1,1,T,value(fcut),Fs,value(filter_type)); + modulator=singlenoise(1,T,[value(lfreq) value(hfreq)],Fs,'BUTTER'); + AUD1=normA1(1:A1_time*srate).*modulator(1:A1_time*srate).*A1_sigma; + end + + if ~isempty(AUD1) + SoundManagerSection(obj, 'set_sound', 'StimAUD1', [AUD1'; AUD1']) + end + + SoundManagerSection(obj, 'send_not_yet_uploaded_sounds'); + + % Plot current stimulus and move to saving stimulus history + + % if value(thisstimlog(n_done_trials+1)) > value(boundary)%value(numClass) + % set(value(h1), 'YData', value(A1), 'color',[0.4 0.8 0.1],'markerfacecolor',[0.4 0.8 0.1]); + % else + % set(value(h1), 'YData', value(A1), 'color',[0.8 0.4 0.1],'markerfacecolor',[0.8 0.4 0.1]); + % end + + if n_done_trials > 0 + if ~violation_history(n_done_trials) && ~timeout_history(n_done_trials) + StimulusSection(obj,'update_stimulus_history'); + else + StimulusSection(obj,'update_stimulus_history_nan'); + end + end + end + %% Case pick_current_stimulus + case 'pick_current_stimulus' + if frequency_categorization + stim_min_log = log(value(minF1)); + stim_max_log = log(value(maxF1)); + else + stim_min_log = log(value(minS1)); + stim_max_log = log(value(maxS1)); + end + + if value(training_stage) == 4 % playing fixed sound during this stage + if (strcmpi(ThisTrial, 'LEFT') & strcmp(Rule,'S1>S_boundary Left')) | ... + (strcmpi(ThisTrial, 'RIGHT') & strcmp(Rule,'S1>S_boundary Right')) + + stim_i_log = stim_min_log; + else + stim_i_log = stim_max_log; + end + + else % will be playing stimuli from the distribution + + if strcmpi(ThisTrial, 'LEFT') + dist_type = value(Prob_Dist_Left); + dist_mean = value(mean_Left); + dist_sigma = value(sigma_Left); + dist_range_multiplier = value(sigma_range_Left); + if strcmp(Rule,'S1>S_boundary Left') + edge_max = stim_max_log; + edge_min = value(boundary); + edge_max = edge_min + dist_range_multiplier * (edge_max - edge_min); + else % the rule is S1>S_boundary Right + edge_min = stim_min_log; + edge_max = value(boundary); + edge_min = edge_max - dist_range_multiplier * (edge_max - edge_min); + end + + else % trial is Right + dist_type = value(Prob_Dist_Right); + dist_mean = value(mean_Right); + dist_sigma = value(sigma_Right); + dist_range_multiplier = value(sigma_range_Right); + if strcmp(Rule,'S1>S_boundary Right') + edge_max = stim_max_log; + edge_min = value(boundary); + edge_max = edge_min + dist_range_multiplier * (edge_max - edge_min); + else % the rule is S1>S_boundary Left + edge_min = stim_min_log; + edge_max = value(boundary); + edge_min = edge_max - dist_range_multiplier * (edge_max - edge_min); + end + end + + % Create a Stimuli with the selected Distribution and Side + switch dist_type + + case 'Uniform' % uniform distribution + stim_i_log = random('Uniform',edge_min,edge_max); + + case 'Exponential' + + lambda = 2.153 * (edge_max - edge_min); % In mice they are using 2.153 for normalized stim range [0 1] + stim_i_log = edge_min - 1; % preinitialize for while loop + while stim_i_log < edge_min || stim_i_log > edge_max + U = rand(1); + if edge_min == value(boundary) % exponentially decreasing + stim_i_log = edge_min + (-(1/lambda)*log(U)); % the distribution would be between range [0 1], so added the edge_min + else + stim_i_log = edge_max - (-(1/lambda)*log(U)); + end + end + + case 'Anti Exponential' + + lambda = 2.153 * (edge_max - edge_min); % In mice they are using 2.153 for normalized stim range [0 1] + stim_i_log = edge_min - 1; % preinitialize for while loop + while stim_i_log < edge_min || stim_i_log > edge_max + U = rand(1); + if edge_min == value(boundary) % exponentially decreasing + stim_i_log = edge_max - (-(1/lambda)*log(U)); % the distribution would be between range [0 1], so added the edge_min + else + stim_i_log = edge_min - ((1/lambda)*log(U)); + end + end + + case 'Half Normal' + if edge_min == value(boundary) + stim_i_log = random('Half Normal',dist_mean,dist_sigma); + while stim_i_log < edge_min || stim_i_log > edge_max + stim_i_log = random('Half Normal',dist_mean,dist_sigma); + end + else + stim_i_log = CreateSamples_from_Distribution('normal',dist_mean,dist_sigma,edge_min,edge_max,1); + end + + case 'Anti Half Normal' + + stim_i_log = CreateSamples_from_Distribution('normal',dist_mean,dist_sigma,edge_min,edge_max,1); + + case 'Normal' + stim_i_log = random('Normal',dist_mean,dist_sigma); + while stim_i_log < edge_min || stim_i_log > edge_max + stim_i_log = random('Normal',dist_mean,dist_sigma); + end + + case 'Sinusoidal' | 'Anti Sinusoidal' + + stim_i_log = CreateSamples_from_Distribution('Sinusoidal',dist_mean,dist_sigma,edge_min,edge_max,1); + + end + end + + thisstim.value=exp(stim_i_log); + thisstimlog(n_done_trials+1) = stim_i_log; + + %% Case plot stimuli distribution + case 'plot_stimuli' + + if frequency_categorization + stim_min_log = log(value(minF1)); + stim_max_log = log(value(maxF1)); + else + stim_min_log = log(value(minS1)); + stim_max_log = log(value(maxS1)); + end + + dist_range_multiplier_left = value(sigma_range_Left); + dist_range_multiplier_right = value(sigma_range_Right); + + if strcmp(Rule,'S1>S_boundary Left') + edge_max_left = stim_max_log; + edge_min_left = value(boundary); + edge_max_left = edge_min_left + dist_range_multiplier_left * (edge_max_left - edge_min_left); + edge_max_right = value(boundary); + edge_min_right = stim_min_log; + edge_min_right = edge_max_right - dist_range_multiplier_right * (edge_max_right - edge_min_right); + + else % the rule is S1>S_boundary Right + + edge_min_left = stim_min_log; + edge_max_left = value(boundary); + edge_min_left = edge_max_left - dist_range_multiplier_left * (edge_max_left - edge_min_left); + edge_max_right = stim_max_log; + edge_min_right = value(boundary); + edge_max_right = edge_min_right + dist_range_multiplier_right * (edge_max_right - edge_min_right); + end + + + cla(value(ax)) + + StimuliDistribution_plot(value(ax),[stim_min_log, value(boundary), stim_max_log], Rule, ... + value(Prob_Dist_Left),value(mean_Left),value(sigma_Left),[edge_min_left edge_max_left], ... + value(Prob_Dist_Right),value(mean_Right),value(sigma_Right),[edge_min_right edge_max_right]); + + hold (value(ax),'on') + xline([stim_min_log value(boundary) stim_max_log],'-',{'Stim Min','Boundary','Stim Max'}); + + ylabel('log_e A','FontSize',16,'FontName','Cambria Math'); + set(value(ax),'Fontsize',15) + xlabel('Sound Categorization','FontSize',16,'FontName','Cambria Math') + + % plot(xd,stim_min_log,'s','MarkerSize',15,'MarkerEdgeColor',[0 0 0],'LineWidth',2) + % hold on + % plot(xd,stim_max_log,'s','MarkerSize',15,'MarkerEdgeColor',[0 0 0],'LineWidth',2) + % line([0,2], [value(boundary),value(boundary)]); + % axis square + % set(value(ax),'ytick',([stim_min_log, stim_max_log]),'xtick',xd); + % set(value(ax),'yticklabel',([stim_min, stim_max]),'xticklabel','S1'); + % ylabel('\sigma_1 in log scale','FontSize',16,'FontName','Cambria Math'); + % set(value(ax),'Fontsize',15) + % xlabel('S1','FontSize',16,'FontName','Cambria Math') + + + %% Boundary Calculate + case 'Cal_Boundary' + if frequency_categorization + val_boundary = (log(value(minF1)) + log(value(maxF1)))/2; + min_val = log(value(minF1)); + else + val_boundary = (log(value(minS1)) + log(value(maxS1)))/2; + min_val = log(value(minS1)); + end + if strcmp(mu_location,'center') + boundary.value = val_boundary; + elseif strcmp(mu_location,'side') + boundary.value = (min_val + val_boundary)/2; + end + + StimulusSection(obj,'Cal_Mean'); % update the mean and sigma values for each side + + %% Updated Mean/Max for Each Side based upon Distribution Selected + case 'Cal_Mean' + + if frequency_categorization + edge_max = log(value(maxF1)); + edge_min = log(value(minF1)); + else + edge_max = log(value(maxS1)); + edge_min = log(value(minS1)); + end + + % Calculation for Left Side + + % Sigma + + dist_sigma_multiplier = value(sigma_range_Left); + if dist_sigma_multiplier < 0.2 + dist_sigma_multiplier = 0.2; + end + if dist_sigma_multiplier > 1 + dist_sigma_multiplier = 1; + end + + if strcmp(Rule,'S1>S_boundary Left') + edge_min_left = value(boundary); + edge_max_left = edge_min_left + dist_sigma_multiplier * (edge_max - edge_min_left); + else % the rule is S1>S_boundary Right + edge_max_left = value(boundary); + edge_min_left = edge_max_left - dist_sigma_multiplier * (edge_max_left - edge_min); + end + + sigma_Left.value = (edge_max_left - edge_min_left) / 3; % as we asked user to provide 3 sigma + + % Mean + if matches(value(Prob_Dist_Left),{'Uniform','Half Normal','Sinusoidal','Exponential'}) + mean_Left.value = value(boundary); + else + if strcmp(Rule,'S1>S_boundary Left') + if matches(Prob_Dist_Left,{'Anti Half Normal','Anti Sinusoidal','Anti Exponential'}) + mean_Left.value = edge_max_left; + elseif matches(Prob_Dist_Left,'Normal') + mean_Left.value = (edge_max_left + value(boundary))/2; + end + else + if matches(value(Prob_Dist_Left),{'Anti Half Normal','Anti Sinusoidal','Anti Exponential'}) + mean_Left.value = edge_min_left; + elseif matches(value(Prob_Dist_Left),'Normal') + mean_Left.value = (edge_min_left + value(boundary))/2; + end + end + end + + % Calculation for Right Side + + % Sigma + dist_sigma_multiplier = value(sigma_range_Right); + if dist_sigma_multiplier < 0.2 + dist_sigma_multiplier = 0.2; + end + if dist_sigma_multiplier > 1 + dist_sigma_multiplier = 1; + end + + if strcmp(Rule,'S1>S_boundary Right') + edge_min_right = value(boundary); + edge_max_right = edge_min_right + dist_sigma_multiplier * (edge_max - edge_min_right); + else % the rule is S1>S_boundary Right + edge_max_right = value(boundary); + edge_min_right = edge_max_right - dist_sigma_multiplier * (edge_max_right - edge_min); + end + + sigma_Right.value = (edge_max_right - edge_min_right) / 3; % as we asked user to provide 3 sigma + + + % Mean + if matches(value(Prob_Dist_Right),{'Uniform','Half Normal','Sinusoidal','Exponential'}) + mean_Right.value = value(boundary); + else + if strcmp(Rule,'S1>S_boundary Right') + if matches(value(Prob_Dist_Right),{'Anti Half Normal','Anti Sinusoidal','Anti Exponential'}) + mean_Right.value = edge_max_right; + elseif matches(value(Prob_Dist_Right),'Normal') + mean_Right.value = (edge_max_right + value(boundary))/2; + end + else + if matches(value(Prob_Dist_Right),{'Anti Half Normal','Anti Sinusoidal','Anti Exponential'}) + mean_Right.value = edge_min_right; + elseif matches(Prob_Dist_Right,'Normal') + mean_Right.value = (edge_min_right + value(boundary))/2; + end + end + end + + %% Calculate Sigma + case 'Cal_Sigma' + + if frequency_categorization + edge_max = log(value(maxF1)); + edge_min = log(value(minF1)); + else + edge_max = log(value(maxS1)); + edge_min = log(value(minS1)); + end + + % Calculation for Left Side Sigma + dist_sigma_multiplier = value(sigma_range_Left); + if dist_sigma_multiplier < 0.2 + dist_sigma_multiplier = 0.2; + end + if dist_sigma_multiplier > 1 + dist_sigma_multiplier = 1; + end + sigma_Left.value = (dist_sigma_multiplier * (edge_max - edge_min)) / 3; % as we asked user to provide 3 sigma + + % Calculation for Right Side Sigma + dist_sigma_multiplier = value(sigma_range_Right); + if dist_sigma_multiplier < 0.2 + dist_sigma_multiplier = 0.2; + end + if dist_sigma_multiplier > 1 + dist_sigma_multiplier = 1; + end + sigma_Right.value = (dist_sigma_multiplier * (edge_max - edge_min)) / 3; % as we asked user to provide 3 sigma + + + %% Case frequency ON + case 'FrequencyCategorization' + if frequency_categorization == 1 + make_visible(maxF1);make_visible(minF1);make_visible(A1_freq);make_visible(volumeF1); + make_invisible(maxS1);make_invisible(minS1);make_invisible(A1_sigma); + make_invisible(fcut);make_invisible(lfreq);make_invisible(hfreq); make_invisible(filter_type); + else + make_visible(maxS1);make_visible(minS1);make_visible(A1_sigma); + make_visible(fcut);make_visible(lfreq);make_visible(hfreq); make_visible(filter_type); + make_invisible(maxF1);make_invisible(minF1);make_invisible(A1_freq); make_visible(volumeF1); + end + + StimulusSection(obj,'Cal_Boundary'); % update the boundary + StimulusSection(obj,'plot_stimuli'); + + %% Case get_stimuli + % case 'get_stimuli' + % if nargout>0 + % x=value(S1); + % end + + + %% Case close + case 'close' + set(value(myfig), 'Visible', 'off'); + set(value(stim_dist_fig), 'Visible', 'off'); + % Delete all SoloParamHandles who belong to this object and whose + % fullname starts with the name of this mfile: + if exist('myfig', 'var') && isa(myfig, 'SoloParamHandle') && ishandle(value(myfig)) %#ok + delete(value(myfig)); + end + if exist('stim_dist_fig', 'var') && isa(stim_dist_fig, 'SoloParamHandle') && ishandle(value(stim_dist_fig)) %#ok + delete(value(stim_dist_fig)); + end + delete_sphandle('owner', ['^@' class(obj) '$'], ... + 'fullname', ['^' mfilename]); + + case 'update_stimulus_history' + ps=value(stimulus_history); + ps(n_done_trials)=value(thisstimlog(n_done_trials)); + stimulus_history.value=ps; + + case 'update_stimulus_history_nan' + ps=value(stimulus_history); + ps(n_done_trials)=value(thisstimlog(n_done_trials));%nan; + stimulus_history.value=ps; + + %% Case hide + case 'hide' + StimulusShow.value = 0; + set(value(myfig), 'Visible', 'off'); + set(value(stim_dist_fig), 'Visible', 'off'); + + %% Case show + case 'show' + StimulusShow.value = 1; + set(value(myfig), 'Visible', 'on'); + set(value(stim_dist_fig), 'Visible', 'on'); + + %% Case Show_hide + case 'show_hide' + if StimulusShow == 1 + set(value(myfig), 'Visible', 'on'); + set(value(stim_dist_fig), 'Visible', 'on');%#ok (defined by GetSoloFunctionArgs) + else + set(value(myfig), 'Visible', 'off'); + set(value(stim_dist_fig), 'Visible', 'off'); + end + +end + +end From 104e24a105283e9862023e25f93fb2d0a3f367cf Mon Sep 17 00:00:00 2001 From: Arpit Date: Thu, 5 Jun 2025 15:24:20 +0100 Subject: [PATCH 126/164] error debug --- .../private/CreateSamples_from_Distribution.m | 142 +++++++++++++ .../private/LoadSettingGUI.m | 196 ++++++++++++++++++ .../private/StimuliDistribution_plot.m | 170 +++++++++++++++ ...eate_ArpitCentrePokeTraining_SettingFile.m | 91 ++++++++ .../@ArpitSoundCatContinuous/private/filt.m | 60 ++++++ .../private/noisestim.m | 47 +++++ .../set_training_stage_last_setting_file.m | 51 +++++ ...tCentrePokeTraining_arpit_AR05_250516a.mat | Bin 0 -> 16329 bytes .../private/singlenoise.m | 32 +++ 9 files changed, 789 insertions(+) create mode 100644 Protocols/@ArpitSoundCatContinuous/private/CreateSamples_from_Distribution.m create mode 100644 Protocols/@ArpitSoundCatContinuous/private/LoadSettingGUI.m create mode 100644 Protocols/@ArpitSoundCatContinuous/private/StimuliDistribution_plot.m create mode 100644 Protocols/@ArpitSoundCatContinuous/private/create_ArpitCentrePokeTraining_SettingFile.m create mode 100644 Protocols/@ArpitSoundCatContinuous/private/filt.m create mode 100644 Protocols/@ArpitSoundCatContinuous/private/noisestim.m create mode 100644 Protocols/@ArpitSoundCatContinuous/private/set_training_stage_last_setting_file.m create mode 100644 Protocols/@ArpitSoundCatContinuous/private/settings_@ArpitCentrePokeTraining_arpit_AR05_250516a.mat create mode 100644 Protocols/@ArpitSoundCatContinuous/private/singlenoise.m diff --git a/Protocols/@ArpitSoundCatContinuous/private/CreateSamples_from_Distribution.m b/Protocols/@ArpitSoundCatContinuous/private/CreateSamples_from_Distribution.m new file mode 100644 index 00000000..23cdd90b --- /dev/null +++ b/Protocols/@ArpitSoundCatContinuous/private/CreateSamples_from_Distribution.m @@ -0,0 +1,142 @@ +function samples = CreateSamples_from_Distribution(distributiontype,mu_val,sigma_val,range_min,range_max,n_samples) +%% + + +% samples = CreateSamples_from_Distribution('Anti_Half_Normal',log(4),log(4),log(10),1000,) + +% pairs = { ... +% 'Distribution_Type' 'Sinusoidal'; ... +% 'Range' [log(0.007) log(0.05)] ; ... +% 'Mean_val' mean([log(0.007),log(0.05)]) ; ... +% 'N_Samples' 1; ... +% 'sigma_val' (log(0.05) - log(0.007))/3 +% }; parseargs(varargin, pairs); + +%% + + +% Ensure the range is valid +if range_max <= range_min + error('Upper bound range_max must be greater than lower bound range_min.'); +end + +if contains(distributiontype,'normal','IgnoreCase',true) + if mu_val == range_min + distributiontype = 'Anti_Half_Normal'; + else + distributiontype = 'Half_Normal'; + end + +elseif contains(distributiontype,'sinusoidal','IgnoreCase',true) + if mu_val == range_min + distributiontype = 'Anti_Sinusoidal'; + else + distributiontype = 'Sinusoidal'; + end +end + +switch distributiontype + + case 'Sinusoidal' + + pdf_func = @(x) sin((pi / 2) * ((x - range_min) / (range_max - range_min))); % Define the PDF (Peak at max value) + A_val = 1 / integral(pdf_func, range_min, range_max); % Compute Normalization Constant A + pdf_func = @(x) A_val * sin((pi / 2) * ((x - range_min) / (range_max - range_min))); % the normalized PDF + cdf_func = @(x) integral(@(t) pdf_func(t), range_min, x); % Define the CDF as the cumulative integral of the normalized PDF + inv_cdf_func = @(u) range_min + (range_max - range_min) * (2/pi) * acos(1 - u); % the Inverse CDF + sample_single_point = @() inv_cdf_func(rand()); % Function for Sampling a Single Point + samples = arrayfun(@(x) sample_single_point(), 1:n_samples);% Sample Random Point + + + case 'Anti_Sinusoidal' + + pdf_func = @(x) sin((pi / 2) * ((range_max - x) / (range_max - range_min))); % Define the PDF (Peak at min value) + A_val = 1 / integral(pdf_func, range_min, range_max); % Compute Normalization Constant A + pdf_func = @(x) A_val * sin((pi / 2) * ((range_max - x) / (range_max - range_min))); % Define the normalized PDF + cdf_func = @(x) integral(@(t) pdf_func(t), range_min, x); % Define the CDF as the cumulative integral of the normalized PDF + inv_cdf_func = @(u) range_max - (range_max - range_min) * (2/pi) * acos(1 - u); % the Inverse CDF + sample_single_point = @() inv_cdf_func(rand()); % Function for Sampling a Single Point + samples = arrayfun(@(x) sample_single_point(), 1:n_samples); % Sample Random Points + + + case 'Anti_Half_Normal' + + pdf_func = @(x, A) A * exp(- (x - mu_val).^2 / (2 * sigma_val^2)); % Define the Half-Normal PDF (Peak at min value) + A_den = integral(@(x) exp(- (x - mu_val).^2 / (2 * sigma_val^2)), range_min, range_max); % Compute Normalization Constant A + A_sol = 1 / A_den; % Ensuring total probability integrates to 1 + + pdf_func = @(x) A_sol * exp(- (x - mu_val).^2 / (2 * sigma_val^2)); % Define the normalized PDF function + + cdf_func = @(x) (erf((x - mu_val) / (sqrt(2) * sigma_val)) - erf((range_min - mu_val) / (sqrt(2) * sigma_val))) ... % CDF formula for truncated normal distribution + / (erf((range_max - mu_val) / (sqrt(2) * sigma_val)) - erf((range_min - mu_val) / (sqrt(2) * sigma_val))); + + inv_cdf_func = @(u) mu_val + sqrt(2) * sigma_val * erfinv( ... % Inverse CDF function (solving for x in terms of U) + u * (erf((range_max - mu_val) / (sqrt(2) * sigma_val)) - erf((range_min - mu_val) / (sqrt(2) * sigma_val))) ... + + erf((range_min - mu_val) / (sqrt(2) * sigma_val))); + + sample_single_point = @() inv_cdf_func(rand()); % Function for Sampling a Single Point + samples = arrayfun(@(x) sample_single_point(), 1:n_samples); % Sample Random Points + + + case 'Half_Normal' + + pdf_func = @(x, A) A * exp(- (range_max - x).^2 / (2 * sigma_val^2)); % Define the Half-Normal PDF (Peak at max value) + A_den = integral(@(x) exp(- (range_max - x).^2 / (2 * sigma_val^2)), range_min, range_max); % Compute Normalization Constant A + A_sol = 1 / A_den; % Ensuring total probability integrates to 1 + + pdf_func = @(x) A_sol * exp(- (range_max - x).^2 / (2 * sigma_val^2)); % Define the final normalized PDF function + + cdf_func = @(x) (erf((x - range_max) / (sqrt(2) * sigma_val)) - erf((range_min - range_max) / (sqrt(2) * sigma_val))) ... % CDF formula for truncated normal distribution + / (erf((range_max - range_max) / (sqrt(2) * sigma_val)) - erf((range_min - range_max) / (sqrt(2) * sigma_val))); + + inv_cdf_func = @(u) range_max - sqrt(2) * sigma_val * erfinv( ... % Inverse CDF function (solving for x in terms of U) + u * (erf((range_max - range_min) / (sqrt(2) * sigma_val)))); + + sample_single_point = @() inv_cdf_func(rand()); % Function for Sampling a Single Point + samples = arrayfun(@(x) sample_single_point(), 1:n_samples); % Sample Random Points + +end + +% check all the samples are within the range (excluding the range) +out_of_range_samples = find(samples <= range_min & samples >= range_max); +if length(out_of_range_samples) >= 1 + samples_replacement = arrayfun(@(x) sample_single_point(), 1:length(out_of_range_samples)); % + samples(out_of_range_samples) = samples_replacement; +end +% Recheck and if its again out of range then replace it in while loop ( was +% trying to avoid it) +out_of_range_samples_new = find(samples <= range_min & samples >= range_max); +if length(out_of_range_samples) >= 1 + for i = length(out_of_range_samples_new) + sample_new = arrayfun(@(x) sample_single_point(), 1); + while sample_new <= range_min || sample_new >= range_max + sample_new = arrayfun(@(x) sample_single_point(), 1); + end + samples(out_of_range_samples_new(i)) = sample_new; + end +end + +% %% Debugging - Validate CDF and Inverse CDF + + % % Create a fine grid of x values + % x_vals = linspace(range_min, range_max, 1000); + % % Evaluate PDF and CDF + % pdf_vals = pdf_func(x_vals); + % cdf_vals = cdf_func(x_vals); + % % Generate uniform random samples and apply inverse CDF + % U = rand(10000,1); + % sampled_x = inv_cdf_func(U); + % %% Plot PDF and CDF + % figure; + % % PDF Plot + % subplot(2,1,1); + % plot(x_vals, pdf_vals, 'b', 'LineWidth', 2); + % xlabel('x'); ylabel('PDF'); + % title('Half-Normal PDF'); + % grid on; + % % CDF Plot + % subplot(2,1,2); + % plot(x_vals, cdf_vals, 'r', 'LineWidth', 2); + % xlabel('x'); ylabel('CDF'); + % title('Cumulative Distribution Function (CDF)'); + % grid on; \ No newline at end of file diff --git a/Protocols/@ArpitSoundCatContinuous/private/LoadSettingGUI.m b/Protocols/@ArpitSoundCatContinuous/private/LoadSettingGUI.m new file mode 100644 index 00000000..84cf7e1a --- /dev/null +++ b/Protocols/@ArpitSoundCatContinuous/private/LoadSettingGUI.m @@ -0,0 +1,196 @@ +% REMOVE THE SETTINGS/PRE OPENED GUI + if exist('BpodSystem','var') == 1 + if ~isempty(BpodSystem) + flush + end + end + +% START FRESH & SET THE REQUIRED DIRECTORY + +home_drive = getenv('HOMEDRIVE'); +current_dir = cd; +ratter_dir = extractBefore(current_dir,'ratter'); +ratter_modules_dir1 = fullfile(ratter_dir, 'ratter', 'ExperPort'); +ratter_modules_dir2 = fullfile(home_drive, 'ratter', 'ExperPort'); +if exist("ratter_modules_dir1",'dir') == 7 + cd(ratter_modules_dir1); +else + cd(ratter_modules_dir2); +end + +% START BPOD +try + Bpod('COM5') +catch + + % Identify the bpod port before starting + bpodPort = getOrSetCOMPort(); + disp(['Using COM Port: ' bpodPort]); + % Bpod(); + Bpod(bpodPort); +end + +newstartup; + +% PRESENT THE USER WITH THE CHOICE OF EXPERIMENTER/RAT + +% Let's identify all the experimenter and their respective rats +try + Experimenter_Name = bdata('select distinct experimenter from rats where extant=1 order by experimenter'); +catch %#ok + disp('ERROR: Unable to connect to MySQL Server'); + Experimenter_Name = ''; +end + +rig_id = bSettings('get','RIGS','Rig_ID'); % the ID of this rig + +if ~isempty(Experimenter_Name) + for n_exp = 1:numel(Experimenter_Name) + % Finding all training rat for this experimenter + % ratnames = bdata(['select ratname from rats where experimenter="',Experimenter_Name{n_exp},'" and extant=1']); + ratnames = bdata(['select ratname from rats where experimenter="',Experimenter_Name{n_exp},'" and in_training=1']); + Rat_Name_all{n_exp} = sortrows(strtrim(ratnames)); + + % Additionaally finding rats for this experimenter with its schedule on this rig + [rats,slots] = bdata(['select ratname, timeslot from scheduler where experimenter="',... + Experimenter_Name{n_exp},'" and rig="',num2str(rig_id),'"']); + + + end +end + +Exp_Rat_Map = containers.Map(Experimenter_Name, Rat_Name_all); + + +% Create figure +fig = figure('Name', 'Dynamic Button GUI', ... + 'Position', [500, 300, 600, 400], ... + 'MenuBar', 'none', ... + 'NumberTitle', 'off', ... + 'Color', [0.9 0.9 0.9]); + +% Create main buttons with resize callback +buttonHandles = createButtons(fig, Experimenter_Name, @(src, ~) mainButtonCallback(src, Exp_Rat_Map, fig)); + +% Setup resize behavior +set(fig, 'ResizeFcn', @(src, ~) resizeButtons(src, buttonHandles)); + +% === Callback for first-level buttons === +function mainButtonCallback(src, map, figHandle) +experimenter = src.String; +subOptions = map(experimenter); +clf(figHandle); % Clear previous buttons + +% Create new buttons and assign second-level callback +newButtons = createButtons(figHandle, subOptions, @(src2, ~) subButtonCallback(src2,experimenter,figHandle)); + +% Update resize function +set(figHandle, 'ResizeFcn', @(src, ~) resizeButtons(src, newButtons)); +end + +% === Final action when second-level button is clicked === +function subButtonCallback(src,experimenter_name,figHandle) +rat_name = src.String; +close(figHandle); +runrats('init'); +pause(3); +runrats('update exp_rat_userclick',experimenter_name,rat_name); +end + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +%% HELPER FUNCTIONS + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +% === Create buttons dynamically and return handles === +function btns = createButtons(figHandle, optionList, callbackFcn) +delete(findall(figHandle, 'Type', 'uicontrol')); % Clear any existing buttons + +n = numel(optionList); +btns = gobjects(1, n); + +for i = 1:n + btns(i) = uicontrol(figHandle, ... + 'Style', 'pushbutton', ... + 'Units', 'normalized', ... + 'String', optionList{i}, ... + 'FontSize', 14, ... + 'FontWeight', 'bold', ... + 'BackgroundColor', [0.7 0.8 1], ... + 'Callback', callbackFcn); +end + +resizeButtons(figHandle, btns); % Initial layout +end + +% === Resize button layout responsively === +function resizeButtons(figHandle, buttons) +n = numel(buttons); +spacing = 0.02; +totalSpacing = spacing * (n + 1); +btnHeight = (1 - totalSpacing) / n; +btnWidth = 0.8; +x = (1 - btnWidth) / 2; + +for i = 1:n + y = 1 - spacing - i * (btnHeight + spacing) + spacing; + set(buttons(i), 'Position', [x, y, btnWidth, btnHeight]); +end +end + + +function comPort = getOrSetCOMPort() +configFile = fullfile(fileparts(mfilename('fullpath')), 'com_config.mat'); +maxAttempts = 3; +success = false; + +% Try existing config or prompt +if exist(configFile, 'file') + data = load(configFile, 'comPort'); + comPort = data.comPort; +else + comPort = promptForPort(); +end + +% Try to validate and possibly retry +for attempt = 1:maxAttempts + try + s = serialport(comPort, 9600); % test connection + clear s; % close it immediately + success = true; + break; + catch + fprintf('[Warning] Failed to open %s. Please select a different COM port.\n', comPort); + comPort = promptForPort(); + end +end + +if ~success + error('Failed to find a valid COM port after %d attempts.', maxAttempts); +end + +% Save the working port +save(configFile, 'comPort'); +end + +function comPort = promptForPort() +availablePorts = serialportlist("available"); + +if isempty(availablePorts) + warning('No serial ports detected. Enter manually.'); + comPort = input('Enter COM port manually (e.g., COM3): ', 's'); +else + fprintf('Available COM ports:\n'); + for i = 1:numel(availablePorts) + fprintf(' %d: %s\n', i, availablePorts(i)); + end + idx = input('Select COM port number: '); + if isnumeric(idx) && idx >= 1 && idx <= numel(availablePorts) + comPort = availablePorts(idx); + else + comPort = input('Enter COM port manually (e.g., COM3): ', 's'); + end +end +end diff --git a/Protocols/@ArpitSoundCatContinuous/private/StimuliDistribution_plot.m b/Protocols/@ArpitSoundCatContinuous/private/StimuliDistribution_plot.m new file mode 100644 index 00000000..8c3c100d --- /dev/null +++ b/Protocols/@ArpitSoundCatContinuous/private/StimuliDistribution_plot.m @@ -0,0 +1,170 @@ +function StimuliDistribution_plot(ax,plot_x_range, Rule, distribution_left,mean_left,sigma_left,... + range_left,distribution_right,mean_right,sigma_right,range_right) + +if strcmp(Rule,'S1>S_boundary Right') + x_left = plot_x_range(1):.01:plot_x_range(2); + x_right = plot_x_range(2):.01:plot_x_range(3); +else + x_right = plot_x_range(1):.01:plot_x_range(2); + x_left = plot_x_range(2):.01:plot_x_range(3); +end + +switch distribution_left + + case 'Uniform' + + pdf_funct_left = makedist('Uniform','lower',range_left(1),'upper',range_left(2)); + pd_left = pdf(pdf_funct_left,x_left); + + case 'Exponential' + + lambda = 2.153 * (range_left(2) - range_left(1)); + lambda = 1/lambda; + if range_left(1) == plot_x_range(2) % decreasing function + Z = exp(-lambda * range_left(1)) - exp(-lambda * range_left(2)); % Define the normalization constant + pdf_func_left = @(x) (lambda * exp(-lambda * x)) / Z; % Normalized truncated exponential PDF + else + Z = exp(lambda * range_left(2)) - exp(lambda * range_left(1));% Normalization constant + A = lambda / Z; + pdf_func_left = @(x) A * exp(lambda * x); + end + + pd_left = pdf_func_left(x_left); + + case 'Normal' + + pdf_funct_left = makedist('Normal','mu',mean_left,'sigma',sigma_left); + pd_left = pdf(pdf_funct_left,x_left); + + case 'Half Normal' + + pdf_funct_init = @(x, A) A * exp(- (range_left(2) - x).^2 / (2 * sigma_left^2)); % Define the Half-Normal PDF (Peak at max value) + A_den = integral(@(x) exp(- (range_left(2) - x).^2 / (2 * sigma_left^2)), range_left(1), range_left(2)); % Compute Normalization Constant A + A_sol = 1 / A_den; % Ensuring total probability integrates to 1 + pdf_funct_left = @(x) A_sol * exp(- (range_left(2) - x).^2 / (2 * sigma_left^2)); % Define the final normalized PDF function + pd_left = pdf_funct_left(x_left); + + case 'Anti Exponential' + + lambda = 2.153 * (range_left(2) - range_left(1)); + + if range_left(2) == plot_x_range(2) % decreasing function + Z = exp(-lambda * range_left(1)) - exp(-lambda * range_left(2)); % Define the normalization constant + pdf_func_left = @(x) (lambda * exp(-lambda * x)) / Z; % Normalized truncated exponential PDF + else + Z = exp(lambda * range_left(2)) - exp(lambda * range_left(1));% Normalization constant + A = lambda / Z; + pdf_func_left = @(x) A * exp(lambda * x); + end + + pd_left = pdf_func_left(x_left); + + case 'Anti Half Normal' + + pdf_funct_left = makedist('HalfNormal','mu',mean_left,'sigma',sigma_left); + pd_left = pdf(pdf_funct_left,x_left); + + case 'Anti Sinusoidal' + + pdf_funct_init = @(x) sin((pi / 2) * ((x - range_left(1)) / (range_left(2) - range_left(1)))); + A_val = 1 / integral(pdf_funct_init, range_left(1), range_left(2)); % Compute Normalization Constant A + pdf_funct_left = @(x) A_val * sin((pi / 2) * ((x - range_left(1)) / (range_left(2) - range_left(1)))); % the normalized PDF + pd_left = pdf_funct_left(x_left); + + case 'Sinusoidal' + + pdf_funct_init = @(x) sin((pi / 2) * ((range_left(2) - x) / (range_left(2) - range_left(1)))); % Define the PDF (Peak at min value) + A_val = 1 / integral(pdf_funct_init, range_left(1), range_left(2)); % Compute Normalization Constant A + pdf_funct_left = @(x) A_val * sin((pi / 2) * ((range_left(2) - x) / (range_left(2) - range_left(1)))); % Define the normalized PDF + pd_left = pdf_funct_left(x_left); + + case 'Monotonic Increase' + +end + +switch distribution_right + + case 'Uniform' + + pdf_funct_right = makedist('Uniform','lower',range_right(1),'upper',range_right(2)); + pd_right = pdf(pdf_funct_right,x_right); + + case 'Exponential' + + lambda = 2.153 * (range_right(2) - range_right(1)); + lambda = 1/lambda; + if range_right(1) == plot_x_range(2) % decreasing function + Z = exp(-lambda * range_right(1)) - exp(-lambda * range_right(2)); % Define the normalization constant + pdf_func_right = @(x) (lambda * exp(-lambda * x)) / Z; % Normalized truncated exponential PDF + else + Z = exp(lambda * range_right(2)) - exp(lambda * range_right(1));% Normalization constant + A = lambda / Z; + pdf_func_right = @(x) A * exp(lambda * x); + end + + pd_right = pdf_func_right(x_right); + + case 'Normal' + + pdf_funct_right = makedist('Normal','mu',mean_right,'sigma',sigma_right); + pd_right = pdf(pdf_funct_right,x_right); + + case 'Half Normal' + + pdf_funct_right = makedist('HalfNormal','mu',mean_right,'sigma',sigma_right); + pd_right = pdf(pdf_funct_right,x_right); + + case 'Anti Half Normal' + + pdf_funct_init = @(x, A) A * exp(- (range_right(2) - x).^2 / (2 * sigma_right^2)); % Define the Half-Normal PDF (Peak at max value) + A_den = integral(@(x) exp(- (range_right(2) - x).^2 / (2 * sigma_right^2)), range_right(1), range_right(2)); % Compute Normalization Constant A + A_sol = 1 / A_den; % Ensuring total probability integrates to 1 + pdf_funct_right = @(x) A_sol * exp(- (range_right(2) - x).^2 / (2 * sigma_right^2)); % Define the final normalized PDF function + pd_right = pdf_funct_right(x_right); + + case 'Anti Exponential' + + lambda = 2.153 * (range_right(2) - range_right(1)); + + if range_right(2) == plot_x_range(2) % decreasing function + Z = exp(-lambda * range_right(1)) - exp(-lambda * range_right(2)); % Define the normalization constant + pdf_func_right = @(x) (lambda * exp(-lambda * x)) / Z; % Normalized truncated exponential PDF + else + Z = exp(lambda * range_right(2)) - exp(lambda * range_right(1));% Normalization constant + A = lambda / Z; + pdf_func_right = @(x) A * exp(lambda * x); + end + + pd_right = pdf_func_right(x_right); + + case 'Anti Sinusoidal' + + pdf_funct_init = @(x) sin((pi / 2) * ((range_right(2) - x) / (range_right(2) - range_right(1)))); % Define the PDF (Peak at min value) + A_val = 1 / integral(pdf_funct_init, range_right(1), range_right(2)); % Compute Normalization Constant A + pdf_funct_right = @(x) A_val * sin((pi / 2) * ((range_right(2) - x) / (range_right(2) - range_right(1)))); % Define the normalized PDF + pd_right = pdf_funct_right(x_right); + + + case 'Sinusoidal' + + pdf_funct_init = @(x) sin((pi / 2) * ((x - range_right(1)) / (range_right(2) - range_right(1)))); + A_val = 1 / integral(pdf_funct_init, range_right(1), range_right(2)); % Compute Normalization Constant A + pdf_funct_right = @(x) A_val * sin((pi / 2) * ((x - range_right(1)) / (range_right(2) - range_right(1)))); % the normalized PDF + pd_right = pdf_funct_right(x_right); + +end + +% Plot the distribution +if strcmp(Rule,'S1>S_boundary Right') + plot_x = [x_left,x_right]; + plot_y = [pd_left,pd_right]; +else + plot_x = [x_right,x_left]; + plot_y = [pd_right,pd_left]; +end + +plot(ax,plot_x,plot_y,'b','LineWidth', 2); +ylim(ax,[0 max(plot_y)]); +xlim(ax,[min(plot_x) max(plot_x)]) + +end \ No newline at end of file diff --git a/Protocols/@ArpitSoundCatContinuous/private/create_ArpitCentrePokeTraining_SettingFile.m b/Protocols/@ArpitSoundCatContinuous/private/create_ArpitCentrePokeTraining_SettingFile.m new file mode 100644 index 00000000..05a556d4 --- /dev/null +++ b/Protocols/@ArpitSoundCatContinuous/private/create_ArpitCentrePokeTraining_SettingFile.m @@ -0,0 +1,91 @@ +function create_ArpitCentrePokeTraining_SettingFile() + +templateDir = 'C:/ratter/'; + +templatePath = [templateDir '/Protocols/@ArpitCentrePokeTraining/private/pipeline_ArpitCentrePokeTraining_arpit_AR05_250516.m']; +setting_filename = 'settings_@ArpitCentrePokeTraining_arpit_AR05_250516a.mat'; +outputRootDir = [templateDir '/SoloData/Settings/']; + +experimenter = input('Enter experimenter name: ', 's'); +ratname = input('Enter rat name: ', 's'); + +% Get today's date +newDate = datestr(now, 'yymmdd'); + +% Read the template file +if ~isfile(templatePath) + error('Template file not found: %s', templatePath); +end +code = fileread(templatePath); +lines = splitlines(code); + +% Find and update the function definition line +newFuncName = ''; +for i = 1:length(lines) + line = strtrim(lines{i}); + if startsWith(line, 'function') && contains(line, 'pipeline_') + % Extract argument list + pattern = 'function\s+varargout\s*=\s*pipeline_.*?\((.*?)\)'; + tokens = regexp(line, pattern, 'tokens'); + if ~isempty(tokens) + args = tokens{1}{1}; + newFuncName = sprintf('pipeline_ArpitCentrePokeTraining_%s_%s_%s', ... + experimenter, ratname, newDate); + lines{i} = sprintf('function varargout = %s(%s)', newFuncName, args); + break; + end + end +end + +if isempty(newFuncName) + error('Could not find valid function definition line in template.'); +end + +% Create output directory if needed +outputDir = fullfile(outputRootDir, experimenter, ratname); +if ~exist(outputDir, 'dir') + mkdir(outputDir); +end + +% Build full new script path +newScriptPath = fullfile(outputDir, [newFuncName, '.m']); + +% Save modified content +newCode = strjoin(lines, newline); +fid = fopen(newScriptPath, 'w'); +if fid == -1 + error('Failed to open new file for writing: %s', newScriptPath); +end +fwrite(fid, newCode); +fclose(fid); + +fprintf('New Session Definition file saved to: %s\n', newScriptPath); + +% Load and modify .mat file +[templateFolder, ~, ~] = fileparts(templatePath); +matTemplatePath = fullfile(templateFolder, setting_filename); + +if ~isfile(matTemplatePath) + warning('No setting_file found in template directory: %s', matTemplatePath); + newMatPath = ''; + return; +end + +load(matTemplatePath,'saved','saved_autoset','fig_position'); % Load struct + +saved.SavingSection_experimenter = experimenter; +saved.SavingSection_ratname = ratname; +saved.SessionDefinition_textTrainingStageFile = newScriptPath; +saved.ArpitCentrePokeTraining_prot_title = sprintf('ArpitCentrePokeTraining: %s, %s',experimenter,ratname); +saved.PokesPlotSection_textHeader = sprintf('PokesPlotSection(%s, %s',experimenter,ratname); +saved.SessionDefinition_textHeader = sprintf('SESSION AUTOMATOR WINDOW: %s, %s',experimenter,ratname); + +new_MATfile = sprintf('settings_@ArpitCentrePokeTraining__%s_%s_%sa.mat', ... + experimenter, ratname, newDate); + +newMatPath = fullfile(outputDir, new_MATfile); + +save(newMatPath,'saved','saved_autoset','fig_position'); +fprintf('New setting file saved to: %s\n', newMatPath); + +end diff --git a/Protocols/@ArpitSoundCatContinuous/private/filt.m b/Protocols/@ArpitSoundCatContinuous/private/filt.m new file mode 100644 index 00000000..e4c572ff --- /dev/null +++ b/Protocols/@ArpitSoundCatContinuous/private/filt.m @@ -0,0 +1,60 @@ +function filtsignal=filt(signal,fcut,Fs,filter_type) +a=2; % wp/ws used in butterworth method and LS linear FIR method +N=200; % filter order used in lowpass FIR method +rp=3; % passband ripple in dB used in butterworth method +rs=60; % stopband attenuation in dB used in butterworth method +beta=0.1102*(rs-8.8); %used in Kaiser window to obtain sidelobe attenuation of rs dB +if strcmp(filter_type, 'GAUS') || strcmp(filter_type, 'MOVAVRG') +window = fix(Fs/fcut); % window size used in Gaussian and moving average methods +end +wp=2*fcut/Fs; % normalized passband corner frequency wp, the cutoff frequency +ws=a*wp; % normalized stopband corner frequency + + +switch filter_type + case 'BUTTER' %Butterworth IIR filter + if length(wp)>1 + ws(1)=2*(fcut(1)/2)/Fs; + ws(2)=2*(fcut(2)+fcut(1)/2)/Fs; + [n,wn]=buttord(wp,ws,rp,rs); + [b,a]=butter(n,wn,'bandpass'); + else + [n,wn]=buttord(wp,ws,rp,rs); + [b,a]=butter(n,wn,'low'); + end + filtsignal=filter(b,a,signal);%conventional filtering + case 'LPFIR' %Lowpass FIR filter + d=fdesign.lowpass('N,Fc',N,fcut,Fs); % Fc is the 6-dB down point, N is the filter order(N+1 filter coefficients) + Hd = design(d); + filtsignal=filter(Hd.Numerator,1,signal); %conventional filtering + case 'FIRLS' %Least square linear-phase FIR filter design + b=firls(255,[0 2*fcut/Fs a*2*fcut/Fs 1],[1 1 0 0]); + filtsignal=filter(b,1,signal); %conventional filtering + case 'EQUIRIP' %Eqiripple FIR filter + d=fdesign.lowpass('Fp,Fst,Ap,Ast',wp,ws,rp,rs); + Hd=design(d,'equiripple'); + filtsignal=filter(Hd.Numerator,1,signal); %conventional filtering + case 'MOVAVRG' % Moving average FIR filtering, Rectangular window + h = ones(window,1)/window; + b = fir1(window-1,wp,h); + filtsignal = filter(b, 1, signal); + case 'HAMMING' % Hamming-window based FIR filtering + b = fir1(150,wp); + filtsignal = filter(b, 1, signal); + filtsignal = filter(h, 1, signal); + case 'GAUS' % Gaussian-window FIR filtering + h = normpdf(1:window, 0, fix(window/2)); + b = fir1(window-1,wp,h); + filtsignal = filter(b, 1, signal); + case 'GAUS1' % Gaussian-window FIR filtering + b = fir1(window-1,wp,gausswin(window,2)/window); + filtsignal = filter(b, 1, signal); + case 'KAISER' %Kaiser-window FIR filtering + h=kaiser(window,beta); + b = fir1(window-1,wp,h); + filtsignal = filter(b, 1, signal); + + otherwise + sprintf('filter_type is wrong!! havaset kojast!!') +end + diff --git a/Protocols/@ArpitSoundCatContinuous/private/noisestim.m b/Protocols/@ArpitSoundCatContinuous/private/noisestim.m new file mode 100644 index 00000000..7f074ab0 --- /dev/null +++ b/Protocols/@ArpitSoundCatContinuous/private/noisestim.m @@ -0,0 +1,47 @@ +function [base,target,normbase,normtarget]=noisestim(sigma_1,sigma_2,T,fcut,Fs,filter_type) + +%%%%%%%%%%%%%%%%% Determines of the type of filter used %%%%%%%%%%%%%%%%%%% +%'LPFIR': lowpass FIR%%%%%'FIRLS': Least square linear-phase FIR filter design +%'BUTTER': IIR Butterworth lowpass filter%%%%%%'GAUS': Gaussian filter (window) +%'MOVAVRG': Moving average FIR filter%%%%%%%%'KAISER': Kaiser-window FIR filtering +% 'EQUIRIP':Eqiripple FIR filter%%%%% 'HAMMING': Hamming-window based FIR +% T is duration of each signal in milisecond, fcut is the cut-off frequency +% Fs is the sampling frequency +% outband=40; +replace=1; +L=floor(T*Fs); % Length of signal +t=L*linspace(0,1,L)/Fs; % time in miliseconds +%%%%%%%%%%% produce position values %%%%%%% +pos1 = sigma_1*randn(Fs,1); +% pos1(pos1>outband)=[]; +% pos1(pos1<-outband)=[]; + +pos2 =sigma_2*randn(Fs,1); +% pos2(pos2>outband)=[]; +% pos2(pos2<-outband)=[]; +base = randsample(pos1,L,replace); +target = randsample(pos2,L,replace); +%%%% Filter the original position values %%%%%% +filtbase=filt(base,fcut,Fs,filter_type); +filttarget=filt(target,fcut,Fs,filter_type); +normbase=filtbase./(max(abs(filtbase))); +normtarget=filttarget./(max(abs(filttarget))); +end + +%%%%%% plot the row and filtered position values %%%%%%%%% +% subplot(2,2,1) +% plot(t,base,'r'); +% ylabel('base') +% xlabel('Time (ms)') +% subplot(2,2,2) +% plot(t,target,'g'); +% ylabel('target') +% xlabel('Time (ms)') +% subplot(2,2,3) +% plot(t,filtbase) +% ylabel('filtbase') +% xlabel('Time (ms)') +% subplot(2,2,4) +% plot(t,filttarget) +% ylabel('filttarget') +% xlabel('Time (ms)') diff --git a/Protocols/@ArpitSoundCatContinuous/private/set_training_stage_last_setting_file.m b/Protocols/@ArpitSoundCatContinuous/private/set_training_stage_last_setting_file.m new file mode 100644 index 00000000..92dd3cd0 --- /dev/null +++ b/Protocols/@ArpitSoundCatContinuous/private/set_training_stage_last_setting_file.m @@ -0,0 +1,51 @@ +function set_training_stage_last_setting_file(owner,experimenter,ratname) + +try + +global Solo_datadir; + +rat_dir = sprintf('%s\\Settings\\%s\\%s\\',Solo_datadir,experimenter,ratname); +% Get the latest setting file + +u = dir([rat_dir 'settings_@' owner '_' experimenter '_' ratname '*.mat']); +if ~isempty(u) + [filenames{1:length(u)}] = deal(u.name); + filenames = sort(filenames'); %#ok (can't use dimension argument with cell sort) + today_date_str = regexprep(char(datetime('today','Format','yy-MM-dd')), '[^0-9]', ''); + for i=length(u):-1:1 % search from the end back + file_date_num = textscan(filenames{i},['settings_@' owner '_' experimenter '_' ratname '_%n%*c.mat']); + + if ~isempty(file_date_num{1}) && file_date_num{1} <= str2double(today_date_str) + fullname = [rat_dir filenames{i}]; % We've found it. + break + end + end +end + +try + loaded_data = load(fullname); +catch + return; +end + +% Lets find the Handle for Training Stage +handles = get_sphandle('owner', owner); +stage_handle_name = 'ParamsSection_training_stage'; +for hi= 1:length(handles) + sph_fullname=get_fullname(handles{hi}); + if strcmpi(sph_fullname,stage_handle_name) + handles{hi}.value = loaded_data.saved.(sph_fullname); + callback(handles{i}); + break + end +end + +return + +catch + + return + +end + +end \ No newline at end of file diff --git a/Protocols/@ArpitSoundCatContinuous/private/settings_@ArpitCentrePokeTraining_arpit_AR05_250516a.mat b/Protocols/@ArpitSoundCatContinuous/private/settings_@ArpitCentrePokeTraining_arpit_AR05_250516a.mat new file mode 100644 index 0000000000000000000000000000000000000000..7e70e094972354707ab4d569647e26f9141390cf GIT binary patch literal 16329 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 LamD$f`qBRa*3e*P literal 0 HcmV?d00001 diff --git a/Protocols/@ArpitSoundCatContinuous/private/singlenoise.m b/Protocols/@ArpitSoundCatContinuous/private/singlenoise.m new file mode 100644 index 00000000..0cf201f3 --- /dev/null +++ b/Protocols/@ArpitSoundCatContinuous/private/singlenoise.m @@ -0,0 +1,32 @@ +function [normbase]=singlenoise(sigma_1,T,fcut,Fs,filter_type) +%GetSoloFunctionArgs(obj); + +%%%%%%%%%%%%%%%%% Determines of the type of filter used %%%%%%%%%%%%%%%%%%% +%'LPFIR': lowpass FIR%%%%%'FIRLS': Least square linear-phase FIR filter design +%'BUTTER': IIR Butterworth lowpass filter%%%%%%'GAUS': Gaussian filter (window) +%'MOVAVRG': Moving average FIR filter%%%%%%%%'KAISER': Kaiser-window FIR filtering +% 'EQUIRIP':Eqiripple FIR filter%%%%% 'HAMMING': Hamming-window based FIR +% T is duration of each signal in milisecond, fcut is the cut-off frequency +% Fs is the sampling frequency +% outband=40; +sigma_1=1; +%T=10000; +%fcut=[3000 4000]; +%Fs=200000; +filter_type='BUTTER'; +outband=60; +replace=1; +L=floor(T*Fs); % Length of signal +%%%%%%%%%%% produce position values %%%%%%% +pos1 = sigma_1*randn(Fs,1); +% pos1(pos1>outband)=[]; +% pos1(pos1<-outband)=[]; + +base = randsample(pos1,L,replace); +%%%% Filter the original position values %%%%%% +%filtbase=filt(base,fcut,Fs,filter_type); +hf = design(fdesign.bandpass('N,F3dB1,F3dB2',10,fcut(1),fcut(2),Fs)); +filtbase=filter(hf,base); +normbase=filtbase./(max(abs(filtbase))); +end + From 7a5d19cab14819d2db042b3c9e7d2cc243c58c90 Mon Sep 17 00:00:00 2001 From: Arpit Date: Fri, 6 Jun 2025 00:11:11 +0100 Subject: [PATCH 127/164] working version of ArpitSoundCatContinuous protocol --- .../ArpitCentrePokeTraining.m | 2 +- ...ing_SessionDefinition_AutoTrainingStages.m | 4 +- .../ArpitSoundCatContinuous.m | 93 +++--- .../ArpitSoundCatContinuousSMA.m | 2 +- .../@ArpitSoundCatContinuous/SideSection.m | 277 +++++++++++------- .../private/Calculate_CentrePoke_Params.m | 87 ++++++ 6 files changed, 323 insertions(+), 142 deletions(-) create mode 100644 Protocols/@ArpitSoundCatContinuous/private/Calculate_CentrePoke_Params.m diff --git a/Protocols/@ArpitCentrePokeTraining/ArpitCentrePokeTraining.m b/Protocols/@ArpitCentrePokeTraining/ArpitCentrePokeTraining.m index 7f6b6b94..18535b0b 100644 --- a/Protocols/@ArpitCentrePokeTraining/ArpitCentrePokeTraining.m +++ b/Protocols/@ArpitCentrePokeTraining/ArpitCentrePokeTraining.m @@ -6,7 +6,7 @@ % Default object is of our own class (mfilename); % we inherit only from Plugins -obj = class(struct, mfilename, pokesplot2, saveload, sessionmodel2, soundmanager, soundui, ... +obj = class(struct, mfilename, pokesplot2, saveload, sessionmodel2, soundmanager, soundui,antibias, ... water, distribui,comments, soundtable, sqlsummary, bonsaicamera); %--------------------------------------------------------------- diff --git a/Protocols/@ArpitCentrePokeTraining/ArpitCentrePokeTraining_SessionDefinition_AutoTrainingStages.m b/Protocols/@ArpitCentrePokeTraining/ArpitCentrePokeTraining_SessionDefinition_AutoTrainingStages.m index 3e305443..694b6686 100644 --- a/Protocols/@ArpitCentrePokeTraining/ArpitCentrePokeTraining_SessionDefinition_AutoTrainingStages.m +++ b/Protocols/@ArpitCentrePokeTraining/ArpitCentrePokeTraining_SessionDefinition_AutoTrainingStages.m @@ -1386,10 +1386,10 @@ % -function [prestim,A1,prego] = param_time_within_range(fixed_length,cp_length,range_min_prestim,range_max_prestim, is_random_prestim, provided_time_prestim,... +function [prestim,A1,prego] = param_time_within_range(not_fixed_length,cp_length,range_min_prestim,range_max_prestim, is_random_prestim, provided_time_prestim,... range_min_A1,range_max_A1, is_random_A1, provided_time_A1,range_min_prego,range_max_prego, is_random_prego, provided_time_prego) -if fixed_length == 1 % warm up stage where cp length is increasing +if not_fixed_length == 0 % warm up stage where cp length is increasing % then calculate the range/typical value if cp_length <= 0.3 prestim = 0.1; diff --git a/Protocols/@ArpitSoundCatContinuous/ArpitSoundCatContinuous.m b/Protocols/@ArpitSoundCatContinuous/ArpitSoundCatContinuous.m index af27eda6..a57da2ac 100644 --- a/Protocols/@ArpitSoundCatContinuous/ArpitSoundCatContinuous.m +++ b/Protocols/@ArpitSoundCatContinuous/ArpitSoundCatContinuous.m @@ -1,13 +1,13 @@ % AltSoundCatCatch protocol % EM, October 2020 -function [obj] = Arpit_SoundCatContinuous(varargin) +function [obj] = ArpitSoundCatContinuous(varargin) % Default object is of our own class (mfilename); % we inherit only from Plugins obj = class(struct, mfilename, pokesplot2, saveload, sessionmodel2, soundmanager, soundui, antibias, ... - water, soundtable, comments, sqlsummary); + water, distribui,soundtable, comments, sqlsummary, bonsaicamera); %--------------------------------------------------------------- % BEGIN SECTION COMMON TO ALL PROTOCOLS, DO NOT MODIFY @@ -24,7 +24,8 @@ if length(varargin) < 2 || ~ischar(varargin{2}) error(['If called with a "%s" object as first arg, a second arg, a ' ... 'string specifying the action, is required\n']); - else action = varargin{2}; varargin = varargin(3:end); %#ok + else + action = varargin{2}; varargin = varargin(3:end); %#ok end else % Ok, regular call with first param being the action string. action = varargin{1}; varargin = varargin(2:end); %#ok @@ -103,10 +104,18 @@ SoloFunctionAddVars('SideSection', 'ro_args', ... {'maxasymp';'slp';'inflp';'minasymp';'assym'}); - [x, y] = WaterValvesSection(obj, 'init', x, y); - [x, y] = PokesPlotSection(obj, 'init', x, y); - [x, y] = CommentsSection(obj, 'init', x, y); - SessionDefinition(obj, 'init', x, y, value(myfig)); next_row(y, 2); %#ok + figpos = get(double(gcf), 'Position'); + [expmtr, rname]=SavingSection(obj, 'get_info'); + HeaderParam(obj, 'prot_title', [mfilename ': ' expmtr ', ' rname], x, y, 'position', [10 figpos(4)-25, 800 20]); + + [x, y] = WaterValvesSection(obj, 'init', x, y);next_row(y); + [x, y] = PokesPlotSection(obj, 'init', x, y);next_row(y); + [x, y] = CommentsSection(obj, 'init', x, y);next_row(y); + [x, y] = BonsaiCameraInterface(obj,'init',x,y,name,expmtr,rname); + + oldx=x; oldy=y; + + next_column(x); y=5; @@ -116,13 +125,27 @@ [x, y] = PerformanceSection(obj, 'init', x, y); [x, y] = StimulatorSection(obj, 'init', x, y); next_row(y, 1.3); - figpos = get(double(gcf), 'Position'); - [expmtr, rname]=SavingSection(obj, 'get_info'); - HeaderParam(obj, 'prot_title', [mfilename ': ' expmtr ', ' rname], x, y, 'position', [10 figpos(4)-25, 800 20]); - SoundCatSMA(obj, 'init'); - feval(mfilename, obj, 'prepare_next_trial'); - + x=oldx; y=oldy; + + SessionDefinition(obj, 'init', x, y, value(myfig)); next_row(y, 2); %#ok + % SoundCatSMA(obj, 'init'); + % feval(mfilename, obj, 'prepare_next_trial'); + + case 'change_water_modulation_params' + display_guys = [1 150 300]; + for i=1:numel(display_guys) + t = display_guys(i); + + myvar = eval(sprintf('trial_%d', t)); + myvar.value = maxasymp + (minasymp/(1+(t/inflp)^slp).^assym); + end + + %% when user presses run on runrats then then is called + case 'start_recording' + + BonsaiCameraInterface(obj,'record_start'); + %% prepare next trial case 'prepare_next_trial' @@ -131,14 +154,15 @@ % Run SessionDefinition *after* SideSection so we know whether the % trial was a violation or not SessionDefinition(obj, 'next_trial'); - StimulatorSection(obj, 'update_values'); - PerformanceSection(obj, 'evaluate'); + StimulatorSection(obj, 'update_values'); StimulusSection(obj,'prepare_next_trial'); SoundManagerSection(obj, 'send_not_yet_uploaded_sounds'); - [sma, prepare_next_trial_states] = SoundCatSMA(obj, 'prepare_next_trial'); + [sma, prepare_next_trial_states] = ArpitSoundCatContinuousSMA(obj, 'prepare_next_trial'); sma = add_trialnum_indicator(sma, n_done_trials); + % PerformanceSection(obj, 'evaluate'); + % Default behavior of following call is that every 20 trials, the data % gets saved, not interactive, no commit to CVS. SavingSection(obj, 'autosave_data'); @@ -147,32 +171,34 @@ if n_done_trials==1 % Auto-append date for convenience. CommentsSection(obj, 'append_date'); CommentsSection(obj, 'append_line', ''); end - - if n_done_trials==1 - [expmtr, rname]=SavingSection(obj, 'get_info'); - prot_title.value=[mfilename ' on rig ' get_hostname ' : ' expmtr ', ' rname '. Started at ' datestr(now, 'HH:MM')]; - end %% trial_completed case 'trial_completed' - - % Do any updates in the protocol that need doing: - feval(mfilename, 'update'); - % And PokesPlot needs completing the trial: - PokesPlotSection(obj, 'trial_completed'); - %% update + + % Change the video trial + BonsaiCameraInterface(obj,'next_trial'); + + % Update the Metrics Calculated + PerformanceSection(obj,'evaluate'); + + % Do any updates in the protocol that need doing: + feval(mfilename, 'update'); + + %% update case 'update' PokesPlotSection(obj, 'update'); + if n_done_trials==1 + [expmtr, rname]=SavingSection(obj, 'get_info'); + prot_title.value=[mfilename ' on rig ' get_hostname ' : ' expmtr ', ' rname '. Started at ' datestr(now, 'HH:MM')]; + end - - %% close + %% close case 'close' PokesPlotSection(obj, 'close'); - %PunishmentSection(obj, 'close'); SideSection(obj, 'close'); StimulusSection(obj,'close'); - - if exist('myfig', 'var') && isa(myfig, 'SoloParamHandle') && ishandle(value(myfig)), %#ok + BonsaiCameraInterface(obj,'close'); + if exist('myfig', 'var') && isa(myfig, 'SoloParamHandle') && ishandle(value(myfig)) %#ok delete(value(myfig)); end delete_sphandle('owner', ['^@' class(obj) '$']); @@ -181,7 +207,8 @@ %% end_session case 'end_session' prot_title.value = [value(prot_title) ', Ended at ' datestr(now, 'HH:MM')]; - + BonsaiCameraInterface(obj,'stop') % Stopping the cameras + %% pre_saving_settings case 'pre_saving_settings' diff --git a/Protocols/@ArpitSoundCatContinuous/ArpitSoundCatContinuousSMA.m b/Protocols/@ArpitSoundCatContinuous/ArpitSoundCatContinuousSMA.m index a262bc07..4d79d36b 100644 --- a/Protocols/@ArpitSoundCatContinuous/ArpitSoundCatContinuousSMA.m +++ b/Protocols/@ArpitSoundCatContinuous/ArpitSoundCatContinuousSMA.m @@ -30,7 +30,7 @@ to_sound_id = SoundManagerSection(obj, 'get_sound_id', 'TimeoutSound'); timeout_snd_duration = SoundManagerSection(obj, 'get_sound_duration', 'TimeoutSound'); - [LeftWValveTime,RightWValveTime] = ParamsSection(obj, 'get_water_amount'); + [LeftWValveTime,RightWValveTime] = SideSection(obj, 'get_water_amount'); side = ParamsSection(obj, 'get_current_side'); if side == 'l' diff --git a/Protocols/@ArpitSoundCatContinuous/SideSection.m b/Protocols/@ArpitSoundCatContinuous/SideSection.m index 2bf17465..e5bdbc48 100644 --- a/Protocols/@ArpitSoundCatContinuous/SideSection.m +++ b/Protocols/@ArpitSoundCatContinuous/SideSection.m @@ -148,12 +148,7 @@ next_row(y); % Toggle Buttons To Control Parameters - ToggleParam(obj, 'warmup_on', 1, x,y,... - 'OnString', 'Warmup ON',... - 'OffString', 'Warmup OFF',... - 'TooltipString', sprintf(['If on (Yellow) then it applies the initial warming up phase, during which the\n',... - 'CP_duration starts small and gradually grows to last_session_max_cp_duration'])); - next_row(y); + ToggleParam(obj, 'stimuli_on', 0, x,y,... 'OnString', 'Stimuli ON',... 'OffString', 'Stimuli OFF',... @@ -179,18 +174,34 @@ next_column(x); y=5; + % Training for Centre Poke Increase next_row(y); - ToggleParam(obj,'use_training',0,x,y,'OnString','Using Autotrain','OffString','Manual Settings'); - + ToggleParam(obj,'increase_CP_training',0,x,y,'OnString','Training Increasing CP','OffString','No Training'); next_row(y); + ToggleParam(obj, 'warmup_on', 0, x,y,... + 'OnString', 'Warmup ON',... + 'OffString', 'Warmup OFF',... + 'TooltipString', sprintf(['If on (Yellow) then it applies the initial warming up phase, during which the\n',... + 'CP_duration starts small and gradually grows to last_session_max_cp_duration. Used during training'])); + next_row(y); + NumeditParam(obj, 'cp_start' ,0.5, x,y,'label','CPDur_Start','TooltipString','CP start time during training'); + next_row(y); + NumeditParam(obj, 'cp_end' ,2, x,y,'label','CPDur_End','TooltipString','CP end time during training'); + next_row(y); + NumeditParam(obj, 'fraction_increase' ,0.001, x,y,'label','Frac_Increment','TooltipString','fraction CP added'); + next_row(y); + NumeditParam(obj, 'cp_reached' ,cp_start, x,y,'label','CPDur_Reached','TooltipString','CP dur reached in last session'); + make_invisible(cp_start); make_invisible(cp_end); make_invisible(fraction_increase);make_invisible(cp_reached); + + SoloFunctionAddVars('ArpitSoundCatContinuousSMA', 'ro_args', ... {'CP_duration';'SideLed_duration';'CenterLed_duration';'side_lights' ; ... 'RewardCollection_duration'; ... 'legal_cbreak' ; 'LED_during_legal_cbreak' ; ... 'SettlingIn_time' ; 'LED_during_settling_legal_cbreak' ; ... - 'time_go_cue'; ... + 'time_go_cue';'timeout_iti'; ... 'stimuli_on';'A1_time';'time_bet_aud1_gocue' ; ... - 'PreStim_time';'warmup_on' + 'PreStim_time';'warmup_on';'time_go_cue'; ... 'drink_time';'reward_delay';'left_wtr_mult';... 'right_wtr_mult';'antibias_wtr_mult';... 'reward_type';'secondhit_delay';'error_iti';'violation_iti'}); @@ -198,36 +209,40 @@ SoloFunctionAddVars('StimulusSection', 'ro_args', ... {'ThisTrial';'A1_time';'time_bet_aud1_gocue' ; ... 'PreStim_time'}); + SoloFunctionAddVars('StimulatorSection', 'ro_args', ... {'A1_time';'time_bet_aud1_gocue';'time_go_cue'; ... 'PreStim_time';'CP_duration';'Total_CP_duration'}); % History of hit/miss: SoloParamHandle(obj, 'deltaf_history', 'value', []); - - - SoloFunctionAddVars('SoundCatSMA', 'ro_args', ... - {'maxasymp';'slp';'inflp';'minasymp';'assym'}); - SoloParamHandle(obj, 'previous_parameters', 'value', []); - - %% change_water_modulation_params - case 'change_water_modulation_params' - display_guys = [1 150 300]; - for i=1:numel(display_guys) - t = display_guys(i); - - myvar = eval(sprintf('trial_%d', t)); - myvar.value = maxasymp + (minasymp/(1+(t/inflp)^slp).^assym); - end - case 'new_leftprob' AntibiasSectionAthena(obj, 'update_biashitfrac', value(LeftProb)); - ccase 'new_CP_duration' + case 'new_CP_duration' + + if random_PreStim_time == 1 + make_visible(PreStim_time_Min); make_visible(PreStim_time_Max); + else + make_invisible(PreStim_time_Min); make_invisible(PreStim_time_Max); + end + + if random_A1_time == 1 + make_visible(A1_time_Min); make_visible(A1_time_Max); + else + make_invisible(A1_time_Min); make_invisible(A1_time_Max); + end + + if random_prego_time == 1 + make_visible(time_bet_aud1_gocue_Min);make_visible(time_bet_aud1_gocue_Max); + else + make_invisible(time_bet_aud1_gocue_Min);make_invisible(time_bet_aud1_gocue_Max); + end + if random_PreStim_time == 1 && (value(PreStim_time) < value(PreStim_time_Min) || value(PreStim_time) > value(PreStim_time_Max)) PreStim_time.value = value(PreStim_time_Min); @@ -262,34 +277,73 @@ disable(secondhit_delay) end - - + case 'prepare_next_trial' - - if random_prego_time == 1 - time_range_go_cue = value(time_bet_aud1_gocue_Min):0.01:value(time_bet_aud1_gocue_Max); - time_bet_aud1_gocue.value = time_range_go_cue(randi([1, numel(time_range_go_cue)],1,1)); - end - if random_A1_time == 1 - time_range_A1_time = value(A1_time_Min): 0.01 : value(A1_time_Max); - A1_time.value = time_range_A1_time(randi([1, numel(time_range_A1_time)],1,1)); - end + %% Calculate the CP Params Time - if random_PreStim_time == 1 - time_range_PreStim_time = value(PreStim_time_Min) : 0.01 : value(PreStim_time_Max); - PreStim_time.value = time_range_PreStim_time(randi([1, numel(time_range_PreStim_time)],1,1)); + % Warm Up If starting a new session irrespective of training + if warmup_on == 1 && n_done_trials <= 10 % warm up trials + if increase_CP_training == 1 %% Training to increase CP + last_CP = value(cp_reached); + else + last_CP = 3; + end + if value(SettlingIn_time) + value(legal_cbreak) < 0.5 + cp_min = 0.5; + else + cp_min = value(SettlingIn_time) + value(legal_cbreak); + end + + fixed_CP_length = 1; + if n_done_trials == 0 + CP_duration.value = value(init_CP_duration); + elseif n_done_trials > 0 && n_done_trials <= 10 % warm up trials + cp_delta = (last_CP - value(init_CP_duration)) / 10; + CP_duration.value = value(init_CP_duration) + cp_delta * n_done_trials; + if value(CP_duration) < cp_min + CP_duration.value = cp_min; + end + end + + cp_length = value(CP_duration) - value(SettlingIn_time); + + else % Non Warmup Trials with Increased CP Training + if increase_CP_training == 1 && value(CP_duration) <= value(cp_end) %% Training to increase CP + if ~violation_history(end) && ~timeout_history(end) + increment = value(CP_duration) * value(fraction_increase); + CP_duration.value = value(CP_duration) + increment; + % Check if the values are within the required range + if value(CP_duration) < value(cp_start) + CP_duration.value = value(cp_start); + end + if value(CP_duration) >= value(cp_end) + CP_duration.value = value(cp_end); + end + end + + fixed_CP_length = 1; + cp_length = value(CP_duration) - value(SettlingIn_time); + + else % Usual Case During Experiment + fixed_CP_length = 0; + cp_length = 3; % doesn't matter as this wont be used + end end + + % Calculate new values of Params + + if cp_length >= 0.3 + [PreStim_time.value ,A1_time.value,time_bet_aud1_gocue.value] = Calculate_CentrePoke_Params(fixed_CP_length,... + cp_length,value(PreStim_time_Min),value(PreStim_time_Max), random_PreStim_time, value(PreStim_time),... + value(A1_time_Min),value(A1_time_Max), random_A1_time, value(A1_time),value(time_bet_aud1_gocue_Min),... + value(time_bet_aud1_gocue_Max),random_prego_time, value(time_bet_aud1_gocue)); - if n_done_trials <1 && warmup_on ==1 - CP_duration.value = value(init_CP_duration); - else - CP_duration.value = value(SettlingIn_time) + value(A1_time) + value(PreStim_time) + value(time_bet_aud1_gocue); + CP_duration.value = value(SettlingIn_time) + value(PreStim_time) + value(A1_time) + value(time_bet_aud1_gocue); end Total_CP_duration.value = value(CP_duration) + value(time_go_cue); %#ok<*NASGU> - - + %% update hit_history, previous_sides, etc was_viol=false; was_hit=false; @@ -322,36 +376,36 @@ hit_history.value=[hit_history(:); nan]; end - % Now calculate the deltaF and sides - this maybe interesting - % even in a violation or timeout case. - - fn=fieldnames(parsed_events.states); - led_states=find(strncmp('led',fn,3)); - deltaF=0; - n_l=0; - n_r=0; - for lx=1:numel(led_states) - lind=led_states(lx); - if rows(parsed_events.states.(fn{lind}))>0 - if fn{lind}(end)=='l' - deltaF=deltaF-1; - n_l=n_l+1; - elseif fn{lind}(end)=='r' - deltaF=deltaF+1; - n_r=n_r+1; - elseif fn{lind}(end)=='b' - n_l=n_l+1; - n_r=n_r+1; - - end - end - - end + % % Now calculate the deltaF and sides - this maybe interesting + % % even in a violation or timeout case. + % + % fn=fieldnames(parsed_events.states); + % led_states=find(strncmp('led',fn,3)); + % deltaF=0; + % n_l=0; + % n_r=0; + % for lx=1:numel(led_states) + % lind=led_states(lx); + % if rows(parsed_events.states.(fn{lind}))>0 + % if fn{lind}(end)=='l' + % deltaF=deltaF-1; + % n_l=n_l+1; + % elseif fn{lind}(end)=='r' + % deltaF=deltaF+1; + % n_r=n_r+1; + % elseif fn{lind}(end)=='b' + % n_l=n_l+1; + % n_r=n_r+1; + % + % end + % end + % + % end % if deltaF>0 then a right poke is a hit % if deltaF<0 then a left poke is a hit - deltaf_history.value=[deltaf_history(:); deltaF]; + % deltaf_history.value=[deltaf_history(:); deltaF]; end @@ -392,28 +446,27 @@ end - - - - %% Do the anti-bias with changing reward delivery + %% Do the anti-bias with changing water vreward delivery % reset anti-bias - left_wtr_mult.value=1; + + left_wtr_mult.value=1; right_wtr_mult.value=1; - if n_done_trials>ntrial_correct_bias && antibias_wtr_mult==1 - hh=hit_history(n_done_trials-ntrial_correct_bias:n_done_trials); - ps=previous_sides(n_done_trials-ntrial_correct_bias:n_done_trials); - - right_hit=nanmean(hh(ps=='r')); - left_hit=nanmean(hh(ps=='l')); - - if abs(right_hit-left_hit)ntrial_correct_bias && antibias_wtr_mult==1 + % hh=hit_history(n_done_trials-ntrial_correct_bias:n_done_trials); + % ps=previous_sides(n_done_trials-ntrial_correct_bias:n_done_trials); + % + % right_hit=nanmean(hh(ps=='r')); + % left_hit=nanmean(hh(ps=='l')); + % + % if abs(right_hit-left_hit) + + case 'get_water_amount' + + %% Calculate the water amount for each side valve + WaterAmount=maxasymp + (minasymp./(1+(n_done_trials/inflp).^slp).^assym); + % WaterValvesSection(obj, 'set_water_amounts', WaterAmount, WaterAmount); + % [LeftWValveTime RightWValveTime] = WaterValvesSection(obj, 'get_water_times'); + WValveTimes = GetValveTimes(WaterAmount, [2 3]); + LeftWValveTime = WValveTimes(1); + RightWValveTime = WValveTimes(2); + [LeftWMult, RightWMult] = ParamsSection(obj, 'get_water_mult'); + x=LeftWValveTime*LeftWMult; + y=RightWValveTime*RightWMult; + + case 'get_previous_sides' + x = value(previous_sides); %#ok case 'get_left_prob' x = value(LeftProb); @@ -444,15 +510,16 @@ x = cell2mat(get_history(A1_time)); case 'update_side_history' - if strcmp(ThisTrial, 'LEFT') - ps=value(previous_sides); - ps(n_done_trials)='l'; - previous_sides.value=ps; - - else - ps=value(previous_sides); - ps(n_done_trials)='r'; - previous_sides.value=ps; + + if strcmp(ThisTrial, 'LEFT') + ps=value(previous_sides); + ps(n_done_trials)='l'; + previous_sides.value=ps; + + else + ps=value(previous_sides); + ps(n_done_trials)='r'; + previous_sides.value=ps; end case 'get_current_side' diff --git a/Protocols/@ArpitSoundCatContinuous/private/Calculate_CentrePoke_Params.m b/Protocols/@ArpitSoundCatContinuous/private/Calculate_CentrePoke_Params.m new file mode 100644 index 00000000..9fb2352c --- /dev/null +++ b/Protocols/@ArpitSoundCatContinuous/private/Calculate_CentrePoke_Params.m @@ -0,0 +1,87 @@ +function [prestim,A1,prego] = Calculate_CentrePoke_Params(fixed_length,cp_length,range_min_prestim,range_max_prestim, is_random_prestim, provided_time_prestim,... + range_min_A1,range_max_A1, is_random_A1, provided_time_A1,range_min_prego,range_max_prego, is_random_prego, provided_time_prego) + +if fixed_length == 1 % warm up stage where cp length is increasing +% then calculate the range/typical value + if cp_length <= 0.3 + prestim = 0.1; + A1 = 0.1; + prego = 0.1; + else + range_size = round(0.3 * cp_length,1); + if range_size > 0.4 + step_size = 0.1; + else + step_size = 0.01; + end + + timerange = 0.1:step_size:range_size; + + if is_random_prestim == 1 + prestim = timerange(randi([1, numel(timerange)],1,1)); + else + if provided_time_prestim <= range_size + prestim = provided_time_prestim; + else + prestim = range_size; + end + + end + + if is_random_A1 == 1 + A1 = timerange(randi([1, numel(timerange)],1,1)); + else + if provided_time_A1 <= range_size + A1 = provided_time_A1; + else + A1 = range_size; + end + end + + prego = cp_length - prestim - A1; + + end + +else + + if is_random_prestim == 1 + range_size_prestim = range_max_prestim - range_min_prestim; + if range_size_prestim > 0.4 + step_size_prestim = 0.1; + else + step_size_prestim = 0.01; + end + time_range_prestim = range_min_prestim:step_size_prestim:range_max_prestim; + prestim = time_range_prestim(randi([1, numel(time_range_prestim)],1,1)); + else + prestim = provided_time_prestim; + end + + if is_random_A1 == 1 + range_size_A1 = range_max_A1 - range_min_A1; + if range_size_A1 > 0.4 + step_size_A1 = 0.1; + else + step_size_A1 = 0.01; + end + time_range_A1 = range_min_A1:step_size_A1:range_max_A1; + A1 = time_range_A1(randi([1, numel(time_range_A1)],1,1)); + else + A1 = provided_time_A1; + end + + if is_random_prego == 1 + range_size_prego = range_max_prego - range_min_prego; + if range_size_prego > 0.4 + step_size_prego = 0.1; + else + step_size_prego = 0.01; + end + time_range_prego = range_min_prego:step_size_prego:range_max_prego; + prego = time_range_prego(randi([1, numel(time_range_prego)],1,1)); + else + prego = provided_time_prego; + end + +end +end \ No newline at end of file From 48b3a85f12ba6cd51a4f2e217e65784dace90286 Mon Sep 17 00:00:00 2001 From: Arpit Date: Fri, 6 Jun 2025 00:24:38 +0100 Subject: [PATCH 128/164] debug --- .../@ArpitSoundCatContinuous/SideSection.m | 25 +++++++++++++------ 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/Protocols/@ArpitSoundCatContinuous/SideSection.m b/Protocols/@ArpitSoundCatContinuous/SideSection.m index e5bdbc48..41e86991 100644 --- a/Protocols/@ArpitSoundCatContinuous/SideSection.m +++ b/Protocols/@ArpitSoundCatContinuous/SideSection.m @@ -110,7 +110,7 @@ NumeditParam(obj, 'PreStim_time', 0.20, x,y,'label','Pre-Stim time','TooltipString','Actual Time in NIC before starting the stimulus'); set_callback(PreStim_time, {mfilename, 'new_CP_duration'}); next_row(y); - NumeditParam(obj, 'PreStim_time_Max', 0.40, x,y,'label','Pre-Stim Max','TooltipString','Max Time in NIC before starting the stimulus'); + NumeditParam(obj, 'PreStim_time_Max', 1, x,y,'label','Pre-Stim Max','TooltipString','Max Time in NIC before starting the stimulus'); set_callback(PreStim_time_Max, {mfilename, 'new_CP_duration'}); next_row(y); % A1 Time @@ -130,7 +130,7 @@ NumeditParam(obj, 'time_bet_aud1_gocue', 0.2, x,y,'label','A1-GoCue time','TooltipString','Actual time between the end of the stimulus and the go cue '); set_callback(time_bet_aud1_gocue, {mfilename, 'new_CP_duration'}); next_row(y); - NumeditParam(obj, 'time_bet_aud1_gocue_Max', 2, x,y,'label','Max A1-GoCue time','TooltipString','Max time between the end of the stimulus and the go cue '); + NumeditParam(obj, 'time_bet_aud1_gocue_Max', 1, x,y,'label','Max A1-GoCue time','TooltipString','Max time between the end of the stimulus and the go cue '); set_callback(time_bet_aud1_gocue_Max, {mfilename, 'new_CP_duration'}); next_row(y); set_callback(time_bet_aud1_gocue, {mfilename, 'new_CP_duration'}); @@ -149,24 +149,24 @@ % Toggle Buttons To Control Parameters - ToggleParam(obj, 'stimuli_on', 0, x,y,... + ToggleParam(obj, 'stimuli_on', 1, x,y,... 'OnString', 'Stimuli ON',... 'OffString', 'Stimuli OFF',... 'TooltipString', sprintf('If on (black) then it enables training with stimuli else using a fixed sound from Stage 5')); next_row(y); - ToggleParam(obj, 'random_PreStim_time', 0, x,y,... + ToggleParam(obj, 'random_PreStim_time', 1, x,y,... 'OnString', 'random PreStim_time ON',... 'OffString', 'random PreStim_time OFF',... 'TooltipString', sprintf('If on (black) then it enables the random time between the user given range')); set_callback(random_PreStim_time, {mfilename, 'new_CP_duration'}); next_row(y); - ToggleParam(obj, 'random_A1_time', 0, x,y,... + ToggleParam(obj, 'random_A1_time', 1, x,y,... 'OnString', 'random A1_time ON',... 'OffString', 'random A1_time OFF',... 'TooltipString', sprintf('If on (black) then it enables the random sampling of A1_time')); set_callback(random_A1_time, {mfilename, 'new_CP_duration'}); next_row(y); - ToggleParam(obj, 'random_prego_time', 0, x,y,... + ToggleParam(obj, 'random_prego_time', 1, x,y,... 'OnString', 'random prego_time ON',... 'OffString', 'random prego_time OFF',... 'TooltipString', sprintf('If on (black) then it enables the random sampling of time between the end of the stimulus and the go cue')); @@ -177,7 +177,8 @@ % Training for Centre Poke Increase next_row(y); ToggleParam(obj,'increase_CP_training',0,x,y,'OnString','Training Increasing CP','OffString','No Training'); - next_row(y); + set_callback(increase_CP_training, {mfilename, 'CP_training'}); + next_row(y); ToggleParam(obj, 'warmup_on', 0, x,y,... 'OnString', 'Warmup ON',... 'OffString', 'Warmup OFF',... @@ -190,7 +191,7 @@ next_row(y); NumeditParam(obj, 'fraction_increase' ,0.001, x,y,'label','Frac_Increment','TooltipString','fraction CP added'); next_row(y); - NumeditParam(obj, 'cp_reached' ,cp_start, x,y,'label','CPDur_Reached','TooltipString','CP dur reached in last session'); + NumeditParam(obj, 'cp_reached' ,value(cp_start), x,y,'label','CPDur_Reached','TooltipString','CP dur reached in last session'); make_invisible(cp_start); make_invisible(cp_end); make_invisible(fraction_increase);make_invisible(cp_reached); @@ -265,6 +266,14 @@ CP_duration.value= value(SettlingIn_time) + value(PreStim_time) + value(A1_time) + value(time_bet_aud1_gocue); Total_CP_duration.value = value(CP_duration) + value(time_go_cue); %#ok<*NASGU> + case 'CP_training' + + if increase_CP_training + make_visible(cp_start); make_visible(cp_end); make_visible(fraction_increase);make_visible(cp_reached); + else + make_invisible(cp_start); make_invisible(cp_end); make_invisible(fraction_increase);make_invisible(cp_reached); + end + case 'new_time_go_cue' Total_CP_duration.value = value(CP_duration) + value(time_go_cue); SoundInterface(obj, 'set', 'GoSound', 'Dur1', value(time_go_cue)); From d7a94ccea99a09d2f05b6545b381ab41d57614ed Mon Sep 17 00:00:00 2001 From: Arpit Date: Fri, 6 Jun 2025 11:49:37 +0100 Subject: [PATCH 129/164] error debug for working copy --- .../ArpitSoundCatContinuous.m | 2 +- .../ArpitSoundCatContinuousSMA.m | 30 +++++++++++++++---- .../@ArpitSoundCatContinuous/SideSection.m | 2 +- 3 files changed, 27 insertions(+), 7 deletions(-) diff --git a/Protocols/@ArpitSoundCatContinuous/ArpitSoundCatContinuous.m b/Protocols/@ArpitSoundCatContinuous/ArpitSoundCatContinuous.m index a57da2ac..ed97e21e 100644 --- a/Protocols/@ArpitSoundCatContinuous/ArpitSoundCatContinuous.m +++ b/Protocols/@ArpitSoundCatContinuous/ArpitSoundCatContinuous.m @@ -111,7 +111,7 @@ [x, y] = WaterValvesSection(obj, 'init', x, y);next_row(y); [x, y] = PokesPlotSection(obj, 'init', x, y);next_row(y); [x, y] = CommentsSection(obj, 'init', x, y);next_row(y); - [x, y] = BonsaiCameraInterface(obj,'init',x,y,name,expmtr,rname); + [x, y] = BonsaiCameraInterface(obj,'init',x,y,name,expmtr,rname);next_row(y); oldx=x; oldy=y; diff --git a/Protocols/@ArpitSoundCatContinuous/ArpitSoundCatContinuousSMA.m b/Protocols/@ArpitSoundCatContinuous/ArpitSoundCatContinuousSMA.m index 4d79d36b..32eae2b2 100644 --- a/Protocols/@ArpitSoundCatContinuous/ArpitSoundCatContinuousSMA.m +++ b/Protocols/@ArpitSoundCatContinuous/ArpitSoundCatContinuousSMA.m @@ -33,18 +33,38 @@ [LeftWValveTime,RightWValveTime] = SideSection(obj, 'get_water_amount'); side = ParamsSection(obj, 'get_current_side'); + if strcmpi(side_lights,'none') + Side_LED = 0; + else + Side_LED = 1; + end + if side == 'l' HitEvent = 'Lin'; ErrorEvent = 'Rin'; % sound_id = sone_sound_id; - SideLight = left1led; + if strcmpi(side_lights,'correct side') + SideLight = left1led; + elseif strcmpi(side_lights,'anti side') + SideLight = right1led; + elseif strcmpi(side_lights,'both') + SideLight = left1led+right1led; + end WValveTime = LeftWValveTime; WValveSide = left1water; + second_hit_light = left1led; else HitEvent = 'Rin'; ErrorEvent = 'Lin'; % sound_id = stwo_sound_id; - SideLight = right1led; + if strcmpi(side_lights,'correct side') + SideLight = right1led; + elseif strcmpi(side_lights,'anti side') + SideLight = left1led; + elseif strcmpi(side_lights,'both') + SideLight = left1led+right1led; + end + second_hit_light = right1led; WValveTime = RightWValveTime; WValveSide = right1water; end @@ -184,7 +204,7 @@ if strcmp(reward_type, 'Always') sma = add_state(sma,'name','second_hit_state','self_timer',RewardCollection_duration,... - 'output_actions',{'DOut', SideLight},... + 'output_actions',{'DOut', second_hit_light},... 'input_to_statechange',{'reward_collection_dur_In', 'timeout_state'; 'Tup','timeout_state'; HitEvent,'hit_state'}); elseif strcmp(reward_type, 'DelayedReward') @@ -193,7 +213,7 @@ 'input_to_statechange',{'Tup','current_state + 1';}); sma = add_state(sma,'self_timer',RewardCollection_duration,... - 'output_actions',{'DOut', SideLight},... + 'output_actions',{'DOut', second_hit_light},... 'input_to_statechange',{'Tup','timeout_state'; HitEvent,'hit_state'}); else % no reward but a punishment iti @@ -205,7 +225,7 @@ end sma = add_state(sma,'name','hit_state','self_timer',reward_delay,... - 'output_actions', {'DOut', SideLight; 'SchedWaveTrig','reward_delivery'},... + 'output_actions', {'DOut', second_hit_light; 'SchedWaveTrig','reward_delivery'},... 'input_to_statechange',{'Tup','drink_state'}); sma = add_state(sma,'name','drink_state','self_timer',drink_time,... diff --git a/Protocols/@ArpitSoundCatContinuous/SideSection.m b/Protocols/@ArpitSoundCatContinuous/SideSection.m index 41e86991..4795fb82 100644 --- a/Protocols/@ArpitSoundCatContinuous/SideSection.m +++ b/Protocols/@ArpitSoundCatContinuous/SideSection.m @@ -175,7 +175,6 @@ y=5; % Training for Centre Poke Increase - next_row(y); ToggleParam(obj,'increase_CP_training',0,x,y,'OnString','Training Increasing CP','OffString','No Training'); set_callback(increase_CP_training, {mfilename, 'CP_training'}); next_row(y); @@ -192,6 +191,7 @@ NumeditParam(obj, 'fraction_increase' ,0.001, x,y,'label','Frac_Increment','TooltipString','fraction CP added'); next_row(y); NumeditParam(obj, 'cp_reached' ,value(cp_start), x,y,'label','CPDur_Reached','TooltipString','CP dur reached in last session'); + next_row(y); make_invisible(cp_start); make_invisible(cp_end); make_invisible(fraction_increase);make_invisible(cp_reached); From 53b01c8559899ad42906942f893057088a3d351a Mon Sep 17 00:00:00 2001 From: viktorpm Date: Fri, 6 Jun 2025 12:59:41 +0100 Subject: [PATCH 130/164] error debug --- ...tCentrePokeTraining_arpit_AR01_250506a.mat | Bin 0 -> 15431 bytes ...tCentrePokeTraining_arpit_AR01_250507a.mat | Bin 0 -> 16758 bytes ...tCentrePokeTraining_arpit_AR02_250506a.mat | Bin 0 -> 16407 bytes ...tCentrePokeTraining_arpit_AR02_250507a.mat | Bin 0 -> 17102 bytes ...tCentrePokeTraining_arpit_AR03_250506a.mat | Bin 0 -> 16495 bytes ...tCentrePokeTraining_arpit_AR03_250507a.mat | Bin 0 -> 17118 bytes ...tCentrePokeTraining_arpit_AR04_250506a.mat | Bin 0 -> 16411 bytes ...tCentrePokeTraining_arpit_AR04_250507a.mat | Bin 0 -> 17231 bytes ...eTraining_experimenter_ratname_250506a.mat | Bin 0 -> 15767 bytes ...eTraining_experimenter_ratname_250508a.mat | Bin 0 -> 16843 bytes ...eTraining_experimenter_ratname_250508b.mat | Bin 0 -> 16889 bytes ...eTraining_experimenter_ratname_250521a.mat | Bin 0 -> 16926 bytes ...eTraining_experimenter_ratname_250521b.mat | Bin 0 -> 16900 bytes ...eTraining_experimenter_ratname_250521d.mat | Bin 0 -> 16852 bytes .../ArpitSoundCatContinuous.m | 5 +- .../ArpitSoundCatContinuousSMA.m | 24 +- .../@ArpitSoundCatContinuous/SideSection.m | 10 +- .../StimulusSection.m | 24 +- Protocols/sendsummary_error_log.txt | 655 ++++++++++++++++++ sendsummary_error_log.txt | 30 + sparse_init.log | 36 + 21 files changed, 755 insertions(+), 29 deletions(-) create mode 100644 ExperPort/settings_@ArpitCentrePokeTraining_arpit_AR01_250506a.mat create mode 100644 ExperPort/settings_@ArpitCentrePokeTraining_arpit_AR01_250507a.mat create mode 100644 ExperPort/settings_@ArpitCentrePokeTraining_arpit_AR02_250506a.mat create mode 100644 ExperPort/settings_@ArpitCentrePokeTraining_arpit_AR02_250507a.mat create mode 100644 ExperPort/settings_@ArpitCentrePokeTraining_arpit_AR03_250506a.mat create mode 100644 ExperPort/settings_@ArpitCentrePokeTraining_arpit_AR03_250507a.mat create mode 100644 ExperPort/settings_@ArpitCentrePokeTraining_arpit_AR04_250506a.mat create mode 100644 ExperPort/settings_@ArpitCentrePokeTraining_arpit_AR04_250507a.mat create mode 100644 ExperPort/settings_@ArpitCentrePokeTraining_experimenter_ratname_250506a.mat create mode 100644 ExperPort/settings_@ArpitCentrePokeTraining_experimenter_ratname_250508a.mat create mode 100644 ExperPort/settings_@ArpitCentrePokeTraining_experimenter_ratname_250508b.mat create mode 100644 ExperPort/settings_@ArpitCentrePokeTraining_experimenter_ratname_250521a.mat create mode 100644 ExperPort/settings_@ArpitCentrePokeTraining_experimenter_ratname_250521b.mat create mode 100644 ExperPort/settings_@ArpitCentrePokeTraining_experimenter_ratname_250521d.mat create mode 100644 Protocols/sendsummary_error_log.txt create mode 100644 sendsummary_error_log.txt create mode 100644 sparse_init.log diff --git a/ExperPort/settings_@ArpitCentrePokeTraining_arpit_AR01_250506a.mat b/ExperPort/settings_@ArpitCentrePokeTraining_arpit_AR01_250506a.mat new file mode 100644 index 0000000000000000000000000000000000000000..5c514ff4b039fd0795758e88e9c3c18a83e8cfe8 GIT binary patch literal 15431 zcma*ObyOQ&)IJKO6ez{rDPG*&THK2}6pDLrhqky&v0?>EDaDEh4ek_o50Vy12o@kf zE^oj4-F4Spzd!CIb258po@dQWcFx&H9FqAb_*TJKO9vth>=m0Q9wXaL|jrpkWr9dP=xXS z=Z5yb8@ien!Q*kY02*3g!RNdg??X|-7>UA`xvFk;jo_sYT6_tc=Qy8KXRVx{k}Hy| zc-!Z(P=q0BHL;h?YK|xP1%6lTBU~bkA5b(}_@lX_r_Q}{{po;Nn=qfZdo<@e>8Zi|ADib5Br-9^?HU8rO zrLt4&JWc))?2PIudC~Vrc4a}2Cg9_g*e`=;z)Vl5QNjyr295`W#r~LK7{HM!K7Pko z;WUELu(^7XEzp9r*r_?}LwhIRoplCq+QmAzG&MJwt24?xYqV{BoXI@l%^?S`kO^;d z^V{sXCu5Mp6nct};PO{T-ml#DPlme!T!ea7yswr%!-uKz!wPjWXB-0HzA7tPZ${sl@VXp5U(LGOkLsH0KpewAu0BEC5L_R{oo-Q6 zkE9UUjwf_swoT;w(fO>UeD4C@4Ye1ESBd5LKDS0z3s$~#ADRVP@3|ql8_mX#ae@;# zBL9i12npX#d@21t#I3fCrqa} z!a7EwU`-=3MN9P#AyJCcuY2J*;jCwi58X`;B)Xe}6yW6a8gm+*77zFJF=HDcrXu`(uh7|GiOvVH~)y;7BhMnzuhItZ5g{90wqG#iUz3Gvtv@HisivN0pl-c*tsG$ zXo_j{@#7w9T6S%&7trq1?4d#OObfCU_wf8dKL}Tj-DDDSH&vM+$bjMA1+DuOBPPz- zwFF+*Ym-<@1!JCUMGM7yFk?O4Mabt!%T#US;+oFO3|@cNydiHpjNYQ`$RYtAwqEyr zl8Z-XV&#oo)=wn{QpU&9D}zg7(*yt5u+RtpGhGR!lLizBnBmj?dV#0z)R#X##LmRO zkWS+L*wddOG8bm_gg}Oys%t!RwSzvJcy8g%PD$*H2+9ADO}T8__;z8@E3*rp{u(el z`Nv7VJ-q8RKY&duB=BTdGpR>_gxPE2pQ$coYx^`MkMhHNHs#$N(9<{-CCwyt7F$~b zB1X3CA9&j(>1PnVrDA4Wpr_3;f{3{|#5!u*Rsz(Vbt z=l0nXG4T#A+V7R0lz$tmli5s_TA)ZklKgD*9a7P&HMfH~9=6jxDB#|uPhHH?5!~1+ zZNT%TJPixEQn;>E{AxL5vq~oo+^;yT^*ivK^fjBwX(Lq3cbG+&1}LP%b(p$soiSv0 z6792wVJ?CiFp~@k@?DLWfn)X=?L6NM1kNYLPNHIOmG}`^ZT7y4z%-0J>o_MPCBXV7 zYMKr~Qlod0M8NS5ot9uSSIyh<29q+DEM^Ix^TnV|bcuNNFGhk+*D@>XH=(XX06i%F9^waSe zZKsfPd8s6l0m}xTug}=ppLqN~HojL*jivU&r0xEjcGGINCt`_oS4p4#off-7$K6u5 zF=~r7h}Qh|5j$1{Xf*jCWmer;^+q8LqM=br(jC`OvKRYEAQ$oHLBHd(vXN zQ)lwtVRhMbSsjsjt;M*CMKr}3yWX#8(jl9f?hP2|71ss0bA*bZ>Ya1sD|*LihVT~@ zKG!hHk9=!8Tt!i*TW*vUQ#D>i>rLaE&!^(j>cb9Md4ZO>Nvv>O0Rgv{WT;eu!W(%7`8;b~Zby3<=jQG~KZPv>^}`AXjV z!!HWVKji(^oZK{Ys92i!IPJqK`Ht&PsyB#L78|e`xBjdQ!2eR;^9{7CBQhz*=IPEC z8CO@;?_bvptV4u;J6Pm|j{Hc-L4vEUGqsu8Ot2Rn=Y@yZVCX{5h2`2@a~2a_uP{B{ z5hC_ocyj5KkN-Gf?*@?-{kpHpsZzenju+9_H#1eEB%r@#ZriSZf3kKgW}4wy--O?) z<<*oS7K(~GB@+s1sLuMf#x_wL#L}X3Qnm@`oqX@3Y`qRRe}{LIw6J}E%`D_zgnRqv zis?E=BOS4*h8DM`Q?A7m#Pw^XrA6>S==@=(vu*)b{k7LWt1d8i>E=C;$<5cv`qKpvqNNCrpkSM^J zDFW7#8e$KAc&G@r&Ien;YNyVZtmTS@rhexgo$sSa1J){GAU?_Ad&l8ahuFMHR#EN^ zv7J1>g0)y<`Sqj317PWIKz}DlGOv|x%_mHfTo2}Ouf-b)3-;%W=om+~yo9l{2c29m zZN}9BwIdFDkFfXK#K(MQX3ju^g>zV-Fi;88duN^TBAG$D#Vxii(7(#<*i_4Nj-S&1 zmes7J=83ixBLg~KCEW|HV|gMhV9!U%dgcZ*&%|s=4mGZ4#@9rr%;Eq_ir=Rsz+Sv~ zu2?nN9uIwTa~ptFY<2#jv5{-V6w)49UnolSh9a;FBH1Mqd-5)I$FY`TbI8epJn-by zZ1wTN!wj z(nYa+(=j(*9nWoeFyxWZQcC?%CJK!~@(zl^F^Y^uQi5N}gljN+y4;nU+?k$P9*kIn zY<8O{SIjj=^|eMbojO>s1%zlI)enbzA~;JUL={30)7RgD%`bq#^Qfz24hHC6vsL4^ zX@cJD;S`Z8RH8Kt%*P~7g-zJIiC)RRC9Sd ztIZB^T|djo4D8?lel)9v=-s_yYOC>vitL9w4 z7TwEsGmMV{qz(2jIsftJT=L1DnCCiox7-|jF5x!iuKKH1s_virlla{>MT?F0&9qC1 zw44S|VrnWVTr}|Ez5qgUoOc!qp_Ch}%Xko5U-f%)JL2U0AB!0<1GZS5k_*q$jrGh&>?8X%XScs4-$minnlNvJZ*izfwd z;if{b-&M${g3xhp(@)BCWR(Rk1ztP}<951zhxT0uO`X6O__ZUGh%?5E9ru&I^J@Xs zVhr@Kb*Bdm_$R$_l0?0a=NjjF+gEc^8yO_Ox#bJ3DY0bfX7m`^ZipQiB;eTS1jm&n z#RdUFnSZxP1uqL36&N@+y54<{kbT{fv+k>y5>lnK*d|xK`ldBhCSxoJOB(vL68y%8 zJ&vI$-8ADcr`WV$Og>$rR!QkbDb6;cX`Mmw_F3tA?7*&C!+4n8eSh=%R<8{IzU+k6 zZ4Q#BY+sff$-#qAbKi>HgOryJGmC~D|980?=P#r0oNiGgdFN_!eGB zEY2ro!%U9LnnDA5cD2(uU7C+1W+u?#pYG4~(!X4m?LRQiikU%Evym8Bk9T$b4r;>3rnf@qAk8z(nUnr*HI~bad&2FQy8NQN>zk zgHa~{1499mD_Swln+|iB5ko-+GkQB?pfqEiG5=sgsmrYkF+{W0IGW zHbYdO3ihNNWB9KUORrF34qK8$PO?zFIdwv~hIRzMFrN?)h$E-;PT=iT#;T`(zzaOK z;cjW}E22yS-d~v&I=!iqCxK^2by;=`Slo6|B&rM)7=XWV$><_!w ztE|f_Nm{peZvCH=(p6u4fb?&{=^Df+UtEpJta-q7Rv>aW-BXCQP*mAnp*tvNc)X^) zZn+)0M&s5Bx%&ZKBY}2=RRvl5-?x4#+!9^BsqnwI-mh1FKjBL%kU;AlFZ~=4RYoV;Djm~Z}tEEb*}j9{M#VDW`+{S-(uq@ z2t!=N6QOSQr0GHJA{v!B#_T;=3gDDE@MVDlG~GmkLS=T{8m<7$1?Sn_Sv_CR;aSfm zen#nlPOUl&tL%wb?bCUi@y{NisKCwDcFv-F`)A7~OV+@26cBR@m*Kvef8L9^D5O=Lgph zsP^dM97NPNBCe-jl8R+y-+(A zcX7gQ&5eB9*b{FfF+^G5{GNX{=GiaJC3Bg8;$T0<=$)VKO`oJcu@s+|^n?JDv0}Vr zf8152B{G*=Y@mm$pGPhLEyKImyj@#PAFQF6yLn4C{N#W%l z9rx*emWfl9KPm0bj=OuhKtq>6r}MLwr2r3H>ApakI^!B@-Xu!dVSeF2LHZHiU(;y%g3bYC zlN7bJj<0@O9i+V;JZXGz@h&SbDiJ1WHa+CrA0`E`_8ZRu{J&_Jm_ZPx0q#5Oop6>z zAgQt}j%$N*x|}-nHdlW>LR~gsJc-m-BuMK#=xHeF{3Ub}LO9rnYh=)tTA}X;A8)%< zH<%MkY*nC4Q`M5)Yu}+2K+{K|O^mSAKop8E71=@a^HO_|RWq%v@f+!^{BYg=$?T5Y z9qLvtIVkqJ6b*2kqSxgtSHvph=k9kL@XPn#_!-Afik21{{QjDX+@=N;^C!QqoC}6O zj$Rc+{$ib_O~KnZx<}ReZT^|?om3b{+gzUKZndzj+Qk^biuJmBcFA}ayDGMwr%=|Z zO}9iFZ@~)pX-W^fjz9&Q?5NOAp_?(%k(2%a!773InTYK<%&fjalf`49m^?t?+J7c4xcn|EjId+%?ZNQPNn;cQjYRFF1l>kFVE!Yq!&mhPJjg4drS{G_YtJ zw^bsh#9Br6s|Y#0me3OCMBQb~(@%B~shq8yotK`%)X7`d5ass|h$FDXR|pFM@fg$W zdjT!or##qcCn4dKa99KpXs5XN8shkHV*^LY@fKmi>IcHA!F|)s(xe|EQsmd3VIqJZ zkrrTCV}O*$UN{`>`5owWl(g8#L73k)AIn2x*c}re7sk*%%EiwPRdx387A$7~#|&U~ z>~%Ntf~l`}aPn${ZPfMOhlBr`bW)!ix5Kw`fC$5N{p^@;#XI9ef^~-NT^YbSf3Gs3 z;FTog0JB2I6-GgZ;y4V87}bma0Q#J=ei8V$)zrf{zp8xeahy27wa= zw+xR?^9I7cgEPV>j@5?f92o5d@+Tuat0PXcl1<2W6^lc3z?=F26(Qk5Jh09C!J5PI zR#vFHNNXhZ(YZ5GXl>b&emufpkOZ$gXQ0a`Dv`n!PE#2xK&LZ={`*XvPT4J+Fl?jg zR`<8pkP%r)CM2I2@jYSu!6|qDzOvA`Mj|@>Rb#Y=bX_<#cZ;(RGGki|2bwuiZS^ta zc(5#Yzx`(Vr%>;Szn9=kiEp;VPPvE>f%jSKKih%K0N62D(4|&xA}HeTu?mc3Q;Ibe zKNbg|uPivj$Q$>G>Htr+BCj@_k5!7V`S|okD-&e~1}%c8DtHT}yV9>cn{GnY&F+d* z3R5!*Q&;`pJg{otgnT-(_B9}_z-Ye!B~6?dDE9yW$i`Aw3 zC?h5yuAroj-`#Do<_0{raJ^PZpCh*7;;q_k7>rm)T7_jwV)W%F@nqcZVWcr%@zU)T zTJ_g=>7I1m!#@q6e&u)~YkDxIFW0a?V)NFX9sXA8I1&FEH}J71538|rsY9`bX>{RK zM&VS*NzY>n=7IEjDJ$tE(-Yl5`==!`yGtjdOUJl%sRdXgn2P$;)$>uyeZv4@)qXWb zT`3dmwgNzdJxA+hi<2js@b3B&zdL;nGT98y!v)k5$2Pz`SDfL(a5f0q9`NO_5(fXt zb2?lz&0H0gJh?A_BxuyZU=3oPhJI6ZYyR4% zF7cT!9NK-d4>y%3PY_3&1e`&lbjc)4MU10$0dha~q@w2HMU6czad^8lYq&`Q0+sJE z=L9B=g2W&Hmsvp10KkT@qqw~QUp$-Ib$aKg#?E>%J{0=Dlr7g%f1aN-<|s+rN@6ns z`zVrOI}ja(7GUc4%r~|cY?*wr;xW`x={yz-+H9H(C!`5h*LRR~6FM#E57E}PYSpJ-7rsas>SO(FA)0^nKy?f%(SIi%7+t|C(YbsfS6m3UfU!E=7U(i2tz*vf0$$BqAEef?DT`JL1bm01q?jSB9dz`9m) zhx360Hu1ud?k0A%a zm5dx4sS({3*SWLBF(3Xg#2|c{IO*XjN50O-Vnj2%S-=S->0>oTtMgCe64&aprwzGh zYSqFX)*TTiI}hyjoOM*$h0ULpSuc&tF)Qrfo9%Xc90cP!^WRw}gIvKn66v05CS#3~ z3||gIzQPH-LOp9b$sxgPvF-2b8^Noia#cps85)vANg>9VoGoUt29y8+E#^TGDm z8)(!%OH>8>PY+KZ8s3!Ec7KhB;gF}%R5YS~#AkW~zj04>`iks(1#bvIT)^1W7~|2y z8sm{WpoMEsK?YhH_ozTjC+&x*>ibQS?gWz$!o0Ri&kMPRS%aB}gq*+0j zUBS;MRpN**72G->r{a&VsNlU)=?}R%t_-SC`-_W919U|cqllg?pEqWEA;@RUIZ8y6 zpA%0`Q7!RuV>dQB>&z^zS9hX-VaH*>s)BXW)DZ2%a@|%gfFb5AQ+}zgkY4bpzX+%z z1K6NH7*xryy>wp~j&AmmoX{mfkUI3yD9@u#wD#j$psCdd5o0E+SOLT(VQ)_)9Kc0$ zURKOWm$JUjl$rS9OCwqzk6&bj))W~*;<<7ngJP+|i<*8`aZTUPYI_1mUK1e0)xE3n zYk*r+s}W7J#@_W1+p18$Zp~jeEkD;V@di5kW%}HqDlHz+WM_Eb$5sP@rv?D80!H?3QHEEY=yeXcHRCM@_YC?*G|u}`^~gq#@`*70nVGpzZ0{Z z8_nhH7ttx*7}={Jm#=cbjb3^119xO3M;dbgr8c zJhJypv-Vw}l|{5G`0i?Lcu6oR35mo+^a1!fp~;;KiMqsmDDDHe`oRI>bQh%SGP$Fj zd73WDmy;6m*Uu5YaX%N|3sWH4s_8@D50?Y1lYsJ-Zxp{PbrdZ_;yL6pT?8gZr!vEj zIEDQ{7X>h_-5prSTwvgmFnxIVqcQACO#t%v4wv8H=&oqc5BPQCZlNFW0{R{5gb>;Z znrS!pQ=vf=?ZSdW^#iy)&Jq=PNrU=F8{Ge4tV%#68yXUPm!yI*OUma{*)=hh7TRZ zs?VYl!s5s_q!y4DTnq1&S@KK&w>w(;PMK0{_$=-pwhdt+3jAhd94((%G{ofdMeT-4k zXYbhD=VY}=o&Z2EqU6C$SrI)6l58`TOxRbGx$|1pto{^I)EjlhyWg} z3P^Gq+n?jz!P&*TV;+>7y%tX3@BjR(kHP6qW^l{=*t$Y(I6cI956T67ux(ueB@E-= zE*~L?FUBnyLWJSE#&-jF$mJ$p+Y34sXgTwe5FmgoAfO&emQq-qN%J&zHf$DHkr2|p z+@-{f-%fNDF*dCEQ1l7Dr6=jXgU=)`@N+y5T*TuK^CEoEr^Gs+?tlG4YY|xjrgyOr zvfmE#o0>{beysIzf_UNpQjAEbftV$mLIH0vwgMpax>wrSIGDfxxhQNh_ z=G#y06O$VWQwpC^G=mS`Wq>8p0QW=f*FpWAX%C`j*<_xkm*7!sto@KyYKCC|b-A0m zm0u6XxH8*jJGf>k#O+q5)6kmKGi}<@E13#w#pZ@#YwMu>%u65`MK)+z34qOpVhK?} zTEOm0fsIHvVydeyfBILvp`)lxqpjk`py!$KHIgZJpYs<18t|4wY1;U%=cl8TlbhzP ze1#lPwL&g(orE|!blE+QuQ(6Ph}Y&}&$-&HAvK(vE`?%aAe@ymrg5tm*1>cgMPiq} z=S~W6(L?E{YV~B3x1~N}WC1=!`JPHbOJm6Qwyj0EluL7VmP4=Ne(>caZ6J74)XgDE z9|sg2U%oq&Xju#hGBINNT+@EwAyHxm88#F9z#{KGUvs|kq_yEi(ok2~wUl2s5Uc;V zlMfP|?2{UsYu{4St<@FxtP$avIRzt$!(l_G?0GtrWsm-m6hl*azO&m;!aik6fbU!< zA2pm0dBFUyv6#<2dhGKO_GH$M$KXvi_*u@inzgP;Hql+V*}Uj~&TjssDW524w7as& zw1(}|E>QTlO&hXAa+Y^wjFc*Cpk0~3z4Ygbw7vXHp$alRHi?|rdKB7w&t$YWnVv87GUxjS;4^G zUsjVA??c}^XIMPtN%pJ(OZljmqG~b&M0BP=H|k*| zHR1u&A{Tpd+~K^t3J$ZH(CKm;>Z$<141KK22IOw%mHr3MJnU;GLhO40SbT z(0q-yS+8|nKU!EP0(Q?xdpdQ}EOaWa@~7t5+{eed6&K}s!uE=O>+0!5 zRNbJ4S5aBV?{}_qamTWU!ci8x9C@m8fSn1#-E9f}98qumO-E{-(URTScgpgrW`D)) z>>y;x0jvi>!ckA``&MSer37Me2NFz03R9sRnPJ-+$z^*~GMKW=2JJasNeo8(X>ceYNc_{0zS)DQZ}sHP(8B)t9Un7pFl zFGa)6!71=~3vV9gU#)Rnq`{f2p_a?(@WKK7Q_T|-AG!44Gw<2K_K_QVh4y$At6Iqf zm5B&0dY+^BsmXntIiTF^g|u0XO3xcwv@>v{7gkXD_2ck-f@g7__yd$Q>Mc!o)_txx zf5qbH+x2Or%`d#;C!o&3f;R+&=EOqF*8gsz>M%#piWWUb`Osx<9{TeElu*GN=uSnh z57kNzIcKWC zuLd%ifjViE@KX^H?x8x%$ZpSM;^Oi^4lbuqoySJUlP>0?E}wU=o-vC1b@tutUgrw3 zenRGVsht*wR7LsU8xTT2&vV!`p2wV*cIu2NrvGoy`!CS7u43e(fbF;iW{HQbT2{7V zs&yHp?Yqio7o?s(Y3Lh2gun%l$)vL3uOzhVk9ma8yKKBSsz1QT9%Ka?WCbVx2eE`A|HsXibWcWGp4N1=}j8SVG1i$nglsnL=#FYi+k!kc(tXuj52jYv4v<-5GH)Ne zMcC`|qQY{GuLpuy3;R{X8j2OHiePKCpedy@N8&xcfrFJT$0xF>L=2*f!QaZ46QE6% zhc4e=hTvzBO^Nizl1(xuesd*;E}wdq;{OQNd`Drfa3}I*hTxP3{Xw$N@%? zeE)KUMx}n|t2|L8_lFZ=ZyR_^L};s@6r~{iI4>qh>oA%hK=?qHLJ;>%`wPA)^TNKp zbzQ)dHqZ3+Rtw%7nZG89C7`U4XL8&4i1$I#90=Y#$GKkTl;t|D)}7`_cj4)w!}fx`To1uE#*7fxo8Psi{_qOC|FSP6_q*ea zbg&^?^pL#3?Y3swdrzV1Cn|Ve5#-w~L>@FdR_Z#m$gB$UcgylqPc(gn3%?YN`$#7{ z-n==c6~zoaAS%A0WN`6S@iuzu?eS8g6-djbU=Xm1fwuKANL6k$2e^|;2;J5rCQuGp&ldo+Y4VYrTvmquZe<~54cwqj^^;q?1& zv1V-8+sM|a(`@n0rXaRD>%`;kgzK7Tkm9#xe$ax71-6$tb}MU7vSI%v zccKaFSVl1cVJgP&Pq8M6t1@$oWD4aau9ThADIMQ(^?39x)>T5G8Y4PF#9RvvD{`YS zi@)CM%-$vG zlZ8-QQ;w$wA>+Vq!4c9F`BTJGTZ~~VKpXwR@-<&4U1YQ^QY_=mEIR2JruF5U8FPS{Xk+ps@W$i|uhr(#J$EX| z%J^h-Hb++enn3SVDIkg)(fquxDhg6yb;R7d2S05^gAhdZ51}A1->( zhf=U1k+z6h-K*}U4#I7%L##R^va>U+ZxHe~2o~Zj4ciaXxyp&$@^oULK}h$DVcnpA zF~s6DZ?od*FAh6b@e)ilou~^C45=)yAHz7{ylN8qwUS017Vy@n=9CXr+<-s+#wJu* zf@YF1PCEDGOR%v0$^lM6pqTvg3a%{muS>;Gx;7cRHocH4zVa1uI4^8jmYrzj%xF1g z#fBFm;Xq!W!YYmgjX}Vrp=)@ztZLDqj1Kb zc|j(LHXH$=04HvKA}5li;cE!cc&72{C)VWiOKXvvVMKX|hciFJo<3WIIU`E{`VQm2 zTy)YzOJ_}Z@sG$T4r1#AeKpJxZT(6=^_SBMm=p%VKNLCHUDn@;)H4qo{9sR! zxoW+n@N+-DJRX3%Wui6Py(XRt@eFWuC=O6;>u^}PFWBJhIQBeTYFTM2h>0lgmU-KJ zpED+OwT9)N+8C0t@m_a*?$0@P2Sq7JVDn8Bg@yan;kCa8-teLRDdg=A z>nE|)(*vb2JPP?|fps{YYEfcTJEoHb={#iq=FXW)9F7S9137y^vEVB-xherJi|Gytv1`g?yS!b% zRTWI=u}L4S;d;}(LYL`W*};=37L8$ubuISvL7-30XuwY^7_T_lqQ?osduh4Q=3zQX zi(*CD8#|VN=3~86c?xn1w;~e9jMA6iedn`qwa(zb@iYH}m8^9&J9|~$pIYKjYVaIk zv3Nk`JZs)fo|1H996L1CfZm8~E={E)d|t$&n(hn@3HGaSZPL{}~a>Tm0FV{N-ht5M+8 zF^DR=n#ED~%Dq(Ck-iEqPyqz>Fd)YU-ooHT%@x_0sMR{zu?p?4zvv9EoxaNmphd^d|N_Qh&ch(s|H_Vm4%Vl%?}c!#Ts-fo?EvjVHuJ+CW?SWb@iD-AB zC88a&Ug-}G=q<;LHOnf?P9!KSV~iYhSL5lpA-kSG*<&>i)_^ zn>9OZHp7o3-0vqK`u(y8z5K;FqeURbbFp_ZpTAv=3xatttf2Q2K}I>Rj{aPRazMO4 z?fGOgL{vY_!fqI@ls@Pt>)1XEnQP3 zE5Br&WQIio`INp3n(M zEGh(kjmh^858RUF7_S@N{p0<5M)7_MmRR;?{RdP3lBWeovJo6kBFU#~ae6cGBT!_k zwR0?b&39|*&TPN$ENso$ms6z99hr5eqE**)(z4J**Rt|-tI#9`W(t7@9Az3e(p~+c zl68GH^(ETatp3yiH0alRNv|`5c@9^*xFMi4mV=K)!c!*+c14)N72`li7#~6IwB~=B zjd`5^w`OBQd+o0ll9T7JCCoc?1V>+$QZeI7o%t8zm*s4(@q)d>P6vhxE|MoLMqK$+ zZ0}@wK#Nt@TxYHsET}MzjN)zB)ajk9glu?d1ZrdOoFZ@$%qYZ5UFGU*4vpPwMNFZt zh17Dze|S&rEpl3V-~zp1qsU-Fl_68_H^)vcM_#8#<5BSaay8E zI)h!9x=u*E%u!vO|I^~}!hNhlv61^Xr%g5Jp=clL<{!h4zPD0c-s_nuF<&V2;p^2c z00)L#yM}+)M6*mU1lNR9=21Zq!le{bM?i`SseNvLR03T1Fc5WGC2%d5_EUM$^+bze zaL&Tr`YSI?1EEGr(YBh?PRr!*UW0wd$ljY5(J+x+`Z~O68E5}oS%m=i zHC)|TtC{q>|JD1g;O4(OZW2h>vFqKWN!iJYnLmvk0U341jYkr@=Tzn-@pm$eQjt1C zJIe6|kPjIbLKtKE>13=kHUu1QcJaUoj?a+mG9F;S8>v-1pxJMZD85lKpjg z|B=slF8{AfaE`XAp2WIVs!A=4#7zgLsm z`EPm@NAHR6)JY5P!<;YJzILYa4RA4=xAM}vZ?pr`t)oZ3+K&&ep-Bt<4E+GZKKz^b z>YE3*u@PP`fjR`dtP93yzy7%ZHTt7w`F^1M(hsoc@h(pVv(&RH^X|3&qaM3arfcJ2eS5E4}{nPrOmrM)4@#aQf}5?|>nsuW-#~EWOU-=Vd~xq#yman zgTA|s178Z`8y3xs#@=xHR6>|^u>SbfUMMZaVCn|WG3Ipn$2QN;KVGdjseE6j8y9R| z8!YX2j)`cFq1X`e!uH(NlV*$A?f^!TuTS!cm-6%w7<+rZkvgR+ZX;yhKj&4e;{uFHN8Yc42(s6X(nM+(@sn>pQO1}vAl4&A6C zxzMaXffkAGuBE0~b+!yoqocX=KiFF6f44}qAn5aKS{Wm?!ZQzj=q(=D82zNVce@P1 zd{k+K5ikR``c>Bv{KN>E|ID)HkVid7cWgQkW+SkU6iNQ_6Jq1bBtuY^*mM5NY4QT1 zzTF+GRE7xB3)$+owY@dtcm%gQNdF`H({UI?X$dQOlzUuAb2k_v|8WIf!^LTZ|G&bG z|8yL)DY^fI8;=T&86b)e-S%f(yl%XrW)kO6{O-MH7M0gXS zxPz_1BqN*hBSbfVOhDl0CJ{*)aeOXin^D+5QOND}Jf(Xd8~Knf3GVKX_-MfVK%aQ- zGV0$FnByGX|Aqr2&Hq~hb6io4#QIq+{f*Vxe!J zmAgEEB~*o0XE?P9gG(;!p?^hXdCZMs=+j!|3p;Ru-(+K5Y8AmfLHw}mezX0qhHEN` za!8GQYc`h=+#S9}|__-OHaun34 zYPKDX!Z*u$lq3%mVJ!eggi~R#5XysUownUj|AiTBRU7!?_AOE z#@0WrrjUDkSW>u>1_04` zJ0tDiD-t-9cv*h(6AzOq@BZjhr8QpO{==cOpYqC$gG!j<$)g(#{Lf7pzx$jSU}59? zQa5^L^y|OlFbioa$}`Gn68bW)zW1csfk}Nhsk-7j0)heS|7+rHFWa&{P8TF`vp@wJ zaJ*9SE+?+*8~brm@sef3eWk|fE|=v6Re4z+(Z^K(0}HJJ4v&Nn%P5l*BPhF!bpPY5 zca^@E2&69a`aWn@;9O7cXjVO#NA3#vKG?n<^zZXOw@^W2Q%B_)_aa$_V|^`7Ah{Hb zD&`;He1?iVWUA1~TF1cufwzt|nk>Pj6aAw1Opn8A8n_q1QGJASq-oKtF5g#B zdlWvvq>bcF*X*8k{NmPj_X=jxsNO{aRm=R^0}IAEv?=nxbr-Y02+bBo*Zne~E%2n@ z_{gp?@RljJjADu}nT*lbfoic9mn)1B5!otBXtnP}G&>9#ub7(A+hiX%Y{<@c;$bL@ zRpg~$8uTLI>lS4ZKNi7*$foogAyX*6TC_yybf?ONWUfl{y?e$fJe2w!tM6NwVFRa7 z(ZqXIOwV_usV6%o@?rUXgENG~`P`+YP-~D}5*YE8w%6UDE~mWuWETu|So4)6hcsd(r1El+ie|_h;=>W z7%TtT$^R1JI-UL_+GoV_-*zR>zCUido@->*E?PB|YLr4Q*8QJp zXagq0Vzp1r|I6+89{0e4-S_X_d7;^gG9EIfT=z O7T|CapalnD#{XaIvLoIA literal 0 HcmV?d00001 diff --git a/ExperPort/settings_@ArpitCentrePokeTraining_arpit_AR01_250507a.mat b/ExperPort/settings_@ArpitCentrePokeTraining_arpit_AR01_250507a.mat new file mode 100644 index 0000000000000000000000000000000000000000..0cdb63a839b4f05808360a5c69c6ecd79990f3ae GIT binary patch literal 16758 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 SL+}vGWFyd{Liu>UkNjT+`iErz literal 0 HcmV?d00001 diff --git a/ExperPort/settings_@ArpitCentrePokeTraining_arpit_AR02_250506a.mat b/ExperPort/settings_@ArpitCentrePokeTraining_arpit_AR02_250506a.mat new file mode 100644 index 0000000000000000000000000000000000000000..4668f92eb4f737870b575ad100e6666d7de678b6 GIT binary patch literal 16407 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 V!&b#Mm_|7dgB=ec&<`c%{{c734t4+l literal 0 HcmV?d00001 diff --git a/ExperPort/settings_@ArpitCentrePokeTraining_arpit_AR02_250507a.mat b/ExperPort/settings_@ArpitCentrePokeTraining_arpit_AR02_250507a.mat new file mode 100644 index 0000000000000000000000000000000000000000..cf64239bcc122c1cf48d66c2b2acd0e93b91d6ea GIT binary patch literal 17102 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 literal 0 HcmV?d00001 diff --git a/ExperPort/settings_@ArpitCentrePokeTraining_arpit_AR03_250506a.mat b/ExperPort/settings_@ArpitCentrePokeTraining_arpit_AR03_250506a.mat new file mode 100644 index 0000000000000000000000000000000000000000..10415c028bf8536a7332c4c549d382d8bef18f85 GIT binary patch literal 16495 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 literal 0 HcmV?d00001 diff --git a/ExperPort/settings_@ArpitCentrePokeTraining_arpit_AR03_250507a.mat b/ExperPort/settings_@ArpitCentrePokeTraining_arpit_AR03_250507a.mat new file mode 100644 index 0000000000000000000000000000000000000000..101ab724e87d845dd5a762ee8725c8ac22b4764a GIT binary patch literal 17118 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 literal 0 HcmV?d00001 diff --git a/ExperPort/settings_@ArpitCentrePokeTraining_arpit_AR04_250506a.mat b/ExperPort/settings_@ArpitCentrePokeTraining_arpit_AR04_250506a.mat new file mode 100644 index 0000000000000000000000000000000000000000..e236f862292f739f96464392c10424bb8674bba9 GIT binary patch literal 16411 zcma)@bx<5Z6X+o%xO?#6?ydoXI|K;s?yh%Ou;A_xAc5c^xO;Fe=;7`T2OMz7Bl+rk zQuV6d+uGf^?wRiUV`qA2X8Wfi`B7O?ii(e&lS)PMBb&9IqZKQax}%w=wTruxFqOKr zu7WB*FDsR_yOo)zl_iynvoO_1FDoh)Ge0USekv|5VLl;Y9&RdbPHsM`|IY*NzX!a6 z3f9YSQ9d|0-`v!!N%up3>6ciLIt4#K>*2euPm8m(9<-2wZYi>QwacP{v25OR0>Ic`-T>5qP>ULCkFw(HV znP`fLhJ#Ot$&zLZN>qcpo$>Y}s&=<;evE)d$DS2~KYER42CbX+@lSXSu5w~(*^h9M zlLfE0H#PRdDpe2coNH|$Uu@;hTb6}@oUO9EXgv%n80!ewf!}XHwdcsH+OI-+hLU|Z zNHwmfoGXED=Mc@tyoRuPEi_9q`Qg{F`z< zRgGf)9@vvnaWL-5*O8;V@Z+jENYq%rB}HA&K<@&#{115dG7v|19a5|3&|HCqU@Tq7-^7yD-p1c}7DwH!uz_j9+EZXP)!WkN@VwI-YHQu{o`#*ZR*dyNgu>P$jeR zI!}>*tGT|5pG-tC@I9#s-{K>xSXo6?bA2cOL^cL1+7J0FC%@#iBaMdT zFasBX&ccHbo`c*7)Et5&$3{0!Aw!xK+MsEF8gryTj(HV9L41i@%vf0zzrmHoas5KEt9Z5 zLL;74xoj8sTat$c#r46n5OzO*ogN~>Uq1rAT&QGSvboQqb#ZyKFu2aTT!bS9;X)a( zxRZ`73WzQ^ak0c=`=zmHF8=h%i(dT`=O}!Mqa!I~Hwz=bpRX_R9%>+973012B{q5; z5=rM!0;_yY;s1%-KZBvcM~s!Oq$0dm8mriW{`{YH{%m*OzCV^|tg7X@S#-nV^5v%zGM=o$@lmFpnk8+ zs=>q5*V)a5r}RNB%UCJE*1SIRJ=$OES#;fFGRaT=t0;d$a#d8N+MbNTYsuH?z9< z99hLBMt(L1SuLC7*tv_(7tKqTNFEyr!EYJ7fwy8XAtGwJFI8t9?OA>hzfHecmXChI zq~B%tg%aMN)$6o2LtRBK4mt3lrOWa%zXuhoL@X({@0|o51~G5LahuS%2#)Q1g^c#quX94)!*!p`&1pD^c(Bc(C18dQnG~H{)Ei$8RzMJ7tTk3Fc-T1vsax=hxkh$se zL?bq$206(th$Cny`ds@hY)^jvS9Itu9RGqza6>Gd^mFhJeX!Wv4A05n1gf0qTx!ST zext6)er<$#u)g|>^&-!I%v#G|ngS!mB2;OQxdLlK|M?vBy{f(7joA1QlO{N6PN!P` zDH@MWXY)ghcEdF5-}0|IK+wuTSNgkNWww0wKso(aJ;yE^WI`6VqJf(_Jl4jFJj>dS zzkil%TU%~p`>�`=$Az&r9>@Z+ebRx(YAaese%a`fs8+X7eKPD0z>p5zn4E{kPWN zgzlsN3~A?Y>oJouX2qY|VNz1z1QyNGkPeM;HdR;o?(&mQiy9#_!hwwI zB{PoScXNqnL>nPk8_%65-4Q2v>`U-RFnHWjVuy0HZLnlToJ$xG?}74Mvy8n)S5ZKE zMdr}23QWy8nTFpk(CWC%3+o>oB>3^OmbwwRnwHF`5pzUX!-PBIBQ=6Z;+!KZ&*X|> z-9}gluf&nR1OxHW`MKVGRx9aw+&^tf%oOtr%y%8v?e(^{hE_Lx*79B}__g$;(*3)bGt`wPKQ1Nt&mV3+@M?Ta7B2xMYW3Dm^{w!e z?UXzlVKgzaGwD{@KD>x+ip{qyJC5DC^j89$Q84=k)z59Q{XKT*zhjv1qmNAL3>!AV z)Ou(DkOL3OR?muQB_TKcT<0(_t#p8JG}{&Y8i~h`&PNT|1g(vsLSL`ZPO2?d)T}l@ zo#G1fjg9R1EgHG*kr$&)5qwiAVj3yTkP~S%W?fB_PR51&4cQh-Q^I#mKHy0zhP6+@ z;`~(11Ww(!I+?N9Yz%p=iw>2*7g$QmZcYt%oWaHYMj;NEy0J8I<@MzG$yJl}nzA}u z%O1dNO=1Ze4hXVe+gpMaIMxI#opqc%ax{UL^Ku$5T=X|VSeu}Zr>k)__ERIJ7v7FS5150lLr`UPDDp&IAzFN#n+s?^k(A?HuSzyth&-;iu=?h z{`X1sNGGiV2wNE%4u-P-<4(7D#!{#R;GICTM)I~O$#=F3gaV{$5H-UMX>nH2C z`kz>LoTcW1D~aD7*~QdPrJ<(6>*1(k7c3=xQkY3`_0=0AE{=gGq;SP^fvXKoU(9zB ztB|rcCDQ+*lCsC3Bg^+?L?om7%WYV*O%D6l7AvCt zWBqCvyXZ$1{`*QS_n!lo{$Q!J+h88OjaPI3oIjM9{{uFhAfZbDlQJGAXjD8h*aeL(w1WymDlrXnzU~#?+Q!#BXjfYOe6O~r@U}HITrdw!3MveP zm0G}S9Rw#^6VD@HH$#FrPyv22*kmaFJ6@k-Q{&1J@RGA|U%~Ub{-6=$7h6u+aF63u zkFnT|^iA67T$I;)%q|E>OJF?2r_psxQR8<$irTf@RC5sq??C9J8I$b=ytVEB7Iqs7 z7I1$G><2V-mbSR!!A{&ql{$iBd=WBSv!E)uDffUfys`p$quno>87ITg5zUO8pl8Gj zs6O~eku1DjNq>eqlhP%(&z;!C}UpyF$h{`w@br z+2nuX$UyuINh^3P(0DkgMyZt_{mrNE<7wUd=<9d#nCHS_9CpZYWr&ue6ybO#ls)E$ zyw^1*^vp*^$qSS`LE2%!dyd(y^8Jt-VYDwU{hr-1z|7-3|7QgdIureHVlw0HQ1PS?6Hy8bhCVp&$MYMEF&| z&(t#O)5NY#J`QxzG~cZrq>Ks(3i!qo7Ny%n;*b+3lUG3Jtg%Pr1>`?`h?L2rG~=+% ztBKDly`B5BXnHyv^uKC!w@SHQYJRj?rVzi)kW zD^t^TICEKA^jEp+ZpgQ0AS7Ch;7knW3B)wBfx-fmA#=p3?R}+pT5BlW*R@jDdWm1L z2*vBHL%UUf^&(kwbX;*ImnE2C?^{StBO!1CUa9+k?R&G57puH^QeHnvepXaCO~U9W(1kt zW*H~cpAS7saDvUGWz~r8!KqozXy8T%WIfh8(SN{;xzFRVsiXdjEGCe#9Bi*P9Fob>8+k*CQ6H^dA9#SKZIg z^;rOy96_?dgL)B_Y)@gkMaYR?o0z@5S1Zwbt=8T6*Z8&#&!Pf?W!gWJxSTGhPFM2$ zUFNoY;lM;s`iyb}Ye_@MXAGbJ2*R6MM?Y|ax5)@(LwJ>>v^OdubUxfPvHHjY(1 z)lO#|@Hhey9)UAo81&OaeGJ5Ot)9w`HvSlAorEmvfB=;oorWay0VY)5+F zWTSdQpNqqu^)`c?HwxC_G}e1!j<*vd;LVTDHoWKk=okFv%#rAaxizmqq|vaF@PLRG zL0r{^OWAzV6g^@VcN4|s47O!t_OIvim3cAI+)$ebY&IL;YT_<1F4PN6z&aHn-Y*J$ z=TvGZxzuS{l;vvAqj!T0e#7Bsv)Pko*0bbhknPSEBgV9)b}I7& z6!#f`X0nc>BEVOJ3ns#SmLV-LBMud#>uOJKu4!!vI!lMHp+DEGFh9#7Js0;s&-Zq9 z^L{4N(mZOk_qvX>WDueJ!-%VctA^eb+bS+moKe19;-A(sjV<{~N?H9EaJ3lolqMSX zBibPUXj4Y%=a7CS>RD%h+uG35)sZ+vW_J?pokZ`?n-18_iX!M(EBU?Q2#IUpx_^}A zVzEHRyP-)OREvI9Duj2+zLO7i+wK8J^jQX1Y8NRNV?=IIf~a2(N5R)8;5hGOuQrR# zjFE>X+-D~=2^UhWY+Pex2?7Z~(ya;pUGVQ(hO3hDJhkLJwQ_Gk4AX_LA0Mt&e?d;| zMRNE*$$oPwxVIL8m0-lPE;Z1v_vZ%J3jGQeyL!0xfF2;QTXO2_R<&f0V!?k z>BFYj!e$G)ANBhROU-TmgR6=b8O@c~0!rnb!u1n*UzLVJ;W7FydxhKXPn!+{vE10} zM*`)6CC+V+NnS<ak_Z=SRAIAxqI?rZbXZD;2hI6KkdDUA`_>;EWp)O7WQ}@7mzvPtferC zv`6OAjc=T&aB+`m!WdV>qVJ@xrov-b5yY7uy~FC&aA^_oIT@$zO0bQUmbKy%cDz@; z&QNqgd*6(n94t$qS3FEf=MykycPK$;(rl%Q)|%gLeJA<~9<{6VcOJn*Sf%3bkB!9SFbCf?#-)c-Rq5GIVA6aV<#lT`u(*RP zfk;oTmTAy~EzCh~U8M&@kK^O#=`M;!--#|lV!=k9i;rDF#q6t_s$^?8hOVafC90?Q zQcv`4P|@_3{2f1O8`jIaKU_1b1%R|>6VevBiCO?$yx7K$1;|IXmuIe!ul6^>Xj{Y+ z)5*ij_Tck#d*+hM5Y$=S)`s}9E_=>qNO=BYd0Zca6)_R_$4*EwiGk`fe6m}jBm%46YZ$%zdmuZYn zU%I~f0C{d=&jD8lV;ATAp1rfE%IkU23^N_KE`HZF#pR|9H!Rf6F;rOwGgVnyhK2V- z=Ei5B+se5%J96=~W;Ja?Zskm#(Uv}ROI9<;z**ciR_8CAQM}wkk2A&PHs({yc=p|5 zOUJ5a6~JA3f5-RTbae}WI7=9Z=jHXw_|>@h5JvfJcpZP^vPb_x_RvhRw9YOHLOuMO zE2s2>gp2;;4^u&#iZ6dA?;`p;3O9dAu+_D9)qBlmYZ14Y#t)H#`G$J!bNHJ$P=Jx^ z?}o>Bm0YnNEt7vr>0(t0<1H79tJ-`E{;2#a-LI?SZN)DC?0{2rKmbvOhR35>56r+u z!k^}1mC?^rR5dz-D>(+yP1bOX)8+ilxEBN}^$Q|3idnG6dsjpI#KQJuemFIG(9j=L zHNK~%c^xF|y{hEB>aY}jn<2Ob!l(|K)X+$d+n)-eIjci+))xqFggM57`Xe;EXd&+q z>G6v8_AH{IzA=$Dv4pwNvO;(KTXo>}6aTes%)3R&;txD(<$ks)a_l9vdu}oIKhynM zJM$UpP2W~uJ3>}Fhm~}8zQpg@%FFtuv#+t#oVh4osM|Uu>tdJOQ50o!2>1mylYx?F z?d>H7UEIOUUcuA#e`20X`a4<%#@*7M=3o%|nyW;S=1*|3MVm{D55UJjl+NJ?w0z)` z_T$G*oZq)jwVz;K%T71rO3hZY)ak5{Icw*YZ3wi9LV$Ep8C)9Z#VgHTTYf1Eq;`T<5NoNsuMw;4pu0%bopzFmk zwziYtZW-XuGa)P5-Rna*iPt!bBKtz$ZSVcdc8g_203!N%)xd;@jF$I}U6davjy=Jf zLUunP8owYHnj)OPI`Y9wHOUD#qLk+!7TB*^-;b^$4X;L4EFCQ@>r`z$n{w2f$%(j> zdvF*9c*WJ{OE(@j2m8DUHbv_Hif-LvJlhE(7f)G;RGUX2_1>&#{0z&*=1#RJ~G z16hzWDt-)NM7pTETQPCebZ7SZ-q;n}#rgH_C0U5`pBf13p;y+Pjt7`f0@022{u_7+ zP5H3bE9&cyXEviw{`2cYk6~SB!`*wN*DIV+$NNz%Z^C;T=AuScKkXGWv3GQKjjk4H zHvocG8a>9i4cdoIOGIqSCY)xLH-4VneNEo`Gt5i3iJ83VzZa(1sU(v27owmtjwf9#Oh2IH)_A``a(=Z(%D|#_rkrO2=bv zxAQdxkV!&iWwj_gCVRXxkWl;H?d`xAjBR1qsW;s7@~Emcs0`N7y7mM1UCX$pe2?hx z=_6nl+;+GS&ig%=yf2fRdA2Dx;J2ni=C=>Mx>2~;86Un)uuiy2=F+iEBr{C}r*!vt zfO$`|qhVt3bEP;`Ei5EH1N|Mr?-NPuR|80VKx1qa@*kcLLJcM)vYF#I*d3Owcm&== z*U+>|7`kI%=EuzSyrF(TX^k)7JGH^{>F?tVwV>+!2Br-=!<} z5{DbA3q_Yc$lN>HKE&w*uVCIcHs5}X(@42g)rY&^CEL^Sij}d zTFIu@PBVio4IcVq8hAc%76;@qGtXObGx6SQlzQVq-F{PVe25DH>L=pXo5p`!HECk6 z#XaqaQwV|JAuCCLOx8ACwF*eTc1>^qj0u>}TVBC3`8cKQ;zuNjbOijsU5OdjoI!RX zL2^*hJP)YV=+s?{m=7eKJ^6y@KD9}<@eZ20Cg5}9`J*|lr4Gb4PM*9p-7LoAV||(r zi2pQNrA*GfCcBIv+H~{Knr@IKeB@Q3oOj(iu99a(uE=9!vNkp5)$28B_z?JGj4V0k zY;9yrh;2gKz9nHiccq;L&&DWWr2pOZ53$m+9dUj)Z&)?Ai@X>%Y$a8M$HjYZV7dMg z>ECi|-pFBzlQ_Z*tFgBD*K~D3D#g_n;}epBY^!8lxL~Eo=!eQi>-e+<%@coQY> z85KW_gjXdhwl$~<-Y)e-uY10=uIjvuU4QaFM=FpfZ7+arr?c@{4m3&M<^o)qde`x3 z=q|2YB1*BLs9?#W87Vzr-L*h#*c-~`^W1f+T#4}Hdv}VJcH?>nF=#1FxL5_`ToH2w zt(+z|2U@ntr3YM1<&7{T4PHkUd>HXmV}s5dV3rmsry>ckK#G=|C48Fq(gRMtwX^AE zihhNPfCkfpj;|zYAR!C_+bs;~0h^w3TM46SkpM1|1ou0nE+9=B$M(g{S1(A^JN zvGeVS{NsMU?Dg^LJ>-Luzq9T`&$qg?bDkG` zeUiV}thO5(oHk77(PJfWFd%ke6>pm{y_$FgTDRCZ3&h0Kj?JC;n-?%>TxWfRNcI5p zuMBkUMX?}80u)c3rN8^yW_)k&zW?q^$RNS$a-Q-1>Kxl?^GnO;=N%vWY#vfmwaDt@ zn6GjDsm{v9Z-DJxBxb?kL=36B(Qk2n_E~b^BZKr>>O3M2{NZRBzN9=6Ssg0+$!*f_dIi|PspH`LG!UD=AzDN@_ zfH_jZ-fll8px_8kq`pG!_z&doZEKdnd*5rC!TZO>M1E>U{xo~*UM$+F(3bz9&`EJX z^_EnCYTJIkgOMZXEm#ZmmU7v{h-l&8wI~D4^_~cT062mg*csq%*fZXR=}=~}EpUcbJ%PjvC*n-uE*qzd(Y|S@%xuHhL(Ww(|tm2 z!-;qEEYqB_Z7w_k_|lAXTc6o^5U|aXORC2JMxOW#o8QcrKUJs;2qQz3 z6>kjsGM#jrA9As#E-bL23TRd@A1&L?=WlD8zkXth4e_%z69{HLdVKGWrEka`Lm$;I zBf)pZKqwK-NGw|pQyA>jS9a_4tP(J|9qMlF3aiy87jA{l2xyv|makBX#6ve=Geg3D z-8{Q??q^)rDIaAow@d9FJ3Lv2L9r(E5(LlkmkIih+;?^(A{>CE2gH%Bvn>-^lb8EUMw4~$aelKv;YlyYB!xK6 zFj7AD@bTn0X8VV6smHK{3&a8UTaq6$?l=EDeE0nA427v7@sA40viGdX-~0isU|Gj(NL&9E@u92IEM(Y#7uu++t%a$jS*4w@GZKqk^ z0XObwe3-FF0xgkvX%Ki8T56f!>snY5A5YFRE9|VeYMmDe=+uR8%q-q)UAp;hF)UW? zakm+bQ%lqxf!@N~AJi}Yqe9J?4%*~bk=&%eCv=dK$PNM@zd834ojKBL0rMyD)4CK- zNGDnvXiR4CLsPba{^Kf5MZL4`kD2>b4D=({AN4XM(F5&c=!&7Obq3$7VKPy-9}+~d zqamLR)&|CZaTj{d@_CJ-%!)|NwZ5Ze$1Oh1J0n>h;Z`SsHg3Cs?;eQ9l3@TL4$oz1 zW@K^y+-%b_q|CrDs9^6;8@739G+GJgXUU#~5>@2srp$Sa>&Jg%a3<^E@8Y&QrF!sZ zq28Q@7`WUk!OJ#gg)P?X(}tx|uon-tz=A+pdOjOv`l@7mM&pjuC z)Vb{u^cM3{jGL&ju~9r7)EL+~_EZjXriT*u;>1T76LIlwvfsX%9wIF)10Rf>`D{a) z0@Ghru_h0rP+!xihqI7ckYH}8@q^4f? zMV+^FsdyXD`DWl5zo*G8b`)$Qu@Tr9B1{;!(e@EGP;uipOAlhfFeGj#Xa|q6(o4={7KKt zx5>`8krk8UH0_qQQoYHK%iqh+-;)(%$8K5Zc=xRWXkB3g=pyhpESv*CZgRgWKyDtG ze4>`!`2CIT{f(;&8GH<2j3kxnOLp zh}cXzmxvmSxTbyd@tfyA)rJCCu8=doAiS5H(h^A2{jz2$_AwBQ^)3qK_0e$?-$T8+ ziwfYHh{R+#nb|WH0 z{N2$J%VHaB4H3*b?y7dH0ksUM_?=F+?s2oz;NfLOE)+JO`UzsW0voFXOgIH90~OUy zV28y*E(TjW7E9mPzy|^PYmIqpe~|HFiJy>id03_PkSDOw$kOms8)@b`Y#ae#Z~w9y zZdTUdqt&{GP8X5LBvalffa^>b=BF>LcUv7Hu(;pa?njZ_P{D+Fp0Aaa19x{Sr9Qsx z&q^y#UDpi|6)aA0nb%3ZDFoVUq@{mUv;6~6oG#v; ztN4H=3*Z*m^Evc1|5WztRM@%0S>#NRbMVbO+K+Be9lU$F73-|k#Sodidwp@d{&wV@ zrb9cgAuBSo!o~&^YB!-S{*D6h0SB9VL#E5!(?wB_#2&ZMGp`=e)0sE8sxGG2T zJM(G$Z@BYWbX?IfCFQ*O)!4lh!RtNEOKIE2?)`;Cb&kXqm&QArfblCQE_p=~opf@Y zb!5>XxzBRf^wQHeayi9s${izgG{y7u?`sbd&!7PXftE4%Zt{hAxl^b?ot9_PH^7lq zP(MSEIL?;PPKrcE|0nUlM=h(zz@B@T)N7L%H~bVl61`fHZ#{yIQn+fj4@F5(gyg|N zrxKm@SSM7_N;gd{>FpmZDceW1dt>Q`YLzRol)g%cJ?^+JeG}NF!CSfatRIhBR$JSD&T3XAgc;R?n3GemI zK@F|v4+JB6Aq^hp?|XC?aWw&z8xVrczCns;bCx2Fev9VWJJ6lRtlFRh{Ke+hsdv-T z>QeZwq>D@G!qQS?-_7aV>IPz>>-KM-uPt5vHMBJ?p; zv_e}6!KHI=#KN92z?v+_ca^JFt%CAZM~3^dTPDdXst6I2Iq0R{elzOZV(~E+8{jU%(!rw zLdL%8u|EC!E;du-6s|%Vr=b|5PjErbH}HYWK6d6=zOR&#Qi!rdhl`S;myua;qJV^q zbYs3OFWk#$4f7x|i=ezWpU?Tzz#lxMKRhJ)2u*ML5k{tZvYdCR?r1<61UjP=eY} zT<89lmSldzD))8j_B*WVLkSFMhS%r8K}VN~j{vP)FGSgx z@*1Q6TF!{Yr^WBeU2Y+QslXKg?}k88s+@YE`Ni9fuzb?l64#QF1sX(@fP*XTD5 zyY5z$C&J#fa{TMQ5g|T^qJMcsT|ApmAU5bRPLCGuhNkOv!6b*(Q9(7wQIXfmNIkF# z-XYrAZ}xMSLaw01L1+tvsTPS!w4t}$_xD~^1c;}9FRS(ugM$G5k+Wcn%cRiwgUW3a z&a*dNn+ZVcqp{zWJ%=&r*;?D-JwDQfD|1SK@b#2r48TBnn4R-t87Tj)_9PmR!23{= zhY(&xk58)ZX{5@o(-s`h20=w%zYFc##=p^_3i% zlBt-MhM6KM{Eur(fpQYX1v5s;qehram~2dZ@Jsq%V5?&jV@n(N*(G*4$2M_~fk{t0 zU>#xlPhe`Vnh65-fPne9&TK4Kb%Qe!BGb3p>ms`N5~{khBN#l1 z7xEn_$a9MIOfNuHfG5$xX9uIrnzZ!_;Kd%GKc6yKEHDsXou#OzY-@U*fyLJU3^+?_ zW*KK^D;we@wEHKg1qXw~e{0O$y1p>zYX}iP#-mJ3)`%Ym_G(5jbjQkfe27%tMYlWxW)H1N5fViNVa)=L8J0g1Rv?E5M!x1RV|an(QovM(0SD$kO~CVr*qZady* zFkJg@QR6{W8lG=vnW@2+$FwquzoBZMUEppJ(}3B>KG?PAs(?sY&(zNb?rrB4>Mptd zJ&Dd@A_YcaW;61pDVvL9NtvkBO_!BxY>OL(Vj!p#8Ky<03kij6X zTE$0Lvrz{{SMc?ZDI>Qs?=ODO)K8BXyB8M+V=sABE)T&GmAzM;g>?xQXKawEy{7>$ zoBXB8{#}Cgcss~Ur>Wk$x(}JkRUwD?fJwI2O(vYu*(ag}a`Q+s%Wah%Hi`3z%+y$1 zdbhR}X9nP7*zL8*c}&8cIQ_VpxPsFBd#=!g%ZJO2{-oLrpRhQH>N)8fqTj|23%=&} zk2INsgEveXVbcC$=85F)&Tq81Aqs~4+ksLDiV@-pl)t3S+oVg-3s2(dBpMS0;3X)} z5^bf7dK)1dvC;H9eYDtXAI<J7lW*9{mJ<=LvIYAn^&FAD{n(gKyy_I}BjU0)uSF%aiuT$CZ{4ZHcLwF4sFmed zftk8q!j%g>^C>=?6@A;`?b{XX)_~aaWh7B30R^E$_+ju}S4I^>1PpFnT;27KKz4?R z{l}4i)cPu1vjDzfXQS6i0J1BE&LWuWgGuCq5Ba*W?umt1>rWRABND2IHq+JWUeFE~ zrtPoYm9dwZX5G1m28?sXAAMJB&izhj{;N5T_Bqj{6<@Wc!>?IeciFbP2q)Q7N5wqr z8l}xQ!#YPR#Yu8Y8A_VBYn@D^Y`B zxqX^+IT1LU(y3K*poRD9>sx`sJigWG0d1X^6syv?B&#bC-O|816{ULBf;EI@v7gm1 zdxyhQhE_(H*tyIAwkoAvY}R4nyGQhM(djRDEjgKGPX#`xP@$PQQt{jcKbZ0$g z4g})iPLp)zN^@LUG*?ITexRM5!m>;3 zGER_2P7DDi&q$#juUu8}b3gT@L=jxd?8-K+D>fx+aR-T0-;f~$#1jNucDS$pn#vP; zmT#1)*#!6{K@y87A+WiMDTQOhO^Qq(HI~V$dZY@7aV^sMO{$k>ztqC76pOZ8#cL~o zMQa*QRx6>Pp^A?sF2P_(TJ9)Y3P}6$R{LPIXfNm|$7Wo)V6@(i7{^*_ zKO3jucBI(kJRa;Sc~9w${Q|~pU(l=b0$a!-Nj{0g;(rn&w=7IeUE$36sdnMa2@#P^ z!!O^4WFjes1;eG*z`ZFG8YrW36Q_!&GL7fX78;S?4Sw&YU@sZyX02@>D5+uZ-x*pJ z8j>GU$cHc%D0R9m%TqwQkmtwb>Oq2>xfN#}FOV!mm(@~sArtL)fGj8F+mhvnjl;t0 z@=0500)~B0Q&L;seF)z6c$LU9LqHcChc5z7cdmf!Z^+9zlyiN3< zBCN>GVF=^oWa>V_oB5f(x{}j1p#zj}WbUl8n97(gOrg}Tz7Ruei?cwO9+aFDlJ^+r z`;qXLkDLJ)2MA?0vXg^j?_&Ymhcw{4+bb2S^$A;E099w5wz7t~f`YwpvoqL3$V>*0ePV_n=THv0I(OiD= z=*|8?W+ITDF~%_9-Fl>0%l&iZgYU2a6I|t57<`a;vg!MAxtDA++o-M@i>QhiDfz_W z(t^^0HYwMr;x?9+Q!d1?K1*1n=A)gd>o^6R45zg2Uxtbp^?zrmR#cY`RA9_(EyqUj zSVCr~C}Hwb#&)j>Kj1C7TV>H^05&_Nbm?C^R_XBbD0c_y1FLkqbhg2?3gtm5+YGlb z9?>qf_ykIc)gYgXhd22RfN_8KFZRhyp59iss2ZWg9C@$dlleElmxI2AjHWTIO z&-t+yQ%)rI8SGc^!cm?6gqP$hC!>*_frI^rquNH&E0~%#HV54kOvbN!wA4L(g&?+= zAm=yo1!KKGPg|6T?4=RZMnRsSQrZl6`5-0_L&CILm-88}&S+_5xDA9?`W|U;XYKNR z==?flGiY49&`}J(NjNbR8_0+FOsY9M=>snXr^*wZE}b0Oc5hk39%Iht{&{0y<;>!C@)LzotXe02h{M3<+9lXslE-$ ztzF2t<0S!jLnSY-?Y&?2=EW@>sFS*oERoKK1(z9O5(_)>a0B!2G1KtnNx(m zsgRQR#N;{^Sj}y72ZXK`#c!TCwKS{L?h~SSP{cwz1ZTq?OeSFS2V$KXHy0;^u=ml| zZh&A+I*@dqR8C;|C@%Fmel`P*xEuWuNfG<^1v@R|OvV2F_a_Ztq|6T7m%tjq3KvwUr@*juM@mf!od0^As)zt-UH1J&~SUm*;N*> z;^*Yy`|RRA(6^$A6gi;5tDSi&62i!1g#A5ib0a5a^C!=H|DQM=AAl5hmGgte0u7F8 z6N7FV|2s&P-K!E{IkHmAs?}!1Dx7P5Tfx`9pC2OaOPaCC#r1k^;7$FyGq4B8=`TnX zfVcP;q{{m*NVRn2Fd5t968jK|F{st-)Ic5^g!lLlq+;bO$nE+EQdtz0vOxVg#lfv4 zIFAk%^jqf@I4>a8F(m2*qqf7>4$SUj#%a&AAr3Bh`zJyoT!qy`U) zbF7=uo(2#{!(OPV&d8+ZvwvdM3WO6E`odQMuwph=YwTt|ioJkT2YxRgReq$5Ly3g$ zE2tPNNe3up5k^6XHw~8B=!~4?+w%M8XiXdR0#ZR=K&o$hW=Rc;(7(RM`d&b)um4Yw z>hT4n(vne*RirgH|A<3HPgs_fX=d_*T*(v-D%TXR2yp4gmWIE8RmJv*T>sds*8dZv zI?VY8QVo{=w#DPG<$M9Dh+jae*cXuMcTfM*zmK%X8RO1d{Mf--UyZIlUTL(#5cT!k z&;_H-%2sK5hAgr^WqUH&3mi;mB-7l<@l5T{Nux7He@+;46@)~4n-1(vtr-Ps8f2aH z`nrl_g~{jERr}qX*m;4U6Mfs$jrI6D>u&_jZuEA<)%v=WySvJ&)*u%j36O>d&nSzf zgQ|E>*9)HX6#^};Q@_vELGME5kKnaJ4+dWz1+zxhRm+yGSy#U6;m}u~FJiWVf4pLU-J!2dgR&aSS@9V>s zDH+I)gZ9UltBE;|p`FDs4F@?jV8e*1xLx^XgJX;pn=E8i|# z;J=j1+RGM}9a|JMc+ml|r`j`rmB)l2AZGq=#$3BvLL2?cfh# zF7gW+2wT4HypJO)Smc2eJLIf%1IbGBA%`t~6s*w94V|_w_^d0n)DD~^#x!Oqd6m)? z&54M5G}Dz~p~ZNFnBT)*KTw9Qs`epTJCIVX&Nh!Nf0z18-2i>r*&Yf)0o`~oqmp$g z7gI#d+_3eAqGTCO$k-8>IC}fQ)eYFW&Keaozv=Z6Z3c}8|I94qi8F|OvNA%uK&b}IVP8Q)CPTf@BHuI&z+8!-t3{^3VCl<^(%s$bI~ZY?t>ke%c~rhQ4iSE`iGw31y4D za1^Q-jh7o8>otuT-9d~MIg71tU1J>H8Y@-VZ>}&DkQXsk^`3Z*S;`R5fO-l&Fm?S%f&rY9^!6qB`_|b`QUN2olDg;OGqC|Tvcdn$x$Lt1>hHJrzmB@8uwQrM6>Za{n?KL`t$&XcwKmhc z;o_n8_O@I*#uMmx%Ft=EXZXLldGX!l_Ahu@m<7Y>Q0EI5)hChEWQLynirH}`+76@sUzx>0xf;ApTRaBlk6{(+J}H){oJAccG`3Nsd* Jzt5ZK{{e}#E-?TA literal 0 HcmV?d00001 diff --git a/ExperPort/settings_@ArpitCentrePokeTraining_arpit_AR04_250507a.mat b/ExperPort/settings_@ArpitCentrePokeTraining_arpit_AR04_250507a.mat new file mode 100644 index 0000000000000000000000000000000000000000..583806dd243a096eeed6ebee5d8cf8b1132c6b96 GIT binary patch literal 17231 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 BbB_Q3 literal 0 HcmV?d00001 diff --git a/ExperPort/settings_@ArpitCentrePokeTraining_experimenter_ratname_250506a.mat b/ExperPort/settings_@ArpitCentrePokeTraining_experimenter_ratname_250506a.mat new file mode 100644 index 0000000000000000000000000000000000000000..24c98418a8569453708ae83132c41b50048f0075 GIT binary patch literal 15767 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 N5f>R=fbX5~{|`c)^Y;J% literal 0 HcmV?d00001 diff --git a/ExperPort/settings_@ArpitCentrePokeTraining_experimenter_ratname_250521b.mat b/ExperPort/settings_@ArpitCentrePokeTraining_experimenter_ratname_250521b.mat new file mode 100644 index 0000000000000000000000000000000000000000..bd207242f0c06dbe17acfee2a16933f1552e4711 GIT binary patch literal 16900 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 literal 0 HcmV?d00001 diff --git a/ExperPort/settings_@ArpitCentrePokeTraining_experimenter_ratname_250521d.mat b/ExperPort/settings_@ArpitCentrePokeTraining_experimenter_ratname_250521d.mat new file mode 100644 index 0000000000000000000000000000000000000000..51250589654ae0e8be3c05bcc953f72429a05cdf GIT binary patch literal 16852 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 CD^&yl literal 0 HcmV?d00001 diff --git a/Protocols/@ArpitSoundCatContinuous/ArpitSoundCatContinuous.m b/Protocols/@ArpitSoundCatContinuous/ArpitSoundCatContinuous.m index ed97e21e..f29c97a3 100644 --- a/Protocols/@ArpitSoundCatContinuous/ArpitSoundCatContinuous.m +++ b/Protocols/@ArpitSoundCatContinuous/ArpitSoundCatContinuous.m @@ -129,8 +129,9 @@ x=oldx; y=oldy; SessionDefinition(obj, 'init', x, y, value(myfig)); next_row(y, 2); %#ok - % SoundCatSMA(obj, 'init'); - % feval(mfilename, obj, 'prepare_next_trial'); + + ArpitSoundCatContinuousSMA(obj, 'init'); + feval(mfilename, obj, 'prepare_next_trial'); case 'change_water_modulation_params' display_guys = [1 150 300]; diff --git a/Protocols/@ArpitSoundCatContinuous/ArpitSoundCatContinuousSMA.m b/Protocols/@ArpitSoundCatContinuous/ArpitSoundCatContinuousSMA.m index 32eae2b2..9f02a446 100644 --- a/Protocols/@ArpitSoundCatContinuous/ArpitSoundCatContinuousSMA.m +++ b/Protocols/@ArpitSoundCatContinuous/ArpitSoundCatContinuousSMA.m @@ -31,7 +31,7 @@ timeout_snd_duration = SoundManagerSection(obj, 'get_sound_duration', 'TimeoutSound'); [LeftWValveTime,RightWValveTime] = SideSection(obj, 'get_water_amount'); - side = ParamsSection(obj, 'get_current_side'); + side = SideSection(obj, 'get_current_side'); if strcmpi(side_lights,'none') Side_LED = 0; @@ -43,7 +43,7 @@ HitEvent = 'Lin'; ErrorEvent = 'Rin'; % sound_id = sone_sound_id; - if strcmpi(side_lights,'correct side') + if strcmpi(side_lights,'correct side') | strcmpi(side_lights,'none') SideLight = left1led; elseif strcmpi(side_lights,'anti side') SideLight = right1led; @@ -57,7 +57,7 @@ HitEvent = 'Rin'; ErrorEvent = 'Lin'; % sound_id = stwo_sound_id; - if strcmpi(side_lights,'correct side') + if strcmpi(side_lights,'correct side') | strcmpi(side_lights,'none') SideLight = right1led; elseif strcmpi(side_lights,'anti side') SideLight = left1led; @@ -86,13 +86,15 @@ end % Scheduled Wave for Go Sound - if value(Go_Sound) == 1 - sma = add_scheduled_wave(sma, 'name', 'Go_Cue', 'preamble', 0.001, ... + + % if value(Go_Sound) == 1 + + sma = add_scheduled_wave(sma, 'name', 'Go_Cue', 'preamble', 0.001, ... 'sustain', go_cue_duration, 'sound_trig', go_sound_id); % to play the Go Cue/Reward Sound - else - sma = add_scheduled_wave(sma, 'name', 'Go_Cue', 'preamble', 0.001, ... - 'sustain', go_cue_duration); % to play the Go Cue/Reward Sound - end + % else + % sma = add_scheduled_wave(sma, 'name', 'Go_Cue', 'preamble', 0.001, ... + % 'sustain', go_cue_duration); % to play the Go Cue/Reward Sound + % end % Scheduled wave for CP Duration if CP_duration <= (SettlingIn_time + legal_cbreak) @@ -219,8 +221,8 @@ else % no reward but a punishment iti sma = add_state(sma,'name','second_hit_state','self_timer',error_iti,... 'input_to_statechange',{'reward_collection_dur_In', 'timeout_state'; 'Tup','preclean_up_state'}); - sma = add_state(sma, 'name', 'hit_state'); - sma = add_state(sma, 'name', 'drink_state'); + % sma = add_state(sma, 'name', 'hit_state'); + % sma = add_state(sma, 'name', 'drink_state'); end diff --git a/Protocols/@ArpitSoundCatContinuous/SideSection.m b/Protocols/@ArpitSoundCatContinuous/SideSection.m index 4795fb82..cabb28e1 100644 --- a/Protocols/@ArpitSoundCatContinuous/SideSection.m +++ b/Protocols/@ArpitSoundCatContinuous/SideSection.m @@ -208,7 +208,7 @@ 'reward_type';'secondhit_delay';'error_iti';'violation_iti'}); SoloFunctionAddVars('StimulusSection', 'ro_args', ... - {'ThisTrial';'A1_time';'time_bet_aud1_gocue' ; ... + {'ThisTrial';'stimuli_on';'A1_time';'time_bet_aud1_gocue' ; ... 'PreStim_time'}); SoloFunctionAddVars('StimulatorSection', 'ro_args', ... @@ -490,8 +490,8 @@ % assym=0.7; % left_wtr_mult.value=maxasymp + (minasymp./(1+(n_done_trials/inflp).^slp).^assym); % right_wtr_mult.value=maxasymp + (minasymp./(1+(n_done_trials/inflp).^slp).^assym); - x=left_wtr_mult+0; - y=right_wtr_mult+0; + x=value(left_wtr_mult); + y=value(right_wtr_mult); case 'get_water_amount' @@ -502,7 +502,9 @@ WValveTimes = GetValveTimes(WaterAmount, [2 3]); LeftWValveTime = WValveTimes(1); RightWValveTime = WValveTimes(2); - [LeftWMult, RightWMult] = ParamsSection(obj, 'get_water_mult'); + % [LeftWMult, RightWMult] = ParamsSection(obj, 'get_water_mult'); + LeftWMult=value(left_wtr_mult); + RightWMult=value(right_wtr_mult); x=LeftWValveTime*LeftWMult; y=RightWValveTime*RightWMult; diff --git a/Protocols/@ArpitSoundCatContinuous/StimulusSection.m b/Protocols/@ArpitSoundCatContinuous/StimulusSection.m index 6ac08a0d..cde040fd 100644 --- a/Protocols/@ArpitSoundCatContinuous/StimulusSection.m +++ b/Protocols/@ArpitSoundCatContinuous/StimulusSection.m @@ -147,7 +147,7 @@ figure(parentfig); case 'prepare_next_trial' - if value(training_stage) > 4 && stimuli_on + if stimuli_on StimulusSection(obj,'pick_current_stimulus'); srate=SoundManagerSection(obj,'get_sample_rate'); Fs=srate; @@ -210,16 +210,16 @@ stim_max_log = log(value(maxS1)); end - if value(training_stage) == 4 % playing fixed sound during this stage - if (strcmpi(ThisTrial, 'LEFT') & strcmp(Rule,'S1>S_boundary Left')) | ... - (strcmpi(ThisTrial, 'RIGHT') & strcmp(Rule,'S1>S_boundary Right')) - - stim_i_log = stim_min_log; - else - stim_i_log = stim_max_log; - end - - else % will be playing stimuli from the distribution + % if value(training_stage) == 4 % playing fixed sound during this stage + % if (strcmpi(ThisTrial, 'LEFT') & strcmp(Rule,'S1>S_boundary Left')) | ... + % (strcmpi(ThisTrial, 'RIGHT') & strcmp(Rule,'S1>S_boundary Right')) + % + % stim_i_log = stim_min_log; + % else + % stim_i_log = stim_max_log; + % end + % + % else % will be playing stimuli from the distribution if strcmpi(ThisTrial, 'LEFT') dist_type = value(Prob_Dist_Left); @@ -309,7 +309,7 @@ stim_i_log = CreateSamples_from_Distribution('Sinusoidal',dist_mean,dist_sigma,edge_min,edge_max,1); end - end + % end thisstim.value=exp(stim_i_log); thisstimlog(n_done_trials+1) = stim_i_log; diff --git a/Protocols/sendsummary_error_log.txt b/Protocols/sendsummary_error_log.txt new file mode 100644 index 00000000..49bba33d --- /dev/null +++ b/Protocols/sendsummary_error_log.txt @@ -0,0 +1,655 @@ +First time connecting with bdata server since matlab start. +Matlab<->MySQL connector v1.36 +Current database is "akrami_db" +connected +54 left_pokes = 0; +Index: 4, Size: 4 +java.lang.IndexOutOfBoundsException: Index: 4, Size: 4 + at java.util.ArrayList.rangeCheck(ArrayList.java:657) + at java.util.ArrayList.get(ArrayList.java:433) + at java.awt.Container.createHierarchyEvents(Container.java:1444) + at java.awt.Container.createHierarchyEvents(Container.java:1444) + at java.awt.Container.createHierarchyEvents(Container.java:1444) + at java.awt.Container.createHierarchyEvents(Container.java:1444) + at java.awt.Container.createHierarchyEvents(Container.java:1444) + at java.awt.Component.show(Component.java:1639) + at java.awt.Component.show(Component.java:1671) + at java.awt.Component.setVisible(Component.java:1623) + at javax.swing.JComponent.setVisible(JComponent.java:2644) + at javax.swing.JTabbedPane.fireStateChanged(JTabbedPane.java:394) + at javax.swing.JTabbedPane$ModelListener.stateChanged(JTabbedPane.java:270) + at javax.swing.DefaultSingleSelectionModel.fireStateChanged(DefaultSingleSelectionModel.java:132) + at javax.swing.DefaultSingleSelectionModel.setSelectedIndex(DefaultSingleSelectionModel.java:67) + at javax.swing.JTabbedPane.setSelectedIndexImpl(JTabbedPane.java:616) + at javax.swing.JTabbedPane.setSelectedIndex(JTabbedPane.java:591) + at com.mathworks.widgets.desk.DTTabbedPane.setSelectedIndex(DTTabbedPane.java:415) + at com.mathworks.widgets.desk.DTTabbedPane.toFront(DTTabbedPane.java:362) + at com.mathworks.widgets.desk.DTNestingContainer.toFront(DTNestingContainer.java:362) + at com.mathworks.widgets.desk.DTMultipleClientFrame.moveToFront(DTMultipleClientFrame.java:718) + at com.mathworks.widgets.desk.Desktop.toFront(Desktop.java:1354) + at sun.reflect.GeneratedMethodAccessor203.invoke(Unknown Source) + at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) + at java.lang.reflect.Method.invoke(Method.java:498) + at com.mathworks.widgets.desk.Desktop$DeferredFacadeProxy.invoke(Desktop.java:8564) + at com.sun.proxy.$Proxy6.toFront(Unknown Source) + at com.mathworks.widgets.desk.Desktop.toFront(Desktop.java:1247) + at com.mathworks.mde.editor.DebugFileOpeningService.doOpenEditorForDebug(DebugFileOpeningService.java:224) + at com.mathworks.mde.editor.DebugFileOpeningService.access$200(DebugFileOpeningService.java:42) + at com.mathworks.mde.editor.DebugFileOpeningService$3.run(DebugFileOpeningService.java:170) + at java.awt.event.InvocationEvent.dispatch(InvocationEvent.java:311) + at java.awt.EventQueue.dispatchEventImpl(EventQueue.java:758) + at java.awt.EventQueue.access$500(EventQueue.java:97) + at java.awt.EventQueue$3.run(EventQueue.java:709) + at java.awt.EventQueue$3.run(EventQueue.java:703) + at java.security.AccessController.doPrivileged(Native Method) + at java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:74) + at java.awt.EventQueue.dispatchEvent(EventQueue.java:728) + at java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:205) + at java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:116) + at java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:105) + at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:101) + at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:93) + at java.awt.EventDispatchThread.run(EventDispatchThread.java:82) +Exception in thread "AWT-EventQueue-0": java.lang.IndexOutOfBoundsException: Index: 4, Size: 4 + at java.util.ArrayList.rangeCheck(ArrayList.java:657) + at java.util.ArrayList.get(ArrayList.java:433) + at java.awt.Container.createHierarchyEvents(Container.java:1444) + at java.awt.Container.createHierarchyEvents(Container.java:1444) + at java.awt.Container.createHierarchyEvents(Container.java:1444) + at java.awt.Container.createHierarchyEvents(Container.java:1444) + at java.awt.Container.createHierarchyEvents(Container.java:1444) + at java.awt.Component.show(Component.java:1639) + at java.awt.Component.show(Component.java:1671) + at java.awt.Component.setVisible(Component.java:1623) + at javax.swing.JComponent.setVisible(JComponent.java:2644) + at javax.swing.JTabbedPane.fireStateChanged(JTabbedPane.java:394) + at javax.swing.JTabbedPane$ModelListener.stateChanged(JTabbedPane.java:270) + at javax.swing.DefaultSingleSelectionModel.fireStateChanged(DefaultSingleSelectionModel.java:132) + at javax.swing.DefaultSingleSelectionModel.setSelectedIndex(DefaultSingleSelectionModel.java:67) + at javax.swing.JTabbedPane.setSelectedIndexImpl(JTabbedPane.java:616) + at javax.swing.JTabbedPane.setSelectedIndex(JTabbedPane.java:591) + at com.mathworks.widgets.desk.DTTabbedPane.setSelectedIndex(DTTabbedPane.java:415) + at com.mathworks.widgets.desk.DTTabbedPane.toFront(DTTabbedPane.java:362) + at com.mathworks.widgets.desk.DTNestingContainer.toFront(DTNestingContainer.java:362) + at com.mathworks.widgets.desk.DTMultipleClientFrame.moveToFront(DTMultipleClientFrame.java:718) + at com.mathworks.widgets.desk.Desktop.toFront(Desktop.java:1354) + at sun.reflect.GeneratedMethodAccessor203.invoke(Unknown Source) + at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) + at java.lang.reflect.Method.invoke(Method.java:498) + at com.mathworks.widgets.desk.Desktop$DeferredFacadeProxy.invoke(Desktop.java:8564) + at com.sun.proxy.$Proxy6.toFront(Unknown Source) + at com.mathworks.widgets.desk.Desktop.toFront(Desktop.java:1247) + at com.mathworks.mde.editor.DebugFileOpeningService.doOpenEditorForDebug(DebugFileOpeningService.java:224) + at com.mathworks.mde.editor.DebugFileOpeningService.access$200(DebugFileOpeningService.java:42) + at com.mathworks.mde.editor.DebugFileOpeningService$3.run(DebugFileOpeningService.java:170) + at java.awt.event.InvocationEvent.dispatch(InvocationEvent.java:311) + at java.awt.EventQueue.dispatchEventImpl(EventQueue.java:758) + at java.awt.EventQueue.access$500(EventQueue.java:97) + at java.awt.EventQueue$3.run(EventQueue.java:709) + at java.awt.EventQueue$3.run(EventQueue.java:703) + at java.security.AccessController.doPrivileged(Native Method) + at java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:74) + at java.awt.EventQueue.dispatchEvent(EventQueue.java:728) + at java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:205) + at java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:116) + at java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:105) + at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:101) + at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:93) + at java.awt.EventDispatchThread.run(EventDispatchThread.java:82) +if system_dependent('IsDebugMode')==1, dbquit; end + +MATLAB:ErrorRecovery:ItemNoLongerOnPath +Error using load +..\PASSWORD_CONFIG-DO_NOT_VERSIONCONTROL.mat is not found in the current folder or on the MATLAB path, but exists in: + C:\ratter\ExperPort + C:\ratter\Protocols + C:\ratter\Bpod Protocols + C:\ratter\SoloData + +Change the MATLAB current folder or add its folder to the MATLAB path. +On line 17 of C:\ratter\ExperPort\MySQLUtility\bdata.m +On line 7 of C:\ratter\ExperPort\MySQLUtility\get_network_info.m +On line 18 of C:\ratter\ExperPort\Plugins\@sqlsummary\CentrePoketrainingsummary.m +On line 317 of C:\ratter\Protocols\@ArpitCentrePokeTraining\ArpitCentrePokeTraining.m +On line 96 of C:\ratter\ExperPort\Modules\@dispatcher\ProtocolsSection.m +On line 40 of C:\ratter\ExperPort\HandleParam\parse_and_call_sph_callback.m +On line 107 of C:\ratter\ExperPort\HandleParam\@SoloParamHandle\callback.m +On line 165 of C:\ratter\ExperPort\Modules\@dispatcher\ProtocolsSection.m +On line 210 of C:\ratter\ExperPort\Modules\@dispatcher\dispatcher.m +Error calculating session length +Index exceeds the number of array elements. Index must not exceed 0. + 7×1 struct array with fields: + + file + name + line + +Failed to send summary to sql +Output argument "y" (and possibly others) not assigned a value in the execution with "sqlsummary.CentrePoketrainingsummary>sess_length" function. + 6×1 struct array with fields: + + file + name + line + +Error occurred during sendsummary execution: +Output argument "y" (and possibly others) not assigned a value in the execution with "sqlsummary.CentrePoketrainingsummary>sess_length" function. + + +******* + + WARNING: Unable to complete 'close' action on protocol + Last error was: "Error using fprintf +Function is not defined for 'struct' inputs." + + + +******* + +Warning: B-data NOT enabled for liquid calibration. No calibration values received. + +MATLAB:cd:NonExistentFolder +Error using cd +Unable to change current folder to 'C:\ratter\Protocols\Sounds' (Name is nonexistent or not a folder). +On line 13 of C:\ratter\ExperPort\Plugins\@soundui\private\fillSoundsMenu.m +On line 173 of C:\ratter\ExperPort\Plugins\@soundui\SoundInterface.m +On line 41 of C:\ratter\Protocols\@ArpitCentrePokeTraining\SoundSection.m +On line 179 of C:\ratter\Protocols\@ArpitCentrePokeTraining\ArpitCentrePokeTraining.m +On line 152 of C:\ratter\ExperPort\Modules\@dispatcher\ProtocolsSection.m +On line 40 of C:\ratter\ExperPort\HandleParam\parse_and_call_sph_callback.m +On line 107 of C:\ratter\ExperPort\HandleParam\@SoloParamHandle\callback.m +On line 5 of C:\ratter\ExperPort\HandleParam\generic_callback.m +Error calculating session length +Index exceeds the number of array elements. Index must not exceed 0. + 8×1 struct array with fields: + + file + name + line + +Failed to send summary to sql +Output argument "y" (and possibly others) not assigned a value in the execution with "sqlsummary.CentrePoketrainingsummary>sess_length" function. + 7×1 struct array with fields: + + file + name + line + +Error occurred during sendsummary execution: +Output argument "y" (and possibly others) not assigned a value in the execution with "sqlsummary.CentrePoketrainingsummary>sess_length" function. + + +******* + + WARNING: Unable to complete 'close' action on protocol + Last error was: "Error using fprintf +Function is not defined for 'struct' inputs." + + + +******* + + +ans = + + dispatcher object: 1-by-1 + +clc +Warning: B-data NOT enabled for liquid calibration. No calibration values received. +64 sessid = getSessID(obj); +if system_dependent('IsDebugMode')==1, dbstep; end +if system_dependent('IsDebugMode')==1, dbstep; end +if system_dependent('IsDebugMode')==1, dbstep; end +if system_dependent('IsDebugMode')==1, dbstep; end + +MATLAB:cd:NonExistentFolder +Error using cd +Unable to change current folder to 'C:\ratter\Protocols\Sounds' (Name is nonexistent or not a folder). +On line 13 of C:\ratter\ExperPort\Plugins\@soundui\private\fillSoundsMenu.m +On line 173 of C:\ratter\ExperPort\Plugins\@soundui\SoundInterface.m +On line 41 of C:\ratter\Protocols\@ArpitCentrePokeTraining\SoundSection.m +On line 179 of C:\ratter\Protocols\@ArpitCentrePokeTraining\ArpitCentrePokeTraining.m +On line 152 of C:\ratter\ExperPort\Modules\@dispatcher\ProtocolsSection.m +On line 40 of C:\ratter\ExperPort\HandleParam\parse_and_call_sph_callback.m +On line 107 of C:\ratter\ExperPort\HandleParam\@SoloParamHandle\callback.m +On line 5 of C:\ratter\ExperPort\HandleParam\generic_callback.m +Error calculating session length +Index exceeds the number of array elements. Index must not exceed 0. + 8×1 struct array with fields: + + file + name + line + +if system_dependent('IsDebugMode')==1, dbquit; end +64 sessid = getSessID(obj); +if system_dependent('IsDebugMode')==1, dbcont; end +Failed to send summary to sql +Dimensions of arrays being concatenated are not consistent. + 6×1 struct array with fields: + + file + name + line + +Error occurred during sendsummary execution: +Dimensions of arrays being concatenated are not consistent. + + +******* + + WARNING: Unable to complete 'close' action on protocol + Last error was: "Error using fprintf +Function is not defined for 'struct' inputs." + + + +******* + +Warning: B-data NOT enabled for liquid calibration. No calibration values received. +64 sessid = getSessID(obj); +if system_dependent('IsDebugMode')==1, dbcont; end +Failed to send summary to sql +Dimensions of arrays being concatenated are not consistent. + 7×1 struct array with fields: + + file + name + line + +Error occurred during sendsummary execution: +Dimensions of arrays being concatenated are not consistent. + + +******* + + WARNING: Unable to complete 'close' action on protocol + Last error was: "Error using fprintf +Function is not defined for 'struct' inputs." + + + +******* + + +ans = + + dispatcher object: 1-by-1 + + +ans = + + dispatcher object: 1-by-1 + + +ans = + + dispatcher object: 1-by-1 + +Warning: B-data NOT enabled for liquid calibration. No calibration values received. +76 colstr = [ +if system_dependent('IsDebugMode')==1, dbstep; end +if system_dependent('IsDebugMode')==1, dbstep; end +if system_dependent('IsDebugMode')==1, dbquit; end +clc +143 valstr = [ +if system_dependent('IsDebugMode')==1, dbcont; end +if system_dependent('IsDebugMode')==1, dbquit; end +!git status +On branch 2_step_soundcat_arpit +Your branch is up to date with 'origin/2_step_soundcat_arpit'. + +Changes not staged for commit: + (use "git add/rm ..." to update what will be committed) + (use "git restore ..." to discard changes in working directory) + deleted: ../ExperPort/Plugins/@sqlsummary/CentrePoketrainingsummary.asv + +Untracked files: + (use "git add ..." to include in what will be committed) + ../ExperPort/settings_@ArpitCentrePokeTraining_arpit_AR01_250506a.mat + ../ExperPort/settings_@ArpitCentrePokeTraining_arpit_AR01_250507a.mat + ../ExperPort/settings_@ArpitCentrePokeTraining_arpit_AR02_250506a.mat + ../ExperPort/settings_@ArpitCentrePokeTraining_arpit_AR02_250507a.mat + ../ExperPort/settings_@ArpitCentrePokeTraining_arpit_AR03_250506a.mat + ../ExperPort/settings_@ArpitCentrePokeTraining_arpit_AR03_250507a.mat + ../ExperPort/settings_@ArpitCentrePokeTraining_arpit_AR04_250506a.mat + ../ExperPort/settings_@ArpitCentrePokeTraining_arpit_AR04_250507a.mat + ../ExperPort/settings_@ArpitCentrePokeTraining_experimenter_ratname_250506a.mat + ../ExperPort/settings_@ArpitCentrePokeTraining_experimenter_ratname_250508a.mat + sendsummary_error_log.txt + ../sendsummary_error_log.txt + +no changes added to commit (use "git add" and/or "git commit -a") +flush +Bpod successfully disconnected. +!git status +On branch 2_step_soundcat_arpit +Your branch is up to date with 'origin/2_step_soundcat_arpit'. + +Changes not staged for commit: + (use "git add/rm ..." to update what will be committed) + (use "git restore ..." to discard changes in working directory) + deleted: ../ExperPort/Plugins/@sqlsummary/CentrePoketrainingsummary.asv + +Untracked files: + (use "git add ..." to include in what will be committed) + ../ExperPort/settings_@ArpitCentrePokeTraining_arpit_AR01_250506a.mat + ../ExperPort/settings_@ArpitCentrePokeTraining_arpit_AR01_250507a.mat + ../ExperPort/settings_@ArpitCentrePokeTraining_arpit_AR02_250506a.mat + ../ExperPort/settings_@ArpitCentrePokeTraining_arpit_AR02_250507a.mat + ../ExperPort/settings_@ArpitCentrePokeTraining_arpit_AR03_250506a.mat + ../ExperPort/settings_@ArpitCentrePokeTraining_arpit_AR03_250507a.mat + ../ExperPort/settings_@ArpitCentrePokeTraining_arpit_AR04_250506a.mat + ../ExperPort/settings_@ArpitCentrePokeTraining_arpit_AR04_250507a.mat + ../ExperPort/settings_@ArpitCentrePokeTraining_experimenter_ratname_250506a.mat + ../ExperPort/settings_@ArpitCentrePokeTraining_experimenter_ratname_250508a.mat + sendsummary_error_log.txt + ../sendsummary_error_log.txt + +no changes added to commit (use "git add" and/or "git commit -a") +!git pull +From https://github.com/LIMLabSWC/ratter + ec159f7..c79aaeb 2_step_soundcat_arpit -> origin/2_step_soundcat_arpit +Updating ec159f7..c79aaeb +Fast-forward + .../@sqlsummary/CentrePoketrainingsummary.asv | 315 --------------------- + .../@sqlsummary/CentrePoketrainingsummary.m | 311 +++++++++++--------- + .../ArpitCentrePokeTraining.m | 2 +- + Protocols/@ArpitCentrePokeTraining/ParamsSection.m | 2 +- + 4 files changed, 177 insertions(+), 453 deletions(-) + delete mode 100644 ExperPort/Plugins/@sqlsummary/CentrePoketrainingsummary.asv +Bpod('COM9'); newstartup; +Starting Bpod Console v1.8.0 +*********************************************************** +UPDATE NOTICE: Bpod Console v'1.8.1 is available to download! + View release notes here +To update run UpdateBpodSoftware() OR see instructionshere +*********************************************************** +Bpod State Machine r2 connected on port COM9 +runrats('init'); +test +First time connecting with bdata server since matlab start. +Matlab<->MySQL connector v1.36 +Current database is "akrami_db" +connected +[Warning: Using 'twister' to set RAND's internal state causes RAND, RANDI, and RANDN to use legacy +random number generators. This syntax is not recommended. See Replace Discouraged Syntaxes of rand +and randn to use RNG to replace the old syntax.] +[> In dispatcher (line 121) +In runrats (line 97)] +############################################################ +ALERT! No WavePlayer module detected! +Protocols will error out if analog scheduled waves are used. +############################################################ +WARNING: Failed to keep runrats on top +ERROR in bdata: mym(CON_ID,'call update_riginfo("{S}","{S}","{S}","{S}","{S}","{S}","{S}")',vs{1}, vs{2} , vs{3} , vs{4} , vs{5} , vs{6} , vs{7} ); + + +Error using mym +PROCEDURE akrami_db.update_riginfo does not exist +On line 158 of C:\ratter\ExperPort\MySQLUtility\bdata.m +On line 19 of C:\ratter\ExperPort\Modules\@runrats\private\update_riginfo.m +On line 286 of C:\ratter\ExperPort\Modules\@runrats\runrats.m +Rig Info updated successfully. +Rig 31: IP=172.24.155.31, MAC=F875A4DEBBD8, Hostname=AKRAMI-RIG031 +Updating Current Session +ERROR in bdata: mym(CON_ID,'call update_rigtrials("{Si}","{Si}","{S}","{S}")',vs{1}, vs{2} , vs{3} , vs{4} ); + + +Error using mym +Incorrect number of arguments for PROCEDURE akrami_db.update_rigtrials; expected 0, got 4 +On line 158 of C:\ratter\ExperPort\MySQLUtility\bdata.m +On line 40 of C:\ratter\ExperPort\Plugins\@sqlsummary\send_n_done_trials.m +On line 1253 of C:\ratter\ExperPort\Modules\@runrats\runrats.m +On line 1244 of C:\ratter\ExperPort\Modules\@runrats\runrats.m +On line 1195 of C:\ratter\ExperPort\Modules\@runrats\runrats.m +On line 40 of C:\ratter\ExperPort\HandleParam\parse_and_call_sph_callback.m +On line 107 of C:\ratter\ExperPort\HandleParam\@SoloParamHandle\callback.m +On line 5 of C:\ratter\ExperPort\HandleParam\generic_callback.m + +MATLAB:minrhs +Not enough input arguments. +On line 61 of C:\Program Files\MATLAB\R2024a\toolbox\matlab\codetools\export.m +On line 155 of C:\ratter\ExperPort\Plugins\@pokesplot\pokesplot_preferences_pane.m +On line 173 of C:\ratter\ExperPort\Plugins\@pokesplot\PokesPlotSection.m +On line 168 of C:\ratter\Protocols\@Rigtest_singletrial\Rigtest_singletrial.m +On line 152 of C:\ratter\ExperPort\Modules\@dispatcher\ProtocolsSection.m +On line 40 of C:\ratter\ExperPort\HandleParam\parse_and_call_sph_callback.m +On line 107 of C:\ratter\ExperPort\HandleParam\@SoloParamHandle\callback.m +On line 184 of C:\ratter\ExperPort\Modules\@dispatcher\ProtocolsSection.m +On line 211 of C:\ratter\ExperPort\Modules\@dispatcher\dispatcher.m +On line 1266 of C:\ratter\ExperPort\Modules\@runrats\runrats.m +On line 1244 of C:\ratter\ExperPort\Modules\@runrats\runrats.m +On line 1195 of C:\ratter\ExperPort\Modules\@runrats\runrats.m +On line 40 of C:\ratter\ExperPort\HandleParam\parse_and_call_sph_callback.m +On line 107 of C:\ratter\ExperPort\HandleParam\@SoloParamHandle\callback.m +On line 5 of C:\ratter\ExperPort\HandleParam\generic_callback.m +Flagged stopping process as complete. +Warning: B-data NOT enabled for liquid calibration. No calibration values received. +[Warning: You created a new handle ("TrainingStageParamsSection_last_session_CP") since these +settings +were saved, no setting for it can be loaded.] +[> In loading_set_soloparamhandle_values (line 50) +In loading_core_load_sequence (line 35) +In load_solouiparamvalues (line 192) +In runrats (line 1386) +In runrats (line 1302) +In timer/timercb (line 71) +In matlab.graphics.interaction.uiaxes/EditInteraction/figureMotionFcn +In matlab.graphics.interaction.uiaxes.EditInteraction>@(~,e)this.figureMotionFcn(e)] +[Warning: You created a new handle ("TrainingStageParamsSection_max_CP") since these settings +were saved, no setting for it can be loaded.] +[> In loading_set_soloparamhandle_values (line 50) +In loading_core_load_sequence (line 35) +In load_solouiparamvalues (line 192) +In runrats (line 1386) +In runrats (line 1302) +In timer/timercb (line 71) +In matlab.graphics.interaction.uiaxes/EditInteraction/figureMotionFcn +In matlab.graphics.interaction.uiaxes.EditInteraction>@(~,e)this.figureMotionFcn(e)] +[Warning: You created a new handle ("TrainingStageParamsSection_CPfraction_inc") since these settings +were saved, no setting for it can be loaded.] +[> In loading_set_soloparamhandle_values (line 50) +In loading_core_load_sequence (line 35) +In load_solouiparamvalues (line 192) +In runrats (line 1386) +In runrats (line 1302) +In timer/timercb (line 71) +In matlab.graphics.interaction.uiaxes/EditInteraction/figureMotionFcn +In matlab.graphics.interaction.uiaxes.EditInteraction>@(~,e)this.figureMotionFcn(e)] +[Warning: You created a new handle ("TrainingStageParamsSection_recent_violation") since these +settings +were saved, no setting for it can be loaded.] +[> In loading_set_soloparamhandle_values (line 50) +In loading_core_load_sequence (line 35) +In load_solouiparamvalues (line 192) +In runrats (line 1386) +In runrats (line 1302) +In timer/timercb (line 71) +In matlab.graphics.interaction.uiaxes/EditInteraction/figureMotionFcn +In matlab.graphics.interaction.uiaxes.EditInteraction>@(~,e)this.figureMotionFcn(e)] +[Warning: You created a new handle ("TrainingStageParamsSection_recent_timeout") since these settings +were saved, no setting for it can be loaded.] +[> In loading_set_soloparamhandle_values (line 50) +In loading_core_load_sequence (line 35) +In load_solouiparamvalues (line 192) +In runrats (line 1386) +In runrats (line 1302) +In timer/timercb (line 71) +In matlab.graphics.interaction.uiaxes/EditInteraction/figureMotionFcn +In matlab.graphics.interaction.uiaxes.EditInteraction>@(~,e)this.figureMotionFcn(e)] +[Warning: You created a new handle ("TrainingStageParamsSection_stage_violation") since these +settings +were saved, no setting for it can be loaded.] +[> In loading_set_soloparamhandle_values (line 50) +In loading_core_load_sequence (line 35) +In load_solouiparamvalues (line 192) +In runrats (line 1386) +In runrats (line 1302) +In timer/timercb (line 71) +In matlab.graphics.interaction.uiaxes/EditInteraction/figureMotionFcn +In matlab.graphics.interaction.uiaxes.EditInteraction>@(~,e)this.figureMotionFcn(e)] + +MATLAB:UndefinedFunction +Undefined function 'callback' for input arguments of type 'struct'. +On line 80 of C:\ratter\SoloData\Settings\experimenter\ratname\pipeline_ArpitCentrePokeTraining_experimenter_ratname_250508a.m +On line 2034 of C:\ratter\ExperPort\Plugins\@sessionmodel2\SessionDefinition.m +On line 264 of C:\ratter\Protocols\@ArpitCentrePokeTraining\ArpitCentrePokeTraining.m +On line 539 of C:\ratter\ExperPort\Modules\@dispatcher\RunningSection.m +On line 314 of C:\ratter\ExperPort\Modules\@dispatcher\RunningSection.m +On line 278 of C:\ratter\ExperPort\Modules\@dispatcher\RunningSection.m +On line 150 of C:\ratter\ExperPort\Modules\@dispatcher\RunningSection.m +On line 202 of C:\ratter\ExperPort\Modules\@dispatcher\dispatcher.m +On line 1501 of C:\ratter\ExperPort\Modules\@runrats\runrats.m +On line 1198 of C:\ratter\ExperPort\Modules\@runrats\runrats.m +On line 40 of C:\ratter\ExperPort\HandleParam\parse_and_call_sph_callback.m +On line 107 of C:\ratter\ExperPort\HandleParam\@SoloParamHandle\callback.m +On line 5 of C:\ratter\ExperPort\HandleParam\generic_callback.m + +MATLAB:UndefinedFunction +Undefined function 'callback' for input arguments of type 'struct'. +On line 80 of C:\ratter\SoloData\Settings\experimenter\ratname\pipeline_ArpitCentrePokeTraining_experimenter_ratname_250508a.m +On line 2034 of C:\ratter\ExperPort\Plugins\@sessionmodel2\SessionDefinition.m +On line 264 of C:\ratter\Protocols\@ArpitCentrePokeTraining\ArpitCentrePokeTraining.m +On line 539 of C:\ratter\ExperPort\Modules\@dispatcher\RunningSection.m +On line 314 of C:\ratter\ExperPort\Modules\@dispatcher\RunningSection.m +On line 278 of C:\ratter\ExperPort\Modules\@dispatcher\RunningSection.m +On line 150 of C:\ratter\ExperPort\Modules\@dispatcher\RunningSection.m +On line 202 of C:\ratter\ExperPort\Modules\@dispatcher\dispatcher.m +On line 1501 of C:\ratter\ExperPort\Modules\@runrats\runrats.m +On line 1198 of C:\ratter\ExperPort\Modules\@runrats\runrats.m +On line 40 of C:\ratter\ExperPort\HandleParam\parse_and_call_sph_callback.m +On line 107 of C:\ratter\ExperPort\HandleParam\@SoloParamHandle\callback.m +On line 5 of C:\ratter\ExperPort\HandleParam\generic_callback.m + +MATLAB:UndefinedFunction +Undefined function 'callback' for input arguments of type 'struct'. +On line 80 of C:\ratter\SoloData\Settings\experimenter\ratname\pipeline_ArpitCentrePokeTraining_experimenter_ratname_250508a.m +On line 2034 of C:\ratter\ExperPort\Plugins\@sessionmodel2\SessionDefinition.m +On line 264 of C:\ratter\Protocols\@ArpitCentrePokeTraining\ArpitCentrePokeTraining.m +On line 539 of C:\ratter\ExperPort\Modules\@dispatcher\RunningSection.m +On line 314 of C:\ratter\ExperPort\Modules\@dispatcher\RunningSection.m +On line 278 of C:\ratter\ExperPort\Modules\@dispatcher\RunningSection.m +On line 150 of C:\ratter\ExperPort\Modules\@dispatcher\RunningSection.m +On line 202 of C:\ratter\ExperPort\Modules\@dispatcher\dispatcher.m +On line 1501 of C:\ratter\ExperPort\Modules\@runrats\runrats.m +On line 1198 of C:\ratter\ExperPort\Modules\@runrats\runrats.m +On line 40 of C:\ratter\ExperPort\HandleParam\parse_and_call_sph_callback.m +On line 107 of C:\ratter\ExperPort\HandleParam\@SoloParamHandle\callback.m +On line 5 of C:\ratter\ExperPort\HandleParam\generic_callback.m + +MATLAB:UndefinedFunction +Undefined function 'callback' for input arguments of type 'struct'. +On line 80 of C:\ratter\SoloData\Settings\experimenter\ratname\pipeline_ArpitCentrePokeTraining_experimenter_ratname_250508a.m +On line 2034 of C:\ratter\ExperPort\Plugins\@sessionmodel2\SessionDefinition.m +On line 264 of C:\ratter\Protocols\@ArpitCentrePokeTraining\ArpitCentrePokeTraining.m +On line 539 of C:\ratter\ExperPort\Modules\@dispatcher\RunningSection.m +On line 314 of C:\ratter\ExperPort\Modules\@dispatcher\RunningSection.m +On line 278 of C:\ratter\ExperPort\Modules\@dispatcher\RunningSection.m +On line 150 of C:\ratter\ExperPort\Modules\@dispatcher\RunningSection.m +On line 202 of C:\ratter\ExperPort\Modules\@dispatcher\dispatcher.m +On line 1501 of C:\ratter\ExperPort\Modules\@runrats\runrats.m +On line 1198 of C:\ratter\ExperPort\Modules\@runrats\runrats.m +On line 40 of C:\ratter\ExperPort\HandleParam\parse_and_call_sph_callback.m +On line 107 of C:\ratter\ExperPort\HandleParam\@SoloParamHandle\callback.m +On line 5 of C:\ratter\ExperPort\HandleParam\generic_callback.m + +MATLAB:UndefinedFunction +Undefined function 'callback' for input arguments of type 'struct'. +On line 80 of C:\ratter\SoloData\Settings\experimenter\ratname\pipeline_ArpitCentrePokeTraining_experimenter_ratname_250508a.m +On line 2034 of C:\ratter\ExperPort\Plugins\@sessionmodel2\SessionDefinition.m +On line 264 of C:\ratter\Protocols\@ArpitCentrePokeTraining\ArpitCentrePokeTraining.m +On line 539 of C:\ratter\ExperPort\Modules\@dispatcher\RunningSection.m +On line 314 of C:\ratter\ExperPort\Modules\@dispatcher\RunningSection.m +On line 278 of C:\ratter\ExperPort\Modules\@dispatcher\RunningSection.m +On line 150 of C:\ratter\ExperPort\Modules\@dispatcher\RunningSection.m +On line 202 of C:\ratter\ExperPort\Modules\@dispatcher\dispatcher.m +On line 1501 of C:\ratter\ExperPort\Modules\@runrats\runrats.m +On line 1198 of C:\ratter\ExperPort\Modules\@runrats\runrats.m +On line 40 of C:\ratter\ExperPort\HandleParam\parse_and_call_sph_callback.m +On line 107 of C:\ratter\ExperPort\HandleParam\@SoloParamHandle\callback.m +On line 5 of C:\ratter\ExperPort\HandleParam\generic_callback.m + +MATLAB:UndefinedFunction +Undefined function 'callback' for input arguments of type 'struct'. +On line 80 of C:\ratter\SoloData\Settings\experimenter\ratname\pipeline_ArpitCentrePokeTraining_experimenter_ratname_250508a.m +On line 2034 of C:\ratter\ExperPort\Plugins\@sessionmodel2\SessionDefinition.m +On line 264 of C:\ratter\Protocols\@ArpitCentrePokeTraining\ArpitCentrePokeTraining.m +On line 539 of C:\ratter\ExperPort\Modules\@dispatcher\RunningSection.m +On line 314 of C:\ratter\ExperPort\Modules\@dispatcher\RunningSection.m +On line 278 of C:\ratter\ExperPort\Modules\@dispatcher\RunningSection.m +On line 150 of C:\ratter\ExperPort\Modules\@dispatcher\RunningSection.m +On line 202 of C:\ratter\ExperPort\Modules\@dispatcher\dispatcher.m +On line 1501 of C:\ratter\ExperPort\Modules\@runrats\runrats.m +On line 1198 of C:\ratter\ExperPort\Modules\@runrats\runrats.m +On line 40 of C:\ratter\ExperPort\HandleParam\parse_and_call_sph_callback.m +On line 107 of C:\ratter\ExperPort\HandleParam\@SoloParamHandle\callback.m +On line 5 of C:\ratter\ExperPort\HandleParam\generic_callback.m + +MATLAB:UndefinedFunction +Undefined function 'callback' for input arguments of type 'struct'. +On line 80 of C:\ratter\SoloData\Settings\experimenter\ratname\pipeline_ArpitCentrePokeTraining_experimenter_ratname_250508a.m +On line 2034 of C:\ratter\ExperPort\Plugins\@sessionmodel2\SessionDefinition.m +On line 264 of C:\ratter\Protocols\@ArpitCentrePokeTraining\ArpitCentrePokeTraining.m +On line 539 of C:\ratter\ExperPort\Modules\@dispatcher\RunningSection.m +On line 314 of C:\ratter\ExperPort\Modules\@dispatcher\RunningSection.m +On line 278 of C:\ratter\ExperPort\Modules\@dispatcher\RunningSection.m +On line 150 of C:\ratter\ExperPort\Modules\@dispatcher\RunningSection.m +On line 202 of C:\ratter\ExperPort\Modules\@dispatcher\dispatcher.m +On line 1501 of C:\ratter\ExperPort\Modules\@runrats\runrats.m +On line 1198 of C:\ratter\ExperPort\Modules\@runrats\runrats.m +On line 40 of C:\ratter\ExperPort\HandleParam\parse_and_call_sph_callback.m +On line 107 of C:\ratter\ExperPort\HandleParam\@SoloParamHandle\callback.m +On line 5 of C:\ratter\ExperPort\HandleParam\generic_callback.m + +MATLAB:UndefinedFunction +Undefined function 'callback' for input arguments of type 'struct'. +On line 80 of C:\ratter\SoloData\Settings\experimenter\ratname\pipeline_ArpitCentrePokeTraining_experimenter_ratname_250508a.m +On line 2034 of C:\ratter\ExperPort\Plugins\@sessionmodel2\SessionDefinition.m +On line 264 of C:\ratter\Protocols\@ArpitCentrePokeTraining\ArpitCentrePokeTraining.m +On line 539 of C:\ratter\ExperPort\Modules\@dispatcher\RunningSection.m +On line 314 of C:\ratter\ExperPort\Modules\@dispatcher\RunningSection.m +On line 278 of C:\ratter\ExperPort\Modules\@dispatcher\RunningSection.m +On line 150 of C:\ratter\ExperPort\Modules\@dispatcher\RunningSection.m +On line 202 of C:\ratter\ExperPort\Modules\@dispatcher\dispatcher.m +On line 1501 of C:\ratter\ExperPort\Modules\@runrats\runrats.m +On line 1198 of C:\ratter\ExperPort\Modules\@runrats\runrats.m +On line 40 of C:\ratter\ExperPort\HandleParam\parse_and_call_sph_callback.m +On line 107 of C:\ratter\ExperPort\HandleParam\@SoloParamHandle\callback.m +On line 5 of C:\ratter\ExperPort\HandleParam\generic_callback.m +Flagged stopping process as complete. +reached sesm +############################################################ +ALERT! No WavePlayer module detected! +Protocols will error out if analog scheduled waves are used. +############################################################ + +ans = + + runrats object: 1-by-1 + +[Warning: WARNING in SoloUtility/add_and_commit.m: +The CVSROOT_STRING setting is empty. +Saved behavioral data and settings files will not be sent +to any data repository. + +IF YOU WISH TO EMPLOY THIS FEATURE, +please set CVSROOT_STRING in the custom settings file +Settings/Settings_Custom.conf. Instructions are available +there. If you are running the old RPbox-based system and +Settings_Custom.conf does not exist, copy +Settings_Template.conf to Settings_Custom.conf and then +try again.] +[> In add_and_commit (line 89) +In save_soloparamvalues (line 229) +In saveload/SavingSection (line 289) +In runrats (line 1558) +In timer/timercb (line 71) +In dispatcher/RunningSection (line 280) +In dispatcher/RunningSection (line 150) +In dispatcher (line 202) +In runrats (line 1501) +In runrats (line 1198) +In parse_and_call_sph_callback (line 40) +In SoloParamHandle/callback (line 107) +In generic_callback (line 5)] diff --git a/sendsummary_error_log.txt b/sendsummary_error_log.txt new file mode 100644 index 00000000..6229bd22 --- /dev/null +++ b/sendsummary_error_log.txt @@ -0,0 +1,30 @@ +Failed to send summary to sql +Unable to resolve the name 'perf.violation_rate'. + 7×1 struct array with fields: + + file + name + line + +Error occurred during sendsummary execution: +Unable to resolve the name 'perf.violation_rate'. + + +******* + + WARNING: Unable to complete 'close' action on protocol + Last error was: "Error using fprintf +Function is not defined for 'struct' inputs." + + + +******* + + +ans = + + dispatcher object: 1-by-1 + +Warning: B-data NOT enabled for liquid calibration. No calibration values received. +47 if isfield(protocol_data, 'violation_rate') +if system_dependent('IsDebugMode')==1, dbquit; end diff --git a/sparse_init.log b/sparse_init.log new file mode 100644 index 00000000..2e78c697 --- /dev/null +++ b/sparse_init.log @@ -0,0 +1,36 @@ +========================================= +🔧 Initializing sparse SVN checkout +📂 Target directory: /c/ratter +📡 Repository URL: http://172.24.155.220/svn/akramilab/ratter +📄 Log file: sparse_init.log +========================================= + +➡️ Checking out repository root (empty)... + U . +Checked out revision 44223. +✅ Fetching: PASSWORD_CONFIG-DO_NOT_VERSIONCONTROL.mat +Updating 'PASSWORD_CONFIG-DO_NOT_VERSIONCONTROL.mat': +A PASSWORD_CONFIG-DO_NOT_VERSIONCONTROL.mat +Updated to revision 44223. + +📁 Preparing SoloData structure... +Updating 'SoloData': +A SoloData +Updated to revision 44223. +Updating 'SoloData\Data': +A SoloData\Data +Updated to revision 44223. +Updating 'SoloData\Settings': +A SoloData\Settings +Updated to revision 44223. + +📁 Adding training_videos folder... +Updating 'training_videos': +A training_videos +Updated to revision 44223. + +✅ DONE: Sparse checkout initialized. +📌 You can now selectively fetch folders like: + svn update --set-depth=infinity SoloData/Data/arpit + svn update --set-depth=infinity training_videos/rig1 + From d2d404091611164aa7d9e441fa74b8f6ad1c4b90 Mon Sep 17 00:00:00 2001 From: Arpit Date: Tue, 10 Jun 2025 00:20:52 +0100 Subject: [PATCH 131/164] changed directory of ratter videos --- .../@bonsaicamera/BonsaiCameraInterface.m | 6 +++--- ...pitCentrePokeTraining_arpit_AR01_250506a.mat | Bin 15431 -> 0 bytes ...pitCentrePokeTraining_arpit_AR01_250507a.mat | Bin 16758 -> 0 bytes ...pitCentrePokeTraining_arpit_AR02_250506a.mat | Bin 16407 -> 0 bytes ...pitCentrePokeTraining_arpit_AR02_250507a.mat | Bin 17102 -> 0 bytes ...pitCentrePokeTraining_arpit_AR03_250506a.mat | Bin 16495 -> 0 bytes ...pitCentrePokeTraining_arpit_AR03_250507a.mat | Bin 17118 -> 0 bytes ...pitCentrePokeTraining_arpit_AR04_250506a.mat | Bin 16411 -> 0 bytes ...pitCentrePokeTraining_arpit_AR04_250507a.mat | Bin 17231 -> 0 bytes ...okeTraining_experimenter_ratname_250506a.mat | Bin 15767 -> 0 bytes ...okeTraining_experimenter_ratname_250508a.mat | Bin 16843 -> 0 bytes ...okeTraining_experimenter_ratname_250508b.mat | Bin 16889 -> 0 bytes ...okeTraining_experimenter_ratname_250521a.mat | Bin 16926 -> 0 bytes ...okeTraining_experimenter_ratname_250521b.mat | Bin 16900 -> 0 bytes ...okeTraining_experimenter_ratname_250521d.mat | Bin 16852 -> 0 bytes 15 files changed, 3 insertions(+), 3 deletions(-) delete mode 100644 ExperPort/settings_@ArpitCentrePokeTraining_arpit_AR01_250506a.mat delete mode 100644 ExperPort/settings_@ArpitCentrePokeTraining_arpit_AR01_250507a.mat delete mode 100644 ExperPort/settings_@ArpitCentrePokeTraining_arpit_AR02_250506a.mat delete mode 100644 ExperPort/settings_@ArpitCentrePokeTraining_arpit_AR02_250507a.mat delete mode 100644 ExperPort/settings_@ArpitCentrePokeTraining_arpit_AR03_250506a.mat delete mode 100644 ExperPort/settings_@ArpitCentrePokeTraining_arpit_AR03_250507a.mat delete mode 100644 ExperPort/settings_@ArpitCentrePokeTraining_arpit_AR04_250506a.mat delete mode 100644 ExperPort/settings_@ArpitCentrePokeTraining_arpit_AR04_250507a.mat delete mode 100644 ExperPort/settings_@ArpitCentrePokeTraining_experimenter_ratname_250506a.mat delete mode 100644 ExperPort/settings_@ArpitCentrePokeTraining_experimenter_ratname_250508a.mat delete mode 100644 ExperPort/settings_@ArpitCentrePokeTraining_experimenter_ratname_250508b.mat delete mode 100644 ExperPort/settings_@ArpitCentrePokeTraining_experimenter_ratname_250521a.mat delete mode 100644 ExperPort/settings_@ArpitCentrePokeTraining_experimenter_ratname_250521b.mat delete mode 100644 ExperPort/settings_@ArpitCentrePokeTraining_experimenter_ratname_250521d.mat diff --git a/ExperPort/Plugins/@bonsaicamera/BonsaiCameraInterface.m b/ExperPort/Plugins/@bonsaicamera/BonsaiCameraInterface.m index b3dbc404..dcb30cab 100644 --- a/ExperPort/Plugins/@bonsaicamera/BonsaiCameraInterface.m +++ b/ExperPort/Plugins/@bonsaicamera/BonsaiCameraInterface.m @@ -175,12 +175,12 @@ % Changing the Video file save location from C:\ratter_Videos to - % C:\ ratter\training_videos + % C:\ratter\training_videos - % ratter_dir = extractBefore(current_dir,'ratter'); % main_dir_video = [current_dir 'ratter_Videos']; current_dir = cd; - main_dir_video = [current_dir '\training_videos']; + ratter_dir = extractBefore(current_dir,'ratter'); + main_dir_video = [ratter_dir '\ratter\training_videos']; date_str = regexprep(char(datetime('today','Format','yyyy-MM-dd')), '[^0-9]', ''); video_foldername = sprintf('video_@%s_%s_%s_%s',protocol_name,experimenter_name,rat_name,date_str); rat_dir = sprintf('%s\\%s\\%s',main_dir_video,experimenter_name,rat_name); diff --git a/ExperPort/settings_@ArpitCentrePokeTraining_arpit_AR01_250506a.mat b/ExperPort/settings_@ArpitCentrePokeTraining_arpit_AR01_250506a.mat deleted file mode 100644 index 5c514ff4b039fd0795758e88e9c3c18a83e8cfe8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 15431 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 O7T|CapalnD#{XaIvLoIA diff --git a/ExperPort/settings_@ArpitCentrePokeTraining_arpit_AR01_250507a.mat b/ExperPort/settings_@ArpitCentrePokeTraining_arpit_AR01_250507a.mat deleted file mode 100644 index 0cdb63a839b4f05808360a5c69c6ecd79990f3ae..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 16758 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 SL+}vGWFyd{Liu>UkNjT+`iErz diff --git a/ExperPort/settings_@ArpitCentrePokeTraining_arpit_AR02_250506a.mat b/ExperPort/settings_@ArpitCentrePokeTraining_arpit_AR02_250506a.mat deleted file mode 100644 index 4668f92eb4f737870b575ad100e6666d7de678b6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 16407 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 V!&b#Mm_|7dgB=ec&<`c%{{c734t4+l diff --git a/ExperPort/settings_@ArpitCentrePokeTraining_arpit_AR02_250507a.mat b/ExperPort/settings_@ArpitCentrePokeTraining_arpit_AR02_250507a.mat deleted file mode 100644 index cf64239bcc122c1cf48d66c2b2acd0e93b91d6ea..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 17102 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 diff --git a/ExperPort/settings_@ArpitCentrePokeTraining_arpit_AR03_250506a.mat b/ExperPort/settings_@ArpitCentrePokeTraining_arpit_AR03_250506a.mat deleted file mode 100644 index 10415c028bf8536a7332c4c549d382d8bef18f85..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 16495 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 diff --git a/ExperPort/settings_@ArpitCentrePokeTraining_arpit_AR03_250507a.mat b/ExperPort/settings_@ArpitCentrePokeTraining_arpit_AR03_250507a.mat deleted file mode 100644 index 101ab724e87d845dd5a762ee8725c8ac22b4764a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 17118 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 diff --git a/ExperPort/settings_@ArpitCentrePokeTraining_arpit_AR04_250506a.mat b/ExperPort/settings_@ArpitCentrePokeTraining_arpit_AR04_250506a.mat deleted file mode 100644 index e236f862292f739f96464392c10424bb8674bba9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 16411 zcma)@bx<5Z6X+o%xO?#6?ydoXI|K;s?yh%Ou;A_xAc5c^xO;Fe=;7`T2OMz7Bl+rk zQuV6d+uGf^?wRiUV`qA2X8Wfi`B7O?ii(e&lS)PMBb&9IqZKQax}%w=wTruxFqOKr zu7WB*FDsR_yOo)zl_iynvoO_1FDoh)Ge0USekv|5VLl;Y9&RdbPHsM`|IY*NzX!a6 z3f9YSQ9d|0-`v!!N%up3>6ciLIt4#K>*2euPm8m(9<-2wZYi>QwacP{v25OR0>Ic`-T>5qP>ULCkFw(HV znP`fLhJ#Ot$&zLZN>qcpo$>Y}s&=<;evE)d$DS2~KYER42CbX+@lSXSu5w~(*^h9M zlLfE0H#PRdDpe2coNH|$Uu@;hTb6}@oUO9EXgv%n80!ewf!}XHwdcsH+OI-+hLU|Z zNHwmfoGXED=Mc@tyoRuPEi_9q`Qg{F`z< zRgGf)9@vvnaWL-5*O8;V@Z+jENYq%rB}HA&K<@&#{115dG7v|19a5|3&|HCqU@Tq7-^7yD-p1c}7DwH!uz_j9+EZXP)!WkN@VwI-YHQu{o`#*ZR*dyNgu>P$jeR zI!}>*tGT|5pG-tC@I9#s-{K>xSXo6?bA2cOL^cL1+7J0FC%@#iBaMdT zFasBX&ccHbo`c*7)Et5&$3{0!Aw!xK+MsEF8gryTj(HV9L41i@%vf0zzrmHoas5KEt9Z5 zLL;74xoj8sTat$c#r46n5OzO*ogN~>Uq1rAT&QGSvboQqb#ZyKFu2aTT!bS9;X)a( zxRZ`73WzQ^ak0c=`=zmHF8=h%i(dT`=O}!Mqa!I~Hwz=bpRX_R9%>+973012B{q5; z5=rM!0;_yY;s1%-KZBvcM~s!Oq$0dm8mriW{`{YH{%m*OzCV^|tg7X@S#-nV^5v%zGM=o$@lmFpnk8+ zs=>q5*V)a5r}RNB%UCJE*1SIRJ=$OES#;fFGRaT=t0;d$a#d8N+MbNTYsuH?z9< z99hLBMt(L1SuLC7*tv_(7tKqTNFEyr!EYJ7fwy8XAtGwJFI8t9?OA>hzfHecmXChI zq~B%tg%aMN)$6o2LtRBK4mt3lrOWa%zXuhoL@X({@0|o51~G5LahuS%2#)Q1g^c#quX94)!*!p`&1pD^c(Bc(C18dQnG~H{)Ei$8RzMJ7tTk3Fc-T1vsax=hxkh$se zL?bq$206(th$Cny`ds@hY)^jvS9Itu9RGqza6>Gd^mFhJeX!Wv4A05n1gf0qTx!ST zext6)er<$#u)g|>^&-!I%v#G|ngS!mB2;OQxdLlK|M?vBy{f(7joA1QlO{N6PN!P` zDH@MWXY)ghcEdF5-}0|IK+wuTSNgkNWww0wKso(aJ;yE^WI`6VqJf(_Jl4jFJj>dS zzkil%TU%~p`>�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 Jzt5ZK{{e}#E-?TA diff --git a/ExperPort/settings_@ArpitCentrePokeTraining_arpit_AR04_250507a.mat b/ExperPort/settings_@ArpitCentrePokeTraining_arpit_AR04_250507a.mat deleted file mode 100644 index 583806dd243a096eeed6ebee5d8cf8b1132c6b96..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 17231 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 BbB_Q3 diff --git a/ExperPort/settings_@ArpitCentrePokeTraining_experimenter_ratname_250506a.mat b/ExperPort/settings_@ArpitCentrePokeTraining_experimenter_ratname_250506a.mat deleted file mode 100644 index 24c98418a8569453708ae83132c41b50048f0075..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 15767 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 N5f>R=fbX5~{|`c)^Y;J% diff --git a/ExperPort/settings_@ArpitCentrePokeTraining_experimenter_ratname_250521b.mat b/ExperPort/settings_@ArpitCentrePokeTraining_experimenter_ratname_250521b.mat deleted file mode 100644 index bd207242f0c06dbe17acfee2a16933f1552e4711..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 16900 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 diff --git a/ExperPort/settings_@ArpitCentrePokeTraining_experimenter_ratname_250521d.mat b/ExperPort/settings_@ArpitCentrePokeTraining_experimenter_ratname_250521d.mat deleted file mode 100644 index 51250589654ae0e8be3c05bcc953f72429a05cdf..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 16852 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 CD^&yl From 9344d00e24c2839bafb7d811307b067e1bf094a7 Mon Sep 17 00:00:00 2001 From: Arpit Date: Wed, 18 Jun 2025 14:06:27 +0100 Subject: [PATCH 132/164] error debug --- .../@ArpitCentrePokeTraining/ParamsSection.m | 5 +-- .../ArpitSoundCatContinuous.m | 32 +++++++++---------- .../StimulatorSection.m | 4 +-- .../StimulusSection.m | 5 +-- 4 files changed, 21 insertions(+), 25 deletions(-) diff --git a/Protocols/@ArpitCentrePokeTraining/ParamsSection.m b/Protocols/@ArpitCentrePokeTraining/ParamsSection.m index 8d7610a8..86991688 100644 --- a/Protocols/@ArpitCentrePokeTraining/ParamsSection.m +++ b/Protocols/@ArpitCentrePokeTraining/ParamsSection.m @@ -216,10 +216,7 @@ if value(use_auto_train) == 1 disable(training_stage); % user cannot change the training stages else - Training_Stages_List = {'Familiarize with Reward Side Pokes','Timeout Rewarded Side Pokes'... - 'Introduce Centre Poke','Introduce Violation for Centre Poke',... - 'Introduce Stimuli Sound during Centre Poke','Vary Stimuli location during Centre Poke'... - 'Variable Stimuli Go Cue location during Centre Poke','User Setting'}; + Training_Stages_List = SessionDefinition(obj, 'get_stagelist'); stage_name = Training_Stages_List{value(training_stage)}; enable(training_stage); % user can change the training stages SessionDefinition(obj, 'jump_to_stage',stage_name); diff --git a/Protocols/@ArpitSoundCatContinuous/ArpitSoundCatContinuous.m b/Protocols/@ArpitSoundCatContinuous/ArpitSoundCatContinuous.m index f29c97a3..fcb752a9 100644 --- a/Protocols/@ArpitSoundCatContinuous/ArpitSoundCatContinuous.m +++ b/Protocols/@ArpitSoundCatContinuous/ArpitSoundCatContinuous.m @@ -131,7 +131,7 @@ SessionDefinition(obj, 'init', x, y, value(myfig)); next_row(y, 2); %#ok ArpitSoundCatContinuousSMA(obj, 'init'); - feval(mfilename, obj, 'prepare_next_trial'); + % feval(mfilename, obj, 'prepare_next_trial'); case 'change_water_modulation_params' display_guys = [1 150 300]; @@ -216,24 +216,22 @@ StimulusSection(obj,'hide'); SessionDefinition(obj, 'run_eod_logic_without_saving'); - perf = PerformanceSection(obj, 'evaluate'); - cp_durs = SideSection(obj, 'get_cp_history'); - - [stim1dur] = SideSection(obj,'get_stimdur_history'); - %stim_history = StimulatorSection(obj,'get_history'); - - pd.hits=hit_history(:); - pd.sides=previous_sides(:); - pd.viols=violation_history(:); - pd.timeouts=timeout_history(:); -% pd.performance=tot_perf(:); - pd.cp_durs=cp_durs(:); - - pd.stim1dur=stim1dur(:); + % perf = PerformanceSection(obj, 'evaluate'); + % cp_durs = SideSection(obj, 'get_cp_history'); + % + % [stim1dur] = SideSection(obj,'get_stimdur_history'); + %stim_history = StimulatorSection(obj,'get_history'); + % pd.hits=hit_history(:); + % pd.sides=previous_sides(:); + % pd.viols=violation_history(:); + % pd.timeouts=timeout_history(:); + % pd.cp_durs=cp_durs(:); + % pd.stim1dur=stim1dur(:); %pd.stimul=stim_history(:); - - sendsummary(obj,'protocol_data',pd); + % sendsummary(obj,'protocol_data',pd); + + sendsummary(obj); %% otherwise otherwise diff --git a/Protocols/@ArpitSoundCatContinuous/StimulatorSection.m b/Protocols/@ArpitSoundCatContinuous/StimulatorSection.m index 35c40f30..9f4a05ed 100644 --- a/Protocols/@ArpitSoundCatContinuous/StimulatorSection.m +++ b/Protocols/@ArpitSoundCatContinuous/StimulatorSection.m @@ -153,8 +153,8 @@ disp('StimState value greater than list of possible stim states'); else StimState.value = ss; - disp('test ss') - value(ss) + % disp('test ss'); + value(ss); end if sl > length(psl) diff --git a/Protocols/@ArpitSoundCatContinuous/StimulusSection.m b/Protocols/@ArpitSoundCatContinuous/StimulusSection.m index cde040fd..d72dcdb1 100644 --- a/Protocols/@ArpitSoundCatContinuous/StimulusSection.m +++ b/Protocols/@ArpitSoundCatContinuous/StimulusSection.m @@ -168,14 +168,15 @@ tw=sin(t*2*pi*freq1); RW=RVol*tw; %w=[LW;RW]; - AUD1 = RW; + AUD1 = RW; else % produce noise pattern + A1_length = round(A1_time * srate); A1_sigma.value = value(thisstim); A1 = value(thisstimlog(n_done_trials+1)); [rawA1, rawA2, normA1, normA2]=noisestim(1,1,T,value(fcut),Fs,value(filter_type)); modulator=singlenoise(1,T,[value(lfreq) value(hfreq)],Fs,'BUTTER'); - AUD1=normA1(1:A1_time*srate).*modulator(1:A1_time*srate).*A1_sigma; + AUD1=normA1(1:A1_length) .* modulator(1:A1_length).*A1_sigma; end if ~isempty(AUD1) From 0154f39dc74a3820a05d433799d9dfe5eaef985a Mon Sep 17 00:00:00 2001 From: Arpit Date: Wed, 25 Jun 2025 09:32:46 +0100 Subject: [PATCH 133/164] error debug --- .../add_trialnum_indicator.asv | 146 +++++++++++++ .../ArpitSoundCatContinuous.m | 1 - .../ArpitSoundCatContinuousSMA.m | 5 + .../StimulatorSection.m | 2 +- .../private/LoadSettingGUI.m | 196 ------------------ .../set_training_stage_last_setting_file.m | 51 ----- ...tCentrePokeTraining_arpit_AR05_250516a.mat | Bin 16329 -> 0 bytes 7 files changed, 152 insertions(+), 249 deletions(-) create mode 100644 ExperPort/Modules/@StateMachineAssembler/add_trialnum_indicator.asv delete mode 100644 Protocols/@ArpitSoundCatContinuous/private/LoadSettingGUI.m delete mode 100644 Protocols/@ArpitSoundCatContinuous/private/set_training_stage_last_setting_file.m delete mode 100644 Protocols/@ArpitSoundCatContinuous/private/settings_@ArpitCentrePokeTraining_arpit_AR05_250516a.mat diff --git a/ExperPort/Modules/@StateMachineAssembler/add_trialnum_indicator.asv b/ExperPort/Modules/@StateMachineAssembler/add_trialnum_indicator.asv new file mode 100644 index 00000000..67587d48 --- /dev/null +++ b/ExperPort/Modules/@StateMachineAssembler/add_trialnum_indicator.asv @@ -0,0 +1,146 @@ +% [sma] = add_trialnum_indicator(sma, trialnum, {'indicator_states_name', 'sending_trialnum'}, ... +% {'time_per_state', 3e-4}, {'preamble', [1]}, 'DIOLINE', 'from_settings') +% +% The intent behind this method is to provide a time-sync signal on a DIO +% line, indicating start of a trial, for use by neural recording systems. +% That same signal is also used to indicate the trial number. +% +% When this method is called, the sma will be modified so that state_0 +% will not jump straight to the user's first state, but will instead first +% go through a very swift sequence of states that put out, on one of the DIO +% lines, a binary signature of the number trialnum; after that, there is a +% jump to the user's first state. None of the user-defined states are +% changed by a call ,to this method. +% +% The signal on the DIO line will be: High, +% followed by a a 15-bit binary representation of trialnum (1 is High, 0 is +% Low), with most significant bit sent out first. That is, we go through a +% total of 16 states that cover both the initial sync signal and the +% trialnum. The default is to go through each of these states in 1/3th of a +% ms, twice the FSM's cycle time. This +% can be modified using the optional argument 'time_per_state'. All of +% these added states will have the name 'sending_trialnum'. +% +% This method can ONLY be used with StateMachineAssemblers that were +% initialized with the 'full_trial_structure' flag. +% +% The DIO line on which all this will happen is determined, by default, +% using the Settings.m system, with DIOLINES; trialnum_indicator being the +% setting name. For example, the line +% DIOLINES; trialnum_indicator; 32 +% in Settings_Custom.conf would mean DIO line 6 (in binary, 32 is 100000, +% so it is the 6th bit). If no such setting is found, no DOut is generated. +% +% +% RETURNS: +% -------- +% +% sma The updated State Machine Assembler object, after the +% trialnum-sending states have been added. +% +% +% PARAMETERS: +% ----------- +% +% sma The instantiation of the StateMachineAssembler object to which +% the new states will be added. +% +% +% OPTIONAL PARAMETERS: +% -------------------- +% +% 'time_per_state' A scalar positive number indicating the time, in +% seconds, that will be spent on each of the states, that put out +% a signal. Default is 1/3 of a ms. Total time for all added +% states will be time_per_state*(15+length(preamble)). +% +% 'preamble' Sync signal that is sent before the trialnum. Default is +% [1], meaning a single High bit. +% +% 'indicator_states_name' String that defines the name of all the states +% that will be added by this method. Default is +% 'sending_trialnum_data'. +% +% 'DIOLINE' Default value is the string 'from_settings', which indicates +% that the Settings.m system should be used to find setting named +% DIOLINES; trialnum_indicator. However, you can use this optional +% parameter to override the value from the settings files. +% + +% Written by Carlos Brody Aug 2007 + +function [sma] = add_trialnum_indicator(sma, trialnum, varargin) + + time_per_state = []; % hack: time_per_state is a function. unfortunately, assignin (called by parseargs below) will fail when what it is assigning already exists with some meaning. This must be fixed. Perhaps if evalin does not have the same problem, we can evalin to 0 first, then assignin. -s & CB + pairs = { ... + 'time_per_state' 800e-6 ; ... + 'preamble' [1] ; ... + 'indicator_states_name' 'sending_trialnum' ; ... + 'DIOLINE' 'from_settings' ; ... + }; parseargs(varargin, pairs); + +% NOTE: even though 'time_per_state' is 0.8 ms, the actual time per state is +% 1 ms, which is the nearest multiple of the FSM clock. +% Experimentation shows that requesting exactly 0.5 ms often gives states +% that are 1 ms but sometimes 0.5 ms, but asking for 0.8 gives 1 + + nbits = 15; % This is the number of bits used to encode trialnum. + + if strcmp(DIOLINE, 'from_settings'), + % Try the settings system; if any problem, set to zero meaning go + % through states, but do nothing. + try, DIOLINE = bSettings('get', 'DIOLINES', 'trialnum_indicator'); + catch, DIOLINE = 0; + return + end; + end; + if isnan(DIOLINE), DIOLINE = 0; end; + + % --- BEGIN error_checking --- + if ~is_full_trial_structure(sma), + error(['Sorry, ' mfilename ' can only be used with StateMachineAssemblers ' ... + 'initialized with the ''full_trial_structure'' flag on']); + end; + if nargin < 2, error('Need at least two args, sma and trialnum'); end; + % --- END error checking --- + + orig_current_state = sma.current_state; + % Temporarily set current_state marker to state 1, so we start adding + % states there: + sma.current_state = 1; + + % Now add the set of states going through the signal: + dout = preamble(1); % The preamble is a numeric vector. + sma = add_state(sma, 'name', indicator_states_name, 'self_timer', time_per_state, ... + 'input_to_statechange', {'Tup', 'current_state+1'}, ... + 'output_actions', {'DOut', sma.default_DOut + dout*DIOLINE}); + + for i=2:length(preamble) + dout = preamble(i); % The preamble is a numeric vector. + sma = add_state(sma, 'self_timer', time_per_state, ... + 'input_to_statechange', {'Tup', 'current_state+1'}, ... + 'output_actions', {'DOut', sma.default_DOut + dout*DIOLINE}); + end; + trialnum = dec2bin(trialnum); + trialnum = ['0'*ones(1, nbits-length(trialnum)) trialnum]; + for i=1:length(trialnum), + dout = str2num(trialnum(i)); % trialnum at this point is char vector + sma = add_state(sma, 'self_timer', time_per_state, ... + 'input_to_statechange', {'Tup', 'current_state+1'}, ... + 'output_actions', {'DOut', sma.default_DOut + dout*DIOLINE}); + end; + + % Now change the Tup action of the last added state to jump to state 40, + % the regular first user state in 'full_trial_structure'. + TupCol = find(strcmp('Tup', sma.input_map(:,1))); TupCol = sma.input_map{TupCol,2}; + sma.states{sma.current_state, TupCol} = 40; + + % Now change state_0 so it jumps to state 1 instead of state 40: + all_input_cols = cell2mat(sma.input_map(:,2))'; + for i=1:length(all_input_cols), sma.states{1,all_input_cols(i)} = 1; end; + + % We're done: return the current_state marker to its proper value. + sma.current_state = orig_current_state; + + return; + \ No newline at end of file diff --git a/Protocols/@ArpitSoundCatContinuous/ArpitSoundCatContinuous.m b/Protocols/@ArpitSoundCatContinuous/ArpitSoundCatContinuous.m index fcb752a9..8a8f4168 100644 --- a/Protocols/@ArpitSoundCatContinuous/ArpitSoundCatContinuous.m +++ b/Protocols/@ArpitSoundCatContinuous/ArpitSoundCatContinuous.m @@ -160,7 +160,6 @@ SoundManagerSection(obj, 'send_not_yet_uploaded_sounds'); [sma, prepare_next_trial_states] = ArpitSoundCatContinuousSMA(obj, 'prepare_next_trial'); - sma = add_trialnum_indicator(sma, n_done_trials); % PerformanceSection(obj, 'evaluate'); diff --git a/Protocols/@ArpitSoundCatContinuous/ArpitSoundCatContinuousSMA.m b/Protocols/@ArpitSoundCatContinuous/ArpitSoundCatContinuousSMA.m index 9f02a446..611128a4 100644 --- a/Protocols/@ArpitSoundCatContinuous/ArpitSoundCatContinuousSMA.m +++ b/Protocols/@ArpitSoundCatContinuous/ArpitSoundCatContinuousSMA.m @@ -263,7 +263,12 @@ % defined. state_names = get_labels(sma); state_names = state_names(:,1); prepare_next_trial_states = {'side_led_wait_RewardCollection','hit_state','second_hit_state','drink_state', 'violation_state','timeout_state','preclean_up_state'}; + + % After defining the states for behavior, adding states for + % electrophysiology or LED stimulator. sma = StimulatorSection(obj,'prepare_next_trial',sma); + sma = add_trialnum_indicator(sma, n_done_trails); + dispatcher('send_assembler', sma, intersect(state_names, prepare_next_trial_states)); diff --git a/Protocols/@ArpitSoundCatContinuous/StimulatorSection.m b/Protocols/@ArpitSoundCatContinuous/StimulatorSection.m index 9f4a05ed..8421adbb 100644 --- a/Protocols/@ArpitSoundCatContinuous/StimulatorSection.m +++ b/Protocols/@ArpitSoundCatContinuous/StimulatorSection.m @@ -93,7 +93,7 @@ % LegalCBrk_temp.value = value(LegalCBrk); %#ok %end - if ~dispatcher('is_running'); + if ~dispatcher('is_running') %dispatcher is not running, last stim_hist not used, lop it off sh = sh(1:end-1); end diff --git a/Protocols/@ArpitSoundCatContinuous/private/LoadSettingGUI.m b/Protocols/@ArpitSoundCatContinuous/private/LoadSettingGUI.m deleted file mode 100644 index 84cf7e1a..00000000 --- a/Protocols/@ArpitSoundCatContinuous/private/LoadSettingGUI.m +++ /dev/null @@ -1,196 +0,0 @@ -% REMOVE THE SETTINGS/PRE OPENED GUI - if exist('BpodSystem','var') == 1 - if ~isempty(BpodSystem) - flush - end - end - -% START FRESH & SET THE REQUIRED DIRECTORY - -home_drive = getenv('HOMEDRIVE'); -current_dir = cd; -ratter_dir = extractBefore(current_dir,'ratter'); -ratter_modules_dir1 = fullfile(ratter_dir, 'ratter', 'ExperPort'); -ratter_modules_dir2 = fullfile(home_drive, 'ratter', 'ExperPort'); -if exist("ratter_modules_dir1",'dir') == 7 - cd(ratter_modules_dir1); -else - cd(ratter_modules_dir2); -end - -% START BPOD -try - Bpod('COM5') -catch - - % Identify the bpod port before starting - bpodPort = getOrSetCOMPort(); - disp(['Using COM Port: ' bpodPort]); - % Bpod(); - Bpod(bpodPort); -end - -newstartup; - -% PRESENT THE USER WITH THE CHOICE OF EXPERIMENTER/RAT - -% Let's identify all the experimenter and their respective rats -try - Experimenter_Name = bdata('select distinct experimenter from rats where extant=1 order by experimenter'); -catch %#ok - disp('ERROR: Unable to connect to MySQL Server'); - Experimenter_Name = ''; -end - -rig_id = bSettings('get','RIGS','Rig_ID'); % the ID of this rig - -if ~isempty(Experimenter_Name) - for n_exp = 1:numel(Experimenter_Name) - % Finding all training rat for this experimenter - % ratnames = bdata(['select ratname from rats where experimenter="',Experimenter_Name{n_exp},'" and extant=1']); - ratnames = bdata(['select ratname from rats where experimenter="',Experimenter_Name{n_exp},'" and in_training=1']); - Rat_Name_all{n_exp} = sortrows(strtrim(ratnames)); - - % Additionaally finding rats for this experimenter with its schedule on this rig - [rats,slots] = bdata(['select ratname, timeslot from scheduler where experimenter="',... - Experimenter_Name{n_exp},'" and rig="',num2str(rig_id),'"']); - - - end -end - -Exp_Rat_Map = containers.Map(Experimenter_Name, Rat_Name_all); - - -% Create figure -fig = figure('Name', 'Dynamic Button GUI', ... - 'Position', [500, 300, 600, 400], ... - 'MenuBar', 'none', ... - 'NumberTitle', 'off', ... - 'Color', [0.9 0.9 0.9]); - -% Create main buttons with resize callback -buttonHandles = createButtons(fig, Experimenter_Name, @(src, ~) mainButtonCallback(src, Exp_Rat_Map, fig)); - -% Setup resize behavior -set(fig, 'ResizeFcn', @(src, ~) resizeButtons(src, buttonHandles)); - -% === Callback for first-level buttons === -function mainButtonCallback(src, map, figHandle) -experimenter = src.String; -subOptions = map(experimenter); -clf(figHandle); % Clear previous buttons - -% Create new buttons and assign second-level callback -newButtons = createButtons(figHandle, subOptions, @(src2, ~) subButtonCallback(src2,experimenter,figHandle)); - -% Update resize function -set(figHandle, 'ResizeFcn', @(src, ~) resizeButtons(src, newButtons)); -end - -% === Final action when second-level button is clicked === -function subButtonCallback(src,experimenter_name,figHandle) -rat_name = src.String; -close(figHandle); -runrats('init'); -pause(3); -runrats('update exp_rat_userclick',experimenter_name,rat_name); -end - - -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - -%% HELPER FUNCTIONS - -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - -% === Create buttons dynamically and return handles === -function btns = createButtons(figHandle, optionList, callbackFcn) -delete(findall(figHandle, 'Type', 'uicontrol')); % Clear any existing buttons - -n = numel(optionList); -btns = gobjects(1, n); - -for i = 1:n - btns(i) = uicontrol(figHandle, ... - 'Style', 'pushbutton', ... - 'Units', 'normalized', ... - 'String', optionList{i}, ... - 'FontSize', 14, ... - 'FontWeight', 'bold', ... - 'BackgroundColor', [0.7 0.8 1], ... - 'Callback', callbackFcn); -end - -resizeButtons(figHandle, btns); % Initial layout -end - -% === Resize button layout responsively === -function resizeButtons(figHandle, buttons) -n = numel(buttons); -spacing = 0.02; -totalSpacing = spacing * (n + 1); -btnHeight = (1 - totalSpacing) / n; -btnWidth = 0.8; -x = (1 - btnWidth) / 2; - -for i = 1:n - y = 1 - spacing - i * (btnHeight + spacing) + spacing; - set(buttons(i), 'Position', [x, y, btnWidth, btnHeight]); -end -end - - -function comPort = getOrSetCOMPort() -configFile = fullfile(fileparts(mfilename('fullpath')), 'com_config.mat'); -maxAttempts = 3; -success = false; - -% Try existing config or prompt -if exist(configFile, 'file') - data = load(configFile, 'comPort'); - comPort = data.comPort; -else - comPort = promptForPort(); -end - -% Try to validate and possibly retry -for attempt = 1:maxAttempts - try - s = serialport(comPort, 9600); % test connection - clear s; % close it immediately - success = true; - break; - catch - fprintf('[Warning] Failed to open %s. Please select a different COM port.\n', comPort); - comPort = promptForPort(); - end -end - -if ~success - error('Failed to find a valid COM port after %d attempts.', maxAttempts); -end - -% Save the working port -save(configFile, 'comPort'); -end - -function comPort = promptForPort() -availablePorts = serialportlist("available"); - -if isempty(availablePorts) - warning('No serial ports detected. Enter manually.'); - comPort = input('Enter COM port manually (e.g., COM3): ', 's'); -else - fprintf('Available COM ports:\n'); - for i = 1:numel(availablePorts) - fprintf(' %d: %s\n', i, availablePorts(i)); - end - idx = input('Select COM port number: '); - if isnumeric(idx) && idx >= 1 && idx <= numel(availablePorts) - comPort = availablePorts(idx); - else - comPort = input('Enter COM port manually (e.g., COM3): ', 's'); - end -end -end diff --git a/Protocols/@ArpitSoundCatContinuous/private/set_training_stage_last_setting_file.m b/Protocols/@ArpitSoundCatContinuous/private/set_training_stage_last_setting_file.m deleted file mode 100644 index 92dd3cd0..00000000 --- a/Protocols/@ArpitSoundCatContinuous/private/set_training_stage_last_setting_file.m +++ /dev/null @@ -1,51 +0,0 @@ -function set_training_stage_last_setting_file(owner,experimenter,ratname) - -try - -global Solo_datadir; - -rat_dir = sprintf('%s\\Settings\\%s\\%s\\',Solo_datadir,experimenter,ratname); -% Get the latest setting file - -u = dir([rat_dir 'settings_@' owner '_' experimenter '_' ratname '*.mat']); -if ~isempty(u) - [filenames{1:length(u)}] = deal(u.name); - filenames = sort(filenames'); %#ok (can't use dimension argument with cell sort) - today_date_str = regexprep(char(datetime('today','Format','yy-MM-dd')), '[^0-9]', ''); - for i=length(u):-1:1 % search from the end back - file_date_num = textscan(filenames{i},['settings_@' owner '_' experimenter '_' ratname '_%n%*c.mat']); - - if ~isempty(file_date_num{1}) && file_date_num{1} <= str2double(today_date_str) - fullname = [rat_dir filenames{i}]; % We've found it. - break - end - end -end - -try - loaded_data = load(fullname); -catch - return; -end - -% Lets find the Handle for Training Stage -handles = get_sphandle('owner', owner); -stage_handle_name = 'ParamsSection_training_stage'; -for hi= 1:length(handles) - sph_fullname=get_fullname(handles{hi}); - if strcmpi(sph_fullname,stage_handle_name) - handles{hi}.value = loaded_data.saved.(sph_fullname); - callback(handles{i}); - break - end -end - -return - -catch - - return - -end - -end \ No newline at end of file diff --git a/Protocols/@ArpitSoundCatContinuous/private/settings_@ArpitCentrePokeTraining_arpit_AR05_250516a.mat b/Protocols/@ArpitSoundCatContinuous/private/settings_@ArpitCentrePokeTraining_arpit_AR05_250516a.mat deleted file mode 100644 index 7e70e094972354707ab4d569647e26f9141390cf..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 16329 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 LamD$f`qBRa*3e*P From 042da12e2535eb87c850a3635564b709bb7bce34 Mon Sep 17 00:00:00 2001 From: Arpit Date: Wed, 25 Jun 2025 11:17:16 +0100 Subject: [PATCH 134/164] error debug --- .../add_trialnum_indicator.asv | 146 ------------------ .../ArpitSoundCatContinuous.m | 2 +- .../ArpitSoundCatContinuousSMA.m | 2 +- 3 files changed, 2 insertions(+), 148 deletions(-) delete mode 100644 ExperPort/Modules/@StateMachineAssembler/add_trialnum_indicator.asv diff --git a/ExperPort/Modules/@StateMachineAssembler/add_trialnum_indicator.asv b/ExperPort/Modules/@StateMachineAssembler/add_trialnum_indicator.asv deleted file mode 100644 index 67587d48..00000000 --- a/ExperPort/Modules/@StateMachineAssembler/add_trialnum_indicator.asv +++ /dev/null @@ -1,146 +0,0 @@ -% [sma] = add_trialnum_indicator(sma, trialnum, {'indicator_states_name', 'sending_trialnum'}, ... -% {'time_per_state', 3e-4}, {'preamble', [1]}, 'DIOLINE', 'from_settings') -% -% The intent behind this method is to provide a time-sync signal on a DIO -% line, indicating start of a trial, for use by neural recording systems. -% That same signal is also used to indicate the trial number. -% -% When this method is called, the sma will be modified so that state_0 -% will not jump straight to the user's first state, but will instead first -% go through a very swift sequence of states that put out, on one of the DIO -% lines, a binary signature of the number trialnum; after that, there is a -% jump to the user's first state. None of the user-defined states are -% changed by a call ,to this method. -% -% The signal on the DIO line will be: High, -% followed by a a 15-bit binary representation of trialnum (1 is High, 0 is -% Low), with most significant bit sent out first. That is, we go through a -% total of 16 states that cover both the initial sync signal and the -% trialnum. The default is to go through each of these states in 1/3th of a -% ms, twice the FSM's cycle time. This -% can be modified using the optional argument 'time_per_state'. All of -% these added states will have the name 'sending_trialnum'. -% -% This method can ONLY be used with StateMachineAssemblers that were -% initialized with the 'full_trial_structure' flag. -% -% The DIO line on which all this will happen is determined, by default, -% using the Settings.m system, with DIOLINES; trialnum_indicator being the -% setting name. For example, the line -% DIOLINES; trialnum_indicator; 32 -% in Settings_Custom.conf would mean DIO line 6 (in binary, 32 is 100000, -% so it is the 6th bit). If no such setting is found, no DOut is generated. -% -% -% RETURNS: -% -------- -% -% sma The updated State Machine Assembler object, after the -% trialnum-sending states have been added. -% -% -% PARAMETERS: -% ----------- -% -% sma The instantiation of the StateMachineAssembler object to which -% the new states will be added. -% -% -% OPTIONAL PARAMETERS: -% -------------------- -% -% 'time_per_state' A scalar positive number indicating the time, in -% seconds, that will be spent on each of the states, that put out -% a signal. Default is 1/3 of a ms. Total time for all added -% states will be time_per_state*(15+length(preamble)). -% -% 'preamble' Sync signal that is sent before the trialnum. Default is -% [1], meaning a single High bit. -% -% 'indicator_states_name' String that defines the name of all the states -% that will be added by this method. Default is -% 'sending_trialnum_data'. -% -% 'DIOLINE' Default value is the string 'from_settings', which indicates -% that the Settings.m system should be used to find setting named -% DIOLINES; trialnum_indicator. However, you can use this optional -% parameter to override the value from the settings files. -% - -% Written by Carlos Brody Aug 2007 - -function [sma] = add_trialnum_indicator(sma, trialnum, varargin) - - time_per_state = []; % hack: time_per_state is a function. unfortunately, assignin (called by parseargs below) will fail when what it is assigning already exists with some meaning. This must be fixed. Perhaps if evalin does not have the same problem, we can evalin to 0 first, then assignin. -s & CB - pairs = { ... - 'time_per_state' 800e-6 ; ... - 'preamble' [1] ; ... - 'indicator_states_name' 'sending_trialnum' ; ... - 'DIOLINE' 'from_settings' ; ... - }; parseargs(varargin, pairs); - -% NOTE: even though 'time_per_state' is 0.8 ms, the actual time per state is -% 1 ms, which is the nearest multiple of the FSM clock. -% Experimentation shows that requesting exactly 0.5 ms often gives states -% that are 1 ms but sometimes 0.5 ms, but asking for 0.8 gives 1 - - nbits = 15; % This is the number of bits used to encode trialnum. - - if strcmp(DIOLINE, 'from_settings'), - % Try the settings system; if any problem, set to zero meaning go - % through states, but do nothing. - try, DIOLINE = bSettings('get', 'DIOLINES', 'trialnum_indicator'); - catch, DIOLINE = 0; - return - end; - end; - if isnan(DIOLINE), DIOLINE = 0; end; - - % --- BEGIN error_checking --- - if ~is_full_trial_structure(sma), - error(['Sorry, ' mfilename ' can only be used with StateMachineAssemblers ' ... - 'initialized with the ''full_trial_structure'' flag on']); - end; - if nargin < 2, error('Need at least two args, sma and trialnum'); end; - % --- END error checking --- - - orig_current_state = sma.current_state; - % Temporarily set current_state marker to state 1, so we start adding - % states there: - sma.current_state = 1; - - % Now add the set of states going through the signal: - dout = preamble(1); % The preamble is a numeric vector. - sma = add_state(sma, 'name', indicator_states_name, 'self_timer', time_per_state, ... - 'input_to_statechange', {'Tup', 'current_state+1'}, ... - 'output_actions', {'DOut', sma.default_DOut + dout*DIOLINE}); - - for i=2:length(preamble) - dout = preamble(i); % The preamble is a numeric vector. - sma = add_state(sma, 'self_timer', time_per_state, ... - 'input_to_statechange', {'Tup', 'current_state+1'}, ... - 'output_actions', {'DOut', sma.default_DOut + dout*DIOLINE}); - end; - trialnum = dec2bin(trialnum); - trialnum = ['0'*ones(1, nbits-length(trialnum)) trialnum]; - for i=1:length(trialnum), - dout = str2num(trialnum(i)); % trialnum at this point is char vector - sma = add_state(sma, 'self_timer', time_per_state, ... - 'input_to_statechange', {'Tup', 'current_state+1'}, ... - 'output_actions', {'DOut', sma.default_DOut + dout*DIOLINE}); - end; - - % Now change the Tup action of the last added state to jump to state 40, - % the regular first user state in 'full_trial_structure'. - TupCol = find(strcmp('Tup', sma.input_map(:,1))); TupCol = sma.input_map{TupCol,2}; - sma.states{sma.current_state, TupCol} = 40; - - % Now change state_0 so it jumps to state 1 instead of state 40: - all_input_cols = cell2mat(sma.input_map(:,2))'; - for i=1:length(all_input_cols), sma.states{1,all_input_cols(i)} = 1; end; - - % We're done: return the current_state marker to its proper value. - sma.current_state = orig_current_state; - - return; - \ No newline at end of file diff --git a/Protocols/@ArpitSoundCatContinuous/ArpitSoundCatContinuous.m b/Protocols/@ArpitSoundCatContinuous/ArpitSoundCatContinuous.m index 8a8f4168..d8e8c2d5 100644 --- a/Protocols/@ArpitSoundCatContinuous/ArpitSoundCatContinuous.m +++ b/Protocols/@ArpitSoundCatContinuous/ArpitSoundCatContinuous.m @@ -154,7 +154,7 @@ SideSection(obj, 'prepare_next_trial'); % Run SessionDefinition *after* SideSection so we know whether the % trial was a violation or not - SessionDefinition(obj, 'next_trial'); + % SessionDefinition(obj, 'next_trial'); StimulatorSection(obj, 'update_values'); StimulusSection(obj,'prepare_next_trial'); SoundManagerSection(obj, 'send_not_yet_uploaded_sounds'); diff --git a/Protocols/@ArpitSoundCatContinuous/ArpitSoundCatContinuousSMA.m b/Protocols/@ArpitSoundCatContinuous/ArpitSoundCatContinuousSMA.m index 611128a4..fd29a07a 100644 --- a/Protocols/@ArpitSoundCatContinuous/ArpitSoundCatContinuousSMA.m +++ b/Protocols/@ArpitSoundCatContinuous/ArpitSoundCatContinuousSMA.m @@ -267,7 +267,7 @@ % After defining the states for behavior, adding states for % electrophysiology or LED stimulator. sma = StimulatorSection(obj,'prepare_next_trial',sma); - sma = add_trialnum_indicator(sma, n_done_trails); + % sma = add_trialnum_indicator(sma, n_done_trails); dispatcher('send_assembler', sma, intersect(state_names, prepare_next_trial_states)); From 4131fc08c50a9e67e92cefb1f5d72a1e127b2b42 Mon Sep 17 00:00:00 2001 From: Arpit Date: Wed, 25 Jun 2025 11:51:14 +0100 Subject: [PATCH 135/164] error debug for wrong fun name --- Protocols/@ArpitSoundCatContinuous/SideSection.m | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Protocols/@ArpitSoundCatContinuous/SideSection.m b/Protocols/@ArpitSoundCatContinuous/SideSection.m index cabb28e1..d087ad5b 100644 --- a/Protocols/@ArpitSoundCatContinuous/SideSection.m +++ b/Protocols/@ArpitSoundCatContinuous/SideSection.m @@ -221,7 +221,7 @@ case 'new_leftprob' - AntibiasSectionAthena(obj, 'update_biashitfrac', value(LeftProb)); + AntibiasSection(obj, 'update_biashitfrac', value(LeftProb)); case 'new_CP_duration' @@ -437,7 +437,7 @@ ThisTrial.value = 'LEFT'; end else - choiceprobs = AntibiasSectionAthena(obj, 'get_posterior_probs'); + choiceprobs = AntibiasSection(obj, 'get_posterior_probs'); if rand(1) <= choiceprobs(1) ThisTrial.value = 'LEFT'; else From 96edb61e75cb2933c78a01f043a1ae2c78f8a802 Mon Sep 17 00:00:00 2001 From: Arpit Date: Wed, 25 Jun 2025 16:40:42 +0100 Subject: [PATCH 136/164] changes in ArpitSoundCatcontinuous to stimulator section --- .../ArpitSoundCatContinuousSMA.m | 8 +- .../StimulatorSection.m | 133 +++++++++++------- 2 files changed, 86 insertions(+), 55 deletions(-) diff --git a/Protocols/@ArpitSoundCatContinuous/ArpitSoundCatContinuousSMA.m b/Protocols/@ArpitSoundCatContinuous/ArpitSoundCatContinuousSMA.m index fd29a07a..ee575f63 100644 --- a/Protocols/@ArpitSoundCatContinuous/ArpitSoundCatContinuousSMA.m +++ b/Protocols/@ArpitSoundCatContinuous/ArpitSoundCatContinuousSMA.m @@ -266,8 +266,12 @@ % After defining the states for behavior, adding states for % electrophysiology or LED stimulator. - sma = StimulatorSection(obj,'prepare_next_trial',sma); - % sma = add_trialnum_indicator(sma, n_done_trails); + + if strcmpi(value(StimLine),'Opto') + sma = StimulatorSection(obj,'prepare_next_trial',sma); + elseif strcmpi(value(StimLine),'Ephys') + sma = add_trialnum_indicator(sma, n_done_trails); + end dispatcher('send_assembler', sma, intersect(state_names, prepare_next_trial_states)); diff --git a/Protocols/@ArpitSoundCatContinuous/StimulatorSection.m b/Protocols/@ArpitSoundCatContinuous/StimulatorSection.m index 8421adbb..98a9791f 100644 --- a/Protocols/@ArpitSoundCatContinuous/StimulatorSection.m +++ b/Protocols/@ArpitSoundCatContinuous/StimulatorSection.m @@ -28,34 +28,13 @@ % ---------------------------------------------------------------- case 'init' - %NumeditParam(obj,'LCB_onstim', 0,x,y,'position',[x y 100 20],'labelfraction',0.6); - %NumeditParam(obj,'LCB_nostim', 0,x,y,'position',[x+100 y 100 20],'labelfraction',0.6); next_row(y); - %SoloParamHandle(obj,'LegalCBrk_temp', 'value',0); - - MenuParam(obj,'StimInterval',{'WholeTrial','S1','DelayDur','GoCue'},1,x,y,'labelfraction',0.30); next_row(y); - set_callback(StimInterval, {mfilename, 'StimInterval'}); - MenuParam(obj,'StimOnSide',{'both','left','right'},1,x,y,'labelfraction',0.3); next_row(y); - - diolines = bSettings('get','DIOLINES', 'all'); - for i = 1:size(diolines,1); dionames{i} = diolines{i,1}; dionums(i) = diolines{i,2}; end %#ok - [dionums order] = sort(dionums); - dionames2 = cell(0); - for i = 1:length(dionums); if ~isnan(dionums(i)); dionames2{end+1} = dionames{order(i)}; end; end %#ok - dionames2 = cell(0); - dionames2{1} = 'opto'; - - MenuParam(obj,'StimLine',dionames2,1,x,y,'labelfraction',0.30); next_row(y); - - SC = state_colors(obj); - WC = wave_colors(obj); - states = fieldnames(SC); - waves = fieldnames(WC); - states(2:end+1) = states; - states{1} = 'cp'; - states(end+1:end+length(waves)) = waves; - - MenuParam(obj,'StimState',states,1,x,y,'labelfraction',0.30); next_row(y); + % diolines = bSettings('get','DIOLINES', 'all'); + % for i = 1:size(diolines,1); dionames{i} = diolines{i,1}; dionums(i) = diolines{i,2}; end %#ok + % [dionums order] = sort(dionums); + % dionames2 = cell(0); + % for i = 1:length(dionums); if ~isnan(dionums(i)); dionames2{end+1} = dionames{order(i)}; end; end %#ok + NumeditParam(obj,'StimStates', 1,x,y,'position',[x y 100 20],'labelfraction',0.60); NumeditParam(obj,'StimLines', 1,x,y,'position',[x+100 y 100 20],'labelfraction',0.60); next_row(y); @@ -67,14 +46,43 @@ DispParam(obj,'SD',0 ,x,y,'position',[x y 50 20],'labelfraction',0.4); DispParam(obj,'SF',20,x,y,'position',[x+50 y 50 20],'labelfraction',0.4); DispParam(obj,'PW',15,x,y,'position',[x+100 y 50 20],'labelfraction',0.4); - DispParam(obj,'NP',1,x,y,'position',[x+150 y 50 20],'labelfraction',0.4); next_row(y); + DispParam(obj,'NP',1,x,y,'position',[x+150 y 50 20],'labelfraction',0.4); next_row(y); + + MenuParam(obj,'StimInterval',{'WholeCP_Duration','CP_DurationAfterPrestim','Prestim','S1','DelayDur','GoCue'},1,x,y,'labelfraction',0.30); next_row(y); + set_callback(StimInterval, {mfilename, 'StimInterval'}); + + MenuParam(obj,'StimOnSide',{'both','left','right'},1,x,y,'labelfraction',0.3); next_row(y); + SC = state_colors(obj); + WC = wave_colors(obj); + states = fieldnames(SC); + waves = fieldnames(WC); + states(2:end+1) = states; + states{1} = 'cp'; + states(end+1:end+length(waves)) = waves; + + MenuParam(obj,'StimState',states,1,x,y,'labelfraction',0.30); next_row(y); + NumeditParam(obj,'StimProb', 0,x,y,'position',[x y 100 20],'labelfraction',0.65); ToggleParam( obj,'ShuffleValues',0,x,y,'position',[x+100 y 100 20],'OnString','Shuffle','OffString','Lock'); next_row(y); + dionames2 = cell(0); + dionames2{1} = {'none','Opto','Ephys'}; + + MenuParam(obj,'StimLine',dionames2,1,x,y,'labelfraction',0.30); next_row(y); + set_callback(StimLine, {mfilename, 'StimSelected'}); + SoloParamHandle(obj, 'stimulator_history', 'value', []); - + + make_invisible(StimStates); make_invisible(StimLines); make_invisible(StartDelay); make_invisible(StimFreq); + make_invisible(PulseWidth); make_invisible(NumPulses); make_invisible(SD); make_invisible(SF); + make_invisible(PW); make_invisible(NP); make_invisible(StimInterval); make_invisible(StimOnSide); + make_invisible(StimState); make_invisible(StimProb); make_invisible(ShuffleValues); + SubheaderParam(obj, 'title', 'Stimulator Section', x, y); next_row(y); + + SoloFunctionAddVars('ArpitSoundCatContinuousSMA', 'ro_args',{'StimLine'}); + varargout{1} = x; varargout{2} = y; @@ -85,28 +93,40 @@ % % ----------------------------------------------------------------------- - case 'update_values' - - StimulatorSection(obj,'StimInterval'); - sh = value(stimulator_history); %#ok - %if n_done_trials == 0 || sh(end) == 0 - % LegalCBrk_temp.value = value(LegalCBrk); %#ok - %end - - if ~dispatcher('is_running') - %dispatcher is not running, last stim_hist not used, lop it off - sh = sh(1:end-1); - end - - if value(StimProb) == 0 - %LCB_nostim.value = value(LegalCBrk); %#ok - stimulator_history.value = [sh, 0]; - elseif rand(1) <= value(StimProb) %&& ((value(StimOnFree)==1 && strcmp(value(ThisTrial_Free),'FREE')) || value(StimOnFree)==0) - stimulator_history.value = [sh, 1]; - %LegalCBrk.value = value(LCB_onstim); + case 'StimSelected' + + if strcmpi(value(StimLine),'Opto') + make_visible(StimStates); make_visible(StimLines); make_visible(StartDelay); make_visible(StimFreq); + make_visible(PulseWidth); make_visible(NumPulses); make_visible(SD); make_visible(SF); + make_visible(PW); make_visible(NP); make_visible(StimInterval); make_visible(StimOnSide); + make_visible(StimState); make_visible(StimProb); make_visible(ShuffleValues); else - %LegalCBrk.value = value(LCB_nostim); %#ok - stimulator_history.value = [sh, 0]; + make_invisible(StimStates); make_invisible(StimLines); make_invisible(StartDelay); make_invisible(StimFreq); + make_invisible(PulseWidth); make_invisible(NumPulses); make_invisible(SD); make_invisible(SF); + make_invisible(PW); make_invisible(NP); make_invisible(StimInterval); make_invisible(StimOnSide); + make_invisible(StimState); make_invisible(StimProb); make_invisible(ShuffleValues); + end + + case 'update_values' + + if strcmpi(value(StimLine),'Opto') + + StimulatorSection(obj,'StimInterval'); + sh = value(stimulator_history); %#ok + + if ~dispatcher('is_running') + %dispatcher is not running, last stim_hist not used, lop it off + sh = sh(1:end-1); + end + + if value(StimProb) == 0 + stimulator_history.value = [sh, 0]; + elseif rand(1) <= value(StimProb) + stimulator_history.value = [sh, 1]; + else + stimulator_history.value = [sh, 0]; + end + end @@ -231,10 +251,18 @@ %% Case StimInterval case 'StimInterval' - if strcmp(StimInterval, 'WholeTrial') + if strcmp(StimInterval, 'WholeCP_Duration') PulseWidth.value = Total_CP_duration*1000; StimFreq.value = 1000/PulseWidth; + StartDelay.value = 0; + elseif strcmp(StimInterval, 'CP_DurationAfterPrestim') + PulseWidth.value = (Total_CP_duration - PreStim_time)*1000; + StimFreq.value = 1000/PulseWidth; StartDelay.value = PreStim_time; + elseif strcmp(StimInterval, 'Prestim') + PulseWidth.value = PreStim_time*1000; + StimFreq.value = 1000/PulseWidth; + StartDelay.value = 0; elseif strcmp(StimInterval, 'S1') PulseWidth.value = A1_time*1000; StimFreq.value = 1000/PulseWidth; @@ -246,8 +274,7 @@ elseif strcmp(StimInterval, 'GoCue') PulseWidth.value = time_go_cue*1000; StimFreq.value = 1000/PulseWidth; - StartDelay.value = PreStim_time + A1_time + time_bet_aud1_gocue; - + StartDelay.value = PreStim_time + A1_time + time_bet_aud1_gocue; end %% set % ----------------------------------------------------------------------- From ba0db7528d105a8905307ab30091b2bc04781760 Mon Sep 17 00:00:00 2001 From: Arpit Date: Wed, 25 Jun 2025 17:03:01 +0100 Subject: [PATCH 137/164] error debug --- .../add_trialnum_indicator.asv | 146 ++++++++++++++++++ .../add_trialnum_indicator.m | 6 +- .../ArpitSoundCatContinuousSMA.m | 2 +- .../StimulatorSection.m | 5 +- 4 files changed, 152 insertions(+), 7 deletions(-) create mode 100644 ExperPort/Modules/@StateMachineAssembler/add_trialnum_indicator.asv diff --git a/ExperPort/Modules/@StateMachineAssembler/add_trialnum_indicator.asv b/ExperPort/Modules/@StateMachineAssembler/add_trialnum_indicator.asv new file mode 100644 index 00000000..906c8617 --- /dev/null +++ b/ExperPort/Modules/@StateMachineAssembler/add_trialnum_indicator.asv @@ -0,0 +1,146 @@ +% [sma] = add_trialnum_indicator(sma, trialnum, {'indicator_states_name', 'sending_trialnum_data'}, ... +% {'time_per_state', 3e-4}, {'preamble', [1]}, 'DIOLINE', 'from_settings') +% +% The intent behind this method is to provide a time-sync signal on a DIO +% line, indicating start of a trial, for use by neural recording systems. +% That same signal is also used to indicate the trial number. +% +% When this method is called, the sma will be modified so that state_0 +% will not jump straight to the user's first state, but will instead first +% go through a very swift sequence of states that put out, on one of the DIO +% lines, a binary signature of the number trialnum; after that, there is a +% jump to the user's first state. None of the user-defined states are +% changed by a call to this method. +% +% The signal on the DIO line will be: High, +% followed by a a 15-bit binary representation of trialnum (1 is High, 0 is +% Low), with most significant bit sent out first. That is, we go through a +% total of 16 states that cover both the initial sync signal and the +% trialnum. The default is to go through each of these states in 1/3th of a +% ms, twice the FSM's cycle time. This +% can be modified using the optional argument 'time_per_state'. All of +% these added states will have the name 'sending_trialnum_data'. +% +% This method can ONLY be used with StateMachineAssemblers that were +% initialized with the 'full_trial_structure' flag. +% +% The DIO line on which all this will happen is determined, by default, +% using the Settings.m system, with DIOLINES; trialnum_indicator being the +% setting name. For example, the line +% DIOLINES; trialnum_indicator; 32 +% in Settings_Custom.conf would mean DIO line 6 (in binary, 32 is 100000, +% so it is the 6th bit). If no such setting is found, no DOut is generated. +% +% +% RETURNS: +% -------- +% +% sma The updated State Machine Assembler object, after the +% trialnum-sending states have been added. +% +% +% PARAMETERS: +% ----------- +% +% sma The instantiation of the StateMachineAssembler object to which +% the new states will be added. +% +% +% OPTIONAL PARAMETERS: +% -------------------- +% +% 'time_per_state' A scalar positive number indicating the time, in +% seconds, that will be spent on each of the states, that put out +% a signal. Default is 1/3 of a ms. Total time for all added +% states will be time_per_state*(15+length(preamble)). +% +% 'preamble' Sync signal that is sent before the trialnum. Default is +% [1], meaning a single High bit. +% +% 'indicator_states_name' String that defines the name of all the states +% that will be added by this method. Default is +% 'sending_trialnum_data'. +% +% 'DIOLINE' Default value is the string 'from_settings', which indicates +% that the Settings.m system should be used to find setting named +% DIOLINES; trialnum_indicator. However, you can use this optional +% parameter to override the value from the settings files. +% + +% Written by Carlos Brody Aug 2007 + +function [sma] = add_trialnum_indicator(sma, trialnum, varargin) + + time_per_state = []; % hack: time_per_state is a function. unfortunately, assignin (called by parseargs below) will fail when what it is assigning already exists with some meaning. This must be fixed. Perhaps if evalin does not have the same problem, we can evalin to 0 first, then assignin. -s & CB + pairs = { ... + 'time_per_state' 800e-6 ; ... + 'preamble' [1] ; ... + 'indicator_states_name' 'sending_trialnum_data' ; ... + 'DIOLINE' 'from_settings' ; ... + }; parseargs(varargin, pairs); + +% NOTE: even though 'time_per_state' is 0.8 ms, the actual time per state is +% 1 ms, which is the nearest multiple of the FSM clock. +% Experimentation shows that requesting exactly 0.5 ms often gives states +% that are 1 ms but sometimes 0.5 ms, but asking for 0.8 gives 1 + + nbits = 15; % This is the number of bits used to encode trialnum. + + if strcmp(DIOLINE, 'from_settings'), + % Try the settings system; if any problem, set to zero meaning go + % through states, but do nothing. + try, DIOLINE = bSettings('get', 'DIOLINES', 'trialnum_indicator'); + catch, DIOLINE = 0; + return + end; + end; + if isnan(DIOLINE), DIOLINE = 0; end; + + % --- BEGIN error_checking --- + if ~is_full_trial_structure(sma), + error(['Sorry, ' mfilename ' can only be used with StateMachineAssemblers ' ... + 'initialized with the ''full_trial_structure'' flag on']); + end; + if nargin < 2, error('Need at least two args, sma and trialnum'); end; + % --- END error checking --- + + orig_current_state = sma.current_state; + % Temporarily set current_state marker to state 1, so we start adding + % states there: + sma.current_state = 1; + + % Now add the set of states going through the signal: + dout = preamble(1); % The preamble is a numeric vector. + sma = add_state(sma, 'name', indicator_states_name, 'self_timer', time_per_state, ... + 'input_to_statechange', {'Tup', 'current_state+1'}, ... + 'output_actions', {'DOut', sma.default_DOut + dout*DIOLINE}); + + for i=2:length(preamble) + dout = preamble(i); % The preamble is a numeric vector. + sma = add_state(sma, 'self_timer', time_per_state, ... + 'input_to_statechange', {'Tup', 'current_state+1'}, ... + 'output_actions', {'DOut', sma.default_DOut + dout*DIOLINE}); + end; + trialnum = dec2bin(trialnum); + trialnum = ['0'*ones(1, nbits-length(trialnum)) trialnum]; + for i=1:length(trialnum), + dout = str2num(trialnum(i)); % trialnum at this point is char vector + sma = add_state(sma, 'self_timer', time_per_state, ... + 'input_to_statechange', {'Tup', 'current_state+1'}, ... + 'output_actions', {'DOut', sma.default_DOut + dout*DIOLINE}); + end; + + % Now change the Tup action of the last added state to jump to state 40, + % the regular first user state in 'full_trial_structure'. + TupCol = find(strcmp('Tup', sma.input_map(:,1))); TupCol = sma.input_map{TupCol,2}; + sma.states{sma.current_state, TupCol} = 40; + + % Now change state_0 so it jumps to state 1 instead of state 40: + all_input_cols = cell2mat(sma.input_map(:,2))'; + for i=1:length(all_input_cols), sma.states{1,all_input_cols(i)} = 1; end; + + % We're done: return the current_state marker to its proper value. + sma.current_state = orig_current_state; + + return; + \ No newline at end of file diff --git a/ExperPort/Modules/@StateMachineAssembler/add_trialnum_indicator.m b/ExperPort/Modules/@StateMachineAssembler/add_trialnum_indicator.m index f93b266b..906c8617 100644 --- a/ExperPort/Modules/@StateMachineAssembler/add_trialnum_indicator.m +++ b/ExperPort/Modules/@StateMachineAssembler/add_trialnum_indicator.m @@ -1,4 +1,4 @@ -% [sma] = add_trialnum_indicator(sma, trialnum, {'indicator_states_name', 'sending_trialnum'}, ... +% [sma] = add_trialnum_indicator(sma, trialnum, {'indicator_states_name', 'sending_trialnum_data'}, ... % {'time_per_state', 3e-4}, {'preamble', [1]}, 'DIOLINE', 'from_settings') % % The intent behind this method is to provide a time-sync signal on a DIO @@ -19,7 +19,7 @@ % trialnum. The default is to go through each of these states in 1/3th of a % ms, twice the FSM's cycle time. This % can be modified using the optional argument 'time_per_state'. All of -% these added states will have the name 'sending_trialnum'. +% these added states will have the name 'sending_trialnum_data'. % % This method can ONLY be used with StateMachineAssemblers that were % initialized with the 'full_trial_structure' flag. @@ -75,7 +75,7 @@ pairs = { ... 'time_per_state' 800e-6 ; ... 'preamble' [1] ; ... - 'indicator_states_name' 'sending_trialnum' ; ... + 'indicator_states_name' 'sending_trialnum_data' ; ... 'DIOLINE' 'from_settings' ; ... }; parseargs(varargin, pairs); diff --git a/Protocols/@ArpitSoundCatContinuous/ArpitSoundCatContinuousSMA.m b/Protocols/@ArpitSoundCatContinuous/ArpitSoundCatContinuousSMA.m index ee575f63..edb30239 100644 --- a/Protocols/@ArpitSoundCatContinuous/ArpitSoundCatContinuousSMA.m +++ b/Protocols/@ArpitSoundCatContinuous/ArpitSoundCatContinuousSMA.m @@ -270,7 +270,7 @@ if strcmpi(value(StimLine),'Opto') sma = StimulatorSection(obj,'prepare_next_trial',sma); elseif strcmpi(value(StimLine),'Ephys') - sma = add_trialnum_indicator(sma, n_done_trails); + sma = add_trialnum_indicator(sma, n_done_trials); end dispatcher('send_assembler', sma, intersect(state_names, prepare_next_trial_states)); diff --git a/Protocols/@ArpitSoundCatContinuous/StimulatorSection.m b/Protocols/@ArpitSoundCatContinuous/StimulatorSection.m index 98a9791f..8c28aa6c 100644 --- a/Protocols/@ArpitSoundCatContinuous/StimulatorSection.m +++ b/Protocols/@ArpitSoundCatContinuous/StimulatorSection.m @@ -66,10 +66,9 @@ NumeditParam(obj,'StimProb', 0,x,y,'position',[x y 100 20],'labelfraction',0.65); ToggleParam( obj,'ShuffleValues',0,x,y,'position',[x+100 y 100 20],'OnString','Shuffle','OffString','Lock'); next_row(y); - dionames2 = cell(0); - dionames2{1} = {'none','Opto','Ephys'}; + dionames = {'none','Opto','Ephys'}; - MenuParam(obj,'StimLine',dionames2,1,x,y,'labelfraction',0.30); next_row(y); + MenuParam(obj,'StimLine',dionames,1,x,y,'labelfraction',0.30); next_row(y); set_callback(StimLine, {mfilename, 'StimSelected'}); SoloParamHandle(obj, 'stimulator_history', 'value', []); From 9f0273e6bdaa1d7b79947d0c423213cc3f9d2921 Mon Sep 17 00:00:00 2001 From: Arpit Date: Thu, 26 Jun 2025 11:36:54 +0100 Subject: [PATCH 138/164] added trialnum indicator for ephys sychronization --- .../add_trialnum_indicator.asv | 146 ------------------ .../add_trialnum_indicator.m | 6 +- .../@bonsaicamera/BonsaiCameraInterface.m | 2 +- .../ArpitSoundCatContinuous.m | 11 +- .../ArpitSoundCatContinuousSMA.m | 6 +- .../StimulatorSection.m | 6 + 6 files changed, 18 insertions(+), 159 deletions(-) delete mode 100644 ExperPort/Modules/@StateMachineAssembler/add_trialnum_indicator.asv diff --git a/ExperPort/Modules/@StateMachineAssembler/add_trialnum_indicator.asv b/ExperPort/Modules/@StateMachineAssembler/add_trialnum_indicator.asv deleted file mode 100644 index 906c8617..00000000 --- a/ExperPort/Modules/@StateMachineAssembler/add_trialnum_indicator.asv +++ /dev/null @@ -1,146 +0,0 @@ -% [sma] = add_trialnum_indicator(sma, trialnum, {'indicator_states_name', 'sending_trialnum_data'}, ... -% {'time_per_state', 3e-4}, {'preamble', [1]}, 'DIOLINE', 'from_settings') -% -% The intent behind this method is to provide a time-sync signal on a DIO -% line, indicating start of a trial, for use by neural recording systems. -% That same signal is also used to indicate the trial number. -% -% When this method is called, the sma will be modified so that state_0 -% will not jump straight to the user's first state, but will instead first -% go through a very swift sequence of states that put out, on one of the DIO -% lines, a binary signature of the number trialnum; after that, there is a -% jump to the user's first state. None of the user-defined states are -% changed by a call to this method. -% -% The signal on the DIO line will be: High, -% followed by a a 15-bit binary representation of trialnum (1 is High, 0 is -% Low), with most significant bit sent out first. That is, we go through a -% total of 16 states that cover both the initial sync signal and the -% trialnum. The default is to go through each of these states in 1/3th of a -% ms, twice the FSM's cycle time. This -% can be modified using the optional argument 'time_per_state'. All of -% these added states will have the name 'sending_trialnum_data'. -% -% This method can ONLY be used with StateMachineAssemblers that were -% initialized with the 'full_trial_structure' flag. -% -% The DIO line on which all this will happen is determined, by default, -% using the Settings.m system, with DIOLINES; trialnum_indicator being the -% setting name. For example, the line -% DIOLINES; trialnum_indicator; 32 -% in Settings_Custom.conf would mean DIO line 6 (in binary, 32 is 100000, -% so it is the 6th bit). If no such setting is found, no DOut is generated. -% -% -% RETURNS: -% -------- -% -% sma The updated State Machine Assembler object, after the -% trialnum-sending states have been added. -% -% -% PARAMETERS: -% ----------- -% -% sma The instantiation of the StateMachineAssembler object to which -% the new states will be added. -% -% -% OPTIONAL PARAMETERS: -% -------------------- -% -% 'time_per_state' A scalar positive number indicating the time, in -% seconds, that will be spent on each of the states, that put out -% a signal. Default is 1/3 of a ms. Total time for all added -% states will be time_per_state*(15+length(preamble)). -% -% 'preamble' Sync signal that is sent before the trialnum. Default is -% [1], meaning a single High bit. -% -% 'indicator_states_name' String that defines the name of all the states -% that will be added by this method. Default is -% 'sending_trialnum_data'. -% -% 'DIOLINE' Default value is the string 'from_settings', which indicates -% that the Settings.m system should be used to find setting named -% DIOLINES; trialnum_indicator. However, you can use this optional -% parameter to override the value from the settings files. -% - -% Written by Carlos Brody Aug 2007 - -function [sma] = add_trialnum_indicator(sma, trialnum, varargin) - - time_per_state = []; % hack: time_per_state is a function. unfortunately, assignin (called by parseargs below) will fail when what it is assigning already exists with some meaning. This must be fixed. Perhaps if evalin does not have the same problem, we can evalin to 0 first, then assignin. -s & CB - pairs = { ... - 'time_per_state' 800e-6 ; ... - 'preamble' [1] ; ... - 'indicator_states_name' 'sending_trialnum_data' ; ... - 'DIOLINE' 'from_settings' ; ... - }; parseargs(varargin, pairs); - -% NOTE: even though 'time_per_state' is 0.8 ms, the actual time per state is -% 1 ms, which is the nearest multiple of the FSM clock. -% Experimentation shows that requesting exactly 0.5 ms often gives states -% that are 1 ms but sometimes 0.5 ms, but asking for 0.8 gives 1 - - nbits = 15; % This is the number of bits used to encode trialnum. - - if strcmp(DIOLINE, 'from_settings'), - % Try the settings system; if any problem, set to zero meaning go - % through states, but do nothing. - try, DIOLINE = bSettings('get', 'DIOLINES', 'trialnum_indicator'); - catch, DIOLINE = 0; - return - end; - end; - if isnan(DIOLINE), DIOLINE = 0; end; - - % --- BEGIN error_checking --- - if ~is_full_trial_structure(sma), - error(['Sorry, ' mfilename ' can only be used with StateMachineAssemblers ' ... - 'initialized with the ''full_trial_structure'' flag on']); - end; - if nargin < 2, error('Need at least two args, sma and trialnum'); end; - % --- END error checking --- - - orig_current_state = sma.current_state; - % Temporarily set current_state marker to state 1, so we start adding - % states there: - sma.current_state = 1; - - % Now add the set of states going through the signal: - dout = preamble(1); % The preamble is a numeric vector. - sma = add_state(sma, 'name', indicator_states_name, 'self_timer', time_per_state, ... - 'input_to_statechange', {'Tup', 'current_state+1'}, ... - 'output_actions', {'DOut', sma.default_DOut + dout*DIOLINE}); - - for i=2:length(preamble) - dout = preamble(i); % The preamble is a numeric vector. - sma = add_state(sma, 'self_timer', time_per_state, ... - 'input_to_statechange', {'Tup', 'current_state+1'}, ... - 'output_actions', {'DOut', sma.default_DOut + dout*DIOLINE}); - end; - trialnum = dec2bin(trialnum); - trialnum = ['0'*ones(1, nbits-length(trialnum)) trialnum]; - for i=1:length(trialnum), - dout = str2num(trialnum(i)); % trialnum at this point is char vector - sma = add_state(sma, 'self_timer', time_per_state, ... - 'input_to_statechange', {'Tup', 'current_state+1'}, ... - 'output_actions', {'DOut', sma.default_DOut + dout*DIOLINE}); - end; - - % Now change the Tup action of the last added state to jump to state 40, - % the regular first user state in 'full_trial_structure'. - TupCol = find(strcmp('Tup', sma.input_map(:,1))); TupCol = sma.input_map{TupCol,2}; - sma.states{sma.current_state, TupCol} = 40; - - % Now change state_0 so it jumps to state 1 instead of state 40: - all_input_cols = cell2mat(sma.input_map(:,2))'; - for i=1:length(all_input_cols), sma.states{1,all_input_cols(i)} = 1; end; - - % We're done: return the current_state marker to its proper value. - sma.current_state = orig_current_state; - - return; - \ No newline at end of file diff --git a/ExperPort/Modules/@StateMachineAssembler/add_trialnum_indicator.m b/ExperPort/Modules/@StateMachineAssembler/add_trialnum_indicator.m index 906c8617..f93b266b 100644 --- a/ExperPort/Modules/@StateMachineAssembler/add_trialnum_indicator.m +++ b/ExperPort/Modules/@StateMachineAssembler/add_trialnum_indicator.m @@ -1,4 +1,4 @@ -% [sma] = add_trialnum_indicator(sma, trialnum, {'indicator_states_name', 'sending_trialnum_data'}, ... +% [sma] = add_trialnum_indicator(sma, trialnum, {'indicator_states_name', 'sending_trialnum'}, ... % {'time_per_state', 3e-4}, {'preamble', [1]}, 'DIOLINE', 'from_settings') % % The intent behind this method is to provide a time-sync signal on a DIO @@ -19,7 +19,7 @@ % trialnum. The default is to go through each of these states in 1/3th of a % ms, twice the FSM's cycle time. This % can be modified using the optional argument 'time_per_state'. All of -% these added states will have the name 'sending_trialnum_data'. +% these added states will have the name 'sending_trialnum'. % % This method can ONLY be used with StateMachineAssemblers that were % initialized with the 'full_trial_structure' flag. @@ -75,7 +75,7 @@ pairs = { ... 'time_per_state' 800e-6 ; ... 'preamble' [1] ; ... - 'indicator_states_name' 'sending_trialnum_data' ; ... + 'indicator_states_name' 'sending_trialnum' ; ... 'DIOLINE' 'from_settings' ; ... }; parseargs(varargin, pairs); diff --git a/ExperPort/Plugins/@bonsaicamera/BonsaiCameraInterface.m b/ExperPort/Plugins/@bonsaicamera/BonsaiCameraInterface.m index dcb30cab..5350b7f1 100644 --- a/ExperPort/Plugins/@bonsaicamera/BonsaiCameraInterface.m +++ b/ExperPort/Plugins/@bonsaicamera/BonsaiCameraInterface.m @@ -180,7 +180,7 @@ % main_dir_video = [current_dir 'ratter_Videos']; current_dir = cd; ratter_dir = extractBefore(current_dir,'ratter'); - main_dir_video = [ratter_dir '\ratter\training_videos']; + main_dir_video = fullfile(ratter_dir, 'ratter','training_videos'); date_str = regexprep(char(datetime('today','Format','yyyy-MM-dd')), '[^0-9]', ''); video_foldername = sprintf('video_@%s_%s_%s_%s',protocol_name,experimenter_name,rat_name,date_str); rat_dir = sprintf('%s\\%s\\%s',main_dir_video,experimenter_name,rat_name); diff --git a/Protocols/@ArpitSoundCatContinuous/ArpitSoundCatContinuous.m b/Protocols/@ArpitSoundCatContinuous/ArpitSoundCatContinuous.m index d8e8c2d5..76928b7a 100644 --- a/Protocols/@ArpitSoundCatContinuous/ArpitSoundCatContinuous.m +++ b/Protocols/@ArpitSoundCatContinuous/ArpitSoundCatContinuous.m @@ -109,14 +109,13 @@ HeaderParam(obj, 'prot_title', [mfilename ': ' expmtr ', ' rname], x, y, 'position', [10 figpos(4)-25, 800 20]); [x, y] = WaterValvesSection(obj, 'init', x, y);next_row(y); - [x, y] = PokesPlotSection(obj, 'init', x, y);next_row(y); + % [x, y] = PokesPlotSection(obj, 'init', x, y);next_row(y); [x, y] = CommentsSection(obj, 'init', x, y);next_row(y); [x, y] = BonsaiCameraInterface(obj,'init',x,y,name,expmtr,rname);next_row(y); oldx=x; oldy=y; - - + next_column(x); y=5; [x, y] = SideSection(obj, 'init', x, y); %#ok @@ -154,7 +153,7 @@ SideSection(obj, 'prepare_next_trial'); % Run SessionDefinition *after* SideSection so we know whether the % trial was a violation or not - % SessionDefinition(obj, 'next_trial'); + SessionDefinition(obj, 'next_trial'); StimulatorSection(obj, 'update_values'); StimulusSection(obj,'prepare_next_trial'); SoundManagerSection(obj, 'send_not_yet_uploaded_sounds'); @@ -186,7 +185,7 @@ %% update case 'update' - PokesPlotSection(obj, 'update'); + % PokesPlotSection(obj, 'update'); if n_done_trials==1 [expmtr, rname]=SavingSection(obj, 'get_info'); prot_title.value=[mfilename ' on rig ' get_hostname ' : ' expmtr ', ' rname '. Started at ' datestr(now, 'HH:MM')]; @@ -194,7 +193,7 @@ %% close case 'close' - PokesPlotSection(obj, 'close'); + % PokesPlotSection(obj, 'close'); SideSection(obj, 'close'); StimulusSection(obj,'close'); BonsaiCameraInterface(obj,'close'); diff --git a/Protocols/@ArpitSoundCatContinuous/ArpitSoundCatContinuousSMA.m b/Protocols/@ArpitSoundCatContinuous/ArpitSoundCatContinuousSMA.m index edb30239..197d72c7 100644 --- a/Protocols/@ArpitSoundCatContinuous/ArpitSoundCatContinuousSMA.m +++ b/Protocols/@ArpitSoundCatContinuous/ArpitSoundCatContinuousSMA.m @@ -262,15 +262,15 @@ % stages. So we send to dispatcher only those states that are % defined. state_names = get_labels(sma); state_names = state_names(:,1); - prepare_next_trial_states = {'side_led_wait_RewardCollection','hit_state','second_hit_state','drink_state', 'violation_state','timeout_state','preclean_up_state'}; + prepare_next_trial_states = {'side_led_wait_RewardCollection','hit_state','second_hit_state','drink_state', 'violation_state','timeout_state'}; % After defining the states for behavior, adding states for % electrophysiology or LED stimulator. if strcmpi(value(StimLine),'Opto') sma = StimulatorSection(obj,'prepare_next_trial',sma); - elseif strcmpi(value(StimLine),'Ephys') - sma = add_trialnum_indicator(sma, n_done_trials); + % elseif strcmpi(value(StimLine),'Ephys') + % sma = add_trialnum_indicator(sma, n_done_trials+1); end dispatcher('send_assembler', sma, intersect(state_names, prepare_next_trial_states)); diff --git a/Protocols/@ArpitSoundCatContinuous/StimulatorSection.m b/Protocols/@ArpitSoundCatContinuous/StimulatorSection.m index 8c28aa6c..d884292f 100644 --- a/Protocols/@ArpitSoundCatContinuous/StimulatorSection.m +++ b/Protocols/@ArpitSoundCatContinuous/StimulatorSection.m @@ -106,6 +106,12 @@ make_invisible(StimState); make_invisible(StimProb); make_invisible(ShuffleValues); end + if strcmpi(value(StimLine),'Ephys') + dispatcher('set_trialnum_indicator_flag'); + else + dispatcher('unset_trialnum_indicator_flag'); + end + case 'update_values' if strcmpi(value(StimLine),'Opto') From 2de2fe629cb9a1492a2b761fa996140579a6797b Mon Sep 17 00:00:00 2001 From: Arpit Date: Thu, 26 Jun 2025 16:16:41 +0100 Subject: [PATCH 139/164] changes to GUI of ArpitSoundCat --- .../ArpitSoundCatContinuous.m | 6 +- .../ArpitSoundCatContinuousSMA.m | 6 +- .../@ArpitSoundCatContinuous/SideSection.m | 60 ++++--- .../StimulusSection.m | 148 ++++++++---------- 4 files changed, 108 insertions(+), 112 deletions(-) diff --git a/Protocols/@ArpitSoundCatContinuous/ArpitSoundCatContinuous.m b/Protocols/@ArpitSoundCatContinuous/ArpitSoundCatContinuous.m index 76928b7a..2931c2af 100644 --- a/Protocols/@ArpitSoundCatContinuous/ArpitSoundCatContinuous.m +++ b/Protocols/@ArpitSoundCatContinuous/ArpitSoundCatContinuous.m @@ -119,11 +119,11 @@ next_column(x); y=5; [x, y] = SideSection(obj, 'init', x, y); %#ok - [x, y] = SoundSection(obj,'init',x,y); - [x, y] = StimulusSection(obj,'init',x,y); + % [x, y] = SoundSection(obj,'init',x,y); + % [x, y] = StimulusSection(obj,'init',x,y); [x, y] = PerformanceSection(obj, 'init', x, y); - [x, y] = StimulatorSection(obj, 'init', x, y); next_row(y, 1.3); + % [x, y] = StimulatorSection(obj, 'init', x, y); next_row(y, 1.3); x=oldx; y=oldy; diff --git a/Protocols/@ArpitSoundCatContinuous/ArpitSoundCatContinuousSMA.m b/Protocols/@ArpitSoundCatContinuous/ArpitSoundCatContinuousSMA.m index 197d72c7..9f0d5fa9 100644 --- a/Protocols/@ArpitSoundCatContinuous/ArpitSoundCatContinuousSMA.m +++ b/Protocols/@ArpitSoundCatContinuous/ArpitSoundCatContinuousSMA.m @@ -204,13 +204,13 @@ % always or with delay or no reward at all for error event if strcmp(reward_type, 'Always') - + sma = add_state(sma,'name','second_hit_state','self_timer',RewardCollection_duration,... 'output_actions',{'DOut', second_hit_light},... 'input_to_statechange',{'reward_collection_dur_In', 'timeout_state'; 'Tup','timeout_state'; HitEvent,'hit_state'}); - + elseif strcmp(reward_type, 'DelayedReward') - + sma = add_state(sma,'name','second_hit_state','self_timer',secondhit_delay,... 'input_to_statechange',{'Tup','current_state + 1';}); diff --git a/Protocols/@ArpitSoundCatContinuous/SideSection.m b/Protocols/@ArpitSoundCatContinuous/SideSection.m index d087ad5b..c05ab714 100644 --- a/Protocols/@ArpitSoundCatContinuous/SideSection.m +++ b/Protocols/@ArpitSoundCatContinuous/SideSection.m @@ -55,12 +55,15 @@ next_row(y); DispParam(obj, 'ThisTrial', 'LEFT', x, y); next_row(y); - + SubheaderParam(obj, 'title', 'Sides Section', x, y);next_row(y); + + %% Stimulator Section + [x, y] = StimulatorSection(obj, 'init', x, y); next_row(y, 1.3); + %% SoloParamHandle(obj, 'previous_sides', 'value', []); DeclareGlobals(obj, 'ro_args', 'previous_sides'); - SubheaderParam(obj, 'title', 'Sides Section', x, y); - next_row(y, 1.5); - + + next_row(y, 1.5); next_column(x); y = 5; % Reward Collection @@ -107,8 +110,14 @@ NumeditParam(obj, 'PreStim_time_Min', 0.20, x,y,'label','Pre-Stim Min','TooltipString','Min Time in NIC before starting the stimulus'); set_callback(PreStim_time_Min, {mfilename, 'new_CP_duration'}); next_row(y); - NumeditParam(obj, 'PreStim_time', 0.20, x,y,'label','Pre-Stim time','TooltipString','Actual Time in NIC before starting the stimulus'); + NumeditParam(obj, 'PreStim_time', 0.20, [x, y, 175 20],'label','Pre-Stim time','TooltipString','Actual Time in NIC before starting the stimulus'); set_callback(PreStim_time, {mfilename, 'new_CP_duration'}); + % Toggle Buttons To Control Parameters + ToggleParam(obj, 'random_PreStim_time', 1, [x+180 y 20 20],... + 'OnString', 'random PreStim_time ON',... + 'OffString', 'random PreStim_time OFF',... + 'TooltipString', sprintf('If on (black) then it enables the random time between the user given range')); + set_callback(random_PreStim_time, {mfilename, 'new_CP_duration'}); next_row(y); NumeditParam(obj, 'PreStim_time_Max', 1, x,y,'label','Pre-Stim Max','TooltipString','Max Time in NIC before starting the stimulus'); set_callback(PreStim_time_Max, {mfilename, 'new_CP_duration'}); @@ -117,8 +126,14 @@ NumeditParam(obj, 'A1_time_Min', 0.1, x,y,'label','Min time AUD1','TooltipString','Min value to select the Duration of fixed stimulus'); set_callback(A1_time_Min, {mfilename, 'new_CP_duration'}); next_row(y); - NumeditParam(obj, 'A1_time', 0.4, x,y,'label','AUD1 Time','TooltipString','Actual Duration of fixed stimulus'); + NumeditParam(obj, 'A1_time', 0.4, [x, y, 175 20],'label','AUD1 Time','TooltipString','Actual Duration of fixed stimulus'); set_callback(A1_time, {mfilename, 'new_CP_duration'}); + % Toggle Buttons To Control Parameters + ToggleParam(obj, 'random_A1_time', 1, [x+180 y 20 20],... + 'OnString', 'random A1_time ON',... + 'OffString', 'random A1_time OFF',... + 'TooltipString', sprintf('If on (black) then it enables the random sampling of A1_time')); + set_callback(random_A1_time, {mfilename, 'new_CP_duration'}); next_row(y); NumeditParam(obj, 'A1_time_Max', 0.4, x,y,'label','Max time AUD1','TooltipString','Max value to select the Duration of fixed stimulus'); set_callback(A1_time_Max, {mfilename, 'new_CP_duration'}); @@ -127,8 +142,14 @@ NumeditParam(obj, 'time_bet_aud1_gocue_Min', 0.2, x,y,'label','Min A1-GoCue time','TooltipString','Min time between the end of the stimulus and the go cue '); set_callback(time_bet_aud1_gocue_Min, {mfilename, 'new_CP_duration'}); next_row(y); - NumeditParam(obj, 'time_bet_aud1_gocue', 0.2, x,y,'label','A1-GoCue time','TooltipString','Actual time between the end of the stimulus and the go cue '); + NumeditParam(obj, 'time_bet_aud1_gocue', [x, y, 175 20], x,y,'label','A1-GoCue time','TooltipString','Actual time between the end of the stimulus and the go cue '); set_callback(time_bet_aud1_gocue, {mfilename, 'new_CP_duration'}); + % Toggle Buttons To Control Parameters + ToggleParam(obj, 'random_prego_time', 1, [x+180 y 20 20],... + 'OnString', 'random prego_time ON',... + 'OffString', 'random prego_time OFF',... + 'TooltipString', sprintf('If on (black) then it enables the random sampling of time between the end of the stimulus and the go cue')); + set_callback(random_prego_time, {mfilename, 'new_CP_duration'}); next_row(y); NumeditParam(obj, 'time_bet_aud1_gocue_Max', 1, x,y,'label','Max A1-GoCue time','TooltipString','Max time between the end of the stimulus and the go cue '); set_callback(time_bet_aud1_gocue_Max, {mfilename, 'new_CP_duration'}); @@ -147,30 +168,17 @@ DispParam(obj, 'Total_CP_duration', CP_duration+time_go_cue, x, y, 'TooltipString', 'Total nose in center port time, in secs. Sum of CP_duration and Go Cue duration'); %#ok<*NODEF> next_row(y); - % Toggle Buttons To Control Parameters - + % Toggle Buttons To Control Parameters ToggleParam(obj, 'stimuli_on', 1, x,y,... 'OnString', 'Stimuli ON',... 'OffString', 'Stimuli OFF',... 'TooltipString', sprintf('If on (black) then it enables training with stimuli else using a fixed sound from Stage 5')); next_row(y); - ToggleParam(obj, 'random_PreStim_time', 1, x,y,... - 'OnString', 'random PreStim_time ON',... - 'OffString', 'random PreStim_time OFF',... - 'TooltipString', sprintf('If on (black) then it enables the random time between the user given range')); - set_callback(random_PreStim_time, {mfilename, 'new_CP_duration'}); - next_row(y); - ToggleParam(obj, 'random_A1_time', 1, x,y,... - 'OnString', 'random A1_time ON',... - 'OffString', 'random A1_time OFF',... - 'TooltipString', sprintf('If on (black) then it enables the random sampling of A1_time')); - set_callback(random_A1_time, {mfilename, 'new_CP_duration'}); - next_row(y); - ToggleParam(obj, 'random_prego_time', 1, x,y,... - 'OnString', 'random prego_time ON',... - 'OffString', 'random prego_time OFF',... - 'TooltipString', sprintf('If on (black) then it enables the random sampling of time between the end of the stimulus and the go cue')); - set_callback(random_prego_time, {mfilename, 'new_CP_duration'}); + + %% Sound and Stimulus Section + [x, y] = SoundSection(obj,'init',x,y); next_row(y); + [x, y] = StimulusSection(obj,'init',x,y); next_row(y); + next_column(x); y=5; diff --git a/Protocols/@ArpitSoundCatContinuous/StimulusSection.m b/Protocols/@ArpitSoundCatContinuous/StimulusSection.m index d72dcdb1..ff37d4f9 100644 --- a/Protocols/@ArpitSoundCatContinuous/StimulusSection.m +++ b/Protocols/@ArpitSoundCatContinuous/StimulusSection.m @@ -211,106 +211,94 @@ stim_max_log = log(value(maxS1)); end - % if value(training_stage) == 4 % playing fixed sound during this stage - % if (strcmpi(ThisTrial, 'LEFT') & strcmp(Rule,'S1>S_boundary Left')) | ... - % (strcmpi(ThisTrial, 'RIGHT') & strcmp(Rule,'S1>S_boundary Right')) - % - % stim_i_log = stim_min_log; - % else - % stim_i_log = stim_max_log; - % end - % - % else % will be playing stimuli from the distribution - - if strcmpi(ThisTrial, 'LEFT') - dist_type = value(Prob_Dist_Left); - dist_mean = value(mean_Left); - dist_sigma = value(sigma_Left); - dist_range_multiplier = value(sigma_range_Left); - if strcmp(Rule,'S1>S_boundary Left') - edge_max = stim_max_log; - edge_min = value(boundary); - edge_max = edge_min + dist_range_multiplier * (edge_max - edge_min); - else % the rule is S1>S_boundary Right - edge_min = stim_min_log; - edge_max = value(boundary); - edge_min = edge_max - dist_range_multiplier * (edge_max - edge_min); - end + if strcmpi(ThisTrial, 'LEFT') + dist_type = value(Prob_Dist_Left); + dist_mean = value(mean_Left); + dist_sigma = value(sigma_Left); + dist_range_multiplier = value(sigma_range_Left); + if strcmp(Rule,'S1>S_boundary Left') + edge_max = stim_max_log; + edge_min = value(boundary); + edge_max = edge_min + dist_range_multiplier * (edge_max - edge_min); + else % the rule is S1>S_boundary Right + edge_min = stim_min_log; + edge_max = value(boundary); + edge_min = edge_max - dist_range_multiplier * (edge_max - edge_min); + end - else % trial is Right - dist_type = value(Prob_Dist_Right); - dist_mean = value(mean_Right); - dist_sigma = value(sigma_Right); - dist_range_multiplier = value(sigma_range_Right); - if strcmp(Rule,'S1>S_boundary Right') - edge_max = stim_max_log; - edge_min = value(boundary); - edge_max = edge_min + dist_range_multiplier * (edge_max - edge_min); - else % the rule is S1>S_boundary Left - edge_min = stim_min_log; - edge_max = value(boundary); - edge_min = edge_max - dist_range_multiplier * (edge_max - edge_min); - end + else % trial is Right + dist_type = value(Prob_Dist_Right); + dist_mean = value(mean_Right); + dist_sigma = value(sigma_Right); + dist_range_multiplier = value(sigma_range_Right); + if strcmp(Rule,'S1>S_boundary Right') + edge_max = stim_max_log; + edge_min = value(boundary); + edge_max = edge_min + dist_range_multiplier * (edge_max - edge_min); + else % the rule is S1>S_boundary Left + edge_min = stim_min_log; + edge_max = value(boundary); + edge_min = edge_max - dist_range_multiplier * (edge_max - edge_min); end + end - % Create a Stimuli with the selected Distribution and Side - switch dist_type + % Create a Stimuli with the selected Distribution and Side + switch dist_type - case 'Uniform' % uniform distribution - stim_i_log = random('Uniform',edge_min,edge_max); + case 'Uniform' % uniform distribution + stim_i_log = random('Uniform',edge_min,edge_max); - case 'Exponential' + case 'Exponential' - lambda = 2.153 * (edge_max - edge_min); % In mice they are using 2.153 for normalized stim range [0 1] - stim_i_log = edge_min - 1; % preinitialize for while loop - while stim_i_log < edge_min || stim_i_log > edge_max - U = rand(1); - if edge_min == value(boundary) % exponentially decreasing - stim_i_log = edge_min + (-(1/lambda)*log(U)); % the distribution would be between range [0 1], so added the edge_min - else - stim_i_log = edge_max - (-(1/lambda)*log(U)); - end + lambda = 2.153 * (edge_max - edge_min); % In mice they are using 2.153 for normalized stim range [0 1] + stim_i_log = edge_min - 1; % preinitialize for while loop + while stim_i_log < edge_min || stim_i_log > edge_max + U = rand(1); + if edge_min == value(boundary) % exponentially decreasing + stim_i_log = edge_min + (-(1/lambda)*log(U)); % the distribution would be between range [0 1], so added the edge_min + else + stim_i_log = edge_max - (-(1/lambda)*log(U)); end + end - case 'Anti Exponential' + case 'Anti Exponential' - lambda = 2.153 * (edge_max - edge_min); % In mice they are using 2.153 for normalized stim range [0 1] - stim_i_log = edge_min - 1; % preinitialize for while loop - while stim_i_log < edge_min || stim_i_log > edge_max - U = rand(1); - if edge_min == value(boundary) % exponentially decreasing - stim_i_log = edge_max - (-(1/lambda)*log(U)); % the distribution would be between range [0 1], so added the edge_min - else - stim_i_log = edge_min - ((1/lambda)*log(U)); - end + lambda = 2.153 * (edge_max - edge_min); % In mice they are using 2.153 for normalized stim range [0 1] + stim_i_log = edge_min - 1; % preinitialize for while loop + while stim_i_log < edge_min || stim_i_log > edge_max + U = rand(1); + if edge_min == value(boundary) % exponentially decreasing + stim_i_log = edge_max - (-(1/lambda)*log(U)); % the distribution would be between range [0 1], so added the edge_min + else + stim_i_log = edge_min - ((1/lambda)*log(U)); end + end - case 'Half Normal' - if edge_min == value(boundary) + case 'Half Normal' + if edge_min == value(boundary) + stim_i_log = random('Half Normal',dist_mean,dist_sigma); + while stim_i_log < edge_min || stim_i_log > edge_max stim_i_log = random('Half Normal',dist_mean,dist_sigma); - while stim_i_log < edge_min || stim_i_log > edge_max - stim_i_log = random('Half Normal',dist_mean,dist_sigma); - end - else - stim_i_log = CreateSamples_from_Distribution('normal',dist_mean,dist_sigma,edge_min,edge_max,1); end + else + stim_i_log = CreateSamples_from_Distribution('normal',dist_mean,dist_sigma,edge_min,edge_max,1); + end - case 'Anti Half Normal' + case 'Anti Half Normal' - stim_i_log = CreateSamples_from_Distribution('normal',dist_mean,dist_sigma,edge_min,edge_max,1); + stim_i_log = CreateSamples_from_Distribution('normal',dist_mean,dist_sigma,edge_min,edge_max,1); - case 'Normal' + case 'Normal' + stim_i_log = random('Normal',dist_mean,dist_sigma); + while stim_i_log < edge_min || stim_i_log > edge_max stim_i_log = random('Normal',dist_mean,dist_sigma); - while stim_i_log < edge_min || stim_i_log > edge_max - stim_i_log = random('Normal',dist_mean,dist_sigma); - end + end - case 'Sinusoidal' | 'Anti Sinusoidal' + case 'Sinusoidal' | 'Anti Sinusoidal' - stim_i_log = CreateSamples_from_Distribution('Sinusoidal',dist_mean,dist_sigma,edge_min,edge_max,1); + stim_i_log = CreateSamples_from_Distribution('Sinusoidal',dist_mean,dist_sigma,edge_min,edge_max,1); - end - % end + end thisstim.value=exp(stim_i_log); thisstimlog(n_done_trials+1) = stim_i_log; From b5e43618c55114c107b4f6985afd60d396f7f0f6 Mon Sep 17 00:00:00 2001 From: Arpit Date: Thu, 26 Jun 2025 17:06:41 +0100 Subject: [PATCH 140/164] error debug for previous changes --- .../.bonsai/Settings/Camera_Control.editor | 37 ++ .../.bonsai/Settings/Camera_Control.layout | 62 ++++ .../Camera_Control.bonsai.layout | 333 ------------------ .../ArpitSoundCatContinuous.m | 13 +- .../@ArpitSoundCatContinuous/SideSection.m | 28 +- 5 files changed, 117 insertions(+), 356 deletions(-) create mode 100644 ExperPort/Plugins/@bonsaicamera/.bonsai/Settings/Camera_Control.editor create mode 100644 ExperPort/Plugins/@bonsaicamera/.bonsai/Settings/Camera_Control.layout delete mode 100755 ExperPort/Plugins/@bonsaicamera/Camera_Control.bonsai.layout diff --git a/ExperPort/Plugins/@bonsaicamera/.bonsai/Settings/Camera_Control.editor b/ExperPort/Plugins/@bonsaicamera/.bonsai/Settings/Camera_Control.editor new file mode 100644 index 00000000..bbd6dca8 --- /dev/null +++ b/ExperPort/Plugins/@bonsaicamera/.bonsai/Settings/Camera_Control.editor @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/ExperPort/Plugins/@bonsaicamera/.bonsai/Settings/Camera_Control.layout b/ExperPort/Plugins/@bonsaicamera/.bonsai/Settings/Camera_Control.layout new file mode 100644 index 00000000..386f50b5 --- /dev/null +++ b/ExperPort/Plugins/@bonsaicamera/.bonsai/Settings/Camera_Control.layout @@ -0,0 +1,62 @@ + + + + false + + 94 + 95 + + + 333 + 63 + + Bonsai.Design.ObjectTextVisualizer + + + + + + false + + 472 + 106 + + + 333 + 63 + + Bonsai.Design.ObjectTextVisualizer + + + + + + + 15 + 705 + + + 333 + 277 + + Bonsai.Vision.Design.IplImageVisualizer + + + + + + false + + 95 + 246 + + + 333 + 177 + + Bonsai.Design.ObjectTextVisualizer + + + + + \ No newline at end of file diff --git a/ExperPort/Plugins/@bonsaicamera/Camera_Control.bonsai.layout b/ExperPort/Plugins/@bonsaicamera/Camera_Control.bonsai.layout deleted file mode 100755 index f260048f..00000000 --- a/ExperPort/Plugins/@bonsaicamera/Camera_Control.bonsai.layout +++ /dev/null @@ -1,333 +0,0 @@ - - - - false - - 0 - 0 - - - 0 - 0 - - Normal - - - false - - 0 - 0 - - - 0 - 0 - - Normal - - - false - - 94 - 95 - - - 333 - 63 - - Normal - Bonsai.Design.ObjectTextVisualizer - - - - - - false - - 0 - 0 - - - 0 - 0 - - Normal - - - false - - 0 - 0 - - - 0 - 0 - - Normal - - - false - - 0 - 0 - - - 0 - 0 - - Normal - - - false - - 0 - 0 - - - 0 - 0 - - Normal - - - false - - 0 - 0 - - - 0 - 0 - - Normal - - - false - - 636 - 274 - - - 333 - 277 - - Normal - - - false - - 472 - 106 - - - 333 - 63 - - Normal - Bonsai.Design.ObjectTextVisualizer - - - - - - false - - 0 - 0 - - - 0 - 0 - - Normal - - false - - 245 - 255 - - - 313 - 237 - - Normal - - - - true - - 15 - 705 - - - 333 - 277 - - Normal - Bonsai.Vision.Design.IplImageVisualizer - - - - - - false - - 0 - 0 - - - 0 - 0 - - Normal - - - false - - 95 - 246 - - - 333 - 177 - - Normal - Bonsai.Design.ObjectTextVisualizer - - - - - - false - - 98 - 102 - - - 333 - 63 - - Normal - - false - - 270 - 280 - - - 313 - 237 - - Normal - - - - false - - 0 - 0 - - - 0 - 0 - - Normal - - - false - - 0 - 0 - - - 0 - 0 - - Normal - - - false - - 0 - 0 - - - 0 - 0 - - Normal - - - false - - 0 - 0 - - - 0 - 0 - - Normal - - - false - - 0 - 0 - - - 0 - 0 - - Normal - - - false - - 0 - 0 - - - 0 - 0 - - Normal - - - false - - 0 - 0 - - - 0 - 0 - - Normal - - - false - - 0 - 0 - - - 0 - 0 - - Normal - - false - - 220 - 229 - - - 313 - 237 - - Normal - - - \ No newline at end of file diff --git a/Protocols/@ArpitSoundCatContinuous/ArpitSoundCatContinuous.m b/Protocols/@ArpitSoundCatContinuous/ArpitSoundCatContinuous.m index 2931c2af..69ef3055 100644 --- a/Protocols/@ArpitSoundCatContinuous/ArpitSoundCatContinuous.m +++ b/Protocols/@ArpitSoundCatContinuous/ArpitSoundCatContinuous.m @@ -109,24 +109,17 @@ HeaderParam(obj, 'prot_title', [mfilename ': ' expmtr ', ' rname], x, y, 'position', [10 figpos(4)-25, 800 20]); [x, y] = WaterValvesSection(obj, 'init', x, y);next_row(y); - % [x, y] = PokesPlotSection(obj, 'init', x, y);next_row(y); + [x, y] = PokesPlotSection(obj, 'init', x, y);next_row(y); [x, y] = CommentsSection(obj, 'init', x, y);next_row(y); [x, y] = BonsaiCameraInterface(obj,'init',x,y,name,expmtr,rname);next_row(y); - oldx=x; oldy=y; - - + oldx=x; oldy=y; next_column(x); y=5; [x, y] = SideSection(obj, 'init', x, y); %#ok - % [x, y] = SoundSection(obj,'init',x,y); - % [x, y] = StimulusSection(obj,'init',x,y); - + next_row(y, 1.3); [x, y] = PerformanceSection(obj, 'init', x, y); - % [x, y] = StimulatorSection(obj, 'init', x, y); next_row(y, 1.3); - x=oldx; y=oldy; - SessionDefinition(obj, 'init', x, y, value(myfig)); next_row(y, 2); %#ok ArpitSoundCatContinuousSMA(obj, 'init'); diff --git a/Protocols/@ArpitSoundCatContinuous/SideSection.m b/Protocols/@ArpitSoundCatContinuous/SideSection.m index c05ab714..b736295d 100644 --- a/Protocols/@ArpitSoundCatContinuous/SideSection.m +++ b/Protocols/@ArpitSoundCatContinuous/SideSection.m @@ -110,10 +110,10 @@ NumeditParam(obj, 'PreStim_time_Min', 0.20, x,y,'label','Pre-Stim Min','TooltipString','Min Time in NIC before starting the stimulus'); set_callback(PreStim_time_Min, {mfilename, 'new_CP_duration'}); next_row(y); - NumeditParam(obj, 'PreStim_time', 0.20, [x, y, 175 20],'label','Pre-Stim time','TooltipString','Actual Time in NIC before starting the stimulus'); + NumeditParam(obj, 'PreStim_time', 0.20,x,y, 'position', [x, y, 175 20],'label','Pre-Stim time','TooltipString','Actual Time in NIC before starting the stimulus'); set_callback(PreStim_time, {mfilename, 'new_CP_duration'}); % Toggle Buttons To Control Parameters - ToggleParam(obj, 'random_PreStim_time', 1, [x+180 y 20 20],... + ToggleParam(obj, 'random_PreStim_time', 1, x,y, 'position',[x+180 y 20 20],... 'OnString', 'random PreStim_time ON',... 'OffString', 'random PreStim_time OFF',... 'TooltipString', sprintf('If on (black) then it enables the random time between the user given range')); @@ -126,10 +126,10 @@ NumeditParam(obj, 'A1_time_Min', 0.1, x,y,'label','Min time AUD1','TooltipString','Min value to select the Duration of fixed stimulus'); set_callback(A1_time_Min, {mfilename, 'new_CP_duration'}); next_row(y); - NumeditParam(obj, 'A1_time', 0.4, [x, y, 175 20],'label','AUD1 Time','TooltipString','Actual Duration of fixed stimulus'); + NumeditParam(obj, 'A1_time', 0.4,x,y, 'position', [x, y, 175 20],'label','AUD1 Time','TooltipString','Actual Duration of fixed stimulus'); set_callback(A1_time, {mfilename, 'new_CP_duration'}); % Toggle Buttons To Control Parameters - ToggleParam(obj, 'random_A1_time', 1, [x+180 y 20 20],... + ToggleParam(obj, 'random_A1_time', 1, x,y, 'position',[x+180 y 20 20],... 'OnString', 'random A1_time ON',... 'OffString', 'random A1_time OFF',... 'TooltipString', sprintf('If on (black) then it enables the random sampling of A1_time')); @@ -142,10 +142,10 @@ NumeditParam(obj, 'time_bet_aud1_gocue_Min', 0.2, x,y,'label','Min A1-GoCue time','TooltipString','Min time between the end of the stimulus and the go cue '); set_callback(time_bet_aud1_gocue_Min, {mfilename, 'new_CP_duration'}); next_row(y); - NumeditParam(obj, 'time_bet_aud1_gocue', [x, y, 175 20], x,y,'label','A1-GoCue time','TooltipString','Actual time between the end of the stimulus and the go cue '); + NumeditParam(obj, 'time_bet_aud1_gocue', 0.4,x,y, 'position', [x, y, 175 20],'label','A1-GoCue time','TooltipString','Actual time between the end of the stimulus and the go cue '); set_callback(time_bet_aud1_gocue, {mfilename, 'new_CP_duration'}); % Toggle Buttons To Control Parameters - ToggleParam(obj, 'random_prego_time', 1, [x+180 y 20 20],... + ToggleParam(obj, 'random_prego_time', 1, x,y, 'position',[x+180 y 20 20],... 'OnString', 'random prego_time ON',... 'OffString', 'random prego_time OFF',... 'TooltipString', sprintf('If on (black) then it enables the random sampling of time between the end of the stimulus and the go cue')); @@ -166,32 +166,34 @@ set_callback(time_go_cue, {mfilename, 'new_time_go_cue'}); next_row(y); DispParam(obj, 'Total_CP_duration', CP_duration+time_go_cue, x, y, 'TooltipString', 'Total nose in center port time, in secs. Sum of CP_duration and Go Cue duration'); %#ok<*NODEF> - next_row(y); + next_row(y); next_row(y); % Toggle Buttons To Control Parameters ToggleParam(obj, 'stimuli_on', 1, x,y,... 'OnString', 'Stimuli ON',... 'OffString', 'Stimuli OFF',... 'TooltipString', sprintf('If on (black) then it enables training with stimuli else using a fixed sound from Stage 5')); - next_row(y); + next_row(y);next_row(y); %% Sound and Stimulus Section - [x, y] = SoundSection(obj,'init',x,y); next_row(y); + [x, y] = SoundSection(obj,'init',x,y); [x, y] = StimulusSection(obj,'init',x,y); next_row(y); next_column(x); y=5; - % Training for Centre Poke Increase - ToggleParam(obj,'increase_CP_training',0,x,y,'OnString','Training Increasing CP','OffString','No Training'); - set_callback(increase_CP_training, {mfilename, 'CP_training'}); - next_row(y); + % Toggle for WarmUp ToggleParam(obj, 'warmup_on', 0, x,y,... 'OnString', 'Warmup ON',... 'OffString', 'Warmup OFF',... 'TooltipString', sprintf(['If on (Yellow) then it applies the initial warming up phase, during which the\n',... 'CP_duration starts small and gradually grows to last_session_max_cp_duration. Used during training'])); next_row(y); + + % Training for Centre Poke Increase + ToggleParam(obj,'increase_CP_training',0,x,y,'OnString','Training Increasing CP','OffString','No Training'); + set_callback(increase_CP_training, {mfilename, 'CP_training'}); + next_row(y); NumeditParam(obj, 'cp_start' ,0.5, x,y,'label','CPDur_Start','TooltipString','CP start time during training'); next_row(y); NumeditParam(obj, 'cp_end' ,2, x,y,'label','CPDur_End','TooltipString','CP end time during training'); From 609593b40ea166c46f81763411175fa1370eef97 Mon Sep 17 00:00:00 2001 From: Arpit Date: Thu, 26 Jun 2025 18:55:34 +0100 Subject: [PATCH 141/164] fixed Stim Distribution plot --- .../StimulusSection.m | 77 ++++++++++++------- 1 file changed, 48 insertions(+), 29 deletions(-) diff --git a/Protocols/@ArpitSoundCatContinuous/StimulusSection.m b/Protocols/@ArpitSoundCatContinuous/StimulusSection.m index ff37d4f9..94b7694b 100644 --- a/Protocols/@ArpitSoundCatContinuous/StimulusSection.m +++ b/Protocols/@ArpitSoundCatContinuous/StimulusSection.m @@ -24,24 +24,37 @@ oldx=x; oldy=y; parentfig=double(gcf); SoloParamHandle(obj, 'myfig', 'value', figure('closerequestfcn', [mfilename '(' class(obj) ', ''hide'');'],... - 'MenuBar', 'none', 'Name', mfilename), 'saveable', 0); - screen_size = get(0, 'ScreenSize'); - set(value(myfig),'Position',[1 screen_size(4)-740, 400 400]); % put fig at top right - set(double(gcf), 'Visible', 'off'); - + 'MenuBar', 'none', 'Name', mfilename, 'Units', 'normalized'), 'saveable', false); SoundManagerSection(obj, 'declare_new_sound', 'StimAUD1') SoloParamHandle(obj, 'thisstim', 'value', []); SoloParamHandle(obj, 'thisstimlog', 'value', []); SoloParamHandle(obj, 'h1', 'value', []); + + %% Formatting graphics elements + + %myfig + original_width = 0.25; + original_height = 0.75; + max_size = min(original_width, original_height); + aspect_ratio = original_width / original_height; + new_width = max_size; + new_height = new_width / aspect_ratio; + + center_x = 0.5 - (new_width / 2); + center_y = 0.5 - (new_height / 2); + + position_vector = [center_x center_y new_width new_height]; + + set(value(myfig), 'Units', 'normalized', 'Name', mfilename, 'Position', position_vector); + set(double(gcf), 'Visible', 'off'); + x = 10; y=5; next_row(y); next_row(y); PushbuttonParam(obj, 'refresh_stimuli', x,y , 'TooltipString', 'Instantiates the stimuli given the new set of parameters'); set_callback(refresh_stimuli, {mfilename, 'plot_stimuli'}); - - next_row(y); next_row(y); MenuParam(obj, 'Rule', {'S1>S_boundary Left','S1>S_boundary Right'}, ... @@ -131,16 +144,21 @@ make_invisible(maxF1);make_invisible(minF1);make_invisible(A1_freq);make_invisible(volumeF1); next_row(y); - % next_column(y) - SoloParamHandle(obj, 'stim_dist_fig', 'value', figure('closerequestfcn', [mfilename '(' class(obj) ', ''hide'');'], 'MenuBar', 'none', ... - 'Name', 'StimulusPlot'), 'saveable', 0); - set(double(gcf), 'Visible', 'off'); - ax = axes(value(stim_dist_fig),'Position',[0.1 0.1 0.8 0.8]); - ylabel('log_e A','FontSize',16,'FontName','Cambria Math'); - set(ax,'Fontsize',15) - xlabel('Sound Categorization','FontSize',16,'FontName','Cambria Math') - SoloParamHandle(obj, 'ax', 'saveable', 0, 'value', ax); - + % Axes for Plotting + hndl_uipanelSettings = uipanel('Units', 'normalized'); + set(hndl_uipanelSettings, ... + 'Units', 'normalized', ... + 'Parent', value(myfig), ... + 'Title', 'Stimuli Distribution', ... + 'Tag', 'uipanelstimplot', ... + 'Position', [0.06,0.42,0.8,0.4]); + + SoloParamHandle(obj, 'axstimplot', 'value', axes(hndl_uipanelSettings,'Units', 'normalized','Position', [0.2,0.2, ... + 0.75,0.75]), 'saveable', false); + xlabel('Stim Distribution','FontSize',8,'FontName','Cambria Math'); + ylabel('log__e A','FontSize',8,'FontName','Cambria Math'); + + % Plot the Distribution StimulusSection(obj,'plot_stimuli'); x=oldx; y=oldy; @@ -336,17 +354,17 @@ end - cla(value(ax)) + cla(value(axstimplot)) - StimuliDistribution_plot(value(ax),[stim_min_log, value(boundary), stim_max_log], Rule, ... + StimuliDistribution_plot(value(axstimplot),[stim_min_log, value(boundary), stim_max_log], Rule, ... value(Prob_Dist_Left),value(mean_Left),value(sigma_Left),[edge_min_left edge_max_left], ... value(Prob_Dist_Right),value(mean_Right),value(sigma_Right),[edge_min_right edge_max_right]); - hold (value(ax),'on') + hold (value(axstimplot),'on') xline([stim_min_log value(boundary) stim_max_log],'-',{'Stim Min','Boundary','Stim Max'}); ylabel('log_e A','FontSize',16,'FontName','Cambria Math'); - set(value(ax),'Fontsize',15) + set(value(axstimplot),'Fontsize',15) xlabel('Sound Categorization','FontSize',16,'FontName','Cambria Math') % plot(xd,stim_min_log,'s','MarkerSize',15,'MarkerEdgeColor',[0 0 0],'LineWidth',2) @@ -354,10 +372,10 @@ % plot(xd,stim_max_log,'s','MarkerSize',15,'MarkerEdgeColor',[0 0 0],'LineWidth',2) % line([0,2], [value(boundary),value(boundary)]); % axis square - % set(value(ax),'ytick',([stim_min_log, stim_max_log]),'xtick',xd); - % set(value(ax),'yticklabel',([stim_min, stim_max]),'xticklabel','S1'); + % set(value(axstimplot),'ytick',([stim_min_log, stim_max_log]),'xtick',xd); + % set(value(axstimplot),'yticklabel',([stim_min, stim_max]),'xticklabel','S1'); % ylabel('\sigma_1 in log scale','FontSize',16,'FontName','Cambria Math'); - % set(value(ax),'Fontsize',15) + % set(value(axstimplot),'Fontsize',15) % xlabel('S1','FontSize',16,'FontName','Cambria Math') @@ -528,7 +546,8 @@ %% Case close case 'close' set(value(myfig), 'Visible', 'off'); - set(value(stim_dist_fig), 'Visible', 'off'); + % set(value(stim_dist_fig), 'Visible', 'off'); + % Delete all SoloParamHandles who belong to this object and whose % fullname starts with the name of this mfile: if exist('myfig', 'var') && isa(myfig, 'SoloParamHandle') && ishandle(value(myfig)) %#ok @@ -554,22 +573,22 @@ case 'hide' StimulusShow.value = 0; set(value(myfig), 'Visible', 'off'); - set(value(stim_dist_fig), 'Visible', 'off'); + % set(value(stim_dist_fig), 'Visible', 'off'); %% Case show case 'show' StimulusShow.value = 1; set(value(myfig), 'Visible', 'on'); - set(value(stim_dist_fig), 'Visible', 'on'); + % set(value(stim_dist_fig), 'Visible', 'on'); %% Case Show_hide case 'show_hide' if StimulusShow == 1 set(value(myfig), 'Visible', 'on'); - set(value(stim_dist_fig), 'Visible', 'on');%#ok (defined by GetSoloFunctionArgs) + % set(value(stim_dist_fig), 'Visible', 'on');%#ok (defined by GetSoloFunctionArgs) else set(value(myfig), 'Visible', 'off'); - set(value(stim_dist_fig), 'Visible', 'off'); + % set(value(stim_dist_fig), 'Visible', 'off'); end end From 04429705ee9b45422ba80a23697109068dd442ab Mon Sep 17 00:00:00 2001 From: Arpit Date: Sat, 5 Jul 2025 21:52:40 +0100 Subject: [PATCH 142/164] added psychometric curve to ArpitSoundCatContinuous --- .../.bonsai/Settings/Camera_Control.layout | 1 + .../Plugins/@pokesplot2/PokesPlotSection.m | 15 ++++++++++++--- ...rpitSoundCatContinuous_lida_LP12_250702a.mat | Bin 0 -> 13899 bytes ...rpitSoundCatContinuous_lida_LP12_250702b.mat | Bin 0 -> 14079 bytes ...rpitSoundCatContinuous_lida_LP12_250703a.mat | Bin 0 -> 14101 bytes 5 files changed, 13 insertions(+), 3 deletions(-) create mode 100644 ExperPort/settings_@ArpitSoundCatContinuous_lida_LP12_250702a.mat create mode 100644 ExperPort/settings_@ArpitSoundCatContinuous_lida_LP12_250702b.mat create mode 100644 ExperPort/settings_@ArpitSoundCatContinuous_lida_LP12_250703a.mat diff --git a/ExperPort/Plugins/@bonsaicamera/.bonsai/Settings/Camera_Control.layout b/ExperPort/Plugins/@bonsaicamera/.bonsai/Settings/Camera_Control.layout index 386f50b5..6009675f 100644 --- a/ExperPort/Plugins/@bonsaicamera/.bonsai/Settings/Camera_Control.layout +++ b/ExperPort/Plugins/@bonsaicamera/.bonsai/Settings/Camera_Control.layout @@ -31,6 +31,7 @@ + false 15 705 diff --git a/ExperPort/Plugins/@pokesplot2/PokesPlotSection.m b/ExperPort/Plugins/@pokesplot2/PokesPlotSection.m index de731ad1..fae5708a 100644 --- a/ExperPort/Plugins/@pokesplot2/PokesPlotSection.m +++ b/ExperPort/Plugins/@pokesplot2/PokesPlotSection.m @@ -803,16 +803,24 @@ llim = ulim - value(ntrials) + 1; YLim = [llim ulim]; end + + % Panel for Axes + hndl_uipanelAxes = uipanel('Units', 'normalized','Parent', value(myfig), ... + 'Title', 'Pokes_Plot', ... + 'Tag', 'uipanelplot', ... + 'Position', [0.11,0.33,0.55,0.56]); + set(value(axpokesplot), ... 'Units', 'normalized', ... - 'Parent', value(myfig), ... + 'Parent', hndl_uipanelAxes, ... 'Color', [0.3 0.3 0.3], ... 'Tag', 'axpokesplot', ... 'Visible', 'on', ... 'ButtonDownFcn', [mfilename '(' class(obj) ', ''axpokesplot_callback'');'], ... 'YLim', [YLim(1)-0.5 YLim(2)+0.5], ... 'XLim', [value(t0) value(t1)], ... - 'Position', [0.11 0.33333 0.55667 0.56339]); + 'Position', [0.1 0.1 0.88 0.88]); + xlabel(value(axpokesplot), 'Time (seconds)'); if get(value(checkboxUseCustomPreferences), 'Value') ylabel(value(axpokesplot), ''); @@ -1784,7 +1792,8 @@ %% CASE btnShowHideLegendPanelCallback case 'btnShowHideLegendPanelCallback' handles = guihandles(value(myfig)); - off_axpokesplot_position = [0.11 0.33333 0.55667 0.56339]; + % off_axpokesplot_position = [0.11 0.33333 0.55667 0.56339]; + off_axpokesplot_position = [0.1 0.1 0.88 0.88]; legend_position = get(handles.uipanelLegend, 'Position'); on_axpokesplot_position = off_axpokesplot_position; on_axpokesplot_position(3) = on_axpokesplot_position(3) + legend_position(3); diff --git a/ExperPort/settings_@ArpitSoundCatContinuous_lida_LP12_250702a.mat b/ExperPort/settings_@ArpitSoundCatContinuous_lida_LP12_250702a.mat new file mode 100644 index 0000000000000000000000000000000000000000..acc24254e002cd24c31596a6a6d9e722c086b688 GIT binary patch literal 13899 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 literal 0 HcmV?d00001 diff --git a/ExperPort/settings_@ArpitSoundCatContinuous_lida_LP12_250702b.mat b/ExperPort/settings_@ArpitSoundCatContinuous_lida_LP12_250702b.mat new file mode 100644 index 0000000000000000000000000000000000000000..35bc6871599ecd415d2d4eaf26e513aca2354786 GIT binary patch literal 14079 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 literal 0 HcmV?d00001 diff --git a/ExperPort/settings_@ArpitSoundCatContinuous_lida_LP12_250703a.mat b/ExperPort/settings_@ArpitSoundCatContinuous_lida_LP12_250703a.mat new file mode 100644 index 0000000000000000000000000000000000000000..5102bd5c6ec565ca9a65c5ed8875a0ec1a2a7baf GIT binary patch literal 14101 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 C4Tm%U literal 0 HcmV?d00001 From 197cbdd145bc2b83493c58c4e220d5967830132a Mon Sep 17 00:00:00 2001 From: Arpit Date: Sat, 5 Jul 2025 21:59:19 +0100 Subject: [PATCH 143/164] added psychometric for ArpitSoundCat --- .../ArpitSoundCatContinuous.m | 21 +- .../PerformanceSection.m | 5 +- .../PsychometricSection.m | 485 ++++++++++++++++++ .../StimulusSection.m | 117 ++++- .../private/createTemporalColormap.m | 24 + .../private/realtimepsychometricFit.m | 155 ++++++ sendsummary_error_log.txt | 30 -- 7 files changed, 772 insertions(+), 65 deletions(-) create mode 100644 Protocols/@ArpitSoundCatContinuous/PsychometricSection.m create mode 100644 Protocols/@ArpitSoundCatContinuous/private/createTemporalColormap.m create mode 100644 Protocols/@ArpitSoundCatContinuous/private/realtimepsychometricFit.m delete mode 100644 sendsummary_error_log.txt diff --git a/Protocols/@ArpitSoundCatContinuous/ArpitSoundCatContinuous.m b/Protocols/@ArpitSoundCatContinuous/ArpitSoundCatContinuous.m index 69ef3055..baf3e9c2 100644 --- a/Protocols/@ArpitSoundCatContinuous/ArpitSoundCatContinuous.m +++ b/Protocols/@ArpitSoundCatContinuous/ArpitSoundCatContinuous.m @@ -1,5 +1,3 @@ -% AltSoundCatCatch protocol -% EM, October 2020 function [obj] = ArpitSoundCatContinuous(varargin) @@ -65,6 +63,10 @@ DeclareGlobals(obj, 'ro_args', {'stimulus_history'}); SoloFunctionAddVars('StimulusSection', 'rw_args', 'stimulus_history'); + SoloParamHandle(obj, 'stimulus_distribution_history', 'value', cell(0)); + DeclareGlobals(obj, 'ro_args', {'stimulus_distribution_history'}); + SoloFunctionAddVars('StimulusSection', 'rw_args', 'stimulus_distribution_history'); + SoloParamHandle(obj, 'violation_history', 'value', []); DeclareGlobals(obj, 'ro_args', {'violation_history'}); SoloFunctionAddVars('SideSection', 'rw_args', 'violation_history'); @@ -119,11 +121,13 @@ [x, y] = SideSection(obj, 'init', x, y); %#ok next_row(y, 1.3); [x, y] = PerformanceSection(obj, 'init', x, y); + next_row(y, 1.3); + [x, y] = PsychometricSection(obj, 'init', x, y); x=oldx; y=oldy; SessionDefinition(obj, 'init', x, y, value(myfig)); next_row(y, 2); %#ok ArpitSoundCatContinuousSMA(obj, 'init'); - % feval(mfilename, obj, 'prepare_next_trial'); + feval(mfilename, obj, 'prepare_next_trial'); case 'change_water_modulation_params' display_guys = [1 150 300]; @@ -152,7 +156,6 @@ SoundManagerSection(obj, 'send_not_yet_uploaded_sounds'); [sma, prepare_next_trial_states] = ArpitSoundCatContinuousSMA(obj, 'prepare_next_trial'); - % PerformanceSection(obj, 'evaluate'); % Default behavior of following call is that every 20 trials, the data @@ -168,17 +171,17 @@ case 'trial_completed' % Change the video trial - BonsaiCameraInterface(obj,'next_trial'); + BonsaiCameraInterface(obj,'next_trial'); % Update the Metrics Calculated PerformanceSection(obj,'evaluate'); - + PsychometricSection(obj, 'update'); % Do any updates in the protocol that need doing: feval(mfilename, 'update'); %% update case 'update' - % PokesPlotSection(obj, 'update'); + PokesPlotSection(obj, 'update'); if n_done_trials==1 [expmtr, rname]=SavingSection(obj, 'get_info'); prot_title.value=[mfilename ' on rig ' get_hostname ' : ' expmtr ', ' rname '. Started at ' datestr(now, 'HH:MM')]; @@ -186,10 +189,10 @@ %% close case 'close' - % PokesPlotSection(obj, 'close'); + PokesPlotSection(obj, 'close'); SideSection(obj, 'close'); StimulusSection(obj,'close'); - BonsaiCameraInterface(obj,'close'); + BonsaiCameraInterface(obj,'close'); if exist('myfig', 'var') && isa(myfig, 'SoloParamHandle') && ishandle(value(myfig)) %#ok delete(value(myfig)); end diff --git a/Protocols/@ArpitSoundCatContinuous/PerformanceSection.m b/Protocols/@ArpitSoundCatContinuous/PerformanceSection.m index 5b9a0084..eb4dd15a 100644 --- a/Protocols/@ArpitSoundCatContinuous/PerformanceSection.m +++ b/Protocols/@ArpitSoundCatContinuous/PerformanceSection.m @@ -61,7 +61,8 @@ SubheaderParam(obj, 'title', 'Overall Performance', x, y); next_row(y, 1.5); - SoloParamHandle(obj, 'previous_parameters', 'value', []); + % SoloParamHandle(obj, 'previous_parameters', 'value', []); + % ------------------------------------------------------------------ % evaluate @@ -73,7 +74,7 @@ if n_done_trials > 1 ntrials.value = n_done_trials; - n_trials_valid.value = numel(find(~isnan(hit_history))); + ntrials_valid.value = numel(find(~isnan(hit_history))); violation_percent.value = numel(find(violation_history))/n_done_trials; timeout_percent.value = numel(find(timeout_history))/n_done_trials; goods = ~isnan(hit_history)'; diff --git a/Protocols/@ArpitSoundCatContinuous/PsychometricSection.m b/Protocols/@ArpitSoundCatContinuous/PsychometricSection.m new file mode 100644 index 00000000..2e33d9de --- /dev/null +++ b/Protocols/@ArpitSoundCatContinuous/PsychometricSection.m @@ -0,0 +1,485 @@ +function varargout = PsychometricSection(obj, action, varargin) + +GetSoloFunctionArgs(obj); + +switch action + + % ------------------------------------------------------------------ + % INIT + % ------------------------------------------------------------------ + + case 'init' + if length(varargin) < 2 + error('Need at least two arguments, x and y position, to initialize %s', mfilename); + end + x = varargin{1}; y = varargin{2}; + + % Display on the main GUI + + % Context 1 + DispParam(obj, 'Context3_trialStart', 1,x,y, 'position', [x, y, 100 20],'label','t_start','TooltipString','3rd context started at this trial'); + DispParam(obj, 'Context3_trialEnd', 1,x,y, 'position', [x + 101, y, 100 20],'label','t_end','TooltipString','3rd context ended at this trial') + next_row(y); + DispParam(obj, 'Context3_Dist', Category_Dist, x,y,'label','Context3_Distr','TooltipString','stim distribution for 3rd context'); + make_invisible(Context3_Dist); make_invisible(Context3_trialStart);make_invisible(Context3_trialEnd); + next_row(y); + + % Context 2 + DispParam(obj, 'Context2_trialStart', 1,x,y, 'position', [x, y, 100 20],'label','t_start','TooltipString','2nd context started at this trial'); + DispParam(obj, 'Context2_trialEnd', 1,x,y, 'position', [x + 101, y, 100 20],'label','t_end','TooltipString','2nd context ended at this trial') + next_row(y); + DispParam(obj, 'Context2_Dist', Category_Dist, x,y,'label','Context2_Distr','TooltipString','stim distribution for 2nd context'); + make_invisible(Context2_Dist); make_invisible(Context2_trialStart);make_invisible(Context2_trialEnd); + next_row(y); + + % Context 3 + DispParam(obj, 'Context1_trialStart', 1,x,y, 'position', [x, y, 100 20],'label','Trial_start','TooltipString','1st context started at this trial'); + DispParam(obj, 'Context1_trialEnd', 1,x,y, 'position', [x + 101, y, 100 20],'label','Trial_end','TooltipString','1st context ended at this trial') + next_row(y); + DispParam(obj, 'Context1_Dist', Category_Dist, x,y,'label','Context1_Distr','TooltipString','stim distribution for 1st context'); + next_row(y); + + PushbuttonParam(obj, 'Switch_Distr', x, y, 'label', 'Change Stim Distribution','TooltipString', 'Change the context by switching distribution'); + set_callback(Switch_Distr, {mfilename, 'PushButton_Distribution_Switch'}); %#ok (Defined just above) + next_row(y,2) + NumeditParam(obj,'trial_plot',30,x,y,'label','Trails 2 Plot','TooltipString','update psychometric curve after these many valid trials'); + next_row(y); + ToggleParam(obj, 'PsychometricShow', 0, x, y, 'OnString', 'Psychometric Show', ... + 'OffString', 'Psychometric Hidden', 'TooltipString', 'Show/Hide Psychometric panel'); + set_callback(PsychometricShow, {mfilename, 'show_hide'}); %#ok (Defined just above) + next_row(y); + SubheaderParam(obj, 'title', 'Psychometric Section', x, y);next_row(y); + + oldx=x; oldy=y; parentfig=double(gcf); + + % SoloParams + SoloParamHandle(obj, 'thiscontext', 'value', 1); + SoloParamHandle(obj, 'last_trial_plotted', 'value', 0); + vars = ["Select","Rule","Distribution","Start_trial","End_trial","Slope","TrueBoundary",... + "CalBoundary","LapseA","LapseB","fit_Method", "Pred_Y"]; + vars_type = ["logical","string","string","double","double","double","double","double",... + "double","double","string","cell"]; + t = table('Size', [0, numel(vars)], ... + 'VariableTypes', vars_type, ... + 'VariableNames', vars); + SoloParamHandle(obj, 'TableData', 'value', t); + + % New Fig Window to show the Psychometric Curves + SoloParamHandle(obj, 'myfig', 'value', uifigure('closerequestfcn', [mfilename '(' class(obj) ', ''hide'');'],... + 'Name', mfilename, 'Units', 'normalized','Visible', 'off'), 'saveable', false); + + % Panel for Table + hndl_uipanelTable = uipanel('Units', 'normalized','Parent', value(myfig), ... + 'Title', 'Psychometric Summary', ... + 'Tag', 'uipanelTable', ... + 'Position', [0.06,0.2,0.4,0.75]); + SoloParamHandle(obj, 'uit','value', uitable(hndl_uipanelTable,'Units', 'normalized','Data',value(TableData),... + 'ColumnSortable', true,'Position',[0.02 0.02 0.95 0.95],... + 'ColumnEditable', [true, false, false, false, false, false,false, false, false, false, false,false]),'savable',false); + + % Panel for Axes + hndl_uipanelAxes = uipanel('Units', 'normalized','Parent', value(myfig), ... + 'Title', 'Psychometric Plots', ... + 'Tag', 'uipanelplot', ... + 'Position', [0.5,0.05,0.4,0.9]); + SoloParamHandle(obj, 'axplot1', 'value', axes(hndl_uipanelAxes,'Units', 'normalized','Position', [0.1,0.53, ... + 0.8,0.43]), 'saveable', false); + ylabel('Prob A','FontSize',8,'FontName','Cambria Math'); + + SoloParamHandle(obj, 'axplot2', 'value', axes(hndl_uipanelAxes,'Units', 'normalized','Position', [0.1,0.05, ... + 0.8,0.43]), 'saveable', false); + xlabel('Stim','FontSize',8,'FontName','Cambria Math'); + ylabel('Prob A','FontSize',8,'FontName','Cambria Math'); + + % Creating a new figure for Push button and edit box because they + % are based upon Java, so they wont plot on uifig which is web + % based + + % New Fig Window to show the Psychometric Curves + SoloParamHandle(obj, 'myfig1', 'value', figure('closerequestfcn', [mfilename '(' class(obj) ', ''hide'');'],... + 'MenuBar', 'none','Name', mfilename, 'Units', 'normalized','Visible', 'off'), 'saveable', false); + + % Edit and Push Button for Psych Plots + if ~exist('Plot_Trial_Start', 'var') || ~isa(Plot_Trial_Start, 'SoloParamHandle') + NumeditParam(obj, 'Plot_Trial_Start', 1, 1, 1,'label','Trial Start','labelpos','top'); + end + if ~exist('Plot_Trial_End', 'var') || ~isa(Plot_Trial_End, 'SoloParamHandle') + NumeditParam(obj, 'Plot_Trial_End', 1, 1, 1,'label','Trial End','labelpos','top'); + end + if ~exist('Plot_Selected', 'var') || ~isa(Plot_Selected, 'SoloParamHandle') + PushbuttonParam(obj, 'Plot_Selected', 1, 1); + set_callback(Plot_Selected, {mfilename, 'PushButton_SelectedTrial'}); + end + if ~exist('Plot_Context', 'var') || ~isa(Plot_Context, 'SoloParamHandle') + PushbuttonParam(obj, 'Plot_Context', 1, 1); + set_callback(Plot_Context, {mfilename, 'PushButton_Context'}); + end + + hndl_uipanelSettings = uipanel('Units', 'normalized','Parent', value(myfig1), ... + 'Title', 'Plot Variables', ... + 'Tag', 'uipanelsetting', ... + 'Position', [0.02,0.02,0.9,0.9]); + + set(get_ghandle(Plot_Trial_Start), ... + 'Parent', hndl_uipanelSettings, ... + 'Units', 'normalized', ... + 'Tag', 'trial_start', ... + 'TooltipString', 'trial start for plot', ... + 'FontSize', 10.0, ... + 'Position', [0.02,0.5,0.15,0.2]); + delete(get_lhandle(Plot_Trial_Start)); + + set(get_ghandle(Plot_Trial_End), ... + 'Parent', hndl_uipanelSettings, ... + 'Units', 'normalized', ... + 'Tag', 'trial_end', ... + 'TooltipString', 'trial end for plot', ... + 'FontSize', 10.0, ... + 'Position', [0.28,0.5,0.15,0.2]); + delete(get_lhandle(Plot_Trial_End)); + + set(get_ghandle(Plot_Selected), ... + 'Parent', hndl_uipanelSettings, ... + 'Units', 'normalized', ... + 'Tag', 'plotselected_trials', ... + 'Position', [0.04,0.05,0.4,0.4]); + delete(get_lhandle(Plot_Selected)); + + set(get_ghandle(Plot_Context), ... + 'Parent', hndl_uipanelSettings, ... + 'Units', 'normalized', ... + 'Tag', 'plot_context', ... + 'Position', [0.55,0.05,0.4,0.4]); + delete(get_lhandle(Plot_Context)); + + % Edit and Push Button for Psych Plots + % NumeditParam(obj,'Plot_Trial_Start',1,50,100,'position',[5,10,100,20],'label','Trial Start','labelpos','top'); + % NumeditParam(obj,'Plot_Trial_End',1,150,100,'position',[150,10,100,20],'label','Trial End','labelpos','top'); + % PushbuttonParam(obj, 'Plot_Selected', 300, 20, 'label', 'Plot Selected Trials','TooltipString', 'Change the context by switching distribution'); + % set_callback(Plot_Selected, {mfilename, 'PushButton_SelectedTrial'}); + % PushbuttonParam(obj, 'Plot_Context', 300, 40, 'label', 'Plot Context','TooltipString', 'Change the context by switching distribution'); + % set_callback(Switch_Distr, {mfilename, 'PushButton_Context'}); + + varargout{1} = x; + varargout{2} = y; + + case 'PushButton_Distribution_Switch' + + % eval(sprintf('present_context_dist = value(Context%i_Dist)',value(thiscontext))); + eval(sprintf('present_context_start = value(Context%i_trialStart);',value(thiscontext))); + eval(sprintf('present_context_end = value(Context%i_trialEnd);',value(thiscontext))); + + if ~strcmpi(Category_Dist,'Uniform') && present_context_end > present_context_start + + if value(thiscontext) < 3 % & ~strcmpi(present_context_dist,Category_Dist) + thiscontext.value = value(thiscontext) + 1; + eval(sprintf('Context%i_Dist.value = Category_Dist;',value(thiscontext))); + eval(sprintf('Context%i_trialStart.value = n_done_trials + 1;',value(thiscontext))); + eval(sprintf('Context%i_trialEnd.value = n_done_trials + 1;',value(thiscontext))); + eval(sprintf('make_visible(Context%i_Dist);',value(thiscontext))); + eval(sprintf('make_visible(Context%i_trialStart);',value(thiscontext))); + eval(sprintf('make_visible(Context%i_trialEnd);',value(thiscontext))); + end + + if strcmpi(Category_Dist,'Hard A') + StimulusSection(obj,'Pushbutton_SwitchDistribution','Hard B'); + elseif strcmpi(Category_Dist,'Hard B') + StimulusSection(obj,'Pushbutton_SwitchDistribution','Hard A'); + end + end + + case 'StimSection_Distribution_Switch' + + eval(sprintf('present_context_start = value(Context%i_trialStart);',value(thiscontext))); + eval(sprintf('present_context_end = value(Context%i_trialEnd);',value(thiscontext))); + + if present_context_end > present_context_start + if value(thiscontext) < 3 % & ~strcmpi(present_context_dist,Category_Dist) + thiscontext.value = value(thiscontext) + 1; + eval(sprintf('Context%i_Dist.value = Category_Dist;',value(thiscontext))); + eval(sprintf('Context%i_trialStart.value = n_done_trials + 1;',value(thiscontext))); + eval(sprintf('Context%i_trialEnd.value = n_done_trials + 1;',value(thiscontext))); + eval(sprintf('make_visible(Context%i_Dist);',value(thiscontext))); + eval(sprintf('make_visible(Context%i_trialStart);',value(thiscontext))); + eval(sprintf('make_visible(Context%i_trialEnd);',value(thiscontext))); + end + else + eval(sprintf('Context%i_Dist.value = Category_Dist;',value(thiscontext))); + end + + case 'PushButton_SelectedTrial' + try + stim2analyze = stimulus_history(value(Plot_Trial_Start):value(Plot_Trial_End)); + sides2analyze = previous_sides(value(Plot_Trial_Start):value(Plot_Trial_End)); + hit_history2analyze = hit_history(value(Plot_Trial_Start):value(Plot_Trial_End)); + category_distribution2analyze = stimulus_distribution_history(value(Plot_Trial_Start):value(Plot_Trial_End)); + valid_hit_history_trials = find(~isnan(hit_history2analyze)); + sides = sides2analyze(valid_hit_history_trials); + stim = stim2analyze(valid_hit_history_trials); + category_distribution = category_distribution2analyze(valid_hit_history_trials); + hit_values = hit_history2analyze(valid_hit_history_trials)'; + resp = zeros(size(hit_values)); + + if strcmpi(Rule,'S1>S_boundary Left') % Category B is Left so its value is 1 + resp((sides == 108 & hit_values == 1) | (sides == 114 & hit_values == 0)) = 1; + else % Category B is Right so its value is 1 + resp((sides == 114 & hit_values == 1) | (sides == 108 & hit_values == 0)) = 1; + end + + Stim_Params = StimulusSection(obj,'stim_params'); + try + [y_pred, fitParams, methodUsed,fitStatus] = realtimepsychometricFit(stim,resp,Stim_Params); + catch + y_pred =[]; + fitParams = [nan,nan,nan,nan]; + methodUsed = "Failed"; + fitStatus = ""; + end + % Update the Table + + + % Convert category string with comma separator + category = unique(category_distribution); + category_selected = strjoin(category, ','); + + variableNames = ["Select","Rule","Distribution","Start_trial","End_trial","Slope","TrueBoundary",... + "CalBoundary","LapseA","LapseB","fit_Method", "Pred_Y"]; + newRow = table(false,string(Rule),string(category_selected),value(Plot_Trial_Start),value(Plot_Trial_End),... + fitParams(2),Stim_Params(2),fitParams(1),fitParams(3),fitParams(4),string(methodUsed) + " (" + fitStatus + ")",... + {y_pred},'VariableNames', variableNames); + t_new = [value(TableData);newRow]; + TableData.value = t_new; + t_handle = value(uit); + t_handle.Data = t_new; + + % Plot the Psychometric + xGrid = linspace(Stim_Params(1), Stim_Params(3), 300)'; + cla(value(axplot2)) + hold (value(axplot2),'on') + if ~(contains(methodUsed,'Failed') || contains(methodUsed,'Canceled')) + plot(value(axplot2),xGrid, y_pred, 'b-', 'LineWidth', 2); + xline(value(axplot2),fitParams(1), 'g--', sprintf('PSE (%.2f)', fitParams(1))); + end + xline(value(axplot2),Stim_Params(2), 'r--', 'Boundary'); + ylim([-0.05, 1.05]); + xlabel('Stimulus (dB)'); + ylabel('P("Category B" Response)'); + grid on; + catch + end + case 'PushButton_Context' + try + Stim_Params = StimulusSection(obj,'stim_params'); + xGrid = linspace(Stim_Params(1), Stim_Params(3), 300)'; + cla(value(axplot2)); + hold (value(axplot2),'on') + for n_plot = 1:value(thiscontext) + + eval(sprintf('trial_start = value(Context%i_trialStart);',n_plot)); + eval(sprintf('trial_end = value(Context%i_trialEnd);',n_plot)); + stim2analyze = stimulus_history(trial_start:trial_end); + sides2analyze = previous_sides(trial_start:trial_end); + hit_history2analyze = hit_history(trial_start:trial_end); + category_distribution2analyze = stimulus_distribution_history(trial_start:trial_end); + valid_hit_history_trials = find(~isnan(hit_history2analyze)); + sides = sides2analyze(valid_hit_history_trials); + stim = stim2analyze(valid_hit_history_trials); + category_distribution = category_distribution2analyze(valid_hit_history_trials); + hit_values = hit_history2analyze(valid_hit_history_trials)'; + resp = zeros(size(hit_values)); + + if strcmpi(Rule,'S1>S_boundary Left') % Category B is Left so its value is 1 + resp((sides == 108 & hit_values == 1) | (sides == 114 & hit_values == 0)) = 1; + else % Category B is Right so its value is 1 + resp((sides == 114 & hit_values == 1) | (sides == 108 & hit_values == 0)) = 1; + end + + try + [y_pred, fitParams, methodUsed,fitStatus] = realtimepsychometricFit(stim,resp,Stim_Params); + catch + y_pred =[]; + fitParams = [nan,nan,nan,nan]; + methodUsed = "Failed"; + fitStatus = ""; + end + % Update the Table + + % Convert category string with comma separator + category = unique(category_distribution); + category_selected = strjoin(category, ','); + variableNames = ["Select","Rule","Distribution","Start_trial","End_trial","Slope","TrueBoundary",... + "CalBoundary","LapseA","LapseB","fit_Method", "Pred_Y"]; + newRow = table(false,string(Rule),string(category_selected),trial_start,trial_end,... + fitParams(2),Stim_Params(2),fitParams(1),fitParams(3),fitParams(4),string(methodUsed) + " (" + fitStatus + ")",... + {y_pred},'VariableNames', variableNames); + t_new = [value(TableData);newRow]; + TableData.value = t_new; + t_handle = value(uit); + t_handle.Data = t_new; + + % Plot the Psychometric + if ~(contains(methodUsed,'Failed') || contains(methodUsed,'Canceled')) + plot(value(axplot2),xGrid, y_pred, 'b-', 'LineWidth', 2); + xline(value(axplot2),fitParams(1), 'g--', sprintf('PSE (%.2f)', fitParams(1))); + end + + end + + xline(value(axplot2),Stim_Params(2), 'r--', 'Boundary'); + ylim([-0.05, 1.05]); + xlabel('Stimulus (dB)'); + ylabel('P("Category B" Response)'); + grid on; + catch + end + + %%update after each trial + case 'update' + % update the trial end for this context + if n_done_trials > 1 + eval(sprintf('Context%i_trialEnd.value = n_done_trials;',value(thiscontext))); + + try + % Check to see if we need to plot the Psychometric Curve + n_trials_valid = numel(find(~isnan(hit_history))); + + if n_trials_valid >= value(trial_plot) && n_trials_valid > value(last_trial_plotted) % means we may plot the psychometric + if n_trials_valid - value(last_trial_plotted) >= value(trial_plot) + + Stim_Params = StimulusSection(obj,'stim_params'); + sides2analyze = previous_sides(value(last_trial_plotted) + 1: n_done_trials); + stim2analyze = stimulus_history(value(last_trial_plotted) + 1: n_done_trials); + hit_history2analyze = hit_history(value(last_trial_plotted) + 1: n_done_trials); + category_distribution2analyze = stimulus_distribution_history(value(last_trial_plotted) + 1: n_done_trials); + trial2_analyze = find(~isnan(hit_history2analyze)); + stim = stim2analyze(trial2_analyze); + sides = sides2analyze(trial2_analyze); + category_distribution = category_distribution2analyze(trial2_analyze); + hit_values = hit_history2analyze(trial2_analyze)'; + resp = zeros(size(hit_values)); + + if strcmpi(Rule,'S1>S_boundary Left') % Category B is Left so its value is 1 + resp((sides == 108 & hit_values == 1) | (sides == 114 & hit_values == 0)) = 1; + else % Category B is Right so its value is 1 + resp((sides == 114 & hit_values == 1) | (sides == 108 & hit_values == 0)) = 1; + end + + % The psychometric Fit + try + [y_pred, fitParams, methodUsed,fitStatus] = realtimepsychometricFit(stim,resp,Stim_Params); + catch + y_pred =[]; + fitParams = [nan,nan,nan,nan]; + methodUsed = "Failed"; + fitStatus = ""; + end + % Update the Table + + % Based upon fit result we decide whether to plot + % psychometric or not + + % Determine the 'Select' status based on fit success + select_status = ismember(methodUsed, {'ridge', 'robust'}); + + variableNames = ["Select","Rule","Distribution","Start_trial","End_trial","Slope","TrueBoundary",... + "CalBoundary","LapseA","LapseB","fit_Method", "Pred_Y"]; + + % Convert category string with comma separator + category = unique(category_distribution); + category_selected = strjoin(category, ','); + + if select_status + newRow = table(select_status,string(Rule),string(category_selected),value(last_trial_plotted) + 1,n_done_trials,... + fitParams(2),Stim_Params(2),fitParams(1),fitParams(3),fitParams(4),string(methodUsed) + " (" + fitStatus + ")",... + {y_pred},'VariableNames', variableNames); + else + newRow = table(select_status,string(Rule),string(category_selected),value(last_trial_plotted) + 1,n_done_trials,... + nan,Stim_Params(2),nan,nan,nan,string(methodUsed) + " (" + fitStatus + ")",... + {[]},'VariableNames', variableNames); + end + + t_new = [value(TableData); newRow]; + TableData.value = t_new; + t_handle = value(uit); + t_handle.Data = t_new; + + % Identify the user selected trials (usually the one + % plotted realtime and not by user) + selectedData = t_new(t_new.Select, :); + xGrid = linspace(Stim_Params(1), Stim_Params(3), 300)'; + cla(value(axplot1)); + hold (value(axplot1),'on'); + + if ~isempty(selectedData) + % plot the selected Psychometric + num_to_plot = height(selectedData); + colors_to_use = createTemporalColormap(num_to_plot); + for i = 1:num_to_plot + % Get parameters for the i-th selected row + plot_data_matrix = selectedData.Pred_Y{i}; + if isempty(plot_data_matrix), continue; end + y_data = plot_data_matrix(:); + % Plot the curve using the generated color + plot(value(axplot1), xGrid, y_data, ... + 'Color', colors_to_use(i, :), ... + 'LineWidth', 2, ... + 'DisplayName', sprintf('Fit %d (Trials %d-%d)', i, selectedData.Start_trial(i), selectedData.End_trial(i))); + if i == num_to_plot + xline(value(axplot1),fitParams(1), 'g--', sprintf('PSE (%.2f)', fitParams(1))); + end + end + % else + % % Plot the recently calculated Psychometric + % plot(value(axplot1),xGrid, y_pred, 'b-', 'LineWidth', 2); hold on; + end + + xline(value(axplot1),Stim_Params(2), 'r--', 'Boundary'); + ylim([-0.05, 1.05]); + xlabel('Stimulus (dB)'); + ylabel('P("Category B" Response)'); + grid on; + legend(value(axplot1),'show','Location','southeast') + hold (value(axplot1),'off'); + + last_trial_plotted.value = n_done_trials; + end + end + + catch + end + end + + %% Case close + case 'close' + set(value(myfig), 'Visible', 'off'); + set(value(myfig1), 'Visible', 'off'); + % Delete all SoloParamHandles who belong to this object and whose + % fullname starts with the name of this mfile: + if exist('myfig', 'var') && isa(myfig, 'SoloParamHandle') && ishandle(value(myfig)) %#ok + delete(value(myfig)); + end + if exist('myfig1', 'var') && isa(myfig, 'SoloParamHandle') && ishandle(value(myfig1)) %#ok + delete(value(myfig1)); + end + delete_sphandle('owner', ['^@' class(obj) '$'], ... + 'fullname', ['^' mfilename]); + + %% Case hide + case 'hide' + PsychometricShow.value = 0; + set(value(myfig), 'Visible', 'off'); + set(value(myfig1), 'Visible', 'off'); + + %% Case Show_hide + case 'show_hide' + if PsychometricShow == 1 + set(value(myfig), 'Visible', 'on'); + set(value(myfig1), 'Visible', 'on'); + else + set(value(myfig), 'Visible', 'off'); + set(value(myfig1), 'Visible', 'on'); + end + +end + +end \ No newline at end of file diff --git a/Protocols/@ArpitSoundCatContinuous/StimulusSection.m b/Protocols/@ArpitSoundCatContinuous/StimulusSection.m index 94b7694b..a4c8eabc 100644 --- a/Protocols/@ArpitSoundCatContinuous/StimulusSection.m +++ b/Protocols/@ArpitSoundCatContinuous/StimulusSection.m @@ -1,6 +1,6 @@ -function [x, y] = StimulusSection(obj, action, varargin) +function varargout = StimulusSection(obj, action, varargin) GetSoloFunctionArgs(obj); @@ -28,7 +28,6 @@ SoundManagerSection(obj, 'declare_new_sound', 'StimAUD1') SoloParamHandle(obj, 'thisstim', 'value', []); SoloParamHandle(obj, 'thisstimlog', 'value', []); - SoloParamHandle(obj, 'h1', 'value', []); %% Formatting graphics elements @@ -50,12 +49,6 @@ x = 10; y=5; - - next_row(y); - next_row(y); - PushbuttonParam(obj, 'refresh_stimuli', x,y , 'TooltipString', 'Instantiates the stimuli given the new set of parameters'); - set_callback(refresh_stimuli, {mfilename, 'plot_stimuli'}); - next_row(y); next_row(y); MenuParam(obj, 'Rule', {'S1>S_boundary Left','S1>S_boundary Right'}, ... 'S1>S_boundary Left', x, y, 'labelfraction', 0.35, 'TooltipString', sprintf(['\nThis buttom determines the rule\n', ... @@ -95,8 +88,16 @@ '\n''signifying 3 Sigma (99.7 %%) value for the right side distribution, \n',... '\n''A value b/w range [0.2 - 1] is acceptable.'])); set_callback(sigma_range_Right, {mfilename, 'Cal_Sigma'}); - next_row(y); - + next_row(y);next_row(y); + MenuParam(obj, 'Category_Dist', {'Uniform','Hard A','Hard B'}, ... + 'Uniform', x, y, 'label','Category Dist', 'labelfraction', 0.35, 'TooltipString', sprintf(['\n Different Distributions for Category.\n', ... + '\n''Depending upon the rule it will change switch the distributions for Left and Right.\n',... + '\n''If its uniform on both then it will change the distribution to Exponential on one of the side\n',... + '\n''depending upon the choice of rule'])); + set_callback(Category_Dist, {mfilename, 'Distribution_Switch'}); + next_row(y);next_row(y); + PushbuttonParam(obj, 'plot_stim_dist', x,y , 'TooltipString', 'Plots the distribution with the new set of parameters'); + set_callback(plot_stim_dist, {mfilename, 'plot_stimuli'}); next_column(x); y=5; next_row(y, 1) @@ -145,15 +146,15 @@ next_row(y); % Axes for Plotting - hndl_uipanelSettings = uipanel('Units', 'normalized'); - set(hndl_uipanelSettings, ... + hndl_uipanelplotaxes = uipanel('Units', 'normalized'); + set(hndl_uipanelplotaxes, ... 'Units', 'normalized', ... 'Parent', value(myfig), ... 'Title', 'Stimuli Distribution', ... 'Tag', 'uipanelstimplot', ... 'Position', [0.06,0.42,0.8,0.4]); - SoloParamHandle(obj, 'axstimplot', 'value', axes(hndl_uipanelSettings,'Units', 'normalized','Position', [0.2,0.2, ... + SoloParamHandle(obj, 'axstimplot', 'value', axes(hndl_uipanelplotaxes,'Units', 'normalized','Position', [0.2,0.2, ... 0.75,0.75]), 'saveable', false); xlabel('Stim Distribution','FontSize',8,'FontName','Cambria Math'); ylabel('log__e A','FontSize',8,'FontName','Cambria Math'); @@ -164,6 +165,11 @@ x=oldx; y=oldy; figure(parentfig); + SoloFunctionAddVars('PsychometricSection', 'ro_args',{'Category_Dist';'Rule';'boundary'}); + + varargout{1} = x; + varargout{2} = y; + case 'prepare_next_trial' if stimuli_on StimulusSection(obj,'pick_current_stimulus'); @@ -212,11 +218,7 @@ % end if n_done_trials > 0 - if ~violation_history(n_done_trials) && ~timeout_history(n_done_trials) - StimulusSection(obj,'update_stimulus_history'); - else - StimulusSection(obj,'update_stimulus_history_nan'); - end + StimulusSection(obj,'update_stimulus_history'); end end %% Case pick_current_stimulus @@ -489,6 +491,8 @@ end end + StimulusSection(obj,'plot_stimuli'); + %% Calculate Sigma case 'Cal_Sigma' @@ -520,6 +524,40 @@ end sigma_Right.value = (dist_sigma_multiplier * (edge_max - edge_min)) / 3; % as we asked user to provide 3 sigma + case 'stim_params' + + if frequency_categorization + stim_min_log = log(value(minF1)); + stim_max_log = log(value(maxF1)); + else + stim_min_log = log(value(minS1)); + stim_max_log = log(value(maxS1)); + end + + dist_range_multiplier_left = value(sigma_range_Left); + dist_range_multiplier_right = value(sigma_range_Right); + + if strcmp(Rule,'S1>S_boundary Left') + edge_max_left = stim_max_log; + edge_min_left = value(boundary); + edge_max_left = edge_min_left + dist_range_multiplier_left * (edge_max_left - edge_min_left); + edge_max_right = value(boundary); + edge_min_right = stim_min_log; + edge_min_right = edge_max_right - dist_range_multiplier_right * (edge_max_right - edge_min_right); + + varargout{1} = [edge_min_right, value(boundary), edge_max_left]; + + else % the rule is S1>S_boundary Right + + edge_min_left = stim_min_log; + edge_max_left = value(boundary); + edge_min_left = edge_max_left - dist_range_multiplier_left * (edge_max_left - edge_min_left); + edge_max_right = stim_max_log; + edge_min_right = value(boundary); + edge_max_right = edge_min_right + dist_range_multiplier_right * (edge_max_right - edge_min_right); + + varargout{1} = [edge_min_left, value(boundary), edge_max_right]; + end %% Case frequency ON case 'FrequencyCategorization' @@ -530,7 +568,7 @@ else make_visible(maxS1);make_visible(minS1);make_visible(A1_sigma); make_visible(fcut);make_visible(lfreq);make_visible(hfreq); make_visible(filter_type); - make_invisible(maxF1);make_invisible(minF1);make_invisible(A1_freq); make_visible(volumeF1); + make_invisible(maxF1);make_invisible(minF1);make_invisible(A1_freq); make_invisible(volumeF1); end StimulusSection(obj,'Cal_Boundary'); % update the boundary @@ -542,6 +580,39 @@ % x=value(S1); % end + case 'Distribution_Switch' + + switch value(Category_Dist) + + case 'Uniform' + Prob_Dist_Right.value = 'Uniform'; + Prob_Dist_Left.value = 'Uniform'; + + case 'Hard A' + if strcmp(Rule,'S1>S_boundary Right') + Prob_Dist_Right.value = 'Uniform'; + Prob_Dist_Left.value = 'Exponential'; + else + Prob_Dist_Right.value = 'Exponential'; + Prob_Dist_Left.value = 'Uniform'; + end + + case 'Hard B' + if strcmp(Rule,'S1>S_boundary Right') + Prob_Dist_Left.value = 'Uniform'; + Prob_Dist_Right.value = 'Exponential'; + else + Prob_Dist_Left.value = 'Exponential'; + Prob_Dist_Right.value = 'Uniform'; + end + end + StimulusSection(obj,'plot_stimuli'); + PsychometricSection(obj,'StimSection_Distribution_Switch'); + + case 'Pushbutton_SwitchDistribution' + dist = varargin{1}; + Category_Dist.value = dist; + StimulusSection(obj,'Distribution_Switch'); %% Case close case 'close' @@ -561,13 +632,11 @@ case 'update_stimulus_history' ps=value(stimulus_history); + ps1 = value(stimulus_distribution_history); ps(n_done_trials)=value(thisstimlog(n_done_trials)); + ps1{n_done_trials}=value(Category_Dist); stimulus_history.value=ps; - - case 'update_stimulus_history_nan' - ps=value(stimulus_history); - ps(n_done_trials)=value(thisstimlog(n_done_trials));%nan; - stimulus_history.value=ps; + stimulus_distribution_history.value = ps1; %% Case hide case 'hide' diff --git a/Protocols/@ArpitSoundCatContinuous/private/createTemporalColormap.m b/Protocols/@ArpitSoundCatContinuous/private/createTemporalColormap.m new file mode 100644 index 00000000..163f89f9 --- /dev/null +++ b/Protocols/@ArpitSoundCatContinuous/private/createTemporalColormap.m @@ -0,0 +1,24 @@ +function cmap = createTemporalColormap(n_colors) + % createTemporalColormap Generates a colormap for sequential plotting. + % - Newest colors are bright and "hot" (e.g., yellow/red). + % - Oldest colors are darker and "cool" (e.g., deep blue/purple). + % - Hue, saturation, and brightness are all varied for maximum distinction. + + % Define the path in HSV color space + hue_start = 0.6; % Start at blue + hue_end = 0; % End at red + + sat_start = 0.8; % Start slightly desaturated + sat_end = 1.0; % End fully saturated + + val_start = 0.7; % Start dark + val_end = 1.0; % End at full brightness + + % Create linearly spaced vectors for Hue, Saturation, and Value + h = linspace(hue_start, hue_end, n_colors)'; + s = linspace(sat_start, sat_end, n_colors)'; + v = linspace(val_start, val_end, n_colors)'; + + % Convert the HSV values to an RGB colormap + cmap = hsv2rgb([h, s, v]); +end \ No newline at end of file diff --git a/Protocols/@ArpitSoundCatContinuous/private/realtimepsychometricFit.m b/Protocols/@ArpitSoundCatContinuous/private/realtimepsychometricFit.m new file mode 100644 index 00000000..b926d233 --- /dev/null +++ b/Protocols/@ArpitSoundCatContinuous/private/realtimepsychometricFit.m @@ -0,0 +1,155 @@ +function [y_pred, fitParams, methodUsed, fitStatus] = realtimepsychometricFit(stim, resp, rangeStim, options) +% realtimepsychometricFit Robust real-time psychometric fitting and plotting. +% +% This function fits a 4-parameter logistic psychometric function. It includes +% internal checks for data quality and fitting stability. +% +% Inputs: +% stim - vector of stimulus values. +% response - binary response vector (0 or 1). +% rangeStim - 1x2 or 1x3 vector for the stimulus grid [min, max]. +% options - (Optional) struct with fields: +% .MinTrials - Min trials to attempt fit (default: 15). +% .LapseUB - Upper bound for lapse rates (default: 0.1). +% .StdTol - Tolerance for stimulus std dev (default: 1e-6). +% .SlopeTol - Tolerance for slope parameter (default: 1e-5). +% +% Outputs: +% y_pred - Predicted y-values on a grid across rangeStim. +% fitParams - [mu, sigma, lapseL, lapseR] fitted parameters. +% methodUsed - String indicating the final fitting method used. +% fitStatus - String providing information on the fit quality/outcome. + +%% 1. Argument Handling & Pre-computation Guard Clauses +if nargin < 4, options = struct(); end +if ~isfield(options, 'MinTrials'), options.MinTrials = 30; end +if ~isfield(options, 'LapseUB'), options.LapseUB = 0.1; end +if ~isfield(options, 'StdTol'), options.StdTol = 1e-6; end +if ~isfield(options, 'SlopeTol'), options.SlopeTol = 1e-5; end + +fitParams = [nan,nan,nan,nan]; +y_pred = []; +fitStatus = 'Success'; % Assume success initially +% Ensure both inputs are vectors and have the same number of elements +if ~isvector(stim) || ~isvector(resp) + methodUsed = 'Fit Canceled'; + fitStatus = 'Inputs `stim` and `resp` must be vectors.'; + return; +end +if numel(stim) ~= numel(resp) + methodUsed = 'Fit Canceled'; + fitStatus = 'Inputs `stim` and `resp` must have the same number of elements.'; + return; +end + +% Enforce column vector orientation for consistency with fitting functions. +% The (:) operator robustly reshapes any vector into a column vector. +stim = stim(:); +resp = resp(:); + +% GUARD: Check for minimum number of trials +if numel(stim) < options.MinTrials + methodUsed = 'Fit Canceled'; + fitStatus = sprintf('Insufficient trials (n=%d, min=%d)', numel(stim), options.MinTrials); + return; +end + +% GUARD: Check for stimulus variance +if std(stim) < options.StdTol + methodUsed = 'Fit Canceled'; + fitStatus = 'Insufficient stimulus variance'; + return; +end + +%% 2. Initial Fit (Ridge) +stim_std = (stim - mean(stim)) / std(stim); +methodUsed = 'ridge'; + +try + % --- Ridge logistic fit for mu and sigma --- + [B, FitInfo] = lassoglm(stim_std, resp, 'binomial', 'Alpha', 1e-6, 'Lambda', 0.1); % Alpha near 0 for ridge + b0 = FitInfo.Intercept; + b1 = B(1); + + % GUARD: Check for near-zero or excessively large slope from ridge fit + if abs(b1) < options.SlopeTol + throw(MException('MyFit:ZeroSlope', 'Initial ridge fit found no slope.')); + end + if abs(b1) > 10 % Check for quasi-perfect separation + throw(MException('MyFit:SteepSlope', 'Initial ridge fit is too steep.')); + end + + mu = -b0 / b1 * std(stim) + mean(stim); + sigma = std(stim) / b1; + + % Residual lapse estimate for initialization + predTrain = 1 ./ (1 + exp(-(b0 + b1 * stim_std))); + lapseEstimate = mean(abs(predTrain - resp)); + lapseL = min(max(lapseEstimate * 1.2, 0), options.LapseUB); + lapseR = lapseL; + +catch ME + % --- Robust fallback if ridge fit fails for any reason --- + methodUsed = 'robust'; + fitStatus = sprintf('Switched to robust fit. Reason: %s', ME.message); + + try + brob = robustfit(stim, resp, 'logit'); + + % GUARD: Check for near-zero slope from robust fit + if abs(brob(2)) < options.SlopeTol + methodUsed = 'Fit Failed'; + fitStatus = 'Could not find a slope with either method.'; + return; + end + + mu = -brob(1) / brob(2); + sigma = 1 / brob(2); + lapseL = 0.02; % Use fixed lapse guesses for robust fallback + lapseR = 0.02; + catch + methodUsed = 'Fit Failed'; + fitStatus = 'Robustfit also failed to converge.'; + return; + end +end + +%% 3. Final Nonlinear Fit (lsqcurvefit) +psychometricFun = @(params, x) params(3) + (1 - params(3) - params(4)) ./ ... + (1 + exp(-(x - params(1)) / params(2))); + +% Use a slightly wider range for bounds to avoid railing issues +stim_min = min(rangeStim); +stim_max = max(rangeStim); +range_width = stim_max - stim_min; + +init = [mu, sigma, lapseL, lapseR]; +lb = [stim_min - 0.1*range_width, 0.1, 0, 0]; +ub = [stim_max + 0.1*range_width, 15, options.LapseUB, options.LapseUB]; + +% Constrain initial guess to be within bounds +init(1) = max(min(init(1), ub(1)), lb(1)); +init(2) = max(min(init(2), ub(2)), lb(2)); + +optimOpts = optimset('Display', 'off'); +fitParams = lsqcurvefit(psychometricFun, init, stim, resp, lb, ub, optimOpts); + +%% 4. Post-Fit Sanity Checks & Prediction +% CHECK: Did the fit "rail" against the stimulus range bounds? +bound_tolerance = 0.01 * range_width; +if (fitParams(1) <= lb(1) + bound_tolerance) || (fitParams(1) >= ub(1) - bound_tolerance) + fitStatus = 'Warning: Threshold is at the edge of the stimulus range.'; + warning('realtimepsychometricFit:%s', fitStatus); +end + +% CHECK: Did the lapse rates hit their upper bound? +if (fitParams(3) >= options.LapseUB*0.99) || (fitParams(4) >= options.LapseUB*0.99) + fitStatus = 'Warning: Lapse rate may be underestimated (at upper bound).'; + warning('realtimepsychometricFit:%s', fitStatus); +end + +% Predict on a fine grid +xGrid = linspace(stim_min, stim_max, 300)'; +y_pred = psychometricFun(fitParams, xGrid); + +end \ No newline at end of file diff --git a/sendsummary_error_log.txt b/sendsummary_error_log.txt deleted file mode 100644 index 6229bd22..00000000 --- a/sendsummary_error_log.txt +++ /dev/null @@ -1,30 +0,0 @@ -Failed to send summary to sql -Unable to resolve the name 'perf.violation_rate'. - 7×1 struct array with fields: - - file - name - line - -Error occurred during sendsummary execution: -Unable to resolve the name 'perf.violation_rate'. - - -******* - - WARNING: Unable to complete 'close' action on protocol - Last error was: "Error using fprintf -Function is not defined for 'struct' inputs." - - - -******* - - -ans = - - dispatcher object: 1-by-1 - -Warning: B-data NOT enabled for liquid calibration. No calibration values received. -47 if isfield(protocol_data, 'violation_rate') -if system_dependent('IsDebugMode')==1, dbquit; end From 037eb8be9d248ce4a2848f9f73272d8190ac6d1c Mon Sep 17 00:00:00 2001 From: Arpit Date: Sat, 5 Jul 2025 22:02:21 +0100 Subject: [PATCH 144/164] added psychometric for ArpitSoundCat --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index f3381d9c..452cc72f 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,7 @@ .svn/ SoloData/ Bonsai/ +training_videos/ ExperPort/Settings/Settings_Custom_v0_789.conf ExperPort/Settings/Settings_Custom_v2_0.conf ExperPort/Settings/Settings_Custom.conf From 4fc04b768722e26896ee3e36dc0148d1bcbfaa66 Mon Sep 17 00:00:00 2001 From: Arpit Date: Sun, 6 Jul 2025 20:17:10 +0100 Subject: [PATCH 145/164] changes made to accomodate running without runrats --- .../@bonsaicamera/BonsaiCameraInterface.m | 10 +++++++-- ExperPort/Plugins/@saveload/SavingSection.m | 13 +++++++----- .../ArpitSoundCatContinuous.m | 9 +++++++- .../Rigtest_singletrial.m | 21 +++++++++++++++---- 4 files changed, 41 insertions(+), 12 deletions(-) diff --git a/ExperPort/Plugins/@bonsaicamera/BonsaiCameraInterface.m b/ExperPort/Plugins/@bonsaicamera/BonsaiCameraInterface.m index 5350b7f1..e54bb25e 100644 --- a/ExperPort/Plugins/@bonsaicamera/BonsaiCameraInterface.m +++ b/ExperPort/Plugins/@bonsaicamera/BonsaiCameraInterface.m @@ -248,7 +248,9 @@ case 'record_start' % create the folder to save the videos - mkdir(value(Video_Saving_Folder)); + if not(isfolder(value(Video_Saving_Folder))) + mkdir(value(Video_Saving_Folder)); + end pause(3); % To be on safer side that the last streaming message reached lets % resend it @@ -280,10 +282,14 @@ write(value(UDPSender), oscMsg_file_directory, "uint8", bonsaiComputerIP,bonsaiUdpPort); - case 'video_filepath' + case 'get_video_filepath' varargout{1} = value(Video_Saving_Folder); + case 'set_video_filepath' + + Video_Saving_Folder.value = varargin{3}; + %% close bonsai and command window case 'close' diff --git a/ExperPort/Plugins/@saveload/SavingSection.m b/ExperPort/Plugins/@saveload/SavingSection.m index cec0a615..0f5d5d1c 100644 --- a/ExperPort/Plugins/@saveload/SavingSection.m +++ b/ExperPort/Plugins/@saveload/SavingSection.m @@ -93,21 +93,20 @@ %the folder to save video files. If it runss into error or runrat is %not running then as a default it sets values as 'experimenter and %'ratname' + try [~,experimenter_name, rat_name] = runrats('exp_rat_names'); [~, settings_file_str] = runrats('get_settings_file_path'); + [~, settings_file_load_time_num] = runrats('get_settings_file_load_time'); catch %#ok settings_file_str = ''; experimenter_name = 'experimenter'; rat_name = 'ratname'; - end - SoloParamHandle(obj, 'settings_file', 'value', settings_file_str); - try - [~, settings_file_load_time_num] = runrats('get_settings_file_load_time'); - catch %#ok settings_file_load_time_num = 0; end + SoloParamHandle(obj, 'settings_file_load_time', 'value', settings_file_load_time_num); + SoloParamHandle(obj, 'settings_file', 'value', settings_file_str); EditParam(obj, 'experimenter', experimenter_name, x, y); next_row(y, 1.5); EditParam(obj, 'ratname', rat_name, x, y); next_row(y, 1.5); @@ -211,6 +210,10 @@ experimenter.value=x; %#ok return; + case 'set_setting_info' + settings_file.value = varargin{3}; + settings_file_load_time.value = varargin{4}; + case 'savesets', % ------------ CASE SAVESETS -------------------- if nargin == 3, varargin = {x}; elseif nargin == 4, varargin = {x y}; diff --git a/Protocols/@ArpitSoundCatContinuous/ArpitSoundCatContinuous.m b/Protocols/@ArpitSoundCatContinuous/ArpitSoundCatContinuous.m index baf3e9c2..8ab60bce 100644 --- a/Protocols/@ArpitSoundCatContinuous/ArpitSoundCatContinuous.m +++ b/Protocols/@ArpitSoundCatContinuous/ArpitSoundCatContinuous.m @@ -143,7 +143,14 @@ BonsaiCameraInterface(obj,'record_start'); - + case 'set_setting_params' + + SavingSection(obj,'set','ratname',varargin{4}); + SavingSection(obj,'set','experimenter',varargin{3}); + SavingSection(obj,'set_setting_info',varargin{5},varargin{6}); + BonsaiCameraInterface(obj,'set_video_filepath',varargin{7}); + + %% prepare next trial case 'prepare_next_trial' diff --git a/Protocols/@Rigtest_singletrial/Rigtest_singletrial.m b/Protocols/@Rigtest_singletrial/Rigtest_singletrial.m index c663c272..10518a54 100644 --- a/Protocols/@Rigtest_singletrial/Rigtest_singletrial.m +++ b/Protocols/@Rigtest_singletrial/Rigtest_singletrial.m @@ -225,10 +225,23 @@ nTrials.value = n_done_trials; % <~> If we've completed our trial, tell RunRats that we're done. - if nTrials > 0 && runrats('is_running'), - runrats('rigtest_singletrial_is_complete'); - return; - end; + + %% Modified by Arpit to run without Runrats + % <~> If we've completed our trial, tell RunRats that we're done. + if n_done_trials > 0 + try + if nTrials > 0 && runrats('is_running') + runrats('rigtest_singletrial_is_complete'); + return; + end + catch + if nTrials > 0 + Rigtest_singletrial(obj,'close') + return; + end + end + end + % <~> end adaptation for single-trial use :P From cea9925f4a3041974d9222ebcfcdde8a3aee7c4b Mon Sep 17 00:00:00 2001 From: Arpit Date: Tue, 8 Jul 2025 15:27:45 +0100 Subject: [PATCH 146/164] error debug for changes to run without runrat --- .../@bonsaicamera/BonsaiCameraInterface.m | 2 +- ExperPort/Plugins/@saveload/SavingSection.m | 4 ++-- ...itSoundCatContinuous_lida_LP12_250704a.mat | Bin 0 -> 14110 bytes ...itSoundCatContinuous_lida_LP12_250707a.mat | Bin 0 -> 14084 bytes .../ArpitSoundCatContinuous.m | 10 ++++----- .../Rigtest_singletrial.m | 20 ++++++------------ 6 files changed, 14 insertions(+), 22 deletions(-) create mode 100644 ExperPort/settings_@ArpitSoundCatContinuous_lida_LP12_250704a.mat create mode 100644 ExperPort/settings_@ArpitSoundCatContinuous_lida_LP12_250707a.mat diff --git a/ExperPort/Plugins/@bonsaicamera/BonsaiCameraInterface.m b/ExperPort/Plugins/@bonsaicamera/BonsaiCameraInterface.m index e54bb25e..a0cb8a64 100644 --- a/ExperPort/Plugins/@bonsaicamera/BonsaiCameraInterface.m +++ b/ExperPort/Plugins/@bonsaicamera/BonsaiCameraInterface.m @@ -288,7 +288,7 @@ case 'set_video_filepath' - Video_Saving_Folder.value = varargin{3}; + Video_Saving_Folder.value = varargin{1}; %% close bonsai and command window case 'close' diff --git a/ExperPort/Plugins/@saveload/SavingSection.m b/ExperPort/Plugins/@saveload/SavingSection.m index 0f5d5d1c..23722f12 100644 --- a/ExperPort/Plugins/@saveload/SavingSection.m +++ b/ExperPort/Plugins/@saveload/SavingSection.m @@ -211,8 +211,8 @@ return; case 'set_setting_info' - settings_file.value = varargin{3}; - settings_file_load_time.value = varargin{4}; + settings_file.value = x; + settings_file_load_time.value = y; case 'savesets', % ------------ CASE SAVESETS -------------------- if nargin == 3, varargin = {x}; diff --git a/ExperPort/settings_@ArpitSoundCatContinuous_lida_LP12_250704a.mat b/ExperPort/settings_@ArpitSoundCatContinuous_lida_LP12_250704a.mat new file mode 100644 index 0000000000000000000000000000000000000000..af2910e4ebcb96b13ad2637dabbc8993a3b3af21 GIT binary patch literal 14110 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 P$>*RlVZHM6MvD3$nwN&w literal 0 HcmV?d00001 diff --git a/ExperPort/settings_@ArpitSoundCatContinuous_lida_LP12_250707a.mat b/ExperPort/settings_@ArpitSoundCatContinuous_lida_LP12_250707a.mat new file mode 100644 index 0000000000000000000000000000000000000000..0e14b4de078d62f6a2813db1df418236304ffb8f GIT binary patch literal 14084 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

_Vc4PJE_8&tnm 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 literal 0 HcmV?d00001 diff --git a/Protocols/@ArpitSoundCatContinuous/ArpitSoundCatContinuous.m b/Protocols/@ArpitSoundCatContinuous/ArpitSoundCatContinuous.m index 8ab60bce..ccc9b28a 100644 --- a/Protocols/@ArpitSoundCatContinuous/ArpitSoundCatContinuous.m +++ b/Protocols/@ArpitSoundCatContinuous/ArpitSoundCatContinuous.m @@ -127,7 +127,7 @@ SessionDefinition(obj, 'init', x, y, value(myfig)); next_row(y, 2); %#ok ArpitSoundCatContinuousSMA(obj, 'init'); - feval(mfilename, obj, 'prepare_next_trial'); + % feval(mfilename, obj, 'prepare_next_trial'); case 'change_water_modulation_params' display_guys = [1 150 300]; @@ -145,10 +145,10 @@ case 'set_setting_params' - SavingSection(obj,'set','ratname',varargin{4}); - SavingSection(obj,'set','experimenter',varargin{3}); - SavingSection(obj,'set_setting_info',varargin{5},varargin{6}); - BonsaiCameraInterface(obj,'set_video_filepath',varargin{7}); + SavingSection(obj,'set','ratname',varargin{1}); + SavingSection(obj,'set','experimenter',varargin{2}); + SavingSection(obj,'set_setting_info',varargin{3},varargin{4}); + BonsaiCameraInterface(obj,'set_video_filepath',varargin{5}); %% prepare next trial diff --git a/Protocols/@Rigtest_singletrial/Rigtest_singletrial.m b/Protocols/@Rigtest_singletrial/Rigtest_singletrial.m index 10518a54..dd6fd089 100644 --- a/Protocols/@Rigtest_singletrial/Rigtest_singletrial.m +++ b/Protocols/@Rigtest_singletrial/Rigtest_singletrial.m @@ -165,8 +165,8 @@ 'C', [0 0 0], ... 'R', 0.9*[1 0.66 0]); - [x, y] = PokesPlotSection(obj, 'init', x, y, ... - struct('states', my_state_colors, 'pokes', my_poke_colors)); + % [x, y] = PokesPlotSection(obj, 'init', x, y, ... + % struct('states', my_state_colors, 'pokes', my_poke_colors)); next_row(y); SubheaderParam(obj, 'title', 'Poke in lights', x, y); next_row(y); @@ -229,17 +229,12 @@ %% Modified by Arpit to run without Runrats % <~> If we've completed our trial, tell RunRats that we're done. if n_done_trials > 0 - try if nTrials > 0 && runrats('is_running') runrats('rigtest_singletrial_is_complete'); return; - end - catch - if nTrials > 0 - Rigtest_singletrial(obj,'close') + else return; end - end end % <~> end adaptation for single-trial use :P @@ -339,9 +334,6 @@ 'self_timer', 0.2, 'input_to_statechange', {'Tup', 'check_next_trial_ready'}); dispatcher('send_assembler', sma, 'final_state'); - % Defaul behavior of following call is that every 20 trials, the data - % gets saved, not interactive, no commit to CVS. -% SavingSection(obj, 'autosave_data'); %--------------------------------------------------------------- % CASE TRIAL_COMPLETED @@ -350,20 +342,20 @@ % Do any updates in the protocol that need doing: feval(mfilename, 'update'); % And PokesPlot needs completing the trial: - PokesPlotSection(obj, 'trial_completed'); + % PokesPlotSection(obj, 'trial_completed'); %--------------------------------------------------------------- % CASE UPDATE %--------------------------------------------------------------- case 'update' - PokesPlotSection(obj, 'update'); + % PokesPlotSection(obj, 'update'); %--------------------------------------------------------------- % CASE CLOSE %--------------------------------------------------------------- case 'close' - PokesPlotSection(obj, 'close'); + % PokesPlotSection(obj, 'close'); if exist('myfig', 'var') && isa(myfig, 'SoloParamHandle') && ishandle(value(myfig)), %#ok delete(value(myfig)); end; From 093aae48dab0d34ab9319af9ee8839e6026a8f3a Mon Sep 17 00:00:00 2001 From: Arpit Date: Tue, 8 Jul 2025 15:34:52 +0100 Subject: [PATCH 147/164] added file to gitignore --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 452cc72f..3b4f6f90 100644 --- a/.gitignore +++ b/.gitignore @@ -6,6 +6,7 @@ ExperPort/Settings/Settings_Custom_v0_789.conf ExperPort/Settings/Settings_Custom_v2_0.conf ExperPort/Settings/Settings_Custom.conf ExperPort/sendsummary_error_log.txt +/ExperPort/settings_@*.mat PASSWORD_CONFIG-DO_NOT_VERSIONCONTROL.mat .gitattributes .DS_Store From 923d407ec9836d110078ff278a54221b9c652475 Mon Sep 17 00:00:00 2001 From: Arpit Date: Wed, 9 Jul 2025 18:47:46 +0100 Subject: [PATCH 148/164] changes to make protocol work without runrats --- .../Modules/@dispatcher/RunningSection.m | 1 + .../ArpitSoundCatContinuous.m | 29 ++++----- .../PsychometricSection.m | 7 ++- .../Rigtest_singletrial.m | 62 +++++++++++-------- 4 files changed, 54 insertions(+), 45 deletions(-) diff --git a/ExperPort/Modules/@dispatcher/RunningSection.m b/ExperPort/Modules/@dispatcher/RunningSection.m index ffdffd2a..3d82ca5a 100644 --- a/ExperPort/Modules/@dispatcher/RunningSection.m +++ b/ExperPort/Modules/@dispatcher/RunningSection.m @@ -284,6 +284,7 @@ runrats('crashed',me); Running.value = 0; else + Running.value = 0; rethrow(me); end diff --git a/Protocols/@ArpitSoundCatContinuous/ArpitSoundCatContinuous.m b/Protocols/@ArpitSoundCatContinuous/ArpitSoundCatContinuous.m index ccc9b28a..6ad2ff80 100644 --- a/Protocols/@ArpitSoundCatContinuous/ArpitSoundCatContinuous.m +++ b/Protocols/@ArpitSoundCatContinuous/ArpitSoundCatContinuous.m @@ -111,7 +111,7 @@ HeaderParam(obj, 'prot_title', [mfilename ': ' expmtr ', ' rname], x, y, 'position', [10 figpos(4)-25, 800 20]); [x, y] = WaterValvesSection(obj, 'init', x, y);next_row(y); - [x, y] = PokesPlotSection(obj, 'init', x, y);next_row(y); + % [x, y] = PokesPlotSection(obj, 'init', x, y);next_row(y); [x, y] = CommentsSection(obj, 'init', x, y);next_row(y); [x, y] = BonsaiCameraInterface(obj,'init',x,y,name,expmtr,rname);next_row(y); @@ -126,7 +126,12 @@ x=oldx; y=oldy; SessionDefinition(obj, 'init', x, y, value(myfig)); next_row(y, 2); %#ok + % saving auto frequency to 20 just in case changed running ephys + % experiment. If so then it will again change back to 1; + SavingSection(obj,'set_autosave_frequency',20); + ArpitSoundCatContinuousSMA(obj, 'init'); + % feval(mfilename, obj, 'prepare_next_trial'); case 'change_water_modulation_params' @@ -143,11 +148,12 @@ BonsaiCameraInterface(obj,'record_start'); - case 'set_setting_params' + case 'set_setting_params' % special case used during ephys experiments when not using runrats SavingSection(obj,'set','ratname',varargin{1}); SavingSection(obj,'set','experimenter',varargin{2}); SavingSection(obj,'set_setting_info',varargin{3},varargin{4}); + SavingSection(obj,'set_autosave_frequency',1); % saving setting every trial instead of 20 BonsaiCameraInterface(obj,'set_video_filepath',varargin{5}); @@ -183,12 +189,13 @@ % Update the Metrics Calculated PerformanceSection(obj,'evaluate'); PsychometricSection(obj, 'update'); + % Do any updates in the protocol that need doing: feval(mfilename, 'update'); %% update case 'update' - PokesPlotSection(obj, 'update'); + % PokesPlotSection(obj, 'update'); if n_done_trials==1 [expmtr, rname]=SavingSection(obj, 'get_info'); prot_title.value=[mfilename ' on rig ' get_hostname ' : ' expmtr ', ' rname '. Started at ' datestr(now, 'HH:MM')]; @@ -196,7 +203,7 @@ %% close case 'close' - PokesPlotSection(obj, 'close'); + % PokesPlotSection(obj, 'close'); SideSection(obj, 'close'); StimulusSection(obj,'close'); BonsaiCameraInterface(obj,'close'); @@ -218,20 +225,6 @@ StimulusSection(obj,'hide'); SessionDefinition(obj, 'run_eod_logic_without_saving'); - % perf = PerformanceSection(obj, 'evaluate'); - % cp_durs = SideSection(obj, 'get_cp_history'); - % - % [stim1dur] = SideSection(obj,'get_stimdur_history'); - %stim_history = StimulatorSection(obj,'get_history'); - % pd.hits=hit_history(:); - % pd.sides=previous_sides(:); - % pd.viols=violation_history(:); - % pd.timeouts=timeout_history(:); - % pd.cp_durs=cp_durs(:); - % pd.stim1dur=stim1dur(:); - %pd.stimul=stim_history(:); - % sendsummary(obj,'protocol_data',pd); - sendsummary(obj); %% otherwise diff --git a/Protocols/@ArpitSoundCatContinuous/PsychometricSection.m b/Protocols/@ArpitSoundCatContinuous/PsychometricSection.m index 2e33d9de..acb9c7b3 100644 --- a/Protocols/@ArpitSoundCatContinuous/PsychometricSection.m +++ b/Protocols/@ArpitSoundCatContinuous/PsychometricSection.m @@ -273,6 +273,9 @@ xGrid = linspace(Stim_Params(1), Stim_Params(3), 300)'; cla(value(axplot2)); hold (value(axplot2),'on') + + colors_to_use = createTemporalColormap(value(thiscontext)); + for n_plot = 1:value(thiscontext) eval(sprintf('trial_start = value(Context%i_trialStart);',n_plot)); @@ -319,8 +322,8 @@ % Plot the Psychometric if ~(contains(methodUsed,'Failed') || contains(methodUsed,'Canceled')) - plot(value(axplot2),xGrid, y_pred, 'b-', 'LineWidth', 2); - xline(value(axplot2),fitParams(1), 'g--', sprintf('PSE (%.2f)', fitParams(1))); + plot(value(axplot2),xGrid, y_pred, 'Color', colors_to_use(n_plot, :), 'LineWidth', 2,'DisplayName',sprintf('Context %s',category_selected)); + xline(value(axplot2),fitParams(1), '--','Color', colors_to_use(n_plot, :), 'Label', sprintf('PSE (%.2f)', fitParams(1))); end end diff --git a/Protocols/@Rigtest_singletrial/Rigtest_singletrial.m b/Protocols/@Rigtest_singletrial/Rigtest_singletrial.m index dd6fd089..eb93bde0 100644 --- a/Protocols/@Rigtest_singletrial/Rigtest_singletrial.m +++ b/Protocols/@Rigtest_singletrial/Rigtest_singletrial.m @@ -132,8 +132,8 @@ set(value(myfig), 'Name', name, 'Tag', name, ... 'closerequestfcn', 'dispatcher(''close_protocol'')', 'MenuBar', 'none'); - - + % This flag will be set to 1 when the single trial is complete. + SoloParamHandle(obj, 'ManualTestComplete', 'value', 0, 'saveable', 0); % At this point we have one SoloParamHandle, myfig % Let's put the figure where we want it and give it a reasonable size: set(value(myfig), 'Position', [485 144 300 400]); @@ -148,23 +148,24 @@ next_row(y); DispParam(obj, 'nTrials', 0, x, y); next_row(y); - % For plotting with the pokesplot plugin, we need to tell it what - % colors to plot with: - my_state_colors = struct( ... - 'wait_for_left', [0.2 0.5 0.2], ... - 'in_left', [0.5 0.5 1], ... - 'in_center', [0.5 1 0.5], ... - 'in_right', [1 0.5 0.5], ... - 'state_0', [1 1 1], ... - 'final_state', [0.5 0 0], ... - 'check_next_trial_ready', [0.7 0.7 0.7]); - % In pokesplot, the poke colors have a default value, so we don't need - % to specify them, but here they are so you know how to change them. - my_poke_colors = struct( ... - 'L', 0.6*[1 0.66 0], ... - 'C', [0 0 0], ... - 'R', 0.9*[1 0.66 0]); + % % For plotting with the pokesplot plugin, we need to tell it what + % % colors to plot with: + % my_state_colors = struct( ... + % 'wait_for_left', [0.2 0.5 0.2], ... + % 'in_left', [0.5 0.5 1], ... + % 'in_center', [0.5 1 0.5], ... + % 'in_right', [1 0.5 0.5], ... + % 'state_0', [1 1 1], ... + % 'final_state', [0.5 0 0], ... + % 'check_next_trial_ready', [0.7 0.7 0.7]); + % % In pokesplot, the poke colors have a default value, so we don't need + % % to specify them, but here they are so you know how to change them. + % my_poke_colors = struct( ... + % 'L', 0.6*[1 0.66 0], ... + % 'C', [0 0 0], ... + % 'R', 0.9*[1 0.66 0]); + % % [x, y] = PokesPlotSection(obj, 'init', x, y, ... % struct('states', my_state_colors, 'pokes', my_poke_colors)); @@ -229,14 +230,25 @@ %% Modified by Arpit to run without Runrats % <~> If we've completed our trial, tell RunRats that we're done. if n_done_trials > 0 - if nTrials > 0 && runrats('is_running') - runrats('rigtest_singletrial_is_complete'); - return; - else - return; - end + + % Set our completion flag to 1. The main GUI will be polling for this. + ManualTestComplete.value = 1; + + if nTrials > 0 && runrats('is_running') + runrats('rigtest_singletrial_is_complete'); + return; + else % probably called by another function so need to stop the dispatcher from here itself + % Send a dummy state machine that does nothing and ends quickly. + % This prevents the protocol from running a second trial. + sma = StateMachineAssembler('full_trial_structure'); + sma = add_state(sma, 'name', 'do_nothing', 'self_timer', 0.01, ... + 'input_to_statechange', {'Tup', 'check_next_trial_ready'}); + dispatcher('send_assembler', sma, 'do_nothing'); + return; % IMPORTANT: Exit here to prevent building the real state machine again. + + end end - + % <~> end adaptation for single-trial use :P From d17a7cdd8cac96b8c81c2a80c830c11a90eb989e Mon Sep 17 00:00:00 2001 From: Arpit Date: Fri, 11 Jul 2025 11:39:08 +0100 Subject: [PATCH 149/164] new module OpenEphys_neuroblueprint for ephys --- .../OpenEphysHTTPServer.m | 554 +++++++++++++++ .../OpenEphys_Neuroblueprint.m | 652 ++++++++++++++++++ .../Modules/@dispatcher/RunningSection.m | 5 +- ExperPort/Plugins/@saveload/SavingSection.m | 7 +- .../Rigtest_singletrial.m | 41 +- Protocols/sendsummary_error_log.txt | 12 + ...itSoundCatContinuous_lida_LP12_250708c.mat | Bin 0 -> 14120 bytes 7 files changed, 1258 insertions(+), 13 deletions(-) create mode 100644 ExperPort/Modules/@OpenEphys_Neuroblueprint/OpenEphysHTTPServer.m create mode 100644 ExperPort/Modules/@OpenEphys_Neuroblueprint/OpenEphys_Neuroblueprint.m create mode 100644 Protocols/settings_@ArpitSoundCatContinuous_lida_LP12_250708c.mat diff --git a/ExperPort/Modules/@OpenEphys_Neuroblueprint/OpenEphysHTTPServer.m b/ExperPort/Modules/@OpenEphys_Neuroblueprint/OpenEphysHTTPServer.m new file mode 100644 index 00000000..891207e9 --- /dev/null +++ b/ExperPort/Modules/@OpenEphys_Neuroblueprint/OpenEphysHTTPServer.m @@ -0,0 +1,554 @@ +% MIT License +% +% Copyright (c) 2021 Open Ephys +% +% Permission is hereby granted, free of charge, to any person obtaining a copy +% of this software and associated documentation files (the "Software"), to deal +% in the Software without restriction, including without limitation the rights +% to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +% copies of the Software, and to permit persons to whom the Software is +% furnished to do so, subject to the following conditions: +% +% The above copyright notice and this permission notice shall be included in all +% copies or substantial portions of the Software. +% +% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +% IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +% FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +% AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +% LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +% OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +% SOFTWARE. + +classdef OpenEphysHTTPServer < handle + + properties (Constant) + end + + properties + + address + options + + end + + methods + + function self = OpenEphysHTTPServer(host, port) + + self.address = strcat('http://', host, ':', num2str(port)); + self.options = weboptions(... + 'MediaType', 'application/json',... + 'RequestMethod', 'put'); + + end + + end + + methods + + function resp = send(self, varargin) + +% Send a request to the server. +% +% Parameters +% ---------- +% endpoint : String +% The API endpoint for the request. +% Must begin with "/api/" +% payload (optional): Dictionary +% The payload to send with the request. +% +% If a payload is specified, a PUT request +% will be used; otherwise it will be a GET request. + + + endpoint = varargin{1}; + if nargin > 2 + payload = varargin{2}; + end + + try + + if nargin == 2 + resp = webread(strcat(self.address, endpoint)); + else + resp = webwrite(strcat(self.address, endpoint), payload, self.options); + end + + catch ME +% TODO: Catch matlab equivalents of these +% except requests.exceptions.Timeout: +% # Maybe set up for a retry, or continue in a retry loop +% print("Timeout") +% except requests.exceptions.TooManyRedirects: +% # Tell the user their URL was bad and try a different one +% print("Bad URL") +% except requests.exceptions.RequestException as e: +% # Open Ephys server needs to be enabled +% print("Open Ephys HTTP Server likely not enabled") + + resp = "GUI was closed!"; + end + + end + + function resp = load(self, path) + +% Load a configuration file. +% +% Parameters +% ---------- +% path : String +% The path to the configuration file. + + payload = struct('path', path); + + resp = self.send('/api/load', payload); + pause(1); + + end + + function processors = getProcessorList(self) + +% Returns all available processors in the GUI's Processor List + + data = self.send('/api/processors/list'); + processors = string(char({data.processors.name})); + + end + + function processors = getProcessors(self, varargin) + +% Get the list of processors. + +% Parameters +% ---------- +% filterByName : String (Optional) +% Filter the list by processor name. + + data = self.send('/api/processors'); + processors = data.processors; + if nargin > 1 + indices = cellfun(@(v)strcmp(v,varargin{1}),{processors.name}); + processors = processors(indices); + end + + end + + function resp = clearSignalChain(self) + +% Clear the signal chain. + + resp = self.send('/api/processors/clear'); + + end + + function resp = addProcessor(self, name, varargin) + +% Add a processor to the signal chain. +% +% Parameters +% ---------- +% name : String +% The name of the processor to add (e.g. "Record Node") +% source : Integer +% The 3-digit processor ID of the source (e.g. 101) +% dest : Integer +% The 3-digit processor ID of the destination (e.g. 102) + + endpoint = '/api/processors/add'; + payload = struct('name', name); + +% If only processor name is specified, set source to most recently added processor + if nargin == 2 + existingProcessors = self.getProcessors(); + if ~isempty(existingProcessors) + index = find([existingProcessors.id] == max([existingProcessors.id])); + mostRecentProcessor = existingProcessors(index); + payload.source_id = mostRecentProcessor.id; + end + elseif nargin > 2 + payload.source_id = varargin{1}; + if nargin == 4 + payload.dest_id = varargin{2}; + end + end + + resp = self.send(endpoint, payload); + + end + + + function resp = deleteProcessor(self, id) + +% Delete a processor. +% +% Parameters +% ---------- +% processor_id : Integer +% The 3-digit processor ID (e.g. 101) +% + endpoint = '/api/processors/delete'; + payload = struct('id', id); + + resp = self.send(endpoint, payload); + + end + + + function resp = getParameters(self, processorId, streamIndex) + +% Get parameters for a stream. +% +% Parameters +% ---------- +% processorId : Integer +% The 3-digit processor ID (e.g. 101) +% streamIndex : Integer +% The index of the stream (e.g. 0). +% + endpoint = strcat('/api/processors/', num2str(processorId), '/streams/', num2str(streamIndex), '/parameters'); + resp = self.send(endpoint).parameters; + + end + + function resp = setParameters(self, processorID, streamIndex, paramName, value) + +% Update a parameter value +% +% Parameters +% ---------- +% processorID : Integer +% The 3-digit processor ID (e.g. 101) +% streamIndex : Integer +% The index of the stream (e.g. 0) +% paramName : String +% The parameter name (e.g. low_cut) +% value : Any +% The parameter value (must match the parameter type). +% Hint: Float parameters must be sent with a decimal +% included (e.g. 1000.0 instead of 1000) +% + endpoint = strcat('/api/processors/', num2str(processorID), '/streams/', num2str(streamIndex), '/parameters/', paramName); + +% TO FIX:matlab automatically casts doubles to int if no +% integers after the decimal point of a float. + if isa(value, 'double') + payload = struct('value', [], 'class', {'double'}); + payload.value = value + 0.00000000001; + else + payload = struct('value', value); + end + resp = self.send(endpoint, payload); + + end + + + function resp = getRecordingInfo(self, varargin) + +% Get recording information. +% +% Parameters +% ---------- +% key : String +% The key to get. +% + + data = self.send('/api/recording'); + if nargin == 1 + resp = data; + elseif isfield(data, varargin{1}) + resp = data.(varargin{1}); + else + resp = "Invalid key"; + end + end + + function resp = setParentDirectory(self, path) + +% Set the parent directory. +% +% Parameters +% ---------- +% path : String +% The path to the parent directory. + + payload = struct('parent_directory', path); + data = self.send('/api/recording', payload); + resp = data; + + end + + function resp = setPrependText(self, text) + +% Set the prepend text. +% +% Parameters +% ---------- +% text : String +% The text to prepend. +% + payload = struct('prepend_text', text); + data = self.send('/api/recording', payload); + resp = data; + + end + + function resp = setBaseText(self, text) + +% Set the base text. +% +% Parameters +% ---------- +% text : String +% The text to base name of the recording directory (see GUI docs). +% + payload = struct('base_text', text); + data = self.send('/api/recording', payload); + resp = data; + + end + + function resp = setAppendText(self, text) + +% Set the append text. +% +% Parameters +% ---------- +% text : String +% The text to append. +% + payload = struct('append_text', text); + data = self.send('/api/recording', payload); + resp = data; + + end + + + function resp = setStartNewDirectory(self) + +% Set if GUI should start a new directory for the next recording. + + payload = struct('start_new_directory', 'true'); + data = self.send('/api/recording', payload); + resp = data; + + end + + function resp = setFileReaderPath(self, nodeId, filePath) + +% Set the file path. + +% Parameters +% ---------- +% nodeId : Integer +% The node ID. +% filePath : String +% The file path. + + endpoint = strcat('/api/processors/', num2str(nodeId), '/config'); + payload = struct('text', strcat("file=", num2str(filePath))); + data = self.send(endpoint, payload); + + resp = data; + + end + + function resp = setFileReaderIndex(self, nodeId, fileIndex) + +% Set the file index. +% +% Parameters +% ---------- +% nodeId : Integer +% The node ID. +% fileIndex : Integer +% The file index. + + endpoint = strcat('/api/processors/', num2str(nodeId), '/config'); + payload = struct('text', strcat("index=", num2str(fileIndex))); + data = self.send(endpoint, payload); + + resp = data; + + end + + function resp = setRecordEngine(self, nodeId, engine) + +% Set the record engine for a record node. +% +% Parameters +% ---------- +% nodeId : Integer +% The node ID. +% engine: Integer +% The record engine index. + + endpoint = strcat('/api/processors/', num2str(nodeId), '/config'); + payload = struct('text', strcat("engine=", engine)); + data = self.send(endpoint, payload); + + resp = data; + + end + + function resp = setRecordPath(self, nodeId, directory) + +% Set the record path for a Record Node +% +% Parameters +% ---------- +% nodeId: Integer +% The node ID. +% directory : String +% The record path. + + endpoint = strcat('/api/recording/', num2str(nodeId)); + payload = struct('parent_directory', directory); + data = self.send(endpoint, payload); + + resp = data; + + end + function resp = getStatus(self) + +% Returns the current status of the GUI (IDLE, ACQUIRE, or RECORD) + + data = self.send('/api/status'); + + resp = data; + + end + + function resp = acquire(self, varargin) + +% Start acquisition. +% +% Parameters +% ---------- +% duration : Integer (optional) +% The acquisition duration in seconds. If given, the +% GUI will acquire data for the specified interval +% and then stop. +% +% By default, acquisition will continue until it +% is stopped by another command. + + payload = struct('mode', 'ACQUIRE'); + data = self.send('/api/status', payload); + + if nargin == 2 + payload = struct('mode', 'IDLE'); + pause(varargin{1}); + data = self.send('/api/status', payload); + end + + resp = data; + + end + + function resp = record(self, varargin) + +% Start recording. +% +% Parameters +% ---------- +% duration : Integer (optional) +% The record duration in seconds. If given, the +% GUI will record data for the specified interval +% and then stop. +% +% By default, recoridng will continue until it +% is stopped by another command. + + payload = struct('mode', 'RECORD'); + data = self.send('/api/status', payload); + + if nargin == 2 + payload = struct('mode', 'IDLE'); + pause(varargin{1}); + data = self.send('/api/status', payload); + end + + resp = data; + + end + + function resp = idle(self, varargin) + +% Stop acquisition/recording. +% +% Parameters +% ---------- +% duration : Integer (optional) +% The idle duration in seconds. If given, the +% GUI will idle for the specified interval +% and then return to its previous state. +% +% By default, this command will stop +% acquisition/recording and return immediately. + + mode = self.getStatus().mode; + + payload = struct('mode', 'IDLE'); + data = self.send('/api/status', payload); + + if nargin == 2 + payload = struct('mode', mode); + pause(varargin{1}); + data = self.send('/api/status', payload); + end + + resp = data; + + end + + function resp = message(self, message) + +% Broadcast a message to all processors during acquisition +% +% Parameters +% ---------- +% message : String +% The message to send. + + payload = struct('text', message); + data = self.send('/api/message', payload); + + resp = data; + + end + + function resp = config(self, nodeId, message) + +% Send a configuration message to a specific processor. +% +% Parameters +% ---------- +% nodeId : Integer +% The 3-digit processor ID (e.g. 101) +% message : String +% The message to send. + + endpoint = strcat('/api/processors/', num2str(nodeId), '/config'); + payload = struct('text', message); + data = self.send(endpoint, payload); + + resp = data; + + end + + function resp = quit(self) + +% Quit the GUI + + payload = struct('command', 'quit'); + data = self.send('/api/window', payload); + + resp = data; + end + + end + +end \ No newline at end of file diff --git a/ExperPort/Modules/@OpenEphys_Neuroblueprint/OpenEphys_Neuroblueprint.m b/ExperPort/Modules/@OpenEphys_Neuroblueprint/OpenEphys_Neuroblueprint.m new file mode 100644 index 00000000..074f5fca --- /dev/null +++ b/ExperPort/Modules/@OpenEphys_Neuroblueprint/OpenEphys_Neuroblueprint.m @@ -0,0 +1,652 @@ +function [obj, varargout] = OpenEphys_Neuroblueprint(varargin) +% This is a class-based version of the GUI controller, structured similarly +% to runrats.m. It manages the experimental workflow through different +% 'actions' called via a switch statement. +% +% PRE-REQUISITES: +% 1. 'open-ephys-matlab-tools' must be in the MATLAB path. +% 2. The Bpod/ratter/ExperPort environment, including all its dependencies +% (dispatcher, bSettings, etc.), must be fully configured and in the MATLAB path. + +% --- Boilerplate for class definition and action handling --- +obj = class(struct, mfilename); +varargout = {}; % Initialize varargout for actions that return values +if nargin==0 || (nargin==1 && ischar(varargin{1}) && strcmp(varargin{1}, 'empty')), + return; +end; +if isa(varargin{1}, mfilename), + if length(varargin) < 2 || ~ischar(varargin{2}), + error(['If called with a "%s" object as first arg, a second arg, a ' ... + 'string specifying the action, is required\n']); + else action = varargin{2}; varargin = varargin(3:end); + end; +else + action = varargin{1}; varargin = varargin(2:end); +end; +if ~ischar(action), error('The action parameter must be a string'); end; +GetSoloFunctionArgs(obj); +% --- End of boilerplate --- + + +switch action + % ========================================================================= + % CASE INIT + % ========================================================================= + case 'init' + % This case is called once to create the GUI and initialize all parameters. + + % Start Bpod if not already running + if evalin('base', 'exist(''BpodSystem'', ''var'')') + if evalin('base', '~isempty(BpodSystem)'), newstartup; else, flush; end + else, Bpod('COM5');newstartup; + end + + % --- State Variables as SoloParamHandles --- + SoloParamHandle(obj, 'currentState', 'value', 'Load'); + SoloParamHandle(obj, 'oe_controller', 'value', []); + SoloParamHandle(obj, 'behav_obj', 'value', []); + SoloParamHandle(obj, 'blinking_timer', 'value', []); + SoloParamHandle(obj, 'monitor_timer', 'value', []); + SoloParamHandle(obj, 'current_params', 'value', []); + SoloParamHandle(obj, 'session_base_path', 'value', ''); + SoloParamHandle(obj,'is_running','value',0); + scr = timer; + set(scr,'Period', 0.2,'ExecutionMode','FixedRate','TasksToExecute',Inf,... + 'BusyMode','drop','TimerFcn',[mfilename,'(''End_Continued'')']); + SoloParamHandle(obj, 'stopping_complete_timer', 'value', scr); + + % --- Create the GUI Figure --- + SoloParamHandle(obj, 'myfig', 'saveable', 0); + myfig.value = figure('Name', 'Open Ephys & BControl Controller',... + 'NumberTitle', 'off', 'MenuBar', 'none', 'ToolBar', 'none',... + 'Units', 'normalized', 'Position', [0.1, 0.1, 0.6, 0.8],... + 'Color', [0.94, 0.94, 0.94], ... + 'CloseRequestFcn', {@(h,e) feval(mfilename, obj, 'close')}); + + % --- UI Creation --- + handles = struct(); + + % Log Panel + uipanel('Title', 'Activity Log', 'FontSize', 12, 'BorderType', 'etchedin', 'BorderWidth', 1, 'Units', 'normalized', 'Position', [0.64, 0.03, 0.34, 0.94]); + handles.log_box = uicontrol('Style', 'edit', 'Units', 'normalized', 'Position', [0.65, 0.05, 0.32, 0.89], 'String', {'Log started...'}, 'Max', 10, 'Min', 1, 'HorizontalAlignment', 'left', 'Enable', 'inactive', 'BackgroundColor', [1, 1, 1]); + + % Panel 1: Behavior + p1 = uipanel('Title', '1. Behavior', 'FontSize', 12, 'FontWeight', 'bold', 'BorderType', 'etchedin', 'BorderWidth', 1, 'Units', 'normalized', 'Position', [0.02, 0.78, 0.6, 0.2]); + uicontrol(p1, 'Style', 'text', 'String', 'Protocol Name:', 'Units', 'normalized', 'Position', [0.05, 0.75, 0.22, 0.18], 'HorizontalAlignment', 'right'); + handles.protocol_edit = uicontrol(p1, 'Style', 'edit', 'String', 'ArpitSoundCatContinuous', 'Units', 'normalized', 'Position', [0.3, 0.75, 0.5, 0.2]); + handles.manual_test = uicontrol(p1, 'Style', 'checkbox', 'String', 'Manual Test', 'Value', 1, 'Units', 'normalized', 'Position', [0.81, 0.75, 0.18, 0.2]); + uicontrol(p1, 'Style', 'text', 'String', 'Experimenter:', 'Units', 'normalized', 'Position', [0.05, 0.5, 0.22, 0.18], 'HorizontalAlignment', 'right'); + handles.exp_edit = uicontrol(p1, 'Style', 'edit', 'String', 'lida', 'Units', 'normalized', 'Position', [0.3, 0.5, 0.65, 0.2]); + uicontrol(p1, 'Style', 'text', 'String', 'Rat Name:', 'Units', 'normalized', 'Position', [0.05, 0.25, 0.22, 0.18], 'HorizontalAlignment', 'right'); + handles.rat_name_edit = uicontrol(p1, 'Style', 'edit', 'String', 'LP12', 'Units', 'normalized', 'Position', [0.3, 0.25, 0.65, 0.2]); + uicontrol(p1, 'Style', 'text', 'String', 'Behav Code Path:', 'Units', 'normalized', 'Position', [0.01, 0.01, 0.28, 0.18], 'HorizontalAlignment', 'right'); + handles.behav_edit = uicontrol(p1, 'Style', 'edit', 'String', 'C:\ratter', 'Units', 'normalized', 'Position', [0.3, 0.01, 0.5, 0.2]); + handles.behav_browse = uicontrol(p1, 'Style', 'pushbutton', 'String', 'Browse...', 'Units', 'normalized', 'Position', [0.81, 0.01, 0.16, 0.22], 'Callback', {@(h,e) feval(mfilename, obj, 'browse_path', 'behav')}); + + % Panel 2: NeuroBlueprint Format + p2 = uipanel('Title', '2. NeuroBlueprint Format', 'FontSize', 12, 'FontWeight', 'bold', 'BorderType', 'etchedin', 'BorderWidth', 1, 'Units', 'normalized', 'Position', [0.02, 0.38, 0.6, 0.38]); + uicontrol(p2, 'Style', 'text', 'String', 'Project Name (Root):', 'Units', 'normalized', 'Position', [0.01, 0.85, 0.28, 0.1], 'HorizontalAlignment', 'right'); + handles.proj_edit = uicontrol(p2, 'Style', 'edit', 'String', 'sound_cat_rat', 'Units', 'normalized', 'Position', [0.3, 0.85, 0.65, 0.12]); + uicontrol(p2, 'Style', 'text', 'String', 'Subject ID:', 'Units', 'normalized', 'Position', [0.01, 0.7, 0.28, 0.1], 'HorizontalAlignment', 'right'); + handles.sub_edit = uicontrol(p2, 'Style', 'edit', 'String', '002', 'Units', 'normalized', 'Position', [0.3, 0.7, 0.65, 0.12]); + uicontrol(p2, 'Style', 'text', 'String', 'Local Path:', 'Units', 'normalized', 'Position', [0.01, 0.55, 0.28, 0.1], 'HorizontalAlignment', 'right'); + handles.local_edit = uicontrol(p2, 'Style', 'edit', 'String', 'C:\Ephys_Experiment_Data', 'Units', 'normalized', 'Position', [0.3, 0.55, 0.5, 0.12]); + handles.local_browse = uicontrol(p2, 'Style', 'pushbutton', 'String', 'Browse...', 'Units', 'normalized', 'Position', [0.81, 0.55, 0.16, 0.13], 'Callback', {@(h,e) feval(mfilename, obj, 'browse_path', 'local')}); + uicontrol(p2, 'Style', 'text', 'String', 'Central Path (for checking only):', 'Units', 'normalized', 'Position', [0.01, 0.4, 0.28, 0.1], 'HorizontalAlignment', 'right'); + handles.central_edit = uicontrol(p2, 'Style', 'edit', 'String', 'Z:\_projects', 'Units', 'normalized', 'Position', [0.3, 0.4, 0.5, 0.12]); + handles.central_browse = uicontrol(p2, 'Style', 'pushbutton', 'String', 'Browse...', 'Units', 'normalized', 'Position', [0.81, 0.4, 0.16, 0.13], 'Callback', {@(h,e) feval(mfilename, obj, 'browse_path', 'central')}); + uicontrol(p2, 'Style', 'text', 'String', 'Subfolders to Create:', 'Units', 'normalized', 'Position', [0.05, 0.2, 0.9, 0.1], 'HorizontalAlignment', 'left'); + handles.cb_ephys = uicontrol(p2, 'Style', 'checkbox', 'String', 'ephys', 'Value', 1, 'Units', 'normalized', 'Position', [0.05, 0.05, 0.2, 0.15]); + handles.cb_behav = uicontrol(p2, 'Style', 'checkbox', 'String', 'behav', 'Value', 1, 'Units', 'normalized', 'Position', [0.28, 0.05, 0.2, 0.15]); + handles.cb_anat = uicontrol(p2, 'Style', 'checkbox', 'String', 'anat', 'Value', 1, 'Units', 'normalized', 'Position', [0.51, 0.05, 0.2, 0.15]); + handles.cb_funcimg = uicontrol(p2, 'Style', 'checkbox', 'String', 'funcimg', 'Value', 0, 'Units', 'normalized', 'Position', [0.74, 0.05, 0.25, 0.15]); + + % Panel 3: OpenEphys Settings + p3 = uipanel('Title', '3. OpenEphys Settings', 'FontSize', 12, 'FontWeight', 'bold', 'BorderType', 'etchedin', 'BorderWidth', 1, 'Units', 'normalized', 'Position', [0.02, 0.15, 0.6, 0.21]); + uicontrol(p3, 'Style', 'text', 'String', 'GUI IP Address:', 'Units', 'normalized', 'Position', [0.05, 0.65, 0.3, 0.2], 'HorizontalAlignment', 'right'); + handles.ip_edit = uicontrol(p3, 'Style', 'edit', 'String', '127.0.0.1', 'Units', 'normalized', 'Position', [0.38, 0.65, 0.25, 0.25]); + uicontrol(p3, 'Style', 'text', 'String', 'Processor ID:', 'Units', 'normalized', 'Position', [0.05, 0.35, 0.3, 0.2], 'HorizontalAlignment', 'right'); + handles.proc_node_edit = uicontrol(p3, 'Style', 'edit', 'String', '100', 'Units', 'normalized', 'Position', [0.38, 0.35, 0.25, 0.25]); + uicontrol(p3, 'Style', 'text', 'String', 'Record Node ID:', 'Units', 'normalized', 'Position', [0.05, 0.05, 0.3, 0.2], 'HorizontalAlignment', 'right'); + handles.rec_node_edit = uicontrol(p3, 'Style', 'edit', 'String', '101', 'Units', 'normalized', 'Position', [0.38, 0.05, 0.25, 0.25]); + + % Main Control Button + handles.control_button = uicontrol('Style', 'pushbutton', 'String', 'Load',... + 'Units', 'normalized', 'FontSize', 14, 'FontWeight', 'bold', ... + 'Position', [0.02, 0.03, 0.6, 0.1],... + 'BackgroundColor', [0.2, 0.6, 0.8], 'Callback', {@(h,e) feval(mfilename, obj, 'main_control_callback')}); + + % Store the UI handles struct in an SPH for consistent access + SoloParamHandle(obj, 'ui_handles', 'value', handles); + + % ========================================================================= + % CASE MAIN_CONTROL_CALLBACK + % ========================================================================= + case 'main_control_callback' + switch value(currentState) + case 'Load', feval(mfilename, obj, 'load_sequence'); + case 'Run', feval(mfilename, obj, 'run_sequence'); + case 'Stop', feval(mfilename, obj, 'stop_sequence'); + end + + % ========================================================================= + % WORKFLOW ACTIONS + % ========================================================================= + case 'load_sequence' + % handles = feval(mfilename, obj, 'get_ui_handles'); + handles = value(ui_handles); + log_message(handles, '--- LOAD sequence initiated ---'); + set(handles.control_button, 'Enable', 'off', 'String', 'Loading...'); + + params = get_all_params(handles); + if ~validate_inputs(handles, params) + feval(mfilename, obj, 'reset_to_load_state'); + return; + end + + current_params.value = params; + + try + log_message(handles, 'Connecting to Open Ephys GUI...'); + oe_controller.value = OpenEphysHTTPServer(params.gui_ip, 37497); + log_message(handles, 'Connection successful.'); + catch ME + log_message(handles, ['ERROR: Failed to connect to OE. Is it running? Details: ' ME.message]); + getReport(ME, 'extended', 'hyperlinks', 'on'); + errordlg('Failed to connect to Open Ephys GUI.', 'Connection Error'); + feval(mfilename, obj, 'reset_to_load_state'); + return; + end + + [session_path, oe_save_path] = construct_paths(handles, params); + if isempty(session_path) || ~create_directories(handles, params, session_path) + feval(mfilename, obj, 'reset_to_load_state'); + return; + end + session_base_path.value = session_path; + + if get(handles.cb_ephys, 'Value') + try + log_message(handles, ['Setting OE record path to: ' oe_save_path]); + value(oe_controller).setRecordPath(params.rec_node_id, oe_save_path); + log_message(handles, 'Successfully set OE record path.'); + catch ME + log_message(handles, ['ERROR setting OE path: ' ME.message]); + getReport(ME, 'extended', 'hyperlinks', 'on'); + errordlg('Could not set Open Ephys record path.', 'API Error'); + feval(mfilename, obj, 'reset_to_load_state'); + return; + end + end + + feval(mfilename, obj, 'initialize_behavior_system'); + + case 'initialize_behavior_system' + params = value(current_params); + try + log_message(feval(mfilename, obj, 'get_ui_handles'), 'Initializing behavior control system...'); + behav_obj.value = dispatcher('init'); + h=get_sphandle('owner','dispatcher','name','myfig'); + set(value(h{1}), 'Visible','Off'); + + if params.do_manual_test + feval(mfilename, obj, 'behav_control', 'manual_test'); + else + feval(mfilename, obj, 'continue_load_after_manual_test'); + end + catch ME + log_message(feval(mfilename, obj, 'get_ui_handles'), ['FATAL ERROR initializing behavior system: ' ME.message]); + getReport(ME, 'extended', 'hyperlinks', 'on'); + errordlg(['Failed to initialize behavior system. Check path and logs. Error: ' ME.message], 'Behavior System Error'); + feval(mfilename, obj, 'reset_to_load_state'); + end + + case 'manual_test_stopping' + + handles = value(ui_handles); + log_message(handles, 'Manual rig test complete. Cleaning up...'); + dispatcher(value(behav_obj), 'Stop'); + + %Let's pause until we know dispatcher is done running + set(value(stopping_complete_timer), 'TimerFcn', {@(h,e) feval(mfilename, obj, 'manual_test_stopped')}); + start(value(stopping_complete_timer)); + + case 'manual_test_stopped' + + if value(stopping_process_completed) %This is provided by RunningSection + stop(value(stopping_complete_timer)); %Stop looping. + dispatcher('set_protocol', ''); + is_running.value = 0; + feval(mfilename, obj, 'continue_load_after_manual_test'); + end + + case 'continue_load_after_manual_test' + + params = value(current_params); + % handles = feval(mfilename, obj, 'get_ui_handles'); + handles = value(ui_handles); + + video_save_dir = fullfile(params.local_path, value(session_base_path), 'behav'); + try + log_message(handles, 'Loading main behavioral protocol...'); + feval(mfilename, obj, 'behav_control', 'load_main_protocol', params.experimenter, params.rat_name, params.protocol_name, video_save_dir, params.behav_path); + log_message(handles, 'Behavior system loaded and ready.'); + log_message(handles, '--- LOAD sequence complete. Ready to run. ---'); + currentState.value = 'Run'; + set(handles.control_button, 'Enable', 'on', 'String', 'Run', 'BackgroundColor', [0.4, 0.8, 0.4]); + catch ME + log_message(handles, ['FATAL ERROR loading main protocol: ' ME.message]); + getReport(ME, 'extended', 'hyperlinks', 'on'); + errordlg(['Failed to load main protocol. Error: ' ME.message], 'Behavior System Error'); + feval(mfilename, obj, 'reset_to_load_state'); + end + + case 'run_sequence' + params = value(current_params); + % handles = feval(mfilename, obj, 'get_ui_handles'); + handles = value(ui_handles); + log_message(handles, '--- RUN sequence initiated ---'); + + if get(handles.cb_ephys, 'Value') + try + log_message(handles, 'Starting OE acquisition and recording...'); + value(oe_controller).acquire(); + pause(1); + value(oe_controller).record(); + log_message(handles, 'Open Ephys recording is LIVE.'); + catch ME + log_message(handles, ['ERROR starting OE recording: ' ME.message]); + getReport(ME, 'extended', 'hyperlinks', 'on'); + errordlg('Failed to start Open Ephys recording.', 'API Error'); + return; + end + else + log_message(handles, 'Ephys checkbox not selected. No OE recording started.'); + end + + currentState.value = 'Stop'; + set(handles.control_button, 'String', 'Stop'); + feval(mfilename, obj, 'start_blinking'); + feval(mfilename, obj, 'start_monitoring'); + + try + log_message(handles, 'Starting behavioral protocol...'); + feval(mfilename, obj, 'behav_control', 'run', params.protocol_name); + log_message(handles, 'Behavioral protocol is LIVE.'); + catch ME + log_message(handles, ['FATAL ERROR starting behavior protocol: ' ME.message]); + getReport(ME, 'extended', 'hyperlinks', 'on'); + errordlg(['Failed to start behavior protocol. Check logs. Error: ' ME.message], 'Behavior System Error'); + end + + case 'stop_sequence' + params = value(current_params); + % handles = feval(mfilename, obj, 'get_ui_handles'); + handles = value(ui_handles); + log_message(handles, '--- STOP sequence initiated ---'); + feval(mfilename, obj, 'stop_blinking'); + + try + log_message(handles, 'Ending behavioral session (saving data)...'); + feval(mfilename, obj, 'behav_control', 'end', params.protocol_name, params.behav_path); + log_message(handles, 'Behavioral data saved successfully.'); + catch ME + log_message(handles, ['FATAL ERROR ending behavioral session: ' ME.message]); + getReport(ME, 'extended', 'hyperlinks', 'on'); + errordlg(['Failed to save behavioral data. Check logs. Error: ' ME.message], 'Behavior System Error'); + end + + if get(handles.cb_ephys, 'Value') && ~isempty(value(oe_controller)) + try + log_message(handles, 'Stopping OE recording and acquisition...'); + value(oe_controller).idle(); + log_message(handles, 'Open Ephys recording stopped.'); + catch ME + log_message(handles, ['ERROR stopping OE recording: ' ME.message]); + getReport(ME, 'extended', 'hyperlinks', 'on'); + errordlg('Failed to stop Open Ephys recording.', 'API Error'); + end + end + + log_message(handles, '--- Experiment finished. Ready for new session. ---'); + feval(mfilename, obj, 'reset_to_load_state'); + + % ========================================================================= + % BEHAVIOR CONTROL ACTIONS + % ========================================================================= + case 'behav_control' + sub_action = varargin{1}; + args = varargin(2:end); + % handles = feval(mfilename, obj, 'get_ui_handles'); + handles = value(ui_handles); + + switch sub_action + case 'load_main_protocol' + experimenter = args{1}; ratname = args{2}; protocol_name = args{3}; + video_save_dir = args{4}; behav_path = args{5}; + log_message(handles, ['Loading protocol: ' protocol_name]); + dispatcher('set_protocol', protocol_name); + rath = get_sphandle('name', 'ratname', 'owner', protocol_name); + exph = get_sphandle('name', 'experimenter', 'owner', protocol_name); + rath{1}.value = ratname; exph{1}.value = experimenter; + protobj = eval(protocol_name); + log_message(handles, ['Loading settings for ' ratname]); + [~, sfile] = load_solouiparamvalues(ratname, 'experimenter', experimenter, 'owner', class(protobj), 'interactive', 0); + if ~dispatcher('is_running'), pop_history(class(protobj), 'include_non_gui', 1); feval(protocol_name, protobj, 'prepare_next_trial'); end + feval(protocol_name, protobj, 'set_setting_params', ratname, experimenter, sfile, char(datetime('now')), video_save_dir); + + case 'load_protocol_after_crash' + experimenter = args{1}; ratname = args{2}; protocol_name = args{3}; + video_save_dir = args{4}; behav_path = args{5}; + log_message(handles, ['Loading protocol: ' protocol_name]); + dispatcher('set_protocol', protocol_name); + rath = get_sphandle('name', 'ratname', 'owner', protocol_name); + exph = get_sphandle('name', 'experimenter', 'owner', protocol_name); + rath{1}.value = ratname; exph{1}.value = experimenter; + protobj = eval(protocol_name); + % Loading the temporary saved Data file + today_date = char(datetime('now','format','yyMMdd')); + temp_data_dir = fullfile(behav_path,'SoloData','Data',experimenter,ratname); + temp_data_file = sprintf('data_@%s_%s_%s_%s_ASV.mat',protocol_name,experimenter,ratname,today_date); + if isfile(fullfile(temp_data_dir,temp_data_file)) + dispatcher('runstart_disable'); + load_soloparamvalues(ratname, 'experimenter', experimenter,... + 'owner', protobj, 'interactive', 0,'data_file',fullfile(temp_data_dir,temp_data_file)); + dispatcher('runstart_enable'); + end + % [sfile, ~] = SavingSection(protobj, 'get_set_filename'); + if ~dispatcher('is_running'), pop_history(class(protobj), 'include_non_gui', 1); feval(protocol_name, protobj, 'prepare_next_trial'); end + % feval(protocol_name, protobj, 'set_setting_params', ratname, experimenter, sfile, char(datetime('now')), video_save_dir); + + case 'crashed' + log_message(handles, '--- BEHAVIOR CRASH RECOVERY INITIATED ---'); + params = value(current_params); + feval(mfilename, obj, 'behav_control', 'load_protocol_after_crash', params.experimenter, params.rat_name, params.protocol_name, fullfile(params.local_path, value(session_base_path), 'behav'), params.behav_path); + feval(mfilename, obj, 'behav_control', 'run', params.protocol_name); + log_message(handles, '--- RECOVERY COMPLETE: Behavior protocol restarted ---'); + + case 'run' + protocol_name = args{1}; protobj = eval(protocol_name); + log_message(handles, 'Starting video recording via protocol...'); + feval(protocol_name, protobj, 'start_recording'); + log_message(handles, 'Starting dispatcher to run trials...'); + is_running.value = 1; + dispatcher(value(behav_obj), 'Run'); + + + case 'end' + protocol_name = args{1}; root_dir = args{2}; + log_message(handles, 'Stopping dispatcher...'); + dispatcher(value(behav_obj), 'Stop'); + + %Let's pause until we know dispatcher is done running + set(value(stopping_complete_timer), 'Period', 2,'TimerFcn', {@(h,e) feval(mfilename, obj, 'behav_control','end_continued',protocol_name, root_dir)}); + %set(value(stopping_complete_timer),'TimerFcn',[mfilename,obj,'behav_control','(''end_continued'');']); + start(value(stopping_complete_timer)); + + case 'end_continued' + %% end_continued + if value(stopping_process_completed) %This is provided by RunningSection + protocol_name = args{1}; root_dir = args{2}; + stop(value(stopping_complete_timer)); %Stop looping. + is_running.value = 0; + feval(mfilename, obj, 'behav_control', 'send_empty_state_machine'); + protobj = eval(protocol_name); + log_message(handles, 'Ending session via protocol...'); + feval(protocol_name, protobj, 'end_session'); + log_message(handles, 'Saving data and settings...'); + data_file = SavingSection(protobj, 'savedata', 'interactive', 0); + try, feval(protocol_name, protobj, 'pre_saving_settings'); catch, log_message(handles, 'Protocol does not have a pre_saving_settings section.'); end + [settings_file, ~] = SavingSection(protobj, 'get_set_filename'); + SavingSection(protobj, 'savesets', 'interactive', 0); + log_message(handles, 'Committing data and settings to SVN...'); + commit_to_svn(handles, data_file, root_dir); + commit_to_svn(handles, settings_file, root_dir); + dispatcher('set_protocol', ''); + end + + case 'create_svn_data_dir' + experimenter = args{1}; ratname = args{2}; behav_dir = args{3}; dir_name = args{4}; + dirCurrent = cd; + settings_path = fullfile(behav_dir, 'SoloData', dir_name); + exp_path = fullfile(settings_path, experimenter); + rat_path = fullfile(exp_path, ratname); + if ~isfolder(settings_path), mkdir(settings_path); system(['svn add ' dir_name]); end + if ~isfolder(exp_path), cd(settings_path); mkdir(experimenter); system(['svn add ' experimenter]); end + if ~isfolder(rat_path), cd(exp_path); mkdir(ratname); system(['svn add ' ratname]); end + cd(dirCurrent); + log_message(handles, ['Created SVN directory structure for ' ratname]); + + case 'send_empty_state_machine' + state_machine_server = bSettings('get', 'RIGS', 'state_machine_server'); + server_slot = bSettings('get', 'RIGS', 'server_slot'); if isnan(server_slot), server_slot = 0; end + card_slot = bSettings('get', 'RIGS', 'card_slot'); if isnan(card_slot), card_slot = 0; end + sm = BpodSM(state_machine_server, 3333, server_slot); sm = Initialize(sm); + [inL, outL] = MachinesSection(dispatcher, 'determine_io_maps'); + sma = StateMachineAssembler('full_trial_structure'); + sma = add_state(sma, 'name', 'vapid_state_in_vapid_matrix'); + send(sma, sm, 'run_trial_asap', 0, 'input_lines', inL, 'dout_lines', outL, 'sound_card_slot', int2str(card_slot)); + + case 'manual_test' + log_message(handles, 'Loading manual rig test protocol...'); + dispatcher('set_protocol', 'Rigtest_singletrial'); + %Hide protocol window. + h=get_sphandle('owner','Rigtest_singletrial','name', 'myfig'); + for i=1:numel(h); set(value(h{i}),'Visible','Off'); end + + is_running.value = 1; + log_message(handles, 'Starting manual rig test. Please complete the one-trial test.'); + dispatcher(value(behav_obj), 'Run'); + end + + case 'browse_path' + type = varargin{1}; + % handles = feval(mfilename, obj, 'get_ui_handles'); + handles = value(ui_handles); + log_message(handles, ['Opening browse dialog for ' type ' path...']); + folder_path = uigetdir; + if folder_path ~= 0 + if strcmp(type, 'local'), set(handles.local_edit, 'String', folder_path); + elseif strcmp(type, 'central'), set(handles.central_edit, 'String', folder_path); + elseif strcmp(type, 'behav'), set(handles.behav_edit, 'String', folder_path); + end + log_message(handles, [type ' path set.']); + else, log_message(handles, 'Path selection cancelled.'); end + + case 'is_running' + + obj = logical(value(is_running)); + + % ========================================================================= + % OTHER ACTIONS (CLOSE, RESET, etc.) + % ========================================================================= + case 'reset_to_load_state' + % handles = feval(mfilename, obj, 'get_ui_handles'); + handles = value(ui_handles); + currentState.value = 'Load'; + set(handles.control_button, 'Enable', 'on', 'String', 'Load', 'BackgroundColor', [0.2, 0.6, 0.8]); + oe_controller.value = []; + behav_obj.value = []; + current_params.value = []; + + case 'close' + feval(mfilename, obj, 'stop_blinking'); + if ishandle(value(myfig)), delete(value(myfig)); end + delete_sphandle('owner', ['^@' mfilename '$']); + + case 'get_ui_handles' + varargout{1} = value(ui_handles); + + case 'start_blinking' + % handles = feval(mfilename, obj, 'get_ui_handles'); + handles = value(ui_handles); + blinking_timer.value = timer('ExecutionMode', 'fixedRate', 'Period', 0.5, 'TimerFcn', {@toggle_button_color, handles.control_button}); + start(value(blinking_timer)); + + case 'stop_blinking' + % handles = feval(mfilename, obj, 'get_ui_handles'); + handles = value(ui_handles); + if ~isempty(value(blinking_timer)) && isvalid(value(blinking_timer)) + stop(value(blinking_timer)); + delete(value(blinking_timer)); + blinking_timer.value = []; + end + set(handles.control_button, 'BackgroundColor', [1, 0.4, 0.4]); + + + case 'crash_detected' + % handles = feval(mfilename, obj, 'get_ui_handles'); + handles = value(ui_handles); + if ~strcmp(value(currentState), 'Stop') || isempty(value(behav_obj)), return; end + + log_message(handles, '!!! CRASH DETECTED: Behavior system is not running. Attempting recovery...'); + try + feval(mfilename, obj, 'behav_control', 'crashed'); + + catch ME + log_message(handles, sprintf('FATAL: Recovery attempt failed: %s', ME.message)); + getReport(ME, 'extended', 'hyperlinks', 'on'); + errordlg('Automatic recovery failed. Please stop the experiment manually.', 'Recovery Failed'); + end + +end + +return; + +% ========================================================================= +% SUB-FUNCTIONS +% ========================================================================= + +function commit_to_svn(handles, file_path_data,file_path_settings, root_dir) + +if isempty(file_path_data), return; end + if isempty(file_path_settings), return; end + [pname_data, fname_data, ext_data] = fileparts(file_path_data); + [pname_settings, fname_settings, ext_settings] = fileparts(file_path_settings); + + configFilePath = fullfile(root_dir,'PASSWORD_CONFIG-DO_NOT_VERSIONCONTROL.mat'); + if ~exist(configFilePath, 'file') + log_message(handles, ['SVN commit failed: Password config file not found at ' configFilePath]); + return; + end + load(configFilePath, 'svn_user', 'svn_password'); + logmsg = char(strcat('automated commit from GUI for data and settings for ', {' '} ,fname_data,{'@'})); + % current_dir = cd; + cd(pname_data); + add_cmd_data = char(strcat('svn add', {' '}, fname_data, '.mat',{'@'})); + system(add_cmd_data); + + cd(pname_settings); + add_cmd_settings = char(strcat('svn add', {' '}, fname_settings, '.mat',{'@'})); + system(add_cmd_settings); + + commit_cmd = sprintf('svn ci --username="%s" --password="%s" -m "%s"', svn_user, svn_password, logmsg); + [status, ~] = system(commit_cmd); + + if status == 0 + log_message(handles, ['SVN commit successful for ' fname_data]); + else + log_message(handles, ['SVN commit FAILED for ' fname_data '.']); + end + + cd(fullfile(root_dir,'ExperPort')); + + +function params = get_all_params(handles) + params.protocol_name = get(handles.protocol_edit, 'String'); + params.do_manual_test = get(handles.manual_test, 'Value'); + params.experimenter = get(handles.exp_edit, 'String'); + params.rat_name = get(handles.rat_name_edit, 'String'); + params.behav_path = get(handles.behav_edit, 'String'); + params.project_name = get(handles.proj_edit, 'String'); + params.subject_id = get(handles.sub_edit, 'String'); + params.local_path = get(handles.local_edit, 'String'); + params.central_path = get(handles.central_edit, 'String'); + params.gui_ip = get(handles.ip_edit, 'String'); + params.proc_node_id = get(handles.proc_node_edit, 'String'); + params.rec_node_id = get(handles.rec_node_edit, 'String'); + + +function log_message(handles, logStr) + if ~isfield(handles, 'log_box') || ~isvalid(handles.log_box), return; end + current_text = get(handles.log_box, 'String'); + timestamp = datestr(now, '[HH:MM:SS] '); + new_line = [timestamp, logStr]; + new_text = [current_text; {new_line}]; + set(handles.log_box, 'String', new_text, 'Value', numel(new_text)); + drawnow; + + +function is_valid = validate_inputs(handles, params) + is_valid = false; fields = fieldnames(params); + for i = 1:length(fields) + if strcmp(fields{i}, 'central_path') && isempty(params.(fields{i})), continue; end + if isempty(params.(fields{i})) + msg = ['Field "' strrep(fields{i}, '_', ' ') '" cannot be empty.']; + log_message(handles, ['ERROR: ' msg]); errordlg(msg, 'Input Error'); + return; + end + end + if ~get(handles.cb_ephys, 'Value') && ~get(handles.cb_behav, 'Value') && ~get(handles.cb_anat, 'Value') && ~get(handles.cb_funcimg, 'Value') + msg = 'At least one subfolder must be selected.'; + log_message(handles, ['ERROR: ' msg]); errordlg(msg, 'Input Error'); + return; + end + is_valid = true; + + +function [session_base, oe_path] = construct_paths(handles, params) + subject_name = sprintf('sub-%s_id-%s_expmtr-%s', params.subject_id, params.rat_name, params.experimenter); + subject_base_path = fullfile(params.project_name, 'rawdata', subject_name); + local_subject_dir = fullfile(params.local_path, subject_base_path); + central_subject_dir = fullfile(params.central_path, subject_base_path); + new_ses_num = max(find_max_session_number(local_subject_dir), find_max_session_number(central_subject_dir)) + 1; + log_message(handles, sprintf('Last session found: %d. Creating new session: %d.', new_ses_num - 1, new_ses_num)); + session_datetime_str = char(datetime('now', 'Format', 'yyyyMMdd''T''HHmmss')); + session_folder_name = sprintf('ses-%02d_date-%s_dtype-ephys', new_ses_num, session_datetime_str); + session_base = fullfile(subject_base_path, session_folder_name); + oe_path = fullfile(params.local_path, session_base, 'ephys'); + log_message(handles, ['New session path determined: ' session_base]); + + +function max_ses = find_max_session_number(base_path) + max_ses = 0; if ~exist(base_path, 'dir'), return; end + dir_contents = dir(fullfile(base_path, 'ses-*')); + if isempty(dir_contents), return; end + session_numbers = []; + for i = 1:length(dir_contents) + if dir_contents(i).isdir + token = regexp(dir_contents(i).name, '^ses-(\d+)', 'tokens'); + if ~isempty(token), session_numbers(end+1) = str2double(token{1}{1}); end + end + end + if ~isempty(session_numbers), max_ses = max(session_numbers); end + +function success = create_directories(handles, params, session_base_path) + success = false; + subfolders.ephys = get(handles.cb_ephys, 'Value'); + subfolders.behav = get(handles.cb_behav, 'Value'); + subfolders.anat = get(handles.cb_anat, 'Value'); + subfolders.funcimg = get(handles.cb_funcimg, 'Value'); + selected_folders = fieldnames(subfolders)'; + selected_folders = selected_folders([subfolders.ephys, subfolders.behav, subfolders.anat, subfolders.funcimg] == 1); + try + for i = 1:length(selected_folders) + local_target_path = fullfile(params.local_path, session_base_path, selected_folders{i}); + log_message(handles, ['Creating local directory: ' local_target_path]); + if ~exist(local_target_path, 'dir'), mkdir(local_target_path); end + end + log_message(handles, 'All selected local directories created successfully.'); + success = true; + catch ME + msg = sprintf('Failed to create directories: %s', ME.message); + getReport(ME, 'extended', 'hyperlinks', 'on'); + log_message(handles, ['ERROR: ' msg]); errordlg(msg, 'Directory Error'); + end + + +function toggle_button_color(~, ~, button_handle) + if ~isvalid(button_handle), return; end + currentColor = get(button_handle, 'BackgroundColor'); + if isequal(currentColor, [1, 0.4, 0.4]), set(button_handle, 'BackgroundColor', [1, 0.7, 0.4]); + else, set(button_handle, 'BackgroundColor', [1, 0.4, 0.4]); end diff --git a/ExperPort/Modules/@dispatcher/RunningSection.m b/ExperPort/Modules/@dispatcher/RunningSection.m index 3d82ca5a..00912634 100644 --- a/ExperPort/Modules/@dispatcher/RunningSection.m +++ b/ExperPort/Modules/@dispatcher/RunningSection.m @@ -31,6 +31,7 @@ SoloParamHandle(obj, 'stop_after_next_update', 'value', 0); SoloParamHandle(obj, 'stopping_process_completed', 'value', 1); SoloFunctionAddVars('runrats','func_owner','@runrats','ro_args','stopping_process_completed'); % This is a bit unpleasant. We give ro access to the flag denoting the stop process as complete to runrats so that runrats can wait on it using a timer. :P + SoloFunctionAddVars('OpenEphys_Neuroblueprint','func_owner','@OpenEphys_Neuroblueprint','ro_args','stopping_process_completed'); % This is a bit unpleasant. We give ro access to the flag denoting the stop process as complete to runrats so that OpenEphys_Neuropblueprint can wait on it using a timer. :P SoloFunctionAddVars('TowerWaterDelivery','func_owner','@TowerWaterDelivery','ro_args','stopping_process_completed'); % This is a bit unpleasant. We give ro access to the flag denoting the stop process as complete to runrats so that runrats can wait on it using a timer. :P set(get_ghandle(RunButton), 'FontSize', 20); % (defined by GetSoloFunctionArgs) @@ -283,8 +284,10 @@ if runrats('is_running'); runrats('crashed',me); Running.value = 0; - else + elseif OpenEphys_Neuroblueprint('is_running') + OpenEphys_Neuroblueprint('crash_detected'); Running.value = 0; + else rethrow(me); end diff --git a/ExperPort/Plugins/@saveload/SavingSection.m b/ExperPort/Plugins/@saveload/SavingSection.m index 23722f12..2f07173b 100644 --- a/ExperPort/Plugins/@saveload/SavingSection.m +++ b/ExperPort/Plugins/@saveload/SavingSection.m @@ -176,7 +176,11 @@ warning('SAVELOAD:InvalidParam', 'Don''t know how to set "%s", not doing anything', parname); end; - + case 'get_set_filename' + x = value(settings_file); + y = []; + return; + % ------------ CASE GET_ALL_INFO -------------------- %Sundeep Tuteja, 22nd December, 2009: Adding a case to get %experimenter name, rat name, settings file loaded, if any, and data file. Case @@ -208,6 +212,7 @@ case 'set_info', % ------------ CASE SET_INFO -------------------- ratname.value=y; %#ok experimenter.value=x; %#ok + y =[]; return; case 'set_setting_info' diff --git a/Protocols/@Rigtest_singletrial/Rigtest_singletrial.m b/Protocols/@Rigtest_singletrial/Rigtest_singletrial.m index eb93bde0..8959fcb4 100644 --- a/Protocols/@Rigtest_singletrial/Rigtest_singletrial.m +++ b/Protocols/@Rigtest_singletrial/Rigtest_singletrial.m @@ -132,9 +132,7 @@ set(value(myfig), 'Name', name, 'Tag', name, ... 'closerequestfcn', 'dispatcher(''close_protocol'')', 'MenuBar', 'none'); - % This flag will be set to 1 when the single trial is complete. - SoloParamHandle(obj, 'ManualTestComplete', 'value', 0, 'saveable', 0); - % At this point we have one SoloParamHandle, myfig + % At this point we have one SoloParamHandle, myfig % Let's put the figure where we want it and give it a reasonable size: set(value(myfig), 'Position', [485 144 300 400]); @@ -216,6 +214,16 @@ Rigtest_singletrial(obj, 'prepare_next_trial'); + case 'set_callback' + + if not(isempty(varargin)) + callback_info = varargin{1}; + else + callback_info = []; + end + SoloParamHandle(obj, 'ManualTestCompletionCallback', 'value', callback_info); + + %--------------------------------------------------------------- % CASE PREPARE_NEXT_TRIAL @@ -231,21 +239,32 @@ % <~> If we've completed our trial, tell RunRats that we're done. if n_done_trials > 0 - % Set our completion flag to 1. The main GUI will be polling for this. - ManualTestComplete.value = 1; - if nTrials > 0 && runrats('is_running') runrats('rigtest_singletrial_is_complete'); return; + + elseif nTrials > 0 && OpenEphys_Neuroblueprint('is_running') + OpenEphys_Neuroblueprint('manual_test_stopping'); + return; + + else % probably called by another function so need to stop the dispatcher from here itself % Send a dummy state machine that does nothing and ends quickly. % This prevents the protocol from running a second trial. - sma = StateMachineAssembler('full_trial_structure'); - sma = add_state(sma, 'name', 'do_nothing', 'self_timer', 0.01, ... - 'input_to_statechange', {'Tup', 'check_next_trial_ready'}); - dispatcher('send_assembler', sma, 'do_nothing'); - return; % IMPORTANT: Exit here to prevent building the real state machine again. + + % Find the callback handle created by the main GUI calling it. + h_callback = get_sphandle('owner', ['@' class(obj) '$'], 'name', 'ManualTestCompletionCallback'); + + if ~isempty(h_callback) + callback_info = value(h_callback{1}); + if ~isempty(callback_info) && isa(callback_info, 'cell') && numel(callback_info) == 2 + % Execute the callback: feval(function_handle, arguments...) + % This directly calls 'continue_load_after_manual_test' in the main GUI. + feval(callback_info{1}, callback_info{2}); + end + end + return; % IMPORTANT: Exit here to prevent building the real state machine again. end end diff --git a/Protocols/sendsummary_error_log.txt b/Protocols/sendsummary_error_log.txt index 49bba33d..ed2506da 100644 --- a/Protocols/sendsummary_error_log.txt +++ b/Protocols/sendsummary_error_log.txt @@ -653,3 +653,15 @@ In parse_and_call_sph_callback (line 40) In SoloParamHandle/callback (line 107) In generic_callback (line 5)] +ERROR in bdata: mym(CON_ID,'insert into parsed_events values ("{S}", "{M}")',vs{1}, vs{2} ); + + +Error using mym +Data too long for column 'events' at row 1 +On line 158 of C:\ratter\ExperPort\MySQLUtility\bdata.m +On line 177 of C:\ratter\ExperPort\Plugins\@sqlsummary\sendsummary.m +On line 228 of C:\ratter\Protocols\@ArpitSoundCatContinuous\ArpitSoundCatContinuous.m +On line 423 of C:\MATLABscripts_EphysExperiment\OpenEphys_Bcontrol_Neuroblueprint_GUI.m +On line 323 of C:\MATLABscripts_EphysExperiment\OpenEphys_Bcontrol_Neuroblueprint_GUI.m +On line 157 of C:\MATLABscripts_EphysExperiment\OpenEphys_Bcontrol_Neuroblueprint_GUI.m +No errors encountered during sendsummary execution. diff --git a/Protocols/settings_@ArpitSoundCatContinuous_lida_LP12_250708c.mat b/Protocols/settings_@ArpitSoundCatContinuous_lida_LP12_250708c.mat new file mode 100644 index 0000000000000000000000000000000000000000..756f390cf273bedfc559daab05d38d2ee190ee14 GIT binary patch literal 14120 zcmb8#byQo;-ym?LSZQ&JyOrYZ(&Fw=yf`iH?pEC0iWG_$hvM#TgHm|9&* zPeGNRmz7$|)ymA>%97g2QJ7lW!-`tb!=9R2keW+Sn43qKlZTp{lbetF|M5WhuLDs* z1^YSv%z}X6otKxr;d;!E6C5C1o^Nuauj;$nF-Y@TOP*Lc08YC_j2rSql|=+?|DQ z@lMq03@FnWYV0rWX%aIUtbU>8wVf)RwM0(Kol5xHViHR0Dnokza!rX}lgt%UG}A+h zlp&s#VMeu(UNfu@*38H<{C<{{RezT4mQbBkJHFQsIcACa)eqLoywsSSM4FNrq0d>s z&+B?A`h3hSt&$lcntd@X>um~Kua>U!kBv9(tRCd2kkykc%?ZuA*0{OU4J(t$=of^I zypv7d)(-IO8_rTGEqD-;-M=ZIK@$o|r!gD@{NxcD?Q{dYJ)u6d?^3`yrpC`-dH+Th zF@Qm-@^Q_Fdd@A3+lIevvW=H&y`HXtov1X#%MH6J1{Hl&Ll(s{kf{%<~85<_E19B@Nj>t^A;#*){j)A-PEw3KW#IJXDm(kkMxv+V>_^m z&#p=ZF}P!eF+;m)lLsXobN<&!i7{FY6XmDX+eauYl5!k~YTWBoo-30H0iF|<(WM)F zGHl;h1$RGvv}PA7j~7{%vs7iqkI=6Dinq{W?AuG+{!>XCVI(^9U3OD_^;-e;X7Wm+ z$wUvT21CcpT9<58H3b1u8su`)gGqi|@iHPR@Lcr)i$3M%Kw4~?p|Z1(8Zi(R6LZ{E zxsQ+nAZjb4ME&TMJIQ2XuwKGk8*YLsZhjI$bW8qZi*!J}P!)wV zGd5#!%K0*TlB>)3mfFErIF=5}RdFkJ@#Rt8I-0UOl4!k>RP|1D=`o(6qt(qAv9n#Q!{h)LF$4K(Er}x6C8jF{9@ikV6k)mYy$l6a zh?g^l>&&jh>ju8)hk`;DJ3n;xvwb-USMFcUZ_I(&+@smpAzb9`_44|piOK=Q^wdvj zgU5r8E2`Al4U~a?inqBmGBOM&i-=EavbCktqbF6;_yLwk`}I672<0tY_#C5HhM_b~ zHMOG#84jZd=&LiTtUk-!j$gD5>!yh4V=(QGXg$1yxl&x~!b`h~Ve8*+8H(sdQbu$ghQ05=$Rst=gH^lG4LVL!1!aH}pCt?Ts@F$ajlQqK;1hDq0;3p%!1c z)NO%a>d)X)O+Bl6HGy4R@d+3j0sJRofL^7D735&D`M5+jGCZcp=zS7{ygOVolVN>qxcZQaz)l$NW^eHQHm4xx`sE z@KJM%}~&gM_;eoNpYg%T+p$q8vykV@c(F1 z+fi=acxxeDIOZ4pJ(a!K2{=M<6WNRQD&L9rYMsHyhVbvQdFvou za9R%ofQ23gJ5c)#OBIa44!@IV0R|i}D2Zt^Ktk*Sq%C@}Or!MN-kr}q*Y~V9xOGaj zCYjQm?9txFco)Cuccz3x{*4?yBl3v67|hi|+?hH<-hp;a`C!7>qP-=orvX6U=dOqt zis(OAI@m<|ry>Q?&J+ayt3Wx@ph%SH-=xER2P4!@R$Om=+VckKZL8?s3A~V%6Z(H6 zukVLS8R-A!|N18tz{RX{@?v|#{&Pd|e^tn2K;lq(Ve(wEfDQ3Eg25YL!r>9)RVnFu z3@mkux+dw>o$vI0+EumxjkJ)YU8B@}eBS-&>?(A&+@wcMSbJ=^S^!|Rux63se&F4m*I@dc{p=u`S_rq#d-4|NfPEXngWD?JB zO_#GB{%4$?9ct|P7H~IFVMz-AXNaz6O*sMh33}6cF>;Uqzs|S`r1}$OQ)tueYO zQs3M&`*-)`*(oaP=|5$o{jdOo1A#6LGQLoI-gx(qBNF|rZ@~`nT8jSt`k{rZdz3`% zwH2lftVg4`L$dW^y+2Yh3=-%~B$2oJYPG9E^;`>0)94J}C!|)CvfYtXGV54@?t!7>hq-T?M6@F`|GGitdrDKTmbB(r zH`fvFf#u4oHlA@#O86NGfR{ch50#P~X=e2m^*;N2k5Y=S7SiN8uKS3Oe4w(%ZW303 zCXu{%AHsn_E)#|uimZ4fADLnZI$gD1oIBcVraZwE;BBW~s+2a?PNyM-2_BNb7D zT@~I`L{ygFcS$_`Qr9)u=$RX~())E5Z5(M}@a5!3hU8?wie~XSYZ@V*QG-8z|i^$@?Rh%J3;QLav9ZrG`x)hMzu+Q+=&Y6k^I zGNv0wFmCIU@CZKy>CKyH%Ym^~qSXwsl#er;dMlB?bQe9mF6$^K@O{h<<}F+B z+K#eZAzG&!1g)IyUQ=xPY3@lFD@_6{Ie;nPX6${1720c$!4X2SJ}~WtG3TB*&mM@_ zSt-W9UA>^Aq;17%cwHC6(ZjWeETPAf7ap75CBaei2WH#;4z&^?9NZ?Kj>Cw7>Aj6S z9ZW42JgsvxeAChS9vT}*(L8=##id7#3btZl=d+@KOcdm+kzTvBh*lZLIFV|S*7Fw86oibJh zy63AUmiv0SU2n!7EnRwjQxK(vIwX;g(2K?5ip6H^IgMoR^+8Ugq>)dN$3n#GrXQS& zIiA?d-qi*M-;niJi>J2fG9Pg{_s{S6l0ms9OtcB|>lUBDs#BRCDv+;C;H6a(tX7;qwGJugrJ5seM(XBF(Ls|6Pl zUei@L3O=N&N-mKcplEij{!aWkLufRj7`M_8xU~2(=6V&43l_q+U=zaTOoTaUv}9>N zc<$jJ5AmYepn^gYEnw?f{fFQF#3jU!RtGa}GvB?mk8l&??n#)W=KgFc+pcIid^R=B zLB}H;Q^z3$gF}RUQg7&v+G{EWGI>I4u@;;xwn{e>MhZooM&Qr|hezl!aHg)u=O#*{ z@MD9c0M&#u9O>`RwYg<50wby{=M>r?mgFSrAi3luQo2pV<@-wSB3^WK$@{)+=8B=0 zeHCL2+4nx`Zm*Ii%?8Y|=u(8JA4!g?6GM~(Xf2O-%V&eIUfu1=1S9g9ND7M5NiZ!5 zQEz^4dW%cI_vhAvCkkbV{7q!(Q_3szYp$X{7UuHo-<&#ouk~@9nOF=2L+)x(NYaPY zp_DJKF^i7SF2a!?sI>Fm3WUucRaDKZIA$syp+*+=5wBmCfb4PZ@Krl(7M68CO`|>lm6t5eKR`5~6gC z^h+EPje_YRwr<|DlZY2m+-_P|N=o0pKVP5{S=X)~bn5>C7jeuBE zRpPv>LsKWyMtLfo~aSUFYpE^phs8gu<+f)@NM)(^2;;mhc7QyeALCaQIu5_Tk29EQo|TbZ^I8$c4WTpDDS@G^ z3y?&9kk-o^tV3E>3BepIA6jhZk}SVlsV>$U#5M@u zWm6>2iydklBer4`YseKV$dy{8dE8x6;@V8Ck$A~|nVAu28jmMH!b9axB~i^3LZu6N z@pF?A#|yPLx?3cl4DPlwgLy{xb#uD)#BQOk(5jm%iK7;bJzwDi@fF5w7jCZqmH57m z`MWr-p6$vWPf(9w8}Cz>hQbE*|W_o(2{3WQjg3caqtPHX!%zhbNyb^Ak-&x zOHsCX@A)1Oh~qkmi@W#u+N-LQ86~c(je0HAs@yV*5P#k*PSG3G8O-q|xy}E`49>yn z;{Oy75fqRY3cRkdoUU>15l6f?Mf|CXB#^7rz4-%b**=3~ee`%MI3qN0Ff}sz__%ji z?)RE5`HFDRt2L2J>a4}_6XVm%)i>g%BW5c_F9yf_%^nU3p|t#Y2}{LjSv=-Pj4gsi z{Mr)NP$;eSM#(I*9Cl`GniGzy#0iiW#ft@Ar}`%_V?)m~jCuqx-X)SnH)a!lUh~$U z<-I@1BVG@qXCf(`6P(abrVB6b@Aw_nnvj6EoBFb3v8(!=4RVN&Hv=rgBE8WEq(Ylzp^6Bn3y~?TduCb_T>h>+-6KT!d_$v%G|XW4+Js+c~AJ_YsmZ<}1!@bqfjb z+jS9A_7TzdY&;TF?>b#i_ec(K*+$!5`{ce9X147^u(VHB1dU;V+#(+RF})(&y@|{3 z&1=F`dLnuitVaWioeS-OW#>pQWw=j_l;hgBN~C^eZC5PP0Q#V%&JqqlyhvjF5GGx` zzVMkh!30O>`@yP`vNO1;dGnxEtPU_i2e{?;)Z5){90rFRYw8K+axRt9{8cOwDnbz| zl{3!4p+{z9{5)zTEz1_F0_NKSheoEdhRwkmc(PM1uIhO_s_LKJ!EaLfY+=)==-^;0}xZRSSCMLc^O#WAJiFNXpt3+E&EGUr%lbv z4FcH%J4M1*HmzW*STM2(xHV=_!e^xP-(2}owIK4Zi!WRZPnXDj!%O^;EM8>n&fA+< zy>1LBGc*+o@+Ox38mF=OkxC3goQR8pn-|OaBEeSE;K$ACKAb8h-<9>!AO_tOf_-uk z7s#`Je3>{NkN*Ngo->)SuXL-u${Bpz=4v0y^w1F7pv`|a!tdj;;V{qiLsXauF_~WP zV8b#1b{mE~f=01|y&*2>`S9(~-FVw??o*ez%F}C^(s|JcdRdmt0A8R@p#ZLf&w_s` z@bZQH8w za*IUY6d65kSW?;D7sqm6KqC76iVl~)AEk^i!eF)M6D}Wbhs{3R$ACN@9sNFOADX=D z%rr0o|BmfM-a-lMKDGSSt~_h8i7_3oa2D=3 z7yPzgv7;35QB<t1b{RXFbk28MNvyY2@@*WPl(U&1_|6f@U?J>WfpBb9L7ozlH` zKBXD~I_k#15P!~wJIPC_jT?CkHP3kIW3pp zm^AMdu3%aNaK+EZ$*Ij-p6%tsMkP0?>)*DS1O~^2*E-7~jejUswqgL5KuRk2e007rD+6UwI~DMzt$R zPjGO=RqtchH)dv<92k{|yL-&dRd=f8Hf8vbrw8Y|es`m;g2TCRFCG}drXFxN=mSgPp^9)>t znlq^~Iwe$&=L{ntx)UV}pF=BIz})XA=VAhv$HXfk=tXn&?0f{^O1j7|ghBp6IoHj!twl`d`fxV z&iT+EiHp}(EE%@P9sIYE>V#Hqp6m+C(e&~I>D?)v9A)el* z7TczOZ0=Yd?x(i1$+FGPthEDWzZmR_=co{vvgYijPnuS^>Hax2pmY$G7tM%xUr)d3 zt-}X$s2#{f%uyc-_+papcr|(x`a;yPp3LMBdJs7Ml;k6L)2K+bbuf41Nnu=_B*o}` zWPLI?HFEOs+of36kcxxeJj}!+(DCYkh1Yog1|WTN-#)eRW0A{qL!AxQfd>I$3G^yF zuz;mneCE*a5q2?p{Rv>sE8^>-rlF=bFcdX0xX($|AYI%AfV1H7^SaLMl3$y~(xnXb zgkd(e7d>0cC!Dgo z-V;dB?F4D@pj98ZN%Nk%Sle`GlLvu&# z{a`Z?$il6%QP+7@q?w;=&43rbSm(PN?HbbL2xxls`^aIChh^^m-I4!B6*Ak@A2O(C ziod(HJEB@znN$f{+I3Ef$(3_g_i(c&6&$TA1XX0*=aK#>Kvn~_mSJQT$ z_sE!WuiR{%I9}ZBuOf-Lr5)%m+GQLnKGsrnzqU8LRwxOwUOvKv^o16~y!?eYfK^L2 z!!yQh$0t-c*QjOR^|#u!)X^QAWC?DDd8w>iDsG^(U2Qr#Vk@gl?Vk!5H!V4}Hf%OB z+aQ?j2_>3x91NQiv?ddU97ePtY6Vqy7O>6LoiNb7cv2@`6Kw;a(}F83_==xAnAtC{PN5z&11t!;tD_VF8g_OD)Vda@Uf zTV?VPOqm~{?{@;<9>@y({th3HToDZv@aVksQ4!DVX{$KYXcr&k`Y4rObsDDP%vr#E zsNq1dV?-Q~S%psy(Sj{nXR$4SWhREqc2ap6qF zS6_RAd36qZZ~Tgc&+qiu?^D48c=5O}l|Vfd>t1gl#w-w#^H+PEYEnF<)Hm(?Ec_XDL zF~_b&EAEY9K2{f|HTzdbLzjGMsT*K6(4!-Jh4F5Gkj2GT(*;w?jut5qNMrf}YIz~i z{=6mBsR|1+pSrR<6ODl(v88M>tRzA!$!U1ygqJ>~xMj%htZxJgbUlCXZUde`@z%DP z6<0o^ZmJUvOoBluBs0`X!LMXVgP~d)O@PZ+M8N^uX;15`1W`(x881b0k5_ik7rjEV_^i%<}c@SA#kd%MYjKl z7{~gp>_n1;@E)_Bo$iP=d1KdIs+aW(Ne8;W^ikA0>a5cTj0kw+I;+^cXvFBqis*qP zs5i`1YF;LTsTn-=e_IK5d+EU@>Dinr7X>T3FCeicfm}5zdfLE zecROtlEbI%=4D)$W7mXj@_{46`r%Jq3QDwFq+@>c8N~0kNo727s=9nM-JpCbks~9J z2)~_n^;q0><(H`|Ay(x?8+bgO0P=oJu>&-OP1w3>;yo7<6ASt(@jqy*2naEPHQ&&K%;PwcCv4x+u?QVChBRTsUuqFV9mD0*fQ+A55JvV_!ZG6 zMXcB1OSLOSSZDRZ+vU8Be91ZF_J{hax(_-EmvMrSN4rOYWkl3oWdB9;13sGy0vQGJf$aS5=Q;E?nWDt5hYU~GF9HuBBXI!Pc zH23CCJ1>Oay^eTg7VNbq6qtLDTmu3}KYFOGf5P^?V45<5c4$!MmVgQXK6`Spj~BwV zLcH|I)BQZ$eK#dvN}vD-3HSdx8~7k;4gV*fJ)T-;YNOHY2FSKieBk*@jMQk_6!ui`oG( z4>_gnZ)02o`r+TcGt>|mglhl6;{s#}k)lxp3>+47*Udg1_8ylet6G-+<9-8k6K z)&;xOSu6?gW3zDU!M0284dh1#nQDVvT!)PdWVyYy!F$&N=jc#g*w)bo$Fj)1H@=a6 zN*xqEB(^%4u0c>%6sHy5;p74laFv)!<*#+3tBrDd9;kh?j;#inJQz1M~B8ioOPn-QGL+xwTAx1`~4$FUTzBI~$bNTJ4iOy?~ zF9_&U31?)5iCB#GSIM0*XChCVd1C!^XV}EZDtUee>E4ZZ2myVGA-{JhS6tJ*b|Gp$ z0)g<1&DUq9QerQ$46b{?>9BQBkvgmft#`=DJBehw~mcI_vWSow+8H!K_@hwnI| z{3J2uQVriI@ zE@OOSNd`5tc`V)Q( zid;K9;lUMS{q-tR&_r#5L=a;IU4Vp7MUlJ|cd|Sa0E`Vm`9G zLhpI%`y7CB6;40iU=>#O{A>_!eVuuBs0a#rXb<|&z|L~8190L5^ZXtbkJmtj&fetD zXLL(mVz;E#hFOQd<9$5HmR1;fCo01Gu-l}tL1Z0lja^A-K+i*6=Q6)eWFwJ z5xtOGw+LezyEHT=c|nWIt!VIYqfygs0A70dwhY)mTkhC-h&M2eDZD??a+xyO-m-Mb z{R-%F^+NPo>F+uVczq}jqTB}Qx3_Y^JN96eKuSbVay-i}F1XT*xjRtxUR|R#x{ZRC zUCPx9!E2-+3YwX__gEeATBjm&+~^iWUgyDt6sT#|k$s(`a-#lFz)Jz?N^^_xzeWx+ zYwNLVI}G-71d4w>s!mDTaZ>?HFqRc1?olFuLgLBdQz;W0${x|QyxOkUqNU)@6--JO zkAuuJN3`4WwG%>AcO=5t?CWwQNqc%Ob6a8T7g#~Ja~HoB9o`Nfy&9A8?>Ugux31`Z zoWgbvxW;kDK3fNc6~5Ge5=W6xv~YV;f5PvTr~qaXVy8j^dbe7yns?@$MO1F)xb;{l z-&4fzXe`wi`x5w!=_Gyy(Cl!BiWlo7ev+_T8t|S%**5vzu6}cUh`zHjF4wy>9yK<> zux+x|PL`hdiR#P#Kpw=RSF9HJX1ul1W&m1R9QeXx=~ylqYy?H0?$VV&s@0VUefn}k z_f@Zo>itI31K(f(0rzMPEBC?5?0uE1(t+H7X)aY{(965>=BMU+zC970ZW0j9ZqVzV z-Cnt;EyMj>Gb)_`C0TjRtrXn(y>en$@_r97lIG=V&X)IQaI^J3~JPIkFnZ%^&66Q_Jiz?aC#QBfLE?DG{yh50B&Mg>h99%<=eZ~@#r z>o@H&{y3{b+c<*4r9)(l_Pu(ff~Fjgkv-I==jc9JVxMcbg#3)*UX0^fjtAEzsP#(k}Rc){N&qe8&NC^UYpFY;;y_VMhXl+ja!SuB8`xR>W+ zYu}%?`1J19mD*~yrijOuL>4bGgSJ|rKkMTGa{_s6>UZUoM#~^5{XLt`{uO>9Ip=%N zC%@}XrSbSN%gg-k?)jCOrPh0L?Oplg&TsNAxIk&#>774|qVV^oS^)uCU2u<^&3KAU zizvwNnTZ(Q?{{j_n5^&D(Xb#Pg;h!FOj*Tj?%fb_P!|^am>LSK{D~s`aTF+iychWC zWh`HD(V?F#apVl7d!Q?bK`*pjy(jqzKXw!;3IfnRzbE#btuH%A_xx$3?*=~xio3PV zbQ-O$NAu9$=%rGD{q9;RU=AFnxH1=fJ`vTQof3Bz>B+*qMt~a^SM>S^=sV?6Sm2^4 zuWE{>MgBOoCbke5xE76VMY_^z*hWxirKVLJB&2Ws(sBjVJ?lDy`Eq&%bm!zVCAbU! z(7xgEZNE4^8cw)h`DFZ~E$1qfE$Me;gG~;>%Stb={52qMrJ+)FOI3^N%~C2679=9E zDz&kfb#+)F8)Hwxl2Y>2c>=g~dEVG$-(2L)%gG^^yHN78^T)x>J+yY9<7C%tuoBN) zqUdtebpmhRIqZ+x$B%w^s4YzNiI=oxqV+VCx?l&zj9#A)4^b7)ElVSqfUFPq8~ii* z!7r^=QUIs~;eLOdu|?XE>KR%ZBNw}vupkQ9UQDk;Z}7jbSPusjof=-fHAT%o%vqov zDVM|PfbE=5dY5%X&ieK6Ic94AJ`J_LJwO=)o*WO1WnRpV)me)#c2m}x=+tngt^m-y z+ic`3Eu_af53r(5sr1UjEcSsnIbxFUm zt6_NPV7VxpVFj9SkOfx?-l1EJ%+30F;0Z9`DZb!z6D>uswcUAfR7O8r6-gT%TSs$U-#Bg000c1L|0qw?0AEl#w^ zuu=+VD1>I}GQxAQ`hohbGgopE&!w+`?<%_e#sq?hdSG?!MD{KO^opY`flTA$Lh zFdJ?KMtW!A_vOwGcmgEFqM6X%VY!oeayxO=IvH}>DW&K_wjLW=8pG?|L$~|T5^t66 z0>S&~yr>FB+ngHP>72(|by<9q0h3*tcuU%uT!14jIl3jYb1d$?5Za<(X7qC=d8`U- z%lfP>3(}%gbEUp3jHaH=c7s9fm9u*< zfj+p+l3i^#J=-tDCupEF^agh%P?Yh)+bP*RMD5@$Qavl-Nu8N9nU7=4yM7^FK?6F} zQ282>cb622uM(C8-Bsfot=vQa{S&k^6aJY(rb9bbwC#Bd5?!sJnv+}9fbvICQc+L2 zFeaPzldV_7dRR7;4kFkCSFtLGl@8m7j}_a7K4DIoKBop?j_Z5ypeeQ6G)a?im0x#G1tRc02xf=7XoH4VXb8cK0zv#UkV+14Fw{?3@ z>82Yzf@GHYQN3A{hUB#*!|nEJviY~4Et!2heHoH}u(#Cl>2qO)E`J&eb*!%mp8xjc z#pW~pKsA%JN6%s;c%`=6oibUeaN&b#*-l4TZ95U4dmXQ?vQ@FU6mMFV zxG0K%qQIz&yIjSFkKFv7wEaDI%|HaxQN)g@s8%-$l#9<;t3KHYi0hh!|B<92z4Nt5 zYS^SmYkUKi>-2K&iy|!UIUfp3GlE&YhSFbk`Dl1z0&m@{Eym-NuKFm*Xs=w~lX?q4 zB&%%Iol}C;K2F6^N|KT!6Hy0I$-~0JHbPBCXWt5q^9=c}1C*7lJ;`JlBen)31sX9e+o_z2O&{_h>P&s~YLwKVyQ zk?$Ha*R>3dg+42MlFBM)CEnXQVG#tAjd*IJaY ziRp+uV_t3xo@+9s@@Ek4qgG}}-GuryfTTPZ&gS{p7q;1tWa%tF=?&zqGWL&L@9c`@ zEcr7`4ESZaCcDOBupeTGt$#9mub;r&R&0@rR%44{33p5tVhF&zA@vGoC@JIyXSWYj z`@RQcwU~~P`K$IC(G%wc3yoeM7xe~Qx{--oe_(V|cFGi!&n=kV`5Klt520=TAj||o z#Lq|YI-SR<@}{l$HJAZN`(<9GgPomaoFY&ne^&Fu-;#-qq-&9%&|}&L&tLJjLOhxG zm0vkAe&U#56*7?NgKE~-5LG+@hw9@B0Vm9{POWpNAQ2n3mGl00jYmr2O&)02^{Gq7 ztboYIR|5BMPerklSEmlC9kgh}!q%Ir2%`dkbB1L%MKEqHD9k>fpG+s~<}9bXG*UC> z%K+!Ap^e+PJTGpCTX)+Z1~OejAZC4c4-G=p^|c+I10Zo6UW@$-`gh{{h-w1-Izg7t zWP{8YiV8AWX3X0p?Gl^{6eU`&6ZGYeJZ&%94=-9!S97ETW+3U!8!s|%&r7I;NHz>G z>(7;s_9mlxi4)d8mMX`~m5ICK;RGt0dzfe1F(oPMWA}(LJjN##7pHi9K?`)Clhd@> zyU{6*7UvGR{GI(fL<^7=O=KCx)`zpEt7pkJ!Efmnerm&%A!+lzPaV|RXt%Ib;+>x>{L z&@FBdX$q+rZ}^F(GFcwk23}>ez6lby5_P{>aYTn&G{4UEXvDnWBrjxtJU3w98%dK9 zxPGK5@2oYs{@TL2MHSiBj~4x3$BElmEGdv zMtWbIe!SRu*0Z^IU!U>pUxHh+fd8+=<)HZF_^ez1LR!$R&kk^tu@hxRk(zQ?*<0kP zQB|q4uqLS~#W z8AOg>QsqxiUVj~y0DN?v{=zsUS>V{77W2VAp{Y*#Vbo050UUCge~$WDm%p#Hqsg$e6#G(r?1@eh z@~*XGy|}Ig14v=5O-i3ZB90fgiq(1|1!E7?sHU{7wIc2qzBxth{YQ0!z1yS*MS%1mgLkdP^@M7>Tft^g&C)z^B&k=Sii;Et9 zZ13aTJI#F5_k86;Vlyh~LrvJfWf#l~Ej-9$>WAvanfx#h-;GhSsfpZM z60)nKiW6eq=*wbM%kyBSdwfb59Te|DKfayn3=Y$`FEU9+x=5+NU2 z{gTLr0OPt}uu~-9l0nwV-FY|B$p5zSYT)w$Jz6ek zqT^=Qp=lU0CalupP2_MP)ueFl8_|GU0n}+VW&CF3#_d~Zf_JNI`WC6!mdm>&;J#Fb z@hWcsY3qLx{$Yk$r5f^I*FWqK0xx2>0sNXi> z4+4+{dA%aqn8@zMK6Bb9z#Y7tm=x|23y?PO#haPj&{>NQ9=!x zs@w6!=5_)rVOQ|~_NaCFXPlo+)MFq}zk1P&{!Vx2?K6OTgGeUV)_ZlnXCNKtt=FJ; z+qo(r2LA$z|38~~{DoYxg@pch{>$J0`1Hvc!JMD^Ab6g-_vQWo0v*m3f{TH* ZumAf%7i$IUKuY;MjCa@wzFsKN{{u(Ta?$_* literal 0 HcmV?d00001 From b7b36feef6433a89bedf6abe083b43c3f5ca39e6 Mon Sep 17 00:00:00 2001 From: Arpit Date: Thu, 17 Jul 2025 15:20:08 +0100 Subject: [PATCH 150/164] new module for ephys with openephys --- .../OpenEphysHTTPServer.m | 554 ------------------ .../OpenEphys_Neuroblueprint.m | 74 ++- .../.bonsai/Settings/Camera_Control.layout | 16 + ExperPort/Plugins/@saveload/SavingSection.m | 3 +- .../SoundCatContextSwitchSummary.m | 384 ++++++++++++ ...itSoundCatContinuous_lida_LP12_250702a.mat | Bin 13899 -> 0 bytes ...itSoundCatContinuous_lida_LP12_250702b.mat | Bin 14079 -> 0 bytes ...itSoundCatContinuous_lida_LP12_250703a.mat | Bin 14101 -> 0 bytes ...itSoundCatContinuous_lida_LP12_250704a.mat | Bin 14110 -> 0 bytes ...itSoundCatContinuous_lida_LP12_250707a.mat | Bin 14084 -> 0 bytes .../ArpitCentrePokeTraining.m | 5 +- .../ArpitSoundCatContinuous.m | 13 +- .../ArpitSoundCatContinuousSMA.m | 18 +- .../PsychometricSection.m | 60 ++ 14 files changed, 539 insertions(+), 588 deletions(-) delete mode 100644 ExperPort/Modules/@OpenEphys_Neuroblueprint/OpenEphysHTTPServer.m create mode 100644 ExperPort/Plugins/@sqlsummary/SoundCatContextSwitchSummary.m delete mode 100644 ExperPort/settings_@ArpitSoundCatContinuous_lida_LP12_250702a.mat delete mode 100644 ExperPort/settings_@ArpitSoundCatContinuous_lida_LP12_250702b.mat delete mode 100644 ExperPort/settings_@ArpitSoundCatContinuous_lida_LP12_250703a.mat delete mode 100644 ExperPort/settings_@ArpitSoundCatContinuous_lida_LP12_250704a.mat delete mode 100644 ExperPort/settings_@ArpitSoundCatContinuous_lida_LP12_250707a.mat diff --git a/ExperPort/Modules/@OpenEphys_Neuroblueprint/OpenEphysHTTPServer.m b/ExperPort/Modules/@OpenEphys_Neuroblueprint/OpenEphysHTTPServer.m deleted file mode 100644 index 891207e9..00000000 --- a/ExperPort/Modules/@OpenEphys_Neuroblueprint/OpenEphysHTTPServer.m +++ /dev/null @@ -1,554 +0,0 @@ -% MIT License -% -% Copyright (c) 2021 Open Ephys -% -% Permission is hereby granted, free of charge, to any person obtaining a copy -% of this software and associated documentation files (the "Software"), to deal -% in the Software without restriction, including without limitation the rights -% to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -% copies of the Software, and to permit persons to whom the Software is -% furnished to do so, subject to the following conditions: -% -% The above copyright notice and this permission notice shall be included in all -% copies or substantial portions of the Software. -% -% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -% IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -% FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -% AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -% LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -% OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -% SOFTWARE. - -classdef OpenEphysHTTPServer < handle - - properties (Constant) - end - - properties - - address - options - - end - - methods - - function self = OpenEphysHTTPServer(host, port) - - self.address = strcat('http://', host, ':', num2str(port)); - self.options = weboptions(... - 'MediaType', 'application/json',... - 'RequestMethod', 'put'); - - end - - end - - methods - - function resp = send(self, varargin) - -% Send a request to the server. -% -% Parameters -% ---------- -% endpoint : String -% The API endpoint for the request. -% Must begin with "/api/" -% payload (optional): Dictionary -% The payload to send with the request. -% -% If a payload is specified, a PUT request -% will be used; otherwise it will be a GET request. - - - endpoint = varargin{1}; - if nargin > 2 - payload = varargin{2}; - end - - try - - if nargin == 2 - resp = webread(strcat(self.address, endpoint)); - else - resp = webwrite(strcat(self.address, endpoint), payload, self.options); - end - - catch ME -% TODO: Catch matlab equivalents of these -% except requests.exceptions.Timeout: -% # Maybe set up for a retry, or continue in a retry loop -% print("Timeout") -% except requests.exceptions.TooManyRedirects: -% # Tell the user their URL was bad and try a different one -% print("Bad URL") -% except requests.exceptions.RequestException as e: -% # Open Ephys server needs to be enabled -% print("Open Ephys HTTP Server likely not enabled") - - resp = "GUI was closed!"; - end - - end - - function resp = load(self, path) - -% Load a configuration file. -% -% Parameters -% ---------- -% path : String -% The path to the configuration file. - - payload = struct('path', path); - - resp = self.send('/api/load', payload); - pause(1); - - end - - function processors = getProcessorList(self) - -% Returns all available processors in the GUI's Processor List - - data = self.send('/api/processors/list'); - processors = string(char({data.processors.name})); - - end - - function processors = getProcessors(self, varargin) - -% Get the list of processors. - -% Parameters -% ---------- -% filterByName : String (Optional) -% Filter the list by processor name. - - data = self.send('/api/processors'); - processors = data.processors; - if nargin > 1 - indices = cellfun(@(v)strcmp(v,varargin{1}),{processors.name}); - processors = processors(indices); - end - - end - - function resp = clearSignalChain(self) - -% Clear the signal chain. - - resp = self.send('/api/processors/clear'); - - end - - function resp = addProcessor(self, name, varargin) - -% Add a processor to the signal chain. -% -% Parameters -% ---------- -% name : String -% The name of the processor to add (e.g. "Record Node") -% source : Integer -% The 3-digit processor ID of the source (e.g. 101) -% dest : Integer -% The 3-digit processor ID of the destination (e.g. 102) - - endpoint = '/api/processors/add'; - payload = struct('name', name); - -% If only processor name is specified, set source to most recently added processor - if nargin == 2 - existingProcessors = self.getProcessors(); - if ~isempty(existingProcessors) - index = find([existingProcessors.id] == max([existingProcessors.id])); - mostRecentProcessor = existingProcessors(index); - payload.source_id = mostRecentProcessor.id; - end - elseif nargin > 2 - payload.source_id = varargin{1}; - if nargin == 4 - payload.dest_id = varargin{2}; - end - end - - resp = self.send(endpoint, payload); - - end - - - function resp = deleteProcessor(self, id) - -% Delete a processor. -% -% Parameters -% ---------- -% processor_id : Integer -% The 3-digit processor ID (e.g. 101) -% - endpoint = '/api/processors/delete'; - payload = struct('id', id); - - resp = self.send(endpoint, payload); - - end - - - function resp = getParameters(self, processorId, streamIndex) - -% Get parameters for a stream. -% -% Parameters -% ---------- -% processorId : Integer -% The 3-digit processor ID (e.g. 101) -% streamIndex : Integer -% The index of the stream (e.g. 0). -% - endpoint = strcat('/api/processors/', num2str(processorId), '/streams/', num2str(streamIndex), '/parameters'); - resp = self.send(endpoint).parameters; - - end - - function resp = setParameters(self, processorID, streamIndex, paramName, value) - -% Update a parameter value -% -% Parameters -% ---------- -% processorID : Integer -% The 3-digit processor ID (e.g. 101) -% streamIndex : Integer -% The index of the stream (e.g. 0) -% paramName : String -% The parameter name (e.g. low_cut) -% value : Any -% The parameter value (must match the parameter type). -% Hint: Float parameters must be sent with a decimal -% included (e.g. 1000.0 instead of 1000) -% - endpoint = strcat('/api/processors/', num2str(processorID), '/streams/', num2str(streamIndex), '/parameters/', paramName); - -% TO FIX:matlab automatically casts doubles to int if no -% integers after the decimal point of a float. - if isa(value, 'double') - payload = struct('value', [], 'class', {'double'}); - payload.value = value + 0.00000000001; - else - payload = struct('value', value); - end - resp = self.send(endpoint, payload); - - end - - - function resp = getRecordingInfo(self, varargin) - -% Get recording information. -% -% Parameters -% ---------- -% key : String -% The key to get. -% - - data = self.send('/api/recording'); - if nargin == 1 - resp = data; - elseif isfield(data, varargin{1}) - resp = data.(varargin{1}); - else - resp = "Invalid key"; - end - end - - function resp = setParentDirectory(self, path) - -% Set the parent directory. -% -% Parameters -% ---------- -% path : String -% The path to the parent directory. - - payload = struct('parent_directory', path); - data = self.send('/api/recording', payload); - resp = data; - - end - - function resp = setPrependText(self, text) - -% Set the prepend text. -% -% Parameters -% ---------- -% text : String -% The text to prepend. -% - payload = struct('prepend_text', text); - data = self.send('/api/recording', payload); - resp = data; - - end - - function resp = setBaseText(self, text) - -% Set the base text. -% -% Parameters -% ---------- -% text : String -% The text to base name of the recording directory (see GUI docs). -% - payload = struct('base_text', text); - data = self.send('/api/recording', payload); - resp = data; - - end - - function resp = setAppendText(self, text) - -% Set the append text. -% -% Parameters -% ---------- -% text : String -% The text to append. -% - payload = struct('append_text', text); - data = self.send('/api/recording', payload); - resp = data; - - end - - - function resp = setStartNewDirectory(self) - -% Set if GUI should start a new directory for the next recording. - - payload = struct('start_new_directory', 'true'); - data = self.send('/api/recording', payload); - resp = data; - - end - - function resp = setFileReaderPath(self, nodeId, filePath) - -% Set the file path. - -% Parameters -% ---------- -% nodeId : Integer -% The node ID. -% filePath : String -% The file path. - - endpoint = strcat('/api/processors/', num2str(nodeId), '/config'); - payload = struct('text', strcat("file=", num2str(filePath))); - data = self.send(endpoint, payload); - - resp = data; - - end - - function resp = setFileReaderIndex(self, nodeId, fileIndex) - -% Set the file index. -% -% Parameters -% ---------- -% nodeId : Integer -% The node ID. -% fileIndex : Integer -% The file index. - - endpoint = strcat('/api/processors/', num2str(nodeId), '/config'); - payload = struct('text', strcat("index=", num2str(fileIndex))); - data = self.send(endpoint, payload); - - resp = data; - - end - - function resp = setRecordEngine(self, nodeId, engine) - -% Set the record engine for a record node. -% -% Parameters -% ---------- -% nodeId : Integer -% The node ID. -% engine: Integer -% The record engine index. - - endpoint = strcat('/api/processors/', num2str(nodeId), '/config'); - payload = struct('text', strcat("engine=", engine)); - data = self.send(endpoint, payload); - - resp = data; - - end - - function resp = setRecordPath(self, nodeId, directory) - -% Set the record path for a Record Node -% -% Parameters -% ---------- -% nodeId: Integer -% The node ID. -% directory : String -% The record path. - - endpoint = strcat('/api/recording/', num2str(nodeId)); - payload = struct('parent_directory', directory); - data = self.send(endpoint, payload); - - resp = data; - - end - function resp = getStatus(self) - -% Returns the current status of the GUI (IDLE, ACQUIRE, or RECORD) - - data = self.send('/api/status'); - - resp = data; - - end - - function resp = acquire(self, varargin) - -% Start acquisition. -% -% Parameters -% ---------- -% duration : Integer (optional) -% The acquisition duration in seconds. If given, the -% GUI will acquire data for the specified interval -% and then stop. -% -% By default, acquisition will continue until it -% is stopped by another command. - - payload = struct('mode', 'ACQUIRE'); - data = self.send('/api/status', payload); - - if nargin == 2 - payload = struct('mode', 'IDLE'); - pause(varargin{1}); - data = self.send('/api/status', payload); - end - - resp = data; - - end - - function resp = record(self, varargin) - -% Start recording. -% -% Parameters -% ---------- -% duration : Integer (optional) -% The record duration in seconds. If given, the -% GUI will record data for the specified interval -% and then stop. -% -% By default, recoridng will continue until it -% is stopped by another command. - - payload = struct('mode', 'RECORD'); - data = self.send('/api/status', payload); - - if nargin == 2 - payload = struct('mode', 'IDLE'); - pause(varargin{1}); - data = self.send('/api/status', payload); - end - - resp = data; - - end - - function resp = idle(self, varargin) - -% Stop acquisition/recording. -% -% Parameters -% ---------- -% duration : Integer (optional) -% The idle duration in seconds. If given, the -% GUI will idle for the specified interval -% and then return to its previous state. -% -% By default, this command will stop -% acquisition/recording and return immediately. - - mode = self.getStatus().mode; - - payload = struct('mode', 'IDLE'); - data = self.send('/api/status', payload); - - if nargin == 2 - payload = struct('mode', mode); - pause(varargin{1}); - data = self.send('/api/status', payload); - end - - resp = data; - - end - - function resp = message(self, message) - -% Broadcast a message to all processors during acquisition -% -% Parameters -% ---------- -% message : String -% The message to send. - - payload = struct('text', message); - data = self.send('/api/message', payload); - - resp = data; - - end - - function resp = config(self, nodeId, message) - -% Send a configuration message to a specific processor. -% -% Parameters -% ---------- -% nodeId : Integer -% The 3-digit processor ID (e.g. 101) -% message : String -% The message to send. - - endpoint = strcat('/api/processors/', num2str(nodeId), '/config'); - payload = struct('text', message); - data = self.send(endpoint, payload); - - resp = data; - - end - - function resp = quit(self) - -% Quit the GUI - - payload = struct('command', 'quit'); - data = self.send('/api/window', payload); - - resp = data; - end - - end - -end \ No newline at end of file diff --git a/ExperPort/Modules/@OpenEphys_Neuroblueprint/OpenEphys_Neuroblueprint.m b/ExperPort/Modules/@OpenEphys_Neuroblueprint/OpenEphys_Neuroblueprint.m index 074f5fca..542dd7f0 100644 --- a/ExperPort/Modules/@OpenEphys_Neuroblueprint/OpenEphys_Neuroblueprint.m +++ b/ExperPort/Modules/@OpenEphys_Neuroblueprint/OpenEphys_Neuroblueprint.m @@ -1,4 +1,4 @@ -function [obj, varargout] = OpenEphys_Neuroblueprint(varargin) +function [obj, varargout] = OpenEphys_Neuroblueprint_GUI(varargin) % This is a class-based version of the GUI controller, structured similarly % to runrats.m. It manages the experimental workflow through different % 'actions' called via a switch statement. @@ -11,19 +11,19 @@ % --- Boilerplate for class definition and action handling --- obj = class(struct, mfilename); varargout = {}; % Initialize varargout for actions that return values -if nargin==0 || (nargin==1 && ischar(varargin{1}) && strcmp(varargin{1}, 'empty')), +if nargin==0 || (nargin==1 && ischar(varargin{1}) && strcmp(varargin{1}, 'empty')) return; -end; -if isa(varargin{1}, mfilename), - if length(varargin) < 2 || ~ischar(varargin{2}), +end +if isa(varargin{1}, mfilename) + if length(varargin) < 2 || ~ischar(varargin{2}) error(['If called with a "%s" object as first arg, a second arg, a ' ... 'string specifying the action, is required\n']); else action = varargin{2}; varargin = varargin(3:end); - end; + end else action = varargin{1}; varargin = varargin(2:end); -end; -if ~ischar(action), error('The action parameter must be a string'); end; +end +if ~ischar(action), error('The action parameter must be a string'); end GetSoloFunctionArgs(obj); % --- End of boilerplate --- @@ -35,6 +35,9 @@ case 'init' % This case is called once to create the GUI and initialize all parameters. + % So that only the CPU-based software renderer instead of your graphics card + opengl software; + % Start Bpod if not already running if evalin('base', 'exist(''BpodSystem'', ''var'')') if evalin('base', '~isempty(BpodSystem)'), newstartup; else, flush; end @@ -46,7 +49,6 @@ SoloParamHandle(obj, 'oe_controller', 'value', []); SoloParamHandle(obj, 'behav_obj', 'value', []); SoloParamHandle(obj, 'blinking_timer', 'value', []); - SoloParamHandle(obj, 'monitor_timer', 'value', []); SoloParamHandle(obj, 'current_params', 'value', []); SoloParamHandle(obj, 'session_base_path', 'value', ''); SoloParamHandle(obj,'is_running','value',0); @@ -88,7 +90,7 @@ uicontrol(p2, 'Style', 'text', 'String', 'Project Name (Root):', 'Units', 'normalized', 'Position', [0.01, 0.85, 0.28, 0.1], 'HorizontalAlignment', 'right'); handles.proj_edit = uicontrol(p2, 'Style', 'edit', 'String', 'sound_cat_rat', 'Units', 'normalized', 'Position', [0.3, 0.85, 0.65, 0.12]); uicontrol(p2, 'Style', 'text', 'String', 'Subject ID:', 'Units', 'normalized', 'Position', [0.01, 0.7, 0.28, 0.1], 'HorizontalAlignment', 'right'); - handles.sub_edit = uicontrol(p2, 'Style', 'edit', 'String', '002', 'Units', 'normalized', 'Position', [0.3, 0.7, 0.65, 0.12]); + handles.sub_edit = uicontrol(p2, 'Style', 'edit', 'String', '003', 'Units', 'normalized', 'Position', [0.3, 0.7, 0.65, 0.12]); uicontrol(p2, 'Style', 'text', 'String', 'Local Path:', 'Units', 'normalized', 'Position', [0.01, 0.55, 0.28, 0.1], 'HorizontalAlignment', 'right'); handles.local_edit = uicontrol(p2, 'Style', 'edit', 'String', 'C:\Ephys_Experiment_Data', 'Units', 'normalized', 'Position', [0.3, 0.55, 0.5, 0.12]); handles.local_browse = uicontrol(p2, 'Style', 'pushbutton', 'String', 'Browse...', 'Units', 'normalized', 'Position', [0.81, 0.55, 0.16, 0.13], 'Callback', {@(h,e) feval(mfilename, obj, 'browse_path', 'local')}); @@ -267,7 +269,6 @@ currentState.value = 'Stop'; set(handles.control_button, 'String', 'Stop'); feval(mfilename, obj, 'start_blinking'); - feval(mfilename, obj, 'start_monitoring'); try log_message(handles, 'Starting behavioral protocol...'); @@ -285,10 +286,11 @@ handles = value(ui_handles); log_message(handles, '--- STOP sequence initiated ---'); feval(mfilename, obj, 'stop_blinking'); - + behav_save_dir = fullfile(params.local_path, value(session_base_path), 'behav'); + try log_message(handles, 'Ending behavioral session (saving data)...'); - feval(mfilename, obj, 'behav_control', 'end', params.protocol_name, params.behav_path); + feval(mfilename, obj, 'behav_control', 'end', params.protocol_name, params.behav_path,behav_save_dir); log_message(handles, 'Behavioral data saved successfully.'); catch ME log_message(handles, ['FATAL ERROR ending behavioral session: ' ME.message]); @@ -337,7 +339,7 @@ case 'load_protocol_after_crash' experimenter = args{1}; ratname = args{2}; protocol_name = args{3}; - video_save_dir = args{4}; behav_path = args{5}; + behav_path = args{4}; log_message(handles, ['Loading protocol: ' protocol_name]); dispatcher('set_protocol', protocol_name); rath = get_sphandle('name', 'ratname', 'owner', protocol_name); @@ -351,17 +353,15 @@ if isfile(fullfile(temp_data_dir,temp_data_file)) dispatcher('runstart_disable'); load_soloparamvalues(ratname, 'experimenter', experimenter,... - 'owner', protobj, 'interactive', 0,'data_file',fullfile(temp_data_dir,temp_data_file)); + 'owner', protocol_name, 'interactive', 0,'data_file',fullfile(temp_data_dir,temp_data_file)); dispatcher('runstart_enable'); end - % [sfile, ~] = SavingSection(protobj, 'get_set_filename'); if ~dispatcher('is_running'), pop_history(class(protobj), 'include_non_gui', 1); feval(protocol_name, protobj, 'prepare_next_trial'); end - % feval(protocol_name, protobj, 'set_setting_params', ratname, experimenter, sfile, char(datetime('now')), video_save_dir); - + case 'crashed' log_message(handles, '--- BEHAVIOR CRASH RECOVERY INITIATED ---'); params = value(current_params); - feval(mfilename, obj, 'behav_control', 'load_protocol_after_crash', params.experimenter, params.rat_name, params.protocol_name, fullfile(params.local_path, value(session_base_path), 'behav'), params.behav_path); + feval(mfilename, obj, 'behav_control', 'load_protocol_after_crash', params.experimenter, params.rat_name, params.protocol_name, params.behav_path); feval(mfilename, obj, 'behav_control', 'run', params.protocol_name); log_message(handles, '--- RECOVERY COMPLETE: Behavior protocol restarted ---'); @@ -375,19 +375,19 @@ case 'end' - protocol_name = args{1}; root_dir = args{2}; + protocol_name = args{1}; root_dir = args{2}; behav_copy_dir = args{3}; log_message(handles, 'Stopping dispatcher...'); dispatcher(value(behav_obj), 'Stop'); %Let's pause until we know dispatcher is done running - set(value(stopping_complete_timer), 'Period', 2,'TimerFcn', {@(h,e) feval(mfilename, obj, 'behav_control','end_continued',protocol_name, root_dir)}); + set(value(stopping_complete_timer), 'Period', 2,'TimerFcn', {@(h,e) feval(mfilename, obj, 'behav_control','end_continued',protocol_name, root_dir,behav_copy_dir)}); %set(value(stopping_complete_timer),'TimerFcn',[mfilename,obj,'behav_control','(''end_continued'');']); start(value(stopping_complete_timer)); case 'end_continued' %% end_continued if value(stopping_process_completed) %This is provided by RunningSection - protocol_name = args{1}; root_dir = args{2}; + protocol_name = args{1}; root_dir = args{2}; destination_path = args{3}; stop(value(stopping_complete_timer)); %Stop looping. is_running.value = 0; feval(mfilename, obj, 'behav_control', 'send_empty_state_machine'); @@ -396,13 +396,35 @@ feval(protocol_name, protobj, 'end_session'); log_message(handles, 'Saving data and settings...'); data_file = SavingSection(protobj, 'savedata', 'interactive', 0); - try, feval(protocol_name, protobj, 'pre_saving_settings'); catch, log_message(handles, 'Protocol does not have a pre_saving_settings section.'); end + try + feval(protocol_name, protobj, 'pre_saving_settings'); + catch + log_message(handles, 'Protocol does not have a pre_saving_settings section.'); + end [settings_file, ~] = SavingSection(protobj, 'get_set_filename'); SavingSection(protobj, 'savesets', 'interactive', 0); log_message(handles, 'Committing data and settings to SVN...'); - commit_to_svn(handles, data_file, root_dir); - commit_to_svn(handles, settings_file, root_dir); + commit_to_svn(handles, data_file, settings_file, root_dir); dispatcher('set_protocol', ''); + dispatcher('close'); + + % wait sometime for dispatcher to stop using a timer object + t = timer; + t.StartDelay = 1; + t.TimerFcn = @(~,~) log_message('waiting for before copying'); + start(t);% Start the timer (it runs in the background) + wait(t); % wait() pauses the script until the timer object is done. + delete(t);% Clean up the timer from memory + + % Copy the data file to the folder saving ephys data + [status, msg] = copyfile(data_file, destination_path); + % Check if the copy was successful + if status + log_message(handles,'Data File copied successfully.'); + else + log_message(handles,['Error copying Data file: ' msg]); + end + end case 'create_svn_data_dir' @@ -492,7 +514,7 @@ blinking_timer.value = []; end set(handles.control_button, 'BackgroundColor', [1, 0.4, 0.4]); - + case 'crash_detected' % handles = feval(mfilename, obj, 'get_ui_handles'); diff --git a/ExperPort/Plugins/@bonsaicamera/.bonsai/Settings/Camera_Control.layout b/ExperPort/Plugins/@bonsaicamera/.bonsai/Settings/Camera_Control.layout index 6009675f..1d5df696 100644 --- a/ExperPort/Plugins/@bonsaicamera/.bonsai/Settings/Camera_Control.layout +++ b/ExperPort/Plugins/@bonsaicamera/.bonsai/Settings/Camera_Control.layout @@ -15,6 +15,22 @@ + + false + + 234 + 234 + + + 336 + 279 + + Maximized + Bonsai.Vision.Design.IplImageVisualizer + + + + false diff --git a/ExperPort/Plugins/@saveload/SavingSection.m b/ExperPort/Plugins/@saveload/SavingSection.m index 2f07173b..380b731f 100644 --- a/ExperPort/Plugins/@saveload/SavingSection.m +++ b/ExperPort/Plugins/@saveload/SavingSection.m @@ -275,7 +275,8 @@ case 'get_settings_file_load_time' [dummy, x1] = runrats('get_settings_file_load_time'); clear('dummy'); x2 = value(settings_file_load_time); - x = max(x1, x2); settings_file_load_time.value = x; + x = max(double(x1), double(x2)); + settings_file_load_time.value = x; y = []; z = []; return; diff --git a/ExperPort/Plugins/@sqlsummary/SoundCatContextSwitchSummary.m b/ExperPort/Plugins/@sqlsummary/SoundCatContextSwitchSummary.m new file mode 100644 index 00000000..587d03b5 --- /dev/null +++ b/ExperPort/Plugins/@sqlsummary/SoundCatContextSwitchSummary.m @@ -0,0 +1,384 @@ +function [err] = SoundCatContextSwitchSummary(obj, varargin) + %% Function Description + % Sends session data to the bdata.sessions table + % By default, it tries to get data from standard plugins + % See the pairs structure below for details + + %% Initialize Error Logging + diary('sendsummary_error_log.txt'); + + try + %% Define Default Parameters + pairs = { + 'force_send', 0;... + 'hits', get_val('hit_history');... + 'sides', get_val('previous_sides');... + 'violations' get_val('violation_history') + 'savetime', get_savetime(obj);... + 'endtime', get_endtime(obj);... + 'sessiondate', get_sessiondate(obj);... + 'hostname', get_rigid;... + 'IP_addr', get_network_info;... + 'experimenter', get_val('SavingSection_experimenter');... + 'ratname', get_val('SavingSection_ratname');... + 'n_done_trials', get_val('n_completed_trials');... + 'protocol', class(obj);... + 'protocol_data', 'NULL';... + 'peh', get_parsed_events;... + 'last_comment', cleanup(CommentsSection(obj,'get_latest'));... + 'data_file', SavingSection(obj,'get_data_file');... + 'technotes', get_val('TechComment')... % Added technician comments field + }; + parseargs(varargin, pairs); + + %% Validate Rig ID + [rigID, e, m] = bSettings('get','RIGS','Rig_ID'); + if ~force_send && isnan(rigID) + err = 42; + return + end + + %% Handle Tech Notes + if ~ischar(technotes) || isempty(technotes) + technotes = ''; + end + + %% Calculate Performance Metrics + hits = hits(1:n_done_trials); + sides = sides(1:n_done_trials); + total_correct = nanmean(hits); + + try + right_correct = nanmean(hits(sides=='r')); + left_correct = nanmean(hits(sides=='l')); + catch ME + fprintf(2, 'Error calculating correct pokes\n'); + disp(ME.message); + disp(ME.stack); + right_correct = -1; + left_correct = -1; + end + + %% Extract Path Information + [pth, fl] = extract_path(data_file); + + %% Calculate Violation Percentage + try + percent_violations = mean(isnan(hits)); + catch + percent_violations = -1; + end + + + %% Calculate Poke Counts + left_pokes = 0; + center_pokes = 0; + right_pokes = 0; + for px = 1:numel(peh) + left_pokes = left_pokes + numel(peh(px).pokes.L); + center_pokes = center_pokes + numel(peh(px).pokes.C); + right_pokes = right_pokes + numel(peh(px).pokes.R); + end + + %% Get Session ID and Start Time + sessid = getSessID(obj); + starttime = get_starttime(sessid); % added 20091214 + + if isempty(starttime) + % Compute start time if not found in sess_started table + starttime = datestr(datenum(savetime)-sess_length(obj)/60/60/24, 13); + else + % Update sess_started table indicating session end + bdata('call set_sess_ended("{Si}", "{Si}")', sessid, 1); + end + + % psych_result{n_context}.trial_start = trial_start; + % psych_result{n_context}.trial_end = trial_end; + % psych_result{n_context}.context = unique(category_distribution); + % psych_result{n_context}.percept_boundary = fitParams(1); + % psych_result{n_context}.total_correct = -1; + % psych_result{n_context}.violations = -1; + % psych_result{n_context}.right_correct = -1; + % psych_result{n_context}.left_correct = -1; + + %% Define SQL columns and placeholders + colstr = [ + 'sessid, ',... + 'sessiondate, '... DATE + 'starttime, '... TIME + 'endtime, '... TIME + 'ratname, '... VARCHAR + 'experimenter, '... VARCHAR + 'protocol, '... VARCHAR + 'hostname, '... VARCHAR + 'IP_address, '... VARCHAR + 'training_stage_no, '... INT + 'training_stage_name, '... VARCHAR + 'n_done_trials, '... INT + 'percent_violations, '... VARCHAR + 'percent_timeout, '... VARCHAR + 'stage1_trials_total, '... INT + 'stage1_trials_today, '... INT + 'stage1_trials_valid, '... INT + 'stage1_percent_violation, '... VARCHAR + 'stage1_percent_timeout, '... VARCHAR + 'stage2_trials_total, '... INT + 'stage2_trials_today, '... INT + 'stage2_trials_valid, '... INT + 'stage2_percent_violation, '... VARCHAR + 'stage2_percent_timeout, '... VARCHAR + 'stage3_trials_total, '... INT + 'stage3_trials_today, '... INT + 'stage3_trials_valid, '... INT + 'stage3_percent_violation, '... VARCHAR + 'stage3_percent_timeout, '... VARCHAR + 'stage4_trials_total, '... INT + 'stage4_trials_today, '... INT + 'stage4_trials_valid, '... INT + 'stage4_percent_violation, '... VARCHAR + 'stage4_percent_timeout, '... VARCHAR + 'datafile, '... VARCHAR + 'datapath, '... VARCHAR + 'videopath, '... VARCHAR + 'CP_Dur_reached, '... VARCHAR + 'centre_poke, '... VARCHAR + 'left_poke, '... VARCHAR + 'right_poke, '... VARCHAR + 'comments, '... VARCHAR + 'tech_notes']; % total 63 columns + + +valstr = [ + '"{Si}",', ... % sessid + '"{S}",', ... % sessiondate + '"{S}",', ... % starttime + '"{S}",', ... % endtime + '"{S}",', ... % ratname + '"{S}",', ... % experimenter + '"{S}",', ... % protocol + '"{S}",', ... % hostname + '"{S}",', ... % IP_address + '"{S}",', ... % training_stage_no + '"{S}",', ... % training_stage_name + '"{S}",', ... % n_done_trials + '"{S}",', ... % percent_violations + '"{S}",', ... % percent_timeout + '"{S}",', ... % stage1_trials_total + '"{S}",', ... % stage1_trials_today + '"{S}",', ... % stage1_trials_valid + '"{S}",', ... % stage1_percent_violation + '"{S}",', ... % stage1_percent_timeout + '"{S}",', ... % stage2_trials_total + '"{S}",', ... % stage2_trials_today + '"{S}",', ... % stage2_trials_valid + '"{S}",', ... % stage2_percent_violation + '"{S}",', ... % stage2_percent_timeout + '"{S}",', ... % stage3_trials_total + '"{S}",', ... % stage3_trials_today + '"{S}",', ... % stage3_trials_valid + '"{S}",', ... % stage3_percent_violation + '"{S}",', ... % stage3_percent_timeout + '"{S}",', ... % stage4_trials_total + '"{S}",', ... % stage4_trials_today + '"{S}",', ... % stage4_trials_valid + '"{S}",', ... % stage4_percent_violation + '"{S}",', ... % stage4_percent_timeout + '"{S}",', ... % stage5_trials_total + '"{S}",', ... % stage5_trials_today + '"{S}",', ... % stage5_trials_valid + '"{S}",', ... % stage5_percent_violation + '"{S}",', ... % stage5_percent_timeout + '"{S}",', ... % stage6_trials_total + '"{S}",', ... % stage6_trials_today + '"{S}",', ... % stage6_trials_valid + '"{S}",', ... % stage6_percent_violation + '"{S}",', ... % stage6_percent_timeout + '"{S}",', ... % stage7_trials_total + '"{S}",', ... % stage7_trials_today + '"{S}",', ... % stage7_trials_valid + '"{S}",', ... % stage7_percent_violation + '"{S}",', ... % stage7_percent_timeout + '"{S}",', ... % stage8_trials_total + '"{S}",', ... % stage8_trials_today + '"{S}",', ... % stage8_trials_valid + '"{S}",', ... % stage8_percent_violation + '"{S}",', ... % stage8_percent_timeout + '"{S}",', ... % datafile + '"{S}",', ... % datapath + '"{S}",', ... % video_path + '"{S}",', ... % videofile + '"{S}",', ... % left_pokes + '"{S}",', ... % center_pokes + '"{S}",', ... % right_pokes + '"{S}",', ... % comments + '"{S}"', ... % technotes + ]; + + + %% Construct SQL string + sqlstr = ['insert into CentrePokeTraining (' strtrim(colstr) ') values (' strtrim(valstr) ')']; + + + %% Execute SQL Query + bdata(sqlstr, ... + sessid, ... + sessiondate, ... + starttime, ... + endtime, ... + ratname, ... + experimenter, ... + protocol, ... + hostname, ... + IP_addr, ... + protocol_data.stage_no, ... + protocol_data.stage_name, ... + n_done_trials, ... + protocol_data.violation_percent, ... + protocol_data.timeout_percent, ... + protocol_data.stage1_trials_total, ... + protocol_data.stage1_trials_today, ... + protocol_data.stage1_trials_valid, ... + protocol_data.stage1_violationrate, ... + protocol_data.stage1_timeoutrate, ... + protocol_data.stage2_trials_total, ... + protocol_data.stage2_trials_today, ... + protocol_data.stage2_trials_valid, ... + protocol_data.stage2_violationrate, ... + protocol_data.stage2_timeoutrate, ... + protocol_data.stage3_trials_total, ... + protocol_data.stage3_trials_today, ... + protocol_data.stage3_trials_valid, ... + protocol_data.stage3_violationrate, ... + protocol_data.stage3_timeoutrate, ... + protocol_data.stage4_trials_total, ... + protocol_data.stage4_trials_today, ... + protocol_data.stage4_trials_valid, ... + protocol_data.stage4_violationrate, ... + protocol_data.stage4_timeoutrate, ... + pth, ... + fl, ... + protocol_data.video_filepath, ... + protocol_data.CP_Duration, .... + left_pokes, ... + center_pokes, ... + right_pokes, ... + last_comment, ... + technotes ... + ); + + % Log successful execution + fprintf('No errors encountered during sendsummary execution.\n'); + + + catch ME + fprintf(2, 'Failed to send summary to sql\n'); + disp(ME.message); + disp(ME.stack); + err = 1; + + % Log error details + fprintf('Error occurred during sendsummary execution:\n'); + fprintf('%s\n', ME.message); + fprintf('%s\n', ME.stack); + end + + diary off; +end + +%% Helper Functions +function y = get_val(x) + y = get_sphandle('fullname', x); + if isempty(y) + y = ''; + else + y = value(y{1}); + end +end + +function y = get_parsed_events + y = get_sphandle('fullname', 'ProtocolsSection_parsed_events'); + y = cell2mat(get_history(y{1})); +end + +function y = sess_length(obj) + % Estimate session length + GetSoloFunctionArgs(obj); + + try + st = parsed_events_history{1}.states; %#ok + ss = st.starting_state; + es = st.starting_state; + eval(['ST = min(min(st.', ss, '));']); + eval(['ET = max(max(st.', es, '));']); + + D1 = round(ET - ST); + + pt = get_sphandle('name', 'prot_title'); + [Ts, Te] = get_times_from_prottitle(value(pt{1})); + Ts = [Ts, ':00']; + Te = [Te, ':00']; + + Dt = timediff(Ts, Te, 2); + y = Dt + D1; + catch ME + showerror; % Assuming showerror is a function that displays errors + fprintf(2, 'Error calculating session length\n'); + disp(ME.message); + disp(ME.stack); + end +end + +function y = cleanup(M) + try + y = strtrim(sprintf('%s', M')); + catch + y = ''; + end +end + +function [p, f] = extract_path(s) + last_fs = find(s == filesep, 1, 'last' ); + p = s(1:last_fs); + f = s(last_fs+1:end); +end + +function y = get_savetime(obj) + [x, x, y] = SavingSection(obj, 'get_info'); + if y == '_' + y = datestr(now); + end +end + +function y = get_endtime(obj) + [x, x, savetime] = SavingSection(obj, 'get_info'); + if savetime == '_' + y = datestr(now, 13); + else + y = datestr(savetime, 13); + end +end + +function y = get_starttime(sessid) + y = bdata('select starttime from sess_started where sessid="{Si}"', sessid); + if ~isempty(y) + y = y{1}; + end +end + +function y = get_sessiondate(obj) + [x, x, savetime] = SavingSection(obj, 'get_info'); + if savetime == '_' + y = datestr(now, 29); + else + y = datestr(savetime, 29); + end +end + +function y = get_rigid + y = getRigID; + if isnan(y) + y = 'Unknown'; + elseif isnumeric(y) + y = sprintf('Rig%02d', y); + end +end diff --git a/ExperPort/settings_@ArpitSoundCatContinuous_lida_LP12_250702a.mat b/ExperPort/settings_@ArpitSoundCatContinuous_lida_LP12_250702a.mat deleted file mode 100644 index acc24254e002cd24c31596a6a6d9e722c086b688..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 13899 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 diff --git a/ExperPort/settings_@ArpitSoundCatContinuous_lida_LP12_250702b.mat b/ExperPort/settings_@ArpitSoundCatContinuous_lida_LP12_250702b.mat deleted file mode 100644 index 35bc6871599ecd415d2d4eaf26e513aca2354786..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 14079 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 diff --git a/ExperPort/settings_@ArpitSoundCatContinuous_lida_LP12_250703a.mat b/ExperPort/settings_@ArpitSoundCatContinuous_lida_LP12_250703a.mat deleted file mode 100644 index 5102bd5c6ec565ca9a65c5ed8875a0ec1a2a7baf..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 14101 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 C4Tm%U diff --git a/ExperPort/settings_@ArpitSoundCatContinuous_lida_LP12_250704a.mat b/ExperPort/settings_@ArpitSoundCatContinuous_lida_LP12_250704a.mat deleted file mode 100644 index af2910e4ebcb96b13ad2637dabbc8993a3b3af21..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 14110 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 P$>*RlVZHM6MvD3$nwN&w diff --git a/ExperPort/settings_@ArpitSoundCatContinuous_lida_LP12_250707a.mat b/ExperPort/settings_@ArpitSoundCatContinuous_lida_LP12_250707a.mat deleted file mode 100644 index 0e14b4de078d62f6a2813db1df418236304ffb8f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 14084 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

_Vc4PJE_8&tnm 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 diff --git a/Protocols/@ArpitCentrePokeTraining/ArpitCentrePokeTraining.m b/Protocols/@ArpitCentrePokeTraining/ArpitCentrePokeTraining.m index 18535b0b..527bcd27 100644 --- a/Protocols/@ArpitCentrePokeTraining/ArpitCentrePokeTraining.m +++ b/Protocols/@ArpitCentrePokeTraining/ArpitCentrePokeTraining.m @@ -324,6 +324,9 @@ % instead not sending it asif required can be taken from session data % sendsummary(obj); + + % Sending Summary Statistics to SQL Database + perf = struct([]); perf = PerformanceSummarySection(obj, 'evaluate'); [perf.violation_percent,perf.timeout_percent] = SessionPerformanceSection(obj, 'evaluate'); @@ -337,7 +340,7 @@ perf.video_filepath = BonsaiCameraInterface(obj,'video_filepath'); - CentrePoketrainingsummary(obj,'protocol_data',perf); + CentrePoketrainingsummary(obj,'protocol_data',perf); % CommentsSection(obj, 'append_line', ... % sprintf(['ntrials = %d, violations = %.2f, timeouts=%.2f, hits = %.2f\n', ... diff --git a/Protocols/@ArpitSoundCatContinuous/ArpitSoundCatContinuous.m b/Protocols/@ArpitSoundCatContinuous/ArpitSoundCatContinuous.m index 6ad2ff80..1c8044d6 100644 --- a/Protocols/@ArpitSoundCatContinuous/ArpitSoundCatContinuous.m +++ b/Protocols/@ArpitSoundCatContinuous/ArpitSoundCatContinuous.m @@ -35,7 +35,7 @@ %% init case 'init' - dispatcher('set_trialnum_indicator_flag'); + % dispatcher('set_trialnum_indicator_flag'); hackvar = 10; SoloFunctionAddVars('SessionModel', 'ro_args', 'hackvar'); %#ok SoloParamHandle(obj, 'myfig', 'saveable', 0); myfig.value = figure; @@ -132,7 +132,7 @@ ArpitSoundCatContinuousSMA(obj, 'init'); - % feval(mfilename, obj, 'prepare_next_trial'); + feval(mfilename, obj, 'prepare_next_trial'); case 'change_water_modulation_params' display_guys = [1 150 300]; @@ -163,13 +163,12 @@ SideSection(obj, 'prepare_next_trial'); % Run SessionDefinition *after* SideSection so we know whether the % trial was a violation or not - SessionDefinition(obj, 'next_trial'); + % SessionDefinition(obj, 'next_trial'); StimulatorSection(obj, 'update_values'); StimulusSection(obj,'prepare_next_trial'); SoundManagerSection(obj, 'send_not_yet_uploaded_sounds'); [sma, prepare_next_trial_states] = ArpitSoundCatContinuousSMA(obj, 'prepare_next_trial'); - % PerformanceSection(obj, 'evaluate'); % Default behavior of following call is that every 20 trials, the data % gets saved, not interactive, no commit to CVS. @@ -223,9 +222,13 @@ case 'pre_saving_settings' StimulusSection(obj,'hide'); + PsychometricSection(obj,'hide'); SessionDefinition(obj, 'run_eod_logic_without_saving'); - sendsummary(obj); + % Sending Summary Statistics to SQL Database + perf = PsychometricSection(obj, 'evaluate'); + + % SoundCatContextSwitchSummary(obj,'protocol_data',perf); %% otherwise otherwise diff --git a/Protocols/@ArpitSoundCatContinuous/ArpitSoundCatContinuousSMA.m b/Protocols/@ArpitSoundCatContinuous/ArpitSoundCatContinuousSMA.m index 9f0d5fa9..f15401bb 100644 --- a/Protocols/@ArpitSoundCatContinuous/ArpitSoundCatContinuousSMA.m +++ b/Protocols/@ArpitSoundCatContinuous/ArpitSoundCatContinuousSMA.m @@ -109,6 +109,21 @@ 'sustain', WValveTime, 'DOut', WValveSide); % water delivery side sma = add_scheduled_wave(sma, 'name', 'reward_collection_dur', 'preamble', SideLed_duration + RewardCollection_duration); % time to collect the reward + % Scheduled Wave for Ephys Trial + + if ~isnan(bSettings('get', 'DIOLINES', 'trialnum_indicator')) + trigephys = bSettings('get', 'DIOLINES', 'trialnum_indicator'); + else + trigephys = nan; + end + + if strcmpi(value(StimLine),'Ephys') + sma = add_scheduled_wave(sma, 'name', 'EphysTrig', 'preamble', 0, 'sustain', ... + 0.4, 'DOut', trigephys, 'loop', 0); %for Ephys + else + sma = add_scheduled_wave(sma, 'name', 'EphysTrig', 'preamble', 0, 'sustain', 0); %dummy wave. + end + %% %%%%%%%%%%% % *STATES* %%%%%%%%%%%%%%%%%%% @@ -251,7 +266,8 @@ sma = add_state(sma, 'self_timer', max(0.001, violation_iti-viol_snd_duration), ... 'input_to_statechange',{'Tup','preclean_up_state'}); - sma = add_state(sma,'name','preclean_up_state','self_timer',0.5,... + sma = add_state(sma,'name','preclean_up_state','self_timer',0.8,... + 'output_actions', {'SchedWaveTrig','EphysTrig'},... 'input_to_statechange',{'Tup','check_next_trial_ready'}); varargout{2} = {'check_next_trial_ready'}; diff --git a/Protocols/@ArpitSoundCatContinuous/PsychometricSection.m b/Protocols/@ArpitSoundCatContinuous/PsychometricSection.m index acb9c7b3..ad017b35 100644 --- a/Protocols/@ArpitSoundCatContinuous/PsychometricSection.m +++ b/Protocols/@ArpitSoundCatContinuous/PsychometricSection.m @@ -267,6 +267,7 @@ grid on; catch end + case 'PushButton_Context' try Stim_Params = StimulusSection(obj,'stim_params'); @@ -452,6 +453,65 @@ end end + case 'evaluate' + if n_done_trials > 1 + + Stim_Params = StimulusSection(obj,'stim_params'); + + psych_result = cell(1,value(thiscontext)); + + for n_context = 1:value(thiscontext) + + eval(sprintf('trial_start = value(Context%i_trialStart);',n_context)); + eval(sprintf('trial_end = value(Context%i_trialEnd);',n_context)); + stim2analyze = stimulus_history(trial_start:trial_end); + sides2analyze = previous_sides(trial_start:trial_end); + hit_history2analyze = hit_history(trial_start:trial_end); + category_distribution2analyze = stimulus_distribution_history(trial_start:trial_end); + valid_hit_history_trials = find(~isnan(hit_history2analyze)); + sides = sides2analyze(valid_hit_history_trials); + stim = stim2analyze(valid_hit_history_trials); + category_distribution = category_distribution2analyze(valid_hit_history_trials); + hit_values = hit_history2analyze(valid_hit_history_trials)'; + resp = zeros(size(hit_values)); + + if strcmpi(Rule,'S1>S_boundary Left') % Category B is Left so its value is 1 + resp((sides == 108 & hit_values == 1) | (sides == 114 & hit_values == 0)) = 1; + else % Category B is Right so its value is 1 + resp((sides == 114 & hit_values == 1) | (sides == 108 & hit_values == 0)) = 1; + end + + try + [~, fitParams, ~,~] = realtimepsychometricFit(stim,resp,Stim_Params); + catch + fitParams = [nan,nan,nan,nan]; + end + + psych_result{n_context}.trial_start = trial_start; + psych_result{n_context}.trial_end = trial_end; + psych_result{n_context}.context = char(unique(category_distribution)); + psych_result{n_context}.percept_boundary = fitParams(1); + try + psych_result{n_context}.total_correct = mean(hit_history2analyze,"omitnan"); + psych_result{n_context}.violations = mean(isnan(hit_history2analyze)); + psych_result{n_context}.right_correct = mean(hit_history2analyze(sides2analyze==114),"omitnan"); + psych_result{n_context}.left_correct = mean(hit_history2analyze(sides2analyze==108),"omitnan"); + catch + fprintf(2, 'Error calculating correct pokes\n'); + disp(ME.message); + disp(ME.stack); + psych_result{n_context}.total_correct = -1; + psych_result{n_context}.violations = -1; + psych_result{n_context}.right_correct = -1; + psych_result{n_context}.left_correct = -1; + end + + end + + varargout{1} = psych_result; + + end + %% Case close case 'close' set(value(myfig), 'Visible', 'off'); From 6fbc5261cccf0d11e4cc52fafb4089d38e69a533 Mon Sep 17 00:00:00 2001 From: Arpit Date: Wed, 30 Jul 2025 16:32:35 +0100 Subject: [PATCH 151/164] major refactoring of the scripts to change the stimulus selection and plot as well as new module for ephys to control both spikeglx and openephys. Work in progress and could see another big update --- .../NeuropixelNeuroblueprint.asv | 1256 ++++++++++++++++ .../NeuropixelNeuroblueprint.m | 1259 +++++++++++++++++ .../OpenEphys_Neuroblueprint.m | 585 ++++++-- .../Modules/@dispatcher/RunningSection.m | 6 +- ExperPort/Modules/@runrats/runrats.m | 19 +- .../ArpitSoundCatContinuous.m | 2 +- .../@ArpitSoundCatContinuous/SideSection.m | 2 +- .../StimulatorSection.m | 4 + .../StimulusSection.asv | 809 +++++++++++ .../StimulusSection.m | 494 ++++--- .../private/CreateSamples_from_Distribution.m | 885 ++++++++++-- .../private/StimuliDistribution_plot.m | 170 --- .../Rigtest_singletrial.m | 5 +- 13 files changed, 4946 insertions(+), 550 deletions(-) create mode 100644 ExperPort/Modules/@NeuropixelNeuroblueprint/NeuropixelNeuroblueprint.asv create mode 100644 ExperPort/Modules/@NeuropixelNeuroblueprint/NeuropixelNeuroblueprint.m create mode 100644 Protocols/@ArpitSoundCatContinuous/StimulusSection.asv delete mode 100644 Protocols/@ArpitSoundCatContinuous/private/StimuliDistribution_plot.m diff --git a/ExperPort/Modules/@NeuropixelNeuroblueprint/NeuropixelNeuroblueprint.asv b/ExperPort/Modules/@NeuropixelNeuroblueprint/NeuropixelNeuroblueprint.asv new file mode 100644 index 00000000..17fbcd94 --- /dev/null +++ b/ExperPort/Modules/@NeuropixelNeuroblueprint/NeuropixelNeuroblueprint.asv @@ -0,0 +1,1256 @@ +function [obj, varargout] = NeuropixelNeuroblueprint(varargin) +% NEUROPIXEL_NEUROBLUEPRINT_GUI - Integrated GUI for electrophysiology and behavior experiments +% +% This GUI manages experimental workflows for coordinated electrophysiology recording +% (using either Open Ephys or SpikeGLX) with behavioral protocols using Bpod/ExperPort. +% +% FEATURES: +% - Support for both Open Ephys and SpikeGLX recording systems +% - Neuropixels probe management (NP 1.0 and 2.0) +% - NeuroBlueprint data organization format +% - Automated session management and data saving +% - SVN integration for version control +% - Pre/post-session sampling across probe banks +% +% PRE-REQUISITES: +% 1. 'open-ephys-matlab-tools' must be in MATLAB path (for Open Ephys) +% 2. 'SpikeGLX-MATLAB-SDK' must be in MATLAB path (for SpikeGLX) +% 3. Bpod/ratter/ExperPort environment fully configured +% +% USAGE: +% gui_obj = NeuropixelNeuroblueprintGUI(); +% + +%% Boilerplate for class definition and action handling +obj = class(struct, mfilename); +varargout = {}; + + +% Display usage help when function is called directly +if nargin==0 || (nargin==1 && ischar(varargin{1}) && strcmp(varargin{1}, 'empty')) + display_usage_help(); + return; +end + +if isa(varargin{1}, mfilename) + if length(varargin) < 2 || ~ischar(varargin{2}) + error(['If called with a "%s" object as first arg, a second arg, a ' ... + 'string specifying the action, is required\n']); + else + action = varargin{2}; + varargin = varargin(3:end); + end +else + action = varargin{1}; + varargin = varargin(2:end); +end + +if ~ischar(action) + error('The action parameter must be a string'); +end + +GetSoloFunctionArgs(obj); + +%% Main Action Router +switch action + % ========================================================================= + % CASE INIT + % ========================================================================= + case 'init' + % So that only the CPU-based software renderer instead of your graphics card + % opengl software; + + % Start Bpod if not already running + if evalin('base', 'exist(''BpodSystem'', ''var'')') + if evalin('base', '~isempty(BpodSystem)'), newstartup; else, flush; end + else, Bpod('COM5');newstartup; + end + + % --- State Variables as SoloParamHandles --- + SoloParamHandle(obj, 'currentState', 'value', 'Load'); + SoloParamHandle(obj, 'behavState', 'value', 'Run'); + SoloParamHandle(obj, 'ephysState', 'value', 'Run'); + SoloParamHandle(obj, 'is_running', 'value', 0); + SoloParamHandle(obj, 'recording_software', 'value', 'OpenEphys'); % 'OpenEphys' or 'SpikeGLX' + SoloParamHandle(obj, 'recording_controller', 'value', []); % Will hold OE or SpikeGLX controller + SoloParamHandle(obj, 'behav_obj', 'value', []); + SoloParamHandle(obj, 'blinking_timer', 'value', []); + SoloParamHandle(obj, 'current_params', 'value', []); + SoloParamHandle(obj, 'session_base_path', 'value', ''); + SoloParamHandle(obj, 'probe_settings', 'value', struct('version', '2.0', 'reference', 'Tip', 'bank', 0, 'imro_path', '')); + SoloParamHandle(obj, 'probe_gui_handles', 'value', []); + + % Create stopping timer for behavior + scr = timer; + set(scr,'Period', 0.2,'ExecutionMode','FixedRate','TasksToExecute',Inf,... + 'BusyMode','drop','TimerFcn',[mfilename,'(''End_Continued'')']); + SoloParamHandle(obj, 'stopping_complete_timer', 'value', scr); + + % --- Create the GUI Figure --- + SoloParamHandle(obj, 'myfig', 'saveable', 0); + myfig.value = figure('Name', 'Neuropixels Recording & Behavior Controller',... + 'NumberTitle', 'off', 'MenuBar', 'none', ... + 'ToolBar', 'none', 'Units', 'normalized', ... + 'Position', [0.1, 0.1, 0.7, 0.85],... + 'Color', [0.94, 0.94, 0.94], ... + 'CloseRequestFcn', {@(h,e) feval(mfilename, obj, 'close')}); + + % --- UI Creation --- + handles = struct(); + + % Activity Log Panel + uipanel('Title', 'Activity Log', 'FontSize', 12, 'BorderType', 'etchedin', 'BorderWidth', 1, 'Units', 'normalized', 'Position', [0.64, 0.03, 0.34, 0.94]); + handles.log_box = uicontrol('Style', 'edit', 'Units', 'normalized', 'Position', [0.65, 0.05, 0.32, 0.89], 'String', {'Log started...'}, 'Max', 10, 'Min', 1, 'HorizontalAlignment', 'left', 'Enable', 'inactive', 'BackgroundColor', [1, 1, 1]); + + % Panel 0: Recording Software Selection + p0 = uipanel('Title', '0. Recording Software', 'FontSize', 12, 'FontWeight', 'bold', 'BorderType', 'etchedin', 'BorderWidth', 1, 'Units', 'normalized', 'Position', [0.02, 0.88, 0.6, 0.09]); + handles.software_group = uibuttongroup(p0, 'Title', '', 'BorderType', 'none', 'Units', 'normalized', 'Position', [0.05, 0.1, 0.9, 0.8], 'SelectionChangedFcn', {@(h,e) feval(mfilename, obj, 'recording_software_callback')}); + uicontrol(handles.software_group, 'Style', 'radiobutton', 'String', 'Open Ephys', 'Units', 'normalized', 'Position', [0.1, 0.3, 0.4, 0.4], 'Tag', 'OpenEphys', 'FontSize', 10); + uicontrol(handles.software_group, 'Style', 'radiobutton', 'String', 'SpikeGLX', 'Units', 'normalized', 'Position', [0.5, 0.3, 0.4, 0.4], 'Tag', 'SpikeGLX', 'FontSize', 10); + + % Panel 1: Behavior + p1 = uipanel('Title', '1. Behavior', 'FontSize', 12, 'FontWeight', 'bold', 'BorderType', 'etchedin', 'BorderWidth', 1, 'Units', 'normalized', 'Position', [0.02, 0.74, 0.6, 0.13]); + uicontrol(p1, 'Style', 'text', 'String', 'Protocol Name:', 'Units', 'normalized', 'Position', [0.05, 0.7, 0.22, 0.25], 'HorizontalAlignment', 'right'); + handles.protocol_edit = uicontrol(p1, 'Style', 'edit', 'String', 'ArpitSoundCatContinuous', 'Units', 'normalized', 'Position', [0.3, 0.7, 0.45, 0.25]); + handles.manual_test = uicontrol(p1, 'Style', 'checkbox', 'String', 'Manual Test', 'Value', 1, 'Units', 'normalized', 'Position', [0.78, 0.7, 0.2, 0.25]); + uicontrol(p1, 'Style', 'text', 'String', 'Experimenter:', 'Units', 'normalized', 'Position', [0.05, 0.4, 0.22, 0.25], 'HorizontalAlignment', 'right'); + handles.exp_edit = uicontrol(p1, 'Style', 'edit', 'String', 'lida', 'Units', 'normalized', 'Position', [0.3, 0.4, 0.65, 0.25], 'Callback', {@(h,e) feval(mfilename, obj, 'update_subject_id')}); + uicontrol(p1, 'Style', 'text', 'String', 'Rat Name:', 'Units', 'normalized', 'Position', [0.05, 0.1, 0.22, 0.25], 'HorizontalAlignment', 'right'); + handles.rat_name_edit = uicontrol(p1, 'Style', 'edit', 'String', 'LP12', 'Units', 'normalized', 'Position', [0.3, 0.1, 0.4, 0.25], 'Callback', {@(h,e) feval(mfilename, obj, 'update_subject_id')}); + uicontrol(p1, 'Style', 'text', 'String', 'Path:', 'Units', 'normalized', 'Position', [0.72, 0.1, 0.08, 0.25], 'HorizontalAlignment', 'right'); + handles.behav_edit = uicontrol(p1, 'Style', 'edit', 'String', 'C:\ratter', 'Units', 'normalized', 'Position', [0.81, 0.1, 0.18, 0.25]); + + % Panel 2: NeuroBlueprint Format + p2 = uipanel('Title', '2. NeuroBlueprint Format', 'FontSize', 12, 'FontWeight', 'bold', 'BorderType', 'etchedin', 'BorderWidth', 1, 'Units', 'normalized', 'Position', [0.02, 0.42, 0.6, 0.31]); + uicontrol(p2, 'Style', 'text', 'String', 'Project Name:', 'Units', 'normalized', 'Position', [0.01, 0.85, 0.28, 0.1], 'HorizontalAlignment', 'right'); + handles.proj_edit = uicontrol(p2, 'Style', 'edit', 'String', 'sound_cat_rat', 'Units', 'normalized', 'Position', [0.3, 0.85, 0.65, 0.12]); + uicontrol(p2, 'Style', 'text', 'String', 'Subject ID:', 'Units', 'normalized', 'Position', [0.01, 0.7, 0.28, 0.1], 'HorizontalAlignment', 'right'); + handles.sub_edit = uicontrol(p2, 'Style', 'edit', 'String', '000', 'Units', 'normalized', 'Position', [0.3, 0.7, 0.65, 0.12]); + uicontrol(p2, 'Style', 'text', 'String', 'Local Path:', 'Units', 'normalized', 'Position', [0.01, 0.55, 0.28, 0.1], 'HorizontalAlignment', 'right'); + handles.local_edit = uicontrol(p2, 'Style', 'edit', 'String', 'C:\Ephys_Experiment_Data', 'Units', 'normalized', 'Position', [0.3, 0.55, 0.5, 0.12]); + handles.local_browse = uicontrol(p2, 'Style', 'pushbutton', 'String', 'Browse...', 'Units', 'normalized', 'Position', [0.81, 0.55, 0.16, 0.13], 'Callback', {@(h,e) feval(mfilename, obj, 'browse_path', 'local')}); + uicontrol(p2, 'Style', 'text', 'String', 'Central Path:', 'Units', 'normalized', 'Position', [0.01, 0.4, 0.28, 0.1], 'HorizontalAlignment', 'right'); + handles.central_edit = uicontrol(p2, 'Style', 'edit', 'String', 'Z:\_projects', 'Units', 'normalized', 'Position', [0.3, 0.4, 0.5, 0.12]); + handles.central_browse = uicontrol(p2, 'Style', 'pushbutton', 'String', 'Browse...', 'Units', 'normalized', 'Position', [0.81, 0.4, 0.16, 0.13], 'Callback', {@(h,e) feval(mfilename, obj, 'browse_path', 'central')}); + uicontrol(p2, 'Style', 'text', 'String', 'Subfolders to Create:', 'Units', 'normalized', 'Position', [0.05, 0.2, 0.9, 0.1], 'HorizontalAlignment', 'left'); + handles.cb_ephys = uicontrol(p2, 'Style', 'checkbox', 'String', 'ephys', 'Value', 1, 'Units', 'normalized', 'Position', [0.05, 0.05, 0.2, 0.15]); + handles.cb_behav = uicontrol(p2, 'Style', 'checkbox', 'String', 'behav', 'Value', 1, 'Units', 'normalized', 'Position', [0.28, 0.05, 0.2, 0.15]); + handles.cb_anat = uicontrol(p2, 'Style', 'checkbox', 'String', 'anat', 'Value', 1, 'Units', 'normalized', 'Position', [0.51, 0.05, 0.2, 0.15]); + handles.cb_funcimg = uicontrol(p2, 'Style', 'checkbox', 'String', 'funcimg', 'Value', 0, 'Units', 'normalized', 'Position', [0.74, 0.05, 0.25, 0.15]); + + % Panel 3: Pre/Post-Experiment Sampling + p3 = uipanel('Title', '3. Pre/Post-Experiment Sampling', 'FontSize', 12, 'FontWeight', 'bold', 'BorderType', 'etchedin', 'BorderWidth', 1, 'Units', 'normalized', 'Position', [0.02, 0.28, 0.6, 0.12]); + uicontrol(p3, 'Style', 'text', 'String', 'Duration/Bank (s):', 'Units', 'normalized', 'Position', [0.01, 0.6, 0.25, 0.25], 'HorizontalAlignment', 'right'); + handles.sample_duration = uicontrol(p3, 'Style', 'edit', 'String', '60', 'Units', 'normalized', 'Position', [0.27, 0.6, 0.1, 0.3]); + handles.target_display = uicontrol(p3, 'Style', 'text', 'String', 'Target: Bank 0', 'Units', 'normalized', 'Position', [0.38, 0.6, 0.3, 0.25], 'HorizontalAlignment', 'right', 'FontWeight', 'bold'); + handles.probe_button = uicontrol(p3, 'Style', 'pushbutton', 'String', 'Probe Setting', 'Units', 'normalized', 'Position', [0.7, 0.55, 0.28, 0.4], 'FontSize', 10, 'Callback', {@(h,e) feval(mfilename, obj, 'open_probe_gui')}); + handles.sample_button = uicontrol(p3, 'Style', 'pushbutton', 'String', 'Start Sample Recording', 'Units', 'normalized', 'Position', [0.05, 0.1, 0.9, 0.4], 'FontSize', 12, 'FontWeight', 'bold', 'BackgroundColor', [0.8, 0.7, 1], 'Callback', {@(h,e) feval(mfilename, obj, 'sample_recording_wrapper')}); + + % Panel 4: Recording Settings + p4 = uipanel('Title', '4. Recording Settings', 'FontSize', 12, 'FontWeight', 'bold', 'BorderType', 'etchedin', 'BorderWidth', 1, 'Units', 'normalized', 'Position', [0.02, 0.15, 0.6, 0.11]); + handles.settings_panel = p4; + + % Open Ephys Settings (initially visible) + handles.oe_ip_label = uicontrol(p4, 'Style', 'text', 'String', 'GUI IP:', 'Units', 'normalized', 'Position', [0.01, 0.3, 0.15, 0.4], 'HorizontalAlignment', 'right'); + handles.oe_ip_edit = uicontrol(p4, 'Style', 'edit', 'String', '127.0.0.1', 'Units', 'normalized', 'Position', [0.17, 0.25, 0.15, 0.5]); + handles.oe_proc_label = uicontrol(p4, 'Style', 'text', 'String', 'Proc ID:', 'Units', 'normalized', 'Position', [0.33, 0.3, 0.15, 0.4], 'HorizontalAlignment', 'right'); + handles.oe_proc_edit = uicontrol(p4, 'Style', 'edit', 'String', '100', 'Units', 'normalized', 'Position', [0.49, 0.25, 0.15, 0.5]); + handles.oe_rec_label = uicontrol(p4, 'Style', 'text', 'String', 'Rec ID:', 'Units', 'normalized', 'Position', [0.65, 0.3, 0.15, 0.4], 'HorizontalAlignment', 'right'); + handles.oe_rec_edit = uicontrol(p4, 'Style', 'edit', 'String', '101', 'Units', 'normalized', 'Position', [0.81, 0.25, 0.15, 0.5]); + + % SpikeGLX Settings (initially hidden) + handles.sglx_host_label = uicontrol(p4, 'Style', 'text', 'String', 'Host IP:', 'Units', 'normalized', 'Position', [0.01, 0.3, 0.15, 0.4], 'HorizontalAlignment', 'right', 'Visible', 'off'); + handles.sglx_host_edit = uicontrol(p4, 'Style', 'edit', 'String', 'localhost', 'Units', 'normalized', 'Position', [0.17, 0.25, 0.15, 0.5], 'Visible', 'off'); + handles.sglx_port_label = uicontrol(p4, 'Style', 'text', 'String', 'Port:', 'Units', 'normalized', 'Position', [0.33, 0.3, 0.15, 0.4], 'HorizontalAlignment', 'right', 'Visible', 'off'); + handles.sglx_port_edit = uicontrol(p4, 'Style', 'edit', 'String', '4142', 'Units', 'normalized', 'Position', [0.49, 0.25, 0.15, 0.5], 'Visible', 'off'); + handles.sglx_probe_label = uicontrol(p4, 'Style', 'text', 'String', 'Probe Idx:', 'Units', 'normalized', 'Position', [0.65, 0.3, 0.15, 0.4], 'HorizontalAlignment', 'right', 'Visible', 'off'); + handles.sglx_probe_edit = uicontrol(p4, 'Style', 'edit', 'String', '0', 'Units', 'normalized', 'Position', [0.81, 0.25, 0.15, 0.5], 'Visible', 'off'); + + % --- Control Buttons Panel --- + p5 = uipanel('Title', 'Controls', 'FontSize', 12, 'FontWeight', 'bold', 'BorderType', 'etchedin', 'BorderWidth', 1, 'Units', 'normalized', 'Position', [0.02, 0.02, 0.6, 0.11]); + handles.control_button = uicontrol(p5, 'Style', 'pushbutton', 'String', 'Load', 'Units', 'normalized', 'FontSize', 14, 'FontWeight', 'bold', 'Position', [0.02, 0.1, 0.3, 0.8], 'BackgroundColor', [0.2, 0.6, 0.8], 'Callback', {@(h,e) feval(mfilename, obj, 'main_control_callback')}); + handles.behav_button = uicontrol(p5, 'Style', 'pushbutton', 'String', 'Run Behav', 'Units', 'normalized', 'FontSize', 12, 'Position', [0.35, 0.1, 0.3, 0.8], 'BackgroundColor', [1, 0.8, 0.6], 'Callback', {@(h,e) feval(mfilename, obj, 'behav_control_callback')}); + handles.ephys_button = uicontrol(p5, 'Style', 'pushbutton', 'String', 'Run Ephys', 'Units', 'normalized', 'FontSize', 12, 'Position', [0.68, 0.1, 0.3, 0.8], 'BackgroundColor', [0.8, 0.6, 1], 'Callback', {@(h,e) feval(mfilename, obj, 'ephys_control_callback')}); + + SoloParamHandle(obj, 'ui_handles', 'value', handles); + + feval(mfilename, obj, 'update_subject_id'); + log_message(handles, 'GUI initialization complete.'); + + % ========================================================================= + % CASE MAIN_CONTROL_CALLBACK + % ========================================================================= + case 'main_control_callback' + switch value(currentState) + case 'Load', feval(mfilename, obj, 'load_sequence'); + case 'Run', feval(mfilename, obj, 'run_sequence'); + case 'Stop', feval(mfilename, obj, 'stop_sequence'); + case 'PostExperiment', feval(mfilename, obj, 'reset_to_load_state'); + end + + % ========================================================================= + % CASE RECORDING_SOFTWARE_CALLBACK + % ========================================================================= + case 'recording_software_callback' + handles = value(ui_handles); + selected_software = get(get(handles.software_group, 'SelectedObject'), 'Tag'); + recording_software.value = selected_software; + + if strcmp(selected_software, 'OpenEphys') + % Show OpenEphys settings, hide SpikeGLX + set([handles.oe_ip_label, handles.oe_ip_edit, handles.oe_proc_label, handles.oe_proc_edit, handles.oe_rec_label, handles.oe_rec_edit], 'Visible', 'on'); + set([handles.sglx_host_label, handles.sglx_host_edit, handles.sglx_port_label, handles.sglx_port_edit, handles.sglx_probe_label, handles.sglx_probe_edit], 'Visible', 'off'); + log_message(handles, 'Switched to Open Ephys recording mode.'); + else % SpikeGLX + % Hide OpenEphys settings, show SpikeGLX + set([handles.oe_ip_label, handles.oe_ip_edit, handles.oe_proc_label, handles.oe_proc_edit, handles.oe_rec_label, handles.oe_rec_edit], 'Visible', 'off'); + set([handles.sglx_host_label, handles.sglx_host_edit, handles.sglx_port_label, handles.sglx_port_edit, handles.sglx_probe_label, handles.sglx_probe_edit], 'Visible', 'on'); + log_message(handles, 'Switched to SpikeGLX recording mode.'); + end + + % ========================================================================= + % WORKFLOW ACTIONS + % ========================================================================= + case 'load_sequence' + handles = value(ui_handles); + log_message(handles, '--- LOAD sequence initiated ---'); + set(handles.control_button, 'Enable', 'off', 'String', 'Loading...'); + set(handles.sample_button, 'Enable', 'off'); + + try + software = value(recording_software); + params = get_all_parameters(handles,software); + if ~validate_all_inputs(params,handles,software) + feval(mfilename, obj, 'reset_to_load_state'); + return; + end + current_params.value = params; + + [session_path, recording_save_path] = construct_session_paths(handles, params); + if isempty(session_path) || ~create_session_directories(handles, params, session_path) + feval(mfilename, obj, 'reset_to_load_state'); + return; + end + session_base_path.value = session_path; + + % Initialize the Behavior system + feval(mfilename, obj, 'initialize_behavior_system'); + + catch ME + log_message(handles, sprintf('ERROR during load sequence: %s', ME.message)); + feval(mfilename, obj, 'reset_to_load_state'); + rethrow(ME); + end + + case 'run_sequence' + handles = value(ui_handles); + params = value(current_params); + log_message(handles, '--- RUN sequence initiated ---'); + set(handles.sample_button, 'Enable', 'off'); + + try + if get(handles.cb_ephys, 'Value') + % Initialize the selected recording system + recording_save_path = fullfile(params.local_path, value(session_base_path), 'ephys'); + feval(mfilename, obj, 'initialize_recording_system', params, recording_save_path); + if isempty(value(recording_controller)) + feval(mfilename, obj, 'reset_to_load_state'); + return; + end + % Start Ephys Recording + feval(mfilename, obj, 'start_electrophysiology_recording', params); + else + log_message(handles, 'Ephys checkbox not selected. No recording started.'); + end + + currentState.value = 'Stop'; + set(handles.control_button, 'String', 'Stop'); + feval(mfilename, obj, 'start_blinking'); + + feval(mfilename, obj, 'start_behavioral_protocol', params); + log_message(handles, '--- RUN sequence complete. Experiment is live. ---'); + + catch ME + log_message(handles, sprintf('ERROR during run sequence: %s', ME.message)); + feval(mfilename, obj, 'stop_blinking'); + rethrow(ME); + end + + case 'stop_sequence' + handles = value(ui_handles); + params = value(current_params); + log_message(handles, '--- STOP sequence initiated ---'); + feval(mfilename, obj, 'stop_blinking'); + + try + behav_save_dir = fullfile(params.local_path, value(session_base_path), 'behav'); + feval(mfilename, obj, 'stop_behavioral_protocol', params, behav_save_dir); + + if get(handles.cb_ephys, 'Value') && ~isempty(value(recording_controller)) + feval(mfilename, obj, 'stop_electrophysiology_recording'); + end + + log_message(handles, '--- Experiment finished. Post-session sampling available. ---'); + currentState.value = 'PostExperiment'; + set(handles.control_button, 'String', 'Start New Experiment', 'BackgroundColor', [0.2, 0.8, 0.6]); + set(handles.sample_button, 'Enable', 'on'); + + catch ME + log_message(handles, sprintf('ERROR during stop sequence: %s', ME.message)); + rethrow(ME); + end + + % ========================================================================= + % SYSTEM INITIALIZATION & CONTROL + % ========================================================================= + case 'initialize_recording_system' + params = varargin{1}; + save_path = varargin{2}; + software = value(recording_software); + handles = value(ui_handles); + + try + if strcmp(software, 'OpenEphys') + log_message(handles, 'Initializing Open Ephys controller...'); + controller = OpenEphysHTTPServer(params.oe_gui_ip, 37497); + if isempty(controller), error('Failed to create Open Ephys controller'); end + else % SpikeGLX + log_message(handles, 'Initializing SpikeGLX controller...'); + controller = SpikeGL(params.sglx_host_ip, params.sglx_port); + if ~controller.IsConnected(), error('Failed to connect to SpikeGLX'); end + end + recording_controller.value = controller; + log_message(handles, sprintf('%s controller initialized successfully.', software)); + + % Set initial recording path + feval(mfilename, obj, 'set_recording_path', save_path); + + catch ME + log_message(handles, sprintf('Failed to initialize %s: %s', software, ME.message)); + recording_controller.value = []; + rethrow(ME); + end + + case 'set_recording_path' + save_path = varargin{1}; + software = value(recording_software); + controller = value(recording_controller); + params = value(current_params); + handles = value(ui_handles); + + if isempty(controller), return; end + + try + if strcmp(software, 'OpenEphys') + controller.setRecordPath(params.oe_rec_node_id, save_path); + else % SpikeGLX + controller.SetDataDir(save_path); + end + log_message(handles, sprintf('Recording path set to: %s', save_path)); + catch ME + log_message(handles, sprintf('Failed to set recording path: %s', ME.message)); + rethrow(ME); + end + + case 'start_electrophysiology_recording' + params = varargin{1}; + software = value(recording_software); + controller = value(recording_controller); + handles = value(ui_handles); + + if isempty(controller), error('Recording controller not initialized'); end + + try + probe_settings_struct = value(probe_settings); + feval(mfilename, obj, 'apply_probe_configuration', probe_settings_struct); + + main_ephys_path = fullfile(params.local_path, value(session_base_path), 'ephys'); + feval(mfilename, obj, 'set_recording_path', main_ephys_path); + + if strcmp(software, 'OpenEphys') + log_message(handles, 'Starting Open Ephys acquisition and recording...'); + controller.acquire(); pause(1); + controller.record(); + else % SpikeGLX + log_message(handles, 'Starting SpikeGLX recording...'); + run_name = sprintf('experiment_%s', datestr(now, 'yyyymmdd_HHMMSS')); + controller.SetRunName(run_name); + controller.StartRun(); + end + + log_message(handles, 'Electrophysiology recording is LIVE.'); + ephysState.value = 'Stop'; + set(handles.ephys_button, 'String', 'Stop Ephys', 'BackgroundColor', [1 0.6 0.6]); + + catch ME + log_message(handles, sprintf('Failed to start recording: %s', ME.message)); + rethrow(ME); + end + + case 'stop_electrophysiology_recording' + software = value(recording_software); + controller = value(recording_controller); + handles = value(ui_handles); + if isempty(controller), return; end + + try + if strcmp(software, 'OpenEphys') + log_message(handles, 'Stopping Open Ephys recording...'); + controller.idle(); + else % SpikeGLX + log_message(handles, 'Stopping SpikeGLX recording...'); + controller.StopRun(); + end + + log_message(handles, 'Electrophysiology recording stopped.'); + ephysState.value = 'Run'; + set(handles.ephys_button, 'String', 'Run Ephys', 'BackgroundColor', [0.8, 0.6, 1]); + + catch ME + log_message(handles, sprintf('Failed to stop recording: %s', ME.message)); + rethrow(ME); + end + + case 'initialize_behavior_system' + params = value(current_params); + handles = value(ui_handles); + try + log_message(handles, 'Initializing behavior control system...'); + behav_obj.value = dispatcher('init'); + h=get_sphandle('owner','dispatcher','name','myfig'); + set(value(h{1}), 'Visible','Off'); + if params.do_manual_test + feval(mfilename, obj, 'behav_control', 'manual_test'); + else + feval(mfilename, obj, 'continue_load_after_manual_test'); + end + catch ME + log_message(handles, ['FATAL ERROR initializing behavior system: ' ME.message]); + errordlg(['Failed to initialize behavior system. Check path and logs. Error: ' ME.message], 'Behavior System Error'); + rethrow(ME); + end + + case 'start_behavioral_protocol' + params = varargin{1}; + handles = value(ui_handles); + try + log_message(handles, 'Starting behavioral protocol...'); + feval(mfilename, obj, 'behav_control', 'run', params.protocol_name); + log_message(handles, 'Behavioral protocol is LIVE.'); + catch ME + log_message(handles, ['FATAL ERROR starting behavior protocol: ' ME.message]); + errordlg(['Failed to start behavior protocol. Check logs. Error: ' ME.message], 'Behavior System Error'); + rethrow(ME); + end + + case 'stop_behavioral_protocol' + params = varargin{1}; + behav_save_dir = varargin{2}; + handles = value(ui_handles); + try + log_message(handles, 'Ending behavioral session (saving data)...'); + feval(mfilename, obj, 'behav_control', 'end', params.protocol_name, params.behav_path, behav_save_dir); + log_message(handles, 'Behavioral data saved successfully.'); + catch ME + log_message(handles, ['FATAL ERROR ending behavioral session: ' ME.message]); + errordlg(['Failed to save behavioral data. Check logs. Error: ' ME.message], 'Behavior System Error'); + rethrow(ME); + end + + case 'manual_test_stopping' + + handles = value(ui_handles); + log_message(handles, 'Manual rig test complete. Cleaning up...'); + dispatcher(value(behav_obj), 'Stop'); + + %Let's pause until we know dispatcher is done running + set(value(stopping_complete_timer), 'TimerFcn', {@(h,e) feval(mfilename, obj, 'manual_test_stopped')}); + start(value(stopping_complete_timer)); + + case 'manual_test_stopped' + + if value(stopping_process_completed) %This is provided by RunningSection + stop(value(stopping_complete_timer)); %Stop looping. + dispatcher('set_protocol', ''); + is_running.value = 0; + feval(mfilename, obj, 'continue_load_after_manual_test'); + end + + case 'continue_load_after_manual_test' + params = value(current_params); + handles = value(ui_handles); + video_save_dir = fullfile(params.local_path, value(session_base_path), 'behav'); + try + log_message(handles, 'Loading main behavioral protocol...'); + feval(mfilename, obj, 'behav_control', 'load_main_protocol', params.experimenter, params.rat_name, params.protocol_name, video_save_dir, params.behav_path); + log_message(handles, 'Behavior system loaded and ready.'); + log_message(handles, '--- LOAD sequence complete. Ready to run. ---'); + currentState.value = 'Run'; + set(handles.control_button, 'Enable', 'on', 'String', 'Run', 'BackgroundColor', [0.4, 0.8, 0.4]); + set(handles.sample_button, 'Enable', 'on'); + catch ME + log_message(handles, ['FATAL ERROR loading main protocol: ' ME.message]); + errordlg(['Failed to load main protocol. Error: ' ME.message], 'Behavior System Error'); + feval(mfilename, obj, 'reset_to_load_state'); + end + + % ========================================================================= + % INDIVIDUAL CONTROL CALLBACKS + % ========================================================================= + case 'behav_control_callback' + switch value(behavState) + case 'Run', feval(mfilename, obj, 'behav_control', 'load_run'); + case 'Stop', feval(mfilename, obj, 'behav_control', 'end'); + end + + case 'ephys_control_callback' + switch value(ephysState) + case 'Run', feval(mfilename, obj, 'run_ephys_individually'); + case 'Stop', feval(mfilename, obj, 'stop_ephys_individually'); + end + + case 'run_ephys_individually' + params = value(current_params); + handles = value(ui_handles); + log_message(handles, '--- Starting Ephys Recording Individually ---'); + set(handles.ephys_button, 'Enable', 'off'); + try + if isempty(value(recording_controller)) + feval(mfilename, obj, 'initialize_recording_system', params, ''); + end + feval(mfilename, obj, 'start_electrophysiology_recording', params); + catch ME + log_message(handles, sprintf('ERROR starting ephys: %s', ME.message)); + end + set(handles.ephys_button, 'Enable', 'on'); + + case 'stop_ephys_individually' + handles = value(ui_handles); + log_message(handles, '--- Stopping Ephys Recording Individually ---'); + set(handles.ephys_button, 'Enable', 'off'); + try + feval(mfilename, obj, 'stop_electrophysiology_recording'); + catch ME + log_message(handles, sprintf('ERROR stopping ephys: %s', ME.message)); + end + set(handles.ephys_button, 'Enable', 'on'); + + % ========================================================================= + % BEHAVIOR CONTROL ACTIONS + % ========================================================================= + case 'behav_control' + sub_action = varargin{1}; + args = varargin(2:end); + handles = value(ui_handles); + + switch sub_action + case 'load_main_protocol' + experimenter = args{1}; ratname = args{2}; protocol_name = args{3}; + video_save_dir = args{4}; behav_path = args{5}; + log_message(handles, ['Loading protocol: ' protocol_name]); + dispatcher('set_protocol', protocol_name); + rath = get_sphandle('name', 'ratname', 'owner', protocol_name); + exph = get_sphandle('name', 'experimenter', 'owner', protocol_name); + rath{1}.value = ratname; exph{1}.value = experimenter; + protobj = eval(protocol_name); + log_message(handles, ['Loading settings for ' ratname]); + [~, sfile] = load_solouiparamvalues(ratname, 'experimenter', experimenter, 'owner', class(protobj), 'interactive', 0); + feval(protocol_name, protobj, 'set_setting_params', ratname, experimenter, sfile, char(datetime('now')), video_save_dir); + if ~dispatcher('is_running'), pop_history(class(protobj), 'include_non_gui', 1); feval(protocol_name, protobj, 'prepare_next_trial'); end + + case 'crashed' + log_message(handles, '--- BEHAVIOR CRASH RECOVERY INITIATED ---'); + params = value(current_params); + video_save_dir = fullfile(params.local_path, value(session_base_path), 'behav'); + feval(mfilename, obj, 'behav_control', 'load_protocol_after_crash', params.experimenter, params.rat_name, params.protocol_name, video_save_dir, params.behav_path); + feval(mfilename, obj, 'behav_control', 'run', params.protocol_name); + log_message(handles, '--- RECOVERY COMPLETE: Behavior protocol restarted ---'); + + case 'load_protocol_after_crash' + experimenter = args{1}; ratname = args{2}; protocol_name = args{3}; + video_save_dir = args{4}; behav_path = args{5}; + log_message(handles, ['Loading protocol after crash: ' protocol_name]); + dispatcher('set_protocol', protocol_name); + rath = get_sphandle('name', 'ratname', 'owner', protocol_name); + exph = get_sphandle('name', 'experimenter', 'owner', protocol_name); + rath{1}.value = ratname; exph{1}.value = experimenter; + protobj = eval(protocol_name); + try + log_message(handles, ['Loading previous data for ' ratname]); + today_date = char(datetime('now','format','yyMMdd')); + temp_data_dir = fullfile(behav_path,'SoloData','Data',experimenter,ratname); + temp_data_file = sprintf('data_@%s_%s_%s_%s_ASV.mat',protocol_name,experimenter,ratname,today_date); + if isfile(fullfile(temp_data_dir,temp_data_file)) + dispatcher('runstart_disable'); + load_soloparamvalues(ratname, 'experimenter', experimenter, 'owner', protocol_name, 'interactive', 0,'data_file',fullfile(temp_data_dir,temp_data_file)); + dispatcher('runstart_enable'); + end + if ~dispatcher('is_running'), pop_history(class(protobj), 'include_non_gui', 1); feval(protocol_name, protobj, 'prepare_next_trial'); end + catch + log_message(handles, ['Loading settings for ' ratname]); + [~, sfile] = load_solouiparamvalues(ratname, 'experimenter', experimenter, 'owner', class(protobj), 'interactive', 0); + feval(protocol_name, protobj, 'set_setting_params', ratname, experimenter, sfile, char(datetime('now')), video_save_dir); + if ~dispatcher('is_running'), pop_history(class(protobj), 'include_non_gui', 1); feval(protocol_name, protobj, 'prepare_next_trial'); end + end + + case 'load_run' + set(handles.behav_button, 'Enable', 'off'); + log_message(handles, '--- STARTING BEHAV PROTOCOL ---'); + params = value(current_params); + video_save_dir = fullfile(params.local_path, value(session_base_path), 'behav'); + feval(mfilename, obj, 'behav_control', 'load_protocol_after_crash', params.experimenter, params.rat_name, params.protocol_name, video_save_dir, params.behav_path); + feval(mfilename, obj, 'behav_control', 'run', params.protocol_name); + log_message(handles, '--- START COMPLETE: Behavior protocol started ---'); + set(handles.behav_button, 'Enable', 'on'); + + case 'run' + protocol_name = args{1}; protobj = eval(protocol_name); + log_message(handles, 'Starting video recording via protocol...'); + feval(protocol_name, protobj, 'start_recording'); + log_message(handles, 'Starting dispatcher to run trials...'); + is_running.value = 1; + behavState.value = 'Stop'; + set(handles.behav_button, 'String', 'Stop Behav', 'BackgroundColor', [1 0.6 0.6]); + dispatcher(value(behav_obj), 'Run'); + + case 'end' + set(handles.behav_button, 'Enable', 'off'); + if length(args) >= 3 + protocol_name = args{1}; root_dir = args{2}; behav_copy_dir = args{3}; + else + params = value(current_params); + protocol_name = params.protocol_name; + root_dir = params.behav_path; + behav_copy_dir = fullfile(params.local_path, value(session_base_path), 'behav'); + end + log_message(handles, 'Stopping dispatcher...'); + dispatcher(value(behav_obj), 'Stop'); + set(value(stopping_complete_timer), 'Period', 0.8,'TimerFcn', {@(h,e) feval(mfilename, obj, 'behav_control','end_continued',protocol_name, root_dir, behav_copy_dir)}); + start(value(stopping_complete_timer)); + + case 'end_continued' + if value(stopping_process_completed) % This is provided by RunningSection + protocol_name = args{1}; root_dir = args{2}; destination_path = args{3}; + stop(value(stopping_complete_timer)); %Stop looping. + is_running.value = 0; + feval(mfilename, obj, 'behav_control', 'send_empty_state_machine'); + protobj = eval(protocol_name); + log_message(handles, 'Ending session via protocol...'); + feval(protocol_name, protobj, 'end_session'); + log_message(handles, 'Saving data and settings...'); + data_file = SavingSection(protobj, 'savedata', 'interactive', 0); + try + feval(protocol_name, protobj, 'pre_saving_settings'); + catch + log_message(handles, 'Protocol does not have a pre_saving_settings section.'); + end + [settings_file, ~] = SavingSection(protobj, 'get_set_filename'); + SavingSection(protobj, 'savesets', 'interactive', 0); + log_message(handles, 'Committing data and settings to SVN...'); + commit_to_svn(handles, data_file, settings_file, root_dir); + dispatcher('set_protocol', ''); + data_file = [data_file '.mat']; + [status, msg] = copyfile(data_file, destination_path); + if status, log_message(handles,'Data File copied successfully.'); + else, log_message(handles,['Error copying Data file: ' msg]); + end + behavState.value = 'Run'; + set(handles.behav_button, 'String', 'Run Behav', 'BackgroundColor', [1, 0.8, 0.6]); + feval(mfilename, obj, 'save_log_file'); + set(handles.behav_button, 'Enable', 'on'); + end + + case 'manual_test' + log_message(handles, 'Loading manual rig test protocol...'); + dispatcher('set_protocol', 'Rigtest_singletrial'); + h=get_sphandle('owner','Rigtest_singletrial','name', 'myfig'); + for i=1:numel(h); set(value(h{i}),'Visible','Off'); end + is_running.value = 1; + log_message(handles, 'Starting manual rig test. Please complete the one-trial test.'); + dispatcher(value(behav_obj), 'Run'); + + case 'create_svn_data_dir' + experimenter = args{1}; ratname = args{2}; behav_dir = args{3}; dir_name = args{4}; + dirCurrent = cd; + settings_path = fullfile(behav_dir, 'SoloData', dir_name); + exp_path = fullfile(settings_path, experimenter); + rat_path = fullfile(exp_path, ratname); + if ~isfolder(settings_path), mkdir(settings_path); system(['svn add ' dir_name]); end + if ~isfolder(exp_path), cd(settings_path); mkdir(experimenter); system(['svn add ' experimenter]); end + if ~isfolder(rat_path), cd(exp_path); mkdir(ratname); system(['svn add ' ratname]); end + cd(dirCurrent); + log_message(handles, ['Created SVN directory structure for ' ratname]); + + case 'send_empty_state_machine' + state_machine_server = bSettings('get', 'RIGS', 'state_machine_server'); + server_slot = bSettings('get', 'RIGS', 'server_slot'); if isnan(server_slot), server_slot = 0; end + card_slot = bSettings('get', 'RIGS', 'card_slot'); if isnan(card_slot), card_slot = 0; end + sm = BpodSM(state_machine_server, 3333, server_slot); sm = Initialize(sm); + [inL, outL] = MachinesSection(dispatcher, 'determine_io_maps'); + sma = StateMachineAssembler('full_trial_structure'); + sma = add_state(sma, 'name', 'vapid_state_in_vapid_matrix'); + send(sma, sm, 'run_trial_asap', 0, 'input_lines', inL, 'dout_lines', outL, 'sound_card_slot', int2str(card_slot)); + + end + + case 'crash_detected' + % handles = feval(mfilename, obj, 'get_ui_handles'); + handles = value(ui_handles); + if ~strcmp(value(currentState), 'Stop') || isempty(value(behav_obj)), return; end + + log_message(handles, '!!! CRASH DETECTED: Behavior system is not running. Attempting recovery...'); + try + feval(mfilename, obj, 'behav_control', 'crashed'); + + catch ME + log_message(handles, sprintf('FATAL: Recovery attempt failed: %s', ME.message)); + getReport(ME, 'extended', 'hyperlinks', 'on'); + errordlg('Automatic recovery failed. Please stop the experiment manually.', 'Recovery Failed'); + end + % ========================================================================= + % SAMPLE EPHYS RECORDINGS + % ========================================================================= + case 'sample_recording_wrapper' + if strcmp(value(currentState), 'PostExperiment') + feval(mfilename, obj, 'sample_recording', 'post_session'); + else + feval(mfilename, obj, 'sample_recording', 'pre_session'); + end + + case 'sample_recording' + prefix = varargin{1}; + handles = value(ui_handles); + log_message(handles, ['--- ' upper(prefix) ' SAMPLE RECORDING INITIATED ---']); + set([handles.sample_button, handles.control_button], 'Enable', 'off', 'String', 'Sampling...'); + drawnow; + + try + software = value(recording_software); + params = get_all_parameters(handles,software); + if isempty(value(recording_controller)) + feval(mfilename, obj, 'initialize_recording_system', params, ''); + end + + if isempty(value(session_base_path)) + [session_path, ~] = construct_session_paths(handles, params); + if isempty(session_path) || ~create_session_directories(handles, params, session_path) + error('Failed to create session directories'); + end + session_base_path.value = session_path; + end + + sample_dir_name = sprintf('%s_sample_recording', prefix); + sample_save_path = fullfile(params.local_path, value(session_base_path), 'ephys', sample_dir_name); + if ~exist(sample_save_path, 'dir'), mkdir(sample_save_path); end + + software = value(recording_software); + if strcmp(software, 'OpenEphys') + feval(mfilename, obj, 'execute_openephys_sampling', sample_save_path); + else + feval(mfilename, obj, 'execute_spikeglx_sampling', sample_save_path); + end + log_message(handles, '--- SAMPLE RECORDING COMPLETE ---'); + + catch ME + log_message(handles, sprintf('ERROR during sample recording: %s', ME.message)); + rethrow(ME); + end + + % Reset button states + if strcmp(value(currentState), 'PostExperiment') + set(handles.control_button, 'Enable', 'on', 'String', 'Start New Experiment'); + feval(mfilename, obj, 'save_log_file'); + else + set(handles.control_button, 'Enable', 'on', 'String', 'Load'); + end + set(handles.sample_button, 'Enable', 'on', 'String', 'Start Sample Recording'); + + case 'execute_openephys_sampling' + save_path = varargin{1}; + controller = value(recording_controller); + params = value(current_params); + handles = value(ui_handles); + probe_settings_struct = value(probe_settings); + duration = str2double(get(handles.sample_duration, 'String')); + + controller.setParameters(params.oe_proc_node_id, 0, 'Reference', probe_settings_struct.reference); + if strcmp(probe_settings_struct.version, '1.0'), num_banks = 3; else, num_banks = 4; end + + for bank = 0:(num_banks - 1) + log_message(handles, sprintf('Recording OE Bank %d for %d seconds...', bank, duration)); + controller.setParameters(params.oe_proc_node_id, 0, 'bank', bank); pause(1); + controller.setRecordPath(params.oe_rec_node_id, save_path); pause(1); + controller.acquire(duration); pause(1); + controller.record(duration); + controller.idle(); + log_message(handles, sprintf('Finished recording Bank %d.', bank)); pause(1); + end + + if ~isempty(probe_settings_struct.imro_path) + controller.config(params.oe_proc_node_id, ['LOADIMRO ' probe_settings_struct.imro_path]); + else + controller.setParameters(params.oe_proc_node_id, 0, 'bank', probe_settings_struct.bank); + end + + case 'execute_spikeglx_sampling' + save_path = varargin{1}; + controller = value(recording_controller); + handles = value(ui_handles); + probe_settings_struct = value(probe_settings); + duration = str2double(get(handles.sample_duration, 'String')); + + controller.SetDataDir(save_path); + if strcmp(probe_settings_struct.version, '1.0'), num_banks = 3; else, num_banks = 4; end + + for bank = 0:(num_banks - 1) + log_message(handles, sprintf('Recording SGLX Bank %d for %d seconds...', bank, duration)); + % Bank selection for SpikeGLX depends on specific API calls for channel selection, not a simple 'bank' parameter + % This is a placeholder for more complex channel/bank setting logic. + % For now, we record with the currently active map. + + run_name = sprintf('sample_bank_%d_%s', bank, datestr(now, 'yyyymmdd_HHMMSS')); + controller.SetRunName(run_name); + controller.StartRun(); + pause(duration); + controller.StopRun(); + log_message(handles, sprintf('Finished recording Bank %d.', bank)); pause(1); + end + + % ========================================================================= + % PROBE GUI AND SETTINGS + % ========================================================================= + case 'open_probe_gui' + handles = value(ui_handles); + log_message(handles, 'Opening probe settings GUI...'); + probe_fig = figure('Name', 'Neuropixel Probe Settings', 'Position', [300 300 450 300], ... + 'MenuBar', 'none', 'ToolBar', 'none', 'NumberTitle', 'off', 'Resize', 'off'); + p_handles = struct(); + p_handles.version_group = uibuttongroup(probe_fig, 'Title', 'Probe Version', 'Position', [0.05 0.75 0.9 0.2]); + uicontrol(p_handles.version_group, 'Style', 'radiobutton', 'String', 'NP 1.0 (3 Banks)', 'Position', [10 5 150 25], 'Tag', '1.0'); + uicontrol(p_handles.version_group, 'Style', 'radiobutton', 'String', 'NP 2.0 (4 Banks)', 'Position', [200 5 150 25], 'Tag', '2.0'); + p_handles.ref_group = uibuttongroup(probe_fig, 'Title', 'Reference', 'Position', [0.05 0.5 0.4 0.2]); + uicontrol(p_handles.ref_group, 'Style', 'radiobutton', 'String', 'Tip', 'Position', [10 5 80 25], 'Tag', 'Tip'); + uicontrol(p_handles.ref_group, 'Style', 'radiobutton', 'String', 'External', 'Position', [100 5 80 25], 'Tag', 'External'); + p_handles.bank_panel = uipanel(probe_fig, 'Title', 'Target Bank', 'Position', [0.5 0.5 0.45 0.2]); + uicontrol(p_handles.bank_panel, 'Style', 'text', 'String', 'Bank:', 'Position', [10 5 40 20]); + p_handles.bank_edit = uicontrol(p_handles.bank_panel, 'Style', 'edit', 'String', '0', 'Position', [60 5 50 25]); + uicontrol(probe_fig, 'Style', 'text', 'String', 'IMRO File:', 'Position', [20 120 60 20]); + p_handles.imro_text = uicontrol(probe_fig, 'Style', 'text', 'String', 'None selected', 'Position', [90 120 280 20], 'HorizontalAlignment', 'left'); + uicontrol(probe_fig, 'Style', 'pushbutton', 'String', 'Browse...', 'Position', [20 85 100 30], 'Callback', {@(h,e) feval(mfilename, obj, 'browse_imro', p_handles)}); + uicontrol(probe_fig, 'Style', 'pushbutton', 'String', 'Clear IMRO', 'Position', [130 85 100 30], 'Callback', {@(h,e) feval(mfilename, obj, 'clear_imro', p_handles)}); + uicontrol(probe_fig, 'Style', 'pushbutton', 'String', 'Apply & Close', 'Position', [250 25 180 30], 'FontWeight', 'bold', 'Callback', {@(h,e) feval(mfilename, obj, 'apply_probe_settings', p_handles)}); + probe_gui_handles.value = p_handles; + + case 'browse_imro' + p_handles = varargin{1}; + [file, path] = uigetfile('*.imro', 'Select IMRO File'); + if isequal(file, 0) || isequal(path, 0), return; + else + full_path = fullfile(path, file); + set(p_handles.imro_text, 'String', full_path); + set(findobj(p_handles.bank_panel, '-property', 'Enable'), 'Enable', 'off'); + end + + case 'clear_imro' + p_handles = varargin{1}; + set(p_handles.imro_text, 'String', 'None selected'); + set(findobj(p_handles.bank_panel, '-property', 'Enable'), 'Enable', 'on'); + + case 'apply_probe_settings' + p_handles = varargin{1}; + handles = value(ui_handles); + settings.version = get(get(p_handles.version_group, 'SelectedObject'), 'Tag'); + settings.reference = get(get(p_handles.ref_group, 'SelectedObject'), 'Tag'); + settings.bank = str2double(get(p_handles.bank_edit, 'String')); + settings.imro_path = get(p_handles.imro_text, 'String'); + if strcmp(settings.imro_path, 'None selected'), settings.imro_path = ''; end + probe_settings.value = settings; + if ~isempty(settings.imro_path) + set(handles.target_display, 'String', 'Target: IMRO File'); + else + set(handles.target_display, 'String', ['Target: Bank ' num2str(settings.bank)]); + end + log_message(handles, 'Probe settings saved.'); + close(p_handles.ref_group.Parent); + probe_gui_handles.value = []; + + case 'apply_probe_configuration' + probe_settings = varargin{1}; + software = value(recording_software); + controller = value(recording_controller); + params = value(current_params); + handles = value(ui_handles); + if isempty(controller), return; end + + try + if strcmp(software, 'OpenEphys') + log_message(handles, sprintf('Setting OE reference to: %s', probe_settings.reference)); + controller.setParameters(params.oe_proc_node_id, 0, 'Reference', probe_settings.reference); + if ~isempty(probe_settings.imro_path) + log_message(handles, sprintf('Loading IMRO file: %s', probe_settings.imro_path)); + controller.config(params.oe_proc_node_id, ['LOADIMRO ' probe_settings.imro_path]); + else + log_message(handles, sprintf('Setting bank to: %d', probe_settings.bank)); + controller.setParameters(params.oe_proc_node_id, 0, 'bank', probe_settings.bank); + end + else % SpikeGLX + % SpikeGLX probe configuration (e.g., reference, channel map) is more complex + % and typically handled by setting parameters or loading a meta file. + % This is a placeholder for those more complex API calls. + log_message(handles, 'Applying SpikeGLX probe settings (via meta file or API)...'); + if ~isempty(probe_settings.imro_path) + log_message(handles, 'Note: For SpikeGLX, ensure IMRO settings are loaded within the SpikeGLX GUI and save as part of the meta file.'); + end + end + log_message(handles, 'Probe configuration applied successfully.'); + catch ME + log_message(handles, sprintf('Failed to apply probe settings: %s', ME.message)); + rethrow(ME); + end + + % ========================================================================= + % UTILITY & OTHER ACTIONS + % ========================================================================= + case 'browse_path' + type = varargin{1}; + handles = value(ui_handles); + log_message(handles, ['Opening browse dialog for ' type ' path...']); + folder_path = uigetdir; + if folder_path ~= 0 + if strcmp(type, 'local'), set(handles.local_edit, 'String', folder_path); + elseif strcmp(type, 'central'), set(handles.central_edit, 'String', folder_path); + elseif strcmp(type, 'behav'), set(handles.behav_edit, 'String', folder_path); + end + log_message(handles, [type ' path set.']); + else, log_message(handles, 'Path selection cancelled.'); end + + case 'update_subject_id' + handles = value(ui_handles); + software = value(recording_software); + params = get_all_parameters(handles,software); + if isempty(params.local_path) || isempty(params.central_path) || isempty(params.project_name) || isempty(params.rat_name) + return; + end + max_local_id = find_max_subject_id(params.local_path, params.project_name, params.rat_name); + max_central_id = find_max_subject_id(params.central_path, params.project_name, params.rat_name); + final_id = max(max_local_id, max_central_id); + if final_id > 0 + log_message(handles, ['Found existing Subject ID: ' num2str(final_id) '. Populating field.']); + set(handles.sub_edit, 'String', sprintf('%03d', final_id)); + else + log_message(handles, 'No existing Subject ID found for this rat. Please enter a new ID.'); + end + + case 'save_log_file' + handles = value(ui_handles); + params = value(current_params); + session_path = value(session_base_path); + if isempty(session_path) + log_message(handles, 'WARNING: Cannot save log file. Session path not set.'); return; + end + log_path = fullfile(params.local_path, session_path, 'behav'); + if ~exist(log_path, 'dir') + log_message(handles, ['WARNING: Behavior folder not found. Cannot save log. Path: ' log_path]); return; + end + log_file_path = fullfile(log_path, 'session_log.txt'); + try + log_content = get(handles.log_box, 'String'); + fid = fopen(log_file_path, 'w'); + if fid == -1, error('Could not open file for writing.'); end + for i = 1:length(log_content), fprintf(fid, '%s\n', log_content{i}); end + fclose(fid); + log_message(handles, ['Log file saved successfully to: ' log_file_path]); + catch ME + log_message(handles, ['ERROR: Could not save log file. Details: ' ME.message]); + end + + case 'reset_to_load_state' + handles = value(ui_handles); + currentState.value = 'Load'; + behavState.value = 'Run'; + ephysState.value = 'Run'; + set(handles.control_button, 'Enable', 'on', 'String', 'Load', 'BackgroundColor', [0.2, 0.6, 0.8]); + set(handles.sample_button, 'Enable', 'on'); + recording_controller.value = []; + behav_obj.value = []; + current_params.value = []; + session_base_path.value = ''; + log_message(handles, 'GUI reset to load state.'); + + case 'close' + try + feval(mfilename, obj, 'stop_blinking'); + if ~isempty(value(recording_controller)), delete(value(recording_controller)); end + if ishandle(value(myfig)), delete(value(myfig)); end + delete_sphandle('owner', ['^@' mfilename '$']); + if ~isempty(value(behav_obj)), dispatcher(value(behav_obj),'close'); end + obj = []; + catch + if exist('myfig','var') == 1 + if ishandle(value(myfig)), delete(value(myfig)); end + delete_sphandle('owner', ['^@' mfilename '$']); + obj = []; + end + + case 'is_running' + if exist('is_running','var') == 1 + obj = logical(value(is_running)); + else + obj = 0; + end + + case 'start_blinking' + handles = value(ui_handles); + blinking_timer.value = timer('ExecutionMode', 'fixedRate', 'Period', 0.5, 'TimerFcn', {@toggle_button_color, handles.control_button}); + start(value(blinking_timer)); + + case 'stop_blinking' + handles = value(ui_handles); + if ~isempty(value(blinking_timer)) && isvalid(value(blinking_timer)) + stop(value(blinking_timer)); + delete(value(blinking_timer)); + blinking_timer.value = []; + end + set(handles.control_button, 'BackgroundColor', [1, 0.4, 0.4]); + + otherwise + error('Unknown action: %s', action); +end +return; + +%% ======================================================================= +% PARAMETER AND VALIDATION FUNCTIONS +% ======================================================================= +function params = get_all_parameters(handles,software) + params.protocol_name = get(handles.protocol_edit, 'String'); + params.do_manual_test = get(handles.manual_test, 'Value'); + params.experimenter = get(handles.exp_edit, 'String'); + params.rat_name = get(handles.rat_name_edit, 'String'); + params.behav_path = get(handles.behav_edit, 'String'); + params.project_name = get(handles.proj_edit, 'String'); + params.subject_id = get(handles.sub_edit, 'String'); + params.local_path = get(handles.local_edit, 'String'); + params.central_path = get(handles.central_edit, 'String'); + + if strcmp(software, 'OpenEphys') + params.oe_gui_ip = get(handles.oe_ip_edit, 'String'); + params.oe_proc_node_id = get(handles.oe_proc_edit, 'String'); + params.oe_rec_node_id = get(handles.oe_rec_edit, 'String'); + else + params.sglx_host_ip = get(handles.sglx_host_edit, 'String'); + params.sglx_port = str2double(get(handles.sglx_port_edit, 'String')); + params.sglx_probe_index = str2double(get(handles.sglx_probe_edit, 'String')); + end + +function is_valid = validate_all_inputs(params,handles,software) + is_valid = false; + required_fields = {'protocol_name', 'rat_name', 'behav_path', 'project_name', 'subject_id', 'local_path'}; + for i = 1:length(required_fields) + if ~isfield(params, required_fields{i}) || isempty(params.(required_fields{i})) + msg = sprintf('Field "%s" cannot be empty.', strrep(required_fields{i}, '_', ' ')); + log_message(handles, sprintf('ERROR: %s', msg)); errordlg(msg, 'Input Error'); + return; + end + end + if ~get(handles.cb_ephys, 'Value') && ~get(handles.cb_behav, 'Value') && ~get(handles.cb_anat, 'Value') && ~get(handles.cb_funcimg, 'Value') + msg = 'At least one subfolder must be selected.'; + log_message(handles, sprintf('ERROR: %s', msg)); errordlg(msg, 'Input Error'); + return; + end + if strcmp(software, 'OpenEphys') + if isempty(params.oe_gui_ip) || isempty(params.oe_proc_node_id) || isempty(params.oe_rec_node_id) + msg = 'Open Ephys connection parameters cannot be empty.'; + log_message(handles, sprintf('ERROR: %s', msg)); errordlg(msg, 'Input Error'); + return; + end + else + if isempty(params.sglx_host_ip) || isnan(params.sglx_port) || isnan(params.sglx_probe_index) + msg = 'SpikeGLX connection parameters cannot be empty or non-numeric.'; + log_message(handles, sprintf('ERROR: %s', msg)); errordlg(msg, 'Input Error'); + return; + end + end + is_valid = true; + +%% ======================================================================= +% PATH AND DIRECTORY FUNCTIONS +% ======================================================================= +function [session_base, recording_path] = construct_session_paths(handles, params) + if isempty(params.experimenter) + subject_name = sprintf('sub-%s_id-%s', params.subject_id, params.rat_name); + else + subject_name = sprintf('sub-%s_id-%s_expmtr-%s', params.subject_id, params.rat_name, params.experimenter); + end + subject_base_path = fullfile(params.project_name, 'rawdata', subject_name); + local_subject_dir = fullfile(params.local_path, subject_base_path); + central_subject_dir = fullfile(params.central_path, subject_base_path); + new_ses_num = max(find_max_session_number(local_subject_dir), find_max_session_number(central_subject_dir)) + 1; + log_message(handles, sprintf('Last session found: %d. Creating new session: %d.', new_ses_num - 1, new_ses_num)); + session_datetime_str = char(datetime('now', 'Format', 'yyyyMMdd''T''HHmmss')); + session_folder_name = sprintf('ses-%02d_date-%s_dtype-ephys', new_ses_num, session_datetime_str); + session_base = fullfile(subject_base_path, session_folder_name); + recording_path = fullfile(params.local_path, session_base, 'ephys'); + log_message(handles, ['New session path determined: ' session_base]); + +function max_ses = find_max_session_number(base_path) + max_ses = 0; if ~exist(base_path, 'dir'), return; end + dir_contents = dir(fullfile(base_path, 'ses-*')); + if isempty(dir_contents), return; end + session_numbers = []; + for i = 1:length(dir_contents) + if dir_contents(i).isdir + token = regexp(dir_contents(i).name, '^ses-(\d+)', 'tokens'); + if ~isempty(token), session_numbers(end+1) = str2double(token{1}{1}); end + end + end + if ~isempty(session_numbers), max_ses = max(session_numbers); end + +function success = create_session_directories(handles, params,session_base_path) + success = false; + subfolders = {}; + if get(handles.cb_ephys, 'Value'), subfolders{end+1} = 'ephys'; end + if get(handles.cb_behav, 'Value'), subfolders{end+1} = 'behav'; end + if get(handles.cb_anat, 'Value'), subfolders{end+1} = 'anat'; end + if get(handles.cb_funcimg, 'Value'), subfolders{end+1} = 'funcimg'; end + try + for i = 1:length(subfolders) + local_target_path = fullfile(params.local_path, session_base_path, subfolders{i}); + log_message(handles, ['Creating local directory: ' local_target_path]); + if ~exist(local_target_path, 'dir'), mkdir(local_target_path); end + end + log_message(handles, 'All selected local directories created successfully.'); + success = true; + catch ME + msg = sprintf('Failed to create directories: %s', ME.message); + log_message(handles, ['ERROR: ' msg]); errordlg(msg, 'Directory Error'); + end + +function max_id = find_max_subject_id(base_path, project, rat) + max_id = 0; + search_path = fullfile(base_path, project, 'rawdata'); + if ~exist(search_path, 'dir'), return; end + dir_contents = dir(search_path); + if isempty(dir_contents), return; end + subject_ids = []; + pattern = sprintf('^sub-(\\d+)_id-%s', rat); + for i = 1:length(dir_contents) + if dir_contents(i).isdir + token = regexp(dir_contents(i).name, pattern, 'tokens'); + if ~isempty(token), subject_ids(end+1) = str2double(token{1}{1}); end + end + end + if ~isempty(subject_ids), max_id = max(subject_ids); end + +%% ======================================================================= +% HELPER & UTILITY FUNCTIONS +% ======================================================================= +function log_message(handles,logStr) + try + if ~isfield(handles, 'log_box') || ~isvalid(handles.log_box), return; end + current_text = get(handles.log_box, 'String'); + timestamp = datestr(now, '[HH:MM:SS] '); + new_line = [timestamp, logStr]; + new_text = [current_text; {new_line}]; + set(handles.log_box, 'String', new_text, 'Value', numel(new_text)); + drawnow; + catch + fprintf('%s: %s\n', datestr(now, '[HH:MM:SS]'), logStr); + end + +function toggle_button_color(~, ~, button_handle) + if ~isvalid(button_handle), return; end + currentColor = get(button_handle, 'BackgroundColor'); + if isequal(currentColor, [1, 0.4, 0.4]), set(button_handle, 'BackgroundColor', [1, 0.7, 0.4]); + else, set(button_handle, 'BackgroundColor', [1, 0.4, 0.4]); end + + +function commit_to_svn(handles, file_path_data,file_path_settings, root_dir) + +if isempty(file_path_data), return; end + if isempty(file_path_settings), return; end + [pname_data, fname_data, ~] = fileparts(file_path_data); + [pname_settings, fname_settings, ~] = fileparts(file_path_settings); + + configFilePath = fullfile(root_dir,'PASSWORD_CONFIG-DO_NOT_VERSIONCONTROL.mat'); + if ~exist(configFilePath, 'file') + log_message(handles, ['SVN commit failed: Password config file not found at ' configFilePath]); + return; + end + load(configFilePath, 'svn_user', 'svn_password'); + logmsg = char(strcat('automated commit from GUI for data and settings for ', {' '} ,fname_data,{'@'})); + % current_dir = cd; + cd(pname_data); + add_cmd_data = char(strcat('svn add', {' '}, fname_data, '.mat',{'@'})); + system(add_cmd_data); + + cd(pname_settings); + add_cmd_settings = char(strcat('svn add', {' '}, fname_settings, '.mat',{'@'})); + system(add_cmd_settings); + + commit_cmd = sprintf('svn ci --username="%s" --password="%s" -m "%s"', svn_user, svn_password, logmsg); + [status, ~] = system(commit_cmd); + + if status == 0 + log_message(handles, ['SVN commit successful for ' fname_data]); + else + log_message(handles, ['SVN commit FAILED for ' fname_data '.']); + end + + cd(fullfile(root_dir,'ExperPort')); + + + %% ======================================================================= +% DOCUMENTATION AND USAGE EXAMPLES +% ======================================================================= + +function display_usage_help() +% DISPLAY_USAGE_HELP - Display usage instructions and examples +% +% This function provides comprehensive usage documentation for the GUI + + fprintf('\n=== Neuropixels Recording & Behavior Controller Usage Guide ===\n\n'); + + fprintf('1. INITIALIZATION:\n'); + fprintf(' OpenEphys_Neuroblueprint_GUI(''init'');\n\n'); + + fprintf('2. WORKFLOW:\n'); + fprintf(' a) Select recording software (Open Ephys or SpikeGLX)\n'); + fprintf(' b) Configure behavior settings (protocol, experimenter, rat)\n'); + fprintf(' c) Set up NeuroBlueprint data paths\n'); + fprintf(' d) Configure probe settings (version, reference, bank/IMRO)\n'); + fprintf(' e) Set recording software connection parameters\n'); + fprintf(' f) Click "Load" to initialize systems\n'); + fprintf(' g) Click "Run" to start experiment\n'); + fprintf(' h) Click "Stop" to end experiment and save data\n\n'); + + fprintf('3. PROBE CONFIGURATION:\n'); + fprintf(' - Supports Neuropixels 1.0 (3 banks) and 2.0 (4 banks)\n'); + fprintf(' - Reference options: Tip or External\n'); + fprintf(' - Bank selection: Manual bank number or IMRO file\n'); + fprintf(' - Pre/post-session sampling across all banks\n\n'); + + fprintf('4. DATA ORGANIZATION:\n'); + fprintf(' - Follows NeuroBlueprint format\n'); + fprintf(' - Structure: project/rawdata/subject/session/datatype/\n'); + fprintf(' - Automatic session numbering\n'); + fprintf(' - SVN integration for version control\n\n'); + + fprintf('5. RECORDING SOFTWARE SUPPORT:\n'); + fprintf(' Open Ephys:\n'); + fprintf(' - HTTP API control\n'); + fprintf(' - Real-time parameter adjustment\n'); + fprintf(' - Acquisition and recording control\n\n'); + fprintf(' SpikeGLX:\n'); + fprintf(' - MATLAB SDK integration\n'); + fprintf(' - Run name management\n'); + fprintf(' - Recording enable/disable control\n\n'); + + fprintf('6. ERROR HANDLING:\n'); + fprintf(' - Comprehensive validation of inputs\n'); + fprintf(' - Automatic crash recovery for behavior protocols\n'); + fprintf(' - Detailed logging with timestamps\n'); + fprintf(' - Graceful fallbacks for system failures\n\n'); + + fprintf('For more information, see function documentation within the code.\n'); + fprintf('================================================================\n\n'); + diff --git a/ExperPort/Modules/@NeuropixelNeuroblueprint/NeuropixelNeuroblueprint.m b/ExperPort/Modules/@NeuropixelNeuroblueprint/NeuropixelNeuroblueprint.m new file mode 100644 index 00000000..003173ba --- /dev/null +++ b/ExperPort/Modules/@NeuropixelNeuroblueprint/NeuropixelNeuroblueprint.m @@ -0,0 +1,1259 @@ +function [obj, varargout] = NeuropixelNeuroblueprint(varargin) +% NEUROPIXEL_NEUROBLUEPRINT_GUI - Integrated GUI for electrophysiology and behavior experiments +% +% This GUI manages experimental workflows for coordinated electrophysiology recording +% (using either Open Ephys or SpikeGLX) with behavioral protocols using Bpod/ExperPort. +% +% FEATURES: +% - Support for both Open Ephys and SpikeGLX recording systems +% - Neuropixels probe management (NP 1.0 and 2.0) +% - NeuroBlueprint data organization format +% - Automated session management and data saving +% - SVN integration for version control +% - Pre/post-session sampling across probe banks +% +% PRE-REQUISITES: +% 1. 'open-ephys-matlab-tools' must be in MATLAB path (for Open Ephys) +% 2. 'SpikeGLX-MATLAB-SDK' must be in MATLAB path (for SpikeGLX) +% 3. Bpod/ratter/ExperPort environment fully configured +% +% USAGE: +% gui_obj = NeuropixelNeuroblueprintGUI(); +% + +%% Boilerplate for class definition and action handling +obj = class(struct, mfilename); +varargout = {}; + + +% Display usage help when function is called directly +if nargin==0 || (nargin==1 && ischar(varargin{1}) && strcmp(varargin{1}, 'empty')) + display_usage_help(); + return; +end + +if isa(varargin{1}, mfilename) + if length(varargin) < 2 || ~ischar(varargin{2}) + error(['If called with a "%s" object as first arg, a second arg, a ' ... + 'string specifying the action, is required\n']); + else + action = varargin{2}; + varargin = varargin(3:end); + end +else + action = varargin{1}; + varargin = varargin(2:end); +end + +if ~ischar(action) + error('The action parameter must be a string'); +end + +GetSoloFunctionArgs(obj); + +%% Main Action Router +switch action + % ========================================================================= + % CASE INIT + % ========================================================================= + case 'init' + % So that only the CPU-based software renderer instead of your graphics card + % opengl software; + + % Start Bpod if not already running + if evalin('base', 'exist(''BpodSystem'', ''var'')') + if evalin('base', '~isempty(BpodSystem)'), newstartup; else, flush; end + else, Bpod('COM5');newstartup; + end + + % --- State Variables as SoloParamHandles --- + SoloParamHandle(obj, 'currentState', 'value', 'Load'); + SoloParamHandle(obj, 'behavState', 'value', 'Run'); + SoloParamHandle(obj, 'ephysState', 'value', 'Run'); + SoloParamHandle(obj, 'is_running', 'value', 0); + SoloParamHandle(obj, 'recording_software', 'value', 'OpenEphys'); % 'OpenEphys' or 'SpikeGLX' + SoloParamHandle(obj, 'recording_controller', 'value', []); % Will hold OE or SpikeGLX controller + SoloParamHandle(obj, 'behav_obj', 'value', []); + SoloParamHandle(obj, 'blinking_timer', 'value', []); + SoloParamHandle(obj, 'current_params', 'value', []); + SoloParamHandle(obj, 'session_base_path', 'value', ''); + SoloParamHandle(obj, 'probe_settings', 'value', struct('version', '2.0', 'reference', 'Tip', 'bank', 0, 'imro_path', '')); + SoloParamHandle(obj, 'probe_gui_handles', 'value', []); + + % Create stopping timer for behavior + scr = timer; + set(scr,'Period', 0.2,'ExecutionMode','FixedRate','TasksToExecute',Inf,... + 'BusyMode','drop','TimerFcn',[mfilename,'(''End_Continued'')']); + SoloParamHandle(obj, 'stopping_complete_timer', 'value', scr); + + % --- Create the GUI Figure --- + SoloParamHandle(obj, 'myfig', 'saveable', 0); + myfig.value = figure('Name', 'Neuropixels Recording & Behavior Controller',... + 'NumberTitle', 'off', 'MenuBar', 'none', ... + 'ToolBar', 'none', 'Units', 'normalized', ... + 'Position', [0.1, 0.1, 0.7, 0.85],... + 'Color', [0.94, 0.94, 0.94], ... + 'CloseRequestFcn', {@(h,e) feval(mfilename, obj, 'close')}); + + % --- UI Creation --- + handles = struct(); + + % Activity Log Panel + uipanel('Title', 'Activity Log', 'FontSize', 12, 'BorderType', 'etchedin', 'BorderWidth', 1, 'Units', 'normalized', 'Position', [0.64, 0.03, 0.34, 0.94]); + handles.log_box = uicontrol('Style', 'edit', 'Units', 'normalized', 'Position', [0.65, 0.05, 0.32, 0.89], 'String', {'Log started...'}, 'Max', 10, 'Min', 1, 'HorizontalAlignment', 'left', 'Enable', 'inactive', 'BackgroundColor', [1, 1, 1]); + + % Panel 0: Recording Software Selection + p0 = uipanel('Title', '0. Recording Software', 'FontSize', 12, 'FontWeight', 'bold', 'BorderType', 'etchedin', 'BorderWidth', 1, 'Units', 'normalized', 'Position', [0.02, 0.88, 0.6, 0.09]); + handles.software_group = uibuttongroup(p0, 'Title', '', 'BorderType', 'none', 'Units', 'normalized', 'Position', [0.05, 0.1, 0.9, 0.8], 'SelectionChangedFcn', {@(h,e) feval(mfilename, obj, 'recording_software_callback')}); + uicontrol(handles.software_group, 'Style', 'radiobutton', 'String', 'Open Ephys', 'Units', 'normalized', 'Position', [0.1, 0.3, 0.4, 0.4], 'Tag', 'OpenEphys', 'FontSize', 10); + uicontrol(handles.software_group, 'Style', 'radiobutton', 'String', 'SpikeGLX', 'Units', 'normalized', 'Position', [0.5, 0.3, 0.4, 0.4], 'Tag', 'SpikeGLX', 'FontSize', 10); + + % Panel 1: Behavior + p1 = uipanel('Title', '1. Behavior', 'FontSize', 12, 'FontWeight', 'bold', 'BorderType', 'etchedin', 'BorderWidth', 1, 'Units', 'normalized', 'Position', [0.02, 0.74, 0.6, 0.13]); + uicontrol(p1, 'Style', 'text', 'String', 'Protocol Name:', 'Units', 'normalized', 'Position', [0.05, 0.7, 0.22, 0.25], 'HorizontalAlignment', 'right'); + handles.protocol_edit = uicontrol(p1, 'Style', 'edit', 'String', 'ArpitSoundCatContinuous', 'Units', 'normalized', 'Position', [0.3, 0.7, 0.45, 0.25]); + handles.manual_test = uicontrol(p1, 'Style', 'checkbox', 'String', 'Manual Test', 'Value', 1, 'Units', 'normalized', 'Position', [0.78, 0.7, 0.2, 0.25]); + uicontrol(p1, 'Style', 'text', 'String', 'Experimenter:', 'Units', 'normalized', 'Position', [0.05, 0.4, 0.22, 0.25], 'HorizontalAlignment', 'right'); + handles.exp_edit = uicontrol(p1, 'Style', 'edit', 'String', 'lida', 'Units', 'normalized', 'Position', [0.3, 0.4, 0.65, 0.25], 'Callback', {@(h,e) feval(mfilename, obj, 'update_subject_id')}); + uicontrol(p1, 'Style', 'text', 'String', 'Rat Name:', 'Units', 'normalized', 'Position', [0.05, 0.1, 0.22, 0.25], 'HorizontalAlignment', 'right'); + handles.rat_name_edit = uicontrol(p1, 'Style', 'edit', 'String', 'LP12', 'Units', 'normalized', 'Position', [0.3, 0.1, 0.4, 0.25], 'Callback', {@(h,e) feval(mfilename, obj, 'update_subject_id')}); + uicontrol(p1, 'Style', 'text', 'String', 'Path:', 'Units', 'normalized', 'Position', [0.72, 0.1, 0.08, 0.25], 'HorizontalAlignment', 'right'); + handles.behav_edit = uicontrol(p1, 'Style', 'edit', 'String', 'C:\ratter', 'Units', 'normalized', 'Position', [0.81, 0.1, 0.18, 0.25]); + + % Panel 2: NeuroBlueprint Format + p2 = uipanel('Title', '2. NeuroBlueprint Format', 'FontSize', 12, 'FontWeight', 'bold', 'BorderType', 'etchedin', 'BorderWidth', 1, 'Units', 'normalized', 'Position', [0.02, 0.42, 0.6, 0.31]); + uicontrol(p2, 'Style', 'text', 'String', 'Project Name:', 'Units', 'normalized', 'Position', [0.01, 0.85, 0.28, 0.1], 'HorizontalAlignment', 'right'); + handles.proj_edit = uicontrol(p2, 'Style', 'edit', 'String', 'sound_cat_rat', 'Units', 'normalized', 'Position', [0.3, 0.85, 0.65, 0.12]); + uicontrol(p2, 'Style', 'text', 'String', 'Subject ID:', 'Units', 'normalized', 'Position', [0.01, 0.7, 0.28, 0.1], 'HorizontalAlignment', 'right'); + handles.sub_edit = uicontrol(p2, 'Style', 'edit', 'String', '000', 'Units', 'normalized', 'Position', [0.3, 0.7, 0.65, 0.12]); + uicontrol(p2, 'Style', 'text', 'String', 'Local Path:', 'Units', 'normalized', 'Position', [0.01, 0.55, 0.28, 0.1], 'HorizontalAlignment', 'right'); + handles.local_edit = uicontrol(p2, 'Style', 'edit', 'String', 'C:\Ephys_Experiment_Data', 'Units', 'normalized', 'Position', [0.3, 0.55, 0.5, 0.12]); + handles.local_browse = uicontrol(p2, 'Style', 'pushbutton', 'String', 'Browse...', 'Units', 'normalized', 'Position', [0.81, 0.55, 0.16, 0.13], 'Callback', {@(h,e) feval(mfilename, obj, 'browse_path', 'local')}); + uicontrol(p2, 'Style', 'text', 'String', 'Central Path:', 'Units', 'normalized', 'Position', [0.01, 0.4, 0.28, 0.1], 'HorizontalAlignment', 'right'); + handles.central_edit = uicontrol(p2, 'Style', 'edit', 'String', 'Z:\_projects', 'Units', 'normalized', 'Position', [0.3, 0.4, 0.5, 0.12]); + handles.central_browse = uicontrol(p2, 'Style', 'pushbutton', 'String', 'Browse...', 'Units', 'normalized', 'Position', [0.81, 0.4, 0.16, 0.13], 'Callback', {@(h,e) feval(mfilename, obj, 'browse_path', 'central')}); + uicontrol(p2, 'Style', 'text', 'String', 'Subfolders to Create:', 'Units', 'normalized', 'Position', [0.05, 0.2, 0.9, 0.1], 'HorizontalAlignment', 'left'); + handles.cb_ephys = uicontrol(p2, 'Style', 'checkbox', 'String', 'ephys', 'Value', 1, 'Units', 'normalized', 'Position', [0.05, 0.05, 0.2, 0.15]); + handles.cb_behav = uicontrol(p2, 'Style', 'checkbox', 'String', 'behav', 'Value', 1, 'Units', 'normalized', 'Position', [0.28, 0.05, 0.2, 0.15]); + handles.cb_anat = uicontrol(p2, 'Style', 'checkbox', 'String', 'anat', 'Value', 1, 'Units', 'normalized', 'Position', [0.51, 0.05, 0.2, 0.15]); + handles.cb_funcimg = uicontrol(p2, 'Style', 'checkbox', 'String', 'funcimg', 'Value', 0, 'Units', 'normalized', 'Position', [0.74, 0.05, 0.25, 0.15]); + + % Panel 3: Pre/Post-Experiment Sampling + p3 = uipanel('Title', '3. Pre/Post-Experiment Sampling', 'FontSize', 12, 'FontWeight', 'bold', 'BorderType', 'etchedin', 'BorderWidth', 1, 'Units', 'normalized', 'Position', [0.02, 0.28, 0.6, 0.12]); + uicontrol(p3, 'Style', 'text', 'String', 'Duration/Bank (s):', 'Units', 'normalized', 'Position', [0.01, 0.6, 0.25, 0.25], 'HorizontalAlignment', 'right'); + handles.sample_duration = uicontrol(p3, 'Style', 'edit', 'String', '60', 'Units', 'normalized', 'Position', [0.27, 0.6, 0.1, 0.3]); + handles.target_display = uicontrol(p3, 'Style', 'text', 'String', 'Target: Bank 0', 'Units', 'normalized', 'Position', [0.38, 0.6, 0.3, 0.25], 'HorizontalAlignment', 'right', 'FontWeight', 'bold'); + handles.probe_button = uicontrol(p3, 'Style', 'pushbutton', 'String', 'Probe Setting', 'Units', 'normalized', 'Position', [0.7, 0.55, 0.28, 0.4], 'FontSize', 10, 'Callback', {@(h,e) feval(mfilename, obj, 'open_probe_gui')}); + handles.sample_button = uicontrol(p3, 'Style', 'pushbutton', 'String', 'Start Sample Recording', 'Units', 'normalized', 'Position', [0.05, 0.1, 0.9, 0.4], 'FontSize', 12, 'FontWeight', 'bold', 'BackgroundColor', [0.8, 0.7, 1], 'Callback', {@(h,e) feval(mfilename, obj, 'sample_recording_wrapper')}); + + % Panel 4: Recording Settings + p4 = uipanel('Title', '4. Recording Settings', 'FontSize', 12, 'FontWeight', 'bold', 'BorderType', 'etchedin', 'BorderWidth', 1, 'Units', 'normalized', 'Position', [0.02, 0.15, 0.6, 0.11]); + handles.settings_panel = p4; + + % Open Ephys Settings (initially visible) + handles.oe_ip_label = uicontrol(p4, 'Style', 'text', 'String', 'GUI IP:', 'Units', 'normalized', 'Position', [0.01, 0.3, 0.15, 0.4], 'HorizontalAlignment', 'right'); + handles.oe_ip_edit = uicontrol(p4, 'Style', 'edit', 'String', '127.0.0.1', 'Units', 'normalized', 'Position', [0.17, 0.25, 0.15, 0.5]); + handles.oe_proc_label = uicontrol(p4, 'Style', 'text', 'String', 'Proc ID:', 'Units', 'normalized', 'Position', [0.33, 0.3, 0.15, 0.4], 'HorizontalAlignment', 'right'); + handles.oe_proc_edit = uicontrol(p4, 'Style', 'edit', 'String', '100', 'Units', 'normalized', 'Position', [0.49, 0.25, 0.15, 0.5]); + handles.oe_rec_label = uicontrol(p4, 'Style', 'text', 'String', 'Rec ID:', 'Units', 'normalized', 'Position', [0.65, 0.3, 0.15, 0.4], 'HorizontalAlignment', 'right'); + handles.oe_rec_edit = uicontrol(p4, 'Style', 'edit', 'String', '101', 'Units', 'normalized', 'Position', [0.81, 0.25, 0.15, 0.5]); + + % SpikeGLX Settings (initially hidden) + handles.sglx_host_label = uicontrol(p4, 'Style', 'text', 'String', 'Host IP:', 'Units', 'normalized', 'Position', [0.01, 0.3, 0.15, 0.4], 'HorizontalAlignment', 'right', 'Visible', 'off'); + handles.sglx_host_edit = uicontrol(p4, 'Style', 'edit', 'String', 'localhost', 'Units', 'normalized', 'Position', [0.17, 0.25, 0.15, 0.5], 'Visible', 'off'); + handles.sglx_port_label = uicontrol(p4, 'Style', 'text', 'String', 'Port:', 'Units', 'normalized', 'Position', [0.33, 0.3, 0.15, 0.4], 'HorizontalAlignment', 'right', 'Visible', 'off'); + handles.sglx_port_edit = uicontrol(p4, 'Style', 'edit', 'String', '4142', 'Units', 'normalized', 'Position', [0.49, 0.25, 0.15, 0.5], 'Visible', 'off'); + handles.sglx_probe_label = uicontrol(p4, 'Style', 'text', 'String', 'Probe Idx:', 'Units', 'normalized', 'Position', [0.65, 0.3, 0.15, 0.4], 'HorizontalAlignment', 'right', 'Visible', 'off'); + handles.sglx_probe_edit = uicontrol(p4, 'Style', 'edit', 'String', '0', 'Units', 'normalized', 'Position', [0.81, 0.25, 0.15, 0.5], 'Visible', 'off'); + + % --- Control Buttons Panel --- + p5 = uipanel('Title', 'Controls', 'FontSize', 12, 'FontWeight', 'bold', 'BorderType', 'etchedin', 'BorderWidth', 1, 'Units', 'normalized', 'Position', [0.02, 0.02, 0.6, 0.11]); + handles.control_button = uicontrol(p5, 'Style', 'pushbutton', 'String', 'Load', 'Units', 'normalized', 'FontSize', 14, 'FontWeight', 'bold', 'Position', [0.02, 0.1, 0.3, 0.8], 'BackgroundColor', [0.2, 0.6, 0.8], 'Callback', {@(h,e) feval(mfilename, obj, 'main_control_callback')}); + handles.behav_button = uicontrol(p5, 'Style', 'pushbutton', 'String', 'Run Behav', 'Units', 'normalized', 'FontSize', 12, 'Position', [0.35, 0.1, 0.3, 0.8], 'BackgroundColor', [1, 0.8, 0.6], 'Callback', {@(h,e) feval(mfilename, obj, 'behav_control_callback')}); + handles.ephys_button = uicontrol(p5, 'Style', 'pushbutton', 'String', 'Run Ephys', 'Units', 'normalized', 'FontSize', 12, 'Position', [0.68, 0.1, 0.3, 0.8], 'BackgroundColor', [0.8, 0.6, 1], 'Callback', {@(h,e) feval(mfilename, obj, 'ephys_control_callback')}); + + SoloParamHandle(obj, 'ui_handles', 'value', handles); + + feval(mfilename, obj, 'update_subject_id'); + log_message(handles, 'GUI initialization complete.'); + + % ========================================================================= + % CASE MAIN_CONTROL_CALLBACK + % ========================================================================= + case 'main_control_callback' + switch value(currentState) + case 'Load', feval(mfilename, obj, 'load_sequence'); + case 'Run', feval(mfilename, obj, 'run_sequence'); + case 'Stop', feval(mfilename, obj, 'stop_sequence'); + case 'PostExperiment', feval(mfilename, obj, 'reset_to_load_state'); + end + + % ========================================================================= + % CASE RECORDING_SOFTWARE_CALLBACK + % ========================================================================= + case 'recording_software_callback' + handles = value(ui_handles); + selected_software = get(get(handles.software_group, 'SelectedObject'), 'Tag'); + recording_software.value = selected_software; + + if strcmp(selected_software, 'OpenEphys') + % Show OpenEphys settings, hide SpikeGLX + set([handles.oe_ip_label, handles.oe_ip_edit, handles.oe_proc_label, handles.oe_proc_edit, handles.oe_rec_label, handles.oe_rec_edit], 'Visible', 'on'); + set([handles.sglx_host_label, handles.sglx_host_edit, handles.sglx_port_label, handles.sglx_port_edit, handles.sglx_probe_label, handles.sglx_probe_edit], 'Visible', 'off'); + log_message(handles, 'Switched to Open Ephys recording mode.'); + else % SpikeGLX + % Hide OpenEphys settings, show SpikeGLX + set([handles.oe_ip_label, handles.oe_ip_edit, handles.oe_proc_label, handles.oe_proc_edit, handles.oe_rec_label, handles.oe_rec_edit], 'Visible', 'off'); + set([handles.sglx_host_label, handles.sglx_host_edit, handles.sglx_port_label, handles.sglx_port_edit, handles.sglx_probe_label, handles.sglx_probe_edit], 'Visible', 'on'); + log_message(handles, 'Switched to SpikeGLX recording mode.'); + end + + % ========================================================================= + % WORKFLOW ACTIONS + % ========================================================================= + case 'load_sequence' + handles = value(ui_handles); + log_message(handles, '--- LOAD sequence initiated ---'); + set(handles.control_button, 'Enable', 'off', 'String', 'Loading...'); + set(handles.sample_button, 'Enable', 'off'); + + try + software = value(recording_software); + params = get_all_parameters(handles,software); + if ~validate_all_inputs(params,handles,software) + feval(mfilename, obj, 'reset_to_load_state'); + return; + end + current_params.value = params; + + [session_path, recording_save_path] = construct_session_paths(handles, params); + if isempty(session_path) || ~create_session_directories(handles, params, session_path) + feval(mfilename, obj, 'reset_to_load_state'); + return; + end + session_base_path.value = session_path; + + % Initialize the Behavior system + feval(mfilename, obj, 'initialize_behavior_system'); + + catch ME + log_message(handles, sprintf('ERROR during load sequence: %s', ME.message)); + feval(mfilename, obj, 'reset_to_load_state'); + rethrow(ME); + end + + case 'run_sequence' + handles = value(ui_handles); + params = value(current_params); + log_message(handles, '--- RUN sequence initiated ---'); + set(handles.sample_button, 'Enable', 'off'); + + try + if get(handles.cb_ephys, 'Value') + % Initialize the selected recording system + recording_save_path = fullfile(params.local_path, value(session_base_path), 'ephys'); + feval(mfilename, obj, 'initialize_recording_system', params, recording_save_path); + if isempty(value(recording_controller)) + feval(mfilename, obj, 'reset_to_load_state'); + return; + end + % Start Ephys Recording + feval(mfilename, obj, 'start_electrophysiology_recording', params); + else + log_message(handles, 'Ephys checkbox not selected. No recording started.'); + end + + currentState.value = 'Stop'; + set(handles.control_button, 'String', 'Stop'); + feval(mfilename, obj, 'start_blinking'); + + feval(mfilename, obj, 'start_behavioral_protocol', params); + log_message(handles, '--- RUN sequence complete. Experiment is live. ---'); + + catch ME + log_message(handles, sprintf('ERROR during run sequence: %s', ME.message)); + feval(mfilename, obj, 'stop_blinking'); + rethrow(ME); + end + + case 'stop_sequence' + handles = value(ui_handles); + params = value(current_params); + log_message(handles, '--- STOP sequence initiated ---'); + feval(mfilename, obj, 'stop_blinking'); + + try + behav_save_dir = fullfile(params.local_path, value(session_base_path), 'behav'); + feval(mfilename, obj, 'stop_behavioral_protocol', params, behav_save_dir); + + if get(handles.cb_ephys, 'Value') && ~isempty(value(recording_controller)) + feval(mfilename, obj, 'stop_electrophysiology_recording'); + end + + log_message(handles, '--- Experiment finished. Post-session sampling available. ---'); + currentState.value = 'PostExperiment'; + set(handles.control_button, 'String', 'Start New Experiment', 'BackgroundColor', [0.2, 0.8, 0.6]); + set(handles.sample_button, 'Enable', 'on'); + + catch ME + log_message(handles, sprintf('ERROR during stop sequence: %s', ME.message)); + rethrow(ME); + end + + % ========================================================================= + % SYSTEM INITIALIZATION & CONTROL + % ========================================================================= + case 'initialize_recording_system' + params = varargin{1}; + save_path = varargin{2}; + software = value(recording_software); + handles = value(ui_handles); + + try + if strcmp(software, 'OpenEphys') + log_message(handles, 'Initializing Open Ephys controller...'); + controller = OpenEphysHTTPServer(params.oe_gui_ip, 37497); + if isempty(controller), error('Failed to create Open Ephys controller'); end + else % SpikeGLX + log_message(handles, 'Initializing SpikeGLX controller...'); + controller = SpikeGL(params.sglx_host_ip, params.sglx_port); + if ~controller.IsConnected(), error('Failed to connect to SpikeGLX'); end + end + recording_controller.value = controller; + log_message(handles, sprintf('%s controller initialized successfully.', software)); + + % Set initial recording path + feval(mfilename, obj, 'set_recording_path', save_path); + + catch ME + log_message(handles, sprintf('Failed to initialize %s: %s', software, ME.message)); + recording_controller.value = []; + rethrow(ME); + end + + case 'set_recording_path' + save_path = varargin{1}; + software = value(recording_software); + controller = value(recording_controller); + params = value(current_params); + handles = value(ui_handles); + + if isempty(controller), return; end + + try + if strcmp(software, 'OpenEphys') + controller.setRecordPath(params.oe_rec_node_id, save_path); + else % SpikeGLX + controller.SetDataDir(save_path); + end + log_message(handles, sprintf('Recording path set to: %s', save_path)); + catch ME + log_message(handles, sprintf('Failed to set recording path: %s', ME.message)); + rethrow(ME); + end + + case 'start_electrophysiology_recording' + params = varargin{1}; + software = value(recording_software); + controller = value(recording_controller); + handles = value(ui_handles); + + if isempty(controller), error('Recording controller not initialized'); end + + try + probe_settings_struct = value(probe_settings); + feval(mfilename, obj, 'apply_probe_configuration', probe_settings_struct); + + main_ephys_path = fullfile(params.local_path, value(session_base_path), 'ephys'); + feval(mfilename, obj, 'set_recording_path', main_ephys_path); + + if strcmp(software, 'OpenEphys') + log_message(handles, 'Starting Open Ephys acquisition and recording...'); + controller.acquire(); pause(1); + controller.record(); + else % SpikeGLX + log_message(handles, 'Starting SpikeGLX recording...'); + run_name = sprintf('experiment_%s', datestr(now, 'yyyymmdd_HHMMSS')); + controller.SetRunName(run_name); + controller.StartRun(); + end + + log_message(handles, 'Electrophysiology recording is LIVE.'); + ephysState.value = 'Stop'; + set(handles.ephys_button, 'String', 'Stop Ephys', 'BackgroundColor', [1 0.6 0.6]); + + catch ME + log_message(handles, sprintf('Failed to start recording: %s', ME.message)); + rethrow(ME); + end + + case 'stop_electrophysiology_recording' + software = value(recording_software); + controller = value(recording_controller); + handles = value(ui_handles); + if isempty(controller), return; end + + try + if strcmp(software, 'OpenEphys') + log_message(handles, 'Stopping Open Ephys recording...'); + controller.idle(); + else % SpikeGLX + log_message(handles, 'Stopping SpikeGLX recording...'); + controller.StopRun(); + end + + log_message(handles, 'Electrophysiology recording stopped.'); + ephysState.value = 'Run'; + set(handles.ephys_button, 'String', 'Run Ephys', 'BackgroundColor', [0.8, 0.6, 1]); + + catch ME + log_message(handles, sprintf('Failed to stop recording: %s', ME.message)); + rethrow(ME); + end + + case 'initialize_behavior_system' + params = value(current_params); + handles = value(ui_handles); + try + log_message(handles, 'Initializing behavior control system...'); + behav_obj.value = dispatcher('init'); + h=get_sphandle('owner','dispatcher','name','myfig'); + set(value(h{1}), 'Visible','Off'); + if params.do_manual_test + feval(mfilename, obj, 'behav_control', 'manual_test'); + else + feval(mfilename, obj, 'continue_load_after_manual_test'); + end + catch ME + log_message(handles, ['FATAL ERROR initializing behavior system: ' ME.message]); + errordlg(['Failed to initialize behavior system. Check path and logs. Error: ' ME.message], 'Behavior System Error'); + rethrow(ME); + end + + case 'start_behavioral_protocol' + params = varargin{1}; + handles = value(ui_handles); + try + log_message(handles, 'Starting behavioral protocol...'); + feval(mfilename, obj, 'behav_control', 'run', params.protocol_name); + log_message(handles, 'Behavioral protocol is LIVE.'); + catch ME + log_message(handles, ['FATAL ERROR starting behavior protocol: ' ME.message]); + errordlg(['Failed to start behavior protocol. Check logs. Error: ' ME.message], 'Behavior System Error'); + rethrow(ME); + end + + case 'stop_behavioral_protocol' + params = varargin{1}; + behav_save_dir = varargin{2}; + handles = value(ui_handles); + try + log_message(handles, 'Ending behavioral session (saving data)...'); + feval(mfilename, obj, 'behav_control', 'end', params.protocol_name, params.behav_path, behav_save_dir); + log_message(handles, 'Behavioral data saved successfully.'); + catch ME + log_message(handles, ['FATAL ERROR ending behavioral session: ' ME.message]); + errordlg(['Failed to save behavioral data. Check logs. Error: ' ME.message], 'Behavior System Error'); + rethrow(ME); + end + + case 'manual_test_stopping' + + handles = value(ui_handles); + log_message(handles, 'Manual rig test complete. Cleaning up...'); + dispatcher(value(behav_obj), 'Stop'); + + %Let's pause until we know dispatcher is done running + set(value(stopping_complete_timer), 'TimerFcn', {@(h,e) feval(mfilename, obj, 'manual_test_stopped')}); + start(value(stopping_complete_timer)); + + case 'manual_test_stopped' + + if value(stopping_process_completed) %This is provided by RunningSection + stop(value(stopping_complete_timer)); %Stop looping. + dispatcher('set_protocol', ''); + is_running.value = 0; + feval(mfilename, obj, 'continue_load_after_manual_test'); + end + + case 'continue_load_after_manual_test' + params = value(current_params); + handles = value(ui_handles); + video_save_dir = fullfile(params.local_path, value(session_base_path), 'behav'); + try + log_message(handles, 'Loading main behavioral protocol...'); + feval(mfilename, obj, 'behav_control', 'load_main_protocol', params.experimenter, params.rat_name, params.protocol_name, video_save_dir, params.behav_path); + log_message(handles, 'Behavior system loaded and ready.'); + log_message(handles, '--- LOAD sequence complete. Ready to run. ---'); + currentState.value = 'Run'; + set(handles.control_button, 'Enable', 'on', 'String', 'Run', 'BackgroundColor', [0.4, 0.8, 0.4]); + set(handles.sample_button, 'Enable', 'on'); + catch ME + log_message(handles, ['FATAL ERROR loading main protocol: ' ME.message]); + errordlg(['Failed to load main protocol. Error: ' ME.message], 'Behavior System Error'); + feval(mfilename, obj, 'reset_to_load_state'); + end + + % ========================================================================= + % INDIVIDUAL CONTROL CALLBACKS + % ========================================================================= + case 'behav_control_callback' + switch value(behavState) + case 'Run', feval(mfilename, obj, 'behav_control', 'load_run'); + case 'Stop', feval(mfilename, obj, 'behav_control', 'end'); + end + + case 'ephys_control_callback' + switch value(ephysState) + case 'Run', feval(mfilename, obj, 'run_ephys_individually'); + case 'Stop', feval(mfilename, obj, 'stop_ephys_individually'); + end + + case 'run_ephys_individually' + params = value(current_params); + handles = value(ui_handles); + log_message(handles, '--- Starting Ephys Recording Individually ---'); + set(handles.ephys_button, 'Enable', 'off'); + try + if isempty(value(recording_controller)) + feval(mfilename, obj, 'initialize_recording_system', params, ''); + end + feval(mfilename, obj, 'start_electrophysiology_recording', params); + catch ME + log_message(handles, sprintf('ERROR starting ephys: %s', ME.message)); + end + set(handles.ephys_button, 'Enable', 'on'); + + case 'stop_ephys_individually' + handles = value(ui_handles); + log_message(handles, '--- Stopping Ephys Recording Individually ---'); + set(handles.ephys_button, 'Enable', 'off'); + try + feval(mfilename, obj, 'stop_electrophysiology_recording'); + catch ME + log_message(handles, sprintf('ERROR stopping ephys: %s', ME.message)); + end + set(handles.ephys_button, 'Enable', 'on'); + + % ========================================================================= + % BEHAVIOR CONTROL ACTIONS + % ========================================================================= + case 'behav_control' + sub_action = varargin{1}; + args = varargin(2:end); + handles = value(ui_handles); + + switch sub_action + case 'load_main_protocol' + experimenter = args{1}; ratname = args{2}; protocol_name = args{3}; + video_save_dir = args{4}; behav_path = args{5}; + log_message(handles, ['Loading protocol: ' protocol_name]); + dispatcher('set_protocol', protocol_name); + rath = get_sphandle('name', 'ratname', 'owner', protocol_name); + exph = get_sphandle('name', 'experimenter', 'owner', protocol_name); + rath{1}.value = ratname; exph{1}.value = experimenter; + protobj = eval(protocol_name); + log_message(handles, ['Loading settings for ' ratname]); + [~, sfile] = load_solouiparamvalues(ratname, 'experimenter', experimenter, 'owner', class(protobj), 'interactive', 0); + feval(protocol_name, protobj, 'set_setting_params', ratname, experimenter, sfile, char(datetime('now')), video_save_dir); + if ~dispatcher('is_running'), pop_history(class(protobj), 'include_non_gui', 1); feval(protocol_name, protobj, 'prepare_next_trial'); end + + case 'crashed' + log_message(handles, '--- BEHAVIOR CRASH RECOVERY INITIATED ---'); + params = value(current_params); + video_save_dir = fullfile(params.local_path, value(session_base_path), 'behav'); + feval(mfilename, obj, 'behav_control', 'load_protocol_after_crash', params.experimenter, params.rat_name, params.protocol_name, video_save_dir, params.behav_path); + feval(mfilename, obj, 'behav_control', 'run', params.protocol_name); + log_message(handles, '--- RECOVERY COMPLETE: Behavior protocol restarted ---'); + + case 'load_protocol_after_crash' + experimenter = args{1}; ratname = args{2}; protocol_name = args{3}; + video_save_dir = args{4}; behav_path = args{5}; + log_message(handles, ['Loading protocol after crash: ' protocol_name]); + dispatcher('set_protocol', protocol_name); + rath = get_sphandle('name', 'ratname', 'owner', protocol_name); + exph = get_sphandle('name', 'experimenter', 'owner', protocol_name); + rath{1}.value = ratname; exph{1}.value = experimenter; + protobj = eval(protocol_name); + try + log_message(handles, ['Loading previous data for ' ratname]); + today_date = char(datetime('now','format','yyMMdd')); + temp_data_dir = fullfile(behav_path,'SoloData','Data',experimenter,ratname); + temp_data_file = sprintf('data_@%s_%s_%s_%s_ASV.mat',protocol_name,experimenter,ratname,today_date); + if isfile(fullfile(temp_data_dir,temp_data_file)) + dispatcher('runstart_disable'); + load_soloparamvalues(ratname, 'experimenter', experimenter, 'owner', protocol_name, 'interactive', 0,'data_file',fullfile(temp_data_dir,temp_data_file)); + dispatcher('runstart_enable'); + end + if ~dispatcher('is_running'), pop_history(class(protobj), 'include_non_gui', 1); feval(protocol_name, protobj, 'prepare_next_trial'); end + catch + log_message(handles, ['Loading settings for ' ratname]); + [~, sfile] = load_solouiparamvalues(ratname, 'experimenter', experimenter, 'owner', class(protobj), 'interactive', 0); + feval(protocol_name, protobj, 'set_setting_params', ratname, experimenter, sfile, char(datetime('now')), video_save_dir); + if ~dispatcher('is_running'), pop_history(class(protobj), 'include_non_gui', 1); feval(protocol_name, protobj, 'prepare_next_trial'); end + end + + case 'load_run' + set(handles.behav_button, 'Enable', 'off'); + log_message(handles, '--- STARTING BEHAV PROTOCOL ---'); + params = value(current_params); + video_save_dir = fullfile(params.local_path, value(session_base_path), 'behav'); + feval(mfilename, obj, 'behav_control', 'load_protocol_after_crash', params.experimenter, params.rat_name, params.protocol_name, video_save_dir, params.behav_path); + feval(mfilename, obj, 'behav_control', 'run', params.protocol_name); + log_message(handles, '--- START COMPLETE: Behavior protocol started ---'); + set(handles.behav_button, 'Enable', 'on'); + + case 'run' + protocol_name = args{1}; protobj = eval(protocol_name); + log_message(handles, 'Starting video recording via protocol...'); + feval(protocol_name, protobj, 'start_recording'); + log_message(handles, 'Starting dispatcher to run trials...'); + is_running.value = 1; + behavState.value = 'Stop'; + set(handles.behav_button, 'String', 'Stop Behav', 'BackgroundColor', [1 0.6 0.6]); + dispatcher(value(behav_obj), 'Run'); + + case 'end' + set(handles.behav_button, 'Enable', 'off'); + if length(args) >= 3 + protocol_name = args{1}; root_dir = args{2}; behav_copy_dir = args{3}; + else + params = value(current_params); + protocol_name = params.protocol_name; + root_dir = params.behav_path; + behav_copy_dir = fullfile(params.local_path, value(session_base_path), 'behav'); + end + log_message(handles, 'Stopping dispatcher...'); + dispatcher(value(behav_obj), 'Stop'); + set(value(stopping_complete_timer), 'Period', 0.8,'TimerFcn', {@(h,e) feval(mfilename, obj, 'behav_control','end_continued',protocol_name, root_dir, behav_copy_dir)}); + start(value(stopping_complete_timer)); + + case 'end_continued' + if value(stopping_process_completed) % This is provided by RunningSection + protocol_name = args{1}; root_dir = args{2}; destination_path = args{3}; + stop(value(stopping_complete_timer)); %Stop looping. + is_running.value = 0; + feval(mfilename, obj, 'behav_control', 'send_empty_state_machine'); + protobj = eval(protocol_name); + log_message(handles, 'Ending session via protocol...'); + feval(protocol_name, protobj, 'end_session'); + log_message(handles, 'Saving data and settings...'); + data_file = SavingSection(protobj, 'savedata', 'interactive', 0); + try + feval(protocol_name, protobj, 'pre_saving_settings'); + catch + log_message(handles, 'Protocol does not have a pre_saving_settings section.'); + end + [settings_file, ~] = SavingSection(protobj, 'get_set_filename'); + SavingSection(protobj, 'savesets', 'interactive', 0); + log_message(handles, 'Committing data and settings to SVN...'); + commit_to_svn(handles, data_file, settings_file, root_dir); + dispatcher('set_protocol', ''); + data_file = [data_file '.mat']; + [status, msg] = copyfile(data_file, destination_path); + if status, log_message(handles,'Data File copied successfully.'); + else, log_message(handles,['Error copying Data file: ' msg]); + end + behavState.value = 'Run'; + set(handles.behav_button, 'String', 'Run Behav', 'BackgroundColor', [1, 0.8, 0.6]); + feval(mfilename, obj, 'save_log_file'); + set(handles.behav_button, 'Enable', 'on'); + end + + case 'manual_test' + log_message(handles, 'Loading manual rig test protocol...'); + dispatcher('set_protocol', 'Rigtest_singletrial'); + h=get_sphandle('owner','Rigtest_singletrial','name', 'myfig'); + for i=1:numel(h); set(value(h{i}),'Visible','Off'); end + is_running.value = 1; + log_message(handles, 'Starting manual rig test. Please complete the one-trial test.'); + dispatcher(value(behav_obj), 'Run'); + + case 'create_svn_data_dir' + experimenter = args{1}; ratname = args{2}; behav_dir = args{3}; dir_name = args{4}; + dirCurrent = cd; + settings_path = fullfile(behav_dir, 'SoloData', dir_name); + exp_path = fullfile(settings_path, experimenter); + rat_path = fullfile(exp_path, ratname); + if ~isfolder(settings_path), mkdir(settings_path); system(['svn add ' dir_name]); end + if ~isfolder(exp_path), cd(settings_path); mkdir(experimenter); system(['svn add ' experimenter]); end + if ~isfolder(rat_path), cd(exp_path); mkdir(ratname); system(['svn add ' ratname]); end + cd(dirCurrent); + log_message(handles, ['Created SVN directory structure for ' ratname]); + + case 'send_empty_state_machine' + state_machine_server = bSettings('get', 'RIGS', 'state_machine_server'); + server_slot = bSettings('get', 'RIGS', 'server_slot'); if isnan(server_slot), server_slot = 0; end + card_slot = bSettings('get', 'RIGS', 'card_slot'); if isnan(card_slot), card_slot = 0; end + sm = BpodSM(state_machine_server, 3333, server_slot); sm = Initialize(sm); + [inL, outL] = MachinesSection(dispatcher, 'determine_io_maps'); + sma = StateMachineAssembler('full_trial_structure'); + sma = add_state(sma, 'name', 'vapid_state_in_vapid_matrix'); + send(sma, sm, 'run_trial_asap', 0, 'input_lines', inL, 'dout_lines', outL, 'sound_card_slot', int2str(card_slot)); + + end + + case 'crash_detected' + % handles = feval(mfilename, obj, 'get_ui_handles'); + handles = value(ui_handles); + if ~strcmp(value(currentState), 'Stop') || isempty(value(behav_obj)), return; end + + log_message(handles, '!!! CRASH DETECTED: Behavior system is not running. Attempting recovery...'); + try + feval(mfilename, obj, 'behav_control', 'crashed'); + + catch ME + log_message(handles, sprintf('FATAL: Recovery attempt failed: %s', ME.message)); + getReport(ME, 'extended', 'hyperlinks', 'on'); + errordlg('Automatic recovery failed. Please stop the experiment manually.', 'Recovery Failed'); + end + % ========================================================================= + % SAMPLE EPHYS RECORDINGS + % ========================================================================= + case 'sample_recording_wrapper' + if strcmp(value(currentState), 'PostExperiment') + feval(mfilename, obj, 'sample_recording', 'post_session'); + else + feval(mfilename, obj, 'sample_recording', 'pre_session'); + end + + case 'sample_recording' + prefix = varargin{1}; + handles = value(ui_handles); + log_message(handles, ['--- ' upper(prefix) ' SAMPLE RECORDING INITIATED ---']); + set([handles.sample_button, handles.control_button], 'Enable', 'off', 'String', 'Sampling...'); + drawnow; + + try + software = value(recording_software); + params = get_all_parameters(handles,software); + if isempty(value(recording_controller)) + feval(mfilename, obj, 'initialize_recording_system', params, ''); + end + + if isempty(value(session_base_path)) + [session_path, ~] = construct_session_paths(handles, params); + if isempty(session_path) || ~create_session_directories(handles, params, session_path) + error('Failed to create session directories'); + end + session_base_path.value = session_path; + end + + sample_dir_name = sprintf('%s_sample_recording', prefix); + sample_save_path = fullfile(params.local_path, value(session_base_path), 'ephys', sample_dir_name); + if ~exist(sample_save_path, 'dir'), mkdir(sample_save_path); end + + software = value(recording_software); + if strcmp(software, 'OpenEphys') + feval(mfilename, obj, 'execute_openephys_sampling', sample_save_path); + else + feval(mfilename, obj, 'execute_spikeglx_sampling', sample_save_path); + end + log_message(handles, '--- SAMPLE RECORDING COMPLETE ---'); + + catch ME + log_message(handles, sprintf('ERROR during sample recording: %s', ME.message)); + rethrow(ME); + end + + % Reset button states + if strcmp(value(currentState), 'PostExperiment') + set(handles.control_button, 'Enable', 'on', 'String', 'Start New Experiment'); + feval(mfilename, obj, 'save_log_file'); + else + set(handles.control_button, 'Enable', 'on', 'String', 'Load'); + end + set(handles.sample_button, 'Enable', 'on', 'String', 'Start Sample Recording'); + + case 'execute_openephys_sampling' + save_path = varargin{1}; + controller = value(recording_controller); + params = value(current_params); + handles = value(ui_handles); + probe_settings_struct = value(probe_settings); + duration = str2double(get(handles.sample_duration, 'String')); + + controller.setParameters(params.oe_proc_node_id, 0, 'Reference', probe_settings_struct.reference); + if strcmp(probe_settings_struct.version, '1.0'), num_banks = 3; else, num_banks = 4; end + + for bank = 0:(num_banks - 1) + log_message(handles, sprintf('Recording OE Bank %d for %d seconds...', bank, duration)); + controller.setParameters(params.oe_proc_node_id, 0, 'bank', bank); pause(1); + controller.setRecordPath(params.oe_rec_node_id, save_path); pause(1); + controller.acquire(duration); pause(1); + controller.record(duration); + controller.idle(); + log_message(handles, sprintf('Finished recording Bank %d.', bank)); pause(1); + end + + if ~isempty(probe_settings_struct.imro_path) + controller.config(params.oe_proc_node_id, ['LOADIMRO ' probe_settings_struct.imro_path]); + else + controller.setParameters(params.oe_proc_node_id, 0, 'bank', probe_settings_struct.bank); + end + + case 'execute_spikeglx_sampling' + save_path = varargin{1}; + controller = value(recording_controller); + handles = value(ui_handles); + probe_settings_struct = value(probe_settings); + duration = str2double(get(handles.sample_duration, 'String')); + + controller.SetDataDir(save_path); + if strcmp(probe_settings_struct.version, '1.0'), num_banks = 3; else, num_banks = 4; end + + for bank = 0:(num_banks - 1) + log_message(handles, sprintf('Recording SGLX Bank %d for %d seconds...', bank, duration)); + % Bank selection for SpikeGLX depends on specific API calls for channel selection, not a simple 'bank' parameter + % This is a placeholder for more complex channel/bank setting logic. + % For now, we record with the currently active map. + + run_name = sprintf('sample_bank_%d_%s', bank, datestr(now, 'yyyymmdd_HHMMSS')); + controller.SetRunName(run_name); + controller.StartRun(); + pause(duration); + controller.StopRun(); + log_message(handles, sprintf('Finished recording Bank %d.', bank)); pause(1); + end + + % ========================================================================= + % PROBE GUI AND SETTINGS + % ========================================================================= + case 'open_probe_gui' + handles = value(ui_handles); + log_message(handles, 'Opening probe settings GUI...'); + probe_fig = figure('Name', 'Neuropixel Probe Settings', 'Position', [300 300 450 300], ... + 'MenuBar', 'none', 'ToolBar', 'none', 'NumberTitle', 'off', 'Resize', 'off'); + p_handles = struct(); + p_handles.version_group = uibuttongroup(probe_fig, 'Title', 'Probe Version', 'Position', [0.05 0.75 0.9 0.2]); + uicontrol(p_handles.version_group, 'Style', 'radiobutton', 'String', 'NP 1.0 (3 Banks)', 'Position', [10 5 150 25], 'Tag', '1.0'); + uicontrol(p_handles.version_group, 'Style', 'radiobutton', 'String', 'NP 2.0 (4 Banks)', 'Position', [200 5 150 25], 'Tag', '2.0'); + p_handles.ref_group = uibuttongroup(probe_fig, 'Title', 'Reference', 'Position', [0.05 0.5 0.4 0.2]); + uicontrol(p_handles.ref_group, 'Style', 'radiobutton', 'String', 'Tip', 'Position', [10 5 80 25], 'Tag', 'Tip'); + uicontrol(p_handles.ref_group, 'Style', 'radiobutton', 'String', 'External', 'Position', [100 5 80 25], 'Tag', 'External'); + p_handles.bank_panel = uipanel(probe_fig, 'Title', 'Target Bank', 'Position', [0.5 0.5 0.45 0.2]); + uicontrol(p_handles.bank_panel, 'Style', 'text', 'String', 'Bank:', 'Position', [10 5 40 20]); + p_handles.bank_edit = uicontrol(p_handles.bank_panel, 'Style', 'edit', 'String', '0', 'Position', [60 5 50 25]); + uicontrol(probe_fig, 'Style', 'text', 'String', 'IMRO File:', 'Position', [20 120 60 20]); + p_handles.imro_text = uicontrol(probe_fig, 'Style', 'text', 'String', 'None selected', 'Position', [90 120 280 20], 'HorizontalAlignment', 'left'); + uicontrol(probe_fig, 'Style', 'pushbutton', 'String', 'Browse...', 'Position', [20 85 100 30], 'Callback', {@(h,e) feval(mfilename, obj, 'browse_imro', p_handles)}); + uicontrol(probe_fig, 'Style', 'pushbutton', 'String', 'Clear IMRO', 'Position', [130 85 100 30], 'Callback', {@(h,e) feval(mfilename, obj, 'clear_imro', p_handles)}); + uicontrol(probe_fig, 'Style', 'pushbutton', 'String', 'Apply & Close', 'Position', [250 25 180 30], 'FontWeight', 'bold', 'Callback', {@(h,e) feval(mfilename, obj, 'apply_probe_settings', p_handles)}); + probe_gui_handles.value = p_handles; + + case 'browse_imro' + p_handles = varargin{1}; + [file, path] = uigetfile('*.imro', 'Select IMRO File'); + if isequal(file, 0) || isequal(path, 0), return; + else + full_path = fullfile(path, file); + set(p_handles.imro_text, 'String', full_path); + set(findobj(p_handles.bank_panel, '-property', 'Enable'), 'Enable', 'off'); + end + + case 'clear_imro' + p_handles = varargin{1}; + set(p_handles.imro_text, 'String', 'None selected'); + set(findobj(p_handles.bank_panel, '-property', 'Enable'), 'Enable', 'on'); + + case 'apply_probe_settings' + p_handles = varargin{1}; + handles = value(ui_handles); + settings.version = get(get(p_handles.version_group, 'SelectedObject'), 'Tag'); + settings.reference = get(get(p_handles.ref_group, 'SelectedObject'), 'Tag'); + settings.bank = str2double(get(p_handles.bank_edit, 'String')); + settings.imro_path = get(p_handles.imro_text, 'String'); + if strcmp(settings.imro_path, 'None selected'), settings.imro_path = ''; end + probe_settings.value = settings; + if ~isempty(settings.imro_path) + set(handles.target_display, 'String', 'Target: IMRO File'); + else + set(handles.target_display, 'String', ['Target: Bank ' num2str(settings.bank)]); + end + log_message(handles, 'Probe settings saved.'); + close(p_handles.ref_group.Parent); + probe_gui_handles.value = []; + + case 'apply_probe_configuration' + probe_settings = varargin{1}; + software = value(recording_software); + controller = value(recording_controller); + params = value(current_params); + handles = value(ui_handles); + if isempty(controller), return; end + + try + if strcmp(software, 'OpenEphys') + log_message(handles, sprintf('Setting OE reference to: %s', probe_settings.reference)); + controller.setParameters(params.oe_proc_node_id, 0, 'Reference', probe_settings.reference); + if ~isempty(probe_settings.imro_path) + log_message(handles, sprintf('Loading IMRO file: %s', probe_settings.imro_path)); + controller.config(params.oe_proc_node_id, ['LOADIMRO ' probe_settings.imro_path]); + else + log_message(handles, sprintf('Setting bank to: %d', probe_settings.bank)); + controller.setParameters(params.oe_proc_node_id, 0, 'bank', probe_settings.bank); + end + else % SpikeGLX + % SpikeGLX probe configuration (e.g., reference, channel map) is more complex + % and typically handled by setting parameters or loading a meta file. + % This is a placeholder for those more complex API calls. + log_message(handles, 'Applying SpikeGLX probe settings (via meta file or API)...'); + if ~isempty(probe_settings.imro_path) + log_message(handles, 'Note: For SpikeGLX, ensure IMRO settings are loaded within the SpikeGLX GUI and save as part of the meta file.'); + end + end + log_message(handles, 'Probe configuration applied successfully.'); + catch ME + log_message(handles, sprintf('Failed to apply probe settings: %s', ME.message)); + rethrow(ME); + end + + % ========================================================================= + % UTILITY & OTHER ACTIONS + % ========================================================================= + case 'browse_path' + type = varargin{1}; + handles = value(ui_handles); + log_message(handles, ['Opening browse dialog for ' type ' path...']); + folder_path = uigetdir; + if folder_path ~= 0 + if strcmp(type, 'local'), set(handles.local_edit, 'String', folder_path); + elseif strcmp(type, 'central'), set(handles.central_edit, 'String', folder_path); + elseif strcmp(type, 'behav'), set(handles.behav_edit, 'String', folder_path); + end + log_message(handles, [type ' path set.']); + else, log_message(handles, 'Path selection cancelled.'); end + + case 'update_subject_id' + handles = value(ui_handles); + software = value(recording_software); + params = get_all_parameters(handles,software); + if isempty(params.local_path) || isempty(params.central_path) || isempty(params.project_name) || isempty(params.rat_name) + return; + end + max_local_id = find_max_subject_id(params.local_path, params.project_name, params.rat_name); + max_central_id = find_max_subject_id(params.central_path, params.project_name, params.rat_name); + final_id = max(max_local_id, max_central_id); + if final_id > 0 + log_message(handles, ['Found existing Subject ID: ' num2str(final_id) '. Populating field.']); + set(handles.sub_edit, 'String', sprintf('%03d', final_id)); + else + log_message(handles, 'No existing Subject ID found for this rat. Please enter a new ID.'); + end + + case 'save_log_file' + handles = value(ui_handles); + params = value(current_params); + session_path = value(session_base_path); + if isempty(session_path) + log_message(handles, 'WARNING: Cannot save log file. Session path not set.'); return; + end + log_path = fullfile(params.local_path, session_path, 'behav'); + if ~exist(log_path, 'dir') + log_message(handles, ['WARNING: Behavior folder not found. Cannot save log. Path: ' log_path]); return; + end + log_file_path = fullfile(log_path, 'session_log.txt'); + try + log_content = get(handles.log_box, 'String'); + fid = fopen(log_file_path, 'w'); + if fid == -1, error('Could not open file for writing.'); end + for i = 1:length(log_content), fprintf(fid, '%s\n', log_content{i}); end + fclose(fid); + log_message(handles, ['Log file saved successfully to: ' log_file_path]); + catch ME + log_message(handles, ['ERROR: Could not save log file. Details: ' ME.message]); + end + + case 'reset_to_load_state' + handles = value(ui_handles); + currentState.value = 'Load'; + behavState.value = 'Run'; + ephysState.value = 'Run'; + set(handles.control_button, 'Enable', 'on', 'String', 'Load', 'BackgroundColor', [0.2, 0.6, 0.8]); + set(handles.sample_button, 'Enable', 'on'); + recording_controller.value = []; + behav_obj.value = []; + current_params.value = []; + session_base_path.value = ''; + log_message(handles, 'GUI reset to load state.'); + + case 'close' + try + feval(mfilename, obj, 'stop_blinking'); + if ~isempty(value(recording_controller)), delete(value(recording_controller)); end + if ishandle(value(myfig)), delete(value(myfig)); end + delete_sphandle('owner', ['^@' mfilename '$']); + if ~isempty(value(behav_obj)), dispatcher(value(behav_obj),'close'); end + obj = []; + catch + if exist('myfig','var') == 1 + if ishandle(value(myfig)), delete(value(myfig)); end + else + delete(gcbf); + end + delete_sphandle('owner', ['^@' mfilename '$']); + obj = []; + end + + case 'is_running' + if exist('is_running','var') == 1 + obj = logical(value(is_running)); + else + obj = 0; + end + + case 'start_blinking' + handles = value(ui_handles); + blinking_timer.value = timer('ExecutionMode', 'fixedRate', 'Period', 0.5, 'TimerFcn', {@toggle_button_color, handles.control_button}); + start(value(blinking_timer)); + + case 'stop_blinking' + handles = value(ui_handles); + if ~isempty(value(blinking_timer)) && isvalid(value(blinking_timer)) + stop(value(blinking_timer)); + delete(value(blinking_timer)); + blinking_timer.value = []; + end + set(handles.control_button, 'BackgroundColor', [1, 0.4, 0.4]); + + otherwise + error('Unknown action: %s', action); +end +return; + +%% ======================================================================= +% PARAMETER AND VALIDATION FUNCTIONS +% ======================================================================= +function params = get_all_parameters(handles,software) + params.protocol_name = get(handles.protocol_edit, 'String'); + params.do_manual_test = get(handles.manual_test, 'Value'); + params.experimenter = get(handles.exp_edit, 'String'); + params.rat_name = get(handles.rat_name_edit, 'String'); + params.behav_path = get(handles.behav_edit, 'String'); + params.project_name = get(handles.proj_edit, 'String'); + params.subject_id = get(handles.sub_edit, 'String'); + params.local_path = get(handles.local_edit, 'String'); + params.central_path = get(handles.central_edit, 'String'); + + if strcmp(software, 'OpenEphys') + params.oe_gui_ip = get(handles.oe_ip_edit, 'String'); + params.oe_proc_node_id = get(handles.oe_proc_edit, 'String'); + params.oe_rec_node_id = get(handles.oe_rec_edit, 'String'); + else + params.sglx_host_ip = get(handles.sglx_host_edit, 'String'); + params.sglx_port = str2double(get(handles.sglx_port_edit, 'String')); + params.sglx_probe_index = str2double(get(handles.sglx_probe_edit, 'String')); + end + +function is_valid = validate_all_inputs(params,handles,software) + is_valid = false; + required_fields = {'protocol_name', 'rat_name', 'behav_path', 'project_name', 'subject_id', 'local_path'}; + for i = 1:length(required_fields) + if ~isfield(params, required_fields{i}) || isempty(params.(required_fields{i})) + msg = sprintf('Field "%s" cannot be empty.', strrep(required_fields{i}, '_', ' ')); + log_message(handles, sprintf('ERROR: %s', msg)); errordlg(msg, 'Input Error'); + return; + end + end + if ~get(handles.cb_ephys, 'Value') && ~get(handles.cb_behav, 'Value') && ~get(handles.cb_anat, 'Value') && ~get(handles.cb_funcimg, 'Value') + msg = 'At least one subfolder must be selected.'; + log_message(handles, sprintf('ERROR: %s', msg)); errordlg(msg, 'Input Error'); + return; + end + if strcmp(software, 'OpenEphys') + if isempty(params.oe_gui_ip) || isempty(params.oe_proc_node_id) || isempty(params.oe_rec_node_id) + msg = 'Open Ephys connection parameters cannot be empty.'; + log_message(handles, sprintf('ERROR: %s', msg)); errordlg(msg, 'Input Error'); + return; + end + else + if isempty(params.sglx_host_ip) || isnan(params.sglx_port) || isnan(params.sglx_probe_index) + msg = 'SpikeGLX connection parameters cannot be empty or non-numeric.'; + log_message(handles, sprintf('ERROR: %s', msg)); errordlg(msg, 'Input Error'); + return; + end + end + is_valid = true; + +%% ======================================================================= +% PATH AND DIRECTORY FUNCTIONS +% ======================================================================= +function [session_base, recording_path] = construct_session_paths(handles, params) + if isempty(params.experimenter) + subject_name = sprintf('sub-%s_id-%s', params.subject_id, params.rat_name); + else + subject_name = sprintf('sub-%s_id-%s_expmtr-%s', params.subject_id, params.rat_name, params.experimenter); + end + subject_base_path = fullfile(params.project_name, 'rawdata', subject_name); + local_subject_dir = fullfile(params.local_path, subject_base_path); + central_subject_dir = fullfile(params.central_path, subject_base_path); + new_ses_num = max(find_max_session_number(local_subject_dir), find_max_session_number(central_subject_dir)) + 1; + log_message(handles, sprintf('Last session found: %d. Creating new session: %d.', new_ses_num - 1, new_ses_num)); + session_datetime_str = char(datetime('now', 'Format', 'yyyyMMdd''T''HHmmss')); + session_folder_name = sprintf('ses-%02d_date-%s_dtype-ephys', new_ses_num, session_datetime_str); + session_base = fullfile(subject_base_path, session_folder_name); + recording_path = fullfile(params.local_path, session_base, 'ephys'); + log_message(handles, ['New session path determined: ' session_base]); + +function max_ses = find_max_session_number(base_path) + max_ses = 0; if ~exist(base_path, 'dir'), return; end + dir_contents = dir(fullfile(base_path, 'ses-*')); + if isempty(dir_contents), return; end + session_numbers = []; + for i = 1:length(dir_contents) + if dir_contents(i).isdir + token = regexp(dir_contents(i).name, '^ses-(\d+)', 'tokens'); + if ~isempty(token), session_numbers(end+1) = str2double(token{1}{1}); end + end + end + if ~isempty(session_numbers), max_ses = max(session_numbers); end + +function success = create_session_directories(handles, params,session_base_path) + success = false; + subfolders = {}; + if get(handles.cb_ephys, 'Value'), subfolders{end+1} = 'ephys'; end + if get(handles.cb_behav, 'Value'), subfolders{end+1} = 'behav'; end + if get(handles.cb_anat, 'Value'), subfolders{end+1} = 'anat'; end + if get(handles.cb_funcimg, 'Value'), subfolders{end+1} = 'funcimg'; end + try + for i = 1:length(subfolders) + local_target_path = fullfile(params.local_path, session_base_path, subfolders{i}); + log_message(handles, ['Creating local directory: ' local_target_path]); + if ~exist(local_target_path, 'dir'), mkdir(local_target_path); end + end + log_message(handles, 'All selected local directories created successfully.'); + success = true; + catch ME + msg = sprintf('Failed to create directories: %s', ME.message); + log_message(handles, ['ERROR: ' msg]); errordlg(msg, 'Directory Error'); + end + +function max_id = find_max_subject_id(base_path, project, rat) + max_id = 0; + search_path = fullfile(base_path, project, 'rawdata'); + if ~exist(search_path, 'dir'), return; end + dir_contents = dir(search_path); + if isempty(dir_contents), return; end + subject_ids = []; + pattern = sprintf('^sub-(\\d+)_id-%s', rat); + for i = 1:length(dir_contents) + if dir_contents(i).isdir + token = regexp(dir_contents(i).name, pattern, 'tokens'); + if ~isempty(token), subject_ids(end+1) = str2double(token{1}{1}); end + end + end + if ~isempty(subject_ids), max_id = max(subject_ids); end + +%% ======================================================================= +% HELPER & UTILITY FUNCTIONS +% ======================================================================= +function log_message(handles,logStr) + try + if ~isfield(handles, 'log_box') || ~isvalid(handles.log_box), return; end + current_text = get(handles.log_box, 'String'); + timestamp = datestr(now, '[HH:MM:SS] '); + new_line = [timestamp, logStr]; + new_text = [current_text; {new_line}]; + set(handles.log_box, 'String', new_text, 'Value', numel(new_text)); + drawnow; + catch + fprintf('%s: %s\n', datestr(now, '[HH:MM:SS]'), logStr); + end + +function toggle_button_color(~, ~, button_handle) + if ~isvalid(button_handle), return; end + currentColor = get(button_handle, 'BackgroundColor'); + if isequal(currentColor, [1, 0.4, 0.4]), set(button_handle, 'BackgroundColor', [1, 0.7, 0.4]); + else, set(button_handle, 'BackgroundColor', [1, 0.4, 0.4]); end + + +function commit_to_svn(handles, file_path_data,file_path_settings, root_dir) + +if isempty(file_path_data), return; end + if isempty(file_path_settings), return; end + [pname_data, fname_data, ~] = fileparts(file_path_data); + [pname_settings, fname_settings, ~] = fileparts(file_path_settings); + + configFilePath = fullfile(root_dir,'PASSWORD_CONFIG-DO_NOT_VERSIONCONTROL.mat'); + if ~exist(configFilePath, 'file') + log_message(handles, ['SVN commit failed: Password config file not found at ' configFilePath]); + return; + end + load(configFilePath, 'svn_user', 'svn_password'); + logmsg = char(strcat('automated commit from GUI for data and settings for ', {' '} ,fname_data,{'@'})); + % current_dir = cd; + cd(pname_data); + add_cmd_data = char(strcat('svn add', {' '}, fname_data, '.mat',{'@'})); + system(add_cmd_data); + + cd(pname_settings); + add_cmd_settings = char(strcat('svn add', {' '}, fname_settings, '.mat',{'@'})); + system(add_cmd_settings); + + commit_cmd = sprintf('svn ci --username="%s" --password="%s" -m "%s"', svn_user, svn_password, logmsg); + [status, ~] = system(commit_cmd); + + if status == 0 + log_message(handles, ['SVN commit successful for ' fname_data]); + else + log_message(handles, ['SVN commit FAILED for ' fname_data '.']); + end + + cd(fullfile(root_dir,'ExperPort')); + + + %% ======================================================================= +% DOCUMENTATION AND USAGE EXAMPLES +% ======================================================================= + +function display_usage_help() +% DISPLAY_USAGE_HELP - Display usage instructions and examples +% +% This function provides comprehensive usage documentation for the GUI + + fprintf('\n=== Neuropixels Recording & Behavior Controller Usage Guide ===\n\n'); + + fprintf('1. INITIALIZATION:\n'); + fprintf(' OpenEphys_Neuroblueprint_GUI(''init'');\n\n'); + + fprintf('2. WORKFLOW:\n'); + fprintf(' a) Select recording software (Open Ephys or SpikeGLX)\n'); + fprintf(' b) Configure behavior settings (protocol, experimenter, rat)\n'); + fprintf(' c) Set up NeuroBlueprint data paths\n'); + fprintf(' d) Configure probe settings (version, reference, bank/IMRO)\n'); + fprintf(' e) Set recording software connection parameters\n'); + fprintf(' f) Click "Load" to initialize systems\n'); + fprintf(' g) Click "Run" to start experiment\n'); + fprintf(' h) Click "Stop" to end experiment and save data\n\n'); + + fprintf('3. PROBE CONFIGURATION:\n'); + fprintf(' - Supports Neuropixels 1.0 (3 banks) and 2.0 (4 banks)\n'); + fprintf(' - Reference options: Tip or External\n'); + fprintf(' - Bank selection: Manual bank number or IMRO file\n'); + fprintf(' - Pre/post-session sampling across all banks\n\n'); + + fprintf('4. DATA ORGANIZATION:\n'); + fprintf(' - Follows NeuroBlueprint format\n'); + fprintf(' - Structure: project/rawdata/subject/session/datatype/\n'); + fprintf(' - Automatic session numbering\n'); + fprintf(' - SVN integration for version control\n\n'); + + fprintf('5. RECORDING SOFTWARE SUPPORT:\n'); + fprintf(' Open Ephys:\n'); + fprintf(' - HTTP API control\n'); + fprintf(' - Real-time parameter adjustment\n'); + fprintf(' - Acquisition and recording control\n\n'); + fprintf(' SpikeGLX:\n'); + fprintf(' - MATLAB SDK integration\n'); + fprintf(' - Run name management\n'); + fprintf(' - Recording enable/disable control\n\n'); + + fprintf('6. ERROR HANDLING:\n'); + fprintf(' - Comprehensive validation of inputs\n'); + fprintf(' - Automatic crash recovery for behavior protocols\n'); + fprintf(' - Detailed logging with timestamps\n'); + fprintf(' - Graceful fallbacks for system failures\n\n'); + + fprintf('For more information, see function documentation within the code.\n'); + fprintf('================================================================\n\n'); + diff --git a/ExperPort/Modules/@OpenEphys_Neuroblueprint/OpenEphys_Neuroblueprint.m b/ExperPort/Modules/@OpenEphys_Neuroblueprint/OpenEphys_Neuroblueprint.m index 542dd7f0..fd2ae8b1 100644 --- a/ExperPort/Modules/@OpenEphys_Neuroblueprint/OpenEphys_Neuroblueprint.m +++ b/ExperPort/Modules/@OpenEphys_Neuroblueprint/OpenEphys_Neuroblueprint.m @@ -1,3 +1,4 @@ +% function [obj, varargout] = OpenEphys_Neuroblueprint_GUI(varargin) % This is a class-based version of the GUI controller, structured similarly % to runrats.m. It manages the experimental workflow through different @@ -14,6 +15,7 @@ if nargin==0 || (nargin==1 && ischar(varargin{1}) && strcmp(varargin{1}, 'empty')) return; end + if isa(varargin{1}, mfilename) if length(varargin) < 2 || ~ischar(varargin{2}) error(['If called with a "%s" object as first arg, a second arg, a ' ... @@ -36,7 +38,7 @@ % This case is called once to create the GUI and initialize all parameters. % So that only the CPU-based software renderer instead of your graphics card - opengl software; + % opengl software; % Start Bpod if not already running if evalin('base', 'exist(''BpodSystem'', ''var'')') @@ -46,12 +48,17 @@ % --- State Variables as SoloParamHandles --- SoloParamHandle(obj, 'currentState', 'value', 'Load'); + SoloParamHandle(obj, 'behavState', 'value', 'Run'); % States for individual buttons + SoloParamHandle(obj, 'ephysState', 'value', 'Run'); SoloParamHandle(obj, 'oe_controller', 'value', []); SoloParamHandle(obj, 'behav_obj', 'value', []); SoloParamHandle(obj, 'blinking_timer', 'value', []); SoloParamHandle(obj, 'current_params', 'value', []); SoloParamHandle(obj, 'session_base_path', 'value', ''); SoloParamHandle(obj,'is_running','value',0); + SoloParamHandle(obj, 'probe_settings', 'value', struct('version', '2.0', 'reference', 'Tip', 'bank', 0, 'imro_path', '')); + SoloParamHandle(obj, 'probe_gui_handles', 'value', []); + scr = timer; set(scr,'Period', 0.2,'ExecutionMode','FixedRate','TasksToExecute',Inf,... 'BusyMode','drop','TimerFcn',[mfilename,'(''End_Continued'')']); @@ -73,24 +80,24 @@ handles.log_box = uicontrol('Style', 'edit', 'Units', 'normalized', 'Position', [0.65, 0.05, 0.32, 0.89], 'String', {'Log started...'}, 'Max', 10, 'Min', 1, 'HorizontalAlignment', 'left', 'Enable', 'inactive', 'BackgroundColor', [1, 1, 1]); % Panel 1: Behavior - p1 = uipanel('Title', '1. Behavior', 'FontSize', 12, 'FontWeight', 'bold', 'BorderType', 'etchedin', 'BorderWidth', 1, 'Units', 'normalized', 'Position', [0.02, 0.78, 0.6, 0.2]); + p1 = uipanel('Title', '1. Behavior', 'FontSize', 12, 'FontWeight', 'bold', 'BorderType', 'etchedin', 'BorderWidth', 1, 'Units', 'normalized', 'Position', [0.02, 0.82, 0.6, 0.16]); uicontrol(p1, 'Style', 'text', 'String', 'Protocol Name:', 'Units', 'normalized', 'Position', [0.05, 0.75, 0.22, 0.18], 'HorizontalAlignment', 'right'); handles.protocol_edit = uicontrol(p1, 'Style', 'edit', 'String', 'ArpitSoundCatContinuous', 'Units', 'normalized', 'Position', [0.3, 0.75, 0.5, 0.2]); handles.manual_test = uicontrol(p1, 'Style', 'checkbox', 'String', 'Manual Test', 'Value', 1, 'Units', 'normalized', 'Position', [0.81, 0.75, 0.18, 0.2]); uicontrol(p1, 'Style', 'text', 'String', 'Experimenter:', 'Units', 'normalized', 'Position', [0.05, 0.5, 0.22, 0.18], 'HorizontalAlignment', 'right'); - handles.exp_edit = uicontrol(p1, 'Style', 'edit', 'String', 'lida', 'Units', 'normalized', 'Position', [0.3, 0.5, 0.65, 0.2]); + handles.exp_edit = uicontrol(p1, 'Style', 'edit', 'String', 'lida', 'Units', 'normalized', 'Position', [0.3, 0.5, 0.65, 0.2], 'Callback', {@(h,e) feval(mfilename, obj, 'update_subject_id')}); uicontrol(p1, 'Style', 'text', 'String', 'Rat Name:', 'Units', 'normalized', 'Position', [0.05, 0.25, 0.22, 0.18], 'HorizontalAlignment', 'right'); - handles.rat_name_edit = uicontrol(p1, 'Style', 'edit', 'String', 'LP12', 'Units', 'normalized', 'Position', [0.3, 0.25, 0.65, 0.2]); + handles.rat_name_edit = uicontrol(p1, 'Style', 'edit', 'String', 'LP12', 'Units', 'normalized', 'Position', [0.3, 0.25, 0.65, 0.2], 'Callback', {@(h,e) feval(mfilename, obj, 'update_subject_id')}); uicontrol(p1, 'Style', 'text', 'String', 'Behav Code Path:', 'Units', 'normalized', 'Position', [0.01, 0.01, 0.28, 0.18], 'HorizontalAlignment', 'right'); handles.behav_edit = uicontrol(p1, 'Style', 'edit', 'String', 'C:\ratter', 'Units', 'normalized', 'Position', [0.3, 0.01, 0.5, 0.2]); handles.behav_browse = uicontrol(p1, 'Style', 'pushbutton', 'String', 'Browse...', 'Units', 'normalized', 'Position', [0.81, 0.01, 0.16, 0.22], 'Callback', {@(h,e) feval(mfilename, obj, 'browse_path', 'behav')}); % Panel 2: NeuroBlueprint Format - p2 = uipanel('Title', '2. NeuroBlueprint Format', 'FontSize', 12, 'FontWeight', 'bold', 'BorderType', 'etchedin', 'BorderWidth', 1, 'Units', 'normalized', 'Position', [0.02, 0.38, 0.6, 0.38]); + p2 = uipanel('Title', '2. NeuroBlueprint Format', 'FontSize', 12, 'FontWeight', 'bold', 'BorderType', 'etchedin', 'BorderWidth', 1, 'Units', 'normalized', 'Position', [0.02, 0.42, 0.6, 0.38]); uicontrol(p2, 'Style', 'text', 'String', 'Project Name (Root):', 'Units', 'normalized', 'Position', [0.01, 0.85, 0.28, 0.1], 'HorizontalAlignment', 'right'); handles.proj_edit = uicontrol(p2, 'Style', 'edit', 'String', 'sound_cat_rat', 'Units', 'normalized', 'Position', [0.3, 0.85, 0.65, 0.12]); uicontrol(p2, 'Style', 'text', 'String', 'Subject ID:', 'Units', 'normalized', 'Position', [0.01, 0.7, 0.28, 0.1], 'HorizontalAlignment', 'right'); - handles.sub_edit = uicontrol(p2, 'Style', 'edit', 'String', '003', 'Units', 'normalized', 'Position', [0.3, 0.7, 0.65, 0.12]); + handles.sub_edit = uicontrol(p2, 'Style', 'edit', 'String', '000', 'Units', 'normalized', 'Position', [0.3, 0.7, 0.65, 0.12]); uicontrol(p2, 'Style', 'text', 'String', 'Local Path:', 'Units', 'normalized', 'Position', [0.01, 0.55, 0.28, 0.1], 'HorizontalAlignment', 'right'); handles.local_edit = uicontrol(p2, 'Style', 'edit', 'String', 'C:\Ephys_Experiment_Data', 'Units', 'normalized', 'Position', [0.3, 0.55, 0.5, 0.12]); handles.local_browse = uicontrol(p2, 'Style', 'pushbutton', 'String', 'Browse...', 'Units', 'normalized', 'Position', [0.81, 0.55, 0.16, 0.13], 'Callback', {@(h,e) feval(mfilename, obj, 'browse_path', 'local')}); @@ -103,24 +110,35 @@ handles.cb_anat = uicontrol(p2, 'Style', 'checkbox', 'String', 'anat', 'Value', 1, 'Units', 'normalized', 'Position', [0.51, 0.05, 0.2, 0.15]); handles.cb_funcimg = uicontrol(p2, 'Style', 'checkbox', 'String', 'funcimg', 'Value', 0, 'Units', 'normalized', 'Position', [0.74, 0.05, 0.25, 0.15]); - % Panel 3: OpenEphys Settings - p3 = uipanel('Title', '3. OpenEphys Settings', 'FontSize', 12, 'FontWeight', 'bold', 'BorderType', 'etchedin', 'BorderWidth', 1, 'Units', 'normalized', 'Position', [0.02, 0.15, 0.6, 0.21]); - uicontrol(p3, 'Style', 'text', 'String', 'GUI IP Address:', 'Units', 'normalized', 'Position', [0.05, 0.65, 0.3, 0.2], 'HorizontalAlignment', 'right'); - handles.ip_edit = uicontrol(p3, 'Style', 'edit', 'String', '127.0.0.1', 'Units', 'normalized', 'Position', [0.38, 0.65, 0.25, 0.25]); - uicontrol(p3, 'Style', 'text', 'String', 'Processor ID:', 'Units', 'normalized', 'Position', [0.05, 0.35, 0.3, 0.2], 'HorizontalAlignment', 'right'); - handles.proc_node_edit = uicontrol(p3, 'Style', 'edit', 'String', '100', 'Units', 'normalized', 'Position', [0.38, 0.35, 0.25, 0.25]); - uicontrol(p3, 'Style', 'text', 'String', 'Record Node ID:', 'Units', 'normalized', 'Position', [0.05, 0.05, 0.3, 0.2], 'HorizontalAlignment', 'right'); - handles.rec_node_edit = uicontrol(p3, 'Style', 'edit', 'String', '101', 'Units', 'normalized', 'Position', [0.38, 0.05, 0.25, 0.25]); - - % Main Control Button - handles.control_button = uicontrol('Style', 'pushbutton', 'String', 'Load',... - 'Units', 'normalized', 'FontSize', 14, 'FontWeight', 'bold', ... - 'Position', [0.02, 0.03, 0.6, 0.1],... - 'BackgroundColor', [0.2, 0.6, 0.8], 'Callback', {@(h,e) feval(mfilename, obj, 'main_control_callback')}); + % Panel 3: Pre-Experiment Sampling + p3 = uipanel('Title', '3. Pre/Post-Experiment Sampling', 'FontSize', 12, 'FontWeight', 'bold', 'BorderType', 'etchedin', 'BorderWidth', 1, 'Units', 'normalized', 'Position', [0.02, 0.28, 0.6, 0.12]); + uicontrol(p3, 'Style', 'text', 'String', 'Duration/Bank (s):', 'Units', 'normalized', 'Position', [0.01, 0.6, 0.25, 0.25], 'HorizontalAlignment', 'right'); + handles.sample_duration = uicontrol(p3, 'Style', 'edit', 'String', '60', 'Units', 'normalized', 'Position', [0.27, 0.6, 0.1, 0.3]); + handles.target_display = uicontrol(p3, 'Style', 'text', 'String', 'Target: Bank 0', 'Units', 'normalized', 'Position', [0.38, 0.6, 0.3, 0.25], 'HorizontalAlignment', 'right', 'FontWeight', 'bold'); + handles.probe_button = uicontrol(p3, 'Style', 'pushbutton', 'String', 'Probe Setting', 'Units', 'normalized', 'Position', [0.7, 0.55, 0.28, 0.4], 'FontSize', 10, 'Callback', {@(h,e) feval(mfilename, obj, 'open_probe_gui')}); + handles.sample_button = uicontrol(p3, 'Style', 'pushbutton', 'String', 'Start Sample Recording', 'Units', 'normalized', 'Position', [0.05, 0.1, 0.9, 0.4], 'FontSize', 12, 'FontWeight', 'bold', 'BackgroundColor', [0.8, 0.7, 1], 'Callback', {@(h,e) feval(mfilename, obj, 'sample_recording_wrapper')}); + + % Panel 4: OpenEphys Settings + p4 = uipanel('Title', '4. OpenEphys Settings', 'FontSize', 12, 'FontWeight', 'bold', 'BorderType', 'etchedin', 'BorderWidth', 1, 'Units', 'normalized', 'Position', [0.02, 0.15, 0.6, 0.11]); + uicontrol(p4, 'Style', 'text', 'String', 'GUI IP:', 'Units', 'normalized', 'Position', [0.01, 0.3, 0.15, 0.4], 'HorizontalAlignment', 'right'); + handles.ip_edit = uicontrol(p4, 'Style', 'edit', 'String', '127.0.0.1', 'Units', 'normalized', 'Position', [0.17, 0.25, 0.15, 0.5]); + uicontrol(p4, 'Style', 'text', 'String', 'Proc ID:', 'Units', 'normalized', 'Position', [0.33, 0.3, 0.15, 0.4], 'HorizontalAlignment', 'right'); + handles.proc_node_edit = uicontrol(p4, 'Style', 'edit', 'String', '100', 'Units', 'normalized', 'Position', [0.49, 0.25, 0.15, 0.5]); + uicontrol(p4, 'Style', 'text', 'String', 'Rec ID:', 'Units', 'normalized', 'Position', [0.65, 0.3, 0.15, 0.4], 'HorizontalAlignment', 'right'); + handles.rec_node_edit = uicontrol(p4, 'Style', 'edit', 'String', '101', 'Units', 'normalized', 'Position', [0.81, 0.25, 0.15, 0.5]); + % --- Control Buttons Panel --- + p5 = uipanel('Title', 'Controls', 'FontSize', 12, 'FontWeight', 'bold', 'BorderType', 'etchedin', 'BorderWidth', 1, 'Units', 'normalized', 'Position', [0.02, 0.02, 0.6, 0.11]); + handles.control_button = uicontrol(p5, 'Style', 'pushbutton', 'String', 'Load', 'Units', 'normalized', 'FontSize', 14, 'FontWeight', 'bold', 'Position', [0.02, 0.1, 0.3, 0.8], 'BackgroundColor', [0.2, 0.6, 0.8], 'Callback', {@(h,e) feval(mfilename, obj, 'main_control_callback')}); + handles.behav_button = uicontrol(p5, 'Style', 'pushbutton', 'String', 'Run Behav', 'Units', 'normalized', 'FontSize', 12, 'Position', [0.35, 0.1, 0.3, 0.8], 'BackgroundColor', [1, 0.8, 0.6], 'Callback', {@(h,e) feval(mfilename, obj, 'behav_control_callback')}); + handles.ephys_button = uicontrol(p5, 'Style', 'pushbutton', 'String', 'Run Ephys', 'Units', 'normalized', 'FontSize', 12, 'Position', [0.68, 0.1, 0.3, 0.8], 'BackgroundColor', [0.8, 0.6, 1], 'Callback', {@(h,e) feval(mfilename, obj, 'ephys_control_callback')}); + % Store the UI handles struct in an SPH for consistent access SoloParamHandle(obj, 'ui_handles', 'value', handles); + % Run initial subject ID check on startup + feval(mfilename, obj, 'update_subject_id'); + % ========================================================================= % CASE MAIN_CONTROL_CALLBACK % ========================================================================= @@ -129,6 +147,7 @@ case 'Load', feval(mfilename, obj, 'load_sequence'); case 'Run', feval(mfilename, obj, 'run_sequence'); case 'Stop', feval(mfilename, obj, 'stop_sequence'); + case 'PostExperiment', feval(mfilename, obj, 'reset_to_load_state'); end % ========================================================================= @@ -139,6 +158,7 @@ handles = value(ui_handles); log_message(handles, '--- LOAD sequence initiated ---'); set(handles.control_button, 'Enable', 'off', 'String', 'Loading...'); + set(handles.sample_button, 'Enable', 'off'); params = get_all_params(handles); if ~validate_inputs(handles, params) @@ -148,18 +168,8 @@ current_params.value = params; - try - log_message(handles, 'Connecting to Open Ephys GUI...'); - oe_controller.value = OpenEphysHTTPServer(params.gui_ip, 37497); - log_message(handles, 'Connection successful.'); - catch ME - log_message(handles, ['ERROR: Failed to connect to OE. Is it running? Details: ' ME.message]); - getReport(ME, 'extended', 'hyperlinks', 'on'); - errordlg('Failed to connect to Open Ephys GUI.', 'Connection Error'); - feval(mfilename, obj, 'reset_to_load_state'); - return; - end + [session_path, oe_save_path] = construct_paths(handles, params); if isempty(session_path) || ~create_directories(handles, params, session_path) feval(mfilename, obj, 'reset_to_load_state'); @@ -167,22 +177,23 @@ end session_base_path.value = session_path; - if get(handles.cb_ephys, 'Value') - try - log_message(handles, ['Setting OE record path to: ' oe_save_path]); - value(oe_controller).setRecordPath(params.rec_node_id, oe_save_path); - log_message(handles, 'Successfully set OE record path.'); - catch ME - log_message(handles, ['ERROR setting OE path: ' ME.message]); - getReport(ME, 'extended', 'hyperlinks', 'on'); - errordlg('Could not set Open Ephys record path.', 'API Error'); - feval(mfilename, obj, 'reset_to_load_state'); - return; - end - end + % if get(handles.cb_ephys, 'Value') + % try + % log_message(handles, ['Setting OE record path to: ' oe_save_path]); + % value(oe_controller).setRecordPath(params.rec_node_id, oe_save_path); + % log_message(handles, 'Successfully set OE record path.'); + % catch ME + % log_message(handles, ['ERROR setting OE path: ' ME.message]); + % getReport(ME, 'extended', 'hyperlinks', 'on'); + % errordlg('Could not set Open Ephys record path.', 'API Error'); + % feval(mfilename, obj, 'reset_to_load_state'); + % return; + % end + % end feval(mfilename, obj, 'initialize_behavior_system'); + case 'initialize_behavior_system' params = value(current_params); try @@ -248,24 +259,48 @@ % handles = feval(mfilename, obj, 'get_ui_handles'); handles = value(ui_handles); log_message(handles, '--- RUN sequence initiated ---'); + set(handles.sample_button, 'Enable', 'off'); + % --- EPHYS CONNECTION --- if get(handles.cb_ephys, 'Value') try + if isempty(value(oe_controller)) + log_message(handles, 'Connecting to Open Ephys GUI...'); + oe_controller.value = OpenEphysHTTPServer(params.gui_ip, 37497); + if isempty(value(oe_controller)), error('Failed to create OE controller.'); end + end + + log_message(handles, 'Applying probe settings for main experiment...'); + settings = value(probe_settings); + value(oe_controller).setParameters(params.proc_node_id, 0, 'Reference', settings.reference); + if ~isempty(settings.imro_path) + value(oe_controller).config(params.proc_node_id, ['LOADIMRO ' settings.imro_path]); + else + value(oe_controller).setParameters(params.proc_node_id, 0, 'bank', settings.bank); + end + + main_ephys_path = fullfile(params.local_path, value(session_base_path), 'ephys'); + log_message(handles, ['Confirming main record path: ' main_ephys_path]); + value(oe_controller).setRecordPath(params.rec_node_id, main_ephys_path); + pause(1); log_message(handles, 'Starting OE acquisition and recording...'); value(oe_controller).acquire(); pause(1); value(oe_controller).record(); log_message(handles, 'Open Ephys recording is LIVE.'); + ephysState.value = 'Stop'; + set(handles.ephys_button, 'String', 'Stop Ephys', 'BackgroundColor', [1 0.6 0.6]); + catch ME log_message(handles, ['ERROR starting OE recording: ' ME.message]); getReport(ME, 'extended', 'hyperlinks', 'on'); - errordlg('Failed to start Open Ephys recording.', 'API Error'); + errordlg('Failed to start Open Ephys recording. Check command window for details.', 'API Error'); return; end else log_message(handles, 'Ephys checkbox not selected. No OE recording started.'); end - + currentState.value = 'Stop'; set(handles.control_button, 'String', 'Stop'); feval(mfilename, obj, 'start_blinking'); @@ -310,8 +345,83 @@ end end - log_message(handles, '--- Experiment finished. Ready for new session. ---'); - feval(mfilename, obj, 'reset_to_load_state'); + log_message(handles, '--- Experiment finished. Post-session sampling is now available. ---'); + + currentState.value = 'PostExperiment'; + set(handles.control_button, 'String', 'Start New Experiment', 'BackgroundColor', [0.2, 0.8, 0.6]); + set(handles.sample_button, 'Enable', 'on'); + + + % ========================================================================= + % INDIVIDUAL CONTROL CALLBACKS + % ========================================================================= + case 'behav_control_callback' + switch value(behavState) + case 'Run', feval(mfilename, obj, 'behav_control', 'load_run'); + case 'Stop', feval(mfilename, obj, 'behav_control', 'end'); + end + + case 'ephys_control_callback' + switch value(ephysState) + case 'Run', feval(mfilename, obj, 'run_ephys_only'); + case 'Stop', feval(mfilename, obj, 'stop_ephys_only'); + end + + case 'run_ephys_only' + params = value(current_params); + % handles = feval(mfilename, obj, 'get_ui_handles'); + handles = value(ui_handles); + log_message(handles, '--- Starting Ephys Recording Individually ---'); + set(handles.ephys_button, 'Enable', 'off'); + + try + if isempty(value(oe_controller)), oe_controller.value = OpenEphysHTTPServer(params.gui_ip, 37497); end + log_message(handles, 'Connected to Open Ephys GUI...'); + + % Apply probe settings + log_message(handles, 'Applying probe settings...'); + settings = value(probe_settings); + log_message(handles, ['- Setting Reference to: ' settings.reference]); + value(oe_controller).setParameters(params.proc_node_id, 0, 'Reference', settings.reference); + if ~isempty(settings.imro_path) + log_message(handles, ['- Loading IMRO file: ' settings.imro_path]); + config_string = ['LOADIMRO ' settings.imro_path]; + value(oe_controller).config(params.proc_node_id, config_string); + else + log_message(handles, ['- Setting Bank to: ' num2str(settings.bank)]); + value(oe_controller).setParameters(params.proc_node_id, 0, 'bank', settings.bank); + end + log_message(handles, 'Probe settings applied successfully.'); + + main_ephys_path = fullfile(params.local_path, value(session_base_path), 'ephys'); + + % Set Record Path and Start Recording + value(oe_controller).setRecordPath(params.rec_node_id, main_ephys_path); pause(1); + value(oe_controller).acquire(); pause(1); value(oe_controller).record(); + log_message(handles, 'Ephys recording is LIVE.'); + ephysState.value = 'Stop'; + set(handles.ephys_button, 'String', 'Stop Ephys', 'BackgroundColor', [1 0.6 0.6]); + catch ME + log_message(handles, ['ERROR starting ephys: ' ME.message]); + getReport(ME, 'extended', 'hyperlinks', 'on'); + end + set(handles.ephys_button, 'Enable', 'on'); + + case 'stop_ephys_only' + % handles = feval(mfilename, obj, 'get_ui_handles'); + handles = value(ui_handles); + log_message(handles, '--- Stopping Ephys Recording Individually ---'); + set(handles.ephys_button, 'Enable', 'off'); + try + if ~isempty(value(oe_controller)), value(oe_controller).idle(); end + log_message(handles, 'Ephys recording stopped.'); + ephysState.value = 'Run'; + set(handles.ephys_button, 'String', 'Run Ephys', 'BackgroundColor', [0.8, 0.6, 1]); + catch ME + log_message(handles, ['ERROR stopping ephys: ' ME.message]); + getReport(ME, 'extended', 'hyperlinks', 'on'); + end + set(handles.ephys_button, 'Enable', 'on'); % ========================================================================= % BEHAVIOR CONTROL ACTIONS @@ -323,6 +433,7 @@ handles = value(ui_handles); switch sub_action + case 'load_main_protocol' experimenter = args{1}; ratname = args{2}; protocol_name = args{3}; video_save_dir = args{4}; behav_path = args{5}; @@ -339,29 +450,49 @@ case 'load_protocol_after_crash' experimenter = args{1}; ratname = args{2}; protocol_name = args{3}; - behav_path = args{4}; + video_save_dir = args{4}; behav_path = args{5}; log_message(handles, ['Loading protocol: ' protocol_name]); dispatcher('set_protocol', protocol_name); rath = get_sphandle('name', 'ratname', 'owner', protocol_name); exph = get_sphandle('name', 'experimenter', 'owner', protocol_name); rath{1}.value = ratname; exph{1}.value = experimenter; protobj = eval(protocol_name); + % Loading the temporary saved Data file - today_date = char(datetime('now','format','yyMMdd')); - temp_data_dir = fullfile(behav_path,'SoloData','Data',experimenter,ratname); - temp_data_file = sprintf('data_@%s_%s_%s_%s_ASV.mat',protocol_name,experimenter,ratname,today_date); - if isfile(fullfile(temp_data_dir,temp_data_file)) - dispatcher('runstart_disable'); - load_soloparamvalues(ratname, 'experimenter', experimenter,... - 'owner', protocol_name, 'interactive', 0,'data_file',fullfile(temp_data_dir,temp_data_file)); - dispatcher('runstart_enable'); + try + log_message(handles, ['Loading previous data for ' ratname]); + today_date = char(datetime('now','format','yyMMdd')); + temp_data_dir = fullfile(behav_path,'SoloData','Data',experimenter,ratname); + temp_data_file = sprintf('data_@%s_%s_%s_%s_ASV.mat',protocol_name,experimenter,ratname,today_date); + if isfile(fullfile(temp_data_dir,temp_data_file)) + dispatcher('runstart_disable'); + load_soloparamvalues(ratname, 'experimenter', experimenter,... + 'owner', protocol_name, 'interactive', 0,'data_file',fullfile(temp_data_dir,temp_data_file)); + dispatcher('runstart_enable'); + end + if ~dispatcher('is_running'), pop_history(class(protobj), 'include_non_gui', 1); feval(protocol_name, protobj, 'prepare_next_trial'); end + catch % else loading the saved setting file + log_message(handles, ['Loading settings for ' ratname]); + [~, sfile] = load_solouiparamvalues(ratname, 'experimenter', experimenter, 'owner', class(protobj), 'interactive', 0); + feval(protocol_name, protobj, 'set_setting_params', ratname, experimenter, sfile, char(datetime('now')), video_save_dir); + if ~dispatcher('is_running'), pop_history(class(protobj), 'include_non_gui', 1); feval(protocol_name, protobj, 'prepare_next_trial'); end end - if ~dispatcher('is_running'), pop_history(class(protobj), 'include_non_gui', 1); feval(protocol_name, protobj, 'prepare_next_trial'); end - + + case 'load_run' + set(handles.behav_button, 'Enable', 'off'); + log_message(handles, '--- STARTING BEHAV PROTOCOL ---'); + params = value(current_params); + video_save_dir = fullfile(params.local_path, value(session_base_path), 'behav'); + feval(mfilename, obj, 'behav_control', 'load_protocol_after_crash', params.experimenter, params.rat_name, params.protocol_name, video_save_dir, params.behav_path); + feval(mfilename, obj, 'behav_control', 'run', params.protocol_name); + log_message(handles, '--- START COMPLETE: Behavior protocol started ---'); + set(handles.behav_button, 'Enable', 'on'); + case 'crashed' log_message(handles, '--- BEHAVIOR CRASH RECOVERY INITIATED ---'); params = value(current_params); - feval(mfilename, obj, 'behav_control', 'load_protocol_after_crash', params.experimenter, params.rat_name, params.protocol_name, params.behav_path); + video_save_dir = fullfile(params.local_path, value(session_base_path), 'behav'); + feval(mfilename, obj, 'behav_control', 'load_protocol_after_crash', params.experimenter, params.rat_name, params.protocol_name, video_save_dir, params.behav_path); feval(mfilename, obj, 'behav_control', 'run', params.protocol_name); log_message(handles, '--- RECOVERY COMPLETE: Behavior protocol restarted ---'); @@ -371,17 +502,19 @@ feval(protocol_name, protobj, 'start_recording'); log_message(handles, 'Starting dispatcher to run trials...'); is_running.value = 1; + behavState.value = 'Stop'; + set(handles.behav_button, 'String', 'Stop Behav', 'BackgroundColor', [1 0.6 0.6]); dispatcher(value(behav_obj), 'Run'); case 'end' + set(handles.behav_button, 'Enable', 'off'); protocol_name = args{1}; root_dir = args{2}; behav_copy_dir = args{3}; log_message(handles, 'Stopping dispatcher...'); dispatcher(value(behav_obj), 'Stop'); %Let's pause until we know dispatcher is done running - set(value(stopping_complete_timer), 'Period', 2,'TimerFcn', {@(h,e) feval(mfilename, obj, 'behav_control','end_continued',protocol_name, root_dir,behav_copy_dir)}); - %set(value(stopping_complete_timer),'TimerFcn',[mfilename,obj,'behav_control','(''end_continued'');']); + set(value(stopping_complete_timer), 'Period', 0.8,'TimerFcn', {@(h,e) feval(mfilename, obj, 'behav_control','end_continued',protocol_name, root_dir,behav_copy_dir)}); start(value(stopping_complete_timer)); case 'end_continued' @@ -406,16 +539,8 @@ log_message(handles, 'Committing data and settings to SVN...'); commit_to_svn(handles, data_file, settings_file, root_dir); dispatcher('set_protocol', ''); - dispatcher('close'); - - % wait sometime for dispatcher to stop using a timer object - t = timer; - t.StartDelay = 1; - t.TimerFcn = @(~,~) log_message('waiting for before copying'); - start(t);% Start the timer (it runs in the background) - wait(t); % wait() pauses the script until the timer object is done. - delete(t);% Clean up the timer from memory - + + data_file = [data_file '.mat']; % Copy the data file to the folder saving ephys data [status, msg] = copyfile(data_file, destination_path); % Check if the copy was successful @@ -425,6 +550,12 @@ log_message(handles,['Error copying Data file: ' msg]); end + behavState.value = 'Run'; + set(handles.behav_button, 'String', 'Run Behav', 'BackgroundColor', [1, 0.8, 0.6]); + + % Saving the Log file + feval(mfilename, obj, 'save_log_file'); % Save the log file + set(handles.behav_button, 'Enable', 'on'); end case 'create_svn_data_dir' @@ -477,7 +608,11 @@ case 'is_running' - obj = logical(value(is_running)); + if exist('is_running','var') == 1 + obj = logical(value(is_running)); + else + obj = 0; + end % ========================================================================= % OTHER ACTIONS (CLOSE, RESET, etc.) @@ -486,16 +621,26 @@ % handles = feval(mfilename, obj, 'get_ui_handles'); handles = value(ui_handles); currentState.value = 'Load'; + behavState.value = 'Run'; + ephysState.value = 'Run'; set(handles.control_button, 'Enable', 'on', 'String', 'Load', 'BackgroundColor', [0.2, 0.6, 0.8]); oe_controller.value = []; behav_obj.value = []; current_params.value = []; - case 'close' - feval(mfilename, obj, 'stop_blinking'); - if ishandle(value(myfig)), delete(value(myfig)); end - delete_sphandle('owner', ['^@' mfilename '$']); - + case 'close' + try + feval(mfilename, obj, 'stop_blinking'); + if ishandle(value(myfig)), delete(value(myfig)); end + delete_sphandle('owner', ['^@' mfilename '$']); + dispatcher(value(behav_obj),'close'); + obj = []; + catch + if ishandle(value(myfig)), delete(value(myfig)); end + delete_sphandle('owner', ['^@' mfilename '$']); + obj = []; + end + case 'get_ui_handles' varargout{1} = value(ui_handles); @@ -530,8 +675,256 @@ getReport(ME, 'extended', 'hyperlinks', 'on'); errordlg('Automatic recovery failed. Please stop the experiment manually.', 'Recovery Failed'); end + + + %% SAMPLE EPHYS RECORDINGS + case 'sample_recording_wrapper' + % This wrapper determines if we are doing a pre- or post-session sample + if strcmp(value(currentState), 'PostExperiment') + feval(mfilename, obj, 'sample_recording', 'post_session'); + else + feval(mfilename, obj, 'sample_recording', 'pre_session'); + end -end + + case 'sample_recording' + prefix = varargin{1}; % 'pre_session' or 'post_session' + %handles = feval(mfilename, obj, 'get_ui_handles'); + handles = value(ui_handles); + log_message(handles, ['--- ' upper(prefix) ' SAMPLE RECORDING INITIATED ---']); + set([handles.sample_button, handles.control_button], 'Enable', 'off', 'String', 'Sampling...'); + drawnow; + + params = get_all_params(handles); + + % Ensure we have an OE connection + if isempty(value(oe_controller)) + try + log_message(handles, 'Connecting to Open Ephys GUI for sampling...'); + oe_controller.value = OpenEphysHTTPServer(params.gui_ip, 37497); + if isempty(value(oe_controller)), error('Failed to connect.'); end + catch ME + log_message(handles, ['ERROR: Could not connect to OE: ' ME.message]); + errordlg('Could not connect to Open Ephys for sampling.', 'Connection Error'); + feval(mfilename, obj, 'reset_to_load_state'); + return; + end + end + + % Create directories if they don't exist yet + if isempty(value(session_base_path)) + [session_path, ~] = construct_paths(handles, params); + if isempty(session_path) || ~create_directories(handles, params, session_path) + feval(mfilename, obj, 'reset_to_load_state'); + return; + end + session_base_path.value = session_path; + end + + sample_dir_name = [prefix '_sample_recording']; + sample_save_path = fullfile(params.local_path, value(session_base_path), 'ephys', sample_dir_name); + if ~exist(sample_save_path, 'dir'), mkdir(sample_save_path); end + + duration = str2double(get(handles.sample_duration, 'String')); + settings = value(probe_settings); + + try + log_message(handles, ['- Setting Reference to: ' settings.reference]); + value(oe_controller).setParameters(params.proc_node_id, 0, 'Reference', settings.reference); + + if strcmp(settings.version, '1.0'), num_banks = 3; else, num_banks = 4; end + + for bank = 0:(num_banks - 1) + log_message(handles, sprintf('Switching to Neuropixel Bank %d...', bank)); + value(oe_controller).setParameters(params.proc_node_id, 0, 'bank', bank); + pause(1); % Allow time for the setting to apply + log_message(handles, sprintf('Recording Bank %d for %d seconds...', bank, duration)); + value(oe_controller).setRecordPath(params.rec_node_id, sample_save_path); + pause(1); % Allow time for the setting to apply + value(oe_controller).acquire(duration); + pause(1); % Allow acquisition to stabilize + value(oe_controller).record(duration); + value(oe_controller).idle(); % Stop recording, but keep acquiring + log_message(handles, sprintf('Finished recording Bank %d.', bank)); + pause(1); + end + + log_message(handles, 'Restoring probe settings for main experiment...'); + if ~isempty(settings.imro_path) + config_string = ['LOADIMRO ' settings.imro_path]; + value(oe_controller).config(params.proc_node_id, config_string); + else + value(oe_controller).setParameters(params.proc_node_id, 0, 'bank', settings.bank); + end + + log_message(handles, '--- SAMPLE RECORDING COMPLETE ---'); + + catch ME + log_message(handles, ['ERROR during sample recording: ' ME.message]); + getReport(ME, 'extended', 'hyperlinks', 'on'); + errordlg('An error occurred during sample recording. Check command window.', 'Sampling Error'); + value(oe_controller).idle(); % Attempt to stop recording + end + + % Reset button states + if strcmp(value(currentState), 'PostExperiment') + set(handles.control_button, 'Enable', 'on', 'String', 'Start New Experiment'); + set(handles.sample_button, 'Enable', 'on', 'String', 'Start Sample Recording'); + + % Save/overwrite the log file after sampling is complete + feval(mfilename, obj, 'save_log_file'); + else + set(handles.control_button, 'Enable', 'on', 'String', 'Load'); + set(handles.sample_button, 'Enable', 'on', 'String', 'Start Sample Recording'); + end + + + + case 'open_probe_gui' + % This action opens the separate GUI for probe settings. + %handles = feval(mfilename, obj, 'get_ui_handles'); + handles = value(ui_handles); + log_message(handles, 'Opening probe settings GUI...'); + + probe_fig = figure('Name', 'Neuropixel Probe Settings', 'Position', [300 300 450 250], ... + 'MenuBar', 'none', 'ToolBar', 'none', 'NumberTitle', 'off', 'Resize', 'off'); + + p_handles = struct(); + + % Probe Version + p_handles.version_group = uibuttongroup(probe_fig, 'Title', 'Probe Version', 'Position', [0.05 0.7 0.9 0.25]); + uicontrol(p_handles.version_group, 'Style', 'radiobutton', 'String', 'NP 1.0 (3 Banks)', 'Position', [10 5 150 25], 'Tag', '1.0'); + uicontrol(p_handles.version_group, 'Style', 'radiobutton', 'String', 'NP 2.0 (4 Banks)', 'Position', [200 5 150 25], 'Tag', '2.0'); + + % Reference Selection + p_handles.ref_group = uibuttongroup(probe_fig, 'Title', 'Reference', 'Position', [0.05 0.4 0.4 0.25]); + uicontrol(p_handles.ref_group, 'Style', 'radiobutton', 'String', 'Tip', 'Position', [10 5 100 25], 'Tag', 'Tip'); + uicontrol(p_handles.ref_group, 'Style', 'radiobutton', 'String', 'External', 'Position', [100 5 100 25], 'Tag', 'External'); + + % Bank Selection Panel (can be disabled) + p_handles.bank_panel = uipanel(probe_fig, 'Title', 'Target Bank', 'Position', [0.5 0.4 0.45 0.25]); + uicontrol(p_handles.bank_panel, 'Style', 'text', 'String', 'Bank:', 'Position', [10 5 40 20]); + p_handles.bank_edit = uicontrol(p_handles.bank_panel, 'Style', 'edit', 'String', '0', 'Position', [60 5 50 25]); + + % IMRO File + uicontrol(probe_fig, 'Style', 'text', 'String', 'IMRO File:', 'Position', [20 80 60 20]); + p_handles.imro_text = uicontrol(probe_fig, 'Style', 'text', 'String', 'None selected', 'Position', [90 80 280 20], 'HorizontalAlignment', 'left'); + uicontrol(probe_fig, 'Style', 'pushbutton', 'String', 'Browse...', 'Position', [20 45 100 30], ... + 'Callback', {@(h,e) feval(mfilename, obj, 'browse_imro', p_handles)}); + uicontrol(probe_fig, 'Style', 'pushbutton', 'String', 'Clear IMRO', 'Position', [130 45 100 30], ... + 'Callback', {@(h,e) feval(mfilename, obj, 'clear_imro', p_handles)}); + + % Apply Button + uicontrol(probe_fig, 'Style', 'pushbutton', 'String', 'Apply & Close', 'Position', [250 15 180 30], ... + 'FontWeight', 'bold', 'Callback', {@(h,e) feval(mfilename, obj, 'apply_probe_settings', p_handles)}); + + probe_gui_handles.value = p_handles; + + case 'browse_imro' + p_handles = varargin{1}; + [file, path] = uigetfile('*.imro', 'Select IMRO File'); + if isequal(file, 0) || isequal(path, 0) + % User cancelled + return; + else + full_path = fullfile(path, file); + set(p_handles.imro_text, 'String', full_path); + end + + case 'clear_imro' + p_handles = varargin{1}; + set(p_handles.imro_text, 'String', 'None selected'); + % Re-enable bank selection + set(findobj(p_handles.bank_panel, '-property', 'Enable'), 'Enable', 'on'); + + + case 'apply_probe_settings' + p_handles = varargin{1}; + + %handles = feval(mfilename, obj, 'get_ui_handles'); + handles = value(ui_handles); + + % Get values from the probe GUI + settings.version = get(get(p_handles.version_group, 'SelectedObject'), 'Tag'); + settings.reference = get(get(p_handles.ref_group, 'SelectedObject'), 'Tag'); + settings.bank = str2double(get(p_handles.bank_edit, 'String')); + settings.imro_path = get(p_handles.imro_text, 'String'); + if strcmp(settings.imro_path, 'None selected'), settings.imro_path = ''; end + + % Save settings to the main GUI's SPH + probe_settings.value = settings; + + % Update the main GUI display + if ~isempty(settings.imro_path) + set(handles.target_display, 'String', 'Target: IMRO File'); + else + set(handles.target_display, 'String', ['Target: Bank ' num2str(settings.bank)]); + end + + log_message(handles, 'Probe settings saved.'); + + % Close the probe GUI + close(p_handles.ref_group.Parent); + probe_gui_handles.value = []; + + case 'update_subject_id' + % handles = feval(mfilename, obj, 'get_ui_handles'); + handles = value(ui_handles); + params = get_all_params(handles); + + % Don't run if essential fields are empty + if isempty(params.local_path) || isempty(params.central_path) || isempty(params.project_name) || isempty(params.rat_name) + return; + end + + max_local_id = find_max_subject_id(params.local_path, params.project_name, params.rat_name); + max_central_id = find_max_subject_id(params.central_path, params.project_name, params.rat_name); + + final_id = max(max_local_id, max_central_id); + + if final_id > 0 + log_message(handles, ['Found existing Subject ID: ' num2str(final_id) '. Populating field.']); + set(handles.sub_edit, 'String', sprintf('%03d', final_id)); % Format as 3 digits + else + log_message(handles, 'No existing Subject ID found for this rat. Please enter a new ID.'); + end + + case 'save_log_file' + + %handles = feval(mfilename, obj, 'get_ui_handles'); + handles = value(ui_handles); + params = value(current_params); + session_path = value(session_base_path); + + if isempty(session_path) + log_message(handles, 'WARNING: Cannot save log file. Session path not set.'); + return; + end + + log_path = fullfile(params.local_path, session_path, 'behav'); + if ~exist(log_path, 'dir') + log_message(handles, ['WARNING: Behavior folder not found. Cannot save log. Path: ' log_path]); + return; + end + + log_file_path = fullfile(log_path, 'session_log.txt'); + + try + log_content = get(handles.log_box, 'String'); + fid = fopen(log_file_path, 'w'); + if fid == -1 + error('Could not open file for writing.'); + end + for i = 1:length(log_content) + fprintf(fid, '%s\n', log_content{i}); + end + fclose(fid); + log_message(handles, ['Log file saved successfully to: ' log_file_path]); + catch ME + log_message(handles, ['ERROR: Could not save log file. Details: ' ME.message]); + end + +end % end of action return; @@ -601,7 +994,8 @@ function log_message(handles, logStr) function is_valid = validate_inputs(handles, params) is_valid = false; fields = fieldnames(params); - for i = 1:length(fields) + for i = 1:length(fields) + if strcmp(fields{i}, 'experimenter'), continue; end % Make experimenter optional if strcmp(fields{i}, 'central_path') && isempty(params.(fields{i})), continue; end if isempty(params.(fields{i})) msg = ['Field "' strrep(fields{i}, '_', ' ') '" cannot be empty.']; @@ -618,7 +1012,12 @@ function log_message(handles, logStr) function [session_base, oe_path] = construct_paths(handles, params) - subject_name = sprintf('sub-%s_id-%s_expmtr-%s', params.subject_id, params.rat_name, params.experimenter); + + if isempty(params.experimenter) + subject_name = sprintf('sub-%s_id-%s', params.subject_id, params.rat_name); + else + subject_name = sprintf('sub-%s_id-%s_expmtr-%s', params.subject_id, params.rat_name, params.experimenter); + end subject_base_path = fullfile(params.project_name, 'rawdata', subject_name); local_subject_dir = fullfile(params.local_path, subject_base_path); central_subject_dir = fullfile(params.central_path, subject_base_path); @@ -630,6 +1029,30 @@ function log_message(handles, logStr) oe_path = fullfile(params.local_path, session_base, 'ephys'); log_message(handles, ['New session path determined: ' session_base]); +function max_id = find_max_subject_id(base_path, project, rat) + max_id = 0; + search_path = fullfile(base_path, project, 'rawdata'); + if ~exist(search_path, 'dir'), return; end + + dir_contents = dir(search_path); + if isempty(dir_contents), return; end + + subject_ids = []; + pattern = sprintf('^sub-(\\d+)_id-%s', rat); % Match based on rat name only + + for i = 1:length(dir_contents) + if dir_contents(i).isdir + token = regexp(dir_contents(i).name, pattern, 'tokens'); + if ~isempty(token) + subject_ids(end+1) = str2double(token{1}{1}); + end + end + end + + if ~isempty(subject_ids) + max_id = max(subject_ids); + end + function max_ses = find_max_session_number(base_path) max_ses = 0; if ~exist(base_path, 'dir'), return; end diff --git a/ExperPort/Modules/@dispatcher/RunningSection.m b/ExperPort/Modules/@dispatcher/RunningSection.m index 00912634..56735e1d 100644 --- a/ExperPort/Modules/@dispatcher/RunningSection.m +++ b/ExperPort/Modules/@dispatcher/RunningSection.m @@ -31,7 +31,8 @@ SoloParamHandle(obj, 'stop_after_next_update', 'value', 0); SoloParamHandle(obj, 'stopping_process_completed', 'value', 1); SoloFunctionAddVars('runrats','func_owner','@runrats','ro_args','stopping_process_completed'); % This is a bit unpleasant. We give ro access to the flag denoting the stop process as complete to runrats so that runrats can wait on it using a timer. :P - SoloFunctionAddVars('OpenEphys_Neuroblueprint','func_owner','@OpenEphys_Neuroblueprint','ro_args','stopping_process_completed'); % This is a bit unpleasant. We give ro access to the flag denoting the stop process as complete to runrats so that OpenEphys_Neuropblueprint can wait on it using a timer. :P + SoloFunctionAddVars('OpenEphys_Neuroblueprint','func_owner','@OpenEphys_Neuroblueprint','ro_args','stopping_process_completed'); % This is a bit unpleasant. We give ro access to the flag denoting the stop process as complete so that OpenEphys_Neuropblueprint can wait on it using a timer. :P + SoloFunctionAddVars('NeuropixelNeuroblueprint','func_owner','@NeuropixelNeuroblueprint','ro_args','stopping_process_completed'); % This is a bit unpleasant. We give ro access to the flag denoting the stop process as complete so that NeuropixelNeuropblueprint can wait on it using a timer. :P SoloFunctionAddVars('TowerWaterDelivery','func_owner','@TowerWaterDelivery','ro_args','stopping_process_completed'); % This is a bit unpleasant. We give ro access to the flag denoting the stop process as complete to runrats so that runrats can wait on it using a timer. :P set(get_ghandle(RunButton), 'FontSize', 20); % (defined by GetSoloFunctionArgs) @@ -287,6 +288,9 @@ elseif OpenEphys_Neuroblueprint('is_running') OpenEphys_Neuroblueprint('crash_detected'); Running.value = 0; + elseif NeuropixelNeuroblueprint('is_running') + NeuropixelNeuroblueprint('crash_detected'); + Running.value = 0; else rethrow(me); end diff --git a/ExperPort/Modules/@runrats/runrats.m b/ExperPort/Modules/@runrats/runrats.m index 6ebf0cb3..cd3fd2db 100644 --- a/ExperPort/Modules/@runrats/runrats.m +++ b/ExperPort/Modules/@runrats/runrats.m @@ -45,14 +45,15 @@ display('test'); %If we are starting from a forced reboot from runrats itself we %need to make sure the do_on_reboot.bat file is set back to nothing - try %#ok - p = pwd; - cd('\ratter\Rigscripts') - - !del do_on_reboot.bat - !copy nothing.bat do_on_reboot.bat /Y - cd(p); - end + + % try %#ok + % p = pwd; + % cd('\ratter\Rigscripts') + % + % !del do_on_reboot.bat + % !copy nothing.bat do_on_reboot.bat /Y + % cd(p); + % end %If a dispatcher is already open, let's close it so we don't ever have %more than 1 @@ -115,7 +116,7 @@ SoloParamHandle(obj,'myfig', 'value',fig); try - set(myfig, 'WindowStyle', 'modal'); + set(value(myfig), 'WindowStyle', 'modal'); pause(0.1); catch %#ok diff --git a/Protocols/@ArpitSoundCatContinuous/ArpitSoundCatContinuous.m b/Protocols/@ArpitSoundCatContinuous/ArpitSoundCatContinuous.m index 1c8044d6..03d985bf 100644 --- a/Protocols/@ArpitSoundCatContinuous/ArpitSoundCatContinuous.m +++ b/Protocols/@ArpitSoundCatContinuous/ArpitSoundCatContinuous.m @@ -205,7 +205,7 @@ % PokesPlotSection(obj, 'close'); SideSection(obj, 'close'); StimulusSection(obj,'close'); - BonsaiCameraInterface(obj,'close'); + BonsaiCameraInterface(obj,'close'); if exist('myfig', 'var') && isa(myfig, 'SoloParamHandle') && ishandle(value(myfig)) %#ok delete(value(myfig)); end diff --git a/Protocols/@ArpitSoundCatContinuous/SideSection.m b/Protocols/@ArpitSoundCatContinuous/SideSection.m index b736295d..6728b7a6 100644 --- a/Protocols/@ArpitSoundCatContinuous/SideSection.m +++ b/Protocols/@ArpitSoundCatContinuous/SideSection.m @@ -219,7 +219,7 @@ SoloFunctionAddVars('StimulusSection', 'ro_args', ... {'ThisTrial';'stimuli_on';'A1_time';'time_bet_aud1_gocue' ; ... - 'PreStim_time'}); + 'PreStim_time';'LeftProb'}); SoloFunctionAddVars('StimulatorSection', 'ro_args', ... {'A1_time';'time_bet_aud1_gocue';'time_go_cue'; ... diff --git a/Protocols/@ArpitSoundCatContinuous/StimulatorSection.m b/Protocols/@ArpitSoundCatContinuous/StimulatorSection.m index d884292f..a636fce4 100644 --- a/Protocols/@ArpitSoundCatContinuous/StimulatorSection.m +++ b/Protocols/@ArpitSoundCatContinuous/StimulatorSection.m @@ -134,6 +134,10 @@ end + if strcmpi(value(StimLine),'Ephys') & n_done_trials == 0 + dispatcher('set_trialnum_indicator_flag'); + end + %% prepare_next_trial diff --git a/Protocols/@ArpitSoundCatContinuous/StimulusSection.asv b/Protocols/@ArpitSoundCatContinuous/StimulusSection.asv new file mode 100644 index 00000000..9159c5b8 --- /dev/null +++ b/Protocols/@ArpitSoundCatContinuous/StimulusSection.asv @@ -0,0 +1,809 @@ + + +function varargout = StimulusSection(obj, action, varargin) + +GetSoloFunctionArgs(obj); + +switch action + + % ------------------------------------------------------------------ + % INIT + % ------------------------------------------------------------------ + + case 'init' + if length(varargin) < 2 + error('Need at least two arguments, x and y position, to initialize %s', mfilename); + end + x = varargin{1}; y = varargin{2}; + + ToggleParam(obj, 'StimulusShow', 0, x, y, 'OnString', 'Stimuli Show', ... + 'OffString', 'Stimuli Hidden', 'TooltipString', 'Show/Hide Stimulus panel'); + set_callback(StimulusShow, {mfilename, 'show_hide'}); %#ok (Defined just above) + next_row(y); + + oldx=x; oldy=y; parentfig=double(gcf); + + SoloParamHandle(obj, 'myfig', 'value', figure('closerequestfcn', [mfilename '(' class(obj) ', ''hide'');'],... + 'MenuBar', 'none', 'Name', mfilename, 'Units', 'normalized'), 'saveable', false); + SoundManagerSection(obj, 'declare_new_sound', 'StimAUD1') + SoloParamHandle(obj, 'thisstim', 'value', []); + SoloParamHandle(obj, 'thisstimlog', 'value', []); + + %% Formatting graphics elements + + %myfig + original_width = 0.45; + original_height = 0.75; + max_size = min(original_width, original_height); + aspect_ratio = original_width / original_height; + new_width = max_size; + new_height = new_width / aspect_ratio; + + center_x = 0.5 - (new_width / 2); + center_y = 0.5 - (new_height / 2); + + position_vector = [center_x center_y new_width new_height]; + + set(value(myfig), 'Units', 'normalized', 'Name', mfilename, 'Position', position_vector); + set(double(gcf), 'visible', 'off'); + + + x = 10; y=5; + next_row(y); + MenuParam(obj, 'Rule', {'S1>S_boundary Left','S1>S_boundary Right'}, ... + 'S1>S_boundary Left', x, y, 'labelfraction', 0.35, 'TooltipString', sprintf(['\nThis buttom determines the rule\n', ... + '\n''S1>S_boundary Left'' means if Aud1 > Aud_boundry then reward will be delivered from the left water spout and if Aud1 < Aud_boundry then water comes from right\n',... + '\n''S1>S_boundary Right'' means if Aud1 < Aud_boundry then reward will be delivered from the left water spout and if Aud1 > Aud_boundry then water comes from right\n'])); + + next_row(y, 1);next_row(y, 1); + NumeditParam(obj,'P_centre_region',0.25,x,y,'label','P_centre','TooltipString','probability for choosing stim near boundary. 0 = same probability as the rest, 1 = only choose from centre'); + next_row(y); + NumeditParam(obj,'centre_region_width',0.1,x,y,'label','Centre Width','TooltipString','total width around boundary to be considered as central region'); + next_row(y);next_row(y); + + MenuParam(obj, 'Prob_Dist_Left', {'Uniform','Exponential','Half Normal','Normal','Sinusoidal','Anti Exponential','Anti Half Normal','Anti Sinusoidal'}, ... + 'Uniform', x, y,'label','Left Dist', 'labelfraction', 0.35, 'TooltipString', sprintf(['\n Different Probability Distributions for Category A.\n', ... + '\n''Normal - the mean is at mid point of range. Half Normal - truncated normal with mean at boundary.\n',... + '\n''Anti Half Normal - the mean/max is at the side edge of the range.\n',... + '\n''Sinosidal - using sine function instead of half normal and Anti Sinusoidal is when max is at the edge, same as anti half normal.\n'])); + set_callback(Prob_Dist_Left, {mfilename, 'Cal_Mean'}); + next_row(y); + DispParam(obj, 'mean_Left', 0.01, x,y,'label','μ Left','TooltipString','mean/max log stim value for the left side distribution'); + next_row(y); + NumeditParam(obj, 'sigma_Left', 0.15, x,y,'label','σ Left','TooltipString','sigma value for normal/half normal distribution or decay rate for exponential for the left side distribution'); + next_row(y); + NumeditParam(obj, 'sigma_range_Left', 1, x,y,'label','3σ Left','TooltipString',sprintf(['\n A way to reduce the range and increase more distribution towards mean\n', ... + '\n''signifying 3 Sigma (99.7%%) value for the left side distribution, \n',... + '\n''A value b/w range [0.2 - 1] is acceptable.'])); + set_callback(sigma_range_Left, {mfilename, 'Cal_Sigma'}); + next_row(y); next_row(y); + + MenuParam(obj, 'Prob_Dist_Right', {'Uniform','Exponential','Half Normal','Normal','Sinusoidal','Anti Exponential','Anti Half Normal','Anti Sinusoidal'}, ... + 'Uniform', x, y, 'label','Right Dist', 'labelfraction', 0.35, 'TooltipString', sprintf(['\n Different Probability Distributions for Category A.\n', ... + '\n''Normal - the mean is at mid point of range (side edge - boundary). Half Normal - truncated normal with mean at boundary.\n',... + '\n''Anti Half Normal - the mean/max is at the side edge of the range.\n',... + '\n''Sinosidal - using sine function instead of half normal and Anti Sinusoidal is when max is at the edge, same as anti half normal'])); + set_callback(Prob_Dist_Right, {mfilename, 'Cal_Mean'}); + next_row(y); + DispParam(obj, 'mean_Right', 0.01, x,y,'label','μ Right','TooltipString','mean/max log stim value for the right side distribution'); + next_row(y); + NumeditParam(obj, 'sigma_Right', 0.15, x,y,'label','σ Right','TooltipString','sigma value for normal/half normal distribution or decay rate for exponential for the left side distribution'); + next_row(y); + NumeditParam(obj, 'sigma_range_Right', 1, x,y,'label','3σ Right','TooltipString',sprintf(['\n A way to reduce the range and increase more distribution towards mean\n', ... + '\n''signifying 3 Sigma (99.7 %%) value for the right side distribution, \n',... + '\n''A value b/w range [0.2 - 1] is acceptable.'])); + set_callback(sigma_range_Right, {mfilename, 'Cal_Sigma'}); + next_row(y);next_row(y); + MenuParam(obj, 'Category_Dist', {'Uniform','Hard A','Hard B'}, ... + 'Uniform', x, y, 'label','Category Dist', 'labelfraction', 0.35, 'TooltipString', sprintf(['\n Different Distributions for Category.\n', ... + '\n''Depending upon the rule it will change switch the distributions for Left and Right.\n',... + '\n''If its uniform on both then it will change the distribution to Exponential on one of the side\n',... + '\n''depending upon the choice of rule'])); + set_callback(Category_Dist, {mfilename, 'Distribution_Switch'}); + next_row(y);next_row(y); + PushbuttonParam(obj, 'plot_stim_dist', x,y , 'TooltipString', 'Plots the distribution with the new set of parameters'); + set_callback(plot_stim_dist, {mfilename, 'plot_stim_distribution'}); + next_column(x); + y=5; + next_row(y, 1) + MenuParam(obj, 'filter_type', {'GAUS','LPFIR', 'FIRLS','BUTTER','MOVAVRG','KAISER','EQUIRIP','HAMMING'}, ... + 'GAUS', x, y, 'labelfraction', 0.35, 'TooltipString', sprintf(['\nDifferent filters. ''LPFIR'': lowpass FIR ''FIRLS'': Least square linear-phase FIR filter design\n', ... + '\n''BUTTER'': IIR Butterworth lowpass filter ''GAUS'': Gaussian filter (window)\n', ... + '\n''MOVAVRG'': Moving average FIR filter ''KAISER'': Kaiser-window FIR filtering\n', ... + '\n''EQUIRIP'':Eqiripple FIR filter ''HAMMING'': Hamming-window based FIR'])); + next_row(y); + NumeditParam(obj,'fcut',110,x,y,'label','fcut','TooltipString','Cut off frequency on the original white noise'); + next_row(y); + NumeditParam(obj,'lfreq',2000,x,y,'label','Modulator_LowFreq','TooltipString','Lower bound for the frequency modulator'); + next_row(y); + NumeditParam(obj,'hfreq',20000,x,y,'label','Modulator_HighFreq','TooltipString','Upper bound for the frequency modulator'); + next_row(y); + NumeditParam(obj,'minS1',0.007,x,y,'label','minS1','TooltipString','min sigma value for AUD1'); + set_callback(minS1, {mfilename, 'Cal_Boundary'}); + next_row(y); + NumeditParam(obj,'maxS1',0.05,x,y,'label','maxS1','TooltipString','max sigma value for AUD1'); + set_callback(maxS1, {mfilename, 'Cal_Boundary'}); + next_row(y); + DispParam(obj, 'A1_sigma', 0.01, x,y,'label','A1_sigma','TooltipString','Sigma value for the first stimulus'); + next_row(y); + NumeditParam(obj,'minF1',4,x,y,'label','minF1','TooltipString','min frequency value for AUD1'); + set_callback(minF1, {mfilename, 'Cal_Boundary'}); + next_row(y); + NumeditParam(obj,'maxF1',10,x,y,'label','maxF1','TooltipString','max frequency value for AUD1'); + set_callback(maxF1, {mfilename, 'Cal_Boundary'}); + next_row(y); + NumeditParam(obj,'volumeF1',0.007,x,y,'label','VolumeF1','TooltipString','volume of tone for AUD1'); + next_row(y); + DispParam(obj, 'A1_freq', 0.01, x,y,'label','A1_freq','TooltipString','Sigma value for the first stimulus'); + next_row(y); + DispParam(obj,'boundary',-3.9,x,y,'label','boundary(log)','TooltipString','decision boundary for categorisation (log)'); + next_row(y); + MenuParam(obj, 'mu_location', {'center', 'side'}, ... + 'center', x, y, 'labelfraction', 0.35, 'TooltipString', sprintf('\nLocation of boundary')); + set_callback(mu_location, {mfilename, 'Cal_Boundary'}); + next_row(y); + ToggleParam(obj, 'frequency_categorization', 0, x,y,... + 'OnString', 'Frequency(Tone)',... + 'OffString', 'Amplitude(Noise)',... + 'TooltipString', sprintf('If on (black) then it enables the presentation of pure tones')); + set_callback(frequency_categorization, {mfilename, 'FrequencyCategorization'}); + make_invisible(maxF1);make_invisible(minF1);make_invisible(A1_freq);make_invisible(volumeF1); + next_row(y); + + % Axes for Plotting + hndl_uipanelplotaxes = uipanel('Units', 'normalized'); + set(hndl_uipanelplotaxes, ... + 'Units', 'normalized', ... + 'Parent', value(myfig), ... + 'Title', 'Stimuli Distribution', ... + 'Tag', 'uipanelstimplot', ... + 'Position', [0.06,0.52,0.8,0.42]); + + SoloParamHandle(obj, 'axstimplot', 'value', axes(hndl_uipanelplotaxes,'Units', 'normalized','Position', [0.07,0.07, ... + 0.8,0.85]), 'saveable', false); + + SoloParamHandle(obj, 'checkboxHist', 'value', ... + uicontrol('Parent', hndl_uipanelplotaxes, ... + 'Units', 'normalized', ... + 'Style', 'checkbox', ... + 'String', 'Histogram', ... + 'TooltipString', 'Show/hide histogram', ... + 'Value', 0, ... + 'Tag', 'checkboxHist', ... + 'Position', [0.9 0.3 0.1 0.12]), ... + 'saveable', false); + + SoloParamHandle(obj, 'checkboxLegends', 'value', ... + uicontrol('Parent', hndl_uipanelplotaxes, ... + 'Units', 'normalized', ... + 'Style', 'checkbox', ... + 'String', 'Legends', ... + 'TooltipString', 'Show/hide legends', ... + 'Value', 0, ... + 'Tag', 'checkboxlegend', ... + 'Position', [0.9 0.5 0.1 0.12]), ... + 'saveable', false); + + SoloParamHandle(obj, 'checkboxStim', 'value', ... + uicontrol('Parent', hndl_uipanelplotaxes, ... + 'Units', 'normalized', ... + 'Style', 'checkbox', ... + 'String', 'ThisStim', ... + 'TooltipString', 'Show/hide present stim', ... + 'Value', 0, ... + 'Tag', 'checkboxStim', ... + 'Position', [0.9 0.7 0.1 0.12]), ... + 'saveable', false); + + SoloParamHandle(obj, 'plot_h', 'value', struct(), 'saveable', false); % Initialize output + + % Plot the Distribution + StimulusSection(obj,'plot_stim_distribution'); + set(value(myfig), 'visible', 'off'); + + x=oldx; y=oldy; + figure(parentfig); + + SoloFunctionAddVars('PsychometricSection', 'ro_args',{'Category_Dist';'Rule';'boundary'}); + + varargout{1} = x; + varargout{2} = y; + + case 'prepare_next_trial' + if stimuli_on + StimulusSection(obj,'pick_current_stimulus'); + srate=SoundManagerSection(obj,'get_sample_rate'); + Fs=srate; + T=value(A1_time); + + if frequency_categorization + % produce the tone + A1_freq.value = value(thisstim); + A1 = value(thisstimlog(n_done_trials+1)); + dur1 = A1_time*1000; + bal=0; + freq1=A1_freq*1000; + vol=value(volumeF1); + RVol=vol*min(1,(1+bal)); + LVol=vol*min(1,(1-bal)); + t=0:(1/srate):(dur1/1000); + t = t(1:end-1); + tw=sin(t*2*pi*freq1); + RW=RVol*tw; + %w=[LW;RW]; + AUD1 = RW; + else + % produce noise pattern + A1_length = round(A1_time * srate); + A1_sigma.value = value(thisstim); + A1 = value(thisstimlog(n_done_trials+1)); + [rawA1, rawA2, normA1, normA2]=noisestim(1,1,T,value(fcut),Fs,value(filter_type)); + modulator=singlenoise(1,T,[value(lfreq) value(hfreq)],Fs,'BUTTER'); + AUD1=normA1(1:A1_length) .* modulator(1:A1_length).*A1_sigma; + end + + if ~isempty(AUD1) + SoundManagerSection(obj, 'set_sound', 'StimAUD1', [AUD1'; AUD1']) + end + + SoundManagerSection(obj, 'send_not_yet_uploaded_sounds'); + + if n_done_trials > 0 + + StimulusSection(obj,'update_stimulus_history'); + + % Update the stim histogram and show chosen stimuli + + if value(StimulusShow) == 1 % only run if the figure window is open + + stim_values = value(stimulus_history); + stim_present = stim_values(end); + stim_history = stim_values(1:end-1); + + % figure out if legends,present stim and histogram to be plotted or not + legend_obj = value(checkboxLegends); + legend_draw = logical(legend_obj.Value); + + if n_done_trials < 10 + hist_draw = false; + else + hist_obj = value(checkboxHist); + hist_draw = logical(hist_obj.Value); + end + + stim_obj = value(checkboxStim); + stim_draw = logical(stim_obj.Value); + + if hist_draw || stim_draw + [~,~, handle_h] = CreateSamples_from_Distribution(... + 'Mode', 'update_plot', ... + 'ax_handle', value(axstimplot), ... % Get current axes handle + 'plot_handles_in', value(plot_h), ... + 'current_stimulus', stim_present,... + 'samples_history_in',stim_history,... + 'plot_histogram', hist_draw, ... + 'plot_chosen_stimuli', stim_draw, ... + 'plot_distribution', true, ... + 'plot_legend',legend_draw... + ); + plot_h.value = handle_h; + end + + end + end + end + + %% Case pick_current_stimulus + case 'pick_current_stimulus' + if frequency_categorization + stim_min_log = log(value(minF1)); + stim_max_log = log(value(maxF1)); + else + stim_min_log = log(value(minS1)); + stim_max_log = log(value(maxS1)); + end + + dist_sigma_left_multiplier = value(sigma_Left) * value(sigma_range_Left); + dist_sigma_right_multiplier = value(sigma_Right) * value(sigma_range_Right); + + % The left-right side according the animal is not the same as left-right of + % stim distribution because it depends upon the Rule whether Stim should be + % considered left/right (below/up of boundary) based upon this rule. + % Changing animals side reference to stimuli reference depending upon Rule + % Even the setting in StimulusSection is based upon side not on the + % basis of whether left or right of boundary + + if strcmp(Rule,'S1>S_boundary Left') % side left is right to stim boundary + if strcmpi(ThisTrial, 'LEFT') + stim_side = 'right'; + else + stim_side = 'left'; + end + dist_left = value(Prob_Dist_Right); + dist_right = value(Prob_Dist_Left); + range_percent_left = value(sigma_range_Right) * 100; + range_percent_right = value(sigma_range_Left) * 100; + dist_mean_left = value(mean_Right); + dist_mean_right = value(mean_Left); + dist_sigma_left = dist_sigma_right_multiplier * (value(boundary) - stim_min_log); + dist_sigma_right = dist_sigma_left_multiplier * (stim_max_log - value(boundary)); + exp_decay_left = value(sigma_Right); + exp_decay_right = value(sigma_Left); + + else % the rule is S1>S_boundary Right + if strcmpi(ThisTrial, 'LEFT') + stim_side = 'left'; + else + stim_side = 'right'; + end + dist_right = value(Prob_Dist_Right); + dist_left = value(Prob_Dist_Left); + range_percent_right = value(sigma_range_Right) * 100; + range_percent_left = value(sigma_range_Left) * 100; + dist_mean_right = value(mean_Right); + dist_mean_left = value(mean_Left); + dist_sigma_left = dist_sigma_left_multiplier * (value(boundary) - stim_min_log); + dist_sigma_right = dist_sigma_right_multiplier * (stim_max_log - value(boundary)); + exp_decay_left = value(sigma_Left); + exp_decay_right = value(sigma_Right); + end + + if ~exist('LeftProb','var') + LeftProb = SideSection(obj,'get_left_prob'); + end + + % Generate a single sample, explicitly chosen side 'left' + [stim_i_log,~,~] = CreateSamples_from_Distribution(... + 'Mode', 'generate_single_sample', ... + 'chosen_side', stim_side, ... % Force pick from chosen side + 'left_edge_value', stim_min_log,... + 'boundary_value', value(boundary),... + 'right_edge_value', stim_max_log,... + 'left_probability', LeftProb, ... % These still define the overall PDF, but this pick is forced chosen side + 'right_probability', 1 - LeftProb, ... + 'left_dist_type', dist_left, ... + 'decay_rate_magnitude_left', exp_decay_left,... + 'normal_mean_left', dist_mean_left,... + 'normal_std_dev_left', dist_sigma_left,... + 'half_normal_std_dev_left', dist_sigma_left,... + 'range_percentage_left',range_percent_left , ... + 'right_dist_type', dist_right, ... + 'decay_rate_magnitude_right', exp_decay_right,... + 'normal_mean_right', dist_mean_right,... + 'normal_std_dev_right', dist_sigma_right,... + 'half_normal_std_dev_right', dist_sigma_right,... + 'range_percentage_right',range_percent_right, ... + 'P_central_region', value(P_centre_region), 'central_region_width', value(centre_region_width) ... + ); + + thisstim.value=exp(stim_i_log); + thisstimlog(n_done_trials+1) = stim_i_log; + + %% Case plot stimuli distribution + case 'plot_stim_distribution' + + if frequency_categorization + stim_min_log = log(value(minF1)); + stim_max_log = log(value(maxF1)); + else + stim_min_log = log(value(minS1)); + stim_max_log = log(value(maxS1)); + end + + dist_sigma_left_multiplier = value(sigma_Left) * value(sigma_range_Left); + dist_sigma_right_multiplier = value(sigma_Right) * value(sigma_range_Right); + + % The left-right side according the animal is not the same as left-right of + % stim distribution because it depends upon the Rule whether Stim should be + % considered left/right (below/up of boundary) based upon this rule. + % Changing animals side reference to stimuli reference depending upon Rule + + if strcmp(Rule,'S1>S_boundary Left') % side left is right to stim boundary + dist_left = value(Prob_Dist_Right); + dist_right = value(Prob_Dist_Left); + range_percent_left = value(sigma_range_Right) * 100; + range_percent_right = value(sigma_range_Left) * 100; + dist_mean_left = value(mean_Right); + dist_mean_right = value(mean_Left); + dist_sigma_left = dist_sigma_right_multiplier * (value(boundary) - stim_min_log); + dist_sigma_right = dist_sigma_left_multiplier * (stim_max_log - value(boundary)); + exp_decay_right = value(sigma_Left); + exp_decay_left = value(sigma_Right); + + else % the rule is S1>S_boundary Right + dist_right = value(Prob_Dist_Right); + dist_left = value(Prob_Dist_Left); + range_percent_right = value(sigma_range_Right) * 100; + range_percent_left = value(sigma_range_Left) * 100; + dist_mean_right = value(mean_Right); + dist_mean_left = value(mean_Left); + dist_sigma_left = dist_sigma_left_multiplier * (value(boundary) - stim_min_log); + dist_sigma_right = dist_sigma_right_multiplier * (stim_max_log - value(boundary)); + exp_decay_left = value(sigma_Left); + exp_decay_right = value(sigma_Right); + end + + if ~exist('LeftProb','var') + LeftProb = SideSection(obj,'get_left_prob'); + end + + % figure out if legends to be plotted or not + legend_obj = value(checkboxLegends); + legend_draw = logical(legend_obj.Value); + + + [~,~, handle_h] = CreateSamples_from_Distribution(... + 'Mode', 'initialize_plot', ... + 'ax_handle', value(axstimplot), ... % Get current axes handle + 'plot_handles_in', value(plot_h), ... + 'plot_histogram', false, ... + 'plot_chosen_stimuli', false, ... + 'plot_distribution', true, ... + 'left_edge_value', stim_min_log,... + 'boundary_value', value(boundary),... + 'right_edge_value', stim_max_log,... + 'left_probability', LeftProb, ... % These still define the overall PDF, but this pick is forced chosen side + 'right_probability', 1 - LeftProb, ... + 'left_dist_type', dist_left, ... + 'normal_mean_left', dist_mean_left,... + 'decay_rate_magnitude_left', exp_decay_left,... + 'normal_std_dev_left', dist_sigma_left,... + 'half_normal_std_dev_left', dist_sigma_left,... + 'range_percentage_left',range_percent_left , ... + 'right_dist_type', dist_right, ... + 'decay_rate_magnitude_right', exp_decay_right,... + 'normal_mean_right', dist_mean_right,... + 'normal_std_dev_right', dist_sigma_right,... + 'half_normal_std_dev_right', dist_sigma_right,... + 'range_percentage_right',range_percent_right, ... + 'P_central_region', value(P_centre_region), 'central_region_width', value(centre_region_width), ... + 'plot_legend',legend_draw... + ); + + plot_h.value = handle_h; + drawnow; + + + + %% Boundary Calculate + case 'Cal_Boundary' + if frequency_categorization + val_boundary = (log(value(minF1)) + log(value(maxF1)))/2; + min_val = log(value(minF1)); + else + val_boundary = (log(value(minS1)) + log(value(maxS1)))/2; + min_val = log(value(minS1)); + end + if strcmp(mu_location,'center') + boundary.value = val_boundary; + elseif strcmp(mu_location,'side') + boundary.value = (min_val + val_boundary)/2; + end + + StimulusSection(obj,'Cal_Mean'); % update the mean and sigma values for each side + + %% Updated Mean/Max for Each Side based upon Distribution Selected + case 'Cal_Mean' + + if frequency_categorization + edge_max = log(value(maxF1)); + edge_min = log(value(minF1)); + else + edge_max = log(value(maxS1)); + edge_min = log(value(minS1)); + end + + % Calculation for Left Side + + % Sigma + + dist_sigma_multiplier = value(sigma_range_Left); + if dist_sigma_multiplier < 0.2 + dist_sigma_multiplier = 0.2; + end + if dist_sigma_multiplier > 1 + dist_sigma_multiplier = 1; + end + + if strcmp(Rule,'S1>S_boundary Left') + edge_min_left = value(boundary); + edge_max_left = edge_min_left + dist_sigma_multiplier * (edge_max - edge_min_left); + else % the rule is S1>S_boundary Right + edge_max_left = value(boundary); + edge_min_left = edge_max_left - dist_sigma_multiplier * (edge_max_left - edge_min); + end + + make_invisible(sigma_Left); + switch value(Prob_Dist_Left) + case {'Half Normal', 'Anti Half Normal'} + make_visible(sigma_Left); + sigma_Left.value = 0.25; + case 'Normal' + make_visible(sigma_Left); + sigma_Left.value = 0.25; + case {'Exponential','Anti Exponential'} + make_visible(sigma_Left); + sigma_Left.value = 2.153; + end + + % Mean + if matches(value(Prob_Dist_Left),{'Uniform','Half Normal','Sinusoidal','Exponential'}) + mean_Left.value = value(boundary); + else + if strcmp(Rule,'S1>S_boundary Left') + if matches(value(Prob_Dist_Left),{'Anti Half Normal','Anti Sinusoidal','Anti Exponential'}) + mean_Left.value = edge_max_left; + elseif matches(value(Prob_Dist_Left),'Normal') + mean_Left.value = (edge_max_left + value(boundary))/2; + end + else + if matches(value(Prob_Dist_Left),{'Anti Half Normal','Anti Sinusoidal','Anti Exponential'}) + mean_Left.value = edge_min_left; + elseif matches(value(Prob_Dist_Left),'Normal') + mean_Left.value = (edge_min_left + value(boundary))/2; + end + end + end + + % Calculation for Right Side + + % Sigma + dist_sigma_multiplier = value(sigma_range_Right); + if dist_sigma_multiplier < 0.2 + dist_sigma_multiplier = 0.2; + end + if dist_sigma_multiplier > 1 + dist_sigma_multiplier = 1; + end + + if strcmp(Rule,'S1>S_boundary Right') + edge_min_right = value(boundary); + edge_max_right = edge_min_right + dist_sigma_multiplier * (edge_max - edge_min_right); + else % the rule is S1>S_boundary Right + edge_max_right = value(boundary); + edge_min_right = edge_max_right - dist_sigma_multiplier * (edge_max_right - edge_min); + end + + make_invisible(sigma_Right); + switch value(Prob_Dist_Right) + case {'Half Normal', 'Anti Half Normal'} + make_visible(sigma_Right); + sigma_Right.value = 0.25; + case 'Normal' + make_visible(sigma_Right); + sigma_Right.value = 0.25; + case {'Exponential','Anti Exponential'} + make_visible(sigma_Right); + sigma_Right.value = 2.153; + end + + + % Mean + if matches(value(Prob_Dist_Right),{'Uniform','Half Normal','Sinusoidal','Exponential'}) + mean_Right.value = value(boundary); + else + if strcmp(Rule,'S1>S_boundary Right') + if matches(value(Prob_Dist_Right),{'Anti Half Normal','Anti Sinusoidal','Anti Exponential'}) + mean_Right.value = edge_max_right; + elseif matches(value(Prob_Dist_Right),'Normal') + mean_Right.value = (edge_max_right + value(boundary))/2; + end + else + if matches(value(Prob_Dist_Right),{'Anti Half Normal','Anti Sinusoidal','Anti Exponential'}) + mean_Right.value = edge_min_right; + elseif matches(value(Prob_Dist_Right),'Normal') + mean_Right.value = (edge_min_right + value(boundary))/2; + end + end + end + + StimulusSection(obj,'plot_stim_distribution'); + + %% Calculate Sigma + case 'Cal_Sigma' + + if frequency_categorization + edge_max = log(value(maxF1)); + edge_min = log(value(minF1)); + else + edge_max = log(value(maxS1)); + edge_min = log(value(minS1)); + end + + % Calculation for Left Side Sigma + dist_sigma_multiplier = value(sigma_range_Left); + if dist_sigma_multiplier < 0.2 + sigma_range_Left.value = 0.2; + end + if dist_sigma_multiplier > 1 + sigma_range_Left.value = 1; + end + + make_invisible(sigma_Left); + switch value(Prob_Dist_Left) + case {'Half Normal', 'Anti Half Normal'} + make_visible(sigma_Left); + sigma_Left.value = 0.25; + case 'Normal' + make_visible(sigma_Left); + sigma_Left.value = 0.25; + case {'Exponential','Anti Exponential'} + make_visible(sigma_Left); + sigma_Left.value = 2.153; + end + + % Calculation for Right Side Sigma + dist_sigma_multiplier = value(sigma_range_Right); + if dist_sigma_multiplier < 0.2 + sigma_range_Right.value = 0.2; + end + if dist_sigma_multiplier > 1 + sigma_range_Right.value = 1; + end + + make_invisible(sigma_Right); + switch value(Prob_Dist_Right) + case {'Half Normal', 'Anti Half Normal'} + make_visible(sigma_Right); + sigma_Right.value = 0.25; + case 'Normal' + make_visible(sigma_Right); + sigma_Right.value = 0.25; + case {'Exponential','Anti Exponential'} + make_visible(sigma_Right); + sigma_Right.value = 2.153; + end + + case 'stim_params' + + if frequency_categorization + stim_min_log = log(value(minF1)); + stim_max_log = log(value(maxF1)); + else + stim_min_log = log(value(minS1)); + stim_max_log = log(value(maxS1)); + end + + dist_range_multiplier_left = value(sigma_range_Left); + dist_range_multiplier_right = value(sigma_range_Right); + + if strcmp(Rule,'S1>S_boundary Left') + edge_max_left = stim_max_log; + edge_min_left = value(boundary); + edge_max_left = edge_min_left + dist_range_multiplier_left * (edge_max_left - edge_min_left); + edge_max_right = value(boundary); + edge_min_right = stim_min_log; + edge_min_right = edge_max_right - dist_range_multiplier_right * (edge_max_right - edge_min_right); + + varargout{1} = [edge_min_right, value(boundary), edge_max_left]; + + else % the rule is S1>S_boundary Right + + edge_min_left = stim_min_log; + edge_max_left = value(boundary); + edge_min_left = edge_max_left - dist_range_multiplier_left * (edge_max_left - edge_min_left); + edge_max_right = stim_max_log; + edge_min_right = value(boundary); + edge_max_right = edge_min_right + dist_range_multiplier_right * (edge_max_right - edge_min_right); + + varargout{1} = [edge_min_left, value(boundary), edge_max_right]; + end + + %% Case frequency ON + case 'FrequencyCategorization' + if frequency_categorization == 1 + make_visible(maxF1);make_visible(minF1);make_visible(A1_freq);make_visible(volumeF1); + make_invisible(maxS1);make_invisible(minS1);make_invisible(A1_sigma); + make_invisible(fcut);make_invisible(lfreq);make_invisible(hfreq); make_invisible(filter_type); + else + make_visible(maxS1);make_visible(minS1);make_visible(A1_sigma); + make_visible(fcut);make_visible(lfreq);make_visible(hfreq); make_visible(filter_type); + make_invisible(maxF1);make_invisible(minF1);make_invisible(A1_freq); make_invisible(volumeF1); + end + + StimulusSection(obj,'Cal_Boundary'); % update the boundary + StimulusSection(obj,'plot_stim_distribution'); + + %% Case get_stimuli + % case 'get_stimuli' + % if nargout>0 + % x=value(S1); + % end + + case 'Distribution_Switch' + + switch value(Category_Dist) + + case 'Uniform' + Prob_Dist_Right.value = 'Uniform'; + make_invisible(sigma_Right); + Prob_Dist_Left.value = 'Uniform'; + make_invisible(sigma_Left); + + case 'Hard A' + if strcmp(Rule,'S1>S_boundary Right') + Prob_Dist_Right.value = 'Uniform'; + make_invisible(sigma_Right); + Prob_Dist_Left.value = 'Exponential'; + sigma_Left.value = 2.153; + make_visible(sigma_Left); + else + Prob_Dist_Right.value = 'Exponential'; + sigma_Right.value = 2.153; + make_visible(sigma_Right); + Prob_Dist_Left.value = 'Uniform'; + make_invisible(sigma_Left); + end + + case 'Hard B' + if strcmp(Rule,'S1>S_boundary Right') + Prob_Dist_Left.value = 'Uniform'; + make_invisible(sigma_Left); + Prob_Dist_Right.value = 'Exponential'; + make_visible(sigma_Right); + sigma_Right.value = 2.153; + else + Prob_Dist_Left.value = 'Exponential'; + make_visible(sigma_Left); + sigma_Left.value = 2.153; + Prob_Dist_Right.value = 'Uniform'; + make_invisible(sigma_Right); + end + end + StimulusSection(obj,'plot_stim_distribution'); + PsychometricSection(obj,'StimSection_Distribution_Switch'); + + case 'Pushbutton_SwitchDistribution' + dist = varargin{1}; + Category_Dist.value = dist; + StimulusSection(obj,'Distribution_Switch'); + + %% Case close + case 'close' + set(value(myfig), 'visible', 'off'); + % set(value(stim_dist_fig), 'visible', 'off'); + + % Delete all SoloParamHandles who belong to this object and whose + % fullname starts with the name of this mfile: + if exist('myfig', 'var') && isa(myfig, 'SoloParamHandle') && ishandle(value(myfig)) %#ok + delete(value(myfig)); + end + if exist('stim_dist_fig', 'var') && isa(stim_dist_fig, 'SoloParamHandle') && ishandle(value(stim_dist_fig)) %#ok + delete(value(stim_dist_fig)); + end + delete_sphandle('owner', ['^@' class(obj) '$'], ... + 'fullname', ['^' mfilename]); + + case 'update_stimulus_history' + ps=value(stimulus_history); + ps1 = value(stimulus_distribution_history); + ps(n_done_trials)=value(thisstimlog(n_done_trials)); + ps1{n_done_trials}=value(Category_Dist); + stimulus_history.value=ps; + stimulus_distribution_history.value = ps1; + + %% Case hide + case 'hide' + StimulusShow.value = 0; + set(value(myfig), 'visible', 'off'); + % set(value(stim_dist_fig), 'visible', 'off'); + + %% Case show + case 'show' + StimulusShow.value = 1; + set(value(myfig), 'visible', 'on'); + % set(value(stim_dist_fig), 'visible', 'on'); + + %% Case Show_hide + case 'show_hide' + if StimulusShow == 1 + set(value(myfig), 'visible', 'on'); + % set(value(stim_dist_fig), 'visible', 'on');%#ok (defined by GetSoloFunctionArgs) + else + set(value(myfig), 'visible', 'off'); + % set(value(stim_dist_fig), 'visible', 'off'); + end + +end + +return; diff --git a/Protocols/@ArpitSoundCatContinuous/StimulusSection.m b/Protocols/@ArpitSoundCatContinuous/StimulusSection.m index a4c8eabc..62e59608 100644 --- a/Protocols/@ArpitSoundCatContinuous/StimulusSection.m +++ b/Protocols/@ArpitSoundCatContinuous/StimulusSection.m @@ -32,7 +32,7 @@ %% Formatting graphics elements %myfig - original_width = 0.25; + original_width = 0.45; original_height = 0.75; max_size = min(original_width, original_height); aspect_ratio = original_width / original_height; @@ -45,7 +45,7 @@ position_vector = [center_x center_y new_width new_height]; set(value(myfig), 'Units', 'normalized', 'Name', mfilename, 'Position', position_vector); - set(double(gcf), 'Visible', 'off'); + set(double(gcf), 'visible', 'off'); x = 10; y=5; @@ -54,7 +54,12 @@ 'S1>S_boundary Left', x, y, 'labelfraction', 0.35, 'TooltipString', sprintf(['\nThis buttom determines the rule\n', ... '\n''S1>S_boundary Left'' means if Aud1 > Aud_boundry then reward will be delivered from the left water spout and if Aud1 < Aud_boundry then water comes from right\n',... '\n''S1>S_boundary Right'' means if Aud1 < Aud_boundry then reward will be delivered from the left water spout and if Aud1 > Aud_boundry then water comes from right\n'])); + next_row(y, 1);next_row(y, 1); + NumeditParam(obj,'P_centre_region',0.25,x,y,'label','P_centre','TooltipString','probability for choosing stim near boundary. 0 = same probability as the rest, 1 = only choose from centre'); + next_row(y); + NumeditParam(obj,'centre_region_width',0.1,x,y,'label','Centre Width','TooltipString','total width around boundary to be considered as central region'); + next_row(y);next_row(y); MenuParam(obj, 'Prob_Dist_Left', {'Uniform','Exponential','Half Normal','Normal','Sinusoidal','Anti Exponential','Anti Half Normal','Anti Sinusoidal'}, ... 'Uniform', x, y,'label','Left Dist', 'labelfraction', 0.35, 'TooltipString', sprintf(['\n Different Probability Distributions for Category A.\n', ... @@ -65,7 +70,7 @@ next_row(y); DispParam(obj, 'mean_Left', 0.01, x,y,'label','μ Left','TooltipString','mean/max log stim value for the left side distribution'); next_row(y); - DispParam(obj, 'sigma_Left', 0.01, x,y,'label','σ Left','TooltipString','sigma value(log) for normal distribution for the left side distribution'); + NumeditParam(obj, 'sigma_Left', 0.15, x,y,'label','σ Left','TooltipString','sigma value for normal/half normal distribution or decay rate for exponential for the left side distribution'); next_row(y); NumeditParam(obj, 'sigma_range_Left', 1, x,y,'label','3σ Left','TooltipString',sprintf(['\n A way to reduce the range and increase more distribution towards mean\n', ... '\n''signifying 3 Sigma (99.7%%) value for the left side distribution, \n',... @@ -82,7 +87,7 @@ next_row(y); DispParam(obj, 'mean_Right', 0.01, x,y,'label','μ Right','TooltipString','mean/max log stim value for the right side distribution'); next_row(y); - DispParam(obj, 'sigma_Right', 0.01, x,y,'label','σ Right','TooltipString','sigma value (log) for normal distribution for the right side distribution'); + NumeditParam(obj, 'sigma_Right', 0.15, x,y,'label','σ Right','TooltipString','sigma value for normal/half normal distribution or decay rate for exponential for the left side distribution'); next_row(y); NumeditParam(obj, 'sigma_range_Right', 1, x,y,'label','3σ Right','TooltipString',sprintf(['\n A way to reduce the range and increase more distribution towards mean\n', ... '\n''signifying 3 Sigma (99.7 %%) value for the right side distribution, \n',... @@ -97,7 +102,7 @@ set_callback(Category_Dist, {mfilename, 'Distribution_Switch'}); next_row(y);next_row(y); PushbuttonParam(obj, 'plot_stim_dist', x,y , 'TooltipString', 'Plots the distribution with the new set of parameters'); - set_callback(plot_stim_dist, {mfilename, 'plot_stimuli'}); + set_callback(plot_stim_dist, {mfilename, 'plot_stim_distribution'}); next_column(x); y=5; next_row(y, 1) @@ -152,15 +157,49 @@ 'Parent', value(myfig), ... 'Title', 'Stimuli Distribution', ... 'Tag', 'uipanelstimplot', ... - 'Position', [0.06,0.42,0.8,0.4]); + 'Position', [0.06,0.52,0.8,0.42]); + + SoloParamHandle(obj, 'axstimplot', 'value', axes(hndl_uipanelplotaxes,'Units', 'normalized','Position', [0.07,0.07, ... + 0.8,0.85]), 'saveable', false); + + SoloParamHandle(obj, 'checkboxHist', 'value', ... + uicontrol('Parent', hndl_uipanelplotaxes, ... + 'Units', 'normalized', ... + 'Style', 'checkbox', ... + 'String', 'Histogram', ... + 'TooltipString', 'Show/hide histogram', ... + 'Value', 0, ... + 'Tag', 'checkboxHist', ... + 'Position', [0.9 0.3 0.1 0.12]), ... + 'saveable', false); + + SoloParamHandle(obj, 'checkboxLegends', 'value', ... + uicontrol('Parent', hndl_uipanelplotaxes, ... + 'Units', 'normalized', ... + 'Style', 'checkbox', ... + 'String', 'Legends', ... + 'TooltipString', 'Show/hide legends', ... + 'Value', 0, ... + 'Tag', 'checkboxlegend', ... + 'Position', [0.9 0.5 0.1 0.12]), ... + 'saveable', false); + + SoloParamHandle(obj, 'checkboxStim', 'value', ... + uicontrol('Parent', hndl_uipanelplotaxes, ... + 'Units', 'normalized', ... + 'Style', 'checkbox', ... + 'String', 'ThisStim', ... + 'TooltipString', 'Show/hide present stim', ... + 'Value', 0, ... + 'Tag', 'checkboxStim', ... + 'Position', [0.9 0.7 0.1 0.12]), ... + 'saveable', false); + + SoloParamHandle(obj, 'plot_h', 'value', struct(), 'saveable', false); % Initialize output - SoloParamHandle(obj, 'axstimplot', 'value', axes(hndl_uipanelplotaxes,'Units', 'normalized','Position', [0.2,0.2, ... - 0.75,0.75]), 'saveable', false); - xlabel('Stim Distribution','FontSize',8,'FontName','Cambria Math'); - ylabel('log__e A','FontSize',8,'FontName','Cambria Math'); - % Plot the Distribution - StimulusSection(obj,'plot_stimuli'); + StimulusSection(obj,'plot_stim_distribution'); + set(value(myfig), 'visible', 'off'); x=oldx; y=oldy; figure(parentfig); @@ -209,18 +248,53 @@ SoundManagerSection(obj, 'send_not_yet_uploaded_sounds'); - % Plot current stimulus and move to saving stimulus history - - % if value(thisstimlog(n_done_trials+1)) > value(boundary)%value(numClass) - % set(value(h1), 'YData', value(A1), 'color',[0.4 0.8 0.1],'markerfacecolor',[0.4 0.8 0.1]); - % else - % set(value(h1), 'YData', value(A1), 'color',[0.8 0.4 0.1],'markerfacecolor',[0.8 0.4 0.1]); - % end - if n_done_trials > 0 + StimulusSection(obj,'update_stimulus_history'); + + % Update the stim histogram and show chosen stimuli + + if value(StimulusShow) == 1 % only run if the figure window is open + + stim_values = value(thisstimlog); + stim_present = stim_values(n_done_trials); + stim_history = stim_values(1:end-1); + + % figure out if legends,present stim and histogram to be plotted or not + legend_obj = value(checkboxLegends); + legend_draw = logical(legend_obj.Value); + + if n_done_trials < 10 + hist_draw = false; + else + hist_obj = value(checkboxHist); + hist_draw = logical(hist_obj.Value); + end + + stim_obj = value(checkboxStim); + stim_draw = logical(stim_obj.Value); + + if hist_draw || stim_draw + [~,~, handle_h] = CreateSamples_from_Distribution(... + 'Mode', 'update_plot', ... + 'ax_handle', value(axstimplot), ... % Get current axes handle + 'plot_handles_in', value(plot_h), ... + 'current_stimulus', stim_present,... + 'samples_history_in',stim_history,... + 'plot_histogram', hist_draw, ... + 'plot_chosen_stimuli', stim_draw, ... + 'plot_distribution', true, ... + 'plot_legend',legend_draw... + ); + plot_h.value = handle_h; + drawnow; + pause(0.01); + end + + end end end + %% Case pick_current_stimulus case 'pick_current_stimulus' if frequency_categorization @@ -231,100 +305,84 @@ stim_max_log = log(value(maxS1)); end - if strcmpi(ThisTrial, 'LEFT') - dist_type = value(Prob_Dist_Left); - dist_mean = value(mean_Left); - dist_sigma = value(sigma_Left); - dist_range_multiplier = value(sigma_range_Left); - if strcmp(Rule,'S1>S_boundary Left') - edge_max = stim_max_log; - edge_min = value(boundary); - edge_max = edge_min + dist_range_multiplier * (edge_max - edge_min); - else % the rule is S1>S_boundary Right - edge_min = stim_min_log; - edge_max = value(boundary); - edge_min = edge_max - dist_range_multiplier * (edge_max - edge_min); + dist_sigma_left_multiplier = value(sigma_Left) * value(sigma_range_Left); + dist_sigma_right_multiplier = value(sigma_Right) * value(sigma_range_Right); + + % The left-right side according the animal is not the same as left-right of + % stim distribution because it depends upon the Rule whether Stim should be + % considered left/right (below/up of boundary) based upon this rule. + % Changing animals side reference to stimuli reference depending upon Rule + % Even the setting in StimulusSection is based upon side not on the + % basis of whether left or right of boundary + + if strcmp(Rule,'S1>S_boundary Left') % side left is right to stim boundary + if strcmpi(ThisTrial, 'LEFT') + stim_side = 'right'; + else + stim_side = 'left'; end + dist_left = value(Prob_Dist_Right); + dist_right = value(Prob_Dist_Left); + range_percent_left = value(sigma_range_Right) * 100; + range_percent_right = value(sigma_range_Left) * 100; + dist_mean_left = value(mean_Right); + dist_mean_right = value(mean_Left); + dist_sigma_left = dist_sigma_right_multiplier * (value(boundary) - stim_min_log); + dist_sigma_right = dist_sigma_left_multiplier * (stim_max_log - value(boundary)); + exp_decay_left = value(sigma_Right); + exp_decay_right = value(sigma_Left); - else % trial is Right - dist_type = value(Prob_Dist_Right); - dist_mean = value(mean_Right); - dist_sigma = value(sigma_Right); - dist_range_multiplier = value(sigma_range_Right); - if strcmp(Rule,'S1>S_boundary Right') - edge_max = stim_max_log; - edge_min = value(boundary); - edge_max = edge_min + dist_range_multiplier * (edge_max - edge_min); - else % the rule is S1>S_boundary Left - edge_min = stim_min_log; - edge_max = value(boundary); - edge_min = edge_max - dist_range_multiplier * (edge_max - edge_min); + else % the rule is S1>S_boundary Right + if strcmpi(ThisTrial, 'LEFT') + stim_side = 'left'; + else + stim_side = 'right'; end + dist_right = value(Prob_Dist_Right); + dist_left = value(Prob_Dist_Left); + range_percent_right = value(sigma_range_Right) * 100; + range_percent_left = value(sigma_range_Left) * 100; + dist_mean_right = value(mean_Right); + dist_mean_left = value(mean_Left); + dist_sigma_left = dist_sigma_left_multiplier * (value(boundary) - stim_min_log); + dist_sigma_right = dist_sigma_right_multiplier * (stim_max_log - value(boundary)); + exp_decay_left = value(sigma_Left); + exp_decay_right = value(sigma_Right); end - % Create a Stimuli with the selected Distribution and Side - switch dist_type - - case 'Uniform' % uniform distribution - stim_i_log = random('Uniform',edge_min,edge_max); - - case 'Exponential' - - lambda = 2.153 * (edge_max - edge_min); % In mice they are using 2.153 for normalized stim range [0 1] - stim_i_log = edge_min - 1; % preinitialize for while loop - while stim_i_log < edge_min || stim_i_log > edge_max - U = rand(1); - if edge_min == value(boundary) % exponentially decreasing - stim_i_log = edge_min + (-(1/lambda)*log(U)); % the distribution would be between range [0 1], so added the edge_min - else - stim_i_log = edge_max - (-(1/lambda)*log(U)); - end - end - - case 'Anti Exponential' - - lambda = 2.153 * (edge_max - edge_min); % In mice they are using 2.153 for normalized stim range [0 1] - stim_i_log = edge_min - 1; % preinitialize for while loop - while stim_i_log < edge_min || stim_i_log > edge_max - U = rand(1); - if edge_min == value(boundary) % exponentially decreasing - stim_i_log = edge_max - (-(1/lambda)*log(U)); % the distribution would be between range [0 1], so added the edge_min - else - stim_i_log = edge_min - ((1/lambda)*log(U)); - end - end - - case 'Half Normal' - if edge_min == value(boundary) - stim_i_log = random('Half Normal',dist_mean,dist_sigma); - while stim_i_log < edge_min || stim_i_log > edge_max - stim_i_log = random('Half Normal',dist_mean,dist_sigma); - end - else - stim_i_log = CreateSamples_from_Distribution('normal',dist_mean,dist_sigma,edge_min,edge_max,1); - end - - case 'Anti Half Normal' - - stim_i_log = CreateSamples_from_Distribution('normal',dist_mean,dist_sigma,edge_min,edge_max,1); - - case 'Normal' - stim_i_log = random('Normal',dist_mean,dist_sigma); - while stim_i_log < edge_min || stim_i_log > edge_max - stim_i_log = random('Normal',dist_mean,dist_sigma); - end - - case 'Sinusoidal' | 'Anti Sinusoidal' - - stim_i_log = CreateSamples_from_Distribution('Sinusoidal',dist_mean,dist_sigma,edge_min,edge_max,1); - + if ~exist('LeftProb','var') + LeftProb = SideSection(obj,'get_left_prob'); end + % Generate a single sample, explicitly chosen side 'left' + [stim_i_log,~,~] = CreateSamples_from_Distribution(... + 'Mode', 'generate_single_sample', ... + 'chosen_side', stim_side, ... % Force pick from chosen side + 'left_edge_value', stim_min_log,... + 'boundary_value', value(boundary),... + 'right_edge_value', stim_max_log,... + 'left_probability', LeftProb, ... % These still define the overall PDF, but this pick is forced chosen side + 'right_probability', 1 - LeftProb, ... + 'left_dist_type', dist_left, ... + 'decay_rate_magnitude_left', exp_decay_left,... + 'normal_mean_left', dist_mean_left,... + 'normal_std_dev_left', dist_sigma_left,... + 'half_normal_std_dev_left', dist_sigma_left,... + 'range_percentage_left',range_percent_left , ... + 'right_dist_type', dist_right, ... + 'decay_rate_magnitude_right', exp_decay_right,... + 'normal_mean_right', dist_mean_right,... + 'normal_std_dev_right', dist_sigma_right,... + 'half_normal_std_dev_right', dist_sigma_right,... + 'range_percentage_right',range_percent_right, ... + 'P_central_region', value(P_centre_region), 'central_region_width', value(centre_region_width) ... + ); + thisstim.value=exp(stim_i_log); thisstimlog(n_done_trials+1) = stim_i_log; %% Case plot stimuli distribution - case 'plot_stimuli' + case 'plot_stim_distribution' if frequency_categorization stim_min_log = log(value(minF1)); @@ -332,54 +390,82 @@ else stim_min_log = log(value(minS1)); stim_max_log = log(value(maxS1)); - end - - dist_range_multiplier_left = value(sigma_range_Left); - dist_range_multiplier_right = value(sigma_range_Right); - - if strcmp(Rule,'S1>S_boundary Left') - edge_max_left = stim_max_log; - edge_min_left = value(boundary); - edge_max_left = edge_min_left + dist_range_multiplier_left * (edge_max_left - edge_min_left); - edge_max_right = value(boundary); - edge_min_right = stim_min_log; - edge_min_right = edge_max_right - dist_range_multiplier_right * (edge_max_right - edge_min_right); - - else % the rule is S1>S_boundary Right - - edge_min_left = stim_min_log; - edge_max_left = value(boundary); - edge_min_left = edge_max_left - dist_range_multiplier_left * (edge_max_left - edge_min_left); - edge_max_right = stim_max_log; - edge_min_right = value(boundary); - edge_max_right = edge_min_right + dist_range_multiplier_right * (edge_max_right - edge_min_right); - end + end + + dist_sigma_left_multiplier = value(sigma_Left) * value(sigma_range_Left); + dist_sigma_right_multiplier = value(sigma_Right) * value(sigma_range_Right); + + % The left-right side according the animal is not the same as left-right of + % stim distribution because it depends upon the Rule whether Stim should be + % considered left/right (below/up of boundary) based upon this rule. + % Changing animals side reference to stimuli reference depending upon Rule + + if strcmp(Rule,'S1>S_boundary Left') % side left is right to stim boundary + dist_left = value(Prob_Dist_Right); + dist_right = value(Prob_Dist_Left); + range_percent_left = value(sigma_range_Right) * 100; + range_percent_right = value(sigma_range_Left) * 100; + dist_mean_left = value(mean_Right); + dist_mean_right = value(mean_Left); + dist_sigma_left = dist_sigma_right_multiplier * (value(boundary) - stim_min_log); + dist_sigma_right = dist_sigma_left_multiplier * (stim_max_log - value(boundary)); + exp_decay_right = value(sigma_Left); + exp_decay_left = value(sigma_Right); + else % the rule is S1>S_boundary Right + dist_right = value(Prob_Dist_Right); + dist_left = value(Prob_Dist_Left); + range_percent_right = value(sigma_range_Right) * 100; + range_percent_left = value(sigma_range_Left) * 100; + dist_mean_right = value(mean_Right); + dist_mean_left = value(mean_Left); + dist_sigma_left = dist_sigma_left_multiplier * (value(boundary) - stim_min_log); + dist_sigma_right = dist_sigma_right_multiplier * (stim_max_log - value(boundary)); + exp_decay_left = value(sigma_Left); + exp_decay_right = value(sigma_Right); + end + + if ~exist('LeftProb','var') + LeftProb = SideSection(obj,'get_left_prob'); + end + + % figure out if legends to be plotted or not + legend_obj = value(checkboxLegends); + legend_draw = logical(legend_obj.Value); + + + [~,~, handle_h] = CreateSamples_from_Distribution(... + 'Mode', 'initialize_plot', ... + 'ax_handle', value(axstimplot), ... % Get current axes handle + 'plot_handles_in', value(plot_h), ... + 'plot_histogram', false, ... + 'plot_chosen_stimuli', false, ... + 'plot_distribution', true, ... + 'left_edge_value', stim_min_log,... + 'boundary_value', value(boundary),... + 'right_edge_value', stim_max_log,... + 'left_probability', LeftProb, ... % These still define the overall PDF, but this pick is forced chosen side + 'right_probability', 1 - LeftProb, ... + 'left_dist_type', dist_left, ... + 'normal_mean_left', dist_mean_left,... + 'decay_rate_magnitude_left', exp_decay_left,... + 'normal_std_dev_left', dist_sigma_left,... + 'half_normal_std_dev_left', dist_sigma_left,... + 'range_percentage_left',range_percent_left , ... + 'right_dist_type', dist_right, ... + 'decay_rate_magnitude_right', exp_decay_right,... + 'normal_mean_right', dist_mean_right,... + 'normal_std_dev_right', dist_sigma_right,... + 'half_normal_std_dev_right', dist_sigma_right,... + 'range_percentage_right',range_percent_right, ... + 'P_central_region', value(P_centre_region), 'central_region_width', value(centre_region_width), ... + 'plot_legend',legend_draw... + ); + + plot_h.value = handle_h; + drawnow; - cla(value(axstimplot)) - StimuliDistribution_plot(value(axstimplot),[stim_min_log, value(boundary), stim_max_log], Rule, ... - value(Prob_Dist_Left),value(mean_Left),value(sigma_Left),[edge_min_left edge_max_left], ... - value(Prob_Dist_Right),value(mean_Right),value(sigma_Right),[edge_min_right edge_max_right]); - - hold (value(axstimplot),'on') - xline([stim_min_log value(boundary) stim_max_log],'-',{'Stim Min','Boundary','Stim Max'}); - - ylabel('log_e A','FontSize',16,'FontName','Cambria Math'); - set(value(axstimplot),'Fontsize',15) - xlabel('Sound Categorization','FontSize',16,'FontName','Cambria Math') - - % plot(xd,stim_min_log,'s','MarkerSize',15,'MarkerEdgeColor',[0 0 0],'LineWidth',2) - % hold on - % plot(xd,stim_max_log,'s','MarkerSize',15,'MarkerEdgeColor',[0 0 0],'LineWidth',2) - % line([0,2], [value(boundary),value(boundary)]); - % axis square - % set(value(axstimplot),'ytick',([stim_min_log, stim_max_log]),'xtick',xd); - % set(value(axstimplot),'yticklabel',([stim_min, stim_max]),'xticklabel','S1'); - % ylabel('\sigma_1 in log scale','FontSize',16,'FontName','Cambria Math'); - % set(value(axstimplot),'Fontsize',15) - % xlabel('S1','FontSize',16,'FontName','Cambria Math') - %% Boundary Calculate case 'Cal_Boundary' @@ -428,17 +514,28 @@ edge_max_left = value(boundary); edge_min_left = edge_max_left - dist_sigma_multiplier * (edge_max_left - edge_min); end - - sigma_Left.value = (edge_max_left - edge_min_left) / 3; % as we asked user to provide 3 sigma + + make_invisible(sigma_Left); + switch value(Prob_Dist_Left) + case {'Half Normal', 'Anti Half Normal'} + make_visible(sigma_Left); + sigma_Left.value = 0.25; + case 'Normal' + make_visible(sigma_Left); + sigma_Left.value = 0.25; + case {'Exponential','Anti Exponential'} + make_visible(sigma_Left); + sigma_Left.value = 2.153; + end % Mean if matches(value(Prob_Dist_Left),{'Uniform','Half Normal','Sinusoidal','Exponential'}) mean_Left.value = value(boundary); else if strcmp(Rule,'S1>S_boundary Left') - if matches(Prob_Dist_Left,{'Anti Half Normal','Anti Sinusoidal','Anti Exponential'}) + if matches(value(Prob_Dist_Left),{'Anti Half Normal','Anti Sinusoidal','Anti Exponential'}) mean_Left.value = edge_max_left; - elseif matches(Prob_Dist_Left,'Normal') + elseif matches(value(Prob_Dist_Left),'Normal') mean_Left.value = (edge_max_left + value(boundary))/2; end else @@ -469,7 +566,18 @@ edge_min_right = edge_max_right - dist_sigma_multiplier * (edge_max_right - edge_min); end - sigma_Right.value = (edge_max_right - edge_min_right) / 3; % as we asked user to provide 3 sigma + make_invisible(sigma_Right); + switch value(Prob_Dist_Right) + case {'Half Normal', 'Anti Half Normal'} + make_visible(sigma_Right); + sigma_Right.value = 0.25; + case 'Normal' + make_visible(sigma_Right); + sigma_Right.value = 0.25; + case {'Exponential','Anti Exponential'} + make_visible(sigma_Right); + sigma_Right.value = 2.153; + end % Mean @@ -485,13 +593,13 @@ else if matches(value(Prob_Dist_Right),{'Anti Half Normal','Anti Sinusoidal','Anti Exponential'}) mean_Right.value = edge_min_right; - elseif matches(Prob_Dist_Right,'Normal') + elseif matches(value(Prob_Dist_Right),'Normal') mean_Right.value = (edge_min_right + value(boundary))/2; end end end - StimulusSection(obj,'plot_stimuli'); + StimulusSection(obj,'plot_stim_distribution'); %% Calculate Sigma case 'Cal_Sigma' @@ -507,22 +615,46 @@ % Calculation for Left Side Sigma dist_sigma_multiplier = value(sigma_range_Left); if dist_sigma_multiplier < 0.2 - dist_sigma_multiplier = 0.2; + sigma_range_Left.value = 0.2; end if dist_sigma_multiplier > 1 - dist_sigma_multiplier = 1; + sigma_range_Left.value = 1; end - sigma_Left.value = (dist_sigma_multiplier * (edge_max - edge_min)) / 3; % as we asked user to provide 3 sigma + + make_invisible(sigma_Left); + switch value(Prob_Dist_Left) + case {'Half Normal', 'Anti Half Normal'} + make_visible(sigma_Left); + sigma_Left.value = 0.25; + case 'Normal' + make_visible(sigma_Left); + sigma_Left.value = 0.25; + case {'Exponential','Anti Exponential'} + make_visible(sigma_Left); + sigma_Left.value = 2.153; + end % Calculation for Right Side Sigma dist_sigma_multiplier = value(sigma_range_Right); if dist_sigma_multiplier < 0.2 - dist_sigma_multiplier = 0.2; + sigma_range_Right.value = 0.2; end if dist_sigma_multiplier > 1 - dist_sigma_multiplier = 1; + sigma_range_Right.value = 1; + end + + make_invisible(sigma_Right); + switch value(Prob_Dist_Right) + case {'Half Normal', 'Anti Half Normal'} + make_visible(sigma_Right); + sigma_Right.value = 0.25; + case 'Normal' + make_visible(sigma_Right); + sigma_Right.value = 0.25; + case {'Exponential','Anti Exponential'} + make_visible(sigma_Right); + sigma_Right.value = 2.153; end - sigma_Right.value = (dist_sigma_multiplier * (edge_max - edge_min)) / 3; % as we asked user to provide 3 sigma case 'stim_params' @@ -572,7 +704,7 @@ end StimulusSection(obj,'Cal_Boundary'); % update the boundary - StimulusSection(obj,'plot_stimuli'); + StimulusSection(obj,'plot_stim_distribution'); %% Case get_stimuli % case 'get_stimuli' @@ -586,27 +718,41 @@ case 'Uniform' Prob_Dist_Right.value = 'Uniform'; + make_invisible(sigma_Right); Prob_Dist_Left.value = 'Uniform'; + make_invisible(sigma_Left); case 'Hard A' if strcmp(Rule,'S1>S_boundary Right') Prob_Dist_Right.value = 'Uniform'; + make_invisible(sigma_Right); Prob_Dist_Left.value = 'Exponential'; + sigma_Left.value = 2.153; + make_visible(sigma_Left); else Prob_Dist_Right.value = 'Exponential'; + sigma_Right.value = 2.153; + make_visible(sigma_Right); Prob_Dist_Left.value = 'Uniform'; + make_invisible(sigma_Left); end case 'Hard B' if strcmp(Rule,'S1>S_boundary Right') Prob_Dist_Left.value = 'Uniform'; + make_invisible(sigma_Left); Prob_Dist_Right.value = 'Exponential'; + make_visible(sigma_Right); + sigma_Right.value = 2.153; else Prob_Dist_Left.value = 'Exponential'; + make_visible(sigma_Left); + sigma_Left.value = 2.153; Prob_Dist_Right.value = 'Uniform'; + make_invisible(sigma_Right); end end - StimulusSection(obj,'plot_stimuli'); + StimulusSection(obj,'plot_stim_distribution'); PsychometricSection(obj,'StimSection_Distribution_Switch'); case 'Pushbutton_SwitchDistribution' @@ -616,8 +762,8 @@ %% Case close case 'close' - set(value(myfig), 'Visible', 'off'); - % set(value(stim_dist_fig), 'Visible', 'off'); + set(value(myfig), 'visible', 'off'); + % set(value(stim_dist_fig), 'visible', 'off'); % Delete all SoloParamHandles who belong to this object and whose % fullname starts with the name of this mfile: @@ -641,25 +787,25 @@ %% Case hide case 'hide' StimulusShow.value = 0; - set(value(myfig), 'Visible', 'off'); - % set(value(stim_dist_fig), 'Visible', 'off'); + set(value(myfig), 'visible', 'off'); + % set(value(stim_dist_fig), 'visible', 'off'); %% Case show case 'show' StimulusShow.value = 1; - set(value(myfig), 'Visible', 'on'); - % set(value(stim_dist_fig), 'Visible', 'on'); + set(value(myfig), 'visible', 'on'); + % set(value(stim_dist_fig), 'visible', 'on'); %% Case Show_hide case 'show_hide' if StimulusShow == 1 - set(value(myfig), 'Visible', 'on'); - % set(value(stim_dist_fig), 'Visible', 'on');%#ok (defined by GetSoloFunctionArgs) + set(value(myfig), 'visible', 'on'); + % set(value(stim_dist_fig), 'visible', 'on');%#ok (defined by GetSoloFunctionArgs) else - set(value(myfig), 'Visible', 'off'); - % set(value(stim_dist_fig), 'Visible', 'off'); + set(value(myfig), 'visible', 'off'); + % set(value(stim_dist_fig), 'visible', 'off'); end end -end +return; diff --git a/Protocols/@ArpitSoundCatContinuous/private/CreateSamples_from_Distribution.m b/Protocols/@ArpitSoundCatContinuous/private/CreateSamples_from_Distribution.m index 23cdd90b..60faeb7c 100644 --- a/Protocols/@ArpitSoundCatContinuous/private/CreateSamples_from_Distribution.m +++ b/Protocols/@ArpitSoundCatContinuous/private/CreateSamples_from_Distribution.m @@ -1,142 +1,803 @@ -function samples = CreateSamples_from_Distribution(distributiontype,mu_val,sigma_val,range_min,range_max,n_samples) -%% +function [samples_history_out, h_fig_out, plot_handles_out] = CreateSamples_from_Distribution(varargin) +% CreateSamples_from_Distribution +% +% This function generates random samples from a customizable bimodal +% distribution and can optionally plot the distribution, a history histogram, +% and dynamic current picks. +% +% The overall distribution spans a user-defined range [left_edge_value, right_edge_value] +% with a user-defined boundary at 'boundary_value'. +% The actual distribution (PDF) for each side is generated over a percentage +% of the range between the overall edge and the boundary. +% A central sampling bias mechanism is added to draw more picks near the +% boundary without altering the PDF shape. +% +% Inputs (Name-Value Pairs): +% 'Mode' - (char) 'run_simulation' (default), 'generate_single_sample', 'initialize_plot', or 'update_plot'. +% 'left_probability' - (double) Relative probability for the left side. +% 'right_probability' - (double) Relative probability for the right side. +% 'chosen_side' - (char, optional) For 'generate_single_sample' mode: 'left', 'right', or 'auto' (default). +% If 'left' or 'right', the sample is forced from that side. +% +% 'left_edge_value' - (double) The absolute value for the leftmost boundary of the overall plot range. +% 'boundary_value' - (double) The absolute value for the central boundary between left and right distributions. +% 'right_edge_value' - (double) The absolute value for the rightmost boundary of the overall plot range. +% +% 'left_dist_type' - (char) Type for left side: 'Uniform', 'Exponential', 'Anti_Exponential', +% 'Normal', 'Half Normal', 'Anti Half Normal', 'Sinusoidal', 'Anti_Sinusoidal'. +% 'decay_rate_magnitude_left' - (double) Positive magnitude for 'Exponential' types. +% 'normal_mean_left' - (double) Mean for 'Normal' distribution. +% 'normal_std_dev_left' - (double) Standard deviation for 'Normal' distribution. +% 'half_normal_std_dev_left' - (double) Standard deviation for 'Half Normal' types. +% 'sinusoidal_amplitude_factor_left' - (double) Amplitude (0 to 1) for 'Sinusoidal' types. +% 'sinusoidal_frequency_factor_left' - (double) Frequency for 'Sinusoidal' types. +% 'range_percentage_left' - (double) Percentage (0 to 100) of the [left_edge_value, boundary_value] range +% that the left distribution is active over (e.g., 70 for inner 70%). +% +% 'right_dist_type' - (char) Type for right side (same options as left_dist_type). +% 'decay_rate_magnitude_right' - (double) Positive magnitude for 'Exponential' types. +% 'normal_mean_right' - (double) Mean for 'Normal' distribution. +% 'normal_std_dev_right' - (double) Standard deviation for 'Normal' distribution. +% 'half_normal_std_dev_right' - (double) Standard deviation for 'Half Normal' types. +% 'sinusoidal_amplitude_factor_right' - (double) Amplitude (0 to 1) for 'Sinusoidal' types. +% 'sinusoidal_frequency_factor_right' - (double) Frequency for 'Sinusoidal' types. +% 'range_percentage_right' - (double) Percentage (0 to 100) of the [boundary_value, right_edge_value] range +% that the right distribution is active over (e.g., 70 for inner 70%). +% +% 'P_central_region' - (double) Probability (0 to 1) of applying central sampling bias. +% 'central_region_width' - (double) Width of the central zone for biased sampling. +% +% 'num_simulations' - (int) Number of random picks to simulate (only in 'run_simulation' mode). +% 'pause_duration' - (double) Pause duration between updates in seconds (only in 'run_simulation' mode). +% +% For 'initialize_plot' mode: +% 'ax_handle' - (axes handle, optional) Handle to the axes to plot on. If not provided, a new figure/axes is created. +% +% For 'update_plot' mode: +% 'ax_handle' - (axes handle) Handle to the axes to plot on. +% 'current_stimulus' - (double) The most recent stimulus value to plot. +% 'samples_history_in' - (double array) The current history of all samples. +% 'plot_handles_in' - (struct) Struct containing handles of existing plot elements (from initialize_static_plot). +% 'plot_histogram' - (logical) True to plot/update histogram. +% 'plot_chosen_stimuli' - (logical) True to plot/update current stimulus marker. +% 'plot_distribution' - (logical) True to plot/show the PDF curve. +% 'plot_legend' - (logical) True to plot/show the legends. +% +% Outputs: +% samples_history_out - (double array) Array of generated samples. +% If 'generate_single_sample' mode, it's a single value. +% h_fig_out - (figure handle) Handle to the plot figure. +% plot_handles_out - (struct) Struct containing handles of plot elements. -% samples = CreateSamples_from_Distribution('Anti_Half_Normal',log(4),log(4),log(10),1000,) +% --- Input Parsing --- +p = inputParser; +p.KeepUnmatched = true; % Allow unmatched for future flexibility -% pairs = { ... -% 'Distribution_Type' 'Sinusoidal'; ... -% 'Range' [log(0.007) log(0.05)] ; ... -% 'Mean_val' mean([log(0.007),log(0.05)]) ; ... -% 'N_Samples' 1; ... -% 'sigma_val' (log(0.05) - log(0.007))/3 -% }; parseargs(varargin, pairs); +% Define parameters and their default values +addParameter(p, 'Mode', 'run_simulation', @(x) ismember(x, {'run_simulation', 'generate_single_sample', 'initialize_plot', 'update_plot'})); -%% +% Side Probabilities +addParameter(p, 'left_probability', 0.5, @(x) isscalar(x) && x >= 0); +addParameter(p, 'right_probability', 0.5, @(x) isscalar(x) && x >= 0); +addParameter(p, 'chosen_side', 'auto', @(x) ismember(lower(x), {'left', 'right', 'auto'})); % For single sample mode +% Custom Boundary and Edge Values +addParameter(p, 'left_edge_value', -1.0, @(x) isscalar(x) && isnumeric(x)); +addParameter(p, 'boundary_value', 0.0, @(x) isscalar(x) && isnumeric(x)); +addParameter(p, 'right_edge_value', 1.0, @(x) isscalar(x) && isnumeric(x)); -% Ensure the range is valid -if range_max <= range_min - error('Upper bound range_max must be greater than lower bound range_min.'); +% Left Side Parameters +addParameter(p, 'left_dist_type', 'Half Normal', @ischar); +addParameter(p, 'decay_rate_magnitude_left', 2.153, @(x) isscalar(x) && x >= 0); +addParameter(p, 'normal_mean_left', -0.5, @isnumeric); +addParameter(p, 'normal_std_dev_left', 0.15, @(x) isscalar(x) && x > 0); +addParameter(p, 'half_normal_std_dev_left', 0.2, @(x) isscalar(x) && x > 0); +addParameter(p, 'sinusoidal_amplitude_factor_left', 0.8, @(x) isscalar(x) && x >= 0 && x <= 1); +addParameter(p, 'sinusoidal_frequency_factor_left', 1, @(x) isscalar(x) && x >= 0); +% Removed sinusoidal_peak_location_percentage_left as it's now derived +addParameter(p, 'range_percentage_left', 100, @(x) isscalar(x) && x >= 0 && x <= 100); + +% Right Side Parameters +addParameter(p, 'right_dist_type', 'Sinusoidal', @ischar); +addParameter(p, 'decay_rate_magnitude_right', 5, @(x) isscalar(x) && x >= 0); +addParameter(p, 'normal_mean_right', 0.5, @isnumeric); +addParameter(p, 'normal_std_dev_right', 0.15, @(x) isscalar(x) && x > 0); +addParameter(p, 'half_normal_std_dev_right', 0.2, @(x) isscalar(x) && x > 0); +addParameter(p, 'sinusoidal_amplitude_factor_right', 0.8, @(x) isscalar(x) && x >= 0 && x <= 1); +addParameter(p, 'sinusoidal_frequency_factor_right', 1, @(x) isscalar(x) && x >= 0); +% Removed sinusoidal_peak_location_percentage_right as it's now derived +addParameter(p, 'range_percentage_right', 100, @(x) isscalar(x) && x >= 0 && x <= 100); + +% Central Sampling Bias Parameters +addParameter(p, 'P_central_region', 0.3, @(x) isscalar(x) && x >= 0 && x <= 1); +addParameter(p, 'central_region_width', 0.2, @(x) isscalar(x) && x > 0); + +% Simulation Parameters +addParameter(p, 'num_simulations', 200, @(x) isscalar(x) && x > 0 && mod(x,1)==0); +addParameter(p, 'pause_duration', 0.05, @(x) isscalar(x) && x >= 0); + +% Plot Update Specific Parameters +addParameter(p, 'ax_handle', [], @(x) isempty(x) || isgraphics(x, 'axes')); +addParameter(p, 'current_stimulus', NaN, @isnumeric); +addParameter(p, 'samples_history_in', [], @isnumeric); +addParameter(p, 'plot_handles_in', struct(), @isstruct); +addParameter(p, 'plot_histogram', true, @islogical); +addParameter(p, 'plot_chosen_stimuli', true, @islogical); +addParameter(p, 'plot_distribution', true, @islogical); +addParameter(p, 'plot_legend', true, @islogical); + + +% Parse inputs +parse(p, varargin{:}); +params = p.Results; + +% --- Derived Parameters and Input Validation --- + +% Overall edges and main boundary +overall_left_edge = params.left_edge_value; +overall_right_edge = params.right_edge_value; +main_boundary_actual = params.boundary_value; + +% Validate custom edge and boundary values +if overall_left_edge >= main_boundary_actual + error('left_edge_value must be less than boundary_value.'); +end +if main_boundary_actual >= overall_right_edge + error('boundary_value must be less than right_edge_value.'); +end +if params.central_region_width > (overall_right_edge - overall_left_edge) + error('central_region_width cannot exceed the total range (right_edge_value - left_edge_value).'); end -if contains(distributiontype,'normal','IgnoreCase',true) - if mu_val == range_min - distributiontype = 'Anti_Half_Normal'; +% Calculate P_left_derived from left_probability and right_probability +total_prob = params.left_probability + params.right_probability; +if total_prob == 0 + error('left_probability and right_probability cannot both be zero.'); +end +P_left_derived = params.left_probability / total_prob; + +% --- Determine Peak Behavior Flags for each distribution type --- + +% For Exponential: true means peak at main_boundary_actual, false means peak at respective edge +params.left_exp_peak_at_boundary_flag = contains(params.left_dist_type, 'Exponential', 'IgnoreCase', true) && ~contains(params.left_dist_type, 'Anti', 'IgnoreCase', true); +params.right_exp_peak_at_boundary_flag = contains(params.right_dist_type, 'Exponential', 'IgnoreCase', true) && ~contains(params.right_dist_type, 'Anti', 'IgnoreCase', true); + +% For Half Normal: true means peak at main_boundary_actual, false means peak at respective edge +params.left_hn_peak_at_boundary_flag = contains(params.left_dist_type, 'Half Normal', 'IgnoreCase', true) && ~contains(params.left_dist_type, 'Anti', 'IgnoreCase', true); +params.right_hn_peak_at_boundary_flag = contains(params.right_dist_type, 'Half Normal', 'IgnoreCase', true) && ~contains(params.right_dist_type, 'Anti', 'IgnoreCase', true); + +% For Sinusoidal: true means peak at main_boundary_actual, false means peak at respective edge +params.left_sin_peak_at_boundary_flag = contains(params.left_dist_type, 'Sinusoidal', 'IgnoreCase', true) && ~contains(params.left_dist_type, 'Anti', 'IgnoreCase', true); +params.right_sin_peak_at_boundary_flag = contains(params.right_dist_type, 'Sinusoidal', 'IgnoreCase', true) && ~contains(params.right_dist_type, 'Anti', 'IgnoreCase', true); + + +% Determine actual lambda values for exponential based on derived flags +params.lambda_left_actual = params.decay_rate_magnitude_left; +if contains(params.left_dist_type, 'Anti Exponential', 'IgnoreCase', true) + params.lambda_left_actual = -params.decay_rate_magnitude_left; +end + +params.lambda_right_actual = params.decay_rate_magnitude_right; +if contains(params.right_dist_type, 'Anti Exponential', 'IgnoreCase', true) + params.lambda_right_actual = -params.decay_rate_magnitude_right; +end + +% These are the *active* ranges for the PDF definition and unbiased sampling +active_left_dist_start = overall_left_edge + (main_boundary_actual - overall_left_edge) * (1 - params.range_percentage_left / 100); +active_right_dist_end = main_boundary_actual + (overall_right_edge - main_boundary_actual) * (params.range_percentage_right / 100); + +% These are the boundaries for the central sampling zone +min_central_sampling_range = main_boundary_actual - params.central_region_width / 2; +max_central_sampling_range = main_boundary_actual + params.central_region_width / 2; + + +% --- Helper Functions (Nested for parameter access) --- + +function pdf_val = get_pdf_general(x, dist_type_full_name, current_params, min_val, max_val, is_exp_peak_at_boundary_flag, is_hn_peak_at_boundary_flag, is_sin_peak_at_boundary_flag, current_main_boundary) + % current_params: struct with lambda_actual, normal_mean, normal_std_dev, etc. + % current_main_boundary: The main boundary value (e.g., 0.0 or user-defined) + + pdf_val = zeros(size(x)); + idx_in_range = (x >= min_val & x <= max_val); + x_in_range = x(idx_in_range); + range_length = max_val - min_val; + + if range_length <= 0 || isempty(x_in_range) + return; + end + + switch lower(dist_type_full_name) + case 'uniform' + pdf_val(idx_in_range) = 1 / range_length; + + case {'exponential', 'anti exponential'} + lambda = current_params.lambda_actual; + if abs(lambda) < 1e-6 + pdf_val(idx_in_range) = 1 / range_length; + else + % Adjust lambda sign based on peak_at_boundary_flag for consistency + if is_exp_peak_at_boundary_flag % Peak at current_main_boundary + if min_val < current_main_boundary % Left side + lambda_eff = abs(lambda); % Increase towards main_boundary_actual + else % Right side + lambda_eff = -abs(lambda); % Decrease towards main_boundary_actual + end + else % Peak at edge (min_val or max_val) + if min_val < current_main_boundary % Left side + lambda_eff = -abs(lambda); % Decrease towards main_boundary_actual (peak at min_val) + else % Right side + lambda_eff = abs(lambda); % Increase towards max_val (peak at max_val) + end + end + + denominator = (exp(lambda_eff * max_val) - exp(lambda_eff * min_val)); + if denominator == 0 + pdf_val(idx_in_range) = 0; + else + normalization_factor = lambda_eff / denominator; + pdf_val(idx_in_range) = normalization_factor .* exp(lambda_eff .* x_in_range); + end + end + + case 'normal' + mu = current_params.normal_mean; + sigma = current_params.normal_std_dev; + cdf_min = normcdf(min_val, mu, sigma); + cdf_max = normcdf(max_val, mu, sigma); + trunc_prob = cdf_max - cdf_min; + if trunc_prob <= 0 + pdf_val(idx_in_range) = 0; + else + pdf_val(idx_in_range) = normpdf(x_in_range, mu, sigma) / trunc_prob; + end + + case {'half normal', 'anti half normal'} + sigma = current_params.half_normal_std_dev; + + % Determine mu_half_normal based on is_hn_peak_at_boundary_flag and range + if is_hn_peak_at_boundary_flag % Peak at the main boundary + mu_half_normal = current_main_boundary; + else % Peak at edge of the current sub-range (min_val or max_val) + if max_val == current_main_boundary % Left side distribution (peaks at min_val) + mu_half_normal = min_val; + else % Right side distribution (peaks at max_val) + mu_half_normal = max_val; + end + end + + integral_over_range = normcdf(max_val, mu_half_normal, sigma) - normcdf(min_val, mu_half_normal, sigma); + if integral_over_range <= 0 + pdf_val(idx_in_range) = 0; + else + pdf_val(idx_in_range) = 2 * normpdf(x_in_range, mu_half_normal, sigma) / integral_over_range; + end + + case {'sinusoidal', 'anti sinusoidal'} + amplitude = current_params.sinusoidal_amplitude_factor; + frequency = current_params.sinusoidal_frequency_factor; + + % Determine target x_peak based on is_sin_peak_at_boundary_flag + if is_sin_peak_at_boundary_flag % Regular Sinusoidal: peaks at boundary + if min_val < current_main_boundary && max_val == current_main_boundary % Left side + target_x_peak = max_val; % Peak at boundary_value + else % Right side + target_x_peak = min_val; % Peak at boundary_value + end + else % Anti_Sinusoidal: peaks at outer edge + if min_val < current_main_boundary && max_val == current_main_boundary % Left side + target_x_peak = min_val; % Peak at active_left_dist_start + else % Right side + target_x_peak = max_val; % Peak at active_right_dist_end + end + end + + % Calculate phase for this target_x_peak + % We want (frequency * pi * (target_x_peak - min_val) / range_length + calculated_phase) = pi/2 (for a positive peak) + calculated_phase = pi/2 - frequency * pi * (target_x_peak - min_val) / range_length; + + unnormalized_func = @(val) (1 + amplitude * sin(frequency * pi * (val - min_val) / range_length + calculated_phase)); + + if frequency == 0 + integral_val = (1 + amplitude * sin(calculated_phase)) * range_length; + else + k_norm = frequency * pi / range_length; + integral_val = (range_length * (1 + amplitude * sin(calculated_phase)) - ... + (amplitude / k_norm) * (cos(frequency * pi + calculated_phase) - cos(calculated_phase))); + end + + if integral_val <= 0 + normalization_constant = 0; + else + normalization_constant = 1 / integral_val; + end + pdf_val(idx_in_range) = normalization_constant .* unnormalized_func(x_in_range); + end +end + +function rand_val = generate_rand_general(dist_type_full_name, current_params, min_val, max_val, is_exp_peak_at_boundary_flag, is_hn_peak_at_boundary_flag, is_sin_peak_at_boundary_flag, current_main_boundary) + % current_params: struct with lambda_actual, normal_mean, normal_std_dev, etc. + % current_main_boundary: The main boundary value (e.g., 0.0 or user-defined) + + range_length = max_val - min_val; + if range_length <= 0 + rand_val = min_val; % Fallback for zero or negative range + return; + end + + % Nested helper to encapsulate the generation logic for one attempt + function [val, U_val_used] = generate_single_attempt(dist_type_full_name, current_params, min_val, max_val, is_exp_peak_at_boundary_flag, is_hn_peak_at_boundary_flag, is_sin_peak_at_boundary_flag, current_main_boundary,range_length) + U_val_used = rand(); % Generate a uniform random number in (0,1) + + switch lower(dist_type_full_name) + case 'uniform' + val = min_val + U_val_used * range_length; + case {'exponential', 'anti exponential'} + lambda = current_params.lambda_actual; + if abs(lambda) < 1e-6 + val = min_val + U_val_used * range_length; + else + % Adjust lambda_eff based on peak_at_boundary_flag + if is_exp_peak_at_boundary_flag + if min_val < current_main_boundary + lambda_eff = abs(lambda); + else + lambda_eff = -abs(lambda); + end + else + if min_val < current_main_boundary + lambda_eff = -abs(lambda); + else + lambda_eff = abs(lambda); + end + end + term_inside_log = U_val_used * (exp(lambda_eff * max_val) - exp(lambda_eff * min_val)) + exp(lambda_eff * min_val); + if term_inside_log <= 0 + val = min_val + U_val_used * range_length; % Fallback + else + val = (1/lambda_eff) * log(term_inside_log); + end + end + + case 'normal' + mu = current_params.normal_mean; + sigma = current_params.normal_std_dev; + cdf_min = normcdf(min_val, mu, sigma); + cdf_max = normcdf(max_val, mu, sigma); + val = norminv(cdf_min + U_val_used * (cdf_max - cdf_min), mu, sigma); + + case {'half normal', 'anti half normal'} + sigma = current_params.half_normal_std_dev; + if is_hn_peak_at_boundary_flag + mu_half_normal = current_main_boundary; + else + if max_val == current_main_boundary + mu_half_normal = min_val; + else + mu_half_normal = max_val; + end + end + cdf_min_trunc = normcdf(min_val, mu_half_normal, sigma); + cdf_max_trunc = normcdf(max_val, mu_half_normal, sigma); + rand_val_candidate = norminv(cdf_min_trunc + U_val_used * (cdf_max_trunc - cdf_min_trunc), mu_half_normal, sigma); + if is_hn_peak_at_boundary_flag + if min_val < current_main_boundary + val = current_main_boundary - abs(rand_val_candidate - current_main_boundary); + else + val = current_main_boundary + abs(rand_val_candidate - current_main_boundary); + end + else + if max_val == current_main_boundary + val = mu_half_normal + abs(rand_val_candidate - mu_half_normal); + else + val = mu_half_normal - abs(rand_val_candidate - mu_half_normal); + end + end + % Apply clamping to ensure it's within [min_val, max_val] before the strict check + val = max(min_val, min(max_val, val)); + + case {'sinusoidal', 'anti sinusoidal'} + amplitude = current_params.sinusoidal_amplitude_factor; + frequency = current_params.sinusoidal_frequency_factor; + if is_sin_peak_at_boundary_flag + if min_val < current_main_boundary && max_val == current_main_boundary + target_x_peak = max_val; + else + target_x_peak = min_val; + end + else + if min_val < current_main_boundary && max_val == current_main_boundary + target_x_peak = min_val; + else + target_x_peak = max_val; + end + end + calculated_phase = pi/2 - frequency * pi * (target_x_peak - min_val) / range_length; + if frequency == 0 + integral_val = (1 + amplitude * sin(calculated_phase)) * range_length; + else + k_norm = frequency * pi / range_length; + integral_val = (range_length * (1 + amplitude * sin(calculated_phase)) - ... + (amplitude / k_norm) * (cos(frequency * pi + calculated_phase) - cos(calculated_phase))); + end + if integral_val <= 0 + val = min_val + U_val_used * range_length; % Fallback + else + normalization_constant = 1 / integral_val; + cdf_func_sinusoidal = @(x_val) (... + normalization_constant * (... + (x_val - min_val) + ... + (amplitude / (frequency * pi / range_length)) * (... + -cos(frequency * pi * (x_val - min_val) / range_length + calculated_phase) + ... + cos(calculated_phase) ... + )... + )... + ); + func_to_solve = @(x_val) cdf_func_sinusoidal(x_val) - U_val_used; + try + val = fzero(func_to_solve, [min_val, max_val]); + catch ME + val = min_val + U_val_used * range_length; % Fallback + end + end + end + end + + % Initial generation attempt + rand_val = generate_single_attempt(dist_type_full_name, current_params, min_val, max_val, is_exp_peak_at_boundary_flag, is_hn_peak_at_boundary_flag, is_sin_peak_at_boundary_flag, current_main_boundary,range_length); + + % Ensure rand_val is strictly between min_val and max_val + % Re-sample if it falls exactly on an edge due to floating point precision + while rand_val <= min_val || rand_val >= max_val + rand_val = generate_single_attempt(dist_type_full_name, current_params, min_val, max_val, is_exp_peak_at_boundary_flag, is_hn_peak_at_boundary_flag, is_sin_peak_at_boundary_flag, current_main_boundary,range_length); % Re-generate + end +end + +% --- Plot Static Elements Helper Function --- + function plot_handles = initialize_static_plot(ax, total_pdf_vals, x_plot, overall_left_edge, overall_right_edge, main_boundary_actual, min_central_sampling_range, max_central_sampling_range, active_left_dist_start, active_right_dist_end,plot_legend) + + axes(ax); + cla(ax); % Clear axes content if already exists + hold on; + + % Plot the Bimodal Probability Density Function (PDF) + plot_handles.h_pdf = plot(x_plot, total_pdf_vals, 'LineWidth', 2, 'Color', 'b'); + plot_handles.total_pdf_vals_max = max(total_pdf_vals); + + % Add a dashed red line at the main boundary (x=0) for clarity + plot_handles.h_main_boundary_line = plot([main_boundary_actual main_boundary_actual], [0 max(total_pdf_vals)*1.1], 'r--', 'LineWidth', 1.5); + + % Plot the central sampling region boundaries + plot_handles.h_central_left_line = plot([min_central_sampling_range min_central_sampling_range], [0 max(total_pdf_vals)*1.1], 'g:', 'LineWidth', 1); + plot_handles.h_central_right_line = plot([max_central_sampling_range max_central_sampling_range], [0 max(total_pdf_vals)*1.1], 'g:', 'LineWidth', 1); + plot_handles.h_central_text_left = text(min_central_sampling_range, max(total_pdf_vals)*1.15, sprintf('CB Start (%.2f)', min_central_sampling_range), ... + 'VerticalAlignment', 'bottom', 'HorizontalAlignment', 'center', 'Color', 'g', 'FontSize', 8); + plot_handles.h_central_text_right = text(max_central_sampling_range, max(total_pdf_vals)*1.15, sprintf('CB End (%.2f)', max_central_sampling_range), ... + 'VerticalAlignment', 'bottom', 'HorizontalAlignment', 'center', 'Color', 'g', 'FontSize', 8); + + % Plot the overall range boundaries (user-defined edges) + plot_handles.h_overall_left_edge_line = plot([overall_left_edge overall_left_edge], [0 max(total_pdf_vals)*1.1], 'k--', 'LineWidth', 1); + plot_handles.h_overall_left_edge_text = text(overall_left_edge, max(total_pdf_vals)*1.15, sprintf('Overall Left Edge (%.2f)', overall_left_edge), ... + 'VerticalAlignment', 'bottom', 'HorizontalAlignment', 'center', 'Color', 'k', 'FontSize', 8); + plot_handles.h_overall_right_edge_line = plot([overall_right_edge overall_right_edge], [0 max(total_pdf_vals)*1.1], 'k--', 'LineWidth', 1); + plot_handles.h_overall_right_edge_text = text(overall_right_edge, max(total_pdf_vals)*1.15, sprintf('Overall Right Edge (%.2f)', overall_right_edge), ... + 'VerticalAlignment', 'bottom', 'HorizontalAlignment', 'center', 'Color', 'k', 'FontSize', 8); + + % Plot the active distribution range boundaries (derived from percentage) + plot_handles.h_active_left_dist_line = plot([active_left_dist_start active_left_dist_start], [0 max(total_pdf_vals)*1.1], 'm:', 'LineWidth', 1); + plot_handles.h_active_left_dist_text = text(active_left_dist_start, max(total_pdf_vals)*1.10, sprintf('Left Active Start (%.2f)', active_left_dist_start), ... + 'VerticalAlignment', 'top', 'HorizontalAlignment', 'center', 'Color', 'm', 'FontSize', 8); + plot_handles.h_active_right_dist_line = plot([active_right_dist_end active_right_dist_end], [0 max(total_pdf_vals)*1.1], 'm:', 'LineWidth', 1); + plot_handles.h_active_right_dist_text = text(active_right_dist_end, max(total_pdf_vals)*1.10, sprintf('Right Active End (%.2f)', active_right_dist_end), ... + 'VerticalAlignment', 'top', 'HorizontalAlignment', 'center', 'Color', 'm', 'FontSize', 8); + + % Customize plot appearance (static elements) + title(ax, 'Bimodal Probability Density Function with Central Sampling Bias and Sample History'); + xlabel(ax, 'Value (x)'); + ylabel(ax, 'Probability Density'); + grid(ax, 'on'); + xlim(ax, [overall_left_edge - 0.1*(overall_right_edge - overall_left_edge), overall_right_edge + 0.1*(overall_right_edge - overall_left_edge)]); + ylim(ax, [0 max(total_pdf_vals)*1.2]); % Set initial Y-limit based on PDF + + % Dynamically build legend entries + % Create dynamic plot objects here to get their handles for the legend + plot_handles.h_hist = histogram(ax, [], 'Normalization', 'pdf', 'BinLimits', [overall_left_edge, overall_right_edge], 'NumBins', 50, ... + 'FaceColor', [0.7 0.7 0.7], 'EdgeColor', [0.5 0.5 0.5], 'FaceAlpha', 0.5, 'Visible', 'off'); % Initially off + plot_handles.h_pick_marker = plot(ax, NaN, NaN, 'go', 'MarkerSize', 10, 'MarkerFaceColor', 'g', 'Visible', 'off'); % Initially off + plot_handles.h_pick_text = text(ax, NaN, NaN, '', 'VerticalAlignment', 'bottom', 'HorizontalAlignment', 'center', ... + 'Color', 'g', 'FontWeight', 'bold', 'Visible', 'off'); % Initially off + + legend_handles = [plot_handles.h_pdf, plot_handles.h_main_boundary_line, ... + plot_handles.h_central_left_line, plot_handles.h_overall_left_edge_line, ... + plot_handles.h_active_left_dist_line, ... + plot_handles.h_hist, plot_handles.h_pick_marker]; % Use actual handles + legend_strings = {'Overall PDF', 'Main Boundary', 'Central Bias Zone Boundary', 'Overall Range Edge', 'Active Dist. Range', 'Sample History Histogram', 'Current Pick'}; + + plot_handles.h_legend = legend(ax, legend_handles, legend_strings, 'Location', 'best', 'AutoUpdate', 'off'); + + if plot_legend + set(plot_handles.h_legend,'Visible','on') + else + set(plot_handles.h_legend,'Visible','off') + end + + hold off; +end + +% --- Update Dynamic Plot Elements Helper Function --- +function [samples_history_out, plot_handles_out] = update_dynamic_plot(ax, current_stimulus, samples_history_in, plot_handles_in, plot_histogram_flag, plot_chosen_stimuli_flag, plot_legend_flag,plot_distribution_flag) + % params_passed_for_limits: contains overall_left_edge, overall_right_edge for histogram bins, and other static params + + samples_history_out = samples_history_in; % Initialize output with input history + plot_handles_out = plot_handles_in; % Initialize output with input handles (including static ones) + + % Set current axes + axes(ax); + hold on; + + % Add new stimulus to history if valid + if ~isnan(current_stimulus) + samples_history_out = [samples_history_in, current_stimulus]; %#ok + end + + % Update histogram + if plot_histogram_flag + set(plot_handles_out.h_hist, 'Data', samples_history_out, 'Visible', 'on'); + else + set(plot_handles_out.h_hist, 'Visible', 'off'); + end + + % Update current stimulus marker + if plot_chosen_stimuli_flag && ~isnan(current_stimulus) + set(plot_handles_out.h_pick_marker, 'XData', current_stimulus, 'YData', 0, 'Visible', 'on'); + set(plot_handles_out.h_pick_text, 'Position', [current_stimulus, plot_handles_out.total_pdf_vals_max*0.05, 0], ... + 'String', sprintf('Picked: %.4f', current_stimulus), 'Visible', 'on'); else - distributiontype = 'Half_Normal'; + set(plot_handles_out.h_pick_marker, 'Visible', 'off'); + set(plot_handles_out.h_pick_text, 'Visible', 'off'); end -elseif contains(distributiontype,'sinusoidal','IgnoreCase',true) - if mu_val == range_min - distributiontype = 'Anti_Sinusoidal'; + % Show/hide legend + if plot_legend_flag + set(plot_handles_out.h_legend,'Visible','on') else - distributiontype = 'Sinusoidal'; + set(plot_handles_out.h_legend,'Visible','off') end + + % Update static distribution elements visibility based on plot_distribution_flag + % (assuming plot_handles_out already contains these from initialize_static_plot) + static_elements_visibility = 'off'; + if plot_distribution_flag + static_elements_visibility = 'on'; + end + + % Iterate through known static handles and set visibility + static_handle_names = {'h_pdf', 'h_main_boundary_line', 'h_central_left_line', 'h_central_right_line', ... + 'h_central_text_left', 'h_central_text_right', 'h_overall_left_edge_line', ... + 'h_overall_left_edge_text', 'h_overall_right_edge_line', 'h_overall_right_edge_text', ... + 'h_active_left_dist_line', 'h_active_left_dist_text', 'h_active_right_dist_line', ... + 'h_active_right_dist_text'}; + + for i = 1:numel(static_handle_names) + handle_name = static_handle_names{i}; + if isfield(plot_handles_out, handle_name) && isvalid(plot_handles_out.(handle_name)) + set(plot_handles_out.(handle_name), 'Visible', static_elements_visibility); + end + end + + hold off; end -switch distributiontype - case 'Sinusoidal' +% --- Main Function Logic --- +samples_history_out = []; % Initialize output +h_fig_out = []; % Initialize output +plot_handles_out = struct(); % Initialize output + +% Prepare parameters for helper functions (structs for left and right sides) +current_params_left.lambda_actual = params.lambda_left_actual; +current_params_left.normal_mean = params.normal_mean_left; +current_params_left.normal_std_dev = params.normal_std_dev_left; +current_params_left.half_normal_std_dev = params.half_normal_std_dev_left; +current_params_left.sinusoidal_amplitude_factor = params.sinusoidal_amplitude_factor_left; +current_params_left.sinusoidal_frequency_factor = params.sinusoidal_frequency_factor_left; + +current_params_right.lambda_actual = params.lambda_right_actual; +current_params_right.normal_mean = params.normal_mean_right; +current_params_right.normal_std_dev = params.normal_std_dev_right; +current_params_right.half_normal_std_dev = params.half_normal_std_dev_right; +current_params_right.sinusoidal_amplitude_factor = params.sinusoidal_amplitude_factor_right; +current_params_right.sinusoidal_frequency_factor = params.sinusoidal_frequency_factor_right; + + +% Handle different modes of operation +if strcmp(params.Mode, 'generate_single_sample') + % Generate a single sample and return + + % Determine the side for this specific sample + actual_side_for_sample = lower(params.chosen_side); + if strcmp(actual_side_for_sample, 'auto') + U_side_choice = rand(); + if U_side_choice < P_left_derived + actual_side_for_sample = 'left'; + else + actual_side_for_sample = 'right'; + end + end + + U_bias_decision = rand(); + if U_bias_decision < params.P_central_region + % Biased Sampling: Try to get a sample from the central zone + current_pick_found = false; + while ~current_pick_found + if strcmp(actual_side_for_sample, 'left') + % Sample from the full left overall range + candidate_pick = generate_rand_general(params.left_dist_type, current_params_left, overall_left_edge, main_boundary_actual, ... + params.left_exp_peak_at_boundary_flag, params.left_hn_peak_at_boundary_flag, params.left_sin_peak_at_boundary_flag, main_boundary_actual); + else % 'right' + % Sample from the full right overall range + candidate_pick = generate_rand_general(params.right_dist_type, current_params_right, main_boundary_actual, overall_right_edge, ... + params.right_exp_peak_at_boundary_flag, params.right_hn_peak_at_boundary_flag, params.right_sin_peak_at_boundary_flag, main_boundary_actual); + end + if candidate_pick >= min_central_sampling_range && candidate_pick <= max_central_sampling_range + samples_history_out = candidate_pick; + current_pick_found = true; + end + end + else + % Unbiased Sampling: Pick from the determined side's ACTIVE range + if strcmp(actual_side_for_sample, 'left') + samples_history_out = generate_rand_general(params.left_dist_type, current_params_left, active_left_dist_start, main_boundary_actual, ... + params.left_exp_peak_at_boundary_flag, params.left_hn_peak_at_boundary_flag, params.left_sin_peak_at_boundary_flag, main_boundary_actual); + else % 'right' + samples_history_out = generate_rand_general(params.right_dist_type, current_params_right, main_boundary_actual, active_right_dist_end, ... + params.right_exp_peak_at_boundary_flag, params.right_hn_peak_at_boundary_flag, params.right_sin_peak_at_boundary_flag, main_boundary_actual); + end + end + + % Outputs for 'generate_single_sample' mode + h_fig_out = []; + plot_handles_out = struct(); + +elseif strcmp(params.Mode, 'run_simulation') + % --- Plot Initialization (Static) --- + h_fig_out = figure; + ax = gca; % Get current axes handle + + % Generate a fine grid of x values for smooth plotting across the overall range + x_plot = linspace(overall_left_edge, overall_right_edge, 500); + + % Calculate PDF values for the left and right components. These define the overall PDF shape. + pdf_left_component = get_pdf_general(x_plot, params.left_dist_type, current_params_left, active_left_dist_start, main_boundary_actual, ... + params.left_exp_peak_at_boundary_flag, params.left_hn_peak_at_boundary_flag, params.left_sin_peak_at_boundary_flag, main_boundary_actual); + pdf_right_component = get_pdf_general(x_plot, params.right_dist_type, current_params_right, main_boundary_actual, active_right_dist_end, ... + params.right_exp_peak_at_boundary_flag, params.right_hn_peak_at_boundary_flag, params.right_sin_peak_at_boundary_flag, main_boundary_actual); + + % Combine the components to form the total bimodal PDF (this is the theoretical distribution) + total_pdf_vals = P_left_derived * pdf_left_component + (1 - P_left_derived) * pdf_right_component; + + % Initialize static plot elements + plot_handles_out = initialize_static_plot(ax, total_pdf_vals, x_plot, overall_left_edge, overall_right_edge, main_boundary_actual, min_central_sampling_range, max_central_sampling_range, active_left_dist_start, active_right_dist_end,params.plot_legend); + + % Initialize dynamic plot handles (will be updated by update_dynamic_plot) + plot_handles_out.h_hist = []; % Initialize as empty/null + plot_handles_out.h_pick_marker = []; + plot_handles_out.h_pick_text = []; + + fprintf('Starting simulation of %d random picks...\n', params.num_simulations); + + for k = 1:params.num_simulations + % Generate a uniform random number to decide if we apply central sampling bias + U_bias_decision = rand(); - pdf_func = @(x) sin((pi / 2) * ((x - range_min) / (range_max - range_min))); % Define the PDF (Peak at max value) - A_val = 1 / integral(pdf_func, range_min, range_max); % Compute Normalization Constant A - pdf_func = @(x) A_val * sin((pi / 2) * ((x - range_min) / (range_max - range_min))); % the normalized PDF - cdf_func = @(x) integral(@(t) pdf_func(t), range_min, x); % Define the CDF as the cumulative integral of the normalized PDF - inv_cdf_func = @(u) range_min + (range_max - range_min) * (2/pi) * acos(1 - u); % the Inverse CDF - sample_single_point = @() inv_cdf_func(rand()); % Function for Sampling a Single Point - samples = arrayfun(@(x) sample_single_point(), 1:n_samples);% Sample Random Point - - - case 'Anti_Sinusoidal' - - pdf_func = @(x) sin((pi / 2) * ((range_max - x) / (range_max - range_min))); % Define the PDF (Peak at min value) - A_val = 1 / integral(pdf_func, range_min, range_max); % Compute Normalization Constant A - pdf_func = @(x) A_val * sin((pi / 2) * ((range_max - x) / (range_max - range_min))); % Define the normalized PDF - cdf_func = @(x) integral(@(t) pdf_func(t), range_min, x); % Define the CDF as the cumulative integral of the normalized PDF - inv_cdf_func = @(u) range_max - (range_max - range_min) * (2/pi) * acos(1 - u); % the Inverse CDF - sample_single_point = @() inv_cdf_func(rand()); % Function for Sampling a Single Point - samples = arrayfun(@(x) sample_single_point(), 1:n_samples); % Sample Random Points - - - case 'Anti_Half_Normal' - - pdf_func = @(x, A) A * exp(- (x - mu_val).^2 / (2 * sigma_val^2)); % Define the Half-Normal PDF (Peak at min value) - A_den = integral(@(x) exp(- (x - mu_val).^2 / (2 * sigma_val^2)), range_min, range_max); % Compute Normalization Constant A - A_sol = 1 / A_den; % Ensuring total probability integrates to 1 + current_pick_side_str = ''; % To store 'LEFT' or 'RIGHT' for fprintf - pdf_func = @(x) A_sol * exp(- (x - mu_val).^2 / (2 * sigma_val^2)); % Define the normalized PDF function + if U_bias_decision < params.P_central_region + % --- Biased Sampling: Try to get a sample from the central zone --- + current_pick_found = false; + while ~current_pick_found + % Decide if the underlying pick is from the left or right distribution + U_side_choice = rand(); + if U_side_choice < P_left_derived + % Sample from the full left overall range + candidate_pick = generate_rand_general(params.left_dist_type, current_params_left, overall_left_edge, main_boundary_actual, ... + params.left_exp_peak_at_boundary_flag, params.left_hn_peak_at_boundary_flag, params.left_sin_peak_at_boundary_flag, main_boundary_actual); + current_pick_side_str = 'LEFT'; + else + % Sample from the full right overall range + candidate_pick = generate_rand_general(params.right_dist_type, current_params_right, main_boundary_actual, overall_right_edge, ... + params.right_exp_peak_at_boundary_flag, params.right_hn_peak_at_boundary_flag, params.right_sin_peak_at_boundary_flag, main_boundary_actual); + current_pick_side_str = 'RIGHT'; + end + + % Check if the candidate falls within the central sampling zone + if candidate_pick >= min_central_sampling_range && candidate_pick <= max_central_sampling_range + current_pick = candidate_pick; + current_pick_found = true; + fprintf('Random pick (BIASED) from %s distribution: %.4f\n', current_pick_side_str, current_pick); + end + % If not found, the loop continues to re-sample + end + else + % --- Unbiased Sampling: Pick from the overall bimodal distribution's ACTIVE range --- + U_side_choice = rand(); + if U_side_choice < P_left_derived + current_pick = generate_rand_general(params.left_dist_type, current_params_left, active_left_dist_start, main_boundary_actual, ... + params.left_exp_peak_at_boundary_flag, params.left_hn_peak_at_boundary_flag, params.left_sin_peak_at_boundary_flag, main_boundary_actual); + current_pick_side_str = 'LEFT'; + fprintf('Random pick (UNBIASED) from LEFT side (active range [%.2f, %.2f]): %.4f\n', active_left_dist_start, main_boundary_actual, current_pick); + else + current_pick = generate_rand_general(params.right_dist_type, current_params_right, main_boundary_actual, active_right_dist_end, ... + params.right_exp_peak_at_boundary_flag, params.right_hn_peak_at_boundary_flag, params.right_sin_peak_at_boundary_flag, main_boundary_actual); + current_pick_side_str = 'RIGHT'; + fprintf('Random pick (UNBIASED) from RIGHT side (active range [%.2f, %.2f]): %.4f\n', main_boundary_actual, active_right_dist_end, current_pick); + end + end - cdf_func = @(x) (erf((x - mu_val) / (sqrt(2) * sigma_val)) - erf((range_min - mu_val) / (sqrt(2) * sigma_val))) ... % CDF formula for truncated normal distribution - / (erf((range_max - mu_val) / (sqrt(2) * sigma_val)) - erf((range_min - mu_val) / (sqrt(2) * sigma_val))); - - inv_cdf_func = @(u) mu_val + sqrt(2) * sigma_val * erfinv( ... % Inverse CDF function (solving for x in terms of U) - u * (erf((range_max - mu_val) / (sqrt(2) * sigma_val)) - erf((range_min - mu_val) / (sqrt(2) * sigma_val))) ... - + erf((range_min - mu_val) / (sqrt(2) * sigma_val))); + % Update the plot elements + [samples_history_out, plot_handles_out] = update_dynamic_plot(ax, current_pick, samples_history_out, plot_handles_out, ... + true, true, true,true);% Always show all during simulation - sample_single_point = @() inv_cdf_func(rand()); % Function for Sampling a Single Point - samples = arrayfun(@(x) sample_single_point(), 1:n_samples); % Sample Random Points - + drawnow; + pause(params.pause_duration); + + fprintf('Pick %d: %.4f\n', k, current_pick); + end - case 'Half_Normal' + fprintf('Simulation complete. Total picks: %d\n', params.num_simulations); - pdf_func = @(x, A) A * exp(- (range_max - x).^2 / (2 * sigma_val^2)); % Define the Half-Normal PDF (Peak at max value) - A_den = integral(@(x) exp(- (range_max - x).^2 / (2 * sigma_val^2)), range_min, range_max); % Compute Normalization Constant A - A_sol = 1 / A_den; % Ensuring total probability integrates to 1 - - pdf_func = @(x) A_sol * exp(- (range_max - x).^2 / (2 * sigma_val^2)); % Define the final normalized PDF function +elseif strcmp(params.Mode, 'initialize_plot') + % --- Initialize Static Plot --- + % Get axes handle, create if not provided + if isempty(params.ax_handle) || ~isgraphics(params.ax_handle, 'axes') + h_fig_out = figure; + ax = gca; + else + ax = params.ax_handle; + h_fig_out = get(ax, 'Parent'); % Get figure handle from axes + end - cdf_func = @(x) (erf((x - range_max) / (sqrt(2) * sigma_val)) - erf((range_min - range_max) / (sqrt(2) * sigma_val))) ... % CDF formula for truncated normal distribution - / (erf((range_max - range_max) / (sqrt(2) * sigma_val)) - erf((range_min - range_max) / (sqrt(2) * sigma_val))); + % Generate a fine grid of x values for smooth plotting across the overall range + x_plot = linspace(overall_left_edge, overall_right_edge, 500); - inv_cdf_func = @(u) range_max - sqrt(2) * sigma_val * erfinv( ... % Inverse CDF function (solving for x in terms of U) - u * (erf((range_max - range_min) / (sqrt(2) * sigma_val)))); + % Calculate PDF values for the left and right components. These define the overall PDF shape. + pdf_left_component = get_pdf_general(x_plot, params.left_dist_type, current_params_left, active_left_dist_start, main_boundary_actual, ... + params.left_exp_peak_at_boundary_flag, params.left_hn_peak_at_boundary_flag, params.left_sin_peak_at_boundary_flag, main_boundary_actual); + pdf_right_component = get_pdf_general(x_plot, params.right_dist_type, current_params_right, main_boundary_actual, active_right_dist_end, ... + params.right_exp_peak_at_boundary_flag, params.right_hn_peak_at_boundary_flag, params.right_sin_peak_at_boundary_flag, main_boundary_actual); - sample_single_point = @() inv_cdf_func(rand()); % Function for Sampling a Single Point - samples = arrayfun(@(x) sample_single_point(), 1:n_samples); % Sample Random Points + % Combine the components to form the total bimodal PDF (this is the theoretical distribution) + total_pdf_vals = P_left_derived * pdf_left_component + (1 - P_left_derived) * pdf_right_component; -end + % Call helper to initialize static plot elements + plot_handles_out = initialize_static_plot(ax, total_pdf_vals, x_plot, overall_left_edge, overall_right_edge, main_boundary_actual, min_central_sampling_range, max_central_sampling_range, active_left_dist_start, active_right_dist_end,params.plot_legend); + + % No samples history returned for this mode + samples_history_out = []; -% check all the samples are within the range (excluding the range) -out_of_range_samples = find(samples <= range_min & samples >= range_max); -if length(out_of_range_samples) >= 1 - samples_replacement = arrayfun(@(x) sample_single_point(), 1:length(out_of_range_samples)); % - samples(out_of_range_samples) = samples_replacement; -end -% Recheck and if its again out of range then replace it in while loop ( was -% trying to avoid it) -out_of_range_samples_new = find(samples <= range_min & samples >= range_max); -if length(out_of_range_samples) >= 1 - for i = length(out_of_range_samples_new) - sample_new = arrayfun(@(x) sample_single_point(), 1); - while sample_new <= range_min || sample_new >= range_max - sample_new = arrayfun(@(x) sample_single_point(), 1); - end - samples(out_of_range_samples_new(i)) = sample_new; +elseif strcmp(params.Mode, 'update_plot') + % --- Update existing plot elements --- + if isempty(params.ax_handle) || ~isgraphics(params.ax_handle, 'axes') + % error('In ''update_plot'' mode, an existing axes handle (''ax_handle'') must be provided.'); + return; end + + % % Re-calculate PDF values as they are needed for scaling and text placement + % % (This is necessary because total_pdf_vals is used for text positioning, etc.) + % x_plot = linspace(overall_left_edge, overall_right_edge, 500); + % pdf_left_component = get_pdf_general(x_plot, params.left_dist_type, current_params_left, active_left_dist_start, main_boundary_actual, ... + % params.left_exp_peak_at_boundary_flag, params.left_hn_peak_at_boundary_flag, params.left_sin_peak_at_boundary_flag, main_boundary_actual); + % pdf_right_component = get_pdf_general(x_plot, params.right_dist_type, current_params_right, main_boundary_actual, active_right_dist_end, ... + % params.right_exp_peak_at_boundary_flag, params.right_hn_peak_at_boundary_flag, params.right_sin_peak_at_boundary_flag, main_boundary_actual); + % total_pdf_vals = P_left_derived * pdf_left_component + (1 - P_left_derived) * pdf_right_component; + + [samples_history_out, plot_handles_out] = update_dynamic_plot(params.ax_handle, params.current_stimulus, params.samples_history_in, params.plot_handles_in, ... + params.plot_histogram, params.plot_chosen_stimuli, params.plot_legend,params.plot_distribution); + + h_fig_out = get(params.ax_handle, 'Parent'); % Return figure handle of the updated axes + drawnow; +else + return;% error('Invalid Mode specified. Use ''run_simulation'', ''generate_single_sample'', ''initialize_plot'', or ''update_plot''.'); end -% %% Debugging - Validate CDF and Inverse CDF - - % % Create a fine grid of x values - % x_vals = linspace(range_min, range_max, 1000); - % % Evaluate PDF and CDF - % pdf_vals = pdf_func(x_vals); - % cdf_vals = cdf_func(x_vals); - % % Generate uniform random samples and apply inverse CDF - % U = rand(10000,1); - % sampled_x = inv_cdf_func(U); - % %% Plot PDF and CDF - % figure; - % % PDF Plot - % subplot(2,1,1); - % plot(x_vals, pdf_vals, 'b', 'LineWidth', 2); - % xlabel('x'); ylabel('PDF'); - % title('Half-Normal PDF'); - % grid on; - % % CDF Plot - % subplot(2,1,2); - % plot(x_vals, cdf_vals, 'r', 'LineWidth', 2); - % xlabel('x'); ylabel('CDF'); - % title('Cumulative Distribution Function (CDF)'); - % grid on; \ No newline at end of file +end % End of main function diff --git a/Protocols/@ArpitSoundCatContinuous/private/StimuliDistribution_plot.m b/Protocols/@ArpitSoundCatContinuous/private/StimuliDistribution_plot.m deleted file mode 100644 index 8c3c100d..00000000 --- a/Protocols/@ArpitSoundCatContinuous/private/StimuliDistribution_plot.m +++ /dev/null @@ -1,170 +0,0 @@ -function StimuliDistribution_plot(ax,plot_x_range, Rule, distribution_left,mean_left,sigma_left,... - range_left,distribution_right,mean_right,sigma_right,range_right) - -if strcmp(Rule,'S1>S_boundary Right') - x_left = plot_x_range(1):.01:plot_x_range(2); - x_right = plot_x_range(2):.01:plot_x_range(3); -else - x_right = plot_x_range(1):.01:plot_x_range(2); - x_left = plot_x_range(2):.01:plot_x_range(3); -end - -switch distribution_left - - case 'Uniform' - - pdf_funct_left = makedist('Uniform','lower',range_left(1),'upper',range_left(2)); - pd_left = pdf(pdf_funct_left,x_left); - - case 'Exponential' - - lambda = 2.153 * (range_left(2) - range_left(1)); - lambda = 1/lambda; - if range_left(1) == plot_x_range(2) % decreasing function - Z = exp(-lambda * range_left(1)) - exp(-lambda * range_left(2)); % Define the normalization constant - pdf_func_left = @(x) (lambda * exp(-lambda * x)) / Z; % Normalized truncated exponential PDF - else - Z = exp(lambda * range_left(2)) - exp(lambda * range_left(1));% Normalization constant - A = lambda / Z; - pdf_func_left = @(x) A * exp(lambda * x); - end - - pd_left = pdf_func_left(x_left); - - case 'Normal' - - pdf_funct_left = makedist('Normal','mu',mean_left,'sigma',sigma_left); - pd_left = pdf(pdf_funct_left,x_left); - - case 'Half Normal' - - pdf_funct_init = @(x, A) A * exp(- (range_left(2) - x).^2 / (2 * sigma_left^2)); % Define the Half-Normal PDF (Peak at max value) - A_den = integral(@(x) exp(- (range_left(2) - x).^2 / (2 * sigma_left^2)), range_left(1), range_left(2)); % Compute Normalization Constant A - A_sol = 1 / A_den; % Ensuring total probability integrates to 1 - pdf_funct_left = @(x) A_sol * exp(- (range_left(2) - x).^2 / (2 * sigma_left^2)); % Define the final normalized PDF function - pd_left = pdf_funct_left(x_left); - - case 'Anti Exponential' - - lambda = 2.153 * (range_left(2) - range_left(1)); - - if range_left(2) == plot_x_range(2) % decreasing function - Z = exp(-lambda * range_left(1)) - exp(-lambda * range_left(2)); % Define the normalization constant - pdf_func_left = @(x) (lambda * exp(-lambda * x)) / Z; % Normalized truncated exponential PDF - else - Z = exp(lambda * range_left(2)) - exp(lambda * range_left(1));% Normalization constant - A = lambda / Z; - pdf_func_left = @(x) A * exp(lambda * x); - end - - pd_left = pdf_func_left(x_left); - - case 'Anti Half Normal' - - pdf_funct_left = makedist('HalfNormal','mu',mean_left,'sigma',sigma_left); - pd_left = pdf(pdf_funct_left,x_left); - - case 'Anti Sinusoidal' - - pdf_funct_init = @(x) sin((pi / 2) * ((x - range_left(1)) / (range_left(2) - range_left(1)))); - A_val = 1 / integral(pdf_funct_init, range_left(1), range_left(2)); % Compute Normalization Constant A - pdf_funct_left = @(x) A_val * sin((pi / 2) * ((x - range_left(1)) / (range_left(2) - range_left(1)))); % the normalized PDF - pd_left = pdf_funct_left(x_left); - - case 'Sinusoidal' - - pdf_funct_init = @(x) sin((pi / 2) * ((range_left(2) - x) / (range_left(2) - range_left(1)))); % Define the PDF (Peak at min value) - A_val = 1 / integral(pdf_funct_init, range_left(1), range_left(2)); % Compute Normalization Constant A - pdf_funct_left = @(x) A_val * sin((pi / 2) * ((range_left(2) - x) / (range_left(2) - range_left(1)))); % Define the normalized PDF - pd_left = pdf_funct_left(x_left); - - case 'Monotonic Increase' - -end - -switch distribution_right - - case 'Uniform' - - pdf_funct_right = makedist('Uniform','lower',range_right(1),'upper',range_right(2)); - pd_right = pdf(pdf_funct_right,x_right); - - case 'Exponential' - - lambda = 2.153 * (range_right(2) - range_right(1)); - lambda = 1/lambda; - if range_right(1) == plot_x_range(2) % decreasing function - Z = exp(-lambda * range_right(1)) - exp(-lambda * range_right(2)); % Define the normalization constant - pdf_func_right = @(x) (lambda * exp(-lambda * x)) / Z; % Normalized truncated exponential PDF - else - Z = exp(lambda * range_right(2)) - exp(lambda * range_right(1));% Normalization constant - A = lambda / Z; - pdf_func_right = @(x) A * exp(lambda * x); - end - - pd_right = pdf_func_right(x_right); - - case 'Normal' - - pdf_funct_right = makedist('Normal','mu',mean_right,'sigma',sigma_right); - pd_right = pdf(pdf_funct_right,x_right); - - case 'Half Normal' - - pdf_funct_right = makedist('HalfNormal','mu',mean_right,'sigma',sigma_right); - pd_right = pdf(pdf_funct_right,x_right); - - case 'Anti Half Normal' - - pdf_funct_init = @(x, A) A * exp(- (range_right(2) - x).^2 / (2 * sigma_right^2)); % Define the Half-Normal PDF (Peak at max value) - A_den = integral(@(x) exp(- (range_right(2) - x).^2 / (2 * sigma_right^2)), range_right(1), range_right(2)); % Compute Normalization Constant A - A_sol = 1 / A_den; % Ensuring total probability integrates to 1 - pdf_funct_right = @(x) A_sol * exp(- (range_right(2) - x).^2 / (2 * sigma_right^2)); % Define the final normalized PDF function - pd_right = pdf_funct_right(x_right); - - case 'Anti Exponential' - - lambda = 2.153 * (range_right(2) - range_right(1)); - - if range_right(2) == plot_x_range(2) % decreasing function - Z = exp(-lambda * range_right(1)) - exp(-lambda * range_right(2)); % Define the normalization constant - pdf_func_right = @(x) (lambda * exp(-lambda * x)) / Z; % Normalized truncated exponential PDF - else - Z = exp(lambda * range_right(2)) - exp(lambda * range_right(1));% Normalization constant - A = lambda / Z; - pdf_func_right = @(x) A * exp(lambda * x); - end - - pd_right = pdf_func_right(x_right); - - case 'Anti Sinusoidal' - - pdf_funct_init = @(x) sin((pi / 2) * ((range_right(2) - x) / (range_right(2) - range_right(1)))); % Define the PDF (Peak at min value) - A_val = 1 / integral(pdf_funct_init, range_right(1), range_right(2)); % Compute Normalization Constant A - pdf_funct_right = @(x) A_val * sin((pi / 2) * ((range_right(2) - x) / (range_right(2) - range_right(1)))); % Define the normalized PDF - pd_right = pdf_funct_right(x_right); - - - case 'Sinusoidal' - - pdf_funct_init = @(x) sin((pi / 2) * ((x - range_right(1)) / (range_right(2) - range_right(1)))); - A_val = 1 / integral(pdf_funct_init, range_right(1), range_right(2)); % Compute Normalization Constant A - pdf_funct_right = @(x) A_val * sin((pi / 2) * ((x - range_right(1)) / (range_right(2) - range_right(1)))); % the normalized PDF - pd_right = pdf_funct_right(x_right); - -end - -% Plot the distribution -if strcmp(Rule,'S1>S_boundary Right') - plot_x = [x_left,x_right]; - plot_y = [pd_left,pd_right]; -else - plot_x = [x_right,x_left]; - plot_y = [pd_right,pd_left]; -end - -plot(ax,plot_x,plot_y,'b','LineWidth', 2); -ylim(ax,[0 max(plot_y)]); -xlim(ax,[min(plot_x) max(plot_x)]) - -end \ No newline at end of file diff --git a/Protocols/@Rigtest_singletrial/Rigtest_singletrial.m b/Protocols/@Rigtest_singletrial/Rigtest_singletrial.m index 8959fcb4..73b77e63 100644 --- a/Protocols/@Rigtest_singletrial/Rigtest_singletrial.m +++ b/Protocols/@Rigtest_singletrial/Rigtest_singletrial.m @@ -247,7 +247,10 @@ OpenEphys_Neuroblueprint('manual_test_stopping'); return; - + elseif nTrials > 0 && NeuropixelNeuroblueprint('is_running') + NeuropixelNeuroblueprint('manual_test_stopping'); + return; + else % probably called by another function so need to stop the dispatcher from here itself % Send a dummy state machine that does nothing and ends quickly. % This prevents the protocol from running a second trial. From 6d4195d3ee1fe41f9c5e1378d3779c66f18aeed5 Mon Sep 17 00:00:00 2001 From: Arpit Date: Fri, 1 Aug 2025 16:49:00 +0100 Subject: [PATCH 152/164] added more realtime plots like hit rate and stim distribution in ArpitSoundCatContniuous --- .../ArpitSoundCatContinuous.m | 10 +- .../PsychometricSection.asv | 405 ++++++++ .../PsychometricSection.m | 657 +++++-------- .../StimulusSection.asv | 12 +- .../StimulusSection.m | 8 + .../private/CreateSamples_from_Distribution.m | 3 + .../private/RealTimeAnalysis.asv | 768 +++++++++++++++ .../private/RealTimeAnalysis.m | 903 ++++++++++++++++++ 8 files changed, 2361 insertions(+), 405 deletions(-) create mode 100644 Protocols/@ArpitSoundCatContinuous/PsychometricSection.asv create mode 100644 Protocols/@ArpitSoundCatContinuous/private/RealTimeAnalysis.asv create mode 100644 Protocols/@ArpitSoundCatContinuous/private/RealTimeAnalysis.m diff --git a/Protocols/@ArpitSoundCatContinuous/ArpitSoundCatContinuous.m b/Protocols/@ArpitSoundCatContinuous/ArpitSoundCatContinuous.m index 03d985bf..c7462c2d 100644 --- a/Protocols/@ArpitSoundCatContinuous/ArpitSoundCatContinuous.m +++ b/Protocols/@ArpitSoundCatContinuous/ArpitSoundCatContinuous.m @@ -67,6 +67,14 @@ DeclareGlobals(obj, 'ro_args', {'stimulus_distribution_history'}); SoloFunctionAddVars('StimulusSection', 'rw_args', 'stimulus_distribution_history'); + SoloParamHandle(obj, 'stimulus_left_distribution_history', 'value', cell(0)); + DeclareGlobals(obj, 'ro_args', {'stimulus_left_distribution_history'}); + SoloFunctionAddVars('StimulusSection', 'rw_args', 'stimulus_left_distribution_history'); + + SoloParamHandle(obj, 'stimulus_right_distribution_history', 'value', cell(0)); + DeclareGlobals(obj, 'ro_args', {'stimulus_right_distribution_history'}); + SoloFunctionAddVars('StimulusSection', 'rw_args', 'stimulus_right_distribution_history'); + SoloParamHandle(obj, 'violation_history', 'value', []); DeclareGlobals(obj, 'ro_args', {'violation_history'}); SoloFunctionAddVars('SideSection', 'rw_args', 'violation_history'); @@ -226,7 +234,7 @@ SessionDefinition(obj, 'run_eod_logic_without_saving'); % Sending Summary Statistics to SQL Database - perf = PsychometricSection(obj, 'evaluate'); + % perf = PsychometricSection(obj, 'evaluate'); % SoundCatContextSwitchSummary(obj,'protocol_data',perf); diff --git a/Protocols/@ArpitSoundCatContinuous/PsychometricSection.asv b/Protocols/@ArpitSoundCatContinuous/PsychometricSection.asv new file mode 100644 index 00000000..ca3b00e4 --- /dev/null +++ b/Protocols/@ArpitSoundCatContinuous/PsychometricSection.asv @@ -0,0 +1,405 @@ +function varargout = PsychometricSection(obj, action, varargin) + +GetSoloFunctionArgs(obj); + +switch action + + % ------------------------------------------------------------------ + % INIT + % ------------------------------------------------------------------ + + + case 'init' + if length(varargin) < 2 + error('Need at least two arguments, x and y position, to initialize %s', mfilename); + end + x = varargin{1}; y = varargin{2}; + + % Display on the main GUI + + % Context 1 + DispParam(obj, 'Context3_trialStart', 1,x,y, 'position', [x, y, 100 20],'label','t_start','TooltipString','3rd context started at this trial'); + DispParam(obj, 'Context3_trialEnd', 1,x,y, 'position', [x + 101, y, 100 20],'label','t_end','TooltipString','3rd context ended at this trial') + next_row(y); + DispParam(obj, 'Context3_Dist', Category_Dist, x,y,'label','Context3_Distr','TooltipString','stim distribution for 3rd context'); + make_invisible(Context3_Dist); make_invisible(Context3_trialStart);make_invisible(Context3_trialEnd); + next_row(y); + + % Context 2 + DispParam(obj, 'Context2_trialStart', 1,x,y, 'position', [x, y, 100 20],'label','t_start','TooltipString','2nd context started at this trial'); + DispParam(obj, 'Context2_trialEnd', 1,x,y, 'position', [x + 101, y, 100 20],'label','t_end','TooltipString','2nd context ended at this trial') + next_row(y); + DispParam(obj, 'Context2_Dist', Category_Dist, x,y,'label','Context2_Distr','TooltipString','stim distribution for 2nd context'); + make_invisible(Context2_Dist); make_invisible(Context2_trialStart);make_invisible(Context2_trialEnd); + next_row(y); + + % Context 3 + DispParam(obj, 'Context1_trialStart', 1,x,y, 'position', [x, y, 100 20],'label','Trial_start','TooltipString','1st context started at this trial'); + DispParam(obj, 'Context1_trialEnd', 1,x,y, 'position', [x + 101, y, 100 20],'label','Trial_end','TooltipString','1st context ended at this trial') + next_row(y); + DispParam(obj, 'Context1_Dist', Category_Dist, x,y,'label','Context1_Distr','TooltipString','stim distribution for 1st context'); + next_row(y); + + PushbuttonParam(obj, 'Switch_Distr', x, y, 'label', 'Change Stim Distribution','TooltipString', 'Change the context by switching distribution'); + set_callback(Switch_Distr, {mfilename, 'PushButton_Distribution_Switch'}); %#ok (Defined just above) + next_row(y,2) + NumeditParam(obj,'trial_plot',30,x,y,'label','Trails 2 Plot','TooltipString','update psychometric curve after these many valid trials'); + next_row(y); + ToggleParam(obj, 'PsychometricShow', 0, x, y, 'OnString', 'Psychometric Show', ... + 'OffString', 'Psychometric Hidden', 'TooltipString', 'Show/Hide Psychometric panel'); + set_callback(PsychometricShow, {mfilename, 'show_hide'}); %#ok (Defined just above) + next_row(y); + SubheaderParam(obj, 'title', 'Psychometric Section', x, y);next_row(y); + + oldx=x; oldy=y; parentfig=double(gcf); + + % --- State Management with SoloParamHandles --- + state.last_analyzed_valid_trial = 0; + state.block_count = 0; + state.blockStatsHistory = struct('indices', {}, 'hitRates', {}, 'stimCounts', {}); + SoloParamHandle(obj, 'states_value', 'value', state); + SoloParamHandle(obj, 'thiscontext', 'value', 1); + SoloParamHandle(obj, 'last_trial_plotted', 'value', 0); + + vars = ["Select","Rule","DistributionLeft","DistributionRight","Start_trial","End_trial","Slope","TrueBoundary",... + "CalBoundary","LapseA","LapseB","fit_Method", "Overall Hit %", "Left Hit %", "Right Hit %"]; + vars_type = ["logical","string","string","string","double","double","double","double","double",... + "double","double","string","double","double","double"]; + t = table('Size', [0, numel(vars)], 'VariableTypes', vars_type, 'VariableNames', vars); + + + % --- GUI Figure and Component Handles --- + + % Main analysis figure (web-based uifigure) + SoloParamHandle(obj, 'myfig', 'value', figure('closerequestfcn', [mfilename '(' class(obj) ', ''hide'');'],... + 'Name', 'Real-Time Analysis', 'Units', 'normalized','Position', [0.3, 0.1, 0.6, 0.8],'Visible', 'off'), 'saveable', false); + + % --- pre-Define Controls for the Bottom Panel later we will arrange it into panel. This is because if not done before it + % removes all the previous created axes panel. --- + + % Edit Boxes + NumeditParam(obj, 'Plot_Trial_Start', 1, 1, 1,'TooltipString', 'Start trial for custom plot'); + NumeditParam(obj, 'Plot_Trial_End', 30, 1, 1, 'TooltipString', 'End trial for custom plot'); + % Push Buttons + ToggleParam(obj, 'Show_Table_Toggle', 0, 1, 1, 'OnString', 'Table Shown', 'OffString', 'Show Table'); + PushbuttonParam(obj, 'Plot_Custom_Button', 1, 1, 'label', 'Plot Custom Range'); + PushbuttonParam(obj, 'Plot_Context_Button', 1, 1, 'label', 'Plot Context'); + + % --- Create Panels for Layout --- + % Top panel for live plots + hndl_live_plots_panel = uipanel('Parent', value(myfig), 'Title', 'Live Update Plots', ... + 'Units', 'normalized', 'Position', [0.05, 0.55, 0.9, 0.43]); + + % Middle panel for custom plots + hndl_custom_plots_panel = uipanel('Parent', value(myfig), 'Title', 'Custom Range Plots', ... + 'Units', 'normalized', 'Position', [0.05, 0.2, 0.9, 0.33]); + + % Bottom panel for controls + hndl_controls_panel = uipanel('Parent', value(myfig), 'Title', 'Controls', ... + 'Units', 'normalized', 'Position', [0.05, 0.02, 0.9, 0.16]); + + % --- Define the 6 Axes and store in a struct --- + axes_h.live_psych = axes('Parent', hndl_live_plots_panel, 'Units', 'normalized', 'Position', [0.06, 0.1, 0.28, 0.8]); + title(axes_h.live_psych, 'Live Psychometric'); ylabel(axes_h.live_psych, 'P(Right)'); + + axes_h.live_hitrate = axes('Parent', hndl_live_plots_panel, 'Units', 'normalized', 'Position', [0.38, 0.1, 0.28, 0.8]); + title(axes_h.live_hitrate, 'Live Hit Rate'); xlabel(axes_h.live_hitrate, 'Block'); + + axes_h.live_stim = axes('Parent', hndl_live_plots_panel, 'Units', 'normalized', 'Position', [0.7, 0.1, 0.28, 0.8]); + title(axes_h.live_stim, 'Live Stimulus Dist.'); + + axes_h.custom_psych = axes('Parent', hndl_custom_plots_panel, 'Units', 'normalized', 'Position', [0.06, 0.1, 0.28, 0.8]); + title(axes_h.custom_psych, 'Custom Psychometric'); ylabel(axes_h.custom_psych, 'P(Right)'); + + axes_h.custom_hitrate = axes('Parent', hndl_custom_plots_panel, 'Units', 'normalized', 'Position', [0.38, 0.1, 0.28, 0.8]); + title(axes_h.custom_hitrate, 'Custom Hit Rate'); + + axes_h.custom_stim = axes('Parent', hndl_custom_plots_panel, 'Units', 'normalized', 'Position', [0.7, 0.1, 0.28, 0.8]); + title(axes_h.custom_stim, 'Custom Stimulus Dist.'); + + % Store the entire struct of handles in a single SoloParamHandle + SoloParamHandle(obj, 'PlotAxes', 'value', axes_h, 'saveable', false); + + % --- Arrange Controls in the Bottom Panel --- + + % Left Column: Show Table Toggle + set(get_ghandle(Show_Table_Toggle), 'Parent', hndl_controls_panel, 'Units', 'normalized', 'Position', [0.03, 0.25, 0.18, 0.5]); + + % Center Column: Custom Trial Plotting + uicontrol('Parent', hndl_controls_panel, 'Style','text', 'String','Start Trial', 'Units','normalized', 'Position',[0.25, 0.7, 0.1, 0.2]); + set(get_ghandle(Plot_Trial_Start), 'Parent', hndl_controls_panel, 'Units', 'normalized', 'Position', [0.35, 0.65, 0.1, 0.3]); + delete(get_lhandle(Plot_Trial_Start)); % remove bcontrol label + uicontrol('Parent', hndl_controls_panel, 'Style','text', 'String','End Trial', 'Units','normalized', 'Position',[0.47, 0.7, 0.1, 0.2]); + set(get_ghandle(Plot_Trial_End), 'Parent', hndl_controls_panel, 'Units', 'normalized', 'Position', [0.57, 0.65, 0.1, 0.3]); + delete(get_lhandle(Plot_Trial_End)); % remove bcontrol label + set(get_ghandle(Plot_Custom_Button), 'Parent', hndl_controls_panel, 'Units', 'normalized', 'Position', [0.35, 0.15, 0.22, 0.4]); + + % Right Column: Context Plot Button + set(get_ghandle(Plot_Context_Button), 'Parent', hndl_controls_panel, 'Units', 'normalized', 'Position', [0.68, 0.25, 0.18, 0.5]); + + % Far Right Column: Live Update Checkboxes + SoloParamHandle(obj, 'Update_Psychometric', 'value', uicontrol('Parent', hndl_controls_panel, 'Style', 'checkbox', 'Units', 'normalized', 'String', 'Psych', 'Value', 1, 'Position', [0.9, 0.65, 0.08, 0.3])); + SoloParamHandle(obj, 'Update_HitRate', 'value', uicontrol('Parent', hndl_controls_panel, 'Style', 'checkbox', 'Units', 'normalized', 'String', 'HitRate', 'Value', 1, 'Position', [0.9, 0.35, 0.08, 0.3])); + SoloParamHandle(obj, 'Update_Stimulus', 'value', uicontrol('Parent', hndl_controls_panel, 'Style', 'checkbox', 'Units', 'normalized', 'String', 'Stim', 'Value', 1, 'Position', [0.9, 0.05, 0.08, 0.3])); + + % --- Set Callbacks --- + set_callback(Show_Table_Toggle, {mfilename, 'show_hide_table'}); + set_callback(Plot_Custom_Button, {mfilename, 'PushButton_SelectedTrial'}); + set_callback(Plot_Context_Button, {mfilename, 'PushButton_Context'}); % To be defined later + + % --- Separate, Initially Invisible Figure for the Table --- + + SoloParamHandle(obj, 'myfig_table', 'value', uifigure('closerequestfcn', [mfilename '(' class(obj) ', ''hide_table'');'],... + 'Name', 'Psychometric Summary Table', 'Units', 'normalized','Visible', 'off','Position', [0.1, 0.2, 0.4, 0.6]), 'saveable', false); + + SoloParamHandle(obj, 'uit', 'value', uitable(value(myfig_table), 'Data', t, ... + 'Units', 'normalized', 'Position', [0.02 0.02 0.96 0.96], ... + 'ColumnEditable', [true, false, false, false, false, false, false, false, false, false, false, false]), ... + 'saveable', false); + + + % Returning the x , y position for the main callback GUI + varargout{1} = oldx; + varargout{2} = oldy; + + + %% Calculate Parameters + case 'Calculate_Params' + + try + % figure out if psychometric,sides = zeros(size(previous_sides)); % change the sides from ascii 'l' or 'r' to 0 and 1 + sides(previous_sides == 114) = 1; + + data.hit_history = hit_history; + data.previous_sides = sides; + data.stim_history = stimulus_history; + data.full_rule_history = Rule; + data.full_dist_right = stimulus_right_distribution_history; + data.full_dist_left = stimulus_left_distribution_history; + + handles.ui_table = value(uit); + handles.main_fig = value(myfig); + handles.axes_h = value(PlotAxes); + + Stim_Params = StimulusSection(obj,'stim_params'); + config.trials_per_block = value(trial_plot); + config.true_mu = Stim_Params(2); + config.stimuli_range = [Stim_Params(1), Stim_Params(3)]; + config.debug = true; + + state = value(states_value); + + % psychometric, hit rate and stim correct/incorrect histogram to be plotted or not + psych_obj = value(Update_Psychometric); + flags.psych = logical(psych_obj.Value); + + hit_obj = value(Update_HitRate); + flags.hit = logical(hit_obj.Value); + + stim_obj = value(Update_Stimulus); + flags.stim = logical(stim_obj.Value); + + varargout{1} = data; + varargout{2} = handles; + varargout{3} = config; + varargout{4} = ; + varargout{5} = flags; + + catch + varargout{1} = []; + varargout{2} = []; + varargout{3} = []; + varargout{4} = []; + varargout{5} = []; + end + + + %% Other actions + + case 'PushButton_Distribution_Switch' + + % eval(sprintf('present_context_dist = value(Context%i_Dist)',value(thiscontext))); + eval(sprintf('present_context_start = value(Context%i_trialStart);',value(thiscontext))); + eval(sprintf('present_context_end = value(Context%i_trialEnd);',value(thiscontext))); + + if ~strcmpi(Category_Dist,'Uniform') && present_context_end > present_context_start + + if value(thiscontext) < 3 % & ~strcmpi(present_context_dist,Category_Dist) + thiscontext.value = value(thiscontext) + 1; + eval(sprintf('Context%i_Dist.value = Category_Dist;',value(thiscontext))); + eval(sprintf('Context%i_trialStart.value = n_done_trials + 1;',value(thiscontext))); + eval(sprintf('Context%i_trialEnd.value = n_done_trials + 1;',value(thiscontext))); + eval(sprintf('make_visible(Context%i_Dist);',value(thiscontext))); + eval(sprintf('make_visible(Context%i_trialStart);',value(thiscontext))); + eval(sprintf('make_visible(Context%i_trialEnd);',value(thiscontext))); + end + + if strcmpi(Category_Dist,'Hard A') + StimulusSection(obj,'Pushbutton_SwitchDistribution','Hard B'); + elseif strcmpi(Category_Dist,'Hard B') + StimulusSection(obj,'Pushbutton_SwitchDistribution','Hard A'); + end + + % Also update the table and make changes to plot + [state, data, handles, config, flags] = PsychometricSection(obj,'Calculate_Params'); + % Calling the function to update the table and plot + state = RealTimeAnalysis('context_switch', state, data, handles, config, flags); + states_value.value = state; + end + + case 'StimSection_Distribution_Switch' + + eval(sprintf('present_context_start = value(Context%i_trialStart);',value(thiscontext))); + eval(sprintf('present_context_end = value(Context%i_trialEnd);',value(thiscontext))); + + if present_context_end > present_context_start + if value(thiscontext) < 3 % & ~strcmpi(present_context_dist,Category_Dist) + thiscontext.value = value(thiscontext) + 1; + eval(sprintf('Context%i_Dist.value = Category_Dist;',value(thiscontext))); + eval(sprintf('Context%i_trialStart.value = n_done_trials + 1;',value(thiscontext))); + eval(sprintf('Context%i_trialEnd.value = n_done_trials + 1;',value(thiscontext))); + eval(sprintf('make_visible(Context%i_Dist);',value(thiscontext))); + eval(sprintf('make_visible(Context%i_trialStart);',value(thiscontext))); + eval(sprintf('make_visible(Context%i_trialEnd);',value(thiscontext))); + + % Firstly make sure that at least 20 trials happened in this + % context if so then update the table and make changes to plot + if present_context_end - present_context_start >= 20 + [state, data, handles, config, flags] = PsychometricSection(obj,'Calculate_Params'); + % Calling the function to update the table and plot + state = RealTimeAnalysis('context_switch', state, data, handles, config, flags); + states_value.value = state; + end + end + else + eval(sprintf('Context%i_Dist.value = Category_Dist;',value(thiscontext))); + end + + case 'PushButton_SelectedTrial' + [state, data, handles, config, flags] = PsychometricSection(obj,'Calculate_Params'); + % Calling the function to update the table and plot + state = RealTimeAnalysis('custom', state, data, handles, config, flags,value(Plot_Trial_Start),value(Plot_Trial_End)); + states_value.value = state; + + case 'PushButton_Context' + [state, data, handles, config, flags] = PsychometricSection(obj,'Calculate_Params'); + % create a cell array containing the start and end of each context + context_trials = cell(1,value(thiscontext)); + for n_plot = 1:value(thiscontext) + eval(sprintf('trial_start = value(Context%i_trialStart);',n_plot)); + eval(sprintf('trial_end = value(Context%i_trialEnd);',n_plot)); + context_trials{1,n_plot} = [trial_start, trial_end]; + end + % Calling the function to update the table and plot + state = RealTimeAnalysis('context', state, data, handles, config, flags, context_trials); + states_value.value = state; + + %% update after each trial + case 'update' + % update the trial end for this context + if n_done_trials > 1 + eval(sprintf('Context%i_trialEnd.value = n_done_trials;',value(thiscontext))); + [state, data, handles, config, flags] = PsychometricSection(obj,'Calculate_Params'); + % Calling the function to update the table and plot + state = RealTimeAnalysis('live', state, data, handles, config, flags); + states_value.value = state; + end + + %% update the figure if user opened the figure window + case 'update_plot' + if n_done_trials > 30 + [state, data, handles, config, flags] = PsychometricSection(obj,'Calculate_Params'); + % Calling the function to update the table and plot + state = RealTimeAnalysis('redraw', state, data, handles, config, flags); + states_value.value = state; + end + + case 'evaluate' + if n_done_trials > 1 + try + % create a cell array containing the start and end of each context + context_trials = cell(1,value(thiscontext)); + psych_result = cell(1,value(thiscontext)); + + for n_context = 1:value(thiscontext) + eval(sprintf('trial_start = value(Context%i_trialStart);',n_context)); + eval(sprintf('trial_end = value(Context%i_trialEnd);',n_context)); + context_trials{1,n_context} = [trial_start, trial_end]; + + psych_result{1,n_context}.start_trial = trial_start; + psych_result{1,n_context}.end_trial = trial_end; + psych_result{1,n_context}.distribution_type = char(unique(category_distribution)); + psych_result{1,n_context}.calculated_boundary = nan; + psych_result{1,n_context}.total_hit_percent = -1; + psych_result{1,n_context}.total_violations_percent = -1; + psych_result{1,n_context}.right_correct_percent = -1; + psych_result{1,n_context}.left_correct_percent = -1; + end + + [state, data, handles, config, flags] = PsychometricSection(obj,'Calculate_Params'); + % Calling the function to update the table and plot + psych_result = RealTimeAnalysis('evaluate', state, data, handles, config, flags, context_trials); + + catch + fprintf(2, 'Error calculating correct pokes\n'); + disp(ME.message); + disp(ME.stack); + end + + end + + varargout{1} = psych_result; + + %% cases related to figure handles + + case 'close' + set(value(myfig), 'Visible', 'off'); + set(value(myfig_table), 'Visible', 'off'); + % Delete all SoloParamHandles who belong to this object and whose + % fullname starts with the name of this mfile: + if exist('myfig', 'var') && isa(myfig, 'SoloParamHandle') && ishandle(value(myfig)) %#ok + delete(value(myfig)); + end + if exist('myfig_table', 'var') && isa(myfig_table, 'SoloParamHandle') && ishandle(value(myfig_table)) %#ok + delete(value(myfig_table)); + end + delete_sphandle('owner', ['^@' class(obj) '$'], ... + 'fullname', ['^' mfilename]); + + case 'hide' + PsychometricShow.value = 0; + set(value(myfig), 'Visible', 'off'); + set(value(myfig_table), 'Visible', 'off'); + + %% Case Show_hide main psychometric GUI + case 'show_hide' + if PsychometricShow == 1 + set(value(myfig), 'Visible', 'on'); + + % Update the plot for live update + PsychometricSection(obj,'update_plot'); + + if Show_Table_Toggle == 1 + set(value(myfig_table), 'Visible', 'on'); + else + set(value(myfig_table), 'Visible', 'off'); + end + else + set(value(myfig), 'Visible', 'off'); + set(value(myfig_table), 'Visible', 'off'); + end + + %% case for table toggle + case 'show_hide_table' + if Show_Table_Toggle == 1 + set(value(myfig_table), 'Visible', 'on'); + else + set(value(myfig_table), 'Visible', 'off'); + end + + case 'hide_table' + Show_Table_Toggle.value = 0; + set(value(myfig_table), 'Visible', 'off'); +end + +end \ No newline at end of file diff --git a/Protocols/@ArpitSoundCatContinuous/PsychometricSection.m b/Protocols/@ArpitSoundCatContinuous/PsychometricSection.m index ad017b35..cf1a084c 100644 --- a/Protocols/@ArpitSoundCatContinuous/PsychometricSection.m +++ b/Protocols/@ArpitSoundCatContinuous/PsychometricSection.m @@ -8,6 +8,7 @@ % INIT % ------------------------------------------------------------------ + case 'init' if length(varargin) < 2 error('Need at least two arguments, x and y position, to initialize %s', mfilename); @@ -23,7 +24,7 @@ DispParam(obj, 'Context3_Dist', Category_Dist, x,y,'label','Context3_Distr','TooltipString','stim distribution for 3rd context'); make_invisible(Context3_Dist); make_invisible(Context3_trialStart);make_invisible(Context3_trialEnd); next_row(y); - + % Context 2 DispParam(obj, 'Context2_trialStart', 1,x,y, 'position', [x, y, 100 20],'label','t_start','TooltipString','2nd context started at this trial'); DispParam(obj, 'Context2_trialEnd', 1,x,y, 'position', [x + 101, y, 100 20],'label','t_end','TooltipString','2nd context ended at this trial') @@ -31,14 +32,14 @@ DispParam(obj, 'Context2_Dist', Category_Dist, x,y,'label','Context2_Distr','TooltipString','stim distribution for 2nd context'); make_invisible(Context2_Dist); make_invisible(Context2_trialStart);make_invisible(Context2_trialEnd); next_row(y); - + % Context 3 DispParam(obj, 'Context1_trialStart', 1,x,y, 'position', [x, y, 100 20],'label','Trial_start','TooltipString','1st context started at this trial'); DispParam(obj, 'Context1_trialEnd', 1,x,y, 'position', [x + 101, y, 100 20],'label','Trial_end','TooltipString','1st context ended at this trial') next_row(y); DispParam(obj, 'Context1_Dist', Category_Dist, x,y,'label','Context1_Distr','TooltipString','stim distribution for 1st context'); next_row(y); - + PushbuttonParam(obj, 'Switch_Distr', x, y, 'label', 'Change Stim Distribution','TooltipString', 'Change the context by switching distribution'); set_callback(Switch_Distr, {mfilename, 'PushButton_Distribution_Switch'}); %#ok (Defined just above) next_row(y,2) @@ -52,116 +53,168 @@ oldx=x; oldy=y; parentfig=double(gcf); - % SoloParams + % --- State Management with SoloParamHandles --- + state.last_analyzed_valid_trial = 0; + state.block_count = 0; + state.blockStatsHistory = struct('indices', {}, 'hitRates', {}, 'stimCounts', {}); + SoloParamHandle(obj, 'states_value', 'value', state); SoloParamHandle(obj, 'thiscontext', 'value', 1); SoloParamHandle(obj, 'last_trial_plotted', 'value', 0); - vars = ["Select","Rule","Distribution","Start_trial","End_trial","Slope","TrueBoundary",... - "CalBoundary","LapseA","LapseB","fit_Method", "Pred_Y"]; - vars_type = ["logical","string","string","double","double","double","double","double",... - "double","double","string","cell"]; - t = table('Size', [0, numel(vars)], ... - 'VariableTypes', vars_type, ... - 'VariableNames', vars); - SoloParamHandle(obj, 'TableData', 'value', t); - - % New Fig Window to show the Psychometric Curves - SoloParamHandle(obj, 'myfig', 'value', uifigure('closerequestfcn', [mfilename '(' class(obj) ', ''hide'');'],... - 'Name', mfilename, 'Units', 'normalized','Visible', 'off'), 'saveable', false); - - % Panel for Table - hndl_uipanelTable = uipanel('Units', 'normalized','Parent', value(myfig), ... - 'Title', 'Psychometric Summary', ... - 'Tag', 'uipanelTable', ... - 'Position', [0.06,0.2,0.4,0.75]); - SoloParamHandle(obj, 'uit','value', uitable(hndl_uipanelTable,'Units', 'normalized','Data',value(TableData),... - 'ColumnSortable', true,'Position',[0.02 0.02 0.95 0.95],... - 'ColumnEditable', [true, false, false, false, false, false,false, false, false, false, false,false]),'savable',false); - - % Panel for Axes - hndl_uipanelAxes = uipanel('Units', 'normalized','Parent', value(myfig), ... - 'Title', 'Psychometric Plots', ... - 'Tag', 'uipanelplot', ... - 'Position', [0.5,0.05,0.4,0.9]); - SoloParamHandle(obj, 'axplot1', 'value', axes(hndl_uipanelAxes,'Units', 'normalized','Position', [0.1,0.53, ... - 0.8,0.43]), 'saveable', false); - ylabel('Prob A','FontSize',8,'FontName','Cambria Math'); - - SoloParamHandle(obj, 'axplot2', 'value', axes(hndl_uipanelAxes,'Units', 'normalized','Position', [0.1,0.05, ... - 0.8,0.43]), 'saveable', false); - xlabel('Stim','FontSize',8,'FontName','Cambria Math'); - ylabel('Prob A','FontSize',8,'FontName','Cambria Math'); - - % Creating a new figure for Push button and edit box because they - % are based upon Java, so they wont plot on uifig which is web - % based - - % New Fig Window to show the Psychometric Curves - SoloParamHandle(obj, 'myfig1', 'value', figure('closerequestfcn', [mfilename '(' class(obj) ', ''hide'');'],... - 'MenuBar', 'none','Name', mfilename, 'Units', 'normalized','Visible', 'off'), 'saveable', false); - - % Edit and Push Button for Psych Plots - if ~exist('Plot_Trial_Start', 'var') || ~isa(Plot_Trial_Start, 'SoloParamHandle') - NumeditParam(obj, 'Plot_Trial_Start', 1, 1, 1,'label','Trial Start','labelpos','top'); - end - if ~exist('Plot_Trial_End', 'var') || ~isa(Plot_Trial_End, 'SoloParamHandle') - NumeditParam(obj, 'Plot_Trial_End', 1, 1, 1,'label','Trial End','labelpos','top'); - end - if ~exist('Plot_Selected', 'var') || ~isa(Plot_Selected, 'SoloParamHandle') - PushbuttonParam(obj, 'Plot_Selected', 1, 1); - set_callback(Plot_Selected, {mfilename, 'PushButton_SelectedTrial'}); - end - if ~exist('Plot_Context', 'var') || ~isa(Plot_Context, 'SoloParamHandle') - PushbuttonParam(obj, 'Plot_Context', 1, 1); - set_callback(Plot_Context, {mfilename, 'PushButton_Context'}); - end - hndl_uipanelSettings = uipanel('Units', 'normalized','Parent', value(myfig1), ... - 'Title', 'Plot Variables', ... - 'Tag', 'uipanelsetting', ... - 'Position', [0.02,0.02,0.9,0.9]); - - set(get_ghandle(Plot_Trial_Start), ... - 'Parent', hndl_uipanelSettings, ... - 'Units', 'normalized', ... - 'Tag', 'trial_start', ... - 'TooltipString', 'trial start for plot', ... - 'FontSize', 10.0, ... - 'Position', [0.02,0.5,0.15,0.2]); - delete(get_lhandle(Plot_Trial_Start)); - - set(get_ghandle(Plot_Trial_End), ... - 'Parent', hndl_uipanelSettings, ... - 'Units', 'normalized', ... - 'Tag', 'trial_end', ... - 'TooltipString', 'trial end for plot', ... - 'FontSize', 10.0, ... - 'Position', [0.28,0.5,0.15,0.2]); - delete(get_lhandle(Plot_Trial_End)); - - set(get_ghandle(Plot_Selected), ... - 'Parent', hndl_uipanelSettings, ... - 'Units', 'normalized', ... - 'Tag', 'plotselected_trials', ... - 'Position', [0.04,0.05,0.4,0.4]); - delete(get_lhandle(Plot_Selected)); - - set(get_ghandle(Plot_Context), ... - 'Parent', hndl_uipanelSettings, ... - 'Units', 'normalized', ... - 'Tag', 'plot_context', ... - 'Position', [0.55,0.05,0.4,0.4]); - delete(get_lhandle(Plot_Context)); - - % Edit and Push Button for Psych Plots - % NumeditParam(obj,'Plot_Trial_Start',1,50,100,'position',[5,10,100,20],'label','Trial Start','labelpos','top'); - % NumeditParam(obj,'Plot_Trial_End',1,150,100,'position',[150,10,100,20],'label','Trial End','labelpos','top'); - % PushbuttonParam(obj, 'Plot_Selected', 300, 20, 'label', 'Plot Selected Trials','TooltipString', 'Change the context by switching distribution'); - % set_callback(Plot_Selected, {mfilename, 'PushButton_SelectedTrial'}); - % PushbuttonParam(obj, 'Plot_Context', 300, 40, 'label', 'Plot Context','TooltipString', 'Change the context by switching distribution'); - % set_callback(Switch_Distr, {mfilename, 'PushButton_Context'}); + vars = ["Select","Rule","DistributionLeft","DistributionRight","Start_trial","End_trial","Slope","TrueBoundary",... + "CalBoundary","LapseA","LapseB","fit_Method", "Overall Hit %", "Left Hit %", "Right Hit %"]; + vars_type = ["logical","string","string","string","double","double","double","double","double",... + "double","double","string","double","double","double"]; + t = table('Size', [0, numel(vars)], 'VariableTypes', vars_type, 'VariableNames', vars); + + + % --- GUI Figure and Component Handles --- + + % Main analysis figure (web-based uifigure) + SoloParamHandle(obj, 'myfig', 'value', figure('closerequestfcn', [mfilename '(' class(obj) ', ''hide'');'],... + 'Name', 'Real-Time Analysis', 'Units', 'normalized','Position', [0.3, 0.1, 0.6, 0.8],'Visible', 'off'), 'saveable', false); + + % --- pre-Define Controls for the Bottom Panel later we will arrange it into panel. This is because if not done before it + % removes all the previous created axes panel. --- + + % Edit Boxes + NumeditParam(obj, 'Plot_Trial_Start', 1, 1, 1,'TooltipString', 'Start trial for custom plot'); + NumeditParam(obj, 'Plot_Trial_End', 30, 1, 1, 'TooltipString', 'End trial for custom plot'); + % Push Buttons + ToggleParam(obj, 'Show_Table_Toggle', 0, 1, 1, 'OnString', 'Table Shown', 'OffString', 'Show Table'); + PushbuttonParam(obj, 'Plot_Custom_Button', 1, 1, 'label', 'Plot Custom Range'); + PushbuttonParam(obj, 'Plot_Context_Button', 1, 1, 'label', 'Plot Context'); + + % --- Create Panels for Layout --- + % Top panel for live plots + hndl_live_plots_panel = uipanel('Parent', value(myfig), 'Title', 'Live Update Plots', ... + 'Units', 'normalized', 'Position', [0.05, 0.55, 0.9, 0.43]); + + % Middle panel for custom plots + hndl_custom_plots_panel = uipanel('Parent', value(myfig), 'Title', 'Custom Range Plots', ... + 'Units', 'normalized', 'Position', [0.05, 0.2, 0.9, 0.33]); + + % Bottom panel for controls + hndl_controls_panel = uipanel('Parent', value(myfig), 'Title', 'Controls', ... + 'Units', 'normalized', 'Position', [0.05, 0.02, 0.9, 0.16]); + + % --- Define the 6 Axes and store in a struct --- + axes_h.live_psych = axes('Parent', hndl_live_plots_panel, 'Units', 'normalized', 'Position', [0.06, 0.1, 0.28, 0.8]); + title(axes_h.live_psych, 'Live Psychometric'); ylabel(axes_h.live_psych, 'P(Right)'); + + axes_h.live_hitrate = axes('Parent', hndl_live_plots_panel, 'Units', 'normalized', 'Position', [0.38, 0.1, 0.28, 0.8]); + title(axes_h.live_hitrate, 'Live Hit Rate'); xlabel(axes_h.live_hitrate, 'Block'); + + axes_h.live_stim = axes('Parent', hndl_live_plots_panel, 'Units', 'normalized', 'Position', [0.7, 0.1, 0.28, 0.8]); + title(axes_h.live_stim, 'Live Stimulus Dist.'); + + axes_h.custom_psych = axes('Parent', hndl_custom_plots_panel, 'Units', 'normalized', 'Position', [0.06, 0.1, 0.28, 0.8]); + title(axes_h.custom_psych, 'Custom Psychometric'); ylabel(axes_h.custom_psych, 'P(Right)'); + + axes_h.custom_hitrate = axes('Parent', hndl_custom_plots_panel, 'Units', 'normalized', 'Position', [0.38, 0.1, 0.28, 0.8]); + title(axes_h.custom_hitrate, 'Custom Hit Rate'); + + axes_h.custom_stim = axes('Parent', hndl_custom_plots_panel, 'Units', 'normalized', 'Position', [0.7, 0.1, 0.28, 0.8]); + title(axes_h.custom_stim, 'Custom Stimulus Dist.'); + + % Store the entire struct of handles in a single SoloParamHandle + SoloParamHandle(obj, 'PlotAxes', 'value', axes_h, 'saveable', false); + + % --- Arrange Controls in the Bottom Panel --- + + % Left Column: Show Table Toggle + set(get_ghandle(Show_Table_Toggle), 'Parent', hndl_controls_panel, 'Units', 'normalized', 'Position', [0.03, 0.25, 0.18, 0.5]); + + % Center Column: Custom Trial Plotting + uicontrol('Parent', hndl_controls_panel, 'Style','text', 'String','Start Trial', 'Units','normalized', 'Position',[0.25, 0.7, 0.1, 0.2]); + set(get_ghandle(Plot_Trial_Start), 'Parent', hndl_controls_panel, 'Units', 'normalized', 'Position', [0.35, 0.65, 0.1, 0.3]); + delete(get_lhandle(Plot_Trial_Start)); % remove bcontrol label + uicontrol('Parent', hndl_controls_panel, 'Style','text', 'String','End Trial', 'Units','normalized', 'Position',[0.47, 0.7, 0.1, 0.2]); + set(get_ghandle(Plot_Trial_End), 'Parent', hndl_controls_panel, 'Units', 'normalized', 'Position', [0.57, 0.65, 0.1, 0.3]); + delete(get_lhandle(Plot_Trial_End)); % remove bcontrol label + set(get_ghandle(Plot_Custom_Button), 'Parent', hndl_controls_panel, 'Units', 'normalized', 'Position', [0.35, 0.15, 0.22, 0.4]); + + % Right Column: Context Plot Button + set(get_ghandle(Plot_Context_Button), 'Parent', hndl_controls_panel, 'Units', 'normalized', 'Position', [0.68, 0.25, 0.18, 0.5]); + + % Far Right Column: Live Update Checkboxes + SoloParamHandle(obj, 'Update_Psychometric', 'value', uicontrol('Parent', hndl_controls_panel, 'Style', 'checkbox', 'Units', 'normalized', 'String', 'Psych', 'Value', 1, 'Position', [0.9, 0.65, 0.08, 0.3])); + SoloParamHandle(obj, 'Update_HitRate', 'value', uicontrol('Parent', hndl_controls_panel, 'Style', 'checkbox', 'Units', 'normalized', 'String', 'HitRate', 'Value', 1, 'Position', [0.9, 0.35, 0.08, 0.3])); + SoloParamHandle(obj, 'Update_Stimulus', 'value', uicontrol('Parent', hndl_controls_panel, 'Style', 'checkbox', 'Units', 'normalized', 'String', 'Stim', 'Value', 1, 'Position', [0.9, 0.05, 0.08, 0.3])); + + % --- Set Callbacks --- + set_callback(Show_Table_Toggle, {mfilename, 'show_hide_table'}); + set_callback(Plot_Custom_Button, {mfilename, 'PushButton_SelectedTrial'}); + set_callback(Plot_Context_Button, {mfilename, 'PushButton_Context'}); % To be defined later + + % --- Separate, Initially Invisible Figure for the Table --- + + SoloParamHandle(obj, 'myfig_table', 'value', uifigure('closerequestfcn', [mfilename '(' class(obj) ', ''hide_table'');'],... + 'Name', 'Psychometric Summary Table', 'Units', 'normalized','Visible', 'off','Position', [0.1, 0.2, 0.4, 0.6]), 'saveable', false); + + SoloParamHandle(obj, 'uit', 'value', uitable(value(myfig_table), 'Data', t, ... + 'Units', 'normalized', 'Position', [0.02 0.02 0.96 0.96], ... + 'ColumnEditable', [true, false, false, false, false, false, false, false, false, false, false, false]), ... + 'saveable', false); + + + % Returning the x , y position for the main callback GUI + varargout{1} = oldx; + varargout{2} = oldy; + + + %% Calculate Parameters + case 'Calculate_Params' - varargout{1} = x; - varargout{2} = y; + try + % figure out if psychometric,sides = zeros(size(previous_sides)); % change the sides from ascii 'l' or 'r' to 0 and 1 + sides(previous_sides == 114) = 1; + + data.hit_history = hit_history; + data.previous_sides = sides; + data.stim_history = stimulus_history; + data.full_rule_history = Rule; + data.full_dist_right = stimulus_right_distribution_history; + data.full_dist_left = stimulus_left_distribution_history; + + handles.ui_table = value(uit); + handles.main_fig = value(myfig); + handles.axes_h = value(PlotAxes); + + Stim_Params = StimulusSection(obj,'stim_params'); + config.trials_per_block = value(trial_plot); + config.true_mu = Stim_Params(2); + config.stimuli_range = [Stim_Params(1), Stim_Params(3)]; + config.debug = false; + + state = value(states_value); + + % psychometric, hit rate and stim correct/incorrect histogram to be plotted or not + psych_obj = value(Update_Psychometric); + flags.psych = logical(psych_obj.Value); + + hit_obj = value(Update_HitRate); + flags.hit = logical(hit_obj.Value); + + stim_obj = value(Update_Stimulus); + flags.stim = logical(stim_obj.Value); + + varargout{1} = state; + varargout{2} = data; + varargout{3} = handles; + varargout{4} = config; + varargout{5} = flags; + + catch + varargout{1} = []; + varargout{2} = []; + varargout{3} = []; + varargout{4} = []; + varargout{5} = []; + end + + + %% Other actions case 'PushButton_Distribution_Switch' @@ -186,6 +239,12 @@ elseif strcmpi(Category_Dist,'Hard B') StimulusSection(obj,'Pushbutton_SwitchDistribution','Hard A'); end + + % Also update the table and make changes to plot + [state, data, handles, config, flags] = PsychometricSection(obj,'Calculate_Params'); + % Calling the function to update the table and plot + state = RealTimeAnalysis('context_switch', state, data, handles, config, flags); + states_value.value = state; end case 'StimSection_Distribution_Switch' @@ -202,347 +261,145 @@ eval(sprintf('make_visible(Context%i_Dist);',value(thiscontext))); eval(sprintf('make_visible(Context%i_trialStart);',value(thiscontext))); eval(sprintf('make_visible(Context%i_trialEnd);',value(thiscontext))); + + % Firstly make sure that at least 20 trials happened in this + % context if so then update the table and make changes to plot + if present_context_end - present_context_start >= 20 + [state, data, handles, config, flags] = PsychometricSection(obj,'Calculate_Params'); + % Calling the function to update the table and plot + state = RealTimeAnalysis('context_switch', state, data, handles, config, flags); + states_value.value = state; + end end else eval(sprintf('Context%i_Dist.value = Category_Dist;',value(thiscontext))); end case 'PushButton_SelectedTrial' - try - stim2analyze = stimulus_history(value(Plot_Trial_Start):value(Plot_Trial_End)); - sides2analyze = previous_sides(value(Plot_Trial_Start):value(Plot_Trial_End)); - hit_history2analyze = hit_history(value(Plot_Trial_Start):value(Plot_Trial_End)); - category_distribution2analyze = stimulus_distribution_history(value(Plot_Trial_Start):value(Plot_Trial_End)); - valid_hit_history_trials = find(~isnan(hit_history2analyze)); - sides = sides2analyze(valid_hit_history_trials); - stim = stim2analyze(valid_hit_history_trials); - category_distribution = category_distribution2analyze(valid_hit_history_trials); - hit_values = hit_history2analyze(valid_hit_history_trials)'; - resp = zeros(size(hit_values)); - - if strcmpi(Rule,'S1>S_boundary Left') % Category B is Left so its value is 1 - resp((sides == 108 & hit_values == 1) | (sides == 114 & hit_values == 0)) = 1; - else % Category B is Right so its value is 1 - resp((sides == 114 & hit_values == 1) | (sides == 108 & hit_values == 0)) = 1; - end - - Stim_Params = StimulusSection(obj,'stim_params'); - try - [y_pred, fitParams, methodUsed,fitStatus] = realtimepsychometricFit(stim,resp,Stim_Params); - catch - y_pred =[]; - fitParams = [nan,nan,nan,nan]; - methodUsed = "Failed"; - fitStatus = ""; - end - % Update the Table - - - % Convert category string with comma separator - category = unique(category_distribution); - category_selected = strjoin(category, ','); - - variableNames = ["Select","Rule","Distribution","Start_trial","End_trial","Slope","TrueBoundary",... - "CalBoundary","LapseA","LapseB","fit_Method", "Pred_Y"]; - newRow = table(false,string(Rule),string(category_selected),value(Plot_Trial_Start),value(Plot_Trial_End),... - fitParams(2),Stim_Params(2),fitParams(1),fitParams(3),fitParams(4),string(methodUsed) + " (" + fitStatus + ")",... - {y_pred},'VariableNames', variableNames); - t_new = [value(TableData);newRow]; - TableData.value = t_new; - t_handle = value(uit); - t_handle.Data = t_new; - - % Plot the Psychometric - xGrid = linspace(Stim_Params(1), Stim_Params(3), 300)'; - cla(value(axplot2)) - hold (value(axplot2),'on') - if ~(contains(methodUsed,'Failed') || contains(methodUsed,'Canceled')) - plot(value(axplot2),xGrid, y_pred, 'b-', 'LineWidth', 2); - xline(value(axplot2),fitParams(1), 'g--', sprintf('PSE (%.2f)', fitParams(1))); - end - xline(value(axplot2),Stim_Params(2), 'r--', 'Boundary'); - ylim([-0.05, 1.05]); - xlabel('Stimulus (dB)'); - ylabel('P("Category B" Response)'); - grid on; - catch - end + [state, data, handles, config, flags] = PsychometricSection(obj,'Calculate_Params'); + % Calling the function to update the table and plot + state = RealTimeAnalysis('custom', state, data, handles, config, flags,value(Plot_Trial_Start),value(Plot_Trial_End)); + states_value.value = state; case 'PushButton_Context' - try - Stim_Params = StimulusSection(obj,'stim_params'); - xGrid = linspace(Stim_Params(1), Stim_Params(3), 300)'; - cla(value(axplot2)); - hold (value(axplot2),'on') - - colors_to_use = createTemporalColormap(value(thiscontext)); - + [state, data, handles, config, flags] = PsychometricSection(obj,'Calculate_Params'); + % create a cell array containing the start and end of each context + context_trials = cell(1,value(thiscontext)); for n_plot = 1:value(thiscontext) - eval(sprintf('trial_start = value(Context%i_trialStart);',n_plot)); eval(sprintf('trial_end = value(Context%i_trialEnd);',n_plot)); - stim2analyze = stimulus_history(trial_start:trial_end); - sides2analyze = previous_sides(trial_start:trial_end); - hit_history2analyze = hit_history(trial_start:trial_end); - category_distribution2analyze = stimulus_distribution_history(trial_start:trial_end); - valid_hit_history_trials = find(~isnan(hit_history2analyze)); - sides = sides2analyze(valid_hit_history_trials); - stim = stim2analyze(valid_hit_history_trials); - category_distribution = category_distribution2analyze(valid_hit_history_trials); - hit_values = hit_history2analyze(valid_hit_history_trials)'; - resp = zeros(size(hit_values)); - - if strcmpi(Rule,'S1>S_boundary Left') % Category B is Left so its value is 1 - resp((sides == 108 & hit_values == 1) | (sides == 114 & hit_values == 0)) = 1; - else % Category B is Right so its value is 1 - resp((sides == 114 & hit_values == 1) | (sides == 108 & hit_values == 0)) = 1; - end - - try - [y_pred, fitParams, methodUsed,fitStatus] = realtimepsychometricFit(stim,resp,Stim_Params); - catch - y_pred =[]; - fitParams = [nan,nan,nan,nan]; - methodUsed = "Failed"; - fitStatus = ""; - end - % Update the Table - - % Convert category string with comma separator - category = unique(category_distribution); - category_selected = strjoin(category, ','); - variableNames = ["Select","Rule","Distribution","Start_trial","End_trial","Slope","TrueBoundary",... - "CalBoundary","LapseA","LapseB","fit_Method", "Pred_Y"]; - newRow = table(false,string(Rule),string(category_selected),trial_start,trial_end,... - fitParams(2),Stim_Params(2),fitParams(1),fitParams(3),fitParams(4),string(methodUsed) + " (" + fitStatus + ")",... - {y_pred},'VariableNames', variableNames); - t_new = [value(TableData);newRow]; - TableData.value = t_new; - t_handle = value(uit); - t_handle.Data = t_new; - - % Plot the Psychometric - if ~(contains(methodUsed,'Failed') || contains(methodUsed,'Canceled')) - plot(value(axplot2),xGrid, y_pred, 'Color', colors_to_use(n_plot, :), 'LineWidth', 2,'DisplayName',sprintf('Context %s',category_selected)); - xline(value(axplot2),fitParams(1), '--','Color', colors_to_use(n_plot, :), 'Label', sprintf('PSE (%.2f)', fitParams(1))); - end - + context_trials{1,n_plot} = [trial_start, trial_end]; end + % Calling the function to update the table and plot + state = RealTimeAnalysis('context', state, data, handles, config, flags, context_trials); + states_value.value = state; - xline(value(axplot2),Stim_Params(2), 'r--', 'Boundary'); - ylim([-0.05, 1.05]); - xlabel('Stimulus (dB)'); - ylabel('P("Category B" Response)'); - grid on; - catch - end - - %%update after each trial + %% update after each trial case 'update' % update the trial end for this context if n_done_trials > 1 eval(sprintf('Context%i_trialEnd.value = n_done_trials;',value(thiscontext))); + [state, data, handles, config, flags] = PsychometricSection(obj,'Calculate_Params'); + % Calling the function to update the table and plot + state = RealTimeAnalysis('live', state, data, handles, config, flags); + states_value.value = state; + end - try - % Check to see if we need to plot the Psychometric Curve - n_trials_valid = numel(find(~isnan(hit_history))); - - if n_trials_valid >= value(trial_plot) && n_trials_valid > value(last_trial_plotted) % means we may plot the psychometric - if n_trials_valid - value(last_trial_plotted) >= value(trial_plot) - - Stim_Params = StimulusSection(obj,'stim_params'); - sides2analyze = previous_sides(value(last_trial_plotted) + 1: n_done_trials); - stim2analyze = stimulus_history(value(last_trial_plotted) + 1: n_done_trials); - hit_history2analyze = hit_history(value(last_trial_plotted) + 1: n_done_trials); - category_distribution2analyze = stimulus_distribution_history(value(last_trial_plotted) + 1: n_done_trials); - trial2_analyze = find(~isnan(hit_history2analyze)); - stim = stim2analyze(trial2_analyze); - sides = sides2analyze(trial2_analyze); - category_distribution = category_distribution2analyze(trial2_analyze); - hit_values = hit_history2analyze(trial2_analyze)'; - resp = zeros(size(hit_values)); - - if strcmpi(Rule,'S1>S_boundary Left') % Category B is Left so its value is 1 - resp((sides == 108 & hit_values == 1) | (sides == 114 & hit_values == 0)) = 1; - else % Category B is Right so its value is 1 - resp((sides == 114 & hit_values == 1) | (sides == 108 & hit_values == 0)) = 1; - end - - % The psychometric Fit - try - [y_pred, fitParams, methodUsed,fitStatus] = realtimepsychometricFit(stim,resp,Stim_Params); - catch - y_pred =[]; - fitParams = [nan,nan,nan,nan]; - methodUsed = "Failed"; - fitStatus = ""; - end - % Update the Table - - % Based upon fit result we decide whether to plot - % psychometric or not - - % Determine the 'Select' status based on fit success - select_status = ismember(methodUsed, {'ridge', 'robust'}); - - variableNames = ["Select","Rule","Distribution","Start_trial","End_trial","Slope","TrueBoundary",... - "CalBoundary","LapseA","LapseB","fit_Method", "Pred_Y"]; - - % Convert category string with comma separator - category = unique(category_distribution); - category_selected = strjoin(category, ','); - - if select_status - newRow = table(select_status,string(Rule),string(category_selected),value(last_trial_plotted) + 1,n_done_trials,... - fitParams(2),Stim_Params(2),fitParams(1),fitParams(3),fitParams(4),string(methodUsed) + " (" + fitStatus + ")",... - {y_pred},'VariableNames', variableNames); - else - newRow = table(select_status,string(Rule),string(category_selected),value(last_trial_plotted) + 1,n_done_trials,... - nan,Stim_Params(2),nan,nan,nan,string(methodUsed) + " (" + fitStatus + ")",... - {[]},'VariableNames', variableNames); - end - - t_new = [value(TableData); newRow]; - TableData.value = t_new; - t_handle = value(uit); - t_handle.Data = t_new; - - % Identify the user selected trials (usually the one - % plotted realtime and not by user) - selectedData = t_new(t_new.Select, :); - xGrid = linspace(Stim_Params(1), Stim_Params(3), 300)'; - cla(value(axplot1)); - hold (value(axplot1),'on'); - - if ~isempty(selectedData) - % plot the selected Psychometric - num_to_plot = height(selectedData); - colors_to_use = createTemporalColormap(num_to_plot); - for i = 1:num_to_plot - % Get parameters for the i-th selected row - plot_data_matrix = selectedData.Pred_Y{i}; - if isempty(plot_data_matrix), continue; end - y_data = plot_data_matrix(:); - % Plot the curve using the generated color - plot(value(axplot1), xGrid, y_data, ... - 'Color', colors_to_use(i, :), ... - 'LineWidth', 2, ... - 'DisplayName', sprintf('Fit %d (Trials %d-%d)', i, selectedData.Start_trial(i), selectedData.End_trial(i))); - if i == num_to_plot - xline(value(axplot1),fitParams(1), 'g--', sprintf('PSE (%.2f)', fitParams(1))); - end - end - % else - % % Plot the recently calculated Psychometric - % plot(value(axplot1),xGrid, y_pred, 'b-', 'LineWidth', 2); hold on; - end - - xline(value(axplot1),Stim_Params(2), 'r--', 'Boundary'); - ylim([-0.05, 1.05]); - xlabel('Stimulus (dB)'); - ylabel('P("Category B" Response)'); - grid on; - legend(value(axplot1),'show','Location','southeast') - hold (value(axplot1),'off'); - - last_trial_plotted.value = n_done_trials; - end - end - - catch - end + %% update the figure if user opened the figure window + case 'update_plot' + if n_done_trials > 30 + [state, data, handles, config, flags] = PsychometricSection(obj,'Calculate_Params'); + % Calling the function to update the table and plot + state = RealTimeAnalysis('redraw', state, data, handles, config, flags); + states_value.value = state; end case 'evaluate' if n_done_trials > 1 - - Stim_Params = StimulusSection(obj,'stim_params'); - - psych_result = cell(1,value(thiscontext)); - - for n_context = 1:value(thiscontext) - - eval(sprintf('trial_start = value(Context%i_trialStart);',n_context)); - eval(sprintf('trial_end = value(Context%i_trialEnd);',n_context)); - stim2analyze = stimulus_history(trial_start:trial_end); - sides2analyze = previous_sides(trial_start:trial_end); - hit_history2analyze = hit_history(trial_start:trial_end); - category_distribution2analyze = stimulus_distribution_history(trial_start:trial_end); - valid_hit_history_trials = find(~isnan(hit_history2analyze)); - sides = sides2analyze(valid_hit_history_trials); - stim = stim2analyze(valid_hit_history_trials); - category_distribution = category_distribution2analyze(valid_hit_history_trials); - hit_values = hit_history2analyze(valid_hit_history_trials)'; - resp = zeros(size(hit_values)); - - if strcmpi(Rule,'S1>S_boundary Left') % Category B is Left so its value is 1 - resp((sides == 108 & hit_values == 1) | (sides == 114 & hit_values == 0)) = 1; - else % Category B is Right so its value is 1 - resp((sides == 114 & hit_values == 1) | (sides == 108 & hit_values == 0)) = 1; - end - - try - [~, fitParams, ~,~] = realtimepsychometricFit(stim,resp,Stim_Params); - catch - fitParams = [nan,nan,nan,nan]; + try + % create a cell array containing the start and end of each context + context_trials = cell(1,value(thiscontext)); + psych_result = cell(1,value(thiscontext)); + + for n_context = 1:value(thiscontext) + eval(sprintf('trial_start = value(Context%i_trialStart);',n_context)); + eval(sprintf('trial_end = value(Context%i_trialEnd);',n_context)); + context_trials{1,n_context} = [trial_start, trial_end]; + + psych_result{1,n_context}.start_trial = trial_start; + psych_result{1,n_context}.end_trial = trial_end; + psych_result{1,n_context}.distribution_type = char(unique(category_distribution)); + psych_result{1,n_context}.calculated_boundary = nan; + psych_result{1,n_context}.total_hit_percent = -1; + psych_result{1,n_context}.total_violations_percent = -1; + psych_result{1,n_context}.right_correct_percent = -1; + psych_result{1,n_context}.left_correct_percent = -1; end - psych_result{n_context}.trial_start = trial_start; - psych_result{n_context}.trial_end = trial_end; - psych_result{n_context}.context = char(unique(category_distribution)); - psych_result{n_context}.percept_boundary = fitParams(1); - try - psych_result{n_context}.total_correct = mean(hit_history2analyze,"omitnan"); - psych_result{n_context}.violations = mean(isnan(hit_history2analyze)); - psych_result{n_context}.right_correct = mean(hit_history2analyze(sides2analyze==114),"omitnan"); - psych_result{n_context}.left_correct = mean(hit_history2analyze(sides2analyze==108),"omitnan"); - catch - fprintf(2, 'Error calculating correct pokes\n'); - disp(ME.message); - disp(ME.stack); - psych_result{n_context}.total_correct = -1; - psych_result{n_context}.violations = -1; - psych_result{n_context}.right_correct = -1; - psych_result{n_context}.left_correct = -1; - end + [state, data, handles, config, flags] = PsychometricSection(obj,'Calculate_Params'); + % Calling the function to update the table and plot + psych_result = RealTimeAnalysis('evaluate', state, data, handles, config, flags, context_trials); + catch + fprintf(2, 'Error calculating correct pokes\n'); + disp(ME.message); + disp(ME.stack); end - varargout{1} = psych_result; - end - %% Case close + varargout{1} = psych_result; + + %% cases related to figure handles + case 'close' set(value(myfig), 'Visible', 'off'); - set(value(myfig1), 'Visible', 'off'); + set(value(myfig_table), 'Visible', 'off'); % Delete all SoloParamHandles who belong to this object and whose % fullname starts with the name of this mfile: if exist('myfig', 'var') && isa(myfig, 'SoloParamHandle') && ishandle(value(myfig)) %#ok delete(value(myfig)); end - if exist('myfig1', 'var') && isa(myfig, 'SoloParamHandle') && ishandle(value(myfig1)) %#ok - delete(value(myfig1)); + if exist('myfig_table', 'var') && isa(myfig_table, 'SoloParamHandle') && ishandle(value(myfig_table)) %#ok + delete(value(myfig_table)); end delete_sphandle('owner', ['^@' class(obj) '$'], ... 'fullname', ['^' mfilename]); - %% Case hide case 'hide' PsychometricShow.value = 0; set(value(myfig), 'Visible', 'off'); - set(value(myfig1), 'Visible', 'off'); + set(value(myfig_table), 'Visible', 'off'); - %% Case Show_hide + %% Case Show_hide main psychometric GUI case 'show_hide' if PsychometricShow == 1 - set(value(myfig), 'Visible', 'on'); - set(value(myfig1), 'Visible', 'on'); + set(value(myfig), 'Visible', 'on'); + + % Update the plot for live update + PsychometricSection(obj,'update_plot'); + + if Show_Table_Toggle == 1 + set(value(myfig_table), 'Visible', 'on'); + else + set(value(myfig_table), 'Visible', 'off'); + end else set(value(myfig), 'Visible', 'off'); - set(value(myfig1), 'Visible', 'on'); + set(value(myfig_table), 'Visible', 'off'); + end + + %% case for table toggle + case 'show_hide_table' + if Show_Table_Toggle == 1 + set(value(myfig_table), 'Visible', 'on'); + else + set(value(myfig_table), 'Visible', 'off'); end + case 'hide_table' + Show_Table_Toggle.value = 0; + set(value(myfig_table), 'Visible', 'off'); end end \ No newline at end of file diff --git a/Protocols/@ArpitSoundCatContinuous/StimulusSection.asv b/Protocols/@ArpitSoundCatContinuous/StimulusSection.asv index 9159c5b8..17d9b72b 100644 --- a/Protocols/@ArpitSoundCatContinuous/StimulusSection.asv +++ b/Protocols/@ArpitSoundCatContinuous/StimulusSection.asv @@ -204,7 +204,7 @@ switch action x=oldx; y=oldy; figure(parentfig); - SoloFunctionAddVars('PsychometricSection', 'ro_args',{'Category_Dist';'Rule';'boundary'}); + SoloFunctionAddVars('PsychometricSection', 'ro_args',{'Category_Dist';'Rule';'boundary';'Prob_Dist_Right';'Prob_Dist_Left'}); varargout{1} = x; varargout{2} = y; @@ -256,8 +256,8 @@ switch action if value(StimulusShow) == 1 % only run if the figure window is open - stim_values = value(stimulus_history); - stim_present = stim_values(end); + stim_values = value(thisstimlog); + stim_present = stim_values(n_done_trials); stim_history = stim_values(1:end-1); % figure out if legends,present stim and histogram to be plotted or not @@ -287,6 +287,8 @@ switch action 'plot_legend',legend_draw... ); plot_h.value = handle_h; + drawnow; + pause(0.01); end end @@ -312,7 +314,7 @@ switch action % Changing animals side reference to stimuli reference depending upon Rule % Even the setting in StimulusSection is based upon side not on the % basis of whether left or right of boundary - + if strcmp(Rule,'S1>S_boundary Left') % side left is right to stim boundary if strcmpi(ThisTrial, 'LEFT') stim_side = 'right'; @@ -777,6 +779,8 @@ switch action case 'update_stimulus_history' ps=value(stimulus_history); ps1 = value(stimulus_distribution_history); + ps2 = value(stimulus_right_distribution_history); + ps3 = value(stimulus_left_distribution_history); ps(n_done_trials)=value(thisstimlog(n_done_trials)); ps1{n_done_trials}=value(Category_Dist); stimulus_history.value=ps; diff --git a/Protocols/@ArpitSoundCatContinuous/StimulusSection.m b/Protocols/@ArpitSoundCatContinuous/StimulusSection.m index 62e59608..af403640 100644 --- a/Protocols/@ArpitSoundCatContinuous/StimulusSection.m +++ b/Protocols/@ArpitSoundCatContinuous/StimulusSection.m @@ -779,10 +779,18 @@ case 'update_stimulus_history' ps=value(stimulus_history); ps1 = value(stimulus_distribution_history); + ps2 = value(stimulus_right_distribution_history); + ps3 = value(stimulus_left_distribution_history); + ps(n_done_trials)=value(thisstimlog(n_done_trials)); ps1{n_done_trials}=value(Category_Dist); + ps2{n_done_trials}=value(Prob_Dist_Right); + ps3{n_done_trials}=value(Prob_Dist_Left); + stimulus_history.value=ps; stimulus_distribution_history.value = ps1; + stimulus_right_distribution_history.value = ps2; + stimulus_left_distribution_history.value = ps3; %% Case hide case 'hide' diff --git a/Protocols/@ArpitSoundCatContinuous/private/CreateSamples_from_Distribution.m b/Protocols/@ArpitSoundCatContinuous/private/CreateSamples_from_Distribution.m index 60faeb7c..50a3a00b 100644 --- a/Protocols/@ArpitSoundCatContinuous/private/CreateSamples_from_Distribution.m +++ b/Protocols/@ArpitSoundCatContinuous/private/CreateSamples_from_Distribution.m @@ -2,6 +2,9 @@ % CreateSamples_from_Distribution % +% This function is specifically called within StimulusSection to perform +% the actions + % This function generates random samples from a customizable bimodal % distribution and can optionally plot the distribution, a history histogram, % and dynamic current picks. diff --git a/Protocols/@ArpitSoundCatContinuous/private/RealTimeAnalysis.asv b/Protocols/@ArpitSoundCatContinuous/private/RealTimeAnalysis.asv new file mode 100644 index 00000000..dea47c8b --- /dev/null +++ b/Protocols/@ArpitSoundCatContinuous/private/RealTimeAnalysis.asv @@ -0,0 +1,768 @@ +function state = RealTimeAnalysis(action, state, data, handles, config, varargin) +% RealTimeAnalysis function to process and plot trial data for BControl. +% This function is called repeatedly from a parent script +% (e.g., Psychometric.m) and is made stateless by passing all required +% information in and out on every call. It uses a try/catch block to +% prevent crashes during live experiments. +% +% Args: +% action (char): The operation to perform. Can be: +% 'live': Standard update after a block of trials. +% 'redraw': Redraws live plots, typically when a figure becomes visible. +% 'context_switch': Special handling for leftover trials when a context changes. +% 'custom': Plots a single, user-defined range of trials. +% 'context': Plots multiple, user-defined ranges for comparison. +% +% state (struct): Contains variables that are modified and passed back. +% .blockStatsHistory (struct array): Stores analysis results for each block. +% .block_count (double): Counter for the number of blocks analyzed. +% .last_analyzed_valid_trial (double): Counter for the last valid trial included in an analysis. +% +% data (struct): Contains the complete, read-only data histories for the session. +% .hit_history (vector): History of hits (1), misses (0), or NaN. +% .previous_sides (vector): History of sides presented (e.g., 0 for left, 1 for right). +% .stim_history (vector): History of stimulus values. +% .full_rule_history (cell array of strings): History of the psychometric rule. +% .full_dist_right, .full_dist_left: Histories of stimulus distributions. +% +% handles (struct): Contains handles to all necessary GUI components. +% .main_fig (handle): Handle to the main analysis figure. +% .axes_h (struct): A struct containing handles to the 6 plot axes. +% .ui_table (handle): Handle to the results table. +% +% config (struct): Contains session-wide constants. +% .trials_per_block (double): Number of valid trials needed for a live update. +% .true_mu (double): The true boundary of the stimulus categories. +% .stimuli_range (1x2 vector): The [min, max] of possible stimulus values. +% .debug (logical): If true, errors will be rethrown; if false, they will be caught and displayed as warnings. +% +% varargin: Action-specific arguments based on the 'action' string. +% For 'live', 'redraw', 'context_switch': +% varargin{1} (struct): A flags struct with logical values. +% .psych (logical): Toggles the psychometric plot. +% .hit (logical): Toggles the hit rate plot. +% .stim (logical): Toggles the stimulus histogram. +% For 'custom': +% varargin{1} (struct): The flags struct (as above). +% varargin{2} (double): The start trial for the custom range. +% varargin{3} (double): The end trial for the custom range. +% For 'context': +% varargin{1} (struct): The flags struct (as above). +% varargin{2} (cell): A cell array of trial ranges, e.g., {[s1,e1], [s2,e2]}. +% +% Returns: +% state (struct): The updated state struct, which should be saved back +% to the corresponding SoloParamHandles in the parent script. + + try + switch lower(action) + % ================================================================= + % LIVE UPDATE ACTION + % ================================================================= + case 'live' + % This action is called on every trial to check if enough new + % data has accumulated for a block analysis. + if numel(varargin) < 1, error('Live action requires a flags struct.'); end + flags = varargin{1}; + + lastAnalyzed = state.last_analyzed_valid_trial; + validTrials = sum(~isnan(data.hit_history)); + + % Check if the number of new valid trials meets the block size requirement. + if (validTrials - lastAnalyzed) >= config.trials_per_block + valid_indices = find(~isnan(data.hit_history)); + buffer_indices = valid_indices(end - config.trials_per_block + 1 : end); + + dataBuffer.stim = data.stim_history(buffer_indices); + dataBuffer.hit = data.hit_history(buffer_indices); + dataBuffer.side = data.previous_sides(buffer_indices); + dataBuffer.indices = buffer_indices; + + % Analyze the new block and update the state. + state = analyzeLiveChunk(state, data, handles, config, dataBuffer, flags); + end + + % ================================================================= + % CONTEXT SWITCH ACTION + % ================================================================= + case 'context_switch' + % This action handles leftover trials when a context is manually changed. + if numel(varargin) < 1, error('context_switch action requires a flags struct.'); end + flags = varargin{1}; + + lastAnalyzed = state.last_analyzed_valid_trial; + validTrials = sum(~isnan(data.hit_history)); + remaining_valid_trials = validTrials - lastAnalyzed; + + if remaining_valid_trials > 0 + valid_indices = find(~isnan(data.hit_history)); + new_valid_indices = valid_indices(valid_indices > find(valid_indices == lastAnalyzed, 1, 'last')); + + % If there are few leftover trials, merge them with the previous block. + if remaining_valid_trials < 20 && state.block_count > 0 + last_block_indices = state.blockStatsHistory(end).indices; + combined_indices = [last_block_indices, new_valid_indices']; + + dataBuffer.stim = data.stim_history(combined_indices); + dataBuffer.hit = data.hit_history(combined_indices); + dataBuffer.side = data.previous_sides(combined_indices); + dataBuffer.indices = combined_indices; + + % Re-analyze the merged block, replacing the last entry. + state = reAnalyzeLastChunk(state, data, handles, config, dataBuffer, flags); + + % If there are enough leftover trials, treat them as a new block. + elseif remaining_valid_trials >= 20 + dataBuffer.stim = data.stim_history(new_valid_indices); + dataBuffer.hit = data.hit_history(new_valid_indices); + dataBuffer.side = data.previous_sides(new_valid_indices); + dataBuffer.indices = new_valid_indices; + + state = analyzeLiveChunk(state, data, handles, config, dataBuffer, flags); + end + end + + % ================================================================= + % REDRAW ACTION + % ================================================================= + case 'redraw' + % This action is called when the GUI figure becomes visible. It only + % redraws the live plots using the current, pre-computed state. + if numel(varargin) < 1, error('Redraw action requires a flags struct.'); end + flags = varargin{1}; + + updateLivePlots(state, data, handles, config, flags); + + % ================================================================= + % CUSTOM PLOT ACTION + % ================================================================= + case 'custom' + % This action plots a single, user-defined range of trials on the custom axes. + if numel(varargin) < 3, error('Custom action requires flags, start_trial, and end_trial.'); end + flags = varargin{1}; + start_idx = varargin{2}; + end_idx = varargin{3}; + + if start_idx >= end_idx || start_idx < 1 || end_idx > numel(data.hit_history), warning('Invalid trial range for custom plot.'); return; end + + indices = start_idx:end_idx; + hit_chunk = data.hit_history(indices); + valid_mask = ~isnan(hit_chunk); + if sum(valid_mask) < 10, warning('Not enough valid trials in custom range to plot.'); return; end + + stim_fit = data.stim_history(indices(valid_mask)); + side_fit = data.previous_sides(indices(valid_mask)); + hit_fit = hit_chunk(valid_mask); + + rules_in_chunk = unique(data.full_rule_history(indices(valid_mask))); + if numel(rules_in_chunk) > 1, warning('Multiple rules in custom range; using the first one.'); end + current_rule = rules_in_chunk{1}; + + if flags.psych, plotCustomPsychometric(handles.axes_h.custom_psych, stim_fit, side_fit, hit_fit, config, current_rule); end + if flags.hit, plotCustomHitRates(handles.axes_h.custom_hitrate, hit_fit, side_fit, [start_idx, end_idx], state.blockStatsHistory); end + if flags.stim, plotCustomStimulusHistogram(handles.axes_h.custom_stim, stim_fit, hit_fit, [start_idx, end_idx], state.blockStatsHistory, config); end + + % ================================================================= + % CONTEXT PLOT ACTION + % ================================================================= + case 'context' + % This action plots multiple trial ranges on the custom axes for comparison. + if numel(varargin) < 2, error('Context action requires flags and a cell array of contexts.'); end + flags = varargin{1}; + contexts = varargin{2}; + + if ~iscell(contexts) || isempty(contexts), error('Contexts must be a non-empty cell array of [start, end] pairs.'); end + + num_contexts = numel(contexts); + context_colors = createTemporalColormap(num_contexts); + + psych_data = cell(1, num_contexts); + hit_rate_data = zeros(num_contexts, 3); + hit_rate_std = zeros(num_contexts, 3); + stim_hist_data = cell(1, num_contexts); + + % Loop through each context, gather its data, and perform analysis. + for i = 1:num_contexts + start_idx = contexts{i}(1); + end_idx = contexts{i}(2); + + if start_idx >= end_idx || start_idx < 1 || end_idx > numel(data.hit_history), continue; end + + indices = start_idx:end_idx; + hit_chunk = data.hit_history(indices); + valid_mask = ~isnan(hit_chunk); + if sum(valid_mask) < 10, continue; end + + stim_fit = data.stim_history(indices(valid_mask)); + side_fit = data.previous_sides(indices(valid_mask)); + hit_fit = hit_chunk(valid_mask); + + rules_in_context = unique(data.full_rule_history(indices(valid_mask))); + current_rule = rules_in_context{1}; + + physical_response = zeros(size(hit_fit)); + physical_response(hit_fit == 1) = side_fit(hit_fit == 1); + physical_response(hit_fit == 0) = 1 - side_fit(hit_fit == 0); + + response_for_fitting = physical_response; + if contains(current_rule, 'Left', 'IgnoreCase', true), response_for_fitting = 1 - response_for_fitting; end + + options.MinTrials = 10; + [y_pred, fitParams, ~, ~] = realtimepsychometricFit(stim_fit, response_for_fitting, config.stimuli_range, options); + psych_data{i} = struct('y_pred', y_pred, 'fitParams', fitParams, 'stim_fit', stim_fit, 'physical_response', physical_response, 'rule', current_rule); + + hit_rate_data(i, 1) = 100 * sum(hit_fit == 1) / numel(hit_fit); + hit_rate_data(i, 2) = 100 * sum(hit_fit(side_fit==0)==1) / sum(side_fit==0); + hit_rate_data(i, 3) = 100 * sum(hit_fit(side_fit==1)==1) / sum(side_fit==1); + + left_edges = linspace(min(config.stimuli_range), config.true_mu, 6); + right_edges = linspace(config.true_mu, max(config.stimuli_range), 6); + bin_edges = unique([left_edges, right_edges]); + stim_hist_data{i}.correct = histcounts(stim_fit(hit_fit == 1), bin_edges); + stim_hist_data{i}.incorrect = histcounts(stim_fit(hit_fit == 0), bin_edges); + + relevant_blocks = []; + for j = 1:numel(state.blockStatsHistory) + block_indices = state.blockStatsHistory(j).indices; + if min(block_indices) >= start_idx && max(block_indices) <= end_idx + relevant_blocks = [relevant_blocks, state.blockStatsHistory(j)]; + end + end + + if ~isempty(relevant_blocks) + hit_rate_std(i, 1) = std([relevant_blocks.hitRates.overall], 'omitnan'); + hit_rate_std(i, 2) = std([relevant_blocks.hitRates.left], 'omitnan'); + hit_rate_std(i, 3) = std([relevant_blocks.hitRates.right], 'omitnan'); + counts_matrix_corr = vertcat(relevant_blocks.stimCounts.correct); + counts_matrix_incorr = vertcat(relevant_blocks.stimCounts.incorrect); + stim_hist_data{i}.mean_corr = mean(counts_matrix_corr, 1); + stim_hist_data{i}.std_corr = std(counts_matrix_corr, 0, 1); + stim_hist_data{i}.mean_incorr = mean(counts_matrix_incorr, 1); + stim_hist_data{i}.std_incorr = std(counts_matrix_incorr, 0, 1); + else + hit_rate_std(i, :) = 0; + end + end + + if flags.psych, plotContextPsychometric(handles.axes_h.custom_psych, psych_data, config, context_colors); end + if flags.hit, plotContextHitRates(handles.axes_h.custom_hitrate, hit_rate_data, hit_rate_std, context_colors); end + if flags.stim, plotContextStimulusHistogram(handles.axes_h.custom_stim, stim_hist_data, config, context_colors); end + + otherwise + error('Unknown action: "%s". Use "live", "custom", or "context".', action); + end + catch ME + % Error handling block + warning('RealTimeAnalysisApp:Error', 'An error occurred in RealTimeAnalysisApp: %s', ME.message); + + % In debug mode, rethrow the error to pause execution. + if config.debug + rethrow(ME); + end + % In experiment mode, the function will gracefully return the original state. + end + + % ================================================================= + % NESTED HELPER FUNCTIONS + % ================================================================= + + %% LIVE ANALYSIS HELPERS + function state = analyzeLiveChunk(state, data, handles, config, dataBuffer, flags) + % This function analyzes a new chunk of data and ADDS it to the history. + state.block_count = state.block_count + 1; + [newBlockStat, newRow] = processBlock(data, config, dataBuffer); + state.blockStatsHistory = [state.blockStatsHistory, newBlockStat]; + updateTable(handles, newRow); + + % Only plot if the figure is visible to save computation. + if strcmp(get(handles.main_fig, 'Visible'), 'on') + updateLivePlots(state, data, handles, config, flags); + end + + state.last_analyzed_valid_trial = sum(~isnan(data.hit_history)); + end + + function state = reAnalyzeLastChunk(state, data, handles, config, dataBuffer, flags) + % This function re-analyzes a chunk and REPLACES the last entry in the history. + [newBlockStat, newRow] = processBlock(data, config, dataBuffer); + state.blockStatsHistory(end) = newBlockStat; + replaceLastTableRow(handles, newRow); + + if strcmp(get(handles.main_fig, 'Visible'), 'on') + updateLivePlots(state, data, handles, config, flags); + end + + state.last_analyzed_valid_trial = sum(~isnan(data.hit_history)); + end + + function [blockStat, tableRow] = processBlock(data, config, dataBuffer) + % Common processing logic for any block of data. + % 1. Convert hit/side to a physical response (0=left, 1=right). + physical_response = zeros(size(dataBuffer.hit)); + physical_response(dataBuffer.hit == 1) = dataBuffer.side(dataBuffer.hit == 1); + physical_response(dataBuffer.hit == 0) = 1 - dataBuffer.side(dataBuffer.hit == 0); + + % 2. Determine the rule for this block. + current_rule = data.full_rule_history; + + % 3. Invert the response if the rule is 'Left' for correct fitting. + response_for_fitting = physical_response; + if contains(current_rule, 'Left', 'IgnoreCase', true) + response_for_fitting = 1 - physical_response; + end + + % 4. Fit the psychometric curve. + options.MinTrials = 20; + [~, fitParams, methodUsed, fitStatus] = realtimepsychometricFit(dataBuffer.stim, response_for_fitting, config.stimuli_range, options); + + % 5. Calculate hit rates. + hr.overall = 100 * sum(dataBuffer.hit == 1) / numel(dataBuffer.hit); + left_mask = (dataBuffer.side == 0); + if any(left_mask), hr.left = 100 * sum(dataBuffer.hit(left_mask)==1) / sum(left_mask); else, hr.left = NaN; end + right_mask = (dataBuffer.side == 1); + if any(right_mask), hr.right = 100 * sum(dataBuffer.hit(right_mask)==1) / sum(right_mask); else, hr.right = NaN; end + + % 6. Package block statistics for history. + blockStat.indices = dataBuffer.indices; + blockStat.hitRates = hr; + + % 7. Bin stimulus data for history. + left_edges = linspace(min(config.stimuli_range), config.true_mu, 6); + right_edges = linspace(config.true_mu, max(config.stimuli_range), 6); + bin_edges = unique([left_edges, right_edges]); + stim_correct = dataBuffer.stim(dataBuffer.hit == 1); + stim_incorrect = dataBuffer.stim(dataBuffer.hit == 0); + blockStat.stimCounts.correct = histcounts(stim_correct, bin_edges); + blockStat.stimCounts.incorrect = histcounts(stim_incorrect, bin_edges); + + % 8. Package table row data. + start_trial = min(dataBuffer.indices); end_trial = max(dataBuffer.indices); + dist_right = strjoin(string(unique(data.full_dist_right(dataBuffer.indices))), ', '); + dist_left = strjoin(string(unique(data.full_dist_left(dataBuffer.indices))), ', '); + + select_status = ismember(methodUsed, {'ridge', 'robust'}); + if select_status + tableRow = {select_status, current_rule, dist_left, dist_right, start_trial, end_trial, fitParams(2), config.true_mu, fitParams(1), fitParams(3), fitParams(4), string(methodUsed) + " (" + fitStatus + ")", hr.overall, hr.left, hr.right}; + else + tableRow = {select_status, current_rule, dist_left, dist_right, start_trial, end_trial, NaN, config.true_mu, NaN, NaN, NaN, string(methodUsed) + " (" + fitStatus + ")", hr.overall, hr.left, hr.right}; + end + end + + function updateTable(handles, newRow) + % Appends a new row to the table and selects it. + currentData = get(handles.ui_table, 'Data'); + if ~isempty(currentData), currentData.Select(:) = false; end + newRow{1} = true; % Select the new row + set(handles.ui_table, 'Data', [currentData; newRow]); + end + + function replaceLastTableRow(handles, newRow) + % Replaces the last row of the table and selects it. + currentData = get(handles.ui_table, 'Data'); + if ~isempty(currentData) + currentData.Select(:) = false; + newRow{1} = true; % Select the new row + currentData(end,:) = newRow; + set(handles.ui_table, 'Data', currentData); + end + end + + function updateLivePlots(state, data, handles, config, flags) + % Calls the individual plot updaters based on checkbox flags. + if flags.psych, updatePsychometricPlot(handles.axes_h.live_psych, handles.ui_table, config); end + if flags.hit, updateHitRatePlot(handles.axes_h.live_hitrate, state.blockStatsHistory); end + if flags.stim, updateStimulusHistogram(handles.axes_h.live_stim, data, handles.ui_table, config); end + end + + function updatePsychometricPlot(ax, ui_table_handle, config) + % Redraws the live psychometric plot from data in the table. + allData = get(ui_table_handle, 'Data'); + selectedData = allData(allData.Select, :); + cla(ax, 'reset'); hold(ax, 'on'); + + xline(ax, config.true_mu, '--k', 'LineWidth', 1.5, 'HandleVisibility', 'off'); + yline(ax, 0.5, ':', 'Color', [0.5 0.5 0.5], 'HandleVisibility', 'off'); + + if ~isempty(selectedData) + num_to_plot = height(selectedData); + colors = createTemporalColormap(num_to_plot); + + psychometricFun = @(params, x) params(3) + (1 - params(3) - params(4)) ./ (1 + exp(-(x - params(1)) / params(2))); + xGrid = linspace(config.stimuli_range(1), config.stimuli_range(2), 300)'; + + for i = 1:num_to_plot + row = selectedData(i, :); + + fitParams = [row.CalBoundary, row.Slope, row.LapseA, row.LapseB]; + if any(isnan(fitParams)), continue; end + + y_curve = psychometricFun(fitParams, xGrid); + + alpha = 0.4; width = 1.5; + if i == num_to_plot, alpha = 0.9; width = 2.5; end + + plot(ax, xGrid, y_curve, 'Color', [colors(i,:), alpha], 'LineWidth', width, 'DisplayName', sprintf('Block %d', i)); + xline(ax, row.CalBoundary, '-', 'Color', [colors(i,:), alpha], 'LineWidth', width-0.5, 'HandleVisibility', 'off'); + end + legend(ax, 'show', 'Location', 'southeast'); + end + grid(ax, 'on'); hold(ax, 'off'); + ylabel(ax, 'P(Choice)'); title(ax, 'Live Psychometric Curves'); + end + + function updateHitRatePlot(ax, blockStats) + % Redraws the live hit rate plot from the block history. + if isempty(blockStats), return; end + + hr_overall = [blockStats.hitRates.overall]; + hr_left = [blockStats.hitRates.left]; + hr_right = [blockStats.hitRates.right]; + + cla(ax, 'reset'); hold(ax, 'on'); + x_axis = 1:numel(hr_overall); + plot(ax, x_axis, hr_overall, '-ok', 'LineWidth', 2, 'DisplayName', 'Overall'); + plot(ax, x_axis, hr_left, '--ob', 'LineWidth', 1.5, 'DisplayName', 'Left'); + plot(ax, x_axis, hr_right, '--or', 'LineWidth', 1.5, 'DisplayName', 'Right'); + hold(ax, 'off'); legend(ax, 'show', 'Location', 'southeast'); + xlim(ax, [0.5, max(10, numel(hr_overall) + 0.5)]); ylim(ax, [0 100]); + xlabel(ax, 'Block Number'); ylabel(ax, 'Hit %'); + title(ax, 'Live Hit Rate per Block'); + end + + function updateStimulusHistogram(ax, data, ui_table_handle, config) + % Redraws the live stimulus histogram for the latest block. + table_data = get(ui_table_handle, 'Data'); + if isempty(table_data), return; end + latest_block_data = table_data(end, :); + + indices = latest_block_data.Start_trial:latest_block_data.End_trial; + valid_mask = ~isnan(data.hit_history(indices)); + stim_valid = data.stim_history(indices(valid_mask)); + hit_valid = data.hit_history(indices(valid_mask)); + + cla(ax, 'reset'); hold(ax, 'on'); + histogram(ax, stim_valid(hit_valid == 0), 'BinMethod', 'auto', 'FaceColor', [0.9 0.2 0.1], 'DisplayName', 'Incorrect'); + histogram(ax, stim_valid(hit_valid == 1), 'BinMethod', 'auto', 'FaceColor', [0 0.65 0], 'DisplayName', 'Correct'); + xline(ax, config.true_mu, '--k', 'Boundary', 'LineWidth', 2); + hold(ax, 'off'); legend(ax, 'Location', 'northwest'); + title(ax, sprintf('Stimulus Dist. (Block %d)', size(table_data, 1))); + xlabel(ax, 'Stimulus Value'); ylabel(ax, 'Trial Count'); + end + + %% CUSTOM PLOTTING HELPERS + function plotCustomPsychometric(ax, stim_fit, side_fit, hit_fit, config, rule) + physical_response = zeros(size(hit_fit)); + physical_response(hit_fit == 1) = side_fit(hit_fit == 1); + physical_response(hit_fit == 0) = 1 - side_fit(hit_fit == 0); + + response_for_fitting = physical_response; + y_label = 'P(Right)'; + + if contains(rule, 'Left', 'IgnoreCase', true) + response_for_fitting = 1 - physical_response; + y_label = 'P(Left)'; + end + + options.MinTrials = 10; + [y_pred, fitParams, ~, ~] = realtimepsychometricFit(stim_fit, response_for_fitting, config.stimuli_range, options); + + cla(ax, 'reset'); hold(ax, 'on'); + xGrid = linspace(config.stimuli_range(1), config.stimuli_range(2), 300)'; + plot(ax, xGrid, y_pred, 'r-', 'LineWidth', 2, 'DisplayName', 'Fitted Curve'); + xline(ax, config.true_mu, '--k', 'LineWidth', 1.5, 'DisplayName', 'True Boundary'); + xline(ax, fitParams(1), '--b', 'LineWidth', 1.5, 'DisplayName', 'Calculated'); + yline(ax, 0.5, ':', 'Color', [0.5 0.5 0.5]); + grid(ax, 'on'); hold(ax, 'off'); + legend(ax); title(ax, sprintf('Custom Fit (Mu=%.2f)', fitParams(1))); + xlabel(ax, 'Stimulus'); ylabel(ax, y_label); + end + + function plotCustomHitRates(ax, hit_fit, side_fit, custom_range, blockStats) + cla(ax, 'reset'); hold(ax, 'on'); + + hr_custom.overall = 100 * sum(hit_fit == 1) / numel(hit_fit); + hr_custom.left = 100 * sum(hit_fit(side_fit==0)==1) / sum(side_fit==0); + hr_custom.right = 100 * sum(hit_fit(side_fit==1)==1) / sum(side_fit==1); + + relevant_blocks = []; + for i = 1:numel(blockStats) + block_indices = blockStats(i).indices; + if min(block_indices) >= custom_range(1) && max(block_indices) <= custom_range(2) + relevant_blocks = [relevant_blocks, blockStats(i)]; + end + end + + if ~isempty(relevant_blocks) + std_dev.overall = std([relevant_blocks.hitRates.overall], 'omitnan'); + std_dev.left = std([relevant_blocks.hitRates.left], 'omitnan'); + std_dev.right = std([relevant_blocks.hitRates.right], 'omitnan'); + else + std_dev.overall = 0; std_dev.left = 0; std_dev.right = 0; + end + + cats = categorical({'Overall', 'Left', 'Right'}); + errorbar(ax, cats, [hr_custom.overall, hr_custom.left, hr_custom.right], ... + [std_dev.overall, std_dev.left, std_dev.right], ... + 'o', 'MarkerSize', 8, 'CapSize', 15, 'LineWidth', 1.5); + + ylabel(ax, 'Hit %'); title(ax, 'Hit Rates (w/ Block STD)'); + ylim(ax, [0 105]); grid(ax, 'on'); + end + + function plotCustomStimulusHistogram(ax, stim_fit, hit_fit, custom_range, blockStats, config) + cla(ax, 'reset'); hold(ax, 'on'); + + left_edges = linspace(min(config.stimuli_range), config.true_mu, 6); + right_edges = linspace(config.true_mu, max(config.stimuli_range), 6); + bin_edges = unique([left_edges, right_edges]); + bin_centers = (bin_edges(1:end-1) + bin_edges(2:end)) / 2; + + counts_custom.correct = histcounts(stim_fit(hit_fit == 1), bin_edges); + counts_custom.incorrect = histcounts(stim_fit(hit_fit == 0), bin_edges); + + relevant_blocks = []; + for i = 1:numel(blockStats) + block_indices = blockStats(i).indices; + if min(block_indices) >= custom_range(1) && max(block_indices) <= custom_range(2) + relevant_blocks = [relevant_blocks, blockStats(i)]; + end + end + + if ~isempty(relevant_blocks) + counts_matrix_corr = vertcat(relevant_blocks.stimCounts.correct); + counts_matrix_incorr = vertcat(relevant_blocks.stimCounts.incorrect); + + mean_corr = mean(counts_matrix_corr, 1); + std_corr = std(counts_matrix_corr, 0, 1); + mean_incorr = mean(counts_matrix_incorr, 1); + std_incorr = std(counts_matrix_incorr, 0, 1); + + fill(ax, [bin_centers, fliplr(bin_centers)], [mean_corr - std_corr, fliplr(mean_corr + std_corr)], ... + [0 0.65 0], 'FaceAlpha', 0.2, 'EdgeColor', 'none', 'DisplayName', 'Correct (Block STD)'); + fill(ax, [bin_centers, fliplr(bin_centers)], [mean_incorr - std_incorr, fliplr(mean_incorr + std_incorr)], ... + [0.9 0.2 0.1], 'FaceAlpha', 0.2, 'EdgeColor', 'none', 'DisplayName', 'Incorrect (Block STD)'); + end + + plot(ax, bin_centers, counts_custom.correct, '-o', 'Color', [0 0.65 0], 'LineWidth', 2, 'DisplayName', 'Correct (Custom)'); + plot(ax, bin_centers, counts_custom.incorrect, '-o', 'Color', [0.9 0.2 0.1], 'LineWidth', 2, 'DisplayName', 'Incorrect (Custom)'); + + xline(ax, config.true_mu, '--k', 'Boundary', 'LineWidth', 2); + hold(ax, 'off'); legend(ax, 'Location', 'northwest'); + title(ax, 'Stimulus Distribution (w/ Block STD)'); + xlabel(ax, 'Stimulus Value'); ylabel(ax, 'Trial Count'); + end + + %% CONTEXT PLOTTING HELPERS + function plotContextPsychometric(ax, psych_data, config, colors) + cla(ax, 'reset'); hold(ax, 'on'); + xGrid = linspace(config.stimuli_range(1), config.stimuli_range(2), 300)'; + + xline(ax, config.true_mu, '--k', 'LineWidth', 1.5, 'DisplayName', 'True Boundary'); + yline(ax, 0.5, ':', 'Color', [0.5 0.5 0.5]); + + contains_left_rule = false; + contains_right_rule = false; + + for i = 1:numel(psych_data) + if isempty(psych_data{i}), continue; end + + if contains(psych_data{i}.rule, 'Left', 'IgnoreCase', true) + contains_left_rule = true; + else + contains_right_rule = true; + end + + plot(ax, xGrid, psych_data{i}.y_pred, '-', 'Color', colors(i,:), 'LineWidth', 2, 'DisplayName', sprintf('Context %d', i)); + xline(ax, psych_data{i}.fitParams(1), '--', 'Color', colors(i,:), 'LineWidth', 1.5, 'HandleVisibility', 'off'); + end + + if contains_left_rule && ~contains_right_rule, ylabel(ax, 'P(Left)'); + elseif ~contains_left_rule && contains_right_rule, ylabel(ax, 'P(Right)'); + else, ylabel(ax, 'P(Choice)'); end + + grid(ax, 'on'); hold(ax, 'off'); + legend(ax, 'show', 'Location', 'southeast'); + title(ax, 'Contextual Psychometric Fits'); + xlabel(ax, 'Stimulus'); + end + + function plotContextHitRates(ax, hit_rate_data, hit_rate_std, colors) + cla(ax, 'reset'); hold(ax, 'on'); + + if isempty(hit_rate_data), return; end + + num_contexts = size(hit_rate_data, 1); + + b = bar(ax, hit_rate_data', 'grouped'); + + for i = 1:num_contexts + b(i).FaceColor = colors(i,:); + x_coords = b(i).XData + b(i).XOffset; + errorbar(ax, x_coords, hit_rate_data(i,:), hit_rate_std(i,:), 'k', 'linestyle', 'none', 'CapSize', 4); + end + + ax.XTickLabel = {'Overall', 'Left', 'Right'}; + ylabel(ax, 'Hit %'); + title(ax, 'Contextual Hit Rates (w/ Block STD)'); + ylim(ax, [0 105]); + grid(ax, 'on'); + + legend_labels = arrayfun(@(x) sprintf('Context %d', x), 1:num_contexts, 'UniformOutput', false); + legend(ax, legend_labels, 'Location', 'northeastoutside'); + end + + function plotContextStimulusHistogram(ax, stim_hist_data, config, colors) + cla(ax, 'reset'); hold(ax, 'on'); + + left_edges = linspace(min(config.stimuli_range), config.true_mu, 6); + right_edges = linspace(config.true_mu, max(config.stimuli_range), 6); + bin_centers = (bin_edges(1:end-1) + bin_edges(2:end)) / 2; + + for i = 1:numel(stim_hist_data) + if isempty(stim_hist_data{i}), continue; end + + if isfield(stim_hist_data{i}, 'mean_corr') + fill(ax, [bin_centers, fliplr(bin_centers)], [stim_hist_data{i}.mean_corr - stim_hist_data{i}.std_corr, fliplr(stim_hist_data{i}.mean_corr + stim_hist_data{i}.std_corr)], ... + colors(i,:), 'FaceAlpha', 0.15, 'EdgeColor', 'none'); + fill(ax, [bin_centers, fliplr(bin_centers)], [stim_hist_data{i}.mean_incorr - stim_hist_data{i}.std_incorr, fliplr(stim_hist_data{i}.mean_incorr + stim_hist_data{i}.std_incorr)], ... + colors(i,:), 'FaceAlpha', 0.15, 'EdgeColor', 'none'); + end + + plot(ax, bin_centers, stim_hist_data{i}.correct, '-o', 'Color', colors(i,:), 'LineWidth', 2, 'DisplayName', sprintf('Correct C%d', i)); + plot(ax, bin_centers, stim_hist_data{i}.incorrect, ':x', 'Color', colors(i,:), 'LineWidth', 1.5, 'DisplayName', sprintf('Incorrect C%d', i)); + end + + xline(ax, config.true_mu, '--k', 'Boundary', 'LineWidth', 2); + hold(ax, 'off'); + legend(ax, 'show', 'Location', 'northwest'); + title(ax, 'Contextual Stimulus Distributions'); + xlabel(ax, 'Stimulus Value'); + ylabel(ax, 'Trial Count'); + end + + + %% GENERAL UTILITY FUNCTIONS + function [y_pred, fitParams, methodUsed, fitStatus] = realtimepsychometricFit(stim, resp, rangeStim, options) + % realtimepsychometricFit Robust real-time psychometric fitting and plotting. + % + % This function fits a 4-parameter logistic psychometric function. It includes + % internal checks for data quality and fitting stability. + % + % Inputs: + % stim - vector of stimulus values. + % response - binary response vector (0 or 1). + % rangeStim - 1x2 or 1x3 vector for the stimulus grid [min, max]. + % options - (Optional) struct with fields: + % .MinTrials - Min trials to attempt fit (default: 15). + + % .LapseUB - Upper bound for lapse rates (default: 0.1). + % .StdTol - Tolerance for stimulus std dev (default: 1e-6). + % .SlopeTol - Tolerance for slope parameter (default: 1e-5). + % + % Outputs: + % y_pred - Predicted y-values on a grid across rangeStim. + % fitParams - [mu, sigma, lapseL, lapseR] fitted parameters. + % methodUsed - String indicating the final fitting method used. + % fitStatus - String providing information on the fit quality/outcome. + + %% 1. Argument Handling & Pre-computation Guard Clauses + if nargin < 4, options = struct(); end + if ~isfield(options, 'MinTrials'), options.MinTrials = 15; end + if ~isfield(options, 'LapseUB'), options.LapseUB = 0.1; end + if ~isfield(options, 'StdTol'), options.StdTol = 1e-6; end + if ~isfield(options, 'SlopeTol'), options.SlopeTol = 1e-5; end + fitParams = [nan,nan,nan,nan]; y_pred = []; fitStatus = 'Success'; % Assume success initially + + % Ensure both inputs are vectors and have the same number of elements + + if ~isvector(stim) || ~isvector(resp) + methodUsed = 'Fit Canceled'; + fitStatus = 'Inputs `stim` and `resp` must be vectors.'; + return; + end + if numel(stim) ~= numel(resp) + methodUsed = 'Fit Canceled'; + fitStatus = 'Inputs `stim` and `resp` must have the same number of elements.'; + return; + end + + % Enforce column vector orientation for consistency with fitting functions. + % The (:) operator robustly reshapes any vector into a column vector. + stim = stim(:); + resp = resp(:); + + % GUARD: Check for minimum number of trials + if numel(stim) < options.MinTrials, methodUsed = 'Fit Canceled'; fitStatus = sprintf('Insufficient trials (n=%d, min=%d)', numel(stim), options.MinTrials); return; end + % GUARD: Check for stimulus variance + if std(stim) < options.StdTol, methodUsed = 'Fit Canceled'; fitStatus = 'Insufficient stimulus variance'; return; end + + %% 2. Initial Fit (Ridge) + stim_std = (stim - mean(stim)) / std(stim); methodUsed = 'ridge'; + try + % --- Ridge logistic fit for mu and sigma --- + [B, FitInfo] = lassoglm(stim_std, resp, 'binomial', 'Alpha', 1e-6, 'Lambda', 0.1); + b0 = FitInfo.Intercept; b1 = B(1); + % GUARD: Check for near-zero or excessively large slope from ridge fit + if abs(b1) < options.SlopeTol, throw(MException('MyFit:ZeroSlope', 'Initial ridge fit found no slope.')); end + if abs(b1) > 10 % Check for quasi-perfect separation + throw(MException('MyFit:SteepSlope', 'Initial ridge fit is too steep.')); + end + mu = -b0 / b1 * std(stim) + mean(stim); sigma = std(stim) / b1; + % Residual lapse estimate for initialization + predTrain = 1 ./ (1 + exp(-(b0 + b1 * stim_std))); + lapseEstimate = mean(abs(predTrain - resp)); + lapseL = min(max(lapseEstimate * 1.2, 0), options.LapseUB); lapseR = lapseL; + catch ME + % --- Robust fallback if ridge fit fails for any reason --- + methodUsed = 'robust'; fitStatus = sprintf('Switched to robust fit. Reason: %s', ME.message); + try + brob = robustfit(stim, resp, 'logit'); + % GUARD: Check for near-zero slope from robust fit + if abs(brob(2)) < options.SlopeTol, methodUsed = 'Fit Failed'; fitStatus = 'Could not find a slope.'; return; end + mu = -brob(1) / brob(2); sigma = 1 / brob(2); + lapseL = 0.02; lapseR = 0.02; % Use fixed lapse guesses for robust fallback + catch, methodUsed = 'Fit Failed'; fitStatus = 'Robustfit also failed.'; return; + end + end + %% 3. Final Nonlinear Fit (lsqcurvefit) + psychometricFun = @(params, x) params(3) + (1 - params(3) - params(4)) ./ (1 + exp(-(x - params(1)) / params(2))); + % Use a slightly wider range for bounds to avoid railing issues + stim_min = min(rangeStim); stim_max = max(rangeStim); range_width = stim_max - stim_min; + init = [mu, sigma, lapseL, lapseR]; + lb = [stim_min - 0.1*range_width, 0.1, 0, 0]; + ub = [stim_max + 0.1*range_width, 15, options.LapseUB, options.LapseUB]; + % Constrain initial guess to be within bounds + init(1) = max(min(init(1), ub(1)), lb(1)); init(2) = max(min(init(2), ub(2)), lb(2)); + optimOpts = optimset('Display', 'off'); + fitParams = lsqcurvefit(psychometricFun, init, stim, resp, lb, ub, optimOpts); + + %% 4. Post-Fit Sanity Checks & Prediction + % CHECK: Did the fit "rail" against the stimulus range bounds? + bound_tolerance = 0.01 * range_width; + % CHECK: Did the lapse rates hit their upper bound? + if (fitParams(1) <= lb(1) + bound_tolerance) || (fitParams(1) >= ub(1) - bound_tolerance), fitStatus = 'Warning: Threshold at edge of range.'; end + if (fitParams(3) >= options.LapseUB*0.99) || (fitParams(4) >= options.LapseUB*0.99), fitStatus = 'Warning: Lapse rate at upper bound.'; end + xGrid = linspace(stim_min, stim_max, 300)'; + y_pred = psychometricFun(fitParams, xGrid); + end + + function cmap = createTemporalColormap(n_colors) + if n_colors == 0, cmap = []; return; end + if n_colors == 1, cmap = [0.8 0 0]; return; end % A single dark red + % Create a high-contrast colormap for a few items + if n_colors <= 5 + cmap = [0.8 0.1 0.1; % Red + 0.1 0.5 0.8; % Blue + 0.1 0.7 0.2; % Green + 0.7 0.2 0.7; % Purple + 0.9 0.6 0.0]; % Orange + cmap = cmap(1:n_colors, :); + else % Fallback for more colors + h = linspace(0.6, 0, n_colors)'; + s = linspace(0.8, 1, n_colors)'; + v = linspace(0.7, 1, n_colors)'; + cmap = hsv2rgb([h, s, v]); + end + end + +end diff --git a/Protocols/@ArpitSoundCatContinuous/private/RealTimeAnalysis.m b/Protocols/@ArpitSoundCatContinuous/private/RealTimeAnalysis.m new file mode 100644 index 00000000..3087c8b4 --- /dev/null +++ b/Protocols/@ArpitSoundCatContinuous/private/RealTimeAnalysis.m @@ -0,0 +1,903 @@ +function state = RealTimeAnalysis(action, state, data, handles, config, varargin) +% RealTimeAnalysis function to process and plot trial data for BControl. +% This function is called repeatedly from a parent script +% (e.g., Psychometric.m) and is made stateless by passing all required +% information in and out on every call. It uses a try/catch block to +% prevent crashes during live experiments. +% +% Args: +% action (char): The operation to perform. Can be: +% 'live': Standard update after a block of trials. +% 'redraw': Redraws live plots, typically when a figure becomes visible. +% 'context_switch': Special handling for leftover trials when a context changes. +% 'custom': Plots a single, user-defined range of trials and adds a row to the table. +% 'context': Plots multiple, user-defined ranges for comparison and adds rows to the table. +% 'evaluate': Computes key performance metrics for given trial ranges without plotting. +% +% state (struct): Contains variables that are modified and passed back. +% .blockStatsHistory (struct array): Stores analysis results for each block. +% .block_count (double): Counter for the number of blocks analyzed. +% .last_analyzed_valid_trial (double): Counter for the last valid trial included in an analysis. +% +% data (struct): Contains the complete, read-only data histories for the session. +% .hit_history (vector): History of hits (1), misses (0), or NaN. +% .previous_sides (vector): History of sides presented (e.g., 0 for left, 1 for right). +% .stim_history (vector): History of stimulus values. +% .full_rule_history (string): psychometric rule. +% .full_dist_right, .full_dist_left: Histories of stimulus distributions. +% +% handles (struct): Contains handles to all necessary GUI components. +% .main_fig (handle): Handle to the main analysis figure. +% .axes_h (struct): A struct containing handles to the 6 plot axes. +% .ui_table (handle): Handle to the results table. +% +% config (struct): Contains session-wide constants. +% .trials_per_block (double): Number of valid trials needed for a live update. +% .true_mu (double): The true boundary of the stimulus categories. +% .stimuli_range (1x2 vector): The [min, max] of possible stimulus values. +% .debug (logical): If true, errors will be rethrown; if false, they will be caught and displayed as warnings. +% +% varargin: Action-specific arguments based on the 'action' string. +% For 'live', 'redraw', 'context_switch': +% varargin{1} (struct): A flags struct with logical values. +% .psych (logical): Toggles the psychometric plot. +% .hit (logical): Toggles the hit rate plot. +% .stim (logical): Toggles the stimulus histogram. +% For 'custom': +% varargin{1} (struct): The flags struct (as above). +% varargin{2} (double): The start trial for the custom range. +% varargin{3} (double): The end trial for the custom range. +% For 'context': +% varargin{1} (struct): The flags struct (as above). +% varargin{2} (cell): A cell array of trial ranges, e.g., {[s1,e1], [s2,e2]}. +% For 'evaluate': +% varargin{1} (cell): A cell array of trial ranges, e.g., {[s1,e1], [s2,e2]}. +% +% Returns: +% state (struct): For most actions, this is the updated state struct. +% For 'evaluate', this is a struct array with performance metrics: +% .start_trial +% .end_trial +% .distribution_type +% .calculated_boundary +% .total_hit_percent +% .total_violations_percent +% .right_correct_percent +% .left_correct_percent + + try + switch lower(action) + % ================================================================= + % LIVE UPDATE ACTION + % ================================================================= + case 'live' + if numel(varargin) < 1, error('Live action requires a flags struct.'); end + flags = varargin{1}; + + lastAnalyzed = state.last_analyzed_valid_trial; + validTrials = sum(~isnan(data.hit_history)); + + if (validTrials - lastAnalyzed) >= config.trials_per_block + valid_indices = find(~isnan(data.hit_history)); + buffer_indices = valid_indices(end - config.trials_per_block + 1 : end); + + dataBuffer.stim = data.stim_history(buffer_indices); + dataBuffer.hit = data.hit_history(buffer_indices); + dataBuffer.side = data.previous_sides(buffer_indices); + dataBuffer.indices = buffer_indices; + + state = analyzeLiveChunk(state, data, handles, config, dataBuffer, flags); + end + + % ================================================================= + % CONTEXT SWITCH ACTION + % ================================================================= + case 'context_switch' + if numel(varargin) < 1, error('context_switch action requires a flags struct.'); end + flags = varargin{1}; + + lastAnalyzed = state.last_analyzed_valid_trial; + validTrials = sum(~isnan(data.hit_history)); + remaining_valid_trials = validTrials - lastAnalyzed; + + if remaining_valid_trials > 0 + valid_indices = find(~isnan(data.hit_history)); + new_valid_indices = valid_indices(valid_indices > find(valid_indices == lastAnalyzed, 1, 'last')); + + if remaining_valid_trials < 20 && state.block_count > 0 + last_block_indices = state.blockStatsHistory(end).indices; + combined_indices = [last_block_indices, new_valid_indices']; + + dataBuffer.stim = data.stim_history(combined_indices); + dataBuffer.hit = data.hit_history(combined_indices); + dataBuffer.side = data.previous_sides(combined_indices); + dataBuffer.indices = combined_indices; + + state = reAnalyzeLastChunk(state, data, handles, config, dataBuffer, flags); + + elseif remaining_valid_trials >= 20 + dataBuffer.stim = data.stim_history(new_valid_indices); + dataBuffer.hit = data.hit_history(new_valid_indices); + dataBuffer.side = data.previous_sides(new_valid_indices); + dataBuffer.indices = new_valid_indices; + + state = analyzeLiveChunk(state, data, handles, config, dataBuffer, flags); + end + end + + % ================================================================= + % REDRAW ACTION + % ================================================================= + case 'redraw' + if numel(varargin) < 1, error('Redraw action requires a flags struct.'); end + flags = varargin{1}; + + updateLivePlots(state, data, handles, config, flags); + + % ================================================================= + % CUSTOM PLOT ACTION + % ================================================================= + case 'custom' + if numel(varargin) < 3, error('Custom action requires flags, start_trial, and end_trial.'); end + flags = varargin{1}; + start_idx = varargin{2}; + end_idx = varargin{3}; + + if start_idx >= end_idx || start_idx < 1 || end_idx > numel(data.hit_history), warning('Invalid trial range for custom plot.'); return; end + + indices = start_idx:end_idx; + hit_chunk = data.hit_history(indices); + valid_mask = ~isnan(hit_chunk); + if sum(valid_mask) < 10, warning('Not enough valid trials in custom range to plot.'); return; end + + stim_fit = data.stim_history(indices(valid_mask)); + side_fit = data.previous_sides(indices(valid_mask)); + hit_fit = hit_chunk(valid_mask); + + current_rule = data.full_rule_history; + + % Add results to table, but don't select for live plotting + [~, newRow] = processBlock(data, config, struct('stim', stim_fit, 'hit', hit_fit, 'side', side_fit, 'indices', indices)); + newRow{1} = false; % Ensure it's not selected + updateTable(handles, newRow); + + if flags.psych, plotCustomPsychometric(handles.axes_h.custom_psych, stim_fit, side_fit, hit_fit, config, current_rule); end + if flags.hit, plotCustomHitRates(handles.axes_h.custom_hitrate, hit_fit, side_fit, [start_idx, end_idx], state.blockStatsHistory); end + if flags.stim, plotCustomStimulusHistogram(handles.axes_h.custom_stim, stim_fit, hit_fit, [start_idx, end_idx], state.blockStatsHistory, config); end + + % ================================================================= + % CONTEXT PLOT ACTION + % ================================================================= + case 'context' + if numel(varargin) < 2, error('Context action requires flags and a cell array of contexts.'); end + flags = varargin{1}; + contexts = varargin{2}; + + if ~iscell(contexts) || isempty(contexts), error('Contexts must be a non-empty cell array of [start, end] pairs.'); end + + num_contexts = numel(contexts); + context_colors = createTemporalColormap(num_contexts); + + psych_data = cell(1, num_contexts); + hit_rate_data = zeros(num_contexts, 3); + hit_rate_std = zeros(num_contexts, 3); + stim_hist_data = cell(1, num_contexts); + + for i = 1:num_contexts + start_idx = contexts{i}(1); + end_idx = contexts{i}(2); + + if start_idx >= end_idx || start_idx < 1 || end_idx > numel(data.hit_history), continue; end + + indices = start_idx:end_idx; + hit_chunk = data.hit_history(indices); + valid_mask = ~isnan(hit_chunk); + if sum(valid_mask) < 10, continue; end + + stim_fit = data.stim_history(indices(valid_mask)); + side_fit = data.previous_sides(indices(valid_mask)); + hit_fit = hit_chunk(valid_mask); + + current_rule = data.full_rule_history; + + % Add results to table, but don't select for live plotting + [~, newRow] = processBlock(data, config, struct('stim', stim_fit, 'hit', hit_fit, 'side', side_fit, 'indices', indices)); + newRow{1} = false; % Ensure it's not selected + updateTable(handles, newRow); + + physical_response = zeros(size(hit_fit)); + physical_response(hit_fit == 1) = side_fit(hit_fit == 1); + physical_response(hit_fit == 0) = 1 - side_fit(hit_fit == 0); + + response_for_fitting = physical_response; + if contains(current_rule, 'Left', 'IgnoreCase', true), response_for_fitting = 1 - response_for_fitting; end + + options.MinTrials = 10; + [y_pred, fitParams, ~, ~] = realtimepsychometricFit(stim_fit, response_for_fitting, config.stimuli_range, options); + psych_data{i} = struct('y_pred', y_pred, 'fitParams', fitParams, 'stim_fit', stim_fit, 'physical_response', physical_response, 'rule', current_rule); + + hit_rate_data(i, 1) = 100 * sum(hit_fit == 1) / numel(hit_fit); + hit_rate_data(i, 2) = 100 * sum(hit_fit(side_fit==0)==1) / sum(side_fit==0); + hit_rate_data(i, 3) = 100 * sum(hit_fit(side_fit==1)==1) / sum(side_fit==1); + + left_edges = linspace(min(config.stimuli_range), config.true_mu, 6); + right_edges = linspace(config.true_mu, max(config.stimuli_range), 6); + bin_edges = unique([left_edges, right_edges]); + stim_hist_data{i}.correct = histcounts(stim_fit(hit_fit == 1), bin_edges); + stim_hist_data{i}.incorrect = histcounts(stim_fit(hit_fit == 0), bin_edges); + + relevant_blocks = []; + for j = 1:numel(state.blockStatsHistory) + block_indices = state.blockStatsHistory(j).indices; + if min(block_indices) >= start_idx && max(block_indices) <= end_idx + relevant_blocks = [relevant_blocks, state.blockStatsHistory(j)]; + end + end + + if ~isempty(relevant_blocks) + hit_rate_std(i, 1) = std([relevant_blocks.hitRates.overall], 'omitnan'); + hit_rate_std(i, 2) = std([relevant_blocks.hitRates.left], 'omitnan'); + hit_rate_std(i, 3) = std([relevant_blocks.hitRates.right], 'omitnan'); + counts_matrix_corr = vertcat(relevant_blocks.stimCounts.correct); + counts_matrix_incorr = vertcat(relevant_blocks.stimCounts.incorrect); + stim_hist_data{i}.mean_corr = mean(counts_matrix_corr, 1); + stim_hist_data{i}.std_corr = std(counts_matrix_corr, 0, 1); + stim_hist_data{i}.mean_incorr = mean(counts_matrix_incorr, 1); + stim_hist_data{i}.std_incorr = std(counts_matrix_incorr, 0, 1); + else + hit_rate_std(i, :) = 0; + end + end + + if flags.psych, plotContextPsychometric(handles.axes_h.custom_psych, psych_data, config, context_colors); end + if flags.hit, plotContextHitRates(handles.axes_h.custom_hitrate, hit_rate_data, hit_rate_std, context_colors); end + if flags.stim, plotContextStimulusHistogram(handles.axes_h.custom_stim, stim_hist_data, config, context_colors); end + + % ================================================================= + % EVALUATE ACTION + % ================================================================= + case 'evaluate' + if numel(varargin) < 1, error('Evaluate action requires a cell array of contexts.'); end + contexts = varargin{1}; + if ~iscell(contexts) || isempty(contexts), error('Contexts must be a non-empty cell array of [start, end] pairs.'); end + + num_contexts = numel(contexts); + results = struct('start_trial', [], 'end_trial', [], 'distribution_type', [], 'calculated_boundary', [], 'total_hit_percent', [], 'total_violations_percent', [], 'right_correct_percent', [], 'left_correct_percent', []); + results = repmat(results, 1, num_contexts); + + for i = 1:num_contexts + start_idx = contexts{i}(1); + end_idx = contexts{i}(2); + + if start_idx >= end_idx || start_idx < 1 || end_idx > numel(data.hit_history), continue; end + + indices = start_idx:end_idx; + hit_chunk = data.hit_history(indices); + side_chunk = data.previous_sides(indices); + stim_chunk = data.stim_history(indices); + + valid_mask = ~isnan(hit_chunk); + if sum(valid_mask) < 10, continue; end + + stim_fit = stim_chunk(valid_mask); + side_fit = side_chunk(valid_mask); + hit_fit = hit_chunk(valid_mask); + + current_rule = data.full_rule_history; + + physical_response = zeros(size(hit_fit)); + physical_response(hit_fit == 1) = side_fit(hit_fit == 1); + physical_response(hit_fit == 0) = 1 - side_fit(hit_fit == 0); + + response_for_fitting = physical_response; + if contains(current_rule, 'Left', 'IgnoreCase', true), response_for_fitting = 1 - response_for_fitting; end + + options.MinTrials = 10; + [~, fitParams, ~, ~] = realtimepsychometricFit(stim_fit, response_for_fitting, config.stimuli_range, options); + + results(i).start_trial = start_idx; + results(i).end_trial = end_idx; + results(i).distribution_type = getDistributionType(data, indices, current_rule); + results(i).calculated_boundary = fitParams(1); + results(i).total_hit_percent = 100 * mean(hit_fit); + results(i).total_violations_percent = 100 * mean(isnan(data.hit_history(indices))); + + right_trials = (side_fit == 1); + left_trials = (side_fit == 0); + results(i).right_correct_percent = 100 * mean(hit_fit(right_trials)); + results(i).left_correct_percent = 100 * mean(hit_fit(left_trials)); + end + state = results; % Override the return value for this action + return; % Exit early + + otherwise + error('Unknown action: "%s". Use "live", "custom", or "context".', action); + end + catch ME + % Create a more detailed error message including the line number. + if ~isempty(ME.stack) + errorLocation = sprintf('File: %s, Function: %s, Line: %d', ... + ME.stack(1).file, ME.stack(1).name, ME.stack(1).line); + else + errorLocation = 'Location not available in error stack.'; + end + + fullErrorMessage = sprintf('An error occurred in RealTimeAnalysisApp:\n Error: %s\n %s', ... + ME.message, errorLocation); + + warning('RealTimeAnalysisApp:Error', fullErrorMessage); + + if config.debug, rethrow(ME); end % In experiment mode, the function will gracefully return the original state. + end + + % ================================================================= + % NESTED HELPER FUNCTIONS + % ================================================================= + + %% LIVE ANALYSIS HELPERS + function state = analyzeLiveChunk(state, data, handles, config, dataBuffer, flags) + state.block_count = state.block_count + 1; + [newBlockStat, newRow] = processBlock(data, config, dataBuffer); + state.blockStatsHistory = [state.blockStatsHistory, newBlockStat]; + updateTable(handles, newRow); + + if strcmp(get(handles.main_fig, 'Visible'), 'on') + updateLivePlots(state, data, handles, config, flags); + end + + state.last_analyzed_valid_trial = sum(~isnan(data.hit_history)); + end + + function state = reAnalyzeLastChunk(state, data, handles, config, dataBuffer, flags) + [newBlockStat, newRow] = processBlock(data, config, dataBuffer); + state.blockStatsHistory(end) = newBlockStat; + replaceLastTableRow(handles, newRow); + + if strcmp(get(handles.main_fig, 'Visible'), 'on') + updateLivePlots(state, data, handles, config, flags); + end + + state.last_analyzed_valid_trial = sum(~isnan(data.hit_history)); + end + + function [blockStat, tableRow] = processBlock(data, config, dataBuffer) + physical_response = zeros(size(dataBuffer.hit)); + physical_response(dataBuffer.hit == 1) = dataBuffer.side(dataBuffer.hit == 1); + physical_response(dataBuffer.hit == 0) = 1 - dataBuffer.side(dataBuffer.hit == 0); + + current_rule = data.full_rule_history; + + response_for_fitting = physical_response; + if contains(current_rule, 'Left', 'IgnoreCase', true) + response_for_fitting = 1 - physical_response; + end + + options.MinTrials = 20; + [~, fitParams, methodUsed, fitStatus] = realtimepsychometricFit(dataBuffer.stim, response_for_fitting, config.stimuli_range, options); + + hr.overall = 100 * sum(dataBuffer.hit == 1) / numel(dataBuffer.hit); + left_mask = (dataBuffer.side == 0); + if any(left_mask), hr.left = 100 * sum(dataBuffer.hit(left_mask)==1) / sum(left_mask); else, hr.left = NaN; end + right_mask = (dataBuffer.side == 1); + if any(right_mask), hr.right = 100 * sum(dataBuffer.hit(right_mask)==1) / sum(right_mask); else, hr.right = NaN; end + + blockStat.indices = dataBuffer.indices; + blockStat.hitRates = hr; + + left_edges = linspace(min(config.stimuli_range), config.true_mu, 6); + right_edges = linspace(config.true_mu, max(config.stimuli_range), 6); + bin_edges = unique([left_edges, right_edges]); + stim_correct = dataBuffer.stim(dataBuffer.hit == 1); + stim_incorrect = dataBuffer.stim(dataBuffer.hit == 0); + blockStat.stimCounts.correct = histcounts(stim_correct, bin_edges); + blockStat.stimCounts.incorrect = histcounts(stim_incorrect, bin_edges); + + start_trial = min(dataBuffer.indices); end_trial = max(dataBuffer.indices); + dist_right = strjoin(string(unique(data.full_dist_right(dataBuffer.indices))), ', '); + dist_left = strjoin(string(unique(data.full_dist_left(dataBuffer.indices))), ', '); + + select_status = ismember(methodUsed, {'ridge', 'robust'}); + if select_status + tableRow = {select_status, current_rule, dist_left, dist_right, start_trial, end_trial, fitParams(2), config.true_mu, fitParams(1), fitParams(3), fitParams(4), string(methodUsed) + " (" + fitStatus + ")", hr.overall, hr.left, hr.right}; + else + tableRow = {select_status, current_rule, dist_left, dist_right, start_trial, end_trial, NaN, config.true_mu, NaN, NaN, NaN, string(methodUsed) + " (" + fitStatus + ")", hr.overall, hr.left, hr.right}; + end + end + + function updateTable(handles, newRow) + currentData = get(handles.ui_table, 'Data'); + if newRow{1} == true && ~isempty(currentData) + currentData.Select(:) = false; + end + set(handles.ui_table, 'Data', [currentData; newRow]); + end + + function replaceLastTableRow(handles, newRow) + currentData = get(handles.ui_table, 'Data'); + if ~isempty(currentData) + currentData.Select(:) = false; + newRow{1} = true; % Select the new row + currentData(end,:) = newRow; + set(handles.ui_table, 'Data', currentData); + end + end + + function updateLivePlots(state, data, handles, config, flags) + if flags.psych, updatePsychometricPlot(handles.axes_h.live_psych, handles.ui_table, config); end + if flags.hit, updateHitRatePlot(handles.axes_h.live_hitrate, state.blockStatsHistory); end + if flags.stim, updateStimulusHistogram(handles.axes_h.live_stim, state, data, config); end + end + + function updatePsychometricPlot(ax, ui_table_handle, config) + allData = get(ui_table_handle, 'Data'); + selectedData = allData(allData.Select, :); + cla(ax, 'reset'); hold(ax, 'on'); + + xline(ax, config.true_mu, '--k', 'LineWidth', 1.5, 'HandleVisibility', 'off'); + yline(ax, 0.5, ':', 'Color', [0.5 0.5 0.5], 'HandleVisibility', 'off'); + + if ~isempty(selectedData) + num_to_plot = height(selectedData); + colors = createTemporalColormap(num_to_plot); + + psychometricFun = @(params, x) params(3) + (1 - params(3) - params(4)) ./ (1 + exp(-(x - params(1)) / params(2))); + xGrid = linspace(config.stimuli_range(1), config.stimuli_range(2), 300)'; + + for i = 1:num_to_plot + row = selectedData(i, :); + + fitParams = [row.CalBoundary, row.Slope, row.LapseA, row.LapseB]; + if any(isnan(fitParams)), continue; end + + y_curve = psychometricFun(fitParams, xGrid); + + alpha = 0.4; width = 1.5; + if i == num_to_plot, alpha = 0.9; width = 2.5; end + + plot(ax, xGrid, y_curve, 'Color', [colors(i,:), alpha], 'LineWidth', width, 'DisplayName', sprintf('Block %d', i)); + xline(ax, row.CalBoundary, '-', 'Color', [colors(i,:), alpha], 'LineWidth', width-0.5, 'HandleVisibility', 'off'); + end + legend(ax, 'show', 'Location', 'southeast'); + end + grid(ax, 'on'); hold(ax, 'off'); + ylabel(ax, 'P(Choice)'); title(ax, 'Live Psychometric Curves'); + end + + function updateHitRatePlot(ax, blockStats) + if isempty(blockStats), return; end + + hr_overall = [blockStats.hitRates.overall]; + hr_left = [blockStats.hitRates.left]; + hr_right = [blockStats.hitRates.right]; + + cla(ax, 'reset'); hold(ax, 'on'); + x_axis = 1:numel(hr_overall); + plot(ax, x_axis, hr_overall, '-ok', 'LineWidth', 2, 'DisplayName', 'Overall'); + plot(ax, x_axis, hr_left, '--ob', 'LineWidth', 1.5, 'DisplayName', 'Left'); + plot(ax, x_axis, hr_right, '--or', 'LineWidth', 1.5, 'DisplayName', 'Right'); + hold(ax, 'off'); legend(ax, 'show', 'Location', 'southeast'); + xlim(ax, [0.5, max(10, numel(hr_overall) + 0.5)]); ylim(ax, [0 100]); + xlabel(ax, 'Block Number'); ylabel(ax, 'Hit %'); + title(ax, 'Live Hit Rate per Block'); + end + + function updateStimulusHistogram(ax, state, data, config) + if state.block_count == 0, return; end + + cla(ax, 'reset'); + + n_blocks = state.block_count; + red_map = interp1([0 1], [1 0.7 0.7; 0.9 0.2 0.1], linspace(0, 1, n_blocks)); + green_map = interp1([0 1], [0.7 1 0.7; 0 0.65 0], linspace(0, 1, n_blocks)); + + left_edges = linspace(min(config.stimuli_range), config.true_mu, 6); + right_edges = linspace(config.true_mu, max(config.stimuli_range), 6); + bin_edges = unique([left_edges, right_edges]); + bin_centers = (bin_edges(1:end-1) + bin_edges(2:end)) / 2; + + yyaxis(ax, 'left'); + hold(ax, 'on'); + yyaxis(ax, 'right'); + hold(ax, 'on'); + + max_count = 0; + + for i = 1:n_blocks + block_stat = state.blockStatsHistory(i); + block_indices = block_stat.indices; + + multiplier = 1; + if i == n_blocks, multiplier = 2; end + + yyaxis(ax, 'left'); + plot(ax, bin_centers, block_stat.stimCounts.incorrect, '-', 'Color', red_map(i,:), 'LineWidth', multiplier * 1.5); + plot(ax, bin_centers, block_stat.stimCounts.correct, '-', 'Color', green_map(i,:), 'LineWidth', multiplier * 1.5); + max_count = max([max_count, block_stat.stimCounts.correct, block_stat.stimCounts.incorrect]); + + yyaxis(ax, 'right'); + valid_mask = ~isnan(data.hit_history(block_indices)); + stim_valid = data.stim_history(block_indices(valid_mask)); + hit_valid = data.hit_history(block_indices(valid_mask)); + + stim_correct = stim_valid(hit_valid == 1); + stim_incorrect = stim_valid(hit_valid == 0); + + jitter_base = (i - 1) * 0.2; + jitter_incorrect = jitter_base + 0.08 * rand(size(stim_incorrect)); + jitter_correct = jitter_base + 0.08 * rand(size(stim_correct)); + + scatter(ax, stim_incorrect, jitter_incorrect, multiplier * 25, red_map(i,:), 'filled', 'MarkerFaceAlpha', multiplier * 0.4); + scatter(ax, stim_correct, jitter_correct, multiplier * 25, green_map(i,:), 'filled', 'MarkerFaceAlpha', multiplier * 0.4); + end + + yyaxis(ax, 'left'); + ylabel(ax, 'Trial Count (Binned)'); + ax.YColor = 'k'; + ylim(ax, [0, max(1, max_count * 1.1)]); + + yyaxis(ax, 'right'); + ylim(ax, [0, n_blocks * 0.2 + 0.1]); + ax.YTick = []; + ax.YColor = 'none'; + + hold(ax, 'off'); + xline(ax, config.true_mu, '--k', 'Boundary', 'LineWidth', 2); + xlabel(ax, 'Stimulus Value'); + title(ax, 'Live Stimulus Distribution'); + yyaxis(ax, 'left'); + end + + %% CUSTOM PLOTTING HELPERS + function plotCustomPsychometric(ax, stim_fit, side_fit, hit_fit, config, rule) + physical_response = zeros(size(hit_fit)); + physical_response(hit_fit == 1) = side_fit(hit_fit == 1); + physical_response(hit_fit == 0) = 1 - side_fit(hit_fit == 0); + + response_for_fitting = physical_response; + y_label = 'P(Right)'; + + if contains(rule, 'Left', 'IgnoreCase', true) + response_for_fitting = 1 - physical_response; + y_label = 'P(Left)'; + end + + options.MinTrials = 10; + [y_pred, fitParams, ~, ~] = realtimepsychometricFit(stim_fit, response_for_fitting, config.stimuli_range, options); + + cla(ax, 'reset'); hold(ax, 'on'); + xGrid = linspace(config.stimuli_range(1), config.stimuli_range(2), 300)'; + plot(ax, xGrid, y_pred, 'r-', 'LineWidth', 2, 'DisplayName', 'Fitted Curve'); + xline(ax, config.true_mu, '--k', 'LineWidth', 1.5, 'DisplayName', 'True Boundary'); + xline(ax, fitParams(1), '--b', 'LineWidth', 1.5, 'DisplayName', 'Calculated'); + yline(ax, 0.5, ':', 'Color', [0.5 0.5 0.5]); + grid(ax, 'on'); hold(ax, 'off'); + legend(ax); title(ax, sprintf('Custom Fit (Mu=%.2f)', fitParams(1))); + xlabel(ax, 'Stimulus'); ylabel(ax, y_label); + end + + function plotCustomHitRates(ax, hit_fit, side_fit, custom_range, blockStats) + cla(ax, 'reset'); hold(ax, 'on'); + + hr_custom.overall = 100 * sum(hit_fit == 1) / numel(hit_fit); + hr_custom.left = 100 * sum(hit_fit(side_fit==0)==1) / sum(side_fit==0); + hr_custom.right = 100 * sum(hit_fit(side_fit==1)==1) / sum(side_fit==1); + + relevant_blocks = []; + for i = 1:numel(blockStats) + block_indices = blockStats(i).indices; + if min(block_indices) >= custom_range(1) && max(block_indices) <= custom_range(2) + relevant_blocks = [relevant_blocks, blockStats(i)]; + end + end + + if ~isempty(relevant_blocks) + std_dev.overall = std([relevant_blocks.hitRates.overall], 'omitnan'); + std_dev.left = std([relevant_blocks.hitRates.left], 'omitnan'); + std_dev.right = std([relevant_blocks.hitRates.right], 'omitnan'); + else + std_dev.overall = 0; std_dev.left = 0; std_dev.right = 0; + end + + cats = categorical({'Overall', 'Left', 'Right'}); + errorbar(ax, cats, [hr_custom.overall, hr_custom.left, hr_custom.right], ... + [std_dev.overall, std_dev.left, std_dev.right], ... + 'o', 'MarkerSize', 8, 'CapSize', 15, 'LineWidth', 1.5); + + ylabel(ax, 'Hit %'); title(ax, 'Hit Rates (w/ Block STD)'); + ylim(ax, [0 105]); grid(ax, 'on'); + end + + function plotCustomStimulusHistogram(ax, stim_fit, hit_fit, custom_range, blockStats, config) + cla(ax, 'reset'); hold(ax, 'on'); + + left_edges = linspace(min(config.stimuli_range), config.true_mu, 6); + right_edges = linspace(config.true_mu, max(config.stimuli_range), 6); + bin_edges = unique([left_edges, right_edges]); + bin_centers = (bin_edges(1:end-1) + bin_edges(2:end)) / 2; + + counts_custom.correct = histcounts(stim_fit(hit_fit == 1), bin_edges); + counts_custom.incorrect = histcounts(stim_fit(hit_fit == 0), bin_edges); + + relevant_blocks = []; + for i = 1:numel(blockStats) + block_indices = blockStats(i).indices; + if min(block_indices) >= custom_range(1) && max(block_indices) <= custom_range(2) + relevant_blocks = [relevant_blocks, blockStats(i)]; + end + end + + if ~isempty(relevant_blocks) + counts_matrix_corr = vertcat(relevant_blocks.stimCounts.correct); + counts_matrix_incorr = vertcat(relevant_blocks.stimCounts.incorrect); + + mean_corr = mean(counts_matrix_corr, 1); + std_corr = std(counts_matrix_corr, 0, 1); + mean_incorr = mean(counts_matrix_incorr, 1); + std_incorr = std(counts_matrix_incorr, 0, 1); + + fill(ax, [bin_centers, fliplr(bin_centers)], [mean_corr - std_corr, fliplr(mean_corr + std_corr)], ... + [0 0.65 0], 'FaceAlpha', 0.2, 'EdgeColor', 'none', 'DisplayName', 'Correct (Block STD)'); + fill(ax, [bin_centers, fliplr(bin_centers)], [mean_incorr - std_incorr, fliplr(mean_incorr + std_incorr)], ... + [0.9 0.2 0.1], 'FaceAlpha', 0.2, 'EdgeColor', 'none', 'DisplayName', 'Incorrect (Block STD)'); + end + + plot(ax, bin_centers, counts_custom.correct, '-o', 'Color', [0 0.65 0], 'LineWidth', 2, 'DisplayName', 'Correct (Custom)'); + plot(ax, bin_centers, counts_custom.incorrect, '-o', 'Color', [0.9 0.2 0.1], 'LineWidth', 2, 'DisplayName', 'Incorrect (Custom)'); + + xline(ax, config.true_mu, '--k', 'Boundary', 'LineWidth', 2); + hold(ax, 'off'); legend(ax, 'Location', 'northwest'); + title(ax, 'Stimulus Distribution (w/ Block STD)'); + xlabel(ax, 'Stimulus Value'); ylabel(ax, 'Trial Count'); + end + + %% CONTEXT PLOTTING HELPERS + function plotContextPsychometric(ax, psych_data, config, colors) + cla(ax, 'reset'); hold(ax, 'on'); + xGrid = linspace(config.stimuli_range(1), config.stimuli_range(2), 300)'; + + xline(ax, config.true_mu, '--k', 'LineWidth', 1.5, 'DisplayName', 'True Boundary'); + yline(ax, 0.5, ':', 'Color', [0.5 0.5 0.5]); + + contains_left_rule = false; + contains_right_rule = false; + + for i = 1:numel(psych_data) + if isempty(psych_data{i}), continue; end + + if contains(psych_data{i}.rule, 'Left', 'IgnoreCase', true) + contains_left_rule = true; + else + contains_right_rule = true; + end + + plot(ax, xGrid, psych_data{i}.y_pred, '-', 'Color', colors(i,:), 'LineWidth', 2, 'DisplayName', sprintf('Context %d', i)); + xline(ax, psych_data{i}.fitParams(1), '--', 'Color', colors(i,:), 'LineWidth', 1.5, 'HandleVisibility', 'off'); + end + + if contains_left_rule && ~contains_right_rule, ylabel(ax, 'P(Left)'); + elseif ~contains_left_rule && contains_right_rule, ylabel(ax, 'P(Right)'); + else, ylabel(ax, 'P(Choice)'); end + + grid(ax, 'on'); hold(ax, 'off'); + legend(ax, 'show', 'Location', 'southeast'); + title(ax, 'Contextual Psychometric Fits'); + xlabel(ax, 'Stimulus'); + end + + function plotContextHitRates(ax, hit_rate_data, hit_rate_std, colors) + cla(ax, 'reset'); hold(ax, 'on'); + + if isempty(hit_rate_data), return; end + + num_contexts = size(hit_rate_data, 1); + + b = bar(ax, hit_rate_data', 'grouped'); + + for i = 1:num_contexts + b(i).FaceColor = colors(i,:); + x_coords = b(i).XData + b(i).XOffset; + errorbar(ax, x_coords, hit_rate_data(i,:), hit_rate_std(i,:), 'k', 'linestyle', 'none', 'CapSize', 4); + end + + ax.XTickLabel = {'Overall', 'Left', 'Right'}; + ylabel(ax, 'Hit %'); + title(ax, 'Contextual Hit Rates (w/ Block STD)'); + ylim(ax, [0 105]); + grid(ax, 'on'); + + legend_labels = arrayfun(@(x) sprintf('Context %d', x), 1:num_contexts, 'UniformOutput', false); + legend(ax, legend_labels, 'Location', 'northeastoutside'); + end + + function plotContextStimulusHistogram(ax, stim_hist_data, config, colors) + cla(ax, 'reset'); hold(ax, 'on'); + + left_edges = linspace(min(config.stimuli_range), config.true_mu, 6); + right_edges = linspace(config.true_mu, max(config.stimuli_range), 6); + bin_centers = (bin_edges(1:end-1) + bin_edges(2:end)) / 2; + + for i = 1:numel(stim_hist_data) + if isempty(stim_hist_data{i}), continue; end + + if isfield(stim_hist_data{i}, 'mean_corr') + fill(ax, [bin_centers, fliplr(bin_centers)], [stim_hist_data{i}.mean_corr - stim_hist_data{i}.std_corr, fliplr(stim_hist_data{i}.mean_corr + stim_hist_data{i}.std_corr)], ... + colors(i,:), 'FaceAlpha', 0.15, 'EdgeColor', 'none'); + fill(ax, [bin_centers, fliplr(bin_centers)], [stim_hist_data{i}.mean_incorr - stim_hist_data{i}.std_incorr, fliplr(stim_hist_data{i}.mean_incorr + stim_hist_data{i}.std_incorr)], ... + colors(i,:), 'FaceAlpha', 0.15, 'EdgeColor', 'none'); + end + + plot(ax, bin_centers, stim_hist_data{i}.correct, '-o', 'Color', colors(i,:), 'LineWidth', 2, 'DisplayName', sprintf('Correct C%d', i)); + plot(ax, bin_centers, stim_hist_data{i}.incorrect, ':x', 'Color', colors(i,:), 'LineWidth', 1.5, 'DisplayName', sprintf('Incorrect C%d', i)); + end + + xline(ax, config.true_mu, '--k', 'Boundary', 'LineWidth', 2); + hold(ax, 'off'); + legend(ax, 'show', 'Location', 'northwest'); + title(ax, 'Contextual Stimulus Distributions'); + xlabel(ax, 'Stimulus Value'); + ylabel(ax, 'Trial Count'); + end + + %% GENERAL UTILITY FUNCTIONS + function dist_type = getDistributionType(data, indices, rule) + % Determines the distribution type based on the rule and the + % distributions for left and right sides within the given indices. + dist_left = unique(data.full_dist_left(indices)); + dist_right = unique(data.full_dist_right(indices)); + + if numel(dist_left) > 1, dist_left = dist_left{1}; end + if numel(dist_right) > 1, dist_right = dist_right{1}; end + + hard_dists = {'exponential', 'half-normal', 'sinusoidal'}; + + if strcmp(dist_left, dist_right) + dist_type = dist_left; + return; + end + + is_left_hard = ismember(dist_left, hard_dists); + is_right_hard = ismember(dist_right, hard_dists); + + if contains(rule, 'Right', 'IgnoreCase', true) % High stimulus values correspond to Right + if is_right_hard && ~is_left_hard + dist_type = 'hard high'; + elseif ~is_right_hard && is_left_hard + dist_type = 'hard low'; + else + dist_type = 'mixed'; + end + else % High stimulus values correspond to Left + if is_left_hard && ~is_right_hard + dist_type = 'hard high'; + elseif ~is_left_hard && is_right_hard + dist_type = 'hard low'; + else + dist_type = 'mixed'; + end + end + end + + function [y_pred, fitParams, methodUsed, fitStatus] = realtimepsychometricFit(stim, resp, rangeStim, options) + % realtimepsychometricFit Robust real-time psychometric fitting and plotting. + % + % This function fits a 4-parameter logistic psychometric function. It includes + % internal checks for data quality and fitting stability. + % + % Inputs: + % stim - vector of stimulus values. + % response - binary response vector (0 or 1). + % rangeStim - 1x2 or 1x3 vector for the stimulus grid [min, max]. + % options - (Optional) struct with fields: + % .MinTrials - Min trials to attempt fit (default: 15). + + % .LapseUB - Upper bound for lapse rates (default: 0.1). + % .StdTol - Tolerance for stimulus std dev (default: 1e-6). + % .SlopeTol - Tolerance for slope parameter (default: 1e-5). + % + % Outputs: + % y_pred - Predicted y-values on a grid across rangeStim. + % fitParams - [mu, sigma, lapseL, lapseR] fitted parameters. + % methodUsed - String indicating the final fitting method used. + % fitStatus - String providing information on the fit quality/outcome. + + %% 1. Argument Handling & Pre-computation Guard Clauses + if nargin < 4, options = struct(); end + if ~isfield(options, 'MinTrials'), options.MinTrials = 15; end + if ~isfield(options, 'LapseUB'), options.LapseUB = 0.1; end + if ~isfield(options, 'StdTol'), options.StdTol = 1e-6; end + if ~isfield(options, 'SlopeTol'), options.SlopeTol = 1e-5; end + fitParams = [nan,nan,nan,nan]; y_pred = []; fitStatus = 'Success'; % Assume success initially + + % Ensure both inputs are vectors and have the same number of elements + + if ~isvector(stim) || ~isvector(resp) + methodUsed = 'Fit Canceled'; + fitStatus = 'Inputs `stim` and `resp` must be vectors.'; + return; + end + if numel(stim) ~= numel(resp) + methodUsed = 'Fit Canceled'; + fitStatus = 'Inputs `stim` and `resp` must have the same number of elements.'; + return; + end + + % Enforce column vector orientation for consistency with fitting functions. + % The (:) operator robustly reshapes any vector into a column vector. + stim = stim(:); + resp = resp(:); + + % GUARD: Check for minimum number of trials + if numel(stim) < options.MinTrials, methodUsed = 'Fit Canceled'; fitStatus = sprintf('Insufficient trials (n=%d, min=%d)', numel(stim), options.MinTrials); return; end + % GUARD: Check for stimulus variance + if std(stim) < options.StdTol, methodUsed = 'Fit Canceled'; fitStatus = 'Insufficient stimulus variance'; return; end + + %% 2. Initial Fit (Ridge) + stim_std = (stim - mean(stim)) / std(stim); methodUsed = 'ridge'; + try + % --- Ridge logistic fit for mu and sigma --- + [B, FitInfo] = lassoglm(stim_std, resp, 'binomial', 'Alpha', 1e-6, 'Lambda', 0.1); + b0 = FitInfo.Intercept; b1 = B(1); + % GUARD: Check for near-zero or excessively large slope from ridge fit + if abs(b1) < options.SlopeTol, throw(MException('MyFit:ZeroSlope', 'Initial ridge fit found no slope.')); end + if abs(b1) > 10 % Check for quasi-perfect separation + throw(MException('MyFit:SteepSlope', 'Initial ridge fit is too steep.')); + end + mu = -b0 / b1 * std(stim) + mean(stim); sigma = std(stim) / b1; + % Residual lapse estimate for initialization + predTrain = 1 ./ (1 + exp(-(b0 + b1 * stim_std))); + lapseEstimate = mean(abs(predTrain - resp)); + lapseL = min(max(lapseEstimate * 1.2, 0), options.LapseUB); lapseR = lapseL; + catch ME + % --- Robust fallback if ridge fit fails for any reason --- + methodUsed = 'robust'; fitStatus = sprintf('Switched to robust fit. Reason: %s', ME.message); + try + brob = robustfit(stim, resp, 'logit'); + % GUARD: Check for near-zero slope from robust fit + if abs(brob(2)) < options.SlopeTol, methodUsed = 'Fit Failed'; fitStatus = 'Could not find a slope.'; return; end + mu = -brob(1) / brob(2); sigma = 1 / brob(2); + lapseL = 0.02; lapseR = 0.02; % Use fixed lapse guesses for robust fallback + catch, methodUsed = 'Fit Failed'; fitStatus = 'Robustfit also failed.'; return; + end + end + %% 3. Final Nonlinear Fit (lsqcurvefit) + psychometricFun = @(params, x) params(3) + (1 - params(3) - params(4)) ./ (1 + exp(-(x - params(1)) / params(2))); + % Use a slightly wider range for bounds to avoid railing issues + stim_min = min(rangeStim); stim_max = max(rangeStim); range_width = stim_max - stim_min; + init = [mu, sigma, lapseL, lapseR]; + lb = [stim_min - 0.1*range_width, 0.1, 0, 0]; + ub = [stim_max + 0.1*range_width, 15, options.LapseUB, options.LapseUB]; + % Constrain initial guess to be within bounds + init(1) = max(min(init(1), ub(1)), lb(1)); init(2) = max(min(init(2), ub(2)), lb(2)); + optimOpts = optimset('Display', 'off'); + fitParams = lsqcurvefit(psychometricFun, init, stim, resp, lb, ub, optimOpts); + + %% 4. Post-Fit Sanity Checks & Prediction + % CHECK: Did the fit "rail" against the stimulus range bounds? + bound_tolerance = 0.01 * range_width; + % CHECK: Did the lapse rates hit their upper bound? + if (fitParams(1) <= lb(1) + bound_tolerance) || (fitParams(1) >= ub(1) - bound_tolerance), fitStatus = 'Warning: Threshold at edge of range.'; end + if (fitParams(3) >= options.LapseUB*0.99) || (fitParams(4) >= options.LapseUB*0.99), fitStatus = 'Warning: Lapse rate at upper bound.'; end + xGrid = linspace(stim_min, stim_max, 300)'; + y_pred = psychometricFun(fitParams, xGrid); + end + + function cmap = createTemporalColormap(n_colors) + if n_colors == 0, cmap = []; return; end + if n_colors == 1, cmap = [0.8 0 0]; return; end % A single dark red + % Create a high-contrast colormap for a few items + if n_colors <= 5 + cmap = [0.8 0.1 0.1; % Red + 0.1 0.5 0.8; % Blue + 0.1 0.7 0.2; % Green + 0.7 0.2 0.7; % Purple + 0.9 0.6 0.0]; % Orange + cmap = cmap(1:n_colors, :); + else % Fallback for more colors + h = linspace(0.6, 0, n_colors)'; + s = linspace(0.8, 1, n_colors)'; + v = linspace(0.7, 1, n_colors)'; + cmap = hsv2rgb([h, s, v]); + end + end + +end From bc9d19b22e1fbadc42e414cfe5893d6aa140fa71 Mon Sep 17 00:00:00 2001 From: Arpit Date: Fri, 8 Aug 2025 17:00:03 +0100 Subject: [PATCH 153/164] New module to do neuropixel recordings with ArpitSoundCatContinuous --- .../NeuropixelNeuroblueprint.asv | 1256 ----------------- .../NeuropixelNeuroblueprint.m | 79 +- .../OpenEphys_Neuroblueprint.m | 1097 -------------- .../Modules/@dispatcher/RunningSection.m | 4 - .../ArpitSoundCatContinuous.m | 20 +- .../PerformanceSection.m | 27 +- .../PsychometricSection.asv | 405 ------ .../PsychometricSection.m | 102 +- .../@ArpitSoundCatContinuous/SideSection.m | 19 +- .../StimulusSection.asv | 813 ----------- .../StimulusSection.m | 2 +- .../private/RealTimeAnalysis.asv | 768 ---------- .../private/RealTimeAnalysis.m | 332 +++-- .../Rigtest_singletrial.m | 4 - 14 files changed, 422 insertions(+), 4506 deletions(-) delete mode 100644 ExperPort/Modules/@NeuropixelNeuroblueprint/NeuropixelNeuroblueprint.asv delete mode 100644 ExperPort/Modules/@OpenEphys_Neuroblueprint/OpenEphys_Neuroblueprint.m delete mode 100644 Protocols/@ArpitSoundCatContinuous/PsychometricSection.asv delete mode 100644 Protocols/@ArpitSoundCatContinuous/StimulusSection.asv delete mode 100644 Protocols/@ArpitSoundCatContinuous/private/RealTimeAnalysis.asv diff --git a/ExperPort/Modules/@NeuropixelNeuroblueprint/NeuropixelNeuroblueprint.asv b/ExperPort/Modules/@NeuropixelNeuroblueprint/NeuropixelNeuroblueprint.asv deleted file mode 100644 index 17fbcd94..00000000 --- a/ExperPort/Modules/@NeuropixelNeuroblueprint/NeuropixelNeuroblueprint.asv +++ /dev/null @@ -1,1256 +0,0 @@ -function [obj, varargout] = NeuropixelNeuroblueprint(varargin) -% NEUROPIXEL_NEUROBLUEPRINT_GUI - Integrated GUI for electrophysiology and behavior experiments -% -% This GUI manages experimental workflows for coordinated electrophysiology recording -% (using either Open Ephys or SpikeGLX) with behavioral protocols using Bpod/ExperPort. -% -% FEATURES: -% - Support for both Open Ephys and SpikeGLX recording systems -% - Neuropixels probe management (NP 1.0 and 2.0) -% - NeuroBlueprint data organization format -% - Automated session management and data saving -% - SVN integration for version control -% - Pre/post-session sampling across probe banks -% -% PRE-REQUISITES: -% 1. 'open-ephys-matlab-tools' must be in MATLAB path (for Open Ephys) -% 2. 'SpikeGLX-MATLAB-SDK' must be in MATLAB path (for SpikeGLX) -% 3. Bpod/ratter/ExperPort environment fully configured -% -% USAGE: -% gui_obj = NeuropixelNeuroblueprintGUI(); -% - -%% Boilerplate for class definition and action handling -obj = class(struct, mfilename); -varargout = {}; - - -% Display usage help when function is called directly -if nargin==0 || (nargin==1 && ischar(varargin{1}) && strcmp(varargin{1}, 'empty')) - display_usage_help(); - return; -end - -if isa(varargin{1}, mfilename) - if length(varargin) < 2 || ~ischar(varargin{2}) - error(['If called with a "%s" object as first arg, a second arg, a ' ... - 'string specifying the action, is required\n']); - else - action = varargin{2}; - varargin = varargin(3:end); - end -else - action = varargin{1}; - varargin = varargin(2:end); -end - -if ~ischar(action) - error('The action parameter must be a string'); -end - -GetSoloFunctionArgs(obj); - -%% Main Action Router -switch action - % ========================================================================= - % CASE INIT - % ========================================================================= - case 'init' - % So that only the CPU-based software renderer instead of your graphics card - % opengl software; - - % Start Bpod if not already running - if evalin('base', 'exist(''BpodSystem'', ''var'')') - if evalin('base', '~isempty(BpodSystem)'), newstartup; else, flush; end - else, Bpod('COM5');newstartup; - end - - % --- State Variables as SoloParamHandles --- - SoloParamHandle(obj, 'currentState', 'value', 'Load'); - SoloParamHandle(obj, 'behavState', 'value', 'Run'); - SoloParamHandle(obj, 'ephysState', 'value', 'Run'); - SoloParamHandle(obj, 'is_running', 'value', 0); - SoloParamHandle(obj, 'recording_software', 'value', 'OpenEphys'); % 'OpenEphys' or 'SpikeGLX' - SoloParamHandle(obj, 'recording_controller', 'value', []); % Will hold OE or SpikeGLX controller - SoloParamHandle(obj, 'behav_obj', 'value', []); - SoloParamHandle(obj, 'blinking_timer', 'value', []); - SoloParamHandle(obj, 'current_params', 'value', []); - SoloParamHandle(obj, 'session_base_path', 'value', ''); - SoloParamHandle(obj, 'probe_settings', 'value', struct('version', '2.0', 'reference', 'Tip', 'bank', 0, 'imro_path', '')); - SoloParamHandle(obj, 'probe_gui_handles', 'value', []); - - % Create stopping timer for behavior - scr = timer; - set(scr,'Period', 0.2,'ExecutionMode','FixedRate','TasksToExecute',Inf,... - 'BusyMode','drop','TimerFcn',[mfilename,'(''End_Continued'')']); - SoloParamHandle(obj, 'stopping_complete_timer', 'value', scr); - - % --- Create the GUI Figure --- - SoloParamHandle(obj, 'myfig', 'saveable', 0); - myfig.value = figure('Name', 'Neuropixels Recording & Behavior Controller',... - 'NumberTitle', 'off', 'MenuBar', 'none', ... - 'ToolBar', 'none', 'Units', 'normalized', ... - 'Position', [0.1, 0.1, 0.7, 0.85],... - 'Color', [0.94, 0.94, 0.94], ... - 'CloseRequestFcn', {@(h,e) feval(mfilename, obj, 'close')}); - - % --- UI Creation --- - handles = struct(); - - % Activity Log Panel - uipanel('Title', 'Activity Log', 'FontSize', 12, 'BorderType', 'etchedin', 'BorderWidth', 1, 'Units', 'normalized', 'Position', [0.64, 0.03, 0.34, 0.94]); - handles.log_box = uicontrol('Style', 'edit', 'Units', 'normalized', 'Position', [0.65, 0.05, 0.32, 0.89], 'String', {'Log started...'}, 'Max', 10, 'Min', 1, 'HorizontalAlignment', 'left', 'Enable', 'inactive', 'BackgroundColor', [1, 1, 1]); - - % Panel 0: Recording Software Selection - p0 = uipanel('Title', '0. Recording Software', 'FontSize', 12, 'FontWeight', 'bold', 'BorderType', 'etchedin', 'BorderWidth', 1, 'Units', 'normalized', 'Position', [0.02, 0.88, 0.6, 0.09]); - handles.software_group = uibuttongroup(p0, 'Title', '', 'BorderType', 'none', 'Units', 'normalized', 'Position', [0.05, 0.1, 0.9, 0.8], 'SelectionChangedFcn', {@(h,e) feval(mfilename, obj, 'recording_software_callback')}); - uicontrol(handles.software_group, 'Style', 'radiobutton', 'String', 'Open Ephys', 'Units', 'normalized', 'Position', [0.1, 0.3, 0.4, 0.4], 'Tag', 'OpenEphys', 'FontSize', 10); - uicontrol(handles.software_group, 'Style', 'radiobutton', 'String', 'SpikeGLX', 'Units', 'normalized', 'Position', [0.5, 0.3, 0.4, 0.4], 'Tag', 'SpikeGLX', 'FontSize', 10); - - % Panel 1: Behavior - p1 = uipanel('Title', '1. Behavior', 'FontSize', 12, 'FontWeight', 'bold', 'BorderType', 'etchedin', 'BorderWidth', 1, 'Units', 'normalized', 'Position', [0.02, 0.74, 0.6, 0.13]); - uicontrol(p1, 'Style', 'text', 'String', 'Protocol Name:', 'Units', 'normalized', 'Position', [0.05, 0.7, 0.22, 0.25], 'HorizontalAlignment', 'right'); - handles.protocol_edit = uicontrol(p1, 'Style', 'edit', 'String', 'ArpitSoundCatContinuous', 'Units', 'normalized', 'Position', [0.3, 0.7, 0.45, 0.25]); - handles.manual_test = uicontrol(p1, 'Style', 'checkbox', 'String', 'Manual Test', 'Value', 1, 'Units', 'normalized', 'Position', [0.78, 0.7, 0.2, 0.25]); - uicontrol(p1, 'Style', 'text', 'String', 'Experimenter:', 'Units', 'normalized', 'Position', [0.05, 0.4, 0.22, 0.25], 'HorizontalAlignment', 'right'); - handles.exp_edit = uicontrol(p1, 'Style', 'edit', 'String', 'lida', 'Units', 'normalized', 'Position', [0.3, 0.4, 0.65, 0.25], 'Callback', {@(h,e) feval(mfilename, obj, 'update_subject_id')}); - uicontrol(p1, 'Style', 'text', 'String', 'Rat Name:', 'Units', 'normalized', 'Position', [0.05, 0.1, 0.22, 0.25], 'HorizontalAlignment', 'right'); - handles.rat_name_edit = uicontrol(p1, 'Style', 'edit', 'String', 'LP12', 'Units', 'normalized', 'Position', [0.3, 0.1, 0.4, 0.25], 'Callback', {@(h,e) feval(mfilename, obj, 'update_subject_id')}); - uicontrol(p1, 'Style', 'text', 'String', 'Path:', 'Units', 'normalized', 'Position', [0.72, 0.1, 0.08, 0.25], 'HorizontalAlignment', 'right'); - handles.behav_edit = uicontrol(p1, 'Style', 'edit', 'String', 'C:\ratter', 'Units', 'normalized', 'Position', [0.81, 0.1, 0.18, 0.25]); - - % Panel 2: NeuroBlueprint Format - p2 = uipanel('Title', '2. NeuroBlueprint Format', 'FontSize', 12, 'FontWeight', 'bold', 'BorderType', 'etchedin', 'BorderWidth', 1, 'Units', 'normalized', 'Position', [0.02, 0.42, 0.6, 0.31]); - uicontrol(p2, 'Style', 'text', 'String', 'Project Name:', 'Units', 'normalized', 'Position', [0.01, 0.85, 0.28, 0.1], 'HorizontalAlignment', 'right'); - handles.proj_edit = uicontrol(p2, 'Style', 'edit', 'String', 'sound_cat_rat', 'Units', 'normalized', 'Position', [0.3, 0.85, 0.65, 0.12]); - uicontrol(p2, 'Style', 'text', 'String', 'Subject ID:', 'Units', 'normalized', 'Position', [0.01, 0.7, 0.28, 0.1], 'HorizontalAlignment', 'right'); - handles.sub_edit = uicontrol(p2, 'Style', 'edit', 'String', '000', 'Units', 'normalized', 'Position', [0.3, 0.7, 0.65, 0.12]); - uicontrol(p2, 'Style', 'text', 'String', 'Local Path:', 'Units', 'normalized', 'Position', [0.01, 0.55, 0.28, 0.1], 'HorizontalAlignment', 'right'); - handles.local_edit = uicontrol(p2, 'Style', 'edit', 'String', 'C:\Ephys_Experiment_Data', 'Units', 'normalized', 'Position', [0.3, 0.55, 0.5, 0.12]); - handles.local_browse = uicontrol(p2, 'Style', 'pushbutton', 'String', 'Browse...', 'Units', 'normalized', 'Position', [0.81, 0.55, 0.16, 0.13], 'Callback', {@(h,e) feval(mfilename, obj, 'browse_path', 'local')}); - uicontrol(p2, 'Style', 'text', 'String', 'Central Path:', 'Units', 'normalized', 'Position', [0.01, 0.4, 0.28, 0.1], 'HorizontalAlignment', 'right'); - handles.central_edit = uicontrol(p2, 'Style', 'edit', 'String', 'Z:\_projects', 'Units', 'normalized', 'Position', [0.3, 0.4, 0.5, 0.12]); - handles.central_browse = uicontrol(p2, 'Style', 'pushbutton', 'String', 'Browse...', 'Units', 'normalized', 'Position', [0.81, 0.4, 0.16, 0.13], 'Callback', {@(h,e) feval(mfilename, obj, 'browse_path', 'central')}); - uicontrol(p2, 'Style', 'text', 'String', 'Subfolders to Create:', 'Units', 'normalized', 'Position', [0.05, 0.2, 0.9, 0.1], 'HorizontalAlignment', 'left'); - handles.cb_ephys = uicontrol(p2, 'Style', 'checkbox', 'String', 'ephys', 'Value', 1, 'Units', 'normalized', 'Position', [0.05, 0.05, 0.2, 0.15]); - handles.cb_behav = uicontrol(p2, 'Style', 'checkbox', 'String', 'behav', 'Value', 1, 'Units', 'normalized', 'Position', [0.28, 0.05, 0.2, 0.15]); - handles.cb_anat = uicontrol(p2, 'Style', 'checkbox', 'String', 'anat', 'Value', 1, 'Units', 'normalized', 'Position', [0.51, 0.05, 0.2, 0.15]); - handles.cb_funcimg = uicontrol(p2, 'Style', 'checkbox', 'String', 'funcimg', 'Value', 0, 'Units', 'normalized', 'Position', [0.74, 0.05, 0.25, 0.15]); - - % Panel 3: Pre/Post-Experiment Sampling - p3 = uipanel('Title', '3. Pre/Post-Experiment Sampling', 'FontSize', 12, 'FontWeight', 'bold', 'BorderType', 'etchedin', 'BorderWidth', 1, 'Units', 'normalized', 'Position', [0.02, 0.28, 0.6, 0.12]); - uicontrol(p3, 'Style', 'text', 'String', 'Duration/Bank (s):', 'Units', 'normalized', 'Position', [0.01, 0.6, 0.25, 0.25], 'HorizontalAlignment', 'right'); - handles.sample_duration = uicontrol(p3, 'Style', 'edit', 'String', '60', 'Units', 'normalized', 'Position', [0.27, 0.6, 0.1, 0.3]); - handles.target_display = uicontrol(p3, 'Style', 'text', 'String', 'Target: Bank 0', 'Units', 'normalized', 'Position', [0.38, 0.6, 0.3, 0.25], 'HorizontalAlignment', 'right', 'FontWeight', 'bold'); - handles.probe_button = uicontrol(p3, 'Style', 'pushbutton', 'String', 'Probe Setting', 'Units', 'normalized', 'Position', [0.7, 0.55, 0.28, 0.4], 'FontSize', 10, 'Callback', {@(h,e) feval(mfilename, obj, 'open_probe_gui')}); - handles.sample_button = uicontrol(p3, 'Style', 'pushbutton', 'String', 'Start Sample Recording', 'Units', 'normalized', 'Position', [0.05, 0.1, 0.9, 0.4], 'FontSize', 12, 'FontWeight', 'bold', 'BackgroundColor', [0.8, 0.7, 1], 'Callback', {@(h,e) feval(mfilename, obj, 'sample_recording_wrapper')}); - - % Panel 4: Recording Settings - p4 = uipanel('Title', '4. Recording Settings', 'FontSize', 12, 'FontWeight', 'bold', 'BorderType', 'etchedin', 'BorderWidth', 1, 'Units', 'normalized', 'Position', [0.02, 0.15, 0.6, 0.11]); - handles.settings_panel = p4; - - % Open Ephys Settings (initially visible) - handles.oe_ip_label = uicontrol(p4, 'Style', 'text', 'String', 'GUI IP:', 'Units', 'normalized', 'Position', [0.01, 0.3, 0.15, 0.4], 'HorizontalAlignment', 'right'); - handles.oe_ip_edit = uicontrol(p4, 'Style', 'edit', 'String', '127.0.0.1', 'Units', 'normalized', 'Position', [0.17, 0.25, 0.15, 0.5]); - handles.oe_proc_label = uicontrol(p4, 'Style', 'text', 'String', 'Proc ID:', 'Units', 'normalized', 'Position', [0.33, 0.3, 0.15, 0.4], 'HorizontalAlignment', 'right'); - handles.oe_proc_edit = uicontrol(p4, 'Style', 'edit', 'String', '100', 'Units', 'normalized', 'Position', [0.49, 0.25, 0.15, 0.5]); - handles.oe_rec_label = uicontrol(p4, 'Style', 'text', 'String', 'Rec ID:', 'Units', 'normalized', 'Position', [0.65, 0.3, 0.15, 0.4], 'HorizontalAlignment', 'right'); - handles.oe_rec_edit = uicontrol(p4, 'Style', 'edit', 'String', '101', 'Units', 'normalized', 'Position', [0.81, 0.25, 0.15, 0.5]); - - % SpikeGLX Settings (initially hidden) - handles.sglx_host_label = uicontrol(p4, 'Style', 'text', 'String', 'Host IP:', 'Units', 'normalized', 'Position', [0.01, 0.3, 0.15, 0.4], 'HorizontalAlignment', 'right', 'Visible', 'off'); - handles.sglx_host_edit = uicontrol(p4, 'Style', 'edit', 'String', 'localhost', 'Units', 'normalized', 'Position', [0.17, 0.25, 0.15, 0.5], 'Visible', 'off'); - handles.sglx_port_label = uicontrol(p4, 'Style', 'text', 'String', 'Port:', 'Units', 'normalized', 'Position', [0.33, 0.3, 0.15, 0.4], 'HorizontalAlignment', 'right', 'Visible', 'off'); - handles.sglx_port_edit = uicontrol(p4, 'Style', 'edit', 'String', '4142', 'Units', 'normalized', 'Position', [0.49, 0.25, 0.15, 0.5], 'Visible', 'off'); - handles.sglx_probe_label = uicontrol(p4, 'Style', 'text', 'String', 'Probe Idx:', 'Units', 'normalized', 'Position', [0.65, 0.3, 0.15, 0.4], 'HorizontalAlignment', 'right', 'Visible', 'off'); - handles.sglx_probe_edit = uicontrol(p4, 'Style', 'edit', 'String', '0', 'Units', 'normalized', 'Position', [0.81, 0.25, 0.15, 0.5], 'Visible', 'off'); - - % --- Control Buttons Panel --- - p5 = uipanel('Title', 'Controls', 'FontSize', 12, 'FontWeight', 'bold', 'BorderType', 'etchedin', 'BorderWidth', 1, 'Units', 'normalized', 'Position', [0.02, 0.02, 0.6, 0.11]); - handles.control_button = uicontrol(p5, 'Style', 'pushbutton', 'String', 'Load', 'Units', 'normalized', 'FontSize', 14, 'FontWeight', 'bold', 'Position', [0.02, 0.1, 0.3, 0.8], 'BackgroundColor', [0.2, 0.6, 0.8], 'Callback', {@(h,e) feval(mfilename, obj, 'main_control_callback')}); - handles.behav_button = uicontrol(p5, 'Style', 'pushbutton', 'String', 'Run Behav', 'Units', 'normalized', 'FontSize', 12, 'Position', [0.35, 0.1, 0.3, 0.8], 'BackgroundColor', [1, 0.8, 0.6], 'Callback', {@(h,e) feval(mfilename, obj, 'behav_control_callback')}); - handles.ephys_button = uicontrol(p5, 'Style', 'pushbutton', 'String', 'Run Ephys', 'Units', 'normalized', 'FontSize', 12, 'Position', [0.68, 0.1, 0.3, 0.8], 'BackgroundColor', [0.8, 0.6, 1], 'Callback', {@(h,e) feval(mfilename, obj, 'ephys_control_callback')}); - - SoloParamHandle(obj, 'ui_handles', 'value', handles); - - feval(mfilename, obj, 'update_subject_id'); - log_message(handles, 'GUI initialization complete.'); - - % ========================================================================= - % CASE MAIN_CONTROL_CALLBACK - % ========================================================================= - case 'main_control_callback' - switch value(currentState) - case 'Load', feval(mfilename, obj, 'load_sequence'); - case 'Run', feval(mfilename, obj, 'run_sequence'); - case 'Stop', feval(mfilename, obj, 'stop_sequence'); - case 'PostExperiment', feval(mfilename, obj, 'reset_to_load_state'); - end - - % ========================================================================= - % CASE RECORDING_SOFTWARE_CALLBACK - % ========================================================================= - case 'recording_software_callback' - handles = value(ui_handles); - selected_software = get(get(handles.software_group, 'SelectedObject'), 'Tag'); - recording_software.value = selected_software; - - if strcmp(selected_software, 'OpenEphys') - % Show OpenEphys settings, hide SpikeGLX - set([handles.oe_ip_label, handles.oe_ip_edit, handles.oe_proc_label, handles.oe_proc_edit, handles.oe_rec_label, handles.oe_rec_edit], 'Visible', 'on'); - set([handles.sglx_host_label, handles.sglx_host_edit, handles.sglx_port_label, handles.sglx_port_edit, handles.sglx_probe_label, handles.sglx_probe_edit], 'Visible', 'off'); - log_message(handles, 'Switched to Open Ephys recording mode.'); - else % SpikeGLX - % Hide OpenEphys settings, show SpikeGLX - set([handles.oe_ip_label, handles.oe_ip_edit, handles.oe_proc_label, handles.oe_proc_edit, handles.oe_rec_label, handles.oe_rec_edit], 'Visible', 'off'); - set([handles.sglx_host_label, handles.sglx_host_edit, handles.sglx_port_label, handles.sglx_port_edit, handles.sglx_probe_label, handles.sglx_probe_edit], 'Visible', 'on'); - log_message(handles, 'Switched to SpikeGLX recording mode.'); - end - - % ========================================================================= - % WORKFLOW ACTIONS - % ========================================================================= - case 'load_sequence' - handles = value(ui_handles); - log_message(handles, '--- LOAD sequence initiated ---'); - set(handles.control_button, 'Enable', 'off', 'String', 'Loading...'); - set(handles.sample_button, 'Enable', 'off'); - - try - software = value(recording_software); - params = get_all_parameters(handles,software); - if ~validate_all_inputs(params,handles,software) - feval(mfilename, obj, 'reset_to_load_state'); - return; - end - current_params.value = params; - - [session_path, recording_save_path] = construct_session_paths(handles, params); - if isempty(session_path) || ~create_session_directories(handles, params, session_path) - feval(mfilename, obj, 'reset_to_load_state'); - return; - end - session_base_path.value = session_path; - - % Initialize the Behavior system - feval(mfilename, obj, 'initialize_behavior_system'); - - catch ME - log_message(handles, sprintf('ERROR during load sequence: %s', ME.message)); - feval(mfilename, obj, 'reset_to_load_state'); - rethrow(ME); - end - - case 'run_sequence' - handles = value(ui_handles); - params = value(current_params); - log_message(handles, '--- RUN sequence initiated ---'); - set(handles.sample_button, 'Enable', 'off'); - - try - if get(handles.cb_ephys, 'Value') - % Initialize the selected recording system - recording_save_path = fullfile(params.local_path, value(session_base_path), 'ephys'); - feval(mfilename, obj, 'initialize_recording_system', params, recording_save_path); - if isempty(value(recording_controller)) - feval(mfilename, obj, 'reset_to_load_state'); - return; - end - % Start Ephys Recording - feval(mfilename, obj, 'start_electrophysiology_recording', params); - else - log_message(handles, 'Ephys checkbox not selected. No recording started.'); - end - - currentState.value = 'Stop'; - set(handles.control_button, 'String', 'Stop'); - feval(mfilename, obj, 'start_blinking'); - - feval(mfilename, obj, 'start_behavioral_protocol', params); - log_message(handles, '--- RUN sequence complete. Experiment is live. ---'); - - catch ME - log_message(handles, sprintf('ERROR during run sequence: %s', ME.message)); - feval(mfilename, obj, 'stop_blinking'); - rethrow(ME); - end - - case 'stop_sequence' - handles = value(ui_handles); - params = value(current_params); - log_message(handles, '--- STOP sequence initiated ---'); - feval(mfilename, obj, 'stop_blinking'); - - try - behav_save_dir = fullfile(params.local_path, value(session_base_path), 'behav'); - feval(mfilename, obj, 'stop_behavioral_protocol', params, behav_save_dir); - - if get(handles.cb_ephys, 'Value') && ~isempty(value(recording_controller)) - feval(mfilename, obj, 'stop_electrophysiology_recording'); - end - - log_message(handles, '--- Experiment finished. Post-session sampling available. ---'); - currentState.value = 'PostExperiment'; - set(handles.control_button, 'String', 'Start New Experiment', 'BackgroundColor', [0.2, 0.8, 0.6]); - set(handles.sample_button, 'Enable', 'on'); - - catch ME - log_message(handles, sprintf('ERROR during stop sequence: %s', ME.message)); - rethrow(ME); - end - - % ========================================================================= - % SYSTEM INITIALIZATION & CONTROL - % ========================================================================= - case 'initialize_recording_system' - params = varargin{1}; - save_path = varargin{2}; - software = value(recording_software); - handles = value(ui_handles); - - try - if strcmp(software, 'OpenEphys') - log_message(handles, 'Initializing Open Ephys controller...'); - controller = OpenEphysHTTPServer(params.oe_gui_ip, 37497); - if isempty(controller), error('Failed to create Open Ephys controller'); end - else % SpikeGLX - log_message(handles, 'Initializing SpikeGLX controller...'); - controller = SpikeGL(params.sglx_host_ip, params.sglx_port); - if ~controller.IsConnected(), error('Failed to connect to SpikeGLX'); end - end - recording_controller.value = controller; - log_message(handles, sprintf('%s controller initialized successfully.', software)); - - % Set initial recording path - feval(mfilename, obj, 'set_recording_path', save_path); - - catch ME - log_message(handles, sprintf('Failed to initialize %s: %s', software, ME.message)); - recording_controller.value = []; - rethrow(ME); - end - - case 'set_recording_path' - save_path = varargin{1}; - software = value(recording_software); - controller = value(recording_controller); - params = value(current_params); - handles = value(ui_handles); - - if isempty(controller), return; end - - try - if strcmp(software, 'OpenEphys') - controller.setRecordPath(params.oe_rec_node_id, save_path); - else % SpikeGLX - controller.SetDataDir(save_path); - end - log_message(handles, sprintf('Recording path set to: %s', save_path)); - catch ME - log_message(handles, sprintf('Failed to set recording path: %s', ME.message)); - rethrow(ME); - end - - case 'start_electrophysiology_recording' - params = varargin{1}; - software = value(recording_software); - controller = value(recording_controller); - handles = value(ui_handles); - - if isempty(controller), error('Recording controller not initialized'); end - - try - probe_settings_struct = value(probe_settings); - feval(mfilename, obj, 'apply_probe_configuration', probe_settings_struct); - - main_ephys_path = fullfile(params.local_path, value(session_base_path), 'ephys'); - feval(mfilename, obj, 'set_recording_path', main_ephys_path); - - if strcmp(software, 'OpenEphys') - log_message(handles, 'Starting Open Ephys acquisition and recording...'); - controller.acquire(); pause(1); - controller.record(); - else % SpikeGLX - log_message(handles, 'Starting SpikeGLX recording...'); - run_name = sprintf('experiment_%s', datestr(now, 'yyyymmdd_HHMMSS')); - controller.SetRunName(run_name); - controller.StartRun(); - end - - log_message(handles, 'Electrophysiology recording is LIVE.'); - ephysState.value = 'Stop'; - set(handles.ephys_button, 'String', 'Stop Ephys', 'BackgroundColor', [1 0.6 0.6]); - - catch ME - log_message(handles, sprintf('Failed to start recording: %s', ME.message)); - rethrow(ME); - end - - case 'stop_electrophysiology_recording' - software = value(recording_software); - controller = value(recording_controller); - handles = value(ui_handles); - if isempty(controller), return; end - - try - if strcmp(software, 'OpenEphys') - log_message(handles, 'Stopping Open Ephys recording...'); - controller.idle(); - else % SpikeGLX - log_message(handles, 'Stopping SpikeGLX recording...'); - controller.StopRun(); - end - - log_message(handles, 'Electrophysiology recording stopped.'); - ephysState.value = 'Run'; - set(handles.ephys_button, 'String', 'Run Ephys', 'BackgroundColor', [0.8, 0.6, 1]); - - catch ME - log_message(handles, sprintf('Failed to stop recording: %s', ME.message)); - rethrow(ME); - end - - case 'initialize_behavior_system' - params = value(current_params); - handles = value(ui_handles); - try - log_message(handles, 'Initializing behavior control system...'); - behav_obj.value = dispatcher('init'); - h=get_sphandle('owner','dispatcher','name','myfig'); - set(value(h{1}), 'Visible','Off'); - if params.do_manual_test - feval(mfilename, obj, 'behav_control', 'manual_test'); - else - feval(mfilename, obj, 'continue_load_after_manual_test'); - end - catch ME - log_message(handles, ['FATAL ERROR initializing behavior system: ' ME.message]); - errordlg(['Failed to initialize behavior system. Check path and logs. Error: ' ME.message], 'Behavior System Error'); - rethrow(ME); - end - - case 'start_behavioral_protocol' - params = varargin{1}; - handles = value(ui_handles); - try - log_message(handles, 'Starting behavioral protocol...'); - feval(mfilename, obj, 'behav_control', 'run', params.protocol_name); - log_message(handles, 'Behavioral protocol is LIVE.'); - catch ME - log_message(handles, ['FATAL ERROR starting behavior protocol: ' ME.message]); - errordlg(['Failed to start behavior protocol. Check logs. Error: ' ME.message], 'Behavior System Error'); - rethrow(ME); - end - - case 'stop_behavioral_protocol' - params = varargin{1}; - behav_save_dir = varargin{2}; - handles = value(ui_handles); - try - log_message(handles, 'Ending behavioral session (saving data)...'); - feval(mfilename, obj, 'behav_control', 'end', params.protocol_name, params.behav_path, behav_save_dir); - log_message(handles, 'Behavioral data saved successfully.'); - catch ME - log_message(handles, ['FATAL ERROR ending behavioral session: ' ME.message]); - errordlg(['Failed to save behavioral data. Check logs. Error: ' ME.message], 'Behavior System Error'); - rethrow(ME); - end - - case 'manual_test_stopping' - - handles = value(ui_handles); - log_message(handles, 'Manual rig test complete. Cleaning up...'); - dispatcher(value(behav_obj), 'Stop'); - - %Let's pause until we know dispatcher is done running - set(value(stopping_complete_timer), 'TimerFcn', {@(h,e) feval(mfilename, obj, 'manual_test_stopped')}); - start(value(stopping_complete_timer)); - - case 'manual_test_stopped' - - if value(stopping_process_completed) %This is provided by RunningSection - stop(value(stopping_complete_timer)); %Stop looping. - dispatcher('set_protocol', ''); - is_running.value = 0; - feval(mfilename, obj, 'continue_load_after_manual_test'); - end - - case 'continue_load_after_manual_test' - params = value(current_params); - handles = value(ui_handles); - video_save_dir = fullfile(params.local_path, value(session_base_path), 'behav'); - try - log_message(handles, 'Loading main behavioral protocol...'); - feval(mfilename, obj, 'behav_control', 'load_main_protocol', params.experimenter, params.rat_name, params.protocol_name, video_save_dir, params.behav_path); - log_message(handles, 'Behavior system loaded and ready.'); - log_message(handles, '--- LOAD sequence complete. Ready to run. ---'); - currentState.value = 'Run'; - set(handles.control_button, 'Enable', 'on', 'String', 'Run', 'BackgroundColor', [0.4, 0.8, 0.4]); - set(handles.sample_button, 'Enable', 'on'); - catch ME - log_message(handles, ['FATAL ERROR loading main protocol: ' ME.message]); - errordlg(['Failed to load main protocol. Error: ' ME.message], 'Behavior System Error'); - feval(mfilename, obj, 'reset_to_load_state'); - end - - % ========================================================================= - % INDIVIDUAL CONTROL CALLBACKS - % ========================================================================= - case 'behav_control_callback' - switch value(behavState) - case 'Run', feval(mfilename, obj, 'behav_control', 'load_run'); - case 'Stop', feval(mfilename, obj, 'behav_control', 'end'); - end - - case 'ephys_control_callback' - switch value(ephysState) - case 'Run', feval(mfilename, obj, 'run_ephys_individually'); - case 'Stop', feval(mfilename, obj, 'stop_ephys_individually'); - end - - case 'run_ephys_individually' - params = value(current_params); - handles = value(ui_handles); - log_message(handles, '--- Starting Ephys Recording Individually ---'); - set(handles.ephys_button, 'Enable', 'off'); - try - if isempty(value(recording_controller)) - feval(mfilename, obj, 'initialize_recording_system', params, ''); - end - feval(mfilename, obj, 'start_electrophysiology_recording', params); - catch ME - log_message(handles, sprintf('ERROR starting ephys: %s', ME.message)); - end - set(handles.ephys_button, 'Enable', 'on'); - - case 'stop_ephys_individually' - handles = value(ui_handles); - log_message(handles, '--- Stopping Ephys Recording Individually ---'); - set(handles.ephys_button, 'Enable', 'off'); - try - feval(mfilename, obj, 'stop_electrophysiology_recording'); - catch ME - log_message(handles, sprintf('ERROR stopping ephys: %s', ME.message)); - end - set(handles.ephys_button, 'Enable', 'on'); - - % ========================================================================= - % BEHAVIOR CONTROL ACTIONS - % ========================================================================= - case 'behav_control' - sub_action = varargin{1}; - args = varargin(2:end); - handles = value(ui_handles); - - switch sub_action - case 'load_main_protocol' - experimenter = args{1}; ratname = args{2}; protocol_name = args{3}; - video_save_dir = args{4}; behav_path = args{5}; - log_message(handles, ['Loading protocol: ' protocol_name]); - dispatcher('set_protocol', protocol_name); - rath = get_sphandle('name', 'ratname', 'owner', protocol_name); - exph = get_sphandle('name', 'experimenter', 'owner', protocol_name); - rath{1}.value = ratname; exph{1}.value = experimenter; - protobj = eval(protocol_name); - log_message(handles, ['Loading settings for ' ratname]); - [~, sfile] = load_solouiparamvalues(ratname, 'experimenter', experimenter, 'owner', class(protobj), 'interactive', 0); - feval(protocol_name, protobj, 'set_setting_params', ratname, experimenter, sfile, char(datetime('now')), video_save_dir); - if ~dispatcher('is_running'), pop_history(class(protobj), 'include_non_gui', 1); feval(protocol_name, protobj, 'prepare_next_trial'); end - - case 'crashed' - log_message(handles, '--- BEHAVIOR CRASH RECOVERY INITIATED ---'); - params = value(current_params); - video_save_dir = fullfile(params.local_path, value(session_base_path), 'behav'); - feval(mfilename, obj, 'behav_control', 'load_protocol_after_crash', params.experimenter, params.rat_name, params.protocol_name, video_save_dir, params.behav_path); - feval(mfilename, obj, 'behav_control', 'run', params.protocol_name); - log_message(handles, '--- RECOVERY COMPLETE: Behavior protocol restarted ---'); - - case 'load_protocol_after_crash' - experimenter = args{1}; ratname = args{2}; protocol_name = args{3}; - video_save_dir = args{4}; behav_path = args{5}; - log_message(handles, ['Loading protocol after crash: ' protocol_name]); - dispatcher('set_protocol', protocol_name); - rath = get_sphandle('name', 'ratname', 'owner', protocol_name); - exph = get_sphandle('name', 'experimenter', 'owner', protocol_name); - rath{1}.value = ratname; exph{1}.value = experimenter; - protobj = eval(protocol_name); - try - log_message(handles, ['Loading previous data for ' ratname]); - today_date = char(datetime('now','format','yyMMdd')); - temp_data_dir = fullfile(behav_path,'SoloData','Data',experimenter,ratname); - temp_data_file = sprintf('data_@%s_%s_%s_%s_ASV.mat',protocol_name,experimenter,ratname,today_date); - if isfile(fullfile(temp_data_dir,temp_data_file)) - dispatcher('runstart_disable'); - load_soloparamvalues(ratname, 'experimenter', experimenter, 'owner', protocol_name, 'interactive', 0,'data_file',fullfile(temp_data_dir,temp_data_file)); - dispatcher('runstart_enable'); - end - if ~dispatcher('is_running'), pop_history(class(protobj), 'include_non_gui', 1); feval(protocol_name, protobj, 'prepare_next_trial'); end - catch - log_message(handles, ['Loading settings for ' ratname]); - [~, sfile] = load_solouiparamvalues(ratname, 'experimenter', experimenter, 'owner', class(protobj), 'interactive', 0); - feval(protocol_name, protobj, 'set_setting_params', ratname, experimenter, sfile, char(datetime('now')), video_save_dir); - if ~dispatcher('is_running'), pop_history(class(protobj), 'include_non_gui', 1); feval(protocol_name, protobj, 'prepare_next_trial'); end - end - - case 'load_run' - set(handles.behav_button, 'Enable', 'off'); - log_message(handles, '--- STARTING BEHAV PROTOCOL ---'); - params = value(current_params); - video_save_dir = fullfile(params.local_path, value(session_base_path), 'behav'); - feval(mfilename, obj, 'behav_control', 'load_protocol_after_crash', params.experimenter, params.rat_name, params.protocol_name, video_save_dir, params.behav_path); - feval(mfilename, obj, 'behav_control', 'run', params.protocol_name); - log_message(handles, '--- START COMPLETE: Behavior protocol started ---'); - set(handles.behav_button, 'Enable', 'on'); - - case 'run' - protocol_name = args{1}; protobj = eval(protocol_name); - log_message(handles, 'Starting video recording via protocol...'); - feval(protocol_name, protobj, 'start_recording'); - log_message(handles, 'Starting dispatcher to run trials...'); - is_running.value = 1; - behavState.value = 'Stop'; - set(handles.behav_button, 'String', 'Stop Behav', 'BackgroundColor', [1 0.6 0.6]); - dispatcher(value(behav_obj), 'Run'); - - case 'end' - set(handles.behav_button, 'Enable', 'off'); - if length(args) >= 3 - protocol_name = args{1}; root_dir = args{2}; behav_copy_dir = args{3}; - else - params = value(current_params); - protocol_name = params.protocol_name; - root_dir = params.behav_path; - behav_copy_dir = fullfile(params.local_path, value(session_base_path), 'behav'); - end - log_message(handles, 'Stopping dispatcher...'); - dispatcher(value(behav_obj), 'Stop'); - set(value(stopping_complete_timer), 'Period', 0.8,'TimerFcn', {@(h,e) feval(mfilename, obj, 'behav_control','end_continued',protocol_name, root_dir, behav_copy_dir)}); - start(value(stopping_complete_timer)); - - case 'end_continued' - if value(stopping_process_completed) % This is provided by RunningSection - protocol_name = args{1}; root_dir = args{2}; destination_path = args{3}; - stop(value(stopping_complete_timer)); %Stop looping. - is_running.value = 0; - feval(mfilename, obj, 'behav_control', 'send_empty_state_machine'); - protobj = eval(protocol_name); - log_message(handles, 'Ending session via protocol...'); - feval(protocol_name, protobj, 'end_session'); - log_message(handles, 'Saving data and settings...'); - data_file = SavingSection(protobj, 'savedata', 'interactive', 0); - try - feval(protocol_name, protobj, 'pre_saving_settings'); - catch - log_message(handles, 'Protocol does not have a pre_saving_settings section.'); - end - [settings_file, ~] = SavingSection(protobj, 'get_set_filename'); - SavingSection(protobj, 'savesets', 'interactive', 0); - log_message(handles, 'Committing data and settings to SVN...'); - commit_to_svn(handles, data_file, settings_file, root_dir); - dispatcher('set_protocol', ''); - data_file = [data_file '.mat']; - [status, msg] = copyfile(data_file, destination_path); - if status, log_message(handles,'Data File copied successfully.'); - else, log_message(handles,['Error copying Data file: ' msg]); - end - behavState.value = 'Run'; - set(handles.behav_button, 'String', 'Run Behav', 'BackgroundColor', [1, 0.8, 0.6]); - feval(mfilename, obj, 'save_log_file'); - set(handles.behav_button, 'Enable', 'on'); - end - - case 'manual_test' - log_message(handles, 'Loading manual rig test protocol...'); - dispatcher('set_protocol', 'Rigtest_singletrial'); - h=get_sphandle('owner','Rigtest_singletrial','name', 'myfig'); - for i=1:numel(h); set(value(h{i}),'Visible','Off'); end - is_running.value = 1; - log_message(handles, 'Starting manual rig test. Please complete the one-trial test.'); - dispatcher(value(behav_obj), 'Run'); - - case 'create_svn_data_dir' - experimenter = args{1}; ratname = args{2}; behav_dir = args{3}; dir_name = args{4}; - dirCurrent = cd; - settings_path = fullfile(behav_dir, 'SoloData', dir_name); - exp_path = fullfile(settings_path, experimenter); - rat_path = fullfile(exp_path, ratname); - if ~isfolder(settings_path), mkdir(settings_path); system(['svn add ' dir_name]); end - if ~isfolder(exp_path), cd(settings_path); mkdir(experimenter); system(['svn add ' experimenter]); end - if ~isfolder(rat_path), cd(exp_path); mkdir(ratname); system(['svn add ' ratname]); end - cd(dirCurrent); - log_message(handles, ['Created SVN directory structure for ' ratname]); - - case 'send_empty_state_machine' - state_machine_server = bSettings('get', 'RIGS', 'state_machine_server'); - server_slot = bSettings('get', 'RIGS', 'server_slot'); if isnan(server_slot), server_slot = 0; end - card_slot = bSettings('get', 'RIGS', 'card_slot'); if isnan(card_slot), card_slot = 0; end - sm = BpodSM(state_machine_server, 3333, server_slot); sm = Initialize(sm); - [inL, outL] = MachinesSection(dispatcher, 'determine_io_maps'); - sma = StateMachineAssembler('full_trial_structure'); - sma = add_state(sma, 'name', 'vapid_state_in_vapid_matrix'); - send(sma, sm, 'run_trial_asap', 0, 'input_lines', inL, 'dout_lines', outL, 'sound_card_slot', int2str(card_slot)); - - end - - case 'crash_detected' - % handles = feval(mfilename, obj, 'get_ui_handles'); - handles = value(ui_handles); - if ~strcmp(value(currentState), 'Stop') || isempty(value(behav_obj)), return; end - - log_message(handles, '!!! CRASH DETECTED: Behavior system is not running. Attempting recovery...'); - try - feval(mfilename, obj, 'behav_control', 'crashed'); - - catch ME - log_message(handles, sprintf('FATAL: Recovery attempt failed: %s', ME.message)); - getReport(ME, 'extended', 'hyperlinks', 'on'); - errordlg('Automatic recovery failed. Please stop the experiment manually.', 'Recovery Failed'); - end - % ========================================================================= - % SAMPLE EPHYS RECORDINGS - % ========================================================================= - case 'sample_recording_wrapper' - if strcmp(value(currentState), 'PostExperiment') - feval(mfilename, obj, 'sample_recording', 'post_session'); - else - feval(mfilename, obj, 'sample_recording', 'pre_session'); - end - - case 'sample_recording' - prefix = varargin{1}; - handles = value(ui_handles); - log_message(handles, ['--- ' upper(prefix) ' SAMPLE RECORDING INITIATED ---']); - set([handles.sample_button, handles.control_button], 'Enable', 'off', 'String', 'Sampling...'); - drawnow; - - try - software = value(recording_software); - params = get_all_parameters(handles,software); - if isempty(value(recording_controller)) - feval(mfilename, obj, 'initialize_recording_system', params, ''); - end - - if isempty(value(session_base_path)) - [session_path, ~] = construct_session_paths(handles, params); - if isempty(session_path) || ~create_session_directories(handles, params, session_path) - error('Failed to create session directories'); - end - session_base_path.value = session_path; - end - - sample_dir_name = sprintf('%s_sample_recording', prefix); - sample_save_path = fullfile(params.local_path, value(session_base_path), 'ephys', sample_dir_name); - if ~exist(sample_save_path, 'dir'), mkdir(sample_save_path); end - - software = value(recording_software); - if strcmp(software, 'OpenEphys') - feval(mfilename, obj, 'execute_openephys_sampling', sample_save_path); - else - feval(mfilename, obj, 'execute_spikeglx_sampling', sample_save_path); - end - log_message(handles, '--- SAMPLE RECORDING COMPLETE ---'); - - catch ME - log_message(handles, sprintf('ERROR during sample recording: %s', ME.message)); - rethrow(ME); - end - - % Reset button states - if strcmp(value(currentState), 'PostExperiment') - set(handles.control_button, 'Enable', 'on', 'String', 'Start New Experiment'); - feval(mfilename, obj, 'save_log_file'); - else - set(handles.control_button, 'Enable', 'on', 'String', 'Load'); - end - set(handles.sample_button, 'Enable', 'on', 'String', 'Start Sample Recording'); - - case 'execute_openephys_sampling' - save_path = varargin{1}; - controller = value(recording_controller); - params = value(current_params); - handles = value(ui_handles); - probe_settings_struct = value(probe_settings); - duration = str2double(get(handles.sample_duration, 'String')); - - controller.setParameters(params.oe_proc_node_id, 0, 'Reference', probe_settings_struct.reference); - if strcmp(probe_settings_struct.version, '1.0'), num_banks = 3; else, num_banks = 4; end - - for bank = 0:(num_banks - 1) - log_message(handles, sprintf('Recording OE Bank %d for %d seconds...', bank, duration)); - controller.setParameters(params.oe_proc_node_id, 0, 'bank', bank); pause(1); - controller.setRecordPath(params.oe_rec_node_id, save_path); pause(1); - controller.acquire(duration); pause(1); - controller.record(duration); - controller.idle(); - log_message(handles, sprintf('Finished recording Bank %d.', bank)); pause(1); - end - - if ~isempty(probe_settings_struct.imro_path) - controller.config(params.oe_proc_node_id, ['LOADIMRO ' probe_settings_struct.imro_path]); - else - controller.setParameters(params.oe_proc_node_id, 0, 'bank', probe_settings_struct.bank); - end - - case 'execute_spikeglx_sampling' - save_path = varargin{1}; - controller = value(recording_controller); - handles = value(ui_handles); - probe_settings_struct = value(probe_settings); - duration = str2double(get(handles.sample_duration, 'String')); - - controller.SetDataDir(save_path); - if strcmp(probe_settings_struct.version, '1.0'), num_banks = 3; else, num_banks = 4; end - - for bank = 0:(num_banks - 1) - log_message(handles, sprintf('Recording SGLX Bank %d for %d seconds...', bank, duration)); - % Bank selection for SpikeGLX depends on specific API calls for channel selection, not a simple 'bank' parameter - % This is a placeholder for more complex channel/bank setting logic. - % For now, we record with the currently active map. - - run_name = sprintf('sample_bank_%d_%s', bank, datestr(now, 'yyyymmdd_HHMMSS')); - controller.SetRunName(run_name); - controller.StartRun(); - pause(duration); - controller.StopRun(); - log_message(handles, sprintf('Finished recording Bank %d.', bank)); pause(1); - end - - % ========================================================================= - % PROBE GUI AND SETTINGS - % ========================================================================= - case 'open_probe_gui' - handles = value(ui_handles); - log_message(handles, 'Opening probe settings GUI...'); - probe_fig = figure('Name', 'Neuropixel Probe Settings', 'Position', [300 300 450 300], ... - 'MenuBar', 'none', 'ToolBar', 'none', 'NumberTitle', 'off', 'Resize', 'off'); - p_handles = struct(); - p_handles.version_group = uibuttongroup(probe_fig, 'Title', 'Probe Version', 'Position', [0.05 0.75 0.9 0.2]); - uicontrol(p_handles.version_group, 'Style', 'radiobutton', 'String', 'NP 1.0 (3 Banks)', 'Position', [10 5 150 25], 'Tag', '1.0'); - uicontrol(p_handles.version_group, 'Style', 'radiobutton', 'String', 'NP 2.0 (4 Banks)', 'Position', [200 5 150 25], 'Tag', '2.0'); - p_handles.ref_group = uibuttongroup(probe_fig, 'Title', 'Reference', 'Position', [0.05 0.5 0.4 0.2]); - uicontrol(p_handles.ref_group, 'Style', 'radiobutton', 'String', 'Tip', 'Position', [10 5 80 25], 'Tag', 'Tip'); - uicontrol(p_handles.ref_group, 'Style', 'radiobutton', 'String', 'External', 'Position', [100 5 80 25], 'Tag', 'External'); - p_handles.bank_panel = uipanel(probe_fig, 'Title', 'Target Bank', 'Position', [0.5 0.5 0.45 0.2]); - uicontrol(p_handles.bank_panel, 'Style', 'text', 'String', 'Bank:', 'Position', [10 5 40 20]); - p_handles.bank_edit = uicontrol(p_handles.bank_panel, 'Style', 'edit', 'String', '0', 'Position', [60 5 50 25]); - uicontrol(probe_fig, 'Style', 'text', 'String', 'IMRO File:', 'Position', [20 120 60 20]); - p_handles.imro_text = uicontrol(probe_fig, 'Style', 'text', 'String', 'None selected', 'Position', [90 120 280 20], 'HorizontalAlignment', 'left'); - uicontrol(probe_fig, 'Style', 'pushbutton', 'String', 'Browse...', 'Position', [20 85 100 30], 'Callback', {@(h,e) feval(mfilename, obj, 'browse_imro', p_handles)}); - uicontrol(probe_fig, 'Style', 'pushbutton', 'String', 'Clear IMRO', 'Position', [130 85 100 30], 'Callback', {@(h,e) feval(mfilename, obj, 'clear_imro', p_handles)}); - uicontrol(probe_fig, 'Style', 'pushbutton', 'String', 'Apply & Close', 'Position', [250 25 180 30], 'FontWeight', 'bold', 'Callback', {@(h,e) feval(mfilename, obj, 'apply_probe_settings', p_handles)}); - probe_gui_handles.value = p_handles; - - case 'browse_imro' - p_handles = varargin{1}; - [file, path] = uigetfile('*.imro', 'Select IMRO File'); - if isequal(file, 0) || isequal(path, 0), return; - else - full_path = fullfile(path, file); - set(p_handles.imro_text, 'String', full_path); - set(findobj(p_handles.bank_panel, '-property', 'Enable'), 'Enable', 'off'); - end - - case 'clear_imro' - p_handles = varargin{1}; - set(p_handles.imro_text, 'String', 'None selected'); - set(findobj(p_handles.bank_panel, '-property', 'Enable'), 'Enable', 'on'); - - case 'apply_probe_settings' - p_handles = varargin{1}; - handles = value(ui_handles); - settings.version = get(get(p_handles.version_group, 'SelectedObject'), 'Tag'); - settings.reference = get(get(p_handles.ref_group, 'SelectedObject'), 'Tag'); - settings.bank = str2double(get(p_handles.bank_edit, 'String')); - settings.imro_path = get(p_handles.imro_text, 'String'); - if strcmp(settings.imro_path, 'None selected'), settings.imro_path = ''; end - probe_settings.value = settings; - if ~isempty(settings.imro_path) - set(handles.target_display, 'String', 'Target: IMRO File'); - else - set(handles.target_display, 'String', ['Target: Bank ' num2str(settings.bank)]); - end - log_message(handles, 'Probe settings saved.'); - close(p_handles.ref_group.Parent); - probe_gui_handles.value = []; - - case 'apply_probe_configuration' - probe_settings = varargin{1}; - software = value(recording_software); - controller = value(recording_controller); - params = value(current_params); - handles = value(ui_handles); - if isempty(controller), return; end - - try - if strcmp(software, 'OpenEphys') - log_message(handles, sprintf('Setting OE reference to: %s', probe_settings.reference)); - controller.setParameters(params.oe_proc_node_id, 0, 'Reference', probe_settings.reference); - if ~isempty(probe_settings.imro_path) - log_message(handles, sprintf('Loading IMRO file: %s', probe_settings.imro_path)); - controller.config(params.oe_proc_node_id, ['LOADIMRO ' probe_settings.imro_path]); - else - log_message(handles, sprintf('Setting bank to: %d', probe_settings.bank)); - controller.setParameters(params.oe_proc_node_id, 0, 'bank', probe_settings.bank); - end - else % SpikeGLX - % SpikeGLX probe configuration (e.g., reference, channel map) is more complex - % and typically handled by setting parameters or loading a meta file. - % This is a placeholder for those more complex API calls. - log_message(handles, 'Applying SpikeGLX probe settings (via meta file or API)...'); - if ~isempty(probe_settings.imro_path) - log_message(handles, 'Note: For SpikeGLX, ensure IMRO settings are loaded within the SpikeGLX GUI and save as part of the meta file.'); - end - end - log_message(handles, 'Probe configuration applied successfully.'); - catch ME - log_message(handles, sprintf('Failed to apply probe settings: %s', ME.message)); - rethrow(ME); - end - - % ========================================================================= - % UTILITY & OTHER ACTIONS - % ========================================================================= - case 'browse_path' - type = varargin{1}; - handles = value(ui_handles); - log_message(handles, ['Opening browse dialog for ' type ' path...']); - folder_path = uigetdir; - if folder_path ~= 0 - if strcmp(type, 'local'), set(handles.local_edit, 'String', folder_path); - elseif strcmp(type, 'central'), set(handles.central_edit, 'String', folder_path); - elseif strcmp(type, 'behav'), set(handles.behav_edit, 'String', folder_path); - end - log_message(handles, [type ' path set.']); - else, log_message(handles, 'Path selection cancelled.'); end - - case 'update_subject_id' - handles = value(ui_handles); - software = value(recording_software); - params = get_all_parameters(handles,software); - if isempty(params.local_path) || isempty(params.central_path) || isempty(params.project_name) || isempty(params.rat_name) - return; - end - max_local_id = find_max_subject_id(params.local_path, params.project_name, params.rat_name); - max_central_id = find_max_subject_id(params.central_path, params.project_name, params.rat_name); - final_id = max(max_local_id, max_central_id); - if final_id > 0 - log_message(handles, ['Found existing Subject ID: ' num2str(final_id) '. Populating field.']); - set(handles.sub_edit, 'String', sprintf('%03d', final_id)); - else - log_message(handles, 'No existing Subject ID found for this rat. Please enter a new ID.'); - end - - case 'save_log_file' - handles = value(ui_handles); - params = value(current_params); - session_path = value(session_base_path); - if isempty(session_path) - log_message(handles, 'WARNING: Cannot save log file. Session path not set.'); return; - end - log_path = fullfile(params.local_path, session_path, 'behav'); - if ~exist(log_path, 'dir') - log_message(handles, ['WARNING: Behavior folder not found. Cannot save log. Path: ' log_path]); return; - end - log_file_path = fullfile(log_path, 'session_log.txt'); - try - log_content = get(handles.log_box, 'String'); - fid = fopen(log_file_path, 'w'); - if fid == -1, error('Could not open file for writing.'); end - for i = 1:length(log_content), fprintf(fid, '%s\n', log_content{i}); end - fclose(fid); - log_message(handles, ['Log file saved successfully to: ' log_file_path]); - catch ME - log_message(handles, ['ERROR: Could not save log file. Details: ' ME.message]); - end - - case 'reset_to_load_state' - handles = value(ui_handles); - currentState.value = 'Load'; - behavState.value = 'Run'; - ephysState.value = 'Run'; - set(handles.control_button, 'Enable', 'on', 'String', 'Load', 'BackgroundColor', [0.2, 0.6, 0.8]); - set(handles.sample_button, 'Enable', 'on'); - recording_controller.value = []; - behav_obj.value = []; - current_params.value = []; - session_base_path.value = ''; - log_message(handles, 'GUI reset to load state.'); - - case 'close' - try - feval(mfilename, obj, 'stop_blinking'); - if ~isempty(value(recording_controller)), delete(value(recording_controller)); end - if ishandle(value(myfig)), delete(value(myfig)); end - delete_sphandle('owner', ['^@' mfilename '$']); - if ~isempty(value(behav_obj)), dispatcher(value(behav_obj),'close'); end - obj = []; - catch - if exist('myfig','var') == 1 - if ishandle(value(myfig)), delete(value(myfig)); end - delete_sphandle('owner', ['^@' mfilename '$']); - obj = []; - end - - case 'is_running' - if exist('is_running','var') == 1 - obj = logical(value(is_running)); - else - obj = 0; - end - - case 'start_blinking' - handles = value(ui_handles); - blinking_timer.value = timer('ExecutionMode', 'fixedRate', 'Period', 0.5, 'TimerFcn', {@toggle_button_color, handles.control_button}); - start(value(blinking_timer)); - - case 'stop_blinking' - handles = value(ui_handles); - if ~isempty(value(blinking_timer)) && isvalid(value(blinking_timer)) - stop(value(blinking_timer)); - delete(value(blinking_timer)); - blinking_timer.value = []; - end - set(handles.control_button, 'BackgroundColor', [1, 0.4, 0.4]); - - otherwise - error('Unknown action: %s', action); -end -return; - -%% ======================================================================= -% PARAMETER AND VALIDATION FUNCTIONS -% ======================================================================= -function params = get_all_parameters(handles,software) - params.protocol_name = get(handles.protocol_edit, 'String'); - params.do_manual_test = get(handles.manual_test, 'Value'); - params.experimenter = get(handles.exp_edit, 'String'); - params.rat_name = get(handles.rat_name_edit, 'String'); - params.behav_path = get(handles.behav_edit, 'String'); - params.project_name = get(handles.proj_edit, 'String'); - params.subject_id = get(handles.sub_edit, 'String'); - params.local_path = get(handles.local_edit, 'String'); - params.central_path = get(handles.central_edit, 'String'); - - if strcmp(software, 'OpenEphys') - params.oe_gui_ip = get(handles.oe_ip_edit, 'String'); - params.oe_proc_node_id = get(handles.oe_proc_edit, 'String'); - params.oe_rec_node_id = get(handles.oe_rec_edit, 'String'); - else - params.sglx_host_ip = get(handles.sglx_host_edit, 'String'); - params.sglx_port = str2double(get(handles.sglx_port_edit, 'String')); - params.sglx_probe_index = str2double(get(handles.sglx_probe_edit, 'String')); - end - -function is_valid = validate_all_inputs(params,handles,software) - is_valid = false; - required_fields = {'protocol_name', 'rat_name', 'behav_path', 'project_name', 'subject_id', 'local_path'}; - for i = 1:length(required_fields) - if ~isfield(params, required_fields{i}) || isempty(params.(required_fields{i})) - msg = sprintf('Field "%s" cannot be empty.', strrep(required_fields{i}, '_', ' ')); - log_message(handles, sprintf('ERROR: %s', msg)); errordlg(msg, 'Input Error'); - return; - end - end - if ~get(handles.cb_ephys, 'Value') && ~get(handles.cb_behav, 'Value') && ~get(handles.cb_anat, 'Value') && ~get(handles.cb_funcimg, 'Value') - msg = 'At least one subfolder must be selected.'; - log_message(handles, sprintf('ERROR: %s', msg)); errordlg(msg, 'Input Error'); - return; - end - if strcmp(software, 'OpenEphys') - if isempty(params.oe_gui_ip) || isempty(params.oe_proc_node_id) || isempty(params.oe_rec_node_id) - msg = 'Open Ephys connection parameters cannot be empty.'; - log_message(handles, sprintf('ERROR: %s', msg)); errordlg(msg, 'Input Error'); - return; - end - else - if isempty(params.sglx_host_ip) || isnan(params.sglx_port) || isnan(params.sglx_probe_index) - msg = 'SpikeGLX connection parameters cannot be empty or non-numeric.'; - log_message(handles, sprintf('ERROR: %s', msg)); errordlg(msg, 'Input Error'); - return; - end - end - is_valid = true; - -%% ======================================================================= -% PATH AND DIRECTORY FUNCTIONS -% ======================================================================= -function [session_base, recording_path] = construct_session_paths(handles, params) - if isempty(params.experimenter) - subject_name = sprintf('sub-%s_id-%s', params.subject_id, params.rat_name); - else - subject_name = sprintf('sub-%s_id-%s_expmtr-%s', params.subject_id, params.rat_name, params.experimenter); - end - subject_base_path = fullfile(params.project_name, 'rawdata', subject_name); - local_subject_dir = fullfile(params.local_path, subject_base_path); - central_subject_dir = fullfile(params.central_path, subject_base_path); - new_ses_num = max(find_max_session_number(local_subject_dir), find_max_session_number(central_subject_dir)) + 1; - log_message(handles, sprintf('Last session found: %d. Creating new session: %d.', new_ses_num - 1, new_ses_num)); - session_datetime_str = char(datetime('now', 'Format', 'yyyyMMdd''T''HHmmss')); - session_folder_name = sprintf('ses-%02d_date-%s_dtype-ephys', new_ses_num, session_datetime_str); - session_base = fullfile(subject_base_path, session_folder_name); - recording_path = fullfile(params.local_path, session_base, 'ephys'); - log_message(handles, ['New session path determined: ' session_base]); - -function max_ses = find_max_session_number(base_path) - max_ses = 0; if ~exist(base_path, 'dir'), return; end - dir_contents = dir(fullfile(base_path, 'ses-*')); - if isempty(dir_contents), return; end - session_numbers = []; - for i = 1:length(dir_contents) - if dir_contents(i).isdir - token = regexp(dir_contents(i).name, '^ses-(\d+)', 'tokens'); - if ~isempty(token), session_numbers(end+1) = str2double(token{1}{1}); end - end - end - if ~isempty(session_numbers), max_ses = max(session_numbers); end - -function success = create_session_directories(handles, params,session_base_path) - success = false; - subfolders = {}; - if get(handles.cb_ephys, 'Value'), subfolders{end+1} = 'ephys'; end - if get(handles.cb_behav, 'Value'), subfolders{end+1} = 'behav'; end - if get(handles.cb_anat, 'Value'), subfolders{end+1} = 'anat'; end - if get(handles.cb_funcimg, 'Value'), subfolders{end+1} = 'funcimg'; end - try - for i = 1:length(subfolders) - local_target_path = fullfile(params.local_path, session_base_path, subfolders{i}); - log_message(handles, ['Creating local directory: ' local_target_path]); - if ~exist(local_target_path, 'dir'), mkdir(local_target_path); end - end - log_message(handles, 'All selected local directories created successfully.'); - success = true; - catch ME - msg = sprintf('Failed to create directories: %s', ME.message); - log_message(handles, ['ERROR: ' msg]); errordlg(msg, 'Directory Error'); - end - -function max_id = find_max_subject_id(base_path, project, rat) - max_id = 0; - search_path = fullfile(base_path, project, 'rawdata'); - if ~exist(search_path, 'dir'), return; end - dir_contents = dir(search_path); - if isempty(dir_contents), return; end - subject_ids = []; - pattern = sprintf('^sub-(\\d+)_id-%s', rat); - for i = 1:length(dir_contents) - if dir_contents(i).isdir - token = regexp(dir_contents(i).name, pattern, 'tokens'); - if ~isempty(token), subject_ids(end+1) = str2double(token{1}{1}); end - end - end - if ~isempty(subject_ids), max_id = max(subject_ids); end - -%% ======================================================================= -% HELPER & UTILITY FUNCTIONS -% ======================================================================= -function log_message(handles,logStr) - try - if ~isfield(handles, 'log_box') || ~isvalid(handles.log_box), return; end - current_text = get(handles.log_box, 'String'); - timestamp = datestr(now, '[HH:MM:SS] '); - new_line = [timestamp, logStr]; - new_text = [current_text; {new_line}]; - set(handles.log_box, 'String', new_text, 'Value', numel(new_text)); - drawnow; - catch - fprintf('%s: %s\n', datestr(now, '[HH:MM:SS]'), logStr); - end - -function toggle_button_color(~, ~, button_handle) - if ~isvalid(button_handle), return; end - currentColor = get(button_handle, 'BackgroundColor'); - if isequal(currentColor, [1, 0.4, 0.4]), set(button_handle, 'BackgroundColor', [1, 0.7, 0.4]); - else, set(button_handle, 'BackgroundColor', [1, 0.4, 0.4]); end - - -function commit_to_svn(handles, file_path_data,file_path_settings, root_dir) - -if isempty(file_path_data), return; end - if isempty(file_path_settings), return; end - [pname_data, fname_data, ~] = fileparts(file_path_data); - [pname_settings, fname_settings, ~] = fileparts(file_path_settings); - - configFilePath = fullfile(root_dir,'PASSWORD_CONFIG-DO_NOT_VERSIONCONTROL.mat'); - if ~exist(configFilePath, 'file') - log_message(handles, ['SVN commit failed: Password config file not found at ' configFilePath]); - return; - end - load(configFilePath, 'svn_user', 'svn_password'); - logmsg = char(strcat('automated commit from GUI for data and settings for ', {' '} ,fname_data,{'@'})); - % current_dir = cd; - cd(pname_data); - add_cmd_data = char(strcat('svn add', {' '}, fname_data, '.mat',{'@'})); - system(add_cmd_data); - - cd(pname_settings); - add_cmd_settings = char(strcat('svn add', {' '}, fname_settings, '.mat',{'@'})); - system(add_cmd_settings); - - commit_cmd = sprintf('svn ci --username="%s" --password="%s" -m "%s"', svn_user, svn_password, logmsg); - [status, ~] = system(commit_cmd); - - if status == 0 - log_message(handles, ['SVN commit successful for ' fname_data]); - else - log_message(handles, ['SVN commit FAILED for ' fname_data '.']); - end - - cd(fullfile(root_dir,'ExperPort')); - - - %% ======================================================================= -% DOCUMENTATION AND USAGE EXAMPLES -% ======================================================================= - -function display_usage_help() -% DISPLAY_USAGE_HELP - Display usage instructions and examples -% -% This function provides comprehensive usage documentation for the GUI - - fprintf('\n=== Neuropixels Recording & Behavior Controller Usage Guide ===\n\n'); - - fprintf('1. INITIALIZATION:\n'); - fprintf(' OpenEphys_Neuroblueprint_GUI(''init'');\n\n'); - - fprintf('2. WORKFLOW:\n'); - fprintf(' a) Select recording software (Open Ephys or SpikeGLX)\n'); - fprintf(' b) Configure behavior settings (protocol, experimenter, rat)\n'); - fprintf(' c) Set up NeuroBlueprint data paths\n'); - fprintf(' d) Configure probe settings (version, reference, bank/IMRO)\n'); - fprintf(' e) Set recording software connection parameters\n'); - fprintf(' f) Click "Load" to initialize systems\n'); - fprintf(' g) Click "Run" to start experiment\n'); - fprintf(' h) Click "Stop" to end experiment and save data\n\n'); - - fprintf('3. PROBE CONFIGURATION:\n'); - fprintf(' - Supports Neuropixels 1.0 (3 banks) and 2.0 (4 banks)\n'); - fprintf(' - Reference options: Tip or External\n'); - fprintf(' - Bank selection: Manual bank number or IMRO file\n'); - fprintf(' - Pre/post-session sampling across all banks\n\n'); - - fprintf('4. DATA ORGANIZATION:\n'); - fprintf(' - Follows NeuroBlueprint format\n'); - fprintf(' - Structure: project/rawdata/subject/session/datatype/\n'); - fprintf(' - Automatic session numbering\n'); - fprintf(' - SVN integration for version control\n\n'); - - fprintf('5. RECORDING SOFTWARE SUPPORT:\n'); - fprintf(' Open Ephys:\n'); - fprintf(' - HTTP API control\n'); - fprintf(' - Real-time parameter adjustment\n'); - fprintf(' - Acquisition and recording control\n\n'); - fprintf(' SpikeGLX:\n'); - fprintf(' - MATLAB SDK integration\n'); - fprintf(' - Run name management\n'); - fprintf(' - Recording enable/disable control\n\n'); - - fprintf('6. ERROR HANDLING:\n'); - fprintf(' - Comprehensive validation of inputs\n'); - fprintf(' - Automatic crash recovery for behavior protocols\n'); - fprintf(' - Detailed logging with timestamps\n'); - fprintf(' - Graceful fallbacks for system failures\n\n'); - - fprintf('For more information, see function documentation within the code.\n'); - fprintf('================================================================\n\n'); - diff --git a/ExperPort/Modules/@NeuropixelNeuroblueprint/NeuropixelNeuroblueprint.m b/ExperPort/Modules/@NeuropixelNeuroblueprint/NeuropixelNeuroblueprint.m index 003173ba..eea1cc64 100644 --- a/ExperPort/Modules/@NeuropixelNeuroblueprint/NeuropixelNeuroblueprint.m +++ b/ExperPort/Modules/@NeuropixelNeuroblueprint/NeuropixelNeuroblueprint.m @@ -109,17 +109,23 @@ uicontrol(handles.software_group, 'Style', 'radiobutton', 'String', 'SpikeGLX', 'Units', 'normalized', 'Position', [0.5, 0.3, 0.4, 0.4], 'Tag', 'SpikeGLX', 'FontSize', 10); % Panel 1: Behavior - p1 = uipanel('Title', '1. Behavior', 'FontSize', 12, 'FontWeight', 'bold', 'BorderType', 'etchedin', 'BorderWidth', 1, 'Units', 'normalized', 'Position', [0.02, 0.74, 0.6, 0.13]); + p1 = uipanel('Title', '1. Behavior', 'FontSize', 12, 'FontWeight', 'bold', 'BorderType', 'etchedin', 'BorderWidth', 1, 'Units', 'normalized', 'Position', [0.02, 0.74, 0.6, 0.13]); uicontrol(p1, 'Style', 'text', 'String', 'Protocol Name:', 'Units', 'normalized', 'Position', [0.05, 0.7, 0.22, 0.25], 'HorizontalAlignment', 'right'); handles.protocol_edit = uicontrol(p1, 'Style', 'edit', 'String', 'ArpitSoundCatContinuous', 'Units', 'normalized', 'Position', [0.3, 0.7, 0.45, 0.25]); handles.manual_test = uicontrol(p1, 'Style', 'checkbox', 'String', 'Manual Test', 'Value', 1, 'Units', 'normalized', 'Position', [0.78, 0.7, 0.2, 0.25]); - uicontrol(p1, 'Style', 'text', 'String', 'Experimenter:', 'Units', 'normalized', 'Position', [0.05, 0.4, 0.22, 0.25], 'HorizontalAlignment', 'right'); - handles.exp_edit = uicontrol(p1, 'Style', 'edit', 'String', 'lida', 'Units', 'normalized', 'Position', [0.3, 0.4, 0.65, 0.25], 'Callback', {@(h,e) feval(mfilename, obj, 'update_subject_id')}); - uicontrol(p1, 'Style', 'text', 'String', 'Rat Name:', 'Units', 'normalized', 'Position', [0.05, 0.1, 0.22, 0.25], 'HorizontalAlignment', 'right'); - handles.rat_name_edit = uicontrol(p1, 'Style', 'edit', 'String', 'LP12', 'Units', 'normalized', 'Position', [0.3, 0.1, 0.4, 0.25], 'Callback', {@(h,e) feval(mfilename, obj, 'update_subject_id')}); - uicontrol(p1, 'Style', 'text', 'String', 'Path:', 'Units', 'normalized', 'Position', [0.72, 0.1, 0.08, 0.25], 'HorizontalAlignment', 'right'); - handles.behav_edit = uicontrol(p1, 'Style', 'edit', 'String', 'C:\ratter', 'Units', 'normalized', 'Position', [0.81, 0.1, 0.18, 0.25]); - + uicontrol(p1, 'Style', 'text', 'String', 'Experimenter:', 'Units', 'normalized', 'Position', [0.02, 0.4, 0.2, 0.25], 'HorizontalAlignment', 'right'); + handles.exp_edit = uicontrol(p1, 'Style', 'edit', 'String', 'lida', 'Units', 'normalized', 'Position', [0.23, 0.4, 0.25, 0.25], 'Callback', {@(h,e) feval(mfilename, obj, 'update_subject_id')}); + uicontrol(p1, 'Style', 'text', 'String', 'Rat Name:', 'Units', 'normalized', 'Position', [0.50, 0.4, 0.18, 0.25], 'HorizontalAlignment', 'right'); + handles.rat_name_edit = uicontrol(p1, 'Style', 'edit', 'String', 'LP12', 'Units', 'normalized', 'Position', [0.69, 0.4, 0.30, 0.25], 'Callback', {@(h,e) feval(mfilename, obj, 'update_subject_id')}); + uicontrol(p1, 'Style', 'text', 'String', 'Distribution:', 'Units', 'normalized', 'Position', [0.02, 0.1, 0.18, 0.25], 'HorizontalAlignment', 'right'); + handles.distribution_popup = uicontrol(p1, 'Style', 'popupmenu', ... + 'String', {'random', 'Uniform', 'Hard A', 'Hard B'}, ... + 'Units', 'normalized', ... + 'Position', [0.21, 0.1, 0.35, 0.25]); + uicontrol(p1, 'Style', 'text', 'String', 'Path:', 'Units', 'normalized', 'Position', [0.58, 0.1, 0.1, 0.25], 'HorizontalAlignment', 'right'); + handles.behav_edit = uicontrol(p1, 'Style', 'edit', 'String', 'C:\ratter', 'Units', 'normalized', 'Position', [0.69, 0.1, 0.30, 0.25]);% + + % Panel 2: NeuroBlueprint Format p2 = uipanel('Title', '2. NeuroBlueprint Format', 'FontSize', 12, 'FontWeight', 'bold', 'BorderType', 'etchedin', 'BorderWidth', 1, 'Units', 'normalized', 'Position', [0.02, 0.42, 0.6, 0.31]); uicontrol(p2, 'Style', 'text', 'String', 'Project Name:', 'Units', 'normalized', 'Position', [0.01, 0.85, 0.28, 0.1], 'HorizontalAlignment', 'right'); @@ -317,7 +323,7 @@ else % SpikeGLX log_message(handles, 'Initializing SpikeGLX controller...'); controller = SpikeGL(params.sglx_host_ip, params.sglx_port); - if ~controller.IsConnected(), error('Failed to connect to SpikeGLX'); end + % if ~controller.IsConnected(), error('Failed to connect to SpikeGLX'); end end recording_controller.value = controller; log_message(handles, sprintf('%s controller initialized successfully.', software)); @@ -344,7 +350,8 @@ if strcmp(software, 'OpenEphys') controller.setRecordPath(params.oe_rec_node_id, save_path); else % SpikeGLX - controller.SetDataDir(save_path); + controller = SetDataDir( controller, 0, save_path); + recording_controller.value = controller; end log_message(handles, sprintf('Recording path set to: %s', save_path)); catch ME @@ -374,8 +381,22 @@ else % SpikeGLX log_message(handles, 'Starting SpikeGLX recording...'); run_name = sprintf('experiment_%s', datestr(now, 'yyyymmdd_HHMMSS')); - controller.SetRunName(run_name); - controller.StartRun(); + boolval = IsInitialized( controller); + if boolval + spikeglx_params = GetParams( controller ); + controller = SetRunName(controller,run_name); % setting run name + controller = StartRun(controller); % starting acquisition + pause(2); + + runningval = false; + while ~runningval % waiting for acquisition to start + runningval = IsRunning( controller ); + if runningval + controller = SetRecordingEnable( controller, 1 ); % Start Recording + end + end + recording_controller.value = controller; + end end log_message(handles, 'Electrophysiology recording is LIVE.'); @@ -399,7 +420,15 @@ controller.idle(); else % SpikeGLX log_message(handles, 'Stopping SpikeGLX recording...'); - controller.StopRun(); + boolval = IsSaving( controller ); + if boolval + controller = SetRecordingEnable( controller, 0 ); + pause(1); % Brief pause to ensure recording stops + controller = StopRun(controller); + else + controller = StopRun(controller); + end + recording_controller.value = controller; end log_message(handles, 'Electrophysiology recording stopped.'); @@ -482,7 +511,7 @@ video_save_dir = fullfile(params.local_path, value(session_base_path), 'behav'); try log_message(handles, 'Loading main behavioral protocol...'); - feval(mfilename, obj, 'behav_control', 'load_main_protocol', params.experimenter, params.rat_name, params.protocol_name, video_save_dir, params.behav_path); + feval(mfilename, obj, 'behav_control', 'load_main_protocol', params.experimenter, params.rat_name, params.protocol_name, video_save_dir, params.behav_path,params.stim_distribution); log_message(handles, 'Behavior system loaded and ready.'); log_message(handles, '--- LOAD sequence complete. Ready to run. ---'); currentState.value = 'Run'; @@ -546,7 +575,7 @@ switch sub_action case 'load_main_protocol' experimenter = args{1}; ratname = args{2}; protocol_name = args{3}; - video_save_dir = args{4}; behav_path = args{5}; + video_save_dir = args{4}; behav_path = args{5}; stim_distribution = args{6}; log_message(handles, ['Loading protocol: ' protocol_name]); dispatcher('set_protocol', protocol_name); rath = get_sphandle('name', 'ratname', 'owner', protocol_name); @@ -556,6 +585,7 @@ log_message(handles, ['Loading settings for ' ratname]); [~, sfile] = load_solouiparamvalues(ratname, 'experimenter', experimenter, 'owner', class(protobj), 'interactive', 0); feval(protocol_name, protobj, 'set_setting_params', ratname, experimenter, sfile, char(datetime('now')), video_save_dir); + feval(protocol_name, protobj, 'set_stim_distribution',stim_distribution); if ~dispatcher('is_running'), pop_history(class(protobj), 'include_non_gui', 1); feval(protocol_name, protobj, 'prepare_next_trial'); end case 'crashed' @@ -585,6 +615,7 @@ load_soloparamvalues(ratname, 'experimenter', experimenter, 'owner', protocol_name, 'interactive', 0,'data_file',fullfile(temp_data_dir,temp_data_file)); dispatcher('runstart_enable'); end + feval(protocol_name, protobj, 'psychometricUpdate_aftercrash'); % update parameters for psychometric plots which were not saved so cant be loaded if ~dispatcher('is_running'), pop_history(class(protobj), 'include_non_gui', 1); feval(protocol_name, protobj, 'prepare_next_trial'); end catch log_message(handles, ['Loading settings for ' ratname]); @@ -1029,6 +1060,8 @@ params.do_manual_test = get(handles.manual_test, 'Value'); params.experimenter = get(handles.exp_edit, 'String'); params.rat_name = get(handles.rat_name_edit, 'String'); + popup_string = get(handles.distribution_popup,'String'); + params.stim_distribution = popup_string{get(handles.distribution_popup,'Value')}; params.behav_path = get(handles.behav_edit, 'String'); params.project_name = get(handles.proj_edit, 'String'); params.subject_id = get(handles.sub_edit, 'String'); @@ -1151,13 +1184,13 @@ function log_message(handles,logStr) try if ~isfield(handles, 'log_box') || ~isvalid(handles.log_box), return; end current_text = get(handles.log_box, 'String'); - timestamp = datestr(now, '[HH:MM:SS] '); + timestamp = char(datetime('now', 'Format', '[HH:mm:ss] ')); new_line = [timestamp, logStr]; new_text = [current_text; {new_line}]; set(handles.log_box, 'String', new_text, 'Value', numel(new_text)); drawnow; catch - fprintf('%s: %s\n', datestr(now, '[HH:MM:SS]'), logStr); + fprintf('%s: %s\n', char(datetime('now', 'Format', '[HH:mm:ss] ')), logStr); end function toggle_button_color(~, ~, button_handle) @@ -1180,18 +1213,20 @@ function commit_to_svn(handles, file_path_data,file_path_settings, root_dir) return; end load(configFilePath, 'svn_user', 'svn_password'); - logmsg = char(strcat('automated commit from GUI for data and settings for ', {' '} ,fname_data,{'@'})); + logmsg_data = char(strcat('automated commit from GUI for data and settings for ', {' '} ,fname_data,{'@'})); % current_dir = cd; cd(pname_data); add_cmd_data = char(strcat('svn add', {' '}, fname_data, '.mat',{'@'})); - system(add_cmd_data); + system(add_cmd_data); + commit_cmd_data = sprintf('svn ci --username="%s" --password="%s" -m "%s"', svn_user, svn_password, logmsg_data); + [status, ~] = system(commit_cmd_data); cd(pname_settings); add_cmd_settings = char(strcat('svn add', {' '}, fname_settings, '.mat',{'@'})); system(add_cmd_settings); - - commit_cmd = sprintf('svn ci --username="%s" --password="%s" -m "%s"', svn_user, svn_password, logmsg); - [status, ~] = system(commit_cmd); + logmsg_setting = char(strcat('automated commit from GUI for data and settings for ', {' '} ,fname_settings,{'@'})); + commit_cmd_setting = sprintf('svn ci --username="%s" --password="%s" -m "%s"', svn_user, svn_password, logmsg_setting); + [status, ~] = system(commit_cmd_setting); if status == 0 log_message(handles, ['SVN commit successful for ' fname_data]); diff --git a/ExperPort/Modules/@OpenEphys_Neuroblueprint/OpenEphys_Neuroblueprint.m b/ExperPort/Modules/@OpenEphys_Neuroblueprint/OpenEphys_Neuroblueprint.m deleted file mode 100644 index fd2ae8b1..00000000 --- a/ExperPort/Modules/@OpenEphys_Neuroblueprint/OpenEphys_Neuroblueprint.m +++ /dev/null @@ -1,1097 +0,0 @@ -% -function [obj, varargout] = OpenEphys_Neuroblueprint_GUI(varargin) -% This is a class-based version of the GUI controller, structured similarly -% to runrats.m. It manages the experimental workflow through different -% 'actions' called via a switch statement. -% -% PRE-REQUISITES: -% 1. 'open-ephys-matlab-tools' must be in the MATLAB path. -% 2. The Bpod/ratter/ExperPort environment, including all its dependencies -% (dispatcher, bSettings, etc.), must be fully configured and in the MATLAB path. - -% --- Boilerplate for class definition and action handling --- -obj = class(struct, mfilename); -varargout = {}; % Initialize varargout for actions that return values -if nargin==0 || (nargin==1 && ischar(varargin{1}) && strcmp(varargin{1}, 'empty')) - return; -end - -if isa(varargin{1}, mfilename) - if length(varargin) < 2 || ~ischar(varargin{2}) - error(['If called with a "%s" object as first arg, a second arg, a ' ... - 'string specifying the action, is required\n']); - else action = varargin{2}; varargin = varargin(3:end); - end -else - action = varargin{1}; varargin = varargin(2:end); -end -if ~ischar(action), error('The action parameter must be a string'); end -GetSoloFunctionArgs(obj); -% --- End of boilerplate --- - - -switch action - % ========================================================================= - % CASE INIT - % ========================================================================= - case 'init' - % This case is called once to create the GUI and initialize all parameters. - - % So that only the CPU-based software renderer instead of your graphics card - % opengl software; - - % Start Bpod if not already running - if evalin('base', 'exist(''BpodSystem'', ''var'')') - if evalin('base', '~isempty(BpodSystem)'), newstartup; else, flush; end - else, Bpod('COM5');newstartup; - end - - % --- State Variables as SoloParamHandles --- - SoloParamHandle(obj, 'currentState', 'value', 'Load'); - SoloParamHandle(obj, 'behavState', 'value', 'Run'); % States for individual buttons - SoloParamHandle(obj, 'ephysState', 'value', 'Run'); - SoloParamHandle(obj, 'oe_controller', 'value', []); - SoloParamHandle(obj, 'behav_obj', 'value', []); - SoloParamHandle(obj, 'blinking_timer', 'value', []); - SoloParamHandle(obj, 'current_params', 'value', []); - SoloParamHandle(obj, 'session_base_path', 'value', ''); - SoloParamHandle(obj,'is_running','value',0); - SoloParamHandle(obj, 'probe_settings', 'value', struct('version', '2.0', 'reference', 'Tip', 'bank', 0, 'imro_path', '')); - SoloParamHandle(obj, 'probe_gui_handles', 'value', []); - - scr = timer; - set(scr,'Period', 0.2,'ExecutionMode','FixedRate','TasksToExecute',Inf,... - 'BusyMode','drop','TimerFcn',[mfilename,'(''End_Continued'')']); - SoloParamHandle(obj, 'stopping_complete_timer', 'value', scr); - - % --- Create the GUI Figure --- - SoloParamHandle(obj, 'myfig', 'saveable', 0); - myfig.value = figure('Name', 'Open Ephys & BControl Controller',... - 'NumberTitle', 'off', 'MenuBar', 'none', 'ToolBar', 'none',... - 'Units', 'normalized', 'Position', [0.1, 0.1, 0.6, 0.8],... - 'Color', [0.94, 0.94, 0.94], ... - 'CloseRequestFcn', {@(h,e) feval(mfilename, obj, 'close')}); - - % --- UI Creation --- - handles = struct(); - - % Log Panel - uipanel('Title', 'Activity Log', 'FontSize', 12, 'BorderType', 'etchedin', 'BorderWidth', 1, 'Units', 'normalized', 'Position', [0.64, 0.03, 0.34, 0.94]); - handles.log_box = uicontrol('Style', 'edit', 'Units', 'normalized', 'Position', [0.65, 0.05, 0.32, 0.89], 'String', {'Log started...'}, 'Max', 10, 'Min', 1, 'HorizontalAlignment', 'left', 'Enable', 'inactive', 'BackgroundColor', [1, 1, 1]); - - % Panel 1: Behavior - p1 = uipanel('Title', '1. Behavior', 'FontSize', 12, 'FontWeight', 'bold', 'BorderType', 'etchedin', 'BorderWidth', 1, 'Units', 'normalized', 'Position', [0.02, 0.82, 0.6, 0.16]); - uicontrol(p1, 'Style', 'text', 'String', 'Protocol Name:', 'Units', 'normalized', 'Position', [0.05, 0.75, 0.22, 0.18], 'HorizontalAlignment', 'right'); - handles.protocol_edit = uicontrol(p1, 'Style', 'edit', 'String', 'ArpitSoundCatContinuous', 'Units', 'normalized', 'Position', [0.3, 0.75, 0.5, 0.2]); - handles.manual_test = uicontrol(p1, 'Style', 'checkbox', 'String', 'Manual Test', 'Value', 1, 'Units', 'normalized', 'Position', [0.81, 0.75, 0.18, 0.2]); - uicontrol(p1, 'Style', 'text', 'String', 'Experimenter:', 'Units', 'normalized', 'Position', [0.05, 0.5, 0.22, 0.18], 'HorizontalAlignment', 'right'); - handles.exp_edit = uicontrol(p1, 'Style', 'edit', 'String', 'lida', 'Units', 'normalized', 'Position', [0.3, 0.5, 0.65, 0.2], 'Callback', {@(h,e) feval(mfilename, obj, 'update_subject_id')}); - uicontrol(p1, 'Style', 'text', 'String', 'Rat Name:', 'Units', 'normalized', 'Position', [0.05, 0.25, 0.22, 0.18], 'HorizontalAlignment', 'right'); - handles.rat_name_edit = uicontrol(p1, 'Style', 'edit', 'String', 'LP12', 'Units', 'normalized', 'Position', [0.3, 0.25, 0.65, 0.2], 'Callback', {@(h,e) feval(mfilename, obj, 'update_subject_id')}); - uicontrol(p1, 'Style', 'text', 'String', 'Behav Code Path:', 'Units', 'normalized', 'Position', [0.01, 0.01, 0.28, 0.18], 'HorizontalAlignment', 'right'); - handles.behav_edit = uicontrol(p1, 'Style', 'edit', 'String', 'C:\ratter', 'Units', 'normalized', 'Position', [0.3, 0.01, 0.5, 0.2]); - handles.behav_browse = uicontrol(p1, 'Style', 'pushbutton', 'String', 'Browse...', 'Units', 'normalized', 'Position', [0.81, 0.01, 0.16, 0.22], 'Callback', {@(h,e) feval(mfilename, obj, 'browse_path', 'behav')}); - - % Panel 2: NeuroBlueprint Format - p2 = uipanel('Title', '2. NeuroBlueprint Format', 'FontSize', 12, 'FontWeight', 'bold', 'BorderType', 'etchedin', 'BorderWidth', 1, 'Units', 'normalized', 'Position', [0.02, 0.42, 0.6, 0.38]); - uicontrol(p2, 'Style', 'text', 'String', 'Project Name (Root):', 'Units', 'normalized', 'Position', [0.01, 0.85, 0.28, 0.1], 'HorizontalAlignment', 'right'); - handles.proj_edit = uicontrol(p2, 'Style', 'edit', 'String', 'sound_cat_rat', 'Units', 'normalized', 'Position', [0.3, 0.85, 0.65, 0.12]); - uicontrol(p2, 'Style', 'text', 'String', 'Subject ID:', 'Units', 'normalized', 'Position', [0.01, 0.7, 0.28, 0.1], 'HorizontalAlignment', 'right'); - handles.sub_edit = uicontrol(p2, 'Style', 'edit', 'String', '000', 'Units', 'normalized', 'Position', [0.3, 0.7, 0.65, 0.12]); - uicontrol(p2, 'Style', 'text', 'String', 'Local Path:', 'Units', 'normalized', 'Position', [0.01, 0.55, 0.28, 0.1], 'HorizontalAlignment', 'right'); - handles.local_edit = uicontrol(p2, 'Style', 'edit', 'String', 'C:\Ephys_Experiment_Data', 'Units', 'normalized', 'Position', [0.3, 0.55, 0.5, 0.12]); - handles.local_browse = uicontrol(p2, 'Style', 'pushbutton', 'String', 'Browse...', 'Units', 'normalized', 'Position', [0.81, 0.55, 0.16, 0.13], 'Callback', {@(h,e) feval(mfilename, obj, 'browse_path', 'local')}); - uicontrol(p2, 'Style', 'text', 'String', 'Central Path (for checking only):', 'Units', 'normalized', 'Position', [0.01, 0.4, 0.28, 0.1], 'HorizontalAlignment', 'right'); - handles.central_edit = uicontrol(p2, 'Style', 'edit', 'String', 'Z:\_projects', 'Units', 'normalized', 'Position', [0.3, 0.4, 0.5, 0.12]); - handles.central_browse = uicontrol(p2, 'Style', 'pushbutton', 'String', 'Browse...', 'Units', 'normalized', 'Position', [0.81, 0.4, 0.16, 0.13], 'Callback', {@(h,e) feval(mfilename, obj, 'browse_path', 'central')}); - uicontrol(p2, 'Style', 'text', 'String', 'Subfolders to Create:', 'Units', 'normalized', 'Position', [0.05, 0.2, 0.9, 0.1], 'HorizontalAlignment', 'left'); - handles.cb_ephys = uicontrol(p2, 'Style', 'checkbox', 'String', 'ephys', 'Value', 1, 'Units', 'normalized', 'Position', [0.05, 0.05, 0.2, 0.15]); - handles.cb_behav = uicontrol(p2, 'Style', 'checkbox', 'String', 'behav', 'Value', 1, 'Units', 'normalized', 'Position', [0.28, 0.05, 0.2, 0.15]); - handles.cb_anat = uicontrol(p2, 'Style', 'checkbox', 'String', 'anat', 'Value', 1, 'Units', 'normalized', 'Position', [0.51, 0.05, 0.2, 0.15]); - handles.cb_funcimg = uicontrol(p2, 'Style', 'checkbox', 'String', 'funcimg', 'Value', 0, 'Units', 'normalized', 'Position', [0.74, 0.05, 0.25, 0.15]); - - % Panel 3: Pre-Experiment Sampling - p3 = uipanel('Title', '3. Pre/Post-Experiment Sampling', 'FontSize', 12, 'FontWeight', 'bold', 'BorderType', 'etchedin', 'BorderWidth', 1, 'Units', 'normalized', 'Position', [0.02, 0.28, 0.6, 0.12]); - uicontrol(p3, 'Style', 'text', 'String', 'Duration/Bank (s):', 'Units', 'normalized', 'Position', [0.01, 0.6, 0.25, 0.25], 'HorizontalAlignment', 'right'); - handles.sample_duration = uicontrol(p3, 'Style', 'edit', 'String', '60', 'Units', 'normalized', 'Position', [0.27, 0.6, 0.1, 0.3]); - handles.target_display = uicontrol(p3, 'Style', 'text', 'String', 'Target: Bank 0', 'Units', 'normalized', 'Position', [0.38, 0.6, 0.3, 0.25], 'HorizontalAlignment', 'right', 'FontWeight', 'bold'); - handles.probe_button = uicontrol(p3, 'Style', 'pushbutton', 'String', 'Probe Setting', 'Units', 'normalized', 'Position', [0.7, 0.55, 0.28, 0.4], 'FontSize', 10, 'Callback', {@(h,e) feval(mfilename, obj, 'open_probe_gui')}); - handles.sample_button = uicontrol(p3, 'Style', 'pushbutton', 'String', 'Start Sample Recording', 'Units', 'normalized', 'Position', [0.05, 0.1, 0.9, 0.4], 'FontSize', 12, 'FontWeight', 'bold', 'BackgroundColor', [0.8, 0.7, 1], 'Callback', {@(h,e) feval(mfilename, obj, 'sample_recording_wrapper')}); - - % Panel 4: OpenEphys Settings - p4 = uipanel('Title', '4. OpenEphys Settings', 'FontSize', 12, 'FontWeight', 'bold', 'BorderType', 'etchedin', 'BorderWidth', 1, 'Units', 'normalized', 'Position', [0.02, 0.15, 0.6, 0.11]); - uicontrol(p4, 'Style', 'text', 'String', 'GUI IP:', 'Units', 'normalized', 'Position', [0.01, 0.3, 0.15, 0.4], 'HorizontalAlignment', 'right'); - handles.ip_edit = uicontrol(p4, 'Style', 'edit', 'String', '127.0.0.1', 'Units', 'normalized', 'Position', [0.17, 0.25, 0.15, 0.5]); - uicontrol(p4, 'Style', 'text', 'String', 'Proc ID:', 'Units', 'normalized', 'Position', [0.33, 0.3, 0.15, 0.4], 'HorizontalAlignment', 'right'); - handles.proc_node_edit = uicontrol(p4, 'Style', 'edit', 'String', '100', 'Units', 'normalized', 'Position', [0.49, 0.25, 0.15, 0.5]); - uicontrol(p4, 'Style', 'text', 'String', 'Rec ID:', 'Units', 'normalized', 'Position', [0.65, 0.3, 0.15, 0.4], 'HorizontalAlignment', 'right'); - handles.rec_node_edit = uicontrol(p4, 'Style', 'edit', 'String', '101', 'Units', 'normalized', 'Position', [0.81, 0.25, 0.15, 0.5]); - - % --- Control Buttons Panel --- - p5 = uipanel('Title', 'Controls', 'FontSize', 12, 'FontWeight', 'bold', 'BorderType', 'etchedin', 'BorderWidth', 1, 'Units', 'normalized', 'Position', [0.02, 0.02, 0.6, 0.11]); - handles.control_button = uicontrol(p5, 'Style', 'pushbutton', 'String', 'Load', 'Units', 'normalized', 'FontSize', 14, 'FontWeight', 'bold', 'Position', [0.02, 0.1, 0.3, 0.8], 'BackgroundColor', [0.2, 0.6, 0.8], 'Callback', {@(h,e) feval(mfilename, obj, 'main_control_callback')}); - handles.behav_button = uicontrol(p5, 'Style', 'pushbutton', 'String', 'Run Behav', 'Units', 'normalized', 'FontSize', 12, 'Position', [0.35, 0.1, 0.3, 0.8], 'BackgroundColor', [1, 0.8, 0.6], 'Callback', {@(h,e) feval(mfilename, obj, 'behav_control_callback')}); - handles.ephys_button = uicontrol(p5, 'Style', 'pushbutton', 'String', 'Run Ephys', 'Units', 'normalized', 'FontSize', 12, 'Position', [0.68, 0.1, 0.3, 0.8], 'BackgroundColor', [0.8, 0.6, 1], 'Callback', {@(h,e) feval(mfilename, obj, 'ephys_control_callback')}); - - % Store the UI handles struct in an SPH for consistent access - SoloParamHandle(obj, 'ui_handles', 'value', handles); - - % Run initial subject ID check on startup - feval(mfilename, obj, 'update_subject_id'); - - % ========================================================================= - % CASE MAIN_CONTROL_CALLBACK - % ========================================================================= - case 'main_control_callback' - switch value(currentState) - case 'Load', feval(mfilename, obj, 'load_sequence'); - case 'Run', feval(mfilename, obj, 'run_sequence'); - case 'Stop', feval(mfilename, obj, 'stop_sequence'); - case 'PostExperiment', feval(mfilename, obj, 'reset_to_load_state'); - end - - % ========================================================================= - % WORKFLOW ACTIONS - % ========================================================================= - case 'load_sequence' - % handles = feval(mfilename, obj, 'get_ui_handles'); - handles = value(ui_handles); - log_message(handles, '--- LOAD sequence initiated ---'); - set(handles.control_button, 'Enable', 'off', 'String', 'Loading...'); - set(handles.sample_button, 'Enable', 'off'); - - params = get_all_params(handles); - if ~validate_inputs(handles, params) - feval(mfilename, obj, 'reset_to_load_state'); - return; - end - - current_params.value = params; - - - - [session_path, oe_save_path] = construct_paths(handles, params); - if isempty(session_path) || ~create_directories(handles, params, session_path) - feval(mfilename, obj, 'reset_to_load_state'); - return; - end - session_base_path.value = session_path; - - % if get(handles.cb_ephys, 'Value') - % try - % log_message(handles, ['Setting OE record path to: ' oe_save_path]); - % value(oe_controller).setRecordPath(params.rec_node_id, oe_save_path); - % log_message(handles, 'Successfully set OE record path.'); - % catch ME - % log_message(handles, ['ERROR setting OE path: ' ME.message]); - % getReport(ME, 'extended', 'hyperlinks', 'on'); - % errordlg('Could not set Open Ephys record path.', 'API Error'); - % feval(mfilename, obj, 'reset_to_load_state'); - % return; - % end - % end - - feval(mfilename, obj, 'initialize_behavior_system'); - - - case 'initialize_behavior_system' - params = value(current_params); - try - log_message(feval(mfilename, obj, 'get_ui_handles'), 'Initializing behavior control system...'); - behav_obj.value = dispatcher('init'); - h=get_sphandle('owner','dispatcher','name','myfig'); - set(value(h{1}), 'Visible','Off'); - - if params.do_manual_test - feval(mfilename, obj, 'behav_control', 'manual_test'); - else - feval(mfilename, obj, 'continue_load_after_manual_test'); - end - catch ME - log_message(feval(mfilename, obj, 'get_ui_handles'), ['FATAL ERROR initializing behavior system: ' ME.message]); - getReport(ME, 'extended', 'hyperlinks', 'on'); - errordlg(['Failed to initialize behavior system. Check path and logs. Error: ' ME.message], 'Behavior System Error'); - feval(mfilename, obj, 'reset_to_load_state'); - end - - case 'manual_test_stopping' - - handles = value(ui_handles); - log_message(handles, 'Manual rig test complete. Cleaning up...'); - dispatcher(value(behav_obj), 'Stop'); - - %Let's pause until we know dispatcher is done running - set(value(stopping_complete_timer), 'TimerFcn', {@(h,e) feval(mfilename, obj, 'manual_test_stopped')}); - start(value(stopping_complete_timer)); - - case 'manual_test_stopped' - - if value(stopping_process_completed) %This is provided by RunningSection - stop(value(stopping_complete_timer)); %Stop looping. - dispatcher('set_protocol', ''); - is_running.value = 0; - feval(mfilename, obj, 'continue_load_after_manual_test'); - end - - case 'continue_load_after_manual_test' - - params = value(current_params); - % handles = feval(mfilename, obj, 'get_ui_handles'); - handles = value(ui_handles); - - video_save_dir = fullfile(params.local_path, value(session_base_path), 'behav'); - try - log_message(handles, 'Loading main behavioral protocol...'); - feval(mfilename, obj, 'behav_control', 'load_main_protocol', params.experimenter, params.rat_name, params.protocol_name, video_save_dir, params.behav_path); - log_message(handles, 'Behavior system loaded and ready.'); - log_message(handles, '--- LOAD sequence complete. Ready to run. ---'); - currentState.value = 'Run'; - set(handles.control_button, 'Enable', 'on', 'String', 'Run', 'BackgroundColor', [0.4, 0.8, 0.4]); - catch ME - log_message(handles, ['FATAL ERROR loading main protocol: ' ME.message]); - getReport(ME, 'extended', 'hyperlinks', 'on'); - errordlg(['Failed to load main protocol. Error: ' ME.message], 'Behavior System Error'); - feval(mfilename, obj, 'reset_to_load_state'); - end - - case 'run_sequence' - params = value(current_params); - % handles = feval(mfilename, obj, 'get_ui_handles'); - handles = value(ui_handles); - log_message(handles, '--- RUN sequence initiated ---'); - set(handles.sample_button, 'Enable', 'off'); - - % --- EPHYS CONNECTION --- - if get(handles.cb_ephys, 'Value') - try - if isempty(value(oe_controller)) - log_message(handles, 'Connecting to Open Ephys GUI...'); - oe_controller.value = OpenEphysHTTPServer(params.gui_ip, 37497); - if isempty(value(oe_controller)), error('Failed to create OE controller.'); end - end - - log_message(handles, 'Applying probe settings for main experiment...'); - settings = value(probe_settings); - value(oe_controller).setParameters(params.proc_node_id, 0, 'Reference', settings.reference); - if ~isempty(settings.imro_path) - value(oe_controller).config(params.proc_node_id, ['LOADIMRO ' settings.imro_path]); - else - value(oe_controller).setParameters(params.proc_node_id, 0, 'bank', settings.bank); - end - - main_ephys_path = fullfile(params.local_path, value(session_base_path), 'ephys'); - log_message(handles, ['Confirming main record path: ' main_ephys_path]); - value(oe_controller).setRecordPath(params.rec_node_id, main_ephys_path); - pause(1); - log_message(handles, 'Starting OE acquisition and recording...'); - value(oe_controller).acquire(); - pause(1); - value(oe_controller).record(); - log_message(handles, 'Open Ephys recording is LIVE.'); - ephysState.value = 'Stop'; - set(handles.ephys_button, 'String', 'Stop Ephys', 'BackgroundColor', [1 0.6 0.6]); - - catch ME - log_message(handles, ['ERROR starting OE recording: ' ME.message]); - getReport(ME, 'extended', 'hyperlinks', 'on'); - errordlg('Failed to start Open Ephys recording. Check command window for details.', 'API Error'); - return; - end - else - log_message(handles, 'Ephys checkbox not selected. No OE recording started.'); - end - - currentState.value = 'Stop'; - set(handles.control_button, 'String', 'Stop'); - feval(mfilename, obj, 'start_blinking'); - - try - log_message(handles, 'Starting behavioral protocol...'); - feval(mfilename, obj, 'behav_control', 'run', params.protocol_name); - log_message(handles, 'Behavioral protocol is LIVE.'); - catch ME - log_message(handles, ['FATAL ERROR starting behavior protocol: ' ME.message]); - getReport(ME, 'extended', 'hyperlinks', 'on'); - errordlg(['Failed to start behavior protocol. Check logs. Error: ' ME.message], 'Behavior System Error'); - end - - case 'stop_sequence' - params = value(current_params); - % handles = feval(mfilename, obj, 'get_ui_handles'); - handles = value(ui_handles); - log_message(handles, '--- STOP sequence initiated ---'); - feval(mfilename, obj, 'stop_blinking'); - behav_save_dir = fullfile(params.local_path, value(session_base_path), 'behav'); - - try - log_message(handles, 'Ending behavioral session (saving data)...'); - feval(mfilename, obj, 'behav_control', 'end', params.protocol_name, params.behav_path,behav_save_dir); - log_message(handles, 'Behavioral data saved successfully.'); - catch ME - log_message(handles, ['FATAL ERROR ending behavioral session: ' ME.message]); - getReport(ME, 'extended', 'hyperlinks', 'on'); - errordlg(['Failed to save behavioral data. Check logs. Error: ' ME.message], 'Behavior System Error'); - end - - if get(handles.cb_ephys, 'Value') && ~isempty(value(oe_controller)) - try - log_message(handles, 'Stopping OE recording and acquisition...'); - value(oe_controller).idle(); - log_message(handles, 'Open Ephys recording stopped.'); - catch ME - log_message(handles, ['ERROR stopping OE recording: ' ME.message]); - getReport(ME, 'extended', 'hyperlinks', 'on'); - errordlg('Failed to stop Open Ephys recording.', 'API Error'); - end - end - - log_message(handles, '--- Experiment finished. Post-session sampling is now available. ---'); - - currentState.value = 'PostExperiment'; - set(handles.control_button, 'String', 'Start New Experiment', 'BackgroundColor', [0.2, 0.8, 0.6]); - set(handles.sample_button, 'Enable', 'on'); - - - % ========================================================================= - % INDIVIDUAL CONTROL CALLBACKS - % ========================================================================= - case 'behav_control_callback' - switch value(behavState) - case 'Run', feval(mfilename, obj, 'behav_control', 'load_run'); - case 'Stop', feval(mfilename, obj, 'behav_control', 'end'); - end - - case 'ephys_control_callback' - switch value(ephysState) - case 'Run', feval(mfilename, obj, 'run_ephys_only'); - case 'Stop', feval(mfilename, obj, 'stop_ephys_only'); - end - - case 'run_ephys_only' - params = value(current_params); - % handles = feval(mfilename, obj, 'get_ui_handles'); - handles = value(ui_handles); - log_message(handles, '--- Starting Ephys Recording Individually ---'); - set(handles.ephys_button, 'Enable', 'off'); - - try - if isempty(value(oe_controller)), oe_controller.value = OpenEphysHTTPServer(params.gui_ip, 37497); end - log_message(handles, 'Connected to Open Ephys GUI...'); - - % Apply probe settings - log_message(handles, 'Applying probe settings...'); - settings = value(probe_settings); - log_message(handles, ['- Setting Reference to: ' settings.reference]); - value(oe_controller).setParameters(params.proc_node_id, 0, 'Reference', settings.reference); - if ~isempty(settings.imro_path) - log_message(handles, ['- Loading IMRO file: ' settings.imro_path]); - config_string = ['LOADIMRO ' settings.imro_path]; - value(oe_controller).config(params.proc_node_id, config_string); - else - log_message(handles, ['- Setting Bank to: ' num2str(settings.bank)]); - value(oe_controller).setParameters(params.proc_node_id, 0, 'bank', settings.bank); - end - log_message(handles, 'Probe settings applied successfully.'); - - main_ephys_path = fullfile(params.local_path, value(session_base_path), 'ephys'); - - % Set Record Path and Start Recording - value(oe_controller).setRecordPath(params.rec_node_id, main_ephys_path); pause(1); - value(oe_controller).acquire(); pause(1); value(oe_controller).record(); - log_message(handles, 'Ephys recording is LIVE.'); - ephysState.value = 'Stop'; - set(handles.ephys_button, 'String', 'Stop Ephys', 'BackgroundColor', [1 0.6 0.6]); - catch ME - log_message(handles, ['ERROR starting ephys: ' ME.message]); - getReport(ME, 'extended', 'hyperlinks', 'on'); - end - set(handles.ephys_button, 'Enable', 'on'); - - case 'stop_ephys_only' - % handles = feval(mfilename, obj, 'get_ui_handles'); - handles = value(ui_handles); - log_message(handles, '--- Stopping Ephys Recording Individually ---'); - set(handles.ephys_button, 'Enable', 'off'); - try - if ~isempty(value(oe_controller)), value(oe_controller).idle(); end - log_message(handles, 'Ephys recording stopped.'); - ephysState.value = 'Run'; - set(handles.ephys_button, 'String', 'Run Ephys', 'BackgroundColor', [0.8, 0.6, 1]); - catch ME - log_message(handles, ['ERROR stopping ephys: ' ME.message]); - getReport(ME, 'extended', 'hyperlinks', 'on'); - end - set(handles.ephys_button, 'Enable', 'on'); - - % ========================================================================= - % BEHAVIOR CONTROL ACTIONS - % ========================================================================= - case 'behav_control' - sub_action = varargin{1}; - args = varargin(2:end); - % handles = feval(mfilename, obj, 'get_ui_handles'); - handles = value(ui_handles); - - switch sub_action - - case 'load_main_protocol' - experimenter = args{1}; ratname = args{2}; protocol_name = args{3}; - video_save_dir = args{4}; behav_path = args{5}; - log_message(handles, ['Loading protocol: ' protocol_name]); - dispatcher('set_protocol', protocol_name); - rath = get_sphandle('name', 'ratname', 'owner', protocol_name); - exph = get_sphandle('name', 'experimenter', 'owner', protocol_name); - rath{1}.value = ratname; exph{1}.value = experimenter; - protobj = eval(protocol_name); - log_message(handles, ['Loading settings for ' ratname]); - [~, sfile] = load_solouiparamvalues(ratname, 'experimenter', experimenter, 'owner', class(protobj), 'interactive', 0); - if ~dispatcher('is_running'), pop_history(class(protobj), 'include_non_gui', 1); feval(protocol_name, protobj, 'prepare_next_trial'); end - feval(protocol_name, protobj, 'set_setting_params', ratname, experimenter, sfile, char(datetime('now')), video_save_dir); - - case 'load_protocol_after_crash' - experimenter = args{1}; ratname = args{2}; protocol_name = args{3}; - video_save_dir = args{4}; behav_path = args{5}; - log_message(handles, ['Loading protocol: ' protocol_name]); - dispatcher('set_protocol', protocol_name); - rath = get_sphandle('name', 'ratname', 'owner', protocol_name); - exph = get_sphandle('name', 'experimenter', 'owner', protocol_name); - rath{1}.value = ratname; exph{1}.value = experimenter; - protobj = eval(protocol_name); - - % Loading the temporary saved Data file - try - log_message(handles, ['Loading previous data for ' ratname]); - today_date = char(datetime('now','format','yyMMdd')); - temp_data_dir = fullfile(behav_path,'SoloData','Data',experimenter,ratname); - temp_data_file = sprintf('data_@%s_%s_%s_%s_ASV.mat',protocol_name,experimenter,ratname,today_date); - if isfile(fullfile(temp_data_dir,temp_data_file)) - dispatcher('runstart_disable'); - load_soloparamvalues(ratname, 'experimenter', experimenter,... - 'owner', protocol_name, 'interactive', 0,'data_file',fullfile(temp_data_dir,temp_data_file)); - dispatcher('runstart_enable'); - end - if ~dispatcher('is_running'), pop_history(class(protobj), 'include_non_gui', 1); feval(protocol_name, protobj, 'prepare_next_trial'); end - catch % else loading the saved setting file - log_message(handles, ['Loading settings for ' ratname]); - [~, sfile] = load_solouiparamvalues(ratname, 'experimenter', experimenter, 'owner', class(protobj), 'interactive', 0); - feval(protocol_name, protobj, 'set_setting_params', ratname, experimenter, sfile, char(datetime('now')), video_save_dir); - if ~dispatcher('is_running'), pop_history(class(protobj), 'include_non_gui', 1); feval(protocol_name, protobj, 'prepare_next_trial'); end - end - - case 'load_run' - set(handles.behav_button, 'Enable', 'off'); - log_message(handles, '--- STARTING BEHAV PROTOCOL ---'); - params = value(current_params); - video_save_dir = fullfile(params.local_path, value(session_base_path), 'behav'); - feval(mfilename, obj, 'behav_control', 'load_protocol_after_crash', params.experimenter, params.rat_name, params.protocol_name, video_save_dir, params.behav_path); - feval(mfilename, obj, 'behav_control', 'run', params.protocol_name); - log_message(handles, '--- START COMPLETE: Behavior protocol started ---'); - set(handles.behav_button, 'Enable', 'on'); - - case 'crashed' - log_message(handles, '--- BEHAVIOR CRASH RECOVERY INITIATED ---'); - params = value(current_params); - video_save_dir = fullfile(params.local_path, value(session_base_path), 'behav'); - feval(mfilename, obj, 'behav_control', 'load_protocol_after_crash', params.experimenter, params.rat_name, params.protocol_name, video_save_dir, params.behav_path); - feval(mfilename, obj, 'behav_control', 'run', params.protocol_name); - log_message(handles, '--- RECOVERY COMPLETE: Behavior protocol restarted ---'); - - case 'run' - protocol_name = args{1}; protobj = eval(protocol_name); - log_message(handles, 'Starting video recording via protocol...'); - feval(protocol_name, protobj, 'start_recording'); - log_message(handles, 'Starting dispatcher to run trials...'); - is_running.value = 1; - behavState.value = 'Stop'; - set(handles.behav_button, 'String', 'Stop Behav', 'BackgroundColor', [1 0.6 0.6]); - dispatcher(value(behav_obj), 'Run'); - - - case 'end' - set(handles.behav_button, 'Enable', 'off'); - protocol_name = args{1}; root_dir = args{2}; behav_copy_dir = args{3}; - log_message(handles, 'Stopping dispatcher...'); - dispatcher(value(behav_obj), 'Stop'); - - %Let's pause until we know dispatcher is done running - set(value(stopping_complete_timer), 'Period', 0.8,'TimerFcn', {@(h,e) feval(mfilename, obj, 'behav_control','end_continued',protocol_name, root_dir,behav_copy_dir)}); - start(value(stopping_complete_timer)); - - case 'end_continued' - %% end_continued - if value(stopping_process_completed) %This is provided by RunningSection - protocol_name = args{1}; root_dir = args{2}; destination_path = args{3}; - stop(value(stopping_complete_timer)); %Stop looping. - is_running.value = 0; - feval(mfilename, obj, 'behav_control', 'send_empty_state_machine'); - protobj = eval(protocol_name); - log_message(handles, 'Ending session via protocol...'); - feval(protocol_name, protobj, 'end_session'); - log_message(handles, 'Saving data and settings...'); - data_file = SavingSection(protobj, 'savedata', 'interactive', 0); - try - feval(protocol_name, protobj, 'pre_saving_settings'); - catch - log_message(handles, 'Protocol does not have a pre_saving_settings section.'); - end - [settings_file, ~] = SavingSection(protobj, 'get_set_filename'); - SavingSection(protobj, 'savesets', 'interactive', 0); - log_message(handles, 'Committing data and settings to SVN...'); - commit_to_svn(handles, data_file, settings_file, root_dir); - dispatcher('set_protocol', ''); - - data_file = [data_file '.mat']; - % Copy the data file to the folder saving ephys data - [status, msg] = copyfile(data_file, destination_path); - % Check if the copy was successful - if status - log_message(handles,'Data File copied successfully.'); - else - log_message(handles,['Error copying Data file: ' msg]); - end - - behavState.value = 'Run'; - set(handles.behav_button, 'String', 'Run Behav', 'BackgroundColor', [1, 0.8, 0.6]); - - % Saving the Log file - feval(mfilename, obj, 'save_log_file'); % Save the log file - set(handles.behav_button, 'Enable', 'on'); - end - - case 'create_svn_data_dir' - experimenter = args{1}; ratname = args{2}; behav_dir = args{3}; dir_name = args{4}; - dirCurrent = cd; - settings_path = fullfile(behav_dir, 'SoloData', dir_name); - exp_path = fullfile(settings_path, experimenter); - rat_path = fullfile(exp_path, ratname); - if ~isfolder(settings_path), mkdir(settings_path); system(['svn add ' dir_name]); end - if ~isfolder(exp_path), cd(settings_path); mkdir(experimenter); system(['svn add ' experimenter]); end - if ~isfolder(rat_path), cd(exp_path); mkdir(ratname); system(['svn add ' ratname]); end - cd(dirCurrent); - log_message(handles, ['Created SVN directory structure for ' ratname]); - - case 'send_empty_state_machine' - state_machine_server = bSettings('get', 'RIGS', 'state_machine_server'); - server_slot = bSettings('get', 'RIGS', 'server_slot'); if isnan(server_slot), server_slot = 0; end - card_slot = bSettings('get', 'RIGS', 'card_slot'); if isnan(card_slot), card_slot = 0; end - sm = BpodSM(state_machine_server, 3333, server_slot); sm = Initialize(sm); - [inL, outL] = MachinesSection(dispatcher, 'determine_io_maps'); - sma = StateMachineAssembler('full_trial_structure'); - sma = add_state(sma, 'name', 'vapid_state_in_vapid_matrix'); - send(sma, sm, 'run_trial_asap', 0, 'input_lines', inL, 'dout_lines', outL, 'sound_card_slot', int2str(card_slot)); - - case 'manual_test' - log_message(handles, 'Loading manual rig test protocol...'); - dispatcher('set_protocol', 'Rigtest_singletrial'); - %Hide protocol window. - h=get_sphandle('owner','Rigtest_singletrial','name', 'myfig'); - for i=1:numel(h); set(value(h{i}),'Visible','Off'); end - - is_running.value = 1; - log_message(handles, 'Starting manual rig test. Please complete the one-trial test.'); - dispatcher(value(behav_obj), 'Run'); - end - - case 'browse_path' - type = varargin{1}; - % handles = feval(mfilename, obj, 'get_ui_handles'); - handles = value(ui_handles); - log_message(handles, ['Opening browse dialog for ' type ' path...']); - folder_path = uigetdir; - if folder_path ~= 0 - if strcmp(type, 'local'), set(handles.local_edit, 'String', folder_path); - elseif strcmp(type, 'central'), set(handles.central_edit, 'String', folder_path); - elseif strcmp(type, 'behav'), set(handles.behav_edit, 'String', folder_path); - end - log_message(handles, [type ' path set.']); - else, log_message(handles, 'Path selection cancelled.'); end - - case 'is_running' - - if exist('is_running','var') == 1 - obj = logical(value(is_running)); - else - obj = 0; - end - - % ========================================================================= - % OTHER ACTIONS (CLOSE, RESET, etc.) - % ========================================================================= - case 'reset_to_load_state' - % handles = feval(mfilename, obj, 'get_ui_handles'); - handles = value(ui_handles); - currentState.value = 'Load'; - behavState.value = 'Run'; - ephysState.value = 'Run'; - set(handles.control_button, 'Enable', 'on', 'String', 'Load', 'BackgroundColor', [0.2, 0.6, 0.8]); - oe_controller.value = []; - behav_obj.value = []; - current_params.value = []; - - case 'close' - try - feval(mfilename, obj, 'stop_blinking'); - if ishandle(value(myfig)), delete(value(myfig)); end - delete_sphandle('owner', ['^@' mfilename '$']); - dispatcher(value(behav_obj),'close'); - obj = []; - catch - if ishandle(value(myfig)), delete(value(myfig)); end - delete_sphandle('owner', ['^@' mfilename '$']); - obj = []; - end - - case 'get_ui_handles' - varargout{1} = value(ui_handles); - - case 'start_blinking' - % handles = feval(mfilename, obj, 'get_ui_handles'); - handles = value(ui_handles); - blinking_timer.value = timer('ExecutionMode', 'fixedRate', 'Period', 0.5, 'TimerFcn', {@toggle_button_color, handles.control_button}); - start(value(blinking_timer)); - - case 'stop_blinking' - % handles = feval(mfilename, obj, 'get_ui_handles'); - handles = value(ui_handles); - if ~isempty(value(blinking_timer)) && isvalid(value(blinking_timer)) - stop(value(blinking_timer)); - delete(value(blinking_timer)); - blinking_timer.value = []; - end - set(handles.control_button, 'BackgroundColor', [1, 0.4, 0.4]); - - - case 'crash_detected' - % handles = feval(mfilename, obj, 'get_ui_handles'); - handles = value(ui_handles); - if ~strcmp(value(currentState), 'Stop') || isempty(value(behav_obj)), return; end - - log_message(handles, '!!! CRASH DETECTED: Behavior system is not running. Attempting recovery...'); - try - feval(mfilename, obj, 'behav_control', 'crashed'); - - catch ME - log_message(handles, sprintf('FATAL: Recovery attempt failed: %s', ME.message)); - getReport(ME, 'extended', 'hyperlinks', 'on'); - errordlg('Automatic recovery failed. Please stop the experiment manually.', 'Recovery Failed'); - end - - - %% SAMPLE EPHYS RECORDINGS - case 'sample_recording_wrapper' - % This wrapper determines if we are doing a pre- or post-session sample - if strcmp(value(currentState), 'PostExperiment') - feval(mfilename, obj, 'sample_recording', 'post_session'); - else - feval(mfilename, obj, 'sample_recording', 'pre_session'); - end - - - case 'sample_recording' - prefix = varargin{1}; % 'pre_session' or 'post_session' - %handles = feval(mfilename, obj, 'get_ui_handles'); - handles = value(ui_handles); - log_message(handles, ['--- ' upper(prefix) ' SAMPLE RECORDING INITIATED ---']); - set([handles.sample_button, handles.control_button], 'Enable', 'off', 'String', 'Sampling...'); - drawnow; - - params = get_all_params(handles); - - % Ensure we have an OE connection - if isempty(value(oe_controller)) - try - log_message(handles, 'Connecting to Open Ephys GUI for sampling...'); - oe_controller.value = OpenEphysHTTPServer(params.gui_ip, 37497); - if isempty(value(oe_controller)), error('Failed to connect.'); end - catch ME - log_message(handles, ['ERROR: Could not connect to OE: ' ME.message]); - errordlg('Could not connect to Open Ephys for sampling.', 'Connection Error'); - feval(mfilename, obj, 'reset_to_load_state'); - return; - end - end - - % Create directories if they don't exist yet - if isempty(value(session_base_path)) - [session_path, ~] = construct_paths(handles, params); - if isempty(session_path) || ~create_directories(handles, params, session_path) - feval(mfilename, obj, 'reset_to_load_state'); - return; - end - session_base_path.value = session_path; - end - - sample_dir_name = [prefix '_sample_recording']; - sample_save_path = fullfile(params.local_path, value(session_base_path), 'ephys', sample_dir_name); - if ~exist(sample_save_path, 'dir'), mkdir(sample_save_path); end - - duration = str2double(get(handles.sample_duration, 'String')); - settings = value(probe_settings); - - try - log_message(handles, ['- Setting Reference to: ' settings.reference]); - value(oe_controller).setParameters(params.proc_node_id, 0, 'Reference', settings.reference); - - if strcmp(settings.version, '1.0'), num_banks = 3; else, num_banks = 4; end - - for bank = 0:(num_banks - 1) - log_message(handles, sprintf('Switching to Neuropixel Bank %d...', bank)); - value(oe_controller).setParameters(params.proc_node_id, 0, 'bank', bank); - pause(1); % Allow time for the setting to apply - log_message(handles, sprintf('Recording Bank %d for %d seconds...', bank, duration)); - value(oe_controller).setRecordPath(params.rec_node_id, sample_save_path); - pause(1); % Allow time for the setting to apply - value(oe_controller).acquire(duration); - pause(1); % Allow acquisition to stabilize - value(oe_controller).record(duration); - value(oe_controller).idle(); % Stop recording, but keep acquiring - log_message(handles, sprintf('Finished recording Bank %d.', bank)); - pause(1); - end - - log_message(handles, 'Restoring probe settings for main experiment...'); - if ~isempty(settings.imro_path) - config_string = ['LOADIMRO ' settings.imro_path]; - value(oe_controller).config(params.proc_node_id, config_string); - else - value(oe_controller).setParameters(params.proc_node_id, 0, 'bank', settings.bank); - end - - log_message(handles, '--- SAMPLE RECORDING COMPLETE ---'); - - catch ME - log_message(handles, ['ERROR during sample recording: ' ME.message]); - getReport(ME, 'extended', 'hyperlinks', 'on'); - errordlg('An error occurred during sample recording. Check command window.', 'Sampling Error'); - value(oe_controller).idle(); % Attempt to stop recording - end - - % Reset button states - if strcmp(value(currentState), 'PostExperiment') - set(handles.control_button, 'Enable', 'on', 'String', 'Start New Experiment'); - set(handles.sample_button, 'Enable', 'on', 'String', 'Start Sample Recording'); - - % Save/overwrite the log file after sampling is complete - feval(mfilename, obj, 'save_log_file'); - else - set(handles.control_button, 'Enable', 'on', 'String', 'Load'); - set(handles.sample_button, 'Enable', 'on', 'String', 'Start Sample Recording'); - end - - - - case 'open_probe_gui' - % This action opens the separate GUI for probe settings. - %handles = feval(mfilename, obj, 'get_ui_handles'); - handles = value(ui_handles); - log_message(handles, 'Opening probe settings GUI...'); - - probe_fig = figure('Name', 'Neuropixel Probe Settings', 'Position', [300 300 450 250], ... - 'MenuBar', 'none', 'ToolBar', 'none', 'NumberTitle', 'off', 'Resize', 'off'); - - p_handles = struct(); - - % Probe Version - p_handles.version_group = uibuttongroup(probe_fig, 'Title', 'Probe Version', 'Position', [0.05 0.7 0.9 0.25]); - uicontrol(p_handles.version_group, 'Style', 'radiobutton', 'String', 'NP 1.0 (3 Banks)', 'Position', [10 5 150 25], 'Tag', '1.0'); - uicontrol(p_handles.version_group, 'Style', 'radiobutton', 'String', 'NP 2.0 (4 Banks)', 'Position', [200 5 150 25], 'Tag', '2.0'); - - % Reference Selection - p_handles.ref_group = uibuttongroup(probe_fig, 'Title', 'Reference', 'Position', [0.05 0.4 0.4 0.25]); - uicontrol(p_handles.ref_group, 'Style', 'radiobutton', 'String', 'Tip', 'Position', [10 5 100 25], 'Tag', 'Tip'); - uicontrol(p_handles.ref_group, 'Style', 'radiobutton', 'String', 'External', 'Position', [100 5 100 25], 'Tag', 'External'); - - % Bank Selection Panel (can be disabled) - p_handles.bank_panel = uipanel(probe_fig, 'Title', 'Target Bank', 'Position', [0.5 0.4 0.45 0.25]); - uicontrol(p_handles.bank_panel, 'Style', 'text', 'String', 'Bank:', 'Position', [10 5 40 20]); - p_handles.bank_edit = uicontrol(p_handles.bank_panel, 'Style', 'edit', 'String', '0', 'Position', [60 5 50 25]); - - % IMRO File - uicontrol(probe_fig, 'Style', 'text', 'String', 'IMRO File:', 'Position', [20 80 60 20]); - p_handles.imro_text = uicontrol(probe_fig, 'Style', 'text', 'String', 'None selected', 'Position', [90 80 280 20], 'HorizontalAlignment', 'left'); - uicontrol(probe_fig, 'Style', 'pushbutton', 'String', 'Browse...', 'Position', [20 45 100 30], ... - 'Callback', {@(h,e) feval(mfilename, obj, 'browse_imro', p_handles)}); - uicontrol(probe_fig, 'Style', 'pushbutton', 'String', 'Clear IMRO', 'Position', [130 45 100 30], ... - 'Callback', {@(h,e) feval(mfilename, obj, 'clear_imro', p_handles)}); - - % Apply Button - uicontrol(probe_fig, 'Style', 'pushbutton', 'String', 'Apply & Close', 'Position', [250 15 180 30], ... - 'FontWeight', 'bold', 'Callback', {@(h,e) feval(mfilename, obj, 'apply_probe_settings', p_handles)}); - - probe_gui_handles.value = p_handles; - - case 'browse_imro' - p_handles = varargin{1}; - [file, path] = uigetfile('*.imro', 'Select IMRO File'); - if isequal(file, 0) || isequal(path, 0) - % User cancelled - return; - else - full_path = fullfile(path, file); - set(p_handles.imro_text, 'String', full_path); - end - - case 'clear_imro' - p_handles = varargin{1}; - set(p_handles.imro_text, 'String', 'None selected'); - % Re-enable bank selection - set(findobj(p_handles.bank_panel, '-property', 'Enable'), 'Enable', 'on'); - - - case 'apply_probe_settings' - p_handles = varargin{1}; - - %handles = feval(mfilename, obj, 'get_ui_handles'); - handles = value(ui_handles); - - % Get values from the probe GUI - settings.version = get(get(p_handles.version_group, 'SelectedObject'), 'Tag'); - settings.reference = get(get(p_handles.ref_group, 'SelectedObject'), 'Tag'); - settings.bank = str2double(get(p_handles.bank_edit, 'String')); - settings.imro_path = get(p_handles.imro_text, 'String'); - if strcmp(settings.imro_path, 'None selected'), settings.imro_path = ''; end - - % Save settings to the main GUI's SPH - probe_settings.value = settings; - - % Update the main GUI display - if ~isempty(settings.imro_path) - set(handles.target_display, 'String', 'Target: IMRO File'); - else - set(handles.target_display, 'String', ['Target: Bank ' num2str(settings.bank)]); - end - - log_message(handles, 'Probe settings saved.'); - - % Close the probe GUI - close(p_handles.ref_group.Parent); - probe_gui_handles.value = []; - - case 'update_subject_id' - % handles = feval(mfilename, obj, 'get_ui_handles'); - handles = value(ui_handles); - params = get_all_params(handles); - - % Don't run if essential fields are empty - if isempty(params.local_path) || isempty(params.central_path) || isempty(params.project_name) || isempty(params.rat_name) - return; - end - - max_local_id = find_max_subject_id(params.local_path, params.project_name, params.rat_name); - max_central_id = find_max_subject_id(params.central_path, params.project_name, params.rat_name); - - final_id = max(max_local_id, max_central_id); - - if final_id > 0 - log_message(handles, ['Found existing Subject ID: ' num2str(final_id) '. Populating field.']); - set(handles.sub_edit, 'String', sprintf('%03d', final_id)); % Format as 3 digits - else - log_message(handles, 'No existing Subject ID found for this rat. Please enter a new ID.'); - end - - case 'save_log_file' - - %handles = feval(mfilename, obj, 'get_ui_handles'); - handles = value(ui_handles); - params = value(current_params); - session_path = value(session_base_path); - - if isempty(session_path) - log_message(handles, 'WARNING: Cannot save log file. Session path not set.'); - return; - end - - log_path = fullfile(params.local_path, session_path, 'behav'); - if ~exist(log_path, 'dir') - log_message(handles, ['WARNING: Behavior folder not found. Cannot save log. Path: ' log_path]); - return; - end - - log_file_path = fullfile(log_path, 'session_log.txt'); - - try - log_content = get(handles.log_box, 'String'); - fid = fopen(log_file_path, 'w'); - if fid == -1 - error('Could not open file for writing.'); - end - for i = 1:length(log_content) - fprintf(fid, '%s\n', log_content{i}); - end - fclose(fid); - log_message(handles, ['Log file saved successfully to: ' log_file_path]); - catch ME - log_message(handles, ['ERROR: Could not save log file. Details: ' ME.message]); - end - -end % end of action - -return; - -% ========================================================================= -% SUB-FUNCTIONS -% ========================================================================= - -function commit_to_svn(handles, file_path_data,file_path_settings, root_dir) - -if isempty(file_path_data), return; end - if isempty(file_path_settings), return; end - [pname_data, fname_data, ext_data] = fileparts(file_path_data); - [pname_settings, fname_settings, ext_settings] = fileparts(file_path_settings); - - configFilePath = fullfile(root_dir,'PASSWORD_CONFIG-DO_NOT_VERSIONCONTROL.mat'); - if ~exist(configFilePath, 'file') - log_message(handles, ['SVN commit failed: Password config file not found at ' configFilePath]); - return; - end - load(configFilePath, 'svn_user', 'svn_password'); - logmsg = char(strcat('automated commit from GUI for data and settings for ', {' '} ,fname_data,{'@'})); - % current_dir = cd; - cd(pname_data); - add_cmd_data = char(strcat('svn add', {' '}, fname_data, '.mat',{'@'})); - system(add_cmd_data); - - cd(pname_settings); - add_cmd_settings = char(strcat('svn add', {' '}, fname_settings, '.mat',{'@'})); - system(add_cmd_settings); - - commit_cmd = sprintf('svn ci --username="%s" --password="%s" -m "%s"', svn_user, svn_password, logmsg); - [status, ~] = system(commit_cmd); - - if status == 0 - log_message(handles, ['SVN commit successful for ' fname_data]); - else - log_message(handles, ['SVN commit FAILED for ' fname_data '.']); - end - - cd(fullfile(root_dir,'ExperPort')); - - -function params = get_all_params(handles) - params.protocol_name = get(handles.protocol_edit, 'String'); - params.do_manual_test = get(handles.manual_test, 'Value'); - params.experimenter = get(handles.exp_edit, 'String'); - params.rat_name = get(handles.rat_name_edit, 'String'); - params.behav_path = get(handles.behav_edit, 'String'); - params.project_name = get(handles.proj_edit, 'String'); - params.subject_id = get(handles.sub_edit, 'String'); - params.local_path = get(handles.local_edit, 'String'); - params.central_path = get(handles.central_edit, 'String'); - params.gui_ip = get(handles.ip_edit, 'String'); - params.proc_node_id = get(handles.proc_node_edit, 'String'); - params.rec_node_id = get(handles.rec_node_edit, 'String'); - - -function log_message(handles, logStr) - if ~isfield(handles, 'log_box') || ~isvalid(handles.log_box), return; end - current_text = get(handles.log_box, 'String'); - timestamp = datestr(now, '[HH:MM:SS] '); - new_line = [timestamp, logStr]; - new_text = [current_text; {new_line}]; - set(handles.log_box, 'String', new_text, 'Value', numel(new_text)); - drawnow; - - -function is_valid = validate_inputs(handles, params) - is_valid = false; fields = fieldnames(params); - for i = 1:length(fields) - if strcmp(fields{i}, 'experimenter'), continue; end % Make experimenter optional - if strcmp(fields{i}, 'central_path') && isempty(params.(fields{i})), continue; end - if isempty(params.(fields{i})) - msg = ['Field "' strrep(fields{i}, '_', ' ') '" cannot be empty.']; - log_message(handles, ['ERROR: ' msg]); errordlg(msg, 'Input Error'); - return; - end - end - if ~get(handles.cb_ephys, 'Value') && ~get(handles.cb_behav, 'Value') && ~get(handles.cb_anat, 'Value') && ~get(handles.cb_funcimg, 'Value') - msg = 'At least one subfolder must be selected.'; - log_message(handles, ['ERROR: ' msg]); errordlg(msg, 'Input Error'); - return; - end - is_valid = true; - - -function [session_base, oe_path] = construct_paths(handles, params) - - if isempty(params.experimenter) - subject_name = sprintf('sub-%s_id-%s', params.subject_id, params.rat_name); - else - subject_name = sprintf('sub-%s_id-%s_expmtr-%s', params.subject_id, params.rat_name, params.experimenter); - end - subject_base_path = fullfile(params.project_name, 'rawdata', subject_name); - local_subject_dir = fullfile(params.local_path, subject_base_path); - central_subject_dir = fullfile(params.central_path, subject_base_path); - new_ses_num = max(find_max_session_number(local_subject_dir), find_max_session_number(central_subject_dir)) + 1; - log_message(handles, sprintf('Last session found: %d. Creating new session: %d.', new_ses_num - 1, new_ses_num)); - session_datetime_str = char(datetime('now', 'Format', 'yyyyMMdd''T''HHmmss')); - session_folder_name = sprintf('ses-%02d_date-%s_dtype-ephys', new_ses_num, session_datetime_str); - session_base = fullfile(subject_base_path, session_folder_name); - oe_path = fullfile(params.local_path, session_base, 'ephys'); - log_message(handles, ['New session path determined: ' session_base]); - -function max_id = find_max_subject_id(base_path, project, rat) - max_id = 0; - search_path = fullfile(base_path, project, 'rawdata'); - if ~exist(search_path, 'dir'), return; end - - dir_contents = dir(search_path); - if isempty(dir_contents), return; end - - subject_ids = []; - pattern = sprintf('^sub-(\\d+)_id-%s', rat); % Match based on rat name only - - for i = 1:length(dir_contents) - if dir_contents(i).isdir - token = regexp(dir_contents(i).name, pattern, 'tokens'); - if ~isempty(token) - subject_ids(end+1) = str2double(token{1}{1}); - end - end - end - - if ~isempty(subject_ids) - max_id = max(subject_ids); - end - - -function max_ses = find_max_session_number(base_path) - max_ses = 0; if ~exist(base_path, 'dir'), return; end - dir_contents = dir(fullfile(base_path, 'ses-*')); - if isempty(dir_contents), return; end - session_numbers = []; - for i = 1:length(dir_contents) - if dir_contents(i).isdir - token = regexp(dir_contents(i).name, '^ses-(\d+)', 'tokens'); - if ~isempty(token), session_numbers(end+1) = str2double(token{1}{1}); end - end - end - if ~isempty(session_numbers), max_ses = max(session_numbers); end - -function success = create_directories(handles, params, session_base_path) - success = false; - subfolders.ephys = get(handles.cb_ephys, 'Value'); - subfolders.behav = get(handles.cb_behav, 'Value'); - subfolders.anat = get(handles.cb_anat, 'Value'); - subfolders.funcimg = get(handles.cb_funcimg, 'Value'); - selected_folders = fieldnames(subfolders)'; - selected_folders = selected_folders([subfolders.ephys, subfolders.behav, subfolders.anat, subfolders.funcimg] == 1); - try - for i = 1:length(selected_folders) - local_target_path = fullfile(params.local_path, session_base_path, selected_folders{i}); - log_message(handles, ['Creating local directory: ' local_target_path]); - if ~exist(local_target_path, 'dir'), mkdir(local_target_path); end - end - log_message(handles, 'All selected local directories created successfully.'); - success = true; - catch ME - msg = sprintf('Failed to create directories: %s', ME.message); - getReport(ME, 'extended', 'hyperlinks', 'on'); - log_message(handles, ['ERROR: ' msg]); errordlg(msg, 'Directory Error'); - end - - -function toggle_button_color(~, ~, button_handle) - if ~isvalid(button_handle), return; end - currentColor = get(button_handle, 'BackgroundColor'); - if isequal(currentColor, [1, 0.4, 0.4]), set(button_handle, 'BackgroundColor', [1, 0.7, 0.4]); - else, set(button_handle, 'BackgroundColor', [1, 0.4, 0.4]); end diff --git a/ExperPort/Modules/@dispatcher/RunningSection.m b/ExperPort/Modules/@dispatcher/RunningSection.m index 56735e1d..65db96a3 100644 --- a/ExperPort/Modules/@dispatcher/RunningSection.m +++ b/ExperPort/Modules/@dispatcher/RunningSection.m @@ -31,7 +31,6 @@ SoloParamHandle(obj, 'stop_after_next_update', 'value', 0); SoloParamHandle(obj, 'stopping_process_completed', 'value', 1); SoloFunctionAddVars('runrats','func_owner','@runrats','ro_args','stopping_process_completed'); % This is a bit unpleasant. We give ro access to the flag denoting the stop process as complete to runrats so that runrats can wait on it using a timer. :P - SoloFunctionAddVars('OpenEphys_Neuroblueprint','func_owner','@OpenEphys_Neuroblueprint','ro_args','stopping_process_completed'); % This is a bit unpleasant. We give ro access to the flag denoting the stop process as complete so that OpenEphys_Neuropblueprint can wait on it using a timer. :P SoloFunctionAddVars('NeuropixelNeuroblueprint','func_owner','@NeuropixelNeuroblueprint','ro_args','stopping_process_completed'); % This is a bit unpleasant. We give ro access to the flag denoting the stop process as complete so that NeuropixelNeuropblueprint can wait on it using a timer. :P SoloFunctionAddVars('TowerWaterDelivery','func_owner','@TowerWaterDelivery','ro_args','stopping_process_completed'); % This is a bit unpleasant. We give ro access to the flag denoting the stop process as complete to runrats so that runrats can wait on it using a timer. :P @@ -285,9 +284,6 @@ if runrats('is_running'); runrats('crashed',me); Running.value = 0; - elseif OpenEphys_Neuroblueprint('is_running') - OpenEphys_Neuroblueprint('crash_detected'); - Running.value = 0; elseif NeuropixelNeuroblueprint('is_running') NeuropixelNeuroblueprint('crash_detected'); Running.value = 0; diff --git a/Protocols/@ArpitSoundCatContinuous/ArpitSoundCatContinuous.m b/Protocols/@ArpitSoundCatContinuous/ArpitSoundCatContinuous.m index c7462c2d..b93c99f9 100644 --- a/Protocols/@ArpitSoundCatContinuous/ArpitSoundCatContinuous.m +++ b/Protocols/@ArpitSoundCatContinuous/ArpitSoundCatContinuous.m @@ -140,7 +140,7 @@ ArpitSoundCatContinuousSMA(obj, 'init'); - feval(mfilename, obj, 'prepare_next_trial'); + % feval(mfilename, obj, 'prepare_next_trial'); case 'change_water_modulation_params' display_guys = [1 150 300]; @@ -164,6 +164,22 @@ SavingSection(obj,'set_autosave_frequency',1); % saving setting every trial instead of 20 BonsaiCameraInterface(obj,'set_video_filepath',varargin{5}); + case 'set_stim_distribution' + + if strcmpi(varargin{1},'random') + rand_num = rand(1); + if rand_num > 0.5 + StimulusSection(obj,'Pushbutton_SwitchDistribution','Hard B'); + else + StimulusSection(obj,'Pushbutton_SwitchDistribution','Hard A'); + end + else + StimulusSection(obj,'Pushbutton_SwitchDistribution',varargin{1}); + end + + case 'psychometricUpdate_aftercrash' + + PsychometricSection(obj, 'reload_after_crash'); %% prepare next trial case 'prepare_next_trial' @@ -234,7 +250,7 @@ SessionDefinition(obj, 'run_eod_logic_without_saving'); % Sending Summary Statistics to SQL Database - % perf = PsychometricSection(obj, 'evaluate'); + %perf = PsychometricSection(obj, 'evaluate'); % SoundCatContextSwitchSummary(obj,'protocol_data',perf); diff --git a/Protocols/@ArpitSoundCatContinuous/PerformanceSection.m b/Protocols/@ArpitSoundCatContinuous/PerformanceSection.m index eb4dd15a..2dd7ee80 100644 --- a/Protocols/@ArpitSoundCatContinuous/PerformanceSection.m +++ b/Protocols/@ArpitSoundCatContinuous/PerformanceSection.m @@ -72,17 +72,22 @@ if n_done_trials > 1 - - ntrials.value = n_done_trials; - ntrials_valid.value = numel(find(~isnan(hit_history))); - violation_percent.value = numel(find(violation_history))/n_done_trials; - timeout_percent.value = numel(find(timeout_history))/n_done_trials; - goods = ~isnan(hit_history)'; - lefts = previous_sides(1:n_done_trials)=='l'; - rights = previous_sides(1:n_done_trials)=='r'; - Left_hit_frac.value = mean(hit_history(goods & lefts)); - Right_hit_frac.value = mean(hit_history(goods & rights)); - hit_frac.value = mean(hit_history(goods)); + try + ntrials.value = n_done_trials; + ntrials_valid.value = numel(find(~isnan(hit_history))); + violation_percent.value = numel(find(violation_history))/n_done_trials; + timeout_percent.value = numel(find(timeout_history))/n_done_trials; + goods = ~isnan(hit_history(1:n_done_trials)); + lefts = previous_sides(1:n_done_trials)=='l'; + rights = previous_sides(1:n_done_trials)=='r'; + goods = goods(:); + lefts = lefts(:); + rights = rights(:); + Left_hit_frac.value = mean(hit_history(goods & lefts)); + Right_hit_frac.value = mean(hit_history(goods & rights)); + hit_frac.value = mean(hit_history(goods)); + catch + end end if nargout > 0 diff --git a/Protocols/@ArpitSoundCatContinuous/PsychometricSection.asv b/Protocols/@ArpitSoundCatContinuous/PsychometricSection.asv deleted file mode 100644 index ca3b00e4..00000000 --- a/Protocols/@ArpitSoundCatContinuous/PsychometricSection.asv +++ /dev/null @@ -1,405 +0,0 @@ -function varargout = PsychometricSection(obj, action, varargin) - -GetSoloFunctionArgs(obj); - -switch action - - % ------------------------------------------------------------------ - % INIT - % ------------------------------------------------------------------ - - - case 'init' - if length(varargin) < 2 - error('Need at least two arguments, x and y position, to initialize %s', mfilename); - end - x = varargin{1}; y = varargin{2}; - - % Display on the main GUI - - % Context 1 - DispParam(obj, 'Context3_trialStart', 1,x,y, 'position', [x, y, 100 20],'label','t_start','TooltipString','3rd context started at this trial'); - DispParam(obj, 'Context3_trialEnd', 1,x,y, 'position', [x + 101, y, 100 20],'label','t_end','TooltipString','3rd context ended at this trial') - next_row(y); - DispParam(obj, 'Context3_Dist', Category_Dist, x,y,'label','Context3_Distr','TooltipString','stim distribution for 3rd context'); - make_invisible(Context3_Dist); make_invisible(Context3_trialStart);make_invisible(Context3_trialEnd); - next_row(y); - - % Context 2 - DispParam(obj, 'Context2_trialStart', 1,x,y, 'position', [x, y, 100 20],'label','t_start','TooltipString','2nd context started at this trial'); - DispParam(obj, 'Context2_trialEnd', 1,x,y, 'position', [x + 101, y, 100 20],'label','t_end','TooltipString','2nd context ended at this trial') - next_row(y); - DispParam(obj, 'Context2_Dist', Category_Dist, x,y,'label','Context2_Distr','TooltipString','stim distribution for 2nd context'); - make_invisible(Context2_Dist); make_invisible(Context2_trialStart);make_invisible(Context2_trialEnd); - next_row(y); - - % Context 3 - DispParam(obj, 'Context1_trialStart', 1,x,y, 'position', [x, y, 100 20],'label','Trial_start','TooltipString','1st context started at this trial'); - DispParam(obj, 'Context1_trialEnd', 1,x,y, 'position', [x + 101, y, 100 20],'label','Trial_end','TooltipString','1st context ended at this trial') - next_row(y); - DispParam(obj, 'Context1_Dist', Category_Dist, x,y,'label','Context1_Distr','TooltipString','stim distribution for 1st context'); - next_row(y); - - PushbuttonParam(obj, 'Switch_Distr', x, y, 'label', 'Change Stim Distribution','TooltipString', 'Change the context by switching distribution'); - set_callback(Switch_Distr, {mfilename, 'PushButton_Distribution_Switch'}); %#ok (Defined just above) - next_row(y,2) - NumeditParam(obj,'trial_plot',30,x,y,'label','Trails 2 Plot','TooltipString','update psychometric curve after these many valid trials'); - next_row(y); - ToggleParam(obj, 'PsychometricShow', 0, x, y, 'OnString', 'Psychometric Show', ... - 'OffString', 'Psychometric Hidden', 'TooltipString', 'Show/Hide Psychometric panel'); - set_callback(PsychometricShow, {mfilename, 'show_hide'}); %#ok (Defined just above) - next_row(y); - SubheaderParam(obj, 'title', 'Psychometric Section', x, y);next_row(y); - - oldx=x; oldy=y; parentfig=double(gcf); - - % --- State Management with SoloParamHandles --- - state.last_analyzed_valid_trial = 0; - state.block_count = 0; - state.blockStatsHistory = struct('indices', {}, 'hitRates', {}, 'stimCounts', {}); - SoloParamHandle(obj, 'states_value', 'value', state); - SoloParamHandle(obj, 'thiscontext', 'value', 1); - SoloParamHandle(obj, 'last_trial_plotted', 'value', 0); - - vars = ["Select","Rule","DistributionLeft","DistributionRight","Start_trial","End_trial","Slope","TrueBoundary",... - "CalBoundary","LapseA","LapseB","fit_Method", "Overall Hit %", "Left Hit %", "Right Hit %"]; - vars_type = ["logical","string","string","string","double","double","double","double","double",... - "double","double","string","double","double","double"]; - t = table('Size', [0, numel(vars)], 'VariableTypes', vars_type, 'VariableNames', vars); - - - % --- GUI Figure and Component Handles --- - - % Main analysis figure (web-based uifigure) - SoloParamHandle(obj, 'myfig', 'value', figure('closerequestfcn', [mfilename '(' class(obj) ', ''hide'');'],... - 'Name', 'Real-Time Analysis', 'Units', 'normalized','Position', [0.3, 0.1, 0.6, 0.8],'Visible', 'off'), 'saveable', false); - - % --- pre-Define Controls for the Bottom Panel later we will arrange it into panel. This is because if not done before it - % removes all the previous created axes panel. --- - - % Edit Boxes - NumeditParam(obj, 'Plot_Trial_Start', 1, 1, 1,'TooltipString', 'Start trial for custom plot'); - NumeditParam(obj, 'Plot_Trial_End', 30, 1, 1, 'TooltipString', 'End trial for custom plot'); - % Push Buttons - ToggleParam(obj, 'Show_Table_Toggle', 0, 1, 1, 'OnString', 'Table Shown', 'OffString', 'Show Table'); - PushbuttonParam(obj, 'Plot_Custom_Button', 1, 1, 'label', 'Plot Custom Range'); - PushbuttonParam(obj, 'Plot_Context_Button', 1, 1, 'label', 'Plot Context'); - - % --- Create Panels for Layout --- - % Top panel for live plots - hndl_live_plots_panel = uipanel('Parent', value(myfig), 'Title', 'Live Update Plots', ... - 'Units', 'normalized', 'Position', [0.05, 0.55, 0.9, 0.43]); - - % Middle panel for custom plots - hndl_custom_plots_panel = uipanel('Parent', value(myfig), 'Title', 'Custom Range Plots', ... - 'Units', 'normalized', 'Position', [0.05, 0.2, 0.9, 0.33]); - - % Bottom panel for controls - hndl_controls_panel = uipanel('Parent', value(myfig), 'Title', 'Controls', ... - 'Units', 'normalized', 'Position', [0.05, 0.02, 0.9, 0.16]); - - % --- Define the 6 Axes and store in a struct --- - axes_h.live_psych = axes('Parent', hndl_live_plots_panel, 'Units', 'normalized', 'Position', [0.06, 0.1, 0.28, 0.8]); - title(axes_h.live_psych, 'Live Psychometric'); ylabel(axes_h.live_psych, 'P(Right)'); - - axes_h.live_hitrate = axes('Parent', hndl_live_plots_panel, 'Units', 'normalized', 'Position', [0.38, 0.1, 0.28, 0.8]); - title(axes_h.live_hitrate, 'Live Hit Rate'); xlabel(axes_h.live_hitrate, 'Block'); - - axes_h.live_stim = axes('Parent', hndl_live_plots_panel, 'Units', 'normalized', 'Position', [0.7, 0.1, 0.28, 0.8]); - title(axes_h.live_stim, 'Live Stimulus Dist.'); - - axes_h.custom_psych = axes('Parent', hndl_custom_plots_panel, 'Units', 'normalized', 'Position', [0.06, 0.1, 0.28, 0.8]); - title(axes_h.custom_psych, 'Custom Psychometric'); ylabel(axes_h.custom_psych, 'P(Right)'); - - axes_h.custom_hitrate = axes('Parent', hndl_custom_plots_panel, 'Units', 'normalized', 'Position', [0.38, 0.1, 0.28, 0.8]); - title(axes_h.custom_hitrate, 'Custom Hit Rate'); - - axes_h.custom_stim = axes('Parent', hndl_custom_plots_panel, 'Units', 'normalized', 'Position', [0.7, 0.1, 0.28, 0.8]); - title(axes_h.custom_stim, 'Custom Stimulus Dist.'); - - % Store the entire struct of handles in a single SoloParamHandle - SoloParamHandle(obj, 'PlotAxes', 'value', axes_h, 'saveable', false); - - % --- Arrange Controls in the Bottom Panel --- - - % Left Column: Show Table Toggle - set(get_ghandle(Show_Table_Toggle), 'Parent', hndl_controls_panel, 'Units', 'normalized', 'Position', [0.03, 0.25, 0.18, 0.5]); - - % Center Column: Custom Trial Plotting - uicontrol('Parent', hndl_controls_panel, 'Style','text', 'String','Start Trial', 'Units','normalized', 'Position',[0.25, 0.7, 0.1, 0.2]); - set(get_ghandle(Plot_Trial_Start), 'Parent', hndl_controls_panel, 'Units', 'normalized', 'Position', [0.35, 0.65, 0.1, 0.3]); - delete(get_lhandle(Plot_Trial_Start)); % remove bcontrol label - uicontrol('Parent', hndl_controls_panel, 'Style','text', 'String','End Trial', 'Units','normalized', 'Position',[0.47, 0.7, 0.1, 0.2]); - set(get_ghandle(Plot_Trial_End), 'Parent', hndl_controls_panel, 'Units', 'normalized', 'Position', [0.57, 0.65, 0.1, 0.3]); - delete(get_lhandle(Plot_Trial_End)); % remove bcontrol label - set(get_ghandle(Plot_Custom_Button), 'Parent', hndl_controls_panel, 'Units', 'normalized', 'Position', [0.35, 0.15, 0.22, 0.4]); - - % Right Column: Context Plot Button - set(get_ghandle(Plot_Context_Button), 'Parent', hndl_controls_panel, 'Units', 'normalized', 'Position', [0.68, 0.25, 0.18, 0.5]); - - % Far Right Column: Live Update Checkboxes - SoloParamHandle(obj, 'Update_Psychometric', 'value', uicontrol('Parent', hndl_controls_panel, 'Style', 'checkbox', 'Units', 'normalized', 'String', 'Psych', 'Value', 1, 'Position', [0.9, 0.65, 0.08, 0.3])); - SoloParamHandle(obj, 'Update_HitRate', 'value', uicontrol('Parent', hndl_controls_panel, 'Style', 'checkbox', 'Units', 'normalized', 'String', 'HitRate', 'Value', 1, 'Position', [0.9, 0.35, 0.08, 0.3])); - SoloParamHandle(obj, 'Update_Stimulus', 'value', uicontrol('Parent', hndl_controls_panel, 'Style', 'checkbox', 'Units', 'normalized', 'String', 'Stim', 'Value', 1, 'Position', [0.9, 0.05, 0.08, 0.3])); - - % --- Set Callbacks --- - set_callback(Show_Table_Toggle, {mfilename, 'show_hide_table'}); - set_callback(Plot_Custom_Button, {mfilename, 'PushButton_SelectedTrial'}); - set_callback(Plot_Context_Button, {mfilename, 'PushButton_Context'}); % To be defined later - - % --- Separate, Initially Invisible Figure for the Table --- - - SoloParamHandle(obj, 'myfig_table', 'value', uifigure('closerequestfcn', [mfilename '(' class(obj) ', ''hide_table'');'],... - 'Name', 'Psychometric Summary Table', 'Units', 'normalized','Visible', 'off','Position', [0.1, 0.2, 0.4, 0.6]), 'saveable', false); - - SoloParamHandle(obj, 'uit', 'value', uitable(value(myfig_table), 'Data', t, ... - 'Units', 'normalized', 'Position', [0.02 0.02 0.96 0.96], ... - 'ColumnEditable', [true, false, false, false, false, false, false, false, false, false, false, false]), ... - 'saveable', false); - - - % Returning the x , y position for the main callback GUI - varargout{1} = oldx; - varargout{2} = oldy; - - - %% Calculate Parameters - case 'Calculate_Params' - - try - % figure out if psychometric,sides = zeros(size(previous_sides)); % change the sides from ascii 'l' or 'r' to 0 and 1 - sides(previous_sides == 114) = 1; - - data.hit_history = hit_history; - data.previous_sides = sides; - data.stim_history = stimulus_history; - data.full_rule_history = Rule; - data.full_dist_right = stimulus_right_distribution_history; - data.full_dist_left = stimulus_left_distribution_history; - - handles.ui_table = value(uit); - handles.main_fig = value(myfig); - handles.axes_h = value(PlotAxes); - - Stim_Params = StimulusSection(obj,'stim_params'); - config.trials_per_block = value(trial_plot); - config.true_mu = Stim_Params(2); - config.stimuli_range = [Stim_Params(1), Stim_Params(3)]; - config.debug = true; - - state = value(states_value); - - % psychometric, hit rate and stim correct/incorrect histogram to be plotted or not - psych_obj = value(Update_Psychometric); - flags.psych = logical(psych_obj.Value); - - hit_obj = value(Update_HitRate); - flags.hit = logical(hit_obj.Value); - - stim_obj = value(Update_Stimulus); - flags.stim = logical(stim_obj.Value); - - varargout{1} = data; - varargout{2} = handles; - varargout{3} = config; - varargout{4} = ; - varargout{5} = flags; - - catch - varargout{1} = []; - varargout{2} = []; - varargout{3} = []; - varargout{4} = []; - varargout{5} = []; - end - - - %% Other actions - - case 'PushButton_Distribution_Switch' - - % eval(sprintf('present_context_dist = value(Context%i_Dist)',value(thiscontext))); - eval(sprintf('present_context_start = value(Context%i_trialStart);',value(thiscontext))); - eval(sprintf('present_context_end = value(Context%i_trialEnd);',value(thiscontext))); - - if ~strcmpi(Category_Dist,'Uniform') && present_context_end > present_context_start - - if value(thiscontext) < 3 % & ~strcmpi(present_context_dist,Category_Dist) - thiscontext.value = value(thiscontext) + 1; - eval(sprintf('Context%i_Dist.value = Category_Dist;',value(thiscontext))); - eval(sprintf('Context%i_trialStart.value = n_done_trials + 1;',value(thiscontext))); - eval(sprintf('Context%i_trialEnd.value = n_done_trials + 1;',value(thiscontext))); - eval(sprintf('make_visible(Context%i_Dist);',value(thiscontext))); - eval(sprintf('make_visible(Context%i_trialStart);',value(thiscontext))); - eval(sprintf('make_visible(Context%i_trialEnd);',value(thiscontext))); - end - - if strcmpi(Category_Dist,'Hard A') - StimulusSection(obj,'Pushbutton_SwitchDistribution','Hard B'); - elseif strcmpi(Category_Dist,'Hard B') - StimulusSection(obj,'Pushbutton_SwitchDistribution','Hard A'); - end - - % Also update the table and make changes to plot - [state, data, handles, config, flags] = PsychometricSection(obj,'Calculate_Params'); - % Calling the function to update the table and plot - state = RealTimeAnalysis('context_switch', state, data, handles, config, flags); - states_value.value = state; - end - - case 'StimSection_Distribution_Switch' - - eval(sprintf('present_context_start = value(Context%i_trialStart);',value(thiscontext))); - eval(sprintf('present_context_end = value(Context%i_trialEnd);',value(thiscontext))); - - if present_context_end > present_context_start - if value(thiscontext) < 3 % & ~strcmpi(present_context_dist,Category_Dist) - thiscontext.value = value(thiscontext) + 1; - eval(sprintf('Context%i_Dist.value = Category_Dist;',value(thiscontext))); - eval(sprintf('Context%i_trialStart.value = n_done_trials + 1;',value(thiscontext))); - eval(sprintf('Context%i_trialEnd.value = n_done_trials + 1;',value(thiscontext))); - eval(sprintf('make_visible(Context%i_Dist);',value(thiscontext))); - eval(sprintf('make_visible(Context%i_trialStart);',value(thiscontext))); - eval(sprintf('make_visible(Context%i_trialEnd);',value(thiscontext))); - - % Firstly make sure that at least 20 trials happened in this - % context if so then update the table and make changes to plot - if present_context_end - present_context_start >= 20 - [state, data, handles, config, flags] = PsychometricSection(obj,'Calculate_Params'); - % Calling the function to update the table and plot - state = RealTimeAnalysis('context_switch', state, data, handles, config, flags); - states_value.value = state; - end - end - else - eval(sprintf('Context%i_Dist.value = Category_Dist;',value(thiscontext))); - end - - case 'PushButton_SelectedTrial' - [state, data, handles, config, flags] = PsychometricSection(obj,'Calculate_Params'); - % Calling the function to update the table and plot - state = RealTimeAnalysis('custom', state, data, handles, config, flags,value(Plot_Trial_Start),value(Plot_Trial_End)); - states_value.value = state; - - case 'PushButton_Context' - [state, data, handles, config, flags] = PsychometricSection(obj,'Calculate_Params'); - % create a cell array containing the start and end of each context - context_trials = cell(1,value(thiscontext)); - for n_plot = 1:value(thiscontext) - eval(sprintf('trial_start = value(Context%i_trialStart);',n_plot)); - eval(sprintf('trial_end = value(Context%i_trialEnd);',n_plot)); - context_trials{1,n_plot} = [trial_start, trial_end]; - end - % Calling the function to update the table and plot - state = RealTimeAnalysis('context', state, data, handles, config, flags, context_trials); - states_value.value = state; - - %% update after each trial - case 'update' - % update the trial end for this context - if n_done_trials > 1 - eval(sprintf('Context%i_trialEnd.value = n_done_trials;',value(thiscontext))); - [state, data, handles, config, flags] = PsychometricSection(obj,'Calculate_Params'); - % Calling the function to update the table and plot - state = RealTimeAnalysis('live', state, data, handles, config, flags); - states_value.value = state; - end - - %% update the figure if user opened the figure window - case 'update_plot' - if n_done_trials > 30 - [state, data, handles, config, flags] = PsychometricSection(obj,'Calculate_Params'); - % Calling the function to update the table and plot - state = RealTimeAnalysis('redraw', state, data, handles, config, flags); - states_value.value = state; - end - - case 'evaluate' - if n_done_trials > 1 - try - % create a cell array containing the start and end of each context - context_trials = cell(1,value(thiscontext)); - psych_result = cell(1,value(thiscontext)); - - for n_context = 1:value(thiscontext) - eval(sprintf('trial_start = value(Context%i_trialStart);',n_context)); - eval(sprintf('trial_end = value(Context%i_trialEnd);',n_context)); - context_trials{1,n_context} = [trial_start, trial_end]; - - psych_result{1,n_context}.start_trial = trial_start; - psych_result{1,n_context}.end_trial = trial_end; - psych_result{1,n_context}.distribution_type = char(unique(category_distribution)); - psych_result{1,n_context}.calculated_boundary = nan; - psych_result{1,n_context}.total_hit_percent = -1; - psych_result{1,n_context}.total_violations_percent = -1; - psych_result{1,n_context}.right_correct_percent = -1; - psych_result{1,n_context}.left_correct_percent = -1; - end - - [state, data, handles, config, flags] = PsychometricSection(obj,'Calculate_Params'); - % Calling the function to update the table and plot - psych_result = RealTimeAnalysis('evaluate', state, data, handles, config, flags, context_trials); - - catch - fprintf(2, 'Error calculating correct pokes\n'); - disp(ME.message); - disp(ME.stack); - end - - end - - varargout{1} = psych_result; - - %% cases related to figure handles - - case 'close' - set(value(myfig), 'Visible', 'off'); - set(value(myfig_table), 'Visible', 'off'); - % Delete all SoloParamHandles who belong to this object and whose - % fullname starts with the name of this mfile: - if exist('myfig', 'var') && isa(myfig, 'SoloParamHandle') && ishandle(value(myfig)) %#ok - delete(value(myfig)); - end - if exist('myfig_table', 'var') && isa(myfig_table, 'SoloParamHandle') && ishandle(value(myfig_table)) %#ok - delete(value(myfig_table)); - end - delete_sphandle('owner', ['^@' class(obj) '$'], ... - 'fullname', ['^' mfilename]); - - case 'hide' - PsychometricShow.value = 0; - set(value(myfig), 'Visible', 'off'); - set(value(myfig_table), 'Visible', 'off'); - - %% Case Show_hide main psychometric GUI - case 'show_hide' - if PsychometricShow == 1 - set(value(myfig), 'Visible', 'on'); - - % Update the plot for live update - PsychometricSection(obj,'update_plot'); - - if Show_Table_Toggle == 1 - set(value(myfig_table), 'Visible', 'on'); - else - set(value(myfig_table), 'Visible', 'off'); - end - else - set(value(myfig), 'Visible', 'off'); - set(value(myfig_table), 'Visible', 'off'); - end - - %% case for table toggle - case 'show_hide_table' - if Show_Table_Toggle == 1 - set(value(myfig_table), 'Visible', 'on'); - else - set(value(myfig_table), 'Visible', 'off'); - end - - case 'hide_table' - Show_Table_Toggle.value = 0; - set(value(myfig_table), 'Visible', 'off'); -end - -end \ No newline at end of file diff --git a/Protocols/@ArpitSoundCatContinuous/PsychometricSection.m b/Protocols/@ArpitSoundCatContinuous/PsychometricSection.m index cf1a084c..3b4992fe 100644 --- a/Protocols/@ArpitSoundCatContinuous/PsychometricSection.m +++ b/Protocols/@ArpitSoundCatContinuous/PsychometricSection.m @@ -56,6 +56,8 @@ % --- State Management with SoloParamHandles --- state.last_analyzed_valid_trial = 0; state.block_count = 0; + state.context_blocks = 0; + state.table_row_editable = []; state.blockStatsHistory = struct('indices', {}, 'hitRates', {}, 'stimCounts', {}); SoloParamHandle(obj, 'states_value', 'value', state); SoloParamHandle(obj, 'thiscontext', 'value', 1); @@ -138,9 +140,9 @@ set(get_ghandle(Plot_Context_Button), 'Parent', hndl_controls_panel, 'Units', 'normalized', 'Position', [0.68, 0.25, 0.18, 0.5]); % Far Right Column: Live Update Checkboxes - SoloParamHandle(obj, 'Update_Psychometric', 'value', uicontrol('Parent', hndl_controls_panel, 'Style', 'checkbox', 'Units', 'normalized', 'String', 'Psych', 'Value', 1, 'Position', [0.9, 0.65, 0.08, 0.3])); - SoloParamHandle(obj, 'Update_HitRate', 'value', uicontrol('Parent', hndl_controls_panel, 'Style', 'checkbox', 'Units', 'normalized', 'String', 'HitRate', 'Value', 1, 'Position', [0.9, 0.35, 0.08, 0.3])); - SoloParamHandle(obj, 'Update_Stimulus', 'value', uicontrol('Parent', hndl_controls_panel, 'Style', 'checkbox', 'Units', 'normalized', 'String', 'Stim', 'Value', 1, 'Position', [0.9, 0.05, 0.08, 0.3])); + SoloParamHandle(obj, 'Update_Psychometric', 'value', uicontrol('Parent', hndl_controls_panel, 'Style', 'checkbox', 'Units', 'normalized', 'String', 'Psych', 'Value', 1, 'Position', [0.9, 0.65, 0.08, 0.3]),'saveable', false); + SoloParamHandle(obj, 'Update_HitRate', 'value', uicontrol('Parent', hndl_controls_panel, 'Style', 'checkbox', 'Units', 'normalized', 'String', 'HitRate', 'Value', 1, 'Position', [0.9, 0.35, 0.08, 0.3]),'saveable', false); + SoloParamHandle(obj, 'Update_Stimulus', 'value', uicontrol('Parent', hndl_controls_panel, 'Style', 'checkbox', 'Units', 'normalized', 'String', 'Stim', 'Value', 1, 'Position', [0.9, 0.05, 0.08, 0.3]),'saveable', false); % --- Set Callbacks --- set_callback(Show_Table_Toggle, {mfilename, 'show_hide_table'}); @@ -154,8 +156,9 @@ SoloParamHandle(obj, 'uit', 'value', uitable(value(myfig_table), 'Data', t, ... 'Units', 'normalized', 'Position', [0.02 0.02 0.96 0.96], ... - 'ColumnEditable', [true, false, false, false, false, false, false, false, false, false, false, false]), ... - 'saveable', false); + 'ColumnEditable', [true, false, false, false, false, false, false, false, false, false, false, false], ... + 'CellEditCallback',@(src, evt) PsychometricSection(obj, 'check_box_table', src, evt)), ... % end of uitable definition + 'saveable', true); % Returning the x , y position for the main callback GUI @@ -167,15 +170,16 @@ case 'Calculate_Params' try - % figure out if psychometric,sides = zeros(size(previous_sides)); % change the sides from ascii 'l' or 'r' to 0 and 1 + % figure out if psychometric, change the sides from ascii 'l' or 'r' to 0 and 1 + sides = zeros(size(previous_sides)); sides(previous_sides == 114) = 1; - data.hit_history = hit_history; - data.previous_sides = sides; - data.stim_history = stimulus_history; + data.hit_history = hit_history(1:n_done_trials); + data.previous_sides = sides(1:n_done_trials); + data.stim_history = stimulus_history(1:n_done_trials); data.full_rule_history = Rule; - data.full_dist_right = stimulus_right_distribution_history; - data.full_dist_left = stimulus_left_distribution_history; + data.full_dist_right = stimulus_right_distribution_history(1:n_done_trials); + data.full_dist_left = stimulus_left_distribution_history(1:n_done_trials); handles.ui_table = value(uit); handles.main_fig = value(myfig); @@ -189,6 +193,27 @@ state = value(states_value); + % make the table_row of same size as table rows + required_length = height(handles.ui_table.Data); % Get the required length from the table height + % Case 1: The field doesn't exist yet. Initialize it. + if ~isfield(state, 'table_row_editable') + % Create a column vector of 'false' values matching the table height. + state.table_row_editable = false(required_length, 1); + % Case 2: The field exists. Check if it needs resizing. + elseif numel(state.table_row_editable) ~= required_length + current_length = numel(state.table_row_editable); + if required_length > current_length + % The table is LONGER, so append 'false' values. + num_to_add = required_length - current_length; + state.table_row_editable = [state.table_row_editable(:); false(num_to_add, 1)]; + else + % The table is SHORTER, so truncate the vector. This is more readable. + state.table_row_editable = state.table_row_editable(1:required_length); + end + end + % Optional: Ensure it's a column vector for consistency + state.table_row_editable = state.table_row_editable(:); + % psychometric, hit rate and stim correct/incorrect histogram to be plotted or not psych_obj = value(Update_Psychometric); flags.psych = logical(psych_obj.Value); @@ -282,18 +307,37 @@ states_value.value = state; case 'PushButton_Context' - [state, data, handles, config, flags] = PsychometricSection(obj,'Calculate_Params'); + [state, data, handles, config, flags] = PsychometricSection(obj,'Calculate_Params'); % create a cell array containing the start and end of each context context_trials = cell(1,value(thiscontext)); + contexts_name = cell(1,value(thiscontext)); for n_plot = 1:value(thiscontext) eval(sprintf('trial_start = value(Context%i_trialStart);',n_plot)); eval(sprintf('trial_end = value(Context%i_trialEnd);',n_plot)); + eval(sprintf('context_name = value(Context%i_Dist);',n_plot)); context_trials{1,n_plot} = [trial_start, trial_end]; + contexts_name{1,n_plot} = context_name; end % Calling the function to update the table and plot - state = RealTimeAnalysis('context', state, data, handles, config, flags, context_trials); + state = RealTimeAnalysis('context', state, data, handles, config, flags, context_trials,contexts_name); states_value.value = state; + case 'reload_after_crash' + % make sure that the context values are visible + for n_contexts = 1:value(thiscontext) + eval(sprintf('make_visible(Context%i_Dist);',n_contexts)); + eval(sprintf('make_visible(Context%i_trialStart);',n_contexts)); + eval(sprintf('make_visible(Context%i_trialEnd);',n_contexts)); + end + % set the fig value for uitable as we didn't save the table + ui_table_handle = value(uit); + table_fig = value(myfig_table); + ui_table_handle.Parent = table_fig; + if isempty(ui_table_handle.CellEditCallback) + ui_table_handle.CellEditCallback = @(src, evt) PsychometricSection(obj, 'check_box_table', src, evt); + end + uit.value = ui_table_handle; + %% update after each trial case 'update' % update the trial end for this context @@ -307,6 +351,9 @@ %% update the figure if user opened the figure window case 'update_plot' + % if n_done_trials > 1 + % PsychometricSection(obj, 'reload_after_crash'); + % end if n_done_trials > 30 [state, data, handles, config, flags] = PsychometricSection(obj,'Calculate_Params'); % Calling the function to update the table and plot @@ -352,6 +399,35 @@ %% cases related to figure handles + case 'check_box_table' + try + if numel(varargin) < 2 + warning('check_box_table action called without event data.'); + return; + end + source = varargin{1}; + event = varargin{2}; + editedRow = event.Indices(1); + editedCol = event.Indices(2); + % Rule 1: Only act on our "Select" column (column 1) + if editedCol ~= 1, return; end + % Rule 2: Only act when the user tries to CHECK a box to TRUE + if ~event.NewData, return; end + % Rule 3: Check if the edited row is a "locked" row + state = value(states_value); + isRowEditable = state.table_row_editable; + if editedRow > numel(isRowEditable) + warning('Row editability status is out of sync with the table data.'); + return; + end + % If the row is NOT editable, then it's locked. + if ~isRowEditable(editedRow) + source.Data.Select(editedRow) = false; + % warndlg('This row is protected and cannot be selected.', 'Selection Blocked'); + end + catch + end + case 'close' set(value(myfig), 'Visible', 'off'); set(value(myfig_table), 'Visible', 'off'); diff --git a/Protocols/@ArpitSoundCatContinuous/SideSection.m b/Protocols/@ArpitSoundCatContinuous/SideSection.m index 6728b7a6..a819b8cd 100644 --- a/Protocols/@ArpitSoundCatContinuous/SideSection.m +++ b/Protocols/@ArpitSoundCatContinuous/SideSection.m @@ -379,22 +379,27 @@ end end - - violation_history.value=[violation_history(:); was_viol]; - timeout_history.value=[timeout_history(:); was_timeout]; + + vio_history = value(violation_history); + vio_history(n_done_trials) = was_viol; + violation_history.value= vio_history; + tout_history = value(timeout_history); + tout_history(n_done_trials) = was_timeout; + timeout_history.value= tout_history; SideSection(obj,'update_side_history'); + hit_his = value(hit_history); if ~was_viol && ~was_timeout %was_hit=rows(parsed_events.states.hit_state)>0; was_hit=rows(parsed_events.states.second_hit_state)==0; - hit_history.value=[hit_history(:); was_hit]; - + hit_his(n_done_trials) = was_hit; else % There was a violation or timeout - hit_history.value=[hit_history(:); nan]; + hit_his(n_done_trials) = nan; end - + hit_history.value = hit_his; + % % Now calculate the deltaF and sides - this maybe interesting % % even in a violation or timeout case. % diff --git a/Protocols/@ArpitSoundCatContinuous/StimulusSection.asv b/Protocols/@ArpitSoundCatContinuous/StimulusSection.asv deleted file mode 100644 index 17d9b72b..00000000 --- a/Protocols/@ArpitSoundCatContinuous/StimulusSection.asv +++ /dev/null @@ -1,813 +0,0 @@ - - -function varargout = StimulusSection(obj, action, varargin) - -GetSoloFunctionArgs(obj); - -switch action - - % ------------------------------------------------------------------ - % INIT - % ------------------------------------------------------------------ - - case 'init' - if length(varargin) < 2 - error('Need at least two arguments, x and y position, to initialize %s', mfilename); - end - x = varargin{1}; y = varargin{2}; - - ToggleParam(obj, 'StimulusShow', 0, x, y, 'OnString', 'Stimuli Show', ... - 'OffString', 'Stimuli Hidden', 'TooltipString', 'Show/Hide Stimulus panel'); - set_callback(StimulusShow, {mfilename, 'show_hide'}); %#ok (Defined just above) - next_row(y); - - oldx=x; oldy=y; parentfig=double(gcf); - - SoloParamHandle(obj, 'myfig', 'value', figure('closerequestfcn', [mfilename '(' class(obj) ', ''hide'');'],... - 'MenuBar', 'none', 'Name', mfilename, 'Units', 'normalized'), 'saveable', false); - SoundManagerSection(obj, 'declare_new_sound', 'StimAUD1') - SoloParamHandle(obj, 'thisstim', 'value', []); - SoloParamHandle(obj, 'thisstimlog', 'value', []); - - %% Formatting graphics elements - - %myfig - original_width = 0.45; - original_height = 0.75; - max_size = min(original_width, original_height); - aspect_ratio = original_width / original_height; - new_width = max_size; - new_height = new_width / aspect_ratio; - - center_x = 0.5 - (new_width / 2); - center_y = 0.5 - (new_height / 2); - - position_vector = [center_x center_y new_width new_height]; - - set(value(myfig), 'Units', 'normalized', 'Name', mfilename, 'Position', position_vector); - set(double(gcf), 'visible', 'off'); - - - x = 10; y=5; - next_row(y); - MenuParam(obj, 'Rule', {'S1>S_boundary Left','S1>S_boundary Right'}, ... - 'S1>S_boundary Left', x, y, 'labelfraction', 0.35, 'TooltipString', sprintf(['\nThis buttom determines the rule\n', ... - '\n''S1>S_boundary Left'' means if Aud1 > Aud_boundry then reward will be delivered from the left water spout and if Aud1 < Aud_boundry then water comes from right\n',... - '\n''S1>S_boundary Right'' means if Aud1 < Aud_boundry then reward will be delivered from the left water spout and if Aud1 > Aud_boundry then water comes from right\n'])); - - next_row(y, 1);next_row(y, 1); - NumeditParam(obj,'P_centre_region',0.25,x,y,'label','P_centre','TooltipString','probability for choosing stim near boundary. 0 = same probability as the rest, 1 = only choose from centre'); - next_row(y); - NumeditParam(obj,'centre_region_width',0.1,x,y,'label','Centre Width','TooltipString','total width around boundary to be considered as central region'); - next_row(y);next_row(y); - - MenuParam(obj, 'Prob_Dist_Left', {'Uniform','Exponential','Half Normal','Normal','Sinusoidal','Anti Exponential','Anti Half Normal','Anti Sinusoidal'}, ... - 'Uniform', x, y,'label','Left Dist', 'labelfraction', 0.35, 'TooltipString', sprintf(['\n Different Probability Distributions for Category A.\n', ... - '\n''Normal - the mean is at mid point of range. Half Normal - truncated normal with mean at boundary.\n',... - '\n''Anti Half Normal - the mean/max is at the side edge of the range.\n',... - '\n''Sinosidal - using sine function instead of half normal and Anti Sinusoidal is when max is at the edge, same as anti half normal.\n'])); - set_callback(Prob_Dist_Left, {mfilename, 'Cal_Mean'}); - next_row(y); - DispParam(obj, 'mean_Left', 0.01, x,y,'label','μ Left','TooltipString','mean/max log stim value for the left side distribution'); - next_row(y); - NumeditParam(obj, 'sigma_Left', 0.15, x,y,'label','σ Left','TooltipString','sigma value for normal/half normal distribution or decay rate for exponential for the left side distribution'); - next_row(y); - NumeditParam(obj, 'sigma_range_Left', 1, x,y,'label','3σ Left','TooltipString',sprintf(['\n A way to reduce the range and increase more distribution towards mean\n', ... - '\n''signifying 3 Sigma (99.7%%) value for the left side distribution, \n',... - '\n''A value b/w range [0.2 - 1] is acceptable.'])); - set_callback(sigma_range_Left, {mfilename, 'Cal_Sigma'}); - next_row(y); next_row(y); - - MenuParam(obj, 'Prob_Dist_Right', {'Uniform','Exponential','Half Normal','Normal','Sinusoidal','Anti Exponential','Anti Half Normal','Anti Sinusoidal'}, ... - 'Uniform', x, y, 'label','Right Dist', 'labelfraction', 0.35, 'TooltipString', sprintf(['\n Different Probability Distributions for Category A.\n', ... - '\n''Normal - the mean is at mid point of range (side edge - boundary). Half Normal - truncated normal with mean at boundary.\n',... - '\n''Anti Half Normal - the mean/max is at the side edge of the range.\n',... - '\n''Sinosidal - using sine function instead of half normal and Anti Sinusoidal is when max is at the edge, same as anti half normal'])); - set_callback(Prob_Dist_Right, {mfilename, 'Cal_Mean'}); - next_row(y); - DispParam(obj, 'mean_Right', 0.01, x,y,'label','μ Right','TooltipString','mean/max log stim value for the right side distribution'); - next_row(y); - NumeditParam(obj, 'sigma_Right', 0.15, x,y,'label','σ Right','TooltipString','sigma value for normal/half normal distribution or decay rate for exponential for the left side distribution'); - next_row(y); - NumeditParam(obj, 'sigma_range_Right', 1, x,y,'label','3σ Right','TooltipString',sprintf(['\n A way to reduce the range and increase more distribution towards mean\n', ... - '\n''signifying 3 Sigma (99.7 %%) value for the right side distribution, \n',... - '\n''A value b/w range [0.2 - 1] is acceptable.'])); - set_callback(sigma_range_Right, {mfilename, 'Cal_Sigma'}); - next_row(y);next_row(y); - MenuParam(obj, 'Category_Dist', {'Uniform','Hard A','Hard B'}, ... - 'Uniform', x, y, 'label','Category Dist', 'labelfraction', 0.35, 'TooltipString', sprintf(['\n Different Distributions for Category.\n', ... - '\n''Depending upon the rule it will change switch the distributions for Left and Right.\n',... - '\n''If its uniform on both then it will change the distribution to Exponential on one of the side\n',... - '\n''depending upon the choice of rule'])); - set_callback(Category_Dist, {mfilename, 'Distribution_Switch'}); - next_row(y);next_row(y); - PushbuttonParam(obj, 'plot_stim_dist', x,y , 'TooltipString', 'Plots the distribution with the new set of parameters'); - set_callback(plot_stim_dist, {mfilename, 'plot_stim_distribution'}); - next_column(x); - y=5; - next_row(y, 1) - MenuParam(obj, 'filter_type', {'GAUS','LPFIR', 'FIRLS','BUTTER','MOVAVRG','KAISER','EQUIRIP','HAMMING'}, ... - 'GAUS', x, y, 'labelfraction', 0.35, 'TooltipString', sprintf(['\nDifferent filters. ''LPFIR'': lowpass FIR ''FIRLS'': Least square linear-phase FIR filter design\n', ... - '\n''BUTTER'': IIR Butterworth lowpass filter ''GAUS'': Gaussian filter (window)\n', ... - '\n''MOVAVRG'': Moving average FIR filter ''KAISER'': Kaiser-window FIR filtering\n', ... - '\n''EQUIRIP'':Eqiripple FIR filter ''HAMMING'': Hamming-window based FIR'])); - next_row(y); - NumeditParam(obj,'fcut',110,x,y,'label','fcut','TooltipString','Cut off frequency on the original white noise'); - next_row(y); - NumeditParam(obj,'lfreq',2000,x,y,'label','Modulator_LowFreq','TooltipString','Lower bound for the frequency modulator'); - next_row(y); - NumeditParam(obj,'hfreq',20000,x,y,'label','Modulator_HighFreq','TooltipString','Upper bound for the frequency modulator'); - next_row(y); - NumeditParam(obj,'minS1',0.007,x,y,'label','minS1','TooltipString','min sigma value for AUD1'); - set_callback(minS1, {mfilename, 'Cal_Boundary'}); - next_row(y); - NumeditParam(obj,'maxS1',0.05,x,y,'label','maxS1','TooltipString','max sigma value for AUD1'); - set_callback(maxS1, {mfilename, 'Cal_Boundary'}); - next_row(y); - DispParam(obj, 'A1_sigma', 0.01, x,y,'label','A1_sigma','TooltipString','Sigma value for the first stimulus'); - next_row(y); - NumeditParam(obj,'minF1',4,x,y,'label','minF1','TooltipString','min frequency value for AUD1'); - set_callback(minF1, {mfilename, 'Cal_Boundary'}); - next_row(y); - NumeditParam(obj,'maxF1',10,x,y,'label','maxF1','TooltipString','max frequency value for AUD1'); - set_callback(maxF1, {mfilename, 'Cal_Boundary'}); - next_row(y); - NumeditParam(obj,'volumeF1',0.007,x,y,'label','VolumeF1','TooltipString','volume of tone for AUD1'); - next_row(y); - DispParam(obj, 'A1_freq', 0.01, x,y,'label','A1_freq','TooltipString','Sigma value for the first stimulus'); - next_row(y); - DispParam(obj,'boundary',-3.9,x,y,'label','boundary(log)','TooltipString','decision boundary for categorisation (log)'); - next_row(y); - MenuParam(obj, 'mu_location', {'center', 'side'}, ... - 'center', x, y, 'labelfraction', 0.35, 'TooltipString', sprintf('\nLocation of boundary')); - set_callback(mu_location, {mfilename, 'Cal_Boundary'}); - next_row(y); - ToggleParam(obj, 'frequency_categorization', 0, x,y,... - 'OnString', 'Frequency(Tone)',... - 'OffString', 'Amplitude(Noise)',... - 'TooltipString', sprintf('If on (black) then it enables the presentation of pure tones')); - set_callback(frequency_categorization, {mfilename, 'FrequencyCategorization'}); - make_invisible(maxF1);make_invisible(minF1);make_invisible(A1_freq);make_invisible(volumeF1); - next_row(y); - - % Axes for Plotting - hndl_uipanelplotaxes = uipanel('Units', 'normalized'); - set(hndl_uipanelplotaxes, ... - 'Units', 'normalized', ... - 'Parent', value(myfig), ... - 'Title', 'Stimuli Distribution', ... - 'Tag', 'uipanelstimplot', ... - 'Position', [0.06,0.52,0.8,0.42]); - - SoloParamHandle(obj, 'axstimplot', 'value', axes(hndl_uipanelplotaxes,'Units', 'normalized','Position', [0.07,0.07, ... - 0.8,0.85]), 'saveable', false); - - SoloParamHandle(obj, 'checkboxHist', 'value', ... - uicontrol('Parent', hndl_uipanelplotaxes, ... - 'Units', 'normalized', ... - 'Style', 'checkbox', ... - 'String', 'Histogram', ... - 'TooltipString', 'Show/hide histogram', ... - 'Value', 0, ... - 'Tag', 'checkboxHist', ... - 'Position', [0.9 0.3 0.1 0.12]), ... - 'saveable', false); - - SoloParamHandle(obj, 'checkboxLegends', 'value', ... - uicontrol('Parent', hndl_uipanelplotaxes, ... - 'Units', 'normalized', ... - 'Style', 'checkbox', ... - 'String', 'Legends', ... - 'TooltipString', 'Show/hide legends', ... - 'Value', 0, ... - 'Tag', 'checkboxlegend', ... - 'Position', [0.9 0.5 0.1 0.12]), ... - 'saveable', false); - - SoloParamHandle(obj, 'checkboxStim', 'value', ... - uicontrol('Parent', hndl_uipanelplotaxes, ... - 'Units', 'normalized', ... - 'Style', 'checkbox', ... - 'String', 'ThisStim', ... - 'TooltipString', 'Show/hide present stim', ... - 'Value', 0, ... - 'Tag', 'checkboxStim', ... - 'Position', [0.9 0.7 0.1 0.12]), ... - 'saveable', false); - - SoloParamHandle(obj, 'plot_h', 'value', struct(), 'saveable', false); % Initialize output - - % Plot the Distribution - StimulusSection(obj,'plot_stim_distribution'); - set(value(myfig), 'visible', 'off'); - - x=oldx; y=oldy; - figure(parentfig); - - SoloFunctionAddVars('PsychometricSection', 'ro_args',{'Category_Dist';'Rule';'boundary';'Prob_Dist_Right';'Prob_Dist_Left'}); - - varargout{1} = x; - varargout{2} = y; - - case 'prepare_next_trial' - if stimuli_on - StimulusSection(obj,'pick_current_stimulus'); - srate=SoundManagerSection(obj,'get_sample_rate'); - Fs=srate; - T=value(A1_time); - - if frequency_categorization - % produce the tone - A1_freq.value = value(thisstim); - A1 = value(thisstimlog(n_done_trials+1)); - dur1 = A1_time*1000; - bal=0; - freq1=A1_freq*1000; - vol=value(volumeF1); - RVol=vol*min(1,(1+bal)); - LVol=vol*min(1,(1-bal)); - t=0:(1/srate):(dur1/1000); - t = t(1:end-1); - tw=sin(t*2*pi*freq1); - RW=RVol*tw; - %w=[LW;RW]; - AUD1 = RW; - else - % produce noise pattern - A1_length = round(A1_time * srate); - A1_sigma.value = value(thisstim); - A1 = value(thisstimlog(n_done_trials+1)); - [rawA1, rawA2, normA1, normA2]=noisestim(1,1,T,value(fcut),Fs,value(filter_type)); - modulator=singlenoise(1,T,[value(lfreq) value(hfreq)],Fs,'BUTTER'); - AUD1=normA1(1:A1_length) .* modulator(1:A1_length).*A1_sigma; - end - - if ~isempty(AUD1) - SoundManagerSection(obj, 'set_sound', 'StimAUD1', [AUD1'; AUD1']) - end - - SoundManagerSection(obj, 'send_not_yet_uploaded_sounds'); - - if n_done_trials > 0 - - StimulusSection(obj,'update_stimulus_history'); - - % Update the stim histogram and show chosen stimuli - - if value(StimulusShow) == 1 % only run if the figure window is open - - stim_values = value(thisstimlog); - stim_present = stim_values(n_done_trials); - stim_history = stim_values(1:end-1); - - % figure out if legends,present stim and histogram to be plotted or not - legend_obj = value(checkboxLegends); - legend_draw = logical(legend_obj.Value); - - if n_done_trials < 10 - hist_draw = false; - else - hist_obj = value(checkboxHist); - hist_draw = logical(hist_obj.Value); - end - - stim_obj = value(checkboxStim); - stim_draw = logical(stim_obj.Value); - - if hist_draw || stim_draw - [~,~, handle_h] = CreateSamples_from_Distribution(... - 'Mode', 'update_plot', ... - 'ax_handle', value(axstimplot), ... % Get current axes handle - 'plot_handles_in', value(plot_h), ... - 'current_stimulus', stim_present,... - 'samples_history_in',stim_history,... - 'plot_histogram', hist_draw, ... - 'plot_chosen_stimuli', stim_draw, ... - 'plot_distribution', true, ... - 'plot_legend',legend_draw... - ); - plot_h.value = handle_h; - drawnow; - pause(0.01); - end - - end - end - end - - %% Case pick_current_stimulus - case 'pick_current_stimulus' - if frequency_categorization - stim_min_log = log(value(minF1)); - stim_max_log = log(value(maxF1)); - else - stim_min_log = log(value(minS1)); - stim_max_log = log(value(maxS1)); - end - - dist_sigma_left_multiplier = value(sigma_Left) * value(sigma_range_Left); - dist_sigma_right_multiplier = value(sigma_Right) * value(sigma_range_Right); - - % The left-right side according the animal is not the same as left-right of - % stim distribution because it depends upon the Rule whether Stim should be - % considered left/right (below/up of boundary) based upon this rule. - % Changing animals side reference to stimuli reference depending upon Rule - % Even the setting in StimulusSection is based upon side not on the - % basis of whether left or right of boundary - - if strcmp(Rule,'S1>S_boundary Left') % side left is right to stim boundary - if strcmpi(ThisTrial, 'LEFT') - stim_side = 'right'; - else - stim_side = 'left'; - end - dist_left = value(Prob_Dist_Right); - dist_right = value(Prob_Dist_Left); - range_percent_left = value(sigma_range_Right) * 100; - range_percent_right = value(sigma_range_Left) * 100; - dist_mean_left = value(mean_Right); - dist_mean_right = value(mean_Left); - dist_sigma_left = dist_sigma_right_multiplier * (value(boundary) - stim_min_log); - dist_sigma_right = dist_sigma_left_multiplier * (stim_max_log - value(boundary)); - exp_decay_left = value(sigma_Right); - exp_decay_right = value(sigma_Left); - - else % the rule is S1>S_boundary Right - if strcmpi(ThisTrial, 'LEFT') - stim_side = 'left'; - else - stim_side = 'right'; - end - dist_right = value(Prob_Dist_Right); - dist_left = value(Prob_Dist_Left); - range_percent_right = value(sigma_range_Right) * 100; - range_percent_left = value(sigma_range_Left) * 100; - dist_mean_right = value(mean_Right); - dist_mean_left = value(mean_Left); - dist_sigma_left = dist_sigma_left_multiplier * (value(boundary) - stim_min_log); - dist_sigma_right = dist_sigma_right_multiplier * (stim_max_log - value(boundary)); - exp_decay_left = value(sigma_Left); - exp_decay_right = value(sigma_Right); - end - - if ~exist('LeftProb','var') - LeftProb = SideSection(obj,'get_left_prob'); - end - - % Generate a single sample, explicitly chosen side 'left' - [stim_i_log,~,~] = CreateSamples_from_Distribution(... - 'Mode', 'generate_single_sample', ... - 'chosen_side', stim_side, ... % Force pick from chosen side - 'left_edge_value', stim_min_log,... - 'boundary_value', value(boundary),... - 'right_edge_value', stim_max_log,... - 'left_probability', LeftProb, ... % These still define the overall PDF, but this pick is forced chosen side - 'right_probability', 1 - LeftProb, ... - 'left_dist_type', dist_left, ... - 'decay_rate_magnitude_left', exp_decay_left,... - 'normal_mean_left', dist_mean_left,... - 'normal_std_dev_left', dist_sigma_left,... - 'half_normal_std_dev_left', dist_sigma_left,... - 'range_percentage_left',range_percent_left , ... - 'right_dist_type', dist_right, ... - 'decay_rate_magnitude_right', exp_decay_right,... - 'normal_mean_right', dist_mean_right,... - 'normal_std_dev_right', dist_sigma_right,... - 'half_normal_std_dev_right', dist_sigma_right,... - 'range_percentage_right',range_percent_right, ... - 'P_central_region', value(P_centre_region), 'central_region_width', value(centre_region_width) ... - ); - - thisstim.value=exp(stim_i_log); - thisstimlog(n_done_trials+1) = stim_i_log; - - %% Case plot stimuli distribution - case 'plot_stim_distribution' - - if frequency_categorization - stim_min_log = log(value(minF1)); - stim_max_log = log(value(maxF1)); - else - stim_min_log = log(value(minS1)); - stim_max_log = log(value(maxS1)); - end - - dist_sigma_left_multiplier = value(sigma_Left) * value(sigma_range_Left); - dist_sigma_right_multiplier = value(sigma_Right) * value(sigma_range_Right); - - % The left-right side according the animal is not the same as left-right of - % stim distribution because it depends upon the Rule whether Stim should be - % considered left/right (below/up of boundary) based upon this rule. - % Changing animals side reference to stimuli reference depending upon Rule - - if strcmp(Rule,'S1>S_boundary Left') % side left is right to stim boundary - dist_left = value(Prob_Dist_Right); - dist_right = value(Prob_Dist_Left); - range_percent_left = value(sigma_range_Right) * 100; - range_percent_right = value(sigma_range_Left) * 100; - dist_mean_left = value(mean_Right); - dist_mean_right = value(mean_Left); - dist_sigma_left = dist_sigma_right_multiplier * (value(boundary) - stim_min_log); - dist_sigma_right = dist_sigma_left_multiplier * (stim_max_log - value(boundary)); - exp_decay_right = value(sigma_Left); - exp_decay_left = value(sigma_Right); - - else % the rule is S1>S_boundary Right - dist_right = value(Prob_Dist_Right); - dist_left = value(Prob_Dist_Left); - range_percent_right = value(sigma_range_Right) * 100; - range_percent_left = value(sigma_range_Left) * 100; - dist_mean_right = value(mean_Right); - dist_mean_left = value(mean_Left); - dist_sigma_left = dist_sigma_left_multiplier * (value(boundary) - stim_min_log); - dist_sigma_right = dist_sigma_right_multiplier * (stim_max_log - value(boundary)); - exp_decay_left = value(sigma_Left); - exp_decay_right = value(sigma_Right); - end - - if ~exist('LeftProb','var') - LeftProb = SideSection(obj,'get_left_prob'); - end - - % figure out if legends to be plotted or not - legend_obj = value(checkboxLegends); - legend_draw = logical(legend_obj.Value); - - - [~,~, handle_h] = CreateSamples_from_Distribution(... - 'Mode', 'initialize_plot', ... - 'ax_handle', value(axstimplot), ... % Get current axes handle - 'plot_handles_in', value(plot_h), ... - 'plot_histogram', false, ... - 'plot_chosen_stimuli', false, ... - 'plot_distribution', true, ... - 'left_edge_value', stim_min_log,... - 'boundary_value', value(boundary),... - 'right_edge_value', stim_max_log,... - 'left_probability', LeftProb, ... % These still define the overall PDF, but this pick is forced chosen side - 'right_probability', 1 - LeftProb, ... - 'left_dist_type', dist_left, ... - 'normal_mean_left', dist_mean_left,... - 'decay_rate_magnitude_left', exp_decay_left,... - 'normal_std_dev_left', dist_sigma_left,... - 'half_normal_std_dev_left', dist_sigma_left,... - 'range_percentage_left',range_percent_left , ... - 'right_dist_type', dist_right, ... - 'decay_rate_magnitude_right', exp_decay_right,... - 'normal_mean_right', dist_mean_right,... - 'normal_std_dev_right', dist_sigma_right,... - 'half_normal_std_dev_right', dist_sigma_right,... - 'range_percentage_right',range_percent_right, ... - 'P_central_region', value(P_centre_region), 'central_region_width', value(centre_region_width), ... - 'plot_legend',legend_draw... - ); - - plot_h.value = handle_h; - drawnow; - - - - %% Boundary Calculate - case 'Cal_Boundary' - if frequency_categorization - val_boundary = (log(value(minF1)) + log(value(maxF1)))/2; - min_val = log(value(minF1)); - else - val_boundary = (log(value(minS1)) + log(value(maxS1)))/2; - min_val = log(value(minS1)); - end - if strcmp(mu_location,'center') - boundary.value = val_boundary; - elseif strcmp(mu_location,'side') - boundary.value = (min_val + val_boundary)/2; - end - - StimulusSection(obj,'Cal_Mean'); % update the mean and sigma values for each side - - %% Updated Mean/Max for Each Side based upon Distribution Selected - case 'Cal_Mean' - - if frequency_categorization - edge_max = log(value(maxF1)); - edge_min = log(value(minF1)); - else - edge_max = log(value(maxS1)); - edge_min = log(value(minS1)); - end - - % Calculation for Left Side - - % Sigma - - dist_sigma_multiplier = value(sigma_range_Left); - if dist_sigma_multiplier < 0.2 - dist_sigma_multiplier = 0.2; - end - if dist_sigma_multiplier > 1 - dist_sigma_multiplier = 1; - end - - if strcmp(Rule,'S1>S_boundary Left') - edge_min_left = value(boundary); - edge_max_left = edge_min_left + dist_sigma_multiplier * (edge_max - edge_min_left); - else % the rule is S1>S_boundary Right - edge_max_left = value(boundary); - edge_min_left = edge_max_left - dist_sigma_multiplier * (edge_max_left - edge_min); - end - - make_invisible(sigma_Left); - switch value(Prob_Dist_Left) - case {'Half Normal', 'Anti Half Normal'} - make_visible(sigma_Left); - sigma_Left.value = 0.25; - case 'Normal' - make_visible(sigma_Left); - sigma_Left.value = 0.25; - case {'Exponential','Anti Exponential'} - make_visible(sigma_Left); - sigma_Left.value = 2.153; - end - - % Mean - if matches(value(Prob_Dist_Left),{'Uniform','Half Normal','Sinusoidal','Exponential'}) - mean_Left.value = value(boundary); - else - if strcmp(Rule,'S1>S_boundary Left') - if matches(value(Prob_Dist_Left),{'Anti Half Normal','Anti Sinusoidal','Anti Exponential'}) - mean_Left.value = edge_max_left; - elseif matches(value(Prob_Dist_Left),'Normal') - mean_Left.value = (edge_max_left + value(boundary))/2; - end - else - if matches(value(Prob_Dist_Left),{'Anti Half Normal','Anti Sinusoidal','Anti Exponential'}) - mean_Left.value = edge_min_left; - elseif matches(value(Prob_Dist_Left),'Normal') - mean_Left.value = (edge_min_left + value(boundary))/2; - end - end - end - - % Calculation for Right Side - - % Sigma - dist_sigma_multiplier = value(sigma_range_Right); - if dist_sigma_multiplier < 0.2 - dist_sigma_multiplier = 0.2; - end - if dist_sigma_multiplier > 1 - dist_sigma_multiplier = 1; - end - - if strcmp(Rule,'S1>S_boundary Right') - edge_min_right = value(boundary); - edge_max_right = edge_min_right + dist_sigma_multiplier * (edge_max - edge_min_right); - else % the rule is S1>S_boundary Right - edge_max_right = value(boundary); - edge_min_right = edge_max_right - dist_sigma_multiplier * (edge_max_right - edge_min); - end - - make_invisible(sigma_Right); - switch value(Prob_Dist_Right) - case {'Half Normal', 'Anti Half Normal'} - make_visible(sigma_Right); - sigma_Right.value = 0.25; - case 'Normal' - make_visible(sigma_Right); - sigma_Right.value = 0.25; - case {'Exponential','Anti Exponential'} - make_visible(sigma_Right); - sigma_Right.value = 2.153; - end - - - % Mean - if matches(value(Prob_Dist_Right),{'Uniform','Half Normal','Sinusoidal','Exponential'}) - mean_Right.value = value(boundary); - else - if strcmp(Rule,'S1>S_boundary Right') - if matches(value(Prob_Dist_Right),{'Anti Half Normal','Anti Sinusoidal','Anti Exponential'}) - mean_Right.value = edge_max_right; - elseif matches(value(Prob_Dist_Right),'Normal') - mean_Right.value = (edge_max_right + value(boundary))/2; - end - else - if matches(value(Prob_Dist_Right),{'Anti Half Normal','Anti Sinusoidal','Anti Exponential'}) - mean_Right.value = edge_min_right; - elseif matches(value(Prob_Dist_Right),'Normal') - mean_Right.value = (edge_min_right + value(boundary))/2; - end - end - end - - StimulusSection(obj,'plot_stim_distribution'); - - %% Calculate Sigma - case 'Cal_Sigma' - - if frequency_categorization - edge_max = log(value(maxF1)); - edge_min = log(value(minF1)); - else - edge_max = log(value(maxS1)); - edge_min = log(value(minS1)); - end - - % Calculation for Left Side Sigma - dist_sigma_multiplier = value(sigma_range_Left); - if dist_sigma_multiplier < 0.2 - sigma_range_Left.value = 0.2; - end - if dist_sigma_multiplier > 1 - sigma_range_Left.value = 1; - end - - make_invisible(sigma_Left); - switch value(Prob_Dist_Left) - case {'Half Normal', 'Anti Half Normal'} - make_visible(sigma_Left); - sigma_Left.value = 0.25; - case 'Normal' - make_visible(sigma_Left); - sigma_Left.value = 0.25; - case {'Exponential','Anti Exponential'} - make_visible(sigma_Left); - sigma_Left.value = 2.153; - end - - % Calculation for Right Side Sigma - dist_sigma_multiplier = value(sigma_range_Right); - if dist_sigma_multiplier < 0.2 - sigma_range_Right.value = 0.2; - end - if dist_sigma_multiplier > 1 - sigma_range_Right.value = 1; - end - - make_invisible(sigma_Right); - switch value(Prob_Dist_Right) - case {'Half Normal', 'Anti Half Normal'} - make_visible(sigma_Right); - sigma_Right.value = 0.25; - case 'Normal' - make_visible(sigma_Right); - sigma_Right.value = 0.25; - case {'Exponential','Anti Exponential'} - make_visible(sigma_Right); - sigma_Right.value = 2.153; - end - - case 'stim_params' - - if frequency_categorization - stim_min_log = log(value(minF1)); - stim_max_log = log(value(maxF1)); - else - stim_min_log = log(value(minS1)); - stim_max_log = log(value(maxS1)); - end - - dist_range_multiplier_left = value(sigma_range_Left); - dist_range_multiplier_right = value(sigma_range_Right); - - if strcmp(Rule,'S1>S_boundary Left') - edge_max_left = stim_max_log; - edge_min_left = value(boundary); - edge_max_left = edge_min_left + dist_range_multiplier_left * (edge_max_left - edge_min_left); - edge_max_right = value(boundary); - edge_min_right = stim_min_log; - edge_min_right = edge_max_right - dist_range_multiplier_right * (edge_max_right - edge_min_right); - - varargout{1} = [edge_min_right, value(boundary), edge_max_left]; - - else % the rule is S1>S_boundary Right - - edge_min_left = stim_min_log; - edge_max_left = value(boundary); - edge_min_left = edge_max_left - dist_range_multiplier_left * (edge_max_left - edge_min_left); - edge_max_right = stim_max_log; - edge_min_right = value(boundary); - edge_max_right = edge_min_right + dist_range_multiplier_right * (edge_max_right - edge_min_right); - - varargout{1} = [edge_min_left, value(boundary), edge_max_right]; - end - - %% Case frequency ON - case 'FrequencyCategorization' - if frequency_categorization == 1 - make_visible(maxF1);make_visible(minF1);make_visible(A1_freq);make_visible(volumeF1); - make_invisible(maxS1);make_invisible(minS1);make_invisible(A1_sigma); - make_invisible(fcut);make_invisible(lfreq);make_invisible(hfreq); make_invisible(filter_type); - else - make_visible(maxS1);make_visible(minS1);make_visible(A1_sigma); - make_visible(fcut);make_visible(lfreq);make_visible(hfreq); make_visible(filter_type); - make_invisible(maxF1);make_invisible(minF1);make_invisible(A1_freq); make_invisible(volumeF1); - end - - StimulusSection(obj,'Cal_Boundary'); % update the boundary - StimulusSection(obj,'plot_stim_distribution'); - - %% Case get_stimuli - % case 'get_stimuli' - % if nargout>0 - % x=value(S1); - % end - - case 'Distribution_Switch' - - switch value(Category_Dist) - - case 'Uniform' - Prob_Dist_Right.value = 'Uniform'; - make_invisible(sigma_Right); - Prob_Dist_Left.value = 'Uniform'; - make_invisible(sigma_Left); - - case 'Hard A' - if strcmp(Rule,'S1>S_boundary Right') - Prob_Dist_Right.value = 'Uniform'; - make_invisible(sigma_Right); - Prob_Dist_Left.value = 'Exponential'; - sigma_Left.value = 2.153; - make_visible(sigma_Left); - else - Prob_Dist_Right.value = 'Exponential'; - sigma_Right.value = 2.153; - make_visible(sigma_Right); - Prob_Dist_Left.value = 'Uniform'; - make_invisible(sigma_Left); - end - - case 'Hard B' - if strcmp(Rule,'S1>S_boundary Right') - Prob_Dist_Left.value = 'Uniform'; - make_invisible(sigma_Left); - Prob_Dist_Right.value = 'Exponential'; - make_visible(sigma_Right); - sigma_Right.value = 2.153; - else - Prob_Dist_Left.value = 'Exponential'; - make_visible(sigma_Left); - sigma_Left.value = 2.153; - Prob_Dist_Right.value = 'Uniform'; - make_invisible(sigma_Right); - end - end - StimulusSection(obj,'plot_stim_distribution'); - PsychometricSection(obj,'StimSection_Distribution_Switch'); - - case 'Pushbutton_SwitchDistribution' - dist = varargin{1}; - Category_Dist.value = dist; - StimulusSection(obj,'Distribution_Switch'); - - %% Case close - case 'close' - set(value(myfig), 'visible', 'off'); - % set(value(stim_dist_fig), 'visible', 'off'); - - % Delete all SoloParamHandles who belong to this object and whose - % fullname starts with the name of this mfile: - if exist('myfig', 'var') && isa(myfig, 'SoloParamHandle') && ishandle(value(myfig)) %#ok - delete(value(myfig)); - end - if exist('stim_dist_fig', 'var') && isa(stim_dist_fig, 'SoloParamHandle') && ishandle(value(stim_dist_fig)) %#ok - delete(value(stim_dist_fig)); - end - delete_sphandle('owner', ['^@' class(obj) '$'], ... - 'fullname', ['^' mfilename]); - - case 'update_stimulus_history' - ps=value(stimulus_history); - ps1 = value(stimulus_distribution_history); - ps2 = value(stimulus_right_distribution_history); - ps3 = value(stimulus_left_distribution_history); - ps(n_done_trials)=value(thisstimlog(n_done_trials)); - ps1{n_done_trials}=value(Category_Dist); - stimulus_history.value=ps; - stimulus_distribution_history.value = ps1; - - %% Case hide - case 'hide' - StimulusShow.value = 0; - set(value(myfig), 'visible', 'off'); - % set(value(stim_dist_fig), 'visible', 'off'); - - %% Case show - case 'show' - StimulusShow.value = 1; - set(value(myfig), 'visible', 'on'); - % set(value(stim_dist_fig), 'visible', 'on'); - - %% Case Show_hide - case 'show_hide' - if StimulusShow == 1 - set(value(myfig), 'visible', 'on'); - % set(value(stim_dist_fig), 'visible', 'on');%#ok (defined by GetSoloFunctionArgs) - else - set(value(myfig), 'visible', 'off'); - % set(value(stim_dist_fig), 'visible', 'off'); - end - -end - -return; diff --git a/Protocols/@ArpitSoundCatContinuous/StimulusSection.m b/Protocols/@ArpitSoundCatContinuous/StimulusSection.m index af403640..d147ee75 100644 --- a/Protocols/@ArpitSoundCatContinuous/StimulusSection.m +++ b/Protocols/@ArpitSoundCatContinuous/StimulusSection.m @@ -777,7 +777,7 @@ 'fullname', ['^' mfilename]); case 'update_stimulus_history' - ps=value(stimulus_history); + ps = value(stimulus_history); ps1 = value(stimulus_distribution_history); ps2 = value(stimulus_right_distribution_history); ps3 = value(stimulus_left_distribution_history); diff --git a/Protocols/@ArpitSoundCatContinuous/private/RealTimeAnalysis.asv b/Protocols/@ArpitSoundCatContinuous/private/RealTimeAnalysis.asv deleted file mode 100644 index dea47c8b..00000000 --- a/Protocols/@ArpitSoundCatContinuous/private/RealTimeAnalysis.asv +++ /dev/null @@ -1,768 +0,0 @@ -function state = RealTimeAnalysis(action, state, data, handles, config, varargin) -% RealTimeAnalysis function to process and plot trial data for BControl. -% This function is called repeatedly from a parent script -% (e.g., Psychometric.m) and is made stateless by passing all required -% information in and out on every call. It uses a try/catch block to -% prevent crashes during live experiments. -% -% Args: -% action (char): The operation to perform. Can be: -% 'live': Standard update after a block of trials. -% 'redraw': Redraws live plots, typically when a figure becomes visible. -% 'context_switch': Special handling for leftover trials when a context changes. -% 'custom': Plots a single, user-defined range of trials. -% 'context': Plots multiple, user-defined ranges for comparison. -% -% state (struct): Contains variables that are modified and passed back. -% .blockStatsHistory (struct array): Stores analysis results for each block. -% .block_count (double): Counter for the number of blocks analyzed. -% .last_analyzed_valid_trial (double): Counter for the last valid trial included in an analysis. -% -% data (struct): Contains the complete, read-only data histories for the session. -% .hit_history (vector): History of hits (1), misses (0), or NaN. -% .previous_sides (vector): History of sides presented (e.g., 0 for left, 1 for right). -% .stim_history (vector): History of stimulus values. -% .full_rule_history (cell array of strings): History of the psychometric rule. -% .full_dist_right, .full_dist_left: Histories of stimulus distributions. -% -% handles (struct): Contains handles to all necessary GUI components. -% .main_fig (handle): Handle to the main analysis figure. -% .axes_h (struct): A struct containing handles to the 6 plot axes. -% .ui_table (handle): Handle to the results table. -% -% config (struct): Contains session-wide constants. -% .trials_per_block (double): Number of valid trials needed for a live update. -% .true_mu (double): The true boundary of the stimulus categories. -% .stimuli_range (1x2 vector): The [min, max] of possible stimulus values. -% .debug (logical): If true, errors will be rethrown; if false, they will be caught and displayed as warnings. -% -% varargin: Action-specific arguments based on the 'action' string. -% For 'live', 'redraw', 'context_switch': -% varargin{1} (struct): A flags struct with logical values. -% .psych (logical): Toggles the psychometric plot. -% .hit (logical): Toggles the hit rate plot. -% .stim (logical): Toggles the stimulus histogram. -% For 'custom': -% varargin{1} (struct): The flags struct (as above). -% varargin{2} (double): The start trial for the custom range. -% varargin{3} (double): The end trial for the custom range. -% For 'context': -% varargin{1} (struct): The flags struct (as above). -% varargin{2} (cell): A cell array of trial ranges, e.g., {[s1,e1], [s2,e2]}. -% -% Returns: -% state (struct): The updated state struct, which should be saved back -% to the corresponding SoloParamHandles in the parent script. - - try - switch lower(action) - % ================================================================= - % LIVE UPDATE ACTION - % ================================================================= - case 'live' - % This action is called on every trial to check if enough new - % data has accumulated for a block analysis. - if numel(varargin) < 1, error('Live action requires a flags struct.'); end - flags = varargin{1}; - - lastAnalyzed = state.last_analyzed_valid_trial; - validTrials = sum(~isnan(data.hit_history)); - - % Check if the number of new valid trials meets the block size requirement. - if (validTrials - lastAnalyzed) >= config.trials_per_block - valid_indices = find(~isnan(data.hit_history)); - buffer_indices = valid_indices(end - config.trials_per_block + 1 : end); - - dataBuffer.stim = data.stim_history(buffer_indices); - dataBuffer.hit = data.hit_history(buffer_indices); - dataBuffer.side = data.previous_sides(buffer_indices); - dataBuffer.indices = buffer_indices; - - % Analyze the new block and update the state. - state = analyzeLiveChunk(state, data, handles, config, dataBuffer, flags); - end - - % ================================================================= - % CONTEXT SWITCH ACTION - % ================================================================= - case 'context_switch' - % This action handles leftover trials when a context is manually changed. - if numel(varargin) < 1, error('context_switch action requires a flags struct.'); end - flags = varargin{1}; - - lastAnalyzed = state.last_analyzed_valid_trial; - validTrials = sum(~isnan(data.hit_history)); - remaining_valid_trials = validTrials - lastAnalyzed; - - if remaining_valid_trials > 0 - valid_indices = find(~isnan(data.hit_history)); - new_valid_indices = valid_indices(valid_indices > find(valid_indices == lastAnalyzed, 1, 'last')); - - % If there are few leftover trials, merge them with the previous block. - if remaining_valid_trials < 20 && state.block_count > 0 - last_block_indices = state.blockStatsHistory(end).indices; - combined_indices = [last_block_indices, new_valid_indices']; - - dataBuffer.stim = data.stim_history(combined_indices); - dataBuffer.hit = data.hit_history(combined_indices); - dataBuffer.side = data.previous_sides(combined_indices); - dataBuffer.indices = combined_indices; - - % Re-analyze the merged block, replacing the last entry. - state = reAnalyzeLastChunk(state, data, handles, config, dataBuffer, flags); - - % If there are enough leftover trials, treat them as a new block. - elseif remaining_valid_trials >= 20 - dataBuffer.stim = data.stim_history(new_valid_indices); - dataBuffer.hit = data.hit_history(new_valid_indices); - dataBuffer.side = data.previous_sides(new_valid_indices); - dataBuffer.indices = new_valid_indices; - - state = analyzeLiveChunk(state, data, handles, config, dataBuffer, flags); - end - end - - % ================================================================= - % REDRAW ACTION - % ================================================================= - case 'redraw' - % This action is called when the GUI figure becomes visible. It only - % redraws the live plots using the current, pre-computed state. - if numel(varargin) < 1, error('Redraw action requires a flags struct.'); end - flags = varargin{1}; - - updateLivePlots(state, data, handles, config, flags); - - % ================================================================= - % CUSTOM PLOT ACTION - % ================================================================= - case 'custom' - % This action plots a single, user-defined range of trials on the custom axes. - if numel(varargin) < 3, error('Custom action requires flags, start_trial, and end_trial.'); end - flags = varargin{1}; - start_idx = varargin{2}; - end_idx = varargin{3}; - - if start_idx >= end_idx || start_idx < 1 || end_idx > numel(data.hit_history), warning('Invalid trial range for custom plot.'); return; end - - indices = start_idx:end_idx; - hit_chunk = data.hit_history(indices); - valid_mask = ~isnan(hit_chunk); - if sum(valid_mask) < 10, warning('Not enough valid trials in custom range to plot.'); return; end - - stim_fit = data.stim_history(indices(valid_mask)); - side_fit = data.previous_sides(indices(valid_mask)); - hit_fit = hit_chunk(valid_mask); - - rules_in_chunk = unique(data.full_rule_history(indices(valid_mask))); - if numel(rules_in_chunk) > 1, warning('Multiple rules in custom range; using the first one.'); end - current_rule = rules_in_chunk{1}; - - if flags.psych, plotCustomPsychometric(handles.axes_h.custom_psych, stim_fit, side_fit, hit_fit, config, current_rule); end - if flags.hit, plotCustomHitRates(handles.axes_h.custom_hitrate, hit_fit, side_fit, [start_idx, end_idx], state.blockStatsHistory); end - if flags.stim, plotCustomStimulusHistogram(handles.axes_h.custom_stim, stim_fit, hit_fit, [start_idx, end_idx], state.blockStatsHistory, config); end - - % ================================================================= - % CONTEXT PLOT ACTION - % ================================================================= - case 'context' - % This action plots multiple trial ranges on the custom axes for comparison. - if numel(varargin) < 2, error('Context action requires flags and a cell array of contexts.'); end - flags = varargin{1}; - contexts = varargin{2}; - - if ~iscell(contexts) || isempty(contexts), error('Contexts must be a non-empty cell array of [start, end] pairs.'); end - - num_contexts = numel(contexts); - context_colors = createTemporalColormap(num_contexts); - - psych_data = cell(1, num_contexts); - hit_rate_data = zeros(num_contexts, 3); - hit_rate_std = zeros(num_contexts, 3); - stim_hist_data = cell(1, num_contexts); - - % Loop through each context, gather its data, and perform analysis. - for i = 1:num_contexts - start_idx = contexts{i}(1); - end_idx = contexts{i}(2); - - if start_idx >= end_idx || start_idx < 1 || end_idx > numel(data.hit_history), continue; end - - indices = start_idx:end_idx; - hit_chunk = data.hit_history(indices); - valid_mask = ~isnan(hit_chunk); - if sum(valid_mask) < 10, continue; end - - stim_fit = data.stim_history(indices(valid_mask)); - side_fit = data.previous_sides(indices(valid_mask)); - hit_fit = hit_chunk(valid_mask); - - rules_in_context = unique(data.full_rule_history(indices(valid_mask))); - current_rule = rules_in_context{1}; - - physical_response = zeros(size(hit_fit)); - physical_response(hit_fit == 1) = side_fit(hit_fit == 1); - physical_response(hit_fit == 0) = 1 - side_fit(hit_fit == 0); - - response_for_fitting = physical_response; - if contains(current_rule, 'Left', 'IgnoreCase', true), response_for_fitting = 1 - response_for_fitting; end - - options.MinTrials = 10; - [y_pred, fitParams, ~, ~] = realtimepsychometricFit(stim_fit, response_for_fitting, config.stimuli_range, options); - psych_data{i} = struct('y_pred', y_pred, 'fitParams', fitParams, 'stim_fit', stim_fit, 'physical_response', physical_response, 'rule', current_rule); - - hit_rate_data(i, 1) = 100 * sum(hit_fit == 1) / numel(hit_fit); - hit_rate_data(i, 2) = 100 * sum(hit_fit(side_fit==0)==1) / sum(side_fit==0); - hit_rate_data(i, 3) = 100 * sum(hit_fit(side_fit==1)==1) / sum(side_fit==1); - - left_edges = linspace(min(config.stimuli_range), config.true_mu, 6); - right_edges = linspace(config.true_mu, max(config.stimuli_range), 6); - bin_edges = unique([left_edges, right_edges]); - stim_hist_data{i}.correct = histcounts(stim_fit(hit_fit == 1), bin_edges); - stim_hist_data{i}.incorrect = histcounts(stim_fit(hit_fit == 0), bin_edges); - - relevant_blocks = []; - for j = 1:numel(state.blockStatsHistory) - block_indices = state.blockStatsHistory(j).indices; - if min(block_indices) >= start_idx && max(block_indices) <= end_idx - relevant_blocks = [relevant_blocks, state.blockStatsHistory(j)]; - end - end - - if ~isempty(relevant_blocks) - hit_rate_std(i, 1) = std([relevant_blocks.hitRates.overall], 'omitnan'); - hit_rate_std(i, 2) = std([relevant_blocks.hitRates.left], 'omitnan'); - hit_rate_std(i, 3) = std([relevant_blocks.hitRates.right], 'omitnan'); - counts_matrix_corr = vertcat(relevant_blocks.stimCounts.correct); - counts_matrix_incorr = vertcat(relevant_blocks.stimCounts.incorrect); - stim_hist_data{i}.mean_corr = mean(counts_matrix_corr, 1); - stim_hist_data{i}.std_corr = std(counts_matrix_corr, 0, 1); - stim_hist_data{i}.mean_incorr = mean(counts_matrix_incorr, 1); - stim_hist_data{i}.std_incorr = std(counts_matrix_incorr, 0, 1); - else - hit_rate_std(i, :) = 0; - end - end - - if flags.psych, plotContextPsychometric(handles.axes_h.custom_psych, psych_data, config, context_colors); end - if flags.hit, plotContextHitRates(handles.axes_h.custom_hitrate, hit_rate_data, hit_rate_std, context_colors); end - if flags.stim, plotContextStimulusHistogram(handles.axes_h.custom_stim, stim_hist_data, config, context_colors); end - - otherwise - error('Unknown action: "%s". Use "live", "custom", or "context".', action); - end - catch ME - % Error handling block - warning('RealTimeAnalysisApp:Error', 'An error occurred in RealTimeAnalysisApp: %s', ME.message); - - % In debug mode, rethrow the error to pause execution. - if config.debug - rethrow(ME); - end - % In experiment mode, the function will gracefully return the original state. - end - - % ================================================================= - % NESTED HELPER FUNCTIONS - % ================================================================= - - %% LIVE ANALYSIS HELPERS - function state = analyzeLiveChunk(state, data, handles, config, dataBuffer, flags) - % This function analyzes a new chunk of data and ADDS it to the history. - state.block_count = state.block_count + 1; - [newBlockStat, newRow] = processBlock(data, config, dataBuffer); - state.blockStatsHistory = [state.blockStatsHistory, newBlockStat]; - updateTable(handles, newRow); - - % Only plot if the figure is visible to save computation. - if strcmp(get(handles.main_fig, 'Visible'), 'on') - updateLivePlots(state, data, handles, config, flags); - end - - state.last_analyzed_valid_trial = sum(~isnan(data.hit_history)); - end - - function state = reAnalyzeLastChunk(state, data, handles, config, dataBuffer, flags) - % This function re-analyzes a chunk and REPLACES the last entry in the history. - [newBlockStat, newRow] = processBlock(data, config, dataBuffer); - state.blockStatsHistory(end) = newBlockStat; - replaceLastTableRow(handles, newRow); - - if strcmp(get(handles.main_fig, 'Visible'), 'on') - updateLivePlots(state, data, handles, config, flags); - end - - state.last_analyzed_valid_trial = sum(~isnan(data.hit_history)); - end - - function [blockStat, tableRow] = processBlock(data, config, dataBuffer) - % Common processing logic for any block of data. - % 1. Convert hit/side to a physical response (0=left, 1=right). - physical_response = zeros(size(dataBuffer.hit)); - physical_response(dataBuffer.hit == 1) = dataBuffer.side(dataBuffer.hit == 1); - physical_response(dataBuffer.hit == 0) = 1 - dataBuffer.side(dataBuffer.hit == 0); - - % 2. Determine the rule for this block. - current_rule = data.full_rule_history; - - % 3. Invert the response if the rule is 'Left' for correct fitting. - response_for_fitting = physical_response; - if contains(current_rule, 'Left', 'IgnoreCase', true) - response_for_fitting = 1 - physical_response; - end - - % 4. Fit the psychometric curve. - options.MinTrials = 20; - [~, fitParams, methodUsed, fitStatus] = realtimepsychometricFit(dataBuffer.stim, response_for_fitting, config.stimuli_range, options); - - % 5. Calculate hit rates. - hr.overall = 100 * sum(dataBuffer.hit == 1) / numel(dataBuffer.hit); - left_mask = (dataBuffer.side == 0); - if any(left_mask), hr.left = 100 * sum(dataBuffer.hit(left_mask)==1) / sum(left_mask); else, hr.left = NaN; end - right_mask = (dataBuffer.side == 1); - if any(right_mask), hr.right = 100 * sum(dataBuffer.hit(right_mask)==1) / sum(right_mask); else, hr.right = NaN; end - - % 6. Package block statistics for history. - blockStat.indices = dataBuffer.indices; - blockStat.hitRates = hr; - - % 7. Bin stimulus data for history. - left_edges = linspace(min(config.stimuli_range), config.true_mu, 6); - right_edges = linspace(config.true_mu, max(config.stimuli_range), 6); - bin_edges = unique([left_edges, right_edges]); - stim_correct = dataBuffer.stim(dataBuffer.hit == 1); - stim_incorrect = dataBuffer.stim(dataBuffer.hit == 0); - blockStat.stimCounts.correct = histcounts(stim_correct, bin_edges); - blockStat.stimCounts.incorrect = histcounts(stim_incorrect, bin_edges); - - % 8. Package table row data. - start_trial = min(dataBuffer.indices); end_trial = max(dataBuffer.indices); - dist_right = strjoin(string(unique(data.full_dist_right(dataBuffer.indices))), ', '); - dist_left = strjoin(string(unique(data.full_dist_left(dataBuffer.indices))), ', '); - - select_status = ismember(methodUsed, {'ridge', 'robust'}); - if select_status - tableRow = {select_status, current_rule, dist_left, dist_right, start_trial, end_trial, fitParams(2), config.true_mu, fitParams(1), fitParams(3), fitParams(4), string(methodUsed) + " (" + fitStatus + ")", hr.overall, hr.left, hr.right}; - else - tableRow = {select_status, current_rule, dist_left, dist_right, start_trial, end_trial, NaN, config.true_mu, NaN, NaN, NaN, string(methodUsed) + " (" + fitStatus + ")", hr.overall, hr.left, hr.right}; - end - end - - function updateTable(handles, newRow) - % Appends a new row to the table and selects it. - currentData = get(handles.ui_table, 'Data'); - if ~isempty(currentData), currentData.Select(:) = false; end - newRow{1} = true; % Select the new row - set(handles.ui_table, 'Data', [currentData; newRow]); - end - - function replaceLastTableRow(handles, newRow) - % Replaces the last row of the table and selects it. - currentData = get(handles.ui_table, 'Data'); - if ~isempty(currentData) - currentData.Select(:) = false; - newRow{1} = true; % Select the new row - currentData(end,:) = newRow; - set(handles.ui_table, 'Data', currentData); - end - end - - function updateLivePlots(state, data, handles, config, flags) - % Calls the individual plot updaters based on checkbox flags. - if flags.psych, updatePsychometricPlot(handles.axes_h.live_psych, handles.ui_table, config); end - if flags.hit, updateHitRatePlot(handles.axes_h.live_hitrate, state.blockStatsHistory); end - if flags.stim, updateStimulusHistogram(handles.axes_h.live_stim, data, handles.ui_table, config); end - end - - function updatePsychometricPlot(ax, ui_table_handle, config) - % Redraws the live psychometric plot from data in the table. - allData = get(ui_table_handle, 'Data'); - selectedData = allData(allData.Select, :); - cla(ax, 'reset'); hold(ax, 'on'); - - xline(ax, config.true_mu, '--k', 'LineWidth', 1.5, 'HandleVisibility', 'off'); - yline(ax, 0.5, ':', 'Color', [0.5 0.5 0.5], 'HandleVisibility', 'off'); - - if ~isempty(selectedData) - num_to_plot = height(selectedData); - colors = createTemporalColormap(num_to_plot); - - psychometricFun = @(params, x) params(3) + (1 - params(3) - params(4)) ./ (1 + exp(-(x - params(1)) / params(2))); - xGrid = linspace(config.stimuli_range(1), config.stimuli_range(2), 300)'; - - for i = 1:num_to_plot - row = selectedData(i, :); - - fitParams = [row.CalBoundary, row.Slope, row.LapseA, row.LapseB]; - if any(isnan(fitParams)), continue; end - - y_curve = psychometricFun(fitParams, xGrid); - - alpha = 0.4; width = 1.5; - if i == num_to_plot, alpha = 0.9; width = 2.5; end - - plot(ax, xGrid, y_curve, 'Color', [colors(i,:), alpha], 'LineWidth', width, 'DisplayName', sprintf('Block %d', i)); - xline(ax, row.CalBoundary, '-', 'Color', [colors(i,:), alpha], 'LineWidth', width-0.5, 'HandleVisibility', 'off'); - end - legend(ax, 'show', 'Location', 'southeast'); - end - grid(ax, 'on'); hold(ax, 'off'); - ylabel(ax, 'P(Choice)'); title(ax, 'Live Psychometric Curves'); - end - - function updateHitRatePlot(ax, blockStats) - % Redraws the live hit rate plot from the block history. - if isempty(blockStats), return; end - - hr_overall = [blockStats.hitRates.overall]; - hr_left = [blockStats.hitRates.left]; - hr_right = [blockStats.hitRates.right]; - - cla(ax, 'reset'); hold(ax, 'on'); - x_axis = 1:numel(hr_overall); - plot(ax, x_axis, hr_overall, '-ok', 'LineWidth', 2, 'DisplayName', 'Overall'); - plot(ax, x_axis, hr_left, '--ob', 'LineWidth', 1.5, 'DisplayName', 'Left'); - plot(ax, x_axis, hr_right, '--or', 'LineWidth', 1.5, 'DisplayName', 'Right'); - hold(ax, 'off'); legend(ax, 'show', 'Location', 'southeast'); - xlim(ax, [0.5, max(10, numel(hr_overall) + 0.5)]); ylim(ax, [0 100]); - xlabel(ax, 'Block Number'); ylabel(ax, 'Hit %'); - title(ax, 'Live Hit Rate per Block'); - end - - function updateStimulusHistogram(ax, data, ui_table_handle, config) - % Redraws the live stimulus histogram for the latest block. - table_data = get(ui_table_handle, 'Data'); - if isempty(table_data), return; end - latest_block_data = table_data(end, :); - - indices = latest_block_data.Start_trial:latest_block_data.End_trial; - valid_mask = ~isnan(data.hit_history(indices)); - stim_valid = data.stim_history(indices(valid_mask)); - hit_valid = data.hit_history(indices(valid_mask)); - - cla(ax, 'reset'); hold(ax, 'on'); - histogram(ax, stim_valid(hit_valid == 0), 'BinMethod', 'auto', 'FaceColor', [0.9 0.2 0.1], 'DisplayName', 'Incorrect'); - histogram(ax, stim_valid(hit_valid == 1), 'BinMethod', 'auto', 'FaceColor', [0 0.65 0], 'DisplayName', 'Correct'); - xline(ax, config.true_mu, '--k', 'Boundary', 'LineWidth', 2); - hold(ax, 'off'); legend(ax, 'Location', 'northwest'); - title(ax, sprintf('Stimulus Dist. (Block %d)', size(table_data, 1))); - xlabel(ax, 'Stimulus Value'); ylabel(ax, 'Trial Count'); - end - - %% CUSTOM PLOTTING HELPERS - function plotCustomPsychometric(ax, stim_fit, side_fit, hit_fit, config, rule) - physical_response = zeros(size(hit_fit)); - physical_response(hit_fit == 1) = side_fit(hit_fit == 1); - physical_response(hit_fit == 0) = 1 - side_fit(hit_fit == 0); - - response_for_fitting = physical_response; - y_label = 'P(Right)'; - - if contains(rule, 'Left', 'IgnoreCase', true) - response_for_fitting = 1 - physical_response; - y_label = 'P(Left)'; - end - - options.MinTrials = 10; - [y_pred, fitParams, ~, ~] = realtimepsychometricFit(stim_fit, response_for_fitting, config.stimuli_range, options); - - cla(ax, 'reset'); hold(ax, 'on'); - xGrid = linspace(config.stimuli_range(1), config.stimuli_range(2), 300)'; - plot(ax, xGrid, y_pred, 'r-', 'LineWidth', 2, 'DisplayName', 'Fitted Curve'); - xline(ax, config.true_mu, '--k', 'LineWidth', 1.5, 'DisplayName', 'True Boundary'); - xline(ax, fitParams(1), '--b', 'LineWidth', 1.5, 'DisplayName', 'Calculated'); - yline(ax, 0.5, ':', 'Color', [0.5 0.5 0.5]); - grid(ax, 'on'); hold(ax, 'off'); - legend(ax); title(ax, sprintf('Custom Fit (Mu=%.2f)', fitParams(1))); - xlabel(ax, 'Stimulus'); ylabel(ax, y_label); - end - - function plotCustomHitRates(ax, hit_fit, side_fit, custom_range, blockStats) - cla(ax, 'reset'); hold(ax, 'on'); - - hr_custom.overall = 100 * sum(hit_fit == 1) / numel(hit_fit); - hr_custom.left = 100 * sum(hit_fit(side_fit==0)==1) / sum(side_fit==0); - hr_custom.right = 100 * sum(hit_fit(side_fit==1)==1) / sum(side_fit==1); - - relevant_blocks = []; - for i = 1:numel(blockStats) - block_indices = blockStats(i).indices; - if min(block_indices) >= custom_range(1) && max(block_indices) <= custom_range(2) - relevant_blocks = [relevant_blocks, blockStats(i)]; - end - end - - if ~isempty(relevant_blocks) - std_dev.overall = std([relevant_blocks.hitRates.overall], 'omitnan'); - std_dev.left = std([relevant_blocks.hitRates.left], 'omitnan'); - std_dev.right = std([relevant_blocks.hitRates.right], 'omitnan'); - else - std_dev.overall = 0; std_dev.left = 0; std_dev.right = 0; - end - - cats = categorical({'Overall', 'Left', 'Right'}); - errorbar(ax, cats, [hr_custom.overall, hr_custom.left, hr_custom.right], ... - [std_dev.overall, std_dev.left, std_dev.right], ... - 'o', 'MarkerSize', 8, 'CapSize', 15, 'LineWidth', 1.5); - - ylabel(ax, 'Hit %'); title(ax, 'Hit Rates (w/ Block STD)'); - ylim(ax, [0 105]); grid(ax, 'on'); - end - - function plotCustomStimulusHistogram(ax, stim_fit, hit_fit, custom_range, blockStats, config) - cla(ax, 'reset'); hold(ax, 'on'); - - left_edges = linspace(min(config.stimuli_range), config.true_mu, 6); - right_edges = linspace(config.true_mu, max(config.stimuli_range), 6); - bin_edges = unique([left_edges, right_edges]); - bin_centers = (bin_edges(1:end-1) + bin_edges(2:end)) / 2; - - counts_custom.correct = histcounts(stim_fit(hit_fit == 1), bin_edges); - counts_custom.incorrect = histcounts(stim_fit(hit_fit == 0), bin_edges); - - relevant_blocks = []; - for i = 1:numel(blockStats) - block_indices = blockStats(i).indices; - if min(block_indices) >= custom_range(1) && max(block_indices) <= custom_range(2) - relevant_blocks = [relevant_blocks, blockStats(i)]; - end - end - - if ~isempty(relevant_blocks) - counts_matrix_corr = vertcat(relevant_blocks.stimCounts.correct); - counts_matrix_incorr = vertcat(relevant_blocks.stimCounts.incorrect); - - mean_corr = mean(counts_matrix_corr, 1); - std_corr = std(counts_matrix_corr, 0, 1); - mean_incorr = mean(counts_matrix_incorr, 1); - std_incorr = std(counts_matrix_incorr, 0, 1); - - fill(ax, [bin_centers, fliplr(bin_centers)], [mean_corr - std_corr, fliplr(mean_corr + std_corr)], ... - [0 0.65 0], 'FaceAlpha', 0.2, 'EdgeColor', 'none', 'DisplayName', 'Correct (Block STD)'); - fill(ax, [bin_centers, fliplr(bin_centers)], [mean_incorr - std_incorr, fliplr(mean_incorr + std_incorr)], ... - [0.9 0.2 0.1], 'FaceAlpha', 0.2, 'EdgeColor', 'none', 'DisplayName', 'Incorrect (Block STD)'); - end - - plot(ax, bin_centers, counts_custom.correct, '-o', 'Color', [0 0.65 0], 'LineWidth', 2, 'DisplayName', 'Correct (Custom)'); - plot(ax, bin_centers, counts_custom.incorrect, '-o', 'Color', [0.9 0.2 0.1], 'LineWidth', 2, 'DisplayName', 'Incorrect (Custom)'); - - xline(ax, config.true_mu, '--k', 'Boundary', 'LineWidth', 2); - hold(ax, 'off'); legend(ax, 'Location', 'northwest'); - title(ax, 'Stimulus Distribution (w/ Block STD)'); - xlabel(ax, 'Stimulus Value'); ylabel(ax, 'Trial Count'); - end - - %% CONTEXT PLOTTING HELPERS - function plotContextPsychometric(ax, psych_data, config, colors) - cla(ax, 'reset'); hold(ax, 'on'); - xGrid = linspace(config.stimuli_range(1), config.stimuli_range(2), 300)'; - - xline(ax, config.true_mu, '--k', 'LineWidth', 1.5, 'DisplayName', 'True Boundary'); - yline(ax, 0.5, ':', 'Color', [0.5 0.5 0.5]); - - contains_left_rule = false; - contains_right_rule = false; - - for i = 1:numel(psych_data) - if isempty(psych_data{i}), continue; end - - if contains(psych_data{i}.rule, 'Left', 'IgnoreCase', true) - contains_left_rule = true; - else - contains_right_rule = true; - end - - plot(ax, xGrid, psych_data{i}.y_pred, '-', 'Color', colors(i,:), 'LineWidth', 2, 'DisplayName', sprintf('Context %d', i)); - xline(ax, psych_data{i}.fitParams(1), '--', 'Color', colors(i,:), 'LineWidth', 1.5, 'HandleVisibility', 'off'); - end - - if contains_left_rule && ~contains_right_rule, ylabel(ax, 'P(Left)'); - elseif ~contains_left_rule && contains_right_rule, ylabel(ax, 'P(Right)'); - else, ylabel(ax, 'P(Choice)'); end - - grid(ax, 'on'); hold(ax, 'off'); - legend(ax, 'show', 'Location', 'southeast'); - title(ax, 'Contextual Psychometric Fits'); - xlabel(ax, 'Stimulus'); - end - - function plotContextHitRates(ax, hit_rate_data, hit_rate_std, colors) - cla(ax, 'reset'); hold(ax, 'on'); - - if isempty(hit_rate_data), return; end - - num_contexts = size(hit_rate_data, 1); - - b = bar(ax, hit_rate_data', 'grouped'); - - for i = 1:num_contexts - b(i).FaceColor = colors(i,:); - x_coords = b(i).XData + b(i).XOffset; - errorbar(ax, x_coords, hit_rate_data(i,:), hit_rate_std(i,:), 'k', 'linestyle', 'none', 'CapSize', 4); - end - - ax.XTickLabel = {'Overall', 'Left', 'Right'}; - ylabel(ax, 'Hit %'); - title(ax, 'Contextual Hit Rates (w/ Block STD)'); - ylim(ax, [0 105]); - grid(ax, 'on'); - - legend_labels = arrayfun(@(x) sprintf('Context %d', x), 1:num_contexts, 'UniformOutput', false); - legend(ax, legend_labels, 'Location', 'northeastoutside'); - end - - function plotContextStimulusHistogram(ax, stim_hist_data, config, colors) - cla(ax, 'reset'); hold(ax, 'on'); - - left_edges = linspace(min(config.stimuli_range), config.true_mu, 6); - right_edges = linspace(config.true_mu, max(config.stimuli_range), 6); - bin_centers = (bin_edges(1:end-1) + bin_edges(2:end)) / 2; - - for i = 1:numel(stim_hist_data) - if isempty(stim_hist_data{i}), continue; end - - if isfield(stim_hist_data{i}, 'mean_corr') - fill(ax, [bin_centers, fliplr(bin_centers)], [stim_hist_data{i}.mean_corr - stim_hist_data{i}.std_corr, fliplr(stim_hist_data{i}.mean_corr + stim_hist_data{i}.std_corr)], ... - colors(i,:), 'FaceAlpha', 0.15, 'EdgeColor', 'none'); - fill(ax, [bin_centers, fliplr(bin_centers)], [stim_hist_data{i}.mean_incorr - stim_hist_data{i}.std_incorr, fliplr(stim_hist_data{i}.mean_incorr + stim_hist_data{i}.std_incorr)], ... - colors(i,:), 'FaceAlpha', 0.15, 'EdgeColor', 'none'); - end - - plot(ax, bin_centers, stim_hist_data{i}.correct, '-o', 'Color', colors(i,:), 'LineWidth', 2, 'DisplayName', sprintf('Correct C%d', i)); - plot(ax, bin_centers, stim_hist_data{i}.incorrect, ':x', 'Color', colors(i,:), 'LineWidth', 1.5, 'DisplayName', sprintf('Incorrect C%d', i)); - end - - xline(ax, config.true_mu, '--k', 'Boundary', 'LineWidth', 2); - hold(ax, 'off'); - legend(ax, 'show', 'Location', 'northwest'); - title(ax, 'Contextual Stimulus Distributions'); - xlabel(ax, 'Stimulus Value'); - ylabel(ax, 'Trial Count'); - end - - - %% GENERAL UTILITY FUNCTIONS - function [y_pred, fitParams, methodUsed, fitStatus] = realtimepsychometricFit(stim, resp, rangeStim, options) - % realtimepsychometricFit Robust real-time psychometric fitting and plotting. - % - % This function fits a 4-parameter logistic psychometric function. It includes - % internal checks for data quality and fitting stability. - % - % Inputs: - % stim - vector of stimulus values. - % response - binary response vector (0 or 1). - % rangeStim - 1x2 or 1x3 vector for the stimulus grid [min, max]. - % options - (Optional) struct with fields: - % .MinTrials - Min trials to attempt fit (default: 15). - - % .LapseUB - Upper bound for lapse rates (default: 0.1). - % .StdTol - Tolerance for stimulus std dev (default: 1e-6). - % .SlopeTol - Tolerance for slope parameter (default: 1e-5). - % - % Outputs: - % y_pred - Predicted y-values on a grid across rangeStim. - % fitParams - [mu, sigma, lapseL, lapseR] fitted parameters. - % methodUsed - String indicating the final fitting method used. - % fitStatus - String providing information on the fit quality/outcome. - - %% 1. Argument Handling & Pre-computation Guard Clauses - if nargin < 4, options = struct(); end - if ~isfield(options, 'MinTrials'), options.MinTrials = 15; end - if ~isfield(options, 'LapseUB'), options.LapseUB = 0.1; end - if ~isfield(options, 'StdTol'), options.StdTol = 1e-6; end - if ~isfield(options, 'SlopeTol'), options.SlopeTol = 1e-5; end - fitParams = [nan,nan,nan,nan]; y_pred = []; fitStatus = 'Success'; % Assume success initially - - % Ensure both inputs are vectors and have the same number of elements - - if ~isvector(stim) || ~isvector(resp) - methodUsed = 'Fit Canceled'; - fitStatus = 'Inputs `stim` and `resp` must be vectors.'; - return; - end - if numel(stim) ~= numel(resp) - methodUsed = 'Fit Canceled'; - fitStatus = 'Inputs `stim` and `resp` must have the same number of elements.'; - return; - end - - % Enforce column vector orientation for consistency with fitting functions. - % The (:) operator robustly reshapes any vector into a column vector. - stim = stim(:); - resp = resp(:); - - % GUARD: Check for minimum number of trials - if numel(stim) < options.MinTrials, methodUsed = 'Fit Canceled'; fitStatus = sprintf('Insufficient trials (n=%d, min=%d)', numel(stim), options.MinTrials); return; end - % GUARD: Check for stimulus variance - if std(stim) < options.StdTol, methodUsed = 'Fit Canceled'; fitStatus = 'Insufficient stimulus variance'; return; end - - %% 2. Initial Fit (Ridge) - stim_std = (stim - mean(stim)) / std(stim); methodUsed = 'ridge'; - try - % --- Ridge logistic fit for mu and sigma --- - [B, FitInfo] = lassoglm(stim_std, resp, 'binomial', 'Alpha', 1e-6, 'Lambda', 0.1); - b0 = FitInfo.Intercept; b1 = B(1); - % GUARD: Check for near-zero or excessively large slope from ridge fit - if abs(b1) < options.SlopeTol, throw(MException('MyFit:ZeroSlope', 'Initial ridge fit found no slope.')); end - if abs(b1) > 10 % Check for quasi-perfect separation - throw(MException('MyFit:SteepSlope', 'Initial ridge fit is too steep.')); - end - mu = -b0 / b1 * std(stim) + mean(stim); sigma = std(stim) / b1; - % Residual lapse estimate for initialization - predTrain = 1 ./ (1 + exp(-(b0 + b1 * stim_std))); - lapseEstimate = mean(abs(predTrain - resp)); - lapseL = min(max(lapseEstimate * 1.2, 0), options.LapseUB); lapseR = lapseL; - catch ME - % --- Robust fallback if ridge fit fails for any reason --- - methodUsed = 'robust'; fitStatus = sprintf('Switched to robust fit. Reason: %s', ME.message); - try - brob = robustfit(stim, resp, 'logit'); - % GUARD: Check for near-zero slope from robust fit - if abs(brob(2)) < options.SlopeTol, methodUsed = 'Fit Failed'; fitStatus = 'Could not find a slope.'; return; end - mu = -brob(1) / brob(2); sigma = 1 / brob(2); - lapseL = 0.02; lapseR = 0.02; % Use fixed lapse guesses for robust fallback - catch, methodUsed = 'Fit Failed'; fitStatus = 'Robustfit also failed.'; return; - end - end - %% 3. Final Nonlinear Fit (lsqcurvefit) - psychometricFun = @(params, x) params(3) + (1 - params(3) - params(4)) ./ (1 + exp(-(x - params(1)) / params(2))); - % Use a slightly wider range for bounds to avoid railing issues - stim_min = min(rangeStim); stim_max = max(rangeStim); range_width = stim_max - stim_min; - init = [mu, sigma, lapseL, lapseR]; - lb = [stim_min - 0.1*range_width, 0.1, 0, 0]; - ub = [stim_max + 0.1*range_width, 15, options.LapseUB, options.LapseUB]; - % Constrain initial guess to be within bounds - init(1) = max(min(init(1), ub(1)), lb(1)); init(2) = max(min(init(2), ub(2)), lb(2)); - optimOpts = optimset('Display', 'off'); - fitParams = lsqcurvefit(psychometricFun, init, stim, resp, lb, ub, optimOpts); - - %% 4. Post-Fit Sanity Checks & Prediction - % CHECK: Did the fit "rail" against the stimulus range bounds? - bound_tolerance = 0.01 * range_width; - % CHECK: Did the lapse rates hit their upper bound? - if (fitParams(1) <= lb(1) + bound_tolerance) || (fitParams(1) >= ub(1) - bound_tolerance), fitStatus = 'Warning: Threshold at edge of range.'; end - if (fitParams(3) >= options.LapseUB*0.99) || (fitParams(4) >= options.LapseUB*0.99), fitStatus = 'Warning: Lapse rate at upper bound.'; end - xGrid = linspace(stim_min, stim_max, 300)'; - y_pred = psychometricFun(fitParams, xGrid); - end - - function cmap = createTemporalColormap(n_colors) - if n_colors == 0, cmap = []; return; end - if n_colors == 1, cmap = [0.8 0 0]; return; end % A single dark red - % Create a high-contrast colormap for a few items - if n_colors <= 5 - cmap = [0.8 0.1 0.1; % Red - 0.1 0.5 0.8; % Blue - 0.1 0.7 0.2; % Green - 0.7 0.2 0.7; % Purple - 0.9 0.6 0.0]; % Orange - cmap = cmap(1:n_colors, :); - else % Fallback for more colors - h = linspace(0.6, 0, n_colors)'; - s = linspace(0.8, 1, n_colors)'; - v = linspace(0.7, 1, n_colors)'; - cmap = hsv2rgb([h, s, v]); - end - end - -end diff --git a/Protocols/@ArpitSoundCatContinuous/private/RealTimeAnalysis.m b/Protocols/@ArpitSoundCatContinuous/private/RealTimeAnalysis.m index 3087c8b4..6bc4aa1e 100644 --- a/Protocols/@ArpitSoundCatContinuous/private/RealTimeAnalysis.m +++ b/Protocols/@ArpitSoundCatContinuous/private/RealTimeAnalysis.m @@ -18,7 +18,11 @@ % .blockStatsHistory (struct array): Stores analysis results for each block. % .block_count (double): Counter for the number of blocks analyzed. % .last_analyzed_valid_trial (double): Counter for the last valid trial included in an analysis. -% +% .context_blocks (double): array to keep track of the blocks when the context was switched +% .table_row_editable (double): array same size as the number of rows in table decides which row the user can +% edit. Its a sanity check so that user doesn't select custom or context rows +% +% % data (struct): Contains the complete, read-only data histories for the session. % .hit_history (vector): History of hits (1), misses (0), or NaN. % .previous_sides (vector): History of sides presented (e.g., 0 for left, 1 for right). @@ -50,6 +54,7 @@ % For 'context': % varargin{1} (struct): The flags struct (as above). % varargin{2} (cell): A cell array of trial ranges, e.g., {[s1,e1], [s2,e2]}. +% varargin{3} (cell): A cell array of context names, e.g., {'Hard A', 'Uniform', 'Hard B'}. % For 'evaluate': % varargin{1} (cell): A cell array of trial ranges, e.g., {[s1,e1], [s2,e2]}. % @@ -65,6 +70,29 @@ % .right_correct_percent % .left_correct_percent +% Ensure that the core data vectors are of the same length to prevent indexing errors. +% This can happen if the session is interrupted mid-trial. + try + len_hit = numel(data.hit_history); + len_sides = numel(data.previous_sides); + len_stim = numel(data.stim_history); + + min_len = min([len_hit, len_sides, len_stim]); + + if len_hit > min_len || len_sides > min_len || len_stim > min_len + warning('RealTimeAnalysis:DataMismatch', ... + 'Data history vectors have mismatched lengths. Truncating to the shortest length (%d).', min_len); + + data.hit_history = data.hit_history(1:min_len); + data.previous_sides = data.previous_sides(1:min_len); + data.stim_history = data.stim_history(1:min_len); + end + catch ME + warning('RealTimeAnalysis:DataIntegrityError', 'Could not perform data integrity check: %s', ME.message); + end + % --- End of Data Integrity Check --- + + try switch lower(action) % ================================================================= @@ -96,15 +124,18 @@ if numel(varargin) < 1, error('context_switch action requires a flags struct.'); end flags = varargin{1}; + merge_threshold = round(2 * config.trials_per_block / 3); + lastAnalyzed = state.last_analyzed_valid_trial; validTrials = sum(~isnan(data.hit_history)); remaining_valid_trials = validTrials - lastAnalyzed; + state.context_blocks(end + 1) = state.block_count; if remaining_valid_trials > 0 valid_indices = find(~isnan(data.hit_history)); - new_valid_indices = valid_indices(valid_indices > find(valid_indices == lastAnalyzed, 1, 'last')); + new_valid_indices = valid_indices(lastAnalyzed + 1 : end); - if remaining_valid_trials < 20 && state.block_count > 0 + if remaining_valid_trials < merge_threshold && state.block_count > 0 last_block_indices = state.blockStatsHistory(end).indices; combined_indices = [last_block_indices, new_valid_indices']; @@ -115,7 +146,7 @@ state = reAnalyzeLastChunk(state, data, handles, config, dataBuffer, flags); - elseif remaining_valid_trials >= 20 + elseif remaining_valid_trials >= merge_threshold dataBuffer.stim = data.stim_history(new_valid_indices); dataBuffer.hit = data.hit_history(new_valid_indices); dataBuffer.side = data.previous_sides(new_valid_indices); @@ -160,6 +191,7 @@ [~, newRow] = processBlock(data, config, struct('stim', stim_fit, 'hit', hit_fit, 'side', side_fit, 'indices', indices)); newRow{1} = false; % Ensure it's not selected updateTable(handles, newRow); + state.table_row_editable(end+1) = false; if flags.psych, plotCustomPsychometric(handles.axes_h.custom_psych, stim_fit, side_fit, hit_fit, config, current_rule); end if flags.hit, plotCustomHitRates(handles.axes_h.custom_hitrate, hit_fit, side_fit, [start_idx, end_idx], state.blockStatsHistory); end @@ -172,6 +204,7 @@ if numel(varargin) < 2, error('Context action requires flags and a cell array of contexts.'); end flags = varargin{1}; contexts = varargin{2}; + contexts_names = varargin{3}; if ~iscell(contexts) || isempty(contexts), error('Contexts must be a non-empty cell array of [start, end] pairs.'); end @@ -204,13 +237,14 @@ [~, newRow] = processBlock(data, config, struct('stim', stim_fit, 'hit', hit_fit, 'side', side_fit, 'indices', indices)); newRow{1} = false; % Ensure it's not selected updateTable(handles, newRow); + state.table_row_editable(end+1) = false; physical_response = zeros(size(hit_fit)); physical_response(hit_fit == 1) = side_fit(hit_fit == 1); physical_response(hit_fit == 0) = 1 - side_fit(hit_fit == 0); response_for_fitting = physical_response; - if contains(current_rule, 'Left', 'IgnoreCase', true), response_for_fitting = 1 - response_for_fitting; end + if contains(string(current_rule), 'Left', 'IgnoreCase', true), response_for_fitting = 1 - response_for_fitting; end options.MinTrials = 10; [y_pred, fitParams, ~, ~] = realtimepsychometricFit(stim_fit, response_for_fitting, config.stimuli_range, options); @@ -235,11 +269,13 @@ end if ~isempty(relevant_blocks) - hit_rate_std(i, 1) = std([relevant_blocks.hitRates.overall], 'omitnan'); - hit_rate_std(i, 2) = std([relevant_blocks.hitRates.left], 'omitnan'); - hit_rate_std(i, 3) = std([relevant_blocks.hitRates.right], 'omitnan'); - counts_matrix_corr = vertcat(relevant_blocks.stimCounts.correct); - counts_matrix_incorr = vertcat(relevant_blocks.stimCounts.incorrect); + hit_rate_std(i, 1) = std(arrayfun(@(blk) blk.hitRates.overall, relevant_blocks), 'omitnan'); + hit_rate_std(i, 2) = std(arrayfun(@(blk) blk.hitRates.left, relevant_blocks), 'omitnan'); + hit_rate_std(i, 3) = std(arrayfun(@(blk) blk.hitRates.right, relevant_blocks), 'omitnan'); + correct_cells = arrayfun(@(s) s.stimCounts.correct(:)', relevant_blocks, 'UniformOutput', false); + counts_matrix_corr = cell2mat(correct_cells); + incorrect_cells = arrayfun(@(s) s.stimCounts.incorrect(:)', relevant_blocks, 'UniformOutput', false); + counts_matrix_incorr = cell2mat(incorrect_cells); stim_hist_data{i}.mean_corr = mean(counts_matrix_corr, 1); stim_hist_data{i}.std_corr = std(counts_matrix_corr, 0, 1); stim_hist_data{i}.mean_incorr = mean(counts_matrix_incorr, 1); @@ -249,9 +285,9 @@ end end - if flags.psych, plotContextPsychometric(handles.axes_h.custom_psych, psych_data, config, context_colors); end - if flags.hit, plotContextHitRates(handles.axes_h.custom_hitrate, hit_rate_data, hit_rate_std, context_colors); end - if flags.stim, plotContextStimulusHistogram(handles.axes_h.custom_stim, stim_hist_data, config, context_colors); end + if flags.psych, plotContextPsychometric(handles.axes_h.custom_psych, psych_data, config, context_colors,contexts_names); end + if flags.hit, plotContextHitRates(handles.axes_h.custom_hitrate, hit_rate_data, hit_rate_std, context_colors,contexts_names); end + if flags.stim, plotContextStimulusHistogram(handles.axes_h.custom_stim, stim_hist_data, config, context_colors,contexts_names); end % ================================================================= % EVALUATE ACTION @@ -290,7 +326,7 @@ physical_response(hit_fit == 0) = 1 - side_fit(hit_fit == 0); response_for_fitting = physical_response; - if contains(current_rule, 'Left', 'IgnoreCase', true), response_for_fitting = 1 - response_for_fitting; end + if contains(string(current_rule), 'Left', 'IgnoreCase', true), response_for_fitting = 1 - response_for_fitting; end options.MinTrials = 10; [~, fitParams, ~, ~] = realtimepsychometricFit(stim_fit, response_for_fitting, config.stimuli_range, options); @@ -316,16 +352,27 @@ catch ME % Create a more detailed error message including the line number. if ~isempty(ME.stack) - errorLocation = sprintf('File: %s, Function: %s, Line: %d', ... - ME.stack(1).file, ME.stack(1).name, ME.stack(1).line); + + % safe_filename = strrep(ME.stack(1).file, '\', '\\'); + % errorLocation = sprintf('File: %s, Function: %s, Line: %d', ... + % safe_filename, ME.stack(1).name, ME.stack(1).line); + + errorLocation = ['File: ' ME.stack(1).file ... + ', Function: ' ME.stack(1).name ... + ', Line: ' num2str(ME.stack(1).line)]; else errorLocation = 'Location not available in error stack.'; end - fullErrorMessage = sprintf('An error occurred in RealTimeAnalysisApp:\n Error: %s\n %s', ... - ME.message, errorLocation); + % fullErrorMessage = sprintf('An error occurred in RealTimeAnalysis:\n Error: %s\n %s', ... + % ME.message, errorLocation); - warning('RealTimeAnalysisApp:Error', fullErrorMessage); + fullErrorMessage = ['An error occurred in RealTimeAnalysis:' newline ... + ' Error: ' ME.message newline ... + ' ' errorLocation]; + + fullErrorMsg = strrep(fullErrorMessage, '\', '\\'); + warning('RealTimeAnalysis:Error', fullErrorMsg); if config.debug, rethrow(ME); end % In experiment mode, the function will gracefully return the original state. end @@ -338,14 +385,17 @@ function state = analyzeLiveChunk(state, data, handles, config, dataBuffer, flags) state.block_count = state.block_count + 1; [newBlockStat, newRow] = processBlock(data, config, dataBuffer); - state.blockStatsHistory = [state.blockStatsHistory, newBlockStat]; + state.blockStatsHistory = [state.blockStatsHistory, newBlockStat]; updateTable(handles, newRow); - + state.table_row_editable(end+1) = true; + + state.last_analyzed_valid_trial = sum(~isnan(data.hit_history)); + if strcmp(get(handles.main_fig, 'Visible'), 'on') updateLivePlots(state, data, handles, config, flags); end - state.last_analyzed_valid_trial = sum(~isnan(data.hit_history)); + end function state = reAnalyzeLastChunk(state, data, handles, config, dataBuffer, flags) @@ -353,11 +403,13 @@ state.blockStatsHistory(end) = newBlockStat; replaceLastTableRow(handles, newRow); + state.last_analyzed_valid_trial = sum(~isnan(data.hit_history)); + if strcmp(get(handles.main_fig, 'Visible'), 'on') updateLivePlots(state, data, handles, config, flags); end - state.last_analyzed_valid_trial = sum(~isnan(data.hit_history)); + end function [blockStat, tableRow] = processBlock(data, config, dataBuffer) @@ -368,7 +420,7 @@ current_rule = data.full_rule_history; response_for_fitting = physical_response; - if contains(current_rule, 'Left', 'IgnoreCase', true) + if contains(string(current_rule), 'Left', 'IgnoreCase', true) response_for_fitting = 1 - physical_response; end @@ -406,16 +458,12 @@ function updateTable(handles, newRow) currentData = get(handles.ui_table, 'Data'); - if newRow{1} == true && ~isempty(currentData) - currentData.Select(:) = false; - end set(handles.ui_table, 'Data', [currentData; newRow]); end function replaceLastTableRow(handles, newRow) currentData = get(handles.ui_table, 'Data'); if ~isempty(currentData) - currentData.Select(:) = false; newRow{1} = true; % Select the new row currentData(end,:) = newRow; set(handles.ui_table, 'Data', currentData); @@ -424,13 +472,20 @@ function replaceLastTableRow(handles, newRow) function updateLivePlots(state, data, handles, config, flags) if flags.psych, updatePsychometricPlot(handles.axes_h.live_psych, handles.ui_table, config); end - if flags.hit, updateHitRatePlot(handles.axes_h.live_hitrate, state.blockStatsHistory); end - if flags.stim, updateStimulusHistogram(handles.axes_h.live_stim, state, data, config); end + if flags.hit, updateHitRatePlot(handles.axes_h.live_hitrate,state.context_blocks, handles.ui_table); end + if flags.stim, updateStimulusHistogram(handles.axes_h.live_stim, state, data, config,handles.ui_table); end end function updatePsychometricPlot(ax, ui_table_handle, config) allData = get(ui_table_handle, 'Data'); - selectedData = allData(allData.Select, :); + if isempty(allData) + cla(ax, 'reset'); + return; + end + + logical_indices = allData.Select == 1; + selectedData = allData(logical_indices, :); + cla(ax, 'reset'); hold(ax, 'on'); xline(ax, config.true_mu, '--k', 'LineWidth', 1.5, 'HandleVisibility', 'off'); @@ -452,9 +507,10 @@ function updatePsychometricPlot(ax, ui_table_handle, config) y_curve = psychometricFun(fitParams, xGrid); alpha = 0.4; width = 1.5; + if i == num_to_plot, alpha = 0.9; width = 2.5; end - plot(ax, xGrid, y_curve, 'Color', [colors(i,:), alpha], 'LineWidth', width, 'DisplayName', sprintf('Block %d', i)); + plot(ax, xGrid, y_curve, 'Color', [colors(i,:), alpha], 'LineWidth', width, 'DisplayName', sprintf('Block (T %d)', row.Start_trial)); xline(ax, row.CalBoundary, '-', 'Color', [colors(i,:), alpha], 'LineWidth', width-0.5, 'HandleVisibility', 'off'); end legend(ax, 'show', 'Location', 'southeast'); @@ -463,32 +519,66 @@ function updatePsychometricPlot(ax, ui_table_handle, config) ylabel(ax, 'P(Choice)'); title(ax, 'Live Psychometric Curves'); end - function updateHitRatePlot(ax, blockStats) - if isempty(blockStats), return; end - - hr_overall = [blockStats.hitRates.overall]; - hr_left = [blockStats.hitRates.left]; - hr_right = [blockStats.hitRates.right]; + function updateHitRatePlot(ax, context_blocks, ui_table_handle) + allData = get(ui_table_handle, 'Data'); + if isempty(allData) + cla(ax, 'reset'); + return; + end - cla(ax, 'reset'); hold(ax, 'on'); - x_axis = 1:numel(hr_overall); + logical_mask = allData.Select == 1; + selectedData = allData(logical_mask, :); + + cla(ax, 'reset'); + if isempty(selectedData) + title(ax, 'Live Hit Rate per Block (Nothing Selected)'); + xlim(ax, [0.5, 10.5]); ylim(ax, [0 100]); grid(ax, 'on'); + return; + end + + hr_overall = selectedData.("Overall Hit %"); + hr_left = selectedData.("Left Hit %"); + hr_right = selectedData.("Right Hit %"); + + hold(ax, 'on'); + x_axis = 1:height(selectedData); plot(ax, x_axis, hr_overall, '-ok', 'LineWidth', 2, 'DisplayName', 'Overall'); plot(ax, x_axis, hr_left, '--ob', 'LineWidth', 1.5, 'DisplayName', 'Left'); plot(ax, x_axis, hr_right, '--or', 'LineWidth', 1.5, 'DisplayName', 'Right'); + % plotting context change + if length(context_blocks) > 1 + for k = 2:length(context_blocks) + xline(ax, context_blocks(k), '--k', 'LineWidth', 1.5, 'HandleVisibility', 'off'); + end + end hold(ax, 'off'); legend(ax, 'show', 'Location', 'southeast'); - xlim(ax, [0.5, max(10, numel(hr_overall) + 0.5)]); ylim(ax, [0 100]); - xlabel(ax, 'Block Number'); ylabel(ax, 'Hit %'); - title(ax, 'Live Hit Rate per Block'); + xlim(ax, [0.5, max(10, height(selectedData) + 0.5)]); ylim(ax, [0 100]); + xlabel(ax, 'Selected Block Number'); ylabel(ax, 'Hit %'); + title(ax, 'Live Hit Rate for Selected Blocks'); + grid(ax, 'on'); end - function updateStimulusHistogram(ax, state, data, config) - if state.block_count == 0, return; end + function updateStimulusHistogram(ax, state, data, config, ui_table_handle) + + allData = get(ui_table_handle, 'Data'); + if isempty(allData) || state.block_count == 0 + cla(ax, 'reset'); + return; + end + + logical_mask = allData.Select == 1; + selected_indices = find(logical_mask); % Get row numbers of selected blocks + cla(ax, 'reset'); + if isempty(selected_indices) + title(ax, 'Live Choice Distribution (Nothing Selected)'); + return; + end - n_blocks = state.block_count; - red_map = interp1([0 1], [1 0.7 0.7; 0.9 0.2 0.1], linspace(0, 1, n_blocks)); - green_map = interp1([0 1], [0.7 1 0.7; 0 0.65 0], linspace(0, 1, n_blocks)); + n_selected_blocks = numel(selected_indices); + red_map = interp1([0 1], [1 0.7 0.7; 0.9 0.2 0.1], linspace(0, 1, n_selected_blocks)); + green_map = interp1([0 1], [0.7 1 0.7; 0 0.65 0], linspace(0, 1, n_selected_blocks)); left_edges = linspace(min(config.stimuli_range), config.true_mu, 6); right_edges = linspace(config.true_mu, max(config.stimuli_range), 6); @@ -502,48 +592,47 @@ function updateStimulusHistogram(ax, state, data, config) max_count = 0; - for i = 1:n_blocks - block_stat = state.blockStatsHistory(i); - block_indices = block_stat.indices; - - multiplier = 1; - if i == n_blocks, multiplier = 2; end + for i = 1:n_selected_blocks + block_idx = selected_indices(i); % Use the index of the selected row + block_stat = state.blockStatsHistory(block_idx); + multiplier = 1; + if i == n_selected_blocks, multiplier = 2; end + yyaxis(ax, 'left'); plot(ax, bin_centers, block_stat.stimCounts.incorrect, '-', 'Color', red_map(i,:), 'LineWidth', multiplier * 1.5); plot(ax, bin_centers, block_stat.stimCounts.correct, '-', 'Color', green_map(i,:), 'LineWidth', multiplier * 1.5); max_count = max([max_count, block_stat.stimCounts.correct, block_stat.stimCounts.incorrect]); - + yyaxis(ax, 'right'); - valid_mask = ~isnan(data.hit_history(block_indices)); - stim_valid = data.stim_history(block_indices(valid_mask)); - hit_valid = data.hit_history(block_indices(valid_mask)); - + block_indices_raw = block_stat.indices; + valid_mask = ~isnan(data.hit_history(block_indices_raw)); + stim_valid = data.stim_history(block_indices_raw(valid_mask)); + hit_valid = data.hit_history(block_indices_raw(valid_mask)); stim_correct = stim_valid(hit_valid == 1); stim_incorrect = stim_valid(hit_valid == 0); - + jitter_base = (i - 1) * 0.2; jitter_incorrect = jitter_base + 0.08 * rand(size(stim_incorrect)); jitter_correct = jitter_base + 0.08 * rand(size(stim_correct)); - scatter(ax, stim_incorrect, jitter_incorrect, multiplier * 25, red_map(i,:), 'filled', 'MarkerFaceAlpha', multiplier * 0.4); scatter(ax, stim_correct, jitter_correct, multiplier * 25, green_map(i,:), 'filled', 'MarkerFaceAlpha', multiplier * 0.4); end - + yyaxis(ax, 'left'); ylabel(ax, 'Trial Count (Binned)'); ax.YColor = 'k'; ylim(ax, [0, max(1, max_count * 1.1)]); - + yyaxis(ax, 'right'); - ylim(ax, [0, n_blocks * 0.2 + 0.1]); + ylim(ax, [0, n_selected_blocks * 0.2 + 0.1]); ax.YTick = []; ax.YColor = 'none'; - + hold(ax, 'off'); xline(ax, config.true_mu, '--k', 'Boundary', 'LineWidth', 2); xlabel(ax, 'Stimulus Value'); - title(ax, 'Live Stimulus Distribution'); + title(ax, 'Live Choice Distribution for Selected Blocks'); yyaxis(ax, 'left'); end @@ -556,7 +645,7 @@ function plotCustomPsychometric(ax, stim_fit, side_fit, hit_fit, config, rule) response_for_fitting = physical_response; y_label = 'P(Right)'; - if contains(rule, 'Left', 'IgnoreCase', true) + if contains(string(rule), 'Left', 'IgnoreCase', true) response_for_fitting = 1 - physical_response; y_label = 'P(Left)'; end @@ -569,7 +658,7 @@ function plotCustomPsychometric(ax, stim_fit, side_fit, hit_fit, config, rule) plot(ax, xGrid, y_pred, 'r-', 'LineWidth', 2, 'DisplayName', 'Fitted Curve'); xline(ax, config.true_mu, '--k', 'LineWidth', 1.5, 'DisplayName', 'True Boundary'); xline(ax, fitParams(1), '--b', 'LineWidth', 1.5, 'DisplayName', 'Calculated'); - yline(ax, 0.5, ':', 'Color', [0.5 0.5 0.5]); + yline(ax, 0.5, ':', 'Color', [0.5 0.5 0.5],'HandleVisibility', 'off'); grid(ax, 'on'); hold(ax, 'off'); legend(ax); title(ax, sprintf('Custom Fit (Mu=%.2f)', fitParams(1))); xlabel(ax, 'Stimulus'); ylabel(ax, y_label); @@ -589,11 +678,16 @@ function plotCustomHitRates(ax, hit_fit, side_fit, custom_range, blockStats) relevant_blocks = [relevant_blocks, blockStats(i)]; end end - + if ~isempty(relevant_blocks) - std_dev.overall = std([relevant_blocks.hitRates.overall], 'omitnan'); - std_dev.left = std([relevant_blocks.hitRates.left], 'omitnan'); - std_dev.right = std([relevant_blocks.hitRates.right], 'omitnan'); + overall_values = arrayfun(@(blk) blk.hitRates.overall, relevant_blocks); + left_values = arrayfun(@(blk) blk.hitRates.left, relevant_blocks); + right_values = arrayfun(@(blk) blk.hitRates.right, relevant_blocks); + + % Now calculate the standard deviation on the resulting vectors + std_dev.overall = std(overall_values, 'omitnan'); + std_dev.left = std(left_values, 'omitnan'); + std_dev.right = std(right_values, 'omitnan'); else std_dev.overall = 0; std_dev.left = 0; std_dev.right = 0; end @@ -627,36 +721,45 @@ function plotCustomStimulusHistogram(ax, stim_fit, hit_fit, custom_range, blockS end if ~isempty(relevant_blocks) - counts_matrix_corr = vertcat(relevant_blocks.stimCounts.correct); - counts_matrix_incorr = vertcat(relevant_blocks.stimCounts.incorrect); + correct_cells = arrayfun(@(blk) blk.stimCounts.correct, relevant_blocks,'UniformOutput',false); + incorrect_cells = arrayfun(@(blk) blk.stimCounts.incorrect, relevant_blocks,'UniformOutput',false); + + counts_matrix_corr = cell2mat(correct_cells); + counts_matrix_incorr = cell2mat(incorrect_cells); mean_corr = mean(counts_matrix_corr, 1); std_corr = std(counts_matrix_corr, 0, 1); mean_incorr = mean(counts_matrix_incorr, 1); std_incorr = std(counts_matrix_incorr, 0, 1); - fill(ax, [bin_centers, fliplr(bin_centers)], [mean_corr - std_corr, fliplr(mean_corr + std_corr)], ... - [0 0.65 0], 'FaceAlpha', 0.2, 'EdgeColor', 'none', 'DisplayName', 'Correct (Block STD)'); - fill(ax, [bin_centers, fliplr(bin_centers)], [mean_incorr - std_incorr, fliplr(mean_incorr + std_incorr)], ... - [0.9 0.2 0.1], 'FaceAlpha', 0.2, 'EdgeColor', 'none', 'DisplayName', 'Incorrect (Block STD)'); + if sum(std_corr) > eps + fill(ax, [bin_centers, fliplr(bin_centers)], [mean_corr - std_corr, fliplr(mean_corr + std_corr)], ... + [0 0.65 0], 'FaceAlpha', 0.2, 'EdgeColor', 'none', 'DisplayName', 'Correct (Block STD)'); + end + + % --- Plot the fill area for INCORRECT trials, only if there is variance --- + if sum(std_incorr) > eps + fill(ax, [bin_centers, fliplr(bin_centers)], [mean_incorr - std_incorr, fliplr(mean_incorr + std_incorr)], ... + [0.9 0.2 0.1], 'FaceAlpha', 0.2, 'EdgeColor', 'none', 'DisplayName', 'Incorrect (Block STD)'); + end end plot(ax, bin_centers, counts_custom.correct, '-o', 'Color', [0 0.65 0], 'LineWidth', 2, 'DisplayName', 'Correct (Custom)'); plot(ax, bin_centers, counts_custom.incorrect, '-o', 'Color', [0.9 0.2 0.1], 'LineWidth', 2, 'DisplayName', 'Incorrect (Custom)'); - xline(ax, config.true_mu, '--k', 'Boundary', 'LineWidth', 2); + xline(ax, config.true_mu, '--k', 'Boundary', 'LineWidth', 2,'HandleVisibility', 'off'); hold(ax, 'off'); legend(ax, 'Location', 'northwest'); - title(ax, 'Stimulus Distribution (w/ Block STD)'); + title(ax, 'Choice Distribution (w/ Block STD)'); xlabel(ax, 'Stimulus Value'); ylabel(ax, 'Trial Count'); end %% CONTEXT PLOTTING HELPERS - function plotContextPsychometric(ax, psych_data, config, colors) + function plotContextPsychometric(ax, psych_data, config, colors,context_names) cla(ax, 'reset'); hold(ax, 'on'); xGrid = linspace(config.stimuli_range(1), config.stimuli_range(2), 300)'; xline(ax, config.true_mu, '--k', 'LineWidth', 1.5, 'DisplayName', 'True Boundary'); - yline(ax, 0.5, ':', 'Color', [0.5 0.5 0.5]); + yline(ax, 0.5, ':', 'Color', [0.5 0.5 0.5], 'HandleVisibility', 'off'); contains_left_rule = false; contains_right_rule = false; @@ -664,13 +767,13 @@ function plotContextPsychometric(ax, psych_data, config, colors) for i = 1:numel(psych_data) if isempty(psych_data{i}), continue; end - if contains(psych_data{i}.rule, 'Left', 'IgnoreCase', true) + if contains(string(psych_data{i}.rule), 'Left', 'IgnoreCase', true) contains_left_rule = true; else contains_right_rule = true; end - plot(ax, xGrid, psych_data{i}.y_pred, '-', 'Color', colors(i,:), 'LineWidth', 2, 'DisplayName', sprintf('Context %d', i)); + plot(ax, xGrid, psych_data{i}.y_pred, '-', 'Color', colors(i,:), 'LineWidth', 2, 'DisplayName', context_names{i}); xline(ax, psych_data{i}.fitParams(1), '--', 'Color', colors(i,:), 'LineWidth', 1.5, 'HandleVisibility', 'off'); end @@ -684,46 +787,68 @@ function plotContextPsychometric(ax, psych_data, config, colors) xlabel(ax, 'Stimulus'); end - function plotContextHitRates(ax, hit_rate_data, hit_rate_std, colors) + function plotContextHitRates(ax, hit_rate_data, hit_rate_std, colors,context_names) cla(ax, 'reset'); hold(ax, 'on'); if isempty(hit_rate_data), return; end num_contexts = size(hit_rate_data, 1); - + num_groups = size(hit_rate_data, 2); % Should be 3 for Overall, Left, Right + b = bar(ax, hit_rate_data', 'grouped'); + % Set colors for each context for i = 1:num_contexts b(i).FaceColor = colors(i,:); - x_coords = b(i).XData + b(i).XOffset; - errorbar(ax, x_coords, hit_rate_data(i,:), hit_rate_std(i,:), 'k', 'linestyle', 'none', 'CapSize', 4); end - + + % Calculate the x-positions for error bars + % For grouped bars, we need to calculate the offset for each group + group_width = min(0.8, num_contexts/(num_contexts + 1.5)); + + for i = 1:num_contexts + % Calculate x-coordinates for this context across all groups + x_offset = (-(num_contexts-1)/2 + (i-1)) * group_width/num_contexts; + x_coords = (1:num_groups) + x_offset; + + % Plot error bars for this context + errorbar(ax, x_coords, hit_rate_data(i,:), hit_rate_std(i,:), ... + 'k', 'linestyle', 'none', 'CapSize', 4, 'LineWidth', 1); + end + + ax.XTick = 1:num_groups; ax.XTickLabel = {'Overall', 'Left', 'Right'}; ylabel(ax, 'Hit %'); title(ax, 'Contextual Hit Rates (w/ Block STD)'); ylim(ax, [0 105]); grid(ax, 'on'); - legend_labels = arrayfun(@(x) sprintf('Context %d', x), 1:num_contexts, 'UniformOutput', false); - legend(ax, legend_labels, 'Location', 'northeastoutside'); + % legend_labels = arrayfun(@(x) sprintf('Context %d', x), 1:num_contexts, 'UniformOutput', false); + % legend_labels = context_names + % legend(ax, legend_labels, 'Location', 'northeastoutside'); + hold(ax, 'off'); end - function plotContextStimulusHistogram(ax, stim_hist_data, config, colors) + function plotContextStimulusHistogram(ax, stim_hist_data, config, colors,context_names) cla(ax, 'reset'); hold(ax, 'on'); left_edges = linspace(min(config.stimuli_range), config.true_mu, 6); right_edges = linspace(config.true_mu, max(config.stimuli_range), 6); + bin_edges = unique([left_edges, right_edges]); bin_centers = (bin_edges(1:end-1) + bin_edges(2:end)) / 2; for i = 1:numel(stim_hist_data) if isempty(stim_hist_data{i}), continue; end if isfield(stim_hist_data{i}, 'mean_corr') - fill(ax, [bin_centers, fliplr(bin_centers)], [stim_hist_data{i}.mean_corr - stim_hist_data{i}.std_corr, fliplr(stim_hist_data{i}.mean_corr + stim_hist_data{i}.std_corr)], ... - colors(i,:), 'FaceAlpha', 0.15, 'EdgeColor', 'none'); - fill(ax, [bin_centers, fliplr(bin_centers)], [stim_hist_data{i}.mean_incorr - stim_hist_data{i}.std_incorr, fliplr(stim_hist_data{i}.mean_incorr + stim_hist_data{i}.std_incorr)], ... - colors(i,:), 'FaceAlpha', 0.15, 'EdgeColor', 'none'); + if sum(stim_hist_data{i}.std_corr) > eps + fill(ax, [bin_centers, fliplr(bin_centers)], [stim_hist_data{i}.mean_corr - stim_hist_data{i}.std_corr, fliplr(stim_hist_data{i}.mean_corr + stim_hist_data{i}.std_corr)], ... + colors(i,:), 'FaceAlpha', 0.15, 'EdgeColor', 'none'); + end + if sum(stim_hist_data{i}.std_incorr) > eps + fill(ax, [bin_centers, fliplr(bin_centers)], [stim_hist_data{i}.mean_incorr - stim_hist_data{i}.std_incorr, fliplr(stim_hist_data{i}.mean_incorr + stim_hist_data{i}.std_incorr)], ... + colors(i,:), 'FaceAlpha', 0.15, 'EdgeColor', 'none'); + end end plot(ax, bin_centers, stim_hist_data{i}.correct, '-o', 'Color', colors(i,:), 'LineWidth', 2, 'DisplayName', sprintf('Correct C%d', i)); @@ -732,8 +857,8 @@ function plotContextStimulusHistogram(ax, stim_hist_data, config, colors) xline(ax, config.true_mu, '--k', 'Boundary', 'LineWidth', 2); hold(ax, 'off'); - legend(ax, 'show', 'Location', 'northwest'); - title(ax, 'Contextual Stimulus Distributions'); + % legend(ax, 'show', 'Location', 'northwest'); + title(ax, 'Contextual Choice Distributions'); xlabel(ax, 'Stimulus Value'); ylabel(ax, 'Trial Count'); end @@ -742,11 +867,12 @@ function plotContextStimulusHistogram(ax, stim_hist_data, config, colors) function dist_type = getDistributionType(data, indices, rule) % Determines the distribution type based on the rule and the % distributions for left and right sides within the given indices. - dist_left = unique(data.full_dist_left(indices)); - dist_right = unique(data.full_dist_right(indices)); - if numel(dist_left) > 1, dist_left = dist_left{1}; end - if numel(dist_right) > 1, dist_right = dist_right{1}; end + dist_left_cell = unique(data.full_dist_left(indices)); + dist_right_cell = unique(data.full_dist_right(indices)); + + dist_left = dist_left_cell{1}; + dist_right = dist_right_cell{1}; hard_dists = {'exponential', 'half-normal', 'sinusoidal'}; @@ -758,7 +884,7 @@ function plotContextStimulusHistogram(ax, stim_hist_data, config, colors) is_left_hard = ismember(dist_left, hard_dists); is_right_hard = ismember(dist_right, hard_dists); - if contains(rule, 'Right', 'IgnoreCase', true) % High stimulus values correspond to Right + if contains(string(rule), 'Right', 'IgnoreCase', true) % High stimulus values correspond to Right if is_right_hard && ~is_left_hard dist_type = 'hard high'; elseif ~is_right_hard && is_left_hard diff --git a/Protocols/@Rigtest_singletrial/Rigtest_singletrial.m b/Protocols/@Rigtest_singletrial/Rigtest_singletrial.m index 73b77e63..8bf794ed 100644 --- a/Protocols/@Rigtest_singletrial/Rigtest_singletrial.m +++ b/Protocols/@Rigtest_singletrial/Rigtest_singletrial.m @@ -243,10 +243,6 @@ runrats('rigtest_singletrial_is_complete'); return; - elseif nTrials > 0 && OpenEphys_Neuroblueprint('is_running') - OpenEphys_Neuroblueprint('manual_test_stopping'); - return; - elseif nTrials > 0 && NeuropixelNeuroblueprint('is_running') NeuropixelNeuroblueprint('manual_test_stopping'); return; From 04f50d97cd1fc32e3a7210dee6d5f0d5bcddd20c Mon Sep 17 00:00:00 2001 From: Arpit Date: Sun, 28 Sep 2025 16:26:02 +0100 Subject: [PATCH 154/164] added neuropixel helper module --- .../OpenEphys_MATLAB-SDK/NetworkControl.m | 179 ++++ .../NetworkEventsConsole.m | 47 ++ .../OpenEphysHTTPServer.m | 554 ++++++++++++ .../OpenEphys_MATLAB-SDK/README.md | 240 ++++++ .../SpikeGLX-MATLAB-SDK-main/.editorconfig | 8 + .../SpikeGLX-MATLAB-SDK-main/.gitignore | 61 ++ .../API/@SpikeGL/ChkConn.m | 34 + .../API/@SpikeGL/Close.m | 11 + .../API/@SpikeGL/ConsoleHide.m | 8 + .../API/@SpikeGL/ConsoleShow.m | 8 + .../API/@SpikeGL/Contents.m | 623 ++++++++++++++ .../API/@SpikeGL/DoGetCells.m | 17 + .../API/@SpikeGL/DoQuery.m | 20 + .../API/@SpikeGL/DoSimpleCmd.m | 14 + .../API/@SpikeGL/EnumDataDir.m | 15 + .../API/@SpikeGL/Fetch.m | 74 ++ .../API/@SpikeGL/FetchLatest.m | 42 + .../API/@SpikeGL/GetDataDir.m | 15 + .../API/@SpikeGL/GetGeomMap.m | 47 ++ .../API/@SpikeGL/GetImecChanGains.m | 12 + .../API/@SpikeGL/GetParams.m | 54 ++ .../API/@SpikeGL/GetParamsImecCommon.m | 54 ++ .../API/@SpikeGL/GetParamsImecProbe.m | 54 ++ .../API/@SpikeGL/GetParamsOnebox.m | 59 ++ .../API/@SpikeGL/GetProbeList.m | 14 + .../API/@SpikeGL/GetRunName.m | 8 + .../API/@SpikeGL/GetStreamAcqChans.m | 13 + .../API/@SpikeGL/GetStreamFileStart.m | 9 + .../API/@SpikeGL/GetStreamI16ToVolts.m | 9 + .../API/@SpikeGL/GetStreamMaxInt.m | 8 + .../API/@SpikeGL/GetStreamNP.m | 9 + .../API/@SpikeGL/GetStreamSN.m | 14 + .../API/@SpikeGL/GetStreamSampleCount.m | 9 + .../API/@SpikeGL/GetStreamSampleRate.m | 8 + .../API/@SpikeGL/GetStreamSaveChans.m | 9 + .../API/@SpikeGL/GetStreamShankMap.m | 37 + .../API/@SpikeGL/GetStreamVoltageRange.m | 12 + .../API/@SpikeGL/GetTime.m | 9 + .../API/@SpikeGL/GetVersion.m | 8 + .../API/@SpikeGL/IsConsoleHidden.m | 10 + .../API/@SpikeGL/IsInitialized.m | 9 + .../API/@SpikeGL/IsRunning.m | 8 + .../API/@SpikeGL/IsSaving.m | 9 + .../API/@SpikeGL/IsUserOrder.m | 9 + .../API/@SpikeGL/MapSample.m | 12 + .../API/@SpikeGL/NI_DO_Set.m | 15 + .../API/@SpikeGL/NI_Wave_Arm.m | 27 + .../API/@SpikeGL/NI_Wave_Load.m | 18 + .../API/@SpikeGL/NI_Wave_StartStop.m | 22 + .../API/@SpikeGL/OBX_AO_Set.m | 20 + .../API/@SpikeGL/OBX_Wave_Arm.m | 27 + .../API/@SpikeGL/OBX_Wave_Load.m | 19 + .../API/@SpikeGL/OBX_Wave_StartStop.m | 27 + .../API/@SpikeGL/Opto_emit.m | 11 + .../API/@SpikeGL/Opto_getAttenuations.m | 13 + .../API/@SpikeGL/Par2.m | 43 + .../API/@SpikeGL/PauseGraphs.m | 13 + .../API/@SpikeGL/ReceiveOK.m | 10 + .../API/@SpikeGL/ReceiveREADY.m | 10 + .../API/@SpikeGL/SetAnatomy_Pinpoint.m | 19 + .../API/@SpikeGL/SetAudioEnable.m | 18 + .../API/@SpikeGL/SetAudioParams.m | 49 ++ .../API/@SpikeGL/SetDataDir.m | 13 + .../API/@SpikeGL/SetMetaData.m | 39 + .../API/@SpikeGL/SetMultiDriveEnable.m | 12 + .../API/@SpikeGL/SetNextFileName.m | 40 + .../API/@SpikeGL/SetParams.m | 43 + .../API/@SpikeGL/SetParamsImecCommon.m | 42 + .../API/@SpikeGL/SetParamsImecProbe.m | 44 + .../API/@SpikeGL/SetParamsOnebox.m | 50 ++ .../API/@SpikeGL/SetRecordingEnable.m | 16 + .../API/@SpikeGL/SetRunName.m | 13 + .../API/@SpikeGL/SetTriggerOffBeep.m | 9 + .../API/@SpikeGL/SetTriggerOnBeep.m | 9 + .../API/@SpikeGL/SpikeGL.m | 35 + .../API/@SpikeGL/StartRun.m | 45 + .../API/@SpikeGL/StopRun.m | 9 + .../API/@SpikeGL/TriggerGT.m | 19 + .../API/@SpikeGL/VerifySha1.m | 31 + .../API/CalinsNetMex.mexw64 | Bin 0 -> 69632 bytes .../Build/CalinsNetMex.cpp | 792 ++++++++++++++++++ .../Build/NetClient.cpp | 223 +++++ .../Build/NetClient.h | 43 + .../SpikeGLX-MATLAB-SDK-main/Build/Socket.cpp | 625 ++++++++++++++ .../SpikeGLX-MATLAB-SDK-main/Build/Socket.h | 80 ++ .../Build/makemex64.bat | 2 + .../Demos/DemoRemoteAPI.m | 141 ++++ .../Demos/JWave/jwave.meta | 6 + .../Demos/JWave/jwave.txt | 6 + .../Demos/LatencyTest.m | 59 ++ .../Demos/wp_ni_soft_start.m | 41 + .../Demos/wp_soft_start.m | 45 + .../Demos/wp_trig_start.m | 55 ++ .../Docs/Contents.txt | 44 + .../Docs/GettingStarted.txt | 65 ++ .../SpikeGLX-MATLAB-SDK-main/Docs/Help.txt | 74 ++ .../Docs/WhatsNew.txt | 344 ++++++++ .../SpikeGLX-MATLAB-SDK-main/LICENSE.txt | 12 + .../MATLAB_latency.png | Bin 0 -> 13357 bytes .../SpikeGLX-MATLAB-SDK-main/README.md | 39 + .../npy-matlab/constructNPYheader.m | 88 ++ .../npy-matlab/datToNPY.m | 42 + .../npy-matlab/readNPY.m | 37 + .../npy-matlab/readNPYheader.m | 72 ++ .../npy-matlab/writeNPY.m | 25 + .../read_TTL_Events.m | 120 +++ 106 files changed, 6393 insertions(+) create mode 100644 ExperPort/Modules/NeuropixelHelper_Modules/OpenEphys_MATLAB-SDK/NetworkControl.m create mode 100644 ExperPort/Modules/NeuropixelHelper_Modules/OpenEphys_MATLAB-SDK/NetworkEventsConsole.m create mode 100644 ExperPort/Modules/NeuropixelHelper_Modules/OpenEphys_MATLAB-SDK/OpenEphysHTTPServer.m create mode 100644 ExperPort/Modules/NeuropixelHelper_Modules/OpenEphys_MATLAB-SDK/README.md create mode 100644 ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/.editorconfig create mode 100644 ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/.gitignore create mode 100644 ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/API/@SpikeGL/ChkConn.m create mode 100644 ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/API/@SpikeGL/Close.m create mode 100644 ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/API/@SpikeGL/ConsoleHide.m create mode 100644 ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/API/@SpikeGL/ConsoleShow.m create mode 100644 ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/API/@SpikeGL/Contents.m create mode 100644 ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/API/@SpikeGL/DoGetCells.m create mode 100644 ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/API/@SpikeGL/DoQuery.m create mode 100644 ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/API/@SpikeGL/DoSimpleCmd.m create mode 100644 ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/API/@SpikeGL/EnumDataDir.m create mode 100644 ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/API/@SpikeGL/Fetch.m create mode 100644 ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/API/@SpikeGL/FetchLatest.m create mode 100644 ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/API/@SpikeGL/GetDataDir.m create mode 100644 ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/API/@SpikeGL/GetGeomMap.m create mode 100644 ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/API/@SpikeGL/GetImecChanGains.m create mode 100644 ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/API/@SpikeGL/GetParams.m create mode 100644 ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/API/@SpikeGL/GetParamsImecCommon.m create mode 100644 ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/API/@SpikeGL/GetParamsImecProbe.m create mode 100644 ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/API/@SpikeGL/GetParamsOnebox.m create mode 100644 ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/API/@SpikeGL/GetProbeList.m create mode 100644 ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/API/@SpikeGL/GetRunName.m create mode 100644 ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/API/@SpikeGL/GetStreamAcqChans.m create mode 100644 ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/API/@SpikeGL/GetStreamFileStart.m create mode 100644 ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/API/@SpikeGL/GetStreamI16ToVolts.m create mode 100644 ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/API/@SpikeGL/GetStreamMaxInt.m create mode 100644 ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/API/@SpikeGL/GetStreamNP.m create mode 100644 ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/API/@SpikeGL/GetStreamSN.m create mode 100644 ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/API/@SpikeGL/GetStreamSampleCount.m create mode 100644 ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/API/@SpikeGL/GetStreamSampleRate.m create mode 100644 ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/API/@SpikeGL/GetStreamSaveChans.m create mode 100644 ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/API/@SpikeGL/GetStreamShankMap.m create mode 100644 ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/API/@SpikeGL/GetStreamVoltageRange.m create mode 100644 ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/API/@SpikeGL/GetTime.m create mode 100644 ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/API/@SpikeGL/GetVersion.m create mode 100644 ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/API/@SpikeGL/IsConsoleHidden.m create mode 100644 ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/API/@SpikeGL/IsInitialized.m create mode 100644 ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/API/@SpikeGL/IsRunning.m create mode 100644 ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/API/@SpikeGL/IsSaving.m create mode 100644 ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/API/@SpikeGL/IsUserOrder.m create mode 100644 ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/API/@SpikeGL/MapSample.m create mode 100644 ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/API/@SpikeGL/NI_DO_Set.m create mode 100644 ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/API/@SpikeGL/NI_Wave_Arm.m create mode 100644 ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/API/@SpikeGL/NI_Wave_Load.m create mode 100644 ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/API/@SpikeGL/NI_Wave_StartStop.m create mode 100644 ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/API/@SpikeGL/OBX_AO_Set.m create mode 100644 ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/API/@SpikeGL/OBX_Wave_Arm.m create mode 100644 ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/API/@SpikeGL/OBX_Wave_Load.m create mode 100644 ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/API/@SpikeGL/OBX_Wave_StartStop.m create mode 100644 ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/API/@SpikeGL/Opto_emit.m create mode 100644 ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/API/@SpikeGL/Opto_getAttenuations.m create mode 100644 ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/API/@SpikeGL/Par2.m create mode 100644 ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/API/@SpikeGL/PauseGraphs.m create mode 100644 ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/API/@SpikeGL/ReceiveOK.m create mode 100644 ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/API/@SpikeGL/ReceiveREADY.m create mode 100644 ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/API/@SpikeGL/SetAnatomy_Pinpoint.m create mode 100644 ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/API/@SpikeGL/SetAudioEnable.m create mode 100644 ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/API/@SpikeGL/SetAudioParams.m create mode 100644 ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/API/@SpikeGL/SetDataDir.m create mode 100644 ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/API/@SpikeGL/SetMetaData.m create mode 100644 ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/API/@SpikeGL/SetMultiDriveEnable.m create mode 100644 ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/API/@SpikeGL/SetNextFileName.m create mode 100644 ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/API/@SpikeGL/SetParams.m create mode 100644 ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/API/@SpikeGL/SetParamsImecCommon.m create mode 100644 ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/API/@SpikeGL/SetParamsImecProbe.m create mode 100644 ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/API/@SpikeGL/SetParamsOnebox.m create mode 100644 ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/API/@SpikeGL/SetRecordingEnable.m create mode 100644 ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/API/@SpikeGL/SetRunName.m create mode 100644 ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/API/@SpikeGL/SetTriggerOffBeep.m create mode 100644 ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/API/@SpikeGL/SetTriggerOnBeep.m create mode 100644 ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/API/@SpikeGL/SpikeGL.m create mode 100644 ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/API/@SpikeGL/StartRun.m create mode 100644 ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/API/@SpikeGL/StopRun.m create mode 100644 ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/API/@SpikeGL/TriggerGT.m create mode 100644 ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/API/@SpikeGL/VerifySha1.m create mode 100644 ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/API/CalinsNetMex.mexw64 create mode 100644 ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/Build/CalinsNetMex.cpp create mode 100644 ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/Build/NetClient.cpp create mode 100644 ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/Build/NetClient.h create mode 100644 ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/Build/Socket.cpp create mode 100644 ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/Build/Socket.h create mode 100644 ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/Build/makemex64.bat create mode 100644 ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/Demos/DemoRemoteAPI.m create mode 100644 ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/Demos/JWave/jwave.meta create mode 100644 ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/Demos/JWave/jwave.txt create mode 100644 ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/Demos/LatencyTest.m create mode 100644 ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/Demos/wp_ni_soft_start.m create mode 100644 ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/Demos/wp_soft_start.m create mode 100644 ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/Demos/wp_trig_start.m create mode 100644 ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/Docs/Contents.txt create mode 100644 ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/Docs/GettingStarted.txt create mode 100644 ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/Docs/Help.txt create mode 100644 ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/Docs/WhatsNew.txt create mode 100644 ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/LICENSE.txt create mode 100644 ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/MATLAB_latency.png create mode 100644 ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/README.md create mode 100644 ExperPort/Modules/NeuropixelHelper_Modules/npy-matlab/constructNPYheader.m create mode 100644 ExperPort/Modules/NeuropixelHelper_Modules/npy-matlab/datToNPY.m create mode 100644 ExperPort/Modules/NeuropixelHelper_Modules/npy-matlab/readNPY.m create mode 100644 ExperPort/Modules/NeuropixelHelper_Modules/npy-matlab/readNPYheader.m create mode 100644 ExperPort/Modules/NeuropixelHelper_Modules/npy-matlab/writeNPY.m create mode 100644 ExperPort/Modules/NeuropixelHelper_Modules/read_TTL_Events.m diff --git a/ExperPort/Modules/NeuropixelHelper_Modules/OpenEphys_MATLAB-SDK/NetworkControl.m b/ExperPort/Modules/NeuropixelHelper_Modules/OpenEphys_MATLAB-SDK/NetworkControl.m new file mode 100644 index 00000000..5f339aa9 --- /dev/null +++ b/ExperPort/Modules/NeuropixelHelper_Modules/OpenEphys_MATLAB-SDK/NetworkControl.m @@ -0,0 +1,179 @@ +% MIT License + +% Copyright (c) 2021 Open Ephys + +% Permission is hereby granted, free of charge, to any person obtaining a copy +% of this software and associated documentation files (the "Software"), to deal +% in the Software without restriction, including without limitation the rights +% to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +% copies of the Software, and to permit persons to whom the Software is +% furnished to do so, subject to the following conditions: + +% The above copyright notice and this permission notice shall be included in all +% copies or substantial portions of the Software. + +% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +% IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +% FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +% AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +% LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +% OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +% SOFTWARE. + +classdef NetworkControl + + %NETWORKCONTROL - A class that communicates with the Open Ephys NetworkEvents plugin + % NETWORKCONTROL - See: https://github.com/open-ephys-plugins/NetworkEvents for more info. + % + %It can be used to start/stop acquisition, start/stop recording, and + %send TTL events to an instance of the Open Ephys GUI, either locally + %or over a network connection. + % + % SYNTAX: + % gui = NetworkControl( '127.0.0.1', 5556 ) + % + % PROPERTIES: + % ipAddress - IP Adress of machine running OpenEphys + % port - port as indicated by active NetworkEvents plugin in OpenEphys + % url - full tcp address + % context - ZMQ context running in REQ-REP patter + % socket - the socket for this context + % + % EXAMPLES: + % gui.startAcquisition() + % Response: StartedAcquisition + % + % gui.isAcquiring() + % True + % + % gui.record() + % Response: StartedRecording + % + % gui.isRecording() + % True + % + % gui.sendTTL(5, 1) + % Response: TTLHandled: Channel=5 on=1 + % + % gui.stopRecording() + % Response: StoppedRecording + % + % gui.isRecording() + % False + % + % gui.stopAcquisition() + % Response: StoppedAcquisition + + properties + + ipAddress + port + + url + context + socket + + end + + methods + + function self = NetworkControl(varargin) + + if nargin == 0 + self.ipAddress = '127.0.0.1'; + self.port = 5556; + elseif nargin == 2 + self.ipAddress = varargin{1}; + self.port = varargin{2}; + else + fprintf("Error: NetworkControl takes either 0 or 2 input parameters\n"); + return; + end + + self.url = ['tcp://' self.ipAddress ':' num2str(self.port)]; + + self.context = zmq.core.ctx_new(); + self.socket = zmq.core.socket(self.context, 'ZMQ_REQ'); + + zmq.core.connect(self.socket, self.url); + + end + + function delete(self) + + zmq.core.disconnect(self.socket, self.url); + zmq.core.close(self.socket); + + end + + function startAcquisition(self) + + zmq.core.send(self.socket, uint8('StartAcquisition')); + reply = char(zmq.core.recv(self.socket)); + + end + + function stopAcquisition(self) + + zmq.core.send(self.socket, uint8('StopAcquisition')); + reply = char(zmq.core.recv(self.socket)); + + end + + function record(self) + + zmq.core.send(self.socket, uint8('StartRecord')); + reply = char(zmq.core.recv(self.socket)); + + end + + function startRecording(self) + + self.record(); + + end + + function stopRecording(self) + + zmq.core.send(self.socket, uint8('StopRecord')); + reply = char(zmq.core.recv(self.socket)); + + end + + function reply = isRecording(self) + + zmq.core.send(self.socket, uint8('IsRecording')); + reply = char(zmq.core.recv(self.socket)) == '1'; + + end + + function reply = isAcquiring(self) + + zmq.core.send(self.socket, uint8('IsAcquiring')); + reply = char(zmq.core.recv(self.socket)) == '1'; + + end + + function sendTTL(self, channel, state) + + zmq.core.send(['TTL Channel=' num2str(channel) ' on=' num2str(state)]); + reply = char(zmq.core.recv(self.socket)); + + end + + function wait(self, timeInSeconds) + + pause(timeInSeconds); + + end + + function getResponse(self) + + zmq.core.send(self.socket, uint8('StopAcquisition')); + reply = char(zmq.core.recv(self.socket)); + + end + + end + +end \ No newline at end of file diff --git a/ExperPort/Modules/NeuropixelHelper_Modules/OpenEphys_MATLAB-SDK/NetworkEventsConsole.m b/ExperPort/Modules/NeuropixelHelper_Modules/OpenEphys_MATLAB-SDK/NetworkEventsConsole.m new file mode 100644 index 00000000..992e3982 --- /dev/null +++ b/ExperPort/Modules/NeuropixelHelper_Modules/OpenEphys_MATLAB-SDK/NetworkEventsConsole.m @@ -0,0 +1,47 @@ +%Command line version of NetworkControl + +menu = [ ... +'\nAvailable commands: \n' ... +'--------------------\n' ... +'startAcquisition\n' ... +'stopAcquisition\n' ... +'startRecording\n' ... +'stopRecording \n\n' ... +'Available queries: \n' ... +'-------------------\n' ... +'IsAcquiring\n' ... +'IsRecording\n\n' ... +'Send a TTL event: \n' ... +'---------------------\n' ... +'TTL Channel=1 on=1\n' ... +'TTL Channel=1 on=0\n\n' ... +'To exit, enter "q", "quit", or "exit"\n\n\n' ... +]; + +networkControl = NetworkControl(); +prompt = menu; + +while true + + cmd = input(prompt, 's'); + + if any(strcmp({'q','quit','exit','quit()'}, cmd)) + break; + end + + try + nargin = length(strsplit(cmd)); + if nargin == 3 %TLL event + ttlMessage = strsplit(cmd); + channel = strsplit(ttlMessage{2}, '='); + state = strsplit(ttlMessage{3}, '='); + cmd = ['sendTTL(', channel{2}, ',', state{2}, ')'] + end + eval(['networkControl.' cmd]); + prompt = '\n'; + catch ME %unrecognized command + fprintf("Invalid command!\n\n\n"); + prompt = menu; + end + +end \ No newline at end of file diff --git a/ExperPort/Modules/NeuropixelHelper_Modules/OpenEphys_MATLAB-SDK/OpenEphysHTTPServer.m b/ExperPort/Modules/NeuropixelHelper_Modules/OpenEphys_MATLAB-SDK/OpenEphysHTTPServer.m new file mode 100644 index 00000000..891207e9 --- /dev/null +++ b/ExperPort/Modules/NeuropixelHelper_Modules/OpenEphys_MATLAB-SDK/OpenEphysHTTPServer.m @@ -0,0 +1,554 @@ +% MIT License +% +% Copyright (c) 2021 Open Ephys +% +% Permission is hereby granted, free of charge, to any person obtaining a copy +% of this software and associated documentation files (the "Software"), to deal +% in the Software without restriction, including without limitation the rights +% to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +% copies of the Software, and to permit persons to whom the Software is +% furnished to do so, subject to the following conditions: +% +% The above copyright notice and this permission notice shall be included in all +% copies or substantial portions of the Software. +% +% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +% IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +% FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +% AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +% LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +% OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +% SOFTWARE. + +classdef OpenEphysHTTPServer < handle + + properties (Constant) + end + + properties + + address + options + + end + + methods + + function self = OpenEphysHTTPServer(host, port) + + self.address = strcat('http://', host, ':', num2str(port)); + self.options = weboptions(... + 'MediaType', 'application/json',... + 'RequestMethod', 'put'); + + end + + end + + methods + + function resp = send(self, varargin) + +% Send a request to the server. +% +% Parameters +% ---------- +% endpoint : String +% The API endpoint for the request. +% Must begin with "/api/" +% payload (optional): Dictionary +% The payload to send with the request. +% +% If a payload is specified, a PUT request +% will be used; otherwise it will be a GET request. + + + endpoint = varargin{1}; + if nargin > 2 + payload = varargin{2}; + end + + try + + if nargin == 2 + resp = webread(strcat(self.address, endpoint)); + else + resp = webwrite(strcat(self.address, endpoint), payload, self.options); + end + + catch ME +% TODO: Catch matlab equivalents of these +% except requests.exceptions.Timeout: +% # Maybe set up for a retry, or continue in a retry loop +% print("Timeout") +% except requests.exceptions.TooManyRedirects: +% # Tell the user their URL was bad and try a different one +% print("Bad URL") +% except requests.exceptions.RequestException as e: +% # Open Ephys server needs to be enabled +% print("Open Ephys HTTP Server likely not enabled") + + resp = "GUI was closed!"; + end + + end + + function resp = load(self, path) + +% Load a configuration file. +% +% Parameters +% ---------- +% path : String +% The path to the configuration file. + + payload = struct('path', path); + + resp = self.send('/api/load', payload); + pause(1); + + end + + function processors = getProcessorList(self) + +% Returns all available processors in the GUI's Processor List + + data = self.send('/api/processors/list'); + processors = string(char({data.processors.name})); + + end + + function processors = getProcessors(self, varargin) + +% Get the list of processors. + +% Parameters +% ---------- +% filterByName : String (Optional) +% Filter the list by processor name. + + data = self.send('/api/processors'); + processors = data.processors; + if nargin > 1 + indices = cellfun(@(v)strcmp(v,varargin{1}),{processors.name}); + processors = processors(indices); + end + + end + + function resp = clearSignalChain(self) + +% Clear the signal chain. + + resp = self.send('/api/processors/clear'); + + end + + function resp = addProcessor(self, name, varargin) + +% Add a processor to the signal chain. +% +% Parameters +% ---------- +% name : String +% The name of the processor to add (e.g. "Record Node") +% source : Integer +% The 3-digit processor ID of the source (e.g. 101) +% dest : Integer +% The 3-digit processor ID of the destination (e.g. 102) + + endpoint = '/api/processors/add'; + payload = struct('name', name); + +% If only processor name is specified, set source to most recently added processor + if nargin == 2 + existingProcessors = self.getProcessors(); + if ~isempty(existingProcessors) + index = find([existingProcessors.id] == max([existingProcessors.id])); + mostRecentProcessor = existingProcessors(index); + payload.source_id = mostRecentProcessor.id; + end + elseif nargin > 2 + payload.source_id = varargin{1}; + if nargin == 4 + payload.dest_id = varargin{2}; + end + end + + resp = self.send(endpoint, payload); + + end + + + function resp = deleteProcessor(self, id) + +% Delete a processor. +% +% Parameters +% ---------- +% processor_id : Integer +% The 3-digit processor ID (e.g. 101) +% + endpoint = '/api/processors/delete'; + payload = struct('id', id); + + resp = self.send(endpoint, payload); + + end + + + function resp = getParameters(self, processorId, streamIndex) + +% Get parameters for a stream. +% +% Parameters +% ---------- +% processorId : Integer +% The 3-digit processor ID (e.g. 101) +% streamIndex : Integer +% The index of the stream (e.g. 0). +% + endpoint = strcat('/api/processors/', num2str(processorId), '/streams/', num2str(streamIndex), '/parameters'); + resp = self.send(endpoint).parameters; + + end + + function resp = setParameters(self, processorID, streamIndex, paramName, value) + +% Update a parameter value +% +% Parameters +% ---------- +% processorID : Integer +% The 3-digit processor ID (e.g. 101) +% streamIndex : Integer +% The index of the stream (e.g. 0) +% paramName : String +% The parameter name (e.g. low_cut) +% value : Any +% The parameter value (must match the parameter type). +% Hint: Float parameters must be sent with a decimal +% included (e.g. 1000.0 instead of 1000) +% + endpoint = strcat('/api/processors/', num2str(processorID), '/streams/', num2str(streamIndex), '/parameters/', paramName); + +% TO FIX:matlab automatically casts doubles to int if no +% integers after the decimal point of a float. + if isa(value, 'double') + payload = struct('value', [], 'class', {'double'}); + payload.value = value + 0.00000000001; + else + payload = struct('value', value); + end + resp = self.send(endpoint, payload); + + end + + + function resp = getRecordingInfo(self, varargin) + +% Get recording information. +% +% Parameters +% ---------- +% key : String +% The key to get. +% + + data = self.send('/api/recording'); + if nargin == 1 + resp = data; + elseif isfield(data, varargin{1}) + resp = data.(varargin{1}); + else + resp = "Invalid key"; + end + end + + function resp = setParentDirectory(self, path) + +% Set the parent directory. +% +% Parameters +% ---------- +% path : String +% The path to the parent directory. + + payload = struct('parent_directory', path); + data = self.send('/api/recording', payload); + resp = data; + + end + + function resp = setPrependText(self, text) + +% Set the prepend text. +% +% Parameters +% ---------- +% text : String +% The text to prepend. +% + payload = struct('prepend_text', text); + data = self.send('/api/recording', payload); + resp = data; + + end + + function resp = setBaseText(self, text) + +% Set the base text. +% +% Parameters +% ---------- +% text : String +% The text to base name of the recording directory (see GUI docs). +% + payload = struct('base_text', text); + data = self.send('/api/recording', payload); + resp = data; + + end + + function resp = setAppendText(self, text) + +% Set the append text. +% +% Parameters +% ---------- +% text : String +% The text to append. +% + payload = struct('append_text', text); + data = self.send('/api/recording', payload); + resp = data; + + end + + + function resp = setStartNewDirectory(self) + +% Set if GUI should start a new directory for the next recording. + + payload = struct('start_new_directory', 'true'); + data = self.send('/api/recording', payload); + resp = data; + + end + + function resp = setFileReaderPath(self, nodeId, filePath) + +% Set the file path. + +% Parameters +% ---------- +% nodeId : Integer +% The node ID. +% filePath : String +% The file path. + + endpoint = strcat('/api/processors/', num2str(nodeId), '/config'); + payload = struct('text', strcat("file=", num2str(filePath))); + data = self.send(endpoint, payload); + + resp = data; + + end + + function resp = setFileReaderIndex(self, nodeId, fileIndex) + +% Set the file index. +% +% Parameters +% ---------- +% nodeId : Integer +% The node ID. +% fileIndex : Integer +% The file index. + + endpoint = strcat('/api/processors/', num2str(nodeId), '/config'); + payload = struct('text', strcat("index=", num2str(fileIndex))); + data = self.send(endpoint, payload); + + resp = data; + + end + + function resp = setRecordEngine(self, nodeId, engine) + +% Set the record engine for a record node. +% +% Parameters +% ---------- +% nodeId : Integer +% The node ID. +% engine: Integer +% The record engine index. + + endpoint = strcat('/api/processors/', num2str(nodeId), '/config'); + payload = struct('text', strcat("engine=", engine)); + data = self.send(endpoint, payload); + + resp = data; + + end + + function resp = setRecordPath(self, nodeId, directory) + +% Set the record path for a Record Node +% +% Parameters +% ---------- +% nodeId: Integer +% The node ID. +% directory : String +% The record path. + + endpoint = strcat('/api/recording/', num2str(nodeId)); + payload = struct('parent_directory', directory); + data = self.send(endpoint, payload); + + resp = data; + + end + function resp = getStatus(self) + +% Returns the current status of the GUI (IDLE, ACQUIRE, or RECORD) + + data = self.send('/api/status'); + + resp = data; + + end + + function resp = acquire(self, varargin) + +% Start acquisition. +% +% Parameters +% ---------- +% duration : Integer (optional) +% The acquisition duration in seconds. If given, the +% GUI will acquire data for the specified interval +% and then stop. +% +% By default, acquisition will continue until it +% is stopped by another command. + + payload = struct('mode', 'ACQUIRE'); + data = self.send('/api/status', payload); + + if nargin == 2 + payload = struct('mode', 'IDLE'); + pause(varargin{1}); + data = self.send('/api/status', payload); + end + + resp = data; + + end + + function resp = record(self, varargin) + +% Start recording. +% +% Parameters +% ---------- +% duration : Integer (optional) +% The record duration in seconds. If given, the +% GUI will record data for the specified interval +% and then stop. +% +% By default, recoridng will continue until it +% is stopped by another command. + + payload = struct('mode', 'RECORD'); + data = self.send('/api/status', payload); + + if nargin == 2 + payload = struct('mode', 'IDLE'); + pause(varargin{1}); + data = self.send('/api/status', payload); + end + + resp = data; + + end + + function resp = idle(self, varargin) + +% Stop acquisition/recording. +% +% Parameters +% ---------- +% duration : Integer (optional) +% The idle duration in seconds. If given, the +% GUI will idle for the specified interval +% and then return to its previous state. +% +% By default, this command will stop +% acquisition/recording and return immediately. + + mode = self.getStatus().mode; + + payload = struct('mode', 'IDLE'); + data = self.send('/api/status', payload); + + if nargin == 2 + payload = struct('mode', mode); + pause(varargin{1}); + data = self.send('/api/status', payload); + end + + resp = data; + + end + + function resp = message(self, message) + +% Broadcast a message to all processors during acquisition +% +% Parameters +% ---------- +% message : String +% The message to send. + + payload = struct('text', message); + data = self.send('/api/message', payload); + + resp = data; + + end + + function resp = config(self, nodeId, message) + +% Send a configuration message to a specific processor. +% +% Parameters +% ---------- +% nodeId : Integer +% The 3-digit processor ID (e.g. 101) +% message : String +% The message to send. + + endpoint = strcat('/api/processors/', num2str(nodeId), '/config'); + payload = struct('text', message); + data = self.send(endpoint, payload); + + resp = data; + + end + + function resp = quit(self) + +% Quit the GUI + + payload = struct('command', 'quit'); + data = self.send('/api/window', payload); + + resp = data; + end + + end + +end \ No newline at end of file diff --git a/ExperPort/Modules/NeuropixelHelper_Modules/OpenEphys_MATLAB-SDK/README.md b/ExperPort/Modules/NeuropixelHelper_Modules/OpenEphys_MATLAB-SDK/README.md new file mode 100644 index 00000000..4d26a663 --- /dev/null +++ b/ExperPort/Modules/NeuropixelHelper_Modules/OpenEphys_MATLAB-SDK/README.md @@ -0,0 +1,240 @@ +# `open_ephys.control` + +This module makes it possible to control the [Open Ephys GUI](https://open-ephys.org/gui) via Matlab, either running locally or over a network. + +## OpenEphysHTTPServer + +Starting in GUI v0.6.0 we recommend using the OpenEphysHTTPServer to control the GUI remotely. + +### Usage + +Create an instance of the OpenEphysHTTPServer class: + +```matlab +gui = OpenEphysHTTPServer('127.0.0.1', 37497) +``` + +Get the processor list (all available processors to use in a signal chain) + +```matlab +gui.getProcessorList() +``` + +Get the processors in the current signal chain + +```matlab +gui.getProcessors() +``` + +Clear the current signal chain + +```matlab +gui.clearSignalChain() +``` + +Add a processor to the signal chain (source and destination are optional, if not included will add to end of signal chain) + +```matlab +gui.addProcessor(processorName, source, destination) +``` + +Delete a processor from the signal chain + +```matlab +gui.deleteProcessor(processorId) +``` + +Get the parameters for a processor + +```matlab +gui.getParameters(processorId, streamIdx) +``` + +Set the parameters for a processor + +```matlab +gui.setParameter(processorId, streamIdx, paramName, value) +``` + +Get recording information + +```matlab +gui.getRecordingInfo(key) +``` + +Set parent recording directory + +```matlab +gui.setParentDirectory(path) +``` + +Set prepend text + +```matlab +gui.setPrependText(text) +``` + +Set base text + +```matlab +gui.setBaseText(text) +``` + +Set append text + +```matlab +gui.setAppendText(text) +``` + +Set start new directory flag (starts a new directory for the next recording) + +```matlab +gui.setStartNewDirectory() +``` + +Set file path to load for a FileReader + +```matlab +gui.setFileReaderPath(nodeId, path) +``` + +Set file index to load for a FileReader + +```matlab +gui.setFileReaderIndex(nodeId, index) +``` + +Set record engine + +```matlab +gui.setRecordEngine(nodeId, engine) +``` + +Set record path + +```matlab +gui.setRecordPath(nodeId, directory) +``` + +Get GUI status (acquiring, recording or idle) + +```matlab +gui.getStatus() +``` + +Start acquisition (duration is optional) + +```matlab +gui.acquire(duration) +``` + +Stop recording (duration is optional) + +```matlab +gui.record(duration) +``` + +Stop acquisition/recording (duration is optional) + +```matlab +gui.idle(duration) +``` + +Send a text message to all processors in the signal chain + +```matlab +gui.message(text) +``` + +Quit the GUI + +```matlab +gui.quit() +``` + +## NetworkControl + +### Usage + +Your GUI's signal chain must include a [NetworkEvents](https://open-ephys.github.io/gui-docs/User-Manual/Plugins/Network-Events.html) plugin in order for this module to work. + +To use the control module in Matlab: + +- [Download ZeroMQ](https://zeromq.org/download/) for your specific platform +- Edit the paths to your zmq library file locations in control/matlab-zmq/config.m +- Run control/matlab-zmq/make.m to generate the required .mex files + +Note: There are known issues when generating mex files for Matlab 2017+. It is possible to generate the mex files with an older version of Matlab and copy them to a newer version of Matlab. + +See this issue for more information: +https://github.com/fagg/matlab-zmq/issues/40#issuecomment-1030198530 + +## Usage + +### Initialization + +To control a GUI instance running on the same machine, simply enter: + +```matlab +gui = NetworkControl() +``` + +To specify a custom IP address or port number, use: + +```matlab +gui = NetworkControl('10.127.50.1', 2000) +``` + +### Starting and stopping acquisition + +To start acquisition, enter: + +```matlab +gui.startAcquisition() +``` + +To stop acquisition, enter: + +```matlab +gui.stopAcquisition() +``` + +To query acquisition status, use: + +```matlab +gui.isAcquiring() +``` + +### Starting and stopping recording + +To start recording, enter: + +```matlab +gui.startRecording() +``` + +To stop recording while keeping acquisition active, enter: + +```matlab +gui.stopRecording() +``` + +To query recording status, use: + +```matlab +gui.isRecording() +``` + +### Sending TTL events + +To send a TTL "ON" event, enter: + +```matlab +gui.sendTTL(5, 1) %channel = 5, state = 1 +``` + +To send a TTL "OFF" event, enter: + +```matlab +gui.sendTTL(5, 0) %channel = 5, state = 0 +``` diff --git a/ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/.editorconfig b/ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/.editorconfig new file mode 100644 index 00000000..f85932e7 --- /dev/null +++ b/ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/.editorconfig @@ -0,0 +1,8 @@ + +root = true + +[*] +indent_style = space +indent_size = 4 +tab_width = 4 + diff --git a/ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/.gitignore b/ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/.gitignore new file mode 100644 index 00000000..23a42b9e --- /dev/null +++ b/ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/.gitignore @@ -0,0 +1,61 @@ +# Compiled object files +*.slo +*.lo +*.o +*.obj + +# Compiled dynamic libraries +*.dylib + +# Compiled static libraries +*.lai +*.la +*.a + +# Precompiled headers +*.gch +*.pch + +# Qt generated +build-* +Makefile +Makefile.* +moc_*.cpp +qrc_*.cpp +ui_*.h +*.moc + +# Project files +*.pro.user +*.pro.user.* +*.qmlproject.user +*.qmlproject.user.* + +# Windows-specific +Win32/ +Debug/ +Release/ +Thumbs.db +desktop.ini +*.idb +*.ncb +*.aps +*.sln +*.suo +*.vcproj +*.vcproj.* + +# Mac-specific +.DS_Store + +# Log files +*.log + +# Editor temporary files +*~ +*.autosave + +# Other +*.bin +*.exe + diff --git a/ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/API/@SpikeGL/ChkConn.m b/ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/API/@SpikeGL/ChkConn.m new file mode 100644 index 00000000..9d2776ff --- /dev/null +++ b/ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/API/@SpikeGL/ChkConn.m @@ -0,0 +1,34 @@ +% THIS FUNCTION IS PRIVATE AND SHOULD NOT BE CALLED BY OUTSIDE CODE! +% +function [sm] = ChkConn( sm ) + + if( sm.in_chkconn ) + return; + end + + sm.in_chkconn = 1; + + if( sm.handle == -1 ) + + sm.handle = CalinsNetMex( 'create', sm.host, sm.port ); + + if( isempty( CalinsNetMex( 'connect', sm.handle ) ) ) + error( 'ChkConn: Unable to connect to server.' ); + end + + sm.ver = DoQuery( sm, 'GETVERSION' ); + + else + + ok = CalinsNetMex( 'sendstring', sm.handle, sprintf( 'NOOP\n' ) ); + + if( isempty( ok ) || isempty( CalinsNetMex( 'readline', sm.handle ) ) ) + + if( isempty( CalinsNetMex( 'connect', sm.handle ) ) ) + error( 'ChkConn: Still unable to connect to server.' ); + end + end + end + + sm.in_chkconn = 0; +end diff --git a/ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/API/@SpikeGL/Close.m b/ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/API/@SpikeGL/Close.m new file mode 100644 index 00000000..b82b660a --- /dev/null +++ b/ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/API/@SpikeGL/Close.m @@ -0,0 +1,11 @@ +% myobj = Close( myobj ) +% +% Close the network connection to SpikeGLX and release +% associated MATLAB resources. +% +function [s] = Close( s ) + + CalinsNetMex( 'disconnect', s.handle ); + CalinsNetMex( 'destroy', s.handle ); + s.handle = -1; +end diff --git a/ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/API/@SpikeGL/ConsoleHide.m b/ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/API/@SpikeGL/ConsoleHide.m new file mode 100644 index 00000000..595f352f --- /dev/null +++ b/ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/API/@SpikeGL/ConsoleHide.m @@ -0,0 +1,8 @@ +% myobj = ConsoleHide( myobj ) +% +% Hide SpikeGLX console window to reduce screen clutter. +% +function [s] = ConsoleHide( s ) + + s = DoSimpleCmd( s, 'CONSOLEHIDE' ); +end diff --git a/ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/API/@SpikeGL/ConsoleShow.m b/ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/API/@SpikeGL/ConsoleShow.m new file mode 100644 index 00000000..3cc8633e --- /dev/null +++ b/ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/API/@SpikeGL/ConsoleShow.m @@ -0,0 +1,8 @@ +% myobj = ConsoleShow( myobj ) +% +% Show SpikeGLX console window. +% +function [s] = ConsoleShow( s ) + + s = DoSimpleCmd( s, 'CONSOLESHOW' ); +end diff --git a/ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/API/@SpikeGL/Contents.m b/ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/API/@SpikeGL/Contents.m new file mode 100644 index 00000000..3a411987 --- /dev/null +++ b/ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/API/@SpikeGL/Contents.m @@ -0,0 +1,623 @@ +% SYNOPSIS +% -------- +% +% The @SpikeGL class is a MATLAB object with methods to access the +% SpikeGLX program via TCP/IP. SpikeGLX and MATLAB can run on the +% same machine (via loopback socket address 127.0.0.1 and port 4142) +% or across a network. +% +% This class provides extensive control over a running SpikeGLX process: +% starting and stopping a run, setting parameters, calling the Par2 and +% SHA1 tools, and so on. +% +% Users of this class merely need to construct an instance of a @SpikeGL +% object and all network communication with the SpikeGLX process is handled +% automatically. +% +% The network socket handle is used with the 'CalinsNetMex' mexFunction, +% which is a helper mexFunction that does all the actual socket +% communications for this class (since MATLAB lacks native network +% support). +% +% Instances of @SpikeGL are weakly stateful: merely keeping a handle to a +% network socket. It is ok to create and destroy several of these objects. +% Each network connection cleans up after itself after 10 seconds of +% inactivity. By the way, if your script has pauses longer than 10 seconds, +% and you reuse a handle that has timed out, the handle will automatically +% reestablish a connection and the script will likely continue without +% problems, but a warning will appear in the Command Window reflecting +% the timeout. Such warnings have a warningid, so you can suppress them +% by typing >> warning( 'off', 'CalinsNetMex:connectionClosed' ). +% +% EXAMPLES +% -------- +% +% my_s = SpikeGL; % connect to SpikeGLX running on local machine +% +% prms = GetParams( my_s ); % retrieve run params +% +% SetParams( my_s, struct('niMNChans1','0:5','niDualDevMode','false',...) ); +% +% StartRun( my_s ); % starts data acquisition run using last-set params +% +% StopRun( my_s ); % stop run and clean up +% +% (js, ip) +% -------- +% +% The two integer values (js, ip) select a data stream. +% js: stream type: {0=nidq, 1=obx, 2=imec-probe}. +% ip: substream: {0=nidq (if js=0), 0+=which OneBox or imec probe}. +% Examples (js, ip): +% (0, 0) = nidq. // for nidq, ip is arbitrary but zero by convention +% (1, 4) = obx4. +% (2, 7) = imec7. +% Note: ip has range [0..np-1], where, np is queried using GetStreamNP(). +% +% FUNCTION REFERENCE +% ------------------ +% +% myobj = SpikeGL() +% myobj = SpikeGL( host ) +% myobj = SpikeGL( host, port ) +% +% Construct a new @SpikeGL instance and immediately attempt +% a network connection. If omitted, the defaults for host and +% port are {'localhost, 4142}. +% +% myobj = Close( myobj ) +% +% Close the network connection to SpikeGLX and release +% associated MATLAB resources. +% +% myobj = ConsoleHide( myobj ) +% +% Hide SpikeGLX console window to reduce screen clutter. +% +% myobj = ConsoleShow( myobj ) +% +% Show the SpikeGLX console window. +% +% params = EnumDataDir( myobj, i ) +% +% Retrieve a listing of files in the ith data directory. +% Get main data directory by setting i=0 or omitting it. +% +% [daqData,headCt] = Fetch( myObj, js, ip, start_samp, max_samps, channel_subset, downsample_ratio ) +% +% Get MxN matrix of stream data. +% M = samp_count, MIN(max_samps,available). +% N = channel count... +% Data are int16 type. +% If filtered IM stream buffers are enabled, you may fetch from them with js=-2. +% Fetching starts at index start_samp. +% channel_subset is an optional vector of specific channels to fetch [a,b,c...], or, +% [-1] = all acquired channels, or, +% [-2] = all saved channels. +% downsample_ratio is an integer; return every Nth sample (default = 1). +% Also returns headCt = index of first sample in matrix. +% +% [daqData,headCt] = FetchLatest( myObj, js, ip, max_samps, channel_subset, downsample_ratio ) +% +% Get MxN matrix of the most recent stream data. +% M = samp_count, MIN(max_samps,available). +% N = channel count... +% Data are int16 type. +% If filtered IM stream buffers are enabled, you may fetch from them with js=-2. +% channel_subset is an optional vector of specific channels to fetch [a,b,c...], or, +% [-1] = all acquired channels, or, +% [-2] = all saved channels. +% downsample_ratio is an integer; return every Nth sample (default = 1). +% Also returns headCt = index of first sample in matrix. +% +% dir = GetDataDir( myobj, i ) +% +% Get ith global data directory. +% Get main data directory by setting i=0 or omitting it. +% +% params = GetGeomMap( myobj, ip ) +% +% Get geomMap for given logical imec probe. +% Returned as a struct of name/value pairs. +% Header fields: +% head_partNumber ; string +% head_numShanks +% head_shankPitch ; microns +% head_shankWidth ; microns +% Channel 5, e.g.: +% ch5_s ; shank index +% ch5_x ; microns from left edge of shank +% ch5_z ; microns from center of tip-most electrode row +% ch5_u ; used-flag (in CAR operations) +% +% [APgain,LFgain] = GetImecChanGains( myobj, ip, chan ) +% +% Returns the AP and LF gains for given probe and channel. +% +% params = GetParams( myobj ) +% +% Get the most recently used run parameters. +% These are a struct of name/value pairs. +% +% params = GetParamsImecCommon( myobj ) +% +% Get imec parameters common to all enabled probes. +% Returned as a struct of name/value pairs. +% +% params = GetParamsImecProbe( myobj, ip ) +% +% Get imec parameters for given logical probe. +% Returned as a struct of name/value pairs. +% +% params = GetParamsOneBox( myobj, ip, slot ) +% +% Get parameters for selected OneBox; +% returned as a struct of name/value pairs. +% +% To reference a OneBox configured as a recording stream +% set ip to its stream-id; if ip >= 0, slot is ignored. +% Any selected OneBox can also be referenced by setting +% ip = -1, and giving its slot index. +% +% list = GetProbeList( myobj ) +% +% Get string with format: +% (probeID,nShanks,partNumber)()... +% - A parenthesized entry for each selected probe. +% - probeID: zero-based integer. +% - nShanks: integer {1,4}. +% - partNumber: string, e.g., NP1000. +% - If no probes, return '()'. +% +% name = GetRunName( myobj ) +% +% Get run base name. +% +% chanCounts = GetStreamAcqChans( myobj, js, ip ) +% +% For the selected substream, returns a vector of the +% number of channels of each type that stream is acquiring. +% +% js = 0: NI channels: {MN,MA,XA,DW}. +% js = 1: OB channels: {XA,DW,SY}. +% js = 2: IM channels: {AP,LF,SY}. +% +% startingSample = GetStreamFileStart( myobj, js, ip ) +% +% Returns index of first sample in selected file, +% or zero if unavailable. +% +% mult = GetStreamI16ToVolts( myobj, js, ip, chan ) +% +% Returns multiplier converting 16-bit binary channel to volts. +% +% maxInt = GetStreamMaxInt( myobj, js, ip ) +% +% Returns largest positive integer value for selected stream. +% +% n_substreams = GetStreamNP( myobj, js ) +% +% Returns number (np) of js-type substreams. +% For the given js, ip has range [0..np-1]. +% +% sampleCount = GetStreamSampleCount( myobj, js, ip ) +% +% Returns number of samples since current run started, +% or zero if not running. +% +% sampleRate = GetStreamSampleRate( myobj, js, ip ) +% +% Returns sample rate of selected stream in Hz. +% +% channelSubset = GetStreamSaveChans( myobj, js, ip ) +% +% Returns a vector containing the indices of +% the acquired channels that are being saved. +% +% [nS,nC,nR,mat] = GetStreamShankMap( myObj, js, ip ) +% +% Get shank map for NI stream (js = 0): +% {nS,nC,nR} = max {shanks, cols, rows} on this probe; +% mat = Mx4 matrix of shank map entries, where, +% M = channel count. +% 4 = given channel's zero-based {shank, col, row} indices, +% plus a 'used' flag which is 1 if the channel should be +% included in displays and spatial averaging operations. +% Data are int16 type. +% +% [SN,type] = GetStreamSN( myobj, js, ip ) +% +% js = 1: Return OneBox SN and slot. +% js = 2: Return probe SN and type. +% SN = serial number string. +% +% [Vmin,Vmax] = GetStreamVoltageRange( myobj, js, ip ) +% +% Returns voltage range of selected data stream. +% +% time = GetTime( myobj ) +% +% Returns (double) number of seconds since SpikeGLX application +% was launched. +% +% version = GetVersion( myobj ) +% +% Get SpikeGLX version string. +% +% boolval = IsConsoleHidden( myobj ) +% +% Returns 1 if console window is hidden, false otherwise. +% The console window may be hidden/shown using ConsoleHide() +% and ConsoleShow(). +% +% boolval = IsInitialized( myobj ) +% +% Return 1 if SpikeGLX has completed its startup +% initialization and is ready to run. +% +% boolval = IsRunning( myobj ) +% +% Returns 1 if SpikeGLX is currently acquiring data. +% +% boolval = IsSaving( myobj ) +% +% Returns 1 if the software is currently running +% AND saving data. +% +% boolval = IsUserOrder( myobj, js, ip ) +% +% Returns 1 if graphs currently sorted in user order. +% This query is sent only to the main Graphs window. +% +% dstSample = MapSample( myobj, dstjs, dstip, srcSample, srcjs, srcip ) +% +% Returns sample in dst stream corresponding to +% given sample in src stream. +% +% myobj = NI_DO_Set( myobj, 'lines', bits ) +% +% Set one or more NI lines high/low. +% - lines is a string list of lines to set, e.g.: +% 'Dev6/port0/line2,Dev6/port0/line5' +% 'Dev6/port1/line0:3' +% 'Dev6/port1:2' +% - bits is a uint32 value, each bit maps to a line: +% The lowest 8 bits map to port 0. +% The next higher 8 bits map to port 1, etc. +% +% myobj = NI_Wave_Arm( myobj, 'outChan', 'trigTerm' ) +% +% General sequence: +% 1. NI_Wave_Load : Load wave from SpikeGLX/_Waves folder. +% 2. NI_Wave_Arm : Set triggering parameters. +% 3. NI_Wave_StartStop : Start if software trigger, stop when done. +% +% Set trigger method. +% - trigTerm is a string naming any trigger-capable terminal on your +% device, e.g., '/dev1/pfi2'. NI-DAQ requires names of terminals to +% start with a '/' character. This is indeed different than channel +% names which do not start with a slash. +% (1) Give a correct terminal string to trigger playback upon +% receiving a rising edge at that terminal. +% (2) Give any string that does NOT start with a '/' to trigger +% playback via the NI_Wave_StartStop command. +% - Multiple trigger events can NOT be given. For each trigger +% event after the first, you must first call NI_Wave_StartStop +% AND NI_Wave_Arm to stop and then rearm the task. +% +% outChan is a string naming any wave-capable analog output +% channel on your device, e.g., 'dev1/ao1'. +% +% myobj = NI_Wave_Load( myobj, 'outChan', 'wave_name', loop_mode ) +% +% General sequence: +% 1. NI_Wave_Load : Load wave from SpikeGLX/_Waves folder. +% 2. NI_Wave_Arm : Set triggering parameters. +% 3. NI_Wave_StartStop : Start if software trigger, stop when done. +% +% Load a wave descriptor already placed in SpikeGLX/_Waves. +% - Pass 'mywavename' to this function; no path; no extension. +% - The playback loop_modes are: {1=loop until stopped, 0=once only}. +% +% outChan is a string naming any wave-capable analog output +% channel on your device, e.g., 'dev1/ao1'. +% +% myobj = NI_Wave_StartStop( myobj, 'outChan', start_bool ) +% +% General sequence: +% 1. NI_Wave_Load : Load wave from SpikeGLX/_Waves folder. +% 2. NI_Wave_Arm : Set triggering parameters. +% 3. NI_Wave_StartStop : Start if software trigger, stop when done. +% +% Start (optionally) or stop wave playback. +% - If you selected software triggering with NI_Wave_Arm, +% then set start_bool=1 to start playback. +% - In all cases, set start_bool=0 to stop playback. +% - It is best to stop playback before changing wave parameters. +% - After playback or if looping mode is interrupted, the voltage +% remains at the last output level. +% +% outChan is a string naming any wave-capable analog output +% channel on your device, e.g., 'dev1/ao1'. +% +% myobj = OBX_AO_Set( myobj, ip, slot, 'chn_vlt' ) +% +% Set one or more OneBox AO (DAC) channel voltages. +% - chn_vlt is a string with format: (chan,volts)(chan,volts)...() +% - The chan values are integer AO indices in range [0,11]. +% - You can only use AO channels already listed on the OBX setup tab. +% - Voltages are double values in range [-5.0,5.0] V. +% - DAC is 16-bit; theoretical resolution is (10 V)/(2^16) ~ .0001526 V. +% - Practical resolution, given noise, appears to be ~ 0.002 V. +% - AO channels are disabled at run start/end; voltage ~ 1.56 V. +% +% To reference a OneBox configured as a recording stream +% set ip to its stream-id; if ip >= 0, slot is ignored. +% Any selected OneBox can also be referenced by setting +% ip = -1, and giving its slot index. +% +% myobj = OBX_Wave_Arm( myobj, ip, slot, trigger, loop_mode ) +% +% General sequence: +% 1. OBX_Wave_Load : Load wave from SpikeGLX/_Waves folder. +% 2. OBX_Wave_Arm : Set triggering parameters. +% 3. OBX_Wave_StartStop : Start if software trigger, stop when done. +% +% Set trigger method, and playback loop_mode. +% - Trigger values...Playback starts: +% -2 : By calling OBX_Wave_StartStop. +% -1 : When TTL rising edge sent to SMA1. +% 0-11 : When TTL rising edge sent to that XA (ADC) channel. +% - To use an ADC channel, you must name it as an XA channel on +% the OBX setup tab of the Acquisition Configuration dialog. +% - Multiple trigger events (either hardware or software) can be +% given without needing to rearm. +% - The playback loop_modes are: {1=loop until stopped, 0=once only}. +% +% To reference a OneBox configured as a recording stream +% set ip to its stream-id; if ip >= 0, slot is ignored. +% Any selected OneBox can also be referenced by setting +% ip = -1, and giving its slot index. +% +% myobj = OBX_Wave_Load( myobj, ip, slot, 'wave_name' ) +% +% General sequence: +% 1. OBX_Wave_Load : Load wave from SpikeGLX/_Waves folder. +% 2. OBX_Wave_Arm : Set triggering parameters. +% 3. OBX_Wave_StartStop : Start if software trigger, stop when done. +% +% Load a wave descriptor already placed in SpikeGLX/_Waves. +% - Pass 'mywavename' to this function; no path; no extension. +% +% To reference a OneBox configured as a recording stream +% set ip to its stream-id; if ip >= 0, slot is ignored. +% Any selected OneBox can also be referenced by setting +% ip = -1, and giving its slot index. +% +% myobj = OBX_Wave_StartStop( myobj, ip, slot, start_bool ) +% +% General sequence: +% 1. OBX_Wave_Load : Load wave from SpikeGLX/_Waves folder. +% 2. OBX_Wave_Arm : Set triggering parameters. +% 3. OBX_Wave_StartStop : Start if software trigger, stop when done. +% +% Start (optionally) or stop wave playback. +% - If you selected software triggering with OBX_Wave_Arm, +% then set start_bool=1 to start playback. +% - In all cases, set start_bool=0 to stop playback. +% - It is best to stop playback before changing wave parameters. +% - Waves only play at AO (DAC) channel-0. +% - To use the waveplayer, you must name channel AO-0 on +% the OBX setup tab of the Acquisition Configuration dialog. +% - After playback or if looping mode is interrupted, the voltage +% remains at the last output level. +% +% To reference a OneBox configured as a recording stream +% set ip to its stream-id; if ip >= 0, slot is ignored. +% Any selected OneBox can also be referenced by setting +% ip = -1, and giving its slot index. +% +% myobj = Opto_emit( myobj, ip, color, site ) +% +% Direct emission to specified site (-1=dark). +% ip: imec probe index. +% color: {0=blue, 1=red}. +% site: [0..13], or, -1=dark. +% +% [site_atten_factors] = Opto_getAttenuations( myobj, ip, color ) +% +% Returns vector of 14 (double) site power attenuation factors. +% ip: imec probe index. +% color: {0=blue, 1=red}. +% +% res = Par2( myobj, op, 'filename' ) +% +% Create, Verify, or Repair Par2 redundancy files for +% 'filename'. Arguments: +% +% op: a string that is either 'c', 'v', or 'r' for create, +% verify or repair respectively. +% +% filename: the .par2 or .bin file to which 'op' is applied. +% +% Progress is reported to the command window. +% +% myobj = PauseGraphs( myobj, bool_flag ) +% +% Pause Graphs window displays. +% Note: The displays are updated at ~10 Hz. +% +% myobj = SetAnatomy_Pinpoint( myobj, 'shankdat' ) +% +% Set anatomy data string with Pinpoint format: +% [probe-id,shank-id](startpos,endpos,R,G,B,rgnname)(startpos,endpos,R,G,B,rgnname)...() +% - probe-id: SpikeGLX logical probe id. +% - shank-id: [0..n-shanks]. +% - startpos: region start in microns from tip. +% - endpos: region end in microns from tip. +% - R,G,B: region color as RGB, each [0..255]. +% - rgnname: region name text. +% +% myobj = SetAudioEnable( myobj, bool_flag ) +% +% Set audio output on/off. Note that this command has +% no effect if not currently running. +% +% myobj = SetAudioParams( myobj, group_string, params_struct ) +% +% Set subgroup of parameters for audio-out operation. Parameters +% are a struct of name/value pairs. This call stops current output. +% Call SetAudioEnable( myobj, 1 ) to restart it. +% +% myobj = SetDataDir( myobj, idir, 'dir' ) +% +% Set ith global data directory. +% Set required parameter idir to zero for main data directory. +% +% myobj = SetMetadata( myobj, metadata_struct ) +% +% If a run is in progress, set metadata to be added to the +% next output file-set. Metadata must be in the form of a +% struct of name/value pairs. +% +% myobj = SetMultiDriveEnable( myobj, bool_flag ) +% +% Set multi-drive run-splitting on/off. +% +% myobj = SetNextFileName( myobj, 'name' ) +% +% For only the next trigger (file writing event) this overrides +% all auto-naming, giving you complete control of where to save +% the files, the file name, and what g- and t-indices you want +% (if any). For example, regardless of the run's current data dir, +% run name and indices, if you set: 'otherdir/yyy_g5/yyy_g5_t7', +% SpikeGLX will save the next files in flat directory yyy_g5/: +% - otherdir/yyy_g5/yyy.g5_t7.nidq.bin,meta +% - otherdir/yyy_g5/yyy.g5_t7.imec0.ap.bin,meta +% - otherdir/yyy_g5/yyy.g5_t7.imec0.lf.bin,meta +% - otherdir/yyy_g5/yyy.g5_t7.imec1.ap.bin,meta +% - otherdir/yyy_g5/yyy.g5_t7.imec1.lf.bin,meta +% - etc. +% +% - The destination directory must already exist...No parent directories +% or probe subfolders are created in this naming mode. +% - The run must already be in progress. +% - Neither the custom name nor its indices are displayed in the Graphs +% window toolbars. Rather, the toolbars reflect standard auto-names. +% - After writing this file set, the override is cleared and auto-naming +% will resume as if you never called setNextFileName. You have to call +% setNextFileName before each trigger event to create custom trial series. +% For example, you can build a software-triggered t-series using sequence: +% + setNextFileName( 'otherdir/yyy_g0/yyy_g0_t0' ) +% + setRecordingEnable( 1 ) +% + setRecordingEnable( 0 ) +% + setNextFileName( 'otherdir/yyy_g0/yyy_g0_t1' ) +% + setRecordingEnable( 1 ) +% + setRecordingEnable( 0 ) +% + etc. +% +% myobj = SetParams( myobj, params_struct ) +% +% The inverse of GetParams.m, this sets run parameters. +% Alternatively, you can pass the parameters to StartRun() +% which calls this in turn. Run parameters are a struct of +% name/value pairs. The call will error if a run is currently +% in progress. +% +% Note: You can set any subset of [DAQSettings]. +% +% myobj = SetParamsImecCommon( myobj, params_struct ) +% +% The inverse of GetParamsImecCommon.m, this sets parameters +% common to all enabled probes. Parameters are a struct of +% name/value pairs. The call will error if a run is currently +% in progress. +% +% Note: You can set any subset of [DAQ_Imec_All]. +% +% myobj = SetParamsImecProbe( myobj, params_struct, ip ) +% +% The inverse of GetParamsImecProbe.m, this sets parameters +% for a given logical probe. Parameters are a struct of +% name/value pairs. The call will error if file writing +% is currently in progress. +% +% Note: You can set any subset of fields under [SerialNumberToProbe]/SNjjj. +% +% myobj = SetParamsOneBox( myobj, params_struct, ip, slot ) +% +% The inverse of GetParamsOneBox.m, this sets params +% for a selected OneBox. Parameters are a struct of +% name/value pairs. +% +% To reference a OneBox configured as a recording stream +% set ip to its stream-id; if ip >= 0, slot is ignored. +% Any selected OneBox can also be referenced by setting +% ip = -1, and giving its slot index. +% +% The call will error if a run is currently in progress. +% +% Note: You can set any subset of fields under [SerialNumberToOneBox]/SNjjj. +% +% myobj = SetRecordingEnable( myobj, bool_flag ) +% +% Set gate (file writing) on/off during run. +% +% When auto-naming is in effect, opening the gate advances +% the g-index and resets the t-index to zero. Auto-naming is +% on unless SetNextFileName has been used to override it. +% +% myobj = SetRunName( myobj, 'name' ) +% +% Set the run name for the next time files are created +% (either by trigger, SetRecordingEnable() or by StartRun()). +% +% myobj = SetTriggerOffBeep( myobj, hertz, millisec ) +% +% During a run, set frequency and duration of Windows +% beep signaling file closure. hertz=0 disables the beep. +% +% myobj = SetTriggerOnBeep( myobj, hertz, millisec ) +% +% During a run set frequency and duration of Windows +% beep signaling file creation. hertz=0 disables the beep. +% +% myobj = StartRun( myobj ) +% myobj = StartRun( myobj, params_struct ) +% myobj = StartRun( myobj, 'runName' ) +% +% Start data acquisition run. Optional second argument (params) +% is a struct of name/value pairs as returned from GetParams.m. +% Alternatively, the second argument can be a string (runName). +% Last-used parameters remain in effect if not specified here. +% An error is flagged if already running or a parameter is bad. +% +% myobj = StopRun( myobj ) +% +% Unconditionally stop current run, close data files +% and return to idle state. +% +% myobj = TriggerGT( myobj, g, t ) +% +% Using standard auto-naming, set both the gate (g) and +% trigger (t) levels that control file writing. +% -1 = no change. +% 0 = set low. +% 1 = increment and set high. +% E.g., triggerGT( -1, 1 ) = same g, increment t, start writing. +% +% - TriggerGT only affects the 'Remote controlled' gate type and/or +% the 'Remote controlled' trigger type. +% - The 'Enable Recording' button, when shown, is a master override +% switch. TriggerGT is blocked until you click the button or call +% SetRecordingEnable. +% +% res = VerifySha1( myobj, 'filename' ) +% +% Verifies the SHA1 sum of the file specified by filename. +% If filename is relative, it is appended to the run dir. +% Absolute path/filenames are also supported. Since this is +% a potentially long operation, it uses the 'disp' command +% to print progress information to the MATLAB console. The +% returned value is 1 if verified, 0 otherwise. + diff --git a/ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/API/@SpikeGL/DoGetCells.m b/ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/API/@SpikeGL/DoGetCells.m new file mode 100644 index 00000000..86c5eaf1 --- /dev/null +++ b/ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/API/@SpikeGL/DoGetCells.m @@ -0,0 +1,17 @@ +% THIS FUNCTION IS PRIVATE AND SHOULD NOT BE CALLED BY OUTSIDE CODE! +% +% Fetch one or more lines, excluding {OK, ERROR} lines. +% Return as cell-array of strings. +% +function [res] = DoGetCells( sm, cmd ) + + ChkConn( sm ); + + ok = CalinsNetMex( 'sendstring', sm.handle, sprintf( '%s\n', cmd ) ); + + if( isempty( ok ) ) + error( '[%s] error: Cannot send string.', cmd ); + end + + res = CalinsNetMex( 'getcells', sm.handle ); +end diff --git a/ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/API/@SpikeGL/DoQuery.m b/ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/API/@SpikeGL/DoQuery.m new file mode 100644 index 00000000..05c86bcc --- /dev/null +++ b/ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/API/@SpikeGL/DoQuery.m @@ -0,0 +1,20 @@ +% THIS FUNCTION IS PRIVATE AND SHOULD NOT BE CALLED BY OUTSIDE CODE! +% +% Fetch one line, excluding {OK, ERROR} lines. +% Return as single string. +% +% To include OK, m-files should directly call: +% line = CalinsNetMex( 'readline', s.handle ); +% +function [res] = DoQuery( sm, cmd ) + + ChkConn( sm ); + + ok = CalinsNetMex( 'sendstring', sm.handle, sprintf( '%s\n', cmd ) ); + + if( isempty( ok ) ) + error( '[%s] error: Cannot send string.', cmd ); + end + + res = CalinsNetMex( 'querystring', sm.handle ); +end diff --git a/ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/API/@SpikeGL/DoSimpleCmd.m b/ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/API/@SpikeGL/DoSimpleCmd.m new file mode 100644 index 00000000..58cc3ea8 --- /dev/null +++ b/ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/API/@SpikeGL/DoSimpleCmd.m @@ -0,0 +1,14 @@ +% THIS FUNCTION IS PRIVATE AND SHOULD NOT BE CALLED BY OUTSIDE CODE! +% +function [res] = DoSimpleCmd( sm, cmd ) + + ChkConn( sm ); + + res = CalinsNetMex( 'sendstring', sm.handle, sprintf( '%s\n', cmd ) ); + + if( isempty( res ) ) + error( 'Empty result for command [%s]; probably disconnected.', cmd ); + end + + ReceiveOK( sm, cmd ); +end diff --git a/ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/API/@SpikeGL/EnumDataDir.m b/ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/API/@SpikeGL/EnumDataDir.m new file mode 100644 index 00000000..72cbfd8d --- /dev/null +++ b/ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/API/@SpikeGL/EnumDataDir.m @@ -0,0 +1,15 @@ +% params = EnumDataDir( myobj, i ) +% +% Retrieve a listing of files in the ith data directory. +% Get main data directory by setting i=0 or omitting it. +% +function [ret] = EnumDataDir( s, varargin ) + + i = 0; + + if( nargin > 1 ) + i = varargin{1}; + end + + ret = DoGetCells( s, sprintf( 'ENUMDATADIR %d', i ) ); +end diff --git a/ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/API/@SpikeGL/Fetch.m b/ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/API/@SpikeGL/Fetch.m new file mode 100644 index 00000000..c177db52 --- /dev/null +++ b/ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/API/@SpikeGL/Fetch.m @@ -0,0 +1,74 @@ +% [daqData,headCt] = Fetch( myObj, js, ip, start_samp, max_samps, channel_subset, downsample_ratio ) +% +% Get MxN matrix of stream data. +% M = samp_count, MIN(max_samps,available). +% N = channel count... +% Data are int16 type. +% If filtered IM stream buffers are enabled, you may fetch from them with js=-2. +% Fetching starts at index start_samp. +% channel_subset is an optional vector of specific channels to fetch [a,b,c...], or, +% [-1] = all acquired channels, or, +% [-2] = all saved channels. +% downsample_ratio is an integer; return every Nth sample (default = 1). +% Also returns headCt = index of first sample in matrix. +% +function [mat,headCt] = Fetch( s, js, ip, start_samp, max_samps, varargin ) + + if( nargin < 5 ) + error( 'Fetch: Requires at least 5 arguments.' ); + end + + if( ~isnumeric( start_samp ) || ~size( start_samp, 1 ) ) + error( 'Fetch: Invalid samp_start parameter.' ); + end + + if( ~isnumeric( max_samps ) || ~size( max_samps, 1 ) ) + error( 'Fetch: Invalid max_samps parameter.' ); + end + + ChkConn( s ); + + % subset has pattern id1#id2#... + if( nargin >= 6 ) + subset = sprintf( '%d#', varargin{1} ); + else + subset = '-1#'; + end + + dwnsmp = 1; + + if( nargin >= 7 ) + + dwnsmp = varargin{2}; + + if( ~isnumeric( dwnsmp ) || length( dwnsmp ) > 1 ) + error( 'Fetch: Downsample factor must be a single numeric value.' ); + end + end + + ok = CalinsNetMex( 'sendstring', s.handle, ... + sprintf( 'FETCH %d %d %u %d %s %d\n', ... + js, ip, uint64(start_samp), max_samps, subset, dwnsmp ) ); + + line = CalinsNetMex( 'readline', s.handle ); + + if( isempty( line ) ) + error( 'Fetch: Failed - see warning.' ); + end + + % cells = strsplit( line ); + cells = strread( line, '%s' ); + mat_dims = [str2num(cells{2}) str2num(cells{3})]; + headCt = str2num(cells{4}); + + if( ~isnumeric( mat_dims ) || ~size( mat_dims, 2 ) ) + error( 'Fetch: Invalid matrix dimensions.' ); + end + + mat = CalinsNetMex( 'readmatrix', s.handle, 'int16', mat_dims ); + + % transpose + mat = mat'; + + ReceiveOK( s, 'FETCH' ); +end diff --git a/ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/API/@SpikeGL/FetchLatest.m b/ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/API/@SpikeGL/FetchLatest.m new file mode 100644 index 00000000..d029202e --- /dev/null +++ b/ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/API/@SpikeGL/FetchLatest.m @@ -0,0 +1,42 @@ +% [daqData,headCt] = FetchLatest( myObj, js, ip, max_samps, channel_subset, downsample_ratio ) +% +% Get MxN matrix of the most recent stream data. +% M = samp_count, MIN(max_samps,available). +% N = channel count... +% Data are int16 type. +% If filtered IM stream buffers are enabled, you may fetch from them with js=-2. +% channel_subset is an optional vector of specific channels to fetch [a,b,c...], or, +% [-1] = all acquired channels, or, +% [-2] = all saved channels. +% downsample_ratio is an integer; return every Nth sample (default = 1). +% Also returns headCt = index of first sample in matrix. +% +function [mat,headCt] = FetchLatest( s, js, ip, max_samps, varargin ) + + if( nargin < 4 ) + error( 'FetchLatest: Requires at least four arguments.' ); + else if( nargin >= 5 ) + subset = varargin{1}; + else + subset = [-1]; + end + + dwnsmp = 1; + + if( nargin >= 6 ) + + dwnsmp = varargin{2}; + + if( ~isnumeric( dwnsmp ) || length( dwnsmp ) > 1 ) + error( 'FetchLatest: Downsample factor must be a single numeric value.' ); + end + end + + max_ct = GetStreamSampleCount( s, js, ip ); + + if( max_samps > max_ct ) + max_samps = max_ct; + end + + [mat,headCt] = Fetch( s, js, ip, max_ct - max_samps, max_samps, subset, dwnsmp ); +end diff --git a/ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/API/@SpikeGL/GetDataDir.m b/ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/API/@SpikeGL/GetDataDir.m new file mode 100644 index 00000000..edb2764c --- /dev/null +++ b/ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/API/@SpikeGL/GetDataDir.m @@ -0,0 +1,15 @@ +% dir = GetDataDir( myobj, i ) +% +% Get ith global data directory. +% Get main data directory by setting i=0 or omitting it. +% +function [ret] = GetDataDir( s, varargin ) + + i = 0; + + if( nargin > 1 ) + i = varargin{1}; + end + + ret = DoQuery( s, sprintf( 'GETDATADIR %d', i ) ); +end diff --git a/ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/API/@SpikeGL/GetGeomMap.m b/ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/API/@SpikeGL/GetGeomMap.m new file mode 100644 index 00000000..d19cfb9f --- /dev/null +++ b/ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/API/@SpikeGL/GetGeomMap.m @@ -0,0 +1,47 @@ +% params = GetGeomMap( myobj, ip ) +% +% Get geomMap for given logical imec probe. +% Returned as a struct of name/value pairs. +% Header fields: +% head_partNumber ; string +% head_numShanks +% head_shankPitch ; microns +% head_shankWidth ; microns +% Channel 5, e.g.: +% ch5_s ; shank index +% ch5_x ; microns from left edge of shank +% ch5_z ; microns from center of tip-most electrode row +% ch5_u ; used-flag (in CAR operations) +% +function ret = GetGeomMap( s, ip ) + + ret = struct(); + res = DoGetCells( s, sprintf( 'GETGEOMMAP %d', ip ) ); + + % res is a cell array, each cell containing a string of form + % ' = ' + % Parameter names are sequences of word characters [a-z_A-Z0-9]. + % Parameter values become doubles. + + for i = 1:length( res ) + + % (?expr) captures token matching expr and names it 'xxx' + + pair = ... + regexp( res{i}, ... + '^\s*(?\w+)\s*=\s*(?.*)\s*$', 'names' ); + + % pair is a struct array with at most one element. If there + % is an element, then pair.name is the (string) name, and + % pair.value is a double value, except for head_partNumber. + + if( ~isempty( pair ) ) + if( i == 1 ) + % partNumber string + ret.(pair.name) = pair.value; + else + ret.(pair.name) = str2num( pair.value ); + end + end + end +end diff --git a/ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/API/@SpikeGL/GetImecChanGains.m b/ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/API/@SpikeGL/GetImecChanGains.m new file mode 100644 index 00000000..52b73344 --- /dev/null +++ b/ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/API/@SpikeGL/GetImecChanGains.m @@ -0,0 +1,12 @@ +% [APgain,LFgain] = GetImecChanGains( myobj, ip, chan ) +% +% Returns the AP and LF gains for given probe and channel. +% +function [APgain,LFgain] = GetImecChanGains( s, ip, chan ) + + ret = DoQuery( s, sprintf( 'GETIMECCHANGAINS %d %d', ip, chan ) ); + C = textscan( ret, '%f %f' ); + + APgain = C{1}; + LFgain = C{2}; +end diff --git a/ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/API/@SpikeGL/GetParams.m b/ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/API/@SpikeGL/GetParams.m new file mode 100644 index 00000000..35cba5c1 --- /dev/null +++ b/ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/API/@SpikeGL/GetParams.m @@ -0,0 +1,54 @@ +% params = GetParams( myobj ) +% +% Get the most recently used run parameters. +% These are a struct of name/value pairs. +% +function ret = GetParams( s ) + + ret = struct(); + res = DoGetCells( s, 'GETPARAMS' ); + + % res is a cell array, each cell containing a string of form + % ' = ' + % Parameter names are sequences of word characters [a-z_A-Z0-9] + % some examples of parameter values: + % 3 + % 0.5 + % -1 + % false + % 0:1 + % 0=1,1=2 + % Dev1 + % C:/Documents and Settings/labadmin/My Documents/data.bin + + for i = 1:length( res ) + + % (?expr) captures token matching expr and names it 'xxx' + + pair = ... + regexp( res{i}, ... + '^\s*(?\w+)\s*=\s*(?.*)\s*$', 'names' ); + + % pair is a struct array with at most one element. If there + % is an element, then pair.name is the (string) name, and + % pair.value is the (string) value. + + if( ~isempty( pair ) ) + + % If looks like a double, convert to double. + + dblpat = ... + regexp( pair.value, ... + '^\s*[-+]?[0-9]*\.?[0-9]+([eE][-+]?[0-9]+)?\s*$', ... + 'once' ); + + if( isempty( dblpat ) ) + % store the value as a string + ret.(pair.name) = pair.value; + else + % convert to double + ret.(pair.name) = str2double( pair.value ); + end + end + end +end diff --git a/ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/API/@SpikeGL/GetParamsImecCommon.m b/ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/API/@SpikeGL/GetParamsImecCommon.m new file mode 100644 index 00000000..b7afa569 --- /dev/null +++ b/ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/API/@SpikeGL/GetParamsImecCommon.m @@ -0,0 +1,54 @@ +% params = GetParamsImecCommon( myobj ) +% +% Get imec parameters common to all enabled probes. +% Returned as a struct of name/value pairs. +% +function ret = GetParamsImecCommon( s ) + + ret = struct(); + res = DoGetCells( s, 'GETPARAMSIMALL' ); + + % res is a cell array, each cell containing a string of form + % ' = ' + % Parameter names are sequences of word characters [a-z_A-Z0-9] + % some examples of parameter values: + % 3 + % 0.5 + % -1 + % false + % 0:1 + % 0=1,1=2 + % Dev1 + % C:/Documents and Settings/labadmin/My Documents/data.bin + + for i = 1:length( res ) + + % (?expr) captures token matching expr and names it 'xxx' + + pair = ... + regexp( res{i}, ... + '^\s*(?\w+)\s*=\s*(?.*)\s*$', 'names' ); + + % pair is a struct array with at most one element. If there + % is an element, then pair.name is the (string) name, and + % pair.value is the (string) value. + + if( ~isempty( pair ) ) + + % If looks like a double, convert to double. + + dblpat = ... + regexp( pair.value, ... + '^\s*[-+]?[0-9]*\.?[0-9]+([eE][-+]?[0-9]+)?\s*$', ... + 'once' ); + + if( isempty( dblpat ) ) + % store the value as a string + ret.(pair.name) = pair.value; + else + % convert to double + ret.(pair.name) = str2double( pair.value ); + end + end + end +end diff --git a/ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/API/@SpikeGL/GetParamsImecProbe.m b/ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/API/@SpikeGL/GetParamsImecProbe.m new file mode 100644 index 00000000..5074e97f --- /dev/null +++ b/ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/API/@SpikeGL/GetParamsImecProbe.m @@ -0,0 +1,54 @@ +% params = GetParamsImecProbe( myobj, ip ) +% +% Get imec parameters for given logical probe. +% Returned as a struct of name/value pairs. +% +function ret = GetParamsImecProbe( s, ip ) + + ret = struct(); + res = DoGetCells( s, sprintf( 'GETPARAMSIMPRB %d', ip ) ); + + % res is a cell array, each cell containing a string of form + % ' = ' + % Parameter names are sequences of word characters [a-z_A-Z0-9] + % some examples of parameter values: + % 3 + % 0.5 + % -1 + % false + % 0:1 + % 0=1,1=2 + % Dev1 + % C:/Documents and Settings/labadmin/My Documents/data.bin + + for i = 1:length( res ) + + % (?expr) captures token matching expr and names it 'xxx' + + pair = ... + regexp( res{i}, ... + '^\s*(?\w+)\s*=\s*(?.*)\s*$', 'names' ); + + % pair is a struct array with at most one element. If there + % is an element, then pair.name is the (string) name, and + % pair.value is the (string) value. + + if( ~isempty( pair ) ) + + % If looks like a double, convert to double. + + dblpat = ... + regexp( pair.value, ... + '^\s*[-+]?[0-9]*\.?[0-9]+([eE][-+]?[0-9]+)?\s*$', ... + 'once' ); + + if( isempty( dblpat ) ) + % store the value as a string + ret.(pair.name) = pair.value; + else + % convert to double + ret.(pair.name) = str2double( pair.value ); + end + end + end +end diff --git a/ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/API/@SpikeGL/GetParamsOnebox.m b/ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/API/@SpikeGL/GetParamsOnebox.m new file mode 100644 index 00000000..87d7993b --- /dev/null +++ b/ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/API/@SpikeGL/GetParamsOnebox.m @@ -0,0 +1,59 @@ +% params = GetParamsOneBox( myobj, ip, slot ) +% +% Get parameters for selected OneBox; +% returned as a struct of name/value pairs. +% +% To reference a OneBox configured as a recording stream +% set ip to its stream-id; if ip >= 0, slot is ignored. +% Any selected OneBox can also be referenced by setting +% ip = -1, and giving its slot index. +% +function ret = GetParamsOneBox( s, ip, slot ) + + ret = struct(); + res = DoGetCells( s, sprintf( 'GETPARAMSOBX %d %d', ip, slot ) ); + + % res is a cell array, each cell containing a string of form + % ' = ' + % Parameter names are sequences of word characters [a-z_A-Z0-9] + % some examples of parameter values: + % 3 + % 0.5 + % -1 + % false + % 0:1 + % 0=1,1=2 + % Dev1 + % C:/Documents and Settings/labadmin/My Documents/data.bin + + for i = 1:length( res ) + + % (?expr) captures token matching expr and names it 'xxx' + + pair = ... + regexp( res{i}, ... + '^\s*(?\w+)\s*=\s*(?.*)\s*$', 'names' ); + + % pair is a struct array with at most one element. If there + % is an element, then pair.name is the (string) name, and + % pair.value is the (string) value. + + if( ~isempty( pair ) ) + + % If looks like a double, convert to double. + + dblpat = ... + regexp( pair.value, ... + '^\s*[-+]?[0-9]*\.?[0-9]+([eE][-+]?[0-9]+)?\s*$', ... + 'once' ); + + if( isempty( dblpat ) ) + % store the value as a string + ret.(pair.name) = pair.value; + else + % convert to double + ret.(pair.name) = str2double( pair.value ); + end + end + end +end diff --git a/ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/API/@SpikeGL/GetProbeList.m b/ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/API/@SpikeGL/GetProbeList.m new file mode 100644 index 00000000..65d528ad --- /dev/null +++ b/ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/API/@SpikeGL/GetProbeList.m @@ -0,0 +1,14 @@ +% list = GetProbeList( myobj ) +% +% Get string with format: +% (probeID,nShanks,partNumber)()... +% - A parenthesized entry for each selected probe. +% - probeID: zero-based integer. +% - nShanks: integer {1,4}. +% - partNumber: string, e.g., NP1000. +% - If no probes, return '()'. +% +function [list] = GetProbeList( s ) + + list = DoQuery( s, 'GETPROBELIST' ); +end diff --git a/ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/API/@SpikeGL/GetRunName.m b/ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/API/@SpikeGL/GetRunName.m new file mode 100644 index 00000000..2ce170ec --- /dev/null +++ b/ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/API/@SpikeGL/GetRunName.m @@ -0,0 +1,8 @@ +% name = GetRunName( myobj ) +% +% Get run base name. +% +function [name] = GetRunName( s ) + + name = DoQuery( s, 'GETRUNNAME' ); +end diff --git a/ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/API/@SpikeGL/GetStreamAcqChans.m b/ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/API/@SpikeGL/GetStreamAcqChans.m new file mode 100644 index 00000000..b2588abc --- /dev/null +++ b/ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/API/@SpikeGL/GetStreamAcqChans.m @@ -0,0 +1,13 @@ +% chanCounts = GetStreamAcqChans( myobj, js, ip ) +% +% For the selected substream, returns a vector of the +% number of channels of each type that stream is acquiring. +% +% js = 0: NI channels: {MN,MA,XA,DW}. +% js = 1: OB channels: {XA,DW,SY}. +% js = 2: IM channels: {AP,LF,SY}. +% +function [ret] = GetStreamAcqChans( s, js, ip ) + + ret = str2num( DoQuery( s, sprintf( 'GETSTREAMACQCHANS %d %d', js, ip ) ) ); +end diff --git a/ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/API/@SpikeGL/GetStreamFileStart.m b/ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/API/@SpikeGL/GetStreamFileStart.m new file mode 100644 index 00000000..85b23f1b --- /dev/null +++ b/ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/API/@SpikeGL/GetStreamFileStart.m @@ -0,0 +1,9 @@ +% startingSample = GetStreamFileStart( myobj, js, ip ) +% +% Returns index of first sample in selected file, +% or zero if unavailable. +% +function [ret] = GetStreamFileStart( s, js, ip ) + + ret = str2double( DoQuery( s, sprintf( 'GETSTREAMFILESTART %d %d', js, ip ) ) ); +end diff --git a/ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/API/@SpikeGL/GetStreamI16ToVolts.m b/ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/API/@SpikeGL/GetStreamI16ToVolts.m new file mode 100644 index 00000000..4bc2009e --- /dev/null +++ b/ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/API/@SpikeGL/GetStreamI16ToVolts.m @@ -0,0 +1,9 @@ +% mult = GetStreamI16ToVolts( myobj, js, ip, chan ) +% +% Returns multiplier converting 16-bit binary channel to volts. +% +function [ret] = GetStreamI16ToVolts( s, js, ip, chan ) + + ret = str2double( DoQuery( s, ... + sprintf( 'GETSTREAMI16TOVOLTS %d %d %d', js, ip, chan ) ) ); +end diff --git a/ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/API/@SpikeGL/GetStreamMaxInt.m b/ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/API/@SpikeGL/GetStreamMaxInt.m new file mode 100644 index 00000000..66a70f64 --- /dev/null +++ b/ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/API/@SpikeGL/GetStreamMaxInt.m @@ -0,0 +1,8 @@ +% maxInt = GetStreamMaxInt( myobj, js, ip ) +% +% Returns largest positive integer value for selected stream. +% +function [ret] = GetStreamMaxInt( s, js, ip ) + + ret = sscanf( DoQuery( s, sprintf( 'GETSTREAMMAXINT %d %d', js, ip ) ), '%d' ); +end diff --git a/ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/API/@SpikeGL/GetStreamNP.m b/ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/API/@SpikeGL/GetStreamNP.m new file mode 100644 index 00000000..d21c352a --- /dev/null +++ b/ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/API/@SpikeGL/GetStreamNP.m @@ -0,0 +1,9 @@ +% n_substreams = GetStreamNP( myobj, js ) +% +% Returns number (np) of js-type substreams. +% For the given js, ip has range [0..np-1]. +% +function [ret] = GetStreamNP( s, js ) + + ret = sscanf( DoQuery( s, sprintf( 'GETSTREAMNP %d', js ) ), '%d' ); +end diff --git a/ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/API/@SpikeGL/GetStreamSN.m b/ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/API/@SpikeGL/GetStreamSN.m new file mode 100644 index 00000000..af9f76b3 --- /dev/null +++ b/ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/API/@SpikeGL/GetStreamSN.m @@ -0,0 +1,14 @@ +% [SN,type] = GetStreamSN( myobj, js, ip ) +% +% js = 1: Return OneBox SN and slot. +% js = 2: Return probe SN and type. +% SN = serial number string. +% +function [SN,type] = GetStreamSN( s, js, ip ) + + ret = DoQuery( s, sprintf( 'GETSTREAMSN %d %d', js, ip ) ); + C = textscan( ret, '%[^ ] %d' ); + + SN = C{1}; + type = C{2}; +end diff --git a/ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/API/@SpikeGL/GetStreamSampleCount.m b/ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/API/@SpikeGL/GetStreamSampleCount.m new file mode 100644 index 00000000..f1cc2f0d --- /dev/null +++ b/ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/API/@SpikeGL/GetStreamSampleCount.m @@ -0,0 +1,9 @@ +% sampleCount = GetStreamSampleCount( myobj, js, ip ) +% +% Returns number of samples since current run started, +% or zero if not running. +% +function [ret] = GetStreamSampleCount( s, js, ip ) + + ret = str2double( DoQuery( s, sprintf( 'GETSTREAMSAMPLECOUNT %d %d', js, ip ) ) ); +end diff --git a/ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/API/@SpikeGL/GetStreamSampleRate.m b/ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/API/@SpikeGL/GetStreamSampleRate.m new file mode 100644 index 00000000..c475c274 --- /dev/null +++ b/ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/API/@SpikeGL/GetStreamSampleRate.m @@ -0,0 +1,8 @@ +% sampleRate = GetStreamSampleRate( myobj, js, ip ) +% +% Returns sample rate of selected stream in Hz. +% +function [ret] = GetStreamSampleRate( s, js, ip ) + + ret = str2double( DoQuery( s, sprintf( 'GETSTREAMSAMPLERATE %d %d', js, ip ) ) ); +end diff --git a/ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/API/@SpikeGL/GetStreamSaveChans.m b/ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/API/@SpikeGL/GetStreamSaveChans.m new file mode 100644 index 00000000..d7fe7ed8 --- /dev/null +++ b/ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/API/@SpikeGL/GetStreamSaveChans.m @@ -0,0 +1,9 @@ +% channelSubset = GetStreamSaveChans( myobj, js, ip ) +% +% Returns a vector containing the indices of +% the acquired channels that are being saved. +% +function [ret] = GetStreamSaveChans( s, js, ip ) + + ret = str2num( DoQuery( s, sprintf( 'GETSTREAMSAVECHANS %d %d', js, ip ) ) ); +end diff --git a/ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/API/@SpikeGL/GetStreamShankMap.m b/ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/API/@SpikeGL/GetStreamShankMap.m new file mode 100644 index 00000000..ca2b9c76 --- /dev/null +++ b/ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/API/@SpikeGL/GetStreamShankMap.m @@ -0,0 +1,37 @@ +% [nS,nC,nR,mat] = GetStreamShankMap( myObj, js, ip ) +% +% Get shank map for NI stream (js = 0): +% {nS,nC,nR} = max {shanks, cols, rows} on this probe; +% mat = Mx4 matrix of shank map entries, where, +% M = channel count. +% 4 = given channel's zero-based {shank, col, row} indices, +% plus a 'used' flag which is 1 if the channel should be +% included in displays and spatial averaging operations. +% Data are int16 type. +% +function [nS,nC,nR,mat] = GetStreamShankMap( s, js, ip ) + + ChkConn( s ); + + ok = CalinsNetMex( 'sendstring', s.handle, ... + sprintf( 'GETSTREAMSHANKMAP %d %d\n', js, ip ) ); + + line = CalinsNetMex( 'readline', s.handle ); + + if( isempty( line ) ) + error( 'GetStreamShankMap: Failed - see warning.' ); + end + + cells = strread( line, '%s' ); + nS = str2num(cells{2}); + nC = str2num(cells{3}); + nR = str2num(cells{4}); + dims = [4 str2num(cells{5})]; + + mat = CalinsNetMex( 'readmatrix', s.handle, 'int16', dims ); + + % transpose + mat = mat'; + + ReceiveOK( s, 'GETSTREAMSHANKMAP' ); +end diff --git a/ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/API/@SpikeGL/GetStreamVoltageRange.m b/ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/API/@SpikeGL/GetStreamVoltageRange.m new file mode 100644 index 00000000..9da8eee6 --- /dev/null +++ b/ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/API/@SpikeGL/GetStreamVoltageRange.m @@ -0,0 +1,12 @@ +% [Vmin,Vmax] = GetStreamVoltageRange( myobj, js, ip ) +% +% Returns voltage range of selected data stream. +% +function [Vmin,Vmax] = GetStreamVoltageRange( s, js, ip ) + + ret = DoQuery( s, sprintf( 'GETSTREAMVOLTAGERANGE %d %d', js, ip ) ); + C = textscan( ret, '%f %f' ); + + Vmin = C{1}; + Vmax = C{2}; +end diff --git a/ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/API/@SpikeGL/GetTime.m b/ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/API/@SpikeGL/GetTime.m new file mode 100644 index 00000000..393d8a2a --- /dev/null +++ b/ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/API/@SpikeGL/GetTime.m @@ -0,0 +1,9 @@ +% time = GetTime( myobj ) +% +% Returns (double) number of seconds since SpikeGLX application +% was launched. +% +function [ret] = GetTime( s ) + + ret = sscanf( DoQuery( s, 'GETTIME' ), '%f' ); +end diff --git a/ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/API/@SpikeGL/GetVersion.m b/ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/API/@SpikeGL/GetVersion.m new file mode 100644 index 00000000..ee65de7c --- /dev/null +++ b/ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/API/@SpikeGL/GetVersion.m @@ -0,0 +1,8 @@ +% version = GetVersion( myobj ) +% +% Get SpikeGLX version string. +% +function [ret] = GetVersion( s ) + + ret = DoQuery( s, 'GETVERSION' ); +end diff --git a/ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/API/@SpikeGL/IsConsoleHidden.m b/ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/API/@SpikeGL/IsConsoleHidden.m new file mode 100644 index 00000000..056fb4a9 --- /dev/null +++ b/ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/API/@SpikeGL/IsConsoleHidden.m @@ -0,0 +1,10 @@ +% boolval = IsConsoleHidden( myobj ) +% +% Returns 1 if console window is hidden, false otherwise. +% The console window may be hidden/shown using ConsoleHide() +% and ConsoleShow(). +% +function [ret] = IsConsoleHidden( s ) + + ret = sscanf( DoQuery( s, 'ISCONSOLEHIDDEN' ), '%d' ); +end diff --git a/ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/API/@SpikeGL/IsInitialized.m b/ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/API/@SpikeGL/IsInitialized.m new file mode 100644 index 00000000..b58c75a4 --- /dev/null +++ b/ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/API/@SpikeGL/IsInitialized.m @@ -0,0 +1,9 @@ +% boolval = IsInitialized( myobj ) +% +% Return 1 if SpikeGLX has completed its startup +% initialization and is ready to run. +% +function ret = IsInitialized( s ) + + ret = sscanf( DoQuery( s, 'ISINITIALIZED' ), '%d' ); +end diff --git a/ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/API/@SpikeGL/IsRunning.m b/ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/API/@SpikeGL/IsRunning.m new file mode 100644 index 00000000..c56c9fe6 --- /dev/null +++ b/ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/API/@SpikeGL/IsRunning.m @@ -0,0 +1,8 @@ +% boolval = IsRunning( myobj ) +% +% Returns 1 if SpikeGLX is currently acquiring data. +% +function [ret] = IsRunning( s ) + + ret = sscanf( DoQuery( s, 'ISRUNNING' ), '%d' ); +end diff --git a/ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/API/@SpikeGL/IsSaving.m b/ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/API/@SpikeGL/IsSaving.m new file mode 100644 index 00000000..87f4facc --- /dev/null +++ b/ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/API/@SpikeGL/IsSaving.m @@ -0,0 +1,9 @@ +% boolval = IsSaving( myobj ) +% +% Returns 1 if the software is currently running +% AND saving data. +% +function [ret] = IsSaving( s ) + + ret = sscanf( DoQuery( s, 'ISSAVING' ), '%d' ); +end diff --git a/ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/API/@SpikeGL/IsUserOrder.m b/ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/API/@SpikeGL/IsUserOrder.m new file mode 100644 index 00000000..f5014fcb --- /dev/null +++ b/ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/API/@SpikeGL/IsUserOrder.m @@ -0,0 +1,9 @@ +% boolval = IsUserOrder( myobj, js, ip ) +% +% Returns 1 if graphs currently sorted in user order. +% This query is sent only to the main Graphs window. +% +function [ret] = IsUserOrder( s, js, ip ) + + ret = sscanf( DoQuery( s, sprintf( 'ISUSRORDER %d %d', js, ip ) ), '%d' ); +end diff --git a/ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/API/@SpikeGL/MapSample.m b/ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/API/@SpikeGL/MapSample.m new file mode 100644 index 00000000..5b5ce191 --- /dev/null +++ b/ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/API/@SpikeGL/MapSample.m @@ -0,0 +1,12 @@ +% dstSample = MapSample( myobj, dstjs, dstip, srcSample, srcjs, srcip ) +% +% Returns sample in dst stream corresponding to +% given sample in src stream. +% +function [ret] = MapSample( s, dstjs, dstip, srcSample, srcjs, srcip ) + + ret = str2double( ... + DoQuery( s, ... + sprintf( 'MAPSAMPLE %d %d %u %d %d', ... + dstjs, dstip, uint64(srcSample), srcjs, srcip ) ) ); +end diff --git a/ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/API/@SpikeGL/NI_DO_Set.m b/ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/API/@SpikeGL/NI_DO_Set.m new file mode 100644 index 00000000..3c1cb073 --- /dev/null +++ b/ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/API/@SpikeGL/NI_DO_Set.m @@ -0,0 +1,15 @@ +% myobj = NI_DO_Set( myobj, 'lines', bits ) +% +% Set one or more NI lines high/low. +% - lines is a string list of lines to set, e.g.: +% 'Dev6/port0/line2,Dev6/port0/line5' +% 'Dev6/port1/line0:3' +% 'Dev6/port1:2' +% - bits is a uint32 value, each bit maps to a line: +% The lowest 8 bits map to port 0. +% The next higher 8 bits map to port 1, etc. +% +function [s] = NI_DO_Set( s, lines, bits ) + + DoSimpleCmd( s, sprintf( 'NIDOSET %s %u', lines, bits ) ); +end diff --git a/ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/API/@SpikeGL/NI_Wave_Arm.m b/ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/API/@SpikeGL/NI_Wave_Arm.m new file mode 100644 index 00000000..16c9a7b8 --- /dev/null +++ b/ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/API/@SpikeGL/NI_Wave_Arm.m @@ -0,0 +1,27 @@ +% myobj = NI_Wave_Arm( myobj, 'outChan', 'trigTerm' ) +% +% General sequence: +% 1. NI_Wave_Load : Load wave from SpikeGLX/_Waves folder. +% 2. NI_Wave_Arm : Set triggering parameters. +% 3. NI_Wave_StartStop : Start if software trigger, stop when done. +% +% Set trigger method. +% - trigTerm is a string naming any trigger-capable terminal on your +% device, e.g., '/dev1/pfi2'. NI-DAQ requires names of terminals to +% start with a '/' character. This is indeed different than channel +% names which do not start with a slash. +% (1) Give a correct terminal string to trigger playback upon +% receiving a rising edge at that terminal. +% (2) Give any string that does NOT start with a '/' to trigger +% playback via the NI_Wave_StartStop command. +% - Multiple trigger events can NOT be given. For each trigger +% event after the first, you must first call NI_Wave_StartStop +% AND NI_Wave_Arm to stop and then rearm the task. +% +% outChan is a string naming any wave-capable analog output +% channel on your device, e.g., 'dev1/ao1'. +% +function [s] = NI_Wave_Arm( s, outChan, trigTerm ) + + DoSimpleCmd( s, sprintf( 'NIWVARM %s %s', outChan, trigTerm ) ); +end diff --git a/ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/API/@SpikeGL/NI_Wave_Load.m b/ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/API/@SpikeGL/NI_Wave_Load.m new file mode 100644 index 00000000..6d1e3b7f --- /dev/null +++ b/ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/API/@SpikeGL/NI_Wave_Load.m @@ -0,0 +1,18 @@ +% myobj = NI_Wave_Load( myobj, 'outChan', 'wave_name', loop_mode ) +% +% General sequence: +% 1. NI_Wave_Load : Load wave from SpikeGLX/_Waves folder. +% 2. NI_Wave_Arm : Set triggering parameters. +% 3. NI_Wave_StartStop : Start if software trigger, stop when done. +% +% Load a wave descriptor already placed in SpikeGLX/_Waves. +% - Pass 'mywavename' to this function; no path; no extension. +% - The playback loop_modes are: {1=loop until stopped, 0=once only}. +% +% outChan is a string naming any wave-capable analog output +% channel on your device, e.g., 'dev1/ao1'. +% +function [s] = NI_Wave_Load( s, outChan, wave_name, loop_mode ) + + DoSimpleCmd( s, sprintf( 'NIWVLOAD %s %s %d', outChan, wave_name, loop_mode ) ); +end diff --git a/ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/API/@SpikeGL/NI_Wave_StartStop.m b/ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/API/@SpikeGL/NI_Wave_StartStop.m new file mode 100644 index 00000000..58d999aa --- /dev/null +++ b/ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/API/@SpikeGL/NI_Wave_StartStop.m @@ -0,0 +1,22 @@ +% myobj = NI_Wave_StartStop( myobj, 'outChan', start_bool ) +% +% General sequence: +% 1. NI_Wave_Load : Load wave from SpikeGLX/_Waves folder. +% 2. NI_Wave_Arm : Set triggering parameters. +% 3. NI_Wave_StartStop : Start if software trigger, stop when done. +% +% Start (optionally) or stop wave playback. +% - If you selected software triggering with NI_Wave_Arm, +% then set start_bool=1 to start playback. +% - In all cases, set start_bool=0 to stop playback. +% - It is best to stop playback before changing wave parameters. +% - After playback or if looping mode is interrupted, the voltage +% remains at the last output level. +% +% outChan is a string naming any wave-capable analog output +% channel on your device, e.g., 'dev1/ao1'. +% +function [s] = NI_Wave_StartStop( s, outChan, start_bool ) + + DoSimpleCmd( s, sprintf( 'NIWVSTSP %s %d', outChan, start_bool ) ); +end diff --git a/ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/API/@SpikeGL/OBX_AO_Set.m b/ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/API/@SpikeGL/OBX_AO_Set.m new file mode 100644 index 00000000..ec0bd8eb --- /dev/null +++ b/ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/API/@SpikeGL/OBX_AO_Set.m @@ -0,0 +1,20 @@ +% myobj = OBX_AO_Set( myobj, ip, slot, 'chn_vlt' ) +% +% Set one or more OneBox AO (DAC) channel voltages. +% - chn_vlt is a string with format: (chan,volts)(chan,volts)...() +% - The chan values are integer AO indices in range [0,11]. +% - You can only use AO channels already listed on the OBX setup tab. +% - Voltages are double values in range [-5.0,5.0] V. +% - DAC is 16-bit; theoretical resolution is (10 V)/(2^16) ~ .0001526 V. +% - Practical resolution, given noise, appears to be ~ 0.002 V. +% - AO channels are disabled at run start/end; voltage ~ 1.56 V. +% +% To reference a OneBox configured as a recording stream +% set ip to its stream-id; if ip >= 0, slot is ignored. +% Any selected OneBox can also be referenced by setting +% ip = -1, and giving its slot index. +% +function [s] = OBX_AO_Set( s, ip, slot, chn_vlt ) + + DoSimpleCmd( s, sprintf( 'OBXAOSET %d %d %s', ip, slot, chn_vlt ) ); +end diff --git a/ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/API/@SpikeGL/OBX_Wave_Arm.m b/ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/API/@SpikeGL/OBX_Wave_Arm.m new file mode 100644 index 00000000..e0e21e9b --- /dev/null +++ b/ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/API/@SpikeGL/OBX_Wave_Arm.m @@ -0,0 +1,27 @@ +% myobj = OBX_Wave_Arm( myobj, ip, slot, trigger, loop_mode ) +% +% General sequence: +% 1. OBX_Wave_Load : Load wave from SpikeGLX/_Waves folder. +% 2. OBX_Wave_Arm : Set triggering parameters. +% 3. OBX_Wave_StartStop : Start if software trigger, stop when done. +% +% Set trigger method, and playback loop_mode. +% - Trigger values...Playback starts: +% -2 : By calling OBX_Wave_StartStop. +% -1 : When TTL rising edge sent to SMA1. +% 0-11 : When TTL rising edge sent to that XA (ADC) channel. +% - To use an ADC channel, you must name it as an XA channel on +% the OBX setup tab of the Acquisition Configuration dialog. +% - Multiple trigger events (either hardware or software) can be +% given without needing to rearm. +% - The playback loop_modes are: {1=loop until stopped, 0=once only}. +% +% To reference a OneBox configured as a recording stream +% set ip to its stream-id; if ip >= 0, slot is ignored. +% Any selected OneBox can also be referenced by setting +% ip = -1, and giving its slot index. +% +function [s] = OBX_Wave_Arm( s, ip, slot, trigger, loop_mode ) + + DoSimpleCmd( s, sprintf( 'OBXWVARM %d %d %d %d', ip, slot, trigger, loop_mode ) ); +end diff --git a/ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/API/@SpikeGL/OBX_Wave_Load.m b/ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/API/@SpikeGL/OBX_Wave_Load.m new file mode 100644 index 00000000..7c0cf5e0 --- /dev/null +++ b/ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/API/@SpikeGL/OBX_Wave_Load.m @@ -0,0 +1,19 @@ +% myobj = OBX_Wave_Load( myobj, ip, slot, 'wave_name' ) +% +% General sequence: +% 1. OBX_Wave_Load : Load wave from SpikeGLX/_Waves folder. +% 2. OBX_Wave_Arm : Set triggering parameters. +% 3. OBX_Wave_StartStop : Start if software trigger, stop when done. +% +% Load a wave descriptor already placed in SpikeGLX/_Waves. +% - Pass 'mywavename' to this function; no path; no extension. +% +% To reference a OneBox configured as a recording stream +% set ip to its stream-id; if ip >= 0, slot is ignored. +% Any selected OneBox can also be referenced by setting +% ip = -1, and giving its slot index. +% +function [s] = OBX_Wave_Load( s, ip, slot, wave_name ) + + DoSimpleCmd( s, sprintf( 'OBXWVLOAD %d %d %s', ip, slot, wave_name ) ); +end diff --git a/ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/API/@SpikeGL/OBX_Wave_StartStop.m b/ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/API/@SpikeGL/OBX_Wave_StartStop.m new file mode 100644 index 00000000..1f144463 --- /dev/null +++ b/ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/API/@SpikeGL/OBX_Wave_StartStop.m @@ -0,0 +1,27 @@ +% myobj = OBX_Wave_StartStop( myobj, ip, slot, start_bool ) +% +% General sequence: +% 1. OBX_Wave_Load : Load wave from SpikeGLX/_Waves folder. +% 2. OBX_Wave_Arm : Set triggering parameters. +% 3. OBX_Wave_StartStop : Start if software trigger, stop when done. +% +% Start (optionally) or stop wave playback. +% - If you selected software triggering with OBX_Wave_Arm, +% then set start_bool=1 to start playback. +% - In all cases, set start_bool=0 to stop playback. +% - It is best to stop playback before changing wave parameters. +% - Waves only play at AO (DAC) channel-0. +% - To use the waveplayer, you must name channel AO-0 on +% the OBX setup tab of the Acquisition Configuration dialog. +% - After playback or if looping mode is interrupted, the voltage +% remains at the last output level. +% +% To reference a OneBox configured as a recording stream +% set ip to its stream-id; if ip >= 0, slot is ignored. +% Any selected OneBox can also be referenced by setting +% ip = -1, and giving its slot index. +% +function [s] = OBX_Wave_StartStop( s, ip, slot, start_bool ) + + DoSimpleCmd( s, sprintf( 'OBXWVSTSP %d %d %d', ip, slot, start_bool ) ); +end diff --git a/ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/API/@SpikeGL/Opto_emit.m b/ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/API/@SpikeGL/Opto_emit.m new file mode 100644 index 00000000..69244d88 --- /dev/null +++ b/ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/API/@SpikeGL/Opto_emit.m @@ -0,0 +1,11 @@ +% myobj = Opto_emit( myobj, ip, color, site ) +% +% Direct emission to specified site (-1=dark). +% ip: imec probe index. +% color: {0=blue, 1=red}. +% site: [0..13], or, -1=dark. +% +function [s] = Opto_emit( s, ip, color, site ) + + DoSimpleCmd( s, sprintf( 'OPTOEMIT %d %d %d', ip, color, site ) ); +end diff --git a/ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/API/@SpikeGL/Opto_getAttenuations.m b/ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/API/@SpikeGL/Opto_getAttenuations.m new file mode 100644 index 00000000..1f734e6b --- /dev/null +++ b/ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/API/@SpikeGL/Opto_getAttenuations.m @@ -0,0 +1,13 @@ +% [site_atten_factors] = Opto_getAttenuations( myobj, ip, color ) +% +% Returns vector of 14 (double) site power attenuation factors. +% ip: imec probe index. +% color: {0=blue, 1=red}. +% +function [ret] = Opto_getAttenuations( s, ip, color ) + + ret = sscanf( ... + DoQuery( s, ... + sprintf( 'OPTOGETATTENS %d %d', ip, color ) ), ... + '%f' ); +end diff --git a/ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/API/@SpikeGL/Par2.m b/ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/API/@SpikeGL/Par2.m new file mode 100644 index 00000000..0d8e4ef7 --- /dev/null +++ b/ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/API/@SpikeGL/Par2.m @@ -0,0 +1,43 @@ +% res = Par2( myobj, op, 'filename' ) +% +% Create, Verify, or Repair Par2 redundancy files for +% 'filename'. Arguments: +% +% op: a string that is either 'c', 'v', or 'r' for create, +% verify or repair respectively. +% +% filename: the .par2 or .bin file to which 'op' is applied. +% +% Progress is reported to the command window. +% +function [res] = Par2( s, op, file ) + + res = 0; + + if( ~strcmp( op, 'v' ) && ~strcmp( op, 'r' ) && ~strcmp( op, 'c' ) ) + error( 'Par2: Op must be one of ''v'', ''r'' or ''c''.' ); + end + + ChkConn( s ); + + if( IsRunning( s ) ) + error( 'Par2: Cannot use while running.' ); + end + + ok = CalinsNetMex( 'sendstring', s.handle, ... + sprintf( 'PAR2 %s %s\n', op, file ) ); + + while( 1 ) + + line = CalinsNetMex( 'readline', s.handle ); + + if( strcmp( line, 'OK' ) ) + res = 1; + break; + end + + if( ~isempty( line ) ) + fprintf( '%s\n', line ); + end + end +end diff --git a/ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/API/@SpikeGL/PauseGraphs.m b/ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/API/@SpikeGL/PauseGraphs.m new file mode 100644 index 00000000..6bf25154 --- /dev/null +++ b/ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/API/@SpikeGL/PauseGraphs.m @@ -0,0 +1,13 @@ +% myobj = PauseGraphs( myobj, bool_flag ) +% +% Pause Graphs window displays. +% Note: The displays are updated at ~10 Hz. +% +function [s] = PauseGraphs( s, b ) + + if( ~isnumeric( b ) ) + error( 'PauseGraphs: Arg 2 must be a Boolean value {0,1}.' ); + end + + DoSimpleCmd( s, sprintf( 'PAUSEGRF %d', b ) ); +end diff --git a/ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/API/@SpikeGL/ReceiveOK.m b/ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/API/@SpikeGL/ReceiveOK.m new file mode 100644 index 00000000..43706f02 --- /dev/null +++ b/ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/API/@SpikeGL/ReceiveOK.m @@ -0,0 +1,10 @@ +% THIS FUNCTION IS PRIVATE AND SHOULD NOT BE CALLED BY OUTSIDE CODE! +% +function [] = ReceiveOK( sm, cmd ) + + line = CalinsNetMex( 'readline', sm.handle ); + + if( isempty( strfind( line, 'OK' ) ) ) + error( 'After cmd [%s] got [%s] but expected ''OK''.\n', cmd, line ); + end +end diff --git a/ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/API/@SpikeGL/ReceiveREADY.m b/ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/API/@SpikeGL/ReceiveREADY.m new file mode 100644 index 00000000..35c3cf16 --- /dev/null +++ b/ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/API/@SpikeGL/ReceiveREADY.m @@ -0,0 +1,10 @@ +% THIS FUNCTION IS PRIVATE AND SHOULD NOT BE CALLED BY OUTSIDE CODE! +% +function [] = ReceiveREADY( sm, cmd ) + + line = CalinsNetMex( 'readline', sm.handle ); + + if( isempty( strfind( line, 'READY' ) ) ) + error( 'After cmd [%s] got [%s] but expected ''READY''.\n', cmd, line ); + end +end diff --git a/ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/API/@SpikeGL/SetAnatomy_Pinpoint.m b/ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/API/@SpikeGL/SetAnatomy_Pinpoint.m new file mode 100644 index 00000000..c9bbcb13 --- /dev/null +++ b/ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/API/@SpikeGL/SetAnatomy_Pinpoint.m @@ -0,0 +1,19 @@ +% myobj = SetAnatomy_Pinpoint( myobj, 'shankdat' ) +% +% Set anatomy data string with Pinpoint format: +% [probe-id,shank-id](startpos,endpos,R,G,B,rgnname)(startpos,endpos,R,G,B,rgnname)...() +% - probe-id: SpikeGLX logical probe id. +% - shank-id: [0..n-shanks]. +% - startpos: region start in microns from tip. +% - endpos: region end in microns from tip. +% - R,G,B: region color as RGB, each [0..255]. +% - rgnname: region name text. +% +function [s] = SetAnatomy_Pinpoint( s, shankdat ) + + if( ~ischar( shankdat ) ) + error( 'SetAnatomy_Pinpoint: Argument must be a string.' ); + end + + DoSimpleCmd( s, sprintf( 'SETANATOMYPP %s', shankdat ) ); +end diff --git a/ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/API/@SpikeGL/SetAudioEnable.m b/ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/API/@SpikeGL/SetAudioEnable.m new file mode 100644 index 00000000..f701590d --- /dev/null +++ b/ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/API/@SpikeGL/SetAudioEnable.m @@ -0,0 +1,18 @@ +% myobj = SetAudioEnable( myobj, bool_flag ) +% +% Set audio output on/off. Note that this command has +% no effect if not currently running. +% +function [s] = SetAudioEnable( s, b ) + + if( ~isnumeric( b ) ) + error( 'SetAudioEnable: Arg 2 must be a Boolean value {0,1}.' ); + end + + if( ~IsRunning( s ) ) + warning( 'SetAudioEnable: Not running, command ignored.' ); + return; + end + + DoSimpleCmd( s, sprintf( 'SETAUDIOENABLE %d', b ) ); +end diff --git a/ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/API/@SpikeGL/SetAudioParams.m b/ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/API/@SpikeGL/SetAudioParams.m new file mode 100644 index 00000000..c09c0779 --- /dev/null +++ b/ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/API/@SpikeGL/SetAudioParams.m @@ -0,0 +1,49 @@ +% myobj = SetAudioParams( myobj, group_string, params_struct ) +% +% Set subgroup of parameters for audio-out operation. Parameters +% are a struct of name/value pairs. This call stops current output. +% Call SetAudioEnable( myobj, 1 ) to restart it. +% +function [s] = SetAudioParams( s, group, params ) + + if( ~ischar( group ) ) + error( 'SetAudioParams: ''group'' argument must be a string.' ); + end + + if( ~length( group ) ) + error( 'SetAudioParams: ''group'' argument must not be empty.' ); + end + + if( ~isstruct( params ) ) + error( 'SetAudioParams: ''params'' argument must be a struct.' ); + end + + ChkConn( s ); + + ok = CalinsNetMex( 'sendstring', s.handle, ... + sprintf( 'SETAUDIOPARAMS %s\n', group ) ); + + ReceiveREADY( s, 'SETAUDIOPARAMS' ); + + names = fieldnames( params ); + + for i = 1:length( names ) + + f = params.(names{i}); + + if( isnumeric( f ) && isscalar( f ) ) + line = sprintf( '%s=%g\n', names{i}, f ); + elseif( ischar( f ) ) + line = sprintf( '%s=%s\n', names{i}, f ); + else + error( 'SetAudioParams: Field %s must be numeric scalar or a string.', names{i} ); + end + + ok = CalinsNetMex( 'sendstring', s.handle, line ); + end + + % end with blank line + ok = CalinsNetMex( 'sendstring', s.handle, sprintf( '\n' ) ); + + ReceiveOK( s, 'SETAUDIOPARAMS' ); +end diff --git a/ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/API/@SpikeGL/SetDataDir.m b/ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/API/@SpikeGL/SetDataDir.m new file mode 100644 index 00000000..22903ba9 --- /dev/null +++ b/ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/API/@SpikeGL/SetDataDir.m @@ -0,0 +1,13 @@ +% myobj = SetDataDir( myobj, idir, 'dir' ) +% +% Set ith global data directory. +% Set required parameter idir to zero for main data directory. +% +function [s] = SetDataDir( s, idir, dir ) + + if( ~ischar( dir ) ) + error( 'SetDataDir: ''dir'' argument must be a string.' ); + end + + DoSimpleCmd( s, sprintf( 'SETDATADIR %d %s', idir, dir ) ); +end diff --git a/ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/API/@SpikeGL/SetMetaData.m b/ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/API/@SpikeGL/SetMetaData.m new file mode 100644 index 00000000..bfab7f3d --- /dev/null +++ b/ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/API/@SpikeGL/SetMetaData.m @@ -0,0 +1,39 @@ +% myobj = SetMetadata( myobj, metadata_struct ) +% +% If a run is in progress, set metadata to be added to the +% next output file-set. Metadata must be in the form of a +% struct of name/value pairs. +% +function [s] = SetMetadata( s, meta ) + + if( ~isstruct( meta ) ) + error( 'SetMetaData: Argument must be a struct.' ); + end + + ChkConn( s ); + + ok = CalinsNetMex( 'sendstring', s.handle, sprintf( 'SETMETADATA\n' ) ); + ReceiveREADY( s, 'SETMETADATA' ); + + names = fieldnames( meta ); + + for i = 1:length( names ) + + f = meta.(names{i}); + + if( isnumeric( f ) && isscalar( f ) ) + line = sprintf( '%s=%g\n', names{i}, f ); + elseif( ischar( f ) ) + line = sprintf( '%s=%s\n', names{i}, f ); + else + error( 'SetMetaData: Field %s must be numeric scalar or a string.', names{i} ); + end + + ok = CalinsNetMex( 'sendstring', s.handle, line ); + end + + % end with blank line + ok = CalinsNetMex( 'sendstring', s.handle, sprintf( '\n' ) ); + + ReceiveOK( s, 'SETMETADATA' ); +end diff --git a/ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/API/@SpikeGL/SetMultiDriveEnable.m b/ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/API/@SpikeGL/SetMultiDriveEnable.m new file mode 100644 index 00000000..f0991d4a --- /dev/null +++ b/ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/API/@SpikeGL/SetMultiDriveEnable.m @@ -0,0 +1,12 @@ +% myobj = SetMultiDriveEnable( myobj, bool_flag ) +% +% Set multi-drive run-splitting on/off. +% +function [s] = SetMultiDriveEnable( s, b ) + + if( ~isnumeric( b ) ) + error( 'SetMultiDriveEnable: Arg 2 must be a Boolean value {0,1}.' ); + end + + DoSimpleCmd( s, sprintf( 'SETMULTIDRIVEENABLE %d', b ) ); +end diff --git a/ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/API/@SpikeGL/SetNextFileName.m b/ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/API/@SpikeGL/SetNextFileName.m new file mode 100644 index 00000000..f5522dcc --- /dev/null +++ b/ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/API/@SpikeGL/SetNextFileName.m @@ -0,0 +1,40 @@ +% myobj = SetNextFileName( myobj, 'name' ) +% +% For only the next trigger (file writing event) this overrides +% all auto-naming, giving you complete control of where to save +% the files, the file name, and what g- and t-indices you want +% (if any). For example, regardless of the run's current data dir, +% run name and indices, if you set: 'otherdir/yyy_g5/yyy_g5_t7', +% SpikeGLX will save the next files in flat directory yyy_g5/: +% - otherdir/yyy_g5/yyy.g5_t7.nidq.bin,meta +% - otherdir/yyy_g5/yyy.g5_t7.imec0.ap.bin,meta +% - otherdir/yyy_g5/yyy.g5_t7.imec0.lf.bin,meta +% - otherdir/yyy_g5/yyy.g5_t7.imec1.ap.bin,meta +% - otherdir/yyy_g5/yyy.g5_t7.imec1.lf.bin,meta +% - etc. +% +% - The destination directory must already exist...No parent directories +% or probe subfolders are created in this naming mode. +% - The run must already be in progress. +% - Neither the custom name nor its indices are displayed in the Graphs +% window toolbars. Rather, the toolbars reflect standard auto-names. +% - After writing this file set, the override is cleared and auto-naming +% will resume as if you never called setNextFileName. You have to call +% setNextFileName before each trigger event to create custom trial series. +% For example, you can build a software-triggered t-series using sequence: +% + setNextFileName( 'otherdir/yyy_g0/yyy_g0_t0' ) +% + setRecordingEnable( 1 ) +% + setRecordingEnable( 0 ) +% + setNextFileName( 'otherdir/yyy_g0/yyy_g0_t1' ) +% + setRecordingEnable( 1 ) +% + setRecordingEnable( 0 ) +% + etc. +% +function [s] = SetNextFileName( s, name ) + + if( ~ischar( name ) ) + error( 'SetNextFileName: Argument must be a string.' ); + end + + DoSimpleCmd( s, sprintf( 'SETNEXTFILENAME %s', name ) ); +end diff --git a/ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/API/@SpikeGL/SetParams.m b/ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/API/@SpikeGL/SetParams.m new file mode 100644 index 00000000..a7f514ee --- /dev/null +++ b/ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/API/@SpikeGL/SetParams.m @@ -0,0 +1,43 @@ +% myobj = SetParams( myobj, params_struct ) +% +% The inverse of GetParams.m, this sets run parameters. +% Alternatively, you can pass the parameters to StartRun() +% which calls this in turn. Run parameters are a struct of +% name/value pairs. The call will error if a run is currently +% in progress. +% +% Note: You can set any subset of [DAQSettings]. +% +function [s] = SetParams( s, params ) + + if( ~isstruct( params ) ) + error( 'SetParams: Argument must be a struct.' ); + end + + ChkConn( s ); + + ok = CalinsNetMex( 'sendstring', s.handle, sprintf( 'SETPARAMS\n' ) ); + ReceiveREADY( s, 'SETPARAMS' ); + + names = fieldnames( params ); + + for i = 1:length( names ) + + f = params.(names{i}); + + if( isnumeric( f ) && isscalar( f ) ) + line = sprintf( '%s=%g\n', names{i}, f ); + elseif( ischar( f ) ) + line = sprintf( '%s=%s\n', names{i}, f ); + else + error( 'SetParams: Field %s must be numeric scalar or a string.', names{i} ); + end + + ok = CalinsNetMex( 'sendstring', s.handle, line ); + end + + % end with blank line + ok = CalinsNetMex( 'sendstring', s.handle, sprintf( '\n' ) ); + + ReceiveOK( s, 'SETPARAMS' ); +end diff --git a/ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/API/@SpikeGL/SetParamsImecCommon.m b/ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/API/@SpikeGL/SetParamsImecCommon.m new file mode 100644 index 00000000..1c9aafbf --- /dev/null +++ b/ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/API/@SpikeGL/SetParamsImecCommon.m @@ -0,0 +1,42 @@ +% myobj = SetParamsImecCommon( myobj, params_struct ) +% +% The inverse of GetParamsImecCommon.m, this sets parameters +% common to all enabled probes. Parameters are a struct of +% name/value pairs. The call will error if a run is currently +% in progress. +% +% Note: You can set any subset of [DAQ_Imec_All]. +% +function [s] = SetParamsImecCommon( s, params ) + + if( ~isstruct( params ) ) + error( 'SetParamsImecCommon: Argument must be a struct.' ); + end + + ChkConn( s ); + + ok = CalinsNetMex( 'sendstring', s.handle, sprintf( 'SETPARAMSIMALL\n' ) ); + ReceiveREADY( s, 'SETPARAMSIMALL' ); + + names = fieldnames( params ); + + for i = 1:length( names ) + + f = params.(names{i}); + + if( isnumeric( f ) && isscalar( f ) ) + line = sprintf( '%s=%g\n', names{i}, f ); + elseif( ischar( f ) ) + line = sprintf( '%s=%s\n', names{i}, f ); + else + error( 'SetParamsImecCommon: Field %s must be numeric scalar or a string.', names{i} ); + end + + ok = CalinsNetMex( 'sendstring', s.handle, line ); + end + + % end with blank line + ok = CalinsNetMex( 'sendstring', s.handle, sprintf( '\n' ) ); + + ReceiveOK( s, 'SETPARAMSIMALL' ); +end diff --git a/ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/API/@SpikeGL/SetParamsImecProbe.m b/ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/API/@SpikeGL/SetParamsImecProbe.m new file mode 100644 index 00000000..5276a63a --- /dev/null +++ b/ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/API/@SpikeGL/SetParamsImecProbe.m @@ -0,0 +1,44 @@ +% myobj = SetParamsImecProbe( myobj, params_struct, ip ) +% +% The inverse of GetParamsImecProbe.m, this sets parameters +% for a given logical probe. Parameters are a struct of +% name/value pairs. The call will error if file writing +% is currently in progress. +% +% Note: You can set any subset of fields under [SerialNumberToProbe]/SNjjj. +% +function [s] = SetParamsImecProbe( s, params, ip ) + + if( ~isstruct( params ) ) + error( 'SetParamsImecProbe: Argument must be a struct.' ); + end + + ChkConn( s ); + + ok = CalinsNetMex( 'sendstring', s.handle, ... + sprintf( 'SETPARAMSIMPRB %d\n',ip ) ); + + ReceiveREADY( s, 'SETPARAMSIMPRB' ); + + names = fieldnames( params ); + + for i = 1:length( names ) + + f = params.(names{i}); + + if( isnumeric( f ) && isscalar( f ) ) + line = sprintf( '%s=%g\n', names{i}, f ); + elseif( ischar( f ) ) + line = sprintf( '%s=%s\n', names{i}, f ); + else + error( 'SetParamsImecProbe: Field %s must be numeric scalar or a string.', names{i} ); + end + + ok = CalinsNetMex( 'sendstring', s.handle, line ); + end + + % end with blank line + ok = CalinsNetMex( 'sendstring', s.handle, sprintf( '\n' ) ); + + ReceiveOK( s, 'SETPARAMSIMPRB' ); +end diff --git a/ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/API/@SpikeGL/SetParamsOnebox.m b/ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/API/@SpikeGL/SetParamsOnebox.m new file mode 100644 index 00000000..1f1ad517 --- /dev/null +++ b/ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/API/@SpikeGL/SetParamsOnebox.m @@ -0,0 +1,50 @@ +% myobj = SetParamsOneBox( myobj, params_struct, ip, slot ) +% +% The inverse of GetParamsOneBox.m, this sets params +% for a selected OneBox. Parameters are a struct of +% name/value pairs. +% +% To reference a OneBox configured as a recording stream +% set ip to its stream-id; if ip >= 0, slot is ignored. +% Any selected OneBox can also be referenced by setting +% ip = -1, and giving its slot index. +% +% The call will error if a run is currently in progress. +% +% Note: You can set any subset of fields under [SerialNumberToOneBox]/SNjjj. +% +function [s] = SetParamsOneBox( s, params, ip, slot ) + + if( ~isstruct( params ) ) + error( 'SetParamsOneBox: Argument must be a struct.' ); + end + + ChkConn( s ); + + ok = CalinsNetMex( 'sendstring', s.handle, ... + sprintf( 'SETPARAMSOBX %d %d\n', ip, slot ) ); + + ReceiveREADY( s, 'SETPARAMSOBX' ); + + names = fieldnames( params ); + + for i = 1:length( names ) + + f = params.(names{i}); + + if( isnumeric( f ) && isscalar( f ) ) + line = sprintf( '%s=%g\n', names{i}, f ); + elseif( ischar( f ) ) + line = sprintf( '%s=%s\n', names{i}, f ); + else + error( 'SetParamsOneBox: Field %s must be numeric scalar or a string.', names{i} ); + end + + ok = CalinsNetMex( 'sendstring', s.handle, line ); + end + + % end with blank line + ok = CalinsNetMex( 'sendstring', s.handle, sprintf( '\n' ) ); + + ReceiveOK( s, 'SETPARAMSOBX' ); +end diff --git a/ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/API/@SpikeGL/SetRecordingEnable.m b/ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/API/@SpikeGL/SetRecordingEnable.m new file mode 100644 index 00000000..21a81d9b --- /dev/null +++ b/ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/API/@SpikeGL/SetRecordingEnable.m @@ -0,0 +1,16 @@ +% myobj = SetRecordingEnable( myobj, bool_flag ) +% +% Set gate (file writing) on/off during run. +% +% When auto-naming is in effect, opening the gate advances +% the g-index and resets the t-index to zero. Auto-naming is +% on unless SetNextFileName has been used to override it. +% +function [s] = SetRecordingEnable( s, b ) + + if( ~isnumeric( b ) ) + error( 'SetRecordingEnable: Arg 2 must be a Boolean value {0,1}.' ); + end + + DoSimpleCmd( s, sprintf( 'SETRECORDENAB %d', b ) ); +end diff --git a/ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/API/@SpikeGL/SetRunName.m b/ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/API/@SpikeGL/SetRunName.m new file mode 100644 index 00000000..959b6702 --- /dev/null +++ b/ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/API/@SpikeGL/SetRunName.m @@ -0,0 +1,13 @@ +% myobj = SetRunName( myobj, 'name' ) +% +% Set the run name for the next time files are created +% (either by trigger, SetRecordingEnable() or by StartRun()). +% +function [s] = SetRunName( s, name ) + + if( ~ischar( name ) ) + error( 'SetRunName: Argument must be a string.' ); + end + + DoSimpleCmd( s, sprintf( 'SETRUNNAME %s', name ) ); +end diff --git a/ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/API/@SpikeGL/SetTriggerOffBeep.m b/ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/API/@SpikeGL/SetTriggerOffBeep.m new file mode 100644 index 00000000..325c6e17 --- /dev/null +++ b/ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/API/@SpikeGL/SetTriggerOffBeep.m @@ -0,0 +1,9 @@ +% myobj = SetTriggerOffBeep( myobj, hertz, millisec ) +% +% During a run, set frequency and duration of Windows +% beep signaling file closure. hertz=0 disables the beep. +% +function [s] = SetTriggerOffBeep( s, hertz, millisec ) + + DoSimpleCmd( s, sprintf( 'SETTRIGGEROFFBEEP %d %d', hertz, millisec ) ); +end diff --git a/ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/API/@SpikeGL/SetTriggerOnBeep.m b/ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/API/@SpikeGL/SetTriggerOnBeep.m new file mode 100644 index 00000000..9291f88f --- /dev/null +++ b/ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/API/@SpikeGL/SetTriggerOnBeep.m @@ -0,0 +1,9 @@ +% myobj = SetTriggerOnBeep( myobj, hertz, millisec ) +% +% During a run set frequency and duration of Windows +% beep signaling file creation. hertz=0 disables the beep. +% +function [s] = SetTriggerOnBeep( s, hertz, millisec ) + + DoSimpleCmd( s, sprintf( 'SETTRIGGERONBEEP %d %d', hertz, millisec ) ); +end diff --git a/ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/API/@SpikeGL/SpikeGL.m b/ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/API/@SpikeGL/SpikeGL.m new file mode 100644 index 00000000..dd06f9ae --- /dev/null +++ b/ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/API/@SpikeGL/SpikeGL.m @@ -0,0 +1,35 @@ +% myobj = SpikeGL() +% myobj = SpikeGL( host ) +% myobj = SpikeGL( host, port ) +% +% Construct a new @SpikeGL instance and immediately attempt +% a network connection. If omitted, the defaults for host and +% port are {'localhost, 4142}. +% +function [s] = SpikeGL( varargin ) + + host = 'localhost'; + port = 4142; + + if( nargin >= 1 ) + host = varargin{1}; + end + + if( nargin >= 2 ) + port = varargin{2}; + end + + if( ~ischar( host ) || ~isnumeric( port ) ) + error( 'SpikeGL: Host must be a string; port must be a number.' ); + end + + s = struct; + s.host = host; + s.port = port; + s.in_chkconn = 0; + s.handle = -1; + s.ver = ''; + + s = class( s, 'SpikeGL' ); + s = ChkConn( s ); +end diff --git a/ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/API/@SpikeGL/StartRun.m b/ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/API/@SpikeGL/StartRun.m new file mode 100644 index 00000000..4792c0fa --- /dev/null +++ b/ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/API/@SpikeGL/StartRun.m @@ -0,0 +1,45 @@ +% myobj = StartRun( myobj ) +% myobj = StartRun( myobj, params_struct ) +% myobj = StartRun( myobj, 'runName' ) +% +% Start data acquisition run. Optional second argument (params) +% is a struct of name/value pairs as returned from GetParams.m. +% Alternatively, the second argument can be a string (runName). +% Last-used parameters remain in effect if not specified here. +% An error is flagged if already running or a parameter is bad. +% +function [s] = StartRun( varargin ) + + s = varargin{1}; + rname = []; + params = []; + + if( nargin > 1 ) + + arg2 = varargin{2}; + + if( ischar( arg2 ) ) + rname = arg2; + elseif( isstruct( arg2 ) ) + params = arg2; + else + error( 'StartRun: Invalid second argument; must be a string or struct.' ); + end + end + + if( IsRunning( s ) ) + error( 'StartRun: Already running.' ); + end + + if( isempty( params ) ) + params = GetParams( s ); + end + + if( ~isempty( rname ) ) + params.snsRunName = rname; + end + + SetParams( s, params ); + + DoSimpleCmd( s, 'STARTRUN' ); +end diff --git a/ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/API/@SpikeGL/StopRun.m b/ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/API/@SpikeGL/StopRun.m new file mode 100644 index 00000000..101ed21b --- /dev/null +++ b/ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/API/@SpikeGL/StopRun.m @@ -0,0 +1,9 @@ +% myobj = StopRun( myobj ) +% +% Unconditionally stop current run, close data files +% and return to idle state. +% +function [s] = StopRun( s ) + + DoSimpleCmd( s, 'STOPRUN' ); +end diff --git a/ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/API/@SpikeGL/TriggerGT.m b/ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/API/@SpikeGL/TriggerGT.m new file mode 100644 index 00000000..cd81aa26 --- /dev/null +++ b/ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/API/@SpikeGL/TriggerGT.m @@ -0,0 +1,19 @@ +% myobj = TriggerGT( myobj, g, t ) +% +% Using standard auto-naming, set both the gate (g) and +% trigger (t) levels that control file writing. +% -1 = no change. +% 0 = set low. +% 1 = increment and set high. +% E.g., triggerGT( -1, 1 ) = same g, increment t, start writing. +% +% - TriggerGT only affects the 'Remote controlled' gate type and/or +% the 'Remote controlled' trigger type. +% - The 'Enable Recording' button, when shown, is a master override +% switch. TriggerGT is blocked until you click the button or call +% SetRecordingEnable. +% +function [s] = TriggerGT( s, g, t ) + + DoSimpleCmd( s, sprintf( 'TRIGGERGT %d %d', g, t ) ); +end diff --git a/ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/API/@SpikeGL/VerifySha1.m b/ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/API/@SpikeGL/VerifySha1.m new file mode 100644 index 00000000..071aa11f --- /dev/null +++ b/ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/API/@SpikeGL/VerifySha1.m @@ -0,0 +1,31 @@ +% res = VerifySha1( myobj, 'filename' ) +% +% Verifies the SHA1 sum of the file specified by filename. +% If filename is relative, it is appended to the run dir. +% Absolute path/filenames are also supported. Since this is +% a potentially long operation, it uses the 'disp' command +% to print progress information to the MATLAB console. The +% returned value is 1 if verified, 0 otherwise. +% +function [res] = VerifySha1( s, file ) + + res = 0; + + ChkConn( s ); + + ok = CalinsNetMex( 'sendstring', s.handle, sprintf( 'VERIFYSHA1 %s\n', file ) ); + + while( 1 ) + + line = CalinsNetMex( 'readline', s.handle ); + + if( strcmp( line, 'OK' ) ) + res = 1; + break; + end + + if( ~isempty( line ) ) + fprintf( 'Verify progress: %s%%\n', line ); + end + end +end diff --git a/ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/API/CalinsNetMex.mexw64 b/ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/API/CalinsNetMex.mexw64 new file mode 100644 index 0000000000000000000000000000000000000000..b32c1a3061f9147033c7a0147597da4391ba7add GIT binary patch literal 69632 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 P^r5PU+PTO5Pjvqu^+8>d literal 0 HcmV?d00001 diff --git a/ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/Build/CalinsNetMex.cpp b/ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/Build/CalinsNetMex.cpp new file mode 100644 index 00000000..2420c888 --- /dev/null +++ b/ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/Build/CalinsNetMex.cpp @@ -0,0 +1,792 @@ + +#include "NetClient.h" + +#include +#include + +#include + +#include +#include + +/* ---------------------------------------------------------------- */ +/* Shared-Mem-Filename -------------------------------------------- */ +/* ---------------------------------------------------------------- */ + +#ifdef WIN32 + +#include + +class SMF { +private: + const char *mapName; + HANDLE hMapFile; + void *pBuf; + const uint BUF_SIZE; +public: + SMF() + : mapName("Global\\SpikeGLFileNameShm"), + hMapFile(NULL), pBuf(NULL), BUF_SIZE(1024) {} + virtual ~SMF() {detach();} + + string getName(); + +private: + void attach(); + void detach(); +}; + + +string SMF::getName() +{ + attach(); + + if( hMapFile && pBuf ) + return (char*)pBuf; + + return "Not attached to shared memory."; +} + + +void SMF::attach() +{ + if( hMapFile ) + return; + + hMapFile = OpenFileMappingA( + FILE_MAP_ALL_ACCESS, // read/write access + FALSE, // do not inherit the name + mapName ); // name of mapping object + + if( !hMapFile ) { + mexWarnMsgTxt( + "Could not attach to SpikeGLFileNameShm" + " -- OpenFileMappingA() failed!." ); + return; + } + + pBuf = MapViewOfFile( + hMapFile, // handle to map object + FILE_MAP_ALL_ACCESS, // read/write perms + 0, // view offset-hi + 0, // view offset-lo + BUF_SIZE ); // view length + + if( !pBuf ) { + detach(); + mexWarnMsgTxt( + "Could not attach to SpikeGLFileNameShm" + " -- MapViewOfFile() failed." ); + } +} + + +void SMF::detach() +{ + if( !hMapFile ) + return; + + if( pBuf ) { + UnmapViewOfFile( pBuf ); + pBuf = NULL; + } + + if( hMapFile ) { + CloseHandle( hMapFile ); + hMapFile = NULL; + } +} + + +#else + +class SMF { +public: + string getName() {return "Requires Windows OS.";} +}; + +#endif + +/* ---------------------------------------------------------------- */ +/* Types ---------------------------------------------------------- */ +/* ---------------------------------------------------------------- */ + +typedef map NetClientMap; + +/* ---------------------------------------------------------------- */ +/* Macros --------------------------------------------------------- */ +/* ---------------------------------------------------------------- */ + +#ifndef _MSC_VER +#define _strcmpi strcasecmp +#endif + +#define RETURN( x ) \ + do { \ + plhs[0] = mxCreateDoubleScalar( static_cast(x) ); \ + return; \ + } while(0) + +#define RETURN_NULL() \ + do { \ + plhs[0] = mxCreateDoubleMatrix( 0, 0, mxREAL ); \ + return; \ + } while(0) + +/* ---------------------------------------------------------------- */ +/* Statics -------------------------------------------------------- */ +/* ---------------------------------------------------------------- */ + +static SMF smf; +static NetClientMap clientMap; +static int handleId = 0; // keeps getting incremented.. + +/* ---------------------------------------------------------------- */ +/* Helpers -------------------------------------------------------- */ +/* ---------------------------------------------------------------- */ + +static NetClient *MapFind( int handle ) +{ + NetClientMap::iterator it = clientMap.find( handle ); + + if( it == clientMap.end() ) + return NULL; + + return it->second; +} + + +static void MapPut( int handle, NetClient *client ) +{ + NetClient *old = MapFind( handle ); + + if( old ) + delete old; + + clientMap[handle] = client; +} + + +static void MapDestroy( int handle ) +{ + NetClientMap::iterator it = clientMap.find( handle ); + + if( it != clientMap.end() ) { + + delete it->second; + clientMap.erase( it ); + } + else { + mexWarnMsgTxt( + "Invalid or unknown handle passed" + " to CalinsNetMex MapDestroy." ); + } +} + + +static int GetHandle( int nrhs, const mxArray *prhs[] ) +{ + if( nrhs < 1 ) + mexErrMsgTxt( "Need numeric handle argument." ); + + const mxArray *handle = prhs[0]; + + if( !mxIsDouble( handle ) + || mxGetM( handle ) != 1 + || mxGetN( handle ) != 1 ) { + + mexErrMsgTxt( "Handle must be a scalar double value." ); + } + + return static_cast(*mxGetPr( handle )); +} + + +static NetClient *GetNetClient( int nrhs, const mxArray *prhs[] ) +{ + NetClient *nc = MapFind( GetHandle( nrhs, prhs ) ); + + if( !nc ) { + mexErrMsgTxt( + "INTERNAL ERROR -- Cannot find the NetClient" + " for the specified handle in CalinsNetMex." ); + } + + return nc; +} + +/* ---------------------------------------------------------------- */ +/* Handlers ------------------------------------------------------- */ +/* ---------------------------------------------------------------- */ + +// handle = createNewClient( host, port ) +// +void createNewClient( + int nlhs, + mxArray *plhs[], + int nrhs, + const mxArray *prhs[] ) +{ + if( nlhs != 1 ) { + mexErrMsgTxt( "createNewClient: Returns handle to LHS." ); + } + + if( nrhs != 2 ) { + mexErrMsgTxt( "createNewClient: Requires RHS: host, port." ); + } + + const mxArray *host = prhs[0], + *port = prhs[1]; + + if( !mxIsChar( host ) || mxGetM( host ) != 1 ) + mexErrMsgTxt( "createNewClient: Hostname must be a string row vector." ); + + if( !mxIsDouble( port ) + || mxGetM( port ) != 1 + || mxGetN( port ) != 1 ) { + + mexErrMsgTxt( "createNewClient: Port must be a scalar numeric value." ); + } + + char *hostStr = mxArrayToString( host ); + uint16 portNum = static_cast(*mxGetPr( port )); + NetClient *nc = new NetClient( hostStr, portNum ); + int h = handleId++; + + mxFree( hostStr ); + + MapPut( h, nc ); + + RETURN( h ); +} + + +// destroyClient( handle ) +// +void destroyClient( + int nlhs, + mxArray *plhs[], + int nrhs, + const mxArray *prhs[] ) +{ + MapDestroy( GetHandle( nrhs, prhs ) ); +} + + +// ok = tryConnection( handle ) +// +void tryConnection( + int nlhs, + mxArray *plhs[], + int nrhs, + const mxArray *prhs[] ) +{ + if( nlhs < 1 ) + mexErrMsgTxt( "tryConnection: Returns ok to LHS." ); + + NetClient *nc = GetNetClient( nrhs, prhs ); + + try { + + if( !nc->tcpConnect() ) { + + mexWarnMsgTxt( nc->errorReason().c_str() ); + RETURN_NULL(); + } + } + catch( const exception &e ) { + + if( e.what()[0] ) + mexWarnMsgTxt( e.what() ); + + RETURN_NULL(); + } + + RETURN( 1 ); +} + + +// closeSocket( handle ) +// +void closeSocket( + int nlhs, + mxArray *plhs[], + int nrhs, + const mxArray *prhs[] ) +{ + NetClient *nc = GetNetClient( nrhs, prhs ); + + nc->tcpDisconnect(); +} + + +// ok = sendString( handle, string ) +// +void sendString( + int nlhs, + mxArray *plhs[], + int nrhs, + const mxArray *prhs[] ) +{ + if( nlhs < 1 ) + mexErrMsgTxt( "sendString: Returns ok to LHS." ); + + if( nrhs != 2 ) + mexErrMsgTxt( "sendString: Requires RHS: handle, string." ); + + if( mxGetClassID( prhs[1] ) != mxCHAR_CLASS ) + mexErrMsgTxt( "sendString: Arg 2 must be a string." ); + + char *tmp = mxArrayToString( prhs[1] ); + string theString = tmp; + mxFree( tmp ); + + NetClient *nc = GetNetClient( nrhs, prhs ); + + try { + nc->sendString( theString ); + } + catch( const exception &e ) { + + if( e.what()[0] ) + mexWarnMsgTxt( e.what() ); + + RETURN_NULL(); + } + + RETURN( 1 ); +} + + +// ok = sendMatrix( handle, matrix ) +// +void sendMatrix( + int nlhs, + mxArray *plhs[], + int nrhs, + const mxArray *prhs[] ) +{ + if( nlhs < 1 ) + mexErrMsgTxt( "sendMatrix: Returns ok to LHS." ); + + if( nrhs != 2 ) + mexErrMsgTxt( "sendMatrix: Requires RHS: handle, matrix." ); + + ulong datalen = ulong(mxGetM( prhs[1] ) * mxGetN( prhs[1] )); + + switch( mxGetClassID( prhs[1] ) ) { + + case mxINT8_CLASS: + case mxUINT8_CLASS: + case mxCHAR_CLASS: + break; + case mxINT16_CLASS: + case mxUINT16_CLASS: + datalen *= sizeof(short); + break; + case mxUINT32_CLASS: + case mxINT32_CLASS: + datalen *= sizeof(int); + break; + case mxSINGLE_CLASS: + datalen *= sizeof(float); + break; + case mxDOUBLE_CLASS: + datalen *= sizeof(double); + break; + default: + mexErrMsgTxt( "sendMatrix: Sent matrix must have numeric type." ); + } + + NetClient *nc = GetNetClient( nrhs, prhs ); + void *theMatrix = mxGetPr( prhs[1] ); + + try { + nc->sendData( theMatrix, datalen ); + } + catch( const exception &e ) { + + if( e.what()[0] ) + mexErrMsgTxt( e.what() ); + + RETURN_NULL(); + } + + RETURN( 1 ); +} + + +// Fetch next line, excluding ERROR lines. +// Return as single string. +// +void readLine( + int nlhs, + mxArray *plhs[], + int nrhs, + const mxArray *prhs[] ) +{ + if( nlhs < 1 ) + mexErrMsgTxt( "readLine: Returns string to LHS." ); + + NetClient *nc = GetNetClient( nrhs, prhs ); + + try { + vector line; + nc->rcvLine( line ); + + if( line.size() ) { + + const char *s = &line[0]; + + if( !strncmp( s, "ERROR", 5 ) ) + throw std::runtime_error( s ); + + plhs[0] = mxCreateString( s ); + } + } + catch( const exception &e ) { + + if( e.what()[0] ) + mexWarnMsgTxt( e.what() ); + + RETURN_NULL(); + } +} + + +// Fetch one line, excluding {OK, ERROR} lines. +// Return as single string. +// +void queryString( + int nlhs, + mxArray *plhs[], + int nrhs, + const mxArray *prhs[] ) +{ + if( nlhs < 1 ) + mexErrMsgTxt( "queryString: Returns string to LHS." ); + + NetClient *nc = GetNetClient( nrhs, prhs ); + + try { + vector line; + + for(;;) { + + nc->rcvLine( line ); + + if( line.size() ) { + + const char *s = &line[0]; + + if( !strcmp( s, "OK" ) ) + return; + + if( !strncmp( s, "ERROR", 5 ) ) + throw std::runtime_error( s ); + + plhs[0] = mxCreateString( s ); + } + } + } + catch( const exception &e ) { + + if( e.what()[0] ) + mexErrMsgTxt( e.what() ); + + RETURN_NULL(); + } +} + + +// Fetch one or more lines, excluding {OK, ERROR} lines. +// Return as cell-array of strings. +// +void getCells( + int nlhs, + mxArray *plhs[], + int nrhs, + const mxArray *prhs[] ) +{ + if( nlhs < 1 ) + mexErrMsgTxt( "getCells: Returns cells{} to LHS." ); + + NetClient *nc = GetNetClient( nrhs, prhs ); + + try { + vector > vlines; + nc->rcvLines( vlines ); + + mwSize m = mwSize(vlines.size()); + + if( m ) { + + plhs[0] = mxCreateCellArray( 1, &m ); + + for( int i = 0; i < m; ++i ) + mxSetCell( plhs[0], i, mxCreateString( &vlines[i][0] ) ); + } + } + catch( const exception &e ) { + + if( e.what()[0] ) + mexErrMsgTxt( e.what() ); + + RETURN_NULL(); + } +} + + +// matrix = readMatrix( handle, datatype, dims-vector ) +// +void readMatrix( + int nlhs, + mxArray *plhs[], + int nrhs, + const mxArray *prhs[] ) +{ +// ------------- +// Validate args +// ------------- + + int ndims, datalen; + + if( !nlhs ) + mexErrMsgTxt( "readMatrix: Returns matrix to LHS." ); + + if( nrhs < 3 + || !mxIsChar( prhs[1] ) + || !mxIsDouble( prhs[2] ) + || (ndims = int(mxGetN( prhs[2] ))) < 2 + || mxGetM( prhs[2] ) != 1 ) { + + mexErrMsgTxt( + "readMatrix: Needs arguments:\n" + " (1) Handle\n" + " (2) String datatype {'double', 'single', 'uint8'}\n" + " (3) Vector of dims {1x2, 1x3, 1x4} holding m,n[,o,p]" ); + } + +// ---------------- +// Get datatype str +// ---------------- + + const char *str; + int buflen = int(mxGetM( prhs[1] ) * mxGetN( prhs[1] )) + 1; + string stdstr( buflen, '0' ); + + mxGetString( prhs[1], &stdstr[0], buflen ); + str = stdstr.c_str(); + +// ------------------------------------- +// Calculate data size (product of dims) +// ------------------------------------- + + double *pdims = mxGetPr( prhs[2] ); + + if( ndims > 4 ) + ndims = 4; + + int dims[] = { + static_cast(pdims[0]), + static_cast(pdims[1]), + (ndims >= 3 ? static_cast(pdims[2]) : 1), + (ndims >= 4 ? static_cast(pdims[3]) : 1) + }; + + datalen = dims[0] * dims[1] * dims[2] * dims[3]; + +// -------------------------- +// Adjust sizing for datatype +// -------------------------- + + mxClassID cls; + + if( !_strcmpi( str, "double" ) ) { + cls = mxDOUBLE_CLASS; + datalen *= sizeof(double); + } + else if( !_strcmpi( str, "single" ) ) { + cls = mxSINGLE_CLASS; + datalen *= sizeof(float); + } + else if( !_strcmpi( str, "int8" ) ) { + cls = mxINT8_CLASS; + datalen *= sizeof(char); + } + else if( !_strcmpi( str, "uint8" ) ) { + cls = mxUINT8_CLASS; + datalen *= sizeof(char); + } + else if( !_strcmpi( str, "int16" ) ) { + cls = mxINT16_CLASS; + datalen *= sizeof(short); + } + else if( !_strcmpi( str, "uint16" ) ) { + cls = mxUINT16_CLASS; + datalen *= sizeof(short); + } + else if( !_strcmpi( str, "int32" ) ) { + cls = mxINT32_CLASS; + datalen *= sizeof(int); + } + else if( !_strcmpi( str, "uint32" ) ) { + cls = mxUINT32_CLASS; + datalen *= sizeof(int); + } + else { + mexErrMsgTxt( + "readMatrix: Output matrix type must be one of {" + "'single', 'double', 'int8', 'uint8'," + " 'int16', 'uint16', 'int32', 'uint32'." ); + } + +// -------------------- +// Allocate destination +// -------------------- + + plhs[0] = mxCreateNumericArray( ndims, dims, cls, mxREAL ); + +// -------- +// Get data +// -------- + + NetClient *nc = GetNetClient( nrhs, prhs ); + + try { + nc->receiveData( mxGetData( plhs[0] ), datalen ); + } + catch( const exception &e ) { + + if( e.what()[0] ) + mexErrMsgTxt( e.what() ); + + mxDestroyArray( plhs[0] ); + plhs[0] = 0; + + RETURN_NULL(); + } +} + + +// filename = fastGetFilename() +// +void fastGetFilename( + int nlhs, + mxArray *plhs[], + int nrhs, + const mxArray *prhs[] ) +{ + if( nlhs < 1 ) + mexErrMsgTxt( "fastGetFilename: Returns filename to LHS." ); + + plhs[0] = mxCreateString( smf.getName().c_str() ); +} + +/* ---------------------------------------------------------------- */ +/* Dispatch - Entry point ----------------------------------------- */ +/* ---------------------------------------------------------------- */ + +typedef void (*T_Mexfunc)( int, mxArray**, int, const mxArray** ); + +static map cmd2fun; + + +// var = mexFunction( cmdstring, var ) +// +void mexFunction( + int nlhs, + mxArray *plhs[], + int nrhs, + const mxArray *prhs[] ) +{ +// --------------------------------------- +// Build function table (lowercase keys!!) +// --------------------------------------- + + if( !cmd2fun.size() ) { + cmd2fun["create"] = createNewClient; + cmd2fun["destroy"] = destroyClient; + cmd2fun["connect"] = tryConnection; + cmd2fun["disconnect"] = closeSocket; + cmd2fun["sendstring"] = sendString; + cmd2fun["sendmatrix"] = sendMatrix; + cmd2fun["readline"] = readLine; + cmd2fun["querystring"] = queryString; + cmd2fun["getcells"] = getCells; + cmd2fun["readmatrix"] = readMatrix; + cmd2fun["getspikeglfilenamefromshm"] = fastGetFilename; + } + +// ------------------ +// At least two args? +// ------------------ + + const mxArray *cmd; + string scmd, + serr; + + if( nrhs < 2 ) { + serr += "CalinsNetMex requires at least two RHS args.\n"; + goto usage; + } + else + cmd = prhs[0]; + +// -------------------- +// Command is a string? +// -------------------- + + if( !mxIsChar( cmd ) ) { + serr += "CalinsNetMex arg 1 must be a string.\n"; + goto usage; + } + + if( mxGetM( cmd ) != 1 ) { + serr += "CalinsNetMex arg 1 must be a row vector.\n"; + goto usage; + } + +// ------------------------- +// Convert command to string +// ------------------------- + + { + char *tmp = mxArrayToString( cmd ); + scmd = tmp; + mxFree( tmp ); + } + +// -------------------------------- +// Dispatch using lowercase command +// -------------------------------- + + { + transform( scmd.begin(), scmd.end(), scmd.begin(), tolower ); + + map::iterator it = cmd2fun.find( scmd ); + + if( it != cmd2fun.end() ) { + it->second( nlhs, plhs, nrhs - 1, prhs + 1 ); + return; + } + } + + serr += "CalinsNetMex unknown command <" + scmd + ">.\n"; + +// ----- +// Error +// ----- + +usage: + serr += "Legal commands:\n"; + + map::iterator it = cmd2fun.begin(); + + while( it != cmd2fun.end() ) + serr += " - " + it++->first + "\n"; + + mexErrMsgTxt( serr.c_str() ); +} + + diff --git a/ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/Build/NetClient.cpp b/ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/Build/NetClient.cpp new file mode 100644 index 00000000..f9b820b9 --- /dev/null +++ b/ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/Build/NetClient.cpp @@ -0,0 +1,223 @@ + +#include "NetClient.h" + +#include + + + + +NetClient::NetClient( + const string &host, + uint16 port, + uint read_timeout_secs ) + : Socket(TCP), read_timeout_secs(read_timeout_secs) +{ + setHost( host ); + setPort( port ); +} + + +uint NetClient::sendData( const void *src, uint srcBytes ) noexcept(false) +{ + static const uint maxsend = 2*1024*1024; + + vbuf.clear(); // clear response + + if( !srcBytes ) + return 0; + + uint sent = 0; + const char *buf = static_cast(src); + + while( sent < srcBytes ) { + + uint tosend = srcBytes - sent; + + if( tosend > maxsend ) + tosend = maxsend; + + sent += Socket::sendData( buf + sent, tosend ); + } + + return sent; +} + + +uint NetClient::receiveData( void *dst, uint dstBytes ) noexcept(false) +{ + static const uint maxrecv = 2*1024*1024; + + if( !dstBytes ) + return 0; + + ulong recvd = 0, + nB = ulong(vbuf.size()); + char *buf = static_cast(dst); + + if( nB ) { + + recvd = nB; + + if( recvd > dstBytes ) + recvd = dstBytes; + + memcpy( buf, &vbuf[0], recvd ); + + if( (nB -= recvd) > 0 ) + memcpy( &vbuf[0], &vbuf[recvd], nB ); + + vbuf.resize( nB ); + } + + while( recvd < dstBytes ) { + + uint retval, + nR = nReadyForRead(); + + if( nR ) { + + if( nR > dstBytes - recvd ) + nR = dstBytes - recvd; + + if( nR > maxrecv ) + nR = maxrecv; + + retval = Socket::receiveData( buf + recvd, nR ); + recvd += retval; + + if( retval == 0 ) + break; + } + else if( !waitData( 1000 * read_timeout_secs ) ) + throw std::runtime_error("receiveData: Receive timed out."); + else if( !nReadyForRead() ) { + + // In this failure mode... + // flush out garbage byte for next time + char dump[4]; + Socket::receiveData( dump, 1 ); + throw std::runtime_error("receiveData: Data transfer interrupted."); + } + } + + return recvd; +} + + +uint NetClient::sendString( const string &s ) noexcept(false) +{ + vbuf.clear(); // clear response + + return Socket::sendData( s.data(), uint(s.length()) ); +} + + +// Return one line; newline stripped, null-terminated. +// +void NetClient::rcvLine( vector &line ) noexcept(false) +{ + line.clear(); + + for(;;) { + + char *v0, *vN; + uint nB = uint(vbuf.size()); + + // ------------------ + // Is '\n' in buffer? + // ------------------ + + if( nB && (vN = (char*)memchr( v0 = &vbuf[0], '\n', nB )) ) { + + // ------------- + // Copy data out + // ------------- + + *vN++ = 0; // convert '\n' to null + + uint nL = uint(vN - v0); + + line.resize( nL ); + memcpy( &line[0], v0, nL ); + + // ----------------------- + // Remove line from buffer + // ----------------------- + + if( (nB -= nL) > 0 ) + memcpy( v0, vN, nB ); + + vbuf.resize( nB ); + break; + } + + // ----------------------- + // Else, buffer more chars + // ----------------------- + + uint nR = nReadyForRead(); + + if( nR ) { + + vbuf.resize( nB + nR ); + Socket::receiveData( &vbuf[nB], nR ); + } + else if( !waitData( 1000 * read_timeout_secs ) ) { + + // nothing to read - quit. + line.push_back( 0 ); + throw std::runtime_error("rcvLine: Receive timed out."); + } + else { + + // should be something to read after the wait + + uint nR = nReadyForRead(); + + if( nR ) { + vbuf.resize( nB + nR ); + Socket::receiveData( &vbuf[nB], nR ); + } + else { + // In this failure mode... + // flush out garbage character for next time + vbuf.resize( nB + 1 ); + Socket::receiveData( &vbuf[nB], 1 ); + throw std::runtime_error("rcvLine: Data transfer interrupted."); + } + } + } +} + + +// Return true if OK received, data excludes OK. +// Return false if ERROR received, ERROR line is thrown. +// +bool NetClient::rcvLines( vector > &vlines ) noexcept(false) +{ + vlines.clear(); + + vector line; + + for(;;) { + + rcvLine( line ); + + if( line.size() ) { + + const char *s = &line[0]; + + if( !strcmp( s, "OK" ) ) + return true; + + if( !strncmp( s, "ERROR", 5 ) ) { + throw std::runtime_error( (string("rcvLines: ") + s).c_str() ); + return false; + } + + vlines.push_back( line ); + } + } +} + + diff --git a/ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/Build/NetClient.h b/ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/Build/NetClient.h new file mode 100644 index 00000000..fa6be996 --- /dev/null +++ b/ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/Build/NetClient.h @@ -0,0 +1,43 @@ +#ifndef NETCLIENT_H +#define NETCLIENT_H + +/* ---------------------------------------------------------------- */ +/* Includes ------------------------------------------------------- */ +/* ---------------------------------------------------------------- */ + +#include "Socket.h" + +#include +using namespace std; + +/* ---------------------------------------------------------------- */ +/* NetClient ------------------------------------------------------ */ +/* ---------------------------------------------------------------- */ + +class NetClient : public Socket +{ +private: + vector vbuf; // response buffer + uint read_timeout_secs; + +public: + NetClient( + const string &host = "localhost", + uint16 port = 0, + uint read_timeout_secs = 10 ); + + virtual uint sendData( const void *src, uint srcBytes ) noexcept(false); + + virtual uint receiveData( + void *dst, + uint dstBytes ) noexcept(false); + + uint sendString( const string &s ) noexcept(false); + + void rcvLine( vector &line ) noexcept(false); + bool rcvLines( vector > &vlines ) noexcept(false); +}; + +#endif // NETCLIENT_H + + diff --git a/ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/Build/Socket.cpp b/ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/Build/Socket.cpp new file mode 100644 index 00000000..67ca2d3d --- /dev/null +++ b/ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/Build/Socket.cpp @@ -0,0 +1,625 @@ + +//#include "stdafx.h" // enable if using Visual Studio precompiled headers + +#include "Socket.h" + +#include +#include + +#define CONNCLOSED std::runtime_error("Connection closed by peer.") + + +/* ---------------------------------------------------------------- */ +/* ---------------------------------------------------------------- */ +/* WINDOWS -------------------------------------------------------- */ +/* ---------------------------------------------------------------- */ +/* ---------------------------------------------------------------- */ + +#ifdef WIN32 // WINDOWS + +#include +#include + +static std::string WSAGetLastErrorMessage( + const char *prefix = "", + int errorid = 0 ); + +typedef int socklen_t; + +#define SHUT_RDWR 2 +#define CLOSE( x ) closesocket( x ) +#define IOCTL( x, y, z ) ioctlsocket( x, y, z ) +#define LASTERROR_STR( prefix ) WSAGetLastErrorMessage( prefix ) +#define LASTERROR_IS_CONNCLOSED() \ + (WSAGetLastError() == WSAENETRESET || \ + WSAGetLastError() == WSAECONNABORTED || \ + WSAGetLastError() == WSAECONNRESET) + +static bool StartupCalled = false; +static WSADATA wsaData; + +/* ---------------------------------------------------------------- */ +/* DoCleanup ------------------------------------------------------ */ +/* ---------------------------------------------------------------- */ + +// Called from atexit() as C-fun + +extern "C" void DoCleanup() +{ + if( StartupCalled ) + WSACleanup(); + + StartupCalled = false; +} + +/* ---------------------------------------------------------------- */ +/* DO_STARTUP ----------------------------------------------------- */ +/* ---------------------------------------------------------------- */ + +static inline void DO_STARTUP() +{ + if( !StartupCalled ) { + + if( WSAStartup( 1<<8 | 1, &wsaData ) ) { + + throw std::runtime_error( + "Could not start up winsock dll," + " WSAStartup() failed." ); + } + + atexit( DoCleanup ); + StartupCalled = true; + } +} + +/* ---------------------------------------------------------------- */ +/* ---------------------------------------------------------------- */ +/* UNIX ----------------------------------------------------------- */ +/* ---------------------------------------------------------------- */ +/* ---------------------------------------------------------------- */ + +#else // UNIX + +#include +#include +#include +#include +#include + +static std::string pfxerr( const char *prefix, const char *serr ); + +#define DO_STARTUP() do { } while(0) +#define CLOSE( x ) close( x ) +#define IOCTL( x, y, z ) ioctl( x, y, z ) +#define LASTERROR_STR( prefix ) pfxerr( prefix, std::strerror( errno ) ) +#define LASTERROR_IS_CONNCLOSED() \ + (errno == ECONNRESET || errno == ENOTCONN || errno == EPIPE) + +#endif // WIN32 or UNIX + +/* ---------------------------------------------------------------- */ +/* ---------------------------------------------------------------- */ +/* Common --------------------------------------------------------- */ +/* ---------------------------------------------------------------- */ +/* ---------------------------------------------------------------- */ + +Socket::Socket( int type ) + : m_addr(0), m_host("localhost"), m_error("Success"), + m_sock(-1), m_type(type), m_port(-1), + m_tcpNDelay(true), m_reuseAddr(true) +{ + DO_STARTUP(); + m_addr = new struct sockaddr_in; + m_addr->sin_family = AF_INET; +} + + +Socket::~Socket() +{ + tcpDisconnect(); + delete m_addr; + m_addr = 0; +} + + +bool Socket::tcpConnect( const std::string &host, uint16 port ) noexcept(false) +{ + tcpDisconnect(); + + if( host.length() ) + setHost( host ); + + if( port ) + setPort( port ); + + switch( m_type ) { + + case UDP: + m_sock = socket( PF_INET, SOCK_DGRAM, IPPROTO_IP ); + break; + + default: + m_sock = socket( PF_INET, SOCK_STREAM, IPPROTO_IP ); + break; + } + + if( !isValid() ) { + m_error = LASTERROR_STR( "tcpConnect: Invalid socket" ); + throw std::runtime_error( m_error ); + return false; + } + + if( m_type == TCP ) { + + setTCPNDelay(); + resolveHostAddr(); + + if( connect( + m_sock, + reinterpret_cast(m_addr), + sizeof(*m_addr) ) ) { + + m_error = LASTERROR_STR( "tcpConnect: Can't connect" ); + CLOSE( m_sock ); + m_sock = -1; + throw std::runtime_error( m_error ); + + return false; + } + } + + return true; +} + + +void Socket::tcpDisconnect() +{ + if( isValid() ) { + + shutdown( m_sock, SHUT_RDWR ); + CLOSE( m_sock ); + } + + m_sock = -1; +} + + +void Socket::setSocketOption( int option, bool enable ) +{ + if( !isValid() ) + return; + + switch( option ) { + + case TCPNoDelay: + m_tcpNDelay = enable; + setTCPNDelay(); + break; + + case ReuseAddr: + m_reuseAddr = enable; + setReuseAddr(); + break; + } +} + + +bool Socket::bind( const std::string &iface, uint16 port ) noexcept(false) +{ + setReuseAddr(); + + struct sockaddr_in addr; + + addr.sin_addr.s_addr = inet_addr( iface.c_str() ); + addr.sin_port = htons( port ); + addr.sin_family = AF_INET; + + if( ::bind( + m_sock, + reinterpret_cast(&addr), + sizeof(addr) ) != 0 ) { + + m_error = LASTERROR_STR( __func__ ); + throw std::runtime_error( m_error ); + + return false; + } + + return true; +} + + +uint Socket::sendData( const void *src, uint srcBytes ) noexcept(false) +{ + if( !srcBytes ) + return 0; + + int count = 0; + +// TODO: non-blocking IO here!? + + if( m_type == UDP ) { + + // Datagram/UDP + resolveHostAddr(); + + count = sendto( + m_sock, + static_cast(src), + srcBytes, + 0, + reinterpret_cast(m_addr), + sizeof(*m_addr) ); + } + else { + + // Stream/TCP + count = send( + m_sock, + static_cast(src), + srcBytes, + 0 ); + } + + if( count < 0 ) { + + if( LASTERROR_IS_CONNCLOSED() ) + throw CONNCLOSED; + + m_error = LASTERROR_STR( __func__ ); + throw std::runtime_error( m_error ); + } + else if( count == 0 ) + throw std::runtime_error(std::string("sendData: EOF on socket to ") + m_host); + + return count; +} + + +uint Socket::receiveData( void *dst, uint dstBytes ) noexcept(false) +{ + if( !dstBytes ) + return 0; + + int count = 0; + +// TODO: non-blocking IO here!? + + if( m_type == UDP ) { + + // Datagram/UDP + socklen_t fromlen = sizeof(*m_addr); + + resolveHostAddr(); + + count = recvfrom( + m_sock, + static_cast(dst), + dstBytes, + 0, + reinterpret_cast(m_addr), + &fromlen ); + } + else { + + // Stream/TCP + count = recv( + m_sock, + static_cast(dst), + dstBytes, + 0 ); + } + + if( count < 0 ) { + + if( LASTERROR_IS_CONNCLOSED() ) + throw CONNCLOSED; + + m_error = LASTERROR_STR( __func__ ); + throw std::runtime_error( m_error ); + } + else if( count == 0 ) + throw std::runtime_error(std::string("receiveData: EOF on socket to ") + m_host); + + return count; +} + + +bool Socket::waitData( uint waitMS ) noexcept(false) +{ + if( !isValid() ) + return false; + + int sec = waitMS / 1000, + ms = waitMS - 1000 * sec; + + struct timeval tv; + tv.tv_sec = sec; + tv.tv_usec = 1000 * ms; + + fd_set readfds; + FD_ZERO( &readfds ); + FD_SET( m_sock, &readfds ); + + int ret = select( m_sock + 1, &readfds, 0, 0, &tv ); + +// Ready + if( ret > 0 ) + return true; + +// !Ready + if( ret == 0 ) + return false; + +// Error + if( LASTERROR_IS_CONNCLOSED() ) + throw CONNCLOSED; + +// Else + m_error = LASTERROR_STR( __func__ ); + throw std::runtime_error( m_error ); + + return false; +} + + +uint Socket::nReadyForRead() noexcept(false) +{ + if( !isValid() ) + return 0; + +#ifdef WIN32 + ulong n = 0; +#else + int n = 0; +#endif + + if( IOCTL( m_sock, FIONREAD, &n ) == 0 ) + return (uint)n; + + if( LASTERROR_IS_CONNCLOSED() ) + throw CONNCLOSED; + + m_error = LASTERROR_STR( __func__ ); + throw std::runtime_error( m_error ); + + return 0; +} + +/* ---------------------------------------------------------------- */ +/* Private -------------------------------------------------------- */ +/* ---------------------------------------------------------------- */ + +void Socket::resolveHostAddr() +{ + resolveHostAddr( *m_addr, m_host, m_port ); +} + + +void Socket::resolveHostAddr( + struct sockaddr_in &addr, + const std::string &host, + uint16 port ) +{ + struct hostent *he = gethostbyname( host.c_str() ); + + if( !he ) { + + throw std::runtime_error( + host + + " is not found by the resolver (" + + LASTERROR_STR( "" ) + + ")." ); + } + + memcpy( &addr.sin_addr.s_addr, he->h_addr, he->h_length ); + addr.sin_port = htons( port ); + addr.sin_family = AF_INET; +} + + +void Socket::setTCPNDelay() const +{ +#ifdef WIN32 + BOOL flag = m_tcpNDelay; + int ret = setsockopt( + m_sock, + IPPROTO_TCP, + TCP_NODELAY, + reinterpret_cast(&flag), + sizeof(flag) ); +#else + long flag = m_tcpNDelay; + int ret = setsockopt( + m_sock, + IPPROTO_TCP, + TCP_NODELAY, + &flag, + sizeof(flag) ); +#endif + + if( ret != 0 ) + throw std::runtime_error( "Could not set TCP No Delay." ); +} + + +void Socket::setReuseAddr() const +{ +#ifdef WIN32 + BOOL flag = m_reuseAddr; + int ret = setsockopt( + m_sock, + SOL_SOCKET , + SO_REUSEADDR, + reinterpret_cast(&flag), + sizeof(flag) ); +#else + long flag = m_reuseAddr; + int ret = setsockopt( + m_sock, + SOL_SOCKET, + SO_REUSEADDR, + &flag, + sizeof(flag) ); +#endif + + if( ret != 0 ) + throw std::runtime_error( "Could not set SO_REUSEADDR." ); +} + +/* ---------------------------------------------------------------- */ +/* ---------------------------------------------------------------- */ +/* WINDOWS -------------------------------------------------------- */ +/* ---------------------------------------------------------------- */ +/* ---------------------------------------------------------------- */ + +#ifdef WIN32 + +#include +#include + +/* ---------------------------------------------------------------- */ +/* ErrorEntry ----------------------------------------------------- */ +/* ---------------------------------------------------------------- */ + +// List of Winsock error constants mapped to an interpretation string. +// Note that this list must remain sorted by the error constant +// values, because we do a binary search on the list when looking up +// items. + +static struct ErrorEntry { + const char* msg; + int nID; + + ErrorEntry( int id, const char* msg = 0 ) : msg(msg), nID(id) {} + + bool operator< ( const ErrorEntry &rhs ) {return nID < rhs.nID;} + +} gaErrorList[] = { + ErrorEntry(0, "No error"), + ErrorEntry(WSAEINTR, "Interrupted system call"), + ErrorEntry(WSAEBADF, "Bad file number"), + ErrorEntry(WSAEACCES, "Permission denied"), + ErrorEntry(WSAEFAULT, "Bad address"), + ErrorEntry(WSAEINVAL, "Invalid argument"), + ErrorEntry(WSAEMFILE, "Too many open sockets"), + ErrorEntry(WSAEWOULDBLOCK, "Operation would block"), + ErrorEntry(WSAEINPROGRESS, "Operation now in progress"), + ErrorEntry(WSAEALREADY, "Operation already in progress"), + ErrorEntry(WSAENOTSOCK, "Socket operation on non-socket"), + ErrorEntry(WSAEDESTADDRREQ, "Destination address required"), + ErrorEntry(WSAEMSGSIZE, "Message too long"), + ErrorEntry(WSAEPROTOTYPE, "Protocol wrong type for socket"), + ErrorEntry(WSAENOPROTOOPT, "Bad protocol option"), + ErrorEntry(WSAEPROTONOSUPPORT, "Protocol not supported"), + ErrorEntry(WSAESOCKTNOSUPPORT, "Socket type not supported"), + ErrorEntry(WSAEOPNOTSUPP, "Operation not supported on socket"), + ErrorEntry(WSAEPFNOSUPPORT, "Protocol family not supported"), + ErrorEntry(WSAEAFNOSUPPORT, "Address family not supported"), + ErrorEntry(WSAEADDRINUSE, "Address already in use"), + ErrorEntry(WSAEADDRNOTAVAIL, "Can't assign requested address"), + ErrorEntry(WSAENETDOWN, "Network is down"), + ErrorEntry(WSAENETUNREACH, "Network is unreachable"), + ErrorEntry(WSAENETRESET, "Net connection reset"), + ErrorEntry(WSAECONNABORTED, "Software caused connection abort"), + ErrorEntry(WSAECONNRESET, "Connection reset by peer"), + ErrorEntry(WSAENOBUFS, "No buffer space available"), + ErrorEntry(WSAEISCONN, "Socket is already connected"), + ErrorEntry(WSAENOTCONN, "Socket is not connected"), + ErrorEntry(WSAESHUTDOWN, "Can't send after socket shutdown"), + ErrorEntry(WSAETOOMANYREFS, "Too many references, can't splice"), + ErrorEntry(WSAETIMEDOUT, "Connection timed out"), + ErrorEntry(WSAECONNREFUSED, "Connection refused"), + ErrorEntry(WSAELOOP, "Too many levels of symbolic links"), + ErrorEntry(WSAENAMETOOLONG, "File name too long"), + ErrorEntry(WSAEHOSTDOWN, "Host is down"), + ErrorEntry(WSAEHOSTUNREACH, "No route to host"), + ErrorEntry(WSAENOTEMPTY, "Directory not empty"), + ErrorEntry(WSAEPROCLIM, "Too many processes"), + ErrorEntry(WSAEUSERS, "Too many users"), + ErrorEntry(WSAEDQUOT, "Disc quota exceeded"), + ErrorEntry(WSAESTALE, "Stale NFS file handle"), + ErrorEntry(WSAEREMOTE, "Too many levels of remote in path"), + ErrorEntry(WSASYSNOTREADY, "Network system is unavailable"), + ErrorEntry(WSAVERNOTSUPPORTED, "Winsock version out of range"), + ErrorEntry(WSANOTINITIALISED, "WSAStartup not yet called"), + ErrorEntry(WSAEDISCON, "Graceful shutdown in progress"), + ErrorEntry(WSAHOST_NOT_FOUND, "Host not found"), + ErrorEntry(WSANO_DATA, "No host data of that type was found") +}; + +static const int kNumMessages = sizeof(gaErrorList) / sizeof(ErrorEntry); + +/* ---------------------------------------------------------------- */ +/* WSAGetLastErrorMessage ----------------------------------------- */ +/* ---------------------------------------------------------------- */ + +// A function similar in spirit to Unix's perror() that tacks a canned +// interpretation of the value of WSAGetLastError() onto the end of a +// passed string, separated by a ": ". +// +std::string WSAGetLastErrorMessage( + const char *prefix, + int errorid ) +{ +// Build basic error string + + std::ostringstream outs; + + if( prefix && *prefix ) + outs << prefix << ": "; + +// Tack appropriate canned message onto end of supplied message +// prefix. Note that we do a binary search here: gaErrorList must +// be sorted by error constant value. + + ErrorEntry Target( errorid ? errorid : WSAGetLastError() ); + ErrorEntry *pEnd = gaErrorList + kNumMessages; + ErrorEntry *it = std::lower_bound( gaErrorList, pEnd, Target ); + + if( it != pEnd && it->nID == Target.nID ) + outs << it->msg; + else + outs << "unknown error"; + + outs << " (" << Target.nID << ")"; + +// Terminate message + + outs << std::ends; + + return outs.str(); +} + +#else // UNIX + +/* ---------------------------------------------------------------- */ +/* ---------------------------------------------------------------- */ +/* UNIX ----------------------------------------------------------- */ +/* ---------------------------------------------------------------- */ +/* ---------------------------------------------------------------- */ + +#include + +/* ---------------------------------------------------------------- */ +/* pfxerr --------------------------------------------------------- */ +/* ---------------------------------------------------------------- */ + +// if( prefix ) Return prefix + ": " + serr. +// else Return serr. +// +std::string pfxerr( const char *prefix, const char *serr ) +{ + std::ostringstream outs; + + if( prefix && *prefix ) + outs << prefix << ": "; + + outs << serr << std::ends; + + return outs.str(); +} + +#endif + + diff --git a/ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/Build/Socket.h b/ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/Build/Socket.h new file mode 100644 index 00000000..5e5b98f4 --- /dev/null +++ b/ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/Build/Socket.h @@ -0,0 +1,80 @@ +#ifndef SOCKET_H +#define SOCKET_H + +/* ---------------------------------------------------------------- */ +/* Includes ------------------------------------------------------- */ +/* ---------------------------------------------------------------- */ + +#include +#include + +struct sockaddr_in; + +/* ---------------------------------------------------------------- */ +/* Types ---------------------------------------------------------- */ +/* ---------------------------------------------------------------- */ + +typedef unsigned short uint16; +typedef unsigned int uint; +typedef unsigned long ulong; + +/* ---------------------------------------------------------------- */ +/* Socket --------------------------------------------------------- */ +/* ---------------------------------------------------------------- */ + +// NB: UDP is untested and probably doesn't work +class Socket +{ +private: + typedef int Sock_t; + + struct sockaddr_in *m_addr; + std::string m_host, + m_error; + Sock_t m_sock; + int m_type; + uint16 m_port; + bool m_tcpNDelay, + m_reuseAddr; + +public: + enum SocketType { TCP, UDP }; + enum SocketOption { TCPNoDelay, ReuseAddr }; + + Socket( int type = TCP ); + virtual ~Socket(); + + void setHost( const std::string &host ) {m_host = host;} + const std::string &host() const {return m_host;} + + void setPort( uint16 port ) {m_port = port;} + uint16 port() const {return m_port;} + + bool tcpConnect( const std::string &host = "", uint16 port = 0 ) noexcept(false); + void tcpDisconnect(); + + bool isValid() const {return m_sock > -1;} + std::string errorReason() const {return m_error;} + + void setSocketOption( int option, bool enable ); + bool bind( const std::string &iface = "0.0.0.0", uint16 port = 0 ) noexcept(false); + + virtual uint sendData( const void *src, uint srcBytes ) noexcept(false); + virtual uint receiveData( void *dst, uint dstBytes ) noexcept(false); + + bool waitData( uint waitMS = 10 ) noexcept(false); + uint nReadyForRead() noexcept(false); + +private: + void resolveHostAddr(); + static void resolveHostAddr( + struct sockaddr_in &addr, + const std::string &host, + uint16 port ); + void setTCPNDelay() const; + void setReuseAddr() const; +}; + +#endif // SOCKET_H + + diff --git a/ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/Build/makemex64.bat b/ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/Build/makemex64.bat new file mode 100644 index 00000000..c54f3352 --- /dev/null +++ b/ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/Build/makemex64.bat @@ -0,0 +1,2 @@ +"C:\Program Files\MATLAB\R2018b\bin\win64\mex" -DWIN32 -compatibleArrayDims -I. CalinsNetMex.cpp Socket.cpp NetClient.cpp -lWS2_32 -outdir ..\API +pause diff --git a/ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/Demos/DemoRemoteAPI.m b/ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/Demos/DemoRemoteAPI.m new file mode 100644 index 00000000..cc1aeb63 --- /dev/null +++ b/ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/Demos/DemoRemoteAPI.m @@ -0,0 +1,141 @@ +% ----------------------------------------- +% Variety of remote calls with timing stats +% ----------------------------------------- +% +function DemoRemoteAPI + +% Create connection (edit the IP address) +hSGL = SpikeGL(); + +% Don't call repeatedly if not running +if IsRunning(hSGL) + numCalls = 100; +else + numCalls = 1; +end + +% Init the timing measurements +getTimes = zeros(numCalls, 1); + +% ----------------------------------------------- +% Init parameters outside timing loop (as needed) +% ----------------------------------------------- + +% subset = GetStreamSaveChans(hSGL, 2, 0); +% subset = subset(1:64); +% subset = [10,66,192,193,194,195]; + +% ------------------------ +% Start of the timing loop +% ------------------------ + +for i=1:numCalls + +% --------------------- +% tic = start the clock +% --------------------- + + t0 = tic; + +% ------------------------- +% Demo setting audio params +% ------------------------- + +% prm = struct(); +% prm.stream = 'nidq'; +% SetAudioParams(hSGL, 'AOCtl_All', prm); +% prm.left = 0; +% prm.right = 0; +% prm.loCut = 'OFF'; +% prm.hiCut = 'INF'; +% prm.volume = 4.0; +% SetAudioParams(hSGL, 'AOCtl_Stream_-1', prm); +% SetAudioEnable(hSGL, 1); + +% ---------------------- +% Demo setting meta data +% ---------------------- + +% meta = struct(); +% meta.animal = 'Mr. Mouse'; +% meta.code = 'llrj17W0'; +% meta.trial = 19; +% SetMetaData(hSGL, meta); + +% ------------------------------- +% Variety of set/get calls to try +% ------------------------------- + + param = GetParams(hSGL) +% SetParams(hSGL, param); + +% count = GetStreamSampleCount(hSGL, 2, 0) + file = GetDataDir(hSGL) +% IsConsoleHidden(hSGL) +% ConsoleShow(hSGL); + % EnumDataDir(hSGL) +% Par2(hSGL, 'v', 'C:/SGL_DATA/myRun_g0_t0.nidq.bin'); +% VerifySha1(hSGL, 'C:/SGL_DATA/myRun_g0_t0.nidq.bin') +% StopRun(hSGL); +% StartRun(hSGL); +% NI_DO_Set(hSGL, 'PXI1Slot4/port0/line4', hex2dec('FF')) +% OBX_AO_Set(hSGL, -1, 21, '(5,-1.25)') + SetRunName(hSGL, 'myRun_g5_t5'); +% SetRecordingEnable(hSGL, 1); +% IsSaving(hSGL) +% a = GetStreamAcqChans(hSGL, 0, 0) +% a = GetStreamSaveChans(hSGL, 0, 0) + +% ----------------------- +% Fetch data for graphing +% ----------------------- + +% mat = FetchLatest(hSGL, 2, 0, 2000); + +% -------------------- +% toc = stop the clock +% -------------------- + + getTimes(i) = toc(t0); + +% --------------- +% Progress report +% --------------- + + if mod(i, 50) == 0 + fprintf('Completed %d calls...\n', i); + end + +% ----------------------- +% Graph fetched data here +% ----------------------- + +% showdata(mat); + +end % timing loop + +% ------------ +% Timing stats +% ------------ + +fprintf('Execution time -- mean: %g ms\tstd: %g ms\n', ... + 1000*mean(getTimes), 1000*std(getTimes)); + +end % DemoRemoteAPI + + +% ---------------------- +% Tiny graphing function +% ---------------------- +% +function showdata(mat) + x = 1:size(mat,1); + y = mat(:,1); + figure(1); + % set(gcf, 'doublebuffer', 'on'); + p = plot(x, y); + set(p, 'XData', x, 'YData', y); + drawnow; +end % showdata + + diff --git a/ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/Demos/JWave/jwave.meta b/ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/Demos/JWave/jwave.meta new file mode 100644 index 00000000..45b372ac --- /dev/null +++ b/ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/Demos/JWave/jwave.meta @@ -0,0 +1,6 @@ +[WaveMeta] +sample_frequency_Hz_dbl=25000 +wave_Vmax_dbl=2.5 +device_Vmax_dbl=5 +data_type_txt_i16_f32=txt +num_samples_i32=0 diff --git a/ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/Demos/JWave/jwave.txt b/ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/Demos/JWave/jwave.txt new file mode 100644 index 00000000..44e4544e --- /dev/null +++ b/ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/Demos/JWave/jwave.txt @@ -0,0 +1,6 @@ +do 5 { + level( 0, 50 ) + ramp( 0, 0.5, 10 ) + level( 0.5, 100 ) + ramp( 0.5, 0, 10 ) +} \ No newline at end of file diff --git a/ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/Demos/LatencyTest.m b/ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/Demos/LatencyTest.m new file mode 100644 index 00000000..27965686 --- /dev/null +++ b/ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/Demos/LatencyTest.m @@ -0,0 +1,59 @@ +% Continuously fetch LFP-band data from imec probe-0. +% Threshold channel 393 @ 0.45 mV. +% Send digital out command tracking threshold crossings. +% +% Runs until error or Ctrl-C. +% +% To measure closed-loop latency, immerse the probe in +% saline and give it a square wave signal (1 mV p-p, 1 Hz). +% We fetch all 384 channels of these data in a remote program. +% We analyze one of these channels looking for a rising edge. +% We then react to that threshold crossing by commanding an +% NI device to make another edge that is sent to the probe's +% SMA connector as a digital input. Now the separation between +% the LFP threshold event and the resulting NI event gives a +% direct readout of closed-loop latency. We measure the typical +% closed-loop latency to be 6.5 ms using the MATLAB API. +% +function LatencyTest + +hSGL = SpikeGL('127.0.0.1'); + +mv2i16 = 1.0/(1200.0/250/1024); % assume default gain of 250 for NP 1.0 +line = 'Dev4/port0/line5'; % edit for your setup +js = 2; +ip = 0; +id = 1 + (393 - 384); % add 1 for MATLAB +thresh = 0.45*mv2i16; +bits = 0; +channels = [384:768]; % zero-based for SpikeGLX + +fromCt = GetStreamSampleCount( hSGL, js, ip ); + +NI_DO_Set( hSGL, line, bits ); + +while 1 + + [M,headCt] = Fetch( hSGL, js, ip, fromCt, 120, channels ); + + [tpts,~] = size(M); + + if tpts > 1 + v_diff = M(tpts,id) - M(1,id); + + if v_diff > thresh && bits == 0 + bits = hex2dec('FF'); + NI_DO_Set( hSGL, line, bits ); + elseif v_diff < -thresh && bits == hex2dec('FF') + bits = 0; + NI_DO_Set( hSGL, line, bits ); + end + + fromCt = headCt + tpts; + end + +end + +end + + diff --git a/ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/Demos/wp_ni_soft_start.m b/ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/Demos/wp_ni_soft_start.m new file mode 100644 index 00000000..8c55e624 --- /dev/null +++ b/ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/Demos/wp_ni_soft_start.m @@ -0,0 +1,41 @@ +% Plays wave 'jwave' at NI channel AO-0. +% Playback is triggered by software command. +% +function wp_ni_soft_start + + % Handle to SpikeGLX + hSGL = SpikeGL( '127.0.0.1' ); + + % Load the wave plan, select infinite looping + wave_name = 'jwave'; + outChan = 'PXI1Slot6_2/ao0'; + looping = 1; + NI_Wave_Load( hSGL, outChan, wave_name, looping ); + + % Select software triggering + trigTerm = 'software'; + NI_Wave_Arm( hSGL, outChan, trigTerm ); + + % Start playback now + start = 1; + NI_Wave_StartStop( hSGL, outChan, start ); + + % This section demonstrates a method to capture your + % wave plan in action. The best pause parameters will + % depend upon the duration of your wave plan and how + % fast your SpikeGLX graphs are sweeping + % + % Let this play for 1 second + % Then pause the SpikeGLX Graphs Window for 2 seconds + % Then resume Graphs for 5 seconds + pause( 1.0 ); + PauseGraphs( hSGL, 1 ); + pause( 2.0 ); + PauseGraphs( hSGL, 0 ); + pause( 5.0 ); + + % Stop playback + start = 0; + NI_Wave_StartStop( hSGL, outChan, start ); + +end % wp_ni_soft_start diff --git a/ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/Demos/wp_soft_start.m b/ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/Demos/wp_soft_start.m new file mode 100644 index 00000000..ca26f6cb --- /dev/null +++ b/ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/Demos/wp_soft_start.m @@ -0,0 +1,45 @@ +% Plays wave 'jwave' at OneBox channel AO-0. +% Playback is triggered by software command. +% +function wp_soft_start + + % Handle to SpikeGLX + hSGL = SpikeGL( '127.0.0.1' ); + + % For demo purposes we assume the OneBox is not recording... + % So we refer to it using its slot index + ip = -1; + slot = 21; + + % Load the wave plan + wave_name = 'jwave'; + OBX_Wave_Load( hSGL, ip, slot, wave_name ); + + % Select software triggering and infinite looping + trigger = -2; + looping = 1; + OBX_Wave_Arm( hSGL, ip, slot, trigger, looping ); + + % Start playback now, output is always at AO-0 + start = 1; + OBX_Wave_StartStop( hSGL, ip, slot, start ); + + % This section demonstrates a method to capture your + % wave plan in action. The best pause parameters will + % depend upon the duration of your wave plan and how + % fast your SpikeGLX graphs are sweeping + % + % Let this play for 1 second + % Then pause the SpikeGLX Graphs Window for 2 seconds + % Then resume Graphs for 5 seconds + pause( 1.0 ); + PauseGraphs( hSGL, 1 ); + pause( 2.0 ); + PauseGraphs( hSGL, 0 ); + pause( 5.0 ); + + % Stop playback + start = 0; + OBX_Wave_StartStop( hSGL, ip, slot, start ); + +end % wp_soft_start diff --git a/ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/Demos/wp_trig_start.m b/ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/Demos/wp_trig_start.m new file mode 100644 index 00000000..bab1fd56 --- /dev/null +++ b/ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/Demos/wp_trig_start.m @@ -0,0 +1,55 @@ +% Plays wave 'jwave' at OneBox channel AO-0. +% Playback is triggered when OneBox channel AI-1 goes high. +% User needs to list AI-1 in the XA box of OBX Setup tab. +% We will configure NI device to send TTL rising edge from line-4. +% User needs to wire NI line-4 to OneBox AI-1. +% +function wp_trig_start + + % Handle to SpikeGLX + hSGL = SpikeGL( '127.0.0.1' ); + + % OneBox assumed to be recording stream ip=0... + % So the slot number is ignored in this case + ip = 0; + slot = -1; + + % Load the wave plan + wave_name = 'jwave'; + OBX_Wave_Load( hSGL, ip, slot, wave_name ); + + % Select AI-1 triggering and no looping + trigger = 1; + looping = 0; + OBX_Wave_Arm( hSGL, ip, slot, trigger, looping ); + + % Configure NI line-4 + digOutTerm = '/PXI1Slot6_2/line4'; + digBits = 0x10; % binary 1 at bit-4, zero elsewhere + NI_DO_Set( hSGL, digOutTerm, 0 ); + + % Start playback now, output is always at AO-0 + NI_DO_Set( hSGL, digOutTerm, digBits ); + + % Reset trigger after 50 ms + pause( 0.05 ); + NI_DO_Set( hSGL, digOutTerm, 0 ); + + % This section demonstrates a method to capture your + % wave plan in action. The best pause parameters will + % depend upon the duration of your wave plan and how + % fast your SpikeGLX graphs are sweeping + % + % Let this play for 1 second + % Then pause the SpikeGLX Graphs Window for 2 seconds + % Then resume Graphs + pause( 1.0 ); + PauseGraphs( hSGL, 1 ); + pause( 2.0 ); + PauseGraphs( hSGL, 0 ); + + % Stop playback + start = 0; + OBX_Wave_StartStop( hSGL, ip, slot, start ); + +end % wp_trig_start diff --git a/ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/Docs/Contents.txt b/ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/Docs/Contents.txt new file mode 100644 index 00000000..0f757945 --- /dev/null +++ b/ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/Docs/Contents.txt @@ -0,0 +1,44 @@ + +======== +Contents +======== + +API folder +========== + +API contains a complete prebuilt set of API components: + + + @SpikeGL // place in client MATLAB project directory + + CalinsNetMex.mexw64 // place in client MATLAB project directory + +Build folder +============ + +Note: Building is optional: the API components are prebuilt. + +Build contains the sources and make(*.bat) file to generate CalinsNetMex.mexw64: + + + CalinsNetMex.mexw64 // generated in API directory + +Demos folder +============ + +Tools demonstrating how to use the API. + +- JWave folder: This is a waveform you can play with the wp_XXX scripts. Copy these files into the _Waves folder at the top level of your SpikeGLX folder. + +- DemoRemoteAPI.m +- LatencyTest.m +- wp_ni_soft_start.m +- wp_soft_start.m +- wp_trig_start.m + +Docs folder +=========== + +- Contents.txt +- GettingStarted.txt +- Help.txt +- WhatsNew.txt + + diff --git a/ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/Docs/GettingStarted.txt b/ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/Docs/GettingStarted.txt new file mode 100644 index 00000000..46ea6a8a --- /dev/null +++ b/ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/Docs/GettingStarted.txt @@ -0,0 +1,65 @@ +==================================== +Talking live to SpikeGLX from MATLAB +==================================== + + +SpikeGLX Setup +============== + +1. Launch SpikeGLX on the data acquisition machine. This machine and the MATLAB client machine must be connected to the same network. + +2. Open SpikeGLX dialog 'Options/Command Server Settings...'. + +3. Click 'Enable Remote Command Server' and then click 'My Address'. + +4. Write down the IP address; you'll need to type that into the MATLAB client code. + +Note: You can run SpikeGLX and MATLAB on the same machine, and in this configuration you can either use the computer's actual network address (per step 3), or the 'loopback' address if you don't have a network connection on this computer. Every computer has an internal 'loopback' address set to '127.0.0.1'. Use that with default port number 4142. + +Note: If the script times out (about 10 seconds) when trying to connect to SpikeGLX, it may be Windows Firewall blocking access. You might try adding SpikeGLX to the allowed list of applications in the Firewall controls. + + +MATLAB Setup +============ + +Needed components are located within Release subfolder 'SpikeGLX-MATLAB-SDK/API'. + +1. Create a folder for your MATLAB client project, such as 'My_SGLX_MATLAB'. + +2. Copy '@SpikeGL' and 'CalinsNetMex.mexw64' from API into 'My_SGLX_MATLAB'. + +3. To get started with example code, copy 'DemoRemoteAPI.m' into 'My_SGLX_MATLAB'. + +In summary, your folder looks like: + + My_SGLX_MATLAB + @SpikeGL + CalinsNetMex.mexw64 + DemoRemoteAPI.m + + +Running MATLAB +============== + +1. Launch MATLAB, add 'My_SGLX_MATLAB' to your path. + +2. Navigate to 'My_SGLX_MATLAB' and open 'DemoRemoteAPI.m'. + +3. Edit the line 'hSGL = SpikeGL('10.101.20.29');' to reflect the correct IP address. + +4. Go to the section called 'Variety of set/get calls to try' and choose a trial test function by uncommenting it. For example, try 'file = GetDataDir(hSGL)' which will return the data directory whether SpikeGLX is running an acquisition or is currently idle. + +5. Experiment with other commands (only a few are demonstrated in this m file). + + +Next +==== + +The full set of commands is in file '@SpikeGL/Contents.m'. + +'LatencyTest.m' is a simple demonstration of real-time fetching and +processing of streaming imec probe data. + +{wp_XXX.m} demonstrate calling the waveplayer from a script. + + diff --git a/ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/Docs/Help.txt b/ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/Docs/Help.txt new file mode 100644 index 00000000..97bb55f3 --- /dev/null +++ b/ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/Docs/Help.txt @@ -0,0 +1,74 @@ +======== +SYNOPSIS +======== + +The @SpikeGL API class is a MATLAB object with methods to access the SpikeGLX program via TCP/IP. On top of that, our message exchange protocol implements acknowledgement, validation and error reporting. SpikeGLX and MATLAB can run on the same machine (via loopback socket address 127.0.0.1) or across a network. + +The API provides extensive control over a running SpikeGLX process: starting and stopping a run, setting parameters, fetching live data, calling the Par2 and SHA1 tools, and so on. Everything you need to integate SpikeGLX into your workflow. + +Users (clients) of this class merely need to construct an instance of a @SpikeGL object and all network communication with the SpikeGLX server process is handled automatically. + +The network socket handle is used with the 'CalinsNetMex' mexFunction, which is a helper mexFunction that does all the actual socket communications for this class. + +Instances of @SpikeGL are weakly stateful: merely keeping a handle to a network socket. It is ok for MATLAB to create and destroy several of these objects. Each network connection cleans up after itself on the server side after 10 seconds of inactivity. By the way, if your script has paused longer than 10 seconds, and you reuse a handle that has timed out, the handle will automatically reestablish a connection and the script will likely continue without problems, but a warning will appear in the Command Window reflecting the timeout. Such warnings have a warningid, so you can suppress them by typing >> warning( 'off', 'CalinsNetMex:connectionClosed' ). + +======== +EXAMPLES +======== + +my_s = SpikeGL; // connect to SpikeGLX running on local machine + +prms = GetParams( my_s ); // optionally retrieve run params as struct + +SetParams( my_s, struct('niMNChans1','0:5','niDualDevMode','false',...) ); // make changes + +StartRun( my_s ); // start data acquisition run using last-set params + +StopRun( my_s ); // stop run and clean up + +======== +(js, ip) +======== + +The two integer values (js, ip) select a data stream. +js: stream type: {0=nidq, 1=obx, 2=imec-probe}. +ip: substream: {0=nidq (if js=0), 0+=which OneBox or imec probe}. +Examples (js, ip): +(0, 0) = nidq. // for nidq, ip is arbitrary but zero by convention +(1, 4) = obx4. +(2, 7) = imec7. +Note: ip has range [0..np-1], where, np is queried using GetStreamNP(). + +======================= +GetParams and SetParams +======================= + +Manual Pre-validation +===================== + +You'll find that several of the API functions to get or set run parameters complain if you haven't validated any parameters yet. To validate parameters, visit the Configure dialog in SpikeGLX and make choices for your experiment, then click either 'Run' or 'Verify|Save'. Either of these buttons apply a battery of self-consistency checks to your choices. The most recent set of Configuration settings that have passed all the sanity checks are saved in: + +- 'SpikeGLX/_Configs/daq.ini' +- 'SpikeGLX/_Calibration/imec_probe_settings.ini' +- 'SpikeGLX/_Calibration/imec_onebox_settings.ini'. + +The above ini files are used to initialize the Configure dialog each time you open it. Open the ini files in a text editor. You'll see several subgroups of settings. This is the best way to see the exact spelling, case, and value type of the items you can read and write via the API. Examples of accessing the subgroups: + +- Group [DAQSettings]: GetParams() +- Group [DAQ_Imec_All]: GetParamsImecCommon() +- Group [SerialNumberToProbe]/SNjjj: GetParamsImecProbe() +- Group [SerialNumberToOneBox]/SNjjj: GetParamsOneBox() + +Generally, follow this workflow: + +(1) Start SpikeGLX and make sure its Command Server is listening (you'll see a message to that effect in the Console/Log window). + +(2) Open the Configure dialog to elect which hardware streams you want to run. + +(3) Click 'Detect' and then 'Verify|Save'. + +Now you are ready to run from MATLAB. + +(4) Typically you will need to adjust just a few settings from within your script. + + diff --git a/ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/Docs/WhatsNew.txt b/ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/Docs/WhatsNew.txt new file mode 100644 index 00000000..3c99ab9e --- /dev/null +++ b/ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/Docs/WhatsNew.txt @@ -0,0 +1,344 @@ +============== +AS OF 20250325 +============== + +- Add waveplayer example scripts and waveform. + +============== +AS OF 20241215 +============== + +- GetParamsOneBox takes (... ip, slot). +- SetParamsOneBox takes (... ip, slot). +- SetDigOut replaced by NI_DO_Set. +- Add NI_Wave_Arm. +- Add NI_Wave_Load. +- Add NI_Wave_StartStop. +- Add OBX_AO_Set. +- Add OBX_Wave_Arm. +- Add OBX_Wave_Load. +- Add OBX_Wave_StartStop. +- Add PauseGraphs. + +============== +AS OF 20240129 +============== + +- Allow remote fetch of filtered IM stream. + +============== +AS OF 20230425 +============== + +New functions +------------- +GetGeomMap +GetProbeList +SetAnatomy_Pinpoint + +============== +AS OF 20230411 +============== + +- Fecthes returning zero samples do not generate warnings. + +============== +AS OF 20230202 +============== + +- GetStreamShankMap can only be used on NI stream (js = 0) because imec +ShankMaps are for internal use only. They will be replaced by geomMap. + +- "Onebox" -> "OneBox" + +============== +AS OF 20220101 +============== + +- Add 'LatencyTest.m' demo file. + +- Retire 32-bit MATLAB support. + +- Streamline CalinsNetMex. + +- Simplify error handling. + +- Users must reinstall @SpikeGL and CalinsNetMex.mexw64. + +- Formerly streams were referenced by the single parameter streamID: {-1=nidq, 0+=imec-probe}. +Henceforth two parameters (js, ip) are used to select an expanded variety of streams: +- js: stream type: {0=nidq, 1=obx, 2=imec-probe}. +- ip: substream: {0=nidq (if js=0), 0+=which OneBox or imec probe}. +Examples (js, ip): +(0, 0) = nidq. // for nidq, ip is arbitrary but zero by convention +(1, 4) = obx4. +(2, 7) = imec7. +Note: ip has range [0..np-1], where, np is queried using GetStreamNP(). + +Affected functions +------------------ +Fetch +FetchLatest +GetAcqChanCounts -> GetStreamAcqChans +GetFileStartCount -> GetStreamFileStart +GetImProbeCount -> GetStreamNP +GetImProbeSN -> GetStreamSN +GetImVoltageRange -> GetStreamVoltageRange +GetSampleRate -> GetStreamSampleRate +GetSaveChans -> GetStreamSaveChans +GetScanCount -> GetStreamSampleCount +IsUserOrder +MapSample + +New functions +------------- +GetImecChanGains +GetParamsImecCommon +SetParamsImecCommon +GetParamsImecProbe +SetParamsImecProbe +GetParamsOneBox +SetParamsOneBox +GetStreamI16ToVolts +GetStreamMaxInt +GetStreamShankMap + +Old -> New function replacements +-------------------------------- +SetMetaData -> SetMetadata + +Changed behavior +---------------- +Fetch, +FetchLatest -> default channel subset is all_acquired, rather than all_saved. + +Opto functions +-------------- +Opto_getAttenuations +Opto_emit + + +============== +AS OF 20200309 +============== + +New functions +------------- +SetMultiDriveEnable + +New parameters +-------------- +GetDataDir +SetDataDir +EnumDataDir + + +============== +AS OF 20190327 +============== + +New functions +------------- +GetImProbeCount +GetImVoltageRange +MapSample +SetTriggerOffBeep +SetTriggerOnBeep + + +============== +AS OF 20190305 +============== + +New functions +------------- +SetNextFileName + + +============== +AS OF 20190214 +============== + +Old -> New function replacements +-------------------------------- +GetRunDir -> GetDataDir +SetRunDir -> SetDataDir +EnumRunDir -> EnumDataDir + + +============== +AS OF 20180829 +============== + +Consolidate NI/IM functions using streamID as follows: +streamID = -1: NI, +streamID = 0: probe 0, +streamID = 1: probe 1, ... + +New functions +------------- +GetSampleRate + +Changed syntax +-------------- +Fetch +FetchLatest +IsUserOrder +GetFileStartCount +GetScanCount +GetSaveChans +GetAcqChanCounts + + +============== +AS OF 20180715 +============== + +New functions +------------- +GetImProbeSN + + +============== +AS OF 20170903 +============== + +Old -> New function replacements +-------------------------------- +GetAcqChanCounts -> GetAcqChanCountsNI, GetAcqChanCountsIm + + +Changed syntax +-------------- +GetAcqChanCountsIm requires a streamID. +GetSaveChansIm now requires a streamID. +GetFileStartCountIm now requires a streamID. +GetScanCountIm now requires a streamID. +FetchIm, FetchLatestIm now require a streamID. +SetAudioParams now requires a subgroup name and params struct. + + +============== +AS OF 20170724 +============== + +New functions +------------- +SetMetaData + + +============== +AS OF 20170501 +============== + +Old -> New function replacements +-------------------------------- +SetAOEnable -> SetAudioEnable +SetAOParams -> SetAudioParams + + +============== +AS OF 20160601 +============== + +New functions +------------- +GetFileStartCountIm, GetFileStartCountNi + + +Changed syntax +-------------- +FetchLatestNi, FetchLatestIm now return [matrix, headCt]. + + +============== +AS OF 20160502 +============== + +New functions +------------- +IsUserOrderIm, IsUserOrderNi + + +============== +AS OF 20160404 +============== + +Old -> New function replacements +-------------------------------- +SetTrgEnable -> SetRecordingEnable + + +============== +AS OF 20160120 +============== + +Old -> New function replacements +-------------------------------- +GetScanCount -> GetScanCountNi, GetScanCountIm +GetChannelSubset -> GetSaveChansNi, GetSaveChansIm +GetDAQData -> FetchNi, FetchIm +GetLastNDAQData -> FetchLatestNi, FetchLatestIm + + +Changed syntax +-------------- +GetAcqChanCounts now returns vector including {AP,LF,SY,MN,MA,XA,DW}. + + +All other functions +------------------- +Same syntax + + +IMPORTANT +--------- +Use new mex files. + + +============== +AS OF 20151231 +============== + +Contents.m documentation file is accurate. + + +Old retired functions +--------------------- +FastSettle +GetCurrentRunFile +GetCurrentSaveFile + + +Old -> New function replacements +-------------------------------- +ConsoleUnhide -> ConsoleShow +IsAcquiring -> IsRunning +DoQueryMatrix -> GetDAQData +GetDir -> EnumRunDir +GetSaveDir -> GetRunDir +GetSaveFile -> GetRunName +SetSaveDir -> SetRunDir +SetSaveFile -> SetRunName +SetSaving -> SetTrgEnable +StartACQ -> StartRun +StopACQ -> StopRun + + +New functions +------------- +GetAcqChanCounts +SetAOParams +SetAOEnable +SetDigOut + + +Changed syntax +-------------- +GetDAQData now returns two params [mat,headCt]; where headCt is the +zero-based index of the first timepoint in the matrix. This allows +consecutive fetches. + +GetAcqChanCounts now returns vector including {AP,LF,MN,MA,XA,DW}. + + diff --git a/ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/LICENSE.txt b/ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/LICENSE.txt new file mode 100644 index 00000000..b6ac09c8 --- /dev/null +++ b/ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/LICENSE.txt @@ -0,0 +1,12 @@ + +The Janelia Research Campus Software Copyright 1.2 + +Copyright (c) 2024, Howard Hughes Medical Institute, All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + + Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + Neither the name of the Howard Hughes Medical Institute nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, ANY IMPLIED WARRANTIES OF MERCHANTABILITY, NON-INFRINGEMENT, OR FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; REASONABLE ROYALTIES; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/MATLAB_latency.png b/ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/MATLAB_latency.png new file mode 100644 index 0000000000000000000000000000000000000000..4251bb4b0c3c7742bfd8ede21fe5f8c0b5431c87 GIT binary patch literal 13357 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 literal 0 HcmV?d00001 diff --git a/ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/README.md b/ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/README.md new file mode 100644 index 00000000..00446adf --- /dev/null +++ b/ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX-MATLAB-SDK-main/README.md @@ -0,0 +1,39 @@ +SpikeGLX-MATLAB-SDK +=================== + +### What + +The SDK lets MATLAB applications communicate with SpikeGLX phase30 versions +20220101 and later: + +* Set/get parameters. +* Start/Stop runs. +* Start/stop writing. +* Fetch data in real time. +* Everything you need to integrate SpikeGLX into your workflow. + +There's an included closed-loop latency test program; used to obtain these +results on an i7-8850H, 2.6 GHz laptop: + +![MATLAB API Latency](MATLAB_latency.png) + +>*Note: Low latency mode is available with SpikeGLX 20230815 and later.* + +### Who + +The SDK is developed by [Bill Karsh](https://www.janelia.org/people/bill-karsh) +of the [Tim Harris Lab](https://www.janelia.org/lab/harris-lab-apig) at +HHMI/Janelia Research Campus. + +### Building in Windows + +I build using MS Visual Studio C++ 2015, and MATLAB R2018b. + +### Licensing + +Use is subject to Janelia Research Campus Software Copyright 1.2 license terms: +[http://license.janelia.org/license](http://license.janelia.org/license). + + +_fin_ + diff --git a/ExperPort/Modules/NeuropixelHelper_Modules/npy-matlab/constructNPYheader.m b/ExperPort/Modules/NeuropixelHelper_Modules/npy-matlab/constructNPYheader.m new file mode 100644 index 00000000..ca2840ec --- /dev/null +++ b/ExperPort/Modules/NeuropixelHelper_Modules/npy-matlab/constructNPYheader.m @@ -0,0 +1,88 @@ + + + +function header = constructNPYheader(dataType, shape, varargin) + + if ~isempty(varargin) + fortranOrder = varargin{1}; % must be true/false + littleEndian = varargin{2}; % must be true/false + else + fortranOrder = true; + littleEndian = true; + end + + dtypesMatlab = {'uint8','uint16','uint32','uint64','int8','int16','int32','int64','single','double', 'logical'}; + dtypesNPY = {'u1', 'u2', 'u4', 'u8', 'i1', 'i2', 'i4', 'i8', 'f4', 'f8', 'b1'}; + + magicString = uint8([147 78 85 77 80 89]); %x93NUMPY + + majorVersion = uint8(1); + minorVersion = uint8(0); + + % build the dict specifying data type, array order, endianness, and + % shape + dictString = '{''descr'': '''; + + if littleEndian + dictString = [dictString '<']; + else + dictString = [dictString '>']; + end + + dictString = [dictString dtypesNPY{strcmp(dtypesMatlab,dataType)} ''', ']; + + dictString = [dictString '''fortran_order'': ']; + + if fortranOrder + dictString = [dictString 'True, ']; + else + dictString = [dictString 'False, ']; + end + + dictString = [dictString '''shape'': (']; + +% if length(shape)==1 && shape==1 +% +% else +% for s = 1:length(shape) +% if s==length(shape) && shape(s)==1 +% +% else +% dictString = [dictString num2str(shape(s))]; +% if length(shape)>1 && s+1==length(shape) && shape(s+1)==1 +% dictString = [dictString ',']; +% elseif length(shape)>1 && s %s', tempFilename, inFilename, outFilename)); + + otherwise + fprintf(1, 'I don''t know how to concatenate files for your OS, but you can finish making the NPY youself by concatenating %s with %s.\n', tempFilename, inFilename); +end + diff --git a/ExperPort/Modules/NeuropixelHelper_Modules/npy-matlab/readNPY.m b/ExperPort/Modules/NeuropixelHelper_Modules/npy-matlab/readNPY.m new file mode 100644 index 00000000..9095d003 --- /dev/null +++ b/ExperPort/Modules/NeuropixelHelper_Modules/npy-matlab/readNPY.m @@ -0,0 +1,37 @@ + + +function data = readNPY(filename) +% Function to read NPY files into matlab. +% *** Only reads a subset of all possible NPY files, specifically N-D arrays of certain data types. +% See https://github.com/kwikteam/npy-matlab/blob/master/tests/npy.ipynb for +% more. +% + +[shape, dataType, fortranOrder, littleEndian, totalHeaderLength, ~] = readNPYheader(filename); + +if littleEndian + fid = fopen(filename, 'r', 'l'); +else + fid = fopen(filename, 'r', 'b'); +end + +try + + [~] = fread(fid, totalHeaderLength, 'uint8'); + + % read the data + data = fread(fid, prod(shape), [dataType '=>' dataType]); + + if length(shape)>1 && ~fortranOrder + data = reshape(data, shape(end:-1:1)); + data = permute(data, [length(shape):-1:1]); + elseif length(shape)>1 + data = reshape(data, shape); + end + + fclose(fid); + +catch me + fclose(fid); + rethrow(me); +end diff --git a/ExperPort/Modules/NeuropixelHelper_Modules/npy-matlab/readNPYheader.m b/ExperPort/Modules/NeuropixelHelper_Modules/npy-matlab/readNPYheader.m new file mode 100644 index 00000000..7776de52 --- /dev/null +++ b/ExperPort/Modules/NeuropixelHelper_Modules/npy-matlab/readNPYheader.m @@ -0,0 +1,72 @@ + + +function [arrayShape, dataType, fortranOrder, littleEndian, totalHeaderLength, npyVersion] = readNPYheader(filename) +% function [arrayShape, dataType, fortranOrder, littleEndian, ... +% totalHeaderLength, npyVersion] = readNPYheader(filename) +% +% parse the header of a .npy file and return all the info contained +% therein. +% +% Based on spec at http://docs.scipy.org/doc/numpy-dev/neps/npy-format.html + +fid = fopen(filename); + +% verify that the file exists +if (fid == -1) + if ~isempty(dir(filename)) + error('Permission denied: %s', filename); + else + error('File not found: %s', filename); + end +end + +try + + dtypesMatlab = {'uint8','uint16','uint32','uint64','int8','int16','int32','int64','single','double', 'logical'}; + dtypesNPY = {'u1', 'u2', 'u4', 'u8', 'i1', 'i2', 'i4', 'i8', 'f4', 'f8', 'b1'}; + + + magicString = fread(fid, [1 6], 'uint8=>uint8'); + + if ~all(magicString == [147,78,85,77,80,89]) + error('readNPY:NotNUMPYFile', 'Error: This file does not appear to be NUMPY format based on the header.'); + end + + majorVersion = fread(fid, [1 1], 'uint8=>uint8'); + minorVersion = fread(fid, [1 1], 'uint8=>uint8'); + + npyVersion = [majorVersion minorVersion]; + + headerLength = fread(fid, [1 1], 'uint16=>uint16'); + + totalHeaderLength = 10+headerLength; + + arrayFormat = fread(fid, [1 headerLength], 'char=>char'); + + % to interpret the array format info, we make some fairly strict + % assumptions about its format... + + r = regexp(arrayFormat, '''descr''\s*:\s*''(.*?)''', 'tokens'); + if isempty(r) + error('Couldn''t parse array format: "%s"', arrayFormat); + end + dtNPY = r{1}{1}; + + littleEndian = ~strcmp(dtNPY(1), '>'); + + dataType = dtypesMatlab{strcmp(dtNPY(2:3), dtypesNPY)}; + + r = regexp(arrayFormat, '''fortran_order''\s*:\s*(\w+)', 'tokens'); + fortranOrder = strcmp(r{1}{1}, 'True'); + + r = regexp(arrayFormat, '''shape''\s*:\s*\((.*?)\)', 'tokens'); + shapeStr = r{1}{1}; + arrayShape = str2num(shapeStr(shapeStr~='L')); + + + fclose(fid); + +catch me + fclose(fid); + rethrow(me); +end diff --git a/ExperPort/Modules/NeuropixelHelper_Modules/npy-matlab/writeNPY.m b/ExperPort/Modules/NeuropixelHelper_Modules/npy-matlab/writeNPY.m new file mode 100644 index 00000000..844dc35c --- /dev/null +++ b/ExperPort/Modules/NeuropixelHelper_Modules/npy-matlab/writeNPY.m @@ -0,0 +1,25 @@ + + +function writeNPY(var, filename) +% function writeNPY(var, filename) +% +% Only writes little endian, fortran (column-major) ordering; only writes +% with NPY version number 1.0. +% +% Always outputs a shape according to matlab's convention, e.g. (10, 1) +% rather than (10,). + + +shape = size(var); +dataType = class(var); + +header = constructNPYheader(dataType, shape); + +fid = fopen(filename, 'w'); +fwrite(fid, header, 'uint8'); +fwrite(fid, var, dataType); +fclose(fid); + + +end + diff --git a/ExperPort/Modules/NeuropixelHelper_Modules/read_TTL_Events.m b/ExperPort/Modules/NeuropixelHelper_Modules/read_TTL_Events.m new file mode 100644 index 00000000..b1840902 --- /dev/null +++ b/ExperPort/Modules/NeuropixelHelper_Modules/read_TTL_Events.m @@ -0,0 +1,120 @@ +% This script demonstrates how to load and analyze TTL event data from an +% Open Ephys recording session saved in the binary (.npy) format. +% +% PRE-REQUISITES: +% 1. The 'npy-matlab' library must be downloaded from GitHub: +% https://github.com/kwikteam/npy-matlab +% 2. The path to the 'npy-matlab' folder must be added to your MATLAB path. +% e.g., addpath('C:\path\to\npy-matlab\npy-matlab') + +clear; clc; close all; + +%% --- User-Defined Parameters --- + +% 1. Set the path to the folder containing your TTL event files. +% This is typically a path like: +% ...\\events\Rhythm_FPGA-100.0\TTL_1 +% ttl_folder_path = ['C:\Ephys_Experiment_Data\sound_cat_rat\rawdata\sub-002_id-LP12_expmtr-lida\ses-01_date-20250707T151043_dtype-ephys\ephys\2025-07-07_15-11-36\Record Node 101\experiment1\recording2...' ... + % '\events\Neuropix-PXI-100.ProbeA\TTL']; + + ttl_folder_path = ['C:\Ephys_Experiment_Data\sound_cat_rat\rawdata\sub-003_id-LP12_expmtr-lida\ses-20_date-20250812T124748_dtype-ephys\ephys\2025-08-12_12-58-23\Record Node 101\experiment1\recording1\events\Neuropix-PXI-100.ProbeA-LFP\TTL']; +% 2. Define which TTL channel you want to analyze. +% (e.g., channel 1, 2, 3, etc.) +channel_to_analyze = 1; + + +%% --- Check for Required Files and Library --- + +% Verify that the npy-matlab library function exists +if ~exist('readNPY.m', 'file') + error(['The ''readNPY.m'' function was not found. ' ... + 'Please ensure the npy-matlab library is downloaded ' ... + 'and added to your MATLAB path.']); +end + +% Construct the full file paths +timestamps_file = fullfile(ttl_folder_path, 'timestamps.npy'); +states_file = fullfile(ttl_folder_path, 'states.npy'); + +% Verify that the data files exist +if ~exist(timestamps_file, 'file') || ~exist(states_file, 'file') + error('Could not find "timestamps.npy" or "states.npy" in the specified folder: %s', ttl_folder_path); +end + + +%% --- Load the Data using readNPY --- + +fprintf('Loading data from: %s\n', ttl_folder_path); + +try + % readNPY will read the .npy file and convert it into a MATLAB array. + % The data types are handled automatically. + timestamps = readNPY(timestamps_file); % Timestamps in seconds + channel_states = readNPY(states_file); % Channel state changes (+/- channel number) + + fprintf('Successfully loaded %d events.\n', length(timestamps)); + +catch ME + error('Failed to read .npy files. Error details: %s', ME.message); +end + + +%% --- Analyze and Extract Specific Events --- + +% Find the indices of all events related to the channel of interest. +% We are interested in both rising edges (ON state, positive number) and +% falling edges (OFF state, negative number). +indices_for_channel = find(abs(channel_states) == channel_to_analyze); + +if isempty(indices_for_channel) + fprintf('No events found for TTL channel %d.\n', channel_to_analyze); + return; +end + +% Extract the specific timestamps and states for our channel +channel_timestamps = timestamps(indices_for_channel); +channel_specific_states = channel_states(indices_for_channel); + +% --- Find Rising Edges (Pulse Starts) --- +% A rising edge corresponds to a positive channel number in the states array. +rising_edge_indices = find(channel_specific_states > 0); +rising_edge_timestamps = channel_timestamps(rising_edge_indices); + +% --- Find Falling Edges (Pulse Ends) --- +% A falling edge corresponds to a negative channel number. +falling_edge_indices = find(channel_specific_states < 0); +falling_edge_timestamps = channel_timestamps(falling_edge_indices); + +fprintf('Found %d rising edges (pulse starts) for channel %d.\n', ... + length(rising_edge_timestamps), channel_to_analyze); +fprintf('Found %d falling edges (pulse ends) for channel %d.\n', ... + length(falling_edge_timestamps), channel_to_analyze); + + +%% --- Visualization --- + +figure('Name', 'TTL Event Analysis', 'NumberTitle', 'off', 'Color', 'w'); +hold on; +grid on; + +% Plot all events for the specified channel as vertical lines +% Green for ON (rising edge), Red for OFF (falling edge) +for i = 1:length(rising_edge_timestamps) + plot([rising_edge_timestamps(i), rising_edge_timestamps(i)], [0, 1], 'g-', 'LineWidth', 1.5); +end + +for i = 1:length(falling_edge_timestamps) + plot([falling_edge_timestamps(i), falling_edge_timestamps(i)], [0, 1], 'r--', 'LineWidth', 1.5); +end + +% Make the plot informative +title(sprintf('TTL Events for Channel %d', channel_to_analyze)); +xlabel('Time (seconds)'); +ylabel('Event'); +ylim([-0.1, 1.1]); +set(gca, 'YTick', [0, 1], 'YTickLabel', {'OFF', 'ON'}); +legend({'Rising Edge (ON)', 'Falling Edge (OFF)'}, 'Location', 'best'); + +fprintf('Plot generated successfully.\n'); + +%% From a143bdc6df0afd86e8aa90c1a48f3c52bbe6ccf9 Mon Sep 17 00:00:00 2001 From: arpit99ag Date: Mon, 29 Sep 2025 14:53:05 +0100 Subject: [PATCH 155/164] error debug --- Protocols/@ArpitSoundCatContinuous/PsychometricSection.m | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Protocols/@ArpitSoundCatContinuous/PsychometricSection.m b/Protocols/@ArpitSoundCatContinuous/PsychometricSection.m index 3b4992fe..5b009237 100644 --- a/Protocols/@ArpitSoundCatContinuous/PsychometricSection.m +++ b/Protocols/@ArpitSoundCatContinuous/PsychometricSection.m @@ -19,7 +19,7 @@ % Context 1 DispParam(obj, 'Context3_trialStart', 1,x,y, 'position', [x, y, 100 20],'label','t_start','TooltipString','3rd context started at this trial'); - DispParam(obj, 'Context3_trialEnd', 1,x,y, 'position', [x + 101, y, 100 20],'label','t_end','TooltipString','3rd context ended at this trial') + DispParam(obj, 'Context3_trialEnd', 1,x,y, 'position', [x + 101, y, 100 20],'label','t_end','TooltipString','3rd context ended at this trial'); next_row(y); DispParam(obj, 'Context3_Dist', Category_Dist, x,y,'label','Context3_Distr','TooltipString','stim distribution for 3rd context'); make_invisible(Context3_Dist); make_invisible(Context3_trialStart);make_invisible(Context3_trialEnd); @@ -27,7 +27,7 @@ % Context 2 DispParam(obj, 'Context2_trialStart', 1,x,y, 'position', [x, y, 100 20],'label','t_start','TooltipString','2nd context started at this trial'); - DispParam(obj, 'Context2_trialEnd', 1,x,y, 'position', [x + 101, y, 100 20],'label','t_end','TooltipString','2nd context ended at this trial') + DispParam(obj, 'Context2_trialEnd', 1,x,y, 'position', [x + 101, y, 100 20],'label','t_end','TooltipString','2nd context ended at this trial'); next_row(y); DispParam(obj, 'Context2_Dist', Category_Dist, x,y,'label','Context2_Distr','TooltipString','stim distribution for 2nd context'); make_invisible(Context2_Dist); make_invisible(Context2_trialStart);make_invisible(Context2_trialEnd); @@ -35,7 +35,7 @@ % Context 3 DispParam(obj, 'Context1_trialStart', 1,x,y, 'position', [x, y, 100 20],'label','Trial_start','TooltipString','1st context started at this trial'); - DispParam(obj, 'Context1_trialEnd', 1,x,y, 'position', [x + 101, y, 100 20],'label','Trial_end','TooltipString','1st context ended at this trial') + DispParam(obj, 'Context1_trialEnd', 1,x,y, 'position', [x + 101, y, 100 20],'label','Trial_end','TooltipString','1st context ended at this trial'); next_row(y); DispParam(obj, 'Context1_Dist', Category_Dist, x,y,'label','Context1_Distr','TooltipString','stim distribution for 1st context'); next_row(y); From 2e1bb73c21812755fa21be17830d76e351cca98a Mon Sep 17 00:00:00 2001 From: arpit99ag Date: Wed, 1 Oct 2025 13:15:21 +0100 Subject: [PATCH 156/164] error debug --- .../.bonsai/Settings/Camera_Control.layout | 79 +------------------ .../private/RealTimeAnalysis.m | 18 ++--- 2 files changed, 10 insertions(+), 87 deletions(-) diff --git a/ExperPort/Plugins/@bonsaicamera/.bonsai/Settings/Camera_Control.layout b/ExperPort/Plugins/@bonsaicamera/.bonsai/Settings/Camera_Control.layout index 1d5df696..0829d87c 100644 --- a/ExperPort/Plugins/@bonsaicamera/.bonsai/Settings/Camera_Control.layout +++ b/ExperPort/Plugins/@bonsaicamera/.bonsai/Settings/Camera_Control.layout @@ -1,79 +1,2 @@  - - - false - - 94 - 95 - - - 333 - 63 - - Bonsai.Design.ObjectTextVisualizer - - - - - - false - - 234 - 234 - - - 336 - 279 - - Maximized - Bonsai.Vision.Design.IplImageVisualizer - - - - - - false - - 472 - 106 - - - 333 - 63 - - Bonsai.Design.ObjectTextVisualizer - - - - - - false - - 15 - 705 - - - 333 - 277 - - Bonsai.Vision.Design.IplImageVisualizer - - - - - - false - - 95 - 246 - - - 333 - 177 - - Bonsai.Design.ObjectTextVisualizer - - - - - \ No newline at end of file + \ No newline at end of file diff --git a/Protocols/@ArpitSoundCatContinuous/private/RealTimeAnalysis.m b/Protocols/@ArpitSoundCatContinuous/private/RealTimeAnalysis.m index 6bc4aa1e..218545d3 100644 --- a/Protocols/@ArpitSoundCatContinuous/private/RealTimeAnalysis.m +++ b/Protocols/@ArpitSoundCatContinuous/private/RealTimeAnalysis.m @@ -592,16 +592,16 @@ function updateStimulusHistogram(ax, state, data, config, ui_table_handle) max_count = 0; - for i = 1:n_selected_blocks - block_idx = selected_indices(i); % Use the index of the selected row + for p = 1:n_selected_blocks + block_idx = selected_indices(p); % Use the index of the selected row block_stat = state.blockStatsHistory(block_idx); - multiplier = 1; - if i == n_selected_blocks, multiplier = 2; end + multiplier = 1; + if p == n_selected_blocks, multiplier = 2; end yyaxis(ax, 'left'); - plot(ax, bin_centers, block_stat.stimCounts.incorrect, '-', 'Color', red_map(i,:), 'LineWidth', multiplier * 1.5); - plot(ax, bin_centers, block_stat.stimCounts.correct, '-', 'Color', green_map(i,:), 'LineWidth', multiplier * 1.5); + plot(ax, bin_centers, block_stat.stimCounts.incorrect, '-', 'Color', red_map(p,:), 'LineWidth', multiplier * 1.5); + plot(ax, bin_centers, block_stat.stimCounts.correct, '-', 'Color', green_map(p,:), 'LineWidth', multiplier * 1.5); max_count = max([max_count, block_stat.stimCounts.correct, block_stat.stimCounts.incorrect]); yyaxis(ax, 'right'); @@ -612,11 +612,11 @@ function updateStimulusHistogram(ax, state, data, config, ui_table_handle) stim_correct = stim_valid(hit_valid == 1); stim_incorrect = stim_valid(hit_valid == 0); - jitter_base = (i - 1) * 0.2; + jitter_base = (p - 1) * 0.2; jitter_incorrect = jitter_base + 0.08 * rand(size(stim_incorrect)); jitter_correct = jitter_base + 0.08 * rand(size(stim_correct)); - scatter(ax, stim_incorrect, jitter_incorrect, multiplier * 25, red_map(i,:), 'filled', 'MarkerFaceAlpha', multiplier * 0.4); - scatter(ax, stim_correct, jitter_correct, multiplier * 25, green_map(i,:), 'filled', 'MarkerFaceAlpha', multiplier * 0.4); + scatter(ax, stim_incorrect, jitter_incorrect, multiplier * 25, red_map(p,:), 'filled', 'MarkerFaceAlpha', multiplier * 0.4); + scatter(ax, stim_correct, jitter_correct, multiplier * 25, green_map(p,:), 'filled', 'MarkerFaceAlpha', multiplier * 0.4); end yyaxis(ax, 'left'); From 5ea797681474c18577af7693bbd83b330976210e Mon Sep 17 00:00:00 2001 From: Arpit Date: Wed, 8 Oct 2025 11:13:56 +0100 Subject: [PATCH 157/164] added functionality to neuropixel recording GUI --- .../NeuropixelNeuroblueprint.asv | 1514 +++++++++++++++++ .../NeuropixelNeuroblueprint.m | 487 ++++-- .../MATLAB/DemoReadSGLXData.m | 48 + .../MATLAB/DiagnoseSyncBits.m | 117 ++ .../MATLAB/ExtractFixedPulsesByBit.m | 73 + .../MATLAB/ExtractSyncPulsesChunked.m | 83 + .../MATLAB/SGLX_readMeta.m | 495 ++++++ .../MATLAB/ks25_phy_toBinary.m | 261 +++ .../MATLAB/loadSMAfromAP.m | 42 + .../Python/DemoReadSGLXData/__init__.py | 0 .../Python/DemoReadSGLXData/readSGLX.py | 460 +++++ .../Python/read_SGLX_analog.ipynb | 117 ++ .../Python/read_SGLX_digital.ipynb | 118 ++ .../SpikeGLX_Datafile_Tools-main/README.md | 11 + .../.bonsai/Settings/Camera_Control.editor | 37 - .../.bonsai/Settings/Camera_Control.layout | 2 - .../Camera_Control.bonsai.layout | 333 ++++ 17 files changed, 4038 insertions(+), 160 deletions(-) create mode 100644 ExperPort/Modules/@NeuropixelNeuroblueprint/NeuropixelNeuroblueprint.asv create mode 100644 ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX_Datafile_Tools-main/MATLAB/DemoReadSGLXData.m create mode 100644 ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX_Datafile_Tools-main/MATLAB/DiagnoseSyncBits.m create mode 100644 ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX_Datafile_Tools-main/MATLAB/ExtractFixedPulsesByBit.m create mode 100644 ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX_Datafile_Tools-main/MATLAB/ExtractSyncPulsesChunked.m create mode 100644 ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX_Datafile_Tools-main/MATLAB/SGLX_readMeta.m create mode 100644 ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX_Datafile_Tools-main/MATLAB/ks25_phy_toBinary.m create mode 100644 ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX_Datafile_Tools-main/MATLAB/loadSMAfromAP.m create mode 100644 ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX_Datafile_Tools-main/Python/DemoReadSGLXData/__init__.py create mode 100644 ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX_Datafile_Tools-main/Python/DemoReadSGLXData/readSGLX.py create mode 100644 ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX_Datafile_Tools-main/Python/read_SGLX_analog.ipynb create mode 100644 ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX_Datafile_Tools-main/Python/read_SGLX_digital.ipynb create mode 100644 ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX_Datafile_Tools-main/README.md delete mode 100644 ExperPort/Plugins/@bonsaicamera/.bonsai/Settings/Camera_Control.editor delete mode 100644 ExperPort/Plugins/@bonsaicamera/.bonsai/Settings/Camera_Control.layout create mode 100644 ExperPort/Plugins/@bonsaicamera/Camera_Control.bonsai.layout diff --git a/ExperPort/Modules/@NeuropixelNeuroblueprint/NeuropixelNeuroblueprint.asv b/ExperPort/Modules/@NeuropixelNeuroblueprint/NeuropixelNeuroblueprint.asv new file mode 100644 index 00000000..1e357a40 --- /dev/null +++ b/ExperPort/Modules/@NeuropixelNeuroblueprint/NeuropixelNeuroblueprint.asv @@ -0,0 +1,1514 @@ +function [obj, varargout] = NeuropixelNeuroblueprint(varargin) +% NEUROPIXEL_NEUROBLUEPRINT_GUI - Integrated GUI for electrophysiology and behavior experiments +% +% This GUI manages experimental workflows for coordinated electrophysiology recording +% (using either Open Ephys or SpikeGLX) with behavioral protocols using Bpod/ExperPort. +% +% FEATURES: +% - Support for both Open Ephys and SpikeGLX recording systems +% - Neuropixels probe management (NP 1.0 and 2.0) +% - NeuroBlueprint data organization format +% - Automated session management and data saving +% - SVN integration for version control +% - Pre/post-session sampling across probe banks +% +% PRE-REQUISITES: +% 1. 'open-ephys-matlab-tools' must be in MATLAB path (for Open Ephys) +% 2. 'SpikeGLX-MATLAB-SDK' must be in MATLAB path (for SpikeGLX) +% 3. Bpod/ratter/ExperPort environment fully configured +% +% USAGE: +% gui_obj = NeuropixelNeuroblueprintGUI(); +% +%% Boilerplate for class definition and action handling +obj = class(struct, mfilename); +varargout = {}; +% Display usage help when function is called directly +if nargin==0 || (nargin==1 && ischar(varargin{1}) && strcmp(varargin{1}, 'empty')) + display_usage_help(); + return; +end +if isa(varargin{1}, mfilename) + if length(varargin) < 2 || ~ischar(varargin{2}) + error(['If called with a "%s" object as first arg, a second arg, a ' ... + 'string specifying the action, is required\n']); + else + action = varargin{2}; + varargin = varargin(3:end); + end +else + action = varargin{1}; + varargin = varargin(2:end); +end +if ~ischar(action) + error('The action parameter must be a string'); +end +GetSoloFunctionArgs(obj); +%% Main Action Router +switch action + % ========================================================================= + % CASE INIT + % ========================================================================= + case 'init' + % So that only the CPU-based software renderer instead of your graphics card + % opengl software; + + % Start Bpod if not already running + if evalin('base', 'exist(''BpodSystem'', ''var'')') + if evalin('base', '~isempty(BpodSystem)'), newstartup; else, flush; end + else, Bpod('COM5');newstartup; + end + + % --- State Variables as SoloParamHandles --- + SoloParamHandle(obj, 'currentState', 'value', 'Load'); + SoloParamHandle(obj, 'behavState', 'value', 'Run'); + SoloParamHandle(obj, 'ephysState', 'value', 'Run'); + SoloParamHandle(obj, 'is_running', 'value', 0); + SoloParamHandle(obj, 'recording_software', 'value', 'OpenEphys'); % 'OpenEphys' or 'SpikeGLX' + SoloParamHandle(obj, 'recording_controller', 'value', []); % Will hold OE or SpikeGLX controller + SoloParamHandle(obj, 'behav_obj', 'value', []); + SoloParamHandle(obj, 'blinking_timer', 'value', []); + SoloParamHandle(obj, 'current_params', 'value', []); + SoloParamHandle(obj, 'session_base_path', 'value', ''); + SoloParamHandle(obj, 'probe_settings', 'value', struct('version', '2.0', 'reference', 'Tip', 'bank', 0, 'imro_path', '')); + SoloParamHandle(obj, 'probe_gui_handles', 'value', []); + SoloParamHandle(obj, 'session_info_list', 'value', []); % Holds parsed info from folders + + % Create stopping timer for behavior + scr = timer; + set(scr,'Period', 0.2,'ExecutionMode','FixedRate','TasksToExecute',Inf,... + 'BusyMode','drop','TimerFcn',[mfilename,'(''End_Continued'')']); + SoloParamHandle(obj, 'stopping_complete_timer', 'value', scr); + + % --- Create the GUI Figure --- + SoloParamHandle(obj, 'myfig', 'saveable', 0); + myfig.value = figure('Name', 'Neuropixels Recording & Behavior Controller',... + 'NumberTitle', 'off', 'MenuBar', 'none', ... + 'ToolBar', 'none', 'Units', 'normalized', ... + 'Position', [0.1, 0.1, 0.7, 0.85],... + 'Color', [0.94, 0.94, 0.94], ... + 'CloseRequestFcn', {@(h,e) feval(mfilename, obj, 'close')}); + % --- UI Creation --- + handles = struct(); + + % Activity Log Panel + uipanel('Title', 'Activity Log', 'FontSize', 12, 'BorderType', 'etchedin', 'BorderWidth', 1, 'Units', 'normalized', 'Position', [0.64, 0.03, 0.34, 0.94]); + handles.log_box = uicontrol('Style', 'edit', 'Units', 'normalized', 'Position', [0.65, 0.05, 0.32, 0.89], 'String', {'Log started...'}, 'Max', 10, 'Min', 1, 'HorizontalAlignment', 'left', 'Enable', 'inactive', 'BackgroundColor', [1, 1, 1]); + + % Panel 0: Recording Software Selection + p0 = uipanel('Title', '0. Recording Software', 'FontSize', 12, 'FontWeight', 'bold', 'BorderType', 'etchedin', 'BorderWidth', 1, 'Units', 'normalized', 'Position', [0.02, 0.88, 0.6, 0.09]); + handles.software_group = uibuttongroup(p0, 'Title', '', 'BorderType', 'none', 'Units', 'normalized', 'Position', [0.05, 0.1, 0.9, 0.8], 'SelectionChangedFcn', {@(h,e) feval(mfilename, obj, 'recording_software_callback')}); + uicontrol(handles.software_group, 'Style', 'radiobutton', 'String', 'Open Ephys', 'Units', 'normalized', 'Position', [0.1, 0.3, 0.4, 0.4], 'Tag', 'OpenEphys', 'FontSize', 10); + uicontrol(handles.software_group, 'Style', 'radiobutton', 'String', 'SpikeGLX', 'Units', 'normalized', 'Position', [0.5, 0.3, 0.4, 0.4], 'Tag', 'SpikeGLX', 'FontSize', 10); + + % Panel 1: Behavior + p1 = uipanel('Title', '1. Behavior', 'FontSize', 12, 'FontWeight', 'bold', 'BorderType', 'etchedin', 'BorderWidth', 1, 'Units', 'normalized', 'Position', [0.02, 0.74, 0.6, 0.13]); + uicontrol(p1, 'Style', 'text', 'String', 'Protocol Name:', 'Units', 'normalized', 'Position', [0.05, 0.7, 0.22, 0.25], 'HorizontalAlignment', 'right'); + handles.protocol_edit = uicontrol(p1, 'Style', 'edit', 'String', 'ArpitSoundCatContinuous', 'Units', 'normalized', 'Position', [0.3, 0.7, 0.45, 0.25]); + handles.manual_test = uicontrol(p1, 'Style', 'checkbox', 'String', 'Manual Test', 'Value', 1, 'Units', 'normalized', 'Position', [0.78, 0.7, 0.2, 0.25]); + uicontrol(p1, 'Style', 'text', 'String', 'Experimenter:', 'Units', 'normalized', 'Position', [0.02, 0.4, 0.2, 0.25], 'HorizontalAlignment', 'right'); + handles.exp_popup = uicontrol(p1, 'Style', 'popupmenu', 'String', {'-'}, 'Units', 'normalized', 'Position', [0.23, 0.4, 0.25, 0.25], 'Callback', {@(h,e) feval(mfilename, obj, 'populate_and_filter_lists', 'filter')}); + uicontrol(p1, 'Style', 'text', 'String', 'Rat Name:', 'Units', 'normalized', 'Position', [0.50, 0.4, 0.18, 0.25], 'HorizontalAlignment', 'right'); + handles.rat_name_popup = uicontrol(p1, 'Style', 'popupmenu', 'String', {'-'}, 'Units', 'normalized', 'Position', [0.69, 0.4, 0.30, 0.25], 'Callback', {@(h,e) feval(mfilename, obj, 'populate_and_filter_lists', 'filter')}); + uicontrol(p1, 'Style', 'text', 'String', 'Distribution:', 'Units', 'normalized', 'Position', [0.02, 0.1, 0.18, 0.25], 'HorizontalAlignment', 'right'); + handles.distribution_popup = uicontrol(p1, 'Style', 'popupmenu', 'String', {'random', 'Uniform', 'Hard A', 'Hard B'}, 'Units', 'normalized', 'Position', [0.21, 0.1, 0.35, 0.25]); + uicontrol(p1, 'Style', 'text', 'String', 'Path:', 'Units', 'normalized', 'Position', [0.58, 0.1, 0.1, 0.25], 'HorizontalAlignment', 'right'); + handles.behav_edit = uicontrol(p1, 'Style', 'edit', 'String', 'C:\ratter', 'Units', 'normalized', 'Position', [0.69, 0.1, 0.30, 0.25]); + + % Panel 2: NeuroBlueprint Format + p2 = uipanel('Title', '2. NeuroBlueprint Format', 'FontSize', 12, 'FontWeight', 'bold', 'BorderType', 'etchedin', 'BorderWidth', 1, 'Units', 'normalized', 'Position', [0.02, 0.42, 0.6, 0.31]); + uicontrol(p2, 'Style', 'text', 'String', 'Project Name:', 'Units', 'normalized', 'Position', [0.01, 0.85, 0.28, 0.1], 'HorizontalAlignment', 'right'); + handles.proj_edit = uicontrol(p2, 'Style', 'edit', 'String', 'sound_cat_rat', 'Units', 'normalized', 'Position', [0.3, 0.85, 0.65, 0.12], 'Callback', {@(h,e) feval(mfilename, obj, 'populate_and_filter_lists', 'rescan')}); + uicontrol(p2, 'Style', 'text', 'String', 'Subject ID:', 'Units', 'normalized', 'Position', [0.01, 0.7, 0.28, 0.1], 'HorizontalAlignment', 'right'); + handles.sub_popup = uicontrol(p2, 'Style', 'popupmenu', 'String', {'-'}, 'Units', 'normalized', 'Position', [0.3, 0.7, 0.3, 0.12], 'Enable', 'inactive'); + uicontrol(p2, 'Style', 'text', 'String', 'Session ID:', 'Units', 'normalized', 'Position', [0.61, 0.7, 0.2, 0.1], 'HorizontalAlignment', 'right'); + handles.session_popup = uicontrol(p2, 'Style', 'popupmenu', 'String', {'-'}, 'Units', 'normalized', 'Position', [0.82, 0.7, 0.15, 0.12]); + uicontrol(p2, 'Style', 'text', 'String', 'Local Path:', 'Units', 'normalized', 'Position', [0.01, 0.55, 0.28, 0.1], 'HorizontalAlignment', 'right'); + handles.local_edit = uicontrol(p2, 'Style', 'edit', 'String', 'C:\Ephys_Experiment_Data', 'Units', 'normalized', 'Position', [0.3, 0.55, 0.5, 0.12], 'Callback', {@(h,e) feval(mfilename, obj, 'populate_and_filter_lists', 'rescan')}); + handles.local_browse = uicontrol(p2, 'Style', 'pushbutton', 'String', 'Browse...', 'Units', 'normalized', 'Position', [0.81, 0.55, 0.16, 0.13], 'Callback', {@(h,e) feval(mfilename, obj, 'browse_path', 'local')}); + uicontrol(p2, 'Style', 'text', 'String', 'Central Path:', 'Units', 'normalized', 'Position', [0.01, 0.4, 0.28, 0.1], 'HorizontalAlignment', 'right'); + handles.central_edit = uicontrol(p2, 'Style', 'edit', 'String', 'Z:\_projects', 'Units', 'normalized', 'Position', [0.3, 0.4, 0.5, 0.12], 'Callback', {@(h,e) feval(mfilename, obj, 'populate_and_filter_lists', 'rescan')}); + handles.central_browse = uicontrol(p2, 'Style', 'pushbutton', 'String', 'Browse...', 'Units', 'normalized', 'Position', [0.81, 0.4, 0.16, 0.13], 'Callback', {@(h,e) feval(mfilename, obj, 'browse_path', 'central')}); + uicontrol(p2, 'Style', 'text', 'String', 'Subfolders to Create:', 'Units', 'normalized', 'Position', [0.05, 0.2, 0.9, 0.1], 'HorizontalAlignment', 'left'); + handles.cb_ephys = uicontrol(p2, 'Style', 'checkbox', 'String', 'ephys', 'Value', 1, 'Units', 'normalized', 'Position', [0.05, 0.05, 0.2, 0.15]); + handles.cb_behav = uicontrol(p2, 'Style', 'checkbox', 'String', 'behav', 'Value', 1, 'Units', 'normalized', 'Position', [0.28, 0.05, 0.2, 0.15]); + handles.cb_anat = uicontrol(p2, 'Style', 'checkbox', 'String', 'anat', 'Value', 0, 'Units', 'normalized', 'Position', [0.51, 0.05, 0.2, 0.15]); + handles.cb_funcimg = uicontrol(p2, 'Style', 'checkbox', 'String', 'funcimg', 'Value', 0, 'Units', 'normalized', 'Position', [0.74, 0.05, 0.25, 0.15]); + + % Panel 3: Pre/Post-Experiment Sampling + p3 = uipanel('Title', '3. Pre/Post-Experiment Sampling', 'FontSize', 12, 'FontWeight', 'bold', 'BorderType', 'etchedin', 'BorderWidth', 1, 'Units', 'normalized', 'Position', [0.02, 0.28, 0.6, 0.12]); + uicontrol(p3, 'Style', 'text', 'String', 'Duration/Bank (s):', 'Units', 'normalized', 'Position', [0.01, 0.6, 0.25, 0.25], 'HorizontalAlignment', 'right'); + handles.sample_duration = uicontrol(p3, 'Style', 'edit', 'String', '60', 'Units', 'normalized', 'Position', [0.27, 0.6, 0.1, 0.3]); + handles.target_display = uicontrol(p3, 'Style', 'text', 'String', 'Target: Bank 0', 'Units', 'normalized', 'Position', [0.38, 0.6, 0.3, 0.25], 'HorizontalAlignment', 'right', 'FontWeight', 'bold'); + handles.probe_button = uicontrol(p3, 'Style', 'pushbutton', 'String', 'Probe Setting', 'Units', 'normalized', 'Position', [0.7, 0.55, 0.28, 0.4], 'FontSize', 10, 'Callback', {@(h,e) feval(mfilename, obj, 'open_probe_gui')}); + handles.sample_button = uicontrol(p3, 'Style', 'pushbutton', 'String', 'Start Sample Recording', 'Units', 'normalized', 'Position', [0.05, 0.1, 0.9, 0.4], 'FontSize', 12, 'FontWeight', 'bold', 'BackgroundColor', [0.8, 0.7, 1], 'Callback', {@(h,e) feval(mfilename, obj, 'sample_recording_wrapper')}); + + % Panel 4: Recording Settings + p4 = uipanel('Title', '4. Recording Settings', 'FontSize', 12, 'FontWeight', 'bold', 'BorderType', 'etchedin', 'BorderWidth', 1, 'Units', 'normalized', 'Position', [0.02, 0.15, 0.6, 0.11]); + handles.settings_panel = p4; + + % Open Ephys Settings (initially visible) + handles.oe_ip_label = uicontrol(p4, 'Style', 'text', 'String', 'GUI IP:', 'Units', 'normalized', 'Position', [0.01, 0.3, 0.15, 0.4], 'HorizontalAlignment', 'right'); + handles.oe_ip_edit = uicontrol(p4, 'Style', 'edit', 'String', '127.0.0.1', 'Units', 'normalized', 'Position', [0.17, 0.25, 0.15, 0.5]); + handles.oe_proc_label = uicontrol(p4, 'Style', 'text', 'String', 'Proc ID:', 'Units', 'normalized', 'Position', [0.33, 0.3, 0.15, 0.4], 'HorizontalAlignment', 'right'); + handles.oe_proc_edit = uicontrol(p4, 'Style', 'edit', 'String', '100', 'Units', 'normalized', 'Position', [0.49, 0.25, 0.15, 0.5]); + handles.oe_rec_label = uicontrol(p4, 'Style', 'text', 'String', 'Rec ID:', 'Units', 'normalized', 'Position', [0.65, 0.3, 0.15, 0.4], 'HorizontalAlignment', 'right'); + handles.oe_rec_edit = uicontrol(p4, 'Style', 'edit', 'String', '101', 'Units', 'normalized', 'Position', [0.81, 0.25, 0.15, 0.5]); + + % SpikeGLX Settings (initially hidden) + handles.sglx_host_label = uicontrol(p4, 'Style', 'text', 'String', 'Host IP:', 'Units', 'normalized', 'Position', [0.01, 0.3, 0.15, 0.4], 'HorizontalAlignment', 'right', 'Visible', 'off'); + handles.sglx_host_edit = uicontrol(p4, 'Style', 'edit', 'String', 'localhost', 'Units', 'normalized', 'Position', [0.17, 0.25, 0.15, 0.5], 'Visible', 'off'); + handles.sglx_port_label = uicontrol(p4, 'Style', 'text', 'String', 'Port:', 'Units', 'normalized', 'Position', [0.33, 0.3, 0.15, 0.4], 'HorizontalAlignment', 'right', 'Visible', 'off'); + handles.sglx_port_edit = uicontrol(p4, 'Style', 'edit', 'String', '4142', 'Units', 'normalized', 'Position', [0.49, 0.25, 0.15, 0.5], 'Visible', 'off'); + handles.sglx_probe_label = uicontrol(p4, 'Style', 'text', 'String', 'Probe Idx:', 'Units', 'normalized', 'Position', [0.65, 0.3, 0.15, 0.4], 'HorizontalAlignment', 'right', 'Visible', 'off'); + handles.sglx_probe_edit = uicontrol(p4, 'Style', 'edit', 'String', '0', 'Units', 'normalized', 'Position', [0.81, 0.25, 0.15, 0.5], 'Visible', 'off'); + % --- Control Buttons Panel --- + p5 = uipanel('Title', 'Controls', 'FontSize', 12, 'FontWeight', 'bold', 'BorderType', 'etchedin', 'BorderWidth', 1, 'Units', 'normalized', 'Position', [0.02, 0.02, 0.6, 0.11]); + handles.control_button = uicontrol(p5, 'Style', 'pushbutton', 'String', 'Load', 'Units', 'normalized', 'FontSize', 14, 'FontWeight', 'bold', 'Position', [0.02, 0.1, 0.3, 0.8], 'BackgroundColor', [0.2, 0.6, 0.8], 'Callback', {@(h,e) feval(mfilename, obj, 'main_control_callback')}); + handles.behav_button = uicontrol(p5, 'Style', 'pushbutton', 'String', 'Run Behav', 'Units', 'normalized', 'FontSize', 12, 'Position', [0.35, 0.1, 0.3, 0.8], 'BackgroundColor', [1, 0.8, 0.6], 'Callback', {@(h,e) feval(mfilename, obj, 'behav_control_callback')}); + handles.ephys_button = uicontrol(p5, 'Style', 'pushbutton', 'String', 'Run Ephys', 'Units', 'normalized', 'FontSize', 12, 'Position', [0.68, 0.1, 0.3, 0.8], 'BackgroundColor', [0.8, 0.6, 1], 'Callback', {@(h,e) feval(mfilename, obj, 'ephys_control_callback')}); + + SoloParamHandle(obj, 'ui_handles', 'value', handles); + + feval(mfilename, obj, 'populate_and_filter_lists', 'rescan'); + log_message(handles, 'GUI initialization complete.'); + % ========================================================================= + % CASE MAIN_CONTROL_CALLBACK + % ========================================================================= + case 'main_control_callback' + switch value(currentState) + case 'Load', feval(mfilename, obj, 'load_sequence'); + case 'Run', feval(mfilename, obj, 'run_sequence'); + case 'Stop', feval(mfilename, obj, 'stop_sequence'); + case 'PostExperiment', feval(mfilename, obj, 'reset_to_load_state'); + end + % ========================================================================= + % CASE RECORDING_SOFTWARE_CALLBACK + % ========================================================================= + case 'recording_software_callback' + handles = value(ui_handles); + selected_software = get(get(handles.software_group, 'SelectedObject'), 'Tag'); + recording_software.value = selected_software; + + if strcmp(selected_software, 'OpenEphys') + % Show OpenEphys settings, hide SpikeGLX + set([handles.oe_ip_label, handles.oe_ip_edit, handles.oe_proc_label, handles.oe_proc_edit, handles.oe_rec_label, handles.oe_rec_edit], 'Visible', 'on'); + set([handles.sglx_host_label, handles.sglx_host_edit, handles.sglx_port_label, handles.sglx_port_edit, handles.sglx_probe_label, handles.sglx_probe_edit], 'Visible', 'off'); + log_message(handles, 'Switched to Open Ephys recording mode.'); + else % SpikeGLX + % Hide OpenEphys settings, show SpikeGLX + set([handles.oe_ip_label, handles.oe_ip_edit, handles.oe_proc_label, handles.oe_proc_edit, handles.oe_rec_label, handles.oe_rec_edit], 'Visible', 'off'); + set([handles.sglx_host_label, handles.sglx_host_edit, handles.sglx_port_label, handles.sglx_port_edit, handles.sglx_probe_label, handles.sglx_probe_edit], 'Visible', 'on'); + log_message(handles, 'Switched to SpikeGLX recording mode.'); + end + + % ========================================================================= + % WORKFLOW ACTIONS + % ========================================================================= + case 'load_sequence' + handles = value(ui_handles); + log_message(handles, '--- LOAD sequence initiated ---'); + set(handles.control_button, 'Enable', 'off', 'String', 'Loading...'); + set(handles.sample_button, 'Enable', 'off'); + + try + software = value(recording_software); + params = get_all_parameters(handles,software); + + % --- ADDED: Confirmation for overwriting existing session data --- + if ~strcmp(params.session_id, 'New') + % Construct path to check for existing data + subject_name_part = sprintf('sub-%s_id-%s_expmtr-%s', params.subject_id, params.rat_name, params.experimenter); + session_name_part = sprintf('ses-%s', params.session_id); % Assumes session_id is formatted '01', '02' etc. + base_path_part = fullfile(params.project_name, 'rawdata', subject_name_part, session_name_part); + + local_behav_path = fullfile(params.local_path, base_path_part, 'behav'); + local_ephys_path = fullfile(params.local_path, base_path_part, 'ephys'); + central_behav_path = fullfile(params.central_path, base_path_part, 'behav'); + central_ephys_path = fullfile(params.central_path, base_path_part, 'ephys'); + + data_exists = does_dir_have_data(local_behav_path) || does_dir_have_data(local_ephys_path) || ... + does_dir_have_data(central_behav_path) || does_dir_have_data(central_ephys_path); + + if data_exists + question = sprintf('Session %s for this subject already contains data. Continuing may result in data being overwritten. Are you sure you want to proceed?', params.session_id); + answer = questdlg(question, 'Confirm Overwrite', 'Yes', 'No', 'No'); + if strcmp(answer, 'No') + log_message(handles, 'Load sequence aborted by user to prevent overwriting data.'); + feval(mfilename, obj, 'reset_to_load_state'); + return; % Abort the load sequence + end + end + end + % --- END ADDED SECTION --- + + if ~validate_all_inputs(params,handles,software) + feval(mfilename, obj, 'reset_to_load_state'); + return; + end + current_params.value = params; + + [session_path, ~] = construct_session_paths(handles, params); + if isempty(session_path) || ~create_session_directories(handles, params, session_path) + feval(mfilename, obj, 'reset_to_load_state'); + return; + end + session_base_path.value = session_path; + + % Initialize the Behavior system + feval(mfilename, obj, 'initialize_behavior_system'); + + catch ME + log_message(handles, sprintf('ERROR during load sequence: %s', ME.message)); + feval(mfilename, obj, 'reset_to_load_state'); + rethrow(ME); + end + case 'run_sequence' + handles = value(ui_handles); + params = value(current_params); + + % --- ADDED: Pre-run checklist dialog --- + software = value(recording_software); + if strcmp(software, 'OpenEphys') + title = 'Open Ephys Pre-Run Checklist'; + message = { + 'Before continuing, please ensure the following in Open Ephys:', ... + '', ... + '1. Refresh the Neuropixel PXI module to ensure probes are detected.', ... + '2. Set the probe geometry (Bank, Reference, or select the IMRO file).', ... + '3. Configure the main sync slot and SMA settings.', ... + '', ... + 'NOTE: The file save directory and name will be set automatically by this program.' ... + }; + else % SpikeGLX + title = 'SpikeGLX Pre-Run Checklist'; + message = { + 'Before continuing, please ensure the following in SpikeGLX:', ... + '', ... + '1. Press ''Detect'' to find the connected probes.', ... + '2. In Sync settings, set ''Square Wave Source'' to ''Disable sync waveform''.', ... + '3. Set the inputs for the ''imec PXI SMA slot''.', ... + '4. Select the correct IMRO file for the animal within SpikeGLX.',... + '', ... + 'NOTE: The run name and directory will be set automatically by this program.'... + }; + end + + answer = questdlg(message, title, 'Continue', 'Cancel', 'Continue'); + + if ~strcmp(answer, 'Continue') + log_message(handles, 'Run sequence cancelled by user at pre-run checklist.'); + return; % Abort the run sequence + end + % --- END ADDED SECTION --- + + log_message(handles, '--- RUN sequence initiated ---'); + set(handles.sample_button, 'Enable', 'off'); + + try + if get(handles.cb_ephys, 'Value') + % Initialize the selected recording system + recording_save_path = fullfile(params.local_path, value(session_base_path), 'ephys'); + feval(mfilename, obj, 'initialize_recording_system', params, recording_save_path); + if isempty(value(recording_controller)) + feval(mfilename, obj, 'reset_to_load_state'); + return; + end + % Start Ephys Recording + feval(mfilename, obj, 'start_electrophysiology_recording', params); + else + log_message(handles, 'Ephys checkbox not selected. No recording started.'); + end + + currentState.value = 'Stop'; + set(handles.control_button, 'String', 'Stop'); + feval(mfilename, obj, 'start_blinking'); + + feval(mfilename, obj, 'start_behavioral_protocol', params); + log_message(handles, '--- RUN sequence complete. Experiment is live. ---'); + + catch ME + log_message(handles, sprintf('ERROR during run sequence: %s', ME.message)); + feval(mfilename, obj, 'stop_blinking'); + rethrow(ME); + end + case 'stop_sequence' + handles = value(ui_handles); + params = value(current_params); + log_message(handles, '--- STOP sequence initiated ---'); + feval(mfilename, obj, 'stop_blinking'); + + try + behav_save_dir = fullfile(params.local_path, value(session_base_path), 'behav'); + feval(mfilename, obj, 'stop_behavioral_protocol', params, behav_save_dir); + + if get(handles.cb_ephys, 'Value') && ~isempty(value(recording_controller)) + feval(mfilename, obj, 'stop_electrophysiology_recording'); + end + + log_message(handles, '--- Experiment finished. Post-session sampling available. ---'); + currentState.value = 'PostExperiment'; + set(handles.control_button, 'String', 'Start New Experiment', 'BackgroundColor', [0.2, 0.8, 0.6]); + set(handles.sample_button, 'Enable', 'on'); + + catch ME + log_message(handles, sprintf('ERROR during stop sequence: %s', ME.message)); + rethrow(ME); + end + % ========================================================================= + % SYSTEM INITIALIZATION & CONTROL + % ========================================================================= + case 'initialize_recording_system' + params = varargin{1}; + save_path = varargin{2}; + software = value(recording_software); + handles = value(ui_handles); + + try + if strcmp(software, 'OpenEphys') + log_message(handles, 'Initializing Open Ephys controller...'); + controller = OpenEphysHTTPServer(params.oe_gui_ip, 37497); + if isempty(controller), error('Failed to create Open Ephys controller'); end + else % SpikeGLX + log_message(handles, 'Initializing SpikeGLX controller...'); + controller = SpikeGL(params.sglx_host_ip, params.sglx_port); + % if ~controller.IsConnected(), error('Failed to connect to SpikeGLX'); end + end + recording_controller.value = controller; + log_message(handles, sprintf('%s controller initialized successfully.', software)); + + % Set initial recording path + feval(mfilename, obj, 'set_recording_path', save_path); + + catch ME + log_message(handles, sprintf('Failed to initialize %s: %s', software, ME.message)); + recording_controller.value = []; + rethrow(ME); + end + + case 'set_recording_path' + save_path = varargin{1}; + software = value(recording_software); + controller = value(recording_controller); + params = value(current_params); + handles = value(ui_handles); + if isempty(controller), return; end + + try + if strcmp(software, 'OpenEphys') + controller.setRecordPath(params.oe_rec_node_id, save_path); + else % SpikeGLX + controller = SetDataDir( controller, 0, save_path); + recording_controller.value = controller; + end + log_message(handles, sprintf('Recording path set to: %s', save_path)); + catch ME + log_message(handles, sprintf('Failed to set recording path: %s', ME.message)); + rethrow(ME); + end + case 'start_electrophysiology_recording' + params = varargin{1}; + software = value(recording_software); + controller = value(recording_controller); + handles = value(ui_handles); + if isempty(controller), error('Recording controller not initialized'); end + + try + probe_settings_struct = value(probe_settings); + feval(mfilename, obj, 'apply_probe_configuration', probe_settings_struct); + + main_ephys_path = fullfile(params.local_path, value(session_base_path), 'ephys'); + feval(mfilename, obj, 'set_recording_path', main_ephys_path); + + if strcmp(software, 'OpenEphys') + log_message(handles, 'Starting Open Ephys acquisition and recording...'); + controller.acquire(); pause(1); + controller.record(); + else % SpikeGLX + log_message(handles, 'Starting SpikeGLX recording...'); + run_name = sprintf('experiment_%s', datestr(now, 'yyyymmdd_HHMMSS')); + boolval = IsInitialized( controller); + if boolval + spikeglx_params = GetParams( controller ); + controller = SetRunName(controller,run_name); % setting run name + controller = StartRun(controller); % starting acquisition + pause(2); + + runningval = false; + while ~runningval % waiting for acquisition to start + runningval = IsRunning( controller ); + if runningval + controller = SetRecordingEnable( controller, 1 ); % Start Recording + end + end + recording_controller.value = controller; + end + end + + log_message(handles, 'Electrophysiology recording is LIVE.'); + ephysState.value = 'Stop'; + set(handles.ephys_button, 'String', 'Stop Ephys', 'BackgroundColor', [1 0.6 0.6]); + + catch ME + log_message(handles, sprintf('Failed to start recording: %s', ME.message)); + rethrow(ME); + end + case 'stop_electrophysiology_recording' + software = value(recording_software); + controller = value(recording_controller); + handles = value(ui_handles); + if isempty(controller), return; end + + try + if strcmp(software, 'OpenEphys') + log_message(handles, 'Stopping Open Ephys recording...'); + controller.idle(); + else % SpikeGLX + log_message(handles, 'Stopping SpikeGLX recording...'); + boolval = IsSaving( controller ); + if boolval + controller = SetRecordingEnable( controller, 0 ); + pause(1); % Brief pause to ensure recording stops + controller = StopRun(controller); + else + controller = StopRun(controller); + end + recording_controller.value = controller; + end + + log_message(handles, 'Electrophysiology recording stopped.'); + ephysState.value = 'Run'; + set(handles.ephys_button, 'String', 'Run Ephys', 'BackgroundColor', [0.8, 0.6, 1]); + + catch ME + log_message(handles, sprintf('Failed to stop recording: %s', ME.message)); + rethrow(ME); + end + case 'initialize_behavior_system' + params = value(current_params); + handles = value(ui_handles); + try + log_message(handles, 'Initializing behavior control system...'); + behav_obj.value = dispatcher('init'); + h=get_sphandle('owner','dispatcher','name','myfig'); + set(value(h{1}), 'Visible','Off'); + if params.do_manual_test + feval(mfilename, obj, 'behav_control', 'manual_test'); + else + feval(mfilename, obj, 'continue_load_after_manual_test'); + end + catch ME + log_message(handles, ['FATAL ERROR initializing behavior system: ' ME.message]); + errordlg(['Failed to initialize behavior system. Check path and logs. Error: ' ME.message], 'Behavior System Error'); + rethrow(ME); + end + + case 'start_behavioral_protocol' + params = varargin{1}; + handles = value(ui_handles); + try + log_message(handles, 'Starting behavioral protocol...'); + feval(mfilename, obj, 'behav_control', 'run', params.protocol_name); + log_message(handles, 'Behavioral protocol is LIVE.'); + catch ME + log_message(handles, ['FATAL ERROR starting behavior protocol: ' ME.message]); + errordlg(['Failed to start behavior protocol. Check logs. Error: ' ME.message], 'Behavior System Error'); + rethrow(ME); + end + case 'stop_behavioral_protocol' + params = varargin{1}; + behav_save_dir = varargin{2}; + handles = value(ui_handles); + try + log_message(handles, 'Ending behavioral session (saving data)...'); + feval(mfilename, obj, 'behav_control', 'end', params.protocol_name, params.behav_path, behav_save_dir); + log_message(handles, 'Behavioral data saved successfully.'); + catch ME + log_message(handles, ['FATAL ERROR ending behavioral session: ' ME.message]); + errordlg(['Failed to save behavioral data. Check logs. Error: ' ME.message], 'Behavior System Error'); + rethrow(ME); + end + + case 'manual_test_stopping' + handles = value(ui_handles); + log_message(handles, 'Manual rig test complete. Cleaning up...'); + dispatcher(value(behav_obj), 'Stop'); + %Let's pause until we know dispatcher is done running + set(value(stopping_complete_timer), 'TimerFcn', {@(h,e) feval(mfilename, obj, 'manual_test_stopped')}); + start(value(stopping_complete_timer)); + case 'manual_test_stopped' + if value(stopping_process_completed) %This is provided by RunningSection + stop(value(stopping_complete_timer)); %Stop looping. + dispatcher('set_protocol', ''); + is_running.value = 0; + feval(mfilename, obj, 'continue_load_after_manual_test'); + end + + case 'continue_load_after_manual_test' + params = value(current_params); + handles = value(ui_handles); + video_save_dir = fullfile(params.local_path, value(session_base_path), 'behav'); + try + log_message(handles, 'Loading main behavioral protocol...'); + feval(mfilename, obj, 'behav_control', 'load_main_protocol', params.experimenter, params.rat_name, params.protocol_name, video_save_dir, params.behav_path,params.stim_distribution); + log_message(handles, 'Behavior system loaded and ready.'); + log_message(handles, '--- LOAD sequence complete. Ready to run. ---'); + currentState.value = 'Run'; + set(handles.control_button, 'Enable', 'on', 'String', 'Run', 'BackgroundColor', [0.4, 0.8, 0.4]); + set(handles.sample_button, 'Enable', 'on'); + + % --- ADDED: Confirmation dialog after successful load --- + msgbox('Behavior settings loaded successfully. The system is now ready to run.', 'Load Complete', 'help'); + % --- END ADDED SECTION --- + + catch ME + log_message(handles, ['FATAL ERROR loading main protocol: ' ME.message]); + errordlg(['Failed to load main protocol. Error: ' ME.message], 'Behavior System Error'); + feval(mfilename, obj, 'reset_to_load_state'); + end + + % ========================================================================= + % INDIVIDUAL CONTROL CALLBACKS + % ========================================================================= + case 'behav_control_callback' + switch value(behavState) + case 'Run', feval(mfilename, obj, 'behav_control', 'load_run'); + case 'Stop', feval(mfilename, obj, 'behav_control', 'end'); + end + + case 'ephys_control_callback' + switch value(ephysState) + case 'Run', feval(mfilename, obj, 'run_ephys_individually'); + case 'Stop', feval(mfilename, obj, 'stop_ephys_individually'); + end + + case 'run_ephys_individually' + params = value(current_params); + handles = value(ui_handles); + log_message(handles, '--- Starting Ephys Recording Individually ---'); + set(handles.ephys_button, 'Enable', 'off'); + try + if isempty(value(recording_controller)) + feval(mfilename, obj, 'initialize_recording_system', params, ''); + end + feval(mfilename, obj, 'start_electrophysiology_recording', params); + catch ME + log_message(handles, sprintf('ERROR starting ephys: %s', ME.message)); + end + set(handles.ephys_button, 'Enable', 'on'); + + case 'stop_ephys_individually' + handles = value(ui_handles); + log_message(handles, '--- Stopping Ephys Recording Individually ---'); + set(handles.ephys_button, 'Enable', 'off'); + try + feval(mfilename, obj, 'stop_electrophysiology_recording'); + catch ME + log_message(handles, sprintf('ERROR stopping ephys: %s', ME.message)); + end + set(handles.ephys_button, 'Enable', 'on'); + % ========================================================================= + % BEHAVIOR CONTROL ACTIONS + % ========================================================================= + case 'behav_control' + sub_action = varargin{1}; + args = varargin(2:end); + handles = value(ui_handles); + + switch sub_action + case 'load_main_protocol' + experimenter = args{1}; ratname = args{2}; protocol_name = args{3}; + video_save_dir = args{4}; behav_path = args{5}; stim_distribution = args{6}; + log_message(handles, ['Loading protocol: ' protocol_name]); + dispatcher('set_protocol', protocol_name); + rath = get_sphandle('name', 'ratname', 'owner', protocol_name); + exph = get_sphandle('name', 'experimenter', 'owner', protocol_name); + rath{1}.value = ratname; exph{1}.value = experimenter; + protobj = eval(protocol_name); + log_message(handles, ['Loading settings for ' ratname]); + [~, sfile] = load_solouiparamvalues(ratname, 'experimenter', experimenter, 'owner', class(protobj), 'interactive', 0); + feval(protocol_name, protobj, 'set_setting_params', ratname, experimenter, sfile, char(datetime('now')), video_save_dir); + feval(protocol_name, protobj, 'set_stim_distribution',stim_distribution); + if ~dispatcher('is_running'), pop_history(class(protobj), 'include_non_gui', 1); feval(protocol_name, protobj, 'prepare_next_trial'); end + + case 'crashed' + log_message(handles, '--- BEHAVIOR CRASH RECOVERY INITIATED ---'); + params = value(current_params); + video_save_dir = fullfile(params.local_path, value(session_base_path), 'behav'); + feval(mfilename, obj, 'behav_control', 'load_protocol_after_crash', params.experimenter, params.rat_name, params.protocol_name, video_save_dir, params.behav_path); + feval(mfilename, obj, 'behav_control', 'run', params.protocol_name); + log_message(handles, '--- RECOVERY COMPLETE: Behavior protocol restarted ---'); + + case 'load_protocol_after_crash' + experimenter = args{1}; ratname = args{2}; protocol_name = args{3}; + video_save_dir = args{4}; behav_path = args{5}; + log_message(handles, ['Loading protocol after crash: ' protocol_name]); + dispatcher('set_protocol', protocol_name); + rath = get_sphandle('name', 'ratname', 'owner', protocol_name); + exph = get_sphandle('name', 'experimenter', 'owner', protocol_name); + rath{1}.value = ratname; exph{1}.value = experimenter; + protobj = eval(protocol_name); + try + log_message(handles, ['Loading previous data for ' ratname]); + today_date = char(datetime('now','format','yyMMdd')); + temp_data_dir = fullfile(behav_path,'SoloData','Data',experimenter,ratname); + temp_data_file = sprintf('data_@%s_%s_%s_%s_ASV.mat',protocol_name,experimenter,ratname,today_date); + if isfile(fullfile(temp_data_dir,temp_data_file)) + dispatcher('runstart_disable'); + load_soloparamvalues(ratname, 'experimenter', experimenter, 'owner', protocol_name, 'interactive', 0,'data_file',fullfile(temp_data_dir,temp_data_file)); + dispatcher('runstart_enable'); + end + feval(protocol_name, protobj, 'psychometricUpdate_aftercrash'); % update parameters for psychometric plots which were not saved so cant be loaded + if ~dispatcher('is_running'), pop_history(class(protobj), 'include_non_gui', 1); feval(protocol_name, protobj, 'prepare_next_trial'); end + catch + log_message(handles, ['Loading settings for ' ratname]); + [~, sfile] = load_solouiparamvalues(ratname, 'experimenter', experimenter, 'owner', class(protobj), 'interactive', 0); + feval(protocol_name, protobj, 'set_setting_params', ratname, experimenter, sfile, char(datetime('now')), video_save_dir); + if ~dispatcher('is_running'), pop_history(class(protobj), 'include_non_gui', 1); feval(protocol_name, protobj, 'prepare_next_trial'); end + end + case 'load_run' + set(handles.behav_button, 'Enable', 'off'); + log_message(handles, '--- STARTING BEHAV PROTOCOL ---'); + params = value(current_params); + video_save_dir = fullfile(params.local_path, value(session_base_path), 'behav'); + feval(mfilename, obj, 'behav_control', 'load_protocol_after_crash', params.experimenter, params.rat_name, params.protocol_name, video_save_dir, params.behav_path); + feval(mfilename, obj, 'behav_control', 'run', params.protocol_name); + log_message(handles, '--- START COMPLETE: Behavior protocol started ---'); + set(handles.behav_button, 'Enable', 'on'); + case 'run' + protocol_name = args{1}; protobj = eval(protocol_name); + log_message(handles, 'Starting video recording via protocol...'); + feval(protocol_name, protobj, 'start_recording'); + log_message(handles, 'Starting dispatcher to run trials...'); + is_running.value = 1; + behavState.value = 'Stop'; + set(handles.behav_button, 'String', 'Stop Behav', 'BackgroundColor', [1 0.6 0.6]); + dispatcher(value(behav_obj), 'Run'); + + case 'end' + set(handles.behav_button, 'Enable', 'off'); + if length(args) >= 3 + protocol_name = args{1}; root_dir = args{2}; behav_copy_dir = args{3}; + else + params = value(current_params); + protocol_name = params.protocol_name; + root_dir = params.behav_path; + behav_copy_dir = fullfile(params.local_path, value(session_base_path), 'behav'); + end + log_message(handles, 'Stopping dispatcher...'); + dispatcher(value(behav_obj), 'Stop'); + set(value(stopping_complete_timer), 'Period', 0.8,'TimerFcn', {@(h,e) feval(mfilename, obj, 'behav_control','end_continued',protocol_name, root_dir, behav_copy_dir)}); + start(value(stopping_complete_timer)); + case 'end_continued' + if value(stopping_process_completed) % This is provided by RunningSection + protocol_name = args{1}; root_dir = args{2}; destination_path = args{3}; + stop(value(stopping_complete_timer)); %Stop looping. + is_running.value = 0; + feval(mfilename, obj, 'behav_control', 'send_empty_state_machine'); + protobj = eval(protocol_name); + log_message(handles, 'Ending session via protocol...'); + feval(protocol_name, protobj, 'end_session'); + log_message(handles, 'Saving data and settings...'); + data_file = SavingSection(protobj, 'savedata', 'interactive', 0); + try + feval(protocol_name, protobj, 'pre_saving_settings'); + catch + log_message(handles, 'Protocol does not have a pre_saving_settings section.'); + end + [settings_file, ~] = SavingSection(protobj, 'get_set_filename'); + SavingSection(protobj, 'savesets', 'interactive', 0); + log_message(handles, 'Committing data and settings to SVN...'); + commit_to_svn(handles, data_file, settings_file, root_dir); + dispatcher('set_protocol', ''); + data_file = [data_file '.mat']; + [status, msg] = copyfile(data_file, destination_path); + if status, log_message(handles,'Data File copied successfully.'); + else, log_message(handles,['Error copying Data file: ' msg]); + end + behavState.value = 'Run'; + set(handles.behav_button, 'String', 'Run Behav', 'BackgroundColor', [1, 0.8, 0.6]); + feval(mfilename, obj, 'save_log_file'); + set(handles.behav_button, 'Enable', 'on'); + end + case 'manual_test' + log_message(handles, 'Loading manual rig test protocol...'); + dispatcher('set_protocol', 'Rigtest_singletrial'); + h=get_sphandle('owner','Rigtest_singletrial','name', 'myfig'); + for i=1:numel(h); set(value(h{i}),'Visible','Off'); end + is_running.value = 1; + log_message(handles, 'Starting manual rig test. Please complete the one-trial test.'); + dispatcher(value(behav_obj), 'Run'); + case 'create_svn_data_dir' + experimenter = args{1}; ratname = args{2}; behav_dir = args{3}; dir_name = args{4}; + dirCurrent = cd; + settings_path = fullfile(behav_dir, 'SoloData', dir_name); + exp_path = fullfile(settings_path, experimenter); + rat_path = fullfile(exp_path, ratname); + if ~isfolder(settings_path), mkdir(settings_path); system(['svn add ' dir_name]); end + if ~isfolder(exp_path), cd(settings_path); mkdir(experimenter); system(['svn add ' experimenter]); end + if ~isfolder(rat_path), cd(exp_path); mkdir(ratname); system(['svn add ' ratname]); end + cd(dirCurrent); + log_message(handles, ['Created SVN directory structure for ' ratname]); + case 'send_empty_state_machine' + state_machine_server = bSettings('get', 'RIGS', 'state_machine_server'); + server_slot = bSettings('get', 'RIGS', 'server_slot'); if isnan(server_slot), server_slot = 0; end + card_slot = bSettings('get', 'RIGS', 'card_slot'); if isnan(card_slot), card_slot = 0; end + sm = BpodSM(state_machine_server, 3333, server_slot); sm = Initialize(sm); + [inL, outL] = MachinesSection(dispatcher, 'determine_io_maps'); + sma = StateMachineAssembler('full_trial_structure'); + sma = add_state(sma, 'name', 'vapid_state_in_vapid_matrix'); + send(sma, sm, 'run_trial_asap', 0, 'input_lines', inL, 'dout_lines', outL, 'sound_card_slot', int2str(card_slot)); + + end + + case 'crash_detected' + % handles = feval(mfilename, obj, 'get_ui_handles'); + handles = value(ui_handles); + if ~strcmp(value(currentState), 'Stop') || isempty(value(behav_obj)), return; end + log_message(handles, '!!! CRASH DETECTED: Behavior system is not running. Attempting recovery...'); + try + feval(mfilename, obj, 'behav_control', 'crashed'); + catch ME + log_message(handles, sprintf('FATAL: Recovery attempt failed: %s', ME.message)); + getReport(ME, 'extended', 'hyperlinks', 'on'); + errordlg('Automatic recovery failed. Please stop the experiment manually.', 'Recovery Failed'); + end + % ========================================================================= + % SAMPLE EPHYS RECORDINGS + % ========================================================================= + case 'sample_recording_wrapper' + if strcmp(value(currentState), 'PostExperiment') + feval(mfilename, obj, 'sample_recording', 'post_session'); + else + feval(mfilename, obj, 'sample_recording', 'pre_session'); + end + + case 'sample_recording' + prefix = varargin{1}; + handles = value(ui_handles); + log_message(handles, ['--- ' upper(prefix) ' SAMPLE RECORDING INITIATED ---']); + set([handles.sample_button, handles.control_button], 'Enable', 'off', 'String', 'Sampling...'); + drawnow; + + try + software = value(recording_software); + params = get_all_parameters(handles,software); + if isempty(value(recording_controller)) + feval(mfilename, obj, 'initialize_recording_system', params, ''); + end + + if isempty(value(session_base_path)) + [session_path, ~] = construct_session_paths(handles, params); + if isempty(session_path) || ~create_session_directories(handles, params, session_path) + error('Failed to create session directories'); + end + session_base_path.value = session_path; + end + + sample_dir_name = sprintf('%s_sample_recording', prefix); + sample_save_path = fullfile(params.local_path, value(session_base_path), 'ephys', sample_dir_name); + if ~exist(sample_save_path, 'dir'), mkdir(sample_save_path); end + + software = value(recording_software); + if strcmp(software, 'OpenEphys') + feval(mfilename, obj, 'execute_openephys_sampling', sample_save_path); + else + feval(mfilename, obj, 'execute_spikeglx_sampling', sample_save_path); + end + log_message(handles, '--- SAMPLE RECORDING COMPLETE ---'); + + catch ME + log_message(handles, sprintf('ERROR during sample recording: %s', ME.message)); + rethrow(ME); + end + + % Reset button states + if strcmp(value(currentState), 'PostExperiment') + set(handles.control_button, 'Enable', 'on', 'String', 'Start New Experiment'); + feval(mfilename, obj, 'save_log_file'); + else + set(handles.control_button, 'Enable', 'on', 'String', 'Load'); + end + set(handles.sample_button, 'Enable', 'on', 'String', 'Start Sample Recording'); + + case 'execute_openephys_sampling' + save_path = varargin{1}; + controller = value(recording_controller); + params = value(current_params); + handles = value(ui_handles); + probe_settings_struct = value(probe_settings); + duration = str2double(get(handles.sample_duration, 'String')); + + controller.setParameters(params.oe_proc_node_id, 0, 'Reference', probe_settings_struct.reference); + if strcmp(probe_settings_struct.version, '1.0'), num_banks = 3; else, num_banks = 4; end + + for bank = 0:(num_banks - 1) + log_message(handles, sprintf('Recording OE Bank %d for %d seconds...', bank, duration)); + controller.setParameters(params.oe_proc_node_id, 0, 'bank', bank); pause(1); + controller.setRecordPath(params.oe_rec_node_id, save_path); pause(1); + controller.acquire(duration); pause(1); + controller.record(duration); + controller.idle(); + log_message(handles, sprintf('Finished recording Bank %d.', bank)); pause(1); + end + + if ~isempty(probe_settings_struct.imro_path) + controller.config(params.oe_proc_node_id, ['LOADIMRO ' probe_settings_struct.imro_path]); + else + controller.setParameters(params.oe_proc_node_id, 0, 'bank', probe_settings_struct.bank); + end + + case 'execute_spikeglx_sampling' + save_path = varargin{1}; + controller = value(recording_controller); + handles = value(ui_handles); + probe_settings_struct = value(probe_settings); + duration = str2double(get(handles.sample_duration, 'String')); + + controller.SetDataDir(save_path); + if strcmp(probe_settings_struct.version, '1.0'), num_banks = 3; else, num_banks = 4; end + + for bank = 0:(num_banks - 1) + log_message(handles, sprintf('Recording SGLX Bank %d for %d seconds...', bank, duration)); + % Bank selection for SpikeGLX depends on specific API calls for channel selection, not a simple 'bank' parameter + % This is a placeholder for more complex channel/bank setting logic. + % For now, we record with the currently active map. + + run_name = sprintf('sample_bank_%d_%s', bank, datestr(now, 'yyyymmdd_HHMMSS')); + controller.SetRunName(run_name); + controller.StartRun(); + pause(duration); + controller.StopRun(); + log_message(handles, sprintf('Finished recording Bank %d.', bank)); pause(1); + end + + % ========================================================================= + % PROBE GUI AND SETTINGS + % ========================================================================= + case 'open_probe_gui' + handles = value(ui_handles); + log_message(handles, 'Opening probe settings GUI...'); + probe_fig = figure('Name', 'Neuropixel Probe Settings', 'Position', [300 300 450 350], ... + 'MenuBar', 'none', 'ToolBar', 'none', 'NumberTitle', 'off', 'Resize', 'off'); + p_handles = struct(); + + p_handles.version_group = uibuttongroup(probe_fig, 'Title', 'Probe Version', 'Position', [0.05 0.78 0.9 0.2]); + uicontrol(p_handles.version_group, 'Style', 'radiobutton', 'String', 'NP 1.0 (3 Banks)', 'Position', [10 5 150 25], 'Tag', '1.0'); + uicontrol(p_handles.version_group, 'Style', 'radiobutton', 'String', 'NP 2.0 (4 Banks)', 'Position', [200 5 150 25], 'Tag', '2.0'); + + p_handles.ref_group = uibuttongroup(probe_fig, 'Title', 'Reference', 'Position', [0.05 0.55 0.4 0.2]); + uicontrol(p_handles.ref_group, 'Style', 'radiobutton', 'String', 'Tip', 'Position', [10 5 80 25], 'Tag', 'Tip'); + uicontrol(p_handles.ref_group, 'Style', 'radiobutton', 'String', 'External', 'Position', [100 5 80 25], 'Tag', 'External'); + + p_handles.bank_panel = uipanel(probe_fig, 'Title', 'Target Bank', 'Position', [0.5 0.55 0.45 0.2]); + uicontrol(p_handles.bank_panel, 'Style', 'text', 'String', 'Bank:', 'Position', [10 5 40 20]); + p_handles.bank_edit = uicontrol(p_handles.bank_panel, 'Style', 'edit', 'String', '0', 'Position', [60 5 50 25]); + + uicontrol(probe_fig, 'Style', 'text', 'String', 'Sync IMEC Slot:', 'Position', [20 160 100 20], 'HorizontalAlignment', 'right'); + p_handles.sync_slot_edit = uicontrol(probe_fig, 'Style', 'edit', 'String', '2', 'Position', [130 160 50 25]); + + uicontrol(probe_fig, 'Style', 'text', 'String', 'IMRO File:', 'Position', [20 120 100 20], 'HorizontalAlignment', 'right'); + p_handles.imro_text = uicontrol(probe_fig, 'Style', 'text', 'String', 'None selected', 'Position', [130 120 280 20], 'HorizontalAlignment', 'left'); + + uicontrol(probe_fig, 'Style', 'pushbutton', 'String', 'Browse...', 'Position', [130 85 100 30], 'Callback', {@(h,e) feval(mfilename, obj, 'browse_imro', p_handles)}); + uicontrol(probe_fig, 'Style', 'pushbutton', 'String', 'Clear IMRO', 'Position', [240 85 100 30], 'Callback', {@(h,e) feval(mfilename, obj, 'clear_imro', p_handles)}); + + uicontrol(probe_fig, 'Style', 'pushbutton', 'String', 'Apply & Close', 'Position', [250 25 180 30], 'FontWeight', 'bold', 'Callback', {@(h,e) feval(mfilename, obj, 'apply_probe_settings', p_handles)}); + probe_gui_handles.value = p_handles; + + case 'browse_imro' + p_handles = varargin{1}; + [file, path] = uigetfile('*.imro', 'Select IMRO File'); + if isequal(file, 0) || isequal(path, 0), return; + else + full_path = fullfile(path, file); + set(p_handles.imro_text, 'String', full_path); + set(findobj(p_handles.bank_panel, '-property', 'Enable'), 'Enable', 'off'); + end + + case 'clear_imro' + p_handles = varargin{1}; + set(p_handles.imro_text, 'String', 'None selected'); + set(findobj(p_handles.bank_panel, '-property', 'Enable'), 'Enable', 'on'); + + case 'apply_probe_settings' + p_handles = varargin{1}; + handles = value(ui_handles); + settings.version = get(get(p_handles.version_group, 'SelectedObject'), 'Tag'); + settings.reference = get(get(p_handles.ref_group, 'SelectedObject'), 'Tag'); + settings.bank = str2double(get(p_handles.bank_edit, 'String')); + settings.imro_path = get(p_handles.imro_text, 'String'); + settings.sync_slot = str2double(get(p_handles.sync_slot_edit, 'String')); + if strcmp(settings.imro_path, 'None selected'), settings.imro_path = ''; end + probe_settings.value = settings; + if ~isempty(settings.imro_path) + set(handles.target_display, 'String', 'Target: IMRO File'); + else + set(handles.target_display, 'String', ['Target: Bank ' num2str(settings.bank)]); + end + log_message(handles, 'Probe settings saved.'); + close(p_handles.ref_group.Parent); + probe_gui_handles.value = []; + + case 'apply_probe_configuration' + probe_settings = varargin{1}; + software = value(recording_software); + controller = value(recording_controller); + params = value(current_params); + handles = value(ui_handles); + if isempty(controller), return; end + + try + if strcmp(software, 'OpenEphys') + log_message(handles, sprintf('Setting OE reference to: %s', probe_settings.reference)); + controller.setParameters(params.oe_proc_node_id, 0, 'Reference', probe_settings.reference); + % Applying Sync Slot setting + log_message(handles, sprintf('Applying Sync IMEC Slot: %d', probe_settings.sync_slot)); + % NOTE: This assumes a parameter like 'syncSlot' exists in the Neuropix-PXI plugin. + % The actual parameter name might need to be verified in the Open Ephys plugin. + % controller.setParameters(params.oe_proc_node_id, 0, 'syncSlot', probe_settings.sync_slot); + if ~isempty(probe_settings.imro_path) + log_message(handles, sprintf('Loading IMRO file: %s', probe_settings.imro_path)); + controller.config(params.oe_proc_node_id, ['LOADIMRO ' probe_settings.imro_path]); + else + log_message(handles, sprintf('Setting bank to: %d', probe_settings.bank)); + controller.setParameters(params.oe_proc_node_id, 0, 'bank', probe_settings.bank); + end + else % SpikeGLX + % SpikeGLX probe configuration (e.g., reference, channel map) is more complex + % and typically handled by setting parameters or loading a meta file. + % This is a placeholder for those more complex API calls. + log_message(handles, 'Applying SpikeGLX probe settings (via meta file or API)...'); + if ~isempty(probe_settings.imro_path) + log_message(handles, 'Note: For SpikeGLX, ensure IMRO settings are loaded within the SpikeGLX GUI and save as part of the meta file.'); + end + end + log_message(handles, 'Probe configuration applied successfully.'); + catch ME + log_message(handles, sprintf('Failed to apply probe settings: %s', ME.message)); + rethrow(ME); + end + + % ========================================================================= + % UTILITY & OTHER ACTIONS + % ========================================================================= + case 'browse_path' + type = varargin{1}; + handles = value(ui_handles); + log_message(handles, ['Opening browse dialog for ' type ' path...']); + folder_path = uigetdir; + if folder_path ~= 0 + if strcmp(type, 'local') + set(handles.local_edit, 'String', folder_path); + feval(mfilename, obj, 'populate_and_filter_lists', 'rescan'); + elseif strcmp(type, 'central') + set(handles.central_edit, 'String', folder_path); + feval(mfilename, obj, 'populate_and_filter_lists', 'rescan'); + elseif strcmp(type, 'behav') + set(handles.behav_edit, 'String', folder_path); + end + log_message(handles, [type ' path set.']); + else, log_message(handles, 'Path selection cancelled.'); end + + case 'populate_and_filter_lists' + trigger_type = varargin{1}; % 'rescan' or 'filter' + handles = value(ui_handles); + + % --- Step 1: Scan directories if needed --- + if strcmp(trigger_type, 'rescan') + log_message(handles, 'Scanning directories for session info...'); + local_path = get(handles.local_edit, 'String'); + central_path = get(handles.central_edit, 'String'); + project_name = get(handles.proj_edit, 'String'); + + all_info = struct('folder_name', {}, 'subject_id', {}, 'rat_name', {}, 'experimenter', {}); + if ~isempty(local_path) && exist(local_path, 'dir') + all_info = [all_info; scan_for_info(fullfile(local_path, project_name, 'rawdata'))]; + end + if ~isempty(central_path) && exist(central_path, 'dir') + all_info = [all_info; scan_for_info(fullfile(central_path, project_name, 'rawdata'))]; + end + + if ~isempty(all_info) + keys = cell(numel(all_info), 1); + for i = 1:numel(all_info) + keys{i} = sprintf('%s|%s|%s', all_info(i).subject_id, all_info(i).rat_name, all_info(i).experimenter); + end + [~, ia] = unique(keys, 'stable'); + session_info_list.value = all_info(ia); + log_message(handles, sprintf('Found %d unique subjects.', numel(value(session_info_list)))); + else + session_info_list.value = []; + log_message(handles, 'No valid subject folders found.'); + end + + % Initial population of just the experimenter list + exp_options = unique([{value(session_info_list).experimenter}]); + update_popup(handles.exp_popup, exp_options, '-'); + update_popup(handles.rat_name_popup, {}, '-'); + set(handles.sub_popup, 'String', {'-'}, 'Value', 1); + set(handles.session_popup, 'String', {'-'}, 'Value', 1, 'Enable', 'off'); + return; + end + + % --- Step 2: Filter Logic triggered by user interaction --- + full_list = value(session_info_list); + source_handle = gcbo; + + % Get current selections, handling 'Add New...' dialogs + [exp_selection, exp_updated] = get_and_handle_new(handles.exp_popup, 'New Experimenter'); + if exp_updated % New exp added, reset everything below it + update_popup(handles.rat_name_popup, {}, '-'); + set(handles.sub_popup, 'String', {'-'}, 'Value', 1); + set(handles.session_popup, 'String', {'-'}, 'Value', 1, 'Enable', 'off'); + return; + end + + if source_handle == handles.exp_popup + if ~strcmp(exp_selection, '-') + filtered_by_exp = full_list(strcmpi({full_list.experimenter}, exp_selection)); + rat_options = unique([{filtered_by_exp.rat_name}]); + update_popup(handles.rat_name_popup, rat_options, '-'); + else + update_popup(handles.rat_name_popup, {}, '-'); + end + set(handles.sub_popup, 'String', {'-'}, 'Value', 1); + set(handles.session_popup, 'String', {'-'}, 'Value', 1, 'Enable', 'off'); + return; + end + + [rat_selection, rat_updated] = get_and_handle_new(handles.rat_name_popup, 'New Rat Name'); + + if source_handle == handles.rat_name_popup + if ~strcmp(rat_selection, '-') + subject_entry = full_list(strcmpi({full_list.experimenter}, exp_selection) & strcmpi({full_list.rat_name}, rat_selection)); + + if ~isempty(subject_entry) % Existing pair + sub_id = subject_entry(1).subject_id; + set(handles.sub_popup, 'String', {sub_id}, 'Value', 1); + elseif rat_updated % New Rat for this Experimenter + max_id = 0; + if ~isempty(full_list) + all_ids = str2double({full_list.subject_id}); + if ~all(isnan(all_ids)), max_id = max(all_ids(~isnan(all_ids))); end + end + sub_id = sprintf('%03d', max_id + 1); + set(handles.sub_popup, 'String', {sub_id}, 'Value', 1); + else + sub_id = '-'; + set(handles.sub_popup, 'String', {'-'}, 'Value', 1); + end + + % Populate Session list + if ~strcmp(sub_id, '-') + local_path = get(handles.local_edit, 'String'); + central_path = get(handles.central_edit, 'String'); + project_name = get(handles.proj_edit, 'String'); + subject_folder = sprintf('sub-%s_id-%s_expmtr-%s', sub_id, rat_selection, exp_selection); + session_numbers = []; + path1 = fullfile(local_path, project_name, 'rawdata', subject_folder); + if exist(path1, 'dir'), session_numbers = [session_numbers, find_session_numbers(path1)]; end + path2 = fullfile(central_path, project_name, 'rawdata', subject_folder); + if exist(path2, 'dir'), session_numbers = [session_numbers, find_session_numbers(path2)]; end + + unique_sessions = unique(session_numbers); + if ~isempty(unique_sessions) + sorted_sessions = sort(unique_sessions, 'descend'); + last_three = sorted_sessions(1:min(3, end)); + session_strings = arrayfun(@(x) sprintf('%02d', x), sort(last_three), 'UniformOutput', false); + set(handles.session_popup, 'String', [{'New'}, session_strings], 'Value', 1, 'Enable', 'on'); + else + set(handles.session_popup, 'String', {'New'}, 'Value', 1, 'Enable', 'on'); + end + else + set(handles.session_popup, 'String', {'-'}, 'Value', 1, 'Enable', 'off'); + end + else % rat set to '-' + set(handles.sub_popup, 'String', {'-'}, 'Value', 1); + set(handles.session_popup, 'String', {'-'}, 'Value', 1, 'Enable', 'off'); + end + end + + case 'save_log_file' + handles = value(ui_handles); + params = value(current_params); + session_path = value(session_base_path); + if isempty(session_path) + log_message(handles, 'WARNING: Cannot save log file. Session path not set.'); return; + end + log_path = fullfile(params.local_path, session_path, 'behav'); + if ~exist(log_path, 'dir') + log_message(handles, ['WARNING: Behavior folder not found. Cannot save log. Path: ' log_path]); return; + end + log_file_path = fullfile(log_path, 'session_log.txt'); + try + log_content = get(handles.log_box, 'String'); + fid = fopen(log_file_path, 'w'); + if fid == -1, error('Could not open file for writing.'); end + for i = 1:length(log_content), fprintf(fid, '%s\n', log_content{i}); end + fclose(fid); + log_message(handles, ['Log file saved successfully to: ' log_file_path]); + catch ME + log_message(handles, ['ERROR: Could not save log file. Details: ' ME.message]); + end + case 'reset_to_load_state' + handles = value(ui_handles); + currentState.value = 'Load'; + behavState.value = 'Run'; + ephysState.value = 'Run'; + set(handles.control_button, 'Enable', 'on', 'String', 'Load', 'BackgroundColor', [0.2, 0.6, 0.8]); + set(handles.sample_button, 'Enable', 'on'); + recording_controller.value = []; + behav_obj.value = []; + current_params.value = []; + session_base_path.value = ''; + log_message(handles, 'GUI reset to load state.'); + case 'close' + try + feval(mfilename, obj, 'stop_blinking'); + if ~isempty(value(recording_controller)), delete(value(recording_controller)); end + if ishandle(value(myfig)), delete(value(myfig)); end + delete_sphandle('owner', ['^@' mfilename '$']); + if ~isempty(value(behav_obj)), dispatcher(value(behav_obj),'close'); end + obj = []; + catch + if exist('myfig','var') == 1 + if ishandle(value(myfig)), delete(value(myfig)); end + else + delete(gcbf); + end + delete_sphandle('owner', ['^@' mfilename '$']); + obj = []; + end + case 'is_running' + if exist('is_running','var') == 1 + obj = logical(value(is_running)); + else + obj = 0; + end + + case 'start_blinking' + handles = value(ui_handles); + blinking_timer.value = timer('ExecutionMode', 'fixedRate', 'Period', 0.5, 'TimerFcn', {@toggle_button_color, handles.control_button}); + start(value(blinking_timer)); + case 'stop_blinking' + handles = value(ui_handles); + if ~isempty(value(blinking_timer)) && isvalid(value(blinking_timer)) + stop(value(blinking_timer)); + delete(value(blinking_timer)); + blinking_timer.value = []; + end + set(handles.control_button, 'BackgroundColor', [1, 0.4, 0.4]); + otherwise + error('Unknown action: %s', action); +end +return; +%% ======================================================================= +% PARAMETER AND VALIDATION FUNCTIONS +% ======================================================================= +function params = get_all_parameters(handles,software) + params.protocol_name = get(handles.protocol_edit, 'String'); + params.do_manual_test = get(handles.manual_test, 'Value'); + + % Get values from popup menus + exp_items = get(handles.exp_popup, 'String'); + params.experimenter = exp_items{get(handles.exp_popup, 'Value')}; + + rat_items = get(handles.rat_name_popup, 'String'); + params.rat_name = rat_items{get(handles.rat_name_popup, 'Value')}; + + sub_items = get(handles.sub_popup, 'String'); + params.subject_id = sub_items{get(handles.sub_popup, 'Value')}; + + ses_items = get(handles.session_popup, 'String'); + params.session_id = ses_items{get(handles.session_popup, 'Value')}; + + popup_string = get(handles.distribution_popup,'String'); + params.stim_distribution = popup_string{get(handles.distribution_popup,'Value')}; + params.behav_path = get(handles.behav_edit, 'String'); + params.project_name = get(handles.proj_edit, 'String'); + params.local_path = get(handles.local_edit, 'String'); + params.central_path = get(handles.central_edit, 'String'); + + if strcmp(software, 'OpenEphys') + params.oe_gui_ip = get(handles.oe_ip_edit, 'String'); + params.oe_proc_node_id = get(handles.oe_proc_edit, 'String'); + params.oe_rec_node_id = get(handles.oe_rec_edit, 'String'); + else + params.sglx_host_ip = get(handles.sglx_host_edit, 'String'); + params.sglx_port = str2double(get(handles.sglx_port_edit, 'String')); + params.sglx_probe_index = str2double(get(handles.sglx_probe_edit, 'String')); + end +function is_valid = validate_all_inputs(params,handles,software) + is_valid = false; + required_fields = {'protocol_name', 'rat_name', 'behav_path', 'project_name', 'subject_id', 'local_path', 'session_id'}; + for i = 1:length(required_fields) + field_val = params.(required_fields{i}); + if ~isfield(params, required_fields{i}) || isempty(field_val) || strcmp(field_val, '-') + msg = sprintf('Field "%s" must have a valid selection before loading.', strrep(required_fields{i}, '_', ' ')); + log_message(handles, sprintf('ERROR: %s', msg)); errordlg(msg, 'Input Error'); + return; + end + end + if ~get(handles.cb_ephys, 'Value') && ~get(handles.cb_behav, 'Value') && ~get(handles.cb_anat, 'Value') && ~get(handles.cb_funcimg, 'Value') + msg = 'At least one subfolder must be selected.'; + log_message(handles, sprintf('ERROR: %s', msg)); errordlg(msg, 'Input Error'); + return; + end + if strcmp(software, 'OpenEphys') + if isempty(params.oe_gui_ip) || isempty(params.oe_proc_node_id) || isempty(params.oe_rec_node_id) + msg = 'Open Ephys connection parameters cannot be empty.'; + log_message(handles, sprintf('ERROR: %s', msg)); errordlg(msg, 'Input Error'); + return; + end + else + if isempty(params.sglx_host_ip) || isnan(params.sglx_port) || isnan(params.sglx_probe_index) + msg = 'SpikeGLX connection parameters cannot be empty or non-numeric.'; + log_message(handles, sprintf('ERROR: %s', msg)); errordlg(msg, 'Input Error'); + return; + end + end + is_valid = true; +%% ======================================================================= +% PATH AND DIRECTORY FUNCTIONS +% ======================================================================= +function [session_base, recording_path] = construct_session_paths(handles, params) + if isempty(params.experimenter) || strcmp(params.experimenter, '-') + subject_name = sprintf('sub-%s_id-%s', params.subject_id, params.rat_name); + else + subject_name = sprintf('sub-%s_id-%s_expmtr-%s', params.subject_id, params.rat_name, params.experimenter); + end + subject_base_path = fullfile(params.project_name, 'rawdata', subject_name); + local_subject_dir = fullfile(params.local_path, subject_base_path); + central_subject_dir = fullfile(params.central_path, subject_base_path); + + if strcmp(params.session_id, 'New') + new_ses_num = max(find_max_session_number(local_subject_dir), find_max_session_number(central_subject_dir)) + 1; + log_message(handles, sprintf('Last session found: %d. Creating new session: %d.', new_ses_num - 1, new_ses_num)); + else + new_ses_num = str2double(params.session_id); + log_message(handles, sprintf('Using existing session number: %d.', new_ses_num)); + end + + session_datetime_str = char(datetime('now', 'Format', 'yyyyMMdd''T''HHmmss')); + session_folder_name = sprintf('ses-%02d_date-%s_dtype-ephys', new_ses_num, session_datetime_str); + session_base = fullfile(subject_base_path, session_folder_name); + recording_path = fullfile(params.local_path, session_base, 'ephys'); + log_message(handles, ['New session path determined: ' session_base]); +function max_ses = find_max_session_number(base_path) + max_ses = 0; if ~exist(base_path, 'dir'), return; end + dir_contents = dir(fullfile(base_path, 'ses-*')); + if isempty(dir_contents), return; end + session_numbers = []; + for i = 1:length(dir_contents) + if dir_contents(i).isdir + token = regexp(dir_contents(i).name, '^ses-(\d+)', 'tokens'); + if ~isempty(token), session_numbers(end+1) = str2double(token{1}{1}); end + end + end + if ~isempty(session_numbers), max_ses = max(session_numbers); end +function success = create_session_directories(handles, params,session_base_path) + success = false; + subfolders = {}; + if get(handles.cb_ephys, 'Value'), subfolders{end+1} = 'ephys'; end + if get(handles.cb_behav, 'Value'), subfolders{end+1} = 'behav'; end + if get(handles.cb_anat, 'Value'), subfolders{end+1} = 'anat'; end + if get(handles.cb_funcimg, 'Value'), subfolders{end+1} = 'funcimg'; end + try + for i = 1:length(subfolders) + local_target_path = fullfile(params.local_path, session_base_path, subfolders{i}); + log_message(handles, ['Creating local directory: ' local_target_path]); + if ~exist(local_target_path, 'dir'), mkdir(local_target_path); end + end + log_message(handles, 'All selected local directories created successfully.'); + success = true; + catch ME + msg = sprintf('Failed to create directories: %s', ME.message); + log_message(handles, ['ERROR: ' msg]); errordlg(msg, 'Directory Error'); + end +%% ======================================================================= +% HELPER & UTILITY FUNCTIONS +% ======================================================================= +function info = scan_for_info(search_path) + info = struct('folder_name', {}, 'subject_id', {}, 'rat_name', {}, 'experimenter', {}); + if ~exist(search_path, 'dir'), return; end + + dir_contents = dir(search_path); + if isempty(dir_contents), return; end + + pattern = '^sub-([^_]+)_id-([^_]+)_expmtr-([^_]+)$'; % More robust pattern + + for i = 1:length(dir_contents) + if dir_contents(i).isdir + folder_name = dir_contents(i).name; + tokens = regexp(folder_name, pattern, 'tokens'); + + if ~isempty(tokens) + new_entry.folder_name = folder_name; + new_entry.subject_id = tokens{1}{1}; + new_entry.rat_name = tokens{1}{2}; + new_entry.experimenter = tokens{1}{3}; + info(end+1, 1) = new_entry; + end + end + end +function session_nums = find_session_numbers(subject_path) + session_nums = []; + if ~exist(subject_path, 'dir'), return; end + dir_contents = dir(fullfile(subject_path, 'ses-*')); + for i = 1:length(dir_contents) + if dir_contents(i).isdir + token = regexp(dir_contents(i).name, '^ses-(\d+)', 'tokens'); + if ~isempty(token), session_nums(end+1) = str2double(token{1}{1}); end + end + end +function update_popup(h, options, selection) + if isempty(options), options = {}; end + % --- MODIFIED: Removed 'All' from options --- + new_string = [{'-'}, unique(options), {'Add New...'}]; + + % Find the index for the current selection in the new list + val = find(strcmp(new_string, selection), 1); + if isempty(val), val = 1; end % Default to first item '-' if not found + % --- END MODIFICATION --- + + set(h, 'String', new_string, 'Value', val); +function [selection, updated] = get_and_handle_new(h, prompt_title) + updated = false; + items = get(h, 'String'); + val = get(h, 'Value'); + selection = items{val}; + + if strcmp(selection, 'Add New...') + new_val_cell = inputdlg(['Enter new value for ' prompt_title], prompt_title, [1 50]); + if ~isempty(new_val_cell) && ~isempty(new_val_cell{1}) + new_val = new_val_cell{1}; + % Check if it already exists (case-insensitive) + existing_idx = find(strcmpi(items, new_val), 1); + if ~isempty(existing_idx) + set(h, 'Value', existing_idx); + else + items{end} = new_val; % Replace 'Add New...' with the new value + items{end+1} = 'Add New...'; % Add it back at the end + set(h, 'String', items, 'Value', numel(items)-1); + end + updated = true; + selection = new_val; + else + set(h, 'Value', 1); % Revert to first item '-' if cancelled + selection = '-'; + end + end +function log_message(handles,logStr) + try + if ~isfield(handles, 'log_box') || ~isvalid(handles.log_box), return; end + current_text = get(handles.log_box, 'String'); + timestamp = char(datetime('now', 'Format', '[HH:mm:ss] ')); + new_line = [timestamp, logStr]; + new_text = [current_text; {new_line}]; + set(handles.log_box, 'String', new_text, 'Value', numel(new_text)); + drawnow; + catch + fprintf('%s: %s\n', char(datetime('now', 'Format', '[HH:mm:ss] ')), logStr); + end +% --- ADDED: Helper function to check for data in a directory --- +function has_data = does_dir_have_data(path_to_check) + has_data = false; + if exist(path_to_check, 'dir') + dir_contents = dir(path_to_check); + % Check if there are more than 2 entries (i.e., more than '.' and '..') + if numel(dir_contents) > 2 + has_data = true; + end + end +function toggle_button_color(~, ~, button_handle) + if ~isvalid(button_handle), return; end + currentColor = get(button_handle, 'BackgroundColor'); + if isequal(currentColor, [1, 0.4, 0.4]), set(button_handle, 'BackgroundColor', [1, 0.7, 0.4]); + else, set(button_handle, 'BackgroundColor', [1, 0.4, 0.4]); end +function commit_to_svn(handles, file_path_data,file_path_settings, root_dir) + +if isempty(file_path_data), return; end + if isempty(file_path_settings), return; end + [pname_data, fname_data, ~] = fileparts(file_path_data); + [pname_settings, fname_settings, ~] = fileparts(file_path_settings); + + configFilePath = fullfile(root_dir,'PASSWORD_CONFIG-DO_NOT_VERSIONCONTROL.mat'); + if ~exist(configFilePath, 'file') + log_message(handles, ['SVN commit failed: Password config file not found at ' configFilePath]); + return; + end + load(configFilePath, 'svn_user', 'svn_password'); + logmsg_data = char(strcat('automated commit from GUI for data and settings for ', {' '} ,fname_data,{'@'})); + % current_dir = cd; + cd(pname_data); + add_cmd_data = char(strcat('svn add', {' '}, fname_data, '.mat',{'@'})); + system(add_cmd_data); + commit_cmd_data = sprintf('svn ci --username="%s" --password="%s" -m "%s"', svn_user, svn_password, logmsg_data); + [status, ~] = system(commit_cmd_data); + cd(pname_settings); + add_cmd_settings = char(strcat('svn add', {' '}, fname_settings, '.mat',{'@'})); + system(add_cmd_settings); + logmsg_setting = char(strcat('automated commit from GUI for data and settings for ', {' '} ,fname_settings,{'@'})); + commit_cmd_setting = sprintf('svn ci --username="%s" --password="%s" -m "%s"', svn_user, svn_password, logmsg_setting); + [status, ~] = system(commit_cmd_setting); + + if status == 0 + log_message(handles, ['SVN commit successful for ' fname_data]); + else + log_message(handles, ['SVN commit FAILED for ' fname_data '.']); + end + + cd(fullfile(root_dir,'ExperPort')); + %% ======================================================================= +% DOCUMENTATION AND USAGE EXAMPLES +% ======================================================================= +function display_usage_help() +% DISPLAY_USAGE_HELP - Display usage instructions and examples +% +% This function provides comprehensive usage documentation for the GUI + fprintf('\n=== Neuropixels Recording & Behavior Controller Usage Guide ===\n\n'); + + fprintf('1. INITIALIZATION:\n'); + fprintf(' OpenEphys_Neuroblueprint_GUI(''init'');\n\n'); + + fprintf('2. WORKFLOW:\n'); + fprintf(' a) Select recording software (Open Ephys or SpikeGLX)\n'); + fprintf(' b) Configure behavior settings (protocol, experimenter, rat)\n'); + fprintf(' c) Set up NeuroBlueprint data paths\n'); + fprintf(' d) Configure probe settings (version, reference, bank/IMRO)\n'); + fprintf(' e) Set recording software connection parameters\n'); + fprintf(' f) Click "Load" to initialize systems\n'); + fprintf(' g) Click "Run" to start experiment\n'); + fprintf(' h) Click "Stop" to end experiment and save data\n\n'); + + fprintf('3. PROBE CONFIGURATION:\n'); + fprintf(' - Supports Neuropixels 1.0 (3 banks) and 2.0 (4 banks)\n'); + fprintf(' - Reference options: Tip or External\n'); + fprintf(' - Bank selection: Manual bank number or IMRO file\n'); + fprintf(' - Pre/post-session sampling across all banks\n\n'); + + fprintf('4. DATA ORGANIZATION:\n'); + fprintf(' - Follows NeuroBlueprint format\n'); + fprintf(' - Structure: project/rawdata/subject/session/datatype/\n'); + fprintf(' - Automatic session numbering\n'); + fprintf(' - SVN integration for version control\n\n'); + + fprintf('5. RECORDING SOFTWARE SUPPORT:\n'); + fprintf(' Open Ephys:\n'); + fprintf(' - HTTP API control\n'); + fprintf(' - Real-time parameter adjustment\n'); + fprintf(' - Acquisition and recording control\n\n'); + fprintf(' SpikeGLX:\n'); + fprintf(' - MATLAB SDK integration\n'); + fprintf(' - Run name management\n'); + fprintf(' - Recording enable/disable control\n\n'); + + fprintf('6. ERROR HANDLING:\n'); + fprintf(' - Comprehensive validation of inputs\n'); + fprintf(' - Automatic crash recovery for behavior protocols\n'); + fprintf(' - Detailed logging with timestamps\n'); + fprintf(' - Graceful fallbacks for system failures\n\n'); + + fprintf('For more information, see function documentation within the code.\n'); + fprintf('================================================================\n\n'); \ No newline at end of file diff --git a/ExperPort/Modules/@NeuropixelNeuroblueprint/NeuropixelNeuroblueprint.m b/ExperPort/Modules/@NeuropixelNeuroblueprint/NeuropixelNeuroblueprint.m index eea1cc64..2ab54c68 100644 --- a/ExperPort/Modules/@NeuropixelNeuroblueprint/NeuropixelNeuroblueprint.m +++ b/ExperPort/Modules/@NeuropixelNeuroblueprint/NeuropixelNeuroblueprint.m @@ -20,18 +20,14 @@ % USAGE: % gui_obj = NeuropixelNeuroblueprintGUI(); % - %% Boilerplate for class definition and action handling obj = class(struct, mfilename); varargout = {}; - - % Display usage help when function is called directly if nargin==0 || (nargin==1 && ischar(varargin{1}) && strcmp(varargin{1}, 'empty')) display_usage_help(); return; end - if isa(varargin{1}, mfilename) if length(varargin) < 2 || ~ischar(varargin{2}) error(['If called with a "%s" object as first arg, a second arg, a ' ... @@ -44,13 +40,10 @@ action = varargin{1}; varargin = varargin(2:end); end - if ~ischar(action) error('The action parameter must be a string'); end - GetSoloFunctionArgs(obj); - %% Main Action Router switch action % ========================================================================= @@ -77,8 +70,9 @@ SoloParamHandle(obj, 'blinking_timer', 'value', []); SoloParamHandle(obj, 'current_params', 'value', []); SoloParamHandle(obj, 'session_base_path', 'value', ''); - SoloParamHandle(obj, 'probe_settings', 'value', struct('version', '2.0', 'reference', 'Tip', 'bank', 0, 'imro_path', '')); + SoloParamHandle(obj, 'probe_settings', 'value', struct('version', '2.0', 'reference', 'Tip', 'bank', 0, 'imro_path', '', 'sync_slot', 2)); SoloParamHandle(obj, 'probe_gui_handles', 'value', []); + SoloParamHandle(obj, 'session_info_list', 'value', []); % Holds parsed info from folders % Create stopping timer for behavior scr = timer; @@ -94,7 +88,6 @@ 'Position', [0.1, 0.1, 0.7, 0.85],... 'Color', [0.94, 0.94, 0.94], ... 'CloseRequestFcn', {@(h,e) feval(mfilename, obj, 'close')}); - % --- UI Creation --- handles = struct(); @@ -107,41 +100,39 @@ handles.software_group = uibuttongroup(p0, 'Title', '', 'BorderType', 'none', 'Units', 'normalized', 'Position', [0.05, 0.1, 0.9, 0.8], 'SelectionChangedFcn', {@(h,e) feval(mfilename, obj, 'recording_software_callback')}); uicontrol(handles.software_group, 'Style', 'radiobutton', 'String', 'Open Ephys', 'Units', 'normalized', 'Position', [0.1, 0.3, 0.4, 0.4], 'Tag', 'OpenEphys', 'FontSize', 10); uicontrol(handles.software_group, 'Style', 'radiobutton', 'String', 'SpikeGLX', 'Units', 'normalized', 'Position', [0.5, 0.3, 0.4, 0.4], 'Tag', 'SpikeGLX', 'FontSize', 10); - + % Panel 1: Behavior - p1 = uipanel('Title', '1. Behavior', 'FontSize', 12, 'FontWeight', 'bold', 'BorderType', 'etchedin', 'BorderWidth', 1, 'Units', 'normalized', 'Position', [0.02, 0.74, 0.6, 0.13]); + p1 = uipanel('Title', '1. Behavior', 'FontSize', 12, 'FontWeight', 'bold', 'BorderType', 'etchedin', 'BorderWidth', 1, 'Units', 'normalized', 'Position', [0.02, 0.74, 0.6, 0.13]); uicontrol(p1, 'Style', 'text', 'String', 'Protocol Name:', 'Units', 'normalized', 'Position', [0.05, 0.7, 0.22, 0.25], 'HorizontalAlignment', 'right'); handles.protocol_edit = uicontrol(p1, 'Style', 'edit', 'String', 'ArpitSoundCatContinuous', 'Units', 'normalized', 'Position', [0.3, 0.7, 0.45, 0.25]); handles.manual_test = uicontrol(p1, 'Style', 'checkbox', 'String', 'Manual Test', 'Value', 1, 'Units', 'normalized', 'Position', [0.78, 0.7, 0.2, 0.25]); uicontrol(p1, 'Style', 'text', 'String', 'Experimenter:', 'Units', 'normalized', 'Position', [0.02, 0.4, 0.2, 0.25], 'HorizontalAlignment', 'right'); - handles.exp_edit = uicontrol(p1, 'Style', 'edit', 'String', 'lida', 'Units', 'normalized', 'Position', [0.23, 0.4, 0.25, 0.25], 'Callback', {@(h,e) feval(mfilename, obj, 'update_subject_id')}); + handles.exp_popup = uicontrol(p1, 'Style', 'popupmenu', 'String', {'-'}, 'Units', 'normalized', 'Position', [0.23, 0.4, 0.25, 0.25], 'Callback', {@(h,e) feval(mfilename, obj, 'populate_and_filter_lists', 'filter')}); uicontrol(p1, 'Style', 'text', 'String', 'Rat Name:', 'Units', 'normalized', 'Position', [0.50, 0.4, 0.18, 0.25], 'HorizontalAlignment', 'right'); - handles.rat_name_edit = uicontrol(p1, 'Style', 'edit', 'String', 'LP12', 'Units', 'normalized', 'Position', [0.69, 0.4, 0.30, 0.25], 'Callback', {@(h,e) feval(mfilename, obj, 'update_subject_id')}); + handles.rat_name_popup = uicontrol(p1, 'Style', 'popupmenu', 'String', {'-'}, 'Units', 'normalized', 'Position', [0.69, 0.4, 0.30, 0.25], 'Callback', {@(h,e) feval(mfilename, obj, 'populate_and_filter_lists', 'filter')}); uicontrol(p1, 'Style', 'text', 'String', 'Distribution:', 'Units', 'normalized', 'Position', [0.02, 0.1, 0.18, 0.25], 'HorizontalAlignment', 'right'); - handles.distribution_popup = uicontrol(p1, 'Style', 'popupmenu', ... - 'String', {'random', 'Uniform', 'Hard A', 'Hard B'}, ... - 'Units', 'normalized', ... - 'Position', [0.21, 0.1, 0.35, 0.25]); + handles.distribution_popup = uicontrol(p1, 'Style', 'popupmenu', 'String', {'random', 'Uniform', 'Hard A', 'Hard B'}, 'Units', 'normalized', 'Position', [0.21, 0.1, 0.35, 0.25]); uicontrol(p1, 'Style', 'text', 'String', 'Path:', 'Units', 'normalized', 'Position', [0.58, 0.1, 0.1, 0.25], 'HorizontalAlignment', 'right'); - handles.behav_edit = uicontrol(p1, 'Style', 'edit', 'String', 'C:\ratter', 'Units', 'normalized', 'Position', [0.69, 0.1, 0.30, 0.25]);% - + handles.behav_edit = uicontrol(p1, 'Style', 'edit', 'String', 'C:\ratter', 'Units', 'normalized', 'Position', [0.69, 0.1, 0.30, 0.25]); % Panel 2: NeuroBlueprint Format p2 = uipanel('Title', '2. NeuroBlueprint Format', 'FontSize', 12, 'FontWeight', 'bold', 'BorderType', 'etchedin', 'BorderWidth', 1, 'Units', 'normalized', 'Position', [0.02, 0.42, 0.6, 0.31]); uicontrol(p2, 'Style', 'text', 'String', 'Project Name:', 'Units', 'normalized', 'Position', [0.01, 0.85, 0.28, 0.1], 'HorizontalAlignment', 'right'); - handles.proj_edit = uicontrol(p2, 'Style', 'edit', 'String', 'sound_cat_rat', 'Units', 'normalized', 'Position', [0.3, 0.85, 0.65, 0.12]); + handles.proj_edit = uicontrol(p2, 'Style', 'edit', 'String', 'sound_cat_rat', 'Units', 'normalized', 'Position', [0.3, 0.85, 0.65, 0.12], 'Callback', {@(h,e) feval(mfilename, obj, 'populate_and_filter_lists', 'rescan')}); uicontrol(p2, 'Style', 'text', 'String', 'Subject ID:', 'Units', 'normalized', 'Position', [0.01, 0.7, 0.28, 0.1], 'HorizontalAlignment', 'right'); - handles.sub_edit = uicontrol(p2, 'Style', 'edit', 'String', '000', 'Units', 'normalized', 'Position', [0.3, 0.7, 0.65, 0.12]); + handles.sub_popup = uicontrol(p2, 'Style', 'popupmenu', 'String', {'-'}, 'Units', 'normalized', 'Position', [0.3, 0.7, 0.3, 0.12], 'Enable', 'inactive'); + uicontrol(p2, 'Style', 'text', 'String', 'Session ID:', 'Units', 'normalized', 'Position', [0.61, 0.7, 0.2, 0.1], 'HorizontalAlignment', 'right'); + handles.session_popup = uicontrol(p2, 'Style', 'popupmenu', 'String', {'-'}, 'Units', 'normalized', 'Position', [0.82, 0.7, 0.15, 0.12]); uicontrol(p2, 'Style', 'text', 'String', 'Local Path:', 'Units', 'normalized', 'Position', [0.01, 0.55, 0.28, 0.1], 'HorizontalAlignment', 'right'); - handles.local_edit = uicontrol(p2, 'Style', 'edit', 'String', 'C:\Ephys_Experiment_Data', 'Units', 'normalized', 'Position', [0.3, 0.55, 0.5, 0.12]); + handles.local_edit = uicontrol(p2, 'Style', 'edit', 'String', 'C:\Ephys_Experiment_Data', 'Units', 'normalized', 'Position', [0.3, 0.55, 0.5, 0.12], 'Callback', {@(h,e) feval(mfilename, obj, 'populate_and_filter_lists', 'rescan')}); handles.local_browse = uicontrol(p2, 'Style', 'pushbutton', 'String', 'Browse...', 'Units', 'normalized', 'Position', [0.81, 0.55, 0.16, 0.13], 'Callback', {@(h,e) feval(mfilename, obj, 'browse_path', 'local')}); uicontrol(p2, 'Style', 'text', 'String', 'Central Path:', 'Units', 'normalized', 'Position', [0.01, 0.4, 0.28, 0.1], 'HorizontalAlignment', 'right'); - handles.central_edit = uicontrol(p2, 'Style', 'edit', 'String', 'Z:\_projects', 'Units', 'normalized', 'Position', [0.3, 0.4, 0.5, 0.12]); + handles.central_edit = uicontrol(p2, 'Style', 'edit', 'String', 'Z:\_projects', 'Units', 'normalized', 'Position', [0.3, 0.4, 0.5, 0.12], 'Callback', {@(h,e) feval(mfilename, obj, 'populate_and_filter_lists', 'rescan')}); handles.central_browse = uicontrol(p2, 'Style', 'pushbutton', 'String', 'Browse...', 'Units', 'normalized', 'Position', [0.81, 0.4, 0.16, 0.13], 'Callback', {@(h,e) feval(mfilename, obj, 'browse_path', 'central')}); uicontrol(p2, 'Style', 'text', 'String', 'Subfolders to Create:', 'Units', 'normalized', 'Position', [0.05, 0.2, 0.9, 0.1], 'HorizontalAlignment', 'left'); handles.cb_ephys = uicontrol(p2, 'Style', 'checkbox', 'String', 'ephys', 'Value', 1, 'Units', 'normalized', 'Position', [0.05, 0.05, 0.2, 0.15]); handles.cb_behav = uicontrol(p2, 'Style', 'checkbox', 'String', 'behav', 'Value', 1, 'Units', 'normalized', 'Position', [0.28, 0.05, 0.2, 0.15]); - handles.cb_anat = uicontrol(p2, 'Style', 'checkbox', 'String', 'anat', 'Value', 1, 'Units', 'normalized', 'Position', [0.51, 0.05, 0.2, 0.15]); + handles.cb_anat = uicontrol(p2, 'Style', 'checkbox', 'String', 'anat', 'Value', 0, 'Units', 'normalized', 'Position', [0.51, 0.05, 0.2, 0.15]); handles.cb_funcimg = uicontrol(p2, 'Style', 'checkbox', 'String', 'funcimg', 'Value', 0, 'Units', 'normalized', 'Position', [0.74, 0.05, 0.25, 0.15]); % Panel 3: Pre/Post-Experiment Sampling @@ -171,7 +162,6 @@ handles.sglx_port_edit = uicontrol(p4, 'Style', 'edit', 'String', '4142', 'Units', 'normalized', 'Position', [0.49, 0.25, 0.15, 0.5], 'Visible', 'off'); handles.sglx_probe_label = uicontrol(p4, 'Style', 'text', 'String', 'Probe Idx:', 'Units', 'normalized', 'Position', [0.65, 0.3, 0.15, 0.4], 'HorizontalAlignment', 'right', 'Visible', 'off'); handles.sglx_probe_edit = uicontrol(p4, 'Style', 'edit', 'String', '0', 'Units', 'normalized', 'Position', [0.81, 0.25, 0.15, 0.5], 'Visible', 'off'); - % --- Control Buttons Panel --- p5 = uipanel('Title', 'Controls', 'FontSize', 12, 'FontWeight', 'bold', 'BorderType', 'etchedin', 'BorderWidth', 1, 'Units', 'normalized', 'Position', [0.02, 0.02, 0.6, 0.11]); handles.control_button = uicontrol(p5, 'Style', 'pushbutton', 'String', 'Load', 'Units', 'normalized', 'FontSize', 14, 'FontWeight', 'bold', 'Position', [0.02, 0.1, 0.3, 0.8], 'BackgroundColor', [0.2, 0.6, 0.8], 'Callback', {@(h,e) feval(mfilename, obj, 'main_control_callback')}); @@ -180,9 +170,8 @@ SoloParamHandle(obj, 'ui_handles', 'value', handles); - feval(mfilename, obj, 'update_subject_id'); + feval(mfilename, obj, 'populate_and_filter_lists', 'rescan'); log_message(handles, 'GUI initialization complete.'); - % ========================================================================= % CASE MAIN_CONTROL_CALLBACK % ========================================================================= @@ -193,7 +182,6 @@ case 'Stop', feval(mfilename, obj, 'stop_sequence'); case 'PostExperiment', feval(mfilename, obj, 'reset_to_load_state'); end - % ========================================================================= % CASE RECORDING_SOFTWARE_CALLBACK % ========================================================================= @@ -226,13 +214,41 @@ try software = value(recording_software); params = get_all_parameters(handles,software); + + % --- ADDED: Confirmation for overwriting existing session data --- + if ~strcmp(params.session_id, 'New') + % Construct path to check for existing data + subject_name_part = sprintf('sub-%s_id-%s_expmtr-%s', params.subject_id, params.rat_name, params.experimenter); + session_name_part = sprintf('ses-%s', params.session_id); % Assumes session_id is formatted '01', '02' etc. + base_path_part = fullfile(params.project_name, 'rawdata', subject_name_part, session_name_part); + + local_behav_path = fullfile(params.local_path, base_path_part, 'behav'); + local_ephys_path = fullfile(params.local_path, base_path_part, 'ephys'); + central_behav_path = fullfile(params.central_path, base_path_part, 'behav'); + central_ephys_path = fullfile(params.central_path, base_path_part, 'ephys'); + + data_exists = does_dir_have_data(local_behav_path) || does_dir_have_data(local_ephys_path) || ... + does_dir_have_data(central_behav_path) || does_dir_have_data(central_ephys_path); + + if data_exists + question = sprintf('Session %s for this subject already contains data. Continuing may result in data being overwritten. Are you sure you want to proceed?', params.session_id); + answer = questdlg(question, 'Confirm Overwrite', 'Yes', 'No', 'No'); + if strcmp(answer, 'No') + log_message(handles, 'Load sequence aborted by user to prevent overwriting data.'); + feval(mfilename, obj, 'reset_to_load_state'); + return; % Abort the load sequence + end + end + end + % --- END ADDED SECTION --- + if ~validate_all_inputs(params,handles,software) feval(mfilename, obj, 'reset_to_load_state'); return; end current_params.value = params; - [session_path, recording_save_path] = construct_session_paths(handles, params); + [session_path, ~] = construct_session_paths(handles, params); if isempty(session_path) || ~create_session_directories(handles, params, session_path) feval(mfilename, obj, 'reset_to_load_state'); return; @@ -247,10 +263,45 @@ feval(mfilename, obj, 'reset_to_load_state'); rethrow(ME); end - case 'run_sequence' handles = value(ui_handles); params = value(current_params); + + % --- ADDED: Pre-run checklist dialog --- + software = value(recording_software); + if strcmp(software, 'OpenEphys') + title = 'Open Ephys Pre-Run Checklist'; + message = { + 'Before continuing, please ensure the following in Open Ephys:', ... + '', ... + '1. Refresh the Neuropixel PXI module to ensure probes are detected.', ... + '2. Set the probe geometry (Bank, Reference, or select the IMRO file).', ... + '3. Configure the main sync slot and SMA settings.', ... + '', ... + 'NOTE: The file save directory and name will be set automatically by this program.' ... + }; + else % SpikeGLX + title = 'SpikeGLX Pre-Run Checklist'; + message = { + 'Before continuing, please ensure the following in SpikeGLX:', ... + '', ... + '1. Press ''Detect'' to find the connected probes.', ... + '2. In Sync settings, set ''Square Wave Source'' to ''Disable sync waveform''.', ... + '3. Set the inputs for the ''imec PXI SMA slot''.', ... + '4. Select the correct IMRO file for the animal within SpikeGLX.',... + '', ... + 'NOTE: The run name and directory will be set automatically by this program.'... + }; + end + + answer = questdlg(message, title, 'Continue', 'Cancel', 'Continue'); + + if ~strcmp(answer, 'Continue') + log_message(handles, 'Run sequence cancelled by user at pre-run checklist.'); + return; % Abort the run sequence + end + % --- END ADDED SECTION --- + log_message(handles, '--- RUN sequence initiated ---'); set(handles.sample_button, 'Enable', 'off'); @@ -281,7 +332,6 @@ feval(mfilename, obj, 'stop_blinking'); rethrow(ME); end - case 'stop_sequence' handles = value(ui_handles); params = value(current_params); @@ -305,7 +355,6 @@ log_message(handles, sprintf('ERROR during stop sequence: %s', ME.message)); rethrow(ME); end - % ========================================================================= % SYSTEM INITIALIZATION & CONTROL % ========================================================================= @@ -343,7 +392,6 @@ controller = value(recording_controller); params = value(current_params); handles = value(ui_handles); - if isempty(controller), return; end try @@ -358,13 +406,11 @@ log_message(handles, sprintf('Failed to set recording path: %s', ME.message)); rethrow(ME); end - case 'start_electrophysiology_recording' params = varargin{1}; software = value(recording_software); controller = value(recording_controller); handles = value(ui_handles); - if isempty(controller), error('Recording controller not initialized'); end try @@ -407,7 +453,6 @@ log_message(handles, sprintf('Failed to start recording: %s', ME.message)); rethrow(ME); end - case 'stop_electrophysiology_recording' software = value(recording_software); controller = value(recording_controller); @@ -439,7 +484,6 @@ log_message(handles, sprintf('Failed to stop recording: %s', ME.message)); rethrow(ME); end - case 'initialize_behavior_system' params = value(current_params); handles = value(ui_handles); @@ -471,7 +515,6 @@ errordlg(['Failed to start behavior protocol. Check logs. Error: ' ME.message], 'Behavior System Error'); rethrow(ME); end - case 'stop_behavioral_protocol' params = varargin{1}; behav_save_dir = varargin{2}; @@ -487,17 +530,13 @@ end case 'manual_test_stopping' - handles = value(ui_handles); log_message(handles, 'Manual rig test complete. Cleaning up...'); dispatcher(value(behav_obj), 'Stop'); - %Let's pause until we know dispatcher is done running set(value(stopping_complete_timer), 'TimerFcn', {@(h,e) feval(mfilename, obj, 'manual_test_stopped')}); start(value(stopping_complete_timer)); - case 'manual_test_stopped' - if value(stopping_process_completed) %This is provided by RunningSection stop(value(stopping_complete_timer)); %Stop looping. dispatcher('set_protocol', ''); @@ -517,6 +556,11 @@ currentState.value = 'Run'; set(handles.control_button, 'Enable', 'on', 'String', 'Run', 'BackgroundColor', [0.4, 0.8, 0.4]); set(handles.sample_button, 'Enable', 'on'); + + % --- ADDED: Confirmation dialog after successful load --- + msgbox('Behavior settings loaded successfully. The system is now ready to run.', 'Load Complete', 'help'); + % --- END ADDED SECTION --- + catch ME log_message(handles, ['FATAL ERROR loading main protocol: ' ME.message]); errordlg(['Failed to load main protocol. Error: ' ME.message], 'Behavior System Error'); @@ -563,7 +607,6 @@ log_message(handles, sprintf('ERROR stopping ephys: %s', ME.message)); end set(handles.ephys_button, 'Enable', 'on'); - % ========================================================================= % BEHAVIOR CONTROL ACTIONS % ========================================================================= @@ -623,7 +666,6 @@ feval(protocol_name, protobj, 'set_setting_params', ratname, experimenter, sfile, char(datetime('now')), video_save_dir); if ~dispatcher('is_running'), pop_history(class(protobj), 'include_non_gui', 1); feval(protocol_name, protobj, 'prepare_next_trial'); end end - case 'load_run' set(handles.behav_button, 'Enable', 'off'); log_message(handles, '--- STARTING BEHAV PROTOCOL ---'); @@ -633,7 +675,6 @@ feval(mfilename, obj, 'behav_control', 'run', params.protocol_name); log_message(handles, '--- START COMPLETE: Behavior protocol started ---'); set(handles.behav_button, 'Enable', 'on'); - case 'run' protocol_name = args{1}; protobj = eval(protocol_name); log_message(handles, 'Starting video recording via protocol...'); @@ -658,7 +699,6 @@ dispatcher(value(behav_obj), 'Stop'); set(value(stopping_complete_timer), 'Period', 0.8,'TimerFcn', {@(h,e) feval(mfilename, obj, 'behav_control','end_continued',protocol_name, root_dir, behav_copy_dir)}); start(value(stopping_complete_timer)); - case 'end_continued' if value(stopping_process_completed) % This is provided by RunningSection protocol_name = args{1}; root_dir = args{2}; destination_path = args{3}; @@ -690,7 +730,6 @@ feval(mfilename, obj, 'save_log_file'); set(handles.behav_button, 'Enable', 'on'); end - case 'manual_test' log_message(handles, 'Loading manual rig test protocol...'); dispatcher('set_protocol', 'Rigtest_singletrial'); @@ -699,7 +738,6 @@ is_running.value = 1; log_message(handles, 'Starting manual rig test. Please complete the one-trial test.'); dispatcher(value(behav_obj), 'Run'); - case 'create_svn_data_dir' experimenter = args{1}; ratname = args{2}; behav_dir = args{3}; dir_name = args{4}; dirCurrent = cd; @@ -711,7 +749,6 @@ if ~isfolder(rat_path), cd(exp_path); mkdir(ratname); system(['svn add ' ratname]); end cd(dirCurrent); log_message(handles, ['Created SVN directory structure for ' ratname]); - case 'send_empty_state_machine' state_machine_server = bSettings('get', 'RIGS', 'state_machine_server'); server_slot = bSettings('get', 'RIGS', 'server_slot'); if isnan(server_slot), server_slot = 0; end @@ -728,11 +765,9 @@ % handles = feval(mfilename, obj, 'get_ui_handles'); handles = value(ui_handles); if ~strcmp(value(currentState), 'Stop') || isempty(value(behav_obj)), return; end - log_message(handles, '!!! CRASH DETECTED: Behavior system is not running. Attempting recovery...'); try feval(mfilename, obj, 'behav_control', 'crashed'); - catch ME log_message(handles, sprintf('FATAL: Recovery attempt failed: %s', ME.message)); getReport(ME, 'extended', 'hyperlinks', 'on'); @@ -853,22 +888,31 @@ case 'open_probe_gui' handles = value(ui_handles); log_message(handles, 'Opening probe settings GUI...'); - probe_fig = figure('Name', 'Neuropixel Probe Settings', 'Position', [300 300 450 300], ... + probe_fig = figure('Name', 'Neuropixel Probe Settings', 'Position', [300 300 450 350], ... 'MenuBar', 'none', 'ToolBar', 'none', 'NumberTitle', 'off', 'Resize', 'off'); p_handles = struct(); - p_handles.version_group = uibuttongroup(probe_fig, 'Title', 'Probe Version', 'Position', [0.05 0.75 0.9 0.2]); + + p_handles.version_group = uibuttongroup(probe_fig, 'Title', 'Probe Version', 'Position', [0.05 0.78 0.9 0.2]); uicontrol(p_handles.version_group, 'Style', 'radiobutton', 'String', 'NP 1.0 (3 Banks)', 'Position', [10 5 150 25], 'Tag', '1.0'); uicontrol(p_handles.version_group, 'Style', 'radiobutton', 'String', 'NP 2.0 (4 Banks)', 'Position', [200 5 150 25], 'Tag', '2.0'); - p_handles.ref_group = uibuttongroup(probe_fig, 'Title', 'Reference', 'Position', [0.05 0.5 0.4 0.2]); + + p_handles.ref_group = uibuttongroup(probe_fig, 'Title', 'Reference', 'Position', [0.05 0.55 0.4 0.2]); uicontrol(p_handles.ref_group, 'Style', 'radiobutton', 'String', 'Tip', 'Position', [10 5 80 25], 'Tag', 'Tip'); uicontrol(p_handles.ref_group, 'Style', 'radiobutton', 'String', 'External', 'Position', [100 5 80 25], 'Tag', 'External'); - p_handles.bank_panel = uipanel(probe_fig, 'Title', 'Target Bank', 'Position', [0.5 0.5 0.45 0.2]); + + p_handles.bank_panel = uipanel(probe_fig, 'Title', 'Target Bank', 'Position', [0.5 0.55 0.45 0.2]); uicontrol(p_handles.bank_panel, 'Style', 'text', 'String', 'Bank:', 'Position', [10 5 40 20]); p_handles.bank_edit = uicontrol(p_handles.bank_panel, 'Style', 'edit', 'String', '0', 'Position', [60 5 50 25]); - uicontrol(probe_fig, 'Style', 'text', 'String', 'IMRO File:', 'Position', [20 120 60 20]); - p_handles.imro_text = uicontrol(probe_fig, 'Style', 'text', 'String', 'None selected', 'Position', [90 120 280 20], 'HorizontalAlignment', 'left'); - uicontrol(probe_fig, 'Style', 'pushbutton', 'String', 'Browse...', 'Position', [20 85 100 30], 'Callback', {@(h,e) feval(mfilename, obj, 'browse_imro', p_handles)}); - uicontrol(probe_fig, 'Style', 'pushbutton', 'String', 'Clear IMRO', 'Position', [130 85 100 30], 'Callback', {@(h,e) feval(mfilename, obj, 'clear_imro', p_handles)}); + + uicontrol(probe_fig, 'Style', 'text', 'String', 'Sync IMEC Slot:', 'Position', [20 160 100 20], 'HorizontalAlignment', 'right'); + p_handles.sync_slot_edit = uicontrol(probe_fig, 'Style', 'edit', 'String', '2', 'Position', [130 160 50 25]); + + uicontrol(probe_fig, 'Style', 'text', 'String', 'IMRO File:', 'Position', [20 120 100 20], 'HorizontalAlignment', 'right'); + p_handles.imro_text = uicontrol(probe_fig, 'Style', 'text', 'String', 'None selected', 'Position', [130 120 280 20], 'HorizontalAlignment', 'left'); + + uicontrol(probe_fig, 'Style', 'pushbutton', 'String', 'Browse...', 'Position', [130 85 100 30], 'Callback', {@(h,e) feval(mfilename, obj, 'browse_imro', p_handles)}); + uicontrol(probe_fig, 'Style', 'pushbutton', 'String', 'Clear IMRO', 'Position', [240 85 100 30], 'Callback', {@(h,e) feval(mfilename, obj, 'clear_imro', p_handles)}); + uicontrol(probe_fig, 'Style', 'pushbutton', 'String', 'Apply & Close', 'Position', [250 25 180 30], 'FontWeight', 'bold', 'Callback', {@(h,e) feval(mfilename, obj, 'apply_probe_settings', p_handles)}); probe_gui_handles.value = p_handles; @@ -894,8 +938,11 @@ settings.reference = get(get(p_handles.ref_group, 'SelectedObject'), 'Tag'); settings.bank = str2double(get(p_handles.bank_edit, 'String')); settings.imro_path = get(p_handles.imro_text, 'String'); + settings.sync_slot = str2double(get(p_handles.sync_slot_edit, 'String')); if strcmp(settings.imro_path, 'None selected'), settings.imro_path = ''; end + probe_settings.value = settings; + if ~isempty(settings.imro_path) set(handles.target_display, 'String', 'Target: IMRO File'); else @@ -917,6 +964,13 @@ if strcmp(software, 'OpenEphys') log_message(handles, sprintf('Setting OE reference to: %s', probe_settings.reference)); controller.setParameters(params.oe_proc_node_id, 0, 'Reference', probe_settings.reference); + + % Applying Sync Slot setting + log_message(handles, sprintf('Applying Sync IMEC Slot: %d', probe_settings.sync_slot)); + % NOTE: This assumes a parameter like 'syncSlot' exists in the Neuropix-PXI plugin. + % The actual parameter name might need to be verified in the Open Ephys plugin. + % controller.setParameters(params.oe_proc_node_id, 0, 'syncSlot', probe_settings.sync_slot); + if ~isempty(probe_settings.imro_path) log_message(handles, sprintf('Loading IMRO file: %s', probe_settings.imro_path)); controller.config(params.oe_proc_node_id, ['LOADIMRO ' probe_settings.imro_path]); @@ -925,12 +979,33 @@ controller.setParameters(params.oe_proc_node_id, 0, 'bank', probe_settings.bank); end else % SpikeGLX - % SpikeGLX probe configuration (e.g., reference, channel map) is more complex - % and typically handled by setting parameters or loading a meta file. - % This is a placeholder for those more complex API calls. - log_message(handles, 'Applying SpikeGLX probe settings (via meta file or API)...'); + log_message(handles, 'Applying SpikeGLX sync settings...'); + + %% Done Manually by the User but can be uncommented + % params_to_set = struct(); + % params_to_set.syncSourceIdx = 1; % Set sync source to PXI SMA + % params_to_set.syncImInputSlot = probe_settings.sync_slot; + % + % SetParams(controller, params_to_set); + % + % log_message(handles, 'Waiting for SpikeGLX to stabilise (3 seconds)...'); + % pause(3); + % + % log_message(handles, 'Reconnecting to SpikeGLX to reflect changes...'); + % controller = SpikeGL(params.sglx_host_ip, params.sglx_port); + % recording_controller.value = controller; % Update the handle + % + + % Verify settings + prms = GetParams(controller); + if prms.syncSourceIdx == 1 && prms.syncImInputSlot == probe_settings.sync_slot + log_message(handles, 'Success! SpikeGLX sync settings applied correctly.'); + else + log_message(handles, 'Warning: SpikeGLX settings may not have been applied. Please check SpikeGLX.'); + end + if ~isempty(probe_settings.imro_path) - log_message(handles, 'Note: For SpikeGLX, ensure IMRO settings are loaded within the SpikeGLX GUI and save as part of the meta file.'); + log_message(handles, 'Note: For SpikeGLX, ensure IMRO file is loaded within the SpikeGLX GUI and save as part of the meta file.'); end end log_message(handles, 'Probe configuration applied successfully.'); @@ -938,7 +1013,6 @@ log_message(handles, sprintf('Failed to apply probe settings: %s', ME.message)); rethrow(ME); end - % ========================================================================= % UTILITY & OTHER ACTIONS % ========================================================================= @@ -948,28 +1022,135 @@ log_message(handles, ['Opening browse dialog for ' type ' path...']); folder_path = uigetdir; if folder_path ~= 0 - if strcmp(type, 'local'), set(handles.local_edit, 'String', folder_path); - elseif strcmp(type, 'central'), set(handles.central_edit, 'String', folder_path); - elseif strcmp(type, 'behav'), set(handles.behav_edit, 'String', folder_path); + if strcmp(type, 'local') + set(handles.local_edit, 'String', folder_path); + feval(mfilename, obj, 'populate_and_filter_lists', 'rescan'); + elseif strcmp(type, 'central') + set(handles.central_edit, 'String', folder_path); + feval(mfilename, obj, 'populate_and_filter_lists', 'rescan'); + elseif strcmp(type, 'behav') + set(handles.behav_edit, 'String', folder_path); end log_message(handles, [type ' path set.']); else, log_message(handles, 'Path selection cancelled.'); end - - case 'update_subject_id' + + case 'populate_and_filter_lists' + trigger_type = varargin{1}; % 'rescan' or 'filter' handles = value(ui_handles); - software = value(recording_software); - params = get_all_parameters(handles,software); - if isempty(params.local_path) || isempty(params.central_path) || isempty(params.project_name) || isempty(params.rat_name) + + % --- Step 1: Scan directories if needed --- + if strcmp(trigger_type, 'rescan') + log_message(handles, 'Scanning directories for session info...'); + local_path = get(handles.local_edit, 'String'); + central_path = get(handles.central_edit, 'String'); + project_name = get(handles.proj_edit, 'String'); + + all_info = struct('folder_name', {}, 'subject_id', {}, 'rat_name', {}, 'experimenter', {}); + if ~isempty(local_path) && exist(local_path, 'dir') + all_info = [all_info; scan_for_info(fullfile(local_path, project_name, 'rawdata'))]; + end + if ~isempty(central_path) && exist(central_path, 'dir') + all_info = [all_info; scan_for_info(fullfile(central_path, project_name, 'rawdata'))]; + end + + if ~isempty(all_info) + keys = cell(numel(all_info), 1); + for i = 1:numel(all_info) + keys{i} = sprintf('%s|%s|%s', all_info(i).subject_id, all_info(i).rat_name, all_info(i).experimenter); + end + [~, ia] = unique(keys, 'stable'); + session_info_list.value = all_info(ia); + log_message(handles, sprintf('Found %d unique subjects.', numel(value(session_info_list)))); + else + session_info_list.value = []; + log_message(handles, 'No valid subject folders found.'); + end + + % Initial population of just the experimenter list + exp_options = unique([{value(session_info_list).experimenter}]); + update_popup(handles.exp_popup, exp_options, '-'); + update_popup(handles.rat_name_popup, {}, '-'); + set(handles.sub_popup, 'String', {'-'}, 'Value', 1); + set(handles.session_popup, 'String', {'-'}, 'Value', 1, 'Enable', 'off'); return; end - max_local_id = find_max_subject_id(params.local_path, params.project_name, params.rat_name); - max_central_id = find_max_subject_id(params.central_path, params.project_name, params.rat_name); - final_id = max(max_local_id, max_central_id); - if final_id > 0 - log_message(handles, ['Found existing Subject ID: ' num2str(final_id) '. Populating field.']); - set(handles.sub_edit, 'String', sprintf('%03d', final_id)); - else - log_message(handles, 'No existing Subject ID found for this rat. Please enter a new ID.'); + + % --- Step 2: Filter Logic triggered by user interaction --- + full_list = value(session_info_list); + source_handle = gcbo; + + % Get current selections, handling 'Add New...' dialogs + [exp_selection, exp_updated] = get_and_handle_new(handles.exp_popup, 'New Experimenter'); + if exp_updated % New exp added, reset everything below it + update_popup(handles.rat_name_popup, {}, '-'); + set(handles.sub_popup, 'String', {'-'}, 'Value', 1); + set(handles.session_popup, 'String', {'-'}, 'Value', 1, 'Enable', 'off'); + return; + end + + if source_handle == handles.exp_popup + if ~strcmp(exp_selection, '-') + filtered_by_exp = full_list(strcmpi({full_list.experimenter}, exp_selection)); + rat_options = unique([{filtered_by_exp.rat_name}]); + update_popup(handles.rat_name_popup, rat_options, '-'); + else + update_popup(handles.rat_name_popup, {}, '-'); + end + set(handles.sub_popup, 'String', {'-'}, 'Value', 1); + set(handles.session_popup, 'String', {'-'}, 'Value', 1, 'Enable', 'off'); + return; + end + + [rat_selection, rat_updated] = get_and_handle_new(handles.rat_name_popup, 'New Rat Name'); + + if source_handle == handles.rat_name_popup + if ~strcmp(rat_selection, '-') + subject_entry = full_list(strcmpi({full_list.experimenter}, exp_selection) & strcmpi({full_list.rat_name}, rat_selection)); + + if ~isempty(subject_entry) % Existing pair + sub_id = subject_entry(1).subject_id; + set(handles.sub_popup, 'String', {sub_id}, 'Value', 1); + elseif rat_updated % New Rat for this Experimenter + max_id = 0; + if ~isempty(full_list) + all_ids = str2double({full_list.subject_id}); + if ~all(isnan(all_ids)), max_id = max(all_ids(~isnan(all_ids))); end + end + sub_id = sprintf('%03d', max_id + 1); + set(handles.sub_popup, 'String', {sub_id}, 'Value', 1); + else + sub_id = '-'; + set(handles.sub_popup, 'String', {'-'}, 'Value', 1); + end + + % Populate Session list + if ~strcmp(sub_id, '-') + local_path = get(handles.local_edit, 'String'); + central_path = get(handles.central_edit, 'String'); + project_name = get(handles.proj_edit, 'String'); + subject_folder = sprintf('sub-%s_id-%s_expmtr-%s', sub_id, rat_selection, exp_selection); + session_numbers = []; + path1 = fullfile(local_path, project_name, 'rawdata', subject_folder); + if exist(path1, 'dir'), session_numbers = [session_numbers, find_session_numbers(path1)]; end + path2 = fullfile(central_path, project_name, 'rawdata', subject_folder); + if exist(path2, 'dir'), session_numbers = [session_numbers, find_session_numbers(path2)]; end + + unique_sessions = unique(session_numbers); + if ~isempty(unique_sessions) + sorted_sessions = sort(unique_sessions, 'descend'); + last_three = sorted_sessions(1:min(3, end)); + session_strings = arrayfun(@(x) sprintf('%02d', x), sort(last_three), 'UniformOutput', false); + set(handles.session_popup, 'String', [{'New'}, session_strings], 'Value', 1, 'Enable', 'on'); + else + set(handles.session_popup, 'String', {'New'}, 'Value', 1, 'Enable', 'on'); + end + else + set(handles.session_popup, 'String', {'-'}, 'Value', 1, 'Enable', 'off'); + end + else % rat set to '-' + set(handles.sub_popup, 'String', {'-'}, 'Value', 1); + set(handles.session_popup, 'String', {'-'}, 'Value', 1, 'Enable', 'off'); + end end case 'save_log_file' @@ -994,7 +1175,6 @@ catch ME log_message(handles, ['ERROR: Could not save log file. Details: ' ME.message]); end - case 'reset_to_load_state' handles = value(ui_handles); currentState.value = 'Load'; @@ -1007,7 +1187,6 @@ current_params.value = []; session_base_path.value = ''; log_message(handles, 'GUI reset to load state.'); - case 'close' try feval(mfilename, obj, 'stop_blinking'); @@ -1025,7 +1204,6 @@ delete_sphandle('owner', ['^@' mfilename '$']); obj = []; end - case 'is_running' if exist('is_running','var') == 1 obj = logical(value(is_running)); @@ -1037,7 +1215,6 @@ handles = value(ui_handles); blinking_timer.value = timer('ExecutionMode', 'fixedRate', 'Period', 0.5, 'TimerFcn', {@toggle_button_color, handles.control_button}); start(value(blinking_timer)); - case 'stop_blinking' handles = value(ui_handles); if ~isempty(value(blinking_timer)) && isvalid(value(blinking_timer)) @@ -1046,25 +1223,34 @@ blinking_timer.value = []; end set(handles.control_button, 'BackgroundColor', [1, 0.4, 0.4]); - otherwise error('Unknown action: %s', action); end return; - %% ======================================================================= % PARAMETER AND VALIDATION FUNCTIONS % ======================================================================= function params = get_all_parameters(handles,software) params.protocol_name = get(handles.protocol_edit, 'String'); params.do_manual_test = get(handles.manual_test, 'Value'); - params.experimenter = get(handles.exp_edit, 'String'); - params.rat_name = get(handles.rat_name_edit, 'String'); + + % Get values from popup menus + exp_items = get(handles.exp_popup, 'String'); + params.experimenter = exp_items{get(handles.exp_popup, 'Value')}; + + rat_items = get(handles.rat_name_popup, 'String'); + params.rat_name = rat_items{get(handles.rat_name_popup, 'Value')}; + + sub_items = get(handles.sub_popup, 'String'); + params.subject_id = sub_items{get(handles.sub_popup, 'Value')}; + + ses_items = get(handles.session_popup, 'String'); + params.session_id = ses_items{get(handles.session_popup, 'Value')}; + popup_string = get(handles.distribution_popup,'String'); params.stim_distribution = popup_string{get(handles.distribution_popup,'Value')}; params.behav_path = get(handles.behav_edit, 'String'); params.project_name = get(handles.proj_edit, 'String'); - params.subject_id = get(handles.sub_edit, 'String'); params.local_path = get(handles.local_edit, 'String'); params.central_path = get(handles.central_edit, 'String'); @@ -1077,13 +1263,13 @@ params.sglx_port = str2double(get(handles.sglx_port_edit, 'String')); params.sglx_probe_index = str2double(get(handles.sglx_probe_edit, 'String')); end - function is_valid = validate_all_inputs(params,handles,software) is_valid = false; - required_fields = {'protocol_name', 'rat_name', 'behav_path', 'project_name', 'subject_id', 'local_path'}; + required_fields = {'protocol_name', 'rat_name', 'behav_path', 'project_name', 'subject_id', 'local_path', 'session_id'}; for i = 1:length(required_fields) - if ~isfield(params, required_fields{i}) || isempty(params.(required_fields{i})) - msg = sprintf('Field "%s" cannot be empty.', strrep(required_fields{i}, '_', ' ')); + field_val = params.(required_fields{i}); + if ~isfield(params, required_fields{i}) || isempty(field_val) || strcmp(field_val, '-') + msg = sprintf('Field "%s" must have a valid selection before loading.', strrep(required_fields{i}, '_', ' ')); log_message(handles, sprintf('ERROR: %s', msg)); errordlg(msg, 'Input Error'); return; end @@ -1107,12 +1293,11 @@ end end is_valid = true; - %% ======================================================================= % PATH AND DIRECTORY FUNCTIONS % ======================================================================= function [session_base, recording_path] = construct_session_paths(handles, params) - if isempty(params.experimenter) + if isempty(params.experimenter) || strcmp(params.experimenter, '-') subject_name = sprintf('sub-%s_id-%s', params.subject_id, params.rat_name); else subject_name = sprintf('sub-%s_id-%s_expmtr-%s', params.subject_id, params.rat_name, params.experimenter); @@ -1120,14 +1305,20 @@ subject_base_path = fullfile(params.project_name, 'rawdata', subject_name); local_subject_dir = fullfile(params.local_path, subject_base_path); central_subject_dir = fullfile(params.central_path, subject_base_path); - new_ses_num = max(find_max_session_number(local_subject_dir), find_max_session_number(central_subject_dir)) + 1; - log_message(handles, sprintf('Last session found: %d. Creating new session: %d.', new_ses_num - 1, new_ses_num)); + + if strcmp(params.session_id, 'New') + new_ses_num = max(find_max_session_number(local_subject_dir), find_max_session_number(central_subject_dir)) + 1; + log_message(handles, sprintf('Last session found: %d. Creating new session: %d.', new_ses_num - 1, new_ses_num)); + else + new_ses_num = str2double(params.session_id); + log_message(handles, sprintf('Using existing session number: %d.', new_ses_num)); + end + session_datetime_str = char(datetime('now', 'Format', 'yyyyMMdd''T''HHmmss')); session_folder_name = sprintf('ses-%02d_date-%s_dtype-ephys', new_ses_num, session_datetime_str); session_base = fullfile(subject_base_path, session_folder_name); recording_path = fullfile(params.local_path, session_base, 'ephys'); log_message(handles, ['New session path determined: ' session_base]); - function max_ses = find_max_session_number(base_path) max_ses = 0; if ~exist(base_path, 'dir'), return; end dir_contents = dir(fullfile(base_path, 'ses-*')); @@ -1140,7 +1331,6 @@ end end if ~isempty(session_numbers), max_ses = max(session_numbers); end - function success = create_session_directories(handles, params,session_base_path) success = false; subfolders = {}; @@ -1160,26 +1350,79 @@ msg = sprintf('Failed to create directories: %s', ME.message); log_message(handles, ['ERROR: ' msg]); errordlg(msg, 'Directory Error'); end - -function max_id = find_max_subject_id(base_path, project, rat) - max_id = 0; - search_path = fullfile(base_path, project, 'rawdata'); +%% ======================================================================= +% HELPER & UTILITY FUNCTIONS +% ======================================================================= +function info = scan_for_info(search_path) + info = struct('folder_name', {}, 'subject_id', {}, 'rat_name', {}, 'experimenter', {}); if ~exist(search_path, 'dir'), return; end + dir_contents = dir(search_path); if isempty(dir_contents), return; end - subject_ids = []; - pattern = sprintf('^sub-(\\d+)_id-%s', rat); + + pattern = '^sub-([^_]+)_id-([^_]+)_expmtr-([^_]+)$'; % More robust pattern + for i = 1:length(dir_contents) if dir_contents(i).isdir - token = regexp(dir_contents(i).name, pattern, 'tokens'); - if ~isempty(token), subject_ids(end+1) = str2double(token{1}{1}); end + folder_name = dir_contents(i).name; + tokens = regexp(folder_name, pattern, 'tokens'); + + if ~isempty(tokens) + new_entry.folder_name = folder_name; + new_entry.subject_id = tokens{1}{1}; + new_entry.rat_name = tokens{1}{2}; + new_entry.experimenter = tokens{1}{3}; + info(end+1, 1) = new_entry; + end + end + end +function session_nums = find_session_numbers(subject_path) + session_nums = []; + if ~exist(subject_path, 'dir'), return; end + dir_contents = dir(fullfile(subject_path, 'ses-*')); + for i = 1:length(dir_contents) + if dir_contents(i).isdir + token = regexp(dir_contents(i).name, '^ses-(\d+)', 'tokens'); + if ~isempty(token), session_nums(end+1) = str2double(token{1}{1}); end + end + end +function update_popup(h, options, selection) + if isempty(options), options = {}; end + % --- MODIFIED: Removed 'All' from options --- + new_string = [{'-'}, unique(options), {'Add New...'}]; + + % Find the index for the current selection in the new list + val = find(strcmp(new_string, selection), 1); + if isempty(val), val = 1; end % Default to first item '-' if not found + % --- END MODIFICATION --- + + set(h, 'String', new_string, 'Value', val); +function [selection, updated] = get_and_handle_new(h, prompt_title) + updated = false; + items = get(h, 'String'); + val = get(h, 'Value'); + selection = items{val}; + + if strcmp(selection, 'Add New...') + new_val_cell = inputdlg(['Enter new value for ' prompt_title], prompt_title, [1 50]); + if ~isempty(new_val_cell) && ~isempty(new_val_cell{1}) + new_val = new_val_cell{1}; + % Check if it already exists (case-insensitive) + existing_idx = find(strcmpi(items, new_val), 1); + if ~isempty(existing_idx) + set(h, 'Value', existing_idx); + else + items{end} = new_val; % Replace 'Add New...' with the new value + items{end+1} = 'Add New...'; % Add it back at the end + set(h, 'String', items, 'Value', numel(items)-1); + end + updated = true; + selection = new_val; + else + set(h, 'Value', 1); % Revert to first item '-' if cancelled + selection = '-'; end end - if ~isempty(subject_ids), max_id = max(subject_ids); end - -%% ======================================================================= -% HELPER & UTILITY FUNCTIONS -% ======================================================================= function log_message(handles,logStr) try if ~isfield(handles, 'log_box') || ~isvalid(handles.log_box), return; end @@ -1192,14 +1435,21 @@ function log_message(handles,logStr) catch fprintf('%s: %s\n', char(datetime('now', 'Format', '[HH:mm:ss] ')), logStr); end - +% --- ADDED: Helper function to check for data in a directory --- +function has_data = does_dir_have_data(path_to_check) + has_data = false; + if exist(path_to_check, 'dir') + dir_contents = dir(path_to_check); + % Check if there are more than 2 entries (i.e., more than '.' and '..') + if numel(dir_contents) > 2 + has_data = true; + end + end function toggle_button_color(~, ~, button_handle) if ~isvalid(button_handle), return; end currentColor = get(button_handle, 'BackgroundColor'); if isequal(currentColor, [1, 0.4, 0.4]), set(button_handle, 'BackgroundColor', [1, 0.7, 0.4]); else, set(button_handle, 'BackgroundColor', [1, 0.4, 0.4]); end - - function commit_to_svn(handles, file_path_data,file_path_settings, root_dir) if isempty(file_path_data), return; end @@ -1220,7 +1470,6 @@ function commit_to_svn(handles, file_path_data,file_path_settings, root_dir) system(add_cmd_data); commit_cmd_data = sprintf('svn ci --username="%s" --password="%s" -m "%s"', svn_user, svn_password, logmsg_data); [status, ~] = system(commit_cmd_data); - cd(pname_settings); add_cmd_settings = char(strcat('svn add', {' '}, fname_settings, '.mat',{'@'})); system(add_cmd_settings); @@ -1235,17 +1484,13 @@ function commit_to_svn(handles, file_path_data,file_path_settings, root_dir) end cd(fullfile(root_dir,'ExperPort')); - - %% ======================================================================= % DOCUMENTATION AND USAGE EXAMPLES % ======================================================================= - function display_usage_help() % DISPLAY_USAGE_HELP - Display usage instructions and examples % % This function provides comprehensive usage documentation for the GUI - fprintf('\n=== Neuropixels Recording & Behavior Controller Usage Guide ===\n\n'); fprintf('1. INITIALIZATION:\n'); diff --git a/ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX_Datafile_Tools-main/MATLAB/DemoReadSGLXData.m b/ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX_Datafile_Tools-main/MATLAB/DemoReadSGLXData.m new file mode 100644 index 00000000..9d43548d --- /dev/null +++ b/ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX_Datafile_Tools-main/MATLAB/DemoReadSGLXData.m @@ -0,0 +1,48 @@ +% ============================================================= +% Simple demo program calling functions from the +% SGLX_readMeta class. +% +function DemoReadSGLXData() + +% Ask user for binary file +[binName,path] = uigetfile('*.bin', 'Select Binary File'); + +% Parse the corresponding metafile +meta = SGLX_readMeta.ReadMeta(binName, path); + +% Get first one second of data +nSamp = floor(1.0 * SGLX_readMeta.SampRate(meta)); +dataArray = SGLX_readMeta.ReadBin(0, nSamp, meta, binName, path); +dataType = 'A'; %set to 'A' for analog, 'D' for digital data + +% For an analog channel: gain correct saved channel ch (1-based for MATLAB). +ch = 1; + +% For a digital channel: read this digital word dw in the saved file +% (1-based). For imec data there is never more than one saved digital word. +dw = 1; + +% Read these lines in dw (0-based). +% For 3B2 imec data: the sync pulse is stored in line 6. +% May be 1 or more line indices. +dLineList = [0,1,6]; + +if dataType == 'A' + switch meta.typeThis + case 'imec' + dataArray = SGLX_readMeta.GainCorrectIM(dataArray, [ch], meta); + case 'nidq' + dataArray = SGLX_readMeta.GainCorrectNI(dataArray, [ch], meta); + case 'obx' + dataArray = SGLX_readMeta.GainCorrectOBX(dataArray, [ch], meta); + end + plot(1e6*dataArray(ch,:)); +else + digArray = SGLX_readMeta.ExtractDigital(dataArray, meta, dw, dLineList); + for i = 1:numel(dLineList) + plot(digArray(i,:)); + hold on + end + hold off +end +end % DemoReadSGLXData \ No newline at end of file diff --git a/ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX_Datafile_Tools-main/MATLAB/DiagnoseSyncBits.m b/ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX_Datafile_Tools-main/MATLAB/DiagnoseSyncBits.m new file mode 100644 index 00000000..3e730ea2 --- /dev/null +++ b/ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX_Datafile_Tools-main/MATLAB/DiagnoseSyncBits.m @@ -0,0 +1,117 @@ +function [stats] = DiagnoseSyncBits(binName, path, chunkSize, maxBits) +% DiagnoseSyncBits Scan SY word bits and report pulse counts and mean durations. +% stats = DiagnoseSyncBits(binName, path, chunkSize, maxBits) +% +% - binName: filename of .ap.bin (string) +% - path: folder containing the file (string) +% - chunkSize: number of samples per chunk (default 1e6) +% - maxBits: how many bits to scan (default 16) +% +% Returns a struct array stats(b) with fields: +% .bit bit number (1-based) +% .nPulses number of completed pulses detected +% .meanDur_s mean pulse duration (seconds) +% .medianDur_s median pulse duration (seconds) +% .durations_s vector of durations (seconds) [may be large] +% + if nargin < 3 || isempty(chunkSize), chunkSize = 1e6; end + if nargin < 4 || isempty(maxBits), maxBits = 16; end + + meta = SGLX_readMeta.ReadMeta(binName, path); + sRate = SGLX_readMeta.SampRate(meta); + nSavedChans = str2double(meta.nSavedChans); + syncCh = nSavedChans; + bytesPerSamp = 2; + + nFileBytes = str2double(meta.fileSizeBytes); + nSampTotal = nFileBytes / (nSavedChans * bytesPerSamp); + + fid = fopen(fullfile(path, binName), 'r'); + if fid < 0, error('Cannot open %s', fullfile(path, binName)); end + + % init + openStart = zeros(1, maxBits); % 0 if no open pulse; otherwise sample index of start + pulses = cell(1, maxBits); % will hold Nx2 arrays [startSample endSample] + for b=1:maxBits, pulses{b} = zeros(0,2); end + + nChunks = ceil(nSampTotal / chunkSize); + for c = 1:nChunks + startSamp0 = (c-1)*chunkSize; % 0-based sample index of chunk start + nRead = min(chunkSize, nSampTotal - startSamp0); + + % seek to sync channel first sample of chunk + offset = (startSamp0 * nSavedChans + (syncCh-1)) * bytesPerSamp; + fseek(fid, offset, 'bof'); + + % read nRead sync words with stride + raw = fread(fid, nRead, 'int16', (nSavedChans-1)*bytesPerSamp); + if isempty(raw), break; end + sy = uint16(raw(:)'); % row vector of uint16 + + % process each bit + for b = 1:maxBits + vec = logical(bitget(sy, b)); % 1..nRead logical vector + prev = openStart(b) > 0; % previous chunk state: 1 if currently inside a pulse + % detect rising positions and falling positions in this chunk + % prevVec is previous sample for first element + if nRead==0, continue; end + prevVec = [prev, vec(1:end-1)]; + risePos = find(prevVec==0 & vec==1); % indices in 1..nRead + fallPos = find(prevVec==1 & vec==0); + + % handle rises + for i = 1:numel(risePos) + if openStart(b) == 0 + openStart(b) = startSamp0 + risePos(i); % store 0-based sample index + else + % a new rise found while previous open exists; treat as restart: + % close previous at this rise (best-effort) then start new + pulses{b}(end+1, :) = [openStart(b), startSamp0 + risePos(i)]; + openStart(b) = startSamp0 + risePos(i); + end + end + + % handle falls + for i = 1:numel(fallPos) + if openStart(b) ~= 0 + pulses{b}(end+1, :) = [openStart(b), startSamp0 + fallPos(i)]; + openStart(b) = 0; + else + % fall without open start: ignore + end + end + end + end + + fclose(fid); + + % if any open starts remain, close them at end of file + for b = 1:maxBits + if openStart(b) ~= 0 + pulses{b}(end+1, :) = [openStart(b), nSampTotal]; + openStart(b) = 0; + end + end + + % compute stats + stats = struct(); + for b = 1:maxBits + pairs = pulses{b}; + if isempty(pairs) + stats(b).bit = b; + stats(b).nPulses = 0; + stats(b).meanDur_s = NaN; + stats(b).medianDur_s = NaN; + stats(b).durations_s = []; + else + durs = (pairs(:,2) - pairs(:,1)) / sRate; + stats(b).bit = b; + stats(b).nPulses = size(pairs,1); + stats(b).meanDur_s = mean(durs); + stats(b).medianDur_s = median(durs); + stats(b).durations_s = durs; + end + fprintf('Bit %2d : pulses = %4d, mean dur = %0.4f s, median = %0.4f s\n', ... + stats(b).bit, stats(b).nPulses, stats(b).meanDur_s, stats(b).medianDur_s); + end +end diff --git a/ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX_Datafile_Tools-main/MATLAB/ExtractFixedPulsesByBit.m b/ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX_Datafile_Tools-main/MATLAB/ExtractFixedPulsesByBit.m new file mode 100644 index 00000000..c608fabf --- /dev/null +++ b/ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX_Datafile_Tools-main/MATLAB/ExtractFixedPulsesByBit.m @@ -0,0 +1,73 @@ +function pulseTimes = ExtractFixedPulsesByBit(binName, path, bit, chunkSize, pulseLen_s) +% ExtractFixedPulsesByBit Extract start/end times using a chosen SY bit. +% pulseTimes = ExtractFixedPulsesByBit(binName, path, bit, chunkSize, pulseLen_s) +% - bit is 1-based bit index (1..16) +% - pulseLen_s default 0.400 + if nargin < 4 || isempty(chunkSize), chunkSize = 1e6; end + if nargin < 5 || isempty(pulseLen_s), pulseLen_s = 0.400; end + + meta = SGLX_readMeta.ReadMeta(binName, path); + sRate = SGLX_readMeta.SampRate(meta); + nSavedChans = str2double(meta.nSavedChans); + syncCh = nSavedChans; + bytesPerSamp = 2; + nFileBytes = str2double(meta.fileSizeBytes); + nSampTotal = nFileBytes / (nSavedChans * bytesPerSamp); + + fid = fopen(fullfile(path, binName), 'r'); + if fid < 0, error('Cannot open file %s', fullfile(path, binName)); end + + openStart = 0; + pulseList = zeros(0,2); + + nChunks = ceil(nSampTotal / chunkSize); + for c = 1:nChunks + startSamp0 = (c-1)*chunkSize; + nRead = min(chunkSize, nSampTotal - startSamp0); + + offset = (startSamp0 * nSavedChans + (syncCh-1)) * bytesPerSamp; + fseek(fid, offset, 'bof'); + + raw = fread(fid, nRead, 'int16', (nSavedChans-1)*bytesPerSamp); + if isempty(raw), break; end + sy = uint16(raw(:)'); + + vec = logical(bitget(sy, bit)); + prevVec = [openStart>0, vec(1:end-1)]; + risePos = find(prevVec==0 & vec==1); + fallPos = find(prevVec==1 & vec==0); + + % rises + for i = 1:numel(risePos) + if openStart == 0 + openStart = startSamp0 + risePos(i); % 0-based + else + % unexpected, close old at this rise + pulseList(end+1,:) = [openStart, startSamp0 + risePos(i)]; + openStart = startSamp0 + risePos(i); + end + end + + % falls + for i = 1:numel(fallPos) + if openStart ~= 0 + pulseList(end+1,:) = [openStart, startSamp0 + fallPos(i)]; + openStart = 0; + end + end + end + + % close outstanding open pulse if any, use fixed pulse length if necessary + if openStart ~= 0 + endSample = min(nSampTotal, openStart + round(pulseLen_s * sRate)); + pulseList(end+1,:) = [openStart, endSample]; + openStart = 0; + end + + fclose(fid); + + % If pulses are fixed length, optionally override ends: + pulseTimes = [(pulseList(:,1)-1)/sRate, (pulseList(:,1)-1)/sRate + pulseLen_s]; + + fprintf('Extracted %d pulses (bit %d). Returned times in seconds.\n', size(pulseTimes,1), bit); +end diff --git a/ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX_Datafile_Tools-main/MATLAB/ExtractSyncPulsesChunked.m b/ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX_Datafile_Tools-main/MATLAB/ExtractSyncPulsesChunked.m new file mode 100644 index 00000000..8b184f11 --- /dev/null +++ b/ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX_Datafile_Tools-main/MATLAB/ExtractSyncPulsesChunked.m @@ -0,0 +1,83 @@ +function pulseTimes = ExtractSyncPulsesChunked(binName, path, chunkSize) +% Extract start/end times of sync pulses without loading entire file. +% Works on very large SpikeGLX .ap.bin recordings. +% +% binName = filename of .ap.bin +% path = folder containing binName +% chunkSize = number of samples to read per chunk (e.g. 1e6) + + if nargin < 3 + chunkSize = 1e6; % default: 1 million samples (~33 s at 30 kHz) + end + + % --- Load metadata + meta = SGLX_readMeta.ReadMeta(binName, path); + sRate = SGLX_readMeta.SampRate(meta); + nSavedChans = str2double(meta.nSavedChans); + syncCh = nSavedChans; % last channel + bytesPerSamp = 2; % int16 + + % --- Figure out total samples + nFileBytes = str2double(meta.fileSizeBytes); + nSampTotal = nFileBytes / (nSavedChans * bytesPerSamp); + + % --- Open file + fid = fopen(fullfile(path, binName), 'r'); + if fid < 0, error('Cannot open file %s', binName); end + + % --- Loop through chunks + pulseTimes = []; + carryOver = 0; % last value of previous chunk + + nChunks = ceil(nSampTotal / chunkSize); + fprintf('Processing %d chunks...\n', nChunks); + + for c = 1:nChunks + startSamp = (c-1)*chunkSize; + nRead = min(chunkSize, nSampTotal - startSamp); + + % Position to sync channel for this chunk + offset = (startSamp * nSavedChans + (syncCh-1)) * bytesPerSamp; + fseek(fid, offset, 'bof'); + + % Read with stride to skip other channels + syncData = fread(fid, [1 nRead], ... + ['int16=>' 'double'], (nSavedChans-1)*bytesPerSamp); + + % Threshold + syncData = syncData > 0; + + % Add carry-over at the start + if carryOver + syncData = [carryOver syncData]; + startIdxOffset = startSamp - 1; % adjust for added sample + else + startIdxOffset = startSamp; + end + + % Find edges + dSync = diff([0 syncData 0]); + riseIdx = find(dSync == 1); + fallIdx = find(dSync == -1); + + % Convert to absolute sample indices + riseIdx = riseIdx + startIdxOffset; + fallIdx = fallIdx + startIdxOffset; + + % Convert to time in seconds + riseTime = (riseIdx - 1) / sRate; + fallTime = (fallIdx - 1) / sRate; + + % Store + nPulses = min(numel(riseTime), numel(fallTime)); + if nPulses > 0 + pulseTimes = [pulseTimes; [riseTime(1:nPulses)' fallTime(1:nPulses)']]; + end + + % Update carry-over + carryOver = syncData(end); + end + + fclose(fid); + fprintf('Found %d pulses total.\n', size(pulseTimes,1)); +end diff --git a/ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX_Datafile_Tools-main/MATLAB/SGLX_readMeta.m b/ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX_Datafile_Tools-main/MATLAB/SGLX_readMeta.m new file mode 100644 index 00000000..6190b70e --- /dev/null +++ b/ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX_Datafile_Tools-main/MATLAB/SGLX_readMeta.m @@ -0,0 +1,495 @@ +% ============================================================= +% Simple class of MATLAB functions deomonstrating +% how to read and manipulate SpikeGLX meta and binary files. +% When this file is included in the MATLAB path, the individual methods +% are called with SGLX_readMeta. +% +% The most important method in the class is ReadMeta(). +% The functions for reading binary data are not optimized for speed, +% and are included as a useful starting point. +% +classdef SGLX_readMeta + +methods(Static) + +% ========================================================= +% Parse ini file returning a structure whose field names +% are the metadata left-hand-side tags, and whose right- +% hand-side values are MATLAB strings. We remove any +% leading '~' characters from tags because MATLAB uses +% '~' as an operator. +% +% If you're unfamiliar with structures, the benefit +% is that after calling this function you can refer +% to metafile items by name. For example: +% +% meta.fileCreateTime // file create date and time +% meta.nSavedChans // channels per timepoint +% +% All of the values are MATLAB strings, but you can +% obtain a numeric value using str2double(meta.nSavedChans). +% More complicated parsing of values is demonstrated in the +% utility functions below. +% +function [meta] = ReadMeta(binName, path) + + % Create the matching metafile name + [~,name,~] = fileparts(binName); + metaName = strcat(name, '.meta'); + + % Parse ini file into cell entries C{1}{i} = C{2}{i} + fid = fopen(fullfile(path, metaName), 'r'); +% ------------------------------------------------------------- +% Need 'BufSize' adjustment for MATLAB earlier than 2014 +% C = textscan(fid, '%[^=] = %[^\r\n]', 'BufSize', 32768); + C = textscan(fid, '%[^=] = %[^\r\n]'); +% ------------------------------------------------------------- + fclose(fid); + + % New empty struct + meta = struct(); + + % Convert each cell entry into a struct entry + for i = 1:length(C{1}) + tag = C{1}{i}; + if tag(1) == '~' + % remake tag excluding first character + tag = sprintf('%s', tag(2:end)); + end + meta.(tag) = C{2}{i}; + end +end % ReadMeta + + +% ========================================================= +% Read nSamp timepoints from the binary file, starting +% at timepoint offset samp0. The returned array has +% dimensions [nChan,nSamp]. Note that nSamp returned +% is the lesser of: {nSamp, timepoints available}. +% +% IMPORTANT: samp0 and nSamp must be integers. +% +function dataArray = ReadBin(samp0, nSamp, meta, binName, path) + + nChan = str2double(meta.nSavedChans); + + nFileSamp = str2double(meta.fileSizeBytes) / (2 * nChan); + samp0 = max(samp0, 0); + nSamp = min(nSamp, nFileSamp - samp0); + + sizeA = [nChan, nSamp]; + + fid = fopen(fullfile(path, binName), 'rb'); + fseek(fid, samp0 * 2 * nChan, 'bof'); + dataArray = fread(fid, sizeA, 'int16=>double'); + fclose(fid); +end % ReadBin + + +% ========================================================= +% Return an array [lines X timepoints] of uint8 values for +% a specified set of digital lines. +% +% - dwReq is the one-based index into the saved file of the +% 16-bit word that contains the digital lines of interest. +% - dLineList is a zero-based list of one or more lines/bits +% to scan from word dwReq. +% +function digArray = ExtractDigital(dataArray, meta, dwReq, dLineList) + % Get channel index of requested digital word dwReq + switch meta.typeThis + case 'imec' + [AP, LF, SY] = SGLX_readMeta.ChannelCountsIM(meta); + if SY == 0 + fprintf('No imec sync channel saved\n'); + digArray = []; + return; + else + digCh = AP + LF + dwReq; + end + case 'nidq' + [MN,MA,XA,DW] = SGLX_readMeta.ChannelCountsNI(meta); + if dwReq > DW + fprintf('Maximum digital word in file = %d\n', DW); + digArray = []; + return; + else + digCh = MN + MA + XA + dwReq; + end + case 'obx' + [XA,DW,SY] = SGLX_readMeta.ChannelCountsOBX(meta); + if dwReq > DW + fprintf('Maximum digital word in file = %d\n', DW); + digArray = []; + return; + else + digCh = XA + dwReq; + end + end + [~,nSamp] = size(dataArray); + digArray = zeros(numel(dLineList), nSamp, 'uint8'); + for i = 1:numel(dLineList) + digArray(i,:) = bitget(dataArray(digCh,:), dLineList(i)+1, 'int16'); + end +end % ExtractDigital + + +% ========================================================= +% Return sample rate as double. +% +function srate = SampRate(meta) + switch meta.typeThis + case 'imec' + srate = str2double(meta.imSampRate); + case 'nidq' + srate = str2double(meta.niSampRate); + case 'obx' + srate = str2double(meta.obSampRate); + end +end % SampRate + + +% ========================================================= +% Return a multiplicative factor for converting 16-bit +% file data to voltage. This does not take gain into +% account. The full conversion with gain is: +% +% dataVolts = dataInt * fI2V / gain. +% +% Note that each channel may have its own gain. +% +function fI2V = Int2Volts(meta) + switch meta.typeThis + case 'imec' + if isfield(meta,'imMaxInt') + maxInt = str2num(meta.imMaxInt); + else + maxInt = 512; + end + fI2V = str2double(meta.imAiRangeMax) / maxInt; + case 'nidq' + fI2V = str2double(meta.niAiRangeMax) / str2double(meta.niMaxInt); + case 'obx' + fI2V = str2double(meta.obAiRangeMax) / str2double(meta.obMaxInt); + end +end % Int2Volts + + +% ========================================================= +% Return array of original channel IDs. As an example, +% suppose we want the imec gain for the ith channel stored +% in the binary data. A gain array can be obtained using +% ChanGainsIM() but we need an original channel index to +% do the look-up. Because you can selectively save channels +% the ith channel in the file isn't necessarily the ith +% acquired channel, so use this function to convert from +% ith stored to original index. +% +% Note: In SpikeGLX channels are 0-based, but MATLAB uses +% 1-based indexing, so we add 1 to the original IDs here. +% +function chans = OriginalChans(meta) + if strcmp(meta.snsSaveChanSubset, 'all') + chans = (1:str2double(meta.nSavedChans)); + else + chans = str2num(meta.snsSaveChanSubset); + chans = chans + 1; + end +end % OriginalChans + + +% ========================================================= +% Return counts of each imec channel type that compose +% the timepoints stored in binary file. +% +function [AP,LF,SY] = ChannelCountsIM(meta) + M = str2num(meta.snsApLfSy); + AP = M(1); + LF = M(2); + SY = M(3); +end % ChannelCountsIM + +% ========================================================= +% Return counts of each nidq channel type that compose +% the timepoints stored in binary file. +% +function [MN,MA,XA,DW] = ChannelCountsNI(meta) + M = str2num(meta.snsMnMaXaDw); + MN = M(1); + MA = M(2); + XA = M(3); + DW = M(4); +end % ChannelCountsNI + +% ========================================================= +% Return counts of each obx channel type that compose +% the timepoints stored in binary file. +% +function [XA,DW,SY] = ChannelCountsOBX(meta) + M = str2num(meta.snsXaDwSy); + XA = M(1); + DW = M(2); + SY = M(3); +end % ChannelCountsOBX + +% ========================================================= +% Return gain for ith channel stored in the nidq file. +% +% ichan is a saved channel index, rather than an original +% (acquired) index. +% +% Note: there is no matching function for OBX, because +% the gain is fixed at 1. +% +function gain = ChanGainNI(ichan, savedMN, savedMA, meta) + if ichan <= savedMN + gain = str2double(meta.niMNGain); + elseif ichan <= savedMN + savedMA + gain = str2double(meta.niMAGain); + else + gain = 1; + end +end % ChanGainNI + + +% ========================================================= +% Return gain arrays for imec channels. +% +% Index into these with original (acquired) channel IDs. +% +function [APgain,LFgain, APChan0_to_uV, LFChan0_to_uV] = ChanGainsIM(meta) + % list of probe types with NP 1.0 imro format + np1_imro = [0,1020,1030,1200,1100,1120,1121,1122,1123,1300]; + % number of channels acquired + acqCountList = str2num(meta.acqApLfSy); + + APgain = zeros(acqCountList(1)); % default type = float64 + LFgain = zeros(acqCountList(2)); % empty array for 2.0 + + if isfield(meta,'imDatPrb_type') + probeType = str2double(meta.imDatPrb_type); + else + probeType = 0; + end + + if ismember(probeType, np1_imro) + % imro + probe allows setting gain independently for each channel + % 3A or 3B data? + % 3A metadata has field "typeEnabled" which was replaced + % with "typeImEnabled" and "typeNiEnabled" in 3B. + % The 3B imro table has an additional field for the + % high pass filter enabled/disabled + if isfield(meta,'typeEnabled') + % 3A data + C = textscan(meta.imroTbl, '(%*s %*s %*s %d %d', ... + 'EndOfLine', ')', 'HeaderLines', 1 ); + else + % 3B data + C = textscan(meta.imroTbl, '(%*s %*s %*s %d %d %*s', ... + 'EndOfLine', ')', 'HeaderLines', 1 ); + end + APgain = double(cell2mat(C(1))); + LFgain = double(cell2mat(C(2))); + else + % get gain from imChan0apGain, if present + if isfield(meta,'imChan0apGain') + APgain = APgain + str2num(meta.imChan0apGain); + if acqCountList(2) > 0 + LFgain = LFgain + str2num(meta.imChan0lfGain); + end + elseif (probeType == 1110) + % active UHD, for metadata lacking imChan0apGain, get gain from + % imro table header + currList = sscanf(meta.imroTbl, '(%d,%d,%d,%d,%d'); + APgain = APgain + currList(4); + LFgain = LFgain + currList(5); + elseif (probeType == 21) || (probeType == 24) + % development NP 2.0; APGain = 80 for all AP + % return 0 for LFgain (no LF channels) + APgain = APgain + 80; + elseif (probeType == 2013) + % commercial NP 2.0; APGain = 80 for all AP + APgain = APgain + 100; + else + fprintf('unknown gain, setting APgain to 1\n'); + APgain = APgain + 1; + end + end + fI2V = SGLX_readMeta.Int2Volts(meta); + APChan0_to_uV = 1e6*fI2V/APgain(1); + if size(LFgain) > 0 + LFChan0_to_uV = 1e6*fI2V/LFgain(1); + else + LFChan0_to_uV = 0; + end +end % ChanGainsIM + + +% ========================================================= +% Having acquired a block of raw nidq data using ReadBin(), +% convert values to gain-corrected voltages. The conversion +% is only applied to the saved-channel indices in chanList. +% Remember saved-channel indices are in range [1:nSavedChans]. +% The dimensions of the dataArray remain unchanged. ChanList +% examples: +% +% [1:MN] % all MN chans (MN from ChannelCountsNI) +% [2,6,20] % just these three channels +% +% +function dataArray = GainCorrectNI(dataArray, chanList, meta) + + [MN,MA] = SGLX_readMeta.ChannelCountsNI(meta); + fI2V = SGLX_readMeta.Int2Volts(meta); + + for i = 1:length(chanList) + j = chanList(i); % index into timepoint + conv = fI2V / SGLX_readMeta.ChanGainNI(j, MN, MA, meta); + dataArray(j,:) = dataArray(j,:) * conv; + end +end % GainCorrectNI + +% ========================================================= +% Having acquired a block of raw obx data using ReadBin(), +% convert values voltages. The conversion is only applied +% to the saved-channel indices in chanList. The conversion +% factor is the same for all channels, because the gain is fixed. +% Remember saved-channel indices are in range [1:nSavedChans]. +% The dimensions of the dataArray remain unchanged. ChanList +% examples: +% +% [2,6,20] % just these three channels +% +% +function dataArray = GainCorrectOBX(dataArray, chanList, meta) + + fI2V = SGLX_readMeta.Int2Volts(meta); + + for i = 1:length(chanList) + j = chanList(i); % index into timepoint + dataArray(j,:) = dataArray(j,:) * fI2V; + end +end % GainCorrectOBX + + +% ========================================================= +% Having acquired a block of raw imec data using ReadBin(), +% convert values to gain-corrected voltages. The conversion +% is only applied to the saved-channel indices in chanList. +% Remember saved-channel indices are in range [1:nSavedChans]. +% The dimensions of the dataArray remain unchanged. ChanList +% examples: +% +% [1:AP] % all AP chans (AP from ChannelCountsIM) +% [2,6,20] % just these three channels +% +function dataArray = GainCorrectIM(dataArray, chanList, meta) + + % Look up gain with acquired channel ID + chans = SGLX_readMeta.OriginalChans(meta); + [APgain,LFgain] = SGLX_readMeta.ChanGainsIM(meta); + nAP = length(APgain); + nNu = nAP * 2; + + % Common conversion factor + fI2V = SGLX_readMeta.Int2Volts(meta); + + for i = 1:length(chanList) + j = chanList(i); % index into timepoint + k = chans(j); % acquisition index + if k <= nAP + conv = fI2V / APgain(k); + elseif k <= nNu + conv = fI2V / LFgain(k - nAP); + else + continue; + end + dataArray(j,:) = dataArray(j,:) * conv; + end +end % GainCorrectIM + +% ========================================================= +% Return array of survey bank times +% +% +function bankTimes = svyBankTimes(meta) + + % Look up gain with acquired channel ID + C = textscan(meta.svySBTT, '(%d %d %d %d', ... + 'EndOfLine', ')' ); + + nBank = numel(C{1}) + 1; % bank0/shank0 is at time = 0 + srate = SGLX_readMeta.SampRate(meta); + + bankTimes = zeros([nBank,4], "double"); + bankTimes(2:nBank,1) = double(C{1}); + bankTimes(2:nBank,2) = double(C{2}); + bankTimes(2:nBank,3) = double(C{3})/srate; + bankTimes(2:nBank,4) = double(C{4})/srate; + +end % svyBankTimes + +% ========================================================= +% Write metadata file using values in meta structure +% +% +function writeMeta(meta, newPath) + + % Write out metadata file. Tag order matches order of addition to + % structure when read + fmeta = fopen( newPath, 'w'); + tildeTags{1} = 'muxTbl'; + tildeTags{2} = 'imroTbl'; + tildeTags{3} = 'snsChanMap'; + tildeTags{4} = 'snsGeomMap'; + tildeTags{5} = 'snsShankMap'; + + fn = fieldnames(meta); + for i = 1:numel(fieldnames(meta)) + currTag = fn{i}; + tagFound = find(strcmp(tildeTags, currTag)); + if isempty(tagFound) + currLine = sprintf('%s=%s',currTag,meta.(currTag)); + fprintf(fmeta,'%s\n',currLine); + else + currLine = sprintf('~%s=%s',currTag,meta.(currTag)); + fprintf(fmeta,'%s\n',currLine); + end + end + +end % writeMeta + +% =========================================================== +% parse SGLX imec filename with or without extension, return +% runName, +% gateStr, e.g. 'g0' +% triggerStr, e.g. 't0' or 'tcat' +% probeStr, e.g. 'imec0' +% streamStr, e.g. 'ap' or 'lf' +% +% +function [runName,gateStr,triggerStr,probeStr,streamStr] = parseFileName(fileName) + + % Remove extension, if present + if endsWith(fileName, '.bin') + fileName = fileName(1:length(fileName)-4); + elseif endsWith(fileName, '.meta') + fileName = fileName(1:length(fileName)-5); + end + + % Find periods and underscores + perPos = strfind(fileName,'.'); + usPos = strfind(fileName,'_'); + nPer = length(perPos); + nUS = length(usPos); + streamStr = fileName(perPos(nPer)+1:end); + probeStr = fileName(perPos(nPer-1)+1:perPos(nPer)-1); + triggerStr = fileName(usPos(nUS)+1:perPos(nPer-1)-1); + gateStr = fileName(usPos(nUS-1)+1:usPos(nUS)-1); + runName = fileName(1:usPos(nUS-1)-1); + +end % parseFileName + +end % SGLX_readMeta methods + +end % SGLX_readMeta classdef diff --git a/ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX_Datafile_Tools-main/MATLAB/ks25_phy_toBinary.m b/ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX_Datafile_Tools-main/MATLAB/ks25_phy_toBinary.m new file mode 100644 index 00000000..41ca862b --- /dev/null +++ b/ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX_Datafile_Tools-main/MATLAB/ks25_phy_toBinary.m @@ -0,0 +1,261 @@ +function ks25_phy_toBinary( varargin ) + +% Read drift corrected data from ks25, unwhiten and rescale for input +% into C_Waves. Requires a full phy folder of results. + +% IMPORTANT NOTE: the preprocessed file only contains channels that KS2.5 +% uses for sorting. Channels with connected=0 in the chanMap file OR +% eliminated due to low spike count (ops.minfr_goodchannels > 0) will not +% be included. The information in the phy output is used to generate a new +% SGLX metadata file which includes the correct channels. + +% Another important note: The correct information to unwhiten and rescale +% the KS2.5 drift-corrected output is ONLY available in the rez2.mat output +% file. The whitening.mat.npy and whitening_mat_inv.npy are just identity +% matrices. Voltage values will be incorrectly scaled and the spatial +% fields are post-whitening if the rez2.mat file is unavailable. + +% Requires: +% SGLX_readMeta class: https://github.com/jenniferColonell/SpikeGLX_Datafile_Tools/ +% C_Waves: https://billkarsh.github.io/SpikeGLX/#post-processing-tools +% npy-matlab: https://github.com/kwikteam/npy-matlab/tree/master/npy-matlab +% + +% user parameters +modStr = 'ksprocUW'; % will be added to the run name of the output binary and metadata +bRunCWaves = 0; % whether to run C_Waves +exePath = 'C:\Users\colonellj\Documents\C_Waves-win'; % path to C_Waves directory on local machine + +if isempty(varargin ) + phyDir = uigetdir([],'select phy directory'); + [binName,binPath] = uigetfile({'*.bin';'*.dat'},'Select PROCESSED binary file'); + fprocFullPath = fullfile(binPath,binName); + [metaName, mPath] = uigetfile('*.meta','Select ORIGINAL metadata file'); + metaName = metaName(1:length(metaName)-5); % remove extension +else + % called from another function + inputCell = varargin(1); + phyDir = inputCell{1}; + inputCell = varargin(2); + fprocFullPath = inputCell{1}; + inputCell = varargin(3); + origMetaFullpath = inputCell{1}; + [mPath, metaName, ~] = fileparts(origMetaFullpath); +end + + +[baseName,gateStr,triggerStr,probeStr,~] = SGLX_readMeta.parseFileName(metaName); + +outName = sprintf( '%s_%s_%s_%s.%s', baseName, modStr, gateStr, triggerStr, probeStr); +binOutName = sprintf( '%s%s', outName, '.ap.bin'); +metaOutName = sprintf( '%s%s', outName, '.ap.meta'); +outFullPath = fullfile(phyDir, binOutName); + + +% KS2.5 makes a readable binary from its datashifted data. Rather +% than overlapping the batches, it reads in some extra points for filtering +% and then trims them back off. ops.ntbuff = ntb +% read NTbuff = NT + 3*ntb points +% for a standard batch (neither first nor last) read: +% --ntb points overlapping the last batch +% --NT points that belong to this batch +% --2*ntb more points; first set of ntb will be blended with next +% batch, 2nd ntb just filtering buffer +% After fitering, points ntb+1:ntb are blended with "dat_prev" which is +% NT+ntb+1:NT+2*ntb saved from the previous batch. +% Batch zero gets ntb zeros prepended to its data and blended with the +% initialized dat_prev (also zeros). +% After filtering, the data is whitened and then transposed back to NChan +% rows by NT columns to save. When these batches are read for sorting in +% learnTemplates, the data is transposed after reading. + +% read whitening matrix and chanMap from phy directory +chanMap = readNPY(fullfile(phyDir,'channel_map.npy')); % 0 based indicies of the channels in the output file +Nchan = numel(chanMap); + +if isfile(fullfile(phyDir,'rez2.mat')) + rez = load(fullfile(phyDir,'rez2.mat')); + Wrot = rez.rez.Wrot; +else + fprintf('Wrot unavailable, will use identity matrix\n'); + fprintf('Voltages will NOT be correct.\n') + Wrot = eye(Nchan); + % In the release version of KS2.5, the whitening matrix saved in + % whitening_mat.npy is just the identity matrix/rez.ops.scaleProc. + % whitening_mat_inv.npy is the inverse of whitening_mat. + % Result: whitening_mat.npy is closer to being the inverse of the + % whitening matrix than whitening_mat_inv.npy, but NEITHER is correct. + % To obtain correctly scaled, unwhitened waveforms from the drift + % corrected data, it is necessary to save the rez2.mat file in the + % script calling KS2.5. +end + + +% get number of data points +fp = dir(fprocFullPath); +procSizeBytes = fp.bytes %double +Nchan +totNT = procSizeBytes/(Nchan*2) %total number of time points +fprintf('Total time points in binary %d\n', totNT); +if totNT ~= floor(totNT) + fprintf('binary doesn not match phy output'); + return; +end + +fid = fopen(fprocFullPath, 'r'); +fidW = fopen(outFullPath, 'w'); % open for writing processed data, transposed + +% NT is set here to the KS default, but the results are independent of the +% value of NT used by KS2.5. +% NOTE: this is not true for KS2.0. +NT = 65600; + +Nbatch = floor(totNT/NT); +batchstart = 0:NT:NT*Nbatch; % batches start at these timepoints +for ibatch = 1:Nbatch + offset = 2 * Nchan*batchstart(ibatch); % binary file offset in bytes + fseek(fid, offset, 'bof'); + dat = fread(fid, [Nchan NT], '*int16'); + dat = int16(single(dat')/Wrot); + fwrite(fidW, dat', 'int16'); % write transposed batch to binary, these chunks are in order +end +% get the end of the file +NTlast = totNT - Nbatch*NT; +offset = 2*Nchan*batchstart(Nbatch+1); +fseek(fid, offset, 'bof'); +dat = fread(fid, [Nchan NTlast], '*int16'); +dat = int16(single(dat')/Wrot); +fwrite(fidW, dat', 'int16'); +fclose(fid); +fclose(fidW); + +fp = dir(outFullPath); +newBytes = uint64(fp.bytes); + +% get binaary name and path from origMetaFullPath +origBinName = sprintf('%s.bin', metaName); +origMeta = SGLX_readMeta.ReadMeta(origBinName, mPath); + +newMeta = origMeta; +newMeta.fileName = sprintf('%s', outFullPath); +newMeta.fileSizeBytes = sprintf('%ld', newBytes); +newMeta.nSavedChans = sprintf('%d', Nchan); % read from phy channel_map.npy; +newMeta.snsApLfSy = sprintf('%d,0,0', Nchan); + +% map channels to original channels. This is required for correct +% intepretation of gains read from the imro table for NP 1.0-like probes + +origChans = SGLX_readMeta.OriginalChans(origMeta) - 1; % zero based indicies of channels in the original binary +origChansNew = origChans(chanMap+1); % zero based indicies of channels in the processed binary +newMeta.snsSaveChanSubset = build_sep_str(origChansNew,','); + +% snsChanMap, snsGeomMap and snsShankMap all include only the saved +% channels. Need to get the array of entries from the string, index to +% get the channels included in the output file, +mapTags = {'snsChanMap', 'snsGeomMap', 'snsShankMap'}; +for i = 1:numel(mapTags) + if isfield(origMeta,mapTags{i}) + currMap = origMeta.(mapTags{i}); + mapArr = split(currMap,')'); + mapArr(numel(mapArr)) = []; % removing last entry from final ')' + % mapArr is original Nchan + 1 long, first entry is the header + % each entry is '(' plus the string inside the parens. + % Entries in the new map are the header (entry 1) + chanMap entries + 2 + new_map_ind = zeros([Nchan+1,1]); + new_map_ind(1) = 1; % for header entry + new_map_ind(2:Nchan+1) = chanMap(1:Nchan) + 2; + mapArrNew = mapArr(new_map_ind); + if strcmp(mapTags{i},'snsChanMap') + % chanMap header to match saved entries + mapArrNew(1) = {sprintf('(%d,0,0)', Nchan)}; + end + mapNewStr = build_sep_str(mapArrNew,')'); + newMeta.(mapTags{i}) = sprintf('%s)',mapNewStr); % adding final close paren + end +end + +SGLX_readMeta.writeMeta(newMeta, fullfile(phyDir,metaOutName) ); +fprintf( 'Output file has %d channels\n', Nchan ); + +if bRunCWaves + spkFilePath = fullfile(phyDir,'spike_times.npy'); + spkCluPath = fullfile(phyDir,'spike_clusters.npy'); + clusTablePath = fullfile(phyDir,'clus_Table.npy'); + if ~isfile(clusTablePath) + phy_to_clusTable(phyDir, Wrot); + end + call_CWaves( phyDir, outFullPath, spkFilePath, spkCluPath, clusTablePath, exePath ); +end + +end + +function sepStr = build_sep_str(m, sep) + % for a 1D array m and separator setp, build the string + ne = numel(m); + sepStr = ''; + switch class(m) + case 'cell' + for i = 1:ne-1 + % get the contents of the cell + cellElem = m(i); + cellStr = cellElem{1}; + sepStr = sprintf('%s%s%s',sepStr,cellStr,sep); + end + % last element + cellElem = m(ne); + cellStr = cellElem{1}; + sepStr = sprintf('%s%s',sepStr,cellStr); + otherwise + for i = 1:ne-1 + sepStr = sprintf('%s%g%s',sepStr,m(i),sep); + end + % last element + sepStr = sprintf('%s%g',sepStr,m(ne)); + end +end + +function phy_to_clusTable(phyDir, Wrot) +% buld clusTable for C_Waves from information in phy directory +% clus table is an npy file containing: +% 2-col table (uint32): {num_spike, pk-chan} + templates = readNPY(fullfile(phyDir,'templates.npy')); + templates_unwh = zeros(size(templates)); + [nUnit,~,~] = size(templates); + for i = 1:nUnit + templates_unwh(i,:,:) = squeeze(templates(i,:,:))/Wrot; + end + pp_all = squeeze(max(templates_unwh,[],2) - min(templates_unwh,[],2)); + [~,maxChan] = max(pp_all,[],2); + spikeClusters = readNPY(fullfile(phyDir,'spike_clusters.npy')); + [counts,labels] = groupcounts(spikeClusters); + clu_arr = zeros([nUnit,2],'uint32'); + clu_arr(labels+1,1) = uint32(counts); + clu_arr(:,2) = uint32(maxChan); + writeNPY(clu_arr, fullfile(phyDir,'clus_Table.npy')) + +end + +function call_CWaves( inputPath, binPath, spkFilePath, spkCluPath, clusTablePath, exePath ) + +% build command line to call CWaves + + args = sprintf("-spikeglx_bin=%s", binPath); + args = sprintf("%s -clus_table_npy=%s", args, clusTablePath); + args = sprintf("%s -clus_time_npy=%s", args, spkFilePath); + args = sprintf("%s -clus_lbl_npy=%s", args, spkCluPath); + args = sprintf("%s -dest=%s", args, inputPath); + args = sprintf("%s -prefix=ksproc -samples_per_spike=82 -pre_samples=20 -num_spikes=1000 -snr_radius=8 -snr_radius_um=140", args); + + cwaves_cmd = sprintf("%s %s", fullfile(exePath,'runit.bat'), args); + fprintf("%s\n", cwaves_cmd); + status = system(cwaves_cmd) + +% typical command line: +% -spikeglx_bin=\\dm11\apig\C_waves_test_data\SC024_092319_NP1.0_Midbrain_g0_tcat.imec0.ap.bin ^ +% -clus_table_npy=\\dm11\apig\C_waves_test_data\clus_Table.npy ^ +% -clus_time_npy=\\dm11\apig\C_waves_test_data\spike_times.npy ^ +% -clus_lbl_npy=\\dm11\apig\C_waves_test_data\spike_clusters.npy ^ +% -dest=\\dm11\apig\C_waves_test_data\out ^ +% -samples_per_spike=82 -pre_samples=20 -num_spikes=1000 -snr_radius=8 -snr_radius_um=140) +end + diff --git a/ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX_Datafile_Tools-main/MATLAB/loadSMAfromAP.m b/ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX_Datafile_Tools-main/MATLAB/loadSMAfromAP.m new file mode 100644 index 00000000..f747c080 --- /dev/null +++ b/ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX_Datafile_Tools-main/MATLAB/loadSMAfromAP.m @@ -0,0 +1,42 @@ +function DemoReadSync_IMEC() + + % Ask user for binary file + [binName, path] = uigetfile('*.bin', 'Select .ap.bin File'); + if isequal(binName,0), return; end + + % Parse corresponding .meta file + meta = SGLX_readMeta.ReadMeta(binName, path); + + % Sampling rate + sRate = SGLX_readMeta.SampRate(meta); + + % Read first 2 seconds of data + nSamp = floor(2.0 * sRate); + dataArray = SGLX_readMeta.ReadBin(0, nSamp, meta, binName, path); + + % --- Channel indices --- + nSavedChans = str2double(meta.nSavedChans); % should be 385 + syncCh = nSavedChans; % last channel is sync + apCh = 1; % example: channel 1 (first electrode) + + % Gain-correct AP channel + apData = SGLX_readMeta.GainCorrectIM(dataArray(apCh,:), apCh, meta); + + % Sync channel (already digital, no gain correction) + syncData = dataArray(syncCh,:); + + % --- Plot --- + figure; + subplot(2,1,1); + plot((0:nSamp-1)/sRate, 1e6*apData); + xlabel('Time (s)'); + ylabel('Voltage (uV)'); + title('AP channel 1'); + + subplot(2,1,2); + plot((0:nSamp-1)/sRate, syncData); + xlabel('Time (s)'); + ylabel('Sync (raw)'); + title('Sync channel'); + +end diff --git a/ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX_Datafile_Tools-main/Python/DemoReadSGLXData/__init__.py b/ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX_Datafile_Tools-main/Python/DemoReadSGLXData/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX_Datafile_Tools-main/Python/DemoReadSGLXData/readSGLX.py b/ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX_Datafile_Tools-main/Python/DemoReadSGLXData/readSGLX.py new file mode 100644 index 00000000..4f8ef036 --- /dev/null +++ b/ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX_Datafile_Tools-main/Python/DemoReadSGLXData/readSGLX.py @@ -0,0 +1,460 @@ +# -*- coding: utf-8 -*- +""" +Requires python 3 + +The main() function at the bottom of this file can run from an +interpreter, or, the helper functions can be imported into a +new module or Jupyter notebook (an example is included). + +Simple helper functions and python dictionary demonstrating +how to read and manipulate SpikeGLX meta and binary files. + +The most important part of the demo is readMeta(). +Please read the comments for that function. Use of +the 'meta' dictionary will make your data handling +much easier! + +""" +import numpy as np +import matplotlib.pyplot as plt +from pathlib import Path +from tkinter import Tk +from tkinter import filedialog + + +# Parse ini file returning a dictionary whose keys are the metadata +# left-hand-side-tags, and values are string versions of the right-hand-side +# metadata values. We remove any leading '~' characters in the tags to match +# the MATLAB version of readMeta. +# +# The string values are converted to numbers using the "int" and "float" +# functions. Note that python 3 has no size limit for integers. +# +def readMeta(binFullPath): + metaName = binFullPath.stem + ".meta" + metaPath = Path(binFullPath.parent / metaName) + metaDict = {} + if metaPath.exists(): + # print("meta file present") + with metaPath.open() as f: + mdatList = f.read().splitlines() + # convert the list entries into key value pairs + for m in mdatList: + csList = m.split(sep='=') + if csList[0][0] == '~': + currKey = csList[0][1:len(csList[0])] + else: + currKey = csList[0] + metaDict.update({currKey: csList[1]}) + else: + print("no meta file") + return(metaDict) + + +# Return sample rate as python float. +# On most systems, this will be implemented as C++ double. +# Use python command sys.float_info to get properties of float on your system. +# +def SampRate(meta): + if meta['typeThis'] == 'imec': + srate = float(meta['imSampRate']) + elif meta['typeThis'] == 'nidq': + srate = float(meta['niSampRate']) + elif meta['typeThis'] == 'obx': + srate = float(meta['obSampRate']) + else: + print('Error: unknown stream type') + srate = 1 + + return(srate) + + +# Return a multiplicative factor for converting 16-bit file data +# to voltage. This does not take gain into account. The full +# conversion with gain is: +# dataVolts = dataInt * fI2V / gain +# Note that each channel may have its own gain. +# +def Int2Volts(meta): + if meta['typeThis'] == 'imec': + if 'imMaxInt' in meta: + maxInt = int(meta['imMaxInt']) + else: + maxInt = 512 + fI2V = float(meta['imAiRangeMax'])/maxInt + elif meta['typeThis'] == 'nidq': + maxInt = int(meta['niMaxInt']) + fI2V = float(meta['niAiRangeMax'])/maxInt + elif meta['typeThis'] == 'obx': + maxInt = int(meta['obMaxInt']) + fI2V = float(meta['obAiRangeMax'])/maxInt + else: + print('Error: unknown stream type') + fI2V = 1 + + return(fI2V) + + +# Return array of original channel IDs. As an example, suppose we want the +# imec gain for the ith channel stored in the binary data. A gain array +# can be obtained using ChanGainsIM(), but we need an original channel +# index to do the lookup. Because you can selectively save channels, the +# ith channel in the file isn't necessarily the ith acquired channel. +# Use this function to convert from ith stored to original index. +# Note that the SpikeGLX channels are 0 based. +# +def OriginalChans(meta): + if meta['snsSaveChanSubset'] == 'all': + # output = int32, 0 to nSavedChans - 1 + chans = np.arange(0, int(meta['nSavedChans'])) + else: + # parse the snsSaveChanSubset string + # split at commas + chStrList = meta['snsSaveChanSubset'].split(sep=',') + chans = np.arange(0, 0) # creates an empty array of int32 + for sL in chStrList: + currList = sL.split(sep=':') + if len(currList) > 1: + # each set of contiguous channels specified by + # chan1:chan2 inclusive + newChans = np.arange(int(currList[0]), int(currList[1])+1) + else: + newChans = np.arange(int(currList[0]), int(currList[0])+1) + chans = np.append(chans, newChans) + return(chans) + + +# Return counts of each nidq channel type that composes the timepoints +# stored in the binary file. +# +def ChannelCountsNI(meta): + chanCountList = meta['snsMnMaXaDw'].split(sep=',') + MN = int(chanCountList[0]) + MA = int(chanCountList[1]) + XA = int(chanCountList[2]) + DW = int(chanCountList[3]) + return(MN, MA, XA, DW) + + +# Return counts of each imec channel type that composes the timepoints +# stored in the binary files. +# +def ChannelCountsIM(meta): + chanCountList = meta['snsApLfSy'].split(sep=',') + AP = int(chanCountList[0]) + LF = int(chanCountList[1]) + SY = int(chanCountList[2]) + return(AP, LF, SY) + +# Return counts of each obx channel type that composes the timepoints +# stored in the binary files. +# +def ChannelCountsOBX(meta): + chanCountList = meta['snsXaDwSy'].split(sep=',') + XA = int(chanCountList[0]) + DW = int(chanCountList[1]) + SY = int(chanCountList[2]) + return(XA, DW, SY) + + +# Return gain for ith channel stored in nidq file. +# ichan is a saved channel index, rather than the original (acquired) index. +# +# Note: there is nomatching function for OBX, where the gain is fixed = 1 +def ChanGainNI(ichan, savedMN, savedMA, meta): + if ichan < savedMN: + gain = float(meta['niMNGain']) + elif ichan < (savedMN + savedMA): + gain = float(meta['niMAGain']) + else: + gain = 1 # non multiplexed channels have no extra gain + return(gain) + + +# Return gain for imec channels. +# Index into these with the original (acquired) channel IDs. +# +def ChanGainsIM(meta): + # list of probe types with NP 1.0 imro format + np1_imro = [0,1020,1030,1200,1100,1120,1121,1122,1123,1300] + # number of channels acquired + acqCountList = meta['acqApLfSy'].split(sep=',') + APgain = np.zeros(int(acqCountList[0])) # default type = float64 + LFgain = np.zeros(int(acqCountList[1])) # empty array for 2.0 + + if 'imDatPrb_type' in meta: + probeType = int(meta['imDatPrb_type']) + else: + probeType = 0 + + if sum(np.isin(np1_imro, probeType)): + # imro + probe allows setting gain independently for each channel + imroList = meta['imroTbl'].split(sep=')') + # One entry for each channel plus header entry, + # plus a final empty entry following the last ')' + for i in range(0, int(acqCountList[0])): + currList = imroList[i+1].split(sep=' ') + APgain[i] = float(currList[3]) + LFgain[i] = float(currList[4]) + else: + # get gain from imChan0apGain + if 'imChan0apGain' in meta: + APgain = APgain + float(meta['imChan0apGain']) + if int(acqCountList[1]) > 0: + LFgain = LFgain + float(meta['imChan0lfGain']) + elif (probeType == 1110): + # active UHD, for metadata lacking imChan0apGain, get gain from + # imro table header + imroList = meta['imroTbl'].split(sep=')') + currList = imroList[0].split(sep=',') + APgain = APgain + float(currList[3]) + LFgain = LFgain + float(currList[4]) + elif (probeType == 21) or (probeType == 24): + # development NP 2.0; APGain = 80 for all AP + # return 0 for LFgain (no LF channels) + APgain = APgain + 80 + elif (probeType == 2013): + # commercial NP 2.0; APGain = 100 for all AP + APgain = APgain + 100 + else: + print('unknown gain, setting APgain to 1') + APgain = APgain + 1 + fI2V = Int2Volts(meta) + APChan0_to_uV = 1e6*fI2V/APgain[0] + if LFgain.size > 0: + LFChan0_to_uV = 1e6*fI2V/LFgain[0] + else: + LFChan0_to_uV = 0 + return(APgain, LFgain, APChan0_to_uV, LFChan0_to_uV) + + +# Having accessed a block of raw nidq data using makeMemMapRaw, convert +# values to gain-corrected voltage. The conversion is only applied to the +# saved-channel indices in chanList. Remember, saved-channel indices are +# in the range [0:nSavedChans-1]. The dimensions of dataArray remain +# unchanged. ChanList examples: +# [0:MN-1] all MN channels (MN from ChannelCountsNI) +# [2,6,20] just these three channels (zero based, as they appear in SGLX). +# +def GainCorrectNI(dataArray, chanList, meta): + MN, MA, XA, DW = ChannelCountsNI(meta) + fI2V = Int2Volts(meta) + # print statements used for testing... + # print("NI fI2V: %.3e" % (fI2V)) + # print("NI ChanGainNI: %.3f" % (ChanGainNI(0, MN, MA, meta))) + + # make array of floats to return. dataArray contains only the channels + # in chanList, so output matches that shape + convArray = np.zeros(dataArray.shape, dtype=float) + for i in range(0, len(chanList)): + j = chanList[i] # index in saved data + conv = fI2V/ChanGainNI(j, MN, MA, meta) + # dataArray contains only the channels in chanList + convArray[i, :] = dataArray[i, :] * conv + return(convArray) + +# Having accessed a block of raw obx data using makeMemMapRaw, convert +# values to volts. The conversion is only applied to the +# saved-channel indices in chanList. Remember, saved-channel indices are +# in the range [0:nSavedChans-1]. The dimensions of dataArray remain +# [2,6,20] just these three channels (zero based, as they appear in SGLX). +# +def GainCorrectOBX(dataArray, chanList, meta): + + fI2V = Int2Volts(meta) + + # make array of floats to return. dataArray contains only the channels + # in chanList, so output matches that shape + convArray = np.zeros(dataArray.shape, dtype=float) + for i in range(0, len(chanList)): + # dataArray contains only the channels in chanList + convArray[i, :] = dataArray[i, :] * fI2V + return(convArray) + + +# Having accessed a block of raw imec data using makeMemMapRaw, convert +# values to gain corrected voltages. The conversion is only applied to +# the saved-channel indices in chanList. Remember saved-channel indices +# are in the range [0:nSavedChans-1]. The dimensions of the dataArray +# remain unchanged. ChanList examples: +# [0:AP-1] all AP channels +# [2,6,20] just these three channels (zero based) +# Remember that for an lf file, the saved channel indices (fetched by +# OriginalChans) will be in the range 384-767 for a standard 3A or 3B probe. +# +def GainCorrectIM(dataArray, chanList, meta): + # Look up gain with acquired channel ID + chans = OriginalChans(meta) + APgain, LFgain, _, _ = ChanGainsIM(meta) + nAP = len(APgain) + nNu = nAP * 2 + + # Common conversion factor + fI2V = Int2Volts(meta) + + # make array of floats to return. dataArray contains only the channels + # in chanList, so output matches that shape + convArray = np.zeros(dataArray.shape, dtype='float') + for i in range(0, len(chanList)): + j = chanList[i] # index into timepoint + k = chans[j] # acquisition index + if k < nAP: + conv = fI2V / APgain[k] + elif k < nNu: + conv = fI2V / LFgain[k - nAP] + else: + conv = 1 + # The dataArray contains only the channels in chanList + convArray[i, :] = dataArray[i, :]*conv + return(convArray) + +# Return memmap for the raw data +# Fortran ordering is used to match the MATLAB version +# of these tools. +# +def makeMemMapRaw(binFullPath, meta): + nChan = int(meta['nSavedChans']) + nFileSamp = int(int(meta['fileSizeBytes'])/(2*nChan)) + print("nChan: %d, nFileSamp: %d" % (nChan, nFileSamp)) + rawData = np.memmap(binFullPath, dtype='int16', mode='r', + shape=(nChan, nFileSamp), offset=0, order='F') + return(rawData) + + +# Return an array [lines X timepoints] of uint8 values for a +# specified set of digital lines. +# +# - dwReq is the zero-based index into the saved file of the +# 16-bit word that contains the digital lines of interest. +# - dLineList is a zero-based list of one or more lines/bits +# to scan from word dwReq. +# +def ExtractDigital(rawData, firstSamp, lastSamp, dwReq, dLineList, meta): + # Get channel index of requested digital word dwReq + if meta['typeThis'] == 'imec': + AP, LF, SY = ChannelCountsIM(meta) + if SY == 0: + print("No imec sync channel saved.") + digArray = np.zeros((0), 'uint8') + return(digArray) + else: + digCh = AP + LF + dwReq + elif meta['typeThis'] == 'nidq': + MN, MA, XA, DW = ChannelCountsNI(meta) + if dwReq > DW-1: + print("Maximum digital word in file = %d" % (DW-1)) + digArray = np.zeros((0), 'uint8') + return(digArray) + else: + digCh = MN + MA + XA + dwReq + elif meta['typeThis'] == 'obx': + XA, DW, SY = ChannelCountsOBX(meta) + if dwReq > DW-1: + print("Maximum digital word in file = %d" % (DW-1)) + digArray = np.zeros((0), 'uint8') + return(digArray) + else: + digCh = XA + dwReq + else: + print('unknown data stream') + + selectData = np.ascontiguousarray(rawData[digCh, firstSamp:lastSamp+1], 'int16') + nSamp = lastSamp-firstSamp + 1 + + # unpack bits of selectData; unpack bits works with uint8 + # original data is int16 + bitWiseData = np.unpackbits(selectData.view(dtype='uint8')) + # output is 1-D array, nSamp*16. Reshape and transpose + bitWiseData = np.transpose(np.reshape(bitWiseData, (nSamp, 16))) + + nLine = len(dLineList) + digArray = np.zeros((nLine, nSamp), 'uint8') + for i in range(0, nLine): + byteN, bitN = np.divmod(dLineList[i], 8) + targI = byteN*8 + (7 - bitN) + digArray[i, :] = bitWiseData[targI, :] + return(digArray) + + +# Sample calling program to get a file from the user, +# read metadata fetch sample rate, voltage conversion +# values for this file and channel, and plot a small range +# of voltages from a single channel. +# Note that this code merely demonstrates indexing into the +# data file, without any optimization for efficiency. +# +def main(): + + # Get file from user + root = Tk() # create the Tkinter widget + root.withdraw() # hide the Tkinter root window + + # Windows specific; forces the window to appear in front + root.attributes("-topmost", True) + + binFullPath = Path(filedialog.askopenfilename(title="Select binary file")) + root.destroy() # destroy the Tkinter widget + + # Other parameters about what data to read + tStart = 0 + tEnd = 2 + dataType = 'A' # 'A' for analog, 'D' for digital data + + # For analog channels: zero-based index of a channel to extract, + # gain correct and plot (plots first channel only) + chanList = [0] + + # For a digital channel: zero based index of the digital word in + # the saved file. For imec data there is never more than one digital word. + dw = 0 + + # Zero-based Line indices to read from the digital word and plot. + # For 3B2 imec data: the sync pulse is stored in line 6. + dLineList = [0, 1, 6] + + # Read in metadata; returns a dictionary with string for values + meta = readMeta(binFullPath) + + # parameters common to NI and imec data + sRate = SampRate(meta) + firstSamp = int(sRate*tStart) + lastSamp = int(sRate*tEnd) + # array of times for plot + tDat = np.arange(firstSamp, lastSamp+1, dtype='uint64') + tDat = 1000*tDat/sRate # plot time axis in msec + + rawData = makeMemMapRaw(binFullPath, meta) + + if dataType == 'A': + selectData = rawData[chanList, firstSamp:lastSamp+1] + if meta['typeThis'] == 'imec': + # apply gain correction and convert to uV + convData = 1e6*GainCorrectIM(selectData, chanList, meta) + elif meta['typeThis'] == 'nidq': + MN, MA, XA, DW = ChannelCountsNI(meta) + # print("NI channel counts: %d, %d, %d, %d" % (MN, MA, XA, DW)) + # apply gain correction and convert to mV + convData = 1e3*GainCorrectNI(selectData, chanList, meta) + elif meta['typeThis'] == 'obx': + # Gain correct is just conversion to volts + convData = 1e3*GainCorrectOBX(selectData, chanList, meta) + + # Plot the first of the extracted channels + fig, ax = plt.subplots() + ax.plot(tDat, convData[0, :]) + plt.show() + + else: + digArray = ExtractDigital(rawData, firstSamp, lastSamp, dw, + dLineList, meta) + + # Plot the first of the extracted channels + fig, ax = plt.subplots() + + for i in range(0, len(dLineList)): + ax.plot(tDat, digArray[i, :]) + plt.show() + + +if __name__ == "__main__": + main() diff --git a/ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX_Datafile_Tools-main/Python/read_SGLX_analog.ipynb b/ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX_Datafile_Tools-main/Python/read_SGLX_analog.ipynb new file mode 100644 index 00000000..dadca219 --- /dev/null +++ b/ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX_Datafile_Tools-main/Python/read_SGLX_analog.ipynb @@ -0,0 +1,117 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# This example imports functions from the DemoReadSGLXData module to read\n", + "# analog data and convert it to volts based on the metadata information.\n", + "# The metadata file must be present in the same directory as the binary file.\n", + "# Works with both imec and nidq analog channels.\n", + "import numpy as np\n", + "import matplotlib.pyplot as plt\n", + "from pathlib import Path\n", + "from tkinter import Tk\n", + "from tkinter import filedialog\n", + "from DemoReadSGLXData.readSGLX import readMeta, SampRate, makeMemMapRaw, GainCorrectIM, GainCorrectNI" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Get bin file from user\n", + "root = Tk() # create the Tkinter widget\n", + "root.withdraw() # hide the Tkinter root window\n", + "\n", + "# Windows specific; forces the window to appear in front\n", + "root.attributes(\"-topmost\", True)\n", + "\n", + "binFullPath = Path(filedialog.askopenfilename(title=\"Select binary file\"))\n", + "\n", + "root.destroy()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Other parameters about what data to read\n", + "tStart = 0 # in seconds\n", + "tEnd = 0.1\n", + "chanList = [0] # list of channels to extract, by index in saved file" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "meta = readMeta(binFullPath)\n", + "sRate = SampRate(meta)\n", + "\n", + "firstSamp = int(sRate*tStart)\n", + "lastSamp = int(sRate*tEnd)\n", + "rawData = makeMemMapRaw(binFullPath, meta)\n", + "selectData = rawData[chanList, firstSamp:lastSamp+1]\n", + "\n", + "\n", + "if meta['typeThis'] == 'imec':\n", + " # apply gain correction and convert to uV\n", + " convData = 1e6*GainCorrectIM(selectData, chanList, meta)\n", + "else:\n", + " # apply gain correction and convert to mV\n", + " convData = 1e3*GainCorrectNI(selectData, chanList, meta)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Plot the first of the extracted channels\n", + "tDat = np.arange(firstSamp, lastSamp+1, dtype='uint64')\n", + "tDat = 1000*tDat/sRate # plot time axis in msec\n", + "fig, ax = plt.subplots()\n", + "ax.plot(tDat, convData[0, :])\n", + "plt.show()\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.5" + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX_Datafile_Tools-main/Python/read_SGLX_digital.ipynb b/ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX_Datafile_Tools-main/Python/read_SGLX_digital.ipynb new file mode 100644 index 00000000..b920f7b3 --- /dev/null +++ b/ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX_Datafile_Tools-main/Python/read_SGLX_digital.ipynb @@ -0,0 +1,118 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# This example imports functions from the DemoReadSGLXData module to read\n", + "# digital data. The metadata file must be present in the same directory as the binary file.\n", + "# Works with both imec and nidq digital channels.\n", + "import numpy as np\n", + "import matplotlib.pyplot as plt\n", + "from pathlib import Path\n", + "from tkinter import Tk\n", + "from tkinter import filedialog\n", + "from DemoReadSGLXData.readSGLX import readMeta, SampRate, makeMemMapRaw, ExtractDigital" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Get file from user\n", + "root = Tk() # create the Tkinter widget\n", + "root.withdraw() # hide the Tkinter root window\n", + "\n", + "# Windows specific; forces the window to appear in front\n", + "root.attributes(\"-topmost\", True)\n", + "\n", + "binFullPath = Path(filedialog.askopenfilename(title=\"Select binary file\"))\n", + "\n", + "root.destroy()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Other parameters about what data to read\n", + "tStart = 0 # in seconds\n", + "tEnd = 1\n", + "# Which digital word to read. \n", + "# For imec, there is only 1 digital word, dw = 0.\n", + "# For NI, digital lines 0-15 are in word 0, lines 16-31 are in word 1, etc.\n", + "dw = 0 \n", + "# Which lines within the digital word, zero-based\n", + "# Note that the SYNC line for PXI 3B is stored in line 6.\n", + "dLineList = [0,1,6]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "meta = readMeta(binFullPath)\n", + "sRate = SampRate(meta)\n", + "\n", + "firstSamp = int(sRate*tStart)\n", + "lastSamp = int(sRate*tEnd)\n", + "rawData = makeMemMapRaw(binFullPath, meta)\n", + "\n", + "# get digital data for the selected lines\n", + "digArray = ExtractDigital(rawData, firstSamp, lastSamp, dw, dLineList, meta)\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Plot the extracted digital channels\n", + "tDat = np.arange(firstSamp, lastSamp+1, dtype='uint64')\n", + "tDat = 1000*tDat/sRate # plot time axis in msec\n", + "fig, ax = plt.subplots()\n", + "for i in range(0, len(dLineList)):\n", + " ax.plot(tDat, digArray[i, :])\n", + "plt.show()\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.5" + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX_Datafile_Tools-main/README.md b/ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX_Datafile_Tools-main/README.md new file mode 100644 index 00000000..0bb2681c --- /dev/null +++ b/ExperPort/Modules/NeuropixelHelper_Modules/SpikeGLX_Datafile_Tools-main/README.md @@ -0,0 +1,11 @@ +# SpikeGLX_Datafile_Tools + +Simple helper functions and data structures demonstrating how to read and manipulate SpikeGLX meta and binary files. + +Works with 3A and all current probe types, as of 1/3/24. + +The same basic functionality is provided for MATLAB and Python. + +The Python material can be used from an interpreter or imported into your own modules. An example Jupyter notebook is included. + +The examples are functional and illustrate how to parse the data, but they are not optimized for speed or for memory usage. diff --git a/ExperPort/Plugins/@bonsaicamera/.bonsai/Settings/Camera_Control.editor b/ExperPort/Plugins/@bonsaicamera/.bonsai/Settings/Camera_Control.editor deleted file mode 100644 index bbd6dca8..00000000 --- a/ExperPort/Plugins/@bonsaicamera/.bonsai/Settings/Camera_Control.editor +++ /dev/null @@ -1,37 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/ExperPort/Plugins/@bonsaicamera/.bonsai/Settings/Camera_Control.layout b/ExperPort/Plugins/@bonsaicamera/.bonsai/Settings/Camera_Control.layout deleted file mode 100644 index 0829d87c..00000000 --- a/ExperPort/Plugins/@bonsaicamera/.bonsai/Settings/Camera_Control.layout +++ /dev/null @@ -1,2 +0,0 @@ - - \ No newline at end of file diff --git a/ExperPort/Plugins/@bonsaicamera/Camera_Control.bonsai.layout b/ExperPort/Plugins/@bonsaicamera/Camera_Control.bonsai.layout new file mode 100644 index 00000000..2ced8526 --- /dev/null +++ b/ExperPort/Plugins/@bonsaicamera/Camera_Control.bonsai.layout @@ -0,0 +1,333 @@ + + + + false + + 0 + 0 + + + 0 + 0 + + Normal + + + false + + 0 + 0 + + + 0 + 0 + + Normal + + + false + + 94 + 95 + + + 333 + 63 + + Normal + Bonsai.Design.ObjectTextVisualizer + + + + + + false + + 0 + 0 + + + 0 + 0 + + Normal + + + false + + 0 + 0 + + + 0 + 0 + + Normal + + + false + + 0 + 0 + + + 0 + 0 + + Normal + + + false + + 0 + 0 + + + 0 + 0 + + Normal + + + false + + 0 + 0 + + + 0 + 0 + + Normal + + + false + + 636 + 274 + + + 333 + 277 + + Normal + + + false + + 472 + 106 + + + 333 + 63 + + Normal + Bonsai.Design.ObjectTextVisualizer + + + + + + false + + 0 + 0 + + + 0 + 0 + + Normal + + false + + 245 + 255 + + + 313 + 237 + + Normal + + + + true + + 15 + 705 + + + 333 + 277 + + Normal + Bonsai.Vision.Design.IplImageVisualizer + + + + + + false + + 0 + 0 + + + 0 + 0 + + Normal + + + false + + 95 + 246 + + + 333 + 177 + + Normal + Bonsai.Design.ObjectTextVisualizer + + + + + + false + + 98 + 102 + + + 333 + 63 + + Normal + + false + + 270 + 280 + + + 313 + 237 + + Normal + + + + false + + 0 + 0 + + + 0 + 0 + + Normal + + + false + + 0 + 0 + + + 0 + 0 + + Normal + + + false + + 0 + 0 + + + 0 + 0 + + Normal + + + false + + 0 + 0 + + + 0 + 0 + + Normal + + + false + + 0 + 0 + + + 0 + 0 + + Normal + + + false + + 0 + 0 + + + 0 + 0 + + Normal + + + false + + 0 + 0 + + + 0 + 0 + + Normal + + + false + + 0 + 0 + + + 0 + 0 + + Normal + + false + + 220 + 229 + + + 313 + 237 + + Normal + + + \ No newline at end of file From 890cd3542667dcd417bc97f1794cb7ac3511857f Mon Sep 17 00:00:00 2001 From: Arpit Date: Sun, 12 Oct 2025 17:46:06 +0100 Subject: [PATCH 158/164] added missing functionality to control invalid button press --- .../.bonsai/Settings/Camera_Control.editor | 37 ++ .../.bonsai/Settings/Camera_Control.layout | 62 ++++ .../Camera_Control.bonsai.layout | 333 ------------------ .../PsychometricSection.m | 135 ++++--- .../@ArpitSoundCatContinuous/SideSection.m | 22 +- 5 files changed, 197 insertions(+), 392 deletions(-) create mode 100644 ExperPort/Plugins/@bonsaicamera/.bonsai/Settings/Camera_Control.editor create mode 100644 ExperPort/Plugins/@bonsaicamera/.bonsai/Settings/Camera_Control.layout delete mode 100644 ExperPort/Plugins/@bonsaicamera/Camera_Control.bonsai.layout diff --git a/ExperPort/Plugins/@bonsaicamera/.bonsai/Settings/Camera_Control.editor b/ExperPort/Plugins/@bonsaicamera/.bonsai/Settings/Camera_Control.editor new file mode 100644 index 00000000..bbd6dca8 --- /dev/null +++ b/ExperPort/Plugins/@bonsaicamera/.bonsai/Settings/Camera_Control.editor @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/ExperPort/Plugins/@bonsaicamera/.bonsai/Settings/Camera_Control.layout b/ExperPort/Plugins/@bonsaicamera/.bonsai/Settings/Camera_Control.layout new file mode 100644 index 00000000..386f50b5 --- /dev/null +++ b/ExperPort/Plugins/@bonsaicamera/.bonsai/Settings/Camera_Control.layout @@ -0,0 +1,62 @@ + + + + false + + 94 + 95 + + + 333 + 63 + + Bonsai.Design.ObjectTextVisualizer + + + + + + false + + 472 + 106 + + + 333 + 63 + + Bonsai.Design.ObjectTextVisualizer + + + + + + + 15 + 705 + + + 333 + 277 + + Bonsai.Vision.Design.IplImageVisualizer + + + + + + false + + 95 + 246 + + + 333 + 177 + + Bonsai.Design.ObjectTextVisualizer + + + + + \ No newline at end of file diff --git a/ExperPort/Plugins/@bonsaicamera/Camera_Control.bonsai.layout b/ExperPort/Plugins/@bonsaicamera/Camera_Control.bonsai.layout deleted file mode 100644 index 2ced8526..00000000 --- a/ExperPort/Plugins/@bonsaicamera/Camera_Control.bonsai.layout +++ /dev/null @@ -1,333 +0,0 @@ - - - - false - - 0 - 0 - - - 0 - 0 - - Normal - - - false - - 0 - 0 - - - 0 - 0 - - Normal - - - false - - 94 - 95 - - - 333 - 63 - - Normal - Bonsai.Design.ObjectTextVisualizer - - - - - - false - - 0 - 0 - - - 0 - 0 - - Normal - - - false - - 0 - 0 - - - 0 - 0 - - Normal - - - false - - 0 - 0 - - - 0 - 0 - - Normal - - - false - - 0 - 0 - - - 0 - 0 - - Normal - - - false - - 0 - 0 - - - 0 - 0 - - Normal - - - false - - 636 - 274 - - - 333 - 277 - - Normal - - - false - - 472 - 106 - - - 333 - 63 - - Normal - Bonsai.Design.ObjectTextVisualizer - - - - - - false - - 0 - 0 - - - 0 - 0 - - Normal - - false - - 245 - 255 - - - 313 - 237 - - Normal - - - - true - - 15 - 705 - - - 333 - 277 - - Normal - Bonsai.Vision.Design.IplImageVisualizer - - - - - - false - - 0 - 0 - - - 0 - 0 - - Normal - - - false - - 95 - 246 - - - 333 - 177 - - Normal - Bonsai.Design.ObjectTextVisualizer - - - - - - false - - 98 - 102 - - - 333 - 63 - - Normal - - false - - 270 - 280 - - - 313 - 237 - - Normal - - - - false - - 0 - 0 - - - 0 - 0 - - Normal - - - false - - 0 - 0 - - - 0 - 0 - - Normal - - - false - - 0 - 0 - - - 0 - 0 - - Normal - - - false - - 0 - 0 - - - 0 - 0 - - Normal - - - false - - 0 - 0 - - - 0 - 0 - - Normal - - - false - - 0 - 0 - - - 0 - 0 - - Normal - - - false - - 0 - 0 - - - 0 - 0 - - Normal - - - false - - 0 - 0 - - - 0 - 0 - - Normal - - false - - 220 - 229 - - - 313 - 237 - - Normal - - - \ No newline at end of file diff --git a/Protocols/@ArpitSoundCatContinuous/PsychometricSection.m b/Protocols/@ArpitSoundCatContinuous/PsychometricSection.m index 5b009237..76c1a73e 100644 --- a/Protocols/@ArpitSoundCatContinuous/PsychometricSection.m +++ b/Protocols/@ArpitSoundCatContinuous/PsychometricSection.m @@ -160,12 +160,13 @@ 'CellEditCallback',@(src, evt) PsychometricSection(obj, 'check_box_table', src, evt)), ... % end of uitable definition 'saveable', true); + SoloFunctionAddVars('SideSection', 'rw_args', ... + {'Switch_Distr'}); % Returning the x , y position for the main callback GUI varargout{1} = oldx; varargout{2} = oldy; - %% Calculate Parameters case 'Calculate_Params' @@ -243,40 +244,58 @@ case 'PushButton_Distribution_Switch' - % eval(sprintf('present_context_dist = value(Context%i_Dist)',value(thiscontext))); - eval(sprintf('present_context_start = value(Context%i_trialStart);',value(thiscontext))); - eval(sprintf('present_context_end = value(Context%i_trialEnd);',value(thiscontext))); - - if ~strcmpi(Category_Dist,'Uniform') && present_context_end > present_context_start - - if value(thiscontext) < 3 % & ~strcmpi(present_context_dist,Category_Dist) - thiscontext.value = value(thiscontext) + 1; - eval(sprintf('Context%i_Dist.value = Category_Dist;',value(thiscontext))); - eval(sprintf('Context%i_trialStart.value = n_done_trials + 1;',value(thiscontext))); - eval(sprintf('Context%i_trialEnd.value = n_done_trials + 1;',value(thiscontext))); - eval(sprintf('make_visible(Context%i_Dist);',value(thiscontext))); - eval(sprintf('make_visible(Context%i_trialStart);',value(thiscontext))); - eval(sprintf('make_visible(Context%i_trialEnd);',value(thiscontext))); - end + % --- Confirmation Dialog --- + % Display a dialog box to ask the user for confirmation before proceeding. + % 'questdlg' creates a dialog with a question, title, and button options. + choice = questdlg('Are you sure you want to switch the distribution?', ... + 'Confirm Action', ... + 'Yes', 'No', 'No'); % The last 'No' sets it as the default button. + + % --- Handle User's Response --- + % The code proceeds only if the user clicks the 'Yes' button. + % The strcmp function compares the 'choice' variable with 'Yes'. + if strcmp(choice, 'Yes') + + % --- Original Code Execution --- + % This is your original code, which will run after confirmation. + + % eval(sprintf('present_context_dist = value(Context%i_Dist)',value(thiscontext))); + eval(sprintf('present_context_start = value(Context%i_trialStart);',value(thiscontext))); + eval(sprintf('present_context_end = value(Context%i_trialEnd);',value(thiscontext))); + + if ~strcmpi(Category_Dist,'Uniform') && present_context_end > present_context_start + + if value(thiscontext) < 3 % & ~strcmpi(present_context_dist,Category_Dist) + thiscontext.value = value(thiscontext) + 1; + eval(sprintf('Context%i_Dist.value = Category_Dist;',value(thiscontext))); + eval(sprintf('Context%i_trialStart.value = n_done_trials + 1;',value(thiscontext))); + eval(sprintf('Context%i_trialEnd.value = n_done_trials + 1;',value(thiscontext))); + eval(sprintf('make_visible(Context%i_Dist);',value(thiscontext))); + eval(sprintf('make_visible(Context%i_trialStart);',value(thiscontext))); + eval(sprintf('make_visible(Context%i_trialEnd);',value(thiscontext))); + end + if strcmpi(Category_Dist,'Hard A') + StimulusSection(obj,'Pushbutton_SwitchDistribution','Hard B'); + elseif strcmpi(Category_Dist,'Hard B') + StimulusSection(obj,'Pushbutton_SwitchDistribution','Hard A'); + end - if strcmpi(Category_Dist,'Hard A') - StimulusSection(obj,'Pushbutton_SwitchDistribution','Hard B'); - elseif strcmpi(Category_Dist,'Hard B') - StimulusSection(obj,'Pushbutton_SwitchDistribution','Hard A'); + % Also update the table and make changes to plot + [state, data, handles, config, flags] = PsychometricSection(obj,'Calculate_Params'); + % Calling the function to update the table and plot + state = RealTimeAnalysis('context_switch', state, data, handles, config, flags); + states_value.value = state; end - - % Also update the table and make changes to plot - [state, data, handles, config, flags] = PsychometricSection(obj,'Calculate_Params'); - % Calling the function to update the table and plot - state = RealTimeAnalysis('context_switch', state, data, handles, config, flags); - states_value.value = state; - end - + + end % This 'end' closes the 'if strcmp(choice, 'Yes')' block. + % If the user clicks 'No' or closes the dialog, the code inside the if-block is skipped. + + case 'StimSection_Distribution_Switch' - + eval(sprintf('present_context_start = value(Context%i_trialStart);',value(thiscontext))); eval(sprintf('present_context_end = value(Context%i_trialEnd);',value(thiscontext))); - + if present_context_end > present_context_start if value(thiscontext) < 3 % & ~strcmpi(present_context_dist,Category_Dist) thiscontext.value = value(thiscontext) + 1; @@ -301,33 +320,33 @@ end case 'PushButton_SelectedTrial' - [state, data, handles, config, flags] = PsychometricSection(obj,'Calculate_Params'); - % Calling the function to update the table and plot - state = RealTimeAnalysis('custom', state, data, handles, config, flags,value(Plot_Trial_Start),value(Plot_Trial_End)); - states_value.value = state; + [state, data, handles, config, flags] = PsychometricSection(obj,'Calculate_Params'); + % Calling the function to update the table and plot + state = RealTimeAnalysis('custom', state, data, handles, config, flags,value(Plot_Trial_Start),value(Plot_Trial_End)); + states_value.value = state; case 'PushButton_Context' - [state, data, handles, config, flags] = PsychometricSection(obj,'Calculate_Params'); - % create a cell array containing the start and end of each context - context_trials = cell(1,value(thiscontext)); - contexts_name = cell(1,value(thiscontext)); - for n_plot = 1:value(thiscontext) - eval(sprintf('trial_start = value(Context%i_trialStart);',n_plot)); - eval(sprintf('trial_end = value(Context%i_trialEnd);',n_plot)); - eval(sprintf('context_name = value(Context%i_Dist);',n_plot)); - context_trials{1,n_plot} = [trial_start, trial_end]; - contexts_name{1,n_plot} = context_name; - end - % Calling the function to update the table and plot - state = RealTimeAnalysis('context', state, data, handles, config, flags, context_trials,contexts_name); - states_value.value = state; + [state, data, handles, config, flags] = PsychometricSection(obj,'Calculate_Params'); + % create a cell array containing the start and end of each context + context_trials = cell(1,value(thiscontext)); + contexts_name = cell(1,value(thiscontext)); + for n_plot = 1:value(thiscontext) + eval(sprintf('trial_start = value(Context%i_trialStart);',n_plot)); + eval(sprintf('trial_end = value(Context%i_trialEnd);',n_plot)); + eval(sprintf('context_name = value(Context%i_Dist);',n_plot)); + context_trials{1,n_plot} = [trial_start, trial_end]; + contexts_name{1,n_plot} = context_name; + end + % Calling the function to update the table and plot + state = RealTimeAnalysis('context', state, data, handles, config, flags, context_trials,contexts_name); + states_value.value = state; - case 'reload_after_crash' + case 'reload_after_crash' % make sure that the context values are visible for n_contexts = 1:value(thiscontext) - eval(sprintf('make_visible(Context%i_Dist);',n_contexts)); - eval(sprintf('make_visible(Context%i_trialStart);',n_contexts)); - eval(sprintf('make_visible(Context%i_trialEnd);',n_contexts)); + eval(sprintf('make_visible(Context%i_Dist);',n_contexts)); + eval(sprintf('make_visible(Context%i_trialStart);',n_contexts)); + eval(sprintf('make_visible(Context%i_trialEnd);',n_contexts)); end % set the fig value for uitable as we didn't save the table ui_table_handle = value(uit); @@ -338,7 +357,7 @@ end uit.value = ui_table_handle; - %% update after each trial + %% update after each trial case 'update' % update the trial end for this context if n_done_trials > 1 @@ -349,16 +368,16 @@ states_value.value = state; end - %% update the figure if user opened the figure window + %% update the figure if user opened the figure window case 'update_plot' % if n_done_trials > 1 % PsychometricSection(obj, 'reload_after_crash'); % end if n_done_trials > 30 - [state, data, handles, config, flags] = PsychometricSection(obj,'Calculate_Params'); - % Calling the function to update the table and plot - state = RealTimeAnalysis('redraw', state, data, handles, config, flags); - states_value.value = state; + [state, data, handles, config, flags] = PsychometricSection(obj,'Calculate_Params'); + % Calling the function to update the table and plot + state = RealTimeAnalysis('redraw', state, data, handles, config, flags); + states_value.value = state; end case 'evaluate' diff --git a/Protocols/@ArpitSoundCatContinuous/SideSection.m b/Protocols/@ArpitSoundCatContinuous/SideSection.m index a819b8cd..aebaf345 100644 --- a/Protocols/@ArpitSoundCatContinuous/SideSection.m +++ b/Protocols/@ArpitSoundCatContinuous/SideSection.m @@ -182,6 +182,14 @@ next_column(x); y=5; + ToggleParam(obj, 'control_active', 0, x,y,... + 'OnString', 'Buttons Deative',... + 'OffString', 'Buttons Active',... + 'TooltipString', sprintf(['If on (Yellow) then the user can change parameters.\n',... + 'This is to prevent any unwanted button press'])); + set_callback(control_active, {mfilename, 'User_Control'}); + next_row(y); + % Toggle for WarmUp ToggleParam(obj, 'warmup_on', 0, x,y,... 'OnString', 'Warmup ON',... @@ -229,7 +237,19 @@ SoloParamHandle(obj, 'deltaf_history', 'value', []); SoloParamHandle(obj, 'previous_parameters', 'value', []); - + case 'User_Control' + + if value(control_active) == 0 + disable(antibias_LRprob); disable(stimuli_on); disable(Switch_Distr); disable(antibias_wtr_mult); + disable(random_PreStim_time); disable(random_A1_time); disable(random_prego_time); + disable(warmup_on); disable(increase_CP_training); + else + enable(antibias_LRprob); enable(stimuli_on); enable(Switch_Distr); enable(antibias_wtr_mult); + enable(random_PreStim_time); enable(random_A1_time); enable(random_prego_time); + enable(warmup_on); enable(increase_CP_training); + end + + case 'new_leftprob' AntibiasSection(obj, 'update_biashitfrac', value(LeftProb)); From a2914d6b824fbd439d0c7ef16078029a6d2d4a5e Mon Sep 17 00:00:00 2001 From: arpit99ag Date: Tue, 21 Oct 2025 18:36:37 +0100 Subject: [PATCH 159/164] error debug --- .../ArpitSoundCatContinuousSMA.m | 9 ++++-- .../private/RealTimeAnalysis.m | 29 +++++++++++++------ 2 files changed, 27 insertions(+), 11 deletions(-) diff --git a/Protocols/@ArpitSoundCatContinuous/ArpitSoundCatContinuousSMA.m b/Protocols/@ArpitSoundCatContinuous/ArpitSoundCatContinuousSMA.m index f15401bb..73ef60a9 100644 --- a/Protocols/@ArpitSoundCatContinuous/ArpitSoundCatContinuousSMA.m +++ b/Protocols/@ArpitSoundCatContinuous/ArpitSoundCatContinuousSMA.m @@ -234,8 +234,12 @@ 'input_to_statechange',{'Tup','timeout_state'; HitEvent,'hit_state'}); else % no reward but a punishment iti - sma = add_state(sma,'name','second_hit_state','self_timer',error_iti,... - 'input_to_statechange',{'reward_collection_dur_In', 'timeout_state'; 'Tup','preclean_up_state'}); + sma = add_state(sma,'name','second_hit_state','self_timer',2,... + 'output_actions', {'DOut', second_hit_light},... + 'input_to_statechange',{'Tup','current_state + 1'}); + sma = add_state(sma,'self_timer',error_iti - 2,... + 'input_to_statechange',{'Tup','preclean_up_state'}); + % sma = add_state(sma, 'name', 'hit_state'); % sma = add_state(sma, 'name', 'drink_state'); @@ -246,6 +250,7 @@ 'input_to_statechange',{'Tup','drink_state'}); sma = add_state(sma,'name','drink_state','self_timer',drink_time,... + 'output_actions', {'DOut', second_hit_light},... 'input_to_statechange',{'Tup','preclean_up_state'}); %%%%%%%%%%%%%%% FAILURE TO CENTRE POKE %%%%%%%%%%%%%%%%%%%%%% diff --git a/Protocols/@ArpitSoundCatContinuous/private/RealTimeAnalysis.m b/Protocols/@ArpitSoundCatContinuous/private/RealTimeAnalysis.m index 218545d3..9fb5049f 100644 --- a/Protocols/@ArpitSoundCatContinuous/private/RealTimeAnalysis.m +++ b/Protocols/@ArpitSoundCatContinuous/private/RealTimeAnalysis.m @@ -385,11 +385,10 @@ function state = analyzeLiveChunk(state, data, handles, config, dataBuffer, flags) state.block_count = state.block_count + 1; [newBlockStat, newRow] = processBlock(data, config, dataBuffer); - state.blockStatsHistory = [state.blockStatsHistory, newBlockStat]; - updateTable(handles, newRow); - state.table_row_editable(end+1) = true; - + state.blockStatsHistory = [state.blockStatsHistory, newBlockStat]; state.last_analyzed_valid_trial = sum(~isnan(data.hit_history)); + updateTable(handles, newRow); + state.table_row_editable(end+1) = true; if strcmp(get(handles.main_fig, 'Visible'), 'on') updateLivePlots(state, data, handles, config, flags); @@ -401,9 +400,8 @@ function state = reAnalyzeLastChunk(state, data, handles, config, dataBuffer, flags) [newBlockStat, newRow] = processBlock(data, config, dataBuffer); state.blockStatsHistory(end) = newBlockStat; - replaceLastTableRow(handles, newRow); - state.last_analyzed_valid_trial = sum(~isnan(data.hit_history)); + replaceLastEditableTableRow(handles, newRow, state.table_row_editable); if strcmp(get(handles.main_fig, 'Visible'), 'on') updateLivePlots(state, data, handles, config, flags); @@ -461,12 +459,25 @@ function updateTable(handles, newRow) set(handles.ui_table, 'Data', [currentData; newRow]); end - function replaceLastTableRow(handles, newRow) + function replaceLastTableRow(handles, newRow, table_row_editable) currentData = get(handles.ui_table, 'Data'); - if ~isempty(currentData) + if isempty(currentData) + return; + end + + % Find the index of the last 'live' block (which is editable) + last_editable_row_index = find(table_row_editable, 1, 'last'); + + if ~isempty(last_editable_row_index) newRow{1} = true; % Select the new row - currentData(end,:) = newRow; + + % Replace the correct row, not just the last one + currentData(last_editable_row_index, :) = newRow; + set(handles.ui_table, 'Data', currentData); + else + % Fallback in case no editable row is found (should not happen) + updateTable(handles, newRow); end end From 0bb94bad0247620494ea49c1725cd28efa140f6d Mon Sep 17 00:00:00 2001 From: Arpit Date: Wed, 22 Oct 2025 12:01:54 +0100 Subject: [PATCH 160/164] error debug --- .../ArpitSoundCatContinuousSMA.m | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/Protocols/@ArpitSoundCatContinuous/ArpitSoundCatContinuousSMA.m b/Protocols/@ArpitSoundCatContinuous/ArpitSoundCatContinuousSMA.m index 73ef60a9..10ccb91a 100644 --- a/Protocols/@ArpitSoundCatContinuous/ArpitSoundCatContinuousSMA.m +++ b/Protocols/@ArpitSoundCatContinuous/ArpitSoundCatContinuousSMA.m @@ -227,17 +227,19 @@ elseif strcmp(reward_type, 'DelayedReward') sma = add_state(sma,'name','second_hit_state','self_timer',secondhit_delay,... - 'input_to_statechange',{'Tup','current_state + 1';}); + 'input_to_statechange',{'Tup','current_state + 1'}); sma = add_state(sma,'self_timer',RewardCollection_duration,... 'output_actions',{'DOut', second_hit_light},... 'input_to_statechange',{'Tup','timeout_state'; HitEvent,'hit_state'}); else % no reward but a punishment iti + sma = add_state(sma,'name','second_hit_state','self_timer',2,... - 'output_actions', {'DOut', second_hit_light},... - 'input_to_statechange',{'Tup','current_state + 1'}); - sma = add_state(sma,'self_timer',error_iti - 2,... + 'output_actions',{'DOut', second_hit_light},... + 'input_to_statechange',{'Tup','current_state+1'}); + + sma = add_state(sma,'self_timer',max(0.001,error_iti - 2),... 'input_to_statechange',{'Tup','preclean_up_state'}); % sma = add_state(sma, 'name', 'hit_state'); From f10773b2fc5f7c479d3ad04ce427b4225d34f628 Mon Sep 17 00:00:00 2001 From: arpit99ag Date: Tue, 4 Nov 2025 16:29:10 +0000 Subject: [PATCH 161/164] change setting value for stim distribution in ArpitSoundCatContinuous --- Protocols/@ArpitSoundCatContinuous/StimulusSection.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Protocols/@ArpitSoundCatContinuous/StimulusSection.m b/Protocols/@ArpitSoundCatContinuous/StimulusSection.m index d147ee75..a8e82eed 100644 --- a/Protocols/@ArpitSoundCatContinuous/StimulusSection.m +++ b/Protocols/@ArpitSoundCatContinuous/StimulusSection.m @@ -56,7 +56,7 @@ '\n''S1>S_boundary Right'' means if Aud1 < Aud_boundry then reward will be delivered from the left water spout and if Aud1 > Aud_boundry then water comes from right\n'])); next_row(y, 1);next_row(y, 1); - NumeditParam(obj,'P_centre_region',0.25,x,y,'label','P_centre','TooltipString','probability for choosing stim near boundary. 0 = same probability as the rest, 1 = only choose from centre'); + NumeditParam(obj,'P_centre_region',0,x,y,'label','P_centre','TooltipString','probability for choosing stim near boundary. 0 = same probability as the rest, 1 = only choose from centre'); next_row(y); NumeditParam(obj,'centre_region_width',0.1,x,y,'label','Centre Width','TooltipString','total width around boundary to be considered as central region'); next_row(y);next_row(y); From c5b11b657ac84fbebd935a50e20f8d321b7d61ac Mon Sep 17 00:00:00 2001 From: arpit99ag Date: Fri, 7 Nov 2025 13:53:07 +0000 Subject: [PATCH 162/164] changed the rewarded water amount for ArpitSoundCatContinuous --- Protocols/@ArpitSoundCatContinuous/ArpitSoundCatContinuous.m | 4 ++-- Protocols/@ArpitSoundCatContinuous/SideSection.m | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/Protocols/@ArpitSoundCatContinuous/ArpitSoundCatContinuous.m b/Protocols/@ArpitSoundCatContinuous/ArpitSoundCatContinuous.m index b93c99f9..b4e0035f 100644 --- a/Protocols/@ArpitSoundCatContinuous/ArpitSoundCatContinuous.m +++ b/Protocols/@ArpitSoundCatContinuous/ArpitSoundCatContinuous.m @@ -91,14 +91,14 @@ %% slow ramp up of water amount %%the water volume is controlled by a 5-parameter logistic function: WaterAmount(t) = maxasymp + (minasymp/(1+(t/inflp)^slp).^assym) - NumeditParam(obj, 'maxasymp', 38, x,y,'label','maxasymp','TooltipString',... + NumeditParam(obj, 'maxasymp', 30, x,y,'label','maxasymp','TooltipString',... 'the water volume is controlled by a 5-parameter logistic function: WaterAmount(trialnum) = maxasymp + (minasymp/(1+(trialnum/inflp)^slp).^assym)'); next_row(y); NumeditParam(obj, 'slp', 3, x,y,'label','slp','TooltipString','Water Modulation: Slope of the logistic function'); next_row(y); NumeditParam(obj, 'inflp', 350, x,y,'label','inflp','TooltipString','Water Modulation: concentration at the inflection point'); next_row(y); - NumeditParam(obj, 'minasymp', -21, x,y,'label','inflp','TooltipString','Water Modulation: minimum asymptote'); + NumeditParam(obj, 'minasymp', -13, x,y,'label','minasymp','TooltipString','Water Modulation: minimum asymptote'); next_row(y); NumeditParam(obj, 'assym', 0.7, x,y,'label','assym','TooltipString','Water Modulation: asymmetry factor'); next_row(y); diff --git a/Protocols/@ArpitSoundCatContinuous/SideSection.m b/Protocols/@ArpitSoundCatContinuous/SideSection.m index aebaf345..6aecc7f9 100644 --- a/Protocols/@ArpitSoundCatContinuous/SideSection.m +++ b/Protocols/@ArpitSoundCatContinuous/SideSection.m @@ -531,7 +531,8 @@ case 'get_water_amount' %% Calculate the water amount for each side valve - WaterAmount=maxasymp + (minasymp./(1+(n_done_trials/inflp).^slp).^assym); + trials_use = max(1,numel(find(~isnan(hit_history)))); + WaterAmount=maxasymp + (minasymp./(1+(trials_use/inflp).^slp).^assym); % using valid trials only instead of total trials % WaterValvesSection(obj, 'set_water_amounts', WaterAmount, WaterAmount); % [LeftWValveTime RightWValveTime] = WaterValvesSection(obj, 'get_water_times'); WValveTimes = GetValveTimes(WaterAmount, [2 3]); From d5aad1e851cf4556f8c6f946c2c070aa9fafc116 Mon Sep 17 00:00:00 2001 From: arpit99ag Date: Fri, 7 Nov 2025 17:08:06 +0000 Subject: [PATCH 163/164] changed the rewarded water amount for ArpitSoundCatContinuous error debug --- Protocols/@ArpitSoundCatContinuous/SideSection.m | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/Protocols/@ArpitSoundCatContinuous/SideSection.m b/Protocols/@ArpitSoundCatContinuous/SideSection.m index 6aecc7f9..2f44c99a 100644 --- a/Protocols/@ArpitSoundCatContinuous/SideSection.m +++ b/Protocols/@ArpitSoundCatContinuous/SideSection.m @@ -531,7 +531,11 @@ case 'get_water_amount' %% Calculate the water amount for each side valve - trials_use = max(1,numel(find(~isnan(hit_history)))); + if n_done_trials > 30 + trials_use = max(1,numel(find(~isnan(hit_history)))); + else + trials_use = n_done_trials; + end WaterAmount=maxasymp + (minasymp./(1+(trials_use/inflp).^slp).^assym); % using valid trials only instead of total trials % WaterValvesSection(obj, 'set_water_amounts', WaterAmount, WaterAmount); % [LeftWValveTime RightWValveTime] = WaterValvesSection(obj, 'get_water_times'); From abc98a7c3594094a7b5fb18a7a4b86aee6ea49e2 Mon Sep 17 00:00:00 2001 From: Arpit Date: Mon, 10 Nov 2025 13:41:55 +0000 Subject: [PATCH 164/164] error debug --- Protocols/@ArpitSoundCatContinuous/SideSection.m | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Protocols/@ArpitSoundCatContinuous/SideSection.m b/Protocols/@ArpitSoundCatContinuous/SideSection.m index 2f44c99a..384a5e6a 100644 --- a/Protocols/@ArpitSoundCatContinuous/SideSection.m +++ b/Protocols/@ArpitSoundCatContinuous/SideSection.m @@ -531,9 +531,9 @@ case 'get_water_amount' %% Calculate the water amount for each side valve - if n_done_trials > 30 - trials_use = max(1,numel(find(~isnan(hit_history)))); - else + try + trials_use = max(1,numel(find(~isnan(value(hit_history))))); + catch trials_use = n_done_trials; end WaterAmount=maxasymp + (minasymp./(1+(trials_use/inflp).^slp).^assym); % using valid trials only instead of total trials