Multifunction DAQ

cancel
Showing results for 
Search instead for 
Did you mean: 

Python sync analog inputs

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

0 Kudos
Message 1 of 5
(3,308 Views)

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

CAUTION! New LabVIEW adopters -- it's too late for me, but you *can* save yourself. The new subscription policy for LabVIEW puts NI's hand in your wallet for the rest of your working life. Are you sure you're *that* dedicated to LabVIEW? (Summary of my reasons in this post, part of a voluminous thread of mostly complaints starting here).
Message 2 of 5
(3,266 Views)

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

0 Kudos
Message 3 of 5
(3,202 Views)

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 

CAUTION! New LabVIEW adopters -- it's too late for me, but you *can* save yourself. The new subscription policy for LabVIEW puts NI's hand in your wallet for the rest of your working life. Are you sure you're *that* dedicated to LabVIEW? (Summary of my reasons in this post, part of a voluminous thread of mostly complaints starting here).
0 Kudos
Message 4 of 5
(3,157 Views)

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()
Message 5 of 5
(3,041 Views)