Howdy, Stranger!

It looks like you're new here. If you want to get involved, click one of these buttons!

Supported by

Pygaze wait_for_saccade_end with timeout?

Hi all,

I was wondering if it is possible to use the wait_for_saccade_end() function with a timeout. Specifically, we are using an Eyelink and would like to wait for completed saccade OR until 2000 ms have passed.

Since wait_for_saccade_end() essentially block the scripts until a saccade has been made, we're having trouble implementing the later.

While I think it should be possible to adapt the pygaze version of the saccade detection to add a timeout parameter, the 'native' functionality that is called for Eyelink seems a lot harder to adapt.

So I was hoping someone else has tackled this problem before.

One workaround I am currently considering / testing, but seems quite crude is to run wait_for_saccade_end() in a new thread using the threading module, and adding a trace so the thread can be killed if needed (https://www.geeksforgeeks.org/python-different-ways-to-kill-a-thread/)

Kind regards,

Kerwin

Comments

  • Hi @kolfers ,

    I would adapt the the wait_for_event() function from PyGaze to break after a timeout.

    I blind-coded the example below (I don't have an eye tracker now to test) but it should convey the general idea of how wait for a saccade using the EyeLink's native event detection with a timeout. This script should simply go into an inline_script item.

    import pylink
    
    t0 = clock.get_time()
    saccade = False
    timeout = 2000
    for ms in clock.loop_for(timeout):
        d = pylink.getEYELINK().getNextData()
        if d == pylink.STARTSACC:
            float_data  = pylink.getEYELINK().getFloatData()
            tc = float_data.getTime() - eyetracker._get_eyelink_clock_async()
            if tc > t0:
                saccade = True
                break
    

    One workaround I am currently considering / testing, but seems quite crude is to run wait_for_saccade_end() in a new thread using the threading module, and adding a trace so the thread can be killed if needed

    Threading is generally not a good idea for things like this because the timing is quite unreliable, and it's also easy to create race conditions that unpredictably make your experiment crash!

    — Sebastiaan

  • Hi Sebastiaan,

    Thank you, that approach did the trick. I couldn't get the clock.loop_for to work though. Perhaps because of the imported clock from pygaze.libtime? In case someone else finds it useful in the future:

        def wait_for_event_timeout(self, event, timeout):
    
            """See pygaze._eyetracker.baseeyetracker.BaseEyeTracker"""
    
            if not self.recording:
                raise Exception(
                    "Error in libeyelink.libeyelink.wait_for_event(): Recording "
                    "was not started before collecting eyelink data!")
                
            start_time = clock.get_time()
            check_time = False
            if isinstance(timeout,int) and timeout > 0:
                check_time = True 
                end_time = start_time + timeout
            else:
                raise Exception("timeout (in ms) should be an integer larger than 0")
    
            if self.eye_used == None:
                self.set_eye_used()
            if self.eventdetection == 'native':
                # since the link buffer was not have been polled, old data has
                # accumulated in the buffer -- so ignore events that are old:
                t0 = clock.get_time() # time of call
                while True:
                    if check_time and clock.get_time() >= end_time: return "timeout", clock.get_time
                    d = pylink.getEYELINK().getNextData()
                    if d == event:
                        float_data  = pylink.getEYELINK().getFloatData()
                        # corresponding clock_time
                        tc = float_data.getTime() - self._get_eyelink_clock_async()
                        if tc > t0:
                            return tc, float_data
    
        def wait_for_saccade_start_timeout(self,timeout=None):
    
                """See pygaze._eyetracker.baseeyetracker.BaseEyeTracker"""
                # # # # #
                # EyeLink method
    
                if self.eventdetection == 'native':
                    t,d = self.wait_for_event_timeout(pylink.STARTSACC, timeout)
                    if t == "timeout": return t, None
                    return t, d.getStartGaze()
    
        def wait_for_saccade_end_timeout(self,timeout=None):
    
            """See pygaze._eyetracker.baseeyetracker.BaseEyeTracker"""
    
            # # # # #
            # EyeLink method
    
            if self.eventdetection == 'native':
                t,d = self.wait_for_event_timeout(pylink.ENDSACC,timeout)
                if t == "timeout": return t, None,None
                return t, d.getStartGaze(), d.getEndGaze()
    


Sign In or Register to comment.