#!/usr/bin/env python
# -*- coding: utf-8 -*-
##
# Copyright (c) 1996-2012, SR Research Ltd., All Rights Reserved
#
#
# For use by SR Research licencees only. Redistribution and use in source
# and binary forms, with or without modification, are NOT permitted.
#
#
#
# Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in
# the documentation and/or other materials provided with the distribution.
#
# Neither name of SR Research Ltd nor the name of contributors may be used
# to endorse or promote products derived from this software without
# specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS
# IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
# TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
# PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
# LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
# $Date: 2012/10/25 19:51:00 $
#
#
import math
from . import constants
## The EyeLinkAddress class is used to hold addresses to EyeLink nodes. An instance of EyeLinkAddress class
# can be initialized with the class constructor:
#
# \code
# EyeLinkAddress(ip = (100,1,1,1), port = 4000)
# \endcode
#
# where <i>ip</i> is a four-item tuple containing the IP address of the EyeLink node and <i>port</i> is the port number of
# the connection.
#
# For example,
# \code
# myAddress = EyeLinkAddress((100, 1, 1, 1), 4000)
# \endcode
class EyeLinkAddress:
## Constructor
# @param ip optional ipaddress in tuple form. eg. if the ip address is 192.168.25.48, the tuple form is (192,168,25,48)
# The default ip address is 100.1.1.1
# @param port optional port value as integer. The default value is 4000.
def __init__(self, ip = (100,1,1,1), port = 4000):
self.ip = ip
self.port = port
## Returns the IP address of the EyeLink node.
# @return
# A four-item tuple (integer) containing the IP address of the EyeLink node.
def getIP(self):
return self.ip
## Returns the port number of the EyeLink node.
# @return
# An integer for the port number of the connection.
def getPort(self):
return self.port
def __str__(self):
return "%d.%d.%d.%d:%d"%(self.ip[0],self.ip[1],self.ip[2],self.ip[3],self.port)
## EyelinkMessage class, derived from EyeLinkAddress class, is used to send and receive messages between
# EyeLink nodes. Instances of this class are commonly used as the return values of the \c nodeReceive() and
# \c getNode() methods of the EyeLinkListener or EyeLink class. An instance of EyelinkMessage class can be
# initialized with the class constructor:
#
# \code
# EyelinkMessage(ip = (100,1,1,1), port = 4000, msg = "")
# \endcode
#
# where <i>ip</i> is a four-item tuple containing the IP address of the EyeLink node, <i>port</i> is the port number of the
# connection, <i>msg</i> is the message to be sent to or received from the node.
#
# For example,
# \code
# myMessage = EyelinkMessage((100, 1, 1, 1), 4000, "test")
# \endcode
class EyelinkMessage(EyeLinkAddress):
## Constructor
# @param ip optional ipaddress in tuple form. eg. if the ip address is 192.168.25.48, the tuple form is (192,168,25,48)
# The default ip address is 100.1.1.1
# @param port optional port value as integer. The default value is 4000.
# @param msg text message.
def __init__(self, ip = (100,1,1,1), port = 4000, msg = ""):
EyeLinkAddress.__init__(self,ip,port)
self.msg = msg
def __str__(self):
return "%s -- %s"%(EyeLinkAddress.__str__(self), self.msg)
## Returns the message to be sent to or received from the node.
# @return
# Text message.
def getText():
return self.msg
## Sample data for left and right eye.
#
# The \c getRightEye() or \c getLeftEye() functions returns an instance of SampleData class, which contains the current
# sample position (raw, HREF, or gaze) and pupil size information of the desired eye. The following methods can
# be used to retrieve the attributes of an instance of the SampleData class.
#
# For example, the x gaze position of the left eye for a given sample can be retrieved as:
#
# \code
# newSample = EYELINK.getFloatData()
# gaze = newSample. getLeftEye().getGaze()
# left_eye_gaze_x = gaze[0]
# \endcode
#
# If certain property information not sent for this sample, the value \c MISSING_DATA (or \c 0, depending on the field)
# will be returned, and the corresponding bit in the flags field will be zero (see eye_data.h for a list of bits).
# Data may be missing because of the tracker configuration (set by commands sent at the start of the experiment,
# from the Set Options screen of the %EyeLink II tracker, or from the default configuration set by the DATA.INI file
# for the %EyeLink I tracker). Eye position data may also be set to \c MISSING_VALUE during a blink.
class SampleData:
def __init__(self,px, py,hx, hy,pa,gx, gy):
self.__pupil__ =(px, py)
self.__headref__ = (hx, hy)
self.__area__ = pa
self.__gaze__ = (gx,gy)
## Display gaze position (in pixel coordinates set by the
# \c screen_pixel_coords command). The first and second
# item of the tuple store the x- and y- coordinate gaze
# position respectively.
# @return
# Two-item tuple in the format of (float, float).
def getGaze(self):
return self.__gaze__
## HREF angular coordinates. The first and second items of the
# tuple are for the x and y coordinates, respectively.
# @return
# Two-item tuple in the format of (float, float).
def getHREF(self):
return self.__headref__
## Camera x, y of pupil center. The first and second items of
# the tuple store pupil center in the x- and y- coordinate
# respectively.
# @return
# Two-item tuple in the format of (float, float).
def getRawPupil(self):
return self.__pupil__
## Pupil size (in arbitrary units, area or diameter as selected).
# @return
# Float.
def getPupilSize(self):
return self.__area__
def __str__(self):
return " %d %d %d %d %d %d %d "%(self.__pupil__[0],self.__pupil__[1], self.__headref__[0],self.__headref__[1], self.__area__, self.__gaze__[0],self.__gaze__[1])
## The EyeLink toolkit library defines special data classes that allow the same programming calls to be used on
# different platforms such as MS-DOS, Windows, and Macintosh. You will need to know these classes to read the
# examples and to write your own experiments. In this documentation, the common data classes are: Sample class,
# Eye Event Classes, MessageEvent Class, and IOEvent Class. You only need to read this section if you are
# planning to use real-time link data for gaze-contingent displays or gaze-controlled interfaces, or to use data
# playback.
#
# The %EyeLink tracker measures eye position 250 or 500 times per second depending on the tracking mode you are
# working with, and computes true gaze position on the display using the head camera data. This data is stored
# in the EDF file, and made available through the link in as little as 3 milliseconds after a physical eye movement.
#
# Samples can be read from the link by \c getFloatData() or \c getNewestSample() method of the EyeLink/EyeLinkLisenter
# class. These functions can return instances of Sample class. For example,
#
# \code
# newSample = EYELINK.getFloatData()
# \endcode
#
# The following methods can be used to retrieve properties of a Sample class instance. For example, the time of
# the sample can be retrieved as \c newSample.getTime(). Please note that all methods for the Sample class do not
# take a parameter whereas the return values are noted.
class Sample:
def __init__(self, time, type, flags, px, py, hx, hy, pa, gx, gy, rx, ry, status, input, buttons, htype, hdata):
self.__time__ = time
self.__type__ = type
self.__flags__ = flags
if(flags & 0x8000):
self.__leftData__ = SampleData(px[0], py[0],hx[0], hy[0],pa[0],gx[0], gy[0])
else:
self.__leftData__ = None
if(flags & 0x4000):
self.__rightData__= SampleData(px[1], py[1],hx[1], hy[1],pa[1],gx[1], gy[1])
else:
self.__rightData__ = None
self.__ppd__ = (rx, ry)
self.__status__ = status
self.__input__ = input
self.__buttons__ = buttons
self.__htype__ = htype
self.__hdata__ = hdata
## Convenient method to clone a sample
#
def initFromSample(self,sample):
self.__time__ = sample.__time__
self.__type__ = sample.__type__
self.__flags__ = sample.__flags__
self.__leftData__ = sample.__leftData__
self.__rightData__= sample.__rightData__
self.__ppd__ = sample.__ppd__
self.__status__ = sample.__status__
self.__input__ = sample.__input__
self.__buttons__ = sample.__buttons__
self.__htype__ = sample.__htype__
self.__hdata__ = sample.__hdata__
## \c 1 if the sample contains the left eye data; \c 0 if not.
# @return
# Integer.
def isLeftSample(self):
return (self.__leftData__ != None)
## \c 1 if the sample contains the right eye data; \c 0 if not.
# @return
# Integer.
def isRightSample(self):
return (self.__rightData__ != None)
## \c 1 if the sample contains data from both eyes; \c 0 if not.
# @return
# Integer.
def isBinocular(self):
return (self.__leftData__ != None and self.__rightData__ != None)
## Timestamp when camera imaged eye (in milliseconds since %EyeLink tracker was activated).
# @return
# Long integer.
def getTime(self):
return self.__time__
## Always \c SAMPLE_TYPE.
# @return
# Integer.
def getType(self):
return self.__type__
## Angular resolution at current gaze position in screen pixels
# per visual degree. The first item of the tuple stores the x-coordinate
# resolution and the second item of the tuple
# stores the y-coordinate resolution.
# @return
# Two-item tuple in the format of (float, float).
def getPPD(self):
return self.__ppd__
## Error and status flags (only useful for %EyeLink II, report CR
# status and tracking error). See eye_data.h for useful bits.
# @return
# Integer.
def getStatus(self):
return self.__status__
## Data from input port(s).
# @return
# Integer.
def getInput(self):
return self.__input__
## Bits indicating what types of data are present, and for which eye(s). See eye_data.h for useful bits.
# @return
# Integer.
def getFlags(self):
return self.__flags__
## Button input data: high 8 bits indicate changes from last
# sample, low 8 bits indicate current state of buttons 8 (MSB) to 1 (LSB).
# @return
# Integer.
def getButtons(self):
return self.__buttons__
## Returns the sample data information from the desired eye.
# @return
# Instance of sample data class.
def getRightEye(self):
return self.__rightData__
## Returns the sample data information from the desired eye.
# @return
# Instance of sample data class.
def getLeftEye(self):
return self.__leftData__
## Returns the href data
#
def getHData(self):
return self.__hdata__
## Returns Eye data status.
# @return
# \arg 2 if both left and right eye data are present
# \arg 1 if right eye data present
# \arg 0 if left eye data present
def getEye(self):
if (self.__leftData__ is not None and self.__rightData__ is not None):
return 2
elif self.__leftData__ is not None :
return 0
else:
return 1
## Target Distance
# @return
# Float.
def getTargetDistance(self):
return self.__hdata__[2]
## Target X
# @return
# Float.
def getTargetX(self):
return self.__hdata__[0]
## Target Y
# @return
# Float.
def getTargetY(self):
return self.__hdata__[1]
## Target Flags
# @return
# Float.
def getTargetFlags(self):
return self.__hdata__[3]
def __str__(self):
rv= "Sample: %i %d %d %i %i %i %i "%(self.__time__, self.__ppd__[0],self.__ppd__[1], self.__status__, self.__input__, self.__buttons__, self.__htype__)
rv += self.__leftData__.__str__()
rv += self.__rightData__.__str__()
return rv
def newSample(time, flags, px, py, hx, hy, pa, gx, gy, rx, ry, status, input, buttons, htype, hdata):
return Sample(time,constants.SAMPLE_TYPE, flags, px, py, hx, hy, pa, gx, gy, rx, ry, status, input, buttons, htype, hdata)
class rawSample:
def __init__(self, time, left_raw_pupil_x, left_raw_pupil_y, left_raw_cr_x, left_raw_cr_y, left_pupil_area, left_cr_area, left_pupil_dimension_x,left_pupil_dimension_y, left_cr_dimension_x, left_cr_dimension_y, left_win_pos_x, left_win_pos_y, left_pupil_cr_x, left_pupil_cr_y, left_cr_area2, left_raw_cr2_x, left_raw_cr2_y, right_raw_pupil_x, right_raw_pupil_y, right_raw_cr_x, right_raw_cr_y, right_pupil_area, right_cr_area, right_pupil_dimension_x, right_pupil_dimension_y, right_cr_dimension_x, right_cr_dimension_y, right_win_pos_x, right_win_pos_y, right_pupil_cr_x, right_pupil_cr_y, right_cr_area2, right_raw_cr2_x, right_raw_cr2_y ):
#print "pupil dimension", left_pupil_dimension_x,left_pupil_dimension_y
self.__time__ = time
self.__left_rawpupil__ = (left_raw_pupil_x, left_raw_pupil_y)
self.__left_rawcr__ = (left_raw_cr_x, left_raw_cr_y)
self.__left_pupilarea__ = left_pupil_area
self.__left_crarea__ = left_cr_area
self.__left_pupildimension__ = (left_pupil_dimension_x,left_pupil_dimension_y)
self.__left_crdimension__ = (left_cr_dimension_x, left_cr_dimension_y)
self.__left_winpos__ = (left_win_pos_x, left_win_pos_y)
self.__left_pupilcr__ = (left_pupil_cr_x, left_pupil_cr_y)
self.__left_crarea2__ = (left_cr_area2)
self.__left_rawcr2__ = (left_raw_cr2_x, left_raw_cr2_y)
self.__right_rawpupil__ = (right_raw_pupil_x, right_raw_pupil_y)
self.__right_rawcr__ = (right_raw_cr_x, right_raw_cr_y)
self.__right_pupilarea__ = right_pupil_area
self.__right_crarea__ = right_cr_area
self.__right_pupildimension__ = (right_pupil_dimension_x,right_pupil_dimension_y)
self.__right_crdimension__ = (right_cr_dimension_x, right_cr_dimension_y)
self.__right_winpos__ = (right_win_pos_x, right_win_pos_y)
self.__right_pupilcr__ = (right_pupil_cr_x, right_pupil_cr_y)
self.__right_crarea2__ = (right_cr_area2)
self.__right_rawcr2__ = (right_raw_cr2_x, right_raw_cr2_y)
def __str__(self):
rv= "rawSample: %i %f %f %f %f %d %d %d %d %d %d %d %d %f %f %d %f %f %f %f %f %f %d %d %d %d %d %d %d %d %f %f %d %f %f "%(self.__time__, self.__left_rawpupil__[0],self.__left_rawpupil__[1],self.__left_rawcr__[0], self.__left_rawcr__[1], self.__left_pupilarea__,self.__left_crarea__,self.__left_pupildimension__[0],self.__left_pupildimension__[1],self.__left_crdimension__[0],self.__left_crdimension__[1],self.__left_winpos__[0],self.__left_winpos__[1], self.__left_pupilcr__[0],self.__left_pupilcr__[1],self.__left_crarea2__,self.__left_rawcr2__[0],self.__left_rawcr2__[1],self.__right_rawpupil__[0],self.__right_rawpupil__[1],self.__right_rawcr__[0],self.__right_rawcr__[1],self.__right_pupilarea__,self.__right_crarea__,self.__right_pupildimension__[0],self.__right_pupildimension__[1],self.__right_crdimension__[0],self.__right_crdimension__[1],self.__right_winpos__[0],self.__right_winpos__[1],self.__right_pupilcr__[0],self.__right_pupilcr__[1],self.__right_crarea2__,self.__right_rawcr2__[0],self.__right_rawcr2__[1])
return rv
def getRawSampleTime(self):
return self.__time__
def getLeftrRawPupil(self):
return self.__left_rawpupil__
def getLeftRawCr(self):
return self.__left_rawcr__
def getLeftPupilArea(self):
return self.__left_pupilarea__
def getLeftCrArea(self):
return self.__left_crarea__
def getLeftPupilDimension(self):
return self.__left_pupildimension__
def getLeftCrDimension(self):
return self.__left_crdimension__
def getLeftWindowPosition(self):
return self.__left_winpos__
def getLeftPupilCr(self):
return self.__left_pupilcr__
def getLeftCrArea2(self):
return self.__left_crarea2__
def getLeftRawCr2(self):
return self.__left_rawcr2__
def getRightrRawPupil(self):
return self.__right_rawpupil__
def getRightRawCr(self):
return self.__right_rawcr__
def getRightPupilArea(self):
return self.__right_pupilarea__
def getRightCrArea(self):
return self.__right_crarea__
def getRightPupilDimension(self):
return self.__right_pupildimension__
def getRightCrDimension(self):
return self.__right_crdimension__
def getRightWindowPosition(self):
return self.__right_winpos__
def getRightPupilCr(self):
return self.__right_pupilcr__
def getRightCrArea2(self):
return self.__right_crarea2__
def getRightRawCr2(self):
return self.__right_rawcr2__
def newRawSample(time, left_raw_pupil_x, left_raw_pupil_y, left_raw_cr_x, left_raw_cr_y, left_pupil_area, left_cr_area, left_pupil_dimension_x,left_pupil_dimension_y, left_cr_dimension_x, left_cr_dimension_y, left_win_pos_x, left_win_pos_y, left_pupil_cr_x, left_pupil_cr_y, left_cr_area2, left_raw_cr2_x, left_raw_cr2_y, right_raw_pupil_x, right_raw_pupil_y, right_raw_cr_x, right_raw_cr_y, right_pupil_area, right_cr_area, right_pupil_dimension_x, right_pupil_dimension_y, right_cr_dimension_x, right_cr_dimension_y, right_win_pos_x, right_win_pos_y, right_pupil_cr_x, right_pupil_cr_y, right_cr_area2, right_raw_cr2_x, right_raw_cr2_y):
#print "pupil dimension in def", left_pupil_dimension_x,left_pupil_dimension_y
return rawSample(time, left_raw_pupil_x, left_raw_pupil_y, left_raw_cr_x, left_raw_cr_y, left_pupil_area, left_cr_area, left_pupil_dimension_x,left_pupil_dimension_y, left_cr_dimension_x, left_cr_dimension_y, left_win_pos_x, left_win_pos_y, left_pupil_cr_x, left_pupil_cr_y, left_cr_area2, left_raw_cr2_x, left_raw_cr2_y, right_raw_pupil_x, right_raw_pupil_y, right_raw_cr_x, right_raw_cr_y, right_pupil_area, right_cr_area, right_pupil_dimension_x, right_pupil_dimension_y, right_cr_dimension_x, right_cr_dimension_y, right_win_pos_x, right_win_pos_y, right_pupil_cr_x, right_pupil_cr_y, right_cr_area2, right_raw_cr2_x, right_raw_cr2_y)
## The %EyeLink tracker simplifies data analysis by detecting important changes in the sample data and
# placing corresponding events into the data stream. These include eye-data events (blinks, saccades,
# and fixations), button events, input-port events, and messages. Several classes have been created to
# holds eye event data (start/end of fixation, start/end of saccade, start/end of blink, fixation update)
# information. Start events contain only the start time, and optionally the start eye or gaze position.
# End events contain the start and end time, plus summary data on saccades and fixations.
#
# It is important to remember that data sent over the link does not arrive in strict time sequence.
# Typically, eye events (such as \c STARTSACC and \c ENDFIX) arrive up to 32 milliseconds after the corresponding
# samples, and messages and buttons may arrive before a sample with the same time code. This differs from
# the order seen in an ASC file, where the events and samples have been sorted into a consistent order by
# their timestamps.
#
# The \c LOST_DATA_EVENT is a new event, introduced for %EyeLink tracker version 2.1 and later, and produced
# within the DLL to mark the location of lost data. It is possible that data may be lost, either during
# recording with real-time data enabled, or during playback. This might happen because of a lost link packet
# or because data was not read fast enough (data is stored in a large queue that can hold 2 to 10 seconds
# of data, and once it is full the oldest data is discarded to make room for new data). This event has no
# data or time associated with it.
#
# Event data returned by the \c getFloatData() method the EyeLink class.
#
# For example,
#
# \code
# newEvent = EYELINK.getFloatData()
# \endcode
#
# Right now, the developer kit implements the following eye events:
#
# <table>
# <tr><td class="indexkey">Constant Name</td><td class="indexkey" align="center">Value</td><td class="indexkey">Description</td></tr>
# <tr><td>STARTBLINK</td><td align="center">3</td><td>Pupil disappeared (time only)</td></tr>
# <tr><td>ENDBLINK</td><td align="center">4</td><td>Pupil reappeared (duration data)</td></tr>
# <tr><td>STARTSACC</td><td align="center">5</td><td>Start of saccade (with time only)</td></tr>
# <tr><td>ENDSACC</td><td align="center">6</td><td>End of saccade (with summary data)</td></tr>
# <tr><td>STARTFIX</td><td align="center">7</td><td>Start of fixation (with time only)</td></tr>
# <tr><td>ENDFIX</td><td align="center">8</td><td>End of fixation (with summary data)</td></tr>
# <tr><td>FIXUPDATE</td><td align="center">9</td><td>Update within fixation (summary data for interval)</td></tr>
# <tr><td>MESSAGEEVENT</td><td align="center">24</td><td>User-definable text (IMESSAGE structure)</td></tr>
# <tr><td>BUTTONEVENT</td><td align="center">25</td><td>Button state change (IOEVENT structure)</td></tr>
# <tr><td>INPUTEVENT</td><td align="center">28</td><td>Change of input port (IOEVENT structure)</td></tr>
# <tr><td>SAMPLE_TYPE</td><td align="center">200</td><td>Event flags gap in data stream</td></tr>
# </table>
#
# Please note that due to the tracker configuration, some of the property information returned may be a missing value
# \c MISSING_DATA (or \c 0, depending on the field). So make sure you check for the validity of the data before
# trying to use them. To do the tracker configuration, the user can use the \c setLinkEventFilter() and
# \c setLinkEventData() methods of the EyeLink class to send commands at the start of the experiment or modify
# the DATA.INI file on the tracker PC.
#
# When both eyes are being tracked, left and right eye events are produced. The eye from which data
# was produced can be retrieved by the \c getEye() method.
[docs]class EyeEvent:
def __init__(self, time, type, eye, read, sttime):
self.__time__ = time
self.__type__ = type
self.__eye__ = eye
self.__read__ = read
self.__sttime__ = sttime
self.__status__ = 0
## Timestamp of the sample causing event (in milliseconds since %EyeLink tracker was activated).
# @return
# Long integer.
def getTime(self):
return self.__time__
## The event code.
# @return
# Integer.
def getType(self):
return self.__type__
## Which eye produced the event: \c 0 (\c LEFT_EYE) or \c 1 (\c RIGHT_EYE).
# @return
# Integer.
def getEye(self):
return self.__eye__
## Bits indicating which data fields contain valid data (see
# eye_data.h file for details of the bits information).
# @return
# Integer.
def getRead(self):
return self.__read__
## Timestamp of the first sample of the event.
# @return
# Long integer.
def getStartTime(self):
return self.__sttime__
## Errors and warnings of the event.
#
def getStatus(self):
return self.__status__
## Class to represent Start Blink event.
# There are no direct properties for this interface. All properties are inherited from EyeEvent.
class StartBlinkEvent(EyeEvent):
def __init__(self, time, type, eye, read, sttime):
EyeEvent.__init__(self, time, type, eye, read, sttime)
def newStartBlinkEvent(time, type, eye, read, sttime):
return StartBlinkEvent(time, type, eye, read, sttime)
## This interface is never used as is. FixUpdateEvent, StartFixationEvent and
# StartSaccadeEvent types inherit this.
# This also inherits all properties from EyeEvent.
class StartNonBlinkEvent(EyeEvent):
def __init__(self, time, type, eye, read, sttime, gstx, gsty, hstx, hsty, svel, supd_x, supd_y):
EyeEvent.__init__(self, time, type, eye, read, sttime)
self.__startGaze__ = (gstx, gsty)
self.__startHref__ = (hstx, hsty)
self.__startVelocity__ = svel
self.__startUpd__ = (supd_x, supd_y)
## Gaze position at the start of the event (in pixel coordinates
# set by the \c screen_pixel_coords command). The first and
# second items of the tuple store the x- and y- gaze position
# respectively.
# @return
# Two-item tuple in the format of (float, float).
def getStartGaze(self):
return self.__startGaze__
## HEADREF position at the start of the event. The first and
# second items of the tuple store the x- and y- HREF data
# respectively.
# @return
# Two-item tuple in the format of (float, float).
def getStartHREF(self):
return self.__startHref__
## Gaze velocity at the start of the event (in visual degrees per second).
# @return
# Float.
def getStartVelocity(self):
return self.__startVelocity__
## Angular resolution at the start of the event (in screen pixels
# per visual degree, PPD). The first item of the tuple stores
# the x-coordinate PPD resolution and the second item of the
# tuple stores the y-coordinate PPD resolution.
# @return
# Two-item tuple in the format of (float, float).
def getStartPPD(self):
return self.__startUpd__
## Class to represent Start Saccade event.
# There are no direct properties for this interface. All properties are inherited from StartNonBlinkEvent.
class StartSaccadeEvent(StartNonBlinkEvent):
def __init__(self, time, type, eye, read, sttime, gstx, gsty, hstx, hsty, svel, supd_x, supd_y):
StartNonBlinkEvent.__init__(self, time, type, eye, read, sttime, gstx, gsty, hstx, hsty, svel, supd_x, supd_y)
def newStartSaccadeEvent(time, type, eye, read, sttime, gstx, gsty, hstx, hsty, svel, supd_x, supd_y):
return StartSaccadeEvent(time, type, eye, read, sttime, gstx, gsty, hstx, hsty, svel, supd_x, supd_y)
## Class to represent Start Fixation event.
# This also inherits all properties from StartNonBlinkEvent.
class StartFixationEvent(StartNonBlinkEvent):
def __init__(self, time, type, eye, read, sttime, gstx, gsty, hstx, hsty, sta, svel, supd_x, supd_y):
StartNonBlinkEvent.__init__(self, time, type, eye, read, sttime, gstx, gsty, hstx, hsty, svel, supd_x, supd_y)
self.__sta__ = sta
## Pupil size (in arbitrary units, area or diameter as selected) at the start of a fixation interval.
# @return
# Float.
def getStartPupilSize(self):
return self.__sta__
def newStartFixationEvent(time, type, eye, read, sttime, gstx, gsty, hstx, hsty, sta, svel, supd_x, supd_y):
return StartFixationEvent(time, type, eye, read, sttime, gstx, gsty, hstx, hsty, sta, svel, supd_x, supd_y)
## Class to represent End Blink event. This also contains the Start Blink data.
# This also inherits all properties from EyeEvent.
class EndBlinkEvent(EyeEvent):
def __init__(self, time, type, eye, read, sttime, entime):
EyeEvent.__init__(self, time, type, eye, read, sttime)
self.__entime__ = entime
## Timestamp of the last sample of the blink.
# @return
# Long integer.
def getEndTime(self):
return self.__entime__
def newEndBlinkEvent(time, type, eye, read, sttime, entime):
return EndBlinkEvent(time, type, eye, read, sttime, entime)
## This interface is never used as is. EndFixationEvent, EndSaccadeEvent and FixUpdateEvent
# types inherit this.
#
# Please note that the \c getStartTime() and \c getEndTime() methods of the event class are the
# timestamps of the first and last samples in the event. To compute duration, subtract
# these two values and add 4 msec (even in 500 Hz tracking modes, the internal parser of
# %EyeLink II quantizes event times to 4 milliseconds).
#
# Peak velocity returned by \c getPeakVelocity() for fixations is usually corrupted by terminal
# segments of the preceding and following saccades.
#
# The \c getStartPPD() and \c getEndPPD() methods contain the angular resolution at the start and
# end of the saccade or fixation. The average of the start and end angular resolution can
# be used to compute the size of saccades in degrees. This Python code would compute the
# true magnitude of a saccade from an \c ENDSACC event in the following way:
#
# \code
# newEvent = EYELINK.getFloatData()
#
# if isinstance(newEvent, EndFixationEvent):
# Start_Gaze = newEvent.getStartGaze()
# Start_PPD = newEvent.getStartPPD()
# End_Gaze = newEvent.getEndGaze()
# End_PPD = newEvent. getEndPPD()
#
# dx = (End_Gaze[0] - Start_Gaze[0]) / ((End_PPD[0] + Start_PPD[0])/2.0);
# dy = (End_Gaze[1] - Start_Gaze[1]) / ((End_PPD[1] + Start_PPD[1])/2.0);
# dist = math.sqrt(dx*dx + dy*dy);
# \endcode
#
# Please note that the average velocity for saccades may be larger than the saccade magnitude
# divided by its duration because of overshoots and returns.
class EndNonBlinkEvent:
def __init__(self, entime, genx, geny, henx, heny, evel, avel, pvel, eupd_x, eupd_y):
self.__endTime__ = entime
self.__endGaze__ = (genx, geny)
self.__endHref__ = (henx, heny)
self.__endVelocity__ = evel
self.__avgVelocity__ = avel
self.__peakVelocity__ = pvel
self.__endUpd__ = (eupd_x, eupd_y)
## Timestamp of the last sample of the event.
# @return
# Long integer.
def getEndTime(self):
return self.__endTime__
## Gaze position at the end of the event (in pixel coordinates
# set by the \c screen_pixel_coords command). The first and
# second items of the returned tuple store the x- and y- gaze position
# respectively.
# @return
# Two-item tuple in the format of (float, float).
def getEndGaze(self):
return self.__endGaze__
## HEADREF position at the end of the event. The first and
# second items of the returned tuple store the x- and y- coordinate HREF data
# respectively.
# @return
# Two-item tuple in the format of (float, float).
def getEndHREF(self):
return self.__endHref__
## Gaze velocity at the end of the event (in visual degrees per second).
# @return
# Float.
def getEndVelocity(self):
return self.__endVelocity__
## Average gaze velocity during event (in visual degrees per second).
# @return
# Float.
def getAverageVelocity(self):
return self.__avgVelocity__
## Peak gaze velocity during event (in visual degrees per second).
# @return
# Float.
def getPeakVelocity(self):
return self.__peakVelocity__
## Angular resolution at the end of the event (in screen pixels per visual degree).
# @return
# Two-item tuple in the format of (float, float).
def getEndPPD(self):
return self.__endUpd__
def getAngle(xavgres,yavgres,x1,y1, x2, y2):
if x1 == constants.MISSING_DATA or y1 == constants.MISSING_DATA or x2 == constants.MISSING_DATA or y2 == constants.MISSING_DATA\
or(x1==x2 and y1==y2):
return constants.MISSING_DATA
x = (x1 -x2)/xavgres
y = (y1 -y2)/yavgres
rv= 57.296*math.atan2(y,0-x);
#make sure the angle is between -180 to +180 So the calculation are simpler to do
rv = math.fmod(rv,360)
if rv >180:
rv = rv-360
elif rv < -180:
rv = 360+rv
return rv
## Class to represent End Saccade event. This also contains the Start Saccade data.
# This also inherits all properties from StartSaccadeEvent and EndNonBlinkEvent.
class EndSaccadeEvent(StartSaccadeEvent, EndNonBlinkEvent):
def __init__(self, time, type, eye, read, sttime, gstx, gsty, hstx, hsty, svel, supd_x, supd_y, entime, genx, geny, henx, heny, evel, avel, pvel, eupd_x, eupd_y):
StartSaccadeEvent.__init__(self, time, type, eye, read, sttime, gstx, gsty, hstx, hsty, svel, supd_x, supd_y)
EndNonBlinkEvent.__init__(self, entime, genx, geny, henx, heny, evel, avel, pvel, eupd_x, eupd_y)
## Returns the amplitude between the start and end of the event.
# @return Amplitude between the start and end of the event.
def getAmplitude(self):
ppdx = (self.getStartPPD()[0]+self.getEndPPD()[0])/2
ppdy = (self.getStartPPD()[1]+self.getEndPPD()[1])/2
x = (self.getEndGaze()[0] -self.getStartGaze()[0])/ppdx
y = (self.getEndGaze()[1] -self.getStartGaze()[1])/ppdy
return (x,y)
## Returns the angle between the start and end of the event.
# @return Angle between the start and end of the event.
def getAngle(self):
ppdx = (self.getStartPPD()[0]+self.getEndPPD()[0])/2
ppdy = (self.getStartPPD()[1]+self.getEndPPD()[1])/2
# x = (self.getStartGaze()[0] -self.getEndGaze()[0])/ppdx
# y = (self.getStartGaze()[1] -self.getEndGaze()[1])/ppdy
# rv= 57.296*math.atan2(y,0-x);
return getAngle(ppdx,ppdy,self.getStartGaze()[0],self.getStartGaze()[1] ,self.getEndGaze()[0],self.getEndGaze()[1])
# return rv
def newEndSaccadeEvent(time, type, eye, read, sttime, gstx, gsty, hstx, hsty, svel, supd_x, supd_y, entime, genx, geny, henx, heny, evel, avel, pvel, eupd_x, eupd_y):
return EndSaccadeEvent(time, type, eye, read, sttime, gstx, gsty, hstx, hsty, svel, supd_x, supd_y, entime, genx, geny, henx, heny, evel, avel, pvel, eupd_x, eupd_y)
## Class to represent End Fixation event. This also contains the Start Fixation data.
# This also inherits all properties from StartFixationEvent and EndNonBlinkEvent.
class EndFixationEvent(StartFixationEvent, EndNonBlinkEvent):
def __init__(self, time, type, eye, read, sttime, gstx, gsty, hstx, hsty, sta, svel, supd_x, supd_y, entime, genx, geny, gavx, gavy, henx, heny, havx, havy, ena, ava, evel, avel, pvel, eupd_x, eupd_y):
StartFixationEvent.__init__(self, time, type, eye, read, sttime, gstx, gsty, hstx, hsty, sta, svel, supd_x, supd_y)
EndNonBlinkEvent.__init__(self, entime, genx, geny, henx, heny, evel, avel, pvel, eupd_x, eupd_y)
self.__avgGaze__=(gavx, gavy)
self.__avgHref__=(havx, havy)
self.__ena__=ena
self.__ava__=ava
## The average gaze position during the fixation period (in
# pixel coordinates set by the \c screen_pixel_coords command).
# @return
# Two-item tuple in the format of (float, float).
def getAverageGaze(self):
return self.__avgGaze__
## Average HEADREF position during the fixation period.
# @return
# Two-item tuple in the format of (float, float).
def getAverageHREF(self):
return self.__avgHref__
## Average pupil size (in arbitrary units, area or diameter as selected) during a fixation.
# @return
# Float.
def getAveragePupilSize(self):
return self.__ava__
## Pupil size (in arbitrary units, area or diameter as selected) at the end of a fixation interval.
# @return
# Float.
def getEndPupilSize(self):
return self.__ena__
def newEndFixationEvent(time, type, eye, read, sttime, gstx, gsty, hstx, hsty, sta, svel, supd_x, supd_y, entime, genx, geny, gavx, gavy, henx, heny, havx, havy, ena, ava, evel, avel, pvel, eupd_x, eupd_y):
return EndFixationEvent(time, type, eye, read, sttime, gstx, gsty, hstx, hsty, sta, svel, supd_x, supd_y, entime, genx, geny, gavx, gavy, henx, heny, havx, havy, ena, ava, evel, avel, pvel, eupd_x, eupd_y)
## Class to represent the Fix Update event.
# This also inherits all properties from StartNonBlinkEvent and EndNonBlinkEvent.
class FixUpdateEvent(StartNonBlinkEvent, EndNonBlinkEvent):
def __init__(self, time, type, eye, read, sttime, gstx, gsty, hstx, hsty, sta, svel, supd_x, supd_y, entime, genx, geny, gavx, gavy, henx, heny, havx, havy, ena, ava, evel, avel, pvel, eupd_x, eupd_y):
StartNonBlinkEvent.__init__(self, time, type, eye, read, sttime, gstx, gsty, hstx, hsty, svel, supd_x, supd_y)
EndNonBlinkEvent.__init__(self, entime, genx, geny, henx, heny, evel, avel, pvel, eupd_x, eupd_y)
self.__avgGaze__=(gavx, gavy)
self.__avgHref__=(havx, havy)
self.__ena__=ena
self.__ava__=ava
self.__sta__ = sta
## Pupil size (in arbitrary units, area or diameter as selected) at the start of a fixation interval.
# @return
# Float.
def getStartPupilSize(self):
return self.__sta__
## The average gaze position during the fixation period (in
# pixel coordinates set by the \c screen_pixel_coords command).
# @return
# Two-item tuple in the format of (float, float).
def getAverageGaze(self):
return self.__avgGaze__
## Average HEADREF position during the fixation period.
# @return
# Two-item tuple in the format of (float, float).
def getAverageHREF(self):
return self.__avgHref__
## Average pupil size (in arbitrary units, area or diameter as selected) during a fixation.
# @return
# Float.
def getAveragePupilSize(self):
return self.__ava__
## Pupil size (in arbitrary units, area or diameter as selected) at the end of a fixation interval.
# @return
# Float.
def getEndPupilSize(self):
return self.__ena__
def __str__(self):
rv= "FixationUpdate: %i %i %f %f"%(self.__time__,self.getEndTime()-self.getStartTime(),self.getAverageGaze()[0], self.getAverageGaze()[1])
return rv
def newFixUpdateEvent(time, type, eye, read, sttime, gstx, gsty, hstx, hsty, sta, svel, supd_x, supd_y, entime, genx, geny, gavx, gavy, henx, heny, havx, havy, ena, ava, evel, avel, pvel, eupd_x, eupd_y):
return FixUpdateEvent(time, type, eye, read, sttime, gstx, gsty, hstx, hsty, sta, svel, supd_x, supd_y, entime, genx, geny, gavx, gavy, henx, heny, havx, havy, ena, ava, evel, avel, pvel, eupd_x, eupd_y)
## A message event is created by your experiment program and placed in the EDF file. It
# is possible to enable the sending of these messages back through the link, although
# there is rarely a reason to do this. The MessageEvent class is designed to hold
# information on %EyeLink message events retrieved from the link. Please note that all
# methods for the MessageEvent class do not take a parameter.
#
# Returned by \c getFloatData() whenever there is a Message Event.
class MessageEvent:
def __init__(self, time, type, msg):
self.__time__ = time;
self.__type__ = type
self.__text__ = msg
## Timestamp of the sample causing event (when camera imaged eye,
# in milliseconds since %EyeLink tracker was activated).
# @return
# Long integer.
def getTime(self):
return self.__time__
## The event code. This should be \c MESSAGEEVENT (i.e., 24).
# @return
# Integer.
def getType(self):
return self.__type__
## Message contents (max length 255 characters).
# @return
# String.
def getText(self):
return self.__text__
def newMessageEvent(time, type, text):
return MessageEvent(time, type, text)
## IOEvent class is used to handle \c BUTTONEVENT and \c INPUTEVENT types, which report changes
# in button status or in the input port data. The \c getTime() method records the timestamp
# of the eye-data sample where the change occurred, although the event itself is usually
# sent before that sample. Button events from the link are rarely used; monitoring buttons
# with one of \c readKeybutton(), \c lastButtonPress(), or \c buttonStates() of the EyeLink class
# methods is preferable, since these can report button states at any time, not just during
# recording.
#
# Returned by \c getFloatData() whenever there is an IOEvent.
class IOEvent:
def __init__(self, iotype, time, data):
self.__time__ = time
self.__iotype__ = iotype
self.__data__ = data
## Timestamp of the sample causing event (in milliseconds since %EyeLink tracker was activated).
# @return
# Long integer.
def getTime(self):
return self.__time__
## The event code. This should be \c BUTTONEVENT (i.e., 25) or \c INPUTEVENT (i.e., 28).
# @return
# Integer.
def getType(self):
return self.__iotype__
## Coded event data.
# @return
# Long integer.
def getData(self):
return self.__data__
def __str__(self):
return str(self.__time__)+"\t IOEvent type="+str(self.__iotype__)+"\tdata="+str(self.__data__)
## A ButtonEvent class, derived from the IOEvent class, is created to handle specifics of
# button events. For this class, in addition to the generic IOEvent class methods,
# two additional methods can be used for instances of the ButtonEvent class.
#
# Returned by \c getFloatData() whenever there is a Button Event.
class ButtonEvent(IOEvent):
def __init__(self,time,data):
IOEvent.__init__(self,25,time,data)
self.__activeButtons__=list()
self.__states__=list()
b = data>>8
bs = 255 & data
abuttons = [1,2,3,4,5,6,7,8]
for i in abuttons:
if (b&1) != 0:
self.__activeButtons__.append(i)
self.__states__.append(bs&1)
b = b>>1
bs = bs >>1
## A list of buttons pressed or released.
# @return
# List of integers.
def getButtons(self):
if len(self.__activeButtons__) == 0:
return [-1]
return self.__activeButtons__
## The button state (\c 1 for pressed or \c 0 for released).
# @return
# List of integers.
def getStates(self):
if len(self.__states__) == 0:
return [-1]
return self.__states__
def __str__(self):
return str(self.__time__)+"\t Button Event"+str(self.getType())+"\t"+str(self.getButtons())+str(self.__states__)
def newIOEvent(btype,time, data):
# print("New IOEvent ",str(BUTTONEVENT), str(btype))
if(int(btype) == constants.BUTTONEVENT):
return ButtonEvent(time, data)
return IOEvent(btype, time,data)
## The DisplayInfo class is used to contain information on display configurations, including
# width, height, color bits, and refresh rate. The current display configuration can be
# retrieved by the \c getDisplayInformation() function of the pylink module.
#
# For example:
#
# \code
# from pylink import *
#
# // Code to open the display
# currentDisplay = getDisplayInformation();
# print("Current display settings: ", currentDisplay.width, currentDisplay.height, \
# print "Current display settings: ", currentDisplay.width, currentDisplay.height, \
# currentDisplay.bits, currentDisplay.refresh
# \endcode
#
# The DisplayInfo has the following four attributes.
#
# <table>
# <tr><td>width</td><td>Integer</td><td>Display width in screen pixels.</td></tr>
# <tr><td>height</td><td>Integer</td><td>Display height in screen pixels.</td></tr>
# <tr><td>bits</td><td>Integer</td><td>Color resolution, in bits per pixel, of the display device (for example: 4 bits for 16 colors, 8 bits for 256 colors, or 16 bits for 65,536 colors).</td></tr>
# <tr><td>refresh</td><td>Float</td><td>Refresh rate.</td></tr>
# </table>
#
class DisplayInfo:
def __init__(self, width, height, bits, refresh):
self.width = width
self.height = height
self.bits = bits
self.refresh = refresh
## @var width
# Display width in screen pixels.
## @var height
# Display height in screen pixels.
## @var bits
# Color resolution, in bits per pixel, of the display device (for example: 4 bits for 16 colors, 8 bits for 256 colors, or 16 bits for 65,536 colors).
## @var refresh
# Refresh rate.
def newDisplayInfo(width, height, bits, refresh):
return DisplayInfo(width,height,bits, refresh)
## Class to represent tracker status information such as time stamps, flags, tracker addresses and so on.
#
# A valid reference to this object can be obtained by calling the function \c EYELINK.getTrackerInfo().
[docs]class ILinkData:
def __init__(self):
self._time = None
self._samrate= None
self._samdiv= None
self._prescaler= None
self._vprescaler= None
self._pprescaler= None
self._hprescaler= None
self._sample_data= None
self._event_data= None
self._event_types= None
self._in_sample_block= None
self._in_event_block= None
self._have_left_eye= None
self._have_right_eye= None
self._last_data_gap_types= None
self._last_data_buffer_type= None
self._last_data_buffer_size= None
self._control_read= None
self._first_in_block= None
self._last_data_item_time= None
self._last_data_item_type= None
self._last_data_item_contents= None
self._block_number= None
self._block_sample= None
self._block_event= None
self._last_resx= None
self._last_resy= None
self._last_pupil= None
self._last_status= None
self._queued_samples= None
self._queued_events= None
self._queue_size= None
self._queue_free= None
self._last_rcve_time= None
self._samples_on= None
self._events_on= None
self._packet_flags= None
self._link_flags= None
self._state_flags= None
self._link_dstatus= None
self._link_pendcmd= None
self._polling_remotes= None
self._poll_responses= None
self._reserved= None
self._our_name= None
self._eye_name= None
self._nodes= None
self._last_data_item= None
self._our_address= None
self._eye_address= None
self._ebroadcast_address= None
self._rbroadcast_address= None
## Time of last control event.
# Equivalent field in ILINKDATA "C": Time.
def getTime(self):
return self._time
## 10*sample rate (\c 0 if no samples, \c 1 if nonconstant).
# Equivalent field in ILINKDATA "C": samrate.
def getSampleRate(self):
return self._samrate
## Sample "divisor" (min msec between samples).
# Equivalent field in ILINKDATA "C": samdiv.
def getSampleDivisor(self):
return self._samdiv
## Amount to divide gaze x,y,res by.
# Equivalent field in ILINKDATA "C": prescaler.
def getPrescaler(self):
return self._prescaler
## Amount to divide velocity by.
# Equivalent field in ILINKDATA "C": vprescaler.
def getVelocityPrescaler(self):
return self._vprescaler
## Pupil prescale (\c 1 if area, greater if diameter).
# Equivalent field in ILINKDATA "C": pprescaler.
def getPupilPrescaler(self):
return self._pprescaler
## Head-distance prescale (to mm).
# Equivalent field in ILINKDATA "C": hprescaler.
def getHeadDistencePrescaler(self):
return self._hprescaler
## \c 0 if off, else all flags.
# Equivalent field in ILINKDATA "C": sample_data.
def getSampleDataFlags(self):
return self._sample_data
## \c 0 if off, else all flags.
# Equivalent field in ILINKDATA "C": event_data.
def getEventDataFlags(self):
return self._event_data
## \c 0 if off, else event-type flags.
# Equivalent field in ILINKDATA "C": event_types.
def getEventTypeFlags(self):
return self._event_types
## Set if in block with samples.
# Equivalent field in ILINKDATA "C": in_sample_block.
def isInBlockWithSamples(self):
return self._in_sample_block
## Set if in block with events.
# Equivalent field in ILINKDATA "C": in_event_block.
def isInBlockWithEvents(self):
return self._in_event_block
## Set if any left-eye data expected.
# Equivalent field in ILINKDATA "C": have_left_eye.
def haveLeftEye(self):
return self._have_left_eye
## Set if any right-eye data expected.
# Equivalent field in ILINKDATA "C": have_right_eye.
def haveRightEye(self):
return self._have_right_eye
## Flags what we lost before last item.
# Equivalent field in ILINKDATA "C": last_data_gap_types.
def getLostDataTypes(self):
return self._last_data_gap_types
## Buffer-type code.
# Equivalent field in ILINKDATA "C": last_data_buffer_type.
def getLastBufferType(self):
return self._last_data_buffer_type
## Buffer size of last item.
# Equivalent field in ILINKDATA "C": last_data_buffer_size.
def getLastBufferSize(self):
return self._last_data_buffer_size
## Set if control event read with last data.
# Equivalent field in ILINKDATA "C": control_read.
def isControlEvent(self):
return self._control_read
## Set if control event started new block.
# Equivalent field in ILINKDATA "C": first_in_block.
def isNewBlock(self):
return self._first_in_block
## Time field of item.
# Equivalent field in ILINKDATA "C": last_data_item_time.
def getLastItemTimeStamp(self):
return self._last_data_item_time
## Type: \c 100 = sample, \c 0 = none, else event type.
# Equivalent field in ILINKDATA "C": last_data_item_type.
def getLastItemType(self):
return self._last_data_item_type
## Content: <read> (\c IEVENT), <flags> (\c ISAMPLE).
# Equivalent field in ILINKDATA "C": last_data_item_contents.
def getLastItemContent(self):
return self._last_data_item_contents
## Block in file.
# Equivalent field in ILINKDATA "C": block_number.
def getBlockNumber(self):
return self._block_number
## Samples read in block so far.
# Equivalent field in ILINKDATA "C": block_sample.
def getSamplesInBlock(self):
return self._block_sample
## Events (excl. control read in block).
# Equivalent field in ILINKDATA "C": block_event.
def getEventsInBlock(self):
return self._block_event
## Updated by samples only.
# Equivalent field in ILINKDATA "C": last_resx.
def getLastResX(self):
return self._last_resx
## Updated by samples only.
# Equivalent field in ILINKDATA "C": last_resy.
def getLastResY(self):
return self._last_resy
## Updated by samples only.
# Equivalent field in ILINKDATA "C": last_pupil.
def getLastPupil(self):
return self._last_pupil
## Updated by samples, events.
# Equivalent field in ILINKDATA "C": last_status.
def getLastItemStatus(self):
return self._last_status
## Number of items in queue.
# Equivalent field in ILINKDATA "C": queued_samples.
def getSampleQueueLength(self):
return self._queued_samples
## Includes control events.
# Equivalent field in ILINKDATA "C": queued_events.
def getEventQueueLength(self):
return self._queued_events
## Total queue buffer size.
# Equivalent field in ILINKDATA "C": queue_size.
def getQueueSize(self):
return self._queue_size
## Unused bytes in queue.
# Equivalent field in ILINKDATA "C": queue_free.
def getFreeQueueLength(self):
return self._queue_free
## Time tracker last sent packet.
# Equivalent field in ILINKDATA "C": last_rcve_time.
def getLastReceiveTime(self):
return self._last_rcve_time
## Data type rcve enable (switch).
# Equivalent field in ILINKDATA "C": samples_on.
def isSamplesEnabled(self):
return self._samples_on
## Data type rcve enable (switch).
# Equivalent field in ILINKDATA "C": events_on.
def isEventsEnabled(self):
return self._events_on
## Status flags from data packet.
# Equivalent field in ILINKDATA "C": packet_flags.
def getPacketFlags(self):
return self._packet_flags
## Status flags from link packet header.
# Equivalent field in ILINKDATA "C": link_flags.
def getLinkFlags(self):
return self._link_flags
## Tracker error state flags.
# Equivalent field in ILINKDATA "C": state_flags.
def getStateFlags(self):
return self._state_flags
## Tracker data output state.
# Equivalent field in ILINKDATA "C": link_dstatus.
def getTrackerDataOutputState(self):
return self._link_dstatus
## Tracker commands pending.
# Equivalent field in ILINKDATA "C": link_pendcmd.
def getPendingCommands(self):
return self._link_pendcmd
## \c 1 if polling remotes, else polling trackers.
# Equivalent field in ILINKDATA "C": polling_remotes.
def isPoolingRemote(self):
return self._polling_remotes
## Total nodes responding to polling.
# Equivalent field in ILINKDATA "C": poll_responses.
def getPoolResponse(self):
return self._poll_responses
## \c 0 for %EyeLink I or original %EyeLink API DLL.
# Equivalent field in ILINKDATA "C": reserved.
def getReserved(self):
return self._reserved
## A name for our machine.
# Equivalent field in ILINKDATA "C": our_name.
def getName(self):
return self._our_name
## Name of tracker connected to.
# Equivalent field in ILINKDATA "C": eye_name.
def getTrackerName(self):
return self._eye_name
## Data on nodes.
# Equivalent field in ILINKDATA "C": nodes.
def getNodes(self):
return self._nodes
## Buffer containing last item.
# Equivalent field in ILINKDATA "C": last_data_item.
def getLastItem(self):
return self._last_data_item
## Address of our machine.
# Equivalent field in ILINKDATA "C": our_address.
def getAddress(self):
return self._our_address
## Address of the connected tracker.
# Equivalent field in ILINKDATA "C": eye_address.
def getTrackerAddress(self):
return self._eye_address
## Broadcast address for eye trackers.
# Equivalent field in ILINKDATA "C": ebroadcast_address.
def getTrackerBroadcastAddress(self):
return self._ebroadcast_address
## Broadcast address for remotes
# Equivalent field in ILINKDATA "C": rbroadcast_address.
def getRemoteBroadcastAddress(self):
return self._rbroadcast_address
def _updatePerTrialDynamic(dt,x):
(dt._samrate,dt._samdiv,dt._prescaler,dt._vprescaler,dt._pprescaler,dt._hprescaler,dt._sample_data,dt._event_data,dt._event_types )=x
def _updateBooleanFlags(dt, x):
(dt._in_sample_block,dt._in_event_block,dt._have_left_eye,dt._have_right_eye,dt._samples_on,dt._events_on,dt._control_read,dt._first_in_block)=x
def _updateTimeStamps(dt, x):
(dt._time,dt._last_data_item_time,dt._last_rcve_time)=x
def _updateStatusFlags(dt, x):
(dt._last_status,dt._packet_flags,dt._link_flags,dt._state_flags,dt._link_dstatus,dt._link_pendcmd)=x
def _updateTypeFlags(dt,x):
(dt._last_data_gap_types,dt._last_data_buffer_type,dt._last_data_item_type)=x
def _updateSampleEventData(dt, x, pl, ldi):
(dt._last_data_buffer_size,dt._last_data_item_contents,dt._block_number,dt._block_sample,dt._block_event,dt._last_resx,dt._last_resy,dt._queued_samples,dt._queued_events,dt._queue_size,dt._queue_free) =x
dt._last_pupil= pl
dt._last_data_item=ldi
def _updateNetworkData(dt, x, nodes):
(dt._polling_remotes, dt._poll_responses)= x
dt._nodes= nodes
def _updateAddresses(dt, nm, ad):
(dt._our_name,dt._eye_name)= nm
(dt._eye_address, dt._our_address,dt._ebroadcast_address,dt._rbroadcast_address)=ad
def _updateReserved(dt, r):
dt._reserved=r
def drawCalTarget(x,y):
if(constants.EYELINK != None):
constants.EYELINK.drawCalTarget((x,y))
## This represents a key input. This is used with EyeLinkCustomDisplay to notify the eyelink_core.dll
# that a key input is available.
class KeyInput:
def __init__(self,key,mod=0,state=0):
self.__key__ = key
self.__state__ =state
self.__type__= 0x1
self.__mod__ = mod
## EyeLinkCustomDisplay is an abstract class, that one would implement to present calibration/validation/drift correction targets and
# camera images. In addition, EyeLinkCustomDisplay can also play target beeps and display error messages.
#
# To use custom display do the following.
# - 1. Implement EyeLinkCustomDisplay
# - 2. Create an instance of the custom diplay object
# - 3. Use pylink.openGraphicsEx to let pylink know to use the custom display object
#
# \b Example:
# @code
# genv = EyeLinkCoreGraphicsPyGame(800,600,eyelinktracker)
# openGraphicsEx(genv)
# @endcode
# \link customdisplayexample Code to implement EyeLinkCustomDisplay using pygame.
[docs]class EyeLinkCustomDisplay:
## Constructor takes no parameters
#
def __init__(self):
from pylink.pylink_c import request_cross_hair_draw
self.request_cross_hair_draw = request_cross_hair_draw
pass
## Internal function to update the mage size.
# The size set by this function is used to draw cross hair when \c draw_cross_hair() is called
# This function should \b not be overrided and should \b not be called other than the
# display mechanism.
#
# @param width Width of the image.
# @param height Height of the image.
def __updateimgsize__(self,width,height):
self.__imgwidth__ = width
self.__imgheight__ = height
## This function is called to setup calibration/validation display.
# This will be called just before we enter into the calibration or validataion or drift correction mode.
# Any allocation per calibration or validation drift corretion can be done here.
# Also, it is normal to clear the display in this call.
def setup_cal_display(self):
print("%s.setup_cal_display not implemented"%(self.__class__))
print(self.setup_cal_display.__doc__)
## This function is called just before exiting calibration/validation display.
# Any resource allocation done in \c setup_cal_display() can be cleared.
def exit_cal_display(self):
print("%s.exit_cal_display not implemented"%(self.__class__))
print(self.exit_cal_display.__doc__)
## This function is called if abort of record. It is used to hide display from subject.
def record_abort_hide(self):
print("%s.record_abort_hide not implemented"%(self.__class__))
print(self.record_abort_hide.__doc__)
## This function is used to setup image display.
# It takes expected image size of the source image. This may be called repeatedly for same display. If this fails,
# It should return \c 1 if success and \c 0 otherwise.
#
# @param width Width of the incoming image.
# @param height Height of the incoming image.
# @return \c 1 if success, \c 0 otherwise.
def setup_image_display(self, width, height):
print("%s.setup_image_display not implemented"%(self.__class__))
print(self.setup_image_display.__doc__)
## This function displays the camera image title.
# This is called whenever the title changes
#
# @param title title change
def image_title(self, title):
print("%s.setup_cal_display not implemented"%(self.__class__))
print(self.image_title.__doc__)
## This function is used to display an image line. This function is called with an
# array of bytes containing picture colors. The byte on pixels are just palette
# indexes. This index should be used against the palette created on the call to
# \c set_image_palette_hook(). The image is given line by line from top to bottom.
# It may be efficient to collect one full image and do a full blit of the entire
# image.
#
# @code
# i =0
# imline = self.imagebuffer[line-1]
# while i <width:
# imline[i] = self.pal[buff[i]]
# i= i+1
# @endcode
def draw_image_line(self, width, line, totlines,buff):
print("%s.draw_image_line not implemented"%(self.__class__))
print(self.draw_image_line.__doc__)
## This function is called to setup the image palettes. The function is called
# with a set of RGB colors to set up for next image.
# @code
# self.pal = []
# while i < sz:
# rf = int(b[i])
# gf = int(g[i])
# bf = int(r[i])
# self.pal.append((rf<<16) | (gf<<8) | (bf))
# i = i+1
# @endcode
def set_image_palette(self, red, green, blue):
print("%s.set_image_palette not implemented"%(self.__class__))
print(self.set_image_palette.__doc__)
## Called to end the image display.
def exit_image_display(self):
print("%s.exit_image_display not implemented"%(self.__class__))
print(self.exit_image_display.__doc__)
## Called to clear the calibration display.
def clear_cal_display(self):
print("%s.clear_cal_display not implemented"%(self.__class__))
print(self.clear_cal_display.__doc__)
## Called to erase the calibration or validation target.
# Erase the target drawn by the previous call to \c draw_cal_target().
def erase_cal_target(self):
print("%s.erase_cal_target not implemented"%(self.__class__))
print(self.erase_cal_target.__doc__)
## Called to draw the calibration or validation target.
# Draw a target at x,y and display it.
#
# @remark x and y values are relative to the active \c screen_pixel_coords command.
# @param x X location to draw the target.
# @param y Y location to draw the target.
def draw_cal_target(self, x, y):
print("%s.draw_cal_target not implemented"%(self.__class__))
print(self.draw_cal_target.__doc__)
## Called to play target beeps.
#
# @param beepid Id of the beep to be played.
# Possible values for beepid are:
# \arg CAL_ERR_BEEP =-1
# \arg DC_ERR_BEEP =-2
# \arg CAL_GOOD_BEEP = 0
# \arg CAL_TARG_BEEP = 1
# \arg DC_GOOD_BEEP = 2
# \arg DC_TARG_BEEP = 3
def play_beep(self, beepid):
print("%s.play_beep not implemented"%(self.__class__))
print(self.play_beep.__doc__)
## This function should return list of KeyInput.
# If there are not keys, return an empty list or None.
def get_input_key(self):
print("%s.get_input_key not implemented"%(self.__class__))
print(self.get_input_key.__doc__)
## Called to notify any error message to display or print.
def alert_printf(self,msg):
print("%s.alert_printf not implemented"%(self.__class__))
print(self.alert_printf.__doc__)
## Called to draw the cross hair, in response to the call to \c draw_cross_hair().
# This function should draw a line from (x1,y1) to (x2,y2). The x and y values
# are relative to the width and height of the image, given at \c setup_image_display().
#
# @param x1 Starting x position.
# @param y1 Starting y position.
# @param x2 Ending x position.
# @param y2 Ending y position.
# @param colorindex Color id of the line.
# Possible value for colorindex are:
# \arg CR_HAIR_COLOR=1
# \arg PUPIL_HAIR_COLOR=2
# \arg PUPIL_BOX_COLOR=3
# \arg SEARCH_LIMIT_BOX_COLOR=4
# \arg MOUSE_CURSOR_COLOR=5
def draw_line(self,x1,y1,x2,y2,colorindex):
print("%s.draw_line not implemented"%(self.__class__))
print(self.draw_line.__doc__)
## Called to draw the cross hair, in response to the call to \c draw_cross_hair().
# This function should draw an lozenge bounded by the box (x,y),(width,height). The x and y values
# are relative to the width and height of the image, given at \c setup_image_display().
#
# @remark This function is not used at the moment.
# @param x Starting x position.
# @param y Starting y position.
# @param width bounding width
# @param height bounding height
# @param colorindex Color id of the ellipse.
# Possible value for colorindex are:
# \arg CR_HAIR_COLOR=1
# \arg PUPIL_HAIR_COLOR=2
# \arg PUPIL_BOX_COLOR=3
# \arg SEARCH_LIMIT_BOX_COLOR=4
# \arg MOUSE_CURSOR_COLOR=5
def draw_lozenge(self,x,y,width,height,colorindex):
print("%s.draw_lozenge not implemented"%(self.__class__))
print(self.draw_lozenge.__doc__)
## Called to get the mouse location.
# This function should return the mouse location and the state at the time of call. ((x,y),state).
# At the moment we only care if the mouse is clicked or not. So, if clicked the state = 1, 0 otherwise.
# This function is only useful for EyeLink1000.
def get_mouse_state(self):
print("%s.get_mouse_state not implemented"%(self.__class__))
print(self.get_mouse_state.__doc__)
## User call this function to request draw cross hair.
# After completion of filling in the camera image, call this function to draw the
# cross hair on the camera image.
def draw_cross_hair(self):
self.request_cross_hair_draw(self)