Skip to content
Snippets Groups Projects
Commit e02df711 authored by Alexandros Asonitis's avatar Alexandros Asonitis
Browse files

First photodiode interface to be tested!

parent b7755632
No related branches found
No related tags found
No related merge requests found
"""
This is the photodiode measurement. Now the parser arguments are widgets
"""
import sys
import os
import time
import datetime
import traceback
import numpy as np
import matplotlib.pyplot as plt
try:
import ivl
from ivl.instruments import Agilent66332A
from ivl.measurements import ivl_scan_photodiode
except ModuleNotFoundError:
sys.path.append(".")
sys.path.append("..")
import ivl
from ivl.instruments import Agilent66332A
from ivl.measurements import ivl_scan_photodiode
from PyQt6.QtWidgets import *
from PyQt6.QtGui import *
from PyQt6.QtCore import *
from matplotlib.backends.backend_qtagg import FigureCanvasQTAgg,NavigationToolbar2QT as NavigationToolbar
from matplotlib.figure import Figure
import tkinter as tk
from tkinter import filedialog
import tkinter.messagebox
def create_file(filename):
root = tk.Tk()
root.withdraw()
root.lift() #show window above all other applications
root.attributes("-topmost", True)#window stays above all other applications
file = filedialog.asksaveasfilename(defaultextension=".txt", filetypes=[("Text files","*.txt")],title = "save results path",initialfile =filename)
#check if the file path is correct(.txt)
while file.endswith(".txt") == False:
#open again filedialog with error message box
tk.messagebox.showerror(message='invalid filename!')
file = filedialog.asksaveasfilename(defaultextension=".txt", filetypes=[("Text files","*.txt")],title = "save results path",initialfile =filename)
root.destroy()
return file
def information_box(information):
#open dialog and hide the main window
root = tk.Tk()
root.withdraw()
root.lift() #show window above all other applications
root.attributes("-topmost", True)#window stays above all other applications
#display meaagebox
tkinter.messagebox.showinfo(message=information)
root.destroy()
class MplCanvas(FigureCanvasQTAgg):
def __init__(self):
fig = Figure(figsize=(15,9),dpi=100)
self.axes = fig.subplots(nrows=2,ncols=2)
self.axes = self.axes.flatten()
self.axes[0].set_xlabel("Voltage [$V$]")
self.axes[0].set_ylabel("Current Density [$mA/cm^2$]")
self.axes[1].set_xlabel("Voltage [$V$]")
self.axes[1].set_ylabel("Radiance [$W/m^2*sr$]")
self.axes[2].set_xlabel("Current Density [$mA/cm^2$]")
self.axes[2].set_ylabel("Radiance [$W/m^2*sr$]")
self.axes[3].set_xlabel("Radiance [$W/m^2*sr$]")
self.axes[3].set_ylabel("EQE [%]")
# set the scales
self.axes[2].set_xscale("log")
self.axes[3].set_xscale("log")
self.axes[2].set_yscale("log")
self.axes[0].set_yscale("log")
self.axes[1].set_yscale("log")
super().__init__(fig)
def clear_axes(self):
for ax in self.axes:
# remove line plots
for line in ax.get_lines():
line.remove()
#remove scatter plots
for scatter in ax.collections:
scatter.remove()
ax.set_prop_cycle(None) # Reset colors
# define the mainwindow
class MainWindow(QMainWindow):
def __init__(self):
super().__init__()
self.setupUI()
def setupUI(self):
"""
Here we set the layout of the app
V_start : First voltage point in [V]
V_stop : "Last voltage point in [V]",
V_step : Voltage sweep increament in [V]
for the file we will create a Filedialog
the info can be added
info: Information to add to the header of result file.
points: Points to average for a single measurement.
(Reduce to reduce voltage on time. Reducing below
~100 points does not yield increase measurement speed above ~ 50 ms
per data points.)
offset: offset voltage of amplifier in [V]. If not supplied,offset is measured at the start of the script
delay: Time in [s] to wait between measurement points. (Voltage is
set to 0 V during this time, mimicking a simple pulsed measurement)
address: Address of GPIB and serial interfaces
verbose: "Write info about current measurement state" True/False
RFA: Feedback resistivity * configuration factor * pinhole area
spectrum: path to reference spectrum for calculation of Radio-photometric quantities
substrate:"Active area in cm^2"
We always display plots
"""
self.widget = QWidget() # Abstract main widget
self.app_grid = QGridLayout()
self.parameters = QVBoxLayout()
self.start_meas = QPushButton(text="Start Measurement")
self.load_spectrum = QPushButton(text = "Load spectrum reference file")
# I will search for the acceptable range of values later
self.v_start = QDoubleSpinBox()
self.v_start.setDecimals(3)
self.v_start.setRange(-20.475,20.475)
self.v_start.setValue(-8)
self.v_stop = QDoubleSpinBox()
self.v_stop.setDecimals(3)
self.v_stop.setRange(-20.475,20.475)
self.v_stop.setValue(2)
self.v_step = QDoubleSpinBox()
self.v_step.setDecimals(3)
self.v_step.setRange(-2*20.475,2*20.475)
self.v_step.setValue(0.5)
self.info = QPlainTextEdit()
self.points = QSpinBox()
self.points.setRange(0,4096)
self.points.setValue(4096)
self.offset = QDoubleSpinBox()
self.offset.setDecimals(4)
self.offset.setRange(5e-3,10e-3)
self.offset.setValue(7.5e-3)
self.measure_offset = QCheckBox("Measure offset voltage instead of using the above value")
self.delay = QDoubleSpinBox() #only softwrare delay leave it like this
# Minimum 0 Maximum 99.99 and 2 decimals places
self.address_serial = QSpinBox() # well this is also ok for the 0 to 99 just set the default value
self.address_serial.setValue(3)
self.address_gpib = QSpinBox()
self.address_gpib.setValue(1)
self.verbose = QCheckBox("Verbose")
self.rfa = QDoubleSpinBox() # here also set the default value
self.rfa.setDecimals(6)
self.rfa.setValue(0.827575)
self.spectrum = QLineEdit()
self.substrate = QDoubleSpinBox()
self.substrate.setRange(0,1)
self.substrate.setValue(0.11)
self.clear_plots = QCheckBox("Clear Plots Before Measurement")
self.parameters.addWidget(QLabel("First voltage point in [V]"))
self.parameters.addWidget(self.v_start)
self.parameters.addWidget(QLabel("Last voltage point in [V]"))
self.parameters.addWidget(self.v_stop)
self.parameters.addWidget(QLabel("Voltage sweep increament in [V]"))
self.parameters.addWidget(self.v_step)
self.parameters.addWidget(QLabel("Information to add to the header of result file"))
self.parameters.addWidget(self.info)
self.parameters.addWidget(QLabel("Points to average for a single measurement"))
self.parameters.addWidget(self.points)
self.parameters.addWidget(QLabel("Offset voltage of amplifier in [V]"))
self.parameters.addWidget(self.offset)
self.parameters.addWidget(self.measure_offset)
self.parameters.addWidget(QLabel("Time in [s] to wait between measurement points"))
self.parameters.addWidget(self.delay)
self.parameters.addWidget(QLabel("Serial address"))
self.parameters.addWidget(self.address_serial)
self.parameters.addWidget(QLabel("GPIB address"))
self.parameters.addWidget(self.address_gpib)
self.parameters.addWidget(self.verbose)
self.parameters.addWidget(QLabel("RFA: Feedback resistivity * configuration factor * pinhole area"))
self.parameters.addWidget(self.rfa)
self.parameters.addWidget(QLabel("path to reference spectrum for calculation of Radio-photometric quantities"))
self.parameters.addWidget(self.spectrum)
self.parameters.addWidget(QLabel("Active area in cm^2"))
self.parameters.addWidget(self.substrate)
self.parameters.addWidget(self.clear_plots)
self.parameters.addWidget(self.load_spectrum)
self.parameters.addWidget(self.start_meas)
self.parameters_widget = QWidget()
self.parameters_widget.setLayout(self.parameters) # an abstract widget to hold the QVBoxLayout
self.app_grid.addWidget(self.parameters_widget,0,0)
# add the canvas
# abstract widget for figure
self.figure_widget = QWidget()
self.sc = MplCanvas()
self.figure_toolbar = NavigationToolbar(self.sc, self)
self.figure_widget_layout = QVBoxLayout()
self.figure_widget_layout.addWidget(self.figure_toolbar)
self.figure_widget_layout.addWidget(self.sc)
self.figure_widget.setLayout(self.figure_widget_layout)
self.app_grid.addWidget(self.figure_widget,0,1)
self.setWindowTitle("Photodiode IVL Measurement")
self.widget.setLayout(self.app_grid)
self.setCentralWidget(self.widget)
self.showMaximized()
self.start_meas.clicked.connect(self.start_meas_clicked)
self.load_spectrum.clicked.connect(self.load_spectrum_clicked)
#initialize a counter to keep track of how many measurements are done
self.counter = 1
# use of threads
self.threadpool = QThreadPool()
def start_meas_clicked(self):
self.worker = Worker(self)
self.threadpool.start(self.worker)
def load_spectrum_clicked(self):
self.widget.setEnabled(False)
root = tk.Tk()
root.withdraw()
root.lift() #show window above all other applications
root.attributes("-topmost", True)#window stays above all other applications
file = filedialog.askopenfilename(title ='Select spectrum reference file')
root.destroy()
self.spectrum.setText(file)
self.widget.setEnabled(True)
class Worker(QRunnable):
def __init__(self,window:MainWindow):
self.window = window # Keep a referernce to the main window
super().__init__()
@pyqtSlot()
def run(self):
self.window.widget.setEnabled(False)
with(
Agilent66332A(address=str(self.window.address_gpib.value()),timeout=30) as agi_source,
Agilent66332A(connection = "serial", address = str(self.window.address_serial.value()),timeout=30) as agi_photo):
# clear the plots
if self.window.clear_plots.isChecked():
self.window.sc.clear_axes()
self.window.counter = 1
else:
self.window.counter =+ 1 #increase counter
if agi_source.device is None or agi_photo.device is None:
if "IVL_OFFLINE" not in os.environ.keys():
information_box("Could not open one of the two necessary sourcemeters!")
self.window.widget.setEnabled(True)
return
agi_source.set_measurement_param(points=self.window.points.value())
agi_photo.set_measurement_param(points=self.window.points.value())
agi_source.low_current_mode(True)
agi_photo.low_current_mode(True)
if self.window.measure_offset.isChecked():
dark_voltage = np.array([agi_photo.get_voltage() for _ in range(100)])
offset = dark_voltage.mean()
rms = np.std(dark_voltage)
if self.window.verbose.isChecked():
information_box(f"Ampl. Offset: {offset*1e3:.3f} mV\n"+ f"RMS: {rms*1e3:.3} mV\n")
self.window.offset.setValue(offset) # change the value on the interface
scan_data = ivl_scan_photodiode(
self.window.v_start.value(),self.window.v_stop.value(),self.window.v_step.value(),
agi_source,agi_photo,
delay=self.window.delay.value(), verbose = self.window.verbose.isChecked(),
offset = self.window.offset.value(), substrate=self.window.substrate.value(),
ref_spectrum= self.window.spectrum.text(),
RFA= self.window.rfa.value()
)
voltage = scan_data["voltage_source"]
current_dens = scan_data["current_density"]
radiance = scan_data["radiance"]
eqe = scan_data["eqe"]
luminance = scan_data["luminance"]
# Plot the results
self.window.sc.axes[0].plot(voltage, current_dens,label = f"Measurement {self.window.counter}")
self.window.sc.axes[1].plot(voltage, radiance,label = f"Measurement {self.window.counter}")
self.window.sc.axes[2].scatter(current_dens,radiance,label = f"Measurement {self.window.counter}")
self.window.sc.axes[3].scatter(radiance,eqe,label = f"Measurement {self.window.counter}")
for i in range(4):
self.window.sc.axes[i].legend()
self.window.sc.draw_idle()
filename = "_ivl.txt"
header = "Voltage Current_Density Radiance Luminance EQE"
header += " Raw_Current Raw_Photovoltage\n"
header += "[V] [mA/cm^2] [W/m^2*sr] [cd/m^2] [%] [A] [V]\n"
info = str(datetime.datetime.now().date())
data = (
voltage, current_dens, radiance, luminance, eqe,
scan_data["current"], scan_data["photo_voltage"]
)
data = np.array(data).T
if self.window.info.toPlainText() != "":
info += ": "+ self.window.info.toPlainText()
header += info
data_path = create_file(filename)
np.savetxt(data_path, data, header = header, fmt='%.3e')
self.window.widget.setEnabled(True)
app= QApplication(sys.argv)
w= MainWindow()
app.exec()
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment