Developing instrument drivers ============================= An instrument driver translates the public API of an instrument class into the protocol of a concrete device. Communication details and domain measurement workflows remain outside the specific driver. See :doc:`common instrument-driver behavior ` for the normative runtime rules covering status codes, return shapes, initialization, bus policy, quantities, and safety. This page focuses on implementing a concrete driver. Choose the correct base class ----------------------------- A power meter derives from ``POWERMETER``, a receiver from ``RECEIVER``, and a signal generator from ``SIGNALGENERATOR``. These base classes define method names, the unit contract, configuration schema, and shared fallbacks. Derive directly from ``DRIVER`` only when introducing a new instrument class. .. code-block:: python from mpylab.device.powermeter import POWERMETER as BasePowerMeter class POWERMETER(BasePowerMeter): def __init__(self, SearchPaths=None): super().__init__(SearchPaths=SearchPaths) self._internal_unit = "dBm" Return shape and signature always follow the base class. Queries commonly return ``(status, value)``; action methods may return ``status`` alone. Measurement values are SCUQ quantities expressed in the requested channel unit. Initialization -------------- All maintained drivers use the signature ``Init(ini=None, channel=None, ignore_bus=None)``. ``None`` selects the driver-specific default: active instruments normally open their bus, while purely passive and virtual drivers set ``_default_ignore_bus = True``. Explicit ``True`` or ``False`` overrides that default. ``super().Init(ini, channel, ignore_bus=ignore_bus)`` reads the INI file, resolves the bus policy, and establishes the communication class when needed. The resolved policy is available as ``self.bus_ignored``. A concrete active driver returns on ``self.error`` or ``self.bus_ignored`` before sending presets, mode changes, or other instrument commands. A failed mode or range change must not silently be treated as success. Communication ------------- ``DRIVER`` provides ``write``, ``read``, and ``query`` through a communication class. This unifies GPIB, VISA, Prologix, and debug/virtual operation. Access ``self.dev`` directly only when the shared interface cannot represent a required operation. Consume replies completely. Multiline or additionally terminated replies need an explicit drain strategy so residual data cannot corrupt the next query. Units ----- ``_internal_unit`` is the fixed unit in which the device accepts or returns values. The channel unit from the INI file is the unit requested by the user. Use ``mpylab.tools.uconv`` for conversion instead of local dB formulas. Virtual driver and tests ------------------------ A virtual driver exposing the same public API is useful for new instrument classes and complex APIs. Tests cover at least: * initialization without hardware; * command formatting and response parsing; * units and uncertainty; * invalid parameters and instrument status; * multichannel instances without shared mutable state; * trigger, nonblocking, and timeout behavior; * safe ``Quit`` and RF-off where applicable. Inline smoke tests and example INI files help with later hardware testing but do not replace automated tests. Drivers exposing several logical graph nodes through one physical device must additionally follow the :doc:`multichannel controller and facade model `.