Source code for herosdevices.hardware.menlo.ceo

"""HERO driver for the carrier-envelope-offset (CEO) stabilization loop of a Menlo Systems frequency comb."""

from herosdevices.helper import mark_driver

from .beat_loop import BeatLoop
from .dds import DDS
from .functional_layer import FunctionalLayerModule
from .mixins import FrequencyDistribution, SelectMonitor
from .ofc import OFC


[docs] @mark_driver( name="Menlo CEO", info="Carrier-envelope-offset (CEO) stabilization loop of a Menlo Systems frequency comb, via QWebChannel", state="alpha", additional_docs=["/tutorials/menlo_ofc.rst"], product_page="https://www.menlosystems.com/", ) class CEO(DDS, BeatLoop): """Driver for the carrier-envelope-offset (CEO) stabilization loop of a Menlo Systems frequency comb. Wraps the OFC's `functionalLayer.ceoSettings` sub-tree, a fixed, singleton module (there is only one CEO loop per OFC, so unlike :py:class:`~herosdevices.hardware.menlo.DDS`/ :py:class:`~herosdevices.hardware.menlo.LaserLock` there is no `funclayer_identifier` to select). Combines :py:class:`~herosdevices.hardware.menlo.DDS` with :py:class:`~herosdevices.hardware.menlo.BeatLoop`. This means `CEO` implements atomiq's `RFSource` interface via the DDS: `_set_frequency` tunes the DDS directly, not the loop's beat-frequency setpoint (see :py:attr:`beat_frequency_setpoint` for that). Also polls `xpsTec`, the temperature-controlled actuator's own TEC loop, and exposes `frequency_distribution`/`select_monitor`. Args: ofc: The OFC HERO this loop belongs to. observables: Additional observables to poll, merged on top of `DEFAULT_OBSERVABLES`, see :py:class:`~herosdevices.hardware.menlo.functional_layer.FunctionalLayerModule`. """ DEFAULT_OBSERVABLES: dict[str, dict[str, str]] = BeatLoop.DEFAULT_OBSERVABLES | { "beat_frequency": {"path": "carrierEnvelopeOffsetFrequency.ceoFrequencyActual", "unit": "Hz"}, "beat_frequency_setpoint": {"path": "carrierEnvelopeOffsetFrequency.ceoFrequencySetpoint", "unit": "Hz"}, "amp_enabled": {"path": "mainControls.ampEnabled", "unit": ""}, "xps_tec_temperature": {"path": "xpsTec.t_actual_il", "unit": "deg C"}, "xps_tec_current": {"path": "xpsTec.i_actual", "unit": "A"}, } def __init__(self, ofc: OFC, observables: dict[str, dict[str, str]] | None = None) -> None: FunctionalLayerModule.__init__(self, ofc, "functionalLayer.ceoSettings", observables=observables) self.frequency_distribution = FrequencyDistribution(self, "frequencyDistributionCeoBeat") self.select_monitor = SelectMonitor(self, "selectMonitor") @property def beat_frequency(self) -> float: """The currently measured CEO beat frequency. Read-only.""" return self.ofc.get_node(self._path("carrierEnvelopeOffsetFrequency.ceoFrequencyActual")) @property def beat_frequency_setpoint(self) -> float: """Target CEO beat frequency.""" return self.ofc.get_node(self._path("carrierEnvelopeOffsetFrequency.ceoFrequencySetpoint")) @beat_frequency_setpoint.setter def beat_frequency_setpoint(self, frequency: float) -> None: self.ofc.set_node(self._path("carrierEnvelopeOffsetFrequency.ceoFrequencySetpoint"), frequency) @property def amp_enabled(self) -> bool: """Whether the CEO loop's actuator amplifier is enabled.""" return self.ofc.get_node(self._path("mainControls.ampEnabled")) @amp_enabled.setter def amp_enabled(self, enabled: bool) -> None: self.ofc.set_node(self._path("mainControls.ampEnabled"), enabled)