Analytic Signal & Hilbert Transform Demo

Hilbert analytic signal demo

Generate an AM, two-tone or chirp signal (optionally with noise), build its analytic signal z[n] = x[n] + j·x̂[n] with the FFT method (zero the negative frequencies, double the positive ones), and read off the envelope, the unwrapped instantaneous phase and the instantaneous frequency. Sampling rate is fixed at 1000 Hz.

Loading…
AM and chirp are well-behaved (narrowband). The two-tone beat has a perfectly good analytic signal, but its envelope and frequency only make sense as a pair of beating components.
Centre frequency (Nyquist is 500 Hz). Bedrosian needs the envelope bandwidth to stay below fₜ; push fₜ toward 30 Hz with a deep AM to see the envelope start to break.
Modulation index of the 8 Hz message. m = 1 drives the envelope to zero.
Additive white Gaussian noise relative to the clean signal power. 60 dB = noise off.
Record length. Short records mean fewer cycles and bigger end effects.
Hilbert energy check Σ x̂² / Σ x² —
Orthogonality Σ x·x̂ / √(Σx²·Σx̂²) —
Mean envelope (central 90%) · measured (ideal) —
Envelope RMS error vs ideal —
Mean inst. frequency (central 90%) · measured (ideal) —

Analytic signal components, unwrapped phase and instantaneous frequency

Spectrum in dB (0 dB = peak of |X|): original two-sided |X| and one-sided analytic |Z|. Z has no negative-frequency content and is 6 dB higher where it survives.

Envelope detector: Signal = Tone AM, fₜ = 120 Hz, m = 0.6, SNR = 60 dB. The green envelope follows the slow 8 Hz message and the frequency panel is a flat line at 120 Hz.

Noise and phase: Lower SNR to 10 dB. The envelope barely moves but the instantaneous frequency becomes ragged — differentiating phase amplifies noise.

Chirp: Signal = Linear chirp. The envelope is flat at 1 and the frequency panel is a straight ramp; the unwrapped phase is a parabola.

Break Bedrosian: Signal = Tone AM, fₜ = 30 Hz, m = 1. The sidebands now come close to DC, and the end-of-record error grows.

Full write-up: Analytic signal, Hilbert transform and FFT. Next lessons: envelope & instantaneous frequency and phase demodulation.

Controls explained

Signal type
Tone AM — carrier fₜ with an 8 Hz sinusoidal message: x = (1 + m cos 2π·8t) cos 2πfₜt. Ideal envelope is 1 + m cos 2π·8t and ideal frequency is flat at fₜ. Two-tone — carrier plus a second tone 12 Hz higher with relative amplitude m; the envelope beats at 12 Hz and the instantaneous frequency swings about the stronger tone. Chirp — a linear sweep centred on fₜ (span 20 + 160·m Hz); unit envelope, linearly rising frequency.
Carrier fₜ
Centre frequency in Hz. A tone that is not an integer number of cycles in the record leaks across FFT bins, which is what drives the end-of-record errors in the envelope and frequency. Raise fₜ to separate the signal from DC; the hard limit is Nyquist at 500 Hz.
AM depth m / tone ratio / sweep
Reused control. In Tone AM it is the modulation index (m = 0 is a pure carrier, m = 1 touches zero envelope). In Two-tone it is the amplitude of the second tone relative to the first. In Chirp it sets the sweep span, 20 Hz at m = 0 up to 180 Hz at m = 1.
SNR
Signal-to-noise ratio of the real input in dB, from 0 to 60. The noise is white Gaussian with a fixed seed so shared links reproduce exactly (use “New noise” to draw another realization). At 60 dB the noise is switched off entirely.
FFT length N
Number of samples in the record and the size of the FFT that builds the analytic signal. The frequency resolution is 1000/N Hz. The analytic signal is formed by zeroing bins N/2+1 … N−1, doubling bins 1 … N/2−1 and leaving DC and N/2 untouched, then inverse FFT. Metrics and plots ignore the first and last 5% of the record where wrap-around effects live.
Hilbert energy check
Ratio of the energy in the imaginary part to the energy in the input. It is 1 for any signal with no DC and no Nyquist content, because the Hilbert transform only rotates phases by ±90°. A value below 1 means DC or Nyquist energy was thrown away, since the Hilbert transform of a constant is zero.
Orthogonality
Normalized inner product of x with its Hilbert transform. For an analytic signal built from the FFT it is zero to machine precision, no matter what the signal is.
Mean envelope and RMS error
Average of |z[n]| over the central 90% of the record, with the ideal value in parentheses, and the RMS difference between measured and ideal envelope. The ideal is computed from the noise-free closed-form analytic signal. The RMS error rises with noise, with spectral leakage and when Bedrosian’s condition fails.
Mean instantaneous frequency
Average of (fs/2π)·dφ/dn after phase unwrapping and a 5-point moving average, again on the central 90% and shown against the ideal. It is exact for a clean AM tone and noisy at low SNR. The frequency panel clamps outliers to the panel edge.

How to use this demo

Pick a waveform, set the carrier and depth, then watch what the FFT-based Hilbert transform does. The top panel shows the real part (your input), the imaginary part (its Hilbert transform, a 90° phase-shifted copy) and the envelope |z|. The middle panel shows the unwrapped phase; the bottom panel shows its derivative, the instantaneous frequency. The spectrum panel makes the construction visible: negative frequencies are gone, positive ones are 6 dB higher.

Quick checks: clean AM tone (flat 120 Hz frequency, envelope tracks the message), same signal at 10 dB SNR (envelope survives, frequency is ragged), linear chirp (frequency ramp), and two-tone beat (a single “frequency” that is really two components).

Read next

FAQ

Is the imaginary part the Hilbert transform or the analytic signal? Both, depending on how you slice it. The analytic signal is z = x + j·x̂ where x̂ is the Hilbert transform of x. The orange curve is x̂, the whole complex trace (blue plus orange) is z, and its magnitude is the green envelope. MATLAB’s hilbert and SciPy’s scipy.signal.hilbert both return z, not x̂.

Why are the ends of the envelope wrong? The FFT assumes the record repeats forever. When the carrier does not complete a whole number of cycles in N samples, the wrap-around creates a discontinuity, the “spectrum” leaks, and the Hilbert transform rings near both ends. Discard a few percent at each end, window, or pad before trusting those samples.

Why is the energy ratio sometimes not exactly 1? The Hilbert transform removes DC and Nyquist content (its frequency response is zero there). For any signal without DC or energy at fs/2 the ratio is 1 to numerical precision. Add an offset to the signal in your own code and watch it drop.

Why is the two-tone “instantaneous frequency” so strange? Instantaneous frequency has a single-valued meaning only for a mono-component signal. A sum of two tones is a bi-component signal, so the derivative of its phase swings above and below both tones, and for m close to 1 it can even leave the interval between them. That is a property of the signal, not a bug.

Does this match MATLAB or SciPy? Yes. The weights 1, 2, …, 2, 1, 0, …, 0 (for even N) are exactly what hilbert uses. Generate the same signal and compare with np.allclose(z, scipy.signal.hilbert(x)).