diff options
author | Ben Reynwar <ben@reynwar.net> | 2011-01-20 14:21:07 -0700 |
---|---|---|
committer | Ben Reynwar <ben@reynwar.net> | 2011-01-20 14:21:07 -0700 |
commit | c23d8a22a8b7859e83d95bd0b439229876c2d5b0 (patch) | |
tree | 1ce54260e2998bbfa6dc650205f8577732254dc7 | |
parent | 1ec42d6fa313c6d3eeefae783a74a8600da3b76e (diff) |
Added support for PSK to generic modulation.
-rw-r--r-- | gnuradio-core/src/lib/general/gr_constellation.cc | 96 | ||||
-rw-r--r-- | gnuradio-core/src/lib/general/gr_constellation.h | 115 | ||||
-rw-r--r-- | gnuradio-core/src/lib/general/gr_constellation.i | 42 | ||||
-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 | 119 | ||||
-rw-r--r-- | gnuradio-core/src/python/gnuradio/blks2impl/psk2.py | 100 | ||||
-rw-r--r-- | gnuradio-core/src/python/gnuradio/blks2impl/qam.py | 92 | ||||
-rw-r--r-- | gnuradio-core/src/python/gnuradio/modulation_utils2.py | 20 | ||||
-rw-r--r-- | gnuradio-core/src/python/gnuradio/utils/gray_code.py | 25 |
9 files changed, 495 insertions, 115 deletions
diff --git a/gnuradio-core/src/lib/general/gr_constellation.cc b/gnuradio-core/src/lib/general/gr_constellation.cc index 03589dee82..8b98a57311 100644 --- a/gnuradio-core/src/lib/general/gr_constellation.cc +++ b/gnuradio-core/src/lib/general/gr_constellation.cc @@ -24,20 +24,25 @@ #include <gr_constellation.h> #include <gr_math.h> #include <gr_complex.h> +#include <math.h> +#include <iostream> +#include <stdlib.h> + +#define M_TWOPI (2*M_PI) gr_constellation_sptr gr_make_constellation(std::vector<gr_complex> constellation) { return gr_constellation_sptr(new gr_constellation (constellation)); - } +} // Base Constellation Class -gr_constellation::gr_constellation (std::vector<gr_complex> constellation) { - d_constellation = constellation; +gr_constellation::gr_constellation (std::vector<gr_complex> constellation) : + d_constellation(constellation) +{ } - unsigned int get_closest_point(std::vector<gr_complex> constellation, gr_complex sample) { unsigned int table_size = constellation.size(); @@ -66,23 +71,11 @@ unsigned int gr_constellation::decision_maker(gr_complex sample) return min_index; } -gr_constellation_sector_sptr -gr_make_constellation_sector(std::vector<gr_complex> constellation, - unsigned int real_sectors, unsigned int imag_sectors, - float width_real_sectors, float width_imag_sectors) -{ - return gr_constellation_sector_sptr(new gr_constellation_sector (constellation, real_sectors, imag_sectors, width_real_sectors, width_imag_sectors)); - } - gr_constellation_sector::gr_constellation_sector (std::vector<gr_complex> constellation, - unsigned int real_sectors, unsigned int imag_sectors, - float width_real_sectors, float width_imag_sectors) : + unsigned int n_sectors) : gr_constellation(constellation), - n_sectors(real_sectors * imag_sectors), - n_real_sectors(real_sectors), n_imag_sectors(imag_sectors), - d_width_real_sectors(width_real_sectors), d_width_imag_sectors(width_imag_sectors) + n_sectors(n_sectors) { - find_sector_values(); } unsigned int gr_constellation_sector::decision_maker (gr_complex sample) { @@ -91,7 +84,33 @@ unsigned int gr_constellation_sector::decision_maker (gr_complex sample) { return sector_values[sector]; } -unsigned int gr_constellation_sector::get_sector (gr_complex sample) { +void gr_constellation_sector::find_sector_values () { + unsigned int i; + sector_values.clear(); + for (i=0; i<n_sectors; i++) { + sector_values.push_back(calc_sector_value(i)); + } +} + +gr_constellation_rect_sptr +gr_make_constellation_rect(std::vector<gr_complex> constellation, + unsigned int real_sectors, unsigned int imag_sectors, + float width_real_sectors, float width_imag_sectors) +{ + return gr_constellation_rect_sptr(new gr_constellation_rect (constellation, real_sectors, imag_sectors, width_real_sectors, width_imag_sectors)); + } + +gr_constellation_rect::gr_constellation_rect (std::vector<gr_complex> constellation, + unsigned int real_sectors, unsigned int imag_sectors, + float width_real_sectors, float width_imag_sectors) : + gr_constellation_sector(constellation, real_sectors * imag_sectors), + n_real_sectors(real_sectors), n_imag_sectors(imag_sectors), + d_width_real_sectors(width_real_sectors), d_width_imag_sectors(width_imag_sectors) +{ + find_sector_values(); +} + +unsigned int gr_constellation_rect::get_sector (gr_complex sample) { int real_sector, imag_sector; unsigned int sector; real_sector = int(real(sample)/d_width_real_sectors + n_real_sectors/2.0); @@ -104,7 +123,7 @@ unsigned int gr_constellation_sector::get_sector (gr_complex sample) { return sector; } -unsigned int gr_constellation_sector::calc_sector_value (unsigned int sector) { +unsigned int gr_constellation_rect::calc_sector_value (unsigned int sector) { unsigned int real_sector, imag_sector; gr_complex sector_center; unsigned int closest_point; @@ -117,11 +136,36 @@ unsigned int gr_constellation_sector::calc_sector_value (unsigned int sector) { } -void gr_constellation_sector::find_sector_values () { - unsigned int i; - sector_values.clear(); - for (i=0; i<n_sectors; i++) { - sector_values.push_back(calc_sector_value(i)); - } +gr_constellation_psk_sptr +gr_make_constellation_psk(std::vector<gr_complex> constellation, unsigned int n_sectors) +{ + return gr_constellation_psk_sptr(new gr_constellation_psk (constellation, n_sectors)); } +gr_constellation_psk::gr_constellation_psk (std::vector<gr_complex> constellation, + unsigned int n_sectors) : + gr_constellation_sector(constellation, n_sectors) +{ + find_sector_values(); +} + +unsigned int gr_constellation_psk::get_sector (gr_complex sample) { + float phase = arg(sample); + float width = M_TWOPI / n_sectors; + int sector = floor(phase/width + 0.5); + unsigned int u_sector; + if (sector < 0) sector += n_sectors; + u_sector = sector; + // std::cout << phase << " " << width << " " << sector << std::endl; + return sector; +} + +unsigned int gr_constellation_psk::calc_sector_value (unsigned int sector) { + float phase = sector * M_TWOPI / n_sectors; + gr_complex sector_center = gr_complex(cos(phase), sin(phase)); + unsigned int closest_point = get_closest_point(d_constellation, sector_center); + // std::cout << phase << " " << sector_center << " " << closest_point << std::endl; + return closest_point; +} + + diff --git a/gnuradio-core/src/lib/general/gr_constellation.h b/gnuradio-core/src/lib/general/gr_constellation.h index 92ae3c5e2f..d7f7b09b43 100644 --- a/gnuradio-core/src/lib/general/gr_constellation.h +++ b/gnuradio-core/src/lib/general/gr_constellation.h @@ -28,6 +28,12 @@ #include <gr_complex.h> #include <boost/enable_shared_from_this.hpp> +/************************************************************/ +/* gr_constellation */ +/* */ +/* Decision maker uses nearest-point method. */ +/************************************************************/ + class gr_constellation; typedef boost::shared_ptr<gr_constellation> gr_constellation_sptr; @@ -36,6 +42,7 @@ gr_constellation_sptr gr_make_constellation (std::vector<gr_complex> constellation); class gr_constellation : public boost::enable_shared_from_this<gr_constellation> +//class gr_constellation { public: @@ -53,6 +60,7 @@ class gr_constellation : public boost::enable_shared_from_this<gr_constellation> } gr_constellation_sptr base() { + //return gr_constellation_sptr(this); return shared_from_this(); } @@ -65,44 +73,119 @@ class gr_constellation : public boost::enable_shared_from_this<gr_constellation> gr_make_constellation (std::vector<gr_complex> constellation); }; -class gr_constellation_sector; -typedef boost::shared_ptr<gr_constellation_sector> gr_constellation_sector_sptr; - -// public constructor -gr_constellation_sector_sptr -gr_make_constellation_sector (std::vector<gr_complex> constellation, unsigned int real_sectors, unsigned int imag_sectors, - float width_real_sectors, float width_imag_sectors); +/************************************************************/ +/* gr_constellation_sector */ +/* */ +/* An abstract class. */ +/* Constellation space is divided into sectors. */ +/* Each sector is associated with the nearest constellation */ +/* point. */ +/************************************************************/ class gr_constellation_sector : public gr_constellation { public: - gr_constellation_sector (std::vector<gr_complex> constellation, unsigned int real_sectors, unsigned int imag_sectors, - float width_real_sectors, float width_imag_sectors); + gr_constellation_sector (std::vector<gr_complex> constellation, + unsigned int n_sectors); unsigned int decision_maker (gr_complex sample); - protected: + // protected: - virtual unsigned int get_sector (gr_complex sample); + virtual unsigned int get_sector (gr_complex sample) = 0; - virtual unsigned int calc_sector_value (unsigned int sector); + virtual unsigned int calc_sector_value (unsigned int sector) = 0; void find_sector_values (); - private: + unsigned int n_sectors; + + // private: std::vector<unsigned int> sector_values; - unsigned int n_sectors; + +}; + +/************************************************************/ +/* gr_constellation_rect */ +/* */ +/* Constellation space is divided into rectangular sectors. */ +/* Each sector is associated with the nearest constellation */ +/* point. */ +/* Works well for square QAM. */ +/* Works for any generic constellation provided sectors are */ +/* not too large. */ +/************************************************************/ + +class gr_constellation_rect; +typedef boost::shared_ptr<gr_constellation_rect> gr_constellation_rect_sptr; + +// public constructor +gr_constellation_rect_sptr +gr_make_constellation_rect (std::vector<gr_complex> constellation, unsigned int real_sectors, unsigned int imag_sectors, + float width_real_sectors, float width_imag_sectors); + +class gr_constellation_rect : public gr_constellation_sector +{ + public: + + gr_constellation_rect (std::vector<gr_complex> constellation, unsigned int real_sectors, unsigned int imag_sectors, + float width_real_sectors, float width_imag_sectors); + + // protected: + + unsigned int get_sector (gr_complex sample); + + unsigned int calc_sector_value (unsigned int sector); + + // private: + unsigned int n_real_sectors; unsigned int n_imag_sectors; float d_width_real_sectors; float d_width_imag_sectors; - friend gr_constellation_sector_sptr - gr_make_constellation_sector (std::vector<gr_complex> constellation, unsigned int real_sectors, unsigned int imag_sectors, + friend gr_constellation_rect_sptr + gr_make_constellation_rect (std::vector<gr_complex> constellation, unsigned int real_sectors, unsigned int imag_sectors, float width_real_sectors, float width_imag_sectors); }; +/************************************************************/ +/* gr_constellation_psk */ +/* */ +/* Constellation space is divided into pie slices sectors. */ +/* Each slice is associated with the nearest constellation */ +/* point. */ +/* Works well for PSK but nothing else. */ +/* Assumes that there is a constellation point at 1. */ +/************************************************************/ + +class gr_constellation_psk; +typedef boost::shared_ptr<gr_constellation_psk> gr_constellation_psk_sptr; + +// public constructor +gr_constellation_psk_sptr +gr_make_constellation_psk (std::vector<gr_complex> constellation, unsigned int n_sectors); + +class gr_constellation_psk : public gr_constellation_sector +{ + public: + + gr_constellation_psk (std::vector<gr_complex> constellation, unsigned int n_sectors); + + // protected: + + unsigned int get_sector (gr_complex sample); + + unsigned int calc_sector_value (unsigned int sector); + + // private: + + friend gr_constellation_psk_sptr + gr_make_constellation_psk (std::vector<gr_complex> constellation, unsigned int n_sectors); + +}; + #endif diff --git a/gnuradio-core/src/lib/general/gr_constellation.i b/gnuradio-core/src/lib/general/gr_constellation.i index bdc5436f27..2d15b8b1e3 100644 --- a/gnuradio-core/src/lib/general/gr_constellation.i +++ b/gnuradio-core/src/lib/general/gr_constellation.i @@ -40,19 +40,19 @@ public: gr_constellation_sptr base (); }; -class gr_constellation_sector; -typedef boost::shared_ptr<gr_constellation_sector> gr_constellation_sector_sptr; -%template(gr_constellation_sector_sptr) boost::shared_ptr<gr_constellation_sector>; -%rename(constellation_sector) gr_make_constellation_sector; -gr_constellation_sector_sptr gr_make_constellation_sector(std::vector<gr_complex> constellation_sector, +class gr_constellation_rect; +typedef boost::shared_ptr<gr_constellation_rect> gr_constellation_rect_sptr; +%template(gr_constellation_rect_sptr) boost::shared_ptr<gr_constellation_rect>; +%rename(constellation_rect) gr_make_constellation_rect; +gr_constellation_rect_sptr gr_make_constellation_rect(std::vector<gr_complex> constellation, unsigned int real_sectors, unsigned int imag_sectors, float width_real_sectors, float width_imag_sectors); -%ignore gr_constellation_sector; +%ignore gr_constellation_rect; -class gr_constellation_sector : public gr_constellation +class gr_constellation_rect : public gr_constellation_sector { public: - gr_constellation_sector (std::vector<gr_complex> constellation, + gr_constellation_rect (std::vector<gr_complex> constellation, unsigned int real_sectors, unsigned int imag_sectors, float width_real_sectors, float width_imag_sectors); std::vector<gr_complex> points (); @@ -60,3 +60,29 @@ public: unsigned int bits_per_symbol (); gr_constellation_sptr base (); }; + +class gr_constellation_psk; +typedef boost::shared_ptr<gr_constellation_psk> gr_constellation_psk_sptr; +%template(gr_constellation_psk_sptr) boost::shared_ptr<gr_constellation_psk>; +%rename(constellation_psk) gr_make_constellation_psk; +gr_constellation_psk_sptr gr_make_constellation_psk(std::vector<gr_complex> constellation, + unsigned int n_sectors); +%ignore gr_constellation_psk; + +class gr_constellation_psk : public gr_constellation_sector +{ +public: + gr_constellation_psk (std::vector<gr_complex> constellation, + unsigned int n_sectors); + std::vector<gr_complex> points (); + unsigned int decision_maker (gr_complex sample); + unsigned int bits_per_symbol (); + + gr_constellation_sptr base (); + + unsigned int get_sector (gr_complex sample); + unsigned int calc_sector_value (unsigned int sector); + void find_sector_values (); + unsigned int n_sectors; + std::vector<unsigned int> sector_values; +}; diff --git a/gnuradio-core/src/python/gnuradio/blks2impl/Makefile.am b/gnuradio-core/src/python/gnuradio/blks2impl/Makefile.am index 45d0c628c1..f7e92442f2 100644 --- a/gnuradio-core/src/python/gnuradio/blks2impl/Makefile.am +++ b/gnuradio-core/src/python/gnuradio/blks2impl/Makefile.am @@ -57,6 +57,7 @@ grblkspython_PYTHON = \ pfb_interpolator.py \ pkt.py \ psk.py \ + psk2.py \ qam.py \ rational_resampler.py \ standard_squelch.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 index 90dcac971c..933de91131 100644 --- a/gnuradio-core/src/python/gnuradio/blks2impl/generic_mod_demod.py +++ b/gnuradio-core/src/python/gnuradio/blks2impl/generic_mod_demod.py @@ -25,11 +25,9 @@ Generic modulation and demodulation. """ -from gnuradio import gr, gru, modulation_utils2 -from math import pi, sqrt -import psk -import cmath -from pprint import pprint +from gnuradio import gr +from gnuradio.modulation_utils2 import extract_kwargs_from_options_for_class +from gnuradio.utils.gray_code import gray_code, inverse_gray_code # default values (used in __init__ and add_options) _def_samples_per_symbol = 2 @@ -47,6 +45,27 @@ _def_timing_max_dev = 1.5 _def_phase_alpha = 0.1 # Number of points in constellation _def_constellation_points = 16 +# Whether differential coding is used. +_def_differential = True +_def_gray_coded = True + +def add_common_options(parser): + """ + Sets options common to both modulator and demodulator. + """ + parser.add_option("-p", "--constellation-points", type="int", default=_def_constellation_points, + help="set the number of constellation points (must be a power of 2 (power of 4 for QAM) [default=%default]") + parser.add_option("", "--differential", action="store_true", dest="differential", default=True, + help="use differential encoding [default=%default]") + parser.add_option("", "--not-differential", action="store_false", dest="differential", + help="do not use differential encoding [default=%default]") + parser.add_option("", "--gray-coded", action="store_true", dest="gray_coded", default=True, + help="use gray code [default=%default]") + parser.add_option("", "--not-gray-coded", action="store_false", dest="gray_coded", + help="do not use gray code [default=%default]") + parser.add_option("", "--excess-bw", type="float", default=_def_excess_bw, + help="set RRC excess bandwith factor [default=%default]") + # ///////////////////////////////////////////////////////////////////////////// # Generic modulator @@ -55,6 +74,8 @@ _def_constellation_points = 16 class generic_mod(gr.hier_block2): def __init__(self, constellation, + differential=_def_differential, + gray_coded=_def_gray_coded, samples_per_symbol=_def_samples_per_symbol, excess_bw=_def_excess_bw, verbose=_def_verbose, @@ -84,6 +105,8 @@ class generic_mod(gr.hier_block2): self._constellation = constellation.base() self._samples_per_symbol = samples_per_symbol self._excess_bw = excess_bw + self._differential = differential + self._gray_coded = gray_coded 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) @@ -96,7 +119,11 @@ class generic_mod(gr.hier_block2): self.bytes2chunks = \ gr.packed_to_unpacked_bb(self.bits_per_symbol(), gr.GR_MSB_FIRST) - self.diffenc = gr.diff_encoder_bb(arity) + if gray_coded: + self.symbol_mapper = gr.map_bb(gray_code(arity)) + + if differential: + self.diffenc = gr.diff_encoder_bb(arity) self.chunks2symbols = gr.chunks_to_symbols_bc(self._constellation.points()) @@ -112,8 +139,13 @@ class generic_mod(gr.hier_block2): self.rrc_taps) # Connect - self.connect(self, self.bytes2chunks, self.diffenc, - self.chunks2symbols, self.rrc_filter, self) + blocks = [self, self.bytes2chunks] + if gray_coded: + blocks.append(self.symbol_mapper) + if differential: + blocks.append(self.diffenc) + blocks += [self.chunks2symbols, self.rrc_filter, self] + self.connect(*blocks) if verbose: self._print_verbage() @@ -132,19 +164,15 @@ class generic_mod(gr.hier_block2): """ Adds generic modulation options to the standard parser """ - parser.add_option("-p", "--constellation-points", type="int", default=_def_constellation_points, - help="set the number of constellation points (must be a power of 4 for QAM) [default=%default]") - parser.add_option("", "--excess-bw", type="float", default=_def_excess_bw, - help="set RRC excess bandwith factor [default=%default]") + add_common_options(parser) add_options=staticmethod(add_options) - def extract_kwargs_from_options(options): + def extract_kwargs_from_options(cls, options): """ Given command line options, create dictionary suitable for passing to __init__ """ - return modulation_utils2.extract_kwargs_from_options( - generic_mod.__init__, ('self',), options) - extract_kwargs_from_options=staticmethod(extract_kwargs_from_options) + return extract_kwargs_from_options_for_class(cls, options) + extract_kwargs_from_options=classmethod(extract_kwargs_from_options) def _print_verbage(self): @@ -156,8 +184,12 @@ class generic_mod(gr.hier_block2): 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")) + if self._gray_coded: + self.connect(self.symbol_mapper, + gr.file_sink(gr.sizeof_char, "tx_symbol_mapper.dat")) + if self._differential: + 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, @@ -175,6 +207,8 @@ class generic_demod(gr.hier_block2): def __init__(self, constellation, samples_per_symbol=_def_samples_per_symbol, + differential=_def_differential, + gray_coded=_def_gray_coded, excess_bw=_def_excess_bw, freq_alpha=_def_freq_alpha, timing_alpha=_def_timing_alpha, @@ -221,7 +255,9 @@ class generic_demod(gr.hier_block2): self._timing_alpha = timing_alpha self._timing_beta = _def_timing_beta self._timing_max_dev=timing_max_dev - + self._differential = differential + self._gray_coded = gray_coded + 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) @@ -255,9 +291,13 @@ class generic_demod(gr.hier_block2): self._constellation, self._phase_alpha, self._phase_beta, fmin, fmax) - + # Do differential decoding based on phase change of symbols - self.diffdec = gr.diff_decoder_bb(arity) + if differential: + self.diffdec = gr.diff_decoder_bb(arity) + + if gray_coded: + self.symbol_mapper = gr.map_bb(inverse_gray_code(arity)) # unpack the k bit vector into a stream of bits self.unpack = gr.unpack_k_bits_bb(self.bits_per_symbol()) @@ -269,8 +309,13 @@ class generic_demod(gr.hier_block2): 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) + blocks = [self, self.agc, self.freq_recov, self.time_recov, self.receiver] + if differential: + blocks.append(self.diffdec) + if gray_coded: + blocks.append(self.symbol_mapper) + blocks += [self.unpack, self] + self.connect(*blocks) def samples_per_symbol(self): return self._samples_per_symbol @@ -317,8 +362,12 @@ class generic_demod(gr.hier_block2): gr.file_sink(gr.sizeof_float, "rx_receiver_phase.dat")) self.connect((self.receiver, 3), gr.file_sink(gr.sizeof_float, "rx_receiver_freq.dat")) - self.connect(self.diffdec, - gr.file_sink(gr.sizeof_char, "rx_diffdec.dat")) + if self._differential: + self.connect(self.diffdec, + gr.file_sink(gr.sizeof_char, "rx_diffdec.dat")) + if self._gray_coded: + self.connect(self.symbol_mapper, + gr.file_sink(gr.sizeof_char, "rx_symbol_mapper.dat")) self.connect(self.unpack, gr.file_sink(gr.sizeof_char, "rx_unpack.dat")) @@ -326,10 +375,9 @@ class generic_demod(gr.hier_block2): """ Adds generic demodulation options to the standard parser """ - parser.add_option("-p", "--constellation-points", type="int", default=_def_constellation_points, - help="set the number of constellation points (must be a power of 4 for QAM) [default=%default]") - parser.add_option("", "--excess-bw", type="float", default=_def_excess_bw, - help="set RRC excess bandwith factor [default=%default]") + # Add options shared with modulator. + add_common_options(parser) + # Add options specific to demodulator. parser.add_option("", "--freq-alpha", type="float", default=_def_freq_alpha, help="set frequency lock loop alpha gain value [default=%default]") parser.add_option("", "--phase-alpha", type="float", default=_def_phase_alpha, @@ -342,15 +390,10 @@ class generic_demod(gr.hier_block2): help="set timing symbol sync loop maximum deviation [default=%default]") add_options=staticmethod(add_options) - def extract_kwargs_from_options(options): + def extract_kwargs_from_options(cls, options): """ Given command line options, create dictionary suitable for passing to __init__ """ - return modulation_utils2.extract_kwargs_from_options( - generic_demod.__init__, ('self',), options) - extract_kwargs_from_options=staticmethod(extract_kwargs_from_options) -## -# Add these to the mod/demod registry -# -#modulation_utils2.add_type_1_mod('generic', generic_mod) -#modulation_utils2.add_type_1_demod('generic', generic_demod) + return extract_kwargs_from_options_for_class(cls, options) + extract_kwargs_from_options=classmethod(extract_kwargs_from_options) + diff --git a/gnuradio-core/src/python/gnuradio/blks2impl/psk2.py b/gnuradio-core/src/python/gnuradio/blks2impl/psk2.py new file mode 100644 index 0000000000..4fd2c77fec --- /dev/null +++ b/gnuradio-core/src/python/gnuradio/blks2impl/psk2.py @@ -0,0 +1,100 @@ +# +# 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 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. +# + +""" +PSK modulation and demodulation. +""" + +from math import pi, log +from cmath import exp + +from gnuradio import gr, modulation_utils2 +from gnuradio.blks2impl.generic_mod_demod import generic_mod, generic_demod + +# Default number of points in constellation. +_def_constellation_points = 4 +# Whether differential coding is used. +_def_differential = True + +# ///////////////////////////////////////////////////////////////////////////// +# PSK constellation +# ///////////////////////////////////////////////////////////////////////////// + +def psk_constellation(m=_def_constellation_points): + """ + Creates a PSK constellation object. + """ + k = log(m) / log(2.0) + if (k != int(k)): + raise StandardError('Number of constellation points must be a power of two.') + points = [exp(2*pi*(0+1j)*i/m) for i in range(0,m)] + constellation = gr.constellation_psk(points, m) + return constellation + +# ///////////////////////////////////////////////////////////////////////////// +# PSK modulator +# ///////////////////////////////////////////////////////////////////////////// + +class psk_mod(generic_mod): + + def __init__(self, constellation_points=_def_constellation_points, + *args, **kwargs): + + """ + Hierarchical block for RRC-filtered PSK modulation. + + The input is a byte stream (unsigned char) and the + output is the complex modulated signal at baseband. + + See generic_mod block for list of parameters. + """ + + constellation = psk_constellation(constellation_points) + super(psk_mod, self).__init__(constellation, *args, **kwargs) + +# ///////////////////////////////////////////////////////////////////////////// +# PSK demodulator +# +# ///////////////////////////////////////////////////////////////////////////// + +class psk_demod(generic_demod): + + def __init__(self, constellation_points=_def_constellation_points, + *args, **kwargs): + + """ + Hierarchical block for RRC-filtered PSK modulation. + + The input is a byte stream (unsigned char) and the + output is the complex modulated signal at baseband. + + See generic_demod block for list of parameters. + """ + + constellation = psk_constellation(constellation_points) + super(psk_demod, self).__init__(constellation, *args, **kwargs) + +# +# Add these to the mod/demod registry +# +modulation_utils2.add_type_1_mod('psk', psk_mod) +modulation_utils2.add_type_1_demod('psk', psk_demod) +modulation_utils2.add_type_1_constellation('psk', psk_constellation) diff --git a/gnuradio-core/src/python/gnuradio/blks2impl/qam.py b/gnuradio-core/src/python/gnuradio/blks2impl/qam.py index b6096d2cb9..220a3f62f5 100644 --- a/gnuradio-core/src/python/gnuradio/blks2impl/qam.py +++ b/gnuradio-core/src/python/gnuradio/blks2impl/qam.py @@ -25,12 +25,17 @@ QAM modulation and demodulation. from math import pi, sqrt, log -from gnuradio import gr, gru, modulation_utils2 +from gnuradio import gr, modulation_utils2 from gnuradio.blks2impl.generic_mod_demod import generic_mod, generic_demod from gnuradio.utils.gray_code import gray_code # Default number of points in constellation. _def_constellation_points = 16 +# Whether the quadrant bits are coded differentially. +_def_differential = True +# Whether gray coding is used. If differential is True then gray +# coding is used within but not between each quadrant. +_def_gray_coded = True def is_power_of_four(x): v = log(x)/log(4) @@ -47,13 +52,16 @@ def get_bits(x, n, k): # Remove all bits bigger than n+k-1 return v % pow(2, k) -def make_constellation(m): +def make_differential_constellation(m, gray_coded=_def_gray_coded): """ Create a constellation with m possible symbols where m must be a power of 4. Points are laid out in a square grid. + Bits referring to the quadrant are differentilly encoded, + remaining bits are gray coded. + """ sqrtm = pow(m, 0.5) if (not isinstance(m, int) or m < 4 or not is_power_of_four(m)): @@ -63,10 +71,13 @@ def make_constellation(m): # 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 = gray_code(side) - # Get inverse gray codes. - i_gcs = dict([(v, key) for key, v in enumerate(gcs)]) + if gray_coded: + # Number rows and columns using gray codes. + gcs = gray_code(side) + # Get inverse gray codes. + i_gcs = dict([(v, key) for key, v in enumerate(gcs)]) + else: + i_gcs = dict([(i, i) for i in range(0, side)]) # The distance between points is found. step = 1/(side-0.5) @@ -100,19 +111,51 @@ def make_constellation(m): return const_map +def make_not_differential_constellation(m, gray_coded=_def_gray_coded): + side = 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)) + if gray_coded: + # Number rows and columns using gray codes. + gcs = gray_code(side) + # Get inverse gray codes. + i_gcs = dict([(v, key) for key, v in enumerate(gcs)]) + else: + i_gcs = dict([(i, i) for i in range(0, m)]) + # The distance between points is found. + step = 2/(side-1) + + gc_to_x = [-1 + i_gcs[gc]*step for gc in range(0, side)] + + # First k/2 bits determine x position. + # Following k/2 bits determine y position. + const_map = [] + for i in range(m): + y = gc_to_x(get_bits(i, 0, k/2)) + x = gc_to_x(get_bits(i, k/2, k/2)) + const_map.append(complex(x,y)) + + return const_map # ///////////////////////////////////////////////////////////////////////////// # QAM constellation # ///////////////////////////////////////////////////////////////////////////// -def qam_constellation(constellation_points=_def_constellation_points): +def qam_constellation(constellation_points=_def_constellation_points, + differential=_def_differential, + gray_coded=_def_gray_coded,): """ Creates a QAM constellation object. """ - points = make_constellation(constellation_points) + if differential: + points = make_differential_constellation(constellation_points, gray_coded) + else: + points = make_not_differential_constellation(constellation_points, gray_coded) side = int(sqrt(constellation_points)) width = 2.0/(side-1) - constellation = gr.constellation_sector(points, side, side, width, width) + constellation = gr.constellation_rect(points, side, side, width, width) return constellation # ///////////////////////////////////////////////////////////////////////////// @@ -121,7 +164,10 @@ def qam_constellation(constellation_points=_def_constellation_points): class qam_mod(generic_mod): - def __init__(self, constellation_points=_def_constellation_points, *args, **kwargs): + def __init__(self, constellation_points=_def_constellation_points, + differential=_def_differential, + gray_coded=_def_gray_coded, + *args, **kwargs): """ Hierarchical block for RRC-filtered QAM modulation. @@ -132,10 +178,11 @@ class qam_mod(generic_mod): See generic_mod block for list of parameters. """ - if not isinstance(constellation_points, int) or not is_power_of_four(constellation_points): - raise ValueError("number of constellation points must be a power of four.") - constellation = qam_constellation(constellation_points) - super(qam_mod, self).__init__(constellation, *args, **kwargs) + constellation = qam_constellation(constellation_points, differential, gray_coded) + # We take care of the gray coding in the constellation generation so it doesn't + # need to be done in the block. + super(qam_mod, self).__init__(constellation, differential=differential, + gray_coded=False, *args, **kwargs) # ///////////////////////////////////////////////////////////////////////////// # QAM demodulator @@ -144,7 +191,10 @@ class qam_mod(generic_mod): class qam_demod(generic_demod): - def __init__(self, constellation_points=_def_constellation_points, *args, **kwargs): + def __init__(self, constellation_points=_def_constellation_points, + differential=_def_differential, + gray_coded=_def_gray_coded, + *args, **kwargs): """ Hierarchical block for RRC-filtered QAM modulation. @@ -154,12 +204,18 @@ class qam_demod(generic_demod): See generic_demod block for list of parameters. """ - - constellation = qam_constellation(constellation_points) - super(qam_demod, self).__init__(constellation, *args, **kwargs) + print(args) + print(kwargs) + constellation = qam_constellation(constellation_points, differential, gray_coded) + # We take care of the gray coding in the constellation generation so it doesn't + # need to be done in the block. + super(qam_demod, self).__init__(constellation, differential=differential, + gray_coded=False, + *args, **kwargs) # # Add these to the mod/demod registry # modulation_utils2.add_type_1_mod('qam', qam_mod) modulation_utils2.add_type_1_demod('qam', qam_demod) +modulation_utils2.add_type_1_constellation('qam', qam_constellation) diff --git a/gnuradio-core/src/python/gnuradio/modulation_utils2.py b/gnuradio-core/src/python/gnuradio/modulation_utils2.py index c5dba3e791..f30055f4ac 100644 --- a/gnuradio-core/src/python/gnuradio/modulation_utils2.py +++ b/gnuradio-core/src/python/gnuradio/modulation_utils2.py @@ -47,6 +47,15 @@ def type_1_demods(): def add_type_1_demod(name, demod_class): _type_1_demodulators[name] = demod_class +# Also record the constellation making functions of the modulations +_type_1_constellations = {} + +def type_1_constellations(): + return _type_1_constellations + +def add_type_1_constellation(name, constellation): + _type_1_constellations[name] = constellation + def extract_kwargs_from_options(function, excluded_args, options): """ @@ -79,3 +88,14 @@ def extract_kwargs_from_options(function, excluded_args, options): if getattr(options, kw) is not None: d[kw] = getattr(options, kw) return d + +def extract_kwargs_from_options_for_class(cls, options): + """ + Given command line options, create dictionary suitable for passing to __init__ + """ + d = extract_kwargs_from_options( + cls.__init__, ('self',), options) + for base in cls.__bases__: + if hasattr(base, 'extract_kwargs_from_options'): + d.update(base.extract_kwargs_from_options(options)) + return d diff --git a/gnuradio-core/src/python/gnuradio/utils/gray_code.py b/gnuradio-core/src/python/gnuradio/utils/gray_code.py index af8b8cd147..7d3e0fcb8d 100644 --- a/gnuradio-core/src/python/gnuradio/utils/gray_code.py +++ b/gnuradio-core/src/python/gnuradio/utils/gray_code.py @@ -29,16 +29,23 @@ class GrayCodeGenerator(object): if self.i == self.lp2: # if i is a power of two then gray number is of form 1100000... result = self.i + self.i/2 - else: - # if not we take advantage of the symmetry of all but the last bit - # around a power of two. - result = self.gcs[2*self.lp2-1-self.i] + self.lp2 - self.gcs.append(result) - self.i += 1 - if self.i == self.np2: - self.lp2 = self.i - self.np2 = self.i*2 + else: + # if not we take advantage of the symmetry of all but the last bit + # around a power of two. + result = self.gcs[2*self.lp2-1-self.i] + self.lp2 + self.gcs.append(result) + self.i += 1 + if self.i == self.np2: + self.lp2 = self.i + self.np2 = self.i*2 _gray_code_generator = GrayCodeGenerator() gray_code = _gray_code_generator.get_gray_code + +def inverse_gray_code(length): + gc = enumerate(gray_code(length)) + igc = [(b, a) for (a, b) in gc] + igc.sort() + return [a for (b, a) in igc] + |