LabVIEW

cancel
Showing results for 
Search instead for 
Did you mean: 

Timing multiple loop DAQ software with FFT

Hi everybody,

 

I'm currently building data acquisition and analysis software in LabVIEW for a custom built DAQ-device.
I have managed to get the communication to the device running using NI-VISA's USB RAW communication class.
The software should be able to acquire the signal from the DAQ, display it in real time (time domain) and calculate and display the FFT (frequency domain). Everything should be performed more or less at the same time, so I have built a simple program with multiple, parallel running while loops.
Performance is good, but there are a number of issues I would like to resolve that maybe you help me with.

 

Because it's tied to custom hardware, I have built a simplified version of the original VI for the forum. It's attached to this post.
The basic structure is the same, the FFT scale is wrong in the 'forum version' (but that has probably something to do with the sine wave simulator).

 

Loop 1 is the DAQ loop: it checks multiple times per second (the 'DAQ refresh rate') if the DAQ has new sample data, if so, it will get the data, display it (numeric indicator) and store it in a local variable. The original VI uses a functional global (action engine) instead of a local variable, I have omitted the functional global here to keep it simple.
I have replace the NI-VISA/hardware subVI with a simple sine wave generator.

 

Loop 2 is the time domain display loop: it display the samples in a chart. In this version the loop runs at the same rate as the DAQ loop, in the original version it's the same as the DAQ's hardware sample rate (100 samples/second).

 

Loop 3 is the frequency domain display loop: it stores a number of samples in a buffer array. The contents of the array are used to calculate the FFT. The single-sided real part of the FFT is scaled, made absolute and displayed in a graph.
No FFT windows are applied yet, but a future version will feature a rectangular window.
FFT calculation and scaling is done according to this white paper: http://www.ni.com/white-paper/4541/en

 

There are a number of issues with this VI and the original on which it is based:

 

1. The FFT scale in the 'forum version' is wrong, it seems to be scaled by 2. The original version scales correctly, but this one does not. They both use the same FFT calculation and scaling method, so is this caused by the DAQ simulation?

 

2. The FFT graph 'bounces' up and down. Why does it do that and can the bouncing be decreased by using a different window?

 

3. I have used the 'Wait' subVI to control loop execution priorities. Not every loop has to run at the same speed and the DAQ loop is more important than the display loop, so implementing a timing mechanism seems like a good idea. I'm not sure what's the preferred way to this; is it better to use 'wait until next ms multiple' or does that alter the FFT-calculation?

 

Thanks in advance for your help and/or suggestions.

 

Paul

 

Block diagram

0 Kudos
Message 1 of 7
(3,962 Views)

Paul,

 

using variables for passing values between the loops is a risky thing. You should instead use notifiers/queues for passing data (see master/slave or producer/consumer design pattern).

 

I cannot test your VI, so i cannot answer specific to your questions, but i can give you some thoughts:

 

1. As your code already "considers", FFT is a complex arithmetics. It will result in a mirrowed frequency domain representation. You are cutting this half. I would expect this to be the source of the "scaling error".

 

2. 'Bounce' refers to autoscaling? Try disabling this and work with static scales.

 

3. This is a longer point:

a) Windows is no real time OS, hence your software timed application will never achieve determinism.

b) There is a big difference between Wait(ms) and Wait Until Next Multiple (ms). Read this for further information.

c) You might also try out timed loops for having better information if the loop keeps the desired timing. You can configure it to catch up (shorten iteration timings) or to leave out iterations and "restart in synch". 

 

hope this helps,

Norbert 

Norbert
----------------------------------------------------------------------------------------------------
CEO: What exactly is stopping us from doing this?
Expert: Geometry
Marketing Manager: Just ignore it.
0 Kudos
Message 2 of 7
(3,958 Views)

Your "bounce" is due to the discontinuities in the input signal. Put a graph on the array going into the FFT and you will see that the signal is different on each iteration.

 

Discontinuity.png

 

Like Norbert I do not like using local variables to pass data.  Queues should only be read at one place so you will need to use two queues.  A functional global subVI might be a better choice.

 

For what you seem to be doing Waits should be OK.  If you switch to queues, you can use the timeout on the Dequeue function to provide a delay which waits only until data is available.

 

