diff options
author | Tom Rondeau <tom@trondeau.com> | 2014-12-02 15:11:05 -0500 |
---|---|---|
committer | Tom Rondeau <tom@trondeau.com> | 2014-12-03 19:02:26 -0500 |
commit | 57ee0e7e122668a2b7f628ecb84fe1f90f9d2d11 (patch) | |
tree | 4a2bee2978248f55c9f3c14cdb68d0bb953950ae /gr-digital/python/digital/soft_dec_lut_gen.py | |
parent | 92ac02f7fd7289d11fa6523e0491c6a6d8c3cb56 (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.py | 96 |
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 |