VE7XEN's ham radio and electronics blog

Recording NBFM Scanner with RTLSDR

| Comments

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, it’s 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_fm tool 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 -f options 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 guide.

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 two filters.

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 (-p) output:

tee >(sox -p <file.wav>) | play --buffer 128 -p

tee along with process substitution lets us copy 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 device.


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 rtl_fm’s -t flag, which cause it to exit when the squelch closes. Combined with a for loop and the date command 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_fm to 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_fm or a new tool.

73 and have fun

Comments