First-class M17 protocol support:
- Full M17 client handling: connect (CONN/LSTN), disconnect (DISC),
keepalive (PING/PONG), voice streaming with 40ms→20ms frame split
- M17 base-40 callsign encoding/decoding per specification
- M17 wire stream ID generation (random-seeded per module)
- Codec2 3200bps frame pairing (two 8-byte frames per 40ms packet)
- Synthetic EOT detection (500ms timer) for clients that don't send
proper end-of-transmission, with cross-mode distribution via
CDvLastFramePacket pushed to reflector stream
- Byte-swapped stream ID fallback for M17 EOT packets
- DISC packet sent to all M17 clients on reflector shutdown
- Listen-only client support
P25 and NXDN peering:
- P25 peer protocol with IMBE voice frame handling
- NXDN peer protocol with AMBE2+ voice frame handling
- AMBE2+↔AMBE2+ gain levelling for NXDN within xlxd
- AMBE2+↔AMBE2+ gain levelling for YSF mode within xlxd
DCS, DPlus (REF), and DExtra (XRF) peer link management:
- Automatic peer connect/disconnect from gatekeeper peer list
- Peer keepalives with timeout detection and reconnection
- Connection state machine (connecting/connected/disconnected)
- Peer client objects for voice routing through existing reflector
- Peer mirror prevention via full IP:port owner matching on DCS,
DPlus, and DExtra peer send paths
AMBEd software vocoder support:
- Async Codec2 encoder thread in CVocodecChannel with condition
variable wake-up, PCM queue, and encoded output queue
- Async IMBE encoder thread (same pattern) using imbe_vocoder library
- Embedded Codec2 library for M17 codec support
- Embedded IMBE vocoder library for P25 codec support
- Staging mechanism in CStream::Task(): hold hardware AMBE result
until software codecs catch up (1s timeout safety net)
- Three-codec output per stream (e.g., AMBEPLUS→AMBE2PLUS+CODEC2+IMBE)
- Precomputed gain multipliers to avoid pow() per packet
- Codec2 decode gain (+6dB) applied in AMBEd stream processing
- IMBE encode gain (-4dB) applied to all PCM→IMBE paths
- Software queue depth caps (SOFTWARE_QUEUE_MAX_DEPTH=50)
Transcoding gains (tuned from live testing):
- Codec2 path: CODEC2_TO_AMBEPLUS -3dB, CODEC2_TO_AMBE2PLUS -13dB,
CODEC2_TO_IMBE -13dB
- IMBE path: IMBE_DECODE_GAIN +18dB, IMBE_TO_AMBE2PLUS -13dB,
IMBE_TO_CODEC2 -7dB
- Hardware: CODECGAIN_AMBEPLUS -10dB, CODECGAIN_AMBE2PLUS +10dB
- Cross-mode AMBE2+: NXDN↔DMR -3dB, DMR→YSF -4dB, YSF→DMR +10dB
RX/TX thread split:
- CProtocol base class: dual-thread infrastructure with RxTask/TxTask,
condition variable on m_Queue for TX wake-up, backward-compatible
via UsesSplitThreads() virtual. Shutdown joins RX first then TX
with final drain loop
- Split protocols: DCS, M17, DExtra (XRF), DPlus (REF), YSF, NXDN, P25
- Legacy single-thread: XLX interlink, DMR+ (DMRplus), DMR MMDVM
- Per-slot std::mutex on stream cache items protecting shared state
between RX and TX threads
- Prevents transmission tail loss by ensuring TX processing never
blocks inbound packet reception
XLX interlink DMR peer generalisation:
- Rename CBmPeer/CBmClient to CXlxDmrPeer/CXlxDmrClient
- Callsign prefix matching for BM (BrandMeister), FD (FreeDMR),
FS (FreeStar), PH (DVSPH) — all rev 2 AMBE2+ only
D-Star end-of-transmission fixes:
- Strip 0x40 end flag from D-Star PID (& 0x1F) in all five D-Star
protocol parsers — the flag shifted EncodeDvPacket's sequence by 1
- Preserve DCS wire format tail (bytes 58-99) via per-module cache
with m_bHasDcsTail guard for cross-mode sources
- DCS HandleQueue sends EOT reliably via independent TX thread
Thread safety and memory safety hardening:
- CPacketStream: cache owner IP at Open() time, eliminating use-after-
free on m_OwnerClient. NULL pointer under stream lock in CloseStream
- Fix ABBA deadlock in all peer management (DCS, DExtra, DPlus, YSF):
snapshot GateKeeper peer list before acquiring Peers/Clients locks
- Fix lock ordering across all protocols to prevent ABBA deadlocks
between Peers, Clients, and GateKeeper mutexes
- Collect items for removal after iteration instead of modifying
containers while iterating
- CUdpSocket::SendVoice: EAGAIN retry (5 attempts, 1ms backoff)
- AMBEd Phase 4 timeout reverted to counter-only (no packet deletion)
- Remove dead CPacketQueue shadow fields, debug RouterThread logging,
IsStreaming() stub. Make g_bDscpLogged atomic
- Client match in keepalive/header paths uses full CIp::operator==
(IP:port) preventing false matches with shared-IP peers
Security hardening:
- Send-time owner exclusion across all protocols using per-module
client cache with snapshotted owner fields under slot lock
- Echo detection for DCS, DExtra (XRF), and DPlus (REF) to reject
reflected audio from peers
- Reject tracker with progressive backoff for invalid connections,
capped map size to prevent memory exhaustion from spoofed IPs
- Sanitized log output to prevent terminal injection
- std::random_device for DMR authentication seed
- Bounds checks on module indices across all protocol handlers
- Use-after-free fixes in OnDvHeaderPacketIn across all protocols
- ARM-portable unaligned access fixes (memcpy for uint16/uint32)
Performance:
- Per-module client cache for DExtra/DPlus/DCS outbound packets
- DSCP EF marking on voice packet sockets for QoS
- Peer lookup maps built once per drain cycle instead of per-packet
- Skip M17 idle packet encoding when no clients connected