merging all laser timing files
This commit is contained in:
+183
-13
@@ -4,8 +4,15 @@ import numpy as np
|
||||
import time
|
||||
from slic.core.task import Task
|
||||
|
||||
_basefolder = "/sf/bernina/config/com/data/src/lasertiming"
|
||||
|
||||
#TODO
|
||||
#bernina
|
||||
PS = "SLAAR02-TSPL-EPL"
|
||||
_basefolder = "/sf/bernina/config/eco/offsets"
|
||||
#alvra:
|
||||
PS = "SLAAR01-TSPL-EPL"
|
||||
_basefolder = "/sf/alvra/config/lasertiming"
|
||||
|
||||
_posTypes = ["user", "dial", "raw"]
|
||||
|
||||
|
||||
@@ -22,7 +29,31 @@ def timeToStr(value, n=12):
|
||||
return ret_str
|
||||
|
||||
|
||||
def niceTimeToStr(delay, fmt="%+.0f"):
|
||||
a_delay = abs(delay)
|
||||
if a_delay >= 1:
|
||||
ret = fmt % delay + "s"
|
||||
elif 1e-3 <= a_delay < 1:
|
||||
ret = fmt % (delay * 1e3) + "ms"
|
||||
elif 1e-6 <= a_delay < 1e-3:
|
||||
ret = fmt % (delay * 1e6) + "us"
|
||||
elif 1e-9 <= a_delay < 1e-6:
|
||||
ret = fmt % (delay * 1e9) + "ns"
|
||||
elif 1e-12 <= a_delay < 1e-9:
|
||||
ret = fmt % (delay * 1e12) + "ps"
|
||||
elif 1e-15 <= a_delay < 1e-12:
|
||||
ret = fmt % (delay * 1e12) + "fs"
|
||||
elif 1e-18 <= a_delay < 1e-15:
|
||||
ret = fmt % (delay * 1e12) + "as"
|
||||
elif a_delay < 1e-18:
|
||||
ret = "0s"
|
||||
else:
|
||||
ret = str(delay) + "s"
|
||||
return ret
|
||||
|
||||
|
||||
class Storage(object):
|
||||
""" this class is needed to store the offset in files and read in s """
|
||||
|
||||
def __init__(self, pvname):
|
||||
self._filename = os.path.join(_basefolder, pvname)
|
||||
@@ -61,10 +92,11 @@ class Storage(object):
|
||||
|
||||
class Pockels_trigger(PV):
|
||||
|
||||
def __init__(self, pv_basename):
|
||||
pvname = pv_basename + "-RB"
|
||||
def __init__(self, pv_get, pv_set, pv_offset_get): #TODO make offset optional
|
||||
pvname = pv_get
|
||||
PV.__init__(self, pvname)
|
||||
self._pv_setvalue = PV(pv_basename + "-SP")
|
||||
self._pv_offset_get = PV(pv_offset_get)
|
||||
self._pv_setvalue = PV(pv_set)
|
||||
self._filename = os.path.join(_basefolder, pvname)
|
||||
self._storage = Storage(pvname)
|
||||
|
||||
@@ -73,7 +105,7 @@ class Pockels_trigger(PV):
|
||||
return self._storage.value
|
||||
|
||||
def get_dial(self):
|
||||
return super().get() * 1e-6
|
||||
return np.round(super().get() * 1e-6, 9) + self._pv_offset_get.get() * 1e-9 - 7.41e-9 #TODO what is the constant?
|
||||
|
||||
def get(self):
|
||||
""" convert time to sec """
|
||||
@@ -98,16 +130,20 @@ class Pockels_trigger(PV):
|
||||
return "Pockel Trigger PV: %s user , dial = %s, %s" % (self.pvname, user, dial)
|
||||
|
||||
|
||||
class Phase_shifter(PV):
|
||||
""" this class is needed to store the offset in files and read in ps """
|
||||
_OSCILLATOR_PERIOD = 1 / 71.368704e6
|
||||
|
||||
def __init__(self, pv_basename="SLAAR01-TSPL-EPL"):
|
||||
|
||||
class Phase_shifter(PV):
|
||||
|
||||
def __init__(self, pv_basename=PS, dial_max=14.0056e-9, precision=100e-15):
|
||||
pvname = pv_basename + ":CURR_DELTA_T"
|
||||
PV.__init__(self, pvname)
|
||||
self._filename = os.path.join(_basefolder, pvname)
|
||||
self._pv_setvalue = PV(pv_basename + ":NEW_DELTA_T")
|
||||
self._pv_execute = PV(pv_basename + ":SET_NEW_PHASE.PROC")
|
||||
self._storage = Storage(pvname)
|
||||
self.dial_max = dial_max
|
||||
self.retry = precision
|
||||
|
||||
@property
|
||||
def offset(self):
|
||||
@@ -125,17 +161,25 @@ class Phase_shifter(PV):
|
||||
value = self.get_dial()
|
||||
self._storage.store(value)
|
||||
|
||||
def move(self, value):
|
||||
def move(self, value, accuracy=None):
|
||||
if accuracy is None:
|
||||
accuracy = self.retry
|
||||
dial = value + self.offset
|
||||
dial = np.mod(dial, _OSCILLATOR_PERIOD)
|
||||
if dial > self.dial_max:
|
||||
dial = self.dial_max
|
||||
dial_ps = dial * 1e12
|
||||
self._pv_setvalue.put(dial_ps)
|
||||
time.sleep(0.1)
|
||||
self._pv_execute.put(1)
|
||||
while np.abs(self.get() - value) > 100e-15:
|
||||
#print(accuracy)
|
||||
while np.abs(self.get_dial() - dial) > accuracy:
|
||||
#print(np.abs(self.get_dial()-dial))
|
||||
time.sleep(0.2)
|
||||
|
||||
def set(self, value):
|
||||
newoffset = self.get_dial() - value
|
||||
newoffset = np.mod(newoffset, _OSCILLATOR_PERIOD)
|
||||
self.store(newoffset)
|
||||
|
||||
def __repr__(self):
|
||||
@@ -144,9 +188,135 @@ class Phase_shifter(PV):
|
||||
return "Phase Shifter: user,dial = %s , %s" % (user, dial)
|
||||
|
||||
|
||||
#_pockels_in = Pockels_trigger("SLAAR-LTIM01-EVR0:Pul2-Delay")
|
||||
#_pockels_out = Pockels_trigger("SLAAR-LTIM01-EVR0:Pul3-Delay")
|
||||
#_phase_shifter = Phase_shifter("SLAAR01-TSPL-EPL")
|
||||
|
||||
#TODO
|
||||
|
||||
#bernina
|
||||
sg_get = "SLAAR-LTIM02-EVR0:Pul3-Delay-RB"
|
||||
sg_set = "SLAAR-LTIM02-EVR0:Pul3_NEW_DELAY"
|
||||
sg_off = "SLAAR-LTIM02-EVR0:UnivDlyModule1-Delay1-RB"
|
||||
|
||||
sdg_get = "SLAAR-LTIM02-EVR0:Pul2-Delay-RB"
|
||||
sdg_set = "SLAAR-LTIM02-EVR0:Pul2_NEW_DELAY"
|
||||
sdg_off = "SLAAR-LTIM02-EVR0:UnivDlyModule1-Delay0-RB"
|
||||
|
||||
#alvra:
|
||||
sg = "SLAAR-LTIM01-EVR0:Pul2-Delay"
|
||||
sg_get = sg + "-RB"
|
||||
sg_set = sg + "-SP"
|
||||
sg_offset = "SLAAR-LTIM01-EVR0:UnivDlyModule1-Delay1-RB"
|
||||
|
||||
sdg = "SLAAR-LTIM01-EVR0:Pul3-Delay"
|
||||
sdg_get = sdg + "-RB"
|
||||
sdg_set = sdg + "-SP"
|
||||
|
||||
|
||||
|
||||
_slicer_gate = Pockels_trigger(sg_get, sg_set, sg_off)
|
||||
_sdg1 = Pockels_trigger(sdg_get, sdg_set, sdg_off)
|
||||
_phase_shifter = Phase_shifter(PS)
|
||||
|
||||
|
||||
_POCKELS_CELL_RESOLUTION = 7e-9
|
||||
|
||||
|
||||
class Lxt(object):
|
||||
|
||||
def __init__(self, accuracy_poly=[100e-15, 1e-7]):
|
||||
self.sdg1 = _sdg1
|
||||
self.slicer_gate = _slicer_gate
|
||||
self.phase_shifter = _phase_shifter
|
||||
self.Id = PS
|
||||
self.name = "lxt"
|
||||
self.elog = None
|
||||
self.accuracy_poly = accuracy_poly
|
||||
|
||||
def move_sdg(self, value):
|
||||
self.sdg1.move(value)
|
||||
|
||||
def move(self, value, accuracy=None):
|
||||
self.sdg1.move(-value)
|
||||
self.slicer_gate.move(-value)
|
||||
if not accuracy:
|
||||
accuracy = np.abs(value) * self.accuracy_poly[1] + self.accuracy_poly[0]
|
||||
self.phase_shifter.move(value, accuracy=accuracy)
|
||||
|
||||
def set(self, value):
|
||||
self.phase_shifter.set(value)
|
||||
self.slicer_gate.set(-value)
|
||||
self.sdg1.set(-value)
|
||||
|
||||
def get(self):
|
||||
# pulses are at SOME_IDX*OSCILLATOR_PERIOD-PHASESHITER
|
||||
# the -PHASESHITER is due to the inverted sign
|
||||
phase_shifter = self.phase_shifter.get()
|
||||
sdg1_delay = self.sdg1.get()
|
||||
|
||||
idx_pulse = (sdg1_delay + phase_shifter) / _OSCILLATOR_PERIOD
|
||||
|
||||
delay = int(idx_pulse) * _OSCILLATOR_PERIOD - phase_shifter
|
||||
return -delay
|
||||
|
||||
def set_target_value(self, value, hold=False):
|
||||
""" Adjustable convention"""
|
||||
changer = lambda: self.move(value)
|
||||
return Task(changer, hold=hold)
|
||||
|
||||
def get_current_value(self):
|
||||
return self.get()
|
||||
|
||||
def set_current_value(self, value):
|
||||
self.set(value)
|
||||
|
||||
def __repr__(self):
|
||||
delay = niceTimeToStr(lxt.get())
|
||||
return "delay = %s" % (delay)
|
||||
|
||||
|
||||
|
||||
|
||||
class eTiming:
|
||||
|
||||
def __init__(self, Id):
|
||||
self.Id = Id
|
||||
self.name = "Globi laser electronic timing (us)"
|
||||
self._eTimeSet = PV("SLAAR01-LTIM-PDLY:DELAY") # current laser timing number from Edwin's timing PV
|
||||
self._eTimeRBK = PV("SLAAR-LGEN:DLY_OFFS1") # readback on Edwin's timing PV
|
||||
self._moving = PV("SLAAR01-LTIM-PDLY:WAITING")
|
||||
|
||||
def get_current_value(self):
|
||||
return self._eTimeRBK.get() * 1e6 # convert from us to ps
|
||||
|
||||
def wait_for_valid_value(self):
|
||||
tval = np.nan
|
||||
while not np.isfinite(tval):
|
||||
tval = self.get_current_value()
|
||||
return tval
|
||||
|
||||
def move_and_wait(self, value, checktime=0.01):
|
||||
self._eTimeSet.put(value)
|
||||
time.sleep(0.2)
|
||||
while self._moving.get() == 0.0:
|
||||
time.sleep(checktime)
|
||||
|
||||
def set_current_value(self, value):
|
||||
self._eTimeSet.put(value)
|
||||
|
||||
def __repr__(self):
|
||||
return self.__str__()
|
||||
|
||||
def set_target_value(self, value, hold=False):
|
||||
changer = lambda: self.move_and_wait(value)
|
||||
return Task(changer, hold=hold)
|
||||
|
||||
def __str__(self):
|
||||
eTimingRBKStr = str(self.get_current_value())
|
||||
eTimingSetStr = self._eTimeSet.get(as_string=True)
|
||||
s = "Globi laser electronic timing\n"
|
||||
s += "- electronic timing readback (ps): {}\n".format(eTimingRBKStr)
|
||||
s += "- electronic timing setpoint (ps): {}".format(eTimingSetStr)
|
||||
return s
|
||||
|
||||
|
||||
|
||||
class PhaseShifterAramis:
|
||||
|
||||
@@ -13,6 +13,8 @@ _basefolder = "/sf/bernina/config/eco/offsets"
|
||||
PS = "SLAAR01-TSPL-EPL"
|
||||
_basefolder = "/sf/alvra/config/lasertiming"
|
||||
|
||||
_posTypes = ["user", "dial", "raw"]
|
||||
|
||||
|
||||
def timeToStr(value, n=12):
|
||||
fmt = "%%+.%df" % n
|
||||
@@ -317,3 +319,41 @@ class eTiming:
|
||||
|
||||
|
||||
|
||||
class PhaseShifterAramis:
|
||||
|
||||
def __init__(self, Id, name=None, elog=None, z_undulator=None, description=None):
|
||||
self.Id = Id
|
||||
self._pshifter = Phase_shifter(Id)
|
||||
self._elog = elog
|
||||
self.name = name
|
||||
|
||||
def set_target_value(self, value, hold=False, check=True):
|
||||
""" Adjustable convention"""
|
||||
mover = lambda: self._pshifter.move(value)
|
||||
return Task(mover, hold=hold)
|
||||
|
||||
def stop(self):
|
||||
""" Adjustable convention"""
|
||||
pass
|
||||
|
||||
def get_current_value(self, posType="user", readback=True):
|
||||
""" Adjustable convention"""
|
||||
_keywordChecker([("posType", posType, _posTypes)])
|
||||
if posType == "user":
|
||||
return self._pshifter.get()
|
||||
if posType == "dial":
|
||||
return self._pshifter.get_dial()
|
||||
|
||||
def set_current_value(self, value, posType="user"):
|
||||
""" Adjustable convention"""
|
||||
_keywordChecker([("posType", posType, _posTypes)])
|
||||
if posType == "user":
|
||||
return self._motor.set(value)
|
||||
|
||||
|
||||
def _keywordChecker(kw_key_list_tups):
|
||||
for tkw, tkey, tlist in kw_key_list_tups:
|
||||
assert tkey in tlist, "Keyword %s should be one of %s" % (tkw, tlist)
|
||||
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user