From 747e424871506d02461e4a30541c40aace7af60a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Leon=20Teichr=C3=B6b?= Date: Fri, 15 Oct 2021 11:21:56 +0200 Subject: [PATCH] Calibration results can now be saved to config. --- src/helmholtz_cage_device.py | 24 ++++++++++++++----- src/user_interface.py | 45 ++++++++++++++++++++++++++++++++---- 2 files changed, 58 insertions(+), 11 deletions(-) diff --git a/src/helmholtz_cage_device.py b/src/helmholtz_cage_device.py index 48252b5..1db51dc 100644 --- a/src/helmholtz_cage_device.py +++ b/src/helmholtz_cage_device.py @@ -70,8 +70,6 @@ class HelmholtzCageDevice: self.psu1 = None self.psu2 = None - self.connect_hardware() - # --- AXIS CONFIGURATION --- # This is also hardware related, but in a separate section to keep it clean. # Get all settings from the config file @@ -81,6 +79,8 @@ class HelmholtzCageDevice: # The axes talks to the HW objects (Arduino, PSU) referenced in this object self.axes.append(Axis(i, self)) + self.connect_hardware() + # Zero and activate channels. This is a sort of "armed" state so that we can send commands later self.idle() @@ -137,6 +137,10 @@ class HelmholtzCageDevice: self.psu2 = None ui_print("Error creating PSU device:\n{}".format(e)) + # The axes may not be deleted, so a special method for reinitialization is provided. + for axis in self.axes: + axis.reload_config() + def idle(self): """ Zero and activate channels """ if self.psu1 is not None: @@ -393,16 +397,24 @@ class Axis: self.name = g.AXIS_NAMES[axis_idx] # Also used in some indexing operations, like in the config file # Create other axis properties using config parameters + self.coil_const = None + self.ambient_field = None + self.resistance = None + self.max_volts = None + self.max_amps = None + self.reload_config() + + # State variables + self.target_current = 0 + self.polarity = False + + def reload_config(self): self.coil_const = float(config_handling.read_from_config(self.name, "coil_const", config_handling.CONFIG_OBJECT)) self.ambient_field = float(config_handling.read_from_config(self.name, "ambient_field", config_handling.CONFIG_OBJECT)) self.resistance = float(config_handling.read_from_config(self.name, "resistance", config_handling.CONFIG_OBJECT)) self.max_volts = float(config_handling.read_from_config(self.name, "max_volts", config_handling.CONFIG_OBJECT)) self.max_amps = float(config_handling.read_from_config(self.name, "max_amps", config_handling.CONFIG_OBJECT)) - # State variables - self.target_current = 0 - self.polarity = False - def set_field_raw(self, field): self.set_signed_current(field / self.coil_const) diff --git a/src/user_interface.py b/src/user_interface.py index 319362e..a85e420 100644 --- a/src/user_interface.py +++ b/src/user_interface.py @@ -570,11 +570,13 @@ class CalibrateAmbientField(Frame): self.ambient_field_result_vars = [StringVar(), StringVar(), StringVar()] self.ambient_field_ut_result_vars = [StringVar(), StringVar(), StringVar()] self.ambient_field_residual_vars = [StringVar(), StringVar(), StringVar()] + self.ambient_field_result = None # Used for saving to config file # Contains results for coil constant calibration self.coil_constant_vars = [StringVar(), StringVar(), StringVar()] self.coil_constant_dev_vars = [StringVar(), StringVar(), StringVar()] self.coil_angle_vars = [StringVar(), StringVar(), StringVar()] self.coil_constant_raw_data = None # Used for export to csv + self.coil_constant_results = None # Used for saving to config file row_counter = 0 @@ -612,6 +614,7 @@ class CalibrateAmbientField(Frame): # Calibration start and save to csv buttons start_button_frame = Frame(self.left_column) start_button_frame.grid(row=row_counter, column=0, sticky="sw") + # TODO: Add command self.save_ambient_calibration_button = Button(start_button_frame, text="Save results to CSV", command=None, state="disabled", @@ -646,10 +649,10 @@ class CalibrateAmbientField(Frame): # Ambient field calibration results row_counter = 0 ambient_field_results_frame = LabelFrame(self.right_column, text="Ambient Field Results") - ambient_field_results_frame.grid(row=row_counter, column=1, padx=(100, 0), pady=20, sticky="nw") + ambient_field_results_frame.grid(row=row_counter, column=1, padx=(100, 0), pady=5, sticky="nw") for i, label in enumerate(['X', 'Y', 'Z']): axis_label = Label(ambient_field_results_frame, text=label) - axis_label.grid(row=0, column=i+1, padx=5, pady=5, sticky="nw") + axis_label.grid(row=0, column=i+1, padx=5, pady=(5, 0), sticky="nw") # Ambient field value (A) ambient_field_results_label = Label(ambient_field_results_frame, text="Ambient Field:") ambient_field_results_label.grid(row=1, column=0, padx=5, pady=5, sticky="nw") @@ -683,14 +686,19 @@ class CalibrateAmbientField(Frame): axis_data.grid(row=3, column=i+1, padx=5, pady=5, sticky="nw") ambient_field_residual_unit = Label(ambient_field_results_frame, text="\u03BCT") ambient_field_residual_unit.grid(row=3, column=4, padx=5, pady=5, sticky="nw") + # Save calibration to config + self.ambient_field_save_results_button = Button(ambient_field_results_frame, text="Save to config and apply", + command=self.save_and_apply_ambient_calibration, + state="disabled") + self.ambient_field_save_results_button.grid(row=4, column=0, columnspan=5, padx=5, pady=5) row_counter += 1 # Coil constant results coil_constants_results_frame = LabelFrame(self.right_column, text="Coil Constants") - coil_constants_results_frame.grid(row=row_counter, column=1, padx=(100, 0), pady=20, sticky="nw") + coil_constants_results_frame.grid(row=row_counter, column=1, padx=(100, 0), pady=5, sticky="nw") for i, label in enumerate(['X', 'Y', 'Z']): axis_label = Label(coil_constants_results_frame, text=label) - axis_label.grid(row=0, column=i + 1, padx=5, pady=5, sticky="nw") + axis_label.grid(row=0, column=i + 1, padx=5, pady=(5, 0), sticky="nw") # Coil constants coil_constants_results_label = Label(coil_constants_results_frame, text="K:") coil_constants_results_label.grid(row=1, column=0, padx=5, pady=5, sticky="nw") @@ -716,7 +724,7 @@ class CalibrateAmbientField(Frame): # Inter-axis angle labels for i, label in enumerate(['X-Y', 'Y-Z', 'X-Z']): axis_label = Label(coil_constants_results_frame, text=label) - axis_label.grid(row=3, column=i + 1, padx=5, pady=5, sticky="nw") + axis_label.grid(row=3, column=i + 1, padx=5, pady=0, sticky="nw") # Inter-axis angles coil_angles_label = Label(coil_constants_results_frame, text="Angles:") coil_angles_label.grid(row=4, column=0, padx=5, pady=5, sticky="nw") @@ -728,6 +736,10 @@ class CalibrateAmbientField(Frame): axis_data.grid(row=4, column=i + 1, padx=5, pady=5, sticky="nw") coil_angles_unit = Label(coil_constants_results_frame, text="°") coil_angles_unit.grid(row=4, column=4, padx=5, pady=5, sticky="nw") + self.coil_constant_save_results_button = Button(coil_constants_results_frame, text="Save to config and apply", + command=self.save_and_apply_coil_constants, + state="disabled") + self.coil_constant_save_results_button.grid(row=5, column=0, columnspan=5, padx=5, pady=5) # This starts an endless polling loop self.update_view() @@ -790,7 +802,9 @@ class CalibrateAmbientField(Frame): self.ambient_field_ut_result_vars[i].set("{:.3f}".format(results['ambient_ut'][i])) self.ambient_field_residual_vars[i].set("{:.3f}".format(results['residual'][i] * 1e6)) + self.ambient_field_result = results['ambient_ut'] * 1e-6 self.save_ambient_calibration_button.configure(state='normal') + self.ambient_field_save_results_button.configure(state='normal') def update_coil_constant_results(self, results): for i in range(3): @@ -803,7 +817,9 @@ class CalibrateAmbientField(Frame): self.coil_angle_vars[2].set("{:.2f}".format(results['angle']['xz'])) self.coil_constant_raw_data = results['raw_data'] + self.coil_constant_results = results['k'] self.save_k_calibration_button.configure(state='normal') + self.coil_constant_save_results_button.configure(state='normal') def calibration_procedure_ambient(self): try: @@ -836,6 +852,25 @@ class CalibrateAmbientField(Frame): for row in self.coil_constant_raw_data: csv_writer.writerow(row) + def save_and_apply_ambient_calibration(self): + if self.ambient_field_result is not None: + ui_print("Saving ambient field calibration data") + config.edit_config("X-Axis", "ambient_field", self.ambient_field_result[0]) + config.edit_config("Y-Axis", "ambient_field", self.ambient_field_result[1]) + config.edit_config("Z-Axis", "ambient_field", self.ambient_field_result[2]) + + ui_print("Reinitializing devices...") + g.CAGE_DEVICE.reconnect_hardware() # setup everything with the defaults + + def save_and_apply_coil_constants(self): + if self.coil_constant_results is not None: + ui_print("Saving coil constant calibration data") + config.edit_config("X-Axis", "coil_const", self.coil_constant_results[0]) + config.edit_config("Y-Axis", "coil_const", self.coil_constant_results[1]) + config.edit_config("Z-Axis", "coil_const", self.coil_constant_results[2]) + + ui_print("Reinitializing devices...") + g.CAGE_DEVICE.reconnect_hardware() # setup everything with the defaults class CalibrateMagnetometer(Frame): def __init__(self, parent, controller):