EyeTrackerEyeLink.C

Go to the documentation of this file.
00001 /*!@file Psycho/EyeTrackerEyeLink.C Abstraction for an EyeLink eye-tracker */
00002 
00003 // //////////////////////////////////////////////////////////////////// //
00004 // The iLab Neuromorphic Vision C++ Toolkit - Copyright (C) 2000-2005   //
00005 // by the University of Southern California (USC) and the iLab at USC.  //
00006 // See http://iLab.usc.edu for information about this project.          //
00007 // //////////////////////////////////////////////////////////////////// //
00008 // Major portions of the iLab Neuromorphic Vision Toolkit are protected //
00009 // under the U.S. patent ``Computation of Intrinsic Perceptual Saliency //
00010 // in Visual Environments, and Applications'' by Christof Koch and      //
00011 // Laurent Itti, California Institute of Technology, 2001 (patent       //
00012 // pending; application number 09/912,225 filed July 23, 2001; see      //
00013 // http://pair.uspto.gov/cgi-bin/final/home.pl for current status).     //
00014 // //////////////////////////////////////////////////////////////////// //
00015 // This file is part of the iLab Neuromorphic Vision C++ Toolkit.       //
00016 //                                                                      //
00017 // The iLab Neuromorphic Vision C++ Toolkit is free software; you can   //
00018 // redistribute it and/or modify it under the terms of the GNU General  //
00019 // Public License as published by the Free Software Foundation; either  //
00020 // version 2 of the License, or (at your option) any later version.     //
00021 //                                                                      //
00022 // The iLab Neuromorphic Vision C++ Toolkit is distributed in the hope  //
00023 // that it will be useful, but WITHOUT ANY WARRANTY; without even the   //
00024 // implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR      //
00025 // PURPOSE.  See the GNU General Public License for more details.       //
00026 //                                                                      //
00027 // You should have received a copy of the GNU General Public License    //
00028 // along with the iLab Neuromorphic Vision C++ Toolkit; if not, write   //
00029 // to the Free Software Foundation, Inc., 59 Temple Place, Suite 330,   //
00030 // Boston, MA 02111-1307 USA.                                           //
00031 // //////////////////////////////////////////////////////////////////// //
00032 //
00033 // Primary maintainer for this file: Laurent Itti <itti@usc.edu>
00034 // $HeadURL: svn://isvn.usc.edu/software/invt/trunk/saliency/src/Psycho/EyeTrackerEyeLink.C $
00035 // $Id: EyeTrackerEyeLink.C 14444 2011-01-21 17:04:13Z jshen $
00036 //
00037 
00038 #include "Psycho/EyeTrackerEyeLink.H"
00039 
00040 #include "Component/OptionManager.H"
00041 #include "GUI/GUIOpts.H"
00042 #include "Psycho/PsychoOpts.H"
00043 #include "Util/sformat.H"
00044 #include "Component/EventLog.H"
00045 #include "Psycho/PsychoDisplay.H"
00046 #include <SDL/SDL.h>
00047 #include "Raster/Raster.H"
00048 
00049 #ifdef HAVE_EYELINK
00050 #include <eyelink/eyelink.h>
00051 #include <eyelink/sdl_expt.h>
00052 #include <eyelink/sdl_text_support.h>
00053 ALLF_DATA evt; //buffer to hold sample and event data
00054 #endif
00055 
00056 SDL_Color target_background_color = { 0, 0, 0};
00057 SDL_Color target_foreground_color = { 192, 192, 192 };
00058 
00059 // ######################################################################
00060 EyeTrackerEyeLink::EyeTrackerEyeLink(OptionManager& mgr,
00061                                      const std::string& descrName,
00062                                      const std::string& tagName) :
00063   EyeTracker(mgr, descrName, tagName),
00064   itsEDFfname(&OPT_EyeTrackerEDFfname, this),
00065   itsDims(&OPT_SDLdisplayDims, this)
00066 {  }
00067 
00068 // ######################################################################
00069 EyeTrackerEyeLink::~EyeTrackerEyeLink()
00070 {  }
00071 
00072 // ######################################################################
00073 void EyeTrackerEyeLink::start1()
00074 {
00075 #ifndef HAVE_EYELINK
00076   LFATAL("Proprietary EyeLink developer API not installed");
00077 #else
00078   // get the link to the eye-tracker going:
00079   if (open_eyelink_connection(0))
00080     LFATAL("Cannot open link to EyeLink tracker - make sure your "
00081            "hostname resolves to 100.1.1.2 (check /etc/hosts)");
00082 
00083   // Basic initializations:
00084   set_offline_mode();
00085   flush_getkey_queue();/* initialize getkey() system */
00086 
00087   // Open EyeLink version of SDL
00088   openSDL();
00089 
00090   // set the display characteristics: 
00091   DISPLAYINFO di; // defined in eyelink/core_expt.h
00092   di.width = itsDims.getVal().w(); di.height = itsDims.getVal().h();
00093   di.left = 0; di.top = 0; di.right = di.width-1; di.bottom = di.height-1;
00094   di.bits = 24; di.palsize = 0; di.pages = 0; di.refresh = 60.0F;
00095   di.winnt = 0;
00096 
00097   set_calibration_colors(&target_foreground_color, &target_background_color);
00098 
00099         // open EDF file if set:
00100   if (itsEDFfname.getVal().empty() == false)
00101     {
00102       if (open_data_file((char *)(itsEDFfname.getVal().c_str())) != 0)
00103         LFATAL("Cannot open EDF file '%s'", itsEDFfname.getVal().c_str());
00104 
00105       eyecmd_printf(const_cast<char*>("add_file_preamble_text 'RECORDED BY iLab code' "));
00106     }
00107 
00108   // Add resolution to EDF file:
00109   eyemsg_printf(const_cast<char*>("DISPLAY_COORDS %ld %ld %ld %ld"),
00110                 di.left, di.top, di.right, di.bottom);
00111 
00112   /* SET UP TRACKER CONFIGURATION */
00113   /* set parser saccade thresholds (conservative settings) */
00114   /* 0 = standard sensitivity */
00115   eyecmd_printf(const_cast<char*>("select_parser_configuration 0"));
00116 
00117   // set EDF file contents:
00118   eyecmd_printf(const_cast<char*>("file_event_filter = LEFT,RIGHT,FIXATION,SACCADE,"
00119                                   "BLINK,MESSAGE,BUTTON"));
00120   eyecmd_printf(const_cast<char*>("file_sample_data = LEFT,RIGHT,GAZE,AREA,GAZERES,STATUS"));
00121 
00122   // set link data (used for gaze cursor):
00123   eyecmd_printf(const_cast<char*>("link_event_filter = LEFT,RIGHT,FIXATION,FIXUPDATE,SACCADE,BLINK,BUTTON"));
00124   eyecmd_printf(const_cast<char*>("link_sample_data = LEFT,RIGHT,GAZE,GAZERES,AREA,STATUS"));
00125 
00126   // Program button #5 for use in drift correction:
00127   eyecmd_printf(const_cast<char*>("button_function 5 'accept_target_fixation'"));
00128   
00129         // setup eyelink filtering: default is "1 2"
00130         eyecmd_printf(const_cast<char*>("heuristic_filter = 1 2"));
00131         eyelink_wait_for_mode_ready(500);
00132 
00133   // make sure we're still alive:
00134   if (!eyelink_is_connected() || break_pressed())
00135     LFATAL("Connection to EyeLink broken or aborted");
00136 
00137   /* TRIAL_VAR_LABELS message is recorded for EyeLink Data Viewer analysis
00138      It specifies the list of trial variables for the trial
00139      This should be written once only and put before the recording of
00140      individual trials */
00141   eyemsg_printf(const_cast<char*>("TRIAL_VAR_LABELS CONDITION"));
00142 
00143   // Configure EyeLink to send fixation updates every 50 msec:
00144   //eyecmd_printf("link_event_filter = LEFT,RIGHT,FIXUPDATE");
00145   //eyecmd_printf("fixation_update_interval = 50");
00146   //eyecmd_printf("fixation_update_accumulate = 50");
00147 
00148   EyeTracker::start1();
00149 #endif
00150 }
00151 
00152 // ######################################################################
00153 void EyeTrackerEyeLink::openSDL()
00154 {
00155 #ifdef HAVE_EYELINK
00156   // set the display characteristics:
00157         DISPLAYINFO di;
00158   di.width = itsDims.getVal().w(); di.height = itsDims.getVal().h();
00159   di.left = 0; di.top = 0; di.right = di.width-1; di.bottom = di.height-1;
00160   di.bits = 24; di.palsize = 0; di.pages = 0; di.refresh = 60.0F;
00161   di.winnt = 0;
00162 
00163   // open the display with our wishes:
00164   if (init_expt_graphics(NULL, &di))
00165     LFATAL("Cannot open display");
00166 
00167   // see what we actually got:
00168   get_display_information(&di);
00169   LINFO("DISPLAYINFO: [%dx%d}: (%d,%d)-(%d,%d) %dbpp %.1fHz",
00170         int(di.width), int(di.height), int(di.left), int(di.top),
00171         int(di.right), int(di.bottom), int(di.bits), di.refresh);
00172   if (di.palsize) LFATAL("Paletized color modes not supported");
00173 
00174   // Prepare for calibration and other settings:
00175   set_target_size(10, 2);
00176   set_calibration_colors(&target_foreground_color, &target_background_color);
00177   set_cal_sounds(const_cast<char*>(""), const_cast<char*>(""), const_cast<char*>(""));
00178   set_dcorr_sounds(const_cast<char*>(""), const_cast<char*>("off"), const_cast<char*>("off"));
00179 
00180   // Setup calibration type:
00181   eyecmd_printf(const_cast<char*>("calibration_type = HV9"));
00182 
00183   // Configure tracker for display:
00184   eyecmd_printf(const_cast<char*>("screen_pixel_coords = %ld %ld %ld %ld"),
00185                 di.left, di.top, di.right, di.bottom);
00186 
00187         if (di.refresh > 40.0F)
00188     eyemsg_printf(const_cast<char*>("FRAMERATE %1.2f Hz."), di.refresh);
00189 #endif
00190 }
00191 
00192 // ######################################################################
00193 void EyeTrackerEyeLink::stop1()
00194 {
00195 #ifndef HAVE_EYELINK
00196   LFATAL("Proprietary EyeLink developer API not installed");
00197 #else
00198   set_offline_mode();
00199   pump_delay(500); // give some time to tracker to stop
00200 
00201   // close EDF data file if any:
00202   if (itsEDFfname.getVal().empty() == false)
00203     eyecmd_printf(const_cast<char*>("close_data_file"));
00204         
00205 
00206   // shutdown EyeLink properly:
00207   close_expt_graphics();
00208 
00209   // transfer the EDF file if any:
00210   if (itsEDFfname.getVal().empty() == false)
00211     {
00212       LINFO("Transferring EDF file to '%s'", itsEDFfname.getVal().c_str());
00213       receive_data_file((char *)itsEDFfname.getVal().c_str(),
00214                         (char *)itsEDFfname.getVal().c_str(), 0);
00215     }
00216 
00217   // Disconnect from eyelink:
00218   close_eyelink_connection();
00219 
00220   EyeTracker::stop1();
00221 #endif
00222 }
00223 
00224 // ######################################################################
00225 void EyeTrackerEyeLink::calibrate(nub::soft_ref<PsychoDisplay> d)
00226 {
00227 #ifndef HAVE_EYELINK
00228   LFATAL("Proprietary EyeLink developer API not installed");
00229 #else
00230         //create SDL_Surface of a image that replaces calibration point
00231         if(!d->getModelParamString("PsychoDisplayFixationIcon").empty()){
00232                 Image< PixRGB<byte> > fixicon = 
00233                         Raster::ReadRGB(d->getModelParamString("PsychoDisplayFixationIcon"));
00234                 SDL_Surface *img = d->makeBlittableSurface(fixicon, false, 
00235                                                                                 PixRGB<byte>(target_background_color.r, target_background_color.g, target_background_color.b));
00236                 set_cal_target_surface(img);
00237         }
00238         
00239   // Do camera setup and calibration:
00240   do_tracker_setup();
00241 
00242         // Get calibration result
00243         char message[256];
00244         eyelink_cal_message(message);
00245         eyemsg_printf(message);
00246 #endif
00247 }
00248 
00249 // ######################################################################
00250 void EyeTrackerEyeLink::calibrate2(nub::soft_ref<PsychoDisplay> d)
00251 {
00252 #ifndef HAVE_EYELINK
00253   LFATAL("Proprietary EyeLink developer API not installed");
00254 #else
00255         //create SDL_Surface of a image that replaces calibration point
00256         if(!d->getModelParamString("PsychoDisplayFixationIcon").empty()){
00257                 Image< PixRGB<byte> > fixicon = 
00258                         Raster::ReadRGB(d->getModelParamString("PsychoDisplayFixationIcon"));
00259                 SDL_Surface *img = d->makeBlittableSurface(fixicon, false, 
00260                                                                                 PixRGB<byte>(target_background_color.r, target_background_color.g, target_background_color.b));
00261                 set_cal_target_surface(img);
00262         }
00263         
00264   // Do camera setup and calibration:
00265         do_tracker_setup();
00266 
00267 //      INT16 x = 1024;
00268 //      INT16 y = 768;
00269 //      draw_cal_target(x, y);
00270 
00271         // Get calibration result
00272         char message[256];
00273         eyelink_cal_message(message);
00274         eyemsg_printf(message);
00275 #endif
00276 }
00277 
00278 // ######################################################################
00279 void EyeTrackerEyeLink::setBackgroundColor(nub::soft_ref<PsychoDisplay> d)
00280 {
00281 #ifndef HAVE_EYELINK
00282   LFATAL("Proprietary EyeLink developer API not installed");
00283 #else
00284         SDL_Color bgcolor = { d->getGrey().red(), d->getGrey().green(), d->getGrey().blue()};
00285         SDL_Color fgcolor = { 192, 192, 192};
00286         
00287         set_calibration_colors(&fgcolor, &bgcolor);
00288         LINFO("RGB: %i %i %i", d->getGrey().red(), d->getGrey().green(), d->getGrey().blue());
00289 #endif
00290 }
00291 
00292 // ######################################################################
00293 void EyeTrackerEyeLink::manualDriftCorrection(Point2D<double> eyepos, 
00294                                                                                               Point2D<double> targetpos)
00295 {
00296 #ifndef HAVE_EYELINK
00297   LFATAL("Proprietary EyeLink developer API not installed");
00298 #else
00299         char message[256];
00300         eyecmd_printf(const_cast<char*>("drift_correction %ld %ld %ld %ld"), 
00301                                                                                          targetpos.i-eyepos.i, targetpos.j-eyepos.j,
00302                                                                                                                                          targetpos.i, targetpos.j);
00303 
00304         // Get drift correction result
00305         eyelink_cal_message(message);
00306         eyemsg_printf(message);
00307 #endif
00308 }
00309 
00310 // ######################################################################
00311 void EyeTrackerEyeLink::recalibrate(nub::soft_ref<PsychoDisplay> d,int repeats)
00312 {
00313 #ifndef HAVE_EYELINK
00314   LFATAL("Proprietary EyeLink developer API not installed");
00315 #else
00316         //create SDL_Surface of a image that replaces calibration point
00317         if(!d->getModelParamString("PsychoDisplayFixationIcon").empty()){
00318                 Image< PixRGB<byte> > fixicon =
00319                 Raster::ReadRGB(d->getModelParamString("PsychoDisplayFixationIcon"));
00320     SDL_Surface *img = d->makeBlittableSurface(fixicon, false, 
00321                                                                                 PixRGB<byte>(target_background_color.r, target_background_color.g, target_background_color.b));
00322           set_cal_target_surface(img);
00323         }
00324 
00325         do_drift_correct(d->getWidth()/2, d->getHeight()/2, 1, 1);
00326         
00327         // Get calibration result
00328         char message[256];
00329         eyelink_cal_message(message);
00330         eyemsg_printf(message);
00331 
00332         // set offline mode to ensure trackign
00333         set_offline_mode();
00334 #endif
00335 }
00336 
00337 // ######################################################################
00338 void EyeTrackerEyeLink::closeSDL()
00339 {
00340 #ifdef HAVE_EYELINK
00341   close_expt_graphics();
00342 #endif
00343 }
00344 
00345 
00346 // ######################################################################
00347 void EyeTrackerEyeLink::startTracking()
00348 {
00349 #ifndef HAVE_EYELINK
00350   LFATAL("Proprietary EyeLink developer API not installed");
00351 #else
00352   // Show message at bottom of tracker display:
00353   eyecmd_printf(const_cast<char*>("record_status_message 'Recording [%d]...' "), getSession());
00354 
00355         // set FIXUPDATE event property
00356   eyecmd_printf(const_cast<char*>("fixation_update_interval = 50"));
00357   eyecmd_printf(const_cast<char*>("fixation_update_accumulate = 50"));
00358 
00359   // Always send a TRIALID message before starting to record.  It
00360   // should contain trial condition data required for analysis:
00361   eyemsg_printf(const_cast<char*>("TRIALID EYETRACK"));
00362 
00363   // TRIAL_VAR_DATA message is recorded for EyeLink Data Viewer
00364   // analysis It specifies the list of trial variables value for the
00365   // trial This must be specified within the scope of an individual
00366   // trial (i.e., after "TRIALID" and before "TRIAL_RESULT"):
00367   eyemsg_printf(const_cast<char*>("!V TRIAL_VAR_DATA EYETRACKING"));
00368 
00369   // Log eye tracker time at onset of clip:
00370   if (itsEventLog.isValid())
00371     itsEventLog->pushEvent(sformat("Eye Tracker Time = %d", int(current_msec())));
00372 
00373   // Actually start the recording:
00374   if (start_recording(1, 1, 1, 1))
00375     LFATAL("Error trying to start recording");
00376 #endif
00377 }
00378 
00379 // ######################################################################
00380 void EyeTrackerEyeLink::stopTracking()
00381 {
00382 #ifndef HAVE_EYELINK
00383   LFATAL("Proprietary EyeLink developer API not installed");
00384 #else
00385   // report response result: 0=timeout, else button number:
00386   eyemsg_printf(const_cast<char*>("TRIAL_RESULT 1"));
00387 
00388   // stop the tracker:
00389   stop_recording();
00390 
00391         // set FIXUPDATE event property (close it)
00392   eyecmd_printf(const_cast<char*>("fixation_update_interval = 0"));
00393   eyecmd_printf(const_cast<char*>("fixation_update_accumulate = 0"));
00394 
00395         set_offline_mode();
00396 #endif
00397 }
00398 
00399 // ######################################################################
00400 bool EyeTrackerEyeLink::isFixating()
00401 {
00402   LFATAL("Unimplemented for now");
00403   return false;
00404 }
00405 
00406 // ######################################################################
00407 bool EyeTrackerEyeLink::isSaccade()
00408 {
00409   LFATAL("Unimplemented for now");
00410   return false;
00411 }
00412 
00413 // ######################################################################
00414 Point2D<int> EyeTrackerEyeLink::getEyePos() const
00415 {
00416 #ifndef HAVE_EYELINK
00417   LFATAL("Proprietary EyeLink developer API not installed");
00418         return Point2D<int>(-1,-1);
00419 #else
00420         // get gaze position
00421         eyelink_newest_float_sample(&evt);
00422 
00423         // make sure pupil is present
00424         if(evt.fs.gx[RIGHT_EYE]!=MISSING_DATA && 
00425                  evt.fs.gy[RIGHT_EYE]!=MISSING_DATA && evt.fs.pa[RIGHT_EYE]>0){
00426                 return Point2D<int>((int)evt.fs.gx[RIGHT_EYE], (int)evt.fs.gy[RIGHT_EYE]);
00427         } else {
00428                 return Point2D<int>(-1, -1);
00429         }
00430 #endif
00431 }
00432 
00433 // ######################################################################
00434 Point2D<int> EyeTrackerEyeLink::getFixationPos() const
00435 {
00436 #ifndef HAVE_EYELINK
00437   LFATAL("Proprietary EyeLink developer API not installed");
00438         return Point2D<int>(-1,-1);
00439 #else
00440         int i, x, y;                            
00441 
00442         while (1) { // using while loop to consume the eye data queue 
00443     i = eyelink_get_next_data(NULL);   // check for new data item
00444                 if (i == FIXUPDATE) {
00445         eyelink_get_float_data(&evt);
00446                         x = (int)evt.fe.gavx;
00447                         y = (int)evt.fe.gavy;
00448         //LINFO("Eyelink Time (FIXUPDATE) : %i (%i, %i)\n", (int)evt.fe.time, x, y);
00449                         break;
00450                 }
00451         }
00452 
00453         return Point2D<int>(x, y);
00454 #endif 
00455 }
00456 
00457 // ######################################################################
00458 CalibrationTransform::Data EyeTrackerEyeLink::getCalibrationSet(nub::soft_ref<PsychoDisplay> d) const
00459 {
00460     LINFO("\n getting calibration set...");
00461     CalibrationTransform::Data dummy;
00462     dummy.addData(Point2D<double>(-1.0,-1.0),Point2D<double>(-1.0,-1.0));
00463     return dummy;
00464 }
00465 
00466 
00467 // ######################################################################
00468 /* So things look consistent in everyone's emacs... */
00469 /* Local Variables: */
00470 /* mode: c++ */
00471 /* indent-tabs-mode: nil */
00472 /* End: */
Generated on Sun May 8 08:05:32 2011 for iLab Neuromorphic Vision Toolkit by  doxygen 1.6.3