-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathGRFIntertrialState.m
More file actions
197 lines (170 loc) · 6.4 KB
/
GRFIntertrialState.m
File metadata and controls
197 lines (170 loc) · 6.4 KB
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
196
197
//
// GRFIntertrialState.m
// Experiment
//
// Copyright (c) 2006. All rights reserved.
//
#import "GRFIntertrialState.h"
#import "UtilityFunctions.h"
@implementation GRFIntertrialState
- (void)dumpTrial;
{
NSLog(@"\n catch numStim targetIndex");
NSLog(@"%d %ld %ld\n", trial.catchTrial, trial.numStim, trial.targetIndex);
}
- (void)stateAction;
{
[[task dataController] startDevice];
expireTime = [LLSystemUtil timeFromNow:[[task defaults] integerForKey:GRFIntertrialMSKey]];
eotCode = kMyEOTCorrect; // default eot code is correct
brokeDuringStim = NO; // flag for fixation break during stimulus presentation
updateBlockStatus();
[[task dataDoc] putEvent:@"blockStatus" withData:(void *)&blockStatus];
[[task dataDoc] putEvent:@"mappingBlockStatus" withData:(void *)&mappingBlockStatus];
if (![self selectTrial] || mappingBlockStatus.blocksDone >= mappingBlockStatus.blockLimit) {
[task setMode:kTaskIdle]; // all blocks have been done
return;
}
// [self dumpTrial];
[stimuli makeStimLists:&trial];
// [stimuli dumpStimList];
}
- (NSString *)name {
return @"GRFIntertrial";
}
- (LLState *)nextState {
if ([task mode] == kTaskIdle) {
eotCode = kMyEOTQuit;
return [[task stateSystem] stateNamed:@"Endtrial"];
}
else if ([LLSystemUtil timeIsPast:expireTime]) {
return [[task stateSystem] stateNamed:@"GRFStarttrial"];
}
return nil;
}
// Decide which trial type to do next
- (BOOL)selectTrial;
{
long targetIndex, maxTargetIndex;
long index, repsDone, repsNeeded;
long stimulusMS, interstimMS, reactMS;
long minTargetMS, maxTargetMS;
BOOL isCatchTrial, valid;
float minTargetS, maxTargetS, meanTargetS, meanRateHz, lambda;
float u, targetOnsetS;
float catchTrialPC, catchTrialMaxPC;
extern long argRand;
BlockStatus *pBS = &blockStatus;
// First check that the user hasn't changed any of the entries affecting block size
for (index = repsDone = repsNeeded = 0; index < pBS->changes; index++) {
repsNeeded += pBS->validReps[index] + pBS->invalidReps[index];
repsDone += pBS->validRepsDone[index] + pBS->invalidRepsDone[index];
}
if (repsDone >= repsNeeded) {
for (index = 0; index < pBS->changes; index++) {
pBS->validRepsDone[index] = pBS->invalidRepsDone[index] = 0;
}
pBS->instructDone = 0;
if (++(pBS->sidesDone) >= kLocations) {
pBS->blocksDone++;
pBS->sidesDone = 0;
}
}
// If we have done all the requested blocks, return now
if (pBS->blocksDone >= pBS->blockLimit) {
NSLog(@"select trial stopping because blocksDone is %ld and blocklimit is %ld", pBS->blocksDone, pBS->blockLimit);
return NO;
}
// [Vinay] - check whether it is a tfunc protocol running and if so decide the trial type i.e. eyes open or closed trial
//[Vinay] - adding lines for the Transfer Function Protocol
long taskSelectorRand = 4;
eyesClosed = NO;
if ([[task defaults] boolForKey:GRFTFProtocolKey]) {
taskSelectorRand = arc4random_uniform(2);
if (taskSelectorRand != 0) {
taskSelectTFP = kOpenTask;
eyesClosed = NO;
}
else if (taskSelectorRand == 0) {
taskSelectTFP = kCloseTask;
eyesClosed = YES;
}
}
NSLog(@"This trial random number and eyes closed: %ld and %ld", taskSelectorRand, taskSelectTFP);
// [Vinay] - till here
// determin target onset time and whether it is a catch trial
// Pick a stimulus count for the target, using an exponential distribution
stimulusMS = [[task defaults] integerForKey:GRFStimDurationMSKey];
interstimMS = [[task defaults] integerForKey:GRFInterstimMSKey];
minTargetMS = [[task defaults] integerForKey:GRFMinTargetMSKey];
maxTargetMS = [[task defaults] integerForKey:GRFMaxTargetMSKey];
meanTargetS = [[task defaults] integerForKey:GRFMeanTargetMSKey] / 1000.0;;
minTargetS = minTargetMS / 1000.0;
maxTargetS = maxTargetMS / 1000.0;
reactMS = [[task defaults] integerForKey:GRFRespTimeMSKey];
catchTrialPC = [[task defaults] floatForKey:GRFCatchTrialPCKey];
catchTrialMaxPC = [[task defaults] floatForKey:GRFCatchTrialMaxPCKey];
// Decide which orientation change to do, and whether it will be valid or invalid
trial.instructTrial = (pBS->instructDone < pBS->instructTrials);
for (;;) {
index = rand() % pBS->changes;
valid = (trial.instructTrial) ? YES : rand() % 2;
if (valid) {
if (pBS->validRepsDone[index] < pBS->validReps[index]) {
break;
}
}
else {
if (pBS->invalidRepsDone[index] < pBS->invalidReps[index]) {
break;
}
}
}
trial.orientationChangeIndex = index;
// Decide whether to use uniform or exponential distribution of target times
meanRateHz = 1000.0 / (stimulusMS + interstimMS);
maxTargetIndex = maxTargetS * meanRateHz; // last position for target
isCatchTrial = NO;
switch ([[task defaults] integerForKey:GRFStimDistributionKey]) {
case kUniform:
targetIndex = round(((minTargetMS + (rand() % (maxTargetMS - minTargetMS +1))) / 1000.0) * meanRateHz);
if (!trial.instructTrial && (rand() % 1000) <= (catchTrialPC * 10.0)) {
isCatchTrial = YES;
}
break;
case kExponential:
default:
/*
To minimize the lower occurence of the first target caused by roundoff,
the exponential pdf is advanced by half the stimulus period (i.e., stimulus duration + interstimulus interval).
To make the mean target onset time closer to what is specified by the user, the lambda of the exponential
is scaled by (mean target onset - minimum target onset + half the stimulus period).
*/
lambda = log(2.0) / (meanTargetS - minTargetS + 0.5 / meanRateHz); // lambda of exponential distribution
for (;;) {
do {
u = randUnitInterval(&argRand); // from Press et al. (1992), use when understood
targetOnsetS = -1.0 * log(1.0 - u) / lambda + (minTargetS - 0.5 / meanRateHz);
// inverse cdf of the exponential target onset distribution
} while ((targetOnsetS > (maxTargetS + 0.5 / meanRateHz)) && trial.instructTrial);
if (targetOnsetS > (maxTargetS + 0.5 / meanRateHz)) {
if ((rand() % 1000) < (catchTrialPC / catchTrialMaxPC * 1000.0)) {
targetIndex = maxTargetIndex;
isCatchTrial = YES;
break;
}
}
else {
targetIndex = round(targetOnsetS * meanRateHz);
break;
}
}
break;
}
trial.catchTrial = isCatchTrial;
trial.targetIndex = targetIndex;
trial.orientationChangeDeg = pBS->orientationChangeDeg[trial.orientationChangeIndex];
trial.numStim = targetIndex + reactMS / 1000.0 * meanRateHz + 1;
return YES;
}
@end