-
Notifications
You must be signed in to change notification settings - Fork 901
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #3685 from isolver/wip_iohub_refresh
initial batch of iohub specific cleanup / updates
- Loading branch information
Showing
53 changed files
with
1,204 additions
and
3,806 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,228 @@ | ||
#!/usr/bin/env python | ||
# -*- coding: utf-8 -*- | ||
|
||
""" | ||
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 at least 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. | ||
""" | ||
|
||
from __future__ import absolute_import, division, print_function | ||
|
||
from numpy import zeros | ||
from scipy.stats import norm | ||
from psychopy import visual | ||
from psychopy.iohub import Computer, launchHubServer | ||
from psychopy.iohub.constants import EventConstants | ||
from collections import OrderedDict | ||
|
||
totalEventRequestsForTest = 1000 | ||
numEventRequests = 0 | ||
|
||
def run(): | ||
global numEventRequests | ||
io = launchHubServer(experiment_code='delay_test') | ||
|
||
lastFlipTime = 0.0 | ||
|
||
# create fullscreen pyglet window at current resolution, as well as required resources / drawings | ||
psychoWindow, psychoStim = createPsychoGraphicsWindow(io) | ||
|
||
# create stats numpy arrays, set experiment process to high priority. | ||
# Init Results numpy array | ||
results= zeros((totalEventRequestsForTest,3),dtype='f4') | ||
|
||
numEventRequests=0 | ||
|
||
# clear the ioHub event Buffer before starting the test. | ||
# This is VERY IMPORTANT, given an existing bug in ioHub. | ||
# You would want to do this before each trial started until the bug is fixed. | ||
io.clearEvents('all') | ||
|
||
# draw and flip to the updated graphics state. | ||
flipTime = drawAndFlipPsychoWindow(psychoStim, psychoWindow, io, None) | ||
ifi = flipTime - lastFlipTime | ||
lastFlipTime = flipTime | ||
|
||
# START TEST LOOP >>>>>>>>>>>>>>>>>>>>>>>>>> | ||
while numEventRequests < totalEventRequestsForTest: | ||
# send an Experiment Event to the ioHub server process | ||
io.sendMessageEvent("This is a test message %.3f" % 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 | ||
events, callDuration = checkForEvents(io) | ||
if events: | ||
# events were available | ||
results[numEventRequests][0] = callDuration # ctime it took to get events from ioHub | ||
results[numEventRequests][1] = len(events) # number of events returned | ||
results[numEventRequests][2] = ifi * 1000.0 # calculating inter flip interval. | ||
numEventRequests += 1 # incrementing tally counterfgh | ||
# draw and flip to the updated graphics state. | ||
|
||
flipTime = drawAndFlipPsychoWindow(psychoStim, psychoWindow, io, events) | ||
ifi = flipTime - lastFlipTime | ||
lastFlipTime = flipTime | ||
|
||
# END TEST LOOP <<<<<<<<<<<<<<<<<<<<<<<<<< | ||
|
||
psychoWindow.close() | ||
|
||
# plot collected delay and retrace detection results. | ||
plotResults(results) | ||
printResults(results) | ||
|
||
def createPsychoGraphicsWindow(io): | ||
#create a window | ||
psychoStim = OrderedDict() | ||
psychoWindow = visual.Window(io.devices.display.getPixelResolution(), | ||
monitor=io.devices.display.getPsychopyMonitorName(), | ||
units=io.devices.display.getCoordinateType(), | ||
color=[128,128,128], colorSpace='rgb255', | ||
fullscr=True, allowGUI=False, | ||
screen=io.devices.display.getIndex() | ||
) | ||
|
||
currentPosition= io.devices.mouse.setPosition((0,0)) | ||
psychoWindow.setMouseVisible(False) | ||
|
||
fixation = visual.PatchStim(psychoWindow, size=25, pos=[0,0], sf=0, | ||
color=[-1,-1,-1], colorSpace='rgb') | ||
title = visual.TextStim(win=psychoWindow, | ||
text="ioHub getEvents Delay Test", pos = [0,125], | ||
height=36, color=[1,.5,0], colorSpace='rgb', | ||
wrapWidth=800.0) | ||
|
||
instr = visual.TextStim(win=psychoWindow, | ||
text='Move the mouse around, press keyboard keys and mouse buttons', | ||
pos = [0,-125], height=32, color=[-1,-1,-1], | ||
colorSpace='rgb', wrapWidth=800.0) | ||
|
||
psychoStim['static'] = visual.BufferImageStim(win=psychoWindow, | ||
stim=(fixation, title, instr)) | ||
psychoStim['grating'] = visual.PatchStim(psychoWindow, | ||
mask="circle", size=75,pos=[-100,0], | ||
sf=.075) | ||
psychoStim['keytext'] = visual.TextStim(win=psychoWindow, | ||
text='key', pos = [0,300], height=48, | ||
color=[-1,-1,-1], colorSpace='rgb', | ||
wrapWidth=800.0) | ||
psychoStim['mouseDot'] = visual.GratingStim(win=psychoWindow, | ||
tex=None, mask="gauss", | ||
pos=currentPosition,size=(50,50), | ||
color='purple') | ||
psychoStim['progress'] = visual.ShapeStim(win=psychoWindow, | ||
vertices=[(0,0),(0,0),(0,0),(0,0)], | ||
pos=(400, -300)) | ||
|
||
return psychoWindow, psychoStim | ||
|
||
def drawAndFlipPsychoWindow(psychoStim, psychoWindow, io, events): | ||
psychoStim['grating'].setPhase(0.05, '+')#advance phase by 0.05 of a cycle | ||
currentPosition,currentDisplayIndex =io.devices.mouse.getPosition(return_display_index=True) | ||
|
||
if currentDisplayIndex == io.devices.display.getIndex(): | ||
currentPosition=(float(currentPosition[0]),float(currentPosition[1])) | ||
psychoStim['mouseDot'].setPos(currentPosition) | ||
|
||
if events: | ||
diff = totalEventRequestsForTest - numEventRequests | ||
v = psychoWindow.size[1] / 2.0 * diff / totalEventRequestsForTest | ||
vert = [[0,0], [0,v], [2,v],[2,0]] | ||
psychoStim['progress'].setVertices(vert) | ||
|
||
for r in events: | ||
if r.type is EventConstants.KEYBOARD_PRESS: #keypress code | ||
psychoStim['keytext'].setText(r.key) | ||
|
||
[psychoStim[skey].draw() for skey in psychoStim] | ||
|
||
flipTime=psychoWindow.flip() | ||
return flipTime | ||
|
||
def checkForEvents(io): | ||
# get the time we request events from the ioHub | ||
stime=Computer.getTime() | ||
r = io.getEvents() | ||
if r and len(r) > 0: | ||
# so there were events returned in the request, so include this getEvent request in the tally | ||
etime=Computer.getTime() | ||
dur=etime-stime | ||
return r, dur*1000.0 | ||
return None, None | ||
|
||
|
||
def plotResults(results): | ||
#### calculate stats on collected data and draw some plots #### | ||
from matplotlib.pyplot import axis, title, xlabel, hist, grid, show, ylabel, plot | ||
import pylab | ||
|
||
durations=results[:,0] | ||
flips=results[1:,2] | ||
|
||
dmean=durations.mean() | ||
dstd=durations.std() | ||
|
||
fmean=flips.mean() | ||
fstd=flips.std() | ||
|
||
pylab.figure(figsize=(7,5)) | ||
pylab.subplot(1,3,1) | ||
|
||
# the histogram of the delay data | ||
n, bins, patches = hist(durations, 50, facecolor='blue', alpha=0.75) | ||
# add a 'best fit' line | ||
y = norm.pdf( bins, dmean, dstd) | ||
plot(bins, y, 'r--', linewidth=1) | ||
xlabel('ioHub getEvents Delay') | ||
ylabel('Percentage') | ||
title('ioHub Event Delays (msec):\n'+r'$\ \mu={0:.3f},\ \sigma={1:.3f}$'.format(dmean, dstd)) | ||
axis([0, durations.max()+1.0, 0, 25.0]) | ||
grid(True) | ||
|
||
# graphs of the retrace data ( taken from retrace example in psychopy demos folder) | ||
intervalsMS = flips | ||
m=fmean | ||
sd=fstd | ||
distString= "Mean={0:.1f}ms, s.d.={1:.1f}, 99%CI={2:.1f}-{3:.1f}".format( | ||
m, sd, m - 3 * sd, m + 3 * sd) | ||
nTotal=len(intervalsMS) | ||
nDropped=sum(intervalsMS>(1.5*m)) | ||
droppedString = "Dropped/Frames = {0:d}/{1:d} = {2:0.2f}%".format( | ||
nDropped, nTotal, int(nDropped) / float(nTotal)) | ||
|
||
pylab.subplot(1,3,2) | ||
|
||
#plot the frameintervals | ||
pylab.plot(intervalsMS, '-') | ||
pylab.ylabel('t (ms)') | ||
pylab.xlabel('frame N') | ||
pylab.title(droppedString) | ||
|
||
pylab.subplot(1,3,3) | ||
pylab.hist(intervalsMS, 50, histtype='stepfilled') | ||
pylab.xlabel('t (ms)') | ||
pylab.ylabel('n frames') | ||
pylab.title(distString) | ||
show() | ||
|
||
def printResults(results): | ||
durations=results[:,0] | ||
dmean=durations.mean() | ||
dstd=durations.std() | ||
print("ioHub getEvent Delays:") | ||
print("\tMEAN: ", dmean) | ||
print("\tSDEV: ", dstd) | ||
|
||
|
||
if __name__ == "__main__": | ||
run() |
10 changes: 0 additions & 10 deletions
10
psychopy/demos/coder/iohub/delaytest/experiment_config.yaml
This file was deleted.
Oops, something went wrong.
This file was deleted.
Oops, something went wrong.
Oops, something went wrong.