summaryrefslogtreecommitdiff
path: root/gr-digital/python/digital/qa_constellation.py
diff options
context:
space:
mode:
authorTom Rondeau <tom@trondeau.com>2014-12-02 15:11:05 -0500
committerTom Rondeau <tom@trondeau.com>2014-12-03 19:02:26 -0500
commit57ee0e7e122668a2b7f628ecb84fe1f90f9d2d11 (patch)
tree4a2bee2978248f55c9f3c14cdb68d0bb953950ae /gr-digital/python/digital/qa_constellation.py
parent92ac02f7fd7289d11fa6523e0491c6a6d8c3cb56 (diff)
digital: fixes issues with the constellation soft decoder, specifically how the decisions are calculated in the C++ code and some issues with the QAM16 constellation in particular.
This addresses issue #737. The patch attached to that issue is not actually valid and is only an ordering problem/confusion. I will be adding an example GRC flowgraph that compares the output of the hard decision and soft decision versions to the original input stream to show how they match. Increased testing coverage in the QA to test certain known points as well as random samples.
Diffstat (limited to 'gr-digital/python/digital/qa_constellation.py')
-rw-r--r--gr-digital/python/digital/qa_constellation.py107
1 files changed, 78 insertions, 29 deletions
diff --git a/gr-digital/python/digital/qa_constellation.py b/gr-digital/python/digital/qa_constellation.py
index 00248f4f09..42e49bb059 100644
--- a/gr-digital/python/digital/qa_constellation.py
+++ b/gr-digital/python/digital/qa_constellation.py
@@ -1,26 +1,26 @@
#!/usr/bin/env python
#
# Copyright 2011,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.
-#
+#
-import random
+import random, math
from cmath import exp, pi, log, sqrt
from gnuradio import gr, gr_unittest, digital, blocks
@@ -40,7 +40,7 @@ tested_mod_codes = (mod_codes.NO_CODE, mod_codes.GRAY_CODE)
def twod_constell():
"""
-
+
"""
points = ((1+0j), (0+1j),
(-1+0j), (0-1j))
@@ -76,12 +76,12 @@ easy_constellation_info = (
False, None),
(qam.qam_constellation,
{'constellation_points': (4,),
- 'mod_code': tested_mod_codes,
+ 'mod_code': tested_mod_codes,
'large_ampls_to_corners': [False],},
True, None),
(qam.qam_constellation,
{'constellation_points': (4, 16, 64),
- 'mod_code': tested_mod_codes,
+ 'mod_code': tested_mod_codes,
'differential': (False,)},
False, None),
(digital.constellation_bpsk, {}, True, None),
@@ -101,7 +101,7 @@ medium_constellation_info = (
True, None),
(qam.qam_constellation,
{'constellation_points': (16 ,),
- 'mod_code': tested_mod_codes,
+ 'mod_code': tested_mod_codes,
'large_ampls_to_corners': [False, True],},
True, None),
(qamlike.qam32_holeinside_constellation,
@@ -113,11 +113,20 @@ medium_constellation_info = (
difficult_constellation_info = (
(qam.qam_constellation,
{'constellation_points': (64,),
- 'mod_code': tested_mod_codes,
+ 'mod_code': tested_mod_codes,
'large_ampls_to_corners': [False, True],},
- True, None),
+ True, None),
)
+def slicer(x):
+ ret = []
+ for xi in x:
+ if(xi < 0):
+ ret.append(0.0)
+ else:
+ ret.append(1.0)
+ return ret
+
def tested_constellations(easy=True, medium=True, difficult=True):
"""
Generator to produce (constellation, differential) tuples for testing purposes.
@@ -153,7 +162,7 @@ def tested_constellations(easy=True, medium=True, difficult=True):
this_poss_arg[2] = 0
if sum([argindex for argname, argvalues, argindex in poss_args]) == 0:
break
-
+
class test_constellation(gr_unittest.TestCase):
@@ -170,7 +179,7 @@ class test_constellation(gr_unittest.TestCase):
for constellation, differential in tested_constellations():
if differential:
rs = constellation.rotational_symmetry()
- rotations = [exp(i*2*pi*(0+1j)/rs) for i in range(0, rs)]
+ rotations = [exp(i*2*pi*(0+1j)/rs) for i in range(0, rs)]
else:
rotations = [None]
for rotation in rotations:
@@ -216,17 +225,17 @@ class test_constellation(gr_unittest.TestCase):
y_cpp_raw_calc = []
y_cpp_table = []
for sample in samples:
- y_python_raw_calc += digital.calc_soft_dec(sample, constel, code)
- y_python_gen_calc += digital.sd_psk_4_0(sample, Es)
- y_python_table += digital.calc_soft_dec_from_table(sample, table, prec, Es)
+ y_python_raw_calc += slicer(digital.calc_soft_dec(sample, constel, code))
+ y_python_gen_calc += slicer(digital.sd_psk_4_0(sample, Es))
+ y_python_table += slicer(digital.calc_soft_dec_from_table(sample, table, prec, Es))
y_cpp_raw_calc += c.calc_soft_dec(sample)
y_cpp_table += c.soft_decision_maker(sample)
- self.assertFloatTuplesAlmostEqual(y_python_raw_calc, y_python_gen_calc, 4)
- self.assertFloatTuplesAlmostEqual(y_python_raw_calc, y_python_table, 2)
- self.assertFloatTuplesAlmostEqual(y_cpp_raw_calc, y_cpp_table, 4)
-
+ self.assertFloatTuplesAlmostEqual(y_python_raw_calc, y_python_gen_calc, 0)
+ self.assertFloatTuplesAlmostEqual(y_python_gen_calc, y_python_table, 0)
+ self.assertFloatTuplesAlmostEqual(y_cpp_raw_calc, y_cpp_table, 0)
+
def test_soft_qpsk_calc(self):
prec = 8
constel, code = digital.psk_4_0()
@@ -257,14 +266,54 @@ class test_constellation(gr_unittest.TestCase):
y_cpp_raw_calc = []
y_cpp_table = []
for sample in samples:
- y_python_raw_calc += digital.calc_soft_dec(sample, constel, code)
- y_python_table += digital.calc_soft_dec_from_table(sample, table, prec, Es)
+ y_python_raw_calc += slicer(digital.calc_soft_dec(sample, constel, code))
+ y_python_table += slicer(digital.calc_soft_dec_from_table(sample, table, prec, Es))
- y_cpp_raw_calc += c.calc_soft_dec(sample)
- y_cpp_table += c.soft_decision_maker(sample)
+ y_cpp_raw_calc += slicer(c.calc_soft_dec(sample))
+ y_cpp_table += slicer(c.soft_decision_maker(sample))
+
+ self.assertEqual(y_python_raw_calc, y_python_table)
+ self.assertEqual(y_cpp_raw_calc, y_cpp_table)
+
+
+ def test_soft_qam16_calc(self):
+ prec = 8
+ constel, code = digital.qam_16_0()
- self.assertFloatTuplesAlmostEqual(y_python_raw_calc, y_python_table, 4)
- self.assertFloatTuplesAlmostEqual(y_cpp_raw_calc, y_cpp_table, 4)
+ rot_sym = 1
+ side = 2
+ width = 2
+ c = digital.constellation_rect(constel, code, rot_sym,
+ side, side, width, width)
+
+ # Get max energy/symbol in constellation
+ constel = c.points()
+ Es = max([abs(constel_i) for constel_i in constel])
+
+ table = digital.soft_dec_table(constel, code, prec)
+ c.gen_soft_dec_lut(prec)
+
+ x = sqrt(2.0)/2.0
+ step = (x.real+x.real) / (2**prec - 1)
+ samples = [ -x-x*1j, -x+x*1j,
+ x+x*1j, x-x*1j,
+ (-x+128*step)+(-x+128*step)*1j,
+ (-x+64*step) +(-x+64*step)*1j, (-x+64*step) +(-x+192*step)*1j,
+ (-x+192*step)+(-x+192*step)*1j, (-x+192*step)+(-x+64*step)*1j,]
+
+ y_python_raw_calc = []
+ y_python_table = []
+ y_cpp_raw_calc = []
+ y_cpp_table = []
+ for sample in samples:
+ y_python_raw_calc += slicer(digital.calc_soft_dec(sample, constel, code))
+ y_python_table += slicer(digital.calc_soft_dec_from_table(sample, table, prec, Es))
+
+ y_cpp_raw_calc += slicer(c.calc_soft_dec(sample))
+ y_cpp_table += slicer(c.soft_decision_maker(sample))
+
+ self.assertFloatTuplesAlmostEqual(y_python_raw_calc, y_python_table, 0)
+ self.assertFloatTuplesAlmostEqual(y_cpp_raw_calc, y_cpp_table, 0)
class mod_demod(gr.hier_block2):
def __init__(self, constellation, differential, rotation):
@@ -317,7 +366,7 @@ class mod_demod(gr.hier_block2):
if self.constellation.apply_pre_diff_code():
self.blocks.append(digital.map_bb(
mod_codes.invert_code(self.constellation.pre_diff_code())))
- # unpack the k bit vector into a stream of bits
+ # unpack the k bit vector into a stream of bits
self.blocks.append(blocks.unpack_k_bits_bb(
self.constellation.bits_per_symbol()))
# connect to block output
@@ -326,6 +375,6 @@ class mod_demod(gr.hier_block2):
self.blocks.append(weakref.proxy(self))
self.connect(*self.blocks)
-
+
if __name__ == '__main__':
gr_unittest.run(test_constellation, "test_constellation.xml")