:orphan:

Menlo Oscillator
================

**From:** `Menlo Systems <https://www.menlosystems.com/>`_

**Class:** :py:class:`herosdevices.hardware.menlo.oscillator.Oscillator`


**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 <https://boss-eb4966.gitlab.io/getting_started.html#additional-dependencies-in-docker>`_)
      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 the main seed oscillator (fs laser) of a Menlo Systems frequency comb.

Wraps the OFC's `functionalLayer.ofcSettings` sub-tree, a fixed, singleton module (there is only one
seed oscillator per OFC, so unlike :py:class:`~herosdevices.hardware.menlo.DDS`/
:py:class:`~herosdevices.hardware.menlo.LaserLock` there is no `funclayer_identifier` to select).

`ofcSettings` also has `xps`/`fsOscillator` pump-diode current-control nodes (e.g. `xps.diode01`,
`fsOscillator.preampDiode01`), but which diodes are actually populated appears to vary by comb
configuration (one observed unit had `diode01`/`diode03`/`diode04` under both trees, with no `diode02`) -
not modeled here since it isn't confirmed to be a fixed, general shape. Poll/control specific diode nodes
for your comb via `observables`, or `ofc.get_node`/`set_node` directly.

.. 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.oscillator.Oscillator` 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**
           - **<class 'herosdevices.hardware.menlo.ofc.OFC'>**
           - 
           - The OFC HERO this oscillator belongs to.
         * - 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:`Oscillator <herosdevices.hardware.menlo.oscillator.Oscillator>` using             `BOSS <https://boss-eb4966.gitlab.io/>`_.
      
      .. code-block:: json
      
         {
             "_id": "device_menlo_ofc_oscillator",
             "classname": "herosdevices.hardware.menlo.Oscillator",
             "arguments": {
                 "ofc": "$device_menlo_ofc"
             },
             "datasource": {
                 "async": false,
                 "interval": 15
             }
         }
      .. note::
      
          This example contains a variable that     :external:ref:`references another HERO <json-remote-hero>` with ``$``.
      
      
      :sup:`from examples/menlo/ofc.json` 
      
      
      .. code-block:: json
      
         {
             "_id": "my_Oscillator",
             "classname": "herosdevices.hardware.menlo.oscillator.Oscillator",
             "arguments": {
                 "ofc": "<class 'herosdevices.hardware.menlo.ofc.OFC'>",
                 "observables": null
             }
         }
      
      :sup:`generated from signature`
   .. tab-item:: Inheritance
   
   
      .. inheritance-diagram:: herosdevices.hardware.menlo.oscillator.Oscillator
      
