A pure-Python FLEX paging protocol decoder library. It consumes
FM-demodulated audio at 22050 Hz (mono float32, e.g. the output of
rtl_fm -M fm -s 22050) and emits decoded pager messages.
The decoder is heavily based on
multimon-ng's
demod_flex.c — the symbol recovery, sync detection, deinterleaving,
and message parsing logic were ported from that C implementation. This
port fixes several bugs that affect real-world message recovery (see
below) and packages the decoder as a reusable Python library.
from flexdec import FlexDecoder
decoder = FlexDecoder(sample_rate=22050)
# feed() accepts any iterable of float samples in [-1, 1]
for msg in decoder.feed(audio_samples):
print(f"[{msg.baud}/{msg.levels}] capcode={msg.capcode} "
f"type={msg.kind} body={msg.body}")
# Decoder state is available at any time
print(decoder.stats.locked, decoder.stats.frames_decoded)feed() returns a list of FlexMessage objects. You can also pass
a callback via on_message= to receive messages as they are decoded.
Each FlexMessage contains: capcode, kind ("ALN", "NUM",
"TON", "UNK"), body, baud, levels, phase, cycleno,
frameno, frag_flag, and BCH error counts.
pip install git+https://github.com/lmore377-aiexp/flexdec.gitRequires Python >= 3.11 and numpy >= 1.26. For development (tests):
pip install "flexdec[dev] @ git+https://github.com/lmore377-aiexp/flexdec.git"| Sync code | Baud | Levels |
|---|---|---|
| 0x870C | 1600 | 2 |
| 0xB068 | 1600 | 4 |
| 0x7B18 | 3200 | 2 |
| 0xDEA0 | 3200 | 4 |
| 0x4C7C | 3200 | 4 |
The decoder has been tested off-air against multimon-ng in 3200/4 mode.
This port fixes several bugs in multimon-ng's demod_flex.c:
-
Early return on BCH failure (high severity) -- multimon-ng abandons an entire phase on the first uncorrectable codeword, silently truncating all remaining messages in that phase. flexdec marks bad codewords individually and decodes everything else.
-
cycleno++mutation (medium) -- a post-increment side effect corrupts the FIW cycle number mid-frame, producing wrong cycle/frame labels on emitted messages. -
Long-address capcodes are wrong (medium) -- the two-word long-address path is commented out upstream; capcodes above 0x1E0000 are decoded incorrectly by both decoders (fix is TODO here too).
-
Lock flag never clears (medium) -- once locked, multimon-ng stays "locked" forever even on pure noise. flexdec adds a watchdog that drops the lock after ~4 frame periods without a valid FIW.
-
Aggressive idle detection (low) -- bailing on the first all-zero codeword can theoretically lose binary-payload messages.
Full details with code examples and suggested upstream patches are in UPSTREAM_BUGS.md. (Note: this was written entirely by Claude and this doc might not actually be accurate. I still need to test the stuff in there)
This library is licensed under the GNU General Public License v3.0 or
later (GPL-3.0-or-later), as it is derived from multimon-ng's
GPL-v3-licensed demod_flex.c.
The BCH error-correction module (bch.py) is ported from multimon-ng's
bch.c, which is released into the public domain (Unlicense).