Random Quote Board

A Test of Different RBDS / RDS Demodulators Using Gnu Radio

Gary Schafer, January 2024

I've already created a post on demodulating RBDS signals. I've since decided to dive into it a bit, nay, a lot deeper. We're talking Marianas Trench of deep dives.

What is RDS / RBDS?

Quick overview: The RDS (European) / RBDS (American) signal is that 57 kHz subcarrier you find on FM broadcast stations. It's a digital signal modulated using binary phase shift keying (BPSK), carrying information such as the name of the station, title of songs, name of the singer, the date and time, and even advertisements. You can demodulate and decode that information, if you have the correct receiver. Gnu Radio gives you the ability to demodulate the bitstream, and then it gives you the ability to decode the bitstream to recover the original information if you have the gr-rds plugins for Gnu Radio. I was able to load the plug-in using the Linux Mint Software Manager. Otherwise, you might have to create the plugin from source.

Baseband spectrum of a FM broadcast station, specifically WETA (90.9 MHz) in Washington, DC. This is a typical station transmitting stereo audio. The spectrum from 0 - 15 kHz is both left (L) and right (R) channels combined. At 19 kHz, there is a pilot tone. This tone serves multiple purposes. First, its appearance tells your radio that it is receiving a stereo signal. Its second purpose is to demodulate the "L-R Audio", also called the "stereo signal", at 38 kHz (second harmonic). It may also be used to demodulate the "RBDS" signal at 57 kHz (third harmonic). The last signal is a "SCA" or "Subsidiary Communications Authority". It's purpose is typically to provide a secondary audio signal. (NOTE: This was copied from my August 2021 post on RBDS.)

How RDS Signals Are Made (Or Maybe Not)

You see, there's a Mommy RDS signal and a Daddy RDS signal... no... that's not how it works.

