Example #1
0
def do_calibration(comms, args):
    """
    Run DPS calibration prompts
    """
    print("For calibration you will need:")
    print("\tA multimeter")
    print("\tA known load capable of handling the required power")
    print("\tA thick wire for shorting the output of the DPS")
    print("\t2 stable input voltages\r\n")
    print(
        "Please ensure nothing is connected to the output of the DPS before starting calibration!\r\n"
    )

    t = raw_input("Would you like to proceed? (y/n): ")
    if t.lower() != 'y':
        return

    # Change to the settings screen
    communicate(comms,
                create_change_screen(protocol.CHANGE_SCREEN_SETTINGS),
                args,
                quiet=True)

    print("\r\nInput Voltage Calibration:")
    calibration_input_voltage = []
    calibration_vin_adc = []

    print("Please hook up the first lower supply voltage to the DPS now")
    print("ensuring that the serial connection is connected after boot")
    calibration_input_voltage.append(
        float(raw_input("Type input voltage in mV: ")))
    calibration_vin_adc.append(get_average_calibration_result(
        comms, 'vin_adc'))

    # Do second Voltage Hookup
    print("\r\nPlease hook up the second higher supply voltage to the DPS now")
    print("ensuring that the serial connection is connected after boot")
    calibration_input_voltage.append(
        float(raw_input("Type input voltage in mV: ")))
    calibration_vin_adc.append(get_average_calibration_result(
        comms, 'vin_adc'))

    # Calculate and set the Vin_ADC coeffecients
    vin_adc_k, vin_adc_c = best_fit(calibration_vin_adc,
                                    calibration_input_voltage)
    args.calibration_set = [
        'VIN_ADC_K={}'.format(vin_adc_k), 'VIN_ADC_C={}'.format(vin_adc_c)
    ]
    payload = create_set_calibration(args.calibration_set)
    communicate(comms, payload, args, quiet=True)

    # Draw data in graph
    if calibration_debug_plotting:
        plt.title("Input Voltage Calibration")
        ax = plt.gca()
        plt.text(0.03,
                 0.97,
                 'y = {} * x + {}'.format(vin_adc_k, vin_adc_c),
                 transform=ax.transAxes,
                 fontsize=9,
                 va='top')
        plt.xlabel("Vin_adc")
        x_data = list(calibration_vin_adc)
        plt.ylabel("Input Voltage (V)")
        y_data = list(calibration_input_voltage)
        plt.plot(x_data, y_data, 'ro')
        y_data = []
        x_data.insert(
            0, 0)  # Ensure we have a x = 0 point on our line of best fit
        for i in range(len(x_data)):
            y_data.append(x_data[i] * vin_adc_k + vin_adc_c)
        plt.plot(x_data, y_data, '-')  # Draw line of best fit
        plt.axis(xmin=0, ymin=0)
        plt.show()

    print("\r\nOutput Voltage Calibration:")
    print("Finding maximum output V_DAC value", end='')

    args.parameter = ["V_DAC=0", "A_DAC=4095"]
    payload = create_set_parameter(args.parameter)
    communicate(comms, payload, args, quiet=True)
    communicate(comms, create_enable_output("on"), args,
                quiet=True)  # Turn the output on
    time.sleep(
        4
    )  # Ensure the device has settled, this can take a while with an open circuit output

    # To find the maximum output V_DAC value we sweep through a range of output DAC values and read back the ADC values
    num_steps = 100
    output_adc = []
    output_dac = []
    output_gradient = []
    for x in range(num_steps + 1):
        output_dac.append(x * (4095 / num_steps))
        args.parameter = ["V_DAC={}".format(output_dac[-1])]
        payload = create_set_parameter(args.parameter)
        communicate(comms, payload, args, quiet=True)
        time.sleep(0.01)
        data = communicate(comms,
                           create_cmd(protocol.CMD_CAL_REPORT),
                           args,
                           quiet=True)
        output_adc.append(data['vout_adc'])
        if not x % 4:
            print(".", end='')
    print(" Done")

    # Once this is complete we calculate the gradient between every other point
    for x in range(num_steps):
        k, _ = best_fit(output_dac[x:x + 2], output_adc[x:x + 2])
        output_gradient.append(k)

    # If the gradient is near zero then we know this is our maximum
    for x in range(len(output_gradient)):
        if output_gradient[x] < 0.1:
            max_v_dac = output_dac[x - 1]  # Use the one before this
            break

    # Draw data in graph
    if calibration_debug_plotting:
        plt.title("Output Voltage Sweep")
        plt.xlabel("V_DAC")
        x_data = list(output_dac)
        plt.ylabel("V_ADC")
        y_data = list(output_adc)
        plt.plot(x_data, y_data, 'ro')
        plt.axvline(x=max_v_dac)
        plt.axis(xmin=0, ymin=0)
        plt.show()

    # Get the user to give us two output voltages readings
    calibration_real_voltage = []
    calibration_v_adc = []
    calibration_v_dac = []

    print("\r\nCalibration Point 1 of 2, 10% of Max")
    output_dac = int(max_v_dac * 0.1)
    args.parameter = ["V_DAC={}".format(output_dac)]
    payload = create_set_parameter(args.parameter)
    communicate(comms, payload, args, quiet=True)
    calibration_real_voltage.append(
        float(raw_input("Type measured voltage on output in mV: ")))
    calibration_v_adc.append(get_average_calibration_result(comms, 'vout_adc'))
    calibration_v_dac.append(output_dac)

    print("\r\nCalibration Point 1 of 2, 90% of Max")
    output_dac = int(max_v_dac * 0.9)
    args.parameter = ["V_DAC={}".format(output_dac)]
    payload = create_set_parameter(args.parameter)
    communicate(comms, payload, args, quiet=True)
    calibration_real_voltage.append(
        float(raw_input("Type measured voltage on output in mV: ")))
    calibration_v_adc.append(get_average_calibration_result(comms, 'vout_adc'))
    calibration_v_dac.append(output_dac)

    # Calculate and set the V_DAC coeffecients
    v_dac_k, v_dac_c = best_fit(calibration_real_voltage, calibration_v_dac)
    args.calibration_set = [
        'V_DAC_K={}'.format(v_dac_k), 'V_DAC_C={}'.format(v_dac_c)
    ]
    payload = create_set_calibration(args.calibration_set)
    communicate(comms, payload, args, quiet=True)

    # Calculate and set the V_ADC coeffecients
    v_adc_k, v_adc_c = best_fit(calibration_v_adc, calibration_real_voltage)
    args.calibration_set = [
        'V_ADC_K={}'.format(v_adc_k), 'V_ADC_C={}'.format(v_adc_c)
    ]
    payload = create_set_calibration(args.calibration_set)
    communicate(comms, payload, args, quiet=True)

    communicate(comms, create_enable_output("off"), args,
                quiet=True)  # Turn the output off

    # Draw data in graph
    if calibration_debug_plotting:
        plt.title("Output Voltage Calibration (V_DAC)")
        ax = plt.gca()
        plt.text(0.03,
                 0.97,
                 'y = {} * x + {}'.format(v_dac_k, v_dac_c),
                 transform=ax.transAxes,
                 fontsize=9,
                 va='top')
        plt.xlabel("Output Voltage (V)")
        x_data = list(calibration_real_voltage)
        plt.ylabel("V_DAC")
        y_data = list(calibration_v_dac)
        plt.plot(x_data, y_data, 'ro')
        y_data = []
        x_data.insert(
            0, 0)  # Ensure we have a x = 0 point on our line of best fit
        for i in range(len(x_data)):
            y_data.append(x_data[i] * v_dac_k + v_dac_c)
        plt.plot(x_data, y_data, '-')  # Draw line of best fit
        plt.axis(xmin=0, ymin=0)
        plt.show()

    # Draw data in graph
    if calibration_debug_plotting:
        plt.title("Output Voltage Calibration (V_ADC)")
        ax = plt.gca()
        plt.text(0.03,
                 0.97,
                 'y = {} * x + {}'.format(v_adc_k, v_adc_c),
                 transform=ax.transAxes,
                 fontsize=9,
                 va='top')
        plt.xlabel("V_ADC")
        x_data = list(calibration_v_adc)
        plt.ylabel("Output Voltage (V)")
        y_data = list(calibration_real_voltage)
        plt.plot(x_data, y_data, 'ro')
        y_data = []
        x_data.insert(
            0, 0)  # Ensure we have a x = 0 point on our line of best fit
        for i in range(len(x_data)):
            y_data.append(x_data[i] * v_adc_k + v_adc_c)
        plt.plot(x_data, y_data, '-')  # Draw line of best fit
        plt.axis(xmin=0, ymin=0)
        plt.show()

    print("\r\nOutput Current Calibration:")
    max_dps_current = float(
        raw_input(
            "Max output current of your DPS (e.g 5 for the DPS5005) in amps: ")
    )
    load_resistance = float(raw_input("Load resistance in ohms: "))
    load_max_wattage = float(raw_input("Load wattage rating in watts: "))

    # There are three potential limiting factors for the output voltage, these are:
    output_voltage_based_on_input_voltage_mv = calibration_input_voltage[
        1] * 0.9  # 90% of input voltage
    output_voltage_based_on_max_wattage_of_load_mv = math.sqrt(
        load_max_wattage * load_resistance
    ) * 1000  # Maximum supported voltage of the load V = Sqrt(W x R)
    output_voltage_based_on_max_output_current_mv = max_dps_current * load_resistance * 1000  # V = I x R

    max_output_voltage_mv = min(
        output_voltage_based_on_input_voltage_mv,
        output_voltage_based_on_max_wattage_of_load_mv,
        output_voltage_based_on_max_output_current_mv)

    # The more max_output_voltage_mv is maximised the more accurate the results of the current calibration will be

    raw_input(
        "Please connect the load to the output of the DPS, then press enter")

    # Take multiple current readings at different voltages and construct an Iout vs Iadc array
    print("Calibrating output current ADC", end='')
    num_steps = 15
    calibration_i_out = []
    calibration_a_adc = []
    for x in range(num_steps):
        # Calculate our output voltage DAC value
        output_voltage = max_output_voltage_mv * (x / num_steps)
        output_dac = int(round(v_dac_k * output_voltage + v_dac_c))

        # Set the output voltage
        args.parameter = ["V_DAC={}".format(output_dac)]
        payload = create_set_parameter(args.parameter)
        communicate(comms, payload, args, quiet=True)
        communicate(comms, create_enable_output("on"), args, quiet=True)
        time.sleep(1)  # Wait for the DPS output to settle

        # Add these readings to our array
        calibration_i_out.append(
            (get_average_calibration_result(comms, 'vout_adc') * v_adc_k +
             v_dac_c) / load_resistance)
        calibration_a_adc.append(
            get_average_calibration_result(comms, 'iout_adc'))
        print(".", end='')
    print(" Done")

    communicate(comms, create_enable_output("off"), args,
                quiet=True)  # Turn the output off

    # Calculate and set the A_ADC coeffecients
    a_adc_k, a_adc_c = best_fit(calibration_a_adc, calibration_i_out)
    args.calibration_set = [
        'A_ADC_K={}'.format(a_adc_k), 'A_ADC_C={}'.format(a_adc_c)
    ]
    payload = create_set_calibration(args.calibration_set)
    communicate(comms, payload, args, quiet=True)

    # Draw data in graph
    if calibration_debug_plotting:
        plt.title("Output Current Calibration (A_ADC)")
        ax = plt.gca()
        plt.text(0.03,
                 0.97,
                 'y = {} * x + {}'.format(a_adc_k, a_adc_c),
                 transform=ax.transAxes,
                 fontsize=9,
                 va='top')
        plt.xlabel("A_ADC")
        x_data = list(calibration_a_adc)
        plt.ylabel("Output Current (A)")
        y_data = list(calibration_i_out)
        plt.plot(x_data, y_data, 'ro')
        y_data = []
        x_data.insert(
            0, 0)  # Ensure we have a x = 0 point on our line of best fit
        for i in range(len(x_data)):
            y_data.append(x_data[i] * a_adc_k + a_adc_c)
        plt.plot(x_data, y_data, '-')  # Draw line of best fit
        plt.axis(xmin=0, ymin=0)
        plt.show()

    print("\r\nConstant Current Calibration:")
    raw_input(
        "Please short the output of the DPS with a thick wire capable of carrying {}A, then press enter"
        .format(max_dps_current))

    # Set the V_DAC output to the maximum
    args.parameter = ["V_DAC={}".format(4095)]
    payload = create_set_parameter(args.parameter)
    communicate(comms, payload, args, quiet=True)

    # Sweep the full range of the A_DAC so we can find out what its workable region is
    print("\r\nFinding maximum output A_DAC value", end='')
    num_steps = 100
    calibration_a_adc = []
    calibration_a_dac = []
    output_gradient = []
    for x in range(num_steps + 1):
        calibration_a_dac.append(int(x * (4095 / num_steps)))
        args.parameter = ["A_DAC={}".format(calibration_a_dac[-1])]
        payload = create_set_parameter(args.parameter)
        communicate(comms, payload, args, quiet=True)
        communicate(comms, create_enable_output("on"), args, quiet=True)
        time.sleep(0.01)

        data = communicate(comms,
                           create_cmd(protocol.CMD_CAL_REPORT),
                           args,
                           quiet=True)
        calibration_a_adc.append(data['iout_adc'])
        if not x % 4:
            print(".", end='')
    print(" Done")

    communicate(comms, create_enable_output("off"), args,
                quiet=True)  # Turn the output off

    # Once this is complete we calculate the gradient between every other point
    for x in range(num_steps):
        k, _ = best_fit(calibration_a_dac[x:x + 2], calibration_a_adc[x:x + 2])
        output_gradient.append(k)

    # Find the first point where the gradient is non-zero
    first_point = 0
    for x in range(len(output_gradient)):
        if (output_gradient[x] > 0.1):
            first_point = x
            break

    # Find the last point where the gradient is non-zero
    last_point = len(output_gradient) - 1
    for x in range(first_point, len(output_gradient)):
        if (output_gradient[x] < 0.1):
            last_point = x - 1
            break

    # Find the A_DAC output range. Bringing the points in by 20% to trim any edge values out
    a_dac_lower_range = calibration_a_dac[first_point] + (
        calibration_a_dac[last_point] - calibration_a_dac[first_point]) * 0.1
    a_dac_upper_range = calibration_a_dac[last_point] - (
        calibration_a_dac[last_point] - calibration_a_dac[first_point]) * 0.1

    # Draw data in graph
    if calibration_debug_plotting:
        plt.title("Output Current Sweep")
        plt.xlabel("A_DAC")
        x_data = list(calibration_a_dac)
        plt.ylabel("A_ADC")
        y_data = list(calibration_a_adc)
        plt.plot(x_data, y_data, 'ro')
        plt.axvline(x=a_dac_lower_range)
        plt.axvline(x=a_dac_upper_range)
        plt.axis(xmin=0, ymin=0)
        plt.show()

    # Take multiple current readings in this range
    print("Calibrating output current DAC", end='')
    num_steps = 15
    calibration_i_out = []
    calibration_a_dac = []
    for x in range(num_steps):
        # Calculate our output current DAC value
        output_dac = int(a_dac_lower_range +
                         ((a_dac_upper_range - a_dac_lower_range) *
                          (x / num_steps)))

        # Set the output current
        args.parameter = ["A_DAC={}".format(output_dac)]
        payload = create_set_parameter(args.parameter)
        communicate(comms, payload, args, quiet=True)
        communicate(comms, create_enable_output("on"), args, quiet=True)
        time.sleep(1)  # Wait for the DPS output to settle

        # Add these readings to our array
        calibration_i_out.append(
            (get_average_calibration_result(comms, 'iout_adc') * a_adc_k +
             a_adc_c))
        calibration_a_dac.append(output_dac)
        print(".", end='')
    print(" Done")

    communicate(comms, create_enable_output("off"), args,
                quiet=True)  # Turn the output off

    # Calculate and set the A_DAC coeffecients
    a_dac_k, a_dac_c = best_fit(calibration_i_out, calibration_a_dac)
    args.calibration_set = [
        'A_DAC_K={}'.format(a_dac_k), 'A_DAC_C={}'.format(a_dac_c)
    ]
    payload = create_set_calibration(args.calibration_set)
    communicate(comms, payload, args, quiet=True)

    # Draw data in graph
    if calibration_debug_plotting:
        plt.title("Output Current Calibration (A_DAC)")
        ax = plt.gca()
        plt.text(0.03,
                 0.97,
                 'y = {} * x + {}'.format(a_dac_k, a_dac_c),
                 transform=ax.transAxes,
                 fontsize=9,
                 va='top')
        plt.xlabel("Output Current (A)")
        x_data = list(calibration_i_out)
        plt.ylabel("A_DAC")
        y_data = list(calibration_a_dac)
        plt.plot(x_data, y_data, 'ro')
        y_data = []
        x_data.insert(
            0, 0)  # Ensure we have a x = 0 point on our line of best fit
        for i in range(len(x_data)):
            y_data.append(x_data[i] * a_dac_k + a_dac_c)
        plt.plot(x_data, y_data, '-')  # Draw line of best fit
        plt.axis(xmin=0, ymin=0)
        plt.show()

    # Change to the main screen
    communicate(comms,
                create_change_screen(protocol.CHANGE_SCREEN_MAIN),
                args,
                quiet=True)

    print("\r\nCalibration Complete!\r\n")
    print(
        "To restore the device to the OpenDPS defaults use dpsctl.py --calibration_reset"
    )
