Source code for instro.dmm.drivers.virtual

"""Virtual DMM driver: readings simulated in-process, with no transport, server, or hardware."""

from __future__ import annotations

from instro.dmm import DMMDriverBase
from instro.dmm.scpi_sim_server import (
    DEFAULT_STIMULUS,
    FUNCTION_AC_CURRENT,
    FUNCTION_AC_VOLTAGE,
    FUNCTION_DC_CURRENT,
    FUNCTION_DC_VOLTAGE,
    FUNCTION_RESISTANCE,
    NOISE_PERCENT,
    add_noise,
)
from instro.dmm.types import MeasurementFunction

# the SCPI simulator's defaults, so both simulated DMMs read the same values
_DEFAULT_STIMULUS = {
    MeasurementFunction.DC_VOLTAGE: DEFAULT_STIMULUS[FUNCTION_DC_VOLTAGE],
    MeasurementFunction.AC_VOLTAGE: DEFAULT_STIMULUS[FUNCTION_AC_VOLTAGE],
    MeasurementFunction.DC_CURRENT: DEFAULT_STIMULUS[FUNCTION_DC_CURRENT],
    MeasurementFunction.AC_CURRENT: DEFAULT_STIMULUS[FUNCTION_AC_CURRENT],
    MeasurementFunction.TWO_WIRE_RESISTANCE: DEFAULT_STIMULUS[FUNCTION_RESISTANCE],
    MeasurementFunction.FOUR_WIRE_RESISTANCE: DEFAULT_STIMULUS[FUNCTION_RESISTANCE],
}


[docs] class VirtualDMM(DMMDriverBase): """DMM simulated in-process: no transport, server, or hardware. Each function reads its stimulus value plus noise (0.5%, the SCPI simulator's level). Range, NPLC, digits, and aperture settings are accepted and have no effect on readings, so any `InstroDMM` call works. To exercise the VISA transport as well, use `SimulatedDMM` with the bundled SCPI simulator. """ def __init__(self, stimulus: dict[MeasurementFunction, float] | None = None) -> None: """Create a virtual DMM. Args: stimulus (dict[MeasurementFunction, float] | None): The value each function measures before noise, in volts, amperes, or ohms. Functions left out keep their defaults (1.25 V DC, 0.5 V AC, 0.1 A DC, 0.05 A AC, 1000 ohms). Example:: from instro.dmm import InstroDMM, MeasurementFunction from instro.dmm.drivers import VirtualDMM dmm = InstroDMM(name="dmm", driver=VirtualDMM({MeasurementFunction.DC_VOLTAGE: 3.3})) with dmm: print(dmm.read_dc_voltage().latest) # about 3.3 """ self.stimulus: dict[MeasurementFunction, float] = {**_DEFAULT_STIMULUS, **(stimulus or {})}
[docs] def open(self) -> None: """Nothing to connect to."""
[docs] def close(self) -> None: """Nothing to disconnect."""
[docs] def set_measurement_function(self, function: MeasurementFunction) -> None: """Accept any function; `InstroDMM` calls the matching ``measure_*`` method to read it."""
[docs] def set_digits(self, n: int) -> None: """Accepted; no effect on readings."""
[docs] def set_aperture_seconds(self, seconds: float) -> None: """Accepted; no effect on readings."""
[docs] def set_dc_voltage_range(self, value: float | None) -> None: """Accepted; no effect on readings."""
[docs] def set_ac_voltage_range(self, value: float | None) -> None: """Accepted; no effect on readings."""
[docs] def set_dc_current_range(self, value: float | None) -> None: """Accepted; no effect on readings."""
[docs] def set_ac_current_range(self, value: float | None) -> None: """Accepted; no effect on readings."""
[docs] def set_two_wire_resistance_range(self, value: float | None) -> None: """Accepted; no effect on readings."""
[docs] def set_four_wire_resistance_range(self, value: float | None) -> None: """Accepted; no effect on readings."""
[docs] def set_dc_voltage_nplc(self, nplc: float) -> None: """Accepted; no effect on readings."""
[docs] def set_ac_voltage_nplc(self, nplc: float) -> None: """Accepted; no effect on readings."""
[docs] def set_dc_current_nplc(self, nplc: float) -> None: """Accepted; no effect on readings."""
[docs] def set_ac_current_nplc(self, nplc: float) -> None: """Accepted; no effect on readings."""
[docs] def set_two_wire_resistance_nplc(self, nplc: float) -> None: """Accepted; no effect on readings."""
[docs] def set_four_wire_resistance_nplc(self, nplc: float) -> None: """Accepted; no effect on readings."""
[docs] def measure_dc_voltage(self) -> float: return self._read(MeasurementFunction.DC_VOLTAGE)
[docs] def measure_ac_voltage(self) -> float: return self._read(MeasurementFunction.AC_VOLTAGE)
[docs] def measure_dc_current(self) -> float: return self._read(MeasurementFunction.DC_CURRENT)
[docs] def measure_ac_current(self) -> float: return self._read(MeasurementFunction.AC_CURRENT)
[docs] def measure_resistance(self) -> float: return self._read(MeasurementFunction.TWO_WIRE_RESISTANCE)
[docs] def measure_four_wire_resistance(self) -> float: return self._read(MeasurementFunction.FOUR_WIRE_RESISTANCE)
def _read(self, function: MeasurementFunction) -> float: return add_noise(self.stimulus[function], NOISE_PERCENT)