Skip to content

Commit

Permalink
V2.0.5 -- GUI panels for eyelink and tobii now work to set default
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
iandol committed Nov 24, 2021
1 parent 5a26345 commit ef8ee75
Show file tree
Hide file tree
Showing 10 changed files with 267 additions and 194 deletions.
86 changes: 50 additions & 36 deletions CoreProtocols/Saccade_AntiSaccadeStateInfo.m
Original file line number Diff line number Diff line change
Expand Up @@ -4,24 +4,30 @@
%> Objects provide many methods you can run, like sending triggers, showing
%> stimuli, controlling the eyetracker etc.
%
%> The following class objects are already loaded and available to use:
%> This state file is loaded by the runExperiment class. runExperiment
%> initialises other classes that are used to control the experiment. The
%> following class objects are already loaded and available to use:
%
%> me = runExperiment object
%> io = digital I/O to recording system
%> s = screenManager
%> aM = audioManager
%> sM = State Machine
%> eT = eyetracker manager
%> task = task sequence (taskSequence class)
%> rM = Reward Manager (LabJack or Arduino TTL trigger to reward system/Magstim)
%> bR = behavioural record plot (on screen GUI during task run)
%> stims = our list of stimuli
%> tS = general struct to hold variables for this run, will be saved as part of the data
%> me = runExperiment object
%> s = screenManager
%> sM = State Machine
%> eT = eyetracker manager
%> task = task sequence (taskSequence class)
%> stims = our list of stimuli
%> io = digital I/O to recording system
%> aM = audioManager
%> rM = Reward Manager (LabJack or Arduino TTL trigger to reward system/Magstim)
%> bR = behavioural record plot (on screen GUI of trial performance during task run)
%> tL = timeLog that records the timing of experiment
%> tS = general struct to hold variables for this run, will be saved as part of the data

%==================================================================
%---------------------------TASK SWITCH----------------------------
%---------------------------TASK CONFIG----------------------------
%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
tS.includeErrors = false;
tS.type = 'saccade'; %will be be saccade or antisaccade task run?
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
if strcmp(tS.type,'saccade')
stims{1}.showOnTracker = true;
stims{2}.showOnTracker = false;
Expand Down Expand Up @@ -49,7 +55,7 @@
%==================================================================
%------------Debug logging to command window-----------------
%io.verbose = true; %==print out io commands for debugging
%eT.verbose = true; %==print out eyelink commands for debugging
%eT.verbose = true; %==print out eyetracker commands for debugging
%rM.verbose = true; %==print out reward commands for debugging

%==================================================================
Expand All @@ -71,14 +77,14 @@

%==================================================================
%---------------------------Eyetracker setup-----------------------
% note: opticka UI sets some defaults, but these will override the UI
if me.useEyeLink
warning('Note this protocol is optimised for the Tobii eyetracker, beware...')
eT.name = tS.name;
eT.sampleRate = 250; % sampling rate
eT.calibrationStyle = 'HV5'; % calibration style
eT.calibrationProportion = [0.4 0.4]; %the proportion of the screen occupied by the calibration stimuli
if tS.saveData == true; eT.recordData = true; end %===save EDF file?
if me.dummyMode; eT.isDummy = true; end %===use dummy or real eyetracker?
%-----------------------
% remote calibration enables manual control and selection of each fixation
% this is useful for a baby or monkey who has not been trained for fixation
Expand All @@ -94,23 +100,22 @@
eT.modify.targetbeep = 1; % beep during calibration
elseif me.useTobii
eT.name = tS.name;
eT.model = 'Tobii Pro Spectrum';
eT.sampleRate = 300;
eT.trackingMode = 'human';
eT.calibrationStimulus = 'animated';
eT.autoPace = true;
%eT.model = 'Tobii Pro Spectrum';
%eT.sampleRate = 300;
%eT.trackingMode = 'human';
%eT.calibrationStimulus = 'animated';
%eT.autoPace = true;
%-----------------------
% remote calibration enables manual control and selection of each fixation
% this is useful for a baby or monkey who has not been trained for fixation
eT.manualCalibration = false;
%eT.manualCalibration = false;
%-----------------------
eT.calPositions = [ .2 .5; .5 .5; .8 .5];
eT.valPositions = [ .5 .5 ];
if me.dummyMode; eT.isDummy = true; end %===use dummy or real eyetracker?
%eT.calPositions = [ .2 .5; .5 .5; .8 .5];
%eT.valPositions = [ .5 .5 ];
end
%Initialise the eyeTracker object with X, Y, FixInitTime, FixTime, Radius, StrictFix
eT.updateFixationValues(tS.fixX, tS.fixY, tS.firstFixInit, tS.firstFixTime, tS.firstFixRadius, tS.strict);
%make sure we don't start with any exclusion zones set up
%Ensure we don't start with any exclusion zones set up
eT.resetExclusionZones();

%==================================================================
Expand Down Expand Up @@ -171,16 +176,20 @@
sM.skipExitStates = {'fixate','incorrect|breakfix'};

