demodulator updates
parent
5edfc1c463
commit
35da7d958a
|
@ -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]
|
||||
|
|
Loading…
Reference in New Issue