[Guide] TwoToneDetect with RTL-SDR and local audio

Not open for further replies.


Feb 20, 2020
Uniden's newer scanner models have a feature called Fire Tone Out that will silently monitor a conventional analog frequency for two tone fire dispatches, and play the dispatch over the speaker when it hears a matching tone set. Since I haven't been able to find a scanner with this feature for a price I'm willing to pay, I decided to try to build one using a RTL-SDR, Raspberry Pi, rtl_airband and TwoToneDetect. This turned out to be WAY more of a challenge than I expected. And since I could not find an end-to-end guide, I figured I'd post how I managed it.

The end result of this configuration is a headless Raspberry Pi with a RTL-SDR stick that will listen to a single analog frequency for two tone fire dispatch calls. If it hears programmed tones, it will play the audio over the onboard headphone jack and record the audio. Once the call ends (channel is silent for 30 seconds) it will convert the recording to MP3 and send it to a Telegram group chat.

First, some random notes about this configuration:
  • Credit to the following wiki article. This is the magic that makes this setup possible: https://wiki.radioreference.com/index.php/RTL_SDR_multiple_FM_channels
  • My goal here was to create a standalone, single purpose device to handle this. I wanted it to be as hands-off and stable as possible. To do that, I set everything up to start automatically and run as a read-only system that will be resistant to SD card corruption.
  • There are a lot of moving pieces working together here. So, there are likely many ways to accomplish similar configurations. What I posted here is what works for me. If you have suggestions for better ways to do any of these steps, let me know.
  • This setup turned out to have several advantages over using a proper scanner
    • Cost: I happened to have a Raspberry Pi and RTL-SDR stick available. But even if you purchase them new, this setup will be far less expensive than any Uniden model that has the FTO feature.
    • Flexibility: Uniden's FTO feature only works on conventional analog channels. The fire dispatch system I wanted to work with does use that today, so this setup is similar. However, there's no reason this same approach can't be used with something like op25 to do this on digital or trunked networks.
    • Features: This setup allows me to send a recording to Telegram so I can hear it on my phone if I'm not near the speakers. TwoToneDetect also natively supports sending an email with the recording. To do the same with a normal scanner, you'd have to pair it up with something like ProScan.
  • I recommend using at least a Raspberry Pi 2 for this. I have tested a Pi 1, and it just barely work when it was overclocked. But since it only has a single core, it takes a LOT of tweaking to get it to run without the audio skipping.
  • Everything here is done as root. On Raspberry Pi 1, 2 and 3 RTL-Airband uses the FTT library for GPU acceleration so it must run as root. There are several ways to do that on Raspbian, but since my goal is to make a standalone appliance, I just enabled root logons and ran everything that way.
  • Since this setup uses a read-only root volume, I install TwoToneDetect to a USB stick. This is so that it can record audio to the "audio" subfolder that it automatically creates.
  • This post is based on the notes I took during my setup. If you try this config and find any errors, let me know and I'll correct them.
  • If you have any questions, post and I will try to answer.
