Source code for apstools.devices.srs570_preamplifier

"""
Ophyd support for Stanford Research Systems 570 preamplifier from synApps

Public Structures

.. autosummary::

    ~SRS570_PreAmplifier

This device connects with the SRS570 support from synApps.
(https://github.com/epics-modules/ip/blob/master/ipApp/Db/SR570.db)

The SRS570 synApps support is part of the ``ip`` module:
https://htmlpreview.github.io/?https://raw.githubusercontent.com/epics-modules/ip/R3-6-1/documentation/swaitRecord.html

:see: https://github.com/epics-modules/ip
"""

import logging

import pint
from ophyd import Component
from ophyd import EpicsSignal
from ophyd.signal import DEFAULT_WRITE_TIMEOUT

from .preamp_base import PreamplifierBaseDevice

logger = logging.getLogger(__name__)


gain_units = ["pA/V", "nA/V", "uA/V", "mA/V"]
gain_values = ["1", "2", "5", "10", "20", "50", "100", "200", "500"]
gain_modes = ["LOW NOISE", "HIGH BW"]

# Settling times measured from the 25-ID-C upstream I0 chamber's SR-570
# (sensitivity_value, sensitivity_unit, gain_mode): settle_time
settling_times = {
    # pA/V
    ("1", "pA/V", "HIGH BW"): 2.5,
    ("2", "pA/V", "HIGH BW"): 2,
    ("5", "pA/V", "HIGH BW"): 2.0,
    ("10", "pA/V", "HIGH BW"): 0.5,
    ("20", "pA/V", "HIGH BW"): 0.5,
    ("50", "pA/V", "HIGH BW"): 0.5,
    ("100", "pA/V", "HIGH BW"): 0.5,
    ("200", "pA/V", "HIGH BW"): 0.3,
    ("500", "pA/V", "HIGH BW"): 0.3,
    ("1", "pA/V", "LOW NOISE"): 3.0,
    ("2", "pA/V", "LOW NOISE"): 2.5,
    ("5", "pA/V", "LOW NOISE"): 2.0,
    ("10", "pA/V", "LOW NOISE"): 2.0,
    ("20", "pA/V", "LOW NOISE"): 1.75,
    ("50", "pA/V", "LOW NOISE"): 1.5,
    ("100", "pA/V", "LOW NOISE"): 1.25,
    ("200", "pA/V", "LOW NOISE"): 0.5,
    ("500", "pA/V", "LOW NOISE"): 0.5,
}
settling_times.update(
    {
        # nA/V, high bandwidth
        (gain_values[idx], "nA/V", "HIGH BW"): 0.3
        for idx in range(9)
    }
)
settling_times.update(
    {
        # nA/V, low noise
        (gain_values[idx], "nA/V", "LOW NOISE"): 0.3
        for idx in range(9)
    }
)
settling_times.update(
    {
        # μA/V, high bandwidth
        (gain_values[idx], "uA/V", "HIGH BW"): 0.3
        for idx in range(9)
    }
)
settling_times.update(
    {
        # μA/V, low noise
        (gain_values[idx], "uA/V", "LOW NOISE"): 0.3
        for idx in range(9)
    }
)
settling_times.update(
    {
        ("1", "mA/V", "HIGH BW"): 0.3,
        ("1", "mA/V", "LOW NOISE"): 0.3,
    }
)


