-
Notifications
You must be signed in to change notification settings - Fork 2
Expand file tree
/
Copy pathmodulationFunctions.py
More file actions
196 lines (171 loc) · 9.33 KB
/
Copy pathmodulationFunctions.py
File metadata and controls
196 lines (171 loc) · 9.33 KB
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
# -*- coding: utf-8 -*-
"""
Created on Mon Mar 27 13:03:02 2023
@author: Megatron
"""
import os
import sys
#from numba import jit, int32, float32, types, typed, boolean, float64, int64
#import math
projectDir = os.environ.get('IEEE8032DJ')
if projectDir == None:
import pathlib
projectDir = pathlib.Path(__file__).parent.absolute()
sys.path.insert(1, projectDir)
import numpy as np
import time
import ieeeConstants
from ieeeConstants import IEEE_8023_INT_DATA_TYPE, IEEE_8023_DECIMAL_DATA_TYPE, PAM4_GRAYCODED, PAM4_LEVEL_HIGH, PAM4_LEVEL_LOW, PAM4_LEVEL_MID_HIGH, PAM4_LEVEL_MID_LOW, PAM4_NOGRAYCODING, PAM4_GRAYCODED, PAM4_LEVELS
#@jit(nopython = True)
def slicer(vector):
## Omer Sella: slicer puts a threshold, everything above 0 is translated to 1, otherwise 0 (including equality). Do not confuse with the reserved function name slice !
length = vector.shape[0]
slicedVector = np.ones(length, dtype = IEEE_8023_INT_DATA_TYPE)
slicedVector[np.where(vector <= 0)] = 0
return slicedVector
#@jit(nopython = True)
def modulatePAM2(vector):
modulatedVector = np.ones(vector.shape[0], dtype = IEEE_8023_DECIMAL_DATA_TYPE)
modulatedVector[np.where(vector == 0)] = -1
return modulatedVector
def precoder(vector, init = 'default'):
# 1/(1+D) modulu 4 precoder - need referece
vectorPrecoded = np.zeros(len(vector), dtype = IEEE_8023_INT_DATA_TYPE)
if init == 'default':
vectorPrecoded[0] = (vector[0] - vector[1])%4
else:
assert(init == 0 or init == 1 or init == 2 or init == 3)
vectorPrecoded[0] = init
for i in range(1,len(vector)):
vectorPrecoded[i] = (vector[i] - vectorPrecoded[i-1]) %4
return vectorPrecoded
def modulatePAM4(vector, grayCoding = True, precoding = False, precoderInit = 'default',
levels = [ieeeConstants.PAM4_LEVEL_LOW,
ieeeConstants.PAM4_LEVEL_MID_LOW,
ieeeConstants.PAM4_LEVEL_MID_HIGH,
ieeeConstants.PAM4_LEVEL_HIGH]):
# 177.4.7.1 of draft 1.0 refers to 120.5.7.1
# 120.5.7.1 Gray mapping for PAM4 encoded lanes
# For output lanes encoded as PAM4 (for 200GBASE-R, where the number of output lanes is 4, or for
# 400GBASE-R, where the number of output lanes is 4 or 8), the PMA transmit process shall map consecutive
# pairs of bits {A, B}, where A is the bit arriving first, to a Gray-coded symbol as follows:
# {0, 0} maps to 0,
# {0, 1} maps to 1,
# {1, 1} maps to 2, and
# {1, 0} maps to 3
#vector is assumed to be a binary vector of even length
# note that the following line means the stream goes [MSB0,LSB0,MSB1,LSB1 ...]
if grayCoding:
pam4Symbols = 2 * np.array(vector[0::2]) + ((np.array(vector[1::2]) + np.array(vector[0::2])) %2)
else:
pam4Symbols = 2 * vector[0::2] + vector[1::2]
if precoding:
pam4SymbolsPrecoded = precoder(pam4Symbols, init = precoderInit)
else:
pam4SymbolsPrecoded = pam4Symbols
modulatedVector = np.zeros(vector.shape[0] // 2, dtype = IEEE_8023_DECIMAL_DATA_TYPE)
modulatedVector[np.where(pam4SymbolsPrecoded == 0)] = levels[0] #PAM4_LEVEL_LOW
modulatedVector[np.where(pam4SymbolsPrecoded == 1)] = levels[1] #PAM4_LEVEL_MID_LOW
modulatedVector[np.where(pam4SymbolsPrecoded == 2)] = levels[2] #PAM4_LEVEL_MID_HIGH
modulatedVector[np.where(pam4SymbolsPrecoded == 3)] = levels[3] #PAM4_LEVEL_HIGH
return modulatedVector, pam4Symbols, pam4SymbolsPrecoded
def pam4Slicer(vector, greyCoded = True):
"""
Deprecated !
"""
pam4Symbols = np.zeros((vector.shape[0]), dtype = IEEE_8023_INT_DATA_TYPE)
bitsDemodulated = np.zeros((2 * vector.shape[0]), dtype = IEEE_8023_INT_DATA_TYPE)
if greyCoded == True:
pam4Symbols[np.where(vector <= -(2/3))] = 0
mask = (vector > -(2/3)) & (vector <= 0 )
pam4Symbols[np.where(mask)] = 1
mask = (vector > 0) & (vector <= (2/3))
pam4Symbols[np.where(mask)] = 3
pam4Symbols[np.where( (vector > (2/3))) ] = 2
else:
pam4Symbols[np.where(vector <= -(2/3))] = 0
mask = (vector > -(2/3)) & (vector <= 0)
pam4Symbols[np.where(mask)] = 1
mask = (vector > 0) & (vector <= 2/3 )
pam4Symbols[np.where(mask)] = 2
pam4Symbols[np.where( (vector > 2/3)) ] = 3
for i in range(vector.shape[0]):
symbol = pam4Symbols[i]
#if greyCoded:
#print(symbol)
if symbol == 0:
bitsDemodulated[2 * i ] = 0
bitsDemodulated[2 * i + 1] = 0
elif symbol == 1:
bitsDemodulated[2 * i ] = 0
bitsDemodulated[2 * i + 1] = 1
elif symbol == 2:
bitsDemodulated[2 * i ] = 1
bitsDemodulated[2 * i + 1] = 0
else:
bitsDemodulated[2 * i ] = 1
bitsDemodulated[2 * i + 1] = 1
return bitsDemodulated, pam4Symbols
def pam4Quantize(signal, effectiveNumberOfBits = 6, high = ieeeConstants.PAM4_LEVEL_HIGH, low = ieeeConstants.PAM4_LEVEL_LOW):
q = 1/(2 ** effectiveNumberOfBits)
quantizedSignal = q * np.floor(signal/q)
return quantizedSignal
def pam4Slice(signal, reference = PAM4_LEVELS):#np.array([PAM4_LEVEL_LOW, PAM4_LEVEL_MID_LOW, PAM4_LEVEL_MID_HIGH, PAM4_LEVEL_HIGH])):
"""
Input signal of type np array of real numbers assumed to be a 1 dimensional column vector - no safety
"""
referenceRepeated = np.tile(reference[:,np.newaxis], (1,len(signal)))
signalRepeated = np.tile(signal, (len(reference),1))
g = lambda x: np.exp(-1*np.abs(x))
f = lambda x: 1/ np.abs(x)
h = lambda x: 2 ** (-1 * np.abs(x))
def taperedTriangle(x, base = 4/3, epsilon = 0):
"""
If x is out of bounds (less than or greater than base/2) return 0
If within base bounds, return 1 if within epsilon bounds, and otherwise
a (piecewise) linear function of x that depends on base and epsilon
"""
ans = np.zeros(x.shape)
# The result is part of two things: if x< base/2 or x>base/2 then 0, otherwise,
ans = np.where( (np.abs(x) > (base/2)), 0,
np.where(np.abs(x) < epsilon/2, 1,
np.where(x < epsilon/2 , ((base/2) + x) / (base/2 - epsilon/2), ((base/2) - x) / (base/2 - epsilon/2))))
return ans
classifier = taperedTriangle(signalRepeated - referenceRepeated) #/ np.sum( g(signalRepeated - referenceRepeated), axis = 0)
# Reassignment of probability to be 1 if the reference signal is strictly higher or lower than pam4 highest or lowest correspondigly.
classifier[3,:] = np.where(signalRepeated[3,:] > PAM4_LEVEL_HIGH, 1, classifier[3,:])
classifier[0,:] = np.where(signalRepeated[0,:] < PAM4_LEVEL_LOW, 1, classifier[0,:])
# the result of np.argmin(np.abs(signalRepeated - referenceRepeated), axis = 0) is a vector with values 0,1,2,3 depending on the ordering of the reference levels - no safety, the user needs to think how they order the reference so that the PAM4 slicing will be correct
pam4Symbols = np.argmin(np.abs(signalRepeated - referenceRepeated), axis = 0)
errorAbsoluteValue = np.min(np.abs(signalRepeated - referenceRepeated), axis = 0)
return pam4Symbols, errorAbsoluteValue, classifier
def pam4SymbolsToBits(pam4Symbols, grayCoded = True):
bits = np.zeros(len(pam4Symbols) * 2, dtype = IEEE_8023_INT_DATA_TYPE)
if grayCoded:
for i in range(len(pam4Symbols)):
bits[2 * i] = 1 * (pam4Symbols[i] > 1)
bits[2 * i + 1] = 1 * (pam4Symbols[i] == 1 or pam4Symbols[i] == 2)
else:
for i in range(len(pam4Symbols)):
bits[2 * i] = pam4Symbols[i] // 2
bits[2 * i + 1] = pam4Symbols[i] % 2
return bits
def pam4ClassifierToBits(classifier, grayCoded = True):
"""
The idea is simple - we multiply the classifier value (probability) by the corresponding bit pair and sum.
There is no need to use pam4SymbolsToBits, because the classifier represents probabilities for ALL pam4 symbols.
In fact, since the is no control logic (if,else) or list comprehension here, this should be faster than pam4SymbolsToBits.
"""
if grayCoded:
symbolsToBits = PAM4_GRAYCODED
else:
symbolsToBits = PAM4_NOGRAYCODING
msbRepeated = np.tile(np.expand_dims(symbolsToBits[:,0], axis = 1), (1,classifier.shape[1]))
lsbRepeated = np.tile(np.expand_dims(symbolsToBits[:,1], axis = 1), (1,classifier.shape[1]))
probabilityOfReceivingOneMsb = np.sum(msbRepeated * classifier, axis = 0)
probabilityOfReceivingOneLsb = np.sum(lsbRepeated * classifier, axis = 0)
# each vector is a row vector, so stack them MSB on top of LSB, and then flatten it using Fortran ordering, i.e.: column first, so stack[0,0], stack[0,1], stack[1,0], stack[1,1] ...
probabilityOfReceivingOne = np.vstack((probabilityOfReceivingOneMsb, probabilityOfReceivingOneLsb)).ravel(order = 'F')
bits = pam4SymbolsToBits(np.argmax(classifier, axis = 0), grayCoded = grayCoded)
probabilities = np.where(bits == 1, probabilityOfReceivingOne, 1 - probabilityOfReceivingOne)
return bits, probabilities