DMR Tier III trunking: plan
Status: plan, 2026-10-10. The only built part is a host-side prototype of the PDU coding
(tests/host/tier3ProtoTest.c, section 7 WP T3-0). This page describes ETSI Tier III support in RadioFoundry: what it needs from
each layer, what the radios can do, the IP position, the test strategy and the work packages.
The sources are public and free from etsi.org. Clause numbers below refer to these versions:
| Short name | Document | Version |
|---|---|---|
| -1 | ETSI TS 102 361-1, DMR air interface (AI) protocol | V2.7.1 (2026-05) |
| -2 | ETSI TS 102 361-2, DMR voice and generic services and facilities | V2.5.1 |
| -3 | ETSI TS 102 361-3, DMR data protocol | V1.3.1 |
| -4 | ETSI TS 102 361-4, DMR trunking protocol | V1.12.1 (2023-07) |
| TR 398 | ETSI TR 102 398, DMR general system design (informative background only) | V1.5.1 |
RadioFoundry inputs: dmr-modem.md (the modem contract), modem-hrc6000.md, modem-hrc7000.md, gotchas.md, a board's hardware page, codeplug-native.md, docs/ux/principles.md, debuglink.md,
tests/hil/README.md, docs/testing/dmr-bench.md.
| Term | Meaning |
|---|---|
| TS | Trunked station: the infrastructure at a site (the glossary's TS is a timeslot) |
| payload channel | A logical channel granted for a call |
| MVP | The minimal viable subset of section 1.3 |
| Contents | |
|---|---|
| 1, 2 | Scope, protocol facts the implementation relies on |
| 3 | Mapping onto RadioFoundry |
| 4, 5 | Feasibility per board kind and risks, IP and legal |
| 6 | Test strategy |
| 7, 8, 9 | Phased implementation plan, open questions, references |
1. Scope
1.1 What Tier III is
A Tier III site runs one or more duplex base stations. One TDMA logical channel is the TSCC. Its outbound is a continuous BS-sourced data channel with a CACH that carries CSBK, MBC and UDT signaling (-4 5.0). Idle radios listen to the TSCC. A radio asks for service with a random access request (C_RAND) in an inbound slot that the TSCC hasn't withdrawn. Random access follows a slotted Aloha discipline (-4 6.2). The TSCC answers with acknowledgements. For a call it sends a channel grant (PV_GRANT, TV_GRANT and others, -4 7.1.1.1.1) that names a payload channel (LPCN and timeslot). The parties retune and run a Tier II style call there (-2 procedures plus trunking maintenance PDUs). They return to the TSCC when the payload channel is cleared (P_CLEAR) or when they disconnect (P_MAINT). Registration, authentication, short data, status and polling all run on the TSCC. The protocol descends from MPT 1327.
1.2 MS features and the clauses they depend on
Phase: MVP (section 1.3), P2 (second phase, MS), P3 (later), no (not planned).
| Item | What the MS does | -4 clauses | Other parts | Phase |
|---|---|---|---|---|
| TSCC framing | receive a BS-sourced continuous outbound with CACH; send MS-sourced data bursts inbound | 5.0, 5.2, 5.4.1, 5.4.2 | -1 4.5, 4.6.1, 9.1.1 (sync), 9.3.8 (AT), 9.3.9 (TC) | MVP |
| Channel timing | aligned or offset inbound / outbound relationship (C_ALOHA Offset bit, grant Offset bit) | 6.6.2.3.0, 7.1.1.1.4 | -1 5.1.1, 5.1.4.1 | MVP (the mode the target system uses), both in P2 |
| CACH short LC | C_SYS_Parms: 14 bits of C_SYScode, Reg, Common_Slot_Counter; P_SYS_Parms on payload channels | 5.1, 7.1.2, B.2 | -1 7.1.4, 9.1.7 | P2 (MVP works from CSBKs alone, 1.3) |
| Hunting | resume, commanded, short hunt list (up to 64 LPCNs), comprehensive hunt | 6.3.0, 6.3.2.1, Annex D | MVP: resume + commanded + short hunt; P2: comprehensive | |
| Verification | C_SYScode MODEL / NET / SITE list / PAR check, SYS_AREA vs denied list | 6.3.2.2, 6.3.2.2.1.1-4, 6.3.2.2.2, 6.3.2.2.3 | MVP | |
| Confirmation | signal quality thresholds (hunt vs idle), color code | 6.3.2.3, 6.3.2.4, 6.2.1.3 | MVP | |
| Leaving a TSCC | BER, NSYSerr syscode mismatches, T_Nosig, C_MOVE, registration refused / denied | 6.3.3 | MVP (C_MOVE in P2) | |
| Random access | Mask, Service_Function, withdrawn slots by the AT bit, NRand_Wait, Backoff, NRand_NR / NRand_NE retries, TRand_TC | 6.1.3, 6.2.1.1.1-7, 6.2.1.2, 7.2.1-7.2.5, A.1, A.2 | -1 4.5 (AT timing) | MVP |
| Registration | C_RAND Registration service, C_ACKD Reg_Accepted, C_NACKD Reg_Refused / Reg_Denied, C_WACKD, registration record | 6.4.1-6.4.4.1.9, table 6.9 | MVP | |
| De-registration | at power off / network change, T_Dereg | 6.4.6 | MVP | |
| Mass re-registration | C_BCAST MassReg with Reg_Window | 6.4.5, 7.2.19.5 | P2 | |
| Talkgroup attachment | TRGT_ADR_CNTS = 01 / 10 in the registration C_RAND, appended UDT list | 6.4.4.1.13 | P2 | |
| Authentication | C_AHOY challenge, RC4 response in C_ACKU / P_ACKU, optional PSN | 6.4.4.1.5, 6.4.4.1.11, 6.4.8, 6.6.2.3.2.2 | P2 (the algorithm is in the MVP prototype already) | |
| Group call, calling | C_RAND talkgroup voice, C_WACKD / C_QACKD / C_NACKD, TV_GRANT, TP_Timer | 6.6.2.2.0-6.6.2.2.2, 6.6.2.2.5, 6.6.2.2.7, table 6.40 | MVP | |
| Group call, called | TV_GRANT applicability (listen list, own source), late-entry grants | 6.6.2.2.7, 6.6.2.4 | MVP | |
| Individual call | PV_GRANT; called-party radio check C_AHOY answered C_ACKU MS_Accepted (OACSU) | 6.6.2.2.6, 4.6.2.1.1 | -2 5.2.2 (OACSU) | MVP (OACSU); FOACSU (alerting, answer call service) P2 |
| Payload channel | move within the timing budget, PATCS call start, P_CLEAR, P_PROTECT, P_AHOY presence / selective clear, P_MAINT DISCON at the end, TV_Inactive / TV_Item / call timers, return to the TSCC | 6.6.1.6, 6.6.2.3, 6.6.2.3.2.1-9, 7.1.1.3, 7.1.1.4 | -2 5.2.2.1 (PATCS), -1 5.1.2 | MVP (P_PROTECT DIS_PTT and P_AHOY in P2) |
| Late entry | join a group call from a Late_Entry TV_GRANT, and from embedded LC on the payload channel | 6.6.2.4 | -1 7.1.3, dmr-modem.md 2.4 |
MVP |
| Emergency | C_RAND with EMERG = 1 (ignores Service_Function and Mask < 24, NRand_NE retries), emergency grant bit | 6.2.1.1.1, 6.2.1.1.2, 6.2.1.1.9, table 6.40 | MVP for the request; emergency alarm by status P2 | |
| Status | status delivery (emergency alarm = status 126, cancel = 124) and polling | 6.6.6, 6.6.6.3 | P2 | |
| UDT short data | send / receive short messages on the TSCC (C_UDTHU / C_UDTHD + appended blocks, formats B.3) | 6.5, 6.6.4, Annex B.3 | -1 8.2.1.8, 8.2.2.5 | P2 |
| Packet data on payload | PD_GRANT / TD_GRANT, -3 packet data on the payload channel | 6.6.3 | -3 | P3 (the existing data service would carry it) |
| Absolute grants | MBC grant with CG_AP (CdefParms, color code) | 6.1.2, 7.1.1.1.2, 7.2.42, C.1.1.4 | -1 7.4 (MBC) | P2 |
| Channel plan broadcast | C_BCAST logical channel / frequency relationship | 6.7.1.6, 7.2.19.6 | P2 | |
| Adjacent sites, Vote Now | C_BCAST adjacent site, vote now advice | 6.7.1.3, 6.7.1.7 | P3 | |
| Power save | synchronized sleep to the Common_Slot_Counter | 6.4.7 | P3 | |
| TSCCAS, USBD polling | second control slot, LIP location polling | 6.1.1.3, 6.6.11 | -1 8.2.3.1 | P3 |
| Stun / revive / kill | 6.4.9, 6.4.10 | P3, owner decision (kill is permanent) | ||
| Call diversion, DGNA, ALS, full duplex MS-MS, PSTN / PABX dialing, supplementary data, IP connection advice, preferential call notification | 6.6.7, 6.6.8, 6.6.2.6, 6.6.9, 6.6.10, 6.4.11, 6.4.13, 6.6.2.5 | no (re-evaluate on demand) | ||
| Asynchronous / unregulated TSCC (shared spectrum, BS activation) | 5.3.3, 6.1.1.2 | -2 5.1.1.1 (BS outbound activation) | P3 | |
| Power control and de-key by reverse channel | 6.4.14, 6.4.15 | -1 5.1.5, 6.4 | no (needs RC transmit / receive the drivers don't have) |
1.3 The minimal viable subset
"Join an ETSI Tier III single-site system, register, receive and make group calls, receive and make OACSU private calls, send an emergency request, de-register at power off."
In the MVP the radio:
- Starts from a trunked "network" entry in the codeplug (section 3.7): MODEL, NET, optional SITE list, DMRLA, control category, short hunt list, channel plan, its own ID, its talkgroups.
- Hunts: last confirmed TSCC first (resume / commanded, kept in non-volatile storage as -4 6.3.1 allows), then the short hunt list. Samples each candidate: BS data sync and color code, then a CSBK carrying C_SYScode (C_ALOHA or a grant). The CACH short LC is not needed: every C_ALOHA carries the full 16-bit C_SYScode and the Reg bit.
- Verifies and confirms (6.3.2), then registers when Reg = 1 (6.4.3, 6.4.4.1), with random access per 6.2.
- Idles on the TSCC: decodes every outbound CSBK on the TSCC slot, follows Mask / Service_Function / NRand_Wait / Backoff from C_ALOHA, applies grants addressed to it or its talkgroups.
- On PTT sends a C_RAND (group or individual voice; emergency when the emergency key started it), waits for the grant (talk permit tone on the grant, a busy tone and message on C_NACKD with the reason in words, a "queued" message on C_QACKD / C_WACKD), moves to the payload channel, transmits with the existing call stack (headers, superframes, terminator, PATCS impolite), and on the end of the call sends N_Maint (4) P_MAINT DISCON when it is the caller of an individual call or the initiator of a group call (6.6.2.3.2.5), then resumes the TSCC.
- On the payload channel obeys P_CLEAR, the inactivity timer TV_Inactive, the item timer TV_Item and the call timer (6.6.2.3.2.6, 6.6.2.3.2.9).
- Answers a called-party radio check C_AHOY with C_ACKU(MS_Accepted) (OACSU, 6.6.2.2.6).
- Joins a group call already running from a Late_Entry TV_GRANT (6.6.2.4.1), and ignores Late_Entry grants for a call it left (same clause, last paragraph).
- De-registers at power off (6.4.6) within the existing power-off hold (gotcha 7.4).
Assumptions the MVP makes about the target system, to be confirmed with the owner (section 8):
- Fixed or flexible channel plan programmed into the radio; no absolute (MBC) grants required. (qradiolink's dmrtc notes that absolute grants are only usable on some radios when they hunt in a flexible plan.)
- Site timeslot synchronized (C_ALOHA Site Timeslot Synchronization = 1) preferred; unsynchronized supported with the 4-CACH resynchronization allowance of 6.6.1.6.2.2.
- No authentication required, or authentication with a key the owner can program (the algorithm is ready, 2.9).
- One timing mode (aligned or offset) on the target system; the other follows in P2.
Out of the MVP on purpose: UDT short data, status, mass re-registration, talkgroup attachment, FOACSU, packet data on payload, power save, TSCCAS, adjacent sites, C_MOVE, absolute grants, channel plan broadcasts.
1.4 Later phases: the trunked station side
| Phase | What | Notes |
|---|---|---|
| P3a | Host TSCC simulator (already needed for the MS tests, section 6.1): a TSCC and payload channel model in C on the host, driving the MS state machine through the simulated modem | Becomes the reference for any TS work |
| P3b | Single-carrier composite site on the hotspot: one duplex frequency pair, TS1 = TSCC, TS2 = payload channel (a non-dedicated / composite arrangement, -4 5.3.2), run by the PC side through the MMDVM protocol or by firmware on a duplex modem | A two-frequency duplex BS needs a transmitter and a receiver running at once. None of the single-transceiver radios can do that. On the SDR bench's MMDVM_HS dual board it is possible today (section 6.2) |
| P3c | Two-chip HR_C7000 radio: one chip transmits the TSCC / payload outbound continuously, the other receives the inbound; true duplex on one radio, a one-carrier site | Depends on a port of such a radio; the C7000 has BS framing registers (LAYER2_BS_MODE, BS_CACH) never used by any working firmware |
| P3d | Multi-carrier site, multi-site, network interconnect | Out of scope for radio firmware; belongs to a PC or Linux controller. Mentioned only so nothing in P1/P2 blocks it |
The TS side carries more IP risk than the MS side (section 5) and needs regulator-compliant duplex hardware. Plan it only after the MS works against a third-party TSCC.
2. Protocol facts the implementation relies on
Everything here is checked against the clauses cited. Bit layouts are in the prototype (tests/host/tier3ProtoTest.c),
which packs and unpacks each PDU and checks the field sums (64 bits in octets 2-9 for every CSBK below).
2.1 Framing and slots
- The TSCC outbound uses BS-sourced data sync; the inbound uses MS-sourced data sync (-4 5.0). Both logical channels of the carrier share the CACH (-1 4.5).
- A power-save frame is 8 TDMA frames (480 ms): four short LCs, one per 120 ms (-4 5.0, 5.1.0).
- On a TSCC the CACH carries C_SYS_Parms (SLCO 0010): MODEL (2), NET + SITE (12), Reg (1), Common_Slot_Counter (9, +1 per 120 ms, wraps at 511) (-4 5.1, 7.1.2.1). A payload carrier carries P_SYS_Parms (SLCO 0011) instead (7.1.2.2). Only the 14 MSBs of C_SYScode fit the CACH; PAR is in the CSBKs only (5.1.1).
2.2 C_SYScode (-4 6.3.2.2.1.1, table 6.5)
16 bits: MODEL (2) | NET | SITE | PAR (2). NET / SITE widths by model: tiny 9 / 3, small 7 / 5, large 4 / 8, huge 2 / 10. SYS_AREA (the registration record) is the DMRLA most significant bits of SITE (6.4.1.2), DMRLA 1-3 / 1-5 / 1-8 / 1-10 by model. PAR: 01 category A only, 10 category B only, 11 both, 00 reserved (6.3.2.2.1.4). Authorization: MODEL, then NET, then (if the radio holds a SITE list) SITE, then PAR against the radio's control category (6.3.2.2.2), then SYS_AREA against the denied registration list (6.3.2.2.3).
2.3 Random access (-4 6.2)
- C_ALOHA (CSBKO 25) carries Mask (5), Service_Function (2), NRand_Wait (4), Reg (1), Backoff (4), C_SYScode (16), an MS address (24), and the flags TSCCAS, Site Timeslot Synchronization, Version (3), Offset, Active_Connection (7.1.1.1.4).
- Mask: the PDU applies to us when its Mask LSBs of the address match ours (6.1.3). Mask 24 with ADRNULL forbids all random access. Emergency may ignore any Mask except 24 (6.2.1.1.1).
- Service_Function: 00 all, 01 payload services + registration, 10 non-payload services + registration, 11 registration only (table 6.1). Emergency ignores it (6.2.1.1.2).
- Withdrawn slots: an inbound slot is withdrawn when the AT bit of the CACH before it says busy. The CACH before outbound slot N gives the state of inbound slot N, one slot later, to leave time to decode and key up (-1 4.5, figure 4.8). The MS must have decoded that AT bit to use the slot; an AT bit not received counts as withdrawn (-4 6.2.1.1.6, note c). This is the hardest real-time requirement in the MVP (section 3.2).
- After a C_RAND the response may come in the next TDMA frame or up to NWait frames later: NRand_Wait 0-13 = 0-13 frames, 14 = 15, 15 = 24 (table 6.2). Default before any C_ALOHA: NDefault_NW = 5 (A.2).
- No response in NWait + 1 frames: pick the next slot uniformly in 1..Backoff frames; Backoff 1-15 = 1, 2, 3, 4, 5, 8, 11, 15, 20, 26, 33, 41, 50, 70, 100 frames (table 6.3). The first attempt may use the next slot (6.2.1.1.6).
- Give up after NRand_NR = 6 attempts (NRand_NE = 10 for emergency) or TRand_TC (2-60 s) (6.2.1.1.7, A.1, A.2).
- After a valid answer: stop sending, run TP_Timer (4-60 s, services that need a payload channel) or TNP_Timer (2-60 s, TSCC-only services), restarted by further acknowledgements (6.2.1.2).
- A PDU that needs an answer from a given MS withdraws the slot one TDMA frame after its last block; the MS answers there (6.2.1.1.3). So C_AHOY answers (radio check, authentication) go out exactly one frame after the C_AHOY, without random access.
2.4 Registration (-4 6.4.3-6.4.6)
- Reg = 0: no registration; free to call. Reg = 1 and no record for this SYS_AREA: register before sending anything but a registration request or an authentication answer (6.4.3).
- C_RAND Service_Kind 1110 with Reg_Dereg = 1, target = 0x00 followed by C_SYScode (table 6.9). The TSCC drops a request whose C_SYScode doesn't match its own (6.4.4.1.0). Delete the old SYS_AREA record when sending.
- Answers: C_ACKD Reg_Accepted (0110 0010) from REGI = record the SYS_AREA; C_NACKD Reg_Refused (0010 1010) = hunt again; C_NACKD Reg_Denied = add SYS_AREA to the denied list (at least 8 entries, FIFO, lifetime T_DENREG, discarded at power off) and hunt; C_WACKD = wait TNP_Timer; C_AHOY = authentication challenge (6.4.4.1.1-6.4.4.1.5, table 6.8).
- The current registration record is retained at power off (table 6.8); the denied list is not.
- De-registration: the same C_RAND with Reg_Dereg = 0, IP_Inform = 0, PS_RQ = 0; only answer C_ACKD Reg_Accepted; abandon after T_Dereg (0.2-2 s) (6.4.6, A.1).
2.5 Calls (-4 6.6.1-6.6.2)
- Voice C_RAND: Service_Options = EMERG, privacy (0), SUPED_SV, BCAST_SV, reserved, PRIORITY (2; 0 when EMERG), then proxy, appended supplementary data (2), ALS, reserved, Service_Kind 0000 individual / 0001 talkgroup, target, source (table 6.40).
- Valid answers to a single-part voice request: C_WACKD / C_QACKD / C_NACKD / C_ACKD(callback), a called-party radio check, a diversion UDT, the grant, a supplementary data C_AHOY (6.6.2.2.2). The MVP handles all but the last two (those need features it doesn't request).
- Grants (CSBKO 48 PV_GRANT, 49 TV_GRANT, 50 BTV_GRANT): LPCN (12; 0 invalid, 4095 = absolute parameters in an appended MBC block), logical channel (0 = TS1), late entry (TV only), emergency, offset timing, target, source (7.1.1.1.1). Applicable when the source is us, or (private) the target is us, or (group) the target is one of our talkgroups (6.6.2.2.7).
- Timing budget to the payload channel (6.6.1.6.2): site timeslot synchronized: be ready on the second occurrence of TS1, or the third of TS2, after decoding the grant, that is about 60-120 ms. Not synchronized: ready on the next assigned slot after synchronizing to the payload outbound, with at least 4 CACH (2 or 4, a recommended programmable value) of idle sent by the TS for that.
- The payload channel starts with PATCS (-4 6.6.2.3.0; -2 5.2.2.1): the caller keys at once; for an individual call the radio check already happened on the TSCC.
- Payload outbound may carry P_GRANT, P_CLEAR, P_PROTECT, P_AHOY, P_xACK, the call's voice, terminators, idle (6.6.1.6.2.0). The payload carrier's CACH never carries C_SYS_Parms (6.6.2.3.0).
- End: caller of an individual call or initiator of a group call sends P_MAINT DISCON (N_Maint = 4 consecutively); a group recipient leaves silently; both go back to TSCC acquisition, starting with the TSCC they came from (6.6.2.3.2.5). P_CLEAR LPCN 0 means "the TSCC you were last confirmed on" (7.1.1.3.2).
- Timers on the payload channel: TV_Inactive (0-20 s, no adequate signal), TV_Item (10-60 s, then terminator and PTT disabled until released), call timer tokens (A.1, tables A.2-A.5).
- Trunking discipline (message / transmission / quasi-transmission trunking with TV_Hangtime) is the TS's choice; the MS just follows grants and clears (4.11, 6.6.2.3.0).
2.6 Late entry and emergency
- Late entry: while a group call runs the TSCC may repeat TV_GRANT with Late_Entry = 1 at intervals (6.6.2.4.1).
On the payload channel the existing embedded LC late entry still applies (
dmr-modem.md2.4). - Emergency: a C_RAND with EMERG = 1; the grant has its emergency bit; the TS uses T_EMERG_TIMER. Status 126 / 124 for
alarm / cancel without a call (6.6.6.3). The Tier II emergency alarm CSBK of
dmr-emergency.mdis not a Tier III procedure; on a trunked network the emergency feature maps onto these.
2.7 Channel plans (Annex C)
- Fixed plan: f_MS_Tx = f_base + (LPCN - 1) x separation; f_MS_Rx = f_MS_Tx -/+ duplex split (TXRX_SPLIT 0 = MS Tx high, 1 = MS Tx low); separation 5, 6.25, 10, 12.5, 15, 20, 25, 30 kHz; split 0-50 MHz in 2.5 kHz steps (C.1.1.2).
- Flexible plan: a table of LPCN -> Tx, Rx, color code (C.1.1.3).
- Absolute: CdefParms gives Tx and Rx as 10-bit MHz + 13-bit count of 125 Hz (C.1.1.4, 7.2.42); the CG_AP block also gives the color code (7.1.1.1.2).
- Color code: 0000 by default, or per channel from the plan, or the TSCC's own outbound color code (6.2.1.3).
2.8 Defaults for the configurable timers
The standard gives ranges, not values. The MVP needs defaults the codeplug can override:
| Timer / constant | Range (A.1, A.2) | RadioFoundry default | Why |
|---|---|---|---|
| TRand_TC | 2-60 s | 10 s | long enough for NRand_NR retries at Backoff 15 |
| T_Nosig | 1-15 s | 5 s | |
| TP_Timer / TNP_Timer | 4-60 s / 2-60 s | 20 s / 10 s | |
| TV_Inactive | 0-20 s | 5 s | |
| TV_Item | 10-60 s | the channel TOT, at most 60 s | one meaning for the user |
| T_Dereg | 0.2-2 s | 1 s | fits the power-off hold |
| T_DENREG | 0 or 10-10 000 s | 0 (inactive) | |
| NSYSerr | 1-3 | 3 | |
| Hunt threshold / idle threshold (6.3.2.3) | manufacturer | RSSI and burst CRC rate, both settable |
2.9 Authentication (-4 6.4.8)
Response = bytes 257-259 of an RC4 keystream keyed with challenge (3 octets) followed by K (16 octets), optionally
XORed with a 3-octet PSN. The prototype reproduces both ETSI test vectors (tables 6.15 and 6.16: K = 01..10,
challenge 7A 17 C0 gives E8 CA 1D, with PSN 01 02 03 gives E9 C8 1E). The challenge comes in C_AHOY (Service_Kind 1110,
challenge in the source field, 6.4.8.2); the answer is C_ACKU (CSBKO 33) with reason 0100 1000 and the response in
the target field (6.4.8.3), or P_ACKU on a payload channel. K is a per-radio secret: it belongs in the settings store
key slots like the privacy keys (codeplug-native.md 1), never in a codeplug file.
3. Mapping onto RadioFoundry
3.1 Layers
UI (network / site / registration / call setup states) core/ui (later WP, not now)
|
radio core: channel vs trunked network, retune, PTT routing core/radio (small hooks)
|
trunk MS: hunt, verify, register, random access, grants, payload core/dmr/trunk/ (new, CONFIG_DMR_TRUNK)
| \
| dmr_call (reused on the payload channel, and as the
| CSBK / data receive path on the TSCC) core/dmr/call
|
hal_dmr_modem (+ AT bit, slot-exact single bursts, timing hold) hal/ (contract additions)
hal_radio (+ split per-burst TX / RX, fast retune) hal/
|
drivers / boards: HR_C6000 + AT1846S, HR_C7000 + FD6818
core/dmr/trunk/ (new, behind CONFIG_DMR_TRUNK, default n, a sub-feature of CONFIG_DMR):
| File | Role |
|---|---|
t3_pdu.c/h |
pack / unpack of the CSBKs, MBC grants, C_SYS_Parms; reason / service kind tables. Starts from the prototype |
t3_sys.c/h |
C_SYScode split, authorization, SYS_AREA, denied list, registration record |
t3_access.c/h |
the random access engine: Aloha state, Mask / Service_Function, slot choice, withdrawn-slot check, NWait, Backoff, retries, timers |
t3_hunt.c/h |
hunt lists and order (resume, commanded, short, comprehensive), per-candidate sampling, confirmation thresholds |
t3_plan.c/h |
LPCN -> (Tx Hz, Rx Hz, CC) for fixed, flexible and absolute definitions |
t3_ms.c/h |
the MS state machine: HUNT, VERIFY, CONFIRM, REGISTER, IDLE, SETUP (waiting for grant), PAYLOAD, RETURN, DEREG |
t3_auth.c/h |
RC4 response (P2) |
The trunk MS is modem neutral and board neutral like dmr_call: it sees hal_dmr_modem_t events through
dmr_call (or directly while hunting), asks the radio core to retune, and never includes a board header.
How the pieces share the modem:
- On the TSCC the trunk MS owns the transmit side.
dmr_callstays configured RMO on the TSCC slot with its voice path idle; its data receive hook (dmr_call_set_data_rx) already delivers every CSBK / MBC / data burst with our color code. The trunk MS needs both slots on the carrier (a TSCC may be on either; a composite carrier has a payload slot next to it), so while hunting and idle the receiver runs every slot (gotcha 1.6) and the trunk MS filters by slot itself. A C_RAND or an AHOY answer goes out through a new single-burst request (3.2), not throughdmr_call_data_tx(which does listen-before-talk and BS activation, both wrong on a TSCC). - On the payload channel
dmr_callruns the call as today with a "trunked payload" profile: RMO framing, no BS activation, impolite access (PATCS), no reply window (the grant decides the target), its fade and terminator logic, plus hooks for the trunk MS to see P_CLEAR / P_PROTECT / P_AHOY (data receive hook) and to queue P_MAINT / P_ACKU after the terminator. A PTT pressed on the payload channel during message trunking keys straight away. - The radio core gets a third kind of "where am I": a trunked network (besides channel and VFO). It routes PTT to
the trunk MS, applies its retune requests (the only component that calls
hal_radio), and keeps Home's state.
3.2 dmr_modem HAL additions
The Tier II contract (dmr-modem.md 6) covers most of a payload channel. The TSCC needs:
| # | Capability | Why | Contract change |
|---|---|---|---|
| H1 | AT bit per slot in DMR_EV_SLOT_TICK, with "valid" (decoded this slot) |
withdrawn-slot rule (2.3) | dmr_event_t gains at (0 / 1 / unknown). The prose contract already lists "CACH AT (busy) if heard" for the slot tick; the struct doesn't carry it |
| H2 | Slot-exact single burst with a last-moment veto: "send this burst in inbound slot N unless the CACH AT for slot N says busy (or wasn't decoded)", the decision taken by the driver in its slot interrupt | the MS can't make that decision at task level in time (one slot = 30 ms; the HR_C6000 needs the set-up at the interrupt before) | new tx_slot(slot_counter, burst, veto_on_at); result event DMR_EV_TX_SLOT_DONE (sent / vetoed / late) |
| H3 | Inbound slot selection independent of the outbound slot heard, for aligned vs offset timing (-1 5.1.1 table 5.1: aligned logical channel 1 = inbound TS2 + outbound TS1) | TSCC and payload channels may use either mode | tx_begin(DMR_TIMING_ALIGNED, ts, ...) gets an explicit inbound TS; the driver maps it to its slot clock |
| H4 | Timing hold across a retune: keep the slot clock running (passive timing free-runs) while the RF is retuned, and resynchronize without dropping to idle | site timeslot synchronized grants (60-120 ms budget, 2.5) | rx_restart gains a "keep timing" variant; or the board's retune keeps the modem untouched |
| H5 | Both-slot receive with slot tags while locked to a BS outbound | TSCC on either slot, composite carriers, TSCCAS | exists (rx_lock_slot(0)); confirm the HR_C6000 driver reports ts from TC for every burst |
| H6 | MBC delivery: header + continuation(s) grouped, CRC over the whole MBC checked | absolute grants, C_BCAST channel plans (P2) | the stack can group them itself from burst_rx; no driver change if every block is delivered |
| H7 | Short LC (CACH) delivery, optional | C_SYS_Parms for faster hunting, power save | new event kind; the C6000 only flags "short LC received" (0x86 bit 2) and the C7000 has dll_shortlc_rx_interp; where the 28 bits are readable is unknown for both. The MVP does without it |
| H8 | Per-burst RSSI / BER for confirmation thresholds | 6.3.2.3 | rssi_dbm exists; the C7000 has RECV_INFO_COUNT (sync / FSK errors); the C6000 path uses the AT1846S RSSI |
| H9 | Reverse channel TX / RX | power control, de-key (6.4.14) | not planned |
Everything else (CSBK and MBC TX / RX with chip-computed CRC, BS sync reception, slot tracking from TC, voice) is already in the contract and the two drivers.
3.3 hal_radio additions
| # | Capability | Why |
|---|---|---|
| R1 | Split frequencies per burst: TX on f_MS_Tx for our burst, back to f_MS_Rx right after it | random access: listen to the TSCC outbound, send one inbound burst, hear the answer NWait + 1 frames later. Tier II RMO needs the same, but today an AT1846S board stays on the TX frequency between bursts (the per-burst switch only writes reg 0x30) and an FD6818 board sets the TX frequency once per transmission |
| R2 | Fast retune from task level to a new (Rx, Tx) pair without the full rx() set-up (tones, bandwidth tables) |
hunting and payload moves; the AT1846S retune is three register writes (about 0.25 ms on I2C3, measured as the per-burst cost) |
| R3 | A dwell measurement for hunting: RSSI after settling, sync seen or not, in a bounded time | short and comprehensive hunts (Annex D) |
Lock time after a jump is the unknown in all three: no AT1846S or FD6818 datasheet is available, and the only measured retunes are same-frequency (simplex) ones. It must be measured (section 6.3, WP T3-4) before the timing budget can be trusted.
Budget, site timeslot synchronized, worst case TS1 grant: grant decoded about 5 ms into its slot, ready by the second TS1 after it, so 60 ms minus margins for the move: the I2C writes (0.25 ms), PLL settling (unknown), the modem keeping its slot clock (H4), and the first payload burst being received in step. Even a 10 ms lock leaves room. The random access burst is tighter: RX on the outbound to read the AT bit, retune to Tx and key within about one slot, burst (27.5 ms), back to Rx for the next frame. That is the same per-burst switching Tier II RMO already needs, with the frequency changing each time.
3.4 HR_C6000
What helps:
- RMO reception, TC slot tracking, BS sync detection, CSBK / MBC / data reception and CSBK transmission are all on air
or host-tested (
modem-hrc6000.md5-6). - 0x52 bit 3 is the received AT bit (2); it is read at every system interrupt and at the timeslot interrupt.
- The chip's polite access mode (0x20 / 0x21) refuses to send while "the channel is busy" and raises "send request rejected" (0x82 bit 7). On a BS outbound that may well be exactly the AT-busy rule; if so the chip does the H2 veto in hardware. Unverified: test it (WP T3-4).
- CRC_MCU_Control lets the MCU supply CSBK CRCs (not needed: the chip computes the 0xA5A5 / 0xAAAA masks).
Known limits and risks:
- Set-up deadline. The burst for slot N is set up in the timeslot interrupt before it ("writes at the start of the handler, the burst goes out in the following slot", 4.1). The AT bit for inbound slot N arrives in the CACH just before outbound slot N. Whether 0x52 already holds that CACH's AT when the timeslot interrupt for the set-up fires is unknown; if it holds the previous CACH, the veto is one slot stale. Measure: log 0x52 AT at each timeslot interrupt against a known AT pattern from the bench TSCC (WP T3-4).
- CACH only refreshed in received slots (4.1, 5.3): once the receiver is locked to one slot it stops seeing the other slot's CACH, which carries the AT bit for our inbound slot in aligned timing. The TSCC must be received on both slots (H5), which also matches gotcha 1.6.
- No RF RX interrupt on a board that routes only the RF TX interrupt: the PA drops at the next timeslot interrupt (gotcha 3.4). For a single random access burst followed by reception on another frequency, the switch back to Rx (and its retune) waits for that interrupt, about 1.3 ms into the next slot. Acceptable: the answer comes a frame later at the earliest.
- Reports while transmitting are unreliable (7.8, gotcha 14.1). The MS sends single bursts on the TSCC, so this only matters if the TSCC answers in the slot right after ours; NRand_Wait 0 means "next TDMA frame", which is after our burst.
- Boards with a synthesizer per band: relock per burst was already the open SFR risk (gotcha 14.6). The same applies to R1 here.
- MK22 boards: flash nearly full (
capabilities.md, gotcha 12.3): the MVP (estimate 12-20 KB) does not fit without dropping other features.
3.5 HR_C7000
What helps:
- RX_TYPE_INFO gives the AT bit (bit 23), TC (bit 22), CACH LCSS, current RX slot and all CRC flags per burst
(
modem-hrc7000.md2.7). RX_INFO bits 24-16 are a "trunking multiframe number", and the chip has layer-3 call control; neither has been used by any firmware we know of. Stay at layer 2: our stack owns the protocol, the chip frames bursts. - The chip computes CSBK / MBC CRCs with programmable masks (2.3). Fine for Tier III: every PDU we send has a correct CRC.
- Separate TX and RX timeslot interrupts (vectors 62 / 63) and LAYER2_SLOT_PRE (processing-interrupt advance, 0x551010) give more room for the H2 decision than the C6000 has. How much is unknown (10.4).
dll_shortlc_rx_interp(SYS_INTERP bit 4) can be enabled in the interrupt mask: the chip decodes short LCs. Where it puts them is not documented.
Known limits and risks:
- Tier III waits for Tier II repeater-mode transmit (BS activation, aligned TX) on this chip.
- The board switches the FD6818 per burst but keeps the TX frequency for the whole transmission: R1 is new driver work. The FD6818 has no datasheet; lock time is unknown.
- Flash: an HR_C7000 image must fit between 0x0300C000 and 0x03061000 with about 9-20 KB spare (gotcha 12.3). The MVP
does not fit without trading features (
capabilities.md: about 20 KB free); plan it on an AT1846S board first.
3.6 RF retuning per chip
| Board kind | RF | Retune path | Known cost | Unknown |
|---|---|---|---|---|
| HR_C6000 with one AT1846S | AT1846S, one for both bands | I2C3 400 kHz, regs 0x30 (idle), 0x29, 0x2A, then 0x30 (TX / RX); done today from the modem ISR per burst | 0.25 ms extra per burst (measured) | PLL settling for a duplex split (5 MHz UHF, 0.6 MHz VHF); the 3 kHz TX offset trap when RX was on before TX (gotcha 3.5, bench finding 2026-10-10) |
| HR_C7000 with an FD6818 | FD6818 | bit-banged 3-wire, 2 x 16-bit frequency words at 1 us per bit | under 0.1 ms of bus time | settling; register meaning (no datasheet) |
| HR_C6000 with a synthesizer per band | one synthesizer per band | per-burst relock (gotcha 14.6) | ||
| HR_C5000 | SKY72310 PLL | not ported for DMR TX yet | ||
| Two-chip HR_C7000 radio (not ported) | two transceivers (RDA1846 / FD6818 / AT1141) | one per chip | the port itself |
3.7 Codeplug
Nothing in the open-firmware layout can hold a trunked network, so Tier III depends on the native codeplug
(codeplug-native.md 3-4) or a stopgap record area. Proposed native records:
| Record | Fields | Notes |
|---|---|---|
| network (1..8) | name; MODEL; NET; SITE authorization list (0..8 entries); DMRLA; control category A / B; registration wanted; own ID (0 = radio's); auth key slot (0 = none; key in the settings store); PSN use; color code strategy (default 0 / per plan / TSCC's); timing mode hint; timer overrides (2.8) | fixed during personalization in -4 terms |
| hunt list (per network, up to 64 LPCNs, -4 D.1.0 c) | LPCNs, duplicates allowed for bias; Low_Comp_Ch / High_Comp_Ch; Comp_Flag | |
| channel plan (per network) | type fixed / flexible; fixed: f_base, separation code, TXRX_SPLIT, split; flexible: table of LPCN -> Tx Hz, Rx Hz, CC (size to decide, up to 4094 in the standard) | the absolute and broadcast forms arrive over the air |
| talkgroups (per network) | the network's listen list (existing listen list records can be reused), plus attachment flags (P2) | |
| zone entry | a zone may name a network instead of a channel: selecting it starts the trunk MS | the UI sees "a channel kind" |
Run-time state, in the settings store (not the codeplug, so a codeplug file stays shareable):
| State | Survives power off | Source |
|---|---|---|
| last confirmed TSCC LPCN per network | yes (commanded hunt) | -4 6.3.1 allows it, D.1.0 b |
| registration record (SYS_AREA) per network | yes | table 6.8 |
| denied registration list | no | table 6.8 |
| hunt list additions from announcements | yes | table 6.4 (P2) |
3.8 UI (design input for a later UI work package; core/ui is not edited by this plan)
Following docs/ux/principles.md:
- P2 "you always know what PTT will do": Home on a trunked network shows the network name where the channel name goes, the site and registration state in the place row ("Site 3 · Registered", "Searching…", "Not allowed here"), and the talk target as today. TS / CC chips disappear: the system chooses them.
- The status strip states: idle, "Searching", "Registering", RX (caller and TG as today), TX, and a new transient state "Requesting" between PTT and the grant. TX red only once the grant arrived and the radio keyed.
- PTT: the talk-permit tone plays on the grant, not on the press; while waiting, Home says "Requesting TG 3100…". If PTT is released before the grant, the request is canceled where the standard allows (call cancel, P2) or the grant is ignored and the radio stays on the TSCC.
- P9 messages, one per C_NACKD reason family: "TG 3100 is busy. The system queued your call." (C_QACKD), "Call refused: the system doesn't allow this service for your radio." (Perm_User_Refused), "No answer from 1234567: the radio is off or out of range." (MSaway_Refused), "This site doesn't accept your radio. Searching for another." (Reg_Denied). No reason codes alone.
- P6: typing a TG or ID and pressing PTT works as on a channel: the C_RAND carries any target.
- P7: the network entry is chosen like a channel; nothing a trunked operation does writes the codeplug.
- Emergency stays hold-to-activate; on a network it sends the emergency C_RAND (and the status alarm in P2).
- Diagnostics page: the trunk view section (3.10) rendered as text.
3.9 CPS
qdmr has no Tier III support (its device list and documentation are conventional only; nothing found in a search of its project pages). The open-firmware CPS shim can't carry trunked records. Options, owner decision (section 8):
- The native CPS protocol (
cps-protocol.md3) with record types for network, hunt list, channel plan; a smalltools/editor (JSON in, records out) until a GUI exists. - Contribute a Tier III extension to qdmr's open-firmware driver later (qdmr is GPL: we contribute there, nothing comes back into RadioFoundry).
- Stopgap for bench work: a debug-link request that writes one network definition into the settings store.
3.10 Debug link observability
A new view section trunk (core/debuglink/debuglink_view_trunk.c or appended to debuglink_view_ext.c), keys:
t3.built t3.state (off / hunt / verify / confirm / register / idle / setup / payload / return / dereg)
t3.net t3.lpcn t3.ts t3.syscode t3.model t3.netid t3.site t3.par t3.reg_req t3.registered t3.sys_area t3.denied_n
t3.aloha_mask t3.aloha_sf t3.nwait t3.backoff t3.site_sync t3.offset t3.version
t3.ra_state t3.ra_attempts t3.ra_vetoed t3.ra_late t3.ra_sent t3.ra_answers t3.ra_timeouts
t3.setup_target t3.setup_group t3.setup_result (granted / queued / refused:t3.grant_lpcn t3.grant_ts t3.grant_ms (grant decoded to first in-step payload burst)
t3.payload_rx_hz t3.payload_tx_hz t3.clears t3.maint_sent t3.inactive_timeouts
counters: t3.rx_csbk t3.rx_aloha t3.rx_grants t3.rx_grants_ours t3.rx_bcast t3.rx_ahoy t3.at_busy t3.at_unknown t3.hunts t3.hunt_ms t3.retunes t3.retune_max_us.
Plus a debug-link radio request to select a network and one to force a hunt, so HIL tests need no keys.
4. Feasibility per board kind and risks
| Board kind | MVP feasibility | Blocking work | Notes |
|---|---|---|---|
| HR_C6000 + AT1846S | Plausible. RAM is there; flash was (213 KB of 428 KB when planned) but the default image now leaves only a few KB free (docs/feature-matrix.md), so room has to be made (LTO, about 20 KB, or features off) |
R1 (split per burst), H1 / H2 measurements, Tier II RMO on air first | has a bench radio; first target |
| HR_C7000 + FD6818 | Plausible on the modem side (AT / TC per burst), blocked on flash and on repeater-mode TX on air | R1 driver work, flash budget | second |
| MK22 + HR_C6000 | Modem as the AT1846S boards, but no flash | flash | only with features dropped |
| Synthesizer per band | Doubtful until per-burst synthesizer relock is measured | gotcha 14.6 | |
| HR_C5000 | Not before its DMR TX works; HR_C5000 lacks the C6000's MCU CRC path (irrelevant here) | DMR bring-up | |
| Two-chip HR_C7000 | MS as the HR_C7000 boards; the only candidate for a radio-hosted TS (two chips) | the port | P3c |
Risks, highest first:
| # | Risk | Effect | Mitigation / test |
|---|---|---|---|
| K1 | The AT bit for slot N isn't available in time for the C6000's set-up of slot N (3.4) | random access either never fires (all slots look withdrawn) or fires blind into withdrawn slots and collides with solicited answers | measure on the bench TSCC with a scripted AT pattern (WP T3-4); fallbacks: the chip's own polite access as the veto; or transmit only in slots following a C_ALOHA (Aloha PDUs never withdraw the next slot, 6.2.1.1.3 a), which needs no AT read at all |
| K2 | PLL settling after a duplex jump longer than the RF TX interrupt lead | first symbols of each inbound burst off frequency; the TSCC misses C_RAND | SDR bench measurement of the burst ramp and frequency per burst (sdrmon.py), Tier II RMO first |
| K3 | Inbound slot placement for aligned vs offset timing wrong | every inbound burst in the wrong slot | H3, checked against the bench TSCC and the SDR's slot phase report |
| K4 | No reachable ETSI-conformant TSCC to test against (section 6.2) | MVP can only be host-tested | build the bench TSCC (WP T3-6); qradiolink's dmrtc as a black-box second opinion |
| K5 | Real systems require authentication or vendor extensions (Capacity Max "open system" setting, Hytera options) | registration refused | owner to name a target system; dmrtc's compatibility notes show Motorola radios needing system settings on the TS side |
| K6 | Timing budget to the payload channel missed (H4) | late entry into our own call; first headers lost | measure t3.grant_ms; site-synchronized and unsynchronized cases |
| K7 | Codeplug: the native format isn't built | no place for the network | stopgap in the settings store (3.9 option 3) for bench work |
| K8 | Power: hunting and idling keep the receiver on every slot | idle current higher than on a channel; no power save in MVP | P3 power save (6.4.7); measure idle current on the bench |
| K9 | IP position (section 5) | stay on ETSI procedures; legal review before release |
5. IP and legal
Research notes, not legal advice.
- The standard. TS 102 361-4 is a published ETSI technical specification, free to download and implement. ETSI's IPR policy means members declare patents they consider essential and offer licenses on FRAND terms; it does not mean the standard is royalty free. The ETSI IPR database (ipr.etsi.org) has not been searched for declarations against -4 yet: do this before any release and record the result next to this plan.
- What this plan does to stay clear:
- ETSI procedures only: no rest channel, no Capacity Plus / Linked Capacity Plus / Capacity Max signaling, no Hytera XPT, no vendor CSBKs (FID other than 0x00 is ignored by the trunk MS, as -4 6.6.1.6.2.0 allows).
- BS activation (repeater wake) is only used where ETSI uses it (the unregulated TSCC, P3).
- The TS side (P3) needs a separate legal review before it is built.
- Nothing is taken from third-party trunking code (dmrtc, MMDVMHost forks, SDRTrunk, OP25); those tools may be used as black-box test equipment only.
- Copyright: wire formats aren't protected; the risk is provenance. Everything here comes from the ETSI text.
- Spectrum: transmitting on a licensed trunked system needs the system operator's permission; the bench TSCC needs a licensed duplex pair (amateur 70 cm repeater sub-band pairs, or the owner's business license).
6. Test strategy
6.1 Host simulation (no hardware)
- PDU tests:
tests/host/tier3ProtoTest.ctoday (72 checks: layouts, CRC, syscode, tables, channel plans, authentication vectors). It grows intot3PduTestwhent3_pdu.cexists. - TSCC simulator (
tests/host/sim/t3_tscc_sim.c, new): a slot-accurate model of one site on top of the existing simulated modem (tests/host/sim/dmr_sim_modem.c): a TSCC outbound (C_ALOHA with configurable Mask / SF / NRand_Wait / Backoff, AT bits per slot, solicited withdrawals), inbound collision model (two MS in one slot = both lost), registration answers, grants after a configurable delay, a payload channel (idle, P_CLEAR, P_AHOY), and RF retune latency. A virtual clock in slots. - MS scenarios (
tests/host/t3MsTest.c): power-on hunt and register; Reg = 0; refused, denied, timeout paths; random access with Mask, SF 11, withdrawn slots, lost AT, backoff distribution (chi-square over many runs), NRand_NR exhaustion; group call out (grant, payload, P_MAINT x 4, return); group call in (grant to a listen list TG; grant to another TG ignored); late entry grant; left call not re-joined; individual call with radio check; P_CLEAR LPCN 0; TV_Inactive; emergency ignoring SF and Mask < 24; de-registration at power off within T_Dereg; C_SYScode change NSYSerr times; T_Nosig. Timing assertions in slots (the grant-to-payload budget of 6.6.1.6.2). - Radio core test additions: a zone entry naming a network; PTT routing; Home state strings.
6.2 The SDR bench as a Tier III site
What the bench has (docs/testing/dmr-bench.md 1): an MMDVM_HS dual board (two ADF7021s, duplex capable), an
RTL-SDR, and the rfoundry_sdrbench package, which already runs a duplex repeater with BS sync and CACH short LC
(tools/bench/bench_repeater.py).
Plan, WP T3-6: tools/bench/bench_tscc.py (MIT, ours), a single-carrier composite site on the dual board:
- TS1 = TSCC: continuous C_ALOHA, C_SYS_Parms in the CACH, registration answers, grants; TS2 = the payload channel of the same carrier (LPCN = the TSCC's own channel, logical channel 2). No retune in the first tests: this isolates the protocol from the RF retune risk (K2).
- Then a second payload channel needing a retune is impossible with one duplex board; for K2 / K6 the bench radio
instead uses a channel plan whose payload LPCN maps to the same pair, and the retune is measured separately
against
bench_repeater.pyon a second frequency pair (Tier II RMO). - Needs from the modem firmware: control over the CACH AT bit per slot and the short LC, continuous BS data bursts on
TS1 while TS2 is idle or carrying voice. MMDVM's protocol has DMR_SHORTLC and a trunking flag / DMR_ALOHA in some
firmware (
mmdvm-protocol.md2.10); whether MMDVM_HS honors the trunking flag and lets the host set AT per slot is unknown and is the first thing to check (owner question). If it doesn't, the fallback is an SDR transmitter (none on the bench today) or a two-chip HR_C7000 radio later. - The RTL-SDR with
sdrmon.pychecks every burst the radio sends: slot phase against the TSCC, sync family, data type, CSBK contents, carrier offset and ramps per burst (the retune measurement).
Black-box second opinions (used as instruments only, never read as code; GPL):
| Tool | License | Use |
|---|---|---|
| qradiolink dmrtc (with MMDVMHost-SDR / MMDVM-SDR, a LimeSDR-class transmitter) | GPL-3.0 | an independent TSCC implementing registration, group and private calls, late entry, channel plans, authentication; its README lists Hytera and Motorola radios that registered on it. Needs an SDR transmitter the bench doesn't have |
| SDRTrunk | GPL-3.0 | decoding a TSCC and our inbound bursts; whether it follows ETSI Tier III (as opposed to Capacity Max) needs checking |
| DSD-FME | mixed (ISC-style DSD core; check per file before any doubt) | decoding CSBKs off air; Tier III support to be confirmed |
A real commercial Tier III system (Hytera, Tait, Kenwood, Motorola Capacity Max in "open" mode) is the final interoperability test, if the owner has access to one.
6.3 HIL tests (tests/hil, opt-in)
| Test | Needs | Pass |
|---|---|---|
test_t3_retune.py |
--allow-rf --allow-service-stop, a duplex pair |
radio keys bursts alternating Rx / Tx frequencies (a debug-link test request); SDR: every burst within --max-foff, full length, in its slot; t3.retune_max_us under budget |
test_t3_at_timing.py |
bench TSCC | scripted AT pattern; the radio's t3.ra_vetoed / t3.ra_sent match the pattern exactly; no burst in a withdrawn slot on the SDR |
test_t3_register.py |
bench TSCC | t3.state=idle, t3.registered=1 within 5 s of selecting the network; one C_RAND heard (more with induced losses); de-registration heard at power off (opt-in --allow-reboot) |
test_t3_group_call.py |
bench TSCC, codec | PTT: C_RAND heard, grant sent, voice on TS2 with our ID and TG, 4 P_MAINT after the terminator, back on the TSCC (t3.state=idle) |
test_t3_group_rx.py |
bench TSCC + bench modem as a second MS | grant for a listen-list TG: the call is played; other TG: not moved |
test_t3_late_entry.py |
bench TSCC | late entry grant joins; a call the radio left isn't re-joined |
7. Phased implementation plan
Work packages sized for one agent each. "Obs" = the debug-link keys it must add for its own HIL check.
Phase 1: MVP
| WP | Package | Content | Depends on | Obs | Tests |
|---|---|---|---|---|---|
| T3-0 | prototype (done) | PDU coding, tables, channel plans, auth vectors in tests/host/tier3ProtoTest.c |
host | ||
| T3-1 | t3_pdu, t3_sys, t3_plan |
real encoders / decoders (CSBK set of 1.3, MBC grant + CG_AP, C_SYS_Parms), syscode authorization, SYS_AREA, denied list, channel plan lookup; CONFIG_DMR_TRUNK switch in config/features.mk and core/config.h |
T3-0 | none (library) | host: t3PduTest replaces the prototype |
| T3-2 | host TSCC simulator | tests/host/sim/t3_tscc_sim.c on the simulated modem, scenario scripting |
T3-1 | host | |
| T3-3 | t3_access, t3_ms, t3_hunt |
the MS state machine and random access engine against the simulator; the trunk view section |
T3-1, T3-2, H1 / H2 in the sim modem | t3.* (3.10) |
host: t3MsTest (6.1) |
| T3-4 | modem + RF on an AT1846S board | H1 (AT per tick), H2 (slot-exact burst with veto; try the C6000 polite access as the veto), H3 (inbound slot choice), H4 (timing hold), R1 / R2 (split per burst, fast retune) in drivers/modem/hrc6000, boards/<id>; debug-link test requests: alternate-frequency burst train, AT log |
dmr-modem.md contract update; coordinate with the HR_C6000 RX driver owner |
modem.at_*, rf.retune_*, per-burst AT log in the trace |
host: hrc6000DmrTest additions; HIL: test_t3_retune.py, test_t3_at_timing.py |
| T3-5 | radio core + codeplug stopgap | trunked network as a zone entry; PTT routing; retune requests; network definition in the settings store via a debug-link request (until the native codeplug); registration record persistence | T3-3 | core section: mode=trunk, network name |
host: radioCoreTest additions |
| T3-6 | bench TSCC | tools/bench/bench_tscc.py on the dual board: composite site TS1 TSCC / TS2 payload, scripted AT patterns, registration and grant logic, JSON log; docs/testing/dmr-bench.md cases |
owner answer on MMDVM_HS trunking support; a licensed duplex pair | bench JSON log | bench self-test with sdrmon.py |
| T3-7 | MVP integration on air | run 6.3 tests; fix; document results in this file and feature-matrix.md |
T3-3..T3-6 | HIL | |
| T3-8 | UI | Home / status strip / messages per 3.8 in core/ui with simulator screens and press budgets |
T3-5 | ui.* |
uiFlowTest, gallery |
T3-1, T3-2, T3-4 and T3-6 can run in parallel; T3-3 needs T3-1 and T3-2; T3-5 and T3-8 need T3-3; T3-7 needs all. Tier II RMO TX on air (BS activation, aligned TX, split frequencies) is a prerequisite for T3-4's on-air part and is worth doing first on its own: it retires K2 and K3 with an ordinary repeater.
Phase 2
| WP | Content | Depends on |
|---|---|---|
| T3-9 | native codeplug records (3.7) and the native CPS record types; tools/ editor |
codeplug-native.md 4 being built |
| T3-10 | authentication (C_AHOY / C_ACKU / P_ACKU), key slot in the settings store, PSN | T3-3 |
| T3-11 | UDT short data in / out, status delivery and polling, emergency alarm by status; bridge to the existing inbox | T3-3, data service |
| T3-12 | absolute grants (MBC + CG_AP), C_BCAST channel plan / adjacent sites / call timers, C_MOVE, mass re-registration, talkgroup attachment | T3-3 |
| T3-13 | FOACSU (alerting, answer call service), call cancel, P_PROTECT DIS_PTT / EN_PTT, P_AHOY presence and selective clear | T3-3 |
| T3-14 | both timing modes, unsynchronized sites, comprehensive hunt, CACH short LC (H7) where the chips allow | T3-4 |
| T3-15 | HR_C7000 port of T3-4 (FD6818 split per burst, C7000 AT from RX_TYPE_INFO) | repeater-mode TX on air on the chip, flash budget |
Phase 3
Power save (6.4.7), TSCCAS / USBD LIP polling, packet data on payload channels, the TS side (1.4: host simulator as a reference, composite site on the hotspot or bench, two-chip HR_C7000 site), after a legal review of the TS side.
8. Open questions for the owner
- Target system. Is there a specific Tier III system to join (vendor, timing mode, fixed or flexible plan, authentication on or off, site synchronized)? The MVP's assumptions (1.3) should match it.
- Bench site. May we build
bench_tscc.pyon the SDR bench's MMDVM_HS dual board with rfoundry_sdrbench, and which licensed duplex pair should it use? Do you know whether the MMDVM_HS firmware on the bench honors the MMDVM trunking flag (CACH AT control, continuous TSCC), or should we plan an SDR transmitter for the bench? - Third-party oracles. OK to run qradiolink's dmrtc (GPL-3.0) as black-box test equipment if an SDR transmitter becomes available, keeping it outside the repository?
- Order of work. Do Tier II repeater mode on air (BS activation, split frequencies, aligned TX) first, as the plan recommends?
- Codeplug. Build the native codeplug (T3-9) before Tier III, or use the settings-store stopgap for the MVP?
- CPS. Native CPS records plus a small
tools/editor, or work toward a qdmr contribution? - Radios. Agree the HR_C6000 + AT1846S boards first, the HR_C7000 boards second, MK22, per-band synthesizer and HR_C5000 boards not planned for Tier III?
- Legal. Should the ETSI IPR database search for TS 102 361-4 declarations, and a check of whether shipping Tier III MS code needs a license, happen before any code lands, or before the first release?
- Stun / kill. Implement them at all (P3)? Kill permanently disables a radio by over-the-air command; for an open firmware the owner may prefer "stun only, kill treated as stun".
- TS side. Is a radio-hosted or hotspot-hosted trunked site (P3b / P3c) a goal, or only the MS?
9. References
-
ETSI TS 102 361-1 V2.7.1, -2 V2.5.1, -3 V1.3.1, -4 V1.12.1: https://www.etsi.org/deliver/etsi_ts/102300_102399/ (folders 10236101, 10236102, 10236103, 10236104).
-
ETSI TR 102 398 V1.5.1: https://www.etsi.org/deliver/etsi_tr/102300_102399/102398/
-
qradiolink dmrtc (GPL-3.0), README only (feature list and radio compatibility): https://github.com/qradiolink/dmrtc
-
SDRTrunk (GPL-3.0): https://github.com/DSheirer/sdrtrunk
-
DSD-FME: https://github.com/lwvmobile/dsd-fme
-
qdmr: https://dm3mat.darc.de/qdmr/