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