
The RDS specifies the filtering used for 57 kHz sub carrier, only in a notation that is alien to me. See section 1.7 of the RBDS or EN 50067 standard.
Here are the blighters:
| (1) | |
| (2) | |
![]() |
(3) |
![]() |
(4) |
Formula (4) defines the data period, 842 µs. This is 1/48 of the sub-carrier frequency. That's not too painful.
The first two are fairly straight forward. The
threw me. Three D.S.P. text books later, I saw things more clearly.
A
(t)
is known as the Dirac delta function, or impulse function.
To paraphrase (1): a Logic 1 at source gives a positive impulse, and half a data period later, a negative impulse.
To paraphrase (2): a Logic 0 at source gives a negative impulse, and half a data period later, a positive impulse.
These agree with the following diagram taken from the spec:

This is how we get from waveform 2, the differentially encoded data, to the bi-phase data in waveform 4.
That leaves us with formula (3) that gets us waveform 5.
A positive impulse would look like this:

This is the sinc or sin(x pi)/x pi function.
In formula (3) we are only interested in f between 0 and twice the data rate:

This is like a glitch with pre-ringing.
Assembling theses impulses into the data stream used in the specification example:

(We're getting close!) Applying the above waveform through a cosine filter that's "resonant" at 1/4 of the data rate frequency, we reduce the double peak:

This is the waveform we apply to the balanced modulator.
The PIC has limited memory. I store the following highlighted part of the above waveform as a lookup table:

This is a modest table of a mere 40 bytes, enough to reproduce the waveform sampling at 38 kHz. By judicious coding either the double peak, or its inverse, or a sine wave can be generated.
Here's a screen shot of the PIC DTA output:

And how were the images on this page created?
I created a MFC VC++ dialog app that used techniques taken from DSP text books.
The app performs a series of operations on a pair of buffers. A function reads an input buffer, "does something with it" and updates the output buffer.
The app goes through the following stages, simulating a real RDS transmitter. (Well for a 13ms period - long enough to simulate the above waveforms.)
All functions have the same signature. Here's one of the more tricky ones:
| void sinc (const double in[], double out[]) { for (int i1 = 0; i1 < N; i1++) { for (int i2 = -8; i2 < 16; i2++) { int index = i1 + i2; double d1 = ((index < N) && (index >= 0)) ? in[index]: 0.0; if (i2 != 0) out[i1] += d1 * (sin((TWOPI * i2) / 16.0) / ((TWOPI * i2 )/ 16.0)) ; else out[i1] += d1; } out[i1] /= 1; } } |
The buffer size used was 500 (N). 16 samples per data period - that gives me a chance to display 31 or so data periods.
The output buffer gets painted on the WM_PAINT, as you do. The PIC table enjoyed a more circuitous route from output buffer, via TRACE statements, Excel for encoding as 7 bit data, and finally to the PIC source.
This page was last updated on the 30th July 2002