def dmrd_received(self, _peer_id, _rf_src, _dst_id, _seq, _slot, _call_type, _frame_type, _dtype_vseq, _stream_id, _data): pkt_time = time() dmrpkt = _data[20:53] _bits = _data[15] if _call_type == 'group': # Is this is a new call stream? if (_stream_id != self.STATUS[_slot]['RX_STREAM_ID']): self.STATUS['RX_START'] = pkt_time logger.info('(%s) *START RECORDING* STREAM ID: %s SUB: %s (%s) REPEATER: %s (%s) TGID %s (%s), TS %s', \ self._system, int_id(_stream_id), get_alias(_rf_src, subscriber_ids), int_id(_rf_src), get_alias(_peer_id, peer_ids), int_id(_peer_id), get_alias(_dst_id, talkgroup_ids), int_id(_dst_id), _slot) self.CALL_DATA.append(_data) self.STATUS[_slot]['RX_STREAM_ID'] = _stream_id return # Final actions - Is this a voice terminator? if (_frame_type == const.HBPF_DATA_SYNC) and ( _dtype_vseq == const.HBPF_SLT_VTERM) and ( self.STATUS[_slot]['RX_TYPE'] != const.HBPF_SLT_VTERM) and (self.CALL_DATA): call_duration = pkt_time - self.STATUS['RX_START'] self.CALL_DATA.append(_data) logger.info('(%s) *END RECORDING* STREAM ID: %s', self._system, int_id(_stream_id)) sleep(2) logger.info('(%s) *START PLAYBACK* STREAM ID: %s SUB: %s (%s) REPEATER: %s (%s) TGID %s (%s), TS %s, Duration: %s', \ self._system, int_id(_stream_id), get_alias(_rf_src, subscriber_ids), int_id(_rf_src), get_alias(_peer_id, peer_ids), int_id(_peer_id), get_alias(_dst_id, talkgroup_ids), int_id(_dst_id), _slot, call_duration) if (int_id(_dst_id) == 9996): fout = open( '/home/mmdvm/' + str(int_id(_stream_id)) + '.ambe', 'w+b') for i in self.CALL_DATA: if (int_id(_dst_id) == 9996): fout.write(i) # self.send_system(i) self.send_system(i[:5] + bytes_3(int_id(_peer_id)) + i[8:]) #print(i) sleep(0.06) if (int_id(_dst_id) == 9996): fout.close() self.CALL_DATA = [] logger.info('(%s) *END PLAYBACK* STREAM ID: %s', self._system, int_id(_stream_id)) else: if self.CALL_DATA: self.CALL_DATA.append(_data) # Mark status variables for use later self.STATUS[_slot]['RX_RFS'] = _rf_src self.STATUS[_slot]['RX_TYPE'] = _dtype_vseq self.STATUS[_slot]['RX_TGID'] = _dst_id self.STATUS[_slot]['RX_TIME'] = pkt_time self.STATUS[_slot]['RX_STREAM_ID'] = _stream_id
def dmrd_received(self, _peer_id, _rf_src, _dst_id, _seq, _slot, _call_type, _frame_type, _dtype_vseq, _stream_id, _data): pkt_time = time() dmrpkt = _data[20:53] _bits = _data[15] if _call_type == 'group': # Is this is a new call stream? if (_stream_id != self.STATUS[_slot]['RX_STREAM_ID']): self.STATUS['RX_START'] = pkt_time logger.info('(%s) *START RECORDING* STREAM ID: %s SUB: %s (%s) REPEATER: %s (%s) TGID %s (%s), TS %s', \ self._system, int_id(_stream_id), get_alias(_rf_src, subscriber_ids), int_id(_rf_src), get_alias(_peer_id, peer_ids), int_id(_peer_id), get_alias(_dst_id, talkgroup_ids), int_id(_dst_id), _slot) self.CALL_DATA.append(_data) self.STATUS[_slot]['RX_STREAM_ID'] = _stream_id return # Final actions - Is this a voice terminator? if (_frame_type == const.HBPF_DATA_SYNC) and ( _dtype_vseq == const.HBPF_SLT_VTERM) and ( self.STATUS[_slot]['RX_TYPE'] != const.HBPF_SLT_VTERM) and (self.CALL_DATA): call_duration = pkt_time - self.STATUS['RX_START'] #Change the stream ID self.CALL_DATA.append(_data) logger.info('(%s) *END RECORDING* STREAM ID: %s', self._system, int_id(_stream_id)) sleep(2) _new_stream_id = bytes_4(randint(0x00, 0xFFFFFFFF)) logger.info('(%s) *START PLAYBACK* STREAM ID: %s SUB: %s (%s) REPEATER: %s (%s) TGID %s (%s), TS %s, Duration: %s', \ self._system, int_id(_new_stream_id), get_alias(_rf_src, subscriber_ids), int_id(_rf_src), get_alias(_peer_id, peer_ids), int_id(_peer_id), get_alias(_dst_id, talkgroup_ids), int_id(_dst_id), _slot, call_duration) for i in self.CALL_DATA: i = i[:16] + _new_stream_id + i[20:] self.send_system(i) sleep(0.06) self.CALL_DATA = [] logger.info('(%s) *END PLAYBACK* STREAM ID: %s', self._system, int_id(_new_stream_id)) else: if self.CALL_DATA: #Change the stream ID self.CALL_DATA.append(_data) # Mark status variables for use later self.STATUS[_slot]['RX_RFS'] = _rf_src self.STATUS[_slot]['RX_TYPE'] = _dtype_vseq self.STATUS[_slot]['RX_TGID'] = _dst_id self.STATUS[_slot]['RX_TIME'] = pkt_time self.STATUS[_slot]['RX_STREAM_ID'] = _stream_id
def alias_call(_id, _dict): alias = get_alias(_id, _dict, 'CALLSIGN') if type(alias) == list: for x, item in enumerate(alias): if item == None: alias.pop(x) return ', '.join(alias) else: return str(alias)
def alias_string(_id, _dict): alias = get_alias(_id, _dict, 'CALLSIGN', 'CITY', 'STATE') if type(alias) == list: for x, item in enumerate(alias): if item == None: alias.pop(x) return ', '.join(alias) else: return alias
def alias_tgid(_id, _dict): alias = get_alias(_id, _dict, 'NAME') if type(alias) == list: return str(alias[0]) else: return str(alias)
def dmrd_received(self, _peer_id, _rf_src, _dst_id, _seq, _slot, _call_type, _frame_type, _dtype_vseq, _stream_id, _data): pkt_time = time() dmrpkt = _data[20:53] _bits = _data[15] if _call_type == 'group': # Is this is a new call stream? new_stream = (_stream_id != self.STATUS[_slot]['RX_STREAM_ID']) if new_stream: self.STATUS[_slot]['RX_START'] = pkt_time self.STATUS[_slot]['RX_LOSS'] = 0 self.STATUS[_slot]['RX_SEQ'] = _seq logger.info('(%s) *CALL START* STREAM ID: %s SUB: %s (%s) PEER: %s (%s) TGID %s (%s), TS %s', \ self._system, int_id(_stream_id), get_alias(_rf_src, subscriber_ids), int_id(_rf_src), get_alias(_peer_id, peer_ids), int_id(_peer_id), get_alias(_dst_id, talkgroup_ids), int_id(_dst_id), _slot) else: # This could be much better, it will have errors during roll-over if _seq > (self.STATUS[_slot]['RX_SEQ'] + 1): #print(_seq, self.STATUS[_slot]['RX_SEQ']) self.STATUS[_slot]['RX_LOSS'] += _seq - ( self.STATUS[_slot]['RX_SEQ'] + 1) # Final actions - Is this a voice terminator? if (_frame_type == const.HBPF_DATA_SYNC) and ( _dtype_vseq == const.HBPF_SLT_VTERM) and ( self.STATUS[_slot]['RX_TYPE'] != const.HBPF_SLT_VTERM): call_duration = pkt_time - self.STATUS[_slot]['RX_START'] logger.info('(%s) *CALL END* STREAM ID: %s SUB: %s (%s) PEER: %s (%s) TGID %s (%s), TS %s, Loss: %s, Duration: %s', \ self._system, int_id(_stream_id), get_alias(_rf_src, subscriber_ids), int_id(_rf_src), get_alias(_peer_id, peer_ids), int_id(_peer_id), get_alias(_dst_id, talkgroup_ids), int_id(_dst_id), _slot, self.STATUS[_slot]['RX_LOSS'], call_duration) for _target in self._CONFIG['SYSTEMS']: if _target != self._system: _target_status = systems[_target].STATUS _target_system = self._CONFIG['SYSTEMS'][_target] # BEGIN STANDARD CONTENTION HANDLING # # The rules for each of the 4 "ifs" below are listed here for readability. The Frame To Send is: # From a different group than last RX from this HBSystem, but it has been less than Group Hangtime # From a different group than last TX to this HBSystem, but it has been less than Group Hangtime # From the same group as the last RX from this HBSystem, but from a different subscriber, and it has been less than stream timeout # From the same group as the last TX to this HBSystem, but from a different subscriber, and it has been less than stream timeout # The "continue" at the end of each means the next iteration of the for loop that tests for matching rules # if ((_dst_id != _target_status[_slot]['RX_TGID']) and ((pkt_time - _target_status[_slot]['RX_TIME']) < _target_system['GROUP_HANGTIME'])): if _frame_type == const.HBPF_DATA_SYNC and _dtype_vseq == const.HBPF_SLT_VHEAD and self.STATUS[ _slot]['RX_STREAM_ID'] != _stream_id: logger.info( '(%s) Call not routed to TGID %s, target active or in group hangtime: HBSystem: %s, TS: %s, TGID: %s', self._system, int_id(_dst_id), _target, _slot, int_id(_target_status[_slot]['RX_TGID'])) continue if ((_dst_id != _target_status[_slot]['TX_TGID']) and ((pkt_time - _target_status[_slot]['TX_TIME']) < _target_system['GROUP_HANGTIME'])): if _frame_type == const.HBPF_DATA_SYNC and _dtype_vseq == const.HBPF_SLT_VHEAD and self.STATUS[ _slot]['RX_STREAM_ID'] != _stream_id: logger.info( '(%s) Call not routed to TGID%s, target in group hangtime: HBSystem: %s, TS: %s, TGID: %s', self._system, int_id(_dst_id), _target, _slot, int_id(_target_status[_slot]['TX_TGID'])) continue if (_dst_id == _target_status[_slot]['RX_TGID']) and ( (pkt_time - _target_status[_slot]['RX_TIME']) < const.STREAM_TO): if _frame_type == const.HBPF_DATA_SYNC and _dtype_vseq == const.HBPF_SLT_VHEAD and self.STATUS[ _slot]['RX_STREAM_ID'] != _stream_id: logger.info( '(%s) Call not routed to TGID%s, matching call already active on target: HBSystem: %s, TS: %s, TGID: %s', self._system, int_id(_dst_id), _target, _slot, int_id(_target_status[_slot]['RX_TGID'])) continue if (_dst_id == _target_status[_slot]['TX_TGID']) and ( _rf_src != _target_status[_slot]['TX_RFS']) and ( (pkt_time - _target_status[_slot]['TX_TIME']) < const.STREAM_TO): if _frame_type == const.HBPF_DATA_SYNC and _dtype_vseq == const.HBPF_SLT_VHEAD and self.STATUS[ _slot]['RX_STREAM_ID'] != _stream_id: logger.info( '(%s) Call not routed for subscriber %s, call route in progress on target: HBSystem: %s, TS: %s, TGID: %s, SUB: %s', self._system, int_id(_rf_src), _target, _slot, int_id(_target_status[_slot]['TX_TGID']), int_id(_target_status[_slot]['TX_RFS'])) continue # ACL Processing if self._CONFIG['GLOBAL']['USE_ACL']: if not acl_check(_rf_src, self._CONFIG['GLOBAL']['SUB_ACL']): if _stream_id != _target_status[_slot][ 'TX_STREAM_ID']: logger.info( '(%s) CALL DROPPED ON EGRESS WITH STREAM ID %s FROM SUBSCRIBER %s BY GLOBAL ACL', _target, int_id(_stream_id), int_id(_rf_src)) _target_status[_slot][ 'TX_STREAM_ID'] = _stream_id continue if _slot == 1 and not acl_check( _dst_id, self._CONFIG['GLOBAL']['TG1_ACL']): if _stream_id != _target_status[_slot][ 'TX_STREAM_ID']: logger.info( '(%s) CALL DROPPED ON EGRESS WITH STREAM ID %s ON TGID %s BY GLOBAL TS1 ACL', _target, int_id(_stream_id), int_id(_dst_id)) _target_status[_slot][ 'TX_STREAM_ID'] = _stream_id continue if _slot == 2 and not acl_check( _dst_id, self._CONFIG['GLOBAL']['TG2_ACL']): if _stream_id != _target_status[_slot][ 'TX_STREAM_ID']: logger.info( '(%s) CALL DROPPED ON EGRESS WITH STREAM ID %s ON TGID %s BY GLOBAL TS2 ACL', _target, int_id(_stream_id), int_id(_dst_id)) _target_status[_slot][ 'TX_STREAM_ID'] = _stream_id continue if _target_system['USE_ACL']: if not acl_check(_rf_src, _target_system['SUB_ACL']): if _stream_id != _target_status[_slot][ 'TX_STREAM_ID']: logger.info( '(%s) CALL DROPPED ON EGRESS WITH STREAM ID %s FROM SUBSCRIBER %s BY SYSTEM ACL', _target, int_id(_stream_id), int_id(_rf_src)) _target_status[_slot][ 'TX_STREAM_ID'] = _stream_id continue if _slot == 1 and not acl_check( _dst_id, _target_system['TG1_ACL']): if _stream_id != _target_status[_slot][ 'TX_STREAM_ID']: logger.info( '(%s) CALL DROPPED ON EGRESS WITH STREAM ID %s ON TGID %s BY SYSTEM TS1 ACL', _target, int_id(_stream_id), int_id(_dst_id)) _target_status[_slot][ 'TX_STREAM_ID'] = _stream_id continue if _slot == 2 and not acl_check( _dst_id, _target_system['TG2_ACL']): if _stream_id != _target_status[_slot][ 'TX_STREAM_ID']: logger.info( '(%s) CALL DROPPED ON EGRESS WITH STREAM ID %s ON TGID %s BY SYSTEM TS2 ACL', _target, int_id(_stream_id), int_id(_dst_id)) _target_status[_slot][ 'TX_STREAM_ID'] = _stream_id continue # Record this stuff for later # Is this a new call stream? if new_stream: # Record the DST TGID and Stream ID _target_status[_slot]['TX_START'] = pkt_time _target_status[_slot]['TX_TGID'] = _dst_id _target_status[_slot]['TX_RFS'] = _rf_src _target_status[_slot]['TX_PEER'] = _peer_id _target_status[_slot]['TX_STREAM_ID'] = _stream_id _target_status[_slot]['TX_TIME'] = pkt_time systems[_target].send_system(_data) #logger.debug('(%s) Packet routed to system: %s', self._system, _target) # Mark status variables for use later self.STATUS[_slot]['RX_RFS'] = _rf_src self.STATUS[_slot]['RX_SEQ'] = _seq self.STATUS[_slot]['RX_TYPE'] = _dtype_vseq self.STATUS[_slot]['RX_TGID'] = _dst_id self.STATUS[_slot]['RX_TIME'] = pkt_time self.STATUS[_slot]['RX_STREAM_ID'] = _stream_id
def dmrd_received(self, _peer_id, _rf_src, _dst_id, _seq, _slot, _call_type, _frame_type, _dtype_vseq, _stream_id, _data): # Capture data headers global n_packet_assembly #logger.info(_dtype_vseq) if int_id(_dst_id) == data_id: #logger.info(type(_seq)) if type(_seq) is bytes: pckt_seq = int.from_bytes(_seq, 'big') else: pckt_seq = _seq if _call_type == call_type or ( _call_type == 'vcsbk' and pckt_seq > 3): #int.from_bytes(_seq, 'big') > 3 ): if _dtype_vseq == 6 or _dtype_vseq == 'group': global btf logger.info('Header from ' + str(int_id(_rf_src))) logger.info(ahex(bptc_decode(_data))) logger.info('Blocks to follow: ' + str(ba2num(bptc_decode(_data)[65:72]))) btf = ba2num(bptc_decode(_data)[65:72]) # Data blocks at 1/2 rate, see https://github.com/g4klx/MMDVM/blob/master/DMRDefines.h for data types. _dtype_seq defined here also if _dtype_vseq == 7: btf = btf - 1 logger.info('Block #: ' + str(btf)) #logger.info(_seq) global packet_assembly logger.info('Data block from ' + str(int_id(_rf_src))) logger.info(ahex(bptc_decode(_data))) if _seq == 0: n_packet_assembly = 0 packet_assembly = '' if btf < btf + 1: n_packet_assembly = n_packet_assembly + 1 packet_assembly = packet_assembly + str( bptc_decode(_data) ) #str((decode_full_lc(b_packet)).strip('bitarray(')) # Use block 0 as trigger. $GPRMC must also be in string to indicate NMEA. # This triggers the APRS upload if btf == 0: #_seq == 12: final_packet = bitarray( re.sub("\)|\(|bitarray|'", '', packet_assembly)).tobytes().decode( 'utf-8', 'ignore') if '$GPRMC' in final_packet: logger.info(final_packet + '\n') logger.info('Latitude: ' + re.sub(',', '', final_packet[29:40]) + ' Longitude: ' + re.sub(',', '', final_packet[41:53]) + ' Direction: ' + re.sub(',', '', final_packet[58:62]) + ' Speed: ' + re.sub(',', '', final_packet[53:58])) # Begin APRS format and upload aprs_loc_packet = str( get_alias(int_id(_rf_src), subscriber_ids) ) + '-' + str(user_ssid) + '>APRS,TCPIP*:/' + str( datetime.datetime.utcnow().strftime("%H%M%Sh") ) + str(final_packet[29:36]) + str( final_packet[39]) + '/' + str( re.sub(',', '', final_packet[41:49]) ) + str( final_packet[52] ) + '[/' + aprs_comment + ' DMR ID: ' + str( int_id(_rf_src)) logger.info(aprs_loc_packet) try: # Try parse of APRS packet. If it fails, it will not upload to APRS-IS aprslib.parse(aprs_loc_packet) AIS = aprslib.IS(aprs_callsign, passwd=aprs_passcode, host=aprs_server, port=aprs_port) AIS.connect() AIS.sendall(aprs_loc_packet) AIS.close() except: logger.info( 'Failed to parse packet. Packet may be deformed. Not uploaded.' ) # Get callsign based on DMR ID #logger.info(get_alias(int_id(_rf_src), subscriber_ids)) # End APRS-IS upload else: logger.info(final_packet) packet_assembly = '' logger.info(_seq) #logger.info(_dtype_vseq) logger.info(ahex(bptc_decode(_data)).decode('utf-8', 'ignore')) #logger.info(bitarray(re.sub("\)|\(|bitarray|'", '', str(bptc_decode(_data)).tobytes().decode('utf-8', 'ignore')))) else: pass