# # GMSK modulation and demodulation. # # # Copyright 2005,2006 Free Software Foundation, Inc. # # This file is part of GNU Radio # # GNU Radio is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2, or (at your option) # any later version. # # GNU Radio is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with GNU Radio; see the file COPYING. If not, write to # the Free Software Foundation, Inc., 51 Franklin Street, # Boston, MA 02110-1301, USA. # # See gnuradio-examples/python/gmsk2 for examples from gnuradio import gr from math import pi import Numeric # ///////////////////////////////////////////////////////////////////////////// # GMSK mod/demod with steams of bytes as data i/o # ///////////////////////////////////////////////////////////////////////////// class gmsk2_mod(gr.hier_block): def __init__(self, fg, spb = 2, bt = 0.3): """ Hierarchical block for Gaussian Minimum Shift Key (GMSK) modulation. The input is a byte stream (unsigned char) and the output is the complex modulated signal at baseband. @param fg: flow graph @type fg: flow graph @param spb: samples per baud >= 2 @type spb: integer @param bt: Gaussian filter bandwidth * symbol time @type bt: float """ if not isinstance(spb, int) or spb < 2: raise TypeError, "sbp must be an integer >= 2" self.spb = spb ntaps = 4 * spb # up to 3 bits in filter at once sensitivity = (pi / 2) / spb # phase change per bit = pi / 2 # Turn it into NRZ data. self.nrz = gr.bytes_to_syms() # Form Gaussian filter # Generate Gaussian response (Needs to be convolved with window below). self.gaussian_taps = gr.firdes.gaussian( 1, # gain spb, # symbol_rate bt, # bandwidth * symbol time ntaps # number of taps ) self.sqwave = (1,) * spb # rectangular window self.taps = Numeric.convolve(Numeric.array(self.gaussian_taps),Numeric.array(self.sqwave)) self.gaussian_filter = gr.interp_fir_filter_fff(spb, self.taps) # FM modulation self.fmmod = gr.frequency_modulator_fc(sensitivity) # Connect fg.connect(self.nrz, self.gaussian_filter, self.fmmod) # Initialize base class gr.hier_block.__init__(self, fg, self.nrz, self.fmmod) def samples_per_baud(self): return self.spb def bits_per_baud(self=None): # staticmethod that's also callable on an instance return 1 bits_per_baud = staticmethod(bits_per_baud) # make it a static method. RTFM class gmsk2_demod(gr.hier_block): def __init__(self, fg, spb=2, omega=None, gain_mu=0.03, mu=0.5, omega_relative_limit=0.000200, freq_error=0.0): """ Hierarchical block for Gaussian Minimum Shift Key (GMSK) demodulation. The input is the complex modulated signal at baseband. The output is a stream of symbols ready to be sliced at zero. @param fg: flow graph @type fg: flow graph @param spb: samples per baud @type spb: integer Clock recovery parameters. These all have reasonble defaults. @param omega: nominal relative freq (defaults to spb) @type omega: float @param gain_mu: controls rate of mu adjustment @type gain_mu: float @param mu: fractional delay [0.0, 1.0] @type mu: float @param omega_relative_limit: sets max variation in omega @type omega_relative_limit: float, typically 0.000200 (200 ppm) @param freq_error: bit rate error as a fraction @param float """ if spb < 2: raise TypeError, "sbp >= 2" self.spb = spb if omega is None: omega = spb*(1+freq_error) gain_omega = .25*gain_mu*gain_mu # critically damped # Automatic gain control self.preamp = gr.multiply_const_cc(10e-5) self.agc = gr.agc_cc(1e-3, 1, 1, 1000) # Demodulate FM sensitivity = (pi / 2) / spb self.fmdemod = gr.quadrature_demod_cf(1.0 / sensitivity) alpha = 0.0008 # the clock recovery block tracks the symbol clock and resamples as needed. # the output of the block is a stream of soft symbols (float) self.clock_recovery = gr.clock_recovery_mm_ff(omega, gain_omega, mu, gain_mu, omega_relative_limit) # slice the floats at 0, outputting 1 bit (the LSB of the output byte) per sample self.slicer = gr.binary_slicer_fb() fg.connect(self.preamp, self.agc, self.fmdemod, self.clock_recovery, self.slicer) # Initialize base class gr.hier_block.__init__(self, fg, self.preamp, self.slicer) def samples_per_baud(self): return self.spb def bits_per_baud(self=None): # staticmethod that's also callable on an instance return 1 bits_per_baud = staticmethod(bits_per_baud) # make it a static method. RTFM