function change % % ___________________________________________________________________ % % Demo implemetation of detecting eye in fixation window on display % computer through fixation update events from host pc % % stimulus: A changing checker board image % % This task demonstrates stimuli presentation with Eyelink and Data Viewer % integration. % % NOTE: Please make sure you are in the same directory where the change % images are located. % % Press space to exit change % ___________________________________________________________________ % HISTORY % mm/dd/yy % % 01/28/11 NJ created % 12/20/13 LJ changed isoctave to IsOctave and EyeLink to Eyelink, case sensitive for the latest matlab % added parameters for function infixationWindow, in order to run with octave % fixed issue with non integer arguments for Eyelink('message' ...) % if ~IsOctave commandwindow; else more off; end dummymode = 0; % set trial times TIMER_DISPLAY = 150; TIMER_BLANK = 50; TRIAL_TIMEOUT = 60000; % images to use and locations of interest areas % Only 24 and 32 bit images are supported. Octave cares about case imageListA = {'REG1A.jpg' 'REG2A.jpg' 'RND1A.jpg' 'RND2A.jpg'}; imageListB = {'REG1B.jpg' 'REG2B.jpg' 'RND1B.jpg' 'RND2B.jpg'}; trigLocs = [ [149 ; 365] [279;176] [404;365] [404;176]]; stopkey=KbName('space'); try %%%%%%%%% % STEP 1% %%%%%%%%% % Added a dialog box to set your own EDF file name before opening % experiment graphics. Make sure the entered EDF file name is 1 to 8 % characters in length and only numbers or letters are allowed. if IsOctave edfFile = 'DEMO'; else prompt = {'Enter tracker EDF file name (1 to 8 letters or numbers)'}; dlg_title = 'Create EDF file'; num_lines= 1; def = {'DEMO'}; answer = inputdlg(prompt,dlg_title,num_lines,def); edfFile = answer{1}; fprintf('EDFFile: %s\n', edfFile ); end %%%%%%%%%% % STEP 2 % %%%%%%%%%% % Open a graphics window on the main screen % using the PsychToolbox's Screen function. screenNumber=max(Screen('Screens')); window=Screen('OpenWindow', screenNumber, 0,[],32,2); Screen(window,'BlendFunction',GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); [winWidth, winHeight] = WindowSize(window); %%%%%%%%%% % 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). el=EyelinkInitDefaults(window); % We are changing calibration to a black background with white targets, % no sound and smaller targets el.backgroundcolour = WhiteIndex(el.window); el.msgfontcolour = BlackIndex(el.window); el.targetbeep = 0; el.calibrationtargetcolour= BlackIndex(el.window); % for lower resolutions you might have to play around with these values % a little. If you would like to draw larger targets on lower res % settings please edit PsychEyelinkDispatchCallback.m and see comments % in the EyelinkDrawCalibrationTarget function el.calibrationtargetsize= 1; el.calibrationtargetwidth=0.5; % call this function for changes to the calibration structure to take % affect EyelinkUpdateDefaults(el); %%%%%%%%%% % STEP 4 % %%%%%%%%%% % Initialization of the connection with the Eyelink tracker % exit program if this fails. if ~EyelinkInit(dummymode) fprintf('Eyelink Init aborted.\n'); cleanup; % cleanup function return; end % open file to record data to res = Eyelink('Openfile', edfFile); if res~=0 fprintf('Cannot create EDF file ''%s'' ', edffilename); cleanup; return; end % make sure we're still connected. if Eyelink('IsConnected')~=1 && ~dummymode cleanup; return; end %%%%%%%%%% % STEP 5 % %%%%%%%%%% % SET UP TRACKER CONFIGURATION % Setting the proper recording resolution, proper calibration type, % as well as the data file content; Eyelink('command', 'add_file_preamble_text ''Recorded by EyelinkToolbox demo-experiment'''); % This command is crucial to map the gaze positions from the tracker to % screen pixel positions to determine fixation Eyelink('command','screen_pixel_coords = %ld %ld %ld %ld', 0, 0, winWidth-1, winHeight-1); Eyelink('message', 'DISPLAY_COORDS %ld %ld %ld %ld', 0, 0, winWidth-1, winHeight-1); % set calibration type. Eyelink('command', 'calibration_type = HV9'); Eyelink('command', 'generate_default_targets = YES'); % set parser (conservative saccade thresholds) Eyelink('command', 'saccade_velocity_threshold = 35'); Eyelink('command', 'saccade_acceleration_threshold = 9500'); % 5.1 retrieve tracker version and tracker software version [v,vs] = Eyelink('GetTrackerVersion'); fprintf('Running experiment on a ''%s'' tracker.\n', vs ); vsn = regexp(vs,'\d','match'); % set EDF file contents. Note the FIXUPDATE event for fixation update if v ==3 && str2double(vsn{1}) == 4 % if EL 1000 and tracker version 4.xx % remote mode possible add HTARGET ( head target) Eyelink('command', 'file_event_filter = LEFT,RIGHT,FIXATION,SACCADE,BLINK,MESSAGE,BUTTON,INPUT'); Eyelink('command', 'file_sample_data = LEFT,RIGHT,GAZE,HREF,AREA,GAZERES,STATUS,INPUT,HTARGET'); % set link data (used for gaze cursor) Eyelink('command', 'link_event_filter = LEFT,RIGHT,FIXATION,SACCADE,BLINK,MESSAGE,BUTTON,INPUT,FIXUPDATE'); Eyelink('command', 'link_sample_data = LEFT,RIGHT,GAZE,GAZERES,AREA,STATUS,INPUT,HTARGET'); else Eyelink('command', 'file_event_filter = LEFT,RIGHT,FIXATION,SACCADE,BLINK,MESSAGE,BUTTON,INPUT,FIXUPDATE'); Eyelink('command', 'file_sample_data = LEFT,RIGHT,GAZE,HREF,AREA,GAZERES,STATUS,INPUT'); % set link data (used for gaze cursor) Eyelink('command', 'link_event_filter = LEFT,RIGHT,FIXATION,SACCADE,BLINK,MESSAGE,BUTTON,INPUT,FIXUPDATE '); Eyelink('command', 'link_sample_data = LEFT,RIGHT,GAZE,GAZERES,AREA,STATUS,INPUT'); end % allow to use the big button on the eyelink gamepad to accept the % calibration/drift correction target Eyelink('command', 'button_function 5 "accept_target_fixation"'); % Tell the Eyelink to send a fixation update every 50 ms Eyelink('command', 'fixation_update_interval = %d', 50); Eyelink('command', 'fixation_update_accumulate = %d', 50); %%%%%%%%%% % STEP 6 % %%%%%%%%%% if ~dummymode % Hide the mouse cursor and Calibrate the eye tracker Screen('HideCursorHelper', window); end % enter Eyetracker camera setup mode, calibration and validation EyelinkDoTrackerSetup(el); %%%%%%%%%% % STEP 7 % %%%%%%%%%% % Now starts running individual trials % You can keep the rest of the code except for the implementation % of graphics and event monitoring % Each trial should have a pair of "StartRecording" and "StopRecording" % calls as well integration messages to the data file (message to mark % the time of critical events and the image/interest area/condition % information for the trial) for i = 1 : 4 stopKeyPressed = 0; % select the two images to be displayed in the trial imgfileA = char(imageListA(i)); imgfileB = char(imageListB(i)); % STEP 7.1 % Sending a 'TRIALID' message to mark the start of a trial in Data % Viewer. This is different than the start of recording message % START that is logged when the trial recording begins. The viewer % will not parse any messages, events, or samples, that exist in % the data file prior to this message. Eyelink('Message', 'TRIALID %d', i); % This supplies the title at the bottom of the eyetracker display Eyelink('command', 'record_status_message "* TRIAL %d/%d %s"', i,4, imgfileA); % Before recording, we place reference graphics on the host display % Must be in offline mode to transfer image to Host PC Eyelink('Command', 'set_idle_mode'); % clear tracker display and draw box at center Eyelink('Command', 'clear_screen %d', 0); % pause between trials WaitSecs(0.1); % transfer image A to host pc imgArray = imread(imgfileA); finfo = imfinfo(imgfileA); transferStatus = Eyelink('ImageTransfer', finfo.Filename ,0,0,0,0,round(winWidth/2 - finfo.Width/2) ,round(winHeight/2 - finfo.Height/2),4); if transferStatus ~= 0 fprintf('Image to host transfer failed\n'); end % give image transfer time to finish WaitSecs(0.1); % find interest area locations for fixation window trigLocs(1,i) = trigLocs(1,i) + (winWidth/2 - finfo.Width/2); trigLocs(2,i) = trigLocs(2,i) + (winHeight/2 - finfo.Height/2); fixationWindow = [trigLocs(1,i) trigLocs(2,i) trigLocs(1,i)+64 trigLocs(2,i)+64]; % draw filled box after image transfer so it is drawn on top of % image on Host PC Eyelink('command', 'draw_filled_box %d %d %d %d 2', fixationWindow(1), fixationWindow(2), fixationWindow(3), fixationWindow(4)); % STEP 7.2 % Do a drift correction at the beginning of each trial % Performing drift correction (checking) is optional for % EyeLink 1000 eye trackers. EyelinkDoDriftCorrection(el); % STEP 7.3 % start recording eye position (preceded by a short pause so that % the tracker can finish the mode transition) % The paramerters for the 'StartRecording' call controls the % file_samples, file_events, link_samples, link_events availability Eyelink('Command', 'set_idle_mode'); WaitSecs(0.05); % we are disabling samples transfered to the display but we're still % getting events. This speeds things up a little since in this % example we do not care about samples but about events from the % tracker Eyelink('StartRecording',1,1,0,1); % record a few samples before we actually start displaying % otherwise you may lose a few msec of data WaitSecs(0.1); % get eye that's tracked eye_used = Eyelink('EyeAvailable'); % returns 0 (LEFT_EYE), 1 (RIGHT_EYE) or 2 (BINOCULAR) depending on what data is if eye_used == 2 eye_used = 1; % use the right_eye data end % STEP 7.4 % Prepare and show the screen. Screen('FillRect', window, el.backgroundcolour); Screen('Flip', window); % create trial textures tex(1) = Screen('MakeTexture', window,imgArray); imgArray = imread(imgfileB); tex(2) = Screen('MakeTexture', window,imgArray); % variables to determine what to display in each iteration counter = 0; counter2 = 0; correct = 0; trialtime = GetSecs + TRIAL_TIMEOUT/1000; totalFixTime = 0; mouseTimer = GetSecs; % loop until either trial time over or subject fixates changing % square while GetSecs < trialtime if correct break; end display = mod(counter,2) + 1; if display < 2 tex2show = mod(counter2, 2) + 1; counter2 = counter2 + 1; displayTimer = GetSecs + TIMER_DISPLAY/1000; else displayTimer = GetSecs + TIMER_BLANK/1000; end while GetSecs < displayTimer [keyIsDown,secs,keyCode] = KbCheck; %#ok<*ASGLU> % if spacebar was pressed stop display if keyCode(stopkey ) sprintf('Space pressed, exiting trial\n'); Eyelink('Message', 'Stop Key pressed'); stopKeyPressed = 1; break; end if display < 2 % display Screen('FillRect', window, el.backgroundcolour); Screen('DrawTexture', window, tex(tex2show)); Screen('Flip',window); % send a msg to host with display 1 or 2 ( A or B) Eyelink('Message', 'DISPLAY %d', tex2show); else % blank screen Screen('FillRect', window, el.backgroundcolour); Screen('Flip',window); Eyelink('Message', 'BLANK_SCREEN'); end if dummymode==0 error=Eyelink('CheckRecording'); if(error~=0) disp('Error in Recording'); break; end % we need to loop over this a few times ( 30 is % randomly chosen) so that we do not miss any events % and to prevent any buffer overflow for j=1:30 evtype = Eyelink('GetNextDataType'); if evtype == el.FIXUPDATE if Eyelink('isconnected') == el.connected % if we're really measuring eye-movements evt = Eyelink('getfloatdata', evtype);% get data % only process if its the desired eye if evt.eye == eye_used % send msg with details of fixation % update event Eyelink('message', 'Fixupdate: avg_x %d, y %d, dur %d',floor(evt.gavx), floor(evt.gavy), floor(evt.entime)-floor(evt.sttime)); % determine if gaze values are within % interest region and if gaze has been % maintained over 300 ms. This method % allows for saccades as long as they % are withing interest area if infixationWindow(fixationWindow, evt.gavx,evt.gavy) totalFixTime = totalFixTime + 50; if totalFixTime >= 300 break; end else % broke fixation reset time totalFixTime = 0; end end else disp('Eyelink disconnected!'); end end end %end for else % using display PC mouse [x,y] = GetMouse(window); %#ok<*NASGU> evt.type=el.FIXUPDATE; evt.gavx=x; evt.gavy=y; if infixationWindow(fixationWindow, evt.gavx,evt.gavy) if GetSecs - mouseTimer >= 0.300 disp('in fixation window'); correct = 1; break; end else % reset mouseTimer = GetSecs; end end % has the subject completed 300 ms of fixation within iA? if totalFixTime >= 300 Beeper(el.calibration_success_beep(1), el.calibration_success_beep(2), el.calibration_success_beep(3)); correct = 1; break; end end if stopKeyPressed break; end counter = counter + 1; [keyIsDown,secs,keyCode] = KbCheck; % if spacebar was pressed stop display if keyCode(stopkey ) sprintf('Space pressed, exiting trial\n'); Eyelink('Message', 'Key pressed'); break; end end % STEP 7.6 % add 100 msec of data to catch final events and blank display WaitSecs(0.1); Eyelink('StopRecording'); Screen('FillRect', window, el.backgroundcolour); Screen('Flip', window); % did the trial time out without user fixating? send a message to % edf if GetSecs > trialtime disp('Trial time out'); Eyelink('Message', 'Trial time out'); end % STEP 7.7 % Send out necessary integration messages for data analysis % See "Protocol for EyeLink Data to Viewer Integration-> Interest % Area Commands" section of the EyeLink Data Viewer User Manual % IMPORTANT! Don't send too many messages in a very short period of % time or the EyeLink tracker may not be able to write them all % to the EDF file. % Consider adding a short delay every few messages. WaitSecs(0.001); % Send an integration message so that an image can be loaded as % overlay backgound when performing Data Viewer analysis. This % message can be placed anywhere within the scope of a trial (i.e., % after the 'TRIALID' message and before 'TRIAL_RESULT') % See "Protocol for EyeLink Data to Viewer Integration -> Image % Commands" section of the EyeLink Data Viewer User Manual. Eyelink('Message', '!V IMGLOAD CENTER %s %d %d', imgfileA, winWidth/2, winHeight/2); % Send out interest area information for the trial Eyelink('Message', '!V IAREA RECTANGLE %d %d %d %d %d %s', 2, fixationWindow(1), fixationWindow(2), fixationWindow(3), fixationWindow(4),'change'); % Send messages to report trial condition information % Each message may be a pair of trial condition variable and its % corresponding value follwing the '!V TRIAL_VAR' token message % See "Protocol for EyeLink Data to Viewer Integration-> Trial % Message Commands" section of the EyeLink Data Viewer User Manual WaitSecs(0.001); Eyelink('Message', '!V TRIAL_VAR index %d', i); Eyelink('Message', '!V TRIAL_VAR imgfile %s', imgfileA); % STEP 7.8 % Sending a 'TRIAL_RESULT' message to mark the end of a trial in % Data Viewer. This is different than the end of recording message % END that is logged when the trial recording ends. The viewer will % not parse any messages, events, or samples that exist in the data % file after this message. Eyelink('Message', 'TRIAL_RESULT 0'); end %%%%%%%%%% % STEP 8 % %%%%%%%%%% % End of Experiment; close the file first % close graphics window, close data file and shut down tracker Eyelink('Command', 'set_idle_mode'); WaitSecs(0.5); Eyelink('CloseFile'); try fprintf('Receiving data file ''%s''\n', edfFile ); status=Eyelink('ReceiveFile'); if status > 0 fprintf('ReceiveFile status %d\n', status); end if 2==exist(edfFile, 'file') fprintf('Data file ''%s'' can be found in ''%s''\n', edfFile, pwd ); end catch fprintf('Problem receiving data file ''%s''\n', edfFile ); end %%%%%%%%%% % STEP 9 % %%%%%%%%%% % run cleanup function (close the eye tracker and window). cleanup; catch %#ok<*CTCH> cleanup; fprintf('%s: some error occured\n', mfilename); psychrethrow(lasterror); %#ok end function cleanup % Shutdown Eyelink: Eyelink('Shutdown'); Screen('CloseAll'); end function fix = infixationWindow(fixationWindow, mx,my) % determine if gx and gy are within fixation window fix = mx > fixationWindow(1) && mx < fixationWindow(3) && ... my > fixationWindow(2) && my < fixationWindow(4) ; end end