def __init__(self, costas_alpha=0.04, gain_mu=0.025, input_rate=48000, output_rate=4800): gr.hier_block2.__init__( self, "CQPSK Demodulator", gr.io_signature(1, 1, gr.sizeof_gr_complex*1), gr.io_signature(1, 1, gr.sizeof_float*1), ) ################################################## # Parameters ################################################## self.costas_alpha = costas_alpha self.gain_mu = gain_mu self.input_rate = input_rate self.output_rate = output_rate ################################################## # Variables ################################################## self.alpha = alpha = costas_alpha self.omega = omega = float(input_rate) / float(output_rate) self.gain_omega = gain_omega = 0.1 * gain_mu * gain_mu self.fmax = fmax = 2 * math.pi * 2400 / float(input_rate) self.beta = beta = 0.125 * alpha * alpha ################################################## # Blocks ################################################## self.to_float = blocks.complex_to_arg(1) self.rescale = blocks.multiply_const_vff((1 / (math.pi / 4), )) self.diffdec = digital.diff_phasor_cc() self.clock = op25_repeater.gardner_costas_cc(omega, gain_mu, gain_omega, alpha, beta, fmax, -fmax) self.agc = analog.feedforward_agc_cc(16, 1.0) ################################################## # Connections ################################################## self.connect((self.agc, 0), (self.clock, 0)) self.connect((self.clock, 0), (self.diffdec, 0)) self.connect((self.diffdec, 0), (self.to_float, 0)) self.connect((self, 0), (self.agc, 0)) self.connect((self.rescale, 0), (self, 0)) self.connect((self.to_float, 0), (self.rescale, 0))
def __init__(self, input_rate=None, demod_type='cqpsk', relative_freq=0, offset=0, if_rate=_def_if_rate, gain_mu=_def_gain_mu, costas_alpha=_def_costas_alpha, symbol_rate=_def_symbol_rate): """ Hierarchical block for P25 demodulation. The complex input is tuned, decimated and demodulated @param input_rate: sample rate of complex input channel @type input_rate: int """ gr.hier_block2.__init__( self, "p25_demod_cb", gr.io_signature(1, 1, gr.sizeof_gr_complex), # Input signature gr.io_signature(1, 1, gr.sizeof_char)) # Output signature # gr.io_signature(0, 0, 0)) # Output signature p25_demod_base.__init__(self, if_rate=if_rate, symbol_rate=symbol_rate) self.input_rate = input_rate self.if_rate = if_rate self.symbol_rate = symbol_rate self.connect_state = None self.offset = 0 self.sps = 0.0 self.lo_freq = 0 self.float_sink = None self.complex_sink = None # local osc self.lo = analog.sig_source_c(input_rate, analog.GR_SIN_WAVE, 0, 1.0, 0) self.mixer = blocks.multiply_cc() lpf_coeffs = filter.firdes.low_pass(1.0, input_rate, 7250, 725, filter.firdes.WIN_HANN) decimation = int(input_rate / if_rate) self.lpf = filter.fir_filter_ccf(decimation, lpf_coeffs) resampled_rate = float(input_rate) / float( decimation) # rate at output of self.lpf self.arb_resampler = filter.pfb.arb_resampler_ccf( float(self.if_rate) / resampled_rate) self.connect(self, (self.mixer, 0)) self.connect(self.lo, (self.mixer, 1)) self.connect(self.mixer, self.lpf, self.arb_resampler) levels = [-2.0, 0.0, 2.0, 4.0] self.slicer = op25_repeater.fsk4_slicer_fb(levels) omega = float(self.if_rate) / float(self.symbol_rate) gain_omega = 0.1 * gain_mu * gain_mu alpha = costas_alpha beta = 0.125 * alpha * alpha fmax = 2400 # Hz fmax = 2 * pi * fmax / float(self.if_rate) self.clock = op25_repeater.gardner_costas_cc(omega, gain_mu, gain_omega, alpha, beta, fmax, -fmax) self.agc = analog.feedforward_agc_cc(16, 1.0) # Perform Differential decoding on the constellation self.diffdec = digital.diff_phasor_cc() # take angle of the difference (in radians) self.to_float = blocks.complex_to_arg() # convert from radians such that signal is in -3/-1/+1/+3 self.rescale = blocks.multiply_const_ff((1 / (pi / 4))) # fm demodulator (needed in fsk4 case) fm_demod_gain = if_rate / (2.0 * pi * _def_symbol_deviation) self.fm_demod = analog.quadrature_demod_cf(fm_demod_gain) self.connect_chain(demod_type) self.connect(self.slicer, self) self.set_relative_frequency(relative_freq)
def __init__(self, input_rate = None, demod_type = 'cqpsk', filter_type = None, excess_bw = _def_excess_bw, relative_freq = 0, offset = 0, if_rate = _def_if_rate, gain_mu = _def_gain_mu, costas_alpha = _def_costas_alpha, symbol_rate = _def_symbol_rate): """ Hierarchical block for P25 demodulation. The complex input is tuned, decimated and demodulated @param input_rate: sample rate of complex input channel @type input_rate: int """ gr.hier_block2.__init__(self, "p25_demod_cb", gr.io_signature(1, 1, gr.sizeof_gr_complex), # Input signature gr.io_signature(1, 1, gr.sizeof_char)) # Output signature # gr.io_signature(0, 0, 0)) # Output signature p25_demod_base.__init__(self, if_rate=if_rate, symbol_rate=symbol_rate, filter_type=filter_type) self.input_rate = input_rate self.if_rate = if_rate self.symbol_rate = symbol_rate self.connect_state = None self.aux_fm_connected = False self.offset = 0 self.sps = 0.0 self.lo_freq = 0 self.float_sink = None self.complex_sink = None self.if1 = 0 self.if2 = 0 self.t_cache = {} if filter_type == 'rrc': self.set_baseband_gain(0.61) # local osc self.lo = analog.sig_source_c (input_rate, analog.GR_SIN_WAVE, 0, 1.0, 0) self.mixer = blocks.multiply_cc() decimator_values = get_decim(input_rate) if decimator_values: self.decim, self.decim2 = decimator_values self.if1 = input_rate / self.decim self.if2 = self.if1 / self.decim2 sys.stderr.write( 'Using two-stage decimator for speed=%d, decim=%d/%d if1=%d if2=%d\n' % (input_rate, self.decim, self.decim2, self.if1, self.if2)) bpf_coeffs = filter.firdes.complex_band_pass(1.0, input_rate, -self.if1/2, self.if1/2, self.if1/2, filter.firdes.WIN_HAMMING) self.t_cache[0] = bpf_coeffs fa = 6250 fb = self.if2 / 2 lpf_coeffs = filter.firdes.low_pass(1.0, self.if1, (fb+fa)/2, fb-fa, filter.firdes.WIN_HAMMING) self.bpf = filter.fir_filter_ccc(self.decim, bpf_coeffs) self.lpf = filter.fir_filter_ccf(self.decim2, lpf_coeffs) resampled_rate = self.if2 self.bfo = analog.sig_source_c (self.if1, analog.GR_SIN_WAVE, 0, 1.0, 0) self.connect(self, self.bpf, (self.mixer, 0)) self.connect(self.bfo, (self.mixer, 1)) else: sys.stderr.write( 'Unable to use two-stage decimator for speed=%d\n' % (input_rate)) # local osc self.lo = analog.sig_source_c (input_rate, analog.GR_SIN_WAVE, 0, 1.0, 0) lpf_coeffs = filter.firdes.low_pass(1.0, input_rate, 7250, 1450, filter.firdes.WIN_HANN) decimation = int(input_rate / if_rate) self.lpf = filter.fir_filter_ccf(decimation, lpf_coeffs) resampled_rate = float(input_rate) / float(decimation) # rate at output of self.lpf self.connect(self, (self.mixer, 0)) self.connect(self.lo, (self.mixer, 1)) self.connect(self.mixer, self.lpf) if self.if_rate != resampled_rate: self.if_out = filter.pfb.arb_resampler_ccf(float(self.if_rate) / resampled_rate) self.connect(self.lpf, self.if_out) else: self.if_out = self.lpf fa = 6250 fb = fa + 625 cutoff_coeffs = filter.firdes.low_pass(1.0, self.if_rate, (fb+fa)/2, fb-fa, filter.firdes.WIN_HANN) self.cutoff = filter.fir_filter_ccf(1, cutoff_coeffs) omega = float(self.if_rate) / float(self.symbol_rate) gain_omega = 0.1 * gain_mu * gain_mu alpha = costas_alpha beta = 0.125 * alpha * alpha fmax = 2400 # Hz fmax = 2*pi * fmax / float(self.if_rate) self.clock = op25_repeater.gardner_costas_cc(omega, gain_mu, gain_omega, alpha, beta, fmax, -fmax) self.agc = analog.feedforward_agc_cc(16, 1.0) # Perform Differential decoding on the constellation self.diffdec = digital.diff_phasor_cc() # take angle of the difference (in radians) self.to_float = blocks.complex_to_arg() # convert from radians such that signal is in -3/-1/+1/+3 self.rescale = blocks.multiply_const_ff( (1 / (pi / 4)) ) # fm demodulator (needed in fsk4 case) fm_demod_gain = if_rate / (2.0 * pi * _def_symbol_deviation) self.fm_demod = analog.quadrature_demod_cf(fm_demod_gain) self.connect_chain(demod_type) self.connect(self.slicer, self) self.set_relative_frequency(relative_freq)
def __init__(self, input_rate = None, demod_type = 'cqpsk', relative_freq = 0, offset = 0, if_rate = _def_if_rate, gain_mu = _def_gain_mu, costas_alpha = _def_costas_alpha, symbol_rate = _def_symbol_rate): """ Hierarchical block for P25 demodulation. The complex input is tuned, decimated and demodulated @param input_rate: sample rate of complex input channel @type input_rate: int """ gr.hier_block2.__init__(self, "p25_demod_cb", gr.io_signature(1, 1, gr.sizeof_gr_complex), # Input signature gr.io_signature(1, 1, gr.sizeof_char)) # Output signature # gr.io_signature(0, 0, 0)) # Output signature p25_demod_base.__init__(self, if_rate=if_rate, symbol_rate=symbol_rate) self.input_rate = input_rate self.if_rate = if_rate self.symbol_rate = symbol_rate self.connect_state = None self.offset = 0 self.sps = 0.0 self.lo_freq = 0 self.float_sink = None self.complex_sink = None # local osc self.lo = analog.sig_source_c (input_rate, analog.GR_SIN_WAVE, 0, 1.0, 0) self.mixer = blocks.multiply_cc() lpf_coeffs = filter.firdes.low_pass(1.0, input_rate, 7250, 725, filter.firdes.WIN_HANN) decimation = int(input_rate / if_rate) self.lpf = filter.fir_filter_ccf(decimation, lpf_coeffs) resampled_rate = float(input_rate) / float(decimation) # rate at output of self.lpf self.arb_resampler = filter.pfb.arb_resampler_ccf( float(self.if_rate) / resampled_rate) self.connect(self, (self.mixer, 0)) self.connect(self.lo, (self.mixer, 1)) self.connect(self.mixer, self.lpf, self.arb_resampler) levels = [ -2.0, 0.0, 2.0, 4.0 ] self.slicer = op25_repeater.fsk4_slicer_fb(levels) omega = float(self.if_rate) / float(self.symbol_rate) gain_omega = 0.1 * gain_mu * gain_mu alpha = costas_alpha beta = 0.125 * alpha * alpha fmax = 2400 # Hz fmax = 2*pi * fmax / float(self.if_rate) self.clock = op25_repeater.gardner_costas_cc(omega, gain_mu, gain_omega, alpha, beta, fmax, -fmax) self.agc = analog.feedforward_agc_cc(16, 1.0) # Perform Differential decoding on the constellation self.diffdec = digital.diff_phasor_cc() # take angle of the difference (in radians) self.to_float = blocks.complex_to_arg() # convert from radians such that signal is in -3/-1/+1/+3 self.rescale = blocks.multiply_const_ff( (1 / (pi / 4)) ) # fm demodulator (needed in fsk4 case) fm_demod_gain = if_rate / (2.0 * pi * _def_symbol_deviation) self.fm_demod = analog.quadrature_demod_cf(fm_demod_gain) self.connect_chain(demod_type) self.connect(self.slicer, self) self.set_relative_frequency(relative_freq)
def __init__(self): gr.top_block.__init__(self) parser = OptionParser(option_class=eng_option) parser.add_option("-1", "--one-channel", action="store_true", default=False, help="software synthesized Q channel") parser.add_option("-a", "--agc", action="store_true", default=False, help="automatic gain control (overrides --gain)") parser.add_option("-c", "--calibration", type="eng_float", default=0, help="freq offset") parser.add_option("-d", "--debug", action="store_true", default=False, help="allow time at init to attach gdb") parser.add_option("-C", "--costas-alpha", type="eng_float", default=0.125, help="Costas alpha") parser.add_option("-g", "--gain", type="eng_float", default=1.0) parser.add_option("-i", "--input-file", type="string", default="in.dat", help="specify the input file") parser.add_option("-I", "--imbe", action="store_true", default=False, help="output IMBE codewords") parser.add_option("-L", "--low-pass", type="eng_float", default=6.5e3, help="low pass cut-off", metavar="Hz") parser.add_option("-o", "--output-file", type="string", default="out.dat", help="specify the output file") parser.add_option("-p", "--polarity", action="store_true", default=False, help="use reversed polarity") parser.add_option("-r", "--raw-symbols", type="string", default=None, help="dump decoded symbols to file") parser.add_option("-s", "--sample-rate", type="int", default=96000, help="input sample rate") parser.add_option("-t", "--tone-detect", action="store_true", default=False, help="use experimental tone detect algorithm") parser.add_option("-v", "--verbose", action="store_true", default=False, help="additional output") parser.add_option("-6", "--k6k", action="store_true", default=False, help="use 6K symbol rate") (options, args) = parser.parse_args() sample_rate = options.sample_rate if options.k6k: symbol_rate = 6000 else: symbol_rate = 4800 samples_per_symbol = sample_rate // symbol_rate IN = blocks.file_source(gr.sizeof_gr_complex, options.input_file) if options.one_channel: C2F = blocks.complex_to_float() F2C = blocks.float_to_complex() # osc./mixer for mixing signal down to approx. zero IF LO = analog.sig_source_c(sample_rate, analog.GR_COS_WAVE, options.calibration, 1.0, 0) MIXER = blocks.multiply_cc() # get signal into normalized range (-1.0 - +1.0) if options.agc: AMP = analog.feedforward_agc_cc(16, 1.0) else: AMP = blocks.multiply_const_cc(options.gain) lpf_taps = filter.firdes.low_pass(1.0, sample_rate, options.low_pass, options.low_pass * 0.1, filter.firdes.WIN_HANN) decim_amt = 1 if options.tone_detect: if sample_rate != 96000: print "warning, only 96K has been tested." print "other rates may require theta to be reviewed/adjusted." step_size = 7.5e-8 theta = -4 # optimum timing sampling point cic_length = 48 DEMOD = op25_repeater.tdetect_cc(samples_per_symbol, step_size, theta, cic_length) else: # decim by 2 to get 48k rate samples_per_symbol /= 2 # for DECIM sample_rate /= 2 # for DECIM decim_amt = 2 # create Gardner/Costas loop # the loop will not work if the sample levels aren't normalized (above) timing_error_gain = 0.025 # loop error gain gain_omega = 0.25 * timing_error_gain * timing_error_gain alpha = options.costas_alpha beta = 0.125 * alpha * alpha fmin = -0.025 # fmin and fmax are in radians/s fmax = 0.025 DEMOD = op25_repeater.gardner_costas_cc(samples_per_symbol, timing_error_gain, gain_omega, alpha, beta, fmax, fmin) DECIM = filter.fir_filter_ccf(decim_amt, lpf_taps) # probably too much phase noise etc to attempt coherent demodulation # so we use differential DIFF = digital.diff_phasor_cc() # take angle of the phase difference (in radians) TOFLOAT = blocks.complex_to_arg() # convert from radians such that signal is in [-3, -1, +1, +3] RESCALE = blocks.multiply_const_ff(1 / (pi / 4.0)) # optional polarity reversal (should be unnec. - now autodetected) p = 1.0 if options.polarity: p = -1.0 POLARITY = blocks.multiply_const_ff(p) # hard decision at specified points levels = [-2.0, 0.0, 2.0, 4.0] SLICER = op25_repeater.fsk4_slicer_fb(levels) # assemble received frames and route to Wireshark via UDP hostname = "127.0.0.1" port = 23456 debug = 0 if options.verbose: debug = 255 do_imbe = False if options.imbe: do_imbe = True do_output = True # enable block's output stream do_msgq = False # msgq output not yet implemented msgq = gr.msg_queue(2) DECODER = op25_repeater.p25_frame_assembler(hostname, port, debug, do_imbe, do_output, do_msgq, msgq, False, False) OUT = blocks.file_sink(gr.sizeof_char, options.output_file) if options.one_channel: self.connect(IN, C2F, F2C, (MIXER, 0)) else: self.connect(IN, (MIXER, 0)) self.connect(LO, (MIXER, 1)) self.connect(MIXER, AMP, DECIM, DEMOD, DIFF, TOFLOAT, RESCALE, POLARITY, SLICER, DECODER, OUT) if options.raw_symbols: SINKC = blocks.file_sink(gr.sizeof_char, options.raw_symbols) self.connect(SLICER, SINKC) if options.debug: print 'Ready for GDB to attach (pid = %d)' % (os.getpid(), ) raw_input("Press 'Enter' to continue...")
def __init__(self, system, site_uuid, overseer_uuid): gr.top_block.__init__(self, "p25 receiver") #set globals self.is_locked = False self.system = system self.instance_uuid = '%s' % uuid.uuid4() self.log = logging.getLogger('overseer.p25_control_demod.%s' % self.instance_uuid) self.protocol_log = logging.getLogger('protocol.%s' % self.instance_uuid) self.log.info('Initializing instance: %s site: %s overseer: %s' % (self.instance_uuid, site_uuid, overseer_uuid)) self.site_uuid = site_uuid self.overseer_uuid = overseer_uuid self.control_channel = system['channels'][ system['default_control_channel']] self.control_channel_i = system['default_control_channel'] self.channel_identifier_table = {} try: self.modulation = system['modulation'] except: self.modulation = 'C4FM' self.channel_rate = 12500 symbol_rate = 4800 self.site_detail = {} self.site_detail['WACN ID'] = None self.site_detail['System ID'] = None self.site_detail['Control Channel'] = None self.site_detail['System Service Class'] = None self.site_detail['Site ID'] = None self.site_detail['RF Sub-system ID'] = None self.site_detail['RFSS Network Connection'] = None self.bad_messages = 0 self.total_messages = 0 self.quality = [] self.keep_running = True self.source = None # channel filter channel_rate = self.channel_rate * 2 self.control_prefilter = filter.freq_xlating_fir_filter_ccc( 1, (1, ), 0, channel_rate) # power squelch #power_squelch = gr.pwr_squelch_cc(squelch, 1e-3, 0, True) #self.connect(self.channel_filter, power_squelch) autotuneq = gr.msg_queue(2) self.demod_watcher = demod_watcher(self) self.symbol_deviation = 600.0 if self.modulation == 'C4FM': # FM demodulator fm_demod_gain = channel_rate / (2.0 * pi * self.symbol_deviation) self.fm_demod = fm_demod = analog.quadrature_demod_cf( fm_demod_gain) moving_sum = blocks.moving_average_ff(10000, 1, 40000) subtract = blocks.sub_ff(1) divide_const = blocks.multiply_const_vff((0.0001, )) self.probe = blocks.probe_signal_f() self.connect(self.fm_demod, moving_sum, divide_const, self.probe) # symbol filter symbol_decim = 1 samples_per_symbol = channel_rate // symbol_rate symbol_coeffs = (1.0 / samples_per_symbol, ) * samples_per_symbol symbol_filter = filter.fir_filter_fff(symbol_decim, symbol_coeffs) demod_fsk4 = op25.fsk4_demod_ff(autotuneq, channel_rate, symbol_rate) elif self.modulation == 'CQPSK': # FM demodulator fm_demod_gain = channel_rate / (2.0 * pi * self.symbol_deviation) self.fm_demod = fm_demod = analog.quadrature_demod_cf( fm_demod_gain) moving_sum = blocks.moving_average_ff(10000, 1, 40000) subtract = blocks.sub_ff(1) divide_const = blocks.multiply_const_vff((0.0001, )) self.probe = blocks.probe_signal_f() self.connect(fm_demod, moving_sum, divide_const, self.probe) #self.resampler = filter.pfb.arb_resampler_ccf(float(48000)/float(channel_rate)) self.resampler = blocks.multiply_const_cc(1.0) self.agc = analog.feedforward_agc_cc(1024, 1.0) self.symbol_filter_c = blocks.multiply_const_cc(1.0) gain_mu = 0.025 omega = float(channel_rate) / float(symbol_rate) gain_omega = 0.1 * gain_mu * gain_mu alpha = 0.04 beta = 0.125 * alpha * alpha fmax = 1200 # Hz fmax = 2 * pi * fmax / float(channel_rate) self.clock = repeater.gardner_costas_cc(omega, gain_mu, gain_omega, alpha, beta, fmax, -fmax) self.diffdec = digital.diff_phasor_cc() self.to_float = blocks.complex_to_arg() self.rescale = blocks.multiply_const_ff((1 / (pi / 4))) # symbol slicer levels = [-2.0, 0.0, 2.0, 4.0] slicer = op25.fsk4_slicer_fb(levels) # frame decoder self.decodequeue = decodequeue = gr.msg_queue(1000) qsink = blocks.message_sink(gr.sizeof_char, self.decodequeue, False) self.decoder = decoder = repeater.p25_frame_assembler( '', 0, 0, False, True, True, autotuneq, False, False) if self.modulation == 'C4FM': self.connect(self.control_prefilter, fm_demod, symbol_filter, demod_fsk4, slicer, decoder, qsink) elif self.modulation == 'CQPSK': self.connect(self.resampler, self.agc, self.symbol_filter_c, self.clock, self.diffdec, self.to_float, self.rescale, slicer, decoder, qsink) ################################################## # Threads ################################################## self.connector = frontend_connector() self.client_redis = client_redis() self.redis_demod_publisher = redis_demod_publisher(parent_demod=self) quality_check_0 = threading.Thread(target=self.quality_check) quality_check_0.daemon = True quality_check_0.start() # Adjust the channel offset # self.tune_next_control_channel() #self.receive_engine() receive_engine = threading.Thread(target=self.receive_engine) receive_engine.daemon = True receive_engine.start()
def configure_blocks(self, protocol): if protocol == 'provoice' or protocol == 'analog_edacs': protocol = 'analog' self.log.debug('configure_blocks(%s)' % protocol) if not (protocol == 'p25' or protocol == 'p25_tdma' or protocol == 'p25_cqpsk' or protocol == 'p25_cqpsk_tdma' or protocol == 'provoice' or protocol == 'dsd_p25' or protocol == 'analog' or protocol == 'none'): raise Exception('Invalid protocol %s' % protocol) if self.protocol == protocol: return True self.lock() if self.protocol == 'analog': self.disconnect(self.source, self.signal_squelch, self.audiodemod, self.high_pass, self.resampler, self.sink) self.signal_squelch = None self.audiodemod = None self.high_pass = None self.resampler = None elif self.protocol == 'p25' or 'p25_tdma': try: self.disconnect(self.source, self.prefilter, self.fm_demod) #, (self.subtract,0)) self.disconnect(self.fm_demod, self.symbol_filter, self.demod_fsk4, self.slicer, self.decoder, self.float_conversion, self.sink) self.disconnect(self.slicer, self.decoder2, self.qsink) self.demod_watcher.keep_running = False except: pass #self.disconnect(self.fm_demod, self.avg, self.mult, (self.subtract,1)) self.prefilter = None self.fm_demod = None #self.avg = None #self.mult = None #self.subtract = None self.symbol_filter = None self.demod_fsk4 = None self.slicer = None self.decoder = None self.decoder2 = None self.qsink = None self.imbe = None self.float_conversion = None self.resampler = None elif self.protocol == 'p25_cqpsk' or self.protocol == 'p25_cqpsk_tdma': self.disconnect(self.source, self.resampler, self.agc, self.symbol_filter_c, self.clock, self.diffdec, self.to_float, self.rescale, self.slicer, self.decoder2, self.qsink) #, (self.subtract,0)) self.disconnect(self.slicer, self.decoder, self.float_conversion, self.sink) self.prefilter = None self.resampler = None self.agc = None self.symbol_filter_c = None self.clock = None self.diffdec = None self.to_float = None self.rescale = None self.slicer = None self.imbe = None self.decodequeue3 = None self.decodequeue2 = None self.decodequeue = None self.demod_watcher = None self.decoder = None self.decoder2 = None self.qsink = None self.float_conversion = None elif self.protocol == 'provoice': self.disconnect(self.source, self.fm_demod, self.resampler_in, self.dsd, self.out_squelch, self.sink) self.fm_demod = None self.resampler_in = None self.dsd = None self.out_squelch = None elif self.protocol == 'dsd_p25': self.disconnect(self.source, self.fm_demod, self.resampler_in, self.dsd, self.sink) self.fm_demod = None self.resampler_in = None self.dsd = None self.protocol = protocol if protocol == 'analog': self.signal_squelch = analog.pwr_squelch_cc(-100, 0.01, 0, True) #self.tone_squelch = gr.tone_squelch_ff(audiorate, 4800.0, 0.05, 300, 0, True) #tone squelch is EDACS ONLY self.audiodemod = analog.fm_demod_cf( channel_rate=self.input_rate, audio_decim=1, deviation=15000, audio_pass=(self.input_rate * 0.25), audio_stop=((self.input_rate * 0.25) + 2000), gain=8, tau=75e-6) self.high_pass = filter.fir_filter_fff( 1, firdes.high_pass(1, self.input_rate, 300, 30, firdes.WIN_HAMMING, 6.76)) self.resampler = filter.rational_resampler_fff( interpolation=8000, decimation=self.input_rate, taps=None, fractional_bw=None, ) self.connect(self.source, self.signal_squelch, self.audiodemod, self.high_pass, self.resampler, self.sink) elif protocol == 'p25' or protocol == 'p25_tdma': self.symbol_deviation = symbol_deviation = 600.0 if protocol == 'p25_tdma': symbol_rate = 6000 else: symbol_rate = 4800 channel_rate = self.input_rate self.prefilter = filter.freq_xlating_fir_filter_ccc( 1, (1, ), 0, self.input_rate) fm_demod_gain = channel_rate / (2.0 * pi * symbol_deviation) self.fm_demod = analog.quadrature_demod_cf(fm_demod_gain) #self.avg = blocks.moving_average_ff(1000, 1, 4000) #self.mult = blocks.multiply_const_vff((0.001, )) #self.subtract = blocks.sub_ff(1) symbol_decim = 1 samples_per_symbol = channel_rate // symbol_rate symbol_coeffs = (1.0 / samples_per_symbol, ) * samples_per_symbol self.symbol_filter = filter.fir_filter_fff(symbol_decim, symbol_coeffs) autotuneq = gr.msg_queue(2) self.demod_fsk4 = op25.fsk4_demod_ff(autotuneq, channel_rate, symbol_rate) # symbol slicer levels = [-2.0, 0.0, 2.0, 4.0] self.slicer = op25.fsk4_slicer_fb(levels) self.imbe = repeater.vocoder(False, True, 0, "", 0, False) self.decodequeue3 = decodequeue3 = gr.msg_queue(10000) self.decodequeue2 = decodequeue2 = gr.msg_queue(10000) self.decodequeue = decodequeue = gr.msg_queue(10000) self.demod_watcher = None #demod_watcher(decodequeue2, self.adjust_channel_offset) self.decoder = repeater.p25_frame_assembler( '', 0, 0, True, True, False, decodequeue2, True, (True if protocol == 'p25_tdma' else False)) self.decoder2 = repeater.p25_frame_assembler( '', 0, 0, False, True, False, decodequeue3, False, False) self.qsink = blocks.message_sink(gr.sizeof_char, self.decodequeue, False) self.float_conversion = blocks.short_to_float(1, 8192) self.connect(self.source, self.prefilter, self.fm_demod) #, (self.subtract,0)) #self.connect(self.fm_demod, self.symbol_filter, self.demod_fsk4, self.slicer, self.decoder, self.imbe, self.float_conversion, self.sink) self.connect(self.fm_demod, self.symbol_filter, self.demod_fsk4, self.slicer, self.decoder, self.float_conversion, self.sink) self.connect(self.slicer, self.decoder2, self.qsink) #self.connect(self.fm_demod, self.avg, self.mult, (self.subtract,1)) elif protocol == 'p25_cqpsk' or protocol == 'p25_cqpsk_tdma': self.symbol_deviation = symbol_deviation = 600.0 self.resampler = blocks.multiply_const_cc(1.0) self.agc = analog.feedforward_agc_cc(1024, 1.0) self.symbol_filter_c = blocks.multiply_const_cc(1.0) gain_mu = 0.025 if protocol == 'p25_cqpsk_tdma': symbol_rate = 6000 else: symbol_rate = 4800 omega = float(self.input_rate) / float(symbol_rate) gain_omega = 0.1 * gain_mu * gain_mu alpha = 0.04 beta = 0.125 * alpha * alpha fmax = 1200 # Hz fmax = 2 * pi * fmax / float(self.input_rate) self.clock = repeater.gardner_costas_cc(omega, gain_mu, gain_omega, alpha, beta, fmax, -fmax) self.diffdec = digital.diff_phasor_cc() self.to_float = blocks.complex_to_arg() self.rescale = blocks.multiply_const_ff((1 / (pi / 4))) # symbol slicer levels = [-2.0, 0.0, 2.0, 4.0] self.slicer = op25.fsk4_slicer_fb(levels) #self.imbe = repeater.vocoder(False, True, 0, "", 0, False) self.decodequeue3 = decodequeue3 = gr.msg_queue(2) self.decodequeue2 = decodequeue2 = gr.msg_queue(2) self.decodequeue = decodequeue = gr.msg_queue(10000) #self.demod_watcher = demod_watcher(decodequeue2, self.adjust_channel_offset) self.decoder = repeater.p25_frame_assembler( '', 0, 0, True, True, False, decodequeue2, True, (False if protocol == 'p25_cqpsk' else True)) self.decoder2 = repeater.p25_frame_assembler( '', 0, 0, False, True, True, decodequeue3, False, False) #temp for debug #self.debug_sink = blocks.file_sink(1, '/dev/null') #self.connect(self.slicer, self.debug_sink) self.qsink = blocks.message_sink(gr.sizeof_char, self.decodequeue, False) self.float_conversion = blocks.short_to_float(1, 8192) self.connect(self.source, self.resampler, self.agc, self.symbol_filter_c, self.clock, self.diffdec, self.to_float, self.rescale, self.slicer, self.decoder2, self.qsink) #, (self.subtract,0)) self.connect(self.slicer, self.decoder, self.float_conversion, self.sink) elif protocol == 'provoice': fm_demod_gain = 0.6 self.fm_demod = analog.quadrature_demod_cf(fm_demod_gain) self.resampler_in = filter.rational_resampler_fff( interpolation=48000, decimation=self.input_rate, taps=None, fractional_bw=None, ) self.dsd = dsd.block_ff(dsd.dsd_FRAME_PROVOICE, dsd.dsd_MOD_AUTO_SELECT, 3, 0, False) self.out_squelch = analog.pwr_squelch_ff(-100, 0.01, 0, True) self.connect(self.source, self.fm_demod, self.resampler_in, self.dsd, self.out_squelch, self.sink) elif protocol == 'dsd_p25': symbol_deviation = 600.0 fm_demod_gain = 0.4 #self.input_rate / (2.0 * pi * symbol_deviation) self.fm_demod = analog.quadrature_demod_cf(fm_demod_gain) self.resampler_in = filter.rational_resampler_fff( interpolation=48000, decimation=self.input_rate, taps=None, fractional_bw=None, ) self.dsd = dsd.block_ff(dsd.dsd_FRAME_P25_PHASE_1, dsd.dsd_MOD_AUTO_SELECT, 3, 3, False) self.connect(self.source, self.fm_demod, self.resampler_in, self.dsd, self.sink) self.unlock()
def __init__(self): gr.top_block.__init__(self) parser = OptionParser(option_class=eng_option) parser.add_option("-1", "--one-channel", action="store_true", default=False, help="software synthesized Q channel") parser.add_option("-a", "--agc", action="store_true", default=False, help="automatic gain control (overrides --gain)") parser.add_option("-c", "--calibration", type="eng_float", default=0, help="freq offset") parser.add_option("-d", "--debug", action="store_true", default=False, help="allow time at init to attach gdb") parser.add_option("-C", "--costas-alpha", type="eng_float", default=0.125, help="Costas alpha") parser.add_option("-g", "--gain", type="eng_float", default=1.0) parser.add_option("-i", "--input-file", type="string", default="in.dat", help="specify the input file") parser.add_option("-I", "--imbe", action="store_true", default=False, help="output IMBE codewords") parser.add_option("-L", "--low-pass", type="eng_float", default=6.5e3, help="low pass cut-off", metavar="Hz") parser.add_option("-o", "--output-file", type="string", default="out.dat", help="specify the output file") parser.add_option("-p", "--polarity", action="store_true", default=False, help="use reversed polarity") parser.add_option("-r", "--raw-symbols", type="string", default=None, help="dump decoded symbols to file") parser.add_option("-s", "--sample-rate", type="int", default=96000, help="input sample rate") parser.add_option("-t", "--tone-detect", action="store_true", default=False, help="use experimental tone detect algorithm") parser.add_option("-v", "--verbose", action="store_true", default=False, help="additional output") parser.add_option("-6", "--k6k", action="store_true", default=False, help="use 6K symbol rate") (options, args) = parser.parse_args() sample_rate = options.sample_rate if options.k6k: symbol_rate = 6000 else: symbol_rate = 4800 samples_per_symbol = sample_rate // symbol_rate IN = blocks.file_source(gr.sizeof_gr_complex, options.input_file) if options.one_channel: C2F = blocks.complex_to_float() F2C = blocks.float_to_complex() # osc./mixer for mixing signal down to approx. zero IF LO = analog.sig_source_c (sample_rate, analog.GR_COS_WAVE, options.calibration, 1.0, 0) MIXER = blocks.multiply_cc() # get signal into normalized range (-1.0 - +1.0) if options.agc: AMP = analog.feedforward_agc_cc(16, 1.0) else: AMP = blocks.multiply_const_cc(options.gain) lpf_taps = filter.firdes.low_pass(1.0, sample_rate, options.low_pass, options.low_pass * 0.1, filter.firdes.WIN_HANN) decim_amt = 1 if options.tone_detect: if sample_rate != 96000: print "warning, only 96K has been tested." print "other rates may require theta to be reviewed/adjusted." step_size = 7.5e-8 theta = -4 # optimum timing sampling point cic_length = 48 DEMOD = op25_repeater.tdetect_cc(samples_per_symbol, step_size, theta, cic_length) else: # decim by 2 to get 48k rate samples_per_symbol /= 2 # for DECIM sample_rate /= 2 # for DECIM decim_amt = 2 # create Gardner/Costas loop # the loop will not work if the sample levels aren't normalized (above) timing_error_gain = 0.025 # loop error gain gain_omega = 0.25 * timing_error_gain * timing_error_gain alpha = options.costas_alpha beta = 0.125 * alpha * alpha fmin = -0.025 # fmin and fmax are in radians/s fmax = 0.025 DEMOD = op25_repeater.gardner_costas_cc(samples_per_symbol, timing_error_gain, gain_omega, alpha, beta, fmax, fmin) DECIM = filter.fir_filter_ccf (decim_amt, lpf_taps) # probably too much phase noise etc to attempt coherent demodulation # so we use differential DIFF = digital.diff_phasor_cc() # take angle of the phase difference (in radians) TOFLOAT = blocks.complex_to_arg() # convert from radians such that signal is in [-3, -1, +1, +3] RESCALE = blocks.multiply_const_ff(1 / (pi / 4.0)) # optional polarity reversal (should be unnec. - now autodetected) p = 1.0 if options.polarity: p = -1.0 POLARITY = blocks.multiply_const_ff(p) # hard decision at specified points levels = [-2.0, 0.0, 2.0, 4.0 ] SLICER = op25_repeater.fsk4_slicer_fb(levels) # assemble received frames and route to Wireshark via UDP hostname = "127.0.0.1" port = 23456 debug = 0 if options.verbose: debug = 255 do_imbe = False if options.imbe: do_imbe = True do_output = True # enable block's output stream do_msgq = False # msgq output not yet implemented msgq = gr.msg_queue(2) DECODER = op25_repeater.p25_frame_assembler(hostname, port, debug, do_imbe, do_output, do_msgq, msgq, False, False) OUT = blocks.file_sink(gr.sizeof_char, options.output_file) if options.one_channel: self.connect(IN, C2F, F2C, (MIXER, 0)) else: self.connect(IN, (MIXER, 0)) self.connect(LO, (MIXER, 1)) self.connect(MIXER, AMP, DECIM, DEMOD, DIFF, TOFLOAT, RESCALE, POLARITY, SLICER, DECODER, OUT) if options.raw_symbols: SINKC = blocks.file_sink(gr.sizeof_char, options.raw_symbols) self.connect(SLICER, SINKC) if options.debug: print 'Ready for GDB to attach (pid = %d)' % (os.getpid(),) raw_input("Press 'Enter' to continue...")