-
Notifications
You must be signed in to change notification settings - Fork 15
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Create visa_dmm custom driver and updated nivisa_dmm_measurement (#439)
* Refactored VISA examples to use custom driver * fixed lint errors * Fixed black formatting issues * fixed black issues in helper.py * Updated the Readme file * fixed review comments by refactoring visa dmm py * Fixed Brad's comment on visa dmm driver * Updated error message --------- Co-authored-by: Brad Keryan <[email protected]>
- Loading branch information
1 parent
60ead5b
commit 16a5ecb
Showing
9 changed files
with
210 additions
and
209 deletions.
There are no files selected for viewing
68 changes: 0 additions & 68 deletions
68
examples/nivisa_dmm_measurement/NIInstrumentSimulatorV2_0.yaml
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,136 @@ | ||
"""Custom instrument driver for MeasurementLink NI-VISA DMM examples.""" | ||
from __future__ import annotations | ||
|
||
import pathlib | ||
import sys | ||
from enum import Enum | ||
from types import TracebackType | ||
from typing import ( | ||
TYPE_CHECKING, | ||
Optional, | ||
Type, | ||
) | ||
|
||
import pyvisa | ||
import pyvisa.resources | ||
import pyvisa.typing | ||
|
||
|
||
if TYPE_CHECKING: | ||
if sys.version_info >= (3, 11): | ||
from typing import Self | ||
else: | ||
from typing_extensions import Self | ||
|
||
|
||
# Pin map instrument type constant for VISA DMM | ||
INSTRUMENT_TYPE_VISA_DMM = "VisaDmm" | ||
|
||
_SIMULATION_YAML_PATH = pathlib.Path(__file__).resolve().parent / "_visa_dmm_sim.yaml" | ||
|
||
_RESOLUTION_DIGITS_TO_VALUE = {"3.5": 0.001, "4.5": 0.0001, "5.5": 1e-5, "6.5": 1e-6} | ||
|
||
# Supported NI-VISA DMM instrument IDs, both real and simulated, can be added here | ||
_SUPPORTED_INSTRUMENT_IDS = ["Waveform Generator Simulator", "34401"] | ||
|
||
|
||
class Function(Enum): | ||
"""Enum that represents the measurement function.""" | ||
|
||
DC_VOLTS = 0 | ||
AC_VOLTS = 1 | ||
|
||
|
||
_FUNCTION_TO_VALUE = { | ||
Function.DC_VOLTS: "VOLT:DC", | ||
Function.AC_VOLTS: "VOLT:AC", | ||
} | ||
|
||
|
||
class Session: | ||
"""An NI-VISA DMM session.""" | ||
|
||
def __init__( | ||
self, | ||
resource_name: str, | ||
id_query: bool = True, | ||
reset_device: bool = True, | ||
simulate: bool = False, | ||
) -> None: | ||
"""Open NI-VISA DMM session.""" | ||
# Create a real or simulated VISA resource manager.""" | ||
visa_library = f"{_SIMULATION_YAML_PATH}@sim" if simulate else "" | ||
resource_manager = pyvisa.ResourceManager(visa_library) | ||
|
||
session = resource_manager.open_resource( | ||
resource_name, read_termination="\n", write_termination="\n" | ||
) | ||
|
||
if not isinstance(session, pyvisa.resources.MessageBasedResource): | ||
raise TypeError("The 'session' object must be an instance of MessageBasedResource.") | ||
self._session = session | ||
|
||
if id_query: | ||
self._validate_id() | ||
|
||
if reset_device: | ||
self._reset() | ||
|
||
def close(self) -> None: | ||
"""Close the session.""" | ||
self._session.close() | ||
|
||
def __enter__(self) -> Self: | ||
"""Context management protocol. Returns self.""" | ||
return self | ||
|
||
def __exit__( | ||
self, | ||
exc_type: Optional[Type[BaseException]], | ||
exc_val: Optional[BaseException], | ||
traceback: Optional[TracebackType], | ||
) -> None: | ||
"""Context management protocol. Calls close().""" | ||
self.close() | ||
|
||
def configure_measurement_digits( | ||
self, function: Function, range: float, resolution_digits: float | ||
) -> None: | ||
"""Configure the common properties of the measurement. | ||
These properties include function, range, and resolution_digits. | ||
""" | ||
function_enum = _FUNCTION_TO_VALUE[function] | ||
resolution_value = _RESOLUTION_DIGITS_TO_VALUE[str(resolution_digits)] | ||
|
||
self._session.write("CONF:%s %.g,%.g" % (function_enum, range, resolution_value)) | ||
self._check_error() | ||
|
||
def read(self) -> float: | ||
"""Acquires a single measurement and returns the measured value.""" | ||
response = self._session.query("READ?") | ||
self._check_error() | ||
return float(response) | ||
|
||
def _check_error(self) -> None: | ||
"""Query the instrument's error queue.""" | ||
response = self._session.query("SYST:ERR?") | ||
fields = response.split(",", maxsplit=1) | ||
assert len(fields) >= 1 | ||
if int(fields[0]) != 0: | ||
raise RuntimeError("Instrument returned error %s: %s" % (fields[0], fields[1])) | ||
|
||
def _validate_id(self) -> None: | ||
"""Check the selected instrument is proper and responding..""" | ||
instrument_id = self._session.query("*IDN?") | ||
if not any(id_check in instrument_id for id_check in _SUPPORTED_INSTRUMENT_IDS): | ||
raise RuntimeError( | ||
"The ID query failed. This may mean that you selected the wrong instrument, your instrument did not respond, " | ||
f"or you are using a model that is not officially supported by this driver. Instrument ID: {instrument_id}" | ||
) | ||
|
||
def _reset(self) -> None: | ||
"""Reset the instrument to a known state.""" | ||
self._session.write("*CLS") | ||
self._session.write("*RST") | ||
self._check_error() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
spec: "1.1" | ||
devices: | ||
VisaDmm: | ||
eom: | ||
GPIB INSTR: | ||
q: "\n" | ||
r: "\n" | ||
dialogues: | ||
- q: "*CLS" | ||
- q: "*IDN?" | ||
r: "National Instruments,Waveform Generator Simulator (simulated with pyvisa-sim),00000000,2.0.1" | ||
- q: "*RST" | ||
- q: "READ?" | ||
r: "1.23456" | ||
error: | ||
error_queue: | ||
- q: 'SYST:ERR?' | ||
default: '0,No Error' | ||
command_error: '-100,Command Error' | ||
query_error: '-400,Query Error' | ||
properties: | ||
configuration: | ||
default: "VOLT:DC 5.000000,0.001000" | ||
getter: | ||
q: "CONF?" | ||
r: "{:s}" | ||
setter: | ||
q: "CONF:{:s}" | ||
|
||
resources: | ||
GPIB0::3::INSTR: | ||
device: VisaDmm |
This file was deleted.
Oops, something went wrong.
Oops, something went wrong.