2121
2222"""
2323from bisect import insort
24- from itertools import product
2524
2625import matplotlib .pyplot as plt
27- from numpy import arange , array , zeros , pi , cos , sin , sqrt , log2 , argmin , \
26+ from numpy import arange , array , zeros , pi , sqrt , log2 , argmin , \
2827 hstack , repeat , tile , dot , shape , concatenate , exp , \
29- log , vectorize , empty , eye , kron , inf , full , abs , newaxis , minimum , clip
28+ log , vectorize , empty , eye , kron , inf , full , abs , newaxis , minimum , clip , fromiter
3029from numpy .fft import fft , ifft
3130from numpy .linalg import qr , norm
31+ from sympy .combinatorics .graycode import GrayCode
3232
3333from commpy .utilities import bitarray2dec , dec2bitarray , signal_power
3434
@@ -65,10 +65,16 @@ class Modem:
6565 If the constellation is changed to an array-like with length that is not a power of 2.
6666 """
6767
68- def __init__ (self , constellation ):
68+ def __init__ (self , constellation , reorder_as_gray = True ):
6969 """ Creates a custom Modem object. """
7070
71- self .constellation = constellation
71+ if reorder_as_gray :
72+ m = log2 (len (constellation ))
73+ gray_code_sequence = GrayCode (m ).generate_gray ()
74+ gray_code_sequence_array = fromiter ((int (g , 2 ) for g in gray_code_sequence ), int , len (constellation ))
75+ self .constellation = array (constellation )[gray_code_sequence_array .argsort ()]
76+ else :
77+ self .constellation = constellation
7278
7379 def modulate (self , input_bits ):
7480 """ Modulate (map) an array of bits to constellation symbols.
@@ -197,10 +203,11 @@ class PSKModem(Modem):
197203 def __init__ (self , m ):
198204 """ Creates a Phase Shift Keying (PSK) Modem object. """
199205
200- def _constellation_symbol (i ):
201- return cos (2 * pi * (i - 1 ) / m ) + sin (2 * pi * (i - 1 ) / m ) * (0 + 1j )
206+ num_bits_symbol = log2 (m )
207+ if num_bits_symbol != int (num_bits_symbol ):
208+ raise ValueError ('Constellation length must be a power of 2.' )
202209
203- self . constellation = list ( map ( _constellation_symbol , arange (m )))
210+ super (). __init__ ( exp ( 1j * arange (0 , 2 * pi , 2 * pi / m )))
204211
205212
206213class QAMModem (Modem ):
@@ -229,6 +236,7 @@ class QAMModem(Modem):
229236 ------
230237 ValueError
231238 If the constellation is changed to an array-like with length that is not a power of 2.
239+ If the parameter m would lead to an non-square QAM during initialization.
232240 """
233241
234242 def __init__ (self , m ):
@@ -237,16 +245,21 @@ def __init__(self, m):
237245 Parameters
238246 ----------
239247 m : int
240- Size of the QAM constellation.
248+ Size of the QAM constellation. Must lead to a square QAM (ie sqrt(m) is an integer).
241249
250+ Raises
251+ ------
252+ ValueError
253+ If m would lead to an non-square QAM.
242254 """
243255
244- def _constellation_symbol (i ):
245- return (2 * i [0 ] - 1 ) + (2 * i [1 ] - 1 ) * (1j )
256+ num_symb_pam = sqrt (m )
257+ if num_symb_pam != int (num_symb_pam ):
258+ raise ValueError ('m must lead to a square QAM.' )
246259
247- mapping_array = arange (1 , sqrt ( m ) + 1 ) - ( sqrt ( m ) / 2 )
248- self . constellation = list ( map ( _constellation_symbol ,
249- list ( product ( mapping_array , repeat = 2 ))) )
260+ pam = arange (- num_symb_pam + 1 , num_symb_pam , 2 )
261+ constellation = tile ( hstack (( pam , pam [:: - 1 ])), int ( num_symb_pam ) // 2 ) * 1j + pam . repeat ( num_symb_pam )
262+ super (). __init__ ( constellation )
250263
251264
252265def ofdm_tx (x , nfft , nsc , cp_length ):
0 commit comments