8
$\begingroup$

When creating the spectrum of a signal, I understand that applying a window function can help with spectral leakage to get a more accurate picture of the frequency distribution. But sometimes I've seen that we need to then divide by the average of the window we have applied, or in other cases it is the average of the squared window. This answer even suggests that it should be the squared sum of the window values. Which one is correct, and why do we do this?

# some signal x
x <- runif(512)
window <- signal::hanning(length(x))
sig_wind <- x * window
# why this step?? or should it be mean(window^2) ? or sum(window^2)??
sig_wind <- sig_wind / mean(window)
spec <- 20*log10(abs(fft(sig_wind)))
$\endgroup$
2
  • $\begingroup$ I would consider this question as a duplicate (following the reference to SigProc SE and reference there, gives an exhaustive answer. $\endgroup$
    – WMXZ
    Commented Sep 30, 2022 at 15:49
  • 4
    $\begingroup$ I read through the reference, and it later lists the sum of squared window values as the appropriate denominator, as opposed to the squared sum of windows that the SE answer states. I still don't understand which one is appropriate for what use case. I'm hoping that an answer to this question here will provide an easily searchable answer. This seems like it should be a straight forward calculation that is used by almost everyone in our field, but the details are almost always glossed over. $\endgroup$ Commented Oct 5, 2022 at 18:52

2 Answers 2

3
$\begingroup$

This is answered in this brilliant report (https://holometer.fnal.gov/GH_FFT.pdf from p. 21), but in short you can use windows in two ways:

  1. You aim to process the whole signal in one lump. In this case you apply the window, e.g. a Von Hann (aka Hanning) window of the same length as the signal to the whole signal. You have now reduced the amplitude of much of the signal (for a Hann window, I think this averages to 0.5). Thus to get the correct amplitude for you spectrum, you need to compensate for having halved the average amplitude of you signal. This means dividing each sample by the mean of the window amplitude. Note that this only works for continuous noise, where the SPL can be assumed to be more or less constant throughout.
  2. You aim to process your signal piece by piece (it might be too long to do in one FFT or you want to see the spectral changes over time). This time you'll chose a sensible bin length for the FFT (based on your required frequency/time resolution, see section 12, p. 21 in above reference). For each of these bins you'll apply a window. You now have a choice, have bins meet end-to-end, e.g. bin one goes from sample 0 to 99, and bin two from sample 100 to 199. You apply your chosen window to each bin before the FFT. However, if you do this, your window will have attenuated half your signal, and you've lost a lot of information. The common solution is to have overlapping windows/bins, so that bin 1 goes from e.g. sample 0 to 99, and bin 2 from sample 50 to 149 (50 % overlap, gives full amplitude conservation for Hann window, again see linked source, table 2, column "ROV[%]", p. 29). In this case (as opposed to before) the overlapping windows compensate for the loss in amplitude, and you only have a tiny, un-compensated loss at either end of your signal.

To square or not to square: In the question you link to they are using squared values, you are likely not doing that, you'll presumably be working in linear units (maybe Pa?) and thus your window will be in linear units too, no squaring...

Edit 2022/10/18 See p. 54 of reference, Amplitude Flatness (AF) is 50 % overlap, but Power Flatness (PF) is ~2/3 overlap for a Hann window. I suspect squaring your values (and window) will mimic this, but I haven't tested this.

$\endgroup$
1
  • $\begingroup$ points 1 and 2 describe the difference between spectrum and spectrogram and the issue of overlap that for a spectrogram has to be considered, but this, IMHO, was not the question in OP $\endgroup$
    – WMXZ
    Commented Oct 7, 2022 at 5:40
1
$\begingroup$

To answer this question, I did some tests in Matlab. This shows that the answer depends on

  • implementation of FFT
  • type of signal

a) FFT implementation: there is no unique FFT implementation. The only rule is that IFFT(FFT(x)) = x. for this somewhere a division by the length of the FFT (nfft) must be included. Very often this scaling is used asymmetrically with the inverse FFT (IFFT). But symmetrical use (scaling FFT and IFFT by 1/sqrt(nfft)) can be found. This must be checked for every implementation.

b) the following Matlab code considers 3 signals

  • transient
  • tonal
  • random noise

using the Matlab implementation of the FFT I get the following results

  • transient: peak spectral level is obtained with FFT without window and scaling
  • tonal: peak spectral level is obtained by scaling with 2/sum(window)
  • noise: RMS (std) spectral level is obtained by scaling with 2/sqrt(sum(window.^2))

Noise (or any random signal) is treated differently as only RMS is a sensible measure but not peak level as with tonals.

 % amplitude for signal or std for noise
 aa=100;
 
 % window size and fft length 
 nw=2048; % result does not change when set to, say 512
 nfft=max(2048,nw);
 
 % generate impulse with amplitude aa at some location 
 xx=zeros(nw,1);
 xx(11)=aa;
 
 % generate sinusoidal signal with constant amplitude aa
 yy=aa*sin(2*pi*16*(0:(nw-1))'/nw);  
 % generate window 
 ww=hann(nw);
 y1=yy.*ww; 
 y2=2*y1/sum(ww);
 
 % generate random noise (normalize to std=1) 
 uu=randn(nw,1); 
 zz=aa*uu/std(uu);  
 z1=zz.*ww; 
 z2=2*z1/sqrt(sum(ww.^2));
 
 [aa,max(abs(fft(xx))), max(abs(fft(y2,nfft))), std(abs(fft(z2,nfft)))]

resulting to

 100.0000  100.0000  100.0000   90.3902

Note the spectral noise level itself is a random variable scattering around the expected (true) value.

Edit: Note FFT length (per se) is not relevant, what is important is the window length (any zero padding does not add to the FFT result apart from interpolating the frequency bins). In case of a rectangular window, the scale factor is equal to the window length. This can be seen changing the nw parameter to say 1024 and keeping the nfft at 2048.

The actual FFT length and therefore the spectral bin-width is, however, important if the result (of noise) is presented as power spectral density (PSD) (e.g. in Pa^2/Hz), and the bin-width is rescaled to 1 Hz, but this is another question.

$\endgroup$
5
  • $\begingroup$ Are you not forgetting to scale your results by the FFT length? $\endgroup$
    – Rasmus
    Commented Oct 6, 2022 at 10:07
  • $\begingroup$ I believe the FFT length is sort of implicitly integrated here because sum(window) is used instead of mean(window) and sum(window) = n * mean(window) $\endgroup$ Commented Oct 7, 2022 at 0:15
  • $\begingroup$ Okay, so in the RMS example we use sum(ww^2) because the quantity we are interested in (RMS) deals with the squared inputs, is that correct? So if I am going to create a power spectral density as 20*log10, then I should also be normalizing by sum(ww^2) because I am interested in a squared quantity $\endgroup$ Commented Oct 7, 2022 at 0:51
  • $\begingroup$ Some additional info one could look into for the harmonic components (when your signal is dominated by, well separated, harmonics) is the FlatTop window function (en.wikipedia.org/wiki/List_of_window_functions#Flat_top_window) and the scalloping loss (recordingblogs.com/wiki/scalloping-loss). $\endgroup$
    – ZaellixA
    Commented Oct 9, 2022 at 10:59
  • $\begingroup$ Rasmus's link provides some more, quite nice, information on this too! $\endgroup$
    – ZaellixA
    Commented Oct 9, 2022 at 11:01

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.