Tracking markers
This Python example, tracking_markers.py, demonstrates using tracking markers in Oscilloscope mode. A simulated module and waveform is used instead of an DCA-X. Python 3 supports unicode, so a Greek symbol is shown in output from a print statement.
Example Script
Copy
tracking-markers.py
# -*- coding: utf-8 -*-
""" Installs simulated module and demonstrates using tracking markers on a simulated waveform.
Python 3 is unicode, so a Greek symbol is shown in output from a print statement.
"""
import pyvisa as visa # import VISA library
ADDRESS = 'TCPIP0::localhost::hislip0,4880::INSTR'
CHANNEL = '5A'
def open_flexdca_connection(address):
""" Opens visa connection to FlexFlexDCA. """
print('Connecting to Flexdca ...')
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)
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.
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 waveform. 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.
"""
flexdca.write('*CLS')
flexdca.query(':SYSTem:DEFault;*OPC?')
all_channels_off(flexdca)
install_simulated_module(flexdca, channel, 'DEM')
flexdca.write(':CHAN'+channel+':COLor TCOLor2')
flexdca.write(':CHANnel' + channel + ':DISPlay ON')
flexdca.query(':SYSTem:MODE OSCilloscope;*OPC?')
flexdca.query(':TRIGger:PLOCk ON;*OPC?')
flexdca.query(':SYSTem:AUToscale;*OPC?')
flexdca.write(':ACQuire:EPATtern ON') # Acquire entire pattern
flexdca.write(':ACQuire:SPBit:MODe MANual') # Select samples/bit
flexdca.write(':ACQuire:SPBit 31.99212598') # 32 samples/bit
def create_math_operator(flexdca, channel): # Function Apply S2P
""" Creates a "Bessel" waveform signal processing function.
The required s2p file is selected from the demo folder as it is
always available for this example. You can substitute your own file.
"""
flexdca.write(':FUNCtion1:FOPerator BESSel')
flexdca.write(':SPRocess1:BESSel:BANDwidth 4.000E+9')
flexdca.write(':FUNCtion1:OPERand1 CHAN' + channel)
flexdca.write(':FUNCtion1:COLor TCOLor4') # Color for Function 1
flexdca.write(':FUNCtion1:DISPlay ON') # Turn on Function 1
flexdca.query(':SYSTem:AUToscale;*OPC?')
def run_acquisition_limit_test(flexdca):
""" Configures and runs acquisition limit test.
Sixteen patterns are acquired. For pattern smoothing, patterns acquired
equal patterns smoothed.
"""
PatternCount = '16'
flexdca.write(':ACQuire:SMOothing AVERage')
flexdca.write(':LTESt:ACQuire:CTYPe:PATTerns ' + PatternCount)
flexdca.write(':ACQuire:ECOunt ' + PatternCount)
flexdca.write('ACQuire:SINGle')
flexdca.write(':ACQuire:CDISplay') # Clear acquired data
flexdca.query(':LTESt:ACQuire:STATe ON;*OPC?')
flexdca.timeout = 60000 # 60 seconds
print('Acquiring ' + PatternCount + ' patterns.', flush=True)
flexdca.query(':ACQuire:RUN;*OPC?')
flexdca.write(':LTESt:ACQuire:STATe OFF')
def find_symbol_sequence(flexdca, channel):
flexdca.write(':ACQuire:STOP')
flexdca.query(':ACQuire:SINGle;*OPC?')
flexdca.write(':TIMebase:UNITs UINTerval')
flexdca.write(':TIMebase:FIND:SIGNal CHAN' + channel)
flexdca.write(':TIMebase:FIND:SEQuence "0101"')
i = float(flexdca.query(':TIMebase:UIPosition?'))
pattern_start = int(i)
flexdca.write(':TIMebase:FIND:NEXT') # find first bit sequence
i = float(flexdca.query(':TIMebase:UIPosition?'))
ui_offset = int(i)
ui = ui_offset - pattern_start
if ui == 0:
print('The symbol sequence was not found.')
else:
print('The symbol sequence was found.')
def create_measurement_regions(flexdca):
""" Turns on two measurement regions."""
flexdca.write(':MEASure:REGions:STATe ON')
flexdca.write(':MEASure:REGions:COUNt 2')
flexdca.write(':MEASure:REGions:ALIGn')
def create_markers(flexdca, channel):
""" Turns on 4 tracking markers and positions them. """
# Marker 1 on channel maximum amplitude region 1
flexdca.write(':MEASure:OSCilloscope:TMAXimum:SOURce1 CHAN' + channel)
flexdca.write(':MEASure:OSCilloscope:TMAXimum:REGion REGion1')
s = flexdca.query(':MEASure:OSCilloscope:TMAXimum?')
flexdca.write(':MARKer:Y1A:STATe TRACk')
flexdca.write(':MARKer:X1:POSition ' + s)
# Marker 2 on function maximum amplitude region 1
flexdca.write(':MEASure:OSCilloscope:TMAXimum:SOURce1 FUNCtion1')
flexdca.write(':MEASure:OSCilloscope:TMAXimum:REGion REGion1')
s = flexdca.query(':MEASure:OSCilloscope:TMAXimum?')
flexdca.write(':MARKer:Y2A:STATe TRACk')
flexdca.write(':MARKer:X2:SOURce FUNCtion1')
flexdca.write(':MARKer:X2:POSition ' + s)
# Marker 3 on channel maximum amplitude region 2
flexdca.write(':MEASure:OSCilloscope:TMAXimum:SOURce1 CHAN' + channel)
flexdca.write(':MEASure:OSCilloscope:TMAXimum:REGion REGion2')
s = flexdca.query(':MEASure:OSCilloscope:TMAXimum?')
flexdca.write(':MARKer:Y3A:STATe TRACk')
flexdca.write(':MARKer:X3:POSition ' + s)
# Marker 4 on function maximum amplitude region 2
flexdca.write(':MEASure:OSCilloscope:TMAXimum:SOURce1 FUNCtion1')
flexdca.write(':MEASure:OSCilloscope:TMAXimum:REGion REGion2')
s = flexdca.query(':MEASure:OSCilloscope:TMAXimum?')
flexdca.write(':MARKer:Y4A:STATe TRACk')
flexdca.write(':MARKer:X4:SOURce FUNCtion1')
flexdca.write(':MARKer:X4:POSition ' + s)
flexdca.write(':DISPlay:MARKER:DELTas ON')
def clean_display(flexdca):
""" Turns off display annotations and measurement regions. """
flexdca.write(':MEASure:ANNotations:STATe OFF')
flexdca.write(':MEASure:REGions:STATe OFF')
def show_results(flexdca, channel):
""" Displays marker values for high symbol in symbol sequence. """
DELTAU = '\u0394' # Δ
print('-' * 40)
print('\nResults For "0101" symbol Sequence')
print('-' * 40)
print('For first high symbol:')
print(' '*24, 'Time'.ljust(11, ' '), 'Amplitude')
s = build_table_rows('input waveform (' +
channel + '):', get_marker_values(flexdca, 1))
print(s)
s = build_table_rows('output waveform (F1):',
get_marker_values(flexdca, 2))
print(s)
s = build_table_rows(DELTAU + ':', get_marker_deltas(flexdca, 2))
print(s)
print('\nFor second high symbol:')
s = build_table_rows('input waveform (' +
channel + '):', get_marker_values(flexdca, 3))
print(s)
s = build_table_rows('output waveform (F1):',
get_marker_values(flexdca, 4))
print(s)
s = build_table_rows(DELTAU + ':', get_marker_deltas(flexdca, 4))
print(s)
print()
def eng_notation(numstring, resolution):
""" Converts a string in scientific notation to engineering notation.
Unit multiplier character is appended to in final return string.
Args
====
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
=======
Str 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_marker_values(flexdca, marker):
s = str(marker)
x = flexdca.query(':MARKer:X' + s + ':POSition?')
y = flexdca.query(':MEASure:MARKer:Y' + s + 'A?')
return (eng_notation(x, '1.000'), eng_notation(y, '1.000'))
def get_marker_deltas(flexdca, marker):
s = str(marker - 1)
flexdca.write(':MARKer:REFerence X' + s)
x = flexdca.query(':MEASure:MARKer:DX' + str(marker) + '?')
y = flexdca.query(':MEASure:MARKer:DY' + str(marker) + 'A?')
return (eng_notation(x, '1.000'), eng_notation(y, '1.000'))
def build_table_rows(name, values):
x = values[0] + 's'
x = x.ljust(12, ' ')
y = values[1] + 'V'
return name.rjust(23, ' ') + ' ' + x + y
FlexDCA = open_flexdca_connection(ADDRESS)
configure_FlexDCA(FlexDCA, CHANNEL)
create_math_operator(FlexDCA, CHANNEL)
run_acquisition_limit_test(FlexDCA)
find_symbol_sequence(FlexDCA, CHANNEL)
create_measurement_regions(FlexDCA)
create_markers(FlexDCA, CHANNEL)
clean_display(FlexDCA)
show_results(FlexDCA, CHANNEL)
FlexDCA.write(':SYSTem:GTLocal')
FlexDCA.close()