diff options
author | eb <eb@221aa14e-8319-0410-a670-987f0aec2ac5> | 2006-09-27 05:14:03 +0000 |
---|---|---|
committer | eb <eb@221aa14e-8319-0410-a670-987f0aec2ac5> | 2006-09-27 05:14:03 +0000 |
commit | f1c41f807cb29472d0924149e39d6ec8ad90e6a2 (patch) | |
tree | 47e42f8cdfc83aaa3b706e06862c3efa4ba16745 /gnuradio-core/src/python/gnuradio/blksimpl | |
parent | 04bb51ec4f1539c51c861b7fcad2ca8047a872a3 (diff) |
Merged changes from eb/digital-wip into trunk.
This includes:
* renaming gnuradio-examples/python/gmsk2 to gnuradio-examples/python/digital
* refactoring the digital data tx and rx test code into benchmark_tx
and benchmark_rx. These accept a -m <modulation> argument.
<modulation> can currently be selected from gmsk, dbpsk, dqpsk
* Two new AGC blocks: gr_agc2: separate attack and delay rates;
gr_feedforward_agc: FIR-ish compressor. Normalizes to peak envelope.
* Working DBPSK mod/demod (works fine)
* Working DQPSK mod/demod (works, but still needs more work)
git-svn-id: http://gnuradio.org/svn/gnuradio/trunk@3662 221aa14e-8319-0410-a670-987f0aec2ac5
Diffstat (limited to 'gnuradio-core/src/python/gnuradio/blksimpl')
-rw-r--r-- | gnuradio-core/src/python/gnuradio/blksimpl/Makefile.am | 6 | ||||
-rw-r--r-- | gnuradio-core/src/python/gnuradio/blksimpl/dbpsk.py | 370 | ||||
-rw-r--r-- | gnuradio-core/src/python/gnuradio/blksimpl/dqpsk.py | 370 | ||||
-rw-r--r-- | gnuradio-core/src/python/gnuradio/blksimpl/gmsk.py | 289 | ||||
-rw-r--r-- | gnuradio-core/src/python/gnuradio/blksimpl/gmsk2.py | 159 | ||||
-rw-r--r-- | gnuradio-core/src/python/gnuradio/blksimpl/gmsk2_pkt.py | 174 | ||||
-rw-r--r-- | gnuradio-core/src/python/gnuradio/blksimpl/pkt.py | 4 | ||||
-rw-r--r-- | gnuradio-core/src/python/gnuradio/blksimpl/psk.py | 67 |
8 files changed, 1102 insertions, 337 deletions
diff --git a/gnuradio-core/src/python/gnuradio/blksimpl/Makefile.am b/gnuradio-core/src/python/gnuradio/blksimpl/Makefile.am index 184aa7a29b..8122bfa6a7 100644 --- a/gnuradio-core/src/python/gnuradio/blksimpl/Makefile.am +++ b/gnuradio-core/src/python/gnuradio/blksimpl/Makefile.am @@ -29,14 +29,16 @@ grblkspythondir = $(grpythondir)/blksimpl grblkspython_PYTHON = \ __init__.py \ am_demod.py \ + dbpsk.py \ + dqpsk.py \ filterbank.py \ fm_demod.py \ fm_emph.py \ - gmsk2.py \ - gmsk2_pkt.py \ + gmsk.py \ nbfm_rx.py \ nbfm_tx.py \ pkt.py \ + psk.py \ rational_resampler.py \ standard_squelch.py \ wfm_rcv.py \ diff --git a/gnuradio-core/src/python/gnuradio/blksimpl/dbpsk.py b/gnuradio-core/src/python/gnuradio/blksimpl/dbpsk.py new file mode 100644 index 0000000000..20b940bf05 --- /dev/null +++ b/gnuradio-core/src/python/gnuradio/blksimpl/dbpsk.py @@ -0,0 +1,370 @@ +# +# 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 + +""" +differential BPSK modulation and demodulation. +""" + +from gnuradio import gr, gru, modulation_utils +from math import pi, sqrt +import psk +import cmath +import Numeric +from pprint import pprint + +# default values (used in __init__ and add_options) +_def_samples_per_symbol = 2 +_def_excess_bw = 0.35 +_def_gray_code = True +_def_verbose = False +_def_log = False + +_def_costas_alpha = 0.05 +_def_gain_mu = 0.03 +_def_mu = 0.05 +_def_omega_relative_limit = 0.005 + + +# ///////////////////////////////////////////////////////////////////////////// +# DBPSK modulator +# ///////////////////////////////////////////////////////////////////////////// + +class dbpsk_mod(gr.hier_block): + + def __init__(self, fg, + samples_per_symbol=_def_samples_per_symbol, + excess_bw=_def_excess_bw, + gray_code=_def_gray_code, + verbose=_def_verbose, + log=_def_log): + """ + Hierarchical block for RRC-filtered differential BPSK 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 samples_per_symbol: samples per baud >= 2 + @type samples_per_symbol: integer + @param excess_bw: Root-raised cosine filter excess bandwidth + @type excess_bw: float + @param gray_code: Tell modulator to Gray code the bits + @type gray_code: bool + @param verbose: Print information about modulator? + @type verbose: bool + @param log: Log modulation data to files? + @type log: bool + """ + + self._fg = fg + self._samples_per_symbol = samples_per_symbol + self._excess_bw = excess_bw + self._gray_code = gray_code + + if not isinstance(self._samples_per_symbol, int) or self._samples_per_symbol < 2: + raise TypeError, ("sbp must be an integer >= 2, is %d" % self._samples_per_symbol) + + ntaps = 11 * self._samples_per_symbol + + arity = pow(2,self.bits_per_symbol()) + + # turn bytes into k-bit vectors + self.bytes2chunks = \ + gr.packed_to_unpacked_bb(self.bits_per_symbol(), gr.GR_MSB_FIRST) + + if self._gray_code: + self.symbol_mapper = gr.map_bb(psk.binary_to_gray[arity]) + else: + self.symbol_mapper = gr.map_bb(psk.binary_to_ungray[arity]) + + self.diffenc = gr.diff_encoder_bb(arity) + + self.chunks2symbols = gr.chunks_to_symbols_bc(psk.constellation[arity]) + + # pulse shaping filter + self.rrc_taps = gr.firdes.root_raised_cosine( + self._samples_per_symbol, # gain (samples_per_symbol since we're + # interpolating by samples_per_symbol) + self._samples_per_symbol, # sampling rate + 1.0, # symbol rate + self._excess_bw, # excess bandwidth (roll-off factor) + ntaps) + + self.rrc_filter = gr.interp_fir_filter_ccf(self._samples_per_symbol, + self.rrc_taps) + + # Connect + fg.connect(self.bytes2chunks, self.symbol_mapper, self.diffenc, + self.chunks2symbols, self.rrc_filter) + + if verbose: + self._print_verbage() + + if log: + self._setup_logging() + + # Initialize base class + gr.hier_block.__init__(self, self._fg, self.bytes2chunks, self.rrc_filter) + + def samples_per_symbol(self): + return self._samples_per_symbol + + def bits_per_symbol(self=None): # static method that's also callable on an instance + return 1 + bits_per_symbol = staticmethod(bits_per_symbol) # make it a static method. RTFM + + def add_options(parser): + """ + Adds DBPSK modulation-specific options to the standard parser + """ + parser.add_option("", "--excess-bw", type="float", default=_def_excess_bw, + help="set RRC excess bandwith factor [default=%default]") + parser.add_option("", "--no-gray-code", dest="gray_code", + action="store_false", default=True, + help="disable gray coding on modulated bits (PSK)") + 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(dbpsk_mod.__init__, + ('self', 'fg'), options) + extract_kwargs_from_options=staticmethod(extract_kwargs_from_options) + + + def _print_verbage(self): + print "bits per symbol = %d" % self.bits_per_symbol() + print "Gray code = %s" % self._gray_code + print "RRC roll-off factor = %.2f" % self._excess_bw + + def _setup_logging(self): + print "Modulation logging turned on." + self._fg.connect(self.bytes2chunks, + gr.file_sink(gr.sizeof_char, "bytes2chunks.dat")) + self._fg.connect(self.symbol_mapper, + gr.file_sink(gr.sizeof_char, "graycoder.dat")) + self._fg.connect(self.diffenc, + gr.file_sink(gr.sizeof_char, "diffenc.dat")) + self._fg.connect(self.chunks2symbols, + gr.file_sink(gr.sizeof_gr_complex, "chunks2symbols.dat")) + self._fg.connect(self.rrc_filter, + gr.file_sink(gr.sizeof_gr_complex, "rrc_filter.dat")) + + +# ///////////////////////////////////////////////////////////////////////////// +# DBPSK demodulator +# +# Differentially coherent detection of differentially encoded BPSK +# ///////////////////////////////////////////////////////////////////////////// + +class dbpsk_demod(gr.hier_block): + + def __init__(self, fg, + samples_per_symbol=_def_samples_per_symbol, + excess_bw=_def_excess_bw, + costas_alpha=_def_costas_alpha, + gain_mu=_def_gain_mu, + mu=_def_mu, + omega_relative_limit=_def_omega_relative_limit, + gray_code=_def_gray_code, + verbose=_def_verbose, + log=_def_log): + """ + Hierarchical block for RRC-filtered differential BPSK demodulation + + The input is the complex modulated signal at baseband. + The output is a stream of bits packed 1 bit per byte (LSB) + + @param fg: flow graph + @type fg: flow graph + @param samples_per_symbol: samples per symbol >= 2 + @type samples_per_symbol: float + @param excess_bw: Root-raised cosine filter excess bandwidth + @type excess_bw: float + @param costas_alpha: loop filter gain + @type costas_alphas: float + @param gain_mu: for M&M block + @type gain_mu: float + @param mu: for M&M block + @type mu: float + @param omega_relative_limit: for M&M block + @type omega_relative_limit: float + @param gray_code: Tell modulator to Gray code the bits + @type gray_code: bool + @param verbose: Print information about modulator? + @type verbose: bool + @param debug: Print modualtion data to files? + @type debug: bool + """ + + self._fg = fg + self._samples_per_symbol = samples_per_symbol + self._excess_bw = excess_bw + self._costas_alpha = costas_alpha + self._gain_mu = gain_mu + self._mu = mu + self._omega_relative_limit = omega_relative_limit + self._gray_code = gray_code + + if samples_per_symbol < 2: + raise TypeError, "samples_per_symbol must be >= 2, is %r" % (samples_per_symbol,) + + arity = pow(2,self.bits_per_symbol()) + + # Automatic gain control + scale = (1.0/16384.0) + self.pre_scaler = gr.multiply_const_cc(scale) # scale the signal from full-range to +-1 + #self.agc = gr.agc2_cc(0.6e-1, 1e-3, 1, 1, 100) + self.agc = gr.feedforward_agc_cc(16, 1.0) + + + # Costas loop (carrier tracking) + # FIXME: need to decide how to handle this more generally; do we pull it from higher layer? + costas_order = 2 + beta = .25 * self._costas_alpha * self._costas_alpha + self.costas_loop = gr.costas_loop_cc(self._costas_alpha, beta, 0.002, -0.002, costas_order) + + # RRC data filter + ntaps = 11 * self._samples_per_symbol + self.rrc_taps = gr.firdes.root_raised_cosine( + 1.0, # gain + self._samples_per_symbol, # sampling rate + 1.0, # symbol rate + self._excess_bw, # excess bandwidth (roll-off factor) + ntaps) + + self.rrc_filter=gr.fir_filter_ccf(1, self.rrc_taps) + + # symbol clock recovery + omega = self._samples_per_symbol + gain_omega = .25 * self._gain_mu * self._gain_mu + self.clock_recovery=gr.clock_recovery_mm_cc(omega, gain_omega, + self._mu, self._gain_mu, + self._omega_relative_limit) + + # find closest constellation point + rot = 1 + rotated_const = map(lambda pt: pt * rot, psk.constellation[arity]) + #print "rotated_const =", rotated_const + + self.diffdec = gr.diff_phasor_cc() + #self.diffdec = gr.diff_decoder_bb(arity) + + self.slicer = gr.constellation_decoder_cb(rotated_const, range(arity)) + + if self._gray_code: + self.symbol_mapper = gr.map_bb(psk.gray_to_binary[arity]) + else: + self.symbol_mapper = gr.map_bb(psk.ungray_to_binary[arity]) + + # unpack the k bit vector into a stream of bits + self.unpack = gr.unpack_k_bits_bb(self.bits_per_symbol()) + + if verbose: + self._print_verbage() + + if log: + self._setup_logging() + + # Connect and Initialize base class + self._fg.connect(self.pre_scaler, self.agc, self.costas_loop, + self.rrc_filter, self.clock_recovery, self.diffdec, + self.slicer, self.symbol_mapper, self.unpack) + + gr.hier_block.__init__(self, self._fg, self.pre_scaler, self.unpack) + + 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. RTFM + + def _print_verbage(self): + print "bits per symbol = %d" % self.bits_per_symbol() + print "Gray code = %s" % self._gray_code + print "RRC roll-off factor = %.2f" % self._excess_bw + print "Costas Loop alpha = %.5f" % self._costas_alpha + print "M&M symbol sync gain = %.5f" % self._gain_mu + print "M&M symbol sync mu = %.5f" % self._mu + print "M&M omega relative limit = %.5f" % self._omega_relative_limit + + def _setup_logging(self): + print "Modulation logging turned on." + self._fg.connect(self.pre_scaler, + gr.file_sink(gr.sizeof_gr_complex, "prescaler.dat")) + self._fg.connect(self.agc, + gr.file_sink(gr.sizeof_gr_complex, "agc.dat")) + self._fg.connect(self.costas_loop, + gr.file_sink(gr.sizeof_gr_complex, "costas_loop.dat")) + self._fg.connect((self.costas_loop,1), + gr.file_sink(gr.sizeof_gr_complex, "costas_error.dat")) + self._fg.connect(self.rrc_filter, + gr.file_sink(gr.sizeof_gr_complex, "rrc_filter.dat")) + self._fg.connect(self.clock_recovery, + gr.file_sink(gr.sizeof_gr_complex, "clock_recovery.dat")) + self._fg.connect((self.clock_recovery,1), + gr.file_sink(gr.sizeof_gr_complex, "clock_recovery_error.dat")) + self._fg.connect(self.diffdec, + gr.file_sink(gr.sizeof_gr_complex, "diffdec.dat")) + self._fg.connect(self.slicer, + gr.file_sink(gr.sizeof_char, "slicer.dat")) + self._fg.connect(self.gray_decoder, + gr.file_sink(gr.sizeof_char, "gray_decoder.dat")) + self._fg.connect(self.unpack, + gr.file_sink(gr.sizeof_char, "unpack.dat")) + + def add_options(parser): + """ + Adds DBPSK demodulation-specific options to the standard parser + """ + parser.add_option("", "--excess-bw", type="float", default=_def_excess_bw, + help="set RRC excess bandwith factor [default=%default] (PSK)") + parser.add_option("", "--no-gray-code", dest="gray_code", + action="store_false", default=_def_gray_code, + help="disable gray coding on modulated bits (PSK)") + parser.add_option("", "--costas-alpha", type="float", default=None, + help="set Costas loop alpha value [default=%default] (PSK)") + parser.add_option("", "--gain-mu", type="float", default=_def_gain_mu, + help="set M&M symbol sync loop gain mu value [default=%default] (GMSK/PSK)") + parser.add_option("", "--mu", type="float", default=_def_mu, + help="set M&M symbol sync loop mu value [default=%default] (GMSK/PSK)") + parser.add_option("", "--omega-relative-limit", type="float", default=_def_omega_relative_limit, + help="M&M clock recovery omega relative limit [default=%default] (GMSK/PSK)") + 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( + dbpsk_demod.__init__, ('self', 'fg'), options) + extract_kwargs_from_options=staticmethod(extract_kwargs_from_options) + +# +# Add these to the mod/demod registry +# +modulation_utils.add_type_1_mod('dbpsk', dbpsk_mod) +modulation_utils.add_type_1_demod('dbpsk', dbpsk_demod) diff --git a/gnuradio-core/src/python/gnuradio/blksimpl/dqpsk.py b/gnuradio-core/src/python/gnuradio/blksimpl/dqpsk.py new file mode 100644 index 0000000000..3b60f2242e --- /dev/null +++ b/gnuradio-core/src/python/gnuradio/blksimpl/dqpsk.py @@ -0,0 +1,370 @@ +# +# 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 + +""" +differential QPSK modulation and demodulation. +""" + +from gnuradio import gr, gru, modulation_utils +from math import pi, sqrt +import psk +import cmath +import Numeric +from pprint import pprint + +# default values (used in __init__ and add_options) +_def_samples_per_symbol = 2 +_def_excess_bw = 0.35 +_def_gray_code = True +_def_verbose = False +_def_log = False + +_def_costas_alpha = 0.10 +_def_gain_mu = 0.03 +_def_mu = 0.05 +_def_omega_relative_limit = 0.005 + + +# ///////////////////////////////////////////////////////////////////////////// +# DQPSK modulator +# ///////////////////////////////////////////////////////////////////////////// + +class dqpsk_mod(gr.hier_block): + + def __init__(self, fg, + samples_per_symbol=_def_samples_per_symbol, + excess_bw=_def_excess_bw, + gray_code=_def_gray_code, + verbose=_def_verbose, + log=_def_log): + """ + Hierarchical block for RRC-filtered QPSK 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 samples_per_symbol: samples per symbol >= 2 + @type samples_per_symbol: integer + @param excess_bw: Root-raised cosine filter excess bandwidth + @type excess_bw: float + @param gray_code: Tell modulator to Gray code the bits + @type gray_code: bool + @param verbose: Print information about modulator? + @type verbose: bool + @param debug: Print modualtion data to files? + @type debug: bool + """ + + self._fg = fg + self._samples_per_symbol = samples_per_symbol + self._excess_bw = excess_bw + self._gray_code = gray_code + + if not isinstance(samples_per_symbol, int) or samples_per_symbol < 2: + raise TypeError, ("sbp must be an integer >= 2, is %d" % samples_per_symbol) + + ntaps = 11 * samples_per_symbol + + arity = pow(2,self.bits_per_symbol()) + + # turn bytes into k-bit vectors + self.bytes2chunks = \ + gr.packed_to_unpacked_bb(self.bits_per_symbol(), gr.GR_MSB_FIRST) + + if self._gray_code: + self.symbol_mapper = gr.map_bb(psk.binary_to_gray[arity]) + else: + self.symbol_mapper = gr.map_bb(psk.binary_to_ungray[arity]) + + self.diffenc = gr.diff_encoder_bb(arity) + + rot = .707 + .707j + rotated_const = map(lambda pt: pt * rot, psk.constellation[arity]) + self.chunks2symbols = gr.chunks_to_symbols_bc(rotated_const) + + # pulse shaping filter + self.rrc_taps = gr.firdes.root_raised_cosine( + self._samples_per_symbol, # gain (sps since we're interpolating by sps) + self._samples_per_symbol, # sampling rate + 1.0, # symbol rate + self._excess_bw, # excess bandwidth (roll-off factor) + ntaps) + + self.rrc_filter = gr.interp_fir_filter_ccf(self._samples_per_symbol, self.rrc_taps) + + if verbose: + self._print_verbage() + + if log: + self._setup_logging() + + # Connect & Initialize base class + self._fg.connect(self.bytes2chunks, self.symbol_mapper, self.diffenc, + self.chunks2symbols, self.rrc_filter) + gr.hier_block.__init__(self, self._fg, self.bytes2chunks, self.rrc_filter) + + 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 2 + bits_per_symbol = staticmethod(bits_per_symbol) # make it a static method. RTFM + + def _print_verbage(self): + print "bits per symbol = %d" % self.bits_per_symbol() + print "Gray code = %s" % self._gray_code + print "RRS roll-off factor = %f" % self._excess_bw + + def _setup_logging(self): + print "Modulation logging turned on." + self._fg.connect(self.bytes2chunks, + gr.file_sink(gr.sizeof_char, "bytes2chunks.dat")) + self._fg.connect(self.symbol_mapper, + gr.file_sink(gr.sizeof_char, "graycoder.dat")) + self._fg.connect(self.diffenc, + gr.file_sink(gr.sizeof_char, "diffenc.dat")) + self._fg.connect(self.chunks2symbols, + gr.file_sink(gr.sizeof_gr_complex, "chunks2symbols.dat")) + self._fg.connect(self.rrc_filter, + gr.file_sink(gr.sizeof_gr_complex, "rrc_filter.dat")) + + def add_options(parser): + """ + Adds QPSK modulation-specific options to the standard parser + """ + parser.add_option("", "--excess-bw", type="float", default=_def_excess_bw, + help="set RRC excess bandwith factor [default=%default] (PSK)") + parser.add_option("", "--no-gray-code", dest="gray_code", + action="store_false", default=_def_gray_code, + help="disable gray coding on modulated bits (PSK)") + 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(dqpsk_mod.__init__, + ('self', 'fg'), options) + extract_kwargs_from_options=staticmethod(extract_kwargs_from_options) + + +# ///////////////////////////////////////////////////////////////////////////// +# DQPSK demodulator +# +# Differentially coherent detection of differentially encoded qpsk +# ///////////////////////////////////////////////////////////////////////////// + +class dqpsk_demod(gr.hier_block): + + def __init__(self, fg, + samples_per_symbol=_def_samples_per_symbol, + excess_bw=_def_excess_bw, + costas_alpha=_def_costas_alpha, + gain_mu=_def_gain_mu, + mu=_def_mu, + omega_relative_limit=_def_omega_relative_limit, + gray_code=_def_gray_code, + verbose=_def_verbose, + log=_def_log): + """ + Hierarchical block for RRC-filtered DQPSK demodulation + + The input is the complex modulated signal at baseband. + The output is a stream of bits packed 1 bit per byte (LSB) + + @param fg: flow graph + @type fg: flow graph + @param samples_per_symbol: samples per symbol >= 2 + @type samples_per_symbol: float + @param excess_bw: Root-raised cosine filter excess bandwidth + @type excess_bw: float + @param costas_alpha: loop filter gain + @type costas_alphas: float + @param gain_mu: for M&M block + @type gain_mu: float + @param mu: for M&M block + @type mu: float + @param omega_relative_limit: for M&M block + @type omega_relative_limit: float + @param gray_code: Tell modulator to Gray code the bits + @type gray_code: bool + @param verbose: Print information about modulator? + @type verbose: bool + @param debug: Print modualtion data to files? + @type debug: bool + """ + + self._fg = fg + self._samples_per_symbol = samples_per_symbol + self._excess_bw = excess_bw + self._costas_alpha = costas_alpha + self._gain_mu = gain_mu + self._mu = mu + self._omega_relative_limit = omega_relative_limit + self._gray_code = gray_code + + if samples_per_symbol < 2: + raise TypeError, "sbp must be >= 2, is %d" % samples_per_symbol + + arity = pow(2,self.bits_per_symbol()) + + # Automatic gain control + scale = (1.0/16384.0) + self.pre_scaler = gr.multiply_const_cc(scale) # scale the signal from full-range to +-1 + #self.agc = gr.agc2_cc(0.6e-1, 1e-3, 1, 1, 100) + self.agc = gr.feedforward_agc_cc(16, 1.0) + + # Costas loop (carrier tracking) + # FIXME: need to decide how to handle this more generally; do we pull it from higher layer? + costas_order = 4 + beta = .25 * self._costas_alpha * self._costas_alpha + #self.costas_loop = gr.costas_loop_cc(self._costas_alpha, beta, 0.1, -0.1, costas_order) + self.costas_loop = gr.costas_loop_cc(self._costas_alpha, beta, 0.002, -0.002, costas_order) + + # RRC data filter + ntaps = 11 * samples_per_symbol + self.rrc_taps = gr.firdes.root_raised_cosine( + self._samples_per_symbol, # gain + self._samples_per_symbol, # sampling rate + 1.0, # symbol rate + self._excess_bw, # excess bandwidth (roll-off factor) + ntaps) + + self.rrc_filter=gr.fir_filter_ccf(1, self.rrc_taps) + + # symbol clock recovery + omega = self._samples_per_symbol + gain_omega = .25 * self._gain_mu * self._gain_mu + self.clock_recovery=gr.clock_recovery_mm_cc(omega, gain_omega, + self._mu, self._gain_mu, + self._omega_relative_limit) + + self.diffdec = gr.diff_phasor_cc() + #self.diffdec = gr.diff_decoder_bb(arity) + + # find closest constellation point + rot = 1 + #rot = .707 + .707j + rotated_const = map(lambda pt: pt * rot, psk.constellation[arity]) + #print "rotated_const = %s" % rotated_const + + self.slicer = gr.constellation_decoder_cb(rotated_const, range(arity)) + + if self._gray_code: + self.symbol_mapper = gr.map_bb(psk.gray_to_binary[arity]) + else: + self.symbol_mapper = gr.map_bb(psk.ungray_to_binary[arity]) + + + # unpack the k bit vector into a stream of bits + self.unpack = gr.unpack_k_bits_bb(self.bits_per_symbol()) + + if verbose: + self._print_verbage() + + if log: + self._setup_logging() + + # Connect & Initialize base class + self._fg.connect(self.pre_scaler, self.agc, self.costas_loop, + self.rrc_filter, self.clock_recovery, + self.diffdec, self.slicer, self.symbol_mapper, + self.unpack) + gr.hier_block.__init__(self, self._fg, self.pre_scaler, self.unpack) + + 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 2 + bits_per_symbol = staticmethod(bits_per_symbol) # make it a static method. RTFM + + def _print_verbage(self): + print "bits per symbol = %d" % self.bits_per_symbol() + print "Gray code = %s" % self._gray_code + print "RRC roll-off factor = %.2f" % self._excess_bw + print "Costas Loop alpha = %.5f" % self._costas_alpha + print "M&M symbol sync gain = %.5f" % self._gain_mu + print "M&M symbol sync mu = %.5f" % self._mu + print "M&M omega relative limit = %.5f" % self._omega_relative_limit + + + def _setup_logging(self): + print "Modulation logging turned on." + self._fg.connect(self.pre_scaler, + gr.file_sink(gr.sizeof_gr_complex, "prescaler.dat")) + self._fg.connect(self.agc, + gr.file_sink(gr.sizeof_gr_complex, "agc.dat")) + self._fg.connect(self.costas_loop, + gr.file_sink(gr.sizeof_gr_complex, "costas_loop.dat")) + self._fg.connect((self.costas_loop,1), + gr.file_sink(gr.sizeof_gr_complex, "costas_error.dat")) + self._fg.connect(self.rrc_filter, + gr.file_sink(gr.sizeof_gr_complex, "rrc_filter.dat")) + self._fg.connect(self.clock_recovery, + gr.file_sink(gr.sizeof_gr_complex, "clock_recovery.dat")) + self._fg.connect((self.clock_recovery,1), + gr.file_sink(gr.sizeof_gr_complex, "clock_recovery_error.dat")) + self._fg.connect(self.diffdec, + gr.file_sink(gr.sizeof_gr_complex, "diffdec.dat")) + self._fg.connect(self.slicer, + gr.file_sink(gr.sizeof_char, "slicer.dat")) + self._fg.connect(self.symbol_mapper, + gr.file_sink(gr.sizeof_char, "gray_decoder.dat")) + self._fg.connect(self.unpack, + gr.file_sink(gr.sizeof_char, "unpack.dat")) + + def add_options(parser): + """ + Adds modulation-specific options to the standard parser + """ + parser.add_option("", "--excess-bw", type="float", default=_def_excess_bw, + help="set RRC excess bandwith factor [default=%default] (PSK)") + parser.add_option("", "--no-gray-code", dest="gray_code", + action="store_false", default=_def_gray_code, + help="disable gray coding on modulated bits (PSK)") + parser.add_option("", "--costas-alpha", type="float", default=None, + help="set Costas loop alpha value [default=%default] (PSK)") + parser.add_option("", "--gain-mu", type="float", default=_def_gain_mu, + help="set M&M symbol sync loop gain mu value [default=%default] (PSK)") + parser.add_option("", "--mu", type="float", default=_def_mu, + help="set M&M symbol sync loop mu value [default=%default] (PSK)") + 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( + dqpsk_demod.__init__, ('self', 'fg'), options) + extract_kwargs_from_options=staticmethod(extract_kwargs_from_options) + + +# +# Add these to the mod/demod registry +# +modulation_utils.add_type_1_mod('dqpsk', dqpsk_mod) +modulation_utils.add_type_1_demod('dqpsk', dqpsk_demod) diff --git a/gnuradio-core/src/python/gnuradio/blksimpl/gmsk.py b/gnuradio-core/src/python/gnuradio/blksimpl/gmsk.py new file mode 100644 index 0000000000..098aa74bb5 --- /dev/null +++ b/gnuradio-core/src/python/gnuradio/blksimpl/gmsk.py @@ -0,0 +1,289 @@ +# +# 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 gnuradio import modulation_utils +from math import pi +import Numeric +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 + +_def_gain_mu = 0.05 +_def_mu = 0.5 +_def_freq_error = 0.0 +_def_omega_relative_limit = 0.005 + + +# ///////////////////////////////////////////////////////////////////////////// +# GMSK modulator +# ///////////////////////////////////////////////////////////////////////////// + +class gmsk_mod(gr.hier_block): + + def __init__(self, fg, + 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 fg: flow graph + @type fg: flow graph + @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 + """ + + self._fg = fg + self._samples_per_symbol = samples_per_symbol + self._bt = bt + + if not isinstance(samples_per_symbol, int) or samples_per_symbol < 2: + raise TypeError, ("samples_per_symbol must be an integer >= 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 + ntaps # number of taps + ) + + self.sqwave = (1,) * samples_per_symbol # rectangular window + self.taps = Numeric.convolve(Numeric.array(self.gaussian_taps),Numeric.array(self.sqwave)) + self.gaussian_filter = gr.interp_fir_filter_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._fg.connect(self.nrz, self.gaussian_filter, self.fmmod) + gr.hier_block.__init__(self, self._fg, self.nrz, self.fmmod) + + 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._fg.connect(self.nrz, + gr.file_sink(gr.sizeof_float, "nrz.dat")) + self._fg.connect(self.gaussian_filter, + gr.file_sink(gr.sizeof_float, "gaussian_filter.dat")) + self._fg.connect(self.fmmod, + gr.file_sink(gr.sizeof_gr_complex, "fmmod.dat")) + + + 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', 'fg'), options) + extract_kwargs_from_options=staticmethod(extract_kwargs_from_options) + + + +# ///////////////////////////////////////////////////////////////////////////// +# GMSK demodulator +# ///////////////////////////////////////////////////////////////////////////// + +class gmsk_demod(gr.hier_block): + + def __init__(self, fg, + samples_per_symbol=_def_samples_per_symbol, + gain_mu=_def_gain_mu, + mu=_def_mu, + omega_relative_limit=_def_omega_relative_limit, + freq_error=_def_freq_error, + 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 fg: flow graph + @type fg: flow graph + @param samples_per_symbol: samples per baud + @type samples_per_symbol: integer + @param verbose: Print information about modulator? + @type verbose: bool + @param log: Print modualtion data to files? + @type log: bool + + Clock recovery parameters. These all have reasonble defaults. + + @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 + """ + + self._fg = fg + self._samples_per_symbol = samples_per_symbol + self._gain_mu = gain_mu + self._mu = mu + self._omega_relative_limit = omega_relative_limit + self._freq_error = freq_error + + if samples_per_symbol < 2: + raise TypeError, "samples_per_symbol >= 2, is %f" % samples_per_symbol + + self._omega = samples_per_symbol*(1+self._freq_error) + + self._gain_omega = .25 * self._gain_mu * self._gain_mu # critically damped + + # 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) + self.clock_recovery = gr.clock_recovery_mm_ff(self._omega, self._gain_omega, + self._mu, self._gain_mu, + self._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() + + if verbose: + self._print_verbage() + + if log: + self._setup_logging() + + # Connect & Initialize base class + self._fg.connect(self.fmdemod, self.clock_recovery, self.slicer) + gr.hier_block.__init__(self, self._fg, self.fmdemod, self.slicer) + + 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 "M&M clock recovery omega = %f" % self._omega + print "M&M clock recovery gain mu = %f" % self._gain_mu + print "M&M clock recovery mu = %f" % self._mu + print "M&M clock recovery omega rel. limit = %f" % self._omega_relative_limit + print "frequency error = %f" % self._freq_error + + + def _setup_logging(self): + print "Demodulation logging turned on." + self._fg.connect(self.fmdemod, + gr.file_sink(gr.sizeof_float, "fmdemod.dat")) + self._fg.connect(self.clock_recovery, + gr.file_sink(gr.sizeof_float, "clock_recovery.dat")) + self._fg.connect(self.slicer, + gr.file_sink(gr.sizeof_char, "slicer.dat")) + + def add_options(parser): + """ + Adds GMSK demodulation-specific options to the standard parser + """ + parser.add_option("", "--gain-mu", type="float", default=_def_gain_mu, + help="M&M clock recovery gain mu [default=%default] (GMSK/PSK)") + parser.add_option("", "--mu", type="float", default=_def_mu, + help="M&M clock recovery mu [default=%default] (GMSK/PSK)") + parser.add_option("", "--omega-relative-limit", type="float", default=_def_omega_relative_limit, + help="M&M clock recovery omega relative limit [default=%default] (GMSK/PSK)") + parser.add_option("", "--freq-error", type="float", default=_def_freq_error, + help="M&M clock recovery frequency error [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', 'fg'), 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) diff --git a/gnuradio-core/src/python/gnuradio/blksimpl/gmsk2.py b/gnuradio-core/src/python/gnuradio/blksimpl/gmsk2.py deleted file mode 100644 index 4fc033977b..0000000000 --- a/gnuradio-core/src/python/gnuradio/blksimpl/gmsk2.py +++ /dev/null @@ -1,159 +0,0 @@ -# -# 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 diff --git a/gnuradio-core/src/python/gnuradio/blksimpl/gmsk2_pkt.py b/gnuradio-core/src/python/gnuradio/blksimpl/gmsk2_pkt.py deleted file mode 100644 index 672dbbd698..0000000000 --- a/gnuradio-core/src/python/gnuradio/blksimpl/gmsk2_pkt.py +++ /dev/null @@ -1,174 +0,0 @@ -# -# 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. -# - -from math import pi -import Numeric - -from gnuradio import gr, packet_utils -import gnuradio.gr.gr_threading as _threading -import gmsk2 - - -def _deprecation_warning(old_name, new_name): - print '#' - print '# Warning: %s is deprecated and will be removed soon.' % (old_name,) - print '# Please use the modulation independent block, %s.' % (new_name,) - print "#" - - -# ///////////////////////////////////////////////////////////////////////////// -# GMSK mod/demod with packets as i/o -# ///////////////////////////////////////////////////////////////////////////// - -class gmsk2_mod_pkts(gr.hier_block): - """ - GSM modulator that is a GNU Radio source. - - Send packets by calling send_pkt - """ - def __init__(self, fg, access_code=None, msgq_limit=2, pad_for_usrp=True, *args, **kwargs): - """ - Hierarchical block for Gaussian Minimum Shift Key (GMSK) modulation. - - Packets to be sent are enqueued by calling send_pkt. - The output is the complex modulated signal at baseband. - - @param fg: flow graph - @type fg: flow graph - @param access_code: AKA sync vector - @type access_code: string of 1's and 0's between 1 and 64 long - @param msgq_limit: maximum number of messages in message queue - @type msgq_limit: int - @param pad_for_usrp: If true, packets are padded such that they end up a multiple of 128 samples - - See gmsk_mod for remaining parameters - """ - _deprecation_warning('gmsk2_mod_pkts', 'mod_pkts') - - self.pad_for_usrp = pad_for_usrp - if access_code is None: - access_code = packet_utils.default_access_code - if not packet_utils.is_1_0_string(access_code): - raise ValueError, "Invalid access_code %r. Must be string of 1's and 0's" % (access_code,) - self._access_code = access_code - - # accepts messages from the outside world - self.pkt_input = gr.message_source(gr.sizeof_char, msgq_limit) - self.gmsk_mod = gmsk2.gmsk2_mod(fg, *args, **kwargs) - fg.connect(self.pkt_input, self.gmsk_mod) - gr.hier_block.__init__(self, fg, None, self.gmsk_mod) - - def send_pkt(self, payload='', eof=False): - """ - Send the payload. - - @param payload: data to send - @type payload: string - """ - if eof: - msg = gr.message(1) # tell self.pkt_input we're not sending any more packets - else: - # print "original_payload =", string_to_hex_list(payload) - pkt = packet_utils.make_packet(payload, - self.gmsk_mod.samples_per_baud(), - self.gmsk_mod.bits_per_baud(), - self._access_code, - self.pad_for_usrp) - #print "pkt =", string_to_hex_list(pkt) - msg = gr.message_from_string(pkt) - self.pkt_input.msgq().insert_tail(msg) - - - -class gmsk2_demod_pkts(gr.hier_block): - """ - GSM demodulator that is a GNU Radio sink. - - The input is complex baseband. When packets are demodulated, they are passed to the - app via the callback. - """ - - def __init__(self, fg, access_code=None, callback=None, threshold=-1, *args, **kwargs): - """ - Hierarchical block for Gaussian Minimum Shift Key (GMSK) - demodulation. - - The input is the complex modulated signal at baseband. - Demodulated packets are sent to the handler. - - @param fg: flow graph - @type fg: flow graph - @param access_code: AKA sync vector - @type access_code: string of 1's and 0's - @param callback: function of two args: ok, payload - @type callback: ok: bool; payload: string - @param threshold: detect access_code with up to threshold bits wrong (-1 -> use default) - @type threshold: int - - See gmsk_demod for remaining parameters. - """ - - _deprecation_warning('gmsk2_demod_pkts', 'demod_pkts') - - if access_code is None: - access_code = packet_utils.default_access_code - if not packet_utils.is_1_0_string(access_code): - raise ValueError, "Invalid access_code %r. Must be string of 1's and 0's" % (access_code,) - self._access_code = access_code - - if threshold == -1: - threshold = 12 # FIXME raise exception - - self._rcvd_pktq = gr.msg_queue() # holds packets from the PHY - self.gmsk_demod = gmsk2.gmsk2_demod(fg, *args, **kwargs) - self.correlator = gr.correlate_access_code_bb(access_code, threshold) - - self.framer_sink = gr.framer_sink_1(self._rcvd_pktq) - fg.connect(self.gmsk_demod, self.correlator, self.framer_sink) - - gr.hier_block.__init__(self, fg, self.gmsk_demod, None) - self._watcher = _queue_watcher_thread(self._rcvd_pktq, callback) - - def carrier_sensed(self): - """ - Return True if we detect carrier. - """ - return False # FIXME - - -class _queue_watcher_thread(_threading.Thread): - def __init__(self, rcvd_pktq, callback): - _threading.Thread.__init__(self) - self.setDaemon(1) - self.rcvd_pktq = rcvd_pktq - self.callback = callback - self.keep_running = True - self.start() - - #def stop(self): - # self.keep_running = False - - def run(self): - while self.keep_running: - msg = self.rcvd_pktq.delete_head() - ok, payload = packet_utils.unmake_packet(msg.to_string()) - if self.callback: - self.callback(ok, payload) diff --git a/gnuradio-core/src/python/gnuradio/blksimpl/pkt.py b/gnuradio-core/src/python/gnuradio/blksimpl/pkt.py index 05e22c90e5..a1ece893df 100644 --- a/gnuradio-core/src/python/gnuradio/blksimpl/pkt.py +++ b/gnuradio-core/src/python/gnuradio/blksimpl/pkt.py @@ -81,8 +81,8 @@ class mod_pkts(gr.hier_block): else: # print "original_payload =", string_to_hex_list(payload) pkt = packet_utils.make_packet(payload, - self._modulator.samples_per_baud(), - self._modulator.bits_per_baud(), + self._modulator.samples_per_symbol(), + self._modulator.bits_per_symbol(), self._access_code, self._pad_for_usrp) #print "pkt =", string_to_hex_list(pkt) diff --git a/gnuradio-core/src/python/gnuradio/blksimpl/psk.py b/gnuradio-core/src/python/gnuradio/blksimpl/psk.py new file mode 100644 index 0000000000..fdb6c9e693 --- /dev/null +++ b/gnuradio-core/src/python/gnuradio/blksimpl/psk.py @@ -0,0 +1,67 @@ +# +# 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. +# + +from math import pi, sqrt +import cmath + +def make_constellation(m): + return [cmath.exp(i * 2 * pi / m * 1j) for i in range(m)] + +# Common definition of constellations for Tx and Rx +constellation = { + 2 : make_constellation(2), # BPSK + 4 : make_constellation(4), # QPSK + 8 : make_constellation(8) # 8PSK + } + +# ----------------------- +# Do Gray code +# ----------------------- +# binary to gray coding +binary_to_gray = { + 2 : (0, 1), + 4 : (0, 1, 3, 2), + 8 : (0, 1, 3, 2, 7, 6, 4, 5) + } + +# gray to binary +gray_to_binary = { + 2 : (0, 1), + 4 : (0, 1, 3, 2), + 8 : (0, 1, 3, 2, 6, 7, 5, 4) + } + +# ----------------------- +# Don't Gray code +# ----------------------- +# identity mapping +binary_to_ungray = { + 2 : (0, 1), + 4 : (0, 1, 2, 3), + 8 : (0, 1, 2, 3, 4, 5, 6, 7) + } + +# identity mapping +ungray_to_binary = { + 2 : (0, 1), + 4 : (0, 1, 2, 3), + 8 : (0, 1, 2, 3, 4, 5, 6, 7) + } |