Window gain factor in FFT

I have some vibration data (acceleration) on whcih I need to perform an FFT, and then integrate it and again do an FFT.
I read for the nature of the data that I have, the input to the FFT must be sent throgh the Hann window (or Hanning) in order to avoid spectral leakage in the frequency domain representation after I perform the FFT. I further read that the amplitude of the FFT output vector must be corrected, becasue of the window function's 'window gain factor'.
The said window gain factor for the Hann window is about 0.5 and that means I must multiply the real part of each entry in the output array with 2 to effect this correction. My expectation was - If I DO NOT perform the window gain correction, the amplitudes on the Y axis of my FFTs will be about half the magnitude as that of the rectangular window, and therefore the correction is called for.
However, when I use 3 different window functions - Rectangular, Hanning and Flat-Top - WITHOUT performing any corrections for the gain factor in any case, I still get the similar aplitudes on the Y axis of my FFT. (Refer pictures below - in each case the first graph is the FFT result of acceleration and the second graph is for speed).
A. Rectangular Windowing:
B. Hann Windowing:
C. Flat-top Windowing:
Why does this happen?
If I do perform the window gain correction, for example, in the case where I use the Hann window, then the amplitudes must be roughly double as compared to when I use a Rectangular window. How come that the amplitudes on the FFT results should be so wildly different if I use different window functions and do the correction correctly?
Should I ultimately do the correction for the window gain?

9 Comments

