Skip to content

Commit ef8ee75

Browse files
author
iandol
committed
V2.0.5 -- GUI panels for eyelink and tobii now work to set default
options, can still be overridded by state info file settings. Add a "Save Protocol Copy..." menu entry to save the protocol and copy the attached state info file, useful to make a copy in a new folder to modify. Update behaviouralRecord to use newer tiledlayout UI.
1 parent 5a26345 commit ef8ee75

10 files changed

Lines changed: 267 additions & 194 deletions

CoreProtocols/Saccade_AntiSaccadeStateInfo.m

Lines changed: 50 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -4,24 +4,30 @@
44
%> Objects provide many methods you can run, like sending triggers, showing
55
%> stimuli, controlling the eyetracker etc.
66
%
7-
%> The following class objects are already loaded and available to use:
7+
%> This state file is loaded by the runExperiment class. runExperiment
8+
%> initialises other classes that are used to control the experiment. The
9+
%> following class objects are already loaded and available to use:
810
%
9-
%> me = runExperiment object
10-
%> io = digital I/O to recording system
11-
%> s = screenManager
12-
%> aM = audioManager
13-
%> sM = State Machine
14-
%> eT = eyetracker manager
15-
%> task = task sequence (taskSequence class)
16-
%> rM = Reward Manager (LabJack or Arduino TTL trigger to reward system/Magstim)
17-
%> bR = behavioural record plot (on screen GUI during task run)
18-
%> stims = our list of stimuli
19-
%> tS = general struct to hold variables for this run, will be saved as part of the data
11+
%> me = runExperiment object
12+
%> s = screenManager
13+
%> sM = State Machine
14+
%> eT = eyetracker manager
15+
%> task = task sequence (taskSequence class)
16+
%> stims = our list of stimuli
17+
%> io = digital I/O to recording system
18+
%> aM = audioManager
19+
%> rM = Reward Manager (LabJack or Arduino TTL trigger to reward system/Magstim)
20+
%> bR = behavioural record plot (on screen GUI of trial performance during task run)
21+
%> tL = timeLog that records the timing of experiment
22+
%> tS = general struct to hold variables for this run, will be saved as part of the data
2023

2124
%==================================================================
22-
%---------------------------TASK SWITCH----------------------------
25+
%---------------------------TASK CONFIG----------------------------
26+
%do we update the trial number even for incorrect saccades, if true then we
27+
%call updateTask for both correct and incorrect, otherwise we only call
28+
%updateTask() for correct responses
29+
tS.includeErrors = false;
2330
tS.type = 'saccade'; %will be be saccade or antisaccade task run?
24-
tS.includeErrors = true; %do we update the trial number even for incorrect saccades, if true then we call updateTask for both correct and incorrect, otherwise we only call updateTask() for correct responses
2531
if strcmp(tS.type,'saccade')
2632
stims{1}.showOnTracker = true;
2733
stims{2}.showOnTracker = false;
@@ -49,7 +55,7 @@
4955
%==================================================================
5056
%------------Debug logging to command window-----------------
5157
%io.verbose = true; %==print out io commands for debugging
52-
%eT.verbose = true; %==print out eyelink commands for debugging
58+
%eT.verbose = true; %==print out eyetracker commands for debugging
5359
%rM.verbose = true; %==print out reward commands for debugging
5460

5561
%==================================================================
@@ -71,14 +77,14 @@
7177

