21
21
22
22
"""
23
23
from bisect import insort
24
- from itertools import product
25
24
26
25
import 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 , \
28
27
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
30
29
from numpy .fft import fft , ifft
31
30
from numpy .linalg import qr , norm
31
+ from sympy .combinatorics .graycode import GrayCode
32
32
33
33
from commpy .utilities import bitarray2dec , dec2bitarray , signal_power
34
34
@@ -65,10 +65,16 @@ class Modem:
65
65
If the constellation is changed to an array-like with length that is not a power of 2.
66
66
"""
67
67
68
- def __init__ (self , constellation ):
68
+ def __init__ (self , constellation , reorder_as_gray = True ):
69
69
""" Creates a custom Modem object. """
70
70
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
72
78
73
79
def modulate (self , input_bits ):
74
80
""" Modulate (map) an array of bits to constellation symbols.
@@ -197,10 +203,11 @@ class PSKModem(Modem):
197
203
def __init__ (self , m ):
198
204
""" Creates a Phase Shift Keying (PSK) Modem object. """
199
205
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.' )
202
209
203
- self . constellation = list ( map ( _constellation_symbol , arange (m )))
210
+ super (). __init__ ( exp ( 1j * arange (0 , 2 * pi , 2 * pi / m )))
204
211
205
212
206
213
class QAMModem (Modem ):
@@ -229,6 +236,7 @@ class QAMModem(Modem):
229
236
------
230
237
ValueError
231
238
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.
232
240
"""
233
241
234
242
def __init__ (self , m ):
@@ -237,16 +245,21 @@ def __init__(self, m):
237
245
Parameters
238
246
----------
239
247
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).
241
249
250
+ Raises
251
+ ------
252
+ ValueError
253
+ If m would lead to an non-square QAM.
242
254
"""
243
255
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.' )
246
259
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 )
250
263
251
264
252
265
def ofdm_tx (x , nfft , nsc , cp_length ):
0 commit comments