Source code for csaxs_bec.devices.pseudo_devices.bpm_control

"""
Module for controlling the BPM amplifier settings, such as gain and coupling.
"""

from __future__ import annotations

from typing import TYPE_CHECKING, Literal

from ophyd import Component as Cpt
from ophyd import Kind
from ophyd_devices.interfaces.base_classes.psi_pseudo_device_base import PSIPseudoDeviceBase
from ophyd_devices.utils.bec_processed_signal import BECProcessedSignal

if TYPE_CHECKING:  # pragma: no cover
    from bec_lib.devicemanager import ScanInfo
    from bec_server.device_server.devices.devicemanager import DeviceManagerDS
    from ophyd import Signal

_GAIN_BITS_LOW_NOISE: dict[tuple, int] = {
    (0, 0, 0): int(1e3),
    (0, 0, 1): int(1e4),
    (0, 1, 0): int(1e5),
    (0, 1, 1): int(1e6),
    (1, 0, 0): int(1e7),
    (1, 0, 1): int(1e8),
    (1, 1, 0): int(1e9),
}

_GAIN_BITS_HIGH_SPEED: dict[tuple, int] = {
    (0, 0, 0): int(1e5),
    (0, 0, 1): int(1e6),
    (0, 1, 0): int(1e7),
    (0, 1, 1): int(1e8),
    (1, 0, 0): int(1e9),
    (1, 0, 1): int(1e10),
    (1, 1, 0): int(1e11),
}

_GAIN_TO_BITS: dict[int, tuple] = {}
for _bits, _gain in _GAIN_BITS_LOW_NOISE.items():
    _GAIN_TO_BITS[_gain] = (*_bits, True)
for _bits, _gain in _GAIN_BITS_HIGH_SPEED.items():
    if _gain not in _GAIN_TO_BITS:  # low-noise takes priority
        _GAIN_TO_BITS[_gain] = (*_bits, False)

VALID_GAINS = sorted(_GAIN_TO_BITS.keys())


[docs] class BPMControl(PSIPseudoDeviceBase): """ BPM amplifier control pseudo device. It is responsible for controlling the gain and coupling for the BPM amplifier. It relies on signals from a device in BEC to be available. For cSAXS, these are most liikely to be from the GalilRIO device that controls the BPM amplifier. Args: name (str): Name of the pseudo device. gain_lsb (str): Name of the signal in BEC that controls the LSB of the gain setting. gain_mid (str): Name of the signal in BEC that controls the MID bit of the gain setting. gain_msb (str): Name of the signal in BEC that controls the MSB of the gain setting. coupling (str): Name of the signal in BEC that controls the coupling setting. speed_mode (str): Name of the signal in BEC that controls the speed mode (low-noise vs high-speed) of the amplifier. """ USER_ACCESS = ["set_gain", "set_coupling"] gain = Cpt( BECProcessedSignal, name="gain", model_config=None, kind=Kind.normal, doc="Gain of the amplifier", ) coupling = Cpt( BECProcessedSignal, name="coupling", model_config=None, kind=Kind.normal, doc="Coupling of the amplifier", ) speed = Cpt( BECProcessedSignal, name="speed", model_config=None, kind=Kind.normal, doc="Speed of the amplifier", ) def __init__( self, name: str, gain_lsb: str, gain_mid: str, gain_msb: str, coupling: str, speed_mode: str, device_manager: DeviceManagerDS | None = None, scan_info: ScanInfo | None = None, **kwargs, ): super().__init__(name=name, device_manager=device_manager, scan_info=scan_info, **kwargs) # First we get all signal objects from BEC using the utility method provided by the BECProcessedSignal class. self._gain_lsb = self.gain.get_device_object_from_bec( object_name=gain_lsb, signal_name=self.name, device_manager=device_manager ) self._gain_mid = self.gain.get_device_object_from_bec( object_name=gain_mid, signal_name=self.name, device_manager=device_manager ) self._gain_msb = self.gain.get_device_object_from_bec( object_name=gain_msb, signal_name=self.name, device_manager=device_manager ) self._coupling = self.gain.get_device_object_from_bec( object_name=coupling, signal_name=self.name, device_manager=device_manager ) self._speed_mode = self.gain.get_device_object_from_bec( object_name=speed_mode, signal_name=self.name, device_manager=device_manager ) # Set the compute methods for the virtual signals. self.gain.set_compute_method( self._compute_gain, msb=self._gain_msb, mid=self._gain_mid, lsb=self._gain_lsb, speed_mode=self._speed_mode, ) self.coupling.set_compute_method(self._compute_coupling, coupling=self._coupling) self.speed.set_compute_method(self._compute_speed, speed=self._speed_mode)
[docs] def set_gain( self, gain: Literal[ 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000, 10000000000, 100000000000 ], ) -> None: """ Set the gain of the amplifier. Args: gain (Literal): Must be one of 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000, 10000000000. """ gain_int = int(gain) if gain_int not in VALID_GAINS: raise ValueError( f"{self.name} received invalid gain {gain_int}, must be in {VALID_GAINS}" ) msb, mid, lsb, use_low_noise = _GAIN_TO_BITS[gain_int] self._gain_msb.set(bool(msb)).wait(timeout=2) self._gain_lsb.set(bool(lsb)).wait(timeout=2) self._gain_mid.set(bool(mid)).wait(timeout=2) self._speed_mode.set(bool(use_low_noise))
[docs] def set_coupling(self, coupling: Literal["AC", "DC"]) -> None: """ Set the coupling of the amplifier. Args: coupling (Literal): Must be either "AC" or "DC". """ if coupling not in ["AC", "DC"]: raise ValueError( f"{self.name} received invalid coupling value {coupling}, please use 'AC' or 'DC'" ) self._coupling.set(coupling == "DC").wait(timeout=2)
def _compute_gain(self, msb: Signal, mid: Signal, lsb: Signal, speed_mode: Signal) -> int: """Compute the gain based on the bits and speed mode.""" bits = (msb.get(), mid.get(), lsb.get()) speed_mode = speed_mode.get() if speed_mode: return _GAIN_BITS_LOW_NOISE.get(bits) else: return _GAIN_BITS_HIGH_SPEED.get(bits) def _compute_coupling(self, coupling: Signal) -> str: """Compute the coupling based on the signal.""" return "DC" if coupling.get() else "AC" def _compute_speed(self, speed: Signal) -> str: """Compute the speed based on the signal.""" return "low_speed" if speed.get() else "high_speed"