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" )
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")