From 1fcb24727343c3031420f3321b3716d9c3986c7a Mon Sep 17 00:00:00 2001 From: Andrew M Telford Date: Mon, 23 Jul 2018 11:57:45 +1000 Subject: [PATCH 1/4] Add files via upload New Zurich lockin device --- Devices/Dev_AMT/Dummy_T_UNSW.py | 67 ++++ Devices/Dummy_HF2LI.py | 55 +++ Devices/Dummy_ZA.py | 135 ++++++- Devices/HF2LI.py | 69 ++++ Devices/KeysightE4990A.py | 66 ++-- Devices/KeysightE4990A_multi.py | 197 +++++++++++ Devices/TMC300.py | 41 +++ .../__pycache__/ActonSP2500i.cpython-36.pyc | Bin 0 -> 7825 bytes .../__pycache__/Dummy_ADC_DAC.cpython-36.pyc | Bin 0 -> 4396 bytes .../__pycache__/Dummy_HF2LI.cpython-36.pyc | Bin 0 -> 2264 bytes Devices/__pycache__/Dummy_SMU.cpython-36.pyc | Bin 7762 -> 7767 bytes .../__pycache__/Dummy_T_AMT.cpython-36.pyc | Bin 0 -> 2238 bytes .../Dummy_T_controller.cpython-36.pyc | Bin 0 -> 8717 bytes Devices/__pycache__/Dummy_ZA.cpython-36.pyc | Bin 6447 -> 6476 bytes .../__pycache__/Dummy_lockin.cpython-35.pyc | Bin 0 -> 2227 bytes .../__pycache__/Dummy_lockin.cpython-36.pyc | Bin 0 -> 2139 bytes .../__pycache__/Dummy_lockin.cpython-37.pyc | Bin 0 -> 2224 bytes .../Dummy_monochromator.cpython-35.pyc | Bin 0 -> 4274 bytes .../Dummy_monochromator.cpython-36.pyc | Bin 0 -> 3907 bytes .../Dummy_monochromator.cpython-37.pyc | Bin 0 -> 3876 bytes .../Dummy_oscilloscope.cpython-36.pyc | Bin 0 -> 4018 bytes .../Dummy_spectrometer.cpython-36.pyc | Bin 0 -> 2401 bytes Devices/__pycache__/HF2LI.cpython-35.pyc | Bin 0 -> 3170 bytes Devices/__pycache__/HF2LI.cpython-36.pyc | Bin 0 -> 2827 bytes Devices/__pycache__/HF2LI.cpython-37.pyc | Bin 0 -> 2438 bytes Devices/__pycache__/SR830.cpython-36.pyc | Bin 0 -> 4236 bytes Devices/__pycache__/TMC300.cpython-35.pyc | Bin 0 -> 1842 bytes Devices/__pycache__/__init__.cpython-35.pyc | Bin 0 -> 152 bytes Devices/__pycache__/__init__.cpython-36.pyc | Bin 153 -> 182 bytes Devices/__pycache__/__init__.cpython-37.pyc | Bin 0 -> 156 bytes .../__pycache__/device_manager.cpython-35.pyc | Bin 0 -> 15190 bytes .../__pycache__/device_manager.cpython-36.pyc | Bin 13625 -> 13654 bytes .../__pycache__/device_manager.cpython-37.pyc | Bin 0 -> 13590 bytes .../__pycache__/lakeshore336.cpython-36.pyc | Bin 0 -> 3580 bytes .../__pycache__/serial_ports.cpython-35.pyc | Bin 0 -> 1292 bytes .../__pycache__/serial_ports.cpython-36.pyc | Bin 1186 -> 1215 bytes .../__pycache__/serial_ports.cpython-37.pyc | Bin 0 -> 1189 bytes Devices/__pycache__/visa_ports.cpython-35.pyc | Bin 0 -> 583 bytes Devices/__pycache__/visa_ports.cpython-36.pyc | Bin 549 -> 578 bytes Devices/__pycache__/visa_ports.cpython-37.pyc | Bin 0 -> 552 bytes Devices/devices.ini | 332 ++++++++++-------- Devices/lakeshore336.py | 100 ++++++ 42 files changed, 881 insertions(+), 181 deletions(-) create mode 100644 Devices/Dev_AMT/Dummy_T_UNSW.py create mode 100644 Devices/Dummy_HF2LI.py create mode 100644 Devices/HF2LI.py create mode 100644 Devices/KeysightE4990A_multi.py create mode 100644 Devices/TMC300.py create mode 100644 Devices/__pycache__/ActonSP2500i.cpython-36.pyc create mode 100644 Devices/__pycache__/Dummy_ADC_DAC.cpython-36.pyc create mode 100644 Devices/__pycache__/Dummy_HF2LI.cpython-36.pyc create mode 100644 Devices/__pycache__/Dummy_T_AMT.cpython-36.pyc create mode 100644 Devices/__pycache__/Dummy_T_controller.cpython-36.pyc create mode 100644 Devices/__pycache__/Dummy_lockin.cpython-35.pyc create mode 100644 Devices/__pycache__/Dummy_lockin.cpython-36.pyc create mode 100644 Devices/__pycache__/Dummy_lockin.cpython-37.pyc create mode 100644 Devices/__pycache__/Dummy_monochromator.cpython-35.pyc create mode 100644 Devices/__pycache__/Dummy_monochromator.cpython-36.pyc create mode 100644 Devices/__pycache__/Dummy_monochromator.cpython-37.pyc create mode 100644 Devices/__pycache__/Dummy_oscilloscope.cpython-36.pyc create mode 100644 Devices/__pycache__/Dummy_spectrometer.cpython-36.pyc create mode 100644 Devices/__pycache__/HF2LI.cpython-35.pyc create mode 100644 Devices/__pycache__/HF2LI.cpython-36.pyc create mode 100644 Devices/__pycache__/HF2LI.cpython-37.pyc create mode 100644 Devices/__pycache__/SR830.cpython-36.pyc create mode 100644 Devices/__pycache__/TMC300.cpython-35.pyc create mode 100644 Devices/__pycache__/__init__.cpython-35.pyc create mode 100644 Devices/__pycache__/__init__.cpython-37.pyc create mode 100644 Devices/__pycache__/device_manager.cpython-35.pyc create mode 100644 Devices/__pycache__/device_manager.cpython-37.pyc create mode 100644 Devices/__pycache__/lakeshore336.cpython-36.pyc create mode 100644 Devices/__pycache__/serial_ports.cpython-35.pyc create mode 100644 Devices/__pycache__/serial_ports.cpython-37.pyc create mode 100644 Devices/__pycache__/visa_ports.cpython-35.pyc create mode 100644 Devices/__pycache__/visa_ports.cpython-37.pyc create mode 100644 Devices/lakeshore336.py diff --git a/Devices/Dev_AMT/Dummy_T_UNSW.py b/Devices/Dev_AMT/Dummy_T_UNSW.py new file mode 100644 index 0000000..3d6e452 --- /dev/null +++ b/Devices/Dev_AMT/Dummy_T_UNSW.py @@ -0,0 +1,67 @@ +__author__ = 'Andrew M. Telford & Diego Alonso-Álvarez' + +# Libraries +import datetime + +import matplotlib +matplotlib.use('TkAgg') +import matplotlib.pyplot as plt +import matplotlib.dates as mdates +from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg, NavigationToolbar2TkAgg + +from tkinter import messagebox, ttk +import tkinter as tk +from tkinter import filedialog + +import numpy as np +import time + +from serial import Serial + +# Class definition +class Dummy_T_UNSW(object): + def __init__(self, port='COM4', name='Lakeshore 336', info=None): + self.info = {} + self.info['Name'] = name + if info is not None: + self.info.update(info) + + self.device = "Dummy" + + self.setPoint = 300 + self.temperature = 300 + + print(self.getID()) + + def close(self): + self.device.close() + + def getID(self): + return ("Dummy T Controller") + + def getTemp(self): + self.temperature = self.setPoint + return self.temperature + + def getSP(self): + self.setPoint = self.temperature + return self.setPoint + + def setSP(self, value): + self.setPoint = value + + def setHeater(self, value): + pass + + +class New(Dummy_T): + """ Standarised name for the Lakeshore336 class""" + pass + + +# Testing +if __name__ == '__main__': + root = tk.Tk() + root.withdraw() + test = T_controller(master=root) + root.mainloop() diff --git a/Devices/Dummy_HF2LI.py b/Devices/Dummy_HF2LI.py new file mode 100644 index 0000000..ac4a322 --- /dev/null +++ b/Devices/Dummy_HF2LI.py @@ -0,0 +1,55 @@ +# Libraries +import time +import random +from tkinter import messagebox + + +# Class definition +class Dummy_HF2LI: + + def __init__(self, port=None, name='Dummy HF2LI', info=None): + self.info = {} + self.info['Name'] = name + if info is not None: + self.info.update(info) + + self.integration_time = 300 + self.min_wavelength = 0. + + def readTimeconstant(self): + """ Reads the integration time selected in the lockin + :return: + """ + return self.integration_time + + def update_integration_time(self, new_time): + """ Updates the integration time in the lockin based on the selection in the program + :param new_time: the new integration time selected in the program + :return: + """ + self.integration_time = new_time + + print('Integration time: {} ms'.format(self.integration_time)) + return self.integration_time + + def open(self): + pass + + def measure(self): + return random.random(), random.random(), random.random(), random.random(), random.random(), random.random() + + def close(self): + pass + + def interface(self, master): + messagebox.showinfo(message='No specific configuration available for {0}'.format(self.info['Name']), + detail='Press OK to continue.', title='Device configuration') + +class New(Dummy_HF2LI): + """ Standarised name for the Dummy_monochromator class""" + pass + +# Testing +if __name__ == "__main__": + test = New('COM4') + print(test.measure()) diff --git a/Devices/Dummy_ZA.py b/Devices/Dummy_ZA.py index 9a96b49..0721914 100644 --- a/Devices/Dummy_ZA.py +++ b/Devices/Dummy_ZA.py @@ -23,6 +23,16 @@ def __init__(self, address='GPIB::20', name='Dummy ZA', info=None): if info is not None: self.info.update(info) + # Opening the device is essential. Here is an example for a VISA-based instrument + # rm = visa.ResourceManager() + # self.device = rm.open_resource("{}".format(address)) + ##self.device = None + + ##self.i_range = ['auto', '1e-9 A', '10e-9 A', '100e-9 A', '1e-6 A', '10e-6 A', '100e-6 A', '1e-3 A', '10e-3 A', + ## '100e-3 A'] + ##self.v_range = ['auto', '1.1 V', '11 V'] + ##self.log_points = ['5', '10', '25', '50'] + ##self.int_time = ['0.416', '4', '16.67', '20'] def query(self, msg): """ Send a message to the instrument and then waits for an answer. depending on the nature of the device and @@ -56,39 +66,150 @@ def close(self): pass - def set_za_function(self, fixed): + def set_za_function(self, function, fixed): """ Sets the functionality of the source, what to bias and how to do it. - :param fixed: variable indicating whethe rbias or frequency are fixed + :param function: 'dc' or 'sweep', dc or sweep operating function + :param source: 'v' or 'i', voltage or current source :return: None """ self.fixed = fixed + #self.function = function + + + def update_compliance(self, compliance, measRange): + """ Updates the compliance and the measurement range. + + :param compliance: current or voltage compliance, oposite to the selected source + :param measRange: the meas range as the index of self.i_range or self.v_range + :return: None + """ + + self.compliance = compliance + self.measRange = measRange + + + def update_integration_time(self, new_time): + """ Sets the integration time. + + :param new_time: new integration time as the index from the self.int_time list + :return: None + """ + + self.itime = new_time + + + def update_waiting_time(self, new_time): + """ Sets the delay time between setting a bias and starting a measurement + + :param new_time: New delay time in ms. + :return: None + """ + self.delay = new_time def setup_measurement(self, plot_format, options): - """ Sets up the scan. + """ Prepares and triggers the IV measurement. + + :param function: 'dc' or 'sweep', dc or sweep operating function + :param source: 'v' or 'i' for voltage or current biasing, respectively + :param compliance: meas compliance + :param measRange: the meas range as the index of self.i_range or self.v_range + :param delay: the delay between applying a bias and taking the meas + :param intTime: integration time as the index from the self.int_time list + :return: The measured data """ + ##self.set_source_function(function, fixed) + ##self.update_compliance(compliance, measRange) + ##self.update_integration_time(intTime) + ##self.update_waiting_time(delay) + + ## Setup fixed variable, its value, and the sweep parameters. + + def measure(self, options): - """ Starts the scan. + """ Prepares and triggers the IV measurement. + + :param source: 'v' or 'i' for voltage or current biasing, respectively + :param start: start bias + :param stop: stop bias + :param points: points to measure per decade in current bias mode + :param step: step size in voltage bias mode + :param compliance: meas compliance + :param measRange: the meas range as the index of self.i_range or self.v_range + :param delay: the delay between applying a bias and taking the meas + :param intTime: integration time as the index from the self.int_time list + :return: The estimated measuring time + """ + + # First we setup the experiment + + # We program the sweep. + # We typically will use the AUTO fixed range for the bias, as it might cover may orders of magnitude + + + # We estimate the runing time of the sweep, set the timeout time accordingly and return the control to the IV module. + # This will wait in a "safe way" during the measurement, avoiding freezing the program + #self.totalPoints = nop + #minimumWait = 1 ## in ms + #measTime = self.totalPoints * minimumWait + ##if self.measRange == 0: + ## measTime = measTime * 10 + # self.device.timeout = None + #print("Waiting for Dummy.\nEstimated measurement time = {} s".format(measTime / 1000)) + + + # Finally, we trigger the sweep. Depending on the SMU, you might need to turn it on first + + + #return measTime + + + ##def set_bias(self, biasValue=0.0): + """ Set a bias to the source. If the source is not turned on or the function is not set to 'dc', + the bias will not be applied + + :param biasValue: the chosen bias + :return: None """ + # First we check that the experiment has been setup for DC bias/meas + ##if self.function != 'dc': + ## print('DC measurement not setup correctly. Call self.setup_experiment before setting the bias') + ## return + + # Set the bias in the SMU + + # And triger it, if need it + def return_data(self): - """ Generate random data for testing. + """ We get the data fromt he SMU. Depending on the order provided by the SMU we need to invert the output to + have voltage-current, regardless of who is the bias or the meas. We convert the data to an array and re-shape + it in two columns. - :return: the generated data, a tuple with two vectors: voltage and current + :return: the measured data, a tuple with two vectors: voltage and current """ + # If we are in sweep function, we have to really wait until the meas is finished. In some SMU this is not needed + # as they will not response until they are finished. In others, they don't like to be bothered while measuring. + # Then, we have to turn off the source if not done automatically. In the dc function, the source is turn off externally + totalPoints = 100 # Get the data. # Here we just simulate some random data. two points for DC or a large number for a sweep data = np.random.rand(3*totalPoints) data = np.array(data, dtype=np.float32).reshape((-1, 3)) - return data[:, 0], data[:, 1], data[:, 2] + # Depending of what we are biasing, we might need to invert the order of the output + # We assume in the example that the source produces bias-meas and we want voltage-current, so we invert it + ##if self.source == 0: + return data[:, 0], data[:, 1], data[:, 2] + ##else: + ## return data[:, 1], data[:, 0] def operate_off(self): """ Turn off the output of the SMU diff --git a/Devices/HF2LI.py b/Devices/HF2LI.py new file mode 100644 index 0000000..0839296 --- /dev/null +++ b/Devices/HF2LI.py @@ -0,0 +1,69 @@ +# Libraries +import numpy as np +from tkinter import messagebox +import zhinst.ziPython, zhinst.utils + +debug = False +developing = False + +# Class definition +class HF2LI: + def __init__(self, port=None, name='Zurich Inst HF2LI', info=None): + self.info = {} + self.info['Name'] = name + if info is not None: + self.info.update(info) + self.integration_time = 100 + self.min_wavelength = 0. + self.daq = zhinst.ziPython.ziDAQServer('localhost', 8005) + + def open(self, demod=[0,1,2]): + """ Subscribes to two demodulators to exctract data. + :param demod: list of 2 demodulator numbers (in Python numbering) + :return: none + """ + self.daq.subscribe('/dev1370/demods/'+str(demod[0])+'/sample') + self.daq.subscribe('/dev1370/demods/'+str(demod[1])+'/sample') + self.daq.subscribe('/dev1370/demods/'+str(demod[2])+'/sample') + + def measure(self): + self.daq.sync() + timeout = self.integration_time + 200 + raw_data = self.daq.poll(self.integration_time/1000, timeout) + Xc = np.mean(raw_data['dev1370']['demods']['0']['sample']['x']) + Yc = np.mean(raw_data['dev1370']['demods']['0']['sample']['y']) + Xsum = np.mean(raw_data['dev1370']['demods']['1']['sample']['x']) + Ysum = np.mean(raw_data['dev1370']['demods']['1']['sample']['y']) + Xdif = np.mean(raw_data['dev1370']['demods']['2']['sample']['x']) + Ydif = np.mean(raw_data['dev1370']['demods']['2']['sample']['y']) + return Xc, Yc, Xsum, Ysum, Xdif, Ydif + + def update_integration_time(self, new_time): + """ Updates the integration time in the lockin based on the selection in the program + :param new_time: the new integration time selected in the program + :return: + """ + self.integration_time = new_time + + print('Integration time: {} ms'.format(self.integration_time)) + return self.integration_time + + + def close(self, demod=[0,1,2]): + self.daq.unsubscribe('/dev1370/demods/'+str(demod[0])+'/sample') + self.daq.unsubscribe('/dev1370/demods/'+str(demod[1])+'/sample') + self.daq.unsubscribe('/dev1370/demods/'+str(demod[2])+'/sample') + + def interface(self, master): + messagebox.showinfo(message='No specific configuration available for {0}'.format(self.info['Name']), + detail='Press OK to continue.', title='Device configuration') + + +class New(HF2LI): + """ Standarised name for the HF2LI class""" + pass + +# Testing +if __name__=="__main__": + test = New() + print(test.update_integration_time(0.800)) diff --git a/Devices/KeysightE4990A.py b/Devices/KeysightE4990A.py index c1ce422..833a402 100644 --- a/Devices/KeysightE4990A.py +++ b/Devices/KeysightE4990A.py @@ -7,7 +7,7 @@ class KeysightE4990A: """ - Keysight E4990A impedance analyser class + Keysight E4990A impedance analyser """ def __init__(self, address='USB0::0x0957::0x1809::MY54101596::0::INSTR', name='Keysight E4990A', info=None): """ Constructor of the device class. Add here whatever initialisation routing needed by the SMU. @@ -71,14 +71,18 @@ def close(self): self.rm.close() def setup_measurement(self, plot_format, options): - """ Prepares the measurement by setting up the scan parameters. - - :param plot_format: dictionary of setup parameters for plotting the data - :param options: dictionary of setup parameters for the scan - :return: None + """ Prepares and triggers the IV measurement. + + :param function: 'dc' or 'sweep', dc or sweep operating function + :param source: 'v' or 'i' for voltage or current biasing, respectively + :param compliance: meas compliance + :param measRange: the meas range as the index of self.i_range or self.v_range + :param delay: the delay between applying a bias and taking the meas + :param intTime: integration time as the index from the self.int_time list + :return: The measured data """ ## Clear buffer - self.device.write('*CLS') + #self.device.write('*CLS') ## Set DC bias mode to Voltage self.device.write(':SOURce1:BIAS:MODE %s' % ('VOLTage')) @@ -136,11 +140,9 @@ def setup_measurement(self, plot_format, options): self.device.write(':CALCulate1:PARameter4:DEFine %s' % ('IDC')) ## Trace 4 = IDC def measure(self, options): - """ Starts and stops the measurement. - - :return: None + """ Triggers the measurement. + """ - if options['fixed_value'] != 0: self.device.write(':SOURce:BIAS:STATe %s' % ('ON')) self.device.write(':TRIGger:SEQuence:SOURce %s' % ('BUS')) @@ -151,11 +153,12 @@ def measure(self, options): self.device.write(':SOURce:BIAS:STATe %s' % ('OFF')) def return_data(self): - """ Retrieves the data from the instrument + """ We get the data fromt he SMU. Depending on the order provided by the SMU we need to invert the output to + have voltage-current, regardless of who is the bias or the meas. We convert the data to an array and re-shape + it in two columns. - :return: the measured data, a tuple with three vectors, which depend on the type of measurement performed + :return: the measured data, a tuple with two vectors: voltage and current """ - ## Autoscale all traces self.device.write(':DISPlay:WINDow1:TRACe1:Y:SCALe:AUTO') self.device.write(':DISPlay:WINDow1:TRACe2:Y:SCALe:AUTO') @@ -172,21 +175,25 @@ def return_data(self): data = np.column_stack((x_data, Ch1_data, Ch2_data)) ## Aggregate data return data[:, 0], data[:, 1], data[:, 2] + def operate_off(self): + """ Turn off the output of the SMU + + :return: None + """ + pass + + def operate_on(self): + """ Turn on the output of the SMU + + :return: None + """ + pass def interface(self, master): - """ Givces error if Mordor cannot locate a configuration file for the instrument. - - :return: Error - no configuration available. - """ - messagebox.showinfo(message='No specific configuration available for {0}'.format(self.info['Name']), + messagebox.showinfo(message='No specific configuration available for {0}'.format(self.info['Name']), detail='Press OK to continue.', title='Device configuration') def abort_sweep(self): - """ Aborts scan. - - :return: None - """ - self.device.write(':ABORt') self.device.write(':SOURce:BIAS:STATe %s' % ('OFF')) @@ -194,3 +201,14 @@ class New(KeysightE4990A): """ Standarised name for the KeysightE4990A class""" pass + +#if __name__ == '__main__': +# za = New() +# za.gen_setup() +# za.setup_measurement() +# za.measure() +# temp_values = za.device.query_ascii_values('*OPC?') +# opc = int(temp_values[0]) +# data = za.get_data() +# za.close() +# za.rm.close() diff --git a/Devices/KeysightE4990A_multi.py b/Devices/KeysightE4990A_multi.py new file mode 100644 index 0000000..684059c --- /dev/null +++ b/Devices/KeysightE4990A_multi.py @@ -0,0 +1,197 @@ +__author__ = 'A. M. Telford & D. Alonso-Álvarez' + +import numpy as np +from tkinter import messagebox +import visa + + +class KeysightE4990A: + """ + Keysight E4990A impedance analyser + """ + def __init__(self, address='USB0::0x0957::0x1809::MY54101596::0::INSTR', name='Keysight E4990A', info=None): + """ Constructor of the device class. Add here whatever initialisation routing needed by the SMU. + Usual things are: + - Open the device (ESSENTIAL!!!) + - Reset it to some default values + - Ask for its name + - Enable it for remote use + + :param address: Port where the device is connected + """ + self.info = {} + self.info['Name'] = name + if info is not None: + self.info.update(info) + + # Opening the device is essential. Here is an example for a VISA-based instrument + self.rm = visa.ResourceManager() + self.device = self.rm.open_resource("{}".format(address)) + self.device.timeout = 180000 ## 180 s default timeout (e.g. for OPC?) + + ## Dictionary for GUI-to-machine commands translation + + self.translation = {'Cs (F)' : 'CS', + 'Cp (F)' : 'CP', + 'Rs (Ohm)' : 'RS', + 'Rp (Ohm)' : 'RP', + '|Z| (Ohm)' : 'Z', + 'Tz (deg)' : 'TZ', + 'log' : 'LOGarithmic', + 'linear' : 'LINear', + 'up': 'UP', + 'down':'DOWN'} + + def query(self, msg): + """ Send a message to the instrument and then waits for an answer. depending on the nature of the device and + how is connected, this would look different + + :param msg: command to be executed in the instrument + :return: the answer to that command + """ + return self.device.query(msg) + + def write(self, msg): + """ Send a message to the instrument + + :param msg: message to be sent to the instrument + :return: None + """ + self.device.write(msg) + + def close(self): + """ Closes the connection with the instrument + + :return: None + """ + self.device.close() + + + def gen_setup(self): + """ Sets the functionality of the impedance analyser. + + """ + self.device.write(':SOURce1:BIAS:MODE %s' % ('VOLTage')) + self.device.write(':SOURce1:BIAS:RANGe %s' % ('M1')) + + def setup_measurement(self, plot_format, options): + """ Prepares and triggers the IV measurement. + + :param function: 'dc' or 'sweep', dc or sweep operating function + :param source: 'v' or 'i' for voltage or current biasing, respectively + :param compliance: meas compliance + :param measRange: the meas range as the index of self.i_range or self.v_range + :param delay: the delay between applying a bias and taking the meas + :param intTime: integration time as the index from the self.int_time list + :return: The measured data + """ + + ## Turn on point averaging + self.device.write(':SENSe1:AVERage:STATe %d' % (1)) + ## Set number of point averages + self.device.write(':SENSe1:AVERage:COUNt %d' % (options['navg'])) + ## Set AC bias amplitude (OSC) + self.device.write(':SOURce1:VOLTage:LEVel:IMMediate:AMPLitude %d' % (options['osc_amp'])) + ## Set sweep direction + self.device.write(':SENSe1:SWEep:DIRection %s' % (self.translation[options['sweep_dir']])) + ## Set sweep points + self.device.write(':SENSe1:SWEep:POINts %d' % (options['nop'])) + + ## Y-axis scaling (log or linear) ------------------------------------- + self.device.write(':DISPlay:WINDow1:TRACe1:Y:SPACing %s' % (self.translation[plot_format['Ch1_scale']])) + self.device.write(':DISPlay:WINDow1:TRACe2:Y:SPACing %s' % (self.translation[plot_format['Ch2_scale']])) + + ## Y-axis labels + self.device.write(':CALCulate1:PARameter1:DEFine %s' % (self.translation[plot_format['Ch1_ylabel']])) + self.device.write(':CALCulate1:PARameter2:DEFine %s' % (self.translation[plot_format['Ch2_ylabel']])) + + + if options['fixed'] == 'bias': + self.device.write('SOURce1:BIAS:VOLTage:LEVel:IMMediate:AMPLitude %d' % (options['fixed_value'])) + self.device.write(':SENSe1:FREQuency:STARt %G' % (options['start'])) + self.device.write(':SENSe1:FREQuency:STOP %G' % (options['stop'])) + self.device.write(':SENSe1:SWEep:TYPE %s' % (self.translation[plot_format['x_scale']])) + else: + self.device.write(':SOURce1:BIAS:VOLTage:STARt %G' % (options['start'])) + self.device.write(':SOURce1:BIAS:VOLTage:STOP %G' % (options['stop'])) + if plot_format['x_scale'] == 'linear': + ## The machine has different terms for DC bias sweeps, so the + ## translation dictionary cannot be used here + self.device.write(':SENSe1:SWEep:TYPE %s' % ('BIAS')) + else: + self.device.write(':SENSe1:SWEep:TYPE %s' % ('LBIas')) + + def measure(self, options): + """ Triggers the measurement. + + """ + if options['fixed_value'] != 0: + self.device.write(':SOURce:BIAS:STATe %s' % ('ON')) + self.device.write(':TRIGger:SEQuence:SOURce %s' % ('BUS')) + self.device.write(':INITiate1:CONTinuous %d' % (1)) + self.device.write(':TRIGger:SEQuence:SINGle') + print("Waiting for Keysight E4990A...") + self.opc = self.device.query_ascii_values('*OPC?') + ## if self.opc == 1: + + self.device.write(':SOURce:BIAS:STATe %s' % ('OFF')) + + def return_data(self): + """ We get the data fromt he SMU. Depending on the order provided by the SMU we need to invert the output to + have voltage-current, regardless of who is the bias or the meas. We convert the data to an array and re-shape + it in two columns. + + :return: the measured data, a tuple with two vectors: voltage and current + """ + + self.device.write(':DISPlay:WINDow1:TRACe1:Y:SCALe:AUTO') + self.device.write(':DISPlay:WINDow1:TRACe2:Y:SCALe:AUTO') + #self.device.write(':MMEMory:STORe:FDATa "%s"' % (filename)) + + #return data[:, 0], data[:, 1], data[:, 2] + ##test + #totalPoints = 100 + #data = np.random.rand(3*totalPoints) + self.device.write('CALCulate1:PARameter1:SELect') ## Select Ch1 trace + x_data = self.device.query_ascii_values('CALCulate1:SELected:DATA:XAXis?') + Ch1_data = self.device.query_ascii_values(':CALCulate1:SELected:DATA:FDATa?') + Ch1_data = Ch1_data[::2] ## Skip zeroes in output data list + self.device.write('CALCulate1:PARameter2:SELect') ## Select Ch2 trace + Ch2_data = self.device.query_ascii_values(':CALCulate1:SELected:DATA:FDATa?') + Ch2_data = Ch2_data[::2] ## Skip zeroes in output data list + data = np.column_stack((x_data, Ch1_data, Ch2_data)) ## Aggregate data + return data[:, 0], data[:, 1], data[:, 2] + + def operate_off(self): + """ Turn off the output of the SMU + + :return: None + """ + pass + + def operate_on(self): + """ Turn on the output of the SMU + + :return: None + """ + pass + + def interface(self, master): + messagebox.showinfo(message='No specific configuration available for {0}'.format(self.info['Name']), + detail='Press OK to continue.', title='Device configuration') + +class New(KeysightE4990A): + """ Standarised name for the KeysightE4990A class""" + pass + + +#if __name__ == '__main__': +# za = New() +# za.gen_setup() +# za.setup_measurement() +# za.measure() +# temp_values = za.device.query_ascii_values('*OPC?') +# opc = int(temp_values[0]) +# data = za.get_data() +# za.close() +# za.rm.close() diff --git a/Devices/TMC300.py b/Devices/TMC300.py new file mode 100644 index 0000000..afec24c --- /dev/null +++ b/Devices/TMC300.py @@ -0,0 +1,41 @@ +# Libraries +import visa +from tkinter import messagebox +import pyBen + +debug = False +developing = False + +# Class definition +class TMC300: + def __init__(self, port=None, name='Bentham TMC300', info=None): + self.info = {} + self.info['Name'] = name + if info is not None: + self.info.update(info) + + ## Initialise monochromator + pyBen.build_system_model("C:/Program Files (x86)/Bentham/SDK/Configuration Files/system.cfg") + pyBen.load_setup("C:/Program Files (x86)/Bentham/SDK/Configuration Files/system.atr") + pyBen.initialise() + pyBen.park() + + def move(self, target, speed='Normal'): + pyBen.select_wavelength(float(target),1) # 1s timeout to complete operation + + def close(self): + pyBen.close() + + def interface(self, master): + messagebox.showinfo(message='No specific configuration available for {0}'.format(self.info['Name']), + detail='Press OK to continue.', title='Device configuration') + + +class New(TMC300): + """ Standarised name for the TMC300 class""" + pass + +# Testing +if __name__=="__main__": + test = New() + print(test.update_integration_time(0.800)) diff --git a/Devices/__pycache__/ActonSP2500i.cpython-36.pyc b/Devices/__pycache__/ActonSP2500i.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..3f08bad99ae7bf609d42b3c1cc460e75695d9b4d GIT binary patch literal 7825 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 BK%)Qv literal 0 HcmV?d00001 diff --git a/Devices/__pycache__/Dummy_ADC_DAC.cpython-36.pyc b/Devices/__pycache__/Dummy_ADC_DAC.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..d8a531d10e02590f441c29d5514fff8fcc93730d GIT binary patch literal 4396 zcmb7HO>^7E83sU*1R+Y1zwJ16*Nzj7T1y(+P1DhMQmc|{d!}YIwcJctbub`yMS>y$ zW_KZ(8un7<*y*7^poiZ2H}VU5#9{>&Cj5bylNvxONb#C>^bpv8|ZvE4a9F@W}UHvR-%~SU*#mg`5MsN{51p zw_1j5FH0gFr%5=7^_c0D4a0rGV$BXyxz9wX;=!050wH^qN}fvJ-vQlAAlwirR>9p8 zQ~N?kJ+KsN7KI7x2)37I2?yVX&!uoGhvMK8b_ZNYmI%QWVa0_(8WL@gRBG;=Ug@M5 zJ@6HTs2ScAQt2?EYaj$N7@CO^iDn&e(2EsQk&H)rmF>{vFdVaV&z#JXI0`ih)o^qw z?Ov!@$kZS`1W6<4DV2B>x*~r3DCKkm4<}v4cr2kX$mER9=_?OdC(}eCP!2N)W0*=v zVIbbdoq=FLQS7A&E)U{itidb@J|vlq)6B??lclgio2&i_EZk~E1W&&T22at{9|42~ ztGC(?|FB)*_qoffU)yb$*LeMFs}1Ys4fHj#X!Ld7%@$RtCK2$8i6_ik3fTf zPhgHflK=vnbyOD5@_g>b$zGb*vJnrp$h}UOaY>^E9>eX!L6By8l<9Uegv+MgAWksL zon(~zM?$757{vP`Zy<66a1&fK!-H^;iM&?a&Feao$>)1}d9^F_%dLE2Ln$FC3OtiC zmCJ7KDlyp0-BBua?k3<_UIw*)v*cZL-}W}wci-TK+C6&w$)leU3IEW*mc1s4dIvVF*00v_n z1XqDq-2|}gn(f=3-E?ZsJ^Qk=U@tgL+jEYtmoJ}Qj7U%PQBTcS94U(1(5cg!UOcr> za$hMzJ~pf_+wu;MnsEW{VVc(i2v-Kd5+;h&Z`nuJX7QU7k=exovJYwo{T2o=FiYDL z`xrml($2)zm19TUGb8udkxwUNtkt*IC-#>0yM^By*O)kT`m5D1PRZX{uH`p_5^fMD z622AcFs}?mlKZiW6N;IU$lXD#bY9ijXdv?2v-duKv$OqXC)j@3dc9mRoVPt#(uR~F zbRoj17hpOg4f#H}`4O5Tv$mj*t42o)_R-Z6U8{xp{dbL&o#J~GAwK}Z$Ao&Sgezr) z#=Or|KxfWWK*5JYzg_giJXzjLm{e~A)}ywZ25YCNX8ZL0kf3KF2@I^NG~617D-%GEfsE;Pc))KJQ3!IJp9FO4E;N;qDo@ zN2A*SFVGaFBg<~u@W~l7nWlo1M>ue?);`8gVJjw3L1q(3?Y2cTtXC+6O>5uE(Kqb1 z&{R!L(-njBu)^=D%nR9zm(O1?lbKKhN-T7dF;FDNL$S&>i_$Tp3fa_TTE^Y3K+(yj z-})$&;gDWkH%}+ou!H@XZA87U?yWPPA$KCJQT=Di?QEb>+$L=*0IGv<25Z|t5By&e>WeM#{Lq;N64vj=d|?V~H-Z4pPDy6>9b7lc z)_!)T#3a$RNtY<8Pw>AD|HhgWfq4aun08ya{cz`(CC(`k{f5ezS+*C0z!dCyJ5Ee| z3h3>^;yv4Sj_%A7RX+YbVwgs#f{qL2kXNHYs)QhvM&8b>lFk0fzwl9rp=3e9mNLik z6{>Hjx(b3E${VIU8_>8J1Yc$0px7xq{X?9W_X$w8HF?iiqcKvsMr#iVd_sWSUQ%+9 z9|5#)%%=IN;@Wp+H_C#MPw0vBkYYwAt`Mh-Xo>-Nj_deWyo%RAbMYfs@v43ku;SGu zosQ7XKC-o^Xo}uA`x9g1PmBq~d8H)|k5(9zneb4?6b(ogCSZ^om-NY^L8w#<{z`dC z{+z&504maW8U?|H9Bkl3jAl{@ti+bILi!knD4m0&Yip=Y*R&q5W~u|th~YyNx{?Zc zd5JFn9o4-gD_d^LU(o8O0C^PwX*8Z@kF-O7?hXIuc|&1tcjV9Lyiw41i8%M36z1?5 Z{b5j~bl34v=PrJ^=%R1d3*Z0% literal 0 HcmV?d00001 diff --git a/Devices/__pycache__/Dummy_HF2LI.cpython-36.pyc b/Devices/__pycache__/Dummy_HF2LI.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..bcb92c1ebf77d119faa2fbbd79148f631667b5b4 GIT binary patch literal 2264 zcma)7-EP}96ecBEmfa+E(q>tKV$;@PsIjhSlMDllV#tu7U9r{;)^-<~0YMRIM~NhQ zB%Lg(d*$BlLG}Q98Qtz`uh6TVL)D&=HRvetkQAx&^PP`JTg%JsR&W28C6|!DNUb?A z-+`_I5R5RIlAs!CKw)jAR%Qn_C65TRne&D)XJ!QstQyRPmCK!(9W=Phn#Y#>nKzg> zqd}8RNeaZ<1wa;SUctpFHlrtyaES z#TYs^9MSkLbhQm4AyX2PsWl}>;QRwJwHY~X$uDqyM5ne13U)(_*&tyT0(APN(~mNq z+@XYwK#OTzr*x8hC`xxSW|8J)C&@J*$w((64|S69awSXh@H9H%DbGjx5Wi479hS~m zNL@NP=#-E(@&}R2FuWb^_69E%mugUKeY3f}dH>tNBOyj9_xEIS!Uy|j`Ve%V3VEF5 zqmU(%$6CmkxxY0W?D3N%=4wzw>hAcg^ujR76CH+au&S0Y z9O}|G_2`o2(&Bpk`21)Lv73e?c7rK;kJtf9w0PqG#3QDBeaQV97CvI(L*U>nXRu=q zq#}kO+Wx!NlUz?^-dnurQt5$m7|=H*UgWN|76yGJ0hAF{8+achOnJpZ-ge=S$){7nXkzDF{_mp;n;-E9S9} zr2w8}i&%T(2$p`%PYrK+1|V2{7{&i+x(H*j@%Ud>noRWkSFim{b!~%h*%(X6Rp}0e z%p%>j4BVwR_vIq+8x`4sKj*oA0{~RW2GVe?4lQogb8yKw@23>rS##}Z-x$Ay0gf2+ z{1#d@38h7+ZV@-nsYllgV{gMm17l|&kV6+ajaAK+QO-nmi6703M`|MZmo-I9^@1UwqXU zz8dp5878q0mui@dCRHi(qmw8}qXVcyz!Cq|z1I@87wh{Hs=fd0M_&v4LMQoz--Y5~ zYR-8bUAqLF%)g;@8P}k&Z0JO%yz5BJU+Jksaf;m7-oV^Qk*Y^;jfb@t#;&9^rC~}I*N4^ z8z??T@d*kn*)@T6Nn%04vZZk4S?!kRdTr0f$Lz)MdeM*tJJ1!{S^O69hiE}k?9%S@ z(_+hi4)u*iGQo<1-kCIDjjaOBL@we(Dd6@(dg3%vst>4s3Oc2a;xiCsGYpxC!?5PC z@!xp;Ih?o$-GJy(he~{dIAsz1!NS1VefD%4kaMt<%a4pg4dojWVA5w|GanhdaE>c2 fSK(X*+IyfXy$2Qa-(mhMyzmF)tlaK2Jed6hs^Azs literal 0 HcmV?d00001 diff --git a/Devices/__pycache__/Dummy_SMU.cpython-36.pyc b/Devices/__pycache__/Dummy_SMU.cpython-36.pyc index 39ce7e78f14b7317c4a6f825f656b284eea067a7..94d1231d8527cd2586b4f367076a489059bae796 100644 GIT binary patch delta 1067 zcmZ{i%WD%+6vlIt>11M(5;3jj)kclB)B38h;v&9~NLo;%)`E61jASgq&LrNMS_Hv; z!A&nNbRlj;T!{qtZgk_$jc~y~pk28WbfNdV6Pt%=Aiq1D^WF2f=X@=FD`~}eeAnah zm8JJuULl<6N^^KP5D2^t(0$_gRlCIAC>6D4J)9{nw|*$=qyW1fq9fwqA=_Q%s_D3E z7L^x#q}WUKQ&tLtQb;pG-6MDsW_zW8_yT;lFSm4Uk)#oy?g#SQE-6a2o<}y?{)2E8 z`u*jo>ifCoSZjt?x2^8nEi1N%jL7&C{a2sHKLjfWU_As(0#gEd*e|nFP$#u6{5)KD zyoLTm>xp-vGsoeMdJWH9sWyblrH2s}w`A#js7ZyHtM?kzHa$J&XUek&$?-&Q7J7`m zN@k+nZ}mA@CS;n$MvVzs+pGwy`;UGna<^`}i{{E|Z~Gggv%$G(q(2HV;X^g=QPZL) zU?KunH~)yAh;jiIDT>HZ@SxM=Dm9&2!{!Q3dWt1dxm;cd+gZ6%Gd;8Wz1e)~#ZI$k zzf(Kz0ehYPSvZXnu#x}5tG4NSecSdibG|AYIs!EcmVI=fN2qk6JHOAF?XN*{LeH{m z+2T?fsvIx|i~|FPK{0e1?{KF^8f$< delta 1074 zcmZ{izi-n(6vuOkV>_gQBGNPoX=5PJx|Fsw6jf?a8Gr@`S|BPS4A!B#V2G3Ge4*+N z3ky*yZ5c|wV>x?u>-pH zwER&w6jWxWQa>0A1Oo2@WLSKDTbI~drHJNRKb2=F2PQK_;^M4+oB*AsPQv|JG*gQ?PgptU16Kz!Wewzf1!RvY>A2abe?{Q>CT; zM9&95gyyHf9i=sjD=Rgj^2kXD0=s1CJX|Nj%+>W8aWORq{7hN)IG#PQ|f_fTFY=7O^mAf5y< z;^8&AS+~hFn2^Br?Z4s?Q5HL*2r<~7WMUyNEIf<}!JAxLuhcNj&vK2}-UBQmz;U&c zb7c)vyz?^Ha_ZH7gV0RxJ0t9U`d4la3V}iWA6<2^NBbsm>*!)swsjiRlk8AtXhtrf zC%?Oy&I($W&30iTQedS_{%#6<8Ndi&6tKsn5V&u$0Js2H0u%vP0n32yu=^9uzGq(C zSpm-tz)irdy@X&Z3H%VRrwHgsLDIr?%W|<(wXAJ9*|J4>cW2p`k<^uoLd-S#NKBeI eprH68Apk@pkeY`)w;>0G41!H|u&dDN(eZyN?a*2P diff --git a/Devices/__pycache__/Dummy_T_AMT.cpython-36.pyc b/Devices/__pycache__/Dummy_T_AMT.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..e99b14f3f5c918039eb21e656c58632c37f4ba62 GIT binary patch literal 2238 zcmb7FOK;mo5Z>iWlq}hj^G=+EX@V3g5(BZ57A=AzhK-~J8r6W+3n74@wQHFY$yJvt zyB2(D?Q4HXe@Cx9^t{)e`WJHQ%u-h6G&z*S&g{-C_nX%aHye%GdG}rTi%ZB~WZ`jO z{t8C%ClE$L!YH#+8dAfp(Bjt6X7(ASrN$Y$p=WfJp&wRZ=A^Z3DO}3xVcqO;(?+%& zE@vy@%Iup)*o1u^$Xj7+CR+`!m{YEXSAkd3YuWYiIwfxi^I7$ruqwZCW`{RnU1LkI zUg9@leT&~_^^qn2;LYKkGZL=xwc*_}LJ7ae*I46M8r}y4A_ecpU5`d?13YROE$t`uor6Q7rlNZ`e#a+U<_E27|VxTd$MBMDp!8KZ*+@ z)HgcuQ8I{CBJ!>f>3%FXE_T;4UKH_w?~9+bt<?G=HXZ!oN&)+q^i$}aT6p{yAxAyCTVzUQ#?8a~Ug$xpv1AW$u8hZ+F^`b_&RycE&;~?c{K{Tn4Lu zBl0G&cL&>F^?oS0EPB(;7aPwvo_*1KBg7!(!H!IhcyISa9f~|U7IKv2gNP-P_mz+* zy$s4h$fsFcC@!O|w_UL6*+##&V~i_$vp}L|eSD(*C`$4~MbQlmSrZ7M9<5QI)-BKS zsAo-^vrpD9w)Vk$^B~7z!j0CKVKvo?VWXY4CGkw{_ES;tHkH?4Wjrj&EnMCK@(4y@ z0U>nSxBxUCeHaZh%_v}4o$0FCA9RClkt-?EluL8^N8ql)r=P<351h%&B{($bl5ZS%B6B(AWcY$b)&WgH{*x)3_))ZAU(U6XhpJ z9s<#oC}N@?MGKZ1!;QIkxxR#9D$u76l_65w`NOVYDA$u;K|c7YV`gv9YNH z8ec7o)i@O@P4=}tDY$O^D}X+ND(%gM?7c6`fL)Z zQ9Kw(8?2OAD!QVe%|Qi7_}vrfp~zArS;2{$PcrCaRzB8_vDJgV3BM)jqt+AqLuidA zEyisVIc;@E+K~dR_Ky>F$Ygx1>)oh7?;vm%FF=bCN`)AID6rapS=QCpnB(Fx5Xbf{ ZCeUcq`AeUgV$SH>$a!oD?atW}=HHRGQkV`I^f58Zl{Jv_ELykrULZa*O z)z?*DSAFk#oXG=v_&-oErKT{IX-&1J%37;wz830CKT*|m z#0brrCC4(MUCW|nG@aH|ZK{>4T%(n`)LTkDV2^7|V9cfS}epD48zeyI1gG8-d@5N}n~Ob{yUR%`ck=9`}1^7*Hz0tnPo- zXp}WEvlTSD++X+F2VN|J;%db^2pV1zMD1!6HFrGzXo&5!{5bX+{!Vlx^d#9YtHRk0 znm!A>X4I(D+{2%a#uGg877Aa%-YPYX%`$_T->Ws9SuFFtQZty%vZzhwuqo6Q%dtG_ z3@fl{)HW-!8Pr*(>_)fMI(DmWJ!&UBYBqf?jP>WwKK>ycDOF47Bb7QGH2tV1 zg4XfGPf-L)UvZVb)>lFt@h3`OXUcw--===3_S2)ssH-tO(L*r6i88M+V-fF#+Esen zVhTtyE5fLFEkF1`Rg^OgQ=g%slrzEz+Pjgkx*g^vzOZ9Ic@YKeL}=}fu(;P|QA-$9 zi>btKb$sq6IEb<#jM#7P3ZoP8L>O(%%?p_E?-jm?>W9|)+N(c|eICE+J^t-SzxC+h zPhNcyMUAFk+Tg)~|LVnYvKO`8!-($(?S{(&?$?uuAHQlvoJIV8%Zn4AyPrL)GXLP= z^z5XtguOK31P0Z)aRSe{2wHu(&- zXQ`N@qDaL&inr`k1~gQ`K(~PsONLjM)!y>u#1i5JrK5AP@H%ln=usL>FOo>IzlY#JWDgt_f^Uc2@lDy>k7wp zTM_FvshxA(@48+y9pTIL!U`3uR9vHi*q)cDApRR8j5Giz`QSDRVv3k_gRMGQTgTs^ zM~>A29{uS1pWun$0fsx|dRY2N;j>$OE)hL*)dSpj8Lp38922 zCTLw^Ix}E9U_qD#n_#9E6gERzPS^}-F<~?EtRQX1G%KQZ*bJLRJ;mnOJn9^~!WK~H z*;TfPx*&I9S>(p8N^iQf9bpR+|0r=v_*Eu2il|&CDnCV$fQ^}YN(`+gWKz^nKT)Lj zKqkasRGLgjX-T%v0LPZrn#9c@aa+JVThfS>v+@8%W?;yK+2ODol08Hg+8lZunq6e~ zBy+k^o6JZHMkQ74qFCoN0=*61-Sz5zdBElOvBuc3NIoL=`vi~VBSp)p1*moWNx5Gc z-;f7CuGWb&NqvMWeHU8rphjN^g<^2h7n0FMwz}>f1~7n!!uYNmByUqQ5R)JwYdw&6 zNA1l{l0*m->WrjRo=XW0q9h*`0hE4p~f`g9Khjrq&{f-I)kZzyCTg9Gsng) z$z{YvnM$=+*a%{82d+TM8bW(s+3Mvt+v70(`PLSU45?A!3|I)Bq0phl(la8R^OkMM z`*JQfeXk0{0pAeMq`Z@Z?U~q$iC0D&L7LdaBM<8V6clX)Q)dmnN zEcS_@c%&{3TcTbm|Ij{6P5FLt|SY6J-mw6;)yn|Tvg<{ zOJRW^G(t1Tm5p$Dw9X1fX$JZ_G-4mH!E^w7!Vhyr+^Lr&^ogp83W zq#o?{U`WiReaONt_v1ZRPHM=~jGMmSk*cvhQ*2;}rxwFn1n9{%yXW~;rq31LVTNL~6bn1*V4q!j!m8g%+0_-hFYH08T7 zh(6*L!qMKqA&8#FF7hp-FK0ptE@k>Dct^s`#0oQnhuWrkG?opWk@3(R)8`s7bA zM5jD-CQFymIX>%%Ub1T=M&alnq>vU3G?H3_tw75}3rBl`V|Hz{vjg=<3QOZZW!h^s zagr%wET*rJBBKz5FwK2s4*n$G%MBqiP|2+jEdPeeIU}DT<$?nL8MP&VELy5h$kXIs zq7b=}QXysF6Z{K;+vK!J(WMLluTw!H&&jq9T^0$rAc?<+@l!m~n^hol4rGla(9mun zBE4i!hq`ODowSuB>(fVvA~Z_PoT{)ENL3&gaCB5lv_#q-vK1T3w@;-eH~J)J$OkY) zam#Q@HpQPm zsM(Qr7pha9;u%mY@?N|jdk4N3Q}#ueNFy|$*_(cw|1OPZgE&eRF5f_(uTep+sxS|E z09{A{$4JI@g$lh(^O3w>5HyG^23|_%=+}5F%>igR=(c%%4O*~wbz;ZQu}8a2p(T+m zh3!8fNVMBvGuT)#7ws^)I_LJ-WaI>;+DZ>Ca-6})!@i9a()nmMb%)66(>&}R%bZF! zAN1dsedy9KhgI`<3KTA!>d=LngiQlhMCdSt%^(GpW%+@&oE=zy*mx;cWZ~z-E37~^ z9B(d2C|UcffKCsfDefYmi+~mcD5v5b%7!=1+BSpB~=8H%@`#61yV5UJqyRox?BH!>Xf~2Ro8wwDt1G#Hv-;`fZ>kN0zoY*zVxAROr;lzY!Q1Um1PleMHZeD1P(I-PrtUv%2Yi^JQh@`8PfLUgCTF`R;C)qL9x$?tw{u^Dlg@ z^i1X`--{7;k20cT3EeZ&*)R>#smpXcMuG6zG=ORDOh@iCV7@S*6`R8Hk2*+bNH6A$ z%nKB(b?66;z2pb=zKpdm1V;@Z;Wz(;@1p7zDO)zk%a-o`L3s^skU_ecA7L1I6RRFI zyREj+Ib3QhPJ;UWF>*kXyc{9I;sq3Z!R>9=_ac7g;6-7j+7$XX^-d21mhACg&=7+D zRtrS;4A`u-!CZ1kuGuR9T#_#HfFL=AMCi@Fgh4GKjqGFWL-@_*wO(O;=p>i!#^p7c zXB^J@=U9+`k7BT+vMqG1a0VSs=7GrLBQ*XO==PkigBaPXpy#jkW?rT-K#9C}xj-+! z4QqiceCp_8j$hIoe}+Oh>2Ae16n+8#1`la)vHlYthb}~1@SHG{Ai>20nKfZ1*(S;ih7H z^BXapp2ImwrbHCh`evoN`BG@Wu|Dr6Nz~?)tP|!Y(oe@i-Q?tY@t;!hgo-W-VKjr? zL}U~AAaUTgld_e@RF3?@ba}*T^18-NyN-+v)3%Hm(q@t9$4wG=D@DBEitqSQXB!s~crf)f{F~Q0w6b*JWgY|fOlhuT zP#cK}&*v&#lfj>%9vlQot-g=LoRy&b}np`_=)SFuBf^e`TSQlQex!@VCtb%ul} z;H1Nia7t!Na$%l4k=GPD*kJ*gkzsnthF3Jp=1wVB0ZT2hXZwgLPq2bJ2mHBk{sj3B z8j(HtLhy;mw#*UMYpZV|2QnOm&yy^ND|lAfd}=e<0uon?y-!Q)GGhs~k7P5kO#*o+ zk#Llt3rA1_4mE20KxSo2_z9y5_0yZapsiEAPx<&Wn~J9>pujVbL&!M1W!4GKq7-Ny zH%V56btG$ua05vb*b5V~67gP^)ULd~BUp;7Mq+Ag$)0`Z>DrxVYj?JLjtrGcO@Ehs zdc7Id2j+?QupIw66%=$-pm_g*I{%2Gd~Ga_A}4;#Y1cbVWKISOBolXcXpi{Ug!H#4 zpnWI^&wJe@+HIu~I8089Qa-WBZ=l+e*%wNJm`C1G5R17Op?b>;Bkrbw0*zTszd^~F zTtY_DZF?3*0DAN6Fxpur49dZh&I2mG*1wtCh^Hb{U)#oqx zuh1%I2A4Zh*!e%x8~;QFg|otJ9dES~kx9}U=Zlj6H^4yvFvz|b24cMeB0d`>@-5ne zx`=qhA*lw8w1R^Y*_TUYR4OLoF1qQOzHouT-zA#ltqduCsR2`!M;RbaX@^w#NnJ=9 zP^u+P6pm3XW7Z{ty@%psJTZZ6%}|{j?rCg$%D!Rea9@+d|67Sg_eCua%G^c%fNXF1 z23;KCSm8S0M&)M!2?tp;`haGzBlK?Ui0ji(%8%8LIRjous^ z-GL3m@y8Da4VU>7nRrmBS$T>v7571OlWaxQE^O_V_srTC6MWulADvVlEKY)BmV8Lb>Rw_xPJ)1(E7YqnhAxQmu8 z;4UO>IJ7{V07rukoTI}w@DA`c9CYCvXoumg!=U>Yh5&0ZvIj3#`q{3)ir`M}`o zU?WsGhG{|@Srm0r%Q`R<<{}~t52L`#F<=ZT=|a*oI!5;nB%aaHUc~a0c2LL^)4CV*nHMY)0T{CoRUMc4yNhQe3a4h2j$tskAIYmF8Tp z=4lZgM@Ky4#YFAX8*3aeFLs}B zd8o?iZeEI{6nFDTDlWpWp1(@?(Sz*?o*$(#m%Gad-J721P8g;|s=}~`jwN{q)@3Wq zV{2BIRqGA=xowR3nL$sQ2Fo@oHGac@Rp0sF^C*$N+UNdK!agPJV|F}NJi!xVP?xCZ zUyQ!sY9@;QVk2OJyyK&b4+BCiR&c4;1m5%t{l;Y>RaB@OxGOCb;8eF88CJR_5sp}P~HZt`0B8gd-b=EZs$bvZxQ&A!> z@;J&o}nAI#|y?`Z?WMPQZB)^Xqq-Q$8)fmiKa8qn3(Cl)u)5fms1|6 z<23g1Qy8a{S>0v)=qO6FXfNYFmeYT8|4dLm+D*8^gKGVyKp*~#Z+%tLn<_14{GM*9 zRAn4dGQFeeGH?VHMt9_Xc}(q1yo*cIr7pOl-MR@~W=fYRBF0h71AIeLAi(ahYNG+v zTca-ZC~ARWsC!|Um&q(6-3!AXXHiz4h*i4%8j0&9Zjx9h@g524YfFN^$Z&K|2If z9^oHJIC6GgJbxJUv_s8SP&d?Vg@1{NyQ!MV(7a^sMLRB)SVOxSxc`7lcOKQqe@SJR Q1h+?j63&f}H(I3q1L2VYaR2}S literal 0 HcmV?d00001 diff --git a/Devices/__pycache__/Dummy_lockin.cpython-36.pyc b/Devices/__pycache__/Dummy_lockin.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..5beda1a38245bfe63ab24c8ff95ce1f56ab291be GIT binary patch literal 2139 zcmah~UvC^W5V!Z=?Hx&agr-#`3M)~NR?vzzK}e^n3QACgpx27FJS-=zHf!Hylil@U z`x3IfK3yN__ka(;m)TdI_zHbu#+zKSfe1?;+w1X+XXfYMZmzEOetY}v&%e8b{6#J_ z2j<(*RRn?&MspH1BaJAmt=uZ?$fo2mVK#H#5aukb$bps1JXm?$S=f=wJ=Qt2^-&uV~4G>H5h&4tzWO~&WhqB&P95dmBV+= zkSC);NG9anB2k*l_?ss$7(cqdIUeouqb%iWbTL==rYE%@$61-_IPQU4wSw_bkM^if zS1gZK8!i1a-4voX4M*%It9XCR9^{EuKLkJWgsDL9^WZ`@0?bALp~H1KgB_#L)T$Sp zwt?h&Cd#r zm+<%y7ITX5(3H^w1Ca$qK%9nWReuIAO{uR4uoYLZmneu;G_f|Z12E2lpGpBxDVD(w zrU@*Ak{=uB3`~Y#_5W%7m!->jtZsgH4op*uLGbEzP^i#039j9#gksg+SjZyLp=I(N z`Db^zocWs#-T^^vxZi>VDue@ZJ*!WvJFOyIai|3o0B zqPPMSoq{K7Ciy3DzQF>muD7mns?c(~#4^%_dGT~;g1*9wo906C&o1~_Z}~9WEsy~z zH-QB(fxylHW;5#+P$*I$bKe398}Ht*`g|w?HRWkG&e8x7KF%hyhFOB-D9Q3<4@d#( z6}n_P#bA*oYb)Lz3fmr>D0ILL95@(@la!@zW%RySIyIajiW5_N`Q7ug7@Fple9 z92bJka-6Tk@h`I^Z+7H0yavKT;yafcC~ly@XM5o;$fFVoLgK?l{xJ0BTfMID`90tE zZL^cY&axo@wxKI@u>3ROKM8SX*Y=PfSDV3epc$6P4Bs_g%M<{qt_iUarAYUsfHwq% zN%KUhA>{Km*pwfk_!vapiDM?xIR4K&2cU5mx=Ei;9V+n)Ca6f@R}x3g&eJDbVXt}dEIKl z_Y&$}pVC+ULn86Q58zku5B$m#f1yvztaITU8o^qg-SLiRcE0&M{(5n-OJMxU9{l)6 zi;zE2nGFvXTX5?T1SgzkB&=r|QqY~u$=%SUP*RVIQ|_nbvfDZsB5Sn)2VxcX~Ayfa2F)( zLWbZ|7|F_?jCpKC)k_N_c2#UrSwtqyMYWWtMRXV+h)fi_ zW)I)cA{$lSSSnL_1^84xIQxyLMVQ{)zdP7|riIem<;Lf?H*eqiV*8%Z`$mqpbE&vg zpXISOLPcLae9Fbat&JG=57I>F?RhkAj9*ki6s1LKq6itH7x2W?r(GJ*1;?l5TEl*3 zn?Qbc;EcD$4Nr*p01~u(z`hqT*Uan*HkU8Md>JGs5+gY5ScAG6U3NMKN|=c%24@fY zR0ZH1COoCZpcYG@Ry_rgo`T;*7TUyxc^95)2L$oTtBnNCTPD^D3~@#h*hJog#grle z4CVCLlHi0QlTXug%HP>DTg(d*Y~;muVhy>-Ybmo-M_Iy?n)T-FGd(RcoxESd~ALDvwRyv3Q5U>@4SzU#}Sr z3~Cbk3IM2)4y5HfJz8FC6ybuG-cBkkiv76g&_=(21D+g<;v)=eD@x1dMo8Rk_;C(Ra*9e8Z&0MBFG>>lV}W(RV9A%C`H;=fZ%) zs%27>iN2?>R8^q&pJcdRh@zh+aaQlBOJJ*z77F`_T19ag#X5?sD6k373G6}& z8HG(sqZT;bPT&XKz{Sh%CGhxcAP=_S))?pPSBF12q@P>4LvdJcuqV*Xc&t)vA9y-j z2kfYIrnxL+vZo|`s!*RKi?tpCtnY(Q^#KY7qH0GGmq`@O9oU}Vp87+uxCysKF`yn* z_ylpvWB4aUp?CMu!%aZW!^W-FtV08TQmBBfpUus_WaEOJ&_*qTT@Bh>psIp9HS}L1 R0cyPPf8#BE(rX2<`WuO=6x9F# literal 0 HcmV?d00001 diff --git a/Devices/__pycache__/Dummy_monochromator.cpython-35.pyc b/Devices/__pycache__/Dummy_monochromator.cpython-35.pyc new file mode 100644 index 0000000000000000000000000000000000000000..d214fbdbcc765467ebeec162023b4248f7e2315e GIT binary patch literal 4274 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 A&;S4c literal 0 HcmV?d00001 diff --git a/Devices/__pycache__/Dummy_monochromator.cpython-37.pyc b/Devices/__pycache__/Dummy_monochromator.cpython-37.pyc new file mode 100644 index 0000000000000000000000000000000000000000..4976ff9b41283f0b6726488205c0d99de766d99c GIT binary patch literal 3876 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 R)0LW0tDe4o&T8Y1^*82G!+`(* literal 0 HcmV?d00001 diff --git a/Devices/__pycache__/HF2LI.cpython-36.pyc b/Devices/__pycache__/HF2LI.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..93cea541a4c0e965964e0b05bc35ea049d6a3c48 GIT binary patch literal 2827 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 literal 0 HcmV?d00001 diff --git a/Devices/__pycache__/SR830.cpython-36.pyc b/Devices/__pycache__/SR830.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..268f43e2973b9c2d6fd0024341729013a14716e4 GIT binary patch literal 4236 zcma)9TXPf16`rd!8cVh?2Af^joh3_Ul?4hzE|jyi=1xe0aIgu9%9~-VZi^9;M)b67 ztk_kl5~<1ymB&=_5BLFj$Xk9x9{V*f@nc>|s>pYG#_|PkRc5O1-KWp}oW546_#wg(Yb+=4pDJ~&}vxh6J|5#wF%^0R(Ndyxgzt>y6jWtvk9yeSis7x zf?knLvMH>2tjbQHEwO2K60Of>*eSFV>@=H2TV`|Y4B86&h<%K9lAUGe&`vQ6sD7s0 z<;T~rU3ur-UwUwBVPXDCy-Dn&&qXPt+(t?Njw&{`jnLS(wv9Fj{mR$|6JM0X=QM7c z+qPzawQasAijK6}_O{7vv<`}kQXr<{p903EH$Gq$aneIuLQCTbv}LrF-gP{_T>|9$)Ke>_}UTHL*gQO#FQKM_)SYtev-NOD!^Mz#$BEaq$&uRHnCJ-sbEs5YTT1t+5+0a;xtlIk9nF5M3X;^deDBMDoLOBLXppkDX>`i8gi%)lr(tV z^)s-#e{yXQcfcC(u9@^w8TG*16D=e3o6!dE@Lo$kuer)ed1qbt zbgR>Y{KaYNTB7^b5cBB1dv<$a^$Dm;SBKYraP|9F=YO<%KS^309^4V}247v;lFt(W zO~i}1*9uuIcvB`~YqgsQmWWH;D24pOn-5nQ-6@Newmj1@E&PuDsYkz$dC&B1&#aoB?o;iX!^sTj9>Ata!0?e8lg(;- zT|%d&?e*0Ur3uS3N7fzVn=f`uSccKI80_?bD>o7%+NxRNGL9*;ulb%; zWN_kpsHl??Y{M*o=&Ctq4bNo69-|0NqL)5u$!BT0363;G6*QaM=IK2O;fan9leS)nEC}{zeVL6cXa5^I>Q~u1Lb_xA4ImA~gV{){BK+MR5*ii3S(iZ2X z1A$pNfpD-RBFC-|-GN-cbbUCrE|PB0Nt#etqN`TOu>TB@;htbmjEk=^5Q{Y9rc%WH znw^QKqNxjI0+WTM7e%y5oulLhIC&E#^-vksbVlHbj6iMC8GVN$3Q6k_X+#*bFpDK; zC5X%-3COm$At{OhAkD1T96=0K4k;Tj?r`200cL8hMq3ar;;!DZ8O2iEqcPd{0X;j9 z;ZIR=JtG73$*6L%9Q0{pi4e+Tw5u5GO9T<0_zvW(eeKBABE#p%{olp$U(o#o&@hM9 zgX%y|Z+t810GYOC7E+x2Rs==J{m`8x(_D1foyQUW1vagqq&mD4$x{ae^})^GLhlE6 zf9LiUmdY(_YkHoWF8$e*T?# zv1Th*4*DG~2qS1mzZ1)vnJLC$8K`2HN9jQDRqWi8LnUKIzA_d9F>CBof2px)o02jh z%5Kb$jX8K2GD&8^zW}>gW5>p-JJ#WX&O(aPV|JVJoFa3`$L5|_k3f$*{A=(;$M7_e z&v`(3LkoEavVnlq9TR#!Gw5I~N;wN?4SNyX5RGVOzNs$~obCES{w*MNiMm0LZ|bMm@WyDzDoH2*zGU0#AnXzh;O^j7@4g-i2i7>=ZL6y`3 z{WIhbEcjvK$0E#<^f9Lo*zAQ2n|$hFz0_29uzTM`XEVe;$auymk+mG_B#t^J z%8$1Q@f2YCbaU>NCbE0=k+Ch%+SnEX45YS8_-j}Lv(7-P>HWgoH+Ft|p+5Ywo&;&1 zH{k6VLWucM79?nG-yvLIe)$&MiC!&6Jh6Vu?)&jJKyoRD#Ez%LJHvXtsD z3|KvkSwFETs7U~X8;E6xLstl2r0dU>l6>EBIlhM21!eNL)E?l>7LG{2p`h$^dnS=ZWlbyEBYnB=-Hlw z-}Wk0YrLD-GX6-9ZlI(T;>K@MdW}-Jv6Wrto5KsiGSYB{w-9+CqC-oM{E*g}pxKGi zlw$R707ZmUJwv66VaSqZ7#@prgdLkGn%k5P3z{Jwb|WP7VgB^lU93$BGCXmgmfhZ< z+uzdOM0`#YMTu+&!qcMH*$tk<91nq{kF;G=tO0A>kVkMh9C9!jZ;xT7^1jMM_qBy> XDLoq~DW|`TY{#)DUsPQW+3NoQ?CkDd literal 0 HcmV?d00001 diff --git a/Devices/__pycache__/TMC300.cpython-35.pyc b/Devices/__pycache__/TMC300.cpython-35.pyc new file mode 100644 index 0000000000000000000000000000000000000000..dea3e62dadee5aef1eee89133f585efdbbc6e0b5 GIT binary patch literal 1842 zcmbtUOK%)S5U!q?eR`dkyv!?P#R0KG*pAIhQ3MVfg+s^^#^stboZ{~ z#he@t`~>&|h`-cVPW}s=_^M}ZITkls)73TA)m>lLYjt_KeOG<|9x-QhXy@i+qu*_8q2-%<&&6&=c5K@ z8&iJ^B5|8w{D6uJl8ORpK(8vuHCmt&4m-fK7~v4|HYH1r2W&RpvGo_v`db?tgZZ_u zWnuQCyjuz40JeDW>SOjXHOx{W0;0nfsm+D-KoErdPUF!5w!MoV z=h5>W_>w9e_zxVe+35uX8w{dc&L8z3tUp)fSOM9SG?Tjf;ma>R>#Z-)uJ1g4y528~ zQ97Qg$fRXaX|3yHZDhU{kH-J^ipZ#5!v<+FDs6o_Ng^X{ZE_3%!y9|kG)uzDR+yKG z%!ci(j3AO`IvKXpA~k80rCJVyNu&;Yfep0GMmCs~%GjX56BUQ|FH%cb{^|E0?Eavo z(!2AmPd7i=y#M*`W2p~KIoZuim6Yn^Ji^0N_~_Y-M9%JSjj=yVW2twmxUEf&Z8Hqn za2WEHdIf|;ODv0ySn-#!w#EGVLi=3P)(54^qb%l7^Wc?dA5*^#Vi0OU2<3@OUZf97 zpbC~ZI1|w5`Kp0s_y%4mV-p@lGcd*2?AzK1UNKN;Lc!Keq{h*izUioRx*an!&SXF)pRT;Qm@fMOAg^2!8mEbA)6`bb4HWuYQ|7cKl@$xtw zrExcYEq9|?lxERhCcC3jb${OYMFp^C>xnejnBRV`&?36uJ?)y3Pnxut$~9XvsmbL0 zR+R~_U)c*3qqj}HUmjICjH`1bHK!eXR7Ir9sw>{Ht+YU{jiOlIg|cQWB<_j%%EC(* z+b9qu9cObG+IGmLFwL0ngyB!qD64kVGR~`;OxW-09VVQkmk6hT@jhz^^Il#g2x~N1{i@12OutH0TL+;48fX=ek&P@K*9*(m#ec?OlWaxQE^O_ zv8j=Xk)e5vOKNd;Nq#|0Zhlcpevxi&VsS}oQM{vXNJ?s%p>aA;zAQ62wKyg|J~J<~ aBtBlRpz;=nO>TZlX-=vg$oOI)W&i;2?Ijuj literal 0 HcmV?d00001 diff --git a/Devices/__pycache__/__init__.cpython-36.pyc b/Devices/__pycache__/__init__.cpython-36.pyc index 92b027d41b805d9612f487f02d86054b7e161dc6..4998efa9a6cac442f7b7e8843e0dde9ff021a784 100644 GIT binary patch delta 100 zcmbQqxQ&s+n3tEU#??1^B8PcEva?l8XmM&$aZHu5sga41p?QpZetvpRs)9>VW?5=X zKxIireqMZeeo=O2UV3~=W>IQ#Nq$jfOm2QrN`8@UZenpsYEitSZ%9gNnW1so#3FS7 Db;2Vj delta 71 zcmdnSIFpgXn3tD}i@h~!B8R!Lo3m9+XmM&$aZHtCUSduqkm;C`o0%8ml3JWyl3x&$ Zn_rZYU!g`kf=Ruu(IEOUh=2h`Aj1KOi&=m~3PUi1CZpdI zQ)O&wWMX7!9^;Z)oL!P%5R;oF4FJp(0` literal 0 HcmV?d00001 diff --git a/Devices/__pycache__/device_manager.cpython-35.pyc b/Devices/__pycache__/device_manager.cpython-35.pyc new file mode 100644 index 0000000000000000000000000000000000000000..f14002c7ef9ea6c66a2629af2bd2d2317c195199 GIT binary patch literal 15190 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 Yh zoZ^wv-Z|=4@>1+3nAWSD$^!)DiOrjY$FlxA2C{{|!`K-HB4TbUT{c+dcM&8P1+z81 z7#NS?rjz>jQR^Gr91bEX4<3yBSy_9peaLp;7)-zRBs8w>{+JtYp~|OQhGHhS%r!j0 zXJ5Q!2$y8P#HTWJAH!=!{o%Qc?SZ>f*hf`?lOT~YW~|nLi^`sgrA)aVV2XKbK^Qgf z6?{^526ll|=D_HedL0x!rmz<_mt;)|cEsiq=z?s6eX+SDZ%W{~+FSy45r;|Nz)%;N zqDKVU80L)xexm>#k$cDNC6kuDQ>x8?paxjnJ+XkATn~(qIJjAWONeO=-q#YLlJ}r- zu^ra~U?@W;z96)Yv|g-#PY(!;H;1v4(XH5(Jx%h1>ijWY*T zj)^{Y%vtEPMObud^AL6LGYB??936-fC)ep^Bc#JUH*wJy6IGP=Uva6d5#lED})a+M_y4&EOn2 z8tEJ0Cmf%zt?ZL_dOfbMLP{T0RD7=CkxRH_Fmf5g0-0yacK3$!nT8unn7!MMo4pDz zI$H6QG_f>qZMwkybd2${gla0+9f_&N{DH}msxsM=i1a4QnBc%N7kZHfAbRgGi+hiT zA^l(8Asgk*$rcT}UIUxCv9ONIoW9=-;HZFU)YIF&AcYm)vNQMda=bp1z9h?L%6s@z z77<6QQL!4|!mL)KQnmU3Dt=s2s8(ISUaiWnHS{SELCc1Ho3Zy8$nM4(DzUnXRY-rE zNvc!&M+|tVYwlV7V+KEA@KXl8!@mb;s!@mW$lm7FlG35l7`_Kf#nM})U8T9w{?eY( z-qLR594qBZ1JB_<*AxVDJ`$BWzg+ z@nae8EKwV|;F^q}vKyG-uMwxsy%=-#MK04+cmq_5dOv71cWeHI4YSOxjKjbq*QRcp zAA9LKd!A?TSq5?+l{Z@Tn+zry@J;m35JdL975uis_d~gf31vVF#kU|>G<4Z`4z6%x l1?LffW>)+qPXt%wQ_A7$6uw1RK6Y7CnWk(^b diff --git a/Devices/__pycache__/device_manager.cpython-37.pyc b/Devices/__pycache__/device_manager.cpython-37.pyc new file mode 100644 index 0000000000000000000000000000000000000000..c56d3fb2fe94bd194eff534f7bd25bee76be3bfe GIT binary patch literal 13590 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 kFCj2j9JuEmnI2dkA2097MF$U1rUGN?qg8%>k literal 0 HcmV?d00001 diff --git a/Devices/__pycache__/lakeshore336.cpython-36.pyc b/Devices/__pycache__/lakeshore336.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..977eee36c29ee93046fbc17fe39b286218f890e2 GIT binary patch literal 3580 zcmcIm&2JmW72j_zSENkIvSr6g!ld~qQ(1^4CoNJpwrZ)i(3lY-1qHT37c0()UUJE0 zXNOWl=u*hn`~mHu_oA1gw_bATwTGVPR5b0K@TO@z zHki+f&ka`O%jb4@3-f@@VLr!iVSbyx#!78V{GC@?cg~G)g|D<;KQ~N+zrj~n`D-(L z6PPMtn6+=z=KD3Mwbf;UpH`o)RS$T2oC#KauezP^W>(!wvu>W<{o8NTlUVS<1(=Px zau3>D&1TI~3qMPmeZe2a-IF-iNVQy#Pm*RVldOA?W$95YHpbjRhv#|R>7HXRoDXhB3oqF(Fe?i_BT}Nv$)OkXPMxen;$FZ(dSR^y(mv`RUqpP ziU9hk_t74q<-Y<*j58xL&a5+|1>fH{&TMA1i{c%cx6Csg6Vf_EyfB;2OK1JsgNzLh z=bWTnj66LmNFU?kxuGM;!0G(Da_Vu1C&oVwF}&CYk_i-?s2BPevd&&kTS|+{3K~`p#85Rtfdw`-1aazu=BdYO>Q>Eg8FCRY8UA>Ky!V)xM} zhhOGg9p2;M!QSLAIlhdTQE}^B4?0rf&sV z-mKRwaSM3mHqtETHB-Eanbv6tzDLt{0qFYV7Jy+67BB6pB`)t7y57adYvi4%*AJb+|WE2T7rg;HHGwaElWrZu`8XV7SWuc1Q_eAK&WlO%OU zwl}2NLZpqN%x*anzL7{pE%;{HU^bdFjKUe5?rcs*(dpHjzyBS5e|<10kO`{ms{?D| z*b#1HwN?czv1<-A_ZKzc$#gt zLH-OeiQGIf1>SCn%Z40n-!{ky%Lc~o)Y#MG0%-ooKrr36;U{Cy*sJ($WXF4H69-c! z_V#wSVPQep>xH>0uA#pmpHJIdGr{DZxC@F{BS6{g<;@bWV>&jqjWw^@+M{P+cY6zL zBVz~V$L1;-`}(E(G~~Nv?Ma&ZH>9U5fJBGNZr!%6_`DJ;toBZs5L*L^f!moF}`FpNvuKtDDpGsmc24htm{HYVIq2 zK6bZz5~qEB)!{`r@(HJ^;0gJ>3Sj8hbrEn0uk5AXqS@f))kqjFP_qTyPca8jug%fR zO@%=esUV6v8SAGsE=AF=`*Av?h<8YU-cvzOlAt^;DD{bT0+av+UHqw2zfDv93>Pt? zq9d;W_-3%^+rHzw_zQkP5Ve6e`RM9AMAPh(f|5!}Du``m*ZJvSwYo3kE<-7mb5^B8 zR#6vM&3d zR>_q+?7Ysfe`|{2C~mZQm*s1tK_uI8vngzxQqb*2N!dmf$c3P%M$lucEZG*iMs>UW z4(c_l+tZ<_kV>eiA61*#QHwXE_KMb88%|jVZRLmzhxShs`3yDfsVW^rjjSt0mZn_b y-QYQ>WS}(5db8}R{QJY``jlLpQ%T|2zJ-X;f5ockV$nB!t7KPpDjr7o1M|QA>?30U literal 0 HcmV?d00001 diff --git a/Devices/__pycache__/serial_ports.cpython-35.pyc b/Devices/__pycache__/serial_ports.cpython-35.pyc new file mode 100644 index 0000000000000000000000000000000000000000..47182351ecc022ab08d230df2e49741067800dd0 GIT binary patch literal 1292 zcmZ`%&2Aev5FT<@YiYAWVj-}BAVBxviws!#Yf->3j2gBN0csnlnx<7`EEp{5R zBqdvde8|tC*FHi|MW3T@!D~;x^Z|0}a3vu^fpT{S#WA2Y?LcPNF^p5yLdT~J3lMA91szyJ&g49c_^OtoB5m=<%I zGC2y5kZu9Q#fo*srPe>JNCGEODHtCHKBQ~z>Aaq<*i=iKUzawurA-6|y9?d(1A#uk zErFY#V1^x?pTaGL$u^h(25T^*rH=|gZq}!Ne4lBfV{^%4HfCAQ-sU{kTxBee3#p6_ z!x+!5VwvVTJ{IFlNipQY94jT&ZmD$^XCfX6Jt~|NPh+X@c_rkvh>M(=3#o>B5s90T z68ekhL7Za^vAp=d3S4;WU1y?)lPr(hvjEAhnchN%SR#t&j~t?DId1F^*KHG9-TaNh zy8yeT);l~qxpP69#CqB&6w%gGOR_&fRZ;x;<>7Ak6}GRt(-+@7|N8l}Z@WiaUm00+ zhf<|d?F^YVTqOr5Kc;;A>_s2-DCjPC#ow3R#)qZ=FaCVgyty8Z;SJuznT=3uA8cBbdsHb|Lrs&J`lZBHM* zS{cC-cjX$k{;3qaS;w{~+E$l38*0OF%Ga5>v=zlfpIg75%eVHye@AVDd51P=m+N6G zy)siCw^RqXt$Nit-aNRsB74U>e8%%xOG zq8^}9J#=itu^5@N6>P|p#8wKG3A0j*%dE5aMTg9fJOvAFWQR8zduhy A9RL6T literal 0 HcmV?d00001 diff --git a/Devices/__pycache__/serial_ports.cpython-36.pyc b/Devices/__pycache__/serial_ports.cpython-36.pyc index 8bd5872d020b586c89095694b63b1d9ffdf5633c..8a5df5993e9ce7fab3b0c13b366b141b7bcb98bf 100644 GIT binary patch delta 102 zcmZ3)xu286n3tEU#??1^BS$(@K)SP4OlWaxQE^O_v8j=Xk)e5vdwza;PO5@SQD#|c zOh9ExMt)v=d45rLW?p)HN@h`Na!GzsWlU~MQ%t+ zwgmamD%T$J59Cu(Apatl9P$fz&B=curwmszVif2A^X+mToSARPy` z)h9@{7XIiIn_m|HF~qiPI1BmGsgI!xdjj@OWnZrBiH&$V#ed-*I>E=kPEZT&JvbpF zYUA0`gg{I%31-PD!f1kL%ie;~bM#t<$fh=ab!nd*rurQQKSqCm{ddsk$krx!iuolV z;e*fPHJ>-ZK*GxSpU1hehS~!`*^uQ0dszr-gw9z(OQo$zQwp-CS#E@(+j5v|CHq3! zZLO7FtGqs9N78{bgVHAnPL+n^p;SkbmIbr>O83p;CSeC!ny;Cgw16I{+W)@}{Piw4 z%4HYB0=)P~5QFZ5n(qO6XoB|YCz!*2Ch!!l!8Wd;x8!vQQxX?!?L0a2$qnoRV>Osa z^R~nJzep!w{`_cbt@8}VHl6XkAMf73d*|m)OPE8e%1&QtuJmf387p+Q`Q$e)hIj5U z@DKB@Fr7JBSw&W}9DPpL{Wx9KFE_6G_U65g7o;xY)(h2Th1qC+4L*aVAr}?792~p2 zJL2oBqp1OeB!R+7klaY{7aCSY_5c6? literal 0 HcmV?d00001 diff --git a/Devices/__pycache__/visa_ports.cpython-35.pyc b/Devices/__pycache__/visa_ports.cpython-35.pyc new file mode 100644 index 0000000000000000000000000000000000000000..4fe3ffb6f1b3745fc9c4468696fea87d52192ccc GIT binary patch literal 583 zcmYjN!A`60zi17pm(F@izO}jy~W$R8Mk>J7T5BM2= zhkwzdp8N$)&J+|UnU|TjFYoQ{`qGj$K5Jfo0>BsbUrCxysBxRX2j~zA@FfHSIy7X! z&%!J5lF4>4H&`WMqr6oQbRTu;<2e|*MvcCSg_g(2#kSn1)L18o0qz9606hH$km;C6 z$z|P+8rT(ZFToshGR;$-l3Si2BQAE%P`9aZji8%)-4tfxHwh05(0&KQ%A!nU zc_$H3x}`8VKRvRqP#LsGm4m(gz1>5*q3}M|ksWH|Yf}ncjFoZf%}ZYmcPm{wAND+j z_OOSp6KNA;IeMH})u4y5r^BeW$hYCZJd8H~sj7kY+yHCkxioCMy>T z^}u+l>Gs{OGM#0%b<0my>bkSiiDO@THFvmD1A i#JAcwj>!|1^8c*(_EW#bYN-7|plbZ*m?c<{1^EM{=z?Sb literal 0 HcmV?d00001 diff --git a/Devices/__pycache__/visa_ports.cpython-36.pyc b/Devices/__pycache__/visa_ports.cpython-36.pyc index 98abc4e375fcc0172a72321c0ab7f02e0d131e2b..98705526e3a24ac7b0f7a5fd328397ff819e094c 100644 GIT binary patch delta 102 zcmZ3=a)^b)n3tEU#??1^BFEE!RA;N0(Bjmh;+QI9QzH{2L-QE-{QUHsR0Wr!%(B#& zfXb4L{Ji+`{G#m4y!7~#%%arflKi5|nB4rLl>8#y+{EIN)S`Gt-;k8lGDG9E%?gaH Fi~s_*Bc%WU delta 73 zcmX@avXq6xn3tD}i@h~!BF9r>4`-{G(Bjmh;+QJOyu_SJAk#4=H#0BBCAB!aB)=dg bH@_$)zeqPXvA86)DBjUG#C)?YBP$~Sf1eoc diff --git a/Devices/__pycache__/visa_ports.cpython-37.pyc b/Devices/__pycache__/visa_ports.cpython-37.pyc new file mode 100644 index 0000000000000000000000000000000000000000..87fbe89cecd8852e8e2c80325577a20baf6dabe9 GIT binary patch literal 552 zcmYjO%SyvQ6rGtyiNP0Lxe~V(3_cO06sr|i?ZWCNMncF8wU~6`+_Ye+3(>6$@dtG2 zclZ}s>dIf}%6p?yFWfV8U+2u@U~bM~XwQ#F=O4I!Qt)R2H~Z+Ygdmy6B$p!Q9+xF) zJupv5(PovYei_RRccmWd9_Y3AyLb>9HT=RXYej@)L{>sSL(|Y{Xu~ zD{HWOe#=KF(BW6YtV*j>#3a*-I8KrMQMGXoiwGSYzp!cWNnBfP_QnkLR75V^^_-_g zRKWQ7aL+#jWxyY9?QCvuZtVIE1y`9){X`q7%~}#drc6+8o=P>?*b1>e=tc_sK^MXx z)g}Wsy%{^zxC>dNleA{jIY@At4VV9QRbw56G1S~iYtC}3rF*JU$}Q^57fuxDzKK*b z?1dd=ym_Jx%x@Ox4kqztNH?I; Date: Mon, 23 Jul 2018 12:00:21 +1000 Subject: [PATCH 2/4] Add files via upload New Experiment Photoreflectance --- Experiments/DLCP_batch.py | 152 ++++ Experiments/Dev_AMT/batch_control_AMT1.py | 283 ++++++ Experiments/Dev_AMT/cv_batch.py | 157 ++++ Experiments/Dev_AMT/cv_multi.py | 468 ++++++++++ Experiments/Dev_AMT/iv_AMT1.py | 381 ++++++++ Experiments/Dev_AMT/temperature_UNSW.py | 357 ++++++++ Experiments/Dev_AMT/temperature_batch_UNSW.py | 162 ++++ Experiments/__init__.py | 1 + .../__pycache__/DLCP_batch.cpython-36.pyc | Bin 0 -> 4831 bytes .../__pycache__/__init__.cpython-35.pyc | Bin 0 -> 426 bytes .../__pycache__/__init__.cpython-36.pyc | Bin 346 -> 432 bytes .../__pycache__/__init__.cpython-37.pyc | Bin 0 -> 406 bytes .../__pycache__/batch_control.cpython-35.pyc | Bin 0 -> 9513 bytes .../__pycache__/batch_control.cpython-36.pyc | Bin 8498 -> 8590 bytes .../__pycache__/batch_control.cpython-37.pyc | Bin 0 -> 8510 bytes Experiments/__pycache__/cv.cpython-35.pyc | Bin 0 -> 17093 bytes Experiments/__pycache__/cv.cpython-36.pyc | Bin 14507 -> 14557 bytes Experiments/__pycache__/cv.cpython-37.pyc | Bin 0 -> 14421 bytes Experiments/__pycache__/flash.cpython-35.pyc | Bin 0 -> 14290 bytes Experiments/__pycache__/flash.cpython-36.pyc | Bin 12044 -> 12073 bytes Experiments/__pycache__/flash.cpython-37.pyc | Bin 0 -> 11946 bytes Experiments/__pycache__/iv.cpython-35.pyc | Bin 0 -> 12515 bytes Experiments/__pycache__/iv.cpython-36.pyc | Bin 11029 -> 11058 bytes Experiments/__pycache__/iv.cpython-37.pyc | Bin 0 -> 10971 bytes .../photoreflectance.cpython-35.pyc | Bin 0 -> 26477 bytes .../photoreflectance.cpython-36.pyc | Bin 0 -> 23367 bytes .../photoreflectance.cpython-37.pyc | Bin 0 -> 21264 bytes .../__pycache__/spectroscopy.cpython-35.pyc | Bin 0 -> 20815 bytes .../__pycache__/spectroscopy.cpython-36.pyc | Bin 18533 -> 18562 bytes .../__pycache__/spectroscopy.cpython-37.pyc | Bin 0 -> 18401 bytes .../__pycache__/temperature.cpython-35.pyc | Bin 0 -> 11803 bytes .../__pycache__/temperature.cpython-36.pyc | Bin 9876 -> 9902 bytes .../__pycache__/temperature.cpython-37.pyc | Bin 0 -> 10179 bytes .../temperature_batch.cpython-36.pyc | Bin 0 -> 5092 bytes Experiments/batch_control.py | 33 +- Experiments/cv.py | 186 ++-- Experiments/iv_.py | 381 ++++++++ Experiments/photoreflectance.py | 821 ++++++++++++++++++ Experiments/temperature.py | 690 ++++++++------- 39 files changed, 3607 insertions(+), 465 deletions(-) create mode 100644 Experiments/DLCP_batch.py create mode 100644 Experiments/Dev_AMT/batch_control_AMT1.py create mode 100644 Experiments/Dev_AMT/cv_batch.py create mode 100644 Experiments/Dev_AMT/cv_multi.py create mode 100644 Experiments/Dev_AMT/iv_AMT1.py create mode 100644 Experiments/Dev_AMT/temperature_UNSW.py create mode 100644 Experiments/Dev_AMT/temperature_batch_UNSW.py create mode 100644 Experiments/__pycache__/DLCP_batch.cpython-36.pyc create mode 100644 Experiments/__pycache__/__init__.cpython-35.pyc create mode 100644 Experiments/__pycache__/__init__.cpython-37.pyc create mode 100644 Experiments/__pycache__/batch_control.cpython-35.pyc create mode 100644 Experiments/__pycache__/batch_control.cpython-37.pyc create mode 100644 Experiments/__pycache__/cv.cpython-35.pyc create mode 100644 Experiments/__pycache__/cv.cpython-37.pyc create mode 100644 Experiments/__pycache__/flash.cpython-35.pyc create mode 100644 Experiments/__pycache__/flash.cpython-37.pyc create mode 100644 Experiments/__pycache__/iv.cpython-35.pyc create mode 100644 Experiments/__pycache__/iv.cpython-37.pyc create mode 100644 Experiments/__pycache__/photoreflectance.cpython-35.pyc create mode 100644 Experiments/__pycache__/photoreflectance.cpython-36.pyc create mode 100644 Experiments/__pycache__/photoreflectance.cpython-37.pyc create mode 100644 Experiments/__pycache__/spectroscopy.cpython-35.pyc create mode 100644 Experiments/__pycache__/spectroscopy.cpython-37.pyc create mode 100644 Experiments/__pycache__/temperature.cpython-35.pyc create mode 100644 Experiments/__pycache__/temperature.cpython-37.pyc create mode 100644 Experiments/__pycache__/temperature_batch.cpython-36.pyc create mode 100644 Experiments/iv_.py create mode 100644 Experiments/photoreflectance.py diff --git a/Experiments/DLCP_batch.py b/Experiments/DLCP_batch.py new file mode 100644 index 0000000..2912d29 --- /dev/null +++ b/Experiments/DLCP_batch.py @@ -0,0 +1,152 @@ +__author__ = 'Andrew M. Telford & D. Alonso-Álvarez' + +import os +import numpy as np +import datetime +import time + +import tkinter as tk +from tkinter import ttk +from tkinter import filedialog + +#from Devices.Dummy_T_AMT import Dummy_T + +class DLCP_batch: + """ Base class for the batch mode in DLCP experiments """ + + def __init__(self, master, fileheader=''): + """ Constructor of the Batch class + + :param master: the script batch_control.py + :return: None + """ + self.master = master + self.header = fileheader + + self.create_interface() + + def create_interface(self): + + bias_frame = ttk.Frame(self.master.control_frame) + bias_frame.grid(column=0, row=0, sticky=(tk.EW)) + bias_frame.columnconfigure(0, weight=1) + + # Set widgets --------------------------------- + set_frame = ttk.Labelframe(bias_frame, text='Set:', padding=(0, 5, 0, 15)) + set_frame.grid(column=0, row=0, sticky=(tk.EW)) + set_frame.columnconfigure(0, weight=1) + + self.minAC_var = tk.DoubleVar() + self.minAC_var.set('0.01') + ttk.Label(set_frame, text='Min AC bias [V]: ').grid(column=0, row=0, sticky=(tk.EW)) + ttk.Entry(set_frame, width=15, textvariable=self.minAC_var).grid(column=0, row=1, sticky=(tk.EW)) + + self.maxAC_var = tk.DoubleVar() + self.maxAC_var.set('0.1') + ttk.Label(set_frame, text='Maximum AC bias [V]: ').grid(column=1, row=0, sticky=(tk.EW)) + ttk.Entry(set_frame, width=15, textvariable=self.maxAC_var).grid(column=1, row=1, sticky=(tk.EW)) + + self.ACstep_var = tk.DoubleVar() + self.ACstep_var.set('0.01') + ttk.Label(set_frame, text='AC bias step [V]: ').grid(column=2, row=0, sticky=(tk.EW)) + ttk.Entry(set_frame, width=15, textvariable=self.ACstep_var).grid(column=2, row=1, sticky=(tk.EW)) + + self.maxDC_var = tk.DoubleVar() + self.maxDC_var.set('-0.5') + ttk.Label(set_frame, text='Upper DC bias [V]: ').grid(column=0, row=2, sticky=(tk.EW)) + ttk.Entry(set_frame, width=15, textvariable=self.maxDC_var).grid(column=1, row=3, sticky=(tk.EW)) + + + def batch_ready(self): + """ After selecting all the conditions for the batch, we arrange the last things, select the folder to save + the data and fill the batch list. The batch is then ready to be launched. + + :return: None + """ + + rootname = self.master.get_rootname() + if rootname == '': return + + temp = filedialog.askdirectory(initialdir=self.master.path) + if temp == '': + return + else: + self.master.path = temp + + start = self.minAC_var.get() + end = self.maxAC_var.get() + step = self.ACstep_var.get() + steps = 1+(abs(end-start)/step) + + self.stepList = range(int(steps)) + self.master.count = 0 + self.master.batch_length = steps + self.rootname = os.path.join(self.master.path, rootname) + self.master.data_file = self.rootname + '.txt' + self.suffix = self.master.suffix_var.get() + self.master.populate_batch_list(['Step {0}'.format(i+1) for i in self.stepList]) + self.master.ready = True + + def batch_proceed(self): + """ This function is called from the main program to execute the next step in the batch. + The data and the filename of this measurement are stored in the "step_info" variable. + + :return: None + """ + ## Make summary file + if self.master.count == 0: + #self.timeZero = datetime.datetime.now() + self.master.save_batch_data(['Step', 'AC bias [V]', 'DC bias [V]', 'Start time', 'End time', 'Duration (s)', 'Filename']) + + self.ini_time = datetime.datetime.now() + + ## Send AC/DC parameters to CV experiment + # Include delay for transmission + + ## Calculate AC/DC biases + if self.master.count == 0: + self.AC = self.minAC_var.get() + else: + self.AC = round(self.AC + self.ACstep_var.get(), 3) + self.DC = self.maxDC_var.get() - self.AC/2 + print("DC bias is: ", self.DC) + + ## Send AC/DC bias values to CV + ################################ + + if self.suffix == 0: + self.filename = self.rootname + '_{0}.txt'.format(self.ini_time.strftime('%y%m%d_%H-%M-%S-%f')) + else: + self.filename = self.rootname + '_{0}.txt'.format(str(self.master.count+1).zfill(3)) + + self.step_info = [self.master.count+1, self.AC, self.DC, self.ini_time.strftime('%Y-%m-%d %H:%M:%S.%f'), self.filename] + self.step_string = 'Step = {0} \tStart time = {3} \tFilename = {4}'.format(*self.step_info) + + print('Next point in batch {0}/{1}:\n\t {2}'.format(self.master.count+1, self.master.batch_length, self.step_string)) + + self.master.update_batch_list() + + def batch_wrapup(self, data): + """ After taking a measurement in the main function, this function takes the measured data and saves it + following the convention of the batch + + :param data: data to be saved + :return: None + """ + end_time = datetime.datetime.now() + + self.step_info.insert(-1, end_time.strftime('%Y-%m-%d %H:%M:%S.%f')) + self.step_info.insert(-1, (end_time - self.ini_time).total_seconds()) + + self.master.save_batch_data(self.step_info) + + np.savetxt(self.filename, data, fmt='%.4e', delimiter='\t', + header='{0}\n{1}'.format(self.step_string, self.header)) + + self.master.count = self.master.count + 1 + if self.master.count == self.master.batch_length: + self.master.update_batch_list(True) + self.master.ready = False + self.master.count = 0 + + print('DLCP batch completed!!\n') diff --git a/Experiments/Dev_AMT/batch_control_AMT1.py b/Experiments/Dev_AMT/batch_control_AMT1.py new file mode 100644 index 0000000..3ad160d --- /dev/null +++ b/Experiments/Dev_AMT/batch_control_AMT1.py @@ -0,0 +1,283 @@ +__author__ = 'A. M. Telford & D. Alonso-Álvarez' + +import os +import numpy as np + +import tkinter as tk +from tkinter import ttk +from tkinter import filedialog, messagebox + +from Devices import device_manager + + +class Batch(object): + """ Base class for the batch mode in spectroscopy experiments """ + + def __init__(self, root, devman, fileheader='', mode='Dummy', device=None): + """ Constructor of the Batch class + + :param root: The main window of the program + :param devman: Device manager + :param mode: The type of batch to be done (default='Dummy') + :return: None + """ + self.root = root + self.dm = devman + self.ready = False + self.count = 0 + self.batch_length = 0 + self.path = os.path.expanduser('~') + self.data_file = None + self.interface() + + if mode == 'IV': + from Experiments.iv_batch import IV_batch + self.batch_data = IV_batch(self, self.dm, fileheader=fileheader) + self._batch_ready = self.batch_data.batch_ready + self._batch_proceed = self.batch_data.batch_proceed + self._batch_wrapup = self.batch_data.batch_wrapup + self.mode = mode + + elif mode == 'Temperature': + self.window.withdraw() + messagebox.showinfo(message='{0} batch mode not implemented, yet.'.format(mode)) + self.mode = 'Dummy' + + elif mode == 'Time': + from Experiments.time_batch import Time_batch + self.batch_data = Time_batch(self, fileheader=fileheader) + self._batch_ready = self.batch_data.batch_ready + self._batch_proceed = self.batch_data.batch_proceed + self._batch_wrapup = self.batch_data.batch_wrapup + self.mode = mode + + elif mode == 'CV_batch': + from Experiments.cv_batch import CV_batch + self.batch_data = CV_batch(self, self.dm, fileheader=fileheader) + self._batch_ready = self.batch_data.batch_ready + self._batch_proceed = self.batch_data.batch_proceed + self._batch_wrapup = self.batch_data.batch_wrapup + self.mode = mode + + else: + self.window.withdraw() + self.mode = 'Dummy' + + def null(self, *args, **kwargs): + """ Empty function that does nothing + + :return: None + """ + pass + + def quit(self): + """ Destroys this object + + :return: None + """ + self.window.destroy() + + def show(self): + """ Shows the batch window + + :return: None + """ + if self.mode is not 'Dummy': + self.window.update() + self.window.deiconify() + + def create_menu_bar(self): + """ Creates the menu bar and the elements within + + :return: None + """ + self.menubar = tk.Menu(self.window) + self.window['menu'] = self.menubar + + self.menu_hardware = tk.Menu(self.menubar) + self.menubar.add_cascade(menu=self.menu_hardware, label='Hardware') + + # Hardware menu + self.menu_hardware.add_command(label='Hardware configuration', command=self.dm.show) + self.menu_hardware.add_separator() + + def interface(self): + """ Creates the graphical interface for the batch + + :return: None + """ + + self.window = tk.Toplevel(self.root.window) + self.window.title('Batch window') + self.window.protocol('WM_DELETE_WINDOW', self.batch_cancel) # We hide it rather than quiting it + self.window.option_add('*tearOff', False) # Prevents tearing the menus + self.window.lift(self.root.window) # Brings the hardware window to the top + + self.create_menu_bar() + + # Here we will add the corresponding batch interface, dependent on what si the batch about + self.control_frame = ttk.Frame(master=self.window, padding=(15, 15, 15, 0)) + self.control_frame.grid(column=0, row=0, sticky=tk.NSEW) + self.control_frame.columnconfigure(0, weight=1) + + # Here we will add the corresponding batch control (see below) + self.batch_frame = ttk.Frame(master=self.window, padding=(15, 15, 15, 0)) + self.batch_frame.grid(column=0, row=1, sticky=tk.NSEW) + self.control_frame.columnconfigure(0, weight=1) + + # Save widgets --------------------------------- + save_frame = ttk.Labelframe(self.batch_frame, text='Save:', padding=(0, 5, 0, 15)) + save_frame.columnconfigure(0, weight=1) + save_frame.grid(column=0, row=0, sticky=tk.NSEW) + + self.root_var = tk.StringVar() + self.root_var.set('ID') + ttk.Label(save_frame, text='Sample ID: ').grid(column=0, row=0, sticky=(tk.E, tk.W, tk.S)) + ttk.Entry(save_frame, width=10, textvariable=self.root_var).grid(column=1, row=0, sticky=(tk.E, tk.W, tk.S)) + + self.meas_var = tk.StringVar() + self.meas_var.set('meas') + ttk.Label(save_frame, text='Measurement: ').grid(column=0, row=1, sticky=(tk.E, tk.W, tk.S)) + ttk.Entry(save_frame, width=10, textvariable=self.meas_var).grid(column=1, row=1, sticky=(tk.E, tk.W, tk.S)) + + self.comment_var = tk.StringVar() + self.comment_var.set('') + ttk.Label(save_frame, text='Comments: ').grid(column=0, row=2, sticky=(tk.E, tk.W, tk.S)) + ttk.Entry(save_frame, width=10, textvariable=self.comment_var).grid(column=1, row=2, sticky=(tk.E, tk.W, tk.S)) + + self.suffix_var = tk.IntVar() + self.suffix_var.set(1) + ttk.Label(save_frame, text='Suffix: ').grid(column=0, row=52, sticky=(tk.E, tk.W, tk.S)) + ttk.Radiobutton(save_frame, text="Batch variable", variable=self.suffix_var, value=0).grid(column=0, row=53, sticky=(tk.E, tk.W, tk.S)) + ttk.Radiobutton(save_frame, text="Consecutive numbers", variable=self.suffix_var, value=1).grid(column=1, row=53, sticky=(tk.E, tk.W, tk.S)) + + # run widgets --------------------------------- + run_frame = ttk.Labelframe(self.batch_frame, text='Run:', padding=(0, 5, 0, 15)) + run_frame.columnconfigure(0, weight=1) + run_frame.columnconfigure(1, weight=1) + run_frame.columnconfigure(2, weight=1) + run_frame.grid(column=0, row=1, sticky=tk.NSEW) + + ttk.Button(master=run_frame, width=7, text='Cancel', command=self.batch_cancel).grid(column=0, row=0, sticky=(tk.E, tk.W, tk.S)) + ttk.Button(master=run_frame, width=7, text='Clear', command=self.batch_clear).grid(column=1, row=0, sticky=(tk.E, tk.W, tk.S)) + ttk.Button(master=run_frame, width=7, text='Ready!', command=self.batch_ready).grid(column=2, row=0, sticky=(tk.E, tk.W, tk.S)) + + # Batch list widgets + list_frame = ttk.Frame(self.window, padding=(0, 15, 15, 15)) + list_frame.grid(column=1, row=0, rowspan=3, sticky=tk.NSEW) + list_frame.rowconfigure(0, weight=1) + + self.batch_list = tk.Listbox(master=list_frame) + batch_list_scroll = ttk.Scrollbar(master=list_frame, orient=tk.VERTICAL, command=self.batch_list.yview) + self.batch_list.configure(yscrollcommand=batch_list_scroll.set) + self.batch_list.grid(column=0, row=0, sticky=(tk.NSEW)) + batch_list_scroll.grid(column=1, row=0, sticky=(tk.NS)) + + def disable(self): + """ Dissables the batch mode. + + :return: None + """ + self.ready = False + messagebox.showinfo(message='Batch mode disabled') + + def populate_batch_list(self, new_list): + """ Fills the batch list with the bias steps that will be done + + :param new_list: The list of steps to be done + :return: None + """ + self.batch_list.delete(0, tk.END) + for name in new_list: + self.batch_list.insert(tk.END, name) + + def update_batch_list(self, last=False): + """ Updates the color of the elements of the list. + + - Red = done + - Green = in process + - Black = not done yet + + :param last: flag indicating if this one is the last element + :return: None + """ + if self.count > 0: + self.batch_list.itemconfig(self.count-1, fg='red') + + if last: + return + + self.batch_list.itemconfig(self.count, fg='green') + + def batch_clear(self): + """ Clears the batch list and disables the batch + + :return: None + """ + self.ready = False + self.batch_list.delete(0, tk.END) + + def batch_cancel(self): + """ Cancels the creation of the batch + + :return: None + """ + self.batch_clear() + + # Finally, we hide this window + self.window.withdraw() + + def save_batch_data(self, new_data): + """ Saves the data associate to each step of the batch. The data is append to the end of an existing file. + + :param new_data: The data to append + :return: None + """ + + with open(self.data_file, 'a') as f: + f.writelines("%s\t" % i for i in new_data) + f.write('\n') + + def get_rootname(self): + + sampleid = self.root_var.get() + meas = self.meas_var.get() + comment = self.comment_var.get() + + if sampleid == '' or meas == '': + messagebox.showinfo(message='Sample ID and Measurement fields can not be empty.') + return '' + else: + if comment != '': + rootname = sampleid + '_' + meas + '_' + comment + else: + rootname = sampleid + '_' + meas + + return rootname + + def batch_ready(self): + """ Not used. It will be overwritten by the batch specific functions. However. it needs to exists in order to + create the interface + """ + self._batch_ready() + + def batch_proceed(self): + """ Not used. It will be overwritten by the batch specific functions. However. it needs to exists in order to + create the interface + """ + self._batch_proceed() + + def batch_wrapup(self, data): + """ Not used. It will be overwritten by the batch specific functions. However. it needs to exists in order to + create the interface + """ + self._batch_wrapup(data) + +if __name__ == '__main__': + + root = tk.Tk() + dm = device_manager.Devman(root) + b = Batch(root, dm) + root.mainloop() + + diff --git a/Experiments/Dev_AMT/cv_batch.py b/Experiments/Dev_AMT/cv_batch.py new file mode 100644 index 0000000..9fe043a --- /dev/null +++ b/Experiments/Dev_AMT/cv_batch.py @@ -0,0 +1,157 @@ +__author__ = 'A. M. Telford & D. Alonso-Álvarez' + +import os +import numpy as np + +from tkinter import filedialog, messagebox + +from Experiments.cv import CV +from Experiments.batch_control_AMT1 import Batch + +class CV_batch(CV): + """ Base class for the batch mode in CV experiments """ + + def __init__(self, master, devman, fileheader='', in_batch=True): + """ Constructor of the CV_batch class, same as CV.__init__ but without + assignment of device. + + :param root: The main window of the program + :param devman: Device manager + :param mode: The type of batch to be done (default='Dummy') + :return: None + """ + self.master = master + self.dm = devman + self.id = 'CV_batch' + + + # Create the main variables of the class + self.in_batch = in_batch + self.create_variables() + + self.plot_format = {'ratios': (1, 1), + 'xlabel': 'Frequency (Hz)', + 'x_scale' : 'log', + 'Ch1_ylabel': '|Z| (Ω)', + 'Ch2_ylabel': 'Θ (°)', + 'Ch1_scale': 'log', + 'Ch2_scale': 'log'} + #self.update_header() + self.header='' + self.extension = 'txt' + self.batch = Batch(self.master, self.dm, fileheader=self.header) + + # Create the interface + self.create_interface() + + # We DO NOT load the dummy devices by default + #self.fill_devices() + self.za = self.root.experiment.za + + ## HIDE UNUSED INTERFACES + + #self.header = fileheader + #CV.__init__(self, master, devman, in_batch=True) + #self.za = device + + + def batch_ready(self): + """ After selecting all the conditions for the batch, we arrange the last things, select the folder to save + the data and fill the batch list. The batch is then ready to be launched. + + :return: None + """ + + rootname = self.master.get_rootname() + if rootname == '': return + + temp = filedialog.askdirectory(initialdir=self.master.path) + if temp == '': + return + else: + self.master.path = temp + + mode = 'fixed' ## Call to fixed_var in CV experiment + + start = float(self.start_var.get()) + stop = float(self.stop_var.get()) + #step = float(self.step_var.get()) + nop = int(self.nop_var.get()) + + #integration_time = int(self.integration_time_list.current()) + #delay = int(self.waiting_time_entry.get()) + #compliance = float(self.compliance_var.get()) + #meas_range = self.range_list.current() + + + step = (stop - start)/nop + self.valuesList = np.arange(start, stop + 0.0001 * step, step) + #else: + # totalPoints = int(np.log10(max(stop, start) / max(1e-9, min(stop, start))) * points) + 1 + # ini = np.log10(max(start, 1e-9)) + # fin = np.log10(max(stop, 1e-9)) + # self.biasList = np.logspace(ini, fin, totalPoints) + + self.master.count = 0 + self.master.batch_length = len(self.valuesList) + + self.options = {'fixed': mode, + 'start': start, + 'stop': stop, + 'nop': nop} + + rootname = os.path.join(self.master.path, rootname) + self.master.data_file = rootname + '.txt' + suffix = self.master.suffix_var.get() + if suffix == 0: + self.filenames = [rootname + '_{0}_{1:.4e}.txt'.format(mode.upper(), bias) for bias in self.valuesList] + else: + self.filenames = [rootname + '_{0}.txt'.format(str(i+1).zfill(3)) for i in range(self.master.batch_length)] + + self.master.populate_batch_list(['{0} = {1:.4e}'.format(mode.upper(), bias) for bias in self.valuesList]) + + self.master.ready = True + + def batch_proceed(self): + """ This function is called from the main program to execute the next step in the batch. + The voltage, current and the filename of this measuremtn are stored in the "step_info" variable. + + :return: None + """ + + if self.master.count == 0: + self.za.setup_measurement(**self.options) + self.za.operate_on() + self.master.save_batch_data(['Voltage (V)', 'Current (A)', 'Filename']) + + fxd = self.valuesList[self.master.count] + #self.za.set_fxd(fxdValue=fxd) + fixed_param = self.za.get_data() + + self.step_info = [self.master.count, fixed_param[0], self.filenames[self.master.count]] + self.step_string = 'V = {1:.3f} V\tI = {2:.4e} A\tFilename = {3}'.format(*self.step_info) + + print('Next point in batch {0}/{1}:\n\t {2}'.format(self.master.count+1, self.master.batch_length, self.step_string)) + + self.master.update_batch_list() + + def batch_wrapup(self, data): + """ After taking a measurement in the main function, this function takes the measured data and saves it + following the convention of the batch + + :param data: data to be saved + :return: None + """ + self.master.save_batch_data(self.step_info) + + np.savetxt(self.filenames[self.master.count], data, fmt='%.4e', delimiter='\t', + header='{0}\n{1}'.format(self.step_string, self.header)) + + self.master.count = self.master.count + 1 + if self.master.count == self.master.batch_length: + self.master.update_batch_list(True) + self.master.ready = False + self.master.count = 0 + self.za.operate_off() + + print('IV batch completed!!\n') diff --git a/Experiments/Dev_AMT/cv_multi.py b/Experiments/Dev_AMT/cv_multi.py new file mode 100644 index 0000000..cff3d50 --- /dev/null +++ b/Experiments/Dev_AMT/cv_multi.py @@ -0,0 +1,468 @@ +__author__ = 'A. M. Telford & D. Alonso-Álvarez' + +import tkinter as tk +from tkinter import ttk + +import numpy as np +from Experiments.batch_control import Batch + +class CV: + """ Base class for impedance sweep experiments """ + + def __init__(self, master, devman, in_batch=False): + self.master = master + self.dm = devman + self.id = 'CV' + + # Create the main variables of the class + self.in_batch = in_batch + self.create_variables() + + self.plot_format = {'ratios': (1, 1), + 'xlabel': 'Frequency (Hz)', + 'x_scale' : 'log', + 'Ch1_ylabel': '|Z| (Ohm)', + 'Ch2_ylabel': 'Tz (deg)', + 'Ch1_scale': 'log', + 'Ch2_scale': 'linear'} + + top0 = 'Bias = ' + top1 = '0 V' + col0 = self.plot_format['xlabel'] + col1 = self.plot_format['Ch1_ylabel'] + col2 = self.plot_format['Ch2_ylabel'] + self.header = str(top0)+'= '+str(top1)+'\n'+str(col0)+'\t'+str(col1)+'\t'+str(col2) + self.header.encode('utf-8') + + self.extension = 'txt' + self.batch = Batch(self.master, self.dm, fileheader=self.header) ## Dummy mode + + # Create the interface + self.create_interface() + + # We load the dummy devices by default + self.fill_devices() + + def quit(self): + """ Safe closing of the devices. The devices must be closed by the Device Manager, not directly, + so they are registered as "free". + """ + + if self.za is not None: + self.dm.close_device(self.za) + + self.batch.window.destroy() + + def create_variables(self): + + # Data variables + self.record = None + + # Hardware variables + self.za = None + + # Acquisition variables + ##self.integration_time = 300 + ##self.delay = 100 + self.n_averages = 10 + self.stop = True + + def create_menu_bar(self): + """ Add elements to the master menu bar + """ + + # Hardware menu + self.master.menu_hardware.add_command(label='ZA', command=lambda: self.za.interface(self.master)) + self.master.menu_hardware.entryconfig("ZA", state="disabled") + + # Batch menu + if not self.in_batch: + self.master.menu_batch.add_command(label='Disable', command=self.batch.disable) + self.master.menu_batch.add_command(label='Temperature', command=lambda: self.new_batch('Temperature')) + self.master.menu_batch.add_command(label='Time', command=lambda: self.new_batch('Time')) + self.master.menu_batch.add_command(label='CV_batch', command=lambda: self.new_batch('CV_batch')) + + def new_batch(self, batch_mode): + """ Shows current batch window or, if a different batch is chosen, destroys the old one and creates a new one + for the new batch mpde + + :param batch_mode: the selected type of batch + :return: None + """ + if self.batch.mode == batch_mode: + self.batch.show() + else: + self.batch.window.destroy() + self.batch = Batch(self.master, self.dm, fileheader=self.header, mode=batch_mode) + + def create_interface(self): + + # Add elements to the menu bar + self.create_menu_bar() + + cv_frame = ttk.Frame(self.master.control_frame) + cv_frame.grid(column=0, row=0, sticky=(tk.EW)) + cv_frame.columnconfigure(0, weight=1) + + # Hardware widgets + hardware_frame = ttk.Labelframe(cv_frame, text='Selected hardware:', padding=(0, 5, 0, 15)) + hardware_frame.grid(column=0, row=0, sticky=(tk.EW)) + hardware_frame.columnconfigure(0, weight=1) + + self.za_var = tk.StringVar() + self.za_box = ttk.Combobox(master=hardware_frame, textvariable=self.za_var, state="readonly") + self.za_box.grid(column=0, row=0, sticky=(tk.EW)) + self.za_box.bind('<>', self.select_za) + + + # Scan mode widgets --------------------------------- + mode_frame = ttk.Labelframe(cv_frame, text='Scan mode:', padding=(0, 5, 0, 15)) + mode_frame.grid(column=0, row=1, sticky=(tk.EW)) + mode_frame.columnconfigure(1, weight=1) + + self.fixed_var = tk.StringVar() + self.fixed_var.set('bias') + ttk.Radiobutton(mode_frame, text="Fixed Bias", variable=self.fixed_var, value='bias', command=self.mode).grid(column=0, row=0, sticky=(tk.E, tk.W, tk.S)) + ttk.Radiobutton(mode_frame, text="Fixed Frequency", variable=self.fixed_var, value='freq', command=self.mode).grid(column=1, row=0, sticky=(tk.E, tk.W, tk.S)) + + self.fixed_value_var = tk.StringVar() + self.fixed_value_var.set('0') + self.fixed_value_label = ttk.Label(mode_frame, text='Set bias (V): ') + self.fixed_value_label.grid(column=0, row=1, sticky=(tk.E, tk.W, tk.S)) + ttk.Entry(master=mode_frame, width=10, textvariable=self.fixed_value_var).grid(column=1, row=1, sticky=(tk.E, tk.W, tk.S)) + + self.osc_amp_var = tk.StringVar() + self.osc_amp_var.set('0.005') + self.osc_amp_label = ttk.Label(mode_frame, text='Set AC bias (V): ') + self.osc_amp_label.grid(column=0, row=2, sticky=(tk.E, tk.W, tk.S)) + ttk.Entry(master=mode_frame, width=10, textvariable=self.osc_amp_var).grid(column=1, row=2, sticky=(tk.E, tk.W, tk.S)) + + # Ch1 setup widgets -------------------------------- + Ch1_frame = ttk.Labelframe(cv_frame, text='Ch1 setup:', padding=(0, 5, 0, 15)) + Ch1_frame.grid(column=0, row=2, sticky=(tk.EW)) + Ch1_frame.columnconfigure(1, weight=1) + self.Ch1_param_var = tk.StringVar() + self.Ch1_param_var.set('Z') + self.Ch1_scale_var = tk.StringVar() + self.Ch1_scale_var.set('log') + ttk.Radiobutton(Ch1_frame, text="Z", variable=self.Ch1_param_var, value='Z', command=self.channel_param).grid(column=0, row=0, sticky=(tk.E, tk.W, tk.S)) + ttk.Radiobutton(Ch1_frame, text="Cs", variable=self.Ch1_param_var, value='Cs', command=self.channel_param).grid(column=1, row=0, sticky=(tk.E, tk.W, tk.S)) + ttk.Radiobutton(Ch1_frame, text="Cp", variable=self.Ch1_param_var, value='Cp', command=self.channel_param).grid(column=2, row=0, sticky=(tk.E, tk.W, tk.S)) + ttk.Radiobutton(Ch1_frame, text="Linear", variable=self.Ch1_scale_var, value='linear', command=self.scan_scale).grid(column=0, row=1, sticky=(tk.E, tk.W, tk.S)) + ttk.Radiobutton(Ch1_frame, text="Log10", variable=self.Ch1_scale_var, value='log', command=self.scan_scale).grid(column=1, row=1, sticky=(tk.E, tk.W, tk.S)) + + # Ch2 setup widgets -------------------------------- + Ch2_frame = ttk.Labelframe(cv_frame, text='Ch2 setup:', padding=(0, 5, 0, 15)) + Ch2_frame.grid(column=0, row=3, sticky=(tk.EW)) + Ch2_frame.columnconfigure(1, weight=1) + self.Ch2_param_var = tk.StringVar() + self.Ch2_param_var.set('Tz') + self.Ch2_scale_var = tk.StringVar() + self.Ch2_scale_var.set('linear') + ttk.Radiobutton(Ch2_frame, text="Tz", variable=self.Ch2_param_var, value='Tz', command=self.channel_param).grid(column=0, row=0, sticky=(tk.E, tk.W, tk.S)) + ttk.Radiobutton(Ch2_frame, text="Rs", variable=self.Ch2_param_var, value='Rs', command=self.channel_param).grid(column=1, row=0, sticky=(tk.E, tk.W, tk.S)) + ttk.Radiobutton(Ch2_frame, text="Rp", variable=self.Ch2_param_var, value='Rp', command=self.channel_param).grid(column=2, row=0, sticky=(tk.E, tk.W, tk.S)) + ttk.Radiobutton(Ch2_frame, text="Linear", variable=self.Ch2_scale_var, value='linear', command=self.scan_scale).grid(column=0, row=1, sticky=(tk.E, tk.W, tk.S)) + self.Ch2log_button = ttk.Radiobutton(Ch2_frame, text="Log10", variable=self.Ch2_scale_var, value='log', command=self.scan_scale) + self.Ch2log_button.grid(column=1, row=1, sticky=(tk.E, tk.W, tk.S)) + + # Sweep setup widgets --------------------------------- + sweep_frame = ttk.Labelframe(cv_frame, text='Sweep setup:', padding=(0, 5, 0, 15)) + sweep_frame.columnconfigure(0, weight=1) + sweep_frame.grid(column=0, row=4, sticky=(tk.EW)) + + self.start_var = tk.StringVar() + self.start_var.set('20') + self.start_label = ttk.Label(sweep_frame, text='Frequency start (Hz): ') + self.start_label.grid(column=0, row=0, sticky=(tk.E, tk.W, tk.S)) + ttk.Entry(master=sweep_frame, width=10, textvariable=self.start_var).grid(column=1, row=0, sticky=(tk.E, tk.W, tk.S)) + + self.stop_var = tk.StringVar() + self.stop_var.set('1e07') + self.stop_label = ttk.Label(sweep_frame, text='Frequency stop (Hz): ') + self.stop_label.grid(column=0, row=1, sticky=(tk.E, tk.W, tk.S)) + ttk.Entry(master=sweep_frame, width=10, textvariable=self.stop_var).grid(column=1, row=1, sticky=(tk.E, tk.W, tk.S)) + + self.x_scale_var = tk.StringVar() + self.x_scale_var.set('log') + ttk.Radiobutton(sweep_frame, text="Linear", variable=self.x_scale_var, value='linear', command=self.scan_scale).grid(column=0, row=2, sticky=(tk.E, tk.W, tk.S)) + self.xlog_button = ttk.Radiobutton(sweep_frame, text="Log10", variable=self.x_scale_var, value='log', command=self.scan_scale) + self.xlog_button.grid(column=1, row=2, sticky=(tk.E, tk.W, tk.S)) + + self.nop_var = tk.StringVar() + self.nop_var.set('401') + self.nop_label = ttk.Label(sweep_frame, text='Number of points: ') + self.nop_label.grid(column=0, row=3, sticky=(tk.E, tk.W, tk.S)) + ttk.Entry(master=sweep_frame, width=10, textvariable=self.nop_var).grid(column=1, row=3, sticky=(tk.E, tk.W, tk.S)) + + self.navg_var = tk.StringVar() + self.navg_var.set('10') + self.navg_label = ttk.Label(sweep_frame, text='Number of point averages: ') + self.navg_label.grid(column=0, row=4, sticky=(tk.E, tk.W, tk.S)) + ttk.Entry(master=sweep_frame, width=10, textvariable=self.navg_var).grid(column=1, row=4, sticky=(tk.E, tk.W, tk.S)) + + self.sweep_dir_var = tk.StringVar() + self.sweep_dir_var.set('up') + ttk.Radiobutton(sweep_frame, text="Sweep Up", variable=self.sweep_dir_var, value='up').grid(column=0, row=5, sticky=(tk.E, tk.W, tk.S)) + ttk.Radiobutton(sweep_frame, text="Sweep Down", variable=self.sweep_dir_var, value='down').grid(column=1, row=5, sticky=(tk.E, tk.W, tk.S)) + + # The "Run" button is only available if not in batch mode + if not self.in_batch: + self.run_button = ttk.Button(master=sweep_frame, width=10, text='Run', command=self.start_stop_scan) + self.run_button.grid(column=1, row=98, sticky=( tk.E, tk.SW, tk.S)) + + def update_header(self): + top0 = self.fixed_var.get() + top1 = self.fixed_value_var.get() + + if top0 == 'bias': + top2 = 'V' + else: + top2 = 'Hz' + + col0 = self.plot_format['xlabel'] + col1 = self.plot_format['Ch1_ylabel'] + col2 = self.plot_format['Ch2_ylabel'] + self.header = str(top0)+'= '+str(top1)+' '+str(top2)+'\n'+str(col0)+'\t'+str(col1)+'\t'+str(col2) + self.header.encode('utf-8') + + def fill_devices(self): + """ Fills the device selectors with the corresponding type of devices + + :return: None + """ + self.za_box['values'] = self.dm.get_devices(['ZA']) + self.za_box.current(0) + + self.select_za() + + def select_za(self, *args): + """ When the Z analyser selector changes, this function updates some variables and the graphical interface + to adapt it to the selected device. + + :param args: Dummy variable that does nothing but must exist (?) + :return: None + """ + + if self.za is not None: + self.dm.close_device(self.za) + + dev_name = self.za_var.get() #works + self.za = self.dm.open_device(dev_name) + + if self.za is None: + self.za_box.current(0) + self.za = self.dm.open_device(self.za_var.get()) + + elif self.dm.current_config[dev_name]['Type'] == 'ZA': + self.fixed_var.set('bias') + + else: + self.za_box.current(0) + self.za = self.dm.open_device(self.za_var.get()) + + # If the device has an interface to set options, we link it to the entry in the menu + interface = getattr(self.za, "interface", None) + if callable(interface): + self.master.menu_hardware.entryconfig("ZA", state="normal") + else: + self.master.menu_hardware.entryconfig("ZA", state="disabled") + + def refresh_buttons(self): + if self.fixed_var.get() == 'freq': + self.x_scale_var.set('linear') + self.scan_scale() ## Updates plot_format, which is used to pass parameters to the device + self.xlog_button.configure(state='disabled') + elif self.fixed_var.get() == 'bias': + self.xlog_button.configure(state='normal') + + if self.Ch2_param_var.get() == 'Tz': + self.Ch2_scale_var.set('linear') + self.scan_scale() ## Updates plot_format, which is used to pass parameters to the device + self.Ch2log_button.configure(state='disabled') + elif self.fixed_var.get() == 'bias': + self.Ch2log_button.configure(state='normal') + + def mode(self): + """ When the bias mode is changed, this function updates some internal variables and the graphical interface + + :return: None + """ + + if self.fixed_var.get() == 'bias': + self.start_label['text'] = 'Frequency start (Hz): ' + self.stop_label['text'] = 'Frequency stop (Hz): ' + self.start_var.set('20') + self.stop_var.set('1e7') + self.fixed_value_label['text'] = 'Set Bias (V): ' + self.fixed_value_var.set('0') + self.osc_amp_var.set('0.005') + + self.plot_format['xlabel'] = 'Frequency (Hz)' + + elif self.fixed_var.get() == 'freq': + self.start_label['text'] = 'Bias start (V): ' + self.stop_label['text'] = 'Bias stop (V): ' + self.start_var.set('-0.5') + self.stop_var.set('0.5') + self.fixed_value_label['text'] = 'Set Frequency (Hz): ' + self.fixed_value_var.set('1e5') + self.plot_format['xlabel'] = 'Bias (V)' + self.fixed_value_var.set('1e5') + self.osc_amp_var.set('0.005') + + self.refresh_buttons() + self.master.update_plot_axis(self.plot_format) + + def channel_param(self): + """ When the channel parameters are changed, this function updates some internal variables and the graphical interface + + :return: None + """ + ## Channel 1 labels ---------------------------------- + if self.Ch1_param_var.get() == 'Z': + self.plot_format['Ch1_ylabel'] = '|Z| (Ohm)' + elif self.Ch1_param_var.get() == 'Cp': + self.plot_format['Ch1_ylabel'] = 'Cp (F)' + elif self.Ch1_param_var.get() == 'Cs': + self.plot_format['Ch1_ylabel'] = 'Cs (F)' + ## Channel 2 labels ---------------------------------- + if self.Ch2_param_var.get() == 'Tz': + self.plot_format['Ch2_ylabel'] = 'Tz (deg)' + self.Ch2_scale_var.set('linear') + self.scan_scale() ## Updates plot_format, which is used to pass parameters to the device + + elif self.Ch2_param_var.get() == 'Rp': + self.plot_format['Ch2_ylabel'] = 'Rp (Ohm)' + elif self.Ch2_param_var.get() == 'Rs': + self.plot_format['Ch2_ylabel'] = 'Rs (Ohm)' + + self.refresh_buttons() + self.master.update_plot_axis(self.plot_format) + + def scan_scale(self): + """ When the scan scales are changed, this function updates some internal variables and the graphical interface + + :return: None + """ + ## X-axis scale ---------------------------------- + if self.x_scale_var.get() == 'linear': + self.plot_format['x_scale'] = 'linear' + elif self.x_scale_var.get() == 'log': + self.plot_format['x_scale'] = 'log' + ## Channel 1 scale ---------------------------------- + if self.Ch1_scale_var.get() == 'linear': + self.plot_format['Ch1_scale'] = 'linear' + elif self.Ch1_scale_var.get() == 'log': + self.plot_format['Ch1_scale'] = 'log' + ## Channel 2 scale ---------------------------------- + if self.Ch2_scale_var.get() == 'linear': + self.plot_format['Ch2_scale'] = 'linear' + elif self.Ch2_scale_var.get() == 'log': + self.plot_format['Ch2_scale'] = 'log' + + self.master.update_plot_axis(self.plot_format) + + + def start_stop_scan(self): + """ Starts and stops a scan + + :return: None + """ + if self.stop: + self.stop = False + + if self.batch.ready: + self.run_button['text'] = 'Stop batch' + else: + self.run_button['state'] = 'disabled' + self.prepare_scan() + else: + if self.batch.ready: + self.batch.ready = False + self.stop = True + self.run_button['text'] = 'Run' + self.run_button['state'] = 'disabled' + + def prepare_scan(self): + """ Any scan is divided in three stages: + 1) Prepare the conditions of the scan (this function), getting starting point, integration time and creating all relevant variables. + 2) Running the scan, performed by a recursive function "get_next_datapoint" + 3) Finish the scan, where we update some variables and save the data. + + :return: None + """ + mode = self.fixed_var.get() + fixed_value = float(self.fixed_value_var.get()) + start = float(self.start_var.get()) + stop = float(self.stop_var.get()) + nop = int(self.nop_var.get()) + navg = int(self.navg_var.get()) + osc_amp = float(self.osc_amp_var.get()) + sweep_dir = self.sweep_dir_var.get() + + self.options = {'fixed': mode, + 'fixed_value': fixed_value, + 'start': start, + 'stop': stop, + 'nop': nop, + 'navg': navg, + 'osc_amp': osc_amp, + 'sweep_dir': sweep_dir} + + # # If we are in a batch, we proceed to the next point + if self.batch.ready: + self.batch.batch_proceed() + + print('Starting scan...') + + # Create the record array. In this case it is irrelevant + self.record = np.zeros((nop+1, 4)) + self.record[:, 0] = np.linspace(0, 1, nop+1) + self.record[:, 1] = np.ones_like(self.record[:, 0]) + self.record[:, 2] = np.ones_like(self.record[:, 0]) + self.master.prepare_meas(self.record) + self.start_scan() + + def start_scan(self): ##Change to use *OPC? + + self.za.setup_measurement(self.plot_format, self.options) + self.za.measure() + self.get_data() + + + def get_data(self): + + data0, data1, data2 = self.za.return_data() + + self.record = np.zeros((len(data0), 3)) + self.record[:, 0] = data0 + self.record[:, 1] = data1 + self.record[:, 2] = data2 + + self.master.update_plot(self.record) + self.finish_scan() + + def finish_scan(self): + """ Finish the scan, updating some global variables, saving the data in the temp file and offering to save the + data somewhere else. + + :return: None + """ + + if self.batch.ready: + self.master.finish_meas(self.record, finish=False) + self.batch.batch_wrapup(self.record) + else: + self.master.finish_meas(self.record, finish=True) + self.stop = True + + if self.stop or not self.batch.ready: + # We reduce the number of counts in the batch to resume at the unfinished point if necessary + # In other words, it repeats the last, un-finished measurement + self.batch.count = self.batch.count - 1 + self.run_button['state'] = 'enabled' + + else: + self.prepare_scan() + diff --git a/Experiments/Dev_AMT/iv_AMT1.py b/Experiments/Dev_AMT/iv_AMT1.py new file mode 100644 index 0000000..cd45f69 --- /dev/null +++ b/Experiments/Dev_AMT/iv_AMT1.py @@ -0,0 +1,381 @@ +__author__ = 'D. Alonso-Álvarez' + +import tkinter as tk +from tkinter import ttk + +import numpy as np +from Experiments.batch_control import Batch + + +class IV: + """ Base class for IV experiments """ + + def __init__(self, master, devman, in_batch=False): + + self.master = master + self.dm = devman + self.id = 'iv' + + # Create the main variables of the class + self.in_batch = in_batch + self.create_variables() + + # Pre-load the batch, witout creating any interface + self.header = 'Voltage (V)\tAbs(Current) (A)\tCurrent (A)' + self.extension = 'iv' + self.batch = Batch(self.master, self.dm, fileheader=self.header) + + # Create the interface + self.create_interface() + self.plot_format = {'ratios': (1, 1), + 'xlabel': 'Voltage (V)', + 'Ch1_ylabel': 'Abs(Current) (A)', + 'Ch2_ylabel': 'Current (A)', + 'Ch1_scale': 'log', + 'Ch2_scale': 'linear'} + + # We load the dummy devices by default + self.fill_devices() + + def quit(self): + """ Safe closing of the devices. The devices must be closed by the Device Manager, not directly, + so they are registered as "free". + """ + + if self.smu is not None: + self.dm.close_device(self.smu) + + self.batch.window.destroy() + + def create_variables(self): + + # Data variables + self.record = None + + # Hardware variables + self.smu = None + + # Adquisition variables + self.integration_time = 300 + self.delay = 100 + self.stop = True + + def create_menu_bar(self): + """ Add elememnts to the master menubar + """ + + # Hardware menu + self.master.menu_hardware.add_command(label='SMU', command=lambda: self.smu.interface(self.master)) + self.master.menu_hardware.entryconfig("SMU", state="disabled") + + # Batch menu + if not self.in_batch: + self.master.menu_batch.add_command(label='Disable', command=self.batch.disable) + self.master.menu_batch.add_command(label='Temperature', command=lambda: self.new_batch('Temperature')) + self.master.menu_batch.add_command(label='Time', command=lambda: self.new_batch('Time')) + + def new_batch(self, batch_mode): + """ Shows current batch window or, if a different batch is chosen, destroys the old one and creates a new one + for the new batch mpde + + :param batch_mode: the selected type of batch + :return: None + """ + if self.batch.mode == batch_mode: + self.batch.show() + else: + self.batch.window.destroy() + self.batch = Batch(self.master, self.dm, mode=batch_mode, fileheader=self.header) + + def create_interface(self): + + # Add elements to the menu bar + self.create_menu_bar() + + iv_frame = ttk.Frame(self.master.control_frame) + iv_frame.grid(column=0, row=0, sticky=(tk.EW)) + iv_frame.columnconfigure(0, weight=1) + + # Hardware widgets + hardware_frame = ttk.Labelframe(iv_frame, text='Selected hardware:', padding=(0, 5, 0, 15)) + hardware_frame.grid(column=0, row=0, sticky=(tk.EW)) + hardware_frame.columnconfigure(0, weight=1) + + self.smu_var = tk.StringVar() + self.smu_box = ttk.Combobox(master=hardware_frame, textvariable=self.smu_var, state="readonly") + self.smu_box.grid(column=0, row=0, sticky=(tk.EW)) + self.smu_box.bind('<>', self.select_smu) + + # Set widgets --------------------------------- + set_frame = ttk.Labelframe(iv_frame, text='Set:', padding=(0, 5, 0, 15)) + set_frame.grid(column=0, row=1, sticky=(tk.EW)) + set_frame.columnconfigure(1, weight=1) + + self.integration_time_button = ttk.Button(master=set_frame, text='Integration time (ms)', command=self.update_integration_time) + self.integration_time_list = ttk.Combobox(set_frame, state="readonly", width=10) + + self.waiting_time_button = ttk.Button(master=set_frame, text='Waiting time (ms)', command=self.update_waiting_time) + self.waiting_time_entry = ttk.Entry(master=set_frame, width=10) + self.waiting_time_entry.insert(0, '10') + + self.integration_time_button.grid(column=0, row=2, sticky=(tk.EW)) + self.integration_time_list.grid(column=1, row=2, sticky=(tk.EW)) + self.waiting_time_button.grid(column=0, row=3, sticky=(tk.EW)) + self.waiting_time_entry.grid(column=1, row=3, sticky=(tk.EW)) + + self.source_var = tk.StringVar() + self.source_var.set('v') + ttk.Radiobutton(set_frame, text="Voltage (V)", variable=self.source_var, value='v', command=self.mode).grid(column=0, row=4, sticky=(tk.E, tk.W, tk.S)) + ttk.Radiobutton(set_frame, text="Current (A)", variable=self.source_var, value='i', command=self.mode).grid(column=1, row=4, sticky=(tk.E, tk.W, tk.S)) + + # Scan widgets --------------------------------- + scan_frame = ttk.Labelframe(iv_frame, text='Scan:', padding=(0, 5, 0, 15)) + scan_frame.columnconfigure(0, weight=1) + scan_frame.grid(column=0, row=3, sticky=(tk.EW)) + + self.start_var = tk.StringVar() + self.start_var.set('0.00') + self.start_label = ttk.Label(scan_frame, text='Start (V): ') + self.start_label.grid(column=0, row=0, sticky=(tk.E, tk.W, tk.S)) + ttk.Entry(master=scan_frame, width=10, textvariable=self.start_var).grid(column=1, row=0, sticky=(tk.E, tk.W, tk.S)) + + self.stop_var = tk.StringVar() + self.stop_var.set('1.00') + self.stop_label = ttk.Label(scan_frame, text='Stop (V): ') + self.stop_label.grid(column=0, row=1, sticky=(tk.E, tk.W, tk.S)) + ttk.Entry(master=scan_frame, width=10, textvariable=self.stop_var).grid(column=1, row=1, sticky=(tk.E, tk.W, tk.S)) + + self.step_var = tk.StringVar() + self.step_var.set('0.05') + self.step_label = ttk.Label(scan_frame, text='Step (V): ') + self.step_entry = ttk.Entry(master=scan_frame, width=10, textvariable=self.step_var) + + self.points_label = ttk.Label(scan_frame, text='Points (per decade): ') + self.points_list = ttk.Combobox(scan_frame, state="readonly", width=10) + + self.compliance_var = tk.StringVar() + self.compliance_var.set('0.01') + self.compliance_label = ttk.Label(scan_frame, text='Compliance (A): ') + self.compliance_label.grid(column=0, row=3, sticky=(tk.E, tk.W, tk.S)) + ttk.Entry(scan_frame, width=10, textvariable=self.compliance_var).grid(column=1, row=3, sticky=(tk.E, tk.W, tk.S)) + + # Combobox containing the available hardware + ttk.Label(scan_frame, text='Range: ').grid(column=0, row=4, sticky=(tk.E, tk.W, tk.S)) + self.range_list = ttk.Combobox(scan_frame, state="readonly", width=10) + self.range_list.grid(column=1, row=4, sticky=(tk.E, tk.W, tk.S)) + + # The "Run" button is only available if not in batch mode + if not self.in_batch: + self.run_button = ttk.Button(master=scan_frame, width=10, text='Run', command=self.start_stop_scan) + self.run_button.grid(column=1, row=98, sticky=( tk.E, tk.W, tk.S)) + + def fill_devices(self): + """ Fills the device selectors with the corresponding type of devices + + :return: None + """ + self.smu_box['values'] = self.dm.get_devices(['SMU']) + self.smu_box.current(0) + + self.select_smu() + + def select_smu(self, *args): + """ When the SMU selector changes, this function updates some variables and the graphical interface + to adapt it to the selected device. + + :param args: Dummy variable that does nothing but must exist (?) + :return: None + """ + + if self.smu is not None: + self.dm.close_device(self.smu) + + dev_name = self.smu_var.get() + self.smu = self.dm.open_device(dev_name) + + if self.smu is None: + self.smu_box.current(0) + self.smu = self.dm.open_device(self.smu_var.get()) + + elif self.dm.current_config[dev_name]['Type'] == 'SMU': + self.source_var.set('v') + self.range_list['values'] = self.smu.i_range + self.range_list.current(0) + self.integration_time_list['values'] = self.smu.int_time + self.integration_time_list.current(0) + self.points_list['values'] = self.smu.log_points + self.points_list.current(1) + self.mode() + + else: + self.smu_box.current(0) + self.smu = self.dm.open_device(self.smu_var.get()) + + # If the device has an interface to set options, we link it to the entry in the menu + interface = getattr(self.smu, "interface", None) + if callable(interface): + self.master.menu_hardware.entryconfig("SMU", state="normal") + else: + self.master.menu_hardware.entryconfig("SMU", state="disabled") + + def mode(self): + """ When the bias mode is changed, this function updates some internal variables and the graphical interface + + :return: None + """ + + if self.source_var.get() == 'v': + self.start_label['text'] = 'Start (V): ' + self.stop_label['text'] = 'Stop (V): ' + self.compliance_label['text'] = 'Compliance (A): ' + self.range_list['values'] = self.smu.i_range + self.start_var.set('0') + self.stop_var.set('1') + self.compliance_var.set('1e-3') + self.step_label.grid(column=0, row=2, sticky=(tk.E, tk.W, tk.S)) + self.step_entry.grid(column=1, row=2, sticky=(tk.E, tk.W, tk.S)) + self.points_label.grid_forget() + self.points_list.grid_forget() + + else: + self.start_label['text'] = 'Start (A): ' + self.stop_label['text'] = 'Stop (A): ' + self.compliance_label['text'] = 'Compliance (V): ' + self.range_list['values'] = self.smu.v_range + self.start_var.set('1e-9') + self.stop_var.set('1e-3') + self.compliance_var.set('2') + self.points_label.grid(column=0, row=2, sticky=(tk.E, tk.W, tk.S)) + self.points_list.grid(column=1, row=2, sticky=(tk.E, tk.W, tk.S)) + self.step_label.grid_forget() + self.step_entry.grid_forget() + + def update_integration_time(self): + """ Sets the value of the integration time. Informtaion IS sent to the instrument. + + :return: + """ + self.integration_time = int(self.integration_time_list.current()) + self.smu.update_integration_time(self.integration_time) + + def update_waiting_time(self): + """ Sets the value of the delay between applying a bias and taking the measurement. Informtaion IS sent to the instrument. + + :return: + """ + self.delay = int(self.waiting_time_entry.get()) + self.smu.update_waiting_time(self.delay) + + def update_compliance_and_range(self): + """ Sets the value of the variables compliance and meas_range. Information IS NOT sent to the instrument. + + :return: None + """ + self.compliance = float(self.compliance_var.get()) + self.meas_range = self.range_list.current() + + def start_stop_scan(self): + """ Starts and stops an scan + + :return: None + """ + if self.stop: + self.stop = False + + if self.batch.ready: + self.run_button['text'] = 'Stop batch' + else: + self.run_button['state'] = 'disabled' + self.prepare_scan() + else: + if self.batch.ready: + self.batch.ready = False + self.stop = True + self.run_button['text'] = 'Run' + self.run_button['state'] = 'disabled' + + def prepare_scan(self): + """ Any scan is divided in three stages: + 1) Prepare the conditions of the scan (this function), getting starting point, integration time and creating all relevant variables. + 2) Runing the scan, performed by a recursive function "get_next_datapoint" + 3) Finish the scan, where we update some variables and save the data. + + :return: None + """ + mode = self.source_var.get() + + start = float(self.start_var.get()) + stop = float(self.stop_var.get()) + step = float(self.step_var.get()) + points = int(self.points_list.current()) + + integration_time = int(self.integration_time_list.current()) + delay = int(self.waiting_time_entry.get()) + compliance = float(self.compliance_var.get()) + meas_range = self.range_list.current() + + self.options = {'source': mode, + 'start': start, + 'stop': stop, + 'step': step, + 'points': points, + 'compliance':compliance, + 'measRange':meas_range, + 'delay': delay, + 'intTime': integration_time} + + # # If we are in a batch, we proceed to the next point + if self.batch.ready: + self.batch.batch_proceed() + + print('Starting scan...') + + # Create the record array. In this case it is irrelevant + self.record = np.zeros((points+1, 3)) + self.record[:, 0] = np.linspace(0, 1, points+1) + self.record[:, 1] = np.ones_like(self.record[:, 1]) + self.record[:, 2] = np.ones_like(self.record[:, 1]) + + self.master.prepare_meas(self.record) + self.start_scan() + + def start_scan(self): + + measTime = self.smu.measure(**self.options) + self.master.window.after(int(measTime), self.get_data) + + def get_data(self): + + data0, data1 = self.smu.get_data() + + self.record = np.zeros((len(data0), 3)) + self.record[:, 0] = data0 + self.record[:, 1] = abs(data1) + self.record[:, 2] = data1 + + self.master.update_plot(self.record) + self.finish_scan() + + def finish_scan(self): + """ Finish the scan, updating some global variables, saving the data in the temp file and offering to save the + data somewhere else. + + :return: None + """ + + if self.batch.ready: + self.master.finish_meas(self.record, finish=False) + self.batch.batch_wrapup(self.record) + else: + self.master.finish_meas(self.record, finish=True) + self.stop = True + + if self.stop or not self.batch.ready: + # We reduce the number of counts in the batch to resume at the unfinished point if necessary + # In other words, it repeats the last, un-finished measurement + self.batch.count = self.batch.count - 1 + self.run_button['state'] = 'enabled' + + else: + self.prepare_scan() + diff --git a/Experiments/Dev_AMT/temperature_UNSW.py b/Experiments/Dev_AMT/temperature_UNSW.py new file mode 100644 index 0000000..ab4d508 --- /dev/null +++ b/Experiments/Dev_AMT/temperature_UNSW.py @@ -0,0 +1,357 @@ +__author__ = 'Diego Alonso-Álvarez' + +# Libraries +import datetime + +import matplotlib +matplotlib.use('TkAgg') +import matplotlib.pyplot as plt +import matplotlib.dates as mdates +from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg, NavigationToolbar2TkAgg + +from tkinter import filedialog, ttk +import tkinter as tk + +import time +import sys +import os + +import numpy as np + +# Class definition +class Temperature(object): + """ Some text + """ + def __init__(self, splash, devman, exp_number): + + self.dm = devman + self.experiment_number = exp_number + self.splash = splash + + self.create_interface() + + self.recording = False + self.heater_status = 'OFF' + self.ramp = 300 + + # We load the dummy devices by default + self.fill_devices() + + def _quit(self): + self.dm.close_device(self.control) + self.window.destroy() + self.splash.show(minus_experiment=True) + + def start_recording(self): + + if not self.recording: + self.recording = True + self.record() + else: + self.recording = False + + def enable_heater(self, event): + self.control.setHeater(self.heater_var.get()) + + def record(self): + + # At the begining, we update the first elements of the arrays + if self.temperature_array[0] == 0: + self.time_array = [datetime.datetime.now()] + self.setpoint_array = [self.control.getSP()] + self.temperature_array = [self.control.getTemp()] + # self.setpoint_var.set(self.control.setPoint) + # self.setpoint = self.control.setPoint + # self.updateSetpoint() + self.update_refresh_time() + time.sleep(1) + + newT = self.control.getTemp() + newSP = self.control.setPoint + PID = [self.P_var.get(), self.I_var.get(), self.D_var.get()] + self.temp_var.set('{0:.2f}'.format(newT)) + + self.time_array.append(datetime.datetime.now()) + self.setpoint_array.append(newSP) + self.temperature_array.append(newT) + + # if we are in a ramp, we update the setpoint + if abs(self.setpoint-newSP) > 0: + delta = self.time_array[-1] - self.time_ini + newSP = self.temp_ini + self.ramp*min(delta.seconds, self.time_to_SP) + self.countdown_var.set('{0:.2f}'.format(self.time_to_SP-delta.seconds)) + self.control.setSP(newSP) + self.current_setpoint_var.set('{0:.2f}'.format(self.control.setPoint)) + + self.control.setPID(PID) + self.update_plot(self.time_array, self.setpoint_array, self.temperature_array) + + if self.recording: + self.window.after(self.refresh_time, self.record) + + def update_plot(self, x, y1, y2): + + self.T_plot.lines[0].set_xdata(x) + self.T_plot.lines[0].set_ydata(y1) + self.T_plot.lines[1].set_xdata(x) + self.T_plot.lines[1].set_ydata(y2) + + self.T_plot.set_xbound(lower=x[0], upper=x[-1]) + self.T_plot.set_ybound(lower=np.min(y2), upper=np.max(y2)) + + self.canvas.draw() + + def update_refresh_time(self): + self.refresh_time = int(float(self.refresh_time_var.get())*1000) + + def reset(self): + self.temperature_array = [0] + + def updateSetpoint(self): + self.time_ini = datetime.datetime.now() + self.temp_ini = self.control.setPoint + self.setpoint = self.setpoint_var.get() + + ramp = self.ramp_var.get() + if ramp == 0: + ramp = 1 + + self.ramp = abs(float(ramp)/60) * (-1)**(self.setpoint < self.temp_ini) + self.time_to_SP = (self.setpoint - self.temp_ini)/self.ramp + + print('Ramp to {0:.2f}K in {1:.2f} s.\n'.format(self.setpoint, self.time_to_SP)) + + def saveRecord(self): + + f = filedialog.asksaveasfile(defaultextension='txt') + + if f is not None: + for i in range(len(self.time_array)): + savedata = self.time_array[i].isoformat() + '\t' + \ + '{0:.2f}'.format(self.temperature_array[i]) + '\t' +\ + '{0:.2f}'.format(self.setpoint_array[i]) + '\n' + f.write(savedata) + f.close() + + def select_controller(self, *args): + + if self.control is not None: + self.dm.close_device(self.control) + + dev_name = self.control_var.get() + self.control = self.dm.open_device(dev_name) + self.setpoint_var.set(self.control.getSP()) + self.setpoint = self.control.setPoint + self.current_setpoint_var.set(self.control.setPoint) + + if self.control is None: + self.control_box.current(0) + self.control = self.dm.open_device(self.control_var.get()) + self.setpoint_var.set(self.control.getSP()) + self.setpoint = self.control.setPoint + self.current_setpoint_var.set(self.control.setPoint) + + def fill_devices(self): + """ Fills the device selectors with the corresponding type of devices + + :return: + """ + + self.control_box['values'] = self.dm.get_devices(['Temperature_controller']) + self.control_box.current(0) + + self.control = None + self.select_controller() + + def create_menu_bar(self): + """ Creates the menu bar and the elements within + """ + self.menubar = tk.Menu(self.window) + self.window['menu'] = self.menubar + + self.menu_file = tk.Menu(self.menubar) + self.menu_hardware = tk.Menu(self.menubar) + self.menu_help = tk.Menu(self.menubar) + self.menubar.add_cascade(menu=self.menu_file, label='File') + self.menubar.add_cascade(menu=self.menu_hardware, label='Hardware') + self.menubar.add_cascade(menu=self.menu_help, label='Help') + + # File menus + self.menu_file.add_command(label='New experiment', command=self.open_new_experiment) + self.menu_file.add_command(label='Save record', command=self.saveRecord) + self.menu_file.add_separator() + self.menu_file.add_command(label='Leave Mordor', command=self._quit) + + # Hardware menu + self.menu_hardware.add_command(label='Hardware configuration', command=self.dm.show) + self.menu_hardware.add_separator() + + # Help menu + self.menu_help.add_command(label='Documentation', command=self.open_documentation) + + def open_new_experiment(self): + """ Opens the splash screen to run a new experiment, in paralel to the current one + + :return: None + """ + + self.splash.show() + + def open_documentation(self): + """ Opens the documentation in the web browser + + :return: None + """ + import webbrowser + address = 'file:' + os.path.join(sys.path[0], 'Doc', 'Mordor.html') + webbrowser.open_new_tab(address) + + def create_interface(self): + + # Top level elements + self.window = tk.Toplevel(self.splash.splashroot) + self.window.geometry('+100+100') + self.window.resizable(False, False) + self.window.protocol('WM_DELETE_WINDOW', self._quit) # Used to force a "safe closing" of the program + self.window.option_add('*tearOff', False) # Prevents tearing the menus + self.window.title('Barad-dûr: Mordor\'s temperature controller') + + self.create_menu_bar() + + # Creates the main frame + plot_frame = ttk.Frame(master=self.window, padding=(5, 5, 5, 5)) + control_frame = ttk.Frame(master=self.window, padding=(15, 15, 15, 15)) + plot_frame.pack(side=tk.RIGHT, fill=tk.BOTH, expand=1) + control_frame.pack(side=tk.LEFT, fill=tk.Y, expand=0) + control_frame.rowconfigure(99, weight=1) + + # Create plot area + self.create_plot_area(plot_frame) + + # Create variables + self.temp_var = tk.StringVar() + self.temp_var.set('-') + self.setpoint_var = tk.IntVar() + self.setpoint_var.set(300) + self.current_setpoint_var = tk.IntVar() + self.current_setpoint_var.set(300) + self.heater_options = ['OFF', 'LOW', 'MED', 'HIGH'] + self.heater_var = tk.StringVar() + self.heater_var.set(self.heater_options[0]) + #self.heaterButton_var = tk.StringVar() + #self.heaterButton_var.set('Enable heater') + self.ramp_var = tk.StringVar() + self.ramp_var.set(20.0) + self.countdown_var = tk.IntVar() + self.countdown_var.set(300) + self.refresh_time_var = tk.StringVar() + self.refresh_time_var.set(1) + self.visualize_min_var = tk.IntVar() + self.visualize_min_var.set(60) + self.visualize_var = tk.IntVar() + self.visualize_var.set(0) + self.P_var = tk.StringVar() + self.P_var.set('10') + self.I_var = tk.StringVar() + self.I_var.set('50') + self.D_var = tk.StringVar() + self.D_var.set('0') + + # Create the elements in the control panel + # Hardware widgets + hardware_frame = ttk.Labelframe(control_frame, text='Selected hardware:', padding=(0, 5, 0, 15)) + hardware_frame.columnconfigure(0, weight=1) + hardware_frame.grid(column=0, row=0, sticky=(tk.EW)) + + self.control_var = tk.StringVar() + self.control_box = ttk.Combobox(master=hardware_frame, textvariable=self.control_var, state="readonly") + self.control_box.bind('<>', self.select_controller) + self.control_box.grid(column=0, row=0, sticky=(tk.EW)) + + # Temperature frame + temp_frame = ttk.Labelframe(control_frame, text='Temperature (K):', padding=(5, 5, 5, 15)) + temp_frame.grid(column=0, row=1, sticky=(tk.NSEW)) + temp_frame.rowconfigure(1, weight=1) + temp_frame.columnconfigure(0, weight=1) + + ttk.Label(master=temp_frame, textvariable=self.temp_var, anchor=tk.CENTER).grid(column=0, row=0, sticky=tk.EW) + ttk.Button(master=temp_frame, width=10, text='Record', command=self.start_recording).grid(column=0, row=1, sticky=tk.EW) + + # Set frame + set_frame = ttk.Labelframe(master=control_frame, text='Set:', padding=(5, 5, 5, 15)) + set_frame.grid(column=0, row=2, sticky=(tk.NSEW)) + set_frame.rowconfigure(1, weight=1) + set_frame.columnconfigure(0, weight=1) + + ttk.Label(master=set_frame, text="Target set point (K):").grid(column=0, row=0, sticky=tk.EW) + ttk.Label(master=set_frame, text="Ramp rate (K/min):").grid(column=0, row=1, sticky=tk.EW) + ttk.Label(master=set_frame, text="Countdown (s):").grid(column=0, row=2, sticky=tk.EW) + ttk.Label(master=set_frame, text="Current set point (K):").grid(column=0, row=3, sticky=tk.EW) + ttk.Label(master=set_frame, text="P:").grid(column=0, row=4, sticky=tk.EW) + ttk.Label(master=set_frame, text="I:").grid(column=0, row=5, sticky=tk.EW) + ttk.Label(master=set_frame, text="D:").grid(column=0, row=6, sticky=tk.EW) + + ttk.Entry(master=set_frame, width=10, textvariable=self.setpoint_var).grid(column=1, row=0, sticky=tk.EW) + ttk.Entry(master=set_frame, width=10, textvariable=self.ramp_var).grid(column=1, row=1, sticky=tk.EW) + ttk.Label(master=set_frame, textvariable=self.countdown_var).grid(column=1, row=2, sticky=tk.E) + ttk.Label(master=set_frame, textvariable=self.current_setpoint_var).grid(column=1, row=3, sticky=tk.E) + ttk.Entry(master=set_frame, width=10, textvariable=self.P_var).grid(column=1, row=4, sticky=tk.EW) + ttk.Entry(master=set_frame, width=10, textvariable=self.I_var).grid(column=1, row=5, sticky=tk.EW) + ttk.Entry(master=set_frame, width=10, textvariable=self.D_var).grid(column=1, row=6, sticky=tk.EW) + + ttk.Button(master=set_frame, width=10, text='Set', command=self.updateSetpoint).grid(column=1, row=7, sticky=tk.EW) + #ttk.Button(master=set_frame, width=10, textvariable=self.heaterButton_var, command=self.enable_heater).grid(column=0, row=8, sticky=tk.EW) + tk.OptionMenu(set_frame, self.heater_var, *self.heater_options, command=self.enable_heater).grid(column=0, row=7, sticky=tk.EW) + + # Visualize frame + visuialize_frame = ttk.Labelframe(master=control_frame, text='Visualize:', padding=(5, 5, 5, 15)) + visuialize_frame.grid(column=0, row=3, sticky=(tk.NSEW)) + visuialize_frame.rowconfigure(1, weight=1) + visuialize_frame.columnconfigure(0, weight=1) + + ttk.Button(master=visuialize_frame, width=10, text="Refresh time (s):", command=self.update_refresh_time)\ + .grid(column=0, row=0, sticky=tk.EW) + ttk.Entry(master=visuialize_frame, width=10, textvariable=self.refresh_time_var).grid(column=1, row=0, sticky=tk.EW) + ttk.Button(master=visuialize_frame, width=10, text='Reset record', command=self.reset).grid(column=0, row=1, columnspan=2, sticky=tk.EW) + + + # ttk.Radiobutton(master=visuialize_frame, text="All", variable=self.visualize_var, value=0).grid(column=0, row=1, sticky=tk.EW) + # ttk.Radiobutton(master=visuialize_frame, text="Last (min):", variable=self.visualize_var, value=1).grid(column=0, row=2, sticky=tk.EW) + # ttk.Entry(master=visuialize_frame, width=10, textvariable=self.visualize_min_var).grid(column=1, row=2, sticky=tk.EW) + + # # These commands give the control to the HW window. I am not sure what they do exactly. + # self.TC_window.lift(self.master) # Brings the hardware window to the top + # self.TC_window.transient(self.master) # ? + + def create_plot_area(self, frame): + """ Creates the plotting area and the ploting variables. + """ + + xlabel = 'Time' + ylabel = 'Temperature (K)' + + self.time_array = [datetime.datetime.now()] + self.temperature_array = [0] + self.setpoint_array = [0] + + f = plt.figure(figsize=(9, 8), dpi=72) + self.T_plot = plt.subplot(111, ylabel=ylabel, xlabel=xlabel) + self.T_plot.grid(True, color='gray') # This is the grid of the plot, not the placing comand + + self.T_plot.plot(self.time_array, self.setpoint_array, label='Set Point') + self.T_plot.plot(self.time_array, self.temperature_array, 'o', label='Temperature') + + f.autofmt_xdate() + myFmt = mdates.DateFormatter('%H:%M:%S') + self.T_plot.xaxis.set_major_formatter(myFmt) + self.T_plot.legend(loc='lower left') + f.tight_layout() + + self.canvas = FigureCanvasTkAgg(f, frame) + self.canvas.get_tk_widget().pack() + self.canvas.show() + + toolbar = NavigationToolbar2TkAgg(self.canvas, frame) + toolbar.update() + self.canvas._tkcanvas.pack(side=tk.TOP, fill=tk.BOTH, expand=1) + diff --git a/Experiments/Dev_AMT/temperature_batch_UNSW.py b/Experiments/Dev_AMT/temperature_batch_UNSW.py new file mode 100644 index 0000000..b2e0c83 --- /dev/null +++ b/Experiments/Dev_AMT/temperature_batch_UNSW.py @@ -0,0 +1,162 @@ +__author__ = 'Andrew M. Telford & D. Alonso-Álvarez' + +import os +import numpy as np +import datetime +import time + +import tkinter as tk +from tkinter import ttk +from tkinter import filedialog + +from Devices.Dummy_T_UNSW import Dummy_T_UNSW + +class Temperature_batch: + """ Base class for the batch mode in spectroscopy experiments """ + + def __init__(self, master, fileheader=''): + """ Constructor of the Batch class + + :param root: The main window of the program + :param devman: Device manager + :param mode: The type of batch to be done (default='Dummy') + :return: None + """ + ## Setup Lakeshore336 temperature controller + self.T_controller = Dummy_T() + self.master = master + self.header = fileheader + self.timeZero = 0 + + self.create_interface() + + def create_interface(self): + + time_frame = ttk.Frame(self.master.control_frame) + time_frame.grid(column=0, row=0, sticky=(tk.EW)) + time_frame.columnconfigure(0, weight=1) + + # Set widgets --------------------------------- + set_frame = ttk.Labelframe(time_frame, text='Set:', padding=(0, 5, 0, 15)) + set_frame.grid(column=0, row=0, sticky=(tk.EW)) + set_frame.columnconfigure(0, weight=1) + + self.temperatureStart_var = tk.IntVar() + self.temperatureStart_var.set('300') + ttk.Label(set_frame, text='Temperature Start [K]: ').grid(column=0, row=0, sticky=(tk.EW)) + ttk.Entry(set_frame, width=15, textvariable=self.temperatureStart_var).grid(column=0, row=1, sticky=(tk.EW)) + + self.setPoint = self.temperatureStart_var.get() + + self.temperatureEnd_var = tk.IntVar() + self.temperatureEnd_var.set('350') + ttk.Label(set_frame, text='Temperature End [K]: ').grid(column=1, row=0, sticky=(tk.EW)) + ttk.Entry(set_frame, width=15, textvariable=self.temperatureEnd_var).grid(column=1, row=1, sticky=(tk.EW)) + + self.temperatureStep_var = tk.IntVar() + self.temperatureStep_var.set('10') + ttk.Label(set_frame, text='Temperature Step [K]: ').grid(column=2, row=0, sticky=(tk.EW)) + ttk.Entry(set_frame, width=15, textvariable=self.temperatureStep_var).grid(column=2, row=1, sticky=(tk.EW)) + + # Countdown widgets --------------------------------- + #countdown_frame = ttk.Labelframe(time_frame, text='Estimated time to next point (s):', padding=(0, 5, 0, 15)) + #countdown_frame.grid(column=0, row=1, sticky=(tk.EW)) + #countdown_frame.columnconfigure(0, weight=1) + + #self.next_var = tk.StringVar() + #self.next_var.set('') + #ttk.Label(countdown_frame, textvariable= self.next_var, anchor=tk.CENTER).grid(column=0, row=0, sticky=(tk.EW)) + + + def batch_ready(self): + """ After selecting all the conditions for the batch, we arrange the last things, select the folder to save + the data and fill the batch list. The batch is then ready to be launched. + + :return: None + """ + + rootname = self.master.get_rootname() + if rootname == '': return + + temp = filedialog.askdirectory(initialdir=self.master.path) + if temp == '': + return + else: + self.master.path = temp + + start = self.temperatureStart_var.get() + end = self.temperatureEnd_var.get() + step = self.temperatureStep_var.get() + steps = 1+(abs(end-start)//step) + self.stepList = range(steps) + self.master.count = 0 + self.master.batch_length = steps + self.rootname = os.path.join(self.master.path, rootname) + self.master.data_file = self.rootname + '.txt' + self.suffix = self.master.suffix_var.get() + self.master.populate_batch_list(['Step {0}'.format(i+1) for i in self.stepList]) + self.master.ready = True + + def batch_proceed(self): + """ This function is called from the main program to execute the next step in the batch. + The data and the filename of this measuremtn are stored in the "step_info" variable. + + :return: None + """ + ## Make summary file + if self.master.count == 0: + self.timeZero = datetime.datetime.now() + self.master.save_batch_data(['Step', 'Temperature (K)','Start time', 'End time', 'Duration (s)', 'Filename']) + + self.ini_time = datetime.datetime.now() + + + ## Send SetTemp command to Lakeshore + if self.master.count == 0: + self.T_controller.setSP(self.temperatureStart_var.get()) + self.temperature = self.T_controller.getTemp() + else: + self.setPoint = self.setPoint + self.temperatureStep_var.get() + self.T_controller.setSP(self.setPoint) + self.temperature = self.T_controller.getTemp() + print(self.setPoint) + print(self.temperature) + + ## ADD OPC SP = Temp + + if self.suffix == 0: + self.filename = self.rootname + '_{0}.txt'.format(self.ini_time.strftime('%y%m%d_%H-%M-%S-%f')) + else: + self.filename = self.rootname + '_{0}.txt'.format(str(self.master.count+1).zfill(3)) + + self.step_info = [self.master.count+1, self.temperature, self.ini_time.strftime('%Y-%m-%d %H:%M:%S.%f'), self.filename] + self.step_string = 'Step = {0} \tTemperature (K) = {1} \tIni time = {2} \tFilename = {3}'.format(*self.step_info) + + print('Next point in batch {0}/{1}:\n\t {2}'.format(self.master.count+1, self.master.batch_length, self.step_string)) + + self.master.update_batch_list() + + def batch_wrapup(self, data): + """ After taking a measurement in the main function, this function takes the measured data and saves it + following the convention of the batch + + :param data: data to be saved + :return: None + """ + end_time = datetime.datetime.now() + + self.step_info.insert(-1, end_time.strftime('%Y-%m-%d %H:%M:%S.%f') ) + self.step_info.insert(-1, (end_time - self.ini_time).total_seconds() ) + + self.master.save_batch_data(self.step_info) + + np.savetxt(self.filename, data, fmt='%.4e', delimiter='\t', + header='{0}\n{1}'.format(self.step_string, self.header)) + + self.master.count = self.master.count + 1 + if self.master.count == self.master.batch_length: + self.master.update_batch_list(True) + self.master.ready = False + self.master.count = 0 + + print('Temperature batch completed!!\n') diff --git a/Experiments/__init__.py b/Experiments/__init__.py index 521b2d3..1f2e0e7 100644 --- a/Experiments/__init__.py +++ b/Experiments/__init__.py @@ -3,3 +3,4 @@ from .temperature import Temperature from .flash import Flash from .cv import CV +from .photoreflectance import Photoreflectance diff --git a/Experiments/__pycache__/DLCP_batch.cpython-36.pyc b/Experiments/__pycache__/DLCP_batch.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..2993e43bc7a3bea3b01214dded44a57a4227503a GIT binary patch literal 4831 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 V+$ZKw>L`#{$aKO)T~({b{{Uz^@2&s< literal 0 HcmV?d00001 diff --git a/Experiments/__pycache__/__init__.cpython-35.pyc b/Experiments/__pycache__/__init__.cpython-35.pyc new file mode 100644 index 0000000000000000000000000000000000000000..90f2b3dd06c742ad11a9f4a42b94fbe5f50e18e3 GIT binary patch literal 426 zcmZ9I-%7(U6vj{5b=}q}yYm@#p|DL6L`0brb}|1IA6~1oa771!QNhLJDNPc0l+)O&u}Qaq|g}&MaDn?NCqJTX4wJZfOH{r zD|SKDKza~*71u!2LHZE-6?-5WAe#_2E3SiRfeauFh<%uL8y}3W>awyuEqH2B=~NXP zVrTq78qByXI3{L=oY1=wiC)<5&~`g_3uP4YxuB!TGUd13cG=c{jL^QQbn-R2HVMqH z9oB5cn_EZpcH7c5)@{-${`sO$^%rd_24W6RW3%w+ld}(wP$N4fhaeP#4%Eq}=*-G#W`xClF(qZ583ko}H Ai2wiq literal 0 HcmV?d00001 diff --git a/Experiments/__pycache__/__init__.cpython-36.pyc b/Experiments/__pycache__/__init__.cpython-36.pyc index e6b486f5354aa58235f3080bb04ff6c3ecfdd878..cc23627bfcef77d7e8354cb029c6eb3313c44883 100644 GIT binary patch delta 238 zcmcb`w1HXKn3tEU%GEb|7Xt&sV<3hDCLqfJh>LwDDmw@=q_E|1<#I=HGcu&G2Qz4L zOw6zp*JQjU5Rj2yl3$dXmXn%Xl9-pA>Zi#wafx&NEr9|wi6VBOVJjJm_<$6c_?7N# z6%$&VT2vfUWo&9>Vq|C@Ll ANdN!< delta 141 zcmdnMe2Yohn3tD}jlDIhn}LDhF%ZK66OiQq#KjI1l^s~wf*CZ~Cze<;`)P7bJmT!I zlA(wPCfF|%TLLdN{*diMM diff --git a/Experiments/__pycache__/__init__.cpython-37.pyc b/Experiments/__pycache__/__init__.cpython-37.pyc new file mode 100644 index 0000000000000000000000000000000000000000..0d5192401588c5de3a7e605e63352db18dc3eb04 GIT binary patch literal 406 zcmZ9JT}s115XZCmXwoK?ULy|*Z52U8)LNmSBDDGt10f_^8r;o>*==aOgU_D9D|nQ= z`s5XSayC#DXW5zG%5KwB0@Bq%;Z*N zwQMn)JCW0}%{ttT+?E~Y@ov;@xx;$gk9=fzZiLU}^BxuN>IA;w-fy zIPJ2mhK`0o^-mXEEWaDDhoN=fv&l*PASuZBWqdR`91V}-D=MFrsA4W47qHJ$sVF2D sw~L(C!*LoYehH~RAt1Z)1Hjm8-v9sr literal 0 HcmV?d00001 diff --git a/Experiments/__pycache__/batch_control.cpython-35.pyc b/Experiments/__pycache__/batch_control.cpython-35.pyc new file mode 100644 index 0000000000000000000000000000000000000000..24ef9caba2861bb4f8384b628c5f921105a3d016 GIT binary patch literal 9513 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 diff --git a/Experiments/__pycache__/batch_control.cpython-37.pyc b/Experiments/__pycache__/batch_control.cpython-37.pyc new file mode 100644 index 0000000000000000000000000000000000000000..8cb41aebe099cdc51b887796e6491358ac352d58 GIT binary patch literal 8510 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 literal 0 HcmV?d00001 diff --git a/Experiments/__pycache__/cv.cpython-35.pyc b/Experiments/__pycache__/cv.cpython-35.pyc new file mode 100644 index 0000000000000000000000000000000000000000..0b90ec6098267c1fb8c4d69c81ee969027b42f60 GIT binary patch literal 17093 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 literal 0 HcmV?d00001 diff --git a/Experiments/__pycache__/cv.cpython-36.pyc b/Experiments/__pycache__/cv.cpython-36.pyc index 45364afc2ae7c3767dc6bfaf5fe8d0b5b417bf77..fce093f4d0c9eadb381528e93305ca11185aaa24 100644 GIT binary patch delta 3557 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 L`WW)&P|JS-(Zc~w diff --git a/Experiments/__pycache__/cv.cpython-37.pyc b/Experiments/__pycache__/cv.cpython-37.pyc new file mode 100644 index 0000000000000000000000000000000000000000..4dc6531cf4761a7fb1ba1c481d73887109d06230 GIT binary patch literal 14421 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 literal 0 HcmV?d00001 diff --git a/Experiments/__pycache__/flash.cpython-36.pyc b/Experiments/__pycache__/flash.cpython-36.pyc index 38239e1bf628a92f59fceab0e82acf2ca468efeb..2663dbd527fc005d7016b9f15555668a58245d6e 100644 GIT binary patch delta 103 zcmeB)TN%e;%*)GFfa1K#H?fOlWaxQE^O_v8j=Xk)e5vdwza;PO5@SQD#|c zOh9ExMt)v=d45rLW?p)HN@h`Na!GzsWlU~fa%vAeTXOlWaxQE^O_V_srTC6MWulADk diff --git a/Experiments/__pycache__/flash.cpython-37.pyc b/Experiments/__pycache__/flash.cpython-37.pyc new file mode 100644 index 0000000000000000000000000000000000000000..3bf605c768098e6fc11e6ba3d0ac72a5a9974122 GIT binary patch literal 11946 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 E1F{^;-v9sr literal 0 HcmV?d00001 diff --git a/Experiments/__pycache__/iv.cpython-35.pyc b/Experiments/__pycache__/iv.cpython-35.pyc new file mode 100644 index 0000000000000000000000000000000000000000..05c643e8893b8498354afda52d2444167c2c66ad GIT binary patch literal 12515 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 literal 0 HcmV?d00001 diff --git a/Experiments/__pycache__/iv.cpython-36.pyc b/Experiments/__pycache__/iv.cpython-36.pyc index 25abc36564b98794613860ca328cc882c57516e3..c86c368adc0ac780f9d2a9d3324e1d8e2ea82141 100644 GIT binary patch delta 103 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 literal 0 HcmV?d00001 diff --git a/Experiments/__pycache__/photoreflectance.cpython-35.pyc b/Experiments/__pycache__/photoreflectance.cpython-35.pyc new file mode 100644 index 0000000000000000000000000000000000000000..d2e7738e53217e7377edaa6c5aaaa9d4dcba0fda GIT binary patch literal 26477 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 literal 0 HcmV?d00001 diff --git a/Experiments/__pycache__/spectroscopy.cpython-35.pyc b/Experiments/__pycache__/spectroscopy.cpython-35.pyc new file mode 100644 index 0000000000000000000000000000000000000000..66d32ba7185aa279d63f1ed601d5739f8f5172ba GIT binary patch literal 20815 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 diff --git a/Experiments/__pycache__/spectroscopy.cpython-37.pyc b/Experiments/__pycache__/spectroscopy.cpython-37.pyc new file mode 100644 index 0000000000000000000000000000000000000000..996b0b5227813427bb3b0aa1b25fcf3ddd06b354 GIT binary patch literal 18401 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 J9`rA{{}T#C-WmV^ literal 0 HcmV?d00001 diff --git a/Experiments/__pycache__/temperature.cpython-35.pyc b/Experiments/__pycache__/temperature.cpython-35.pyc new file mode 100644 index 0000000000000000000000000000000000000000..03e7062551a1ca77e9b8feb5b0baeceea80ff0b6 GIT binary patch literal 11803 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 It*SU90RI_5qW}N^ delta 183 zcmZ4IJH?mFn3tD}jlDH0*mNV;aVBG5XRDad;?$zzm@3D-#GFbX(=jDCGcU#^wK%&Z zzaSr{jo*?BmmI5;>sH&0g;X9NI5bvC&G diff --git a/Experiments/__pycache__/temperature.cpython-37.pyc b/Experiments/__pycache__/temperature.cpython-37.pyc new file mode 100644 index 0000000000000000000000000000000000000000..d3162bdc0e29cecf58aa99dca62af5f6aaae8460 GIT binary patch literal 10179 zcmb7K&2Jk?c4vRG`KeFKk}cak{+Kbxw#N2+D_&=2Y-#L_EgQCWoG@MxEmeymMY1{7 zO-t&Pfla)-NCIfHNe=nMf_Lzw zx<1~ke)a0rt5>gH@rzO^pTYCT`%AxRewxYrCshWIiNa@i!#_mAGSv*znBLN=n#}d8 z?(2Pn83&rCFlO7T+DSRvcB(m9He302p;~Act3_G2TBUZmT5gY3$C9?C>bRshQJs+a zWOY*JQ`IS%PghTTnPE1|{y4+3{>&?*dXni+GZm+I0*u{l`1Qd3ycKl9;My;LtM$U; zetZZ-W<^-5Tc6kK6F|9JJ1FG+I7u zc&(r=j40Y#Cu)O7N8vNP;h!M!Gmu86sxz%>Fr67c&QwiiG7Gt-IIh~vVL8;ZEYAwa z9adx|!n@ggw>bJW-_aclt9&?`tZQqUjoha|(S*N+BhjJRx9lYTu zNcQx-Of9o#Al3FXq^S3egAC0TY9=GR#RzW$la-hZdNfy-h4E;4xp7s?U~YuY+G4`r z0Sk?`--&9SUVF{w!VbGFFWeO4>)iJuzt-qPKHu=xeUan-dcawuQ?Hgb3CC+;1olH= za54y_Q9RS=id)~9zKuX z|M2DqH*b9W{H`BvMM3v@8@&bmTH6b$ug@Q@g368ez2{3Q)8X^zNQevFUE$Qga3iYK z=15-SNHV&u;io$)H{$6bxi1WwuR|vCq1?bL<^3)SdzogY56L!lT1Uu@zDY7g$!gl9 z|NDS5lCZI|TG7OKyV2=|wIj(^vOEVuqPX4)LO&6Z$gT&ShzBiUZ#OzD*cMslhXCv< ziJ9SMuwBtO4M7S@CDTO6s(sUIM3+$vZ6q13tmU8q!zZDGL`C3n(pX~Rgy+DgUbv%pt#p oYAEuFI>NLFqwYuGoa81f{{=MrxcEwb$6IUp zHTBVlXc$T^w0Lfq(~(xGC@nN6p-Vnn&=%s8X>)kDbpC6R*3|m?L8eY?5o@q%_N}Cb z)rcC_qr{Qe9TIz#>*v`(wZ6?GW0cp8mqdHKKf#RtgW3xkY2=l=|E3bn6<)bfgVy4XMcEoapC61%ftV{Q+ZoVL`Sxu=5g-9959%v z2=+}F9W1dt6%|VAkg%XEPalgiRH+-l(kWau3oxmInCf+j0E?5)ReJTsp`y*!H9IIm#T$c0G(d%aZZ*!9UUQUV)*`D zQAmc?Z3U5B=bTKYu)GZzTTvVyF+T%L#gc2PYIgkX)vAT`>Eo(Flq}}AU<1gS9~mk6 z4eeLq0+NhThLOV0bhNyl*BotLo75+@qED~Uh>x=Uuvcp&a=;rMKNMueYCBLePZf7% zF)yKOm{lfZc~_P7PM0r&E_A`$5%#)tmxRfZJxFx^xYeHZyukKU&V*d@rI;L z8AxCL^jU3Qk53Lud`L4=yGJW7wa@dYM43qLie#P1icBMw{s&oUeWmeV2I-72lS#Zlxf*whzgL00_F!yAHb^Zy{ zUusc4D(qHHlSneK5qYgB z?T4wKhpmWDy{-_0&(gUewN1C|Jyb9^kT^MRQudjx9N*cgMtUSS>{pQf-OLZ}$&Jq3 zBMX5ooEax!5BjuGWeIkmp?(B&QkaD;(%94bIb~J8x%e!D+L4X0S8!z6iO5l8{)X3U zkqYmnB{3>5hgsI85a3b z<5b*O_tH{)Nnigx5{L{=C4@YS61*d-r~V3jX(?!}Z;`2nGE;jpwzPoYAtf!bSb)m| zT2Q!VZh!%8hyM*Q@RiuW4YfLgKln7EBZZY~+b!&!17l z_bI7RBG(M94SpNR(Z_4S4$c5-hprgPcAFlr!xg|Wr`GY>{_&}qz=6tNk7`N(Eua4m zNQS47WUwg8^unm;wQ-cNmCAB_W@NsOQ%I+c3_MNU9lYU9Bys|h00ocW}$bB4+JBulxI;?v`Vi5KVuhsKI=^U<9Y<1o$z>6CkM|l$?;$+O#Y?4j+ zjy-1#s6WH24l(vL434eEv%^vxKIo1JFx?^l8w8^C+z3IDX`vU9h+6{;+z1k3(oWT^ zks6sB0|p0<2}LgwcSJ!f#4$!8Hp96;X=64WMqlSxb_haSN&X1ZFVNHxb+pF%Y8F$R z1qiLSQnP#nOWWJP7Jwa7I99~um*S7yJ2GaVrXN9to{OlB>vfo%b*LT%8k8P1I_azm zlbQ+>n&V?z_y(ebcjT@4E#chrINL_RNSOEhRyQuM_}lK#$y(Vxjqn1C3r}IZ zT;(yw#Rops9?B>}JToBWV)<-Pv`L1RMCop@-Xp$bb)_;c^k_?%55bc1h_l2U#vyW4 zs7V`KlEuvda|*Kw4n@IZthVlj>mKuyisDU7Nh1Njc_=i%>%@MEz6t%V2bQoh<^hu$ z%uf?`QW=+FF5j%VVk`;SNan!tU*YrcEE00Ri@I{brPF6nn%5@beCLhP1Nt!^j#&dgGSx*ThxY(Mq&}& zpyR)82;3C{lWCx51NeJEv^8=2)|bAUgS;$IsJzCO!7 zl2XVq8qEiZj?f&D1Ai*T(s+)r1KQ_P+M{C{;4h^3(Qys%7gPM`*ly6+*vH`D!({$* z?31C9(EUhlpp50%$`{-Hhyc_u*f3&gDqp-{u*tgj=_RD|di;0W%4z~WIxE;A!biaX9 zO2s^+gDl@4Ne~;e)zyy3cCi8BD1e6Kee<7>CKphx7`RVrz^5|F`4%>uKsbCV8oW+; z4HqeXqrMq^t6h^6yzOz1bRSM?9C3+;M5 z`BWZ_eC7_^x<#Cr=~SfnmL*~ge4BJ;=f6)C15vo3)oWvq#YT?Xv#7DYg}^f$VHR{+ zyVz%cMm-hj6U0gyboC%Cx;XRW>07t%1nso|XTd@5w{OSeLp!W{`Kt=?F)BJKpoB92 z1(CPC&iZD+5A(93j_>$Pc5Q7pShUIi6e#`zNo*oSvN-r$f@vs+w#Vokl3Nw)j~5T=q!jD- z7v;!e{qEu+IST`({t{(5Ft~U$1~o_nd>@iup@xzFx)JugRwMQoh4Zx{KXOE=avD73JmMg;YLMIF=Vgtd(D7*x3Ht#1kI3G{JQ zm|r|vy(i2EOUtW5`wax%gYCfq#ZNC`DPoMf2_He?rx9Xk^{){;r9-+5Gzj~CCnAUp zJy4jTT25}n6gMG1M{R$dlJ8NnN^K@y4CGhCB+8qE(h-Q_dhbzNa(?*@BqINS4rLp1 z3}Z@$N35E4-e5vsdd4@Ytus(;dGZ$K8ZJ3`hcM>K)1_y^lC*?4~tv=tWV` z;dELN))FkouF#fvRd)MGiu56!$j$>Gqr&|ADB)&xT(*$%QHNpJ?e<*-5Ddcfin8INHiZ`U?mNCW=Un%3_g3eVAYU7BuOlxH$Z&2iA z@Y22Pgg4Kp<#}9}q(9x&T>e`#Q2k_S5(Fh|ru-DM`Z8PTmb&OFS9qfNCe z!^H?3Ku0=o6k%`>b%2x23Q6R_X;LJCHcFHJTOplqYBhiw^iK=Gu@UW=1BV{5yzv7K zt`)T`Be?>DHZ*Y0*Z&UM;+cnf?Xwxs06GELc6TnI1Cif-c^whdq?K57j;~0Vz;xf z00SQ9U=v+=0f`f^42Ea{r>TvdOZOHpJzTu>G|tN_D7WQrP}tdM1?%!Y0LJzY(2&!= zhxi{MS*e^E3j1Fs@FhxQEZOL`=;~7bO$WD-y)_y+Un2ZJA%Rgt9ifa~6l}EBm6neU zo~}qKyjCe<&PBi znQvNoD2G3d`xuquQHhG0I0B;(OoO|48Lled^|IiCc GjsFFZF*P#) literal 0 HcmV?d00001 diff --git a/Experiments/__pycache__/temperature_batch.cpython-36.pyc b/Experiments/__pycache__/temperature_batch.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..e6d7c15bac6e43a54cd139294021d9fd95285def GIT binary patch literal 5092 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 U6hX38BoHxLy100PU~rN7FU~ew&Hw-a literal 0 HcmV?d00001 diff --git a/Experiments/batch_control.py b/Experiments/batch_control.py index 5537212..2600338 100644 --- a/Experiments/batch_control.py +++ b/Experiments/batch_control.py @@ -40,9 +40,12 @@ def __init__(self, root, devman, fileheader='', mode='Dummy'): self.mode = mode elif mode == 'Temperature': - self.window.withdraw() - messagebox.showinfo(message='{0} batch mode not implemented, yet.'.format(mode)) - self.mode = 'Dummy' + from Experiments.temperature_batch import Temperature_batch + self.batch_data = Temperature_batch(self, fileheader=fileheader) + self._batch_ready = self.batch_data.batch_ready + self._batch_proceed = self.batch_data.batch_proceed + self._batch_wrapup = self.batch_data.batch_wrapup + self.mode = mode elif mode == 'Time': from Experiments.time_batch import Time_batch @@ -52,6 +55,14 @@ def __init__(self, root, devman, fileheader='', mode='Dummy'): self._batch_wrapup = self.batch_data.batch_wrapup self.mode = mode + elif mode == 'DLCP': + from Experiments.DLCP_batch import DLCP_batch + self.batch_data = DLCP_batch(self, fileheader=fileheader) + self._batch_ready = self.batch_data.batch_ready + self._batch_proceed = self.batch_data.batch_proceed + self._batch_wrapup = self.batch_data.batch_wrapup + self.mode = mode + else: self.window.withdraw() self.mode = 'Dummy' @@ -167,7 +178,7 @@ def interface(self): batch_list_scroll.grid(column=1, row=0, sticky=(tk.NS)) def disable(self): - """ Dissables the batch mode. + """ Disables the batch mode. :return: None """ @@ -175,7 +186,7 @@ def disable(self): messagebox.showinfo(message='Batch mode disabled') def populate_batch_list(self, new_list): - """ Fills the batch list with the bias steps that will be done + """ Fills the batch list with the steps that will be done :param new_list: The list of steps to be done :return: None @@ -266,11 +277,9 @@ def batch_wrapup(self, data): """ self._batch_wrapup(data) -if __name__ == '__main__': - - root = tk.Tk() - dm = device_manager.Devman(root) - b = Batch(root, dm) - root.mainloop() - +#if __name__ == '__main__': +# root = tk.Tk() +# dm = device_manager.Devman(root) +# b = Batch(root, dm) +# root.mainloop() diff --git a/Experiments/cv.py b/Experiments/cv.py index 8894ac5..62327f8 100644 --- a/Experiments/cv.py +++ b/Experiments/cv.py @@ -8,7 +8,7 @@ from tkinter import messagebox class CV: - """ Base class for impedance analysis experiments """ + """ Base class for impedance sweep experiments """ def __init__(self, master, devman, in_batch=False): self.master = master @@ -26,24 +26,18 @@ def __init__(self, master, devman, in_batch=False): 'Ch2_ylabel': 'Tz (deg)', 'Ch1_scale': 'log', 'Ch2_scale': 'linear'} - - top0 = 'Bias = ' - top1 = '0 V' - col0 = self.plot_format['xlabel'] - col1 = self.plot_format['Ch1_ylabel'] - col2 = self.plot_format['Ch2_ylabel'] - self.header = str(top0)+'= '+str(top1)+'\n'+str(col0)+'\t'+str(col1)+'\t'+str(col2) + + self.header = '' self.header.encode('utf-8') - self.extension = 'txt' self.batch = Batch(self.master, self.dm, fileheader=self.header) ## Dummy mode - + # Create the interface self.create_interface() - + # We load the dummy devices by default self.fill_devices() - + def quit(self): """ Safe closing of the devices. The devices must be closed by the Device Manager, not directly, so they are registered as "free". @@ -79,10 +73,12 @@ def create_menu_bar(self): self.master.menu_batch.add_command(label='Disable', command=self.batch.disable) self.master.menu_batch.add_command(label='Temperature', command=lambda: self.new_batch('Temperature')) self.master.menu_batch.add_command(label='Time', command=lambda: self.new_batch('Time')) - + self.master.menu_batch.add_command(label='DLCP', command=lambda: self.new_batch('DLCP')) + def new_batch(self, batch_mode): """ Shows current batch window or, if a different batch is chosen, destroys the old one and creates a new one for the new batch mode + :param batch_mode: the selected type of batch :return: None """ @@ -93,9 +89,7 @@ def new_batch(self, batch_mode): self.batch = Batch(self.master, self.dm, fileheader=self.header, mode=batch_mode) def create_interface(self): - """ Create GUI for the CV experiment - :return: None - """ + # Add elements to the menu bar self.create_menu_bar() @@ -112,7 +106,7 @@ def create_interface(self): self.za_box = ttk.Combobox(master=hardware_frame, textvariable=self.za_var, state="readonly") self.za_box.grid(column=0, row=0, sticky=(tk.EW)) self.za_box.bind('<>', self.select_za) - + # Scan mode widgets --------------------------------- mode_frame = ttk.Labelframe(cv_frame, text='Scan mode:', padding=(0, 5, 0, 15)) @@ -123,19 +117,19 @@ def create_interface(self): self.fixed_var.set('bias') ttk.Radiobutton(mode_frame, text="Fixed Bias", variable=self.fixed_var, value='bias', command=self.mode).grid(column=0, row=0, sticky=(tk.E, tk.W, tk.S)) ttk.Radiobutton(mode_frame, text="Fixed Frequency", variable=self.fixed_var, value='freq', command=self.mode).grid(column=1, row=0, sticky=(tk.E, tk.W, tk.S)) - + self.fixed_value_var = tk.StringVar() self.fixed_value_var.set('0') self.fixed_value_label = ttk.Label(mode_frame, text='Set bias (V): ') self.fixed_value_label.grid(column=0, row=1, sticky=(tk.E, tk.W, tk.S)) ttk.Entry(master=mode_frame, width=10, textvariable=self.fixed_value_var).grid(column=1, row=1, sticky=(tk.E, tk.W, tk.S)) - + self.osc_amp_var = tk.StringVar() self.osc_amp_var.set('0.01') self.osc_amp_label = ttk.Label(mode_frame, text='Set AC bias (V): ') self.osc_amp_label.grid(column=0, row=2, sticky=(tk.E, tk.W, tk.S)) ttk.Entry(master=mode_frame, width=10, textvariable=self.osc_amp_var).grid(column=1, row=2, sticky=(tk.E, tk.W, tk.S)) - + # Ch1 setup widgets -------------------------------- Ch1_frame = ttk.Labelframe(cv_frame, text='Ch1 setup:', padding=(0, 5, 0, 15)) Ch1_frame.grid(column=0, row=2, sticky=(tk.EW)) @@ -152,8 +146,8 @@ def create_interface(self): self.idc_button.grid(column=4, row=0, sticky=(tk.E, tk.W, tk.S)) self.idc_button.configure(state='disabled') ttk.Radiobutton(Ch1_frame, text="Linear", variable=self.Ch1_scale_var, value='linear', command=self.scan_scale).grid(column=0, row=1, sticky=(tk.E, tk.W, tk.S)) - ttk.Radiobutton(Ch1_frame, text="Log10", variable=self.Ch1_scale_var, value='log', command=self.scan_scale).grid(column=1, row=1, sticky=(tk.E, tk.W, tk.S)) - + ttk.Radiobutton(Ch1_frame, text="Log10", variable=self.Ch1_scale_var, value='log', command=self.scan_scale).grid(column=1, row=1, sticky=(tk.E, tk.W, tk.S)) + # Ch2 setup widgets -------------------------------- Ch2_frame = ttk.Labelframe(cv_frame, text='Ch2 setup:', padding=(0, 5, 0, 15)) Ch2_frame.grid(column=0, row=3, sticky=(tk.EW)) @@ -165,12 +159,12 @@ def create_interface(self): ttk.Radiobutton(Ch2_frame, text="Tz", variable=self.Ch2_param_var, value='Tz', command=self.channel_param).grid(column=0, row=0, sticky=(tk.E, tk.W, tk.S)) ttk.Radiobutton(Ch2_frame, text="Rs", variable=self.Ch2_param_var, value='Rs', command=self.channel_param).grid(column=1, row=0, sticky=(tk.E, tk.W, tk.S)) ttk.Radiobutton(Ch2_frame, text="Rp", variable=self.Ch2_param_var, value='Rp', command=self.channel_param).grid(column=2, row=0, sticky=(tk.E, tk.W, tk.S)) - ttk.Radiobutton(Ch2_frame, text="X", variable=self.Ch2_param_var, value='X', command=self.channel_param).grid(column=3, row=0, sticky=(tk.E, tk.W, tk.S)) + ttk.Radiobutton(Ch2_frame, text="X", variable=self.Ch2_param_var, value='X', command=self.channel_param).grid(column=3, row=0, sticky=(tk.E, tk.W, tk.S)) ttk.Radiobutton(Ch2_frame, text="Linear", variable=self.Ch2_scale_var, value='linear', command=self.scan_scale).grid(column=0, row=1, sticky=(tk.E, tk.W, tk.S)) self.Ch2log_button = ttk.Radiobutton(Ch2_frame, text="Log10", variable=self.Ch2_scale_var, value='log', command=self.scan_scale) self.Ch2log_button.grid(column=1, row=1, sticky=(tk.E, tk.W, tk.S)) self.Ch2log_button.configure(state='disabled') - + # Sweep setup widgets --------------------------------- sweep_frame = ttk.Labelframe(cv_frame, text='Sweep setup:', padding=(0, 5, 0, 15)) sweep_frame.columnconfigure(0, weight=1) @@ -193,52 +187,51 @@ def create_interface(self): ttk.Radiobutton(sweep_frame, text="Linear", variable=self.x_scale_var, value='linear', command=self.scan_scale).grid(column=0, row=2, sticky=(tk.E, tk.W, tk.S)) self.xlog_button = ttk.Radiobutton(sweep_frame, text="Log10", variable=self.x_scale_var, value='log', command=self.scan_scale) self.xlog_button.grid(column=1, row=2, sticky=(tk.E, tk.W, tk.S)) - + self.nop_var = tk.StringVar() self.nop_var.set('400') self.nop_label = ttk.Label(sweep_frame, text='Number of points: ') self.nop_label.grid(column=0, row=3, sticky=(tk.E, tk.W, tk.S)) ttk.Entry(master=sweep_frame, width=10, textvariable=self.nop_var).grid(column=1, row=3, sticky=(tk.E, tk.W, tk.S)) - + self.navg_var = tk.StringVar() self.navg_var.set('3') self.navg_label = ttk.Label(sweep_frame, text='Number of point averages: ') self.navg_label.grid(column=0, row=4, sticky=(tk.E, tk.W, tk.S)) ttk.Entry(master=sweep_frame, width=10, textvariable=self.navg_var).grid(column=1, row=4, sticky=(tk.E, tk.W, tk.S)) - + self.sweep_dir_var = tk.StringVar() self.sweep_dir_var.set('up') ttk.Radiobutton(sweep_frame, text="Sweep Up", variable=self.sweep_dir_var, value='up').grid(column=0, row=5, sticky=(tk.E, tk.W, tk.S)) ttk.Radiobutton(sweep_frame, text="Sweep Down", variable=self.sweep_dir_var, value='down').grid(column=1, row=5, sticky=(tk.E, tk.W, tk.S)) - + #self.abort_button = ttk.Button(master=sweep_frame, width=10, text='ABORT!', command=self.abort) - #self.abort_button.grid(column=0, row=98, sticky=( tk.E, tk.SW, tk.S)) - + #self.abort_button.grid(column=0, row=98, sticky=( tk.E, tk.SW, tk.S)) + # The "Run" button is only available if not in batch mode if not self.in_batch: self.run_button = ttk.Button(master=sweep_frame, width=10, text='Run', command=self.start_stop_scan) self.run_button.grid(column=1, row=98, sticky=( tk.E, tk.SW, tk.S)) def update_header(self): - """ Updates header in output text file - :return: None - """ - top0 = self.fixed_var.get() - top1 = self.fixed_value_var.get() - - if top0 == 'bias': - top2 = 'V' + fixed = self.fixed_var.get() + + if fixed == 'bias': + top0 = 'DC bias [V] = ' else: - top2 = 'Hz' - + top0 = 'Frequency [Hz] = ' + + top1 = self.fixed_value_var.get() + top2 = self.osc_amp_var.get() col0 = self.plot_format['xlabel'] col1 = self.plot_format['Ch1_ylabel'] col2 = self.plot_format['Ch2_ylabel'] - self.header = str(top0)+'= '+str(top1)+' '+str(top2)+'\n'+str(col0)+'\t'+str(col1)+'\t'+str(col2) + self.header = str(top0)+str(top1)+'\n'+'AC bias [V] = '+str(top2)+'\n'+str(col0)+'\t'+str(col1)+'\t'+str(col2) self.header.encode('utf-8') - + def fill_devices(self): """ Fills the device selectors with the corresponding type of devices + :return: None """ self.za_box['values'] = self.dm.get_devices(['ZA']) @@ -249,6 +242,7 @@ def fill_devices(self): def select_za(self, *args): """ When the Z analyser selector changes, this function updates some variables and the graphical interface to adapt it to the selected device. + :param args: Dummy variable that does nothing but must exist (?) :return: None """ @@ -266,7 +260,7 @@ def select_za(self, *args): elif self.dm.current_config[dev_name]['Type'] == 'ZA': pass ## Line required to avoid 'busy device' error - + else: self.za_box.current(0) self.za = self.dm.open_device(self.za_var.get()) @@ -277,11 +271,8 @@ def select_za(self, *args): self.master.menu_hardware.entryconfig("ZA", state="normal") else: self.master.menu_hardware.entryconfig("ZA", state="disabled") - + def refresh_buttons(self): - """ Updates button features when different experimental setups are selected - :return: None - """ if self.fixed_var.get() == 'freq': self.x_scale_var.set('linear') self.scan_scale() ## Updates plot_format, which is used to pass parameters to the device @@ -290,16 +281,17 @@ def refresh_buttons(self): else: self.xlog_button.configure(state='normal') self.idc_button.configure(state='disabled') - + if self.Ch2_param_var.get() == 'Tz' or self.Ch2_param_var.get() == 'X': self.Ch2_scale_var.set('linear') self.scan_scale() ## Updates plot_format, which is used to pass parameters to the device self.Ch2log_button.configure(state='disabled') else: - self.Ch2log_button.configure(state='normal') + self.Ch2log_button.configure(state='normal') def mode(self): - """ When the experimental setup is changed, this function updates some internal variables and the graphical interface + """ When the bias mode is changed, this function updates some internal variables and the graphical interface + :return: None """ @@ -324,71 +316,74 @@ def mode(self): self.plot_format['xlabel'] = 'Bias (V)' self.fixed_value_var.set('1e5') self.osc_amp_var.set('0.01') - - self.refresh_buttons() + + self.refresh_buttons() self.master.update_plot_axis(self.plot_format) - + def channel_param(self): """ When the channel parameters are changed, this function updates some internal variables and the graphical interface + :return: None """ ## Channel 1 labels ---------------------------------- if self.Ch1_param_var.get() == 'Z': self.plot_format['Ch1_ylabel'] = '|Z| (Ohm)' elif self.Ch1_param_var.get() == 'Cp': - self.plot_format['Ch1_ylabel'] = 'Cp (F)' + self.plot_format['Ch1_ylabel'] = 'Cp (F)' elif self.Ch1_param_var.get() == 'Cs': self.plot_format['Ch1_ylabel'] = 'Cs (F)' elif self.Ch1_param_var.get() == 'R': self.plot_format['Ch1_ylabel'] = 'Zreal (Ohm)' elif self.Ch1_param_var.get() == 'Idc': self.plot_format['Ch1_ylabel'] = 'Idc (A)' - ## Channel 2 labels ---------------------------------- + ## Channel 2 labels ---------------------------------- if self.Ch2_param_var.get() == 'Tz': - self.plot_format['Ch2_ylabel'] = 'Tz (deg)' + self.plot_format['Ch2_ylabel'] = 'Tz (deg)' elif self.Ch2_param_var.get() == 'Rp': - self.plot_format['Ch2_ylabel'] = 'Rp (Ohm)' + self.plot_format['Ch2_ylabel'] = 'Rp (Ohm)' elif self.Ch2_param_var.get() == 'Rs': self.plot_format['Ch2_ylabel'] = 'Rs (Ohm)' elif self.Ch2_param_var.get() == 'X': self.plot_format['Ch2_ylabel'] = 'Zimag (Ohm)' - - self.refresh_buttons() + + self.refresh_buttons() self.master.update_plot_axis(self.plot_format) def scan_scale(self): """ When the scan scales are changed, this function updates some internal variables and the graphical interface + :return: None """ ## X-axis scale ---------------------------------- if self.x_scale_var.get() == 'linear': self.plot_format['x_scale'] = 'linear' elif self.x_scale_var.get() == 'log': - self.plot_format['x_scale'] = 'log' + self.plot_format['x_scale'] = 'log' ## Channel 1 scale ---------------------------------- if self.Ch1_scale_var.get() == 'linear': self.plot_format['Ch1_scale'] = 'linear' elif self.Ch1_scale_var.get() == 'log': - self.plot_format['Ch1_scale'] = 'log' - ## Channel 2 scale ---------------------------------- + self.plot_format['Ch1_scale'] = 'log' + ## Channel 2 scale ---------------------------------- if self.Ch2_scale_var.get() == 'linear': self.plot_format['Ch2_scale'] = 'linear' elif self.Ch2_scale_var.get() == 'log': self.plot_format['Ch2_scale'] = 'log' - + self.master.update_plot_axis(self.plot_format) - + def start_stop_scan(self): """ Starts and stops a scan + :return: None """ - self.run_check() + self.run_check() if False not in self.run_ok: - + if self.stop: self.stop = False - + if self.batch.ready: self.run_button['text'] = 'Stop batch' else: @@ -399,15 +394,17 @@ def start_stop_scan(self): self.batch.ready = False self.stop = True self.run_button['text'] = 'Run' - else: - self.run_button['state'] = 'enabled' + else: + self.run_button['state'] = 'enabled' self.finish_scan() def prepare_scan(self): """ Any scan is divided in three stages: - 1) Prepare the conditions of the scan (this function), creating all relevant variables. - 2) Calls the 'start_scan' function to begin the measurement + 1) Prepare the conditions of the scan (this function), getting starting point, integration time and creating all relevant variables. + 2) Running the scan, performed by a recursive function "get_next_datapoint" + 3) Finish the scan, where we update some variables and save the data. + :return: None """ ## If inputs are OK, fill up the options dictionary and proceed. @@ -419,7 +416,7 @@ def prepare_scan(self): 'navg': self.navg, 'osc_amp': self.osc_amp, 'sweep_dir': self.sweep_dir} - + # # If we are in a batch, we proceed to the next point if self.batch.ready: self.batch.batch_proceed() @@ -435,33 +432,27 @@ def prepare_scan(self): self.start_scan() def start_scan(self): - """ Begins the experiment - :return: None - """ - self.za.setup_measurement(self.plot_format, self.options) self.za.measure(self.options) self.get_data() - + def get_data(self): - """ Retireves the data collected during the scan - :return: None - """ - + data0, data1, data2 = self.za.return_data() self.record = np.zeros((len(data0), 3)) self.record[:, 0] = data0 self.record[:, 1] = data1 self.record[:, 2] = data2 - + self.master.update_plot(self.record) self.finish_scan() def finish_scan(self): """ Finish the scan, updating some global variables, saving the data in the temp file and offering to save the data somewhere else. + :return: None """ @@ -470,7 +461,7 @@ def finish_scan(self): self.batch.batch_wrapup(self.record) else: self.master.finish_meas(self.record, finish=True) - self.stop = True + self.stop = True if self.stop or not self.batch.ready: # We reduce the number of counts in the batch to resume at the unfinished point if necessary @@ -481,38 +472,24 @@ def finish_scan(self): else: self.prepare_scan() - + def check_inputs(self, parameter, value, min, max): - """ Check the values of relevant scan parameters and gives an error if they are out of range - - :param parameter: the name of the parameter to check - :param: value: the value stored in the parameter variable - :param: min: the minimum value allowed for the parameter - :param: max: the maximum value allowed for the parameter - :return: Error: parameter out of range - """ - if value < min or value > max: messagebox.showerror("Error", parameter+" is out of range!") self.run_button['state'] = 'enabled' self.run_ok.append(False) - + else: self.run_ok.append(True) - + def run_check(self): - """ Calls the 'check_inputs' function on all of relevant scan parameters - - :return: Error - parameter out of range - """ - self.mode = self.fixed_var.get() self.fixed_value = float(self.fixed_value_var.get()) self.sweep_start = float(self.sweep_start_var.get()) self.sweep_stop = float(self.sweep_stop_var.get()) self.nop = int(self.nop_var.get()) - self.navg = int(self.navg_var.get()) + self.navg = int(self.navg_var.get()) self.osc_amp = float(self.osc_amp_var.get()) self.sweep_dir = self.sweep_dir_var.get() @@ -531,11 +508,8 @@ def run_check(self): self.check_inputs('Number of averages', self.navg, 1, 100) self.check_inputs('AC bias', self.osc_amp, 0.001, 1) - + def abort(self): - """ Aborts scan - - :return: None - """ + #pass self.za.abort_sweep() self.get_data() diff --git a/Experiments/iv_.py b/Experiments/iv_.py new file mode 100644 index 0000000..cd45f69 --- /dev/null +++ b/Experiments/iv_.py @@ -0,0 +1,381 @@ +__author__ = 'D. Alonso-Álvarez' + +import tkinter as tk +from tkinter import ttk + +import numpy as np +from Experiments.batch_control import Batch + + +class IV: + """ Base class for IV experiments """ + + def __init__(self, master, devman, in_batch=False): + + self.master = master + self.dm = devman + self.id = 'iv' + + # Create the main variables of the class + self.in_batch = in_batch + self.create_variables() + + # Pre-load the batch, witout creating any interface + self.header = 'Voltage (V)\tAbs(Current) (A)\tCurrent (A)' + self.extension = 'iv' + self.batch = Batch(self.master, self.dm, fileheader=self.header) + + # Create the interface + self.create_interface() + self.plot_format = {'ratios': (1, 1), + 'xlabel': 'Voltage (V)', + 'Ch1_ylabel': 'Abs(Current) (A)', + 'Ch2_ylabel': 'Current (A)', + 'Ch1_scale': 'log', + 'Ch2_scale': 'linear'} + + # We load the dummy devices by default + self.fill_devices() + + def quit(self): + """ Safe closing of the devices. The devices must be closed by the Device Manager, not directly, + so they are registered as "free". + """ + + if self.smu is not None: + self.dm.close_device(self.smu) + + self.batch.window.destroy() + + def create_variables(self): + + # Data variables + self.record = None + + # Hardware variables + self.smu = None + + # Adquisition variables + self.integration_time = 300 + self.delay = 100 + self.stop = True + + def create_menu_bar(self): + """ Add elememnts to the master menubar + """ + + # Hardware menu + self.master.menu_hardware.add_command(label='SMU', command=lambda: self.smu.interface(self.master)) + self.master.menu_hardware.entryconfig("SMU", state="disabled") + + # Batch menu + if not self.in_batch: + self.master.menu_batch.add_command(label='Disable', command=self.batch.disable) + self.master.menu_batch.add_command(label='Temperature', command=lambda: self.new_batch('Temperature')) + self.master.menu_batch.add_command(label='Time', command=lambda: self.new_batch('Time')) + + def new_batch(self, batch_mode): + """ Shows current batch window or, if a different batch is chosen, destroys the old one and creates a new one + for the new batch mpde + + :param batch_mode: the selected type of batch + :return: None + """ + if self.batch.mode == batch_mode: + self.batch.show() + else: + self.batch.window.destroy() + self.batch = Batch(self.master, self.dm, mode=batch_mode, fileheader=self.header) + + def create_interface(self): + + # Add elements to the menu bar + self.create_menu_bar() + + iv_frame = ttk.Frame(self.master.control_frame) + iv_frame.grid(column=0, row=0, sticky=(tk.EW)) + iv_frame.columnconfigure(0, weight=1) + + # Hardware widgets + hardware_frame = ttk.Labelframe(iv_frame, text='Selected hardware:', padding=(0, 5, 0, 15)) + hardware_frame.grid(column=0, row=0, sticky=(tk.EW)) + hardware_frame.columnconfigure(0, weight=1) + + self.smu_var = tk.StringVar() + self.smu_box = ttk.Combobox(master=hardware_frame, textvariable=self.smu_var, state="readonly") + self.smu_box.grid(column=0, row=0, sticky=(tk.EW)) + self.smu_box.bind('<>', self.select_smu) + + # Set widgets --------------------------------- + set_frame = ttk.Labelframe(iv_frame, text='Set:', padding=(0, 5, 0, 15)) + set_frame.grid(column=0, row=1, sticky=(tk.EW)) + set_frame.columnconfigure(1, weight=1) + + self.integration_time_button = ttk.Button(master=set_frame, text='Integration time (ms)', command=self.update_integration_time) + self.integration_time_list = ttk.Combobox(set_frame, state="readonly", width=10) + + self.waiting_time_button = ttk.Button(master=set_frame, text='Waiting time (ms)', command=self.update_waiting_time) + self.waiting_time_entry = ttk.Entry(master=set_frame, width=10) + self.waiting_time_entry.insert(0, '10') + + self.integration_time_button.grid(column=0, row=2, sticky=(tk.EW)) + self.integration_time_list.grid(column=1, row=2, sticky=(tk.EW)) + self.waiting_time_button.grid(column=0, row=3, sticky=(tk.EW)) + self.waiting_time_entry.grid(column=1, row=3, sticky=(tk.EW)) + + self.source_var = tk.StringVar() + self.source_var.set('v') + ttk.Radiobutton(set_frame, text="Voltage (V)", variable=self.source_var, value='v', command=self.mode).grid(column=0, row=4, sticky=(tk.E, tk.W, tk.S)) + ttk.Radiobutton(set_frame, text="Current (A)", variable=self.source_var, value='i', command=self.mode).grid(column=1, row=4, sticky=(tk.E, tk.W, tk.S)) + + # Scan widgets --------------------------------- + scan_frame = ttk.Labelframe(iv_frame, text='Scan:', padding=(0, 5, 0, 15)) + scan_frame.columnconfigure(0, weight=1) + scan_frame.grid(column=0, row=3, sticky=(tk.EW)) + + self.start_var = tk.StringVar() + self.start_var.set('0.00') + self.start_label = ttk.Label(scan_frame, text='Start (V): ') + self.start_label.grid(column=0, row=0, sticky=(tk.E, tk.W, tk.S)) + ttk.Entry(master=scan_frame, width=10, textvariable=self.start_var).grid(column=1, row=0, sticky=(tk.E, tk.W, tk.S)) + + self.stop_var = tk.StringVar() + self.stop_var.set('1.00') + self.stop_label = ttk.Label(scan_frame, text='Stop (V): ') + self.stop_label.grid(column=0, row=1, sticky=(tk.E, tk.W, tk.S)) + ttk.Entry(master=scan_frame, width=10, textvariable=self.stop_var).grid(column=1, row=1, sticky=(tk.E, tk.W, tk.S)) + + self.step_var = tk.StringVar() + self.step_var.set('0.05') + self.step_label = ttk.Label(scan_frame, text='Step (V): ') + self.step_entry = ttk.Entry(master=scan_frame, width=10, textvariable=self.step_var) + + self.points_label = ttk.Label(scan_frame, text='Points (per decade): ') + self.points_list = ttk.Combobox(scan_frame, state="readonly", width=10) + + self.compliance_var = tk.StringVar() + self.compliance_var.set('0.01') + self.compliance_label = ttk.Label(scan_frame, text='Compliance (A): ') + self.compliance_label.grid(column=0, row=3, sticky=(tk.E, tk.W, tk.S)) + ttk.Entry(scan_frame, width=10, textvariable=self.compliance_var).grid(column=1, row=3, sticky=(tk.E, tk.W, tk.S)) + + # Combobox containing the available hardware + ttk.Label(scan_frame, text='Range: ').grid(column=0, row=4, sticky=(tk.E, tk.W, tk.S)) + self.range_list = ttk.Combobox(scan_frame, state="readonly", width=10) + self.range_list.grid(column=1, row=4, sticky=(tk.E, tk.W, tk.S)) + + # The "Run" button is only available if not in batch mode + if not self.in_batch: + self.run_button = ttk.Button(master=scan_frame, width=10, text='Run', command=self.start_stop_scan) + self.run_button.grid(column=1, row=98, sticky=( tk.E, tk.W, tk.S)) + + def fill_devices(self): + """ Fills the device selectors with the corresponding type of devices + + :return: None + """ + self.smu_box['values'] = self.dm.get_devices(['SMU']) + self.smu_box.current(0) + + self.select_smu() + + def select_smu(self, *args): + """ When the SMU selector changes, this function updates some variables and the graphical interface + to adapt it to the selected device. + + :param args: Dummy variable that does nothing but must exist (?) + :return: None + """ + + if self.smu is not None: + self.dm.close_device(self.smu) + + dev_name = self.smu_var.get() + self.smu = self.dm.open_device(dev_name) + + if self.smu is None: + self.smu_box.current(0) + self.smu = self.dm.open_device(self.smu_var.get()) + + elif self.dm.current_config[dev_name]['Type'] == 'SMU': + self.source_var.set('v') + self.range_list['values'] = self.smu.i_range + self.range_list.current(0) + self.integration_time_list['values'] = self.smu.int_time + self.integration_time_list.current(0) + self.points_list['values'] = self.smu.log_points + self.points_list.current(1) + self.mode() + + else: + self.smu_box.current(0) + self.smu = self.dm.open_device(self.smu_var.get()) + + # If the device has an interface to set options, we link it to the entry in the menu + interface = getattr(self.smu, "interface", None) + if callable(interface): + self.master.menu_hardware.entryconfig("SMU", state="normal") + else: + self.master.menu_hardware.entryconfig("SMU", state="disabled") + + def mode(self): + """ When the bias mode is changed, this function updates some internal variables and the graphical interface + + :return: None + """ + + if self.source_var.get() == 'v': + self.start_label['text'] = 'Start (V): ' + self.stop_label['text'] = 'Stop (V): ' + self.compliance_label['text'] = 'Compliance (A): ' + self.range_list['values'] = self.smu.i_range + self.start_var.set('0') + self.stop_var.set('1') + self.compliance_var.set('1e-3') + self.step_label.grid(column=0, row=2, sticky=(tk.E, tk.W, tk.S)) + self.step_entry.grid(column=1, row=2, sticky=(tk.E, tk.W, tk.S)) + self.points_label.grid_forget() + self.points_list.grid_forget() + + else: + self.start_label['text'] = 'Start (A): ' + self.stop_label['text'] = 'Stop (A): ' + self.compliance_label['text'] = 'Compliance (V): ' + self.range_list['values'] = self.smu.v_range + self.start_var.set('1e-9') + self.stop_var.set('1e-3') + self.compliance_var.set('2') + self.points_label.grid(column=0, row=2, sticky=(tk.E, tk.W, tk.S)) + self.points_list.grid(column=1, row=2, sticky=(tk.E, tk.W, tk.S)) + self.step_label.grid_forget() + self.step_entry.grid_forget() + + def update_integration_time(self): + """ Sets the value of the integration time. Informtaion IS sent to the instrument. + + :return: + """ + self.integration_time = int(self.integration_time_list.current()) + self.smu.update_integration_time(self.integration_time) + + def update_waiting_time(self): + """ Sets the value of the delay between applying a bias and taking the measurement. Informtaion IS sent to the instrument. + + :return: + """ + self.delay = int(self.waiting_time_entry.get()) + self.smu.update_waiting_time(self.delay) + + def update_compliance_and_range(self): + """ Sets the value of the variables compliance and meas_range. Information IS NOT sent to the instrument. + + :return: None + """ + self.compliance = float(self.compliance_var.get()) + self.meas_range = self.range_list.current() + + def start_stop_scan(self): + """ Starts and stops an scan + + :return: None + """ + if self.stop: + self.stop = False + + if self.batch.ready: + self.run_button['text'] = 'Stop batch' + else: + self.run_button['state'] = 'disabled' + self.prepare_scan() + else: + if self.batch.ready: + self.batch.ready = False + self.stop = True + self.run_button['text'] = 'Run' + self.run_button['state'] = 'disabled' + + def prepare_scan(self): + """ Any scan is divided in three stages: + 1) Prepare the conditions of the scan (this function), getting starting point, integration time and creating all relevant variables. + 2) Runing the scan, performed by a recursive function "get_next_datapoint" + 3) Finish the scan, where we update some variables and save the data. + + :return: None + """ + mode = self.source_var.get() + + start = float(self.start_var.get()) + stop = float(self.stop_var.get()) + step = float(self.step_var.get()) + points = int(self.points_list.current()) + + integration_time = int(self.integration_time_list.current()) + delay = int(self.waiting_time_entry.get()) + compliance = float(self.compliance_var.get()) + meas_range = self.range_list.current() + + self.options = {'source': mode, + 'start': start, + 'stop': stop, + 'step': step, + 'points': points, + 'compliance':compliance, + 'measRange':meas_range, + 'delay': delay, + 'intTime': integration_time} + + # # If we are in a batch, we proceed to the next point + if self.batch.ready: + self.batch.batch_proceed() + + print('Starting scan...') + + # Create the record array. In this case it is irrelevant + self.record = np.zeros((points+1, 3)) + self.record[:, 0] = np.linspace(0, 1, points+1) + self.record[:, 1] = np.ones_like(self.record[:, 1]) + self.record[:, 2] = np.ones_like(self.record[:, 1]) + + self.master.prepare_meas(self.record) + self.start_scan() + + def start_scan(self): + + measTime = self.smu.measure(**self.options) + self.master.window.after(int(measTime), self.get_data) + + def get_data(self): + + data0, data1 = self.smu.get_data() + + self.record = np.zeros((len(data0), 3)) + self.record[:, 0] = data0 + self.record[:, 1] = abs(data1) + self.record[:, 2] = data1 + + self.master.update_plot(self.record) + self.finish_scan() + + def finish_scan(self): + """ Finish the scan, updating some global variables, saving the data in the temp file and offering to save the + data somewhere else. + + :return: None + """ + + if self.batch.ready: + self.master.finish_meas(self.record, finish=False) + self.batch.batch_wrapup(self.record) + else: + self.master.finish_meas(self.record, finish=True) + self.stop = True + + if self.stop or not self.batch.ready: + # We reduce the number of counts in the batch to resume at the unfinished point if necessary + # In other words, it repeats the last, un-finished measurement + self.batch.count = self.batch.count - 1 + self.run_button['state'] = 'enabled' + + else: + self.prepare_scan() + diff --git a/Experiments/photoreflectance.py b/Experiments/photoreflectance.py new file mode 100644 index 0000000..d48a791 --- /dev/null +++ b/Experiments/photoreflectance.py @@ -0,0 +1,821 @@ +__author__ = 'A. M. Telford & D. Alonso-Álvarez' + +import numpy as np + +import tkinter as tk +from tkinter import messagebox +from tkinter import ttk +from math import sqrt, atan + +from Experiments.batch_control import Batch + + +class Photoreflectance: + """ Base class for photoreflectance experiments """ + + def __init__(self, master, devman): + + self.master = master + self.dm = devman + self.id = 'PR' + + # Create the main variables of the class + self.create_variables() + + self.plot_format = {'ratios' : (1, 1), + 'xlabel' : 'Wavelength (nm)', + 'x_scale' : 'linear', + 'Ch1_ylabel' : 'Xsignal/Rbaseline', + 'Ch2_ylabel' : 'Rbaseline', + 'Ch1_scale' : 'linear', + 'Ch2_scale' : 'linear'} + self.header = '' + self.header.encode('utf-8') + self.extension = 'txt' + + # Pre-load the batch, witout creating any interface + self.batch = Batch(self.master, self.dm, fileheader=self.header) + + # Create the interface + self.create_interface() + + # We load the dummy devices by default + self.fill_devices() + + def quit(self): + """ Safe closing of the devices. The devices must be closed by the Device Manager, not directly, + so they are registered as "free". + """ + + if self.monochromator is not None: + self.monochromator.close() ## Destroys the system model + self.dm.close_device(self.monochromator) + if self.acquisition is not None: + self.acquisition.close() ## Unsubscribes from the demodulators + self.dm.close_device(self.acquisition) + + self.batch.quit() + + def create_variables(self): + + # Data variables + self.record = None + self.background = None + self.plotdata = None ## Transformed data to be plotted + + # Hardware variables + self.monochromator = None + self.acquisition = None + + # acquisition variables + self.integration_time = 100 + self.waiting_time = 100 + self.stop = True + + def create_interface(self): + + # Add elements to the menu bar + self.create_menu_bar() + + # Creates the photoreflectance frame + self.spectroscopy_frame = ttk.Frame(master=self.master.control_frame) + self.spectroscopy_frame.grid(column=0, row=0, sticky=(tk.EW)) + self.spectroscopy_frame.columnconfigure(0, weight=1) + + # Hardware widgets + hardware_frame = ttk.Labelframe(self.spectroscopy_frame, text='Selected hardware:', padding=(0, 5, 0, 15)) + hardware_frame.columnconfigure(0, weight=1) + hardware_frame.grid(column=0, row=0, sticky=(tk.EW)) + + self.mono_var = tk.StringVar() + self.acq_var = tk.StringVar() + self.monochromator_box = ttk.Combobox(master=hardware_frame, textvariable=self.mono_var, state="readonly") + self.acquisition_box = ttk.Combobox(master=hardware_frame, textvariable=self.acq_var, state="readonly") + + self.monochromator_box.bind('<>', self.select_monochromator) + self.acquisition_box.bind('<>', self.select_acquisition) + + self.monochromator_box.grid(column=0, row=0, sticky=(tk.EW)) + self.acquisition_box.grid(column=0, row=1, sticky=(tk.EW)) + + # Set widgets --------------------------------- + set_frame = ttk.Labelframe(self.spectroscopy_frame, text='Set:', padding=(0, 5, 0, 15)) + set_frame.columnconfigure(1, weight=1) + + self.GoTo_button = ttk.Button(master=set_frame, text='GoTo', command=self.goto) + self.GoTo_entry = ttk.Entry(master=set_frame, width=10) + self.GoTo_entry.insert(0, '700.0') + self.integration_time_button = ttk.Button(master=set_frame, text='Integration time (ms)', + command=self.update_integration_time) + self.integration_time_entry = ttk.Entry(master=set_frame, width=10) + self.integration_time_entry.insert(0, '100') + self.waiting_time_button = ttk.Button(master=set_frame, text='Waiting time (ms)', + command=self.update_waiting_time) + self.waiting_time_entry = ttk.Entry(master=set_frame, width=10) + self.waiting_time_entry.insert(0, '100') + + set_frame.grid(column=0, row=1, sticky=(tk.EW)) + self.GoTo_button.grid(column=0, row=0, sticky=(tk.EW)) + self.GoTo_entry.grid(column=1, row=0, sticky=(tk.EW)) + self.integration_time_button.grid(column=0, row=2, sticky=(tk.EW)) + self.integration_time_entry.grid(column=1, row=2, sticky=(tk.EW)) + self.waiting_time_button.grid(column=0, row=3, sticky=(tk.EW)) + self.waiting_time_entry.grid(column=1, row=3, sticky=(tk.EW)) + + # Live acquisition widgets + live_frame = ttk.Labelframe(self.spectroscopy_frame, text='Live:', padding=(0, 5, 0, 15)) + live_frame.columnconfigure(1, weight=1) + live_frame.columnconfigure(2, weight=1) + + self.window_live_lbl = ttk.Label(master=live_frame, text="Window (points):") + self.window_live_entry = ttk.Entry(master=live_frame, width=10) + self.window_live_entry.insert(0, '100') + + self.record_live_button = ttk.Button(master=live_frame, text='Record', command=self.record_live) + self.pause_live_button = ttk.Button(master=live_frame, text='Pause', state=tk.DISABLED, command=self.pause_live) + + live_frame.grid(column=0, row=2, sticky=(tk.EW)) + self.window_live_lbl.grid(column=0, row=0, sticky=(tk.EW)) + self.window_live_entry.grid(column=1, row=0, columnspan=2, sticky=(tk.EW)) + self.record_live_button.grid(column=1, row=3, sticky=(tk.EW)) + self.pause_live_button.grid(column=2, row=3, sticky=(tk.EW)) + + # Scan widgets --------------------------------- + scan_frame = ttk.Labelframe(self.spectroscopy_frame, text='Scan:', padding=(0, 5, 0, 15)) + scan_frame.columnconfigure(0, weight=1) + + Start_lbl = ttk.Label(master=scan_frame, text="Start (nm):") + self.Start_entry = ttk.Entry(master=scan_frame) + self.Start_entry.insert(0, '700.0') + Stop_lbl = ttk.Label(master=scan_frame, text="Stop (nm):") + self.Stop_entry = ttk.Entry(master=scan_frame) + self.Stop_entry.insert(0, '900.0') + Step_lbl = ttk.Label(master=scan_frame, text="Step (nm):") + self.Step_entry = ttk.Entry(master=scan_frame) + self.Step_entry.insert(0, '5.0') + + self.scan_button = ttk.Button(master=scan_frame, text='Run', command=self.start_stop_scan, width=7) + self.pause_button = ttk.Button(master=scan_frame, text='Pause', state=tk.DISABLED, command=self.pause_scan, + width=7) + + scan_frame.grid(column=0, row=3, sticky=(tk.EW)) + Start_lbl.grid(column=0, row=0, sticky=(tk.EW)) + self.Start_entry.grid(column=1, row=0, columnspan=2, sticky=(tk.EW)) + Stop_lbl.grid(column=0, row=1, sticky=(tk.EW)) + self.Stop_entry.grid(column=1, row=1, columnspan=2, sticky=(tk.EW)) + Step_lbl.grid(column=0, row=2, sticky=(tk.EW)) + self.Step_entry.grid(column=1, row=2, columnspan=2, sticky=(tk.EW)) + self.scan_button.grid(column=1, row=3, sticky=(tk.EW)) + self.pause_button.grid(column=2, row=3, sticky=(tk.EW)) + + # Background widgets--------------------------------------------- + self.background_frame = ttk.Labelframe(self.spectroscopy_frame, text='Background:', padding=(0, 5, 0, 15)) + self.background_frame.columnconfigure(0, weight=1) + self.background_frame.columnconfigure(1, weight=1) + self.background_button = ttk.Button(master=self.background_frame, text='Get', command=self.get_background) + self.clear_background_button = ttk.Button(master=self.background_frame, text='Clear', + command=self.clear_background) + + self.background_frame.grid(column=0, row=4, sticky=(tk.NSEW)) + self.background_button.grid(column=0, row=0, sticky=(tk.EW, tk.S)) + self.clear_background_button.grid(column=1, row=0, sticky=(tk.EW, tk.S)) + + # Ch1 setup widgets -------------------------------- + self.Ch1_frame = ttk.Labelframe(self.spectroscopy_frame, text='Ch1 setup:', padding=(0, 5, 0, 15)) + self.Ch1_frame.grid(column=0, row=5, sticky=(tk.EW)) + self.Ch1_frame.columnconfigure(0, weight=1) + self.Ch1_frame.columnconfigure(1, weight=1) + self.Ch1_param1_var = tk.StringVar() + self.Ch1_param1_var.set('Xsignal') + self.Ch1_param2_var = tk.StringVar() + self.Ch1_param2_var.set('Rbaseline') + self.numer_label = ttk.Label(self.Ch1_frame, text='Numerator: ') + self.numer_label.grid(column=0, row=0, sticky=(tk.E, tk.W, tk.S)) + ttk.Radiobutton(self.Ch1_frame, text="X signal", variable=self.Ch1_param1_var, value='Xsignal', command=self.channel_param).grid(column=1, row=0, sticky=(tk.E, tk.W, tk.S)) + ttk.Radiobutton(self.Ch1_frame, text="R signal", variable=self.Ch1_param1_var, value='Rsignal', command=self.channel_param).grid(column=2, row=0, sticky=(tk.E, tk.W, tk.S)) + self.denom_label = ttk.Label(self.Ch1_frame, text='Denominator: ') + self.denom_label.grid(column=0, row=1, sticky=(tk.E, tk.W, tk.S)) + ttk.Radiobutton(self.Ch1_frame, text="R baseline", variable=self.Ch1_param2_var, value='Rbaseline', command=self.channel_param).grid(column=1, row=1, sticky=(tk.E, tk.W, tk.S)) + ttk.Radiobutton(self.Ch1_frame, text="No denominator", variable=self.Ch1_param2_var, value='ND', command=self.channel_param).grid(column=2, row=1, sticky=(tk.E, tk.W, tk.S)) + + # Ch2 setup widgets -------------------------------- + self.Ch2_frame = ttk.Labelframe(self.spectroscopy_frame, text='Ch2 setup:', padding=(0, 5, 0, 15)) + self.Ch2_frame.grid(column=0, row=6, sticky=(tk.EW)) + self.Ch2_frame.columnconfigure(0, weight=1) + self.Ch2_frame.columnconfigure(1, weight=1) + self.Ch2_param_var = tk.StringVar() + self.Ch2_param_var.set('Rbaseline') + ttk.Radiobutton(self.Ch2_frame, text="R baseline", variable=self.Ch2_param_var, value='Rbaseline', command=self.channel_param).grid(column=0, row=1, sticky=(tk.E, tk.W, tk.S)) + + def update_header(self): + col0 = self.plot_format['xlabel'] + col1 = 'Xc' + col2 = 'Yc' + col3 = 'Xsum' + col4 = 'Ysum' + col5 = 'Xdif' + col6 = 'Ydif' + + self.header = str(col0)+'\t'+str(col1)+'\t'+str(col2)+'\t'+str(col3)+'\t'+str(col4)+'\t'+str(col5)+'\t'+str(col6) + self.header.encode('utf-8') + + def create_menu_bar(self): + """ Add elememnts to the master menubar + """ + + # Hardware menu + self.master.menu_hardware.add_command(label='Monochromator', command=lambda: self.monochromator.interface(self.master)) + self.master.menu_hardware.entryconfig("Monochromator", state="disabled") + self.master.menu_hardware.add_command(label='acquisition', command=lambda: self.acquisition.interface(self.master)) + self.master.menu_hardware.entryconfig("acquisition", state="disabled") + + # Batch menu + self.master.menu_batch.add_command(label='Disable', command=self.batch.disable) + self.master.menu_batch.add_command(label='IV', command=lambda: self.new_batch('IV')) + self.master.menu_batch.add_command(label='Temperature', command=lambda: self.new_batch('Temperature')) + self.master.menu_batch.add_command(label='Time', command=lambda: self.new_batch('Time')) + + def new_batch(self, batch_mode): + """ Shows current batch window or, if a different batch is chosen, destroys the old one and creates a new one + for the new batch mpde + + :param batch_mode: the selected type of batch + :return: None + """ + if self.batch.mode == batch_mode: + self.batch.show() + else: + self.batch.quit() + self.batch = Batch(self.master, self.dm, mode=batch_mode) + + def fill_devices(self): + """ Fills the device selectors with the corresponding type of devices + + :return: + """ + + self.monochromator_box['values'] = self.dm.get_devices(['Monochromator']) + self.monochromator_box.current(0) + self.acquisition_box['values'] = self.dm.get_devices(['Lock-In', 'Spectrometer']) + self.acquisition_box.current(0) + + self.select_monochromator() + self.select_acquisition() + + def select_monochromator(self, *args): + + if self.monochromator is not None: + self.dm.close_device(self.monochromator) + + dev_name = self.mono_var.get() + self.monochromator = self.dm.open_device(dev_name) + + if self.monochromator is None: + self.monochromator_box.current(0) + self.monochromator = self.dm.open_device(self.mono_var.get()) + + elif self.dm.current_config[dev_name]['Type'] == 'Monochromator': + self.move = self.monochromator.move + + else: + self.monochromator_box.current(0) + self.monochromator = self.dm.open_device(self.mono_var.get()) + + # If the device has an interface to set options, we link it to the entry in the menu + interface = getattr(self.monochromator, "interface", None) + if callable(interface): + self.master.menu_hardware.entryconfig("Monochromator", state="normal") + else: + self.master.menu_hardware.entryconfig("Monochromator", state="disabled") + + def select_acquisition(self, *args): + """ When the acquisition selector changes, this function updates some variables and the graphical interface + to adapt it to the selected device. + + :param args: Dummy variable that does nothing but must exist (?) + :return: None + """ + + if self.acquisition is not None: + self.dm.close_device(self.acquisition) + + dev_name = self.acq_var.get() + self.acquisition = self.dm.open_device(dev_name) + + if self.acquisition is None: + self.acquisition_box.current(0) + self.acquisition = self.dm.open_device(self.acq_var.get()) + + elif self.dm.current_config[dev_name]['Type'] == 'Spectrometer': + self.move = self.null + self.prepare_scan = self.prepare_scan_spectrometer + self.get_next_datapoint = self.mode_spectrometer + self.start_live = self.prepare_live_spectrometer + self.live = self.live_spectrometer + self.background_frame.grid(column=0, row=4, sticky=(tk.NSEW)) + self.window_live_lbl.grid_forget() + self.window_live_entry.grid_forget() + self.monochromator_box['state'] = 'disabled' + self.Step_entry['state'] = 'disabled' + self.GoTo_button['state'] = 'disabled' + self.GoTo_entry['state'] = 'disabled' + + elif self.dm.current_config[dev_name]['Type'] in ['Lock-In', 'Multimeter']: + self.move = self.monochromator.move + self.prepare_scan = self.prepare_scan_lockin + self.get_next_datapoint = self.mode_lockin + self.start_live = self.prepare_live_lockin + self.live = self.live_lockin + self.background_frame.grid_forget() + self.window_live_lbl.grid(column=0, row=0, sticky=(tk.EW)) + self.window_live_entry.grid(column=1, row=0, columnspan=2, sticky=(tk.EW)) + self.monochromator_box['state'] = 'normal' + self.Step_entry['state'] = 'normal' + self.GoTo_button['state'] = 'normal' + self.GoTo_entry['state'] = 'normal' + + else: + self.acquisition_box.current(0) + self.acquisition = self.dm.open_device(self.acq_var.get()) + + interface = getattr(self.acquisition, "interface", None) + if callable(interface): + self.master.menu_hardware.entryconfig("acquisition", state="normal") + else: + self.master.menu_hardware.entryconfig("acquisition", state="disabled") + + def channel_param(self): + """ When the channel parameters are changed, this function updates some internal variables and the graphical interface + + :return: None + """ + ## Channel 1 labels ---------------------------------- + if self.Ch1_param1_var.get() == 'Xsignal' and self.Ch1_param2_var.get() == 'Rbaseline': + self.plot_format['Ch1_ylabel'] = 'Xsignal/Rbaseline' + elif self.Ch1_param1_var.get() == 'Xsignal' and self.Ch1_param2_var.get() == 'ND': + self.plot_format['Ch1_ylabel'] = 'Xsignal' + elif self.Ch1_param1_var.get() == 'Rsignal' and self.Ch1_param2_var.get() == 'Rbaseline': + self.plot_format['Ch1_ylabel'] = 'Rsignal/Rbaseline' + elif self.Ch1_param1_var.get() == 'Rsignal' and self.Ch1_param2_var.get() == 'ND': + self.plot_format['Ch1_ylabel'] = 'Rsignal' + ## Channel 2 labels ---------------------------------- + if self.Ch2_param_var.get() == 'Rbaseline': + self.plot_format['Ch2_ylabel'] = 'Rbaseline' + + self.master.update_plot_axis(self.plot_format) + + + def null(self, *args, **kwargs): + """ Empty function that does nothing + + :return: None + """ + pass + + def start_stop_scan(self): + """ Starts and stops an scan + + :return: None + """ + self.run_check() + if self.stop: + self.prepare_scan() + else: + self.stop = True + self.finish_scan() + + def pause_scan(self): + """ Pauses an scan or resumes the acquisition + + :return: None + """ + self.stop = not self.stop + + if self.stop: + self.pause_button['text'] = 'Resume' + else: + self.pause_button['text'] = 'Pause' + + self.get_next_datapoint() + + def prepare_scan_lockin(self): + """ Any scan is divided in three stages: + 1) Prepare the conditions of the scan (this function), getting starting point, integration time and creating all relevant variables. + 2) Runing the scan, performed by a recursive function "mode_spectrometer" or "mode_lockin" + 3) Finish the scan, where we update some variables and save the data. + + :return: None + """ + + self.update_integration_time() + self.update_waiting_time() + self.acquisition.open() + # Get the scan conditions + self.start_wl = max(float(self.Start_entry.get()), 250) + self.stop_wl = max(min(float(self.Stop_entry.get()), 3000), self.start_wl + 1) + step = min(max(float(self.Step_entry.get()), self.acquisition.min_wavelength), self.stop_wl - self.start_wl) + + # # If we are in a batch, we proceed to the next point + if self.batch.ready: + self.batch.batch_proceed() + + self.move(self.start_wl, speed='Fast') + print('Starting scan...') + + # Create the record array + self.size = int(np.ceil((self.stop_wl - self.start_wl + 0.5 * step) / step)) + self.num = self.size + + self.record = np.zeros((self.size, 7)) ## Data to be saved + self.record[:, 0] = np.arange(self.start_wl, self.stop_wl + 0.5 * step, step) + self.record[:, 1] = self.record[:, 1] * np.NaN + self.record[:, 2] = self.record[:, 1] * np.NaN + self.record[:, 3] = self.record[:, 1] * np.NaN + self.record[:, 4] = self.record[:, 1] * np.NaN + + self.plotdata = np.zeros((self.size, 3)) ## Data to be plotted + self.plotdata[:, 0] = self.record[:, 0] + self.plotdata[:, 1] = self.plotdata[:, 1] * np.NaN + self.plotdata[:, 2] = self.plotdata[:, 1] * np.NaN + + self.master.prepare_meas(self.record) + + self.i = 0 + + self.scan_running() + + self.mode_lockin() + + def mode_lockin(self): + """ Gets the next data point in a scan. This function depends on the acquisition device + + :return: None + """ + + if not self.stop: + Xc,Yc,Xsum,Ysum,Xdif,Ydif = self.acquisition.measure() + + ## Correct for RMS and half signals + Xc = Xc * sqrt(2) + Yc = Yc * sqrt(2) + Xsum = Xsum * 2 * sqrt(2) + Ysum = Ysum * 2 * sqrt(2) + Xdif = Xdif * 2 * sqrt(2) + Ydif = Ydif * 2 * sqrt(2) + + self.record[self.i, 1] = Xc + self.record[self.i, 2] = Yc + self.record[self.i, 3] = Xsum + self.record[self.i, 4] = Ysum + self.record[self.i, 5] = Xdif + self.record[self.i, 6] = Ydif + + if self.plot_format['Ch1_ylabel'] == 'Xsignal/Rbaseline': + self.plotdata[self.i, 1] = Xsum / sqrt(Xc**2+Yc**2) + elif self.plot_format['Ch1_ylabel'] == 'Rsignal/Rbaseline': + self.plotdata[self.i, 1] = sqrt(Xsum**2+Ysum**2) / sqrt(Xc**2+Yc**2) + elif self.plot_format['Ch1_ylabel'] == 'Xsignal': + self.plotdata[self.i, 1] = Xsum + elif self.plot_format['Ch1_ylabel'] == 'Rsignal': + self.plotdata[self.i, 1] = sqrt(Xsum**2+Ysum**2) + else: + self.plotdata[self.i, 1] = 0 ## Zero signal indicates an error + print('The signal in Channel 1 is 0, possibly due to a communication Error') + + if self.plot_format['Ch2_ylabel'] == 'Rbaseline': + self.plotdata[self.i, 2] = sqrt(Xc**2+Yc**2) + else: + self.plotdata[self.i, 2] = 0 ## Zero signal indicates an error + print('The signal in Channel 2 is 0, possibly due to a communication Error') + + + self.master.update_plot(self.plotdata) + + if self.i < self.num - 1: + self.i += 1 + self.move(self.record[self.i, 0], speed='Fast') + self.master.window.after(int(self.integration_time + self.waiting_time), self.mode_lockin) + + else: + self.finish_scan() + + def prepare_scan_spectrometer(self): + """ Any scan is divided in three stages: + 1) Prepare the conditions of the scan (this function), getting starting point, integration time and creating all relevant variables. + 2) Runing the scan, performed by a recursive function "mode_spectrometer" or "mode_lockin" + 3) Finish the scan, where we update some variables and save the data. + + :return: None + """ + self.options_mono = {'opt1': self.start_wl, + } #### Edit as required + + self.update_integration_time() + self.update_waiting_time() + + # We check the background is not none and offer to update it + if self.background is None: + meas_bg = messagebox.askyesno(message='There is no background spectrum for this integration time!', + detail='Do you want to measure it now?', icon='question', + title='Measure background?') + + self.get_background(meas_bg) + + # # If we are in a batch, we proceed to the next point + if self.batch.ready: + self.batch.batch_proceed() + + # If the integration time is too long, we have to split the acquisition in several steps, + # otherwise the spectrometer hangs + self.num = int(np.ceil(self.integration_time / self.acquisition.max_integration_time)) + + # Here we select the wavelength range we want to record and re-shape the record array + # Get the scan conditions + self.start_wl = max(float(self.Start_entry.get()), 300) + self.stop_wl = max(min(float(self.Stop_entry.get()), 2000), self.start_wl + 1) + wl = self.acquisition.measure()[0] + self.idx = np.where((self.start_wl <= wl) & (wl <= self.stop_wl)) + self.size = len(self.idx[0]) + + # Create the record array + self.record = np.zeros((self.size, 3)) + self.record[:, 0] = wl[self.idx] + self.record[:, 2] = self.background[self.idx] + + self.master.prepare_meas(self.record) + + self.i = 0 + + self.scan_running() + + self.mode_spectrometer() + + def mode_spectrometer(self): + """ Gets the whole spectrum at once recorded by the spectrometer in the range selected + + :return: None + """ + + if not self.stop: + data = self.acquisition.measure() + intensity = data[1][self.idx] - self.background[self.idx] + self.record[:, 1] = (intensity + self.i * self.record[:, 1]) / (self.i + 1.) + + self.master.update_plot(self.record) + + if self.i < self.num - 1: + self.i = self.i + 1 + self.master.window.after(int(self.integration_time / self.num), self.mode_spectrometer) + + else: + self.finish_scan() + + def finish_scan(self): + """ Finish the scan, updating some global variables, saving the data in the temp file and offering to save the + data somewhere else. + + :return: None + """ + + if self.batch.ready: + self.master.finish_meas(self.record, finish=False) + self.batch.batch_wrapup(self.record) + else: + self.master.finish_meas(self.record, finish=True) + + if self.stop or not self.batch.ready: + # We reduce the number of counts in the batch to resume at the unfinished point if necessary + # In other words, it repeats the last, un-finished measurement + self.batch.count = max(self.batch.count - 1, 0) + self.scan_stopped() + + else: + self.prepare_scan() + + def check_inputs(self, parameter, value, min, max): + """ Checks that the input parameters are within a specified range. + + :return: Error popup + """ + if value < min or value > max: + messagebox.showerror("Error", parameter+" is out of range!") + self.run_button['state'] = 'enabled' + self.run_ok.append(False) + + else: + self.run_ok.append(True) + + def run_check(self): + self.Start_wave = float(self.Start_entry.get()) + self.Stop_wave = float(self.Stop_entry.get()) + self.Step_wave = float(self.Step_entry.get()) + + ## Check that inputs are within safe/correct limits + self.run_ok = [] + self.check_inputs('Start wavelength', self.Start_wave, 100, 2000) + self.check_inputs('End wavelength', self.Stop_wave, 100, 2000) + self.check_inputs('Step', self.Step_wave, 0.1, 2000) + + def scan_running(self): + """ Updates the graphical interface, disable the buttoms that must be disabled during the measurement. + + :return: None + """ + self.scan_button['text'] = 'Stop' + self.pause_button['state'] = 'enabled' + self.record_live_button['state'] = 'disabled' + self.GoTo_button['state'] = 'disabled' + self.integration_time_button['state'] = 'disabled' + self.waiting_time_button['state'] = 'disabled' + self.background_button['state'] = 'disabled' + self.clear_background_button['state'] = 'disabled' + self.stop = False + + def scan_stopped(self): + """ Returns the graphical interface to normal once the measurement has finished. + + :return: None + """ + self.scan_button['text'] = 'Run' + self.pause_button['state'] = 'disabled' + self.record_live_button['state'] = 'enabled' + self.GoTo_button['state'] = 'enabled' + self.integration_time_button['state'] = 'enabled' + self.waiting_time_button['state'] = 'enabled' + self.background_button['state'] = 'enabled' + self.clear_background_button['state'] = 'enabled' + self.stop = True + + def get_background(self, meas_bg=True): + """ Gets the background when using the spectrometer, if requested. + + :parameter: meas_bg True or False: wether to measure a background or just produce a bg with zeros + :return: None + """ + + if meas_bg: + self.update_integration_time() + self.background = self.acquisition.measure()[1] + messagebox.showinfo(message='Background taken!', detail='Press OK to continue.', title='Background taken!') + else: + self.background = self.acquisition.measure()[1]*0.0 + + def clear_background(self): + """ Clears the background when using the spectrometer. + + :return: None + """ + self.background = None + + def record_live(self): + """ Starts and stops a live recording. + + :return: None + """ + if self.stop: + self.stop = False + self.record_live_button['text'] = 'Stop' + self.pause_live_button['state'] = 'enabled' + self.scan_button['state'] = 'disabled' + self.background_button['state'] = 'disabled' + self.clear_background_button['state'] = 'disabled' + self.start_live() + else: + self.record_live_button['text'] = 'Record' + self.pause_live_button['state'] = 'disabled' + self.scan_button['state'] = 'enabled' + self.background_button['state'] = 'enabled' + self.clear_background_button['state'] = 'enabled' + self.stop = True + self.finish_live() + + def pause_live(self): + """ Pauses a live recording or resumes the acquisition + """ + self.stop = not self.stop + + if self.stop: + self.pause_live_button['text'] = 'Resume' + else: + self.pause_live_button['text'] = 'Pause' + + self.live() + + def prepare_live_lockin(self): + """ Prepares the lock-in live acquisition and prepare some variables + """ + self.acquisition.open() + self.goto() + self.update_integration_time() + + self.window_points = int(self.window_live_entry.get()) + + self.live_data = np.zeros((self.window_points, 3)) + self.live_data[:, 0] = np.arange(self.window_points) + + # Removes all plots, but not the data, and change the horizontal axis conditions + self.master.clear_plot(xtitle='Time', ticks='off') + self.master.prepare_meas(self.live_data) + + self.live_lockin() + + def live_lockin(self): + """ Runs the live lock-in acquisition + """ + if not self.stop: + self.live_data[:-1, 1] = self.live_data[1:, 1] + self.live_data[:-1, 2] = self.live_data[1:, 2] + + Xc,Yc,Xsum,Ysum,Xdif,Ydif = self.acquisition.measure() + + ## Correct for RMS and half signals + Xc = Xc * sqrt(2) + Yc = Yc * sqrt(2) + Xsum = Xsum * 2 * sqrt(2) + Ysum = Ysum * 2 * sqrt(2) + Xdif = Xdif * 2 * sqrt(2) + Ydif = Ydif * 2 * sqrt(2) + + if self.plot_format['Ch1_ylabel'] == 'Xsignal/Rbaseline': + self.live_data[-1, 1] = Xsum / sqrt(Xc**2+Yc**2) + elif self.plot_format['Ch1_ylabel'] == 'Rsignal/Rbaseline': + self.live_data[-1, 1] = sqrt(Xsum**2+Ysum**2) / sqrt(Xc**2+Yc**2) + elif self.plot_format['Ch1_ylabel'] == 'Xsignal': + self.live_data[-1, 1] = Xsum + elif self.plot_format['Ch1_ylabel'] == 'Rsignal': + self.live_data[-1, 1] = sqrt(Xsum**2+Ysum**2) + else: + self.live_data[-1, 1] = 0 ## Zero signal indicates an error + print('The signal in Channel 1 is 0, possibly due to a communication Error') + + if self.plot_format['Ch2_ylabel'] == 'Rbaseline': + self.live_data[-1, 2] = sqrt(Xc**2+Yc**2) + else: + self.live_data[-1, 2] = 0 ## Zero signal indicates an error + print('The signal in Channel 2 is 0, possibly due to a communication Error') + + self.master.update_plot(self.live_data) + + self.master.window.after(self.integration_time, self.live_lockin) + + def prepare_live_spectrometer(self): + """ Prepares the spectrometer live acquisition and prepare some variables + """ + self.update_integration_time() + + if self.integration_time > self.acquisition.max_integration_time: + self.integration_time = int(self.acquisition.max_integration_time) + self.acquisition.update_integration_time(self.integration_time) + + data0, data1 = self.acquisition.measure() + + self.live_data = np.zeros((len(data0), 3)) + + self.live_data[:, 0] = data0 + self.live_data[:, 1] = data1 + + # Removes all plots, but not the data, and change the horizontal axis conditions + self.master.clear_plot(xtitle='Wavelength (nm)', ticks='on') + self.master.prepare_meas(self.live_data) + + self.live_spectrometer() + + def live_spectrometer(self): + """ Runs the live spectrometer acquisition + """ + if not self.stop: + data0, data1 = self.acquisition.measure() + self.live_data[:, 1] = data1 + + self.master.update_plot(self.live_data) + + self.master.window.after(self.integration_time, self.live_spectrometer) + + def finish_live(self): + """ Finish the live acquisition, returning the front end to the scan mode + """ + self.master.replot_data(xtitle='Wavelength (nm)', ticks='on') + + def update_integration_time(self): + """ Updates the integration time + """ + old_integration_time = self.integration_time + self.integration_time = int(self.integration_time_entry.get()) + + if old_integration_time != self.integration_time: + self.clear_background() + self.integration_time = self.acquisition.update_integration_time(self.integration_time) + self.integration_time_entry.delete(0, tk.END) + self.integration_time_entry.insert(0, '%i' % self.integration_time) + + def update_waiting_time(self): + """ Updates the waiting time + """ + self.waiting_time = int(self.waiting_time_entry.get()) + + def goto(self): + """ Go to the specified wavelength + """ + wl = float(self.GoTo_entry.get()) + self.move(wl, speed='Fast') + print('Done! Wavelength = {} nm'.format(wl)) diff --git a/Experiments/temperature.py b/Experiments/temperature.py index 931df4b..c8d385f 100644 --- a/Experiments/temperature.py +++ b/Experiments/temperature.py @@ -1,347 +1,343 @@ -__author__ = 'Diego Alonso-Álvarez' - -# Libraries -import datetime - -import matplotlib -matplotlib.use('TkAgg') -import matplotlib.pyplot as plt -import matplotlib.dates as mdates -from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg, NavigationToolbar2TkAgg - -from tkinter import filedialog, ttk -import tkinter as tk - -import time -import sys -import os - -import numpy as np - -# Class definition -class Temperature(object): - """ Some text - """ - def __init__(self, splash, devman, exp_number): - - self.dm = devman - self.experiment_number = exp_number - self.splash = splash - - self.create_interface() - - self.recording = False - self.heater_on = False - self.ramp = 300 - - # We load the dummy devices by default - self.fill_devices() - - def _quit(self): - self.dm.close_device(self.control) - self.window.destroy() - self.splash.show(minus_experiment=True) - - def start_recording(self): - - if not self.recording: - self.recording = True - self.record() - else: - self.recording = False - - def enable_heater(self): - - if not self.heater_on: - self.heater_on = True - self.heater_var.set('Disable heater') - self.control.setHeater('ON') - else: - self.heater_on = False - self.heater_var.set('Enable heater') - self.control.setHeater('OFF') - - def record(self): - - # At the begining, we update the first elements of the arrays - if self.temperature_array[0] == 0: - self.time_array = [datetime.datetime.now()] - self.setpoint_array = [self.control.getSP()] - self.temperature_array = [self.control.getTemp()] - # self.setpoint_var.set(self.control.setPoint) - # self.setpoint = self.control.setPoint - # self.updateSetpoint() - self.update_refresh_time() - time.sleep(1) - - newT = self.control.getTemp() - newSP = self.control.setPoint - self.temp_var.set('{0:.2f}'.format(newT)) - - self.time_array.append(datetime.datetime.now()) - self.setpoint_array.append(newSP) - self.temperature_array.append(newT) - - # if we are in a ramp, we update the setpoint - if abs(self.setpoint-newSP) > 0: - delta = self.time_array[-1] - self.time_ini - newSP = self.temp_ini + self.ramp*min(delta.seconds, self.time_to_SP) - self.countdown_var.set('{0:.2f}'.format(self.time_to_SP-delta.seconds)) - self.control.setSP(newSP) - self.current_setpoint_var.set('{0:.2f}'.format(self.control.setPoint)) - - self.update_plot(self.time_array, self.setpoint_array, self.temperature_array) - - if self.recording: - self.window.after(self.refresh_time, self.record) - - def update_plot(self, x, y1, y2): - - self.T_plot.lines[0].set_xdata(x) - self.T_plot.lines[0].set_ydata(y1) - self.T_plot.lines[1].set_xdata(x) - self.T_plot.lines[1].set_ydata(y2) - - self.T_plot.set_xbound(lower=x[0], upper=x[-1]) - self.T_plot.set_ybound(lower=np.min(y2), upper=np.max(y2)) - - self.canvas.draw() - - def update_refresh_time(self): - self.refresh_time = int(float(self.refresh_time_var.get())*1000) - - def reset(self): - self.temperature_array = [0] - - def updateSetpoint(self): - self.time_ini = datetime.datetime.now() - self.temp_ini = self.control.setPoint - self.setpoint = self.setpoint_var.get() - - ramp = self.ramp_var.get() - if ramp == 0: - ramp = 1 - - self.ramp = abs(float(ramp)/60) * (-1)**(self.setpoint < self.temp_ini) - self.time_to_SP = (self.setpoint - self.temp_ini)/self.ramp - - print('Ramp to {0:.2f}K in {1:.2f} s.\n'.format(self.setpoint, self.time_to_SP)) - - def saveRecord(self): - - f = filedialog.asksaveasfile(defaultextension='txt') - - if f is not None: - for i in range(len(self.time_array)): - savedata = self.time_array[i].isoformat() + '\t' + \ - '{0:.2f}'.format(self.temperature_array[i]) + '\t' +\ - '{0:.2f}'.format(self.setpoint_array[i]) + '\n' - f.write(savedata) - f.close() - - def select_controller(self, *args): - - if self.control is not None: - self.dm.close_device(self.control) - - dev_name = self.control_var.get() - self.control = self.dm.open_device(dev_name) - self.setpoint_var.set(self.control.getSP()) - self.setpoint = self.control.setPoint - self.current_setpoint_var.set(self.control.setPoint) - - if self.control is None: - self.control_box.current(0) - self.control = self.dm.open_device(self.control_var.get()) - self.setpoint_var.set(self.control.getSP()) - self.setpoint = self.control.setPoint - self.current_setpoint_var.set(self.control.setPoint) - - def fill_devices(self): - """ Fills the device selectors with the corresponding type of devices - - :return: - """ - - self.control_box['values'] = self.dm.get_devices(['Temperature_controller']) - self.control_box.current(0) - - self.control = None - self.select_controller() - - def create_menu_bar(self): - """ Creates the menu bar and the elements within - """ - self.menubar = tk.Menu(self.window) - self.window['menu'] = self.menubar - - self.menu_file = tk.Menu(self.menubar) - self.menu_hardware = tk.Menu(self.menubar) - self.menu_help = tk.Menu(self.menubar) - self.menubar.add_cascade(menu=self.menu_file, label='File') - self.menubar.add_cascade(menu=self.menu_hardware, label='Hardware') - self.menubar.add_cascade(menu=self.menu_help, label='Help') - - # File menus - self.menu_file.add_command(label='New experiment', command=self.open_new_experiment) - self.menu_file.add_command(label='Save record', command=self.saveRecord) - self.menu_file.add_separator() - self.menu_file.add_command(label='Leave Mordor', command=self._quit) - - # Hardware menu - self.menu_hardware.add_command(label='Hardware configuration', command=self.dm.show) - self.menu_hardware.add_separator() - - # Help menu - self.menu_help.add_command(label='Documentation', command=self.open_documentation) - - def open_new_experiment(self): - """ Opens the splash screen to run a new experiment, in paralel to the current one - - :return: None - """ - - self.splash.show() - - def open_documentation(self): - """ Opens the documentation in the web browser - - :return: None - """ - import webbrowser - address = 'file:' + os.path.join(sys.path[0], 'Doc', 'Mordor.html') - webbrowser.open_new_tab(address) - - def create_interface(self): - - # Top level elements - self.window = tk.Toplevel(self.splash.splashroot) - self.window.geometry('+100+100') - self.window.resizable(False, False) - self.window.protocol('WM_DELETE_WINDOW', self._quit) # Used to force a "safe closing" of the program - self.window.option_add('*tearOff', False) # Prevents tearing the menus - self.window.title('Barad-dûr: Mordor\'s temperature controller') - - self.create_menu_bar() - - # Creates the main frame - plot_frame = ttk.Frame(master=self.window, padding=(5, 5, 5, 5)) - control_frame = ttk.Frame(master=self.window, padding=(15, 15, 15, 15)) - plot_frame.pack(side=tk.RIGHT, fill=tk.BOTH, expand=1) - control_frame.pack(side=tk.LEFT, fill=tk.Y, expand=0) - control_frame.rowconfigure(99, weight=1) - - # Create plot area - self.create_plot_area(plot_frame) - - # Create variables - self.temp_var = tk.StringVar() - self.temp_var.set('-') - self.setpoint_var = tk.IntVar() - self.setpoint_var.set(300) - self.current_setpoint_var = tk.IntVar() - self.current_setpoint_var.set(300) - self.heater_var = tk.StringVar() - self.heater_var.set('Enable heater') - self.ramp_var = tk.StringVar() - self.ramp_var.set(20.0) - self.countdown_var = tk.IntVar() - self.countdown_var.set(300) - self.refresh_time_var = tk.StringVar() - self.refresh_time_var.set(1) - self.visualize_min_var = tk.IntVar() - self.visualize_min_var.set(60) - self.visualize_var = tk.IntVar() - self.visualize_var.set(0) - - # Create the elements in the control panel - # Hardware widgets - hardware_frame = ttk.Labelframe(control_frame, text='Selected hardware:', padding=(0, 5, 0, 15)) - hardware_frame.columnconfigure(0, weight=1) - hardware_frame.grid(column=0, row=0, sticky=(tk.EW)) - - self.control_var = tk.StringVar() - self.control_box = ttk.Combobox(master=hardware_frame, textvariable=self.control_var, state="readonly") - self.control_box.bind('<>', self.select_controller) - self.control_box.grid(column=0, row=0, sticky=(tk.EW)) - - # Temperature frame - temp_frame = ttk.Labelframe(control_frame, text='Temperature (K):', padding=(5, 5, 5, 15)) - temp_frame.grid(column=0, row=1, sticky=(tk.NSEW)) - temp_frame.rowconfigure(1, weight=1) - temp_frame.columnconfigure(0, weight=1) - - ttk.Label(master=temp_frame, textvariable=self.temp_var, anchor=tk.CENTER).grid(column=0, row=0, sticky=tk.EW) - ttk.Button(master=temp_frame, width=10, text='Record', command=self.start_recording).grid(column=0, row=1, sticky=tk.EW) - - # Set frame - set_frame = ttk.Labelframe(master=control_frame, text='Set:', padding=(5, 5, 5, 15)) - set_frame.grid(column=0, row=2, sticky=(tk.NSEW)) - set_frame.rowconfigure(1, weight=1) - set_frame.columnconfigure(0, weight=1) - - ttk.Label(master=set_frame, text="Target set point (K):").grid(column=0, row=0, sticky=tk.EW) - ttk.Label(master=set_frame, text="Ramp rate (K/min):").grid(column=0, row=1, sticky=tk.EW) - ttk.Label(master=set_frame, text="Countdown (s):").grid(column=0, row=2, sticky=tk.EW) - ttk.Label(master=set_frame, text="Current set point (K):").grid(column=0, row=3, sticky=tk.EW) - - ttk.Entry(master=set_frame, width=10, textvariable=self.setpoint_var).grid(column=1, row=0, sticky=tk.EW) - ttk.Entry(master=set_frame, width=10, textvariable=self.ramp_var).grid(column=1, row=1, sticky=tk.EW) - ttk.Label(master=set_frame, textvariable=self.countdown_var).grid(column=1, row=2, sticky=tk.E) - ttk.Label(master=set_frame, textvariable=self.current_setpoint_var).grid(column=1, row=3, sticky=tk.E) - - ttk.Button(master=set_frame, width=10, text='Set', command=self.updateSetpoint).grid(column=1, row=4, sticky=tk.EW) - ttk.Button(master=set_frame, width=10, textvariable=self.heater_var, command=self.enable_heater).grid(column=0, row=4, sticky=tk.EW) - - # Visualize frame - visuialize_frame = ttk.Labelframe(master=control_frame, text='Visualize:', padding=(5, 5, 5, 15)) - visuialize_frame.grid(column=0, row=3, sticky=(tk.NSEW)) - visuialize_frame.rowconfigure(1, weight=1) - visuialize_frame.columnconfigure(0, weight=1) - - ttk.Button(master=visuialize_frame, width=10, text="Refresh time (s):", command=self.update_refresh_time)\ - .grid(column=0, row=0, sticky=tk.EW) - ttk.Entry(master=visuialize_frame, width=10, textvariable=self.refresh_time_var).grid(column=1, row=0, sticky=tk.EW) - ttk.Button(master=visuialize_frame, width=10, text='Reset record', command=self.reset).grid(column=0, row=1, columnspan=2, sticky=tk.EW) - - - # ttk.Radiobutton(master=visuialize_frame, text="All", variable=self.visualize_var, value=0).grid(column=0, row=1, sticky=tk.EW) - # ttk.Radiobutton(master=visuialize_frame, text="Last (min):", variable=self.visualize_var, value=1).grid(column=0, row=2, sticky=tk.EW) - # ttk.Entry(master=visuialize_frame, width=10, textvariable=self.visualize_min_var).grid(column=1, row=2, sticky=tk.EW) - - # # These commands give the control to the HW window. I am not sure what they do exactly. - # self.TC_window.lift(self.master) # Brings the hardware window to the top - # self.TC_window.transient(self.master) # ? - - def create_plot_area(self, frame): - """ Creates the plotting area and the ploting variables. - """ - - xlabel = 'Time' - ylabel = 'Temperature (K)' - - self.time_array = [datetime.datetime.now()] - self.temperature_array = [0] - self.setpoint_array = [0] - - f = plt.figure(figsize=(9, 8), dpi=72) - self.T_plot = plt.subplot(111, ylabel=ylabel, xlabel=xlabel) - self.T_plot.grid(True, color='gray') # This is the grid of the plot, not the placing comand - - self.T_plot.plot(self.time_array, self.setpoint_array, label='Set Point') - self.T_plot.plot(self.time_array, self.temperature_array, 'o', label='Temperature') - - f.autofmt_xdate() - myFmt = mdates.DateFormatter('%H:%M:%S') - self.T_plot.xaxis.set_major_formatter(myFmt) - self.T_plot.legend(loc='lower left') - f.tight_layout() - - self.canvas = FigureCanvasTkAgg(f, frame) - self.canvas.get_tk_widget().pack() - self.canvas.show() - - toolbar = NavigationToolbar2TkAgg(self.canvas, frame) - toolbar.update() - self.canvas._tkcanvas.pack(side=tk.TOP, fill=tk.BOTH, expand=1) - +__author__ = 'Diego Alonso-Álvarez' + +# Libraries +import datetime + +import matplotlib +matplotlib.use('TkAgg') +import matplotlib.pyplot as plt +import matplotlib.dates as mdates +from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg, NavigationToolbar2TkAgg + +from tkinter import filedialog, ttk +import tkinter as tk + +import time +import sys +import os + +import numpy as np + +# Class definition +class Temperature(object): + """ Some text + """ + def __init__(self, splash, devman, exp_number): + + self.dm = devman + self.experiment_number = exp_number + self.splash = splash + + self.create_interface() + + self.recording = False + self.heater_on = False + self.ramp = 300 + + # We load the dummy devices by default + self.fill_devices() + + def _quit(self): + self.dm.close_device(self.control) + self.window.destroy() + self.splash.show(minus_experiment=True) + + def start_recording(self): + + if not self.recording: + self.recording = True + self.record() + else: + self.recording = False + + def enable_heater(self): + + if not self.heater_on: + self.heater_on = True + self.heater_var.set('Disable heater') + self.control.setHeater('ON') + else: + self.heater_on = False + self.heater_var.set('Enable heater') + self.control.setHeater('OFF') + + def record(self): + + # At the begining, we update the first elements of the arrays + if self.temperature_array[0] == 0: + self.time_array = [datetime.datetime.now()] + self.setpoint_array = [self.control.getSP()] + self.temperature_array = [self.control.getTemp()] + # self.setpoint_var.set(self.control.setPoint) + # self.setpoint = self.control.setPoint + # self.updateSetpoint() + self.update_refresh_time() + time.sleep(1) + + newT = self.control.getTemp() + newSP = self.control.setPoint + self.temp_var.set('{0:.2f}'.format(newT)) + + self.time_array.append(datetime.datetime.now()) + self.setpoint_array.append(newSP) + self.temperature_array.append(newT) + + # if we are in a ramp, we update the setpoint + if abs(self.setpoint-newSP) > 0: + delta = self.time_array[-1] - self.time_ini + newSP = self.temp_ini + self.ramp*min(delta.seconds, self.time_to_SP) + self.countdown_var.set('{0:.2f}'.format(self.time_to_SP-delta.seconds)) + self.control.setSP(newSP) + self.current_setpoint_var.set('{0:.2f}'.format(self.control.setPoint)) + + self.update_plot(self.time_array, self.setpoint_array, self.temperature_array) + + if self.recording: + self.window.after(self.refresh_time, self.record) + + def update_plot(self, x, y1, y2): + + self.T_plot.lines[0].set_xdata(x) + self.T_plot.lines[0].set_ydata(y1) + self.T_plot.lines[1].set_xdata(x) + self.T_plot.lines[1].set_ydata(y2) + + self.T_plot.set_xbound(lower=x[0], upper=x[-1]) + self.T_plot.set_ybound(lower=np.min(y2), upper=np.max(y2)) + + self.canvas.draw() + + def update_refresh_time(self): + self.refresh_time = int(float(self.refresh_time_var.get())*1000) + + def reset(self): + self.temperature_array = [0] + + def updateSetpoint(self): + self.time_ini = datetime.datetime.now() + self.temp_ini = self.control.setPoint + self.setpoint = self.setpoint_var.get() + + ramp = self.ramp_var.get() + if ramp == 0: + ramp = 1 + + self.ramp = abs(float(ramp)/60) * (-1)**(self.setpoint < self.temp_ini) + self.time_to_SP = (self.setpoint - self.temp_ini)/self.ramp + + print('Ramp to {0:.2f}K in {1:.2f} s.\n'.format(self.setpoint, self.time_to_SP)) + + def saveRecord(self): + + f = filedialog.asksaveasfile(defaultextension='txt') + + if f is not None: + for i in range(len(self.time_array)): + savedata = self.time_array[i].isoformat() + '\t' + \ + '{0:.2f}'.format(self.temperature_array[i]) + '\t' +\ + '{0:.2f}'.format(self.setpoint_array[i]) + '\n' + f.write(savedata) + f.close() + + def select_controller(self, *args): + + if self.control is not None: + self.dm.close_device(self.control) + + dev_name = self.control_var.get() + self.control = self.dm.open_device(dev_name) + self.setpoint_var.set(self.control.getSP()) + self.setpoint = self.control.setPoint + self.current_setpoint_var.set(self.control.setPoint) + + if self.control is None: + self.control_box.current(0) + self.control = self.dm.open_device(self.control_var.get()) + self.setpoint_var.set(self.control.getSP()) + self.setpoint = self.control.setPoint + self.current_setpoint_var.set(self.control.setPoint) + + def fill_devices(self): + """ Fills the device selectors with the corresponding type of devices + :return: + """ + + self.control_box['values'] = self.dm.get_devices(['Temperature_controller']) + self.control_box.current(0) + + self.control = None + self.select_controller() + + def create_menu_bar(self): + """ Creates the menu bar and the elements within + """ + self.menubar = tk.Menu(self.window) + self.window['menu'] = self.menubar + + self.menu_file = tk.Menu(self.menubar) + self.menu_hardware = tk.Menu(self.menubar) + self.menu_help = tk.Menu(self.menubar) + self.menubar.add_cascade(menu=self.menu_file, label='File') + self.menubar.add_cascade(menu=self.menu_hardware, label='Hardware') + self.menubar.add_cascade(menu=self.menu_help, label='Help') + + # File menus + self.menu_file.add_command(label='New experiment', command=self.open_new_experiment) + self.menu_file.add_command(label='Save record', command=self.saveRecord) + self.menu_file.add_separator() + self.menu_file.add_command(label='Leave Mordor', command=self._quit) + + # Hardware menu + self.menu_hardware.add_command(label='Hardware configuration', command=self.dm.show) + self.menu_hardware.add_separator() + + # Help menu + self.menu_help.add_command(label='Documentation', command=self.open_documentation) + + def open_new_experiment(self): + """ Opens the splash screen to run a new experiment, in paralel to the current one + :return: None + """ + + self.splash.show() + + def open_documentation(self): + """ Opens the documentation in the web browser + :return: None + """ + import webbrowser + address = 'file:' + os.path.join(sys.path[0], 'Doc', 'Mordor.html') + webbrowser.open_new_tab(address) + + def create_interface(self): + + # Top level elements + self.window = tk.Toplevel(self.splash.splashroot) + self.window.geometry('+100+100') + self.window.resizable(False, False) + self.window.protocol('WM_DELETE_WINDOW', self._quit) # Used to force a "safe closing" of the program + self.window.option_add('*tearOff', False) # Prevents tearing the menus + self.window.title('Barad-dûr: Mordor\'s temperature controller') + + self.create_menu_bar() + + # Creates the main frame + plot_frame = ttk.Frame(master=self.window, padding=(5, 5, 5, 5)) + control_frame = ttk.Frame(master=self.window, padding=(15, 15, 15, 15)) + plot_frame.pack(side=tk.RIGHT, fill=tk.BOTH, expand=1) + control_frame.pack(side=tk.LEFT, fill=tk.Y, expand=0) + control_frame.rowconfigure(99, weight=1) + + # Create plot area + self.create_plot_area(plot_frame) + + # Create variables + self.temp_var = tk.StringVar() + self.temp_var.set('-') + self.setpoint_var = tk.IntVar() + self.setpoint_var.set(300) + self.current_setpoint_var = tk.IntVar() + self.current_setpoint_var.set(300) + self.heater_var = tk.StringVar() + self.heater_var.set('Enable heater') + self.ramp_var = tk.StringVar() + self.ramp_var.set(20.0) + self.countdown_var = tk.IntVar() + self.countdown_var.set(300) + self.refresh_time_var = tk.StringVar() + self.refresh_time_var.set(1) + self.visualize_min_var = tk.IntVar() + self.visualize_min_var.set(60) + self.visualize_var = tk.IntVar() + self.visualize_var.set(0) + + # Create the elements in the control panel + # Hardware widgets + hardware_frame = ttk.Labelframe(control_frame, text='Selected hardware:', padding=(0, 5, 0, 15)) + hardware_frame.columnconfigure(0, weight=1) + hardware_frame.grid(column=0, row=0, sticky=(tk.EW)) + + self.control_var = tk.StringVar() + self.control_box = ttk.Combobox(master=hardware_frame, textvariable=self.control_var, state="readonly") + self.control_box.bind('<>', self.select_controller) + self.control_box.grid(column=0, row=0, sticky=(tk.EW)) + + # Temperature frame + temp_frame = ttk.Labelframe(control_frame, text='Temperature (K):', padding=(5, 5, 5, 15)) + temp_frame.grid(column=0, row=1, sticky=(tk.NSEW)) + temp_frame.rowconfigure(1, weight=1) + temp_frame.columnconfigure(0, weight=1) + + ttk.Label(master=temp_frame, textvariable=self.temp_var, anchor=tk.CENTER).grid(column=0, row=0, sticky=tk.EW) + ttk.Button(master=temp_frame, width=10, text='Record', command=self.start_recording).grid(column=0, row=1, sticky=tk.EW) + + # Set frame + set_frame = ttk.Labelframe(master=control_frame, text='Set:', padding=(5, 5, 5, 15)) + set_frame.grid(column=0, row=2, sticky=(tk.NSEW)) + set_frame.rowconfigure(1, weight=1) + set_frame.columnconfigure(0, weight=1) + + ttk.Label(master=set_frame, text="Target set point (K):").grid(column=0, row=0, sticky=tk.EW) + ttk.Label(master=set_frame, text="Ramp rate (K/min):").grid(column=0, row=1, sticky=tk.EW) + ttk.Label(master=set_frame, text="Countdown (s):").grid(column=0, row=2, sticky=tk.EW) + ttk.Label(master=set_frame, text="Current set point (K):").grid(column=0, row=3, sticky=tk.EW) + + ttk.Entry(master=set_frame, width=10, textvariable=self.setpoint_var).grid(column=1, row=0, sticky=tk.EW) + ttk.Entry(master=set_frame, width=10, textvariable=self.ramp_var).grid(column=1, row=1, sticky=tk.EW) + ttk.Label(master=set_frame, textvariable=self.countdown_var).grid(column=1, row=2, sticky=tk.E) + ttk.Label(master=set_frame, textvariable=self.current_setpoint_var).grid(column=1, row=3, sticky=tk.E) + + ttk.Button(master=set_frame, width=10, text='Set', command=self.updateSetpoint).grid(column=1, row=4, sticky=tk.EW) + ttk.Button(master=set_frame, width=10, textvariable=self.heater_var, command=self.enable_heater).grid(column=0, row=4, sticky=tk.EW) + + # Visualize frame + visuialize_frame = ttk.Labelframe(master=control_frame, text='Visualize:', padding=(5, 5, 5, 15)) + visuialize_frame.grid(column=0, row=3, sticky=(tk.NSEW)) + visuialize_frame.rowconfigure(1, weight=1) + visuialize_frame.columnconfigure(0, weight=1) + + ttk.Button(master=visuialize_frame, width=10, text="Refresh time (s):", command=self.update_refresh_time)\ + .grid(column=0, row=0, sticky=tk.EW) + ttk.Entry(master=visuialize_frame, width=10, textvariable=self.refresh_time_var).grid(column=1, row=0, sticky=tk.EW) + ttk.Button(master=visuialize_frame, width=10, text='Reset record', command=self.reset).grid(column=0, row=1, columnspan=2, sticky=tk.EW) + + + # ttk.Radiobutton(master=visuialize_frame, text="All", variable=self.visualize_var, value=0).grid(column=0, row=1, sticky=tk.EW) + # ttk.Radiobutton(master=visuialize_frame, text="Last (min):", variable=self.visualize_var, value=1).grid(column=0, row=2, sticky=tk.EW) + # ttk.Entry(master=visuialize_frame, width=10, textvariable=self.visualize_min_var).grid(column=1, row=2, sticky=tk.EW) + + # # These commands give the control to the HW window. I am not sure what they do exactly. + # self.TC_window.lift(self.master) # Brings the hardware window to the top + # self.TC_window.transient(self.master) # ? + + def create_plot_area(self, frame): + """ Creates the plotting area and the ploting variables. + """ + + xlabel = 'Time' + ylabel = 'Temperature (K)' + + self.time_array = [datetime.datetime.now()] + self.temperature_array = [0] + self.setpoint_array = [0] + + f = plt.figure(figsize=(9, 8), dpi=72) + self.T_plot = plt.subplot(111, ylabel=ylabel, xlabel=xlabel) + self.T_plot.grid(True, color='gray') # This is the grid of the plot, not the placing comand + + self.T_plot.plot(self.time_array, self.setpoint_array, label='Set Point') + self.T_plot.plot(self.time_array, self.temperature_array, 'o', label='Temperature') + + f.autofmt_xdate() + myFmt = mdates.DateFormatter('%H:%M:%S') + self.T_plot.xaxis.set_major_formatter(myFmt) + self.T_plot.legend(loc='lower left') + f.tight_layout() + + self.canvas = FigureCanvasTkAgg(f, frame) + self.canvas.get_tk_widget().pack() + self.canvas.show() + + toolbar = NavigationToolbar2TkAgg(self.canvas, frame) + toolbar.update() + self.canvas._tkcanvas.pack(side=tk.TOP, fill=tk.BOTH, expand=1) From 03fb673488610fe32cd0f4f2ae78da7880b9b54a Mon Sep 17 00:00:00 2001 From: Andrew M Telford Date: Mon, 23 Jul 2018 12:01:17 +1000 Subject: [PATCH 3/4] Add files via upload Updated mordor for Photoreflectance experiment addition --- Mordor.py | 42 ++++++++++++++++++++++++++---------------- 1 file changed, 26 insertions(+), 16 deletions(-) diff --git a/Mordor.py b/Mordor.py index 8fd0fa6..0a50f44 100644 --- a/Mordor.py +++ b/Mordor.py @@ -1,5 +1,5 @@ __author__ = 'Diego Alonso-Álvarez' -__version__= '2.0_beta_4' +__version__= '2.0_beta_5' __email__ = 'd.alonso-alvarez@imperial.ac.uk' __contributors__ = ['Markus Furher', 'Ture Hinrichsen', 'Jose Videira', 'Tomos Thomas', 'Thomas Wilson', 'Andrew M. Telford'] @@ -21,11 +21,11 @@ import plot_utils as pu import tools -from Experiments import Spectroscopy, IV, Temperature, Flash, CV +from Experiments import Spectroscopy, IV, Temperature, Flash, CV, Photoreflectance from Devices import device_manager class Mordor(object): - """ This class is the core of Mordor. It controls the ploting and saving of the data in the different experiments + """ This class is the core of Mordor. It controls the plotting and saving of the data in the different experiments as well as ensuring that there is a safe *close* and *opening* of the program """ @@ -168,13 +168,13 @@ def save_scan(self): :return: None """ - if self.experiment.id == 'CV': + if self.experiment.id == 'CV' or self.experiment.id == 'PR': self.experiment.update_header() ## This updates the file's header - + self.save.show((self.selected_meas,), (self.experiment.header,), (self.experiment.extension,)) def open_new_experiment(self): - """ Opens the splash screen to run a new experiment, in paralel to the current one + """ Opens the splash screen to run a new experiment, in parallel to the current one :return: None """ @@ -202,11 +202,11 @@ def create_plot_area(self, plot_format): self.Ch1.grid(True, color='gray', axis='both') # This is the grid of the plot, not the placing comand self.Ch2.grid(True, color='gray', axis='both') - + ## Test if optional plot settings are present in the specific experiment ## (e.g. these are all present in the CV experiment, but not in IV) if 'x_scale' in plot_format: - self.Ch1.set_xscale(plot_format['x_scale']) + self.Ch1.set_xscale(plot_format['x_scale']) self.Ch2.set_xscale(plot_format['x_scale']) if 'Ch1_scale' in plot_format: self.Ch1.set_yscale(plot_format['Ch1_scale']) @@ -276,7 +276,7 @@ def clearAxis(self): if n > 0: self.data_list.delete(0, last=tk.END) - + def prepare_meas(self, initial_data): @@ -285,10 +285,10 @@ def prepare_meas(self, initial_data): for i in range(n): plt.setp(self.Ch1.lines[i], color='k') plt.setp(self.Ch2.lines[i], color='k') - + self.Ch1.plot(initial_data[:, 0], initial_data[:, 1]+1, 'r') self.Ch2.plot(initial_data[:, 0], initial_data[:, 2]+1, 'r') - # We are reading both channels simultaneously + # We are reading both channels simultaneously def finish_meas(self, data, finish): @@ -314,7 +314,7 @@ def update_plot(self, data): pu.update(self.Ch2, data[:, 2], data[:, 0]) self.canvas.draw() - + def update_plot_axis(self, plot_format): ## When Y labels are changed pu.update_labels(self.Ch1, plot_format['xlabel'], plot_format['Ch1_ylabel']) pu.update_labels(self.Ch2, plot_format['xlabel'], plot_format['Ch2_ylabel']) @@ -322,7 +322,7 @@ def update_plot_axis(self, plot_format): ## When Y labels are changed pu.update_yscales(self.Ch2, plot_format['Ch2_scale']) pu.update_xscales(self.Ch1, plot_format['x_scale']) pu.update_xscales(self.Ch2, plot_format['x_scale']) - + self.canvas.draw() def clear_plot(self, xtitle='X axis', ticks='on'): @@ -374,7 +374,7 @@ def update_data_selected(self, *args): self.canvas.draw() def remove_selected(self, *args): - j = int(self.data_list.curselection()[0]) + j = int(self.data_list.curselection()[0]) if j != None: # Removes selected plot self.Ch1.lines.remove(self.Ch1.lines[j]) @@ -385,7 +385,7 @@ def remove_selected(self, *args): del self.all_data_names[j] self.selected_meas = None self.data_list.delete(j) - + class Save(object): """ Class for saving data in a formatted way """ @@ -541,6 +541,7 @@ def __init__(self): ttk.Button(control_frame, text='Temperature', command=self.temperature, style="Splash.TButton").grid(column=1, row=2, sticky=tk.NSEW) ttk.Button(control_frame, text='Flash', command=self.flash, style="Splash.TButton").grid(column=1, row=3, sticky=tk.NSEW) ttk.Button(control_frame, text='CV', command=self.cv, style="Splash.TButton").grid(column=1, row=4, sticky=tk.NSEW) + ttk.Button(control_frame, text='Photoreflectance', command=self.photoreflectance, style="Splash.TButton").grid(column=1, row=5, sticky=tk.NSEW) # Small buttons frame smallbuttons = ttk.Frame(control_frame) @@ -659,7 +660,7 @@ def flash(self): self.hide() self.runing.append(Flash(self, self.devman, self.experiments, Save)) self.experiments = self.experiments + 1 - + def cv(self): """ Creates an instance of Mordor to run CV experiments @@ -669,5 +670,14 @@ def cv(self): self.runing.append(Mordor(self, CV, self.devman, self.experiments)) self.experiments = self.experiments + 1 + def photoreflectance(self): + """ Creates an instance of Mordor to run photoreflectance experiments + + :return: None + """ + self.hide() + self.runing.append(Mordor(self, Photoreflectance, self.devman, self.experiments)) + self.experiments = self.experiments + 1 + if __name__ == '__main__': splash = Splash() From 9fcf9418d87eedae4e4fd3e1ad164a1b37f237ae Mon Sep 17 00:00:00 2001 From: Andrew M Telford Date: Mon, 23 Jul 2018 12:01:51 +1000 Subject: [PATCH 4/4] Add files via upload Updated plot_utils for new experiment Photoreflectance