%===================================================================
%-----------------State Machine State Functions---------------------
% each cell {array} holds a set of anonymous function handles which are executed by the
% state machine to control the experiment. The state machine can run sets
% at entry, during, to trigger a transition, and at exit. Remember these
% {sets} need to access the objects that are available within the
% runExperiment context (see top of file). You can also add global
% variables/objects then use these. The values entered here are set on
% load, if you want up-to-date values then you need to use methods/function
% wrappers to retrieve/set them.

%===================================================================
%===================================================================
%-----------------State Machine Task Functions---------------------
% Each cell {array} holds a set of anonymous function handles which are
% executed by the state machine to control the experiment. The state
% machine can run sets at entry ['entryFcn'], during ['withinFcn'], to
% trigger a transition jump to another state ['transitionFcn'], and at exit
% ['exitFcn'. Remember these {sets} need to access the objects that are
% available within the runExperiment context (see top of file). You can
% also add global variables/objects then use these. The values entered here
% are set on load, if you want up-to-date values then you need to use
% methods/function wrappers to retrieve/set them.

%====================================================PAUSE
%pause entry
pauseEntryFcn = {
@()hide(stims);
Expand All @@ -201,6 +210,7 @@
@()startRecording(eT, true); %start recording eye position data again
};

%====================================================PREFIXATION
prefixEntryFcn = {
@()enableFlip(me);
};
Expand All @@ -214,6 +224,7 @@
@()edit(stims,3,'alpha2Out',1);
};

%====================================================FIXATION
%fixate entry
fixEntryFcn = {
@()startRecording(eT);
Expand Down Expand Up @@ -254,6 +265,7 @@
fixExitFcn = [ fixExitFcn; {@()changeSet(stims, 3)} ];
end

%====================================================TARGET STIMULUS
%what to run when we enter the stim presentation state
stimEntryFcn = {
@()doStrobe(me,true);
Expand All @@ -276,6 +288,8 @@
@()doStrobe(me,true);
};

%====================================================DECISION

%if the subject is correct (small reward)
correctEntryFcn = {
@()timedTTL(rM, tS.rewardPin, tS.rewardTime); % send a reward TTL
Expand Down
Binary file modified CoreProtocols/Twostep_Saccade.mat
Binary file not shown.
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,6 @@
task.trialVar.values = {'fixateOS','fixateTS'};
task.trialVar.probability = [0.6 0.4];
task.trialVar.comment = 'one or twostep trial based on 60:40 probability';

tL.stimStateNames = {'onestep','twostep'};

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

%==================================================================
%---------------------------Eyetracker setup-----------------------
% note: opticka UI sets some defaults, but these will override the UI
if me.useEyeLink
warning('Note this protocol is optimised for the Tobii eyetracker, beware...')
eT.name = tS.name;
eT.sampleRate = 250; % sampling rate
eT.calibrationStyle = 'HV5'; % calibration style
eT.calibrationProportion = [0.2 0.2]; %the proportion of the screen occupied by the calibration stimuli
eT.calibrationProportion = [0.4 0.4]; %the proportion of the screen occupied by the calibration stimuli
if tS.saveData == true; eT.recordData = true; end %===save EDF file?
if me.dummyMode; eT.isDummy = true; end %===use dummy or real eyetracker?
%-----------------------
% remote calibration enables manual control and selection of each fixation
% this is useful for a baby or monkey who has not been trained for fixation
Expand All @@ -110,11 +110,18 @@
eT.modify.targetbeep = 1; % beep during calibration
elseif me.useTobii
eT.name = tS.name;
eT.model = 'Tobii Pro Spectrum';
eT.trackingMode = 'human';
eT.calPositions = [ .2 .5; .5 .5; .8 .5];
eT.valPositions = [ .5 .5 ];
if me.dummyMode; eT.isDummy = true; end %===use dummy or real eyetracker?
%eT.model = 'Tobii Pro Spectrum';
%eT.sampleRate = 300;
%eT.trackingMode = 'human';
%eT.calibrationStimulus = 'animated';
%eT.autoPace = true;
%-----------------------
% remote calibration enables manual control and selection of each fixation
% this is useful for a baby or monkey who has not been trained for fixation
%eT.manualCalibration = false;
%-----------------------
%eT.calPositions = [ .2 .5; .5 .5; .8 .5];
%eT.valPositions = [ .5 .5 ];
end
%Initialise the eyeTracker object with X, Y, FixInitTime, FixTime, Radius, StrictFix
eT.updateFixationValues(tS.fixX, tS.fixY, tS.firstFixInit, tS.firstFixTime, tS.firstFixRadius, tS.strict);
Expand Down
2 changes: 1 addition & 1 deletion communication/eyelinkManager.m
Original file line number Diff line number Diff line change
Expand Up @@ -1055,7 +1055,7 @@ function statusMessage(me,message)
%>
%>
% ===================================================================
function trackerMessage(me, message)
function trackerMessage(me, message, varargin)
if me.isConnected
Eyelink('Message', message );
if me.verbose; fprintf('-+-+-> EDF Message: %s\n',message);end
Expand Down
43 changes: 34 additions & 9 deletions opticka.m
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@

properties (SetAccess = protected, GetAccess = public)
%> version number
optickaVersion char = '2.04'
optickaVersion char = '2.05'
%> history of display objects
history
%> is this a remote instance?
Expand All @@ -47,7 +47,8 @@
'OKbackgroundColour','OKAntiAliasing','OKbitDepth','OKUseRetina',...
'OKHideFlash','OKUsePhotoDiode','OKTrainingResearcherName',...
'OKTrainingName','OKarduinoPort','OKdPPMode','OKDummyMode',...
'OKOpenGLBlending'}
'OKOpenGLBlending','OKCalEditField','OKValEditField',...
'OKTrackerDropDown'}
end

