torstai 16. toukokuuta 2019

Stereo FM receiving with RTL-SDR and Gnuradio - part 1

Receiving FM radio broadcasts is a sufficiently easy entry to software defined radio (e.g. rtl-sdr). The stations are abundant, it is easy to see if everything works properly by listening to the signal and the basic receiver is sufficiently simple. Multiple tutorials exist that show how to do the receiver software, and almost all use Gnuradio companion (GRC). GRC is a software that allows creating a software defined radio by taking different blocks (like demodulators and mixers) from the software's library and wiring them together to produce a complete receiver.

Mono receiver

As shown in the above examples, receiving the basic (mono) FM broadcast is very straightforward. The radio receiver is first tuned to correct center frequency with high enough sampling rate, e.g. 2 Mbps. The signal is then low-pass filtered at around 80...100 kHz so that only the relevant part is left. After filtering, the signal can be FM-demodulated. As described e.g. in Wikipedia, the FM deviation is 75 kHz.

After demodulating the signal, it needs to be low-pass filtered to discard everything but the audio. The bandwidth is 15 kHz, so that is a suitable filter corner frequency. Then the signal just needs to be resampled to e.g. 48 kHz and played out from speakers. Here is my take on the mono FM receiver in GRC. The .grc file can be found from my GitHub.


Mono FM receiver


As can be seen, the receiver is very simple. The FM demodulator block already has a low-pass filter which I use. I also added a slider to select the radio frequency, time sink to show the audio waveform and frequency sink to show the spectrum. The window produced by GRC when running the graph looks like following:
GRC window for FM mono receiver

What about stereo?

So, if receiving mono radio was that simple, receiving stereo cannot be much harder, right? Well actually there is quite some more details that needs to be handled. I didn't really find any good examples on how to do it so I had to study the FM specification as well as GRC to get it done.

The complete spectrum of the demodulated FM radio broadcast looks like following (simplified, generated by my python script which I'll explain later):
Demodulated FM broadcast spectrum

In the spectrum I've marked four regions:
  1. Red: 30 Hz ... 15 kHz is the mono signal. A stereo signal is converted to mono by summing both channels, i.e. mono = left + right
  2. Green: 19 kHz pilot signal. This indicates a stereo broadcast and is important for stereo receiving for other reasons too.
  3. Yellow: 23 ... 38 ... 53 kHz "Stereo" signal. The difference of left and right channels (left-right) is sent here. The difference signal is amplitude modulated to a 38 kHz carrier.
  4. Orange: 55 ... 57 ... 59 kHz Radio Data System (RDS) signal is sent around 57 kHz, taking something like 4 kHz band. We might return to this in later parts.
 What does it mean that the "stereo" signal is AM modulated to 38 kHz carrier, is explained by the modulation equation below. The carrier is just multiplied with the signal and added to the mono audio.
Stereo FM modulation equation (from Wikipedia)


The 38 kHz "stereo" carrier is second multiple of the 19 kHz pilot tone. The 38 kHz is not transmitted at all, so it needs to be recovered with the help of the pilot tone. It is very important that the phase is correct, otherwise the stereo signal is deteriorated and in bad cases reduced back to mono (+-90 degrees phase shift) or reversed (180 degree phase shift).

After the 38 kHz carrier is regenerated, the demodulation can be done simply by multiplying the signal with the carrier, and applying a low pass filter again.

Basic concept for stereo receiving

I came up with following procedure to receive the stereo FM broadcast:
  1. FM demodulate the signal, without low-pass filtering (raw signal)
  2. From the raw signal, low pass filter with 15 kHz filter the mono (L+R) signal
  3. From the raw signal, bandpass filter the 19 kHz pilot tone
  4. Use PLL to recover a clean, phase correct pilot with amplitude of 1.0
  5. Square the PLL output and bandpass filter it at 38 kHz to get the second harmonic
  6. Multiply the raw signal with the now regenerated carrier and low pass filter with 15 kHz filter ("stereo" signal)
  7. Add the mono and "stereo" signals together to reconstruct the left channel
  8. Subtrack the mono and "stereo" signals to reconstruct the right channel
  9. Resample properly to get a 48 kHz audio signal

Python helper

Since it is necessary to recreate the pilot tone and the 38 kHz carrier with correct phase, I wrote a python script that creates a baseband signal (what the FM demodulator would output) as a wav file. Then in GRC I can replace the FM demodulator block with a waveform source and use that to develop the receiver. Since the output of the right side resampled went to multiple blocks, I decided to just resample the wav file to higher sample rate for simplicity.

Rtl-sdr and FM demodulator are disabled and replaced with waveform source

The benefit of this approach is that I know exactly what kind of signals to expect and what is the phase shift between the clocks and the different signals is, and I can easily check whether everything works correctly, e.g. if any filters add phase shift or if some gains are wrong.

In the following part I will show comparison of the output of the python script and what is the output of the GRC receiver.

Note about the de-emphasis

After I got the receiver to work, I had some issues with the stereo signal gain. I had to boost the difference channel gain to something like 30 to get good stereo separation even though there should be no need for additional gain. Turns out the reason was de-emphasis.

FM radio broadcast uses something called pre-emphasis to boost high frequency signals to increase the signal-to-noise ratio. The receiver then applies a de-emphasis, low-pass filter, to restore the original signal. The FM demodulator block in GRC has a "tau" to set the time constant of the de-emphasis filter.

However, only after noticing a detail in figure 3 in this pdf file,  I figured out what was going on.
Figure 3 from the pdf file, emphasis mine


In the figure the HPF (high pass filter) denotes the pre-emphasis filter. I noticed that the filter is applied to tboth audio channels separately! This means the when receiving the broadcast, the FM demodulator must NOT do the de-emphasis. Instead the receiver must first separate both channels and only then apply the filter. After I did this change, the stereo separation became as expected without additional gain.

After this I also added the pre-emphasis to my python script.

In the second part I will explain how I made the stereo receiver.

Ei kommentteja:

Lähetä kommentti