class BaseChannel: """ Base class to implement a channel """ def __init__(self, channel, channel_name): self._thread = None self._stop = False self._channel = channel self._data = np.zeros(0) self.sample_readies = 0 self.enabled = True # Create the task self._task = Task('task' + channel_name) # Configure reader. self._task.in_stream.read_all_avail_samp = True self._reader = CounterReader(self._task.in_stream) # Configure timing def __del__(self): self._task.close() def start(self, samples): self._task.stop() self._data = np.zeros(samples) self.sample_readies = 0 if not self.enabled: return if samples == 1: self._task.timing.samp_quant_samp_per_chan = 2 else: self._task.timing.samp_quant_samp_per_chan = samples self._stop = False self._thread = threading.Thread(target=self._read, args=[samples]) self._task.start() self._thread.start() @property def data(self): return self._data[:self.sample_readies] @property def done(self): return self._task.is_task_done() def stop(self): self._stop = True self._task.stop() def _read(self, samples): i = 0 while not self._stop and samples != 0: self._data[i] = self._reader.read_one_sample_double(timeout=-1) i += 1 samples -= 1 self.sample_readies += 1 self._task.stop()
class PulseGenerator: def __init__(self, channel, start_src=None): self._task = None self._channel = channel # TODO implement external start def __del__(self): if self._task is not None: self._task.close() def start(self, samples, high_time, low_time, initial_delay=0): if self._task is not None: self._task.close() self._task = Task('timer') self._timer = self._task.co_channels.add_co_pulse_chan_time( self._channel, high_time=high_time, low_time=low_time, initial_delay=initial_delay) self._task.timing.samp_quant_samp_per_chan = samples self._task.timing.samp_timing_type = SampleTimingType.IMPLICIT self._task.start() def stop(self): self._task.stop() @property def done(self): return self._task.is_task_done()
class PulseCounter: """ Class to implement a pulse counter """ def __init__(self, channel, channel_name, gate_src, timebase_src, edge=Edge.RISING): self._data = None self.sample_readies = 0 self._channel = channel self._task = Task('task' + channel_name) self._counter = self._task.ci_channels.add_ci_pulse_width_chan( channel, channel_name, units=TimeUnits.TICKS, starting_edge=edge) self._counter.ci_ctr_timebase_src = timebase_src self._counter.ci_pulse_width_term = gate_src self._task.in_stream.read_all_avail_samp = True self._reader = CounterReader(self._task.in_stream) self._thread = None self._stop = False def __del__(self): self._task.close() def start(self, samples): self._data = np.zeros(samples) self._task.stop() self._data = np.zeros(samples) self._task.timing.samp_quant_samp_per_chan = samples self._task.timing.samp_timing_type = SampleTimingType.IMPLICIT self._counter.ci_data_xfer_mech = \ DataTransferActiveTransferMode.INTERRUPT self._thread = threading.Thread(target=self._read, args=[samples]) self._task.start() self._stop = False self._thread.start() @property def done(self): return self._task.is_task_done() def stop(self): self._stop = True self._task.stop() def _read(self, samples): i = 0 while not self._stop and samples != 0: self._data[i] = self._reader.read_one_sample_double(timeout=-1) i += 1 samples -= 1 @property def data(self): return self._data
class DAQmxSystem(AcquisitionCard): def __init__(self): super(DAQmxSystem, self).__init__() self.task = Task() self.reading = False self.stop_lock = asyncio.Lock() self.read_lock = asyncio.Lock() @property def samp_clk_max_rate(self): return self.task.timing.samp_clk_max_rate def possible_trigger_channels(self): return [chan.name for chan in self.channels] def close(self): if self.task: self.channels = [] self.task.close() self.task = None def add_channel(self, channel_name, terminal_config, voltage_range): tc = ConcreteTerminalConfig[terminal_config] ai_chan = self.task.ai_channels.add_ai_voltage_chan( channel_name, terminal_config=tc, min_val=-voltage_range, max_val=voltage_range, ) self.channels.append(ai_chan) self.actual_ranges.append(ai_chan.ai_max) def configure_clock(self, sample_rate, samples_per_chan): self.task.timing.cfg_samp_clk_timing(sample_rate, samps_per_chan=samples_per_chan) self.sample_rate = sample_rate self.samples_per_chan = samples_per_chan def configure_trigger( self, trigger_source=None, trigger_level=0, trigger_config=TriggerConfig.EdgeRising, ): st = self.task.triggers.start_trigger if trigger_source is None: print("disable_start_trig") st.disable_start_trig() else: if trigger_config != TriggerConfig.EdgeRising: raise NotImplementedError() # TODO print( f"cfg_anlg_edge_start_trig({trigger_source:}, {trigger_level:})" ) st.cfg_anlg_edge_start_trig(trigger_source, trigger_level=trigger_level) def start(self): self.task.start() self.running = True async def stop(self): async with self.stop_lock: if self.running: self.running = False while self.reading: await asyncio.sleep(1.0) self.task.stop() async def read(self, tmo=None): async with self.read_lock: self.reading = True done = False start = time.monotonic() while self.running: try: await self.loop.run_in_executor(None, self.task.wait_until_done, 1.0) except DaqError: if tmo and time.monotonic() - start > tmo: raise TimeoutException() else: continue done = True break if done and self.running: data = await self.loop.run_in_executor(None, self.task.read, READ_ALL_AVAILABLE) self.last_read = time.monotonic() data = np.array(data) else: data = None self.reading = False return data
class DAQmxSystem(AcquisitionCard): """This class is the concrete implementation for NI DAQmx board using the :mod:`nidaqmx` module. """ def __init__(self): """Constructor method """ super(DAQmxSystem, self).__init__() self.task = Task() self.reading = False self.stop_lock = asyncio.Lock() self.read_lock = asyncio.Lock() @property def samp_clk_max_rate(self): """Maximum sample clock rate """ return self.task.timing.samp_clk_max_rate def possible_trigger_channels(self): """This method returns the list of channels that can be used as trigger. """ return [chan.name for chan in self.channels] def close(self): """This method closes the active task, if there is one. """ if self.task: self.channels = [] self.task.close() self.task = None def add_channel(self, channel_name, terminal_config, voltage_range): """Concrete implementation of :meth:`pymanip.aiodaq.AcquisitionCard.add_channel`. .. todo:: Actually check the type for terminal_config. """ tc = ConcreteTerminalConfig[terminal_config] ai_chan = self.task.ai_channels.add_ai_voltage_chan( channel_name, terminal_config=tc, min_val=-voltage_range, max_val=voltage_range, ) self.channels.append(ai_chan) self.actual_ranges.append(ai_chan.ai_max) def configure_clock(self, sample_rate, samples_per_chan): """Concrete implementation of :meth:`pymanip.aiodaq.AcquisitionCard.configure_clock` """ self.task.timing.cfg_samp_clk_timing(sample_rate, samps_per_chan=samples_per_chan) self.sample_rate = sample_rate self.samples_per_chan = samples_per_chan def configure_trigger( self, trigger_source=None, trigger_level=0, trigger_config=TriggerConfig.EdgeRising, ): """Concrete implementation of :meth:`pymanip.aiodaq.AcquisitionCard.configure_trigger` .. todo:: implement trigger_config other than the defaults value """ st = self.task.triggers.start_trigger if trigger_source is None: print("disable_start_trig") st.disable_start_trig() else: if trigger_config != TriggerConfig.EdgeRising: raise NotImplementedError() # TODO print( f"cfg_anlg_edge_start_trig({trigger_source:}, {trigger_level:})" ) st.cfg_anlg_edge_start_trig(trigger_source, trigger_level=trigger_level) def start(self): """This method starts the task. """ self.task.start() self.running = True async def stop(self): """This asynchronous method aborts the current task. """ async with self.stop_lock: if self.running: self.running = False while self.reading: await asyncio.sleep(1.0) self.task.stop() async def read(self, tmo=None): """This asynchronous method reads data from the task. """ async with self.read_lock: self.reading = True done = False start = time.monotonic() while self.running: try: await self.loop.run_in_executor(None, self.task.wait_until_done, 1.0) except DaqError: if tmo and time.monotonic() - start > tmo: raise TimeoutException() else: continue done = True break if done and self.running: data = await self.loop.run_in_executor(None, self.task.read, READ_ALL_AVAILABLE) self.last_read = time.monotonic() data = np.array(data) else: data = None self.reading = False return data