FFT Example — Multi-Tone Sinusoidal Signal¶
This example builds a two-tone signal
\(x(t) = \sin(2\pi \cdot 5 \cdot t) + 0.5 \cdot \sin(2\pi \cdot 12 \cdot t)\)
with N = 256 samples and computes its one-sided FFT using
transforms.fft_real_forward(), then detects the dominant peak frequencies.
Example Code¶
"""IMSL FFT example: compute FFT of a multi-tone sinusoidal signal.
Signal: x(t) = sin(2π*5*t) + 0.5*sin(2π*12*t), N=256, dt=1/256
Outputs:
- Table printed to stdout showing peak frequencies detected
- SVG plot saved to test_output/demo_imsl_fft.svg
"""
from __future__ import annotations
from pathlib import Path
from typing import Dict
import matplotlib.pyplot as plt
import numpy as np
from transforms import fft_real_forward
def run_demo_imsl_fft() -> Dict[str, object]:
"""Run IMSL FFT example: FFT of a multi-tone sinusoidal signal.
Computes the one-sided FFT of x(t) = sin(2π·5·t) + 0.5·sin(2π·12·t)
sampled at N=256 points with dt=1/256 s.
Args:
None
Returns:
Dict[str, object]: Result dict with keys ``peak_freqs`` (list of
float), ``magnitudes`` (list of float), and ``plot_path`` (str).
"""
N = 256
dt = 1.0 / N
t = np.arange(N) * dt
x = np.sin(2 * np.pi * 5 * t) + 0.5 * np.sin(2 * np.pi * 12 * t)
X = fft_real_forward(x)
freqs = np.fft.rfftfreq(N, d=dt)
magnitude = np.abs(X)
# Find top-3 peaks
sorted_idx = np.argsort(magnitude)[::-1]
top_idx = sorted_idx[:3]
top_freqs = freqs[top_idx].tolist()
top_mags = magnitude[top_idx].tolist()
print("\nIMSL FFT Example: x(t) = sin(2π·5·t) + 0.5·sin(2π·12·t), N=256")
print("-" * 55)
print(f"{'Rank':<6} {'Frequency (Hz)':>16} {'Magnitude':>12}")
print("-" * 55)
for i, (f, m) in enumerate(zip(top_freqs, top_mags), 1):
print(f"{i:<6} {f:>16.2f} {m:>12.4f}")
print("-" * 55)
output_dir = Path("test_output")
output_dir.mkdir(parents=True, exist_ok=True)
plot_path = output_dir / "demo_imsl_fft.svg"
fig, axes = plt.subplots(2, 1, figsize=(10, 7))
axes[0].plot(t, x, color="#6d28d9", linewidth=1.5)
axes[0].set_xlabel("Time (s)")
axes[0].set_ylabel("Amplitude")
axes[0].set_title("Time-Domain Signal: sin(2π·5·t) + 0.5·sin(2π·12·t)")
axes[0].grid(True, alpha=0.3)
axes[1].plot(freqs, magnitude, color="#7c3aed", linewidth=1.5)
axes[1].axvline(5, color="#dc2626", linestyle="--", linewidth=1.2, label="f=5 Hz")
axes[1].axvline(12, color="#d97706", linestyle="--", linewidth=1.2, label="f=12 Hz")
axes[1].set_xlabel("Frequency (Hz)")
axes[1].set_ylabel("|X(f)|")
axes[1].set_title("Frequency Spectrum (One-sided)")
axes[1].set_xlim(0, 40)
axes[1].legend()
axes[1].grid(True, alpha=0.3)
fig.tight_layout()
fig.savefig(plot_path, format="svg")
plt.close(fig)
return {"peak_freqs": top_freqs, "magnitudes": top_mags, "plot_path": str(plot_path)}
if __name__ == "__main__":
run_demo_imsl_fft()
Plot Output¶
Top: time-domain signal showing two superimposed sinusoids. Bottom: one-sided magnitude spectrum with peaks at 5 Hz and 12 Hz.¶
Console Output¶
IMSL FFT Example: x(t) = sin(2π·5·t) + 0.5·sin(2π·12·t), N=256
-------------------------------------------------------
Rank Frequency (Hz) Magnitude
-------------------------------------------------------
1 5.00 128.0000
2 12.00 64.0000
3 89.00 0.0000
-------------------------------------------------------