According to the NRSC-4 specification, the general flow is:

  1. The system creates the data stream. It consists of 16 bit information word, with each information word followed by a 10 bit checkword. (NOTE: I'm assuming this is a standard rectangular pulse bitstream.) The checkword also provides forward error correction (FEC). The combination of the information and check word is a 26-bit block. Four of these 26-bit blocks create one group. A group is 26 x 4 = 104 bits long. The bit rate is, by modern standards, slooooow. The bit rate is 57 kHz / 48 = 1187.5 bps. Over two and one-half hours of such data, information and checkword both, could fit onto an old 3.5" floppy disk.
  2. Basic bit structure of RDS signal. The first data block consists of 16 bits of the program identifier (PI). Every group starts with a program identifier, which is the same 16 bits repeated in every group. As stated in the specification, "1) The first block in every group always contains a Program Identification (PI) code."
    RDS bit stream showing program identifier (PI) code. The first bit of the PI code is also the beginning of the 104-bit group. GRC denotes it as the "Program Type", which I believe is incorrect. The only 16 bits that repeats for every group (104 bits) is the PI code. This solid block of the same bits can be used as a very basic bit error rate (BER) measurement.
  3. The bit stream is fed into a differential encoder. This encoder turns every digital 1 to a change from 1 to 0 or 0 to 1, while a 0 is no change.
  4. The differentially-encoded bit stream is turned into a series of impulses, essentially creating a return-to-zero (RZ) waveform.
  5. The waveform is Manchester encoded to get rid of any DC bias.
  6. The impulses are convolved with a filter impulse response that has a spectrum that is essentially a cosine waveform. This creates the final waveform that is modulated onto the 57 kHz carrier using AM with suppressed carrier. As stated in the specification, this is essentially a binary phase shift keyed (BPSK) signal.
RDS modulator diagram. Note the line going from the 19 kHz pilot tone to the 57 kHz oscillator used for the RBDS subcarrier. This may be how it is done, but not always. (Source: National Radio Systems Committee, NRSC-4-A, United Stated RBDS Standard, April, 2005.)

While it is not explicitly stated in the specification, the filter is a root raised cosine (RRC). Bastian Bloessl put a RRC filter in his RDS demodulator flowgraph on his Github page. I would like to thank Bastian for the "gr-rds" plugin that makes decoding RDS signals possible, as well as this flowgraph. His flowgraph, with its specific filtering, is the correct filter to use. It matches the RDS waveform perfectly.

Time domain view of the impulse response of the filter to be used in the RDS transmitter. (Image credit: National Radio Systems Committee, NRSC-4-A, United Stated RBDS Standard, April, 2005.)
Impulse response of RRC filter with Manchester-encoded impulses. This is the impulse response for a logic 1 that has been Manchester-encoded. The filter is the one provided by Bastian Boessl's flowgraph.
This display shows the differentially-encoded bitstream displayed as polar, return-to-zero (RZ) pulses (top). Using a delay-and-subtract circuit in Gnu Radio, each pulse edge is turned into Manchester-encoded impulses (second from top). The impulses are filtered with the RRC filter, then modulated onto a 57 kHz subcarrier using AM-DSB-SC (second from bottom). The bottom-most graph is an actual RBDS transmission still modulated on the 57 kHz subcarrier.
Time-domain view of modulated RDS signal comparing the signal made with Gnu Radio (top) and a signal shown in the NRSC-4 specification (bottom). NRSC-4 image credit: National Radio Systems Committee, NRSC-4, United States RBDS Standard, Specification of the radio broadcast data system, April, 2005, Part 1 - Standard document.

A More Basic Way to Create RBDS Signals?

While writing this post, I was trying out different things. One thing I tried was using normal, rectangular pulses with a basic (non-RRC) filter to create the RBDS waveform.

Time-domain view of original bitstream (top), differentially-encoded bitstream (middle) and Manchester-encoded, differentially-encoded waveform (bottom).

Using a basic windowed-sinc lowpass filter (the "FFT Low Pass Filter" block in Gnu Radio), then modulating the waveform onto a 57 kHz carrier, the waveform looks very similar to the waveform as both stated in the specification and as seen on all RDS-equipped FM stations.

RDS-like waveform created with rectangular pulses and filtered with a basic lowpass filter.

I only find this a curiosity and would be interested to see how it would fare on an actual RDS-equipped receiver.

Are All RDS Signals Equal?

I know you will be shocked when I say that the answer to the question above is, "No." The quality of the RDS signal is generally based on three factors. These are:

  1. the signal-to-noise ratio (SNR) of the FM signal it is riding on. As the SNR of the FM broadcast signal itself decreases, the SNR of the signals modulated onto it will also see a decrease.
  2. the level that the broadcaster has set for the RDS signal itself. FM broadcasters have only so much deviation they are allowed to put onto their carrier. In the US, that limit is typically +/-75 kHz. Each signal modulated onto the carrier adds to the deviation. That means they need to share. If it's a stereo signal, this means that the L+R audio, 19 kHz pilot tone, stereo signal (L-R) and RBDS must all add up such that, at maximum deviation, it does not exceed 75 (or 82.5 kHz, in certain circumstances). The FCC also states that the subcarriers, such as RBDS, cannot take up more than a certain percentage of the deviation. For monophonic, for example, subcarriers cannot account for more than 30% of the total deviation. Stereo signals can only use up to 20%. It's up to the station to decide how much of that percentage they want to use, since the rule is "may not exceed". They do not have an "at a minimum" rule, either.
  3. the noise added due to clipping from the main audio signal as it reaches the limits of deviation. Again, here in the US, the FCC has stated that FM broadcasters can only deviate the carriers so much, normally 75 kHz. They put in place limiters that keep the modulated signal from deviating more than +/-75 kHz (or whatever the limit is, as it may be higher depending on certain circumstances). If the modulating signal is so limited, it's the equivalent of clipping. This is distortion. It causes a jump in the noise floor of the signal. That jump will affect the SNR of any of the baseband signals (main audio, stereo audio, RBDS and other SCAs). This shows up as essentially impulse noise in the signal. During quiet times, the RBDS signal will look perfect. When the audio gets cranked up to the limits, the RBDS may essentially be "washed out".
  4. A spectral trace (top) and time domain trace (bottom) of the baseband signal of a FM broadcast station. This is a relatively low audio signal with a high SNR on the RBDS signal.
    High audio signal with clipping. Note how the time domain view (bottom) is flat on the top and bottom of the waveform. This is the clipping. This increases the noise floor so much that the RBDS signal is almost completely buried in noise, as shown in the spectral trace (top).

The point is that not every station will provide a RDS signal that is equally easy to demodulate and decode.

The images below are some examples. Each of these stations have roughly similar signal-to-noise ratios (SNR) for the FM station upon which they ride, yet the RDS signal levels are vastly different.

Signal levels and baseband spectrum of FM station 93.1. The RDS is relatively high.
Signal levels and baseband spectrum of FM station 93.9. The RDS is lower than the 93.1 MHz signal.
Signal levels and baseband spectrum of FM station 95.5 (WPGC). The RDS signal is extremely low.

Testing RDS Signal Levels

I can receive 38 different FM stations at my house. All of these 38 FM stations have RDS signals. I measured the RDS signal-to-noise ratio (SNR). I also measured the SNR of the FM station upon which it was modulated. I created a flowgraph to measure the SNR of each of these FM stations, as well as the SNR of their respective RDS signals.

I used a basic RTL-SDR (a RTL-SDR.com v3) for this test. I created a flowgraph that measures the signal power both at the FM station, the power of the RDS, and the power in a bandwidth next to the RDS. Even though the RTL-SDR does not provide calibrated signal power levels, it can provide relative power measurements by measuring two signals and calculating the difference.

I connected the RTL-SDR to my antenna. I used the flowgraph to first measure the FM signal power at each station. I adjusted the gain for each signal separately, and I used the time sink at the output of the RTL-SDR to determine the maximum signal level possible at that frequency. I also used the demodulated spectrum to ensure that the gain was not too high. The SDR can saturate at either the RF front end or the digitizer. The time domain display ensures that the digitizer is not saturated. With the RTL-SDR using a 8 bit digitizer, the maximum level occurs when the time sink using complex samples is at the maximum levels of +/-1. If, however, the RF front end is saturated, then the demodulated output will look distorted. It was a balancing act.

FM broadcast band as seen at my house. This was using a Signal Hound BB60C with their Spike software to capture this spectral trace. Note the large signals at 99.1 MHz and 107.9 MHz. These two, strong signals make it difficult to capture signals near them due to the large signal power difference.

Once the gain was set properly, I used the flowgraph to measure the signal level of the FM station, as well as the RDS SNR.

This is the image above with a graph of the RTL-SDR gain values (the dark blue line with square markers) overlaid. Note that, with the two strong FM stations, the gain value had to be greatly reduced. This would have also affected those signals nearby.

The next step involved remeasuring the power at each FM station, but without the antenna connected. This would provide the noise level. Calculating the difference between the signal level from the first step, and the noise level of this step, provided the signal-to-noise ratio (SNR) for the FM station.

I graphed the FM station signal SNR versus the RDS SNR. The results are quite interesting.

Graph showing the SNR of the FM broadcast station (horizontal axis) against the RDS SNR on that station. Note that there is a maximum curve (red line), but underneath, even for relatively similar broadcast SNRs, the RDS levels vary quite a bit. The FM broadcast signal SNR, as well as the deviation accorded the RDS signal, sets the maximum SNR possible. Beyond that, the amount of distortion created by the audio signal will lower the SNR, perhaps significantly.

It's possible to get a rough idea of the relative deviation of the RDS signal by comparing it with the level of the 19 kHz pilot tone. Obviously, this only works with stereo signals.

This is the baseband spectrum of 93.1 MHz (WPOC). It has a fairly high RDS signal. The amplitude difference between it and the 19 kHz pilot tone is relatively small.
This is the baseband spectrum of 95.5 (WPGC). It has a very low RDS signal. Note that the level of the 19 kHz pilot tone is roughly the same as the pilot tone on the 101.9 MHz station. But the RDS signal is much lower compared with the pilot tone.

Results

The test results show that the relative deviation provided to the RDS signal and the amount of noise created by distortion of the audio signal play a significant role in the RDS signal quality. Every broadcaster must have different ideas on how much the RDS level is required at the receiver. (I have to think that 95.5 (WPGC), shown above, doesn't care. 48 dB of SNR on the main signal, but the RDS barely registers at around 4 dB SNR? Come on.) It could also depend on what type of information is carried on the RDS signal. For example, I've noticed some stations actually run advertising on their RDS. I'm guessing that they take more care to ensure that the RDS is recoverable by the receivers of its listeners. People paying for advertising don't want to pay for silence.

One last note: I created a Gnu Radio flowgraph to measure the relative deviation created by the RDS signals, but it really only works on strong RDS signals. Otherwise, the noise and interference from the audio signals cause too much amplitude fluctuations to accurately measure the actual deviation just from the RDS signal.

Building a Demodulator

The whole purpose of the digital receiver is to get digital information out of it. Not just any digital information, either. The original digital information, with no (or as few) errors as possible. The great thing is that the RDS waveform is extremely hardy. With some basic circuitry, we can demodulate the carrier and get not only bits, but good bits. Ones that we can put into the RDS decoder and get the proper information back.

Basic Sections of a RDS Demodulator

Regardless of the specific details, here are the basic sections you'll find in any RDS decoder:

Downconversion

There are two methods to obtain the local oscillator used to downconvert the RDS signal. These are:

  1. Use the 19 kHz pilot tone and triple it (literally multiply it by itself three times). This requires that the station be a stereo FM station, not monophonic.
  2. Create the oscillator locally using the "best guess" to get as close as possible to 57 kHz. This is the only method possible if there is no stereo pilot tone.

Downconverting Using the Stereo Pilot Tone

The only copy of the RDS spec that I have is NRSC-4, which is dated from 2005. Anyway, that version stated:

During stereo broadcasts the subcarrier frequency will be locked to the third harmonic of the 19-kHz pilot- tone.

If this is true, then using the 3rd harmonic of the 19 kHz pilot tone should provide a oscillator that is already locked to the RDS signal. I have no idea if this is still true. The standard is now controlled by the International Electrotechnical Commission (IEC). I don't feel like shelling out the 185 Swiss francs (roughly $214 USD) for it. Instead, I've created a flowgraph that uses the 19 kHz pilot tone to demodulate the RDS signal.

This is the general flowgraph using the 19 kHz pilot signal to downconvert the 57 kHz RDS signal. This flowgraph operates as follows:
  1. This lowpass filter removes everything except for the analog FM signal.
  2. The "Quad Demod" block frequency demodulates the signal.
  3. This block filters out the 19 kHz pilot tone.
  4. This block calculates the third harmonic (hence three inputs).
  5. This block filters out the 57 kHz tone created by tripling the 19 kHz pilot.
  6. The third harmonic is multiplied against the original baseband signal, shifting the 57 kHz signal up to 0 Hz.
  7. The signal is sent on to be filtered, synced, and decoded.

I scanned through all 33 FM stereo stations that I can successfully receive in my area. Very few showed a good decoding. Most had a lot of errors. It's probable that my flowgraph needs tweaking. But, regardless, given the inability to handle non-stereo signals (which make up a significant fraction of all of the FM stations), I'll stick to a local oscillator.

Long story short: Out of 33 stereo stations I can recover, only 10 (less than a third) have synchronization between the pilot and RDS. This is just over a quarter of all FM stations (38) I can successfully recover. Frankly, this was one of the worst flowgraphs I tried.

SDRangel and RDS Decoding

Yes, SDRangel. I've covered SDRangel in some detail. The one thing that I did not cover was how to make it decode RDS signals. It's discussed in the figure below. I'm including it here because SDRangel demodulates and decodes RDS information using the 19 kHz pilot tone. I have 38 FM stations I can demodulate and get audio. Of those, 33 are stereo. That means there are 5 stations from which SDRangel cannot recover the RDS information. No stereo pilot tone, no RDS. (NOTE: I've also discovered that if the sample rate is too low (for example, I tried at 256 kSam/s), the RDS decoder will not work.) Out of the 38 stations total, I can only recover RDS information reliably from 18 stations, with another 8 stations providing RDS information with a lot of errors. The rest provided no information at all.

SDRangel showing RDS decoding. First, you need to open the "Broadcast FM Demodulator". Second, you need to tune to a stereo signal. That's indicated by the pilot tone (pink arrow) in the baseband spectrum. This gives you access to the "Pilot Lock Indicator" (yellow arrow). Click on it so that it shows two speakers pointing towards each other. If the icon turns green, then click on the "RDS Decode" icon (blue arrow). The RDS decode will be shown by the "Decode" icon (green arrow). All of the information will be shown within the "RDS Data" window.

The NRSC-Specified Receiver

The NRSC, along with specifying a transmitter circuit, also provided a suggested receiver. It has two parts that make it unusable for GRC. These are:

  1. synchronizing with the Manchester-encoded symbols, and multiplying with another signal to essentially remove the encoding. This is done by multiplying the Manchester-encoded signal with a square wave that runs at the symbol rate of 2375 Hz. This creates symbols at the original bit rate of 1187.5 Hz. GRC does not have this capability. Yes, you can create a square wave and multiply it with the signal. The problem is you have to synchronize it with the signal. There's no way (of which I'm aware) to do so in GRC currently.
  2. demodulating the signal using an "integrate-and-dump" circuit. GRC does not have an "integrate-and-dump" circuit (again, of which I'm aware). The "integrate" part is straightforward and, yes, there's a block for that. It's the "and-dump", which also implies resetting the circuit at the correct point in time. Once again, that's a problem of synchronization. That's not possible with GRC.
The receiver suggested in the NRSC RDS specification. This particular receiver relies on the pilot signal to downconvert the RDS signal. It also relies on synchronizing with and multiplying against the Manchester-encoded symbols as well as an "integrate-and-dump". Neither of these things are possible with GRC at the moment.

Downconversion with a Local Oscillator

This is the most straightforward way to downconvert the RDS signal. Create your own local oscillator, mix it with the RDS signal, and Bob's your uncle. It also has the advantage that it will work on any signal, whether stereo or monophonic. The drawback is that, if it is a stereo signal and if the pilot tone is synced with the RDS signal, it will take more circuitry to achieve lock with the RDS signal. While this will not be frequency locked (and, since frequency and phase are related, not phase locked, either), it should be close enough for a Costas loop (a form of phase lock loop or PLL) to lock to the signal.

Filtering the Downconverted Signal

What's the best filter to use on this signal? According to the specification, it should be a root raised cosine (RRC). As I discovered based on testing the different RDS signals in my area, the RRC is the best filter to use as it is a matched filter (filter that matches the actual, incoming signal).

Comparison of filtered waveforms between a standard, windowed-sinc filter (top) and RRC filter (bottom). Note how the RRC filter is much flatter when looking at two of the same symbols next to each other.

Creating a Barebones Basic Receiver

We have enough now to create a basic RDS demodulator. The great thing about the RDS signal is that it is an extremely forgiving signal. If the signal level is high enough, it does not require a lot of processing in order to get a usable bitstream. We can even do so without requiring symbol sync. Because the RDS signal is an amplitude shift keyed (ASK) signal, all we need to do is downconvert it, filter it, phase lock to it, then slice it in half. The half on one side will be a digital 1; those on the other side will be digital 0. Again, this requires a fairly strong signal (SNR greater than ~13 dB). It has the advantage that we don't even need to sync to the symbols, though we do need to phase lock to the carrier. The disadvantage is that, because of the lack of symbol sync, we'll get periodic stream of bit errors. But, for the most part, we should have a signal that is good enough.

Basic RBDS demodulator. This uses a RRC lowpass filter as a matched filter. The output is fed into a Costas loop, which phase locks the samples to the real-axis of the IQ stream. The "Complex to Real" extracts the phase locked samples, which essentially demodulates the ASK signal. The "Binary Slicer" converts the samples to bits. Because of the sample rate, there are 48 samples per symbol. The "Demux" block extracts one bit out of each 48, which represents the value of that bit. It also removes the Manchester-encoding. The final block, the "Differential Decoder", removes the differential encoding. The output of the differential encoder is the original bit stream.
Output of the basic demodulator flowgraph, with an added time domain display from the output of the "Complex to Real" block, which shows the sampled bit stream. The time raster (upper, left) shows how the bitstream is consistent, with the occasional bitslip (center of the raster) due to the lack of symbol sync. Regardless, the rest of the time, the output is nearly perfect.

Adding Symbol Sync

The barebones basic demodulator above has some disadvantages. These are:

While I've listed them as two, different problems (high required SNR and lack of symbol sync), they're actually related. If we can achieve symbol sync, then we can select optimal points to represent each bit. This allows us to successfully demodulate the bitstream even when we have much lower SNRs.

What Are The Issues of Synchronizing?

Demodulating a digital signal requires locking to the frequency and phase of the carrier, and symbols of the digital bitstream. It doesn't necessarily have to occur in that order, either.

The general order for achieving these in Gnu Radio are:

  1. Downconvert using either a local oscillator set for 57 kHz or, if the FM station has a 19 kHz pilot tone, using a triple-conversion to create a 57 kHz oscillator. Even using a local oscillator should get you close enough for the follow-on circuits to achieve lock, either symbol or phase.
  2. Symbol sync using the (appropriately named) "Symbol Sync" block. (NOTE: The "Polyphase Clock Sync" block is being deprecated. The "Symbol Sync" block is the new hotness.
  3. Finish phase locking to the now-synced symbols. This can either be as simple as a Costas loop to achieve general phase lock, or using a "Constellation Decoder" set to "BPSK". This latter will phase lock and recover bits based on the output of the "Symbol Sync" block.

Obtaining Symbol Sync

One of the reasons for this post is that the block I used in my original flowgraph for symbol sync, the "Polyphase Clock Sync", is being deprecated. I had to change it. Another big reason for relooking at this receiver is Prof Jason's videos on software defined radio. I really understood the symbol synchronization much better after watching those videos. Combine that with fred harris's book "Multirate Signal Processing for Communication Systems" and you have the information needed to create a symbol sync circuit (sort of).

What's an Error Signal?

Let's start with Prof Jason's video on symbol sync. Yes, it's 46 minutes long. It's also well worth the watch. He explains what Dr. harris explains in his book.

Let's say we have a demodulated digital signal which came from a carrier in which we had both frequency and phase synchronization. A RDS signal would appear as shown below:

RDS signal phase-synced and demodulated. Looking at the signal, you can eyeball where the maxima and minima are. What you're doing with your eyeball is determining where the signal peaks, which is where its absolute amplitude (y) is at its maximum (or minimum) and its slope (y') is zero.
This is an eye diagram of a RDS signal. The point of symbol synchronization is to select the correct point in time to represent the signal level. We can use three consecutive points to determine where this point resides. If we straddle the symbol point (as shown here), with one point that is too early (red), too late (yellow) and just right (green), then we can determine that the green point is the correct one. This is by calculating the difference between samples (y(n)-y(n-1)), then multiplying that by the point itself (y(n)).

We can create a signal, called an error signal, which would be the signal equivalent of what your eyeball is doing. When the signal is near zero, you're close to the sync point. When it's not at zero, you're not at the sync point. fred harris calculated that this error signal could be approximated with the following equation:

I was able to make a flowgraph that creates an error signal as well as impulses at the appropriate symbol points.

Flowgraph that takes as input the baseband of a FM broadcast station, downconverts the RDS signal to baseband, filters it, phase locks to it, demodulates it, calculates the error signal for determining the symbol point, and creates an impulse at the symbol point.

Running this flowgraph, we get the following:

Display of the RDS signal (black) with the error signal (red) and the points of synchronization (blue impulses). Note that the error signal is at the sync point when it is zero with a negative-going slope.
This is a snapshot from Prof Jason's video on symbol synchronization. Note that the error signal from the GRC flowgraph matches the error signal as shown in the lower, right of this image.

A couple things to bear in mind are:

  1. You need transitions. The more the signal goes from a 1 to a 0 or from 0 to 1, the better the synchronization.
  2. You need multiple samples per symbol. By combining the signal and the differential of the signal, you can figure out the point at which to sample the symbol properly.

It turns out that you can slightly modify the flowgraph above to recover the data bits. By changing the impulses into rectangular pulses, changing the signal to binary (using the "Binary Slicer" block), picking out every 48th sample (creates the correct bit rate plus it removes the biphase encoding), and performing a differential decoding, this signal can be fed into the RDS blocks in GRC.

Display from the error signal flowgraph (discussed above) but with extra blocks for decoding at the sync points. This has not achieved bit sync (that would require a feedback loop, which GRC does not allow), but the decoding is pretty good even without it.

Adding the Symbol Sync Block

Now we can add an actual "Symbol Sync" block. This block can essentially perform the same as the flowgraph above, except it has feedback which adjusts the symbol rate to keep it synchronized with that of the signal. The result is a flowgraph that perfectly demodulates RDS signals when the RDS SNR is greater than about 7 dB. That was a total of 18 out of the 38 stations. It also provides a usable decode (though with errors) down to about 4 dB, which was another 12. That means this flowgraph provided decoding of 30 out of the 38 available. The flowgraph appears as follows:

Flowgraph using yy' maximum likelihood algorithm in "Symbol Sync" block. As before, the filter is the RRC matched filter, which is fed into the "Symbol Sync" block. The output of the block is fed into a BPSK demodulator.
Output of flowgraph using "Symbol Sync" block set to "y[n]y'[n] maximum likelihood" algorithm. The time raster shows the program identifier (PI) columns, which are perfect.

A quick note. The "Constellation Receiver" block performs the actual BPSK demodulation. If I understand it correctly, this block first does a Costas loop to frequency and phase lock to the signal. According to the documentation, it uses constellation points close to each other to estimate the frequency offset. It then does a hard decision to determine the symbol to bit mapping. I think it's performing a Euclidean-distance calculation for each constellation point. (Remember: the previous block determined the proper sample point, but didn't correct any frequency or phase error.) Based on the distance of each point to what it thinks it should be (again, amplitudes are important!), it makes a decision on whether it's a 0 or a 1. This is followed with the differential decoding. After that, the bit stream is ready for the RDS decoder and parser blocks.

