1 # Copyright (C) 2011-2012 W. Trevor King <wking@drexel.edu>
3 # This file is part of pycomedi.
5 # pycomedi is free software: you can redistribute it and/or modify it under the
6 # terms of the GNU General Public License as published by the Free Software
7 # Foundation, either version 2 of the License, or (at your option) any later
10 # pycomedi is distributed in the hope that it will be useful, but WITHOUT ANY
11 # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
12 # A PARTICULAR PURPOSE. See the GNU General Public License for more details.
14 # You should have received a copy of the GNU General Public License along with
15 # pycomedi. If not, see <http://www.gnu.org/licenses/>.
17 """Pythonic wrappers for converting between Comedilib and physical units
19 For one-off conversions, use the functions `comedi_to_physical` and
20 `comedi_from_physical`. For repeated conversions, use an instance of
21 `CalibratedConverter`.
24 cimport numpy as _numpy
25 import numpy as _numpy
29 import constant as _constant
30 import utility as _utility
32 cdef void _setup_comedi_polynomial_t(
33 _comedilib_h.comedi_polynomial_t *p, coefficients, expansion_origin):
34 """Setup the `comedi_polynomial_t` at `p`
36 * `coefficients` is an iterable containing polynomial coefficients
37 * `expansion_origin` is the center of the polynomial expansion
39 for i,x in enumerate(coefficients):
41 p.order = len(coefficients)-1
42 p.expansion_origin = expansion_origin
45 _comedilib_h.comedi_polynomial_t *p, object data, object direction):
46 """Apply the polynomial conversion `p` to `data`.
48 `direction` should be a value from `constant.CONVERSION_DIRECTION`.
50 to_physical = (_constant.bitwise_value(direction)
51 == _constant.CONVERSION_DIRECTION.to_physical.value)
52 if _numpy.isscalar(data):
54 return _comedilib_h.comedi_to_physical(data, p)
56 return _comedilib_h.comedi_from_physical(data, p)
60 dtype = _utility.lsampl
61 array = _numpy.array(data, dtype=dtype)
62 for i,d in enumerate(data):
64 array[i] = _comedilib_h.comedi_to_physical(d, p)
66 array[i] = _comedilib_h.comedi_from_physical(d, p)
69 cpdef comedi_to_physical(data, coefficients, expansion_origin):
70 """Convert Comedi bit values (`lsampl_t`) to physical units (`double`)
72 * `data` is the value to be converted (scalar or array-like)
73 * `coefficients` and `expansion_origin` should be appropriate
74 for `_setup_comedi_polynomial_t`. TODO: expose it's docstring?
76 The conversion algorithm is::
78 x = sum_i c_i * (d-d_o)^i
80 where `x` is the returned physical value, `d` is the supplied data,
81 `c_i` is the `i`\th coefficient, and `d_o` is the expansion origin.
83 >>> print comedi_to_physical.__doc__ # doctest: +ELLIPSIS
84 Convert Comedi bit values (`lsampl_t`) to physical units (`double`)
86 >>> comedi_to_physical(1, [1, 2, 3], 2)
88 >>> comedi_to_physical([1, 2, 3], [1, 2, 3], 2)
91 cdef _comedilib_h.comedi_polynomial_t p
92 _setup_comedi_polynomial_t(&p, coefficients, expansion_origin)
93 return _convert(&p, data, _constant.CONVERSION_DIRECTION.to_physical)
95 cpdef comedi_from_physical(data, coefficients, expansion_origin):
96 """Convert physical units to Comedi bit values
98 Like `comedi_to_physical` but converts `double` -> `lsampl_t`.
100 >>> comedi_from_physical(1, [1,2,3], 2)
102 >>> comedi_from_physical([1, 2, 3], [1, 2, 3], 2)
103 array([2, 1, 6], dtype=uint32)
105 cdef _comedilib_h.comedi_polynomial_t p
106 _setup_comedi_polynomial_t(&p, coefficients, expansion_origin)
107 return _convert(&p, data, _constant.CONVERSION_DIRECTION.from_physical)
110 cdef class CalibratedConverter (object):
111 """Apply a converion polynomial
113 Usually you would get the this converter from
114 `DataChannel.get_converter()` or similar. but for testing, we'll
115 just create one out of thin air.
117 >>> c = CalibratedConverter(
118 ... to_physical_coefficients=[1, 2, 3],
119 ... to_physical_expansion_origin=1)
120 >>> c # doctest: +NORMALIZE_WHITESPACE
122 to_physical:{coefficients:[1.0, 2.0, 3.0] origin:1.0}
123 from_physical:{coefficients:[0.0] origin:0.0}>
127 >>> c.to_physical([0, 1, 2])
129 >>> c.to_physical(_numpy.array([0, 1, 2, 3], dtype=_numpy.uint))
130 array([ 2., 1., 6., 17.])
132 >>> c.get_to_physical_expansion_origin()
134 >>> c.get_to_physical_coefficients()
137 def __init__(self, to_physical_coefficients=None,
138 to_physical_expansion_origin=0,
139 from_physical_coefficients=None,
140 from_physical_expansion_origin=0):
141 if to_physical_coefficients:
142 _setup_comedi_polynomial_t(
143 &self._to_physical, to_physical_coefficients,
144 to_physical_expansion_origin)
145 if from_physical_coefficients:
146 _setup_comedi_polynomial_t(
147 &self._from_physical, from_physical_coefficients,
148 from_physical_expansion_origin)
150 cdef _str_poly(self, _comedilib_h.comedi_polynomial_t polynomial):
151 return '{coefficients:%s origin:%s}' % (
152 [float(polynomial.coefficients[i])
153 for i in range(polynomial.order+1)],
154 float(polynomial.expansion_origin))
157 return '<%s to_physical:%s from_physical:%s>' % (
158 self.__class__.__name__, self._str_poly(self._to_physical),
159 self._str_poly(self._from_physical))
162 return self.__str__()
164 cpdef to_physical(self, data):
165 return _convert(&self._to_physical, data,
166 _constant.CONVERSION_DIRECTION.to_physical)
168 cpdef from_physical(self, data):
169 return _convert(&self._from_physical, data,
170 _constant.CONVERSION_DIRECTION.from_physical)
172 cpdef get_to_physical_expansion_origin(self):
173 return self._to_physical.expansion_origin
175 cpdef get_to_physical_coefficients(self):
176 ret = _numpy.ndarray((self._to_physical.order+1,), _numpy.double)
177 for i in xrange(len(ret)):
178 ret[i] = self._to_physical.coefficients[i]
181 cpdef get_from_physical_expansion_origin(self):
182 return self._from_physical.expansion_origin
184 cpdef get_from_physical_coefficients(self):
185 ret = _numpy.ndarray((self._from_physical.order+1,), _numpy.double)
186 for i in xrange(len(ret)):
187 ret[i] = self._from_physical.coefficients[i]
191 # TODO: see comedi_caldac_t and related at end of comedilib.h