"""Compare exact finite-window coefficients and DTFTs (Python 3.14). Uses the repository's NumPy/Matplotlib dependencies. Writes an opaque PNG to cwd so Make can generate it directly in the mirrored media directory. """ import numpy as np import matplotlib.pyplot as plt N = 64 n = np.arange(N) rectangular = np.ones(N) hann = 0.5 - 0.5 * np.cos(2 * np.pi * n / N) bin_offset = np.linspace(0, 8, 6401) phasors = np.exp(-2j * np.pi * np.outer(bin_offset / N, n)) fig, axes = plt.subplots(1, 2, figsize=(9, 3.8), layout="constrained") fig.patch.set_facecolor("white") colors = ["#1565a8", "#ce6519"] for window, label, color in zip( [rectangular, hann], ["Rectangular", "Periodic Hann"], colors ): axes[0].plot(n, window, color=color, lw=2, label=label) response = np.abs(phasors @ window) / np.sum(window) db = 20 * np.log10(np.maximum(response, 1e-5)) axes[1].plot(bin_offset, db, color=color, lw=1.6, label=label) axes[0].set(title="Time weights: 64 samples", xlabel="Sample index n", ylabel="Window coefficient") axes[0].set_xlim(0, N - 1) axes[0].set_ylim(-0.04, 1.08) axes[0].legend(loc="lower center", framealpha=1) axes[1].set(title="Peak-normalized spectral response", xlabel="Frequency offset (DFT bins)", ylabel="Magnitude (dB)") axes[1].set_xlim(0, 8) axes[1].set_ylim(-85, 3) axes[1].set_xticks(range(9)) axes[1].axvline(1, color=colors[0], ls=":", lw=1, alpha=0.8) axes[1].axvline(2, color=colors[1], ls=":", lw=1, alpha=0.8) axes[1].legend(loc="upper right", framealpha=1) for ax in axes: ax.set_facecolor("white") ax.grid(alpha=0.2) ax.tick_params(labelsize=9) fig.suptitle("Rectangular versus Hann: sidelobe suppression widens the main lobe", fontsize=12) fig.savefig("rectangular-hann-window-comparison.png", dpi=120, facecolor="white", transparent=False) plt.close(fig)