MATLAB Programming/Psychtoolbox/eyelink toolbox

% Short MATLAB example program that uses the Eyelink and Psychophysics % Toolboxes to create a real-time gaze-dependent display.

% edf 09.19.06: adapted from http://psychtoolbox.org/eyelinktoolbox/EyelinkToolbox.pdf % and http://www.kyb.tuebingen.mpg.de/bu/people/kleinerm/ptbosx/ptbdocu-1.0.5MK4R1.html % (also available in local install at Psychtoolbox\ProgrammingTips.html)

% see also % Psychtoolbox\PsychHardware\EyelinkToolbox\EyelinkDemos\Short demos\EyelinkExample.m % which calls some functions that do not work on the windows openGL ptb

% ex: [v vs]=Eyelink('GetTrackerVersion') unimplemented for win % also: el=EyelinkInitDefaults sets el.keysCached to 0 on windows. %      this causes EyelinkDoTrackerSetup and EyelinkDoDriftCorrect to %       fail, because they call EyelinkGetKey, which depends on cached key id's. %       updating the eyelink toolbox to use the new KbName('UnifyKeyNames') %      should fix this.

% necessary for windows: % copy EyelinkToolbox144\EyelinkToolbox\EyelinkBasic\eyelink.dll % (from http://www.psychtoolbox.org/eyelinktoolbox/downloads/EyelinkToolbox144.zip) % to Psychtoolbox\PsychHardware\EyelinkToolbox\EyelinkBasic\Eyelink.dll % (note capitalizing Eyelink.dll is important) % the rest of the eyelink toolbox is included in ptb 1.0.6 openGL beta

% see ptb forum thread http://tech.groups.yahoo.com/group/psychtoolbox/message/4993

% note eyelink functions are documented in % C:\Program Files\SR Research\EyeLink\Docs\*.pdf

function eyelinkWinDemo format long g KbName('UnifyKeyNames') %enables cross-platform key id's

doDisplay=1; %use ptb createFile=1; %record eyetracking data on the remote (eyetracking) computer and suck over the file when done mouseInsteadOfGaze=0; %control gaze cursor using mouse instead of gaze (for testing, in case calibration isn't worked out yet) textOut=1; %write reports from tracker to stdout

edfFile='demo.edf'; %name of remote data file to create screenNum = 1; % use main screen

% STEP 1 % Initialization of the connection with the Eyelink Gazetracker. % exit program if this fails. if (Eyelink('initialize') ~= 0) error('could not init connection to Eyelink') return; end;

try % STEP 2 % Open a graphics window on the main screen % using the PsychToolbox's SCREEN function.

priority = MaxPriority('KbCheck'); oldPriority = Priority;

if doDisplay AssertOpenGL window = Screen('OpenWindow', screenNum, 0, [], 32, 2); HideCursor; Priority(priority); ifi = Screen('GetFlipInterval', window, 200); Priority(oldPriority);

white=WhiteIndex(window); black=BlackIndex(window);

[scrWidth, scrHeight]=Screen('WindowSize', window);

xRange = [0 scrWidth]; %range of gaze estimates over display, which probably come in terms of the ptb stim display yRange = [0 scrHeight];

dotHeight = 7; dotWidth = 7; end

% STEP 3 % Provide Eyelink with details about the graphics environment % and perform some initializations. The information is returned % in a structure that also contains useful defaults % and control codes (e.g. tracker state bit and Eyelink key values). if doDisplay el=EyelinkInitDefaults(window); else el=EyelinkInitDefaults; end % make sure that we get gaze data from the Eyelink status=Eyelink('command','link_sample_data = LEFT,RIGHT,GAZE,AREA,GAZERES,HREF,PUPIL,STATUS,INPUT'); if status~=0 error('link_sample_data error, status: ',status) end % open file to record data to (just an example, not required) if createFile status=Eyelink('openfile',edfFile); if status~=0 error('openfile error, status: ',status) end end

% STEP 4 if doDisplay && strcmp(el.computer,'MAC')==1 % OSX % Calibrate the eye tracker using the standard calibration routines EyelinkDoTrackerSetup(el); %fails on win, see header comments % do a final check of calibration using driftcorrection EyelinkDoDriftCorrect(el); %fails on win, see header comments else warning('cannot do calibration/drift correction unless on OSX with an open ptb window') end % STEP 5 % start recording eye position status=Eyelink('startrecording'); if status~=0 error('startrecording error, status: ',status) end % record a few samples before we actually start displaying WaitSecs(0.1); % mark zero-plot time in data file status=Eyelink('message','SYNCTIME'); if status~=0 error('message error, status: ',status) end

stopkey=KbName('space'); eye_used = -1; %just an initializer to remind us to ask tracker which eye is tracked

% STEP 6 % show gaze-dependent display

Priority(priority); if doDisplay vbl = Screen('Flip', window); %Initially synchronize with retrace, take base time in vbl end

while 1 % loop till error or space bar is pressed

% Check recording status, stop display if error err=Eyelink('checkrecording'); if(err~=0) error('checkrecording problem, status: ',err) break; end

% check for presence of a new sample update status = Eyelink('newfloatsampleavailable'); if status> 0 % get the sample in the form of an event structure evt = Eyelink('newestfloatsample');

if textOut evt end if eye_used ~= -1 % do we know which eye to use yet? % if we do, get current gaze position from sample x = evt.gx(eye_used+1); % +1 as we're accessing MATLAB array y = evt.gy(eye_used+1);

% do we have valid data and is the pupil visible? if (x~=el.MISSING_DATA & y~=el.MISSING_DATA & evt.pa(eye_used+1)>0) || mouseInsteadOfGaze

if mouseInsteadOfGaze if doDisplay [x,y,buttons] = GetMouse(window); else [x,y,buttons] = GetMouse(screenNum); end else x=scrWidth*((x-min(xRange))/range(xRange)); y=scrHeight*((y-min(yRange))/range(yRange)); end

% if data is valid, draw a circle on the screen at current gaze position % using PsychToolbox's SCREEN function if doDisplay gazeRect=[ x-dotWidth/2 y-dotHeight/2 x+dotWidth/2 y+dotHeight/2]; penSize=6; Screen('FrameOval', window, white, gazeRect,penSize,penSize); end else % if data is invalid (e.g. during a blink), clear display if doDisplay Screen('FillRect', window,black); end

disp('blink! (x or y is missing or pupil size<=0)') end

if doDisplay Screen('DrawingFinished', window); vbl = Screen('Flip', window, vbl + 0.5*ifi); end

else % if we don't, first find eye that's being tracked eye_used = Eyelink('eyeavailable'); % get eye that's tracked

switch eye_used case el.BINOCULAR disp('tracker indicates binocular, we''ll use right') eye_used = el.RIGHT_EYE; case el.LEFT_EYE disp('tracker indicates left eye') case el.RIGHT_EYE disp('tracker indicates right eye') case -1 error('eyeavailable returned -1') otherwise error('uninterpretable result from eyeavailable: ',eye_used) end end else disp(sprintf('no sample available, status: %d',status)) end % if sample available

% check for keyboard press [keyIsDown,secs,keyCode] = KbCheck; % if spacebar was pressed stop display if keyCode(stopkey) break; end end % main loop

% wait a while to record a few more samples WaitSecs(0.1);

% STEP 7 % finish up: stop recording eye-movements, % close graphics window, close data file and shut down tracker cleanup(createFile, oldPriority, edfFile); catch cleanup(createFile, oldPriority, edfFile); ers=lasterror ers.stack.file ers.stack.name ers.stack.line rethrow(lasterror) end

function cleanup(createFile, oldPriority, edfFile) Eyelink('stoprecording'); Screen('CloseAll'); ShowCursor; Priority(oldPriority); if createFile status=Eyelink('closefile'); if status ~=0 disp(sprintf('closefile error, status: %d',status)) end status=Eyelink('ReceiveFile',edfFile,pwd,1); if status~=0 fprintf('problem: ReceiveFile status: %d\n', status); end if 2==exist(edfFile, 'file') fprintf('Data file %s can be found in %s\n', edfFile, pwd ); else disp('unknown where data file went') end end Eyelink('shutdown');