Exemple #1
0
 def getEnabledExtensions(self):
     return [
         OrderedDict(name=e.Name,
                     extensionId=e.ExtensionId,
                     protocolVersion=e.ProtocolVersion,
                     realm=e.Realm)
         for e in self._eyetracker.GetEnabledExtensions()
     ]
Exemple #2
0
 def __init__(self, port_num, baud=115200, timeout=0):
     self._port_num = port_num - 1
     self._baud = baud
     self._timeout = timeout
     self._active_requests = OrderedDict()
     self._serial_port = None
     self._rx_events = []
     self._request_replies = []
     self.connectSerial()
Exemple #3
0
 def getTrackerDetails(self):
     tobiiInfoProperties=['product_id','given_name','model','generation','firmware_version','status']
     tprops=OrderedDict()
     eyetracker_info=self._eyetracker_info
     for tp in tobiiInfoProperties:
         ta=getattr(eyetracker_info,tp)
         if callable(ta):
             ta=ta()
         tprops[tp]=ta
     tprops['factory_info_string']=str(eyetracker_info.factory_info)
     return tprops
Exemple #4
0
 def __init__(self, port_num, baud=115200, timeout=0):
     self._port_num = port_num
     self._baud = baud
     self._timeout = timeout
     self._active_requests = OrderedDict()
     self._serial_port = None
     self._rx_events = []
     self._request_replies = []
     self._analog_input_thresholds = [
         0,
     ] * 8
     self.connectSerial()
     self.resetState()
Exemple #5
0
def showSessionInfoDialog():
    '''
    Display a dialog to collect session or participant level information
    at the start of an experiment.

    If the dialog OK button is pressed, a dictionary with the values entered
    for each dialog input is returned. If thew dialogs Cancel button is pressed,
    None is returned.
    :return: dict of session info, or None if dialog was cancelled
    '''
    info = OrderedDict()
    info['Session Code'] = DEFAULT_SESSION_CODE
    info['Conditions File'] = getAvailableConditionsFileNames()
#    info['ExpName'] =EXP_NAME
#    info['ExpVersion'] = EXP_VERSION
    infoDlg = gui.DlgFromDict(dictionary=info,
                              title='{} (v{})'.format(EXP_NAME, EXP_VERSION),
                              order = info.keys(),
                              )
                              # fixed=['ExpName','ExpVersion'])
    if infoDlg.OK:
        return info
    return None
Exemple #6
0
    def stop(cls, empty_queue=True):
        if cls._active:
            cls._active = False
            TobiiTrackerBrowser._event_queue.put(None)
            TobiiTrackerBrowser._browser.stop()

            if cls._mainloop_referenced is False:
                TobiiTrackerBrowser._mainloop.stop()
                TobiiTrackerBrowser._mainloop = None

            if empty_queue is True:
                e = 1
                while e != None:
                    try:
                        e = TobiiTrackerBrowser.getNextEvent()
                    except Queue.Empty:
                        pass

            TobiiTrackerBrowser._browser = None
            TobiiTrackerBrowser._detectedTrackers = OrderedDict()
