DSPRelated.com
Forums

ARM CMSIS FFT output scaling

Started by DNadler 7 years ago8 replieslatest reply 7 years ago1592 views

Background: To analyze mechanical vibration, I'm sampling a G-meter at 1kHz for 1.024 second, doing an FFT, and calculating the magnitude per bin. The scaling factors in the ARM CMSIS output are a bit bizarre and if they're documented its eluded me. It seems like frequencies are scaled by 512, except that DC is scaled by 1024. Is this really correct? Here's my test code:

// buckets to save FFT inputs
#define FFT_INPUT_SAMPLES 1024
static float32_t X_samples[FFT_INPUT_SAMPLES], Y_samples[FFT_INPUT_SAMPLES], Z_samples[FFT_INPUT_SAMPLES];
static float32_t FFT_results[FFT_INPUT_SAMPLES*2]; // 2x because FFT returns real and imaginary parts, twice...
static float32_t FFT_magnitudes[FFT_INPUT_SAMPLES]; static void AddTestFFTsinewave(float32_t (&samples)[FFT_INPUT_SAMPLES], double magnitude, double frequencyHz) {     for(int i=0; i<FFT_INPUT_SAMPLES; i++) {         double rads = (double)i*frequencyHz* 2*M_PI/FFT_INPUT_SAMPLES;         samples[i] += magnitude*sin(rads);     } } static void AddTestFFTdc(float32_t (&samples)[FFT_INPUT_SAMPLES], double magnitude) {     for(int i=0; i<FFT_INPUT_SAMPLES; i++) { samples[i] += magnitude; } } static void PerformFFT(float32_t (&samples)[FFT_INPUT_SAMPLES]) {     arm_rfft_fast_instance_f32 armFFTcontrolStruct;     arm_rfft_fast_init_f32(&armFFTcontrolStruct, FFT_INPUT_SAMPLES);     arm_rfft_fast_f32(&armFFTcontrolStruct, samples, FFT_results, /* inverse FFT flag = */0);     // convert real-complex binned pairs into magnitudes     arm_cmplx_mag_f32(FFT_results, FFT_magnitudes, FFT_INPUT_SAMPLES); } static void test() {     AddTestFFTdc(X_samples,/* magnitude= */27.0); // start with a DC component     AddTestFFTsinewave(X_samples,/* magnitude= */2.0, /* frequency= */1.0/*Hz*/);     AddTestFFTsinewave(X_samples,/* magnitude= */1.0, /* frequency= */5.0/*Hz*/);     PerformFFT(X_samples); }

Printing the resulting magnitude/512.0 gives:
54.00, 2.00, 0.00, 0.00, 0.00, 1.00, 0.00, ... 

So at least the sines are behaving as expected (given 512.0 scaling).
The complex portion of the bin 0 FFT result is 0.0
Is the DC result really just scaled by another factor of 2???

Or have I done something silly?
Thanks in advance,
Best Regards, Dave



[ - ]
Reply by kirantgApril 8, 2017

Hello DNadler,


Check the magnitude of your FFT output  - bin #1 and #1023 must have the same magnitude, and similarly bin #5 and #1019 must have same magnitude. This is the conjugate-symmetry property of FFT on a "real-valued" signal. 

The scaling is actually 1024, so you should have the right DC value, but the power of the sinusoid is distributed equally in the positive and negative frequencies.

Hope this helps,

Kiran

[ - ]
Reply by Tim WescottApril 8, 2017

The normal FFT -- as in, done more than \(99 \frac{44}{100}\) percent of the time -- expects complex inputs, and gives you frequency bins corresponding to a complex tone \(x_n = e^{j 2\pi\frac{n}{N}}\).  DC corresponds to \(x_0 = 1\).  A sine wave, \(\cos \omega t\), corresponds to two tones, because \(\cos \omega t = \frac{e^{j \omega t} + e^{-j \omega t}}{2}\).  So the FFT reflects both of those complex tones -- one at \(+\omega\), and one at \(-\omega\).

This gives rise to what kirantg said.

[ - ]
Reply by DNadlerApril 8, 2017

Got it; missed that arm magnitude function didn't sum positive and negative frequencies. Since they are by definition identical, I can either add them or just adjust the scaling factor for the lower bins, right?
Thanks for Kiran and Tim,
Best Regards, Dave

[ - ]
Reply by Tim WescottApril 8, 2017

Well, not really.  The total energy in the FFT bins will be the same as the total energy in the time-domain signal.  Consider that a unit-magnitude sine wave has a power of \(\frac{1}{\sqrt{2}}\), not 1.

[ - ]
Reply by DNadlerApril 8, 2017

Apologies Tim, I don't understand.
Client wants to see acceleration in XYZ, in Gs, from DC to 500Hz.
Isn't the magnitude of the sine-wave the G-load (ie no power scaling)?
Thanks,
Best Regards, Dave

[ - ]
Reply by Tim WescottApril 8, 2017
Isn't the magnitude of the sine-wave the G-load (ie no power scaling)?

Oi.  That seems like a simple question, doesn't it?

Possibly not in any sensible way, unless you're talking about discrete tones.

The g-loading requirements that I've seen are a composite of the magnitudes of tones at specific frequencies, plus an RMS level for the underlying "random" vibration, plus a requirement for a peak level (usually applied as a shock).

If you're talking about taking a spectrum, then you're usually talking RMS, because the amount of G-loading "power" that an assembly is absorbing matters in that case.  When I've seen this it is was specified as an operational parameter.

If you're talking about g-loads that might damage a unit then you're usually talking about the amplitude of a tone, or the peak amplitude of a shock.  The former can be easily derived from spectral data, but the latter is pretty much a time-domain thing.

If your customer is working off of some commercial or military standard it would probably be a very good idea to get your hands on the text of the standard and read it.  Specifying g loads is one of those things that sounds easy on the surface but can get very byzantine (and for good reason).  Moreover, I'm not an expert -- what I've just told you I'm sure about, but I've mostly learned it by peeking at tests over the shoulders of the real experts.

[ - ]
Reply by DNadlerApril 8, 2017

Got it. Here we're looking at vibration to detect mechanical wear (if assembly vibrates when moving, there's wear in the drive mechanism and/or support bearings and maintenance is required). Previous versions of this measurement system reported raw acceleration; this incarnation customer wants FFT included. Spec calls for acceleration (in G) over frequency bins, not power...

Thanks again for the help,
Best Regards, Dave

[ - ]
Reply by Tim WescottApril 8, 2017

First, if there's a formal, or even an ad-hoc industry standard, you want to follow it or know why you're deviating from it.

Second, a sample-by-sample FFT can be misleading when the noise is random.  You probably want to build up a power spectral density plot.  I've always managed to dodge learning the details of this, but it basically involves taking windowed FFTs of the source data, then averaging the spectral power over several intervals.  If the data is truly random and you don't do the averaging then the bin values jump around too much to make sense of the data.

Search on "power spectral density calculation" and you should find something helpful.  If you do it, you probably want to have a running estimation.