LabVIEW

cancel
Showing results for 
Search instead for 
Did you mean: 

Generate Finite Analog Output Waveform on Demand

Solved!
Go to solution

Hello.

I have a VI that reads in two channels of data (400 samples @ 400 Hz), computes features, and classifies the data based on a model.  If the class is a certain state, a boolean called "Detection" is set to True.  A second Boolean called "Stimulation" is also set to true, and flip-flops on and off every second until the "Detection" is no longer true.  Since I am reading and classifying one second of data, the booleans can only be updated once per second.  I am wanting to generate an analog output while the "Stimulation" boolean is true.  I had been accomplishing this by continuously generating a sine wave and writing it to a buffer in a separate loop (500 samples per write @ 10kHz), and multiplying it by zeros if "Stimulation" was false.  I have a code that more-or-less does what I want, but there is a ~200ms delay between "Stimulation" being set to True and AO generation, and I feel like it could be simplified.  I was wondering if it would be better to generate a finite waveform and trigger it to be written with the "Stimulation" boolean (i.e. write 1-second of a sine wave upon the state changing rather than writing 20 sections of a sine wave in a second while the boolean is True).  

 

Is there a good way to this with the daqmx functions? (Generate a set duration waveform when a control changes)

Message 1 of 31
(7,569 Views)

Try Help -> Find examples with continuous generation and on-board regeneration, for example 

Cont Gen Voltage Wfm-Int Clk.vi

Start task when you need to start it, not immediately after write. It will regenerate buffer continuously untill you call stop task. 

When you stop task in arbitrary moment,  output will maintain last value, you will need to generate a short buffer (1 sample) of zeros, it will take a few ms to switch between tasks.

 

0 Kudos
Message 2 of 31
(7,555 Views)

@Alexander_Sobolev wrote:

it will take a few ms to switch between tasks. 


Or if the sample rate, and number of samples don't change, then you can replace the data in the buffer that is being sent out while the task is running.  Then the same task is only started once, and stopped once.  I can't seem to find an example of this running but I know I've done this trick in the past when I had a PWM that I wanted to change the duty cycle but not the frequency or number of samples, without stopping the task.

 

EDIT:  Best I could do is fine a thread where I mentioned this technique with a few more details mentioned.

 

http://forums.ni.com/t5/LabVIEW/Changing-AO-during-runtime-while-other-AO-continue-to-run/m-p/319423...

0 Kudos
Message 3 of 31
(7,533 Views)

Generally there are two basic ways to try to get lower latency for AO signals:

 

1. use software-timed, on-demand mode for your AO task with no buffer of values.  This is probably *not* a good option for you.  If you have a carefully crafted waveform to generate at a 10 kHz update rate, software timing will not get you anywhere close to what you need.  Low latency yes, but poor ability to replicate the desired waveform.

 

2. manipulate the hw-timed, buffered AO task such that you are providing the task with output data just *barely* before it will be D/A converted.  This is tricky and a little risky.  If you fall behind on feeding data to the task, it will error out.  Item # 1 in a snippet I recently posted has some relevance.  That DAQmx property is probably a necessary part of the overall solution, but not nearly sufficient by itself.  There's other work to be done to carefully feed the task just-in-time.

 

 

-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 4 of 31
(7,503 Views)

Thanks for the input guys. I've tried looking at examples, scoured the web, and I'm still stuck.. I have made a simple VI that almost accomplishes what I want.  When you click the boolean switch, it sends an analog output of specified duration to the AO channel specified.  However, when the task ends, I get error 200018:

 

Possible reason(s):

DAC conversion attempted before data to be converted was available.

Decrease the output frequency to increase the period between DAC conversions, or reduce the size of your output buffer in order to write data more often. If you are using an external clock, check your signal for the presence of noise or glitches.

 

Furthermore, if I change the sample mode from "Continuous Samples" to "Finite Samples" and number of Samples to 10k (same number of samples as waveform I am generating), the VI generates the output on the first click, but doesnt not do so afterward and does not give an error.

 

My end game is to add this into a larger VI (in its own loop) and have it be triggered by a boolean that is generated every second in a separate loop.. if that makes sense.

 

Here are some snap shots of the main VI and two sub VIs.

 

 

 

 

Download All
0 Kudos
Message 5 of 31
(7,439 Views)

The error makes sense for continuous mode.  You've configured the task for non-regeneration and you only write to it once.

 

As for finite sampling mode, I'm not seeing the reason for it to only be able to respond once.  The only thing of note I see is that generating 9999 samples at 10 kHz means that you're attempting to allow only 100 microsec to stop the task, receive the once-per-second trigger, reconfigure the task, compute a waveform and write data to it, and start it again.  That'll definitely not work.  I'd expect several 10's of millsec minimum.

 

But still, I would think you could trigger it more than once, it'd just be slightly delayed from the timing you wanted.

 

 