7278
%==================================================================
7379
%---------------------------Eyetracker setup-----------------------
80+
% note: opticka UI sets some defaults, but these will override the UI
7481
if me.useEyeLink
7582
warning('Note this protocol is optimised for the Tobii eyetracker, beware...')
7683
eT.name = tS.name;
7784
eT.sampleRate = 250; % sampling rate
7885
eT.calibrationStyle = 'HV5'; % calibration style
7986
eT.calibrationProportion = [0.4 0.4]; %the proportion of the screen occupied by the calibration stimuli
8087
if tS.saveData == true; eT.recordData = true; end %===save EDF file?
81-
if me.dummyMode; eT.isDummy = true; end %===use dummy or real eyetracker?
8288
%-----------------------
8389
% remote calibration enables manual control and selection of each fixation
8490
% this is useful for a baby or monkey who has not been trained for fixation
@@ -94,23 +100,22 @@
94100
eT.modify.targetbeep = 1; % beep during calibration
95101
elseif me.useTobii
96102
eT.name = tS.name;
97-
eT.model = 'Tobii Pro Spectrum';
98-
eT.sampleRate = 300;
99-
eT.trackingMode = 'human';
100-
eT.calibrationStimulus = 'animated';
101-
eT.autoPace = true;
103+
%eT.model = 'Tobii Pro Spectrum';
104+
%eT.sampleRate = 300;
105+
%eT.trackingMode = 'human';
106+
%eT.calibrationStimulus = 'animated';
107+
%eT.autoPace = true;
102108
%-----------------------
103109
% remote calibration enables manual control and selection of each fixation
104110
% this is useful for a baby or monkey who has not been trained for fixation
105-
eT.manualCalibration = false;
111+
%eT.manualCalibration = false;
106112
%-----------------------
107-
eT.calPositions = [ .2 .5; .5 .5; .8 .5];
108-
eT.valPositions = [ .5 .5 ];
109-
if me.dummyMode; eT.isDummy = true; end %===use dummy or real eyetracker?
113+
%eT.calPositions = [ .2 .5; .5 .5; .8 .5];
114+
%eT.valPositions = [ .5 .5 ];
110115
end
111116
%Initialise the eyeTracker object with X, Y, FixInitTime, FixTime, Radius, StrictFix
112117
eT.updateFixationValues(tS.fixX, tS.fixY, tS.firstFixInit, tS.firstFixTime, tS.firstFixRadius, tS.strict);
113-
%make sure we don't start with any exclusion zones set up
118+
%Ensure we don't start with any exclusion zones set up
114119
eT.resetExclusionZones();
115120

116121
%==================================================================
@@ -171,16 +176,20 @@
171176
sM.skipExitStates = {'fixate','incorrect|breakfix'};
172177

173178
%===================================================================
174-
%-----------------State Machine State Functions---------------------
175-
% each cell {array} holds a set of anonymous function handles which are executed by the
176-
% state machine to control the experiment. The state machine can run sets
177-
% at entry, during, to trigger a transition, and at exit. Remember these
178-
% {sets} need to access the objects that are available within the
179-
% runExperiment context (see top of file). You can also add global
180-
% variables/objects then use these. The values entered here are set on
181-
% load, if you want up-to-date values then you need to use methods/function
182-
% wrappers to retrieve/set them.
183-
179+
%===================================================================
180+
%===================================================================
181+
%-----------------State Machine Task Functions---------------------
182+
% Each cell {array} holds a set of anonymous function handles which are
183+
% executed by the state machine to control the experiment. The state
184+
% machine can run sets at entry ['entryFcn'], during ['withinFcn'], to
185+
% trigger a transition jump to another state ['transitionFcn'], and at exit
186+
% ['exitFcn'. Remember these {sets} need to access the objects that are
187+
% available within the runExperiment context (see top of file). You can
188+
% also add global variables/objects then use these. The values entered here
189+
% are set on load, if you want up-to-date values then you need to use
190+
% methods/function wrappers to retrieve/set them.
191+
192+
%====================================================PAUSE
184193
%pause entry
185194
pauseEntryFcn = {
186195
@()hide(stims);
@@ -201,6 +210,7 @@
201210
@()startRecording(eT, true); %start recording eye position data again
202211
};
203212

213+
%====================================================PREFIXATION
204214
prefixEntryFcn = {
205215
@()enableFlip(me);
206216
};
@@ -214,6 +224,7 @@
214224
@()edit(stims,3,'alpha2Out',1);
215225
};
216226