Looking at Bastian Bloessl's RDS Receiver

I wanted to compare my flowgraph with Bastian Bloessl's RDS decoder. Looking at his flowgraph, everything is pretty standard through the initial FM station filter and demodulation. He uses a basic complex frequency shift to shift the RDS signal down to baseband. This means it will work with any FM station, stereo or monophonic, since he's not relying on the 19 kHz pilot tone of a stereo signal. While I didn't point it out in my image above, he changes the sample rate from 25 kHz to 19 kHz. This will make it easier to perform the symbol sync and constellation decoding. The reason is that the symbol rate of a fully-encoded RDS signal is 19 kHz / 8 = 2375 kHz.

It's the next, two parts that really set it apart. The first is the filtering. Rather than a straightforward RRC lowpass filter, he uses an impulse response of the full Manchester-encoded filter. The other significant difference is that he uses the "zero crossing" algorithm in the "Symbol Sync" block.

A sidenote: The AGC was interesting to me because it didn't use the defaults. All of the values seemed very deliberate. Come to find out, it's important that the amplitudes going into the "Symbol Sync" meet certain values, especially for certain timing error detectors (TED). The "zero crossing" detector is one such TED. This is a basic detector that essentially counts how often a signal crosses zero (both positive and negative going) to determine its clock rate. The second aspect is that he's selected 16 samples per symbol. This means he's using a symbol rate of 1187.5 bits/sec. This is the unencoded bit rate of the RDS signal. The output is performing both symbol sync and Manchester-decoding at the same time. (NOTE: Again, this only works because of the differential encoding that follows. Without that differential encoding, it would be necessary to separate the symbol sync and Manchester decoding.)

