From 3262d30b9a3262a22b7d083371f422c387ddbe3d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Leon=20Teichr=C3=B6b?= Date: Wed, 25 Jan 2023 17:27:55 +0100 Subject: [PATCH 01/11] Possible mistake with getter of tkinter/numpy Error was get() not possible for float values --- src/calibration.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/calibration.py b/src/calibration.py index 06d660f..4b12d84 100644 --- a/src/calibration.py +++ b/src/calibration.py @@ -531,7 +531,8 @@ class MagnetometerCalibrationComplete(Thread): matrix_trans_mgm_to_hh_np = np.zeros((3, 3)) for row in range(3): for col in range(3): - matrix_trans_mgm_to_hh_np[row][col] = matrix_trans_mgm_to_hh_tk[row][col].get() + #matrix_trans_mgm_to_hh_np[row][col] = matrix_trans_mgm_to_hh_tk[row][col].get() + matrix_trans_mgm_to_hh_np[row][col] = matrix_trans_mgm_to_hh_tk[row][col] # matrix_trans_mgm_to_hh_np = [[-1, 0, 0], [0, 1, 0], [0, 0, -1]] # hardcoded for MGM 1 / 3 # matrix_trans_mgm_to_hh_np = [[0, 1, 0], [-1, 0, 0], [0, 0, 1]] # hardcoded for MGM 0 / 2 ui_print("Applying transformation matrix to data. h_{set,mgm} = mgm_T_hh * h_{set,hh}") From f13389c06de4b04a34237117f3f9f5fac3a2c749 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Leon=20Teichr=C3=B6b?= Date: Thu, 26 Jan 2023 11:43:23 +0100 Subject: [PATCH 02/11] Added Reinitialize buttons to all frames --- src/user_interface.py | 72 +++++++++++++++++++++++++++++++++++-------- 1 file changed, 60 insertions(+), 12 deletions(-) diff --git a/src/user_interface.py b/src/user_interface.py index 25c7ec1..c4033b6 100644 --- a/src/user_interface.py +++ b/src/user_interface.py @@ -64,11 +64,11 @@ class HelmholtzGUI(Tk): else: red = 0 global HEADER_FONT, SUB_HEADER_FONT, BIG_BUTTON_FONT, SMALL_BUTTON_FONT, DEFAULT_FONT - HEADER_FONT = (font, points[0+red], "bold") - SUB_HEADER_FONT = (font, points[2+red], "bold") - BIG_BUTTON_FONT = (font, points[1+red], "bold") - SMALL_BUTTON_FONT = (font, points[2+red]) - DEFAULT_FONT = (font, points[2+red]) + HEADER_FONT = (font, points[0 + red], "bold") + SUB_HEADER_FONT = (font, points[2 + red], "bold") + BIG_BUTTON_FONT = (font, points[1 + red], "bold") + SMALL_BUTTON_FONT = (font, points[2 + red]) + DEFAULT_FONT = (font, points[2 + red]) self.option_add("*font", DEFAULT_FONT) self.Menu = TopMenu(self) # display dropdown menu bar at the top (see TopMenu class for details) @@ -89,7 +89,8 @@ class HelmholtzGUI(Tk): main_area = Frame(self, padx=10, pady=10) # create main area Frame where controls of each mode are displayed main_area.pack(side="top", fill="both", expand=True) # pack main area at the top of the window - main_area.grid_rowconfigure(0, weight=5, minsize=800) # configure rows and columns of the Tkinter grid to expand with window + main_area.grid_rowconfigure(0, weight=5, + minsize=800) # configure rows and columns of the Tkinter grid to expand with window main_area.grid_columnconfigure(0, weight=1) # initialize the GUI pages for the different modes and setup switching between them @@ -579,7 +580,7 @@ class CalibrateAmbientField(Frame): # To center window # self.columnconfigure(0, weight=1) - self.rowconfigure(0, weight=1) + # self.rowconfigure(0, weight=1) self.left_column = Frame(self) self.left_column.grid(row=0, column=0, sticky="nsew") self.right_column = Frame(self) @@ -645,7 +646,7 @@ class CalibrateAmbientField(Frame): field_data_axis_units.grid(row=i, column=3, padx=5, pady=3) row_counter += 1 - # Calibration start and save to csv buttons + # Calibration start and reinitialize buttons start_button_frame = Frame(self.left_column) start_button_frame.grid(row=row_counter, column=0, sticky="sw") self.start_ambient_calibration_button = Button(start_button_frame, text="Calibrate Ambient Field", @@ -656,6 +657,11 @@ class CalibrateAmbientField(Frame): command=self.calibration_procedure_coil_constants, pady=5, padx=5, font=SMALL_BUTTON_FONT) self.start_k_calibration_button.grid(row=0, column=1, padx=10, pady=10) + # Reinitialize button + self.reinitialize_button = Button(start_button_frame, text="Update and Reinitialize", + command=self.reinitialize, + pady=5, padx=5, font=SMALL_BUTTON_FONT) + self.reinitialize_button.grid(row=0, column=2, padx=10, pady=10) row_counter += 1 # Calibration progress bar @@ -969,6 +975,15 @@ class CalibrateAmbientField(Frame): self.clipboard_append(self.coil_constant_clipboard) self.update() + def reinitialize(self): # called on "Reinitialize!" button press + # reinitialize all PSUs and the Arduino + g.CAGE_DEVICE.reconnect_hardware_async() + + # log change to the log file if user has selected event logging in the Configure Logging window + logger = self.controller.pages[ConfigureLogging] # get object of logging configurator + if logger.event_logging: # data should be logged when test bench is commanded + logger.log_datapoint() # log data + class CalibrateMagnetometerSimple(Frame): def __init__(self, parent, controller): @@ -1060,11 +1075,18 @@ class CalibrateMagnetometerSimple(Frame): calibration_point_nr_entry.grid(row=1, column=1, pady=5, sticky="nw") # Calibration start buttons start_button_frame = Frame(controls_frame) - start_button_frame.grid(row=2, column=0, columnspan=2) + start_button_frame.grid(row=2, column=0, columnspan=1, sticky="nw") self.start_calibration_button = Button(start_button_frame, text="Start Calibration", command=self.start_calibration_procedure, pady=5, padx=5, font=SMALL_BUTTON_FONT) - self.start_calibration_button.grid(row=0, column=0, padx=10, pady=(30, 10)) + self.start_calibration_button.grid(row=0, column=0, padx=10, pady=(30, 10), sticky="we") + # Reinitialize button + reinitialize_button_frame = Frame(controls_frame) + reinitialize_button_frame.grid(row=2, column=1, columnspan=1) + self.reinitialize_button = Button(reinitialize_button_frame, text="Update and Reinitialize", + command=self.reinitialize, + pady=5, padx=5, font=SMALL_BUTTON_FONT) + self.reinitialize_button.grid(row=0, column=0, padx=10, pady=(30, 10), sticky="we") # Calibration progress bar progress_bar_frame = Frame(controls_frame) progress_bar_frame.grid(row=3, column=0, columnspan=2) @@ -1409,6 +1431,15 @@ class CalibrateMagnetometerSimple(Frame): [DoubleVar(value=0), DoubleVar(value=1), DoubleVar(value=0)], [DoubleVar(value=0), DoubleVar(value=0), DoubleVar(value=1)]] + def reinitialize(self): # called on "Reinitialize!" button press + # reinitialize all PSUs and the Arduino + g.CAGE_DEVICE.reconnect_hardware_async() + + # log change to the log file if user has selected event logging in the Configure Logging window + logger = self.controller.pages[ConfigureLogging] # get object of logging configurator + if logger.event_logging: # data should be logged when test bench is commanded + logger.log_datapoint() # log data + class CalibrateMagnetometerComplete(Frame): def __init__(self, parent, controller): @@ -1512,11 +1543,18 @@ class CalibrateMagnetometerComplete(Frame): calibration_point_nr_entry.grid(row=1, column=1, pady=5, sticky="nw") # Calibration start buttons start_button_frame = Frame(controls_frame) - start_button_frame.grid(row=2, column=0, columnspan=2, sticky="nw") + start_button_frame.grid(row=2, column=0, columnspan=1, sticky="nw") self.start_calibration_button = Button(start_button_frame, text="Start Calibration", command=self.start_calibration_procedure, pady=5, padx=5, font=SMALL_BUTTON_FONT) self.start_calibration_button.grid(row=0, column=0, padx=10, pady=(30, 10), sticky="we") + # Reinitialize button + reinitialize_button_frame = Frame(controls_frame) + reinitialize_button_frame.grid(row=2, column=1, columnspan=1) + self.reinitialize_button = Button(reinitialize_button_frame, text="Update and Reinitialize", + command=self.reinitialize, + pady=5, padx=5, font=SMALL_BUTTON_FONT) + self.reinitialize_button.grid(row=0, column=0, padx=10, pady=(30, 10), sticky="we") # Calibration progress bar progress_bar_frame = Frame(controls_frame) progress_bar_frame.grid(row=3, column=0, columnspan=2) @@ -1827,7 +1865,8 @@ class CalibrateMagnetometerComplete(Frame): # Execute calibration function and display results sensor_parameters, mag_x_set, mag_y_set, mag_z_set, mag_x_m, mag_y_m, mag_z_m, cal_x, cal_y, cal_z, mag_amp_avg_set = MagnetometerCalibrationComplete.solve_system( raw_data, self.mgm_to_helmholtz_cos_trans) - MagnetometerCalibrationComplete.plot_magnetometer_calibration(self.right_column, mag_x_set, mag_y_set, mag_z_set, + MagnetometerCalibrationComplete.plot_magnetometer_calibration(self.right_column, mag_x_set, mag_y_set, + mag_z_set, mag_x_m, mag_y_m, mag_z_m, cal_x, cal_y, cal_z, mag_amp_avg_set) @@ -1931,6 +1970,15 @@ class CalibrateMagnetometerComplete(Frame): [DoubleVar(value=0), DoubleVar(value=1), DoubleVar(value=0)], [DoubleVar(value=0), DoubleVar(value=0), DoubleVar(value=1)]] + def reinitialize(self): # called on "Reinitialize!" button press + # reinitialize all PSUs and the Arduino + g.CAGE_DEVICE.reconnect_hardware_async() + + # log change to the log file if user has selected event logging in the Configure Logging window + logger = self.controller.pages[ConfigureLogging] # get object of logging configurator + if logger.event_logging: # data should be logged when test bench is commanded + logger.log_datapoint() # log data + class HardwareConfiguration(Frame): """Settings window to set program constants""" From c678332b45f14923900984368dad920501fabedb Mon Sep 17 00:00:00 2001 From: Cleanroom Laptop L15 Date: Thu, 26 Jan 2023 11:54:00 +0100 Subject: [PATCH 03/11] fixed loading CSV trafo matrix --- src/calibration.py | 2 +- src/user_interface.py | 9 ++++++--- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/src/calibration.py b/src/calibration.py index 06d660f..bf78e9d 100644 --- a/src/calibration.py +++ b/src/calibration.py @@ -531,7 +531,7 @@ class MagnetometerCalibrationComplete(Thread): matrix_trans_mgm_to_hh_np = np.zeros((3, 3)) for row in range(3): for col in range(3): - matrix_trans_mgm_to_hh_np[row][col] = matrix_trans_mgm_to_hh_tk[row][col].get() + matrix_trans_mgm_to_hh_np[row][col] = matrix_trans_mgm_to_hh_tk[row][col] # matrix_trans_mgm_to_hh_np = [[-1, 0, 0], [0, 1, 0], [0, 0, -1]] # hardcoded for MGM 1 / 3 # matrix_trans_mgm_to_hh_np = [[0, 1, 0], [-1, 0, 0], [0, 0, 1]] # hardcoded for MGM 0 / 2 ui_print("Applying transformation matrix to data. h_{set,mgm} = mgm_T_hh * h_{set,hh}") diff --git a/src/user_interface.py b/src/user_interface.py index 25c7ec1..7c3db67 100644 --- a/src/user_interface.py +++ b/src/user_interface.py @@ -578,8 +578,8 @@ class CalibrateAmbientField(Frame): self.controller = controller # To center window - # self.columnconfigure(0, weight=1) - self.rowconfigure(0, weight=1) + #self.columnconfigure(0, weight=1) + #self.rowconfigure(0, weight=1) self.left_column = Frame(self) self.left_column.grid(row=0, column=0, sticky="nsew") self.right_column = Frame(self) @@ -1824,9 +1824,12 @@ class CalibrateMagnetometerComplete(Frame): return self.calibration_raw_results = raw_data + # Note: Imported data must already have the MGM and set field aligned! + ui_print('Imported data transformed with unity matrix. Make sure, MGM and set field of file are in same COS!') + mgm_to_helmholtz_cos_trans = [[1, 0, 0], [0, 1, 0], [0, 0, 1]] # Execute calibration function and display results sensor_parameters, mag_x_set, mag_y_set, mag_z_set, mag_x_m, mag_y_m, mag_z_m, cal_x, cal_y, cal_z, mag_amp_avg_set = MagnetometerCalibrationComplete.solve_system( - raw_data, self.mgm_to_helmholtz_cos_trans) + raw_data, mgm_to_helmholtz_cos_trans) MagnetometerCalibrationComplete.plot_magnetometer_calibration(self.right_column, mag_x_set, mag_y_set, mag_z_set, mag_x_m, mag_y_m, mag_z_m, cal_x, cal_y, From 298852f6c817f71e8496169b5bbcb55f2fa92c80 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Leon=20Teichr=C3=B6b?= Date: Thu, 26 Jan 2023 14:01:45 +0100 Subject: [PATCH 04/11] Renamed reinitialize buttons --- src/user_interface.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/user_interface.py b/src/user_interface.py index 743f29b..f893266 100644 --- a/src/user_interface.py +++ b/src/user_interface.py @@ -254,17 +254,17 @@ class ManualMode(Frame): # add button for executing the current entries execute_button = Button(self.buttons_frame, text="Execute", command=self.execute, - pady=5, padx=5, font=BIG_BUTTON_FONT) + pady=5, padx=5, font=SMALL_BUTTON_FONT) execute_button.grid(row=row_counter, column=0, padx=5) # add button for quick power_down power_down_button = Button(self.buttons_frame, text="Power Down All", command=self.power_down, - pady=5, padx=5, font=BIG_BUTTON_FONT) + pady=5, padx=5, font=SMALL_BUTTON_FONT) power_down_button.grid(row=row_counter, column=1, padx=5) # add button for reinitialization of devices reinit_button = Button(self.buttons_frame, text="Reinitialize", command=self.reinitialize, - pady=5, padx=5, font=BIG_BUTTON_FONT) + pady=5, padx=5, font=SMALL_BUTTON_FONT) reinit_button.grid(row=row_counter, column=2, padx=5) row_counter = row_counter + 1 @@ -656,7 +656,7 @@ class CalibrateAmbientField(Frame): pady=5, padx=5, font=SMALL_BUTTON_FONT) self.start_k_calibration_button.grid(row=0, column=1, padx=10, pady=10) # Reinitialize button - self.reinitialize_button = Button(start_button_frame, text="Update and Reinitialize", + self.reinitialize_button = Button(start_button_frame, text="Reinitialize", command=self.reinitialize, pady=5, padx=5, font=SMALL_BUTTON_FONT) self.reinitialize_button.grid(row=0, column=2, padx=10, pady=10) @@ -1081,7 +1081,7 @@ class CalibrateMagnetometerSimple(Frame): # Reinitialize button reinitialize_button_frame = Frame(controls_frame) reinitialize_button_frame.grid(row=2, column=1, columnspan=1) - self.reinitialize_button = Button(reinitialize_button_frame, text="Update and Reinitialize", + self.reinitialize_button = Button(reinitialize_button_frame, text="Reinitialize", command=self.reinitialize, pady=5, padx=5, font=SMALL_BUTTON_FONT) self.reinitialize_button.grid(row=0, column=0, padx=10, pady=(30, 10), sticky="we") @@ -1549,7 +1549,7 @@ class CalibrateMagnetometerComplete(Frame): # Reinitialize button reinitialize_button_frame = Frame(controls_frame) reinitialize_button_frame.grid(row=2, column=1, columnspan=1) - self.reinitialize_button = Button(reinitialize_button_frame, text="Update and Reinitialize", + self.reinitialize_button = Button(reinitialize_button_frame, text="Reinitialize", command=self.reinitialize, pady=5, padx=5, font=SMALL_BUTTON_FONT) self.reinitialize_button.grid(row=0, column=0, padx=10, pady=(30, 10), sticky="we") From 56b8ffcd5e3cc21ab8673cb17ed9b01ab7aa743c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Leon=20Teichr=C3=B6b?= Date: Thu, 26 Jan 2023 19:01:53 +0100 Subject: [PATCH 05/11] Use GUI transformation matrix on imported datasets --- src/user_interface.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/user_interface.py b/src/user_interface.py index f893266..55cd117 100644 --- a/src/user_interface.py +++ b/src/user_interface.py @@ -1860,9 +1860,8 @@ class CalibrateMagnetometerComplete(Frame): return self.calibration_raw_results = raw_data - # Note: Imported data must already have the MGM and set field aligned! - ui_print('Imported data transformed with unity matrix. Make sure, MGM and set field of file are in same COS!') - mgm_to_helmholtz_cos_trans = [[1, 0, 0], [0, 1, 0], [0, 0, 1]] + # Get transformation matrix from GUI and transform data + mgm_to_helmholtz_cos_trans = [[x.get() for x in row] for row in self.mgm_to_helmholtz_cos_trans] # Execute calibration function and display results sensor_parameters, mag_x_set, mag_y_set, mag_z_set, mag_x_m, mag_y_m, mag_z_m, cal_x, cal_y, cal_z, mag_amp_avg_set = MagnetometerCalibrationComplete.solve_system( raw_data, mgm_to_helmholtz_cos_trans) From 1354aed1500979376247adcc0e2f1fba1cfb4b22 Mon Sep 17 00:00:00 2001 From: Markus Koller Date: Sun, 29 Jan 2023 11:34:19 +0100 Subject: [PATCH 06/11] Opening console on largest screen in zoomed mode --- requirements.txt | 3 +++ src/user_interface.py | 21 +++++++++++++++------ 2 files changed, 18 insertions(+), 6 deletions(-) diff --git a/requirements.txt b/requirements.txt index 7282e2f..a5a0817 100644 --- a/requirements.txt +++ b/requirements.txt @@ -12,3 +12,6 @@ python-dateutil==2.8.2 pytz==2021.3 scipy==1.7.1 six==1.16.0 + +numpy~=1.19.3 +screeninfo~=0.8.1 \ No newline at end of file diff --git a/src/user_interface.py b/src/user_interface.py index 55cd117..bb98f97 100644 --- a/src/user_interface.py +++ b/src/user_interface.py @@ -8,6 +8,7 @@ from tkinter import * from tkinter import ttk from tkinter import messagebox from tkinter import filedialog +import screeninfo from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg import matplotlib.pyplot as plt @@ -32,7 +33,7 @@ import src.helmholtz_cage_device as helmholtz_cage_device # Define global font styles: screen_height_limit1 = 1100 # Limit after which font size gets reduced screen_height_limit2 = 900 # Limit after which font size gets reduced -points = [13, 11, 9, 7, 6, 5] +points = [15, 13, 11, 9, 7, 6] font = "Arial" HEADER_FONT = (font, points[0], "bold") SUB_HEADER_FONT = (font, points[2], "bold") @@ -53,13 +54,21 @@ class HelmholtzGUI(Tk): except TclError: pass + # Check if multiple monitors connected and open the window on the largest or the non-primary monitor + monitors = screeninfo.get_monitors() + [sc_no, sc_x_max, sc_y_max, sc_width_max, sc_height_max] = [0, monitors[0].x, monitors[0].y, + monitors[0].width, monitors[0].height] + for i in range(len(monitors)): + if sc_width_max <= monitors[i].width: + [sc_no, sc_x_max, sc_y_max, sc_width_max, sc_height_max] = [i, monitors[i].x, monitors[i].y, + monitors[i].width, monitors[i].height] + print("Opening console on largest screen: #{} with {} x {}".format(sc_no, sc_width_max, sc_height_max)) + Tk.geometry(self, "{}x{}+{}+{}".format(sc_width_max, sc_height_max, sc_x_max, sc_y_max)) + Tk.state(self, 'zoomed') # Change default fonts if display resolution is too small - screen_width = Tk.winfo_screenwidth(self) - screen_height = Tk.winfo_screenheight(self) - print("Screensize: {} x {}".format(screen_width, screen_height)) - if screen_height <= screen_height_limit2: + if sc_height_max <= screen_height_limit2: red = 2 - elif screen_height <= screen_height_limit1: + elif sc_height_max <= screen_height_limit1: red = 1 else: red = 0 From 03eaf8330c0a9a42be6367dae77898d484373658 Mon Sep 17 00:00:00 2001 From: Markus Koller Date: Sun, 29 Jan 2023 12:09:43 +0100 Subject: [PATCH 07/11] Set custom magnetic field magnitude during calibration --- src/calibration.py | 12 ++++----- src/exceptions.py | 5 ++++ src/user_interface.py | 57 +++++++++++++++++++++++++++++++------------ 3 files changed, 52 insertions(+), 22 deletions(-) diff --git a/src/calibration.py b/src/calibration.py index bf78e9d..30254b0 100644 --- a/src/calibration.py +++ b/src/calibration.py @@ -241,12 +241,12 @@ class CoilConstantCalibration(Thread): class MagnetometerCalibrationSimple(Thread): - TEST_VECTOR_MAGNITUDE = 100e-6 # In Tesla. Chosen so it can be achieved with a 3A PSU. - def __init__(self, view_queue, calibration_points, calibration_interval, mgm_to_helmholtz_cos_trans): + def __init__(self, view_queue, calibration_points, calibration_interval, calibration_mag_field, mgm_to_helmholtz_cos_trans): Thread.__init__(self) self.view_queue = view_queue self.calibration_points = calibration_points + self.calibration_mag_field = calibration_mag_field self.calibration_interval = calibration_interval self.matrix_trans_mgm_to_hh = [[x.get() for x in row] for row in mgm_to_helmholtz_cos_trans] @@ -307,7 +307,7 @@ class MagnetometerCalibrationSimple(Thread): # Collect sensor data for each test vector for vec_idx, test_vec in enumerate(test_vectors): # Command output - applied_vec = test_vec * self.TEST_VECTOR_MAGNITUDE + applied_vec = test_vec * self.calibration_mag_field self.cage_dev.set_field_raw(applied_vec) # Sleep for a certain duration to allow psu to stabilize output and magnetometer to supply readings @@ -407,13 +407,13 @@ class MagnetometerCalibrationSimple(Thread): class MagnetometerCalibrationComplete(Thread): - TEST_VECTOR_MAGNITUDE = 100e-6 # In Tesla. Chosen so it can be achieved with a 3A PSU. - def __init__(self, view_queue, calibration_points, calibration_interval, mgm_to_helmholtz_cos_trans, right_column): + def __init__(self, view_queue, calibration_points, calibration_interval, calibration_mag_field, mgm_to_helmholtz_cos_trans, right_column): Thread.__init__(self) self.view_queue = view_queue self.calibration_points = calibration_points self.calibration_interval = calibration_interval + self.calibration_mag_field = calibration_mag_field self.matrix_trans_mgm_to_hh = [[x.get() for x in row] for row in mgm_to_helmholtz_cos_trans] self.right_column = right_column @@ -473,7 +473,7 @@ class MagnetometerCalibrationComplete(Thread): # Collect sensor data for each test vector for vec_idx, test_vec in enumerate(test_vectors): # Command output - applied_vec = test_vec * self.TEST_VECTOR_MAGNITUDE + applied_vec = test_vec * self.calibration_mag_field self.cage_dev.set_field_raw(applied_vec) # Sleep for a certain duration to allow psu to stabilize output and magnetometer to supply readings diff --git a/src/exceptions.py b/src/exceptions.py index f1003d2..7e73f0f 100644 --- a/src/exceptions.py +++ b/src/exceptions.py @@ -11,3 +11,8 @@ class DeviceAccessError(Exception): class DeviceBusy(DeviceAccessError): """Error thrown when the HW proxy (i.e. access) cannot be acquired""" pass + + +class MagFieldOutOfBounds(Exception): + """Set magnetic field must be a positive number between 0 and 200µT!""" + pass diff --git a/src/user_interface.py b/src/user_interface.py index bb98f97..ce973cb 100644 --- a/src/user_interface.py +++ b/src/user_interface.py @@ -26,7 +26,7 @@ import src.config_handling as config import src.csv_logging as log from src.calibration import AmbientFieldCalibration, CoilConstantCalibration, MagnetometerCalibrationSimple, \ MagnetometerCalibrationComplete -from src.exceptions import DeviceAccessError +from src.exceptions import DeviceAccessError, MagFieldOutOfBounds from src.utility import ui_print, save_dict_list_to_csv, load_dict_list_from_csv import src.helmholtz_cage_device as helmholtz_cage_device @@ -1019,6 +1019,7 @@ class CalibrateMagnetometerSimple(Frame): self.calibration_procedure_progress_var = IntVar(value=0) # Calibration parameters self.calibration_points_var = IntVar(value=8) + self.mag_field_magnitude_var = DoubleVar(value=100) self.calibration_interval_var = DoubleVar(value=5) # Calibration results self.sensitivity_result_vars = [StringVar(), StringVar(), StringVar()] @@ -1070,33 +1071,38 @@ class CalibrateMagnetometerSimple(Frame): # Centered controls controls_frame = Frame(self.left_column) controls_frame.grid(row=row_counter, column=0, sticky="nw") + # Magnitude of magnetic field + mag_field_magnitude_label = Label(controls_frame, text="Magnetic field magnitude [µT]") + mag_field_magnitude_label.grid(row=0, column=0, pady=5, sticky="nw") + mag_field_magnitude_entry = Entry(controls_frame, textvariable=self.mag_field_magnitude_var) + mag_field_magnitude_entry.grid(row=0, column=1, pady=5, sticky="nw") # Number of calibration points calibration_point_nr_label = Label(controls_frame, text="# of calibration points") - calibration_point_nr_label.grid(row=0, column=0, pady=5, sticky="nw") + calibration_point_nr_label.grid(row=1, column=0, pady=5, sticky="nw") calibration_point_nr_entry = Entry(controls_frame, textvariable=self.calibration_points_var) - calibration_point_nr_entry.grid(row=0, column=1, pady=5, sticky="nw") + calibration_point_nr_entry.grid(row=1, column=1, pady=5, sticky="nw") # Measurement interval calibration_point_nr_label = Label(controls_frame, text="Measurement interval [s]") - calibration_point_nr_label.grid(row=1, column=0, pady=5, sticky="nw") + calibration_point_nr_label.grid(row=2, column=0, pady=5, sticky="nw") calibration_point_nr_entry = Entry(controls_frame, textvariable=self.calibration_interval_var) - calibration_point_nr_entry.grid(row=1, column=1, pady=5, sticky="nw") + calibration_point_nr_entry.grid(row=2, column=1, pady=5, sticky="nw") # Calibration start buttons start_button_frame = Frame(controls_frame) - start_button_frame.grid(row=2, column=0, columnspan=1, sticky="nw") + start_button_frame.grid(row=3, column=0, columnspan=1, sticky="nw") self.start_calibration_button = Button(start_button_frame, text="Start Calibration", command=self.start_calibration_procedure, pady=5, padx=5, font=SMALL_BUTTON_FONT) self.start_calibration_button.grid(row=0, column=0, padx=10, pady=(30, 10), sticky="we") # Reinitialize button reinitialize_button_frame = Frame(controls_frame) - reinitialize_button_frame.grid(row=2, column=1, columnspan=1) + reinitialize_button_frame.grid(row=3, column=1, columnspan=1) self.reinitialize_button = Button(reinitialize_button_frame, text="Reinitialize", command=self.reinitialize, pady=5, padx=5, font=SMALL_BUTTON_FONT) self.reinitialize_button.grid(row=0, column=0, padx=10, pady=(30, 10), sticky="we") # Calibration progress bar progress_bar_frame = Frame(controls_frame) - progress_bar_frame.grid(row=3, column=0, columnspan=2) + progress_bar_frame.grid(row=4, column=0, columnspan=2) calibration_procedure_progress_label = Label(progress_bar_frame, text="Progress:") calibration_procedure_progress_label.grid(row=0, column=0, padx=10, pady=10, sticky="nw") calibration_procedure_progress = ttk.Progressbar(progress_bar_frame, @@ -1354,12 +1360,18 @@ class CalibrateMagnetometerSimple(Frame): try: calibration_points = self.calibration_points_var.get() calibration_interval = self.calibration_interval_var.get() + calibration_mag_field = self.mag_field_magnitude_var.get() * 1e-6 # converted to micro Tesla + if calibration_mag_field <= 0 or calibration_mag_field > 110e-6: + raise MagFieldOutOfBounds self.calibration_thread = MagnetometerCalibrationSimple(self.view_mpi_queue, calibration_points, calibration_interval, + calibration_mag_field, self.mgm_to_helmholtz_cos_trans) self.calibration_thread.start() self.deactivate_buttons() + except (MagFieldOutOfBounds) as e: + messagebox.showwarning("Calibration failed", "\n{}".format(e)) except (DeviceAccessError, TclError) as e: messagebox.showwarning("Calibration failed", "Failed to start calibration:\n{}".format(e)) @@ -1476,6 +1488,7 @@ class CalibrateMagnetometerComplete(Frame): StringVar(value="No data")] self.calibration_procedure_progress_var = IntVar(value=0) # Calibration parameters + self.mag_field_magnitude_var = DoubleVar(value=100) self.calibration_points_var = IntVar(value=8) self.calibration_interval_var = DoubleVar(value=5) # Calibration results @@ -1538,33 +1551,38 @@ class CalibrateMagnetometerComplete(Frame): # Centered controls controls_frame = Frame(self.left_column) controls_frame.grid(row=row_counter, column=0, sticky="nw") + # Magnitude of magnetic field + mag_field_magnitude_label = Label(controls_frame, text="Magnetic field magnitude [µT]") + mag_field_magnitude_label.grid(row=0, column=0, pady=5, sticky="nw") + mag_field_magnitude_entry = Entry(controls_frame, textvariable=self.mag_field_magnitude_var) + mag_field_magnitude_entry.grid(row=0, column=1, pady=5, sticky="nw") # Number of calibration points calibration_point_nr_label = Label(controls_frame, text="# of calibration points") - calibration_point_nr_label.grid(row=0, column=0, pady=5, sticky="nw") + calibration_point_nr_label.grid(row=1, column=0, pady=5, sticky="nw") calibration_point_nr_entry = Entry(controls_frame, textvariable=self.calibration_points_var) - calibration_point_nr_entry.grid(row=0, column=1, pady=5, sticky="nw") + calibration_point_nr_entry.grid(row=1, column=1, pady=5, sticky="nw") # Measurement interval calibration_point_nr_label = Label(controls_frame, text="Measurement interval [s]") - calibration_point_nr_label.grid(row=1, column=0, pady=5, sticky="nw") + calibration_point_nr_label.grid(row=2, column=0, pady=5, sticky="nw") calibration_point_nr_entry = Entry(controls_frame, textvariable=self.calibration_interval_var) - calibration_point_nr_entry.grid(row=1, column=1, pady=5, sticky="nw") + calibration_point_nr_entry.grid(row=2, column=1, pady=5, sticky="nw") # Calibration start buttons start_button_frame = Frame(controls_frame) - start_button_frame.grid(row=2, column=0, columnspan=1, sticky="nw") + start_button_frame.grid(row=3, column=0, columnspan=1, sticky="nw") self.start_calibration_button = Button(start_button_frame, text="Start Calibration", command=self.start_calibration_procedure, pady=5, padx=5, font=SMALL_BUTTON_FONT) self.start_calibration_button.grid(row=0, column=0, padx=10, pady=(30, 10), sticky="we") # Reinitialize button reinitialize_button_frame = Frame(controls_frame) - reinitialize_button_frame.grid(row=2, column=1, columnspan=1) + reinitialize_button_frame.grid(row=3, column=1, columnspan=1) self.reinitialize_button = Button(reinitialize_button_frame, text="Reinitialize", command=self.reinitialize, pady=5, padx=5, font=SMALL_BUTTON_FONT) self.reinitialize_button.grid(row=0, column=0, padx=10, pady=(30, 10), sticky="we") # Calibration progress bar progress_bar_frame = Frame(controls_frame) - progress_bar_frame.grid(row=3, column=0, columnspan=2) + progress_bar_frame.grid(row=4, column=0, columnspan=2) calibration_procedure_progress_label = Label(progress_bar_frame, text="Progress:") calibration_procedure_progress_label.grid(row=0, column=0, padx=10, pady=10, sticky="nw") calibration_procedure_progress = ttk.Progressbar(progress_bar_frame, @@ -1838,13 +1856,19 @@ class CalibrateMagnetometerComplete(Frame): try: calibration_points = self.calibration_points_var.get() calibration_interval = self.calibration_interval_var.get() + calibration_mag_field = self.mag_field_magnitude_var.get() * 1e-6 # converted to micro Tesla + if calibration_mag_field <= 0 or calibration_mag_field > 110e-6: + raise MagFieldOutOfBounds self.calibration_thread = MagnetometerCalibrationComplete(self.view_mpi_queue, calibration_points, calibration_interval, + calibration_mag_field, self.mgm_to_helmholtz_cos_trans, self.right_column) self.calibration_thread.start() self.deactivate_buttons() + except (MagFieldOutOfBounds) as e: + messagebox.showwarning("Calibration failed", "\n{}".format(e)) except (DeviceAccessError, TclError) as e: messagebox.showwarning("Calibration failed", "Failed to start calibration:\n{}".format(e)) @@ -1874,7 +1898,8 @@ class CalibrateMagnetometerComplete(Frame): # Execute calibration function and display results sensor_parameters, mag_x_set, mag_y_set, mag_z_set, mag_x_m, mag_y_m, mag_z_m, cal_x, cal_y, cal_z, mag_amp_avg_set = MagnetometerCalibrationComplete.solve_system( raw_data, mgm_to_helmholtz_cos_trans) - MagnetometerCalibrationComplete.plot_magnetometer_calibration(self.right_column, mag_x_set, mag_y_set, mag_z_set, + MagnetometerCalibrationComplete.plot_magnetometer_calibration(self.right_column, mag_x_set, mag_y_set, + mag_z_set, mag_x_m, mag_y_m, mag_z_m, cal_x, cal_y, cal_z, mag_amp_avg_set) From 982a7d7a20900247aa5105e485196606c9c3c7ad Mon Sep 17 00:00:00 2001 From: Markus Koller Date: Sun, 29 Jan 2023 12:16:37 +0100 Subject: [PATCH 08/11] Introduce global to limit max field --- src/globals.py | 3 +++ src/user_interface.py | 8 ++++---- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/src/globals.py b/src/globals.py index cfae625..064a7ca 100644 --- a/src/globals.py +++ b/src/globals.py @@ -50,3 +50,6 @@ default_psu_config = { # Configuration for socket interface SOCKET_PORT = 6677 SOCKET_MAX_CONNECTIONS = 5 + +# Hardware safety limits +MAG_MAG_FIELD = 3*37*1e-6 # [µT=A*µT/A] min coil constant x PSU current limit diff --git a/src/user_interface.py b/src/user_interface.py index ce973cb..ed78682 100644 --- a/src/user_interface.py +++ b/src/user_interface.py @@ -1361,7 +1361,7 @@ class CalibrateMagnetometerSimple(Frame): calibration_points = self.calibration_points_var.get() calibration_interval = self.calibration_interval_var.get() calibration_mag_field = self.mag_field_magnitude_var.get() * 1e-6 # converted to micro Tesla - if calibration_mag_field <= 0 or calibration_mag_field > 110e-6: + if calibration_mag_field <= 0 or calibration_mag_field > MAG_MAG_FIELD: raise MagFieldOutOfBounds self.calibration_thread = MagnetometerCalibrationSimple(self.view_mpi_queue, calibration_points, @@ -1370,7 +1370,7 @@ class CalibrateMagnetometerSimple(Frame): self.mgm_to_helmholtz_cos_trans) self.calibration_thread.start() self.deactivate_buttons() - except (MagFieldOutOfBounds) as e: + except MagFieldOutOfBounds as e: messagebox.showwarning("Calibration failed", "\n{}".format(e)) except (DeviceAccessError, TclError) as e: messagebox.showwarning("Calibration failed", "Failed to start calibration:\n{}".format(e)) @@ -1857,7 +1857,7 @@ class CalibrateMagnetometerComplete(Frame): calibration_points = self.calibration_points_var.get() calibration_interval = self.calibration_interval_var.get() calibration_mag_field = self.mag_field_magnitude_var.get() * 1e-6 # converted to micro Tesla - if calibration_mag_field <= 0 or calibration_mag_field > 110e-6: + if calibration_mag_field <= 0 or calibration_mag_field > MAG_MAG_FIELD: raise MagFieldOutOfBounds self.calibration_thread = MagnetometerCalibrationComplete(self.view_mpi_queue, calibration_points, @@ -1867,7 +1867,7 @@ class CalibrateMagnetometerComplete(Frame): self.right_column) self.calibration_thread.start() self.deactivate_buttons() - except (MagFieldOutOfBounds) as e: + except MagFieldOutOfBounds as e: messagebox.showwarning("Calibration failed", "\n{}".format(e)) except (DeviceAccessError, TclError) as e: messagebox.showwarning("Calibration failed", "Failed to start calibration:\n{}".format(e)) From a6e1b43d4960b415064d1b44288c2cd62e85d482 Mon Sep 17 00:00:00 2001 From: Markus Koller Date: Sun, 29 Jan 2023 12:32:34 +0100 Subject: [PATCH 09/11] Added units to calibration frames --- src/globals.py | 2 +- src/user_interface.py | 27 +++++++++++++++++++++++---- 2 files changed, 24 insertions(+), 5 deletions(-) diff --git a/src/globals.py b/src/globals.py index 064a7ca..89bdac6 100644 --- a/src/globals.py +++ b/src/globals.py @@ -52,4 +52,4 @@ SOCKET_PORT = 6677 SOCKET_MAX_CONNECTIONS = 5 # Hardware safety limits -MAG_MAG_FIELD = 3*37*1e-6 # [µT=A*µT/A] min coil constant x PSU current limit +MAG_MAG_FIELD = 5*37*1e-6 # [µT=A*µT/A] min coil constant x PSU current limit diff --git a/src/user_interface.py b/src/user_interface.py index ed78682..5e5b23e 100644 --- a/src/user_interface.py +++ b/src/user_interface.py @@ -1072,20 +1072,33 @@ class CalibrateMagnetometerSimple(Frame): controls_frame = Frame(self.left_column) controls_frame.grid(row=row_counter, column=0, sticky="nw") # Magnitude of magnetic field - mag_field_magnitude_label = Label(controls_frame, text="Magnetic field magnitude [µT]") + mag_field_magnitude_label = Label(controls_frame, text="Magnetic field magnitude") mag_field_magnitude_label.grid(row=0, column=0, pady=5, sticky="nw") mag_field_magnitude_entry = Entry(controls_frame, textvariable=self.mag_field_magnitude_var) mag_field_magnitude_entry.grid(row=0, column=1, pady=5, sticky="nw") + mag_field_magnitude_unit = Label(controls_frame, text="µT (0 to {:.0f} µT)".format(g.MAG_MAG_FIELD * 1e6)) + mag_field_magnitude_unit.grid(row=0, column=2, pady=5, sticky="nw") # Number of calibration points calibration_point_nr_label = Label(controls_frame, text="# of calibration points") calibration_point_nr_label.grid(row=1, column=0, pady=5, sticky="nw") calibration_point_nr_entry = Entry(controls_frame, textvariable=self.calibration_points_var) calibration_point_nr_entry.grid(row=1, column=1, pady=5, sticky="nw") + calibration_point_nr__unit = Label(controls_frame, text="- (> 8 points)") + calibration_point_nr__unit.grid(row=1, column=2, pady=5, sticky="nw") # Measurement interval calibration_point_nr_label = Label(controls_frame, text="Measurement interval [s]") calibration_point_nr_label.grid(row=2, column=0, pady=5, sticky="nw") calibration_point_nr_entry = Entry(controls_frame, textvariable=self.calibration_interval_var) calibration_point_nr_entry.grid(row=2, column=1, pady=5, sticky="nw") + calibration_point_nr_unit = Label(controls_frame, text="s (> 2 s)") + calibration_point_nr_unit.grid(row=2, column=2, pady=5, sticky="nw") + # Measurement interval + calibration_point_nr_label = Label(controls_frame, text="Measurement interval [s]") + calibration_point_nr_label.grid(row=2, column=0, pady=5, sticky="nw") + calibration_point_nr_entry = Entry(controls_frame, textvariable=self.calibration_interval_var) + calibration_point_nr_entry.grid(row=2, column=1, pady=5, sticky="nw") + calibration_point_nr_unit = Label(controls_frame, text="s (> 2 s)") + calibration_point_nr_unit.grid(row=2, column=2, pady=5, sticky="nw") # Calibration start buttons start_button_frame = Frame(controls_frame) start_button_frame.grid(row=3, column=0, columnspan=1, sticky="nw") @@ -1361,7 +1374,7 @@ class CalibrateMagnetometerSimple(Frame): calibration_points = self.calibration_points_var.get() calibration_interval = self.calibration_interval_var.get() calibration_mag_field = self.mag_field_magnitude_var.get() * 1e-6 # converted to micro Tesla - if calibration_mag_field <= 0 or calibration_mag_field > MAG_MAG_FIELD: + if calibration_mag_field <= 0 or calibration_mag_field > g.MAG_MAG_FIELD: raise MagFieldOutOfBounds self.calibration_thread = MagnetometerCalibrationSimple(self.view_mpi_queue, calibration_points, @@ -1552,20 +1565,26 @@ class CalibrateMagnetometerComplete(Frame): controls_frame = Frame(self.left_column) controls_frame.grid(row=row_counter, column=0, sticky="nw") # Magnitude of magnetic field - mag_field_magnitude_label = Label(controls_frame, text="Magnetic field magnitude [µT]") + mag_field_magnitude_label = Label(controls_frame, text="Magnetic field magnitude") mag_field_magnitude_label.grid(row=0, column=0, pady=5, sticky="nw") mag_field_magnitude_entry = Entry(controls_frame, textvariable=self.mag_field_magnitude_var) mag_field_magnitude_entry.grid(row=0, column=1, pady=5, sticky="nw") + mag_field_magnitude_unit = Label(controls_frame, text="µT (0 to {:.0f} µT)".format(g.MAG_MAG_FIELD * 1e6)) + mag_field_magnitude_unit.grid(row=0, column=2, pady=5, sticky="nw") # Number of calibration points calibration_point_nr_label = Label(controls_frame, text="# of calibration points") calibration_point_nr_label.grid(row=1, column=0, pady=5, sticky="nw") calibration_point_nr_entry = Entry(controls_frame, textvariable=self.calibration_points_var) calibration_point_nr_entry.grid(row=1, column=1, pady=5, sticky="nw") + calibration_point_nr__unit = Label(controls_frame, text="- (> 8 points)") + calibration_point_nr__unit.grid(row=1, column=2, pady=5, sticky="nw") # Measurement interval calibration_point_nr_label = Label(controls_frame, text="Measurement interval [s]") calibration_point_nr_label.grid(row=2, column=0, pady=5, sticky="nw") calibration_point_nr_entry = Entry(controls_frame, textvariable=self.calibration_interval_var) calibration_point_nr_entry.grid(row=2, column=1, pady=5, sticky="nw") + calibration_point_nr_unit = Label(controls_frame, text="s (> 2 s)") + calibration_point_nr_unit.grid(row=2, column=2, pady=5, sticky="nw") # Calibration start buttons start_button_frame = Frame(controls_frame) start_button_frame.grid(row=3, column=0, columnspan=1, sticky="nw") @@ -1857,7 +1876,7 @@ class CalibrateMagnetometerComplete(Frame): calibration_points = self.calibration_points_var.get() calibration_interval = self.calibration_interval_var.get() calibration_mag_field = self.mag_field_magnitude_var.get() * 1e-6 # converted to micro Tesla - if calibration_mag_field <= 0 or calibration_mag_field > MAG_MAG_FIELD: + if calibration_mag_field <= 0 or calibration_mag_field > g.MAG_MAG_FIELD: raise MagFieldOutOfBounds self.calibration_thread = MagnetometerCalibrationComplete(self.view_mpi_queue, calibration_points, From 6568515957b45c76aa372a6161da1d056ab76c0f Mon Sep 17 00:00:00 2001 From: Markus Koller Date: Sun, 29 Jan 2023 13:33:28 +0100 Subject: [PATCH 10/11] Implemented slightly different calibration routine from nliaudat https://github.com/nliaudat/magnetometer_calibration --- src/calibration.py | 94 +++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 84 insertions(+), 10 deletions(-) diff --git a/src/calibration.py b/src/calibration.py index 30254b0..de37766 100644 --- a/src/calibration.py +++ b/src/calibration.py @@ -242,7 +242,8 @@ class CoilConstantCalibration(Thread): class MagnetometerCalibrationSimple(Thread): - def __init__(self, view_queue, calibration_points, calibration_interval, calibration_mag_field, mgm_to_helmholtz_cos_trans): + def __init__(self, view_queue, calibration_points, calibration_interval, calibration_mag_field, + mgm_to_helmholtz_cos_trans): Thread.__init__(self) self.view_queue = view_queue self.calibration_points = calibration_points @@ -408,7 +409,8 @@ class MagnetometerCalibrationSimple(Thread): class MagnetometerCalibrationComplete(Thread): - def __init__(self, view_queue, calibration_points, calibration_interval, calibration_mag_field, mgm_to_helmholtz_cos_trans, right_column): + def __init__(self, view_queue, calibration_points, calibration_interval, calibration_mag_field, + mgm_to_helmholtz_cos_trans, right_column): Thread.__init__(self) self.view_queue = view_queue self.calibration_points = calibration_points @@ -444,7 +446,7 @@ class MagnetometerCalibrationComplete(Thread): def calibration_procedure(self): # According to method outlined in: # Chi, C. & Janosek, Lv, J-W., Wang, D (2018). Calibration of triaxial magnetometer with ellipsoid - # fitting method, DOI:10.1088/1755-1315/237/3/032015 + # fitting method, DOI:10.1088/1755-1315/237/3/032015 and https://github.com/nliaudat/magnetometer_calibration # This contains the raw experiment data for exporting # Each row is a dict containing the applied vector and measured vector @@ -580,7 +582,8 @@ class MagnetometerCalibrationComplete(Thread): # Retrieve calibration parameters q_mat_inv = np.linalg.inv(q_mat) b = -np.dot(q_mat_inv, n) - a_mat_inv = np.real(1 / csqrt(np.dot(n.T, np.dot(q_mat_inv, n)) - d) * linalg_scipy.sqrtm(q_mat)) + #a_mat_inv = np.real(1 / csqrt(np.dot(n.T, np.dot(q_mat_inv, n)) - d) * linalg_scipy.sqrtm(q_mat)) + a_mat_inv = np.real(mag_amp_avg_set / np.sqrt(np.dot(n.T, np.dot(q_mat_inv, n)) - d) * scipy.linalg.sqrtm(q_mat)) a_mat = np.linalg.inv(a_mat_inv) # Calculate error cal_x = np.zeros(mag_x_m.shape) @@ -590,14 +593,14 @@ class MagnetometerCalibrationComplete(Thread): for i in range(len(mag_x_m)): h = np.array([[mag_x_m[i], mag_y_m[i], mag_z_m[i]]]).T h_hat = np.matmul(a_mat_inv, h - b) # Scaling issue - cal_x[i] = h_hat[0] * mag_amp_avg_set - cal_y[i] = h_hat[1] * mag_amp_avg_set - cal_z[i] = h_hat[2] * mag_amp_avg_set + cal_x[i] = h_hat[0] + cal_y[i] = h_hat[1] + cal_z[i] = h_hat[2] mag = np.dot(h_hat.T, h_hat) err = (mag[0][0] - 1) ** 2 total_error += err # Scale unity solution back to original scaling - a_mat_inv = a_mat_inv * mag_amp_avg_set + a_mat_inv = a_mat_inv # Console output ui_print("Average magnitude: mag_amp_avg_set = {:.4f} uT, mag_amp_avg_m = {:.4f} uT".format( mag_amp_avg_set * 10 ** 6, mag_amp_avg_m * 10 ** 6)) @@ -698,7 +701,7 @@ class MagnetometerCalibrationComplete(Thread): return mag_x_set, mag_y_set, mag_z_set, mag_x_m, mag_y_m, mag_z_m @staticmethod - def fit_ellipsoid(mag_x_m, mag_y_m, mag_z_m): + def fit_ellipsoid_old(mag_x_m, mag_y_m, mag_z_m): # Script is adapted version from ThePoorEngineer - Calibrating the magnetometer # https://thepoorengineer.com/en/calibrating-the-magnetometer/#Calibration a1 = mag_x_m ** 2 @@ -753,7 +756,78 @@ class MagnetometerCalibrationComplete(Thread): return q_mat, n, d @staticmethod - def plot_magnetometer_calibration(target_column, mag_x_set, mag_y_set, mag_z_set, mag_x_m, mag_y_m, mag_z_m, cal_x, cal_y, + def fit_ellipsoid(mag_x_m, mag_y_m, mag_z_m): + """ Estimate ellipsoid parameters from a set of points. + Parameters + ---------- + mag_x_m, mag_y_m, mag_z_m : array_like, array_like, array_like + The samples (M,N) where M=3 (x,y,z) and N=number of samples. + Returns + ------- + s : array_like + The samples (M,N) where M=3 (x,y,z) and N=number of samples. + Returns + ------- + M, n, d : array_like, array_like, float + The ellipsoid parameters M, n, d. + References + ---------- + .. [1] Qingde Li; Griffiths, J.G., "Least squares ellipsoid specific + fitting," in Geometric Modeling and Processing, 2004. + Proceedings, vol., no., pp.335-340, 2004 + .. https://github.com/nliaudat/magnetometer_calibration/blob/main/calibrate.py + """ + + # Converts to samples (M,N) where M=3 (x,y,z) and N=number of samples. + s = np.array([mag_x_m, mag_y_m, mag_z_m]) + + # d (samples) + d = np.array([s[0] ** 2., s[1] ** 2., s[2] ** 2., + 2. * s[1] * s[2], 2. * s[0] * s[2], 2. * s[0] * s[1], + 2. * s[0], 2. * s[1], 2. * s[2], np.ones_like(s[0])]) + + # s, s_11, s_12, s_21, s_22 (eq. 11) + s = np.dot(d, d.T) + s_11 = s[:6, :6] + s_12 = s[:6, 6:] + s_21 = s[6:, :6] + s_22 = s[6:, 6:] + + # c (Eq. 8, k=4) + c = np.array([[-1, 1, 1, 0, 0, 0], + [1, -1, 1, 0, 0, 0], + [1, 1, -1, 0, 0, 0], + [0, 0, 0, -4, 0, 0], + [0, 0, 0, 0, -4, 0], + [0, 0, 0, 0, 0, -4]]) + + # v_1 (eq. 15, solution) + e = np.dot(np.linalg.inv(c), + s_11 - np.dot(s_12, np.dot(np.linalg.inv(s_22), s_21))) + + e_w, e_v = np.linalg.eig(e) + + v_1 = e_v[:, np.argmax(e_w)] + if v_1[0] < 0: + v_1 = -v_1 + + # v_2 (eq. 13, solution) + v_2 = np.dot(np.dot(-np.linalg.inv(s_22), s_21), v_1) + + # Quadratic-form parameters + m = np.array([[v_1[0], v_1[3], v_1[4]], + [v_1[3], v_1[1], v_1[5]], + [v_1[4], v_1[5], v_1[2]]]) + n = np.array([[v_2[0]], + [v_2[1]], + [v_2[2]]]) + d = v_2[3] + + return m, n, d + + @staticmethod + def plot_magnetometer_calibration(target_column, mag_x_set, mag_y_set, mag_z_set, mag_x_m, mag_y_m, mag_z_m, cal_x, + cal_y, cal_z, mag_amp_avg_set): plot_fontsize = 5 ax_width = 0.2 From 960ae27f6b52f916dbf6c9640089edb7cbce6ec3 Mon Sep 17 00:00:00 2001 From: Markus Koller Date: Sun, 29 Jan 2023 13:40:34 +0100 Subject: [PATCH 11/11] Added units to Magnetometer Results frame --- src/user_interface.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/user_interface.py b/src/user_interface.py index 5e5b23e..06136a8 100644 --- a/src/user_interface.py +++ b/src/user_interface.py @@ -1693,6 +1693,8 @@ class CalibrateMagnetometerComplete(Frame): width=15, state='readonly') axis_data.grid(row=row_counter + row, column=1 + column, padx=5, pady=5, sticky="nw") + results_label_unit = Label(calibration_results_frame, text="-") + results_label_unit.grid(row=row_counter + row, column=1 + column+1, padx=5, pady=5, sticky="nw") row_counter += 3 """ # A_mat @@ -1716,6 +1718,8 @@ class CalibrateMagnetometerComplete(Frame): width=15, state='readonly') axis_data.grid(row=row_counter, column=1 + row, padx=5, pady=5, sticky="nw") + results_label_unit = Label(calibration_results_frame, text="T") + results_label_unit.grid(row=row_counter, column=1 + row+1, padx=5, pady=5, sticky="nw") row_counter += 1 # Save calibration buttons