psychopy.org | Reference | Downloads | Github

Best coding practices to improve timing precision


#1

Hi,
We have recently acquired a 1000Hz keyboard, so I am trying to take advantage of this high sampling frequency. Imagine a simple experiment in which a visual stimulus is presented on a 60 Hz monitor for say 600ms. Imagine two experimental conditions that do not generate any effect on RT. In other words, the true population means of conditions 1 and 2 are identical. Now imagine that we code a given trial of the experiment in the following way:

RTclock =core.Clock()
event.clearEvents(eventType='keyboard')
stimulus.draw()
Mywin.flip() # display stimulus
RTclock.reset()#start clock
While True:
	t = RTclock.getTime()
	if t >= .6:
		Mywin.flip()#remove stimulus
	key_press = event.getKeys(keyList=["-->keys associated to possible responses"])
	if len(key_press) > 0:
		RT = RTclock.getTime()
		#-->store RT and key pressed
		break
	#don't call the flip method at every frame (which would necessarilly put a 60 Hz constraint on keyboard polling rate)

I would appreciate some advice/insight on the following issues :
1-The call to the flip() method to remove the stimulus is going to block the code until the next screen refresh cycle. Say we sample more RTs during this blocking period in condition 1 than condition 2 by chance. Can this generate a spurious RT difference between conditions 1 and 2 ? Would you recommend to use ioHub here, or simply use my coding scheme ?
2-600ms corresponds to exactly 36 frames. Should I really expect some code-blocking latency when calling Mywin.flip() to remove the stimulus? Should I call Mywin.flip() a little before 600ms (e.g., if t >= .59) to ensure that the stimulus will be removed at t = 600ms?

Thanks,
J


#2

This effect will only occur for keypresses that occur between 0.600 and 0.617 s. Look at your distribution of RTs. Is the proportion of responses falling in that interval enough to worry about?

Because you are checking for responses in a tight loop, the main advantages of ioHub for key press checking are kind of lost here, except for the tiny possible effect in that one-screen-refresh interval you noted above.

But shortly (planned for ~ v 3.1), a new form of keyboard checking will be introduced in PsychoPy that will be easier to use than ioHub’s but even slightly higher performance, which will enable you to take full advantage of your keyboard, while also no longer worrying about pauses for drawing or other reasons.

There is an issue with this code currently in that it is not properly conditional: it will currently be called on every screen refresh after t == 0.600, when it only needs to be called once. You should maintain a flag like stimulus_erased that you initialise to False. Then your check would be:

if t >= 0.600 and not stimulus_erased:
    Mywin.flip() #remove stimulus
    stimulus_erased = True

to ensure only one win.flip()

Yes, it could be worth calling this if t > most of one screen refresh period less than 0.600.


#3

Wonderful thanks!! I might worry too much about timing (99% of psychologists may not care about the “potential” RT issue I raised in my previous post).
I am happy to hear that a new form of keyboard checking will be introduced soon. May I ask why it’ll give higher performance than ioHub ? In addition, will this also be introduced for any sort of response device (gamepad controller, mouse etc.)?
All the best,
J


#4

But bear in mind when I first starting writing that post, I didn’t realise that the win.flip() would occur on every screen refresh: my comments were based on the idea that it happens only once (which the suggested modification to the code above would provide for).

That’s a technical question I can’t really answer, but it is also only “better” at a technical level: measurably different at a millisecond level but really not meaningfully different in terms of actually measuring reaction times given their inherent magnitude and variability.


#5

Re iohub vs. PsychHID time stamping accuracy, the amount of difference also depends on the operating system. On Windows 10, PsychHID is a little more accurate than iohub. On macOS PsychHID() it is significantly better because it seems that standard keyboard api’s in macOS have inherent delay, at least when running via Python.

I’ve been using a MilliKey response box to test the time stamp accuracy of event.getKeys(), iohub getPresses(), and a simple PsychHID() getKeys() implementation and can post some results if anyone is interested. The short of it is that PsychHID is the way we should go for keyboard timing moving forward IMO.