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.

Statistics Download as Zip
| Branch: | Revision:

root / multithreshold 1.46 / subjGUI_MT.m @ 25:d2c4c07df02c

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
    showMapOptions.surfSpikes=0;
1585
    UTIL_showMAP(options)
1586
end
1587

    
1588
% No response,  probably caused by hitting 'stop' button
1589
if isempty(ICoutput), return, end
1590

    
1591
% MacGregor response is the sum total of all final stage spiking
1592
MacGregorResponse= sum(ICoutput,1);                 % use IC
1593

    
1594
% ---------------------------------------------------------- end model run
1595

    
1596
dt=ANdt;
1597
time=dt:dt:dt*length(MacGregorResponse);
1598

    
1599
% group delay on unit response
1600
MacGonsetDelay= 0.004;
1601
MacGoffsetDelay= 0.022;
1602

    
1603
% now find the response of the MacGregor model during the target presentation + group delay
1604
switch experiment.threshEstMethod
1605
    case {'2I2AFC++', '2I2AFC+++'}
1606
        idx= time>stimulusParameters.testTargetBegins+MacGonsetDelay ...
1607
            & time<stimulusParameters.testTargetEnds+MacGoffsetDelay;
1608
        nSpikesTrueWindow=sum(MacGregorResponse(:,idx));
1609
        idx=find(time>stimulusParameters.testNonTargetBegins+MacGonsetDelay ...
1610
            & time<stimulusParameters.testNonTargetEnds+MacGoffsetDelay);
1611
        nSpikesFalseWindow=sum(MacGregorResponse(:,idx));
1612
        % nSpikesDuringTarget is +ve when more spikes are found
1613
        %   in the target window
1614
        difference= nSpikesTrueWindow-nSpikesFalseWindow;
1615

    
1616
        if difference>0
1617
            % hit
1618
            nSpikesDuringTarget=experiment.MacGThreshold+1;
1619
        elseif    difference<0
1620
            % miss (wrong choice)
1621
            nSpikesDuringTarget=experiment.MacGThreshold-1;
1622
        else
1623
            if rand>0.5
1624
                % hit (random choice)
1625
                nSpikesDuringTarget=experiment.MacGThreshold+1;
1626
            else
1627
                % miss (random choice)
1628
                nSpikesDuringTarget=experiment.MacGThreshold-1;
1629
            end
1630
        end
1631
        disp(['level target dummy decision: ' ...
1632
            num2str([withinRuns.variableValue nSpikesTrueWindow ...
1633
            nSpikesFalseWindow  nSpikesDuringTarget], '%4.0f') ] )
1634

    
1635
    otherwise
1636
        % idx=find(time>stimulusParameters.testTargetBegins+MacGonsetDelay ...
1637
        %         & time<stimulusParameters.testTargetEnds+MacGoffsetDelay);
1638
        % no delay at onset
1639
        idx=find(time>stimulusParameters.testTargetBegins +MacGonsetDelay...
1640
            & time<stimulusParameters.testTargetEnds+MacGoffsetDelay);
1641
        nSpikesDuringTarget=sum(MacGregorResponse(:,idx));
1642
        
1643
        % find(MacGregorResponse)*dt-stimulusParameters.stimulusDelay
1644
        timeX=time(idx);
1645
end
1646

    
1647
% now find the response of the MacGregor model at the end of the masker
1648
idx2=find(time>stimulusParameters.testTargetBegins-0.02 ...
1649
    & time<stimulusParameters.testTargetBegins);
1650
if ~isempty(idx2)
1651
    maskerRate=mean(mean(MacGregorResponse(idx2)));
1652
else
1653
    %e.g. no masker
1654
    maskerRate=0;
1655
end
1656

    
1657
if experiment.MAPplot
1658
    % add vertical lines to indicate target region
1659
    figure(99), subplot(6,1,6)
1660
    hold on
1661
    yL=get(gca,'YLim');
1662
    plot([stimulusParameters.testTargetBegins + MacGonsetDelay ...
1663
        stimulusParameters.testTargetBegins   + MacGonsetDelay],yL,'r')
1664
    plot([stimulusParameters.testTargetEnds   + MacGoffsetDelay ...
1665
        stimulusParameters.testTargetEnds     + MacGoffsetDelay],yL,'r')
1666
end
1667

    
1668
% specify unambiguous response
1669
switch experiment.paradigm
1670
    case 'gapDetection'
1671
        gapResponse=(maskerRate-nSpikesDuringTarget)/maskerRate;
1672
        if gapResponse>0.2
1673
            modelResponse=2;    % gap detected
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

    
1688
path(savePath)
1689

    
1690
% -----------------------------------------------------statsModelRunsGUI
1691
% The computer presses the buttons
1692
function 	statsModelRunsGUI(handles)
1693
% Decision are made at random using a prescribe statistical function
1694
% to set probabilities as a function of signal level.
1695
global experiment
1696

    
1697
experiment.allowCatchTrials=0;
1698

    
1699
while strcmp(experiment.status,'waitingForGO')
1700
    % i.e. waiting for new run
1701
    if experiment.stop
1702
        % user has requested an abort
1703
        experiment.status= 'waitingForStart';
1704
        addToMsg('manually stopped',1)
1705
        return
1706
    end
1707
    
1708
    % initiates run and plays first stimulus and it returns
1709
    %  without waiting for button press
1710
    % NB stimulus is not actually generated (for speed)
1711
    startNewRun(handles)
1712
    
1713
    while strcmp(experiment.status,'waitingForResponse')
1714
        % create artificial response here
1715
        modelResponse=statsModelGetResponse;
1716
        switch modelResponse
1717
            case 1
1718
                %                 userDoesNotHearTarget(handles)
1719
                userDecides(handles, false)
1720
            case 2