-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 6 of 31
(7,404 Views)

That makes sense. If I remember right, I think I did try changing it to a small number of samples (e.g. 1000 samples) to see if it would let me trigger it more than once, but I don't think it worked.  I'll try that again tomorrow.  

 

In some cases I am wanting to generate 1-second of an analog output, but in some cases I want to generate a longer one (20 or 30 seconds).  What would be the best way to configure the analog output and generating the signal to serve both of these purposes?  Ideally, I'm wanting to make one or two sub VIs that accept the boolean as a trigger.

 

0 Kudos
Message 7 of 31
(7,390 Views)

Backpedaling a few steps to have another look at the big picture.  After reviewing some of your earlier posts that sound like they're related to the same project, some of the more complicated advice given by myself and others in the first few posts of this thread may not be needed.  Those focused on continuously generating AO with changing waveform properties and low latency for changes.

 

It sounds like you may only need intermittent finite AO, each burst having variable waveform properties, and still need low latency for changes.  Fortunately, this is very *very* much simpler.  Still, there are some general things to keep in mind and watch out for:

 

1. When you stop an AO task, the most recently D/A converted voltage value will remain at the output pin.  You probably want to devise an appropriate scheme to start and end at 0 V since the voltage drives a physical actuator.

 

2. I'd recommend putting the AO code inside a "queued message handler" loop.  There's a template for projects based on the queued message handler pattern that ships with LabVIEW.  One benefit is that the message queue makes for a much cleaner triggering mechanism than polling a boolean control or local variable.  Generally, be careful in your thinking about what things are driven by a boolean T/F *state* and what things are driven by a boolean rising/falling *transition*.

 

3. You probably shouldn't need to totally reconfigure the AO for every burst of generation.  It increases your latency.  However it probably isn't a big factor in the grand scheme of things considering that you still *do* need to be able to reallocate the output buffer for different waveform durations.  

 

4. In your 'MainVI' snapshot, you conditionally call your generation sub-vi to Start your task, but then unconditionally Stop the task.  The Stop likely belongs inside that case structure.  Don't forget point #1 above though, you also still want to be sure you've gently brought the output back to 0.

 

 

-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.
Message 8 of 31
(7,378 Views)

To address your points individually:

1. My initial thinking that since I am writing 1-second of a sine wave that it would start and end at zero. However, I now realize that the last point would not be zero, so that needs to be addressed.

 

2. When you say "Queue Message Handler" are you referring to a mechanism like the one in the attached image "queue.png"? It is what I am currently using for my Acquisition, but I haven't tried something like that for output.

 

3.Not sure what you meant on this one, do you mean where I am regenerating a sinewave every time im calling it to write?  I did this because in some experiments I will want to change the frequency/amplitude of the sine wave every write. (e.g. generate 40 Hz for 30 sec, wait 15 sec, generate 30 Hz for 30 sec, wait 15 sec, etc.)

 

4. I removed the Stop Task vi from the 'main loop' and placed it at the end of the 'GenerateAO' vi.  It seems to be working better, but still not quite right.  I have the boolean switch on the front pannel to release with the mouse release (immediately reset to false).  The first time I click the switch, it generates a 1-second output as expected. However, in subsequent generations it only generates for a very short duration, and if I hold it in it sounds like it is stopping and restarting several times.  I thought this may be because of the button being set to "switch until released" but if I change it to "switch until pressed" then it exhibits the same behavior. 

 

0 Kudos
Message 9 of 31
(7,370 Views)

2. Somewhat like that, yes.  In your pic, the queue sends data.  In my proposal, the queue sends a command message (such as "configure", "define waveform", "start generation", "stop generation", etc.).  The QMH template defines messages as a cluster datatype that joins a command message string with a *variant* to hold any arbitrary data.  The case that handles a particular command message needs to know how to convert the variant into the correct datatype. 

 

3. I understand your need to define and DAQmx Write a different sinewave every "trigger."   Most of the things in your ConfigAO don't need to be called every trigger.  In fact, I now notice that you keep creating new tasks in ConfigAO but you never clear them with DAQmx Clear at the end of the generation loop.  I think that'll eventually come back to bite you, if it hasn't already.

   Note that if you adopt a QMH-like structure (#2 above), you may be able to do the somewhat time-consuming "define waveform" function well in advance of the moment you need to trigger, saving you some of your latency.

 

4. I'll reiterate a previous point.  Be really careful in thinking through the distinction between boolean *states* and boolean *transitions*.  The QMH structure supports software "triggers" pretty naturally if you feed them in correctly.  Your description sounds like there's a bit of a muddle about GUI controls and polling and states and latching and transitions.

   In short, I suspect the bulk of your problem is in this level of application code, how & when you're trying to "trigger" the DAQ code.  The DAQ code itself needs no more than minor tweaks.

 

 

-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 10 of 31
(7,348 Views)