Major changes: - Replaced ambiguous 'talkgroups' list with slot1_talkgroups/slot2_talkgroups - Simplified routing: same lists control inbound and outbound (no separate rules) - No wildcards or rewriting (simplicity over flexibility) Configuration updates: - Updated config.json with real KS-DMR settings (TS1: TG2,8,9 / TS2: TG3120) - Updated config_sample.json with slot-specific examples - Created docs/routing.md with complete documentation Code changes: - Added _check_inbound_routing(): Verify repeater can send on TS/TGID - Added _check_outbound_routing(): Verify repeater should receive on TS/TGID - Added _is_slot_busy(): Contention detection with hang time support - Added _update_assumed_stream(): Track streams forwarded to targets - Updated _forward_stream(): Full Phase 3 implementation with routing + contention - Updated StreamState: Added is_assumed flag and end_time field Routing logic: - Inbound: Only accept traffic matching repeater's slot_N_talkgroups - Outbound: Only forward to repeaters with matching slot_N_talkgroups - Contention: Block busy slots, respect hang time after stream ends - Assumed streams: Track forwarded streams on target repeaters Ready for real-world testing with multiple repeaters!
5.9 KiB
Call Routing and Forwarding
Overview
HBlink4 implements configuration-based call routing using timeslot-specific talkgroup lists. This enables precise control over which calls are accepted from repeaters and forwarded to them.
Configuration Structure
Repeater Configuration
Each repeater configuration includes slot-specific talkgroup lists that define which TGIDs are allowed on each timeslot:
{
"name": "Example Repeater",
"match": {
"ids": [312000]
},
"config": {
"enabled": true,
"timeout": 30,
"passphrase": "passw0rd",
"slot1_talkgroups": [8, 9],
"slot2_talkgroups": [3120, 3122],
"description": "KS-DMR Network Repeater"
}
}
Routing Rules
The same talkgroup lists control both inbound (what the repeater can send) and outbound (what it receives):
-
slot1_talkgroups: TGIDs allowed on timeslot 1
- Traffic on TS1 is only processed if TGID is in this list
- Traffic on TS1 is only forwarded to repeaters that have the TGID in their slot1_talkgroups
-
slot2_talkgroups: TGIDs allowed on timeslot 2
- Traffic on TS2 is only processed if TGID is in this list
- Traffic on TS2 is only forwarded to repeaters that have the TGID in their slot2_talkgroups
Forwarding Assumption
Forwarding is always enabled - that's the whole point of HBlink4! If a repeater has a TGID in its slot list, it will both accept traffic on that TS/TGID and receive forwarded traffic for that TS/TGID.
Default Behavior
If a repeater configuration does not include slot talkgroup lists:
- Inbound: All calls are accepted on both slots
- Outbound: No calls are forwarded (empty lists = safe default)
This allows repeaters to transmit freely but requires explicit configuration to participate in call forwarding.
Assumed Slot State
Since HBlink4 forwards calls to repeaters but doesn't receive real-time feedback about transmission state, we must assume the slot state on target repeaters:
Transmission Assumptions
When we forward a stream to a repeater:
- Assume the slot is now active - Track this as an "assumed active" stream
- Block the slot for other traffic - Don't forward other calls to this TS until clear
- Honor hang time - Keep slot blocked for
stream_hang_timeafter stream ends - Track stream lifecycle - Monitor for terminators to know when transmission ends
Slot State Tracking
For each repeater, we track:
- Real streams: Streams originating from this repeater (we receive packets)
- Assumed streams: Streams we're forwarding to this repeater (we send packets)
Both types of streams:
- Block the slot from other traffic
- Respect hang time after completion
- Use terminator detection for immediate end recognition
- Fall back to timeout if terminator is missed
Contention Handling
When a call needs to be forwarded:
-
Check inbound filter: Does source repeater config allow this TS/TGID?
- If no: Drop packet, don't process
-
For each potential target repeater:
- Check outbound filter: Does target config include this TS/TGID?
- If no: Skip this target
-
Check target slot state:
- Is there an active stream (real or assumed) on this slot?
- If yes: Check if it's the same call or different
- Same call: Forward (continue existing stream)
- Different call: Skip (contention - slot busy)
-
Check hang time:
- Has this slot recently ended a stream?
- If within hang time: Skip (slot cooling down)
-
Forward packet:
- Send to target repeater
- Create assumed stream state
- Track for terminator/timeout
Configuration Examples
Simple Statewide Repeater
{
"name": "Statewide Repeater",
"match": {"ids": [312000]},
"config": {
"enabled": true,
"timeout": 30,
"passphrase": "passw0rd",
"slot1_talkgroups": [8],
"slot2_talkgroups": [3120],
"description": "Statewide traffic on TS2"
}
}
Multi-Purpose Repeater
{
"name": "Multi-Purpose Repeater",
"match": {"ids": [312001]},
"config": {
"enabled": true,
"timeout": 30,
"passphrase": "passw0rd",
"slot1_talkgroups": [8, 9],
"slot2_talkgroups": [3120, 3122],
"description": "Local on TS1, Regional/Statewide on TS2"
}
}
Single-Slot Operation
{
"name": "TS1 Only Repeater",
"match": {"ids": [312002]},
"config": {
"enabled": true,
"timeout": 30,
"passphrase": "passw0rd",
"slot1_talkgroups": [8, 9, 3120],
"slot2_talkgroups": [],
"description": "All traffic on TS1, TS2 disabled"
}
}
Same TGID on Both Slots
{
"name": "Dual-Slot Same TG",
"match": {"ids": [312003]},
"config": {
"enabled": true,
"timeout": 30,
"passphrase": "passw0rd",
"slot1_talkgroups": [3120],
"slot2_talkgroups": [3120],
"description": "Statewide TG available on both slots"
}
}
Implementation Notes
Phase 3 Implementation
Current implementation status:
- ✅ Configuration parsing for slot-specific talkgroup lists
- ✅ Inbound filtering (accept/reject incoming calls based on TS/TGID)
- ✅ Outbound filtering (select forwarding targets based on TS/TGID)
- ✅ Assumed slot state tracking (track streams we're forwarding)
- ✅ Contention detection (prevent multiple streams on same slot)
- ✅ Hang time enforcement (respect hang time after stream ends)
Simplicity Over Flexibility
HBlink4 uses a simplified routing model:
- No separate inbound/outbound rules - same lists control both directions
- No wildcards - explicit TGID lists only
- No rewriting - TS/TGID never changed during forwarding
- Forwarding always enabled - the whole purpose of HBlink4
This keeps configuration simple and predictable while covering the vast majority of real-world use cases.