ni / nidaqmx-python

A Python API for interacting with NI-DAQmx
Other
447 stars 161 forks source link

Onboard device memory overflow. #615

Closed Optikan closed 4 months ago

Optikan commented 4 months ago

Hi I have a trigger that generates a sin wave of 9 periods every 3kz. I want to get at least 3 points per period for 9 periods and this for 18 channels. For this example I will try to get only 2 channels. Because the sin I generate has a frequency of 30khz i need to use a sample rate of 90khz to get 3 points per period and read 27 samples to have 9 periods.

My goal is to continuously read the data, calculate the fft and then plot it in real time. But if i just read my program wont bug, but as soon as add more operations after around 1Milion samples acquisition i get this error. I don't understand why and how to fix it. I try to use thread to make the operations outside the main function but still.

I have looked up the documentation and searched for answers but i am stuck with this error of " Onboard device memory overflow. "

Here is my code :


import nidaqmx
from nidaqmx.constants import AcquisitionType

import time
import numpy as np 
from nidaqmx.constants import AcquisitionType, ProductCategory,TerminalConfiguration,Level,WAIT_INFINITELY
import threading
import queue 
# Parameters
f = 30000  # Frequency of the sinusoid (30 kHz)
nb_cycle = 9
nb_samble_per_cycle = 3
number_of_samples_per_channel = nb_cycle*nb_samble_per_cycle
sample_rate =  f*nb_samble_per_cycle # Sampling frequency (60 kHz)

ai_args = {'min_val': -1,
            'max_val': 1,
            'terminal_config': TerminalConfiguration.RSE}

def main(q):
    """Acquires data on each digital trigger."""
    print("Start")
    total_read = 0

    with nidaqmx.Task() as ai_task, nidaqmx.Task() as co_task : 

        def callback(task_handle, every_n_samples_event_type, number_of_samples, callback_data):
            """Callback function for reading singals."""
            nonlocal total_read
            nonlocal q
            start = time.time()
            data = ai_task.read(number_of_samples_per_channel=number_of_samples)
            q.put(data)
            # print("added", q.qsize())
            end = time.time() - start
            read = len(data)
            total_read += read
            print(f"Acquired data: {np.array(data).shape} samples. Total {total_read}. Time : {end}", end="\r")

            return 0

        channel = co_task.co_channels.add_co_pulse_chan_freq(
        "Dev1/ctr0", idle_state=Level.LOW, initial_delay=0.0, freq=3000, duty_cycle=0.91
        )
        channel.co_pulse_term = "/Dev1/PFI12"
        co_task.timing.cfg_implicit_timing(sample_mode=AcquisitionType.CONTINUOUS)
        co_task.start()

        ai_task.ai_channels.add_ai_voltage_chan("Dev1/ai0:1",**ai_args)
        ai_task.timing.cfg_samp_clk_timing(
           sample_rate, sample_mode=AcquisitionType.FINITE, samps_per_chan=number_of_samples_per_channel*10
        )
        ai_task.triggers.start_trigger.cfg_dig_edge_start_trig("/Dev1/PFI12")
        ai_task.triggers.start_trigger.retriggerable = True

        ai_task.register_every_n_samples_acquired_into_buffer_event(number_of_samples_per_channel, callback)
        ai_task.start()

        input("Acquiring samples continuously. Press Enter to stop.\n")

        ai_task.stop()
        co_task.stop()
        print(f"\nAcquired {total_read} total samples.")

def test(q):
    while True:
        if not q.empty():
            dd = q.get()
            # print(np.array(dd).shape)

            # iq_signal = dd[1]# + 1j*dd[0]

            # fft_signalIQ = np.fft.fft(iq_signal,norm="forward")
            # fft_freqsIQ = np.fft.fftfreq(len(fft_signalIQ), 1/sample_rate)
            # # Only take the positive half of the spectrum
            # positive_freq_indicesIQ = np.where(fft_freqsIQ >= 0)
            # fft_signalIQ = fft_signalIQ[positive_freq_indicesIQ]
            # fft_freqsIQ = fft_freqsIQ[positive_freq_indicesIQ]

            print("removed" , q.qsize())

        else:  

            print("__________")
            time.sleep(0.01)

if __name__ == "__main__":

    q = queue.Queue()

    # # # Create and start the processing thread
    # process_thread = threading.Thread(target=test,args=(q,))
    # process_thread.daemon = True
    # process_thread.start()

    main(q)

The error :

Traceback (most recent call last):
  File "C:\Users\Optikan\Desktop\inference_julien\Trig.py", line 102, in <module>
    main(q)
  File "C:\Users\Optikan\Desktop\inference_julien\Trig.py", line 65, in main
    ai_task.stop()
  File "C:\Users\Optikan\anaconda3\envs\ni\lib\site-packages\nidaqmx\task.py", line 908, in stop
    self._interpreter.stop_task(self._handle)
  File "C:\Users\Optikan\anaconda3\envs\ni\lib\site-packages\nidaqmx\_library_interpreter.py", line 5427, in stop_task
    self.check_for_error(error_code)
  File "C:\Users\Optikan\anaconda3\envs\ni\lib\site-packages\nidaqmx\_library_interpreter.py", line 6041, in check_for_error
    raise DaqError(extended_error_info, error_code)
nidaqmx.errors.DaqError: Onboard device memory overflow. Because of system and/or bus-bandwidth limitations, the driver could not read data from the device fast enough to keep up with the device throughput.
Reduce your sample rate. If your data transfer method is interrupts, try using DMA or USB Bulk. You can also use a product with more onboard memory or reduce the number of programs your computer is executing concurrently.
Task Name: _unnamedTask<0>

Status Code: -200361
zhindes commented 4 months ago

First, a question: What device are you using?

Regardless of the answer to that question, your application results in settings that are problematic. This code:

# Parameters
f = 30000  # Frequency of the sinusoid (30 kHz)
nb_cycle = 9
nb_samble_per_cycle = 3
number_of_samples_per_channel = nb_cycle*nb_samble_per_cycle
sample_rate =  f*nb_samble_per_cycle # Sampling frequency (60 kHz)

Is resulting in these settings:

That results in ~3333 reads per second. The software overhead of a read call (our driver, Python, and any code you have) is causing your application to not keep up. I see you have some timing code. How many reads per second are you getting? Therefore, the read is not keeping up with the data throughput, causing the data to back up in the driver's buffer. When that is full it backs up into the device's FIFO. This topic on Continuous Acquisition and Generation with Finite Buffer Size has some details on how this all works.

A good rule of thumb is to read about 10 times a second. Even 100 is probably fine, but things like this can be system dependent. You'll need to characterize that.

Also, this isn't a Python-binding specific issue. Closing, and I hope this helps!