5

I am not being able to acquire continuos data from NI DAQ using nidaqxm on Python 3.

I already acquired finite data with a similar code, although I can't understand what I need to change to acquire data continuously.

import nidaqmx
from nidaqmx import constants
from nidaqmx import stream_readers
from nidaqmx import stream_writers
import matplotlib.pyplot as plt

#user input Acquisition
Ch00_name = 'A00'
Sens_Ch00 = 100#sensibilidade em mV/g
Ch01_name = 'A01'
Sens_Ch01 = 100#sensibilidade em mV/g
fs_acq = 1651 #sample frequency
t_med = 2 #time to acquire data

with nidaqmx.Task() as task:
    task.ai_channels.add_ai_accel_chan(physical_channel="cDAQ9191-1B7B393Mod1/ai0", name_to_assign_to_channel=Ch00_name,
                                       sensitivity=Sens_Ch00, min_val=-5, max_val=5, current_excit_val=0.002)
    task.ai_channels.add_ai_accel_chan(physical_channel="cDAQ9191-1B7B393Mod1/ai1", name_to_assign_to_channel=Ch01_name,
                                       sensitivity=Sens_Ch01, min_val=-5, max_val=5, current_excit_val=0.002)

    task.timing.cfg_samp_clk_timing(rate=fs_acq, sample_mode= constants.AcquisitionType.CONTINUOUS, samps_per_chan=(t_med * fs_acq),)

    reader = stream_readers.AnalogMultiChannelReader(task.in_stream)
    writer = stream_writers.AnalogMultiChannelWriter(task.out_stream)

What do I have to change in my code to acquire continuos data?

Jose Guilherme
  • 325
  • 8
  • 16

1 Answers1

5

You need to register a callback function. I am assuming that your box is running and there is some type of status LED flashing to show that the task is running.

import nidaqmx
from nidaqmx import constants
from nidaqmx import stream_readers
from nidaqmx import stream_writers
import matplotlib.pyplot as plt

import numpy as np

#user input Acquisition
Ch00_name = 'A00'
Sens_Ch00 = 100#sensibilidade em mV/g
Ch01_name = 'A01'
Sens_Ch01 = 100#sensibilidade em mV/g
num_channels = 2
fs_acq = 1651 #sample frequency
t_med = 2 #time to acquire data
    

with nidaqmx.Task() as task:
    task.ai_channels.add_ai_accel_chan(physical_channel="cDAQ9191-1B7B393Mod1/ai0", name_to_assign_to_channel=Ch00_name,
                                       sensitivity=Sens_Ch00, min_val=-5, max_val=5, current_excit_val=0.002)
    task.ai_channels.add_ai_accel_chan(physical_channel="cDAQ9191-1B7B393Mod1/ai1", name_to_assign_to_channel=Ch01_name,
                                       sensitivity=Sens_Ch01, min_val=-5, max_val=5, current_excit_val=0.002)

    task.timing.cfg_samp_clk_timing(rate=fs_acq, sample_mode=constants.AcquisitionType.CONTINUOUS, 
                                    samps_per_chan=(t_med * fs_acq),) # you may not need samps_per_chan

    # I set an input_buf_size
    samples_per_buffer = int(fs_acq // 30)  # 30 hz update
    # task.in_stream.input_buf_size = samples_per_buffer * 10  # plus some extra space

    reader = stream_readers.AnalogMultiChannelReader(task.in_stream)
    writer = stream_writers.AnalogMultiChannelWriter(task.out_stream)

    def reading_task_callback(task_idx, event_type, num_samples, callback_data=None):
        """After data has been read into the NI buffer this callback is called to read in the data from the buffer.

        This callback is for working with the task callback register_every_n_samples_acquired_into_buffer_event.

        Args:
            task_idx (int): Task handle index value
            event_type (nidaqmx.constants.EveryNSamplesEventType): ACQUIRED_INTO_BUFFER
            num_samples (int): Number of samples that was read into the buffer.
            callback_data (object)[None]: No idea. Documentation says: The callback_data parameter contains the value
                you passed in the callback_data parameter of this function.
        """
        buffer = np.zeros((num_channels, num_samples), dtype=np.float32)
        reader.read_many_sample(buffer, num_samples, timeout=constants.WAIT_INFINITELY)

        # Convert the data from channel as a row order to channel as a column
        data = buffer.T.astype(np.float32)

        # Do something with the data
        return 0      # callback must return an integer, otherwise callback throws a wall of errors


    task.register_every_n_samples_acquired_into_buffer_event(samples_per_buffer, reading_task_callback)

This is what worked for me. I hope it helps

mickey
  • 476
  • 5
  • 18
justengel
  • 6,132
  • 4
  • 26
  • 42
  • Thanks for the answer. I tried to run this code, but it didn't work....first the "self.dtype" it is not recognized and also the code does not runs continuously. How can I get continuos data, for exemple print the data that I am measuring? – Jose Guilherme May 01 '20 at 19:15
  • @JoseGuilherme Sorry, "self.dtype" was some left over code. That spot should just be the resulting data type that you want. I changed it to `np.float32`. If you want this to run a few seconds then you need to keep the program running. If you put `time.sleep(10)` after the task starts you should see the NI device flash for 10 seconds. If you want to do something with the data then you will have to print or append the data to an array where you see "# Do something with the data". Typically, you use a circular buffer that can be accessed in another part of the code with a timer or thread. – justengel May 01 '20 at 20:16
  • Have either of you ever gotten error code -200279 while executing this code? I cannot execute it for more than 5 minutes before getting this buffer overflow issue. – mickey May 30 '23 at 20:36
  • @mickey https://knowledge.ni.com/KnowledgeArticleDetails?id=kA00Z0000019KTeSAM. You may have to change `samples_per_buffer`. Sometimes hardware works better with powers of 2 (1024) instead of a 30 hz update rate. Other than that you might need to increase the buffer size setting. I can't remember what that setting is. – justengel May 31 '23 at 00:18
  • @mickey Also your `reading_task_callback` function might be too slow. Operations like `list.append` would get very slow over time. – justengel May 31 '23 at 02:56
  • @justengel thank you again! I realized that it has to do with the tkinter gui that I built to trigger the task start/stop. When I run your code by itself, it works fine, so I need to simplify the gui so that the mainloop does not interfere with the nidaqmx calls. I am surprised that a simple tkinter window with 2 buttons takes so much processing that, even with a low (1000 Hz) sensor sample rate and (32 Hz) buffer read rate, it slows the computer read rate and eventually causes the buffer overflow. – mickey Jun 01 '23 at 13:24
  • @mickey Hmmm, it is probably more about what resources are allowed to run when than it is with the GUI taking up processing. I would run the NI code in a thread. Have the data save to a preallocated buffer. In your GUI have a timer that displays the data. You also have to make sure the buffer is thread safe, but doesn't block the GUI when reading or writing. I created two libraries to help with these operations. I was using Qt. https://github.com/justengel/np_rw_buffer https://github.com/justengel/qt_thread_updater – justengel Jun 03 '23 at 19:56
  • @justengel thanks for the suggestion! I'm trying to implement the NI code on another thread, and I've used multithreading similar to your implementation in qt_thread_updater, but I'm not sure what should be called in the `target` of the thread. In your example, would it be everything inside of the `with` statement, or the `reading_task_callback`, or the `register_every_n_samples_acquired_into_buffer_event`, or none of the above? I'm unsure how I would pass arguments into the last 2 – mickey Jun 06 '23 at 19:54