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