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