Source code for herosdevices.hardware.menlo.dds
"""HERO driver for a DDS-based RF output of a Menlo Systems frequency comb."""
from herosdevices.helper import mark_driver
from herosdevices.interfaces.atomiq import RFSource
from .functional_layer import FunctionalLayerModule
from .ofc import OFC
[docs]
@mark_driver(
name="Menlo DDS",
info="DDS-based RF output of a Menlo Systems frequency comb, via QWebChannel",
state="alpha",
additional_docs=["/tutorials/menlo_ofc.rst"],
product_page="https://www.menlosystems.com/",
)
class DDS(FunctionalLayerModule, RFSource):
"""Driver for a DDS-based RF output of a Menlo Systems frequency comb.
Wraps one `functionalLayer.<funclayer_identifier>Settings` sub-tree of an already-running
:py:class:`~herosdevices.hardware.menlo.ofc.OFC`.
This covers only the bare DDS output. For a module that additionally has a closed feedback loop locking
the DDS to a reference (a beat-note PLL), use :py:class:`~herosdevices.hardware.menlo.LaserLock`
instead.
Polls :py:attr:`DEFAULT_OBSERVABLES` for `observable_data` with live reads from the OFC, superseding the
`RFSource` interface's own cached-value observable.
Args:
ofc: The OFC HERO this DDS output belongs to.
funclayer_identifier: Identifier of the module's functional-layer settings object, e.g. "cw1112_1"
for the sub-tree at `functionalLayer.cw1112_1Settings`. Use `ofc.explore("functionalLayer")` to
find the identifier for a given module.
observables: Additional observables to poll, merged on top of `DEFAULT_OBSERVABLES`, see
:py:class:`~herosdevices.hardware.menlo.functional_layer.FunctionalLayerModule`.
"""
amp_max: float = 1.0
amp_min: float = 0.0
freq_max: float = 100e6
freq_min: float = 0.0
DEFAULT_OBSERVABLES: dict[str, dict[str, str]] = {
"frequency": {"path": "dds.ddsFrequency", "unit": "Hz"},
"amplitude": {"path": "dds.outputPower", "unit": "total"},
"output_enabled": {"path": "dds.outputOn", "unit": ""},
}
def __init__(
self, ofc: OFC, funclayer_identifier: str, observables: dict[str, dict[str, str]] | None = None
) -> None:
FunctionalLayerModule.__init__(
self, ofc, f"functionalLayer.{funclayer_identifier}Settings", observables=observables
)
def _set_frequency(self, frequency: float) -> None:
self.ofc.set_node(self._path("dds.ddsFrequency"), int(frequency))
self.frequency = frequency
def _get_frequency(self) -> float:
return float(self.ofc.get_node(self._path("dds.ddsFrequency")))
def _set_amplitude(self, amplitude: float) -> None:
self.ofc.set_node(self._path("dds.outputPower"), amplitude)
self.amplitude = amplitude
def _get_amplitude(self) -> float:
return float(self.ofc.get_node(self._path("dds.outputPower")))
def _set_phase(self, phase: float) -> None:
raise NotImplementedError("The Menlo DDS does not support setting a phase")
def _get_phase(self) -> float:
raise NotImplementedError("The Menlo DDS does not support setting a phase")
@property
def output_enabled(self) -> bool:
"""Whether the DDS output is enabled."""
return self.ofc.get_node(self._path("dds.outputOn"))
@output_enabled.setter
def output_enabled(self, enabled: bool) -> None:
self.ofc.set_node(self._path("dds.outputOn"), enabled)