Commit 6099043a authored by Dominique Piche's avatar Dominique Piche

change daq board acquisition from finite to continuous

parent f7a9f15c
......@@ -22,8 +22,8 @@ class MecaRobotForDosimetry(MecaRobot):
"""
:param ip : robot ip
:param port : robot port
:param simulation_mode: enables simulation mode, in which the robot does not move but we
can monitor it on the web interace.
:param simulation_mode: enables simulation mode, in which the robot does not move but it is simulated
on the web interace.
:param enable_log : writes in the log.txt file every communication with the robot.
:param scintillators_positions : array of each scintillator's z-position relative to
the end of the robot (flange reference frame), starting with the closest.
......@@ -59,14 +59,13 @@ class MecaRobotForDosimetry(MecaRobot):
if measure_with_nidaq:
self.daq_board = DAQ_Volt_Tracking(daq_path_number=daq_path_number,
input_channels=analog_input_channels,
output_channels=analog_output_channels,
number_of_measures_per_position=1000)
output_channels=analog_output_channels)
self.daq_board.start()
self.source_offset = np.array([0, 0, 0])
# Setting the last scintillator as the tool reference frame (TRF).
self.run('SetTRF', [scintillators_positions[-1], 0, 4.5, 0, -90, 0])
self.run('SetTRF', [scintillators_positions[-1], 0, 3.5, 0, -90, 0])
def start_tracking(self):
"""
......@@ -83,6 +82,7 @@ class MecaRobotForDosimetry(MecaRobot):
file.write(np.array2string(self.source_position, separator=',')[1:-1])
file.write('\nApplied source offset : ')
file.write(np.array2string(self.source_offset)[1:-1])
# Creating time file
self.time_file = open(os.path.join(current_path, 'Data/time.txt'), 'w+')
......@@ -109,7 +109,6 @@ class MecaRobotForDosimetry(MecaRobot):
def _track_data(self):
scintillators_positions_relative_to_source = self.get_scintillators_positions_relative_to_source()
scintillators_distance_to_source = np.linalg.norm(scintillators_positions_relative_to_source, axis=1)
scintillators_signal = self.get_scintillators_signal()
# Writing current time in time file
self.time_file.write(str(time.time()-self.start_time) + '\n')
......@@ -126,6 +125,7 @@ class MecaRobotForDosimetry(MecaRobot):
# Writing the scintillators' signal in the signal file
if self.measure_with_nidaq:
scintillators_signal = self.get_scintillators_signal()
self.scintillators_signal_file.write(np.array2string(scintillators_signal, separator=',')[1:-1] + '\n')
def stop_tracking(self):
......@@ -153,10 +153,12 @@ class MecaRobotForDosimetry(MecaRobot):
def get_scintillators_signal(self) -> np.ndarray:
"""
Reads one sample of the NI DAQ board to get the scintillators' signal. Returns every input channel in
an array.
Reads the last sample acquired from the NI DAQ board to get the scintillators' signal. The board is sampled at 10 Hz.
Returns every input channel in an array.
"""
scintillators_signal = self.daq_board.read_measure()
# Because the DAQ board is sampled at 10 Hz, we wait 0.1 s to make sure the reading corresponds to this dwell position.
time.sleep(0.1)
scintillators_signal = self.daq_board.last_read_val
return scintillators_signal
def get_scintillators_positions(self) -> np.ndarray:
......
......@@ -2,17 +2,24 @@ import nidaqmx
import time
import numpy as np
import warnings
import threading
from nidaqmx.stream_readers import (AnalogSingleChannelReader, AnalogMultiChannelReader)
from nidaqmx.stream_writers import (AnalogSingleChannelWriter, AnalogMultiChannelWriter)
class DAQ_Volt_Tracking:
def __init__(self, daq_path_number: int, input_channels: tuple, output_channels: tuple, number_of_measures_per_position: int):
class DAQ_Volt_Tracking(threading.Thread):
"""
Thread that reads an NI DAQ board in continous mode. The NI DAQ samples the signal at 100 kHz. Every 0.1 s, the thread averages 10000 samples.
"""
def __init__(self, daq_path_number: int, input_channels: tuple, output_channels: tuple):
"""
:param : daq_path_number : number of the daq board path position (i.e. 1 for Dev1, 2 for Dev2)
:param : input_channels : analog input channels to use
:param : output_channels : analog output channels to use
:param : number_of_measures_per_position : number of points to average when reading input signal
"""
# Initiating the thread
super().__init__()
# Creating the tasks
self.read_task = nidaqmx.Task()
self.write_task = nidaqmx.Task()
......@@ -20,7 +27,7 @@ class DAQ_Volt_Tracking:
# Channels parameters
self.number_of_input_channels = len(input_channels)
self.number_of_output_channels = len(output_channels)
self.number_of_measures_per_position = number_of_measures_per_position
self.number_of_samples = 10000
self.sample_rate = 100000
# Adding the channels
......@@ -32,14 +39,28 @@ class DAQ_Volt_Tracking:
channel_name = 'Dev{}/ao{}'.format(daq_path_number, output_channel_number)
self.write_task.ao_channels.add_ao_voltage_chan(channel_name, max_val=1, min_val=0)
Finite_Mode = nidaqmx.constants.AcquisitionType(10178)
self.values_filename = "raw_voltages.txt"
self.values_file = None
self.values_read = np.zeros((self.number_of_input_channels, self.number_of_samples), dtype=np.float64)
Cont_Mode = nidaqmx.constants.AcquisitionType(10123) # Continious acquisition
#self.read_task.timing.cfg_samp_clk_timing(rate=self.sample_rate, samps_per_chan=10000000, sample_mode=Finite_Mode)
self.read_task.timing.cfg_samp_clk_timing(rate=self.sample_rate, samps_per_chan=10000000, sample_mode=Cont_Mode)
self.reader = AnalogMultiChannelReader(self.read_task.in_stream)
self.writer = AnalogMultiChannelWriter(self.write_task.out_stream)
def start(self):
def run(self):
# The function that is called when we start the thread.
self.start_task()
time.sleep(0.1)
self.loop()
def start_task(self):
# Creating the values file
self.values_file = open(self.values_filename, "a")
# Starting tasks
self.read_task.start()
self.write_task.start()
......@@ -47,9 +68,43 @@ class DAQ_Volt_Tracking:
# Writing the gain of the PMT
self.writer.write_one_sample(np.ones(self.number_of_output_channels, dtype=np.float64))
self.start_time = time.time()
self.last_read_time = self.start_time
def loop(self):
self.is_looping = True
self.next_call = time.time()
while self.is_looping:
# Scheduling the next loop call. This is to avoid time drifting.
self.next_call += 0.1
self.reader.read_many_sample(self.values_read, number_of_samples_per_channel=self.number_of_samples, timeout=0.1)
read_time = time.time()
elapsed_time = read_time - self.start_time
time_resolution = read_time - self.last_read_time
# Averaging the 10000 samples
val = np.mean(self.values_read, axis=1)
self.last_read_val = val
try:
self.values_file.write(np.array2string(val, separator=',')[1:-1] + '\n')
except Exception as e:
print(e)
self.last_read_time = read_time
#print("Elapsed: {:.3f} Time resolution: {:.3f}".format(elapsed_time, time_resolution))
time.sleep(self.next_call - time.time())
def close(self):
self.is_looping = False
self.writer.write_one_sample(np.zeros(self.number_of_output_channels, dtype=np.float64))
time.sleep(0.1)
warnings.filterwarnings('error')
# Stoping tasks
try:
......@@ -63,13 +118,5 @@ class DAQ_Volt_Tracking:
self.read_task.close()
self.write_task.close()
def read_measure(self):
# Reading the channels
measure = np.zeros((self.number_of_input_channels, self.number_of_measures_per_position), dtype=np.float64)
self.reader.read_many_sample(measure, number_of_samples_per_channel=self.number_of_measures_per_position, timeout=10)
# Waiting to ensure the measurement is complete
time.sleep(self.number_of_measures_per_position/self.sample_rate)
# Averaging over many measures to improve accuracy
return np.mean(measure, axis=1)
\ No newline at end of file
# Closing raw data file
self.values_file.close()
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment