Source code for crappy.inout.t7Streamer

# coding: utf-8

from time import time
import numpy as np

from .inout import InOut
from .._global import OptionalModule
try:
  from labjack import ljm
except ImportError:
  ljm = OptionalModule("ljm",
                       "Please install Labjack LJM and the ljm Python module")


[docs]class T7_streamer(InOut): """Class to use stream mode with Labjack T7 devices. Note: For single modes, see :ref:`Labjack T7`. You can use :ref:`IOBlock` with ``streamer=True`` to read data at high frequency from the Labjack. Streamer mode makes the Labjack unavailable for any other operation (single acquisition, `DAC` or `DIO`). You can specify each channel as a :obj:`dict`, allowing to set channel-specific settings such as gain, offset (computed on the host machine as this feature is not available on board with streamer mode), range and ability to zero the reading at startup. """
[docs] def __init__(self, device: str = 'ANY', connection: str = 'ANY', identifier: str = 'ANY', channels: list = None, scan_rate: int = 100000, scan_per_read: int = 10000, resolution: int = 1) -> None: """Sets the args and initializes the parent class. Args: device (:obj:`str`, optional): The type of the device to open. Possible values include: :: 'ANY', 'T7', 'T4', 'DIGIT' Only tested with `'T7'` in Crappy. connection (:obj:`str`, optional): How is the Labjack connected ? Possible values include: :: 'ANY', 'TCP', 'USB', 'ETHERNET', 'WIFI' identifier (:obj:`str`, optional): Something to identify the Labjack. It can be a serial number, an IP address, or a device name. channels (:obj:`list`, optional): Channels to use and their settings. It must be a :obj:`list` of :obj:`dict`. scan_rate (:obj:`int`, optional): The acquisition frequency in Hz for the channels. Note that the sample rate (`scan_rate * num of chan`) cannot exceed `100000`. If it is too high it will be lowered to the highest possible value. scan_per_read (:obj:`int`, optional): The number of points to read during each loop. resolution (:obj:`int`, optional): The resolution index for all channels. The bigger this value the better the resolution, but the lower the speed. The possible range is either `1` to `8` or to `12` according to the model. Note: - ``channels`` keys: - name (:obj:`str`): The name of the channel according to Labjack's naming convention. Ex: `'AIN0'`. This will be used to define the direction (in/out) and the available settings. It can be: - `AINx`: An analog input, if gain and/or offset is given, the integrated slope mechanism will be used with the extended features registers. It can also be used for thermocouples (see below). You can use any EF by using the ``write_at_open`` and ``to_read`` keys if necessary. - `(T)DACx`: An analog output, you can specify gain and/or offset. - `(E/F/C/M IOx)`: Digital in/outputs. You can specify the direction. - gain (:obj:`float`, default: `1`): A numeric value that will multiply the given value for inputs and outputs. - offset (:obj:`float`, default: `0`): Will be added to the value. For inputs: :: returned_value = gain * measured_value + offset For outputs: :: set_value = gain * given_value + offset. Where `measured_value` and `set_values` are in Volts. - make_zero (:obj:`bool`): AIN only, if :obj:`True` the input value will be evaluated at startup and the offset will be adjusted to return `0` (or the offset if any). - range (:obj:`float`, default: `10`): The range of the acquisition in Volts. A range of `x` means that values can be read between `-x` and `x` Volts. The possible values are: :: 0.01, 0.1, 1, 10 """ InOut.__init__(self) self.device = device self.connection = connection self.identifier = identifier self.channels = [{'name': 'AIN0'}] if channels is None else channels self.scan_rate = scan_rate self.scan_per_read = scan_per_read self.resolution = resolution default = {'gain': 1, 'offset': 0, 'make_zero': False, 'range': 10} if len(self.channels) * self.scan_rate > 100000: self.scan_rate = 100000 / len(self.channels) print("[Labjack] Warning! scan_rate is too high! Sample rate cannot " "exceed 100kS/s, lowering samplerate to", self.scan_rate, "samples/s") self.chan_list = [] for d in self.channels: if isinstance(d, str): d = {'name': d} for k in ['gain', 'offset', 'make_zero', 'range']: if k not in d: d[k] = default[k] if 'to_write' not in d: d['to_write'] = [] d['to_write'].append(("_RANGE", d['range'])) d['to_read'] = ljm.nameToAddress(d['name'])[0] self.chan_list.append(d)
def open(self) -> None: self.handle = ljm.openS(self.device, self.connection, self.identifier) names, values = [], [] for c in self.chan_list: if "to_write" in c: for n, v in c['to_write']: names.append(c['name'] + n) values.append(v) # names.append("STREAM_NUM_ADDRESSES"); values.append(len(self.channels)) names.append("STREAM_SCANRATE_HZ") values.append(self.scan_rate) names.append("STREAM_RESOLUTION_INDEX") values.append(self.resolution) ljm.eWriteNames(self.handle, len(names), names, values) scan_rate = ljm.eReadName(self.handle, "STREAM_SCANRATE_HZ") if scan_rate != self.scan_rate: print("[Labjack] Actual scan_rate:", scan_rate, "instead of", self.scan_rate) self.scan_rate = scan_rate if any([c.get("make_zero", False) for c in self.chan_list]): print("[Labjack] Please wait during offset evaluation...") off = self.eval_offset() for i, c in enumerate(self.chan_list): if 'make_zero' in c and c['make_zero']: c['offset'] += c['gain']*off[i] self.n = 0 # Number of data points (to rebuild time)
[docs] def get_data(self) -> None: """Short version, only used for :meth:`InOut.eval_offset`.""" return [time()] + ljm.eReadNames(self.handle, len(self.chan_list), [c['name'] for c in self.chan_list])
def start_stream(self) -> None: ljm.eStreamStart(self.handle, self.scan_per_read, len(self.chan_list), [c['to_read'] for c in self.chan_list], self.scan_rate) self.stream_t0 = time() def stop_stream(self) -> None: ljm.eStreamStop(self.handle) def get_stream(self) -> list: a = np.array(ljm.eStreamRead(self.handle)[0]) r = a.reshape(len(a) // len(self.channels), len(self.channels)) for i, c in enumerate(self.chan_list): r[:, i] = c['gain'] * r[:, i] + c['offset'] t = self.stream_t0 + \ np.arange(self.n, self.n + r.shape[0]) / self.scan_rate self.n += r.shape[0] return [t, r] def close(self) -> None: ljm.close(self.handle)