craft-software/Legacy/TF_Control/Main.py
2025-07-04 15:52:40 +02:00

1096 lines
66 KiB
Python

from PyQt6.QtGui import *
from PyQt6.QtWidgets import *
from PyQt6.QtCore import *
import multiprocessing
import multiprocessing.managers
import time
from timeit import default_timer as timer
from datetime import datetime
import collections
from simple_pid import PID
import traceback, sys, os
import numpy as np
import pyqtgraph as pg
from design_files.Main_design import Ui_MainWindow
# Get the current script's directory
current_dir = os.path.dirname(os.path.abspath(__file__))
# Get the parent directory by going one level up
parent_dir = os.path.dirname(current_dir)
# Add the parent directory to sys.path
sys.path.append(parent_dir)
from scripts import import_txt
from scripts import analyse_cooldown_tools
class WorkerSignals(QObject):
'''
Defines the signals available from a running worker thread.
Supported signals are:
finished: No data
error: tuple (exctype, value, traceback.format_exc() )
result: object data returned from processing, anything
progress: int indicating % progress
'''
finished = pyqtSignal()
error = pyqtSignal(tuple)
result = pyqtSignal(object)
progress = pyqtSignal(list)
class Worker(QRunnable):
'''
Worker thread
Inherits from QRunnable to handler worker thread setup, signals and wrap-up.
:param callback: The function callback to run on this worker thread. Supplied args and
kwargs will be passed through to the runner.
:type callback: function
:param args: Arguments to pass to the callback function
:param kwargs: Keywords to pass to the callback function
'''
def __init__(self, fn, *args, **kwargs):
super(Worker, self).__init__()
# Store constructor arguments (re-used for processing)
self.fn = fn
self.args = args
self.kwargs = kwargs
self.signals = WorkerSignals()
# Add the callback to our kwargs
self.kwargs['progress_callback'] = self.signals.progress
@pyqtSlot()
def run(self):
'''
Initialise the runner function with passed args, kwargs.
'''
# Retrieve args/kwargs here; and fire processing using them
try:
result = self.fn(*self.args, **self.kwargs)
except:
traceback.print_exc()
exctype, value = sys.exc_info()[:2]
self.signals.error.emit((exctype, value, traceback.format_exc()))
else:
self.signals.result.emit(result) # Return the result of the processing
finally:
self.signals.finished.emit() # Done
def get_float(Qline,default = 0): #gets value from QLineEdit and converts it to float. If text is empty or cannot be converted, it returns "default" which is 0, if not specified
try:
out = float(Qline.text())
except:
out = default
return(out)
def update_single_entry(dict,name,ind,val):
'''updates a single value in global vars. To do so it gets the current value of "dict('name')", replaces "val" at indec "ind" and sends this back'''
data = dict.get(f"{name}") #get data
data[ind] = val #replace entry
dict.update({f"{name}":data}) #send data back
class MainWindow(QMainWindow, Ui_MainWindow):
def __init__(self, *args, **kwargs):
# Get the current script's directory
self.current_dir = os.path.dirname(os.path.abspath(__file__))
# Get the parent directory by going one level up
self.parent_dir = os.path.dirname(current_dir)
#establish connection to global variables}
try: #try to connect to global variables
manager = multiprocessing.managers.BaseManager(address=('localhost',5001), authkey=b'')
manager.connect()
manager.register('syncdict')
manager.register('sync_BK_9174B')
manager.register('sync_BK_9174B_2')
manager.register('sync_BK_9132B_2')
manager.register('sync_BK_9132B')
manager.register('sync_BK_9131B')
manager.register('sync_Keithley_2230G')
manager.register('sync_LS_336')
manager.register('sync_LS_218')
manager.register('sync_imc')
manager.register('sync_converted')
manager.register('sync_main')
self.syncdict = manager.syncdict()
self.sync_BK_9174B = manager.sync_BK_9174B()
self.sync_BK_9174B_2 = manager.sync_BK_9174B_2()
self.sync_BK_9132B_2 = manager.sync_BK_9132B_2()
self.sync_BK_9132B = manager.sync_BK_9132B()
self.sync_BK_9131B = manager.sync_BK_9131B()
self.sync_Keithley_2230G = manager.sync_Keithley_2230G()
self.sync_LS_336 = manager.sync_LS_336()
self.sync_LS_218 = manager.sync_LS_218()
self.sync_imc = manager.sync_imc()
self.sync_converted = manager.sync_converted()
self.sync_main = manager.sync_main()
except: #open global variables, if no connection can be made (i.e. it is not running). Then connect to it
# subprocess.call(['D:\\Python instrument drivers\\env\\Scripts\\python.exe', 'D:\\Python instrument drivers\\StandAlones\\global_variables.py'])
self.global_vars = QProcess()
self.global_vars.start(self.current_dir+"\\.venv\\Scripts\\python.exe", [self.current_dir+'\\global_variables_TF.py'])
manager.connect()
manager.register('syncdict')
manager.register('sync_BK_9174B')
manager.register('sync_BK_9174B_2')
manager.register('sync_BK_9132B_2')
manager.register('sync_BK_9132B')
manager.register('sync_BK_9131B')
manager.register('sync_Keithley_2230G')
manager.register('sync_LS_336')
manager.register('sync_LS_218')
manager.register('sync_imc')
manager.register('sync_converted')
manager.register('sync_main')
self.syncdict = manager.syncdict()
self.sync_BK_9174B = manager.sync_BK_9174B()
self.sync_BK_9174B_2 = manager.sync_BK_9174B_2()
self.sync_BK_9132B_2 = manager.sync_BK_9132B_2()
self.sync_BK_9132B = manager.sync_BK_9132B()
self.sync_BK_9131B = manager.sync_BK_9131B()
self.sync_Keithley_2230G = manager.sync_Keithley_2230G()
self.sync_LS_336 = manager.sync_LS_336()
self.sync_LS_218 = manager.sync_LS_218()
self.sync_imc = manager.sync_imc()
self.sync_converted = manager.sync_converted()
self.sync_main = manager.sync_main()
print('!!!\nI opened global variables myself. If you close me, global variables will shut down too. Consider starting global variables in own instance for more security\n!!!')
#fill in variables, in case they are not defined in global variables
self.sync_LS_336.update({'T':[0,0,0,0]})
self.sync_LS_218.update({'T':[0,0,0,0,0,0,0,0]})
self.sync_imc.update({'FG':[0,0,0]})
self.sync_converted.update({'T':[0,0,0,0,0,0,0,0]})
self.sync_converted.update({'AMR_x':[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]})
self.sync_converted.update({'AMR_y':[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]})
self.sync_converted.update({'AMR_z':[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]})
self.sync_BK_9132B.update({'P':[0,0,0], 'U':[0,0,0], 'I': [0,0,0]})
self.sync_BK_9132B_2.update({'P':[0,0,0], 'U':[0,0,0], 'I': [0,0,0]})
self.sync_BK_9131B.update({'P':[0,0,0], 'U':[0,0,0], 'I': [0,0,0]})
# self.sync_main.update({'Measurement_Running':False, 'T':[0,0], 'Points_Path':'', 'T_set':[0,0], 'T_reset':False, 'Cooldown_Speed':0, 'Start_ramp':False, 'Stop_ramp':False})
#import Gui from QT designer file
super(MainWindow, self).__init__(*args, **kwargs)
self.setupUi(self)
#setup plot
self.plot_colors = ['#a6cee3', '#1f78b4','#b2df8a','#33a02c','#fb9a99','#e31a1c','#fdbf6f','#ff7f00','#cab2d6','#6a3d9a','#e7298a','#b15928','#016c59','#d9d9d9','#000000'] #list of colors for plots
self.plot_widgets = [self.graphWidget_T,self.graphWidget_Fluxgate,self.graphWidget_AMR_x,self.graphWidget_AMR_y,self.graphWidget_AMR_z] #list of all plot widgets for easier handling
self.plots_T = [i for i in range(11)] #will contain all plots in temperature grapgh widget. Is already the correct lengths, so the right amount of plots are created for the widget. Contains numbers from 0 to the number of plots so the elements can also be used as index below.
self.plots_fluxgate = [i for i in range(4)] #will contain all plots in temperature grapgh widget. Is already the correct lengths, so the right amount of plots are created for the widget. Contains numbers from 0 to the number of plots so the elements can also be used as index below.
self.plots_AMR_x = [i for i in range(15)] #will contain all plots in temperature grapgh widget. Is already the correct lengths, so the right amount of plots are created for the widget. Contains numbers from 0 to the number of plots so the elements can also be used as index below.
self.plots_AMR_y = [i for i in range(15)] #will contain all plots in temperature grapgh widget. Is already the correct lengths, so the right amount of plots are created for the widget. Contains numbers from 0 to the number of plots so the elements can also be used as index below.
self.plots_AMR_z = [i for i in range(15)] #will contain all plots in temperature grapgh widget. Is already the correct lengths, so the right amount of plots are created for the widget. Contains numbers from 0 to the number of plots so the elements can also be used as index below.
self.plot_sets = [self.plots_T, self.plots_fluxgate, self.plots_AMR_x, self.plots_AMR_y, self.plots_AMR_z]#list of all plot-sets. Each plot-set contains all plots of one graph widget
self.pens = [pg.mkPen(color = c, width = 2) for c in self.plot_colors]
self.titles = ['Temperature', 'Fluxgates', 'AMR x', 'AMR y', 'AMR z'] #Plot titles
self.ylabel = ['Temperature [K]', 'B [µT]', 'B [µT]', 'B [µT]', 'B [µT]'] #y axis labels for plots
self.axis = [] #list of axis. They are needed to set x axis to time. List is filled below
for i in range(5): #fill list with axis
self.axis.append(pg.DateAxisItem())
for widget, title, label, axis, plot_set in zip(self.plot_widgets, self.titles, self.ylabel, self.axis, self.plot_sets):
#setup background, title, labels, grid, and time axis
widget.setBackground('w')
widget.setTitle(title)
widget.setLabel('left', label)
widget.setLabel('bottom', 'Temperature')
widget.showGrid(x= True, y= True, alpha = 0.5)
widget.setAxisItems({'bottom':axis})
temp = [time.time(),time.time()-1]
for i in plot_set: #since plot_set so far just contains numbers from 0 to the number of desired plots, the elements can also be used as index
plot_set[i] = (widget.plot(temp,[1,0],pen = self.pens[i]))
plot_set[i].clear()
#define signals and slots
self.action_save_default.triggered.connect(self.save_default)
self.action_load_default.triggered.connect(self.load_default)
self.checkBox_monitoring.stateChanged.connect(self.set_monitoring)
self.SB_plot_points.valueChanged.connect(self.set_Npoints)
self.checkBox_plots_disable.stateChanged.connect(self.disable_plots)
self.dSB_timing_main.valueChanged.connect(self.set_timing_Main)
self.dSB_timing_monitoring.valueChanged.connect(self.set_timing_monitoring)
self.Button_change_Buffer.clicked.connect(self.change_buffer_size)
self.Button_imc.clicked.connect(self.set_imc)
self.Button_LS.clicked.connect(self.set_LS)
self.list_T.itemSelectionChanged.connect(self.plot_List_T_changed)
self.list_FG.itemSelectionChanged.connect(self.plot_List_FG_changed)
self.list_AMR_x.itemSelectionChanged.connect(self.plot_List_AMR_x_changed)
self.list_AMR_y.itemSelectionChanged.connect(self.plot_List_AMR_y_changed)
self.list_AMR_z.itemSelectionChanged.connect(self.plot_List_AMR_z_changed)
self.checkBox_autoscale_T.stateChanged.connect(self.set_autoscale)
self.checkBox_autoscale_FG.stateChanged.connect(self.set_autoscale)
self.checkBox_autoscale_AMR_x.stateChanged.connect(self.set_autoscale)
self.checkBox_autoscale_AMR_y.stateChanged.connect(self.set_autoscale)
self.checkBox_autoscale_AMR_z.stateChanged.connect(self.set_autoscale)
self.SB_T_channel_top.valueChanged.connect(self.set_TB_channel)
self.SB_T_channel_bot.valueChanged.connect(self.set_TB_channel)
self.Button_PID_on.clicked.connect(self.activate_PID)
self.Button_start_ramp.clicked.connect(self.start_ramp)
self.Button_stop_ramp.clicked.connect(self.stop_ramp_pushed)
self.Button_reset_T_set.clicked.connect(self.reset_set_T)
self.dSB_PID_P.valueChanged.connect(self.update_PIDs)
self.dSB_PID_I.valueChanged.connect(self.update_PIDs)
self.dSB_PID_D.valueChanged.connect(self.update_PIDs)
self.dSB_PID_P_2.valueChanged.connect(self.update_PIDs)
self.dSB_PID_I_2.valueChanged.connect(self.update_PIDs)
self.dSB_PID_D_2.valueChanged.connect(self.update_PIDs)
self.dSB_P_max_top.valueChanged.connect(self.update_PIDs)
self.dSB_P_max_bot.valueChanged.connect(self.update_PIDs)
self.dSB_R_top.valueChanged.connect(self.update_PIDs)
self.dSB_R_bot.valueChanged.connect(self.update_PIDs)
self.Button_set_I0.clicked.connect(self.set_I0)
self.Button_set_I1.clicked.connect(self.set_I1)
self.Button_set_I2.clicked.connect(self.set_I2)
self.Button_get_I0.clicked.connect(self.get_I0)
self.Button_get_I1.clicked.connect(self.get_I1)
self.Button_get_I2.clicked.connect(self.get_I2)
self.Button_Coils_off.clicked.connect(self.turn_off_coils)
self.Button_set_B.clicked.connect(self.set_B)
self.checkBox_LED.stateChanged.connect(self.set_LED)
self.checkBox_He_heater.stateChanged.connect(self.switch_he_heater)
self.Button_he_heater_measure_R.clicked.connect(self.measure_R_pressed)
self.dSB_He_heater_P_set.valueChanged.connect(self.switch_he_heater)
self.checkBox_AMR_supply.stateChanged.connect(self.switch_AMR_supply)
self.checkBox_AMR_flip.stateChanged.connect(self.AMR_flip)
self.checkBox_Relais_Fan.stateChanged.connect(self.switch_fan)
#define constants
self.running = True #true while app is running
self.timing_Main = 0.1 #wait time in function "update_Data".
self.monitor = False # Write monitoring data yes or no
self.timing_monitoring = 1 #Save intervall of monitoring function
self.T_imc = False #use imc as source for temperature data
self.buffer_size = 36000 #size of ringbuffers for plotting. 36000 is roughly 1 hour, if timng_main is 0.1 s
self.buffer = collections.deque(maxlen=self.buffer_size)
self.new_Data = []
self.plot_data_T = [[] for i in range(12)] #list of lists which will contain the selected points for plotting for T plots.
self.plot_data_FG = [[] for i in range(4)] #list of lists which will contain the selected points for plotting for Fluxgate plots.
self.plot_data_AMR_x = [[] for i in range(15)] #list of lists which will contain the selected points for plotting for AMR_x plots.
self.plot_data_AMR_y = [[] for i in range(15)] #list of lists which will contain the selected points for plotting for AMR_y plots.
self.plot_data_AMR_z = [[] for i in range(15)] #list of lists which will contain the selected points for plotting for AMR_z plots.
self.show_plot_T = [i for i in range(12)] #list of all T_plots that are active. This is needed for autoscaling. It is changed by self.plot_List_T_changed.
self.show_plot_FG = [i for i in range(4)] #list of all FG_plots that are active. This is needed for autoscaling. It is changed by self.plot_List_FG_changed.
self.show_plot_AMR_x = [i for i in range(15)] #list of all AMR_x_plots that are active. This is needed for autoscaling. It is changed by self.plot_List_AMR_x_changed.
self.show_plot_AMR_y = [i for i in range(15)] #list of all AMR_y_plots that are active. This is needed for autoscaling. It is changed by self.plot_List_AMR_y_changed.
self.show_plot_AMR_z = [i for i in range(15)] #list of all AMR_z_plots that are active. This is needed for autoscaling. It is changed by self.plot_List_AMR_z_changed.
self.show_plot_all = [self.show_plot_T,self.show_plot_FG,self.show_plot_AMR_x,self.show_plot_AMR_y,self.show_plot_AMR_z] #list containing all show plot list. Also needed for autoscaling
self.buffer_t = collections.deque(maxlen=self.buffer_size) #ringbuffer for times
self.Npoints = 200 #number of points to plot
self.ind_plot = [-i for i in range(1,self.Npoints+1)] #list containing all indices which are to be plotted
self.yrange = [[] for i in range(5)] #list of lists which will contain the y-axis ranges for all plots
self.autoscale = [True,True,True,True,True] #Wether to scale the plots or not
self.TB_channels = [0,7] #index of cernox used for PID controllers([top, bottom]).
self.PID_top = PID(1, 0.1, 0.0, setpoint=1) #PID controller for TOP edge
self.PID_bot = PID(1, 0.1, 0.0, setpoint=1) #PID controller for Bottom edge
self.PID_on = False #if set to false, the PID thred (self.PID_thread) stops.
self.ramp = False #is set to True if temperature ramp is activated
self.rate = 0 #ramprate of cooldown
self.set_T = [0,0] #set Temperatures of PID controllers([top,bottom])
self.powers = [] #current power values of heaters will be stored here
self.Automation_Parameters_old = [0,0,0,0,0,0,0,0,0]
self.Automation_Parameters_new = [0,0,0,0,0,1,0,0,0]
self.labels_T = [self.lab_T_1,self.lab_T_2,self.lab_T_3,self.lab_T_4,self.lab_T_5,self.lab_T_6,self.lab_T_7,self.lab_T_8,self.lab_T_9,self.lab_T_10,self.lab_T_11,self.lab_T_12] #List of all Labels containing temperaure data. Is needed for "update_gui"
self.labels_B = [self.lab_Bx_is,self.lab_By_is,self.lab_Bz_is] #List of all Labels containing B data. Is needed for "update_gui"
self.SB_all = [self.dSB_timing_main,self.dSB_timing_monitoring,self.SB_plot_points,self.SB_Buffer_Size,self.dSB_P_max_top,self.dSB_P_max_bot,self.dSB_R_top,self.dSB_R_bot,
self.dSB_T_reset_top,self.dSB_T_reset_bot,self.dSB_T_c,self.SB_T_channel_top,self.SB_T_channel_bot,self.dSB_ramprate,self.dSB_ramprate_t_const,self.dSB_PID_P,self.dSB_PID_I,self.dSB_PID_D,
self.dSB_PID_P_2,self.dSB_PID_I_2,self.dSB_PID_D_2,self.dSB_Bx_set,self.dSB_By_set,self.dSB_Bz_set,self.dSB_Ix_set0,self.dSB_Iy_set0,self.dSB_Iz_set0,
self.dSB_Ix_set1,self.dSB_Iy_set1,self.dSB_Iz_set1,self.dSB_Ix_set2,self.dSB_Iy_set2,self.dSB_Iz_set2] #list of all Spinboxes, helps for saving and loading of default values
self.lines_all = [self.line_Path_points,self.line_Path_Mon,self.line_Path_AMR_calib] #List of all lineedits, helps for saving and loading default values
self.checkBoxes_all = [self.checkBox_monitoring, self.checkBox_sens_ana_1, self.checkBox_sens_ana_2, self.checkBox_sens_ana_3,
self.checkBox_sens_ana_4, self.checkBox_sens_ana_5, self.checkBox_sens_ana_6, self.checkBox_sens_ana_7, self.checkBox_sens_ana_8,
self.checkBox_autoscale_T, self.checkBox_autoscale_FG, self.checkBox_autoscale_T, self.checkBox_autoscale_AMR_x,
self.checkBox_autoscale_AMR_y, self.checkBox_autoscale_AMR_z, self.checkBox_I0x, self.checkBox_I0y, self.checkBox_I0z,
self.checkBox_I1x, self.checkBox_I1y, self.checkBox_I1z, self.checkBox_I2x, self.checkBox_I2y, self.checkBox_I2z]
#set up pyQT threadpool
self.threadpool = QThreadPool()
#read default values from config and set values in gui
self.load_default()
self.set_LS() #make LS 2018 green, since this is the default
#Print process id of this script for priority adjustments in task manager
print(f"Prozess-ID: {os.getpid()}")
#enable all plots and set background color of items to the corresponding plot color
self.list_AMR_x.selectAll()
self.list_AMR_y.selectAll()
self.list_AMR_z.selectAll()
self.list_T.selectAll()
self.list_FG.selectAll()
for list_widget in [self.list_T, self.list_FG, self.list_AMR_x, self.list_AMR_y, self.list_AMR_z]:
items = list_widget.selectedItems()
for item,col in zip(items,self.plot_colors):
item.setBackground(QColor(f"{col}"))
#call update PIDs to make shure the PID parameters correspond to the ones in the gui
self.update_PIDs
#start standard threads
worker = Worker(self.update_Data)
worker.signals.progress.connect(self.update_gui) #The values from update_Data must be transmitted via a signal, so that the gui is set in the main thread. If the plots are not updated in the main thread, they freeze.
self.threadpool.start(worker)
def update_Data(self, progress_callback):#gets data from global variables and triggers update_gui
scale_data_T = [[] for i in range(12)] #list of lists which will contain the data for scaling the T_plot
scale_data_FG = [[] for i in range(4)] #list of lists which will contain the data for scaling the FG-Plot
scale_data_AMR_x = [[] for i in range(15)] #list of lists which will contain the data for scaling the AMR_x plot
scale_data_AMR_y = [[] for i in range(15)] #list of lists which will contain the data for scaling the AMR_y plot
scale_data_AMR_z = [[] for i in range(15)] #list of lists which will contain the data for scaling the AMR_z plot
while self.running == True:
start = timer()
#get power data of heaters. This is not appended to Ringbuffer, since it is not plotter
self.powers = [self.sync_BK_9132B_2.get('P')[0],self.sync_BK_9132B_2.get('P')[1], self.sync_BK_9132B.get('P')[0]]
#sort data in T, FG, AMR
#get lakeshore data
LS336 = self.sync_LS_336.get('T')[0:4]
LS218 = self.sync_converted.get('T')
self.T = LS218 + LS336
#FG data
self.FG = [i*100 for i in self.sync_imc.get('FG')]
squares = [i**2 for i in self.FG]
mag = np.sqrt(sum(squares))
self.FG.append(mag)
#AMR data
self.AMR_x = self.sync_converted.get('AMR_x')
self.AMR_y = self.sync_converted.get('AMR_y')
self.AMR_z = self.sync_converted.get('AMR_z')
#Write Top / Bottom Temperatures into sync_main so that the Automation Program gets T_top and T_bottom temperatures
T_top = self.T[self.TB_channels[0]]
T_bot = self.T[self.TB_channels[1]]
self.sync_main.update({'T':[T_top, T_bot]})
#Check if automated measurement is running (i.e. True). If True: Check for changes in the automation parameters
if self.sync_main.get('Measurement_Running') is None:
self.Measurement_Running = False
else:
self.Measurement_Running = self.sync_main.get('Measurement_Running')
if self.Measurement_Running == True:
for i,n in enumerate(['Points_Path','T_set','T_reset','Cooldown_Speed','Start_ramp','Stop_ramp','0-Field','1-Field','Set_1field']): #get new parameters from global variables and compare to old ones.
self.Automation_Parameters_new[i] = self.sync_main.get(n)
if self.Automation_Parameters_new != self.Automation_Parameters_old: #if a parameter has been changed, check which parameter has been changed.
if self.Automation_Parameters_new[0] != self.Automation_Parameters_old[0]: #if Points_Path has been changed
self.line_Path_points.setText(self.Automation_Parameters_new[0]) # Set line_Path_points
if self.Automation_Parameters_new[1] != self.Automation_Parameters_old[1]: #if T_set has been changed
self.dSB_T_reset_top.setValue(float(self.Automation_Parameters_new[1][0])) #set T_reset_top
self.dSB_T_reset_bot.setValue(float(self.Automation_Parameters_new[1][1])) #set T_reset_bot
if self.Automation_Parameters_new[2] != self.Automation_Parameters_old[2]: #if T_reset has been changed
if self.Automation_Parameters_new[2] == True: #Check if T_reset is True --> Run same function as if T_reset button has been pressed
self.reset_set_T()
self.sync_main.update({'T_reset':False}) #Change T_reset to false
if self.Automation_Parameters_new[3] != self.Automation_Parameters_old[3]: #Cooldown_Speed has been changed
self.dSB_ramprate.setValue(float(self.Automation_Parameters_new[3])) #set ramprate
if self.Automation_Parameters_new[4] != self.Automation_Parameters_old[4]: #if Start_ramp has been changed
if self.Automation_Parameters_new[4] == True: #Check if Start_Ramp is True --> Run same function as if Start_Ramp button has been pressed
self.start_ramp()
self.sync_main.update({'Start_ramp':False}) #Change Start_Ramp to false
if self.Automation_Parameters_new[5] != self.Automation_Parameters_old[5]: #if Stop_ramp has been changed
if self.Automation_Parameters_new[5] == True: #Check if Stop_Ramp is True --> Run same function as if Stop_Ramp button has been pressed
self.stop_ramp_pushed()
self.sync_main.update({'Stop_ramp':False}) #Change Stop_Ramp to false
if self.Automation_Parameters_new[6] != self.Automation_Parameters_old[6]: #if 0-field parameters have been changed
I = self.Automation_Parameters_new[6][0] #[Ix, Iy, Iy2, Iz]
Flip = self.Automation_Parameters_new[6][1] #[Flipx, Flipy, Flipz]
self.dSB_Ix_set0.setValue(I[0])
self.dSB_Iy_set0.setValue(I[1])
self.dSB_Iy2_set0.setValue(I[2])
self.dSB_Iz_set0.setValue(I[3])
for box,state in zip([self.checkBox_I0x,self.checkBox_I0y,self.checkBox_I0z], Flip):
box.setChecked(state)
if self.Automation_Parameters_new[7] != self.Automation_Parameters_old[7]: #if 1-field parameters have been changed
I = self.Automation_Parameters_new[7][0] #[Ix, Iy, Iy2, Iz]
Flip = self.Automation_Parameters_new[7][1] #[Flipx, Flipy, Flipz]
self.dSB_Ix_set1.setValue(I[0])
self.dSB_Iy_set1.setValue(I[1])
self.dSB_Iy2_set1.setValue(I[2])
self.dSB_Iz_set1.setValue(I[3])
for box,state in zip([self.checkBox_I1x,self.checkBox_I1y,self.checkBox_I1z], Flip):
box.setChecked(state)
if self.Automation_Parameters_new[8] != self.Automation_Parameters_old[8]: #if Set_1field has been changed
if self.Automation_Parameters_new[8] == True: #Check if Set_1field is True --> Run same function as if Set_1field button has been pressed
self.set_I1()
self.sync_main.update({'Set_1field':False}) #Change Set_1field to false
#combine data and append Ringbuffer
self.new_Data = [time.time()] + self.T + self.FG + self.AMR_x + self.AMR_y + self.AMR_z
self.buffer.append(self.new_Data)
#Prepare data for plotting. This is done here, to keep the main thread as lean as possible for better performance (espacially due to high demands of plots)
#get subset of ringbuffers which is used for plotting
ind_plot = self.ind_plot #get a "local" copy of self.ind_plot because if Npoints is set to a value larger than len(buffer) in gui while the plot lists are collected the index might be out of range.
if self.Npoints >= len(self.buffer): #as long as the ringbuffers are too short, the indices of the plotted points must be adjusted to the length of the buffer. Otherwise an "index is out of range" will occur
self.downsample(len(self.buffer))
ind_plot = self.ind_plot
len_ind = len(ind_plot)
len_scale = len(self.buffer) #is needed to build "lists_scale"
else:
len_scale = self.Npoints
lists_plot = [self.buffer[i] for i in ind_plot] #get all lists in self.buffer that will be used for plotting
len_ind = len(lists_plot)
plot_data_t = [lists_plot[n][0] for n in range(len_ind)]#sort out times
for i in range(12):#sort out temperature data
self.plot_data_T[i] = [lists_plot[n][i+1] for n in range(len_ind)]
for i in range(4):#sort out Fluxgate data
self.plot_data_FG[i] = [lists_plot[n][i+13] for n in range(len_ind)]
for i in range(15):#sort out AMR_x data
self.plot_data_AMR_x[i] = [lists_plot[n][i+17] for n in range(len_ind)]
for i in range(15):#sort out AMR_y data
self.plot_data_AMR_y[i] = [lists_plot[n][i+32] for n in range(len_ind)]
for i in range(15):#sort out AMR_z data
self.plot_data_AMR_z[i] = [lists_plot[n][i+47] for n in range(len_ind)]
plot_data = [self.plot_data_T,self.plot_data_FG,self.plot_data_AMR_x,self.plot_data_AMR_y,self.plot_data_AMR_z,plot_data_t]
#get the start and stop time for the plots. Is used to scale x-axis
self.t0 = plot_data_t[0]
self.t1 = plot_data_t[-1]
#get min and max values of the plotted data to scale y axis. Get the data from the whole data set of self.Npoints. Otherwise the scales keep jumping when a peak is exluded due to downsampling
lists_scale = [self.buffer[-i] for i in range(1,len_scale+1)] #get all lists in self.buffer that will be used for scaling
for i in range(12):#sort out temperature data
scale_data_T[i] = [lists_scale[n][i+1] for n in range(len_scale)]
for i in range(4):#sort out Fluxgate data
scale_data_FG[i] = [lists_scale[n][i+13] for n in range(len_scale)]
for i in range(15):#sort out AMR_x data
scale_data_AMR_x[i] = [lists_scale[n][i+17] for n in range(len_scale)]
for i in range(15):#sort out AMR_y data
scale_data_AMR_y[i] = [lists_scale[n][i+32] for n in range(len_scale)]
for i in range(15):#sort out AMR_z data
scale_data_AMR_z[i] = [lists_scale[n][i+47] for n in range(len_scale)]
scale_data = [scale_data_T,scale_data_FG,scale_data_AMR_x,scale_data_AMR_y,scale_data_AMR_z]
for i,data in enumerate(scale_data):
active_data = [data[n] for n in self.show_plot_all[i]]
if active_data == []:
y_max = 0
y_min = 0
else:
y_max = max([max(d) for d in active_data])
y_min = min([min(d) for d in active_data])
self.yrange[i] = [y_min,y_max]
progress_callback.emit(plot_data) #emit signal, so that "update_gui" starts. "update_gui" uses the emitted lists as data. If data is transmitted via "global" variables (self....) is can happen that update_data "overtakes" update gui which leads to size differences in the plot data.
end = timer()
# print(end-start)
try:
time.sleep(self.timing_Main-(end-start))
except:
print(end-start)
def update_gui(self,plot_data):#sets labels to new values and updates plots
# start = timer()
#Update labels
for lab,val in zip(self.labels_T,self.T):
lab.setText(f"{val:4.3f}")
for lab,val in zip(self.labels_B,self.FG):
lab.setText(f"{val:.3f}")
self.lab_T_top.setText(f"{self.T[self.TB_channels[0]]:4.3f}")
self.lab_T_bot.setText(f"{self.T[self.TB_channels[1]]:4.3f}")
self.lab_T_set_top.setText(f"{self.set_T[0]:.3f}")
self.lab_T_set_bot.setText(f"{self.set_T[1]:.3f}")
self.lab_P_top.setText(f"{self.powers[0]:2.3f}")
self.lab_P_bot.setText(f"{self.powers[1]:2.3f}")
self.lab_He_heater_P_is.setText(f"{self.powers[2]:2.2f}")
#update plots
for plot,data in zip(self.plots_T,plot_data[0]):
plot.setData(plot_data[5],data)
if self.autoscale[0] == True:
self.graphWidget_T.setXRange(self.t0,self.t1)
self.graphWidget_T.setYRange(self.yrange[0][0],self.yrange[0][1])
for plot,data in zip(self.plots_fluxgate,plot_data[1]):
plot.setData(plot_data[5],data)
if self.autoscale[1] == True:
self.graphWidget_Fluxgate.setXRange(self.t0,self.t1)
self.graphWidget_Fluxgate.setYRange(self.yrange[1][0],self.yrange[1][1])
for plot,data in zip(self.plots_AMR_x,plot_data[2]):
plot.setData(plot_data[5],data)
if self.autoscale[2] == True:
self.graphWidget_AMR_x.setXRange(self.t0,self.t1)
self.graphWidget_AMR_x.setYRange(self.yrange[2][0],self.yrange[2][1])
for plot,data in zip(self.plots_AMR_y,plot_data[3]):
plot.setData(plot_data[5],data)
if self.autoscale[3] == True:
self.graphWidget_AMR_y.setXRange(self.t0,self.t1)
self.graphWidget_AMR_y.setYRange(self.yrange[3][0],self.yrange[3][1])
for plot,data in zip(self.plots_AMR_z,plot_data[4]):
plot.setData(plot_data[5],data)
if self.autoscale[4] == True:
self.graphWidget_AMR_z.setXRange(self.t0,self.t1)
self.graphWidget_AMR_z.setYRange(self.yrange[4][0],self.yrange[4][1])
# end = timer()
# print(end-start)
def PID_thread(self,progress_callback): #function is called in own thread in self.activate_PIDs. Keeps PIDs running in loop, as long self.PID_on == True
V = [0,0] #store values to send to the powersupplies
#bring power supplies in the correct state
update_single_entry(self.sync_BK_9132B_2,'OutputOn',0,True)
update_single_entry(self.sync_BK_9132B_2,'OutputOn',1,True)
update_single_entry(self.sync_BK_9132B_2,'setI',0,1.5)
update_single_entry(self.sync_BK_9132B_2,'setI',1,1.5)
while self.PID_on == True:
start = timer()
#lower setpoint if self.ramp == True
if self.ramp == True:
self.set_T = [i-self.rate/10 for i in self.set_T] #rate is devided by 10 because timing of PID loop is 0.1 s and rate is given in K/s.
self.PID_top.setpoint = self.set_T[0]
self.PID_bot.setpoint = self.set_T[1]
T_is = [self.T[i] for i in [self.SB_T_channel_top.value(),self.SB_T_channel_bot.value()]] #get current values
P = [self.PID_top(T_is[0]),self.PID_bot(T_is[1])]
i=0
for r,p in zip(self.R,P): #transform power values to voltage values and send them to global vars
update_single_entry(self.sync_BK_9132B_2,'setU',i,np.sqrt(p*r))
i=i+1
end = timer()
try:
time.sleep(0.1-(end-start)) #timing of PID loop is always 0.1 s
except:
print(f"PID loop took:{end-start} s")
#turn supplies off
update_single_entry(self.sync_BK_9132B_2,'OutputOn',0,False)
update_single_entry(self.sync_BK_9132B_2,'OutputOn',1,False)
def plot_List_T_changed(self):#is triggered when the plot selection for Temperature plot changes. Sets all entries in show_plot_T to false and then sets only the selected ones to true again. It then shows and hides the corresponding plots
show_plot_T = [False for i in range(12)]
indexes =self.list_T.selectedIndexes() #the return indeces are qt objects. To get a "normal" index the function row() must be used.
for ind in indexes: #set selected indeces to true
row = ind.row()
show_plot_T[row] = True
for show,p in zip(show_plot_T,self.plots_T): #show or hide the plots
if show == True:
p.show()
else:
p.hide()
self.show_plot_T = [i for i,x in enumerate(show_plot_T) if x == True] #create list with indexes of active plots. This is needed for autoscaling
self.show_plot_all = [self.show_plot_T,self.show_plot_FG,self.show_plot_AMR_x,self.show_plot_AMR_y,self.show_plot_AMR_z] #update list with new show_plot_T
def plot_List_FG_changed(self):#is triggered when the plot selection for Fluxgate plot changes. Sets all entries in show_plot_FG to false and then sets only the selected ones to true again. It then shows and hides the corresponding plots
show_plot_FG = [False for i in range(4)]
indexes =self.list_FG.selectedIndexes()
for ind in indexes:
row = ind.row()
show_plot_FG[row] = True
for show,p in zip(show_plot_FG,self.plots_fluxgate):
if show == True:
p.show()
else:
p.hide()
self.show_plot_FG = [i for i,x in enumerate(show_plot_FG) if x == True] #generate list with indixes of active plots
self.show_plot_all = [self.show_plot_T,self.show_plot_FG,self.show_plot_AMR_x,self.show_plot_AMR_y,self.show_plot_AMR_z] #update list with new show_plot_FG
def plot_List_AMR_x_changed(self):#is triggered when the plot selection for AMR_x plot changes. Sets all entries in show_plot_AMR_x to false and then sets only the selected ones to true again. It then shows and hides the corresponding plots
show_plot_AMR_x = [False for i in range(15)]
indexes =self.list_AMR_x.selectedIndexes()
for ind in indexes:
row = ind.row()
show_plot_AMR_x[row] = True
for show,p in zip(show_plot_AMR_x,self.plots_AMR_x):
if show == True:
p.show()
else:
p.hide()
self.show_plot_AMR_x = [i for i,x in enumerate(show_plot_AMR_x) if x == True]#generate list with indixes of active plots
self.show_plot_all = [self.show_plot_T,self.show_plot_FG,self.show_plot_AMR_x,self.show_plot_AMR_y,self.show_plot_AMR_z] #update list with new show_plot_AMR_x
def plot_List_AMR_y_changed(self):#is triggered when the plot selection for AMR_y plot changes. Sets all entries in show_plot_AMR_y to false and then sets only the selected ones to true again. It then shows and hides the corresponding plots
show_plot_AMR_y = [False for i in range(15)]
indexes =self.list_AMR_y.selectedIndexes()
for ind in indexes:
row = ind.row()
show_plot_AMR_y[row] = True
for show,p in zip(show_plot_AMR_y,self.plots_AMR_y):
if show == True:
p.show()
else:
p.hide()
self.show_plot_AMR_y = [i for i,x in enumerate(show_plot_AMR_y) if x == True]#generate list with indixes of active plots
self.show_plot_all = [self.show_plot_T,self.show_plot_FG,self.show_plot_AMR_x,self.show_plot_AMR_y,self.show_plot_AMR_z] #update list with new show_plot_AMR_y
def plot_List_AMR_z_changed(self):#is triggered when the plot selection for AMR_z plot changes. Sets all entries in show_plot_AMR_x to false and then sets only the selected ones to true again. It then shows and hides the corresponding plots
show_plot_AMR_z = [False for i in range(15)]
indexes =self.list_AMR_z.selectedIndexes()
for ind in indexes:
row = ind.row()
show_plot_AMR_z[row] = True
for show,p in zip(show_plot_AMR_z,self.plots_AMR_z):
if show == True:
p.show()
else:
p.hide()
self.show_plot_AMR_z = [i for i,x in enumerate(show_plot_AMR_z) if x == True]#generate list with indixes of active plots
self.show_plot_all = [self.show_plot_T,self.show_plot_FG,self.show_plot_AMR_x,self.show_plot_AMR_y,self.show_plot_AMR_z] #update list with new show_plot_AMR_z
def downsample(self,N): #downsamples N down to 200. It creates list with 200 entries equally spaced from -1 to -N. self.ind_plot is set to this list and is subsequently used to downsample the plot data to 200 points.
if N <= 200: #list of indices to start at the end of the ringbuffer up N
self.ind_plot = [-i for i in range(1,N+1)]
else: #list of indices which downsamples to 200. Indices are again negative, because it starts at the end of the ringbuffer
q = N/(N-200+1) #every q-th index needs to be deleted
ind = [i for i in range(1,N+1)] #list containing all indices
ind_pop = []#empty list. Is filled with indices that will be excluded in the for-loop
for n in range(1,N-200+1): #fill list with indiced
ind_pop.append(round(n*q))
self.ind_plot = [-x for i, x in enumerate(ind) if i not in ind_pop] #exclude indices in "ind_pop" from "ind"
def set_Npoints(self,N): #is triggered when number of points to plot is changed. Sets variable self.Npoints to the new value. If Npoints is larger than 200 self.downsample() is called
self.Npoints = N
self.downsample(N)
def disable_plots(self): #is triggered when disable all plots checkbox is changed. Hides, or shows all plots
for plots in self.plot_sets:
for plot in plots:
if self.checkBox_plots_disable.isChecked() == True:
plot.hide()
else:
plot.show()
def set_autoscale(self): #is triggered when any of the autoscale checkboxes are changed. Sets self.autoscale to the new value
checkboxes = [self.checkBox_autoscale_T,self.checkBox_autoscale_FG,self.checkBox_autoscale_AMR_x,self.checkBox_autoscale_AMR_y,self.checkBox_autoscale_AMR_z]
for i,box in enumerate(checkboxes):
self.autoscale[i] = box.isChecked()
def set_timing_Main(self,t): #is triggered when timing Main is changed. Sets variable self.timin_Main to the new value
self.timing_Main = t
def change_buffer_size(self): #is triggered when self.button_change_Buffer is clicked. Since maxlen cannot be changed after initialization it initializes new buffers with the correct maxlen.
new_len = self.SB_Buffer_Size.value()
for i,buffer in enumerate(self.buffers_T): #replace old buffer with new one with new length. In order to keep the newest data points the data is stored in a list, and the newest points are used for the new buffer
data = list(buffer)
self.buffers_T[i] = collections.deque(data[-new_len:], maxlen = new_len)
for i,buffer in enumerate(self.buffers_FG): #replace old buffer with new one with new length. In order to keep the newest data points the data is stored in a list, and the newest points are used for the new buffer
data = list(buffer)
self.buffers_FG[i] = collections.deque(data[-new_len:], maxlen = new_len)
for i,buffer in enumerate(self.buffers_AMR_x): #replace old buffer with new one with new length. In order to keep the newest data points the data is stored in a list, and the newest points are used for the new buffer
data = list(buffer)
self.buffers_AMR_x[i] = collections.deque(data[-new_len:], maxlen = new_len)
for i,buffer in enumerate(self.buffers_AMR_y): #replace old buffer with new one with new length. In order to keep the newest data points the data is stored in a list, and the newest points are used for the new buffer
data = list(buffer)
self.buffers_AMR_y[i] = collections.deque(data[-new_len:], maxlen = new_len)
for i,buffer in enumerate(self.buffers_AMR_z): #replace old buffer with new one with new length. In order to keep the newest data points the data is stored in a list, and the newest points are used for the new buffer
data = list(buffer)
self.buffers_AMR_z[i] = collections.deque(data[-new_len:], maxlen = new_len)
#replace time buffer
data = list(self.buffer_t)
self.buffer_t = collections.deque(data[-new_len:], maxlen = new_len)
def set_monitoring(self): #is triggered when monitoring checkbox is clicked. If the new value is True, a new thread is started in which self.monitoring is running
if self.checkBox_monitoring.isChecked() == True:
self.monitor = True
self.worker_mon = Worker(self.monitoring)
self.threadpool.start(self.worker_mon)
else:
self.monitor = False
def set_timing_monitoring(self,t): #is triggered when monitoring timing is changed. Sets self.timing_monitoring to new value.
self.timing_monitoring = t
def set_imc(self): #is called when button "imc" is pressed. Sets self.T_imc = True and sets colors of buttons accordingly
self.T_imc = True
self.syncdict.update({"T_imc":True})
self.Button_imc.setStyleSheet("background-color: green; color: white;")
self.Button_LS.setStyleSheet("background-color: '#e1e1e1'; color: black;")
def set_LS(self): #is called when button "LS 2018" is pressed. Sets self.T_imc = False and sets colors of buttons accordingly
self.T_imc = False
self.syncdict.update({"T_imc":False})
self.Button_imc.setStyleSheet("background-color: '#e1e1e1'; color: black;")
self.Button_LS.setStyleSheet("background-color: green; color: white;")
def set_TB_channel(self): #is called when either channel of Top or Bottom cernox is changed. Updates the varable self.TB_channels according to the values
self.TB_channels = [self.SB_T_channel_top.value(), self.SB_T_channel_bot.value()]
def activate_PID(self): #is called when "button_PID_on" is clicked. Changes button text and sets self.PID_on to True or False. If it changed to True, is starts a thread with the PID controllers
if self.Button_PID_on.text() == 'on/OFF': #PID was OFF and is now turned ON
self.Button_PID_on.setText('ON/off')
self.Button_PID_on.setStyleSheet("background-color: green")
self.PID_on = True
#start thread
worker_PID = Worker(self.PID_thread)
self.threadpool.start(worker_PID)
else: #PID was On and is now turned off
self.Button_PID_on.setText('on/OFF')
self.Button_PID_on.setStyleSheet("background-color: red")
self.PID_on = False
def update_PIDs(self): #is called when any PID parameter, except setvalue, is changed. Updates the PID parameters
self.PID_top.tunings = (self.dSB_PID_P.value(),self.dSB_PID_I.value(),self.dSB_PID_D.value())
self.PID_bot.tunings = (self.dSB_PID_P_2.value(),self.dSB_PID_I.value(),self.dSB_PID_D.value())
self.PID_top.output_limits = (0,self.dSB_P_max_top.value())
self.PID_bot.output_limits = (0,self.dSB_P_max_bot.value())
self.R = [self.dSB_R_top.value(),self.dSB_R_bot.value()]
def reset_set_T(self): #is called when button "reset" is pushed. Sets self.set_T to the values in the reset double spin boxes. The values of self.set_T are then set in the labels "set" labes by self.update_gui
self.set_T = [self.dSB_T_reset_top.value(),self.dSB_T_reset_bot.value()]
def start_ramp(self): #is called when "start ramp" button is pushed. #If transition time spin box is not equal 0, it calculates the corresponding cooldown rate. Otherwise the cooldown rate is the correspoing dSB is used. Also sets self.ramp = True so self.set_T is lowered in self.PID_thread. Also stores time when the button is pushed. This will be used when save point button is pushed.
if self.dSB_ramprate_t_const.value() != 0:
self.rate = (self.set_T[0] - self.set_T[1])/self.dSB_ramprate_t_const.value()
else:
self.rate = self.dSB_ramprate.value()
self.ramp = True
self.start_time_ramp = self.new_Data[0] #get time stamp from current data point
self.B_start = self.FG
self.lab_ramp_status.setText("started")
def stop_ramp_pushed(self): #is called when stop ramp button is pushed. Since the programm needs to wait a few seconds in this procesure it is done in a seperate thread. This function only start this thread
worker_stop_ramp = Worker(self.stop_ramp)
self.threadpool.start(worker_stop_ramp)
self.lab_ramp_status.setText("stopped")
def stop_ramp(self,progress_callback): #is started by self.stop_ramp_pushed. Turnes off heaters, takes data when
self.ramp = 0 #stop ramping
self.set_T = [0,0] #setpoint to 0 so heaters are turned off
self.stop_time_ramp = self.new_Data[0] #get time stamp from current data point
time.sleep(3)
self.B_expelled = self.AMR_x + self.AMR_y + self.AMR_z #get data when B-Field is still active
time.sleep(0.5)
self.set_I0() #set 0-Field
self.lab_ramp_status.setText("0-field")
time.sleep(4)
self.save_point()
def save_point(self): #is called at the end of stop_ramp. Start a new thread with self.analyse_cooldown
self.save_time = self.new_Data[0] #get time stamp from current data point
self.B_trapped = self.AMR_x + self.AMR_y + self.AMR_z
worker_analyse = Worker(self.analyse_cooldown)
self.threadpool.start(worker_analyse)
self.lab_ramp_status.setText("finished")
def analyse_cooldown(self,progress_callback): #analyses the cooldown and writes it to file
print("start_analyse")
#find indices of start and stop time of ramp
for i in range(len(self.buffer)): #iterate through buffer from the back. Check the first item in the list to see if it equals the start or stop time
t = self.buffer[-i][0]
if t == self.start_time_ramp:
I_start = -i
break #stop time is found first, because iteration is done from back to front. After I_start is found the loop can be broken.
elif t == self.stop_time_ramp:
I_stop = -i
#prepare cernox and time data
print(self.start_time_ramp)
print(time.strftime("%Y-%m-%d_%H-%M-%S",time.gmtime(self.start_time_ramp))+"\t")
# print(time.strftime("%Y-%m-%d_%H-%M-%S",time.gmtime(self.stop_time_ramp))+"\t")
print(time.strftime("%Y-%m-%d_%H-%M-%S",time.gmtime(self.save_time))+"\t")
print([I_start,I_stop])
lists_ramp = [self.buffer[i] for i in range(I_start,I_stop)] #get all lists in self.buffer that are recorded during ramping
print(len(lists_ramp))
len_ind = len(lists_ramp)
print(lists_ramp[0][0])
time_data = [lists_ramp[n][0] for n in range(len_ind)]#sort out times
temp_data = [i for i in range(8)] #create "empty" list in which the cernox data will be stored
for i in range(8):#sort out temperature data
temp_data[i] = [lists_ramp[n][i+1] for n in range(len_ind)]
#preapre list of active cernox sensors for analysis
act_sens = [] #list of active sensors
for i,c in enumerate([self.checkBox_sens_ana_1, self.checkBox_sens_ana_2, self.checkBox_sens_ana_3, self.checkBox_sens_ana_4, self.checkBox_sens_ana_5, self.checkBox_sens_ana_6,
self.checkBox_sens_ana_7, self.checkBox_sens_ana_8]):
if c.isChecked() == True:
act_sens.append(i+1)
#analyse data
try:
results = analyse_cooldown_tools.calc_gradient(temp_data,time_data,act_sens,self.dSB_T_c.value())
print(f'T_c = {self.dSB_T_c.value()}')
except:
print('could not analyse data')
return
#open file
path = self.line_Path_points.text()
try: #if no valid path is given a file "points.txt" is created in the folder
if os.path.isfile(path) == False:
with open(path,'a') as file:
file.write('t_start\tt_stop\tt_save\tB_start x\tB_start y\tB_start z\tB_start mag\tgrad_glob\tav_grad_glob\te_av_grad_glob\ttrans_time\tav_rate_glob\te_av_rate_glob\tgrad_loc\trate_loc\t15*AMR_x_expelled\t15*AMR_y_expelled\t15*AMR_z_expelled\t45*AMR_trapped\n')
file = open(path,'a')
except:
path = self.current_dir+'\\points.txt'
if os.path.isfile(path) == False:
with open(path,'a') as file:
file.write('t_start\tt_stop\tt_save\tB_start x\tB_start y\tB_start x\tgrad_glob\tav_grad_glob\te_av_grad_glob\ttrans_time\tav_rate_glob\te_av_rate_glob\tgrad_loc\trate_loc\t15*AMR_x_expelled\t15*AMR_y_expelled\t15*AMR_z_expelled\t45*AMR_trapped\n')
file = open(path,'a')
#write data to file
file.write(time.strftime("%Y-%m-%d_%H-%M-%S",time.localtime(self.start_time_ramp))+"\t")
file.write(time.strftime("%Y-%m-%d_%H-%M-%S",time.localtime(self.stop_time_ramp))+"\t")
file.write(time.strftime("%Y-%m-%d_%H-%M-%S",time.localtime(self.save_time))+"\t")
#write contents of all the lists
for L in [self.B_start,results,self.B_expelled,self.B_trapped]:
for i in L:
try:
file.write(f"{i}\t")
except:
file.write("No Value\t")
file.write("\n")#new line
file.close()
def set_I0(self):#sets the current values and relais status of 0-Field
#turn on outputs
self.sync_BK_9174B.update({'OutputOn':[True,True]})
self.sync_BK_9174B_2.update({'OutputOn':[True,True]})
self.sync_BK_9174B.update({'setU':[70,70]})
self.sync_BK_9174B_2.update({'setU':[70,70]})
#set currents
self.sync_BK_9174B.update({'setI':[self.dSB_Ix_set0.value(),self.dSB_Iy_set0.value()]})
self.sync_BK_9174B_2.update({'setI':[self.dSB_Iz_set0.value(),self.dSB_Iy2_set0.value()]})
#set relais
self.sync_BK_9131B.update({'SetU':[5,5,5]})
self.sync_BK_9131B.update({'SetI':[1,1,1]})
self.sync_BK_9131B.update({'OutputOn':[self.checkBox_I0x.isChecked(),self.checkBox_I0y.isChecked(),self.checkBox_I0z.isChecked()]})
def set_I1(self):#sets the current values and relais status of Field 1
#turn on outputs
self.sync_BK_9174B.update({'OutputOn':[True,True]})
self.sync_BK_9174B_2.update({'OutputOn':[True,True]})
self.sync_BK_9174B.update({'setU':[70,70]})
self.sync_BK_9174B_2.update({'setU':[70,70]})
#set currents
self.sync_BK_9174B.update({'setI':[self.dSB_Ix_set1.value(),self.dSB_Iy_set1.value()]})
self.sync_BK_9174B_2.update({'setI':[self.dSB_Iz_set1.value(),self.dSB_Iy2_set1.value()]})
#set relais
self.sync_BK_9131B.update({'SetU':[5,5,5]})
self.sync_BK_9131B.update({'SetI':[1,1,1]})
self.sync_BK_9131B.update({'OutputOn':[self.checkBox_I1x.isChecked(),self.checkBox_I1y.isChecked(),self.checkBox_I1z.isChecked()]})
def set_I2(self):#sets the current values and relais status of Field 2
#turn on outputs
self.sync_BK_9174B.update({'OutputOn':[True,True]})
self.sync_BK_9174B_2.update({'OutputOn':[True,True]})
self.sync_BK_9174B.update({'setU':[70,70]})
self.sync_BK_9174B_2.update({'setU':[70,70]})
#set currents
self.sync_BK_9174B.update({'setI':[self.dSB_Ix_set2.value(),self.dSB_Iy_set2.value()]})
self.sync_BK_9174B_2.update({'setI':[self.dSB_Iz_set2.value(),self.dSB_Iy2_set2.value()]})
#set relais
self.sync_BK_9131B.update({'SetU':[5,5,5]})
self.sync_BK_9131B.update({'SetI':[1,1,1]})
self.sync_BK_9131B.update({'OutputOn':[self.checkBox_I2x.isChecked(),self.checkBox_I2y.isChecked(),self.checkBox_I2z.isChecked()]})
def get_I0(self): #gets momentary current values and relais status and saves them in 0-Field boxes
self.dSB_Ix_set0.setValue(self.sync_BK_9174B.get('setI')[0])
self.dSB_Iy_set0.setValue(self.sync_BK_9174B.get('setI')[1])
self.dSB_Iy2_set0.setValue(self.sync_BK_9174B_2.get('setI')[1])
self.dSB_Iz_set0.setValue(self.sync_BK_9174B_2.get('setI')[0])
for box,state in zip([self.checkBox_I0x,self.checkBox_I0y,self.checkBox_I0z], self.sync_BK_9131B.get('OutputOn')):
box.setChecked(state)
def get_I1(self): #gets momentary current values and relais status and saves them in Field 1 boxes
self.dSB_Ix_set1.setValue(self.sync_BK_9174B.get('setI')[0])
self.dSB_Iy_set1.setValue(self.sync_BK_9174B.get('setI')[1])
self.dSB_Iy2_set1.setValue(self.sync_BK_9174B_2.get('setI')[1])
self.dSB_Iz_set1.setValue(self.sync_BK_9174B_2.get('setI')[0])
for box,state in zip([self.checkBox_I1x,self.checkBox_I1y,self.checkBox_I1z], self.sync_BK_9131B.get('OutputOn')):
box.setChecked(state)
def get_I2(self): #gets momentary current values and relais status and saves them in Field 2 boxes
self.dSB_Ix_set2.setValue(self.sync_BK_9174B.get('setI')[0])
self.dSB_Iy_set2.setValue(self.sync_BK_9174B.get('setI')[1])
self.dSB_Iy2_set2.setValue(self.sync_BK_9174B_2.get('setI')[1])
self.dSB_Iz_set2.setValue(self.sync_BK_9174B_2.get('setI')[0])
for box,state in zip([self.checkBox_I2x,self.checkBox_I2y,self.checkBox_I2z], self.sync_BK_9131B.get('OutputOn')):
box.setChecked(state)
def turn_off_coils(self): #turns off all coils
#turn on outputs
self.sync_BK_9174B.update({'OutputOn':[False,False]})
self.sync_BK_9174B_2.update({'OutputOn':[False,False]})
#set relais
self.sync_BK_9131B.update({'OutputOn':[False,False,False]})
def set_B(self): #set global variables B_set and Start_Compensation to start magnetic field compensation
B_set = [self.dSB_Bx_set.value(), self.dSB_By_set.value(), self.dSB_Bz_set.value()]
self.syncdict.update({"B_set":B_set})
self.syncdict.update({"Start_Compensation":True})
def set_LED(self):
'''turns LED on or off'''
if self.checkBox_LED.isChecked():
update_single_entry(self.sync_BK_9132B_2,'setU',2,5)
update_single_entry(self.sync_BK_9132B_2,'setI',2,1)
update_single_entry(self.sync_BK_9132B_2,'OutputOn',2,True)
else:
update_single_entry(self.sync_BK_9132B_2,'OutputOn',2,False)
def switch_fan(self):
'''turn relais fan on or off'''
if self.checkBox_Relais_Fan.isChecked():
update_single_entry(self.sync_BK_9132B,'setU',1,9)
update_single_entry(self.sync_BK_9132B,'setI',1,1)
update_single_entry(self.sync_BK_9132B,'OutputOn',1,True)
else:
update_single_entry(self.sync_BK_9132B,'OutputOn',1,False)
def switch_AMR_supply(self):
'''turn AMR supply voltage of 9 V on or off'''
if self.checkBox_AMR_supply.isChecked():
update_single_entry(self.sync_Keithley_2230G,'setU',0,9)
update_single_entry(self.sync_Keithley_2230G,'setI',0,1)
update_single_entry(self.sync_Keithley_2230G,'OutputOn',0,True)
else:
update_single_entry(self.sync_Keithley_2230G,'OutputOn',0,False)
def AMR_flip(self):
'''turn AMR flip voltage on or off'''
if self.checkBox_AMR_flip.isChecked():
update_single_entry(self.sync_Keithley_2230G,'setU',1,20)
update_single_entry(self.sync_Keithley_2230G,'setI',1,0.2)
update_single_entry(self.sync_Keithley_2230G,'OutputOn',1,True)
else:
update_single_entry(self.sync_Keithley_2230G,'OutputOn',1,False)
def switch_he_heater(self):
'''turn he heater on or with the desired power. The corresponding voltage is calculated only once when the heater is turned on or the power is changed'''
if self.checkBox_He_heater.isChecked():
#calculate voltage
U = np.sqrt(self.dSB_He_heater_P_set.value()*self.dSB_He_heater_R.value())
update_single_entry(self.sync_BK_9132B,'setU',0,U)
update_single_entry(self.sync_BK_9132B,'setI',0,2)
update_single_entry(self.sync_BK_9132B,'OutputOn',0,True)
else:
update_single_entry(self.sync_BK_9132B,'OutputOn',0,False)
def measure_R_pressed(self):
'''starts thread with self.measure_R. Is triggered by button "measure R"'''
worker_measure_R = Worker(self.measure_R)
self.threadpool.start(worker_measure_R)
def measure_R(self):
'''sets a small voltage across the he heater to measure the resistance and sets in gui, turn off the heater after measurement'''
update_single_entry(self.sync_BK_9132B,'setU',0,1)
update_single_entry(self.sync_BK_9132B,'setI',0,0.2)
update_single_entry(self.sync_BK_9132B,'OutputOn',0,True)
time.sleep(3)
U = self.sync_BK_9132B.get('U')[0]
I = self.sync_BK_9132B.get('I')[0]
R = U/I
update_single_entry(self.sync_BK_9132B,'OutputOn',0,False)
self.dSB_He_heater_R.setValue(R)
def monitoring(self, progress_callback):
#if save checkbox is checked it writes measurement values to file specified in line.filePath. There the full path including file extension must be given.
while self.monitor == True:
time.sleep(self.timing_monitoring) #wait is at beginning so first point is not corrupted when app just started.
path = self.line_Path_Mon.text()
if os.path.isfile(path) == False:
with open(path,'a') as file:
file.write('date\tLS218 CH1[K]\tLS218 CH2[K]\tLS218 CH3[K]\tLS218 CH4[K]\tLS218 CH5[K]\tLS218 CH6[K]\tLS218 CH7[K]\tLS218 CH8[K]\tLS336 CHA[K]\tLS336 CHB[K]\tLS336 CHC[K]\tLS336 CHD[K]\n')
file = open(path,'a')
curr_time = datetime.now()
file.write(curr_time.strftime("%Y-%m-%d_%H-%M-%S-%f")+'\t')
for d in self.T:
file.write(f"{d}\t")
file.write('\n')
file.close
def save_default(self):
#saves current set values to txt file in subdirectory configs. Saves values from al spin boxes and text lines.
#Overwrites old values in config file.
path = self.current_dir+'\\configs\\Main_config.txt' #To make shure the config file is at the right place, independent from where the program is started the location of the file is retrieved
file = open(path,'w')
for SB in self.SB_all:
temp = f"{SB.value()}"
file.write(temp+'\t')
for l in self.lines_all:
file.write(l.text()+'\t')
for c in self.checkBoxes_all:
file.write(str(c.isChecked())+'\t')
file.write('\n')
file.close
def load_default(self):
#reads default values from config file in subdirectory config and sets the values in gui. (If no config file exists, it does nothing.)
path = self.current_dir+'\\configs\\Main_config.txt' #To make shure the config file is read from the right place, independent from where the program is started the location of the file is retrieved
try: #exit function if config file does not exist
vals = import_txt.read_raw(path)
except:
return
for SB,v in zip(self.SB_all,vals[0]):
if type(SB) == QDoubleSpinBox:
v = float(v) #convert string in txt to float, so number can be set in dSB
else:
v = int(v) #convert string in txt to int, so number can be set in SB
SB.setValue(v)
for l,v in zip(self.lines_all,vals[0][len(self.SB_all):]):
l.setText(v)
for c,v in zip(self.checkBoxes_all,vals[0][len(self.SB_all)+len(self.lines_all):]):
c.setChecked(v == 'True')
def closeEvent(self,event): #when window is closed self.running and self.monitor are set to False, so all threads stop
self.running = False
self.monitor = False
self.PID_on = False
time.sleep(1)
event.accept()
app = QApplication(sys.argv)
window = MainWindow()
window.show()
app.exec()