Return Scalar Measurement

This script can control N1010A FlexDCA on a PC or a DCA-X. This script opens a connection to FlexDCA, installs a simulated module and, in Eye mode, returns NRZ eye height measurement. The following diagram shows a Script-on-PC connection. In this configuration any DCA-M module must be connected to the DCA-X before it can be controlled. DCA-M modules connected to the PC cannot be seen.

Example Script

Copy
return-scalar-measurement.py
""" Script on PC controls FlexDCA or DCA-X from PC).
This script opens a connection to FlexDCA, installs a simulated module
and, in Eye mode, returns NRZ eye height measurement.
"""

import pyvisa as visa  # import VISA library
import time

ADDRESS = 'TCPIP0::localhost::hislip0,4880::INSTR'
# ADDRESS = 'TCPIP0::K-86100D-00003::hislip0,4880::INSTR'  # DCA-X
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.
    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.  """
    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)


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 measure_eye_height(FlexDCA):
    """ Makes an eye height measurement. """
    FlexDCA.write(':MEASure:EYE:LIST:CLEar')
    FlexDCA.write(':MEASure:EYE:EHEight')
    meas = return_scalar(FlexDCA, ':MEASure:EYE:EHEight?')
    meas = eng_notation(meas, 0.01)
    print('Eye Height: ' + meas + 'V')


FlexDCA = open_flexdca_connection(ADDRESS)
FlexDCA.query(':SYSTem:DEFault;*OPC?')
all_channels_off(FlexDCA)
install_simulated_module(FlexDCA, CHANNEL, 'DEM')
FlexDCA.write(':ACQuire:RUN')
FlexDCA.query(':SYSTem:MODE EYE;*OPC?')  # Switch to Eye/Mask mode
FlexDCA.query(':SYSTEM:AUToscale;*OPC?')
measure_eye_height(FlexDCA)
FlexDCA.write(':SYSTem:GTLocal')
FlexDCA.close()