forked from Cccccyf/CCDM_for_probabilistic_shaping
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathutils.py
225 lines (180 loc) · 5.86 KB
/
utils.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
import numpy as np
"""
Edson Porto da Silva, Adolfo Fernandes Herbster. "OptiCommPy: Open-source Simulation of Fiber Optic Communications with Python", Journal of Open Source Software, 9(98), 6600, (2024)
https://doi.org/10.21105/joss.06600
"""
def pamConst(M):
"""
Generate a Pulse Amplitude Modulation (PAM) constellation.
Parameters
----------
M : int
Number of symbols in the constellation. It must be an integer.
Returns
-------
np.array
1D PAM constellation.
References
----------
[1] Proakis, J. G., & Salehi, M. Digital Communications (5th Edition). McGraw-Hill Education, 2008.
"""
L = int(M - 1)
return np.arange(-L, L + 1, 2)
def pskConst(M):
"""
Generate a Phase Shift Keying (PSK) constellation.
Parameters
----------
M : int
Number of symbols in the constellation. It must be a power of 2 positive integer.
Returns
-------
np.array
Complex M-PSK constellation.
References
----------
[1] Proakis, J. G., & Salehi, M. Digital Communications (5th Edition). McGraw-Hill Education, 2008.
"""
# generate complex M-PSK constellation
pskPhases = np.arange(0, 2 * np.pi, 2 * np.pi / M)
return np.exp(1j * pskPhases)
def apskConst(M, m1=None, phaseOffset=None):
"""
Generate an Amplitude-Phase Shift Keying (APSK) constellation.
Parameters
----------
M : int
Constellation order.
m1 : int
Number of bits used to index the radii of the constellation.
Returns
-------
const : np.array
APSK constellation
References
----------
[1] Z. Liu, et al "APSK Constellation with Gray Mapping," IEEE Communications Letters, vol. 15, no. 12, pp. 1271-1273, 2011.
[2] Proakis, J. G., & Salehi, M. Digital Communications (5th Edition). McGraw-Hill Education, 2008.
"""
if m1 is None:
if M == 16:
m1 = 1
elif M == 32:
m1 = 2
elif M == 64:
m1 = 2
elif M == 128:
m1 = 3
elif M == 256:
m1 = 3
elif M == 512:
m1 = 4
elif M == 1024:
m1 = 4
nRings = int(2**m1) # bits that index the rings
m2 = int(np.log2(M) - m1) # bits that index the symbols per ring
symbolsPerRing = int(2**m2)
const = np.zeros((M,), dtype=np.complex64)
if phaseOffset is None:
phaseOffset = np.pi / symbolsPerRing
for idx in range(nRings):
radius = np.sqrt(-np.log(1 - ((idx + 1) - 0.5) * symbolsPerRing / M))
if (idx + 1) % 2 == 1:
const[idx * symbolsPerRing : (idx + 1) * symbolsPerRing] = radius * np.flip(
pskConst(symbolsPerRing)
)
else:
const[
idx * symbolsPerRing : (idx + 1) * symbolsPerRing
] = radius * pskConst(symbolsPerRing)
return const * np.exp(1j * phaseOffset)
def qamConst(M):
"""
Generate a Quadrature Amplitude Modulation (QAM) constellation.
Parameters
----------
M : int
Number of symbols in the constellation. It must be a perfect square.
Returns
-------
const : np.array
Complex square M-QAM constellation.
References
----------
[1] Proakis, J. G., & Salehi, M. Digital Communications (5th Edition). McGraw-Hill Education, 2008.
"""
L = int(np.sqrt(M) - 1)
# generate 1D PAM constellation
PAM = np.arange(-L, L + 1, 2)
PAM = np.array([PAM])
# generate complex square M-QAM constellation
const = np.tile(PAM, (L + 1, 1))
const = const + 1j * np.flipud(const.T)
for ind in np.arange(1, L + 1, 2):
const[ind] = np.flip(const[ind], 0)
return const
def grayCode(n):
"""
Gray code generator.
Parameters
----------
n : int
length of the codeword in bits.
Returns
-------
code : list
list of binary strings of the gray code.
"""
code = []
for i in range(1 << n):
# Generating the decimal
# values of gray code then using
# bitset to convert them to binary form
val = i ^ (i >> 1)
# Converting to binary string
s = bin(val)[2::]
code.append(s.zfill(n))
return code
def grayMapping(M, constType):
"""
Gray Mapping for digital modulations.
Parameters
----------
M : int
modulation order
constType : 'qam', 'psk', 'pam' or 'ook'.
type of constellation.
Returns
-------
const : np.array
constellation symbols (sorted according their corresponding
Gray bit sequence as integer decimal).
References
----------
[1] Proakis, J. G., & Salehi, M. Digital Communications (5th Edition). McGraw-Hill Education, 2008.
"""
if M != 2 and constType == "ook":
M = 2
bitsSymb = int(np.log2(M))
code = grayCode(bitsSymb)
if constType == "ook":
const = np.arange(0, 2)
elif constType == "pam":
const = pamConst(M)
elif constType == "qam":
const = qamConst(M)
elif constType == "psk":
const = pskConst(M)
elif constType == "apsk":
const = apskConst(M)
const = const.reshape(M, 1)
const_ = np.zeros((M, 2), dtype=complex)
for ind in range(M):
const_[ind, 0] = const[ind, 0] # complex constellation symbol
const_[ind, 1] = int(code[ind], 2) # mapped bit sequence (as integer decimal)
# sort complex symbols column according to their mapped bit sequence (as integer decimal)
const = const_[const_[:, 1].real.argsort()]
const = const[:, 0]
if constType in ["pam", "ook"]:
const = const.real
return const