Exemple #7
0
    def run(self, *args, **kwargs):
        """
        Tests the round trip delay from when the experiment runtime requests
        new events from the ioHub server to when a response with >=1 new
        event is received and ready for use within the experiment script.
        
        Only getEvent requests that return with atleast one new event are used in
        the calculated statistics to try and ensure the reported delay is measuring
        the higher processing load case of new events being returned, vs. the 
        case of no new events being available. 

        At the end of the test, a MatPlotLib figure is displayed showing a
        histogram of the round trip event request delays as well as two figures
        representing the retrace onset detection stability of PsychoPy when the 
        ioHub is being used.
         
        PsychoPy code is taken from an example psychopy script in the coder 
        documentation.
        """
        self.psychoStim = OrderedDict()
        self.totalEventRequestsForTest = 1000
        self.numEventRequests = 0
        self.psychoWindow = None
        self.lastFlipTime = 0.0
        self.events = None

        # create 'shortcuts' to the devices of interest for this experiment
        self.mouse = self.hub.devices.mouse
        self.kb = self.hub.devices.kb
        self.expRuntime = self.hub.devices.experimentRuntime
        self.display = self.hub.devices.display

        # create fullscreen pyglet window at current resolution, as well as required resources / drawings
        self.createPsychoGraphicsWindow()

        # create stats numpy arrays, set experiment process to high priority.
        self.initStats()

        # enable high priority mode for the experiment process
        #Computer.enableHighPriority()

        #draw and flip to the updated graphics state.
        ifi = self.drawAndFlipPsychoWindow()

        # START TEST LOOP >>>>>>>>>>>>>>>>>>>>>>>>>>

        while self.numEventRequests < self.totalEventRequestsForTest:
            # send an Experiment Event to the ioHub server process
            self.hub.sendMessageEvent("This is a test message %.3f" %
                                      self.flipTime)

            # check for any new events from any of the devices, and return the events list and the time it took to
            # request the events and receive the reply
            self.events, callDuration = self.checkForEvents()
            if self.events:
                # events were available
                self.updateStats(self.events, callDuration, ifi)
                #draw and flip to the updated graphics state.

            ifi = self.drawAndFlipPsychoWindow()

        # END TEST LOOP <<<<<<<<<<<<<<<<<<<<<<<<<<

        # close necessary files / objects, disable high priority.
        self.spinDownTest()

        # plot collected delay and retrace detection results.
        self.plotResults()
Exemple #8
0
    def run(self, *args):
        """
        The run method contains your experiment logic. 
        It is equal to what would be in your main psychopy experiment
        script .py file in a standard psychopy experiment setup. 
        """
        display = self.devices.display
        kb = self.devices.kb
        ain = self.devices.ain

        display_resolution = display.getPixelResolution()
        psychopy_monitor = display.getPsychopyMonitorName()
        unit_type = display.getCoordinateType()
        screen_index = display.getIndex()

        # Create a psychopy window, full screen resolution, full screen mode.
        window = visual.Window(display_resolution,
                               monitor=psychopy_monitor,
                               units=unit_type,
                               color=[128, 128, 128],
                               colorSpace='rgb255',
                               fullscr=True,
                               allowGUI=False,
                               screen=screen_index)

        # Get the number of trials selected in the session dialog.
        #
        user_params = self.getUserDefinedParameters()
        print('user_params: ', user_params)
        trial_count = int(user_params.get('trial_count', 5))

        # Create an ordered dictionary of psychopy stimuli.
        #   An ordered dictionary is one that returns keys in the order
        #   they are added, so you can use it to reference stim by a name
        #   or by 'zorder'.
        #
        psychoStim = OrderedDict()
        psychoStim['grating'] = visual.PatchStim(window,
                                                 mask="circle",
                                                 size=150,
                                                 pos=[0, 0],
                                                 sf=.075)

        psychoStim['title'] = visual.TextStim(
            window,
            text="Analog Input Test. Trial 1 of %d" % (trial_count),
            pos=[0, 200],
            height=36,
            color=[1, .5, 0],
            colorSpace='rgb',
            alignHoriz='center',
            alignVert='center',
            wrapWidth=800.0)

        ai_values_string_proto = "AI_0: %.3f\tAI_1: %.3f\tAI_2: %.3f\tAI_3: %.3f\t\nAI_4: %.3f\tAI_5: %.3f\tAI_6: %.3f\tAI_7: %.3f"
        ai_values = (0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0)
        psychoStim['analog_input_values'] = visual.TextStim(
            window,
            text=ai_values_string_proto % ai_values,
            pos=[0, -200],
            height=24,
            color=[1, 1, 0],
            colorSpace='rgb',
            alignHoriz='center',
            alignVert='center',
            wrapWidth=800.0)

        psychoStim['instruction'] = visual.TextStim(
            window,
            text="Press SPACE Key for Next Trial",
            pos=[0, -300],
            height=36,
            color=[1, 1, 0.5],
            colorSpace='rgb',
            alignHoriz='center',
            alignVert='center',
            wrapWidth=800.0)

        # Clear all events from the global and device level event buffers.
        self.hub.clearEvents('all')

        # Run a number of analog input recording /trials/
        #
        for i in range(trial_count):
            # Clear all events from the global and device level event buffers.
            psychoStim['title'].setText("Analog Input Test. Trial %d of %d" %
                                        (i + 1, trial_count))
            self.hub.clearEvents('all')

            # Start streaming AnalogInput data.
            #
            ain.enableEventReporting(True)

            # Loop until we get a keyboard event where the
            #   SPACE key was pressed.
            #
            while 1:

                # For each retrace, update the grating phase
                #
                psychoStim['grating'].setPhase(0.05, '+')

                # Update analog input values to display
                #
                analog_input_events = ain.getEvents()
                if analog_input_events:
                    event_count = len(analog_input_events)
                    event = analog_input_events[-1]
                    ai_values = (event.AI_0, event.AI_1, event.AI_2,
                                 event.AI_3, event.AI_4, event.AI_5,
                                 event.AI_6, event.AI_7)
                    psychoStim['analog_input_values'].setText(
                        ai_values_string_proto % ai_values)

                # redraw the stim
                [psychoStim[stimName].draw() for stimName in psychoStim]

                # Flip, storing the time of the start of display update retrace.
                #
                flip_time = window.flip()

                # Send a message to the ioHub Process with indicating
                #   that a flip occurred and at what time.
                #
                self.hub.sendMessageEvent("Flip", sec_time=flip_time)

                # Get any new key values from keyboard *Char* Events.
                #
                key_values = [
                    k.key
                    for k in kb.getEvents(EventConstants.KEYBOARD_RELEASE)
                ]

                if u' ' in key_values:
                    break

            # Clear the screen
            #
            window.flip()

            # Stop analog input recording
            #
            ain.enableEventReporting(False)

            # Delay 1/4 second before next trial
            #
            actualDelay = self.hub.wait(0.250)

        # All trials have been run. End demo....
        #   Wait 250 msec before ending the experiment
        actualDelay = self.hub.wait(0.250)

        print("Delay requested %.6f, actual delay %.6f, Diff: %.6f" %
              (0.250, actualDelay, actualDelay - 0.250))
