Field Guide · term

Also known as: Phase 2 frame sync, P25P2 frame sync magic, outbound sync word

The P25 Phase 2 sync word is the fixed 20-dibit (40-bit) pattern that opens a Phase 2 sub-frame and tells the decoder where a burst begins in the H-DQPSK dibit stream.1 Two patterns exist because the link is TDMA and two-directional: the outbound (base → subscriber) sync is 0x575D57F7FF and the inbound (subscriber → base) sync is 0xDFF57D75DF5D, so a receiver locks onto the correct side of the link.2 The outbound value is the authoritative TIA-102.BBAC frame-sync magic, the same constant OP25 and SDRTrunk carry.

slicer output 0x565956A6AA canonical 0x575D57F7FF [0,1,3,2] remap fixes dibit labels 2 ↔ 3 (the two negative-phase quadrants) for the WHOLE stream sync · 20 dibits ISCH MAC / voice payload a sliding detector matches within a dibit-mismatch tolerance to anchor the sub-frame grid
The differential slicer emits the sync 2↔3 transposed; a fixed [0,1,3,2] remap restores the standard value so the detector, ISCH and MAC FEC all match the same canonical bits.

The dibit remap

Phase 2 rides a differentially decoded constellation, and the quadrant slicer GopherTrunk uses assigns the two negative-phase symbols the dibit values 3 and 2 where the TIA-102 dibit convention uses 2 and 3. The consequence is subtle: the raw slicer output for genuine air is the true bits with 2 and 3 transposed, so the standard sync 0x575D57F7FF comes off the wire reading 0x565956A6AA. Rather than matching the transposed constant — which would lock sync but leave the whole MAC and voice payload swapped, so fields like ALGID/KID never populate — the Phase 2 receiver applies a fixed [0,1,3,2] remap (canonicalDibitRemap, mirroring the verified Phase 1 CQPSK remap from issue #492) to every dibit before emitting it. After the remap the sync detector, the ISCH decode, and the MAC FEC all match against the authoritative canonical values, and the transpose is corrected once for the entire chain.

A historical bug is instructive: before this was understood, the outbound constant was a garbled 48-bit value (0x575F7DFF77FF) that was neither the standard nor its transpose. It never matched real air, so Phase 2 superframes never locked — yet every synthesized round-trip test passed, because the test encoder used the same wrong constant on both ends (a self-consistent bug). The fix pins the sync to exactly 20 dibits (40 bits) from the low bits of the constant.

Sync search on the TDMA slots

SyncDetector slides a 20-dibit window over the demodulated stream and reports each absolute dibit index where the pattern matches within a configurable tolerance of dibit mismatches (the Phase 2 control path uses a tolerance of 2). This is the same detector shape as the Phase 1, DMR and NXDN sync searches, so the higher-level state machine stays uniform across protocols. Every hit anchors a sub-frame: the superframe decoder places its 0..11 sub-frame grid on a sync match, then reads the ISCH that immediately follows to learn whether the sub-frame carries voice or a MAC PDU. Because the outbound sync recurs on a fixed cadence within the superframe, a stable lock lets the decoder track both TDMA slots without re-hunting.

The inbound sync is used only by a diagnostic detector and is not yet verified against real uplink captures or this decoder’s dibit convention — GopherTrunk does not rely on it for decode.

Relevance to SDR

internal/radio/p25/phase2/sync.go holds the canonical outbound and inbound constants, OutboundSyncDibits/InboundSyncDibits materialisers, and the SyncDetector. Getting both the constant and the [0,1,3,2] remap right is what turns a locked-but-garbled Phase 2 channel into one whose MAC opcodes and voice actually decode; the sync word is the landmark everything downstream is sliced relative to. The spec is TIA-102.BBAC.

Sources

  1. Project 25 — Wikipedia, on P25 Phase 2 framing. 

  2. Frame synchronization — Wikipedia, on locating frame boundaries with a fixed sync sequence. 

See also