From effcc72966dfbcc0821097c515bdad27a98ed945 Mon Sep 17 00:00:00 2001 From: Markus Koller Date: Fri, 7 Oct 2022 16:01:00 +0200 Subject: [PATCH] Cleaned up minor issues --- src/calibration.py | 107 +++++++++++++++++++++---------------- src/user_interface.py | 120 +++++++++++++++++++++--------------------- 2 files changed, 121 insertions(+), 106 deletions(-) diff --git a/src/calibration.py b/src/calibration.py index 81a9264..bb23905 100644 --- a/src/calibration.py +++ b/src/calibration.py @@ -5,7 +5,6 @@ from threading import Thread import numpy as np from numpy.lib.scimath import sqrt as csqrt import matplotlib.pyplot as plt -from matplotlib.figure import Figure from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg from tkinter import LabelFrame import scipy.optimize @@ -14,7 +13,6 @@ from scipy import linalg as linalg_scipy from src.utility import ui_print from src.exceptions import DeviceBusy, DeviceAccessError import src.globals as g -# from src.user_interface import CalibrateMagnetometerComplete.mgm_to_helmholtz_cos_trans class AmbientFieldCalibration(Thread): @@ -28,6 +26,7 @@ class AmbientFieldCalibration(Thread): P_CONTROL = -7e3 # 0.2 A/s slew-rate at 40uT I_CONTROL = 0 # -1e4 # 0.01A/s slew-rate for 1uTs I_LIMIT = 1e-7 # uTs, Limit I to 0.025 A/s slew-rate to prevent wind-up + # D_CONTROL = Not implemented for now def __init__(self, view_queue): @@ -70,7 +69,7 @@ class AmbientFieldCalibration(Thread): target_time = 0 current_time = datetime.now() while (current_time - start_time).seconds < self.SETTLE_TIME: - # Each axis runs its own PID controller. They are slightly coupled by unorthogonality, which should + # Each axis runs its own PID controller. They are slightly coupled by non-orthogonality, which should # hopefully not destabilize the feedback loop for i in range(3): # Error in tesla @@ -78,11 +77,11 @@ class AmbientFieldCalibration(Thread): e = g.MAGNETOMETER.field[i] # Change in control current du = e * self.P_CONTROL + self.error_integral[i] * self.I_CONTROL - self.axis_currents[i] += du*dt + self.axis_currents[i] += du * dt # Update integral # Add increment - self.error_integral[i] += e*dt + self.error_integral[i] += e * dt # Clamp range self.error_integral = np.clip(self.error_integral, -self.I_LIMIT, self.I_LIMIT) @@ -156,7 +155,7 @@ class CoilConstantCalibration(Thread): # All generated fields will be compared to this using a simple difference method ambient_field = g.MAGNETOMETER.field - # This generates linearly spaced current setpoints and excludes zero + # This generates linearly spaced current set points and excludes zero currents = np.linspace(-self.MEASUREMENT_RANGE, self.MEASUREMENT_RANGE, self.MEASUREMENT_POINTS * 2 + 1) currents = np.delete(currents, self.MEASUREMENT_POINTS) @@ -347,12 +346,13 @@ class MagnetometerCalibrationSimple(Thread): b_e_x = g.CAGE_DEVICE.axes[0].ambient_field b_e_y = g.CAGE_DEVICE.axes[1].ambient_field b_e_z = g.CAGE_DEVICE.axes[2].ambient_field - b_e = sqrt(b_e_x**2 + b_e_y**2 + b_e_z**2) + b_e = sqrt(b_e_x ** 2 + b_e_y ** 2 + b_e_z ** 2) # Perform least squares optimization on all magnetometer axes sensor_parameters = [] for axis, axis_samples in enumerate(samples): - result = scipy.optimize.least_squares(self.residual_function, (1.0, pi/4, pi/4, pi/4), args=(b_e, axis_samples), gtol=1e-13) + result = scipy.optimize.least_squares(self.residual_function, (1.0, pi / 4, pi / 4, pi / 4), + args=(b_e, axis_samples), gtol=1e-13) s, alpha_e, alpha, beta = result.x residual = np.max(np.abs(result.fun)) sensor_parameters.append({'sensitivity': s, @@ -377,7 +377,9 @@ class MagnetometerCalibrationSimple(Thread): b_x = sample['b_x'] b_y = sample['b_y'] b_z = sample['b_z'] - res.append(m - s * (b_e*sin(alpha_e) + b_x*cos(alpha)*cos(beta) + b_y*cos(alpha)*sin(beta) + b_z*sin(alpha))) + res.append(m - s * ( + b_e * sin(alpha_e) + b_x * cos(alpha) * cos(beta) + b_y * cos(alpha) * sin(beta) + b_z * sin( + alpha))) return res @staticmethod @@ -453,9 +455,8 @@ class MagnetometerCalibrationComplete(Thread): self.cage_dev.set_field_compensated([0, 0, 0]) # Sleep for a certain duration to allow psu to stabilize output and magnetometer to supply readings time.sleep(self.calibration_interval) - matrix_trans_mgm_to_hh_np = np.array(self.matrix_trans_mgm_to_hh) # The offsets can easily be read from the magnetometer - offsets = matrix_trans_mgm_to_hh_np.dot(g.MAGNETOMETER.field) + offsets = g.MAGNETOMETER.field # Save data point to raw_data list raw_data.append({'applied_x': 0, 'applied_y': 0, 'applied_z': 0, 'measured_x': offsets[0], 'measured_y': offsets[1], 'measured_z': offsets[2]}) @@ -465,7 +466,7 @@ class MagnetometerCalibrationComplete(Thread): # Generate our set of test vectors test_vectors = self.fibonacci_sphere(self.calibration_points) - # Holds the knowns for each row of our system of equations. These are M, B_x, B_y, B_z + # Holds the known variables for each row of our system of equations. These are M, B_x, B_y, B_z # (B_E is constant for the test and not stored in the array) # Each sensor axis has its own independent system of equations samples = [[], [], []] @@ -479,7 +480,7 @@ class MagnetometerCalibrationComplete(Thread): time.sleep(self.calibration_interval) # Read output and save to array for solver later - raw_reading = matrix_trans_mgm_to_hh_np.dot(g.MAGNETOMETER.field) + raw_reading = g.MAGNETOMETER.field reading = raw_reading - offsets for i in range(3): row = {'m': reading[i], 'b_x': applied_vec[0], 'b_y': applied_vec[1], 'b_z': applied_vec[2]} @@ -497,7 +498,9 @@ class MagnetometerCalibrationComplete(Thread): self.cage_dev.idle() # Use collected data to build and solve system of equations - sensor_parameters = self.solve_system(raw_data) # FLAG: compare to csv import + # sensor_parameters = self.solve_system(raw_data) # FLAG: untested! + 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( + self.view_queue, raw_data, self.mgm_to_helmholtz_cos_trans) # Pass results to UI self.put_message('calibration_data', {'results': sensor_parameters, 'raw_data': raw_data}) @@ -528,8 +531,8 @@ class MagnetometerCalibrationComplete(Thread): 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 = [[-1, 0, 0], [0, 1, 0], [0, 0, -1]] # FLAG: Hardcoded! for MGM 1 / 3 - # matrix_trans_mgm_to_hh_np = [[0, 1, 0], [-1, 0, 0], [0, 0, 1]] # FLAG: Hardcoded! for MGM 0 / 2 + # 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}") ui_print(matrix_trans_mgm_to_hh_np) # Transform hh set magnetic field cos to sensor cos @@ -538,7 +541,6 @@ class MagnetometerCalibrationComplete(Thread): mag_x_set = mag_xyz_set_hh[:, 0] mag_y_set = mag_xyz_set_hh[:, 1] mag_z_set = mag_xyz_set_hh[:, 2] - # FLAG: Currently the coordinate transformation is applied two times (see calibration class) # Calculate total error err_x = 0 err_y = 0 @@ -549,12 +551,14 @@ class MagnetometerCalibrationComplete(Thread): err_z += (mag_z_m[i] - mag_z_set[i]) ** 2 err_t = np.sqrt(err_x ** 2 + err_y ** 2 + err_z ** 2) ui_print('Error in X/Y/Z/total: {:.2e} / {:.2e} / {:.2e} / {:.2e} [uT]'.format(err_x * u_tesla, err_y * u_tesla, - err_z * u_tesla, err_t * u_tesla)) + err_z * u_tesla, + err_t * u_tesla)) # Filter raw data try: mag_x_set, mag_y_set, mag_z_set, mag_x_m, mag_y_m, mag_z_m = \ - MagnetometerCalibrationComplete.filter_magnetometer_data(self, mag_x_set, mag_y_set, mag_z_set, mag_x_m, mag_y_m, mag_z_m) + MagnetometerCalibrationComplete.filter_magnetometer_data(mag_x_set, mag_y_set, mag_z_set, + mag_x_m, mag_y_m, mag_z_m) except Warning as waring_filter_mgm_data: ui_print(f"{waring_filter_mgm_data}") # Get general magnitude of the set magnetic filed @@ -567,10 +571,10 @@ class MagnetometerCalibrationComplete(Thread): # Calculate ellipsoid fit try: - q_mat, n, d = MagnetometerCalibrationComplete.fit_ellipsoid(self, mag_x_m, mag_y_m, mag_z_m) + q_mat, n, d = MagnetometerCalibrationComplete.fit_ellipsoid(mag_x_m, mag_y_m, mag_z_m) ui_print('Q matrix =') ui_print(q_mat) - ui_print('n =') + ui_print('n = [T]') ui_print(n) # Retrieve calibration parameters q_mat_inv = np.linalg.inv(q_mat) @@ -584,7 +588,7 @@ class MagnetometerCalibrationComplete(Thread): total_error = 0 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) # FLAG: Scaling issue + 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 @@ -598,9 +602,9 @@ class MagnetometerCalibrationComplete(Thread): mag_amp_avg_set * 10 ** 6, mag_amp_avg_m * 10 ** 6)) ui_print('Soft-iron correction matrix a_mat_inv = ') ui_print(a_mat_inv) - ui_print('Hard-iron offset b =') + ui_print('Hard-iron offset b = [T]') ui_print(b) - ui_print('Total error E = ', total_error) + ui_print("Normalized total error E = {:4e} [0..1]]".format(total_error)) sensor_parameters = [{'a_mat': a_mat.tolist(), 'a_mat_inv': a_mat_inv.tolist(), 'b': b.tolist(), @@ -613,18 +617,19 @@ class MagnetometerCalibrationComplete(Thread): ui_print('A_inv could not be calculated! A warning occurred.') ui_print('Please check if transformation matrix is input correctly.') ui_print(f"{warning_message}") - except Exception as exception_message: + except Exception as exception_message: ui_print('A_inv could not be calculated! An unknown error occurred.') ui_print('Please check if transformation matrix is input correctly.') ui_print(f"{exception_message}") - def filter_magnetometer_data(self, mag_x_set, mag_y_set, mag_z_set, mag_x_m, mag_y_m, mag_z_m): + @staticmethod + def filter_magnetometer_data(mag_x_set, mag_y_set, mag_z_set, mag_x_m, mag_y_m, mag_z_m): flag_validity = np.ones(len(mag_x_set)) # Issue 1: Sometimes, the old and new measurement are very similar. # The set magnetic field has not changed fast enough. for i in range(1, len(mag_x_set)): - # Flag entry if all measurement results are very close to one another + # Filter entry if all measurement results are very close to one another if mag_x_m[i - 1] != 0: div_x_m = mag_x_m[i] / mag_x_m[i - 1] else: @@ -657,7 +662,7 @@ class MagnetometerCalibrationComplete(Thread): flag_validity[i] = 0 if bound_low < div_z_m < bound_up and not bound_low < div_z_set < bound_up: flag_validity[i] = 0 - # Flag entry if signs of measurement result and set result are different + # Filter entry if signs of measurement result and set result are different thresh = 70.0e-06 # [T] threshold value, difference in magnetic field component too large to be reasonable if np.abs(mag_x_set[i] - mag_x_m[i]) > thresh or np.abs(mag_y_set[i] - mag_y_m[i]) > thresh or np.abs( mag_z_set[i] - mag_z_m[i]) > thresh: @@ -691,7 +696,8 @@ class MagnetometerCalibrationComplete(Thread): else: return mag_x_set, mag_y_set, mag_z_set, mag_x_m, mag_y_m, mag_z_m - def fit_ellipsoid(self, mag_x_m, mag_y_m, mag_z_m): + @staticmethod + def fit_ellipsoid(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 @@ -755,13 +761,13 @@ class MagnetometerCalibrationComplete(Thread): # Plot calibrated results fig1 = plt.figure('MGM_cal_complete_left', figsize=(2.5, 3), dpi=100) - fig1.clf() # clear figure from previous use + fig1.clf() # clear figure from previous use canvas1 = FigureCanvasTkAgg(fig1, plot_frame) u_tesla = 10 ** 6 # Tesla to microTesla conversion factor meas_no = len(mag_x_set) # Measurement number # uncalibrated result plot - ax1 = fig1.add_subplot(211, projection='3d') + ax1 = fig1.add_subplot(221, projection='3d') # ax1.clf() ax1.set_xlabel(r'$B_x [{\mu}T]$', fontsize=plot_fontsize) ax1.set_ylabel(r'$B_y [{\mu}T]$', fontsize=plot_fontsize) @@ -773,11 +779,11 @@ class MagnetometerCalibrationComplete(Thread): mag_z_m * u_tesla): ax1.plot([i, l], [j, m], [k, n], linewidth=0.5, color='b') # set magnetic vectors - ax1.scatter(mag_x_set * u_tesla, mag_y_set * u_tesla, mag_z_set * u_tesla, s=5, color='k', - label="$B_{set}$ Helmholtz field") + l1 = ax1.scatter(mag_x_set * u_tesla, mag_y_set * u_tesla, mag_z_set * u_tesla, s=5, color='k', + label="$B_{set}$ Helmholtz field") # measured values - ax1.scatter(mag_x_m * u_tesla, mag_y_m * u_tesla, mag_z_m * u_tesla, s=5, color='b', - label="$B_{raw}$ uncalibrated") + l2 = ax1.scatter(mag_x_m * u_tesla, mag_y_m * u_tesla, mag_z_m * u_tesla, s=5, color='b', + label="$B_{raw}$ uncalibrated") # plot sphere with magnitude u = np.linspace(0, 2 * np.pi, 100) v = np.linspace(0, np.pi, 100) @@ -786,10 +792,10 @@ class MagnetometerCalibrationComplete(Thread): z = np.outer(np.ones(np.size(u)), np.cos(v)) * mag_amp_avg_set ax1.plot_wireframe(x * u_tesla, y * u_tesla, z * u_tesla, rstride=10, cstride=10, alpha=0.7, color='y') ax1.plot_surface(x * u_tesla, y * u_tesla, z * u_tesla, alpha=0.3, color='y') - ax1.legend(loc='upper right', fontsize=plot_fontsize) + # ax1.legend(loc='upper right', fontsize=plot_fontsize) # Calibrated result plot - ax2 = fig1.add_subplot(212, projection='3d') + ax2 = fig1.add_subplot(223, projection='3d') ax2.set_xlabel(r'$B_x [\mu T]$', fontsize=plot_fontsize) ax2.set_ylabel(r'$B_y [\mu T]$', fontsize=plot_fontsize) ax2.set_zlabel(r'$B_z [\mu T]$', fontsize=plot_fontsize) @@ -800,11 +806,11 @@ class MagnetometerCalibrationComplete(Thread): cal_z * u_tesla): ax2.plot([i, l], [j, m], [k, n], linewidth=0.5, color='r') # set magnetic vectors - ax2.scatter(mag_x_set * u_tesla, mag_y_set * u_tesla, mag_z_set * u_tesla, s=5, color='k', - label="$B_{set}$ Helmholtz field") + l3 = ax2.scatter(mag_x_set * u_tesla, mag_y_set * u_tesla, mag_z_set * u_tesla, s=5, color='k', + label="$B_{set}$ Helmholtz field") # calibrated values ellipsoid fit - ax2.scatter(cal_x * u_tesla, cal_y * u_tesla, cal_z * u_tesla, s=5, color='r', - label="$B_{cal,el}$ ellipsoid fit") + l4 = ax2.scatter(cal_x * u_tesla, cal_y * u_tesla, cal_z * u_tesla, s=5, color='r', + label="$B_{cal,el}$ ellipsoid fit") # plot sphere with magnitude u = np.linspace(0, 2 * np.pi, 100) v = np.linspace(0, np.pi, 100) @@ -813,13 +819,21 @@ class MagnetometerCalibrationComplete(Thread): z = np.outer(np.ones(np.size(u)), np.cos(v)) * mag_amp_avg_set ax2.plot_wireframe(x * u_tesla, y * u_tesla, z * u_tesla, rstride=10, cstride=10, alpha=0.7, color='y') ax2.plot_surface(x * u_tesla, y * u_tesla, z * u_tesla, alpha=0.3, color='y') - ax2.legend(loc='upper right', fontsize=plot_fontsize) + # ax2.legend(loc='upper right', fontsize=plot_fontsize) + ax3 = fig1.add_subplot(222) + ax3.axis('off') + ax3.legend([l1, l2], ["$B_{set}$", "$B_{raw}$"], + loc='upper right', fontsize=plot_fontsize) + ax4 = fig1.add_subplot(224) + ax4.axis('off') + ax4.legend([l3, l4], ["$B_{set}$", "$B_{cal,el}$"], + loc='upper right', fontsize=plot_fontsize) canvas1.draw() canvas1.get_tk_widget().grid(row=0, column=0) # 2d_math plots - fig2 = plt.figure('MGM_cal_complete_right',figsize=(4, 3), dpi=100) - fig2.clf() # clear figure from previous use + fig2 = plt.figure('MGM_cal_complete_right', figsize=(4, 3), dpi=100) + fig2.clf() # clear figure from previous use canvas2 = FigureCanvasTkAgg(fig2, plot_frame) # x panel ax3 = fig2.add_subplot(311) @@ -873,7 +887,8 @@ class MagnetometerCalibrationComplete(Thread): b_x = sample['b_x'] b_y = sample['b_y'] b_z = sample['b_z'] - res.append(m - s * (b_e*sin(alpha_e) + b_x*cos(alpha)*cos(beta) + b_y*cos(alpha)*sin(beta) + b_z*sin(alpha))) + res.append(m - s * (b_e * sin(alpha_e) + b_x * cos(alpha) * cos(beta) + + b_y * cos(alpha) * sin(beta) + b_z * sin(alpha))) return res @staticmethod @@ -898,4 +913,4 @@ class MagnetometerCalibrationComplete(Thread): return points def put_message(self, command, arg): - self.view_queue.put({'cmd': command, 'arg': arg}) \ No newline at end of file + self.view_queue.put({'cmd': command, 'arg': arg}) diff --git a/src/user_interface.py b/src/user_interface.py index c3a5696..b5cc2c1 100644 --- a/src/user_interface.py +++ b/src/user_interface.py @@ -9,10 +9,7 @@ from tkinter import * from tkinter import ttk from tkinter import messagebox from tkinter import filedialog -import matplotlib as plt -from matplotlib.figure import Figure from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg -from matplotlib.backends.backend_tkagg import NavigationToolbar2Tk # import general packages: import numpy as np @@ -26,7 +23,8 @@ import src.globals as g import src.csv_threading as csv_threading import src.config_handling as config import src.csv_logging as log -from src.calibration import AmbientFieldCalibration, CoilConstantCalibration, MagnetometerCalibrationSimple, MagnetometerCalibrationComplete +from src.calibration import AmbientFieldCalibration, CoilConstantCalibration, MagnetometerCalibrationSimple, \ + MagnetometerCalibrationComplete from src.exceptions import DeviceAccessError 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 @@ -991,7 +989,7 @@ class CalibrateMagnetometerSimple(Frame): row_counter = 0 # Create headline - header = Label(self.left_column, text="Magnetometer Calibration\n-simlified method-", font=HEADER_FONT) + header = Label(self.left_column, text="Magnetometer Calibration\n-simplified method-", font=HEADER_FONT) header.grid(row=row_counter, column=0, columnspan=2, padx=100, pady=20, sticky="nw") row_counter += 1 @@ -1140,7 +1138,6 @@ class CalibrateMagnetometerSimple(Frame): label = "-Implementation according to Zikmund et al. [DOI: 10.1109/I2MTC.2014.6860790]\n-Points created by Fibonacci sphere\n-Only accounts for hard-iron offset and MGM scaling errors!" calibration_method_notes = Label(calibration_method_notes_frame, anchor='w', justify='left', text=label) calibration_method_notes.grid(row=3, column=0, padx=5, pady=5, sticky="nw") - # FLAG # RIGHT COLUMN # Input coordinate system conversion matrix @@ -1175,8 +1172,8 @@ class CalibrateMagnetometerSimple(Frame): angle_to_plane_label.grid(row=3, column=0, padx=5, pady=5, sticky="nw") for i in range(3): axis_data = Entry(input_cos_frame, - textvariable=self.mgm_to_helmholtz_cos_trans[2][i], - width=15) + textvariable=self.mgm_to_helmholtz_cos_trans[2][i], + width=15) axis_data.grid(row=3, column=i + 1, padx=5, pady=5, sticky="nw") angle_to_plane_unit = Label(input_cos_frame, text="-") angle_to_plane_unit.grid(row=3, column=4, padx=5, pady=5, sticky="nw") @@ -1303,9 +1300,9 @@ class CalibrateMagnetometerSimple(Frame): calibration_points = self.calibration_points_var.get() calibration_interval = self.calibration_interval_var.get() self.calibration_thread = MagnetometerCalibrationSimple(self.view_mpi_queue, - calibration_points, - calibration_interval, - self.mgm_to_helmholtz_cos_trans) + calibration_points, + calibration_interval, + self.mgm_to_helmholtz_cos_trans) self.calibration_thread.start() self.deactivate_buttons() except (DeviceAccessError, TclError) as e: @@ -1364,13 +1361,14 @@ class CalibrateMagnetometerSimple(Frame): matrix = [[x.get() for x in row] for row in self.mgm_to_helmholtz_cos_trans] matrix = np.array(matrix) ui_print(matrix) - #matrix_max = matrix.max() - #matrix_min = matrix.min() - #matrix = (matrix - matrix_min) / (matrix_max - matrix_min) - def gram_schmidt_columns(X): - Q, R = np.linalg.qr(X) - return Q + # matrix_max = matrix.max() + # matrix_min = matrix.min() + # matrix = (matrix - matrix_min) / (matrix_max - matrix_min) + + def gram_schmidt_columns(x_mat): + q_mat, r_mat = np.linalg.qr(x_mat) + return q_mat matrix = gram_schmidt_columns(matrix) ui_print("Normalized matrix (Gram-Schmidt):") @@ -1385,6 +1383,7 @@ class CalibrateMagnetometerSimple(Frame): [DoubleVar(value=0), DoubleVar(value=1), DoubleVar(value=0)], [DoubleVar(value=0), DoubleVar(value=0), DoubleVar(value=1)]] + class CalibrateMagnetometerComplete(Frame): def __init__(self, parent, controller): Frame.__init__(self, parent) @@ -1416,17 +1415,16 @@ class CalibrateMagnetometerComplete(Frame): self.calibration_points_var = IntVar(value=8) self.calibration_interval_var = DoubleVar(value=5) # Calibration results - # FLAG self.a_mat_result_vars = [[DoubleVar(), DoubleVar(), DoubleVar()], [DoubleVar(), DoubleVar(), DoubleVar()], - [DoubleVar(), DoubleVar(), DoubleVar()],] + [DoubleVar(), DoubleVar(), DoubleVar()], ] self.a_mat_inv_result_vars = [[DoubleVar(), DoubleVar(), DoubleVar()], [DoubleVar(), DoubleVar(), DoubleVar()], - [DoubleVar(), DoubleVar(), DoubleVar()],] + [DoubleVar(), DoubleVar(), DoubleVar()], ] self.b_result_vars = [DoubleVar(), DoubleVar(), DoubleVar()] self.q_mat_result_vars = [[DoubleVar(), DoubleVar(), DoubleVar()], [DoubleVar(), DoubleVar(), DoubleVar()], - [DoubleVar(), DoubleVar(), DoubleVar()],] + [DoubleVar(), DoubleVar(), DoubleVar()], ] self.n_result_vars = [DoubleVar(), DoubleVar(), DoubleVar()] self.mag_amp_avg_set_result_vars = DoubleVar() self.total_error_result_vars = DoubleVar() @@ -1578,14 +1576,14 @@ class CalibrateMagnetometerComplete(Frame): row_counter += 1 # A_mat_inv results_label_a_mat_inv = Label(calibration_results_frame, text="A^-1 =") - results_label_a_mat_inv.grid(row=row_counter+1, column=0, padx=5, pady=5, sticky="nw") + results_label_a_mat_inv.grid(row=row_counter + 1, column=0, padx=5, pady=5, sticky="nw") for row in range(3): for column in range(3): axis_data = Entry(calibration_results_frame, textvariable=self.a_mat_inv_result_vars[row][column], width=15, state='readonly') - axis_data.grid(row=row_counter+row, column=1 + column, padx=5, pady=5, sticky="nw") + axis_data.grid(row=row_counter + row, column=1 + column, padx=5, pady=5, sticky="nw") row_counter += 3 """ # A_mat @@ -1604,11 +1602,11 @@ class CalibrateMagnetometerComplete(Frame): results_label_a_mat_inv = Label(calibration_results_frame, text="b^T =") results_label_a_mat_inv.grid(row=row_counter, column=0, padx=5, pady=5, sticky="nw") for row in range(3): - axis_data = Entry(calibration_results_frame, - textvariable=self.b_result_vars[row], #FLAG _result_vars does not compute - width=15, - state='readonly') - axis_data.grid(row=row_counter, column=1 + row, padx=5, pady=5, sticky="nw") + axis_data = Entry(calibration_results_frame, + textvariable=self.b_result_vars[row], + width=15, + state='readonly') + axis_data.grid(row=row_counter, column=1 + row, padx=5, pady=5, sticky="nw") row_counter += 1 # Save calibration buttons @@ -1626,9 +1624,9 @@ class CalibrateMagnetometerComplete(Frame): pady=5, padx=5) self.copy_calibration_button.grid(row=0, column=1, padx=5, pady=5) self.import_calibration_data_button = Button(save_calibration_results_frame, text="Import data set", - command=self.import_csv_calibration_raw_data, - state="active", - pady=5, padx=5) + command=self.import_csv_calibration_raw_data, + state="active", + pady=5, padx=5) self.import_calibration_data_button.grid(row=0, column=2, padx=5, pady=5) # RIGHT COLUMN @@ -1695,7 +1693,7 @@ class CalibrateMagnetometerComplete(Frame): self.calibration_raw_results = results['raw_data'] # Unpack the dict - result = results['results'] # Flag: 'results' replaced with 'raw_data' + result = results['results'] # Display calibration in GUI self.mag_amp_avg_set_result_vars.set("{:.3e}".format(result[0]['mag_amp_avg_set'])) @@ -1704,7 +1702,6 @@ class CalibrateMagnetometerComplete(Frame): self.b_result_vars[row].set("{:.3e}".format(result[0]['b'][row][0])) self.n_result_vars[row].set("{:.3e}".format(result[0]['n'][row][0])) for column in range(3): - #FLAG self.a_mat_result_vars[row][column].set("{:.6f}".format(result[0]['a_mat'][row][column])) self.a_mat_inv_result_vars[row][column].set("{:.6f}".format(result[0]['a_mat_inv'][row][column])) self.q_mat_result_vars[row][column].set("{:.6f}".format(result[0]['q_mat'][row][column])) @@ -1732,7 +1729,7 @@ class CalibrateMagnetometerComplete(Frame): for column in range(3): self.clipboard += "{:.6e}\t".format(result[0]['q_mat'][row][column]) self.clipboard += "\n" - self.clipboard += "n [-]\n" + self.clipboard += "n [T]\n" for row in range(3): self.clipboard += "{:.6e}\t".format(result[0]['n'][row][0]) @@ -1745,9 +1742,9 @@ class CalibrateMagnetometerComplete(Frame): calibration_points = self.calibration_points_var.get() calibration_interval = self.calibration_interval_var.get() self.calibration_thread = MagnetometerCalibrationComplete(self.view_mpi_queue, - calibration_points, - calibration_interval, - self.mgm_to_helmholtz_cos_trans) + calibration_points, + calibration_interval, + self.mgm_to_helmholtz_cos_trans) self.calibration_thread.start() self.deactivate_buttons() except (DeviceAccessError, TclError) as e: @@ -1774,9 +1771,11 @@ class CalibrateMagnetometerComplete(Frame): self.calibration_raw_results = raw_data # 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(self.view_mpi_queue, raw_data, self.mgm_to_helmholtz_cos_trans) - MagnetometerCalibrationComplete.plot_magnetometer_calibration(self, 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) + 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( + self.view_mpi_queue, raw_data, self.mgm_to_helmholtz_cos_trans) + MagnetometerCalibrationComplete.plot_magnetometer_calibration(self, 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) # Pass results to UI self.put_message('calibration_data', {'results': sensor_parameters, 'raw_data': raw_data}) @@ -1799,7 +1798,7 @@ class CalibrateMagnetometerComplete(Frame): ui_print("Saved MGM to Helmholtz coordinate transformation matrix to magnetometer_cos_trans_matrix.csv.") def put_message(self, command, arg): - self.view_mpi_queue.put({'cmd': command, 'arg': arg}) #FLAG not used? + self.view_mpi_queue.put({'cmd': command, 'arg': arg}) def copy_to_clipboard(self): self.clipboard_clear() @@ -1829,13 +1828,14 @@ class CalibrateMagnetometerComplete(Frame): matrix = [[x.get() for x in row] for row in self.mgm_to_helmholtz_cos_trans] matrix = np.array(matrix) ui_print(matrix) - #matrix_max = matrix.max() - #matrix_min = matrix.min() - #matrix = (matrix - matrix_min) / (matrix_max - matrix_min) - def gram_schmidt_columns(X): - Q, R = np.linalg.qr(X) - return Q + # matrix_max = matrix.max() + # matrix_min = matrix.min() + # matrix = (matrix - matrix_min) / (matrix_max - matrix_min) + + def gram_schmidt_columns(x_mat): + q_mat, r_mat = np.linalg.qr(x_mat) + return q_mat matrix = gram_schmidt_columns(matrix) ui_print("Normalized matrix (Gram-Schmidt):") @@ -2251,7 +2251,7 @@ class ConfigureLogging(Frame): # generate and place all checkboxes: row = 0 column = 0 - for key, name in log.logging_selection_options.items(): # go through all loggable values + for key, name in log.logging_selection_options.items(): # go through all logging values self.checkbox_vars[key] = BooleanVar(value=True) # create variable for checkbox and put it in dictionary checkbox = Checkbutton(self.checkbox_frame, text=name, # generate checkbox variable=self.checkbox_vars[key], onvalue=True, offvalue=False) @@ -2386,26 +2386,26 @@ class StatusDisplay(Frame): self.grid_rowconfigure(ALL, weight=1) self.grid_columnconfigure(ALL, weight=1) - rowCounter = 0 # keep track of which row we are at in the grid layout + row_counter = 0 # keep track of which row we are at in the grid layout x_pad = 10 # centrally set padding # create column headers (X-Axis etc.) col = 0 for header in ["", "X-Axis", "Y-Axis", "Z-Axis"]: # define Column headers # create label: - headLabel = Label(self, text=header, font=SUB_HEADER_FONT, borderwidth=1, - relief="flat", anchor="w", padx=x_pad) - headLabel.grid(row=rowCounter, column=col, sticky="ew") + head_label = Label(self, text=header, font=SUB_HEADER_FONT, borderwidth=1, + relief="flat", anchor="w", padx=x_pad) + head_label.grid(row=row_counter, column=col, sticky="ew") col = col + 1 # move to next column - rowCounter += 1 # increase row counter to place future stuff below header + row_counter += 1 # increase row counter to place future stuff below header # define content of row entries: - TextLabels = ["PSU Serial Port:", "PSU Channel:", "PSU Status:", "Arduino Status:", "", "Output:", - "Remote Control:", - "Voltage Setpoint:", "Actual Voltage:", "Current Setpoint:", "Actual Current:", "", - "Target Field:", "Trgt. Field Raw:", "Target Current:", "Inverted:"] - self.rowNo = len(TextLabels) # get number of label rows + text_labels = ["PSU Serial Port:", "PSU Channel:", "PSU Status:", "Arduino Status:", "", "Output:", + "Remote Control:", + "Voltage Setpoint:", "Actual Voltage:", "Current Setpoint:", "Actual Current:", "", + "Target Field:", "Trgt. Field Raw:", "Target Current:", "Inverted:"] + self.rowNo = len(text_labels) # get number of label rows self.columnNo = 4 # number of label columns # prepare list of lists to contain all labels for row entries in all columns: @@ -2413,7 +2413,7 @@ class StatusDisplay(Frame): # create dictionary to associate (changing) label variables with their labels: self.label_dict = {} # initialize dictionary - for name in TextLabels: # go through all rows + for name in text_labels: # go through all rows self.label_dict[name] = [StringVar() for _ in range(self.columnNo - 1)] # create variables for labels # add static labels for row titles: self.Labels[0].append(Label(self, text=name, borderwidth=1, relief="flat", anchor="w", padx=x_pad)) @@ -2426,7 +2426,7 @@ class StatusDisplay(Frame): col = 0 for LabelCol in self.Labels: # go through table columns for row in range(self.rowNo): # go through table rows - LabelCol[row].grid(row=row + rowCounter, column=col, sticky="nsew") # place label + LabelCol[row].grid(row=row + row_counter, column=col, sticky="nsew") # place label col += 1 # Register callback to populate new data: