summaryrefslogtreecommitdiff
path: root/gr-digital/python/digital/qam_constellations.py
diff options
context:
space:
mode:
authorTom Rondeau <tom@trondeau.com>2013-09-04 15:44:20 -0400
committerTom Rondeau <tom@trondeau.com>2013-09-04 15:44:20 -0400
commit02fd90029a1ec465f7c8da4a0d97dcdb8b3f438f (patch)
tree9bc144a9cb5b0e877108302d13181e50bd885528 /gr-digital/python/digital/qam_constellations.py
parentedf2ac24c9c2f723eb80918de7addf6d299958c2 (diff)
digital: Python functions to support soft decision making and look-up table generation.
Diffstat (limited to 'gr-digital/python/digital/qam_constellations.py')
-rwxr-xr-xgr-digital/python/digital/qam_constellations.py521
1 files changed, 521 insertions, 0 deletions
diff --git a/gr-digital/python/digital/qam_constellations.py b/gr-digital/python/digital/qam_constellations.py
new file mode 100755
index 0000000000..4e8ee08a61
--- /dev/null
+++ b/gr-digital/python/digital/qam_constellations.py
@@ -0,0 +1,521 @@
+#!/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
+from constellation_map_generator import *
+
+'''
+Note on the naming scheme. Each constellation is named using a prefix
+for the type of constellation, the order of the constellation, and a
+distinguishing feature, which comes in three modes:
+
+- No extra feature: the basic Gray-coded constellation map; others
+ will be derived from this type.
+- A single number: an indexed number to uniquely identify different
+ constellation maps.
+- 0xN_x0_x1..._xM: A permutation of the base constellation, explained
+ below.
+
+For rectangular constellations (BPSK, QPSK, QAM), we can define a
+hyperspace and look for all symmetries. This is also known as the
+automorphism group of the hypercube, aka the hyperoctahedral
+group. What this means is that we can easily define all possible
+rotations in terms of the first base mapping by creating the mapping:
+
+ f(x) = k XOR pi(x)
+
+The x is the bit string for the symbol we are altering. Then k is a
+bit string of n bits where n is the number of bits per symbol in the
+constellation (e.g., 2 for QPSK or 6 for QAM64). The pi is a
+permutation function specified as pi_0, pi_1..., pi_n-1. This permutes
+the bits from the base constellation symbol to a new code, which is
+then xor'd by k.
+
+The value of k is from 0 to 2^n-1 and pi is a list of all bit
+positions.
+
+The permutation are given for b0_b1_b2_... for the total number of
+bits. In the constellation diagrams shown in the comments, the bit
+ordering is shown as [b3b2b1b0]. Bit values returned from the soft bit
+LUTs are in the order [b3, b2, b1, b0].
+
+
+The total number of Gray coded modulations is (2^n)*(n!).
+
+We create aliases for all possible naming schemes for the
+constellations. So if a hyperoctahedral group is defined, we also set
+this function equal to a function name using a unique ID number, and
+we always select one rotation as our basic rotation that the other
+rotations are based off of.
+
+For 16QAM:
+ - n = 4
+ - (2^n)*(n!) = 384
+ - k \in [0x0, 0xF]
+ - pi = 0, 1, 2, 3
+ 0, 1, 3, 2
+ 0, 2, 1, 3
+ 0, 2, 3, 1
+ 0, 3, 1, 2
+ 0, 3, 2, 1
+ 1, 0, 2, 3
+ 1, 0, 3, 2
+ 1, 2, 0, 3
+ 1, 2, 3, 0
+ 1, 3, 0, 2
+ 1, 3, 2, 0
+ 2, 0, 1, 3
+ 2, 0, 3, 1
+ 2, 1, 0, 3
+ 2, 1, 3, 0
+ 2, 3, 0, 1
+ 2, 3, 1, 0
+ 3, 0, 1, 2
+ 3, 0, 2, 1
+ 3, 1, 0, 2
+ 3, 1, 2, 0
+ 3, 2, 0, 1
+ 3, 2, 1, 0
+'''
+
+def qam_16_0x0_0_1_2_3():
+ '''
+ 0010 0110 | 1110 1010
+
+ 0011 0111 | 1111 1011
+ -----------------------
+ 0001 0101 | 1101 1001
+
+ 0000 0100 | 1100 1000
+ '''
+ const_points = [-3-3j, -1-3j, 1-3j, 3-3j,
+ -3-1j, -1-1j, 1-1j, 3-1j,
+ -3+1j, -1+1j, 1+1j, 3+1j,
+ -3+3j, -1+3j, 1+3j, 3+3j]
+ symbols = [0x0, 0x4, 0xC, 0x8,
+ 0x1, 0x5, 0xD, 0x9,
+ 0x3, 0x7, 0xF, 0xB,
+ 0x2, 0x6, 0xE, 0xA]
+ return (const_points, symbols)
+qam_16 = qam_16_0x0_0_1_2_3
+qam_16_0 = qam_16
+
+def qam_16_0x1_0_1_2_3():
+ '''
+ 0011 0111 | 1111 1011
+
+ 0010 0110 | 1110 1010
+ -----------------------
+ 0000 0100 | 1100 1000
+
+ 0001 0101 | 1101 1001
+ '''
+ k = 0x1
+ pi = [0, 1, 2, 3]
+ return constellation_map_generator(qam_16, k, pi)
+qam_16_1 = qam_16_0x1_0_1_2_3
+
+def qam_16_0x2_0_1_2_3():
+ '''
+ 0000 0100 | 1100 1000
+
+ 0001 0101 | 1101 1001
+ -----------------------
+ 0011 0111 | 1111 1011
+
+ 0010 0110 | 1110 1010
+ '''
+ k = 0x2
+ pi = [0, 1, 2, 3]
+ return constellation_map_generator(qam_16, k, pi)
+qam_16_2 = qam_16_0x2_0_1_2_3
+
+def qam_16_0x3_0_1_2_3():
+ '''
+ 0001 0101 | 1101 1001
+
+ 0000 0100 | 1100 1000
+ -----------------------
+ 0010 0110 | 1110 1010
+
+ 0011 0111 | 1111 1011
+ '''
+ k = 0x3
+ pi = [0, 1, 2, 3]
+ return constellation_map_generator(qam_16, k, pi)
+qam_16_3 = qam_16_0x3_0_1_2_3
+
+
+def qam_16_0x0_1_0_2_3():
+ '''
+ 0001 0101 | 1101 1001
+
+ 0011 0111 | 1111 1011
+ -----------------------
+ 0010 0110 | 1110 1010
+
+ 0000 0100 | 1100 1000
+ '''
+ k = 0x0
+ pi = [1, 0, 2, 3]
+ return constellation_map_generator(qam_16, k, pi)
+qam_16_4 = qam_16_0x0_1_0_2_3
+
+def qam_16_0x1_1_0_2_3():
+ '''
+ 0000 0100 | 1100 1000
+
+ 0010 0110 | 1110 1010
+ -----------------------
+ 0011 0111 | 1111 1011
+
+ 0001 0101 | 1101 1001
+ '''
+ k = 0x1
+ pi = [1, 0, 2, 3]
+ return constellation_map_generator(qam_16, k, pi)
+qam_16_5 = qam_16_0x1_1_0_2_3
+
+def qam_16_0x2_1_0_2_3():
+ '''
+ 0011 0111 | 1111 1011
+
+ 0001 0101 | 1101 1001
+ -----------------------
+ 0000 0100 | 1100 1000
+
+ 0010 0110 | 1110 1010
+ '''
+ k = 0x2
+ pi = [1, 0, 2, 3]
+ return constellation_map_generator(qam_16, k, pi)
+qam_16_6 = qam_16_0x2_1_0_2_3
+
+def qam_16_0x3_1_0_2_3():
+ '''
+ 0010 0110 | 1110 1010
+
+ 0000 0100 | 1100 1000
+ -----------------------
+ 0001 0101 | 1101 1001
+
+ 0011 0111 | 1111 1011
+ '''
+ k = 0x3
+ pi = [1, 0, 2, 3]
+ return constellation_map_generator(qam_16, k, pi)
+qam_16_7 = qam_16_0x3_1_0_2_3
+
+
+# Soft bit LUT generators
+
+def sd_qam_16_0x0_0_1_2_3(x, Es=1):
+ '''
+ Soft bit LUT generator for constellation:
+
+ 0010 0110 | 1110 1010
+
+ 0011 0111 | 1111 1011
+ -----------------------
+ 0001 0101 | 1101 1001
+
+ 0000 0100 | 1100 1000
+ '''
+
+ dist = Es*numpy.sqrt(2)
+ boundary = dist/3.0
+ dist0 = dist/6.0
+# print "Sample: ", x
+# print "Es: ", Es
+# print "Distance: ", dist
+# print "Boundary: ", boundary
+# print "1st Bound: ", dist0
+
+ x_re = x.real
+ x_im = x.imag
+
+ if x_re < -boundary:
+ b3 = boundary*(x_re + dist0)
+ elif x_re < boundary:
+ b3 = x_re
+ else:
+ b3 = boundary*(x_re - dist0)
+
+ if x_im < -boundary:
+ b1 = boundary*(x_im + dist0)
+ elif x_im < boundary:
+ b1 = x_im
+ else:
+ b1 = boundary*(x_im - dist0)
+
+ b2 = -abs(x_re) + boundary
+ b0 = -abs(x_im) + boundary
+
+ return [(Es/2)*b3, (Es/2)*b2, (Es/2)*b1, (Es/2)*b0]
+sd_qam_16 = sd_qam_16_0x0_0_1_2_3
+sd_qam_16_0 = sd_qam_16
+
+def sd_qam_16_0x1_0_1_2_3(x, Es=1):
+ '''
+ Soft bit LUT generator for constellation:
+
+ 0011 0111 | 1111 1011
+
+ 0010 0110 | 1110 1010
+ -----------------------
+ 0000 0100 | 1100 1000
+
+ 0001 0101 | 1101 1001
+ '''
+ x_re = 3*x.real
+ x_im = 3*x.imag
+
+ if x_re < -2:
+ b3 = 2*(x_re + 1)
+ elif x_re < 2:
+ b3 = x_re
+ else:
+ b3 = 2*(x_re - 1)
+
+ if x_im < -2:
+ b1 = 2*(x_im + 1)
+ elif x_im < 2:
+ b1 = x_im
+ else:
+ b1 = 2*(x_im - 1)
+
+ b2 = -abs(x_re) + 2
+ b0 = +abs(x_im) - 2
+
+ return [b3, b2, b1, b0]
+sd_qam_16_1 = sd_qam_16_0x1_0_1_2_3
+
+def sd_qam_16_0x2_0_1_2_3(x, Es=1):
+ '''
+ Soft bit LUT generator for constellation:
+
+ 0000 0100 | 1100 1000
+
+ 0001 0101 | 1101 1001
+ -----------------------
+ 0011 0111 | 1111 1011
+
+ 0010 0110 | 1110 1010
+ '''
+
+ x_re = 3*x.real
+ x_im = 3*x.imag
+
+ if x_re < -2:
+ b3 = 2*(x_re + 1)
+ elif x_re < 2:
+ b3 = x_re
+ else:
+ b3 = 2*(x_re - 1)
+
+ if x_im < -2:
+ b1 = -2*(x_im + 1)
+ elif x_im < 2:
+ b1 = -x_im
+ else:
+ b1 = -2*(x_im - 1)
+
+ b2 = -abs(x_re) + 2
+ b0 = -abs(x_im) + 2
+
+ return [b3, b2, b1, b0]
+sd_qam_16_2 = sd_qam_16_0x2_0_1_2_3
+
+def sd_qam_16_0x3_0_1_2_3(x, Es=1):
+ '''
+ Soft bit LUT generator for constellation:
+
+ 0001 0101 | 1101 1001
+
+ 0000 0100 | 1100 1000
+ -----------------------
+ 0010 0110 | 1110 1010
+
+ 0011 0111 | 1111 1011
+ '''
+ x_re = 3*x.real
+ x_im = 3*x.imag
+
+ if x_re < -2:
+ b3 = 2*(x_re + 1)
+ elif x_re < 2:
+ b3 = x_re
+ else:
+ b3 = 2*(x_re - 1)
+
+ if x_im < -2:
+ b1 = -2*(x_im + 1)
+ elif x_im < 2:
+ b1 = -x_im
+ else:
+ b1 = -2*(x_im - 1)
+
+ b2 = -abs(x_re) + 2
+ b0 = +abs(x_im) - 2
+
+ return [b3, b2, b1, b0]
+sd_qam_16_3 = sd_qam_16_0x3_0_1_2_3
+
+def sd_qam_16_0x0_1_0_2_3(x, Es=1):
+ '''
+ Soft bit LUT generator for constellation:
+
+ 0001 0101 | 1101 1001
+
+ 0011 0111 | 1111 1011
+ -----------------------
+ 0010 0110 | 1110 1010
+
+ 0000 0100 | 1100 1000
+ '''
+ x_re = 3*x.real
+ x_im = 3*x.imag
+
+ if x_re < -2:
+ b3 = 2*(x_re + 1)
+ elif x_re < 2:
+ b3 = x_re
+ else:
+ b3 = 2*(x_re - 1)
+
+ if x_im < -2:
+ b0 = 2*(x_im + 1)
+ elif x_im < 2:
+ b0 = x_im
+ else:
+ b0 = 2*(x_im - 1)
+
+ b2 = -abs(x_re) + 2
+ b1 = -abs(x_im) + 2
+
+ return [b3, b2, b1, b0]
+sd_qam_16_4 = sd_qam_16_0x0_1_0_2_3
+
+def sd_qam_16_0x1_1_0_2_3(x, Es=1):
+ '''
+ Soft bit LUT generator for constellation:
+
+ 0000 0100 | 1100 1000
+
+ 0010 0110 | 1110 1010
+ -----------------------
+ 0011 0111 | 1111 1011
+
+ 0001 0101 | 1101 1001
+ '''
+ x_re = 3*x.real
+ x_im = 3*x.imag
+
+ if x_re < -2:
+ b3 = 2*(x_re + 1)
+ elif x_re < 2:
+ b3 = x_re
+ else:
+ b3 = 2*(x_re - 1)
+
+ if x_im < -2:
+ b0 = -2*(x_im + 1)
+ elif x_im < 2:
+ b0 = -x_im
+ else:
+ b0 = -2*(x_im - 1)
+
+ b2 = -abs(x_re) + 2
+ b1 = -abs(x_im) + 2
+
+ return [b3, b2, b1, b0]
+sd_qam_16_5 = sd_qam_16_0x1_1_0_2_3
+
+def sd_qam_16_0x2_1_0_2_3(x, Es=1):
+ '''
+ Soft bit LUT generator for constellation:
+
+ 0011 0111 | 1111 1011
+
+ 0001 0101 | 1101 1001
+ -----------------------
+ 0000 0100 | 1100 1000
+
+ 0010 0110 | 1110 1010
+ '''
+ x_re = 3*x.real
+ x_im = 3*x.imag
+
+ if x_re < -2:
+ b3 = 2*(x_re + 1)
+ elif x_re < 2:
+ b3 = x_re
+ else:
+ b3 = 2*(x_re - 1)
+
+ if x_im < -2:
+ b0 = 2*(x_im + 1)
+ elif x_im < 2:
+ b0 = x_im
+ else:
+ b0 = 2*(x_im - 1)
+
+ b2 = -abs(x_re) + 2
+ b1 = +abs(x_im) - 2
+
+ return [b3, b2, b1, b0]
+sd_qam_16_6 = sd_qam_16_0x2_1_0_2_3
+
+def sd_qam_16_0x3_1_0_2_3(x, Es=1):
+ '''
+ Soft bit LUT generator for constellation:
+
+ 0010 0110 | 1110 1010
+
+ 0000 0100 | 1100 1000
+ -----------------------
+ 0001 0101 | 1101 1001
+
+ 0011 0111 | 1111 1011
+ '''
+ x_re = 3*x.real
+ x_im = 3*x.imag
+
+ if x_re < -2:
+ b3 = 2*(x_re + 1)
+ elif x_re < 2:
+ b3 = x_re
+ else:
+ b3 = 2*(x_re - 1)
+
+ if x_im < -2:
+ b0 = -2*(x_im + 1)
+ elif x_im < 2:
+ b0 = -x_im
+ else:
+ b0 = -2*(x_im - 1)
+
+ b2 = -abs(x_re) + 2
+ b1 = +abs(x_im) - 2
+
+ return [b3, b2, b1, b0]
+sd_qam_16_7 = sd_qam_16_0x3_1_0_2_3