Skip to main content
Skip table of contents

Recording and plotting pursuit data with the TRACKPixx3

This demo requires TRACKPixx Revision 18 or later. You can check for recent firmware updates at http://vpixx.com/whatsnew

This simple eye-tracking demo displays a dot moving in a circle around the center of the screen. We record 5 seconds of pursuit data. This data is then superimposed over the target path and saved into a .csv file for further analysis in your software of choice.

At the beginning of the demo there is an optional calibration step. This step calls TPxTrackpixx3CalibrationTesting, which implements a standard MATLAB TRACKPixx3 calibration script. The tracker must be calibrated for every new participant. We also recommend calibrating after a participant moves away from the chinrest.

Following calibration the demo wakes the TRACKPixx3, sets up a tracking schedule with SetupTPxSchedule, and starts this schedule with StartTpxSchedule when the target appears. This schedule controls recording of eye data, which is stored on a data buffer on the DATAPixx3 controller. We record 5 seconds of pursuit data, and plot this in MATLAB for inspection. Eye data is also written into a .csv file for further analysis.

MATLAB
function TPxPursuitTracking(initRequired)
%
% This demo draws a moving dot for 5 seconds, and tracks the smooth
% pursuit movement of the viewer. Data is plotted and saved.

% Most recently tested with:
% -- TRACKPixx3 firmware revision 18 
% -- DATAPixx3 firmware revision 19 
% -- MATLAB version 9.6.0.1150989 (R2019a) 
% -- Psychtoolbox verison 3.0.15 
% -- Datapixx Toolbox version 3.7.5735
% -- Windows 10 version 1903, 64bit

% Oct 22, 2019  lef     Written
% Mar 26  2020  lef     Updated


%% Step 1 - Initialize (if needed)

if nargin==0
    initRequired=1;
end

% Get some user input
fileName= input('Enter participant name: ', 's');
fileID = [fileName '.mat'];

%If a calibration is needed, call the calibration script
if initRequired
    fprintf('\nInitialization required\n\nCalibrating the device...');
    TPxTrackpixx3CalibrationTesting;
end

%Connect to TRACKPixx3
Datapixx('Open');
Datapixx('SetTPxAwake');
Datapixx('RegWrRd');


%% Step 2 - Set up the TRACKPixx recording schedule

Datapixx('SetupTPxSchedule');
%write all commands to the DATAPixx device register
Datapixx('RegWrRd');


%% Step 3 - Show our image and record eye position

AssertOpenGL;                                             

%set our recording time
viewingTime = 5;
          
%open window
Screen('Preference', 'SkipSyncTests', 1 );
screenID = 2;                                              %change to switch display
[windowPtr, rect]=Screen('OpenWindow', screenID, [0,0,0]);
Screen('BlendFunction', windowPtr, 'GL_SRC_ALPHA', 'GL_ONE_MINUS_SRC_ALPHA');
Screen('Flip',windowPtr);

%determine a trajectory
radius = 300; %in pixels for now
targetSize = 20;
trajectory = 0 : 0.01 : 2*pi;

%show instructions to participant
text_to_draw = ['PURSUIT DEMO:\n\nFollow the target with your eyes.\n\nPress any key to start.'];
DrawFormattedText(windowPtr, text_to_draw, 'center', 700, 255);
Screen('Flip', windowPtr);

%wait for participant to continue
[~, ~, ~] = KbPressWait;
Screen('Flip', windowPtr);
WaitSecs(1);

% %set up recording to start on the same frame flip that shows the image.
% %We also get the time of the flip using a Marker which saves a time of the
% %frame flip on the DATAPixx clock
Datapixx('StartTPxSchedule');
Datapixx('SetMarker');
Datapixx('RegWrVideoSync');
    
startTime = NaN;
currentTime = NaN;
positionCounter = 0;

while ~((currentTime - startTime) >= viewingTime)

    positionCounter = positionCounter + 1;
    
    %if we get to the end of our trajectory, restart
    if positionCounter > numel(trajectory)
        positionCounter = 1;
    end
    
    %draw our image and flip
    centerX = rect(3)/2 + radius*(cos(trajectory(positionCounter)));
    centerY = rect(4)/2 + radius*(sin(trajectory(positionCounter)));
    
    Screen('FillOval', windowPtr, [255,50,50], [centerX-targetSize/2, centerY-targetSize/2, centerX+targetSize/2, centerY+targetSize/2 ]);
    Screen('Flip', windowPtr);
    
    Datapixx('RegWrRd');
    startTime = Datapixx('GetMarker');
    currentTime  = Datapixx('GetTime');
    
