mirror of
https://github.com/n0mjs710/dmrlink3
synced 2026-11-08 12:26:30 +00:00
- const.py: rename CALL_MON_NACK→REPEATER_BLOCKED, UNKNOWN_COLLISION→ CALL_INTERRUPT_REQ; add seven previously-unknown opcodes (SYSTEM_MAP_REQ/ REPLY, UNKNOWN_9E, WIRELINE, REMOTE_PROG_REQ/REPLY, OPCODE_0xF0); replace BURST_DATA_TYPE dict with integer constants; add GV field offset constants (GV_BURST_TYPE_OFF, GV_RTP_TS_OFF, GV_BE_FLAG, GV_BE_LC_FLCO_OFF, etc.) - dmrlink.py: fix _ts calculation bug (was returning 1 or 33; now correctly returns 1 or 2); use burst_type for GROUP_VOICE timeslot detection (HEAD/TERM read call_info; SLOT_VOICE reads burst_type bit 7 per protocol); master now sends PEER_LIST_REPLY immediately after MASTER_REG_REPLY without waiting for PEER_LIST_REQ; peer now sends PEER_LIST_REQ immediately on receiving MASTER_REG_REPLY; master sends DE_REG_REPLY and broadcasts updated peer list on DE_REG_REQ; peer_list_req sends only to requester (not broadcast); master watchdog: any packet from a registered peer refreshes keepalive timestamp; new opcodes dispatched at DEBUG instead of error - bridge.py: replace seq_id-based call boundary detection with per-TS call_start tracking using VOICE_HEAD/VOICE_TERM burst types; XPR8400 firmware churns seq_id every superframe when Talker Alias is interleaved, making it unreliable as a call boundary indicator Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
141 lines
5.3 KiB
Python
141 lines
5.3 KiB
Python
#!/usr/bin/env python
|
|
#
|
|
###############################################################################
|
|
# Copyright (C) 2013-2026 Cortney T. Buffington, N0MJS <n0mjs@me.com>
|
|
#
|
|
# This program is free software; you can redistribute it and/or modify
|
|
# it under the terms of the GNU General Public License as published by
|
|
# the Free Software Foundation; either version 3 of the License, or
|
|
# (at your option) any later version.
|
|
#
|
|
# This program is distributed in the hope that it will be useful,
|
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
# GNU General Public License for more details.
|
|
#
|
|
# You should have received a copy of the GNU General Public License
|
|
# along with this program; if not, write to the Free Software Foundation,
|
|
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
###############################################################################
|
|
|
|
# IPSC Protocol Constants and Bitmasks -- merged from ipsc_const.py and ipsc_mask.py
|
|
|
|
# Known IPSC Message Types
|
|
CALL_CONFIRMATION = b'\x05'
|
|
TXT_MESSAGE_ACK = b'\x54'
|
|
CALL_MON_STATUS = b'\x61'
|
|
CALL_MON_RPT = b'\x62'
|
|
REPEATER_BLOCKED = b'\x63' # signal interference / BSI blocking event
|
|
XCMP_XNL = b'\x70'
|
|
GROUP_VOICE = b'\x80'
|
|
PVT_VOICE = b'\x81'
|
|
GROUP_DATA = b'\x83'
|
|
PVT_DATA = b'\x84'
|
|
RPT_WAKE_UP = b'\x85'
|
|
CALL_INTERRUPT_REQ = b'\x86' # call interrupt request
|
|
MASTER_REG_REQ = b'\x90'
|
|
MASTER_REG_REPLY = b'\x91'
|
|
PEER_LIST_REQ = b'\x92'
|
|
PEER_LIST_REPLY = b'\x93'
|
|
PEER_REG_REQ = b'\x94'
|
|
PEER_REG_REPLY = b'\x95'
|
|
MASTER_ALIVE_REQ = b'\x96'
|
|
MASTER_ALIVE_REPLY = b'\x97'
|
|
PEER_ALIVE_REQ = b'\x98'
|
|
PEER_ALIVE_REPLY = b'\x99'
|
|
DE_REG_REQ = b'\x9A'
|
|
DE_REG_REPLY = b'\x9B'
|
|
SYSTEM_MAP_REQ = b'\x9C' # system topology query; purpose not fully known
|
|
SYSTEM_MAP_REPLY = b'\x9D'
|
|
UNKNOWN_9E = b'\x9E' # possibly extended peer registration
|
|
WIRELINE = b'\xB2' # MNIS data sub-protocol
|
|
REMOTE_PROG_REQ = b'\xE0' # CPS remote programming session request
|
|
REMOTE_PROG_REPLY = b'\xE1'
|
|
OPCODE_0xF0 = b'\xF0' # observed, benign, no response needed
|
|
|
|
# IPSC Version Information
|
|
IPSC_VER_14 = b'\x00'
|
|
IPSC_VER_15 = b'\x00'
|
|
IPSC_VER_15A = b'\x00'
|
|
IPSC_VER_16 = b'\x01'
|
|
IPSC_VER_17 = b'\x02'
|
|
IPSC_VER_18 = b'\x02'
|
|
IPSC_VER_19 = b'\x03'
|
|
IPSC_VER_22 = b'\x04'
|
|
|
|
LINK_TYPE_IPSC = b'\x04'
|
|
|
|
# IPSC Version field used in registration packets
|
|
IPSC_VER = LINK_TYPE_IPSC + IPSC_VER_17 + LINK_TYPE_IPSC + IPSC_VER_16
|
|
|
|
# GROUP_VOICE burst type byte values (data[GV_BURST_TYPE_OFF] returns int in Python 3)
|
|
VOICE_HEAD = 0x01
|
|
VOICE_TERM = 0x02
|
|
SLOT1_VOICE = 0x0A
|
|
SLOT2_VOICE = 0x8A
|
|
|
|
# GROUP_VOICE packet field offsets
|
|
GV_CALL_INFO_OFF = 17 # timeslot/end flags byte
|
|
GV_RTP_TS_OFF = 22 # 4-byte RTP timestamp (8 kHz clock, +480 per burst)
|
|
GV_BURST_TYPE_OFF = 30 # burst type byte (see VOICE_HEAD / SLOT1_VOICE etc.)
|
|
GV_BE_FLAG = 0x16 # data[32] value that identifies a burst-E superframe
|
|
GV_BE_LC_FLCO_OFF = 56 # FLCO field within burst-E embedded LC
|
|
GV_MIN_LEN = 33 # minimum valid GROUP_VOICE packet length
|
|
FLCO_GROUP = 0x00 # FLCO value for a group voice call (not Talker Alias)
|
|
|
|
# Packet type membership lists
|
|
ANY_PEER_REQUIRED = [GROUP_VOICE, PVT_VOICE, GROUP_DATA, PVT_DATA, CALL_MON_STATUS,
|
|
CALL_MON_RPT, REPEATER_BLOCKED, CALL_INTERRUPT_REQ, XCMP_XNL,
|
|
RPT_WAKE_UP, DE_REG_REQ]
|
|
PEER_REQUIRED = [PEER_ALIVE_REQ, PEER_ALIVE_REPLY, PEER_REG_REQ, PEER_REG_REPLY]
|
|
MASTER_REQUIRED = [PEER_LIST_REPLY, MASTER_ALIVE_REPLY]
|
|
USER_PACKETS = [GROUP_VOICE, PVT_VOICE, GROUP_DATA, PVT_DATA]
|
|
|
|
# RCM Timeslot constants
|
|
TS = {b'\x00': '1', b'\x01': '2'}
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# IPSC Bitmasks (from ipsc_mask.py)
|
|
# ---------------------------------------------------------------------------
|
|
|
|
# LINKING STATUS byte flags
|
|
# xx.. .... Peer Operational (01 = operational)
|
|
# ..xx .... Peer MODE: 00=No Radio, 01=Analog, 10=Digital
|
|
# .... xx.. IPSC Slot 1: 10=on, 01=off
|
|
# .... ..xx IPSC Slot 2: 10=on, 01=off
|
|
PEER_OP_MSK = 0b01000000
|
|
PEER_MODE_MSK = 0b00110000
|
|
PEER_MODE_ANALOG = 0b00010000
|
|
PEER_MODE_DIGITAL = 0b00100000
|
|
IPSC_TS1_MSK = 0b00001100
|
|
IPSC_TS2_MSK = 0b00000011
|
|
|
|
# SERVICE FLAGS byte 3
|
|
CSBK_MSK = 0b10000000
|
|
RPT_MON_MSK = 0b01000000
|
|
CON_APP_MSK = 0b00100000
|
|
|
|
# SERVICE FLAGS byte 4
|
|
XNL_STAT_MSK = 0b10000000
|
|
XNL_MSTR_MSK = 0b01000000
|
|
XNL_SLAVE_MSK = 0b00100000
|
|
PKT_AUTH_MSK = 0b00010000
|
|
DATA_CALL_MSK = 0b00001000
|
|
VOICE_CALL_MSK = 0b00000100
|
|
MSTR_PEER_MSK = 0b00000001
|
|
|
|
# TIMESLOT CALL & STATUS byte (GV_CALL_INFO_OFF = byte 17 of voice/data packets)
|
|
# .x.. .... End flag (0=in-progress, 1=end)
|
|
# ..x. .... Timeslot (0=TS1, 1=TS2)
|
|
END_MSK = 0b01000000
|
|
TS_CALL_MSK = 0b00100000
|
|
|
|
# RTP header bitmasks
|
|
RTP_VER_MSK = 0b11000000
|
|
RTP_PAD_MSK = 0b00100000
|
|
RTP_EXT_MSK = 0b00010000
|
|
RTP_CSIC_MSK = 0b00001111
|
|
RTP_MRKR_MSK = 0b10000000
|
|
RTP_PAY_TYPE_MSK = 0b01111111
|