.. index:: instrument driver; common behavior, instrument API, status code, ignore_bus Common instrument-driver behavior ================================= Measurement classes and :doc:`MGraph ` do not communicate with one particular instrument directly. They use the public API defined by its instrument-family base class. Every maintained driver in the same family must behave identically at that boundary. A configuration can therefore switch between a real and a virtual instrument without changing the measurement workflow. This page defines the common runtime rules. Instrument-specific commands belong to the concrete implementation; the :doc:`driver API <../../api/drivers>` provides the complete signatures. The base class defines the interface ------------------------------------ A concrete driver derives from the appropriate base class, such as ``SIGNALGENERATOR``, ``RECEIVER``, ``POWERMETER``, ``FIELDPROBE``, or ``AMPLIFIER``. The base class defines: * method names and signatures; * return shapes and status handling; * units and quantity types; * validation of shared parameters; * trigger and nonblocking fallbacks; * the INI configuration schema. A concrete driver may expose additional instrument-specific methods. It must not override an inherited public method with a different signature, return shape, or meaning. Unsupported capabilities are reported unambiguously as defined by the base class instead of being hidden behind an apparently successful substitute. Status and return values ------------------------ ``0`` denotes regular success. A negative status denotes an error. Positive values are used only when the respective instrument family explicitly defines their meaning. Query methods normally return ``(status, value)``. Depending on the base class, action methods return either ``status`` alone or a tuple such as ``(status, effective_value)``. The base-class signature is authoritative; calling code must not infer a return shape from the method name. Drivers retain their latest status in ``self.error``. MGraph additionally stores device results in ``nodes[name]['ret']`` and error details in ``nodes[name]['err']``. An original negative status remains available even when a modern driver does not implement the historical ``GetLastError()`` method. Lifecycle and bus policy ------------------------ The usual lifecycle is: #. create the driver instance; #. read the INI file with ``Init`` and open the bus when required; #. set frequency, channel, and operating state; #. trigger and read data; #. disable RF and execute ``Quit``. The constructor does not open a hardware connection. Likewise, ``MGraph.CreateDevices()`` only creates and connects Python objects. Real hardware access starts with ``Init_Devices()`` or ``Init()``. Every maintained driver uses: .. code-block:: python Init(ini=None, channel=None, ignore_bus=None) ``ignore_bus`` has three states: ``None`` Use the concrete class's ``_default_ignore_bus``. Active instruments normally open their bus; passive and virtual instruments ignore it. ``True`` Explicitly suppress bus access. Configuration may still be parsed, but instrument presets and state changes are not sent. ``False`` Explicitly request bus access where the instrument type supports it. The resolved decision is available as ``self.bus_ignored``. A concrete active driver checks it immediately after ``super().Init(...)`` and sends no commands when the bus is unavailable. Active and inactive graph instruments ------------------------------------- ``EvaluateConditions(context=...)`` updates the active state of nodes and edges. By default, MGraph operations such as ``Init_Devices()``, ``SetFreq_Devices()``, ``Trigger_Devices()``, ``Read()``, and ``Quit_Devices()`` include active instruments only. ``IgnoreInactive=False`` explicitly includes inactive instruments. For frequency-dependent graphs, evaluate conditions with an explicit context. Exactly the expected path must be active before measuring. The :doc:`DOT reference <../configuration/dot>` documents ``FREQUENCY_CONDITION_MAP`` and ``check_paths``; :doc:`path corrections ` describes the interaction with switches and S-parameters. Frequency, triggering, and measurement data ------------------------------------------- Public frequency methods use hertz. Invalid, non-finite, or unsupported values are rejected before issuing a hardware command. The precise return values of ``SetFreq`` and ``GetFreq`` follow the instrument-family base class. Measurement values are SCUQ quantities carrying units and uncertainty. ``_internal_unit`` is the fixed native instrument unit; the channel unit from the :doc:`INI file <../configuration/instrument-data>` is the unit requested by the user. Conversions use ``mpylab.tools.uconv`` centrally. ``Trigger`` starts an acquisition when supported by the instrument family. ``GetData`` performs a blocking read. ``GetDataNB(retrigger=...)`` follows the family-wide nonblocking rule and may fall back to ``GetData`` for a blocking instrument. Field probes always return their three components in ``probe_x``, ``probe_y``, ``probe_z`` order; mapping to cell coordinates happens outside the instrument driver. Safety and shutdown ------------------- ``RFOff`` is a safety function for RF sources and remains available independently of UI state. ``Quit`` first moves the instrument to a safe state and then releases communication resources. An error while shutting down must not prevent the attempt to close the bus. Instrument limits such as ``MAXIN`` are read as quantities from INI/DAT data. MGraph applies them through ``AmplifierProtect`` and ``MaxSafeLevel`` across all active paths. A protection limit must never be exceeded silently. Field probes may additionally provide ``GetBatteryState()``. It returns ``(status, state)``; ``None`` means that no state is available, while a negative state reports a low battery. During migration, MGraph also accepts the legacy spelling ``getBatteryState``. Virtual and passive instruments ------------------------------- A virtual driver follows the same public contract as its real counterpart. It may derive values from another virtual graph instrument, for example power or field strength from a signal-generator level. ``MGraph.CreateDevices()`` binds such sources within the graph; state must not leak between independent graph instances. Passive instruments such as cables, attenuators, and antennas do not open a bus. They read frequency-dependent data from DAT files and return path corrections as SCUQ quantities. A complete virtual graph therefore checks not only individual drivers but also conditions, path selection, units, and coupled measurement values. Testing concrete drivers ------------------------ Every instrument family has contract tests. Add a new concrete driver to the corresponding test and verify at least: * identical public signatures; * correct return shapes, units, and channel ordering; * validation before hardware access; * initialization with and without a bus; * safe error propagation and ``Quit``; * instance-local state; * trigger and ``GetDataNB`` behavior. Follow these with protocol tests, a virtual MGraph integration test, and only then a staged hardware test. See :doc:`testing and commissioning ` for the complete sequence. Shared physical instruments must additionally follow the :doc:`multichannel model `.