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> white = 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
00256 int tntidx[6];
00257 for(int i=0; i< 6; i++)
00258 tntidx[i] = i;
00259
00260
00261 double delay;
00262 int j, iscontrol, ang, thisTarget, thisDistractor, thisSOA;
00263 int tnt[6], correctArray[ntrial];
00264 SDL_Surface *surf = SDL_CreateRGBSurface(SDL_SWSURFACE, d->getWidth(), d->getHeight(), 24, 0, 0, 0, 0);
00265 SDL_Rect rect, rr; rect.x = 0; rect.y = 0; rect.w = d->getWidth(); rect.h = d->getHeight();
00266 Timer timer;
00267 double rt, avg;
00268 avg = 0;
00269
00270 for (int i=0; i < ntrial; i++) {
00271 thisTarget = trials[trialindex[i]];
00272 thisSOA = rand()%150;
00273 thisDistractor = rand()%6;
00274 if (rand()%10 < 1){
00275 iscontrol = 1;
00276 } else {
00277 iscontrol = 0;
00278 }
00279
00280 delay = 1000000;
00281 SDL_FillRect(surf, &rect, d->getUint32color(d->getGrey()));
00282
00283
00284 et->recalibrate(d,13);
00285 d->clearScreen();
00286
00287
00288 randShuffle(tntidx, 6);
00289 for (j=0; j<6; j++)
00290 tnt[tntidx[j]] = j % 2;
00291
00292
00293 d->displayFilledCircle(x, y, 3, white, true);
00294 usleep(900000);
00295 et->track(true);
00296 usleep(100000);
00297
00298
00299 filledCircleRGBA(surf, x, y, 3, white.red(), white.green(), white.blue(), 0XFF);
00300 for (j=0; j<6; j++){
00301 filledCircleRGBA(surf, tcirpos[j][0], tcirpos[j][1], sradius, tcolor.red(), tcolor.green(), tcolor.blue(), 0XFF);
00302 filledCircleRGBA(surf, tcirpos[j][0], tcirpos[j][1], isradius, bcolor.red(), bcolor.green(), bcolor.blue(), 0XFF);
00303
00304 rr.x = tcirpos[j][0]-isT->w/2; rr.y = tcirpos[j][1]-isT->h/2;
00305 if (tnt[j] == 0) {
00306 SDL_BlitSurface(noT, NULL, surf, &rr);
00307 } else {
00308 SDL_BlitSurface(isT, NULL, surf, &rr);
00309 }
00310 }
00311
00312
00313 if (iscontrol == 1) {
00314 filledCircleRGBA(surf, dcirpos[thisDistractor][0], dcirpos[thisDistractor][1], sradius, dcolor.red(), dcolor.green(), dcolor.blue(), 0XFF);
00315 filledCircleRGBA(surf, dcirpos[thisDistractor][0], dcirpos[thisDistractor][1], isradius, bcolor.red(), bcolor.green(), bcolor.blue(), 0XFF);
00316 }
00317
00318
00319 d->displaySurface(surf, -2, true);
00320 d->pushEvent("Array UP");
00321
00322
00323 usleep(delay);
00324
00325
00326 ang = 45 - 60 * thisTarget - 180;
00327 SDL_Surface *tmpsurf = rotozoomSurface(arrowsurf, ang, 1, 0.5);
00328 rr.x = d->getWidth()/2-tmpsurf->w/2; rr.y = d->getHeight()/2-tmpsurf->h/2;
00329 SDL_BlitSurface(tmpsurf, NULL, surf, &rr);
00330 LINFO("thisTarget: %i, isT: %i", thisTarget, tnt[thisTarget]);
00331
00332
00333 timer.reset();
00334 if (iscontrol == 1) {
00335
00336 d->displaySurface(surf, -2, true);
00337 d->pushEvent(sformat("Target direction shown: %d", thisTarget));
00338 d->pushEvent("Distractor shown: control condition");
00339
00340 }else if (thisSOA < 34){
00341
00342 filledCircleRGBA(surf, dcirpos[thisDistractor][0], dcirpos[thisDistractor][1], sradius, dcolor.red(), dcolor.green(), dcolor.blue(), 0XFF);
00343 filledCircleRGBA(surf, dcirpos[thisDistractor][0], dcirpos[thisDistractor][1], isradius, bcolor.red(), bcolor.green(), bcolor.blue(), 0XFF);
00344 d->displaySurface(surf, -2, true);
00345 d->pushEvent(sformat("Target direction shown: %d", thisTarget));
00346 d->pushEvent(sformat("Distractor shown: %f (SOA = %d ms)", thisDistractor+0.5, thisSOA));
00347
00348 } else {
00349
00350
00351 d->displaySurface(surf, -2, true);
00352 d->pushEvent(sformat("Target direction shown: %d", thisTarget));
00353
00354
00355 usleep(thisSOA*1000);
00356
00357
00358 filledCircleRGBA(surf, dcirpos[thisDistractor][0], dcirpos[thisDistractor][1], sradius, dcolor.red(), dcolor.green(), dcolor.blue(), 0XFF);
00359 filledCircleRGBA(surf, dcirpos[thisDistractor][0], dcirpos[thisDistractor][1], isradius, bcolor.red(), bcolor.green(), bcolor.blue(), 0XFF);
00360 d->displaySurface(surf, -2, true);
00361 d->pushEvent(sformat("Distractor shown: %f (SOA = %d ms)", thisDistractor+0.5, thisSOA));
00362
00363 }
00364 SDL_FreeSurface(tmpsurf);
00365
00366
00367
00368 do {
00369 key = d->waitForKey();
00370 } while (key != 52 && key != 54 && key != 32);
00371 rt = timer.getMilliSecs();
00372 int kk = 0;
00373 if (key==52)
00374 kk = 1;
00375 else if (key==32)
00376 kk = -1;
00377
00378 d->pushEvent(sformat("Responded %d (%f ms), correct %d", kk, rt, kk==tnt[thisTarget]));
00379 LINFO("Responded %d (%f ms), correct %d", kk, rt, kk==tnt[thisTarget]);
00380 avg = onlineMean(avg, rt, i+1);
00381
00382 d->clearScreen();
00383
00384 usleep(50000);
00385 et->track(false);
00386
00387 correctArray[i] = (kk==tnt[thisTarget]);
00388
00389
00390 double completion = 100*((double)i+1)/double(ntrial);
00391 if ((int)completion % 5 == 0 && completion - (int)completion == 0){
00392
00393 d->clearScreen();
00394 d->displayText(sformat("%i %% completed", (int)completion), true, 0, 10);
00395 usleep(1000000);
00396 }
00397
00398
00399 if ((int)completion%50 == 0 && i<ntrial-1 && completion-(int)completion==0) {
00400 d->clearScreen();
00401 d->displayText("Please Take a Break", true, 0, 10);
00402
00403
00404 int cc = 0;
00405 for (j=0; j<=i; j++)
00406 if (correctArray[j]==1)
00407 cc++;
00408 LINFO("Average RT: %f ms, Correctness: %f%%", avg, 100*(double)cc/((double)i+1));
00409
00410 LINFO("Break time. Press [Space] to continue, or [ESC] to terminate the experiment.");
00411 d->waitForKey(true);
00412 d->displayText("Calibration", true, 0, 10);
00413 d->pushEventBegin("Calibration");
00414 et->calibrate(d);
00415 d->pushEventEnd("Calibration");
00416 }
00417 }
00418
00419 SDL_FreeSurface(arrowsurf);
00420 SDL_FreeSurface(surf);
00421
00422
00423 int cc = 0;
00424 for (j=0; j<ntrial; j++)
00425 if (correctArray[j]==1)
00426 cc++;
00427 LINFO("Average RT: %f ms, Correctness: %f%%", avg, 100*(double)cc/((double)ntrial));
00428
00429 d->clearScreen();
00430 d->displayText("Experiment complete. Thank you!", true, 0, 10);
00431 d->waitForKey(true);
00432
00433
00434 manager.stop();
00435
00436
00437 return 0;
00438 }
00439
00440
00441 extern "C" int main(const int argc, char** argv)
00442 {
00443
00444
00445
00446
00447 try
00448 {
00449 return submain(argc, argv);
00450 }
00451 catch (...)
00452 {
00453 REPORT_CURRENT_EXCEPTION;
00454 }
00455
00456 return 1;
00457 }
00458
00459
00460
00461
00462
00463