227+
%====================================================FIXATION
217228
%fixate entry
218229
fixEntryFcn = {
219230
@()startRecording(eT);
@@ -254,6 +265,7 @@
254265
fixExitFcn = [ fixExitFcn; {@()changeSet(stims, 3)} ];
255266
end
256267

268+
%====================================================TARGET STIMULUS
257269
%what to run when we enter the stim presentation state
258270
stimEntryFcn = {
259271
@()doStrobe(me,true);
@@ -276,6 +288,8 @@
276288
@()doStrobe(me,true);
277289
};
278290

291+
%====================================================DECISION
292+
279293
%if the subject is correct (small reward)
280294
correctEntryFcn = {
281295
@()timedTTL(rM, tS.rewardPin, tS.rewardTime); % send a reward TTL

CoreProtocols/Twostep_Saccade.mat

-418 KB
Binary file not shown.
Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,6 @@
3636
task.trialVar.values = {'fixateOS','fixateTS'};
3737
task.trialVar.probability = [0.6 0.4];
3838
task.trialVar.comment = 'one or twostep trial based on 60:40 probability';
39-
4039
tL.stimStateNames = {'onestep','twostep'};
4140

4241
%==================================================================
@@ -56,6 +55,7 @@
5655
tS.askForComments = false; %==little UI requestor asks for comments before/after run
5756
tS.saveData = true; %==save behavioural and eye movement data?
5857
tS.name = 'twostep-saccade'; %==name of this protocol
58+
tS.nStims = stims.n; %==number of stimuli
5959
tS.tOut = 1; %if wrong response, how long to time out before next trial
6060
tS.CORRECT = 1; %==the code to send eyetracker for correct trials
6161
tS.BREAKFIX = -1; %==the code to send eyetracker for break fix trials
@@ -87,14 +87,14 @@
8787

8888
%==================================================================
8989
%---------------------------Eyetracker setup-----------------------
90+
% note: opticka UI sets some defaults, but these will override the UI
9091
if me.useEyeLink
9192
warning('Note this protocol is optimised for the Tobii eyetracker, beware...')
9293
eT.name = tS.name;
9394
eT.sampleRate = 250; % sampling rate
9495
eT.calibrationStyle = 'HV5'; % calibration style
95-
eT.calibrationProportion = [0.2 0.2]; %the proportion of the screen occupied by the calibration stimuli
96+
eT.calibrationProportion = [0.4 0.4]; %the proportion of the screen occupied by the calibration stimuli
9697
if tS.saveData == true; eT.recordData = true; end %===save EDF file?
97-
if me.dummyMode; eT.isDummy = true; end %===use dummy or real eyetracker?
9898
%-----------------------
9999
% remote calibration enables manual control and selection of each fixation
100100
% this is useful for a baby or monkey who has not been trained for fixation
@@ -110,11 +110,18 @@
110110
eT.modify.targetbeep = 1; % beep during calibration
111111
elseif me.useTobii
112112
eT.name = tS.name;
113-
eT.model = 'Tobii Pro Spectrum';
114-
eT.trackingMode = 'human';
115-
eT.calPositions = [ .2 .5; .5 .5; .8 .5];
116-
eT.valPositions = [ .5 .5 ];
117-
if me.dummyMode; eT.isDummy = true; end %===use dummy or real eyetracker?
113+
%eT.model = 'Tobii Pro Spectrum';
114+
%eT.sampleRate = 300;
115+
%eT.trackingMode = 'human';
116+
%eT.calibrationStimulus = 'animated';
117+
%eT.autoPace = true;
118+
%-----------------------
119+
% remote calibration enables manual control and selection of each fixation
120+
% this is useful for a baby or monkey who has not been trained for fixation
121+
%eT.manualCalibration = false;
122+
%-----------------------
123+
%eT.calPositions = [ .2 .5; .5 .5; .8 .5];
124+
%eT.valPositions = [ .5 .5 ];
118125
end
119126
%Initialise the eyeTracker object with X, Y, FixInitTime, FixTime, Radius, StrictFix
120127
eT.updateFixationValues(tS.fixX, tS.fixY, tS.firstFixInit, tS.firstFixTime, tS.firstFixRadius, tS.strict);

communication/eyelinkManager.m

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1055,7 +1055,7 @@ function statusMessage(me,message)
10551055
%>
10561056
%>
10571057
% ===================================================================
1058-
function trackerMessage(me, message)
1058+
function trackerMessage(me, message, varargin)
10591059
if me.isConnected
10601060
Eyelink('Message', message );
10611061
if me.verbose; fprintf('-+-+-> EDF Message: %s\n',message);end

opticka.m

Lines changed: 34 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@
3030

3131
properties (SetAccess = protected, GetAccess = public)
3232
%> version number
33-
optickaVersion char = '2.04'
33+
optickaVersion char = '2.05'
3434
%> history of display objects
3535
history
3636
%> is this a remote instance?
@@ -47,7 +47,8 @@
4747
'OKbackgroundColour','OKAntiAliasing','OKbitDepth','OKUseRetina',...
4848
'OKHideFlash','OKUsePhotoDiode','OKTrainingResearcherName',...
4949
'OKTrainingName','OKarduinoPort','OKdPPMode','OKDummyMode',...
50-
'OKOpenGLBlending'}
50+
'OKOpenGLBlending','OKCalEditField','OKValEditField',...
51+
'OKTrackerDropDown'}
5152
end
5253

5354
%=======================================================================
@@ -87,7 +88,7 @@ function router(me,in,vars)
8788
case 'saveData'
8889
me.saveData();
8990
case 'saveProtocol'
90-
me.saveProtocol();
91+
me.saveProtocol(vars);
9192
case 'loadProtocol'
9293
me.loadProtocol(vars);
9394
case 'deleteProtocol'
@@ -320,6 +321,8 @@ function initialiseUI(me)
320321
me.getTaskVals();
321322
me.loadCalibration();
322323
me.refreshProtocolsList();
324+
325+
OKTobiiUpdate(me.h);
323326

