Earlier I modified my radio to output raw bits from its RDS decoder chip. The radio already displays a Programme Service name, but for FM DX purposes, it's fun to see more data. Also, the Programme Identification code can be decoded from very weak signals, identifying distant stations. And I can receive interesting traffic information messages by decrypting RDS-TMC with the encryption keys I've recovered. Now that I have a Raspberry Pi, it's time to make a dedicated little display.
First, I changed the radio connector from my previous modification. This one, salvaged from a mystery phone device I found at a flea market, doesn't come loose as easily as the Mini-DIN:
I bought a discount 2x16-character HD44780-compliant LCD at the local parts store and made a 4-pin connection to the Raspberry Pi GPIO header. I used another two GPIO pins to interface the Pi with my radio (one for data, one for clock). I then wrote a C program to read the 1187 bps RDS data, process it for error correction and synchronization, and output it to a Perl script for interpretation. The script then returns a text string to be displayed on the LCD. The bcm2835 library is used for GPIO interaction.
Turns out GPIO is not very good for data rates this high. The C program takes about 5 samples per RDS clock cycle, and uses 97 % of the CPU time. With the Perl script and LCD update running, the poor Pi can't keep up with realtime data, and lots of bits are lost. But after waiting patiently, I finally get a Programme Service name, Programme Identification code, and RadioText.
On the left, my radio. On the right, the RasPi in its doghouse:
Experimenting with lower sampling rates will hopefully alleviate the problems. But it kind of works already.
Update 2013-04-01: According to this benchmark, this very library should be able to handle GPIO speeds of several megahertz. So I tried running the program in
strace to see what it's doing to burn so much CPU. Turns out it's sitting in a single nanosleep() call and never returning!
I experimented a bit and ended up replacing all calls to bcm2835_delayMicroseconds() (which uses nanosleep()) with calls to usleep(). And it works! CPU utilization is at 10% maximum, and a PS name appears lightning fast when changing stations. So, something was probably wrong with nanosleep() or bcm2835's implementation thereof.