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 animating things.
Everything should work on Windows/Mac/Linux.
See requirements.txt for dependencies.
Install
Current release:
pip install toon
Development version:
pip install git+https://github.com/aforren1/toon
For full install (including dependencies of included devices):
pip install toon[full]
See setup.py for a list of those dependencies, as well as device-specific subdivisions.
See the demos/
folder for usage examples.
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.py
for an example of measuring read performance.
Typical use looks like:
from toon.input import MpDevice
from toon.input.mouse import Mouse
# NB: pass the *class*, not an object
device = MpDevice(Mouse)
with device:
data = device.read()
# alternatively, unpack
# clicks, pos, scroll = device.read()
if data.pos is not None:
# N-D array of data (0th dim is time)
print(data.pos)
# time is 1D array of timestamps
print(data.pos.time)
print(data.pos[-1].time) # most recent timestamp
Creating a custom device is relatively straightforward, though there are a few boxes to check.
from toon.input import BaseDevice, Obs
from ctypes import c_double
# Obs is a class that manages observations
class MyDevice(BaseDevice):
# optional: give a hint for the buffer size
sampling_frequency = 500
# required: each data source gets its own Obs
# this should be defined in the scope of the device
class Pos(Obs):
shape = (3,)
ctype = float # python type, numpy dtype, or ctype
# can have multiple Obs per device
class RotMat(Obs):
shape = (3, 3)
ctype = c_double
# optional: prefer starting communication in __enter__
def __init__(self, **kwargs):
# kwargs are passed in through MpDevice
pass
# optional: configure the device, start communicating
def __enter__(self):
# remember to return self, or else the context manager won't work
return self
# optional: clean up resources, close device
def __exit__(self, *args):
pass
# required (and picky)
def read(self):
# See demos/ for examples of sharing a time source between the processes
time = self.clock()
# store new data with a timestamp
pos = self.Pos(time, (1, 2, 3))
rotmat = self.RotMat(time, [[1, 2, 3], [4, 5, 6], [7, 8, 9]])
# self.Returns is a dynamically-created named tuple
# names derived from any Obs defined in the Device
# prefer using keyword arguments, names are alphabetically sorted!
return self.Returns(pos=pos, rotmat=rotmat)
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 a device only has a single
Obs
,MpDevice
returns a singleTsArray
(a numpy array with atime
attribute). Otherwise,MpDevice
returns a named tuple of observations, where the names are alphabetically-sorted, lowercased versions of the pre-definedObs
. - If the data returned by a single read is scalar (e.g. a 1D force sensor),
MpDevice
will drop the 1st dimension. - If there's no data for a given observation,
None
is returned. The named tuple has a method for checking all members at once (data.any()
).
Other notes:
- The returned data is a view of the local copy of the data.
toon.input.TsArray
s have acopy
method, which may be useful if e.g. appending to a list for later concatenation. - If receiving batches of data when
read()
ing from the device, you can return a list ofReturns
(seetests/input/mockdevices.py
for an example) - Can optionally use
device.start()
/device.stop()
instead of a context manager - Can check for remote errors at any point using
device.check_error()
, though this automatically happens immediately after entering the context manager and whenread()
ing.
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 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 easings available
from toon.anim.easing import linear, elastic_in_out
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_out)
player = Player()
# 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 = []
while default_timer() < t0 + 3.2:
player.advance(default_timer())
vals.append([circle.x, circle.y])
sleep(1/60)
plt.plot(vals)
plt.show()
Other notes:
- Non-numeric attributes, like color strings, can also be modified in this framework (easing is ignored).
- The
Timeline
class (intoon.anim
) can be used to get the time between frames, or the time since some origin time, taken attimeline.start()
. - The
Player
can also be used as a mixin, in which case theobj
argument can be omitted fromplayer.add()
(see thedemos/
folder for examples). - Multiple objects can be modified simultaneously by feeding a list of objects into
player.add()
.
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 Distribution
Hashes for toon-0.11.4-py2.py3-none-any.whl
Algorithm | Hash digest | |
---|---|---|
SHA256 | d614ee475d8d21226594eb02c778b6facb5f2f4968c61d03881cbda6bad0d5a2 |
|
MD5 | dc4a1feddc6717a6148359b0355d7b4e |
|
BLAKE2b-256 | 58385422f2d6219dd5e2b2c89b5ac3fa0b3be7ee75f9b99aaafc82f5cf827a33 |