FM demodulation looks mysterious in GNU Radio because it’s one tidy block… but under the hood it’s actually very simple. FM is just information encoded as changes in instantaneous frequency. So if you can measure how fast the phase is changing, you’ve got your audio.
Let’s walk through how you’d build it yourself from primitives.
Goal: Recreate an FM demodulator block in GNU Radio
FM encodes information as changes in instantaneous frequency.
A complex baseband FM signal looks like:
x[n] = A * exp(j * phi[n])
The information is in how fast phi[n] changes.
Instantaneous frequency is proportional to:
phi[n] - phi[n-1]
So FM demodulation = measure change in phase between samples.
The Key Trick
Instead of computing phase directly (using atan2), we do this:
x[n] * conj(x[n-1])
Why?
Let:
x[n] = A * exp(j * phi[n])
x[n-1] = A * exp(j * phi[n-1])
Then:
x[n] * conj(x[n-1])
= A^2 * exp(j * (phi[n] - phi[n-1]))
The angle of that result is:
phi[n] - phi[n-1]
That is the FM signal.
So:
FM_output = arg( x[n] * conj(x[n-1]) )
How To Build This In GNU Radio
You can build it from basic blocks:
- Delay block Delay the signal by 1 sample
- Conjugate block Take complex conjugate of delayed signal
- Multiply block Multiply current sample by conjugated delayed sample
- Complex to Arg block Extract phase (atan2)
Output of that block is your demodulated FM.
That is essentially what the Quadrature Demod block does internally.
Even More Efficient Version (No atan)
You can avoid atan completely.
Let:
x[n] = I[n] + jQ[n]
x[n-1] = I[n-1] + jQ[n-1]
Then frequency output is proportional to:
I[n] * Q[n-1] - Q[n] * I[n-1]
This comes from the imaginary part of:
x[n] * conj(x[n-1])
In GNU Radio you would use:
- Two Delay blocks (one for I, one for Q)
- Multiply blocks
- Subtract block
That gives a signal proportional to frequency deviation, without computing atan.
Much faster. Often used in hardware.
After Demodulation
Normally you then:
- Low-pass filter (remove high frequency noise)
- Apply de-emphasis (for broadcast FM)
- Resample to audio rate if needed
Why This Works
FM stores information as rate of phase rotation.
Multiplying by the conjugate of the previous sample measures how much the complex vector rotated between samples.
It is literally measuring angular velocity.
Clean. Elegant. Very SDR.