Multifunction DAQ

cancel
Showing results for 
Search instead for 
Did you mean: 

Continuous write analog voltage NI cDAQ 9178 with callbacks

Solved!
Go to solution

UPDATE: I can aim for very low latency apparently: I set the bufsize to 10 (along with bufsize_clk and rate_callback of course), aiming at 100Hz refresh rate (10ms latency). I have timed the process of going from 0 to 5V by increasing the output voltage dynamically by 1/1000th volts every 1/100 seconds, so that it would take 50 seconds to get there. I've only done this with my phone but the result is as consistent as it is impressive: I get to 5.00V (on a multimeter) in exactly 50 seconds, and the multimeter updates pretty smoothly so I believe every write_out action is successful.

 

Basically it seems I can do just about anything now, in terms of latency and so on. Looks like the culprit of my issue was really the regeneration, and not the driver's general ability to reach low latency (which makes this driver more versatile than even you thought at first, I guess).

 

Also, given what I want to do, and if I have understood buffer regeneration well, it is now absolutely obvious to me that NO regeneration is what I should have gone for (since, basically, I am updating the output dynamically, as opposed to having my application generate some type of curve over and over continuously - which would be implemented as some number of samples I would feed to the buffer and the latter would be regenerated by the driver or something like that I guess). Is that correct thinking you believe ?

 

More comments from you are always appreciated here, to nail what exactly is going on. In any case, and whether or not I'll end up hardware timing this (not sure I will, after all, as a matter of fact), thank you very much. You have helped a lot and I've understood many things thanks to your remarks.

 

Cheers !

 

Gustavo

Message 11 of 16
(2,426 Views)

I've been hoping to follow up after getting on real DAQ hardware, but haven't been able to lately.

 

In the past when I experimented with trying to do low latency hw-clocked AO, it wasn't trivial to get < 10 msec even with a PCIe device.  I recall that in order to get there, I needed to set up a few different low-level and fairly advanced DAQmx properties to non-default values.  I wouldn't have expected the defaults for a cDAQ system over USB to support that kind of usage so easily.

 

It looks like the big key is to explicitly declare the task not to allow regeneration.  Apparently, DAQmx uses this setting to drive significant changes in how it transfers data from the task buffer to the device (and maybe it also affects the size of the additional FIFO buffer on the device itself?)

 

All that said, the present app may still do fine with an unbuffered, software-timed AO task.  A non-regenerating task with a small buffer will always be at risk of erroring out, which might be a worse result than the timing jitter you'd get from a software timed update.

 

 

-Kevin P

ALERT! LabVIEW's subscription-only policy came to an end (finally!). Unfortunately, pricing favors the captured and committed over new adopters -- so tread carefully.
0 Kudos
Message 12 of 16
(2,377 Views)

Yep, thanks again.

 

Indeed I have set it up to be a stupid while loop with what needs to be done inside, and it works like a charm (I get approximately 40Hz, which is pretty good). In the loop I read some AI, filter them, store them for further analysis, do some PID and then write the output of these calculations to AOs. Really simple and easy.

 

Now for a slightly different purpose I'm going to have to actually dive into hardware-timed *reads* of AI, but that is very different from my original question which was about AOs so I guess if I encounter problems I'll post a separate thread.

 

Thanks!!

 

Gustavo

0 Kudos
Message 13 of 16
(2,368 Views)

It is possible to make what you need to do to paintings. You simply must do it without callback, In this precise case, your waveform is a easy ramp up wave step characteristic. You can just assemble entire facts set as an array and write all of the it into the buffer straight away https://forums.ni.com/t5/Multifunction-DAQ/Analog-read-too-slow-Expected-behavior/td-p/4049584 here. With regeneration, then your waveform will be regenerated constantly with the clocking timing you have got set up till you stop the undertaking. This is a typically streaming workflow.

0 Kudos
Message 14 of 16
(2,323 Views)

Just wanted to heartily thank you for sharing your code, seeing as I'm pretty sure it's the only actual example of how to do non-regeneration in python on the web haha. I found it to be a very helpful resource. 🙂

0 Kudos
Message 15 of 16
(1,998 Views)

Hello,

 

I recognize this is an old thread but echo j_hubert when I say how much I appreciated the code example. I took a stab at it and tried reorganizing the code to look a bit more like it does on the nidaqmx-python examples in Github. I think the callback function had more detail than it needed with the counter, as the callback function should only be triggered at somewhat predictable intervals to begin with. So, just tossing this out there in case it helps someone else in a year or so. It works for me. 

 

# Continuous write single channel
import numpy as np

import nidaqmx
from nidaqmx.stream_writers import (AnalogSingleChannelWriter)
from nidaqmx.constants import RegenerationMode


nSamples = 10      # number of samples to write per channel, also to trigger callback
sampleRate = 100    # (Hz)
updateDelay = nSamples / sampleRate  # time between callbacks(s)

vps = 0.25              # volts per second, rate at which to ramp voltage 
dV = updateDelay * vps  # voltage increased every callback

global data # initial write
data = np.linspace(0, dV, nSamples)

with nidaqmx.Task() as writeTask:
# define task
    writeTask.ao_channels.add_ao_voltage_chan("PXI1Slot5/ao2")
    writeTask.timing.cfg_samp_clk_timing(
        rate = sampleRate,
        sample_mode = nidaqmx.constants.AcquisitionType.CONTINUOUS,
        samps_per_chan = nSamples
    )  # last arg is the buffer size for continuous output

    writeTask.out_stream.regen_mode = RegenerationMode.DONT_ALLOW_REGENERATION


    def writeCallback(task_idx, every_n_samples_event_type, num_of_samples, callback_data):
        global data

        data[:] = data[:] + dV          # modify the array
        stream.write_many_sample(data)  # write the array

        return 0

    # define stream writer
    stream = AnalogSingleChannelWriter(
        writeTask.out_stream, 
        auto_start=False # with auto_start=True it complains
    )  

    # Callback function is called whenever the buffer is out of samples to write
    writeTask.register_every_n_samples_transferred_from_buffer_event(
        nSamples, 
        writeCallback
    )

    stream.write_many_sample(data)  # first write (necessary to clear buffer)

    writeTask.start()
    input('press ENTER to stop')  # task runs for as long as ENTER is not pressed

 

0 Kudos
Message 16 of 16
(1,587 Views)