summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTom Rondeau <trondeau@vt.edu>2013-03-06 17:18:37 -0500
committerTom Rondeau <trondeau@vt.edu>2013-03-06 17:18:37 -0500
commit5c18bac20fcbdee03c332e29a676ea8f1481b5ea (patch)
tree7988441a1b098d336314bdc8e74979553c7d2d53
parent9ac143a654bc581d9a74363fc02e1ad30be93138 (diff)
parent64d8f82c531a477262304bf93b98c2de4eb83d35 (diff)
Merge branch 'master' into next
Conflicts: gr-digital/include/digital_constellation.h gr-digital/lib/digital_constellation.cc gr-digital/python/qa_constellation.py gr-digital/python/qa_constellation_receiver.py gr-digital/python/qam.py gr-digital/swig/digital_constellation.i
-rw-r--r--gr-blocks/grc/blocks_block_tree.xml1
-rw-r--r--gr-blocks/grc/blocks_tag_debug.xml82
-rw-r--r--gr-blocks/include/blocks/CMakeLists.txt1
-rw-r--r--gr-blocks/include/blocks/tag_debug.h80
-rw-r--r--gr-blocks/lib/CMakeLists.txt1
-rw-r--r--gr-blocks/lib/tag_debug_impl.cc118
-rw-r--r--gr-blocks/lib/tag_debug_impl.h58
-rwxr-xr-xgr-blocks/python/qa_tag_debug.py44
-rw-r--r--gr-blocks/swig/blocks_swig.i3
-rw-r--r--gr-digital/python/CMakeLists.txt1
-rwxr-xr-xgr-digital/python/qa_constellation.py55
-rwxr-xr-xgr-digital/python/qa_constellation_receiver.py57
-rw-r--r--gr-digital/python/qam.py152
-rw-r--r--gr-digital/python/qamlike.py75
14 files changed, 675 insertions, 53 deletions
diff --git a/gr-blocks/grc/blocks_block_tree.xml b/gr-blocks/grc/blocks_block_tree.xml
index 62a5610a0c..c316be2001 100644
--- a/gr-blocks/grc/blocks_block_tree.xml
+++ b/gr-blocks/grc/blocks_block_tree.xml
@@ -41,6 +41,7 @@
<name>Sinks (New)</name>
<block>blocks_file_meta_sink</block>
<block>blocks_tagged_stream_to_pdu</block>
+ <block>blocks_tag_debug</block>
<block>blocks_message_sink</block>
</cat>
<cat>
diff --git a/gr-blocks/grc/blocks_tag_debug.xml b/gr-blocks/grc/blocks_tag_debug.xml
new file mode 100644
index 0000000000..929e53afed
--- /dev/null
+++ b/gr-blocks/grc/blocks_tag_debug.xml
@@ -0,0 +1,82 @@
+<?xml version="1.0"?>
+<!--
+###################################################
+## Tag Debug
+###################################################
+ -->
+<block>
+ <name>Tag Debug</name>
+ <key>blocks_tag_debug</key>
+ <import>from gnuradio import blocks</import>
+ <make>blocks.tag_debug($type.size*$vlen, $name)</make>
+ <callback>set_display($display)</callback>
+ <param>
+ <name>Input Type</name>
+ <key>type</key>
+ <type>enum</type>
+ <option>
+ <name>Complex</name>
+ <key>complex</key>
+ <opt>size:gr.sizeof_gr_complex</opt>
+ </option>
+ <option>
+ <name>Float</name>
+ <key>float</key>
+ <opt>size:gr.sizeof_float</opt>
+ </option>
+ <option>
+ <name>Int</name>
+ <key>int</key>
+ <opt>size:gr.sizeof_int</opt>
+ </option>
+ <option>
+ <name>Short</name>
+ <key>short</key>
+ <opt>size:gr.sizeof_short</opt>
+ </option>
+ <option>
+ <name>Byte</name>
+ <key>byte</key>
+ <opt>size:gr.sizeof_char</opt>
+ </option>
+ </param>
+ <param>
+ <name>Name</name>
+ <key>name</key>
+ <type>string</type>
+ </param>
+ <param>
+ <name>Num Inputs</name>
+ <key>num_inputs</key>
+ <value>1</value>
+ <type>int</type>
+ </param>
+ <param>
+ <name>Vec Length</name>
+ <key>vlen</key>
+ <value>1</value>
+ <type>int</type>
+ </param>
+ <param>
+ <name>Display</name>
+ <key>display</key>
+ <value>True</value>
+ <type>bool</type>
+ <option>
+ <name>On</name>
+ <key>True</key>
+ </option>
+ <option>
+ <name>Off</name>
+ <key>False</key>
+ </option>
+ </param>
+ <check>$num_inputs &gt;= 1</check>
+ <check>$vlen &gt; 0</check>
+ <sink>
+ <name>in</name>
+ <type>$type</type>
+ <vlen>$vlen</vlen>
+ <nports>$num_inputs</nports>
+ </sink>
+</block>
diff --git a/gr-blocks/include/blocks/CMakeLists.txt b/gr-blocks/include/blocks/CMakeLists.txt
index 0da7a0e569..48beaad6e9 100644
--- a/gr-blocks/include/blocks/CMakeLists.txt
+++ b/gr-blocks/include/blocks/CMakeLists.txt
@@ -157,6 +157,7 @@ install(FILES
streams_to_stream.h
streams_to_vector.h
stretch_ff.h
+ tag_debug.h
tagged_stream_to_pdu.h
threshold_ff.h
throttle.h
diff --git a/gr-blocks/include/blocks/tag_debug.h b/gr-blocks/include/blocks/tag_debug.h
new file mode 100644
index 0000000000..9d27a26ecd
--- /dev/null
+++ b/gr-blocks/include/blocks/tag_debug.h
@@ -0,0 +1,80 @@
+/* -*- c++ -*- */
+/*
+ * Copyright 2012-2013 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_TAG_DEBUG_H
+#define INCLUDED_GR_TAG_DEBUG_H
+
+#include <blocks/api.h>
+#include <gr_sync_block.h>
+
+namespace gr {
+ namespace blocks {
+
+ /*!
+ * \brief Bit bucket that prints out any tag received.
+ * \ingroup sink_blk
+ *
+ * This block collects all tags sent to it on all input ports and
+ * displays them to stdout in a formatted way. The \p name
+ * parameter is used to identify which debug sink generated the
+ * tag, so when connecting a block to this debug sink, an
+ * appropriate name is something that identifies the input block.
+ *
+ * This block otherwise acts as a NULL sink in that items from the
+ * input stream are ignored. It is designed to be able to attach
+ * to any block and watch all tags streaming out of that block for
+ * debugging purposes.
+ *
+ * The tags from the last call to this work function are stored
+ * and can be retrieved using the function 'current_tags'.
+ */
+ class BLOCKS_API tag_debug : virtual public gr_sync_block
+ {
+ public:
+ // gr::blocks::tag_debug::sptr
+ typedef boost::shared_ptr<tag_debug> sptr;
+
+ /*!
+ * Build a tag debug block
+ *
+ * \param sizeof_stream_item size of the items in the incoming stream.
+ * \param name name to identify which debug sink generated the info.
+ */
+ static sptr make(size_t sizeof_stream_item,
+ const std::string &name);
+
+ /*!
+ * \brief Returns a vector of gr_tag_t items as of the last call to
+ * work.
+ */
+ virtual std::vector<gr_tag_t> current_tags() = 0;
+
+ /*!
+ * \brief Set the display of tags to stdout on/off.
+ */
+ virtual void set_display(bool d) = 0;
+ };
+
+ } /* namespace blocks */
+} /* namespace gr */
+
+#endif /* INCLUDED_GR_TAG_DEBUG_H */
diff --git a/gr-blocks/lib/CMakeLists.txt b/gr-blocks/lib/CMakeLists.txt
index d84d79de7d..fb52508003 100644
--- a/gr-blocks/lib/CMakeLists.txt
+++ b/gr-blocks/lib/CMakeLists.txt
@@ -185,6 +185,7 @@ list(APPEND gr_blocks_sources
pack_k_bits_bb_impl.cc
patterned_interleaver_impl.cc
pdu.cc
+ tag_debug_impl.cc
pdu_to_tagged_stream_impl.cc
peak_detector2_fb_impl.cc
random_pdu_impl.cc
diff --git a/gr-blocks/lib/tag_debug_impl.cc b/gr-blocks/lib/tag_debug_impl.cc
new file mode 100644
index 0000000000..a216879637
--- /dev/null
+++ b/gr-blocks/lib/tag_debug_impl.cc
@@ -0,0 +1,118 @@
+/* -*- c++ -*- */
+/*
+ * Copyright 2012-2013 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 "tag_debug_impl.h"
+#include <gr_io_signature.h>
+#include <iostream>
+#include <iomanip>
+
+namespace gr {
+ namespace blocks {
+
+ tag_debug::sptr
+ tag_debug::make(size_t sizeof_stream_item,
+ const std::string &name)
+ {
+ return gnuradio::get_initial_sptr
+ (new tag_debug_impl(sizeof_stream_item, name));
+ }
+
+ tag_debug_impl::tag_debug_impl(size_t sizeof_stream_item,
+ const std::string &name)
+ : gr_sync_block("tag_debug",
+ gr_make_io_signature(1, -1, sizeof_stream_item),
+ gr_make_io_signature(0, 0, 0)),
+ d_name(name), d_display(true)
+ {
+ }
+
+ tag_debug_impl::~tag_debug_impl()
+ {
+ }
+
+ std::vector<gr_tag_t>
+ tag_debug_impl::current_tags()
+ {
+ gruel::scoped_lock l(d_mutex);
+ return d_tags;
+ }
+
+ void
+ tag_debug_impl::set_display(bool d)
+ {
+ gruel::scoped_lock l(d_mutex);
+ d_display = d;
+ }
+
+ int
+ tag_debug_impl::work(int noutput_items,
+ gr_vector_const_void_star &input_items,
+ gr_vector_void_star &output_items)
+ {
+ gruel::scoped_lock l(d_mutex);
+
+ std::stringstream sout;
+ if(d_display) {
+ sout << std::endl
+ << "----------------------------------------------------------------------";
+ sout << std::endl << "Tag Debug: " << d_name << std::endl;
+ }
+
+ uint64_t abs_N, end_N;
+ for(size_t i = 0; i < input_items.size(); i++) {
+ abs_N = nitems_read(i);
+ end_N = abs_N + (uint64_t)(noutput_items);
+
+ d_tags.clear();
+ get_tags_in_range(d_tags, i, abs_N, end_N);
+
+ if(d_display) {
+ sout << "Input Stream: " << std::setw(2) << std::setfill('0')
+ << i << std::setfill(' ') << std::endl;
+ for(d_tags_itr = d_tags.begin(); d_tags_itr != d_tags.end(); d_tags_itr++) {
+ sout << std::setw(10) << "Offset: " << d_tags_itr->offset
+ << std::setw(10) << "Source: "
+ << (pmt::pmt_is_symbol(d_tags_itr->srcid) ? pmt::pmt_symbol_to_string(d_tags_itr->srcid) : "n/a")
+ << std::setw(10) << "Key: " << pmt::pmt_symbol_to_string(d_tags_itr->key)
+ << std::setw(10) << "Value: ";
+ sout << d_tags_itr->value << std::endl;
+ }
+ }
+ }
+
+ if(d_display) {
+ sout << "----------------------------------------------------------------------";
+ sout << std::endl;
+
+ if(d_tags.size() > 0)
+ std::cout << sout.str();
+ }
+
+ return noutput_items;
+ }
+
+ } /* namespace blocks */
+} /* namespace gr */
diff --git a/gr-blocks/lib/tag_debug_impl.h b/gr-blocks/lib/tag_debug_impl.h
new file mode 100644
index 0000000000..988d0e1103
--- /dev/null
+++ b/gr-blocks/lib/tag_debug_impl.h
@@ -0,0 +1,58 @@
+/* -*- c++ -*- */
+/*
+ * Copyright 2012-2013 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_TAG_DEBUG_IMPL_H
+#define INCLUDED_GR_TAG_DEBUG_IMPL_H
+
+#include <blocks/tag_debug.h>
+#include <gruel/thread.h>
+#include <stddef.h>
+
+namespace gr {
+ namespace blocks {
+
+ class tag_debug_impl : public tag_debug
+ {
+ private:
+ std::string d_name;
+ std::vector<gr_tag_t> d_tags;
+ std::vector<gr_tag_t>::iterator d_tags_itr;
+ bool d_display;
+ gruel::mutex d_mutex;
+
+ public:
+ tag_debug_impl(size_t sizeof_stream_item, const std::string &name);
+ ~tag_debug_impl();
+
+ std::vector<gr_tag_t> current_tags();
+
+ void set_display(bool d);
+
+ int work(int noutput_items,
+ gr_vector_const_void_star &input_items,
+ gr_vector_void_star &output_items);
+ };
+
+ } /* namespace blocks */
+} /* namespace gr */
+
+#endif /* INCLUDED_GR_TAG_DEBUG_IMPL_H */
diff --git a/gr-blocks/python/qa_tag_debug.py b/gr-blocks/python/qa_tag_debug.py
new file mode 100755
index 0000000000..ad85daebcc
--- /dev/null
+++ b/gr-blocks/python/qa_tag_debug.py
@@ -0,0 +1,44 @@
+#!/usr/bin/env python
+#
+# Copyright 2012-2013 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.
+#
+
+from gnuradio import gr, gr_unittest
+import blocks_swig as blocks
+
+class test_tag_debug(gr_unittest.TestCase):
+
+ def setUp(self):
+ self.tb = gr.top_block()
+
+ def tearDown(self):
+ self.tb = None
+
+ def test_001(self):
+ # Just run some data through and make sure it doesn't puke.
+ src_data = (1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
+ src = gr.vector_source_i(src_data)
+ op = blocks.tag_debug(gr.sizeof_int, "tag QA")
+ self.tb.connect(src, op)
+ self.tb.run()
+ x = op.current_tags()
+
+if __name__ == '__main__':
+ gr_unittest.run(test_tag_debug, "test_tag_debug.xml")
diff --git a/gr-blocks/swig/blocks_swig.i b/gr-blocks/swig/blocks_swig.i
index 560cd87802..867dd5e855 100644
--- a/gr-blocks/swig/blocks_swig.i
+++ b/gr-blocks/swig/blocks_swig.i
@@ -163,6 +163,7 @@
#include "blocks/sub_ss.h"
#include "blocks/sub_ii.h"
#include "blocks/sub_cc.h"
+#include "blocks/tag_debug.h"
#include "blocks/tagged_stream_to_pdu.h"
#include "blocks/threshold_ff.h"
#include "blocks/throttle.h"
@@ -285,6 +286,7 @@
%include "blocks/packed_to_unpacked_ss.h"
%include "blocks/packed_to_unpacked_ii.h"
%include "blocks/patterned_interleaver.h"
+%include "blocks/tag_debug.h"
%include "blocks/pdu_to_tagged_stream.h"
%include "blocks/peak_detector_fb.h"
%include "blocks/peak_detector_ib.h"
@@ -460,6 +462,7 @@ GR_SWIG_BLOCK_MAGIC2(blocks, sub_ff);
GR_SWIG_BLOCK_MAGIC2(blocks, sub_ss);
GR_SWIG_BLOCK_MAGIC2(blocks, sub_ii);
GR_SWIG_BLOCK_MAGIC2(blocks, sub_cc);
+GR_SWIG_BLOCK_MAGIC2(blocks, tag_debug);
GR_SWIG_BLOCK_MAGIC2(blocks, tagged_stream_to_pdu);
GR_SWIG_BLOCK_MAGIC2(blocks, threshold_ff);
GR_SWIG_BLOCK_MAGIC2(blocks, throttle);
diff --git a/gr-digital/python/CMakeLists.txt b/gr-digital/python/CMakeLists.txt
index c44248d54c..7f35e3c2be 100644
--- a/gr-digital/python/CMakeLists.txt
+++ b/gr-digital/python/CMakeLists.txt
@@ -43,6 +43,7 @@ GR_PYTHON_INSTALL(
pkt.py
psk.py
qam.py
+ qamlike.py
qpsk.py
DESTINATION ${GR_PYTHON_DIR}/gnuradio/digital
COMPONENT "digital_python"
diff --git a/gr-digital/python/qa_constellation.py b/gr-digital/python/qa_constellation.py
index 37112f70a3..a593c3ea3e 100755
--- a/gr-digital/python/qa_constellation.py
+++ b/gr-digital/python/qa_constellation.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python
#
-# Copyright 2011 Free Software Foundation, Inc.
+# Copyright 2011,2013 Free Software Foundation, Inc.
#
# This file is part of GNU Radio
#
@@ -31,6 +31,7 @@ import blocks_swig as blocks
# import from local folder
import psk
import qam
+import qamlike
tested_mod_codes = (mod_codes.NO_CODE, mod_codes.GRAY_CODE)
@@ -65,11 +66,14 @@ def threed_constell():
dim = 3
return digital.constellation_calcdist(points, [], rot_sym, dim)
-tested_constellation_info = (
+# A list of tuples for constellation testing. The contents of the
+# tuples are (constructor, poss_args, differential, diff_argname).
+
+# These constellations should lock on well.
+easy_constellation_info = (
(psk.psk_constellation,
- {'m': (2, 4, 8, 16, 32, 64),
- 'mod_code': tested_mod_codes,
- 'differential': (True,)},
+ {'m': (2, 4, 8, 16, ),
+ 'mod_code': tested_mod_codes, },
True, None),
(psk.psk_constellation,
{'m': (2, 4, 8, 16, 32, 64),
@@ -77,9 +81,9 @@ tested_constellation_info = (
'differential': (False,)},
False, None),
(qam.qam_constellation,
- {'constellation_points': (4, 16, 64),
+ {'constellation_points': (4,),
'mod_code': tested_mod_codes,
- 'differential': (True,)},
+ 'large_ampls_to_corners': [False],},
True, None),
(qam.qam_constellation,
{'constellation_points': (4, 16, 64),
@@ -94,11 +98,44 @@ tested_constellation_info = (
(threed_constell, {}, True, None),
)
-def tested_constellations():
+# These constellations don't work nicely.
+# We have a lower required error rate.
+medium_constellation_info = (
+ (psk.psk_constellation,
+ {'m': (32, 64),
+ 'mod_code': tested_mod_codes, },
+ True, None),
+ (qam.qam_constellation,
+ {'constellation_points': (16 ,),
+ 'mod_code': tested_mod_codes,
+ 'large_ampls_to_corners': [False, True],},
+ True, None),
+ (qamlike.qam32_holeinside_constellation,
+ {'large_ampls_to_corners': [True]},
+ True, None),
+)
+
+# These constellation are basically broken in our test
+difficult_constellation_info = (
+ (qam.qam_constellation,
+ {'constellation_points': (64,),
+ 'mod_code': tested_mod_codes,
+ 'large_ampls_to_corners': [False, True],},
+ True, None),
+)
+
+def tested_constellations(easy=True, medium=True, difficult=True):
"""
Generator to produce (constellation, differential) tuples for testing purposes.
"""
- for constructor, poss_args, differential, diff_argname in tested_constellation_info:
+ constellation_info = []
+ if easy:
+ constellation_info += easy_constellation_info
+ if medium:
+ constellation_info += medium_constellation_info
+ if difficult:
+ constellation_info += difficult_constellation_info
+ for constructor, poss_args, differential, diff_argname in constellation_info:
if differential:
diff_poss = (True, False)
else:
diff --git a/gr-digital/python/qa_constellation_receiver.py b/gr-digital/python/qa_constellation_receiver.py
index ff0154d740..bc44220ea9 100755
--- a/gr-digital/python/qa_constellation_receiver.py
+++ b/gr-digital/python/qa_constellation_receiver.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python
#
-# Copyright 2011 Free Software Foundation, Inc.
+# Copyright 2011,2013 Free Software Foundation, Inc.
#
# This file is part of GNU Radio
#
@@ -42,7 +42,9 @@ SEED = 1239
# We need this many to let the frequency recovery block converge.
DATA_LENGTH = 1000
# Test fails if fraction of output that is correct is less than this.
-REQ_CORRECT = 0.7
+EASY_REQ_CORRECT = 0.9
+# For constellations that aren't expected to work so well.
+MEDIUM_REQ_CORRECT = 0.8
# CHANNEL PARAMETERS
NOISE_VOLTAGE = 0.01
@@ -103,30 +105,33 @@ class test_constellation_receiver(gr_unittest.TestCase):
self.indices = alignment.random_sample(
self.max_data_length, self.max_num_samples, SEED)
- for constellation, differential in tested_constellations():
- # The constellation_receiver doesn't work for constellations
- # of multple dimensions (i.e. multiple complex numbers to a
- # single symbol).
- # That is not implemented since the receiver has no way of
- # knowing where the beginning of a symbol is.
- # It also doesn't work for non-differential modulation.
- if constellation.dimensionality() != 1:
- continue
- data_length = DATA_LENGTH * constellation.bits_per_symbol()
- if differential:
- freq_offset=True
- else:
- freq_offset=False
- tb = rec_test_tb(constellation, differential,
- src_data=self.src_data[:data_length],
- freq_offset=freq_offset)
- tb.run()
- data = tb.dst.data()
- d1 = tb.src_data[:int(len(tb.src_data)*self.ignore_fraction)]
- d2 = data[:int(len(data)*self.ignore_fraction)]
- correct, overlap, offset, indices = alignment.align_sequences(
- d1, d2, indices=self.indices)
- self.assertTrue(correct > REQ_CORRECT)
+ requirements = (
+ (EASY_REQ_CORRECT, tested_constellations(easy=True, medium=False, difficult=False)),
+ (MEDIUM_REQ_CORRECT, tested_constellations(easy=False, medium=True, difficult=False)),
+ )
+ for req_correct, tcs in requirements:
+ for constellation, differential in tcs:
+ # The constellation_receiver doesn't work for constellations
+ # of multple dimensions (i.e. multiple complex numbers to a
+ # single symbol).
+ # That is not implemented since the receiver has no way of
+ # knowing where the beginning of a symbol is.
+ # It also doesn't work for non-differential modulation.
+ if constellation.dimensionality() != 1 or not differential:
+ continue
+ data_length = DATA_LENGTH * constellation.bits_per_symbol()
+ tb = rec_test_tb(constellation, differential,
+ src_data=self.src_data[:data_length])
+ tb.run()
+ data = tb.dst.data()
+ d1 = tb.src_data[:int(len(tb.src_data)*self.ignore_fraction)]
+ d2 = data[:int(len(data)*self.ignore_fraction)]
+ correct, overlap, offset, indices = alignment.align_sequences(
+ d1, d2, indices=self.indices)
+ if correct <= req_correct:
+ print("Constellation is {0}. Differential is {1}. Required correct is {2}. Correct is {3}. FAIL.".
+ format(constellation, differential, req_correct, correct))
+ self.assertTrue(correct > req_correct)
class rec_test_tb(gr.top_block):
diff --git a/gr-digital/python/qam.py b/gr-digital/python/qam.py
index 8584c59c6f..5919839186 100644
--- a/gr-digital/python/qam.py
+++ b/gr-digital/python/qam.py
@@ -1,5 +1,5 @@
#
-# Copyright 2005,2006,2011 Free Software Foundation, Inc.
+# Copyright 2005,2006,2011,2013 Free Software Foundation, Inc.
#
# This file is part of GNU Radio
#
@@ -147,9 +147,17 @@ def make_non_differential_constellation(m, gray_coded):
def qam_constellation(constellation_points=_def_constellation_points,
differential=_def_differential,
- mod_code=_def_mod_code):
+ mod_code=_def_mod_code,
+ large_ampls_to_corners=False):
"""
Creates a QAM constellation object.
+
+ If large_ampls_to_corners=True then sectors that are probably
+ occupied due to a phase offset, are not mapped to the closest
+ constellation point. Rather we take into account the fact that a
+ phase offset is probably the problem and map them to the closest
+ corner point. It's a bit hackish but it seems to improve
+ frequency locking.
"""
if mod_code == mod_codes.GRAY_CODE:
gray_coded = True
@@ -163,19 +171,90 @@ def qam_constellation(constellation_points=_def_constellation_points,
points = make_non_differential_constellation(constellation_points, gray_coded)
side = int(sqrt(constellation_points))
width = 2.0/(side-1)
- # For differential and gray-coded then gray-code the first two
- # bits with a pre_diff_code.
- # FIXME: It would be good to have a test to make sure that gray-coded constellations
- # are really gray-coded. Perhaps by checking on the correlation between bit-errors.
- if differential and gray_coded:
- m = constellation_points
- pre_diff_code = range(0, m/2) + range(3*m/4, m) + range(m/2, 3*m/4)
+
+ # No pre-diff code
+ # Should add one so that we can gray-code the quadrant bits too.
+ pre_diff_code = []
+ if not large_ampls_to_corners:
+ constellation = digital_swig.constellation_rect(points, pre_diff_code, 4,
+ side, side, width, width)
else:
- pre_diff_code = []
- constellation = digital.constellation_rect(points, pre_diff_code, 4,
- side, side, width, width)
+ sector_values = large_ampls_to_corners_mapping(side, points, width)
+ constellation = digital_swig.constellation_expl_rect(
+ points, pre_diff_code, 4, side, side, width, width, sector_values)
+
return constellation
+def find_closest_point(p, qs):
+ """
+ Return in index of the closest point in 'qs' to 'p'.
+ """
+ min_dist = None
+ min_i = None
+ for i, q in enumerate(qs):
+ dist = abs(q-p)
+ if min_dist is None or dist < min_dist:
+ min_dist = dist
+ min_i = i
+ return min_i
+
+def large_ampls_to_corners_mapping(side, points, width):
+ """
+ We have a grid that we use for decision making. One additional row/column
+ is placed on each side of the grid. Points in these additional rows/columns
+ are mapped to the corners rather than the closest constellation points.
+
+ Args:
+ side: The number of rows/columns in the grid that we use to do
+ decision making.
+ points: The list of constellation points.
+ width: The width of the rows/columns.
+
+ Returns:
+ sector_values maps the sector index to the constellation
+ point index.
+ """
+ # First find the indices of the corner points.
+ # Assume the corner points are the 4 points with the largest magnitudes.
+ corner_indices = []
+ corner_points = []
+ max_mag = 0
+ for i, p in enumerate(points):
+ if abs(p) > max_mag:
+ corner_indices = [i]
+ corner_points = [p]
+ max_mag = abs(p)
+ elif abs(p) == max_mag:
+ corner_indices.append(i)
+ corner_points.append(p)
+ if len(corner_indices) != 4:
+ raise ValueError("Found {0} corner indices. Expected 4."
+ .format(len(corner_indices)))
+ # We want an additional layer around the constellation
+ # Value in this extra layer will be mapped to the closest corner rather
+ # than the closest constellation point.
+ extra_layers = 1
+ side = side + extra_layers*2
+ # Calculate sector values
+ sector_values = []
+ for real_x in range(side):
+ for imag_x in range(side):
+ sector = real_x * side + imag_x
+ # If this sector is a normal constellation sector then
+ # use the center point.
+ c = ((real_x-side/2.0+0.5)*width +
+ (imag_x-side/2.0+0.5)*width*1j)
+ if (real_x >= extra_layers and real_x < side-extra_layers
+ and imag_x >= extra_layers and imag_x < side-extra_layers):
+ # This is not an edge row/column. Find closest point.
+ index = find_closest_point(c, points)
+ else:
+ # This is an edge. Find closest corner point.
+ index = corner_indices[find_closest_point(c, corner_points)]
+ sector_values.append(index)
+ return sector_values
+
+
# /////////////////////////////////////////////////////////////////////////////
# QAM modulator
# /////////////////////////////////////////////////////////////////////////////
@@ -200,9 +279,25 @@ class qam_mod(generic_mod):
mod_code=_def_mod_code,
*args, **kwargs):
- constellation = qam_constellation(constellation_points, differential, mod_code)
- # We take care of the gray coding in the constellation generation so it doesn't
- # need to be done in the block.
+ """
+ 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.
+
+ Args:
+ constellation_points: Number of constellation points.
+ Must be a power of 4.
+ mod_code: Specifies an encoding to use (typically used to indicated
+ if we want gray coding, see digital.utils.mod_codes)
+
+ See generic_mod block for list of additional parameters.
+ """
+
+ constellation = qam_constellation(constellation_points, differential,
+ mod_code)
+ # 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,
*args, **kwargs)
@@ -229,10 +324,31 @@ class qam_demod(generic_demod):
def __init__(self, constellation_points=_def_constellation_points,
differential=_def_differential,
mod_code=_def_mod_code,
+ large_ampls_to_corner = False,
*args, **kwargs):
- constellation = qam_constellation(constellation_points, differential, mod_code)
- # We take care of the gray coding in the constellation generation so it doesn't
- # need to be done in the block.
+ """
+ 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.
+
+ Args:
+ constellation_points: Number of constellation points.
+ Must be a power of 4.
+ mod_code: Specifies an encoding to use (typically used to indicated
+ if we want gray coding, see digital.utils.mod_codes)
+ large_ampls_to_corners: If this is set to True then when the
+ constellation is making decisions, points that are far outside
+ the constellation are mapped to the closest corner rather than
+ the closet constellation point. This can help with phase
+ locking.
+
+ See generic_demod block for list of additional parameters.
+ """
+ constellation = qam_constellation(constellation_points, differential,
+ mod_code)
+ # 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,
*args, **kwargs)
diff --git a/gr-digital/python/qamlike.py b/gr-digital/python/qamlike.py
new file mode 100644
index 0000000000..2f8c855339
--- /dev/null
+++ b/gr-digital/python/qamlike.py
@@ -0,0 +1,75 @@
+# Copyright 2013 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.
+#
+
+"""
+This file contains constellations that are similar to QAM, but are not perfect squares.
+"""
+
+import digital_swig
+from qam import large_ampls_to_corners_mapping
+
+def qam32_holeinside_constellation(large_ampls_to_corners=False):
+ # First make constellation for one quadrant.
+ # 0 1 2
+ # 2 - 010 111 110
+ # 1 - 011 101 100
+ # 0 - 000 001
+
+ # Have put hole in the side rather than corner.
+ # Corner point is helpful for frequency locking.
+
+ # It has an attempt at some gray-coding, but not
+ # a very good one.
+
+ # Indices are (horizontal, vertical).
+ indices_and_numbers = (
+ ((0, 0), 0b000),
+ ((0, 1), 0b011),
+ ((0, 2), 0b010),
+ ((1, 0), 0b001),
+ ((1, 1), 0b101),
+ ((1, 2), 0b111),
+ ((2, 1), 0b100),
+ ((2, 2), 0b110),
+ )
+ points = [None]*32
+ for indices, number in indices_and_numbers:
+ p_in_quadrant = 0.5+indices[0] + 1j*(0.5+indices[1])
+ for quadrant in range(4):
+ index = number + 8 * quadrant
+ rotation = pow(1j, quadrant)
+ p = p_in_quadrant * rotation
+ points[index] = p
+ side = 6
+ width = 1
+ # Double number of boxes on side
+ # This is so that points in the 'hole' get assigned correctly.
+ side = 12
+ width = 0.5
+ pre_diff_code = []
+ if not large_ampls_to_corners:
+ constellation = digital_swig.constellation_rect(points, pre_diff_code, 4,
+ side, side, width, width)
+ else:
+ sector_values = large_ampls_to_corners_mapping(side, points, width)
+ constellation = digital_swig.constellation_expl_rect(
+ points, pre_diff_code, 4, side, side, width, width, sector_values)
+ return constellation
+