Field Guide · algorithm

Also known as: IMBE FEC, IMBE 4400 channel coding, u_0..u_7 vectors

IMBE channel coding is the forward error correction layer that turns the 88 information bits an IMBE vocoder emits every 20 ms into the 144 channel bits carried in each P25 Phase 1 voice subframe.1 It is unequal protection: the perceptually critical bits get a strong Golay(23,12) code, the mid-importance bits a lighter Hamming(15,11) code, and the least-sensitive bits ride bare. This graceful-degradation design (TIA-102.BABA §7.3) is why a weak P25 signal warbles rather than dropping to silence.

u0Golay 23 u1Golay 23 u2Golay 23 u3Golay 23 u4Ham 15 u5Ham 15 u6Ham 15 u77 88 info bits (12·4 + 11·3 + 7) → 144 channel bits
Eight vectors carry the 88 IMBE bits: u0–u3 in Golay(23,12) codewords, u4–u6 in Hamming(15,11) codewords, and u7 unprotected — 144 channel bits in all.

How it works

The 88 bits are grouped into eight vectors named u_0 through u_7. The four most significant vectors carry 12 bits each and are encoded as Golay(23,12,7) codewords — 23 bits able to correct up to 3 errors. The next three carry 11 bits each as Hamming(15,11,3) codewords — 15 bits correcting a single error. The last vector, u_7, is 7 bits with no coding at all, because it holds the least perceptually sensitive spectral detail. The arithmetic closes exactly: 12×4 + 11×3 + 7 = 88 information bits become 23×4 + 15×3 + 7 = 144 channel bits.

Vector Info bits Channel bits Code
u_0 … u_3 12 each 23 each Golay(23,12,7)
u_4 … u_6 11 each 15 each Hamming(15,11,3)
u_7 7 7 none

GopherTrunk decodes each vector by nearest-codeword search over a precomputed table (4096 Golay codewords, 2048 Hamming codewords), which for errors inside the correction radius returns the unique correct data and a corrected-error count. u_0 matters most: its Golay data seeds the descrambler, so the decoder corrects u_0 first, before it can trust any of u_1u_6. The generator itself is a fixed table — each of the 12 Golay data bits contributes a fixed 11-bit parity pattern, XOR-accumulated:

// 11-bit parity contribution of each Golay(23,12) data bit, MSB-first.
var golayGenerator = [12]uint16{
    0x63a, 0x31d, 0x7b4, 0x3da, 0x1ed, 0x6cc,
    0x366, 0x1b3, 0x6e3, 0x54b, 0x49f, 0x475,
}

Two different Golay codes in one repo

A subtle trap sits under this page. GopherTrunk carries two unrelated Golay/Hamming implementations, and they are not interchangeable. The internal/radio/framing package holds systematic Golay(24,12) and Hamming(15,11) codes used for P25 and DMR link control framing. The vocoder uses the separate internal/voice/imbe/p25fec.go code, transcribed from mbelib’s ecc.c in the exact bit order real IMBE transmitters use. Although framing’s Golay generator list equals golayGenerator shifted by one bit, the two associate generator rows with data bits in opposite order, so they are different codes in practice: a clean real-air IMBE codeword decodes to the wrong data under framing’s Golay. That mismatch was the root of GopherTrunk issue #489, verified against a real P25 voice capture and the mbelib reference decoder. When touching this path, never reach for the framing package’s codec — the vocoder needs the mbelib-order one, and only that one.

Relevance to SDR

Channel coding is the first thing GopherTrunk’s IMBE receiver runs on each 144-bit subframe, after the interleaver and scrambler layers have been undone. The corrected-error counts it returns feed the frame-repeat logic upstream: an uncorrectable vector marks a bad frame that the synthesizer replays and fades rather than voicing. Each P25 Phase 1 LDU carries nine such subframes, so getting the Golay and Hamming math bit-exact — in the mbelib order, not the framing order — is what lets real off-air voice decode at all.

Sources

  1. Multi-Band Excitation — Wikipedia, on the IMBE vocoder family and its role in P25 Phase 1. 

See also