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