summaryrefslogtreecommitdiff
path: root/gr-digital/python/digital/soft_dec_lut_gen.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/soft_dec_lut_gen.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/soft_dec_lut_gen.py')
-rw-r--r--gr-digital/python/digital/soft_dec_lut_gen.py96
1 files changed, 59 insertions, 37 deletions
diff --git a/gr-digital/python/digital/soft_dec_lut_gen.py b/gr-digital/python/digital/soft_dec_lut_gen.py
index 24d8bbdc2e..9c184d60cb 100644
--- a/gr-digital/python/digital/soft_dec_lut_gen.py
+++ b/gr-digital/python/digital/soft_dec_lut_gen.py
@@ -1,30 +1,29 @@
#!/usr/bin/env python
#
# 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.
-#
+#
import numpy
def soft_dec_table_generator(soft_dec_gen, prec, Es=1):
- '''
- Builds a LUT that is a list of tuples. The tuple represents the
+ '''Builds a LUT that is a list of tuples. The tuple represents the
soft decisions for the constellation/bit mapping at any given
point in the complex space, (x,y).
@@ -70,18 +69,19 @@ def soft_dec_table_generator(soft_dec_gen, prec, Es=1):
(1,1) into an index value in the table and returns the tuple of
soft decisions at that index.
- Es is the energy per symbol. This is passed to the function to
- provide the bounds when calling the generator function since they
- don't know how the constellation was normalized. Using the
- (maximum) energy per symbol for constellation allows us to provide
- any scaling of the constellation (normalized to sum to 1,
+ Es is the maximum energy per symbol. This is passed to the
+ function to provide the bounds when calling the generator function
+ since they don't know how the constellation was normalized. Using
+ the (maximum) energy per symbol for constellation allows us to
+ provide any scaling of the constellation (normalized to sum to 1,
normalized so the outside points sit on +/-1, etc.) but still
calculate the soft decisions as we would given the full
constellation.
+
'''
npts = 2.0**prec
- maxd = Es*numpy.sqrt(2)/2
+ maxd = Es*numpy.sqrt(2.0)/2.0
yrng = numpy.linspace(-maxd, maxd, npts)
xrng = numpy.linspace(-maxd, maxd, npts)
@@ -129,7 +129,7 @@ def soft_dec_table(constel, symbols, prec, npwr=1):
table.append(decs)
return table
-def calc_soft_dec_from_table(sample, table, prec, Es=1):
+def calc_soft_dec_from_table(sample, table, prec, Es=1.0):
'''
Takes in a complex sample and converts it from the coordinates
(-1,-1) to (1,1) into an index value. The index value points to a
@@ -152,25 +152,25 @@ def calc_soft_dec_from_table(sample, table, prec, Es=1):
calculate the soft decisions as we would given the full
constellation.
'''
- lut_scale = 2**prec
- maxd = Es*numpy.sqrt(2)/2
- step = 2*maxd / lut_scale
- scale = (lut_scale) / (2*maxd) - step
-
+ lut_scale = 2.0**prec
+ maxd = Es*numpy.sqrt(2.0)/2.0
+ scale = (lut_scale) / (2.0*maxd)
+
+ alpha = 0.99 # to keep index within bounds
xre = sample.real
xim = sample.imag
- xre = int((maxd + min(maxd, max(-maxd, xre))) * scale)
- xim = int((maxd + min(maxd, max(-maxd, xim))) * scale)
- index = int(xre + lut_scale*xim)
+ xre = ((maxd + min(alpha*maxd, max(-alpha*maxd, xre))) * scale)
+ xim = ((maxd + min(alpha*maxd, max(-alpha*maxd, xim))) * scale)
+ index = int(xre) + lut_scale*int(xim)
max_index = lut_scale**2
- if(index > max_index):
- return table[0];
- if(index < 0):
- raise RuntimeError("calc_from_table: input sample out of range.")
+ while(index >= max_index):
+ index -= lut_scale;
+ while(index < 0):
+ index += lut_scale;
- return table[index]
+ return table[int(index)]
def calc_soft_dec(sample, constel, symbols, npwr=1):
'''
@@ -192,25 +192,21 @@ def calc_soft_dec(sample, constel, symbols, npwr=1):
than 0 are more likely to indicate a '0' bit and decisions greater
than 0 are more likely to indicate a '1' bit.
'''
-
+
M = len(constel)
k = int(numpy.log2(M))
tmp = 2*k*[0,]
s = k*[0,]
- # Find a scaling factor for the constellation, however it was normalized.
- constel = numpy.array(constel)
- scale = min(min(abs(constel.real)), min(abs(constel.imag)))
-
for i in range(M):
# Calculate the distance between the sample and the current
# constellation point.
- dist = abs(sample - constel[i])**2
+ dist = abs(sample - constel[i])
# Calculate the probability factor from the distance and the
# scaled noise power.
- d = numpy.exp(-dist/(2*npwr*scale**2))
-
+ d = numpy.exp(-dist/npwr)
+
for j in range(k):
# Get the bit at the jth index
mask = 1<<j
@@ -222,11 +218,37 @@ def calc_soft_dec(sample, constel, symbols, npwr=1):
# else, add to the probability of a one
else:
tmp[2*j+1] += d
-
+
# Calculate the log-likelihood ratio for all bits based on the
# probability of ones (tmp[2*i+1]) over the probability of a zero
# (tmp[2*i+0]).
for i in range(k):
- s[k-1-i] = (numpy.log(tmp[2*i+1]) - numpy.log(tmp[2*i+0])) * scale**2
+ s[k-1-i] = (numpy.log(tmp[2*i+1]) - numpy.log(tmp[2*i+0]))
return s
+
+
+def show_table(table):
+ prec = int(numpy.sqrt(len(table)))
+ nbits = len(table[0])
+ pp = ""
+ subi = 1
+ subj = 0
+ for i in reversed(xrange(prec+1)):
+ if(i == prec//2):
+ pp += "-----" + prec*((nbits*8)+3)*"-" + "\n"
+ subi = 0
+ continue
+ for j in xrange(prec+1):
+ if(j == prec//2):
+ pp += "| "
+ subj = 1
+ else:
+ item = table[prec*(i-subi) + (j-subj)]
+ pp += "( "
+ for t in xrange(nbits-1, -1, -1):
+ pp += "{0: .4f} ".format(item[t])
+ pp += ") "
+ pp += "\n"
+ subj = 0
+ print pp