Exemple #9
0
class TobiiTracker(object):
    LEFT = 0
    RIGHT = 1
    eye_data = OrderedDict(tracker_time_usec=np.NaN,
                           pupil_diameter_mm=np.NaN,
                           gaze_norm=[np.NaN, np.NaN],
                           gaze_mm=[np.NaN, np.NaN, np.NaN],
                           eye_location_norm=[np.NaN, np.NaN, np.NaN],
                           eye_location_mm=[np.NaN, np.NaN, np.NaN],
                           validity_code=np.NaN,
                           status='UNKNOWN',
                           trigger_signal=None)

    def __init__(self,
                 eyetracker_info=None,
                 product_id=None,
                 model=None,
                 mainloop=None,
                 create_sync_manager=True):
        self._eyetracker_info = eyetracker_info
        self._requested_product_id = product_id
        self._requested_model = model
        self._mainloop = mainloop
        self._eyetracker = None
        self._queue = None
        self._tobiiClock = None
        self._getTobiiClockResolution = None
        self._getTobiiClockTime = None
        self._sync_manager = None
        self._syncTimeEventDeque = None
        self._isRecording = False

        if eyetracker_info is None:
            if not TobiiTrackerBrowser.isActive():
                TobiiTrackerBrowser.start()
                self._eyetracker_info = TobiiTrackerBrowser.findDevice(
                    model, product_id)
                if self._eyetracker_info:
                    self._mainloop = TobiiTrackerBrowser.getMainLoop()
                TobiiTrackerBrowser.stop()
            else:
                self._eyetracker_info = TobiiTrackerBrowser.findDevice(
                    model, product_id)

        if self._eyetracker_info is None:
            raise exceptions.BaseException(
                "Could not find a Tobii Eye Tracker matching requirements.")

        if self._mainloop is None:
            if TobiiTrackerBrowser.isActive():
                self._mainloop = TobiiTrackerBrowser.getMainLoop()
            else:
                TobiiPy.init()
                self._mainloop = TobiiPyMainloopThread()
                self._mainloop.start()

        self._queue = Queue.Queue()

        TobiiPyEyeTracker.create_async(self._mainloop, self._eyetracker_info,
                                       self.on_eyetracker_created)

        stime = getTime()
        while getTime() - stime < 10.0:
            try:
                event = self._queue.get(block=True, timeout=.1)
                if isinstance(event, TobiiTrackerCreatedEvent):
                    self._eyetracker = event.tracker_object
                    self._eyetracker.events.OnFramerateChanged += self.on_external_framerate_change
                    if hasattr(self._eyetracker.events,
                               'OnHeadMovementBoxChanged'):
                        self._eyetracker.events.OnHeadMovementBoxChanged += self.on_head_box_change
                    elif hasattr(self._eyetracker.events, 'OnTrackBoxChanged'):
                        self._eyetracker.events.OnTrackBoxChanged += self.on_head_box_change
                    else:
                        print 'WARNING: TobiiClasses could not set callback hook for "self.on_head_box_change".'
                    self._eyetracker.events.OnXConfigurationChanged += self.on_x_series_physical_config_change

                    break
                self._queue.task_done()
            except Queue.Empty:
                pass

        if self._eyetracker is None:
            raise exceptions.BaseException(
                "Could not connect to Tobii. Timeout.")

        if create_sync_manager:
            self._eyetracker.events.OnError += self.on_eyetracker_error
            self._tobiiClock = TobiiPyClock()
            self._getTobiiClockResolution = self._tobiiClock.get_resolution
            self._getTobiiClockTime = self._tobiiClock.get_time
            self._syncTimeEventDeque = collections.deque(maxlen=32)
            self._sync_manager = TobiiPySyncManager(self._tobiiClock,
                                                    self._eyetracker_info,
                                                    self._mainloop,
                                                    self.on_sync_error,
                                                    self.on_sync_status)

    def on_eyetracker_created(self, *args, **kwargs):
        #print 'on_eyetracker_created: entered'
        et = None
        if len(args) >= 2:
            et = args[1]
        else:
            raise exceptions.BaseException(
                "WARNING: on_eyetracker_created: Unhandled args count",
                len(args), args)

        error = kwargs.get('error', None)
        if error:
            raise exceptions.BaseException(
                "Connection to Tobii failed because of an exception: %s" %
                (str(error), ))

        self._queue.put(TobiiTrackerCreatedEvent(et))

        return False

    def on_eyetracker_error(self, *args, **kwargs):
        print2err("TobiiTracker.on_eyetracker_error: ", args, kwargs)
        return False

    def on_sync_error(self, *args, **kwargs):
        print2err("TobiiTracker.on_sync_error: ", args, kwargs)
        return False

    def on_sync_status(self, *args, **kwargs):
        sync_state = args[0]
        self._syncTimeEventDeque.append(sync_state)
        return False

    def on_eyetracker_data(self, *args, **kwargs):
        eye_data_event = args[1]
        LEFT = self.LEFT
        RIGHT = self.RIGHT

        eyes = (copy.deepcopy(self.eye_data), copy.deepcopy(self.eye_data))

        eyes[LEFT]['validity_code'] = eye_data_event.LeftValidity
        eyes[RIGHT]['validity_code'] = eye_data_event.RightValidity
        eyes[LEFT]['tracker_time_usec'] = eye_data_event.Timestamp
        eyes[RIGHT]['tracker_time_usec'] = eye_data_event.Timestamp
        if hasattr(eye_data_event, 'TrigSignal'):
            eyes[LEFT]['trigger_signal'] = eye_data_event.TrigSignal
            eyes[RIGHT]['trigger_signal'] = eye_data_event.TrigSignal

        #print "*** lastEyeData.RightGazePoint2D: ",lastEyeData.RightGazePoint2D.__dict__
        if eye_data_event.LeftValidity >= 2 and eye_data_event.RightValidity >= 2:
            # no eye signal
            eyes[LEFT]['status'] = "Missing"
            eyes[RIGHT]['status'] = "Missing"

        elif eye_data_event.LeftValidity < 2 and eye_data_event.RightValidity >= 2:
            # left eye only available
            eyes[LEFT]['status'] = "Available"
            eyes[RIGHT]['status'] = "Missing"

            eyes[LEFT]['pupil_diameter_mm'] = eye_data_event.LeftPupil
            eyes[LEFT]['gaze_norm'][0] = eye_data_event.LeftGazePoint2D.x
            eyes[LEFT]['gaze_norm'][1] = eye_data_event.LeftGazePoint2D.y
            eyes[LEFT]['gaze_mm'][0] = eye_data_event.LeftGazePoint3D.x
            eyes[LEFT]['gaze_mm'][1] = eye_data_event.LeftGazePoint3D.y
            eyes[LEFT]['gaze_mm'][2] = eye_data_event.LeftGazePoint3D.z
            eyes[LEFT]['eye_location_norm'][
                0] = eye_data_event.LeftEyePosition3DRelative.x
            eyes[LEFT]['eye_location_norm'][
                1] = eye_data_event.LeftEyePosition3DRelative.y
            eyes[LEFT]['eye_location_norm'][
                2] = eye_data_event.LeftEyePosition3DRelative.z
            eyes[LEFT]['eye_location_mm'][
                0] = eye_data_event.LeftEyePosition3D.x
            eyes[LEFT]['eye_location_mm'][
                1] = eye_data_event.LeftEyePosition3D.y
            eyes[LEFT]['eye_location_mm'][
                2] = eye_data_event.LeftEyePosition3D.z

        elif eye_data_event.LeftValidity >= 2 and eye_data_event.RightValidity < 2:
            # right eye only available
            eyes[RIGHT]['status'] = "Available"
            eyes[LEFT]['status'] = "Missing"

            eyes[RIGHT]['pupil_diameter_mm'] = eye_data_event.RightPupil
            eyes[RIGHT]['gaze_norm'][0] = eye_data_event.RightGazePoint2D.x
            eyes[RIGHT]['gaze_norm'][1] = eye_data_event.RightGazePoint2D.y
            eyes[RIGHT]['gaze_mm'][0] = eye_data_event.RightGazePoint3D.x
            eyes[RIGHT]['gaze_mm'][1] = eye_data_event.RightGazePoint3D.y
            eyes[RIGHT]['gaze_mm'][2] = eye_data_event.RightGazePoint3D.z
            eyes[RIGHT]['eye_location_norm'][
                0] = eye_data_event.RightEyePosition3DRelative.x
            eyes[RIGHT]['eye_location_norm'][
                1] = eye_data_event.RightEyePosition3DRelative.y
            eyes[RIGHT]['eye_location_norm'][
                2] = eye_data_event.RightEyePosition3DRelative.z
            eyes[RIGHT]['eye_location_mm'][
                0] = eye_data_event.RightEyePosition3D.x
            eyes[RIGHT]['eye_location_mm'][
                1] = eye_data_event.RightEyePosition3D.y
            eyes[RIGHT]['eye_location_mm'][
                2] = eye_data_event.RightEyePosition3D.z
        else:
            # binocular available
            eyes[RIGHT]['status'] = "Available"
            eyes[LEFT]['status'] = "Available"

            eyes[LEFT]['pupil_diameter_mm'] = eye_data_event.LeftPupil
            eyes[LEFT]['gaze_norm'][0] = eye_data_event.LeftGazePoint2D.x
            eyes[LEFT]['gaze_norm'][1] = eye_data_event.LeftGazePoint2D.y
            eyes[LEFT]['gaze_mm'][0] = eye_data_event.LeftGazePoint3D.x
            eyes[LEFT]['gaze_mm'][1] = eye_data_event.LeftGazePoint3D.y
            eyes[LEFT]['gaze_mm'][2] = eye_data_event.LeftGazePoint3D.z
            eyes[LEFT]['eye_location_norm'][
                0] = eye_data_event.LeftEyePosition3DRelative.x
            eyes[LEFT]['eye_location_norm'][
                1] = eye_data_event.LeftEyePosition3DRelative.y
            eyes[LEFT]['eye_location_norm'][
                2] = eye_data_event.LeftEyePosition3DRelative.z
            eyes[LEFT]['eye_location_mm'][
                0] = eye_data_event.LeftEyePosition3D.x
            eyes[LEFT]['eye_location_mm'][
                1] = eye_data_event.LeftEyePosition3D.y
            eyes[LEFT]['eye_location_mm'][
                2] = eye_data_event.LeftEyePosition3D.z

            eyes[RIGHT]['pupil_diameter_mm'] = eye_data_event.RightPupil
            eyes[RIGHT]['gaze_norm'][0] = eye_data_event.RightGazePoint2D.x
            eyes[RIGHT]['gaze_norm'][1] = eye_data_event.RightGazePoint2D.y
            eyes[RIGHT]['gaze_mm'][0] = eye_data_event.RightGazePoint3D.x
            eyes[RIGHT]['gaze_mm'][1] = eye_data_event.RightGazePoint3D.y
            eyes[RIGHT]['gaze_mm'][2] = eye_data_event.RightGazePoint3D.z
            eyes[RIGHT]['eye_location_norm'][
                0] = eye_data_event.RightEyePosition3DRelative.x
            eyes[RIGHT]['eye_location_norm'][
                1] = eye_data_event.RightEyePosition3DRelative.y
            eyes[RIGHT]['eye_location_norm'][
                2] = eye_data_event.RightEyePosition3DRelative.z
            eyes[RIGHT]['eye_location_mm'][
                0] = eye_data_event.RightEyePosition3D.x
            eyes[RIGHT]['eye_location_mm'][
                1] = eye_data_event.RightEyePosition3D.y
            eyes[RIGHT]['eye_location_mm'][
                2] = eye_data_event.RightEyePosition3D.z

        return False

    def on_start_tracking(self, *args, **kwargs):
        return False

    def on_stop_tracking(self, *args, **kwargs):
        return False

    def on_external_framerate_change(self, *args, **kwargs):
        print2err("NOTE: Tobii System Sampling Rate Changed.")
        return False

    def on_head_box_change(self, *args, **kwargs):
        print2err("NOTE: Tobii Head Movement Box Changed.")
        return False

    def on_x_series_physical_config_change(self, *args, **kwargs):
        print2err("NOTE: Tobii X Series Physical Settings Changed.")
        return False

    def getTimeSyncManager(self):
        return self._sync_manager

    def getTimeSyncState(self):
        return self._sync_manager.sync_state()

    def getCurrentEyeTrackerTime(self):
        return self._sync_manager.convert_from_local_to_remote(
            self._getTobiiClockTime())

    def getCurrentLocalTobiiTime(self):
        return self._getTobiiClockTime()

    def getTobiiTimeResolution(self):
        return self._getTobiiClockResolution()

    def getMainLoop(self):
        return self._mainloop

    def getTrackerDetails(self):
        tobiiInfoProperties = [
            'product_id', 'given_name', 'model', 'generation',
            'firmware_version', 'status'
        ]
        tprops = OrderedDict()
        eyetracker_info = self._eyetracker_info
        for tp in tobiiInfoProperties:
            ta = getattr(eyetracker_info, tp)
            if callable(ta):
                ta = ta()
            tprops[tp] = ta
        tprops['factory_info_string'] = str(eyetracker_info.factory_info)
        return tprops

    def getTrackerInfo(self):
        if hasattr(self._eyetracker, 'GetUnitInfo'):
            return self._eyetracker.GetUnitInfo()
        return None

    def startTracking(self, et_data_rx_callback=None):
        if et_data_rx_callback:
            self.on_eyetracker_data = et_data_rx_callback
        self._eyetracker.events.OnGazeDataReceived += self.on_eyetracker_data
        self._eyetracker.StartTracking(self.on_start_tracking)
        self._isRecording = True
        return True

    def stopTracking(self):
        self._eyetracker.events.OnGazeDataReceived -= self.on_eyetracker_data
        self._eyetracker.StopTracking(self.on_stop_tracking)
        self._isRecording = False

    def getName(self):
        return self._eyetracker.GetUnitName()

    def setName(self, name):
        self._eyetracker.SetUnitName(name)

    def getLowBlinkMode(self):
        try:
            return self._eyetracker.GetLowblinkMode()
        except:
            pass
        return None

    def setLowBlinkMode(self, enable):
        if hasattr(self._eyetracker, 'SetLowblinkMode'):
            try:
                if isinstance(enable, bool) or enable == 0 or enable == 1:
                    self._eyetracker.SetLowblinkMode(enable)
                return self._eyetracker.GetLowblinkMode()
            except:
                pass
        return None

    def setSamplingRate(self, rate):
        if rate in self._eyetracker.EnumerateFramerates():
            self._eyetracker.SetFramerate(rate)
            return rate
        return self._eyetracker.GetFramerate()

    def getAvailableSamplingRates(self):
        return self._eyetracker.EnumerateFramerates()

    def getSamplingRate(self):
        return self._eyetracker.GetFramerate()

    def getIlluminationMode(self):
        try:
            return self._eyetracker.getIlluminationMode()
        except:
            pass
        return None

    def getAvailableIlluminationModes(self):
        try:
            return self._eyetracker.EnumerateIlluminationModes()
        except:
            return []

    def setIlluminationMode(self, imode):
        imodes = self.getAvailableIlluminationModes()
        if len(imodes) > 0:
            if imode in imodes:
                self._eyetracker.SetIlluminationMode(imode)
                return imode
            return self._eyetracker.getIlluminationMode()
        else:
            print 'WARNING: setIlluminationMode is not supported by either your Tobii model, or the version of the Tobii SDK being used.'

    def getHeadBox(self):
        hb = None
        if hasattr(self._eyetracker, 'GetTrackBox'):
            hb = self._eyetracker.GetTrackBox()
        elif hasattr(self._eyetracker, 'GetHeadMovementBox'):
            hb = self._eyetracker.GetHeadMovementBox()

        if hb:
            return np.asarray([(hb.Point1.x, hb.Point1.y, hb.Point1.z),
                               (hb.Point2.x, hb.Point2.y, hb.Point2.z),
                               (hb.Point3.x, hb.Point3.y, hb.Point3.z),
                               (hb.Point4.x, hb.Point4.y, hb.Point4.z),
                               (hb.Point5.x, hb.Point5.y, hb.Point5.z),
                               (hb.Point6.x, hb.Point6.y, hb.Point6.z),
                               (hb.Point7.x, hb.Point7.y, hb.Point7.z),
                               (hb.Point8.x, hb.Point8.y, hb.Point8.z)])
        return None

    def setXSeriesPhysicalPlacement(self, upperLeft, upperRight, lowerLeft):
        if self.getTrackerDetails()['generation'] == 'X':
            self._eyetracker.SetXConfiguration(upperLeft, upperRight,
                                               lowerLeft)
            return True
        return False

    def getEyeTrackerPhysicalPlacement(self):
        etpc = self._eyetracker.GetXConfiguration()
        ll = etpc.LowerLeft
        ul = etpc.UpperLeft
        ur = etpc.UpperRight
        return dict(lowerLeft=(ll.x, ll.y, ll.z),
                    upperLeft=(ul.x, ul.y, ul.z),
                    upperRight=(ur.x, ur.y, ur.z))

    def getAvailableExtensions(self):
        return [
            OrderedDict(name=e.Name,
                        extensionId=e.ExtensionId,
                        protocolVersion=e.ProtocolVersion,
                        realm=e.Realm)
            for e in self._eyetracker.GetAvailableExtensions()
        ]

    def getEnabledExtensions(self):
        return [
            OrderedDict(name=e.Name,
                        extensionId=e.ExtensionId,
                        protocolVersion=e.ProtocolVersion,
                        realm=e.Realm)
            for e in self._eyetracker.GetEnabledExtensions()
        ]

    def enableExtension(self, extension):
        extension_id = extension
        if isinstance(extension, OrderedDict):
            extension_id = extension['extensionId']
        self._eyetracker.EnableExtension(extension_id)

    def disconnect(self):
        if self._isRecording:
            self.stopTracking()
        self._mainloop.stop()
        self._mainloop = None
        self._eyetracker_info = None
        self._requested_product_id = None
        self._requested_model = None
        self._eyetracker = None

    def __del__(self):
        if self._mainloop:
            self.disconnect()
