rmeddis@28: function varargout = subjGUI_MT(varargin) rmeddis@28: rmeddis@28: % Begin initialization code - DO NOT EDIT rmeddis@28: gui_Singleton = 1; rmeddis@28: gui_State = struct('gui_Name', mfilename, ... rmeddis@28: 'gui_Singleton', gui_Singleton, ... rmeddis@28: 'gui_OpeningFcn', @subjGUI_MT_OpeningFcn, ... rmeddis@28: 'gui_OutputFcn', @subjGUI_MT_OutputFcn, ... rmeddis@28: 'gui_LayoutFcn', [] , ... rmeddis@28: 'gui_Callback', []); rmeddis@28: if nargin && isstr(varargin{1}) rmeddis@28: gui_State.gui_Callback = str2func(varargin{1}); rmeddis@28: end rmeddis@28: rmeddis@28: if nargout rmeddis@28: [varargout{1:nargout}] = gui_mainfcn(gui_State, varargin{:}); rmeddis@28: else rmeddis@28: gui_mainfcn(gui_State, varargin{:}); rmeddis@28: end rmeddis@28: % End initialization code - DO NOT EDIT rmeddis@28: rmeddis@28: % --- Executes just before subjGUI_MT is made visible. rmeddis@28: function subjGUI_MT_OpeningFcn(hObject, eventdata, handles, varargin) rmeddis@28: rmeddis@28: % Choose default command line output for subjGUI_MT rmeddis@28: handles.output = hObject; rmeddis@28: initializeGUI(handles) rmeddis@28: guidata(hObject, handles); rmeddis@28: rmeddis@28: function varargout = subjGUI_MT_OutputFcn(hObject, eventdata, handles) rmeddis@28: % Get default command line output from handles structure rmeddis@28: varargout{1} = handles.output; rmeddis@28: rmeddis@28: % -----------------------------------------------------initializeGUI rmeddis@28: function initializeGUI(handles) rmeddis@28: global experiment rmeddis@28: global subjectGUIHandles expGUIhandles rmeddis@28: addpath (['..' filesep 'MAP'], ['..' filesep 'utilities'], ... rmeddis@28: ['..' filesep 'parameterStore'], ['..' filesep 'wavFileStore'],... rmeddis@28: ['..' filesep 'testPrograms']) rmeddis@28: rmeddis@28: dbstop if error rmeddis@28: rmeddis@28: % subjectGUI size and location % [left bottom width height] rmeddis@28: scrnsize=get(0,'screensize'); rmeddis@28: set(0, 'units','pixels') rmeddis@28: switch experiment.ear rmeddis@28: % use default size unless... rmeddis@28: case {'MAPmodel', 'MAPmodelMultich', 'MAPmodelSingleCh', ... rmeddis@28: 'statsModelLogistic','statsModelRareEvent'} rmeddis@28: % subjectGUI not needed for modelling so minimize subject GUI rmeddis@28: set(gcf, 'units','pixels') rmeddis@28: y=[0*scrnsize(3) 0.8*scrnsize(4) 0.1*scrnsize(3) 0.2*scrnsize(4)]; rmeddis@28: set(gcf,'position',y, 'color',[.871 .961 .996]) rmeddis@28: rmeddis@28: rmeddis@28: rmeddis@28: case 'MAPmodelListen', rmeddis@28: % subjectGUI is needed for display purposes. Make it large rmeddis@28: set(gcf, 'units','pixels') rmeddis@28: y=[.665*scrnsize(3) 0.02*scrnsize(4) ... rmeddis@28: 0.33*scrnsize(3) 0.5*scrnsize(4)]; % alongside rmeddis@28: set(gcf,'position',y, 'color',[.871 .961 .996]) rmeddis@28: end rmeddis@28: rmeddis@28: switch experiment.ear rmeddis@28: case{'left', 'right','diotic', 'dichoticLeft','dichoticRight'} rmeddis@28: % Look to see if the button box exists and, if so, initialise it rmeddis@28: buttonBoxIntitialize % harmless if no button box attached rmeddis@28: end rmeddis@28: rmeddis@28: % function varargout = subjGUI_MT(varargin) rmeddis@28: % rmeddis@28: % % Begin initialization code - DO NOT EDIT rmeddis@28: % gui_Singleton = 1; rmeddis@28: % gui_State = struct('gui_Name', mfilename, ... rmeddis@28: % 'gui_Singleton', gui_Singleton, ... rmeddis@28: % 'gui_OpeningFcn', @subjGUI_MT_OpeningFcn, ... rmeddis@28: % 'gui_OutputFcn', @subjGUI_MT_OutputFcn, ... rmeddis@28: % 'gui_LayoutFcn', [] , ... rmeddis@28: % 'gui_Callback', []); rmeddis@28: % if nargin && isstr(varargin{1}) rmeddis@28: % gui_State.gui_Callback = str2func(varargin{1}); rmeddis@28: % end rmeddis@28: % rmeddis@28: % if nargout rmeddis@28: % [varargout{1:nargout}] = gui_mainfcn(gui_State, varargin{:}); rmeddis@28: % else rmeddis@28: % gui_mainfcn(gui_State, varargin{:}); rmeddis@28: % end rmeddis@28: % % End initialization code - DO NOT EDIT rmeddis@28: % rmeddis@28: % % --- Executes just before subjGUI_MT is made visible. rmeddis@28: % function subjGUI_MT_OpeningFcn(hObject, eventdata, handles, varargin) rmeddis@28: % rmeddis@28: % % Choose default command line output for subjGUI_MT rmeddis@28: % handles.output = hObject; rmeddis@28: % initializeGUI(handles) rmeddis@28: % guidata(hObject, handles); rmeddis@28: % rmeddis@28: % function varargout = subjGUI_MT_OutputFcn(hObject, eventdata, handles) rmeddis@28: % % Get default command line output from handles structure rmeddis@28: % varargout{1} = handles.output; rmeddis@28: % rmeddis@28: % % -----------------------------------------------------initializeGUI rmeddis@28: % function initializeGUI(handles) rmeddis@28: % global experiment rmeddis@28: % global subjectGUIHandles expGUIhandles rmeddis@28: % rmeddis@28: % dbstop if error rmeddis@28: % rmeddis@28: % % subjectGUI size and location % [left bottom width height] rmeddis@28: % scrnsize=get(0,'screensize'); rmeddis@28: % set(0, 'units','pixels') rmeddis@28: % switch experiment.ear rmeddis@28: % % use default size unless... rmeddis@28: % case {'MAPmodel', 'MAPmodelMultiCh','MAPmodelSingleCh', ... rmeddis@28: % 'statsModelLogistic','statsModelRareEvent'} rmeddis@28: % % subjectGUI not needed for modelling so minimize subject GUI rmeddis@28: % set(gcf, 'units','pixels') rmeddis@28: % y=[0*scrnsize(3) 0.8*scrnsize(4) 0.1*scrnsize(3) 0.2*scrnsize(4)]; rmeddis@28: % set(gcf,'position',y, 'color',[.871 .961 .996]) rmeddis@28: % rmeddis@28: % case 'MAPmodelListen', rmeddis@28: % % subjectGUI is needed for display purposes. Make it large rmeddis@28: % set(gcf, 'units','pixels') rmeddis@28: % y=[.665*scrnsize(3) 0.02*scrnsize(4) ... rmeddis@28: % 0.33*scrnsize(3) 0.5*scrnsize(4)]; % alongside rmeddis@28: % set(gcf,'position',y, 'color',[.871 .961 .996]) rmeddis@28: % end rmeddis@28: % rmeddis@28: % switch experiment.ear rmeddis@28: % case{'left', 'right','diotic', 'dichoticLeft','dichoticRight'} rmeddis@28: % % Look to see if the button box exists and, if so, initialise it rmeddis@28: % buttonBoxIntitialize % harmless if no button box attached rmeddis@28: % end rmeddis@28: rmeddis@28: % clear display of previous mean values. This is a new measurement series rmeddis@28: axes(expGUIhandles.axes4), cla rmeddis@28: reset (expGUIhandles.axes4) rmeddis@28: rmeddis@28: % handles needed in non-callback routines below rmeddis@28: subjectGUIHandles=handles; rmeddis@28: rmeddis@28: % start immediately rmeddis@28: startNewExperiment(handles, expGUIhandles) rmeddis@28: % This is the end of the experiment. Exit here and return to ExpGUI. rmeddis@28: rmeddis@28: % ----------------------------------------------------- startNewExperiment rmeddis@28: function startNewExperiment(handles, expGUIhandles) rmeddis@28: % An experiment consists of a series of 'runs'. rmeddis@28: % Resets all relevant variables at the beginning of a new experiment. rmeddis@28: global experiment stimulusParameters betweenRuns rmeddis@28: rmeddis@28: % 'start new experiment' button is the only valid action now rmeddis@28: experiment.status='waitingForStart'; rmeddis@28: rmeddis@28: switch experiment.threshEstMethod rmeddis@28: % add appropriate labels to subject GUI buttons rmeddis@28: case {'2I2AFC++', '2I2AFC+++'} rmeddis@28: set(handles.pushbutton3,'string','') rmeddis@28: set(handles.pushbutton2,'string','2') rmeddis@28: set(handles.pushbutton1,'string','1') rmeddis@28: set(handles.pushbutton0,'string','0') rmeddis@28: case {'MaxLikelihood', 'oneIntervalUpDown'} rmeddis@28: if stimulusParameters.includeCue rmeddis@28: set(handles.pushbutton3,'string','') rmeddis@28: set(handles.pushbutton2,'string','2') rmeddis@28: set(handles.pushbutton1,'string','1') rmeddis@28: set(handles.pushbutton0,'string','0') rmeddis@28: else rmeddis@28: set(handles.pushbutton3,'string','') rmeddis@28: set(handles.pushbutton2,'string','YES') rmeddis@28: set(handles.pushbutton1,'string','NO') rmeddis@28: set(handles.pushbutton0,'string','') rmeddis@28: end rmeddis@28: end rmeddis@28: rmeddis@28: switch experiment.paradigm rmeddis@28: case 'discomfort' rmeddis@28: set(handles.pushbutton3,'string','') rmeddis@28: set(handles.pushbutton2,'string','uncomfortable') rmeddis@28: set(handles.pushbutton1,'string','loud') rmeddis@28: set(handles.pushbutton0,'string','comfortable') rmeddis@28: experiment.allowCatchTrials=0; rmeddis@28: end rmeddis@28: rmeddis@28: % experiment.subjGUIfontSize is set on expGUI rmeddis@28: set(handles.pushbutton3,'FontSize',experiment.subjGUIfontSize) rmeddis@28: set(handles.pushbutton2,'FontSize',experiment.subjGUIfontSize) rmeddis@28: set(handles.pushbutton1,'FontSize',experiment.subjGUIfontSize) rmeddis@28: set(handles.pushbutton0,'FontSize',experiment.subjGUIfontSize) rmeddis@28: set(handles.pushbuttoNotSure,'FontSize',experiment.subjGUIfontSize) rmeddis@28: set(handles.pushbuttonGO,'FontSize',experiment.subjGUIfontSize) rmeddis@28: set(handles.textMSG,'FontSize',experiment.subjGUIfontSize) rmeddis@28: rmeddis@28: set(handles.pushbutton19,'visible','off') % unused button rmeddis@28: rmeddis@28: % start command window summary of progress rmeddis@28: fprintf(' \n ----------- NEW MEASUREMENTS\n') rmeddis@28: disp(['paradigm: ' experiment.paradigm]) rmeddis@28: cla(expGUIhandles.axes1) rmeddis@28: cla(expGUIhandles.axes2) rmeddis@28: cla(expGUIhandles.axes4) rmeddis@28: cla(expGUIhandles.axes5) rmeddis@28: rmeddis@28: experiment.stop=0; % status of 'stop' button rmeddis@28: experiment.pleaseRepeat=0; % status of 'repeat' button rmeddis@28: experiment.buttonBoxStatus='not busy'; rmeddis@28: rmeddis@28: % date and time and replace ':' with '_' rmeddis@28: date=datestr(now);idx=findstr(':',date);date(idx)='_'; rmeddis@28: experiment.date=date; rmeddis@28: timeNow=clock; betweenRuns.timeNow= timeNow; rmeddis@28: experiment.timeAtStart=[num2str(timeNow(4)) ':' num2str(timeNow(5))]; rmeddis@28: experiment.minElapsed=0; rmeddis@28: rmeddis@28: % unpack catch trial rates. The rate declines from the start rate rmeddis@28: % to the base rate using a time constant. rmeddis@28: stimulusParameters.catchTrialRate=stimulusParameters.catchTrialRates(1); rmeddis@28: stimulusParameters.catchTrialBaseRate=... rmeddis@28: stimulusParameters.catchTrialRates(2); rmeddis@28: stimulusParameters.catchTrialTimeConstant=... rmeddis@28: stimulusParameters.catchTrialRates(3); rmeddis@28: if stimulusParameters.catchTrialBaseRate==0 rmeddis@28: stimulusParameters.catchTrialRate=0; rmeddis@28: end rmeddis@28: rmeddis@28: % for human measurements only, identify the start catch trial rate rmeddis@28: switch experiment.ear rmeddis@28: case{'left', 'right','diotic', 'dichoticLeft','dichoticRight'} rmeddis@28: fprintf('stimulusParameters.catchTrialRate= %6.3f\n', ... rmeddis@28: stimulusParameters.catchTrialRate) rmeddis@28: end rmeddis@28: rmeddis@28: % Reset betweenRuns parameters. this occurs only at experiment start rmeddis@28: % withinRuns values are reset in 'startNewRun' rmeddis@28: % this approach creates a more readable structure summary printout. rmeddis@28: betweenRuns.thresholds=[]; rmeddis@28: betweenRuns.thresholds_mean=[]; rmeddis@28: betweenRuns.thresholds_median=[]; rmeddis@28: betweenRuns.forceThresholds=[]; rmeddis@28: betweenRuns.observationCount=[]; rmeddis@28: betweenRuns.catchTrials=[]; rmeddis@28: betweenRuns.timesOfFirstReversals=[]; rmeddis@28: betweenRuns.bestThresholdTracks=[]; rmeddis@28: betweenRuns.levelTracks=[]; rmeddis@28: betweenRuns.responseTracks=[]; rmeddis@28: betweenRuns.slopeKTracks=[]; rmeddis@28: betweenRuns.gainTracks=[]; rmeddis@28: betweenRuns.VminTracks=[]; rmeddis@28: betweenRuns.bestGain=[]; rmeddis@28: betweenRuns.bestVMin=[]; rmeddis@28: betweenRuns.bestPaMin=[]; rmeddis@28: betweenRuns.bestLogisticM=[]; rmeddis@28: betweenRuns.bestLogisticK=[]; rmeddis@28: betweenRuns.resets=0; rmeddis@28: betweenRuns.runNumber=0; rmeddis@28: rmeddis@28: % Up to two parameters can be changed between runs rmeddis@28: % Find the variable parameters and randomize them rmeddis@28: % e.g. 'variableList1 = stimulusParameters.targetFrequency;' rmeddis@28: eval(['variableList1=stimulusParameters.' betweenRuns.variableName1 ';']); rmeddis@28: eval(['variableList2=stimulusParameters.' betweenRuns.variableName2 ';']); rmeddis@28: nVar1=length(variableList1); rmeddis@28: nVar2=length(variableList2); rmeddis@28: rmeddis@28: % Create two sequence vectors to represent the sequence of var1 and var2 rmeddis@28: % values. 'var1' changes most rapidly. rmeddis@28: switch betweenRuns.randomizeSequence rmeddis@28: case 'fixed sequence' rmeddis@28: var1Sequence=repmat(betweenRuns.variableList1, 1,nVar2); rmeddis@28: var2Sequence=reshape(repmat(betweenRuns.variableList2, ... rmeddis@28: nVar1,1),1,nVar1*nVar2); rmeddis@28: case 'randomize within blocks' rmeddis@28: % the blocks are not randomized rmeddis@28: var1Sequence=betweenRuns.variableList1; rmeddis@28: ranNums=rand(1, length(var1Sequence)); [x idx]=sort(ranNums); rmeddis@28: var1Sequence=var1Sequence(idx); rmeddis@28: betweenRuns.variableList1=variableList1(idx); rmeddis@28: var1Sequence=repmat(var1Sequence, 1,nVar2); rmeddis@28: var2Sequence=reshape(repmat(betweenRuns.variableList2, nVar1,1)... rmeddis@28: ,1,nVar1*nVar2); rmeddis@28: case 'randomize across blocks' rmeddis@28: var1Sequence=repmat(betweenRuns.variableList1, 1,nVar2); rmeddis@28: var2Sequence=reshape(repmat(betweenRuns.variableList2, nVar1,1),... rmeddis@28: 1,nVar1*nVar2); rmeddis@28: ranNums=rand(1, nVar1*nVar2); rmeddis@28: [x idx]=sort(ranNums); rmeddis@28: var1Sequence=var1Sequence(idx); rmeddis@28: var2Sequence=var2Sequence(idx); rmeddis@28: % there should be one start value for every combination rmeddis@28: % of var1/ var2. In principle this allows these values to be rmeddis@28: % programmed. Not currently in use. rmeddis@28: stimulusParameters.WRVstartValues=... rmeddis@28: stimulusParameters.WRVstartValues(idx); rmeddis@28: end rmeddis@28: betweenRuns.var1Sequence=var1Sequence; rmeddis@28: betweenRuns.var2Sequence=var2Sequence; rmeddis@28: rmeddis@28: % caught out vector needs to be linked to the length of the whole sequence rmeddis@28: betweenRuns.caughtOut=zeros(1,length(var1Sequence)); rmeddis@28: rmeddis@28: disp('planned sequence:') rmeddis@28: if min(var1Sequence)>1 rmeddis@28: % use decidaml places only if necessary rmeddis@28: disp([betweenRuns.variableName1 ': ' num2str(var1Sequence,'%6.0f') ]) rmeddis@28: else rmeddis@28: disp([betweenRuns.variableName1 ': ' num2str(var1Sequence,'%8.3f') ]) rmeddis@28: end rmeddis@28: if min(var1Sequence)>1 rmeddis@28: disp([betweenRuns.variableName2 ': ' num2str(var2Sequence,'%6.0f') ]) rmeddis@28: else rmeddis@28: disp([betweenRuns.variableName2 ': ' num2str(var2Sequence,'%8.3f') ]) rmeddis@28: end rmeddis@28: rmeddis@28: fprintf('\nvariable1 \t variable2\t \n') rmeddis@28: fprintf('%s \t %s\t Threshold \n',betweenRuns.variableName1,... rmeddis@28: betweenRuns.variableName2) rmeddis@28: rmeddis@28: % Light up 'GO' on subjGUI and advise. rmeddis@28: set(handles.editdigitInput,'visible','off') rmeddis@28: switch experiment.ear rmeddis@28: case {'statsModelLogistic', 'statsModelRareEvent',... rmeddis@28: 'MAPmodel', 'MAPmodelMultiCh','MAPmodelSingleCh'} rmeddis@28: % no changes required if model used rmeddis@28: otherwise rmeddis@28: set(handles.pushbuttonGO,'backgroundcolor','y') rmeddis@28: set(handles.pushbuttonGO,'visible','on') rmeddis@28: set(handles.frame1,'visible','off') rmeddis@28: set(handles.textMSG,'backgroundcolor', 'w') rmeddis@28: msg=[{'Ready to start new Experiment'}, {' '}, {'Please, click on the GO button'}]; rmeddis@28: set(handles.textMSG,'string', msg) rmeddis@28: rmeddis@28: set(handles.pushbuttoNotSure,'visible','off') rmeddis@28: set(handles.pushbuttonWrongButton,'visible','off') rmeddis@28: set(handles.pushbutton3,'visible','off') rmeddis@28: set(handles.pushbutton2,'visible','off') rmeddis@28: set(handles.pushbutton1,'visible','off') rmeddis@28: set(handles.pushbutton0,'visible','off') rmeddis@28: pause(.1) % to allow display to be drawn rmeddis@28: end rmeddis@28: rmeddis@28: % Selecting the 'GO' button is the only valid operation action now rmeddis@28: experiment.status='waitingForGO'; % i.e. waiting for new run rmeddis@28: rmeddis@28: % control is now either manual, model (MAP) or randomization rmeddis@28: switch experiment.ear rmeddis@28: case {'MAPmodel','MAPmodelMultiCh','MAPmodelSingleCh','MAPmodelListen'} % MAP model is now the subject rmeddis@28: stimulusParameters.calibrationdB=0; % Pascals required! rmeddis@28: MAPmodelRunsGUI(handles) rmeddis@28: % model is now the subject rmeddis@28: case {'statsModelLogistic', 'statsModelRareEvent'} rmeddis@28: % no catch trials for the statistical model rmeddis@28: stimulusParameters.catchTrialBaseRate=0; rmeddis@28: stimulusParameters.catchTrialRate=0; rmeddis@28: statsModelRunsGUI(handles) rmeddis@28: otherwise rmeddis@28: %manual operation; wait for user to click on 'GO' rmeddis@28: end rmeddis@28: rmeddis@28: % Experiment complete (after MAP or randomization) rmeddis@28: % return to 'initializeGUI' and then back to expGUI rmeddis@28: % Manual control finds its own way home. Program control assumed when rmeddis@28: % the user hits the GO button rmeddis@28: rmeddis@28: % ----------------------------------------------------------------- startNewRun rmeddis@28: function startNewRun(handles) rmeddis@28: % There are many ways to arrive here. rmeddis@28: % Under manual control this is achieved by hitting the GO button rmeddis@28: % either via the button box or a mouse click rmeddis@28: % MAP and randomization methods call this too rmeddis@28: rmeddis@28: global experiment stimulusParameters betweenRuns withinRuns expGUIhandles rmeddis@28: global LevittControl rareEvent rmeddis@28: rmeddis@28: figure(handles.figure1) % guarantee subject GUI visibility rmeddis@28: rmeddis@28: % ignore call if program is not ready rmeddis@28: if ~strcmp(experiment.status,'waitingForGO'), return, end rmeddis@28: rmeddis@28: set(handles.pushbuttonGO,'visible','off') rmeddis@28: rmeddis@28: % append message to expGUI message box to alert experimenter that the user rmeddis@28: % is active rmeddis@28: addToMsg('Starting new trial',0) rmeddis@28: rmeddis@28: cla(expGUIhandles.axes1), title(''); % stimulus rmeddis@28: cla(expGUIhandles.axes2), title(''); % WRV track rmeddis@28: drawnow rmeddis@28: rmeddis@28: betweenRuns.runNumber=betweenRuns.runNumber + 1; rmeddis@28: rmeddis@28: withinRuns.trialNumber=1; rmeddis@28: withinRuns.variableValue=... rmeddis@28: stimulusParameters.WRVstartValues(betweenRuns.runNumber); rmeddis@28: % add random jitter to start level rmeddis@28: if ~experiment.singleShot rmeddis@28: % SS or single shot allows the user to precisely set the WRV rmeddis@28: withinRuns.variableValue=withinRuns.variableValue +... rmeddis@28: (rand-0.5)*stimulusParameters.jitterStartdB; rmeddis@28: end rmeddis@28: rmeddis@28: withinRuns.peaks=[]; rmeddis@28: withinRuns.troughs=[]; rmeddis@28: withinRuns.levelList=[]; rmeddis@28: withinRuns.meanEstTrack=[]; rmeddis@28: withinRuns.bestSlopeK=[]; rmeddis@28: withinRuns.bestGain=[]; rmeddis@28: withinRuns.bestVMin=[]; rmeddis@28: withinRuns.forceThreshold=NaN; rmeddis@28: withinRuns.responseList=[]; rmeddis@28: withinRuns.caughtOut=0; rmeddis@28: withinRuns.wrongButton=0; rmeddis@28: withinRuns.catchTrialCount=0; rmeddis@28: withinRuns.thresholdEstimateTrack=[]; rmeddis@28: rmeddis@28: withinRuns.beginningOfPhase2=0; rmeddis@28: withinRuns.nowInPhase2=0; rmeddis@28: withinRuns.thisIsRepeatTrial=0; rmeddis@28: rmeddis@28: rareEvent.Euclid=NaN; rmeddis@28: rareEvent.bestGain=NaN; rmeddis@28: rareEvent.bestVMin=NaN; rmeddis@28: rareEvent.thresholddB=0; rmeddis@28: rareEvent.bestPaMindB=NaN; rmeddis@28: rareEvent.predictionLevels=[]; rmeddis@28: rareEvent.predictionsRE=[]; rmeddis@28: rmeddis@28: LevittControl.sequence=[]; rmeddis@28: rmeddis@28: % on-screen count of number of runs still to complete rmeddis@28: trialsToGo=length(betweenRuns.var1Sequence)-betweenRuns.runNumber; rmeddis@28: set(handles.toGoCounter,'string', trialsToGo); rmeddis@28: rmeddis@28: switch experiment.threshEstMethod rmeddis@28: case {'2I2AFC++', '2I2AFC+++'} rmeddis@28: % For 2I2AFC the buttons need to be on the screen ab initio rmeddis@28: Levitt2 % inititalize Levitt2 procedure rmeddis@28: end rmeddis@28: rmeddis@28: switch experiment.ear rmeddis@28: case{'left', 'right','diotic', 'dichoticLeft','dichoticRight'} rmeddis@28: % allow subject time to recover from 'go' press rmeddis@28: pause(experiment.clickToStimulusPause) rmeddis@28: end rmeddis@28: rmeddis@28: errormsg=nextStimulus(handles); % get the show on the road rmeddis@28: rmeddis@28: % switch experiment.paradigm rmeddis@28: % case 'SRT' rmeddis@28: % set(handles.editdigitInput,'visible','on') rmeddis@28: % uicontrol(handles.editdigitInput) rmeddis@28: % end rmeddis@28: rmeddis@28: % terminate if there is any kind of problem rmeddis@28: if ~isempty(errormsg) rmeddis@28: % e.g. limits exceeded, clipping rmeddis@28: disp(errormsg) rmeddis@28: runCompleted(handles) rmeddis@28: return rmeddis@28: end rmeddis@28: rmeddis@28: % return route is variable (see intro to this function) rmeddis@28: rmeddis@28: % -----------------------------------------------------buttonBox_callback rmeddis@28: function buttonBox_callback(obj, info) rmeddis@28: global experiment rmeddis@28: global serobj rmeddis@28: global subjectGUIHandles rmeddis@28: rmeddis@28: % do not accept callback if one is already in process rmeddis@28: if strcmp(experiment.buttonBoxStatus,'busy') rmeddis@28: disp(' ignored button press') rmeddis@28: return rmeddis@28: end rmeddis@28: experiment.buttonBoxStatus='busy'; rmeddis@28: % fclose(serobj) rmeddis@28: rmeddis@28: % identify the code of the button pressed rmeddis@28: buttonPressedNo = fscanf(serobj,'%c',1); rmeddis@28: rmeddis@28: % This is the map from the button to the Cedrus codes rmeddis@28: switch experiment.buttonBoxType rmeddis@28: case 'horizontal' rmeddis@28: pbGo='7'; pb0='1'; rmeddis@28: pb1='2'; pb2='3'; rmeddis@28: pbRepeat='4'; pbWrong='6'; pbBlank='5'; rmeddis@28: case 'square' rmeddis@28: pbGo='7'; pb0='1'; rmeddis@28: pb1='3'; pb2='4'; rmeddis@28: pbRepeat='8'; pbWrong='6'; pbBlank='5'; rmeddis@28: end rmeddis@28: rmeddis@28: % decide what to do rmeddis@28: switch experiment.status rmeddis@28: case {'presentingStimulus', 'waitingForStart', 'trialcompleted', ... rmeddis@28: 'endOfExperiment'} rmeddis@28: disp(' ignored button press') rmeddis@28: rmeddis@28: case 'waitingForGO' rmeddis@28: % i.e. waiting for new run rmeddis@28: if strcmp(buttonPressedNo,pbGo) % only GO button accepted rmeddis@28: startNewRun(subjectGUIHandles) rmeddis@28: else rmeddis@28: disp(' ignored button press') rmeddis@28: end rmeddis@28: rmeddis@28: case 'waitingForResponse' rmeddis@28: % response to stimuli rmeddis@28: switch buttonPressedNo rmeddis@28: case pb0 % button 0 (top left) rmeddis@28: switch experiment.threshEstMethod rmeddis@28: case {'2I2AFC++', '2I2AFC+++'} rmeddis@28: disp(' ignored button press') rmeddis@28: otherwise rmeddis@28: set(subjectGUIHandles.pushbutton0,... rmeddis@28: 'backgroundcolor','r') rmeddis@28: pause(.1) rmeddis@28: set(subjectGUIHandles.pushbutton0,... rmeddis@28: 'backgroundcolor',get(0,... rmeddis@28: 'defaultUicontrolBackgroundColor')) rmeddis@28: userSelects0or1(subjectGUIHandles) rmeddis@28: end rmeddis@28: rmeddis@28: case pb1 % button 1 (bottom left) rmeddis@28: switch experiment.threshEstMethod rmeddis@28: case {'2I2AFC++', '2I2AFC+++'} rmeddis@28: userSelects0or1(subjectGUIHandles) rmeddis@28: otherwise rmeddis@28: set(subjectGUIHandles.pushbutton1,... rmeddis@28: 'backgroundcolor','r') rmeddis@28: pause(.1) rmeddis@28: set(subjectGUIHandles.pushbutton1,... rmeddis@28: 'backgroundcolor',get(0,... rmeddis@28: 'defaultUicontrolBackgroundColor')) rmeddis@28: userSelects0or1(subjectGUIHandles) rmeddis@28: end rmeddis@28: rmeddis@28: case pb2 % button 2 (bottom right) rmeddis@28: switch experiment.threshEstMethod rmeddis@28: case {'2I2AFC++', '2I2AFC+++'} rmeddis@28: userSelects2 (subjectGUIHandles) rmeddis@28: otherwise rmeddis@28: set(subjectGUIHandles.pushbutton2,... rmeddis@28: 'backgroundcolor','r') rmeddis@28: pause(.1) rmeddis@28: set(subjectGUIHandles.pushbutton2,... rmeddis@28: 'backgroundcolor',get(0,... rmeddis@28: 'defaultUicontrolBackgroundColor')) rmeddis@28: userSelects2 (subjectGUIHandles) rmeddis@28: end rmeddis@28: rmeddis@28: case pbRepeat % extreme right button rmeddis@28: switch experiment.threshEstMethod rmeddis@28: case {'2I2AFC++', '2I2AFC+++'} rmeddis@28: disp(' ignored button press') rmeddis@28: otherwise rmeddis@28: rmeddis@28: set(subjectGUIHandles.pushbuttoNotSure,... rmeddis@28: 'backgroundcolor','r') rmeddis@28: pause(.1) rmeddis@28: set(subjectGUIHandles.pushbuttoNotSure,... rmeddis@28: 'backgroundcolor',get(0,... rmeddis@28: 'defaultUicontrolBackgroundColor')) rmeddis@28: userSelectsPleaseRepeat (subjectGUIHandles) rmeddis@28: end rmeddis@28: rmeddis@28: case {pbWrong, pbBlank} rmeddis@28: disp(' ignored button press') rmeddis@28: rmeddis@28: otherwise % unrecognised button rmeddis@28: disp('ignored button press') rmeddis@28: end % end (button press number) rmeddis@28: otherwise rmeddis@28: disp('ignored button press') rmeddis@28: end % experiment status rmeddis@28: rmeddis@28: % All processing returns through here. rmeddis@28: % fopen(serobj); % flushes the input buffer rmeddis@28: rmeddis@28: % buttonPressedNo = fscanf(serobj,'%c',1); rmeddis@28: rmeddis@28: % button box remains 'busy' until after the stimulus has been presented rmeddis@28: experiment.buttonBoxStatus='not busy'; rmeddis@28: rmeddis@28: % -------------------------------------------------- pushbuttonGO_Callback rmeddis@28: function pushbuttonGO_Callback(hObject, eventdata, handles) rmeddis@28: % This is a mouse click path rmeddis@28: % GO function is also called directly from button box rmeddis@28: % and from MAP model and stats model rmeddis@28: rmeddis@28: set(handles.pushbuttonGO,'visible','off') rmeddis@28: startNewRun(handles) rmeddis@28: rmeddis@28: % ---------------------------------------------------pushbutton0_Callback rmeddis@28: function pushbutton0_Callback(hObject, eventdata, handles) rmeddis@28: global experiment rmeddis@28: % This is a mouse click path rmeddis@28: rmeddis@28: % ignore 0 button if 2I2AFC used rmeddis@28: if findstr(experiment.threshEstMethod,'2I2AFC'), return, end rmeddis@28: rmeddis@28: % userDoesNotHearTarget(handles) % only possible interpretation rmeddis@28: userDecides(handles, false) rmeddis@28: rmeddis@28: % -------------------------------------------------- pushbutton1_Callback rmeddis@28: function pushbutton1_Callback(hObject, eventdata, handles) rmeddis@28: userSelects0or1(handles) % also called from buttonBox rmeddis@28: rmeddis@28: % ---------------------------------------------------pushbutton2_Callback rmeddis@28: function pushbutton2_Callback(hObject, eventdata, handles) rmeddis@28: userSelects2(handles) % also called from buttonBox rmeddis@28: rmeddis@28: % --------------------------------------------- pushbuttoNotSure_Callback rmeddis@28: function pushbuttoNotSure_Callback(hObject, eventdata, handles) rmeddis@28: userSelectsPleaseRepeat(handles) % also called from buttonBox rmeddis@28: rmeddis@28: % -------------------------------------------------- pushbutton3_Callback rmeddis@28: function pushbutton3_Callback(hObject, eventdata, handles) rmeddis@28: rmeddis@28: % ------------------------------------------------- pushbutton19_Callback rmeddis@28: function pushbutton19_Callback(hObject, eventdata, handles) rmeddis@28: % should be invisible (ignore) rmeddis@28: rmeddis@28: % --------------------------------------- pushbuttonWrongButton_Callback rmeddis@28: function pushbuttonWrongButton_Callback(hObject, eventdata, handles) rmeddis@28: userSelectsWrongButton(handles) rmeddis@28: rmeddis@28: % --------------------------------------- editdigitInput_Callback rmeddis@28: function editdigitInput_Callback(hObject, eventdata, handles) rmeddis@28: userSelects0or1(handles) % after digit string input rmeddis@28: rmeddis@28: rmeddis@28: rmeddis@28: % ----------------------------------------------------- userSelects0or1 rmeddis@28: function userSelects0or1(handles) rmeddis@28: global experiment withinRuns rmeddis@28: rmeddis@28: switch experiment.threshEstMethod rmeddis@28: case {'2I2AFC++', '2I2AFC+++'} rmeddis@28: switch withinRuns.stimulusOrder rmeddis@28: case 'targetFirst'; rmeddis@28: % userHearsTarget(handles) rmeddis@28: userDecides(handles, true) rmeddis@28: otherwise rmeddis@28: % userDoesNotHearTarget(handles) rmeddis@28: userDecides(handles, false) rmeddis@28: end rmeddis@28: otherwise rmeddis@28: % single interval rmeddis@28: % 0 or 1 are treated as equivalent (i.e. target is not heard) rmeddis@28: userDecides(handles, false) rmeddis@28: end rmeddis@28: % return to pushButton1 callback rmeddis@28: rmeddis@28: % ----------------------------------------------------- userSelects2 rmeddis@28: function userSelects2(handles) rmeddis@28: global experiment withinRuns rmeddis@28: switch experiment.threshEstMethod rmeddis@28: case {'2I2AFC++', '2I2AFC+++'} rmeddis@28: switch withinRuns.stimulusOrder rmeddis@28: case 'targetSecond'; rmeddis@28: % userDoesNotHearTarget(handles) rmeddis@28: userDecides(handles, true) rmeddis@28: otherwise rmeddis@28: % userHearsTarget(handles) rmeddis@28: userDecides(handles, false) rmeddis@28: end rmeddis@28: otherwise rmeddis@28: % single interval (2 targets heard) rmeddis@28: userDecides(handles, true) rmeddis@28: end rmeddis@28: % return to pushButton2 callback rmeddis@28: rmeddis@28: % ----------------------------------------------------- ---- userDecides rmeddis@28: function userDecides(handles, saidYes) rmeddis@28: global experiment stimulusParameters betweenRuns withinRuns rmeddis@28: global rareEvent logistic psy levelsBinVector rmeddis@28: rmeddis@28: if experiment.singleShot rmeddis@28: return rmeddis@28: end rmeddis@28: rmeddis@28: % ignore click if not 'waitingForResponse' rmeddis@28: if ~strcmp(experiment.status,'waitingForResponse') rmeddis@28: disp('ignored click') rmeddis@28: return rmeddis@28: end rmeddis@28: rmeddis@28: % speech reception threshold rmeddis@28: if strcmp(stimulusParameters.targetType,'digitStrings') rmeddis@28: digitsInput=get(handles.editdigitInput,'string'); rmeddis@28: % must be three digits rmeddis@28: if ~(length(digitsInput)==3) rmeddis@28: addToMsg(['error message: Wrong no of digits'], 0, 1) rmeddis@28: set(handles.textMSG,'string', 'Wrong no of digits', ... rmeddis@28: 'BackgroundColor','r', 'ForegroundColor', 'w') rmeddis@28: set(handles.editdigitInput,'string','') rmeddis@28: rmeddis@28: return rmeddis@28: end rmeddis@28: % obtain correct answer from file name rmeddis@28: x=stimulusParameters.digitString; rmeddis@28: idx=find(x=='O'); x(idx)='0'; % replace 'oh' with zero rmeddis@28: rmeddis@28: disp([x ' ' digitsInput]) rmeddis@28: rmeddis@28: if x==digitsInput rmeddis@28: saidYes=1; rmeddis@28: else rmeddis@28: saidYes=0; rmeddis@28: end rmeddis@28: set(handles.editdigitInput,'string','') rmeddis@28: set(handles.editdigitInput,'visible','off') rmeddis@28: pause(0.1) rmeddis@28: end rmeddis@28: rmeddis@28: rmeddis@28: rmeddis@28: % no button presses accepted while processing rmeddis@28: experiment.status='processingResponse'; rmeddis@28: rmeddis@28: % catch trials. Restart trial if caught rmeddis@28: if withinRuns.catchTrial rmeddis@28: if saidYes rmeddis@28: disp('catch trial - caught out') rmeddis@28: withinRuns.caughtOut=withinRuns.caughtOut+1; rmeddis@28: rmeddis@28: % special: estimate caught out rate by allowing the trial rmeddis@28: % to continue after catch rmeddis@28: if stimulusParameters.catchTrialBaseRate==0.5 rmeddis@28: % To use this facility, set the catchTrialRate and the rmeddis@28: % catchTrialBaseRate both to 0.5 rmeddis@28: % update false positive rate rmeddis@28: betweenRuns.caughtOut(betweenRuns.runNumber)=... rmeddis@28: withinRuns.caughtOut; rmeddis@28: plotProgressThisTrial(handles) rmeddis@28: nextStimulus(handles); rmeddis@28: return rmeddis@28: end rmeddis@28: rmeddis@28: % Punishment: restart the trial rmeddis@28: set(handles.frame1,'backgroundColor','r') rmeddis@28: set(handles.pushbuttonGO, ... rmeddis@28: 'visible','on', 'backgroundcolor','y') % and go again rmeddis@28: msg=[{'Start again: catch trial error'}, {' '},... rmeddis@28: {'Please,click on the GO button'}]; rmeddis@28: set(handles.textMSG,'string',msg) rmeddis@28: [y,fs]=wavread('ding.wav'); rmeddis@28: wavplay(y/100,fs) rmeddis@28: rmeddis@28: % raise catch trial rate temporarily. rmeddis@28: % this is normally reduced on each new trial (see GO) rmeddis@28: stimulusParameters.catchTrialRate=... rmeddis@28: stimulusParameters.catchTrialRate+0.1; rmeddis@28: if stimulusParameters.catchTrialRate>0.5 rmeddis@28: stimulusParameters.catchTrialRate=0.5; rmeddis@28: end rmeddis@28: fprintf('stimulusParameters.catchTrialRate= %6.3f\n', ... rmeddis@28: stimulusParameters.catchTrialRate) rmeddis@28: rmeddis@28: betweenRuns.caughtOut(betweenRuns.runNumber)=... rmeddis@28: 1+betweenRuns.caughtOut(betweenRuns.runNumber); rmeddis@28: betweenRuns.runNumber=betweenRuns.runNumber-1; rmeddis@28: experiment.status='waitingForGO'; rmeddis@28: return % unwind and wait for button press rmeddis@28: else % (said No) rmeddis@28: % user claims not to have heard target. fortunate as it was not rmeddis@28: % present. So, repeat the stimulus (possibly with target) rmeddis@28: % and behave as if the last trial did not occur rmeddis@28: errormsg=nextStimulus(handles); rmeddis@28: rmeddis@28: % terminate if there is any kind of problem rmeddis@28: if ~isempty(errormsg) rmeddis@28: % e.g. limits exceeded, clipping rmeddis@28: disp(['Error nextStimulus: ' errormsg]) rmeddis@28: runCompleted(handles) rmeddis@28: return rmeddis@28: end rmeddis@28: return % no further action - next trial rmeddis@28: end rmeddis@28: end rmeddis@28: rmeddis@28: % This section analyses the responses, makes tracks and defines next stim. rmeddis@28: rmeddis@28: % Define response and update response list rmeddis@28: if saidYes rmeddis@28: % target was heard, so response=1; rmeddis@28: withinRuns.responseList=[withinRuns.responseList 1]; % 'heard it!' rmeddis@28: else rmeddis@28: % target was not hear heard, so response=0; rmeddis@28: withinRuns.responseList=[withinRuns.responseList 0]; rmeddis@28: end rmeddis@28: withinRuns.levelList=[withinRuns.levelList withinRuns.variableValue]; rmeddis@28: trialNumber=length(withinRuns.responseList); rmeddis@28: rmeddis@28: % keep track of peaks and troughs; rmeddis@28: % identify direction of change during initial period rmeddis@28: if saidYes rmeddis@28: % default step size before first reversal rmeddis@28: WRVinitialStep=-stimulusParameters.WRVinitialStep; rmeddis@28: WRVsmallStep=-stimulusParameters.WRVsmallStep; rmeddis@28: % if the previous direction was 'less difficult', this must be a peak rmeddis@28: if strcmp(withinRuns.direction,'less difficult') ... rmeddis@28: && length(withinRuns.levelList)>1 rmeddis@28: withinRuns.peaks=[withinRuns.peaks withinRuns.variableValue]; rmeddis@28: end rmeddis@28: withinRuns.direction='more difficult'; rmeddis@28: rmeddis@28: else rmeddis@28: % said 'no' rmeddis@28: % default step size before first reversal rmeddis@28: WRVinitialStep=stimulusParameters.WRVinitialStep; rmeddis@28: WRVsmallStep=stimulusParameters.WRVsmallStep; rmeddis@28: rmeddis@28: % if the previous direction was 'up', this must be a peak rmeddis@28: if strcmp(withinRuns.direction,'more difficult') ... rmeddis@28: && length(withinRuns.levelList)>1 rmeddis@28: withinRuns.troughs=[withinRuns.troughs withinRuns.variableValue]; rmeddis@28: end rmeddis@28: withinRuns.direction='less difficult'; rmeddis@28: end rmeddis@28: rmeddis@28: % phase 2 is all the levels after and incuding the first reversal rmeddis@28: % plus the level before that rmeddis@28: if ~withinRuns.nowInPhase2 && length(withinRuns.peaks)+ ... rmeddis@28: length(withinRuns.troughs)>0 rmeddis@28: % if ~withinRuns.nowInPhase2 && (~isempty(withinRuns.peaks) ... rmeddis@28: % || ~isempty(withinRuns.troughs)) rmeddis@28: % define phase 2 rmeddis@28: withinRuns.beginningOfPhase2=trialNumber-1; rmeddis@28: withinRuns.nowInPhase2=1; rmeddis@28: WRVsmallStep=WRVinitialStep/2; rmeddis@28: end rmeddis@28: rmeddis@28: if withinRuns.nowInPhase2 rmeddis@28: % keep a record of all levels and responses in phase 2 only rmeddis@28: withinRuns.levelsPhaseTwo=... rmeddis@28: withinRuns.levelList(withinRuns.beginningOfPhase2:end); rmeddis@28: withinRuns.responsesPhaseTwo=... rmeddis@28: withinRuns.responseList(withinRuns.beginningOfPhase2:end); rmeddis@28: else rmeddis@28: withinRuns.levelsPhaseTwo=[]; rmeddis@28: end rmeddis@28: rmeddis@28: rmeddis@28: % get (or substitute) threshold estimate rmeddis@28: switch experiment.threshEstMethod rmeddis@28: case {'2I2AFC++', '2I2AFC+++'} rmeddis@28: % for plotting psychometric function only rmeddis@28: if withinRuns.beginningOfPhase2>0 rmeddis@28: [psy, levelsBinVector, logistic, rareEvent]= ... rmeddis@28: bestFitPsychometicFunctions... rmeddis@28: (withinRuns.levelsPhaseTwo, withinRuns.responsesPhaseTwo); rmeddis@28: end rmeddis@28: rmeddis@28: if ~isempty(withinRuns.peaks) && ~isempty(withinRuns.troughs) rmeddis@28: thresholdEstimate= ... rmeddis@28: mean([mean(withinRuns.peaks) mean(withinRuns.troughs)]); rmeddis@28: else rmeddis@28: thresholdEstimate=NaN; rmeddis@28: end rmeddis@28: otherwise rmeddis@28: % single interval methods rmeddis@28: try rmeddis@28: % using the s trial after the first reversal rmeddis@28: [psy, levelsBinVector, logistic, rareEvent]= ... rmeddis@28: bestFitPsychometicFunctions(withinRuns.levelsPhaseTwo,... rmeddis@28: withinRuns.responsesPhaseTwo); rmeddis@28: catch rmeddis@28: logistic.bestThreshold=NaN; rmeddis@28: end rmeddis@28: end rmeddis@28: rmeddis@28: if withinRuns.nowInPhase2 rmeddis@28: % save tracks of threshold estimates for plotting andprinting rmeddis@28: switch experiment.functionEstMethod rmeddis@28: case {'logisticLS', 'logisticML'} rmeddis@28: if withinRuns.nowInPhase2 rmeddis@28: withinRuns.meanEstTrack=... rmeddis@28: [withinRuns.meanEstTrack ... rmeddis@28: mean(withinRuns.levelsPhaseTwo)]; rmeddis@28: withinRuns.thresholdEstimateTrack=... rmeddis@28: [withinRuns.thresholdEstimateTrack ... rmeddis@28: logistic.bestThreshold]; rmeddis@28: end rmeddis@28: case 'rareEvent' rmeddis@28: withinRuns.meanEstTrack=... rmeddis@28: [withinRuns.meanEstTrack rareEvent.thresholddB]; rmeddis@28: withinRuns.thresholdEstimateTrack=... rmeddis@28: [withinRuns.thresholdEstimateTrack logistic.bestThreshold]; rmeddis@28: case 'peaksAndTroughs' rmeddis@28: withinRuns.meanEstTrack=... rmeddis@28: [withinRuns.meanEstTrack thresholdEstimate]; rmeddis@28: withinRuns.thresholdEstimateTrack=... rmeddis@28: [withinRuns.thresholdEstimateTrack thresholdEstimate]; rmeddis@28: end rmeddis@28: end rmeddis@28: rmeddis@28: % special discomfort condition rmeddis@28: % run is completed when subject hits '2' button rmeddis@28: switch experiment.paradigm rmeddis@28: case 'discomfort' rmeddis@28: if saidYes rmeddis@28: runCompleted(handles) rmeddis@28: return rmeddis@28: end rmeddis@28: end rmeddis@28: rmeddis@28: % choose the next level for the stimulus rmeddis@28: switch experiment.threshEstMethod rmeddis@28: case {'2I2AFC++', '2I2AFC+++'} rmeddis@28: if saidYes rmeddis@28: [WRVinitialStep, msg]=Levitt2('hit', withinRuns.variableValue); rmeddis@28: else rmeddis@28: [WRVinitialStep, msg]=Levitt2('miss',withinRuns.variableValue); rmeddis@28: end rmeddis@28: rmeddis@28: % empty message means continue as normal rmeddis@28: if ~isempty(msg) rmeddis@28: runCompleted(handles) rmeddis@28: return rmeddis@28: end rmeddis@28: newWRVvalue=withinRuns.variableValue-WRVinitialStep; rmeddis@28: rmeddis@28: case {'MaxLikelihood', 'oneIntervalUpDown'} rmeddis@28: % run completed by virtue of number of trials rmeddis@28: % or restart because listener is in trouble rmeddis@28: if length(withinRuns.levelsPhaseTwo)== experiment.maxTrials rmeddis@28: % Use bonomial test to decide if there is an imbalance in the rmeddis@28: % number of 'yes'es and 'no's rmeddis@28: yesCount=sum(withinRuns.responseList); rmeddis@28: noCount=length(withinRuns.responseList)-yesCount; rmeddis@28: z=abs(yesCount-noCount)/(yesCount+noCount)^0.5; rmeddis@28: if z>1.96 rmeddis@28: betweenRuns.resets=betweenRuns.resets+1; rmeddis@28: disp([ 'reset / z= ' num2str( z) ... rmeddis@28: ' Nresets= ' num2str( betweenRuns.resets) ] ) rmeddis@28: withinRuns.peaks=[]; rmeddis@28: withinRuns.troughs=[]; rmeddis@28: withinRuns.levelList=withinRuns.levelList(end); rmeddis@28: withinRuns.meanEstTrack=withinRuns.meanEstTrack(end); rmeddis@28: withinRuns.forceThreshold=NaN; rmeddis@28: withinRuns.responseList=withinRuns.responseList(end); rmeddis@28: withinRuns.beginningOfPhase2=0; rmeddis@28: withinRuns.nowInPhase2=0; rmeddis@28: withinRuns.thresholdEstimateTrack=... rmeddis@28: withinRuns.thresholdEstimateTrack(end); rmeddis@28: else rmeddis@28: runCompleted(handles) rmeddis@28: return rmeddis@28: end rmeddis@28: end rmeddis@28: rmeddis@28: % set new value for WRV rmeddis@28: if withinRuns.nowInPhase2 rmeddis@28: % phase 2 rmeddis@28: currentMeanEst=withinRuns.thresholdEstimateTrack(end); rmeddis@28: switch experiment.threshEstMethod rmeddis@28: case 'MaxLikelihood' rmeddis@28: newWRVvalue=currentMeanEst; rmeddis@28: case {'oneIntervalUpDown'} rmeddis@28: newWRVvalue=withinRuns.variableValue+WRVsmallStep; rmeddis@28: end rmeddis@28: else rmeddis@28: % phase 1 rmeddis@28: if withinRuns.variableValue+2*WRVinitialStep>... rmeddis@28: stimulusParameters.WRVlimits(2) rmeddis@28: % use smaller steps when close to maximum rmeddis@28: WRVinitialStep=WRVinitialStep/2; rmeddis@28: end rmeddis@28: newWRVvalue=withinRuns.variableValue+WRVinitialStep; rmeddis@28: end rmeddis@28: otherwise rmeddis@28: error( 'assessment method not recognised') rmeddis@28: end rmeddis@28: rmeddis@28: switch experiment.paradigm rmeddis@28: % prevent unrealistic gap durations 'gapDetection' tasks. rmeddis@28: % Note that the gap begins when the ramp ends not when stimulus ends rmeddis@28: case 'gapDetection' rmeddis@28: if newWRVvalue<-2*stimulusParameters.rampDuration rmeddis@28: newWRVvalue=-2*stimulusParameters.rampDuration; rmeddis@28: addToMsg('gap duration fixed at - 2 * ramp!',1, 1) rmeddis@28: end rmeddis@28: end rmeddis@28: rmeddis@28: withinRuns.variableValue=newWRVvalue; rmeddis@28: withinRuns.trialNumber=withinRuns.trialNumber+1; rmeddis@28: rmeddis@28: % Trial continues rmeddis@28: plotProgressThisTrial(handles) rmeddis@28: rmeddis@28: % next stimulus and so the cycle continues rmeddis@28: errormsg=nextStimulus(handles); rmeddis@28: % after the stimulus is presented, control returns here and the system rmeddis@28: % waits for user action. rmeddis@28: rmeddis@28: % terminate if there is any kind of problem rmeddis@28: if ~isempty(errormsg) rmeddis@28: % e.g. limits exceeded, clipping rmeddis@28: disp(['Error nextStimulus: ' errormsg]) rmeddis@28: runCompleted(handles) rmeddis@28: return rmeddis@28: end rmeddis@28: rmeddis@28: % ------------------------------------------------ userSelectsPleaseRepeat rmeddis@28: function userSelectsPleaseRepeat(handles) rmeddis@28: global experiment withinRuns rmeddis@28: % ignore click if not 'waitingForResponse' rmeddis@28: if ~strcmp(experiment.status,'waitingForResponse') rmeddis@28: disp('ignored click') rmeddis@28: return rmeddis@28: end rmeddis@28: % Take no action other than to make a rmeddis@28: % tally of repeat requests rmeddis@28: experiment.pleaseRepeat=experiment.pleaseRepeat+1; rmeddis@28: withinRuns.thisIsRepeatTrial=1; rmeddis@28: nextStimulus(handles); rmeddis@28: rmeddis@28: % ------------------------------------------------ userSelectsWrongButton rmeddis@28: function userSelectsWrongButton(handles) rmeddis@28: global withinRuns experiment rmeddis@28: % restart is the simplest solution for a 'wrong button' request rmeddis@28: withinRuns.wrongButton=withinRuns.wrongButton+1; rmeddis@28: set(handles.pushbuttonGO, 'visible','on', 'backgroundcolor','y') rmeddis@28: msg=[{'Start again: wrong button pressed'}, {' '},... rmeddis@28: {'Please,click on the GO button'}]; rmeddis@28: set(handles.textMSG,'string',msg) rmeddis@28: experiment.status='waitingForGO'; rmeddis@28: rmeddis@28: % ------------------------------------------------- plotProgressThisTrial rmeddis@28: function plotProgressThisTrial(handles) rmeddis@28: rmeddis@28: % used for all responses rmeddis@28: global experiment stimulusParameters betweenRuns withinRuns expGUIhandles rmeddis@28: global psy levelsBinVector binFrequencies rareEvent logistic statsModel rmeddis@28: rmeddis@28: rmeddis@28: % plot the levelTrack and the threshold track rmeddis@28: rmeddis@28: % Panel 2 rmeddis@28: % plot the levelList rmeddis@28: axes(expGUIhandles.axes2); cla rmeddis@28: plot( withinRuns.levelList,'o','markerFaceColor','k'), hold on rmeddis@28: % plot the best threshold estimate tracks rmeddis@28: if length(withinRuns.meanEstTrack)>=1 rmeddis@28: % The length of the levelList is 2 greater than number of thresholds rmeddis@28: ptr=withinRuns.beginningOfPhase2+1; rmeddis@28: plot(ptr: ptr+length(withinRuns.meanEstTrack)-1, ... rmeddis@28: withinRuns.meanEstTrack, 'r') rmeddis@28: plot( ptr: ptr+length(withinRuns.thresholdEstimateTrack)-1, ... rmeddis@28: withinRuns.thresholdEstimateTrack, 'g') rmeddis@28: hold off rmeddis@28: estThresh=withinRuns.thresholdEstimateTrack(end); rmeddis@28: switch experiment.threshEstMethod rmeddis@28: % add appropriate labels to subject GUI buttons rmeddis@28: case {'2I2AFC++', '2I2AFC+++'} rmeddis@28: title([stimulusParameters.WRVname ' = ' ... rmeddis@28: num2str(withinRuns.variableValue, '%5.1f')]) rmeddis@28: otherwise rmeddis@28: title([stimulusParameters.WRVname ' = ' ... rmeddis@28: num2str(withinRuns.variableValue, '%5.1f') ... rmeddis@28: '; TH= ' num2str(estThresh, '%5.1f')]) rmeddis@28: end rmeddis@28: end rmeddis@28: xlim([0 experiment.maxTrials+withinRuns.beginningOfPhase2]); rmeddis@28: ylim(stimulusParameters.WRVlimits) rmeddis@28: grid on rmeddis@28: rmeddis@28: % Panel 4: Summary of threshold estimates (not used here) rmeddis@28: % Earlier estimates are set in 'runCompleted' rmeddis@28: % However, title shows runs/trials remaining rmeddis@28: rmeddis@28: axes(expGUIhandles.axes4) rmeddis@28: runsToGo=length(betweenRuns.var1Sequence)-betweenRuns.runNumber; rmeddis@28: if withinRuns.beginningOfPhase2>0 rmeddis@28: trialsToGo= experiment.singleIntervalMaxTrials(1) ... rmeddis@28: + withinRuns.beginningOfPhase2- withinRuns.trialNumber; rmeddis@28: title(['trials remaining = ' num2str(trialsToGo) ... rmeddis@28: ': runs to go= ' num2str(runsToGo)]) rmeddis@28: end rmeddis@28: rmeddis@28: % plot psychometric function - panel 5 rmeddis@28: axes(expGUIhandles.axes5), cla rmeddis@28: plot(withinRuns.levelList, withinRuns.responseList,'b.'), hold on rmeddis@28: ylim([0 1]) rmeddis@28: title('') rmeddis@28: rmeddis@28: switch experiment.threshEstMethod rmeddis@28: case {'MaxLikelihood', 'oneIntervalUpDown'} rmeddis@28: if withinRuns.beginningOfPhase2>0 rmeddis@28: % display only when in phase 2. rmeddis@28: withinRuns.levelsPhaseTwo=... rmeddis@28: withinRuns.levelList(withinRuns.beginningOfPhase2:end); rmeddis@28: withinRuns.responsesPhaseTwo=... rmeddis@28: withinRuns.responseList(withinRuns.beginningOfPhase2:end); rmeddis@28: rmeddis@28: % organise data as psychometric function rmeddis@28: [psy, levelsBinVector, binFrequencies]= ... rmeddis@28: psychometricFunction(withinRuns.levelsPhaseTwo,... rmeddis@28: withinRuns.responsesPhaseTwo, experiment.psyBinWidth); rmeddis@28: rmeddis@28: % Plot the function rmeddis@28: % point by point with circles of appropiate weighted size rmeddis@28: hold on, rmeddis@28: for i=1:length(psy) rmeddis@28: plot(levelsBinVector(i), psy(i), 'ro', ... rmeddis@28: 'markersize', 50*binFrequencies(i)/sum(binFrequencies)) rmeddis@28: end rmeddis@28: % save info for later rmeddis@28: betweenRuns.psychometicFunction{betweenRuns.runNumber}=... rmeddis@28: [levelsBinVector; psy]; rmeddis@28: rmeddis@28: % fitPsychometric functions is computed in 'userDecides' rmeddis@28: % plot(rareEvent.predictionLevels, rareEvent.predictionsRE,'k') rmeddis@28: plot(logistic.predictionLevels, logistic.predictionsLOG, 'r') rmeddis@28: plot(rareEvent.predictionLevels, rareEvent.predictionsRE, 'k') rmeddis@28: if ~isnan(logistic.bestThreshold ) rmeddis@28: % xlim([ (logistic.bestThreshold -20) ... rmeddis@28: % (logistic.bestThreshold +20) ]) rmeddis@28: xlim([ 0 100 ]) rmeddis@28: % if logistic.bestK< max(experiment.possLogSlopes) rmeddis@28: title(['k= ' num2str(logistic.bestK, '%6.2f') ' g= '... rmeddis@28: num2str(rareEvent.bestGain,'%6.3f') ' A=' ... rmeddis@28: num2str(rareEvent.bestVMin,'%8.1f')]) rmeddis@28: % title('') rmeddis@28: % end rmeddis@28: else rmeddis@28: title(' ') rmeddis@28: end rmeddis@28: rmeddis@28: switch experiment.ear rmeddis@28: %plot green line for statsModel a priori model rmeddis@28: case 'statsModelLogistic' rmeddis@28: % plot proTem logistic (green) used by stats model rmeddis@28: p= 1./(1+exp(-statsModel.logisticSlope... rmeddis@28: *(levelsBinVector-logistic.bestThreshold))); rmeddis@28: if experiment.psyFunSlope<0, p=1-p;end rmeddis@28: titleText=[ ', statsModel: logistic']; rmeddis@28: hold on, plot(levelsBinVector, p,'g') rmeddis@28: case 'statsModelRareEvent' rmeddis@28: pressure=28*10.^(levelsBinVector/20); rmeddis@28: p=1-exp(-stimulusParameters.targetDuration... rmeddis@28: *(statsModel.rareEvenGain... rmeddis@28: * pressure-statsModel.rareEventVmin)); rmeddis@28: p(p<0)=0; rmeddis@28: if experiment.psyFunSlope<0, p=1-p;end rmeddis@28: hold on, plot(levelsBinVector, p,'g') rmeddis@28: end %(estMethod) rmeddis@28: end rmeddis@28: otherwise % 2A2IFC rmeddis@28: rmeddis@28: message3= ... rmeddis@28: ([ 'peaks=' num2str(withinRuns.peaks) ... rmeddis@28: 'troughs=' num2str(withinRuns.troughs)]); rmeddis@28: ylimRM([-0.1 1.1]) % 0=no / 1=yes rmeddis@28: set(gca,'ytick',[0 1], 'yTickLabel', {'no';'yes'}) rmeddis@28: ylabel('psychometric function'), xlabel('target level') rmeddis@28: if length(levelsBinVector)>1 rmeddis@28: xlim([ min(levelsBinVector) max(levelsBinVector)]) rmeddis@28: xlim([ 0 100]) rmeddis@28: end rmeddis@28: end rmeddis@28: rmeddis@28: % command window summary rmeddis@28: % Accumulate things to say in the message window rmeddis@28: message1= (['responses: ' num2str(withinRuns.responseList,'%9.0f')]); rmeddis@28: switch experiment.paradigm rmeddis@28: % more decimal places needed on GUI rmeddis@28: case { 'gapDetection', 'frequencyDiscrimination', 'forwardMaskingD'} rmeddis@28: message2= ([stimulusParameters.WRVname ... rmeddis@28: ': ' num2str(withinRuns.levelList,'%7.3f')]); rmeddis@28: message3= (['Thresh (logistic mean): ' ... rmeddis@28: num2str(withinRuns.thresholdEstimateTrack,'%7.3f')]); rmeddis@28: otherwise rmeddis@28: message2= ([stimulusParameters.WRVname ': ' ... rmeddis@28: num2str(withinRuns.levelList,'%7.1f')]); rmeddis@28: message3= (['Thresh (logistic mean): ' ... rmeddis@28: num2str(withinRuns.thresholdEstimateTrack,'%7.1f')]); rmeddis@28: end rmeddis@28: rmeddis@28: addToMsg(str2mat(message1, message2, message3), 0) rmeddis@28: rmeddis@28: % -----------------------------------------------------runCompleted rmeddis@28: function runCompleted(handles) rmeddis@28: % Used at the end of each run rmeddis@28: global experiment stimulusParameters betweenRuns withinRuns rmeddis@28: global rareEvent expGUIhandles rmeddis@28: % disp('run completed') rmeddis@28: rmeddis@28: experiment.status='runCompleted'; rmeddis@28: rmeddis@28: plotProgressThisTrial(handles) rmeddis@28: rmeddis@28: switch experiment.ear rmeddis@28: case {'statsModelLogistic', 'statsModelRareEvent','MAPmodel', ... rmeddis@28: 'MAPmodelMultiCh','MAPmodelSingleCh', 'MAPmodelListen'} rmeddis@28: % no changes required if model used rmeddis@28: otherwise rmeddis@28: set(handles.frame1,'visible','off') rmeddis@28: set(handles.pushbuttoNotSure,'visible','off') rmeddis@28: set(handles.pushbuttonWrongButton,'visible','off') rmeddis@28: set(handles.pushbutton3,'visible','off') rmeddis@28: set(handles.pushbutton2,'visible','off') rmeddis@28: set(handles.pushbutton1,'visible','off') rmeddis@28: set(handles.pushbutton0,'visible','off') rmeddis@28: set(handles.pushbuttonGO,'visible','off') rmeddis@28: end rmeddis@28: rmeddis@28: if isnan(withinRuns.forceThreshold) rmeddis@28: % the experiment has been aborted for some reason rmeddis@28: threshold=withinRuns.forceThreshold; rmeddis@28: stdev=NaN; rmeddis@28: logistic.bestK=NaN; rmeddis@28: logistic.bestThreshold=NaN; rmeddis@28: medianThreshold=NaN; rmeddis@28: meanThreshold=NaN; rmeddis@28: else rmeddis@28: % use only phase 2 levels and responses for calculating thresholds rmeddis@28: withinRuns.levelsPhaseTwo=... rmeddis@28: withinRuns.levelList(withinRuns.beginningOfPhase2:end); rmeddis@28: withinRuns.responsesPhaseTwo=... rmeddis@28: withinRuns.responseList(withinRuns.beginningOfPhase2:end); rmeddis@28: [psy, levelsPhaseTwoBinVector, logistic, rareEvent]= ... rmeddis@28: bestFitPsychometicFunctions... rmeddis@28: (withinRuns.levelsPhaseTwo, withinRuns.responsesPhaseTwo); rmeddis@28: rmeddis@28: % plot final psychometric function rmeddis@28: axes(expGUIhandles.axes5),cla rmeddis@28: hold on, plot(rareEvent.predictionLevels, rareEvent.predictionsRE, 'k') rmeddis@28: hold on, plot(logistic.predictionLevels, logistic.predictionsLOG, 'r') rmeddis@28: % organise data as psychometric function rmeddis@28: [psy, levelsBinVector, binFrequencies]= ... rmeddis@28: psychometricFunction(withinRuns.levelsPhaseTwo,... rmeddis@28: withinRuns.responsesPhaseTwo, experiment.psyBinWidth); rmeddis@28: % point by point with circles of appropiate weighted size rmeddis@28: hold on, rmeddis@28: for i=1:length(psy) rmeddis@28: plot(levelsBinVector(i), psy(i), 'ro', ... rmeddis@28: 'markersize', 50*binFrequencies(i)/sum(binFrequencies)) rmeddis@28: end rmeddis@28: rmeddis@28: rmeddis@28: % experimental rmeddis@28: medianThreshold=median(withinRuns.levelsPhaseTwo); rmeddis@28: warning off rmeddis@28: meanThreshold=mean(withinRuns.levelsPhaseTwo); rmeddis@28: rmeddis@28: % identify the current threshold estimate rmeddis@28: switch experiment.paradigm rmeddis@28: case 'discomfort' rmeddis@28: % most recent value (not truely a mean value) rmeddis@28: threshold=withinRuns.levelList(end); rmeddis@28: stdev=NaN; rmeddis@28: otherwise rmeddis@28: switch experiment.threshEstMethod rmeddis@28: case {'MaxLikelihood', 'oneIntervalUpDown'} rmeddis@28: % last value in the list rmeddis@28: % threshold=withinRuns.meanEstTrack(end); rmeddis@28: threshold=withinRuns.thresholdEstimateTrack(end); rmeddis@28: stdev=NaN; rmeddis@28: rmeddis@28: case {'2I2AFC++', '2I2AFC+++'} rmeddis@28: % use peaks and troughs rmeddis@28: try % there may not be enough values to use rmeddis@28: peaksUsed=experiment.peaksUsed; rmeddis@28: threshold=... rmeddis@28: mean(... rmeddis@28: [withinRuns.peaks(end-peaksUsed+1:end) ... rmeddis@28: withinRuns.troughs(end-peaksUsed+1:end)]); rmeddis@28: stdev=... rmeddis@28: std([withinRuns.peaks(end-peaksUsed +1:end) ... rmeddis@28: withinRuns.troughs(end-peaksUsed:end)]); rmeddis@28: catch rmeddis@28: threshold=NaN; rmeddis@28: stdev=NaN; rmeddis@28: end rmeddis@28: end rmeddis@28: end rmeddis@28: end rmeddis@28: rmeddis@28: % Store thresholds rmeddis@28: betweenRuns.thresholds=[betweenRuns.thresholds threshold]; rmeddis@28: betweenRuns.thresholds_mean=[betweenRuns.thresholds_mean meanThreshold]; rmeddis@28: betweenRuns.thresholds_median=... rmeddis@28: [betweenRuns.thresholds_median medianThreshold]; rmeddis@28: betweenRuns.forceThresholds=... rmeddis@28: [betweenRuns.forceThresholds withinRuns.forceThreshold]; rmeddis@28: rmeddis@28: % count observations after the startup phase for record keeping rmeddis@28: betweenRuns.observationCount=... rmeddis@28: [betweenRuns.observationCount length(withinRuns.levelList)]; rmeddis@28: betweenRuns.timesOfFirstReversals=... rmeddis@28: [betweenRuns.timesOfFirstReversals withinRuns.beginningOfPhase2]; rmeddis@28: betweenRuns.catchTrials=... rmeddis@28: [betweenRuns.catchTrials withinRuns.catchTrialCount]; rmeddis@28: rmeddis@28: % add variable length tracks to cell arrays rmeddis@28: if withinRuns.beginningOfPhase2>0 rmeddis@28: betweenRuns.bestThresholdTracks{length(betweenRuns.thresholds)}=... rmeddis@28: withinRuns.thresholdEstimateTrack; rmeddis@28: betweenRuns.levelTracks{length(betweenRuns.thresholds)}=... rmeddis@28: withinRuns.levelList(withinRuns.beginningOfPhase2:end); rmeddis@28: betweenRuns.responseTracks{length(betweenRuns.thresholds)}=... rmeddis@28: withinRuns.responseList(withinRuns.beginningOfPhase2:end); rmeddis@28: else rmeddis@28: betweenRuns.bestThresholdTracks{length(betweenRuns.thresholds)}=[]; rmeddis@28: betweenRuns.levelTracks{length(betweenRuns.thresholds)}=[]; rmeddis@28: betweenRuns.responseTracks{length(betweenRuns.thresholds)}=[]; rmeddis@28: end rmeddis@28: rmeddis@28: betweenRuns.bestGain=[betweenRuns.bestGain rareEvent.bestGain]; rmeddis@28: betweenRuns.bestVMin=[betweenRuns.bestVMin rareEvent.bestVMin]; rmeddis@28: betweenRuns.bestPaMin=[betweenRuns.bestPaMin rareEvent.bestPaMindB]; rmeddis@28: betweenRuns.bestLogisticM=... rmeddis@28: [betweenRuns.bestLogisticM logistic.bestThreshold]; rmeddis@28: betweenRuns.bestLogisticK=[betweenRuns.bestLogisticK logistic.bestK]; rmeddis@28: rmeddis@28: resultsSoFar=[betweenRuns.var1Sequence(betweenRuns.runNumber)'... rmeddis@28: betweenRuns.var2Sequence(betweenRuns.runNumber)'... rmeddis@28: betweenRuns.thresholds(betweenRuns.runNumber)' rmeddis@28: ]; rmeddis@28: rmeddis@28: fprintf('%10.3f \t%10.3f \t%10.1f \n', resultsSoFar') rmeddis@28: rmeddis@28: switch experiment.ear rmeddis@28: case {'left', 'right', 'diotic', 'dichoticLeft','dichoticRight'} rmeddis@28: disp(['caught out= ' num2str(betweenRuns.caughtOut)]) rmeddis@28: end rmeddis@28: rmeddis@28: % plot history of thresholds in panel 4 rmeddis@28: axes(expGUIhandles.axes4), cla rmeddis@28: plotColors='rgbmckywrgbmckyw'; rmeddis@28: for i=1:length(betweenRuns.thresholds) rmeddis@28: faceColor=plotColors(floor(i/length(betweenRuns.variableList1)-.01)+1); rmeddis@28: switch betweenRuns.variableName1 rmeddis@28: case {'targetFrequency', 'maskerRelativeFrequency'} rmeddis@28: if min(betweenRuns.var1Sequence)>0 rmeddis@28: % semilogx(betweenRuns.var1Sequence(1:betweenRuns.runNumber), ... rmeddis@28: % betweenRuns.thresholds, 'o', ... rmeddis@28: % 'markerSize', 5,'markerFaceColor',faceColor) rmeddis@28: semilogx(betweenRuns.var1Sequence(i), ... rmeddis@28: betweenRuns.thresholds(i), 'o', ... rmeddis@28: 'markerSize', 5,'markerFaceColor',faceColor) rmeddis@28: else rmeddis@28: plot(betweenRuns.var1Sequence(1:betweenRuns.runNumber), ... rmeddis@28: betweenRuns.thresholds, 'o', ... rmeddis@28: 'markerSize', 5,'markerFaceColor',faceColor) rmeddis@28: plot(betweenRuns.var1Sequence(i), ... rmeddis@28: betweenRuns.thresholds(i), 'o', ... rmeddis@28: 'markerSize', 5,'markerFaceColor',faceColor) rmeddis@28: end rmeddis@28: otherwise rmeddis@28: % plot(betweenRuns.var1Sequence(1:betweenRuns.runNumber), ... rmeddis@28: % betweenRuns.thresholds, 'o', 'markerSize', 5,... rmeddis@28: % 'markerFaceColor',faceColor) rmeddis@28: plot(betweenRuns.var1Sequence(i), ... rmeddis@28: betweenRuns.thresholds(i), 'o', 'markerSize', 5,... rmeddis@28: 'markerFaceColor',faceColor) rmeddis@28: end rmeddis@28: hold on rmeddis@28: end rmeddis@28: xlimRM([ min(betweenRuns.variableList1) max(betweenRuns.variableList1) ]) rmeddis@28: ylim(stimulusParameters.WRVlimits) rmeddis@28: ylabel('thresholds') rmeddis@28: xlabel(betweenRuns.variableName1) rmeddis@28: set(gca,'ytick', [0 20 40 60 80 100]) rmeddis@28: try rmeddis@28: % problems if only one x value rmeddis@28: set(gca,'XTick', sort(betweenRuns.variableList1)) rmeddis@28: catch rmeddis@28: end rmeddis@28: grid on, set(gca,'XMinorGrid', 'off') rmeddis@28: rmeddis@28: % If comparison data is available in pearmeter file, plot it now rmeddis@28: if ~isempty (experiment.comparisonData) rmeddis@28: comparisonData=experiment.comparisonData(:,1:end-1); % ignore final BF rmeddis@28: [x, ncols]=size(comparisonData); rmeddis@28: if length(betweenRuns.variableList1)==ncols rmeddis@28: hold on rmeddis@28: plot (sort(betweenRuns.variableList1), comparisonData, 'r') rmeddis@28: hold off rmeddis@28: end rmeddis@28: end rmeddis@28: rmeddis@28: % End of the Experiment also? rmeddis@28: if betweenRuns.runNumber==length(betweenRuns.var1Sequence) rmeddis@28: % yes, end of experiment rmeddis@28: fileName=['savedData/' experiment.name experiment.date ... rmeddis@28: experiment.paradigm]; rmeddis@28: % save (fileName, 'experiment', 'stimulusParameters', 'betweenRuns', 'withinRuns', 'variableNames', 'paradigmNames', 'LevittControl') rmeddis@28: disp('Experiment completed') rmeddis@28: rmeddis@28: % update subject GUI to acknowledge end of run rmeddis@28: subjGUImsg=[{'Experiment completed'}, {' '}, {'Thank you!'}]; rmeddis@28: set(handles.textMSG,'string', subjGUImsg ) rmeddis@28: % play 'Tada' rmeddis@28: [y,fs,nbits]=wavread('TADA.wav'); rmeddis@28: musicGain=10^(stimulusParameters.musicLeveldB/20); rmeddis@28: y=y*musicGain; rmeddis@28: wavplay(y/100,fs, 'async') rmeddis@28: rmeddis@28: % update experimenter GUI rmeddis@28: addToMsg('Experiment completed.',1) rmeddis@28: rmeddis@28: printReport rmeddis@28: experiment.status='endOfExperiment'; rmeddis@28: return rmeddis@28: else rmeddis@28: % No, hang on. rmeddis@28: switch experiment.ear rmeddis@28: case {'statsModelLogistic', 'statsModelRareEvent','MAPmodel', ... rmeddis@28: 'MAPmodelMultiCh','MAPmodelSingleCh', 'MAPmodelListen'} rmeddis@28: % no changes required if model used rmeddis@28: otherwise rmeddis@28: % decrement catchTrialRate towards baseRate rmeddis@28: stimulusParameters.catchTrialRate=... rmeddis@28: stimulusParameters.catchTrialBaseRate + ... rmeddis@28: (stimulusParameters.catchTrialRate... rmeddis@28: -stimulusParameters.catchTrialBaseRate)... rmeddis@28: *(1-exp(-stimulusParameters.catchTrialTimeConstant)); rmeddis@28: fprintf('stimulusParameters.catchTrialRate= %6.3f\n', ... rmeddis@28: stimulusParameters.catchTrialRate) rmeddis@28: rmeddis@28: % and go again rmeddis@28: set(handles.pushbuttonGO,'backgroundcolor','y') rmeddis@28: set(handles.frame1,'visible','off') rmeddis@28: set(handles.pushbuttonGO,'visible','on') rmeddis@28: msg=[{'Ready to start new trial'}, {' '},... rmeddis@28: {'Please,click on the GO button'}]; rmeddis@28: set(handles.textMSG,'string',msg) rmeddis@28: end rmeddis@28: experiment.status='waitingForGO'; rmeddis@28: % fprintf('\n') rmeddis@28: rmeddis@28: [y,fs,nbits]=wavread('CHIMES.wav'); rmeddis@28: musicGain=10^(stimulusParameters.musicLeveldB/20); rmeddis@28: y=y*musicGain; rmeddis@28: wavplay(y/100,fs,'async') rmeddis@28: end rmeddis@28: rmeddis@28: % -----------------------------------------------------MAPmodelRunsGUI rmeddis@28: % The computer presses the buttons rmeddis@28: function MAPmodelRunsGUI(handles) rmeddis@28: global experiment stimulusParameters method expGUIhandles rmeddis@28: global AN_IHCsynapseParams rmeddis@28: method=[]; rmeddis@28: rmeddis@28: while strcmp(experiment.status,'waitingForGO') rmeddis@28: % no catch trials for MAP model rmeddis@28: experiment.allowCatchTrials=0; rmeddis@28: rmeddis@28: % initiates run and plays first stimulus and it returns rmeddis@28: % without waiting for button press rmeddis@28: startNewRun(handles) rmeddis@28: rmeddis@28: if sum(strcmp(experiment.ear,... rmeddis@28: {'MAPmodelMultiCh', 'MAPmodelListen'})) rmeddis@28: % use BFlist specified in MAPparams file rmeddis@28: BFlist= -1; rmeddis@28: else rmeddis@28: BFlist=stimulusParameters.targetFrequency; rmeddis@28: end rmeddis@28: showParams=0; rmeddis@28: % find model parameters using the 'name' box (e.g. CTa ->MAPparamsCTa) rmeddis@28: paramFunctionName=['method=MAPparams' experiment.name ... rmeddis@28: '(BFlist, stimulusParameters.sampleRate, showParams);']; rmeddis@28: eval(paramFunctionName) % go and fetch the parameters rmeddis@28: rmeddis@28: rmeddis@28: % show sample Rate on GUI; it must be set in MAPparams rmeddis@28: set(expGUIhandles.textsampleRate,'string',... rmeddis@28: num2str(stimulusParameters.sampleRate)) rmeddis@28: rmeddis@28: if experiment.singleShot rmeddis@28: % AN_IHCsynapseParams.showSummaryStatistics=1; rmeddis@28: method.showSummaryStatistics=1; rmeddis@28: AN_IHCsynapseParams.plotSynapseContents=1; rmeddis@28: else rmeddis@28: method.showSummaryStatistics=0; rmeddis@28: AN_IHCsynapseParams.plotSynapseContents=0; rmeddis@28: end rmeddis@28: rmeddis@28: if strcmp(experiment.ear, 'MAPmodelSingleCh') rmeddis@28: method.MGmembranePotentialSave=1; rmeddis@28: end rmeddis@28: rmeddis@28: % continuous loop until the program stops itself rmeddis@28: while strcmp(experiment.status,'waitingForResponse') rmeddis@28: % NB at this point the stimulus has been played rmeddis@28: pause(0.1) % to allow interrupt with CTRL/C rmeddis@28: rmeddis@28: switch experiment.ear rmeddis@28: case { 'MAPmodelListen'} rmeddis@28: set(handles.pushbutton1,'backgroundcolor','y','visible','on') rmeddis@28: set(handles.pushbutton2,'backgroundcolor','y','visible','on') rmeddis@28: end rmeddis@28: rmeddis@28: AN_IHCsynapseParams.mode= 'spikes'; rmeddis@28: rmeddis@28: % Analayse the current stimulus using MAP rmeddis@28: [modelResponse earObject]= MAPmodel( experiment.MAPplot, method); rmeddis@28: rmeddis@28: if experiment.stop || experiment.singleShot rmeddis@28: % trap for single trial or user interrupt using 'stop' button. rmeddis@28: experiment.status= 'waitingForStart'; rmeddis@28: experiment.stop=0; rmeddis@28: addToMsg('manually stopped',1); rmeddis@28: return rmeddis@28: end rmeddis@28: rmeddis@28: switch modelResponse rmeddis@28: case 1 rmeddis@28: % userDoesNotHearTarget(handles) rmeddis@28: switch experiment.ear rmeddis@28: case {'MAPmodelListen'} rmeddis@28: % illuminate appropriate button rmeddis@28: set(handles.pushbutton1,... rmeddis@28: 'backgroundcolor','r','visible','on') rmeddis@28: set(handles.pushbutton2,'backgroundcolor','y') rmeddis@28: end rmeddis@28: userDecides(handles, false) rmeddis@28: if experiment.singleShot, return, end rmeddis@28: rmeddis@28: case 2 rmeddis@28: % userHearsTarget(handles) rmeddis@28: switch experiment.ear rmeddis@28: case {'MAPmodelListen'} rmeddis@28: % illuminate appropriate button (DEMO only) rmeddis@28: set(handles.pushbutton2,'backgroundcolor',... rmeddis@28: 'r','visible','on') rmeddis@28: set(handles.pushbutton1,'backgroundcolor','y') rmeddis@28: end rmeddis@28: rmeddis@28: switch experiment.paradigm rmeddis@28: case 'discomfort' rmeddis@28: % always treat discomfort as 'not heard' rmeddis@28: userDecides(handles, false) rmeddis@28: otherwise rmeddis@28: userDecides(handles, true) rmeddis@28: end rmeddis@28: otherwise rmeddis@28: % probably an abort rmeddis@28: return rmeddis@28: end rmeddis@28: end rmeddis@28: end rmeddis@28: rmeddis@28: function [modelResponse, MacGregorResponse]=MAPmodel( MAPplot, method) rmeddis@28: rmeddis@28: global experiment stimulusParameters audio withinRuns rmeddis@28: global outerMiddleEarParams DRNLParams AN_IHCsynapseParams rmeddis@28: rmeddis@28: savePath=path; rmeddis@28: addpath(['..' filesep 'MAP'], ['..' filesep 'utilities']) rmeddis@28: modelResponse=[]; rmeddis@28: MacGregorResponse=[]; rmeddis@28: rmeddis@28: % mono only (column vector) rmeddis@28: audio=audio(:,1)'; rmeddis@28: rmeddis@28: % if stop button pressed earlier rmeddis@28: if experiment.stop, return, end rmeddis@28: rmeddis@28: % -------------------------------------------------------------- run Model rmeddis@28: MAPparamsName=experiment.name; rmeddis@28: showPlotsAndDetails=experiment.MAPplot; rmeddis@28: AN_spikesOrProbability='spikes'; rmeddis@28: rmeddis@28: % [response, method]=MAPsequenceSeg(audio, method, 1:8); rmeddis@28: global ICoutput ANdt rmeddis@28: MAP1_14(audio, 1/method.dt, method.nonlinCF,... rmeddis@28: MAPparamsName, AN_spikesOrProbability); rmeddis@28: rmeddis@28: if showPlotsAndDetails rmeddis@28: options.printModelParameters=0; rmeddis@28: options.showModelOutput=1; rmeddis@28: options.printFiringRates=1; rmeddis@28: options.showACF=0; rmeddis@28: options.showEfferent=1; rmeddis@28: options.surfProbability=0; rmeddis@28: showMapOptions.surfSpikes=0; rmeddis@28: UTIL_showMAP(options) rmeddis@28: end rmeddis@28: rmeddis@28: % No response, probably caused by hitting 'stop' button rmeddis@28: if isempty(ICoutput), return, end rmeddis@28: rmeddis@28: % MacGregor response is the sum total of all final stage spiking rmeddis@28: MacGregorResponse= sum(ICoutput,1); % use IC rmeddis@28: rmeddis@28: % ---------------------------------------------------------- end model run rmeddis@28: rmeddis@28: dt=ANdt; rmeddis@28: time=dt:dt:dt*length(MacGregorResponse); rmeddis@28: rmeddis@28: % group delay on unit response rmeddis@28: MacGonsetDelay= 0.004; rmeddis@28: MacGoffsetDelay= 0.022; rmeddis@28: rmeddis@28: % now find the response of the MacGregor model during the target presentation + group delay rmeddis@28: switch experiment.threshEstMethod rmeddis@28: case {'2I2AFC++', '2I2AFC+++'} rmeddis@28: idx= time>stimulusParameters.testTargetBegins+MacGonsetDelay ... rmeddis@28: & timestimulusParameters.testNonTargetBegins+MacGonsetDelay ... rmeddis@28: & time0 rmeddis@28: % hit rmeddis@28: nSpikesDuringTarget=experiment.MacGThreshold+1; rmeddis@28: elseif difference<0 rmeddis@28: % miss (wrong choice) rmeddis@28: nSpikesDuringTarget=experiment.MacGThreshold-1; rmeddis@28: else rmeddis@28: if rand>0.5 rmeddis@28: % hit (random choice) rmeddis@28: nSpikesDuringTarget=experiment.MacGThreshold+1; rmeddis@28: else rmeddis@28: % miss (random choice) rmeddis@28: nSpikesDuringTarget=experiment.MacGThreshold-1; rmeddis@28: end rmeddis@28: end rmeddis@28: disp(['level target dummy decision: ' ... rmeddis@28: num2str([withinRuns.variableValue nSpikesTrueWindow ... rmeddis@28: nSpikesFalseWindow nSpikesDuringTarget], '%4.0f') ] ) rmeddis@28: rmeddis@28: otherwise rmeddis@28: % idx=find(time>stimulusParameters.testTargetBegins+MacGonsetDelay ... rmeddis@28: % & timestimulusParameters.testTargetBegins +MacGonsetDelay... rmeddis@28: & timestimulusParameters.testTargetBegins-0.02 ... rmeddis@28: & time0.2 rmeddis@28: modelResponse=2; % gap detected rmeddis@28: else rmeddis@28: modelResponse=1; % gap not detected rmeddis@28: end rmeddis@28: [nSpikesDuringTarget maskerRate gapResponse modelResponse] rmeddis@28: figure(22), plot(timeX,earObject(idx)) rmeddis@28: otherwise rmeddis@28: if nSpikesDuringTarget>experiment.MacGThreshold rmeddis@28: modelResponse=2; % stimulus detected rmeddis@28: else rmeddis@28: modelResponse=1; % nothing heard (default) rmeddis@28: end rmeddis@28: end rmeddis@28: rmeddis@28: rmeddis@28: path(savePath) rmeddis@28: rmeddis@28: % -----------------------------------------------------statsModelRunsGUI rmeddis@28: % The computer presses the buttons rmeddis@28: function statsModelRunsGUI(handles) rmeddis@28: % Decision are made at random using a prescribe statistical function rmeddis@28: % to set probabilities as a function of signal level. rmeddis@28: global experiment rmeddis@28: rmeddis@28: experiment.allowCatchTrials=0; rmeddis@28: rmeddis@28: while strcmp(experiment.status,'waitingForGO') rmeddis@28: % i.e. waiting for new run rmeddis@28: if experiment.stop rmeddis@28: % user has requested an abort rmeddis@28: experiment.status= 'waitingForStart'; rmeddis@28: addToMsg('manually stopped',1) rmeddis@28: return rmeddis@28: end rmeddis@28: rmeddis@28: % initiates run and plays first stimulus and it returns rmeddis@28: % without waiting for button press rmeddis@28: % NB stimulus is not actually generated (for speed) rmeddis@28: startNewRun(handles) rmeddis@28: rmeddis@28: while strcmp(experiment.status,'waitingForResponse') rmeddis@28: % create artificial response here rmeddis@28: modelResponse=statsModelGetResponse; rmeddis@28: switch modelResponse rmeddis@28: case 1 rmeddis@28: % userDoesNotHearTarget(handles) rmeddis@28: userDecides(handles, false) rmeddis@28: case 2 rmeddis@28: % userHearsTarget(handles) rmeddis@28: userDecides(handles, true) rmeddis@28: end rmeddis@28: end rmeddis@28: end rmeddis@28: rmeddis@28: % -----------------------------------------------------statsModelGetResponse rmeddis@28: function modelResponse=statsModelGetResponse(handles) rmeddis@28: global experiment withinRuns statsModel stimulusParameters rmeddis@28: % use the generating function to decide if a detection occurs or not rmeddis@28: rmeddis@28: % pause(0.1) % to allow stopping with CTRL/C but slows things down rmeddis@28: rmeddis@28: % first compute the probability that a detection occurs rmeddis@28: switch experiment.ear rmeddis@28: case {'statsModelLogistic'} rmeddis@28: prob= 1./(1+exp(-statsModel.logisticSlope.*(withinRuns.variableValue-statsModel.logisticMean))); rmeddis@28: % if experiment.psyFunSlope<0, rmeddis@28: % prob=1-prob; rmeddis@28: % end rmeddis@28: rmeddis@28: case 'statsModelRareEvent' rmeddis@28: if experiment.psyFunSlope<0 rmeddis@28: addToMsg('statsModelRareEvent cannot be used with negative slope',0) rmeddis@28: error('statsModelRareEvent cannot be used with negative slope') rmeddis@28: end rmeddis@28: rmeddis@28: % standard formula is prob = 1 – exp(-d (g P – A)) rmeddis@28: % here A->A; To find Pmin use A/gain rmeddis@28: pressure=28*10^(withinRuns.variableValue/20); rmeddis@28: gain=statsModel.rareEvenGain; rmeddis@28: A=statsModel.rareEventVmin; rmeddis@28: d=stimulusParameters.targetDuration; rmeddis@28: gP_Vmin=gain*pressure-A; rmeddis@28: if gP_Vmin>0 rmeddis@28: prob=1-exp(-d*(gP_Vmin)); rmeddis@28: else rmeddis@28: prob=0; rmeddis@28: end rmeddis@28: end rmeddis@28: rmeddis@28: % Use the probability to choose whether or not a detection has occurred rmeddis@28: switch experiment.threshEstMethod rmeddis@28: case {'MaxLikelihood', 'oneIntervalUpDown'} rmeddis@28: if rand1 rmeddis@28: [r c]=size(headers); rmeddis@28: for no=1:r rmeddis@28: fprintf('%s\t',headers(no,:)) rmeddis@28: end rmeddis@28: fprintf('\n') rmeddis@28: end rmeddis@28: rmeddis@28: [r c]=size(M); rmeddis@28: rmeddis@28: for row=1:r rmeddis@28: for col=1:c rmeddis@28: if row==1 && col==1 && M(1,1)==-1000 rmeddis@28: % Print nothing (tab follows below) rmeddis@28: else rmeddis@28: fprintf('%s',num2str(M(row,col))) rmeddis@28: end rmeddis@28: if col