rmeddis@0
|
1 function varargout = subjGUI_MT(varargin)
|
rmeddis@0
|
2
|
rmeddis@0
|
3 % Begin initialization code - DO NOT EDIT
|
rmeddis@0
|
4 gui_Singleton = 1;
|
rmeddis@0
|
5 gui_State = struct('gui_Name', mfilename, ...
|
rmeddis@0
|
6 'gui_Singleton', gui_Singleton, ...
|
rmeddis@0
|
7 'gui_OpeningFcn', @subjGUI_MT_OpeningFcn, ...
|
rmeddis@0
|
8 'gui_OutputFcn', @subjGUI_MT_OutputFcn, ...
|
rmeddis@0
|
9 'gui_LayoutFcn', [] , ...
|
rmeddis@0
|
10 'gui_Callback', []);
|
rmeddis@0
|
11 if nargin && isstr(varargin{1})
|
rmeddis@0
|
12 gui_State.gui_Callback = str2func(varargin{1});
|
rmeddis@0
|
13 end
|
rmeddis@0
|
14
|
rmeddis@0
|
15 if nargout
|
rmeddis@0
|
16 [varargout{1:nargout}] = gui_mainfcn(gui_State, varargin{:});
|
rmeddis@0
|
17 else
|
rmeddis@0
|
18 gui_mainfcn(gui_State, varargin{:});
|
rmeddis@0
|
19 end
|
rmeddis@0
|
20 % End initialization code - DO NOT EDIT
|
rmeddis@0
|
21
|
rmeddis@0
|
22 % --- Executes just before subjGUI_MT is made visible.
|
rmeddis@0
|
23 function subjGUI_MT_OpeningFcn(hObject, eventdata, handles, varargin)
|
rmeddis@0
|
24
|
rmeddis@0
|
25 % Choose default command line output for subjGUI_MT
|
rmeddis@0
|
26 handles.output = hObject;
|
rmeddis@0
|
27 initializeGUI(handles)
|
rmeddis@0
|
28 guidata(hObject, handles);
|
rmeddis@0
|
29
|
rmeddis@0
|
30 function varargout = subjGUI_MT_OutputFcn(hObject, eventdata, handles)
|
rmeddis@0
|
31 % Get default command line output from handles structure
|
rmeddis@0
|
32 varargout{1} = handles.output;
|
rmeddis@0
|
33
|
rmeddis@0
|
34 % -----------------------------------------------------initializeGUI
|
rmeddis@0
|
35 function initializeGUI(handles)
|
rmeddis@0
|
36 global experiment
|
rmeddis@0
|
37 global subjectGUIHandles expGUIhandles
|
rmeddis@28
|
38 addpath (['..' filesep 'MAP'], ['..' filesep 'utilities'], ...
|
rmeddis@0
|
39 ['..' filesep 'parameterStore'], ['..' filesep 'wavFileStore'],...
|
rmeddis@0
|
40 ['..' filesep 'testPrograms'])
|
rmeddis@0
|
41
|
rmeddis@0
|
42 dbstop if error
|
rmeddis@0
|
43
|
rmeddis@0
|
44 % subjectGUI size and location % [left bottom width height]
|
rmeddis@0
|
45 scrnsize=get(0,'screensize');
|
rmeddis@0
|
46 set(0, 'units','pixels')
|
rmeddis@0
|
47 switch experiment.ear
|
rmeddis@0
|
48 % use default size unless...
|
rmeddis@0
|
49 case {'MAPmodel', 'MAPmodelMultich', 'MAPmodelSingleCh', ...
|
rmeddis@0
|
50 'statsModelLogistic','statsModelRareEvent'}
|
rmeddis@0
|
51 % subjectGUI not needed for modelling so minimize subject GUI
|
rmeddis@0
|
52 set(gcf, 'units','pixels')
|
rmeddis@0
|
53 y=[0*scrnsize(3) 0.8*scrnsize(4) 0.1*scrnsize(3) 0.2*scrnsize(4)];
|
rmeddis@0
|
54 set(gcf,'position',y, 'color',[.871 .961 .996])
|
rmeddis@0
|
55
|
rmeddis@0
|
56 case 'MAPmodelListen',
|
rmeddis@0
|
57 % subjectGUI is needed for display purposes. Make it large
|
rmeddis@0
|
58 set(gcf, 'units','pixels')
|
rmeddis@0
|
59 y=[.665*scrnsize(3) 0.02*scrnsize(4) ...
|
rmeddis@0
|
60 0.33*scrnsize(3) 0.5*scrnsize(4)]; % alongside
|
rmeddis@0
|
61 set(gcf,'position',y, 'color',[.871 .961 .996])
|
rmeddis@0
|
62 end
|
rmeddis@0
|
63
|
rmeddis@0
|
64 switch experiment.ear
|
rmeddis@0
|
65 case{'left', 'right','diotic', 'dichoticLeft','dichoticRight'}
|
rmeddis@0
|
66 % Look to see if the button box exists and, if so, initialise it
|
rmeddis@0
|
67 buttonBoxIntitialize % harmless if no button box attached
|
rmeddis@0
|
68 end
|
rmeddis@0
|
69
|
rmeddis@0
|
70 % clear display of previous mean values. This is a new measurement series
|
rmeddis@28
|
71 axes(expGUIhandles.axes4), cla
|
rmeddis@0
|
72 reset (expGUIhandles.axes4)
|
rmeddis@0
|
73
|
rmeddis@0
|
74 % handles needed in non-callback routines below
|
rmeddis@0
|
75 subjectGUIHandles=handles;
|
rmeddis@0
|
76
|
rmeddis@0
|
77 % start immediately
|
rmeddis@0
|
78 startNewExperiment(handles, expGUIhandles)
|
rmeddis@0
|
79 % This is the end of the experiment. Exit here and return to ExpGUI.
|
rmeddis@0
|
80
|
rmeddis@0
|
81 % ----------------------------------------------------- startNewExperiment
|
rmeddis@0
|
82 function startNewExperiment(handles, expGUIhandles)
|
rmeddis@0
|
83 % An experiment consists of a series of 'runs'.
|
rmeddis@0
|
84 % Resets all relevant variables at the beginning of a new experiment.
|
rmeddis@0
|
85 global experiment stimulusParameters betweenRuns
|
rmeddis@0
|
86
|
rmeddis@0
|
87 % 'start new experiment' button is the only valid action now
|
rmeddis@0
|
88 experiment.status='waitingForStart';
|
rmeddis@0
|
89
|
rmeddis@0
|
90 switch experiment.threshEstMethod
|
rmeddis@0
|
91 % add appropriate labels to subject GUI buttons
|
rmeddis@0
|
92 case {'2I2AFC++', '2I2AFC+++'}
|
rmeddis@0
|
93 set(handles.pushbutton3,'string','')
|
rmeddis@0
|
94 set(handles.pushbutton2,'string','2')
|
rmeddis@0
|
95 set(handles.pushbutton1,'string','1')
|
rmeddis@0
|
96 set(handles.pushbutton0,'string','0')
|
rmeddis@0
|
97 case {'MaxLikelihood', 'oneIntervalUpDown'}
|
rmeddis@0
|
98 if stimulusParameters.includeCue
|
rmeddis@0
|
99 set(handles.pushbutton3,'string','')
|
rmeddis@0
|
100 set(handles.pushbutton2,'string','2')
|
rmeddis@0
|
101 set(handles.pushbutton1,'string','1')
|
rmeddis@0
|
102 set(handles.pushbutton0,'string','0')
|
rmeddis@0
|
103 else
|
rmeddis@0
|
104 set(handles.pushbutton3,'string','')
|
rmeddis@0
|
105 set(handles.pushbutton2,'string','YES')
|
rmeddis@0
|
106 set(handles.pushbutton1,'string','NO')
|
rmeddis@0
|
107 set(handles.pushbutton0,'string','')
|
rmeddis@0
|
108 end
|
rmeddis@0
|
109 end
|
rmeddis@0
|
110
|
rmeddis@0
|
111 switch experiment.paradigm
|
rmeddis@0
|
112 case 'discomfort'
|
rmeddis@0
|
113 set(handles.pushbutton3,'string','')
|
rmeddis@0
|
114 set(handles.pushbutton2,'string','uncomfortable')
|
rmeddis@0
|
115 set(handles.pushbutton1,'string','loud')
|
rmeddis@0
|
116 set(handles.pushbutton0,'string','comfortable')
|
rmeddis@28
|
117 experiment.allowCatchTrials=0;
|
rmeddis@0
|
118 end
|
rmeddis@0
|
119
|
rmeddis@0
|
120 % experiment.subjGUIfontSize is set on expGUI
|
rmeddis@0
|
121 set(handles.pushbutton3,'FontSize',experiment.subjGUIfontSize)
|
rmeddis@0
|
122 set(handles.pushbutton2,'FontSize',experiment.subjGUIfontSize)
|
rmeddis@0
|
123 set(handles.pushbutton1,'FontSize',experiment.subjGUIfontSize)
|
rmeddis@0
|
124 set(handles.pushbutton0,'FontSize',experiment.subjGUIfontSize)
|
rmeddis@0
|
125 set(handles.pushbuttoNotSure,'FontSize',experiment.subjGUIfontSize)
|
rmeddis@0
|
126 set(handles.pushbuttonGO,'FontSize',experiment.subjGUIfontSize)
|
rmeddis@0
|
127 set(handles.textMSG,'FontSize',experiment.subjGUIfontSize)
|
rmeddis@0
|
128
|
rmeddis@0
|
129 set(handles.pushbutton19,'visible','off') % unused button
|
rmeddis@0
|
130
|
rmeddis@0
|
131 % start command window summary of progress
|
rmeddis@0
|
132 fprintf(' \n ----------- NEW MEASUREMENTS\n')
|
rmeddis@0
|
133 disp(['paradigm: ' experiment.paradigm])
|
rmeddis@0
|
134 cla(expGUIhandles.axes1)
|
rmeddis@0
|
135 cla(expGUIhandles.axes2)
|
rmeddis@0
|
136 cla(expGUIhandles.axes4)
|
rmeddis@0
|
137 cla(expGUIhandles.axes5)
|
rmeddis@0
|
138
|
rmeddis@0
|
139 experiment.stop=0; % status of 'stop' button
|
rmeddis@0
|
140 experiment.pleaseRepeat=0; % status of 'repeat' button
|
rmeddis@0
|
141 experiment.buttonBoxStatus='not busy';
|
rmeddis@0
|
142
|
rmeddis@0
|
143 % date and time and replace ':' with '_'
|
rmeddis@28
|
144 date=datestr(now);idx=findstr(':',date);date(idx)='_';
|
rmeddis@0
|
145 experiment.date=date;
|
rmeddis@0
|
146 timeNow=clock; betweenRuns.timeNow= timeNow;
|
rmeddis@0
|
147 experiment.timeAtStart=[num2str(timeNow(4)) ':' num2str(timeNow(5))];
|
rmeddis@28
|
148 experiment.minElapsed=0;
|
rmeddis@0
|
149
|
rmeddis@28
|
150 % unpack catch trial rates. The rate declines from the start rate
|
rmeddis@0
|
151 % to the base rate using a time constant.
|
rmeddis@0
|
152 stimulusParameters.catchTrialRate=stimulusParameters.catchTrialRates(1);
|
rmeddis@0
|
153 stimulusParameters.catchTrialBaseRate=...
|
rmeddis@0
|
154 stimulusParameters.catchTrialRates(2);
|
rmeddis@0
|
155 stimulusParameters.catchTrialTimeConstant=...
|
rmeddis@0
|
156 stimulusParameters.catchTrialRates(3);
|
rmeddis@0
|
157 if stimulusParameters.catchTrialBaseRate==0
|
rmeddis@0
|
158 stimulusParameters.catchTrialRate=0;
|
rmeddis@0
|
159 end
|
rmeddis@0
|
160
|
rmeddis@0
|
161 % for human measurements only, identify the start catch trial rate
|
rmeddis@0
|
162 switch experiment.ear
|
rmeddis@0
|
163 case{'left', 'right','diotic', 'dichoticLeft','dichoticRight'}
|
rmeddis@0
|
164 fprintf('stimulusParameters.catchTrialRate= %6.3f\n', ...
|
rmeddis@0
|
165 stimulusParameters.catchTrialRate)
|
rmeddis@0
|
166 end
|
rmeddis@0
|
167
|
rmeddis@0
|
168 % Reset betweenRuns parameters. this occurs only at experiment start
|
rmeddis@0
|
169 % withinRuns values are reset in 'startNewRun'
|
rmeddis@0
|
170 % this approach creates a more readable structure summary printout.
|
rmeddis@0
|
171 betweenRuns.thresholds=[];
|
rmeddis@0
|
172 betweenRuns.thresholds_mean=[];
|
rmeddis@0
|
173 betweenRuns.thresholds_median=[];
|
rmeddis@0
|
174 betweenRuns.forceThresholds=[];
|
rmeddis@0
|
175 betweenRuns.observationCount=[];
|
rmeddis@0
|
176 betweenRuns.catchTrials=[];
|
rmeddis@0
|
177 betweenRuns.timesOfFirstReversals=[];
|
rmeddis@0
|
178 betweenRuns.bestThresholdTracks=[];
|
rmeddis@0
|
179 betweenRuns.levelTracks=[];
|
rmeddis@0
|
180 betweenRuns.responseTracks=[];
|
rmeddis@0
|
181 betweenRuns.slopeKTracks=[];
|
rmeddis@0
|
182 betweenRuns.gainTracks=[];
|
rmeddis@0
|
183 betweenRuns.VminTracks=[];
|
rmeddis@0
|
184 betweenRuns.bestGain=[];
|
rmeddis@0
|
185 betweenRuns.bestVMin=[];
|
rmeddis@0
|
186 betweenRuns.bestPaMin=[];
|
rmeddis@0
|
187 betweenRuns.bestLogisticM=[];
|
rmeddis@0
|
188 betweenRuns.bestLogisticK=[];
|
rmeddis@0
|
189 betweenRuns.resets=0;
|
rmeddis@0
|
190 betweenRuns.runNumber=0;
|
rmeddis@0
|
191
|
rmeddis@0
|
192 % Up to two parameters can be changed between runs
|
rmeddis@0
|
193 % Find the variable parameters and randomize them
|
rmeddis@0
|
194 % e.g. 'variableList1 = stimulusParameters.targetFrequency;'
|
rmeddis@0
|
195 eval(['variableList1=stimulusParameters.' betweenRuns.variableName1 ';']);
|
rmeddis@0
|
196 eval(['variableList2=stimulusParameters.' betweenRuns.variableName2 ';']);
|
rmeddis@0
|
197 nVar1=length(variableList1);
|
rmeddis@0
|
198 nVar2=length(variableList2);
|
rmeddis@0
|
199
|
rmeddis@28
|
200 % Create two sequence vectors to represent the sequence of var1 and var2
|
rmeddis@0
|
201 % values. 'var1' changes most rapidly.
|
rmeddis@0
|
202 switch betweenRuns.randomizeSequence
|
rmeddis@0
|
203 case 'fixed sequence'
|
rmeddis@0
|
204 var1Sequence=repmat(betweenRuns.variableList1, 1,nVar2);
|
rmeddis@0
|
205 var2Sequence=reshape(repmat(betweenRuns.variableList2, ...
|
rmeddis@0
|
206 nVar1,1),1,nVar1*nVar2);
|
rmeddis@0
|
207 case 'randomize within blocks'
|
rmeddis@0
|
208 % the blocks are not randomized
|
rmeddis@0
|
209 var1Sequence=betweenRuns.variableList1;
|
rmeddis@0
|
210 ranNums=rand(1, length(var1Sequence)); [x idx]=sort(ranNums);
|
rmeddis@0
|
211 var1Sequence=var1Sequence(idx);
|
rmeddis@0
|
212 betweenRuns.variableList1=variableList1(idx);
|
rmeddis@0
|
213 var1Sequence=repmat(var1Sequence, 1,nVar2);
|
rmeddis@0
|
214 var2Sequence=reshape(repmat(betweenRuns.variableList2, nVar1,1)...
|
rmeddis@0
|
215 ,1,nVar1*nVar2);
|
rmeddis@0
|
216 case 'randomize across blocks'
|
rmeddis@0
|
217 var1Sequence=repmat(betweenRuns.variableList1, 1,nVar2);
|
rmeddis@0
|
218 var2Sequence=reshape(repmat(betweenRuns.variableList2, nVar1,1),...
|
rmeddis@0
|
219 1,nVar1*nVar2);
|
rmeddis@0
|
220 ranNums=rand(1, nVar1*nVar2);
|
rmeddis@0
|
221 [x idx]=sort(ranNums);
|
rmeddis@0
|
222 var1Sequence=var1Sequence(idx);
|
rmeddis@0
|
223 var2Sequence=var2Sequence(idx);
|
rmeddis@28
|
224 % there should be one start value for every combination
|
rmeddis@28
|
225 % of var1/ var2. In principle this allows these values to be
|
rmeddis@0
|
226 % programmed. Not currently in use.
|
rmeddis@0
|
227 stimulusParameters.WRVstartValues=...
|
rmeddis@0
|
228 stimulusParameters.WRVstartValues(idx);
|
rmeddis@0
|
229 end
|
rmeddis@0
|
230 betweenRuns.var1Sequence=var1Sequence;
|
rmeddis@0
|
231 betweenRuns.var2Sequence=var2Sequence;
|
rmeddis@0
|
232
|
rmeddis@0
|
233 % caught out vector needs to be linked to the length of the whole sequence
|
rmeddis@0
|
234 betweenRuns.caughtOut=zeros(1,length(var1Sequence));
|
rmeddis@0
|
235
|
rmeddis@0
|
236 disp('planned sequence:')
|
rmeddis@0
|
237 if min(var1Sequence)>1
|
rmeddis@0
|
238 % use decidaml places only if necessary
|
rmeddis@28
|
239 disp([betweenRuns.variableName1 ': ' num2str(var1Sequence,'%6.0f') ])
|
rmeddis@0
|
240 else
|
rmeddis@28
|
241 disp([betweenRuns.variableName1 ': ' num2str(var1Sequence,'%8.3f') ])
|
rmeddis@0
|
242 end
|
rmeddis@0
|
243 if min(var1Sequence)>1
|
rmeddis@28
|
244 disp([betweenRuns.variableName2 ': ' num2str(var2Sequence,'%6.0f') ])
|
rmeddis@0
|
245 else
|
rmeddis@28
|
246 disp([betweenRuns.variableName2 ': ' num2str(var2Sequence,'%8.3f') ])
|
rmeddis@0
|
247 end
|
rmeddis@0
|
248
|
rmeddis@0
|
249 fprintf('\nvariable1 \t variable2\t \n')
|
rmeddis@0
|
250 fprintf('%s \t %s\t Threshold \n',betweenRuns.variableName1,...
|
rmeddis@0
|
251 betweenRuns.variableName2)
|
rmeddis@0
|
252
|
rmeddis@0
|
253 % Light up 'GO' on subjGUI and advise.
|
rmeddis@0
|
254 set(handles.editdigitInput,'visible','off')
|
rmeddis@0
|
255 switch experiment.ear
|
rmeddis@0
|
256 case {'statsModelLogistic', 'statsModelRareEvent',...
|
rmeddis@0
|
257 'MAPmodel', 'MAPmodelMultiCh','MAPmodelSingleCh'}
|
rmeddis@0
|
258 % no changes required if model used
|
rmeddis@0
|
259 otherwise
|
rmeddis@0
|
260 set(handles.pushbuttonGO,'backgroundcolor','y')
|
rmeddis@0
|
261 set(handles.pushbuttonGO,'visible','on')
|
rmeddis@0
|
262 set(handles.frame1,'visible','off')
|
rmeddis@0
|
263 set(handles.textMSG,'backgroundcolor', 'w')
|
rmeddis@0
|
264 msg=[{'Ready to start new Experiment'}, {' '}, {'Please, click on the GO button'}];
|
rmeddis@0
|
265 set(handles.textMSG,'string', msg)
|
rmeddis@28
|
266
|
rmeddis@0
|
267 set(handles.pushbuttoNotSure,'visible','off')
|
rmeddis@0
|
268 set(handles.pushbuttonWrongButton,'visible','off')
|
rmeddis@0
|
269 set(handles.pushbutton3,'visible','off')
|
rmeddis@0
|
270 set(handles.pushbutton2,'visible','off')
|
rmeddis@0
|
271 set(handles.pushbutton1,'visible','off')
|
rmeddis@0
|
272 set(handles.pushbutton0,'visible','off')
|
rmeddis@0
|
273 pause(.1) % to allow display to be drawn
|
rmeddis@0
|
274 end
|
rmeddis@0
|
275
|
rmeddis@0
|
276 % Selecting the 'GO' button is the only valid operation action now
|
rmeddis@0
|
277 experiment.status='waitingForGO'; % i.e. waiting for new run
|
rmeddis@0
|
278
|
rmeddis@0
|
279 % control is now either manual, model (MAP) or randomization
|
rmeddis@0
|
280 switch experiment.ear
|
rmeddis@0
|
281 case {'MAPmodel','MAPmodelMultiCh','MAPmodelSingleCh','MAPmodelListen'} % MAP model is now the subject
|
rmeddis@0
|
282 stimulusParameters.calibrationdB=0; % Pascals required!
|
rmeddis@0
|
283 MAPmodelRunsGUI(handles)
|
rmeddis@0
|
284 % model is now the subject
|
rmeddis@28
|
285 case {'statsModelLogistic', 'statsModelRareEvent'}
|
rmeddis@0
|
286 % no catch trials for the statistical model
|
rmeddis@28
|
287 stimulusParameters.catchTrialBaseRate=0;
|
rmeddis@0
|
288 stimulusParameters.catchTrialRate=0;
|
rmeddis@0
|
289 statsModelRunsGUI(handles)
|
rmeddis@0
|
290 otherwise
|
rmeddis@0
|
291 %manual operation; wait for user to click on 'GO'
|
rmeddis@0
|
292 end
|
rmeddis@0
|
293
|
rmeddis@0
|
294 % Experiment complete (after MAP or randomization)
|
rmeddis@0
|
295 % return to 'initializeGUI' and then back to expGUI
|
rmeddis@0
|
296 % Manual control finds its own way home. Program control assumed when
|
rmeddis@0
|
297 % the user hits the GO button
|
rmeddis@0
|
298
|
rmeddis@0
|
299 % ----------------------------------------------------------------- startNewRun
|
rmeddis@0
|
300 function startNewRun(handles)
|
rmeddis@0
|
301 % There are many ways to arrive here.
|
rmeddis@0
|
302 % Under manual control this is achieved by hitting the GO button
|
rmeddis@0
|
303 % either via the button box or a mouse click
|
rmeddis@0
|
304 % MAP and randomization methods call this too
|
rmeddis@0
|
305
|
rmeddis@0
|
306 global experiment stimulusParameters betweenRuns withinRuns expGUIhandles
|
rmeddis@28
|
307 global LevittControl rareEvent errormsg
|
rmeddis@0
|
308
|
rmeddis@0
|
309 figure(handles.figure1) % guarantee subject GUI visibility
|
rmeddis@0
|
310
|
rmeddis@0
|
311 % ignore call if program is not ready
|
rmeddis@0
|
312 if ~strcmp(experiment.status,'waitingForGO'), return, end
|
rmeddis@0
|
313
|
rmeddis@0
|
314 set(handles.pushbuttonGO,'visible','off')
|
rmeddis@0
|
315
|
rmeddis@0
|
316 % append message to expGUI message box to alert experimenter that the user
|
rmeddis@0
|
317 % is active
|
rmeddis@0
|
318 addToMsg('Starting new trial',0)
|
rmeddis@0
|
319
|
rmeddis@0
|
320 cla(expGUIhandles.axes1), title(''); % stimulus
|
rmeddis@0
|
321 cla(expGUIhandles.axes2), title(''); % WRV track
|
rmeddis@0
|
322 drawnow
|
rmeddis@0
|
323
|
rmeddis@0
|
324 betweenRuns.runNumber=betweenRuns.runNumber + 1;
|
rmeddis@0
|
325
|
rmeddis@0
|
326 withinRuns.trialNumber=1;
|
rmeddis@0
|
327 withinRuns.variableValue=...
|
rmeddis@0
|
328 stimulusParameters.WRVstartValues(betweenRuns.runNumber);
|
rmeddis@0
|
329 % add random jitter to start level
|
rmeddis@0
|
330 if ~experiment.singleShot
|
rmeddis@0
|
331 % SS or single shot allows the user to precisely set the WRV
|
rmeddis@0
|
332 withinRuns.variableValue=withinRuns.variableValue +...
|
rmeddis@0
|
333 (rand-0.5)*stimulusParameters.jitterStartdB;
|
rmeddis@0
|
334 end
|
rmeddis@0
|
335
|
rmeddis@0
|
336 withinRuns.peaks=[];
|
rmeddis@0
|
337 withinRuns.troughs=[];
|
rmeddis@0
|
338 withinRuns.levelList=[];
|
rmeddis@0
|
339 withinRuns.meanEstTrack=[];
|
rmeddis@0
|
340 withinRuns.bestSlopeK=[];
|
rmeddis@0
|
341 withinRuns.bestGain=[];
|
rmeddis@0
|
342 withinRuns.bestVMin=[];
|
rmeddis@0
|
343 withinRuns.forceThreshold=NaN;
|
rmeddis@0
|
344 withinRuns.responseList=[];
|
rmeddis@0
|
345 withinRuns.caughtOut=0;
|
rmeddis@0
|
346 withinRuns.wrongButton=0;
|
rmeddis@0
|
347 withinRuns.catchTrialCount=0;
|
rmeddis@0
|
348 withinRuns.thresholdEstimateTrack=[];
|
rmeddis@0
|
349
|
rmeddis@0
|
350 withinRuns.beginningOfPhase2=0;
|
rmeddis@0
|
351 withinRuns.nowInPhase2=0;
|
rmeddis@0
|
352 withinRuns.thisIsRepeatTrial=0;
|
rmeddis@0
|
353
|
rmeddis@0
|
354 rareEvent.Euclid=NaN;
|
rmeddis@0
|
355 rareEvent.bestGain=NaN;
|
rmeddis@0
|
356 rareEvent.bestVMin=NaN;
|
rmeddis@0
|
357 rareEvent.thresholddB=0;
|
rmeddis@0
|
358 rareEvent.bestPaMindB=NaN;
|
rmeddis@0
|
359 rareEvent.predictionLevels=[];
|
rmeddis@0
|
360 rareEvent.predictionsRE=[];
|
rmeddis@0
|
361
|
rmeddis@0
|
362 LevittControl.sequence=[];
|
rmeddis@0
|
363
|
rmeddis@0
|
364 % on-screen count of number of runs still to complete
|
rmeddis@0
|
365 trialsToGo=length(betweenRuns.var1Sequence)-betweenRuns.runNumber;
|
rmeddis@0
|
366 set(handles.toGoCounter,'string', trialsToGo);
|
rmeddis@0
|
367
|
rmeddis@0
|
368 switch experiment.threshEstMethod
|
rmeddis@0
|
369 case {'2I2AFC++', '2I2AFC+++'}
|
rmeddis@0
|
370 % For 2I2AFC the buttons need to be on the screen ab initio
|
rmeddis@0
|
371 Levitt2 % inititalize Levitt2 procedure
|
rmeddis@0
|
372 end
|
rmeddis@0
|
373
|
rmeddis@0
|
374 switch experiment.ear
|
rmeddis@0
|
375 case{'left', 'right','diotic', 'dichoticLeft','dichoticRight'}
|
rmeddis@0
|
376 % allow subject time to recover from 'go' press
|
rmeddis@0
|
377 pause(experiment.clickToStimulusPause)
|
rmeddis@0
|
378 end
|
rmeddis@0
|
379
|
rmeddis@0
|
380 errormsg=nextStimulus(handles); % get the show on the road
|
rmeddis@0
|
381
|
rmeddis@0
|
382 % terminate if there is any kind of problem
|
rmeddis@0
|
383 if ~isempty(errormsg)
|
rmeddis@0
|
384 % e.g. limits exceeded, clipping
|
rmeddis@0
|
385 disp(errormsg)
|
rmeddis@0
|
386 runCompleted(handles)
|
rmeddis@0
|
387 return
|
rmeddis@0
|
388 end
|
rmeddis@0
|
389 % return route is variable (see intro to this function)
|
rmeddis@0
|
390
|
rmeddis@0
|
391 % -----------------------------------------------------buttonBox_callback
|
rmeddis@0
|
392 function buttonBox_callback(obj, info)
|
rmeddis@28
|
393 % deals with a button press on the button box.
|
rmeddis@28
|
394
|
rmeddis@0
|
395 global experiment
|
rmeddis@0
|
396 global serobj
|
rmeddis@0
|
397 global subjectGUIHandles
|
rmeddis@0
|
398
|
rmeddis@0
|
399 % do not accept callback if one is already in process
|
rmeddis@0
|
400 if strcmp(experiment.buttonBoxStatus,'busy')
|
rmeddis@0
|
401 disp(' ignored button press')
|
rmeddis@28
|
402 return % to quiescent state
|
rmeddis@0
|
403 end
|
rmeddis@0
|
404 experiment.buttonBoxStatus='busy';
|
rmeddis@0
|
405
|
rmeddis@0
|
406 % identify the code of the button pressed
|
rmeddis@0
|
407 buttonPressedNo = fscanf(serobj,'%c',1);
|
rmeddis@0
|
408
|
rmeddis@0
|
409 % This is the map from the button to the Cedrus codes
|
rmeddis@0
|
410 switch experiment.buttonBoxType
|
rmeddis@0
|
411 case 'horizontal'
|
rmeddis@28
|
412 pbGo='7'; pb0='1';
|
rmeddis@28
|
413 pb1='2'; pb2='3';
|
rmeddis@0
|
414 pbRepeat='4'; pbWrong='6'; pbBlank='5';
|
rmeddis@0
|
415 case 'square'
|
rmeddis@28
|
416 pbGo='7'; pb0='1';
|
rmeddis@28
|
417 pb1='3'; pb2='4';
|
rmeddis@0
|
418 pbRepeat='8'; pbWrong='6'; pbBlank='5';
|
rmeddis@0
|
419 end
|
rmeddis@0
|
420
|
rmeddis@0
|
421 % decide what to do
|
rmeddis@0
|
422 switch experiment.status
|
rmeddis@0
|
423 case {'presentingStimulus', 'waitingForStart', 'trialcompleted', ...
|
rmeddis@0
|
424 'endOfExperiment'}
|
rmeddis@0
|
425 disp(' ignored button press')
|
rmeddis@28
|
426
|
rmeddis@0
|
427 case 'waitingForGO'
|
rmeddis@0
|
428 % i.e. waiting for new run
|
rmeddis@0
|
429 if strcmp(buttonPressedNo,pbGo) % only GO button accepted
|
rmeddis@0
|
430 startNewRun(subjectGUIHandles)
|
rmeddis@0
|
431 else
|
rmeddis@0
|
432 disp(' ignored button press')
|
rmeddis@0
|
433 end
|
rmeddis@28
|
434
|
rmeddis@0
|
435 case 'waitingForResponse'
|
rmeddis@0
|
436 % response to stimuli
|
rmeddis@0
|
437 switch buttonPressedNo
|
rmeddis@0
|
438 case pb0 % button 0 (top left)
|
rmeddis@0
|
439 switch experiment.threshEstMethod
|
rmeddis@0
|
440 case {'2I2AFC++', '2I2AFC+++'}
|
rmeddis@0
|
441 disp(' ignored button press')
|
rmeddis@0
|
442 otherwise
|
rmeddis@0
|
443 set(subjectGUIHandles.pushbutton0,...
|
rmeddis@0
|
444 'backgroundcolor','r')
|
rmeddis@0
|
445 pause(.1)
|
rmeddis@0
|
446 set(subjectGUIHandles.pushbutton0,...
|
rmeddis@0
|
447 'backgroundcolor',get(0,...
|
rmeddis@0
|
448 'defaultUicontrolBackgroundColor'))
|
rmeddis@0
|
449 userSelects0or1(subjectGUIHandles)
|
rmeddis@0
|
450 end
|
rmeddis@28
|
451
|
rmeddis@0
|
452 case pb1 % button 1 (bottom left)
|
rmeddis@0
|
453 switch experiment.threshEstMethod
|
rmeddis@0
|
454 case {'2I2AFC++', '2I2AFC+++'}
|
rmeddis@28
|
455 userSelects0or1(subjectGUIHandles)
|
rmeddis@0
|
456 otherwise
|
rmeddis@0
|
457 set(subjectGUIHandles.pushbutton1,...
|
rmeddis@0
|
458 'backgroundcolor','r')
|
rmeddis@0
|
459 pause(.1)
|
rmeddis@0
|
460 set(subjectGUIHandles.pushbutton1,...
|
rmeddis@0
|
461 'backgroundcolor',get(0,...
|
rmeddis@0
|
462 'defaultUicontrolBackgroundColor'))
|
rmeddis@0
|
463 userSelects0or1(subjectGUIHandles)
|
rmeddis@0
|
464 end
|
rmeddis@28
|
465
|
rmeddis@0
|
466 case pb2 % button 2 (bottom right)
|
rmeddis@0
|
467 switch experiment.threshEstMethod
|
rmeddis@0
|
468 case {'2I2AFC++', '2I2AFC+++'}
|
rmeddis@0
|
469 userSelects2 (subjectGUIHandles)
|
rmeddis@0
|
470 otherwise
|
rmeddis@0
|
471 set(subjectGUIHandles.pushbutton2,...
|
rmeddis@0
|
472 'backgroundcolor','r')
|
rmeddis@0
|
473 pause(.1)
|
rmeddis@0
|
474 set(subjectGUIHandles.pushbutton2,...
|
rmeddis@0
|
475 'backgroundcolor',get(0,...
|
rmeddis@0
|
476 'defaultUicontrolBackgroundColor'))
|
rmeddis@0
|
477 userSelects2 (subjectGUIHandles)
|
rmeddis@0
|
478 end
|
rmeddis@28
|
479
|
rmeddis@0
|
480 case pbRepeat % extreme right button
|
rmeddis@0
|
481 switch experiment.threshEstMethod
|
rmeddis@0
|
482 case {'2I2AFC++', '2I2AFC+++'}
|
rmeddis@0
|
483 disp(' ignored button press')
|
rmeddis@0
|
484 otherwise
|
rmeddis@28
|
485
|
rmeddis@0
|
486 set(subjectGUIHandles.pushbuttoNotSure,...
|
rmeddis@0
|
487 'backgroundcolor','r')
|
rmeddis@0
|
488 pause(.1)
|
rmeddis@0
|
489 set(subjectGUIHandles.pushbuttoNotSure,...
|
rmeddis@0
|
490 'backgroundcolor',get(0,...
|
rmeddis@0
|
491 'defaultUicontrolBackgroundColor'))
|
rmeddis@0
|
492 userSelectsPleaseRepeat (subjectGUIHandles)
|
rmeddis@0
|
493 end
|
rmeddis@28
|
494
|
rmeddis@0
|
495 case {pbWrong, pbBlank}
|
rmeddis@0
|
496 disp(' ignored button press')
|
rmeddis@28
|
497
|
rmeddis@0
|
498 otherwise % unrecognised button
|
rmeddis@0
|
499 disp('ignored button press')
|
rmeddis@0
|
500 end % end (button press number)
|
rmeddis@0
|
501 otherwise
|
rmeddis@0
|
502 disp('ignored button press')
|
rmeddis@0
|
503 end % experiment status
|
rmeddis@0
|
504
|
rmeddis@0
|
505 % button box remains 'busy' until after the stimulus has been presented
|
rmeddis@0
|
506 experiment.buttonBoxStatus='not busy';
|
rmeddis@0
|
507
|
rmeddis@0
|
508 % -------------------------------------------------- pushbuttonGO_Callback
|
rmeddis@0
|
509 function pushbuttonGO_Callback(hObject, eventdata, handles)
|
rmeddis@0
|
510 % This is a mouse click path
|
rmeddis@0
|
511 % GO function is also called directly from button box
|
rmeddis@0
|
512 % and from MAP model and stats model
|
rmeddis@0
|
513
|
rmeddis@0
|
514 set(handles.pushbuttonGO,'visible','off')
|
rmeddis@0
|
515 startNewRun(handles)
|
rmeddis@0
|
516
|
rmeddis@0
|
517 % ---------------------------------------------------pushbutton0_Callback
|
rmeddis@0
|
518 function pushbutton0_Callback(hObject, eventdata, handles)
|
rmeddis@0
|
519 global experiment
|
rmeddis@0
|
520 % This is a mouse click path
|
rmeddis@0
|
521
|
rmeddis@0
|
522 % ignore 0 button if 2I2AFC used
|
rmeddis@28
|
523 if findstr(experiment.threshEstMethod,'2I2AFC')
|
rmeddis@28
|
524 return % to quiescent state
|
rmeddis@28
|
525 end
|
rmeddis@0
|
526
|
rmeddis@0
|
527 % userDoesNotHearTarget(handles) % only possible interpretation
|
rmeddis@0
|
528 userDecides(handles, false)
|
rmeddis@0
|
529
|
rmeddis@0
|
530 % -------------------------------------------------- pushbutton1_Callback
|
rmeddis@0
|
531 function pushbutton1_Callback(hObject, eventdata, handles)
|
rmeddis@0
|
532 userSelects0or1(handles) % also called from buttonBox
|
rmeddis@0
|
533
|
rmeddis@0
|
534 % ---------------------------------------------------pushbutton2_Callback
|
rmeddis@0
|
535 function pushbutton2_Callback(hObject, eventdata, handles)
|
rmeddis@0
|
536 userSelects2(handles) % also called from buttonBox
|
rmeddis@0
|
537
|
rmeddis@0
|
538 % --------------------------------------------- pushbuttoNotSure_Callback
|
rmeddis@0
|
539 function pushbuttoNotSure_Callback(hObject, eventdata, handles)
|
rmeddis@0
|
540 userSelectsPleaseRepeat(handles) % also called from buttonBox
|
rmeddis@0
|
541
|
rmeddis@0
|
542 % -------------------------------------------------- pushbutton3_Callback
|
rmeddis@0
|
543 function pushbutton3_Callback(hObject, eventdata, handles)
|
rmeddis@0
|
544
|
rmeddis@0
|
545 % ------------------------------------------------- pushbutton19_Callback
|
rmeddis@0
|
546 function pushbutton19_Callback(hObject, eventdata, handles)
|
rmeddis@0
|
547 % should be invisible (ignore)
|
rmeddis@0
|
548
|
rmeddis@0
|
549 % --------------------------------------- pushbuttonWrongButton_Callback
|
rmeddis@0
|
550 function pushbuttonWrongButton_Callback(hObject, eventdata, handles)
|
rmeddis@0
|
551 userSelectsWrongButton(handles)
|
rmeddis@0
|
552
|
rmeddis@0
|
553 % --------------------------------------- editdigitInput_Callback
|
rmeddis@0
|
554 function editdigitInput_Callback(hObject, eventdata, handles)
|
rmeddis@0
|
555 userSelects0or1(handles) % after digit string input
|
rmeddis@0
|
556
|
rmeddis@0
|
557
|
rmeddis@0
|
558
|
rmeddis@0
|
559 % ----------------------------------------------------- userSelects0or1
|
rmeddis@0
|
560 function userSelects0or1(handles)
|
rmeddis@0
|
561 global experiment withinRuns
|
rmeddis@0
|
562
|
rmeddis@28
|
563 switch experiment.threshEstMethod
|
rmeddis@0
|
564 case {'2I2AFC++', '2I2AFC+++'}
|
rmeddis@0
|
565 switch withinRuns.stimulusOrder
|
rmeddis@0
|
566 case 'targetFirst';
|
rmeddis@0
|
567 % userHearsTarget(handles)
|
rmeddis@0
|
568 userDecides(handles, true)
|
rmeddis@0
|
569 otherwise
|
rmeddis@0
|
570 % userDoesNotHearTarget(handles)
|
rmeddis@0
|
571 userDecides(handles, false)
|
rmeddis@0
|
572 end
|
rmeddis@0
|
573 otherwise
|
rmeddis@0
|
574 % single interval
|
rmeddis@0
|
575 % 0 or 1 are treated as equivalent (i.e. target is not heard)
|
rmeddis@0
|
576 userDecides(handles, false)
|
rmeddis@0
|
577 end
|
rmeddis@0
|
578 % return to pushButton1 callback
|
rmeddis@0
|
579
|
rmeddis@0
|
580 % ----------------------------------------------------- userSelects2
|
rmeddis@0
|
581 function userSelects2(handles)
|
rmeddis@0
|
582 global experiment withinRuns
|
rmeddis@28
|
583 switch experiment.threshEstMethod
|
rmeddis@0
|
584 case {'2I2AFC++', '2I2AFC+++'}
|
rmeddis@0
|
585 switch withinRuns.stimulusOrder
|
rmeddis@0
|
586 case 'targetSecond';
|
rmeddis@0
|
587 % userDoesNotHearTarget(handles)
|
rmeddis@0
|
588 userDecides(handles, true)
|
rmeddis@0
|
589 otherwise
|
rmeddis@0
|
590 % userHearsTarget(handles)
|
rmeddis@0
|
591 userDecides(handles, false)
|
rmeddis@0
|
592 end
|
rmeddis@0
|
593 otherwise
|
rmeddis@0
|
594 % single interval (2 targets heard)
|
rmeddis@0
|
595 userDecides(handles, true)
|
rmeddis@0
|
596 end
|
rmeddis@0
|
597 % return to pushButton2 callback
|
rmeddis@0
|
598
|
rmeddis@0
|
599 % ----------------------------------------------------- ---- userDecides
|
rmeddis@0
|
600 function userDecides(handles, saidYes)
|
rmeddis@0
|
601 global experiment stimulusParameters betweenRuns withinRuns
|
rmeddis@28
|
602 global rareEvent logistic psy levelsBinVector errormsg
|
rmeddis@0
|
603
|
rmeddis@0
|
604 if experiment.singleShot
|
rmeddis@28
|
605 return % not clear why this should be here
|
rmeddis@0
|
606 end
|
rmeddis@0
|
607
|
rmeddis@0
|
608 % ignore click if not 'waitingForResponse'
|
rmeddis@0
|
609 if ~strcmp(experiment.status,'waitingForResponse')
|
rmeddis@0
|
610 disp('ignored click')
|
rmeddis@28
|
611 return % to userSelects
|
rmeddis@0
|
612 end
|
rmeddis@0
|
613
|
rmeddis@0
|
614 % speech reception threshold
|
rmeddis@0
|
615 if strcmp(stimulusParameters.targetType,'digitStrings')
|
rmeddis@28
|
616 % read triple digits from userGUI
|
rmeddis@0
|
617 digitsInput=get(handles.editdigitInput,'string');
|
rmeddis@0
|
618 % must be three digits
|
rmeddis@0
|
619 if ~(length(digitsInput)==3)
|
rmeddis@0
|
620 addToMsg(['error message: Wrong no of digits'], 0, 1)
|
rmeddis@0
|
621 set(handles.textMSG,'string', 'Wrong no of digits', ...
|
rmeddis@0
|
622 'BackgroundColor','r', 'ForegroundColor', 'w')
|
rmeddis@0
|
623 set(handles.editdigitInput,'string','')
|
rmeddis@0
|
624 return
|
rmeddis@0
|
625 end
|
rmeddis@0
|
626 % obtain correct answer from file name
|
rmeddis@0
|
627 x=stimulusParameters.digitString;
|
rmeddis@0
|
628 idx=find(x=='O'); x(idx)='0'; % replace 'oh' with zero
|
rmeddis@0
|
629 disp([x ' ' digitsInput])
|
rmeddis@0
|
630 if x==digitsInput
|
rmeddis@28
|
631 saidYes=1; % i.e. correct response
|
rmeddis@0
|
632 else
|
rmeddis@28
|
633 saidYes=0; % i.e wrong response
|
rmeddis@0
|
634 end
|
rmeddis@28
|
635 set(handles.editdigitInput,'string','')
|
rmeddis@28
|
636 set(handles.editdigitInput,'visible','off')
|
rmeddis@28
|
637 pause(0.1)
|
rmeddis@0
|
638 end
|
rmeddis@0
|
639
|
rmeddis@0
|
640 % no button presses accepted while processing
|
rmeddis@0
|
641 experiment.status='processingResponse';
|
rmeddis@0
|
642
|
rmeddis@0
|
643 % catch trials. Restart trial if caught
|
rmeddis@0
|
644 if withinRuns.catchTrial
|
rmeddis@0
|
645 if saidYes
|
rmeddis@0
|
646 disp('catch trial - caught out')
|
rmeddis@0
|
647 withinRuns.caughtOut=withinRuns.caughtOut+1;
|
rmeddis@28
|
648
|
rmeddis@28
|
649 % special: estimate caught out rate by allowing the trial
|
rmeddis@0
|
650 % to continue after catch
|
rmeddis@0
|
651 if stimulusParameters.catchTrialBaseRate==0.5
|
rmeddis@28
|
652 % To use this facility, set the catchTrialRate and the
|
rmeddis@0
|
653 % catchTrialBaseRate both to 0.5
|
rmeddis@0
|
654 % update false positive rate
|
rmeddis@0
|
655 betweenRuns.caughtOut(betweenRuns.runNumber)=...
|
rmeddis@28
|
656 withinRuns.caughtOut;
|
rmeddis@0
|
657 plotProgressThisTrial(handles)
|
rmeddis@0
|
658 nextStimulus(handles);
|
rmeddis@0
|
659 return
|
rmeddis@0
|
660 end
|
rmeddis@28
|
661
|
rmeddis@28
|
662 % Punishment: caught out restarts the trial
|
rmeddis@0
|
663 set(handles.frame1,'backgroundColor','r')
|
rmeddis@0
|
664 set(handles.pushbuttonGO, ...
|
rmeddis@0
|
665 'visible','on', 'backgroundcolor','y') % and go again
|
rmeddis@0
|
666 msg=[{'Start again: catch trial error'}, {' '},...
|
rmeddis@0
|
667 {'Please,click on the GO button'}];
|
rmeddis@0
|
668 set(handles.textMSG,'string',msg)
|
rmeddis@0
|
669 [y,fs]=wavread('ding.wav');
|
rmeddis@0
|
670 wavplay(y/100,fs)
|
rmeddis@28
|
671
|
rmeddis@28
|
672 % raise catch trial rate temporarily.
|
rmeddis@0
|
673 % this is normally reduced on each new trial (see GO)
|
rmeddis@0
|
674 stimulusParameters.catchTrialRate=...
|
rmeddis@0
|
675 stimulusParameters.catchTrialRate+0.1;
|
rmeddis@28
|
676 if stimulusParameters.catchTrialRate>0.5
|
rmeddis@28
|
677 stimulusParameters.catchTrialRate=0.5;
|
rmeddis@0
|
678 end
|
rmeddis@0
|
679 fprintf('stimulusParameters.catchTrialRate= %6.3f\n', ...
|
rmeddis@0
|
680 stimulusParameters.catchTrialRate)
|
rmeddis@28
|
681
|
rmeddis@0
|
682 betweenRuns.caughtOut(betweenRuns.runNumber)=...
|
rmeddis@0
|
683 1+betweenRuns.caughtOut(betweenRuns.runNumber);
|
rmeddis@0
|
684 betweenRuns.runNumber=betweenRuns.runNumber-1;
|
rmeddis@0
|
685 experiment.status='waitingForGO';
|
rmeddis@0
|
686 return % unwind and wait for button press
|
rmeddis@0
|
687 else % (said No)
|
rmeddis@28
|
688 % user claims not to have heard target.
|
rmeddis@28
|
689 % This is good as it was not present.
|
rmeddis@28
|
690 % So, repeat the stimulus (possibly with target)
|
rmeddis@0
|
691 % and behave as if the last trial did not occur
|
rmeddis@0
|
692 errormsg=nextStimulus(handles);
|
rmeddis@28
|
693
|
rmeddis@0
|
694 % terminate if there is any kind of problem
|
rmeddis@0
|
695 if ~isempty(errormsg)
|
rmeddis@0
|
696 % e.g. limits exceeded, clipping
|
rmeddis@0
|
697 disp(['Error nextStimulus: ' errormsg])
|
rmeddis@0
|
698 runCompleted(handles)
|
rmeddis@0
|
699 return
|
rmeddis@0
|
700 end
|
rmeddis@0
|
701 return % no further action - next trial
|
rmeddis@0
|
702 end
|
rmeddis@28
|
703 end % of catch trial
|
rmeddis@0
|
704
|
rmeddis@28
|
705 % Real target: analyse the response, make tracks and define next stim.
|
rmeddis@0
|
706
|
rmeddis@0
|
707 % Define response and update response list
|
rmeddis@0
|
708 if saidYes
|
rmeddis@0
|
709 % target was heard, so response=1;
|
rmeddis@0
|
710 withinRuns.responseList=[withinRuns.responseList 1]; % 'heard it!'
|
rmeddis@0
|
711 else
|
rmeddis@0
|
712 % target was not hear heard, so response=0;
|
rmeddis@0
|
713 withinRuns.responseList=[withinRuns.responseList 0];
|
rmeddis@0
|
714 end
|
rmeddis@0
|
715 withinRuns.levelList=[withinRuns.levelList withinRuns.variableValue];
|
rmeddis@0
|
716 trialNumber=length(withinRuns.responseList);
|
rmeddis@0
|
717
|
rmeddis@0
|
718 % keep track of peaks and troughs;
|
rmeddis@0
|
719 % identify direction of change during initial period
|
rmeddis@0
|
720 if saidYes
|
rmeddis@0
|
721 % default step size before first reversal
|
rmeddis@28
|
722 WRVinitialStep=-stimulusParameters.WRVinitialStep;
|
rmeddis@0
|
723 WRVsmallStep=-stimulusParameters.WRVsmallStep;
|
rmeddis@0
|
724 % if the previous direction was 'less difficult', this must be a peak
|
rmeddis@0
|
725 if strcmp(withinRuns.direction,'less difficult') ...
|
rmeddis@0
|
726 && length(withinRuns.levelList)>1
|
rmeddis@0
|
727 withinRuns.peaks=[withinRuns.peaks withinRuns.variableValue];
|
rmeddis@0
|
728 end
|
rmeddis@0
|
729 withinRuns.direction='more difficult';
|
rmeddis@0
|
730 else
|
rmeddis@0
|
731 % said 'no'
|
rmeddis@0
|
732 % default step size before first reversal
|
rmeddis@28
|
733 WRVinitialStep=stimulusParameters.WRVinitialStep;
|
rmeddis@0
|
734 WRVsmallStep=stimulusParameters.WRVsmallStep;
|
rmeddis@28
|
735
|
rmeddis@0
|
736 % if the previous direction was 'up', this must be a peak
|
rmeddis@0
|
737 if strcmp(withinRuns.direction,'more difficult') ...
|
rmeddis@0
|
738 && length(withinRuns.levelList)>1
|
rmeddis@0
|
739 withinRuns.troughs=[withinRuns.troughs withinRuns.variableValue];
|
rmeddis@0
|
740 end
|
rmeddis@0
|
741 withinRuns.direction='less difficult';
|
rmeddis@0
|
742 end
|
rmeddis@0
|
743
|
rmeddis@28
|
744 % phase 2 is all the levels after and incuding the first reversal
|
rmeddis@0
|
745 % plus the level before that
|
rmeddis@28
|
746 % Look for the end of phase 1
|
rmeddis@0
|
747 if ~withinRuns.nowInPhase2 && length(withinRuns.peaks)+ ...
|
rmeddis@0
|
748 length(withinRuns.troughs)>0
|
rmeddis@0
|
749 % define phase 2
|
rmeddis@0
|
750 withinRuns.beginningOfPhase2=trialNumber-1;
|
rmeddis@0
|
751 withinRuns.nowInPhase2=1;
|
rmeddis@0
|
752 WRVsmallStep=WRVinitialStep/2;
|
rmeddis@0
|
753 end
|
rmeddis@0
|
754
|
rmeddis@0
|
755 if withinRuns.nowInPhase2
|
rmeddis@0
|
756 % keep a record of all levels and responses in phase 2 only
|
rmeddis@0
|
757 withinRuns.levelsPhaseTwo=...
|
rmeddis@0
|
758 withinRuns.levelList(withinRuns.beginningOfPhase2:end);
|
rmeddis@0
|
759 withinRuns.responsesPhaseTwo=...
|
rmeddis@0
|
760 withinRuns.responseList(withinRuns.beginningOfPhase2:end);
|
rmeddis@0
|
761 else
|
rmeddis@0
|
762 withinRuns.levelsPhaseTwo=[];
|
rmeddis@0
|
763 end
|
rmeddis@0
|
764
|
rmeddis@0
|
765 % get (or substitute) threshold estimate
|
rmeddis@0
|
766 switch experiment.threshEstMethod
|
rmeddis@0
|
767 case {'2I2AFC++', '2I2AFC+++'}
|
rmeddis@0
|
768 % for plotting psychometric function only
|
rmeddis@0
|
769 if withinRuns.beginningOfPhase2>0
|
rmeddis@0
|
770 [psy, levelsBinVector, logistic, rareEvent]= ...
|
rmeddis@0
|
771 bestFitPsychometicFunctions...
|
rmeddis@0
|
772 (withinRuns.levelsPhaseTwo, withinRuns.responsesPhaseTwo);
|
rmeddis@0
|
773 end
|
rmeddis@28
|
774
|
rmeddis@0
|
775 if ~isempty(withinRuns.peaks) && ~isempty(withinRuns.troughs)
|
rmeddis@0
|
776 thresholdEstimate= ...
|
rmeddis@0
|
777 mean([mean(withinRuns.peaks) mean(withinRuns.troughs)]);
|
rmeddis@0
|
778 else
|
rmeddis@0
|
779 thresholdEstimate=NaN;
|
rmeddis@0
|
780 end
|
rmeddis@28
|
781
|
rmeddis@0
|
782 otherwise
|
rmeddis@0
|
783 % single interval methods
|
rmeddis@0
|
784 try
|
rmeddis@0
|
785 % using the s trial after the first reversal
|
rmeddis@0
|
786 [psy, levelsBinVector, logistic, rareEvent]= ...
|
rmeddis@0
|
787 bestFitPsychometicFunctions(withinRuns.levelsPhaseTwo,...
|
rmeddis@0
|
788 withinRuns.responsesPhaseTwo);
|
rmeddis@0
|
789 catch
|
rmeddis@0
|
790 logistic.bestThreshold=NaN;
|
rmeddis@0
|
791 end
|
rmeddis@0
|
792 end
|
rmeddis@0
|
793
|
rmeddis@0
|
794 if withinRuns.nowInPhase2
|
rmeddis@0
|
795 % save tracks of threshold estimates for plotting andprinting
|
rmeddis@0
|
796 switch experiment.functionEstMethod
|
rmeddis@0
|
797 case {'logisticLS', 'logisticML'}
|
rmeddis@0
|
798 if withinRuns.nowInPhase2
|
rmeddis@0
|
799 withinRuns.meanEstTrack=...
|
rmeddis@0
|
800 [withinRuns.meanEstTrack ...
|
rmeddis@0
|
801 mean(withinRuns.levelsPhaseTwo)];
|
rmeddis@0
|
802 withinRuns.thresholdEstimateTrack=...
|
rmeddis@0
|
803 [withinRuns.thresholdEstimateTrack ...
|
rmeddis@0
|
804 logistic.bestThreshold];
|
rmeddis@0
|
805 end
|
rmeddis@0
|
806 case 'rareEvent'
|
rmeddis@0
|
807 withinRuns.meanEstTrack=...
|
rmeddis@0
|
808 [withinRuns.meanEstTrack rareEvent.thresholddB];
|
rmeddis@0
|
809 withinRuns.thresholdEstimateTrack=...
|
rmeddis@0
|
810 [withinRuns.thresholdEstimateTrack logistic.bestThreshold];
|
rmeddis@0
|
811 case 'peaksAndTroughs'
|
rmeddis@0
|
812 withinRuns.meanEstTrack=...
|
rmeddis@0
|
813 [withinRuns.meanEstTrack thresholdEstimate];
|
rmeddis@0
|
814 withinRuns.thresholdEstimateTrack=...
|
rmeddis@0
|
815 [withinRuns.thresholdEstimateTrack thresholdEstimate];
|
rmeddis@0
|
816 end
|
rmeddis@0
|
817 end
|
rmeddis@0
|
818
|
rmeddis@0
|
819 % special discomfort condition
|
rmeddis@0
|
820 % run is completed when subject hits '2' button
|
rmeddis@0
|
821 switch experiment.paradigm
|
rmeddis@0
|
822 case 'discomfort'
|
rmeddis@0
|
823 if saidYes
|
rmeddis@0
|
824 runCompleted(handles)
|
rmeddis@0
|
825 return
|
rmeddis@0
|
826 end
|
rmeddis@0
|
827 end
|
rmeddis@0
|
828
|
rmeddis@0
|
829 % choose the next level for the stimulus
|
rmeddis@0
|
830 switch experiment.threshEstMethod
|
rmeddis@0
|
831 case {'2I2AFC++', '2I2AFC+++'}
|
rmeddis@0
|
832 if saidYes
|
rmeddis@0
|
833 [WRVinitialStep, msg]=Levitt2('hit', withinRuns.variableValue);
|
rmeddis@0
|
834 else
|
rmeddis@0
|
835 [WRVinitialStep, msg]=Levitt2('miss',withinRuns.variableValue);
|
rmeddis@0
|
836 end
|
rmeddis@28
|
837
|
rmeddis@0
|
838 % empty message means continue as normal
|
rmeddis@28
|
839 if ~isempty(msg)
|
rmeddis@0
|
840 runCompleted(handles)
|
rmeddis@0
|
841 return
|
rmeddis@0
|
842 end
|
rmeddis@0
|
843 newWRVvalue=withinRuns.variableValue-WRVinitialStep;
|
rmeddis@28
|
844
|
rmeddis@0
|
845 case {'MaxLikelihood', 'oneIntervalUpDown'}
|
rmeddis@28
|
846 % run completed by virtue of number of trials
|
rmeddis@0
|
847 % or restart because listener is in trouble
|
rmeddis@0
|
848 if length(withinRuns.levelsPhaseTwo)== experiment.maxTrials
|
rmeddis@0
|
849 % Use bonomial test to decide if there is an imbalance in the
|
rmeddis@0
|
850 % number of 'yes'es and 'no's
|
rmeddis@0
|
851 yesCount=sum(withinRuns.responseList);
|
rmeddis@0
|
852 noCount=length(withinRuns.responseList)-yesCount;
|
rmeddis@0
|
853 z=abs(yesCount-noCount)/(yesCount+noCount)^0.5;
|
rmeddis@0
|
854 if z>1.96
|
rmeddis@0
|
855 betweenRuns.resets=betweenRuns.resets+1;
|
rmeddis@0
|
856 disp([ 'reset / z= ' num2str( z) ...
|
rmeddis@0
|
857 ' Nresets= ' num2str( betweenRuns.resets) ] )
|
rmeddis@0
|
858 withinRuns.peaks=[];
|
rmeddis@0
|
859 withinRuns.troughs=[];
|
rmeddis@0
|
860 withinRuns.levelList=withinRuns.levelList(end);
|
rmeddis@0
|
861 withinRuns.meanEstTrack=withinRuns.meanEstTrack(end);
|
rmeddis@0
|
862 withinRuns.forceThreshold=NaN;
|
rmeddis@0
|
863 withinRuns.responseList=withinRuns.responseList(end);
|
rmeddis@0
|
864 withinRuns.beginningOfPhase2=0;
|
rmeddis@0
|
865 withinRuns.nowInPhase2=0;
|
rmeddis@0
|
866 withinRuns.thresholdEstimateTrack=...
|
rmeddis@0
|
867 withinRuns.thresholdEstimateTrack(end);
|
rmeddis@0
|
868 else
|
rmeddis@0
|
869 runCompleted(handles)
|
rmeddis@0
|
870 return
|
rmeddis@0
|
871 end
|
rmeddis@0
|
872 end
|
rmeddis@28
|
873
|
rmeddis@0
|
874 % set new value for WRV
|
rmeddis@0
|
875 if withinRuns.nowInPhase2
|
rmeddis@0
|
876 % phase 2
|
rmeddis@0
|
877 currentMeanEst=withinRuns.thresholdEstimateTrack(end);
|
rmeddis@0
|
878 switch experiment.threshEstMethod
|
rmeddis@0
|
879 case 'MaxLikelihood'
|
rmeddis@28
|
880 newWRVvalue=currentMeanEst;
|
rmeddis@0
|
881 case {'oneIntervalUpDown'}
|
rmeddis@0
|
882 newWRVvalue=withinRuns.variableValue+WRVsmallStep;
|
rmeddis@0
|
883 end
|
rmeddis@0
|
884 else
|
rmeddis@0
|
885 % phase 1
|
rmeddis@0
|
886 if withinRuns.variableValue+2*WRVinitialStep>...
|
rmeddis@0
|
887 stimulusParameters.WRVlimits(2)
|
rmeddis@0
|
888 % use smaller steps when close to maximum
|
rmeddis@0
|
889 WRVinitialStep=WRVinitialStep/2;
|
rmeddis@0
|
890 end
|
rmeddis@0
|
891 newWRVvalue=withinRuns.variableValue+WRVinitialStep;
|
rmeddis@0
|
892 end
|
rmeddis@0
|
893 otherwise
|
rmeddis@0
|
894 error( 'assessment method not recognised')
|
rmeddis@0
|
895 end
|
rmeddis@0
|
896
|
rmeddis@0
|
897 switch experiment.paradigm
|
rmeddis@0
|
898 % prevent unrealistic gap durations 'gapDetection' tasks.
|
rmeddis@0
|
899 % Note that the gap begins when the ramp ends not when stimulus ends
|
rmeddis@0
|
900 case 'gapDetection'
|
rmeddis@0
|
901 if newWRVvalue<-2*stimulusParameters.rampDuration
|
rmeddis@0
|
902 newWRVvalue=-2*stimulusParameters.rampDuration;
|
rmeddis@0
|
903 addToMsg('gap duration fixed at - 2 * ramp!',1, 1)
|
rmeddis@0
|
904 end
|
rmeddis@0
|
905 end
|
rmeddis@0
|
906
|
rmeddis@0
|
907 withinRuns.variableValue=newWRVvalue;
|
rmeddis@0
|
908 withinRuns.trialNumber=withinRuns.trialNumber+1;
|
rmeddis@0
|
909
|
rmeddis@0
|
910 % Trial continues
|
rmeddis@0
|
911 plotProgressThisTrial(handles)
|
rmeddis@0
|
912
|
rmeddis@0
|
913 % next stimulus and so the cycle continues
|
rmeddis@0
|
914 errormsg=nextStimulus(handles);
|
rmeddis@0
|
915 % after the stimulus is presented, control returns here and the system
|
rmeddis@0
|
916 % waits for user action.
|
rmeddis@0
|
917
|
rmeddis@0
|
918 % terminate if there is any kind of problem
|
rmeddis@0
|
919 if ~isempty(errormsg)
|
rmeddis@0
|
920 % e.g. limits exceeded, clipping
|
rmeddis@0
|
921 disp(['Error nextStimulus: ' errormsg])
|
rmeddis@0
|
922 runCompleted(handles)
|
rmeddis@0
|
923 return
|
rmeddis@0
|
924 end
|
rmeddis@0
|
925
|
rmeddis@0
|
926 % ------------------------------------------------ userSelectsPleaseRepeat
|
rmeddis@0
|
927 function userSelectsPleaseRepeat(handles)
|
rmeddis@0
|
928 global experiment withinRuns
|
rmeddis@0
|
929 % ignore click if not 'waitingForResponse'
|
rmeddis@0
|
930 if ~strcmp(experiment.status,'waitingForResponse')
|
rmeddis@0
|
931 disp('ignored click')
|
rmeddis@0
|
932 return
|
rmeddis@0
|
933 end
|
rmeddis@28
|
934 % Take no action other than to make a
|
rmeddis@0
|
935 % tally of repeat requests
|
rmeddis@0
|
936 experiment.pleaseRepeat=experiment.pleaseRepeat+1;
|
rmeddis@0
|
937 withinRuns.thisIsRepeatTrial=1;
|
rmeddis@0
|
938 nextStimulus(handles);
|
rmeddis@0
|
939
|
rmeddis@0
|
940 % ------------------------------------------------ userSelectsWrongButton
|
rmeddis@0
|
941 function userSelectsWrongButton(handles)
|
rmeddis@0
|
942 global withinRuns experiment
|
rmeddis@0
|
943 % restart is the simplest solution for a 'wrong button' request
|
rmeddis@0
|
944 withinRuns.wrongButton=withinRuns.wrongButton+1;
|
rmeddis@28
|
945 set(handles.pushbuttonGO, 'visible','on', 'backgroundcolor','y')
|
rmeddis@0
|
946 msg=[{'Start again: wrong button pressed'}, {' '},...
|
rmeddis@0
|
947 {'Please,click on the GO button'}];
|
rmeddis@0
|
948 set(handles.textMSG,'string',msg)
|
rmeddis@0
|
949 experiment.status='waitingForGO';
|
rmeddis@0
|
950
|
rmeddis@0
|
951 % ------------------------------------------------- plotProgressThisTrial
|
rmeddis@0
|
952 function plotProgressThisTrial(handles)
|
rmeddis@28
|
953 % updates GUI: used for all responses
|
rmeddis@0
|
954
|
rmeddis@0
|
955 global experiment stimulusParameters betweenRuns withinRuns expGUIhandles
|
rmeddis@0
|
956 global psy levelsBinVector binFrequencies rareEvent logistic statsModel
|
rmeddis@0
|
957
|
rmeddis@0
|
958 % plot the levelTrack and the threshold track
|
rmeddis@0
|
959
|
rmeddis@0
|
960 % Panel 2
|
rmeddis@0
|
961 % plot the levelList
|
rmeddis@0
|
962 axes(expGUIhandles.axes2); cla
|
rmeddis@0
|
963 plot( withinRuns.levelList,'o','markerFaceColor','k'), hold on
|
rmeddis@0
|
964 % plot the best threshold estimate tracks
|
rmeddis@0
|
965 if length(withinRuns.meanEstTrack)>=1
|
rmeddis@0
|
966 % The length of the levelList is 2 greater than number of thresholds
|
rmeddis@0
|
967 ptr=withinRuns.beginningOfPhase2+1;
|
rmeddis@0
|
968 plot(ptr: ptr+length(withinRuns.meanEstTrack)-1, ...
|
rmeddis@0
|
969 withinRuns.meanEstTrack, 'r')
|
rmeddis@0
|
970 plot( ptr: ptr+length(withinRuns.thresholdEstimateTrack)-1, ...
|
rmeddis@0
|
971 withinRuns.thresholdEstimateTrack, 'g')
|
rmeddis@0
|
972 hold off
|
rmeddis@0
|
973 estThresh=withinRuns.thresholdEstimateTrack(end);
|
rmeddis@0
|
974 switch experiment.threshEstMethod
|
rmeddis@0
|
975 % add appropriate labels to subject GUI buttons
|
rmeddis@0
|
976 case {'2I2AFC++', '2I2AFC+++'}
|
rmeddis@0
|
977 title([stimulusParameters.WRVname ' = ' ...
|
rmeddis@0
|
978 num2str(withinRuns.variableValue, '%5.1f')])
|
rmeddis@0
|
979 otherwise
|
rmeddis@0
|
980 title([stimulusParameters.WRVname ' = ' ...
|
rmeddis@0
|
981 num2str(withinRuns.variableValue, '%5.1f') ...
|
rmeddis@0
|
982 '; TH= ' num2str(estThresh, '%5.1f')])
|
rmeddis@0
|
983 end
|
rmeddis@0
|
984 end
|
rmeddis@0
|
985 xlim([0 experiment.maxTrials+withinRuns.beginningOfPhase2]);
|
rmeddis@0
|
986 ylim(stimulusParameters.WRVlimits)
|
rmeddis@0
|
987 grid on
|
rmeddis@0
|
988
|
rmeddis@0
|
989 % Panel 4: Summary of threshold estimates (not used here)
|
rmeddis@28
|
990 % Estimates from previous runs are set in 'runCompleted'
|
rmeddis@28
|
991 % It is only necessary to change title showing runs/trials remaining
|
rmeddis@0
|
992 axes(expGUIhandles.axes4)
|
rmeddis@0
|
993 runsToGo=length(betweenRuns.var1Sequence)-betweenRuns.runNumber;
|
rmeddis@0
|
994 if withinRuns.beginningOfPhase2>0
|
rmeddis@0
|
995 trialsToGo= experiment.singleIntervalMaxTrials(1) ...
|
rmeddis@0
|
996 + withinRuns.beginningOfPhase2- withinRuns.trialNumber;
|
rmeddis@0
|
997 title(['trials remaining = ' num2str(trialsToGo) ...
|
rmeddis@0
|
998 ': runs to go= ' num2str(runsToGo)])
|
rmeddis@0
|
999 end
|
rmeddis@0
|
1000
|
rmeddis@0
|
1001 % plot psychometric function - panel 5
|
rmeddis@0
|
1002 axes(expGUIhandles.axes5), cla
|
rmeddis@0
|
1003 plot(withinRuns.levelList, withinRuns.responseList,'b.'), hold on
|
rmeddis@0
|
1004 ylim([0 1])
|
rmeddis@0
|
1005 title('')
|
rmeddis@0
|
1006
|
rmeddis@0
|
1007 switch experiment.threshEstMethod
|
rmeddis@0
|
1008 case {'MaxLikelihood', 'oneIntervalUpDown'}
|
rmeddis@0
|
1009 if withinRuns.beginningOfPhase2>0
|
rmeddis@0
|
1010 % display only when in phase 2.
|
rmeddis@0
|
1011 withinRuns.levelsPhaseTwo=...
|
rmeddis@0
|
1012 withinRuns.levelList(withinRuns.beginningOfPhase2:end);
|
rmeddis@0
|
1013 withinRuns.responsesPhaseTwo=...
|
rmeddis@0
|
1014 withinRuns.responseList(withinRuns.beginningOfPhase2:end);
|
rmeddis@28
|
1015
|
rmeddis@0
|
1016 % organise data as psychometric function
|
rmeddis@0
|
1017 [psy, levelsBinVector, binFrequencies]= ...
|
rmeddis@0
|
1018 psychometricFunction(withinRuns.levelsPhaseTwo,...
|
rmeddis@0
|
1019 withinRuns.responsesPhaseTwo, experiment.psyBinWidth);
|
rmeddis@28
|
1020
|
rmeddis@0
|
1021 % Plot the function
|
rmeddis@0
|
1022 % point by point with circles of appropiate weighted size
|
rmeddis@0
|
1023 hold on,
|
rmeddis@0
|
1024 for i=1:length(psy)
|
rmeddis@0
|
1025 plot(levelsBinVector(i), psy(i), 'ro', ...
|
rmeddis@0
|
1026 'markersize', 50*binFrequencies(i)/sum(binFrequencies))
|
rmeddis@0
|
1027 end
|
rmeddis@0
|
1028 % save info for later
|
rmeddis@0
|
1029 betweenRuns.psychometicFunction{betweenRuns.runNumber}=...
|
rmeddis@0
|
1030 [levelsBinVector; psy];
|
rmeddis@28
|
1031
|
rmeddis@0
|
1032 % fitPsychometric functions is computed in 'userDecides'
|
rmeddis@0
|
1033 % plot(rareEvent.predictionLevels, rareEvent.predictionsRE,'k')
|
rmeddis@0
|
1034 plot(logistic.predictionLevels, logistic.predictionsLOG, 'r')
|
rmeddis@0
|
1035 plot(rareEvent.predictionLevels, rareEvent.predictionsRE, 'k')
|
rmeddis@0
|
1036 if ~isnan(logistic.bestThreshold )
|
rmeddis@0
|
1037 xlim([ 0 100 ])
|
rmeddis@28
|
1038 title(['k= ' num2str(logistic.bestK, '%6.2f') ' g= '...
|
rmeddis@28
|
1039 num2str(rareEvent.bestGain,'%6.3f') ' A=' ...
|
rmeddis@28
|
1040 num2str(rareEvent.bestVMin,'%8.1f')])
|
rmeddis@0
|
1041 else
|
rmeddis@0
|
1042 title(' ')
|
rmeddis@0
|
1043 end
|
rmeddis@28
|
1044
|
rmeddis@0
|
1045 switch experiment.ear
|
rmeddis@0
|
1046 %plot green line for statsModel a priori model
|
rmeddis@0
|
1047 case 'statsModelLogistic'
|
rmeddis@0
|
1048 % plot proTem logistic (green) used by stats model
|
rmeddis@0
|
1049 p= 1./(1+exp(-statsModel.logisticSlope...
|
rmeddis@0
|
1050 *(levelsBinVector-logistic.bestThreshold)));
|
rmeddis@0
|
1051 if experiment.psyFunSlope<0, p=1-p;end
|
rmeddis@0
|
1052 titleText=[ ', statsModel: logistic'];
|
rmeddis@0
|
1053 hold on, plot(levelsBinVector, p,'g')
|
rmeddis@0
|
1054 case 'statsModelRareEvent'
|
rmeddis@0
|
1055 pressure=28*10.^(levelsBinVector/20);
|
rmeddis@0
|
1056 p=1-exp(-stimulusParameters.targetDuration...
|
rmeddis@0
|
1057 *(statsModel.rareEvenGain...
|
rmeddis@0
|
1058 * pressure-statsModel.rareEventVmin));
|
rmeddis@0
|
1059 p(p<0)=0;
|
rmeddis@0
|
1060 if experiment.psyFunSlope<0, p=1-p;end
|
rmeddis@0
|
1061 hold on, plot(levelsBinVector, p,'g')
|
rmeddis@0
|
1062 end %(estMethod)
|
rmeddis@0
|
1063 end
|
rmeddis@28
|
1064
|
rmeddis@0
|
1065 otherwise % 2A2IFC
|
rmeddis@0
|
1066 message3= ...
|
rmeddis@0
|
1067 ([ 'peaks=' num2str(withinRuns.peaks) ...
|
rmeddis@0
|
1068 'troughs=' num2str(withinRuns.troughs)]);
|
rmeddis@0
|
1069 ylimRM([-0.1 1.1]) % 0=no / 1=yes
|
rmeddis@0
|
1070 set(gca,'ytick',[0 1], 'yTickLabel', {'no';'yes'})
|
rmeddis@0
|
1071 ylabel('psychometric function'), xlabel('target level')
|
rmeddis@0
|
1072 if length(levelsBinVector)>1
|
rmeddis@0
|
1073 xlim([ min(levelsBinVector) max(levelsBinVector)])
|
rmeddis@0
|
1074 xlim([ 0 100])
|
rmeddis@0
|
1075 end
|
rmeddis@0
|
1076 end
|
rmeddis@0
|
1077
|
rmeddis@0
|
1078 % command window summary
|
rmeddis@0
|
1079 % Accumulate things to say in the message window
|
rmeddis@0
|
1080 message1= (['responses: ' num2str(withinRuns.responseList,'%9.0f')]);
|
rmeddis@0
|
1081 switch experiment.paradigm
|
rmeddis@0
|
1082 % more decimal places needed on GUI
|
rmeddis@0
|
1083 case { 'gapDetection', 'frequencyDiscrimination', 'forwardMaskingD'}
|
rmeddis@0
|
1084 message2= ([stimulusParameters.WRVname ...
|
rmeddis@0
|
1085 ': ' num2str(withinRuns.levelList,'%7.3f')]);
|
rmeddis@0
|
1086 message3= (['Thresh (logistic mean): ' ...
|
rmeddis@0
|
1087 num2str(withinRuns.thresholdEstimateTrack,'%7.3f')]);
|
rmeddis@0
|
1088 otherwise
|
rmeddis@0
|
1089 message2= ([stimulusParameters.WRVname ': ' ...
|
rmeddis@0
|
1090 num2str(withinRuns.levelList,'%7.1f')]);
|
rmeddis@0
|
1091 message3= (['Thresh (logistic mean): ' ...
|
rmeddis@0
|
1092 num2str(withinRuns.thresholdEstimateTrack,'%7.1f')]);
|
rmeddis@0
|
1093 end
|
rmeddis@0
|
1094
|
rmeddis@0
|
1095 addToMsg(str2mat(message1, message2, message3), 0)
|
rmeddis@0
|
1096
|
rmeddis@0
|
1097 % -----------------------------------------------------runCompleted
|
rmeddis@0
|
1098 function runCompleted(handles)
|
rmeddis@0
|
1099 % Used at the end of each run
|
rmeddis@0
|
1100 global experiment stimulusParameters betweenRuns withinRuns
|
rmeddis@0
|
1101 global rareEvent expGUIhandles
|
rmeddis@0
|
1102 % disp('run completed')
|
rmeddis@0
|
1103
|
rmeddis@0
|
1104 experiment.status='runCompleted';
|
rmeddis@28
|
1105 % quick update after final trial just to make sure
|
rmeddis@0
|
1106 plotProgressThisTrial(handles)
|
rmeddis@0
|
1107
|
rmeddis@0
|
1108 switch experiment.ear
|
rmeddis@0
|
1109 case {'statsModelLogistic', 'statsModelRareEvent','MAPmodel', ...
|
rmeddis@0
|
1110 'MAPmodelMultiCh','MAPmodelSingleCh', 'MAPmodelListen'}
|
rmeddis@0
|
1111 % no changes required if model used
|
rmeddis@0
|
1112 otherwise
|
rmeddis@0
|
1113 set(handles.frame1,'visible','off')
|
rmeddis@0
|
1114 set(handles.pushbuttoNotSure,'visible','off')
|
rmeddis@0
|
1115 set(handles.pushbuttonWrongButton,'visible','off')
|
rmeddis@0
|
1116 set(handles.pushbutton3,'visible','off')
|
rmeddis@0
|
1117 set(handles.pushbutton2,'visible','off')
|
rmeddis@0
|
1118 set(handles.pushbutton1,'visible','off')
|
rmeddis@0
|
1119 set(handles.pushbutton0,'visible','off')
|
rmeddis@0
|
1120 set(handles.pushbuttonGO,'visible','off')
|
rmeddis@0
|
1121 end
|
rmeddis@0
|
1122
|
rmeddis@0
|
1123 if isnan(withinRuns.forceThreshold)
|
rmeddis@0
|
1124 % the experiment has been aborted for some reason
|
rmeddis@0
|
1125 threshold=withinRuns.forceThreshold;
|
rmeddis@0
|
1126 stdev=NaN;
|
rmeddis@0
|
1127 logistic.bestK=NaN;
|
rmeddis@0
|
1128 logistic.bestThreshold=NaN;
|
rmeddis@0
|
1129 medianThreshold=NaN;
|
rmeddis@0
|
1130 meanThreshold=NaN;
|
rmeddis@0
|
1131 else
|
rmeddis@0
|
1132 % use only phase 2 levels and responses for calculating thresholds
|
rmeddis@0
|
1133 withinRuns.levelsPhaseTwo=...
|
rmeddis@0
|
1134 withinRuns.levelList(withinRuns.beginningOfPhase2:end);
|
rmeddis@0
|
1135 withinRuns.responsesPhaseTwo=...
|
rmeddis@0
|
1136 withinRuns.responseList(withinRuns.beginningOfPhase2:end);
|
rmeddis@0
|
1137 [psy, levelsPhaseTwoBinVector, logistic, rareEvent]= ...
|
rmeddis@0
|
1138 bestFitPsychometicFunctions...
|
rmeddis@0
|
1139 (withinRuns.levelsPhaseTwo, withinRuns.responsesPhaseTwo);
|
rmeddis@28
|
1140
|
rmeddis@0
|
1141 % plot final psychometric function
|
rmeddis@0
|
1142 axes(expGUIhandles.axes5),cla
|
rmeddis@0
|
1143 hold on, plot(rareEvent.predictionLevels, rareEvent.predictionsRE, 'k')
|
rmeddis@0
|
1144 hold on, plot(logistic.predictionLevels, logistic.predictionsLOG, 'r')
|
rmeddis@0
|
1145 % organise data as psychometric function
|
rmeddis@0
|
1146 [psy, levelsBinVector, binFrequencies]= ...
|
rmeddis@0
|
1147 psychometricFunction(withinRuns.levelsPhaseTwo,...
|
rmeddis@0
|
1148 withinRuns.responsesPhaseTwo, experiment.psyBinWidth);
|
rmeddis@0
|
1149 % point by point with circles of appropiate weighted size
|
rmeddis@0
|
1150 hold on,
|
rmeddis@0
|
1151 for i=1:length(psy)
|
rmeddis@0
|
1152 plot(levelsBinVector(i), psy(i), 'ro', ...
|
rmeddis@0
|
1153 'markersize', 50*binFrequencies(i)/sum(binFrequencies))
|
rmeddis@0
|
1154 end
|
rmeddis@0
|
1155
|
rmeddis@0
|
1156 % experimental
|
rmeddis@0
|
1157 medianThreshold=median(withinRuns.levelsPhaseTwo);
|
rmeddis@0
|
1158 warning off
|
rmeddis@0
|
1159 meanThreshold=mean(withinRuns.levelsPhaseTwo);
|
rmeddis@28
|
1160
|
rmeddis@0
|
1161 % identify the current threshold estimate
|
rmeddis@0
|
1162 switch experiment.paradigm
|
rmeddis@0
|
1163 case 'discomfort'
|
rmeddis@0
|
1164 % most recent value (not truely a mean value)
|
rmeddis@0
|
1165 threshold=withinRuns.levelList(end);
|
rmeddis@28
|
1166 stdev=NaN;
|
rmeddis@0
|
1167 otherwise
|
rmeddis@0
|
1168 switch experiment.threshEstMethod
|
rmeddis@0
|
1169 case {'MaxLikelihood', 'oneIntervalUpDown'}
|
rmeddis@0
|
1170 % last value in the list
|
rmeddis@28
|
1171 % threshold=withinRuns.meanEstTrack(end);
|
rmeddis@0
|
1172 threshold=withinRuns.thresholdEstimateTrack(end);
|
rmeddis@0
|
1173 stdev=NaN;
|
rmeddis@28
|
1174
|
rmeddis@0
|
1175 case {'2I2AFC++', '2I2AFC+++'}
|
rmeddis@0
|
1176 % use peaks and troughs
|
rmeddis@0
|
1177 try % there may not be enough values to use
|
rmeddis@0
|
1178 peaksUsed=experiment.peaksUsed;
|
rmeddis@0
|
1179 threshold=...
|
rmeddis@0
|
1180 mean(...
|
rmeddis@0
|
1181 [withinRuns.peaks(end-peaksUsed+1:end) ...
|
rmeddis@0
|
1182 withinRuns.troughs(end-peaksUsed+1:end)]);
|
rmeddis@0
|
1183 stdev=...
|
rmeddis@0
|
1184 std([withinRuns.peaks(end-peaksUsed +1:end) ...
|
rmeddis@0
|
1185 withinRuns.troughs(end-peaksUsed:end)]);
|
rmeddis@0
|
1186 catch
|
rmeddis@0
|
1187 threshold=NaN;
|
rmeddis@0
|
1188 stdev=NaN;
|
rmeddis@0
|
1189 end
|
rmeddis@0
|
1190 end
|
rmeddis@0
|
1191 end
|
rmeddis@0
|
1192 end
|
rmeddis@0
|
1193
|
rmeddis@0
|
1194 % Store thresholds
|
rmeddis@0
|
1195 betweenRuns.thresholds=[betweenRuns.thresholds threshold];
|
rmeddis@0
|
1196 betweenRuns.thresholds_mean=[betweenRuns.thresholds_mean meanThreshold];
|
rmeddis@0
|
1197 betweenRuns.thresholds_median=...
|
rmeddis@0
|
1198 [betweenRuns.thresholds_median medianThreshold];
|
rmeddis@0
|
1199 betweenRuns.forceThresholds=...
|
rmeddis@0
|
1200 [betweenRuns.forceThresholds withinRuns.forceThreshold];
|
rmeddis@0
|
1201
|
rmeddis@0
|
1202 % count observations after the startup phase for record keeping
|
rmeddis@0
|
1203 betweenRuns.observationCount=...
|
rmeddis@0
|
1204 [betweenRuns.observationCount length(withinRuns.levelList)];
|
rmeddis@0
|
1205 betweenRuns.timesOfFirstReversals=...
|
rmeddis@0
|
1206 [betweenRuns.timesOfFirstReversals withinRuns.beginningOfPhase2];
|
rmeddis@0
|
1207 betweenRuns.catchTrials=...
|
rmeddis@0
|
1208 [betweenRuns.catchTrials withinRuns.catchTrialCount];
|
rmeddis@0
|
1209
|
rmeddis@0
|
1210 % add variable length tracks to cell arrays
|
rmeddis@0
|
1211 if withinRuns.beginningOfPhase2>0
|
rmeddis@0
|
1212 betweenRuns.bestThresholdTracks{length(betweenRuns.thresholds)}=...
|
rmeddis@28
|
1213 withinRuns.thresholdEstimateTrack;
|
rmeddis@0
|
1214 betweenRuns.levelTracks{length(betweenRuns.thresholds)}=...
|
rmeddis@0
|
1215 withinRuns.levelList(withinRuns.beginningOfPhase2:end);
|
rmeddis@0
|
1216 betweenRuns.responseTracks{length(betweenRuns.thresholds)}=...
|
rmeddis@0
|
1217 withinRuns.responseList(withinRuns.beginningOfPhase2:end);
|
rmeddis@0
|
1218 else
|
rmeddis@0
|
1219 betweenRuns.bestThresholdTracks{length(betweenRuns.thresholds)}=[];
|
rmeddis@0
|
1220 betweenRuns.levelTracks{length(betweenRuns.thresholds)}=[];
|
rmeddis@0
|
1221 betweenRuns.responseTracks{length(betweenRuns.thresholds)}=[];
|
rmeddis@0
|
1222 end
|
rmeddis@0
|
1223
|
rmeddis@0
|
1224 betweenRuns.bestGain=[betweenRuns.bestGain rareEvent.bestGain];
|
rmeddis@0
|
1225 betweenRuns.bestVMin=[betweenRuns.bestVMin rareEvent.bestVMin];
|
rmeddis@0
|
1226 betweenRuns.bestPaMin=[betweenRuns.bestPaMin rareEvent.bestPaMindB];
|
rmeddis@0
|
1227 betweenRuns.bestLogisticM=...
|
rmeddis@0
|
1228 [betweenRuns.bestLogisticM logistic.bestThreshold];
|
rmeddis@0
|
1229 betweenRuns.bestLogisticK=[betweenRuns.bestLogisticK logistic.bestK];
|
rmeddis@0
|
1230
|
rmeddis@0
|
1231 resultsSoFar=[betweenRuns.var1Sequence(betweenRuns.runNumber)'...
|
rmeddis@0
|
1232 betweenRuns.var2Sequence(betweenRuns.runNumber)'...
|
rmeddis@0
|
1233 betweenRuns.thresholds(betweenRuns.runNumber)'
|
rmeddis@0
|
1234 ];
|
rmeddis@0
|
1235
|
rmeddis@0
|
1236 fprintf('%10.3f \t%10.3f \t%10.1f \n', resultsSoFar')
|
rmeddis@0
|
1237
|
rmeddis@0
|
1238 switch experiment.ear
|
rmeddis@0
|
1239 case {'left', 'right', 'diotic', 'dichoticLeft','dichoticRight'}
|
rmeddis@0
|
1240 disp(['caught out= ' num2str(betweenRuns.caughtOut)])
|
rmeddis@0
|
1241 end
|
rmeddis@0
|
1242
|
rmeddis@0
|
1243 % plot history of thresholds in panel 4
|
rmeddis@0
|
1244 axes(expGUIhandles.axes4), cla
|
rmeddis@0
|
1245 plotColors='rgbmckywrgbmckyw';
|
rmeddis@0
|
1246 for i=1:length(betweenRuns.thresholds)
|
rmeddis@0
|
1247 faceColor=plotColors(floor(i/length(betweenRuns.variableList1)-.01)+1);
|
rmeddis@0
|
1248 switch betweenRuns.variableName1
|
rmeddis@0
|
1249 case {'targetFrequency', 'maskerRelativeFrequency'}
|
rmeddis@0
|
1250 if min(betweenRuns.var1Sequence)>0
|
rmeddis@0
|
1251 semilogx(betweenRuns.var1Sequence(i), ...
|
rmeddis@0
|
1252 betweenRuns.thresholds(i), 'o', ...
|
rmeddis@0
|
1253 'markerSize', 5,'markerFaceColor',faceColor)
|
rmeddis@0
|
1254 else
|
rmeddis@0
|
1255 plot(betweenRuns.var1Sequence(1:betweenRuns.runNumber), ...
|
rmeddis@0
|
1256 betweenRuns.thresholds, 'o', ...
|
rmeddis@0
|
1257 'markerSize', 5,'markerFaceColor',faceColor)
|
rmeddis@0
|
1258 plot(betweenRuns.var1Sequence(i), ...
|
rmeddis@0
|
1259 betweenRuns.thresholds(i), 'o', ...
|
rmeddis@0
|
1260 'markerSize', 5,'markerFaceColor',faceColor)
|
rmeddis@0
|
1261 end
|
rmeddis@0
|
1262 otherwise
|
rmeddis@0
|
1263 plot(betweenRuns.var1Sequence(i), ...
|
rmeddis@0
|
1264 betweenRuns.thresholds(i), 'o', 'markerSize', 5,...
|
rmeddis@0
|
1265 'markerFaceColor',faceColor)
|
rmeddis@0
|
1266 end
|
rmeddis@0
|
1267 hold on
|
rmeddis@0
|
1268 end
|
rmeddis@0
|
1269 xlimRM([ min(betweenRuns.variableList1) max(betweenRuns.variableList1) ])
|
rmeddis@0
|
1270 ylim(stimulusParameters.WRVlimits)
|
rmeddis@0
|
1271 ylabel('thresholds')
|
rmeddis@0
|
1272 xlabel(betweenRuns.variableName1)
|
rmeddis@0
|
1273 set(gca,'ytick', [0 20 40 60 80 100])
|
rmeddis@0
|
1274 try
|
rmeddis@0
|
1275 % problems if only one x value
|
rmeddis@0
|
1276 set(gca,'XTick', sort(betweenRuns.variableList1))
|
rmeddis@0
|
1277 catch
|
rmeddis@0
|
1278 end
|
rmeddis@0
|
1279 grid on, set(gca,'XMinorGrid', 'off')
|
rmeddis@0
|
1280
|
rmeddis@28
|
1281 % final run?
|
rmeddis@0
|
1282 if betweenRuns.runNumber==length(betweenRuns.var1Sequence)
|
rmeddis@0
|
1283 % yes, end of experiment
|
rmeddis@0
|
1284 fileName=['savedData/' experiment.name experiment.date ...
|
rmeddis@0
|
1285 experiment.paradigm];
|
rmeddis@0
|
1286 % save (fileName, 'experiment', 'stimulusParameters', 'betweenRuns', 'withinRuns', 'variableNames', 'paradigmNames', 'LevittControl')
|
rmeddis@0
|
1287 disp('Experiment completed')
|
rmeddis@28
|
1288
|
rmeddis@0
|
1289 % update subject GUI to acknowledge end of run
|
rmeddis@0
|
1290 subjGUImsg=[{'Experiment completed'}, {' '}, {'Thank you!'}];
|
rmeddis@0
|
1291 set(handles.textMSG,'string', subjGUImsg )
|
rmeddis@0
|
1292 % play 'Tada'
|
rmeddis@0
|
1293 [y,fs,nbits]=wavread('TADA.wav');
|
rmeddis@0
|
1294 musicGain=10^(stimulusParameters.musicLeveldB/20);
|
rmeddis@0
|
1295 y=y*musicGain;
|
rmeddis@0
|
1296 wavplay(y/100,fs, 'async')
|
rmeddis@28
|
1297
|
rmeddis@0
|
1298 % update experimenter GUI
|
rmeddis@0
|
1299 addToMsg('Experiment completed.',1)
|
rmeddis@28
|
1300
|
rmeddis@0
|
1301 printReport
|
rmeddis@28
|
1302 experiment.status='endOfExperiment';
|
rmeddis@0
|
1303 return
|
rmeddis@0
|
1304 else
|
rmeddis@0
|
1305 % No, hang on.
|
rmeddis@0
|
1306 switch experiment.ear
|
rmeddis@0
|
1307 case {'statsModelLogistic', 'statsModelRareEvent','MAPmodel', ...
|
rmeddis@0
|
1308 'MAPmodelMultiCh','MAPmodelSingleCh', 'MAPmodelListen'}
|
rmeddis@0
|
1309 % no changes required if model used
|
rmeddis@0
|
1310 otherwise
|
rmeddis@0
|
1311 % decrement catchTrialRate towards baseRate
|
rmeddis@0
|
1312 stimulusParameters.catchTrialRate=...
|
rmeddis@0
|
1313 stimulusParameters.catchTrialBaseRate + ...
|
rmeddis@0
|
1314 (stimulusParameters.catchTrialRate...
|
rmeddis@0
|
1315 -stimulusParameters.catchTrialBaseRate)...
|
rmeddis@0
|
1316 *(1-exp(-stimulusParameters.catchTrialTimeConstant));
|
rmeddis@0
|
1317 fprintf('stimulusParameters.catchTrialRate= %6.3f\n', ...
|
rmeddis@0
|
1318 stimulusParameters.catchTrialRate)
|
rmeddis@28
|
1319
|
rmeddis@0
|
1320 % and go again
|
rmeddis@28
|
1321 set(handles.pushbuttonGO,'backgroundcolor','y')
|
rmeddis@0
|
1322 set(handles.frame1,'visible','off')
|
rmeddis@0
|
1323 set(handles.pushbuttonGO,'visible','on')
|
rmeddis@0
|
1324 msg=[{'Ready to start new trial'}, {' '},...
|
rmeddis@0
|
1325 {'Please,click on the GO button'}];
|
rmeddis@0
|
1326 set(handles.textMSG,'string',msg)
|
rmeddis@0
|
1327 end
|
rmeddis@0
|
1328 experiment.status='waitingForGO';
|
rmeddis@0
|
1329 % fprintf('\n')
|
rmeddis@28
|
1330
|
rmeddis@0
|
1331 [y,fs,nbits]=wavread('CHIMES.wav');
|
rmeddis@0
|
1332 musicGain=10^(stimulusParameters.musicLeveldB/20);
|
rmeddis@0
|
1333 y=y*musicGain;
|
rmeddis@0
|
1334 wavplay(y/100,fs,'async')
|
rmeddis@0
|
1335 end
|
rmeddis@0
|
1336
|
rmeddis@0
|
1337 % -----------------------------------------------------MAPmodelRunsGUI
|
rmeddis@0
|
1338 % The computer presses the buttons
|
rmeddis@0
|
1339 function MAPmodelRunsGUI(handles)
|
rmeddis@0
|
1340 global experiment stimulusParameters method expGUIhandles
|
rmeddis@0
|
1341 global AN_IHCsynapseParams
|
rmeddis@0
|
1342 method=[];
|
rmeddis@0
|
1343
|
rmeddis@28
|
1344 while strcmp(experiment.status,'waitingForGO')
|
rmeddis@0
|
1345 % no catch trials for MAP model
|
rmeddis@0
|
1346 experiment.allowCatchTrials=0;
|
rmeddis@28
|
1347
|
rmeddis@28
|
1348 % initiates run and plays first stimulus and it returns
|
rmeddis@0
|
1349 % without waiting for button press
|
rmeddis@28
|
1350 startNewRun(handles)
|
rmeddis@0
|
1351
|
rmeddis@0
|
1352 % show sample Rate on GUI; it must be set in MAPparams
|
rmeddis@0
|
1353 set(expGUIhandles.textsampleRate,'string',...
|
rmeddis@0
|
1354 num2str(stimulusParameters.sampleRate))
|
rmeddis@28
|
1355
|
rmeddis@0
|
1356 if experiment.singleShot
|
rmeddis@0
|
1357 AN_IHCsynapseParams.plotSynapseContents=1;
|
rmeddis@0
|
1358 else
|
rmeddis@0
|
1359 AN_IHCsynapseParams.plotSynapseContents=0;
|
rmeddis@0
|
1360 end
|
rmeddis@28
|
1361
|
rmeddis@0
|
1362 % continuous loop until the program stops itself
|
rmeddis@0
|
1363 while strcmp(experiment.status,'waitingForResponse')
|
rmeddis@0
|
1364 % NB at this point the stimulus has been played
|
rmeddis@0
|
1365 pause(0.1) % to allow interrupt with CTRL/C
|
rmeddis@28
|
1366
|
rmeddis@0
|
1367 switch experiment.ear
|
rmeddis@0
|
1368 case { 'MAPmodelListen'}
|
rmeddis@28
|
1369 % flash the buttons to show model response
|
rmeddis@28
|
1370 set(handles.pushbutton1,'backgroundcolor','y','visible','on')
|
rmeddis@28
|
1371 set(handles.pushbutton2,'backgroundcolor','y','visible','on')
|
rmeddis@0
|
1372 end
|
rmeddis@0
|
1373
|
rmeddis@28
|
1374 % Analayse the current stimulus using MAP
|
rmeddis@28
|
1375 [modelResponse earObject]= MAPmodel;
|
rmeddis@0
|
1376
|
rmeddis@0
|
1377 if experiment.stop || experiment.singleShot
|
rmeddis@0
|
1378 % trap for single trial or user interrupt using 'stop' button.
|
rmeddis@0
|
1379 experiment.status= 'waitingForStart';
|
rmeddis@0
|
1380 experiment.stop=0;
|
rmeddis@28
|
1381 errormsg='manually stopped';
|
rmeddis@28
|
1382 addToMsg(errormsg,1)
|
rmeddis@0
|
1383 return
|
rmeddis@0
|
1384 end
|
rmeddis@28
|
1385
|
rmeddis@0
|
1386 switch modelResponse
|
rmeddis@0
|
1387 case 1
|
rmeddis@0
|
1388 % userDoesNotHearTarget(handles)
|
rmeddis@0
|
1389 switch experiment.ear
|
rmeddis@0
|
1390 case {'MAPmodelListen'}
|
rmeddis@0
|
1391 % illuminate appropriate button
|
rmeddis@0
|
1392 set(handles.pushbutton1,...
|
rmeddis@0
|
1393 'backgroundcolor','r','visible','on')
|
rmeddis@0
|
1394 set(handles.pushbutton2,'backgroundcolor','y')
|
rmeddis@0
|
1395 end
|
rmeddis@0
|
1396 userDecides(handles, false)
|
rmeddis@0
|
1397 if experiment.singleShot, return, end
|
rmeddis@0
|
1398
|
rmeddis@0
|
1399 case 2
|
rmeddis@0
|
1400 % userHearsTarget(handles)
|
rmeddis@0
|
1401 switch experiment.ear
|
rmeddis@0
|
1402 case {'MAPmodelListen'}
|
rmeddis@0
|
1403 % illuminate appropriate button (DEMO only)
|
rmeddis@0
|
1404 set(handles.pushbutton2,'backgroundcolor',...
|
rmeddis@0
|
1405 'r','visible','on')
|
rmeddis@0
|
1406 set(handles.pushbutton1,'backgroundcolor','y')
|
rmeddis@0
|
1407 end
|
rmeddis@28
|
1408
|
rmeddis@0
|
1409 switch experiment.paradigm
|
rmeddis@0
|
1410 case 'discomfort'
|
rmeddis@0
|
1411 % always treat discomfort as 'not heard'
|
rmeddis@0
|
1412 userDecides(handles, false)
|
rmeddis@0
|
1413 otherwise
|
rmeddis@0
|
1414 userDecides(handles, true)
|
rmeddis@0
|
1415 end
|
rmeddis@0
|
1416 otherwise
|
rmeddis@0
|
1417 % probably an abort
|
rmeddis@0
|
1418 return
|
rmeddis@0
|
1419 end
|
rmeddis@0
|
1420 end
|
rmeddis@0
|
1421 end
|
rmeddis@0
|
1422
|
rmeddis@28
|
1423 % -------------------------------------------------------MAPmodel
|
rmeddis@28
|
1424 function [modelResponse, MacGregorResponse]=MAPmodel
|
rmeddis@9
|
1425
|
rmeddis@9
|
1426 global experiment stimulusParameters audio withinRuns
|
rmeddis@9
|
1427 global outerMiddleEarParams DRNLParams AN_IHCsynapseParams
|
rmeddis@9
|
1428
|
rmeddis@9
|
1429 savePath=path;
|
rmeddis@24
|
1430 addpath(['..' filesep 'MAP'], ['..' filesep 'utilities'])
|
rmeddis@9
|
1431 modelResponse=[];
|
rmeddis@9
|
1432 MacGregorResponse=[];
|
rmeddis@9
|
1433
|
rmeddis@9
|
1434 % mono only (column vector)
|
rmeddis@9
|
1435 audio=audio(:,1)';
|
rmeddis@9
|
1436
|
rmeddis@9
|
1437 % if stop button pressed earlier
|
rmeddis@28
|
1438 if experiment.stop
|
rmeddis@28
|
1439 errormsg='manually stopped';
|
rmeddis@28
|
1440 addToMsg(errormsg,1)
|
rmeddis@28
|
1441 return
|
rmeddis@28
|
1442 end
|
rmeddis@9
|
1443
|
rmeddis@28
|
1444 % ---------------------------------------------- run Model
|
rmeddis@9
|
1445 MAPparamsName=experiment.name;
|
rmeddis@9
|
1446 showPlotsAndDetails=experiment.MAPplot;
|
rmeddis@9
|
1447 AN_spikesOrProbability='spikes';
|
rmeddis@28
|
1448 AN_spikesOrProbability='probability';
|
rmeddis@9
|
1449
|
rmeddis@9
|
1450 % [response, method]=MAPsequenceSeg(audio, method, 1:8);
|
rmeddis@28
|
1451 global ICoutput ANdt dt savedBFlist ANprobRateOutput expGUIhandles
|
rmeddis@28
|
1452 global stimulusParameters experiment
|
rmeddis@28
|
1453
|
rmeddis@28
|
1454 if sum(strcmp(experiment.ear,{'MAPmodelMultiCh', 'MAPmodelListen'}))
|
rmeddis@28
|
1455 % use BFlist specified in MAPparams file
|
rmeddis@28
|
1456 BFlist= -1;
|
rmeddis@28
|
1457 else
|
rmeddis@28
|
1458 BFlist=stimulusParameters.targetFrequency;
|
rmeddis@28
|
1459 end
|
rmeddis@28
|
1460 paramChanges=get(expGUIhandles.editparamChanges,'string');
|
rmeddis@28
|
1461 eval(paramChanges)
|
rmeddis@28
|
1462
|
rmeddis@28
|
1463 MAP1_14(audio, stimulusParameters.sampleRate, BFlist,...
|
rmeddis@28
|
1464 MAPparamsName, AN_spikesOrProbability, paramChanges);
|
rmeddis@28
|
1465
|
rmeddis@9
|
1466 if showPlotsAndDetails
|
rmeddis@23
|
1467 options.printModelParameters=0;
|
rmeddis@9
|
1468 options.showModelOutput=1;
|
rmeddis@9
|
1469 options.printFiringRates=1;
|
rmeddis@9
|
1470 options.showACF=0;
|
rmeddis@9
|
1471 options.showEfferent=1;
|
rmeddis@23
|
1472 options.surfProbability=0;
|
rmeddis@25
|
1473 showMapOptions.surfSpikes=0;
|
rmeddis@23
|
1474 UTIL_showMAP(options)
|
rmeddis@9
|
1475 end
|
rmeddis@9
|
1476
|
rmeddis@9
|
1477 % No response, probably caused by hitting 'stop' button
|
rmeddis@28
|
1478 if strcmp(AN_spikesOrProbability,'spikes') && isempty(ICoutput)
|
rmeddis@28
|
1479 return
|
rmeddis@28
|
1480 end
|
rmeddis@9
|
1481 % ---------------------------------------------------------- end model run
|
rmeddis@9
|
1482
|
rmeddis@28
|
1483 if strcmp(AN_spikesOrProbability,'spikes')
|
rmeddis@28
|
1484 MacGregorResponse= sum(ICoutput,1); % use IC
|
rmeddis@28
|
1485 dt=ANdt;
|
rmeddis@28
|
1486 time=dt:dt:dt*length(MacGregorResponse);
|
rmeddis@28
|
1487 else
|
rmeddis@28
|
1488 % for one channel, ANprobResponse=ANprobRateOutput
|
rmeddis@28
|
1489 % for multi channel take strongest in any epoch
|
rmeddis@28
|
1490 nChannels=length(savedBFlist);
|
rmeddis@28
|
1491 % use only HSR fibers
|
rmeddis@28
|
1492 ANprobRateOutput=ANprobRateOutput(end-nChannels+1:end,:);
|
rmeddis@28
|
1493 time=dt:dt:dt*length(ANprobRateOutput);
|
rmeddis@28
|
1494 end
|
rmeddis@9
|
1495
|
rmeddis@28
|
1496 % group delay on unit response - these values are iffy
|
rmeddis@28
|
1497 windowOnsetDelay= 0.004;
|
rmeddis@28
|
1498 windowOffsetDelay= 0.022; % long ringing time
|
rmeddis@9
|
1499
|
rmeddis@9
|
1500 % now find the response of the MacGregor model during the target presentation + group delay
|
rmeddis@9
|
1501 switch experiment.threshEstMethod
|
rmeddis@9
|
1502 case {'2I2AFC++', '2I2AFC+++'}
|
rmeddis@28
|
1503 idx= time>stimulusParameters.testTargetBegins+windowOnsetDelay ...
|
rmeddis@28
|
1504 & time<stimulusParameters.testTargetEnds+windowOffsetDelay;
|
rmeddis@9
|
1505 nSpikesTrueWindow=sum(MacGregorResponse(:,idx));
|
rmeddis@28
|
1506 idx=find(time>stimulusParameters.testNonTargetBegins+windowOnsetDelay ...
|
rmeddis@28
|
1507 & time<stimulusParameters.testNonTargetEnds+windowOffsetDelay);
|
rmeddis@28
|
1508 if strcmp(AN_spikesOrProbability,'spikes')
|
rmeddis@28
|
1509 nSpikesFalseWindow=sum(MacGregorResponse(:,idx));
|
rmeddis@28
|
1510 else
|
rmeddis@28
|
1511 nSpikesDuringTarget=mean(ANprobRateOutput(end,idx));
|
rmeddis@28
|
1512 % compare with spontaneous rate
|
rmeddis@28
|
1513 if nSpikesDuringTarget>ANprobRateOutput(end,1)+10
|
rmeddis@28
|
1514 nSpikesDuringTarget=1; % i.e. at leastone MacG spike
|
rmeddis@28
|
1515 else
|
rmeddis@28
|
1516 nSpikesDuringTarget=0;
|
rmeddis@28
|
1517 end
|
rmeddis@28
|
1518 end
|
rmeddis@9
|
1519 % nSpikesDuringTarget is +ve when more spikes are found
|
rmeddis@9
|
1520 % in the target window
|
rmeddis@9
|
1521 difference= nSpikesTrueWindow-nSpikesFalseWindow;
|
rmeddis@9
|
1522
|
rmeddis@9
|
1523 if difference>0
|
rmeddis@9
|
1524 % hit
|
rmeddis@9
|
1525 nSpikesDuringTarget=experiment.MacGThreshold+1;
|
rmeddis@9
|
1526 elseif difference<0
|
rmeddis@9
|
1527 % miss (wrong choice)
|
rmeddis@9
|
1528 nSpikesDuringTarget=experiment.MacGThreshold-1;
|
rmeddis@9
|
1529 else
|
rmeddis@9
|
1530 if rand>0.5
|
rmeddis@9
|
1531 % hit (random choice)
|
rmeddis@9
|
1532 nSpikesDuringTarget=experiment.MacGThreshold+1;
|
rmeddis@9
|
1533 else
|
rmeddis@9
|
1534 % miss (random choice)
|
rmeddis@9
|
1535 nSpikesDuringTarget=experiment.MacGThreshold-1;
|
rmeddis@9
|
1536 end
|
rmeddis@9
|
1537 end
|
rmeddis@9
|
1538 disp(['level target dummy decision: ' ...
|
rmeddis@9
|
1539 num2str([withinRuns.variableValue nSpikesTrueWindow ...
|
rmeddis@9
|
1540 nSpikesFalseWindow nSpikesDuringTarget], '%4.0f') ] )
|
rmeddis@28
|
1541 otherwise
|
rmeddis@28
|
1542 % single interval
|
rmeddis@28
|
1543 idx=find(time>stimulusParameters.testTargetBegins +windowOnsetDelay...
|
rmeddis@28
|
1544 & time<stimulusParameters.testTargetEnds+windowOffsetDelay);
|
rmeddis@28
|
1545 if strcmp(AN_spikesOrProbability,'spikes')
|
rmeddis@28
|
1546 nSpikesDuringTarget=sum(MacGregorResponse(:,idx));
|
rmeddis@28
|
1547 timeX=time(idx);
|
rmeddis@28
|
1548 else
|
rmeddis@28
|
1549 % probability, use channel with the highest average rate
|
rmeddis@28
|
1550 nSpikesDuringTarget=max(mean(ANprobRateOutput(:,idx),2));
|
rmeddis@28
|
1551 if nSpikesDuringTarget>ANprobRateOutput(end,1)+10
|
rmeddis@28
|
1552 nSpikesDuringTarget=1;
|
rmeddis@28
|
1553 else
|
rmeddis@28
|
1554 nSpikesDuringTarget=0;
|
rmeddis@28
|
1555 end
|
rmeddis@9
|
1556
|
rmeddis@28
|
1557 end
|
rmeddis@9
|
1558 end
|
rmeddis@9
|
1559
|
rmeddis@9
|
1560 if experiment.MAPplot
|
rmeddis@9
|
1561 % add vertical lines to indicate target region
|
rmeddis@9
|
1562 figure(99), subplot(6,1,6)
|
rmeddis@9
|
1563 hold on
|
rmeddis@9
|
1564 yL=get(gca,'YLim');
|
rmeddis@28
|
1565 plot([stimulusParameters.testTargetBegins + windowOnsetDelay ...
|
rmeddis@28
|
1566 stimulusParameters.testTargetBegins + windowOnsetDelay],yL,'r')
|
rmeddis@28
|
1567 plot([stimulusParameters.testTargetEnds + windowOffsetDelay ...
|
rmeddis@28
|
1568 stimulusParameters.testTargetEnds + windowOffsetDelay],yL,'r')
|
rmeddis@9
|
1569 end
|
rmeddis@9
|
1570
|
rmeddis@9
|
1571 % specify unambiguous response
|
rmeddis@28
|
1572 if nSpikesDuringTarget>experiment.MacGThreshold
|
rmeddis@28
|
1573 modelResponse=2; % stimulus detected
|
rmeddis@28
|
1574 else
|
rmeddis@28
|
1575 modelResponse=1; % nothing heard (default)
|
rmeddis@9
|
1576 end
|
rmeddis@9
|
1577
|
rmeddis@9
|
1578 path(savePath)
|
rmeddis@0
|
1579
|
rmeddis@0
|
1580 % -----------------------------------------------------statsModelRunsGUI
|
rmeddis@0
|
1581 % The computer presses the buttons
|
rmeddis@0
|
1582 function statsModelRunsGUI(handles)
|
rmeddis@0
|
1583 % Decision are made at random using a prescribe statistical function
|
rmeddis@0
|
1584 % to set probabilities as a function of signal level.
|
rmeddis@0
|
1585 global experiment
|
rmeddis@0
|
1586
|
rmeddis@0
|
1587 experiment.allowCatchTrials=0;
|
rmeddis@0
|
1588
|
rmeddis@0
|
1589 while strcmp(experiment.status,'waitingForGO')
|
rmeddis@0
|
1590 % i.e. waiting for new run
|
rmeddis@0
|
1591 if experiment.stop
|
rmeddis@0
|
1592 % user has requested an abort
|
rmeddis@0
|
1593 experiment.status= 'waitingForStart';
|
rmeddis@0
|
1594 addToMsg('manually stopped',1)
|
rmeddis@0
|
1595 return
|
rmeddis@0
|
1596 end
|
rmeddis@28
|
1597
|
rmeddis@0
|
1598 % initiates run and plays first stimulus and it returns
|
rmeddis@0
|
1599 % without waiting for button press
|
rmeddis@0
|
1600 % NB stimulus is not actually generated (for speed)
|
rmeddis@0
|
1601 startNewRun(handles)
|
rmeddis@28
|
1602
|
rmeddis@0
|
1603 while strcmp(experiment.status,'waitingForResponse')
|
rmeddis@0
|
1604 % create artificial response here
|
rmeddis@0
|
1605 modelResponse=statsModelGetResponse;
|
rmeddis@0
|
1606 switch modelResponse
|
rmeddis@0
|
1607 case 1
|
rmeddis@0
|
1608 % userDoesNotHearTarget(handles)
|
rmeddis@0
|
1609 userDecides(handles, false)
|
rmeddis@0
|
1610 case 2
|
rmeddis@0
|
1611 % userHearsTarget(handles)
|
rmeddis@0
|
1612 userDecides(handles, true)
|
rmeddis@0
|
1613 end
|
rmeddis@0
|
1614 end
|
rmeddis@0
|
1615 end
|
rmeddis@0
|
1616
|
rmeddis@0
|
1617 % -----------------------------------------------------statsModelGetResponse
|
rmeddis@0
|
1618 function modelResponse=statsModelGetResponse(handles)
|
rmeddis@0
|
1619 global experiment withinRuns statsModel stimulusParameters
|
rmeddis@0
|
1620 % use the generating function to decide if a detection occurs or not
|
rmeddis@0
|
1621
|
rmeddis@0
|
1622 % pause(0.1) % to allow stopping with CTRL/C but slows things down
|
rmeddis@0
|
1623
|
rmeddis@0
|
1624 % first compute the probability that a detection occurs
|
rmeddis@0
|
1625 switch experiment.ear
|
rmeddis@0
|
1626 case {'statsModelLogistic'}
|
rmeddis@0
|
1627 prob= 1./(1+exp(-statsModel.logisticSlope.*(withinRuns.variableValue-statsModel.logisticMean)));
|
rmeddis@0
|
1628 % if experiment.psyFunSlope<0,
|
rmeddis@0
|
1629 % prob=1-prob;
|
rmeddis@0
|
1630 % end
|
rmeddis@28
|
1631
|
rmeddis@0
|
1632 case 'statsModelRareEvent'
|
rmeddis@0
|
1633 if experiment.psyFunSlope<0
|
rmeddis@0
|
1634 addToMsg('statsModelRareEvent cannot be used with negative slope',0)
|
rmeddis@0
|
1635 error('statsModelRareEvent cannot be used with negative slope')
|
rmeddis@0
|
1636 end
|
rmeddis@28
|
1637
|
rmeddis@0
|
1638 % standard formula is prob = 1 – exp(-d (g P – A))
|
rmeddis@0
|
1639 % here A->A; To find Pmin use A/gain
|
rmeddis@0
|
1640 pressure=28*10^(withinRuns.variableValue/20);
|
rmeddis@0
|
1641 gain=statsModel.rareEvenGain;
|
rmeddis@0
|
1642 A=statsModel.rareEventVmin;
|
rmeddis@0
|
1643 d=stimulusParameters.targetDuration;
|
rmeddis@0
|
1644 gP_Vmin=gain*pressure-A;
|
rmeddis@0
|
1645 if gP_Vmin>0
|
rmeddis@0
|
1646 prob=1-exp(-d*(gP_Vmin));
|
rmeddis@0
|
1647 else
|
rmeddis@0
|
1648 prob=0;
|
rmeddis@0
|
1649 end
|
rmeddis@0
|
1650 end
|
rmeddis@0
|
1651
|
rmeddis@0
|
1652 % Use the probability to choose whether or not a detection has occurred
|
rmeddis@0
|
1653 switch experiment.threshEstMethod
|
rmeddis@0
|
1654 case {'MaxLikelihood', 'oneIntervalUpDown'}
|
rmeddis@0
|
1655 if rand<prob
|
rmeddis@0
|
1656 modelResponse=2; %bingo
|
rmeddis@0
|
1657 else
|
rmeddis@0
|
1658 modelResponse=1; %nothing heard
|
rmeddis@0
|
1659 end
|
rmeddis@28
|
1660
|
rmeddis@0
|
1661 case {'2I2AFC++', '2I2AFC+++'}
|
rmeddis@0
|
1662 if rand<prob
|
rmeddis@0
|
1663 modelResponse=2; %bingo
|
rmeddis@0
|
1664 else %if the stimulus is not audible, take a 50:50 chance of getting it right
|
rmeddis@0
|
1665 if rand<0.5
|
rmeddis@0
|
1666 modelResponse=2; %bingo
|
rmeddis@0
|
1667 else
|
rmeddis@0
|
1668 modelResponse=1; %nothing heard
|
rmeddis@0
|
1669 end
|
rmeddis@0
|
1670 end
|
rmeddis@0
|
1671 end
|
rmeddis@0
|
1672
|
rmeddis@0
|
1673
|
rmeddis@0
|
1674 % ------------------------------------------------------- printTabTable
|
rmeddis@0
|
1675 function printTabTable(M, headers)
|
rmeddis@0
|
1676 % printTabTable prints a matrix as a table with tabs
|
rmeddis@0
|
1677 %headers are optional
|
rmeddis@0
|
1678 %headers=strvcat('firstname', 'secondname')
|
rmeddis@0
|
1679 % printTabTable([1 2; 3 4],strvcat('a1','a2'));
|
rmeddis@0
|
1680
|
rmeddis@0
|
1681 if nargin>1
|
rmeddis@0
|
1682 [r c]=size(headers);
|
rmeddis@0
|
1683 for no=1:r
|
rmeddis@0
|
1684 fprintf('%s\t',headers(no,:))
|
rmeddis@0
|
1685 end
|
rmeddis@0
|
1686 fprintf('\n')
|
rmeddis@0
|
1687 end
|
rmeddis@0
|
1688
|
rmeddis@0
|
1689 [r c]=size(M);
|
rmeddis@0
|
1690
|
rmeddis@0
|
1691 for row=1:r
|
rmeddis@0
|
1692 for col=1:c
|
rmeddis@0
|
1693 if row==1 && col==1 && M(1,1)==-1000
|
rmeddis@0
|
1694 % Print nothing (tab follows below)
|
rmeddis@0
|
1695 else
|
rmeddis@0
|
1696 fprintf('%s',num2str(M(row,col)))
|
rmeddis@0
|
1697 end
|
rmeddis@0
|
1698 if col<c
|
rmeddis@0
|
1699 fprintf('\t')
|
rmeddis@0
|
1700 end
|
rmeddis@0
|
1701 end
|
rmeddis@0
|
1702 fprintf('\n')
|
rmeddis@0
|
1703 end
|
rmeddis@0
|
1704
|
rmeddis@0
|
1705 % ------------------------------------------------------- xlimRM
|
rmeddis@0
|
1706 function xlimRM(x)
|
rmeddis@0
|
1707 try
|
rmeddis@0
|
1708 xlim([x(1) x(2)])
|
rmeddis@0
|
1709 catch
|
rmeddis@0
|
1710 end
|
rmeddis@0
|
1711
|
rmeddis@0
|
1712 % ------------------------------------------------------- ylimRM
|
rmeddis@0
|
1713 function ylimRM(x)
|
rmeddis@0
|
1714 try
|
rmeddis@0
|
1715 ylim([x(1) x(2)])
|
rmeddis@0
|
1716 catch
|
rmeddis@0
|
1717 end
|
rmeddis@0
|
1718
|
rmeddis@0
|
1719
|
rmeddis@0
|
1720 function editdigitInput_CreateFcn(hObject, eventdata, handles)
|
rmeddis@0
|
1721
|
rmeddis@0
|
1722 % Hint: edit controls usually have a white background on Windows.
|
rmeddis@0
|
1723 % See ISPC and COMPUTER.
|
rmeddis@0
|
1724 if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor'))
|
rmeddis@0
|
1725 set(hObject,'BackgroundColor','white');
|
rmeddis@0
|
1726 end
|
rmeddis@0
|
1727
|
rmeddis@0
|
1728
|
rmeddis@0
|
1729 % -----------------------------------------------------buttonBoxIntitialize
|
rmeddis@0
|
1730 function buttonBoxIntitialize
|
rmeddis@0
|
1731 % initialize button box
|
rmeddis@0
|
1732 global serobj
|
rmeddis@0
|
1733 try
|
rmeddis@0
|
1734 fclose(serobj);
|
rmeddis@0
|
1735 catch
|
rmeddis@0
|
1736 end
|
rmeddis@0
|
1737
|
rmeddis@0
|
1738 try
|
rmeddis@0
|
1739 serobj = serial('COM4') ; % Creating serial port object now its connected to COM4 !!! button boxes in booths are connected to COM2
|
rmeddis@0
|
1740 serobj.Baudrate = 9600; % Set the baud rate at the specific value
|
rmeddis@0
|
1741 set(serobj, 'Parity', 'none') ; % Set parity as none
|
rmeddis@0
|
1742 set(serobj, 'Databits', 8) ; % set the number of data bits
|
rmeddis@0
|
1743 set(serobj, 'StopBits', 1) ; % set number of stop bits as 1
|
rmeddis@0
|
1744 set(serobj, 'Terminator', 'CR') ; % set the terminator value to carriage return
|
rmeddis@0
|
1745 set(serobj, 'InputBufferSize', 512) ; % Buffer for read operation, default it is 512
|
rmeddis@0
|
1746 set(serobj,'timeout',10); % 10 sec timeout on button press
|
rmeddis@0
|
1747 set(serobj, 'ReadAsyncMode', 'continuous')
|
rmeddis@0
|
1748 set(serobj, 'BytesAvailableFcn', @buttonBox_callback)
|
rmeddis@0
|
1749 set(serobj, 'BytesAvailableFcnCount', 1)
|
rmeddis@0
|
1750 set(serobj, 'BytesAvailableFcnMode', 'byte')
|
rmeddis@0
|
1751 % set(serobj, 'BreakInterruptFcn', '@buttonBox_Calback')
|
rmeddis@28
|
1752
|
rmeddis@0
|
1753 fopen(serobj);
|
rmeddis@0
|
1754 buttonBoxStatus=get(serobj,'status');
|
rmeddis@0
|
1755 catch
|
rmeddis@0
|
1756 disp('** no button box found - use mouse **')
|
rmeddis@0
|
1757 end
|