Generating FM MPX on a Raspberry Pi 3

It’s been possible to turn your Raspberry Pi into a little FM transmitter for quite a while now. Download the right software, attach a wire to the GPIO pin in and you’re broadcasting a surprising distance.

But what about stereo or RDS? Turns out that you can team your Pi up with your exciter and get the whole lot for less than you expect. With the right parts and configuration, you can generate the multiplexed signal that feeds a transmitter (or “exciter” as you’ll hear it called) straight out of a Raspberry Pi 3.

That’s great but what exactly does it mean? Let’s look at what makes up a broadcast FM signal. Before going into a transmitter or exciter that’ll do the actual frequency modulation, we build up a baseband signal from a few different parts.

A capture of the baseband multiplexed signal for FM transmission.

A capture of the baseband multiplexed signal for FM transmission.

Between 0 and 15kHz you get the mono audio mix, that’s left and right combined into one. This is the bit that survives best when you’re struggling from poor reception. Not bad as a fallback option really.

At 19kHz is a steady tone called the “pilot tone”. It’s used to indicate that there’s a stereo transmission and turns on that little red light on your radio. As you can guess, it means we can be a little sneaky and turn the light on without actually broadcasting in stereo.

In reality, you’re more likely to see transmissions deliberately in mono. If you’re after a robust signal in challenging terrain, you’re better leaving that stereo light off.

To encode the actual stereo audio, L-R sits between 23kHz and 53kHz, centring on 38kHz (2x 19kHz). The way it’s encoded is slightly different from how the L+R bit is done (it’s a double sideband signal rather than raw audio) but is combined with L+R to generate the left and right channels. Specifically (L+R)+(L-R) gives 2L and (L+R)-(L-R) gives 2R.

Finally for us, centred at 57kHz (3x 19kHz), you’ll find RDS. This is the bit that carries the text you see on your radio’s display and can also trigger that really annoying feature where traffic bulletins burst in over your favourite CDs.

Ok, that’s underselling it a bit. It can also inform your tuner as to what frequencies other transmitters are on and it can even carry traffic or telemetry data as well.

So, taking that into account, that’s around 60kHz of multiplexed “audio” signal you need to feed into your transmitter or exciter. Accounting for something called the Nyquist Frequency that applies to digital systems and it turns out you need a sample rate of at least 120kHz.

So, the Raspberry Pi’s built in audio interface isn’t quite going to do the job for us. Thankfully the Cirrus Logic (ex-Wolfson Audio) audio interface for the Pi exists. Even better, it works with the Pi 3.

A Raspberry Pi 3 combined with Cirrus Logic audio interface.

A Raspberry Pi 3 combined with Cirrus Logic audio interface.

Getting it going involves a little bit of work. You start by installing the full Raspbian desktop image onto a micro SD card and running it on the Pi. That gets the operating system installed but doesn’t get the sound card working.

Turns out you need to replace the kernel with one that has the driver in it. While admittedly it feels like stepping back into the early days of Linux where compiling a kernel was an alarmingly regular suggestion for getting things working, there is a way round in this case. If you go to this website, not only will you find pre-compiled kernels but you’ll also find full instructions for installing all the required tools.

If you’re planning on following the steps in here for yourself, it’s worth noting that you will need MMAP support and you really do need the initialisation scripts. Is it embarrassing to admit that one caught me out?

Either way, you’ll also need the software I used to generate the multiplexed signal – Stereo Tool. There’s a version pre-compiled for the Raspberry Pi 2 that also works just as well on the Pi 3.

Now, I’ll admit this is commercial software. However, it’s available free to try (great for experimenting) and actually does the job really well. As I’ve stated in previous posts, it competes with some hardware audio processors you see in live deployment today.

Getting it going on Linux does require you to know and work with jackd – one of the many audio frameworks you could bump in to. The benefits of jackd include the ability to directly connect any audio software tool, source or destination together. This is something I took advantage of in the experiment I set up – a web stream from VLC plumbed through Stereo Tool and output from the Cirrus Audio interface.

A Raspberry Pi generating the multiplexed baseband signal for FM.

A Raspberry Pi generating the multiplexed baseband signal for FM.

While generating the multiplexed signal involved simply ensuring the sample rate of jackd was set to 192kHz and turning on the right options in Stereo Tool, there was a couple of catches. There’s not quite enough “grunt” in the Pi to both process the audio and generate the multiplexed signal. It’s a shame but not really a surprise. Desktop systems sometimes struggle to meet the challenge and the original Raspberry Pi struggled to perform MP3 encoding in real time.

While thankfully the audio stream I was pulling was already processed, it’s something you’d need to consider if your were ever brave enough to take this live to air. Especially as running anything more than simple clipping will cause audible stuttering.

That said, the Pi has somewhat proven itself a solid option for streaming audio for broadcast, especially with software such as OpenOB. Generating the multiplexed signal for FM transmission isn’t much of a stretch from there.

Another problem is a more practical one – there’s no case I’ve come across that supports both the Pi and the Cirrus Logic audio interface. Leaving an exposed circuit board in a remote location doesn’t strike me as the most reliable option.

Admittedly, that’s a minor issue. The neat bit was being able to generate the signal on a small, low-priced computer with little effort. Even if it wouldn’t be the most practical solution for such a case in the real world.

