diff --git a/pyaml/common/holders/rf_holder.py b/pyaml/common/holders/rf_holder.py index 257c396a..6a7539ab 100644 --- a/pyaml/common/holders/rf_holder.py +++ b/pyaml/common/holders/rf_holder.py @@ -83,10 +83,12 @@ class RFHolder: ---------- transmitter Return the holder for RF transmitter elements. + masterclock + Return the RF plant configured as ``DEFAULT_RF_PLANT``. frequency - Return the default RF plant's frequency interface. + Return the masterclock's frequency interface. voltage - Return the default RF plant's total-voltage interface. + Return the masterclock's total-voltage interface. Methods ------- @@ -94,6 +96,22 @@ class RFHolder: Return an RF plant by name. add(rf) Add an RF plant to the holder. + + Notes + ----- + ``frequency`` and ``voltage`` are backward-compatible aliases for + ``masterclock.frequency`` and ``masterclock.voltage``. Do not confuse + this ``masterclock`` (the default :class:`~pyaml.rf.rf_plant.RFPlant` + object) with :attr:`~pyaml.rf.rf_plant.RFPlant.masterclock` (the + master-clock device name configured on an ``RFPlant``). + + Examples + -------- + >>> masterclock = sr.live.rf.masterclock + >>> masterclock.frequency.set(499.654e6) + >>> masterclock.voltage.set(2.5e6) + >>> same_frequency = sr.live.rf.frequency + >>> spare_rf_plant = sr.live.rf.get("SPARE_RF_PLANT") """ def __init__(self, peer: "ElementHolder"): @@ -108,15 +126,37 @@ def transmitter(self) -> RFTransmitterHolder: """Return the holder for RF transmitter elements.""" return self._rftransmitter_holder + @property + def masterclock(self) -> RFPlant: + """ + Return the RF plant configured as ``DEFAULT_RF_PLANT``. + + Returns + ------- + RFPlant + RF plant registered under ``DEFAULT_RF_PLANT``. + + Raises + ------ + PyAMLException + If no RF plant is registered under ``DEFAULT_RF_PLANT``. + + Examples + -------- + >>> masterclock = sr.live.rf.masterclock + >>> masterclock.frequency.set(499.654e6) + """ + return self.get("DEFAULT_RF_PLANT") + @property def frequency(self) -> ReadWriteFloatScalar: - """Return the default RF plant's frequency interface.""" - return self.get("DEFAULT_RF_PLANT").frequency + """Return the masterclock's frequency interface (alias for ``masterclock.frequency``).""" + return self.masterclock.frequency @property def voltage(self) -> ReadWriteFloatScalar: - """Return the default RF plant's total-voltage interface.""" - return self.get("DEFAULT_RF_PLANT").voltage + """Return the masterclock's total-voltage interface (alias for ``masterclock.voltage``).""" + return self.masterclock.voltage def get(self, name: str) -> RFPlant: """ diff --git a/tests/rf/test_rf.py b/tests/rf/test_rf.py index 1d8b5a57..d896faa1 100644 --- a/tests/rf/test_rf.py +++ b/tests/rf/test_rf.py @@ -3,6 +3,7 @@ from pyaml.accelerator import Accelerator from pyaml.common.exception import PyAMLException +from pyaml.lattice.simulator import Simulator def test_rf(): @@ -105,3 +106,46 @@ def test_rf_multi_notrans(install_test_package): # Check that frequency and voltage has been applied on the masterclock device assert np.isclose(sr.live.rf.frequency.get(), 3.523e8) + + +def test_masterclock_returns_the_default_rf_plant(): + sr: Accelerator = Accelerator.load("tests/config/EBS_rf.yaml", ignore_external=True) + + assert sr.design.rf.masterclock is sr.design.rf.get("DEFAULT_RF_PLANT") + + +def test_frequency_and_voltage_are_masterclock_aliases(): + sr: Accelerator = Accelerator.load("tests/config/EBS_rf.yaml", ignore_external=True) + + assert sr.design.rf.frequency is sr.design.rf.masterclock.frequency + assert sr.design.rf.voltage is sr.design.rf.masterclock.voltage + + sr.design.rf.masterclock.frequency.set(3.6e8) + assert sr.design.rf.frequency.get() == pytest.approx(3.6e8) + + sr.design.rf.frequency.set(3.523e8) + assert sr.design.rf.masterclock.frequency.get() == pytest.approx(3.523e8) + + +def test_masterclock_raises_when_default_rf_plant_missing(ebs_lattice_file): + holder = Simulator(name="empty", lattice=str(ebs_lattice_file)) + + with pytest.raises(PyAMLException) as exc: + _ = holder.rf.masterclock + assert "DEFAULT_RF_PLANT" in str(exc.value) + + +def test_simple_rf_access(): + sr: Accelerator = Accelerator.load("tests/config/EBS_rf.yaml", ignore_external=True) + + masterclock = sr.design.rf.masterclock + masterclock.frequency.set(499.654e6) + masterclock.voltage.set(2.5e6) + + same_frequency = sr.design.rf.frequency + assert same_frequency.get() == pytest.approx(499.654e6) + + # No second RF plant is configured in this fixture: reuse the existing + # one to check that named lookup keeps working alongside the masterclock. + default_rf_plant = sr.design.rf.get("DEFAULT_RF_PLANT") + assert default_rf_plant is masterclock