01-11-2020 08:24 AM
Hi all,
I am using a NI cDAQ 9189 with the following cards:
NI 9232
NI 9213
Now I'd like to measure temperature and acceleration. The measurement should start with the same trigger and with different sampling frequencies.
I wrote the following code:
import nidaqmx
from nidaqmx import constants
from nidaqmx import stream_readers
import numpy as np
deviceName = 'cDAQ9189-1DC2683'
measTime = 5 # measurement time in seconds
# tasks
rateTemp = 100 # sampling rate in Hz
rateAcc = 1000 # sampling rate in Hz
with nidaqmx.Task() as taskTemp, nidaqmx.Task() as taskAcc:
# add channels to task
taskTemp.ai_channels.add_ai_thrmcpl_chan(deviceName+'Mod6/ai0:1',name_to_assign_to_channel='temp', min_val=0.0,
max_val=100.0, units=nidaqmx.constants.TemperatureUnits.DEG_C,
thermocouple_type=nidaqmx.constants.ThermocoupleType.K)
taskAcc.ai_channels.add_ai_accel_chan(deviceName+'Mod1/ai0:2', name_to_assign_to_channel='acc',\
units=constants.AccelUnits.METERS_PER_SECOND_SQUARED,sensitivity=1, \
sensitivity_units=constants.AccelSensitivityUnits.VOLTS_PER_G)
# set sampling rate
taskTemp.timing.cfg_samp_clk_timing(rate = rateTemp, sample_mode=constants.AcquisitionType.CONTINUOUS, \
samps_per_chan = measTime * rateTemp)
taskAcc.timing.cfg_samp_clk_timing(rate = rateAcc, source='taskTemp/SampleClock', \
sample_mode=constants.AcquisitionType.CONTINUOUS, \
samps_per_chan = measTime * rateAcc)
# start tasks
taskTemp.start()
taskAcc.start()
taskTemp.wait_until_done()
taskAcc.wait_until_done()
# measure
reader = stream_readers.AnalogMultiChannelReader(taskTemp.in_stream)
t_data = np.zeros((1,measTime * rateTemp))
reader.read_many_sample(t_data,measTime * rateTemp)
reader = stream_readers.AnalogMultiChannelReader(taskAcc.in_stream)
a_data = np.zeros((1,measTime * rateAcc))
reader.read_many_sample(t_data,measTime * rateAcc)
Unfortunately I get the following error:
DaqError: Requested sample clock source is invalid.
Property: DAQmx_SampClk_Src
Corresponding Value: taskTemp/SampleClock
Device: cDAQ9189-1DC2683
Task Name: _unnamedTask<3A>
Status Code: -200414
What am I missing here?
Many thanks in advance!
Max
01-11-2020 04:29 PM
I'm a LabVIEW guy and don't know details of the DAQmx text-language API's.
That said, here are 3 thoughts:
1. Delta-Sigma devices can't accept *any* external signal as a direct sample clock. Your 9232 is a Delta-Sigma device. Your 9213 also has some special sample clock limitations. See this article for some info on both.
2. If you want different sample rates, you shouldn't be trying to have taskAcc use th taskTemp sample clock anyway. Just leave out the 'source=' part of the function call.
3. You said you wanted both tasks to start with the same trigger but you haven't made any DAQmx calls to configure triggers for the tasks.
-Kevin P
01-13-2020 02:10 AM
Thanks for your reply Kevin!
According to your advice, I changed my code. First, I deleted the source. Second, I tried to implement a software trigger. However, from the documentation on the triggers provided in the python-API it seems that there are only signal-related triggers available (for example start measuring after certain threshold of signal is surpassed). Therefore I now work with task.start():
import nidaqmx
from nidaqmx import constants
from nidaqmx import stream_readers
import numpy as np
deviceName = 'cDAQ9189-1DC2683'
measTime = 10 # measurement time in seconds
#%%
# tasks
rateTemp = 100 # sampling rate in Hz
rateAcc = 1000 # sampling rate in Hz
with nidaqmx.Task() as taskTemp, nidaqmx.Task() as taskAcc:
# add channels to task
taskTemp.ai_channels.add_ai_thrmcpl_chan(deviceName+'Mod6/ai0',name_to_assign_to_channel='temp', min_val=0.0,
max_val=100.0, units=nidaqmx.constants.TemperatureUnits.DEG_C,
thermocouple_type=nidaqmx.constants.ThermocoupleType.K)
taskAcc.ai_channels.add_ai_accel_chan(deviceName+'Mod1/ai0:2', name_to_assign_to_channel='acc',\
units=constants.AccelUnits.METERS_PER_SECOND_SQUARED,sensitivity=1, \
sensitivity_units=constants.AccelSensitivityUnits.VOLTS_PER_G)
# set sampling rate
taskTemp.timing.cfg_samp_clk_timing(rate = rateTemp, sample_mode=constants.AcquisitionType.CONTINUOUS, \
samps_per_chan = measTime * rateTemp)
taskAcc.timing.cfg_samp_clk_timing(rate = rateAcc, sample_mode=constants.AcquisitionType.CONTINUOUS, \
samps_per_chan = measTime * rateAcc)
# start tasks
taskTemp.start()
taskAcc.start()
# measure
reader = stream_readers.AnalogMultiChannelReader(taskTemp.in_stream)
t_data = np.zeros((1,measTime * rateTemp))
reader.read_many_sample(t_data,measTime * rateTemp)
reader = stream_readers.AnalogMultiChannelReader(taskAcc.in_stream)
a_data = np.zeros((3,measTime * rateAcc))
reader.read_many_sample(a_data,measTime * rateAcc)
# start tasks
taskTemp.stop()
taskAcc.stop()
Working with task.Temp.start() and taskAcc.start() in subsequent order leads to a small delay in starting the measurement (roughly half a second). Is there a nicer way to implement a software trigger, which is not signal-related?
Many tahanks,
Max
01-13-2020 11:48 AM
Several things, briefly.
1. I doubt the task start calls are responsible for the 1/2 second delay you mention. It's more likely related to particular characteristics of your modules. Delta-sigma device always induce some delay. Your Temperature module has some unique sample timing constraints too and may have a delay of its own.
2. The *right* kind of trigger for data acq IS a signal-based trigger, not something you try to accomplish solely in software.
Your link included a function call to configure a digital edge start trigger. Look up your terminal constants and you'll find that there are terminal names for signals *internal* to your device. For example, let's suppose you designate your accelerometer device (the Delta-Sigma device) as the master task and that it uses the chassis' "ai" timing engine (other possibilities are te0, te1). You could configure the temperature task for a digital edge start trigger with source terminal name similar to "/cDAQChassisName/ai/StartTrigger". There's an internal "ai/StartTrigger" signal that gets asserted when the "ai" task gets started.
Then you should *also* make the software call to start the temperature task before the accel task. The temperature task will be told to start, but won't *actually* start until the internal trigger signal for the accelerometer task asserts.
3. Even if / after you configure hardware triggering, you will still need to understand your specific devices better to properly correlate their data. (On the other hand, without proper hardware triggering, you'd have NO chance to correlate them in time exactly). As stated before, the Delta-Sigma accelerometer device will have inherent delay. The temperature device has some special considerations I didn't read deeply about but you'll need to.
-Kevin P
02-12-2020 06:20 AM
To wrap this topic up: thanks for the help by kevin p, we managed to write some code that suits our purposes. It continuously writes data from two tasks into a file until the user hits enter:
import nidaqmx as ni
from nidaqmx import constants
from nidaqmx import stream_readers
import numpy as np
import pandas as pd
# temperature sample frequency [Hz]
m_t_frequency = 10
# acceleration sample frequency [Hz]
m_a_frequency = 10000
# sensitivities
# Typ 8762A50
# SN 5406471
s1_ai0 = 100.8
s1_ai1 = 99.7
s1_ai2 = 101.7
# SN 5359037
s2_ai0 = 103.8
s2_ai1 = 103.9
s2_ai2 = 102.9
# SN 5205933
s3_ai0 = 102.9
s3_ai1 = 100.5
s3_ai2 = 102.2
# file counter
file = open("start_counter.txt", "r")
counter = 0
for val in file.read().split():
counter = int(val)
file.close()
# measurement tasks
with ni.Task() as task_t, ni.Task() as task_a:
# add acceleration sensor channels
task_a.ai_channels.add_ai_accel_chan("cDAQ9189-1DC2683Mod1/ai0", sensitivity=s1_ai0, sensitivity_units=constants.AccelSensitivityUnits.M_VOLTS_PER_G)
task_a.ai_channels.add_ai_accel_chan("cDAQ9189-1DC2683Mod1/ai1", sensitivity=s1_ai1, sensitivity_units=constants.AccelSensitivityUnits.M_VOLTS_PER_G)
task_a.ai_channels.add_ai_accel_chan("cDAQ9189-1DC2683Mod1/ai2", sensitivity=s1_ai2, sensitivity_units=constants.AccelSensitivityUnits.M_VOLTS_PER_G)
task_a.ai_channels.add_ai_accel_chan("cDAQ9189-1DC2683Mod2/ai0", sensitivity=s2_ai0, sensitivity_units=constants.AccelSensitivityUnits.M_VOLTS_PER_G)
task_a.ai_channels.add_ai_accel_chan("cDAQ9189-1DC2683Mod2/ai1", sensitivity=s2_ai1, sensitivity_units=constants.AccelSensitivityUnits.M_VOLTS_PER_G)
task_a.ai_channels.add_ai_accel_chan("cDAQ9189-1DC2683Mod2/ai2", sensitivity=s2_ai2, sensitivity_units=constants.AccelSensitivityUnits.M_VOLTS_PER_G)
task_a.ai_channels.add_ai_accel_chan("cDAQ9189-1DC2683Mod3/ai0", sensitivity=s3_ai0, sensitivity_units=constants.AccelSensitivityUnits.M_VOLTS_PER_G)
task_a.ai_channels.add_ai_accel_chan("cDAQ9189-1DC2683Mod3/ai1", sensitivity=s3_ai1, sensitivity_units=constants.AccelSensitivityUnits.M_VOLTS_PER_G)
task_a.ai_channels.add_ai_accel_chan("cDAQ9189-1DC2683Mod3/ai2", sensitivity=s3_ai2, sensitivity_units=constants.AccelSensitivityUnits.M_VOLTS_PER_G)
# add thermocouple channels
task_t.ai_channels.add_ai_thrmcpl_chan("cDAQ9189-1DC2683Mod6/ai0", thermocouple_type=constants.ThermocoupleType.K)
# set sampling clocks
task_a.timing.cfg_samp_clk_timing(rate=m_a_frequency, sample_mode=constants.AcquisitionType.CONTINUOUS)
task_t.timing.cfg_samp_clk_timing(rate=m_t_frequency, sample_mode=constants.AcquisitionType.CONTINUOUS)
# set custum python buffer
samples_per_buffer_a = 10000
samples_per_buffer_t = 10
# initialize stream readers
reader_a = stream_readers.AnalogMultiChannelReader(task_a.in_stream)
reader_t = stream_readers.AnalogMultiChannelReader(task_t.in_stream)
# acceleration file header
a_data = pd.DataFrame(data={'sensor 1, x-direction': [],
'sensor 1, y-direction': [],
'sensor 1, z-direction': [],
'sensor 2, x-direction': [],
'sensor 2, y-direction': [],
'sensor 2, z-direction': [],
'sensor 3, x-direction': [],
'sensor 3, y-direction': [],
'sensor 3, z-direction': []})
a_data.to_csv('acceleration_'+str(counter).zfill(4)+'.csv', mode='a')
# temperature file header
t_data = pd.DataFrame(data={'temperature 1': []})
t_data.to_csv('temperature_'+str(counter).zfill(4)+'.csv', mode='a')
# acceleration callback function
def reading_task_a_callback(task_idx, every_n_samples_event_type, num_samples, callback_data):
buffer_a = np.zeros((9,num_samples))
reader_a.read_many_sample(buffer_a, number_of_samples_per_channel=num_samples)
a_data = pd.DataFrame(data={'sensor 1, x-direction': buffer_a[0,:],
'sensor 1, y-direction': buffer_a[1,:],
'sensor 1, z-direction': buffer_a[2,:],
'sensor 2, x-direction': buffer_a[3,:],
'sensor 2, y-direction': buffer_a[4,:],
'sensor 2, z-direction': buffer_a[5,:],
'sensor 3, x-direction': buffer_a[6,:],
'sensor 3, y-direction': buffer_a[7,:],
'sensor 3, z-direction': buffer_a[8,:]})
a_data.to_csv('acceleration_'+str(counter).zfill(4)+'.csv', mode='a', header=False)
print('Acceleration measurement. Added '+str(num_samples)+' data points per channel to the measurement file.')
return 0
# acceleration buffer event
task_a.register_every_n_samples_acquired_into_buffer_event(samples_per_buffer_a, reading_task_a_callback)
# temperature callback function
def reading_task_t_callback(task_idx2, every_n_samples_event_type, num_samples2, callback_data):
buffer_t = np.zeros((1,num_samples2))
reader_t.read_many_sample(buffer_t, number_of_samples_per_channel=num_samples2)
t_data = pd.DataFrame(data={'temperature': buffer_t[0,:]})
t_data.to_csv('temperature_'+str(counter).zfill(4)+'.csv', mode='a', header=False)
print('Temperature measurement. Added '+str(num_samples2)+' data points per channel to the measurement file.')
return 0
# temperature buffer event
task_t.register_every_n_samples_acquired_into_buffer_event(samples_per_buffer_t, reading_task_t_callback)
# start task
task_t.start()
task_a.start()
# end measurement by pressing enter
input('Measurement active. Press enter to stop.\n')
# increase file counter
counter = counter + 1
# save file counter to a file
file = open("start_counter.txt","w")
file.write (str(counter) + "\n")
file.close()