diff options
Diffstat (limited to 'gr-digital/python/gmsk.py')
-rw-r--r-- | gr-digital/python/gmsk.py | 276 |
1 files changed, 276 insertions, 0 deletions
diff --git a/gr-digital/python/gmsk.py b/gr-digital/python/gmsk.py new file mode 100644 index 000000000..70fa197e3 --- /dev/null +++ b/gr-digital/python/gmsk.py @@ -0,0 +1,276 @@ +# +# GMSK modulation and demodulation. +# +# +# Copyright 2005,2006,2007 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 3, 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/digital for examples + +from gnuradio import gr +import digital_swig +import modulation_utils + +from math import pi +import numpy +from pprint import pprint +import inspect + +# default values (used in __init__ and add_options) +_def_samples_per_symbol = 2 +_def_bt = 0.35 +_def_verbose = False +_def_log = False + +# Symbol timing recovery +_def_timing_bw = 2*pi/100.0 +_def_timing_max_dev = 1.5 + +# ///////////////////////////////////////////////////////////////////////////// +# GMSK modulator +# ///////////////////////////////////////////////////////////////////////////// + +class gmsk_mod(gr.hier_block2): + + def __init__(self, + samples_per_symbol=_def_samples_per_symbol, + bt=_def_bt, + verbose=_def_verbose, + log=_def_log): + """ + 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 samples_per_symbol: samples per baud >= 2 + @type samples_per_symbol: integer + @param bt: Gaussian filter bandwidth * symbol time + @type bt: float + @param verbose: Print information about modulator? + @type verbose: bool + @param debug: Print modualtion data to files? + @type debug: bool + """ + + gr.hier_block2.__init__(self, "gmsk_mod", + gr.io_signature(1, 1, gr.sizeof_char), # Input signature + gr.io_signature(1, 1, gr.sizeof_gr_complex)) # Output signature + + self._samples_per_symbol = samples_per_symbol + self._bt = bt + + self._differential = False # make consistant with other modulators + + if samples_per_symbol < 2: + raise TypeError, ("samples_per_symbol must >= 2, is %r" % \ + (samples_per_symbol,)) + + ntaps = 4 * samples_per_symbol # up to 3 bits in filter at once + sensitivity = (pi / 2) / samples_per_symbol # 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 + samples_per_symbol, # symbol_rate + bt, # bandwidth * symbol time + int(ntaps) # number of taps + ) + + self.sqwave = (1,) * int(samples_per_symbol) # rectangular window + self.taps = numpy.convolve(numpy.array(self.gaussian_taps),numpy.array(self.sqwave)) + self.gaussian_filter = gr.pfb_arb_resampler_fff(samples_per_symbol, self.taps) + + # FM modulation + self.fmmod = gr.frequency_modulator_fc(sensitivity) + + if verbose: + self._print_verbage() + + if log: + self._setup_logging() + + # Connect & Initialize base class + self.connect(self, self.nrz, self.gaussian_filter, self.fmmod, self) + + def samples_per_symbol(self): + return self._samples_per_symbol + + def bits_per_symbol(self=None): # staticmethod that's also callable on an instance + return 1 + bits_per_symbol = staticmethod(bits_per_symbol) # make it a static method. + + + def _print_verbage(self): + print "bits per symbol = %d" % self.bits_per_symbol() + print "Gaussian filter bt = %.2f" % self._bt + + + def _setup_logging(self): + print "Modulation logging turned on." + self.connect(self.nrz, + gr.file_sink(gr.sizeof_float, "tx_gmsk_nrz.32f")) + self.connect(self.gaussian_filter, + gr.file_sink(gr.sizeof_float, "tx_gmsk_gaussian_filter.32f")) + self.connect(self.fmmod, + gr.file_sink(gr.sizeof_gr_complex, "tx_gmsk_fmmod.32fc")) + + + def add_options(parser): + """ + Adds GMSK modulation-specific options to the standard parser + """ + parser.add_option("", "--bt", type="float", default=_def_bt, + help="set bandwidth-time product [default=%default] (GMSK)") + add_options=staticmethod(add_options) + + + def extract_kwargs_from_options(options): + """ + Given command line options, create dictionary suitable for passing to __init__ + """ + return modulation_utils.extract_kwargs_from_options(gmsk_mod.__init__, + ('self',), options) + extract_kwargs_from_options=staticmethod(extract_kwargs_from_options) + + + +# ///////////////////////////////////////////////////////////////////////////// +# GMSK demodulator +# ///////////////////////////////////////////////////////////////////////////// + +class gmsk_demod(gr.hier_block2): + + def __init__(self, + samples_per_symbol=_def_samples_per_symbol, + bt=_def_bt, + timing_bw=_def_timing_bw, + verbose=_def_verbose, + log=_def_log): + """ + Hierarchical block for Gaussian Minimum Shift Key (GMSK) + demodulation. + + The input is the complex modulated signal at baseband. + The output is a stream of bits packed 1 bit per byte (the LSB) + + @param samples_per_symbol: samples per baud + @type samples_per_symbol: integer + @param timing_bw: timing recovery loop lock-in bandwidth + @type timing_bw: float + @param verbose: Print information about modulator? + @type verbose: bool + @param log: Print modualtion data to files? + @type log: bool + """ + + gr.hier_block2.__init__(self, "gmsk_demod", + gr.io_signature(1, 1, gr.sizeof_gr_complex), # Input signature + gr.io_signature(1, 1, gr.sizeof_char)) # Output signature + + self._samples_per_symbol = samples_per_symbol + self._bt = bt + self._timing_bw = timing_bw + self._timing_max_dev= _def_timing_max_dev + + self._differential = False # make consistant with other modulators + + if samples_per_symbol < 2: + raise TypeError, "samples_per_symbol >= 2, is %f" % samples_per_symbol + + # Demodulate FM + sensitivity = (pi / 2) / samples_per_symbol + self.fmdemod = gr.quadrature_demod_cf(1.0 / sensitivity) + + # the clock recovery block tracks the symbol clock and resamples as needed. + # the output of the block is a stream of soft symbols (float) + nfilts = 32 + ntaps = 11 * int(self._samples_per_symbol*nfilts) + taps = gr.firdes.gaussian(nfilts, + nfilts*self._samples_per_symbol, + self._bt, ntaps) + self.clock_recovery = \ + gr.pfb_clock_sync_fff(self._samples_per_symbol, + self._timing_bw, taps, + nfilts, nfilts//2, self._timing_max_dev) + + # slice the floats at 0, outputting 1 bit (the LSB of the output byte) per sample + self.slicer = digital_swig.binary_slicer_fb() + + if verbose: + self._print_verbage() + + if log: + self._setup_logging() + + # Connect & Initialize base class + self.connect(self, self.fmdemod, self.clock_recovery, self.slicer, self) + + def samples_per_symbol(self): + return self._samples_per_symbol + + def bits_per_symbol(self=None): # staticmethod that's also callable on an instance + return 1 + bits_per_symbol = staticmethod(bits_per_symbol) # make it a static method. + + + def _print_verbage(self): + print "bits per symbol: %d" % self.bits_per_symbol() + print "Bandwidth-Time Prod: %f" % self._bt + print "Timing bandwidth: %.2e" % self._timing_bw + + + def _setup_logging(self): + print "Demodulation logging turned on." + self.connect(self.fmdemod, + gr.file_sink(gr.sizeof_float, "rx_gmsk_fmdemod.32f")) + self.connect(self.clock_recovery, + gr.file_sink(gr.sizeof_float, "rx_gmsk_clock_recovery.32f")) + self.connect(self.slicer, + gr.file_sink(gr.sizeof_char, "rx_gmsk_slicer.8b")) + + def add_options(parser): + """ + Adds GMSK demodulation-specific options to the standard parser + """ + parser.add_option("", "--timing-bw", type="float", default=_def_timing_bw, + help="set timing symbol sync loop gain lock-in bandwidth [default=%default]") + parser.add_option("", "--bt", type="float", default=_def_bt, + help="set bandwidth-time product [default=%default] (GMSK)") + add_options=staticmethod(add_options) + + def extract_kwargs_from_options(options): + """ + Given command line options, create dictionary suitable for passing to __init__ + """ + return modulation_utils.extract_kwargs_from_options(gmsk_demod.__init__, + ('self',), options) + extract_kwargs_from_options=staticmethod(extract_kwargs_from_options) + +# +# Add these to the mod/demod registry +# +modulation_utils.add_type_1_mod('gmsk', gmsk_mod) +modulation_utils.add_type_1_demod('gmsk', gmsk_demod) |