Also known as: CAC, common access channel, RCCH
The NXDN Common Access Channel (CAC) is the signalling block an NXDN control channel transmits to coordinate a trunked system — the RCCH messages that announce the site, register radios, and grant voice and data calls onto traffic channels.1 GopherTrunk models it at two layers: a message layer, an 8-bit RCCH opcode plus a 64-bit payload protected by a 16-bit CRC (88 bits total), and a channel-coding layer, the full outbound chain that wraps a 155-bit information block in convolutional FEC, puncturing, and interleaving to survive the air.2
RCCH message types
The 8-bit opcode that opens the message names the RCCH function. GopherTrunk enumerates the subset the trunking state machine acts on:
| Opcode | Name | Purpose |
|---|---|---|
0x01 |
VCALL | voice call setup |
0x02 |
VCALL_ACK | voice call acknowledgement |
0x04 |
VCALL_ASSGN | voice channel assignment (grant) |
0x09 |
DCALL | data call setup |
0x0A |
DCALL_ACK | data call acknowledgement |
0x0D |
DCALL_ASSGN | data channel assignment |
0x38 |
SDCALL | short-data call |
0x3C |
SITE_INFO | site identification broadcast |
0x3D |
SRV_INFO | service information |
0x3F |
CCH | control-channel announcement |
A VCALL_ASSGN or DCALL_ASSGN is the channel grant the engine follows to a traffic channel; SITE_INFO tells it which system and site it is watching. The message payload interpretation depends on the opcode — GopherTrunk parses the VCALL and SITE_INFO variants into typed group/source/system fields.
The coding chain
The channel-coding layer takes a 155-bit information block (8 SR bits plus 144 layer-3 data
bits, padded with 3 null zeros), appends a 16-bit CRC-CCITT (polynomial 0x1021, init
0xFFFF, computed bit-level because 155 is not byte-aligned) and 4 zero tail bits to flush the
encoder, giving 175 input bits. Those are encoded by a constraint-length-5, rate-½
convolutional code — generators g1 = 1+D³+D⁴ (octal 31) and g2 = 1+D+D²+D⁴ (octal 27),
the same primitive the SACCH uses — producing 350 bits. A fixed
puncture matrix of period 7 (keep every G1, drop G2 at two of seven positions) removes 50 bits
to land on 300, and a 25-row by 12-column block interleaver — written row-by-row, read
column-by-column — spreads adjacent bits apart so a channel burst becomes scattered single
errors. The result is 300 channel bits, 150 dibits, carried in the CAC slot of the RCCH
outbound frame (FSW 20 + LICH 16 + CAC 300 + E 24 + Post 24).
The receiver runs the exact inverse: deinterleave, depuncture (inserting a zero-cost sentinel at the 50 dropped positions so the metric ignores them), then a Viterbi decode over 175 stages constrained to end in state 0, strip the tail, and verify the CRC. A CRC match is the gate — only a clean CAC is ingested.
Relevance to SDR
internal/radio/nxdn/cac.go holds the message layer (ParseCAC, the RCCHType opcode enum,
and typed payload parsers) and internal/radio/nxdn/cac_channel.go holds the coding chain
(EncodeCACChannel / DecodeCACChannel) with the puncture positions and interleave
permutation computed and self-checked at package load. Getting the CAC right is what lets
GopherTrunk read an NXDN control channel at all: every grant it follows and every site it
identifies arrives as a CAC message, so the FEC chain — convolution, puncture, interleave,
Viterbi — sits directly on the critical path between a noisy control channel and a decoded
call.
Sources
-
NXDN — Wikipedia, on the NXDN standard and its control-channel signalling. ↩
-
Convolutional code — Wikipedia, on the encoder family the CAC uses and its Viterbi decoding. ↩