1721
                %                 userHearsTarget(handles)
1722
                userDecides(handles, true)
1723
        end
1724
    end
1725
end
1726

    
1727
% -----------------------------------------------------statsModelGetResponse
1728
function modelResponse=statsModelGetResponse(handles)
1729
global experiment  withinRuns  statsModel stimulusParameters
1730
% use the generating function to decide if a detection occurs or not
1731

    
1732
% pause(0.1)  % to allow stopping with CTRL/C but slows things down
1733

    
1734
% first compute the probability that a detection occurs
1735
switch experiment.ear
1736
    case {'statsModelLogistic'}
1737
        prob= 1./(1+exp(-statsModel.logisticSlope.*(withinRuns.variableValue-statsModel.logisticMean)));
1738
        %         if experiment.psyFunSlope<0,
1739
        %             prob=1-prob;
1740
        %         end
1741
        
1742
    case 'statsModelRareEvent'
1743
        if experiment.psyFunSlope<0
1744
            addToMsg('statsModelRareEvent cannot be used with negative slope',0)
1745
            error('statsModelRareEvent cannot be used with negative slope')
1746
        end
1747
        
1748
        % standard formula is prob = 1 ? exp(-d (g P ? A))
1749
        % here A->A;  To find Pmin use A/gain
1750
        pressure=28*10^(withinRuns.variableValue/20);
1751
        gain=statsModel.rareEvenGain;
1752
        A=statsModel.rareEventVmin;
1753
        d=stimulusParameters.targetDuration;
1754
        gP_Vmin=gain*pressure-A;
1755
        if gP_Vmin>0
1756
            prob=1-exp(-d*(gP_Vmin));
1757
        else
1758
            prob=0;
1759
        end
1760
end
1761

    
1762
% Use the probability to choose whether or not a detection has occurred
1763
switch experiment.threshEstMethod
1764
    case {'MaxLikelihood', 'oneIntervalUpDown'}
1765
        if rand<prob
1766
            modelResponse=2; %bingo
1767
        else
1768
            modelResponse=1; %nothing heard
1769
        end
1770
        
1771
    case {'2I2AFC++', '2I2AFC+++'}
1772
        if rand<prob
1773
            modelResponse=2; %bingo
1774
        else %if the stimulus is not audible, take a 50:50 chance of getting it right
1775
            if rand<0.5
1776
                modelResponse=2; %bingo
1777
            else
1778
                modelResponse=1; %nothing heard
1779
            end
1780
        end
1781
end
1782

    
1783

    
1784
% ------------------------------------------------------- printTabTable
1785
function printTabTable(M, headers)
1786
% printTabTable prints a matrix as a table with tabs
1787
%headers are optional
1788
%headers=strvcat('firstname', 'secondname')
1789
%  printTabTable([1 2; 3 4],strvcat('a1','a2'));
1790

    
1791
if nargin>1
1792
    [r c]=size(headers);
1793
    for no=1:r
1794
        fprintf('%s\t',headers(no,:))
1795
    end
1796
    fprintf('\n')
1797
end
1798

    
1799
[r c]=size(M);
1800

    
1801
for row=1:r
1802
    for col=1:c
1803
        if row==1 && col==1 && M(1,1)==-1000
1804
            %   Print nothing (tab follows below)
1805
        else
1806
            fprintf('%s',num2str(M(row,col)))
1807
        end
1808
        if col<c
1809
            fprintf('\t')
1810
        end
1811
    end
1812
    fprintf('\n')
1813
end
1814

    
1815
% ------------------------------------------------------- xlimRM
1816
function xlimRM(x)
1817
try
1818
    xlim([x(1) x(2)])
1819
catch
1820
end
1821

    
1822
% ------------------------------------------------------- ylimRM
1823
function ylimRM(x)
1824
try
1825
    ylim([x(1) x(2)])
1826
catch
1827
end
1828

    
1829

    
1830
function editdigitInput_CreateFcn(hObject, eventdata, handles)
1831

    
1832
% Hint: edit controls usually have a white background on Windows.
1833
%       See ISPC and COMPUTER.
1834
if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor'))
1835
    set(hObject,'BackgroundColor','white');
1836
end
1837

    
1838

    
1839
% -----------------------------------------------------buttonBoxIntitialize
1840
function buttonBoxIntitialize
1841
% initialize button box
1842
global  serobj
1843
try
1844
    fclose(serobj);
1845
catch
1846
end
1847

    
1848
try
1849
    serobj = serial('COM4') ;           	% Creating serial port object now its connected to COM4 !!! button boxes in booths are connected to COM2
1850
    serobj.Baudrate = 9600;           		% Set the baud rate at the specific value
1851
    set(serobj, 'Parity', 'none') ;     	% Set parity as none
1852
    set(serobj, 'Databits', 8) ;          	% set the number of data bits
1853
    set(serobj, 'StopBits', 1) ;         	% set number of stop bits as 1
1854
    set(serobj, 'Terminator', 'CR') ; 		% set the terminator value to carriage return
1855
    set(serobj, 'InputBufferSize', 512) ;  	% Buffer for read operation, default it is 512
1856
    set(serobj,'timeout',10);           	% 10 sec timeout on button press
1857
    set(serobj, 'ReadAsyncMode', 'continuous')
1858
    set(serobj, 'BytesAvailableFcn', @buttonBox_callback)
1859
    set(serobj, 'BytesAvailableFcnCount', 1)
1860
    set(serobj, 'BytesAvailableFcnMode', 'byte')
1861
    % set(serobj, 'BreakInterruptFcn', '@buttonBox_Calback')
1862
    
1863
    fopen(serobj);
1864
    buttonBoxStatus=get(serobj,'status');
1865
catch
1866
    disp('** no button box found - use mouse **')
1867
end