Example #2
0
def handle_commands(args):
    """
    Communicate with the DPS device according to the user's wishes
    """
    if args.scan:
        uhej_scan()
        return

    comms = create_comms(args)

    if args.ping:
        communicate(comms, create_cmd(protocol.CMD_PING), args)

    if args.firmware:
        run_upgrade(comms, args.firmware, args)

    if args.lock:
        communicate(comms, create_lock(1), args)
    if args.unlock:
        communicate(comms, create_lock(0), args)

    if args.list_functions:
        communicate(comms, create_cmd(protocol.CMD_LIST_FUNCTIONS), args)

    if args.list_parameters:
        communicate(comms, create_cmd(protocol.CMD_LIST_PARAMETERS), args)

    if args.function:
        communicate(comms, create_set_function(args.function), args)

    if args.enable:
        if args.enable == 'on' or args.enable == 'off':
            communicate(comms, create_enable_output(args.enable), args)
        else:
            fail("enable is 'on' or 'off'")

    if args.parameter:
        payload = create_set_parameter(args.parameter)
        if payload:
            communicate(comms, payload, args)
        else:
            fail("malformed parameters")

    if args.query:
        communicate(comms, create_cmd(protocol.CMD_QUERY), args)

    if args.version:
        communicate(comms, create_cmd(protocol.CMD_VERSION), args)

    if args.calibration_report:
        data = communicate(comms, create_cmd(protocol.CMD_CAL_REPORT), args)
        print("Calibration Report:")
        print("\tA_ADC_K = {}".format(data['cal']['A_ADC_K']))
        print("\tA_ADC_C = {}".format(data['cal']['A_ADC_C']))
        print("\tA_DAC_K = {}".format(data['cal']['A_DAC_K']))
        print("\tA_DAC_C = {}".format(data['cal']['A_DAC_C']))
        print("\tV_ADC_K = {}".format(data['cal']['V_ADC_K']))
        print("\tV_ADC_C = {}".format(data['cal']['V_ADC_C']))
        print("\tV_DAC_K = {}".format(data['cal']['V_DAC_K']))
        print("\tV_DAC_C = {}".format(data['cal']['V_DAC_C']))
        print("\tVIN_ADC_K = {}".format(data['cal']['VIN_ADC_K']))
        print("\tVIN_ADC_C = {}".format(data['cal']['VIN_ADC_C']))
        print("\tVIN_ADC = {}".format(data['vin_adc']))
        print("\tVOUT_ADC = {}".format(data['vout_adc']))
        print("\tIOUT_ADC = {}".format(data['iout_adc']))
        print("\tIOUT_DAC = {}".format(data['iout_dac']))
        print("\tVOUT_DAC = {}".format(data['vout_dac']))

    if args.calibration_set:
        payload = create_set_calibration(args.calibration_set)
        if payload:
            communicate(comms, payload, args)
        else:
            fail("malformed parameters")

    if hasattr(args, 'temperature') and args.temperature:
        communicate(comms, create_temperature(float(args.temperature)), args)

    if args.calibration_reset:
        communicate(comms, create_cmd(protocol.CMD_CLEAR_CALIBRATION), args)

    if args.switch_screen:
        if (args.switch_screen.lower() == "main"):
            communicate(comms,
                        create_change_screen(protocol.CHANGE_SCREEN_MAIN),
                        args)
        elif (args.switch_screen.lower() == "settings"):
            communicate(comms,
                        create_change_screen(protocol.CHANGE_SCREEN_SETTINGS),
                        args)
        else:
            fail("please specify either 'settings' or 'main' as parameters")

    if args.calibrate:
        do_calibration(comms, args)

    if args.brightness:
        if args.brightness >= 0 and args.brightness <= 100:
            communicate(comms, create_set_brightness(args.brightness), args)
        else:
            fail("brightness must be between 0 and 100")