00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033
00034
00035
00036
00037
00038
00039
00040 #include "Component/ModelManager.H"
00041 #include "Image/Image.H"
00042 #include "Raster/Raster.H"
00043 #include "Media/MPEGStream.H"
00044 #include "Media/MediaOpts.H"
00045 #include "Psycho/PsychoDisplay.H"
00046 #include "Psycho/EyeTrackerConfigurator.H"
00047 #include "Psycho/EyeTracker.H"
00048 #include "Psycho/PsychoOpts.H"
00049 #include "Component/EventLog.H"
00050 #include "Component/ComponentOpts.H"
00051 #include "Util/MathFunctions.H"
00052 #include "Util/Types.H"
00053 #include "Video/VideoFrame.H"
00054 #include "rutz/time.h"
00055
00056 #include <deque>
00057 #include <sstream>
00058
00059
00060 static int waitForFixation(const Point2D<int> fparr [],
00061 const int arraylen,
00062 const int radius,
00063 double fixlen,
00064 nub::soft_ref<EyeTracker> et,
00065 nub::soft_ref<PsychoDisplay> d,
00066 const bool do_drift_correction = false)
00067 {
00068 Point2D<int> ip;
00069 double dist [arraylen], sdist;
00070 double tt=0, adur=0, ax=0, ay=0;
00071 Timer timer, timer2;
00072 fixlen = fixlen/1000;
00073 int status = 1;
00074
00075
00076 while(d->checkForKey() != -1);
00077
00078
00079 timer2.reset();
00080 while(timer.getSecs() < fixlen) {
00081 ip = et->getFixationPos();
00082
00083
00084 for (int i=0; i<arraylen; i++)
00085 dist[i] = sqrt(pow(ip.i-fparr[i].i, 2) + pow(ip.j-fparr[i].j, 2));
00086
00087
00088 if (arraylen == 1)
00089 sdist = dist[0];
00090 else
00091 sdist = std::min(dist[0], dist[1]);
00092
00093
00094 if (sdist > radius && (ip.i!=-1 && ip.j!=-1)){
00095 timer.reset();
00096 }else{
00097 tt = timer.getSecs()-tt;
00098 adur += tt;
00099 ax += tt * ip.i;
00100 ay += tt * ip.j;
00101 }
00102
00103
00104
00105
00106 if (timer2.getSecs() > 5 || d->checkForKey() > 0){
00107 d->pushEvent("bad trial - time out / no response.");
00108 status = -1;
00109 break;
00110 }
00111 }
00112
00113
00114 if (status == 1 && do_drift_correction == true) {
00115
00116 et->manualDriftCorrection(Point2D<double>(ax/adur, ay/adur),
00117 Point2D<double>(fparr[0].i, fparr[0].j));
00118 LINFO("drift correction: (%i, %i) (%i, %i)", (int)(ax/adur), (int)(ay/adur), fparr[0].i, fparr[0].j);
00119 }
00120
00121 return status;
00122 }
00123
00124
00125 static int submain(const int argc, char** argv)
00126 {
00127 MYLOGVERB = LOG_INFO;
00128
00129
00130 ModelManager manager("Psycho Pro-Saccade");
00131
00132
00133 nub::soft_ref<InputMPEGStream> mp
00134 (new InputMPEGStream(manager, "Input MPEG Stream", "InputMPEGStream"));
00135 manager.addSubComponent(mp);
00136
00137 nub::soft_ref<EventLog> el(new EventLog(manager));
00138 manager.addSubComponent(el);
00139
00140 nub::soft_ref<EyeTrackerConfigurator>
00141 etc(new EyeTrackerConfigurator(manager));
00142 manager.addSubComponent(etc);
00143
00144 nub::soft_ref<PsychoDisplay> d(new PsychoDisplay(manager));
00145 manager.addSubComponent(d);
00146
00147 manager.setOptionValString(&OPT_InputMPEGStreamPreload, "true");
00148 manager.setOptionValString(&OPT_EventLogFileName, "psychodata.psy");
00149 manager.setOptionValString(&OPT_EyeTrackerType, "EL");
00150
00151
00152 if (manager.parseCommandLine(argc, argv, "<# of trials> <block(0) or interleave(1)>", 2, 2) == false)
00153 return(1);
00154
00155
00156
00157
00158
00159
00160 int ntrial = fromStr<int>(manager.getExtraArg(0).c_str());
00161 int interleave = fromStr<int>(manager.getExtraArg(1).c_str());
00162
00163 LINFO("Total Trials: %i", ntrial);
00164 int trials[ntrial];
00165 int trialindex[ntrial];
00166 if(interleave==1){
00167
00168 for (int i=0; i< ntrial; i++)
00169 trials[i] = 1 + i % 4;
00170
00171 for (int i = 0; i < ntrial; i ++)
00172 trialindex[i] = i;
00173 randShuffle(trialindex, ntrial);
00174 }else{
00175
00176 int ntrial4 = ntrial/4;
00177 int tmptrials[4][ntrial4];
00178 int tmpidx[ntrial4];
00179 int blockidx[4] = {0, 1, 0, 1};
00180 for (int i=0; i<4; i++){
00181 for (int j=0; j<ntrial4; j++){
00182 if (i%2==0)
00183 tmptrials[i][j] = 1 + j%2;
00184 else
00185 tmptrials[i][j] = 3 + j%2;
00186 }
00187 }
00188 for (int i=0; i<ntrial4; i++)
00189 tmpidx[i] = i;
00190
00191
00192 do {
00193 randShuffle(blockidx, 4);
00194 } while(blockidx[0] == blockidx[1]);
00195
00196
00197 int c = 0;
00198 for (int i=0; i<4; i++){
00199 randShuffle(tmpidx, ntrial4);
00200 for (int j=0; j<ntrial4; j++){
00201 trials[c] = tmptrials[blockidx[i]][tmpidx[j]];
00202 trialindex[c] = c;
00203 c++;
00204 }
00205 }
00206 }
00207
00208
00209 nub::soft_ref<EyeTracker> et = etc->getET();
00210 d->setEyeTracker(et);
00211 d->setEventLog(el);
00212 et->setEventLog(el);
00213
00214
00215 if (etc->getModelParamString("EyeTrackerType").compare("EL") == 0)
00216 d->setModelParamVal("SDLslaveMode", true);
00217
00218
00219 manager.start();
00220
00221
00222 LINFO("*****************************************************************************");
00223 LINFO("visual acuity test: [r] to regenerate another string; other keys to continue.");
00224 int key;
00225 do {
00226 d->displayRandomText(6, 8);
00227 key = d->waitForKey(true);
00228 } while(key == 114 || key == 101 || key == 116 || key == 102);
00229
00230
00231 d->pushEventBegin("Calibration");
00232 et->setBackgroundColor(d);
00233 et->calibrate(d);
00234 d->pushEventEnd("Calibration");
00235
00236 LINFO("Press any key to start......");
00237 d->displayText("Press any key to start......", true, 0, 10);
00238 d->waitForKey(true);
00239
00240
00241 const int x = d->getWidth()/2;
00242 const int y = d->getHeight()/2;
00243 const int dist = x;
00244 int trialtype = 0;
00245 int tx = x, txt = x;
00246 const PixRGB<byte> green = PixRGB<byte>(0,255,0);
00247 const PixRGB<byte> yellow = PixRGB<byte>(255,255,0);
00248
00249 Point2D<int> targetpoint = Point2D<int>(0,y);
00250
00251 Timer timer;
00252 const int fixation_tolerance = 135;
00253 int displayTask = 1;
00254
00255 for (int i=0; i < ntrial; i++) {
00256 trialtype = trials[trialindex[i]];
00257
00258
00259 d->clearScreen();
00260
00261 if (i%10 == 0){
00262 et->recalibrate(d,13);
00263 d->clearScreen();
00264 }
00265
00266 double delay = 1000000;
00267 if (trialtype == 1 || trialtype == 2){
00268
00269 if (displayTask == 1){
00270 d->displayText("Same", true, 0, 10);
00271 if (interleave == 1)
00272 usleep(1000000);
00273 else
00274 d->waitForKey();
00275 }
00276
00277
00278 et->track(true);
00279 usleep(500000);
00280
00281
00282 d->clearScreen();
00283 if (trialtype == 1)
00284 d->pushEvent("====== Trial Type: 1 (leftward, pro-saccade) ======");
00285 else
00286 d->pushEvent("====== Trial Type: 2 (rightward, pro-saccade) ======");
00287
00288
00289 d->displayFixation(x+dist/2, y, false);
00290 d->displayFixation(x-dist/2, y, false);
00291 d->displayFilledCircle(x, y, 5, green);
00292 d->pushEvent("Display cue (green)");
00293 usleep(delay);
00294
00295
00296 tx = x + dist * (trialtype-1.5);
00297 timer.reset();
00298
00299
00300 d->displayFilledCircle(tx, y, 5, green);
00301 d->pushEvent("Display target (green)");
00302
00303
00304 targetpoint.i = tx;
00305 waitForFixation(&targetpoint, 1, fixation_tolerance, 100, et, d);
00306 double tt = timer.getSecs();
00307
00308 LINFO("(pro-saccade) response time: %f", tt);
00309 d->pushEvent(sformat("pro-saccade, response time: %f", tt));
00310
00311 d->displayCircle(targetpoint.i, targetpoint.j, 60, yellow);
00312 usleep(1000000);
00313
00314
00315 d->clearScreen();
00316
00317 } else if (trialtype == 3 || trialtype == 4){
00318
00319 if (displayTask == 1){
00320 d->displayText("Opposite", true, 0, 10);
00321 if (interleave == 1)
00322 usleep(1000000);
00323 else
00324 d->waitForKey();
00325 }
00326
00327
00328 et->track(true);
00329 usleep(500000);
00330
00331
00332 d->clearScreen();
00333 if (trialtype == 3)
00334 d->pushEvent("====== Trial Type: 3 (leftward, anti-saccade) ======");
00335 else
00336 d->pushEvent("====== Trial Type: 4 (rightward, anti-saccade) ======");
00337
00338
00339 d->displayFixation(x+dist/2, y, false);
00340 d->displayFixation(x-dist/2, y, false);
00341 d->displayFilledCircle(x, y, 5, green);
00342 d->pushEvent("Display cue (green)");
00343 usleep(delay);
00344
00345
00346 tx = x + dist * (-1*(trialtype-3.5));
00347 txt = x + dist * (trialtype-3.5);
00348 timer.reset();
00349
00350
00351 d->displayFilledCircle(tx, y, 5, green);
00352 d->pushEvent("Display target (green)");
00353
00354
00355 targetpoint.i = txt;
00356 waitForFixation(&targetpoint, 1, fixation_tolerance, 100, et, d);
00357 double tt = timer.getSecs();
00358
00359 LINFO("(anti-saccade) response time: %f", tt);
00360 d->pushEvent(sformat("anti-saccade, response time: %f", tt));
00361
00362 d->displayCircle(targetpoint.i, targetpoint.j, 60, yellow);
00363 usleep(1000000);
00364
00365
00366 d->clearScreen();
00367 }else{
00368
00369 d->displayColorDotFixation(x, y, PixRGB<byte>(0,0,255));
00370 return(1);
00371 }
00372
00373
00374 usleep(50000);
00375 et->track(false);
00376
00377
00378 double completion = 100*((double)i+1)/double(ntrial);
00379
00380 if ((int)completion % 5 == 0 && completion-(int)completion==0){
00381 d->clearScreen();
00382 d->displayText(sformat("%i %% completed", (int)completion), true, 0, 10);
00383 usleep(1000000);
00384 }
00385
00386 if (interleave == 0)
00387 displayTask = 0;
00388
00389
00390 if ((int)completion%25 == 0 && i<ntrial-1 && completion-(int)completion==0) {
00391 d->clearScreen();
00392 d->displayText("Please Take a Break", true, 0, 10);
00393 LINFO("Break time. Press [Space] to continue, or [ESC] to terminate the experiment.");
00394 d->waitForKey(true);
00395 d->displayText("Calibration", true, 0, 10);
00396 d->pushEventBegin("Calibration");
00397 et->calibrate(d);
00398 d->pushEventEnd("Calibration");
00399 displayTask = 1;
00400 }
00401 }
00402
00403 d->clearScreen();
00404 d->displayText("Experiment complete. Thank you!", true, 0, 10);
00405 d->waitForKey(true);
00406
00407
00408 manager.stop();
00409
00410
00411 return 0;
00412 }
00413
00414
00415 extern "C" int main(const int argc, char** argv)
00416 {
00417
00418
00419
00420
00421 try
00422 {
00423 return submain(argc, argv);
00424 }
00425 catch (...)
00426 {
00427 REPORT_CURRENT_EXCEPTION;
00428 }
00429
00430 return 1;
00431 }
00432
00433
00434
00435
00436
00437