[docs]def calculate_settle_time(gain_value: int, gain_unit: int, gain_mode: str): """Determine the best settle time for a given combination of parameters. Parameters can be strings of indexes. """ # Convert indexes to string values try: gain_value = gain_values[gain_value] except (TypeError, IndexError): pass try: gain_unit = gain_units[gain_unit] except (TypeError, IndexError): pass try: gain_mode = gain_modes[gain_mode] except (TypeError, IndexError): pass # Get calibrated settle time, or None to use the Ophyd default return settling_times.get((gain_value, gain_unit, gain_mode))
[docs]class GainSignal(EpicsSignal): """A signal where the settling time depends on the pre-amp gain. Used to introduce a specific settle time when setting to account for the amp's R–C relaxation time when changing gain. """
[docs] def set(self, value, *, timeout=DEFAULT_WRITE_TIMEOUT, settle_time="auto"): """Set the value of the Signal and return a Status object. If put completion is used for this EpicsSignal, the status object will complete once EPICS reports the put has completed. Otherwise the readback will be polled until equal to the set point (as in `Signal.set`) Parameters ---------- value : any timeout : float, optional Maximum time to wait. settle_time: float, optional Delay after the set() has completed to indicate completion to the caller. If "auto" (default), a reasonable settle time will be chosen based on the gain mode on the pre-amp. Returns ------- st : Status See Also -------- Signal.set EpicsSignal.set """ # Determine optimal settling time. if settle_time == "auto": signals = [self.parent.sensitivity_value, self.parent.sensitivity_unit, self.parent.gain_mode] args = [value if self is sig else sig.get() for sig in signals] val, unit, mode = args # Resolve string values to indices if provided if val in gain_values: val = gain_values.index(val) if unit in gain_units: unit = gain_units.index(unit) if mode in gain_modes: mode = gain_modes.index(mode) # Low-drift mode uses the same settling times as low-noise mode if mode == "LOW DRIFT": mode = "LOW NOISE" # Calculate settling time _settle_time = calculate_settle_time(gain_value=val, gain_unit=unit, gain_mode=mode) else: _settle_time = settle_time return super().set(value, timeout=timeout, settle_time=_settle_time)
[docs]class SRS570_PreAmplifier(PreamplifierBaseDevice): """ Ophyd support for Stanford Research Systems 570 preamplifier from synApps. """ # These values are strings because that is how they are defined # in the EPICS .db file. Must cast them to ``float()`` or ``int()`` # as desired. # see: https://github.com/epics-modules/ip/blob/master/ipApp/Db/SR570.db sensitivity_value = Component(GainSignal, "sens_num", kind="config", string=True) sensitivity_unit = Component(GainSignal, "sens_unit", kind="config", string=True) offset_on = Component(EpicsSignal, "offset_on", kind="config", string=True) offset_sign = Component(EpicsSignal, "offset_sign", kind="config", string=True) offset_value = Component(EpicsSignal, "offset_num", kind="config", string=True) offset_unit = Component(EpicsSignal, "offset_unit", kind="config", string=True) offset_fine = Component(EpicsSignal, "off_u_put", kind="config", string=True) offset_cal = Component(EpicsSignal, "offset_cal", kind="config", string=True) set_all = Component(GainSignal, "init.PROC", kind="config") bias_value = Component(EpicsSignal, "bias_put", kind="config", string=True) bias_on = Component(EpicsSignal, "bias_on", kind="config", string=True) filter_type = Component(EpicsSignal, "filter_type", kind="config", string=True) filter_lowpass = Component(EpicsSignal, "low_freq", kind="config", string=True) filter_highpass = Component(EpicsSignal, "high_freq", kind="config", string=True) gain_mode = Component(GainSignal, "gain_mode", kind="config", string=True) invert = Component(EpicsSignal, "invert_on", kind="config", string=True) blank = Component(EpicsSignal, "blank_on", kind="config", string=True) @property def computed_gain(self): """ Amplifier gain (A/V), as floating-point number. """ return pint.Quantity(float(self.sensitivity_value.get()), self.sensitivity_unit.get()).to("A/V").magnitude
[docs] def cb_gain(self, *args, **kwargs): """ Called when sensitivity changes (EPICS CA monitor event). """ self.gain.put(self.computed_gain)
def __init__(self, *args, **kwargs): """ Update the gain when the sensitivity changes. """ super().__init__(*args, **kwargs) self.sensitivity_value.subscribe(self.cb_gain) self.sensitivity_unit.subscribe(self.cb_gain) # amplifier gain is the most relevant value self.gain.name = self.name
# ----------------------------------------------------------------------------- # :author: Pete R. Jemian # :email: jemian@anl.gov # :copyright: (c) 2017-2023, UChicago Argonne, LLC # # Distributed under the terms of the Argonne National Laboratory Open Source License. # # The full license is in the file LICENSE.txt, distributed with this software. # -----------------------------------------------------------------------------