# 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()