%=======================================================================
Expand Down Expand Up @@ -87,7 +88,7 @@ function router(me,in,vars)
case 'saveData'
me.saveData();
case 'saveProtocol'
me.saveProtocol();
me.saveProtocol(vars);
case 'loadProtocol'
me.loadProtocol(vars);
case 'deleteProtocol'
Expand Down Expand Up @@ -320,6 +321,8 @@ function initialiseUI(me)
me.getTaskVals();
me.loadCalibration();
me.refreshProtocolsList();

OKTobiiUpdate(me.h);

%addlistener(me.r,'abortRun',@me.abortRunEvent);
%addlistener(me.r,'endAllRuns',@me.endRunEvent);
Expand Down Expand Up @@ -915,23 +918,44 @@ function deleteProtocol(me)
%> Save Protocol
%> @param
% ===================================================================
function saveProtocol(me)
function saveProtocol(me, copy)
if ~exist('copy','var'); copy = false; end
me.paths.currentPath = pwd;
cd(me.paths.protocols);
[f,p] = uiputfile('*.mat','Save Opticka Protocol','Protocol.mat');
if isfield(me.store,'protocolName')
fname = me.store.protocolName;
else
fname = 'Protocol.mat';
end
[f,p] = uiputfile('*.mat','Save Opticka Protocol',fname);
if f ~= 0
cd(p);
tmp = clone(me);
tmp.name = f;
tmp.r.name = f;
tmp.r.paths.stateInfoFile = me.r.paths.stateInfoFile;

tmp.store = struct(); %lets just nuke this incase some rogue handles are lurking
tmp.h = struct(); %remove the handles to the UI which will not be valid on reload
if isfield(tmp.r.screenSettings,'optickahandle'); tmp.r.screenSettings.optickahandle = []; end %!!!this fixes matlab bug 01255186
for i = 1:tmp.r.stimuli.n
cleanHandles(tmp.r.stimuli{i}); %just in case!
end
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
[~, ff, ee] = fileparts(me.r.paths.stateInfoFile);
if copy == true
tmp.r.paths.stateInfoFile = [pwd filesep ff ee];
status = copyfile(me.r.paths.stateInfoFile,[ff ee]);
if status ~= 1
warning('Couldn''t copy state info file!');
else
me.r.paths.stateInfoFile = tmp.r.paths.stateInfoFile;
end
save(f,'tmp');
fprintf('\n---> Saving Protocol %s as copy (with state file) to %s\n', f, pwd);
getStateInfo(me);
else
save(f,'tmp');
fprintf('\n---> Saving Protocol %s to %s\n', f, pwd);
end
me.refreshStimulusList;
me.refreshVariableList;
me.refreshProtocolsList;
Expand Down Expand Up @@ -1028,6 +1052,7 @@ function loadProtocol(me,ui)
me.paths.protocols = p;

me.comment = ['Prt: ' file];
me.store.protocolName = file;

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

Expand Down Expand Up @@ -1075,7 +1100,7 @@ function loadProtocol(me,ui)
[~,f,e] = fileparts(tmp.r.paths.stateInfoFile);
newfile = [pwd filesep f e];
if exist(tmp.r.paths.stateInfoFile,'file')
me.r.paths.stateInfoFile =newfile;
me.r.paths.stateInfoFile = newfile;
else
me.r.paths.stateInfoFile = tmp.r.paths.stateInfoFile;
end
Expand Down Expand Up @@ -1216,7 +1241,7 @@ function loadProtocol(me,ui)

end

me.h.OKOptickaVersion.Text = ['Opticka Experiment Manager V' me.optickaVersion ' - ' file];
me.h.OKOptickaVersion.Text = ['Opticka Experiment Manager V' me.optickaVersion ' - ' me.store.protocolName];
me.refreshProtocolsList;
figure(me.h.OKRoot); drawnow;
end
Expand Down
1 change: 0 additions & 1 deletion optickaCore.m
Original file line number Diff line number Diff line change
Expand Up @@ -474,7 +474,6 @@ function parseArgs(me, args, allowedProperties)
%> ignores invalid or non-allowed properties
%>
%> @param args input structure
%> @param allowedProperties properties possible to set on construction
% ===================================================================
function addArgs(me, args)
args = optickaCore.makeArgs(args);
Expand Down
Loading

0 comments on commit ef8ee75

Please sign in to comment.