Normalisation of FFT not consistent.

26 views (last 30 days)
Hi, I am having a few issues normalising my frequency and amplitude for a test FFT I am trying to code. I think it boils down to my understanding of the relationship between signal length, sampling frequency etc.
Below is an example of my code for one set of configuration parameters. We are looking to convert the frequency space results into the physical space. Hence I want to recover a 20Hz frequency and an amplitude of 3 from a FFT.
clc; clear all; close all;
%%Creating test data to ensure FFT is working correctly
Ls = 10; % Length of signal
ds = 10000 % discrete data points
var = linspace(0,Ls,ds);
% Signal Frequencies and amplitudes
kx = 20; ax = 3;
% Signal creation
D = ax*cos(2*pi*kx*var);
%%FFT Stage
Fs = 1000; % Sampling Frequency
n = 2^nextpow2(Fs);
Y = fft(D,n);
% Normalising Y amplitude by number of sampling points
Y = abs(Y/n);
%%Plotting
% Creating vector f that transforms FFT frequency bins to frequency space
% defined by var.
f = linspace(0,1,length(Y))*Fs;
plot(f,Y)
xlabel('Frequency')
ylabel('Amplitude')
This produces the plot:
Which is giving me the correct frequency but not the right amplitude (I am not concerned with the one sided spectrum at the moment).
If I increase my sampling rate by an order of magnitude to Fs = 10000, my frequency changes and my amplitude is still not correct.
I have tried a few permutations of scaling behaviour, but cannot isolate the exact problem. What is the relationship between my signal length (Ls), the resolution of the data over that length (ds), and the sampling frequency used for FFT of that data (Fs)?
Also, how do I best go about increasing the accuracy of FFT so that my frequency is as close and sharp at 20Hz with magnitude exactly 3?
Please help me signal processing gurus! I am eager to learn!

Accepted Answer

Star Strider
Star Strider on 27 Nov 2017
Three problems:
n = 2^nextpow2(ds); % ← Use ‘ds’ Not ‘Fs’
Y = fft(D,n)/ds; % ← Divide by ‘ds’ Here
Y = abs(Y); % ← Do Not Divide By ‘n’ Here
You have to divide by the length of the original signal, because that is where all the energy is. There is no additional energy in the zero-padded signal.
That should solve the amplitude problem. Note that the signal energy is divided into two equal peaks in the two-sided Fourier transform, so the amplitudes will be half the original signal amplitude. In the one-sided Fourier transform, you need to multiply the amplitude by 2.
  13 Comments
David Goodmanson
David Goodmanson on 29 Nov 2017
I agree that with real data you can usually get away with a less exacting approach as in [4], not that that is something to be proud of. But your original point [3] specified some very precise conditions and here is some demo code for that. I used sine instead of cosine because the flaws are easier to see.
By definition, every fft is periodic in whatever you put into its input array. Whether it's periodic in what you want is another question. You can see what the periodicity looks like by concatenating a few arrays side by side.
Here is your coding method for a smaller size array, concatenated. For the plot, don't worry about the x axis scaling, just the shape. At 50 and 100 you see jogs. That's because with linspace, sin = 0 at both ends of the array, so the zero is repeated side by side. You are not periodic in a true sine wave, and that messes up the acuity of the fft.
N = 50; T = 1; f0 = 2; % T is time duration
t = linspace(0,T,N);
y = sin(2*pi*f0*t);
figure(1)
plot([y y y])
The idea is, at the end of the array stop one point short of sin equaling 0, so that you get a true repeating sine wave per the next plot. If you fft this you will get two points with spikes, zero everywhere else.
N = 50; T = 1; f0 = 2; % T is time duration
t = ((0:N-1)/N)*T;
y = sin(2*pi*f0*t);
figure(2)
plot([y y y])
Here is a reconstruction of what nextpow2 does to the function. If using linspace causes some problems, here the periodicity of the sine is totally whacked.
N = 50; T = 1; f0 = 2; % T is time duration
t = ((0:N-1)/N)*T;
y = sin(2*pi*f0*t);
ypad = zeros(1,64);
ypad(1:50) = y;
figure(3)
plot([ypad ypad ypad])
Your present modified code stops short of peak amplitude 1.5 and has broadened out peaks and not spikes. If you make appropriate changes and exile nexpow2 to the outer darkness you can get two spikes at f = +-20, height = 1.5, all with excellent accuracy.
olski one
olski one on 29 Nov 2017
I really cannot thank you enough for the input David. Your examples make your points very clear. These are all things that are not that easy to pick up in the literature.
I feel like I am much better equipped to begin my journey into Fourier space and beyond!
Are there any textbooks you would recommend for clarity and comprehension?

Sign in to comment.

More Answers (1)

David Goodmanson
David Goodmanson on 2 Dec 2017
Good question. I didn't take a course in DSP, so I don't have a textbook recommendation from experience. Rodriguez mentions the problem about the one extra array point at least twice (although not in terms of a linspace function) but he does not really emphasize it. The book has a lot of good information but it is 30 years old, kind of lab oriented, and pretty dense in the text. I wouldn't recommend rushing off and buying a copy. I have not looked at the Schaum's Outlines DFT book but Schaum's is usually pretty good (their complex variables and topology books are excellent), they have lots of solved problems and the price is right. I'm sure there must be a library you can go graze in, but if I had to buy a book sight unseen that would be it.

Community Treasure Hunt

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

Start Hunting!