From I/Q to spectrum: the FFT

The raw I/Q stream is unreadable as-is — a tide of numbers. To see which frequencies are present, we apply the FFT (Fast Fourier Transform): an algorithm that decomposes a block of samples into its frequency components.

Input: a block of N complex samples. Output: N bins, each representing a small slice of frequency and the power found there.

Resolution: the central trade-off

A bin's width = sample_rate / N. This is the formula to remember:

Sample rate N (FFT size) Bin width You can tell apart…
2 MSps 2048 ~1 kHz two walkie-talkies 12.5 kHz apart
8 MSps 2048 ~4 kHz two neighbouring FM stations
20 MSps 4096 ~5 kHz WiFi channels inside 2.4 GHz
2 MSps 65536 ~30 Hz the fine lines of Morse or FT8

The bigger N, the finer the resolution — but each frame needs more samples and more computation: you trade reactivity for detail. A yardstick: at 2 MSps with N = 65536, each frame "consumes" 33 ms of signal; shorter bursts get diluted in it.

The refinements that change everything

Each bin's power is then converted to dB. The result is the curve you see: horizontal axis = frequency, vertical axis = power. A signal = a bump above the noise floor.

From spectrum to detector

This is exactly the heart of the site's engine (Rust on the server, TypeScript in your browser): Hann window → FFT → averaging → dB → floor estimation → anything above floor + threshold becomes a peak with its frequency, width and SNR. It's also what spots a drone: a 10 MHz-wide "peak" looks like nothing else.

A single FFT = one photo. Stack them over time and you get the Le waterfall (cascade temporelle).

Your turn

  1. 8 MSps, N = 2048: bin width? (~3.9 kHz.)
  2. You're hunting a 50 ms LoRa burst. Giant N or max-hold averaging? (Max-hold — a giant N would dilute the burst.)
  3. Why does the Hann window make the floor "cleaner"? (It removes the spectral leakage from block edges, which otherwise drowns neighbouring bins.)