Created
September 9, 2015 13:03
-
-
Save lindemann09/ad09d9d685e782349cfb to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python | |
# -*- coding: utf-8 -*- | |
from math import sqrt | |
import gc | |
import pylink | |
from pylink import getEYELINK | |
from expyriment.stimuli import Circle | |
from expyriment.misc import constants | |
from expyriment.misc.geometry import coordinates2position | |
def init_eyelink(exp, dummy=False): | |
""" | |
Initializes the eytracker for the experiment. | |
Parameters | |
---------- | |
exp : expyriment experiment | |
The experiment object | |
dummy : bool | |
should tracker run in dummy mode | |
Returns | |
------- | |
tracker : EyeLink object | |
the tracker object | |
edfFileName : str | |
name of the EDF file | |
""" | |
if dummy: | |
tracker = pylink.EyeLink(None) | |
else: | |
tracker = pylink.EyeLink("100.1.1.1") #defaults to tracker address "100.1.1.1" | |
pylink.openGraphics() | |
# experimental code | |
exp_code = exp.experiment_info[0] | |
# subject number | |
if exp.subject is not None: # not in developer mode | |
subject_code = str(exp.subject) | |
else: | |
subject_code = "XX" | |
# filename | |
edfFileName = exp_code + subject_code + ".EDF" | |
#print edfFileName | |
pylink.getEYELINK().openDataFile(edfFileName) | |
pylink.flushGetkeyQueue(); #gets rid of old keys from the tracker key queue | |
pylink.getEYELINK().setOfflineMode(); | |
screen_size = exp.screen.size | |
#print screen_size | |
#left, top, right, bottom coordinates: | |
pylink.getEYELINK().sendCommand("screen_pixel_coords = 0 0 %d %d" %(screen_size[0] - 1, screen_size[1] - 1)) | |
pylink.getEYELINK().sendMessage("DISPLAY_COORDS 0 0 %d %d" %(screen_size[0] - 1, screen_size[1] - 1)) | |
## pylink.getEYELINK().sendCommand("screen_pixel_coords = %d %d %d %d" %(-screen_size[0]/2 + 1, screen_size[1]/2 - 1, | |
## screen_size[0]/2 - 1, -screen_size[1]/2 + 1)) | |
## pylink.getEYELINK().sendMessage("DISPLAY_COORDS %d %d %d %d" %(-screen_size[0]/2 + 1, screen_size[1]/2 - 1, | |
## screen_size[0]/2 - 1, -screen_size[1]/2 + 1)) | |
tracker_software_ver = 0 | |
eyelink_ver = pylink.getEYELINK().getTrackerVersion() | |
if eyelink_ver == 3: | |
tvstr = pylink.getEYELINK().getTrackerVersionString() | |
vindex = tvstr.find("EYELINK CL") | |
tracker_software_ver = int(float(tvstr[(vindex + len("EYELINK CL")):].strip())) | |
if eyelink_ver>=2: | |
pylink.getEYELINK().sendCommand("select_parser_configuration 0") | |
if eyelink_ver == 2: #turn off scenelink camera stuff | |
pylink.getEYELINK().sendCommand("scene_camera_gazemap = NO") | |
else: | |
pylink.getEYELINK().sendCommand("saccade_velocity_threshold = 35") | |
pylink.getEYELINK().sendCommand("saccade_acceleration_threshold = 9500") | |
# set EDF file contents | |
pylink.getEYELINK().sendCommand("file_event_filter = LEFT,RIGHT,FIXATION,SACCADE,BLINK,MESSAGE,BUTTON") | |
if tracker_software_ver>=4: | |
pylink.getEYELINK().sendCommand("file_sample_data = LEFT,RIGHT,GAZE,AREA,GAZERES,STATUS,HTARGET") | |
else: | |
pylink.getEYELINK().sendCommand("file_sample_data = LEFT,RIGHT,GAZE,AREA,GAZERES,STATUS") | |
# set link data (used for gaze cursor) | |
pylink.getEYELINK().sendCommand("link_event_filter = LEFT,RIGHT,FIXATION,SACCADE,BLINK,BUTTON") | |
if tracker_software_ver>=4: | |
pylink.getEYELINK().sendCommand("link_sample_data = LEFT,RIGHT,GAZE,GAZERES,AREA,STATUS,HTARGET") | |
else: | |
pylink.getEYELINK().sendCommand("link_sample_data = LEFT,RIGHT,GAZE,GAZERES,AREA,STATUS") | |
#button to accept fixation during drift | |
pylink.getEYELINK().sendCommand("button_function %d 'accept_target_fixation'"%(5)); | |
#pylink.getEYELINK().sendCommand("button_function %d 'accept_target_fixation'"%(pylink.ENTER_KEY)); | |
bg_col = exp.background_colour | |
fg_col = exp.foreground_colour | |
pylink.setCalibrationColors(fg_col, bg_col) | |
pylink.setTargetSize(screen_size[0] / 70, screen_size[0] / 300); #select best size for calibration target | |
pylink.setCalibrationSounds("", "", ""); | |
pylink.setDriftCorrectSounds("", "off", "off"); | |
return tracker, edfFileName | |
def start_recording(disable_gc = True): | |
error = getEYELINK().startRecording(1, 1, 1, 1) #0 if successful, takes 10-30 ms for recording to begin | |
#print getEYELINK().getTrackerMode() | |
if error: | |
print ("ERROR: Could not start Recording!") | |
exit() | |
if disable_gc: | |
gc.disable() #disable python garbage collection to avoid delays | |
pylink.beginRealTimeMode(100) #sets system priority highest (for windows?) and waits 100 ms | |
#wait for sample data via link for max 1000 ms | |
if not getEYELINK().waitForBlockStart(1000,1,0): #returns true if data available | |
end_recording() | |
print ("ERROR: No link samples received!") | |
getEYELINK().sendMessage("TRIAL ERROR") | |
return | |
def end_eyelink(): | |
''' | |
Cleanup and close tracker. | |
''' | |
if pylink.getEYELINK() != None: | |
# File transfer and cleanup! | |
pylink.getEYELINK().setOfflineMode(); | |
pylink.msecDelay(500); | |
#Close the file and transfer it to Display PC | |
pylink.getEYELINK().closeDataFile() | |
#pylink.getEYELINK().receiveDataFile(edfFileName, edfFileName) | |
pylink.getEYELINK().close(); | |
pylink.closeGraphics() | |
def end_recording(enable_gc = True): | |
''' | |
Ends recording: adds 100 msec of data to catch final events | |
''' | |
pylink.getEYELINK().sendMessage("REC_STOP"); | |
pylink.endRealTimeMode(); #set system priority back to normal (still slightly higher though) | |
pylink.pumpDelay(100); | |
pylink.getEYELINK().stopRecording(); | |
#process and dispatch any waiting messages: | |
while pylink.getEYELINK().getkey() : #0 if no key pressed | |
pass; | |
if enable_gc: | |
gc.enable() #re-enable python garbage collection to do memory cleanup at the end of trial | |
def do_drift(exp): | |
#The loop does drift correction at the start of each trial | |
while True: | |
#while pylink.getEYELINK().getkey() != pylink.ESC_KEY: | |
# Checks whether we are still connected to the tracker | |
if not pylink.getEYELINK().isConnected(): | |
return ABORT_EXPT | |
# Does drift correction and handles the re-do camera setup situations | |
try: | |
error = pylink.getEYELINK().doDriftCorrect(exp.screen.center_x, exp.screen.center_y, 1, 0) #0 if successful | |
if error != 27: #27 = escape key pressed at tracker to enter tracker setup menu | |
return | |
else: | |
pylink.getEYELINK().doTrackerSetup() #switches the EyeLink tracker to the setup menu | |
except: | |
pylink.getEYELINK().doTrackerSetup() | |
class GazeDetector(): | |
def __init__(self, position_array, max_deviation, expyriment_screen): | |
""" """ | |
self.position_array = position_array | |
self.max_deviation = max_deviation | |
self.screen = expyriment_screen | |
self._prev_fixation = -1 | |
self._prev_gaze = [0,0] | |
self.fixation_changed = False | |
def get_fixation(self, present_gaze = False): | |
"""Returns the id of the position in the position array that is currently | |
fixated. Returns None, if no predefined position is fixated. | |
""" | |
error = getEYELINK().isRecording() #check if still recording | |
if error: | |
end_recording(); | |
return str(error) + 'error' #ausgabe | |
fixation = None | |
s = getEYELINK().getNewestSample() # check for new sample update | |
if s is not None: #depending on the eye get the x,y coordinates | |
if s.isRightSample(): | |
gaze = coordinates2position(s.getRightEye().getGaze()) | |
elif s.isLeftSample(): | |
gaze = coordinates2position(s.getLeftEye().getGaze()) | |
else: | |
return None | |
for cnt, pos in enumerate(self.position_array): | |
if sqrt((pos[0]-gaze[0])**2 + (pos[1]-gaze[1])**2) < self.max_deviation: | |
fixation = cnt | |
break | |
else: | |
return None | |
self.fixation_changed = (fixation != self._prev_fixation) | |
self._prev_fixation = fixation | |
if present_gaze: | |
Circle(10, colour=constants.C_GREY, | |
position=self._prev_gaze).present(update=False, clear=False) | |
Circle(10, colour=constants.C_RED, | |
position=gaze).present(update=False, clear=False) | |
for cnt, pos in enumerate(self.position_array): | |
if fixation == cnt: | |
colour = constants.C_GREEN | |
else: | |
colour = constants.C_BLACK | |
Circle(self.max_deviation, colour=colour, | |
position=pos).present(update=False, clear=False) | |
self.screen.update() | |
self._prev_gaze = gaze | |
return fixation |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment