diff --git a/config.py b/config.py index e76fbf4..7600642 100755 --- a/config.py +++ b/config.py @@ -199,6 +199,52 @@ def build_config(_config_file): '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': CONFIG['SYSTEMS'].update({section: { 'MODE': config.get(section, 'MODE'), diff --git a/hblink-SAMPLE.cfg b/hblink-SAMPLE.cfg index 2a50767..c2e789d 100755 --- a/hblink-SAMPLE.cfg +++ b/hblink-SAMPLE.cfg @@ -209,4 +209,36 @@ OPTIONS: USE_ACL: True SUB_ACL: DENY:1 TGID_TS1_ACL: PERMIT:ALL -TGID_TS2_ACL: PERMIT:ALL \ No newline at end of file +TGID_TS2_ACL: PERMIT:ALL + +[XLX-1] +MODE: XLXPEER +ENABLED: True +LOOSE: True +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 diff --git a/hblink.py b/hblink.py index f8ac097..7896396 100755 --- a/hblink.py +++ b/hblink.py @@ -223,6 +223,13 @@ class HBSYSTEM(DatagramProtocol): self.datagramReceived = self.peer_datagramReceived 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): # Set up periodic loop for tracking pings from peers. Run every 'PING_TIME' seconds self._system_maintenance = task.LoopingCall(self.maintenance_loop) @@ -282,6 +289,29 @@ class HBSYSTEM(DatagramProtocol): # 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)) + 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): pass @@ -500,7 +530,7 @@ class HBSYSTEM(DatagramProtocol): elif _command == DMRA: _peer_id = _data[4:8] logger.info('(%s) Recieved DMR Talker Alias from peer %s, subscriber %s', self._system, self._peers[_peer_id]['CALLSIGN'], int_id(_rf_src)) - + else: logger.error('(%s) Unrecognized command. Raw HBP PDU: %s', self._system, ahex(_data)) @@ -630,7 +660,12 @@ class HBSYSTEM(DatagramProtocol): self._stats['CONNECTION'] = 'YES' self._stats['CONNECTED'] = time() 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: self._stats['CONNECTION'] = 'NO' logger.error('(%s) Master ACK Contained wrong ID - Connection Reset', self._system)