Tools for neuroscience experiments
Project description
toon
Description
Additional tools for neuroscience experiments, including:
- A framework for polling input devices on a separate process.
- A framework for keyframe-based animation.
- High-resolution clocks.
Everything should work on Windows/Mac/Linux.
Install
Current release:
pip install toon
Development version:
pip install -i https://test.pypi.org/simple/ toon --pre
Or for the latest commit (requires compilation):
pip install git+https://github.com/aforren1/toon
See the demos/ folder for usage examples (note: some require additional packages).
Overview
Input
toon
provides a framework for polling from input devices, including common peripherals like mice and keyboards, with the flexibility to handle less-common devices like eyetrackers, motion trackers, and custom devices (see toon/input/
for examples). The goal is to make it easier to use a wide variety of devices, including those with sampling rates >1kHz, with minimal performance impact on the main process.
We use the built-in multiprocessing
module to control a separate process that hosts the device, and, in concert with numpy
, to move data to the main process via shared memory. It seems that under typical conditions, we can expect single read()
operations to take less than 500 microseconds (and more often < 100 us). See demos/bench_plot.py for an example of measuring user-side read performance.
Typical use looks like this:
from toon.input import MpDevice
from mymouse import Mouse
from timeit import default_timer
device = MpDevice(Mouse())
with device:
t1 = default_timer() + 10
while default_timer() < t1:
res = device.read()
# alternatively, unpack immediately
# time, data = device.read()
if res:
time, data = res # unpack (or access via res.time, res.data)
# N-D array of data (0th dim is time)
print(data)
# 1D array of times
print(time)
Creating a custom device is relatively straightforward, though there are a few boxes to check.
from ctypes import c_double
class MyDevice(BaseDevice):
# optional: give a hint for the buffer size (we'll allocate 1 sec worth of this)
sampling_frequency = 500
# this can either be introduced at the class level, or during __init__
shape = (3, 3)
# ctype can be a python type, numpy dtype, or ctype
# including ctypes.Structures
ctype = c_double
# optional. Do not start device communication here, wait until `enter`
def __init__(self):
pass
## Use `enter` and `exit`, rather than `__enter__` and `__exit__`
# optional: configure the device, start communicating
def enter(self):
pass
# optional: clean up resources, close device
def exit(self):
pass
# required
def read(self):
# See demos/ for examples of sharing a time source between the processes
time = self.clock()
# store new data with a timestamp
data = get_data()
return time, data
This device can then be passed to a toon.input.MpDevice
, which preallocates the shared memory and handles other details.
A few things to be aware of for data returned by MpDevice
:
- If there's no data for a given
read
,None
is returned. - The returned data is a copy of the local copy of the data. If you don't need copies, set
use_views=True
when instantiating theMpDevice
. - If receiving batches of data when reading from the device, you can return a list of (time, data) tuples.
- You can optionally use
device.start()
/device.stop()
instead of a context manager. - You can check for remote errors at any point using
device.check_error()
, though this automatically happens after entering the context manager and when reading. - In addition to python types/dtypes/ctypes, devices can return
ctypes.Structure
s (see input tests or the example_devices folder for examples).
Animation
This is still a work in progress, though I think it has some utility as-is. It's a port of the animation component in the Magnum framework, though lacking some of the features (e.g. Track extrapolation, proper handling of time scaling).
Example:
from math import sin, pi
from time import sleep
from timeit import default_timer
import matplotlib.pyplot as plt
from toon.anim import Track, Player
# see toon/anim/easing.py for all available easings
from toon.anim.easing import LINEAR, ELASTIC_IN
class Circle(object):
x = 0
y = 0
circle = Circle()
# list of (time, value)
keyframes = [(0.2, -0.5), (0.5, 0), (3, 0.5)]
x_track = Track(keyframes, easing=LINEAR)
# we can reuse keyframes
y_track = Track(keyframes, easing=ELASTIC_IN)
player = Player(repeats=3)
# directly modify an attribute
player.add(x_track, 'x', obj=circle)
def y_cb(val, obj):
obj.y = val
# modify via callback
player.add(y_track, y_cb, obj=circle)
t0 = default_timer()
player.start(t0)
vals = []
times = []
while player.is_playing:
t = default_timer()
player.advance(t)
times.append(t)
vals.append([circle.x, circle.y])
# sleep(1/60)
plt.plot(times, vals)
plt.show()
Other notes:
- Non-numeric attributes, like color strings, can also be modified in this framework (easing is ignored).
- Multiple objects can be modified simultaneously by feeding a list of objects into
player.add()
.
Utilities
The util
module includes high-resolution clocks/timers via QueryPerformanceCounter/Frequency
on Windows, mach_absolute_time
on MacOS, and clock_gettime(CLOCK_MONOTONIC)
on Linux. The class is called MonoClock
, and an instantiation called mono_clock
is created upon import. Usage:
from toon.util import mono_clock, MonoClock
clk = mono_clock # re-use pre-instantiated clock
clk2 = MonoClock(relative=False) # time relative to whenever the system's clock started
t0 = clk.get_time()
Another utility currently included is a priority
function, which tries to improve the determinism of the calling script. This is derived from Psychtoolbox's Priority
(doc here). General usage is:
from toon.util import priority
if not priority(1):
raise RuntimeError('Failed to raise priority.')
# ...do stuff...
priority(0)
The input should be a 0 (no priority/cancel), 1 (higher priority), or 2 (realtime). If the requested level is rejected, the function will return False
. The exact implementational details depend on the host operating system. All implementations disable garbage collection.
Windows
- Uses
SetPriorityClass
andSetThreadPriority
/AvSetMmMaxThreadCharacteristics
. level = 2
only seems to work if running Python as administrator.
MacOS
- Only disables/enables garbage collection; I don't have a Mac to test on.
Linux
- Sets the scheduler policy and parameters
sched_setscheduler
. - If
level == 2
, locks the calling process's virtual address space into RAM viamlockall
. - Any
level > 0
seems to fail unless the user is either superuser, or has the right capability. I've used setcap:sudo setcap cap_sys_nice=eip <path to python>
(disable by passingsudo setcap cap_sys_nice= <path>
). For memory locking, I've used Psychtoolbox's 99-psychtoolboxlimits.conf and added myself to the psychtoolbox group.
Your mileage may vary on whether these actually improve latency/determinism. When in doubt, measure! Read the warnings here.
Notes about checking whether parts are working:
Windows
- In the task manager under details, right-clicking on python and mousing over "Set priority" will show the current priority level. I haven't figured out how to verify the Avrt threading parts are working.
Linux
- Check
mlockall
withcat /proc/{python pid}/status | grep VmLck
- Check priority with
top -c -p $(pgrep -d',' -f python)
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distributions
Hashes for toon-0.15.9-cp39-cp39-win_amd64.whl
Algorithm | Hash digest | |
---|---|---|
SHA256 | 8565c096ca5b2dee549850fe7ae20fc589559b89cb451df4f426f3078a471fc3 |
|
MD5 | 60bbac1aab319fc48e0649c29d53f177 |
|
BLAKE2b-256 | d50b17353f23fd0d510df459efe2dbc794fe80fad0f625cc9df46867ba6c7ff5 |
Hashes for toon-0.15.9-cp39-cp39-manylinux2010_x86_64.whl
Algorithm | Hash digest | |
---|---|---|
SHA256 | e20d16ef3f8c391b9b0ac28be0c80a3c5d14385d622990746e138e54e7376d11 |
|
MD5 | 42cccde670ae2233f0dcc236344b41bf |
|
BLAKE2b-256 | eaeb8b6d88c0a86481c530eb78fcbd8b2f9c4d5c0bcc54a89e249f174448a7be |
Hashes for toon-0.15.9-cp39-cp39-manylinux1_x86_64.whl
Algorithm | Hash digest | |
---|---|---|
SHA256 | b995891465fe87e93681f3ce2a33d92a96bf276fff67b1ded1f63642a0b43140 |
|
MD5 | 17c7737bd3c7fba737dabb76c2a747e6 |
|
BLAKE2b-256 | 1c6b5345f94eb5896c3541e804e110c38004d5b96823eb6c76581d615eee6dbc |
Hashes for toon-0.15.9-cp39-cp39-macosx_10_9_x86_64.whl
Algorithm | Hash digest | |
---|---|---|
SHA256 | f05dad37d1bc443fdf27b883b07416b9ee131057a52c0e2ef7541c6ead6c8dda |
|
MD5 | 1fae8e61963f0c52c60c743d8757475a |
|
BLAKE2b-256 | 4c27a124b85bc20be6523f7dfe88e8a3241b7dd45f8e3bbb7e01c26f997920e3 |
Hashes for toon-0.15.9-cp38-cp38-win_amd64.whl
Algorithm | Hash digest | |
---|---|---|
SHA256 | 2d4c6a7ab2119cb3574ca184a60730a39f01af93cf1b5cf2586aae403209ff50 |
|
MD5 | 964c7ed369524f99948f589ab5a6695c |
|
BLAKE2b-256 | 54e3429aa17b6c7bd3feb4df7de58d448d1a6ccb45420defe152a633cb313515 |
Hashes for toon-0.15.9-cp38-cp38-manylinux2010_x86_64.whl
Algorithm | Hash digest | |
---|---|---|
SHA256 | 5615592cc41d9201e7a6f0b9a06960d054e202ba72f2724b7042180ec3afce4f |
|
MD5 | 4aabf3f529aef8a725448d8cdaa8ba1d |
|
BLAKE2b-256 | cbc86b8b2899c21940923070f9244fe048568bc12919300edc959e401f4beb71 |
Hashes for toon-0.15.9-cp38-cp38-manylinux1_x86_64.whl
Algorithm | Hash digest | |
---|---|---|
SHA256 | 8b539840dacb18aa9dafee79fe02989d43e41430b57e91e953bb6a31ef921504 |
|
MD5 | 9a43a6a9fd0d55ef0d08af085c727a6b |
|
BLAKE2b-256 | 12580e5d292253053bcbb7f5a53985ab7ac597efd0bb3691a6e60a9267eedf4f |
Hashes for toon-0.15.9-cp38-cp38-macosx_10_9_x86_64.whl
Algorithm | Hash digest | |
---|---|---|
SHA256 | 68cd3c9e9d6276f935bddc87504d306442bc02586fbee84332a617d214b68d16 |
|
MD5 | d10ef1fb8f57993597c8cdf1d5d53d11 |
|
BLAKE2b-256 | 4b156e47b244866fb8f8f88a1fd95811d68aae85fdd9b88f28164178e2d9d85a |
Hashes for toon-0.15.9-cp37-cp37m-win_amd64.whl
Algorithm | Hash digest | |
---|---|---|
SHA256 | 3b7970a9ba17fb212072b7cde5e24aa44abf9ca92ca1c524bfa4be3c8bf85368 |
|
MD5 | 406331e30a75b009bd3c5bc49d5bd678 |
|
BLAKE2b-256 | 54d77e55f63bc18b1d71dca41b0d9c9d099986313cd36d97308b48607977a3c6 |
Hashes for toon-0.15.9-cp37-cp37m-manylinux2010_x86_64.whl
Algorithm | Hash digest | |
---|---|---|
SHA256 | d08f1413842157822894d8e0f6b84ec0c870852d415676cd29bd733be339c384 |
|
MD5 | 7fd3019cb6afa3d94758efacc6eabdf9 |
|
BLAKE2b-256 | ac240a6bc1c0ce0a35f68e21114ac8356b6f2a5e9ebed81ba1997ce7dbe9d131 |
Hashes for toon-0.15.9-cp37-cp37m-manylinux1_x86_64.whl
Algorithm | Hash digest | |
---|---|---|
SHA256 | 9d93fc9f2803e4999c87bbc5fe20808ff158154684302a5d7e9ba92fc7182ffe |
|
MD5 | a9df312b8b765f9e118067726bc0dab6 |
|
BLAKE2b-256 | 310aad9e4d73917907efe802cb9880e3c0eed476d40b844190c1e954f3c2af0b |
Hashes for toon-0.15.9-cp37-cp37m-macosx_10_9_x86_64.whl
Algorithm | Hash digest | |
---|---|---|
SHA256 | e6e951e18d55e95e79d05f4f8f4e7b2c41b58de18d1e92c9c9e7573e575d4c39 |
|
MD5 | 1f42be5aa4eca946d99a3ca012dc53b4 |
|
BLAKE2b-256 | ef2a7c4219c319eb91dcfef8c5c9c1aa1a1e24ed0eed0079c74396da28f39490 |
Hashes for toon-0.15.9-cp36-cp36m-win_amd64.whl
Algorithm | Hash digest | |
---|---|---|
SHA256 | a9d6131a9e8876b3b6b02f83294582763be57c20db4bafb7934f94c4254b6695 |
|
MD5 | 5bf1d09c8a56e613809ef654b10fecc7 |
|
BLAKE2b-256 | 655322a61663e58387f4953be6cccceb4b3b9a15ea3faf44373679e36a939dba |
Hashes for toon-0.15.9-cp36-cp36m-manylinux2010_x86_64.whl
Algorithm | Hash digest | |
---|---|---|
SHA256 | 3f8d77939dc38ca30b01607b013bc14a8e251eab55c34452d164d26d3b6e2cab |
|
MD5 | 2eecadfbedce66e19d2a826d2722c156 |
|
BLAKE2b-256 | 860e474c3c3d875e1c5ea29e2cd0e75d7600c732da6c15ee2c7baded55f6109f |
Hashes for toon-0.15.9-cp36-cp36m-manylinux1_x86_64.whl
Algorithm | Hash digest | |
---|---|---|
SHA256 | 841f2d4aef5625b1d3e35385183476ff5ee59d9a6782f39f3bd0414747daf1d2 |
|
MD5 | 730f8b292d4e27d11762e1e1412b565d |
|
BLAKE2b-256 | a211c7bf9928ec75ff6536caf70a9c94d3b6a91a15542aa8cc580bf234401338 |
Hashes for toon-0.15.9-cp36-cp36m-macosx_10_9_x86_64.whl
Algorithm | Hash digest | |
---|---|---|
SHA256 | f778202edc63afd7b874a0a42d0d7510ae7c802e6da177923deda9909becc64d |
|
MD5 | bb010847d9a72a0ed5fa4dd3b677000e |
|
BLAKE2b-256 | 3162b67261ef92e5b3878a51ebbaba3e1253e30f40f4cd7cef8ddbd5328866e8 |