def set_freq(self, freq): if self.frequency == freq: return True old_freq = self.frequency self.frequency = freq if not self.demod.set_relative_frequency( self.device.offset + self.device.frequency + self.device.fractional_corr - freq): # First attempt relative tune if self.device.tunable: # then hard tune if allowed self.device.frequency = self.frequency self.device.src.set_center_freq(self.frequency + self.device.offset) self.device.fractional_corr = int( (int(round(self.device.ppm)) - self.device.ppm) * (self.device.frequency / 1e6)) # Calc frac ppm using new freq self.demod.set_relative_frequency(self.device.offset + self.device.frequency + self.device.fractional_corr - freq) if self.verbosity >= 9: sys.stderr.write( "%s [%d] Hardware tune: dev_freq(%d), dev_off(%d), dev_frac(%d), tune_freq(%d)\n" % (log_ts.get(), self.msgq_id, self.device.frequency, self.device.offset, self.device.fractional_corr, (self.device.frequency - (self.device.offset + self.device.frequency + self.device.fractional_corr - freq)))) else: # otherwise fail and reset to prev freq self.demod.set_relative_frequency(self.device.offset + self.device.frequency + self.device.fractional_corr - old_freq) self.frequency = old_freq if self.verbosity: sys.stderr.write( "%s [%d] Unable to tune %s to frequency %f\n" % (log_ts.get(), self.msgq_id, self.name, (freq / 1e6))) return False else: if self.verbosity >= 9: sys.stderr.write( "%s [%d] Relative tune: dev_freq(%d), dev_off(%d), dev_frac(%d), tune_freq(%d)\n" % (log_ts.get(), self.msgq_id, self.device.frequency, self.device.offset, self.device.fractional_corr, (self.device.frequency - (self.device.offset + self.device.frequency + self.device.fractional_corr - freq)))) if 'fft' in self.sinks: self.sinks['fft'][0].set_center_freq(self.device.frequency) self.sinks['fft'][0].set_relative_freq(self.device.frequency - freq) if self.verbosity >= 9: sys.stderr.write("%s [%d] Tuning to frequency %f\n" % (log_ts.get(), self.msgq_id, (freq / 1e6))) self.demod.reset() # reset gardner-costas tracking loop self.decoder.sync_reset() # reset frame_assembler return True
def process_qmsg(self, msg): # Handle UI requests RX_COMMANDS = 'skip lockout hold whitelist reload'.split() if msg is None: return True s = msg.to_string() if type(s) is not str and isinstance(s, bytes): # should only get here if python3 s = s.decode() if s == 'quit': return True elif s == 'update': # UI initiated update request self.ui_last_update = time.time() self.ui_freq_update() if self.trunking is None or self.trunk_rx is None: return False js = self.trunk_rx.to_json() # extract data from trunking module msg = gr.message().make_from_string(js, -4, 0, 0) self.ui_in_q.insert_tail(msg) # send info back to UI self.ui_plot_update() elif s == 'toggle_plot': if not self.get_interactive(): sys.stderr.write( "%s Cannot start plots for non-realtime (replay) sessions\n" % log_ts.get()) return plot_type = int(msg.arg1()) msgq_id = int(msg.arg2()) self.find_channel(msgq_id).toggle_plot(plot_type) elif s == 'adj_tune': freq = msg.arg1() msgq_id = int(msg.arg2()) self.find_channel(msgq_id).adj_tune(freq) elif s == 'set_debug': dbglvl = int(msg.arg1()) self.set_debug(dbglvl) elif s == 'get_config': if self.terminal is not None and self.terminal_config is not None: self.terminal_config['json_type'] = "terminal_config" js = json.dumps(self.terminal_config) msg = gr.message().make_from_string(js, -4, 0, 0) self.ui_in_q.insert_tail(msg) # send configuration back to UI pass else: return False elif s == 'dump_tgids': self.trunk_rx.dump_tgids() elif s == 'watchdog': if self.ui_last_update > 0 and ( time.time() > (self.ui_last_update + self.ui_timeout)): self.ui_last_update = 0 sys.stderr.write("%s UI Timeout\n" % log_ts.get()) for chan in self.channels: chan.close_plots() elif s in RX_COMMANDS: if self.trunking is not None and self.trunk_rx is not None: self.trunk_rx.ui_command(s, msg.arg1(), msg.arg2()) return False
def enqueue(self, addr, grp, cmd, ts): grp_str = "G" if (grp != 0) else "I" if self.is_chan(cmd): freq = self.get_freq(cmd) if self.debug >= 9: sys.stderr.write("%s [%d] SMARTNET OSW (0x%04x,%s,0x%03x,%f)\n" % (log_ts.get(), self.msgq_id, addr, grp_str, cmd, freq)) else: freq = 0.0 if self.debug >= 9: sys.stderr.write("%s [%d] SMARTNET OSW (0x%04x,%s,0x%03x)\n" % (log_ts.get(), self.msgq_id, addr, grp_str, cmd)) self.osw_q.append((addr, (grp != 0), cmd, self.is_chan(cmd), freq, ts))
def load_bl_wl(self): self.skiplist = self.control.get_skiplist() if 'blacklist' in self.config and self.config['blacklist'] != "": sys.stderr.write("%s [%d] reading channel blacklist file: %s\n" % (log_ts.get(), self.msgq_id, self.config['blacklist'])) self.blacklist = get_int_dict(self.config['blacklist'], self.msgq_id) else: self.blacklist = self.control.get_blacklist() if 'whitelist' in self.config and self.config['whitelist'] != "": sys.stderr.write("%s [%d] reading channel whitelist file: %s\n" % (log_ts.get(), self.msgq_id, self.config['whitelist'])) self.whitelist = get_int_dict(self.config['whitelist'], self.msgq_id) else: self.whitelist = self.control.get_whitelist()
def error_tracking(self): UPDATE_TIME = 3.0 if self.last_error_update + UPDATE_TIME > time.time() \ or self.last_change_freq_at + UPDATE_TIME > time.time(): return self.last_error_update = time.time() band = self.demod.get_error_band() freq_error = self.demod.get_freq_error() if band: self.error_band += band if band or abs( freq_error ) >= 200: # avoid hunting by only compensating errors over 200hz self.freq_correction += freq_error * 0.15 do_freq_update = 1 else: do_freq_update = 0 if self.freq_correction > 600: self.freq_correction -= 1200 self.error_band += 1 elif self.freq_correction < -600: self.freq_correction += 1200 self.error_band -= 1 self.tuning_error = self.error_band * 1200 + self.freq_correction e = 0 if self.last_change_freq > 0: err_ppm = round( (self.tuning_error * 1e6) / float(self.last_change_freq)) err_hz = -int(self.tuning_error - (err_ppm * (self.last_change_freq / 1e6))) if self.options.verbosity >= 10: sys.stderr.write('%s frequency_tracking\t%d\t%d\t%d\t%d\t%d\n' % (log_ts.get(), freq_error, self.error_band, self.tuning_error, err_ppm, err_hz)) if do_freq_update: corrected_ppm = self.options.freq_corr + err_ppm # compute new device ppm based on starting point plus adjustment if corrected_ppm != self.last_set_ppm: self.src.set_freq_corr(corrected_ppm) self.last_set_ppm = corrected_ppm if self.options.verbosity >= 1: sys.stderr.write( '%s Adjusting tuning correction: ppm(%d) ["-q %d"]\n' % (log_ts.get(), corrected_ppm, corrected_ppm)) self.options.fine_tune = err_hz # replace existing fine_tune with new correction value self.set_freq(self.target_freq) if self.options.verbosity >= 2: sys.stderr.write( '%s Adjusting tuning: ppm(%d), fine_tune(%d) ["-q %d -d %d"]\n' % (log_ts.get(), corrected_ppm, err_hz, corrected_ppm, err_hz))
def read_tags_file(self, tags_file): import csv try: with open(tags_file, 'r') as csvfile: sreader = csv.reader(decomment(csvfile), delimiter='\t', quotechar='"', quoting=csv.QUOTE_ALL) for row in sreader: if len(row) < 2: continue try: if ord(row[0][0]) == 0xfeff: row[0] = row[0][1:] # remove UTF8_BOM (Python2 version) if ord(row[0][0]) == 0xef and ord(row[0][1]) == 0xbb and ord(row[0][2]) == 0xbf: row[0] = row[0][3:] # remove UTF8_BOM (Python3 version) tgid = int(row[0]) tag = utf_ascii(row[1]) except (IndexError, ValueError) as ex: continue if len(row) >= 3: try: prio = int(row[2]) except ValueError as ex: prio = TGID_DEFAULT_PRIO else: prio = TGID_DEFAULT_PRIO if tgid not in self.talkgroups: self.add_default_tgid(tgid) self.talkgroups[tgid]['tag'] = tag self.talkgroups[tgid]['prio'] = prio sys.stderr.write("%s [%d] setting tgid(%d), prio(%d), tag(%s)\n" % (log_ts.get(), self.msgq_id, tgid, prio, tag)) except IOError as ex: sys.stderr.write("%s [%d] Error: %s: %s\n" % (log_ts.get(), self.msgq_id, ex.strerror, tags_file))
def __init__(self, name, config): gr.hier_block2.__init__( self, "op25_wavsrc_f", gr.io_signature(0, 0, 0), # Input signature gr.io_signature(1, 1, gr.sizeof_float)) # Output signature self.config = config self.name = name self.freq = 0 # load config self.wav_file = str(from_dict(config, 'wav_file', "")) self.wav_size = int(from_dict(config, 'wav_size', 1)) self.wav_gain = float(from_dict(config, 'wav_gain', 1.0)) # Create the source block self.wavsrc = blocks.wavfile_source(self.wav_file) self.rate = self.wavsrc.sample_rate() self.size = self.wavsrc.bits_per_sample() self.chans = self.wavsrc.channels() sys.stderr.write( "%s [%s] Enabling WAV file source: rate=%d, bit=%d, channels=%d\n" % (log_ts.get(), name, self.rate, self.size, self.chans)) # Create the throttle to set playback rate self.throttle = blocks.throttle(gr.sizeof_float, self.rate) # Gain self.gain = blocks.multiply_const_ff(self.wav_gain) # Connect src and throttle self.connect(self.wavsrc, self.throttle, self.gain, self)
def expire_talkgroup(self, tgid=None, update_meta=True, reason="unk", auto_hold=True): expire_time = time.time() self.nbfm_ctrl(self.msgq_id, False) # disable nbfm self.slot_set({'tuner': self.msgq_id, 'slot': 4}) # disable p25cai if self.current_tgid is None: return self.talkgroups[self.current_tgid]['receiver'] = None self.talkgroups[self.current_tgid]['srcaddr'] = 0 self.talkgroups[self.current_tgid]['release_time'] = expire_time if self.debug > 1: sys.stderr.write( "%s [%d] releasing: tg(%d), freq(%f), reason(%s)\n" % (log_ts.get(), self.msgq_id, self.current_tgid, (self.tuned_frequency / 1e6), reason)) if auto_hold: self.hold_tgid = self.current_tgid self.hold_until = expire_time + TGID_HOLD_TIME self.current_tgid = None if update_meta: meta_update(self.meta_q)
def post_init(self): if self.debug >= 1: sys.stderr.write("%s [%d] Initializing DMR receiver\n" % (log_ts.get(), self.msgq_id)) if self.msgq_id == 0: self.tune_next_chan(msgq_id=0, chan=0, slot=0) else: self.tune_next_chan(msgq_id=1, chan=0, slot=4)
def configure_p25_tdma(self, params): set_tdma = False if 'tdma' in params and params['tdma'] is not None: set_tdma = True self.decoder.set_slotid(params['tdma']) if set_tdma == self.tdma_state: return self.tdma_state = set_tdma if set_tdma: hash = '%x%x%x' % (params['nac'], params['sysid'], params['wacn']) if hash not in self.xor_cache: self.xor_cache[hash] = lfsr.p25p2_lfsr( params['nac'], params['sysid'], params['wacn']).xor_chars if self.verbosity >= 5: sys.stderr.write( "%s [%d] Caching TDMA xor mask for NAC: 0x%x, SYSID: 0x%x, WACN: 0x%x\n" % (log_ts.get(), self.msgq_id, params['nac'], params['sysid'], params['wacn'])) self.decoder.set_xormask(self.xor_cache[hash]) rate = 6000 else: rate = self.config['symbol_rate'] self.symbol_rate = rate self.demod.set_omega(rate) if 'eye' in self.sinks: self.sinks['eye'][0].set_sps(self.config['if_rate'] / rate)
def read_tags_file(self, tags_file): import csv try: with open(tags_file, 'r') as csvfile: sreader = csv.reader(csvfile, delimiter='\t', quotechar='"', quoting=csv.QUOTE_ALL) for row in sreader: try: tgid = int(row[0]) tag = utf_ascii(row[1]) except (IndexError, ValueError) as ex: continue if len(row) >= 3: try: prio = int(row[2]) except ValueError as ex: prio = TGID_DEFAULT_PRIO else: prio = TGID_DEFAULT_PRIO if tgid not in self.talkgroups: self.add_default_tgid(tgid) self.talkgroups[tgid]['tag'] = tag self.talkgroups[tgid]['prio'] = prio sys.stderr.write("%s [%d] setting tgid(%d), prio(%d), tag(%s)\n" % (log_ts.get(), self.msgq_id, tgid, prio, tag)) except IOError as ex: sys.stderr.write("%s [%d] Error: %s: %s\n" % (log_ts.get(), self.msgq_id, ex.strerror, tags_file))
def change_freq(self, params): tuner = params['tuner'] if (tuner < 0) or (tuner > len(self.channels)): if self.verbosity: sys.stderr.write("%s No %s channel available for tuning\n" % (log_ts.get(), params['tuner'])) return False chan = self.channels[tuner] if 'sigtype' in params and params['sigtype'] == "P25": # P25 specific config chan.configure_p25_tdma(params) if not chan.set_freq(params['freq']): chan.set_slot(0) return False if 'slot' in params: chan.set_slot(params['slot']) if 'chan' in params: self.trunk_rx.receivers[tuner].current_chan = params['chan'] if 'state' in params: self.trunk_rx.receivers[tuner].current_state = params['state'] if 'type' in params: self.trunk_rx.receivers[tuner].current_type = params['type'] if 'time' in params: self.trunk_rx.receivers[tuner].tune_time = params['time'] return True
def __init__(self, name, config): gr.hier_block2.__init__(self, "op25_iqsrc_c", gr.io_signature(0, 0, 0), # Input signature gr.io_signature(1, 1, gr.sizeof_gr_complex)) # Output signature self.config = config self.name = name self.is_dsd_file = False self.freq = 0 self.ts = 0 sys.stderr.write("%s [%s] Enabling IQ file source\n" % (log_ts.get(), name)) # load config self.iq_file = str(from_dict(config, 'iq_file', "")) self.iq_seek = int(from_dict(config, 'iq_seek', 0)) self.iq_size = int(from_dict(config, 'iq_size', 1)) self.iq_signed = bool(from_dict(config, 'iq_signed', False)) self.rate = int(from_dict(config, 'rate', 2400000)) # Create the source block self.iqsrc = op25_repeater.iqfile_source(self.iq_size, self.iq_file, self.iq_signed, self.iq_seek, 0) if self.iqsrc.is_dsd(): self.is_dsd_file = True self.rate = self.iqsrc.get_dsd_rate() self.freq = self.iqsrc.get_dsd_freq() self.ts = self.iqsrc.get_dsd_ts() # Create the throttle to set playback rate self.throttle = blocks.throttle(gr.sizeof_gr_complex, self.rate) # Connect src and throttle self.connect(self.iqsrc, self.throttle, self)
def update_voice_frequency(self, float_freq, tgid=None, srcaddr=-1, mode=-1, ts=time.time()): if not float_freq: # e.g., channel identifier not yet known return False frequency = int(float_freq * 1e6) # use integer not float as dictionary keys rc = self.update_talkgroups(frequency, tgid, srcaddr, mode, ts) base_tgid = tgid & 0xfff0 tgid_stat = tgid & 0x000f if frequency not in self.voice_frequencies: self.voice_frequencies[frequency] = {'counter': 0} sorted_freqs = collections.OrderedDict( sorted(self.voice_frequencies.items())) self.voice_frequencies = sorted_freqs if self.debug >= 5: sys.stderr.write('%s [%d] new freq=%f\n' % (log_ts.get(), self.msgq_id, (frequency / 1e6))) if 'tgid' not in self.voice_frequencies[frequency]: self.voice_frequencies[frequency]['tgid'] = [None] self.voice_frequencies[frequency]['tgid'] = base_tgid self.voice_frequencies[frequency]['counter'] += 1 self.voice_frequencies[frequency]['time'] = time.time() return rc
def get_int_dict(s, _id=0): # used to read blacklist/whitelist files d = {} try: with open(s, "r") as f: for v in f: v = v.split("\t", 1) # split on tab try: v0 = int( v[0]) # first parameter is tgid or start of tgid range v1 = v0 if (len(v) > 1) and ( int(v[1]) > v0 ): # second parameter if present is end of tgid range v1 = int(v[1]) for tg in range(v0, (v1 + 1)): if tg not in d: # is this a new tg? d[tg] = [ ] # if so, add to dict (key only, value null) sys.stderr.write( '%s [%s] added talkgroup %d from %s\n' % (log_ts.get(), _id, tg, s)) except (IndexError, ValueError) as ex: continue f.close() except (IOError) as ex: sys.stderr.write("%s: %s\n" % (ex.strerror, s)) return dict.fromkeys(d)
def write(self, pcm_data): datalen = len(pcm_data) n_frames = c_ulong(datalen / self.framesize) c_data = c_char_p(pcm_data) ret = 0 if (self.c_pcm.value == None): sys.stderr.write("PCM device is closed\n") return -1 ret = self.libasound.snd_pcm_writei(self.c_pcm, cast(c_data, POINTER(c_void_p)), n_frames) if (ret < 0): if (ret == -errno.EPIPE): # underrun if (LOG_AUDIO_XRUNS): sys.stderr.write("%s PCM underrun\n" % log_ts.get()) ret = self.libasound.snd_pcm_recover(self.c_pcm, ret, 1) if (ret >= 0): ret = self.libasound.snd_pcm_writei(self.c_pcm, cast(c_data, POINTER(c_void_p)), n_frames) else: ret = self.libasound.snd_pcm_prepare(self.c_pcm) ret = self.libasound.snd_pcm_writei(self.c_pcm, cast(c_data, POINTER(c_void_p)), n_frames) elif (ret == -errno.ESTRPIPE): # suspended while True: ret = self.libasound.snd_pcm_resume(self.c_pcm) if (ret != -errno.EAGAIN): break time.sleep(1) if (ret < 0): ret = self.libasound.snd_pcm_prepare(self.c_pcm) elif (ret < 0): # other error ret = self.libasound.snd_pcm_prepare(self.c_pcm) return ret
def process_qmsg(self, msg, curr_time): rc = False m_type = ctypes.c_int16(msg.type() & 0xffff).value m_rxid = int(msg.arg1()) >> 1 m_ts = float(msg.arg2()) if (m_type == -1): # Voice Channel Timeout pass elif (m_type == -4 ): # P25 sync established (indicates this is a digital channel) if self.current_tgid is not None: if self.debug >= 9: sys.stderr.write( "%s [%d] digital sync detected: tg(%d), freq(%f), mode(%d)\n" % (log_ts.get(), self.msgq_id, self.current_tgid, (self.tuned_frequency / 1e6), self.talkgroups[self.current_tgid]['mode'])) self.nbfm_ctrl(self.msgq_id, False) # disable nbfm self.talkgroups[ self.current_tgid]['mode'] = 1 # set mode to digital elif (m_type == 3 ): # DUID-3 (call termination without channel release) pass elif (m_type == 15): # DUID-15 (call termination with channel release) self.expire_talkgroup(reason="duid15") rc = True return rc
def process_qmsg(self, msg, curr_time): m_proto = ctypes.c_int16(msg.type( ) >> 16).value # upper 16 bits of msg.type() is signed protocol if m_proto != 2: # Smartnet m_proto=2 return False m_type = ctypes.c_int16(msg.type() & 0xffff).value m_rxid = int(msg.arg1()) >> 1 m_ts = float(msg.arg2()) if (m_type == -1): # Control Channel Timeout if self.debug > 10: sys.stderr.write("%s [%d] control channel timeout\n" % (log_ts.get(), self.msgq_id)) self.cc_retries += 1 if self.cc_retries >= CC_TIMEOUT_RETRIES: self.tune_next_cc() elif (m_type == 0): # OSW Receieved s = msg.to_string() osw_addr = (ord(s[0]) << 8) + ord(s[1]) osw_grp = ord(s[2]) osw_cmd = (ord(s[3]) << 8) + ord(s[4]) self.enqueue(osw_addr, osw_grp, osw_cmd, m_ts) self.stats['osw_count'] += 1 self.last_osw = m_ts rc = False rc |= self.process_osws() rc |= self.expire_talkgroups(curr_time) return rc
def load_json(self, metacfg): try: with open(metacfg) as json_file: self.cfg = json.load(json_file) except (ValueError, KeyError): sys.stderr.write( "%s meta_server::load_json(): Error reading metadata config file: %s\n" % (log_ts.get(), metacfg))
def skiplist_update(self, start_time): expired_tgs = [tg for tg in list(self.skiplist.keys()) if self.skiplist[tg] is not None and self.skiplist[tg] < start_time] for tg in expired_tgs: self.skiplist.pop(tg) if self.debug > 1: sys.stderr.write("%s [%d] removing expired skiplist: tg(%d)\n" % (log_ts.get(), self.msgq_id, tg));
def __init__(self, debug=0, frequency_set=None, nac_set=None, slot_set=None, nbfm_ctrl=None, chans={}): self.frequency_set = frequency_set self.slot_set = slot_set self.nac_set = nac_set self.debug = debug self.receivers = {} self.chans = {} for _chan in chans: if not 'lcn' in _chan: sys.stderr.write("%s Trunking chan[%d] has no lcn defined\n" % (log_ts.get(), chans.index(_chan))) continue self.chans[_chan['lcn']] = dmr_chan(debug, _chan['lcn'], get_frequency(from_dict(_chan, 'frequency', 0.0))) sys.stderr.write("%s Configuring channel lcn(%d), freq(%f), cc(%d)\n" % (log_ts.get(), _chan['lcn'], get_frequency(from_dict(_chan, 'frequency', 0.0))/1e6, int(from_dict(_chan, 'cc', 0)))) if len(self.chans) == 0: sys.stderr.write("%s Trunking has no valid chans, aborting\n" % (log_ts.get())) exit(1)
def configure_channels(self, config): self.channels = [] for cfg in config: dev = self.find_device(cfg) if (dev is None) and 'frequency' in cfg: sys.stderr.write("* * * Frequency %d not within spectrum band of any device - ignoring!\n" % cfg['frequency']) continue elif dev is None: sys.stderr.write("* * * Channel '%s' not attached to any device - ignoring!\n" % cfg['name']) continue elif dev.tunable: for ch in self.channels: if ch.device == dev: sys.stderr.write("* * * Channel '%s' cannot share a tunable device - ignoring!\n" % cfg['name']) dev = None break if dev == None: continue meta_s, meta_q = None, None if self.metadata is not None and 'meta_stream_name' in cfg and cfg['meta_stream_name'] != "" and cfg['meta_stream_name'] in self.meta_streams: meta_s, meta_q = self.meta_streams[cfg['meta_stream_name']] if self.trunking is not None: msgq_id = len(self.channels) chan = channel(cfg, dev, self.verbosity, msgq_id, self.rx_q, self) self.channels.append(chan) self.trunk_rx.add_receiver(msgq_id, config=cfg, meta_q=meta_q, freq=chan.frequency) else: msgq_id = -1 - len(self.channels) chan = channel(cfg, dev, self.verbosity, msgq_id, self.rx_q, self) self.channels.append(chan) if ("raw_input" in cfg) and (cfg['raw_input'] != ""): sys.stderr.write("%s Reading raw symbols from file: %s\n" % (log_ts.get(), cfg['raw_input'])) chan.raw_file = blocks.file_source(gr.sizeof_char, str(cfg['raw_input']), False) if ("raw_seek" in cfg) and (cfg['raw_seek'] != 0): chan.raw_file.seek(int(cfg['raw_seek']) * 4800, 0) chan.throttle = blocks.throttle(gr.sizeof_char, chan.symbol_rate) chan.throttle.set_max_noutput_items(chan.symbol_rate/50); self.connect(chan.raw_file, chan.throttle) self.connect(chan.throttle, chan.decoder) self.set_interactive(False) # this is non-interactive 'replay' session else: self.connect(dev.src, chan.demod, chan.decoder) if ("raw_output" in cfg) and (cfg['raw_output'] != ""): sys.stderr.write("%s Saving raw symbols to file: %s\n" % (log_ts.get(), cfg['raw_output'])) chan.raw_sink = blocks.file_sink(gr.sizeof_char, str(cfg['raw_output'])) self.connect(chan.demod, chan.raw_sink)
def add_skiplist(self, tgid, end_time=None): if not tgid or (tgid <= 0) or (tgid > 65534): if self.debug > 1: sys.stderr.write( "%s [%d] skiplist tgid(%d) out of range (1-65534)\n" % (log_ts.get(), self.msgq_id, tgid)) return if tgid in self.skiplist: return self.skiplist[tgid] = end_time if self.debug > 1: sys.stderr.write("%s [%d] skiplisting: tgid(%d)\n" % (log_ts.get(), self.msgq_id, tgid)) if self.current_tgid and self.current_tgid in self.skiplist: self.expire_talkgroup(reason="skiplisted") self.hold_mode = False self.hold_tgid = None self.hold_until = time.time()
def post_init(self): if self.debug >= 1: sys.stderr.write("%s [%d] Initializing voice channel\n" % (log_ts.get(), self.msgq_id)) self.slot_set({'tuner': self.msgq_id,'slot': 4}) # disable voice if self.control is not None: self.talkgroups = self.control.get_talkgroups() self.load_bl_wl() self.tgid_hold_time = float(from_dict(self.control.config, 'tgid_hold_time', TGID_HOLD_TIME)) meta_update(self.meta_q)
def post_init(self): if self.msgq_id < 0: sys.stderr.write("%f Smartnet system has no channel assigned!\n" % (time.time())) return if self.debug >= 1: sys.stderr.write("%s [%d] Initializing Smartnet system\n" % (log_ts.get(), self.msgq_id)) if 'tgid_tags_file' in self.config and self.config[ 'tgid_tags_file'] != "": sys.stderr.write( "%s [%d] reading system tgid_tags_file: %s\n" % (log_ts.get(), self.msgq_id, self.config['tgid_tags_file'])) self.read_tags_file(self.config['tgid_tags_file']) if 'blacklist' in self.config and self.config['blacklist'] != "": sys.stderr.write( "%s [%d] reading system blacklist file: %s\n" % (log_ts.get(), self.msgq_id, self.config['blacklist'])) self.blacklist = get_int_dict(self.config['blacklist'], self.msgq_id) if 'whitelist' in self.config and self.config['whitelist'] != "": sys.stderr.write( "%s [%d] reading system whitelist file: %s\n" % (log_ts.get(), self.msgq_id, self.config['whitelist'])) self.whitelist = get_int_dict(self.config['whitelist'], self.msgq_id) cc_list = from_dict(self.config, 'control_channel_list', "") if cc_list == "": sys.stderr.write( "Aborting. Smartnet Trunking 'control_channel_list' parameter is empty or not found\n" ) sys.exit(1) for f in cc_list.split(','): self.cc_list.append(get_frequency(f)) self.tune_next_cc()
def add_whitelist(self, tgid): if not tgid or (tgid <= 0) or (tgid > 65534): if self.debug > 0: sys.stderr.write("%s [%d] whitelist tgid(%d) out of range (1-65534)\n" % (log_ts.get(), self.msgq_id, tgid)) return if self.blacklist and tgid in self.blacklist: self.blacklist.pop(tgid) if self.debug > 1: sys.stderr.write("%s [%d] de-blacklisting tgid(%d)\n" % (log_ts.get(), self.msgq_id, tgid)) if self.whitelist is None: self.whitelist = {} if tgid in self.whitelist: return self.whitelist[tgid] = None if self.debug > 1: sys.stderr.write("%s [%d] whitelisting tgid(%d)\n" % (log_ts.get(), self.msgq_id, tgid)) if self.current_tgid and self.current_tgid not in self.whitelist: self.expire_talkgroup(reason = "not whitelisted") self.hold_mode = False self.hold_tgid = None self.hold_until = time.time()
def get_freq(self, cmd): # Convert 'cmd' into band-dependent frequency freq = 0.0 bandplan = from_dict(self.config, 'bandplan', "800_reband") band = bandplan[:3] subtype = bandplan[3:len(bandplan)].lower().lstrip("_-:") if band == "800": if cmd <= 0x2CF: if subtype == "reband": # REBAND if cmd < 0x1b8: freq = 851.0125 + (0.025 * cmd) if cmd >= 0x1B8 and cmd <= 0x22F: freq = 851.0250 + (0.025 * (cmd - 0x1B8)) elif subtype == "splinter" and cmd <= 0x257: # SPLINTER site freq = 851.0 + (0.025 * cmd) else: freq = 851.0125 + (0.025 * cmd) # STANDARD site elif cmd <= 0x2f7: freq = 866.0000 + (0.025 * (cmd - 0x2D0)) elif cmd >= 0x32F and cmd <= 0x33F: freq = 867.0000 + (0.025 * (cmd - 0x32F)) elif cmd == 0x3BE: freq = 868.9750 elif cmd >= 0x3C1 and cmd <= 0x3FE: freq = 867.4250 + (0.025 * (cmd - 0x3C1)) elif band == "900": freq = 935.0125 + (0.0125 * cmd) elif band == "400": bp_spacing = float(from_dict(self.config, 'bp_spacing', "0.025")) bp_base_offset = int(from_dict(self.config, 'bp_base_offset', 380)) bp_mid_offset = int(from_dict(self.config, 'bp_mid_offset', 760)) bp_high_offset = int(from_dict(self.config, 'bp_high_offset', 760)) bp_base = float(from_dict(self.config, 'bp_base', "0.0")) bp_mid = float(from_dict(self.config, 'bp_mid', "0.0")) bp_high = float(from_dict(self.config, 'bp_high', "0.0")) if (cmd >= bp_base_offset) and (cmd < bp_mid_offset): freq = bp_base + (bp_spacing * (cmd - bp_base_offset)) elif (cmd >= bp_mid_offset) and (cmd < bp_high_offset): freq = bp_mid + (bp_spacing * (cmd - bp_mid_offset)) elif (cmd >= bp_high_offset) and (cmd < 760): freq = bp_high + (bp_spacing * (cmd - bp_high_offset)) else: if self.debug >= 5: sys.stderr.write( "%s [%d] SMARTNET OSW freq cmd: %d out of range\n" % (log_ts.get(), self.msgq_id, cmd)) return round( freq, 5 ) # round to 5 decimal places to eliminate accumulated floating point errors
def open(self, hwdevice): self.out = c_void_p( self.libpa.pa_simple_new(None, "OP25".encode("ascii"), PA_STREAM_PLAYBACK, None, "OP25 Playback".encode('ascii'), byref(self.ss), None, None, byref(self.error))) if not self.out: sys.stderr.write("%s Could not open PulseAudio stream: %s\n" % (log_ts.get(), self.libpa.strerror(self.error))) return self.error.value
def expire_talkgroups(self, curr_time): if curr_time < self.last_expiry_check + EXPIRY_TIMER: return False rc = False self.last_expiry_check = curr_time for tgid in self.talkgroups: if (self.talkgroups[tgid]['receiver'] is not None) and (curr_time >= self.talkgroups[tgid]['time'] + TGID_EXPIRY_TIME): if self.debug > 1: sys.stderr.write("%s [%d] expiring tg(%d), freq(%f)\n" % (log_ts.get(), self.msgq_id, tgid, (self.talkgroups[tgid]['frequency']/1e6))) self.talkgroups[tgid]['receiver'].expire_talkgroup(reason="expiry") rc = True return rc
def __init__(self, dest, debug, input_rate, deviation, squelch, gain, msgq_id, msg_q): gr.hier_block2.__init__( self, "op25_nbfm_c", gr.io_signature(1, 1, gr.sizeof_gr_complex), # Input signature gr.io_signature(0, 0, 0)) # Output signature self.debug = debug self.msgq_id = msgq_id # 'switch' enables the analog decoding to be turned on/off self.switch = blocks.copy(gr.sizeof_gr_complex) self.switch.set_enabled(False) # power squelch self.squelch = analog.simple_squelch_cc(squelch, gain) # quadrature demod fm_demod_gain = input_rate / (4 * pi * deviation) self.fm_demod = analog.quadrature_demod_cf(fm_demod_gain) # fm deemphasis self.deemph = analog.fm_deemph(input_rate, 0.00075) # decimate and filter audio_decim = input_rate // _PCM_RATE lpf_taps = filter.firdes.low_pass( 1.0, # gain input_rate, # sampling rate 3000.0, # Audio high cutoff (remove aliasing) 200.0, # transition filter.firdes.WIN_HAMMING) # filter type hpf_taps = filter.firdes.high_pass( 1.0, # gain _PCM_RATE, # sampling rate 200.0, # Audio low cutoff (remove sub-audio signaling) 10.0, # Sharp transition band filter.firdes.WIN_HAMMING) # filter type self.lp_filter = filter.fir_filter_fff(audio_decim, lpf_taps) self.hp_filter = filter.fir_filter_fff(1, hpf_taps) # analog_udp block converts +/-1.0 float samples to S16LE PCM and sends over UDP self.analog_udp = op25_repeater.analog_udp(dest, debug, msgq_id, msg_q) self.connect(self, self.switch, self.squelch, self.fm_demod, self.deemph, self.lp_filter, self.hp_filter, self.analog_udp) sys.stderr.write("%s [%d] Enabling nbfm analog audio\n" % (log_ts.get(), msgq_id))