FFT Demo — Fast Fourier Transform
The calx FFT module supports arbitrary-length transforms via mixed-radix (radix-2, 3, 5) + Bluestein, with automatic acceleration through SIMD (AVX2/SSE). This page introduces the basic operations through four demos. All output is computed in real time by calx. The source code is provided at the bottom of the page.
Demo 1: Complex FFT (forward and inverse transform)
Applies the FFT to four complex data points and verifies that the inverse transform recovers the original.
FFT<C>::fft operates in-place, and FFT<C>::ifft includes $1/N$ normalization.
Demo 2: Real FFT and spectral analysis
Transforms the 8-point real signal $x = [1, 0, -1, 0, 1, 0, -1, 0]$ (a cosine wave of period 4)
using RealFFT and retrieves the amplitude spectrum.
The output is compressed to $N/2+1 = 5$ bins by exploiting conjugate symmetry.
Demo 3: Convolution (FIR filter)
Computes the convolution of two real arrays efficiently using RealFFT::convolve.
This is equivalent to applying a FIR filter by convolving a signal with an impulse response.
The output size is $|a| + |b| - 1$.
Verification: $(1 + 2x + 3x^2)(4 + 5x) = 4 + 13x + 22x^2 + 15x^3$.
Convolution and polynomial multiplication are the same operation;
FFT::convolve is for complex coefficients, while RealFFT::convolve is optimized for float / double waveforms.
Demo 4: Phase spectrum and unwrapping
Retrieves the spectral phase with phase_spectrum,
then removes $2\pi$ jumps with unwrap_phase to obtain a continuous phase.
This is a necessary preprocessing step for computing group delay $-d\phi/d\omega$ and visualizing phase responses.
phase_spectrum returns the principal value $(-\pi, \pi]$ of std::arg(),
causing the phase to jump at $\pm\pi$. Applying unwrap_phase yields
a continuous phase response, from which group delay can be computed by differentiation.
Source code and how to run
example_fft_demo.cpp (complete source code)
// example_fft_demo.cpp — FFT demo
#include <math/fft/fft.hpp>
#include <math/fft/fft_utils.hpp>
#include <iostream>
#include <iomanip>
using namespace calx;
int main() {
using C = std::complex<double>;
// --- Demo 1: Complex FFT ---
std::cout << "--- Complex FFT ---\n";
std::vector<C> data = {1, 2, 3, 4};
auto orig = data;
FFT<C>::fft(data);
std::cout << "FFT: ";
for (auto& v : data) std::cout << v << " ";
std::cout << "\n";
FFT<C>::ifft(data);
std::cout << "IFFT: ";
for (auto& v : data) std::cout << v << " ";
std::cout << "\n";
// --- Demo 2: Real FFT ---
std::cout << "\n--- Real FFT ---\n";
std::vector<double> signal = {1, 0, -1, 0, 1, 0, -1, 0};
auto spec = RealFFT<double>::fft(signal);
auto amp = amplitude_spectrum<double>(spec);
for (size_t i = 0; i < spec.size(); ++i)
std::cout << "X[" << i << "] = " << spec[i]
<< " |X| = " << amp[i] << "\n";
// --- Demo 3: Convolution ---
std::cout << "\n--- Convolution ---\n";
std::vector<double> a = {1, 2, 3}, b = {4, 5};
auto c = RealFFT<double>::convolve(a, b);
std::cout << "convolve = ";
for (auto v : c) std::cout << v << " ";
std::cout << "\n";
// --- Demo 4: Phase unwrap ---
std::cout << "\n--- Phase unwrap ---\n";
std::vector<double> sig2 = {1, 0.5, 0, -0.5, -1};
auto spec2 = RealFFT<double>::fft(sig2);
auto phase = phase_spectrum<double>(spec2);
auto uphase = unwrap_phase<double>(phase);
for (size_t i = 0; i < phase.size(); ++i)
std::cout << std::fixed << std::setprecision(3)
<< " [" << i << "] " << phase[i]
<< " -> " << uphase[i] << "\n";
return 0;
}
For API details, see the FFT API reference.
Build and run
cd calx
mkdir build && cd build
cmake .. -G "Visual Studio 17 2022" -A x64
cmake --build . --config Release --target example-fft-demo
examples\Release\example-fft-demo.exe