def __init__(self, config, dev, verbosity): sys.stderr.write('channel (dev %s): %s\n' % (dev.name, config)) self.device = dev self.name = config['name'] self.symbol_rate = _def_symbol_rate if 'symbol_rate' in config.keys(): self.symbol_rate = config['symbol_rate'] self.config = config self.demod = p25_demodulator.p25_demod_cb( input_rate = dev.sample_rate, demod_type = config['demod_type'], filter_type = config['filter_type'], excess_bw = config['excess_bw'], relative_freq = dev.frequency + dev.offset - config['frequency'], offset = dev.offset, if_rate = config['if_rate'], symbol_rate = self.symbol_rate) q = gr.msg_queue(1) self.decoder = op25_repeater.frame_assembler(config['destination'], verbosity, q) self.kill_sink = [] if 'plot' not in config.keys(): return self.sinks = [] for plot in config['plot'].split(','): # fixme: allow multiple complex consumers (fft and constellation currently mutually exclusive) if plot == 'datascope': assert config['demod_type'] == 'fsk4' ## datascope plot requires fsk4 demod type sink = eye_sink_f(sps=config['if_rate'] / self.symbol_rate) self.demod.connect_bb('symbol_filter', sink) self.kill_sink.append(sink) elif plot == 'symbol': sink = symbol_sink_f() self.demod.connect_float(sink) self.kill_sink.append(sink) elif plot == 'fft': i = len(self.sinks) self.sinks.append(fft_sink_c()) self.demod.connect_complex('src', self.sinks[i]) self.kill_sink.append(self.sinks[i]) self.sinks[i].set_offset(self.device.offset) self.sinks[i].set_center_freq(self.device.frequency) self.sinks[i].set_relative_freq(self.device.frequency + self.device.offset - self.config['frequency']) self.sinks[i].set_width(self.device.sample_rate) elif plot == 'constellation': i = len(self.sinks) assert config['demod_type'] == 'cqpsk' ## constellation plot requires cqpsk demod type self.sinks.append(constellation_sink_c()) self.demod.connect_complex('diffdec', self.sinks[i]) self.kill_sink.append(self.sinks[i]) elif plot == 'mixer': i = len(self.sinks) self.sinks.append(mixer_sink_c()) self.demod.connect_complex('mixer', self.sinks[i]) self.kill_sink.append(self.sinks[i]) else: sys.stderr.write('unrecognized plot type %s\n' % plot) return
def __init__(self, config, dev, verbosity, msgq_id, rx_q, tb): sys.stderr.write('channel (dev %s): %s\n' % (dev.name, config)) self.verbosity = verbosity ch_name = str(from_dict(config, 'name', "")) self.name = ("[%d] %s" % (msgq_id, ch_name)) if ch_name != "" else ("[%d]" % msgq_id) self.device = dev if 'frequency' in config and (config['frequency'] != ""): self.frequency = config['frequency'] else: self.frequency = self.device.frequency self.msgq_id = msgq_id self.tb = tb self.raw_sink = None self.raw_file = None self.throttle = None self.nbfm = None self.nbfm_mode = 0 self.sinks = {} self.tdma_state = False self.xor_cache = {} self.symbol_rate = _def_symbol_rate if 'symbol_rate' in list(config.keys()): self.symbol_rate = config['symbol_rate'] self.config = config if config['demod_type'] == "fsk": # Motorola 3600bps filter_type = from_dict(config, 'filter_type', 'fsk2mm') if filter_type[:4] != 'fsk2': # has to be 'fsk2' or derivative such as 'fsk2mm' filter_type = 'fsk2mm' self.demod = p25_demodulator.p25_demod_cb( input_rate = dev.sample_rate, demod_type = 'fsk4', filter_type = filter_type, excess_bw = config['excess_bw'], relative_freq = (dev.frequency + dev.offset + dev.fractional_corr) - self.frequency, offset = dev.offset, if_rate = config['if_rate'], symbol_rate = self.symbol_rate) self.decoder = op25_repeater.frame_assembler(str(config['destination']), verbosity, msgq_id, rx_q) else: # P25, DMR, NXDN and everything else self.demod = p25_demodulator.p25_demod_cb( input_rate = dev.sample_rate, demod_type = config['demod_type'], filter_type = config['filter_type'], excess_bw = config['excess_bw'], relative_freq = (dev.frequency + dev.offset + dev.fractional_corr) - self.frequency, offset = dev.offset, if_rate = config['if_rate'], symbol_rate = self.symbol_rate) self.decoder = op25_repeater.frame_assembler(str(config['destination']), verbosity, msgq_id, rx_q) if 'key' in config and (config['key'] != ""): self.set_key(int(config['key'], 0)) enable_analog = str(from_dict(config, 'enable_analog', "auto")).lower() if enable_analog == "off": self.nbfm_mode = 0; elif enable_analog == "on": self.nbfm_mode = 2; else: self.nbfm_mode = 1; if self.nbfm_mode > 0: self.nbfm = op25_nbfm.op25_nbfm_c(str(config['destination']), verbosity, config, msgq_id, rx_q) if self.demod.connect_nbfm(self.nbfm): if self.nbfm_mode == 2: self.nbfm.control(True) else: self.nbfm = None if ('plot' not in list(config.keys())) or (config['plot'] == ""): return for plot in config['plot'].split(','): if plot == 'datascope': self.toggle_eye_plot() elif plot == 'symbol': self.toggle_symbol_plot() elif plot == 'fft': self.toggle_fft_plot() elif plot == 'constellation': self.toggle_constellation_plot() elif plot == 'mixer': self.toggle_mixer_plot() elif plot == 'tuner': self.toggle_tuner_plot() else: sys.stderr.write('unrecognized plot type %s\n' % plot) return
def __init__(self, config, dev, verbosity, msgq=None, process_msg=None, msgq_id=-1, role=''): sys.stderr.write('channel (dev %s): %s\n' % (dev.name, config)) self.device = dev self.name = config['name'] self.symbol_rate = _def_symbol_rate self.process_msg = process_msg self.role = role self.dev = '' self.sysid = [] self.nac = [] if 'symbol_rate' in config.keys(): self.symbol_rate = config['symbol_rate'] self.config = config self.verbosity = verbosity self.frequency = 0 self.tdma_state = False self.xor_cache = {} self.tuning_error = 0 self.freq_correction = 0 self.error_band = 0 self.last_error_update = 0 self.last_set_freq_at = time.time() self.warned_frequencies = {} self.msgq_id = msgq_id self.next_band_change = time.time() self.audio_port = _def_audio_port self.audio_output = _def_audio_output self.audio_gain = 1.0 if 'audio_gain' in config: self.audio_gain = float(config['audio_gain']) if dev.args.startswith('audio:'): self.demod = p25_demodulator.p25_demod_fb( input_rate=dev.sample_rate, filter_type=config['filter_type'], if_rate=config['if_rate'], symbol_rate=self.symbol_rate) else: self.demod = p25_demodulator.p25_demod_cb( input_rate=dev.sample_rate, demod_type=config['demod_type'], filter_type=config['filter_type'], excess_bw=config['excess_bw'], relative_freq=dev.frequency + dev.offset - config['frequency'], offset=dev.offset, if_rate=config['if_rate'], symbol_rate=self.symbol_rate) if msgq is not None: q = msgq else: q = gr.msg_queue(20) if 'decode' in config.keys() and config['decode'].startswith( 'p25_decoder'): num_ambe = 1 (proto, wireshark_host, udp_port) = config['destination'].split(':') assert proto == 'udp' wireshark_host = wireshark_host.replace('/', '') udp_port = int(udp_port) if role == 'vc': self.audio_port = udp_port if 'audio_output' in config.keys(): self.audio_output = config['audio_output'] self.decoder = p25_decoder.p25_decoder_sink_b( dest='audio', do_imbe=True, num_ambe=num_ambe, wireshark_host=wireshark_host, udp_port=udp_port, do_msgq=True, msgq=q, audio_output=self.audio_output, debug=verbosity, msgq_id=self.msgq_id) else: self.decoder = op25_repeater.frame_assembler( config['destination'], verbosity, q, self.msgq_id) if self.symbol_rate == 6000 and role == 'cc': sps = config['if_rate'] // self.symbol_rate self.demod.set_symbol_rate( self.symbol_rate) # this and the foll. call should be merged? self.demod.clock.set_omega(float(sps)) self.demod.clock.set_tdma(True) sys.stderr.write( 'initializing TDMA control channel %s channel ID %d\n' % (self.name, self.msgq_id)) if self.process_msg is not None and msgq is None: self.q_watcher = du_queue_watcher( q, lambda msg: self.process_msg(msg, sender=self)) self.kill_sink = [] if 'blacklist' in config.keys(): for g in config['blacklist'].split(','): self.decoder.insert_blacklist(int(g)) if 'whitelist' in config.keys(): for g in config['whitelist'].split(','): self.decoder.insert_whitelist(int(g)) self.sinks = [] if 'plot' not in config.keys(): return for plot in config['plot'].split(','): if plot == 'datascope': assert config[ 'demod_type'] == 'fsk4' ## datascope plot requires fsk4 demod type sink = eye_sink_f(sps=config['if_rate'] // self.symbol_rate) sink.set_title(self.name) self.sinks.append(sink) self.demod.connect_bb('symbol_filter', sink) self.kill_sink.append(sink) elif plot == 'symbol': sink = symbol_sink_f() sink.set_title(self.name) self.sinks.append(sink) self.demod.connect_float(sink) self.kill_sink.append(sink) elif plot == 'fft': assert config[ 'demod_type'] == 'cqpsk' ## fft plot requires cqpsk demod type i = len(self.sinks) sink = fft_sink_c() sink.set_title(self.name) self.sinks.append(sink) self.demod.connect_complex('src', self.sinks[i]) self.kill_sink.append(self.sinks[i]) elif plot == 'mixer': assert config[ 'demod_type'] == 'cqpsk' ## mixer plot requires cqpsk demod type i = len(self.sinks) sink = mixer_sink_c() sink.set_title(self.name) self.sinks.append(sink) self.demod.connect_complex('mixer', self.sinks[i]) self.kill_sink.append(self.sinks[i]) elif plot == 'constellation': i = len(self.sinks) assert config[ 'demod_type'] == 'cqpsk' ## constellation plot requires cqpsk demod type sink = constellation_sink_c() sink.set_title(self.name) self.sinks.append(sink) self.demod.connect_complex('diffdec', self.sinks[i]) self.kill_sink.append(self.sinks[i]) elif plot == 'correlation': assert config[ 'demod_type'] == 'fsk4' ## correlation plot requires fsk4 demod type assert config[ 'symbol_rate'] == 4800 ## 4800 required for correlation plot sps = config['if_rate'] // self.symbol_rate sinks = setup_correlation(sps, self.name, self.demod.connect_bb) self.kill_sink += sinks self.sinks += sinks else: sys.stderr.write('unrecognized plot type %s\n' % plot) return
def __init__(self, dest, debug, config, 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.config = config self.msgq_id = msgq_id sys.stderr.write("%s [%d] Enabling nbfm analog audio\n" % (log_ts.get(), msgq_id)) # load config input_rate = int(from_dict(config, 'if_rate', 24000)) deviation = int(from_dict(config, 'nbfm_deviation', 4000)) squelch = int(from_dict(config, 'nbfm_squelch_threshold', -60)) gain = float(from_dict(config, 'nbfm_squelch_gain', 0.0015)) subchannel_enabled = bool( from_dict(config, 'nbfm_enable_subchannel', False)) raw_in = str(from_dict(config, 'nbfm_raw_input', "")) raw_out = str(from_dict(config, 'nbfm_raw_output', "")) # '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) # raw playback if raw_in != "": self.null_sink = blocks.null_sink(gr.sizeof_gr_complex) self.connect(self, self.null_sink) # dispose of regular input self.raw_file = blocks.file_source(gr.sizeof_float, raw_in, False) self.throttle = blocks.throttle(gr.sizeof_float, input_rate) self.throttle.set_max_noutput_items(input_rate / 50) self.fm_demod = self.throttle # and replace fm_demod with throttled file source self.connect(self.raw_file, self.throttle) sys.stderr.write("%s [%d] Reading nbfm demod from file: %s\n" % (log_ts.get(), msgq_id, raw_in)) else: self.connect(self, self.switch, self.squelch, self.fm_demod) self.connect(self.fm_demod, self.deemph, self.lp_filter, self.hp_filter, self.analog_udp) # raw capture if raw_in == "" and raw_out != "": sys.stderr.write("%s [%d] Saving nbfm demod to file: %s\n" % (log_ts.get(), msgq_id, raw_out)) self.raw_sink = blocks.file_sink(gr.sizeof_float, raw_out) self.connect(self.fm_demod, self.raw_sink) # subchannel signaling if subchannel_enabled: self.subchannel_decimation = 25 self.subchannel_gain = 10 self.subchannelfilttaps = filter.firdes.low_pass( self.subchannel_gain, input_rate, 200, 40, filter.firdes.WIN_HANN) self.subchannelfilt = filter.fir_filter_fff( self.subchannel_decimation, self.subchannelfilttaps) self.subchannel_syms_per_sec = 150 self.subchannel_samples_per_symbol = ( input_rate / self.subchannel_decimation) / self.subchannel_syms_per_sec sys.stderr.write( "%s [%d] Subchannel samples per symbol: %f\n" % (log_ts.get(), msgq_id, self.subchannel_samples_per_symbol)) self.subchannel_clockrec = digital.clock_recovery_mm_ff( self.subchannel_samples_per_symbol, 0.25 * 0.01 * 0.01, 0.5, 0.01, 0.3) self.subchannel_slicer = digital.binary_slicer_fb() #self.subchannel_correlator = digital.correlate_access_code_bb("01000", 0) self.subchannel_framer = op25_repeater.frame_assembler( "subchannel", debug, msgq_id, msg_q) self.connect(self.fm_demod, self.subchannelfilt, self.subchannel_clockrec, self.subchannel_slicer, self.subchannel_framer)
def __init__(self, config, dev, verbosity, msgq = None): sys.stderr.write('channel (dev %s): %s\n' % (dev.name, config)) self.device = dev self.name = config['name'] self.symbol_rate = _def_symbol_rate if 'symbol_rate' in config.keys(): self.symbol_rate = config['symbol_rate'] self.config = config if dev.args.startswith('audio:'): self.demod = p25_demodulator.p25_demod_fb( input_rate = dev.sample_rate, filter_type = config['filter_type'], if_rate = config['if_rate'], symbol_rate = self.symbol_rate) else: self.demod = p25_demodulator.p25_demod_cb( input_rate = dev.sample_rate, demod_type = config['demod_type'], filter_type = config['filter_type'], excess_bw = config['excess_bw'], relative_freq = dev.frequency + dev.offset - config['frequency'], offset = dev.offset, if_rate = config['if_rate'], symbol_rate = self.symbol_rate) if msgq is None: q = gr.msg_queue(1) else: q = msgq self.decoder = op25_repeater.frame_assembler(config['destination'], verbosity, q) self.kill_sink = [] if 'blacklist' in config.keys(): for g in config['blacklist'].split(','): self.decoder.insert_blacklist(int(g)) if 'whitelist' in config.keys(): for g in config['whitelist'].split(','): self.decoder.insert_whitelist(int(g)) if 'plot' not in config.keys(): return self.sinks = [] for plot in config['plot'].split(','): if plot == 'datascope': assert config['demod_type'] == 'fsk4' ## datascope plot requires fsk4 demod type sink = eye_sink_f(sps=config['if_rate'] // self.symbol_rate) self.demod.connect_bb('symbol_filter', sink) self.kill_sink.append(sink) elif plot == 'symbol': sink = symbol_sink_f() self.demod.connect_float(sink) self.kill_sink.append(sink) elif plot == 'fft': assert config['demod_type'] == 'cqpsk' ## fft plot requires cqpsk demod type i = len(self.sinks) self.sinks.append(fft_sink_c()) self.demod.connect_complex('src', self.sinks[i]) self.kill_sink.append(self.sinks[i]) elif plot == 'mixer': assert config['demod_type'] == 'cqpsk' ## mixer plot requires cqpsk demod type i = len(self.sinks) self.sinks.append(mixer_sink_c()) self.demod.connect_complex('mixer', self.sinks[i]) self.kill_sink.append(self.sinks[i]) elif plot == 'constellation': i = len(self.sinks) assert config['demod_type'] == 'cqpsk' ## constellation plot requires cqpsk demod type self.sinks.append(constellation_sink_c()) self.demod.connect_complex('diffdec', self.sinks[i]) self.kill_sink.append(self.sinks[i]) elif plot == 'correlation': assert config['demod_type'] == 'fsk4' ## correlation plot requires fsk4 demod type assert config['symbol_rate'] == 4800 ## 4800 required for correlation plot sps=config['if_rate'] // self.symbol_rate sinks = setup_correlation(sps, self.name, self.demod.connect_bb) self.kill_sink += sinks self.sinks += sinks else: sys.stderr.write('unrecognized plot type %s\n' % plot) return