:orphan: Menlo Dual Laser Lock ===================== **From:** `Menlo Systems `_ **Class:** :py:class:`herosdevices.hardware.menlo.dual_laser_lock.DualLaserLock` **Driver Quality Index:** alpha Additional Information :bdg-warning:`Check before use` ------------------------------------------------------ .. dropdown:: Menlo OFC Setup This driver is split into one core device and several modules attached to it. :py:class:`~herosdevices.hardware.menlo.OFC` opens the single QWebChannel websocket connection to the comb and exposes its raw control/status tree via ``get_node``/``set_node``/``explore``. Each functional-layer module (:py:class:`~herosdevices.hardware.menlo.DDS`, :py:class:`~herosdevices.hardware.menlo.LaserLock`, :py:class:`~herosdevices.hardware.menlo.DualLaserLock`, :py:class:`~herosdevices.hardware.menlo.FXE`, :py:class:`~herosdevices.hardware.menlo.RepetitionRate`, :py:class:`~herosdevices.hardware.menlo.CEO`, :py:class:`~herosdevices.hardware.menlo.Oscillator`) is a separate HERO. It takes the already-running ``OFC`` HERO as a constructor argument via BOSS's ``"ofc": "$device_menlo_ofc"`` reference, so all modules share the one physical connection instead of each opening their own. See ``examples/menlo/ofc.json`` for a complete BOSS config wiring one comb with all its modules. Some CW channels lock two wavelengths through one shared lock loop, with two separate frequency-distribution blocks instead of one. Deploying plain ``LaserLock`` against one of these fails, since it hardcodes a single frequency-distribution node name that these channels don't have. Use :py:class:`~herosdevices.hardware.menlo.DualLaserLock` instead, passing both wavelengths' node names explicitly; see ``examples/menlo/ofc.json`` for an example. :py:class:`~herosdevices.hardware.menlo.FXE` wraps the comb's 16-channel frequency counter. Unlike the other modules, it has no default observables, since which channel carries which signal is deployment-specific. Pass ``observables`` for the channels relevant to your comb, e.g. a beat on channel 3: .. code-block:: json "arguments": { "ofc": "$device_ofc", "observables": { "cw_beat": {"path": "counterFrequencies.channel03", "unit": "Hz"} } } Node paths (used in ``get_node``/``set_node``/``observables``) are firmware-dependent and not documented by Menlo. Use :py:meth:`~herosdevices.hardware.menlo.OFC.format_tree` to find them interactively: .. code-block:: pycon >>> print(ofc.format_tree("functionalLayer.rrSettings", depth=2)) functionalLayer.rrSettings |-- dds | |-- ddsFrequency = 28286800 | |-- outputOn = True | `-- outputPower = 0.64 |-- mainControls | |-- fastOutput ... | |-- lock = True | `-- slowOutput ... `-- repetitionRate |-- rrCounterRepRate = 250105340.0 `-- rrTargetBeatRF = 250105340 Nodes shown as ``...`` are unexpanded branches; raise ``depth`` or call ``format_tree`` again rooted at that path to descend further. :py:meth:`~herosdevices.hardware.menlo.OFC.explore` returns the same tree as a plain dict instead of a rendered string, if you want to process it programmatically. For anything specific to your comb (e.g. a customer-specific fiber-noise-cancellation module), poll or control it directly instead of adding a new class: every module accepts an ``observables`` argument, merged on top of its ``DEFAULT_OBSERVABLES``, and ``OFC.get_node``/``OFC.set_node`` give full read/write access to any node regardless. .. code-block:: json "arguments": { "host": "IP_OR_HOSTNAME", "observables": { "fnc578_locked": {"path": "functionalLayer.fnc578Settings.mainControls.lock", "unit": ""}, "fnc1157_locked": {"path": "functionalLayer.fnc1157Settings.mainControls.lock", "unit": ""} } } .. important:: The ``OFC`` device's ``_ensure_connected`` method must be reachable from the other modules over the network. HEROS excludes underscore-prefixed methods from a ``RemoteHERO`` proxy unless marked ``force_remote``, so the ``OFC`` row in your BOSS config needs: .. code-block:: json "extra_decorators": [["_ensure_connected", "heros.inspect.force_remote"]] See ``examples/menlo/ofc.json`` for this in context. .. warning:: The PyPI package named ``pywebchannel`` is an unrelated project; installing it will not work. Install it from source instead: .. code-block:: bash pip install "pywebchannel @ git+https://github.com/MenloSystems/pywebchannel" ``pywebchannel`` is not declared as a project dependency, so this install step must be run manually wherever the Menlo driver is used: locally, in CI, and in any Docker image. In a Docker Container deployment via BOSS, use the ``BOSS_PIP_PKGS`` environment variable (see the `BOSS documentation `_) instead of extending the image: .. code-block:: yaml :caption: docker-compose.yml services: device_ofc: image: registry.gitlab.com/atomiq-project/herosdevices:latest restart: always network_mode: host volumes: - ./ofc.json:/ofc.json:ro environment: - BOSS_PIP_PKGS=pywebchannel@git+https://github.com/MenloSystems/pywebchannel command: python -m boss.starter -u file:///ofc.json --log info .................... Driver for a dual-wavelength laser-lock channel of a Menlo Systems frequency comb. Some CW channels (e.g. "CW 507 / 1014 / 1542", "CW 578 / 1157 / 1542" in Menlo's vendor software) lock two wavelengths through one shared `dds`/`mainControls`/`frequencyControl`/`amp*` node set, but have two separate `frequencyDistribution` blocks (one per wavelength) instead of a single plain `frequencyDistribution`, plus a single-wavelength thermo-electric-cooler node. Deploying plain :py:class:`~herosdevices.hardware.menlo.LaserLock` against one of these channels fails, since its `frequencyDistribution.*` properties hardcode a node name these channels don't have - use `DualLaserLock` instead, which composes both wavelength-suffixed nodes plus the TEC node explicitly. .. tab-set:: .. tab-item:: Arguments Bold arguments are mandatory. For more information on the listed arguments refer to the class documentation: :py:class:`herosdevices.hardware.menlo.dual_laser_lock.DualLaserLock` If parameters appear in this list but not in the class definition, please recursively check the linked base classes for the definition of the parameter. .. list-table:: :widths: 50 50 50 100 :header-rows: 1 * - Argument - Type - Default Value - Description * - **ofc** - **** - - The OFC HERO this dual-wavelength channel belongs to. * - **funclayer_identifier** - **** - - Identifier of the module's functional-layer settings object, e.g. "cw507_1014" for the sub-tree at `functionalLayer.cw507_1014Settings`. * - **primary_frequency_distribution_node** - **** - - Relative node name of the first wavelength's frequency-distribution block (mapped to :py:attr:`frequency_distribution`, inherited from `LaserLock`), e.g. `"frequencyDistribution507"`. * - **secondary_frequency_distribution_node** - **** - - Relative node name of the second wavelength's frequency-distribution block (mapped to :py:attr:`secondary_frequency_distribution`), e.g. `"frequencyDistribution1014"`. * - **tec_node** - **** - - Relative node name of the channel's thermo-electric-cooler block, e.g. `"spt1014Tec"`. * - amp_node - str | None - None - Relative node name of the channel's shared laser-diode current-control block (`amp*`), see :py:class:`~herosdevices.hardware.menlo.LaserLock`. * - observables - dict[str, dict[str, str]] | None - None - Additional observables to poll, merged on top of `DEFAULT_OBSERVABLES`, see :py:class:`~herosdevices.hardware.menlo.functional_layer.FunctionalLayerModule`. .. tab-item:: Example JSON for BOSS The following JSON strings can be used to start a HERO device representation of :py:class:`DualLaserLock ` using `BOSS `_. .. code-block:: json { "_id": "device_menlo_ofc_cw507_1014", "classname": "herosdevices.hardware.menlo.DualLaserLock", "arguments": { "ofc": "$device_menlo_ofc", "funclayer_identifier": "cw507_1014", "primary_frequency_distribution_node": "frequencyDistribution507", "secondary_frequency_distribution_node": "frequencyDistribution1014", "tec_node": "spt1014Tec", "amp_node": "amp507_1014" }, "datasource": { "async": false, "interval": 15 } } .. note:: This example contains a variable that :external:ref:`references another HERO ` with ``$``. :sup:`from examples/menlo/ofc.json` .. code-block:: json { "_id": "my_DualLaserLock", "classname": "herosdevices.hardware.menlo.dual_laser_lock.DualLaserLock", "arguments": { "ofc": "", "funclayer_identifier": "", "primary_frequency_distribution_node": "", "secondary_frequency_distribution_node": "", "tec_node": "", "amp_node": null, "observables": null } } :sup:`generated from signature` .. tab-item:: Inheritance .. inheritance-diagram:: herosdevices.hardware.menlo.dual_laser_lock.DualLaserLock