Effect of zero padding on FFT amplitude
Show older comments
Many text books and other literature comment on the improvement in FFT resolution due to zero padding but I have not come across a text that comments on the effect of zero padding on FFT amplitude due to change in signal length (effectively the same energy is spread over longer time). In the real world, say for processing of vibration signal, engineers are interested in amplitude at a frequency.
Would zero padding not change original signal and thereby underestimate amplitudes of FFT?
Thanks
Rahul
10 Comments
Mathieu NOE
on 25 Jan 2023
hello
1 / i am also a noise & vibration engineer and I very rarely need to zero pad my data (I always take much longer records compared to what I really need in terms of resolution. I use the longer records to do linear averaging of fft to improve my SNR rather than the resolution )
2/ no there is no loss of amplitude by zero padding. yes you increase your fft resolution but you don't add any new information to the fft output.
some readings that may still interest you :
There is still one case where I find a practical use of zero padding, is when you do modal analysis on very lightly damped structures. the best test signal is random burst with enough zero padding to let the structure response decay to zero. You can do multiple averages but always let the response decay before next record (average).
Rahul Kale
on 25 Jan 2023
Star Strider
on 25 Jan 2023
Zero-padding using:
NFFT = 2^nextpow2(L)
where ‘L’ is the length (or in a matrix, row size) of the signal array has the added advantage (in addition to increasing the frequency resolution) of making the fft calculation much more efficient. This is due to the way the Fast Fourier Transform is calculated. I also usually window the fft. This corrects for the fft being finite rather than infinite, and (in my opinion) improves the result.
.
Bjorn Gustavsson
on 25 Jan 2023
@Star Strider: If I've understood things right the efficiency-gain with padding to the next power of 2 is less important with fftw - it is rather efficient as long as the size of the input has "few large prime-factors"?
Windowing is important, whether zero-padding or not. And to my understanding it is a bit imprecise to claim that no information is added by zero-padding - the discontinuity (in some derivative) at the transition between the and the zeros, does affect the transform.
Paul
on 25 Jan 2023
Can you clarify the concern about a "contradiction?" An actual example would be better.
The DFT output of the fft is always a set of frequency domain samples of the DTFT of the signal. The effect of zero-padding can only change where in the frequency domain the DTFT is sampled. So zero-padding can change how the DFT samples appear on a plot, but they will always be frequency-domain samples of the same DTFT.
Rahul Kale
on 26 Jan 2023
Signals go on forever, both into the past, and into the future. However, the DFT is only defined for signals x[n] with finite duration of length N, where the signal is 0 for n < 0 and the signal is 0 for n >= N. It's important to understand that this condition does not preclude a run of zeros for n <= N-1.
One way to interpret the DFT is that the results are always the Discrete Fourier Series coefficients of the periodic extension of x[n] with period Nfft >= N. If Nfft = N, then we are computing the DFT w/o zero-padding and if Nfft > N then we are using zero-padding.
Assuming for this discussion the we are limited to deterministic signals, we can use the DFT in one of two ways.
First, x[n] is one period of a periodic signal with period N, and it is this periodic signal we wish to characterize. In this case, we set Nfft = N and compute the DFT. We now have the DFS coefficients of the periodic signal that we actually care about. Suppose we strike the bell three for three beats, starting from n = 0, and then rest for three beats, and we do this for all time into the past and future. So one period of our striking sequence would look like x1 = [1 1 1 0 0 0], with N=6. Those last three zeros are not zero-padding. They are very important in defining one period of the signal. We can take the DFT of that sequence, without zero-padding, to obtain its DFS coefficients, from which we could compute x[n] for any value of n. These six DFS coefficients are all we need.
Second, x[n] is actually a finite duration signal. In this example, we don't strike the bell until n = 0, then srike it twice more, then stop. So x[n] looks something like this x[n] = [... 0 0 0 0 0 0 1 1 1 0 0 0 ...] where the ellipses indicate never ending and the first 1-entry correponds to n = 0. Now, this signal has frequency content at all frequencies, as could be seen by computing its DTFT. However, we want to use the DFT to compute samples of one period of the DTFT. The way we do that is to start with the sequence x2 =[1 1 1] but take Nfft large, i.e., lots of zero padding, so the DFT of x3 = [x2 zeros(1,Nfft-3)]. Now, when we take the DFT of x3, we are getting the DFS coefficients of the periodic extension of x3, with period Nfft, and these are still frequency domain samples of one period of the DTFT of x[n]. As Nfft >> N, then the periodic extension of x3 becomes closer and closer to actually being x[n] (imagine what happens as Nfft -> inf), and the DFT samples become more and more dense giving us a better picture of one period of the DTFT of x[n].
Rahul Kale
on 27 Jan 2023
Paul
on 28 Jan 2023
1. Yes, x1 in the first case are the first six elements in x3 in the second case. I found a typo in that coment and fixed it, it now says: "x3 = [x2 zeros(1,Nfft-3)]." Maybe that typo confused things. I don't understand the second question here that starts "Or, are you saying ...."
2. I've reread this question many times, and not sure I understand it. We've been saying and showing in this thread that zero-padding doesn't affect the amplitude spectrum of the signal. As I've said and MattJ showed, zero-padding changes the location of the frequency domain samples of the DTFT, but the amplitudes of those samples still follow the amplitude of the DTFT the finite duration signal.
3. Yes, the first six elements of x3 are the same as the elements of x1. As for scaling the results of the DFT, what is the definition of "amplitude of the physical quantity" in either the first or second case?
Rahul Kale
on 30 Jan 2023
Accepted Answer
More Answers (3)
Bruno Luong
on 29 Jan 2023
Edited: Bruno Luong
on 29 Jan 2023
If your (windowed) signal dies out to 0 in a smooth way then the padding with 0s on the tail changes little to the FFT spectrum (amplitude), it is equivalent to densify the spectrum (interpolation) without scaling it.
N=70;
x=(0:N-1)-(N-1)/2;
E=exp(-(x./(N/6)).^2);
y = E.*randn(size(x));
f = (0:N-1)/N;
yhat = fft(y);
figure
plot(f,y,'b',f,E,'c');
NPad = 2^nextpow2(N);
ypad=y; ypad(NPad)=0;
fpad = (0:NPad-1)/NPad;
ypadhat = fft(ypad);
figure
plot(f, abs(yhat), 'b-+', fpad, abs(ypadhat), 'r-.')
5 Comments
Rahul Kale
on 30 Jan 2023
"I did try to do resampling by interpolating to get NFFT = 2^nextpow2(L)"
Why you do that? I show zero-padding IS equivalent to interpolating in frequency doman IF you signal decays to 0.
Interpolation frequency does NOT introduce new frequency (how on earth linear interpolation can?), whereas zeros padding with non decay signal will do all sort of effect of the spectrum.
If you are windowing you signal correctly (as you claim) then your signal will decay to 0, then just 0-pad as in my code show, it is more or less interpolation in spectral domain: the amplitude is unchanged, the frequency has to be adjusted with the new array indices.
Afterall that is the main purpose of windowing when study signal in Fourier domain: avoid artefact.
The question of how to scale the FFT to get "physically correct" is a separate question, regardless if you zero pad or not the signal. It seems Matt has address the normalization for your goal (though I haven't read his answer carefully).
The one sided spectrum of y
% yhat = fft(y)*2*sqrt(dt/(N-1))
has unit force/sqrt(Hz) if you original signal is Force, and associated with the corresponding frequency vector f. If you want to get back to force unit you must integrate the yhat^2 on a certain non-zero interval (the width is ~ bandwidth) and then take a square-root of the integration. There are probaby a tone of discussions on this topic. The bandwidth is non-zeros since your signal is finite in length, even for perfect input sine wave, since it is truncated.
% sample frequency and period
dt=0.1;
fs=1/dt;
% signal length and time vector
N=4000;
t=dt*(0:N-1);
% generate tested signal mono frequency
T=10;
A=2023;
y=A*sin(2*pi/T*t);
% Fourier transform and one-sided spectrum
df=fs/(N-1);
yhat=fft(y)*2*sqrt(dt/(N-1));
yhat=abs(yhat(1:ceil((end+1)/2)));
nf = length(yhat);
iseven = mod(N+1,2);
if iseven
duplicatedside = union(1,nf);
else
duplicatedside = 1;
end
yhat(duplicatedside) = yhat(duplicatedside)/sqrt(2);
% associate frequency vector
f=df*(0:nf-1);
plot(f, yhat);
xlabel('f [Hz]')
ylabel('spectrum [V/sqrt(Hz)]')
xline(f(end),'-','Nyquist','LabelHorizontalAlignment','left')
% Retrieve amplitude
fres=1/T;
bw=0.2;
fint = fres+bw/2*[-1 1];
keep = f>=fint(1) & f<=fint(2);
Amplitude = sqrt(trapz(df,yhat(keep).^2))
xlim(fint)
The direct interpretation of the amplitude of the FFT is always problematic, only the energy and RMS value via L^2 integration in fourier domain via and parseval is mathematically correct.
Here is the full code that does windowing and then regroup both facts
- zeros padding => amplitude FFT does not change on windowing signal
- Correct scaling to get back amplittude after integration
%%
%clear
%close all
% sample frequency and sample period
dt=0.3;
fs=1/dt;
% signal length and time vector
N = 600;
t = dt*(-(N-1)/2:(N-1)/2);
% generate tested signal mono frequency of period T
T=10;
A=2023;
y=A*sin(2*pi/T*t);
% Windows function
Ewidth = t(end)/2;
E = exp(-(t/Ewidth).^2);
% Windowed signal to make them decay to 0 smoothly
ywin = y.*E;
figure
h = plot(t, [y(:) ywin(:) A*E(:)]);
xlabel('t [s]')
ylabel('signal [V]')
legend(h, 'original','windowed', 'window');
% Fourier transform of 0 padded signal and one-sided spectrum
Npad = 8*2^nextpow2(N); % 8 factor for fine resolution in Fourier domain
df = fs/Npad;
yhat = fft(ywin,Npad);
scalefft = 2*sqrt(dt/(N-1)); % N, not NPad
yhat = scalefft*abs(yhat(1:ceil((end+1)/2)));
nf = length(yhat);
iseven = mod(Npad+1,2); % EDIT fix a small glitch here
% The DC and Nyquist are common for both sides
if iseven
duplicatedside = union(1,nf);
else
duplicatedside = 1;
end
yhat(duplicatedside) = yhat(duplicatedside)/sqrt(2);
% associate frequency vector
f = df*(0:nf-1);
figure
plot(f, yhat);
xlabel('f [Hz]')
ylabel('spectrum [V/sqrt(Hz)]')
xline(f(end),'-','Nyquist','LabelHorizontalAlignment','left')
% Retrieve amplitude from Parseval
fres=1/T; % we can also detect the frequency of the peak of yhat, but we know it's 1/T
bw=sqrt(2)/Ewidth*sqrt(2);
fint = fres+bw/2*[-1 1];
keep = f>=fint(1) & f<=fint(2);
RMSE = sqrt(trapz(E.^2)/(N-1));
Amplitude = sqrt(trapz(df,yhat(keep).^2)) / RMSE % Estimate of A
% Zoom in the peak of the spectrum
xlim(fint)
Rahul Kale
on 31 Jan 2023
Bruno Luong
on 31 Jan 2023
Edited: Bruno Luong
on 31 Jan 2023
This has been somewhat explained in my various posts if you follow closely.
What happens here is the Parseval equality, or equivalent in discrete is the so called Rayleigh Energy Theorem and DFT that defined with the factor 1/sqrt(N) so make the transformation symetric in time and Fourier domain on the energy point of view.
Combine with the second fact that 0-padding does not change the amplitude of the FFT as I show in my answer, but just make the frequency vector with finer step (denser) on the same frequency domain [0,Niquist], there is no reason to adjust the amplitude with the length of the zero-pad signal.
This thread started with one question about zero-padding the Discrete Fourier Transform (DFT) and the answers and comments brought up many points on other related concepts including the Continuous Fourier Series (CFS), Continuous Time Fourier Transform (CTFT), windowing, and possibly many others. As might be expected for a thread with so many comments and additional questions in the comments, it might be hard to follow how all of the concepts relate and flow to each other. Consequently, I thought it might be good to show exactly that, at least for some of these of these concepts.
I'm posting this as its own answer because I wasn't sure it would flow nicely as a comment in any of the other answers.
Also, this answer does not add to any of the material already posted. Again, it's only trying to use Matlab to illustrate some of the points already made by (in no particluar order) @Mathieu NOE, @Star Strider, @Bjorn Gustavsson, @Matt J and @Bruno Luong and me.
Define a simple, continuous-time signal
syms t f real
Pi = sym(pi);
T1 = sym(2);
T2 = sym(3);
x(t) = 12*sin(2*Pi/T1*t) + 4*cos(2*Pi/T2*t);
x(t) is the sum of two periodic signals. Because the ratio T1/T2 is a rational number, we know that x(t) is periodic. By inspection, its fundamental period is
T = sym(6)
which we can verify by
simplify(x(t) - x(t+T))
Because x(t) is periodic, its CTFT is a train of scaled Dirac delta functions at the fundamental frequency (1/6 Hz) and its harmonics. In this case, we only get non-zero harmonics at f = +-1/3 and f = +-1/2
X(f) = expand(fourier(x(t),t,2*Pi*f))
Because x(t) is periodic, it has an exponential CFS represenation. The CFS coefficients as a function of n are computed by
syms n integer
C(n) = int(x(t)*exp(-1j*2*Pi*n/T*t),t,0,T)/T
% C(f) = fourier(x(t)*rectangularPulse(0,T,t),t,2*Pi*f)/T;
% C(n) = C(n/T);
Because the integrand in int has n as a parameter, it doesn't capture the special cases where abs(n) = 2 or abs(n) =3. Clean that up and C(n) is pretty simple
[~,denC] = numden(C(n));
npoles = solve(denC);
for npole = npoles(:).'
C(n) = piecewise(n == npole,limit(C(n),n,npole),C(n));
end
C(n) = simplify(C(n))
The CFS coefficients are exactly the cofficients in the impulse train that defines X(f), a fact that will be taken advantage of below.
Plot the amplitudes of the four CFS coefficients.
nval = -5:5;
figure
stem(nval/T,abs(C(nval)))
ylim([0 7])
xlabel('Hz')
Because we are using the exponential CFS, the amplitudes of C(n) and C(-n) are each 1/2 of the amplitude of the corresponding harmonic. Therefore, the plot tells us that the amplitude of the harmonic at 1/2 Hz is 12 and at 1/3 Hz is 4, which is exactly what we expect based on the definition of x(t).
Suppose we only have available to us a portion of x(t). The next step shows how Fourier analysis can still extract the amplitude information from the signal.
Define a window function that covers the portion of the signal that we have for processing. The triangular window function (for illustration) is parameterized by the number of periods of x(t) that it covers.
syms numperiod
w(t,numperiod) = triangularPulse(0,numperiod*T,t);
Suppose we have 10 periods of x(t)
nperiod = 10;
The CTFT of the window signal is
W(f) = simplify(fourier(w(t,nperiod),t,2*Pi*f));
Because w(t) covers a significant number of periods, its bandwidth in the frequency domain looks small relative to the frequency spacing between harmonics of x(t)
figure
fplot(abs(W(f)),[-0.3 0.3]);
xline( double( 1/2/T),'r', '1/(2T)');
xline( double(-1/2/T),'r','-1/(2T)');
xlabel('Hz')
ylim([0 31])
The peak of W(f) at f = 0 is
W0 = limit(W(f),f,0)
which is also area under the window.
int(w(t,nperiod),t,0,nperiod*T)
Apply the window to x(t) and compute the CTFT of the result
xw(t) = x(t)*w(t,nperiod);
XW(f) = fourier(xw(t),t,2*Pi*f);
Multiplication in the time domain corresponds to convolution in the frequency domain. Convolution of W(f) with the Dirac delta functions in X(f) correponds to shifting W(f) to the corresponding harmonics and multiplying by the corresponding Dirac coefficients. But those Dirac coefficients are just the CFS coefficients. Therefore, we can just as easily express the CTFT of xw(t) directly as
nval = [-2 2 -3 3];
XWalt(f) = sum(C(nval).*W(f-nval/T));
Show that XW(f) and XWalt(f) are equivalent.
figure
hold on
% fplot was taking too long.
%fplot(abs([XW(f) XWalt(f)]))
fplot(matlabFunction(abs(XW(f))),[-1 1]);
fplot(matlabFunction(abs(XWalt(f))),[-1 1]);
xlim([-1 1]*3/4)
xlabel('Hz')
Because the bandwidth of W(f) is small relative to the harmonic spacing, XW(f) is essentially composed of replicas of W(f) shifted to the harmonic frequencies and scaled by the CFS coefficients. This result suggests that we can recover the amplitudes of C(n) at the harmonic frequencies by simply dividing XW(f) by W0.
figure
fplot(abs(XW(f)/W0))
hold on
stem(nval/T,abs(C(nval)))
xlim([-1 1]*3/4)
ylim([0 7])
xlabel('Hz')
If all we have is the blue curve, we would surmise that the underlying periodic signal is composed of two sinusoids at 1/2 Hz and 1/3 Hz with amplitudes 12 and 4 respectively.
With that framework in continuous-time, now move to discrete-time.
Define functions to evaluate numerically (not stricly necessary, just runs a bit faster than evaluating in symbolic and converting)
xfunc = matlabFunction(x(t));
wfunc = matlabFunction(w(t,numperiod),'Vars',[t numperiod]);
Assume a sampling period of
Ts = 0.01;
The number of samples to cover that many periods is
N = nperiod*double(T)/Ts;
and the associated time samples are
tval = (0:(N-1))*Ts;
Evaluate the x(t) and w(t) at the sample times.
xval = xfunc(tval);
wval = wfunc(tval,nperiod);
Compute the DFT
XWdft= fftshift(fft(xval.*wval));
nfft = numel(XWdft);
fval = ceil(linspace(-nfft/2,nfft/2-1,nfft))/nfft/Ts;
Now, to plot the DFT with the appropriate scaling, we need to divide out the effect of the window function. In discrete-time, without any other scaling on the output of fft, that corresoponds to dividing by the sum of the window samples
figure
stem(fval,abs(XWdft)/sum(wval));
xlim([-0.75 0.75])
xlabel('Hz')
If we had used a rectangular window, then sum(wval) = N and we'd just divide by N.
Overlay with the scaled version of XW(f)
hold on
fplot(abs(XW(f)/W0),'g')
The scaled DFT samples are essentially frequency domain samples of the XW(f)/W0 (in general, this relationship is only approximate where the approximation gets better as Ts gets smaller and N gets larger).
Zoom in to see things a little better
copyobj(gca,figure);
xlim([0.2 0.6])
Because the DFT samples effectively recover XW(f)/W0, we again know the harmonic frequencies and the corresponding amplitudes.
Four DFT samples lay exactly on the harmonic frequencies because of how N was computed to cover exactly 10 periods for the given the value of Ts. In general, we cannot guarantee the data covers an integer number of periods; after all, the period is what we are trying to determine.
Also, the selection of Ts in this example is such that x[n] = x(n*Ts) is periodic in discrete-time.
Suppose we have bit more than 10 periods of samples.
nperiod = 10 + 0.25;
N = round(nperiod*double(T)/Ts);
Going through the same process
tval = (0:(N-1))*Ts;
xval = xfunc(tval);
wval = wfunc(tval,nperiod);
XWdft= fftshift(fft(xval.*wval));
nfft = numel(XWdft);
fval = ceil(linspace(-nfft/2,nfft/2-1,nfft))/nfft/Ts;
figure
stem(fval,abs(XWdft)/sum(wval));
xline(.5,'r')
xline(1/3,'r')
xlabel('Hz')
xlim([0.2 0.6])
ylim([0 7])
shows that the DFT samples are not so "nice," particularly around 1/3 Hz, which is basically halfway between two DFT samples.
Now, we finally get to the original question about zero padding, which we can use to fill in DFT samples in the frequency domain
N = round(nperiod*double(T)/Ts);
tval = (0:(N-1))*Ts;
xval = xfunc(tval);
wval = wfunc(tval,nperiod);
XWdft= fftshift(fft(xval.*wval,2^14)); % use a lot of padding
nfft = numel(XWdft);
fval = ceil(linspace(-nfft/2,nfft/2-1,nfft))/nfft/Ts;
figure
stem(fval,abs(XWdft)/sum(wval));
xline(.5,'r')
xline(1/3,'r')
xlabel('Hz')
xlim([0.2 0.6])
ylim([0 7])
As has been stated repeatedly, zero padding has no impact on the unscaled amplitude spectrum, and we recover the correct scaled amplitude spectrum as long as we divide by sum(wval), not the number of samples in DFT (2^14 in this instance). The zero padding recovers the information we seek, which is that the harmonics are somewhere very near 1/2 Hz and 1/3 Hz, with amplitudes nearly 12 and 4 respectively.
Although it looks like the DFT samples are samples of XW(f), that's not true because XW(f) is based on a 60 second window and the DFT sampes are based on a 60 + 6/4 = 61.5 second window, which has a slightly different CTFT. However, as long as the peak of CTFT of the window (whatever it may be) is at f = 0, the same process applies, i.e., using the peaks of XWdft(n)/sum(wval) to estimate the harmonic frequencies and amplitudes, as long as the DFT is normalized based on the actual window used, as done in this example.
Here is an example showing that the amplitude of an FFT does not change due to zero-padding. In all cases, the peak amplitude is 5.
x=[1,1,1,1 1];
N=(1:5)*5;
for n=N
plotFcn(x,n); %zero-pad x by 2*n and plot FFT
end
hold off; legend("n="+N)
function plotFcn(x,N)
z=zeros(1,N);
x=[z,x,z]; %zero pad
M=floor(numel(x)/2);
X=abs(fftshift(fft(x)));
plot((-M:M)/numel(X), X,'o'); hold on
end
29 Comments
Bjorn Gustavsson
on 26 Jan 2023
This is a wonderful, great and horibly uggly example of what happens with zero-padding!
The uggliness stands out ever more starkly if one labels the frequencies on the x-axis - DC-component has the right magnitude, and then there is a first harmonic component with close to the same amplitude...
Paul
on 26 Jan 2023
I can't tell if your comment is implying that zero-padding is good or bad (or perhaps something else?). The zero-padding is filling in the gaps in the frequency domain, as it should.
Bjorn Gustavsson
on 26 Jan 2023
@Paul: This is true. But Matt J's example also shows that without windowing the fft of a constant (all 1s in x!) signal suddendly also has almost as high amplitudes at the first 2 non-zero-frequency components, and then at all higher frequencies you have some power - corresponding to a sinc - due to the two steps (0 to 1 and 1 to 0) in x - no windowing corresponds to windowing with a top-hat filter with the length of the signal. Zero-padding can be used, but one should know what it means, and what it leads to, and it ought to be combined with windowing of the signal.
Rahul Kale
on 26 Jan 2023
Bjorn Gustavsson
on 26 Jan 2023
@Rahul Kale, yes the spectrum is different. But you also need to think about why and how it is different and what that means. In your "force on structure" scenario the x-only signal would correspond to a static force of 1. The zeropadded signal would correspond to a force that suddendly appears with a magnitude of 1 at "time" 5, then dissapears at time 9. Whe you look at the fouriertransform you have the spectrum of a signal that is periodic with period-time of 13 (or 14, I will count all the samples) and alternates between zero and one, and that is obviously different from a static force.
Paul
on 26 Jan 2023
" Zero-padding can be used, but one should know what it means" - Agree 100%
"and what it leads to," - Agree 100%
"and it ought to be combined with windowing of the signal." Depends on the problem to be solved.
Suppose the problem statment is: Use the DFT to get frequency domain samples of the DTFT of a finite-duration sequence. In this case, using anything other than a rectangular window would be inappropriate, IMO. Of course, there may very well be, or actually are, other problem statement for which windowing is appropriate. Depends on the problem to be solved and the assumptions of the underlying signal.
In fact the example given by Matt J essentially shows that zero padded spectrum is different. So if X axis were frequency then it is different than the original signal.
I have refined my example to better show that the underlying spectrum and its amplitude is not changing with zero padding. You're just seeing more samples of it.
Paul
on 26 Jan 2023
Excellent update. Except now there may be several comments under this answer that were referring to the original example and might not be as meaningful relative to the update.
Bjorn Gustavsson
on 27 Jan 2023
@Matt J: Unfortunately you also removed the fft of the orignal signal (x all ones) which only have a non-zero amplitude at DC - which it corresponds to a Dirac-spike at 0 as it should. When zero-padding such a signal one fundamentally change the signal from a constant-valued signal (that in the fft-sense also is periodic over the length of the signal) to a square-wave with the period of the zero-padded signal-length (which obviously have a sinc as its fft.)
@Paul, 1: yeah, my rant now definitely comes raging out of nowhere. 2: Yes given that problem-description a rectangular window is natural, but "on an exam" I'd hope the next question is something along the lines of "explain how the implicit periodic assumption of the fft leads to aliasing of high-frequency components due to end-effects, and how to control that problem with windowing".
...and if I think we might kind of argue not so much about how many angels should be on the head of this pin, but what colour their wings should be?
This might be a script that illustrates the windowing issues a bit:
t = linspace(0,5*pi,512);
f1 = fftfreq(mean(diff(t(1:end-1))),numel(t(1:end-1)));
f0 = fftfreq(mean(diff(t)),numel(t));
figure
subplot(4,2,1)
% Signal that's periodic and its spectral amplitudes
plot(t(1:end-1),sin((2)*t(1:end-1)))
subplot(4,2,2)
plot(f1,log10(abs(fftshift(fft(sin((2)*t(1:end-1)))))),'.-')
axis([-5 5 -16 1.2*log10(numel(t))])
subplot(4,2,3)
% Signal that's one sample off being periodic
plot(t(1:end),sin((2)*t(1:end)))
subplot(4,2,4)
plot(f0,log10(abs(fftshift(fft(sin(2*t(1:end)))))),'.-')
axis([-5 5 -5 1.2*log10(numel(t))])
subplot(4,2,5)
% signal a quarter period from being periodic
plot(t(1:end-1),sin((2.5)*t(1:end-1)))
subplot(4,2,6)
plot(f1,log10(abs(fftshift(fft(sin((2.5)*t(1:end-1)))))),'.-')
axis([-5 5 -5 1.2*log10(numel(t))])
w1 = tukeywin(numel(t(1:end-1)),0.75);
w2 = chebwin(numel(t(1:end-1)),50);
subplot(4,2,7)
% Windowed versions and their spectral amplitudes
plot(t(1:end-1),sin((2.5)*t(1:end-1)).*w1')
hold on
plot(t(1:end-1),sin((2.5)*t(1:end-1)).*w2')
subplot(4,2,8)
plot(f1,log10(abs(fftshift(fft(w1.'.*sin((2.5)*t(1:end-1)))))),'-')
hold on
plot(f1,log10(abs(fftshift(fft(w2.'.*sin((2.5)*t(1:end-1)))))),'-')
axis([-5 5 -5 1.2*log10(numel(t))])
function f = fftfreq(dt,n)
% FFTFREQ - fft-frequency-array
%
% Calling:
% f = fftfreq(dt,n)
% Input:
% dt - sampling duration (s), double scalar
% n - number of frequencies, scalar int
% Output:
% f - frequency array (Hz), dobule array
if rem(n,2) == 0
f = [0:n/2-1,-n/2:-1] / (dt*n); % if n is even
else
f = [0:(n-1)/2,-(n-1)/2:-1] / (dt*n); % if n is odd
end
end
Should produce a figure something like this:

When zero-padding such a signal one fundamentally change the signal from a constant-valued signal to a square-wave
@Bjorn Gustavsson I see your point, but I think it's a distinction without a difference. The Dirac spectrum that you get from a constant-valued signal can also be seen as a poorly sampled version of the square wave's spectrum. In fact, that's probably a better way to view it, because a proper Dirac spectrum, such as you would get from a true, constant-valued continuous signal would be infinite at DC whereas the spectrum of a discrete sequence of 1's is not..
Bjorn Gustavsson
on 27 Jan 2023
Moved: Matt J
on 27 Jan 2023
@Matt J, yeah, that is true, but in my defense seeing a fourier-transform of a constant-valued function with amplitudes for components at any frequency except at dc just make my brain hurt - and I know about some of the definitions of the Dirac, so that's not a problem.
Paul
on 27 Jan 2023
From my perspective, we are not necessarily taking the Fourier transform of a constant-valued function. Matt J has a finite sequence of data: [1, 1, 1, 1, 1]. Before doing any analysis I think about what I'm assuming about the rest of the signal that is unknown. If these five points are a subset of a constant signal, that's one thing. But if these five points are a subset of a rectangular pulse, then that's a different thing. There may be other things that these five points represent. Whatever that is, it guides me to how I would do the analysis, e.g., zero-padding or not, window or not, etc.
Rahul Kale
on 28 Jan 2023
Rahul Kale
on 28 Jan 2023
@Rahul Kale Paul has already explained to you that the FFT does not view x1 as a single period of a longer periodic signal. The FFT views x1 as a finite sequence. If your signal consists of multiple pulses, the pulses must be explicitly present in the input x vector if you want the FFT to be aware of them. In other words, if you want to find the spectrum of a 3 pulse signal with 50% duty cycle, you would do,
x=[1 1 1 0 0 0 1 1 1 0 0 0 1 1 1 0 0 0];
X=fft(x)
The spectrum X and its amplitude will indeed depend on the duty cycle, i.e., if I add or remove zeros between the pulses, the amplitude will change. However, increasing the number of zeros between the pulses is not the same thing as zero-padding. Zero-padding refers to adding initial and/or trailing zeros exclusively, and has no effect on the amplitude of the spectrum, as I demonstrated to you in my original answer above.
Rahul Kale
on 28 Jan 2023
Rahul Kale
on 28 Jan 2023
Matt J
on 28 Jan 2023
how does FFT know that in your signal x(with 50% duty cycle) the last three zeros are actually present in the signal and is not zero padding?
It doesn't need to know. You are free to view those zeros either as part of the signal or as padded zeros. The result is the same.
Below I have modified my original example to show the effect of varying the duty cycle of a series of dirac pulses on the amplitude spectrum. I was incorrect to say that the amplitude changes, even though as we see, the spectrum does change overall.
x=[1,1,1,1 1];
T=1:3;
for t=T
plotFcn(x,t); %insert t-1 zeros between the x(i) and plot spectrum
end
hold off; legend("duty cycle="+100./T+"%")
function plotFcn(x0,t)
x=zeros(1,t*numel(x0));
x(1:t:end)=x0;
N=numel(x)+1e4;
freqAxis=((0:N-1)-ceil((N-1)/2))/N;
X=fft(x,N);
plot(freqAxis, fftshift(abs(X)),'-'); hold on
xlabel 'Freq (Hz)'
ylabel 'Spectrum Amplitude'
end
To the contrary, I said quite the opposite: ".. the results [of the DFT] are always the Discrete Fourier Series coefficients of the periodic extension of x[n] ..." To be clearer, I should have said "DFS coefficients (to within a scale factor)"
We can see this in this example.
Let the signal x1[n] = [1 1 1 0 0 0] for n = 0:(N1-1), N1 = 6, and zero otherwise
Let the signal x2[n] = [1 1 1 0 0 0 1 1 1 0 0 0 1 1 1 0 0 0] for n = 0:(N2-1), N2 = 18, and zero otherwise.
Let x1p[n] be the periodic extension of x1[n] with period N1.
Let x2p[n] be the periodic extension of x2[n] with period N2.
It should be clear that x1p[n] == x2p[n]
Consequently, the DFTs of one period x1p[n] and x2p[n] should contain the same information
x1 = [1 1 1 0 0 0]; N1 = 6; % one period of x1
x2 = [x1 x1 x1]; N2 = 18;% one period of x2
X1 = fft(x1); f1 = (0:(N1-1))/N1;
X2 = fft(x2); f2 = (0:(N2-1))/N2;
figure
subplot(211);plot(f1,abs(X1),'-o',f2,abs(X2),'-x');
subplot(212);plot(f1,angle(X1),'-o',f2,angle(X2),'-x');
The ampltidues are identical to within a factor of N2/N1 = 3, and the phases are identical. The DFT of one period of x2[n] is the identical (to within that scale factor of 3) as DFT of one period of x1[n], because the periodic extensions of x1[n] and x2[n] are the same. By "identical" I mean wrt the four non-zero points that are encoding all of the information about the underlying periodic signals.
@Paul Very well. Still, another way to view the DFT is the Fourier transform of the continuous signal
formed from the sinc interpolation of the finite sequence
,

and then sampling
at frequencies f=k/N, k=-N/2,1,...,N/2-1.
The continuous signal
so defined is indeed not precisely finite, but it does decay to zero and is certainly not periodic. So, if the spectrum you are trying to compute is the continuous Fourier transform of a multi-pulse signal, the DFT will not be aware of the presence of multiple pulses unless they are explicitly sampled in
.
Rahul Kale
on 28 Jan 2023
Moved: Matt J
on 28 Jan 2023
Would it be possible for you to superimpose FFT of [x2 0 0 0 0 0 0] on the plots that you have given above?
I have already done that for you in my last set of plots, except that instead of adding 6 zeros, I have added 10000. These were the lines of code.
N=numel(x)+1e4;
freqAxis=((0:N-1)-ceil((N-1)/2))/N;
X=fft(x,N); %zero pads x to N=numel(x)+1e4
Sure, we can do that.
x1 = [1 1 1 0 0 0]; N1 = 6; % one period of x1
x2 = [x1 x1 x1]; N2 = 18;% one period of x2
x3 = [x2 zeros(1,6)]; N3 = N2 + 6; % requested by Rahul
X1 = fft(x1); f1 = (0:(N1-1))/N1;
X2 = fft(x2); f2 = (0:(N2-1))/N2;
X3 = fft(x3); f3 = (0:(N3-1))/N3;
Let's also compute the DTFT of x1[n] and x2[n]. The DTFT of x3[n] is exactly the same as that of x2[n], so we don't need to compute it.
[X1DTFT,f1dtft] = freqz(x1,1,2048,'whole',1);
[X2DTFT,f2dtft] = freqz(x2,1,2048,'whole',1);
First. plot the DFT's, and I'll muliplty the DFT of X1 by 3 to get everything on equal footing.
figure
hold on
plot(f1,abs(3*X1),'-o');
plot(f2,abs(X2),'-x');
plot(f3,abs(X3),'-*');
legend('X1*3','X2','X3')
We see now that the zero-padding on x3 has added more frequency domain samples, which is expected because N3 > N2. Why are those X3 frequency domain samples where they are? Well, they are the samples of the DTFT of x3, which as we stated above is identical to the DTFT of x2. Add the DTFTs of x1 and x2 to the plot.
figure
hold on
plot(f1,abs(3*X1),'-o');
plot(f2,abs(X2),'-x');
plot(f3,abs(X3),'-*');
plot(f1dtft,abs(X1DTFT)*3,f2dtft,abs(X2DTFT));
legend('X1*3','X2','X3','X1DTFT*3','X2DTFT')
Zoom in a bit for a better view
copyobj(gca,figure)
xlim([0 0.4])
legend('X1*3','X2','X3','X1DTFT*3','X2DTFT')
As must be the case, the samples of X1 lie on X1DTFT. Also, as must be the case, the samples of X2 and X3 lie on X2DTFT. As we can see, X2DTFT is sinc-y. The samples of X3 when viewed alone, as in the previous plot, might look kind of funky. But that's only because of where those X3 samples lie on X2DTFT (which is the same as X3DTFT if we were to compute it). If we add add more zero-padding to x3, the resulting X3 samples would look more and more sinc-y, just like the green curve.
Also, as is clear, 3*X1DTFT ~= X2DTFT. However, they are equal to each other at four frequencies, which are, not coincidentally, the frequencies of the non-zero samples of their respective DFTs.
Responding to this comment ....
Focusing on all but the last sentence, I agree. We can illustrate with a simple example.
syms t w f real
xn = 1:5; n = 0:4;
x(t) = sum(xn.*sinc(t-n));
figure
fplot(x(t),[-3 10])
hold on
stem(n,xn)
x[n] is a five-sample window of the infinite duration signal x(t),
Going to frequency domain
First the CTFT of x(t)
X(w) = simplify(fourier(x(t),t,w)); % CTFT
X(f) = X(2*sym(pi)*f);
Now the DTFT of x[n]
[Xdtft,fdtft] = freqz(xn,1,linspace(-1.5,1.5,500),1);
Now the DFT of x[n]
Xdft = fftshift(fft(xn));
fdft = (-2:2)/5;
Now plot. I'll lift up Xdtft a bit just so we can see what's going on underneath it
figure
hold on
fplot(abs(X(f)),[-1.5 1.5])
plot(fdtft,1.05*abs(Xdtft))
stem(fdft,abs(Xdft))
legend('CTFT','DTFT','DFT')
As expected, because of the specific definition of x(t), the DTFT of x[n] is an exact periodic extension of X(f).
So far, so good. But I don't see how the above relates to anything about inherit periodicity built into the DFT of a finite duration signal.
"So, if the spectrum you are trying to compute is the continuous Fourier transform of a multi-pulse signal, the DFT will not be aware of the presence of multiple pulses unless they are explicitly sampled in x[n]"
Please correct me if I'm wrong, but I think this statement is in regards to using sampling and the DFT to estimate the CTFT of a finite duration, multi-pulse signal (as opposed to an infinite-duration, periodic signal)
So something like this
x(t) = rectangularPulse(-0.5,2.5,t);
x(t) = x(t) + x(t-6) + x(t-12); % finite duration signal of three pulses
figure
hold on
fplot(x(t),[-1, 20]),grid
axis padded
% samples
xn = repmat([1 1 1 0 0 0],1,3); n = 0:17;
stem((0:17),xn);
CTFT of x(t)
X(f) = subs(simplify(fourier(x(t),t,w)),w,2*sym(pi)*f);
figure
fplot(abs(X(f)),[-0.75 0.75])
ylim([0 9])
If we want to get the best estimate of X(f), we'd use xn, which contains samples of all three pulses, and compute the DTFT
figure
hold on
fplot(abs(X(f)),[-0.75 0.75])
[Xdtft,fdtft] = freqz(xn,1,linspace(-0.5,0.5,1001),1);
plot(fdtft,abs(Xdtft))
The DTFT is a pretty good approximation to the central part of the CTFT, i.e., -0.5 <= f <= 0.5. The DTFT is actually the same as what we'd get with fft(xn,1001), i.e., a massively zero-padded DFT of xn, so we wont plot that here.
Now, we know from above that the DFT of xn are frequency domain samples of the DTFT. Add those to the plot
Xdft = fft(xn);
stem((-9:8)/18,abs(fftshift(Xdft)))
As shown, the DFT of xn are frequency domain samples of the DTFT.
But we can get the exact same (to within the factor of 3) DFT information using only the first pulse
Xdft1 = fft(xn(1:6));
plot((-3:2)/6,3*abs(fftshift(Xdft1)),'-x')
legend('CTFT','DTFT','DFT of three pulses','3*DFT of one pulse')
If the objective is to estimate the CTFT of x(t) via a DFT w/o zero-padding, the only additional information we get using all three pulses is the amplitude scaling of the samples at the four frequencies with non-zero amplitude. Does this result align with what you expected from "the DFT [not being aware] of the presence of multiple pulses"? Or should not including those two additional pulses have a different or an additional effect on how the DFT of a single pulse compares to the CTFT of x(t)?
@Paul I didn't follow all of that, but I can comment on this part:
Please correct me if I'm wrong, but I think this statement is in regards to using sampling and the DFT to estimate the CTFT of a finite duration, multi-pulse signal (as opposed to an infinite-duration, periodic signal)
Yes, because the CTFT of an infinite duration periodic signal is not physical. It will be the CTFT of a single pulse multiplied by a Dirac comb, which is basically zero or infinity at all frequencies. The OP's proposal here to analyze repeated bell pulses with the CTFT therefore only makes sense if there are a finite number of pulses.
Paul
on 28 Jan 2023
Another way to say this is that if c[k] are the exponential CFS coefficients of a periodic signal x(t) with period T, then the CTFT of that periodic signal is
X(w) = 2*pi*sum( c[k]*delta(w - k*w0) ) (w here in rad/sec, non-unitary transform)
where w0 = 2*pi/T and the summation is taken over all integers k.
It's been a good discussion, IMO.
Bjorn Gustavsson
on 30 Jan 2023
By taking the weekend off I missed a lot of discussion. Since this is already a rather sprawling stream of fourier-transform-information I will not be ably to catch up and will stand by and read, but first a last comment on @Paul's reply:
"From my perspective, we are not necessarily taking the Fourier transform of a constant-valued function. Matt J has a finite sequence of data: [1, 1, 1, 1, 1]. Before doing any analysis I think about what I'm assuming about the rest of the signal that is unknown. If these five points are a subset of a constant signal, that's one thing. But if these five points are a subset of a rectangular pulse, then that's a different thing. There may be other things that these five points represent. Whatever that is, it guides me to how I would do the analysis, e.g., zero-padding or not, window or not, etc."
That is a somewhat valid point-of-view, but from an Occhamian (Akaike/Bayesian model-selection) view it will be very hard to justifying additional fourier-components outside of the DC one, since that is enough to perfectly explain the available data, extending outside of that is very dodgy.
Categories
Find more on Spectral Measurements in Help Center and File Exchange
Community Treasure Hunt
Find the treasures in MATLAB Central and discover how the community can help you!
Start Hunting!

































