summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBen Reynwar <ben@reynwar.net>2011-01-20 14:21:07 -0700
committerBen Reynwar <ben@reynwar.net>2011-01-20 14:21:07 -0700
commitc23d8a22a8b7859e83d95bd0b439229876c2d5b0 (patch)
tree1ce54260e2998bbfa6dc650205f8577732254dc7
parent1ec42d6fa313c6d3eeefae783a74a8600da3b76e (diff)
Added support for PSK to generic modulation.
-rw-r--r--gnuradio-core/src/lib/general/gr_constellation.cc96
-rw-r--r--gnuradio-core/src/lib/general/gr_constellation.h115
-rw-r--r--gnuradio-core/src/lib/general/gr_constellation.i42
-rw-r--r--gnuradio-core/src/python/gnuradio/blks2impl/Makefile.am1
-rw-r--r--gnuradio-core/src/python/gnuradio/blks2impl/generic_mod_demod.py119
-rw-r--r--gnuradio-core/src/python/gnuradio/blks2impl/psk2.py100
-rw-r--r--gnuradio-core/src/python/gnuradio/blks2impl/qam.py92
-rw-r--r--gnuradio-core/src/python/gnuradio/modulation_utils2.py20
-rw-r--r--gnuradio-core/src/python/gnuradio/utils/gray_code.py25
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]
+