ni / nidaqmx-python

A Python API for interacting with NI-DAQmx
Other
443 stars 156 forks source link

nidaqmx AnalogMultiChannelReader stalls with low timeout #106

Closed andreasharbo closed 2 years ago

andreasharbo commented 4 years ago

I am trying to integrate a NI CompactDAQ as an interface in my measurement software. My end goal is to acquire samples continuously from several tasks with several channels in each task. For starters I am having trouble with one task and multiple channels.

The module I am using is NI 9231 (8 mic/accel channels) and I'm having trouble acquiring data from more than 4 channels.

I have made an example that mimics the way that it is done in my software. I'm running with a very low timeout on the stream_reader because I don't want to waste time waiting for the reader, there are other tasks that need reading. So I'm catching the error that the buffer is not full and then reading again until there are enough samples in the buffer. Preferably the timeout would be 0, but that does not work either.

This timeout works fine with 4 channels, but when I add the 5th channel the buffer never gets read. In the example, I'm printing the time between buffer reads.

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

class NIStreamReader(object):

    def __init__(self):
        rate = 51200  # sample rate
        input_buffer_size = 2048  # input buffer size
        timeout = 0.001  # timeout of the stream reader
        rec_length = 3  # length in seconds to record
        n = 0  # timing variable

        system = nidaqmx.system.System.local()  # check your device names and
        for dev in system.devices:              # make sure to use the right one for the channels
            print(dev.name)

        task = nidaqmx.Task()  # create the task

        # add the channels
        # THE PROBLEM: 4 channels works fine, 5 channels doesn't
        task.ai_channels.add_ai_microphone_chan('cDAQ2Mod5/ai0', mic_sensitivity=1000, current_excit_val=0.002)
        task.ai_channels.add_ai_microphone_chan('cDAQ2Mod5/ai1', mic_sensitivity=1000, current_excit_val=0.002)
        task.ai_channels.add_ai_microphone_chan('cDAQ2Mod5/ai2', mic_sensitivity=1000, current_excit_val=0.002)
        task.ai_channels.add_ai_microphone_chan('cDAQ2Mod5/ai3', mic_sensitivity=1000, current_excit_val=0.002)
        # task.ai_channels.add_ai_microphone_chan('cDAQ2Mod5/ai7', mic_sensitivity=1000, current_excit_val=0.002)

        # set the timing and mode
        task.timing.cfg_samp_clk_timing(rate=rate,
                                        sample_mode=constants.AcquisitionType.CONTINUOUS,
                                        samps_per_chan=input_buffer_size)

        reader = stream_readers.AnalogMultiChannelReader(task.in_stream)  # create the reader with the tasks in_stream

        print(task.channel_names)
        data = np.zeros((len(task.channel_names), 0))  # prepare array for recording
        task_buffer = np.zeros((len(task.channel_names), input_buffer_size))  # prepare a task buffer

        task.start()  # start the task
        while True:
            try:
                t = timeit.default_timer()
                reader.read_many_sample(task_buffer, number_of_samples_per_channel=task_buffer.shape[1], timeout=timeout)
                n = 0  # reset the timer variable
                data = np.append(data, task_buffer.copy(), axis=1)  # append input buffer to array
                if data.shape[1] > rate * rec_length:
                    break
            except nidaqmx.errors.DaqError as e:
                if e.error_code == -200284:  # buffer not full
                    t2 = timeit.default_timer() - t
                    print('Time between trys', np.round(t2, 9), ': Time since buffer was read', np.round(n, 9))
                    n += t2

        task.close()  # close task

        plt.plot(data.T)
        plt.show()  # plot the data

if __name__ == '__main__':
    obj = NIStreamReader()

I hope some of you have some input to what I could do. It seems strange that an extra channel stalls the reader completely. If I increase the timeout too much I get buffer overflow errors, so id rather keep it low.

Thanks in advance,

Andreas

morf23 commented 3 years ago

I saw your question in stackoverflow, but couldn't comment it because I have not the required "perks". I don't know if this could solve your problem, but me and my dad are usually using NI9234 for vibration measurements, I am starting to aquire the data with python, but he's got more experience using visual basic. He told me that the way it works, is that you can only use one card every time, so I'm just guessing here, but have you tried to start a task with 4 channels, end the task and start another task with the others? btw, thanks for posting your question in stackoverflow, it really helped me a lot with my project.

zhindes commented 2 years ago

This is a general NI-DAQmx problem and not an issue with the nidaqmx Python bindings. Consider posting on https://forums.ni.com/ for help resolving issues such as this.