diff options
-rw-r--r-- | gnuradio-core/src/lib/general/Makefile.am | 5 | ||||
-rw-r--r-- | gnuradio-core/src/lib/general/general.i | 2 | ||||
-rw-r--r-- | gnuradio-core/src/lib/general/gr_constellation_receiver_cb.cc | 146 | ||||
-rw-r--r-- | gnuradio-core/src/lib/general/gr_constellation_receiver_cb.h | 172 | ||||
-rw-r--r-- | gnuradio-core/src/lib/general/gr_constellation_receiver_cb.i | 54 | ||||
-rw-r--r-- | gnuradio-core/src/python/gnuradio/blks2impl/Makefile.am | 1 | ||||
-rw-r--r-- | gnuradio-core/src/python/gnuradio/blks2impl/generic_mod_demod.py | 328 | ||||
-rw-r--r-- | gnuradio-core/src/python/gnuradio/blks2impl/qam.py | 293 | ||||
-rw-r--r-- | gnuradio-core/src/python/gnuradio/blks2impl/qam16.py | 208 | ||||
-rw-r--r-- | gnuradio-core/src/python/gnuradio/blks2impl/qam256.py | 209 | ||||
-rw-r--r-- | gnuradio-core/src/python/gnuradio/blks2impl/qam64.py | 208 | ||||
-rw-r--r-- | gnuradio-core/src/python/gnuradio/blks2impl/qam8.py | 209 | ||||
-rw-r--r-- | gnuradio-examples/python/digital/simple_qam.py | 64 |
13 files changed, 979 insertions, 920 deletions
diff --git a/gnuradio-core/src/lib/general/Makefile.am b/gnuradio-core/src/lib/general/Makefile.am index 3d8a42805..daa3c446e 100644 --- a/gnuradio-core/src/lib/general/Makefile.am +++ b/gnuradio-core/src/lib/general/Makefile.am @@ -100,6 +100,7 @@ libgeneral_la_SOURCES = \ gr_math.cc \ gr_misc.cc \ gr_mpsk_receiver_cc.cc \ + gr_constellation_receiver_cb.cc \ gr_nlog10_ff.cc \ gr_nop.cc \ gr_null_sink.cc \ @@ -256,7 +257,7 @@ grinclude_HEADERS = \ gr_map_bb.h \ gr_math.h \ gr_misc.h \ - gr_mpsk_receiver_cc.h \ + gr_constellation_receiver_cb.h \ gr_nco.h \ gr_nlog10_ff.h \ gr_nop.h \ @@ -418,7 +419,7 @@ swiginclude_HEADERS = \ gr_lms_dfe_cc.i \ gr_lms_dfe_ff.i \ gr_map_bb.i \ - gr_mpsk_receiver_cc.i \ + gr_constellation_receiver_cb.i \ gr_nlog10_ff.i \ gr_nop.i \ gr_null_sink.i \ diff --git a/gnuradio-core/src/lib/general/general.i b/gnuradio-core/src/lib/general/general.i index 68cafce2e..f758211cc 100644 --- a/gnuradio-core/src/lib/general/general.i +++ b/gnuradio-core/src/lib/general/general.i @@ -69,6 +69,7 @@ #include <gr_fake_channel_coder_pp.h> #include <gr_throttle.h> #include <gr_mpsk_receiver_cc.h> +#include <gr_constellation_receiver_cb.h> #include <gr_stream_mux.h> #include <gr_stream_to_streams.h> #include <gr_streams_to_stream.h> @@ -190,6 +191,7 @@ %include "gr_fake_channel_coder_pp.i" %include "gr_throttle.i" %include "gr_mpsk_receiver_cc.i" +%include "gr_constellation_receiver_cb.i" %include "gr_stream_mux.i" %include "gr_stream_to_streams.i" %include "gr_streams_to_stream.i" diff --git a/gnuradio-core/src/lib/general/gr_constellation_receiver_cb.cc b/gnuradio-core/src/lib/general/gr_constellation_receiver_cb.cc new file mode 100644 index 000000000..0a2309ac0 --- /dev/null +++ b/gnuradio-core/src/lib/general/gr_constellation_receiver_cb.cc @@ -0,0 +1,146 @@ +/* -*- c++ -*- */ +/* + * Copyright 2005,2006,2007,2010 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. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <gr_io_signature.h> +#include <gr_prefs.h> +#include <gr_constellation_receiver_cb.h> +#include <stdexcept> +#include <gr_math.h> +#include <gr_expj.h> +// For debugging +#include <iostream> + + +#define M_TWOPI (2*M_PI) +#define VERBOSE_MM 0 // Used for debugging symbol timing loop +#define VERBOSE_COSTAS 0 // Used for debugging phase and frequency tracking + +// Public constructor + +gr_constellation_receiver_cb_sptr +gr_make_constellation_receiver_cb(gr_constellation constell, + float alpha, float beta, + float fmin, float fmax) +{ + return gnuradio::get_initial_sptr(new gr_constellation_receiver_cb (constell, + alpha, beta, + fmin, fmax)); +} + +gr_constellation_receiver_cb::gr_constellation_receiver_cb (gr_constellation constellation, + float alpha, float beta, + float fmin, float fmax) + : gr_block ("constellation_receiver_cb", + gr_make_io_signature (1, 1, sizeof (gr_complex)), + gr_make_io_signature (1, 1, sizeof (unsigned char))), + d_constellation(constellation), + d_alpha(alpha), d_beta(beta), d_freq(0), d_max_freq(fmax), d_min_freq(fmin), d_phase(0), + d_current_const_point(0) +{} + +void +gr_constellation_receiver_cb::phase_error_tracking(float phase_error) +{ + + d_freq += d_beta*phase_error; // adjust frequency based on error + d_phase += d_freq + d_alpha*phase_error; // adjust phase based on error + + // Make sure we stay within +-2pi + while(d_phase > M_TWOPI) + d_phase -= M_TWOPI; + while(d_phase < -M_TWOPI) + d_phase += M_TWOPI; + + // Limit the frequency range + d_freq = gr_branchless_clip(d_freq, d_max_freq); + +#if VERBOSE_COSTAS + printf("cl: phase_error: %f phase: %f freq: %f sample: %f+j%f constellation: %f+j%f\n", + phase_error, d_phase, d_freq, sample.real(), sample.imag(), + d_constellation.constellation()[d_current_const_point].real(), d_constellation.constellation()[d_current_const_point].imag()); +#endif +} + +int +gr_constellation_receiver_cb::general_work (int noutput_items, + gr_vector_int &ninput_items, + gr_vector_const_void_star &input_items, + gr_vector_void_star &output_items) +{ + const gr_complex *in = (const gr_complex *) input_items[0]; + unsigned char *out = (unsigned char *) output_items[0]; + + int i=0, o=0; + + float phase_error; + unsigned int sym_value; + gr_complex sample; + + while((o < noutput_items) && (i < ninput_items[0])) { + sample = in[i]; + sym_value = d_constellation.decision_maker(sample); + //std::cout << "sym_value: " << sym_value << " sample: " << sample << std::endl; + phase_error = -arg(sample*conj(d_constellation.constellation()[sym_value])); + phase_error_tracking(phase_error); // corrects phase and frequency offsets + out[o++] = sym_value; + i++; + } + + #if 0 + printf("ninput_items: %d noutput_items: %d consuming: %d returning: %d\n", + ninput_items[0], noutput_items, i, o); + #endif + + consume_each(i); + return o; +} + +// Base Constellation Class + +gr_constellation::gr_constellation (std::vector<gr_complex> constellation) { + d_constellation = constellation; +} + +// Chooses points base on shortest distance. +// Inefficient. +unsigned int gr_constellation::decision_maker(gr_complex sample) +{ + unsigned int table_size = constellation().size(); + unsigned int min_index = 0; + float min_euclid_dist; + float euclid_dist; + + min_euclid_dist = norm(sample - constellation()[0]); + min_index = 0; + for (unsigned int j = 1; j < table_size; j++){ + euclid_dist = norm(sample - constellation()[j]); + if (euclid_dist < min_euclid_dist){ + min_euclid_dist = euclid_dist; + min_index = j; + } + } + return min_index; +} diff --git a/gnuradio-core/src/lib/general/gr_constellation_receiver_cb.h b/gnuradio-core/src/lib/general/gr_constellation_receiver_cb.h new file mode 100644 index 000000000..ba38bdad6 --- /dev/null +++ b/gnuradio-core/src/lib/general/gr_constellation_receiver_cb.h @@ -0,0 +1,172 @@ +/* -*- c++ -*- */ +/* + * Copyright 2004,2007,2010 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. + */ + +#ifndef INCLUDED_GR_CONSTELLATION_RECEIVER_CB_H +#define INCLUDED_GR_CONSTELLATION_RECEIVER_CB_H + +#include <gr_block.h> +#include <gr_complex.h> +#include <math.h> +#include <fstream> + +class gr_constellation +{ + public: + + gr_constellation (std::vector<gr_complex> constellation); + + //! Returns the set of points in this constellation. + std::vector<gr_complex> constellation() { return d_constellation;} + + //! Returns the constellation point that matches best. + //! Also calculates the phase error. + unsigned int decision_maker (gr_complex sample); + + unsigned int bits_per_symbol () { + return floor(log(d_constellation.size())/log(2)); + } + + private: + + std::vector<gr_complex> d_constellation; +}; + +class gri_mmse_fir_interpolator_cc; + +class gr_constellation_receiver_cb; +typedef boost::shared_ptr<gr_constellation_receiver_cb> gr_constellation_receiver_cb_sptr; + +// public constructor +gr_constellation_receiver_cb_sptr +gr_make_constellation_receiver_cb (gr_constellation constellation, + float alpha, float beta, + float fmin, float fmax); + +/*! + * \brief This block takes care of receiving generic modulated signals through phase, frequency, and symbol + * synchronization. + * \ingroup sync_blk + * \ingroup demod_blk + * + * This block takes care of receiving generic modulated signals through phase, frequency, and symbol + * synchronization. It performs carrier frequency and phase locking as well as symbol timing recovery. + * + * The phase and frequency synchronization are based on a Costas loop that finds the error of the incoming + * signal point compared to its nearest constellation point. The frequency and phase of the NCO are + * updated according to this error. + * + * The symbol synchronization is done using a modified Mueller and Muller circuit from the paper: + * + * G. R. Danesfahani, T.G. Jeans, "Optimisation of modified Mueller and Muller + * algorithm," Electronics Letters, Vol. 31, no. 13, 22 June 1995, pp. 1032 - 1033. + * + * This circuit interpolates the downconverted sample (using the NCO developed by the Costas loop) + * every mu samples, then it finds the sampling error based on this and the past symbols and the decision + * made on the samples. Like the phase error detector, there are optimized decision algorithms for BPSK + * and QPKS, but 8PSK uses another brute force computation against all possible symbols. The modifications + * to the M&M used here reduce self-noise. + * + */ + +class gr_constellation_receiver_cb : public gr_block +{ + public: + int general_work (int noutput_items, + gr_vector_int &ninput_items, + gr_vector_const_void_star &input_items, + gr_vector_void_star &output_items); + + + // Member function related to the phase/frequency tracking portion of the receiver + //! (CL) Returns the value for alpha (the phase gain term) + float alpha() const { return d_alpha; } + + //! (CL) Returns the value of beta (the frequency gain term) + float beta() const { return d_beta; } + + //! (CL) Returns the current value of the frequency of the NCO in the Costas loop + float freq() const { return d_freq; } + + //! (CL) Returns the current value of the phase of the NCO in the Costal loop + float phase() const { return d_phase; } + + //! (CL) Sets the value for alpha (the phase gain term) + void set_alpha(float alpha) { d_alpha = alpha; } + + //! (CL) Setss the value of beta (the frequency gain term) + void set_beta(float beta) { d_beta = beta; } + + //! (CL) Sets the current value of the frequency of the NCO in the Costas loop + void set_freq(float freq) { d_freq = freq; } + + //! (CL) Setss the current value of the phase of the NCO in the Costal loop + void set_phase(float phase) { d_phase = phase; } + + +protected: + + /*! + * \brief Constructor to synchronize incoming M-PSK symbols + * + * \param constellation constellation of points for generic modulation + * \param alpha gain parameter to adjust the phase in the Costas loop (~0.01) + * \param beta gain parameter to adjust the frequency in the Costas loop (~alpha^2/4) + * \param fmin minimum normalized frequency value the loop can achieve + * \param fmax maximum normalized frequency value the loop can achieve + * + * The constructor also chooses which phase detector and decision maker to use in the work loop based on the + * value of M. + */ + gr_constellation_receiver_cb (gr_constellation constellation, + float alpha, float beta, + float fmin, float fmax); + + void phase_error_tracking(float phase_error); + + private: + unsigned int d_M; + + // Members related to carrier and phase tracking + float d_alpha; + float d_beta; + float d_freq, d_max_freq, d_min_freq; + float d_phase; + + gr_constellation d_constellation; + unsigned int d_current_const_point; + + //! delay line length. + static const unsigned int DLLEN = 8; + + //! delay line plus some length for overflow protection + gr_complex d_dl[2*DLLEN] __attribute__ ((aligned(8))); + + //! index to delay line + unsigned int d_dl_idx; + + friend gr_constellation_receiver_cb_sptr + gr_make_constellation_receiver_cb (gr_constellation constell, + float alpha, float beta, + float fmin, float fmax); +}; + +#endif diff --git a/gnuradio-core/src/lib/general/gr_constellation_receiver_cb.i b/gnuradio-core/src/lib/general/gr_constellation_receiver_cb.i new file mode 100644 index 000000000..b48322f45 --- /dev/null +++ b/gnuradio-core/src/lib/general/gr_constellation_receiver_cb.i @@ -0,0 +1,54 @@ +/* -*- c++ -*- */ +/* + * Copyright 2004 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. + */ + +GR_SWIG_BLOCK_MAGIC(gr,constellation_receiver_cb); + +%template(gr_complex_vector) std::vector<gr_complex>; + +class gr_constellation +{ +public: + gr_constellation (std::vector<gr_complex> constellation); + std::vector<gr_complex> constellation(); + unsigned int decision_maker (gr_complex sample); + unsigned int bits_per_symbol (); +}; + +gr_constellation_receiver_cb_sptr gr_make_constellation_receiver_cb (gr_constellation constellation, + float alpha, float beta, + float fmin, float fmax); +class gr_constellation_receiver_cb : public gr_block +{ + private: + gr_constellation_receiver_cb (gr_contellation constellation, + float alpha, float beta, + float fmin, float fmax); +public: + float alpha() const { return d_alpha; } + float beta() const { return d_beta; } + float freq() const { return d_freq; } + float phase() const { return d_phase; } + void set_alpha(float alpha) { d_alpha = alpha; } + void set_beta(float beta) { d_beta = beta; } + void set_freq(float freq) { d_freq = freq; } + void set_phase(float phase) { d_phase = phase; } +}; diff --git a/gnuradio-core/src/python/gnuradio/blks2impl/Makefile.am b/gnuradio-core/src/python/gnuradio/blks2impl/Makefile.am index 7b24fb69d..377b61224 100644 --- a/gnuradio-core/src/python/gnuradio/blks2impl/Makefile.am +++ b/gnuradio-core/src/python/gnuradio/blks2impl/Makefile.am @@ -38,6 +38,7 @@ grblkspython_PYTHON = \ filterbank.py \ fm_demod.py \ fm_emph.py \ + generic_mod_demod.py \ generic_usrp.py \ gmsk.py \ cpm.py \ diff --git a/gnuradio-core/src/python/gnuradio/blks2impl/generic_mod_demod.py b/gnuradio-core/src/python/gnuradio/blks2impl/generic_mod_demod.py new file mode 100644 index 000000000..8bc33d4dc --- /dev/null +++ b/gnuradio-core/src/python/gnuradio/blks2impl/generic_mod_demod.py @@ -0,0 +1,328 @@ +# +# Copyright 2005,2006,2007,2009 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 + +""" +differential BPSK modulation and demodulation. +""" + +from gnuradio import gr, gru, modulation_utils +from math import pi, sqrt +import psk +import cmath +from pprint import pprint + +# default values (used in __init__ and add_options) +_def_samples_per_symbol = 2 +_def_excess_bw = 0.35 +_def_verbose = False +_def_log = False + +# Frequency correction +_def_freq_alpha = 0.010 +# Symbol timing recovery +_def_timing_alpha = 0.100 +_def_timing_beta = 0.010 +_def_timing_max_dev = 1.5 +# Fine frequency / Phase correction +_def_costas_alpha = 0.1 + +# ///////////////////////////////////////////////////////////////////////////// +# Generic modulator +# ///////////////////////////////////////////////////////////////////////////// + +class generic_mod(gr.hier_block2): + + def __init__(self, constellation, + samples_per_symbol=_def_samples_per_symbol, + excess_bw=_def_excess_bw, + verbose=_def_verbose, + log=_def_log): + """ + Hierarchical block for RRC-filtered differential generic modulation. + + The input is a byte stream (unsigned char) and the + output is the complex modulated signal at baseband. + + @param constellation: determines the modulation type + @type constellation: gnuradio.gr.gr_constellation + @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 verbose: Print information about modulator? + @type verbose: bool + @param log: Log modulation data to files? + @type log: bool + """ + + gr.hier_block2.__init__(self, "generic_mod", + gr.io_signature(1, 1, gr.sizeof_char), # Input signature + gr.io_signature(1, 1, gr.sizeof_gr_complex)) # Output signature + + self._constellation = constellation + self._samples_per_symbol = samples_per_symbol + self._excess_bw = excess_bw + + 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) + + self.diffenc = gr.diff_encoder_bb(arity) + + self.chunks2symbols = gr.chunks_to_symbols_bc(self._constellation.constellation()) + + # 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 + self.connect(self, self.bytes2chunks, self.diffenc, + self.chunks2symbols, self.rrc_filter, self) + + if verbose: + self._print_verbage() + + if log: + self._setup_logging() + + + def samples_per_symbol(self): + return self._samples_per_symbol + + def bits_per_symbol(self): # static method that's also callable on an instance + return self._constellation.bits_per_symbol() + + def add_options(parser): + """ + Adds generic 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]") + add_options=staticmethod(add_options) + + def extract_kwargs_from_options(options): + """ + Given command line options, create dictionary suitable for passing to __init__ + """ + return {} + extract_kwargs_from_options=staticmethod(extract_kwargs_from_options) + + + def _print_verbage(self): + print "\nModulator:" + print "bits per symbol: %d" % self.bits_per_symbol() + print "RRC roll-off factor: %.2f" % self._excess_bw + + def _setup_logging(self): + print "Modulation logging turned on." + self.connect(self.bytes2chunks, + gr.file_sink(gr.sizeof_char, "tx_bytes2chunks.dat")) + self.connect(self.diffenc, + gr.file_sink(gr.sizeof_char, "tx_diffenc.dat")) + self.connect(self.chunks2symbols, + gr.file_sink(gr.sizeof_gr_complex, "tx_chunks2symbols.dat")) + self.connect(self.rrc_filter, + gr.file_sink(gr.sizeof_gr_complex, "tx_rrc_filter.dat")) + + +# ///////////////////////////////////////////////////////////////////////////// +# Generic demodulator +# +# Differentially coherent detection of differentially encoded generically +# modulated signal. +# ///////////////////////////////////////////////////////////////////////////// + +class generic_demod(gr.hier_block2): + + def __init__(self, constellation, + samples_per_symbol=_def_samples_per_symbol, + excess_bw=_def_excess_bw, + freq_alpha=_def_freq_alpha, + timing_alpha=_def_timing_alpha, + timing_max_dev=_def_timing_max_dev, + costas_alpha=_def_costas_alpha, + verbose=_def_verbose, + log=_def_log): + """ + Hierarchical block for RRC-filtered differential generic demodulation. + + The input is the complex modulated signal at baseband. + The output is a stream of bits packed 1 bit per byte (LSB) + + @param constellation: determines the modulation type + @type constellation: gnuradio.gr.gr_constellation + @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 freq_alpha: loop filter gain for frequency recovery + @type freq_alpha: float + @param timing_alpha: loop alpha gain for timing recovery + @type timing_alpha: float + @param timing_max_dev: timing loop maximum rate deviations + @type timing_max_dev: float + @param costas_alpha: loop filter gain in costas loop + @type costas_alphas: 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, "generic_demod", + gr.io_signature(1, 1, gr.sizeof_gr_complex), # Input signature + gr.io_signature(1, 1, gr.sizeof_char)) # Output signature + + self._constellation = constellation + self._samples_per_symbol = samples_per_symbol + self._excess_bw = excess_bw + self._costas_alpha = costas_alpha + self._freq_alpha = freq_alpha + self._freq_beta = 0.10*self._freq_alpha + self._timing_alpha = timing_alpha + self._timing_beta = _def_timing_beta + self._timing_max_dev=timing_max_dev + + 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) + + arity = pow(2,self.bits_per_symbol()) + + # Automatic gain control + self.agc = gr.agc2_cc(0.6e-1, 1e-3, 1, 1, 100) + + # Frequency correction + self.freq_recov = gr.fll_band_edge_cc(self._samples_per_symbol, self._excess_bw, + 11*int(self._samples_per_symbol), + self._freq_alpha, self._freq_beta) + + # symbol timing recovery with RRC data filter + nfilts = 32 + ntaps = 11 * int(self._samples_per_symbol*nfilts) + taps = gr.firdes.root_raised_cosine(nfilts, nfilts, + 1.0/float(self._samples_per_symbol), + self._excess_bw, ntaps) + self.time_recov = gr.pfb_clock_sync_ccf(self._samples_per_symbol, + self._timing_alpha, + taps, nfilts, nfilts/2, self._timing_max_dev) + self.time_recov.set_beta(self._timing_beta) + + self._costas_beta = 0.25 * self._costas_alpha * self._costas_alpha + fmin = -0.25 + fmax = 0.25 + + self.receiver = gr.constellation_receiver_cb( + self._constellation, + self._costas_alpha, self._costas_beta, + fmin, fmax) + + # Do differential decoding based on phase change of symbols + self.diffdec = gr.diff_decoder_bb(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.connect(self, self.agc, self.freq_recov, self.time_recov, self.receiver, + self.diffdec, self.unpack, self) + + def samples_per_symbol(self): + return self._samples_per_symbol + + def bits_per_symbol(self): # staticmethod that's also callable on an instance + return self._constellation.bits_per_symbol() + + def _print_verbage(self): + print "\nDemodulator:" + print "bits per symbol: %d" % self.bits_per_symbol() + print "RRC roll-off factor: %.2f" % self._excess_bw + print "Costas Loop alpha: %.2e" % self._costas_alpha + print "Costas Loop beta: %.2e" % self._costas_beta + print "M&M mu: %.2f" % self._mm_mu + print "M&M mu gain: %.2e" % self._mm_gain_mu + print "M&M omega: %.2f" % self._mm_omega + print "M&M omega gain: %.2e" % self._mm_gain_omega + print "M&M omega limit: %.2f" % self._mm_omega_relative_limit + + def _setup_logging(self): + print "Modulation logging turned on." + self.connect(self.pre_scaler, + gr.file_sink(gr.sizeof_gr_complex, "rx_prescaler.dat")) + self.connect(self.agc, + gr.file_sink(gr.sizeof_gr_complex, "rx_agc.dat")) + self.connect(self.rrc_filter, + gr.file_sink(gr.sizeof_gr_complex, "rx_rrc_filter.dat")) + self.connect(self.receiver, + gr.file_sink(gr.sizeof_gr_complex, "rx_receiver.dat")) + self.connect(self.diffdec, + gr.file_sink(gr.sizeof_gr_complex, "rx_diffdec.dat")) + self.connect(self.unpack, + gr.file_sink(gr.sizeof_char, "rx_unpack.dat")) + + def add_options(parser): + """ + Adds generic 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("", "--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 {} + 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/blks2impl/qam.py b/gnuradio-core/src/python/gnuradio/blks2impl/qam.py index 22b1e1dab..55081bcc0 100644 --- a/gnuradio-core/src/python/gnuradio/blks2impl/qam.py +++ b/gnuradio-core/src/python/gnuradio/blks2impl/qam.py @@ -19,95 +19,220 @@ # Boston, MA 02110-1301, USA. # -from math import pi, sqrt -import math +""" +QAM modulation and demodulation. +""" -# These constellations are generated for Gray coding when symbos [1, ..., m] are used -# Mapping to Gray coding is therefore unnecessary +from math import pi, sqrt, log +from itertools import islice + +from gnuradio import gr, gru, modulation_utils +from gnuradio.blks2impl.generic_mod_demod import generic_mod, generic_demod + + +# default values (used in __init__ and add_options) +_def_samples_per_symbol = 2 +_def_excess_bw = 0.35 +_def_verbose = False +_def_log = False + +# Frequency correction +_def_freq_alpha = 0.010 +# Symbol timing recovery +_def_timing_alpha = 0.100 +_def_timing_beta = 0.010 +_def_timing_max_dev = 1.5 +# Fine frequency / Phase correction +_def_costas_alpha = 0.1 + +def is_power_of_four(x): + v = log(x)/log(4) + return int(v) == v + +def get_bit(x, n): + """ Get the n'th bit of integer x (from little end).""" + return (x&(0x01 << n)) >> n + +def get_bits(x, n, k): + """ Get the k bits of integer x starting at bit n(from little end).""" + # Remove the n smallest bits + v = x >> n + # Remove all bits bigger than n+k-1 + return v % pow(2, k) + +def gray_codes(): + """ Generates gray codes.""" + gcs = [0, 1] + yield 0 + yield 1 + # The last power of two passed through. + lp2 = 2 + # The next power of two that will be passed through. + np2 = 4 + i = 2 + while True: + if i == lp2: + # if i is a power of two then gray number is of form 1100000... + result = i + i/2 + else: + # if not we take advantage of the symmetry of all but the last bit + # around a power of two. + result = gcs[2*lp2-1-i] + lp2 + gcs.append(result) + yield result + i += 1 + if i == np2: + lp2 = i + np2 = i*2 def make_constellation(m): - # number of bits/symbol (log2(M)) - k = int(math.log10(m) / math.log10(2.0)) + """ + Create a constellation with m possible symbols where m must be + a power of 4. + + Points are laid out in a square grid. - coeff = 1 + """ + sqrtm = pow(m, 0.5) + if (not isinstance(m, int) or m < 4 or not is_power_of_four(m)): + raise ValueError("m must be a power of 4 integer.") + # Each symbol holds k bits. + k = int(log(m) / log(2.0)) + # First create a constellation for one quadrant containing m/4 points. + # The quadrant has 'side' points along each side of a quadrant. + side = int(sqrtm/2) + # Number rows and columns using gray codes. + gcs = list(islice(gray_codes(), side)) + # Get inverse gray codes. + i_gcs = dict([(v, key) for key, v in enumerate(gcs)]) + # The distance between points is found. + step = 1/(side-0.5) + + gc_to_x = [(i_gcs[gc]+0.5)*step for gc in range(0, side)] + + # Takes the (x, y) location of the point with the quadrant along + # with the quadrant number. (x, y) are integers referring to which + # point within the quadrant it is. + # A complex number representing this location of this point is returned. + def get_c(gc_x, gc_y, quad): + if quad == 0: + return complex(gc_to_x[gc_x], gc_to_x[gc_y]) + if quad == 1: + return complex(-gc_to_x[gc_y], gc_to_x[gc_x]) + if quad == 2: + return complex(-gc_to_x[gc_x], -gc_to_x[gc_y]) + if quad == 3: + return complex(gc_to_x[gc_y], -gc_to_x[gc_x]) + raise StandardError("Impossible!") + + # First two bits determine quadrant. + # Next (k-2)/2 bits determine x position. + # Following (k-2)/2 bits determine y position. + # How x and y relate to real and imag depends on quadrant (see get_c function). const_map = [] for i in range(m): - a = (i&(0x01 << k-1)) >> k-1 - b = (i&(0x01 << k-2)) >> k-2 - bits_i = [((i&(0x01 << k-j-1)) >> k-j-1) for j in range(2, k, 2)] - bits_q = [((i&(0x01 << k-j-1)) >> k-j-1) for j in range(3, k, 2)] - - ss = 0 - ll = len(bits_i) - for ii in range(ll): - rr = 0 - for jj in range(ll-ii): - rr = abs(bits_i[jj] - rr) - ss += rr*pow(2.0, ii+1) - re = (2*a-1)*(ss+1) - - ss = 0 - ll = len(bits_q) - for ii in range(ll): - rr = 0 - for jj in range(ll-ii): - rr = abs(bits_q[jj] - rr) - ss += rr*pow(2.0, ii+1) - im = (2*b-1)*(ss+1) - - a = max(re, im) - if a > coeff: - coeff = a - const_map.append(complex(re, im)) - - norm_map = [complex(i.real/coeff, i.imag/coeff) for i in const_map] - return norm_map + y = get_bits(i, 0, (k-2)/2) + x = get_bits(i, (k-2)/2, (k-2)/2) + quad = get_bits(i, k-2, 2) + const_map.append(get_c(x, y, quad)) + + return const_map -# Common definition of constellations for Tx and Rx -constellation = { - 4 : make_constellation(4), # QAM4 (QPSK) - 8 : make_constellation(8), # QAM8 - 16: make_constellation(16), # QAM16 - 64: make_constellation(64), # QAM64 - 256: make_constellation(256) # QAM256 - } - -# ----------------------- -# Do Gray code -# ----------------------- -# binary to gray coding -binary_to_gray = { - 4 : range(4), - 8 : range(8), - 16: range(16), - 64: range(64), - 256: range(256) - } - -# gray to binary -gray_to_binary = { - 4 : range(4), - 8 : range(8), - 16: range(16), - 64: range(64), - 256: range(256) - } - -# ----------------------- -# Don't Gray code -# ----------------------- -# identity mapping -binary_to_ungray = { - 4 : range(4), - 8 : range(8), - 16: range(16), - 64: range(64) - } + +# ///////////////////////////////////////////////////////////////////////////// +# QAM modulator +# ///////////////////////////////////////////////////////////////////////////// + +class qam_mod(generic_mod): + + def __init__(self, m, + samples_per_symbol=_def_samples_per_symbol, + excess_bw=_def_excess_bw, + verbose=_def_verbose, + log=_def_log): + + """ + Hierarchical block for RRC-filtered QAM modulation. + + The input is a byte stream (unsigned char) and the + output is the complex modulated signal at baseband. + + @param m: Number of constellation points. Must be a power of four. + @type m: integer + @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 verbose: Print information about modulator? + @type verbose: bool + @param log: Log modulation data to files? + @type log: bool + """ + + if not isinstance(m, int) or not is_power_of_four(m): + raise ValueError("m must be a power of two integer greater than or equal to 4.") + + points = make_constellation(m) + constellation = gr.gr_constellation(points) + + super(qam_mod, self).__init__(constellation, samples_per_symbol, + excess_bw, verbose, log) + + +# ///////////////////////////////////////////////////////////////////////////// +# QAM demodulator +# +# ///////////////////////////////////////////////////////////////////////////// + +class qam_demod(generic_demod): + + def __init__(self, m, + samples_per_symbol=_def_samples_per_symbol, + excess_bw=_def_excess_bw, + freq_alpha=_def_freq_alpha, + timing_alpha=_def_timing_alpha, + timing_max_dev=_def_timing_max_dev, + costas_alpha=_def_costas_alpha, + verbose=_def_verbose, + log=_def_log): + + """ + Hierarchical block for RRC-filtered QAM modulation. + + The input is a byte stream (unsigned char) and the + output is the complex modulated signal at baseband. + + @param m: Number of constellation points. Must be a power of four. + @type m: integer + @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 freq_alpha: loop filter gain for frequency recovery + @type freq_alpha: float + @param timing_alpha: loop alpha gain for timing recovery + @type timing_alpha: float + @param timing_max_dev: timing loop maximum rate deviations + @type timing_max_dev: float + @param costas_alpha: loop filter gain in costas loop + @type costas_alphas: float + @param verbose: Print information about modulator? + @type verbose: bool + @param debug: Print modualtion data to files? + @type debug: bool + """ + + points = make_constellation(m) + constellation = gr.gr_constellation(points) + + super(qam_demod, self).__init__(constellation, samples_per_symbol, + excess_bw, freq_alpha, timing_alpha, + timing_max_dev, costas_alpha, verbose, + log) -# identity mapping -ungray_to_binary = { - 4 : range(4), - 8 : range(8), - 16: range(16), - 64: range(64) - } +# +# Add these to the mod/demod registry +# +# NOT READY TO BE USED YET -- ENABLE AT YOUR OWN RISK +#modulation_utils.add_type_1_mod('qam16', qam16_mod) +#modulation_utils.add_type_1_demod('qam16', qam16_demod) diff --git a/gnuradio-core/src/python/gnuradio/blks2impl/qam16.py b/gnuradio-core/src/python/gnuradio/blks2impl/qam16.py deleted file mode 100644 index 0bdb9c6fb..000000000 --- a/gnuradio-core/src/python/gnuradio/blks2impl/qam16.py +++ /dev/null @@ -1,208 +0,0 @@ -# -# 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 - -""" -QAM16 modulation and demodulation. -""" - -from gnuradio import gr, gru, modulation_utils -from math import pi, sqrt -import qam -import cmath -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 = None -_def_gain_mu = 0.03 -_def_mu = 0.05 -_def_omega_relative_limit = 0.005 - - -# ///////////////////////////////////////////////////////////////////////////// -# QAM16 modulator -# ///////////////////////////////////////////////////////////////////////////// - -class qam16_mod(gr.hier_block2): - - def __init__(self, - 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 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 - """ - - gr.hier_block2.__init__(self, "qam16_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._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(qam.binary_to_gray[arity]) - else: - self.symbol_mapper = gr.map_bb(qam.binary_to_ungray[arity]) - - self.diffenc = gr.diff_encoder_bb(arity) - - rot = 1.0 - print "constellation with %d arity" % arity - rotated_const = map(lambda pt: pt * rot, qam.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 - self.connect(self, self.bytes2chunks, self.symbol_mapper, self.diffenc, - self.chunks2symbols, self.rrc_filter, 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 4 - 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.connect(self.bytes2chunks, - gr.file_sink(gr.sizeof_char, "bytes2chunks.dat")) - self.connect(self.symbol_mapper, - gr.file_sink(gr.sizeof_char, "graycoder.dat")) - self.connect(self.diffenc, - gr.file_sink(gr.sizeof_char, "diffenc.dat")) - self.connect(self.chunks2symbols, - gr.file_sink(gr.sizeof_gr_complex, "chunks2symbols.dat")) - self.connect(self.rrc_filter, - gr.file_sink(gr.sizeof_gr_complex, "rrc_filter.dat")) - - def add_options(parser): - """ - Adds QAM 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(qam16_mod.__init__, - ('self',), options) - extract_kwargs_from_options=staticmethod(extract_kwargs_from_options) - - -# ///////////////////////////////////////////////////////////////////////////// -# QAM16 demodulator -# -# ///////////////////////////////////////////////////////////////////////////// - -class qam16_demod(gr.hier_block2): - - def __init__(self, - 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): - - gr.hier_block2.__init__(self, "qam16_demod", - gr.io_signature(1, 1, gr.sizeof_gr_complex), # Input signature - gr.io_signature(1, 1, gr.sizeof_char)) # Output signature - # do this - pass - - def bits_per_symbol(self=None): # staticmethod that's also callable on an instance - return 4 - bits_per_symbol = staticmethod(bits_per_symbol) # make it a static method. RTFM - -# -# Add these to the mod/demod registry -# -# NOT READY TO BE USED YET -- ENABLE AT YOUR OWN RISK -#modulation_utils.add_type_1_mod('qam16', qam16_mod) -#modulation_utils.add_type_1_demod('qam16', qam16_demod) diff --git a/gnuradio-core/src/python/gnuradio/blks2impl/qam256.py b/gnuradio-core/src/python/gnuradio/blks2impl/qam256.py deleted file mode 100644 index fc455f17c..000000000 --- a/gnuradio-core/src/python/gnuradio/blks2impl/qam256.py +++ /dev/null @@ -1,209 +0,0 @@ -# -# 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 - -""" -QAM256 modulation and demodulation. -""" - -from gnuradio import gr, gru, modulation_utils -from math import pi, sqrt -import qam -import cmath -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 = None -_def_gain_mu = 0.03 -_def_mu = 0.05 -_def_omega_relative_limit = 0.005 - - -# ///////////////////////////////////////////////////////////////////////////// -# QAM256 modulator -# ///////////////////////////////////////////////////////////////////////////// - -class qam256_mod(gr.hier_block2): - - def __init__(self, - 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 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 - """ - - gr.hier_block2.__init__(self, "qam256_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._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(qam.binary_to_gray[arity]) - else: - self.symbol_mapper = gr.map_bb(qam.binary_to_ungray[arity]) - - self.diffenc = gr.diff_encoder_bb(arity) - - rot = 1.0 - print "constellation with %d arity" % arity - rotated_const = map(lambda pt: pt * rot, qam.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 - self.connect(self, self.bytes2chunks, self.symbol_mapper, self.diffenc, - self.chunks2symbols, self.rrc_filter, 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 8 - 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.connect(self.bytes2chunks, - gr.file_sink(gr.sizeof_char, "bytes2chunks.dat")) - self.connect(self.symbol_mapper, - gr.file_sink(gr.sizeof_char, "graycoder.dat")) - self.connect(self.diffenc, - gr.file_sink(gr.sizeof_char, "diffenc.dat")) - self.connect(self.chunks2symbols, - gr.file_sink(gr.sizeof_gr_complex, "chunks2symbols.dat")) - self.connect(self.rrc_filter, - gr.file_sink(gr.sizeof_gr_complex, "rrc_filter.dat")) - - def add_options(parser): - """ - Adds QAM 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(qam256_mod.__init__, - ('self',), options) - extract_kwargs_from_options=staticmethod(extract_kwargs_from_options) - - -# ///////////////////////////////////////////////////////////////////////////// -# QAM256 demodulator -# -# ///////////////////////////////////////////////////////////////////////////// - -class qam256_demod(gr.hier_block2): - - def __init__(self, - 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): - - gr.hier_block2.__init__(self, "qam256_demod", - gr.io_signature(1, 1, gr.sizeof_gr_complex), # Input signature - gr.io_signature(1, 1, gr.sizeof_char)) # Output signature - - # do this - pass - - def bits_per_symbol(self=None): # staticmethod that's also callable on an instance - return 8 - bits_per_symbol = staticmethod(bits_per_symbol) # make it a static method. RTFM - -# -# Add these to the mod/demod registry -# -# NOT READY TO BE USED YET -- ENABLE AT YOUR OWN RISK -#modulation_utils.add_type_1_mod('qam256', qam256_mod) -#modulation_utils.add_type_1_demod('qam256', qam256_demod) diff --git a/gnuradio-core/src/python/gnuradio/blks2impl/qam64.py b/gnuradio-core/src/python/gnuradio/blks2impl/qam64.py deleted file mode 100644 index 5509f3745..000000000 --- a/gnuradio-core/src/python/gnuradio/blks2impl/qam64.py +++ /dev/null @@ -1,208 +0,0 @@ -# -# 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 - -""" -differential QPSK modulation and demodulation. -""" - -from gnuradio import gr, gru, modulation_utils -from math import pi, sqrt -import qam -import cmath -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 = None -_def_gain_mu = 0.03 -_def_mu = 0.05 -_def_omega_relative_limit = 0.005 - - -# ///////////////////////////////////////////////////////////////////////////// -# QAM64 modulator -# ///////////////////////////////////////////////////////////////////////////// - -class qam64_mod(gr.hier_block2): - - def __init__(self, - 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 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 - """ - - gr.hier_block2.__init__(self, "qam64_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._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(qam.binary_to_gray[arity]) - else: - self.symbol_mapper = gr.map_bb(qam.binary_to_ungray[arity]) - - self.diffenc = gr.diff_encoder_bb(arity) - - rot = 1.0 - print "constellation with %d arity" % arity - rotated_const = map(lambda pt: pt * rot, qam.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 - self.connect(self, self.bytes2chunks, self.symbol_mapper, self.diffenc, - self.chunks2symbols, self.rrc_filter, 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 6 - 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.connect(self.bytes2chunks, - gr.file_sink(gr.sizeof_char, "bytes2chunks.dat")) - self.connect(self.symbol_mapper, - gr.file_sink(gr.sizeof_char, "graycoder.dat")) - self.connect(self.diffenc, - gr.file_sink(gr.sizeof_char, "diffenc.dat")) - self.connect(self.chunks2symbols, - gr.file_sink(gr.sizeof_gr_complex, "chunks2symbols.dat")) - self.connect(self.rrc_filter, - gr.file_sink(gr.sizeof_gr_complex, "rrc_filter.dat")) - - def add_options(parser): - """ - Adds QAM 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(qam64_mod.__init__, - ('self',), options) - extract_kwargs_from_options=staticmethod(extract_kwargs_from_options) - - -# ///////////////////////////////////////////////////////////////////////////// -# QAM16 demodulator -# -# ///////////////////////////////////////////////////////////////////////////// - -class qam64_demod(gr.hier_block2): - - def __init__(self, - 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): - - gr.hier_block2.__init__(self, "qam64_demod", - gr.io_signature(1, 1, gr.sizeof_gr_complex), # Input signature - gr.io_signature(1, 1, gr.sizeof_char)) # Output signature - - # do this - pass - - def bits_per_symbol(self=None): # staticmethod that's also callable on an instance - return 6 - bits_per_symbol = staticmethod(bits_per_symbol) # make it a static method. RTFM - -# -# Add these to the mod/demod registry -# -# NOT READY TO BE USED YET -- ENABLE AT YOUR OWN RISK -#modulation_utils.add_type_1_mod('qam64', qam64_mod) -#modulation_utils.add_type_1_demod('qam16', qam16_demod) diff --git a/gnuradio-core/src/python/gnuradio/blks2impl/qam8.py b/gnuradio-core/src/python/gnuradio/blks2impl/qam8.py deleted file mode 100644 index 6a7b35597..000000000 --- a/gnuradio-core/src/python/gnuradio/blks2impl/qam8.py +++ /dev/null @@ -1,209 +0,0 @@ -# -# 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 - -""" -QAM8 modulation and demodulation. -""" - -from gnuradio import gr, gru, modulation_utils -from math import pi, sqrt -import qam -import cmath -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 = None -_def_gain_mu = 0.03 -_def_mu = 0.05 -_def_omega_relative_limit = 0.005 - - -# ///////////////////////////////////////////////////////////////////////////// -# QAM8 modulator -# ///////////////////////////////////////////////////////////////////////////// - -class qam8_mod(gr.hier_block2): - - def __init__(self, - 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 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 - """ - - gr.hier_block2.__init__(self, "qam8_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._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(qam.binary_to_gray[arity]) - else: - self.symbol_mapper = gr.map_bb(qam.binary_to_ungray[arity]) - - self.diffenc = gr.diff_encoder_bb(arity) - - rot = 1.0 - print "constellation with %d arity" % arity - rotated_const = map(lambda pt: pt * rot, qam.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 - self.connect(self, self.bytes2chunks, self.symbol_mapper, self.diffenc, - self.chunks2symbols, self.rrc_filter, 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 3 - 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.connect(self.bytes2chunks, - gr.file_sink(gr.sizeof_char, "bytes2chunks.dat")) - self.connect(self.symbol_mapper, - gr.file_sink(gr.sizeof_char, "graycoder.dat")) - self.connect(self.diffenc, - gr.file_sink(gr.sizeof_char, "diffenc.dat")) - self.connect(self.chunks2symbols, - gr.file_sink(gr.sizeof_gr_complex, "chunks2symbols.dat")) - self.connect(self.rrc_filter, - gr.file_sink(gr.sizeof_gr_complex, "rrc_filter.dat")) - - def add_options(parser): - """ - Adds QAM 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(qam8_mod.__init__, - ('self',), options) - extract_kwargs_from_options=staticmethod(extract_kwargs_from_options) - - -# ///////////////////////////////////////////////////////////////////////////// -# QAM8 demodulator -# -# ///////////////////////////////////////////////////////////////////////////// - -class qam8_demod(gr.hier_block2): - - def __init__(self, - 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): - - gr.hier_block2.__init__(self, "qam8_demod", - gr.io_signature(1, 1, gr.sizeof_gr_complex), # Input signature - gr.io_signature(1, 1, gr.sizeof_char)) # Output signature - - # do this - pass - - def bits_per_symbol(self=None): # staticmethod that's also callable on an instance - return 3 - bits_per_symbol = staticmethod(bits_per_symbol) # make it a static method. RTFM - -# -# Add these to the mod/demod registry -# -# NOT READY TO BE USED YET -- ENABLE AT YOUR OWN RISK -modulation_utils.add_type_1_mod('qam8', qam8_mod) -#modulation_utils.add_type_1_demod('qam8', qam8_demod) diff --git a/gnuradio-examples/python/digital/simple_qam.py b/gnuradio-examples/python/digital/simple_qam.py new file mode 100644 index 000000000..dee57baeb --- /dev/null +++ b/gnuradio-examples/python/digital/simple_qam.py @@ -0,0 +1,64 @@ + +from gnuradio import gr, blks2, packet_utils + +# Some constants +NOISE_VOLTAGE = 0 +FREQUENCY_OFFSET = 0 +TIMING_OFFSET = 1.0 +SAMPLES_PER_SYMBOL = 2 +GAIN = 1.0 +SW_DECIM = 1 +BAND_MIDPOINT = 1.0 +BAND_WIDTH = 0.5 + +# Modulation/Demodulation methods +modulator = blks2.qam_mod(4, SAMPLES_PER_SYMBOL) +demodulator = blks2.qam_demod(4, SAMPLES_PER_SYMBOL) + +#Transmission Blocks +packet_transmitter = blks2.mod_pkts(modulator, access_code=None, msgq_limit=4, + pad_for_usrp=True) +# Channel +channel = gr.channel_model(NOISE_VOLTAGE, FREQUENCY_OFFSET, TIMING_OFFSET) +# Receiver Blocks +chan_coeffs = gr.firdes.low_pass(GAIN, SW_DECIM * SAMPLES_PER_SYMBOL, + BAND_MIDPOINT, BAND_WIDTH, gr.firdes.WIN_HANN) +channel_filter = gr.fft_filter_ccc(SW_DECIM, chan_coeffs) +packet_receiver = blks2.demod_pkts(demodulator, access_code=None, callback=None, + threshold=-1) +# Put it all together and start it up (although nothing will be done +# until we send some packets). +tb = gr.top_block() +tb.connect(packet_transmitter, channel, channel_filter, + packet_receiver) +tb.start() + +# The queue into which recieved packets are placed +pkq = packet_receiver._rcvd_pktq + +# The function to create a packet and place it in a queue to be sent. +sender = packet_transmitter.send_pkt + +# Some some packets (The second will not be recieved because it gets cut off +# before it can finish. I think this occurs during modulation.) +sender('hello1') +sender('hello2') +sender('hello3') +sender('hello4') +sender('hello5') +sender('hello6') +sender('hello7') +sender('world') +sender(eof=True) + +# Wait for all the packets to get sent and received. +tb.wait() + +# Check how many messages have been received and print them. +cnt = pkq.count() +print('There are %s messages' % cnt) +for a in range(0, cnt): + msg = pkq.delete_head() + ok, payload = packet_utils.unmake_packet(msg.to_string(), int(msg.arg1())) + print("Message %s is %s" % (a, payload)) + |