Measurement Studio for .NET Languages

cancel
Showing results for 
Search instead for 
Did you mean: 

Append data to an existing AnalogWaveform<Double>[]

Solved!
Go to solution

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);
            }
        }

0 Kudos
Message 1 of 19
(7,205 Views)
Hi Eric,

My recommendation would be to create a separate variable than the AnalogWaveform<Double>[] data type you are using to acquire the data and use it to store the data. You can then append to this variable and display its contents after the acquisition and data manipulation are complete.

-Adri
Adri Kruger
National Instruments
LabVIEW Product Marketing
0 Kudos
Message 2 of 19
(7,180 Views)

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

0 Kudos
Message 3 of 19
(6,978 Views)

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.

Eric B.
National Instruments
0 Kudos
Message 4 of 19
(6,962 Views)

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

0 Kudos
Message 5 of 19
(6,950 Views)
Solution
Accepted by topic author EricJay

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;

 

 

Eric B.
National Instruments
0 Kudos
Message 6 of 19
(6,920 Views)

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.

Eric B.
National Instruments
0 Kudos
Message 7 of 19
(6,919 Views)

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

Message 8 of 19
(6,828 Views)
Glad to hear it!  And thanks for posting the expanded solution; it should be helpful if people find this in a search later on.
Eric B.
National Instruments
0 Kudos
Message 9 of 19
(6,807 Views)

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

0 Kudos
Message 10 of 19
(6,787 Views)