FLEX Pager Codec
Encode and decode Motorola FLEX paging messages in audio WAV format. FLEX is the dominant high-capacity paging protocol that succeeded POCSAG on commercial networks, supporting up to ~600,000 pagers per channel through tightly time- slotted frame structure.
Triggers
FLEX, Motorola FLEX, ReFLEX, FLEX paging, FLEX decoder, FLEX encoder, alphanumeric pager network, commercial paging, SkyTel, PageNet, Arch Wireless, FLEX capcode, FLEX SDR, FLEX WAV, high-capacity paging, paging carrier, FLEX 1600, frame information word, FIW, BS1 sync
What it does
- Encode: Build FLEX transmissions in any of the four standard modes (1600/2, 3200/2, 3200/4, 6400/4) containing one or more pager messages. Each message has a capcode (1..2,097,151), a type (alphanumeric, numeric, or tone), and optional text payload.
- Decode: Recover messages from a WAV recording (e.g. SDR capture). The decoder auto-detects the mode by trying each one's A1 sync codeword in turn, also tries inverted polarity, parses the FIW, then walks the address/vector/ message records.
- Error correction: BCH(31,21) corrects up to 2 bit errors per codeword. An additional even-parity bit on each 32-bit codeword catches further errors.
- 4-FSK Gray coding: 4-level modes pack 2 bits per symbol via Gray code (00→lowest tone, 01, 11, 10→highest tone) so a one-step symbol slip produces only a one-bit error.
Modes
| Mode | Raw rate | Symbol rate | Levels | A1 sync | Tones (Hz) |
|---|---|---|---|---|---|
| 1600/2 | 1600 bps | 1600 | 2-FSK | 0x870C78F3 | 1200 / 2400 |
| 3200/2 | 3200 bps | 3200 | 2-FSK | 0xB068784B | 2400 / 4800 |
| 3200/4 | 3200 bps | 1600 | 4-FSK | 0xDEA0CC1E | 800 / 1600 / 2400 / 3200 |
| 6400/4 | 6400 bps | 3200 | 4-FSK | 0x4F73A8C7 | 1600 / 3200 / 4800 / 6400 |
All modes use 48 kHz sample rate, giving an integer samples-per-symbol count. Tone spacing is at least half the symbol rate so adjacent tones stay discriminable by the per-symbol energy detector.
Usage
Decode a FLEX recording (mode auto-detected)
python3 scripts/flex_decode.py recording.wav
Output starts with the detected mode and FIW, then lists each message
(capcode, type, error count, text). Force a specific mode with
--mode 3200/4. Add --json for machine-readable output.
Encode a single message
python3 scripts/flex_encode.py out.wav --capcode 100200 --type alpha \
--text "CALL DR SMITH STAT" --mode 1600/2
--mode accepts 1600/2, 3200/2, 3200/4, or 6400/4.
Encode multiple messages from JSON
Create messages.json:
[
{"capcode": 100200, "type": "alpha", "text": "CALL DR SMITH STAT"},
{"capcode": 555, "type": "numeric", "text": "911-555-1234"},
{"capcode": 12345, "type": "tone"}
]
Then:
python3 scripts/flex_encode.py out.wav --json messages.json \
--mode 6400/4 --cycle 3 --frame 42
Run the test suite
python3 scripts/flex_test.py
What is FLEX?
FLEX is Motorola's synchronous paging protocol, standardized as TIA-103-A. A FLEX channel is divided into 4-minute cycles of 128 frames, each frame exactly 1.875 s long. Within each frame the carrier transmits a BS1 bit-sync preamble, an A1 sync codeword that announces the speed/level (1600/3200/6400 bps, 2-level or 4-level FSK), an inverted A2, a Frame Information Word, then 11 blocks of 8 codewords each. Every 32-bit codeword is a BCH(31,21) codeword plus an even-parity bit, giving robust correction for up to 2 errors per codeword.
This implementation supports all four standard modes. 4-FSK uses Gray-coded bit pairs so adjacent symbols differ in only one bit, minimizing the bit-error penalty when an energy detector picks the wrong-but-neighboring tone. The on-the-wire codeword encoding (BCH + parity), BS1 preamble (0xA6C6AAAA), capcodes, vector words, and message types match the real protocol. The frame layout is simplified to a single contiguous record list rather than the 11-block fixed structure, so a test transmission roundtrips end-to-end without needing precise 1.875-s timing.
Files
scripts/flex_common.py- BCH(31,21), sync constants, framing primitives, message encoding, FSK modulation/demodulation, WAV I/O.scripts/flex_encode.py- CLI encoder.scripts/flex_decode.py- CLI decoder.scripts/flex_test.py- Self-tests covering BCH error correction, field packers, message codecs, and full WAV roundtrip.
Notes / scope
- Long capcodes (which use a 2-codeword address) are not implemented; short capcodes cover 0..2,097,151 which is sufficient for most demonstrations.
- ReFLEX (the two-way variant) shares the downlink format but adds an uplink protocol that is out of scope for an audio codec.
- The single-frame contiguous record layout is a simplification of the real 11-block / 88-codeword fixed frame; live FLEX captures use a fixed timing structure that this codec does not parse.