05-06-2009 09:47 AM
Hello,
I am performing continuous acquisition of voltage using DAQmx, and I am puzzled as to how I can append new samples to an existing waveform array. Everytime I run my program, I only receive 1000 samples of data in the AnalogWaveform<Double>[] array. Since I need to perform some math after every asynchronous read, I would like to have the waveform array "accumulate" data until the acquisition stops. Then, I can plot the entire waveform. Is this even possible, or should I just store the data in a WaveformPlot? Any suggestions would be appreciated. Thank you!! Here is my callback code:
public void ReadCallBack_MeteringTask(IAsyncResult ar)
{
try
{
if (runningTask == ar.AsyncState)
{
// read the data
AnalogWaveform<Double>[] waveforms = daqReader.EndReadWaveform(ar);
// cancel acquisition if trigger channel is invalid
if (triggerChannelIndex < 0 || triggerChannelIndex >= meteringTask.AIChannels.Count)
{
meteringTask.Dispose();
runningTask = null;
System.Windows.Forms.MessageBox.Show("Invalid trigger channel index.");
return;
}
// continue acquiring data until short circuit has been detected
shortCircuitInitiated = IsShortCircuitInitiated(waveforms, triggerChannelIndex, desiredLevel);
if (shortCircuitInitiated)
{
// short circuit detected, read more and stop acquisition
daqReader.MemoryOptimizedReadWaveform(desiredSamplesPerChannel, waveforms);
// Stop the task
meteringTask.Dispose();
runningTask = null;
return;
}
// Read the next set of samples
daqReader.BeginMemoryOptimizedReadWaveform(desiredSamplesPerChannel, callBack, meteringTask, waveforms);
}
}
catch (DaqException ex)
{
meteringTask.Dispose();
runningTask = null;
System.Windows.Forms.MessageBox.Show(ex.Message);
}
}
Solved! Go to Solution.
05-07-2009 02:08 PM
08-18-2009 03:01 PM
Hi Adri,
Thank you for your response. After some time off from this project, I'm back to the same roadblock. I have a top-level variable called "AcquiredWaveforms" that I want to use to store the complete waveform from start to finish. After each read, I store the data in "waveforms" within the callback, then I want to append "waveforms" onto the end of "AcquiredWaveforms". However, I am unable to make this work, since there seems to be no native way to append data with these datatypes. There is a concat method available, however, it does not seem to accept the waveform data at run-time. Is there another method, or an NI method for appending data from one AnalogWaveform[] to another? Thanks again!!
-Eric
08-19-2009 04:44 PM
Eric,
Do you want to keep the entire waveform to do some calculation on the entire thing or do you just want to plot it? If you just want to plot it, you can call PlotWaveformAppend<Double>(waveform) whenever you get a new waveform to plot everything on the graph. Also, to collect all the information you could just make an array of type AnalogWaveform<Double> and store all the waveforms there, but that would not append the data...it would just keep it all in one place.
08-20-2009 08:23 AM
I would like to keep the entire waveform. What I want the system to do is to start acquiring data when the system is "armed". Once armed, data will be read asynchronously using a callback, until the system is stopped by an external software trigger. After each asynchronous read, I want to append the current data to another AnalogWaveform<> containing the previously read data. That way, at the end of the acquisition, I should have several seconds of total data, starting from 0 seconds. I want to use an AnalogWaveform<> becuase I will need the precision timing values from the entire waveform to do post-acquisition analysis.
As for plotting, I will want to plot the data for visual analysis as well. Once the data is acquired, I can simply dump the data from the AnalogWaveform<> into a plot at a later time.
Thanks again for your help!!
-Eric
08-24-2009 04:37 PM
Hi Eric,
Unfortunately it seems that the AnalogWaveform data type has no ability to append a waveform, mostly due to uncertainty as how to handle waveforms with different timing. In your case, since it's all the same timing, we should be able to piece together a solution.
First, note that AnalogWaveform<Double>[] is an array of AnalogWaveform datatypes returned by your data acquisition function. The array represents the number of channels that your acquisition task is measuring. This example I will provide is for one channel, but you should be able to extrapolate it into multiple channels.
We have to create a global AnalogWaveform variable that will maintain the variable information. Then, every time your function runs, you need to add the waveform information to the end of the global variable. Due to some restrictions in the datatype, we have to do some things to work around this, but hopefully the sample code below is clear. If it isn't, please reply with any questions you may have.
public AnalogWaveform<Double> globalWaveform = new AnalogWaveform<Double>(0); //set to 0 because it is resized later
....
AnalogWaveform<Double>[] waveforms = daqReader.EndReadWaveform(ar);
//First, you have to create a temporary waveform variable that is as large as the existing waveform data stored in the global data plus the size of the incoming data
AnalogWaveform<Double> tempWaveform = new AnalogWaveform<Double>(globalWaveform.Samples.Count + 100); //replace 100 with the known size of your waveform you read in the previous step
//Then we have to copy the timing information from the waveform. Since your timing information won't change, we can just make this copy every time your code runs
tempWaveform.Timing = waveforms[0].Timing;
tempWaveform.PrecisionTiming = waveforms[0].PrecisionTiming;
tempWaveform.ScaleMode = waveforms[0].ScaleMode;
//Then we need to copy the existing globalWaveform data to the larger temporary variable. This must be done because there is no way to append to the global variable.
int index=0;
for (int j=0; j < globalWaveform.Samples.Count; j++)
{
tempWaveform.Samples[index++].Value = globalWaveform.Samples[j].Value;
}
//Finally, we copy the data from the new waveform read in into the tempWaveform variable. This means that tempWaveform is a local variable that contains the entire waveform.
for (int k=0; k < waveforms[0].Samples.Count; k++)
{
tempWaveform.Samples[index++].Value = waveforms[0].Samples[k].Value;
}
//The last step has us make globalWaveform point back to the entire waveform. When this function goes out of scope, the data will still exist since globalWaveform is pointing to it.
globalWaveform = tempWaveform;
08-24-2009 04:38 PM
Eric,
I forgot to note that this is a valid request so I'll be filing a bug report/feature request on your behalf for the developers to look into adding this to a future version of Measurement Studio.
09-08-2009 01:01 PM
Eric,
Thanks for the solution..it works perfectly! I have expanded this method for multiple channels, as you suggested, with no problems. Here is the extrapolated solution:
public void ReadCallBack_MeteringTask(IAsyncResult ar)
{
try
{
if (meteringTask == ar.AsyncState)
{
// read the data
AnalogWaveform<double>[] waveforms = daqReader.EndReadWaveform(ar);
//////// append new data to existing waveform array (AcquiredWaveforms)
// create new temporary AnalogWaveform<>. Will make one for each channel to append
AnalogWaveform<double> waveformToAppend;
// populate waveformToAppend with new data
for (int channel = 0; channel < meteringTask.AIChannels.Count; channel++)
{
// copy first iteration only
if (AcquiredWaveforms[channel] == null)
{
AcquiredWaveforms[channel] = waveforms[channel];
}
// initialize waveformToAppend
waveformToAppend = new AnalogWaveform<double>(AcquiredWaveforms[channel].SampleCount + waveforms[channel].SampleCount);
// copy timing information from new data to waveformToAppend
waveformToAppend.Timing = waveforms[channel].Timing;
waveformToAppend.PrecisionTiming = waveforms[channel].PrecisionTiming;
waveformToAppend.ScaleMode = waveforms[channel].ScaleMode;
// copy global waveform data into new temporary waveform
int index = 0;
for (int j = 0; j < AcquiredWaveforms[channel].Samples.Count; j++)
{
waveformToAppend.Samples[index].Value = AcquiredWaveforms[channel].Samples[j].Value;
index++;
}
// copy data from new waveform to temporary waveform
for (int k = 0; k < waveforms[channel].Samples.Count; k++)
{
waveformToAppend.Samples[index].Value = waveforms[channel].Samples[k].Value;
index++;
}
AcquiredWaveforms[channel] = new AnalogWaveform<double>(waveformToAppend.SampleCount);
AcquiredWaveforms[channel] = waveformToAppend;
}
/////////////// finished appending data to global acquired waveforms
Application.DoEvents();
// Read the next set of samples
daqReader.BeginReadWaveform(Convert.ToInt32(meteringTask.Timing.SamplesPerChannel), callBack, meteringTask);
}
}
catch (DaqException ex)
{
Console.WriteLine("ReadCallBack_MeteringTask(): " + ex.Message);
//meteringTask.Dispose();
}
}
Thanks again!
-Eric
09-09-2009 02:46 PM
09-11-2009 04:30 AM
Hi Eric,
It seems from the post that you have good experience working with Ni DAQ devices.
I am new to working with these Ni DAQ devices.
I donot have any idea about how to initialize the ADC(Analog to Digital converter) with the required sample frequency and the channel number.
After the initialization process I want to read the buffer for the data when the buffer fills completly. I think the device generates an event(I dunno which event?) when the buffer is full. I want to capture this event where I will read the buffer for the data available in it.
Any kind of help is highly appreciated.
Thanks,
Ajay