wip
This commit is contained in:
1 parent
700b584c40
commit
6c2400a439
8 files changed
+177
-117
No files matched your search
@@ -1,60 +1,63 @@
|
||||
""" Module to automatically set the gains for the selected amplifiers"""
|
||||
"""Module to automatically set the gains for the selected amplifiers"""
|
||||
|
||||
from bisect import bisect_right
|
||||
import time
|
||||
import builtins
|
||||
import time
|
||||
from bisect import bisect_right
|
||||
|
||||
import xraydb
|
||||
from bec_lib import bec_logger
|
||||
|
||||
from ...devices.nidaq.nidaq_enums import NidaqState
|
||||
from ...devices.absorber import STATUS as ABS_STATUS
|
||||
from ...devices.op_shutter import STATUS as OP_PH_STATUS
|
||||
from ...devices.eh_shutter import STATUS as EH_PH_STATUS
|
||||
from ...devices.ionization_chambers.ionization_chamber_enums import AmplifierEnable
|
||||
from ...devices.nidaq.nidaq_enums import NidaqState
|
||||
from ...devices.op_shutter import STATUS as OP_PH_STATUS
|
||||
|
||||
logger = bec_logger.logger
|
||||
|
||||
EMIN = -100
|
||||
EMAX = 200
|
||||
|
||||
MIN_RING_CURRENT = 5 # Minimum ring current to use auto-gain
|
||||
NOMINAL_RING_CURRENT = 400 # Nominal ring current of SLS2
|
||||
MIN_RING_CURRENT = 5 # Minimum ring current to use auto-gain
|
||||
NOMINAL_RING_CURRENT = 400 # Nominal ring current of SLS2
|
||||
|
||||
MONO_VELOCITY = 20 # Move velocity in deg/s
|
||||
TIMEOUT_MONO_PV = 5 # Timeout to set a PV on the mono
|
||||
TIMEOUT_MONO_MOVE = 30 # Timeout to finish a movement on the mono
|
||||
MONO_VELOCITY = 20 # Move velocity in deg/s
|
||||
TIMEOUT_MONO_PV = 5 # Timeout to set a PV on the mono
|
||||
TIMEOUT_MONO_MOVE = 30 # Timeout to finish a movement on the mono
|
||||
|
||||
AVAILABLE_GAINS = [1e6, 1e7, 5e7, 1e8, 1e9] # ascending order
|
||||
MIN_SIGNAL = 0.05 # Minimum signal to count as valid signal
|
||||
MIN_SIGNAL = 0.05 # Minimum signal to count as valid signal
|
||||
FULL_SCALE_V = 10.0 # NIDAQ AI full-scale range
|
||||
SAFETY_MARGIN = 0.9 # keep max signal under 90% of full scale
|
||||
|
||||
|
||||
class AutoGainError(Exception):
|
||||
"""AutoGain specific error"""
|
||||
|
||||
class AutoGain():
|
||||
""" Module to automatically set the gains for the selected amplifiers"""
|
||||
|
||||
class AutoGain:
|
||||
"""Module to automatically set the gains for the selected amplifiers"""
|
||||
|
||||
def __init__(self):
|
||||
dev = builtins.__dict__.get("dev")
|
||||
bec = builtins.__dict__.get("bec")
|
||||
if dev is None:
|
||||
raise AutoGainError('Did not get dev')
|
||||
raise AutoGainError("Did not get dev")
|
||||
if bec is None:
|
||||
raise AutoGainError('Did not get bec')
|
||||
raise AutoGainError("Did not get bec")
|
||||
self.dev = dev
|
||||
self.bec = bec
|
||||
|
||||
def start(
|
||||
self,
|
||||
element:str,
|
||||
edge:str,
|
||||
amplifier:list[str] | None=None,
|
||||
comp_ring_current:bool=True
|
||||
) -> None:
|
||||
""" Start the auto-gain sequence. Measure the signals of the specified
|
||||
self,
|
||||
element: str,
|
||||
edge: str,
|
||||
amplifier: list[str] | None = None,
|
||||
comp_ring_current: bool = True,
|
||||
) -> None:
|
||||
"""Start the auto-gain sequence. Measure the signals of the specified
|
||||
amplifiers and set the gains accordingly. Makes sure there is actually beam available.
|
||||
|
||||
|
||||
Args:
|
||||
element(str): Element which defines the energy at which the gain will be set, e.g. 'Cu'
|
||||
edge(str): Corresponding edge, e.g. 'L1'
|
||||
@@ -73,25 +76,25 @@ class AutoGain():
|
||||
If a selected amplifier is switched off
|
||||
If the high voltage of a selected ionization chamber is not enabled or < 1000 V
|
||||
If the gas filling of a selected ionization chamber is not OK
|
||||
If the ring current drops to 0 mA during the measurement (beamdump)
|
||||
If the ring current drops to 0 mA during the measurement (beamdump)
|
||||
"""
|
||||
|
||||
if amplifier is None:
|
||||
amplifier = ['ic0', 'ic1', 'ic2', 'pips']
|
||||
amplifier = ["ic0", "ic1", "ic2", "pips"]
|
||||
|
||||
# Make sure NIDAQ is in standby mode
|
||||
if self.dev.nidaq.state.get() is not NidaqState.STANDBY:
|
||||
raise AutoGainError('NIDAQ was not in Standby mode, cannot proceed.')
|
||||
if self.dev.nidaq.state.get() != NidaqState.STANDBY:
|
||||
raise AutoGainError("NIDAQ was not in Standby mode, cannot proceed.")
|
||||
|
||||
# Check for beam availability
|
||||
if self._get_ring_current() < MIN_RING_CURRENT:
|
||||
raise AutoGainError(f'Ring current is below {MIN_RING_CURRENT} mA')
|
||||
raise AutoGainError(f"Ring current is below {MIN_RING_CURRENT} mA")
|
||||
if self.dev.abs.status.get() != ABS_STATUS.OPEN:
|
||||
raise AutoGainError('Absorber is closed, no beam')
|
||||
if self.dev.op_shutter.status.get() != OP_PH_STATUS.NOT_CLOSED:
|
||||
raise AutoGainError('OP Photon Shutter is closed, no beam')
|
||||
if self.dev.eh_shutter.status.get() != EH_PH_STATUS.NOT_CLOSED:
|
||||
raise AutoGainError('EH Photon Shutter is closed, no beam')
|
||||
raise AutoGainError("Absorber is closed, no beam")
|
||||
if self.dev.op_sh.status.get() != OP_PH_STATUS.NOT_CLOSED:
|
||||
raise AutoGainError("OP Photon Shutter is closed, no beam")
|
||||
if self.dev.eh_sh.status.get() != EH_PH_STATUS.NOT_CLOSED:
|
||||
raise AutoGainError("EH Photon Shutter is closed, no beam")
|
||||
|
||||
# Check if no scan is running
|
||||
scan_id = self.bec.queue.scan_storage.current_scan_id
|
||||
@@ -101,56 +104,57 @@ class AutoGain():
|
||||
# Get edge energy
|
||||
energy = xraydb.xray_edge(element, edge, True)
|
||||
if energy is None:
|
||||
raise ValueError(f'Could not find edge energy for element/edge {element}/{edge}')
|
||||
raise ValueError(f"Could not find edge energy for element/edge {element}/{edge}")
|
||||
emin = energy + EMIN
|
||||
emax = energy + EMAX
|
||||
|
||||
# Check range of mono
|
||||
low_limit, high_limit = self.dev.mo1_bragg.limits()
|
||||
low_limit = self.dev.mo1_bragg.low_lim.get()
|
||||
high_limit = self.dev.mo1_bragg.high_lim.get()
|
||||
if emin < low_limit or emax > high_limit:
|
||||
raise ValueError(
|
||||
f'Chosen element/edge {element}/{edge} with edge energy of {energy}' +
|
||||
' is outside of accessible range of monochromator ' +
|
||||
f'{low_limit:.1f} eV - {high_limit:.1f} eV'
|
||||
f"Chosen element/edge {element}/{edge} with edge energy of {energy}"
|
||||
+ " is outside of accessible range of monochromator "
|
||||
+ f"{low_limit:.1f} eV - {high_limit:.1f} eV"
|
||||
)
|
||||
|
||||
# Map amplifier names to their NIDAQ channels
|
||||
channel_map = {
|
||||
'ic0': {'signal': self.dev.nidaq.ai0, 'self.dev': self.dev.ic0},
|
||||
'ic1': {'signal': self.dev.nidaq.ai2, 'self.dev': self.dev.ic1},
|
||||
'ic2': {'signal': self.dev.nidaq.ai4, 'self.dev': self.dev.ic2},
|
||||
'pips': {'signal': self.dev.nidaq.ai6, 'self.dev': self.dev.pips},
|
||||
"ic0": {"signal": self.dev.nidaq.ai0, "self.dev": self.dev.ic0},
|
||||
"ic1": {"signal": self.dev.nidaq.ai2, "self.dev": self.dev.ic1},
|
||||
"ic2": {"signal": self.dev.nidaq.ai4, "self.dev": self.dev.ic2},
|
||||
"pips": {"signal": self.dev.nidaq.ai6, "self.dev": self.dev.pips},
|
||||
}
|
||||
active_channels = {name: ch for name, ch in channel_map.items() if name in amplifier}
|
||||
|
||||
# Check if amplifieres are switched on
|
||||
for name, ch in active_channels.items():
|
||||
if ch['self.dev'].amp.cOnOff.get() != AmplifierEnable.ON:
|
||||
if ch["self.dev"].amp.cOnOff.get() != AmplifierEnable.ON:
|
||||
raise AutoGainError(f"Amplifier of self.device {name} is not enabled")
|
||||
|
||||
# Check high voltage on ionization chambers
|
||||
for name, ch in active_channels.items():
|
||||
if name != 'pips':
|
||||
if ch['self.dev'].hv_en.ena.get() is not True:
|
||||
if name != "pips":
|
||||
if ch["self.dev"].hv_en.ena.get() != 1:
|
||||
raise AutoGainError(f"High voltage of ionization chamber {name} is not enabled")
|
||||
if ch['self.dev'].hv.v.get() < 1000:
|
||||
if ch["self.dev"].hv.hv_v.get() < 1000:
|
||||
raise AutoGainError(f"HV voltage of ionization chamber {name} is < 1000")
|
||||
if ch['self.dev'].hv.grid_v.get() < 1000:
|
||||
if ch["self.dev"].hv.grid_v.get() < 1000:
|
||||
raise AutoGainError(f"Grid voltage of ionization chamber {name} is < 1000")
|
||||
|
||||
# Check gas filling of ionization chambers
|
||||
for name, ch in active_channels.items():
|
||||
if name != 'pips':
|
||||
if ch['self.dev'].gmes.status.get() is not True:
|
||||
raise AutoGainError(f'Gas filling of ionization chamber {name} is not OK')
|
||||
if name != "pips":
|
||||
if ch["self.dev"].gmes.status.get() != 1:
|
||||
raise AutoGainError(f"Gas filling of ionization chamber {name} is not OK")
|
||||
|
||||
logger.info('All checks done, start preparing for measurement')
|
||||
logger.info("All checks done, start preparing for measurement")
|
||||
|
||||
# Get initial monochromator position and velocity
|
||||
init_pos = self.dev.mo1_bragg.position.get()
|
||||
init_vel = self.dev.mo1_bragg.velocity.get()
|
||||
|
||||
logger.info(f'Move mono to start of {emin} eV')
|
||||
logger.info(f"Move mono to start of {emin} eV")
|
||||
status = self.dev.mo1_bragg.move(emin)
|
||||
status.wait(TIMEOUT_MONO_MOVE)
|
||||
|
||||
@@ -160,20 +164,20 @@ class AutoGain():
|
||||
# Set gains to lowest gain
|
||||
for name, ch in active_channels.items():
|
||||
lowest_gain = AVAILABLE_GAINS[0]
|
||||
ch['self.dev'].set_gain(lowest_gain)
|
||||
ch['gain'] = lowest_gain
|
||||
ch["self.dev"].set_gain(lowest_gain)
|
||||
ch["gain"] = lowest_gain
|
||||
|
||||
remeasure = True
|
||||
logger.info(f'Start measurement from {emin} eV to {emax} eV')
|
||||
logger.info(f"Start measurement from {emin} eV to {emax} eV")
|
||||
while remeasure:
|
||||
# Create temporary storage for max signal per channel
|
||||
data = {name: 0 for name in active_channels}
|
||||
|
||||
# Measure current ring current
|
||||
ring_current_1 = self._get_ring_current()
|
||||
logger.info(f'Ring current right before measurement: {ring_current_1} mA')
|
||||
logger.info(f"Ring current right before measurement: {ring_current_1} mA")
|
||||
if ring_current_1 == 0:
|
||||
raise AutoGainError('Ring current dropped to 0 mA right before measurement')
|
||||
raise AutoGainError("Ring current dropped to 0 mA right before measurement")
|
||||
|
||||
# Scan range, recording the peak NIDAQ signal per channel
|
||||
status = self.dev.mo1_bragg.velocity.put(MONO_VELOCITY)
|
||||
@@ -181,16 +185,16 @@ class AutoGain():
|
||||
self.dev.mo1_bragg.move(emax).wait(timeout=TIMEOUT_MONO_MOVE)
|
||||
status.wait(TIMEOUT_MONO_MOVE)
|
||||
for name, ch in active_channels.items():
|
||||
data[name] = max(data[name], ch['signal'].get())
|
||||
data[name] = max(data[name], ch["signal"].get())
|
||||
|
||||
# Rest max values of NIDAQ signals
|
||||
# TODO implement
|
||||
|
||||
# Measure current ring current again
|
||||
ring_current_2 = self._get_ring_current()
|
||||
logger.info(f'Ring current right after measurement: {ring_current_2} mA')
|
||||
logger.info(f"Ring current right after measurement: {ring_current_2} mA")
|
||||
if ring_current_2 == 0:
|
||||
raise AutoGainError('Ring current dropped to 0 mA during measurement')
|
||||
raise AutoGainError("Ring current dropped to 0 mA during measurement")
|
||||
ring_current = (ring_current_1 + ring_current_2) / 2
|
||||
|
||||
# Move back to first monochromator position
|
||||
@@ -200,35 +204,39 @@ class AutoGain():
|
||||
remeasure = False
|
||||
for name, ch in active_channels.items():
|
||||
raw_signal = data[name]
|
||||
logger.info(f'Raw signal for self.device {name} is {raw_signal} V')
|
||||
logger.info(f"Raw signal for self.device {name} is {raw_signal} V")
|
||||
if comp_ring_current:
|
||||
raw_signal = raw_signal * NOMINAL_RING_CURRENT / ring_current
|
||||
logger.info(f'Compensate for ring current, new raw signal is {raw_signal} V')
|
||||
logger.info(f"Compensate for ring current, new raw signal is {raw_signal} V")
|
||||
if raw_signal < MIN_SIGNAL:
|
||||
logger.info(f'Raw signal for self.device {name} is below {MIN_SIGNAL}')
|
||||
logger.info(f"Raw signal for self.device {name} is below {MIN_SIGNAL}")
|
||||
# Choose next gain to be 100x the current gain, or if this gain does not exist,
|
||||
# choose the next smaller one
|
||||
if ch['gain'] == AVAILABLE_GAINS[-1]:
|
||||
if ch["gain"] == AVAILABLE_GAINS[-1]:
|
||||
logger.warning(
|
||||
f"Amplifier of {name} at highest gain {ch['gain']} and still not" +
|
||||
f" measured signal above {MIN_SIGNAL}"
|
||||
f"Amplifier of {name} at highest gain {ch['gain']} and still not"
|
||||
+ f" measured signal above {MIN_SIGNAL}"
|
||||
)
|
||||
else:
|
||||
next_gain = AVAILABLE_GAINS[
|
||||
bisect_right(AVAILABLE_GAINS, ch['gain'] * 100) - 1
|
||||
bisect_right(AVAILABLE_GAINS, ch["gain"] * 100) - 1
|
||||
]
|
||||
ch['self.dev'].set_gain(next_gain)
|
||||
ch["self.dev"].set_gain(next_gain)
|
||||
logger.info(
|
||||
f'Setting gain of self.device {name} to {next_gain:.0e} and remeasure'
|
||||
f"Setting gain of self.device {name} to {next_gain:.0e} and remeasure"
|
||||
)
|
||||
remeasure = True
|
||||
else:
|
||||
gain = max(
|
||||
(g for g in AVAILABLE_GAINS if raw_signal / ch['gain'] * g <= FULL_SCALE_V * SAFETY_MARGIN),
|
||||
(
|
||||
g
|
||||
for g in AVAILABLE_GAINS
|
||||
if raw_signal / ch["gain"] * g <= FULL_SCALE_V * SAFETY_MARGIN
|
||||
),
|
||||
default=min(AVAILABLE_GAINS),
|
||||
)
|
||||
ch['self.dev'].set_gain(gain)
|
||||
logger.info(f'Calculated final gain for {name} of {gain:.0e}')
|
||||
ch["self.dev"].set_gain(gain)
|
||||
logger.info(f"Calculated final gain for {name} of {gain:.0e}")
|
||||
|
||||
# Wait for mono to return to start position
|
||||
status.wait(TIMEOUT_MONO_MOVE)
|
||||
|
||||
Reference in new issue
Block a user