Home

Awesome

Pi-FM-RDS

FM-RDS transmitter using the Raspberry Pi

This program generates an FM modulation, with RDS (Radio Data System) data generated in real time. It can include monophonic or stereophonic audio.

It is based on the FM transmitter created by Oliver Mattos and Oskar Weigl, and later adapted to using DMA by Richard Hirst. Christophe Jacquet adapted it and added the RDS data generator and modulator. The transmitter uses the Raspberry Pi's PWM generator to produce VHF signals.

It is compatible with both the Raspberry Pi 1 (the original one) and the Raspberry Pi 2, 3 and 4.

PiFmRds has been developed for experimentation only. It is not a media center, it is not intended to broadcast music to your stereo system. See the legal warning.

How to use it?

Pi-FM-RDS, depends on the sndfile library. To install this library on Debian-like distributions, for instance Raspbian, run sudo apt install libsndfile1-dev.

Pi-FM-RDS also depends on the Linux rpi-mailbox driver, so you need a recent Linux kernel. The Raspbian releases have this starting from August 2015.

Important. The binaries compiled for the Raspberry Pi 1 are not compatible with the Raspberry Pi 2/3, and conversely. Always re-compile when switching models, so do not skip the make clean step in the instructions below!

Clone the source repository and run make in the src directory:

git clone https://github.com/ChristopheJacquet/PiFmRds.git
cd PiFmRds/src
make clean
make

Important. If make reports any error, then no pi_fm_rds executable file is generated (and vice versa). So any error must be fixed before you can proceed to the next steps. make may fail if any required library is missing (see above), or it could be a bug on a specific/newer distribution. In this case, please file a bug.

If make reports no error (i.e. the pi_fm_rds executable gets generated), you can then simply run:

sudo ./pi_fm_rds

This will generate an FM transmission on 107.9 MHz, with default station name (PS), radiotext (RT) and PI-code, without audio. The radiofrequency signal is emitted on GPIO 4 (pin 7 on header P1).

You can add monophonic or stereophonic audio by referencing an audio file as follows:

sudo ./pi_fm_rds -audio sound.wav

To test stereophonic audio, you can try the file stereo_44100.wav provided.

The more general syntax for running Pi-FM-RDS is as follows:

pi_fm_rds [-freq freq] [-audio file] [-ppm ppm_error] [-pi pi_code] [-ps ps_text] [-rt rt_text]

All arguments are optional:

By default the PS changes back and forth between Pi-FmRds and a sequence number, starting at 00000000. The PS changes around one time per second.

Clock calibration (only if experiencing difficulties)

The RDS standards states that the error for the 57 kHz subcarrier must be less than ± 6 Hz, i.e. less than 105 ppm (parts per million). The Raspberry Pi's oscillator error may be above this figure. That is where the -ppm parameter comes into play: you specify your Pi's error and Pi-FM-RDS adjusts the clock dividers accordingly.

In practice, I found that Pi-FM-RDS works okay even without using the -ppm parameter. I suppose the receivers are more tolerant than stated in the RDS spec.

One way to measure the ppm error is to play the pulses.wav file: it will play a pulse for precisely 1 second, then play a 1-second silence, and so on. Record the audio output from a radio with a good audio card. Say you sample at 44.1 kHz. Measure 10 intervals. Using Audacity for example determine the number of samples of these 10 intervals: in the absence of clock error, it should be 441,000 samples. With my Pi, I found 441,132 samples. Therefore, my ppm error is (441132-441000)/441000 * 1e6 = 299 ppm, assuming that my sampling device (audio card) has no clock error...

Piping audio into Pi-FM-RDS

If you use the argument -audio -, Pi-FM-RDS reads audio data on standard input. This allows you to pipe the output of a program into Pi-FM-RDS. For instance, this can be used to read MP3 files using Sox:

sox -t mp3 http://www.linuxvoice.com/episodes/lv_s02e01.mp3 -t wav -  | sudo ./pi_fm_rds -audio -

Or to pipe the AUX input of a sound card into Pi-FM-RDS:

sudo arecord -fS16_LE -r 44100 -Dplughw:1,0 -c 2 -  | sudo ./pi_fm_rds -audio -

Changing PS, RT and TA at run-time

You can control PS, RT and TA (Traffic Announcement flag) at run-time using a named pipe (FIFO). For this run Pi-FM-RDS with the -ctl argument.

Example:

mkfifo rds_ctl
sudo ./pi_fm_rds -ctl rds_ctl