324327
%addlistener(me.r,'abortRun',@me.abortRunEvent);
325328
%addlistener(me.r,'endAllRuns',@me.endRunEvent);
@@ -915,23 +918,44 @@ function deleteProtocol(me)
915918
%> Save Protocol
916919
%> @param
917920
% ===================================================================
918-
function saveProtocol(me)
921+
function saveProtocol(me, copy)
922+
if ~exist('copy','var'); copy = false; end
919923
me.paths.currentPath = pwd;
920924
cd(me.paths.protocols);
921-
[f,p] = uiputfile('*.mat','Save Opticka Protocol','Protocol.mat');
925+
if isfield(me.store,'protocolName')
926+
fname = me.store.protocolName;
927+
else
928+
fname = 'Protocol.mat';
929+
end
930+
[f,p] = uiputfile('*.mat','Save Opticka Protocol',fname);
922931
if f ~= 0
923932
cd(p);
924933
tmp = clone(me);
925934
tmp.name = f;
926935
tmp.r.name = f;
927-
tmp.r.paths.stateInfoFile = me.r.paths.stateInfoFile;
936+
928937
tmp.store = struct(); %lets just nuke this incase some rogue handles are lurking
929938
tmp.h = struct(); %remove the handles to the UI which will not be valid on reload
930939
if isfield(tmp.r.screenSettings,'optickahandle'); tmp.r.screenSettings.optickahandle = []; end %!!!this fixes matlab bug 01255186
931940
for i = 1:tmp.r.stimuli.n
932941
cleanHandles(tmp.r.stimuli{i}); %just in case!
933942
end
934-
save(f,'tmp'); %this is the original code -- MAT CRASH on load, it is the same if i save me directly or the cloned variant tmp
943+
[~, ff, ee] = fileparts(me.r.paths.stateInfoFile);
944+
if copy == true
945+
tmp.r.paths.stateInfoFile = [pwd filesep ff ee];
946+
status = copyfile(me.r.paths.stateInfoFile,[ff ee]);
947+
if status ~= 1
948+
warning('Couldn''t copy state info file!');
949+
else
950+
me.r.paths.stateInfoFile = tmp.r.paths.stateInfoFile;
951+
end
952+
save(f,'tmp');
953+
fprintf('\n---> Saving Protocol %s as copy (with state file) to %s\n', f, pwd);
954+
getStateInfo(me);
955+
else
956+
save(f,'tmp');
957+
fprintf('\n---> Saving Protocol %s to %s\n', f, pwd);
958+
end
935959
me.refreshStimulusList;
936960
me.refreshVariableList;
937961
me.refreshProtocolsList;
@@ -1028,6 +1052,7 @@ function loadProtocol(me,ui)
10281052
me.paths.protocols = p;
10291053

10301054
me.comment = ['Prt: ' file];
1055+
me.store.protocolName = file;
10311056

10321057
salutation(me,sprintf('Routing Protocol from %s to %s',tmp.fullName,me.fullName),[],true);
10331058

@@ -1075,7 +1100,7 @@ function loadProtocol(me,ui)
10751100
[~,f,e] = fileparts(tmp.r.paths.stateInfoFile);
10761101
newfile = [pwd filesep f e];
10771102
if exist(tmp.r.paths.stateInfoFile,'file')
1078-
me.r.paths.stateInfoFile =newfile;
1103+
me.r.paths.stateInfoFile = newfile;
10791104
else
10801105
me.r.paths.stateInfoFile = tmp.r.paths.stateInfoFile;
10811106
end
@@ -1216,7 +1241,7 @@ function loadProtocol(me,ui)
12161241

12171242
end
12181243

1219-
me.h.OKOptickaVersion.Text = ['Opticka Experiment Manager V' me.optickaVersion ' - ' file];
1244+
me.h.OKOptickaVersion.Text = ['Opticka Experiment Manager V' me.optickaVersion ' - ' me.store.protocolName];
12201245
me.refreshProtocolsList;
12211246
figure(me.h.OKRoot); drawnow;
12221247
end

optickaCore.m

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -474,7 +474,6 @@ function parseArgs(me, args, allowedProperties)
474474
%> ignores invalid or non-allowed properties
475475
%>
476476
%> @param args input structure
477-
%> @param allowedProperties properties possible to set on construction
478477
% ===================================================================
479478
function addArgs(me, args)
480479
args = optickaCore.makeArgs(args);

0 commit comments

Comments
 (0)