Showing posts with label timing. Show all posts
Showing posts with label timing. Show all posts

Thursday, October 22, 2009

Presenting a stimulus for a fixed duration (despite button presses)

Especially with fMRI, I want my trials to last a particular duration, whether or not the subject responded by pressing a button.

The standard PRESENT function terminates as soon as a button has been pressed. You could immediately re-present the stimulus for the remaining duration, but this creates an annoying flicker.

I sub-classed Text and Image to add a PRESENT_FIXED_DUR function that deals with this problem. This issue was also raised on the PyEPL mailing list.

For images:

#!/usr/bin/python

# from pyepl.hardware.graphics import Image
from pyepl.locals import Image
from pyepl.display import VideoTrack
from pyepl.locals import PresentationClock

class Image2(Image):

    def present_fixed_dur(self, clk=None, duration=None, bc=None, minDuration=None):
        """
        This is just like the standard Image.present, except
        that if you press a button, the image stays on the
        screen until the full duration has passed.

        I got rid of the jitter argument, because it makes
        things complicated.
        """

        v = VideoTrack.lastInstance()
        
        # get the clock if needed
        if clk is None: clk = PresentationClock()

        # show the image
        t = v.showCentered(self)
        timestamp = v.updateScreen(clk)

        if bc:
            # wait for button press
            button,bc_time = bc.waitWithTime(minDuration,duration,clk)
            # figure out how much time is remaining, now
            # that they've pressed the button, and delay for
            # just that
            rt = bc_time[0] - timestamp[0]
            clk.delay(duration-rt)
        else:
            clk.delay(duration)

        # unshow that image
        v.unshow(t)
        upd_ts = v.updateScreen(clk)
        # print 'presented for %ims' % (upd_ts[0] - timestamp[0])

        if bc: return timestamp,button,bc_time
        else: return timestamp

For text:

#!/usr/bin/python

# from pyepl.hardware.graphics import Image
from pyepl.locals import Text
from pyepl.display import VideoTrack

class Text2(Text):

    def present_fixed_dur(self, clk=None, duration=None, bc=None, minDuration=None, xProp=.5, yProp=.5):
        """
        Just as Text2.present, but (like
        Image2.present_fixed_dur): the standard
        Text.present, except that if you press a button, the
        stimulus stays on the screen until the full duration
        has passed.
        """

        # print 'starting present_fixed_dur'
        v = VideoTrack.lastInstance()
        
        # get the clock if needed
        if clk is None: clk = exputils.PresentationClock()

        # show the image
        t = v.showProportional(self,xProp,yProp)
        timestamp = v.updateScreen(clk)

        if bc:
            # wait for button press
            button,bc_time = bc.waitWithTime(minDuration,duration,clk)
            rt = bc_time[0] - timestamp[0]
            # print 'delaying by %i' % (duration-rt)
            # only delay if duration is True
            if duration and (duration-rt): clk.delay(duration-rt)
        else:
            # print 'delaying by %i' % duration
            if duration: clk.delay(duration)

        v.unshow(t)
        v.updateScreen(clk)

        # print 'ending present_fixed_dur'

        if bc: return timestamp,button,bc_time
        else: return timestamp

Sunday, August 3, 2008

Timing how long things are taking

When I first started writing experiments in PyEPL, I had a lot of trouble getting the timing right. I wanted a way to time how long things were taking that didn't make use of the PyEPL PresentationClock, to confirm that all was well. The Stopwatch class below is based heavily on timing-example-2.py.


"""
This is my wrapper for the time module. There's probably an
easier way to time the duration of things, but when I looked
into timing stuff, this was the best I could come up with...

To use:

t = Stopwatch()
t.start_time()

# do something

elapsed = t.finish()
"""

import time


class Stopwatch:
"""
Creates stopwatch timer objects.
"""

# stores the time the stopwatch was started
t0 = None

# stores the time the stopwatch was last looked at
t1 = None


def __init__(self):
self.t0 = 0
self.t1 = 0


def start(self):
"""
Stores the current time in t0.
"""

self.t0 = time.time()


def finish(self):
"""
Returns the elapsed duration in milliseconds. This
stores the current time in t1, and calculates the
difference between t0 (the stored start time) and
t1, so if you call this multiple times, you'll get a
larger answer each time.

You have to call this in order to update t1.
"""

self.t1 = time.time()
return self.milli()


def milli(self):
"""
Returns t1 - t0 in milliseconds. Does not update t1.
"""
return int((self.t1 - self.t0) * 1000)