At this point, Pi-FM-RDS waits until another program opens the named pipe in write mode (for example cat >rds_ctl in the example below) before it starts transmitting.

You can use the named pipe to send “commands” to change PS, RT and TA. For instance, in another terminal:

cat >rds_ctl
PS MyText
RT A text to be sent as radiotext
TA ON
PS OtherTxt
TA OFF
...

[!TIP] The program that opens the named pipe in write mode can be started after Pi-FM-RDS (like above) or before (in which case Pi-FM-RDS does not have to wait at startup).

Every line must start with either PS, RT or TA, followed by one space character, and the desired value. Any other line format is silently ignored. TA ON switches the Traffic Announcement flag to on, any other value switches it to off.

Non-ASCII characters

You can use the full range of characters supported by the RDS protocol. Pi-FM-RDS decodes the input strings based on the system's locale variables. As of early 2024, Raspberry Pi OS uses by default UTF-8 and the LANG variable is set to en_GB.UTF-8. With this setup, it should work out of the box.

If it does not work, look at the first message that Pi-FM-RDS prints out. It should be something sensible, like:

Locale set to en_GB.UTF-8.

If it is not consistent with your setup, or if the locale appears to be set to (null), then your locale variables are not set correctly and Pi-FM-RDS is incapable of working with non-ASCII characters.

Warning and Disclaimer

PiFmRds is an experimental program, designed only for experimentation. It is in no way intended to become a personal media center or a tool to operate a radio station, or even broadcast sound to one's own stereo system.

In most countries, transmitting radio waves without a state-issued licence specific to the transmission modalities (frequency, power, bandwidth, etc.) is illegal.

Therefore, always connect a shielded transmission line from the RaspberryPi directly to a radio receiver, so as not to emit radio waves. Never use an antenna.

Even if you are a licensed amateur radio operator, using PiFmRds to transmit radio waves on ham frequencies without any filtering between the RaspberryPi and an antenna is most probably illegal because the square-wave carrier is very rich in harmonics, so the bandwidth requirements are likely not met.

I could not be held liable for any misuse of your own Raspberry Pi. Any experiment is made under your own responsibility.

Tests

Pi-FM-RDS was successfully tested with all my RDS-able devices, namely:

Reception works perfectly with all the devices above. RDS Surveyor reports no group errors.

CPU Usage

CPU usage is as follows:

CPU usage increases dramatically when adding audio because the program has to upsample the (unspecified) sample rate of the input audio file to 228 kHz, its internal operating sample rate. Doing so, it has to apply an FIR filter, which is costly.

Design

The RDS data generator lies in the rds.c file.

The RDS data generator generates cyclically four 0A groups (for transmitting PS), and one 2A group (for transmitting RT). In addition, every minute, it inserts a 4A group (for transmitting CT, clock time). get_rds_group generates one group, and uses crc for computing the CRC.

To get samples of RDS data, call get_rds_samples. It calls get_rds_group, differentially encodes the signal and generates a shaped biphase symbol. Successive biphase symbols overlap: the samples are added so that the result is equivalent to applying the shaping filter (a root-raised-cosine (RRC) filter specified in the RDS standard) to a sequence of Manchester-encoded pulses.

The shaped biphase symbol is generated once and for all by a Python program called generate_waveforms.py that uses Pydemod, one of my other software radio projects. This Python program generates an array called waveform_biphase that results from the application of the RRC filter to a positive-negative impulse pair. Note that the output of generate_waveforms.py, two files named waveforms.c and waveforms.h, are included in the Git repository, so you don't need to run the Python script yourself to compile Pi-FM-RDS.

Internally, the program samples all signals at 228 kHz, four times the RDS subcarrier's 57 kHz.

The FM multiplex signal (baseband signal) is generated by fm_mpx.c. This file handles the upsampling of the input audio file to 228 kHz, and the generation of the multiplex: unmodulated left+right signal (limited to 15 kHz), possibly the stereo pilot at 19 kHz, possibly the left-right signal, amplitude-modulated on 38 kHz (suppressed carrier) and RDS signal from rds.c. Upsampling is performed using a zero-order hold followed by an FIR low-pass filter of order 60. The filter is a sampled sinc windowed by a Hamming window. The filter coefficients are generated at startup so that the filter cuts frequencies above the minimum of:

The samples are played by pi_fm_rds.c that is adapted from Richard Hirst's PiFmDma. The program was changed to support a sample rate of precisely 228 kHz.

References

History


© Christophe Jacquet (F8FTK), 2014-2024. Released under the GNU GPL v3.