diff --git a/op25/gr-op25_repeater/apps/p25_demodulator.py b/op25/gr-op25_repeater/apps/p25_demodulator.py index 646d621..805746a 100644 --- a/op25/gr-op25_repeater/apps/p25_demodulator.py +++ b/op25/gr-op25_repeater/apps/p25_demodulator.py @@ -45,14 +45,35 @@ _def_costas_alpha = 0.04 _def_symbol_rate = 4800 _def_symbol_deviation = 600.0 _def_bb_gain = 1.0 +_def_excess_bw = 0.2 # ///////////////////////////////////////////////////////////////////////////// # demodulator # ///////////////////////////////////////////////////////////////////////////// +def get_decim(speed): + s = int(speed) + if_freqs = [24000, 25000, 32000] + for i_f in if_freqs: + if s % i_f != 0: + continue + q = s / i_f + if q & 1: + continue + if q >= 40 and q & 3 == 0: + decim = q/4 + decim2 = 4 + else: + decim = q/2 + decim2 = 2 + return decim, decim2 + return None + class p25_demod_base(gr.hier_block2): def __init__(self, if_rate = None, + filter_type = None, + excess_bw = _def_excess_bw, symbol_rate = _def_symbol_rate): """ Hierarchical block for P25 demodulation base class @@ -66,6 +87,12 @@ class p25_demod_base(gr.hier_block2): self.baseband_amp = blocks.multiply_const_ff(_def_bb_gain) coeffs = op25_c4fm_mod.c4fm_taps(sample_rate=self.if_rate, span=9, generator=op25_c4fm_mod.transfer_function_rx).generate() + if filter_type == 'rrc': + sps = self.if_rate / 4800 + ntaps = 7 * sps + if ntaps & 1 == 0: + ntaps += 1 + coeffs = filter.firdes.root_raised_cosine(1.0, if_rate, symbol_rate, excess_bw, ntaps) self.symbol_filter = filter.fir_filter_fff(1, coeffs) autotuneq = gr.msg_queue(2) self.fsk4_demod = op25.fsk4_demod_ff(autotuneq, self.if_rate, self.symbol_rate) @@ -73,6 +100,9 @@ class p25_demod_base(gr.hier_block2): levels = [ -2.0, 0.0, 2.0, 4.0 ] self.slicer = op25_repeater.fsk4_slicer_fb(levels) + def set_symbol_rate(self, rate): + self.symbol_rate = rate + def set_baseband_gain(self, k): self.baseband_amp.set_k(k) @@ -97,6 +127,8 @@ class p25_demod_fb(p25_demod_base): def __init__(self, input_rate = None, + filter_type = None, + excess_bw = _def_excess_bw, symbol_rate = _def_symbol_rate): """ Hierarchical block for P25 demodulation. @@ -110,7 +142,7 @@ class p25_demod_fb(p25_demod_base): gr.io_signature(1, 1, gr.sizeof_float), # Input signature gr.io_signature(1, 1, gr.sizeof_char)) # Output signature - p25_demod_base.__init__(self, if_rate=input_rate, symbol_rate=symbol_rate) + p25_demod_base.__init__(self, if_rate=input_rate, symbol_rate=symbol_rate, filter_type=filter_type) self.input_rate = input_rate self.float_sink = None @@ -135,6 +167,8 @@ class p25_demod_cb(p25_demod_base): 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, @@ -153,7 +187,7 @@ class p25_demod_cb(p25_demod_base): 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) + 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 @@ -164,22 +198,52 @@ class p25_demod_cb(p25_demod_base): self.lo_freq = 0 self.float_sink = None self.complex_sink = None + self.if1 = None + self.if2 = None + 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() - 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) + 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, 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.connect(self, (self.mixer, 0)) + self.connect(self.lo, (self.mixer, 1)) + self.connect(self.mixer, self.lpf) - resampled_rate = float(input_rate) / float(decimation) # rate at output of 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 - 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) + 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) levels = [ -2.0, 0.0, 2.0, 4.0 ] self.slicer = op25_repeater.fsk4_slicer_fb(levels) @@ -214,6 +278,9 @@ class p25_demod_cb(p25_demod_base): self.set_relative_frequency(relative_freq) + def get_freq_error(self): # get error in Hz (approx). + return int(self.clock.get_freq_error() * self.symbol_rate) + def set_omega(self, omega): sps = self.if_rate / float(omega) if sps == self.sps: @@ -228,17 +295,28 @@ class p25_demod_cb(p25_demod_base): return False if freq == self.lo_freq: return True - #print 'set_relative_frequency', freq self.lo_freq = freq - self.lo.set_frequency(self.lo_freq) + if self.if1: + if freq not in self.t_cache.keys(): + self.t_cache[freq] = filter.firdes.complex_band_pass(1.0, self.input_rate, -freq - self.if1/2, -freq + self.if1/2, self.if1/2, filter.firdes.WIN_HAMMING) + self.bpf.set_taps(self.t_cache[freq]) + bfo_f = self.decim * -freq / float(self.input_rate) + bfo_f -= int(bfo_f) + if bfo_f < -0.5: + bfo_f += 1.0 + if bfo_f > 0.5: + bfo_f -= 1.0 + self.bfo.set_frequency(-bfo_f * self.if1) + else: + self.lo.set_frequency(self.lo_freq) return True # assumes lock held or init def disconnect_chain(self): if self.connect_state == 'cqpsk': - self.disconnect(self.arb_resampler, self.agc, self.clock, self.diffdec, self.to_float, self.rescale, self.slicer) + self.disconnect(self.if_out, self.cutoff, self.agc, self.clock, self.diffdec, self.to_float, self.rescale, self.slicer) elif self.connect_state == 'fsk4': - self.disconnect(self.arb_resampler, self.fm_demod, self.baseband_amp, self.symbol_filter, self.fsk4_demod, self.slicer) + self.disconnect(self.if_out, self.cutoff, self.fm_demod, self.baseband_amp, self.symbol_filter, self.fsk4_demod, self.slicer) self.connect_state = None # assumes lock held or init @@ -248,9 +326,9 @@ class p25_demod_cb(p25_demod_base): self.disconnect_chain() self.connect_state = demod_type if demod_type == 'fsk4': - self.connect(self.arb_resampler, self.fm_demod, self.baseband_amp, self.symbol_filter, self.fsk4_demod, self.slicer) + self.connect(self.if_out, self.cutoff, self.fm_demod, self.baseband_amp, self.symbol_filter, self.fsk4_demod, self.slicer) elif demod_type == 'cqpsk': - self.connect(self.arb_resampler, self.agc, self.clock, self.diffdec, self.to_float, self.rescale, self.slicer) + self.connect(self.if_out, self.cutoff, self.agc, self.clock, self.diffdec, self.to_float, self.rescale, self.slicer) else: print 'connect_chain failed, type: %s' % demod_type assert 0 == 1 @@ -299,3 +377,12 @@ class p25_demod_cb(p25_demod_base): elif src == 'src': self.connect(self, sink) self.complex_sink = [self, sink] + elif src == 'bpf': + self.connect(self.bpf, sink) + self.complex_sink = [self.bpf, sink] + elif src == 'if_out': + self.connect(self.if_out, sink) + self.complex_sink = [self.if_out, sink] + elif src == 'agc': + self.connect(self.agc, sink) + self.complex_sink = [self.agc, sink]