Binary transfer of eye diagram data
This Python example, binary_transfer_eye.py, performs a binary transfer of an eye diagram waveform. The resulting data is shown in a tkinter GUI window. If you do not wish to import matplotlib library, comment out the The draw_graph() function.
The main purpose of this program is to demonstrate how to return binary eye diagram data with the graph used as a visual confirmation that the data was correctly interpreted. The example works with FlexDCA offline and installs simulated modules. No hardware is required. This script imports tkinter and matplotlib to draw a graph.
As shown in the following code, query_binary_values() method, is used to return the data.
eyedata = FlexDCA.query_binary_values(message, datatype='L', container=list, is_big_endian=False, header_fmt='ieee')
As described in :WAVeform:EYE:INteger:DATa?
, the returned data is 391,271 points (1,565,084 bytes). This array corresponds to the graticule display, where each point contains a sample hit count. The array is transferred column by column, starting with the lower left corner of the graticule.
The following picture shows an example of the displayed graph.
Example Script
binary-transfer-eye.py
# -*- coding: utf-8 -*-
""" Installs a simulated module to supply waveform.
This program transfers binary eye data for a channel to the PC.
The eye format data (color grade/gray scale database values) are
returned in binary format using ':WAVeform:EYE:INTeger:DATa?'.
The draw_graph() function shows a graph of the data in a tkinter GUI window.
If you do not wish to import matplotlib library, comment out this function.
"""
import pyvisa as visa # import VISA library
import sys
ADDRESS = 'TCPIP0::localhost::hislip0,4880::INSTR'
CHANNEL = '5A'
def open_flexdca_connection(address):
""" Opens visa connection to FlexFlexDCA. """
try:
rm = visa.ResourceManager()
connection = rm.open_resource(address)
connection.timeout = 20000 # Set connection timeout to 20s
connection.read_termination = '\n'
connection.write_termination = '\n'
inst_id = connection.query('*IDN?')
print('\nFlexDCA connection established to:\n' + inst_id, flush=True)
connection.write(':SYSTem:DEFault')
connection.query('*OPC?')
except (visa.VisaIOError, visa.InvalidSession):
print('\nVISA ERROR: Cannot open instrument address.\n', flush=True)
return None
except Exception as other:
print('\nVISA ERROR: Cannot connect to instrument:', other, flush=True)
print('\n')
return None
return connection
def all_channels_off(flexdca):
""" Turns all available channels off. """
for slot in '12345678':
for letter in 'ABCD':
channel = slot + letter
flexdca.write(':CHANnel' + channel + ':DISPlay OFF')
def install_simulated_module(flexdca, channel, model, signal='NRZ'):
""" Simplified installation of a simulated FlexDCA module.
channel
Simulated module's channel to use. Installation slot is
derived from channel.
model
Type of simulated module. "DEM" is a dual-electrical module.
"QEM" is a quad-electrical module. "DOM" is a dual-optical
module. "QEM" is a electrical-optical module.
signal
Format of signal. NRZ or PAM4.
"""
slot = channel[0]
flexdca.write(':EMODules:SLOT' + slot + ':SELection ' + model)
if signal in 'NRZ':
flexdca.write(':SOURce' + channel + ':FORMat NRZ')
else:
flexdca.write(':SOURce' + channel + ':FORMat PAM4')
flexdca.write(':SOURce' + channel + ':DRATe 9.95328E+9')
flexdca.write(':SOURce' + channel + ':WTYPe DATA')
flexdca.write(':SOURce' + channel + ':PLENgth 127')
flexdca.write(':SOURce' + channel + ':AMPLitude 90E-3')
flexdca.write(':SOURce' + channel + ':NOISe:RN 3.0E-6')
flexdca.write(':SOURce' + channel + ':JITTer:RJ 4.0E-12')
flexdca.write(':CHANnel' + channel + ':DISPlay ON')
def configure_FlexDCA(flexdca, channel):
""" Installs a simulated module and prepares FlexDCA for measurements. """
print('Configuring FlexDCA.', flush=True)
flexdca.query(':SYSTem:DEFault;*OPC?')
all_channels_off(flexdca)
install_simulated_module(flexdca, channel, 'DEM')
flexdca.write(':CHAN' + channel + ':DISPlay ON')
flexdca.write(':ACQuire:RUN')
flexdca.write(':SYSTem:MODE EYE')
flexdca.query(':SYSTem:AUToscale;*OPC?')
def waveform_acquisition(flexdca, count):
""" Perform an acquisition limit test to capture waveform data. """
flexdca.write(':ACQuire:STOP') # single acquisition mode
flexdca.write(':ACQuire:CDISplay') # Clear display
if '1' in flexdca.query(':TRIGger:PLOCk?'): # pattern lock is on
flexdca.write(':LTESt:ACQuire:CTYPe:PATTerns ' + str(count))
else:
flexdca.write(':LTESt:ACQuire:CTYPe:WAVeforms ' + str(count))
flexdca.write(':LTESt:ACQuire:STATe ON')
flexdca.query(':ACQuire:RUN;*OPC?') # OPC required when limit testing
flexdca.write(':LTESt:ACQuire:STATe OFF')
def eng_notation(numstring, resolution):
""" Converts a string in scientific notation to engineering notation.
Unit multiplier character is appended to in final return string.
numstring
Number string for conversion. For example, '12.3e-4'.
resolution
An real that indicates number of decimal places in
the result. For example, '0.01' would give '1.23 m'
Returns string representing number with multiplier character.
"""
import decimal as dc
MU = '\u03BC' # μ
if numstring in '9.91E+37':
return ''
dc.getcontext().prec = 10 # prevent limiting of decimal places
multiplier = {12: 'T', 9: 'G', 6: 'M', 3: 'k', 0: '', -3: 'm',
-6: MU, -9: 'n', -12: 'p', -15: 'f'}
numberReal = dc.Decimal(numstring)
# absolute value is not between 0 and 1 (fraction)
if abs(numberReal) >= 1:
exponentMult = 0
while int(numberReal) != 0:
numberReal = numberReal / 1000
exponentMult += 1
numberReal *= 1000
exponentMult -= 1
elif (abs(numberReal) > 0) and (abs(numberReal) < 1): # fraction
exponentMult = 0
while int(numberReal) == 0:
numberReal = numberReal * 1000
exponentMult += 1
exponentMult *= -1
elif numberReal == 0: # number must be zero
exponentMult = 0
exponentMult *= 3
if exponentMult == -15:
n = numberReal.quantize(dc.Decimal('1'))
else:
n = numberReal.quantize(dc.Decimal(str(resolution)))
return str(n) + ' ' + multiplier[exponentMult]
def get_eye_info(flexdca, channel):
""" Returns information about eye diagram. """
label = {}
datasize = {}
print('\nInformation on waveform:', flush=True)
flexdca.write(':WAVeform:SOURce CHANnel' + channel)
# xmin label on graph
xmin = flexdca.query(':WAVeform:EYE:XORigin?')
s = eng_notation(xmin, '0.2') + 's'
print('\tX origin: ', s)
label['xmin'] = s
# ymin label on graph
ymin = flexdca.query(':WAVeform:EYE:YORigin?')
s = eng_notation(ymin, '0.2') + 'V'
print('\tY origin: ', s)
label['ymin'] = s
# time spacing is delta time between columns
timespacing = flexdca.query(':WAVeform:EYE:XINCrement?')
s = eng_notation(timespacing, '0.2') + 's'
print('\tTime Spacing: ', s)
# row spacing is delta amplitude between rows
rowspacing = flexdca.query(':WAVeform:EYE:YINCrement?')
s = eng_notation(rowspacing, '0.2') + 'V'
print('\tRow Spacing: ', s)
# number of data rows and columns
s = flexdca.query(':WAVeform:EYE:ROWS?')
datasize['rows'] = int(s)
print('\tNumber of rows: ', s)
s = flexdca.query(':WAVeform:EYE:COLumns?')
datasize['columns'] = int(s)
print('\tNumber of Columns: ', s)
# Calculate x and y max labels on graph
s = str(float(xmin) + (float(timespacing) * float(datasize['columns'])))
s = eng_notation(s, '0.2') + 's'
label['xmax'] = s
s = str(float(ymin) + (float(rowspacing) * float(datasize['rows'])))
s = eng_notation(s, '0.2') + 'V'
label['ymax'] = s
return label, datasize
def get_binary_eye_data(flexdca, channel):
""" Returns the data points for an eye diagram by transferring binary data to computer.
"""
print('Returning eye waveform data.', flush=True)
flexdca.read_termination = ''
flexdca.write_termination = ''
endiansetting = flexdca.query(':SYSTem:BORDER?')
flexdca.write(':SYSTem:BORDER LENDian')
message = ':WAVeform:EYE:INTeger:DATa?'
eyedata = list()
eyedata = flexdca.query_binary_values(message, datatype='L', container=list, is_big_endian=False, header_fmt='ieee')
flexdca.write(':SYSTem:BORDER ' + endiansetting)
flexdca.read_termination = '\n'
flexdca.write_termination = '\n'
return eyedata
def draw_graph(graph_data, label, datasize, channel):
""" Plots binary data on tkinter window and saves graphics file to show that we received the
data from FlexDCA. Data is not amplitude and time values but rather, for
each database row and column, an integer count of number of hits.
A 0 value indicates not hits for the point.
"""
import tkinter
from matplotlib.backends.backend_tkagg import (
FigureCanvasTkAgg, NavigationToolbar2Tk)
from matplotlib.figure import Figure
root = tkinter.Tk()
root.wm_title("Graph of Eye Width")
print('Drawing graph.', flush=True)
fig = Figure(figsize=(10.0, 8.0), dpi=96, edgecolor='black') # width and height in inches
ax = fig.add_subplot(111)
ax.set_xlim(0, 10)
ax.set_ylim(0, 8)
ax.set_aspect('0.8')
ax.grid(which='major', axis='both', linewidth=0.75,
linestyle='-', color='0.75')
ax.set_xticks([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10])
ax.set_yticks([0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0])
ax.set_xticklabels([label['xmin'], '', '', '', '', '', '', '',
'', '', label['xmax']])
ax.set_yticklabels([label['ymin'], '', '', '', '0', '', '',
'', label['ymax']])
ax.set_ylabel('Voltage', fontsize=12)
ax.set_xlabel('Time', fontsize=12)
fig.suptitle('Channel ' + channel + ' Eye Diagram',
y=0.85, fontsize=12)
x = [] # scaled point x position
y = [] # scaled point y position
n = 0 # list index
col = 0
rows = datasize['rows']
columns = datasize['columns']
while col < columns:
row = 0
while row < rows:
point = graph_data[n]
if point: # point has hits
x.append(round((col / columns * 10), 3)) # scaled x location
y.append(round((row / rows), 3) * 8) # scaled y location
row += 1
n += 1
col += 1
ax.scatter(x, y, s=2, color='green', alpha=1, marker='.')
canvas = FigureCanvasTkAgg(fig, master=root)
canvas.draw()
toolbar = NavigationToolbar2Tk(canvas, root)
toolbar.update()
button = tkinter.Button(master=root, text="Quit", command=root.quit)
button.pack(side=tkinter.BOTTOM)
toolbar.pack(side=tkinter.BOTTOM, fill=tkinter.X)
canvas.get_tk_widget().pack(side=tkinter.TOP, fill=tkinter.BOTH, expand=1)
print('Graph of returned data is displayed on separate window.')
sys.stdout.flush() # Flush print message in stdout
tkinter.mainloop()
FlexDCA = open_flexdca_connection(ADDRESS)
configure_FlexDCA(FlexDCA, CHANNEL)
waveform_acquisition(FlexDCA, 100)
label, datasize = get_eye_info(FlexDCA, CHANNEL)
graph_data = get_binary_eye_data(FlexDCA, CHANNEL)
draw_graph(graph_data, label, datasize, CHANNEL)
FlexDCA.write(':SYSTem:GTLocal')
FlexDCA.close()