Data-channel spectrum shaping

Fathoming out the Formulae

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)

My interpretation

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. 

Formula (3)

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.

PIC Implementation

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:

How was the 40 byte waveform table calculated?

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.

To the RDS generator page

back to the RDS page

This page was last updated on the 30th July 2002