Exemple #10
0
class TobiiTrackerBrowser(object):
    _mainloop = None
    _detectedTrackers = OrderedDict()
    _browser = None
    _event_queue = None
    _active = False
    _mainloop_referenced = False

    TRACKER_FOUND = 1
    TRACKER_UPDATED = 2
    TRACKER_DROPPED = 3

    @classmethod
    def start(cls):
        if TobiiTrackerBrowser._mainloop is None:
            TobiiPy.init()
            TobiiTrackerBrowser._mainloop = TobiiPyMainloopThread()
            TobiiTrackerBrowser._mainloop.start()
            TobiiTrackerBrowser._event_queue = Queue.Queue()
            TobiiTrackerBrowser._browser = TobiiPyEyetrackerBrowser(
                TobiiTrackerBrowser._mainloop,
                TobiiTrackerBrowser.on_eyetracker_browser_event)
            cls._active = True

    @classmethod
    def stop(cls, empty_queue=True):
        if cls._active:
            cls._active = False
            TobiiTrackerBrowser._event_queue.put(None)
            TobiiTrackerBrowser._browser.stop()

            if cls._mainloop_referenced is False:
                TobiiTrackerBrowser._mainloop.stop()
                TobiiTrackerBrowser._mainloop = None

            if empty_queue is True:
                e = 1
                while e != None:
                    try:
                        e = TobiiTrackerBrowser.getNextEvent()
                    except Queue.Empty:
                        pass

            TobiiTrackerBrowser._browser = None
            TobiiTrackerBrowser._detectedTrackers = OrderedDict()

    @classmethod
    def isActive(cls):
        return cls._active

    @classmethod
    def getMainLoop(cls):
        cls._mainloop_referenced = True
        return cls._mainloop

    @classmethod
    def getNextEvent(cls, timeout=None):
        if timeout is None:
            e = TobiiTrackerBrowser._event_queue.get_nowait()
            TobiiTrackerBrowser._event_queue.task_done()
            return e
        e = TobiiTrackerBrowser._event_queue.get(block=True, timeout=timeout)
        TobiiTrackerBrowser._event_queue.task_done()
        return e

    @classmethod
    def on_eyetracker_browser_event(cls, event_type, event_name,
                                    eyetracker_info):
        if event_type == TobiiPyEyetrackerBrowser.FOUND:
            TobiiTrackerBrowser._detectedTrackers[
                eyetracker_info.product_id] = eyetracker_info
            TobiiTrackerBrowser._event_queue.put(
                TrackerFoundEvent(eyetracker_info))
            return False

        if event_type == TobiiPyEyetrackerBrowser.UPDATED:
            TobiiTrackerBrowser._detectedTrackers[
                eyetracker_info.product_id] = eyetracker_info
            TobiiTrackerBrowser._event_queue.put(
                TrackerUpdatedEvent(eyetracker_info))
            return False

        if TobiiPyEyetrackerBrowser.REMOVED:
            del TobiiTrackerBrowser._detectedTrackers[
                eyetracker_info.product_id]
            TobiiTrackerBrowser._event_queue.put(
                TrackerRemovedEvent(eyetracker_info))
            return False

        raise ioDeviceError(
            device=cls,
            msg=
            "TobiiTrackerBrowser.on_eyetracker_browser_event received unhandled event type {0} for Tobii device with info: {1}"
            .format(event_type, eyetracker_info))

    @classmethod
    def getTrackerDetails(cls, tracker_product_id):
        tobiiInfoProperties = [
            'product_id', 'given_name', 'model', 'generation',
            'firmware_version', 'status'
        ]
        tprops = OrderedDict()
        eyetracker_info = TobiiTrackerBrowser._detectedTrackers[
            tracker_product_id]
        for tp in tobiiInfoProperties:
            ta = getattr(eyetracker_info, tp)
            if callable(ta):
                ta = ta()
            tprops[tp] = ta
        tprops['factory_info_string'] = str(eyetracker_info.factory_info)
        return tprops

    @classmethod
    def getDetectedTrackerList(cls):
        return [t for t in TobiiTrackerBrowser._detectedTrackers.values()]

    @classmethod
    def _checkForMatch(cls, tracker_info, model, product_id):
        if product_id:
            if tracker_info.product_id != product_id:
                return False
        if model:
            if model != tracker_info.model:
                return False

        return True

    @classmethod
    def findDevice(cls, model=None, product_id=None, timeout=10.0):
        tracker_info = None

        # check existing detected devices
        matching_tracker_infos = [
            tracker_info for tracker_info in cls.getDetectedTrackerList()
            if cls._checkForMatch(tracker_info, model, product_id) is True
        ]
        if matching_tracker_infos:
            return matching_tracker_infos[0]

        started_browsing_time = getTime()
        while (getTime() - started_browsing_time < timeout):
            try:
                tb_event = TobiiTrackerBrowser.getNextEvent(timeout=0.05)
                if tb_event is None:
                    break
                if isinstance(tb_event, TrackerFoundEvent):
                    tracker_info = tb_event.tracker_info
                    if TobiiTrackerBrowser._checkForMatch(
                            tracker_info, model, product_id) is True:
                        return tracker_info
            except Queue.Empty:
                pass