end
    
%stop recording
Datapixx('StopTPxSchedule');
Datapixx('RegWrRd');
endTime = Datapixx('GetTime');

%read in eye data
status = Datapixx('GetTPxStatus');
toRead = status.newBufferFrames;
[bufferData, ~, ~] = Datapixx('ReadTPxData', toRead);

%bufferData is formatted as follows:
%1      --- Timetag (in seconds)
%2      --- Left Eye X (in pixels) 
%3      --- Left Eye Y (in pixels)
%4      --- Left Pupil Diameter (in pixels)
%5      --- Right Eye X (in pixels)
%6      --- Right Eye Y (in pixels)
%7      --- Right Pupil Diameter (in pixels)
%8      --- Digital Input Values (24 bits)
%9      --- Left Blink Detection (0=no, 1=yes)
%10     --- Right Blink Detection (0=no, 1=yes) 
%11     --- Digital Output Values (24 bits)
%12     --- Left Eye Fixation Flag (0=no, 1=yes) 
%13     --- Right Eye Fixation Flag (0=no, 1=yes)  
%14     --- Left Eye Saccade Flag (0=no, 1=yes) 
%15     --- Right Eye Saccade Flag (0=no, 1=yes)  
%16     --- Message code (integer) 
%17     --- Left Eye Raw X (in pixels) 
%18     --- Left Eye Raw Y (in pixels)  
%19     --- Right Eye Raw X (in pixels)  
%20     --- Right Eye Raw Y (in pixels) 

%IMPORTANT: "RIGHT" and "LEFT" refer to the right and left eyes shown
%in the console overlay. In tabletop and MEG setups, this view is
%inverted. This means "RIGHT" in our labelling convention corresponds
%to the participant's left eye. Similarly "LEFT" in our convention
%refers to left on the screen, which corresponds to the participant's
%right eye.

%If you are using an MRI setup with an inverting mirror, "RIGHT" will
%correspond to the participant's right eye.

%save eye data from trial as a table in the trial structure
eyeData = array2table(bufferData, 'VariableNames', {'TimeTag', 'LeftEyeX', 'LeftEyeY', 'LeftPupilDiameter', 'RightEyeX', 'RightEyeY', 'RightPupilDiameter',...
                                'DigitalIn', 'LeftBlink', 'RightBlink', 'DigitalOut', 'LeftEyeFixationFlag', 'RightEyeFixationFlag', 'LeftEyeSaccadeFlag', 'RightEyeSaccadeFlag',...
                                'MessageCode', 'LeftEyeRawX', 'LeftEyeRawY', 'RightEyeRawX', 'RightEyeRawY'});
                            

%get some other trial data
pursuitTime = endTime - startTime;

%interim save
save(fileID, 'eyeData', 'pursuitTime');    

%Close everything
Screen('Closeall');
Datapixx('SetTPxSleep');
Datapixx('RegWrRd');
Datapixx('Close');


%% Step 4 - Plot some gaze paths

fprintf('\nRecording lasted %f seconds', pursuitTime);
figure();

plot(radius*(cos(trajectory)), radius*(sin(trajectory)), 'Color', [0.5, 0.5, 0.5], 'LineWidth', targetSize/2);
hold on

x = eyeData.LeftEyeX(:,:);
y = eyeData.LeftEyeY(:,:);
plot(x,y, 'ob', 'linewidth', 1, 'markersize', 1); 

x = eyeData.RightEyeX(:,:);
y = eyeData.RightEyeY(:,:);
plot(x,y, 'or', 'linewidth', 1, 'markersize', 1); 

xlim([-rect(3)/2, rect(3)/2]);
ylim([-rect(4)/2, rect(4)/2]);

legend({'Target path','Left eye', 'Right Eye'});
xlabel('X position (pixels)');
ylabel('Y position (pixels)');
title('Gaze path for 5s smooth pursuit');



%% Step 5 - Write data to csv for subsequent analysis

savefig(fileName);
csvFileID = [fileName '_Results.csv'];
writetable(eyeData, csvFileID);


end
JavaScript errors detected

Please note, these errors can depend on your browser setup.

If this problem persists, please contact our support.