improve everything
This commit is contained in:
parent
09b15068c7
commit
5894b5c778
@ -2,6 +2,13 @@
|
|||||||
import pyBen
|
import pyBen
|
||||||
from os import path
|
from os import path
|
||||||
|
|
||||||
|
class DummyBentham:
|
||||||
|
def __init__(self):
|
||||||
|
pass
|
||||||
|
def return_parameters(self): return {}
|
||||||
|
def park(self): print("Parked")
|
||||||
|
def drive(self, wl): print(f"Set to {wl} nm")
|
||||||
|
|
||||||
class Bentham():
|
class Bentham():
|
||||||
"""
|
"""
|
||||||
Controls the Bentham TMC300 monochromator.
|
Controls the Bentham TMC300 monochromator.
|
||||||
|
@ -4,6 +4,11 @@ class Shutter():
|
|||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.daq_name = 'Dev1'
|
self.daq_name = 'Dev1'
|
||||||
|
|
||||||
|
class DummyShutter(Shutter):
|
||||||
|
def __init__(self):
|
||||||
|
super().__init__()
|
||||||
|
def open_(self): print("Dummy shutter open")
|
||||||
|
def close_(self): print("Dummy shutter close")
|
||||||
|
|
||||||
class ShutterPump(Shutter):
|
class ShutterPump(Shutter):
|
||||||
"""
|
"""
|
||||||
|
Binary file not shown.
Binary file not shown.
@ -95,8 +95,6 @@ class SR830(MeasurementDevice):
|
|||||||
except pyvisa.VisaIOError as e:
|
except pyvisa.VisaIOError as e:
|
||||||
raise RuntimeError(f"VisaIOError raised while writing command(s):\n'{cmd}'\n\nVisaIOError:\n{e}")
|
raise RuntimeError(f"VisaIOError raised while writing command(s):\n'{cmd}'\n\nVisaIOError:\n{e}")
|
||||||
|
|
||||||
def get_frequency(self) -> float:
|
|
||||||
return float(self.query("FREQ?"))
|
|
||||||
|
|
||||||
def query(self, query):
|
def query(self, query):
|
||||||
return self.instr.query(query, delay=0.05).strip("\n")
|
return self.instr.query(query, delay=0.05).strip("\n")
|
||||||
@ -127,8 +125,37 @@ class SR830(MeasurementDevice):
|
|||||||
vals = self.query(f"SNAP? {what}").split(",")
|
vals = self.query(f"SNAP? {what}").split(",")
|
||||||
vals = map(float, vals)
|
vals = map(float, vals)
|
||||||
return vals
|
return vals
|
||||||
|
|
||||||
def measureTODO(): pass
|
def try_recover_from_communication_error(self, original_error):
|
||||||
|
"""
|
||||||
|
Try to get into a normal state by flushing the output queue
|
||||||
|
and reading the instrument status
|
||||||
|
"""
|
||||||
|
log.warning(f"Trying to recover from communication error: {original_error}")
|
||||||
|
# flush whatever is in the queue
|
||||||
|
try:
|
||||||
|
self.instr.read_raw()
|
||||||
|
except pyvisa.VisaIOError as e:
|
||||||
|
pass
|
||||||
|
try:
|
||||||
|
status = int(self.query("*ESR?"))
|
||||||
|
if status & 0b00110101 > 0: # check if INP, QRY, EXE or CMD is set
|
||||||
|
raise RuntimeError(f"Failed to recover from exception, device returned status {status:08b}:\n{original_error}")
|
||||||
|
except Exception as e:
|
||||||
|
raise RuntimeError(f"Failed to recover from the following exception:\n{original_error}\nThe following exception occurred while querying the device status:\n{e}")
|
||||||
|
log.info(f"Recovered from error")
|
||||||
|
|
||||||
|
def check_overloads(self) -> bool:
|
||||||
|
status_lia = int(self.query("LIAS?"))
|
||||||
|
if status_lia & (1 << 0): # input amplifier overload
|
||||||
|
return True
|
||||||
|
elif status_lia & (1 << 1): # time constant filter overlaid
|
||||||
|
return True
|
||||||
|
elif status_lia & (1 << 2): # output overload
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
def measureTODO(self): pass
|
||||||
def read_value(self):
|
def read_value(self):
|
||||||
"""Read the value of R"""
|
"""Read the value of R"""
|
||||||
return float(self.query("OUTP? 3"))
|
return float(self.query("OUTP? 3"))
|
||||||
@ -139,10 +166,94 @@ class SR830(MeasurementDevice):
|
|||||||
def test_connection(self):
|
def test_connection(self):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
# PARAMETERS
|
||||||
|
def get_frequency(self) -> float:
|
||||||
|
return float(self.query("FREQ?"))
|
||||||
|
|
||||||
|
SENS = [
|
||||||
|
2e-9, 5e-9, 10e-9, 20e-9, 50e-9, 100e-9, 200e-9, 500e-9,
|
||||||
|
1e-6, 2e-6, 5e-6, 10e-6, 20e-6, 50e-6, 100e-6, 200e-6, 500e-6,
|
||||||
|
1e-3, 2e-3, 5e-3, 10e-3, 20e-3, 50e-3, 100e-3, 200e-3, 500e-3,
|
||||||
|
1
|
||||||
|
]
|
||||||
|
def set_sensitivity_volt(self, volt):
|
||||||
|
if volt not in SR830.SENS:
|
||||||
|
raise ValueError(f"Invalid sensitivity voltage value: {volt}")
|
||||||
|
sens = SR830.SENS.index(volt)
|
||||||
|
self.run(f"SENS {sens}")
|
||||||
|
|
||||||
|
def get_sensitivity_volt(self):
|
||||||
|
sens = int(self.query(f"SENS"))
|
||||||
|
return SR830.SENS[sens]
|
||||||
|
|
||||||
|
OFLT =[
|
||||||
|
10e-6, 30e-6, 100e-6, 300e-6,
|
||||||
|
1e-3, 3e-3, 10e-3, 30e-3, 100e-3, 300e-3,
|
||||||
|
1, 3, 10, 30, 100, 300,
|
||||||
|
1e3, 3e3, 10e3, 30e3,
|
||||||
|
]
|
||||||
|
def set_time_constant_s(self, dt):
|
||||||
|
if dt not in SR830.OFLT:
|
||||||
|
raise ValueError(f"Invalid time constant value: {dt}. Must be one of {SR830.OFLT}")
|
||||||
|
oflt = SR830.OFLT.index(dt)
|
||||||
|
self.run(f"OFLT {oflt}")
|
||||||
|
|
||||||
|
def get_time_constant_s(self):
|
||||||
|
oflt = int(self.query("OFLT?"))
|
||||||
|
return SR830.OFLT[oflt]
|
||||||
|
|
||||||
|
|
||||||
|
OFSL = [6, 12, 18, 24]
|
||||||
|
def set_filter_slope(self, slope_db_oct):
|
||||||
|
if slope_db_oct not in SR830.OFSL:
|
||||||
|
raise ValueError(f"Invalid filter slope value: {slope_db_oct}. Must be one of {SR830.OFSL}")
|
||||||
|
ofsl = SR830.OFSL.index(slope_db_oct)
|
||||||
|
self.run(f"OFSL {ofsl}")
|
||||||
|
|
||||||
|
def get_filter_slope(self):
|
||||||
|
ofsl = int(self.query("OFSL?"))
|
||||||
|
return SR830.OFSL[ofsl]
|
||||||
|
|
||||||
|
def get_wait_time_s(self):
|
||||||
|
"""
|
||||||
|
Get the wait time required to reach 99% of the final value.
|
||||||
|
See Manual 3-21
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
time_const = self.get_time_constant_s()
|
||||||
|
filter_slope = self.get_filter_slope()
|
||||||
|
if filter_slope == 6: return 5 * time_const
|
||||||
|
elif filter_slope == 12: return 7 * time_const
|
||||||
|
elif filter_slope == 18: return 9 * time_const
|
||||||
|
elif filter_slope == 24: return 10 * time_const
|
||||||
|
else:
|
||||||
|
raise ValueError(f"Invalid filter slope value: {filter_slope}")
|
||||||
|
|
||||||
|
def set_sync_filter(self, sync):
|
||||||
|
if sync not in [0, 1]:
|
||||||
|
raise ValueError(f"Invalid sync value: {sync}, must be 0 (off) or 1 (on)")
|
||||||
|
self.run(f"SYNC {sync}")
|
||||||
|
|
||||||
|
def get_sync_filter(self):
|
||||||
|
sync = int(self.query("SYNC?"))
|
||||||
|
return sync
|
||||||
|
|
||||||
|
RMOD = ["High Reserve", "Normal", "Low Noise"]
|
||||||
|
def set_reserve(self, reserve):
|
||||||
|
if not reserve in SR830.RMOD:
|
||||||
|
raise ValueError(f"Invalid reserve value: {reserve}. Must be one of {SR830.RMOD}")
|
||||||
|
rmod = SR830.RMOD.index(reserve)
|
||||||
|
self.run(f"RMOD {rmod}")
|
||||||
|
|
||||||
|
def get_reserve(self):
|
||||||
|
rmod = int(self.query("RMOD?"))
|
||||||
|
return SR830.RMOD[rmod]
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
CH1 = ["X", "R", "X Noise", "Aux In 1", "Aux In 2"]
|
CH1 = ["X", "R", "X Noise", "Aux In 1", "Aux In 2"]
|
||||||
CH2 = ["Y", "Theta", "Y Noise", "Aux In 3", "Aux In 4"]
|
CH2 = ["Y", "Theta", "Y Noise", "Aux In 3", "Aux In 4"]
|
||||||
SRAT = [62.5e-3, 125e-3, 250e-3, 500e-3, 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, "Trigger"]
|
SRAT = [62.5e-3, 125e-3, 250e-3, 500e-3, 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, "Trigger"]
|
||||||
|
|
||||||
def buffer_setup(self, CH1="R", CH2="Theta", length=None, sample_rate=512):
|
def buffer_setup(self, CH1="R", CH2="Theta", length=None, sample_rate=512):
|
||||||
"""
|
"""
|
||||||
Prepare the device for a buffer (curve) measurement
|
Prepare the device for a buffer (curve) measurement
|
||||||
@ -243,6 +354,11 @@ class SR830(MeasurementDevice):
|
|||||||
# struct.unpack("=f", self.instr.read_bytes(4))[0]
|
# struct.unpack("=f", self.instr.read_bytes(4))[0]
|
||||||
if remainder > 0:
|
if remainder > 0:
|
||||||
data[chunks * CHUNK_SIZE:] = np.frombuffer(self.instr.read_bytes(remainder * 4), dtype=np.float32)
|
data[chunks * CHUNK_SIZE:] = np.frombuffer(self.instr.read_bytes(remainder * 4), dtype=np.float32)
|
||||||
|
try:
|
||||||
|
read = self.instr.read_raw()
|
||||||
|
log.warning(f"Unexpected read from device: {read}")
|
||||||
|
except pyvisa.VisaIOError:
|
||||||
|
pass
|
||||||
return data
|
return data
|
||||||
|
|
||||||
def auto_gain(self):
|
def auto_gain(self):
|
||||||
|
215
test.py
215
test.py
@ -1,3 +1,5 @@
|
|||||||
|
import pyvisa
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
import sys
|
import sys
|
||||||
if __package__ is None:
|
if __package__ is None:
|
||||||
@ -11,9 +13,9 @@ from time import sleep
|
|||||||
|
|
||||||
import numpy as np
|
import numpy as np
|
||||||
import scipy as scp
|
import scipy as scp
|
||||||
from Bentham import Bentham
|
from Bentham import Bentham, DummyBentham
|
||||||
from devices.Xenon import Xenon
|
from devices.Xenon import Xenon
|
||||||
from devices.Shutter import ShutterProbe
|
from devices.Shutter import ShutterProbe, DummyShutter
|
||||||
|
|
||||||
from .update_funcs import Monitor
|
from .update_funcs import Monitor
|
||||||
from .measurement_device.impl.sr830 import SR830
|
from .measurement_device.impl.sr830 import SR830
|
||||||
@ -28,7 +30,147 @@ logging.basicConfig(
|
|||||||
logging.StreamHandler()
|
logging.StreamHandler()
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
log = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
def set_measurement_params(lockin: SR830, p: dict={}, **kwargs):
|
||||||
|
params = p | kwargs
|
||||||
|
key_to_setter = {
|
||||||
|
"time_constant_s": lockin.set_time_constant_s,
|
||||||
|
"filter_slope": lockin.set_filter_slope,
|
||||||
|
"sync_filter": lockin.set_sync_filter,
|
||||||
|
"reserve": lockin.set_reserve,
|
||||||
|
"sensitivity_volt": lockin.set_sensitivity_volt,
|
||||||
|
}
|
||||||
|
for k, v in params.items():
|
||||||
|
if k not in key_to_setter.keys():
|
||||||
|
raise KeyError(f"Invalid parameter {k}")
|
||||||
|
key_to_setter[k](v)
|
||||||
|
|
||||||
|
|
||||||
|
def set_offset_laser_only(lockin: SR830, shutter: ShutterProbe, wait_time_s):
|
||||||
|
"""
|
||||||
|
Set the R offset from the signal when only the laser is on.
|
||||||
|
This signal should be stray laser light and laser induced PL
|
||||||
|
:return: Offset as percentage of the full scale R
|
||||||
|
"""
|
||||||
|
log.info("Setting offset when the lamp is off.")
|
||||||
|
shutter.close_()
|
||||||
|
sleep(wait_time_s + 10)
|
||||||
|
lockin.run("AOFF 3") # auto offset R
|
||||||
|
R_offset_fs = float(lockin.query("OEXP? 3").split(",")[0]) # returns R offset and expand
|
||||||
|
return R_offset_fs
|
||||||
|
|
||||||
|
|
||||||
|
def _measure_both_sim(monochromator: Bentham, lockin: SR830, shutter: ShutterProbe, wl_range=(400, 750, 25), aux_DC="Aux In 4", offset_with_laser_only=True, monitor=None):
|
||||||
|
data = {}
|
||||||
|
lockin_params = {
|
||||||
|
"time_constant_s": 10,
|
||||||
|
# "time_constant_s": 100e-3,
|
||||||
|
"sensitivity_volt": 50e-6,
|
||||||
|
"filter_slope": 12,
|
||||||
|
"sync_filter": 1,
|
||||||
|
"reserve": "Normal",
|
||||||
|
}
|
||||||
|
measurement_params = {
|
||||||
|
"measurement_time_s": 30,
|
||||||
|
"sample_rate_Hz": 512,
|
||||||
|
}
|
||||||
|
|
||||||
|
set_measurement_params(lockin, lockin_params)
|
||||||
|
|
||||||
|
measurement_time_s = measurement_params["measurement_time_s"]
|
||||||
|
sample_rate_AC = measurement_params["sample_rate_Hz"]
|
||||||
|
n_bins_AC = measurement_time_s * sample_rate_AC # x sec messen mit <sample_rate> werte pro sekunde
|
||||||
|
timeout_s = 60
|
||||||
|
timeout_interval = 0.5
|
||||||
|
# trigger on the falling edge, since the light comes through when the ref signal is low
|
||||||
|
# could of course also trigger on rising and apply 180° shift
|
||||||
|
lockin.run("RSLP 2")
|
||||||
|
# since we dont expect changes in our signal, we can use larger time constants and aggressive filter slope
|
||||||
|
# for better signal to noise
|
||||||
|
wait_time_s = lockin.get_wait_time_s()
|
||||||
|
|
||||||
|
def run_lockin_cmd(cmd, n_try=2):
|
||||||
|
com_success = n_try
|
||||||
|
e = None
|
||||||
|
while com_success > 0:
|
||||||
|
try:
|
||||||
|
return cmd()
|
||||||
|
except pyvisa.VisaIOError as e:
|
||||||
|
lockin.try_recover_from_communication_error(e)
|
||||||
|
com_success -= 1
|
||||||
|
raise e
|
||||||
|
|
||||||
|
# 5s for setting buffer,
|
||||||
|
# 5s for get values and plot
|
||||||
|
print(f"Time estimate {(measurement_time_s + wait_time_s + 5 + 5)/60 * ((wl_range[1]-wl_range[0])/wl_range[2])} minutes")
|
||||||
|
input("Make sure the laser is turned on and press enter > ")
|
||||||
|
mon = monitor if monitor is not None else Monitor(r"$\lambda$ [nm]", [
|
||||||
|
dict(ax=0, ylabel=r"$\Delta R$", color="green"),
|
||||||
|
dict(ax=1, ylabel=r"$\sigma_{\Delta R}$", color="green"),
|
||||||
|
dict(ax=2, ylabel=r"$R$", color="blue"),
|
||||||
|
dict(ax=3, ylabel=r"$\sigma_R$", color="blue"),
|
||||||
|
dict(ax=4, ylabel=r"$\Delta R/R$", color="red"),
|
||||||
|
dict(ax=5, ylabel=r"$\sigma_{\Delta R/R}$", color="red"),
|
||||||
|
])
|
||||||
|
mon.set_fig_title(f"Turn on laser and plug detector into A and {aux_DC} ")
|
||||||
|
|
||||||
|
data["lock-in-params"] = lockin_params
|
||||||
|
data["measurement-params"] = measurement_params
|
||||||
|
full_scale_voltage = lockin_params["sensitivity_volt"]
|
||||||
|
if offset_with_laser_only:
|
||||||
|
mon.set_fig_title(f"Measuring baseline with lamp off")
|
||||||
|
R_offset_volt = set_offset_laser_only(lockin, shutter, wait_time_s) * full_scale_voltage
|
||||||
|
data["R_offset_volt_before"] = R_offset_volt
|
||||||
|
print(f"R_offset_volt_before {R_offset_volt}")
|
||||||
|
data["reference_freq_Hz_before"] = lockin.get_frequency()
|
||||||
|
|
||||||
|
shutter.open_()
|
||||||
|
for i_wl, wl in enumerate(range(*wl_range)):
|
||||||
|
mon.set_ax_title(f"$\\lambda = {wl}$ nm")
|
||||||
|
run_lockin_cmd(lambda: lockin.buffer_setup(CH1="R", CH2=aux_DC, length=n_bins_AC, sample_rate=sample_rate_AC))
|
||||||
|
mon.set_fig_title(f"Setting wavelength to {wl} nm")
|
||||||
|
monochromator.drive(wl)
|
||||||
|
mon.set_fig_title(f"Waiting for signal to stabilize")
|
||||||
|
# wait the wait time
|
||||||
|
sleep(wait_time_s)
|
||||||
|
mon.set_fig_title(f"Measuring...")
|
||||||
|
run_lockin_cmd(lambda: lockin.buffer_start_fill())
|
||||||
|
t = timeout_s
|
||||||
|
while t > 0:
|
||||||
|
t -= timeout_interval
|
||||||
|
sleep(timeout_interval)
|
||||||
|
if run_lockin_cmd(lambda: lockin.buffer_is_done()):
|
||||||
|
break
|
||||||
|
if t < 0: raise RuntimeError("Timed out waiting for buffer measurement to finish")
|
||||||
|
arr = run_lockin_cmd(lambda: lockin.buffer_get_data(CH1=True, CH2=True))
|
||||||
|
data[wl] = {}
|
||||||
|
data[wl]["raw"] = arr
|
||||||
|
# calculate means
|
||||||
|
means = np.mean(arr, axis=1)
|
||||||
|
errs = np.std(arr, axis=1)
|
||||||
|
dR = means[0]
|
||||||
|
R = means[1]
|
||||||
|
sdR = errs[1]
|
||||||
|
sR = errs[1]
|
||||||
|
data[wl] |= {"dR": dR, "sdR": sdR, "R": R, "sR": sR}
|
||||||
|
dR_R = dR / R
|
||||||
|
sdR_R = np.sqrt((sdR / R) + (dR * sR/R**2))
|
||||||
|
data[wl] |= {"dR_R": dR_R, "sdR_R": sdR_R}
|
||||||
|
mon.update(wl, dR, sdR, R, sR, dR_R, sdR_R)
|
||||||
|
# if it fails, we still want the data returned
|
||||||
|
try:
|
||||||
|
if offset_with_laser_only:
|
||||||
|
mon.set_fig_title(f"Measuring baseline with lamp off")
|
||||||
|
R_offset_volt = set_offset_laser_only(lockin, shutter, wait_time_s) * full_scale_voltage
|
||||||
|
data["R_offset_volt_after"] = R_offset_volt
|
||||||
|
print(f"R_offset_volt_after {R_offset_volt}")
|
||||||
|
data["reference_freq_Hz_after"] = lockin.get_frequency()
|
||||||
|
except Exception as e:
|
||||||
|
print(e)
|
||||||
|
mon.set_fig_title("Photoreflectance")
|
||||||
|
mon.set_ax_title("")
|
||||||
|
return data, mon
|
||||||
|
|
||||||
def _measure_both(monochromator: Bentham, lockin: SR830, shutter: ShutterProbe, wl_range=(400, 750, 25), AC=True, DC=True, monitor=None):
|
def _measure_both(monochromator: Bentham, lockin: SR830, shutter: ShutterProbe, wl_range=(400, 750, 25), AC=True, DC=True, monitor=None):
|
||||||
mon = monitor if monitor is not None else Monitor(r"$\lambda$ [nm]", [
|
mon = monitor if monitor is not None else Monitor(r"$\lambda$ [nm]", [
|
||||||
@ -40,34 +182,56 @@ def _measure_both(monochromator: Bentham, lockin: SR830, shutter: ShutterProbe,
|
|||||||
dict(ax=3, ylabel=r"$\sigma_R$", color="blue"),
|
dict(ax=3, ylabel=r"$\sigma_R$", color="blue"),
|
||||||
dict(ax=4, ylabel=r"$\Delta R/R$", color="red"),
|
dict(ax=4, ylabel=r"$\Delta R/R$", color="red"),
|
||||||
# dict(ax=3, ylabel="R", color="blue"),
|
# dict(ax=3, ylabel="R", color="blue"),
|
||||||
dict(ax=5, ylabel="Phase", color="orange", lim=(-180, 180))
|
dict(ax=5, ylabel=r"$\theta$", color="orange", lim=(-180, 180)),
|
||||||
|
dict(ax=6, ylabel=r"$\sigma_\theta$", color="orange")
|
||||||
])
|
])
|
||||||
N_bins = 512
|
|
||||||
shutter.open_()
|
shutter.open_()
|
||||||
data_raw = []
|
data_raw = []
|
||||||
data_wl = {}
|
data_wl = {}
|
||||||
sample_rate = 512
|
# TODO these are only printed, not set!
|
||||||
|
time_constant = 30e-3
|
||||||
|
filter_slope = 24
|
||||||
|
sensitivity = 1.0
|
||||||
|
|
||||||
|
SYNC = 1
|
||||||
|
sample_rate_AC = 64
|
||||||
|
sample_rate_DC = 512
|
||||||
|
n_bins_AC = 3 * sample_rate_AC # x sec messen mit <sample_rate> werte pro sekunde
|
||||||
|
n_bins_DC = 10 * sample_rate_DC
|
||||||
timeout_s = 60
|
timeout_s = 60
|
||||||
timeout_interval = 0.5
|
timeout_interval = 0.5
|
||||||
# lockin.run("SENS 17") # 1 mV/nA
|
# lockin.run("SENS 17") # 1 mV
|
||||||
lockin.run("SENS 20") # 10 mV/nA
|
# lockin.run("SENS 20") # 10 mV
|
||||||
|
# lockin.run("SENS 21") # 20 mV
|
||||||
|
lockin.run("SENS 26") # 1 V
|
||||||
# trigger on the falling edge, since the light comes through when the ref signal is low
|
# trigger on the falling edge, since the light comes through when the ref signal is low
|
||||||
# could of course also trigger on rising and apply 180° shift
|
# could of course also trigger on rising and apply 180° shift
|
||||||
lockin.run("RSLP 2")
|
lockin.run("RSLP 2")
|
||||||
# since we dont expect changes in our signal, we can use large time constants and aggresive filter slope
|
# since we dont expect changes in our signal, we can use larger time constants and aggressive filter slope
|
||||||
# for better signal to noise
|
# for better signal to noise
|
||||||
# lockin.run("OFLT 5") # 3 ms
|
# lockin.run("OFLT 5") # 3 ms
|
||||||
|
lockin.run("OFLT 7") # 30 ms
|
||||||
|
# lockin.run("OFLT 8") # 100 ms
|
||||||
|
# lockin.run("OFLT 10") # 1 s
|
||||||
lockin.run("RMOD 2") # low noise (small reserve)
|
lockin.run("RMOD 2") # low noise (small reserve)
|
||||||
lockin.run("OFLT 8") # 100 ms
|
# lockin.run("OFSL 0") # 6dB/Oct
|
||||||
lockin.run("OFSL 3") # 24dB/Oct ms
|
lockin.run("OFSL 3") # 24dB/Oct
|
||||||
lockin.run("SYNC 0") # test without sync filter
|
lockin.run(f"SYNC {SYNC}") # sync filter
|
||||||
|
|
||||||
|
print(f"Time estimate {40 * (wl_range[1]-wl_range[0])/(wl_range[2]*60)} minutes")
|
||||||
if AC:
|
if AC:
|
||||||
input("Plug the detector into lock-in port 'A/I' (front panel) and press enter > ")
|
input("Plug the detector into lock-in port 'A/I' (front panel) and press enter > ")
|
||||||
input("Make sure the laser is turned on and press enter > ")
|
input("Make sure the laser is turned on and press enter > ")
|
||||||
|
mon.set_fig_title("Turn on laser and plug detector into A")
|
||||||
for i_wl, wl in enumerate(range(*wl_range)):
|
for i_wl, wl in enumerate(range(*wl_range)):
|
||||||
lockin.buffer_setup(CH1="R", CH2="Theta", length=N_bins, sample_rate=sample_rate)
|
mon.set_ax_title(f"$\\lambda = {wl}$ nm")
|
||||||
|
lockin.buffer_setup(CH1="R", CH2="Theta", length=n_bins_AC, sample_rate=sample_rate_AC)
|
||||||
|
mon.set_fig_title(f"Setting wavelength to {wl} nm")
|
||||||
monochromator.drive(wl)
|
monochromator.drive(wl)
|
||||||
sleep(1.5) # need to wait until lock-in R signal is stable
|
mon.set_fig_title(f"Waiting for signal to stabilize")
|
||||||
|
# wait time depends on filter and time constant, for 24dB/Oct and Sync on the minimum is ~12 time constants
|
||||||
|
sleep(1.0)
|
||||||
|
mon.set_fig_title(f"Measuring...")
|
||||||
lockin.buffer_start_fill()
|
lockin.buffer_start_fill()
|
||||||
t = timeout_s
|
t = timeout_s
|
||||||
while t > 0:
|
while t > 0:
|
||||||
@ -85,14 +249,19 @@ def _measure_both(monochromator: Bentham, lockin: SR830, shutter: ShutterProbe,
|
|||||||
stheta = scp.stats.circstd(arr[1,:], low=-180, high=180)
|
stheta = scp.stats.circstd(arr[1,:], low=-180, high=180)
|
||||||
data_wl[wl] = {"dR": dR, "Theta": theta, "sdR": sdR, "sTheta": stheta}
|
data_wl[wl] = {"dR": dR, "Theta": theta, "sdR": sdR, "sTheta": stheta}
|
||||||
# wl - dR, sdR, R, sR, dR/R, Theta
|
# wl - dR, sdR, R, sR, dR/R, Theta
|
||||||
mon.update(wl, dR, sdR, None, None, None, stheta)
|
mon.update(wl, dR, sdR, None, None, None, theta, stheta)
|
||||||
if DC:
|
if DC:
|
||||||
|
mon.set_ax_title("")
|
||||||
|
mon.set_fig_title("Turn off laser and plug detector into Aux 1")
|
||||||
input("Turn off the laser and press enter > ")
|
input("Turn off the laser and press enter > ")
|
||||||
input("Plug the detector into lock-in port 'Aux In 1' (rear panel) and press enter > ")
|
input("Plug the detector into lock-in port 'Aux In 1' (rear panel) and press enter > ")
|
||||||
for i_wl, wl in enumerate(range(*wl_range)):
|
for i_wl, wl in enumerate(range(*wl_range)):
|
||||||
lockin.buffer_setup(CH1="Aux In 1", CH2="Theta", length=N_bins, sample_rate=sample_rate)
|
mon.set_ax_title(f"$\\lambda = {wl}$ nm")
|
||||||
|
lockin.buffer_setup(CH1="Aux In 1", CH2="Theta", length=n_bins_DC, sample_rate=sample_rate_DC)
|
||||||
|
mon.set_fig_title(f"Setting wavelength to {wl} nm")
|
||||||
monochromator.drive(wl)
|
monochromator.drive(wl)
|
||||||
sleep(0.5)
|
sleep(0.5)
|
||||||
|
mon.set_fig_title(f"Measuring...")
|
||||||
lockin.buffer_start_fill()
|
lockin.buffer_start_fill()
|
||||||
t = timeout_s
|
t = timeout_s
|
||||||
while t > 0:
|
while t > 0:
|
||||||
@ -113,9 +282,11 @@ def _measure_both(monochromator: Bentham, lockin: SR830, shutter: ShutterProbe,
|
|||||||
# wl - dR, sdR, R, sR, dR/R, Theta
|
# wl - dR, sdR, R, sR, dR/R, Theta
|
||||||
if AC:
|
if AC:
|
||||||
dR_R = data_wl[wl]["dR"] / data_wl[wl]["R"]
|
dR_R = data_wl[wl]["dR"] / data_wl[wl]["R"]
|
||||||
mon.override(wl, None, None, data_wl[wl]["R"], data_wl[wl]["sR"], dR_R, None)
|
mon.override(wl, None, None, data_wl[wl]["R"], data_wl[wl]["sR"], dR_R, None, None)
|
||||||
else:
|
else:
|
||||||
mon.update(wl, None, None, data_wl[wl]["R"], data_wl[wl]["sR"], None, None)
|
mon.update(wl, None, None, data_wl[wl]["R"], data_wl[wl]["sR"], None, None, None)
|
||||||
|
mon.set_fig_title(f"Time constant = {time_constant} s\nFilter slope = {filter_slope} dB/oct\nSync Filter = {SYNC}\nSensitivity = {sensitivity*1e3} mV")
|
||||||
|
mon.set_ax_title("")
|
||||||
return data_wl, data_raw, mon
|
return data_wl, data_raw, mon
|
||||||
|
|
||||||
|
|
||||||
@ -126,9 +297,8 @@ def _measure(monochromator: Bentham, lamp: Xenon, lockin: SR830, shutter: Shutte
|
|||||||
# dict(ax=1, ylabel="Ref", color="blue", lim=(0, 5)),
|
# dict(ax=1, ylabel="Ref", color="blue", lim=(0, 5)),
|
||||||
dict(ax=0, ylabel=r"$\Delta R$", color="green"),
|
dict(ax=0, ylabel=r"$\Delta R$", color="green"),
|
||||||
dict(ax=1, ylabel=r"$\sigma_{\Delta R}$", color="green"),
|
dict(ax=1, ylabel=r"$\sigma_{\Delta R}$", color="green"),
|
||||||
# dict(ax=3, ylabel="R", color="blue"),
|
|
||||||
dict(ax=2, ylabel="Phase", color="orange", lim=(-180, 180))
|
dict(ax=2, ylabel="Phase", color="orange", lim=(-180, 180))
|
||||||
|
# dict(ax=3, ylabel="R", color="blue"),
|
||||||
])
|
])
|
||||||
N_bins = 100
|
N_bins = 100
|
||||||
dt = 0.01
|
dt = 0.01
|
||||||
@ -189,10 +359,15 @@ def measure(wl_range=(400, 500, 2)):
|
|||||||
def measure_both(**kwargs):
|
def measure_both(**kwargs):
|
||||||
return _measure_both(mcm, lockin, shutter, **kwargs)
|
return _measure_both(mcm, lockin, shutter, **kwargs)
|
||||||
|
|
||||||
|
def measure_both_sim(**kwargs):
|
||||||
|
return _measure_both_sim(mcm, lockin, shutter, **kwargs)
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
mcm = Bentham()
|
mcm = Bentham()
|
||||||
|
shutter = ShutterProbe()
|
||||||
# mcm.park()
|
# mcm.park()
|
||||||
lamp = Xenon()
|
lamp = Xenon()
|
||||||
lockin = SR830.connect_device(SR830.enumerate_devices()[0])
|
lockin = SR830.connect_device(SR830.enumerate_devices()[0])
|
||||||
# lockin = Model7260.connect_device(Model7260.enumerate_devices()[0])
|
# lockin = Model7260.connect_device(Model7260.enumerate_devices()[0])
|
||||||
shutter = ShutterProbe()
|
# mcm = DummyBentham()
|
||||||
|
# shutter = DummyShutter()
|
||||||
|
@ -32,6 +32,21 @@ class Monitor:
|
|||||||
if opt["ylabel"]:
|
if opt["ylabel"]:
|
||||||
ax.set_ylabel(opt["ylabel"])
|
ax.set_ylabel(opt["ylabel"])
|
||||||
|
|
||||||
|
def set_ax_title(self, title):
|
||||||
|
self.ax[0].set_title(title)
|
||||||
|
self.fig1.canvas.draw()
|
||||||
|
self.fig1.canvas.flush_events()
|
||||||
|
|
||||||
|
def set_fig_title(self, title):
|
||||||
|
self.fig1.suptitle(title)
|
||||||
|
self.fig1.canvas.draw()
|
||||||
|
self.fig1.canvas.flush_events()
|
||||||
|
|
||||||
|
|
||||||
|
def reset(self):
|
||||||
|
self.xdata = []
|
||||||
|
self.ydatas = [[] for _ in range(len(self.ydatas))]
|
||||||
|
|
||||||
def override(self, xval, *yvals):
|
def override(self, xval, *yvals):
|
||||||
idx = self.xdata.index(xval)
|
idx = self.xdata.index(xval)
|
||||||
for i, y in enumerate(yvals):
|
for i, y in enumerate(yvals):
|
||||||
|
Loading…
x
Reference in New Issue
Block a user