This note provides a practical workflow for computing FFT frequency, magnitude, and phase vectors for real-valued signals, including index formulas for both even and odd nfft.
Problem setup
- A vector
xwith lengthL - A sample rate
Fs xis real-valued
Objectives
- FFT result with chosen
nfft - Unshifted FFT-bin frequency, magnitude, and phase vectors
- Single-sided frequency, magnitude, and phase vectors for even
nfft - Single-sided frequency, magnitude, and phase vectors for odd
nfft
Method
FFT with nfft
Choosing nfft > L zero-pads the signal in time domain and decreases frequency-bin spacing. This densifies the displayed spectrum but does not increase the true resolving power set by record length and windowing.
Y = fft(x, nfft);Unshifted FFT-bin spectrum (two-sided content)
Frequency
Use frequency resolution \(\Delta f = Fs / nfft\). This unshifted frequency axis runs from \(0\) to \(Fs-\Delta f\).
frequencyResolution = Fs/nfft;
frequencyVectorDoubleSided = (0:nfft-1)*frequencyResolution;Magnitude
magnitudeDoubleSided = abs(Y)./L;Phase
phase = angle(Y);Why some bins are doubled
Key idea: you double only bins that lost a distinct mirror partner.
For real-valued signals, FFT bins satisfy conjugate symmetry: \(X[k] = X^*[N-k]\). When forming a single-sided spectrum, negative-frequency bins are omitted. Any positive-frequency bin that had a distinct negative-frequency partner should be doubled to preserve one-sided amplitude.
- Never double DC (first bin), because it is self-paired.
- For even
nfft, never double Nyquist (Fs/2), because it is also self-paired. - Double only bins with a distinct partner on the discarded side.
Single-sided spectrum (even nfft)
Frequency
frequencyResolution = Fs/nfft;
frequencyVectorSingleSided_even = (0:nfft/2)*frequencyResolution;Magnitude
Apply the mirror-partner rule above: do not multiply DC or Nyquist. Scale only bins 2 through end-1.
magnitudeSingleSided_even = magnitudeDoubleSided(1:nfft/2+1);
magnitudeSingleSided_even(2:end-1) = 2*magnitudeSingleSided_even(2:end-1);Phase
phase_even = angle(Y(1:nfft/2+1));Single-sided spectrum (odd nfft)
Frequency
frequencyResolution = Fs/nfft;
frequencyVectorSingleSided_odd = (0:(nfft-1)/2)*frequencyResolution;Magnitude
Apply the mirror-partner rule above: do not multiply DC. For odd nfft there is no Nyquist bin, so scale bins 2 through end.
magnitudeSingleSided_odd = magnitudeDoubleSided(1:(nfft+1)/2);
magnitudeSingleSided_odd(2:end) = 2*magnitudeSingleSided_odd(2:end);Phase
phase_odd = angle(Y(1:(nfft+1)/2));Example
This example uses a signal with non-zero mean, one low-frequency sinusoid, and one high-frequency sinusoid.
clc; clear; close all;
Fs = 2001; % Hz
time = 0:1/Fs:2;
signalMean = 4;
signalLowFrequencyComponent = sin(2*pi*10*time + 45/180*pi);
signalHighFrequencyComponent = 0.1*cos(2*pi*94*time + pi*30/180);
signal = signalMean + signalLowFrequencyComponent + signalHighFrequencyComponent;
L = length(signal);Even nfft calculations
nfft_even = 16384;
Y_even = fft(signal, nfft_even);
magnitudeDoubleSided_even = abs(Y_even)./L;
magnitudeSingleSided_even = magnitudeDoubleSided_even(1:nfft_even/2+1);
magnitudeSingleSided_even(2:end-1) = 2*magnitudeSingleSided_even(2:end-1);
frequencyResolution_even = Fs/nfft_even;
frequencyVectorSingleSided_even = (0:nfft_even/2)*frequencyResolution_even;
phase_even = angle(Y_even(1:nfft_even/2+1));
figure
subplot(2,1,1)
semilogy(frequencyVectorSingleSided_even, magnitudeSingleSided_even)
xlabel('Frequency [Hz]')
ylabel('Magnitude [-]')
axis tight
subplot(2,1,2)
plot(frequencyVectorSingleSided_even, phase_even*180/pi)
xlabel('Frequency [Hz]')
ylabel('Phase [deg]')
axis tight
Odd nfft calculations
nfft_odd = nfft_even + 1;
Y_odd = fft(signal, nfft_odd);
magnitudeDoubleSided_odd = abs(Y_odd)./L;
magnitudeSingleSided_odd = magnitudeDoubleSided_odd(1:(nfft_odd+1)/2);
magnitudeSingleSided_odd(2:end) = 2*magnitudeSingleSided_odd(2:end);
frequencyResolution_odd = Fs/nfft_odd;
frequencyVectorSingleSided_odd = (0:(nfft_odd-1)/2)*frequencyResolution_odd;
phase_odd = angle(Y_odd(1:(nfft_odd+1)/2));
figure
subplot(2,1,1)
semilogy(frequencyVectorSingleSided_odd, magnitudeSingleSided_odd)
xlabel('Frequency [Hz]')
ylabel('Magnitude [-]')
axis tight
subplot(2,1,2)
plot(frequencyVectorSingleSided_odd, phase_odd*180/pi)
xlabel('Frequency [Hz]')
ylabel('Phase [deg]')
axis tight
Summary
Use this sequence each time:
- Choose
nfftand computeY = fft(x, nfft). - Build unshifted FFT-bin vectors from
Y. - Build single-sided vectors by selecting half-spectrum and applying correct scaling: for even
nfft, double bins2:end-1; for oddnfft, double bins2:end. - Use even/odd indexing formulas that match
nfftparity.