Hardware needed
  • Raspberry Pi 2 or better
  • RTL-SDR stick (any SDR supported by rtl-airband would work, but you'd need to tweak the config)
  • External speakers for the Pi - This setup uses the 3.5mm jack on the Pi, so anything that plugs in there will work.
  • USB flash drive (mounted to the Pi at /mnt/usbdrive)
Software used
  1. Setup the Raspberry Pi with a standard Raspbian installation. I run it headless and access via SSH, but you could do it with a keyboard/monitor as well. If you want to use the Telegram/email functionality, it will need to have an Internet connection. If not, it can be totally standalone.
  2. Download RTL-Airband and install the prerequisites
    1. These are detailed in the Wiki on the GitHub page
  3. Setup PulseAudio
    1. PulseAudio will have been part of the RTL-Airband prereqs
    2. Convert the standard installation to system-wide mode. This is needed to allow the sinks/monitors to work across different user/logon contexts.
      1. GitHub - shivasiddharth/PulseAudio-System-Wide: Git to help you setup pulse audio as a system wide service. this has been tested and found to work on Raspberry Pi
      2. Note: When running in system mode, pacmd does not seem to work. I have not looked into this to see if there's a workaround.
    3. Add following lines to bottom of /etc/pulse/default.pa and system.pa (note only one of these is needed, but I haven't tested to figure out which). These will create a custom Pulseaudio sink/monitor to route the audio.
      1. load-module module-null-sink sink_name=ttdfreq1sink
      2. update-sink-proplist ttdfreq1sink device.description=ttdfreq1sink
      3. update-source-proplist ttdfreq1sink.monitor device.description=ttdfreq1monitor
    4. Use pacmd to get the name of the input and output sources (sinks and monitors). Run the following commands:
      1. systemctl stop pulseaudio
      2. pulseaudio -D
      3. pacmd list
        1. Note the name of the TTD sink. It should be the same that you created in the step above (ttdfreq1sink)
        2. Note the name of the built in hardware output. It should be something like alsa_output.platform-bcm2835_audio.analog-stereo
      4. pulseaudio -kill
      5. systemctl start pulseaudio
  4. Setup RTL-Airband
    1. Download it (if you didn't in the step above)
    2. Compile it using the directions in the GitHub wiki
      1. Use the following two options during compile:
        1. -DNFM=ON
        2. -DPLATFORM=rpiv1 (Adjust for whichever Pi model being used. Note special directions for rpi 4)
    3. Install using make install directions from wiki
    4. Configure as a system service using directions from wiki
    5. Create folder /etc/rtl-airband
    6. Create a new config file in /etc/rtl-airband (see example below)
      1. Be sure to use the pulse output type and define the ttdfreq1sink from above
    7. Edit the /etc/systemd/system/rtl_airband.service file to use the new config file (example rtl_airband.service file below)
    8. Enable the rtl-airband service
      1. systemctl enable rtl_airband
      2. Optional: systemctl start rtl_airband
  5. Setup TwoToneDetect (TTD)
    1. Mount a USB stick to the Pi. I formatted mine as ext3 just to prevent permission issues. My stick is mounted at /mnt/usbdrive. If you use a different path, you will need to modify the twotonedetect.service file to match.
    2. Download TTD from the email link and extract to the USB stick. It should create a folder called TTD.
    3. Setup config.cfg file and tones.cfg file per the PDF documentation included with TTD. Example config files are below.
      1. Note: The examples below are configured to send notifications and recordings to Telegram via a Telegram Bot.
        1. If you do not want to use this functionality comment out or delete the alert_command and post_email_command lines from tones.cfg
        2. If you do want to use this functionality:
          1. Create a Telegram bot, record the Bot's API token/key
            1. Bots: An introduction for developers
            2. How to create a Telegram Chatbot | SendPulse
          2. Create a new chat group and invite your new bot
          3. Invite @GetIDsBot to the chat group. It will immediately respond with a message including the group chat ID, record this.
          4. Replace <BotToken> and <ChatID> in the tones.cfg Telegram commands with your real IDs.
        3. Run TTD to test that it is working correctly
          1. cd /mnt/usbdrive/TTD
          2. PULSE_SOURCE=ttdfreq1sink.monitor PULSE_SINK=alsa_output.platform-bcm2835_audio.analog-stereo ./TwoToneDetect73l
        4. Setup TTD as a service
          1. Create file /etc/systemd/system/twotonedetect.service, example below
            1. Note the current version is 73l. If you download a newer version, the command in this file needs to be updated to match.
          2. systemctl enable twotonedetect
          3. Optional: systemctl start twotonedetect
  6. Optional: Set the root file system to readonly. This will help prevent SD card corruption if the Pi is unplugged without being shutdown properly. I don't want to have to SSH in and shutdown every time I unplug this setup. Note that with this enabled, any changes you make to the Pi (except for the contents of the USB stick) will not be saved at reboot.
    1. raspi-config
    2. Performance Options > Overlay File System > Yes
    3. Reboot
    4. Repeat, choosing No if you want to unlock the file system to make changes or update
Troubleshooting Tips
  • If you comment out the "sink" line in the rtl-airband config file, it will play over the 3.5mm output. That will allow you to hear the actual broadcast to troubleshoot any reception/rtl-airband issues.
  • The LevelMeter program included with TTD can be used to troubleshoot audio input/output issues. Use the following command to launch it, then select the PulseAudio inputs and outputs. These should both be number 2. If they show up on a different number, adjust the TTD config.cfg file to match. Once its running, the numbers on the console will change when it recieves audio. And it should send that same audio to the output device (speakers).
    • PULSE_SOURCE=ttdfreq1sink.monitor PULSE_SINK=alsa_output.platform-bcm2835_audio.analog-stereo ./LevelMeter
Sample Files
Description=SDR AM/NFM demodulator

ExecStart=/usr/local/bin/rtl_airband -Fe -c /etc/rtl-airband/rtl-airband-aacfd.conf
# The program may exit only due to startup failure (eg. misconfiguration)
# or due to failure of all SDR devices (eg. disconnection). In either case,
# there is no point to restart it, because it would fail once again.



Description=TwoToneDetect for fire tone out paging



  type = "rtlsdr";
  index = 0;
  gain = 25;
  centerfreq = 154.21;
  correction = 0;
  sample_rate = 1.2;
      freq = 154.01;
      modulation = "nfm";
      bandwidth = 5000;
      outputs: (
           type = "pulse";
           sink = "ttdfreq1sink";

TwoToneDetect config.cfg - Note, TTD gets weird if you delete some of the unused values
email_user = emailuser@address.com
email_pwd = cGFzc3dvcmQ=
email_server = localhost.local
email_port = 587
input_device_index = 2
output_device_index = 2
audio_threshold = 54
tone_offset = 0
mp3_bitrate = 32000
BCC = 0
email_priority = 3
audio_channel = mono
email_from = emailuser@address.com
instance_id = none
email_security = STARTTLS
email_authentication = 1
email_send_sequential = none
upload_ftp_server = ftp.yourserver.net
upload_ftp_port = 21
upload_ftp_username =
upload_ftp_password =
upload_file_prefix = http://www.example.come/audio/
upload_ftp_remote_path = /audio/
copy_from_email = 0
start_headless = 1
allow_remote_access = 1
gui_remote_access_port = 8080

TwoToneDetect tones.cfg
Atone = 669.9
Btone = 617.4
Atonelength = 1
Btonelength = 1
Description = St. 2
record_delay = 0
record_seconds = 0
release_time = 30
ignore_after = 10
playback_during_record = 1
alert_command = curl "https://api.telegram.org/bot<BotToken>/sendMessage?chat_id=<ChatID>&" --data-urlencode "text=Dispatch for [d]"
post_email_command = curl -F document=@"[mp3]" https://api.telegram.org/bot<BotToken>/sendDocument?chat_id=<ChatID>

Atone = 669.9
Btone = 651.9
Atonelength = 1
Btonelength = 1
Description = St. 3
record_delay = 0
record_seconds = 15
release_time = 30
ignore_after = 10
playback_during_record = 1
alert_command = curl "https://api.telegram.org/bot<BotToken>/sendMessage?chat_id=<ChatID>&" --data-urlencode "text=Dispatch for [d]"
post_email_command = curl -F document=@"[mp3]" https://api.telegram.org/bot<BotToken>/sendDocument?chat_id=<ChatID>
Not open for further replies.