You didn't show us the code, but I find the expected result with the example code from fft doc without the random noise(*) with and without windowing --
Fs = 1000; % Sampling frequency
T = 1/Fs; % Sampling period
L = 1500; % Length of signal
t = (0:L-1)*T; % Time vector
S = 0.7*sin(2*pi*50*t) + sin(2*pi*120*t);
X=S.';
Y = fft(X);
P2 = abs(Y/L);
P1 = P2(1:L/2+1);
P1(2:end-1) = 2*P1(2:end-1);
f = Fs*(0:(L/2))/L;
subplot(3,1,1);
plot(f,P1,'k-','linewidth',2)
grid on
xlim([0 250]), ylim([0 1])
xlabel('Frequency'),ylabel('Amplitude')
title('No Windowing')
X=X.*hann(length(X),"periodic");
Y = fft(X);
P2 = abs(Y/L);
P1 = P2(1:L/2+1);
P1(2:end-1) = 2*P1(2:end-1);
subplot(3,1,2);
plot(f,P1,'k-','linewidth',2)
grid on
xlim([0 250]), ylim([0 1])
xlabel('Frequency'),ylabel('Amplitude')
title('Hanning Window')
HannMean=mean(hann(length(X),"periodic"));
P1= P1/HannMean;
subplot(3,1,3);
plot(f,P1,'k-','linewidth',2)
xlim([0 250]), ylim([0 1])
grid on
xlabel('Frequency'),ylabel('Amplitude')
title('Hanning Window Normalized')
Would have to see just what you actually did to compensate but
HannMean
HannMean = 0.5000
illustrates that the average window value is 0.5 as expected.
(*) Removing the random noise won't change the demonstration, just makes the output peak values identically equal to the input time series amplitudes for easy visualization. The random noise will cause small changes from being identically 0.7 and 1.0, but the result will still scale by exactly the factor of 2.
Thank you @dpb for your generous help.
Sorry I hadn't posted my code, because it is rather similar to what is on the help page for the fft() function.
I picked up on the idea of analysing the simple sine equation in my code and I was able to replicate the resutls that you obtained. Which showed that my code was working OK.
I further used your code with the data I am working with, and ended up getting the same results as I had posted in the question. That is, the amplitude on the rectangular and hann window FFTs (without correcting for window gain) were still both similar.
This leads me to doubt that this behavior is perhaps due to the nature of my data. We are reaching out to some people who have worked on data that is very similar to ours, and also have done similar analysis. Hopefully we get to learn more about this.
As a side note :
When I first plugged the simple sinusoidal wave S into my code, I had a very different output plot as compared to yours, which threw me off for a while. I realised that my code has a zero-padding step for the input singal to the next power of two (in this case, 2048). Of course, this step led the amplitudes on the FFTs to get reduced as comapred to when a non-padded signal was processed. However, there is not a very significant magnitude change on FFT when I use a zero-padded signal as compared to a non-padded signal. This maybe because I have a much longer signal (about half a million long).
"... has a zero-padding step for the input singal to the next power of two (in this case, 2048). Of course, this step led the amplitudes on the FFTs to get reduced as comapred to when a non-padded signal was processed."
For proper energy conservation and normalization, if zero padding do NOT normalize by the full padded length but by the original sample length; there is no energy in the padding zeros so adding them into the divisor reduces the apparent signal strength by that ratio of L/N. This difference, of course, will be reduced in fractional size as the length of the sampled signal goes up.
The magnitude of the factor owing to the windowing is independent of the the data itself; as shown in the above example, both amplitudes are affected by the same magnitude indepedent of frequency.
The above is true for a stationary signal, the basic assumption underlying using the baseband FFT. Windowing is applied in the time domain; ergo, if the signal varies significantly over the sample window, the amplitude of the input (and hence the amplitude of the output containing the frequences in the portion of the sample window reduced) will be affected if the frequency content isn't the same across the time window as well.
Save one of your input traces as a .mat file and attach it...and the code that produced the above results; something's amiss it would seem; the length normalization would account for at least some of it.
Fs = 1000; % Sampling frequency
T = 1/Fs; % Sampling period
L = 1500; % Length of signal
t = (0:L-1)*T; % Time vector
%S = 0.7*sin(2*pi*50*t) + sin(2*pi*120*t);
S=detrend(rand(size(t))); % use white noise to spread energy across spectrum
X=S.';
Y = fft(X);
P2 = abs(Y/L);
P1 = P2(1:L/2+1);
P1(2:end-1) = 2*P1(2:end-1);
f = Fs*(0:(L/2))/L;
subplot(3,1,1);
plot(f,P1,'k-','linewidth',2)
grid on
xlim([0 250]), ylim([0 0.05])
xlabel('Frequency'),ylabel('Amplitude')
title('No Windowing')
X=X.*hann(length(X),"periodic");
Y = fft(X);
P2 = abs(Y/L);
P1 = P2(1:L/2+1);
P1(2:end-1) = 2*P1(2:end-1);
subplot(3,1,2);
plot(f,P1,'k-','linewidth',2)
grid on
xlim([0 250]), ylim([0 0.05])
xlabel('Frequency'),ylabel('Amplitude')
title('Hanning Window')
HannMean=mean(hann(length(X),"periodic"));
P1= P1/HannMean;
subplot(3,1,3);
plot(f,P1,'k-','linewidth',2)
xlim([0 250]), ylim([0 0.05])
grid on
xlabel('Frequency'),ylabel('Amplitude')
title('Hanning Window Normalized')
Illustrates the effect is independent of frequency; but does show the broadening effect of the sidelobes in the window on amplitudes across the broad band for signals not precisely at a frequency bin midpoint.
The other thing that may be going on in your instance is that you're analyzing a transient signal -- in that case, applying any symmetric window such as Hanning, Hamming, etc., will greatly influence the overall magnitude and provide wrong results. NEVER apply such windowing to transient data. If this is the case, then you'll want to do "overlap" processing or analyze the whole transient without windowing -- the point of Hanning/Hamming/similar windowing is to remove the spurious transients that will be introduced analyzing a stationary time signal that is not sample such that an integral number of samples are taken. It (the window) artificially forces the time signal being analyzed to (or near) zero at both beginning and end to remove that discontinuity. That's not the situation with transient signals.
If you're measuring signals that also contain random noise besides any deterministic content, then you'll really need to average to reduce those effects; I posted an example of that recently as well...
It took a bunch of searching to remember a key word to find it, but the demo of averaging white noise repeated here is
a=2;
b=4;
t=1:1000; %time (10X the length of original -- dpb)
x=a*cos(2*pi*t/10); %signal without noise
xn=x+sqrt(b)*randn(size(x)); %signal with noise added
xn=reshape(xn,100,[]); % N traces of original length -- dpb
Xn=abs(fft(xn)); %abs(FFT) of xn
f=0:.01:.5; %vector of frequencies, up to Nyquist
%plot the results
subplot(211); plot(t,xn(:),'-rx');
xlabel('Time'); title('Time Domain');
subplot(212); plot(f,Xn(1:51,:)); % show the individual PSDs -- DPB
hold on
plot(f,mean(Xn(1:51,:),2),'k-','linewidth',2); % and the result of averaging
xlabel('Frequency'); title('Frequency Domain');
Fs = 1000; % Sampling frequency
T = 1/Fs; % Sampling period
L = 1500; % Length of signal
t = (0:L-1)*T; % Time vector
N=2^nextpow2(L); % FFT length
S = 0.7*sin(2*pi*50*t) + sin(2*pi*120*t);
X=S.';
Y = fft(X,N);
P2 = abs(Y/L);
P1 = P2(1:N/2+1);
P1(2:end-1) = 2*P1(2:end-1);
f = Fs*(0:(N/2))/N;
hTL=tiledlayout(3,1);
hTL.Title.String="Padded FFT Normalization Using Signal Length, NOT Padded Length";
hTL.Title.FontSize=10.5; hTL.Title.FontWeight='bold';
nexttile
plot(f,P1,'k-','linewidth',2)
grid on
xlim([0 250]), ylim([0 1])
xlabel('Frequency'),ylabel('Amplitude')
title('No Windowing')
X=X.*hann(length(X),"periodic");
Y = fft(X,N);
P2 = abs(Y/L);
P1 = P2(1:N/2+1);
P1(2:end-1) = 2*P1(2:end-1);
nexttile
plot(f,P1,'k-','linewidth',2)
grid on
xlim([0 250]), ylim([0 1])
xlabel('Frequency'),ylabel('Amplitude')
title('Hanning Window')
HannMean=mean(hann(length(X),"periodic"));
P1= P1/HannMean;
nexttile
plot(f,P1,'k-','linewidth',2)
xlim([0 250]), ylim([0 1])
grid on
xlabel('Frequency'),ylabel('Amplitude')
title('Hanning Window Normalized')
Illustrates get the correct amplitude when normalize the FFT by only the actual signal length containing energy, NOT by the overall length of the zero-padded signal to increase FFT resolution.
Thank you once again for your time and insightful responses.
  1. You're right about the tip regarding normalizing the magnitudes by a factor corresponding to the orignal signal length and not the length of the padded signal. I'm embarassed I missed it, considering I did compute the 2-sided amplitude first, correctly by dividing the abs value of FFT output with the length of the original signal and not that of the padded signal. I made this correction right away.
  2. I have realised one of the reasons that I am getting the output that I originally showed was the nature of my input signal which is transient in nature. The input signal is in fact only about 300s long, clipped out from a very long recording. The clipping I did in such a way that the input signal to FFT has a lot of near-zero values at the edges, which builds up momentarily in the 'middle' - around about 150s. On simply varying how many entries I include around the 150s mark, I clearly started seeing a different trend in the amplitude of the FFTs done using a Hanning v/s Rectangular window. So, turns out I should probably NOT use the Hann window after all.
  3. Attached is the input trace, if you still would like to take a look. Also given below is the code which was generating the results (note that I retained the mistake of normalising the magnitude with the wrong size intentionally).
