Example 5. A User Measurement
Meas. mode:
Scope
Eye
TDR
Package License:
L-RND
L-SNT
This is the first example that returns an actual user measurement, PreEmphasis. This pre-emphasis algorithm is not meant to be a recommendation on how to perform this measurement. We simply modify dependent measurement results and calculate a new measurement as shown here:
Result = 20 * math.log10(Vpp['Result'] / Vamptd['Result'])
After you've been successful with this example, try Example 6. Adding Functions.
Required Files
PreEmphasis-setup.py
This script configures FlexDCA to required state for the example script. The script installs a simulated module, selects the proper FlexDCA mode, and turns on two dependent measurements: Amplitude and Peak-Peak Amplitude.
Copy
PreEmphasis-setup.py
import pyvisa as visa # import VISA library
visa_address = 'TCPIP0::localhost::hislip0,4880::INSTR'
CHANNEL = '1A'
TIMEOUT = 10000
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 = TIMEOUT # 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.
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 return_scalar(FlexDCA, query):
""" Returns a FlexDCA scalar measurement result. """
import time
class ScalarMeasQuestionableException(Exception):
""" A scalar measurement result is questionable. """
pass
class ScalarMeasTimeOutException(Exception):
""" A scalar measurement has timed out. """
pass
timeout = FlexDCA.timeout / 1000 # convert ms to s
query = query.strip('?')
start_time = time.time() # time in seconds
while(True):
time.sleep(0.2)
status = FlexDCA.query(query + ':STATus?')
if 'CORR' in status: # valid measurement result
return FlexDCA.query(query + '?') # get measurement
elif 'QUES' in status: # questionable results
s = query + '\nReason: ' + FlexDCA.query(query + ':STATus:REASon?')
raise ScalarMeasQuestionableException(s)
elif (int(time.time() - start_time)) > timeout: # IO timout
s = query + '\nReason: ' + FlexDCA.query(query + ':STATus:REASon?')
raise ScalarMeasTimeOutException(s)
flexdca = open_flexdca_connection(visa_address)
flexdca.query(':SYSTem:DEFault;*OPC?')
all_channels_off(flexdca)
install_simulated_module(flexdca, CHANNEL, 'DEM')
flexdca.write(':ACQuire:RUN')
flexdca.query(':SYSTem:MODE OSCilloscope;*OPC?') # Switch to Eye/Mask mode
flexdca.query(':SYSTEM:AUToscale;*OPC?')
flexdca.query(':TRIGger:PLOCk ON;*OPC?')
flexdca.write(':ACQuire:RLENgth:MODE MANual')
flexdca.write(':ACQuire:RLENgth 1024')
flexdca.write(':ACQuire:SMOothing AVERage')
flexdca.write(':TIMebase:SCALe 5.000E-10')
flexdca.write(':MEASure:OSCilloscope:VAMPlitude')
flexdca.write(':MEASure:OSCilloscope:VPP')
flexdca.write(':SYSTem:GTLocal')
flexdca.close()
PreEmphasis.xml
Installs user measurement.
Copy
PreEmphasis.xml
<?xml version="1.0" encoding="utf-8"?>
<Measurement xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
Application="FlexDCA" MinimumSoftwareVersion="5.00.328">
<Name>Pre-Emphasis</Name>
<Abbreviation>PreEmp</Abbreviation>
<Comments>In Scope mode, measures pre-emphasis on a signal.</Comments>
<Script>PreEmphasis.py</Script>
<Icon></Icon>
<MeasurementType>1 Source</MeasurementType>
<ValidMode>Scope</ValidMode>
<Dependent Name = "Amplitude">
<SourceIndex>1</SourceIndex>
</Dependent>
<Dependent Name = "Peak-Peak">
<SourceIndex>1</SourceIndex>
</Dependent>
<Variable Name = "MinSampl" Value = "1000" />
<Variable Name = "MaxSampl" Value = "2000" />
</Measurement>
PreEmphasis.py
User measurement script.
Copy
PreEmphasis.py
import math
def algorithm(inputVars):
""" Initialize variables returned to FlexDCA. Set Status to
Correct only when Result is computed. """
Result = 0.0
Units = 'dB'
# Status is set to 'Correct' after Result is computed.
statusDict = {'Status': 'Invalid', 'ErrorMsg': ''}
# Verify MeasData list of measurement dictionaries exists.
if 'MeasData' not in inputVars:
statusDict['ErrorMsg'] = 'Unable to retrieve any measurement data.\n' \
'Please use Oscilloscope Mode and load the XML configuration file ' \
'rather than loading the Python measurement script directly.'
return statusDict
# Do nothing if there is no SrcData.
SrcData = inputVars['SrcData']
num_samples = len(SrcData)
if num_samples == 0:
statusDict['ErrorMsg'] = 'No waveform data.'
return statusDict
# Specify max and min number of samples for measurement.
# This demonstrates defining your own variables in XML configuration file.
MinSampl = int(inputVars['MinSampl'])
MaxSampl = int(inputVars['MaxSampl'])
# Iterate through MeasData to find V p-p and V amptd
Source = inputVars['Source']
MeasData = inputVars['MeasData']
Vpp, Vamptd = None, None
for meas in MeasData:
if (meas['Source1'] == Source):
if (meas['Name'] == 'Peak-Peak'):
Vpp = meas # Vpp is dictionary of measurement info
elif (meas['Name'] == 'Amplitude'):
Vamptd = meas # Vamptd is dictionary of measurement info
# Verify required measurements have been made.
if (Vpp is None) or (Vamptd is None):
statusDict['ErrorMsg'] = 'Unable to retrieve "Peak-Peak" and "Amplitude" measurements.\n' \
'Use Oscilloscope Mode and include these measurements as ' \
'Dependents in your XML configuration file.'
return statusDict
# Need to have status of V p-p and V amptd carry through
if (Vpp['Status'] == 'Correct') and (Vamptd['Status'] == 'Correct'):
statusDict['Status'] = 'Correct'
elif (Vpp['Status'] == 'Invalid') or (Vamptd['Status'] == 'Invalid'):
statusDict['ErrorMsg'] = 'Dependent measurements have invalid statuses.'
return statusDict
else:
statusDict['Status'] = 'Questionable'
statusDict['ErrorMsg'] = 'Dependent measurements have questionable statuses.'
# Check sample requirements
if (num_samples < MinSampl) or (num_samples > MaxSampl):
Status = 'Questionable'
ErrorMsg = 'The number of samples per acquisition is not within the specified ' \
'range of {0} to {1}.\n Please edit measurement script or change ' \
'the number of samples per acquisition.'.format(MinSampl, MaxSampl)
# Compute result
Result = 20 * math.log10(Vpp['Result'] / Vamptd['Result'])
# Return dictionary of results to FlexDCA
return { 'Result' : Result, # Calculated scalar result
'Units' : Units, # Measurement units
'Status' : statusDict['Status'], # Status string 'Correct', 'Questionable', or 'Invalid'
'ErrorMsg' : statusDict['ErrorMsg'] } # Error message string