The keys to synchronizing multiple loops include: 1. Make sure that the receiving loops work quickly enough to consume all the data.  Otherwise you may lose data or have a buffer or memory overflow which can crach your program. 2. Make sure that each loop has some kind of delay so that it does not grab all the CPU resources. Loops without delays are called greedy loops. 3. Make sure that the loops do not interlock in such a way that the program can never stop gracefully.

 

Lynn

Message 3 of 7
(3,954 Views)

Hello Lynn and Norbert,

 

Thank you for your help; I have adjusted the VI accordingly and wanted to give a follow up.

First off a few remarks:

 

- I shouldn't have used the term 'real time' as a requirement for the VI. Windows is indeed a poor choice for an OS if you need real time performance. I guess 'live view' is a better way to describe it: the end-user should see the incoming signal like on an oscilloscope. There is a delay, but it's barely noticeble to the human eye.

 

- the 'bouncing' is indeed caused by the contents of the array that is fed into the FFT subVI. Graph autoscaling was turned off.

 

- The local variables were only used in the simulator VI I posted to the forum. I know of the risks (like creating race conditions), I used them to keep the VI simple. The VI that uses NI-VISA and real world signals makes use of action engines which can't be written and read at the same time.

 

I have attached a new version of the VI I posted to the forum with several alterations:

 

- the local variables have been replaced by a queue system.

 

- I changed the way how data is fed into the FFT subVI to eliminate the "bouncing" of the FFT graph. In the previous version the memory buffer was directly wired to the FFT subVI. This caused problems because the order of data in the memory array is different from what the FFT subVI expects. The memory array is like a ring buffer with old and new data. In the new version the subVI receives sample data in the correct order (sorted from old to new). This is done by rotating the memory array by ((memory buffer size) - (index counter value) - 1).

 

FFT results still seem strange, I will look into it this weekend. I also haven't yet had a chance to implement the changes into the original VI, but I will post a follow up when I have done it and have got some results.

 

Paul

vi-screenshot02.JPG

 

0 Kudos
Message 4 of 7
(3,939 Views)

Paul,

 

you should delete the wait functions in the consumer loops (FFT and display) and instead use timeouts of the dequeue function. The rest looks quite clean and good.

 

Norbert 

Norbert
----------------------------------------------------------------------------------------------------
CEO: What exactly is stopping us from doing this?
Expert: Geometry
Marketing Manager: Just ignore it.
Message 5 of 7
(3,936 Views)

Paul,

 

1. I cleaned up some things. In particular several calculations were being done in Loop 3 which never change while the loop is running.  I moved them outside the loop. I also replaced the case structure and feedback loop in the index line with Quotient and Remainder.

2. To show the effect of changing the FFT Calc rate I put a Get Queue Status function in.  If the # elements in queue indicator is not staying at zero, then Loop 3 is running to slowly and eventually the program will crash with an out of memory error. A boolean turns these things off as they slow the loop considerably.

3. To show the reason for the FFT display changing I added an indicator on the FFT input and calculated the mean of that signal. You can see that the DC component of the spectrum and the mean value change from one iteration to another. Also the spectral values on both sides of the peak vary. This is due to changes in the inut signal, spectral leakage, and windowing effects.

 

Lynn

Message 6 of 7
(3,922 Views)

Hi Norbert and Lynn,

 

Thank you both for your suggestions: removing the timers from the display loops and placing calculations outside the loop is indeed an improvement.

 

I also figured out why the FFT scale was wrong: it's because of the relationship between memory buffer size and hardware sample rate.
If the memory buffer size is 100 elements and the hardware sample rate is 100 samples per second, the graph scale is correct.

If you set the memory buffer to 1000 elements, the scale will be multiplied by 10 (because the FFT array size has been multiplied tenfold).
The solution is to divide buffer size by hardware sample rate, get the reciprocal (1/x) and tie it to the 'XScale.Multiplier' property node of the FFT graph (see attached image).

 

One way of decreasing the 'bouncing' of the FFT graph might be to tie an averaging subVI to the FFT array, similar to the averaging module in 'Dynamic Signal Analyzer (sim).vi' (found in the NI examples library). Have to try that out.

 

Paul

vi-screenshot03.JPG

0 Kudos
Message 7 of 7
(3,902 Views)