04-01-2013 08:02 PM
I am using a PCI-4461 board under CentOS 6.2, using the NI-DAQmx 8.0.2 drivers. I need to read both channels of analog input, and write both channels of analog output. I have the input side working OK, but have found a few quirks on the output side.
My application needs to output various tones and waveforms in a very timely and accurate manner. I first need to address the "accurate" part before I get to the timing quirks.
There will be cases where I need to output a tone continuously, then abruptly stop the tone and generate a finite waveform output as fast as possible. I'm finding that when I interrupt a tone (or even when a finite waveform terminates normally without having the last sample of the waveform set to 0.0), that the first few samples output on the next analog output operation are remnants from the previous analog output operation.
I've taken one of the sample programs, ContGen-IntClk-AnlgStart.c, and modified it to demonstrate the problem. I first output a continuous signal at +5.0 volts. Then I stop that generation, and output a continuous signal at -5.0 volts. When I do this, I see the output (using an oscilloscope) set at +5.0 volts for the first part, then get set to 0 when I stop the generation, then go back up to 5.0 volts for an instant before going down to -5.0 volts. In fact, if you run the program again, it will start outputting at -5.0 volts for an instant, then go back up to 5.0 volts.
The entire program is below - but here is a synopsis :
DAQmxCreateTask
DAQmxCreateAOVoltageChan (1Khz sample rate, DAQmx_Val_ContSamps)
DAQmxWriteAnalogF64 (data is all +5.0)
<let that go for a while>
DAQmxStopTask
DAQmxWriteAnalogF64 (data is all -5.0)
DAQmxStartTask
When the 5.0 volt signal terminates and the -5.0 begins, the timing I see is :
+5.0 volts
0 volts for 117 ms
+5.0 volts for 37 ms (Not correct behavior - there should be no remnant of the previous signal!)
-5.0 volts
Then when the program is started again, I see it start at -5.0 volts for 37 ms before going to the proper level of +5.0 volts.
Is there something I'm doing wrong, or is there some function I can call to force the internal DAC buffers to get set to 0.0, or is this just a "feature" of the board?
============ PROGRAM SOURCE ====================
#include <NIDAQmx.h>
#include <stdio.h>
#include <math.h>
#include <unistd.h>
#include <strings.h>
#define DAQmxErrChk(functionCall) if( DAQmxFailed(error=(functionCall)) ) goto Error; else
int32 CVICALLBACK DoneCallback(TaskHandle taskHandle, int32 status, void *callbackData);
int main(void)
{
int32 error=0;
TaskHandle taskHandle=0;
float64 data1[1000];
float64 data2[1000];
char errBuff[2048]={'\0'};
int i=0;
float64 zeros[1000];
for(i=0;i<1000;i++)
{
data1[i] = +5.0;
data2[i] = -5.0;
}
bzero (zeros, sizeof (float64) * 1000);
/* Demonstrate problem of the previous waveform being generated for an instant
when a new waveform is started.
Timing :
+5.0 volts
0 volts for 117 ms
+5.0 volts for 37 ms <SHOULD NOT HAPPEN>
-5.0 volts
Then when the program is run again :
-5.0 volts for 37 ms <SHOULD NOT HAPPEN>
+5.0 volts
...
*/
/*********************************************/
// DAQmx Configure Code
/*********************************************/
DAQmxErrChk (DAQmxCreateTask("",&taskHandle));
DAQmxErrChk (DAQmxCreateAOVoltageChan(taskHandle,"Dev1/ao0","",-10.0,10.0,DAQmx_Val_Volts,NULL));
DAQmxErrChk (DAQmxCfgSampClkTiming(taskHandle,"",1000.0,DAQmx_Val_Rising,DAQmx_Val_ContSamps,1000));
DAQmxErrChk (DAQmxRegisterDoneEvent(taskHandle,0,DoneCallback,NULL));
/*********************************************/
// DAQmx Write Code
/*********************************************/
DAQmxErrChk (DAQmxWriteAnalogF64(taskHandle,1000,0,10.0,DAQmx_Val_GroupByChannel,data1,NULL,NULL));
/*********************************************/
// DAQmx Start Code
/*********************************************/
DAQmxErrChk (DAQmxStartTask(taskHandle));
printf("Generating +5.0 voltage continuously. Press Enter to generate -5.0\n");
getchar();
/****** NOW write the -5.0 voltage *****/
DAQmxStopTask(taskHandle);
DAQmxErrChk (DAQmxWriteAnalogF64(taskHandle,1000,0,10.0,DAQmx_Val_GroupByChannel,data2,NULL,NULL));
usleep (100000);
DAQmxErrChk (DAQmxStartTask(taskHandle));
printf("Generating -5.0 voltage continuously. Press Enter to quit\n");
getchar();
#if 0
/* This will stop the next DAC output from generating the invalid first sample, but it generates
0V for 18 ms, then -5 volts for 36 ms, then back to 0V */
DAQmxStopTask(taskHandle);
DAQmxErrChk (DAQmxCfgSampClkTiming(taskHandle,"",1000.0,DAQmx_Val_Rising,DAQmx_Val_FiniteSamps,10));
DAQmxErrChk (DAQmxWriteAnalogF64(taskHandle,10,0,10.0,DAQmx_Val_GroupByChannel,zeros,NULL,NULL));
DAQmxErrChk (DAQmxStartTask(taskHandle));
usleep (10000);
#endif
Error:
if( DAQmxFailed(error) )
DAQmxGetExtendedErrorInfo(errBuff,2048);
if( taskHandle!=0 ) {
/*********************************************/
// DAQmx Stop Code
/*********************************************/
DAQmxStopTask(taskHandle);
DAQmxClearTask(taskHandle);
}
if( DAQmxFailed(error) )
printf("DAQmx Error: %s\n",errBuff);
printf("End of program, press Enter key to quit\n");
getchar();
return 0;
}
int32 CVICALLBACK DoneCallback(TaskHandle taskHandle, int32 status, void *callbackData)
{
int32 error=0;
char errBuff[2048]={'\0'};
// Check to see if an error stopped the task.
DAQmxErrChk (status);
Error:
if( DAQmxFailed(error) ) {
DAQmxGetExtendedErrorInfo(errBuff,2048);
DAQmxClearTask(taskHandle);
printf("DAQmx Error: %s\n",errBuff);
}
return 0;
}
04-02-2013 01:14 PM
Since you require accurate timing (precise number of samples) and value in your generation signal, I recommend that you 'Do not allow regeneration' in your analog output task. Regeneration can have the effect that you described where an output will toggle to a new setpoint, but the timing of the output buffers results in a flip back to the old setpoint value before setting to the new output value permanently. Regeneration can allow for better computer performance, but it costs you sample-deterministic behavior.
If you can duplicate the behavior observed when stopping and restarting the task; it is probably worth a quick search on ni.com to see if this behavior has been seen before.
04-02-2013 01:16 PM
What you may be seeing is the effect of DSA filter delay. See page 2-15 of the DSA manual located here:
http://www.ni.com/pdf/manuals/371235h.pdf
To solve this, try using a dummy write as documented here:
http://digital.ni.com/public.nsf/allkb/F989B25FF6CA55C386256CD20056E27D?OpenDocument
04-02-2013 09:25 PM
Doug -
I'm not sure I follow you - you say that I should not allow regeneration in my output task - do you mean not to regenerate analog output data within the same NI task (i.e., calling DAQmxWriteAnalogF64 more than once from within the same task)?
I've tried creating an entire new NI task with the next output waveform, and it also leaves a remnant of the previously transmitted waveform. In fact, if you terminate the program, then start a new program that generates an output waveform, the first few output samples generated are remnants from the previous waveform transmitted by the previous program. I've tried a few searches to see if there's any other posts on this matter but have come up blank.
Kevin.
04-02-2013 09:35 PM
Patrick -
Thanks for the references. It is very coincidental that page 2-15 of the DA manual states that for a sample rate of 1Khz, the output filter delay is 36.6 samples, and I measured 37 ms (which is 37 samples at 1 Khz) where the DAC is outputting the previous waveform. My program's actual output sample rate is 32Khz, and when I tried it at that sample rate the the previous waveform remnant was very small, but still there - the table states that for 32Khz the output filter delay is 43.2, which is (43.2 / 32000 = ) 1.4 ms (I think I measured 3 ms). So it certanly seems like during this "filter delay" the DAC is outputting the previously transmitted waveform. But doesn't that seem like the wrong thing to do - I would think it should output nothing during this delay.
The second reference is for an analog input case - it talks about doing a dummy read when reading ADC data to account for the filter delay - that makes total sense. But my problem is writing the analog output data - I don't see anything in that document talking about dummy writes, which is an entirely different animal.
Kevin.
04-03-2013 04:37 AM
It is fine to call write multiple times within the context of the same input task. I mean that the DAQmx Write property long name = 'Regeneration Mode' should be set to 'Do Not Allow Regeneration'. This means that you have to continuously write new data to the DAQmx buffer. In effect, this means that the output only ses samples that you explicitly sent to the DAQmx buffer via a call to DAQmx Write.
If you abruptly change the output, say from frequency 1 at amplitude 1 to zero, you will see the effect of the antiimaging filters as a short duration ringing in the analog output. Hard to see how this would translate between independent tasks. If you turn off regeneration, let's see if the remnants disappear. Could you post a screenshot of the observed signals?
04-03-2013 12:21 PM
Doug -
That sounds like it might do the trick, but what is the API to do that?
When I first started to attempt to program the 4461 a couple weeks ago, I was somewhat taken aback by the lack of documentation provided for programming the board in C under Linux. Through some help from NI support, I was directed to the C function reference guide, which, while extremely helpful, is like having someone learn a foreign language by giving them a dictionary. But between that and the example code I was able to figure out how to program the board. However,I don't see any "Regeneration Mode" setting in any of the functions. What is the specific function call to set the "Do Not Allow Regeneration" setting? Also, using this mode, do you still program the board to use a specific output sample rate, or is the timing of the waveform up to your application program (which wouldn't be anywhere near as accurate as using the DAC to control the timing of the sample output)?
Thanks,
Kevin
04-19-2013 07:25 PM
Just to follow up on this in case anyone else out there is having problems with this flaw - here is what we do to minimize it :
If we are transmitting a waveform with sampleMode == DAQmx_Val_FiniteSamps, then the solution is easy - we just add a single sample to the end of the waveform buffer and set it to 0. When the next waveform is transmitted, the remnant that is transmitted is then 0 volts, and there is no obvious hiccup in the output transmission.
However, if we are transmitting a tone with sampleMode == DAQmx_Val_ContSamps or DAQmx_Val_FiniteSamps and we stop the task to stop the transmission, that is where we have the glitch that I can't seem to avoid. Because we need the next transmission to go out cleanly, we can make sure that the glitch occurs after our stopped transmission instead of at the beginning of the next one. To do this, we make the following calls :
DAQmxStopTask(taskHandle); // Stop the current transmission
DAQmxCfgSampClkTiming (taskHandle, "", 204800,
DAQmx_Val_Rising, DAQmx_Val_FiniteSamps, 64);
DAQmxWriteAnalogF64 (taskHandle, 64, 0,
DAQmx_Val_WaitInfinitely,
DAQmx_Val_GroupByChannel, zeroData, NULL, NULL); // zeroData == buffer with all 0.0's
DAQmxStartTask (taskHandle);
DAQmxWaitUntilTaskDone (taskHandle, DAQmx_Val_WaitInfinitely);
DAQmxStopTask (taskHandle);
So what this does is to write, at the fastest sample rate possible, a small amount of 0's, and then immediately transmit those 0's. What we see in this case is :
Previous waveform ends and DAC outputs 0.0 volts
This goes on for about 18.4 milliseconds (it takes around 17 ms for the board to start the new transmission)
We then get our glitch - the last sample of the previously transmitted waveform is transmitted, but only for about 800 microseconds. It's annoying, but at least we can control when it happens and make it is short as possible.
I've also discovered some new (to me) behavior of the calls to transmit data, and just wanted to see if anyone can confirm this is indeed the expected behavior (or even better point me to somewhere where this is documented!)
If you call DAQmxWriteAnalogF64, start the task, stop the task, and then start the task again, the same waveform that was loaded and transmitted the first time is transmitted again. I think this is expected and a good thing.
If you call DAQmxWriteAnalogF64, start the task, stop the task, and then call DAQmxWriteAnalogF64 with different data, it overwrites what was written the first time, so that when you start the task again the new data is transmitted. Again, this is the expected behavior and a good thing.
But, the strange thing I discovered is that if you call DAQmxWriteAnalogF64, but then before you start the task, your waveform changes so you call DAQmxWriteAnalogF64 again with the new waveform, it appends the data to that which was written the first time. So when you start the task, you get the data written in the first call to DAQmxWriteAnalogF64, followed by the data written in the second call. The problem is, I can't find any way to clear out the data from the first call. In our application, we preload a waveform so that when it comes time to actually transmit, we simply call DAQmxStartTask and the latency from this time to when the waveform is actually transmitted is about 17 milliseconds (at a sample rate of 32000). However, if a different waveform needs to be preloaded over the previous one, it seems I have to call DAQmxClearTask and then create a brand new task and configure it via : DAQmxCreateTask and DAQmxCreateAOVoltageChan, which takes several hundred milliseconds to do. Does anyone know of a way to clear the internal buffer so that I don't have to do this? I couldn't find anything in the C reference help document.