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 "SDL/SDL_rotozoom.h"
00054
00055 #include <deque>
00056 #include <sstream>
00057
00058
00059
00060
00061
00062
00063
00064
00065
00066
00067
00068
00069
00070
00071
00072
00073
00074
00075
00076
00077
00078
00079
00080
00081
00082
00083
00084
00085
00086
00087
00088
00089
00090
00091
00092
00093
00094
00095
00096
00097
00098
00099
00100
00101
00102
00103
00104
00105
00106
00107
00108
00109
00110
00111
00112
00113
00114
00115
00116
00117
00118
00119
00120
00121
00122
00123
00124
00125
00126 static int submain(const int argc, char** argv)
00127 {
00128 MYLOGVERB = LOG_INFO;
00129
00130
00131 ModelManager manager("Psycho Oculomotor Capture");
00132
00133
00134 nub::soft_ref<InputMPEGStream> mp
00135 (new InputMPEGStream(manager, "Input MPEG Stream", "InputMPEGStream"));
00136 manager.addSubComponent(mp);
00137
00138 nub::soft_ref<EventLog> el(new EventLog(manager));
00139 manager.addSubComponent(el);
00140
00141 nub::soft_ref<EyeTrackerConfigurator>
00142 etc(new EyeTrackerConfigurator(manager));
00143 manager.addSubComponent(etc);
00144
00145 nub::soft_ref<PsychoDisplay> d(new PsychoDisplay(manager));
00146 manager.addSubComponent(d);
00147
00148 manager.setOptionValString(&OPT_InputMPEGStreamPreload, "true");
00149 manager.setOptionValString(&OPT_EventLogFileName, "psychodata.psy");
00150 manager.setOptionValString(&OPT_EyeTrackerType, "EL");
00151
00152
00153 if (manager.parseCommandLine(argc, argv, "<# of trials>", 1, 1) == false)
00154 return(1);
00155
00156
00157 int ntrial = fromStr<int>(manager.getExtraArg(0).c_str());
00158 LINFO("Total Trials: %i", ntrial);
00159 int trials[ntrial];
00160 for (int i=0; i< ntrial; i++)
00161 trials[i] = i % 6;
00162
00163
00164 int trialindex[ntrial];
00165 for (int i = 0; i < ntrial; i ++)
00166 trialindex[i] = i;
00167 randShuffle(trialindex, ntrial);
00168
00169
00170 nub::soft_ref<EyeTracker> et = etc->getET();
00171 d->setEyeTracker(et);
00172 d->setEventLog(el);
00173 et->setEventLog(el);
00174
00175
00176 if (etc->getModelParamString("EyeTrackerType").compare("EL") == 0)
00177 d->setModelParamVal("SDLslaveMode", true);
00178
00179
00180 manager.start();
00181
00182
00183 LINFO("*****************************************************************************");
00184 LINFO("visual acuity test: [r] to regenerate another string; other keys to continue.");
00185 int key;
00186 do {
00187 d->displayRandomText(6, 8);
00188 key = d->waitForKey(true);
00189 } while(key == 114 || key == 101 || key == 116 || key == 102);
00190
00191
00192 d->pushEventBegin("Calibration");
00193 et->setBackgroundColor(d);
00194 et->calibrate(d);
00195 d->pushEventEnd("Calibration");
00196
00197 LINFO("Press any key to start......");
00198 d->displayText("Press any key to start......", true, 0, 10);
00199 d->waitForKey(true);
00200
00201
00202 const int x = d->getWidth()/2;
00203 const int y = d->getHeight()/2;
00204 const int bradius = x/2.5;
00205 const int sradius = 66;
00206 const int isradius = sradius - 8;
00207
00208
00209 const PixRGB<byte> tcolor = PixRGB<byte>(0,255,0);
00210 const PixRGB<byte> dcolor = PixRGB<byte>(255,0,0);
00211 const PixRGB<byte> black = PixRGB<byte>(255,255,255);
00212 const PixRGB<byte> bcolor = d->getGrey();
00213
00214
00215
00216 int tcirpos[6][2];
00217 int dcirpos[6][2];
00218 const double PI = 3.141592;
00219 for (int i=0; i<6; i++) {
00220
00221 tcirpos[i][0] = x + bradius * cos(i*PI/3);
00222 tcirpos[i][1] = y + bradius * sin(i*PI/3);
00223
00224
00225 dcirpos[i][0] = x + bradius * cos(i*PI/3 + PI/6);
00226 dcirpos[i][1] = y + bradius * sin(i*PI/3 + PI/6);
00227 }
00228
00229
00230 const int arrowsize = 16;
00231 SDL_Surface *arrowsurf = SDL_CreateRGBSurface(SDL_SWSURFACE, arrowsize, arrowsize, 24, 0, 0, 0, 0);
00232 SDL_Rect arect;
00233 arect.x = 0; arect.y = 0; arect.w = arrowsize; arect.h = arrowsize;
00234 SDL_FillRect(arrowsurf, &arect, d->getUint32color(bcolor));
00235 arect.x = 1; arect.y = 1; arect.w = arrowsize-1; arect.h = (int)arrowsize/5;
00236 SDL_FillRect(arrowsurf, &arect, d->getUint32color(tcolor));
00237 arect.w = (int)arrowsize/5; arect.h = arrowsize-1;
00238 SDL_FillRect(arrowsurf, &arect, d->getUint32color(tcolor));
00239
00240
00241 const int sizeT = 10;
00242 SDL_Surface *isT = SDL_CreateRGBSurface(SDL_SWSURFACE, sizeT, sizeT, 24, 0, 0, 0, 0);
00243 SDL_Surface *noT = SDL_CreateRGBSurface(SDL_SWSURFACE, sizeT, sizeT, 24, 0, 0, 0, 0);
00244 arect.x = 0; arect.y = 0; arect.w = sizeT; arect.h = sizeT;
00245 SDL_FillRect(isT, &arect, d->getUint32color(bcolor));
00246 SDL_FillRect(noT, &arect, d->getUint32color(bcolor));
00247 arect.x = sizeT/2; arect.y = 0; arect.w = 1; arect.h = sizeT;
00248 SDL_FillRect(isT, &arect, d->getUint32color(tcolor));
00249 SDL_FillRect(noT, &arect, d->getUint32color(tcolor));
00250 arect.x = 0; arect.y = 0; arect.w = sizeT; arect.h = 1;
00251 SDL_FillRect(isT, &arect, d->getUint32color(tcolor));
00252 arect.x = 0; arect.y = 2; arect.w = sizeT; arect.h = 1;
00253 SDL_FillRect(noT, &arect, d->getUint32color(tcolor));
00254
00255 SDL_Surface *isTd = SDL_CreateRGBSurface(SDL_SWSURFACE, sizeT, sizeT, 24, 0, 0, 0, 0);
00256 SDL_Surface *noTd = SDL_CreateRGBSurface(SDL_SWSURFACE, sizeT, sizeT, 24, 0, 0, 0, 0);
00257 arect.x = 0; arect.y = 0; arect.w = sizeT; arect.h = sizeT;
00258 SDL_FillRect(isTd, &arect, d->getUint32color(bcolor));
00259 SDL_FillRect(noTd, &arect, d->getUint32color(bcolor));
00260 arect.x = sizeT/2; arect.y = 0; arect.w = 1; arect.h = sizeT;
00261 SDL_FillRect(isTd, &arect, d->getUint32color(dcolor));
00262 SDL_FillRect(noTd, &arect, d->getUint32color(dcolor));
00263 arect.x = 0; arect.y = 0; arect.w = sizeT; arect.h = 1;
00264 SDL_FillRect(isTd, &arect, d->getUint32color(dcolor));
00265 arect.x = 0; arect.y = 2; arect.w = sizeT; arect.h = 1;
00266 SDL_FillRect(noTd, &arect, d->getUint32color(dcolor));
00267
00268
00269 int tntidx[6];
00270 for(int i=0; i< 6; i++)
00271 tntidx[i] = i;
00272
00273
00274 double delay;
00275 int j, iscontrol, thisTarget, thisDistractor, thisSOA;
00276 int tnt[6], correctArray[ntrial];
00277 SDL_Surface *surf = SDL_CreateRGBSurface(SDL_SWSURFACE, d->getWidth(), d->getHeight(), 24, 0, 0, 0, 0);
00278 SDL_Rect rect, rr; rect.x = 0; rect.y = 0; rect.w = d->getWidth(); rect.h = d->getHeight();
00279 Timer timer;
00280 double rt, avg;
00281 avg = 0;
00282
00283 for (int i=0; i < ntrial; i++) {
00284 thisTarget = trials[trialindex[i]];
00285 thisSOA = rand()%150;
00286 thisDistractor = rand()%6;
00287 if (rand()%10 < 1){
00288 iscontrol = 1;
00289 } else {
00290 iscontrol = 0;
00291 }
00292
00293 delay = 1000000;
00294 SDL_FillRect(surf, &rect, d->getUint32color(d->getGrey()));
00295
00296
00297 et->recalibrate(d,13);
00298 d->clearScreen();
00299
00300
00301 randShuffle(tntidx, 6);
00302 for (j=0; j<6; j++)
00303 tnt[tntidx[j]] = j % 2;
00304
00305
00306 d->displayFilledCircle(x, y, 3, black, true);
00307 usleep(900000);
00308 et->track(true);
00309 usleep(100000);
00310
00311
00312 for (j=0; j<6; j++){
00313 filledCircleRGBA(surf, tcirpos[j][0], tcirpos[j][1], sradius, tcolor.red(), tcolor.green(), tcolor.blue(), 0XFF);
00314 filledCircleRGBA(surf, tcirpos[j][0], tcirpos[j][1], isradius, bcolor.red(), bcolor.green(), bcolor.blue(), 0XFF);
00315
00316 rr.x = tcirpos[j][0]-isT->w/2; rr.y = tcirpos[j][1]-isT->h/2;
00317 if (tnt[j] == 0) {
00318 SDL_BlitSurface(noT, NULL, surf, &rr);
00319 } else {
00320 SDL_BlitSurface(isT, NULL, surf, &rr);
00321 }
00322 }
00323
00324
00325 if (iscontrol == 1) {
00326 filledCircleRGBA(surf, dcirpos[thisDistractor][0], dcirpos[thisDistractor][1], sradius, tcolor.red(), tcolor.green(), tcolor.blue(), 0XFF);
00327 filledCircleRGBA(surf, dcirpos[thisDistractor][0], dcirpos[thisDistractor][1], isradius, bcolor.red(), bcolor.green(), bcolor.blue(), 0XFF);
00328 }
00329
00330
00331 filledCircleRGBA(surf, x, y, 3, black.red(), black.green(), black.blue(), 0XFF);
00332 d->displaySurface(surf, -2, true);
00333 d->pushEvent("Array UP");
00334
00335
00336 usleep(delay);
00337
00338
00339 LINFO("thisTarget: %i, isT: %i", thisTarget, tnt[thisTarget]);
00340
00341
00342 for (j=0; j<6; j++)
00343 if (j != thisTarget){
00344 filledCircleRGBA(surf, tcirpos[j][0], tcirpos[j][1], sradius, dcolor.red(), dcolor.green(), dcolor.blue(), 0XFF);
00345 filledCircleRGBA(surf, tcirpos[j][0], tcirpos[j][1], isradius, bcolor.red(), bcolor.green(), bcolor.blue(), 0XFF);
00346
00347 rr.x = tcirpos[j][0]-isT->w/2; rr.y = tcirpos[j][1]-isT->h/2;
00348 if (tnt[j] == 0) {
00349 SDL_BlitSurface(noTd, NULL, surf, &rr);
00350 } else {
00351 SDL_BlitSurface(isTd, NULL, surf, &rr);
00352 }
00353 }
00354
00355 timer.reset();
00356 if (thisSOA < 34){
00357 filledCircleRGBA(surf, dcirpos[thisDistractor][0], dcirpos[thisDistractor][1], sradius, dcolor.red(), dcolor.green(), dcolor.blue(), 0XFF);
00358 filledCircleRGBA(surf, dcirpos[thisDistractor][0], dcirpos[thisDistractor][1], isradius, bcolor.red(), bcolor.green(), bcolor.blue(), 0XFF);
00359 d->displaySurface(surf, -2, true);
00360 d->pushEvent(sformat("Target direction shown: %d", thisTarget));
00361 if (iscontrol == 1)
00362 d->pushEvent("Distractor shown: control condition");
00363 else
00364 d->pushEvent(sformat("Distractor shown: %f (SOA = %d ms)", thisDistractor+0.5, thisSOA));
00365
00366 } else {
00367
00368
00369 d->displaySurface(surf, -2, true);
00370 d->pushEvent(sformat("Target direction shown: %d", thisTarget));
00371
00372
00373 usleep(thisSOA*1000);
00374
00375
00376 filledCircleRGBA(surf, dcirpos[thisDistractor][0], dcirpos[thisDistractor][1], sradius, dcolor.red(), dcolor.green(), dcolor.blue(), 0XFF);
00377 filledCircleRGBA(surf, dcirpos[thisDistractor][0], dcirpos[thisDistractor][1], isradius, bcolor.red(), bcolor.green(), bcolor.blue(), 0XFF);
00378 d->displaySurface(surf, -2, true);
00379 d->pushEvent(sformat("Distractor shown: %f (SOA = %d ms)", thisDistractor+0.5, thisSOA));
00380
00381 }
00382
00383
00384
00385 do {
00386 key = d->waitForKey();
00387 } while (key != 52 && key != 54 && key != 32);
00388 rt = timer.getMilliSecs();
00389 int kk = 0;
00390 if (key==52)
00391 kk = 1;
00392 else if (key==32)
00393 kk = -1;
00394
00395 d->pushEvent(sformat("Responded %d (%f ms), correct %d", kk, rt, kk==tnt[thisTarget]));
00396 LINFO("Responded %d (%f ms), correct %d", kk, rt, kk==tnt[thisTarget]);
00397 avg = onlineMean(avg, rt, i+1);
00398
00399 d->clearScreen();
00400
00401 usleep(50000);
00402 et->track(false);
00403
00404 correctArray[i] = (kk==tnt[thisTarget]);
00405
00406
00407 double completion = 100*((double)i+1)/double(ntrial);
00408 if ((int)completion % 5 == 0 && completion - (int)completion == 0){
00409
00410 d->clearScreen();
00411 d->displayText(sformat("%i %% completed", (int)completion), true, 0, 10);
00412 usleep(1000000);
00413 }
00414
00415
00416 if ((int)completion%50 == 0 && i<ntrial-1 && completion-(int)completion==0) {
00417 d->clearScreen();
00418 d->displayText("Please Take a Break", true, 0, 10);
00419
00420
00421 int cc = 0;
00422 for (j=0; j<=i; j++)
00423 if (correctArray[j]==1)
00424 cc++;
00425 LINFO("Average RT: %f ms, Correctness: %f%%", avg, 100*(double)cc/((double)i+1));
00426
00427 LINFO("Break time. Press [Space] to continue, or [ESC] to terminate the experiment.");
00428 d->waitForKey(true);
00429 d->displayText("Calibration", true, 0, 10);
00430 d->pushEventBegin("Calibration");
00431 et->calibrate(d);
00432 d->pushEventEnd("Calibration");
00433 }
00434 }
00435
00436 SDL_FreeSurface(arrowsurf);
00437 SDL_FreeSurface(surf);
00438
00439
00440 int cc = 0;
00441 for (j=0; j<ntrial; j++)
00442 if (correctArray[j]==1)
00443 cc++;
00444 LINFO("Average RT: %f ms, Correctness: %f%%", avg, 100*(double)cc/((double)ntrial));
00445
00446 d->clearScreen();
00447 d->displayText("Experiment complete. Thank you!", true, 0, 10);
00448 d->waitForKey(true);
00449
00450
00451 manager.stop();
00452
00453
00454 return 0;
00455 }
00456
00457
00458 extern "C" int main(const int argc, char** argv)
00459 {
00460
00461
00462
00463
00464 try
00465 {
00466 return submain(argc, argv);
00467 }
00468 catch (...)
00469 {
00470 REPORT_CURRENT_EXCEPTION;
00471 }
00472
00473 return 1;
00474 }
00475
00476
00477
00478
00479
00480