-
Notifications
You must be signed in to change notification settings - Fork 1
/
flankersRun.m
195 lines (188 loc) · 6.98 KB
/
flankersRun.m
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
% flankersRun.m
% Show target with flankers. Measure psychometric function: probability of
% identifying the target as a function of flanker contrast. (with and
% without noise).
% Letter target surrounded by letter flankers.
% Noise annulus on flankers only.
% P=0.75, assuming 9 alternatives
% luminance 250 cd/m2
% binocular, 20 deg right.
% March, 2018
% Denis Pelli
%% GET READY
clear o oo
skipDataCollection=false; % Enable skipDataCollection to check plotting before we have data.
o.questPlusEnable=false;
if verLessThan('matlab','R2013b')
error('This MATLAB is too old. We need MATLAB 2013b or better to use the function "struct2table".');
end
if o.questPlusEnable && ~exist('qpInitialize','file')
error('This script requires the QuestPlus package. Please get it from https://github.com/BrainardLab/mQUESTPlus.')
end
addpath(fullfile(fileparts(mfilename('fullpath')),'lib')); % folder in same directory as this M file
cal=OurScreenCalibrations(0);
%% CREATE LIST OF CONDITIONS TO BE TESTED
% o.useFractionOfScreenToDebug=0.4; % 0: normal, 0.5: small for debugging.
o.seed=[]; % Fresh.
% o.seed=uint32(1506476580); % Copy seed value here to reproduce an old table of conditions.
o.isLuminanceRangeSymmetric=true;
o.isNoiseDynamic=true;
if true
o.useFlankers=true;
% o.thresholdParameter='flankerContrast';
o.thresholdParameter='contrast';
o.task='identifyAll';
end
o.contrast=-0.2;
o.flankerContrast=-0.85; % Negative for dark letters.
o.flankerArrangement='radialAndTangential';
o.flankerArrangement='radial';
o.annularNoiseSD=0;
o.flankerSpacingDeg=3;
o.noiseRadiusDeg=inf;
o.annularNoiseEnvelopeRadiusDeg=o.flankerSpacingDeg;
o.noiseEnvelopeSpaceConstantDeg=o.flankerSpacingDeg/2;
o.annularNoiseBigRadiusDeg=inf;
o.annularNoiseSmallRadiusDeg=0;
o.experiment='flankers';
o.conditionName='P target id. vs. flanker contrast';
o.eccentricityXYDeg=[15 0];
o.nearPointXYInUnitSquare=[0.8 0.5];
o.targetHeightDeg=2;
o.targetDurationSec=0.2;
o.desiredLuminance=[];
o.desiredLuminanceFactor=1;
o.constantStimuli=[-0.01 -0.03 -0.1 -0.3 -0.7];
o.trialsDesired=50*length(o.constantStimuli);
o.useMethodOfConstantStimuli=true;
% o.minScreenWidthDeg=10;
o.eyes='both';
% for noiseSD=Shuffle([0 0.16])
o.condition=1;
for noiseSD=[0]
o.noiseCheckDeg=o.targetHeightDeg/20;
o.noiseSD=noiseSD;
if o.condition==1
oo=o;
else
oo(o.condition)=o;
end
o.condition=o.condition+1;
end
%% PRINT THE LIST OF CONDITIONS (ONE PER ROW)
t=struct2table(oo,'AsArray',true);
% We list parameters here in the order that we want them to appear as
% columns in the table, which we print in the Command Window. Currently we
% do not save the table.
vars={'condition' 'experiment' 'noiseSD' 'flankerSpacingDeg' 'eccentricityXYDeg' 'contrast' 'constantStimuli' 'thresholdParameter'};
disp(t(:,vars)) % Print the oo list of conditions.
%% RUN THE CONDITIONS
if ~skipDataCollection && true
% Typically, you'll select just a few of the conditions stored in oo
% that you want to run now. Select them from the printout of "t" in your
% Command Window.
clear oOut
for oi=1:length(oo) % Edit this line to select which conditions to run now.
o=oo(oi);
if exist('oOut','var')
% Reuse answers from immediately preceding block.
o.experimenter=oOut.experimenter;
o.observer=oOut.observer;
% Setting o.useFilter false forces o.filterTransmission=1.
o.filterTransmission=oOut.filterTransmission;
end
o.fixationBlankingRadiusReEccentricity=0; % No blanking.
if true
% Target letter
o.targetKind='letter';
o.targetFont='Sloan';
o.alphabet='DHKNORSVZ';
else
% Target gabor
o.targetKind='gabor';
o.targetGaborOrientationsDeg=[0 45 90 135];
o.targetGaborNames='1234';
o.alphabet=o.targetGaborNames;
end
o.alternatives=length(o.alphabet);
if all(o.eccentricityXYDeg==0)
o.isTargetLocationMarked=false;
else
o.isTargetLocationMarked=true;
end
o.fixationBlankingRadiusReTargetHeight=0;
o.moviePreSec=0.2;
o.moviePostSec=0.2;
o.targetMarkDeg=1;
o.fixationMarkDeg=3;
if 0
% Use QuestPlus to measure steepness.
o.questPlusEnable=true;
o.questPlusSteepnesses=1:0.1:5;
o.questPlusGuessingRates=1/o.alternatives;
o.questPlusLapseRates=0:0.01:0.05;
o.questPlusLogIntensities=-2.5:0.05:0.5;
o.questPlusPrint=true;
o.questPlusPlot=true;
end
oOut=NoiseDiscrimination(o);
oo(oi).trials=oOut.trials; % Always defined.
if isfield(oOut,'psych')
fprintf(['<strong>%s: noiseSD %.2f, log N %.2f, flankerSpacingDeg %.1f, '...
'target contrast %.3f, threshold flankerContrast %.3f</strong>\n'],...
oOut.conditionName,oOut.noiseSD,log10(oOut.N),oOut.flankerSpacingDeg,...
oOut.contrast,oOut.flankerContrast);
oo(oi).experimenter=oOut.experimenter;
oo(oi).observer=oOut.observer;
oo(oi).filterTransmission=oOut.filterTransmission;
oo(oi).flankerContrast=oOut.flankerContrast;
oo(oi).contrast=oOut.contrast;
oo(oi).N=oOut.N;
oo(oi).E1=oOut.E1;
oo(oi).alphabet=oOut.alphabet;
oo(oi).alternatives=oOut.alternatives;
oo(oi).targetKind=oOut.targetKind;
oo(oi).eyes=oOut.eyes;
oo(oi).LBackground=oOut.LBackground;
oo(oi).targetDurationSec=oOut.targetDurationSec;
oo(oi).eccentricityXYDeg=oOut.eccentricityXYDeg;
oo(oi).targetCyclesPerDeg=oOut.targetCyclesPerDeg;
oo(oi).data=oOut.data;
oo(oi).psych=oOut.psych;
oo(oi).transcript=oOut.transcript;
oo(oi).dataFolder=oOut.dataFolder;
oo(oi).dataFilename=oOut.dataFilename;
oo(oi).trialsSkipped=oOut.trialsSkipped;
end
if oOut.quitExperiment
break
end
end
fprintf('\n');
%% PRINT THE RESULTS
t=struct2table(oo(1:oi),'AsArray',true);
rows=t.trials>0;
vars={'condition' 'observer' 'trials' 'trialsSkipped' 'noiseSD' 'N' 'flankerSpacingDeg' 'eccentricityXYDeg' 'contrast' 'flankerContrast'};
if any(rows)
disp(t(rows,vars)) % Print the oo list of conditions, with measured flanker threshold.
end
%% PLOT IT
if isfield(oo(1),'psych') && isfield(oo(1).psych,'t')
close all % Get rid of any existing figures.
for oi=1:length(oo)
o=oo(oi);
disp(t(oi,vars))
% FIT PSYCHOMETRIC FUNCTION
clear QUESTPlusFit % Clear the persistent variables.
o.alternatives=length(o.alphabet);
o.questPlusLapseRates=0:0.01:0.05;
o.questPlusGuessingRates=0:0.03:0.3;
o.questPlusSteepnesses=[1:0.5:5 6:10];
oOut=QUESTPlusFit(o);
o.plotFilename=[o.dataFilename '.plot'];
file=fullfile(o.dataFolder,[o.plotFilename '.eps']);
saveas(gcf,file,'epsc')
fprintf('Plot saved as "%s".\n',file);
end
end % if isfield(oo(1),'psych') && isfield(o.psych,'t')
end