Measure Eye Width Over Time

This Python example, measure-eye-width-over-time-graph-results.py, demonstrates monitoring a measurement (Eye Width) over time. The resulting data is shown in a matplotlib window. If you do not wish to import the matplotlib library, comment out the draw_graph() function.

The function, monitor_eye_width(), records the data and the create_time_data() function scales the data for the correct time units: seconds, minutes, or hours.

Example Script

measure-eye-width-over-time-graph-results.py

#!python3

"""This program demonstrates an Eye Width measurement that is
monitored over time with the Eye Width measured every interval.
The draw_graph() function shows a graph of the data in a
matplotlib GUI window.
"""

# Import modules.
import pyvisa
import sys
import time
from decimal import Decimal
import matplotlib.pyplot as plt

# Global variables.
oscilloscope_visa_address = "TCPIP0::localhost::hislip0,4880::INSTR"
timeout = 20000  # 20 seconds.
meas_minutes = 1  # Monitor eye with for 1 minute.
meas_interval = 2.0  # Time in seconds between each measurement.


def exit_program():
    """Exit program."""

    # Close connection to oscilloscope.
    sys.stdout.write("Closing oscilloscope connection.\n")
    Infiniium.write(":SYSTem:GTLocal")  # Unlock GUI.
    Infiniium.clear()  # Clear oscilloscope communications interface.
    Infiniium.close()  # Close communications interface to oscilloscope.
    rm.close()  # Close resource manager.
    print("End of program.")
    sys.exit()


def check_error_queue(when):
    """Check error queue."""

    errors_found = False
    while True:
        # Keep reading errors until "No error".
        error_string = Infiniium.query(":SYSTem:ERRor:NEXT? DIAGnostic")
        if error_string:  # If there is an error string value.

            if error_string.find("0,", 0, 2) == -1:  # Not "No error".
                errors_found = True
                print(f"ERROR: {error_string}")

            else:  # "No error"
                break

        else:  # :SYSTem:ERRor:NEXT? DIAGnostic should always return string.
            errors_found = True
            print("ERROR: :SYSTem:ERRor:NEXT? DIAGnostic returned nothing.")
            break

    if errors_found:
        print(f"Exited because error(s) found when: '{when}'")
        exit_program()


def monitor_eye_width(meas_minutes, meas_interval):
    """Monitors the eye width for the required of minutes by
    performing a measurement every interval.
    meas_minutes (int).
    meas_interval (float).
    """

    print(f"Monitoring Eye Width. Time required: {meas_minutes * 60} seconds.")
    print("Please wait...")
    data_length = int((meas_minutes * 60) / meas_interval)  # Number of data points.
    data = [0 for x in range(data_length)]
    data_pt = 0
    while data_pt < data_length:
        Infiniium.query(":ACQuire:SINGle;*OPC?")
        reading = float(Infiniium.query(":MEASure:EYE:EWIDth?"))
        width = Decimal(reading / 1e-12)
        data[data_pt] = width.quantize(Decimal("0.01"))
        data_pt += 1
        time.sleep(meas_interval)
        if data_pt % 50:
            print(" .", end="")
    print("\n")

    return data


def create_time_data(number_of_data_points, meas_minutes, meas_interval):
    """Creates list of time data and scales it for seconds,
    minutes, or hours units. The units (xunits) is returned.
    The argument, number_of_data_points, is an int.
    The meas_minutes argument is the total test time (int).
    The meas_interval argument is the seconds (float) between data points.
    """

    xunits = ""
    time_data = []  # list of times for each data point
    if 0 < meas_minutes < 5:  # 5 minutes
        time_data = [(i * meas_interval) for i in range(0, number_of_data_points)]
        xunits = "seconds"
    elif 5 <= meas_minutes < 180:  # 5 to 180 minutes (3 hours)
        time_data = [(i * meas_interval / 60) for i in range(0, number_of_data_points)]
        xunits = "minutes"
    else:  # >= 3 hours
        time_data = [
            (i * meas_interval / 3600) for i in range(0, number_of_data_points)
        ]
        xunits = "hours"

    return time_data, xunits


def draw_graph(y_data, x_data, xunits, measurement, channel):
    """A matplotlib graph is created and saved to a file in the
    default folder.
    y_data argument is a list of Decimals of Eye Width readings.
    x_data argument is corresponding time positions.
    xunits is 'seconds', 'minutes', or 'hours'.
    channel argument is a string. for example, 'ECHannel2_8'.
    """

    print("Drawing graph.", flush=True)
    fig, ax = plt.subplots()
    ax.set_title(f"Graph of {measurement}")
    ax.grid(visible=True, which="major", axis="both")
    ax.set_xlabel(xunits.capitalize())
    ax.set_ylabel(f"{measurement} (Time in ps)")
    s = f"Channel {channel} {measurement} over Time"
    s += f"\n (Test time: {str(round(x_data[-1], 1))} {xunits})"
    fig.suptitle(s, y=0.85, fontsize=10)

    if len(y_data) < 20:
        ax.plot(x_data, y_data, "g.-")  # Data points shown.
    else:
        ax.plot(x_data, y_data, "g-")  # Data points hidden. Line only.

    print("Graph of returned data is displayed in a separate window on PC.")
    sys.stdout.flush()  # Flush print message in stdout
    plt.show()


# ==========================================================
# Main program:
# ==========================================================

# Connect and initialize oscilloscope.
rm = pyvisa.ResourceManager("C:\\Windows\\System32\\visa64.dll")
try:
    Infiniium = rm.open_resource(oscilloscope_visa_address)
except Exception:
    print(
        f"Unable to connect to oscilloscope at {oscilloscope_visa_address}. Aborting program."
    )
    sys.exit()

Infiniium.timeout = timeout  # Set global timeout.
Infiniium.clear()  # Clear the instrument bus.
# Lock GUI happens automatically.

# Default Setup.
print("Default Setup.")
Infiniium.write(":SYSTem:DEFault")

# # Use auto-scale to automatically set up oscilloscope.
# print("Autoscale.")
# Infiniium.query(":SYSTem:AUToscale;*OPC?")

# Stop acquisitions and clear the display.
Infiniium.write(":ACQuire:STOP")
Infiniium.write(":ACQuire:CDISplay")  # Clear display.

# Turn on CHANnel1 real-time eye.
source = "CHANnel1"
eye_source = "ECHannel1"
Infiniium.query(f":{source}:EYE:DISPlay ON;*OPC?")

# Turn on eye width measurement.
Infiniium.write(f":MEASure:EYE:EWIDth:SOURce {eye_source}")
Infiniium.write(":MEASure:EYE:EWIDth")

# Monitor the eye width over a number of minutes.
y_data = monitor_eye_width(meas_minutes, meas_interval)
pts = len(y_data)  # Data points in channel. 1st row of matirx.

# Make the time data.
x_data, xunits = create_time_data(pts, meas_minutes, meas_interval)

# Plot the measurement results.
draw_graph(y_data, x_data, xunits, "Eye Width", eye_source)

# Check error queue.
check_error_queue("End of program")

# Exit program.
exit_program()