ccemu

P25 Control Channel Emulator

ccemu — P25 Phase 1 Control Channel Emulator

A virtual P25 Phase 1 trunked system control channel (TSCC) transmitter for the HackRF One SDR. Generates a fully standards-compliant RF signal that real P25 scanners and decoders (DSD-FME, OP25, hardware scanners) can lock onto and follow.

What it does

ccemu emulates the control channel of a P25 Phase 1 trunked radio system. It continuously broadcasts system identity packets and simulated activity events so that a listening scanner sees a live-looking trunked system — complete with unit registrations, group affiliations, and voice channel grants.

The RF output is a standard C4FM/FDMA signal at any configurable frequency. No real voice traffic is generated; only the control channel layer is emulated.

Hardware required

  • HackRF One (or compatible)
  • Appropriate antenna for your chosen frequency
  • A Linux host with the HackRF libraries installed

Prerequisites

sudo apt install hackrf libhackrf-dev

Go 1.21 or later is required to build.

Build

git clone https://github.com/SarahRoseLives/ccemu
cd ccemu
go build -o ccemu .

Usage

./ccemu [flags]
Flag Default Description
-freq 145050000 TX centre frequency in Hz
-gain 20 TX VGA gain in dB (0–47)
-amp false Enable the HackRF RF amplifier
-nac 0x293 Network Access Code (12-bit)
-wacn 0xBEEF0 Wide Area Communications Network ID (20-bit)
-sysid 0x001 System ID (12-bit)
-rfssid 0x01 RF Subsystem ID (8-bit)
-siteid 0x01 Site ID (8-bit)
-vchan 1 Voice channel number used in grant messages
-nosim false Disable activity simulation (system broadcasts only)

Example

# Transmit on 145.050 MHz with default virtual system parameters
./ccemu -freq 145050000 -gain 25

# Custom system identity
./ccemu -freq 460125000 -nac 0x123 -wacn 0xABCDE -sysid 0x042 -gain 30

Decoding the output

DSD-FME (from audio / RTL-SDR)

rtl_fm -f 145050000 -s 48000 -g 30 | dsd-fme -fi -i /dev/stdin -nX -P 0 -V 0

OP25 (direct loopback test without RF)

# Pipe ccemu IQ directly into OP25's rx.py (bypasses RF entirely)
./ccemu ... | op25/op25/gr-op25_repeater/apps/rx.py ...

Hardware scanners

Program the control channel frequency with NAC matching the -nac flag. The scanner should identify the system and begin following grants.

How it works

The encoding stack follows TIA-102.BAAA-A and TIA-102.AABC-A exactly:

TSBK PDU (96 bits)
  └─ CRC-CCITT (direct form, poly 0x1021, init 0, final XOR 0xFFFF)
  └─ 1/2-rate trellis encode (49 input → 98 output dibits)
  └─ P25 data interleave
  └─ Prepend: frame sync (48-bit) + BCH(64,16,23) NID
  └─ Insert status symbols (every 35 data dibits)
  └─ C4FM modulation (polyphase RRC, α=0.2, 4800 baud, ±600/1800 Hz deviation)
  └─ HackRF TX at 2.4 Msps (signed int8 IQ)

System broadcast cycle

Every 4th frame is a system broadcast, cycling through:

  1. IDEN_UP — channel frequency plan (bandwidth, spacing, base frequency)
  2. NET_STS — network status (WACN, SYSID, control channel)
  3. RFSS_STS — RF subsystem status (RFSS ID, site ID)
  4. ADJ_STS — adjacent site advertisement

Activity simulation

Between system broadcasts, the simulator generates realistic traffic:

  • On startup: all virtual units register (U_REG_RSP), locate (LOC_REG_RSP), and affiliate (GRP_AFF_RSP)
  • Continuously: random voice grants (GRP_VCH_GRANT) with 500 ms update heartbeats (GRP_VCH_GRANT_UPDT)
  • Occasionally: re-affiliations and re-registrations between calls

6 virtual units across 5 talkgroups are used by default.

Project structure

main.go            — HackRF setup, ring buffer TX, simulation goroutine
p25/
  bch.go           — BCH(64,16,23) NID encoder (precomputed generator matrix)
  crc.go           — CRC-CCITT direct form over 80 TSBK PDU bits
  trellis.go       — 1/2-rate trellis encoder + data interleave
  frame.go         — Frame sync, NID, status symbol insertion, frame assembly
  tsbk.go          — TSBK PDU builders for all supported opcodes
dsp/
  c4fm.go          — C4FM modulator (polyphase RRC upsampling, FM integration)
hackrf-fork/       — Local fork of go-hackrf with TX callback fix

License

This project is intended for amateur radio experimentation and research on frequencies you are licensed to use. Transmitting on public safety or commercial P25 frequencies is illegal without proper authorization.