Mercurial > hg > map
comparison multithreshold 1.46/subjGUI_MT.m @ 28:02aa9826efe0
mainly multiThreshold
author | Ray Meddis <rmeddis@essex.ac.uk> |
---|---|
date | Fri, 01 Jul 2011 12:59:47 +0100 |
parents | d2c4c07df02c |
children | b51bf546ca3f |
comparison
equal
deleted
inserted
replaced
27:d4a7675b0413 | 28:02aa9826efe0 |
---|---|
33 | 33 |
34 % -----------------------------------------------------initializeGUI | 34 % -----------------------------------------------------initializeGUI |
35 function initializeGUI(handles) | 35 function initializeGUI(handles) |
36 global experiment | 36 global experiment |
37 global subjectGUIHandles expGUIhandles | 37 global subjectGUIHandles expGUIhandles |
38 addpath (['..' filesep 'MAP'], ['..' filesep 'utilities'], ... | 38 addpath (['..' filesep 'MAP'], ['..' filesep 'utilities'], ... |
39 ['..' filesep 'parameterStore'], ['..' filesep 'wavFileStore'],... | 39 ['..' filesep 'parameterStore'], ['..' filesep 'wavFileStore'],... |
40 ['..' filesep 'testPrograms']) | 40 ['..' filesep 'testPrograms']) |
41 | 41 |
42 dbstop if error | 42 dbstop if error |
43 | 43 |
50 'statsModelLogistic','statsModelRareEvent'} | 50 'statsModelLogistic','statsModelRareEvent'} |
51 % subjectGUI not needed for modelling so minimize subject GUI | 51 % subjectGUI not needed for modelling so minimize subject GUI |
52 set(gcf, 'units','pixels') | 52 set(gcf, 'units','pixels') |
53 y=[0*scrnsize(3) 0.8*scrnsize(4) 0.1*scrnsize(3) 0.2*scrnsize(4)]; | 53 y=[0*scrnsize(3) 0.8*scrnsize(4) 0.1*scrnsize(3) 0.2*scrnsize(4)]; |
54 set(gcf,'position',y, 'color',[.871 .961 .996]) | 54 set(gcf,'position',y, 'color',[.871 .961 .996]) |
55 | 55 |
56 | |
57 | |
58 case 'MAPmodelListen', | 56 case 'MAPmodelListen', |
59 % subjectGUI is needed for display purposes. Make it large | 57 % subjectGUI is needed for display purposes. Make it large |
60 set(gcf, 'units','pixels') | 58 set(gcf, 'units','pixels') |
61 y=[.665*scrnsize(3) 0.02*scrnsize(4) ... | 59 y=[.665*scrnsize(3) 0.02*scrnsize(4) ... |
62 0.33*scrnsize(3) 0.5*scrnsize(4)]; % alongside | 60 0.33*scrnsize(3) 0.5*scrnsize(4)]; % alongside |
67 case{'left', 'right','diotic', 'dichoticLeft','dichoticRight'} | 65 case{'left', 'right','diotic', 'dichoticLeft','dichoticRight'} |
68 % Look to see if the button box exists and, if so, initialise it | 66 % Look to see if the button box exists and, if so, initialise it |
69 buttonBoxIntitialize % harmless if no button box attached | 67 buttonBoxIntitialize % harmless if no button box attached |
70 end | 68 end |
71 | 69 |
72 % function varargout = subjGUI_MT(varargin) | |
73 % | |
74 % % Begin initialization code - DO NOT EDIT | |
75 % gui_Singleton = 1; | |
76 % gui_State = struct('gui_Name', mfilename, ... | |
77 % 'gui_Singleton', gui_Singleton, ... | |
78 % 'gui_OpeningFcn', @subjGUI_MT_OpeningFcn, ... | |
79 % 'gui_OutputFcn', @subjGUI_MT_OutputFcn, ... | |
80 % 'gui_LayoutFcn', [] , ... | |
81 % 'gui_Callback', []); | |
82 % if nargin && isstr(varargin{1}) | |
83 % gui_State.gui_Callback = str2func(varargin{1}); | |
84 % end | |
85 % | |
86 % if nargout | |
87 % [varargout{1:nargout}] = gui_mainfcn(gui_State, varargin{:}); | |
88 % else | |
89 % gui_mainfcn(gui_State, varargin{:}); | |
90 % end | |
91 % % End initialization code - DO NOT EDIT | |
92 % | |
93 % % --- Executes just before subjGUI_MT is made visible. | |
94 % function subjGUI_MT_OpeningFcn(hObject, eventdata, handles, varargin) | |
95 % | |
96 % % Choose default command line output for subjGUI_MT | |
97 % handles.output = hObject; | |
98 % initializeGUI(handles) | |
99 % guidata(hObject, handles); | |
100 % | |
101 % function varargout = subjGUI_MT_OutputFcn(hObject, eventdata, handles) | |
102 % % Get default command line output from handles structure | |
103 % varargout{1} = handles.output; | |
104 % | |
105 % % -----------------------------------------------------initializeGUI | |
106 % function initializeGUI(handles) | |
107 % global experiment | |
108 % global subjectGUIHandles expGUIhandles | |
109 % | |
110 % dbstop if error | |
111 % | |
112 % % subjectGUI size and location % [left bottom width height] | |
113 % scrnsize=get(0,'screensize'); | |
114 % set(0, 'units','pixels') | |
115 % switch experiment.ear | |
116 % % use default size unless... | |
117 % case {'MAPmodel', 'MAPmodelMultiCh','MAPmodelSingleCh', ... | |
118 % 'statsModelLogistic','statsModelRareEvent'} | |
119 % % subjectGUI not needed for modelling so minimize subject GUI | |
120 % set(gcf, 'units','pixels') | |
121 % y=[0*scrnsize(3) 0.8*scrnsize(4) 0.1*scrnsize(3) 0.2*scrnsize(4)]; | |
122 % set(gcf,'position',y, 'color',[.871 .961 .996]) | |
123 % | |
124 % case 'MAPmodelListen', | |
125 % % subjectGUI is needed for display purposes. Make it large | |
126 % set(gcf, 'units','pixels') | |
127 % y=[.665*scrnsize(3) 0.02*scrnsize(4) ... | |
128 % 0.33*scrnsize(3) 0.5*scrnsize(4)]; % alongside | |
129 % set(gcf,'position',y, 'color',[.871 .961 .996]) | |
130 % end | |
131 % | |
132 % switch experiment.ear | |
133 % case{'left', 'right','diotic', 'dichoticLeft','dichoticRight'} | |
134 % % Look to see if the button box exists and, if so, initialise it | |
135 % buttonBoxIntitialize % harmless if no button box attached | |
136 % end | |
137 | |
138 % clear display of previous mean values. This is a new measurement series | 70 % clear display of previous mean values. This is a new measurement series |
139 axes(expGUIhandles.axes4), cla | 71 axes(expGUIhandles.axes4), cla |
140 reset (expGUIhandles.axes4) | 72 reset (expGUIhandles.axes4) |
141 | 73 |
142 % handles needed in non-callback routines below | 74 % handles needed in non-callback routines below |
143 subjectGUIHandles=handles; | 75 subjectGUIHandles=handles; |
144 | 76 |
180 case 'discomfort' | 112 case 'discomfort' |
181 set(handles.pushbutton3,'string','') | 113 set(handles.pushbutton3,'string','') |
182 set(handles.pushbutton2,'string','uncomfortable') | 114 set(handles.pushbutton2,'string','uncomfortable') |
183 set(handles.pushbutton1,'string','loud') | 115 set(handles.pushbutton1,'string','loud') |
184 set(handles.pushbutton0,'string','comfortable') | 116 set(handles.pushbutton0,'string','comfortable') |
185 experiment.allowCatchTrials=0; | 117 experiment.allowCatchTrials=0; |
186 end | 118 end |
187 | 119 |
188 % experiment.subjGUIfontSize is set on expGUI | 120 % experiment.subjGUIfontSize is set on expGUI |
189 set(handles.pushbutton3,'FontSize',experiment.subjGUIfontSize) | 121 set(handles.pushbutton3,'FontSize',experiment.subjGUIfontSize) |
190 set(handles.pushbutton2,'FontSize',experiment.subjGUIfontSize) | 122 set(handles.pushbutton2,'FontSize',experiment.subjGUIfontSize) |
207 experiment.stop=0; % status of 'stop' button | 139 experiment.stop=0; % status of 'stop' button |
208 experiment.pleaseRepeat=0; % status of 'repeat' button | 140 experiment.pleaseRepeat=0; % status of 'repeat' button |
209 experiment.buttonBoxStatus='not busy'; | 141 experiment.buttonBoxStatus='not busy'; |
210 | 142 |
211 % date and time and replace ':' with '_' | 143 % date and time and replace ':' with '_' |
212 date=datestr(now);idx=findstr(':',date);date(idx)='_'; | 144 date=datestr(now);idx=findstr(':',date);date(idx)='_'; |
213 experiment.date=date; | 145 experiment.date=date; |
214 timeNow=clock; betweenRuns.timeNow= timeNow; | 146 timeNow=clock; betweenRuns.timeNow= timeNow; |
215 experiment.timeAtStart=[num2str(timeNow(4)) ':' num2str(timeNow(5))]; | 147 experiment.timeAtStart=[num2str(timeNow(4)) ':' num2str(timeNow(5))]; |
216 experiment.minElapsed=0; | 148 experiment.minElapsed=0; |
217 | 149 |
218 % unpack catch trial rates. The rate declines from the start rate | 150 % unpack catch trial rates. The rate declines from the start rate |
219 % to the base rate using a time constant. | 151 % to the base rate using a time constant. |
220 stimulusParameters.catchTrialRate=stimulusParameters.catchTrialRates(1); | 152 stimulusParameters.catchTrialRate=stimulusParameters.catchTrialRates(1); |
221 stimulusParameters.catchTrialBaseRate=... | 153 stimulusParameters.catchTrialBaseRate=... |
222 stimulusParameters.catchTrialRates(2); | 154 stimulusParameters.catchTrialRates(2); |
223 stimulusParameters.catchTrialTimeConstant=... | 155 stimulusParameters.catchTrialTimeConstant=... |
263 eval(['variableList1=stimulusParameters.' betweenRuns.variableName1 ';']); | 195 eval(['variableList1=stimulusParameters.' betweenRuns.variableName1 ';']); |
264 eval(['variableList2=stimulusParameters.' betweenRuns.variableName2 ';']); | 196 eval(['variableList2=stimulusParameters.' betweenRuns.variableName2 ';']); |
265 nVar1=length(variableList1); | 197 nVar1=length(variableList1); |
266 nVar2=length(variableList2); | 198 nVar2=length(variableList2); |
267 | 199 |
268 % Create two sequence vectors to represent the sequence of var1 and var2 | 200 % Create two sequence vectors to represent the sequence of var1 and var2 |
269 % values. 'var1' changes most rapidly. | 201 % values. 'var1' changes most rapidly. |
270 switch betweenRuns.randomizeSequence | 202 switch betweenRuns.randomizeSequence |
271 case 'fixed sequence' | 203 case 'fixed sequence' |
272 var1Sequence=repmat(betweenRuns.variableList1, 1,nVar2); | 204 var1Sequence=repmat(betweenRuns.variableList1, 1,nVar2); |
273 var2Sequence=reshape(repmat(betweenRuns.variableList2, ... | 205 var2Sequence=reshape(repmat(betweenRuns.variableList2, ... |
287 1,nVar1*nVar2); | 219 1,nVar1*nVar2); |
288 ranNums=rand(1, nVar1*nVar2); | 220 ranNums=rand(1, nVar1*nVar2); |
289 [x idx]=sort(ranNums); | 221 [x idx]=sort(ranNums); |
290 var1Sequence=var1Sequence(idx); | 222 var1Sequence=var1Sequence(idx); |
291 var2Sequence=var2Sequence(idx); | 223 var2Sequence=var2Sequence(idx); |
292 % there should be one start value for every combination | 224 % there should be one start value for every combination |
293 % of var1/ var2. In principle this allows these values to be | 225 % of var1/ var2. In principle this allows these values to be |
294 % programmed. Not currently in use. | 226 % programmed. Not currently in use. |
295 stimulusParameters.WRVstartValues=... | 227 stimulusParameters.WRVstartValues=... |
296 stimulusParameters.WRVstartValues(idx); | 228 stimulusParameters.WRVstartValues(idx); |
297 end | 229 end |
298 betweenRuns.var1Sequence=var1Sequence; | 230 betweenRuns.var1Sequence=var1Sequence; |
302 betweenRuns.caughtOut=zeros(1,length(var1Sequence)); | 234 betweenRuns.caughtOut=zeros(1,length(var1Sequence)); |
303 | 235 |
304 disp('planned sequence:') | 236 disp('planned sequence:') |
305 if min(var1Sequence)>1 | 237 if min(var1Sequence)>1 |
306 % use decidaml places only if necessary | 238 % use decidaml places only if necessary |
307 disp([betweenRuns.variableName1 ': ' num2str(var1Sequence,'%6.0f') ]) | 239 disp([betweenRuns.variableName1 ': ' num2str(var1Sequence,'%6.0f') ]) |
308 else | 240 else |
309 disp([betweenRuns.variableName1 ': ' num2str(var1Sequence,'%8.3f') ]) | 241 disp([betweenRuns.variableName1 ': ' num2str(var1Sequence,'%8.3f') ]) |
310 end | 242 end |
311 if min(var1Sequence)>1 | 243 if min(var1Sequence)>1 |
312 disp([betweenRuns.variableName2 ': ' num2str(var2Sequence,'%6.0f') ]) | 244 disp([betweenRuns.variableName2 ': ' num2str(var2Sequence,'%6.0f') ]) |
313 else | 245 else |
314 disp([betweenRuns.variableName2 ': ' num2str(var2Sequence,'%8.3f') ]) | 246 disp([betweenRuns.variableName2 ': ' num2str(var2Sequence,'%8.3f') ]) |
315 end | 247 end |
316 | 248 |
317 fprintf('\nvariable1 \t variable2\t \n') | 249 fprintf('\nvariable1 \t variable2\t \n') |
318 fprintf('%s \t %s\t Threshold \n',betweenRuns.variableName1,... | 250 fprintf('%s \t %s\t Threshold \n',betweenRuns.variableName1,... |
319 betweenRuns.variableName2) | 251 betweenRuns.variableName2) |
329 set(handles.pushbuttonGO,'visible','on') | 261 set(handles.pushbuttonGO,'visible','on') |
330 set(handles.frame1,'visible','off') | 262 set(handles.frame1,'visible','off') |
331 set(handles.textMSG,'backgroundcolor', 'w') | 263 set(handles.textMSG,'backgroundcolor', 'w') |
332 msg=[{'Ready to start new Experiment'}, {' '}, {'Please, click on the GO button'}]; | 264 msg=[{'Ready to start new Experiment'}, {' '}, {'Please, click on the GO button'}]; |
333 set(handles.textMSG,'string', msg) | 265 set(handles.textMSG,'string', msg) |
334 | 266 |
335 set(handles.pushbuttoNotSure,'visible','off') | 267 set(handles.pushbuttoNotSure,'visible','off') |
336 set(handles.pushbuttonWrongButton,'visible','off') | 268 set(handles.pushbuttonWrongButton,'visible','off') |
337 set(handles.pushbutton3,'visible','off') | 269 set(handles.pushbutton3,'visible','off') |
338 set(handles.pushbutton2,'visible','off') | 270 set(handles.pushbutton2,'visible','off') |
339 set(handles.pushbutton1,'visible','off') | 271 set(handles.pushbutton1,'visible','off') |
348 switch experiment.ear | 280 switch experiment.ear |
349 case {'MAPmodel','MAPmodelMultiCh','MAPmodelSingleCh','MAPmodelListen'} % MAP model is now the subject | 281 case {'MAPmodel','MAPmodelMultiCh','MAPmodelSingleCh','MAPmodelListen'} % MAP model is now the subject |
350 stimulusParameters.calibrationdB=0; % Pascals required! | 282 stimulusParameters.calibrationdB=0; % Pascals required! |
351 MAPmodelRunsGUI(handles) | 283 MAPmodelRunsGUI(handles) |
352 % model is now the subject | 284 % model is now the subject |
353 case {'statsModelLogistic', 'statsModelRareEvent'} | 285 case {'statsModelLogistic', 'statsModelRareEvent'} |
354 % no catch trials for the statistical model | 286 % no catch trials for the statistical model |
355 stimulusParameters.catchTrialBaseRate=0; | 287 stimulusParameters.catchTrialBaseRate=0; |
356 stimulusParameters.catchTrialRate=0; | 288 stimulusParameters.catchTrialRate=0; |
357 statsModelRunsGUI(handles) | 289 statsModelRunsGUI(handles) |
358 otherwise | 290 otherwise |
359 %manual operation; wait for user to click on 'GO' | 291 %manual operation; wait for user to click on 'GO' |
360 end | 292 end |
370 % Under manual control this is achieved by hitting the GO button | 302 % Under manual control this is achieved by hitting the GO button |
371 % either via the button box or a mouse click | 303 % either via the button box or a mouse click |
372 % MAP and randomization methods call this too | 304 % MAP and randomization methods call this too |
373 | 305 |
374 global experiment stimulusParameters betweenRuns withinRuns expGUIhandles | 306 global experiment stimulusParameters betweenRuns withinRuns expGUIhandles |
375 global LevittControl rareEvent | 307 global LevittControl rareEvent errormsg |
376 | 308 |
377 figure(handles.figure1) % guarantee subject GUI visibility | 309 figure(handles.figure1) % guarantee subject GUI visibility |
378 | 310 |
379 % ignore call if program is not ready | 311 % ignore call if program is not ready |
380 if ~strcmp(experiment.status,'waitingForGO'), return, end | 312 if ~strcmp(experiment.status,'waitingForGO'), return, end |
445 pause(experiment.clickToStimulusPause) | 377 pause(experiment.clickToStimulusPause) |
446 end | 378 end |
447 | 379 |
448 errormsg=nextStimulus(handles); % get the show on the road | 380 errormsg=nextStimulus(handles); % get the show on the road |
449 | 381 |
450 % switch experiment.paradigm | |
451 % case 'SRT' | |
452 % set(handles.editdigitInput,'visible','on') | |
453 % uicontrol(handles.editdigitInput) | |
454 % end | |
455 | |
456 % terminate if there is any kind of problem | 382 % terminate if there is any kind of problem |
457 if ~isempty(errormsg) | 383 if ~isempty(errormsg) |
458 % e.g. limits exceeded, clipping | 384 % e.g. limits exceeded, clipping |
459 disp(errormsg) | 385 disp(errormsg) |
460 runCompleted(handles) | 386 runCompleted(handles) |
461 return | 387 return |
462 end | 388 end |
463 | |
464 % return route is variable (see intro to this function) | 389 % return route is variable (see intro to this function) |
465 | 390 |
466 % -----------------------------------------------------buttonBox_callback | 391 % -----------------------------------------------------buttonBox_callback |
467 function buttonBox_callback(obj, info) | 392 function buttonBox_callback(obj, info) |
393 % deals with a button press on the button box. | |
394 | |
468 global experiment | 395 global experiment |
469 global serobj | 396 global serobj |
470 global subjectGUIHandles | 397 global subjectGUIHandles |
471 | 398 |
472 % do not accept callback if one is already in process | 399 % do not accept callback if one is already in process |
473 if strcmp(experiment.buttonBoxStatus,'busy') | 400 if strcmp(experiment.buttonBoxStatus,'busy') |
474 disp(' ignored button press') | 401 disp(' ignored button press') |
475 return | 402 return % to quiescent state |
476 end | 403 end |
477 experiment.buttonBoxStatus='busy'; | 404 experiment.buttonBoxStatus='busy'; |
478 % fclose(serobj) | |
479 | 405 |
480 % identify the code of the button pressed | 406 % identify the code of the button pressed |
481 buttonPressedNo = fscanf(serobj,'%c',1); | 407 buttonPressedNo = fscanf(serobj,'%c',1); |
482 | 408 |
483 % This is the map from the button to the Cedrus codes | 409 % This is the map from the button to the Cedrus codes |
484 switch experiment.buttonBoxType | 410 switch experiment.buttonBoxType |
485 case 'horizontal' | 411 case 'horizontal' |
486 pbGo='7'; pb0='1'; | 412 pbGo='7'; pb0='1'; |
487 pb1='2'; pb2='3'; | 413 pb1='2'; pb2='3'; |
488 pbRepeat='4'; pbWrong='6'; pbBlank='5'; | 414 pbRepeat='4'; pbWrong='6'; pbBlank='5'; |
489 case 'square' | 415 case 'square' |
490 pbGo='7'; pb0='1'; | 416 pbGo='7'; pb0='1'; |
491 pb1='3'; pb2='4'; | 417 pb1='3'; pb2='4'; |
492 pbRepeat='8'; pbWrong='6'; pbBlank='5'; | 418 pbRepeat='8'; pbWrong='6'; pbBlank='5'; |
493 end | 419 end |
494 | 420 |
495 % decide what to do | 421 % decide what to do |
496 switch experiment.status | 422 switch experiment.status |
497 case {'presentingStimulus', 'waitingForStart', 'trialcompleted', ... | 423 case {'presentingStimulus', 'waitingForStart', 'trialcompleted', ... |
498 'endOfExperiment'} | 424 'endOfExperiment'} |
499 disp(' ignored button press') | 425 disp(' ignored button press') |
500 | 426 |
501 case 'waitingForGO' | 427 case 'waitingForGO' |
502 % i.e. waiting for new run | 428 % i.e. waiting for new run |
503 if strcmp(buttonPressedNo,pbGo) % only GO button accepted | 429 if strcmp(buttonPressedNo,pbGo) % only GO button accepted |
504 startNewRun(subjectGUIHandles) | 430 startNewRun(subjectGUIHandles) |
505 else | 431 else |
506 disp(' ignored button press') | 432 disp(' ignored button press') |
507 end | 433 end |
508 | 434 |
509 case 'waitingForResponse' | 435 case 'waitingForResponse' |
510 % response to stimuli | 436 % response to stimuli |
511 switch buttonPressedNo | 437 switch buttonPressedNo |
512 case pb0 % button 0 (top left) | 438 case pb0 % button 0 (top left) |
513 switch experiment.threshEstMethod | 439 switch experiment.threshEstMethod |
520 set(subjectGUIHandles.pushbutton0,... | 446 set(subjectGUIHandles.pushbutton0,... |
521 'backgroundcolor',get(0,... | 447 'backgroundcolor',get(0,... |
522 'defaultUicontrolBackgroundColor')) | 448 'defaultUicontrolBackgroundColor')) |
523 userSelects0or1(subjectGUIHandles) | 449 userSelects0or1(subjectGUIHandles) |
524 end | 450 end |
525 | 451 |
526 case pb1 % button 1 (bottom left) | 452 case pb1 % button 1 (bottom left) |
527 switch experiment.threshEstMethod | 453 switch experiment.threshEstMethod |
528 case {'2I2AFC++', '2I2AFC+++'} | 454 case {'2I2AFC++', '2I2AFC+++'} |
529 userSelects0or1(subjectGUIHandles) | 455 userSelects0or1(subjectGUIHandles) |
530 otherwise | 456 otherwise |
531 set(subjectGUIHandles.pushbutton1,... | 457 set(subjectGUIHandles.pushbutton1,... |
532 'backgroundcolor','r') | 458 'backgroundcolor','r') |
533 pause(.1) | 459 pause(.1) |
534 set(subjectGUIHandles.pushbutton1,... | 460 set(subjectGUIHandles.pushbutton1,... |
535 'backgroundcolor',get(0,... | 461 'backgroundcolor',get(0,... |
536 'defaultUicontrolBackgroundColor')) | 462 'defaultUicontrolBackgroundColor')) |
537 userSelects0or1(subjectGUIHandles) | 463 userSelects0or1(subjectGUIHandles) |
538 end | 464 end |
539 | 465 |
540 case pb2 % button 2 (bottom right) | 466 case pb2 % button 2 (bottom right) |
541 switch experiment.threshEstMethod | 467 switch experiment.threshEstMethod |
542 case {'2I2AFC++', '2I2AFC+++'} | 468 case {'2I2AFC++', '2I2AFC+++'} |
543 userSelects2 (subjectGUIHandles) | 469 userSelects2 (subjectGUIHandles) |
544 otherwise | 470 otherwise |
548 set(subjectGUIHandles.pushbutton2,... | 474 set(subjectGUIHandles.pushbutton2,... |
549 'backgroundcolor',get(0,... | 475 'backgroundcolor',get(0,... |
550 'defaultUicontrolBackgroundColor')) | 476 'defaultUicontrolBackgroundColor')) |
551 userSelects2 (subjectGUIHandles) | 477 userSelects2 (subjectGUIHandles) |
552 end | 478 end |
553 | 479 |
554 case pbRepeat % extreme right button | 480 case pbRepeat % extreme right button |
555 switch experiment.threshEstMethod | 481 switch experiment.threshEstMethod |
556 case {'2I2AFC++', '2I2AFC+++'} | 482 case {'2I2AFC++', '2I2AFC+++'} |
557 disp(' ignored button press') | 483 disp(' ignored button press') |
558 otherwise | 484 otherwise |
559 | 485 |
560 set(subjectGUIHandles.pushbuttoNotSure,... | 486 set(subjectGUIHandles.pushbuttoNotSure,... |
561 'backgroundcolor','r') | 487 'backgroundcolor','r') |
562 pause(.1) | 488 pause(.1) |
563 set(subjectGUIHandles.pushbuttoNotSure,... | 489 set(subjectGUIHandles.pushbuttoNotSure,... |
564 'backgroundcolor',get(0,... | 490 'backgroundcolor',get(0,... |
565 'defaultUicontrolBackgroundColor')) | 491 'defaultUicontrolBackgroundColor')) |
566 userSelectsPleaseRepeat (subjectGUIHandles) | 492 userSelectsPleaseRepeat (subjectGUIHandles) |
567 end | 493 end |
568 | 494 |
569 case {pbWrong, pbBlank} | 495 case {pbWrong, pbBlank} |
570 disp(' ignored button press') | 496 disp(' ignored button press') |
571 | 497 |
572 otherwise % unrecognised button | 498 otherwise % unrecognised button |
573 disp('ignored button press') | 499 disp('ignored button press') |
574 end % end (button press number) | 500 end % end (button press number) |
575 otherwise | 501 otherwise |
576 disp('ignored button press') | 502 disp('ignored button press') |
577 end % experiment status | 503 end % experiment status |
578 | 504 |
579 % All processing returns through here. | |
580 % fopen(serobj); % flushes the input buffer | |
581 | |
582 % buttonPressedNo = fscanf(serobj,'%c',1); | |
583 | |
584 % button box remains 'busy' until after the stimulus has been presented | 505 % button box remains 'busy' until after the stimulus has been presented |
585 experiment.buttonBoxStatus='not busy'; | 506 experiment.buttonBoxStatus='not busy'; |
586 | 507 |
587 % -------------------------------------------------- pushbuttonGO_Callback | 508 % -------------------------------------------------- pushbuttonGO_Callback |
588 function pushbuttonGO_Callback(hObject, eventdata, handles) | 509 function pushbuttonGO_Callback(hObject, eventdata, handles) |
597 function pushbutton0_Callback(hObject, eventdata, handles) | 518 function pushbutton0_Callback(hObject, eventdata, handles) |
598 global experiment | 519 global experiment |
599 % This is a mouse click path | 520 % This is a mouse click path |
600 | 521 |
601 % ignore 0 button if 2I2AFC used | 522 % ignore 0 button if 2I2AFC used |
602 if findstr(experiment.threshEstMethod,'2I2AFC'), return, end | 523 if findstr(experiment.threshEstMethod,'2I2AFC') |
524 return % to quiescent state | |
525 end | |
603 | 526 |
604 % userDoesNotHearTarget(handles) % only possible interpretation | 527 % userDoesNotHearTarget(handles) % only possible interpretation |
605 userDecides(handles, false) | 528 userDecides(handles, false) |
606 | 529 |
607 % -------------------------------------------------- pushbutton1_Callback | 530 % -------------------------------------------------- pushbutton1_Callback |
635 | 558 |
636 % ----------------------------------------------------- userSelects0or1 | 559 % ----------------------------------------------------- userSelects0or1 |
637 function userSelects0or1(handles) | 560 function userSelects0or1(handles) |
638 global experiment withinRuns | 561 global experiment withinRuns |
639 | 562 |
640 switch experiment.threshEstMethod | 563 switch experiment.threshEstMethod |
641 case {'2I2AFC++', '2I2AFC+++'} | 564 case {'2I2AFC++', '2I2AFC+++'} |
642 switch withinRuns.stimulusOrder | 565 switch withinRuns.stimulusOrder |
643 case 'targetFirst'; | 566 case 'targetFirst'; |
644 % userHearsTarget(handles) | 567 % userHearsTarget(handles) |
645 userDecides(handles, true) | 568 userDecides(handles, true) |
655 % return to pushButton1 callback | 578 % return to pushButton1 callback |
656 | 579 |
657 % ----------------------------------------------------- userSelects2 | 580 % ----------------------------------------------------- userSelects2 |
658 function userSelects2(handles) | 581 function userSelects2(handles) |
659 global experiment withinRuns | 582 global experiment withinRuns |
660 switch experiment.threshEstMethod | 583 switch experiment.threshEstMethod |
661 case {'2I2AFC++', '2I2AFC+++'} | 584 case {'2I2AFC++', '2I2AFC+++'} |
662 switch withinRuns.stimulusOrder | 585 switch withinRuns.stimulusOrder |
663 case 'targetSecond'; | 586 case 'targetSecond'; |
664 % userDoesNotHearTarget(handles) | 587 % userDoesNotHearTarget(handles) |
665 userDecides(handles, true) | 588 userDecides(handles, true) |
674 % return to pushButton2 callback | 597 % return to pushButton2 callback |
675 | 598 |
676 % ----------------------------------------------------- ---- userDecides | 599 % ----------------------------------------------------- ---- userDecides |
677 function userDecides(handles, saidYes) | 600 function userDecides(handles, saidYes) |
678 global experiment stimulusParameters betweenRuns withinRuns | 601 global experiment stimulusParameters betweenRuns withinRuns |
679 global rareEvent logistic psy levelsBinVector | 602 global rareEvent logistic psy levelsBinVector errormsg |
680 | 603 |
681 if experiment.singleShot | 604 if experiment.singleShot |
682 return | 605 return % not clear why this should be here |
683 end | 606 end |
684 | 607 |
685 % ignore click if not 'waitingForResponse' | 608 % ignore click if not 'waitingForResponse' |
686 if ~strcmp(experiment.status,'waitingForResponse') | 609 if ~strcmp(experiment.status,'waitingForResponse') |
687 disp('ignored click') | 610 disp('ignored click') |
688 return | 611 return % to userSelects |
689 end | 612 end |
690 | 613 |
691 % speech reception threshold | 614 % speech reception threshold |
692 if strcmp(stimulusParameters.targetType,'digitStrings') | 615 if strcmp(stimulusParameters.targetType,'digitStrings') |
616 % read triple digits from userGUI | |
693 digitsInput=get(handles.editdigitInput,'string'); | 617 digitsInput=get(handles.editdigitInput,'string'); |
694 % must be three digits | 618 % must be three digits |
695 if ~(length(digitsInput)==3) | 619 if ~(length(digitsInput)==3) |
696 addToMsg(['error message: Wrong no of digits'], 0, 1) | 620 addToMsg(['error message: Wrong no of digits'], 0, 1) |
697 set(handles.textMSG,'string', 'Wrong no of digits', ... | 621 set(handles.textMSG,'string', 'Wrong no of digits', ... |
698 'BackgroundColor','r', 'ForegroundColor', 'w') | 622 'BackgroundColor','r', 'ForegroundColor', 'w') |
699 set(handles.editdigitInput,'string','') | 623 set(handles.editdigitInput,'string','') |
700 | |
701 return | 624 return |
702 end | 625 end |
703 % obtain correct answer from file name | 626 % obtain correct answer from file name |
704 x=stimulusParameters.digitString; | 627 x=stimulusParameters.digitString; |
705 idx=find(x=='O'); x(idx)='0'; % replace 'oh' with zero | 628 idx=find(x=='O'); x(idx)='0'; % replace 'oh' with zero |
706 | |
707 disp([x ' ' digitsInput]) | 629 disp([x ' ' digitsInput]) |
708 | |
709 if x==digitsInput | 630 if x==digitsInput |
710 saidYes=1; | 631 saidYes=1; % i.e. correct response |
711 else | 632 else |
712 saidYes=0; | 633 saidYes=0; % i.e wrong response |
713 end | 634 end |
714 set(handles.editdigitInput,'string','') | 635 set(handles.editdigitInput,'string','') |
715 set(handles.editdigitInput,'visible','off') | 636 set(handles.editdigitInput,'visible','off') |
716 pause(0.1) | 637 pause(0.1) |
717 end | 638 end |
718 | |
719 | |
720 | 639 |
721 % no button presses accepted while processing | 640 % no button presses accepted while processing |
722 experiment.status='processingResponse'; | 641 experiment.status='processingResponse'; |
723 | 642 |
724 % catch trials. Restart trial if caught | 643 % catch trials. Restart trial if caught |
725 if withinRuns.catchTrial | 644 if withinRuns.catchTrial |
726 if saidYes | 645 if saidYes |
727 disp('catch trial - caught out') | 646 disp('catch trial - caught out') |
728 withinRuns.caughtOut=withinRuns.caughtOut+1; | 647 withinRuns.caughtOut=withinRuns.caughtOut+1; |
729 | 648 |
730 % special: estimate caught out rate by allowing the trial | 649 % special: estimate caught out rate by allowing the trial |
731 % to continue after catch | 650 % to continue after catch |
732 if stimulusParameters.catchTrialBaseRate==0.5 | 651 if stimulusParameters.catchTrialBaseRate==0.5 |
733 % To use this facility, set the catchTrialRate and the | 652 % To use this facility, set the catchTrialRate and the |
734 % catchTrialBaseRate both to 0.5 | 653 % catchTrialBaseRate both to 0.5 |
735 % update false positive rate | 654 % update false positive rate |
736 betweenRuns.caughtOut(betweenRuns.runNumber)=... | 655 betweenRuns.caughtOut(betweenRuns.runNumber)=... |
737 withinRuns.caughtOut; | 656 withinRuns.caughtOut; |
738 plotProgressThisTrial(handles) | 657 plotProgressThisTrial(handles) |
739 nextStimulus(handles); | 658 nextStimulus(handles); |
740 return | 659 return |
741 end | 660 end |
742 | 661 |
743 % Punishment: restart the trial | 662 % Punishment: caught out restarts the trial |
744 set(handles.frame1,'backgroundColor','r') | 663 set(handles.frame1,'backgroundColor','r') |
745 set(handles.pushbuttonGO, ... | 664 set(handles.pushbuttonGO, ... |
746 'visible','on', 'backgroundcolor','y') % and go again | 665 'visible','on', 'backgroundcolor','y') % and go again |
747 msg=[{'Start again: catch trial error'}, {' '},... | 666 msg=[{'Start again: catch trial error'}, {' '},... |
748 {'Please,click on the GO button'}]; | 667 {'Please,click on the GO button'}]; |
749 set(handles.textMSG,'string',msg) | 668 set(handles.textMSG,'string',msg) |
750 [y,fs]=wavread('ding.wav'); | 669 [y,fs]=wavread('ding.wav'); |
751 wavplay(y/100,fs) | 670 wavplay(y/100,fs) |
752 | 671 |
753 % raise catch trial rate temporarily. | 672 % raise catch trial rate temporarily. |
754 % this is normally reduced on each new trial (see GO) | 673 % this is normally reduced on each new trial (see GO) |
755 stimulusParameters.catchTrialRate=... | 674 stimulusParameters.catchTrialRate=... |
756 stimulusParameters.catchTrialRate+0.1; | 675 stimulusParameters.catchTrialRate+0.1; |
757 if stimulusParameters.catchTrialRate>0.5 | 676 if stimulusParameters.catchTrialRate>0.5 |
758 stimulusParameters.catchTrialRate=0.5; | 677 stimulusParameters.catchTrialRate=0.5; |
759 end | 678 end |
760 fprintf('stimulusParameters.catchTrialRate= %6.3f\n', ... | 679 fprintf('stimulusParameters.catchTrialRate= %6.3f\n', ... |
761 stimulusParameters.catchTrialRate) | 680 stimulusParameters.catchTrialRate) |
762 | 681 |
763 betweenRuns.caughtOut(betweenRuns.runNumber)=... | 682 betweenRuns.caughtOut(betweenRuns.runNumber)=... |
764 1+betweenRuns.caughtOut(betweenRuns.runNumber); | 683 1+betweenRuns.caughtOut(betweenRuns.runNumber); |
765 betweenRuns.runNumber=betweenRuns.runNumber-1; | 684 betweenRuns.runNumber=betweenRuns.runNumber-1; |
766 experiment.status='waitingForGO'; | 685 experiment.status='waitingForGO'; |
767 return % unwind and wait for button press | 686 return % unwind and wait for button press |
768 else % (said No) | 687 else % (said No) |
769 % user claims not to have heard target. fortunate as it was not | 688 % user claims not to have heard target. |
770 % present. So, repeat the stimulus (possibly with target) | 689 % This is good as it was not present. |
690 % So, repeat the stimulus (possibly with target) | |
771 % and behave as if the last trial did not occur | 691 % and behave as if the last trial did not occur |
772 errormsg=nextStimulus(handles); | 692 errormsg=nextStimulus(handles); |
773 | 693 |
774 % terminate if there is any kind of problem | 694 % terminate if there is any kind of problem |
775 if ~isempty(errormsg) | 695 if ~isempty(errormsg) |
776 % e.g. limits exceeded, clipping | 696 % e.g. limits exceeded, clipping |
777 disp(['Error nextStimulus: ' errormsg]) | 697 disp(['Error nextStimulus: ' errormsg]) |
778 runCompleted(handles) | 698 runCompleted(handles) |
779 return | 699 return |
780 end | 700 end |
781 return % no further action - next trial | 701 return % no further action - next trial |
782 end | 702 end |
783 end | 703 end % of catch trial |
784 | 704 |
785 % This section analyses the responses, makes tracks and defines next stim. | 705 % Real target: analyse the response, make tracks and define next stim. |
786 | 706 |
787 % Define response and update response list | 707 % Define response and update response list |
788 if saidYes | 708 if saidYes |
789 % target was heard, so response=1; | 709 % target was heard, so response=1; |
790 withinRuns.responseList=[withinRuns.responseList 1]; % 'heard it!' | 710 withinRuns.responseList=[withinRuns.responseList 1]; % 'heard it!' |
797 | 717 |
798 % keep track of peaks and troughs; | 718 % keep track of peaks and troughs; |
799 % identify direction of change during initial period | 719 % identify direction of change during initial period |
800 if saidYes | 720 if saidYes |
801 % default step size before first reversal | 721 % default step size before first reversal |
802 WRVinitialStep=-stimulusParameters.WRVinitialStep; | 722 WRVinitialStep=-stimulusParameters.WRVinitialStep; |
803 WRVsmallStep=-stimulusParameters.WRVsmallStep; | 723 WRVsmallStep=-stimulusParameters.WRVsmallStep; |
804 % if the previous direction was 'less difficult', this must be a peak | 724 % if the previous direction was 'less difficult', this must be a peak |
805 if strcmp(withinRuns.direction,'less difficult') ... | 725 if strcmp(withinRuns.direction,'less difficult') ... |
806 && length(withinRuns.levelList)>1 | 726 && length(withinRuns.levelList)>1 |
807 withinRuns.peaks=[withinRuns.peaks withinRuns.variableValue]; | 727 withinRuns.peaks=[withinRuns.peaks withinRuns.variableValue]; |
808 end | 728 end |
809 withinRuns.direction='more difficult'; | 729 withinRuns.direction='more difficult'; |
810 | |
811 else | 730 else |
812 % said 'no' | 731 % said 'no' |
813 % default step size before first reversal | 732 % default step size before first reversal |
814 WRVinitialStep=stimulusParameters.WRVinitialStep; | 733 WRVinitialStep=stimulusParameters.WRVinitialStep; |
815 WRVsmallStep=stimulusParameters.WRVsmallStep; | 734 WRVsmallStep=stimulusParameters.WRVsmallStep; |
816 | 735 |
817 % if the previous direction was 'up', this must be a peak | 736 % if the previous direction was 'up', this must be a peak |
818 if strcmp(withinRuns.direction,'more difficult') ... | 737 if strcmp(withinRuns.direction,'more difficult') ... |
819 && length(withinRuns.levelList)>1 | 738 && length(withinRuns.levelList)>1 |
820 withinRuns.troughs=[withinRuns.troughs withinRuns.variableValue]; | 739 withinRuns.troughs=[withinRuns.troughs withinRuns.variableValue]; |
821 end | 740 end |
822 withinRuns.direction='less difficult'; | 741 withinRuns.direction='less difficult'; |
823 end | 742 end |
824 | 743 |
825 % phase 2 is all the levels after and incuding the first reversal | 744 % phase 2 is all the levels after and incuding the first reversal |
826 % plus the level before that | 745 % plus the level before that |
746 % Look for the end of phase 1 | |
827 if ~withinRuns.nowInPhase2 && length(withinRuns.peaks)+ ... | 747 if ~withinRuns.nowInPhase2 && length(withinRuns.peaks)+ ... |
828 length(withinRuns.troughs)>0 | 748 length(withinRuns.troughs)>0 |
829 % if ~withinRuns.nowInPhase2 && (~isempty(withinRuns.peaks) ... | |
830 % || ~isempty(withinRuns.troughs)) | |
831 % define phase 2 | 749 % define phase 2 |
832 withinRuns.beginningOfPhase2=trialNumber-1; | 750 withinRuns.beginningOfPhase2=trialNumber-1; |
833 withinRuns.nowInPhase2=1; | 751 withinRuns.nowInPhase2=1; |
834 WRVsmallStep=WRVinitialStep/2; | 752 WRVsmallStep=WRVinitialStep/2; |
835 end | 753 end |
842 withinRuns.responseList(withinRuns.beginningOfPhase2:end); | 760 withinRuns.responseList(withinRuns.beginningOfPhase2:end); |
843 else | 761 else |
844 withinRuns.levelsPhaseTwo=[]; | 762 withinRuns.levelsPhaseTwo=[]; |
845 end | 763 end |
846 | 764 |
847 | |
848 % get (or substitute) threshold estimate | 765 % get (or substitute) threshold estimate |
849 switch experiment.threshEstMethod | 766 switch experiment.threshEstMethod |
850 case {'2I2AFC++', '2I2AFC+++'} | 767 case {'2I2AFC++', '2I2AFC+++'} |
851 % for plotting psychometric function only | 768 % for plotting psychometric function only |
852 if withinRuns.beginningOfPhase2>0 | 769 if withinRuns.beginningOfPhase2>0 |
853 [psy, levelsBinVector, logistic, rareEvent]= ... | 770 [psy, levelsBinVector, logistic, rareEvent]= ... |
854 bestFitPsychometicFunctions... | 771 bestFitPsychometicFunctions... |
855 (withinRuns.levelsPhaseTwo, withinRuns.responsesPhaseTwo); | 772 (withinRuns.levelsPhaseTwo, withinRuns.responsesPhaseTwo); |
856 end | 773 end |
857 | 774 |
858 if ~isempty(withinRuns.peaks) && ~isempty(withinRuns.troughs) | 775 if ~isempty(withinRuns.peaks) && ~isempty(withinRuns.troughs) |
859 thresholdEstimate= ... | 776 thresholdEstimate= ... |
860 mean([mean(withinRuns.peaks) mean(withinRuns.troughs)]); | 777 mean([mean(withinRuns.peaks) mean(withinRuns.troughs)]); |
861 else | 778 else |
862 thresholdEstimate=NaN; | 779 thresholdEstimate=NaN; |
863 end | 780 end |
781 | |
864 otherwise | 782 otherwise |
865 % single interval methods | 783 % single interval methods |
866 try | 784 try |
867 % using the s trial after the first reversal | 785 % using the s trial after the first reversal |
868 [psy, levelsBinVector, logistic, rareEvent]= ... | 786 [psy, levelsBinVector, logistic, rareEvent]= ... |
914 if saidYes | 832 if saidYes |
915 [WRVinitialStep, msg]=Levitt2('hit', withinRuns.variableValue); | 833 [WRVinitialStep, msg]=Levitt2('hit', withinRuns.variableValue); |
916 else | 834 else |
917 [WRVinitialStep, msg]=Levitt2('miss',withinRuns.variableValue); | 835 [WRVinitialStep, msg]=Levitt2('miss',withinRuns.variableValue); |
918 end | 836 end |
919 | 837 |
920 % empty message means continue as normal | 838 % empty message means continue as normal |
921 if ~isempty(msg) | 839 if ~isempty(msg) |
922 runCompleted(handles) | 840 runCompleted(handles) |
923 return | 841 return |
924 end | 842 end |
925 newWRVvalue=withinRuns.variableValue-WRVinitialStep; | 843 newWRVvalue=withinRuns.variableValue-WRVinitialStep; |
926 | 844 |
927 case {'MaxLikelihood', 'oneIntervalUpDown'} | 845 case {'MaxLikelihood', 'oneIntervalUpDown'} |
928 % run completed by virtue of number of trials | 846 % run completed by virtue of number of trials |
929 % or restart because listener is in trouble | 847 % or restart because listener is in trouble |
930 if length(withinRuns.levelsPhaseTwo)== experiment.maxTrials | 848 if length(withinRuns.levelsPhaseTwo)== experiment.maxTrials |
931 % Use bonomial test to decide if there is an imbalance in the | 849 % Use bonomial test to decide if there is an imbalance in the |
932 % number of 'yes'es and 'no's | 850 % number of 'yes'es and 'no's |
933 yesCount=sum(withinRuns.responseList); | 851 yesCount=sum(withinRuns.responseList); |
950 else | 868 else |
951 runCompleted(handles) | 869 runCompleted(handles) |
952 return | 870 return |
953 end | 871 end |
954 end | 872 end |
955 | 873 |
956 % set new value for WRV | 874 % set new value for WRV |
957 if withinRuns.nowInPhase2 | 875 if withinRuns.nowInPhase2 |
958 % phase 2 | 876 % phase 2 |
959 currentMeanEst=withinRuns.thresholdEstimateTrack(end); | 877 currentMeanEst=withinRuns.thresholdEstimateTrack(end); |
960 switch experiment.threshEstMethod | 878 switch experiment.threshEstMethod |
961 case 'MaxLikelihood' | 879 case 'MaxLikelihood' |
962 newWRVvalue=currentMeanEst; | 880 newWRVvalue=currentMeanEst; |
963 case {'oneIntervalUpDown'} | 881 case {'oneIntervalUpDown'} |
964 newWRVvalue=withinRuns.variableValue+WRVsmallStep; | 882 newWRVvalue=withinRuns.variableValue+WRVsmallStep; |
965 end | 883 end |
966 else | 884 else |
967 % phase 1 | 885 % phase 1 |
1011 % ignore click if not 'waitingForResponse' | 929 % ignore click if not 'waitingForResponse' |
1012 if ~strcmp(experiment.status,'waitingForResponse') | 930 if ~strcmp(experiment.status,'waitingForResponse') |
1013 disp('ignored click') | 931 disp('ignored click') |
1014 return | 932 return |
1015 end | 933 end |
1016 % Take no action other than to make a | 934 % Take no action other than to make a |
1017 % tally of repeat requests | 935 % tally of repeat requests |
1018 experiment.pleaseRepeat=experiment.pleaseRepeat+1; | 936 experiment.pleaseRepeat=experiment.pleaseRepeat+1; |
1019 withinRuns.thisIsRepeatTrial=1; | 937 withinRuns.thisIsRepeatTrial=1; |
1020 nextStimulus(handles); | 938 nextStimulus(handles); |
1021 | 939 |
1022 % ------------------------------------------------ userSelectsWrongButton | 940 % ------------------------------------------------ userSelectsWrongButton |
1023 function userSelectsWrongButton(handles) | 941 function userSelectsWrongButton(handles) |
1024 global withinRuns experiment | 942 global withinRuns experiment |
1025 % restart is the simplest solution for a 'wrong button' request | 943 % restart is the simplest solution for a 'wrong button' request |
1026 withinRuns.wrongButton=withinRuns.wrongButton+1; | 944 withinRuns.wrongButton=withinRuns.wrongButton+1; |
1027 set(handles.pushbuttonGO, 'visible','on', 'backgroundcolor','y') | 945 set(handles.pushbuttonGO, 'visible','on', 'backgroundcolor','y') |
1028 msg=[{'Start again: wrong button pressed'}, {' '},... | 946 msg=[{'Start again: wrong button pressed'}, {' '},... |
1029 {'Please,click on the GO button'}]; | 947 {'Please,click on the GO button'}]; |
1030 set(handles.textMSG,'string',msg) | 948 set(handles.textMSG,'string',msg) |
1031 experiment.status='waitingForGO'; | 949 experiment.status='waitingForGO'; |
1032 | 950 |
1033 % ------------------------------------------------- plotProgressThisTrial | 951 % ------------------------------------------------- plotProgressThisTrial |
1034 function plotProgressThisTrial(handles) | 952 function plotProgressThisTrial(handles) |
1035 | 953 % updates GUI: used for all responses |
1036 % used for all responses | 954 |
1037 global experiment stimulusParameters betweenRuns withinRuns expGUIhandles | 955 global experiment stimulusParameters betweenRuns withinRuns expGUIhandles |
1038 global psy levelsBinVector binFrequencies rareEvent logistic statsModel | 956 global psy levelsBinVector binFrequencies rareEvent logistic statsModel |
1039 | |
1040 | 957 |
1041 % plot the levelTrack and the threshold track | 958 % plot the levelTrack and the threshold track |
1042 | 959 |
1043 % Panel 2 | 960 % Panel 2 |
1044 % plot the levelList | 961 % plot the levelList |
1068 xlim([0 experiment.maxTrials+withinRuns.beginningOfPhase2]); | 985 xlim([0 experiment.maxTrials+withinRuns.beginningOfPhase2]); |
1069 ylim(stimulusParameters.WRVlimits) | 986 ylim(stimulusParameters.WRVlimits) |
1070 grid on | 987 grid on |
1071 | 988 |
1072 % Panel 4: Summary of threshold estimates (not used here) | 989 % Panel 4: Summary of threshold estimates (not used here) |
1073 % Earlier estimates are set in 'runCompleted' | 990 % Estimates from previous runs are set in 'runCompleted' |
1074 % However, title shows runs/trials remaining | 991 % It is only necessary to change title showing runs/trials remaining |
1075 | |
1076 axes(expGUIhandles.axes4) | 992 axes(expGUIhandles.axes4) |
1077 runsToGo=length(betweenRuns.var1Sequence)-betweenRuns.runNumber; | 993 runsToGo=length(betweenRuns.var1Sequence)-betweenRuns.runNumber; |
1078 if withinRuns.beginningOfPhase2>0 | 994 if withinRuns.beginningOfPhase2>0 |
1079 trialsToGo= experiment.singleIntervalMaxTrials(1) ... | 995 trialsToGo= experiment.singleIntervalMaxTrials(1) ... |
1080 + withinRuns.beginningOfPhase2- withinRuns.trialNumber; | 996 + withinRuns.beginningOfPhase2- withinRuns.trialNumber; |
1094 % display only when in phase 2. | 1010 % display only when in phase 2. |
1095 withinRuns.levelsPhaseTwo=... | 1011 withinRuns.levelsPhaseTwo=... |
1096 withinRuns.levelList(withinRuns.beginningOfPhase2:end); | 1012 withinRuns.levelList(withinRuns.beginningOfPhase2:end); |
1097 withinRuns.responsesPhaseTwo=... | 1013 withinRuns.responsesPhaseTwo=... |
1098 withinRuns.responseList(withinRuns.beginningOfPhase2:end); | 1014 withinRuns.responseList(withinRuns.beginningOfPhase2:end); |
1099 | 1015 |
1100 % organise data as psychometric function | 1016 % organise data as psychometric function |
1101 [psy, levelsBinVector, binFrequencies]= ... | 1017 [psy, levelsBinVector, binFrequencies]= ... |
1102 psychometricFunction(withinRuns.levelsPhaseTwo,... | 1018 psychometricFunction(withinRuns.levelsPhaseTwo,... |
1103 withinRuns.responsesPhaseTwo, experiment.psyBinWidth); | 1019 withinRuns.responsesPhaseTwo, experiment.psyBinWidth); |
1104 | 1020 |
1105 % Plot the function | 1021 % Plot the function |
1106 % point by point with circles of appropiate weighted size | 1022 % point by point with circles of appropiate weighted size |
1107 hold on, | 1023 hold on, |
1108 for i=1:length(psy) | 1024 for i=1:length(psy) |
1109 plot(levelsBinVector(i), psy(i), 'ro', ... | 1025 plot(levelsBinVector(i), psy(i), 'ro', ... |
1110 'markersize', 50*binFrequencies(i)/sum(binFrequencies)) | 1026 'markersize', 50*binFrequencies(i)/sum(binFrequencies)) |
1111 end | 1027 end |
1112 % save info for later | 1028 % save info for later |
1113 betweenRuns.psychometicFunction{betweenRuns.runNumber}=... | 1029 betweenRuns.psychometicFunction{betweenRuns.runNumber}=... |
1114 [levelsBinVector; psy]; | 1030 [levelsBinVector; psy]; |
1115 | 1031 |
1116 % fitPsychometric functions is computed in 'userDecides' | 1032 % fitPsychometric functions is computed in 'userDecides' |
1117 % plot(rareEvent.predictionLevels, rareEvent.predictionsRE,'k') | 1033 % plot(rareEvent.predictionLevels, rareEvent.predictionsRE,'k') |
1118 plot(logistic.predictionLevels, logistic.predictionsLOG, 'r') | 1034 plot(logistic.predictionLevels, logistic.predictionsLOG, 'r') |
1119 plot(rareEvent.predictionLevels, rareEvent.predictionsRE, 'k') | 1035 plot(rareEvent.predictionLevels, rareEvent.predictionsRE, 'k') |
1120 if ~isnan(logistic.bestThreshold ) | 1036 if ~isnan(logistic.bestThreshold ) |
1121 % xlim([ (logistic.bestThreshold -20) ... | |
1122 % (logistic.bestThreshold +20) ]) | |
1123 xlim([ 0 100 ]) | 1037 xlim([ 0 100 ]) |
1124 % if logistic.bestK< max(experiment.possLogSlopes) | 1038 title(['k= ' num2str(logistic.bestK, '%6.2f') ' g= '... |
1125 title(['k= ' num2str(logistic.bestK, '%6.2f') ' g= '... | 1039 num2str(rareEvent.bestGain,'%6.3f') ' A=' ... |
1126 num2str(rareEvent.bestGain,'%6.3f') ' A=' ... | 1040 num2str(rareEvent.bestVMin,'%8.1f')]) |
1127 num2str(rareEvent.bestVMin,'%8.1f')]) | |
1128 % title('') | |
1129 % end | |
1130 else | 1041 else |
1131 title(' ') | 1042 title(' ') |
1132 end | 1043 end |
1133 | 1044 |
1134 switch experiment.ear | 1045 switch experiment.ear |
1135 %plot green line for statsModel a priori model | 1046 %plot green line for statsModel a priori model |
1136 case 'statsModelLogistic' | 1047 case 'statsModelLogistic' |
1137 % plot proTem logistic (green) used by stats model | 1048 % plot proTem logistic (green) used by stats model |
1138 p= 1./(1+exp(-statsModel.logisticSlope... | 1049 p= 1./(1+exp(-statsModel.logisticSlope... |
1148 p(p<0)=0; | 1059 p(p<0)=0; |
1149 if experiment.psyFunSlope<0, p=1-p;end | 1060 if experiment.psyFunSlope<0, p=1-p;end |
1150 hold on, plot(levelsBinVector, p,'g') | 1061 hold on, plot(levelsBinVector, p,'g') |
1151 end %(estMethod) | 1062 end %(estMethod) |
1152 end | 1063 end |
1064 | |
1153 otherwise % 2A2IFC | 1065 otherwise % 2A2IFC |
1154 | |
1155 message3= ... | 1066 message3= ... |
1156 ([ 'peaks=' num2str(withinRuns.peaks) ... | 1067 ([ 'peaks=' num2str(withinRuns.peaks) ... |
1157 'troughs=' num2str(withinRuns.troughs)]); | 1068 'troughs=' num2str(withinRuns.troughs)]); |
1158 ylimRM([-0.1 1.1]) % 0=no / 1=yes | 1069 ylimRM([-0.1 1.1]) % 0=no / 1=yes |
1159 set(gca,'ytick',[0 1], 'yTickLabel', {'no';'yes'}) | 1070 set(gca,'ytick',[0 1], 'yTickLabel', {'no';'yes'}) |
1189 global experiment stimulusParameters betweenRuns withinRuns | 1100 global experiment stimulusParameters betweenRuns withinRuns |
1190 global rareEvent expGUIhandles | 1101 global rareEvent expGUIhandles |
1191 % disp('run completed') | 1102 % disp('run completed') |
1192 | 1103 |
1193 experiment.status='runCompleted'; | 1104 experiment.status='runCompleted'; |
1194 | 1105 % quick update after final trial just to make sure |
1195 plotProgressThisTrial(handles) | 1106 plotProgressThisTrial(handles) |
1196 | 1107 |
1197 switch experiment.ear | 1108 switch experiment.ear |
1198 case {'statsModelLogistic', 'statsModelRareEvent','MAPmodel', ... | 1109 case {'statsModelLogistic', 'statsModelRareEvent','MAPmodel', ... |
1199 'MAPmodelMultiCh','MAPmodelSingleCh', 'MAPmodelListen'} | 1110 'MAPmodelMultiCh','MAPmodelSingleCh', 'MAPmodelListen'} |
1224 withinRuns.responsesPhaseTwo=... | 1135 withinRuns.responsesPhaseTwo=... |
1225 withinRuns.responseList(withinRuns.beginningOfPhase2:end); | 1136 withinRuns.responseList(withinRuns.beginningOfPhase2:end); |
1226 [psy, levelsPhaseTwoBinVector, logistic, rareEvent]= ... | 1137 [psy, levelsPhaseTwoBinVector, logistic, rareEvent]= ... |
1227 bestFitPsychometicFunctions... | 1138 bestFitPsychometicFunctions... |
1228 (withinRuns.levelsPhaseTwo, withinRuns.responsesPhaseTwo); | 1139 (withinRuns.levelsPhaseTwo, withinRuns.responsesPhaseTwo); |
1229 | 1140 |
1230 % plot final psychometric function | 1141 % plot final psychometric function |
1231 axes(expGUIhandles.axes5),cla | 1142 axes(expGUIhandles.axes5),cla |
1232 hold on, plot(rareEvent.predictionLevels, rareEvent.predictionsRE, 'k') | 1143 hold on, plot(rareEvent.predictionLevels, rareEvent.predictionsRE, 'k') |
1233 hold on, plot(logistic.predictionLevels, logistic.predictionsLOG, 'r') | 1144 hold on, plot(logistic.predictionLevels, logistic.predictionsLOG, 'r') |
1234 % organise data as psychometric function | 1145 % organise data as psychometric function |
1240 for i=1:length(psy) | 1151 for i=1:length(psy) |
1241 plot(levelsBinVector(i), psy(i), 'ro', ... | 1152 plot(levelsBinVector(i), psy(i), 'ro', ... |
1242 'markersize', 50*binFrequencies(i)/sum(binFrequencies)) | 1153 'markersize', 50*binFrequencies(i)/sum(binFrequencies)) |
1243 end | 1154 end |
1244 | 1155 |
1245 | |
1246 % experimental | 1156 % experimental |
1247 medianThreshold=median(withinRuns.levelsPhaseTwo); | 1157 medianThreshold=median(withinRuns.levelsPhaseTwo); |
1248 warning off | 1158 warning off |
1249 meanThreshold=mean(withinRuns.levelsPhaseTwo); | 1159 meanThreshold=mean(withinRuns.levelsPhaseTwo); |
1250 | 1160 |
1251 % identify the current threshold estimate | 1161 % identify the current threshold estimate |
1252 switch experiment.paradigm | 1162 switch experiment.paradigm |
1253 case 'discomfort' | 1163 case 'discomfort' |
1254 % most recent value (not truely a mean value) | 1164 % most recent value (not truely a mean value) |
1255 threshold=withinRuns.levelList(end); | 1165 threshold=withinRuns.levelList(end); |
1256 stdev=NaN; | 1166 stdev=NaN; |
1257 otherwise | 1167 otherwise |
1258 switch experiment.threshEstMethod | 1168 switch experiment.threshEstMethod |
1259 case {'MaxLikelihood', 'oneIntervalUpDown'} | 1169 case {'MaxLikelihood', 'oneIntervalUpDown'} |
1260 % last value in the list | 1170 % last value in the list |
1261 % threshold=withinRuns.meanEstTrack(end); | 1171 % threshold=withinRuns.meanEstTrack(end); |
1262 threshold=withinRuns.thresholdEstimateTrack(end); | 1172 threshold=withinRuns.thresholdEstimateTrack(end); |
1263 stdev=NaN; | 1173 stdev=NaN; |
1264 | 1174 |
1265 case {'2I2AFC++', '2I2AFC+++'} | 1175 case {'2I2AFC++', '2I2AFC+++'} |
1266 % use peaks and troughs | 1176 % use peaks and troughs |
1267 try % there may not be enough values to use | 1177 try % there may not be enough values to use |
1268 peaksUsed=experiment.peaksUsed; | 1178 peaksUsed=experiment.peaksUsed; |
1269 threshold=... | 1179 threshold=... |
1298 [betweenRuns.catchTrials withinRuns.catchTrialCount]; | 1208 [betweenRuns.catchTrials withinRuns.catchTrialCount]; |
1299 | 1209 |
1300 % add variable length tracks to cell arrays | 1210 % add variable length tracks to cell arrays |
1301 if withinRuns.beginningOfPhase2>0 | 1211 if withinRuns.beginningOfPhase2>0 |
1302 betweenRuns.bestThresholdTracks{length(betweenRuns.thresholds)}=... | 1212 betweenRuns.bestThresholdTracks{length(betweenRuns.thresholds)}=... |
1303 withinRuns.thresholdEstimateTrack; | 1213 withinRuns.thresholdEstimateTrack; |
1304 betweenRuns.levelTracks{length(betweenRuns.thresholds)}=... | 1214 betweenRuns.levelTracks{length(betweenRuns.thresholds)}=... |
1305 withinRuns.levelList(withinRuns.beginningOfPhase2:end); | 1215 withinRuns.levelList(withinRuns.beginningOfPhase2:end); |
1306 betweenRuns.responseTracks{length(betweenRuns.thresholds)}=... | 1216 betweenRuns.responseTracks{length(betweenRuns.thresholds)}=... |
1307 withinRuns.responseList(withinRuns.beginningOfPhase2:end); | 1217 withinRuns.responseList(withinRuns.beginningOfPhase2:end); |
1308 else | 1218 else |
1336 for i=1:length(betweenRuns.thresholds) | 1246 for i=1:length(betweenRuns.thresholds) |
1337 faceColor=plotColors(floor(i/length(betweenRuns.variableList1)-.01)+1); | 1247 faceColor=plotColors(floor(i/length(betweenRuns.variableList1)-.01)+1); |
1338 switch betweenRuns.variableName1 | 1248 switch betweenRuns.variableName1 |
1339 case {'targetFrequency', 'maskerRelativeFrequency'} | 1249 case {'targetFrequency', 'maskerRelativeFrequency'} |
1340 if min(betweenRuns.var1Sequence)>0 | 1250 if min(betweenRuns.var1Sequence)>0 |
1341 % semilogx(betweenRuns.var1Sequence(1:betweenRuns.runNumber), ... | |
1342 % betweenRuns.thresholds, 'o', ... | |
1343 % 'markerSize', 5,'markerFaceColor',faceColor) | |
1344 semilogx(betweenRuns.var1Sequence(i), ... | 1251 semilogx(betweenRuns.var1Sequence(i), ... |
1345 betweenRuns.thresholds(i), 'o', ... | 1252 betweenRuns.thresholds(i), 'o', ... |
1346 'markerSize', 5,'markerFaceColor',faceColor) | 1253 'markerSize', 5,'markerFaceColor',faceColor) |
1347 else | 1254 else |
1348 plot(betweenRuns.var1Sequence(1:betweenRuns.runNumber), ... | 1255 plot(betweenRuns.var1Sequence(1:betweenRuns.runNumber), ... |
1351 plot(betweenRuns.var1Sequence(i), ... | 1258 plot(betweenRuns.var1Sequence(i), ... |
1352 betweenRuns.thresholds(i), 'o', ... | 1259 betweenRuns.thresholds(i), 'o', ... |
1353 'markerSize', 5,'markerFaceColor',faceColor) | 1260 'markerSize', 5,'markerFaceColor',faceColor) |
1354 end | 1261 end |
1355 otherwise | 1262 otherwise |
1356 % plot(betweenRuns.var1Sequence(1:betweenRuns.runNumber), ... | |
1357 % betweenRuns.thresholds, 'o', 'markerSize', 5,... | |
1358 % 'markerFaceColor',faceColor) | |
1359 plot(betweenRuns.var1Sequence(i), ... | 1263 plot(betweenRuns.var1Sequence(i), ... |
1360 betweenRuns.thresholds(i), 'o', 'markerSize', 5,... | 1264 betweenRuns.thresholds(i), 'o', 'markerSize', 5,... |
1361 'markerFaceColor',faceColor) | 1265 'markerFaceColor',faceColor) |
1362 end | 1266 end |
1363 hold on | 1267 hold on |
1372 set(gca,'XTick', sort(betweenRuns.variableList1)) | 1276 set(gca,'XTick', sort(betweenRuns.variableList1)) |
1373 catch | 1277 catch |
1374 end | 1278 end |
1375 grid on, set(gca,'XMinorGrid', 'off') | 1279 grid on, set(gca,'XMinorGrid', 'off') |
1376 | 1280 |
1377 % If comparison data is available in pearmeter file, plot it now | 1281 % final run? |
1378 if ~isempty (experiment.comparisonData) | |
1379 comparisonData=experiment.comparisonData(:,1:end-1); % ignore final BF | |
1380 [x, ncols]=size(comparisonData); | |
1381 if length(betweenRuns.variableList1)==ncols | |
1382 hold on | |
1383 plot (sort(betweenRuns.variableList1), comparisonData, 'r') | |
1384 hold off | |
1385 end | |
1386 end | |
1387 | |
1388 % End of the Experiment also? | |
1389 if betweenRuns.runNumber==length(betweenRuns.var1Sequence) | 1282 if betweenRuns.runNumber==length(betweenRuns.var1Sequence) |
1390 % yes, end of experiment | 1283 % yes, end of experiment |
1391 fileName=['savedData/' experiment.name experiment.date ... | 1284 fileName=['savedData/' experiment.name experiment.date ... |
1392 experiment.paradigm]; | 1285 experiment.paradigm]; |
1393 % save (fileName, 'experiment', 'stimulusParameters', 'betweenRuns', 'withinRuns', 'variableNames', 'paradigmNames', 'LevittControl') | 1286 % save (fileName, 'experiment', 'stimulusParameters', 'betweenRuns', 'withinRuns', 'variableNames', 'paradigmNames', 'LevittControl') |
1394 disp('Experiment completed') | 1287 disp('Experiment completed') |
1395 | 1288 |
1396 % update subject GUI to acknowledge end of run | 1289 % update subject GUI to acknowledge end of run |
1397 subjGUImsg=[{'Experiment completed'}, {' '}, {'Thank you!'}]; | 1290 subjGUImsg=[{'Experiment completed'}, {' '}, {'Thank you!'}]; |
1398 set(handles.textMSG,'string', subjGUImsg ) | 1291 set(handles.textMSG,'string', subjGUImsg ) |
1399 % play 'Tada' | 1292 % play 'Tada' |
1400 [y,fs,nbits]=wavread('TADA.wav'); | 1293 [y,fs,nbits]=wavread('TADA.wav'); |
1401 musicGain=10^(stimulusParameters.musicLeveldB/20); | 1294 musicGain=10^(stimulusParameters.musicLeveldB/20); |
1402 y=y*musicGain; | 1295 y=y*musicGain; |
1403 wavplay(y/100,fs, 'async') | 1296 wavplay(y/100,fs, 'async') |
1404 | 1297 |
1405 % update experimenter GUI | 1298 % update experimenter GUI |
1406 addToMsg('Experiment completed.',1) | 1299 addToMsg('Experiment completed.',1) |
1407 | 1300 |
1408 printReport | 1301 printReport |
1409 experiment.status='endOfExperiment'; | 1302 experiment.status='endOfExperiment'; |
1410 return | 1303 return |
1411 else | 1304 else |
1412 % No, hang on. | 1305 % No, hang on. |
1413 switch experiment.ear | 1306 switch experiment.ear |
1414 case {'statsModelLogistic', 'statsModelRareEvent','MAPmodel', ... | 1307 case {'statsModelLogistic', 'statsModelRareEvent','MAPmodel', ... |
1421 (stimulusParameters.catchTrialRate... | 1314 (stimulusParameters.catchTrialRate... |
1422 -stimulusParameters.catchTrialBaseRate)... | 1315 -stimulusParameters.catchTrialBaseRate)... |
1423 *(1-exp(-stimulusParameters.catchTrialTimeConstant)); | 1316 *(1-exp(-stimulusParameters.catchTrialTimeConstant)); |
1424 fprintf('stimulusParameters.catchTrialRate= %6.3f\n', ... | 1317 fprintf('stimulusParameters.catchTrialRate= %6.3f\n', ... |
1425 stimulusParameters.catchTrialRate) | 1318 stimulusParameters.catchTrialRate) |
1426 | 1319 |
1427 % and go again | 1320 % and go again |
1428 set(handles.pushbuttonGO,'backgroundcolor','y') | 1321 set(handles.pushbuttonGO,'backgroundcolor','y') |
1429 set(handles.frame1,'visible','off') | 1322 set(handles.frame1,'visible','off') |
1430 set(handles.pushbuttonGO,'visible','on') | 1323 set(handles.pushbuttonGO,'visible','on') |
1431 msg=[{'Ready to start new trial'}, {' '},... | 1324 msg=[{'Ready to start new trial'}, {' '},... |
1432 {'Please,click on the GO button'}]; | 1325 {'Please,click on the GO button'}]; |
1433 set(handles.textMSG,'string',msg) | 1326 set(handles.textMSG,'string',msg) |
1434 end | 1327 end |
1435 experiment.status='waitingForGO'; | 1328 experiment.status='waitingForGO'; |
1436 % fprintf('\n') | 1329 % fprintf('\n') |
1437 | 1330 |
1438 [y,fs,nbits]=wavread('CHIMES.wav'); | 1331 [y,fs,nbits]=wavread('CHIMES.wav'); |
1439 musicGain=10^(stimulusParameters.musicLeveldB/20); | 1332 musicGain=10^(stimulusParameters.musicLeveldB/20); |
1440 y=y*musicGain; | 1333 y=y*musicGain; |
1441 wavplay(y/100,fs,'async') | 1334 wavplay(y/100,fs,'async') |
1442 end | 1335 end |
1446 function MAPmodelRunsGUI(handles) | 1339 function MAPmodelRunsGUI(handles) |
1447 global experiment stimulusParameters method expGUIhandles | 1340 global experiment stimulusParameters method expGUIhandles |
1448 global AN_IHCsynapseParams | 1341 global AN_IHCsynapseParams |
1449 method=[]; | 1342 method=[]; |
1450 | 1343 |
1451 while strcmp(experiment.status,'waitingForGO') | 1344 while strcmp(experiment.status,'waitingForGO') |
1452 % no catch trials for MAP model | 1345 % no catch trials for MAP model |
1453 experiment.allowCatchTrials=0; | 1346 experiment.allowCatchTrials=0; |
1454 | 1347 |
1455 % initiates run and plays first stimulus and it returns | 1348 % initiates run and plays first stimulus and it returns |
1456 % without waiting for button press | 1349 % without waiting for button press |
1457 startNewRun(handles) | 1350 startNewRun(handles) |
1458 | |
1459 if sum(strcmp(experiment.ear,... | |
1460 {'MAPmodelMultiCh', 'MAPmodelListen'})) | |
1461 % use BFlist specified in MAPparams file | |
1462 BFlist= -1; | |
1463 else | |
1464 BFlist=stimulusParameters.targetFrequency; | |
1465 end | |
1466 showParams=0; | |
1467 % find model parameters using the 'name' box (e.g. CTa ->MAPparamsCTa) | |
1468 paramFunctionName=['method=MAPparams' experiment.name ... | |
1469 '(BFlist, stimulusParameters.sampleRate, showParams);']; | |
1470 eval(paramFunctionName) % go and fetch the parameters | |
1471 | |
1472 | 1351 |
1473 % show sample Rate on GUI; it must be set in MAPparams | 1352 % show sample Rate on GUI; it must be set in MAPparams |
1474 set(expGUIhandles.textsampleRate,'string',... | 1353 set(expGUIhandles.textsampleRate,'string',... |
1475 num2str(stimulusParameters.sampleRate)) | 1354 num2str(stimulusParameters.sampleRate)) |
1476 | 1355 |
1477 if experiment.singleShot | 1356 if experiment.singleShot |
1478 % AN_IHCsynapseParams.showSummaryStatistics=1; | |
1479 method.showSummaryStatistics=1; | |
1480 AN_IHCsynapseParams.plotSynapseContents=1; | 1357 AN_IHCsynapseParams.plotSynapseContents=1; |
1481 else | 1358 else |
1482 method.showSummaryStatistics=0; | |
1483 AN_IHCsynapseParams.plotSynapseContents=0; | 1359 AN_IHCsynapseParams.plotSynapseContents=0; |
1484 end | 1360 end |
1485 | 1361 |
1486 if strcmp(experiment.ear, 'MAPmodelSingleCh') | |
1487 method.MGmembranePotentialSave=1; | |
1488 end | |
1489 | |
1490 % continuous loop until the program stops itself | 1362 % continuous loop until the program stops itself |
1491 while strcmp(experiment.status,'waitingForResponse') | 1363 while strcmp(experiment.status,'waitingForResponse') |
1492 % NB at this point the stimulus has been played | 1364 % NB at this point the stimulus has been played |
1493 pause(0.1) % to allow interrupt with CTRL/C | 1365 pause(0.1) % to allow interrupt with CTRL/C |
1494 | 1366 |
1495 switch experiment.ear | 1367 switch experiment.ear |
1496 case { 'MAPmodelListen'} | 1368 case { 'MAPmodelListen'} |
1497 set(handles.pushbutton1,'backgroundcolor','y','visible','on') | 1369 % flash the buttons to show model response |
1498 set(handles.pushbutton2,'backgroundcolor','y','visible','on') | 1370 set(handles.pushbutton1,'backgroundcolor','y','visible','on') |
1499 end | 1371 set(handles.pushbutton2,'backgroundcolor','y','visible','on') |
1500 | 1372 end |
1501 AN_IHCsynapseParams.mode= 'spikes'; | 1373 |
1502 | 1374 % Analayse the current stimulus using MAP |
1503 % Analayse the current stimulus using MAP | 1375 [modelResponse earObject]= MAPmodel; |
1504 [modelResponse earObject]= MAPmodel( experiment.MAPplot, method); | 1376 |
1505 | |
1506 if experiment.stop || experiment.singleShot | 1377 if experiment.stop || experiment.singleShot |
1507 % trap for single trial or user interrupt using 'stop' button. | 1378 % trap for single trial or user interrupt using 'stop' button. |
1508 experiment.status= 'waitingForStart'; | 1379 experiment.status= 'waitingForStart'; |
1509 experiment.stop=0; | 1380 experiment.stop=0; |
1510 addToMsg('manually stopped',1); | 1381 errormsg='manually stopped'; |
1382 addToMsg(errormsg,1) | |
1511 return | 1383 return |
1512 end | 1384 end |
1513 | 1385 |
1514 switch modelResponse | 1386 switch modelResponse |
1515 case 1 | 1387 case 1 |
1516 % userDoesNotHearTarget(handles) | 1388 % userDoesNotHearTarget(handles) |
1517 switch experiment.ear | 1389 switch experiment.ear |
1518 case {'MAPmodelListen'} | 1390 case {'MAPmodelListen'} |
1531 % illuminate appropriate button (DEMO only) | 1403 % illuminate appropriate button (DEMO only) |
1532 set(handles.pushbutton2,'backgroundcolor',... | 1404 set(handles.pushbutton2,'backgroundcolor',... |
1533 'r','visible','on') | 1405 'r','visible','on') |
1534 set(handles.pushbutton1,'backgroundcolor','y') | 1406 set(handles.pushbutton1,'backgroundcolor','y') |
1535 end | 1407 end |
1536 | 1408 |
1537 switch experiment.paradigm | 1409 switch experiment.paradigm |
1538 case 'discomfort' | 1410 case 'discomfort' |
1539 % always treat discomfort as 'not heard' | 1411 % always treat discomfort as 'not heard' |
1540 userDecides(handles, false) | 1412 userDecides(handles, false) |
1541 otherwise | 1413 otherwise |
1546 return | 1418 return |
1547 end | 1419 end |
1548 end | 1420 end |
1549 end | 1421 end |
1550 | 1422 |
1551 function [modelResponse, MacGregorResponse]=MAPmodel( MAPplot, method) | 1423 % -------------------------------------------------------MAPmodel |
1424 function [modelResponse, MacGregorResponse]=MAPmodel | |
1552 | 1425 |
1553 global experiment stimulusParameters audio withinRuns | 1426 global experiment stimulusParameters audio withinRuns |
1554 global outerMiddleEarParams DRNLParams AN_IHCsynapseParams | 1427 global outerMiddleEarParams DRNLParams AN_IHCsynapseParams |
1555 | 1428 |
1556 savePath=path; | 1429 savePath=path; |
1560 | 1433 |
1561 % mono only (column vector) | 1434 % mono only (column vector) |
1562 audio=audio(:,1)'; | 1435 audio=audio(:,1)'; |
1563 | 1436 |
1564 % if stop button pressed earlier | 1437 % if stop button pressed earlier |
1565 if experiment.stop, return, end | 1438 if experiment.stop |
1566 | 1439 errormsg='manually stopped'; |
1567 % -------------------------------------------------------------- run Model | 1440 addToMsg(errormsg,1) |
1441 return | |
1442 end | |
1443 | |
1444 % ---------------------------------------------- run Model | |
1568 MAPparamsName=experiment.name; | 1445 MAPparamsName=experiment.name; |
1569 showPlotsAndDetails=experiment.MAPplot; | 1446 showPlotsAndDetails=experiment.MAPplot; |
1570 AN_spikesOrProbability='spikes'; | 1447 AN_spikesOrProbability='spikes'; |
1448 AN_spikesOrProbability='probability'; | |
1571 | 1449 |
1572 % [response, method]=MAPsequenceSeg(audio, method, 1:8); | 1450 % [response, method]=MAPsequenceSeg(audio, method, 1:8); |
1573 global ICoutput ANdt | 1451 global ICoutput ANdt dt savedBFlist ANprobRateOutput expGUIhandles |
1574 MAP1_14(audio, 1/method.dt, method.nonlinCF,... | 1452 global stimulusParameters experiment |
1575 MAPparamsName, AN_spikesOrProbability); | 1453 |
1576 | 1454 if sum(strcmp(experiment.ear,{'MAPmodelMultiCh', 'MAPmodelListen'})) |
1455 % use BFlist specified in MAPparams file | |
1456 BFlist= -1; | |
1457 else | |
1458 BFlist=stimulusParameters.targetFrequency; | |
1459 end | |
1460 paramChanges=get(expGUIhandles.editparamChanges,'string'); | |
1461 eval(paramChanges) | |
1462 | |
1463 MAP1_14(audio, stimulusParameters.sampleRate, BFlist,... | |
1464 MAPparamsName, AN_spikesOrProbability, paramChanges); | |
1465 | |
1577 if showPlotsAndDetails | 1466 if showPlotsAndDetails |
1578 options.printModelParameters=0; | 1467 options.printModelParameters=0; |
1579 options.showModelOutput=1; | 1468 options.showModelOutput=1; |
1580 options.printFiringRates=1; | 1469 options.printFiringRates=1; |
1581 options.showACF=0; | 1470 options.showACF=0; |
1584 showMapOptions.surfSpikes=0; | 1473 showMapOptions.surfSpikes=0; |
1585 UTIL_showMAP(options) | 1474 UTIL_showMAP(options) |
1586 end | 1475 end |
1587 | 1476 |
1588 % No response, probably caused by hitting 'stop' button | 1477 % No response, probably caused by hitting 'stop' button |
1589 if isempty(ICoutput), return, end | 1478 if strcmp(AN_spikesOrProbability,'spikes') && isempty(ICoutput) |
1590 | 1479 return |
1591 % MacGregor response is the sum total of all final stage spiking | 1480 end |
1592 MacGregorResponse= sum(ICoutput,1); % use IC | |
1593 | |
1594 % ---------------------------------------------------------- end model run | 1481 % ---------------------------------------------------------- end model run |
1595 | 1482 |
1596 dt=ANdt; | 1483 if strcmp(AN_spikesOrProbability,'spikes') |
1597 time=dt:dt:dt*length(MacGregorResponse); | 1484 MacGregorResponse= sum(ICoutput,1); % use IC |
1598 | 1485 dt=ANdt; |
1599 % group delay on unit response | 1486 time=dt:dt:dt*length(MacGregorResponse); |
1600 MacGonsetDelay= 0.004; | 1487 else |
1601 MacGoffsetDelay= 0.022; | 1488 % for one channel, ANprobResponse=ANprobRateOutput |
1489 % for multi channel take strongest in any epoch | |
1490 nChannels=length(savedBFlist); | |
1491 % use only HSR fibers | |
1492 ANprobRateOutput=ANprobRateOutput(end-nChannels+1:end,:); | |
1493 time=dt:dt:dt*length(ANprobRateOutput); | |
1494 end | |
1495 | |
1496 % group delay on unit response - these values are iffy | |
1497 windowOnsetDelay= 0.004; | |
1498 windowOffsetDelay= 0.022; % long ringing time | |
1602 | 1499 |
1603 % now find the response of the MacGregor model during the target presentation + group delay | 1500 % now find the response of the MacGregor model during the target presentation + group delay |
1604 switch experiment.threshEstMethod | 1501 switch experiment.threshEstMethod |
1605 case {'2I2AFC++', '2I2AFC+++'} | 1502 case {'2I2AFC++', '2I2AFC+++'} |
1606 idx= time>stimulusParameters.testTargetBegins+MacGonsetDelay ... | 1503 idx= time>stimulusParameters.testTargetBegins+windowOnsetDelay ... |
1607 & time<stimulusParameters.testTargetEnds+MacGoffsetDelay; | 1504 & time<stimulusParameters.testTargetEnds+windowOffsetDelay; |
1608 nSpikesTrueWindow=sum(MacGregorResponse(:,idx)); | 1505 nSpikesTrueWindow=sum(MacGregorResponse(:,idx)); |
1609 idx=find(time>stimulusParameters.testNonTargetBegins+MacGonsetDelay ... | 1506 idx=find(time>stimulusParameters.testNonTargetBegins+windowOnsetDelay ... |
1610 & time<stimulusParameters.testNonTargetEnds+MacGoffsetDelay); | 1507 & time<stimulusParameters.testNonTargetEnds+windowOffsetDelay); |
1611 nSpikesFalseWindow=sum(MacGregorResponse(:,idx)); | 1508 if strcmp(AN_spikesOrProbability,'spikes') |
1509 nSpikesFalseWindow=sum(MacGregorResponse(:,idx)); | |
1510 else | |
1511 nSpikesDuringTarget=mean(ANprobRateOutput(end,idx)); | |
1512 % compare with spontaneous rate | |
1513 if nSpikesDuringTarget>ANprobRateOutput(end,1)+10 | |
1514 nSpikesDuringTarget=1; % i.e. at leastone MacG spike | |
1515 else | |
1516 nSpikesDuringTarget=0; | |
1517 end | |
1518 end | |
1612 % nSpikesDuringTarget is +ve when more spikes are found | 1519 % nSpikesDuringTarget is +ve when more spikes are found |
1613 % in the target window | 1520 % in the target window |
1614 difference= nSpikesTrueWindow-nSpikesFalseWindow; | 1521 difference= nSpikesTrueWindow-nSpikesFalseWindow; |
1615 | 1522 |
1616 if difference>0 | 1523 if difference>0 |
1629 end | 1536 end |
1630 end | 1537 end |
1631 disp(['level target dummy decision: ' ... | 1538 disp(['level target dummy decision: ' ... |
1632 num2str([withinRuns.variableValue nSpikesTrueWindow ... | 1539 num2str([withinRuns.variableValue nSpikesTrueWindow ... |
1633 nSpikesFalseWindow nSpikesDuringTarget], '%4.0f') ] ) | 1540 nSpikesFalseWindow nSpikesDuringTarget], '%4.0f') ] ) |
1634 | |
1635 otherwise | 1541 otherwise |
1636 % idx=find(time>stimulusParameters.testTargetBegins+MacGonsetDelay ... | 1542 % single interval |
1637 % & time<stimulusParameters.testTargetEnds+MacGoffsetDelay); | 1543 idx=find(time>stimulusParameters.testTargetBegins +windowOnsetDelay... |
1638 % no delay at onset | 1544 & time<stimulusParameters.testTargetEnds+windowOffsetDelay); |
1639 idx=find(time>stimulusParameters.testTargetBegins +MacGonsetDelay... | 1545 if strcmp(AN_spikesOrProbability,'spikes') |
1640 & time<stimulusParameters.testTargetEnds+MacGoffsetDelay); | 1546 nSpikesDuringTarget=sum(MacGregorResponse(:,idx)); |
1641 nSpikesDuringTarget=sum(MacGregorResponse(:,idx)); | 1547 timeX=time(idx); |
1642 | 1548 else |
1643 % find(MacGregorResponse)*dt-stimulusParameters.stimulusDelay | 1549 % probability, use channel with the highest average rate |
1644 timeX=time(idx); | 1550 nSpikesDuringTarget=max(mean(ANprobRateOutput(:,idx),2)); |
1645 end | 1551 if nSpikesDuringTarget>ANprobRateOutput(end,1)+10 |
1646 | 1552 nSpikesDuringTarget=1; |
1647 % now find the response of the MacGregor model at the end of the masker | 1553 else |
1648 idx2=find(time>stimulusParameters.testTargetBegins-0.02 ... | 1554 nSpikesDuringTarget=0; |
1649 & time<stimulusParameters.testTargetBegins); | 1555 end |
1650 if ~isempty(idx2) | 1556 |
1651 maskerRate=mean(mean(MacGregorResponse(idx2))); | 1557 end |
1652 else | |
1653 %e.g. no masker | |
1654 maskerRate=0; | |
1655 end | 1558 end |
1656 | 1559 |
1657 if experiment.MAPplot | 1560 if experiment.MAPplot |
1658 % add vertical lines to indicate target region | 1561 % add vertical lines to indicate target region |
1659 figure(99), subplot(6,1,6) | 1562 figure(99), subplot(6,1,6) |
1660 hold on | 1563 hold on |
1661 yL=get(gca,'YLim'); | 1564 yL=get(gca,'YLim'); |
1662 plot([stimulusParameters.testTargetBegins + MacGonsetDelay ... | 1565 plot([stimulusParameters.testTargetBegins + windowOnsetDelay ... |
1663 stimulusParameters.testTargetBegins + MacGonsetDelay],yL,'r') | 1566 stimulusParameters.testTargetBegins + windowOnsetDelay],yL,'r') |
1664 plot([stimulusParameters.testTargetEnds + MacGoffsetDelay ... | 1567 plot([stimulusParameters.testTargetEnds + windowOffsetDelay ... |
1665 stimulusParameters.testTargetEnds + MacGoffsetDelay],yL,'r') | 1568 stimulusParameters.testTargetEnds + windowOffsetDelay],yL,'r') |
1666 end | 1569 end |
1667 | 1570 |
1668 % specify unambiguous response | 1571 % specify unambiguous response |
1669 switch experiment.paradigm | 1572 if nSpikesDuringTarget>experiment.MacGThreshold |
1670 case 'gapDetection' | 1573 modelResponse=2; % stimulus detected |
1671 gapResponse=(maskerRate-nSpikesDuringTarget)/maskerRate; | 1574 else |
1672 if gapResponse>0.2 | 1575 modelResponse=1; % nothing heard (default) |
1673 modelResponse=2; % gap detected | 1576 end |
1674 else | |
1675 modelResponse=1; % gap not detected | |
1676 end | |
1677 [nSpikesDuringTarget maskerRate gapResponse modelResponse] | |
1678 figure(22), plot(timeX,earObject(idx)) | |
1679 otherwise | |
1680 if nSpikesDuringTarget>experiment.MacGThreshold | |
1681 modelResponse=2; % stimulus detected | |
1682 else | |
1683 modelResponse=1; % nothing heard (default) | |
1684 end | |
1685 end | |
1686 | |
1687 | 1577 |
1688 path(savePath) | 1578 path(savePath) |
1689 | 1579 |
1690 % -----------------------------------------------------statsModelRunsGUI | 1580 % -----------------------------------------------------statsModelRunsGUI |
1691 % The computer presses the buttons | 1581 % The computer presses the buttons |
1702 % user has requested an abort | 1592 % user has requested an abort |
1703 experiment.status= 'waitingForStart'; | 1593 experiment.status= 'waitingForStart'; |
1704 addToMsg('manually stopped',1) | 1594 addToMsg('manually stopped',1) |
1705 return | 1595 return |
1706 end | 1596 end |
1707 | 1597 |
1708 % initiates run and plays first stimulus and it returns | 1598 % initiates run and plays first stimulus and it returns |
1709 % without waiting for button press | 1599 % without waiting for button press |
1710 % NB stimulus is not actually generated (for speed) | 1600 % NB stimulus is not actually generated (for speed) |
1711 startNewRun(handles) | 1601 startNewRun(handles) |
1712 | 1602 |
1713 while strcmp(experiment.status,'waitingForResponse') | 1603 while strcmp(experiment.status,'waitingForResponse') |
1714 % create artificial response here | 1604 % create artificial response here |
1715 modelResponse=statsModelGetResponse; | 1605 modelResponse=statsModelGetResponse; |
1716 switch modelResponse | 1606 switch modelResponse |
1717 case 1 | 1607 case 1 |
1736 case {'statsModelLogistic'} | 1626 case {'statsModelLogistic'} |
1737 prob= 1./(1+exp(-statsModel.logisticSlope.*(withinRuns.variableValue-statsModel.logisticMean))); | 1627 prob= 1./(1+exp(-statsModel.logisticSlope.*(withinRuns.variableValue-statsModel.logisticMean))); |
1738 % if experiment.psyFunSlope<0, | 1628 % if experiment.psyFunSlope<0, |
1739 % prob=1-prob; | 1629 % prob=1-prob; |
1740 % end | 1630 % end |
1741 | 1631 |
1742 case 'statsModelRareEvent' | 1632 case 'statsModelRareEvent' |
1743 if experiment.psyFunSlope<0 | 1633 if experiment.psyFunSlope<0 |
1744 addToMsg('statsModelRareEvent cannot be used with negative slope',0) | 1634 addToMsg('statsModelRareEvent cannot be used with negative slope',0) |
1745 error('statsModelRareEvent cannot be used with negative slope') | 1635 error('statsModelRareEvent cannot be used with negative slope') |
1746 end | 1636 end |
1747 | 1637 |
1748 % standard formula is prob = 1 – exp(-d (g P – A)) | 1638 % standard formula is prob = 1 – exp(-d (g P – A)) |
1749 % here A->A; To find Pmin use A/gain | 1639 % here A->A; To find Pmin use A/gain |
1750 pressure=28*10^(withinRuns.variableValue/20); | 1640 pressure=28*10^(withinRuns.variableValue/20); |
1751 gain=statsModel.rareEvenGain; | 1641 gain=statsModel.rareEvenGain; |
1752 A=statsModel.rareEventVmin; | 1642 A=statsModel.rareEventVmin; |
1765 if rand<prob | 1655 if rand<prob |
1766 modelResponse=2; %bingo | 1656 modelResponse=2; %bingo |
1767 else | 1657 else |
1768 modelResponse=1; %nothing heard | 1658 modelResponse=1; %nothing heard |
1769 end | 1659 end |
1770 | 1660 |
1771 case {'2I2AFC++', '2I2AFC+++'} | 1661 case {'2I2AFC++', '2I2AFC+++'} |
1772 if rand<prob | 1662 if rand<prob |
1773 modelResponse=2; %bingo | 1663 modelResponse=2; %bingo |
1774 else %if the stimulus is not audible, take a 50:50 chance of getting it right | 1664 else %if the stimulus is not audible, take a 50:50 chance of getting it right |
1775 if rand<0.5 | 1665 if rand<0.5 |
1857 set(serobj, 'ReadAsyncMode', 'continuous') | 1747 set(serobj, 'ReadAsyncMode', 'continuous') |
1858 set(serobj, 'BytesAvailableFcn', @buttonBox_callback) | 1748 set(serobj, 'BytesAvailableFcn', @buttonBox_callback) |
1859 set(serobj, 'BytesAvailableFcnCount', 1) | 1749 set(serobj, 'BytesAvailableFcnCount', 1) |
1860 set(serobj, 'BytesAvailableFcnMode', 'byte') | 1750 set(serobj, 'BytesAvailableFcnMode', 'byte') |
1861 % set(serobj, 'BreakInterruptFcn', '@buttonBox_Calback') | 1751 % set(serobj, 'BreakInterruptFcn', '@buttonBox_Calback') |
1862 | 1752 |
1863 fopen(serobj); | 1753 fopen(serobj); |
1864 buttonBoxStatus=get(serobj,'status'); | 1754 buttonBoxStatus=get(serobj,'status'); |
1865 catch | 1755 catch |
1866 disp('** no button box found - use mouse **') | 1756 disp('** no button box found - use mouse **') |
1867 end | 1757 end |