function [signal_out] = do_FFT_hannWindow (time_stamps, signal_in)
T = time_stamps(end)-time_stamps(1); % Duration of Sampling in s
L = size(signal_in,1); % Signal Length
Fs = L/T; % Sampling Frequency = Bidirectional Bandwidth
% Sizing and padding Operations
fft_in = signal_in;
N = 2^(nextpow2(L)); % Ideal size for FFT
if L < N
for i=1:(N-L)
fft_in(end+1)=0; % Padding with 0's
end
end
%N = L;
f = Fs/N:Fs/N:Fs/2; % Frequency Array
w = hann(N,"periodic"); % Hann Window function
%w = rectwin(N);
%w = flattopwin(N); % Flat Top Window function
fft_in = fft_in.*w;
fft_out = fft(fft_in,N);
amp_spec = abs(fft_out)/L; % 2-sided Amplitude spectrum
amp = 2*amp_spec(2:N/2+1); % Single sided amplitude spectrum
% 1st entry of amplitude spectrum is the average value of the signal (DC)
% and is therefore not valid for frequncy domain plotting
amp = amp/mean(w); % WIndow gain correction
signal_out = [f' amp];
But probably what would be more interesting for you would be to take a look at the output now when I'm making the necessary corrections and also accounting correctly for the window gain, done on the entire signal and then the truncated signal:
If it is not too much to ask, I now have a follow-up question. How do I interpret the Y-axis values in my FFT result? My aim is to compare the 'contribution' or 'effect' of the most significant frequency (in this case ~32 Hz.) of this sensor with that of another sensor where a different frequency is the most significant. I started off with the idea that the Y-axis amplitudes in m/s^2 and m/s as seen above, can be comapred across different sensors vibrating as a result of different incident frequencies. But clearly, this comparison is not correct when the amplitude result on FFT varies for differing signal length, for the same sensor. Could you possibly point me in a direction where I can find some means of such a comparison.
Thank you again for your help!
I think it would take having an intimate knowledge of the whole experiment/project to be able to comment intelligently on the analysis at any depth beyond just the basic things we've discussed that are purely mathematical properties of the computations undertaken.
From that viewpoint of a complete outsider looking in, I can think of only a couple general principles/techniques that I think would be paramount -- the first would be to use comparative samples in time of the two sensors; there may be, of course, a phase difference between the two owing to the comparative distance from the excitation source (but then again, of course, I have no idea what that may have been).
Secondly, if possible, still averaging of multiple instances may be the best noise reduction/"real" amplitude technique there is; particularly if this is an operating mechanism with a constant drive speed/load, etc,. ...
Alternatively, one may want to consider spectrographic methods with transient data to observe the time-varying aspects instead of treating the signal as the whole.
So much wisdom in these comments, thank you very much @dpb.
BTW, one more thing just came to mind -- if, indeed, it is rotating machinery or some other sort of device with a repetitive nature; you may do well to consider triggered input on the input shaft or somesuch thing to begin the acquisition of each transient at the same point in the cycle.

Sign in to comment.

Answers (0)

Products

Release

R2020b

Commented:

dpb
on 12 Oct 2022

Community Treasure Hunt

Find the treasures in MATLAB Central and discover how the community can help you!

Start Hunting!