dist
dummy_py
*.pyc
+*.c
+*.so
pycomedi.egg-info
pycomedi/*.pyc
.. [#clb] In the `wtk overlay`_.
-TODO: Migrate from SWIG bindings to direct calls on comedilib via Cython.
-
Installing by hand
------------------
$ nosetests --with-doctest --doctest-extension=txt doc
-Internal unit tests with::
+Run both integration tests and internal unit tests with::
- $ nosetests --with-doctest pycomedi
+ $ ./test.sh
Licence
Import required modules.
->>> from pycomedi.classes import Device, DataChannel
->>> from pycomedi.constants import SUBDEVICE_TYPE, AREF, UNIT
+>>> from pycomedi.device import Device
+>>> from pycomedi.channel import AnalogChannel
+>>> from pycomedi.constant import SUBDEVICE_TYPE, AREF, UNIT
Open a device.
Generate a list of channels you wish to control.
->>> channels = [subdevice.channel(i, factory=DataChannel, aref=AREF.diff)
+>>> channels = [subdevice.channel(i, factory=AnalogChannel, aref=AREF.diff)
... for i in (0, 1, 2, 3)]
Configure the channels.
>>> value = [c.data_read_delayed(nano_sec=1e3) for c in channels]
>>> value # doctest: +SKIP
-[0, 9634, 0, 15083]
+[65535L, 23424L, 0L, 0L]
-TODO: convert to physical values.
+Use a converter to convert these to physical values
+
+>>> converters = [c.get_converter() for c in channels]
+>>> [c.to_physical(v) for c,v in zip(converters, value)] # doctest: +SKIP
+[5.0, 1.787136644541085, 0.0, 0.0]
+>>> [c.range.unit for c in channels]
+[<_NamedInt volt>, <_NamedInt volt>, <_NamedInt volt>, <_NamedInt volt>]
Close the device when you're done.
>>> device.open()
>>> ai_subdevice = device.find_subdevice_by_type(SUBDEVICE_TYPE.ai)
>>> ao_subdevice = device.find_subdevice_by_type(SUBDEVICE_TYPE.ao)
->>> ai_channel = ai_subdevice.channel(0, factory=DataChannel, aref=AREF.diff)
->>> ao_channel = ao_subdevice.channel(0, factory=DataChannel, aref=AREF.diff)
->>> ai_channel.range = ai_channel.find_range(unit=UNIT.volt, min=0, max=5)
->>> ao_channel.range = ao_channel.find_range(unit=UNIT.volt, min=0, max=5)
+>>> ai_channel = ai_subdevice.channel(0, factory=AnalogChannel, aref=AREF.diff)
+>>> ao_channel = ao_subdevice.channel(0, factory=AnalogChannel, aref=AREF.diff)
+>>> ai_channel.range = ai_channel.find_range(unit=UNIT.volt, min=0, max=10)
+>>> ai_channel.range
+<Range unit:volt min:0.0 max:10.0>
+>>> ao_channel.range = ao_channel.find_range(unit=UNIT.volt, min=0, max=10)
+>>> ao_channel.range
+<Range unit:volt min:0.0 max:10.0>
>>> ao_maxdata = ao_channel.get_maxdata()
>>> ai_maxdata = ai_channel.get_maxdata()
>>> ao_start = 0.3 * ao_maxdata
>>> ao_data = linspace(ao_start, ao_stop, points)
>>> ai_data = []
>>> for i in range(points):
-... written = ao_channel.data_write(ao_data[i])
-... assert written == 1, written
-... ai_data.append(ai_channel.data_read_delayed(nano_sec=1e3))
+... ao_channel.data_write(ao_data[i])
+... ai_data.append(ai_channel.data_read_delayed(nano_sec=10e3))
+>>> ao_converter = ao_channel.get_converter()
+>>> ai_converter = ai_channel.get_converter()
+>>> ao_data = ao_converter.to_physical(ao_data)
+>>> ai_data = ao_converter.to_physical(ai_data)
>>> ao_data
->>> ai_data
+array([ 2.9999237 , 3.44441901, 3.88876173, 4.33325704, 4.77775235,
+ 5.22209506, 5.66659037, 6.11108568, 6.5554284 , 6.9999237 ])
+>>> ai_data # doctest: +SKIP
+array([ 3.0156405 , 3.46089876, 3.90630961, 4.35156786, 4.79682612,
+ 5.24238956, 5.687953 , 6.13351644, 6.57862211, 7.02433814])
>>> scaled_ao_data = [d/float(ao_maxdata) for d in ao_data]
>>> scaled_ai_data = [d/float(ai_maxdata) for d in ai_data]
>>> gradient,intercept,r_value,p_value,std_err = linregress(
... scaled_ao_data, scaled_ai_data)
->>> gradient
->>> intercept
->>> r_value
->>> p_value
->>> std_err
+>>> abs(gradient - 1.0) < 0.01
+True
+>>> abs(intercept) < 0.001
+True
+>>> r_value > 0.999
+True
+>>> p_value < 1e-14
+True
+>>> std_err < 1e-4
+True
>>> device.close()
Import required modules.
->>> from pycomedi.classes import Device, DataChannel
->>> from pycomedi.constants import SUBDEVICE_TYPE, IO_DIRECTION
+>>> from pycomedi.device import Device
+>>> from pycomedi.channel import DigitalChannel
+>>> from pycomedi.constant import SUBDEVICE_TYPE, IO_DIRECTION
Open a device.
Generate a list of channels you wish to control.
->>> channels = [subdevice.channel(i, factory=DataChannel)
+>>> channels = [subdevice.channel(i, factory=DigitalChannel)
... for i in (0, 1, 2, 3)]
Configure the channels.
>>> value = subdevice.dio_bitfield(base_channel=0)
>>> value # doctest: +SKIP
-7
+255L
... or sequentially.
>>> value = [c.dio_read() for c in channels]
>>> value # doctest: +SKIP
-[1, 1, 1, 0]
+[1, 1, 1, 1]
Close the device when you're done.
Import required modules.
->>> from numpy import arange, linspace, zeros, sin, pi
->>> from pycomedi.classes import (Device, StreamingSubdevice, DataChannel,
-... Chanlist)
->>> from pycomedi.constants import (AREF, CMDF, INSN, SUBDEVICE_TYPE, TRIG_SRC,
+>>> from numpy import arange, linspace, zeros, sin, pi, float32
+>>> from pycomedi.device import Device
+>>> from pycomedi.subdevice import StreamingSubdevice
+>>> from pycomedi.channel import AnalogChannel
+>>> from pycomedi.chanspec import ChanSpec
+>>> from pycomedi.constant import (AREF, CMDF, INSN, SUBDEVICE_TYPE, TRIG_SRC,
... UNIT)
->>> from pycomedi.utility import inttrig_insn, subdevice_dtype, Reader, Writer
+>>> from pycomedi.utility import inttrig_insn, Reader, Writer
Open a device.
Generate a list of channels you wish to control.
->>> ai_channels = Chanlist(
-... [ai_subdevice.channel(i, factory=DataChannel, aref=AREF.diff)
-... for i in (0, 1, 2, 3)])
->>> ao_channels = Chanlist(
-... [ao_subdevice.channel(i, factory=DataChannel, aref=AREF.diff)
-... for i in (0, 1)])
+>>> ai_channels = [
+... ai_subdevice.channel(i, factory=AnalogChannel, aref=AREF.diff)
+... for i in (0, 1, 2, 3)]
+>>> ao_channels = [
+... ao_subdevice.channel(i, factory=AnalogChannel, aref=AREF.diff)
+... for i in (0, 1)]
Configure the channels.
Use the subdevice flags to determine data types.
->>> ai_dtype = subdevice_dtype(ai_subdevice)
->>> ao_dtype = subdevice_dtype(ao_subdevice)
+>>> ai_dtype = ai_subdevice.get_dtype()
+>>> ao_dtype = ao_subdevice.get_dtype()
Allocate buffers (with channel data interleaved).
>>> frequency = 1000.0 # Hz
>>> scan_period_ns = int(1e9 / frequency) # nanosecond period
->>> ai_subdevice.get_cmd_generic_timed(len(ai_channels), scan_period_ns)
->>> ai_cmd = ai_subdevice.cmd
->>> ao_subdevice.get_cmd_generic_timed(len(ao_channels), scan_period_ns)
->>> ao_cmd = ao_subdevice.cmd
+>>> ai_cmd = ai_subdevice.get_cmd_generic_timed(
+... len(ai_channels), scan_period_ns)
+>>> ao_cmd = ao_subdevice.get_cmd_generic_timed(
+... len(ao_channels), scan_period_ns)
Setup multi-scan run triggering.
>>> ao_cmd.stop_src = TRIG_SRC.count
>>> ao_cmd.stop_arg = n_samps
-Setup multi-channel scan triggering (handled by get_cmd_generic_timed?).
-
-ai_cmd.scan_begin_src = TRIG_SRC.timer
-ai_cmd.scan_begin_arg
-ai_cmd.convert_src
-ai_cmd.convert_arg
-ai_cmd.scan_end_src
-ai_cmd.scan_end_arg
-
Add channel lists.
>>> ai_cmd.chanlist = ai_channels
>>> reader.join()
>>> ai_buffer # doctest: +SKIP
+array([[32669, 27117, 24827, 23111],
+ [33711, 27680, 25273, 23453],
+...
+ [31627, 24590, 22482, 22045],
+ [32668, 25381, 22937, 22189]], dtype=uint16)
+
array([[32342, 31572, 30745, 31926],
[33376, 31797, 30904, 31761],
...
[31308, 24246, 22215, 21824],
[32343, 25044, 22659, 21959]], dtype=uint16)
+Use a converter to convert these to physical values
+
+>>> converters = [c.get_converter() for c in ai_channels]
+>>> ai_physical = zeros(ai_buffer.shape, dtype=float32)
+>>> for i,c in enumerate(converters):
+... ai_physical[:,i] = c.to_physical(ai_buffer[:,i])
+>>> ai_physical # doctest: +SKIP
+array([[ -3.00602727e-02, -1.72442210e+00, -2.42328525e+00,
+ -2.94697499e+00],
+ [ 2.87937731e-01, -1.55260551e+00, -2.28717470e+00,
+ -2.84260321e+00],
+...
+ [ -3.48058283e-01, -2.49561310e+00, -3.13893342e+00,
+ -3.27229714e+00],
+ [ -3.03654540e-02, -2.25421524e+00, -3.00007629e+00,
+ -3.22835135e+00]], dtype=float32)
+>>> [c.range.unit for c in ai_channels]
+[<_NamedInt volt>, <_NamedInt volt>, <_NamedInt volt>, <_NamedInt volt>]
+
Close the device when you're done.
>>> device.close()
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
+"A Pythonic wrapper around Comedilib"
+
import logging as _logging
--- /dev/null
+# Copyright
+
+"Cython interface to comedi.h"
+
+
+cdef extern from 'comedi.h':
+ # From http://docs.cython.org/src/userguide/external_C_code.html
+ # If the header file uses macros to define constants, translate
+ # them into a dummy enum declaration.
+ enum: COMEDI_MAJOR
+ #"comedi's major device number"
+
+ enum: COMEDI_NDEVICES
+ #"""maximum number of minor devices.
+ #
+ #This can be increased, although kernel structures are currently
+ #statically allocated, thus you don't want this to be much more
+ #than you actually use.
+ #"""
+
+ enum: COMEDI_NDEVCONFOPTS
+ #"number of config options in the config structure"
+
+ # length of nth chunk of firmware data
+ enum: COMEDI_DEVCONF_AUX_DATA3_LENGTH
+ enum: COMEDI_DEVCONF_AUX_DATA2_LENGTH
+ enum: COMEDI_DEVCONF_AUX_DATA1_LENGTH
+ enum: COMEDI_DEVCONF_AUX_DATA0_LENGTH
+ enum: COMEDI_DEVCONF_AUX_DATA_HI
+ #"most significant 32 bits of pointer address (if needed)"
+ enum: COMEDI_DEVCONF_AUX_DATA_LO
+ #"least significant 32 bits of pointer address"
+ enum: COMEDI_DEVCONF_AUX_DATA_LENGTH
+ #"total data length"
+
+ enum: COMEDI_NAMELEN
+ #"max length of device and driver names"
+
+ ctypedef unsigned int lsampl_t
+ ctypedef unsigned short sampl_t
+
+ # CR_PACK and CR_PACK_FLAGS replaced by .chanspec.Chanspec
+
+ enum: CR_FLAGS_MASK
+ enum: CR_ALT_FILTER
+ enum: CR_DITHER
+ enum: CR_DEGLITCH
+ enum: CR_ALT_SOURCE
+ enum: CR_EDGE
+ enum: CR_INVERT
+
+ enum: AREF_GROUND
+ #"analog ref = analog ground"
+ enum: AREF_COMMON
+ #"analog ref = analog common"
+ enum: AREF_DIFF
+ #"analog ref = differential"
+ enum: AREF_OTHER
+ #"analog ref = other (undefined)"
+
+# /* counters -- these are arbitrary values */
+# enum: GPCT_RESET 0x0001
+# enum: GPCT_SET_SOURCE 0x0002
+# enum: GPCT_SET_GATE 0x0004
+# enum: GPCT_SET_DIRECTION 0x0008
+# enum: GPCT_SET_OPERATION 0x0010
+# enum: GPCT_ARM 0x0020
+# enum: GPCT_DISARM 0x0040
+# enum: GPCT_GET_INT_CLK_FRQ 0x0080
+#
+# enum: GPCT_INT_CLOCK 0x0001
+# enum: GPCT_EXT_PIN 0x0002
+# enum: GPCT_NO_GATE 0x0004
+# enum: GPCT_UP 0x0008
+# enum: GPCT_DOWN 0x0010
+# enum: GPCT_HWUD 0x0020
+# enum: GPCT_SIMPLE_EVENT 0x0040
+# enum: GPCT_SINGLE_PERIOD 0x0080
+# enum: GPCT_SINGLE_PW 0x0100
+# enum: GPCT_CONT_PULSE_OUT 0x0200
+# enum: GPCT_SINGLE_PULSE_OUT 0x0400
+#
+# /* instructions */
+#
+# enum: INSN_MASK_WRITE 0x8000000
+# enum: INSN_MASK_READ 0x4000000
+# enum: INSN_MASK_SPECIAL 0x2000000
+#
+# enum: INSN_READ ( 0 | INSN_MASK_READ)
+# enum: INSN_WRITE ( 1 | INSN_MASK_WRITE)
+# enum: INSN_BITS ( 2 | INSN_MASK_READ|INSN_MASK_WRITE)
+# enum: INSN_CONFIG ( 3 | INSN_MASK_READ|INSN_MASK_WRITE)
+# enum: INSN_GTOD ( 4 | INSN_MASK_READ|INSN_MASK_SPECIAL)
+# enum: INSN_WAIT ( 5 | INSN_MASK_WRITE|INSN_MASK_SPECIAL)
+# enum: INSN_INTTRIG ( 6 | INSN_MASK_WRITE|INSN_MASK_SPECIAL)
+#
+# /* trigger flags */
+# /* These flags are used in comedi_trig structures */
+#
+# enum: TRIG_BOGUS 0x0001 /* do the motions */
+# enum: TRIG_DITHER 0x0002 /* enable dithering */
+# enum: TRIG_DEGLITCH 0x0004 /* enable deglitching */
+# //enum: TRIG_RT 0x0008 /* perform op in real time */
+# enum: TRIG_CONFIG 0x0010 /* perform configuration, not triggering */
+# enum: TRIG_WAKE_EOS 0x0020 /* wake up on end-of-scan events */
+# //enum: TRIG_WRITE 0x0040 /* write to bidirectional devices */
+#
+# /* command flags */
+# /* These flags are used in comedi_cmd structures */
+#
+# enum: CMDF_PRIORITY 0x00000008 /* try to use a real-time interrupt while performing command */
+#
+# enum: TRIG_RT CMDF_PRIORITY /* compatibility definition */
+#
+# enum: CMDF_WRITE 0x00000040
+# enum: TRIG_WRITE CMDF_WRITE /* compatibility definition */
+#
+# enum: CMDF_RAWDATA 0x00000080
+#
+# enum: COMEDI_EV_START 0x00040000
+# enum: COMEDI_EV_SCAN_BEGIN 0x00080000
+# enum: COMEDI_EV_CONVERT 0x00100000
+# enum: COMEDI_EV_SCAN_END 0x00200000
+# enum: COMEDI_EV_STOP 0x00400000
+#
+# enum: TRIG_ROUND_MASK 0x00030000
+# enum: TRIG_ROUND_NEAREST 0x00000000
+# enum: TRIG_ROUND_DOWN 0x00010000
+# enum: TRIG_ROUND_UP 0x00020000
+# enum: TRIG_ROUND_UP_NEXT 0x00030000
+#
+# /* trigger sources */
+#
+# enum: TRIG_ANY 0xffffffff
+# enum: TRIG_INVALID 0x00000000
+#
+# enum: TRIG_NONE 0x00000001 /* never trigger */
+# enum: TRIG_NOW 0x00000002 /* trigger now + N ns */
+# enum: TRIG_FOLLOW 0x00000004 /* trigger on next lower level trig */
+# enum: TRIG_TIME 0x00000008 /* trigger at time N ns */
+# enum: TRIG_TIMER 0x00000010 /* trigger at rate N ns */
+# enum: TRIG_COUNT 0x00000020 /* trigger when count reaches N */
+# enum: TRIG_EXT 0x00000040 /* trigger on external signal N */
+# enum: TRIG_INT 0x00000080 /* trigger on comedi-internal signal N */
+# enum: TRIG_OTHER 0x00000100 /* driver defined */
+#
+# /* subdevice flags */
+#
+# enum: SDF_BUSY 0x0001 /* device is busy */
+# enum: SDF_BUSY_OWNER 0x0002 /* device is busy with your job */
+# enum: SDF_LOCKED 0x0004 /* subdevice is locked */
+# enum: SDF_LOCK_OWNER 0x0008 /* you own lock */
+# enum: SDF_MAXDATA 0x0010 /* maxdata depends on channel */
+# enum: SDF_FLAGS 0x0020 /* flags depend on channel */
+# enum: SDF_RANGETYPE 0x0040 /* range type depends on channel */
+# enum: SDF_MODE0 0x0080 /* can do mode 0 */
+# enum: SDF_MODE1 0x0100 /* can do mode 1 */
+# enum: SDF_MODE2 0x0200 /* can do mode 2 */
+# enum: SDF_MODE3 0x0400 /* can do mode 3 */
+# enum: SDF_MODE4 0x0800 /* can do mode 4 */
+# enum: SDF_CMD 0x1000 /* can do commands (deprecated) */
+# enum: SDF_SOFT_CALIBRATED 0x2000 /* subdevice uses software calibration */
+# enum: SDF_CMD_WRITE 0x4000 /* can do output commands */
+# enum: SDF_CMD_READ 0x8000 /* can do input commands */
+#
+# enum: SDF_READABLE 0x00010000 /* subdevice can be read (e.g. analog input) */
+# enum: SDF_WRITABLE 0x00020000 /* subdevice can be written (e.g. analog output) */
+# enum: SDF_WRITEABLE SDF_WRITABLE /* spelling error in API */
+# enum: SDF_INTERNAL 0x00040000 /* subdevice does not have externally visible lines */
+# enum: SDF_RT 0x00080000 /* DEPRECATED: subdevice is RT capable */
+# enum: SDF_GROUND 0x00100000 /* can do aref=ground */
+# enum: SDF_COMMON 0x00200000 /* can do aref=common */
+# enum: SDF_DIFF 0x00400000 /* can do aref=diff */
+# enum: SDF_OTHER 0x00800000 /* can do aref=other */
+# enum: SDF_DITHER 0x01000000 /* can do dithering */
+# enum: SDF_DEGLITCH 0x02000000 /* can do deglitching */
+# enum: SDF_MMAP 0x04000000 /* can do mmap() */
+# enum: SDF_RUNNING 0x08000000 /* subdevice is acquiring data */
+# enum: SDF_LSAMPL 0x10000000 /* subdevice uses 32-bit samples */
+# enum: SDF_PACKED 0x20000000 /* subdevice can do packed DIO */
+# /* re recyle these flags for PWM */
+# enum: SDF_PWM_COUNTER SDF_MODE0 /* PWM can automatically switch off */
+# enum: SDF_PWM_HBRIDGE SDF_MODE1 /* PWM is signed (H-bridge) */
+#
+#
+#
+# /* subdevice types */
+#
+ enum comedi_subdevice_type:
+ COMEDI_SUBD_UNUSED, # unused by driver
+ COMEDI_SUBD_AI, # analog input
+ COMEDI_SUBD_AO, # analog output
+ COMEDI_SUBD_DI, # digital input
+ COMEDI_SUBD_DO, # digital output
+ COMEDI_SUBD_DIO, # digital input/output
+ COMEDI_SUBD_COUNTER, # counter
+ COMEDI_SUBD_TIMER, # timer
+ COMEDI_SUBD_MEMORY, # memory, EEPROM, DPRAM
+ COMEDI_SUBD_CALIB, # calibration DACs
+ COMEDI_SUBD_PROC, # processor, DSP
+ COMEDI_SUBD_SERIAL, # serial IO
+ COMEDI_SUBD_PWM # PWM
+
+# /* configuration instructions */
+#
+# enum configuration_ids {
+# INSN_CONFIG_DIO_INPUT = 0,
+# INSN_CONFIG_DIO_OUTPUT = 1,
+# INSN_CONFIG_DIO_OPENDRAIN = 2,
+# INSN_CONFIG_ANALOG_TRIG = 16,
+# // INSN_CONFIG_WAVEFORM = 17,
+# // INSN_CONFIG_TRIG = 18,
+# // INSN_CONFIG_COUNTER = 19,
+# INSN_CONFIG_ALT_SOURCE = 20,
+# INSN_CONFIG_DIGITAL_TRIG = 21,
+# INSN_CONFIG_BLOCK_SIZE = 22,
+# INSN_CONFIG_TIMER_1 = 23,
+# INSN_CONFIG_FILTER = 24,
+# INSN_CONFIG_CHANGE_NOTIFY = 25,
+#
+# /*ALPHA*/ INSN_CONFIG_SERIAL_CLOCK = 26,
+# INSN_CONFIG_BIDIRECTIONAL_DATA = 27,
+# INSN_CONFIG_DIO_QUERY = 28,
+# INSN_CONFIG_PWM_OUTPUT = 29,
+# INSN_CONFIG_GET_PWM_OUTPUT = 30,
+# INSN_CONFIG_ARM = 31,
+# INSN_CONFIG_DISARM = 32,
+# INSN_CONFIG_GET_COUNTER_STATUS = 33,
+# INSN_CONFIG_RESET = 34,
+# INSN_CONFIG_GPCT_SINGLE_PULSE_GENERATOR = 1001, // Use CTR as single pulsegenerator
+# INSN_CONFIG_GPCT_PULSE_TRAIN_GENERATOR = 1002, // Use CTR as pulsetraingenerator
+# INSN_CONFIG_GPCT_QUADRATURE_ENCODER = 1003, // Use the counter as encoder
+# INSN_CONFIG_SET_GATE_SRC = 2001, // Set gate source
+# INSN_CONFIG_GET_GATE_SRC = 2002, // Get gate source
+# INSN_CONFIG_SET_CLOCK_SRC = 2003, // Set master clock source
+# INSN_CONFIG_GET_CLOCK_SRC = 2004, // Get master clock source
+# INSN_CONFIG_SET_OTHER_SRC = 2005, // Set other source
+# // INSN_CONFIG_GET_OTHER_SRC = 2006, // Get other source
+# INSN_CONFIG_GET_HARDWARE_BUFFER_SIZE = 2006, // Get size in bytes of subdevice's on-board fifos used during streaming input/output
+# INSN_CONFIG_SET_COUNTER_MODE = 4097,
+# INSN_CONFIG_8254_SET_MODE = INSN_CONFIG_SET_COUNTER_MODE, /* deprecated */
+# INSN_CONFIG_8254_READ_STATUS = 4098,
+# INSN_CONFIG_SET_ROUTING = 4099,
+# INSN_CONFIG_GET_ROUTING = 4109,
+# /* PWM */
+# INSN_CONFIG_PWM_SET_PERIOD = 5000, /* sets frequency */
+# INSN_CONFIG_PWM_GET_PERIOD = 5001, /* gets frequency */
+# INSN_CONFIG_GET_PWM_STATUS = 5002, /* is it running? */
+# INSN_CONFIG_PWM_SET_H_BRIDGE = 5003, /* sets H bridge: duty cycle and sign bit for a relay at the same time*/
+# INSN_CONFIG_PWM_GET_H_BRIDGE = 5004 /* gets H bridge data: duty cycle and the sign bit */
+# };
+#
+# enum comedi_io_direction {
+# COMEDI_INPUT = 0,
+# COMEDI_OUTPUT = 1,
+# COMEDI_OPENDRAIN = 2
+# };
+#
+# enum comedi_support_level
+# {
+# COMEDI_UNKNOWN_SUPPORT = 0,
+# COMEDI_SUPPORTED,
+# COMEDI_UNSUPPORTED
+# };
+#
+# /* ioctls */
+#
+# enum: CIO 'd'
+# enum: COMEDI_DEVCONFIG _IOW(CIO,0,comedi_devconfig)
+# enum: COMEDI_DEVINFO _IOR(CIO,1,comedi_devinfo)
+# enum: COMEDI_SUBDINFO _IOR(CIO,2,comedi_subdinfo)
+# enum: COMEDI_CHANINFO _IOR(CIO,3,comedi_chaninfo)
+# enum: COMEDI_TRIG _IOWR(CIO,4,comedi_trig)
+# enum: COMEDI_LOCK _IO(CIO,5)
+# enum: COMEDI_UNLOCK _IO(CIO,6)
+# enum: COMEDI_CANCEL _IO(CIO,7)
+# enum: COMEDI_RANGEINFO _IOR(CIO,8,comedi_rangeinfo)
+# enum: COMEDI_CMD _IOR(CIO,9,comedi_cmd)
+# enum: COMEDI_CMDTEST _IOR(CIO,10,comedi_cmd)
+# enum: COMEDI_INSNLIST _IOR(CIO,11,comedi_insnlist)
+# enum: COMEDI_INSN _IOR(CIO,12,comedi_insn)
+# enum: COMEDI_BUFCONFIG _IOR(CIO,13,comedi_bufconfig)
+# enum: COMEDI_BUFINFO _IOWR(CIO,14,comedi_bufinfo)
+# enum: COMEDI_POLL _IO(CIO,15)
+#
+ # structures
+
+ ctypedef struct comedi_cmd:
+ unsigned int subdev
+ unsigned int flags
+ unsigned int start_src
+ unsigned int start_arg
+ unsigned int scan_begin_src
+ unsigned int scan_begin_arg
+ unsigned int convert_src
+ unsigned int convert_arg
+ unsigned int scan_end_src
+ unsigned int scan_end_arg
+ unsigned int stop_src
+ unsigned int stop_arg
+ unsigned int *chanlist # channel/range list
+ unsigned int chanlist_len
+ sampl_t *data # data list, size depends on subd flags
+ unsigned int data_len
+
+ ctypedef struct comedi_insn:
+ unsigned int insn
+ unsigned int n
+ lsampl_t *data
+ unsigned int subdev
+ unsigned int chanspec
+ unsigned int unused[3]
+
+ ctypedef struct comedi_insnlist:
+ unsigned int n_insns
+ comedi_insn *insns
+
+ ctypedef struct comedi_chaninfo:
+ unsigned int subdev
+ lsampl_t *maxdata_list
+ unsigned int *flaglist
+ unsigned int *rangelist
+ unsigned int unused[4]
+
+ ctypedef struct comedi_subdinfo:
+ unsigned int type
+ unsigned int n_chan
+ unsigned int subd_flags
+ unsigned int timer_type
+ unsigned int len_chanlist
+ lsampl_t maxdata
+ unsigned int flags # channel flags
+ unsigned int range_type # lookup in kernel
+ unsigned int settling_time_0
+ unsigned insn_bits_support # see support_level enum for values
+ unsigned int unused[8]
+
+ ctypedef struct comedi_devinfo:
+ unsigned int version_code
+ unsigned int n_subdevs
+ char driver_name[COMEDI_NAMELEN]
+ char board_name[COMEDI_NAMELEN]
+ int read_subdevice
+ int write_subdevice
+ int unused[30]
+
+ ctypedef struct comedi_devconfig:
+ char board_name[COMEDI_NAMELEN]
+ int options[COMEDI_NDEVCONFOPTS]
+
+ ctypedef struct comedi_rangeinfo:
+ unsigned int range_type
+ void *range_ptr
+
+ ctypedef struct comedi_krange:
+ int min # fixed point, multiply by 1e-6
+ int max # fixed point, multiply by 1e-6
+ unsigned int flags
+
+ ctypedef struct comedi_bufconfig:
+ unsigned int subdevice
+ unsigned int flags
+ unsigned int maximum_size
+ unsigned int size
+ unsigned int unused[4]
+
+ ctypedef struct comedi_bufinfo:
+ unsigned int subdevice
+ unsigned int bytes_read
+ unsigned int buf_write_ptr
+ unsigned int buf_read_ptr
+ unsigned int buf_write_count
+ unsigned int buf_read_count
+ unsigned int bytes_written
+ unsigned int unused[4]
+
+ struct comedi_trig:
+ unsigned int subdev # subdevice
+ unsigned int mode # mode
+ unsigned int flags
+ unsigned int n_chan # number of channels
+ unsigned int *chanlist # channel/range list
+ sampl_t *data # data list, size depends on subd flags
+ unsigned int n # number of scans
+ unsigned int trigsrc
+ unsigned int trigvar
+ unsigned int trigvar1
+ unsigned int data_len
+ unsigned int unused[3]
+
+# /* range stuff */
+#
+# enum: __RANGE(a,b) ((((a)&0xffff)<<16)|((b)&0xffff))
+#
+# enum: RANGE_OFFSET(a) (((a)>>16)&0xffff)
+# enum: RANGE_LENGTH(b) ((b)&0xffff)
+#
+# enum: RF_UNIT(flags) ((flags)&0xff)
+# enum: RF_EXTERNAL (1<<8)
+#
+# enum: UNIT_volt 0
+# enum: UNIT_mA 1
+# enum: UNIT_none 2
+#
+# enum: COMEDI_MIN_SPEED ((unsigned int)0xffffffff)
+#
+# /* callback stuff */
+# /* only relevant to kernel modules. */
+#
+# enum: COMEDI_CB_EOS 1 /* end of scan */
+# enum: COMEDI_CB_EOA 2 /* end of acquisition */
+# enum: COMEDI_CB_BLOCK 4 /* new data has arrived: wakes up write()/read() */
+# enum: COMEDI_CB_EOBUF 8 /* DEPRECATED: end of buffer */
+# enum: COMEDI_CB_ERROR 16 /* card error during acquisition */
+# enum: COMEDI_CB_OVERFLOW 32 /* buffer overflow/underflow */
+#
+# /**********************************************************/
+# /* everything after this line is ALPHA */
+# /**********************************************************/
+#
+# /*
+# 8254 specific configuration.
+#
+# It supports two config commands:
+#
+# 0 ID: INSN_CONFIG_SET_COUNTER_MODE
+# 1 8254 Mode
+# I8254_MODE0, I8254_MODE1, ..., I8254_MODE5
+# OR'ed with:
+# I8254_BCD, I8254_BINARY
+#
+# 0 ID: INSN_CONFIG_8254_READ_STATUS
+# 1 <-- Status byte returned here.
+# B7=Output
+# B6=NULL Count
+# B5-B0 Current mode.
+#
+# */
+#
+# enum i8254_mode {
+# I8254_MODE0 = (0 << 1), /* Interrupt on terminal count */
+# I8254_MODE1 = (1 << 1), /* Hardware retriggerable one-shot */
+# I8254_MODE2 = (2 << 1), /* Rate generator */
+# I8254_MODE3 = (3 << 1), /* Square wave mode */
+# I8254_MODE4 = (4 << 1), /* Software triggered strobe */
+# I8254_MODE5 = (5 << 1), /* Hardware triggered strobe (retriggerable) */
+# I8254_BCD = 1, /* use binary-coded decimal instead of binary (pretty useless) */
+# I8254_BINARY = 0
+# };
+#
+# static inline unsigned NI_USUAL_PFI_SELECT(unsigned pfi_channel) {
+# if (pfi_channel < 10)
+# return 0x1 + pfi_channel;
+# else
+# return 0xb + pfi_channel;
+# } static inline unsigned NI_USUAL_RTSI_SELECT(unsigned rtsi_channel) {
+# if (rtsi_channel < 7)
+# return 0xb + rtsi_channel;
+# else
+# return 0x1b;
+# }
+# /* mode bits for NI general-purpose counters, set with INSN_CONFIG_SET_COUNTER_MODE */
+# enum: NI_GPCT_COUNTING_MODE_SHIFT 16
+# enum: NI_GPCT_INDEX_PHASE_BITSHIFT 20
+# enum: NI_GPCT_COUNTING_DIRECTION_SHIFT 24
+# enum ni_gpct_mode_bits {
+# NI_GPCT_GATE_ON_BOTH_EDGES_BIT = 0x4,
+# NI_GPCT_EDGE_GATE_MODE_MASK = 0x18,
+# NI_GPCT_EDGE_GATE_STARTS_STOPS_BITS = 0x0,
+# NI_GPCT_EDGE_GATE_STOPS_STARTS_BITS = 0x8,
+# NI_GPCT_EDGE_GATE_STARTS_BITS = 0x10,
+# NI_GPCT_EDGE_GATE_NO_STARTS_NO_STOPS_BITS = 0x18,
+# NI_GPCT_STOP_MODE_MASK = 0x60,
+# NI_GPCT_STOP_ON_GATE_BITS = 0x00,
+# NI_GPCT_STOP_ON_GATE_OR_TC_BITS = 0x20,
+# NI_GPCT_STOP_ON_GATE_OR_SECOND_TC_BITS = 0x40,
+# NI_GPCT_LOAD_B_SELECT_BIT = 0x80,
+# NI_GPCT_OUTPUT_MODE_MASK = 0x300,
+# NI_GPCT_OUTPUT_TC_PULSE_BITS = 0x100,
+# NI_GPCT_OUTPUT_TC_TOGGLE_BITS = 0x200,
+# NI_GPCT_OUTPUT_TC_OR_GATE_TOGGLE_BITS = 0x300,
+# NI_GPCT_HARDWARE_DISARM_MASK = 0xc00,
+# NI_GPCT_NO_HARDWARE_DISARM_BITS = 0x000,
+# NI_GPCT_DISARM_AT_TC_BITS = 0x400,
+# NI_GPCT_DISARM_AT_GATE_BITS = 0x800,
+# NI_GPCT_DISARM_AT_TC_OR_GATE_BITS = 0xc00,
+# NI_GPCT_LOADING_ON_TC_BIT = 0x1000,
+# NI_GPCT_LOADING_ON_GATE_BIT = 0x4000,
+# NI_GPCT_COUNTING_MODE_MASK = 0x7 << NI_GPCT_COUNTING_MODE_SHIFT,
+# NI_GPCT_COUNTING_MODE_NORMAL_BITS =
+# 0x0 << NI_GPCT_COUNTING_MODE_SHIFT,
+# NI_GPCT_COUNTING_MODE_QUADRATURE_X1_BITS =
+# 0x1 << NI_GPCT_COUNTING_MODE_SHIFT,
+# NI_GPCT_COUNTING_MODE_QUADRATURE_X2_BITS =
+# 0x2 << NI_GPCT_COUNTING_MODE_SHIFT,
+# NI_GPCT_COUNTING_MODE_QUADRATURE_X4_BITS =
+# 0x3 << NI_GPCT_COUNTING_MODE_SHIFT,
+# NI_GPCT_COUNTING_MODE_TWO_PULSE_BITS =
+# 0x4 << NI_GPCT_COUNTING_MODE_SHIFT,
+# NI_GPCT_COUNTING_MODE_SYNC_SOURCE_BITS =
+# 0x6 << NI_GPCT_COUNTING_MODE_SHIFT,
+# NI_GPCT_INDEX_PHASE_MASK = 0x3 << NI_GPCT_INDEX_PHASE_BITSHIFT,
+# NI_GPCT_INDEX_PHASE_LOW_A_LOW_B_BITS =
+# 0x0 << NI_GPCT_INDEX_PHASE_BITSHIFT,
+# NI_GPCT_INDEX_PHASE_LOW_A_HIGH_B_BITS =
+# 0x1 << NI_GPCT_INDEX_PHASE_BITSHIFT,
+# NI_GPCT_INDEX_PHASE_HIGH_A_LOW_B_BITS =
+# 0x2 << NI_GPCT_INDEX_PHASE_BITSHIFT,
+# NI_GPCT_INDEX_PHASE_HIGH_A_HIGH_B_BITS =
+# 0x3 << NI_GPCT_INDEX_PHASE_BITSHIFT,
+# NI_GPCT_INDEX_ENABLE_BIT = 0x400000,
+# NI_GPCT_COUNTING_DIRECTION_MASK =
+# 0x3 << NI_GPCT_COUNTING_DIRECTION_SHIFT,
+# NI_GPCT_COUNTING_DIRECTION_DOWN_BITS =
+# 0x00 << NI_GPCT_COUNTING_DIRECTION_SHIFT,
+# NI_GPCT_COUNTING_DIRECTION_UP_BITS =
+# 0x1 << NI_GPCT_COUNTING_DIRECTION_SHIFT,
+# NI_GPCT_COUNTING_DIRECTION_HW_UP_DOWN_BITS =
+# 0x2 << NI_GPCT_COUNTING_DIRECTION_SHIFT,
+# NI_GPCT_COUNTING_DIRECTION_HW_GATE_BITS =
+# 0x3 << NI_GPCT_COUNTING_DIRECTION_SHIFT,
+# NI_GPCT_RELOAD_SOURCE_MASK = 0xc000000,
+# NI_GPCT_RELOAD_SOURCE_FIXED_BITS = 0x0,
+# NI_GPCT_RELOAD_SOURCE_SWITCHING_BITS = 0x4000000,
+# NI_GPCT_RELOAD_SOURCE_GATE_SELECT_BITS = 0x8000000,
+# NI_GPCT_OR_GATE_BIT = 0x10000000,
+# NI_GPCT_INVERT_OUTPUT_BIT = 0x20000000
+# };
+#
+# /* Bits for setting a clock source with
+# * INSN_CONFIG_SET_CLOCK_SRC when using NI general-purpose counters. */
+# enum ni_gpct_clock_source_bits {
+# NI_GPCT_CLOCK_SRC_SELECT_MASK = 0x3f,
+# NI_GPCT_TIMEBASE_1_CLOCK_SRC_BITS = 0x0,
+# NI_GPCT_TIMEBASE_2_CLOCK_SRC_BITS = 0x1,
+# NI_GPCT_TIMEBASE_3_CLOCK_SRC_BITS = 0x2,
+# NI_GPCT_LOGIC_LOW_CLOCK_SRC_BITS = 0x3,
+# NI_GPCT_NEXT_GATE_CLOCK_SRC_BITS = 0x4,
+# NI_GPCT_NEXT_TC_CLOCK_SRC_BITS = 0x5,
+# NI_GPCT_SOURCE_PIN_i_CLOCK_SRC_BITS = 0x6, /* NI 660x-specific */
+# NI_GPCT_PXI10_CLOCK_SRC_BITS = 0x7,
+# NI_GPCT_PXI_STAR_TRIGGER_CLOCK_SRC_BITS = 0x8,
+# NI_GPCT_ANALOG_TRIGGER_OUT_CLOCK_SRC_BITS = 0x9,
+# NI_GPCT_PRESCALE_MODE_CLOCK_SRC_MASK = 0x30000000,
+# NI_GPCT_NO_PRESCALE_CLOCK_SRC_BITS = 0x0,
+# NI_GPCT_PRESCALE_X2_CLOCK_SRC_BITS = 0x10000000, /* divide source by 2 */
+# NI_GPCT_PRESCALE_X8_CLOCK_SRC_BITS = 0x20000000, /* divide source by 8 */
+# NI_GPCT_INVERT_CLOCK_SRC_BIT = 0x80000000
+# };
+# static inline unsigned NI_GPCT_SOURCE_PIN_CLOCK_SRC_BITS(unsigned n) { /* NI 660x-specific */
+# return 0x10 + n;
+# }
+# static inline unsigned NI_GPCT_RTSI_CLOCK_SRC_BITS(unsigned n) {
+# return 0x18 + n;
+# }
+# static inline unsigned NI_GPCT_PFI_CLOCK_SRC_BITS(unsigned n) { /* no pfi on NI 660x */
+# return 0x20 + n;
+# }
+#
+# /* Possibilities for setting a gate source with
+# INSN_CONFIG_SET_GATE_SRC when using NI general-purpose counters.
+# May be bitwise-or'd with CR_EDGE or CR_INVERT. */
+# enum ni_gpct_gate_select {
+# /* m-series gates */
+# NI_GPCT_TIMESTAMP_MUX_GATE_SELECT = 0x0,
+# NI_GPCT_AI_START2_GATE_SELECT = 0x12,
+# NI_GPCT_PXI_STAR_TRIGGER_GATE_SELECT = 0x13,
+# NI_GPCT_NEXT_OUT_GATE_SELECT = 0x14,
+# NI_GPCT_AI_START1_GATE_SELECT = 0x1c,
+# NI_GPCT_NEXT_SOURCE_GATE_SELECT = 0x1d,
+# NI_GPCT_ANALOG_TRIGGER_OUT_GATE_SELECT = 0x1e,
+# NI_GPCT_LOGIC_LOW_GATE_SELECT = 0x1f,
+# /* more gates for 660x */
+# NI_GPCT_SOURCE_PIN_i_GATE_SELECT = 0x100,
+# NI_GPCT_GATE_PIN_i_GATE_SELECT = 0x101,
+# /* more gates for 660x "second gate" */
+# NI_GPCT_UP_DOWN_PIN_i_GATE_SELECT = 0x201,
+# NI_GPCT_SELECTED_GATE_GATE_SELECT = 0x21e,
+# /* m-series "second gate" sources are unknown,
+# we should add them here with an offset of 0x300 when known. */
+# NI_GPCT_DISABLED_GATE_SELECT = 0x8000,
+# };
+# static inline unsigned NI_GPCT_GATE_PIN_GATE_SELECT(unsigned n) {
+# return 0x102 + n;
+# }
+# static inline unsigned NI_GPCT_RTSI_GATE_SELECT(unsigned n) {
+# return NI_USUAL_RTSI_SELECT(n);
+# }
+# static inline unsigned NI_GPCT_PFI_GATE_SELECT(unsigned n) {
+# return NI_USUAL_PFI_SELECT(n);
+# }
+# static inline unsigned NI_GPCT_UP_DOWN_PIN_GATE_SELECT(unsigned n) {
+# return 0x202 + n;
+# }
+#
+# /* Possibilities for setting a source with
+# INSN_CONFIG_SET_OTHER_SRC when using NI general-purpose counters. */
+# enum ni_gpct_other_index {
+# NI_GPCT_SOURCE_ENCODER_A,
+# NI_GPCT_SOURCE_ENCODER_B,
+# NI_GPCT_SOURCE_ENCODER_Z
+# };
+# enum ni_gpct_other_select {
+# /* m-series gates */
+# // Still unknown, probably only need NI_GPCT_PFI_OTHER_SELECT
+# NI_GPCT_DISABLED_OTHER_SELECT = 0x8000,
+# };
+# static inline unsigned NI_GPCT_PFI_OTHER_SELECT(unsigned n) {
+# return NI_USUAL_PFI_SELECT(n);
+# }
+#
+# /* start sources for ni general-purpose counters for use with
+# INSN_CONFIG_ARM */
+# enum ni_gpct_arm_source {
+# NI_GPCT_ARM_IMMEDIATE = 0x0,
+# NI_GPCT_ARM_PAIRED_IMMEDIATE = 0x1, /* Start both the counter and the adjacent paired counter simultaneously */
+# /* NI doesn't document bits for selecting hardware arm triggers. If
+# the NI_GPCT_ARM_UNKNOWN bit is set, we will pass the least significant
+# bits (3 bits for 660x or 5 bits for m-series) through to the hardware.
+# This will at least allow someone to figure out what the bits do later. */
+# NI_GPCT_ARM_UNKNOWN = 0x1000,
+# };
+#
+# /* digital filtering options for ni 660x for use with INSN_CONFIG_FILTER. */
+# enum ni_gpct_filter_select {
+# NI_GPCT_FILTER_OFF = 0x0,
+# NI_GPCT_FILTER_TIMEBASE_3_SYNC = 0x1,
+# NI_GPCT_FILTER_100x_TIMEBASE_1 = 0x2,
+# NI_GPCT_FILTER_20x_TIMEBASE_1 = 0x3,
+# NI_GPCT_FILTER_10x_TIMEBASE_1 = 0x4,
+# NI_GPCT_FILTER_2x_TIMEBASE_1 = 0x5,
+# NI_GPCT_FILTER_2x_TIMEBASE_3 = 0x6
+# };
+#
+# /* PFI digital filtering options for ni m-series for use with INSN_CONFIG_FILTER. */
+# enum ni_pfi_filter_select {
+# NI_PFI_FILTER_OFF = 0x0,
+# NI_PFI_FILTER_125ns = 0x1,
+# NI_PFI_FILTER_6425ns = 0x2,
+# NI_PFI_FILTER_2550us = 0x3
+# };
+#
+# /* master clock sources for ni mio boards and INSN_CONFIG_SET_CLOCK_SRC */
+# enum ni_mio_clock_source {
+# NI_MIO_INTERNAL_CLOCK = 0,
+# NI_MIO_RTSI_CLOCK = 1, /* doesn't work for m-series, use NI_MIO_PLL_RTSI_CLOCK() */
+# /* the NI_MIO_PLL_* sources are m-series only */
+# NI_MIO_PLL_PXI_STAR_TRIGGER_CLOCK = 2,
+# NI_MIO_PLL_PXI10_CLOCK = 3,
+# NI_MIO_PLL_RTSI0_CLOCK = 4
+# };
+# static inline unsigned NI_MIO_PLL_RTSI_CLOCK(unsigned rtsi_channel) {
+# return NI_MIO_PLL_RTSI0_CLOCK + rtsi_channel;
+# }
+#
+# /* Signals which can be routed to an NI RTSI pin with INSN_CONFIG_SET_ROUTING.
+# The numbers assigned are not arbitrary, they correspond to the bits required
+# to program the board. */
+# enum ni_rtsi_routing {
+# NI_RTSI_OUTPUT_ADR_START1 = 0,
+# NI_RTSI_OUTPUT_ADR_START2 = 1,
+# NI_RTSI_OUTPUT_SCLKG = 2,
+# NI_RTSI_OUTPUT_DACUPDN = 3,
+# NI_RTSI_OUTPUT_DA_START1 = 4,
+# NI_RTSI_OUTPUT_G_SRC0 = 5,
+# NI_RTSI_OUTPUT_G_GATE0 = 6,
+# NI_RTSI_OUTPUT_RGOUT0 = 7,
+# NI_RTSI_OUTPUT_RTSI_BRD_0 = 8,
+# NI_RTSI_OUTPUT_RTSI_OSC = 12 /* pre-m-series always have RTSI clock on line 7 */
+# };
+# static inline unsigned NI_RTSI_OUTPUT_RTSI_BRD(unsigned n) {
+# return NI_RTSI_OUTPUT_RTSI_BRD_0 + n;
+# }
+#
+# /* Signals which can be routed to an NI PFI pin on an m-series board
+# with INSN_CONFIG_SET_ROUTING. These numbers are also returned
+# by INSN_CONFIG_GET_ROUTING on pre-m-series boards, even though
+# their routing cannot be changed. The numbers assigned are
+# not arbitrary, they correspond to the bits required
+# to program the board. */
+# enum ni_pfi_routing {
+# NI_PFI_OUTPUT_PFI_DEFAULT = 0,
+# NI_PFI_OUTPUT_AI_START1 = 1,
+# NI_PFI_OUTPUT_AI_START2 = 2,
+# NI_PFI_OUTPUT_AI_CONVERT = 3,
+# NI_PFI_OUTPUT_G_SRC1 = 4,
+# NI_PFI_OUTPUT_G_GATE1 = 5,
+# NI_PFI_OUTPUT_AO_UPDATE_N = 6,
+# NI_PFI_OUTPUT_AO_START1 = 7,
+# NI_PFI_OUTPUT_AI_START_PULSE = 8,
+# NI_PFI_OUTPUT_G_SRC0 = 9,
+# NI_PFI_OUTPUT_G_GATE0 = 10,
+# NI_PFI_OUTPUT_EXT_STROBE = 11,
+# NI_PFI_OUTPUT_AI_EXT_MUX_CLK = 12,
+# NI_PFI_OUTPUT_GOUT0 = 13,
+# NI_PFI_OUTPUT_GOUT1 = 14,
+# NI_PFI_OUTPUT_FREQ_OUT = 15,
+# NI_PFI_OUTPUT_PFI_DO = 16,
+# NI_PFI_OUTPUT_I_ATRIG = 17,
+# NI_PFI_OUTPUT_RTSI0 = 18,
+# NI_PFI_OUTPUT_PXI_STAR_TRIGGER_IN = 26,
+# NI_PFI_OUTPUT_SCXI_TRIG1 = 27,
+# NI_PFI_OUTPUT_DIO_CHANGE_DETECT_RTSI = 28,
+# NI_PFI_OUTPUT_CDI_SAMPLE = 29,
+# NI_PFI_OUTPUT_CDO_UPDATE = 30
+# };
+# static inline unsigned NI_PFI_OUTPUT_RTSI(unsigned rtsi_channel) {
+# return NI_PFI_OUTPUT_RTSI0 + rtsi_channel;
+# }
+#
+# /* Signals which can be routed to output on a NI PFI pin on a 660x board
+# with INSN_CONFIG_SET_ROUTING. The numbers assigned are
+# not arbitrary, they correspond to the bits required
+# to program the board. Lines 0 to 7 can only be set to
+# NI_660X_PFI_OUTPUT_DIO. Lines 32 to 39 can only be set to
+# NI_660X_PFI_OUTPUT_COUNTER. */
+# enum ni_660x_pfi_routing {
+# NI_660X_PFI_OUTPUT_COUNTER = 1, // counter
+# NI_660X_PFI_OUTPUT_DIO = 2, // static digital output
+# };
+#
+# /* NI External Trigger lines. These values are not arbitrary, but are related to
+# the bits required to program the board (offset by 1 for historical reasons). */
+# static inline unsigned NI_EXT_PFI(unsigned pfi_channel) {
+# return NI_USUAL_PFI_SELECT(pfi_channel) - 1;
+# }
+# static inline unsigned NI_EXT_RTSI(unsigned rtsi_channel) {
+# return NI_USUAL_RTSI_SELECT(rtsi_channel) - 1;
+# }
+#
+# /* status bits for INSN_CONFIG_GET_COUNTER_STATUS */
+# enum comedi_counter_status_flags {
+# COMEDI_COUNTER_ARMED = 0x1,
+# COMEDI_COUNTER_COUNTING = 0x2,
+# COMEDI_COUNTER_TERMINAL_COUNT = 0x4,
+# };
+#
+# /* Clock sources for CDIO subdevice on NI m-series boards.
+# Used as the scan_begin_arg for a comedi_command. These
+# sources may also be bitwise-or'd with CR_INVERT to change polarity. */
+# enum ni_m_series_cdio_scan_begin_src {
+# NI_CDIO_SCAN_BEGIN_SRC_GROUND = 0,
+# NI_CDIO_SCAN_BEGIN_SRC_AI_START = 18,
+# NI_CDIO_SCAN_BEGIN_SRC_AI_CONVERT = 19,
+# NI_CDIO_SCAN_BEGIN_SRC_PXI_STAR_TRIGGER = 20,
+# NI_CDIO_SCAN_BEGIN_SRC_G0_OUT = 28,
+# NI_CDIO_SCAN_BEGIN_SRC_G1_OUT = 29,
+# NI_CDIO_SCAN_BEGIN_SRC_ANALOG_TRIGGER = 30,
+# NI_CDIO_SCAN_BEGIN_SRC_AO_UPDATE = 31,
+# NI_CDIO_SCAN_BEGIN_SRC_FREQ_OUT = 32,
+# NI_CDIO_SCAN_BEGIN_SRC_DIO_CHANGE_DETECT_IRQ = 33
+# };
+# static inline unsigned NI_CDIO_SCAN_BEGIN_SRC_PFI(unsigned pfi_channel) {
+# return NI_USUAL_PFI_SELECT(pfi_channel);
+# }
+# static inline unsigned NI_CDIO_SCAN_BEGIN_SRC_RTSI(unsigned
+# rtsi_channel) {
+# return NI_USUAL_RTSI_SELECT(rtsi_channel);
+# }
+#
+# /* scan_begin_src for scan_begin_arg==TRIG_EXT with analog output command
+# on NI boards. These scan begin sources can also be bitwise-or'd with
+# CR_INVERT to change polarity. */
+# static inline unsigned NI_AO_SCAN_BEGIN_SRC_PFI(unsigned pfi_channel) {
+# return NI_USUAL_PFI_SELECT(pfi_channel);
+# }
+# static inline unsigned NI_AO_SCAN_BEGIN_SRC_RTSI(unsigned rtsi_channel) {
+# return NI_USUAL_RTSI_SELECT(rtsi_channel);
+# }
+#
+# /* Bits for setting a clock source with
+# * INSN_CONFIG_SET_CLOCK_SRC when using NI frequency output subdevice. */
+# enum ni_freq_out_clock_source_bits {
+# NI_FREQ_OUT_TIMEBASE_1_DIV_2_CLOCK_SRC, // 10 MHz
+# NI_FREQ_OUT_TIMEBASE_2_CLOCK_SRC // 100 KHz
+# };
+#
+# /* Values for setting a clock source with INSN_CONFIG_SET_CLOCK_SRC for
+# * 8254 counter subdevices on Amplicon DIO boards (amplc_dio200 driver). */
+# enum amplc_dio_clock_source {
+# AMPLC_DIO_CLK_CLKN, /* per channel external clock
+# input/output pin (pin is only an
+# input when clock source set to this
+# value, otherwise it is an output) */
+# AMPLC_DIO_CLK_10MHZ, /* 10 MHz internal clock */
+# AMPLC_DIO_CLK_1MHZ, /* 1 MHz internal clock */
+# AMPLC_DIO_CLK_100KHZ, /* 100 kHz internal clock */
+# AMPLC_DIO_CLK_10KHZ, /* 10 kHz internal clock */
+# AMPLC_DIO_CLK_1KHZ, /* 1 kHz internal clock */
+# AMPLC_DIO_CLK_OUTNM1, /* output of preceding counter channel
+# (for channel 0, preceding counter
+# channel is channel 2 on preceding
+# counter subdevice, for first counter
+# subdevice, preceding counter
+# subdevice is the last counter
+# subdevice) */
+# AMPLC_DIO_CLK_EXT /* per chip external input pin */
+# };
+#
+# /* Values for setting a gate source with INSN_CONFIG_SET_GATE_SRC for
+# * 8254 counter subdevices on Amplicon DIO boards (amplc_dio200 driver). */
+# enum amplc_dio_gate_source {
+# AMPLC_DIO_GAT_VCC, /* internal high logic level */
+# AMPLC_DIO_GAT_GND, /* internal low logic level */
+# AMPLC_DIO_GAT_GATN, /* per channel external gate input */
+# AMPLC_DIO_GAT_NOUTNM2, /* negated output of counter channel
+# minus 2 (for channels 0 or 1,
+# channel minus 2 is channel 1 or 2 on
+# the preceding counter subdevice, for
+# the first counter subdevice the
+# preceding counter subdevice is the
+# last counter subdevice) */
+# AMPLC_DIO_GAT_RESERVED4,
+# AMPLC_DIO_GAT_RESERVED5,
+# AMPLC_DIO_GAT_RESERVED6,
+# AMPLC_DIO_GAT_RESERVED7
+# };
--- /dev/null
+# Copyright
+
+"Cython interface to comedilib.h"
+
+from _comedi_h cimport *
+
+
+cdef extern from 'comedilib.h':
+ ctypedef struct comedi_t:
+ pass
+
+ ctypedef struct comedi_range:
+ double min
+ double max
+ unsigned int unit
+
+ comedi_t *comedi_open(char *fn)
+ int comedi_close(comedi_t *it)
+
+ # logging
+
+ int comedi_loglevel(int loglevel)
+ void comedi_perror(char *s)
+ char *comedi_strerror(int errnum)
+ int comedi_errno()
+ int comedi_fileno(comedi_t *it)
+
+ # device queries
+
+ int comedi_get_n_subdevices(comedi_t *it)
+ # COMEDI_VERSION_CODE handled by device.Device.get_version_code()
+ int comedi_get_version_code(comedi_t *it)
+ char *comedi_get_driver_name(comedi_t *it)
+ char *comedi_get_board_name(comedi_t *it)
+ int comedi_get_read_subdevice(comedi_t *dev)
+ int comedi_get_write_subdevice(comedi_t *dev)
+
+ # subdevice queries
+
+ int comedi_get_subdevice_type(comedi_t *it,unsigned int subdevice)
+ int comedi_find_subdevice_by_type(comedi_t *it,int type,unsigned int subd)
+ int comedi_get_subdevice_flags(comedi_t *it,unsigned int subdevice)
+ int comedi_get_n_channels(comedi_t *it,unsigned int subdevice)
+ int comedi_range_is_chan_specific(comedi_t *it,unsigned int subdevice)
+ int comedi_maxdata_is_chan_specific(comedi_t *it,unsigned int subdevice)
+
+ # channel queries
+
+ lsampl_t comedi_get_maxdata(comedi_t *it,unsigned int subdevice,
+ unsigned int chan)
+ int comedi_get_n_ranges(comedi_t *it,unsigned int subdevice,
+ unsigned int chan)
+ comedi_range * comedi_get_range(comedi_t *it,unsigned int subdevice,
+ unsigned int chan,unsigned int range)
+ int comedi_find_range(comedi_t *it,unsigned int subd,unsigned int chan,
+ unsigned int unit,double min,double max)
+
+ # buffer queries
+
+ int comedi_get_buffer_size(comedi_t *it,unsigned int subdevice)
+ int comedi_get_max_buffer_size(comedi_t *it,unsigned int subdevice)
+ int comedi_set_buffer_size(comedi_t *it,unsigned int subdevice,
+ unsigned int len)
+
+ # low-level stuff
+
+ int comedi_do_insnlist(comedi_t *it,comedi_insnlist *il)
+ int comedi_do_insn(comedi_t *it,comedi_insn *insn)
+ int comedi_lock(comedi_t *it,unsigned int subdevice)
+ int comedi_unlock(comedi_t *it,unsigned int subdevice)
+
+ # synchronous stuff
+
+ int comedi_data_read(comedi_t *it,unsigned int subd,unsigned int chan,
+ unsigned int range,unsigned int aref,lsampl_t *data)
+ int comedi_data_read_n(comedi_t *it,unsigned int subd,unsigned int chan,
+ unsigned int range,unsigned int aref,lsampl_t *data, unsigned int n)
+ int comedi_data_read_hint(comedi_t *it,unsigned int subd,unsigned int chan,
+ unsigned int range,unsigned int aref)
+ int comedi_data_read_delayed(comedi_t *it,unsigned int subd,unsigned int chan,
+ unsigned int range,unsigned int aref,lsampl_t *data, unsigned int nano_sec)
+ int comedi_data_write(comedi_t *it,unsigned int subd,unsigned int chan,
+ unsigned int range,unsigned int aref,lsampl_t data)
+ int comedi_dio_config(comedi_t *it,unsigned int subd,unsigned int chan,
+ unsigned int dir)
+ int comedi_dio_get_config(comedi_t *it,unsigned int subd,unsigned int chan,
+ unsigned int *dir)
+ int comedi_dio_read(comedi_t *it,unsigned int subd,unsigned int chan,
+ unsigned int *bit)
+ int comedi_dio_write(comedi_t *it,unsigned int subd,unsigned int chan,
+ unsigned int bit)
+ int comedi_dio_bitfield2(comedi_t *it,unsigned int subd,
+ unsigned int write_mask, unsigned int *bits, unsigned int base_channel)
+
+ # streaming I/O (commands)
+
+ int comedi_get_cmd_src_mask(comedi_t *dev,unsigned int subdevice,
+ comedi_cmd *cmd)
+ int comedi_get_cmd_generic_timed(comedi_t *dev,unsigned int subdevice,
+ comedi_cmd *cmd, unsigned chanlist_len, unsigned scan_period_ns)
+ int comedi_cancel(comedi_t *it,unsigned int subdevice)
+ int comedi_command(comedi_t *it,comedi_cmd *cmd)
+ int comedi_command_test(comedi_t *it,comedi_cmd *cmd)
+ int comedi_poll(comedi_t *dev,unsigned int subdevice)
+
+ # buffer control
+
+ int comedi_set_max_buffer_size(comedi_t *it, unsigned int subdev,
+ unsigned int max_size)
+ int comedi_get_buffer_contents(comedi_t *it, unsigned int subdev)
+ int comedi_mark_buffer_read(comedi_t *it, unsigned int subdev,
+ unsigned int bytes)
+ int comedi_mark_buffer_written(comedi_t *it, unsigned int subdev,
+ unsigned int bytes)
+ int comedi_get_buffer_offset(comedi_t *it, unsigned int subdev)
+
+ # structs and functions used for parsing calibration files
+
+ ctypedef struct comedi_caldac_t:
+ unsigned int subdevice
+ unsigned int channel
+ unsigned int value
+
+ enum: COMEDI_MAX_NUM_POLYNOMIAL_COEFFICIENTS
+
+ ctypedef struct comedi_polynomial_t:
+ double coefficients[COMEDI_MAX_NUM_POLYNOMIAL_COEFFICIENTS]
+ double expansion_origin
+ unsigned order
+
+ ctypedef struct comedi_softcal_t:
+ comedi_polynomial_t *to_phys
+ comedi_polynomial_t *from_phys
+
+ enum: CS_MAX_AREFS_LENGTH
+
+ ctypedef struct comedi_calibration_setting_t:
+ unsigned int subdevice
+ unsigned int *channels
+ unsigned int num_channels
+ unsigned int *ranges
+ unsigned int num_ranges
+ unsigned int arefs[ CS_MAX_AREFS_LENGTH ]
+ unsigned int num_arefs
+ comedi_caldac_t *caldacs
+ unsigned int num_caldacs
+ comedi_softcal_t soft_calibration
+
+ ctypedef struct comedi_calibration_t:
+ char *driver_name
+ char *board_name
+ comedi_calibration_setting_t *settings
+ unsigned int num_settings
+
+ comedi_calibration_t* comedi_parse_calibration_file(
+ char *cal_file_path )
+ int comedi_apply_parsed_calibration( comedi_t *dev, unsigned int subdev,
+ unsigned int channel, unsigned int range, unsigned int aref,
+ comedi_calibration_t *calibration )
+ char* comedi_get_default_calibration_path( comedi_t *dev )
+ void comedi_cleanup_calibration( comedi_calibration_t *calibration )
+ int comedi_apply_calibration( comedi_t *dev, unsigned int subdev,
+ unsigned int channel, unsigned int range, unsigned int aref,
+ char *cal_file_path)
+
+ # New stuff to provide conversion between integers and physical values that
+ # can support software calibrations.
+ enum comedi_conversion_direction:
+ COMEDI_TO_PHYSICAL,
+ COMEDI_FROM_PHYSICAL
+ int comedi_get_softcal_converter(
+ unsigned subdevice, unsigned channel, unsigned range,
+ comedi_conversion_direction direction,
+ comedi_calibration_t *calibration,
+ comedi_polynomial_t *polynomial)
+ int comedi_get_hardcal_converter(
+ comedi_t *dev, unsigned subdevice, unsigned channel, unsigned range,
+ comedi_conversion_direction direction,
+ comedi_polynomial_t *polynomial)
+ double comedi_to_physical(lsampl_t data,
+ comedi_polynomial_t *conversion_polynomial)
+ lsampl_t comedi_from_physical(double data,
+ comedi_polynomial_t *conversion_polynomial)
+ #
+ #int comedi_internal_trigger(comedi_t *dev, unsigned subd, unsigned trignum);
+ #/* INSN_CONFIG wrappers */
+ #int comedi_arm(comedi_t *device, unsigned subdevice, unsigned source);
+ #int comedi_reset(comedi_t *device, unsigned subdevice);
+ #int comedi_get_clock_source(comedi_t *device, unsigned subdevice, unsigned channel, unsigned *clock, unsigned *period_ns);
+ #int comedi_get_gate_source(comedi_t *device, unsigned subdevice, unsigned channel,
+ # unsigned gate, unsigned *source);
+ #int comedi_get_routing(comedi_t *device, unsigned subdevice, unsigned channel, unsigned *routing);
+ #int comedi_set_counter_mode(comedi_t *device, unsigned subdevice, unsigned channel, unsigned mode_bits);
+ #int comedi_set_clock_source(comedi_t *device, unsigned subdevice, unsigned channel, unsigned clock, unsigned period_ns);
+ #int comedi_set_filter(comedi_t *device, unsigned subdevice, unsigned channel, unsigned filter);
+ #int comedi_set_gate_source(comedi_t *device, unsigned subdevice, unsigned channel, unsigned gate_index, unsigned gate_source);
+ #int comedi_set_other_source(comedi_t *device, unsigned subdevice, unsigned channel,
+ # unsigned other, unsigned source);
+ #int comedi_set_routing(comedi_t *device, unsigned subdevice, unsigned channel, unsigned routing);
+ #int comedi_get_hardware_buffer_size(comedi_t *device, unsigned subdevice, enum comedi_io_direction direction);
--- /dev/null
+# Copyright
+
+"Useful error checking wrappers around Comedilib function calls"
+
+# No relative imports in Cython yet, see
+# http://trac.cython.org/cython_trac/ticket/542
+from pycomedi import LOG as _LOG
+from pycomedi import PyComediError as _PyComediError
+
+cimport _comedilib_h
+
+
+def raise_error(function_name='', ret=None, error_msg=None):
+ """Report an error while executing a comedilib function
+
+ >>> raise_error(function_name='myfn', ret=-1)
+ Traceback (most recent call last):
+ ...
+ PyComediError: myfn: Success (-1)
+ >>> raise_error(function_name='myfn', ret=-1, error_msg='some error')
+ Traceback (most recent call last):
+ ...
+ PyComediError: myfn (some error): Success (-1)
+ """
+ errno = _comedilib_h.comedi_errno()
+ comedi_msg = _comedilib_h.comedi_strerror(errno)
+ #_comedilib_h.comedi_perror(function_name)
+ if error_msg:
+ msg = '%s (%s): %s (%s)' % (function_name, error_msg, comedi_msg, ret)
+ else:
+ msg = '%s: %s (%s)' % (function_name, comedi_msg, ret)
+ raise _PyComediError(msg)
+
+
+def _comedi_getter(name, is_invalid):
+ # Hmm, cannot get function by name, or pass differing function pointers...
+ #def comedi_get(function_name, *args, **kwargs):
+ def comedi_get(function, *args, **kwargs):
+ if 'error_msg' in kwargs:
+ error_msg = kwargs.pop('error_msg')
+ else:
+ error_msg = 'error while running %s with %s and %s' % (
+ function_name, args, kwargs)
+ #fn = getattr(_comedilib_h, function_name)
+ fn = function # workaround until I get getattr() working
+ function_name = function.__name__
+
+ _LOG.debug('calling %s with %s %s' % (function_name, args, kwargs))
+
+ ret = fn(*args, **kwargs)
+ _LOG.debug(' call to %s returned %s' % (function_name, ret))
+ if is_invalid(ret):
+ raise_error(
+ error_msg=error_msg, function_name=function_name, ret=ret)
+ return ret
+ #comedi_get.__name__ = name
+ #comedi_get.__doc__ = (
+ # "Execute Comedilib's `<function_name>(*args, **kwargs)` safely.")
+ return comedi_get
+
+comedi_int = _comedi_getter('comedi_int', lambda ret: ret < 0)
+comedi_ptr = _comedi_getter('comedi_ptr', lambda ret: ret == None)
+comedi_tup = _comedi_getter('comedi_tup', lambda ret: ret[0] < 0)
--- /dev/null
+# Copyright
+
+"Expose `CalibratedConverter` internals at the C level for other Cython modules"
+
+cimport _comedilib_h
+
+
+cdef class CalibratedConverter (object):
+ cdef _comedilib_h.comedi_polynomial_t _to_physical, _from_physical
+
+ cdef _str_poly(self, _comedilib_h.comedi_polynomial_t polynomial)
+ cpdef to_physical(self, data)
+ cpdef from_physical(self, data)
+ cpdef get_to_physical_expansion_origin(self)
+ cpdef get_to_physical_coefficients(self)
+ cpdef get_from_physical_expansion_origin(self)
+ cpdef get_from_physical_coefficients(self)
--- /dev/null
+# Copyright
+
+"""Pythonic wrappers for converting between Comedilib and physical units
+
+For one-off conversions, use the functions `comedi_to_physical` and
+`comedi_from_physical`. For repeated conversions, use an instance of
+`CalibratedConverter`.
+"""
+
+cimport numpy as _numpy
+import numpy as _numpy
+
+cimport _comedi_h
+cimport _comedilib_h
+import constant as _constant
+
+
+cdef void _setup_comedi_polynomial_t(
+ _comedilib_h.comedi_polynomial_t *p, coefficients, expansion_origin):
+ """Setup the `comedi_polynomial_t` at `p`
+
+ * `coefficients` is an iterable containing polynomial coefficients
+ * `expansion_origin` is the center of the polynomial expansion
+ """
+ for i,x in enumerate(coefficients):
+ p.coefficients[i] = x
+ p.order = len(coefficients)-1
+ p.expansion_origin = expansion_origin
+
+cdef object _convert(
+ _comedilib_h.comedi_polynomial_t *p, object data, object direction):
+ """Apply the polynomial conversion `p` to `data`.
+
+ `direction` should be a value from `constant.CONVERSION_DIRECTION`.
+ """
+ to_physical = (_constant.bitwise_value(direction)
+ == _constant.CONVERSION_DIRECTION.to_physical.value)
+ if _numpy.isscalar(data):
+ if to_physical:
+ return _comedilib_h.comedi_to_physical(data, p)
+ else:
+ return _comedilib_h.comedi_from_physical(data, p)
+ if to_physical:
+ dtype = _numpy.double
+ else:
+ dtype = _numpy.uint
+ array = _numpy.array(data, dtype=dtype)
+ for i,d in enumerate(data):
+ if to_physical:
+ array[i] = _comedilib_h.comedi_to_physical(d, p)
+ else:
+ array[i] = _comedilib_h.comedi_from_physical(d, p)
+ return array
+
+cpdef comedi_to_physical(data, coefficients, expansion_origin):
+ """Convert Comedi bit values (`lsampl_t`) to physical units (`double`)
+
+ * `data` is the value to be converted (scalar or array-like)
+ * `coefficients` and `expansion_origin` should be appropriate
+ for `_setup_comedi_polynomial_t`. TODO: expose it's docstring?
+
+ The conversion algorithm is::
+
+ x = sum_i c_i * (d-d_o)^i
+
+ where `x` is the returned physical value, `d` is the supplied data,
+ `c_i` is the `i`\th coefficient, and `d_o` is the expansion origin.
+
+ >>> print comedi_to_physical.__doc__ # doctest: +ELLIPSIS
+ Convert Comedi bit values (`lsampl_t`) to physical units (`double`)
+ ...
+ >>> comedi_to_physical(1, [1, 2, 3], 2)
+ 2.0
+ >>> comedi_to_physical([1, 2, 3], [1, 2, 3], 2)
+ array([ 2., 1., 6.])
+ """
+ cdef _comedilib_h.comedi_polynomial_t p
+ _setup_comedi_polynomial_t(&p, coefficients, expansion_origin)
+ return _convert(&p, data, _constant.CONVERSION_DIRECTION.to_physical)
+
+cpdef comedi_from_physical(data, coefficients, expansion_origin):
+ """Convert physical units to Comedi bit values
+
+ Like `comedi_to_physical` but converts `double` -> `lsampl_t`.
+
+ >>> comedi_from_physical(1, [1,2,3], 2)
+ 2L
+ >>> comedi_from_physical([1, 2, 3], [1, 2, 3], 2)
+ array([2, 1, 6], dtype=uint32)
+ """
+ cdef _comedilib_h.comedi_polynomial_t p
+ _setup_comedi_polynomial_t(&p, coefficients, expansion_origin)
+ return _convert(&p, data, _constant.CONVERSION_DIRECTION.from_physical)
+
+
+cdef class CalibratedConverter (object):
+ """Apply a converion polynomial
+
+ Usually you would get the this converter from
+ `DataChannel.get_converter()` or similar. but for testing, we'll
+ just create one out of thin air.
+
+ >>> c = CalibratedConverter(
+ ... to_physical_coefficients=[1, 2, 3],
+ ... to_physical_expansion_origin=1)
+ >>> c # doctest: +NORMALIZE_WHITESPACE
+ <CalibratedConverter
+ to_physical:{coefficients:[1.0, 2.0, 3.0] origin:1.0}
+ from_physical:{coefficients:[0.0] origin:0.0}>
+
+ >>> c.to_physical(1)
+ 1.0
+ >>> c.to_physical([0, 1, 2])
+ array([ 2., 1., 6.])
+ >>> c.to_physical(_numpy.array([0, 1, 2, 3], dtype=_numpy.uint))
+ array([ 2., 1., 6., 17.])
+
+ >>> c.get_to_physical_expansion_origin()
+ 1.0
+ >>> c.get_to_physical_coefficients()
+ array([ 1., 2., 3.])
+ """
+ def __init__(self, to_physical_coefficients=None,
+ to_physical_expansion_origin=0,
+ from_physical_coefficients=None,
+ from_physical_expansion_origin=0):
+ if to_physical_coefficients:
+ _setup_comedi_polynomial_t(
+ &self._to_physical, to_physical_coefficients,
+ to_physical_expansion_origin)
+ if from_physical_coefficients:
+ _setup_comedi_polynomial_t(
+ &self._from_physical, from_physical_coefficients,
+ from_physical_expansion_origin)
+
+ cdef _str_poly(self, _comedilib_h.comedi_polynomial_t polynomial):
+ return '{coefficients:%s origin:%s}' % (
+ [float(polynomial.coefficients[i])
+ for i in range(polynomial.order+1)],
+ float(polynomial.expansion_origin))
+
+ def __str__(self):
+ return '<%s to_physical:%s from_physical:%s>' % (
+ self.__class__.__name__, self._str_poly(self._to_physical),
+ self._str_poly(self._from_physical))
+
+ def __repr__(self):
+ return self.__str__()
+
+ cpdef to_physical(self, data):
+ return _convert(&self._to_physical, data,
+ _constant.CONVERSION_DIRECTION.to_physical)
+
+ cpdef from_physical(self, data):
+ return _convert(&self._from_physical, data,
+ _constant.CONVERSION_DIRECTION.from_physical)
+
+ cpdef get_to_physical_expansion_origin(self):
+ return self._to_physical.expansion_origin
+
+ cpdef get_to_physical_coefficients(self):
+ ret = _numpy.ndarray((self._to_physical.order+1,), _numpy.double)
+ for i in xrange(len(ret)):
+ ret[i] = self._to_physical.coefficients[i]
+ return ret
+
+ cpdef get_from_physical_expansion_origin(self):
+ return self._from_physical.expansion_origin
+
+ cpdef get_from_physical_coefficients(self):
+ ret = _numpy.ndarray((self._from_physical.order+1,), _numpy.double)
+ for i in xrange(len(ret)):
+ ret[i] = self._from_physical.coefficients[i]
+ return ret
+
+
+# TODO: see comedi_caldac_t and related at end of comedilib.h
--- /dev/null
+# Copyright
+
+"Wrap channel-wide Comedi functions in `Channel` and related classes"
+
+cimport numpy as _numpy
+import numpy as _numpy
+
+cimport _comedi_h
+cimport _comedilib_h
+from calibration cimport CalibratedConverter as _CalibratedConverter
+from range cimport Range as _Range
+from subdevice cimport Subdevice as _Subdevice
+
+from pycomedi import LOG as _LOG
+from chanspec import ChanSpec as _ChanSpec
+from pycomedi import PyComediError as _PyComediError
+import _error
+import constant as _constant
+
+
+cdef class Channel (object):
+ """Class bundling channel-related functions
+
+ >>> from .device import Device
+ >>> from . import constant
+
+ >>> d = Device('/dev/comedi0')
+ >>> d.open()
+ >>> s = d.get_read_subdevice()
+ >>> c = s.channel(0)
+
+ >>> c.get_maxdata()
+ 65535L
+ >>> c.get_n_ranges()
+ 16
+ >>> c.get_range(0)
+ <Range unit:volt min:-10.0 max:10.0>
+ >>> c.find_range(constant.UNIT.volt, 0, 5)
+ <Range unit:volt min:0.0 max:5.0>
+
+ >>> d.close()
+ """
+ cdef public _Subdevice subdevice
+ cdef public int index
+
+ def __cinit__(self):
+ self.index = -1
+
+ def __init__(self, subdevice, index):
+ super(Channel, self).__init__()
+ self.subdevice = subdevice
+ self.index = index
+
+ def get_maxdata(self):
+ ret = _comedilib_h.comedi_get_maxdata(
+ self.subdevice.device.device,
+ self.subdevice.index, self.index)
+ if ret < 0:
+ _error.raise_error(function_name='comedi_get_maxdata', ret=ret)
+ return ret
+
+ def get_n_ranges(self):
+ ret = _comedilib_h.comedi_get_n_ranges(
+ self.subdevice.device.device,
+ self.subdevice.index, self.index)
+ if ret < 0:
+ _error.raise_error(function_name='comedi_get_n_ranges', ret=ret)
+ return ret
+
+ cdef _get_range(self, index):
+ cdef _comedilib_h.comedi_range *rng
+ cdef _Range ret
+ # Memory pointed to by the return value is freed on Device.close().
+ rng = _comedilib_h.comedi_get_range(
+ self.subdevice.device.device,
+ self.subdevice.index, self.index, index)
+ if rng is NULL:
+ _error.raise_error(function_name='comedi_get_range')
+ ret = _Range(value=index)
+ ret.set_comedi_range(rng[0])
+ # rng[0] is a sneaky way to dereference rng, since Cython
+ # doesn't support *rng.
+ return ret
+
+ def get_range(self, index):
+ return self._get_range(index)
+
+ def _find_range(self, unit, min, max):
+ "Search for range"
+ ret = _comedilib_h.comedi_find_range(
+ self.subdevice.device.device,
+ self.subdevice.index, self.index,
+ _constant.bitwise_value(unit), min, max)
+ if ret < 0:
+ _error.raise_error(function_name='comedi_find_range', ret=ret)
+ return ret
+
+ def find_range(self, unit, min, max):
+ """Search for range
+
+ `unit` should be an item from `constants.UNIT`.
+ """
+ return self.get_range(self._find_range(unit, min, max))
+
+
+cdef class DigitalChannel (Channel):
+ """Channel configured for reading or writing digital data.
+
+ >>> from .device import Device
+ >>> from . import constant
+
+ >>> d = Device('/dev/comedi0')
+ >>> d.open()
+ >>> s = d.find_subdevice_by_type(constant.SUBDEVICE_TYPE.dio)
+ >>> c = s.channel(0, factory=DigitalChannel)
+
+ >>> c.get_maxdata()
+ 1L
+ >>> c.get_n_ranges()
+ 1
+ >>> c.get_range(0)
+ <Range unit:volt min:0.0 max:5.0>
+
+ >>> direction = c.dio_get_config()
+ >>> direction # doctest: +SKIP
+ <_NamedInt input>
+
+ >>> c.dio_config(_constant.IO_DIRECTION.input)
+ >>> data = c.dio_read()
+ >>> data
+ 1
+
+ >>> c.dio_config(_constant.IO_DIRECTION.output)
+ >>> c.dio_write(1)
+
+ >>> c.dio_config(direction)
+
+ >>> d.close()
+ """
+ def dio_config(self, dir):
+ """Change input/output properties
+
+ `dir` should be an item from `constants.IO_DIRECTION`.
+ """
+ ret = _comedilib_h.comedi_dio_config(
+ self.subdevice.device.device,
+ self.subdevice.index, self.index,
+ _constant.bitwise_value(dir))
+ if ret < 0:
+ _error.raise_error(function_name='comedi_dio_config', ret=ret)
+
+ def dio_get_config(self):
+ """Query input/output properties
+
+ Return an item from `constant.IO_DIRECTION`.
+ """
+ cpdef unsigned int dir
+ ret = _comedilib_h.comedi_dio_get_config(
+ self.subdevice.device.device,
+ self.subdevice.index, self.index, &dir)
+ if ret < 0:
+ _error.raise_error(function_name='comedi_dio_get_config', ret=ret)
+ return _constant.IO_DIRECTION.index_by_value(dir)
+
+ def dio_read(self):
+ "Read a single bit"
+ cpdef unsigned int bit
+ ret = _comedilib_h.comedi_dio_read(
+ self.subdevice.device.device,
+ self.subdevice.index, self.index, &bit)
+ if ret < 0:
+ _error.raise_error(function_name='comedi_dio_read', ret=ret)
+ return int(bit)
+
+ def dio_write(self, bit):
+ "Write a single bit"
+ ret = _comedilib_h.comedi_dio_write(
+ self.subdevice.device.device,
+ self.subdevice.index, self.index, bit)
+ if ret < 0:
+ _error.raise_error(function_name='comedi_dio_write', ret=ret)
+
+
+cdef class AnalogChannel (Channel):
+ """Channel configured for reading or writing analog data.
+
+ `range` should be a `Range` instance, `aref` should be an
+ `constants.AREF` instance. If not specified, defaults are chosen
+ based on the capabilities of the subdevice.
+
+ >>> from .device import Device
+ >>> from . import constant
+
+ >>> d = Device('/dev/comedi0')
+ >>> d.open()
+ >>> s = d.get_read_subdevice()
+ >>> c = s.channel(0, factory=AnalogChannel)
+
+ >>> c.range
+ <Range unit:volt min:-10.0 max:10.0>
+ >>> c.aref
+ <_NamedInt ground>
+
+ >>> data = c.data_read()
+ >>> data # doctest: +SKIP
+ 32670L
+ >>> converter = c.get_converter()
+ >>> converter # doctest: +NORMALIZE_WHITESPACE
+ <CalibratedConverter
+ to_physical:{coefficients:[-10.0, 0.00030518043793392844] origin:0.0}
+ from_physical:{coefficients:[0.0, 3276.75] origin:-10.0}>
+ >>> physical_data = converter.to_physical(data)
+ >>> physical_data # doctest: +SKIP
+ -0.029755092698558021
+ >>> converter.from_physical(physical_data) == data
+ True
+
+ >>> data = c.data_read_n(5)
+ >>> data # doctest: +SKIP
+ array([32674, 32673, 32674, 32672, 32675], dtype=uint32)
+
+ >>> c.data_read_hint()
+ >>> c.data_read() # doctest: +SKIP
+ 32672L
+
+ >>> data = c.data_read_delayed(nano_sec=1e3)
+ >>> data # doctest: +SKIP
+ 32672L
+
+ >>> s = d.get_write_subdevice()
+ >>> c = s.channel(0, factory=AnalogChannel)
+
+ >>> converter = c.get_converter()
+ >>> converter # doctest: +NORMALIZE_WHITESPACE
+ <CalibratedConverter
+ to_physical:{coefficients:[-10.0, 0.00030518043793392844] origin:0.0}
+ from_physical:{coefficients:[0.0, 3276.75] origin:-10.0}>
+
+ >>> c.data_write(converter.from_physical(0))
+
+ >>> d.close()
+
+ Even after the device is closed, the range information is
+ retained.
+
+ >>> c.range
+ <Range unit:volt min:-10.0 max:10.0>
+ """
+ cdef public _Range range
+ cdef public object aref
+
+ def __init__(self, range=None, aref=None, **kwargs):
+ super(AnalogChannel, self).__init__(**kwargs)
+ if range == None:
+ range = self.get_range(0)
+ self.range = range
+ if aref == None:
+ flags = self.subdevice.get_flags()
+ for ar in _constant.AREF:
+ if getattr(flags, ar.name):
+ aref = ar
+ break
+ raise _PyComediError(
+ '%s does not support any known analog reference type (%s)'
+ % (self.subdevice, flags))
+ self.aref = aref
+
+ # syncronous stuff
+
+ def data_read(self):
+ "Read one sample"
+ cdef _comedi_h.lsampl_t data
+ ret = _comedilib_h.comedi_data_read(
+ self.subdevice.device.device,
+ self.subdevice.index, self.index,
+ _constant.bitwise_value(self.range),
+ _constant.bitwise_value(self.aref),
+ &data)
+ if ret < 0:
+ _error.raise_error(function_name='comedi_data_read', ret=ret)
+ return data
+
+ def data_read_n(self, n):
+ "Read `n` samples (timing between samples is undefined)."
+ data = _numpy.ndarray(shape=(n,), dtype=_numpy.uint32)
+ ret = _comedilib_h.comedi_data_read_n(
+ self.subdevice.device.device,
+ self.subdevice.index, self.index,
+ _constant.bitwise_value(self.range),
+ _constant.bitwise_value(self.aref),
+ <_comedilib_h.lsampl_t *>_numpy.PyArray_DATA(data), n)
+ if ret < 0:
+ _error.raise_error(function_name='comedi_data_read_n', ret=ret)
+ return data
+
+ def data_read_hint(self):
+ """Tell driver which channel/range/aref you will read next
+
+ Used to prepare an analog input for a subsequent call to
+ comedi_data_read. It is not necessary to use this function,
+ but it can be useful for eliminating inaccuaracies caused by
+ insufficient settling times when switching the channel or gain
+ on an analog input. This function sets an analog input to the
+ channel, range, and aref specified but does not perform an
+ actual analog to digital conversion.
+
+ Alternatively, one can simply use `.data_read_delayed()`,
+ which sets up the input, pauses to allow settling, then
+ performs a conversion.
+ """
+ ret = _comedilib_h.comedi_data_read_hint(
+ self.subdevice.device.device,
+ self.subdevice.index, self.index,
+ _constant.bitwise_value(self.range),
+ _constant.bitwise_value(self.aref))
+ if ret < 0:
+ _error.raise_error(function_name='comedi_data_read_hint', ret=ret)
+
+ def data_read_delayed(self, nano_sec=0):
+ """Read single sample after delaying specified settling time.
+
+ Although the settling time is specified in integer
+ nanoseconds, the actual settling time will be rounded up to
+ the nearest microsecond.
+ """
+ cdef _comedi_h.lsampl_t data
+ ret = _comedilib_h.comedi_data_read_delayed(
+ self.subdevice.device.device,
+ self.subdevice.index, self.index,
+ _constant.bitwise_value(self.range),
+ _constant.bitwise_value(self.aref),
+ &data, int(nano_sec))
+ if ret < 0:
+ _error.raise_error(function_name='comedi_data_read_delayed',
+ ret=ret)
+ return data
+
+ def data_write(self, data):
+ """Write one sample
+
+ Returns 1 (the number of data samples written).
+ """
+ ret = _comedilib_h.comedi_data_write(
+ self.subdevice.device.device,
+ self.subdevice.index, self.index,
+ _constant.bitwise_value(self.range),
+ _constant.bitwise_value(self.aref),
+ int(data))
+ if ret != 1:
+ _error.raise_error(function_name='comedi_data_write', ret=ret)
+
+ def chanspec(self):
+ return _ChanSpec(chan=self.index, range=self.range, aref=self.aref)
+
+
+ cdef _comedilib_h.comedi_polynomial_t get_softcal_converter(
+ self, direction, calibration):
+ """
+
+ `direction` should be a value from `constant.CONVERSION_DIRECTION`.
+ """
+ cdef _comedilib_h.comedi_polynomial_t poly
+ #rc = _comedilib_h.comedi_get_softcal_converter(
+ # self.subdevice.device.device,
+ # self.subdevice.index, self.index,
+ # _constant.bitwise_value(self.range),
+ # _constant.bitwise_value(direction),
+ # calibration, &poly)
+ #if rc < 0:
+ # _error.raise_error(function_name='comedi_get_softcal_converter',
+ # ret=rc)
+ return poly
+
+ cdef _comedilib_h.comedi_polynomial_t get_hardcal_converter(
+ self, direction):
+ """
+
+ `direction` should be a value from `constant.CONVERSION_DIRECTION`.
+ """
+ cdef _comedilib_h.comedi_polynomial_t poly
+ rc = _comedilib_h.comedi_get_hardcal_converter(
+ self.subdevice.device.device,
+ self.subdevice.index, self.index,
+ _constant.bitwise_value(self.range),
+ _constant.bitwise_value(direction), &poly)
+ if rc < 0:
+ _error.raise_error(function_name='comedi_get_hardcal_converter',
+ ret=rc)
+ return poly
+
+ cdef _get_converter(self, calibration):
+ cdef _comedilib_h.comedi_polynomial_t to_physical, from_physical
+ cdef _CalibratedConverter ret
+ flags = self.subdevice.get_flags()
+ if flags.soft_calibrated:
+ #if calibration is None:
+ # calibration = self.subdevice.device.parse_calibration()
+ raise NotImplementedError()
+ else:
+ to_physical = self.get_hardcal_converter(
+ _constant.CONVERSION_DIRECTION.to_physical)
+ from_physical = self.get_hardcal_converter(
+ _constant.CONVERSION_DIRECTION.from_physical)
+ ret = _CalibratedConverter()
+ ret._to_physical = to_physical
+ ret._from_physical = from_physical
+ return ret
+
+ def get_converter(self, calibration=None):
+ return self._get_converter(calibration)
--- /dev/null
+# Copyright
+
+"Replace Comedi's CR_PACK and related macros with a Python class"
+
+cimport _comedi_h
+import constant as _constant
+
+
+class ChanSpec (_constant.BitwiseOperator):
+ """Channel specification bitfield
+
+ >>> from .constant import AREF, CR
+ >>> c = ChanSpec(chan=1, range=3, aref=AREF.diff,
+ ... flags=CR.edge|CR.invert)
+ >>> print c
+ <ChanSpec chan:1 range:3 aref:diff flags:edge|invert>
+ >>> c.chan
+ 1L
+ >>> c.chan = 2
+ >>> c.chan
+ 2L
+ """
+ _fields = ['chan', 'range', 'aref', 'flags']
+ _all = 0xffffffffL
+
+ def __init__(self, chan=0, range=0, aref=0, flags=0):
+ self.value = 0L
+ self.chan = chan
+ self.range = range
+ self.aref = aref
+ self.flags = flags
+
+ def __str__(self):
+ # TODO: consolidate to a utility class or function
+ fields = ['%s:%s' % (f, getattr(self, f)) for f in self._fields]
+ return '<%s %s>' % (self.__class__.__name__, ' '.join(fields))
+
+ def __repr__(self):
+ return self.__str__()
+
+ def _chan_get(self):
+ return self.value & 0xff
+ def _chan_set(self, value):
+ self.value &= self._all - 0xff
+ self.value |= _constant.bitwise_value(value) & 0xff
+ chan = property(fget=_chan_get, fset=_chan_set)
+
+ def _range_get(self):
+ return (self.value >> 16) & 0xff
+ def _range_set(self, value):
+ self.value &= self._all - (0xff << 16)
+ self.value |= (_constant.bitwise_value(value) & 0xff) << 16
+ range = property(fget=_range_get, fset=_range_set)
+
+ def _aref_get(self):
+ v = (self.value >> 24) & 0x03
+ return _constant.AREF.index_by_value(v)
+ def _aref_set(self, value):
+ self.value &= self._all - (0x03 << 24)
+ self.value |= (_constant.bitwise_value(value) & 0x03) << 24
+ aref = property(fget=_aref_get, fset=_aref_set)
+
+ def _flags_get(self):
+ v = self.value & _constant.CR._all.value
+ return _constant.FlagValue(_constant.CR, v)
+ def _flags_set(self, value):
+ self.value &= self._all - _constant.CR._all.value
+ self.value |= _constant.bitwise_value(value) & _constant.CR._all.value
+ flags = property(fget=_flags_get, fset=_flags_set)
+++ /dev/null
-# Copyright (C) 2010-2011 W. Trevor King
-#
-# This program is free software: you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program. If not, see <http://www.gnu.org/licenses/>.
-
-"Object oriented wrappers around the comedi module."
-
-import os as _os
-
-import comedi as _comedi
-import numpy as _numpy
-
-from . import LOG as _LOG
-from . import PyComediError as _PyComediError
-from . import constants as _constants
-
-
-def _comedi_arg(arg):
- "Replace arguments with their comedilib value."
- if isinstance(arg, _constants._BitwiseOperator):
- return arg.value
- elif isinstance(arg, Command):
- _LOG.debug(str(arg))
- return arg.cmd
- elif isinstance(arg, Chanlist):
- return arg.chanlist()
- return arg
-
-def _comedi_getter(name, is_invalid):
- def comedi_get(function_name, *args, **kwargs):
- if 'error_msg' in kwargs:
- error_msg = kwargs.pop('error_msg')
- else:
- error_msg = 'error while running %s with %s and %s' % (
- function_name, args, kwargs)
- fn = getattr(_comedi, function_name)
-
- _LOG.debug('calling %s with %s %s' % (function_name, args, kwargs))
-
- args = list(args)
- for i,arg in enumerate(args):
- args[i] = _comedi_arg(arg)
- for key,value in kwargs.iteritems():
- kwargs[key] = _comedi_arg(value)
-
- ret = fn(*args, **kwargs)
- _LOG.debug(' call to %s returned %s' % (function_name, ret))
- if is_invalid(ret):
- errno = _comedi.comedi_errno()
- comedi_msg = _comedi.comedi_strerror(errno)
- _comedi.comedi_perror(function_name)
- raise _PyComediError('%s: %s (%s)' % (error_msg, comedi_msg, ret))
- return ret
- comedi_get.__name__ = name
- comedi_get.__doc__ = (
- 'Execute `comedi.<function_name>(*args, **kwargs)` safely.')
- return comedi_get
-
-_comedi_int = _comedi_getter('comedi_int', lambda ret: ret < 0)
-_comedi_ptr = _comedi_getter('comedi_ptr', lambda ret: ret == None)
-_comedi_tup = _comedi_getter('comedi_tup', lambda ret: ret[0] < 0)
-
-
-def _cache(method):
- def wrapper(self, *args, **kwargs):
- key = (method.__name__, args, str(kwargs))
- if key not in self._cache:
- self._cache[key] = method(self, *args, **kwargs)
- return self._cache[key]
- wrapper.__name__ = method.__name__
- wrapper.__doc__ = method.__doc__
- return wrapper
-
-
-class CacheObject (object):
- """An object that caches return values for decoreated merthods.
-
- >>> class A (CacheObject):
- ... @_cache
- ... def double(self, x):
- ... print 'calculating 2*%d' % x
- ... return x*2
- >>> a = A()
- >>> a.double(2)
- calculating 2*2
- 4
- >>> a.double(2)
- 4
- >>> a.double(3)
- calculating 2*3
- 6
- >>> a.double(3)
- 6
- >>> print sorted(a._cache.keys())
- [('double', (2,), '{}'), ('double', (3,), '{}')]
- """
- def __init__(self):
- self.clear_cache()
-
- def clear_cache(self):
- self._cache = {}
-
-
-class Channel (CacheObject):
- def __init__(self, subdevice, index):
- super(Channel, self).__init__()
- self._subdevice = subdevice
- self._index = index
-
- @_cache
- def get_maxdata(self):
- return _comedi_int(
- 'comedi_get_maxdata', self._subdevice._device._device,
- self._subdevice._index, self._index)
-
- @_cache
- def get_n_ranges(self):
- return _comedi_int(
- 'comedi_get_n_ranges', self._subdevice._device._device,
- self._subdevice._index, self._index)
-
- def get_range(self, index):
- r = _comedi_ptr(
- 'comedi_get_range', self._subdevice._device._device,
- self._subdevice._index, self._index, index)
- return Range(index=index, range=r)
-
- @_cache
- def _find_range(self, unit, min, max):
- "Search for range"
- return _comedi_int(
- 'comedi_find_range', self._subdevice._device._device,
- self._subdevice._index, self._index, unit.value, min, max)
-
- def find_range(self, unit, min, max):
- """Search for range
-
- `unit` should be an item from `constants.UNIT`.
- """
- return self.get_range(self._find_range(unit, min, max))
-
-
-class Subdevice (CacheObject):
- def __init__(self, device, index):
- super(Subdevice, self).__init__()
- self._device = device
- self._index = index
-
- @_cache
- def get_type(self):
- "Type of subdevice (from `SUBDEVICE_TYPE`)"
- _type = _comedi_int(
- 'comedi_get_subdevice_type', self._device._device, self._index)
- return _constants.SUBDEVICE_TYPE.index_by_value(_type)
-
- @_cache
- def _get_flags(self):
- "Subdevice flags"
- return _comedi_int(
- 'comedi_get_subdevice_flags', self._device._device, self._index)
-
- def get_flags(self):
- "Subdevice flags (an `SDF` `FlagValue`)"
- return _constants.FlagValue(
- _constants.SDF, self._get_flags())
-
- @_cache
- def n_channels(self):
- "Number of subdevice channels"
- return _comedi_int(
- 'comedi_get_n_channels', self._device._device, self._index)
-
- @_cache
- def range_is_chan_specific(self):
- return _comedi_int(
- 'comedi_range_is_chan_specific', self._device._device, self._index)
-
- @_cache
- def maxdata_is_chan_specific(self):
- return _comedi_int(
- 'comedi_maxdata_is_chan_specific',
- self._device._device, self._index)
-
- def lock(self):
- "Reserve the subdevice"
- _comedi_int('comedi_lock', self._device._device, self._index)
-
- def unlock(self):
- "Release the subdevice"
- _comedi_int('comedi_unlock', self._device._device, self._index)
-
- def dio_bitfield(self, bits=0, write_mask=0, base_channel=0):
- """Read/write multiple digital channels.
-
- `bits` and `write_mask` are bit fields with the least
- significant bit representing channel `base_channel`.
-
- Returns a bit field containing the read value of all input
- channels and the last written value of all output channels.
- """
- rc,bits = _comedi_tup(
- 'comedi_dio_bitfield2', self._device._device,
- self._index, write_mask, bits, base_channel)
- return bits
-
- # extensions to make a more idomatic Python interface
-
- def insn(self):
- insn = self._device.insn()
- insn.subdev = self._index
- return insn
-
- def channel(self, index, factory=Channel, **kwargs):
- "`Channel` instance for the `index`\ed channel."
- return factory(subdevice=self, index=index, **kwargs)
-
-
-class Device (CacheObject):
- "Class bundling device-related functions."
- def __init__(self, filename):
- super(Device, self).__init__()
- self.filename = filename
- self._device = None
- self.file = None
-
- def open(self):
- "Open device"
- self._device = _comedi_ptr('comedi_open', self.filename)
- self.file = _os.fdopen(self.fileno(), 'r+')
- self.clear_cache()
-
- def close(self):
- "Close device"
- self.file.flush()
- self.file.close()
- _comedi_int('comedi_close', self._device)
- self._device = None
- self.file = None
- self.clear_cache()
-
- @_cache
- def fileno(self):
- "File descriptor for this device"
- return _comedi_int('comedi_fileno', self._device)
-
- @_cache
- def get_n_subdevices(self):
- "Number of subdevices"
- self._cache
- return _comedi_int('comedi_get_n_subdevices', self._device)
-
- @_cache
- def get_version_code(self):
- """Comedi version code.
-
- This is a kernel-module level property, but a valid device is
- necessary to communicate with the kernel module.
-
- Returns a tuple of version numbers, e.g. `(0, 7, 61)`.
- """
- version = _comedi_int('comedi_get_version_code', self._device)
- ret = []
- for i in range(3):
- ret.insert(0, version & (2**8-1))
- version >>= 2**8 # shift over 8 bits
- return tuple(ret)
-
- @_cache
- def get_driver_name(self):
- "Comedi driver name"
- return _comedi_ptr('get_driver_name', self._device)
-
- @_cache
- def get_board_name(self):
- "Comedi board name"
- return _comedi_ptr('get_board_name', self._device)
-
- @_cache
- def _get_read_subdevice(self):
- "Find streaming input subdevice index"
- return _comedi_int('comedi_get_read_subdevice', self._device)
-
- def get_read_subdevice(self, **kwargs):
- "Find streaming input subdevice"
- return self.subdevice(self._get_read_subdevice(), **kwargs)
-
- @_cache
- def _get_write_subdevice(self):
- "Find streaming output subdevice index"
- return _comedi_int('comedi_get_write_subdevice', self._device)
-
- def _get_write_subdevice(self, **kwargs):
- "Find streaming output subdevice"
- return self.subdevice(self._get_write_subdevice(), **kwargs)
-
- @_cache
- def _find_subdevice_by_type(self, subdevice_type):
- "Search for a subdevice index for type `subdevice_type`)."
- return _comedi_int(
- 'comedi_find_subdevice_by_type',
- self._device, subdevice_type.value, 0) # 0 is starting subdevice
-
- def find_subdevice_by_type(self, subdevice_type, **kwargs):
- """Search for a subdevice by type `subdevice_type`)."
-
- `subdevice_type` should be an item from `constants.SUBDEVICE_TYPE`.
- """
- return self.subdevice(
- self._find_subdevice_by_type(subdevice_type), **kwargs)
-
- def do_insnlist(self, insnlist):
- """Perform multiple instructions
-
- Returns the number of successfully completed instructions.
- """
- return _comedi_int('comedi_do_insn', self._device, insn)
-
- def do_insn(self, insn):
- """Preform a single instruction.
-
- Returns an instruction-specific integer.
- """
- return _comedi_int('comedi_do_insn', self._device, insn)
-
- def get_default_calibration_path(self):
- "The default calibration path for this device"
- assert self._device != None, (
- 'Must call get_default_calibration_path on an open device.')
- return _comedi_ptr('comedi_get_default_calibration_path', self._device)
-
- # extensions to make a more idomatic Python interface
-
- def insn(self):
- return _comedi.comedi_insn_struct()
-
- def subdevices(self, **kwargs):
- "Iterate through all available subdevices."
- for i in range(self.n_subdevices):
- yield self.subdevice(i, **kwargs)
-
- def subdevice(self, index, factory=Subdevice, **kwargs):
- return factory(device=self, index=index, **kwargs)
-
-
-class Range (object):
- def __init__(self, index, range):
- self.index = index
- self.range = range
-
- def __getattr__(self, name):
- return getattr(self.range, name)
-
-
-class Command (object):
- """Wrap `comedi.comedi_cmd` with a nicer interface.
-
- Examples
- --------
-
- >>> from .utility import set_cmd_chanlist, set_cmd_data
- >>> CMDF = _constants.CMDF
- >>> TRIG_SRC = _constants.TRIG_SRC
- >>> c = Command()
- >>> c.subdev = 1
- >>> c.flags = CMDF.priority | CMDF.write
- >>> c.start_src = TRIG_SRC.int | TRIG_SRC.now
- >>> c.scan_begin_src = TRIG_SRC.timer
- >>> c.scan_begin_arg = 10
- >>> c.scan_convert_src = TRIG_SRC.now
- >>> c.scan_end_src = TRIG_SRC.count
- >>> c.scan_end_arg = 4
- >>> c.stop_src = TRIG_SRC.none
- >>> set_cmd_chanlist(c, [])
- >>> set_cmd_data(c, [1,2,3])
- >>> print c # doctest: +ELLIPSIS, +REPORT_UDIFF
- Comedi command:
- subdev : 1
- flags : priority|write
- start_src : now|int
- start_arg : 0
- scan_begin_src : timer
- scan_begin_arg : 10
- convert_src : -
- convert_arg : 0
- scan_end_src : count
- scan_end_arg : 4
- stop_src : none
- stop_arg : 0
- chanlist : <comedi.lsampl_array... at 0x...>
- chanlist_len : 0
- data : <comedi.sampl_array... at 0x...>
- data_len : 3
- """
- _str_fields = [
- 'subdev', 'flags', 'start_src', 'start_arg', 'scan_begin_src',
- 'scan_begin_arg', 'convert_src', 'convert_arg', 'scan_end_src',
- 'scan_end_arg', 'stop_src', 'stop_arg', 'chanlist', 'chanlist_len',
- 'data', 'data_len']
-
- def __init__(self):
- self.cmd = _comedi.comedi_cmd_struct()
-
- def _get_flag_field(self, name, flag):
- f = _constants.FlagValue(flag, getattr(self.cmd, name))
- return f
-
- def get_flags(self):
- return self._get_flag_field('flags', _constants.CMDF)
-
- def get_trigger_field(self, name):
- return self._get_flag_field(name, _constants.TRIG_SRC)
-
- def __str__(self):
- values = {}
- for f in self._str_fields:
- if f.endswith('_src'):
- values[f] = str(self.get_trigger_field(f))
- elif f == 'flags':
- values[f] = str(self.get_flags())
- else:
- values[f] = getattr(self, f)
- max_len = max([len(f) for f in self._str_fields])
- lines = ['%*s : %s' % (max_len, f, values[f])
- for f in self._str_fields]
- return 'Comedi command:\n %s' % '\n '.join(lines)
-
- def __setattr__(self, name, value):
- if name == 'cmd':
- return super(Command, self).__setattr__(name, value)
- return setattr(self.cmd, name, _comedi_arg(value))
-
- def __getattr__(self, name):
- return getattr(self.cmd, name) # TODO: lookup _NamedInt?
-
-
-class DataChannel (Channel):
- """Channel configured for reading data.
-
- `range` should be a `Range` instance, `aref` should be an
- `constants.AREF` instance,
- """
- def __init__(self, range=None, aref=None, **kwargs):
- super(DataChannel, self).__init__(**kwargs)
- self.range = range
- self.aref = aref
-
- # syncronous stuff
-
- def data_read(self):
- "Read one sample"
- read,data = _comedi_tup(
- 'comedi_data_read', self._subdevice._device._device,
- self._subdevice._index, self._index, self.range.index, self.aref)
- return data
-
- def data_read_n(self, n):
- "Read `n` samples (timing between samples is undefined)."
- read,data = _comedi_tup(
- 'comedi_data_read', self._subdevice._device._device,
- self._subdevice._index, self._index, self.range.index, self.aref,
- n)
- return data
-
- def data_read_hint(self):
- """Tell driver which channel/range/aref you will read next
-
- Used to prepare an analog input for a subsequent call to
- comedi_data_read. It is not necessary to use this function,
- but it can be useful for eliminating inaccuaracies caused by
- insufficient settling times when switching the channel or gain
- on an analog input. This function sets an analog input to the
- channel, range, and aref specified but does not perform an
- actual analog to digital conversion.
-
- Alternatively, one can simply use `.data_read_delayed()`, which
- sets up the input, pauses to allow settling, then performs a
- conversion.
- """
- _comedi_int(
- 'comedi_data_read_hint', self._subdevice._device._device,
- self._subdevice._index, self._index, self.range.index,
- self.aref.value)
-
- def data_read_delayed(self, nano_sec=0):
- """Read single sample after delaying specified settling time.
-
- Although the settling time is specified in integer
- nanoseconds, the actual settling time will be rounded up to
- the nearest microsecond.
- """
- read,data = _comedi_tup(
- 'comedi_data_read_delayed', self._subdevice._device._device,
- self._subdevice._index, self._index, self.range.index,
- self.aref.value, int(nano_sec))
- return data
-
- def data_write(self, data):
- "Write one sample"
- written = _comedi_int(
- 'comedi_data_write', self._subdevice._device._device,
- self._subdevice._index, self._index, self.range.index, self.aref,
- int(data))
- return written
-
- def dio_config(self, dir):
- """Change input/output properties
-
- `dir` should be an item from `constants.IO_DIRECTION`.
- """
- _comedi_int(
- 'comedi_dio_config', self._subdevice._device._device,
- self._subdevice._index, self._index, dir)
-
- def _dio_get_config(self):
- "Query input/output properties"
- return _comedi_int(
- 'comedi_dio_get_config', self._subdevice._device._device,
- self._subdevice._index, self._index)
-
- def dio_get_config(self):
- """Query input/output properties
-
- Return an item from `constants.IO_DIRECTION`.
- """
- return _constants.IO_DIRECTION.index_by_value(self._dio_get_config())
-
- def dio_read(self):
- "Read a single bit"
- rc,bit = _comedi_tup(
- 'comedi_dio_read', self._subdevice._device._device,
- self._subdevice._index, self._index)
- return bit
-
- def dio_write(self, bit):
- "Write a single bit"
- return _comedi_int(
- 'comedi_dio_write', self._subdevice._device._device,
- self._subdevice._index, self._index, bit)
-
- def cr_pack(self):
- return _comedi.cr_pack(self._index, self.range.index, self.aref.value)
-
-
-class SlowlyVaryingChannel (Channel):
- "Slowly varying channel"
- def __init__(self, **kwargs):
- super(SlowlyVaryingChannel, self).__init__(**kwargs)
- self._sv = _comedi.comedi_sv_t()
- self.init()
-
- def init(self):
- "Initialise `._sv`"
- _comedi_int(
- 'comedi_sv_init', self._sv, self._subdevice._device._device,
- self._subdevice._index, self._index)
-
- def update(self):
- "Update internal `._sv` parameters."
- _comedi_int('comedi_sv_update', self._sv)
-
- def measure(self):
- """Measure a slowy varying signal.
-
- Returns `(num_samples, physical_value)`.
- """
- return _comedi_tup(
- 'comedi_sv_measure', self._sv)
-
-
-class StreamingSubdevice (Subdevice):
- "Streaming I/O channel"
- def __init__(self, **kwargs):
- super(StreamingSubdevice, self).__init__(**kwargs)
- self.cmd = Command()
-
- def get_cmd_src_mask(self):
- """Detect streaming input/output capabilities
-
- The command capabilities of the subdevice indicated by the
- parameters device and subdevice are probed, and the results
- placed in the command structure *command. The trigger source
- elements of the command structure are set to be the bitwise-or
- of the subdevice's supported trigger sources. Other elements
- in the structure are undefined.
- """
- rc = _comedi_int(
- 'comedi_get_cmd_src_mask', self._device._device, self._index,
- self.cmd)
-
- def get_cmd_generic_timed(self, chanlist_len, scan_period_ns=0):
- """Detect streaming input/output capabilities
-
- The command capabilities of the subdevice indicated by the
- parameters device and subdevice are probed, and the results
- placed in the command structure pointed to by the parameter
- command. The command structure *command is modified to be a
- valid command that can be used as a parameter to
- comedi_command (after the command has additionally been
- assigned a valid chanlist array). The command measures scans
- consisting of chanlist_len channels at a scan rate that
- corresponds to a period of scan_period_ns nanoseconds. The
- rate is adjusted to a rate that the device can handle.
- """
- rc = _comedi_int(
- 'comedi_get_cmd_generic_timed', self._device._device, self._index,
- self.cmd, chanlist_len, scan_period_ns)
-
- def cancel(self):
- "Stop streaming input/output in progress."
- _comedi_int('comedi_cancel', self._device._device, self._index)
-
- def command(self):
- "Start streaming input/output"
- _comedi_int('comedi_command', self._device._device, self.cmd)
-
- _command_test_errors = [
- None, # valid
- 'unsupported *_src trigger', # unsupported trigger bits zeroed
- 'unsupported *_src combo, or multiple triggers',
- '*_arg out of range', # offending members adjusted to valid values
- '*_arg required adjustment', # e.g. trigger timing period rounded
- 'invalid chanlist', # e.g. some boards require same range across chans
- ]
-
- def command_test(self):
- "Test streaming input/output configuration"
- rc = _comedi.comedi_command_test(
- self._device._device, _comedi_arg(self.cmd))
- return self._command_test_errors[rc]
-
- def poll(self):
- """Force updating of streaming buffer
-
- If supported by the driver, all available samples are copied
- to the streaming buffer. These samples may be pending in DMA
- buffers or device FIFOs. If successful, the number of
- additional bytes available is returned.
- """
- return _comedi_int('comedi_poll', self._device._device, self._index)
-
- def get_buffer_size(self):
- "Streaming buffer size of subdevice"
- return _comedi_int(
- 'comedi_get_buffer_size', self._device._device, self._index)
-
- def set_buffer_size(self, size):
- """Change the size of the streaming buffer
-
- Returns the new buffer size in bytes.
-
- The buffer size will be set to size bytes, rounded up to a
- multiple of the virtual memory page size. The virtual memory
- page size can be determined using `sysconf(_SC_PAGE_SIZE)`.
-
- This function does not require special privileges. However, it
- is limited to a (adjustable) maximum buffer size, which can be
- changed by a priveliged user calling
- `.comedi_set_max_buffer_size`, or running the program
- `comedi_config`.
- """
- return _comedi_int(
- 'comedi_set_buffer_size',
- self._device._device, self._index, size)
-
- def get_max_buffer_size(self):
- "Maximum streaming buffer size of subdevice"
- return _comedi_int(
- 'comedi_get_max_buffer_size', self._device._device, self._index)
-
- def set_max_buffer_size(self, max_size):
- """Set the maximum streaming buffer size of subdevice
-
- Returns the old (max?) buffer size on success.
- """
- return _comedi_int(
- 'comedi_set_max_buffer_size', self._device._device, self._index,
- max_size)
-
- def get_buffer_contents(self):
- "Number of bytes available on an in-progress command"
- return _comedi_int(
- 'comedi_get_buffer_contents', self._device._device, self._index)
-
- def mark_buffer_read(self, num_bytes):
- """Next `num_bytes` bytes in the buffer are no longer needed
-
- Returns the number of bytes successfully marked as read.
-
- This method should only be used if you are using a `mmap()` to
- read data from Comedi's buffer (as opposed to calling `read()`
- on the device file), since Comedi will automatically keep
- track of how many bytes have been transferred via `read()`
- calls.
- """
- return _comedi_int(
- 'comedi_mark_buffer_read', self._device._device, self._index,
- num_bytes)
-
- def mark_buffer_written(self, num_bytes):
- """Next `num_bytes` bytes in the buffer are no longer needed
-
- Returns the number of bytes successfully marked as written.
-
- This method should only be used if you are using a `mmap()` to
- read data from Comedi's buffer (as opposed to calling
- `write()` on the device file), since Comedi will automatically
- keep track of how many bytes have been transferred via
- `write()` calls.
- """
- return _comedi_int(
- 'comedi_mark_buffer_written', self._device._device, self._index,
- num_bytes)
-
- def get_buffer_offset(self):
- """Offset in bytes of the read(/write?) pointer in the streaming buffer
-
- This offset is only useful for memory mapped buffers.
- """
- return _comedi_int(
- 'comedi_get_buffer_offset', self._device._device, self._index)
-
-
-class Chanlist (list):
- def chanlist(self):
- ret = _comedi.chanlist(len(self))
- for i,channel in enumerate(self):
- ret[i] = channel.cr_pack()
- return ret
-
-
-class CalibratedConverter (object):
- """Apply a converion polynomial
-
- Usually you would get the conversion polynomial from
- `Channel.get_hardcal_converter()` or similar. bit for testing,
- we'll just create one out of thin air.
-
- TODO: we'll need to use Cython, because the current SWIG bindings
- don't provide a way to create or edit `double *` arrays.
-
- >>> p = _comedi.comedi_polynomial_t()
- >>> p.order = 2
- >>> p.coefficients[0] = 1 # this fails. Silly SWIG.
- >>> p.coefficients[1] = 2
- >>> p.coefficients[2] = 3
- >>> dir(p.coefficients)
- >>> p.coefficients = _numpy.array([1, 2, 3, 4], dtype=_numpy.double)
- >>> p.expansion_origin = -1;
-
- >>> c = CalibratedConverter(polynomial=p)
- >>> c(-1)
- >>> c(_numpy.array([-1, 0, 0.5, 2], dtype=_numpy.double))
- """
- def __init__(self, polynomial):
- self.polynomial = polynomial
-
- def __call__(self, data):
- # Iterating through the coefficients fails. Silly SWIG.
- coefficients = list(reversed(self.polynomial.coefficients))[0:p.order]
- print coefficients
- print self.polynomial.expansion_origin
- return _numpy.polyval(
- coefficients, data-self.polynomial.expansion_origin)
-
-# see comedi_caldac_t and related at end of comedilib.h
--- /dev/null
+# Copyright
+
+"Expose `Command` internals at the C level for other Cython modules"
+
+cimport _comedi_h
+
+
+cdef class Command (object):
+ cdef _comedi_h.comedi_cmd _cmd
+ cdef public list _fields
+
+ cdef _comedi_h.comedi_cmd *get_comedi_cmd_pointer(self)
--- /dev/null
+# Copyright
+
+"Wrap Comedi's `comedi_cmd` struct in the `Command` class"
+
+cimport libc.stdlib as _stdlib
+import numpy as _numpy
+
+cimport _comedi_h
+cimport _comedilib_h
+from pycomedi import PyComediError as _PyComediError
+from chanspec import ChanSpec as _ChanSpec
+import constant as _constant
+
+
+cdef class Command (object):
+ """A Comedi command
+
+ >>> from .constant import AREF, CMDF, TRIG_SRC
+ >>> from .channel import AnalogChannel
+ >>> from .chanspec import ChanSpec
+ >>> from .device import Device
+
+ >>> c = Command()
+ >>> print str(c)
+ subdev: 0
+ flags: -
+ start_src: -
+ start_arg: 0
+ scan_begin_src: -
+ scan_begin_arg: 0
+ convert_src: -
+ convert_arg: 0
+ scan_end_src: -
+ scan_end_arg: 0
+ stop_src: -
+ stop_arg: 0
+ chanlist: []
+ data: []
+
+ `data` takes any iterable that supports `length()` and returns NumPy arrays.
+
+ >>> c.data = [1, 2, 3]
+ >>> type(c.data)
+ <type 'numpy.ndarray'>
+
+ `subdev` is currently just an integer (not a `Subdevice` instance).
+
+ >>> c.subdev = 3
+ >>> c.subdev
+ 3L
+ >>> type(c.subdev)
+ <type 'long'>
+
+ `flags` and trigger sources return `FlagValue` instances.
+
+ >>> c.flags # doctest: +ELLIPSIS
+ <pycomedi.constant.FlagValue object at 0x...>
+ >>> c.flags = CMDF.priority | CMDF.write
+
+ >>> c.start_src # doctest: +ELLIPSIS
+ <pycomedi.constant.FlagValue object at 0x...>
+ >>> c.start_src = TRIG_SRC.int | TRIG_SRC.now
+ >>> c.scan_begin_src = TRIG_SRC.timer
+ >>> c.scan_begin_arg = 10
+ >>> c.convert_src = TRIG_SRC.now
+ >>> c.scan_end_src = TRIG_SRC.count
+ >>> c.scan_end_arg = 4
+ >>> c.stop_src = TRIG_SRC.none
+
+ Because `ChanSpec` instances store their value internally (not
+ using the value stored in the `Command` instance), direct
+ operations on them have no effect on the intruction.
+
+ >>> chanlist = [
+ ... ChanSpec(chan=0, aref=AREF.diff),
+ ... ChanSpec(chan=1, aref=AREF.diff),
+ ... ]
+ >>> c.chanlist = chanlist
+ >>> c.chanlist[0]
+ <ChanSpec chan:0 range:0 aref:diff flags:->
+ >>> c.chanlist[0].aref = AREF.ground
+ >>> c.chanlist[0]
+ <ChanSpec chan:0 range:0 aref:diff flags:->
+
+ To have an effect, you need to explicity set the `chanlist` attribute:
+
+ >>> chanlist = c.chanlist
+ >>> chanlist[0].aref = AREF.ground
+ >>> c.chanlist = chanlist
+ >>> c.chanlist[0]
+ <ChanSpec chan:0 range:0 aref:ground flags:->
+
+ You can also set chanspec items with `AnalogChannel` instances (or
+ any object that has a `chanspec` method).
+
+ >>> d = Device('/dev/comedi0')
+ >>> d.open()
+ >>> subdevice = d.get_read_subdevice()
+ >>> c.chanlist = [subdevice.channel(1, factory=AnalogChannel,
+ ... aref=AREF.diff)]
+ >>> d.close()
+
+ >>> print str(c)
+ subdev: 3
+ flags: priority|write
+ start_src: now|int
+ start_arg: 0
+ scan_begin_src: timer
+ scan_begin_arg: 10
+ convert_src: now
+ convert_arg: 0
+ scan_end_src: count
+ scan_end_arg: 4
+ stop_src: none
+ stop_arg: 0
+ chanlist: [<ChanSpec chan:1 range:0 aref:diff flags:->]
+ data: [1 2 3]
+ """
+ def __cinit__(self):
+ self._cmd.chanlist = NULL
+ self._cmd.data = NULL
+ self._fields = [
+ 'subdev', 'flags', 'start_src', 'start_arg', 'scan_begin_src',
+ 'scan_begin_arg', 'convert_src', 'convert_arg', 'scan_end_src',
+ 'scan_end_arg', 'stop_src', 'stop_arg', 'chanlist', 'data']
+
+ def __dealloc__(self):
+ if self._cmd.chanlist is not NULL:
+ _stdlib.free(self._cmd.chanlist)
+ if self._cmd.data is not NULL:
+ _stdlib.free(self._cmd.data)
+
+ cdef _comedi_h.comedi_cmd *get_comedi_cmd_pointer(self):
+ return &self._cmd
+
+ def __str__(self):
+ max_field_length = max([len(f) for f in self._fields])
+ lines = []
+ for f in self._fields:
+ lines.append('%*s: %s' % (max_field_length, f, getattr(self, f)))
+ return '\n'.join(lines)
+
+ def _subdev_get(self):
+ return self._cmd.subdev
+ def _subdev_set(self, value):
+ self._cmd.subdev = _constant.bitwise_value(value)
+ subdev = property(fget=_subdev_get, fset=_subdev_set)
+
+ def _flags_get(self):
+ return _constant.FlagValue(_constant.CMDF, self._cmd.flags)
+ def _flags_set(self, value):
+ self._cmd.flags = _constant.bitwise_value(value)
+ flags = property(fget=_flags_get, fset=_flags_set)
+
+ def _start_src_get(self):
+ return _constant.FlagValue(_constant.TRIG_SRC, self._cmd.start_src)
+ def _start_src_set(self, value):
+ self._cmd.start_src = _constant.bitwise_value(value)
+ start_src = property(fget=_start_src_get, fset=_start_src_set)
+
+ def _start_arg_get(self):
+ return self._cmd.start_arg
+ def _start_arg_set(self, value):
+ self._cmd.start_arg = value
+ start_arg = property(fget=_start_arg_get, fset=_start_arg_set)
+
+ def _scan_begin_src_get(self):
+ return _constant.FlagValue(_constant.TRIG_SRC, self._cmd.scan_begin_src)
+ def _scan_begin_src_set(self, value):
+ self._cmd.scan_begin_src = _constant.bitwise_value(value)
+ scan_begin_src = property(fget=_scan_begin_src_get, fset=_scan_begin_src_set)
+
+ def _scan_begin_arg_get(self):
+ return self._cmd.scan_begin_arg
+ def _scan_begin_arg_set(self, value):
+ self._cmd.scan_begin_arg = value
+ scan_begin_arg = property(fget=_scan_begin_arg_get, fset=_scan_begin_arg_set)
+
+ def _convert_src_get(self):
+ return _constant.FlagValue(_constant.TRIG_SRC, self._cmd.convert_src)
+ def _convert_src_set(self, value):
+ self._cmd.convert_src = _constant.bitwise_value(value)
+ convert_src = property(fget=_convert_src_get, fset=_convert_src_set)
+
+ def _convert_arg_get(self):
+ return self._cmd.convert_arg
+ def _convert_arg_set(self, value):
+ self._cmd.convert_arg = value
+ convert_arg = property(fget=_convert_arg_get, fset=_convert_arg_set)
+
+
+ def _scan_end_src_get(self):
+ return _constant.FlagValue(_constant.TRIG_SRC, self._cmd.scan_end_src)
+ def _scan_end_src_set(self, value):
+ self._cmd.scan_end_src = _constant.bitwise_value(value)
+ scan_end_src = property(fget=_scan_end_src_get, fset=_scan_end_src_set)
+
+ def _scan_end_arg_get(self):
+ return self._cmd.scan_end_arg
+ def _scan_end_arg_set(self, value):
+ self._cmd.scan_end_arg = value
+ scan_end_arg = property(fget=_scan_end_arg_get, fset=_scan_end_arg_set)
+
+ def _stop_src_get(self):
+ return _constant.FlagValue(_constant.TRIG_SRC, self._cmd.stop_src)
+ def _stop_src_set(self, value):
+ self._cmd.stop_src = _constant.bitwise_value(value)
+ stop_src = property(fget=_stop_src_get, fset=_stop_src_set)
+
+ def _stop_arg_get(self):
+ return self._cmd.stop_arg
+ def _stop_arg_set(self, value):
+ self._cmd.stop_arg = value
+ stop_arg = property(fget=_stop_arg_get, fset=_stop_arg_set)
+
+ def _chanlist_get(self):
+ ret = list()
+ for i in range(self._cmd.chanlist_len):
+ c = _ChanSpec()
+ c.value = self._cmd.chanlist[i]
+ ret.append(c)
+ return ret
+ def _chanlist_set(self, value):
+ if self._cmd.chanlist is not NULL:
+ _stdlib.free(self._cmd.chanlist)
+ self._cmd.chanlist_len = len(value)
+ self._cmd.chanlist = <unsigned int *>_stdlib.malloc(
+ self._cmd.chanlist_len*sizeof(unsigned int))
+ if self._cmd.chanlist is NULL:
+ self._cmd.chanlist_len = 0
+ raise _PyComediError('out of memory?')
+ for i,x in enumerate(value):
+ if hasattr(x, 'chanspec'):
+ x = x.chanspec()
+ self._cmd.chanlist[i] = _constant.bitwise_value(x)
+ chanlist = property(fget=_chanlist_get, fset=_chanlist_set)
+
+ def _data_get(self):
+ data = _numpy.ndarray(shape=(self._cmd.data_len,), dtype=_numpy.uint16)
+ # TODO: point into existing data array?
+ for i in range(self._cmd.data_len):
+ data[i] = self._cmd.data[i]
+ return data
+ def _data_set(self, value):
+ if self._cmd.data is not NULL:
+ _stdlib.free(self._cmd.data)
+ self._cmd.data_len = len(value)
+ self._cmd.data = <_comedi_h.sampl_t *>_stdlib.malloc(
+ self._cmd.data_len*sizeof(_comedi_h.sampl_t))
+ if self._cmd.data is NULL:
+ self._cmd.data_len = 0
+ raise _PyComediError('out of memory?')
+ for i,x in enumerate(value):
+ self._cmd.data[i] = x
+ data = property(fget=_data_get, fset=_data_set)
--- /dev/null
+# Copyright
+
+"Expose `BitwiseOperator` internals at the C level for other Cython modules"
+
+cdef class BitwiseOperator (object):
+ cdef public object value
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
-"""Enums and flags are bundled into class instances for easier browsing.
+"""Enums and flags are bundled into class instances for easier browsing
>>> SUBDEVICE_TYPE # doctest: +NORMALIZE_WHITESPACE
[<_NamedInt unused>, <_NamedInt ai>, <_NamedInt ao>, <_NamedInt di>,
>>> a = TRIG_SRC.now | TRIG_SRC.follow | TRIG_SRC.time | 64
>>> a
-<_BitwiseOperator 78>
+<BitwiseOperator 78>
>>> a.value
78
>>> TRIG_SRC.none & TRIG_SRC.now
-<_BitwiseOperator 0>
+<BitwiseOperator 0>
-But because of the way Python operator overloading works, plain
-integers must go at the end of bitwise chains.
+Because of the way Python operator overloading works [#ops]_, you can
+also start a bitwise chain with an integer.
>>> 64 | TRIG_SRC.now
-Traceback (most recent call last):
- ...
-TypeError: unsupported operand type(s) for |: 'int' and '_NamedInt'
+<BitwiseOperator 66>
+
+.. [#ops] See `emulating numeric types`_ and `NotImplementedError` in
+ `the standard type hierarchy`_.
+
+.. _emulating numeric types:
+ http://docs.python.org/reference/datamodel.html#emulating-numeric-types
+.. _the standard type hierarchy`_
+ http://docs.python.org/reference/datamodel.html#the-standard-type-hierarchy
"""
from math import log as _log
+import sys as _sys
+import numpy as _numpy
import comedi as _comedi
+from pycomedi import LOG as _LOG
+
+
+def bitwise_value(object):
+ """Convenience function for flexible value specification
+
+ This funciton makes it easy for functions and methods to accept
+ either integers or `BitwiseOperator` instances as integer
+ parameters.
+ """
+ if isinstance(object, BitwiseOperator):
+ return object.value
+ return object
+
+
+def _pso(s, o):
+ """External definition of staticmethod until Cython supports the
+ usual syntax.
+
+ http://docs.cython.org/src/userguide/limitations.html#behaviour-of-class-scopes
+ """
+ if isinstance(s, BitwiseOperator):
+ s = s.value
+ if isinstance(o, BitwiseOperator):
+ o = o.value
+ return (long(s), long(o))
+
+
+cdef class BitwiseOperator (object):
+ """General class for bitwise operations.
+
+ This class allows chaining bitwise operations between
+ `_NamedInt`\s and similar objects.
+
+ Because flag values can be large, we cast all values to longs
+ before performing any bitwise operations. This avoids issues like
+
+ >>> int.__or__(1073741824, 2147483648L)
+ NotImplemented
+ """
-class _BitwiseOperator (object):
def __init__(self, value):
self.value = value
def __repr__(self):
return '<%s %s>' % (self.__class__.__name__, self.value)
+ _prepare_self_other = staticmethod(_pso)
+
def __and__(self, other):
- "Bitwise and acts on `_BitwiseOperator.value`."
- if isinstance(other, _BitwiseOperator):
- other = other.value
- return _BitwiseOperator(int.__and__(self.value, other))
+ "Bitwise and acts on `BitwiseOperator.value`."
+ s,o = BitwiseOperator._prepare_self_other(self, other)
+ return BitwiseOperator(int(long.__and__(s, o)))
def __or__(self, other):
- "Bitwise or acts on `_BitwiseOperator.value`."
- if isinstance(other, _BitwiseOperator):
- other = other.value
- return _BitwiseOperator(int.__or__(self.value, other))
+ "Bitwise or acts on `BitwiseOperator.value`."
+ s,o = BitwiseOperator._prepare_self_other(self, other)
+ return BitwiseOperator(int(long.__or__(s, o)))
-class _NamedInt (_BitwiseOperator):
+class _NamedInt (BitwiseOperator):
"A flag or enum item."
def __init__(self, name, value, doc=None):
super(_NamedInt, self).__init__(value)
class _Enum (list):
"An enumerated list"
- def __init__(self, name, prefix, blacklist=None, whitelist=None,
+ def __init__(self, name, prefix='', blacklist=None, whitelist=None,
translation=None):
super(_Enum, self).__init__()
self.name = name
def _add_item(self, attr, item_name):
item_value = getattr(_comedi, attr)
+ if item_value < 0:
+ _LOG.debug('big value for {0:s}: {1:d} ({1:b}) converted to {2:d} ({2:b})'.format(
+ attr, item_value, (1<<32) + item_value))
+ item_value = (1<<32) + item_value # flags are unsigned 32 bit integers, but SWIG signs them
item = _NamedInt(item_name, item_value, doc=attr)
self.append(item)
- self._name_keys[item_name] = item
- if item_value in self._value_keys and item_name:
+
+ def append(self, item):
+ super(_Enum, self).append(item)
+ self._name_keys[item.name] = item
+ if item.value in self._value_keys and item.name:
raise ValueError('value collision in %s: %s = %s = %#x'
- % (self.name, item_name,
- self._value_keys[item_value], item_value))
- self._value_keys[item_value] = item
- setattr(self, item_name, item)
+ % (self.name, item.name,
+ self._value_keys[item.value], item.value))
+ self._value_keys[item.value] = item
+ setattr(self, item.name, item)
+
def index_by_name(self, name):
return self._name_keys[name]
for flag in self:
if flag.value == 0:
self._empty = flag
- elif flag.value < 0 or _log(flag.value, 2) % 1 != 0:
+ elif flag.value < 0 or abs(_log(flag.value, 2)) % 1 > 1e-13: # deal with rounding errors
if self._all:
raise ValueError(
'mutliple multi-bit flags in %s: %s = %#x and %s = %#x'
# blacklist deprecated values (and those belonging to other _Enums or _Flags)
+CR = _Flag('ChanSpec flags', 'CR_', blacklist=['dither', 'deglitch'])
+CR.alt_filter.doc += ' (can also mean "dither" or "deglitch")'
+
AREF = _Enum('analog_reference', 'AREF_')
AREF.diff.doc += ' (differential)'
AREF.other.doc += ' (other / undefined)'
SDF.locked.doc += ' (subdevice is locked)'
SDF.lock_owner.doc += ' (you own lock)'
SDF.maxdata.doc += ' (maxdata depends on channel)'
-SDF.flags.doc += ' (flags depend on channel)'
+SDF.flags.doc += ' (flags depend on channel (BROKEN))'
SDF.rangetype.doc += ' (range type depends on channel)'
SDF.soft_calibrated.doc += ' (subdevice uses software calibration)'
SDF.cmd_write.doc += ' (can do output commands)'
'unknown_support', 'supported', 'unsupported'])
UNIT = _Enum('unit', 'UNIT_', translation={'mA':'mA'})
-
-CB = _Enum('callback_flags', 'COMEDI_CB_', blacklist=['block', 'eobuf'])
-CB.eos.doc += ' (end of scan)'
-CB.eoa.doc += ' (end of acquisition)'
-CB.error.doc += ' (card error during acquisition)'
-CB.overflow.doc += ' (buffer overflow/underflow)'
+# The mA translation avoids lowercasing to 'ma'.
+
+CALLBACK = _Enum('callback_flags', 'COMEDI_CB_', blacklist=['block', 'eobuf'])
+CALLBACK.eos.doc += ' (end of scan)'
+CALLBACK.eoa.doc += ' (end of acquisition)'
+CALLBACK.error.doc += ' (card error during acquisition)'
+CALLBACK.overflow.doc += ' (buffer overflow/underflow)'
+
+CONVERSION_DIRECTION = _Enum('conversion_direction', 'COMEDI_', whitelist=[
+ 'to_physical', 'from_physical'])
+
+# The following constants aren't declared in comedi.h or comedilib.h,
+# but they should be.
+
+LOGLEVEL = _Enum('log level', '', whitelist=[''])
+LOGLEVEL.append(_NamedInt('silent', 0, doc='Comedilib prints nothing.'))
+LOGLEVEL.append(_NamedInt('bug', 1, doc=(
+ 'Comedilib prints error messages when there is a self-consistency '
+ 'error (i.e., an internal bug (default).')))
+LOGLEVEL.append(_NamedInt('invalid', 2, doc=(
+ 'Comedilib prints an error message when an invalid parameter is '
+ 'passed.')))
+LOGLEVEL.append(_NamedInt('error', 3, doc=(
+ 'Comedilib prints an error message whenever an error is generated '
+ 'in the Comedilib library or in the C library, when called by '
+ 'Comedilib.')))
+LOGLEVEL.append(_NamedInt('debug', 4, doc='Comedilib prints a lot of junk.'))
--- /dev/null
+# Copyright
+
+"Expose `Device` internals at the C level for other Cython modules"
+
+cimport _comedilib_h
+from instruction cimport Insn as _Insn
+
+
+cdef class Device (object):
+ cdef _comedilib_h.comedi_t * device
+ cdef public object file
+ cdef public object filename
+
+ cpdef do_insnlist(self, insnlist)
+ cpdef do_insn(self, _Insn insn)
--- /dev/null
+# Copyright
+
+"Wrap device-wide Comedi functions in a `Device` class"
+
+import os as _os
+cimport libc.stdlib as _stdlib
+
+from pycomedi import LOG as _LOG
+from pycomedi import PyComediError as _PyComediError
+cimport _comedi_h
+cimport _comedilib_h
+import _error
+from instruction cimport Insn as _Insn
+from instruction import Insn as _Insn
+from subdevice import Subdevice as _Subdevice
+
+
+cdef class Device (object):
+ """A Comedi device
+
+ >>> from . import constant
+
+ >>> d = Device('/dev/comediX')
+ >>> d.filename
+ '/dev/comediX'
+
+ > d.open() # TODO: re-enable when there is a way to clear comedi_errno
+ Traceback (most recent call last):
+ ...
+ PyComediError: comedi_open (/dev/comediX): No such file or directory (None)
+ >>> d.filename = '/dev/comedi0'
+ >>> d.open()
+ >>> d.fileno()
+ 3
+ >>> d.get_n_subdevices()
+ 14
+ >>> d.get_version_code()
+ (0, 0, 76)
+ >>> d.get_driver_name()
+ 'ni_pcimio'
+ >>> s = d.get_read_subdevice()
+ >>> s.index
+ 0
+ >>> s = d.get_write_subdevice()
+ >>> s.index
+ 1
+ >>> s = d.find_subdevice_by_type(constant.SUBDEVICE_TYPE.calib)
+ >>> s.index
+ 5
+ >>> s = d.find_subdevice_by_type(constant.SUBDEVICE_TYPE.pwm)
+ Traceback (most recent call last):
+ ...
+ PyComediError: comedi_find_subdevice_by_type: Success (-1)
+
+ As a test instruction, we'll get the time of day, which fills in
+ the data field with `[seconds, microseconds]`.
+
+ >>> insn = d.insn()
+ >>> insn.insn = constant.INSN.gtod
+ >>> insn.data = [0, 0] # space where the time value will be stored
+ >>> print str(insn)
+ insn: gtod
+ data: [0 0]
+ subdev: 0
+ chanspec: <ChanSpec chan:0 range:0 aref:ground flags:->
+ >>> d.do_insn(insn)
+ 2
+ >>> print insn.data # doctest: +SKIP
+ [1297377578 105790]
+ >>> insn.data = [0, 0]
+ >>> d.do_insnlist([insn])
+ 1
+ >>> print insn.data # doctest: +SKIP
+ [1297377578 110559]
+
+ >>> d.get_default_calibration_path()
+ '/var/lib/comedi/calibrations/ni_pcimio_pci-6052e_comedi0'
+
+ >>> list(d.subdevices()) # doctest: +ELLIPSIS
+ [<pycomedi.subdevice.Subdevice object at 0x...>,...]
+
+ >>> d.close()
+ """
+ def __cinit__(self):
+ self.device = NULL
+ self.file = None
+ self.filename = None
+
+ def __init__(self, filename):
+ super(Device, self).__init__()
+ self.filename = filename
+
+ def open(self):
+ "Open device"
+ self.device = _comedilib_h.comedi_open(self.filename)
+ if self.device == NULL:
+ _error.raise_error(function_name='comedi_open',
+ error_msg=self.filename)
+ self.file = _os.fdopen(self.fileno(), 'r+')
+
+ def close(self):
+ "Close device"
+ self.file.flush()
+ self.file.close()
+ ret = _comedilib_h.comedi_close(self.device)
+ if ret < 0:
+ _error.raise_error(function_name='comedi_close', ret=ret)
+ self.device = NULL
+ self.file = None
+
+ def fileno(self):
+ "File descriptor for this device"
+ ret = _comedilib_h.comedi_fileno(self.device)
+ if ret < 0:
+ _error.raise_error(function_name='comedi_fileno', ret=ret)
+ return ret
+
+ def get_n_subdevices(self):
+ "Number of subdevices"
+ ret = _comedilib_h.comedi_get_n_subdevices(self.device)
+ if ret < 0:
+ _error.raise_error(function_name='comedi_get_n_subdevices',
+ ret=ret)
+ return ret
+
+ def get_version_code(self):
+ """Comedi version code.
+
+ This is a kernel-module level property, but a valid device is
+ necessary to communicate with the kernel module.
+
+ Returns a tuple of version numbers, e.g. `(0, 7, 61)`.
+ """
+ version = _comedilib_h.comedi_get_version_code(self.device)
+ if version < 0:
+ _error.raise_error(function_name='comedi_get_version_code',
+ ret=version)
+ ret = []
+ for i in range(3):
+ ret.insert(0, version & (2**8-1))
+ version >>= 2**8 # shift over 8 bits
+ return tuple(ret)
+
+ def get_driver_name(self):
+ "Comedi driver name"
+ ret = _comedilib_h.comedi_get_driver_name(self.device)
+ if ret == NULL:
+ _error.raise_error(function_name='comedi_get_driver_name',
+ ret=ret)
+ return ret
+
+ def get_board_name(self):
+ "Comedi board name"
+ ret = _comedilib_h.comedi_get_board_name(self.device)
+ if ret == NULL:
+ _error.raise_error(function_name='comedi_get_driver_name',
+ ret=ret)
+ return ret
+
+ def _get_read_subdevice(self):
+ "Find streaming input subdevice index"
+ ret = _comedilib_h.comedi_get_read_subdevice(self.device)
+ if ret < 0:
+ _error.raise_error(function_name='comedi_get_read_subdevice',
+ ret=ret)
+ return ret
+
+ def get_read_subdevice(self, **kwargs):
+ "Find streaming input subdevice"
+ return self.subdevice(self._get_read_subdevice(), **kwargs)
+
+ def _get_write_subdevice(self):
+ "Find streaming output subdevice index"
+ ret = _comedilib_h.comedi_get_write_subdevice(self.device)
+ if ret < 0:
+ _error.raise_error(function_name='comedi_get_write_subdevice',
+ ret=ret)
+ return ret
+
+ def get_write_subdevice(self, **kwargs):
+ "Find streaming output subdevice"
+ return self.subdevice(self._get_write_subdevice(), **kwargs)
+
+ def _find_subdevice_by_type(self, subdevice_type):
+ "Search for a subdevice index for type `subdevice_type`)."
+ ret = _comedilib_h.comedi_find_subdevice_by_type(
+ self.device, subdevice_type.value, 0) # 0 is starting subdevice
+ if ret < 0:
+ _error.raise_error(function_name='comedi_find_subdevice_by_type',
+ ret=ret)
+ return ret
+
+ def find_subdevice_by_type(self, subdevice_type, **kwargs):
+ """Search for a subdevice by type `subdevice_type`)."
+
+ `subdevice_type` should be an item from `constant.SUBDEVICE_TYPE`.
+ """
+ return self.subdevice(
+ self._find_subdevice_by_type(subdevice_type), **kwargs)
+
+ cpdef do_insnlist(self, insnlist):
+ """Perform multiple instructions
+
+ Returns the number of successfully completed instructions.
+ """
+ cdef _comedi_h.comedi_insnlist il
+ cdef _Insn i
+ il.n_insns = len(insnlist)
+ if il.n_insns == 0:
+ return
+ il.insns = <_comedi_h.comedi_insn *>_stdlib.malloc(
+ il.n_insns*sizeof(_comedi_h.comedi_insn))
+ if il.insns is NULL:
+ raise _PyComediError('out of memory?')
+ try:
+ for j,insn in enumerate(insnlist):
+ i = insn
+ # By copying the pointer to data, changes to this
+ # copied instruction will also affect the original
+ # instruction's data.
+ il.insns[j] = i.get_comedi_insn()
+ ret = _comedilib_h.comedi_do_insnlist(self.device, &il)
+ finally:
+ _stdlib.free(il.insns)
+ if ret < 0:
+ _error.raise_error(function_name='comedi_do_insnlist', ret=ret)
+ return ret
+
+ cpdef do_insn(self, _Insn insn):
+ """Preform a single instruction.
+
+ Returns an instruction-specific integer.
+ """
+ cdef _comedi_h.comedi_insn i
+ # By copying the pointer to data, changes to this
+ # copied instruction will also affect the original
+ # instruction's data.
+ i = insn.get_comedi_insn()
+ ret = _comedilib_h.comedi_do_insn(
+ self.device, &i)
+ if ret < 0:
+ _error.raise_error(function_name='comedi_do_insn', ret=ret)
+ return ret
+
+ def get_default_calibration_path(self):
+ "The default calibration path for this device"
+ assert self.device != NULL, (
+ 'must call get_default_calibration_path on an open device.')
+ ret = _comedilib_h.comedi_get_default_calibration_path(self.device)
+ if ret == NULL:
+ _error.raise_error(
+ function_name='comedi_get_default_calibration_path')
+ return ret
+
+ # extensions to make a more idomatic Python interface
+
+ def insn(self):
+ return _Insn()
+
+ def subdevices(self, **kwargs):
+ "Iterate through all available subdevices."
+ ret = []
+ for i in range(self.get_n_subdevices()):
+ #yield self.subdevice(i, **kwargs)
+ # Generators are not supported in Cython 0.14.1
+ ret.append(self.subdevice(i, **kwargs))
+ return ret
+
+ def subdevice(self, index, factory=_Subdevice, **kwargs):
+ return factory(device=self, index=index, **kwargs)
--- /dev/null
+# Copyright
+
+"Expose `Insn` internals at the C level for other Cython modules"
+
+cimport _comedi_h
+
+
+cdef class Insn (object):
+ cdef _comedi_h.comedi_insn _insn
+ cdef public list _fields
+
+ cdef _comedi_h.comedi_insn get_comedi_insn(self)
--- /dev/null
+# Copyright
+
+"Wrap Comedi's `comedi_insn` struct in the `Insn` class"
+
+cimport libc.stdlib as _stdlib
+import numpy as _numpy
+
+cimport _comedi_h
+cimport _comedilib_h
+from pycomedi import PyComediError as _PyComediError
+from chanspec import ChanSpec as _ChanSpec
+import constant as _constant
+
+
+cdef class Insn (object):
+ """A Comedi instruction
+
+ >>> from .constant import INSN, AREF
+ >>> i = Insn()
+ >>> print str(i)
+ insn: read
+ data: []
+ subdev: 0
+ chanspec: <ChanSpec chan:0 range:0 aref:ground flags:->
+ >>> i.insn = INSN.write
+
+ `data` takes any iterable that supports `length()` and returns NumPy arrays.
+
+ >>> i.data = [1, 2, 3]
+ >>> type(i.data)
+ <type 'numpy.ndarray'>
+
+ `subdev` is currently just an integer (not a `Subdevice` instance).
+
+ >>> i.subdev = 3
+ >>> i.subdev
+ 3
+ >>> type(i.subdev)
+ <type 'int'>
+
+ Because `ChanSpec` instances store their value internally (not
+ using the value stored in the `Insn` instance), direct operations
+ on them have no effect on the intruction.
+
+ >>> i.chanspec.aref = AREF.diff
+ >>> i.chanspec
+ <ChanSpec chan:0 range:0 aref:ground flags:->
+
+ To have an effect, you need to explicity set the `chanspec` attribute:
+
+ >>> c = i.chanspec
+ >>> c.aref = AREF.diff
+ >>> i.chanspec = c
+ >>> i.chanspec
+ <ChanSpec chan:0 range:0 aref:diff flags:->
+
+ >>> print str(i)
+ insn: write
+ data: [1 2 3]
+ subdev: 3
+ chanspec: <ChanSpec chan:0 range:0 aref:diff flags:->
+ """
+ def __cinit__(self):
+ self._insn.insn = _constant.INSN.read.value
+ self._insn.data = NULL
+ self._fields = ['insn', 'data', 'subdev', 'chanspec']
+
+ def __dealloc__(self):
+ if self._insn.data is not NULL:
+ _stdlib.free(self._insn.data)
+
+ cdef _comedi_h.comedi_insn get_comedi_insn(self):
+ return self._insn
+
+ def __str__(self):
+ max_field_length = max([len(f) for f in self._fields])
+ lines = []
+ for f in self._fields:
+ lines.append('%*s: %s' % (max_field_length, f, getattr(self, f)))
+ return '\n'.join(lines)
+
+ def _insn_get(self):
+ return _constant.INSN.index_by_value(self._insn.insn)
+ def _insn_set(self, value):
+ self._insn.insn = _constant.bitwise_value(value)
+ insn = property(fget=_insn_get, fset=_insn_set)
+
+ def _data_get(self):
+ data = _numpy.ndarray(shape=(self._insn.n,), dtype=_numpy.uint)
+ # TODO: point into existing data array?
+ for i in range(self._insn.n):
+ data[i] = self._insn.data[i]
+ return data
+ def _data_set(self, value):
+ if self._insn.data is not NULL:
+ _stdlib.free(self._insn.data)
+ self._insn.n = len(value)
+ self._insn.data = <_comedi_h.lsampl_t *>_stdlib.malloc(
+ self._insn.n*sizeof(_comedi_h.lsampl_t))
+ if self._insn.data is NULL:
+ self._insn.n = 0
+ raise _PyComediError('out of memory?')
+ for i,x in enumerate(value):
+ self._insn.data[i] = x
+ data = property(fget=_data_get, fset=_data_set)
+
+ def _subdev_get(self):
+ return int(self._insn.subdev)
+ def _subdev_set(self, value):
+ self._insn.subdev = value
+ subdev = property(fget=_subdev_get, fset=_subdev_set)
+
+ def _chanspec_get(self):
+ c = _ChanSpec()
+ c.value = self._insn.chanspec
+ return c
+ def _chanspec_set(self, value):
+ self._insn.chanspec = _constant.bitwise_value(value)
+ chanspec = property(fget=_chanspec_get, fset=_chanspec_set)
--- /dev/null
+# Copyright
+
+"Wrap library-wide Comedi functions in a `Device` class"
+
+import os as _os
+
+cimport _comedilib_h
+import constant as _constant
+
+
+def set_loglevel(level):
+ """Control the verbosity of Comedilib debugging and error messages
+
+ This function affects the output of debugging and error messages
+ from Comedilib. By increasing the loglevel, additional debugging
+ information will be printed. Error and debugging messages are
+ printed to the stream stderr.
+
+ The default loglevel can be set by using the environment variable
+ COMEDI_LOGLEVEL. The default loglevel is 1.
+
+ In order to conserve resources, some debugging information is
+ disabled by default when Comedilib is compiled.
+
+ See `constants.LOGLEVEL` for a list of possible levels and their
+ meanings.
+
+ Return value
+ ============
+
+ This function returns the previous loglevel.
+
+ >>> from constant import LOGLEVEL
+ >>> level = set_loglevel(LOGLEVEL.error)
+ >>> level
+ <_NamedInt bug>
+ >>> set_loglevel(level)
+ <_NamedInt error>
+ """
+ ret = _comedilib_h.comedi_loglevel(_constant.bitwise_value(level))
+ return _constant.LOGLEVEL.index_by_value(ret)
--- /dev/null
+# Copyright
+
+"Expose `Range` internals at the C level for other Cython modules"
+
+cimport _comedilib_h
+from constant cimport BitwiseOperator as _BitwiseOperator
+
+
+cdef class Range (_BitwiseOperator):
+ cdef _comedilib_h.comedi_range range
+
+ cdef set_comedi_range(self, _comedilib_h.comedi_range range)
--- /dev/null
+# Copyright
+
+"Wrap `comedi_range` in a Python class"
+
+from constant cimport BitwiseOperator as _BitwiseOperator
+import constant as _constant
+
+
+cdef class Range (_BitwiseOperator):
+ """Stucture displaying a possible channel range
+
+ Warning: You probably want to use `channel.Channel.get_range()` or
+ `channel.Channel.find_range()` rather than initializing this
+ stucture by hand. If you do initialize it by hand (or set any
+ values by hand), remember that it may no longer correspond to your
+ devices built-in range with that index.
+
+ For consistency with other integer wrappers, the range index is
+ stored in the `.value` attribute.
+
+ >>> from constant import UNIT
+ >>> r = Range(1)
+ >>> r
+ <Range unit:volt min:0.0 max:0.0>
+ >>> r.value
+ 1
+ >>> r.unit = UNIT.mA
+ >>> r.min = -2.71828
+ >>> r.max = 3.14159
+ >>> r
+ <Range unit:mA min:-2.71828 max:3.14159>
+ >>> r.unit
+ <_NamedInt mA>
+ """
+ def __cinit__(self):
+ self.value = -1
+
+ def __init__(self, value):
+ self.range.unit = 0
+ self.range.min = 0
+ self.range.max = 0
+ self.value = value
+
+ cdef set_comedi_range(self, _comedilib_h.comedi_range range):
+ self.range = range
+
+ def __str__(self):
+ fields = ['%s:%s' % (f, getattr(self, f))
+ for f in ['unit', 'min', 'max']]
+ return '<%s %s>' % (self.__class__.__name__, ' '.join(fields))
+
+ def __repr__(self):
+ return self.__str__()
+
+ def _unit_get(self):
+ return _constant.UNIT.index_by_value(self.range.unit)
+ def _unit_set(self, value):
+ self.range.unit = _constant.bitwise_value(value)
+ unit = property(fget=_unit_get, fset=_unit_set)
+
+ def _min_get(self):
+ return self.range.min
+ def _min_set(self, value):
+ self.range.min = value
+ min = property(fget=_min_get, fset=_min_set)
+
+ def _max_get(self):
+ return self.range.max
+ def _max_set(self, value):
+ self.range.max = value
+ max = property(fget=_max_get, fset=_max_set)
--- /dev/null
+# Copyright
+
+"Expose `Subdevice` internals at the C level for other Cython modules"
+
+from device cimport Device as _Device
+from command cimport Command as _Command
+
+
+cdef class Subdevice (object):
+ cdef public _Device device
+ cdef public int index
+
+ cpdef dio_bitfield(self, unsigned int bits=*, write_mask=*, base_channel=*)
+
+cdef class StreamingSubdevice (Subdevice):
+ cdef public _Command cmd
+ cdef public list _command_test_errors
--- /dev/null
+# Copyright
+
+"Wrap subdevice-wide Comedi functions in `Subdevice` and related classes"
+
+cimport _comedi_h
+cimport _comedilib_h
+cimport device as _device
+cimport command as _command
+from pycomedi import LOG as _LOG
+import _error
+from channel import Channel as _Channel
+import chanspec as _chanspec
+import constant as _constant
+import command as _command
+from utility import _subdevice_dtype, _subdevice_typecode
+
+
+cdef class Subdevice (object):
+ """Class bundling subdevice-related functions
+
+ >>> from .device import Device
+ >>> from . import constant
+
+ >>> d = Device('/dev/comedi0')
+ >>> d.open()
+
+ >>> s = d.get_read_subdevice()
+ >>> s.get_type()
+ <_NamedInt ai>
+ >>> f = s.get_flags()
+ >>> f # doctest: +ELLIPSIS
+ <pycomedi.constant.FlagValue object at 0x...>
+ >>> print str(f)
+ cmd_read|readable|ground|common|diff|other|dither
+ >>> s.get_n_channels()
+ 16
+ >>> s.range_is_chan_specific()
+ False
+ >>> s.maxdata_is_chan_specific()
+ False
+ >>> s.lock()
+ >>> s.unlock()
+
+ >>> s = d.find_subdevice_by_type(constant.SUBDEVICE_TYPE.dio)
+ >>> s.dio_bitfield()
+ 255L
+
+ >>> s.get_dtype()
+ <type 'numpy.uint16'>
+ >>> s.get_typecode()
+ 'H'
+
+ >>> d.close()
+ """
+ def __cinit__(self):
+ self.index = -1
+
+ def __init__(self, device, index):
+ super(Subdevice, self).__init__()
+ self.device = device
+ self.index = index
+
+ def get_type(self):
+ "Type of subdevice (from `SUBDEVICE_TYPE`)"
+ ret = _comedilib_h.comedi_get_subdevice_type(
+ self.device.device, self.index)
+ if ret < 0:
+ _error.raise_error(function_name='comedi_get_subdevice_type',
+ ret=ret)
+ return _constant.SUBDEVICE_TYPE.index_by_value(ret)
+
+ def _get_flags(self):
+ "Subdevice flags"
+ ret = _comedilib_h.comedi_get_subdevice_flags(
+ self.device.device, self.index)
+ if ret < 0:
+ _error.raise_error(function_name='comedi_get_subdevice_flags',
+ ret=ret)
+ return ret
+
+ def get_flags(self):
+ "Subdevice flags (an `SDF` `FlagValue`)"
+ return _constant.FlagValue(
+ _constant.SDF, self._get_flags())
+
+ def get_n_channels(self):
+ "Number of subdevice channels"
+ ret = _comedilib_h.comedi_get_n_channels(
+ self.device.device, self.index)
+ if ret < 0:
+ _error.raise_error(function_name='comedi_get_n_channels',
+ ret=ret)
+ return ret
+
+ def range_is_chan_specific(self):
+ ret = _comedilib_h.comedi_range_is_chan_specific(
+ self.device.device, self.index)
+ if ret < 0:
+ _error.raise_error(
+ function_name='comedi_range_is_chan_specific', ret=ret)
+ return ret == 1
+
+ def maxdata_is_chan_specific(self):
+ ret = _comedilib_h.comedi_maxdata_is_chan_specific(
+ self.device.device, self.index)
+ if ret < 0:
+ _error.raise_error(
+ function_name='comedi_maxdata_is_chan_specific', ret=ret)
+ return ret == 1
+
+ def lock(self):
+ "Reserve the subdevice"
+ ret = _comedilib_h.comedi_lock(self.device.device, self.index)
+ if ret < 0:
+ _error.raise_error(function_name='comedi_lock', ret=ret)
+
+ def unlock(self):
+ "Release the subdevice"
+ ret = _comedilib_h.comedi_unlock(self.device.device, self.index)
+ if ret < 0:
+ _error.raise_error(function_name='comedi_unlock', ret=ret)
+
+ cpdef dio_bitfield(self, unsigned int bits=0, write_mask=0, base_channel=0):
+ """Read/write multiple digital channels.
+
+ `bits` and `write_mask` are bit fields with the least
+ significant bit representing channel `base_channel`.
+
+ Returns a bit field containing the read value of all input
+ channels and the last written value of all output channels.
+ """
+ ret = _comedilib_h.comedi_dio_bitfield2(
+ self.device.device, self.index, write_mask, &bits, base_channel)
+ if ret < 0:
+ _error.raise_error(function_name='comedi_dio_bitfield2', ret=ret)
+ return bits
+
+ # extensions to make a more idomatic Python interface
+
+ def insn(self):
+ insn = self.device.insn()
+ insn.subdev = self.index
+ return insn
+
+ def channel(self, index, factory=_Channel, **kwargs):
+ "`Channel` instance for the `index`\ed channel."
+ return factory(subdevice=self, index=index, **kwargs)
+
+ def get_dtype(self):
+ "Return the appropriate `numpy.dtype` based on subdevice flags"
+ return _subdevice_dtype(self)
+
+ def get_typecode(self):
+ "Return the appropriate `array` type based on subdevice flags"
+ return _subdevice_typecode(self)
+
+
+cdef class StreamingSubdevice (Subdevice):
+ """Streaming I/O subdevice
+
+ >>> from .device import Device
+ >>> from .chanspec import ChanSpec
+ >>> from . import constant
+
+ >>> d = Device('/dev/comedi0')
+ >>> d.open()
+
+ >>> s = d.get_read_subdevice(factory=StreamingSubdevice)
+
+ >>> cmd = s.get_cmd_src_mask()
+ >>> print str(cmd)
+ subdev: 0
+ flags: -
+ start_src: now|ext|int
+ start_arg: 0
+ scan_begin_src: timer|ext
+ scan_begin_arg: 0
+ convert_src: timer|ext
+ convert_arg: 0
+ scan_end_src: count
+ scan_end_arg: 0
+ stop_src: none|count
+ stop_arg: 0
+ chanlist: []
+ data: []
+
+ >>> chanlist_len = 3
+ >>> cmd = s.get_cmd_generic_timed(chanlist_len=chanlist_len,
+ ... scan_period_ns=1e3)
+ >>> print str(cmd) # doctest: +NORMALIZE_WHITESPACE
+ subdev: 0
+ flags: -
+ start_src: now
+ start_arg: 0
+ scan_begin_src: timer
+ scan_begin_arg: 9000
+ convert_src: timer
+ convert_arg: 3000
+ scan_end_src: count
+ scan_end_arg: 3
+ stop_src: count
+ stop_arg: 2
+ chanlist: [<ChanSpec chan:0 range:0 aref:ground flags:->,
+ <ChanSpec chan:0 range:0 aref:ground flags:->,
+ <ChanSpec chan:0 range:0 aref:ground flags:->]
+ data: []
+
+ >>> cmd.chanlist = [ChanSpec(chan=i, range=0) for i in range(chanlist_len)]
+ >>> s.cmd = cmd
+ >>> s.command_test()
+ >>> s.command()
+ >>> s.cancel()
+
+
+ >>> d.close()
+ """
+ def __cinit__(self):
+ self.cmd = _command.Command()
+ self._command_test_errors = [
+ None, # valid
+ 'unsupported *_src trigger', # unsupported trigger bits zeroed
+ 'unsupported *_src combo, or multiple triggers',
+ '*_arg out of range', # offending members adjusted to valid values
+ '*_arg required adjustment', # e.g. trigger timing period rounded
+ 'invalid chanlist',
+ # e.g. some boards require same range across channels
+ ]
+
+ def get_cmd_src_mask(self):
+ """Detect streaming input/output capabilities
+
+ The command capabilities of the subdevice indicated by the
+ parameters device and subdevice are probed, and the results
+ placed in the command structure *command. The trigger source
+ elements of the command structure are set to be the bitwise-or
+ of the subdevice's supported trigger sources. Other elements
+ in the structure are undefined.
+ """
+ cdef _command.Command cmd
+ cmd = _command.Command()
+ ret = _comedilib_h.comedi_get_cmd_src_mask(
+ self.device.device, self.index, cmd.get_comedi_cmd_pointer())
+ if ret < 0:
+ _error.raise_error(function_name='comedi_get_cmd_src_mask', ret=ret)
+ return cmd
+
+ def get_cmd_generic_timed(self, chanlist_len, scan_period_ns=0):
+ """Detect streaming input/output capabilities
+
+ The command capabilities of the subdevice indicated by the
+ parameters device and subdevice are probed, and the results
+ placed in the command structure pointed to by the parameter
+ command. The command structure *command is modified to be a
+ valid command that can be used as a parameter to
+ comedi_command (after the command has additionally been
+ assigned a valid chanlist array). The command measures scans
+ consisting of chanlist_len channels at a scan rate that
+ corresponds to a period of scan_period_ns nanoseconds. The
+ rate is adjusted to a rate that the device can handle.
+
+ Note that the `ChanSpec` instances in `cmd.chanlist` are not
+ initialized to reasonable values.
+ """
+ cdef _command.Command cmd
+ cmd = _command.Command()
+ ret = _comedilib_h.comedi_get_cmd_generic_timed(
+ self.device.device, self.index, cmd.get_comedi_cmd_pointer(),
+ chanlist_len, int(scan_period_ns))
+ cmd.chanlist = [0 for i in range(chanlist_len)]
+ if ret < 0:
+ _error.raise_error(function_name='comedi_get_cmd_generic_timed',
+ ret=ret)
+ return cmd
+
+ def cancel(self):
+ "Stop streaming input/output in progress."
+ ret = _comedilib_h.comedi_cancel(self.device.device, self.index)
+ if ret < 0:
+ _error.raise_error(function_name='comedi_cancel', ret=ret)
+
+ def command(self):
+ "Start streaming input/output"
+ ret = _comedilib_h.comedi_command(
+ self.device.device, self.cmd.get_comedi_cmd_pointer())
+ if ret < 0:
+ _error.raise_error(function_name='comedi_command', ret=ret)
+
+ def command_test(self):
+ "Test streaming input/output configuration"
+ ret = _comedilib_h.comedi_command_test(
+ self.device.device, self.cmd.get_comedi_cmd_pointer())
+ return self._command_test_errors[ret]
+
+ def poll(self):
+ """Force updating of streaming buffer
+
+ If supported by the driver, all available samples are copied
+ to the streaming buffer. These samples may be pending in DMA
+ buffers or device FIFOs. If successful, the number of
+ additional bytes available is returned.
+ """
+ ret = _comedilib_h.comedi_poll(self.device.device, self.index)
+ if ret < 0:
+ _error.raise_error(function_name='comedi_poll', ret=ret)
+ return ret
+
+ def get_buffer_size(self):
+ "Streaming buffer size of subdevice"
+ ret = _comedilib_h.comedi_get_buffer_size(
+ self.device.device, self.index)
+ if ret < 0:
+ _error.raise_error(function_name='comedi_get_buffer_size', ret=ret)
+ return ret
+
+ def set_buffer_size(self, size):
+ """Change the size of the streaming buffer
+
+ Returns the new buffer size in bytes.
+
+ The buffer size will be set to size bytes, rounded up to a
+ multiple of the virtual memory page size. The virtual memory
+ page size can be determined using `sysconf(_SC_PAGE_SIZE)`.
+
+ This function does not require special privileges. However, it
+ is limited to a (adjustable) maximum buffer size, which can be
+ changed by a priveliged user calling
+ `.comedi_set_max_buffer_size`, or running the program
+ `comedi_config`.
+ """
+ ret = _comedilib_h.comedi_set_buffer_size(
+ self.device.device, self.index, int(size))
+ if ret < 0:
+ _error.raise_error(function_name='comedi_set_buffer_size', ret=ret)
+ return ret
+
+ def get_max_buffer_size(self):
+ "Maximum streaming buffer size of subdevice"
+ ret = _comedilib_h.comedi_get_max_buffer_size(
+ self.device.device, self.index)
+ if ret < 0:
+ _error.raise_error(function_name='comedi_get_max_buffer_size',
+ ret=ret)
+ return ret
+
+ def set_max_buffer_size(self, max_size):
+ """Set the maximum streaming buffer size of subdevice
+
+ Returns the old (max?) buffer size on success.
+ """
+ ret = _comedilib_h.comedi_set_max_buffer_size(
+ self.device.device, self.index, int(max_size))
+ if ret < 0:
+ _error.raise_error(function_name='comedi_set_max_buffer_size',
+ ret=ret)
+ return ret
+
+ def get_buffer_contents(self):
+ "Number of bytes available on an in-progress command"
+ ret = _comedilib_h.comedi_get_buffer_contents(
+ self.device.device, self.index)
+ if ret < 0:
+ _error.raise_error(function_name='comedi_get_buffer_contents',
+ ret=ret)
+ return ret
+
+ def mark_buffer_read(self, num_bytes):
+ """Next `num_bytes` bytes in the buffer are no longer needed
+
+ Returns the number of bytes successfully marked as read.
+
+ This method should only be used if you are using a `mmap()` to
+ read data from Comedi's buffer (as opposed to calling `read()`
+ on the device file), since Comedi will automatically keep
+ track of how many bytes have been transferred via `read()`
+ calls.
+ """
+ ret = _comedilib_h.comedi_mark_buffer_read(
+ self.device.device, self.index, num_bytes)
+ if ret < 0:
+ _error.raise_error(function_name='comedi_mark_buffer_read',
+ ret=ret)
+ return ret
+
+ def mark_buffer_written(self, num_bytes):
+ """Next `num_bytes` bytes in the buffer are no longer needed
+
+ Returns the number of bytes successfully marked as written.
+
+ This method should only be used if you are using a `mmap()` to
+ read data from Comedi's buffer (as opposed to calling
+ `write()` on the device file), since Comedi will automatically
+ keep track of how many bytes have been transferred via
+ `write()` calls.
+ """
+ ret = _comedilib_h.comedi_mark_buffer_written(
+ self.device.device, self.index, num_bytes)
+ if ret < 0:
+ _error.raise_error(function_name='comedi_mark_buffer_written',
+ ret=ret)
+ return ret
+
+ def get_buffer_offset(self):
+ """Offset in bytes of the read(/write?) pointer in the streaming buffer
+
+ This offset is only useful for memory mapped buffers.
+ """
+ ret = _comedilib_h.comedi_get_buffer_offset(
+ self.device.device, self.index)
+ if ret < 0:
+ _error.raise_error(function_name='comedi_get_buffer_offset', ret=ret)
+ return ret
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
-"Useful utility functions and classes."
+"Useful utility functions and classes"
import array as _array
import mmap as _mmap
import threading as _threading
import time as _time
-import comedi as _comedi
import numpy as _numpy
from . import LOG as _LOG
-from . import classes as _classes
-from . import constants as _constants
+from . import constant as _constant
# types from comedi.h
sampl_typecode = 'H'
lsampl_typecode = 'L'
-def subdevice_dtype(subdevice):
+def _subdevice_dtype(subdevice):
"Return the appropriate `numpy.dtype` based on subdevice flags"
if subdevice.get_flags().lsampl:
return lsampl
return sampl
-def subdevice_typecode(subdevice):
+def _subdevice_typecode(subdevice):
"Return the appropriate `array` type based on subdevice flags"
if subdevice.get_flags().lsampl:
return lsampl_typecode
return sampl_typecode
-def sampl_array(list):
- "Convert a Python list/tuple into a `sampl` array"
- ret = _comedi.sampl_array(len(list))
- for i,x in enumerate(list):
- ret[i] = x
- return ret
- #return _array.array(sampl_typecode, list)
-
-def lsampl_array(list):
- "Convert a Python list/tuple into an `lsampl` array"
- ret = _comedi.lsampl_array(len(list))
- for i,x in enumerate(list):
- ret[i] = x
- return ret
- #return _array.array(lsampl_typecode, list)
-
-def set_insn_chanspec(insn, channels):
- "Configure `insn.chanspec` from the list `channels`"
- chanlist = _classes.Chanlist(channels)
- insn.chanspec = chanlist.chanlist()
-
-def set_insn_data(insn, data):
- "Configure `insn.data` and `insn.n` from the iterable `data`"
- insn.n = len(data)
- insn.data = lsampl_array(data)
-
-def set_cmd_chanlist(cmd, channels):
- "Configure `cmd.chanlist` and `cmd.chanlist_len` from the list `channels`"
- chanlist = _classes.Chanlist(channels)
- cmd.chanlist = chanlist.chanlist()
- cmd.chanlist_len = len(chanlist)
-
-def set_cmd_data(cmd, data):
- "Configure `cmd.data` and `cmd.data_len` from the iterable `data`"
- cmd.data = sampl_array(data)
- cmd.data_len = len(data)
-
def inttrig_insn(subdevice):
"""Setup an internal trigger for a given `subdevice`
.. _section 4.4: http://www.comedi.org/doc/x621.html
"""
insn = subdevice.insn()
- insn.insn = _constants.INSN.inttrig.value
- set_insn_data(insn, [0])
+ insn.insn = _constant.INSN.inttrig.value
+ insn.data = [0]
return insn
def __init__(self, subdevice, buffer, name=None):
if name == None:
name = '<%s subdevice %d>' % (
- self.__class__.__name__, subdevice._index)
+ self.__class__.__name__, subdevice.index)
self.subdevice = subdevice
self.buffer = buffer
+ self._setup_buffer()
super(_ReadWriteThread, self).__init__(name=name)
+ def _setup_buffer(self):
+ "Currently just a hook for an MMapWriter hack."
+ pass
+
def _file(self):
"""File for reading/writing data to `.subdevice`
it when you are finished. The file will eventually be closed
when the backing `Device` instance is closed.
"""
- return self.subdevice._device.file
+ return self.subdevice.device.file
class Reader (_ReadWriteThread):
shape=self.buffer.shape, dtype=self.buffer.dtype,
buffer=buf)
self.buffer[:] = a
+ #_LOG.critical('ai running? %s' % self.subdevice.get_flags().running)
+ #while self.subdevice.get_flags().running:
+ ##_LOG.critical('ai running? %s' % self.subdevice.get_flags().running)
+ # _time.sleep(0)
+ #_LOG.critical('ai running? %s' % self.subdevice.get_flags().running)
+ #_time.sleep(1)
class Writer (_ReadWriteThread):
Run the test writer.
- >>> r = TestWriter(subdevice=None, buffer=buf, name='Writer-doctest')
- >>> r.start()
- >>> r.join()
+ >>> preload = 3
+ >>> w = TestWriter(subdevice=None, buffer=buf, name='Writer-doctest',
+ ... preload=preload)
+ >>> a = _array.array('H')
+ >>> a.fromfile(open(t, 'rb'), preload)
+ >>> a
+ array('H', [0, 10, 1])
+ >>> w.start()
+ >>> w.join()
>>> a = _array.array('H')
>>> a.fromfile(open(t, 'rb'), buf.size)
>>> a
>>> f.seek(0)
>>> buf = _array.array('H', [2*x for x in buf.flat])
- >>> r = TestWriter(subdevice=None, buffer=buf, name='Writer-doctest')
- >>> r.start()
- >>> r.join()
+ >>> w = TestWriter(subdevice=None, buffer=buf, name='Writer-doctest',
+ ... preload=preload)
+ >>> a = _array.array('H')
+ >>> a.fromfile(open(t, 'rb'), preload)
+ >>> a
+ array('H', [0, 20, 2])
+ >>> w.start()
+ >>> w.join()
>>> a = _array.array('H')
>>> a.fromfile(open(t, 'rb'), len(buf))
>>> a
>>> f.close() # no need for `close(fd)`
>>> remove(t)
"""
- def run(self):
+ def __init__(self, *args, **kwargs):
+ preload = kwargs.pop('preload', 0)
+ super(Writer, self).__init__(*args, **kwargs)
+ if not _builtin_array(self.buffer): # numpy.ndarray
+ self.buffer = self.buffer.flat
+ preload_buffer = self.buffer[:preload]
+ self._preload_setup = {'remaining_buffer': self.buffer[preload:]}
+ f = self._file()
+ preload_buffer.tofile(f)
+ f.flush()
+
+ def run(self):
+ remaining_buffer = self._preload_setup['remaining_buffer']
+ del(self._preload_setup)
+
f = self._file()
- self.buffer.tofile(f)
+ remaining_buffer.tofile(f)
f.flush()
+ #_LOG.critical('ao running? %s' % self.subdevice.get_flags().running)
+ #while self.subdevice.get_flags().running:
+ ##_LOG.critical('ao running? %s' % self.subdevice.get_flags().running)
+ #_time.sleep(0)
+ #_LOG.critical('ao running? %s' % self.subdevice.get_flags().running)
+ #_time.sleep(1)
class _MMapReadWriteThread (_ReadWriteThread):
"`mmap()`-based reader/wrtier"
def __init__(self, *args, **kwargs):
+ preload = kwargs.pop('preload', 0)
+ access = kwargs.pop('access')
super(_MMapReadWriteThread, self).__init__(*args, **kwargs)
- self._prot = None
-
- def _sleep_time(self, mmap_size):
- "Expected seconds needed to write a tenth of the mmap buffer"
- return 0
- def run(self):
# all sizes measured in bytes
builtin_array = _builtin_array(self.buffer)
- mmap_size = self._mmap_size()
- sleep_time = self._sleep_time(mmap_size)
- mmap = _mmap.mmap(
- self._fileno(), mmap_size, self._prot, _mmap.MAP_SHARED)
- mmap_offset = 0
+ mmap_size = int(self._mmap_size())
+ mmap = _mmap.mmap(self._fileno(), mmap_size, access=access)
buffer_offset = 0
- if builtin_array:
- remaining = len(self.buffer)
- else: # numpy.ndarray
- remaining = self.buffer.size
- remaining *= self.buffer.itemsize
- action = self._initial_action(
- mmap, buffer_offset, remaining,
- mmap_size, action_bytes=mmap_size)
+ remaining = self._buffer_bytes(builtin_array)
+ action,mmap_offset = self._initial_action(
+ mmap, buffer_offset, remaining, mmap_size, action_bytes=mmap_size,
+ builtin_array=builtin_array)
buffer_offset += action
remaining -= action
+ self._preload_setup = {
+ 'builtin_array': builtin_array,
+ 'mmap_size': mmap_size,
+ 'mmap': mmap,
+ 'mmap_offset': mmap_offset,
+ 'buffer_offset': buffer_offset,
+ 'remaining': remaining,
+ }
+ def _sleep_time(self, mmap_size):
+ "Expected seconds needed to write a tenth of the mmap buffer"
+ return 0
+
+ def run(self):
+ builtin_array = self._preload_setup['builtin_array']
+ mmap_size = self._preload_setup['mmap_size']
+ mmap = self._preload_setup['mmap']
+ mmap_offset = self._preload_setup['mmap_offset']
+ buffer_offset = self._preload_setup['buffer_offset']
+ remaining = self._preload_setup['remaining']
+ del(self._preload_setup)
+
+ sleep_time = self._sleep_time(mmap_size)
while remaining > 0:
action_bytes = self._action_bytes()
if action_bytes > 0:
action,mmap_offset = self._act(
- mmap, mmap_offset, buffer_offset, remaining,
- mmap_size, action_bytes=action_bytes,
- builtin_array=builtin_array)
+ mmap, mmap_offset, buffer_offset, remaining, mmap_size,
+ action_bytes=action_bytes, builtin_array=builtin_array)
buffer_offset += action
remaining -= action
else:
wrap = True
else:
wrap = False
- action_size = min(action_bytes, remaining)
+ action_size = min(action_bytes, remaining, mmap_size-mmap_offset)
self._mmap_action(mmap, buffer_offset, action_size, builtin_array)
mmap.flush() # (offset, size), necessary? calls msync?
self._mark_action(action_size)
# hooks for subclasses
+ def _buffer_bytes(self, builtin_array):
+ if builtin_array:
+ return len(self.buffer)*self.buffer.itemsize
+ else: # numpy.ndtype
+ return self.buffer.size*self.buffer.itemsize
+
def _initial_action(self, mmap, buffer_offset, remaining, mmap_size,
- action_bytes=None):
- return 0
+ action_bytes, builtin_array):
+ return (0, 0)
def _mmap_action(self, mmap, offset, size):
raise NotImplementedError()
for _from,_to in [
# convert class and function names
('`read()`', '`mmap()`'),
- ('Writer', 'MMapWriter'),
+ ('Reader', 'MMapReader'),
('def _file', _mmap_docstring_overrides)]:
__doc__ = __doc__.replace(_from, _to)
def __init__(self, *args, **kwargs):
+ assert 'access' not in kwargs
+ kwargs['access'] = _mmap.ACCESS_READ
super(MMapReader, self).__init__(*args, **kwargs)
- self._prot = _mmap.PROT_READ
def _mmap_action(self, mmap, offset, size, builtin_array):
offset /= self.buffer.itemsize
__doc__ = Writer.__doc__
for _from,_to in [
('`write()`', '`mmap()`'),
- ('Reader', 'MMapReader'),
- ('def _file', _mmap_docstring_overrides)]:
+ ('Writer', 'MMapWriter'),
+ ('def _file', _mmap_docstring_overrides),
+ ("f = _os.fdopen(fd, 'r+')",
+ "f = _os.fdopen(fd, 'r+'); f.write(6*'\\x00'); f.flush(); f.seek(0)"),
+ ("a.fromfile(open(t, 'rb'), buf.size)",
+ "a.fromfile(open(t, 'rb'), w._mmap_size()/a.itemsize)"),
+ ("a.fromfile(open(t, 'rb'), len(buf))",
+ "a.fromfile(open(t, 'rb'), w._mmap_size()/a.itemsize)"),
+ ("array('H', [0, 10, 1, 11, 2, 12])", "array('H', [11, 2, 12])"),
+ ("array('H', [0, 20, 2, 22, 4, 24])", "array('H', [22, 4, 24])")]:
+
__doc__ = __doc__.replace(_from, _to)
def __init__(self, *args, **kwargs):
+ assert 'access' not in kwargs
+ kwargs['access'] = _mmap.ACCESS_WRITE
super(MMapWriter, self).__init__(*args, **kwargs)
- self._prot = _mmap.PROT_WRITE
+
+ def _setup_buffer(self):
self.buffer = buffer(self.buffer)
+ def _buffer_bytes(self, builtin_array):
+ return len(self.buffer) # because of buffer() in _setup_buffer
+
+ def _initial_action(self, mmap, buffer_offset, remaining, mmap_size,
+ action_bytes, builtin_array):
+ action_size = min(action_bytes, remaining, mmap_size)
+ self._mmap_action(mmap, buffer_offset, action_size, builtin_array)
+ if action_size == mmap_size:
+ mmap.seek(0)
+ mmap_offset = 0
+ else:
+ mmap_offset = action_size
+ return (action_size, mmap_offset)
+
def _mmap_action(self, mmap, offset, size, builtin_array):
mmap.write(self.buffer[offset:offset+size])
+ mmap.flush()
def _mark_action(self, size):
self.subdevice.mark_buffer_written(size)
"An object-oriented interface for the Comedi drivers."
from distutils.core import setup
+from distutils.extension import Extension
+import os
import os.path
+from Cython.Distutils import build_ext
+import numpy
+
from pycomedi import __version__
+package_name = 'pycomedi'
classifiers = """\
Development Status :: 2 - Pre-Alpha
Intended Audience :: Developers
_this_dir = os.path.dirname(__file__)
-setup(name='pycomedi',
+ext_modules = []
+for filename in sorted(os.listdir(package_name)):
+ basename,extension = os.path.splitext(filename)
+ if extension == '.pyx':
+ ext_modules.append(
+ Extension(
+ '%s.%s' % (package_name, basename),
+ [os.path.join(package_name, filename)],
+ libraries=['comedi'],
+ include_dirs=[numpy.get_include()],
+ ))
+
+setup(name=package_name,
version=__version__,
maintainer='W. Trevor King',
maintainer_email='wking@drexel.edu',
- url='http://www.physics.drexel.edu/~wking/unfolding-disasters/posts/pycomedi/',
- download_url='http://www.physics.drexel.edu/~wking/code/python/pycomedi-%s.tar.gz' % __version__,
+ url='http://www.physics.drexel.edu/~wking/unfolding-disasters/posts/%s/' % package_name,
+ download_url='http://www.physics.drexel.edu/~wking/code/python/%s-%s.tar.gz' % (package_name, __version__),
license='GNU General Public License (GPL)',
platforms=['all'],
description=__doc__,
long_description=open(os.path.join(_this_dir, 'README'), 'r').read(),
classifiers=filter(None, classifiers.split('\n')),
- packages=['pycomedi'],
- provides=['pycomedi'],
+ packages=[package_name],
+ provides=[package_name],
+ cmdclass = {'build_ext': build_ext},
+ ext_modules = ext_modules,
)
--- /dev/null
+#!/bin/bash -e
+# -e to abort script at first error
+
+python setup.py build_ext --inplace
+nosetests --with-doctest pycomedi
+ls pycomedi | grep '.pyx$'| while read file; do
+ mod="${file/.pyx/}"
+ echo "$mod"
+ python -c "import doctest, sys; import pycomedi.$mod as m; r = doctest.testmod(m); print r; sys.exit(r.failed)"
+done
+nosetests --with-doctest --doctest-extension=.txt doc