Merge pull request #3 from AndyTaylorTweet/master

Add support for linking to XLXd via MMDVMHost protocol, with module link on connect.
This commit is contained in:
Cort Buffington 2019-06-19 12:50:47 -05:00 committed by GitHub
commit d973a7c226
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 123 additions and 2 deletions

View File

@ -199,6 +199,52 @@ def build_config(_config_file):
'LAST_PING_ACK_TIME': 0, 'LAST_PING_ACK_TIME': 0,
}}) }})
if config.get(section, 'MODE') == 'XLXPEER':
CONFIG['SYSTEMS'].update({section: {
'MODE': config.get(section, 'MODE'),
'ENABLED': config.getboolean(section, 'ENABLED'),
'LOOSE': config.getboolean(section, 'LOOSE'),
'SOCK_ADDR': (gethostbyname(config.get(section, 'IP')), config.getint(section, 'PORT')),
'IP': gethostbyname(config.get(section, 'IP')),
'PORT': config.getint(section, 'PORT'),
'MASTER_SOCKADDR': (gethostbyname(config.get(section, 'MASTER_IP')), config.getint(section, 'MASTER_PORT')),
'MASTER_IP': gethostbyname(config.get(section, 'MASTER_IP')),
'MASTER_PORT': config.getint(section, 'MASTER_PORT'),
'PASSPHRASE': bytes(config.get(section, 'PASSPHRASE'), 'utf-8'),
'CALLSIGN': bytes(config.get(section, 'CALLSIGN').ljust(8)[:8], 'utf-8'),
'RADIO_ID': config.getint(section, 'RADIO_ID').to_bytes(4, 'big'),
'RX_FREQ': bytes(config.get(section, 'RX_FREQ').ljust(9)[:9], 'utf-8'),
'TX_FREQ': bytes(config.get(section, 'TX_FREQ').ljust(9)[:9], 'utf-8'),
'TX_POWER': bytes(config.get(section, 'TX_POWER').rjust(2,'0'), 'utf-8'),
'COLORCODE': bytes(config.get(section, 'COLORCODE').rjust(2,'0'), 'utf-8'),
'LATITUDE': bytes(config.get(section, 'LATITUDE').ljust(8)[:8], 'utf-8'),
'LONGITUDE': bytes(config.get(section, 'LONGITUDE').ljust(9)[:9], 'utf-8'),
'HEIGHT': bytes(config.get(section, 'HEIGHT').rjust(3,'0'), 'utf-8'),
'LOCATION': bytes(config.get(section, 'LOCATION').ljust(20)[:20], 'utf-8'),
'DESCRIPTION': bytes(config.get(section, 'DESCRIPTION').ljust(19)[:19], 'utf-8'),
'SLOTS': bytes(config.get(section, 'SLOTS'), 'utf-8'),
'URL': bytes(config.get(section, 'URL').ljust(124)[:124], 'utf-8'),
'SOFTWARE_ID': bytes(config.get(section, 'SOFTWARE_ID').ljust(40)[:40], 'utf-8'),
'PACKAGE_ID': bytes(config.get(section, 'PACKAGE_ID').ljust(40)[:40], 'utf-8'),
'GROUP_HANGTIME': config.getint(section, 'GROUP_HANGTIME'),
'XLXMODULE': config.getint(section, 'XLXMODULE'),
'OPTIONS': '',
'USE_ACL': config.getboolean(section, 'USE_ACL'),
'SUB_ACL': config.get(section, 'SUB_ACL'),
'TG1_ACL': config.get(section, 'TGID_TS1_ACL'),
'TG2_ACL': config.get(section, 'TGID_TS2_ACL')
}})
CONFIG['SYSTEMS'][section].update({'XLXSTATS': {
'CONNECTION': 'NO', # NO, RTPL_SENT, AUTHENTICATED, CONFIG-SENT, YES
'CONNECTED': None,
'PINGS_SENT': 0,
'PINGS_ACKD': 0,
'NUM_OUTSTANDING': 0,
'PING_OUTSTANDING': False,
'LAST_PING_TX_TIME': 0,
'LAST_PING_ACK_TIME': 0,
}})
elif config.get(section, 'MODE') == 'MASTER': elif config.get(section, 'MODE') == 'MASTER':
CONFIG['SYSTEMS'].update({section: { CONFIG['SYSTEMS'].update({section: {
'MODE': config.get(section, 'MODE'), 'MODE': config.get(section, 'MODE'),

View File

@ -207,3 +207,35 @@ USE_ACL: True
SUB_ACL: DENY:1 SUB_ACL: DENY:1
TGID_TS1_ACL: PERMIT:ALL TGID_TS1_ACL: PERMIT:ALL
TGID_TS2_ACL: PERMIT:ALL TGID_TS2_ACL: PERMIT:ALL
[XLX-1]
MODE: XLXPEER
ENABLED: True
LOOSE: False
EXPORT_AMBE: False
IP:
PORT: 54002
MASTER_IP: 172.16.1.1
MASTER_PORT: 62030
PASSPHRASE: passw0rd
CALLSIGN: W1ABC
RADIO_ID: 312000
RX_FREQ: 449000000
TX_FREQ: 444000000
TX_POWER: 25
COLORCODE: 1
SLOTS: 1
LATITUDE: 38.0000
LONGITUDE: -095.0000
HEIGHT: 75
LOCATION: Anywhere, USA
DESCRIPTION: This is a cool repeater
URL: www.w1abc.org
SOFTWARE_ID: 20170620
PACKAGE_ID: MMDVM_HBlink
GROUP_HANGTIME: 5
XLXMODULE: 4004
USE_ACL: True
SUB_ACL: DENY:1
TGID_TS1_ACL: PERMIT:ALL
TGID_TS2_ACL: PERMIT:ALL

View File

@ -224,6 +224,13 @@ class HBSYSTEM(DatagramProtocol):
self.datagramReceived = self.peer_datagramReceived self.datagramReceived = self.peer_datagramReceived
self.dereg = self.peer_dereg self.dereg = self.peer_dereg
elif self._config['MODE'] == 'XLXPEER':
self._stats = self._config['XLXSTATS']
self.send_system = self.send_master
self.maintenance_loop = self.peer_maintenance_loop
self.datagramReceived = self.peer_datagramReceived
self.dereg = self.peer_dereg
def startProtocol(self): def startProtocol(self):
# Set up periodic loop for tracking pings from peers. Run every 'PING_TIME' seconds # Set up periodic loop for tracking pings from peers. Run every 'PING_TIME' seconds
self._system_maintenance = task.LoopingCall(self.maintenance_loop) self._system_maintenance = task.LoopingCall(self.maintenance_loop)
@ -283,6 +290,29 @@ class HBSYSTEM(DatagramProtocol):
# KEEP THE FOLLOWING COMMENTED OUT UNLESS YOU'RE DEBUGGING DEEPLY!!!! # KEEP THE FOLLOWING COMMENTED OUT UNLESS YOU'RE DEBUGGING DEEPLY!!!!
# logger.debug('(%s) TX Packet to %s:%s -- %s', self._system, self._config['MASTER_IP'], self._config['MASTER_PORT'], ahex(_packet)) # logger.debug('(%s) TX Packet to %s:%s -- %s', self._system, self._config['MASTER_IP'], self._config['MASTER_PORT'], ahex(_packet))
def send_xlxmaster(self, radio, xlx, mastersock):
radio3 = int.from_bytes(radio, 'big').to_bytes(3, 'big')
radio4 = int.from_bytes(radio, 'big').to_bytes(4, 'big')
xlx3 = xlx.to_bytes(3, 'big')
streamid = randint(0,255).to_bytes(1, 'big')+randint(0,255).to_bytes(1, 'big')+randint(0,255).to_bytes(1, 'big')+randint(0,255).to_bytes(1, 'big')
# Wait for .5 secs for the XLX to log us in
for packetnr in range(5):
if packetnr < 3:
# First 3 packets, voice start, stream type e1
strmtype = 225
payload = bytearray.fromhex('4f2e00b501ae3a001c40a0c1cc7dff57d75df5d5065026f82880bd616f13f185890000')
else:
# Last 2 packets, voice end, stream type e2
strmtype = 226
payload = bytearray.fromhex('4f410061011e3a781c30a061ccbdff57d75df5d2534425c02fe0b1216713e885ba0000')
packetnr1 = packetnr.to_bytes(1, 'big')
strmtype1 = strmtype.to_bytes(1, 'big')
_packet = b''.join([DMRD, packetnr1, radio3, xlx3, radio4, strmtype1, streamid, payload])
self.transport.write(_packet, mastersock)
# KEEP THE FOLLOWING COMMENTED OUT UNLESS YOU'RE DEBUGGING DEEPLY!!!!
#logger.debug('(%s) XLX Module Change Packet: %s', self._system, ahex(_packet))
return
def dmrd_received(self, _peer_id, _rf_src, _dst_id, _seq, _slot, _call_type, _frame_type, _dtype_vseq, _stream_id, _data): def dmrd_received(self, _peer_id, _rf_src, _dst_id, _seq, _slot, _call_type, _frame_type, _dtype_vseq, _stream_id, _data):
pass pass
@ -631,6 +661,11 @@ class HBSYSTEM(DatagramProtocol):
self._stats['CONNECTION'] = 'YES' self._stats['CONNECTION'] = 'YES'
self._stats['CONNECTED'] = time() self._stats['CONNECTED'] = time()
logger.info('(%s) Connection to Master Completed', self._system) logger.info('(%s) Connection to Master Completed', self._system)
# If we are an XLX, send the XLX module request here.
if self._config['MODE'] == 'XLXPEER':
self.send_xlxmaster(self._config['RADIO_ID'], int(4000), self._config['MASTER_SOCKADDR'])
self.send_xlxmaster(self._config['RADIO_ID'], self._config['XLXMODULE'], self._config['MASTER_SOCKADDR'])
logger.info('(%s) Sending XLX Module request', self._system)
else: else:
self._stats['CONNECTION'] = 'NO' self._stats['CONNECTION'] = 'NO'
logger.error('(%s) Master ACK Contained wrong ID - Connection Reset', self._system) logger.error('(%s) Master ACK Contained wrong ID - Connection Reset', self._system)

6
install.sh Executable file
View File

@ -0,0 +1,6 @@
#! /bin/bash
# Install the required support programs
apt-get install python3 python3-pip -y
pip3 install -r requirements.txt

View File

@ -1,5 +1,5 @@
python>=3.5.0
bitstring>=3.1.5 bitstring>=3.1.5
bitarray>=0.8.1 bitarray>=0.8.1
Twisted>=16.3.0 Twisted>=16.3.0
dmr_utils3>=0.1.19 dmr_utils3>=0.1.19
configparser>=3.0.0

View File

@ -15,7 +15,9 @@ configuration file.
* SYSTEM - The name of the sytem as listed in the main hblink configuration file (e.g. hblink.cfg) * SYSTEM - The name of the sytem as listed in the main hblink configuration file (e.g. hblink.cfg)
This MUST be the exact same name as in the main config file!!! This MUST be the exact same name as in the main config file!!!
* TS - Timeslot used for matching traffic to this confernce bridge * TS - Timeslot used for matching traffic to this confernce bridge
XLX connections should *ALWAYS* use TS 2 only.
* TGID - Talkgroup ID used for matching traffic to this conference bridge * TGID - Talkgroup ID used for matching traffic to this conference bridge
XLX connections should *ALWAYS* use TG 9 only.
* ON and OFF are LISTS of Talkgroup IDs used to trigger this system off and on. Even if you * ON and OFF are LISTS of Talkgroup IDs used to trigger this system off and on. Even if you
only want one (as shown in the ON example), it has to be in list format. None can be only want one (as shown in the ON example), it has to be in list format. None can be
handled with an empty list, such as " 'ON': [] ". handled with an empty list, such as " 'ON': [] ".