Bastian Bloessl's flowgraph for decoding RDS transmissions. This has been modified from the original to remove the audio circuit (not needed for RDS decoding), change the original source from a USRP to a RTL-SDR, and make the flowgraph more "readable". The flowgraph uses the following sequence: (1) a frequency shift (to avoid the LO feedthrough of many SDRs) and filter for the FM station, (2) demodulation, (3) shift the RDS signal to baseband and filter it, (4) a RRC matched filter using a root raised cosine (RRC) filter but with the Manchester-encoding, (5) symbol sync using a "zero crossing" error detector, (6) a BPSK decoder that also provides frequency and phase sync, and (7) the differential decoder.

Bloessl's RDS Flowgraph Performance

I'm going to say this upfront. Bloessl's flowgraph works best overall. When I ran it against all 38 FM stations I can receive, it perfectly decoded 26 of them, and had usable decodings for another 6. This meant that, out of 38 signals, it could provide decodings for 32 of them. This was better than my flowgraph by 2 (my flowgraph only provided usable outputs for 30 out of the 38), and for several stations, Bloessl's flowgraph provided perfect decodings whereas mine would provide some errors.

Wrap-up

There you have it. Several ways in which to demodulate RDS signals. If you're just getting into signal processing, this will hopefully provide you some avenues for further exploration.

Here's a Random Fact...