POCSAG and pager traffic

With the recent rise in COVID-19 activities, I decided it was time to resurrect my page sniffer.

If you don’t know about POCSAG, you can start by reading the wikipedia article here.

In a nutshell, you can use RTLSDR to listen to broadcast radio frequencies with a cheap-ish USB dongle and standard antenna. Once you receive that data, you can parse it through different types of software to receive and decode that data. This type of thing can be used for anything from tracking airplanes to receiving images from weather satellites, or many other things.

For this project, I’m going to listen to pager frequencies, then decode the POCSAG specific ones. There’s a pretty good video overview here.

First, you want to get the USB dongle and a standard telescoping antenna.
Next, you want to decide what hardware/software to connect the dongle to and monitor/decode the information. Options include:
– Windows
– Linux
– MacOS
– Raspberry Pi

Windows/Linux/Mac seem to work the best for actively tuning through the frequencies, altho I haven’t had any luck getting the decoder to receive and pipe the audio. Raspberry Pi works well for a stand-alone device to just receive and decode the audio, but doesn’t seem to have enough horsepower to manually scan/display the realtime signal. Something to check out later, since some people have reported being able to get it to work.

Software list:
– GQRX: cross platform software for receiving and visualizing the waveform of signals
– rtlsdr: command line software for receiving signals from the realtech tuners
– multimon-ng: command line software for decoding different formats of pager signals, at least in the US, we’re mostly doing POCSAG

What you’ll want to do first is use GQRX to hunt down the frequency that you want – similar to the video overview above. Once you’ve found the right frequency – you can try to decode on that same device or move on to building the standalone receiver (Pi).

Since my laptop is a Mac, I installed GQRX using MacPorts. I was able to hunt and find a POCSAG signal around 152.112.9Hz. However, I wasn’t able to use the netcat | sox | multimon-ng command to get any decoding to work. Using tcpdump locally, I’m not seeing any of the traffic showing up. I’m not sure if this is an issue with GQRX not sending the traffic, or netcat not receiving it properly.

However, when I put that frequency into my Raspberry Pi, I immediately started receiving messages. Command line:

rtl_fm -A fast -f 152.112M -M fm -s 22050 -g 44.5 -l 200 | multimon-ng -n -v1 -p -t raw -a POCSAG1200 -a POCSAG512 -a POCSAG2400 -f alpha /dev/stdin

RTL Syntax:
rtl_fm :the tuning software for the RTL dongle
- A fast : fast decoding algorythm
- f 152.112M: frequency that I’m tuning to pick up the page signal
-M fm: use FM frequency modulation
-s 22050 : not sure, size? required to feed the data to multimon-ng
-g 44.5 : gain settings
-l 200: the squelch, or signal must be at this level to be accepted

Multimon Syntax:
-t raw
-a POCSAG1200 -a POCSAG512 -a POCSAG2400 : selected decoding protocols, I reduced this down to only POCSAG1200 to get less noise after I got it working
-f alpha /dev/stdin

This works great for command line, but maybe you want to not just watch it, but have it spit out to a file, plus let’s drop anything without the “alpha” tag to reduce some more noise, and add frequency and time stamps, so lets wrap it in python. I found a bunch of this, modified it, etc. In any case, here’s what I ended up w/ python.pocsag.parser.2.py



!! This requires a recent build of Multimon-NG as the old builds wont accept a piped input !!

Change the rtl_fm string to suit your needs.. add -a POCSAG512 , 2400 etc if needed to the Multimon-ng string
This just prints and writes to a file, you can put it in a threaded class and pass though a queue or whatever suits your needs.

import time
import sys
import subprocess
import os

def curtime():
    return time.strftime("%H:%M:%S %Y-%m-%d")

def date():
    return time.strftime("%Y-%m-%d")

with open('error.txt','a') as file:
    file.write(('#' * 20) + '\n' + curtime() + '\n')

multimon_ng = subprocess.Popen("/usr/bin/rtl_fm -A fast -f 152.112M -M fm -s 22050 -g 44.5 -l 0 | /usr/bin/multimon-ng -a POCSAG1200 -f alpha -t raw -",

    while True:
        line = multimon_ng.stdout.readline()
        if line.__contains__("Alpha:"):    # filter out only the alpha
            if line.startswith('POCSAG'):
                address = line[22:28].replace(" ", "").zfill(7)
                message = line.split('Alpha:   ')[1].strip().rstrip('<ETB>').strip()
                output=(address+' '+frequency+' '+curtime()+' '+ message+'\n')
                print address, frequency, curtime(), message
                with open('/vol1/pager/%s.txt' % date(),'a') as f:
        if not "Alpha:" in line:
            with open("missed.txt","a") as missed:
                missed.write(line + '\n')

except KeyboardInterrupt:
    os.kill(multimon_ng.pid, 9)

Basically, this runs the rtl_fm/multimon string and pulls outputs the details we want, adds the timestamp, and writes it to a file. I’m having it use one file per day, and put it some place where I can call it from a web server. Simple php webserver parser to only print alpha numeric contents from the file… index.html

// set the default timezone to use. Available since PHP 5.1
$today = date('Y-m-d');
echo "Realtime Pager log, currently showing $today";
echo "<br><cr><br><br>";
echo date(DATE_ATOM, mktime(0, 0, 0, 7, 1, 2000));

$array = file("/vol1/pager/$today.txt");
foreach($array as $line)
        $output=preg_replace("/[^[:alnum:][:space:]@.:-]/u", '', $line);
           echo $output;
           echo "<br>";


I also have a cron to restart the pocsag parser python code at midnight each day.

Future work: I’m frustrated that I can’t get the network multimon-ng working, so going to try to setup a listener on a Centos vm.

To install multimon-ng on Centos, you need to install the PulseAudio libraries, so update your process to:

git clone https://github.com/EliasOenal/multimon-ng.git
cd multimon-ng
mkdir build
sudo yum install pulseaudio-libs-devel
cmake ..
sudo make install

Install (if not already done) netcat and sox

yum install netcat sox

Update firewall rules on centos host to allow udp 7355 to that VM

nc -l -u -p 7355 | sox -t raw -esigned-integer -b 16 -r 48000 - -esigned-integer -b 16 -r 22050 -t raw - | multimon-ng -t raw -a POCSAG512 -a POCSAG1200 -a POCSAG2400 -f alpha -

-l : listen
-u : UDP
-p 7355 :listen on port 7355

Which totally works.

This entry was posted in Misc and tagged , , , . Bookmark the permalink.

Leave a Reply