Ran update-copyright.py.
[pycomedi.git] / doc / demo / cmd.py
1 #!/usr/bin/env python
2 #
3 # Copyright (C) 2012 W. Trevor King <wking@tremily.us>
4 #
5 # This file is part of pycomedi.
6 #
7 # pycomedi is free software: you can redistribute it and/or modify it under the
8 # terms of the GNU General Public License as published by the Free Software
9 # Foundation, either version 2 of the License, or (at your option) any later
10 # version.
11 #
12 # pycomedi is distributed in the hope that it will be useful, but WITHOUT ANY
13 # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
14 # A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
15 #
16 # You should have received a copy of the GNU General Public License along with
17 # pycomedi.  If not, see <http://www.gnu.org/licenses/>.
18
19 """Use comedi commands for asyncronous input.
20
21 An example for directly using Comedi commands.  Comedi commands
22 are used for asynchronous acquisition, with the timing controlled
23 by on-board timers or external events.
24
25 Based on David A. Schleef's `comedilib/demo/cmd.c`.
26 """
27
28 import sys as _sys
29 import time as _time
30
31 import numpy as _numpy
32
33 from pycomedi import LOG as _LOG
34 import pycomedi.constant as _constant
35 from pycomedi.device import Device as _Device
36 from pycomedi.subdevice import StreamingSubdevice as _StreamingSubdevice
37 from pycomedi.channel import AnalogChannel as _AnalogChannel
38 from pycomedi.chanspec import ChanSpec as _ChanSpec
39 import pycomedi.utility as _utility
40
41
42 def open_channels(device, subdevice, channels, range, aref):
43     """Subdevice index and list of channel indexes
44     to ``Subdevice`` instance and list of ``AnalogChannel`` instances
45     """
46     if subdevice >= 0:
47         subdevice = device.subdevice(subdevice, factory=_StreamingSubdevice)
48     else:
49         subdevice = device.find_subdevice_by_type(
50             _constant.SUBDEVICE_TYPE.ai, factory=_StreamingSubdevice)
51     channels = [subdevice.channel(
52             index=i, factory=_AnalogChannel, range=range, aref=aref)
53                 for i in channels]
54     return(subdevice, channels)
55
56 def prepare_command(subdevice, channels, period, num_scans):
57     """Create a periodic sampling command.
58
59     Ask comedilib to create a generic sampling command and then modify
60     the parts we want.
61     """
62     command = subdevice.get_cmd_generic_timed(
63         len(channels), scan_period_ns=period*1e9)
64     command.chanlist = channels
65     command.stop_src = _constant.TRIG_SRC.count
66     command.stop_arg = num_scans
67     return command
68
69 def test_command(subdevice, num_tests=2):
70     """Adjust a command as necessary to get valid arguments.
71     """
72     _LOG.info('command before testing:\n{}'.format(subdevice.cmd))
73     for i in range(2):
74         rc = subdevice.command_test()
75         if rc is None:
76             _LOG.info('command is valid')
77             return
78         _LOG.info('test {} returned {}\n{}'.format(i, rc, subdevice.cmd))
79     _LOG.error('error preparing command: {}'.format(rc))
80     _sys.exit(1)
81 test_command.__test__ = False  # test_command is not a Nose test
82
83 def write_data(stream, channels, data, physical=False):
84     if physical:
85         converters = [c.get_converter() for c in channels]
86         physical_data = _numpy.zeros(data.shape, dtype=_numpy.float32)
87         for i,c in enumerate(converters):
88             physical_data[:,i] = c.to_physical(data[:,i])
89         data = physical_data
90     for row in range(data.shape[0]):
91         stream.write('\t'.join(str(x) for x in data[row,:]))
92         stream.write('\n')
93
94 class DataWriter (object):
95     def __init__(self, stream, channels, physical=False):
96         self.stream = stream
97         self.channels = channels
98         self.physical = physical
99
100     def __call__(self, data):
101         d = _numpy.ndarray(shape=(1,data.size), dtype=data.dtype, buffer=data)
102         write_data(
103             stream=self.stream, channels=self.channels, data=d,
104             physical=self.physical)
105
106
107 def read(device, subdevice=None, channels=[0], range=0, aref=0, period=0,
108          num_scans=2, reader=_utility.Reader, physical=False,
109          stream=_sys.stdout):
110     """Read ``num_scans`` samples from each specified channel.
111     """
112     subdevice,channels = open_channels(
113         device=device, subdevice=subdevice, channels=channels, range=range,
114         aref=aref)
115     subdevice.cmd = prepare_command(
116         subdevice=subdevice, channels=channels, period=period,
117         num_scans=num_scans)
118     rc = test_command(subdevice=subdevice)
119     kwargs = {}
120     if reader == _utility.CallbackReader:
121         read_buffer_shape = (len(channels),)
122         kwargs = {
123             'callback': DataWriter(
124                 stream=stream, channels=channels, physical=physical),
125             'count': num_scans,
126             }
127     else:
128         read_buffer_shape = (num_scans, len(channels))
129     read_buffer = _numpy.zeros(read_buffer_shape, dtype=subdevice.get_dtype())
130     reader = reader(
131         subdevice=subdevice, buffer=read_buffer, name='Reader', **kwargs)
132     start = _time.time()
133     _LOG.info('start time: {}'.format(start))
134     subdevice.command()
135     reader.start()
136     reader.join()
137     stop = _time.time()
138     _LOG.info('stop time: {}'.format(stop))
139     _LOG.info('time: {}'.format(stop - start))
140     if not isinstance(reader, _utility.CallbackReader):
141         write_data(
142             stream=stream, channels=channels, data=read_buffer,
143             physical=physical)
144
145
146 def run(filename, **kwargs):
147     """
148     >>> import StringIO
149     >>> stdout = StringIO.StringIO()
150     >>> run(filename='/dev/comedi0', stream=stdout)
151     >>> print(stdout.getvalue())  # doctest: +SKIP
152     29694
153     29693
154     <BLANKLINE>
155     >>> stdout.truncate(0)
156     >>> run(filename='/dev/comedi0', reader=_utility.MMapReader, stream=stdout)
157     >>> print(stdout.getvalue())  # doctest: +SKIP
158     29691
159     29691
160     <BLANKLINE>
161     """
162     device = _Device(filename=filename)
163     device.open()
164     try:
165         read(device=device, **kwargs)
166     finally:
167         device.close()
168
169
170 if __name__ == '__main__':
171     import pycomedi_demo_args
172
173     args = pycomedi_demo_args.parse_args(
174         description=__doc__,
175         argnames=['filename', 'subdevice', 'channels', 'aref', 'range',
176                   'num-scans', 'mmap', 'callback', 'frequency', 'physical',
177                   'verbose'])
178
179     _LOG.info(('measuring device={0.filename} subdevice={0.subdevice} '
180                'channels={0.channels} range={0.range} '
181                'analog-reference={0.aref}'
182                ).format(args))
183
184     if args.callback:
185         reader = _utility.CallbackReader
186     elif args.mmap:
187         reader = _utility.MMapReader
188     else:
189         reader = _utility.Reader
190
191     run(filename=args.filename, subdevice=args.subdevice,
192         channels=args.channels, aref=args.aref, range=args.range,
193         num_scans=args.num_scans, reader=reader, period=args.period,
194         physical=args.physical)