Sample 07: Transfering event data and gaze position data during the experiment

What does this sample do?

Consider that you want to increase/decrease stimulus onset time according to saccade latency in the previous trial. Using getEyePosition(), as shown in Sample01, is one of solution for this purpose; however, this solution may be inadequate for researchers who have to measure saccade latency accurately. Why? In Sample01, getEyePosition() is called only once per screen flipping. This means that sampling frequency of gaze position is restricted by display’s refresh rate. If display’s reflesh rate is 60Hz, gaze position data transfered by getEyePosition() is also 60Hz even when a camera connected to SimpleGazeTracker runs at a frequency higher than 60Hz. To overcome this problem, following methods are added to GazeParser 0.6.4,

getEyePositionList() returns the latest N-samples of gaze position as a numpy.ndarray object. This method is useful to detect saccade during trial. For example, let us define sacacde onset by the time when gaze position moved faster than 30deg/s at least 12ms. If camera’s frequency is 250Hz, 12ms corresponds to 3 samples of gaze velocity. Because we have to calculate velocity from position, consecutive 4 samples of gaze positions are necessary. Here shows a sample code for saccade detection.:

#Get the latest 4 samples.
gazedata = tracker.getEyePositionList(4, getPupil=False)

#Calculate horizontal and vertical component of velocity.
#The second and third columns hold horizontal and vertical
#component of gaze position, respectively (monocular recording).
vel = numpy.diff(gazeata,axis=0)[:,1:3]

#Calculate absolute velocity.
absvel = numpy.apply_along_axis(numpy.linalg.norm, 1, vel)

#Check whether all samples exceeds threshold.
#The value of 'threshold' must be set in advance.
if (absvel>threshold).all():
    #saccade is detected.

Don’t request too large samples (e.g. 100) by using getEyePositionList(). Transferring large samples may take tens of milliseconds. When getEyePositionList() is called with a short interval, a part of the data is transferred duplicately (Figure 1).


Figure 1

If you want to avoid duplication, pass negative value to getEyePositionSignal(). getEyePosition() ignores data that have already been transferred if the argument is negative (Figure 2). Absolute value of the argument limits maximum length of transferred data. No data will be transferred if interval of calling this method is too short.


Figure 2

If you have to transfer large samples to prepare for next trial, use getWholeEyePositionList() and getWholeMessageList() after each trial has finished. These functions all gaze data and messages recorded in the latest recording.:

#Recording is finished.

#Call getWholeEyePositionList and getWholeMessageList.
gazedata = tracker.getWholeEyePositionList()
eventdata = tracker.getWholeMessageList()

In the following sample script, target position jumps immediately after a saccade onset is detected. It is known that threshold for detection of target jump increases when target jumps near the time of saccade execution (saccade supression of displacement).

When the sample script is started, the script asks you several parameters (Figure 3). Set parameters appropriate for your environment.


Figure 3

Parameter Description
Camera FPS How many frames your camera captures per second.
Dots per degree How many dots your display includes within a width of 1cm.
Threshold (deg/s) Velocity threshold for saccade detection.
Number of samples How many gaze positions are used to calculate velocity. This determines how long eye must move faster than threshold to be considered as saccade onset.
Offset (pix) Length of target jump.

Figure 4 shows sequence of one trial. A yellow target appears at the left of the screen. Then, the target moved to the right. Please make a saccade to follow the target. The target jumps slightly to the left or right immediately after saccade onset is detected. Please answer the direction of target jump by the left or right arrow key.


Figure 4

Please note that this sample code doesn’t check amplitude and direction of saccade. These parameters should be checked to study saccadic supression.


In order to work getEyePositionList(), getWholeEyePositionList() and getWholeMessageList() under Dummy mode, recordCurrentMousePos() must be called during recording. isDummy() is useful to call recordCurrentMousePos() only when the controller is running in the dummy mode. See sample code for example.

Codes (VisionEgg)

Sorry, sample code for VisionEgg is in preparation.