Now, if only I had an exciter kicking about, I’d love to show you the thing actually working on a real FM receiver…

You may also like...

11 Responses

  1. Nice article, thanks. I was pondering local broadcasting to smartphones with FM receivers…alas that appears to be a doomed idea given the lack of tuners in iphones and others.
    There are cases for the pi + cirus card if you want to make them yourself with a laser cutter. See
    I bought a few from the states and am selling a couple of the extras that I no longer need on ebay if you want one.

    • marc says:

      You’re bang on with regards to tuners in smartphones – it’s a relatively dead thing. That said, RadioDNS is a clever bit of work. They’ve got a standard for flipping between FM, DAB and even online streams in the same smartphone app. It does need the appropriate hardware and custom app for it though. Oh, and from memory, it uses the RDS PI to ID the FM service.

      It’s a real shame though as current mobile IP networks aren’t really built for TV/radio distribution. You’re seeing some multicast being used to deliver extra services on home broadband offerings but there’s still the shared, limited airtime problem on mobile networks to tackle. I could go on but that’s maybe for another post.

      As for the case, I’ll certainly look into the options you’ve posted links to. Getting access to a laser cutter is a little tricky in my world and a custom bodged 1RU metal case is probably a bit overkill for this application. 😉

  2. nick says:

    Hello,really nice article and very usefull,i want to ask you is there any way to pass MPX through local lan over 2 raspberry’s or one pc and one raspberry with external mpx and not stereo tool. which steaming encoding will accept mpx and which player then will receive and decode full mpx audio? is there any way? thanks for your time.

    • marc says:

      Good question. I’ve never tried it myself but I would have thought it would be possible using OpenOB. You would want to use the linear PCM option which at 192kHz sampling rate for a mono stream gives you about 3Mbps bandwidth consumption, which should be do-able on a Pi.

      The only catch with this approach is the author of OpenOB does state he’s not tested it for this application but would expect it to work.

      • nick says:

        Thank you very much mark for your quick reply,i suggest that at the end pi3 before the exciter i need Wolfson Audio sound card for the 192kbps right?
        At forum of stereo tool i readed the following setup
        Encoding Site: Audio > Stereo Tool > FlacEncoder > Icecast
        Decoding Site: Icecast > Mplayer > Wolfson AudioCard
        could it be done?

        • marc says:

          If you’re carrying the MPX signal the whole way as per something like this:

          [ External Processor ] -> [ Pi at Studios ] -> [ IP LINK ] -> [ Pi at Remote End ] -> [ Exciter ]

          You would need 192kHz sample rate cards (Wolfson/Cirrus) at both ends. If everything is done in software at the studio end, it may be possible to do without one of them (use the dummy Jack interface instead). However, the Pi3 might not quite have the “grunt” to do the full processing you’ll likely want for FM.

          The arrangement you’re suggesting could indeed work. FLAC does promise lossless transmission. However, you would only need an Icecast server at one end. Much along the lines of what I did with the Liquidsoap/Pi stuff as detailed elsewhere on this blog.

  3. nick says:

    You’re right the right setup is
    [ External Processor ] -> [ Pi at Studios ] -> [ IP LINK ] -> [ Pi at Remote End ] -> [ Exciter ]

    so you suggest me to try OpenOB to make it work.
    if you have a skype or i can bother you by email please let me know at my email because i’m a newbie at raspberry and maybe i will need some info’s from you and some help if it is possible.
    Thanks again for your time!

  4. Sam says:

    I’ve been testing JMPX in an LPFM station for RDS. I need to modify it a bit to work in my situation, but I can because open source:

    Also, do you recommend OpenOB? I’ll admit to being a little allergic to python and gstreamer, to the point where I’m considering just hacking something together in C that takes what comes in on jack ports and poots it out over a TCP connection.

    • marc says:

      I must say I’ve never heard of JMPX before. Looks somewhat interesting but would still need a multiband compressor sitting in front of it from most services. I could see it being useful though.

      With regards to OpenOB, it can do the job but you need the bandwidth for something like the MPX signal – high sample rate and you’d probably not want to digitally compress it.

      • Sam says:

        I should have been more clear, I’m not using it for MPX. I’m generating the MPX on site, but now I’m considering how to get live, low-latency audio to the studio. The two major off the shelf things I’m looking at are a combination of software to generate an RTP stream to liquidsoap, or OpenOB. I thought about emailing, but I’m not sure where to ask this question. 🙂

        As far as a multiband compressor, I’m using CALF for that:

        Though I daresay I’m far from done tweaking it to my liking. I’ll be putting together a small exciter for testing in the near future. I bought a couple kits for testing, but the frequency stability leaves something to be desired, so I’m going to try building something with a better VCO.

        • marc says:

          OpenOB does get the odd mention on community radio mailing lists and forums such as MediaUK. I think you’ll struggle to get truly low latency but I’ve only heard good things about OpenOB.

          Good luck with your experiments and I’ll have to take a look at CALF. I do remember using some old CLI audio processing tools years ago but CALF looks like it could be a useful modern alternative.

Leave a Reply

Your email address will not be published. Required fields are marked *