Source code for mdl.eyetracking.win.pylink.tracker

#!/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: &lt;read&gt; (\c IEVENT), &lt;flags&gt; (\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)