diff --git a/op25/gr-op25_repeater/apps/tx/op25_c4fm_mod.py b/op25/gr-op25_repeater/apps/tx/op25_c4fm_mod.py index 9e0e872..7952748 100755 --- a/op25/gr-op25_repeater/apps/tx/op25_c4fm_mod.py +++ b/op25/gr-op25_repeater/apps/tx/op25_c4fm_mod.py @@ -2,7 +2,10 @@ # Copyright 2005,2006,2007 Free Software Foundation, Inc. # # OP25 4-Level Modulator Block -# Copyright 2009, 2010, 2011, 2012, 2013, 2014, 2015 Max H. Parke KA1RBI +# Copyright 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017 Max H. Parke KA1RBI +# +# GMSK code from gnuradio gr-digital/python/digital/gmsk.py +# gmsk.py is Copyright 2005-2007,2012 Free Software Foundation, Inc. # # This file is part of GNU Radio and part of OP25 # @@ -23,7 +26,8 @@ # """ -P25 C4FM pre-modulation block. +P25 C4FM pre-modulation block +with additions for RRC (for dmr/ysf) and dstar gmsk """ from gnuradio import gr, gru, eng_notation @@ -42,6 +46,8 @@ _def_reverse = False _def_verbose = False _def_log = False _def_span = 13 #desired number of impulse response coeffs, in units of symbols +_def_gmsk_span = 4 +_def_bt = 0.25 def transfer_function_rx(): # p25 c4fm de-emphasis filter @@ -108,6 +114,32 @@ class c4fm_taps(object): def generate_code(self, varname='taps'): return '%s = [\n\t%s]' % (varname, ',\n\t'.join(['%10.6e' % f for f in self.generate()])) +class gmsk_taps(object): + def __init__(self, filter_gain = 1.0, sample_rate=_def_output_sample_rate, symbol_rate=_def_symbol_rate, span=_def_gmsk_span, bt=_def_bt): + self.filter_gain = filter_gain + self.sample_rate = sample_rate + self.symbol_rate = symbol_rate + self.span = span + self.bt = bt + + self.samples_per_symbol = self.sample_rate / self.symbol_rate + self.ntaps = self.span * self.samples_per_symbol + + def generate(self): + # from gnuradio gr-digital/python/digital/gmsk.py + # Form Gaussian filter + # Generate Gaussian response (Needs to be convolved with window below). + gaussian_taps = filter.firdes.gaussian( + self.filter_gain, # gain + self.samples_per_symbol, # symbol_rate + self.bt, # bandwidth * symbol time + self.ntaps # number of taps + ) + + sqwave = (1,) * self.samples_per_symbol # rectangular window + taps = np.convolve(np.array(gaussian_taps),np.array(sqwave)) + return taps + # ///////////////////////////////////////////////////////////////////////////// # modulator # ///////////////////////////////////////////////////////////////////////////// @@ -119,6 +151,8 @@ class p25_mod_bf(gr.hier_block2): reverse=_def_reverse, verbose=_def_verbose, generator=transfer_function_tx, + dstar=False, + bt=_def_bt, log=_def_log): """ Hierarchical block for RRC-filtered P25 FM modulation. @@ -149,8 +183,14 @@ class p25_mod_bf(gr.hier_block2): self._interp_factor = int(lcm // input_sample_rate) self._decimation = int(lcm // output_sample_rate) - mod_map = [1.0/3.0, 1.0, -(1.0/3.0), -1.0] - self.C2S = digital.chunks_to_symbols_bf(mod_map) + self.dstar = dstar + self.bt = bt + + if self.dstar: + self.C2S = digital.chunks_to_symbols_bf([-1, 1], 1) + else: + mod_map = [1.0/3.0, 1.0, -(1.0/3.0), -1.0] + self.C2S = digital.chunks_to_symbols_bf(mod_map) if reverse: self.polarity = blocks.multiply_const_ff(-1) else: @@ -158,7 +198,11 @@ class p25_mod_bf(gr.hier_block2): self.generator = generator - self.filter = filter.interp_fir_filter_fff(self._interp_factor, c4fm_taps(sample_rate=output_sample_rate, generator=self.generator).generate()) + if self.dstar: + coeffs = gmsk_taps(sample_rate=output_sample_rate, bt=self.bt).generate() + else: + coeffs = c4fm_taps(sample_rate=output_sample_rate, generator=self.generator).generate() + self.filter = filter.interp_fir_filter_fff(self._interp_factor, coeffs) if verbose: self._print_verbage()