From 2343436051f5a9fce5d691b0403079634cbf3db5 Mon Sep 17 00:00:00 2001 From: Mark Wolfman Date: Fri, 25 Aug 2023 13:35:58 -0500 Subject: [PATCH 1/4] ENH Added support for the SRS DG-645 digital delay/pulse generator. --- apstools/devices/__init__.py | 2 + apstools/devices/delay.py | 112 ++++++++++++++++++ apstools/devices/tests/test_delay.py | 166 +++++++++++++++++++++++++++ 3 files changed, 280 insertions(+) create mode 100644 apstools/devices/delay.py create mode 100644 apstools/devices/tests/test_delay.py diff --git a/apstools/devices/__init__.py b/apstools/devices/__init__.py index 679dac52c..13404f70c 100644 --- a/apstools/devices/__init__.py +++ b/apstools/devices/__init__.py @@ -44,6 +44,8 @@ from .axis_tuner import AxisTunerException from .axis_tuner import AxisTunerMixin +from .delay import DG645Delay + from .description_mixin import EpicsDescriptionMixin from .dict_device_support import dict_device_factory diff --git a/apstools/devices/delay.py b/apstools/devices/delay.py new file mode 100644 index 000000000..c50cee3ca --- /dev/null +++ b/apstools/devices/delay.py @@ -0,0 +1,112 @@ +"""Ophyd definitions for digital delay and pulse generators.""" + +from ophyd import Device, Component as Cpt, EpicsSignal, EpicsSignalRO, Kind + + +__all__ = ["DG645Delay"] + + +class EpicsSignalWithIO(EpicsSignal): + # An EPICS signal that simply uses the DG-645 convention of + # 'AO' being the setpoint and 'AI' being the read-back + + def __init__(self, prefix, **kwargs): + super().__init__(f"{prefix}I", write_pv=f"{prefix}O", **kwargs) + + +class DG645Channel(Device): + reference = Cpt(EpicsSignalWithIO, "ReferenceM", kind=Kind.config) + delay = Cpt(EpicsSignalWithIO, "DelayA", kind=Kind.normal) + + +class DG645Output(Device): + output_mode_ttl = Cpt(EpicsSignal, "OutputModeTtlSS.PROC", kind=Kind.omitted) + output_mode_nim = Cpt(EpicsSignal, "OutputModeNimSS.PROC", kind=Kind.omitted) + polarity = Cpt(EpicsSignalWithIO, "OutputPolarityB", kind=Kind.config) + amplitude = Cpt(EpicsSignalWithIO, "OutputAmpA", kind=Kind.config) + offset = Cpt(EpicsSignalWithIO, "OutputOffsetA", kind=Kind.config) + + +class DG645DelayOutput(DG645Output): + trigger_prescale = Cpt(EpicsSignalWithIO, "TriggerPrescaleL", kind=Kind.config) + trigger_phase = Cpt(EpicsSignalWithIO, "TriggerPhaseL", kind=Kind.config) + + +class DG645Delay(Device): + """An SRS DG-645 digial delay/pulse generator. + + This device has four delayed outputs: AB, CD, EF, GH. + + Configuration of the output parameters (e.g. amplitude, polarity) + is done using components ``output_AB``, etc. The individual delays + for the start and end of the output pulse are configured using + individual channels ``channel_A`` etc. + + There is also a ``T0`` output which is the reference pulses used + for the remaining delayed outputs. + + """ + # Individual delay channels + channel_A = Cpt(DG645Channel, "A") + channel_B = Cpt(DG645Channel, "B") + channel_C = Cpt(DG645Channel, "C") + channel_D = Cpt(DG645Channel, "D") + channel_E = Cpt(DG645Channel, "E") + channel_F = Cpt(DG645Channel, "F") + channel_G = Cpt(DG645Channel, "G") + channel_H = Cpt(DG645Channel, "H") + + # 2-channel delay outputs + output_T0 = Cpt(DG645Output, "T0", kind=Kind.config) + output_AB = Cpt(DG645DelayOutput, "AB", kind=Kind.config) + output_CD = Cpt(DG645DelayOutput, "CD", kind=Kind.config) + output_EF = Cpt(DG645DelayOutput, "EF", kind=Kind.config) + output_GH = Cpt(DG645DelayOutput, "GH", kind=Kind.config) + + # General settings + label = Cpt(EpicsSignal, "Label", kind=Kind.config) + status = Cpt(EpicsSignalRO, "StatusSI", kind=Kind.omitted) + clear_error = Cpt(EpicsSignal, "StatusClearBO", kind=Kind.omitted) + device_id = Cpt(EpicsSignalRO, "IdentSI", kind=Kind.config) + goto_remote = Cpt(EpicsSignal, "GotoRemoteBO", kind=Kind.omitted) + goto_local = Cpt(EpicsSignal, "GotoLocalBO", kind=Kind.omitted) + reset = Cpt(EpicsSignal, "ResetBO", kind=Kind.omitted) + status_checking = Cpt(EpicsSignal, "StatusCheckingBO", kind=Kind.omitted) + reset_serial = Cpt(EpicsSignal, "IfaceSerialResetBO", kind=Kind.omitted) + serial_state = Cpt(EpicsSignalWithIO, "IfaceSerialStateB", kind=Kind.config) + serial_baud = Cpt(EpicsSignalWithIO, "IfaceSerialBaudM", kind=Kind.config) + reset_gpib = Cpt(EpicsSignal, "IfaceGpibResetBO", kind=Kind.omitted) + gpib_state = Cpt(EpicsSignalWithIO, "IfaceGpibStateB", kind=Kind.config) + gpib_address = Cpt(EpicsSignalWithIO, "IfaceGpibAddrL", kind=Kind.config) + reset_lan = Cpt(EpicsSignal, "IfaceLanResetBO", kind=Kind.omitted) + mac_address = Cpt(EpicsSignalRO, "IfaceMacAddrSI", kind=Kind.config) + lan_state = Cpt(EpicsSignalWithIO, "IfaceLanStateB", kind=Kind.config) + dhcp_state = Cpt(EpicsSignalWithIO, "IfaceDhcpStateB", kind=Kind.config) + autoip_state = Cpt(EpicsSignalWithIO, "IfaceAutoIpStateB", kind=Kind.config) + static_ip_state = Cpt(EpicsSignalWithIO, "IfaceStaticIpStateB", kind=Kind.config) + bare_socket_state = Cpt( + EpicsSignalWithIO, "IfaceBareSocketStateB", kind=Kind.config + ) + telnet_state = Cpt(EpicsSignalWithIO, "IfaceTelnetStateB", kind=Kind.config) + vxi11_state = Cpt(EpicsSignalWithIO, "IfaceVxiStateB", kind=Kind.config) + ip_address = Cpt(EpicsSignalWithIO, "IfaceIpAddrS", kind=Kind.config) + network_mask = Cpt(EpicsSignalWithIO, "IfaceNetMaskS", kind=Kind.config) + gateway = Cpt(EpicsSignalWithIO, "IfaceGatewayS", kind=Kind.config) + + # Trigger control + trigger_source = Cpt(EpicsSignalWithIO, "TriggerSourceM", kind=Kind.config) + trigger_inhibit = Cpt(EpicsSignalWithIO, "TriggerInhibitM", kind=Kind.config) + trigger_level = Cpt(EpicsSignalWithIO, "TriggerLevelA", kind=Kind.config) + trigger_rate = Cpt(EpicsSignalWithIO, "TriggerRateA", kind=Kind.config) + trigger_advanced_mode = Cpt( + EpicsSignalWithIO, "TriggerAdvancedModeB", kind=Kind.config + ) + trigger_holdoff = Cpt(EpicsSignalWithIO, "TriggerHoldoffA", kind=Kind.config) + trigger_prescale = Cpt(EpicsSignalWithIO, "TriggerPrescaleL", kind=Kind.config) + + # Burst settings + burst_mode = Cpt(EpicsSignalWithIO, "BurstModeB", kind=Kind.config) + burst_count = Cpt(EpicsSignalWithIO, "BurstCountL", kind=Kind.config) + burst_mode = Cpt(EpicsSignalWithIO, "BurstConfigB", kind=Kind.config) + burst_delay = Cpt(EpicsSignalWithIO, "BurstDelayA", kind=Kind.config) + burst_period = Cpt(EpicsSignalWithIO, "BurstPeriodA", kind=Kind.config) diff --git a/apstools/devices/tests/test_delay.py b/apstools/devices/tests/test_delay.py new file mode 100644 index 000000000..ae2202a6c --- /dev/null +++ b/apstools/devices/tests/test_delay.py @@ -0,0 +1,166 @@ +""" +test the SRS DG-645 digital delay device support + +Hardware is not available so test with best efforts +""" + +from ...tests import IOC_GP +from .. import delay + +PV_PREFIX = f"phony:{IOC_GP}DG645:" + + +def test_dg645_device(): + dg645 = delay.DG645Delay(PV_PREFIX, name="delay") + assert not dg645.connected + + read_names = [ + "channel_A", + "channel_A.delay", + "channel_B", + "channel_B.delay", + "channel_C", + "channel_C.delay", + "channel_D", + "channel_D.delay", + "channel_E", + "channel_E.delay", + "channel_F", + "channel_F.delay", + "channel_G", + "channel_G.delay", + "channel_H", + "channel_H.delay", + ] + assert sorted(dg645.read_attrs) == sorted(read_names) + + cfg_names = [ + "autoip_state", + "bare_socket_state", + "burst_count", + "burst_delay", + "burst_mode", + "burst_period", + "channel_A", + "channel_A.reference", + "channel_B", + "channel_B.reference", + "channel_C", + "channel_C.reference", + "channel_D", + "channel_D.reference", + "channel_E", + "channel_E.reference", + "channel_F", + "channel_F.reference", + "channel_G", + "channel_G.reference", + "channel_H", + "channel_H.reference", + "device_id", + "dhcp_state", + "gateway", + "gpib_address", + "gpib_state", + "ip_address", + "label", + "lan_state", + "mac_address", + "network_mask", + "output_AB", + "output_AB.amplitude", + "output_AB.offset", + "output_AB.polarity", + "output_AB.trigger_phase", + "output_AB.trigger_prescale", + "output_CD", + "output_CD.amplitude", + "output_CD.offset", + "output_CD.polarity", + "output_CD.trigger_phase", + "output_CD.trigger_prescale", + "output_EF", + "output_EF.amplitude", + "output_EF.offset", + "output_EF.polarity", + "output_EF.trigger_phase", + "output_EF.trigger_prescale", + "output_GH", + "output_GH.amplitude", + "output_GH.offset", + "output_GH.polarity", + "output_GH.trigger_phase", + "output_GH.trigger_prescale", + "output_T0", + "output_T0.amplitude", + "output_T0.offset", + "output_T0.polarity", + "serial_baud", + "serial_state", + "static_ip_state", + "telnet_state", + "trigger_advanced_mode", + "trigger_holdoff", + "trigger_inhibit", + "trigger_level", + "trigger_prescale", + "trigger_rate", + "trigger_source", + "vxi11_state", + ] + assert sorted(dg645.configuration_attrs) == sorted(cfg_names) + + # List all the components + cpt_names = [ + "autoip_state", + "bare_socket_state", + "burst_count", + "burst_delay", + "burst_mode", + "burst_period", + "channel_A", + "channel_B", + "channel_C", + "channel_D", + "channel_E", + "channel_F", + "channel_G", + "channel_H", + "clear_error", + "device_id", + "dhcp_state", + "gateway", + "goto_local", + "goto_remote", + "gpib_address", + "gpib_state", + "ip_address", + "label", + "lan_state", + "mac_address", + "network_mask", + "output_AB", + "output_CD", + "output_EF", + "output_GH", + "output_T0", + "reset", + "reset_gpib", + "reset_lan", + "reset_serial", + "serial_baud", + "serial_state", + "static_ip_state", + "status", + "status_checking", + "telnet_state", + "trigger_advanced_mode", + "trigger_holdoff", + "trigger_inhibit", + "trigger_level", + "trigger_prescale", + "trigger_rate", + "trigger_source", + "vxi11_state", + ] + assert sorted(dg645.component_names) == sorted(cpt_names) From 606e8498fe5ad6d435135421049e8cf6901c6a78 Mon Sep 17 00:00:00 2001 From: Mark Wolfman Date: Fri, 25 Aug 2023 13:37:28 -0500 Subject: [PATCH 2/4] DOC Included DG645Delay device in devices auto API. --- docs/source/api/_devices.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/source/api/_devices.rst b/docs/source/api/_devices.rst index 1dc2b4c19..f7412639d 100644 --- a/docs/source/api/_devices.rst +++ b/docs/source/api/_devices.rst @@ -201,6 +201,7 @@ Other Support ~apstools.devices.flyer_motor_scaler.SignalValueStack ~apstools.devices.srs570_preamplifier.SRS570_PreAmplifier ~apstools.devices.struck3820.Struck3820 + ~apstools.devices.delay.DG645Delay Internal Routines +++++++++++++++++ From dd3d9949154e54911dbc361942bbf9846161f5b3 Mon Sep 17 00:00:00 2001 From: Mark Wolfman Date: Fri, 25 Aug 2023 20:30:08 -0500 Subject: [PATCH 3/4] STY Black formatting. --- apstools/devices/delay.py | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/apstools/devices/delay.py b/apstools/devices/delay.py index c50cee3ca..5d51fd2f0 100644 --- a/apstools/devices/delay.py +++ b/apstools/devices/delay.py @@ -46,6 +46,7 @@ class DG645Delay(Device): for the remaining delayed outputs. """ + # Individual delay channels channel_A = Cpt(DG645Channel, "A") channel_B = Cpt(DG645Channel, "B") @@ -84,9 +85,7 @@ class DG645Delay(Device): dhcp_state = Cpt(EpicsSignalWithIO, "IfaceDhcpStateB", kind=Kind.config) autoip_state = Cpt(EpicsSignalWithIO, "IfaceAutoIpStateB", kind=Kind.config) static_ip_state = Cpt(EpicsSignalWithIO, "IfaceStaticIpStateB", kind=Kind.config) - bare_socket_state = Cpt( - EpicsSignalWithIO, "IfaceBareSocketStateB", kind=Kind.config - ) + bare_socket_state = Cpt(EpicsSignalWithIO, "IfaceBareSocketStateB", kind=Kind.config) telnet_state = Cpt(EpicsSignalWithIO, "IfaceTelnetStateB", kind=Kind.config) vxi11_state = Cpt(EpicsSignalWithIO, "IfaceVxiStateB", kind=Kind.config) ip_address = Cpt(EpicsSignalWithIO, "IfaceIpAddrS", kind=Kind.config) @@ -98,9 +97,7 @@ class DG645Delay(Device): trigger_inhibit = Cpt(EpicsSignalWithIO, "TriggerInhibitM", kind=Kind.config) trigger_level = Cpt(EpicsSignalWithIO, "TriggerLevelA", kind=Kind.config) trigger_rate = Cpt(EpicsSignalWithIO, "TriggerRateA", kind=Kind.config) - trigger_advanced_mode = Cpt( - EpicsSignalWithIO, "TriggerAdvancedModeB", kind=Kind.config - ) + trigger_advanced_mode = Cpt(EpicsSignalWithIO, "TriggerAdvancedModeB", kind=Kind.config) trigger_holdoff = Cpt(EpicsSignalWithIO, "TriggerHoldoffA", kind=Kind.config) trigger_prescale = Cpt(EpicsSignalWithIO, "TriggerPrescaleL", kind=Kind.config) From 0220a8a2498acfd736ef1942de22de3abab26f45 Mon Sep 17 00:00:00 2001 From: Mark Wolfman Date: Wed, 30 Aug 2023 14:22:07 -0500 Subject: [PATCH 4/4] Added delay module to the automodule API documentation. --- docs/source/api/_devices.rst | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/docs/source/api/_devices.rst b/docs/source/api/_devices.rst index f7412639d..4a0a20100 100644 --- a/docs/source/api/_devices.rst +++ b/docs/source/api/_devices.rst @@ -257,6 +257,12 @@ All Submodules :show-inheritance: :inherited-members: +.. automodule:: apstools.devices.delay + :members: + :private-members: + :show-inheritance: + :inherited-members: + .. automodule:: apstools.devices.description_mixin :members: :private-members: