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@35
|
672 if ispc
|
rmeddis@35
|
673 wavplay(y/100,fs)
|
rmeddis@35
|
674 else
|
rmeddis@35
|
675 sound(y/100,fs)
|
rmeddis@35
|
676 end
|
rmeddis@35
|
677
|
rmeddis@28
|
678
|
rmeddis@28
|
679 % raise catch trial rate temporarily.
|
rmeddis@0
|
680 % this is normally reduced on each new trial (see GO)
|
rmeddis@0
|
681 stimulusParameters.catchTrialRate=...
|
rmeddis@0
|
682 stimulusParameters.catchTrialRate+0.1;
|
rmeddis@28
|
683 if stimulusParameters.catchTrialRate>0.5
|
rmeddis@28
|
684 stimulusParameters.catchTrialRate=0.5;
|
rmeddis@0
|
685 end
|
rmeddis@0
|
686 fprintf('stimulusParameters.catchTrialRate= %6.3f\n', ...
|
rmeddis@0
|
687 stimulusParameters.catchTrialRate)
|
rmeddis@28
|
688
|
rmeddis@0
|
689 betweenRuns.caughtOut(betweenRuns.runNumber)=...
|
rmeddis@0
|
690 1+betweenRuns.caughtOut(betweenRuns.runNumber);
|
rmeddis@0
|
691 betweenRuns.runNumber=betweenRuns.runNumber-1;
|
rmeddis@0
|
692 experiment.status='waitingForGO';
|
rmeddis@0
|
693 return % unwind and wait for button press
|
rmeddis@0
|
694 else % (said No)
|
rmeddis@28
|
695 % user claims not to have heard target.
|
rmeddis@28
|
696 % This is good as it was not present.
|
rmeddis@28
|
697 % So, repeat the stimulus (possibly with target)
|
rmeddis@0
|
698 % and behave as if the last trial did not occur
|
rmeddis@0
|
699 errormsg=nextStimulus(handles);
|
rmeddis@28
|
700
|
rmeddis@0
|
701 % terminate if there is any kind of problem
|
rmeddis@0
|
702 if ~isempty(errormsg)
|
rmeddis@0
|
703 % e.g. limits exceeded, clipping
|
rmeddis@0
|
704 disp(['Error nextStimulus: ' errormsg])
|
rmeddis@0
|
705 runCompleted(handles)
|
rmeddis@0
|
706 return
|
rmeddis@0
|
707 end
|
rmeddis@0
|
708 return % no further action - next trial
|
rmeddis@0
|
709 end
|
rmeddis@28
|
710 end % of catch trial
|
rmeddis@0
|
711
|
rmeddis@28
|
712 % Real target: analyse the response, make tracks and define next stim.
|
rmeddis@0
|
713
|
rmeddis@0
|
714 % Define response and update response list
|
rmeddis@0
|
715 if saidYes
|
rmeddis@0
|
716 % target was heard, so response=1;
|
rmeddis@0
|
717 withinRuns.responseList=[withinRuns.responseList 1]; % 'heard it!'
|
rmeddis@0
|
718 else
|
rmeddis@0
|
719 % target was not hear heard, so response=0;
|
rmeddis@0
|
720 withinRuns.responseList=[withinRuns.responseList 0];
|
rmeddis@0
|
721 end
|
rmeddis@0
|
722 withinRuns.levelList=[withinRuns.levelList withinRuns.variableValue];
|
rmeddis@0
|
723 trialNumber=length(withinRuns.responseList);
|
rmeddis@0
|
724
|
rmeddis@0
|
725 % keep track of peaks and troughs;
|
rmeddis@0
|
726 % identify direction of change during initial period
|
rmeddis@0
|
727 if saidYes
|
rmeddis@0
|
728 % default step size before first reversal
|
rmeddis@28
|
729 WRVinitialStep=-stimulusParameters.WRVinitialStep;
|
rmeddis@0
|
730 WRVsmallStep=-stimulusParameters.WRVsmallStep;
|
rmeddis@0
|
731 % if the previous direction was 'less difficult', this must be a peak
|
rmeddis@0
|
732 if strcmp(withinRuns.direction,'less difficult') ...
|
rmeddis@0
|
733 && length(withinRuns.levelList)>1
|
rmeddis@0
|
734 withinRuns.peaks=[withinRuns.peaks withinRuns.variableValue];
|
rmeddis@0
|
735 end
|
rmeddis@0
|
736 withinRuns.direction='more difficult';
|
rmeddis@0
|
737 else
|
rmeddis@0
|
738 % said 'no'
|
rmeddis@0
|
739 % default step size before first reversal
|
rmeddis@28
|
740 WRVinitialStep=stimulusParameters.WRVinitialStep;
|
rmeddis@0
|
741 WRVsmallStep=stimulusParameters.WRVsmallStep;
|
rmeddis@28
|
742
|
rmeddis@0
|
743 % if the previous direction was 'up', this must be a peak
|
rmeddis@0
|
744 if strcmp(withinRuns.direction,'more difficult') ...
|
rmeddis@0
|
745 && length(withinRuns.levelList)>1
|
rmeddis@0
|
746 withinRuns.troughs=[withinRuns.troughs withinRuns.variableValue];
|
rmeddis@0
|
747 end
|
rmeddis@0
|
748 withinRuns.direction='less difficult';
|
rmeddis@0
|
749 end
|
rmeddis@0
|
750
|
rmeddis@28
|
751 % phase 2 is all the levels after and incuding the first reversal
|
rmeddis@0
|
752 % plus the level before that
|
rmeddis@28
|
753 % Look for the end of phase 1
|
rmeddis@0
|
754 if ~withinRuns.nowInPhase2 && length(withinRuns.peaks)+ ...
|
rmeddis@0
|
755 length(withinRuns.troughs)>0
|
rmeddis@0
|
756 % define phase 2
|
rmeddis@0
|
757 withinRuns.beginningOfPhase2=trialNumber-1;
|
rmeddis@0
|
758 withinRuns.nowInPhase2=1;
|
rmeddis@0
|
759 WRVsmallStep=WRVinitialStep/2;
|
rmeddis@0
|
760 end
|
rmeddis@0
|
761
|
rmeddis@0
|
762 if withinRuns.nowInPhase2
|
rmeddis@0
|
763 % keep a record of all levels and responses in phase 2 only
|
rmeddis@0
|
764 withinRuns.levelsPhaseTwo=...
|
rmeddis@0
|
765 withinRuns.levelList(withinRuns.beginningOfPhase2:end);
|
rmeddis@0
|
766 withinRuns.responsesPhaseTwo=...
|
rmeddis@0
|
767 withinRuns.responseList(withinRuns.beginningOfPhase2:end);
|
rmeddis@0
|
768 else
|
rmeddis@0
|
769 withinRuns.levelsPhaseTwo=[];
|
rmeddis@0
|
770 end
|
rmeddis@0
|
771
|
rmeddis@0
|
772 % get (or substitute) threshold estimate
|
rmeddis@0
|
773 switch experiment.threshEstMethod
|
rmeddis@0
|
774 case {'2I2AFC++', '2I2AFC+++'}
|
rmeddis@0
|
775 % for plotting psychometric function only
|
rmeddis@0
|
776 if withinRuns.beginningOfPhase2>0
|
rmeddis@0
|
777 [psy, levelsBinVector, logistic, rareEvent]= ...
|
rmeddis@0
|
778 bestFitPsychometicFunctions...
|
rmeddis@0
|
779 (withinRuns.levelsPhaseTwo, withinRuns.responsesPhaseTwo);
|
rmeddis@0
|
780 end
|
rmeddis@28
|
781
|
rmeddis@0
|
782 if ~isempty(withinRuns.peaks) && ~isempty(withinRuns.troughs)
|
rmeddis@0
|
783 thresholdEstimate= ...
|
rmeddis@0
|
784 mean([mean(withinRuns.peaks) mean(withinRuns.troughs)]);
|
rmeddis@0
|
785 else
|
rmeddis@0
|
786 thresholdEstimate=NaN;
|
rmeddis@0
|
787 end
|
rmeddis@28
|
788
|
rmeddis@0
|
789 otherwise
|
rmeddis@0
|
790 % single interval methods
|
rmeddis@0
|
791 try
|
rmeddis@0
|
792 % using the s trial after the first reversal
|
rmeddis@0
|
793 [psy, levelsBinVector, logistic, rareEvent]= ...
|
rmeddis@0
|
794 bestFitPsychometicFunctions(withinRuns.levelsPhaseTwo,...
|
rmeddis@0
|
795 withinRuns.responsesPhaseTwo);
|
rmeddis@0
|
796 catch
|
rmeddis@0
|
797 logistic.bestThreshold=NaN;
|
rmeddis@0
|
798 end
|
rmeddis@0
|
799 end
|
rmeddis@0
|
800
|
rmeddis@0
|
801 if withinRuns.nowInPhase2
|
rmeddis@0
|
802 % save tracks of threshold estimates for plotting andprinting
|
rmeddis@0
|
803 switch experiment.functionEstMethod
|
rmeddis@0
|
804 case {'logisticLS', 'logisticML'}
|
rmeddis@0
|
805 if withinRuns.nowInPhase2
|
rmeddis@0
|
806 withinRuns.meanEstTrack=...
|
rmeddis@0
|
807 [withinRuns.meanEstTrack ...
|
rmeddis@0
|
808 mean(withinRuns.levelsPhaseTwo)];
|
rmeddis@0
|
809 withinRuns.thresholdEstimateTrack=...
|
rmeddis@0
|
810 [withinRuns.thresholdEstimateTrack ...
|
rmeddis@0
|
811 logistic.bestThreshold];
|
rmeddis@0
|
812 end
|
rmeddis@0
|
813 case 'rareEvent'
|
rmeddis@0
|
814 withinRuns.meanEstTrack=...
|
rmeddis@0
|
815 [withinRuns.meanEstTrack rareEvent.thresholddB];
|
rmeddis@0
|
816 withinRuns.thresholdEstimateTrack=...
|
rmeddis@0
|
817 [withinRuns.thresholdEstimateTrack logistic.bestThreshold];
|
rmeddis@0
|
818 case 'peaksAndTroughs'
|
rmeddis@0
|
819 withinRuns.meanEstTrack=...
|
rmeddis@0
|
820 [withinRuns.meanEstTrack thresholdEstimate];
|
rmeddis@0
|
821 withinRuns.thresholdEstimateTrack=...
|
rmeddis@0
|
822 [withinRuns.thresholdEstimateTrack thresholdEstimate];
|
rmeddis@0
|
823 end
|
rmeddis@0
|
824 end
|
rmeddis@0
|
825
|
rmeddis@0
|
826 % special discomfort condition
|
rmeddis@0
|
827 % run is completed when subject hits '2' button
|
rmeddis@0
|
828 switch experiment.paradigm
|
rmeddis@0
|
829 case 'discomfort'
|
rmeddis@0
|
830 if saidYes
|
rmeddis@0
|
831 runCompleted(handles)
|
rmeddis@0
|
832 return
|
rmeddis@0
|
833 end
|
rmeddis@0
|
834 end
|
rmeddis@0
|
835
|
rmeddis@0
|
836 % choose the next level for the stimulus
|
rmeddis@0
|
837 switch experiment.threshEstMethod
|
rmeddis@0
|
838 case {'2I2AFC++', '2I2AFC+++'}
|
rmeddis@0
|
839 if saidYes
|
rmeddis@0
|
840 [WRVinitialStep, msg]=Levitt2('hit', withinRuns.variableValue);
|
rmeddis@0
|
841 else
|
rmeddis@0
|
842 [WRVinitialStep, msg]=Levitt2('miss',withinRuns.variableValue);
|
rmeddis@0
|
843 end
|
rmeddis@28
|
844
|
rmeddis@0
|
845 % empty message means continue as normal
|
rmeddis@28
|
846 if ~isempty(msg)
|
rmeddis@0
|
847 runCompleted(handles)
|
rmeddis@0
|
848 return
|
rmeddis@0
|
849 end
|
rmeddis@0
|
850 newWRVvalue=withinRuns.variableValue-WRVinitialStep;
|
rmeddis@28
|
851
|
rmeddis@0
|
852 case {'MaxLikelihood', 'oneIntervalUpDown'}
|
rmeddis@28
|
853 % run completed by virtue of number of trials
|
rmeddis@0
|
854 % or restart because listener is in trouble
|
rmeddis@0
|
855 if length(withinRuns.levelsPhaseTwo)== experiment.maxTrials
|
rmeddis@0
|
856 % Use bonomial test to decide if there is an imbalance in the
|
rmeddis@0
|
857 % number of 'yes'es and 'no's
|
rmeddis@0
|
858 yesCount=sum(withinRuns.responseList);
|
rmeddis@0
|
859 noCount=length(withinRuns.responseList)-yesCount;
|
rmeddis@0
|
860 z=abs(yesCount-noCount)/(yesCount+noCount)^0.5;
|
rmeddis@0
|
861 if z>1.96
|
rmeddis@0
|
862 betweenRuns.resets=betweenRuns.resets+1;
|
rmeddis@0
|
863 disp([ 'reset / z= ' num2str( z) ...
|
rmeddis@0
|
864 ' Nresets= ' num2str( betweenRuns.resets) ] )
|
rmeddis@0
|
865 withinRuns.peaks=[];
|
rmeddis@0
|
866 withinRuns.troughs=[];
|
rmeddis@0
|
867 withinRuns.levelList=withinRuns.levelList(end);
|
rmeddis@0
|
868 withinRuns.meanEstTrack=withinRuns.meanEstTrack(end);
|
rmeddis@0
|
869 withinRuns.forceThreshold=NaN;
|
rmeddis@0
|
870 withinRuns.responseList=withinRuns.responseList(end);
|
rmeddis@0
|
871 withinRuns.beginningOfPhase2=0;
|
rmeddis@0
|
872 withinRuns.nowInPhase2=0;
|
rmeddis@0
|
873 withinRuns.thresholdEstimateTrack=...
|
rmeddis@0
|
874 withinRuns.thresholdEstimateTrack(end);
|
rmeddis@0
|
875 else
|
rmeddis@0
|
876 runCompleted(handles)
|
rmeddis@0
|
877 return
|
rmeddis@0
|
878 end
|
rmeddis@0
|
879 end
|
rmeddis@28
|
880
|
rmeddis@0
|
881 % set new value for WRV
|
rmeddis@0
|
882 if withinRuns.nowInPhase2
|
rmeddis@0
|
883 % phase 2
|
rmeddis@0
|
884 currentMeanEst=withinRuns.thresholdEstimateTrack(end);
|
rmeddis@0
|
885 switch experiment.threshEstMethod
|
rmeddis@0
|
886 case 'MaxLikelihood'
|
rmeddis@28
|
887 newWRVvalue=currentMeanEst;
|
rmeddis@0
|
888 case {'oneIntervalUpDown'}
|
rmeddis@0
|
889 newWRVvalue=withinRuns.variableValue+WRVsmallStep;
|
rmeddis@0
|
890 end
|
rmeddis@0
|
891 else
|
rmeddis@0
|
892 % phase 1
|
rmeddis@0
|
893 if withinRuns.variableValue+2*WRVinitialStep>...
|
rmeddis@0
|
894 stimulusParameters.WRVlimits(2)
|
rmeddis@0
|
895 % use smaller steps when close to maximum
|
rmeddis@0
|
896 WRVinitialStep=WRVinitialStep/2;
|
rmeddis@0
|
897 end
|
rmeddis@0
|
898 newWRVvalue=withinRuns.variableValue+WRVinitialStep;
|
rmeddis@0
|
899 end
|
rmeddis@0
|
900 otherwise
|
rmeddis@0
|
901 error( 'assessment method not recognised')
|
rmeddis@0
|
902 end
|
rmeddis@0
|
903
|
rmeddis@0
|
904 switch experiment.paradigm
|
rmeddis@0
|
905 % prevent unrealistic gap durations 'gapDetection' tasks.
|
rmeddis@0
|
906 % Note that the gap begins when the ramp ends not when stimulus ends
|
rmeddis@0
|
907 case 'gapDetection'
|
rmeddis@0
|
908 if newWRVvalue<-2*stimulusParameters.rampDuration
|
rmeddis@0
|
909 newWRVvalue=-2*stimulusParameters.rampDuration;
|
rmeddis@0
|
910 addToMsg('gap duration fixed at - 2 * ramp!',1, 1)
|
rmeddis@0
|
911 end
|
rmeddis@0
|
912 end
|
rmeddis@0
|
913
|
rmeddis@0
|
914 withinRuns.variableValue=newWRVvalue;
|
rmeddis@0
|
915 withinRuns.trialNumber=withinRuns.trialNumber+1;
|
rmeddis@0
|
916
|
rmeddis@0
|
917 % Trial continues
|
rmeddis@0
|
918 plotProgressThisTrial(handles)
|
rmeddis@0
|
919
|
rmeddis@0
|
920 % next stimulus and so the cycle continues
|
rmeddis@0
|
921 errormsg=nextStimulus(handles);
|
rmeddis@0
|
922 % after the stimulus is presented, control returns here and the system
|
rmeddis@0
|
923 % waits for user action.
|
rmeddis@0
|
924
|
rmeddis@0
|
925 % terminate if there is any kind of problem
|
rmeddis@0
|
926 if ~isempty(errormsg)
|
rmeddis@0
|
927 % e.g. limits exceeded, clipping
|
rmeddis@0
|
928 disp(['Error nextStimulus: ' errormsg])
|
rmeddis@0
|
929 runCompleted(handles)
|
rmeddis@0
|
930 return
|
rmeddis@0
|
931 end
|
rmeddis@0
|
932
|
rmeddis@0
|
933 % ------------------------------------------------ userSelectsPleaseRepeat
|
rmeddis@0
|
934 function userSelectsPleaseRepeat(handles)
|
rmeddis@0
|
935 global experiment withinRuns
|
rmeddis@0
|
936 % ignore click if not 'waitingForResponse'
|
rmeddis@0
|
937 if ~strcmp(experiment.status,'waitingForResponse')
|
rmeddis@0
|
938 disp('ignored click')
|
rmeddis@0
|
939 return
|
rmeddis@0
|
940 end
|
rmeddis@28
|
941 % Take no action other than to make a
|
rmeddis@0
|
942 % tally of repeat requests
|
rmeddis@0
|
943 experiment.pleaseRepeat=experiment.pleaseRepeat+1;
|
rmeddis@0
|
944 withinRuns.thisIsRepeatTrial=1;
|
rmeddis@0
|
945 nextStimulus(handles);
|
rmeddis@0
|
946
|
rmeddis@0
|
947 % ------------------------------------------------ userSelectsWrongButton
|
rmeddis@0
|
948 function userSelectsWrongButton(handles)
|
rmeddis@0
|
949 global withinRuns experiment
|
rmeddis@0
|
950 % restart is the simplest solution for a 'wrong button' request
|
rmeddis@0
|
951 withinRuns.wrongButton=withinRuns.wrongButton+1;
|
rmeddis@28
|
952 set(handles.pushbuttonGO, 'visible','on', 'backgroundcolor','y')
|
rmeddis@0
|
953 msg=[{'Start again: wrong button pressed'}, {' '},...
|
rmeddis@0
|
954 {'Please,click on the GO button'}];
|
rmeddis@0
|
955 set(handles.textMSG,'string',msg)
|
rmeddis@0
|
956 experiment.status='waitingForGO';
|
rmeddis@0
|
957
|
rmeddis@0
|
958 % ------------------------------------------------- plotProgressThisTrial
|
rmeddis@0
|
959 function plotProgressThisTrial(handles)
|
rmeddis@28
|
960 % updates GUI: used for all responses
|
rmeddis@0
|
961
|
rmeddis@0
|
962 global experiment stimulusParameters betweenRuns withinRuns expGUIhandles
|
rmeddis@0
|
963 global psy levelsBinVector binFrequencies rareEvent logistic statsModel
|
rmeddis@0
|
964
|
rmeddis@0
|
965 % plot the levelTrack and the threshold track
|
rmeddis@0
|
966
|
rmeddis@0
|
967 % Panel 2
|
rmeddis@0
|
968 % plot the levelList
|
rmeddis@0
|
969 axes(expGUIhandles.axes2); cla
|
rmeddis@0
|
970 plot( withinRuns.levelList,'o','markerFaceColor','k'), hold on
|
rmeddis@0
|
971 % plot the best threshold estimate tracks
|
rmeddis@0
|
972 if length(withinRuns.meanEstTrack)>=1
|
rmeddis@0
|
973 % The length of the levelList is 2 greater than number of thresholds
|
rmeddis@0
|
974 ptr=withinRuns.beginningOfPhase2+1;
|
rmeddis@0
|
975 plot(ptr: ptr+length(withinRuns.meanEstTrack)-1, ...
|
rmeddis@0
|
976 withinRuns.meanEstTrack, 'r')
|
rmeddis@0
|
977 plot( ptr: ptr+length(withinRuns.thresholdEstimateTrack)-1, ...
|
rmeddis@0
|
978 withinRuns.thresholdEstimateTrack, 'g')
|
rmeddis@0
|
979 hold off
|
rmeddis@0
|
980 estThresh=withinRuns.thresholdEstimateTrack(end);
|
rmeddis@0
|
981 switch experiment.threshEstMethod
|
rmeddis@0
|
982 % add appropriate labels to subject GUI buttons
|
rmeddis@0
|
983 case {'2I2AFC++', '2I2AFC+++'}
|
rmeddis@0
|
984 title([stimulusParameters.WRVname ' = ' ...
|
rmeddis@0
|
985 num2str(withinRuns.variableValue, '%5.1f')])
|
rmeddis@0
|
986 otherwise
|
rmeddis@0
|
987 title([stimulusParameters.WRVname ' = ' ...
|
rmeddis@0
|
988 num2str(withinRuns.variableValue, '%5.1f') ...
|
rmeddis@38
|
989 '; TH= ' num2str(estThresh, '%5.1f') ' dB'])
|
rmeddis@0
|
990 end
|
rmeddis@0
|
991 end
|
rmeddis@0
|
992 xlim([0 experiment.maxTrials+withinRuns.beginningOfPhase2]);
|
rmeddis@0
|
993 ylim(stimulusParameters.WRVlimits)
|
rmeddis@38
|
994 ylabel ('dB SPL')
|
rmeddis@0
|
995 grid on
|
rmeddis@0
|
996
|
rmeddis@0
|
997 % Panel 4: Summary of threshold estimates (not used here)
|
rmeddis@28
|
998 % Estimates from previous runs are set in 'runCompleted'
|
rmeddis@28
|
999 % It is only necessary to change title showing runs/trials remaining
|
rmeddis@0
|
1000 axes(expGUIhandles.axes4)
|
rmeddis@0
|
1001 runsToGo=length(betweenRuns.var1Sequence)-betweenRuns.runNumber;
|
rmeddis@0
|
1002 if withinRuns.beginningOfPhase2>0
|
rmeddis@0
|
1003 trialsToGo= experiment.singleIntervalMaxTrials(1) ...
|
rmeddis@0
|
1004 + withinRuns.beginningOfPhase2- withinRuns.trialNumber;
|
rmeddis@0
|
1005 title(['trials remaining = ' num2str(trialsToGo) ...
|
rmeddis@0
|
1006 ': runs to go= ' num2str(runsToGo)])
|
rmeddis@0
|
1007 end
|
rmeddis@0
|
1008
|
rmeddis@0
|
1009 % plot psychometric function - panel 5
|
rmeddis@0
|
1010 axes(expGUIhandles.axes5), cla
|
rmeddis@0
|
1011 plot(withinRuns.levelList, withinRuns.responseList,'b.'), hold on
|
rmeddis@0
|
1012 ylim([0 1])
|
rmeddis@0
|
1013 title('')
|
rmeddis@0
|
1014
|
rmeddis@0
|
1015 switch experiment.threshEstMethod
|
rmeddis@0
|
1016 case {'MaxLikelihood', 'oneIntervalUpDown'}
|
rmeddis@0
|
1017 if withinRuns.beginningOfPhase2>0
|
rmeddis@0
|
1018 % display only when in phase 2.
|
rmeddis@0
|
1019 withinRuns.levelsPhaseTwo=...
|
rmeddis@0
|
1020 withinRuns.levelList(withinRuns.beginningOfPhase2:end);
|
rmeddis@0
|
1021 withinRuns.responsesPhaseTwo=...
|
rmeddis@0
|
1022 withinRuns.responseList(withinRuns.beginningOfPhase2:end);
|
rmeddis@28
|
1023
|
rmeddis@0
|
1024 % organise data as psychometric function
|
rmeddis@0
|
1025 [psy, levelsBinVector, binFrequencies]= ...
|
rmeddis@0
|
1026 psychometricFunction(withinRuns.levelsPhaseTwo,...
|
rmeddis@0
|
1027 withinRuns.responsesPhaseTwo, experiment.psyBinWidth);
|
rmeddis@28
|
1028
|
rmeddis@0
|
1029 % Plot the function
|
rmeddis@0
|
1030 % point by point with circles of appropiate weighted size
|
rmeddis@0
|
1031 hold on,
|
rmeddis@0
|
1032 for i=1:length(psy)
|
rmeddis@0
|
1033 plot(levelsBinVector(i), psy(i), 'ro', ...
|
rmeddis@0
|
1034 'markersize', 50*binFrequencies(i)/sum(binFrequencies))
|
rmeddis@0
|
1035 end
|
rmeddis@0
|
1036 % save info for later
|
rmeddis@0
|
1037 betweenRuns.psychometicFunction{betweenRuns.runNumber}=...
|
rmeddis@0
|
1038 [levelsBinVector; psy];
|
rmeddis@28
|
1039
|
rmeddis@0
|
1040 % fitPsychometric functions is computed in 'userDecides'
|
rmeddis@0
|
1041 % plot(rareEvent.predictionLevels, rareEvent.predictionsRE,'k')
|
rmeddis@0
|
1042 plot(logistic.predictionLevels, logistic.predictionsLOG, 'r')
|
rmeddis@0
|
1043 plot(rareEvent.predictionLevels, rareEvent.predictionsRE, 'k')
|
rmeddis@0
|
1044 if ~isnan(logistic.bestThreshold )
|
rmeddis@0
|
1045 xlim([ 0 100 ])
|
rmeddis@28
|
1046 title(['k= ' num2str(logistic.bestK, '%6.2f') ' g= '...
|
rmeddis@28
|
1047 num2str(rareEvent.bestGain,'%6.3f') ' A=' ...
|
rmeddis@28
|
1048 num2str(rareEvent.bestVMin,'%8.1f')])
|
rmeddis@0
|
1049 else
|
rmeddis@0
|
1050 title(' ')
|
rmeddis@0
|
1051 end
|
rmeddis@28
|
1052
|
rmeddis@0
|
1053 switch experiment.ear
|
rmeddis@0
|
1054 %plot green line for statsModel a priori model
|
rmeddis@0
|
1055 case 'statsModelLogistic'
|
rmeddis@0
|
1056 % plot proTem logistic (green) used by stats model
|
rmeddis@0
|
1057 p= 1./(1+exp(-statsModel.logisticSlope...
|
rmeddis@0
|
1058 *(levelsBinVector-logistic.bestThreshold)));
|
rmeddis@0
|
1059 if experiment.psyFunSlope<0, p=1-p;end
|
rmeddis@0
|
1060 titleText=[ ', statsModel: logistic'];
|
rmeddis@0
|
1061 hold on, plot(levelsBinVector, p,'g')
|
rmeddis@0
|
1062 case 'statsModelRareEvent'
|
rmeddis@0
|
1063 pressure=28*10.^(levelsBinVector/20);
|
rmeddis@0
|
1064 p=1-exp(-stimulusParameters.targetDuration...
|
rmeddis@0
|
1065 *(statsModel.rareEvenGain...
|
rmeddis@0
|
1066 * pressure-statsModel.rareEventVmin));
|
rmeddis@0
|
1067 p(p<0)=0;
|
rmeddis@0
|
1068 if experiment.psyFunSlope<0, p=1-p;end
|
rmeddis@0
|
1069 hold on, plot(levelsBinVector, p,'g')
|
rmeddis@0
|
1070 end %(estMethod)
|
rmeddis@0
|
1071 end
|
rmeddis@28
|
1072
|
rmeddis@0
|
1073 otherwise % 2A2IFC
|
rmeddis@0
|
1074 message3= ...
|
rmeddis@0
|
1075 ([ 'peaks=' num2str(withinRuns.peaks) ...
|
rmeddis@0
|
1076 'troughs=' num2str(withinRuns.troughs)]);
|
rmeddis@0
|
1077 ylimRM([-0.1 1.1]) % 0=no / 1=yes
|
rmeddis@0
|
1078 set(gca,'ytick',[0 1], 'yTickLabel', {'no';'yes'})
|
rmeddis@0
|
1079 ylabel('psychometric function'), xlabel('target level')
|
rmeddis@0
|
1080 if length(levelsBinVector)>1
|
rmeddis@0
|
1081 xlim([ min(levelsBinVector) max(levelsBinVector)])
|
rmeddis@0
|
1082 xlim([ 0 100])
|
rmeddis@0
|
1083 end
|
rmeddis@0
|
1084 end
|
rmeddis@0
|
1085
|
rmeddis@0
|
1086 % command window summary
|
rmeddis@0
|
1087 % Accumulate things to say in the message window
|
rmeddis@0
|
1088 message1= (['responses: ' num2str(withinRuns.responseList,'%9.0f')]);
|
rmeddis@0
|
1089 switch experiment.paradigm
|
rmeddis@0
|
1090 % more decimal places needed on GUI
|
rmeddis@0
|
1091 case { 'gapDetection', 'frequencyDiscrimination', 'forwardMaskingD'}
|
rmeddis@0
|
1092 message2= ([stimulusParameters.WRVname ...
|
rmeddis@0
|
1093 ': ' num2str(withinRuns.levelList,'%7.3f')]);
|
rmeddis@0
|
1094 message3= (['Thresh (logistic mean): ' ...
|
rmeddis@0
|
1095 num2str(withinRuns.thresholdEstimateTrack,'%7.3f')]);
|
rmeddis@0
|
1096 otherwise
|
rmeddis@0
|
1097 message2= ([stimulusParameters.WRVname ': ' ...
|
rmeddis@0
|
1098 num2str(withinRuns.levelList,'%7.1f')]);
|
rmeddis@0
|
1099 message3= (['Thresh (logistic mean): ' ...
|
rmeddis@0
|
1100 num2str(withinRuns.thresholdEstimateTrack,'%7.1f')]);
|
rmeddis@0
|
1101 end
|
rmeddis@0
|
1102
|
rmeddis@0
|
1103 addToMsg(str2mat(message1, message2, message3), 0)
|
rmeddis@0
|
1104
|
rmeddis@0
|
1105 % -----------------------------------------------------runCompleted
|
rmeddis@0
|
1106 function runCompleted(handles)
|
rmeddis@0
|
1107 % Used at the end of each run
|
rmeddis@0
|
1108 global experiment stimulusParameters betweenRuns withinRuns
|
rmeddis@0
|
1109 global rareEvent expGUIhandles
|
rmeddis@0
|
1110 % disp('run completed')
|
rmeddis@0
|
1111
|
rmeddis@0
|
1112 experiment.status='runCompleted';
|
rmeddis@28
|
1113 % quick update after final trial just to make sure
|
rmeddis@0
|
1114 plotProgressThisTrial(handles)
|
rmeddis@0
|
1115
|
rmeddis@0
|
1116 switch experiment.ear
|
rmeddis@0
|
1117 case {'statsModelLogistic', 'statsModelRareEvent','MAPmodel', ...
|
rmeddis@0
|
1118 'MAPmodelMultiCh','MAPmodelSingleCh', 'MAPmodelListen'}
|
rmeddis@0
|
1119 % no changes required if model used
|
rmeddis@0
|
1120 otherwise
|
rmeddis@0
|
1121 set(handles.frame1,'visible','off')
|
rmeddis@0
|
1122 set(handles.pushbuttoNotSure,'visible','off')
|
rmeddis@0
|
1123 set(handles.pushbuttonWrongButton,'visible','off')
|
rmeddis@0
|
1124 set(handles.pushbutton3,'visible','off')
|
rmeddis@0
|
1125 set(handles.pushbutton2,'visible','off')
|
rmeddis@0
|
1126 set(handles.pushbutton1,'visible','off')
|
rmeddis@0
|
1127 set(handles.pushbutton0,'visible','off')
|
rmeddis@0
|
1128 set(handles.pushbuttonGO,'visible','off')
|
rmeddis@0
|
1129 end
|
rmeddis@0
|
1130
|
rmeddis@0
|
1131 if isnan(withinRuns.forceThreshold)
|
rmeddis@0
|
1132 % the experiment has been aborted for some reason
|
rmeddis@0
|
1133 threshold=withinRuns.forceThreshold;
|
rmeddis@0
|
1134 stdev=NaN;
|
rmeddis@0
|
1135 logistic.bestK=NaN;
|
rmeddis@0
|
1136 logistic.bestThreshold=NaN;
|
rmeddis@0
|
1137 medianThreshold=NaN;
|
rmeddis@0
|
1138 meanThreshold=NaN;
|
rmeddis@0
|
1139 else
|
rmeddis@0
|
1140 % use only phase 2 levels and responses for calculating thresholds
|
rmeddis@0
|
1141 withinRuns.levelsPhaseTwo=...
|
rmeddis@0
|
1142 withinRuns.levelList(withinRuns.beginningOfPhase2:end);
|
rmeddis@0
|
1143 withinRuns.responsesPhaseTwo=...
|
rmeddis@0
|
1144 withinRuns.responseList(withinRuns.beginningOfPhase2:end);
|
rmeddis@0
|
1145 [psy, levelsPhaseTwoBinVector, logistic, rareEvent]= ...
|
rmeddis@0
|
1146 bestFitPsychometicFunctions...
|
rmeddis@0
|
1147 (withinRuns.levelsPhaseTwo, withinRuns.responsesPhaseTwo);
|
rmeddis@28
|
1148
|
rmeddis@0
|
1149 % plot final psychometric function
|
rmeddis@0
|
1150 axes(expGUIhandles.axes5),cla
|
rmeddis@0
|
1151 hold on, plot(rareEvent.predictionLevels, rareEvent.predictionsRE, 'k')
|
rmeddis@0
|
1152 hold on, plot(logistic.predictionLevels, logistic.predictionsLOG, 'r')
|
rmeddis@0
|
1153 % organise data as psychometric function
|
rmeddis@0
|
1154 [psy, levelsBinVector, binFrequencies]= ...
|
rmeddis@0
|
1155 psychometricFunction(withinRuns.levelsPhaseTwo,...
|
rmeddis@0
|
1156 withinRuns.responsesPhaseTwo, experiment.psyBinWidth);
|
rmeddis@0
|
1157 % point by point with circles of appropiate weighted size
|
rmeddis@0
|
1158 hold on,
|
rmeddis@0
|
1159 for i=1:length(psy)
|
rmeddis@0
|
1160 plot(levelsBinVector(i), psy(i), 'ro', ...
|
rmeddis@0
|
1161 'markersize', 50*binFrequencies(i)/sum(binFrequencies))
|
rmeddis@0
|
1162 end
|
rmeddis@0
|
1163
|
rmeddis@0
|
1164 % experimental
|
rmeddis@0
|
1165 medianThreshold=median(withinRuns.levelsPhaseTwo);
|
rmeddis@0
|
1166 warning off
|
rmeddis@0
|
1167 meanThreshold=mean(withinRuns.levelsPhaseTwo);
|
rmeddis@28
|
1168
|
rmeddis@0
|
1169 % identify the current threshold estimate
|
rmeddis@0
|
1170 switch experiment.paradigm
|
rmeddis@0
|
1171 case 'discomfort'
|
rmeddis@0
|
1172 % most recent value (not truely a mean value)
|
rmeddis@0
|
1173 threshold=withinRuns.levelList(end);
|
rmeddis@28
|
1174 stdev=NaN;
|
rmeddis@0
|
1175 otherwise
|
rmeddis@0
|
1176 switch experiment.threshEstMethod
|
rmeddis@0
|
1177 case {'MaxLikelihood', 'oneIntervalUpDown'}
|
rmeddis@0
|
1178 % last value in the list
|
rmeddis@28
|
1179 % threshold=withinRuns.meanEstTrack(end);
|
rmeddis@0
|
1180 threshold=withinRuns.thresholdEstimateTrack(end);
|
rmeddis@0
|
1181 stdev=NaN;
|
rmeddis@28
|
1182
|
rmeddis@0
|
1183 case {'2I2AFC++', '2I2AFC+++'}
|
rmeddis@0
|
1184 % use peaks and troughs
|
rmeddis@0
|
1185 try % there may not be enough values to use
|
rmeddis@0
|
1186 peaksUsed=experiment.peaksUsed;
|
rmeddis@0
|
1187 threshold=...
|
rmeddis@0
|
1188 mean(...
|
rmeddis@0
|
1189 [withinRuns.peaks(end-peaksUsed+1:end) ...
|
rmeddis@0
|
1190 withinRuns.troughs(end-peaksUsed+1:end)]);
|
rmeddis@0
|
1191 stdev=...
|
rmeddis@0
|
1192 std([withinRuns.peaks(end-peaksUsed +1:end) ...
|
rmeddis@0
|
1193 withinRuns.troughs(end-peaksUsed:end)]);
|
rmeddis@0
|
1194 catch
|
rmeddis@0
|
1195 threshold=NaN;
|
rmeddis@0
|
1196 stdev=NaN;
|
rmeddis@0
|
1197 end
|
rmeddis@0
|
1198 end
|
rmeddis@0
|
1199 end
|
rmeddis@0
|
1200 end
|
rmeddis@0
|
1201
|
rmeddis@0
|
1202 % Store thresholds
|
rmeddis@0
|
1203 betweenRuns.thresholds=[betweenRuns.thresholds threshold];
|
rmeddis@0
|
1204 betweenRuns.thresholds_mean=[betweenRuns.thresholds_mean meanThreshold];
|
rmeddis@0
|
1205 betweenRuns.thresholds_median=...
|
rmeddis@0
|
1206 [betweenRuns.thresholds_median medianThreshold];
|
rmeddis@0
|
1207 betweenRuns.forceThresholds=...
|
rmeddis@0
|
1208 [betweenRuns.forceThresholds withinRuns.forceThreshold];
|
rmeddis@0
|
1209
|
rmeddis@0
|
1210 % count observations after the startup phase for record keeping
|
rmeddis@0
|
1211 betweenRuns.observationCount=...
|
rmeddis@0
|
1212 [betweenRuns.observationCount length(withinRuns.levelList)];
|
rmeddis@0
|
1213 betweenRuns.timesOfFirstReversals=...
|
rmeddis@0
|
1214 [betweenRuns.timesOfFirstReversals withinRuns.beginningOfPhase2];
|
rmeddis@0
|
1215 betweenRuns.catchTrials=...
|
rmeddis@0
|
1216 [betweenRuns.catchTrials withinRuns.catchTrialCount];
|
rmeddis@0
|
1217
|
rmeddis@0
|
1218 % add variable length tracks to cell arrays
|
rmeddis@0
|
1219 if withinRuns.beginningOfPhase2>0
|
rmeddis@0
|
1220 betweenRuns.bestThresholdTracks{length(betweenRuns.thresholds)}=...
|
rmeddis@28
|
1221 withinRuns.thresholdEstimateTrack;
|
rmeddis@0
|
1222 betweenRuns.levelTracks{length(betweenRuns.thresholds)}=...
|
rmeddis@0
|
1223 withinRuns.levelList(withinRuns.beginningOfPhase2:end);
|
rmeddis@0
|
1224 betweenRuns.responseTracks{length(betweenRuns.thresholds)}=...
|
rmeddis@0
|
1225 withinRuns.responseList(withinRuns.beginningOfPhase2:end);
|
rmeddis@0
|
1226 else
|
rmeddis@0
|
1227 betweenRuns.bestThresholdTracks{length(betweenRuns.thresholds)}=[];
|
rmeddis@0
|
1228 betweenRuns.levelTracks{length(betweenRuns.thresholds)}=[];
|
rmeddis@0
|
1229 betweenRuns.responseTracks{length(betweenRuns.thresholds)}=[];
|
rmeddis@0
|
1230 end
|
rmeddis@0
|
1231
|
rmeddis@0
|
1232 betweenRuns.bestGain=[betweenRuns.bestGain rareEvent.bestGain];
|
rmeddis@0
|
1233 betweenRuns.bestVMin=[betweenRuns.bestVMin rareEvent.bestVMin];
|
rmeddis@0
|
1234 betweenRuns.bestPaMin=[betweenRuns.bestPaMin rareEvent.bestPaMindB];
|
rmeddis@0
|
1235 betweenRuns.bestLogisticM=...
|
rmeddis@0
|
1236 [betweenRuns.bestLogisticM logistic.bestThreshold];
|
rmeddis@0
|
1237 betweenRuns.bestLogisticK=[betweenRuns.bestLogisticK logistic.bestK];
|
rmeddis@0
|
1238
|
rmeddis@0
|
1239 resultsSoFar=[betweenRuns.var1Sequence(betweenRuns.runNumber)'...
|
rmeddis@0
|
1240 betweenRuns.var2Sequence(betweenRuns.runNumber)'...
|
rmeddis@0
|
1241 betweenRuns.thresholds(betweenRuns.runNumber)'
|
rmeddis@0
|
1242 ];
|
rmeddis@0
|
1243
|
rmeddis@0
|
1244 fprintf('%10.3f \t%10.3f \t%10.1f \n', resultsSoFar')
|
rmeddis@0
|
1245
|
rmeddis@0
|
1246 switch experiment.ear
|
rmeddis@0
|
1247 case {'left', 'right', 'diotic', 'dichoticLeft','dichoticRight'}
|
rmeddis@0
|
1248 disp(['caught out= ' num2str(betweenRuns.caughtOut)])
|
rmeddis@0
|
1249 end
|
rmeddis@0
|
1250
|
rmeddis@0
|
1251 % plot history of thresholds in panel 4
|
rmeddis@0
|
1252 axes(expGUIhandles.axes4), cla
|
rmeddis@0
|
1253 plotColors='rgbmckywrgbmckyw';
|
rmeddis@0
|
1254 for i=1:length(betweenRuns.thresholds)
|
rmeddis@0
|
1255 faceColor=plotColors(floor(i/length(betweenRuns.variableList1)-.01)+1);
|
rmeddis@0
|
1256 switch betweenRuns.variableName1
|
rmeddis@0
|
1257 case {'targetFrequency', 'maskerRelativeFrequency'}
|
rmeddis@0
|
1258 if min(betweenRuns.var1Sequence)>0
|
rmeddis@0
|
1259 semilogx(betweenRuns.var1Sequence(i), ...
|
rmeddis@0
|
1260 betweenRuns.thresholds(i), 'o', ...
|
rmeddis@0
|
1261 'markerSize', 5,'markerFaceColor',faceColor)
|
rmeddis@0
|
1262 else
|
rmeddis@0
|
1263 plot(betweenRuns.var1Sequence(1:betweenRuns.runNumber), ...
|
rmeddis@0
|
1264 betweenRuns.thresholds, 'o', ...
|
rmeddis@0
|
1265 'markerSize', 5,'markerFaceColor',faceColor)
|
rmeddis@0
|
1266 plot(betweenRuns.var1Sequence(i), ...
|
rmeddis@0
|
1267 betweenRuns.thresholds(i), 'o', ...
|
rmeddis@0
|
1268 'markerSize', 5,'markerFaceColor',faceColor)
|
rmeddis@0
|
1269 end
|
rmeddis@0
|
1270 otherwise
|
rmeddis@0
|
1271 plot(betweenRuns.var1Sequence(i), ...
|
rmeddis@0
|
1272 betweenRuns.thresholds(i), 'o', 'markerSize', 5,...
|
rmeddis@0
|
1273 'markerFaceColor',faceColor)
|
rmeddis@0
|
1274 end
|
rmeddis@0
|
1275 hold on
|
rmeddis@0
|
1276 end
|
rmeddis@0
|
1277 xlimRM([ min(betweenRuns.variableList1) max(betweenRuns.variableList1) ])
|
rmeddis@0
|
1278 ylim(stimulusParameters.WRVlimits)
|
rmeddis@38
|
1279 ylabel('thresholds (dB)')
|
rmeddis@0
|
1280 xlabel(betweenRuns.variableName1)
|
rmeddis@0
|
1281 set(gca,'ytick', [0 20 40 60 80 100])
|
rmeddis@0
|
1282 try
|
rmeddis@0
|
1283 % problems if only one x value
|
rmeddis@0
|
1284 set(gca,'XTick', sort(betweenRuns.variableList1))
|
rmeddis@0
|
1285 catch
|
rmeddis@0
|
1286 end
|
rmeddis@0
|
1287 grid on, set(gca,'XMinorGrid', 'off')
|
rmeddis@0
|
1288
|
rmeddis@28
|
1289 % final run?
|
rmeddis@0
|
1290 if betweenRuns.runNumber==length(betweenRuns.var1Sequence)
|
rmeddis@0
|
1291 % yes, end of experiment
|
rmeddis@0
|
1292 fileName=['savedData/' experiment.name experiment.date ...
|
rmeddis@0
|
1293 experiment.paradigm];
|
rmeddis@0
|
1294 % save (fileName, 'experiment', 'stimulusParameters', 'betweenRuns', 'withinRuns', 'variableNames', 'paradigmNames', 'LevittControl')
|
rmeddis@0
|
1295 disp('Experiment completed')
|
rmeddis@28
|
1296
|
rmeddis@0
|
1297 % update subject GUI to acknowledge end of run
|
rmeddis@0
|
1298 subjGUImsg=[{'Experiment completed'}, {' '}, {'Thank you!'}];
|
rmeddis@0
|
1299 set(handles.textMSG,'string', subjGUImsg )
|
rmeddis@0
|
1300 % play 'Tada'
|
rmeddis@0
|
1301 [y,fs,nbits]=wavread('TADA.wav');
|
rmeddis@0
|
1302 musicGain=10^(stimulusParameters.musicLeveldB/20);
|
rmeddis@0
|
1303 y=y*musicGain;
|
rmeddis@35
|
1304 if ispc, wavplay(y/100,fs, 'async'), else sound(y/100,fs, 'async'), end
|
rmeddis@28
|
1305
|
rmeddis@0
|
1306 % update experimenter GUI
|
rmeddis@0
|
1307 addToMsg('Experiment completed.',1)
|
rmeddis@28
|
1308
|
rmeddis@38
|
1309 set(expGUIhandles.pushbuttonSave,'visible','on')
|
rmeddis@0
|
1310 printReport
|
rmeddis@28
|
1311 experiment.status='endOfExperiment';
|
rmeddis@0
|
1312 return
|
rmeddis@0
|
1313 else
|
rmeddis@0
|
1314 % No, hang on.
|
rmeddis@0
|
1315 switch experiment.ear
|
rmeddis@0
|
1316 case {'statsModelLogistic', 'statsModelRareEvent','MAPmodel', ...
|
rmeddis@0
|
1317 'MAPmodelMultiCh','MAPmodelSingleCh', 'MAPmodelListen'}
|
rmeddis@0
|
1318 % no changes required if model used
|
rmeddis@0
|
1319 otherwise
|
rmeddis@0
|
1320 % decrement catchTrialRate towards baseRate
|
rmeddis@0
|
1321 stimulusParameters.catchTrialRate=...
|
rmeddis@0
|
1322 stimulusParameters.catchTrialBaseRate + ...
|
rmeddis@0
|
1323 (stimulusParameters.catchTrialRate...
|
rmeddis@0
|
1324 -stimulusParameters.catchTrialBaseRate)...
|
rmeddis@0
|
1325 *(1-exp(-stimulusParameters.catchTrialTimeConstant));
|
rmeddis@0
|
1326 fprintf('stimulusParameters.catchTrialRate= %6.3f\n', ...
|
rmeddis@0
|
1327 stimulusParameters.catchTrialRate)
|
rmeddis@28
|
1328
|
rmeddis@0
|
1329 % and go again
|
rmeddis@28
|
1330 set(handles.pushbuttonGO,'backgroundcolor','y')
|
rmeddis@0
|
1331 set(handles.frame1,'visible','off')
|
rmeddis@0
|
1332 set(handles.pushbuttonGO,'visible','on')
|
rmeddis@0
|
1333 msg=[{'Ready to start new trial'}, {' '},...
|
rmeddis@0
|
1334 {'Please,click on the GO button'}];
|
rmeddis@0
|
1335 set(handles.textMSG,'string',msg)
|
rmeddis@0
|
1336 end
|
rmeddis@0
|
1337 experiment.status='waitingForGO';
|
rmeddis@0
|
1338 % fprintf('\n')
|
rmeddis@28
|
1339
|
rmeddis@0
|
1340 [y,fs,nbits]=wavread('CHIMES.wav');
|
rmeddis@0
|
1341 musicGain=10^(stimulusParameters.musicLeveldB/20);
|
rmeddis@0
|
1342 y=y*musicGain;
|
rmeddis@35
|
1343 if ispc, wavplay(y/100,fs, 'async'), else sound(y/100,fs, 'async'), end
|
rmeddis@35
|
1344
|
rmeddis@0
|
1345 end
|
rmeddis@0
|
1346
|
rmeddis@0
|
1347 % -----------------------------------------------------MAPmodelRunsGUI
|
rmeddis@0
|
1348 % The computer presses the buttons
|
rmeddis@0
|
1349 function MAPmodelRunsGUI(handles)
|
rmeddis@0
|
1350 global experiment stimulusParameters method expGUIhandles
|
rmeddis@0
|
1351 global AN_IHCsynapseParams
|
rmeddis@0
|
1352 method=[];
|
rmeddis@0
|
1353
|
rmeddis@28
|
1354 while strcmp(experiment.status,'waitingForGO')
|
rmeddis@0
|
1355 % no catch trials for MAP model
|
rmeddis@0
|
1356 experiment.allowCatchTrials=0;
|
rmeddis@28
|
1357
|
rmeddis@28
|
1358 % initiates run and plays first stimulus and it returns
|
rmeddis@0
|
1359 % without waiting for button press
|
rmeddis@28
|
1360 startNewRun(handles)
|
rmeddis@0
|
1361
|
rmeddis@29
|
1362 % show sample Rate on GUI; it must be set in MAPparams ##??
|
rmeddis@0
|
1363 set(expGUIhandles.textsampleRate,'string',...
|
rmeddis@0
|
1364 num2str(stimulusParameters.sampleRate))
|
rmeddis@28
|
1365
|
rmeddis@29
|
1366 if experiment.singleShot % ##??
|
rmeddis@0
|
1367 AN_IHCsynapseParams.plotSynapseContents=1;
|
rmeddis@0
|
1368 else
|
rmeddis@0
|
1369 AN_IHCsynapseParams.plotSynapseContents=0;
|
rmeddis@0
|
1370 end
|
rmeddis@28
|
1371
|
rmeddis@0
|
1372 % continuous loop until the program stops itself
|
rmeddis@0
|
1373 while strcmp(experiment.status,'waitingForResponse')
|
rmeddis@0
|
1374 % NB at this point the stimulus has been played
|
rmeddis@0
|
1375 pause(0.1) % to allow interrupt with CTRL/C
|
rmeddis@28
|
1376
|
rmeddis@0
|
1377 switch experiment.ear
|
rmeddis@0
|
1378 case { 'MAPmodelListen'}
|
rmeddis@28
|
1379 % flash the buttons to show model response
|
rmeddis@28
|
1380 set(handles.pushbutton1,'backgroundcolor','y','visible','on')
|
rmeddis@28
|
1381 set(handles.pushbutton2,'backgroundcolor','y','visible','on')
|
rmeddis@0
|
1382 end
|
rmeddis@0
|
1383
|
rmeddis@28
|
1384 % Analayse the current stimulus using MAP
|
rmeddis@28
|
1385 [modelResponse earObject]= MAPmodel;
|
rmeddis@0
|
1386
|
rmeddis@0
|
1387 if experiment.stop || experiment.singleShot
|
rmeddis@0
|
1388 % trap for single trial or user interrupt using 'stop' button.
|
rmeddis@0
|
1389 experiment.status= 'waitingForStart';
|
rmeddis@33
|
1390 % experiment.stop=0;
|
rmeddis@28
|
1391 errormsg='manually stopped';
|
rmeddis@28
|
1392 addToMsg(errormsg,1)
|
rmeddis@0
|
1393 return
|
rmeddis@0
|
1394 end
|
rmeddis@28
|
1395
|
rmeddis@0
|
1396 switch modelResponse
|
rmeddis@0
|
1397 case 1
|
rmeddis@0
|
1398 % userDoesNotHearTarget(handles)
|
rmeddis@0
|
1399 switch experiment.ear
|
rmeddis@0
|
1400 case {'MAPmodelListen'}
|
rmeddis@0
|
1401 % illuminate appropriate button
|
rmeddis@0
|
1402 set(handles.pushbutton1,...
|
rmeddis@0
|
1403 'backgroundcolor','r','visible','on')
|
rmeddis@0
|
1404 set(handles.pushbutton2,'backgroundcolor','y')
|
rmeddis@0
|
1405 end
|
rmeddis@0
|
1406 userDecides(handles, false)
|
rmeddis@0
|
1407 if experiment.singleShot, return, end
|
rmeddis@0
|
1408
|
rmeddis@0
|
1409 case 2
|
rmeddis@0
|
1410 % userHearsTarget(handles)
|
rmeddis@0
|
1411 switch experiment.ear
|
rmeddis@0
|
1412 case {'MAPmodelListen'}
|
rmeddis@0
|
1413 % illuminate appropriate button (DEMO only)
|
rmeddis@0
|
1414 set(handles.pushbutton2,'backgroundcolor',...
|
rmeddis@0
|
1415 'r','visible','on')
|
rmeddis@0
|
1416 set(handles.pushbutton1,'backgroundcolor','y')
|
rmeddis@0
|
1417 end
|
rmeddis@28
|
1418
|
rmeddis@0
|
1419 switch experiment.paradigm
|
rmeddis@0
|
1420 case 'discomfort'
|
rmeddis@0
|
1421 % always treat discomfort as 'not heard'
|
rmeddis@0
|
1422 userDecides(handles, false)
|
rmeddis@0
|
1423 otherwise
|
rmeddis@0
|
1424 userDecides(handles, true)
|
rmeddis@0
|
1425 end
|
rmeddis@0
|
1426 otherwise
|
rmeddis@0
|
1427 % probably an abort
|
rmeddis@0
|
1428 return
|
rmeddis@0
|
1429 end
|
rmeddis@0
|
1430 end
|
rmeddis@0
|
1431 end
|
rmeddis@0
|
1432
|
rmeddis@28
|
1433 % -------------------------------------------------------MAPmodel
|
rmeddis@28
|
1434 function [modelResponse, MacGregorResponse]=MAPmodel
|
rmeddis@9
|
1435
|
rmeddis@9
|
1436 global experiment stimulusParameters audio withinRuns
|
rmeddis@29
|
1437 % global outerMiddleEarParams DRNLParams AN_IHCsynapseParams
|
rmeddis@38
|
1438 global ICoutput dtSpikes dt savedBFlist ANprobRateOutput expGUIhandles
|
rmeddis@29
|
1439 global paramChanges
|
rmeddis@9
|
1440
|
rmeddis@9
|
1441 savePath=path;
|
rmeddis@24
|
1442 addpath(['..' filesep 'MAP'], ['..' filesep 'utilities'])
|
rmeddis@9
|
1443 modelResponse=[];
|
rmeddis@9
|
1444 MacGregorResponse=[];
|
rmeddis@9
|
1445
|
rmeddis@9
|
1446 % mono only (column vector)
|
rmeddis@9
|
1447 audio=audio(:,1)';
|
rmeddis@9
|
1448
|
rmeddis@9
|
1449 % if stop button pressed earlier
|
rmeddis@28
|
1450 if experiment.stop
|
rmeddis@28
|
1451 errormsg='manually stopped';
|
rmeddis@28
|
1452 addToMsg(errormsg,1)
|
rmeddis@28
|
1453 return
|
rmeddis@28
|
1454 end
|
rmeddis@9
|
1455
|
rmeddis@28
|
1456 % ---------------------------------------------- run Model
|
rmeddis@9
|
1457 MAPparamsName=experiment.name;
|
rmeddis@9
|
1458 showPlotsAndDetails=experiment.MAPplot;
|
rmeddis@29
|
1459
|
rmeddis@29
|
1460 % important buried constant ##??
|
rmeddis@9
|
1461 AN_spikesOrProbability='spikes';
|
rmeddis@32
|
1462 % AN_spikesOrProbability='probability';
|
rmeddis@9
|
1463
|
rmeddis@9
|
1464 % [response, method]=MAPsequenceSeg(audio, method, 1:8);
|
rmeddis@28
|
1465
|
rmeddis@28
|
1466 if sum(strcmp(experiment.ear,{'MAPmodelMultiCh', 'MAPmodelListen'}))
|
rmeddis@28
|
1467 % use BFlist specified in MAPparams file
|
rmeddis@28
|
1468 BFlist= -1;
|
rmeddis@28
|
1469 else
|
rmeddis@28
|
1470 BFlist=stimulusParameters.targetFrequency;
|
rmeddis@28
|
1471 end
|
rmeddis@28
|
1472 paramChanges=get(expGUIhandles.editparamChanges,'string');
|
rmeddis@29
|
1473 % convert from string to a cell array
|
rmeddis@29
|
1474 eval(paramChanges);
|
rmeddis@28
|
1475
|
rmeddis@28
|
1476 MAP1_14(audio, stimulusParameters.sampleRate, BFlist,...
|
rmeddis@28
|
1477 MAPparamsName, AN_spikesOrProbability, paramChanges);
|
rmeddis@28
|
1478
|
rmeddis@9
|
1479 if showPlotsAndDetails
|
rmeddis@23
|
1480 options.printModelParameters=0;
|
rmeddis@9
|
1481 options.showModelOutput=1;
|
rmeddis@9
|
1482 options.printFiringRates=1;
|
rmeddis@9
|
1483 options.showACF=0;
|
rmeddis@38
|
1484 options.showEfferent=0;
|
rmeddis@23
|
1485 options.surfProbability=0;
|
rmeddis@25
|
1486 showMapOptions.surfSpikes=0;
|
rmeddis@23
|
1487 UTIL_showMAP(options)
|
rmeddis@9
|
1488 end
|
rmeddis@9
|
1489
|
rmeddis@9
|
1490 % No response, probably caused by hitting 'stop' button
|
rmeddis@28
|
1491 if strcmp(AN_spikesOrProbability,'spikes') && isempty(ICoutput)
|
rmeddis@28
|
1492 return
|
rmeddis@28
|
1493 end
|
rmeddis@9
|
1494 % ---------------------------------------------------------- end model run
|
rmeddis@9
|
1495
|
rmeddis@28
|
1496 if strcmp(AN_spikesOrProbability,'spikes')
|
rmeddis@28
|
1497 MacGregorResponse= sum(ICoutput,1); % use IC
|
rmeddis@38
|
1498 dt=dtSpikes;
|
rmeddis@28
|
1499 time=dt:dt:dt*length(MacGregorResponse);
|
rmeddis@28
|
1500 else
|
rmeddis@28
|
1501 % for one channel, ANprobResponse=ANprobRateOutput
|
rmeddis@28
|
1502 % for multi channel take strongest in any epoch
|
rmeddis@28
|
1503 nChannels=length(savedBFlist);
|
rmeddis@28
|
1504 % use only HSR fibers
|
rmeddis@28
|
1505 ANprobRateOutput=ANprobRateOutput(end-nChannels+1:end,:);
|
rmeddis@28
|
1506 time=dt:dt:dt*length(ANprobRateOutput);
|
rmeddis@28
|
1507 end
|
rmeddis@9
|
1508
|
rmeddis@28
|
1509 % group delay on unit response - these values are iffy
|
rmeddis@28
|
1510 windowOnsetDelay= 0.004;
|
rmeddis@28
|
1511 windowOffsetDelay= 0.022; % long ringing time
|
rmeddis@9
|
1512
|
rmeddis@38
|
1513 % now find the response of the MacGregor model during
|
rmeddis@38
|
1514 % the target presentation + group delay
|
rmeddis@9
|
1515 switch experiment.threshEstMethod
|
rmeddis@9
|
1516 case {'2I2AFC++', '2I2AFC+++'}
|
rmeddis@28
|
1517 idx= time>stimulusParameters.testTargetBegins+windowOnsetDelay ...
|
rmeddis@28
|
1518 & time<stimulusParameters.testTargetEnds+windowOffsetDelay;
|
rmeddis@9
|
1519 nSpikesTrueWindow=sum(MacGregorResponse(:,idx));
|
rmeddis@28
|
1520 idx=find(time>stimulusParameters.testNonTargetBegins+windowOnsetDelay ...
|
rmeddis@28
|
1521 & time<stimulusParameters.testNonTargetEnds+windowOffsetDelay);
|
rmeddis@28
|
1522 if strcmp(AN_spikesOrProbability,'spikes')
|
rmeddis@28
|
1523 nSpikesFalseWindow=sum(MacGregorResponse(:,idx));
|
rmeddis@28
|
1524 else
|
rmeddis@28
|
1525 nSpikesDuringTarget=mean(ANprobRateOutput(end,idx));
|
rmeddis@28
|
1526 % compare with spontaneous rate
|
rmeddis@28
|
1527 if nSpikesDuringTarget>ANprobRateOutput(end,1)+10
|
rmeddis@28
|
1528 nSpikesDuringTarget=1; % i.e. at leastone MacG spike
|
rmeddis@28
|
1529 else
|
rmeddis@28
|
1530 nSpikesDuringTarget=0;
|
rmeddis@28
|
1531 end
|
rmeddis@28
|
1532 end
|
rmeddis@9
|
1533 % nSpikesDuringTarget is +ve when more spikes are found
|
rmeddis@9
|
1534 % in the target window
|
rmeddis@9
|
1535 difference= nSpikesTrueWindow-nSpikesFalseWindow;
|
rmeddis@9
|
1536
|
rmeddis@9
|
1537 if difference>0
|
rmeddis@9
|
1538 % hit
|
rmeddis@9
|
1539 nSpikesDuringTarget=experiment.MacGThreshold+1;
|
rmeddis@9
|
1540 elseif difference<0
|
rmeddis@9
|
1541 % miss (wrong choice)
|
rmeddis@9
|
1542 nSpikesDuringTarget=experiment.MacGThreshold-1;
|
rmeddis@9
|
1543 else
|
rmeddis@9
|
1544 if rand>0.5
|
rmeddis@9
|
1545 % hit (random choice)
|
rmeddis@9
|
1546 nSpikesDuringTarget=experiment.MacGThreshold+1;
|
rmeddis@9
|
1547 else
|
rmeddis@9
|
1548 % miss (random choice)
|
rmeddis@9
|
1549 nSpikesDuringTarget=experiment.MacGThreshold-1;
|
rmeddis@9
|
1550 end
|
rmeddis@9
|
1551 end
|
rmeddis@9
|
1552 disp(['level target dummy decision: ' ...
|
rmeddis@9
|
1553 num2str([withinRuns.variableValue nSpikesTrueWindow ...
|
rmeddis@9
|
1554 nSpikesFalseWindow nSpikesDuringTarget], '%4.0f') ] )
|
rmeddis@28
|
1555 otherwise
|
rmeddis@38
|
1556
|
rmeddis@38
|
1557 % single interval up/down
|
rmeddis@28
|
1558 idx=find(time>stimulusParameters.testTargetBegins +windowOnsetDelay...
|
rmeddis@28
|
1559 & time<stimulusParameters.testTargetEnds+windowOffsetDelay);
|
rmeddis@28
|
1560 if strcmp(AN_spikesOrProbability,'spikes')
|
rmeddis@28
|
1561 nSpikesDuringTarget=sum(MacGregorResponse(:,idx));
|
rmeddis@28
|
1562 timeX=time(idx);
|
rmeddis@28
|
1563 else
|
rmeddis@28
|
1564 % probability, use channel with the highest average rate
|
rmeddis@28
|
1565 nSpikesDuringTarget=max(mean(ANprobRateOutput(:,idx),2));
|
rmeddis@28
|
1566 if nSpikesDuringTarget>ANprobRateOutput(end,1)+10
|
rmeddis@28
|
1567 nSpikesDuringTarget=1;
|
rmeddis@28
|
1568 else
|
rmeddis@28
|
1569 nSpikesDuringTarget=0;
|
rmeddis@28
|
1570 end
|
rmeddis@9
|
1571
|
rmeddis@28
|
1572 end
|
rmeddis@9
|
1573 end
|
rmeddis@9
|
1574
|
rmeddis@9
|
1575 if experiment.MAPplot
|
rmeddis@9
|
1576 % add vertical lines to indicate target region
|
rmeddis@9
|
1577 figure(99), subplot(6,1,6)
|
rmeddis@9
|
1578 hold on
|
rmeddis@9
|
1579 yL=get(gca,'YLim');
|
rmeddis@28
|
1580 plot([stimulusParameters.testTargetBegins + windowOnsetDelay ...
|
rmeddis@28
|
1581 stimulusParameters.testTargetBegins + windowOnsetDelay],yL,'r')
|
rmeddis@28
|
1582 plot([stimulusParameters.testTargetEnds + windowOffsetDelay ...
|
rmeddis@28
|
1583 stimulusParameters.testTargetEnds + windowOffsetDelay],yL,'r')
|
rmeddis@9
|
1584 end
|
rmeddis@9
|
1585
|
rmeddis@9
|
1586 % specify unambiguous response
|
rmeddis@28
|
1587 if nSpikesDuringTarget>experiment.MacGThreshold
|
rmeddis@28
|
1588 modelResponse=2; % stimulus detected
|
rmeddis@28
|
1589 else
|
rmeddis@28
|
1590 modelResponse=1; % nothing heard (default)
|
rmeddis@9
|
1591 end
|
rmeddis@9
|
1592
|
rmeddis@9
|
1593 path(savePath)
|
rmeddis@0
|
1594
|
rmeddis@0
|
1595 % -----------------------------------------------------statsModelRunsGUI
|
rmeddis@0
|
1596 % The computer presses the buttons
|
rmeddis@0
|
1597 function statsModelRunsGUI(handles)
|
rmeddis@0
|
1598 % Decision are made at random using a prescribe statistical function
|
rmeddis@0
|
1599 % to set probabilities as a function of signal level.
|
rmeddis@0
|
1600 global experiment
|
rmeddis@0
|
1601
|
rmeddis@0
|
1602 experiment.allowCatchTrials=0;
|
rmeddis@0
|
1603
|
rmeddis@0
|
1604 while strcmp(experiment.status,'waitingForGO')
|
rmeddis@0
|
1605 % i.e. waiting for new run
|
rmeddis@0
|
1606 if experiment.stop
|
rmeddis@0
|
1607 % user has requested an abort
|
rmeddis@0
|
1608 experiment.status= 'waitingForStart';
|
rmeddis@0
|
1609 addToMsg('manually stopped',1)
|
rmeddis@0
|
1610 return
|
rmeddis@0
|
1611 end
|
rmeddis@28
|
1612
|
rmeddis@0
|
1613 % initiates run and plays first stimulus and it returns
|
rmeddis@0
|
1614 % without waiting for button press
|
rmeddis@0
|
1615 % NB stimulus is not actually generated (for speed)
|
rmeddis@0
|
1616 startNewRun(handles)
|
rmeddis@28
|
1617
|
rmeddis@0
|
1618 while strcmp(experiment.status,'waitingForResponse')
|
rmeddis@0
|
1619 % create artificial response here
|
rmeddis@0
|
1620 modelResponse=statsModelGetResponse;
|
rmeddis@0
|
1621 switch modelResponse
|
rmeddis@0
|
1622 case 1
|
rmeddis@0
|
1623 % userDoesNotHearTarget(handles)
|
rmeddis@0
|
1624 userDecides(handles, false)
|
rmeddis@0
|
1625 case 2
|
rmeddis@0
|
1626 % userHearsTarget(handles)
|
rmeddis@0
|
1627 userDecides(handles, true)
|
rmeddis@0
|
1628 end
|
rmeddis@0
|
1629 end
|
rmeddis@0
|
1630 end
|
rmeddis@0
|
1631
|
rmeddis@0
|
1632 % -----------------------------------------------------statsModelGetResponse
|
rmeddis@0
|
1633 function modelResponse=statsModelGetResponse(handles)
|
rmeddis@0
|
1634 global experiment withinRuns statsModel stimulusParameters
|
rmeddis@0
|
1635 % use the generating function to decide if a detection occurs or not
|
rmeddis@0
|
1636
|
rmeddis@0
|
1637 % pause(0.1) % to allow stopping with CTRL/C but slows things down
|
rmeddis@0
|
1638
|
rmeddis@0
|
1639 % first compute the probability that a detection occurs
|
rmeddis@0
|
1640 switch experiment.ear
|
rmeddis@0
|
1641 case {'statsModelLogistic'}
|
rmeddis@0
|
1642 prob= 1./(1+exp(-statsModel.logisticSlope.*(withinRuns.variableValue-statsModel.logisticMean)));
|
rmeddis@0
|
1643 % if experiment.psyFunSlope<0,
|
rmeddis@0
|
1644 % prob=1-prob;
|
rmeddis@0
|
1645 % end
|
rmeddis@28
|
1646
|
rmeddis@0
|
1647 case 'statsModelRareEvent'
|
rmeddis@0
|
1648 if experiment.psyFunSlope<0
|
rmeddis@0
|
1649 addToMsg('statsModelRareEvent cannot be used with negative slope',0)
|
rmeddis@0
|
1650 error('statsModelRareEvent cannot be used with negative slope')
|
rmeddis@0
|
1651 end
|
rmeddis@28
|
1652
|
rmeddis@0
|
1653 % standard formula is prob = 1 – exp(-d (g P – A))
|
rmeddis@0
|
1654 % here A->A; To find Pmin use A/gain
|
rmeddis@0
|
1655 pressure=28*10^(withinRuns.variableValue/20);
|
rmeddis@0
|
1656 gain=statsModel.rareEvenGain;
|
rmeddis@0
|
1657 A=statsModel.rareEventVmin;
|
rmeddis@0
|
1658 d=stimulusParameters.targetDuration;
|
rmeddis@0
|
1659 gP_Vmin=gain*pressure-A;
|
rmeddis@0
|
1660 if gP_Vmin>0
|
rmeddis@0
|
1661 prob=1-exp(-d*(gP_Vmin));
|
rmeddis@0
|
1662 else
|
rmeddis@0
|
1663 prob=0;
|
rmeddis@0
|
1664 end
|
rmeddis@0
|
1665 end
|
rmeddis@0
|
1666
|
rmeddis@0
|
1667 % Use the probability to choose whether or not a detection has occurred
|
rmeddis@0
|
1668 switch experiment.threshEstMethod
|
rmeddis@0
|
1669 case {'MaxLikelihood', 'oneIntervalUpDown'}
|
rmeddis@0
|
1670 if rand<prob
|
rmeddis@0
|
1671 modelResponse=2; %bingo
|
rmeddis@0
|
1672 else
|
rmeddis@0
|
1673 modelResponse=1; %nothing heard
|
rmeddis@0
|
1674 end
|
rmeddis@28
|
1675
|
rmeddis@0
|
1676 case {'2I2AFC++', '2I2AFC+++'}
|
rmeddis@0
|
1677 if rand<prob
|
rmeddis@0
|
1678 modelResponse=2; %bingo
|
rmeddis@0
|
1679 else %if the stimulus is not audible, take a 50:50 chance of getting it right
|
rmeddis@0
|
1680 if rand<0.5
|
rmeddis@0
|
1681 modelResponse=2; %bingo
|
rmeddis@0
|
1682 else
|
rmeddis@0
|
1683 modelResponse=1; %nothing heard
|
rmeddis@0
|
1684 end
|
rmeddis@0
|
1685 end
|
rmeddis@0
|
1686 end
|
rmeddis@0
|
1687
|
rmeddis@0
|
1688
|
rmeddis@0
|
1689 % ------------------------------------------------------- printTabTable
|
rmeddis@0
|
1690 function printTabTable(M, headers)
|
rmeddis@0
|
1691 % printTabTable prints a matrix as a table with tabs
|
rmeddis@0
|
1692 %headers are optional
|
rmeddis@0
|
1693 %headers=strvcat('firstname', 'secondname')
|
rmeddis@0
|
1694 % printTabTable([1 2; 3 4],strvcat('a1','a2'));
|
rmeddis@0
|
1695
|
rmeddis@0
|
1696 if nargin>1
|
rmeddis@0
|
1697 [r c]=size(headers);
|
rmeddis@0
|
1698 for no=1:r
|
rmeddis@0
|
1699 fprintf('%s\t',headers(no,:))
|
rmeddis@0
|
1700 end
|
rmeddis@0
|
1701 fprintf('\n')
|
rmeddis@0
|
1702 end
|
rmeddis@0
|
1703
|
rmeddis@0
|
1704 [r c]=size(M);
|
rmeddis@0
|
1705
|
rmeddis@0
|
1706 for row=1:r
|
rmeddis@0
|
1707 for col=1:c
|
rmeddis@0
|
1708 if row==1 && col==1 && M(1,1)==-1000
|
rmeddis@0
|
1709 % Print nothing (tab follows below)
|
rmeddis@0
|
1710 else
|
rmeddis@0
|
1711 fprintf('%s',num2str(M(row,col)))
|
rmeddis@0
|
1712 end
|
rmeddis@0
|
1713 if col<c
|
rmeddis@0
|
1714 fprintf('\t')
|
rmeddis@0
|
1715 end
|
rmeddis@0
|
1716 end
|
rmeddis@0
|
1717 fprintf('\n')
|
rmeddis@0
|
1718 end
|
rmeddis@0
|
1719
|
rmeddis@0
|
1720 % ------------------------------------------------------- xlimRM
|
rmeddis@0
|
1721 function xlimRM(x)
|
rmeddis@0
|
1722 try
|
rmeddis@0
|
1723 xlim([x(1) x(2)])
|
rmeddis@0
|
1724 catch
|
rmeddis@0
|
1725 end
|
rmeddis@0
|
1726
|
rmeddis@0
|
1727 % ------------------------------------------------------- ylimRM
|
rmeddis@0
|
1728 function ylimRM(x)
|
rmeddis@0
|
1729 try
|
rmeddis@0
|
1730 ylim([x(1) x(2)])
|
rmeddis@0
|
1731 catch
|
rmeddis@0
|
1732 end
|
rmeddis@0
|
1733
|
rmeddis@0
|
1734
|
rmeddis@0
|
1735 function editdigitInput_CreateFcn(hObject, eventdata, handles)
|
rmeddis@0
|
1736
|
rmeddis@0
|
1737 % Hint: edit controls usually have a white background on Windows.
|
rmeddis@0
|
1738 % See ISPC and COMPUTER.
|
rmeddis@0
|
1739 if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor'))
|
rmeddis@0
|
1740 set(hObject,'BackgroundColor','white');
|
rmeddis@0
|
1741 end
|
rmeddis@0
|
1742
|
rmeddis@0
|
1743
|
rmeddis@0
|
1744 % -----------------------------------------------------buttonBoxIntitialize
|
rmeddis@0
|
1745 function buttonBoxIntitialize
|
rmeddis@0
|
1746 % initialize button box
|
rmeddis@0
|
1747 global serobj
|
rmeddis@0
|
1748 try
|
rmeddis@0
|
1749 fclose(serobj);
|
rmeddis@0
|
1750 catch
|
rmeddis@0
|
1751 end
|
rmeddis@0
|
1752
|
rmeddis@0
|
1753 try
|
rmeddis@38
|
1754 % !!! button boxes in booths are connected to COM2. User beware of
|
rmeddis@38
|
1755 % connection port.
|
rmeddis@38
|
1756 serobj = serial('COM2') ; % Creating serial port object
|
rmeddis@0
|
1757 serobj.Baudrate = 9600; % Set the baud rate at the specific value
|
rmeddis@0
|
1758 set(serobj, 'Parity', 'none') ; % Set parity as none
|
rmeddis@0
|
1759 set(serobj, 'Databits', 8) ; % set the number of data bits
|
rmeddis@0
|
1760 set(serobj, 'StopBits', 1) ; % set number of stop bits as 1
|
rmeddis@0
|
1761 set(serobj, 'Terminator', 'CR') ; % set the terminator value to carriage return
|
rmeddis@0
|
1762 set(serobj, 'InputBufferSize', 512) ; % Buffer for read operation, default it is 512
|
rmeddis@0
|
1763 set(serobj,'timeout',10); % 10 sec timeout on button press
|
rmeddis@0
|
1764 set(serobj, 'ReadAsyncMode', 'continuous')
|
rmeddis@0
|
1765 set(serobj, 'BytesAvailableFcn', @buttonBox_callback)
|
rmeddis@0
|
1766 set(serobj, 'BytesAvailableFcnCount', 1)
|
rmeddis@0
|
1767 set(serobj, 'BytesAvailableFcnMode', 'byte')
|
rmeddis@0
|
1768 % set(serobj, 'BreakInterruptFcn', '@buttonBox_Calback')
|
rmeddis@28
|
1769
|
rmeddis@0
|
1770 fopen(serobj);
|
rmeddis@0
|
1771 buttonBoxStatus=get(serobj,'status');
|
rmeddis@0
|
1772 catch
|
rmeddis@0
|
1773 disp('** no button box found - use mouse **')
|
rmeddis@0
|
1774 end
|