Source code for photons.equipment.dmm_344xxA

"""
Keysight 344(60|61|65|70)A digital multimeter.
"""
import re

from msl.equipment import EquipmentRecord

from .base import equipment
from .dmm import DMM
from .dmm import Samples
from .dmm import Settings
from .dmm import Trigger

_info_regex: re.Pattern[str] = re.compile(
    r':FUNC "(?P<FUNCTION>[A-Z]+)".*'
    r':TRIG:SOUR (?P<TRIGGER_SOURCE>[A-Z]+).*'
    r':TRIG:COUN (?P<TRIGGER_COUNT>\+\d\.\d+E[-+]\d+).*'
    r':TRIG:DEL (?P<TRIGGER_DELAY>\+\d\.\d+E[-+]\d+).*'
    r':TRIG:DEL:AUTO (?P<TRIGGER_AUTO_DELAY>\d).*'
    r':TRIG:SLOP (?P<TRIGGER_EDGE>[A-Z]+).*'
    r':SAMP:COUN \+(?P<NSAMPLES>\d+).*'
    r':CURR:NPLC (?P<DCI_NPLC>\+\d\.\d+E[-+]\d+).*'
    r':CURR:RANG (?P<DCI_RANGE>\+\d\.\d+E[-+]\d+).*'
    r':CURR:RANG:AUTO (?P<DCI_AUTO_RANGE>\d).*'
    r':CURR:ZERO:AUTO (?P<DCI_AUTO_ZERO>\d).*'
    r':VOLT:NPLC (?P<DCV_NPLC>\+\d\.\d+E[-+]\d+).*'
    r':VOLT:RANG (?P<DCV_RANGE>\+\d\.\d+E[-+]\d+).*'
    r':VOLT:RANG:AUTO (?P<DCV_AUTO_RANGE>\d).*'
    r':VOLT:ZERO:AUTO (?P<DCV_AUTO_ZERO>\d).*'
)


[docs] @equipment(manufacturer=r'Keysight', model=r'344(60|61|65|70)A') class Keysight344XXA(DMM): def __init__(self, record: EquipmentRecord, **kwargs) -> None: """Keysight 344(60|61|65|70)A digital multimeter. Args: record: The equipment record. **kwargs: Keyword arguments. Can be specified as attributes of an XML element in a configuration file (with the tag of the element equal to the alias of `record`). """ if record.model in ('34465A', '34470A'): # these models support the ":FORMAT:DATA REAL" command self._fetch_kwargs = {'fmt': 'ieee', 'dtype': '>d'} else: self._fetch_kwargs = {} super().__init__(record, **kwargs) # these must come after super() self._initiate_cmd: str = 'INITIATE' self._trigger_cmd: str = '*TRG'
[docs] def check_errors(self) -> None: """Query the error queue. Raises an exception if there is an error. """ message = self.connection.query(':SYSTEM:ERROR:NEXT?') if not message.startswith('+0,'): self.raise_exception(message)
[docs] def configure(self, *, function: DMM.Function | str = DMM.Function.DCV, range: DMM.Range | str | float = 10, # noqa: Shadows built-in name 'range' nsamples: int = 10, nplc: float = 10, auto_zero: DMM.Auto | bool | int | str = DMM.Auto.ON, trigger: DMM.Mode | str = DMM.Mode.IMMEDIATE, edge: DMM.Edge | str = DMM.Edge.FALLING, ntriggers: int = 1, delay: float = None) -> Settings: """Configure the digital multimeter. Args: function: The measurement function. range: The range to use for the measurement. nsamples: The number of samples to acquire after a trigger event. nplc: The number of power-line cycles. auto_zero: The auto-zero mode. trigger: The trigger mode. edge: The edge to trigger on. ntriggers: The number of triggers that are accepted before returning to the wait-for-trigger state. delay: The number of seconds to wait after a trigger event before acquiring samples. If None, then the auto-delay feature is enabled where the digital multimeter automatically determines the delay based on the function, range and NPLC. Returns: The result of :meth:`.settings` after applying the configuration. """ auto_zero = self.Auto(auto_zero) trigger = self.Mode(trigger) delay = ':AUTO ON' if delay is None else f' {delay}' range_ = self._get_range(range) function = self.Function(function) if function == self.Function.DCV: function = 'VOLTAGE:DC' elif function == self.Function.DCI: function = 'CURRENT:DC' else: self.raise_exception(f'Unhandled function {function!r}') self._zero_once_cmd = f'{function}:ZERO:AUTO ONCE' edge = self.Edge(edge) if edge == self.Edge.RISING: edge = 'POSITIVE' elif edge == self.Edge.FALLING: edge = 'NEGATIVE' else: self.raise_exception(f'Unsupported trigger edge {edge!r}') fmt = ':FORMAT:DATA REAL;' if self._fetch_kwargs else '' return self._configure( f':CONFIGURE:{function} {range_};' f':SENSE:{function}:NPLC {nplc};' f':SENSE:{function}:ZERO:AUTO {auto_zero};' f':SAMPLE:COUNT {nsamples};' f':TRIGGER:SOURCE {trigger};SLOPE {edge};COUNT {ntriggers};DELAY{delay};' f'{fmt}' )
[docs] def fetch(self, initiate: bool = False) -> Samples: """Fetch the samples. Args: initiate: Whether to call :meth:`.initiate` before fetching the data. """ if initiate: self.initiate() self.logger.info(f'fetch {self.alias!r}') samples = self.connection.query('FETCH?', **self._fetch_kwargs) return self._average_and_emit(samples)
[docs] def settings(self) -> Settings: """Returns the configuration settings of the digital multimeter.""" match = _info_regex.search(self.connection.query('*LRN?')) if not match: self.raise_exception(f'invalid regex pattern for {self.alias!r}') function = self.Function(match['FUNCTION']) return Settings( auto_range=match[f'{function}_AUTO_RANGE'], auto_zero=match[f'{function}_AUTO_ZERO'], function=function, nplc=match[f'{function}_NPLC'], nsamples=match['NSAMPLES'], range=match[f'{function}_RANGE'], trigger=Trigger( auto_delay=match['TRIGGER_AUTO_DELAY'] == '1', count=match['TRIGGER_COUNT'], delay=match['TRIGGER_DELAY'], edge=match['TRIGGER_EDGE'], mode=match['TRIGGER_SOURCE'], ) )