root / gr-digital / python / gmsk.py @ 73ccc57f
History | View | Annotate | Download (9.7 kB)
| 1 | #
|
|---|---|
| 2 | # GMSK modulation and demodulation.
|
| 3 | #
|
| 4 | #
|
| 5 | # Copyright 2005,2006,2007 Free Software Foundation, Inc.
|
| 6 | #
|
| 7 | # This file is part of GNU Radio
|
| 8 | #
|
| 9 | # GNU Radio is free software; you can redistribute it and/or modify
|
| 10 | # it under the terms of the GNU General Public License as published by
|
| 11 | # the Free Software Foundation; either version 3, or (at your option)
|
| 12 | # any later version.
|
| 13 | #
|
| 14 | # GNU Radio is distributed in the hope that it will be useful,
|
| 15 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
|
| 16 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
| 17 | # GNU General Public License for more details.
|
| 18 | #
|
| 19 | # You should have received a copy of the GNU General Public License
|
| 20 | # along with GNU Radio; see the file COPYING. If not, write to
|
| 21 | # the Free Software Foundation, Inc., 51 Franklin Street,
|
| 22 | # Boston, MA 02110-1301, USA.
|
| 23 | #
|
| 24 | |
| 25 | # See gnuradio-examples/python/digital for examples
|
| 26 | |
| 27 | from gnuradio import gr |
| 28 | import digital_swig |
| 29 | import modulation_utils |
| 30 | |
| 31 | from math import pi |
| 32 | import numpy |
| 33 | from pprint import pprint |
| 34 | import inspect |
| 35 | |
| 36 | # default values (used in __init__ and add_options)
|
| 37 | _def_samples_per_symbol = 2
|
| 38 | _def_bt = 0.35
|
| 39 | _def_verbose = False
|
| 40 | _def_log = False
|
| 41 | |
| 42 | # Symbol timing recovery
|
| 43 | _def_timing_bw = 2*pi/100.0 |
| 44 | _def_timing_max_dev = 1.5
|
| 45 | |
| 46 | # /////////////////////////////////////////////////////////////////////////////
|
| 47 | # GMSK modulator
|
| 48 | # /////////////////////////////////////////////////////////////////////////////
|
| 49 | |
| 50 | class gmsk_mod(gr.hier_block2): |
| 51 | |
| 52 | def __init__(self, |
| 53 | samples_per_symbol=_def_samples_per_symbol, |
| 54 | bt=_def_bt, |
| 55 | verbose=_def_verbose, |
| 56 | log=_def_log): |
| 57 | """
|
| 58 | Hierarchical block for Gaussian Minimum Shift Key (GMSK)
|
| 59 | modulation.
|
| 60 | |
| 61 | The input is a byte stream (unsigned char) and the
|
| 62 | output is the complex modulated signal at baseband.
|
| 63 | |
| 64 | @param samples_per_symbol: samples per baud >= 2
|
| 65 | @type samples_per_symbol: integer
|
| 66 | @param bt: Gaussian filter bandwidth * symbol time
|
| 67 | @type bt: float
|
| 68 | @param verbose: Print information about modulator?
|
| 69 | @type verbose: bool
|
| 70 | @param debug: Print modualtion data to files?
|
| 71 | @type debug: bool
|
| 72 | """ |
| 73 | |
| 74 | gr.hier_block2.__init__(self, "gmsk_mod", |
| 75 | gr.io_signature(1, 1, gr.sizeof_char), # Input signature |
| 76 | gr.io_signature(1, 1, gr.sizeof_gr_complex)) # Output signature |
| 77 | |
| 78 | self._samples_per_symbol = samples_per_symbol
|
| 79 | self._bt = bt
|
| 80 | |
| 81 | self._differential = False # make consistant with other modulators |
| 82 | |
| 83 | if samples_per_symbol < 2: |
| 84 | raise TypeError, ("samples_per_symbol must >= 2, is %r" % \ |
| 85 | (samples_per_symbol,)) |
| 86 | |
| 87 | ntaps = 4 * samples_per_symbol # up to 3 bits in filter at once |
| 88 | sensitivity = (pi / 2) / samples_per_symbol # phase change per bit = pi / 2 |
| 89 | |
| 90 | # Turn it into NRZ data.
|
| 91 | self.nrz = gr.bytes_to_syms()
|
| 92 | |
| 93 | # Form Gaussian filter
|
| 94 | # Generate Gaussian response (Needs to be convolved with window below).
|
| 95 | self.gaussian_taps = gr.firdes.gaussian(
|
| 96 | 1, # gain |
| 97 | samples_per_symbol, # symbol_rate
|
| 98 | bt, # bandwidth * symbol time
|
| 99 | int(ntaps) # number of taps |
| 100 | ) |
| 101 | |
| 102 | self.sqwave = (1,) * int(samples_per_symbol) # rectangular window |
| 103 | self.taps = numpy.convolve(numpy.array(self.gaussian_taps),numpy.array(self.sqwave)) |
| 104 | self.gaussian_filter = gr.pfb_arb_resampler_fff(samples_per_symbol, self.taps) |
| 105 | |
| 106 | # FM modulation
|
| 107 | self.fmmod = gr.frequency_modulator_fc(sensitivity)
|
| 108 | |
| 109 | if verbose:
|
| 110 | self._print_verbage()
|
| 111 | |
| 112 | if log:
|
| 113 | self._setup_logging()
|
| 114 | |
| 115 | # Connect & Initialize base class
|
| 116 | self.connect(self, self.nrz, self.gaussian_filter, self.fmmod, self) |
| 117 | |
| 118 | def samples_per_symbol(self): |
| 119 | return self._samples_per_symbol |
| 120 | |
| 121 | def bits_per_symbol(self=None): # staticmethod that's also callable on an instance |
| 122 | return 1 |
| 123 | bits_per_symbol = staticmethod(bits_per_symbol) # make it a static method. |
| 124 | |
| 125 | |
| 126 | def _print_verbage(self): |
| 127 | print "bits per symbol = %d" % self.bits_per_symbol() |
| 128 | print "Gaussian filter bt = %.2f" % self._bt |
| 129 | |
| 130 | |
| 131 | def _setup_logging(self): |
| 132 | print "Modulation logging turned on." |
| 133 | self.connect(self.nrz, |
| 134 | gr.file_sink(gr.sizeof_float, "tx_gmsk_nrz.32f"))
|
| 135 | self.connect(self.gaussian_filter, |
| 136 | gr.file_sink(gr.sizeof_float, "tx_gmsk_gaussian_filter.32f"))
|
| 137 | self.connect(self.fmmod, |
| 138 | gr.file_sink(gr.sizeof_gr_complex, "tx_gmsk_fmmod.32fc"))
|
| 139 | |
| 140 | |
| 141 | def add_options(parser): |
| 142 | """
|
| 143 | Adds GMSK modulation-specific options to the standard parser
|
| 144 | """ |
| 145 | parser.add_option("", "--bt", type="float", default=_def_bt, |
| 146 | help="set bandwidth-time product [default=%default] (GMSK)")
|
| 147 | add_options=staticmethod(add_options)
|
| 148 | |
| 149 | |
| 150 | def extract_kwargs_from_options(options): |
| 151 | """
|
| 152 | Given command line options, create dictionary suitable for passing to __init__
|
| 153 | """ |
| 154 | return modulation_utils.extract_kwargs_from_options(gmsk_mod.__init__,
|
| 155 | ('self',), options)
|
| 156 | extract_kwargs_from_options=staticmethod(extract_kwargs_from_options)
|
| 157 | |
| 158 | |
| 159 | |
| 160 | # /////////////////////////////////////////////////////////////////////////////
|
| 161 | # GMSK demodulator
|
| 162 | # /////////////////////////////////////////////////////////////////////////////
|
| 163 | |
| 164 | class gmsk_demod(gr.hier_block2): |
| 165 | |
| 166 | def __init__(self, |
| 167 | samples_per_symbol=_def_samples_per_symbol, |
| 168 | bt=_def_bt, |
| 169 | timing_bw=_def_timing_bw, |
| 170 | verbose=_def_verbose, |
| 171 | log=_def_log): |
| 172 | """
|
| 173 | Hierarchical block for Gaussian Minimum Shift Key (GMSK)
|
| 174 | demodulation.
|
| 175 | |
| 176 | The input is the complex modulated signal at baseband.
|
| 177 | The output is a stream of bits packed 1 bit per byte (the LSB)
|
| 178 | |
| 179 | @param samples_per_symbol: samples per baud
|
| 180 | @type samples_per_symbol: integer
|
| 181 | @param timing_bw: timing recovery loop lock-in bandwidth
|
| 182 | @type timing_bw: float
|
| 183 | @param verbose: Print information about modulator?
|
| 184 | @type verbose: bool
|
| 185 | @param log: Print modualtion data to files?
|
| 186 | @type log: bool
|
| 187 | """ |
| 188 | |
| 189 | gr.hier_block2.__init__(self, "gmsk_demod", |
| 190 | gr.io_signature(1, 1, gr.sizeof_gr_complex), # Input signature |
| 191 | gr.io_signature(1, 1, gr.sizeof_char)) # Output signature |
| 192 | |
| 193 | self._samples_per_symbol = samples_per_symbol
|
| 194 | self._bt = bt
|
| 195 | self._timing_bw = timing_bw
|
| 196 | self._timing_max_dev= _def_timing_max_dev
|
| 197 | |
| 198 | self._differential = False # make consistant with other modulators |
| 199 | |
| 200 | if samples_per_symbol < 2: |
| 201 | raise TypeError, "samples_per_symbol >= 2, is %f" % samples_per_symbol |
| 202 | |
| 203 | # Demodulate FM
|
| 204 | sensitivity = (pi / 2) / samples_per_symbol
|
| 205 | self.fmdemod = gr.quadrature_demod_cf(1.0 / sensitivity) |
| 206 | |
| 207 | # the clock recovery block tracks the symbol clock and resamples as needed.
|
| 208 | # the output of the block is a stream of soft symbols (float)
|
| 209 | nfilts = 32
|
| 210 | ntaps = 11 * int(self._samples_per_symbol*nfilts) |
| 211 | taps = gr.firdes.gaussian(nfilts, |
| 212 | nfilts*self._samples_per_symbol,
|
| 213 | self._bt, ntaps)
|
| 214 | self.clock_recovery = \
|
| 215 | gr.pfb_clock_sync_fff(self._samples_per_symbol,
|
| 216 | self._timing_bw, taps,
|
| 217 | nfilts, nfilts//2, self._timing_max_dev) |
| 218 | |
| 219 | # slice the floats at 0, outputting 1 bit (the LSB of the output byte) per sample
|
| 220 | self.slicer = digital_swig.binary_slicer_fb()
|
| 221 | |
| 222 | if verbose:
|
| 223 | self._print_verbage()
|
| 224 | |
| 225 | if log:
|
| 226 | self._setup_logging()
|
| 227 | |
| 228 | # Connect & Initialize base class
|
| 229 | self.connect(self, self.fmdemod, self.clock_recovery, self.slicer, self) |
| 230 | |
| 231 | def samples_per_symbol(self): |
| 232 | return self._samples_per_symbol |
| 233 | |
| 234 | def bits_per_symbol(self=None): # staticmethod that's also callable on an instance |
| 235 | return 1 |
| 236 | bits_per_symbol = staticmethod(bits_per_symbol) # make it a static method. |
| 237 | |
| 238 | |
| 239 | def _print_verbage(self): |
| 240 | print "bits per symbol: %d" % self.bits_per_symbol() |
| 241 | print "Bandwidth-Time Prod: %f" % self._bt |
| 242 | print "Timing bandwidth: %.2e" % self._timing_bw |
| 243 | |
| 244 | |
| 245 | def _setup_logging(self): |
| 246 | print "Demodulation logging turned on." |
| 247 | self.connect(self.fmdemod, |
| 248 | gr.file_sink(gr.sizeof_float, "rx_gmsk_fmdemod.32f"))
|
| 249 | self.connect(self.clock_recovery, |
| 250 | gr.file_sink(gr.sizeof_float, "rx_gmsk_clock_recovery.32f"))
|
| 251 | self.connect(self.slicer, |
| 252 | gr.file_sink(gr.sizeof_char, "rx_gmsk_slicer.8b"))
|
| 253 | |
| 254 | def add_options(parser): |
| 255 | """
|
| 256 | Adds GMSK demodulation-specific options to the standard parser
|
| 257 | """ |
| 258 | parser.add_option("", "--timing-bw", type="float", default=_def_timing_bw, |
| 259 | help="set timing symbol sync loop gain lock-in bandwidth [default=%default]")
|
| 260 | parser.add_option("", "--bt", type="float", default=_def_bt, |
| 261 | help="set bandwidth-time product [default=%default] (GMSK)")
|
| 262 | add_options=staticmethod(add_options)
|
| 263 | |
| 264 | def extract_kwargs_from_options(options): |
| 265 | """
|
| 266 | Given command line options, create dictionary suitable for passing to __init__
|
| 267 | """ |
| 268 | return modulation_utils.extract_kwargs_from_options(gmsk_demod.__init__,
|
| 269 | ('self',), options)
|
| 270 | extract_kwargs_from_options=staticmethod(extract_kwargs_from_options)
|
| 271 | |
| 272 | #
|
| 273 | # Add these to the mod/demod registry
|
| 274 | #
|
| 275 | modulation_utils.add_type_1_mod('gmsk', gmsk_mod)
|
| 276 | modulation_utils.add_type_1_demod('gmsk', gmsk_demod)
|