-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathgenerateISI.py
211 lines (166 loc) · 9 KB
/
generateISI.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
###########################################################################
#
# StatOpt Simulator
# by Jeremy Cosson-Martin, Jhoan Salinas of
# Ali Sheikholeslami's group
# Ported to Python 3 by Savo Bajic
# Department of Electrical and Computer Engineering
# University of Toronto
# Copyright Material
# For personal use only
#
###########################################################################
# This function generates an eye diagram due to ISI in the pulse
# response. It does this by first splitting up the pulse response into
# individual cursors, it then determine all combinations of pre, main and
# post cursor data levels and multiplies the pulse response by the
# combination, finally it superimposes the summation of each combination,
# creating several signal traces resembling an eye diagram.
#
# Inputs:
# simSettings: structure containing simulation settings
# simResults: structure containing simulation results
#
###########################################################################
from userSettingsObjects import simulationSettings, nothing
from initializeSimulation import simulationStatus
import numpy as np
import copy
def generateISI(simSettings: simulationSettings, simResults: simulationStatus):
# Import variables
signalingMode = simSettings.general.signalingMode
samplesPerSymb = simSettings.general.samplesPerSymb.value
modulation = simSettings.general.modulation.value
levelNumb = simSettings.general.levelNumb.value
preCursorCount = simSettings.transmitter.preCursorCount.value
postCursorCount = simSettings.transmitter.postCursorCount.value
cursorCount = simSettings.transmitter.cursorCount.value
approximate = simSettings.channel.approximate
speedUpSim = simSettings.adaption.speedUpSim
pulses = simResults.pulseResponse.receiver.outputs
result = nothing()
transitions = 0
if not 'eyeGeneration' in simResults.__dict__:
setattr(simResults, 'eyeGeneration', nothing())
# Break if simulation has already failed
if not simResults.results.successful: return
if speedUpSim:
# Take previous results if repeat simulation
transitions = simResults.eyeGeneration.ISI.thru
else:
# Determine all cursor combinations
combinations = generateCursorCombinations(cursorCount, signalingMode, modulation, levelNumb)
# Clasify ISI trajectories classified by transition
transitions = clasifyTrajectories(combinations, preCursorCount, signalingMode)
# Loop through each available channel file
for chName in pulses.__dict__:
# Skip required channels
if approximate:
if chName not in['thru', 'xtalk']:
continue
else:
if chName in ['next', 'fext', 'xtalk']:
continue
# Split pulse into symbol portions
splitPul = splitPulse(pulses.__dict__[chName], preCursorCount, postCursorCount, samplesPerSymb)
# Apply cursor combinations to the split pulse response
# Need to make a copy to have seperate objects for each channel
temp = copy.deepcopy(transitions) # Copy to new object before it gets filled with more data
applyCursorCombination(temp, splitPul, samplesPerSymb)
setattr(result, chName, temp)
# Save results
simResults.eyeGeneration.ISI = result
###########################################################################
# The following functions returns a structure containing all possible
# cursor combinations. The number of levels is dictated by the modulation
# scheme and the number of cursors. If a signaling mode such as clock is
# selected, combinations which do not have DC components will be created.
###########################################################################
def generateCursorCombinations(cursorCount, signalingMode, modulation, levelNumb):
ISI = nothing()
# Create combinations with DC component
if signalingMode in ['standard', '1+D', '1+0.5D']:
for combination in range(modulation**cursorCount):
baseM = np.base_repr(combination, base=modulation) # create base-M value from combination
vector = np.zeros((cursorCount-len(baseM),))
for number in baseM:
vector = np.concatenate((vector, [float(number)]))
symbolName = 'c'
for number in vector:
symbolName = '{0:s}{1:d}'.format(symbolName, int(number))
polar = np.interp(vector, [0, modulation-1], [-1,1]) # gets polar [-1 1] base-M vector
setattr(ISI, symbolName, nothing())
ISI.__dict__[symbolName].cursors = polar
# Create combinations without DC component
else:
for level in range(levelNumb):
vector = np.ones((cursorCount,))*level
for index in np.arange(1,cursorCount, 2):
vector[index] = levelNumb-1-vector[index] # invert every other bit
polar = np.interp(vector, [0,levelNumb-1],[-1,1]) # gets polar [-1 1] base-M vector
symbolName = 'c'
for number in vector:
symbolName = '{0:s}{1:d}'.format(symbolName, int(number))
setattr(ISI, symbolName, nothing())
ISI.__dict__[symbolName].cursors = polar
return ISI
###########################################################################
# This function classifies all trajectories based on the pre, main and post
# cursor transitions. All three are required for generating edge BER plots.
# It should be noted that the order of cursors is reversed to account for
# time.
###########################################################################
def clasifyTrajectories(ISI, preCursorCount, signalingMode):
classifiedISI = nothing()
for name in ISI.__dict__:
preCursor = name[preCursorCount+0]
mainCursor = name[preCursorCount+1]
postCursor = name[preCursorCount+2]
if signalingMode in ['1+D', '1+0.5D']:
if not ('trans'+postCursor+mainCursor) in classifiedISI.__dict__:
setattr(classifiedISI, ('trans'+postCursor+mainCursor), nothing())
if not name in classifiedISI.__dict__[('trans'+postCursor+mainCursor)].__dict__:
setattr(classifiedISI.__dict__[('trans'+postCursor+mainCursor)], name, nothing())
classifiedISI.__dict__[('trans'+postCursor+mainCursor)].__dict__[name].cursors = ISI.__dict__[name].cursors
else:
if not ('trans'+postCursor+mainCursor+preCursor) in classifiedISI.__dict__:
setattr(classifiedISI, ('trans'+postCursor+mainCursor+preCursor), nothing())
if not name in classifiedISI.__dict__[('trans'+postCursor+mainCursor+preCursor)].__dict__:
setattr(classifiedISI.__dict__[('trans'+postCursor+mainCursor+preCursor)], name, nothing())
classifiedISI.__dict__[('trans'+postCursor+mainCursor+preCursor)].__dict__[name].cursors = ISI.__dict__[name].cursors
return classifiedISI
###########################################################################
# This function splits the pulse response into symbols and returns them
# in a structure categorized by name.
###########################################################################
def splitPulse(pulse, preCursorCount, postCursorCount, samplesPerSymb):
splitPulse = nothing()
# Split pre-cursors
index = 0
for cursor in np.flip(np.arange(preCursorCount)+1):
splitPulse.__dict__['pre' + str(cursor)] = pulse[index:index+samplesPerSymb]
index = index + samplesPerSymb
# Split main-cursor(s)
splitPulse.__dict__['main'] = pulse[index:index+samplesPerSymb]
index = index + samplesPerSymb
# Split post-cursors
for cursor in range(postCursorCount):
splitPulse.__dict__['post'+str(cursor)] = pulse[index:index+samplesPerSymb]
index = index + samplesPerSymb
return splitPulse
###########################################################################
# This function applies the cursor combinations to the split pulse
# response. This creates all possible signal trajectories due to ISI.
###########################################################################
def applyCursorCombination(ISI, splitPulse, samplesPerSymb):
# Loop through transitions
cursors = list(splitPulse.__dict__)
for transName in ISI.__dict__:
# Loop through combinations
for comb in ISI.__dict__[transName].__dict__:
# Loop through cursors while superimposing transformation
vector = ISI.__dict__[transName].__dict__[comb].cursors
ISI.__dict__[transName].__dict__[comb].trajectory = np.zeros((samplesPerSymb,))
for pos in range(len(vector)):
offset = splitPulse.__dict__[cursors[pos]] * vector[pos] # multiply cursor by data levels
ISI.__dict__[transName].__dict__[comb].trajectory = ISI.__dict__[transName].__dict__[comb].trajectory + offset