Ever want to use your $20 RTLSDR dongle as a simple FM scanner to listen to your local public safety or
ham radio repeater traffic? @nottheoilrig on my local hackspace’s
mailing list inquired about how to record analog FM off the air to a file. After some fiddling, I
found this is quite simple with the
rtl_fm tool included in the osmocom
rtl-sdr distribution. The dongle and software does quite
a good job scanning too, its pickup time is very quick even monitoring many channels.
What you will need to do this:
- A suitable RTLSDR (RTL2832U + tuner) dongle
- An antenna for the band of interest. A roughly 1/4λ wire stuck in the antenna input will get you pretty far.
- The osmocom rtl-sdr library and included
rtl_fmtool built and working on your platform of choice. I use Linux, YMMV.
- If you want to use the filtering and file output pipeline I recommend, SoX built and working on your platform.
For the tl;dr among you, we’ll start with a complete example and break it down with annotations further on, so don’t be intimidated! Or, just dive right in and modify the below to suit your needs:
rtl_fm -F9 -f 143.175M -f 146.94M -l500 -s 12k - | \ sox --buffer 128 -t raw -r 12k -es -b 16 -c 1 - -p sinc 200-3.5k compand 0.1,0.8 6:0,-3 | \ tee >(sox -p <file.wav>) | play --buffer 128 -p
Let’s break this down into the 4 components and help you modify it for your needs
rtl_fm -F9 -f 143.175M -f 146.94M -l500 -s 12k -
This command starts the RTLSDR dongle and scans between 143.175MHz and 146.940MHz. When the squelch
opens, it will output raw 12KHz audio samples to
stdout. You will want to change the
for your own frequencies of interest. Any number of -f parameters can be specified directly as
above, or in
start:stop:step format to scan ranges. The
-l parameter determines the squelch
level and isn’t well defined; it also changes sensitivity with
-s, the sample rate, so you’ll
probably need to experiment.
A good description of all of the
rtl_fm options can be found at the bottom of Kyle Keen’s rtl_fm
This output is then piped into SoX where some filtering is performed:
sox --buffer 128 -t raw -r 12k -es -b 16 -c 1 - -p sinc 200-3.5k compand 0.1,0.8 6:0,-3
This looks a bit rough, but the first half of it is straight out of the
rtl_fm manpage and
simply describes the raw output format of the data coming from
rtl_fm. You might need to fiddle
with the buffer setting to get clean output; 128 worked well for me. Really this comes down to the
First we apply a 200Hz - 3.5KHz bandpass with
sinc 200-3.5k. We then do some
companding to balance the levels of different received signals; values from trial and error that
worked for me, see the SoX manpage for a complete description of the
compand filter. Without it
you’ll find that some transmissions are very loud and others very quiet, with loud squelch crashes.
Finally we handle the SoX pipe format (
tee >(sox -p <file.wav>) | play --buffer 128 -p
tee along with process substitution lets us
stdout to a command, which we use to have SoX write us a standard WAV file from a pipe input.
We then take the final
stdout into SoX’s play command to play the audio out the default sound
My experience playing around with this pipeline was very good. Despite the simplicity and “proof-of-concept” nature of the tool, with some good ‘ol UNIX elbow grease, it makes a powerful recording scanner. Since output bytes are only produced when the squelch is open, the resulting file has no (radio) silence recorded. Audio quality is excellent, and while I didn’t test it rigorously, the scanning speed is very good.
Here’s a demo (embedded below if your browser supports it of an hour or two of activity on the local parking enforcement dispatch channel (just a minute or so of recorded audio).
Some things that are missing, which I believe would require at the least some modifications to
rtl_fm if not a dedicated tool:
- Timestamping. You might be able to hack this together using negative values in
-tflag, which cause it to exit when the squelch closes. Combined with a
forloop and the
datecommand to generate a timestamped filename this might suffice for not-very-busy channels. However it will reinitialize the RTLSDR hardware each time which takes a few seconds, so you’re likely to miss some audio. Alternately you could use an external script to poll for filesize changes and write a log entry giving the cue point (easy to calculate for PCM) and real time for each transmission.
- Channel-stamping. As far as I can tell there’s no way to get
rtl_fmto tell you which frequency it’s listening to when the squelch is open. That’s pretty annoying and probably can’t be fixed without modifying
rtl_fm, though it would likely be a simple mod, which you could also use to timestamp squelch openings. Correlating them with save file cue points might be tricky.
- Mutli-channel. Since this is an SDR that can capture a couple MHz of bandwidth at once, there’s
the theoretical potential to monitor many channels at once and record them simultaneously. This
would require substantial modification to
rtl_fmor a new tool.
73 and have fun