Instrument architecture and drivers¶
The instrument subsystem is built around composition and discovery:
stoner_measurement.instruments.base_instrument.BaseInstrumentcomposes a transport and a protocol.stoner_measurement.instruments.driver_manager.InstrumentDriverManagerdiscovers concrete driver classes.
BaseInstrument composition model¶
Each instrument instance contains:
a transport object (byte-level I/O and connection lifecycle), and
a protocol object (command/query formatting, response parsing, and error checks).
This keeps instrument behaviour independent from physical connection details. A single driver can be reused with different transports (for example serial, Ethernet, GPIB, or null transport for tests) as long as the protocol matches the instrument command set.
Base instrument class providing the composition of transport and protocol.
BaseInstrument is the foundation of the instrument hierarchy. Every
concrete instrument class is a (possibly indirect) subclass of
BaseInstrument and gains the ability to communicate with a physical
instrument by holding references to a BaseTransport and a
BaseProtocol instance.
- class stoner_measurement.instruments.base_instrument.BaseInstrument(transport: BaseTransport, protocol: BaseProtocol, *, auto_check_errors: bool = False)[source]¶
Bases:
ABCBase class for all instrument drivers.
Uses a composition pattern: each instrument instance holds a transport responsible for the physical byte-level communication, and a protocol responsible for formatting commands and parsing responses. This design allows any transport/protocol combination to be substituted without changing the instrument driver code.
Error checking is performed on demand via
check_for_errors()and can be enabled automatically for everywrite()andquery()call by settingauto_check_errorstoTrue.Two strategies are supported transparently, chosen based on the protocol:
Response-embedded (Oxford, Lakeshore) —
check_for_errors()inspects the last query response for an inline error token.Error-queue (SCPI) —
check_for_errors()polls the instrument’s error queue. If the transport supports an out-of-band status byte (read_status_byte(), e.g. GPIB serial poll), only the ESB bit is checked first to avoid an unnecessary round-trip when no error is pending.
- Attributes:
- transport (BaseTransport):
Transport layer instance (serial, Ethernet, GPIB, …).
- protocol (BaseProtocol):
Protocol instance (SCPI, Oxford, Lakeshore, …).
- auto_check_errors (bool):
When
True,write()andquery()automatically callcheck_for_errors()after each operation. Defaults toFalse.
- Examples:
>>> from stoner_measurement.instruments.transport import NullTransport >>> from stoner_measurement.instruments.protocol import ScpiProtocol >>> from stoner_measurement.instruments.base_instrument import BaseInstrument >>> t = NullTransport(responses=[b"ACME,Model1,SN001,v1.0\n"]) >>> instr = BaseInstrument(transport=t, protocol=ScpiProtocol()) >>> instr.connect() >>> instr.is_connected True >>> instr.query("*IDN?") 'ACME,Model1,SN001,v1.0' >>> instr.disconnect() >>> instr.is_connected False
- check_for_errors(*, command: str | None = None) None[source]¶
Poll the instrument for errors and raise if one is found.
The strategy depends on the protocol and transport combination:
If the protocol uses response-embedded errors (
errors_in_responseisTrue), this method is a no-op — errors are detected inline byquery().If the transport supports an out-of-band status byte (
read_status_byte()returns a non-Nonevalue), the IEEE 488.2 Event Status Bit (ESB, bit 2) is checked. If it is clear, no error query is sent.Otherwise (or if the ESB bit is set) the protocol’s
error_querycommand is sent andcheck_error()is called on the response.
- Keyword Parameters:
- command (str | None):
The command that preceded this check, used to populate the
commandfield of any raised exception. Defaults toNone.
- Raises:
- InstrumentError:
If the instrument reports an error.
- Examples:
>>> from stoner_measurement.instruments.transport import NullTransport >>> from stoner_measurement.instruments.protocol import ScpiProtocol >>> from stoner_measurement.instruments.base_instrument import BaseInstrument >>> t = NullTransport(responses=[b'+0,"No error"\n']) >>> instr = BaseInstrument(t, ScpiProtocol()) >>> instr.connect() >>> instr.check_for_errors() # no exception — queue is clear >>> instr.disconnect()
- connect() None[source]¶
Open the transport connection to the instrument.
- Raises:
- ConnectionError:
If the underlying transport cannot be opened.
- Examples:
>>> from stoner_measurement.instruments.transport import NullTransport >>> from stoner_measurement.instruments.protocol import ScpiProtocol >>> from stoner_measurement.instruments.base_instrument import BaseInstrument >>> instr = BaseInstrument(NullTransport(), ScpiProtocol()) >>> instr.connect() >>> instr.is_connected True >>> instr.disconnect()
- disconnect() None[source]¶
Close the transport connection to the instrument.
- Examples:
>>> from stoner_measurement.instruments.transport import NullTransport >>> from stoner_measurement.instruments.protocol import ScpiProtocol >>> from stoner_measurement.instruments.base_instrument import BaseInstrument >>> instr = BaseInstrument(NullTransport(), ScpiProtocol()) >>> instr.connect() >>> instr.disconnect() >>> instr.is_connected False
- identify() str[source]¶
Return the instrument identification string (
*IDN?).Sends the standard IEEE 488.2
*IDN?query and returns the response. Instruments that do not support*IDN?should override this method.- Returns:
- (str):
Identification string returned by the instrument.
- Raises:
- ConnectionError:
If the transport is not open.
- Examples:
>>> from stoner_measurement.instruments.transport import NullTransport >>> from stoner_measurement.instruments.protocol import ScpiProtocol >>> from stoner_measurement.instruments.base_instrument import BaseInstrument >>> t = NullTransport(responses=[b"ACME,Model1,SN001,v1.0\n"]) >>> instr = BaseInstrument(t, ScpiProtocol()) >>> instr.connect() >>> instr.identify() 'ACME,Model1,SN001,v1.0' >>> instr.disconnect()
- property is_connected: bool¶
Trueif the underlying transport is currently open.- Returns:
- (bool):
Connection state of the transport.
- Examples:
>>> from stoner_measurement.instruments.transport import NullTransport >>> from stoner_measurement.instruments.protocol import ScpiProtocol >>> from stoner_measurement.instruments.base_instrument import BaseInstrument >>> instr = BaseInstrument(NullTransport(), ScpiProtocol()) >>> instr.is_connected False
- query(command: str) str[source]¶
Send a query and return the instrument’s response.
Combines
write()andread()into a single call. The query is formatted byprotocolbefore transmission.If
auto_check_errorsisTrue:For protocols with response-embedded errors (Oxford, Lakeshore),
check_error()is called on the parsed response directly.For error-queue protocols (SCPI),
check_for_errors()is called after the response is returned.
- Args:
- command (str):
Query string in the instrument’s command language.
- Returns:
- (str):
Parsed response string.
- Raises:
- ConnectionError:
If the transport is not open.
- TimeoutError:
If no response is received within the transport timeout.
- InstrumentError:
If
auto_check_errorsisTrueand the instrument signals an error.
- Examples:
>>> from stoner_measurement.instruments.transport import NullTransport >>> from stoner_measurement.instruments.protocol import ScpiProtocol >>> from stoner_measurement.instruments.base_instrument import BaseInstrument >>> t = NullTransport(responses=[b"ACME,1,SN,v1\n"]) >>> instr = BaseInstrument(t, ScpiProtocol()) >>> instr.connect() >>> instr.query("*IDN?") 'ACME,1,SN,v1' >>> instr.disconnect()
- read() str[source]¶
Read a response from the instrument.
Reads until the protocol’s terminator character is received and parses the raw bytes using
protocol.- Returns:
- (str):
Parsed response string.
- Raises:
- ConnectionError:
If the transport is not open.
- TimeoutError:
If no data is received within the transport timeout.
- Examples:
>>> from stoner_measurement.instruments.transport import NullTransport >>> from stoner_measurement.instruments.protocol import ScpiProtocol >>> from stoner_measurement.instruments.base_instrument import BaseInstrument >>> t = NullTransport(responses=[b"+1.234\n"]) >>> instr = BaseInstrument(t, ScpiProtocol()) >>> instr.connect() >>> instr.read() '+1.234' >>> instr.disconnect()
- reset() None[source]¶
Send the standard IEEE 488.2 reset command (
*RST).Instruments that do not support
*RSTshould override this method.- Raises:
- ConnectionError:
If the transport is not open.
- Examples:
>>> from stoner_measurement.instruments.transport import NullTransport >>> from stoner_measurement.instruments.protocol import ScpiProtocol >>> from stoner_measurement.instruments.base_instrument import BaseInstrument >>> t = NullTransport() >>> instr = BaseInstrument(t, ScpiProtocol()) >>> instr.connect() >>> instr.reset() >>> t.write_log [b'*RST\n'] >>> instr.disconnect()
- write(command: str) None[source]¶
Send a command to the instrument without expecting a response.
The command is formatted by
protocolbefore being passed totransport. Ifauto_check_errorsisTrueand the protocol uses an error queue (not response-embedded errors),check_for_errors()is called after the command is sent.- Args:
- command (str):
Command string in the instrument’s command language.
- Raises:
- ConnectionError:
If the transport is not open.
- InstrumentError:
If
auto_check_errorsisTrueand the instrument reports an error after processing the command.
- Examples:
>>> from stoner_measurement.instruments.transport import NullTransport >>> from stoner_measurement.instruments.protocol import ScpiProtocol >>> from stoner_measurement.instruments.base_instrument import BaseInstrument >>> t = NullTransport() >>> instr = BaseInstrument(t, ScpiProtocol()) >>> instr.connect() >>> instr.write("OUTP ON") >>> t.write_log [b'OUTP ON\n'] >>> instr.disconnect()
Transport layer classes for instrument communications.
Provides an abstract BaseTransport interface and concrete implementations
for serial (RS-232/RS-485), Ethernet (TCP/IP socket), UDP, and GPIB connections,
plus a NullTransport suitable for simulation and unit tests.
- class stoner_measurement.instruments.transport.BaseTransport(timeout: float = 2.0)[source]¶
Bases:
ABCAbstract base for all instrument transport layers.
A transport is responsible for the physical transmission and reception of raw bytes over a specific interface (serial port, Ethernet socket, GPIB bus, etc.). Higher-level protocol formatting is handled separately by
BaseProtocol.- Attributes:
- timeout (float):
Read timeout in seconds. A value of
0means non-blocking andNonemeans block indefinitely.
- Examples:
>>> from stoner_measurement.instruments.transport import NullTransport >>> t = NullTransport() >>> t.is_open False >>> t.open() >>> t.is_open True >>> t.close() >>> t.is_open False
- flush() None[source]¶
Flush any pending data in the transport buffers.
The default implementation is a no-op; override in subclasses where a hardware flush operation is meaningful.
- classmethod from_uri(uri: str) BaseTransport[source]¶
Construct the appropriate transport instance from a URI or VISA resource string.
Two input formats are recognised:
URI strings use a
scheme://...format:serial:///dev/ttyUSB0?baud_rate=9600—SerialTransport. On Windows useserial://COM3?baud_rate=9600. Query parameters mirror theSerialTransportconstructor keyword arguments:baud_rate(aliasbaud),data_bits,stop_bits,parity,timeout.tcp://host:port?timeout=2.0—EthernetTransport.udp://host:port?timeout=2.0—UdpTransport.gpib://board:address/?timeout=2.0orgpib://address/?timeout=2.0—GpibTransport. When only one component is present it is treated as the address; the board defaults to0.
VISA resource strings follow the IVI/VISA convention and do not contain
://:GPIB[N]::address::INSTR—GpibTransport.TCPIP[N]::host::port::SOCKET—EthernetTransport.TCPIP[N]::host::INSTR—EthernetTransporton the default SCPI port5025.ASRL<port>::INSTR—SerialTransport. The port name is everything afterASRLand before::INSTR, e.g.ASRL/dev/ttyUSB0::INSTRorASRLCOM3::INSTR.
- Args:
- uri (str):
URI string or VISA resource string describing the transport.
- Returns:
- (BaseTransport):
A concrete transport instance corresponding to uri.
- Raises:
- ValueError:
If the URI scheme or VISA resource type is not supported, or if required components (host, port, address) are missing.
- Examples:
>>> from stoner_measurement.instruments.transport import BaseTransport, SerialTransport >>> t = BaseTransport.from_uri("serial:///dev/ttyUSB0?baud_rate=9600") >>> isinstance(t, SerialTransport) True >>> t.port '/dev/ttyUSB0' >>> t.baud_rate 9600 >>> from stoner_measurement.instruments.transport import EthernetTransport >>> t = BaseTransport.from_uri("tcp://192.168.1.100:5025") >>> isinstance(t, EthernetTransport) True >>> t.host '192.168.1.100' >>> t.port 5025 >>> from stoner_measurement.instruments.transport import GpibTransport >>> t = BaseTransport.from_uri("GPIB0::22::INSTR") >>> isinstance(t, GpibTransport) True >>> t.address 22 >>> t.board 0
- abstractmethod open() None[source]¶
Open the physical connection to the instrument.
- Raises:
- ConnectionError:
If the connection cannot be established.
- abstractmethod read(num_bytes: int = 4096) bytes[source]¶
Read up to num_bytes from the instrument.
- Args:
- num_bytes (int):
Maximum number of bytes to read. Defaults to
4096.
- Returns:
- (bytes):
The bytes received from the instrument.
- Raises:
- ConnectionError:
If the transport is not open.
- TimeoutError:
If no data is received within
timeoutseconds.
- read_status_byte() int | None[source]¶
Return the instrument status byte via an out-of-band mechanism.
Some transports (notably GPIB) can perform a serial poll to read the IEEE 488.2 status byte (STB) without sending a command, allowing the caller to check whether an error condition exists before issuing an expensive error-queue query.
The default implementation returns
None, indicating that no out-of-band mechanism is available. Subclasses that support hardware-level status polling (e.g.GpibTransport) should override this method.If
read_status_byte()returnsNone,check_for_errors()will fall back to issuing the protocol’serror_querycommand directly.- Returns:
- (int | None):
The 8-bit status byte, or
Noneif not supported.
- Examples:
>>> from stoner_measurement.instruments.transport import NullTransport >>> NullTransport().read_status_byte() is None True
- read_until(terminator: bytes = b'\n') bytes[source]¶
Read bytes from the instrument until terminator is encountered.
The default implementation calls
read()one byte at a time. Subclasses may override this with a more efficient implementation.- Args:
- terminator (bytes):
Byte sequence that marks the end of a response. Defaults to
b"\n".
- Returns:
- (bytes):
All bytes received up to and including the terminator.
- Raises:
- ConnectionError:
If the transport is not open.
- TimeoutError:
If no data is received within
timeoutseconds.
- Examples:
>>> from stoner_measurement.instruments.transport import NullTransport >>> t = NullTransport(responses=[b"hello\n"]) >>> t.open() >>> t.read_until(b"\n") b'hello\n' >>> t.close()
- property transport_address: str¶
Human-readable address string identifying the remote endpoint.
Subclasses override this to expose the connection address in a format appropriate to the transport type (e.g.
"192.168.1.100:5025"for TCP,"GPIB0::22::INSTR"for GPIB,"/dev/ttyUSB0"for serial).The default implementation returns an empty string, which is used by
NullTransportand any transport that does not carry an addressable endpoint.- Returns:
- (str):
Address string, or
""if not applicable.
- Examples:
>>> from stoner_measurement.instruments.transport import NullTransport >>> NullTransport().transport_address ''
- class stoner_measurement.instruments.transport.EthernetTransport(host: str, port: int, timeout: float = 2.0)[source]¶
Bases:
BaseTransportTCP/IP socket transport for network-connected instruments.
- Attributes:
- host (str):
Hostname or IP address of the instrument.
- port (int):
TCP port number (most instruments use 5025 for SCPI).
- timeout (float):
Socket read timeout in seconds. Defaults to
2.0.
- Examples:
>>> from stoner_measurement.instruments.transport import EthernetTransport >>> t = EthernetTransport(host="192.168.1.100", port=5025) >>> t.host '192.168.1.100' >>> t.port 5025
- flush() None[source]¶
Drain any unread data from the socket receive buffer.
Temporarily sets the socket to non-blocking mode and discards any data already in the receive buffer.
- open() None[source]¶
Connect to the instrument over TCP.
- Raises:
- ConnectionError:
If the TCP connection cannot be established.
- read(num_bytes: int = 4096) bytes[source]¶
Receive up to num_bytes from the instrument.
- Args:
- num_bytes (int):
Maximum number of bytes to read. Defaults to
4096.
- Returns:
- (bytes):
Bytes received from the instrument.
- Raises:
- ConnectionError:
If the socket is not open.
- TimeoutError:
If no data arrives within
timeoutseconds.
- property transport_address: str¶
Return the remote endpoint as
"<host>:<port>".- Returns:
- (str):
Ethernet address string, e.g.
"192.168.1.100:5025".
- Examples:
>>> from stoner_measurement.instruments.transport import EthernetTransport >>> EthernetTransport(host="192.168.1.100", port=5025).transport_address '192.168.1.100:5025'
- class stoner_measurement.instruments.transport.GpibTransport(address: int, board: int = 0, timeout: float = 2.0)[source]¶
Bases:
BaseTransportGPIB transport using PyVISA.
Wraps a PyVISA
GPIBInstrumentresource to provide the standardBaseTransportinterface. The VISA resource string is constructed from address and board as"GPIB<board>::<address>::INSTR".- Attributes:
- address (int):
GPIB primary address of the instrument (0–30).
- board (int):
GPIB board (interface) index. Defaults to
0.- timeout (float):
Read timeout in seconds. Defaults to
2.0.
- Examples:
>>> from stoner_measurement.instruments.transport import GpibTransport >>> t = GpibTransport(address=22) >>> t.address 22 >>> t.board 0 >>> t.resource_string 'GPIB0::22::INSTR'
- open() None[source]¶
Open the GPIB resource via PyVISA.
- Raises:
- ConnectionError:
If the resource cannot be opened.
- read(num_bytes: int = 4096) bytes[source]¶
Read up to num_bytes from the GPIB instrument.
- Args:
- num_bytes (int):
Maximum number of bytes to read. Defaults to
4096.
- Returns:
- (bytes):
Bytes received.
- Raises:
- ConnectionError:
If the transport is not open.
- TimeoutError:
If no data arrives within
timeoutseconds.
- read_status_byte() int | None[source]¶
Return the IEEE 488.2 status byte via a GPIB serial poll.
Performs an out-of-band serial poll (PyVISA
read_stb()) without transmitting any command to the instrument. This is the preferred mechanism for checking the GPIB status byte because it does not consume a response from the instrument’s output buffer.- Returns:
- (int | None):
The 8-bit status byte, or
Noneif the transport is not currently open.
- Examples:
>>> from stoner_measurement.instruments.transport import GpibTransport >>> t = GpibTransport(address=22) >>> t.read_status_byte() is None # not open True
- property resource_string: str¶
VISA resource string for this GPIB address.
- Returns:
- (str):
Resource string in the form
"GPIB<board>::<address>::INSTR".
- Examples:
>>> from stoner_measurement.instruments.transport import GpibTransport >>> GpibTransport(address=14, board=1).resource_string 'GPIB1::14::INSTR'
- class stoner_measurement.instruments.transport.NullTransport(responses: list[bytes] | None = None, timeout: float = 2.0)[source]¶
Bases:
BaseTransportLoopback transport for simulation and unit testing.
write()appends bytes towrite_log.read()returns the next entry from the responses queue provided at construction time. When the queue is exhaustedread()returnsb"".- Attributes:
- write_log (list[bytes]):
All data written via
write()since the transport was created.- timeout (float):
Nominal timeout (not actually enforced). Defaults to
2.0.
- Examples:
>>> from stoner_measurement.instruments.transport import NullTransport >>> t = NullTransport(responses=[b"+0.123\n"]) >>> t.open() >>> t.write(b"MEAS:VOLT?\n") >>> t.read_until(b"\n") b'+0.123\n' >>> t.write_log [b'MEAS:VOLT?\n'] >>> t.close()
- clear_log() None[source]¶
Clear the write log and any pending responses.
- Examples:
>>> from stoner_measurement.instruments.transport import NullTransport >>> t = NullTransport(responses=[b"data\n"]) >>> t.open() >>> t.write(b"CMD\n") >>> t.clear_log() >>> t.write_log [] >>> t.close()
- queue_response(response: bytes) None[source]¶
Append response to the end of the response queue.
Useful when building up a sequence of expected replies during a test.
- Args:
- response (bytes):
Bytes to add to the response queue.
- Examples:
>>> from stoner_measurement.instruments.transport import NullTransport >>> t = NullTransport() >>> t.open() >>> t.queue_response(b"OK\n") >>> t.read_until() b'OK\n' >>> t.close()
- read(num_bytes: int = 4096) bytes[source]¶
Return the next pre-loaded response, or
b""when exhausted.- Args:
- num_bytes (int):
Ignored; present for interface compatibility.
- Returns:
- (bytes):
Next response from the queue, or
b""if the queue is empty.
- Raises:
- ConnectionError:
If the transport has not been opened.
- read_until(terminator: bytes = b'\n') bytes[source]¶
Return the next pre-loaded response regardless of terminator.
- Args:
- terminator (bytes):
Ignored; present for interface compatibility.
- Returns:
- (bytes):
Next response from the queue, or
b""if exhausted.
- Raises:
- ConnectionError:
If the transport has not been opened.
- class stoner_measurement.instruments.transport.SerialTransport(port: str, baud_rate: int = 9600, data_bits: int = 8, stop_bits: float = 1, parity: str = 'N', xonxoff: bool = False, rtscts: bool = False, timeout: float = 2.0)[source]¶
Bases:
BaseTransportSerial-port transport using pyserial.
The default parameters match the most common factory settings found on scientific instruments (Keithley, Agilent/Keysight, Oxford Instruments, Lakeshore, Stanford Research Systems, etc.): 9600 baud, 8 data bits, 1 stop bit, no parity, no flow control. Adjust when an instrument’s manual specifies different values.
- Attributes:
- port (str):
System device name (e.g.
"/dev/ttyUSB0"or"COM3").- baud_rate (int):
Baud rate in bits per second. Common values: 9600, 19200, 57600, 115200. Defaults to
9600.- data_bits (int):
Number of data bits per character (5–8). Defaults to
8.- stop_bits (float):
Number of stop bits (1, 1.5, or 2). Defaults to
1.- parity (str):
Parity mode:
"N"(none),"E"(even),"O"(odd),"M"(mark), or"S"(space). Defaults to"N".- xonxoff (bool):
Enable software (XON/XOFF) flow control. Defaults to
False.- rtscts (bool):
Enable hardware (RTS/CTS) flow control. Defaults to
False.- timeout (float):
Read timeout in seconds. Defaults to
2.0.
- Examples:
>>> from stoner_measurement.instruments.transport import SerialTransport >>> t = SerialTransport(port="/dev/ttyUSB0", baud_rate=9600) >>> t.port '/dev/ttyUSB0' >>> t.baud_rate 9600 >>> t.xonxoff False >>> t.rtscts False
- read(num_bytes: int = 4096) bytes[source]¶
Read up to num_bytes from the serial port.
- Args:
- num_bytes (int):
Maximum number of bytes to read. Defaults to
4096.
- Returns:
- (bytes):
Bytes received.
- Raises:
- ConnectionError:
If the port is not open.
- TimeoutError:
If no data arrives within
timeoutseconds.
- read_until(terminator: bytes = b'\n') bytes[source]¶
Read from the serial port until terminator is received.
Uses pyserial’s native
read_untilfor efficiency.- Args:
- terminator (bytes):
Terminator byte sequence. Defaults to
b"\n".
- Returns:
- (bytes):
All bytes up to and including the terminator.
- Raises:
- ConnectionError:
If the port is not open.
- TimeoutError:
If no data arrives within
timeoutseconds.
- class stoner_measurement.instruments.transport.UdpTransport(host: str, port: int, timeout: float = 2.0)[source]¶
Bases:
BaseTransportUDP socket transport for network-connected instruments.
- Attributes:
- host (str):
Hostname or IP address of the instrument.
- port (int):
UDP port number.
- timeout (float):
Socket read timeout in seconds. Defaults to
2.0.
- Examples:
>>> from stoner_measurement.instruments.transport import UdpTransport >>> t = UdpTransport(host="192.168.1.100", port=5025) >>> t.host '192.168.1.100' >>> t.port 5025
- open() None[source]¶
Create a UDP socket and record the default remote address.
socket.connect()is called on the UDP socket so that subsequentsocket.send()andsocket.recv()calls are directed to and filtered from the given host and port without requiring explicit address arguments.- Raises:
- ConnectionError:
If the socket cannot be created or bound.
- read(num_bytes: int = 4096) bytes[source]¶
Receive up to num_bytes from the instrument.
- Args:
- num_bytes (int):
Maximum number of bytes to read per datagram. Defaults to
4096.
- Returns:
- (bytes):
Bytes received from the instrument.
- Raises:
- ConnectionError:
If the socket is not open.
- TimeoutError:
If no datagram arrives within
timeoutseconds.
- property transport_address: str¶
Return the remote endpoint as
"<host>:<port>".- Returns:
- (str):
UDP address string, e.g.
"192.168.1.100:5025".
- Examples:
>>> from stoner_measurement.instruments.transport import UdpTransport >>> UdpTransport(host="192.168.1.100", port=5025).transport_address '192.168.1.100:5025'
Protocol layer classes for instrument communications.
Provides an abstract BaseProtocol interface and concrete
implementations for SCPI (ScpiProtocol), Oxford Instruments
carriage-return-terminated (OxfordProtocol), and Lakeshore
simple-ASCII (LakeshoreProtocol) protocols.
- class stoner_measurement.instruments.protocol.BaseProtocol[source]¶
Bases:
ABCAbstract base for all instrument communication protocols.
A protocol defines the command syntax and response parsing rules for a particular family of instruments. Concrete subclasses implement the
format_command(),format_query(), andparse_response()methods according to the protocol conventions.Two complementary error-detection strategies are supported:
- Response-embedded errors (Oxford Instruments, Lakeshore):
The instrument encodes an error indicator directly inside its normal response string (e.g. a leading
"?").errors_in_responsereturnsTruefor these protocols andcheck_error()should be called on every parsed response.- Error-queue errors (SCPI):
The instrument maintains an internal error queue that must be polled separately, typically via a
SYST:ERR?query.error_queryreturns the query string andcheck_error()is called on the result of that query rather than on normal responses.- Examples:
>>> from stoner_measurement.instruments.protocol import ScpiProtocol >>> p = ScpiProtocol() >>> p.format_command("VOLT 1.0") b'VOLT 1.0\n' >>> p.format_query("VOLT?") b'VOLT?\n' >>> p.parse_response(b' +1.234E+00\n') '+1.234E+00' >>> p.errors_in_response False >>> p.error_query 'SYST:ERR?'
- check_error(response: str, *, command: str | None = None) None[source]¶
Raise
InstrumentErrorif response indicates an error.The default implementation is a no-op. Override in subclasses that support error checking.
For protocols where
errors_in_responseisTrue(Oxford, Lakeshore), response is the parsed reply to any query and the method must inspect it for error indicators.For error-queue protocols (SCPI), response is the parsed reply to the
error_querycommand.- Args:
- response (str):
The parsed response string to examine.
- Keyword Parameters:
- command (str | None):
The original command that was sent (used to populate the
commandfield). Defaults toNone.
- Raises:
- InstrumentError:
If an instrument-level error is detected in the response.
- property error_query: str | None¶
Query string used to retrieve the first error from the error queue.
Returns
None(default) for protocols that embed errors in responses. Override in subclasses that maintain a separate error queue (e.g. SCPI returns"SYST:ERR?").- Returns:
- (str | None):
Query command string, or
Noneif not applicable.
- Examples:
>>> from stoner_measurement.instruments.protocol import ScpiProtocol >>> ScpiProtocol().error_query 'SYST:ERR?' >>> from stoner_measurement.instruments.protocol import OxfordProtocol >>> OxfordProtocol().error_query is None True
- property errors_in_response: bool¶
Trueif errors are embedded in normal query responses.When this property returns
True(Oxford, Lakeshore), the instrument signals command errors directly inside its reply to a query.check_error()should be called on every parsed response.When
False(SCPI), errors are held in a separate error queue and must be retrieved viaerror_query.- Returns:
- (bool):
Truefor response-embedded error protocols;False(default) for error-queue protocols.
- Examples:
>>> from stoner_measurement.instruments.protocol import ScpiProtocol >>> ScpiProtocol().errors_in_response False >>> from stoner_measurement.instruments.protocol import OxfordProtocol >>> OxfordProtocol().errors_in_response True
- abstractmethod format_command(command: str) bytes[source]¶
Format a command string for transmission.
- Args:
- command (str):
The human-readable command string, without any protocol- specific terminators.
- Returns:
- (bytes):
Bytes ready to be passed to
BaseTransport.write().
- abstractmethod format_query(query: str) bytes[source]¶
Format a query string for transmission.
- Args:
- query (str):
The human-readable query string. Many protocols distinguish between commands (which do not elicit a response) and queries (which do); subclasses may add or modify a suffix accordingly.
- Returns:
- (bytes):
Bytes ready to be passed to
BaseTransport.write().
- class stoner_measurement.instruments.protocol.LakeshoreProtocol(terminator: bytes = b'\r\n')[source]¶
Bases:
BaseProtocolLakeshore simple ASCII CRLF-terminated protocol.
Lakeshore instruments signal an unrecognised command by returning
"?"or a"?"-prefixed string in place of the expected value. Because the error indicator is embedded in the normal query response, this protocol setserrors_in_responsetoTrue.- Attributes:
- terminator (bytes):
Byte sequence appended to outgoing messages. Defaults to
b"\r\n".
- Examples:
>>> from stoner_measurement.instruments.protocol import LakeshoreProtocol >>> p = LakeshoreProtocol() >>> p.format_command("SETP 1,10.0") b'SETP 1,10.0\r\n' >>> p.format_query("KRDG? A") b'KRDG? A\r\n' >>> p.parse_response(b'+273.150\r\n') '+273.150' >>> p.errors_in_response True >>> p.error_query is None True
- check_error(response: str, *, command: str | None = None) None[source]¶
Raise
InstrumentErrorif response indicates an error.Some Lakeshore models return
"?"or a"?"-prefixed string for unrecognised commands.- Args:
- response (str):
Parsed response string.
- Keyword Parameters:
- command (str | None):
The original command that was sent.
- Raises:
- InstrumentError:
If the response is
"?"or starts with"?".
- Examples:
>>> from stoner_measurement.instruments.protocol import LakeshoreProtocol >>> LakeshoreProtocol().check_error("+77.350") # no exception >>> try: ... LakeshoreProtocol().check_error("?", command="KRDG? Z") ... except Exception as exc: ... print(type(exc).__name__, exc) InstrumentError Lakeshore: unrecognised command (command: KRDG? Z)
- property errors_in_response: bool¶
True— Lakeshore errors are embedded in the response payload.- Returns:
- (bool):
Always
TrueforLakeshoreProtocol.
- Examples:
>>> from stoner_measurement.instruments.protocol import LakeshoreProtocol >>> LakeshoreProtocol().errors_in_response True
- format_command(command: str) bytes[source]¶
Format a Lakeshore command for transmission.
- Args:
- command (str):
Command string without terminator.
- Returns:
- (bytes):
ASCII-encoded command with CRLF appended.
- Examples:
>>> from stoner_measurement.instruments.protocol import LakeshoreProtocol >>> LakeshoreProtocol().format_command("RAMP 1,1,0.5") b'RAMP 1,1,0.5\r\n'
- format_query(query: str) bytes[source]¶
Format a Lakeshore query for transmission.
- Args:
- query (str):
Query string without terminator.
- Returns:
- (bytes):
ASCII-encoded query with CRLF appended.
- Examples:
>>> from stoner_measurement.instruments.protocol import LakeshoreProtocol >>> LakeshoreProtocol().format_query("SRDG? B") b'SRDG? B\r\n'
- parse_response(raw: bytes) str[source]¶
Parse a raw Lakeshore response.
Decodes the bytes as ASCII and strips surrounding whitespace, including the CRLF terminator.
- Args:
- raw (bytes):
Raw bytes received from the instrument.
- Returns:
- (str):
Decoded response string with whitespace stripped.
- Examples:
>>> from stoner_measurement.instruments.protocol import LakeshoreProtocol >>> LakeshoreProtocol().parse_response(b'+77.350\r\n') '+77.350' >>> LakeshoreProtocol().parse_response(b'1,CONTROL,1\r\n') '1,CONTROL,1'
- class stoner_measurement.instruments.protocol.OxfordProtocol(terminator: bytes = b'\r')[source]¶
Bases:
BaseProtocolOxford Instruments carriage-return-terminated ASCII protocol.
Oxford Instruments instruments echo the command letter as the first character of each response. An error is indicated when the response payload (after stripping the echo character) begins with
"?". Because the error is carried in the normal response, this protocol setserrors_in_responsetoTrue; callers should invokecheck_error()on every parsed query response.- Attributes:
- terminator (bytes):
Byte sequence appended to outgoing messages. Defaults to
b"\r".
- Examples:
>>> from stoner_measurement.instruments.protocol import OxfordProtocol >>> p = OxfordProtocol() >>> p.format_command("S5") b'S5\r' >>> p.format_query("R1") b'R1\r' >>> p.parse_response(b'R1.234\r') '1.234' >>> p.errors_in_response True >>> p.error_query is None True
- check_error(response: str, *, command: str | None = None) None[source]¶
Raise
InstrumentErrorif response indicates an Oxford error.Oxford instruments respond with a payload starting with
"?"when the instrument did not understand the command.- Args:
- response (str):
Parsed response string (echo character already stripped by
parse_response()).
- Keyword Parameters:
- command (str | None):
The original command that was sent.
- Raises:
- InstrumentError:
If the response starts with
"?".
- Examples:
>>> from stoner_measurement.instruments.protocol import OxfordProtocol >>> OxfordProtocol().check_error("1.234") # no exception >>> try: ... OxfordProtocol().check_error("?", command="R99") ... except Exception as exc: ... print(type(exc).__name__, exc) InstrumentError Oxford Instruments: unrecognised command (command: R99)
- property errors_in_response: bool¶
True— Oxford errors are embedded in the response payload.- Returns:
- (bool):
Always
TrueforOxfordProtocol.
- Examples:
>>> from stoner_measurement.instruments.protocol import OxfordProtocol >>> OxfordProtocol().errors_in_response True
- format_command(command: str) bytes[source]¶
Format an Oxford Instruments command for transmission.
- Args:
- command (str):
Command string without terminator.
- Returns:
- (bytes):
ASCII-encoded command with carriage-return appended.
- Examples:
>>> from stoner_measurement.instruments.protocol import OxfordProtocol >>> OxfordProtocol().format_command("H1") b'H1\r'
- format_query(query: str) bytes[source]¶
Format an Oxford Instruments query for transmission.
Oxford Instruments does not formally distinguish commands from queries; both are formatted identically.
- Args:
- query (str):
Query string without terminator.
- Returns:
- (bytes):
ASCII-encoded query with carriage-return appended.
- Examples:
>>> from stoner_measurement.instruments.protocol import OxfordProtocol >>> OxfordProtocol().format_query("R1") b'R1\r'
- parse_response(raw: bytes) str[source]¶
Parse a raw Oxford Instruments response.
Oxford responses echo the command letter as their first character. This method strips the leading echo character, the trailing carriage return, and any surrounding whitespace.
- Args:
- raw (bytes):
Raw bytes received from the instrument.
- Returns:
- (str):
Response payload with the echo character and terminator removed.
- Examples:
>>> from stoner_measurement.instruments.protocol import OxfordProtocol >>> OxfordProtocol().parse_response(b'R1.234\r') '1.234' >>> OxfordProtocol().parse_response(b'S$A0C0H1L0R0B0\r') '$A0C0H1L0R0B0'
- class stoner_measurement.instruments.protocol.ScpiProtocol(terminator: bytes = b'\n')[source]¶
Bases:
BaseProtocolSCPI protocol — newline-terminated ASCII messages.
- Attributes:
- terminator (bytes):
Byte sequence appended to every outgoing message. Defaults to
b"\n".
- Examples:
>>> from stoner_measurement.instruments.protocol import ScpiProtocol >>> p = ScpiProtocol() >>> p.format_command("OUTP ON") b'OUTP ON\n' >>> p.format_query("MEAS:CURR?") b'MEAS:CURR?\n' >>> p.parse_response(b' +1.234E-03\n') '+1.234E-03' >>> p.errors_in_response False >>> p.error_query 'SYST:ERR?'
- check_error(response: str, *, command: str | None = None) None[source]¶
Raise
InstrumentErrorif response signals a SCPI error.response should be the parsed reply to a
SYST:ERR?query. SCPI instruments encode their error queue entries as"<code>,\"<message>\""; a code of+0means No Error.- Args:
- response (str):
A response string previously obtained from
error_query.
- Keyword Parameters:
- command (str | None):
The original command that triggered the error check.
- Raises:
- InstrumentError:
If response does not start with
"+0".
- Examples:
>>> from stoner_measurement.instruments.protocol import ScpiProtocol >>> ScpiProtocol().check_error('+0,"No error"') # no exception >>> try: ... ScpiProtocol().check_error('-113,"Undefined header"', command="*IDN") ... except Exception as exc: ... print(type(exc).__name__, exc) InstrumentError Undefined header (command: *IDN, code: -113)
- property error_query: str¶
SCPI error-queue query string.
- Returns:
- (str):
"SYST:ERR?"— the standard SCPI command for reading the first entry from the instrument’s error queue.
- Examples:
>>> from stoner_measurement.instruments.protocol import ScpiProtocol >>> ScpiProtocol().error_query 'SYST:ERR?'
- format_command(command: str) bytes[source]¶
Format a SCPI command for transmission.
- Args:
- command (str):
SCPI command string (without terminator).
- Returns:
- (bytes):
UTF-8 encoded command with terminator appended.
- Examples:
>>> from stoner_measurement.instruments.protocol import ScpiProtocol >>> ScpiProtocol().format_command("VOLT 5.0") b'VOLT 5.0\n'
- format_query(query: str) bytes[source]¶
Format a SCPI query for transmission.
- Args:
- query (str):
SCPI query string (without terminator).
- Returns:
- (bytes):
UTF-8 encoded query with terminator appended.
- Examples:
>>> from stoner_measurement.instruments.protocol import ScpiProtocol >>> ScpiProtocol().format_query("*IDN?") b'*IDN?\n'
- parse_response(raw: bytes) str[source]¶
Parse a raw SCPI response.
Decodes raw as UTF-8 and strips surrounding whitespace.
- Args:
- raw (bytes):
Raw bytes received from the instrument.
- Returns:
- (str):
Decoded and stripped response string.
- Examples:
>>> from stoner_measurement.instruments.protocol import ScpiProtocol >>> ScpiProtocol().parse_response(b' KEITHLEY,2400,12345,C32\r\n') 'KEITHLEY,2400,12345,C32'
Driver discovery process¶
InstrumentDriverManager uses two
discovery paths:
Built-in discovery scans modules in
stoner_measurement.instruments(excluding theprotocolandtransportsubpackages) and registers non-abstractBaseInstrumentsubclasses.Third-party discovery loads entry points in the
stoner_measurement.instrumentsgroup and registers non-abstractBaseInstrumentsubclasses.
Use drivers_by_type()
to filter the discovered registry by an instrument base type such as
TemperatureController, MagnetController, or SourceMeter.
Instrument driver discovery and filtering utilities.
Provides InstrumentDriverManager for discovering concrete instrument
driver classes from the local package and third-party entry-points.
- class stoner_measurement.instruments.driver_manager.InstrumentDriverManager[source]¶
Bases:
objectDiscover and provide access to concrete instrument driver classes.
Discovery combines:
Built-in drivers found by scanning
stoner_measurement.instrumentsmodules.Third-party drivers exposed via the
stoner_measurement.instrumentsentry-point group.
- property driver_classes: dict[str, type[BaseInstrument]]¶
Return a copy of discovered driver classes.
- drivers_by_type(instrument_type: type[BaseInstrument], *, include_abstract: bool = False) dict[str, type[BaseInstrument]][source]¶
Return discovered drivers that subclass a specific instrument type.
- Args:
- instrument_type (type[BaseInstrument]):
Base class/interface to filter by, for example
MagnetControllerorTemperatureController.
- Keyword Parameters:
- include_abstract (bool):
If
True, include abstract classes in results. Defaults toFalse.
- Returns:
- (dict[str, type[BaseInstrument]]):
Mapping of driver name to driver class.
- Raises:
- TypeError:
If instrument_type is not a
BaseInstrumentsubclass.
- get(name: str) type[BaseInstrument] | None[source]¶
Return a discovered driver class by name, or
None.
Instrument hierarchy and concrete classes¶
Hierarchy overview:
Concrete classes currently included in the package:
Source meter drivers:
Keithley2400,Keithley2410,Keithley2450Temperature controller drivers:
Lakeshore335,Lakeshore336,Lakeshore340,OxfordITC503,OxfordMercuryTemperatureControllerMagnet controller drivers:
Lakeshore525,OxfordIPS120
Instrument driver framework for stoner_measurement.
Provides a two-layer composition architecture for communicating with laboratory instruments:
Transport layer (physical byte-level I/O):
BaseTransport— abstract baseSerialTransport— RS-232/RS-485 via pyserialEthernetTransport— TCP/IP socketGpibTransport— GPIB/IEEE-488 via PyVISANullTransport— loopback for testing
Protocol layer (command formatting and response parsing):
BaseProtocol— abstract baseScpiProtocol— SCPI / IEEE 488.2OxfordProtocol— Oxford Instruments CR protocolLakeshoreProtocol— Lakeshore CRLF ASCII
Instrument hierarchy:
BaseInstrument— holds transport + protocolTemperatureController— abstract typeMagnetController— abstract typeSourceMeter— abstract typeCurrentSource— abstract typeLockInAmplifier— abstract typeDigitalMultimeter— abstract typeElectrometer— abstract typeNanovoltmeter— abstract typeKeithley2000— concrete DMM driverKeithley2700— concrete DMM driverKeithley2182A— concrete nanovoltmeter driverKeithley182— concrete nanovoltmeter driverKeithley2400— concrete SMU driverKeithley2410— concrete SMU driverKeithley2450— concrete SMU driverKeithley6221— concrete current-source driverKeithley6845— concrete electrometer/picoammeter driverKeithley6514— concrete electrometer driverKeithley6517— concrete electrometer driverLakeshoreM81CurrentSource— concrete current-source driverSRS830— concrete lock-in amplifier driverLakeshoreM81LockIn— concrete lock-in amplifier driver
- class stoner_measurement.instruments.AlarmState(*values)[source]¶
Bases:
EnumAlarm status for a sensor channel.
- Attributes:
- DISABLED:
Alarm checking is not active for this channel.
- OK:
Temperature is within the configured alarm limits.
- LOW:
Temperature has fallen below the low-alarm threshold.
- HIGH:
Temperature has risen above the high-alarm threshold.
- DISABLED = 'disabled'¶
- HIGH = 'high'¶
- LOW = 'low'¶
- OK = 'ok'¶
- class stoner_measurement.instruments.BaseInstrument(transport: BaseTransport, protocol: BaseProtocol, *, auto_check_errors: bool = False)[source]¶
Bases:
ABCBase class for all instrument drivers.
Uses a composition pattern: each instrument instance holds a transport responsible for the physical byte-level communication, and a protocol responsible for formatting commands and parsing responses. This design allows any transport/protocol combination to be substituted without changing the instrument driver code.
Error checking is performed on demand via
check_for_errors()and can be enabled automatically for everywrite()andquery()call by settingauto_check_errorstoTrue.Two strategies are supported transparently, chosen based on the protocol:
Response-embedded (Oxford, Lakeshore) —
check_for_errors()inspects the last query response for an inline error token.Error-queue (SCPI) —
check_for_errors()polls the instrument’s error queue. If the transport supports an out-of-band status byte (read_status_byte(), e.g. GPIB serial poll), only the ESB bit is checked first to avoid an unnecessary round-trip when no error is pending.
- Attributes:
- transport (BaseTransport):
Transport layer instance (serial, Ethernet, GPIB, …).
- protocol (BaseProtocol):
Protocol instance (SCPI, Oxford, Lakeshore, …).
- auto_check_errors (bool):
When
True,write()andquery()automatically callcheck_for_errors()after each operation. Defaults toFalse.
- Examples:
>>> from stoner_measurement.instruments.transport import NullTransport >>> from stoner_measurement.instruments.protocol import ScpiProtocol >>> from stoner_measurement.instruments.base_instrument import BaseInstrument >>> t = NullTransport(responses=[b"ACME,Model1,SN001,v1.0\n"]) >>> instr = BaseInstrument(transport=t, protocol=ScpiProtocol()) >>> instr.connect() >>> instr.is_connected True >>> instr.query("*IDN?") 'ACME,Model1,SN001,v1.0' >>> instr.disconnect() >>> instr.is_connected False
- check_for_errors(*, command: str | None = None) None[source]¶
Poll the instrument for errors and raise if one is found.
The strategy depends on the protocol and transport combination:
If the protocol uses response-embedded errors (
errors_in_responseisTrue), this method is a no-op — errors are detected inline byquery().If the transport supports an out-of-band status byte (
read_status_byte()returns a non-Nonevalue), the IEEE 488.2 Event Status Bit (ESB, bit 2) is checked. If it is clear, no error query is sent.Otherwise (or if the ESB bit is set) the protocol’s
error_querycommand is sent andcheck_error()is called on the response.
- Keyword Parameters:
- command (str | None):
The command that preceded this check, used to populate the
commandfield of any raised exception. Defaults toNone.
- Raises:
- InstrumentError:
If the instrument reports an error.
- Examples:
>>> from stoner_measurement.instruments.transport import NullTransport >>> from stoner_measurement.instruments.protocol import ScpiProtocol >>> from stoner_measurement.instruments.base_instrument import BaseInstrument >>> t = NullTransport(responses=[b'+0,"No error"\n']) >>> instr = BaseInstrument(t, ScpiProtocol()) >>> instr.connect() >>> instr.check_for_errors() # no exception — queue is clear >>> instr.disconnect()
- connect() None[source]¶
Open the transport connection to the instrument.
- Raises:
- ConnectionError:
If the underlying transport cannot be opened.
- Examples:
>>> from stoner_measurement.instruments.transport import NullTransport >>> from stoner_measurement.instruments.protocol import ScpiProtocol >>> from stoner_measurement.instruments.base_instrument import BaseInstrument >>> instr = BaseInstrument(NullTransport(), ScpiProtocol()) >>> instr.connect() >>> instr.is_connected True >>> instr.disconnect()
- disconnect() None[source]¶
Close the transport connection to the instrument.
- Examples:
>>> from stoner_measurement.instruments.transport import NullTransport >>> from stoner_measurement.instruments.protocol import ScpiProtocol >>> from stoner_measurement.instruments.base_instrument import BaseInstrument >>> instr = BaseInstrument(NullTransport(), ScpiProtocol()) >>> instr.connect() >>> instr.disconnect() >>> instr.is_connected False
- identify() str[source]¶
Return the instrument identification string (
*IDN?).Sends the standard IEEE 488.2
*IDN?query and returns the response. Instruments that do not support*IDN?should override this method.- Returns:
- (str):
Identification string returned by the instrument.
- Raises:
- ConnectionError:
If the transport is not open.
- Examples:
>>> from stoner_measurement.instruments.transport import NullTransport >>> from stoner_measurement.instruments.protocol import ScpiProtocol >>> from stoner_measurement.instruments.base_instrument import BaseInstrument >>> t = NullTransport(responses=[b"ACME,Model1,SN001,v1.0\n"]) >>> instr = BaseInstrument(t, ScpiProtocol()) >>> instr.connect() >>> instr.identify() 'ACME,Model1,SN001,v1.0' >>> instr.disconnect()
- property is_connected: bool¶
Trueif the underlying transport is currently open.- Returns:
- (bool):
Connection state of the transport.
- Examples:
>>> from stoner_measurement.instruments.transport import NullTransport >>> from stoner_measurement.instruments.protocol import ScpiProtocol >>> from stoner_measurement.instruments.base_instrument import BaseInstrument >>> instr = BaseInstrument(NullTransport(), ScpiProtocol()) >>> instr.is_connected False
- query(command: str) str[source]¶
Send a query and return the instrument’s response.
Combines
write()andread()into a single call. The query is formatted byprotocolbefore transmission.If
auto_check_errorsisTrue:For protocols with response-embedded errors (Oxford, Lakeshore),
check_error()is called on the parsed response directly.For error-queue protocols (SCPI),
check_for_errors()is called after the response is returned.
- Args:
- command (str):
Query string in the instrument’s command language.
- Returns:
- (str):
Parsed response string.
- Raises:
- ConnectionError:
If the transport is not open.
- TimeoutError:
If no response is received within the transport timeout.
- InstrumentError:
If
auto_check_errorsisTrueand the instrument signals an error.
- Examples:
>>> from stoner_measurement.instruments.transport import NullTransport >>> from stoner_measurement.instruments.protocol import ScpiProtocol >>> from stoner_measurement.instruments.base_instrument import BaseInstrument >>> t = NullTransport(responses=[b"ACME,1,SN,v1\n"]) >>> instr = BaseInstrument(t, ScpiProtocol()) >>> instr.connect() >>> instr.query("*IDN?") 'ACME,1,SN,v1' >>> instr.disconnect()
- read() str[source]¶
Read a response from the instrument.
Reads until the protocol’s terminator character is received and parses the raw bytes using
protocol.- Returns:
- (str):
Parsed response string.
- Raises:
- ConnectionError:
If the transport is not open.
- TimeoutError:
If no data is received within the transport timeout.
- Examples:
>>> from stoner_measurement.instruments.transport import NullTransport >>> from stoner_measurement.instruments.protocol import ScpiProtocol >>> from stoner_measurement.instruments.base_instrument import BaseInstrument >>> t = NullTransport(responses=[b"+1.234\n"]) >>> instr = BaseInstrument(t, ScpiProtocol()) >>> instr.connect() >>> instr.read() '+1.234' >>> instr.disconnect()
- reset() None[source]¶
Send the standard IEEE 488.2 reset command (
*RST).Instruments that do not support
*RSTshould override this method.- Raises:
- ConnectionError:
If the transport is not open.
- Examples:
>>> from stoner_measurement.instruments.transport import NullTransport >>> from stoner_measurement.instruments.protocol import ScpiProtocol >>> from stoner_measurement.instruments.base_instrument import BaseInstrument >>> t = NullTransport() >>> instr = BaseInstrument(t, ScpiProtocol()) >>> instr.connect() >>> instr.reset() >>> t.write_log [b'*RST\n'] >>> instr.disconnect()
- write(command: str) None[source]¶
Send a command to the instrument without expecting a response.
The command is formatted by
protocolbefore being passed totransport. Ifauto_check_errorsisTrueand the protocol uses an error queue (not response-embedded errors),check_for_errors()is called after the command is sent.- Args:
- command (str):
Command string in the instrument’s command language.
- Raises:
- ConnectionError:
If the transport is not open.
- InstrumentError:
If
auto_check_errorsisTrueand the instrument reports an error after processing the command.
- Examples:
>>> from stoner_measurement.instruments.transport import NullTransport >>> from stoner_measurement.instruments.protocol import ScpiProtocol >>> from stoner_measurement.instruments.base_instrument import BaseInstrument >>> t = NullTransport() >>> instr = BaseInstrument(t, ScpiProtocol()) >>> instr.connect() >>> instr.write("OUTP ON") >>> t.write_log [b'OUTP ON\n'] >>> instr.disconnect()
- class stoner_measurement.instruments.ControlMode(*values)[source]¶
Bases:
EnumOperating mode of a PID control loop.
- Attributes:
- OFF:
Loop disabled; no control action.
- CLOSED_LOOP:
Standard PID feedback to the assigned input sensor.
- ZONE:
Automatic zone-table (scheduled PID) control.
- OPEN_LOOP:
Manual heater output; sensor reading is not used for control.
- MONITOR:
Sensor monitored but no heater driven (monitor-only input).
- CLOSED_LOOP = 'closed_loop'¶
- MONITOR = 'monitor'¶
- OFF = 'off'¶
- OPEN_LOOP = 'open_loop'¶
- ZONE = 'zone'¶
- class stoner_measurement.instruments.ControllerCapabilities(num_inputs: int, num_loops: int, input_channels: tuple[str, ...], loop_numbers: tuple[int, ...], has_ramp: bool = True, has_pid: bool = True, has_autotune: bool = False, has_alarm: bool = False, has_zone: bool = False, has_user_curves: bool = False, has_sensor_excitation: bool = False, has_cryogen_control: bool = False, min_temperature: float | None = None, max_temperature: float | None = None)[source]¶
Bases:
objectStatic capability descriptor for a temperature controller driver.
- Attributes:
- num_inputs (int):
Total number of sensor input channels.
- num_loops (int):
Total number of PID control loops.
- input_channels (tuple[str, …]):
Ordered tuple of channel identifiers (e.g.
("A", "B", "C")).- loop_numbers (tuple[int, …]):
Ordered tuple of loop numbers (e.g.
(1, 2)).- has_ramp (bool):
Trueif the driver supports setpoint ramping.- has_pid (bool):
Trueif the driver supports PID parameter read/write.- has_autotune (bool):
Trueif the driver supports PID autotuning.- has_alarm (bool):
Trueif the driver supports sensor alarm limits.- has_zone (bool):
Trueif the driver supports zone/table (scheduled PID) control.- has_user_curves (bool):
Trueif the driver supports user-defined calibration curves.- has_sensor_excitation (bool):
Trueif the driver supports configuring sensor excitation.- has_cryogen_control (bool):
Trueif the driver supports cryogen-flow or needle-valve control (e.g. Oxford Instruments ITC503 or Mercury iTC).- min_temperature (float | None):
Minimum achievable temperature in Kelvin, or
Noneif unknown.- max_temperature (float | None):
Maximum controllable temperature in Kelvin, or
Noneif unknown.
- class stoner_measurement.instruments.CurrentSource(transport: BaseTransport, protocol: BaseProtocol)[source]¶
Bases:
BaseInstrumentAbstract base class for precision current-source instruments.
Provides a shared API for DC current level, compliance voltage, and output enable control, with optional AC waveform and balanced-channel extensions advertised via
get_capabilities().- configure_custom_sweep(values: tuple[float, ...], *, delay: float = 0.0, count: int = 1) None[source]¶
Configure a custom point-by-point current sweep.
- Args:
- values (tuple[float, …]):
Explicit current values in amps.
- Keyword Parameters:
- delay (float):
Source settling delay between points in seconds.
- count (int):
Number of sweep repetitions.
- Raises:
- NotImplementedError:
If sweep is not supported. Check
get_capabilities().has_sweepbefore calling.
- configure_linear_sweep(start: float, stop: float, points: int, *, delay: float = 0.0, count: int = 1) None[source]¶
Configure a linearly spaced current sweep.
- Args:
- start (float):
Sweep start current in amps.
- stop (float):
Sweep stop current in amps.
- points (int):
Number of sweep points.
- Keyword Parameters:
- delay (float):
Source settling delay between sweep points in seconds.
- count (int):
Number of sweep repetitions.
- Raises:
- NotImplementedError:
If sweep is not supported. Check
get_capabilities().has_sweepbefore calling.
- configure_log_sweep(start: float, stop: float, points: int, *, delay: float = 0.0, count: int = 1) None[source]¶
Configure a logarithmically spaced current sweep.
- Args:
- start (float):
Sweep start current in amps.
- stop (float):
Sweep stop current in amps.
- points (int):
Number of sweep points.
- Keyword Parameters:
- delay (float):
Source settling delay between sweep points in seconds.
- count (int):
Number of sweep repetitions.
- Raises:
- NotImplementedError:
If sweep is not supported. Check
get_capabilities().has_sweepbefore calling.
- configure_pulsed_sweep(sweep: CurrentSweepConfiguration, pulse: PulsedSweepConfiguration) None[source]¶
Configure a pulsed current sweep.
Each point in sweep becomes a pulse of duration
pulse.widthseconds followed bypulse.off_timeseconds atpulse.low_levelbefore the next sweep point.- Args:
- sweep (CurrentSweepConfiguration):
Sweep point configuration.
- pulse (PulsedSweepConfiguration):
Pulse timing and baseline current configuration.
- Raises:
- NotImplementedError:
If pulsed sweep is not supported by the driver. Check
get_capabilities().has_pulsed_sweepbefore calling.
- configure_sweep(config: CurrentSweepConfiguration) None[source]¶
Configure a built-in current sweep.
- Args:
- config (CurrentSweepConfiguration):
Sweep configuration (spacing, start, stop, points, values, delay, count).
- Raises:
- NotImplementedError:
If sweep is not supported by the driver. Check
get_capabilities().has_sweepbefore calling.
- abstractmethod get_capabilities() CurrentSourceCapabilities[source]¶
Return the static capability descriptor for this driver.
- get_channel_level(channel: int) float[source]¶
Return programmed current for a specific output channel.
- Raises:
- NotImplementedError:
If per-channel current control is not supported by the driver.
- get_frequency() float[source]¶
Return AC waveform frequency in Hz.
- Raises:
- NotImplementedError:
If AC frequency control is not supported by the driver.
- get_offset_current() float[source]¶
Return AC waveform DC offset current in amps.
- Raises:
- NotImplementedError:
If offset-current control is not supported by the driver.
- get_waveform() CurrentWaveform[source]¶
Return the configured output waveform.
- Raises:
- NotImplementedError:
If waveform selection is not supported by the driver.
- set_channel_level(channel: int, value: float) None[source]¶
Set current for a specific output channel.
- Raises:
- NotImplementedError:
If per-channel current control is not supported by the driver.
- set_frequency(value: float) None[source]¶
Set AC waveform frequency in Hz.
- Raises:
- NotImplementedError:
If AC frequency control is not supported by the driver.
- set_offset_current(value: float) None[source]¶
Set AC waveform DC offset current in amps.
- Raises:
- NotImplementedError:
If offset-current control is not supported by the driver.
- set_waveform(waveform: CurrentWaveform) None[source]¶
Set output waveform mode.
- Raises:
- NotImplementedError:
If waveform selection is not supported by the driver.
- class stoner_measurement.instruments.CurrentSourceCapabilities(has_waveform_selection: bool = False, has_frequency_control: bool = False, has_offset_current: bool = False, has_balanced_outputs: bool = False, has_sweep: bool = False, has_pulsed_sweep: bool = False, channel_count: int = 1)[source]¶
Bases:
objectStatic capability descriptor for a current-source driver.
- Attributes:
- has_waveform_selection (bool):
Trueif the source can select waveform mode.- has_frequency_control (bool):
Trueif AC waveform frequency can be configured.- has_offset_current (bool):
Trueif waveform DC offset current can be configured.- has_balanced_outputs (bool):
Trueif the source supports balanced multi-channel output pairs.- has_sweep (bool):
Trueif the source supports built-in source sweeps viaconfigure_sweep(),sweep_start(), andsweep_abort().- has_pulsed_sweep (bool):
Trueif the source supports pulsed sweeps viaconfigure_pulsed_sweep().- channel_count (int):
Number of independently addressable output channels.
- class stoner_measurement.instruments.CurrentSweepConfiguration(start: float = 0.0, stop: float = 0.0, points: int = 0, spacing: CurrentSweepSpacing = CurrentSweepSpacing.LIN, values: tuple[float, ...] | None = None, delay: float = 0.0, count: int = 1)[source]¶
Bases:
objectConfiguration for a current-source sweep.
- Attributes:
- start (float):
Sweep start value in amps. Ignored for list sweeps.
- stop (float):
Sweep stop value in amps. Ignored for list sweeps.
- points (int):
Number of sweep points. For list sweeps this is inferred from
len(values)by the driver; ignored when values is provided.- spacing (CurrentSweepSpacing):
Point-spacing mode. Defaults to
LIN.- values (tuple[float, …] | None):
Explicit source values for
LISTsweeps. Ignored for linear and logarithmic sweeps.- delay (float):
Source settling delay between sweep points in seconds.
- count (int):
Number of sweep repetitions. Defaults to
1.
- Notes:
Default start, stop, and points values are placeholders. For list sweeps supply values and omit start/stop/points.
- spacing: CurrentSweepSpacing = 'LIN'¶
- class stoner_measurement.instruments.CurrentSweepSpacing(*values)[source]¶
Bases:
EnumPoint-spacing mode for a built-in current sweep.
- Attributes:
- LIN:
Linearly spaced sweep points from start to stop.
- LOG:
Logarithmically spaced sweep points from start to stop.
- LIST:
Arbitrary point list supplied in
CurrentSweepConfiguration.values.
- LIN = 'LIN'¶
- LIST = 'LIST'¶
- LOG = 'LOG'¶
- class stoner_measurement.instruments.CurrentWaveform(*values)[source]¶
Bases:
EnumOutput waveform mode of a current-source instrument.
- DC = 'DC'¶
- SINE = 'SINE'¶
- class stoner_measurement.instruments.DigitalMultimeter(transport: BaseTransport, protocol: BaseProtocol)[source]¶
Bases:
BaseInstrumentAbstract base class for digital multimeter instruments.
- Attributes:
- transport (BaseTransport):
Transport layer instance.
- protocol (BaseProtocol):
Protocol instance.
- abort() None[source]¶
Abort a running measurement sequence.
- Raises:
- NotImplementedError:
If trigger configuration is not supported by the instrument.
- clear_buffer() None[source]¶
Clear the instrument reading buffer.
- Raises:
- NotImplementedError:
If buffer operations are not supported by the instrument.
- abstractmethod get_autorange() bool[source]¶
Return
Trueif autorange is enabled.- Returns:
- (bool):
Truewhen autorange is enabled.
- get_buffer_count() int[source]¶
Return the number of readings currently stored in the buffer.
- Raises:
- NotImplementedError:
If buffer operations are not supported by the instrument.
- abstractmethod get_capabilities() DmmCapabilities[source]¶
Return static capability metadata.
- Returns:
- (DmmCapabilities):
Capability descriptor.
- get_filter_count() int[source]¶
Return the configured filter averaging count.
- Raises:
- NotImplementedError:
If filtering is not supported by the instrument.
- get_filter_enabled() bool[source]¶
Return whether filtering is enabled.
- Raises:
- NotImplementedError:
If filtering is not supported by the instrument.
- abstractmethod get_measure_function() DmmFunction[source]¶
Return the active measurement function.
- Returns:
- (DmmFunction):
Active measurement function.
- abstractmethod get_nplc() float[source]¶
Return the integration time in line cycles.
- Returns:
- (float):
Integration time in power-line cycles.
- abstractmethod get_range() float[source]¶
Return the active measurement range.
- Returns:
- (float):
Range value in units of the active function.
- get_trigger_count() int[source]¶
Return the configured trigger count.
- Raises:
- NotImplementedError:
If trigger configuration is not supported by the instrument.
- get_trigger_source() DmmTriggerSource[source]¶
Return the trigger source selection.
- Raises:
- NotImplementedError:
If trigger configuration is not supported by the instrument.
- initiate() None[source]¶
Arm the trigger system and begin measurements.
- Raises:
- NotImplementedError:
If trigger configuration is not supported by the instrument.
- abstractmethod measure() float[source]¶
Trigger a measurement and return its value.
- Returns:
- (float):
Measured scalar value in units of the active function.
- read_buffer(count: int | None = None) tuple[float, ...][source]¶
Read values from the instrument buffer.
- Keyword Parameters:
- count (int | None):
Optional number of points to read from the start of the buffer. If
None, read all available points.
- Returns:
- (tuple[float, …]):
Parsed buffer values.
- Raises:
- NotImplementedError:
If buffer operations are not supported by the instrument.
- abstractmethod set_autorange(state: bool) None[source]¶
Enable or disable autorange.
- Args:
- state (bool):
Trueto enable autorange.
- set_filter_count(count: int) None[source]¶
Set the filter averaging count.
- Raises:
- NotImplementedError:
If filtering is not supported by the instrument.
- set_filter_enabled(state: bool) None[source]¶
Enable or disable measurement filtering.
- Raises:
- NotImplementedError:
If filtering is not supported by the instrument.
- abstractmethod set_measure_function(function: DmmFunction) None[source]¶
Set the active measurement function.
- Args:
- function (DmmFunction):
Function to select.
- abstractmethod set_nplc(value: float) None[source]¶
Set the integration time in line cycles.
- Args:
- value (float):
Integration time in power-line cycles.
- abstractmethod set_range(value: float) None[source]¶
Set the active measurement range.
- Args:
- value (float):
Range value in units of the active function.
- set_trigger_count(count: int) None[source]¶
Set the trigger count.
- Raises:
- NotImplementedError:
If trigger configuration is not supported by the instrument.
- set_trigger_source(source: DmmTriggerSource) None[source]¶
Set the trigger source selection.
- Raises:
- NotImplementedError:
If trigger configuration is not supported by the instrument.
- class stoner_measurement.instruments.DmmCapabilities(has_function_selection: bool = True, has_filter: bool = False, has_trigger: bool = False, has_buffer: bool = False, supported_functions: tuple[DmmFunction, ...] = (DmmFunction.VOLT_DC,))[source]¶
Bases:
objectStatic capability descriptor for a DMM driver.
- supported_functions: tuple[DmmFunction, ...] = (DmmFunction.VOLT_DC,)¶
- class stoner_measurement.instruments.DmmFunction(*values)[source]¶
Bases:
EnumMeasurement functions for digital multimeters.
- CURR_AC = 'CURR:AC'¶
- CURR_DC = 'CURR:DC'¶
- FREQ = 'FREQ'¶
- FRES = 'FRES'¶
- PER = 'PER'¶
- RES = 'RES'¶
- TEMP = 'TEMP'¶
- VOLT_AC = 'VOLT:AC'¶
- VOLT_DC = 'VOLT:DC'¶
- class stoner_measurement.instruments.DmmTriggerSource(*values)[source]¶
Bases:
EnumTrigger-source selection for digital multimeters.
- BUS = 'BUS'¶
- EXT = 'EXT'¶
- IMM = 'IMM'¶
- MAN = 'MAN'¶
- TIM = 'TIM'¶
- class stoner_measurement.instruments.Electrometer(transport: BaseTransport, protocol: BaseProtocol)[source]¶
Bases:
BaseInstrumentAbstract base class for electrometer and picoammeter instruments.
- configure_trigger_model(config: ElectrometerTriggerConfiguration) None[source]¶
Configure trigger and arm model settings.
- abstractmethod get_capabilities() ElectrometerCapabilities[source]¶
Return static feature capabilities for the driver.
- get_data_format() ElectrometerDataFormat[source]¶
Return configured response data format.
- get_measure_functions() tuple[ElectrometerFunction, ...][source]¶
Return enabled measurement functions.
- abstractmethod measure_current() float[source]¶
Trigger a current measurement and return the value in amps.
- set_data_format(data_format: ElectrometerDataFormat) None[source]¶
Set response data format.
- set_measure_functions(functions: tuple[ElectrometerFunction, ...]) None[source]¶
Enable one or more measurement functions.
- class stoner_measurement.instruments.ElectrometerCapabilities(has_function_selection: bool = False, has_filter: bool = False, has_trigger_model: bool = False, has_buffer: bool = False, has_data_format: bool = False)[source]¶
Bases:
objectStatic capability descriptor for an electrometer driver.
- class stoner_measurement.instruments.ElectrometerDataFormat(*values)[source]¶
Bases:
EnumData transfer format used for query responses.
- ASCII = 'ASC'¶
- DREAL = 'DRE'¶
- SREAL = 'SRE'¶
- class stoner_measurement.instruments.ElectrometerFunction(*values)[source]¶
Bases:
EnumMeasurement function selected on an electrometer.
- CHARGE = 'CHAR'¶
- CURR = 'CURR'¶
- RES = 'RES'¶
- VOLT = 'VOLT'¶
- class stoner_measurement.instruments.ElectrometerTriggerConfiguration(trigger_source: ElectrometerTriggerSource = ElectrometerTriggerSource.IMM, trigger_count: int = 1, trigger_delay: float = 0.0, arm_source: ElectrometerTriggerSource = ElectrometerTriggerSource.IMM, arm_count: int = 1)[source]¶
Bases:
objectConfiguration for trigger and arm model settings.
- arm_source: ElectrometerTriggerSource = 'IMM'¶
- trigger_source: ElectrometerTriggerSource = 'IMM'¶
- class stoner_measurement.instruments.ElectrometerTriggerSource(*values)[source]¶
Bases:
EnumTrigger source for a simple trigger model.
- BUS = 'BUS'¶
- EXT = 'EXT'¶
- IMM = 'IMM'¶
- TIM = 'TIM'¶
- TLIN = 'TLIN'¶
- class stoner_measurement.instruments.InstrumentDriverManager[source]¶
Bases:
objectDiscover and provide access to concrete instrument driver classes.
Discovery combines:
Built-in drivers found by scanning
stoner_measurement.instrumentsmodules.Third-party drivers exposed via the
stoner_measurement.instrumentsentry-point group.
- property driver_classes: dict[str, type[BaseInstrument]]¶
Return a copy of discovered driver classes.
- drivers_by_type(instrument_type: type[BaseInstrument], *, include_abstract: bool = False) dict[str, type[BaseInstrument]][source]¶
Return discovered drivers that subclass a specific instrument type.
- Args:
- instrument_type (type[BaseInstrument]):
Base class/interface to filter by, for example
MagnetControllerorTemperatureController.
- Keyword Parameters:
- include_abstract (bool):
If
True, include abstract classes in results. Defaults toFalse.
- Returns:
- (dict[str, type[BaseInstrument]]):
Mapping of driver name to driver class.
- Raises:
- TypeError:
If instrument_type is not a
BaseInstrumentsubclass.
- get(name: str) type[BaseInstrument] | None[source]¶
Return a discovered driver class by name, or
None.
- register(name: str, driver_cls: type[BaseInstrument]) None[source]¶
Manually register a driver class.
- Args:
- name (str):
Unique identifier for the driver.
- driver_cls (type[BaseInstrument]):
Driver class to register.
- Raises:
- TypeError:
If driver_cls is not a
BaseInstrumentsubclass.
- exception stoner_measurement.instruments.InstrumentError(message: str, *, command: str | None = None, error_code: int | None = None)[source]¶
Bases:
ExceptionRaised when an instrument reports a command or execution error.
Carries structured information about the failure so that calling code can log or display a meaningful diagnostic without parsing the exception message string.
- Attributes:
- command (str | None):
The command string that triggered the error, or
Noneif the failing command is not known.- error_code (int | None):
Numeric error code returned by the instrument (e.g. the SCPI integer before the comma in a
SYST:ERR?response), orNonefor protocols that do not use numeric codes.- message (str):
Human-readable description of the error as reported by the instrument.
- Examples:
>>> from stoner_measurement.instruments.errors import InstrumentError >>> exc = InstrumentError( ... "Undefined header", ... command="*IDN", ... error_code=-113, ... ) >>> exc.command '*IDN' >>> exc.error_code -113 >>> str(exc) 'Undefined header (command: *IDN, code: -113)'
- class stoner_measurement.instruments.LockInAmplifier(transport: BaseTransport, protocol: BaseProtocol)[source]¶
Bases:
BaseInstrumentAbstract base class for lock-in amplifier instruments.
Provides a common interface for dual-output lock-in amplifiers where the measured signal is available as in-phase/quadrature components (
X,Y) and polar components (magnitudeR, angletheta).- Attributes:
- transport (BaseTransport):
Transport layer instance.
- protocol (BaseProtocol):
Protocol instance.
- auto_gain() None[source]¶
Run instrument auto-gain adjustment.
- Raises:
- NotImplementedError:
If auto-gain is not supported by the instrument.
- auto_phase() None[source]¶
Run instrument auto-phase adjustment.
- Raises:
- NotImplementedError:
If auto-phase is not supported by the instrument.
- auto_reserve() None[source]¶
Run instrument auto-reserve adjustment.
- Raises:
- NotImplementedError:
If auto-reserve is not supported by the instrument.
- abstractmethod get_capabilities() LockInAmplifierCapabilities[source]¶
Return static capability metadata.
- Returns:
- (LockInAmplifierCapabilities):
Capability descriptor.
- get_dynamic_reserve_db() float[source]¶
Return the dynamic reserve (signal stability) in decibels.
A higher value indicates that the instrument can tolerate larger interfering signals relative to the signal of interest without introducing significant errors.
- Returns:
- (float):
Dynamic reserve in dB.
- Raises:
- NotImplementedError:
If numeric dynamic reserve control is not supported by the instrument.
- get_filter_slope() int[source]¶
Return the output filter roll-off slope in dB/octave.
- Raises:
- NotImplementedError:
If filter-slope control is not supported by the instrument.
- get_harmonic() int[source]¶
Return the selected detection harmonic.
- Raises:
- NotImplementedError:
If harmonic selection is not supported by the instrument.
- get_input_coupling() LockInInputCoupling[source]¶
Return the input coupling mode.
- Raises:
- NotImplementedError:
If input-coupling control is not supported by the instrument.
- get_input_shielding() LockInInputShielding[source]¶
Return the input shield grounding mode.
- Returns:
- (LockInInputShielding):
Active input shield grounding mode.
- Raises:
- NotImplementedError:
If input shielding control is not supported by the instrument.
- get_input_source() LockInInputSource[source]¶
Return the active input source.
- Returns:
- (LockInInputSource):
Active input source selection.
- Raises:
- NotImplementedError:
If input source selection is not supported by the instrument.
- get_line_filter() LockInLineFilter[source]¶
Return the line-frequency notch filter configuration.
- Returns:
- (LockInLineFilter):
Active notch filter configuration.
- Raises:
- NotImplementedError:
If line filter control is not supported by the instrument.
- get_oscillator_amplitude() float[source]¶
Return the internal oscillator sine output amplitude in volts.
- Returns:
- (float):
Oscillator amplitude in volts.
- Raises:
- NotImplementedError:
If the instrument does not have a controllable internal oscillator.
- get_output_offset(channel: LockInOutputChannel) tuple[float, LockInExpandFactor][source]¶
Return the output offset percentage and expand factor for a channel.
- Args:
- channel (LockInOutputChannel):
Output channel to query.
- Returns:
- (tuple[float, LockInExpandFactor]):
(offset_pct, expand_factor)whereoffset_pctis the offset as a percentage andexpand_factoris the expand multiplier.
- Raises:
- NotImplementedError:
If output offset and expand are not supported by the instrument.
- abstractmethod get_reference_frequency() float[source]¶
Return the reference frequency in hertz.
- Returns:
- (float):
Reference frequency in hertz.
- abstractmethod get_reference_phase() float[source]¶
Return the reference phase in degrees.
- Returns:
- (float):
Reference phase in degrees.
- abstractmethod get_reference_source() LockInReferenceSource[source]¶
Return the active reference source.
- Returns:
- (LockInReferenceSource):
Active reference source.
- get_reserve_mode() LockInReserveMode[source]¶
Return the dynamic reserve mode.
- Raises:
- NotImplementedError:
If reserve-mode control is not supported by the instrument.
- abstractmethod get_sensitivity() float[source]¶
Return the active sensitivity scale.
- Returns:
- (float):
Sensitivity in volts.
- get_sync_filter_enabled() bool[source]¶
Return whether the synchronous output filter is enabled.
- Returns:
- (bool):
Truewhen the synchronous filter is enabled.
- Raises:
- NotImplementedError:
If synchronous filter control is not supported by the instrument.
- abstractmethod get_time_constant() float[source]¶
Return the active output filter time constant.
- Returns:
- (float):
Time constant in seconds.
- abstractmethod measure_rt() tuple[float, float][source]¶
Measure and return magnitude and phase outputs.
- Returns:
- (tuple[float, float]):
(magnitude, theta)where magnitude is in volts andthetais in degrees.
- abstractmethod measure_xy() tuple[float, float][source]¶
Measure and return the in-phase and quadrature outputs.
- Returns:
- (tuple[float, float]):
(x, y)values in volts, wherexis the in-phase component andyis the quadrature component.
- set_dynamic_reserve_db(value_db: float) None[source]¶
Set the dynamic reserve (signal stability) in decibels.
- Args:
- value_db (float):
Desired dynamic reserve in dB.
- Raises:
- NotImplementedError:
If numeric dynamic reserve control is not supported by the instrument.
- set_filter_slope(slope: int) None[source]¶
Set the output filter roll-off slope in dB/octave.
- Raises:
- NotImplementedError:
If filter-slope control is not supported by the instrument.
- set_harmonic(harmonic: int) None[source]¶
Set the selected detection harmonic.
- Raises:
- NotImplementedError:
If harmonic selection is not supported by the instrument.
- set_input_coupling(coupling: LockInInputCoupling) None[source]¶
Set the input coupling mode.
- Raises:
- NotImplementedError:
If input-coupling control is not supported by the instrument.
- set_input_shielding(shielding: LockInInputShielding) None[source]¶
Set the input shield grounding mode.
- Args:
- shielding (LockInInputShielding):
Shielding mode to select.
- Raises:
- NotImplementedError:
If input shielding control is not supported by the instrument.
- set_input_source(source: LockInInputSource) None[source]¶
Set the active input source.
- Args:
- source (LockInInputSource):
Input source to select.
- Raises:
- NotImplementedError:
If input source selection is not supported by the instrument.
- set_line_filter(filter_config: LockInLineFilter) None[source]¶
Set the line-frequency notch filter configuration.
- Args:
- filter_config (LockInLineFilter):
Notch filter configuration to apply.
- Raises:
- NotImplementedError:
If line filter control is not supported by the instrument.
- set_oscillator_amplitude(value: float) None[source]¶
Set the internal oscillator sine output amplitude in volts.
- Args:
- value (float):
Amplitude in volts.
- Raises:
- NotImplementedError:
If the instrument does not have a controllable internal oscillator.
- set_output_offset(channel: LockInOutputChannel, offset_pct: float, expand_factor: LockInExpandFactor) None[source]¶
Set the output offset and expand factor for a channel.
- Args:
- channel (LockInOutputChannel):
Output channel to configure.
- offset_pct (float):
Offset as a percentage (typically −105 % to +105 %).
- expand_factor (LockInExpandFactor):
Expand multiplier to apply.
- Raises:
- NotImplementedError:
If output offset and expand are not supported by the instrument.
- abstractmethod set_reference_frequency(value: float) None[source]¶
Set the reference frequency in hertz.
- Args:
- value (float):
Frequency in hertz.
- abstractmethod set_reference_phase(value: float) None[source]¶
Set the reference phase in degrees.
- Args:
- value (float):
Phase in degrees.
- abstractmethod set_reference_source(source: LockInReferenceSource) None[source]¶
Set the active reference source.
- Args:
- source (LockInReferenceSource):
Source to select.
- set_reserve_mode(mode: LockInReserveMode) None[source]¶
Set the dynamic reserve mode.
- Raises:
- NotImplementedError:
If reserve-mode control is not supported by the instrument.
- abstractmethod set_sensitivity(value: float) None[source]¶
Set the active sensitivity scale.
- Args:
- value (float):
Sensitivity in volts.
- class stoner_measurement.instruments.LockInAmplifierCapabilities(has_reference_source_selection: bool = True, has_reference_frequency_control: bool = True, has_reference_phase_control: bool = True, has_harmonic_selection: bool = False, has_filter_slope_control: bool = False, has_input_coupling_control: bool = False, has_reserve_mode_control: bool = False, has_auto_gain: bool = False, has_auto_phase: bool = False, has_auto_reserve: bool = False, has_output_offset: bool = False, has_internal_oscillator: bool = False, has_input_source_selection: bool = False, has_input_shielding_control: bool = False, has_line_filter_control: bool = False, has_sync_filter: bool = False, has_dynamic_reserve_db: bool = False, max_harmonic: int = 0)[source]¶
Bases:
objectStatic capability descriptor for a lock-in amplifier driver.
- Attributes:
- max_harmonic (int):
Highest harmonic supported for lock-in detection. A value of
0means harmonic selection is not available or the limit is not known; whenhas_harmonic_selectionisTrueandmax_harmonicis0the driver imposes no upper bound.
- class stoner_measurement.instruments.LockInExpandFactor(*values)[source]¶
Bases:
EnumExpand factor for output offset operations.
- X1 = 1¶
- X10 = 10¶
- X100 = 100¶
- class stoner_measurement.instruments.LockInInputCoupling(*values)[source]¶
Bases:
EnumInput-coupling selection for lock-in amplifiers.
- AC = 'AC'¶
- DC = 'DC'¶
- class stoner_measurement.instruments.LockInInputShielding(*values)[source]¶
Bases:
EnumInput shield grounding selection for lock-in amplifiers.
- FLOAT = 'FLOAT'¶
- GROUND = 'GROUND'¶
- class stoner_measurement.instruments.LockInInputSource(*values)[source]¶
Bases:
EnumInput source selection for lock-in amplifiers.
- A = 'A'¶
- A_MINUS_B = 'A_MINUS_B'¶
- I_100MOHM = 'I_100MOHM'¶
- I_1MOHM = 'I_1MOHM'¶
- class stoner_measurement.instruments.LockInLineFilter(*values)[source]¶
Bases:
EnumLine-frequency notch filter configuration for lock-in amplifiers.
- BOTH = 'BOTH'¶
- LINE = 'LINE'¶
- LINE_2X = 'LINE_2X'¶
- NONE = 'NONE'¶
- class stoner_measurement.instruments.LockInOutputChannel(*values)[source]¶
Bases:
EnumOutput channel selection for offset and expand operations.
- R = 'R'¶
- X = 'X'¶
- Y = 'Y'¶
- class stoner_measurement.instruments.LockInReferenceSource(*values)[source]¶
Bases:
EnumReference-source selection for lock-in amplifiers.
- EXTERNAL = 'EXTERNAL'¶
- INTERNAL = 'INTERNAL'¶
- class stoner_measurement.instruments.LockInReserveMode(*values)[source]¶
Bases:
EnumDynamic-reserve operating modes.
- HIGH_RESERVE = 'HIGH_RESERVE'¶
- LOW_NOISE = 'LOW_NOISE'¶
- NORMAL = 'NORMAL'¶
- class stoner_measurement.instruments.LoopStatus(setpoint: float, process_value: float, mode: ControlMode, heater_output: float, ramp_enabled: bool, ramp_rate: float, ramp_state: RampState, p: float, i: float, d: float, input_channel: str)[source]¶
Bases:
objectA snapshot of one PID control loop’s complete state.
- Attributes:
- setpoint (float):
Current setpoint temperature in Kelvin.
- process_value (float):
Temperature reading at the loop’s input sensor in Kelvin.
- mode (ControlMode):
Active control mode of the loop.
- heater_output (float):
Heater output as a percentage (0–100 %).
- ramp_enabled (bool):
Truewhen automatic setpoint ramping is enabled.- ramp_rate (float):
Setpoint ramp rate in Kelvin per minute.
- ramp_state (RampState):
Whether the loop is currently executing a ramp.
- p (float):
Proportional gain.
- i (float):
Integral gain.
- d (float):
Derivative gain.
- input_channel (str):
Identifier of the sensor channel driving this loop.
- mode: ControlMode = <dataclasses._MISSING_TYPE object>¶
- class stoner_measurement.instruments.MagnetController(transport: BaseTransport, protocol: BaseProtocol)[source]¶
Bases:
BaseInstrumentAbstract base class for superconducting magnet power supply drivers.
Provides a uniform interface for controlling superconducting magnet power supplies such as the Oxford Instruments IPS120-10. All field values are in tesla and ramp rates in tesla per minute unless otherwise stated.
Subclasses must implement all abstract methods.
- Attributes:
- transport (BaseTransport):
Transport layer instance.
- protocol (BaseProtocol):
Protocol layer instance.
- Examples:
>>> from stoner_measurement.instruments.transport import NullTransport >>> from stoner_measurement.instruments.protocol import OxfordProtocol >>> from stoner_measurement.instruments.magnet_controller import ( ... MagnetController, MagnetState, MagnetStatus, MagnetLimits, ... ) >>> class _MC(MagnetController): ... def get_model(self): return "TestMagnet" ... def get_firmware_version(self): return "1.0" ... @property ... def current(self): return 0.0 ... @property ... def field(self): return 0.0 ... @property ... def voltage(self): return 0.0 ... @property ... def status(self): ... return MagnetStatus( ... state=MagnetState.STANDBY, current=0.0, field=0.0, ... voltage=0.0, persistent=False, heater_on=False, ... at_target=True, ... ) ... @property ... def magnet_constant(self): return 0.1 ... @property ... def limits(self): return MagnetLimits(max_current=100.0) ... @property ... def heater(self): return False ... def set_target_current(self, current): pass ... def set_target_field(self, field): pass ... def set_ramp_rate_current(self, rate): pass ... def set_ramp_rate_field(self, rate): pass ... def set_magnet_constant(self, tesla_per_amp): pass ... def set_limits(self, limits): pass ... def ramp_to_target(self): pass ... def ramp_to_current(self, current, *, wait=False): pass ... def ramp_to_field(self, field, *, wait=False): pass ... def pause_ramp(self): pass ... def abort_ramp(self): pass ... def heater_on(self): pass ... def heater_off(self): pass >>> mc = _MC(NullTransport(), OxfordProtocol()) >>> mc.get_model() 'TestMagnet' >>> mc.status.state <MagnetState.STANDBY: 'standby'>
- abstractmethod abort_ramp() None[source]¶
Abort ramping immediately and hold the output at its current value.
- Raises:
- ConnectionError:
If the transport is not open.
- abstract property current: float¶
Return the current output in amps.
- Returns:
- (float):
Instantaneous output current in amps.
- Raises:
- ConnectionError:
If the transport is not open.
- abstract property field: float¶
Return the magnetic field output in tesla.
- Returns:
- (float):
Estimated magnetic field in tesla derived from the output current and the configured magnet constant.
- Raises:
- ConnectionError:
If the transport is not open.
- abstractmethod get_firmware_version() str[source]¶
Return the firmware version string.
- Returns:
- (str):
Firmware version as reported by the device.
- Raises:
- ConnectionError:
If the transport is not open.
- Examples:
>>> mc.get_firmware_version() '1.0'
- abstractmethod get_model() str[source]¶
Return the instrument model identifier string.
- Returns:
- (str):
Instrument model identifier as reported by the device.
- Raises:
- ConnectionError:
If the transport is not open.
- Examples:
>>> mc.get_model() 'TestMagnet'
- abstract property heater: bool¶
Return the persistent switch heater state.
- Returns:
- (bool):
Truewhen the persistent switch heater is energised,Falsewhen it is off.
- Raises:
- ConnectionError:
If the transport is not open.
- abstractmethod heater_off() None[source]¶
De-energise the persistent switch heater.
- Raises:
- ConnectionError:
If the transport is not open.
- abstractmethod heater_on() None[source]¶
Energise the persistent switch heater.
- Raises:
- ConnectionError:
If the transport is not open.
- abstract property limits: MagnetLimits¶
Return the configured operating limits.
- Returns:
- (MagnetLimits):
Maximum permitted current, field, and ramp rate.
- Raises:
- ConnectionError:
If the transport is not open.
- abstract property magnet_constant: float¶
Return the magnet constant in tesla per amp.
- Returns:
- (float):
Field-to-current conversion factor in T A⁻¹.
- Raises:
- ConnectionError:
If the transport is not open.
- abstractmethod pause_ramp() None[source]¶
Pause an active ramp, holding the output at its current value.
- Raises:
- ConnectionError:
If the transport is not open.
- abstractmethod ramp_to_current(current: float, *, wait: bool = False) None[source]¶
Set a new target current and begin ramping.
- Args:
- current (float):
Desired target current in amps.
- Keyword Parameters:
- wait (bool):
If
True, block until the target is reached. Defaults toFalse.
- Raises:
- ConnectionError:
If the transport is not open.
- ValueError:
If current exceeds the configured maximum.
- abstractmethod ramp_to_field(field: float, *, wait: bool = False) None[source]¶
Set a new target field and begin ramping.
- Args:
- field (float):
Desired target field in tesla.
- Keyword Parameters:
- wait (bool):
If
True, block until the target is reached. Defaults toFalse.
- Raises:
- ConnectionError:
If the transport is not open.
- ValueError:
If field exceeds the configured maximum.
- abstractmethod ramp_to_target() None[source]¶
Start ramping the output towards the currently programmed target.
- Raises:
- ConnectionError:
If the transport is not open.
- abstractmethod set_limits(limits: MagnetLimits) None[source]¶
Set operating limits for the controller.
- Args:
- limits (MagnetLimits):
Maximum current, field, and ramp rate limits to apply.
- Raises:
- ConnectionError:
If the transport is not open.
- abstractmethod set_magnet_constant(tesla_per_amp: float) None[source]¶
Set the magnet constant used for field calculations.
- Args:
- tesla_per_amp (float):
Field-to-current conversion factor in T A⁻¹.
- Raises:
- ConnectionError:
If the transport is not open.
- ValueError:
If tesla_per_amp is not positive.
- abstractmethod set_ramp_rate_current(rate: float) None[source]¶
Set the current ramp rate.
- Args:
- rate (float):
Ramp rate in amps per second.
- Raises:
- ConnectionError:
If the transport is not open.
- ValueError:
If rate exceeds the configured maximum or is negative.
- abstractmethod set_ramp_rate_field(rate: float) None[source]¶
Set the field ramp rate.
- Args:
- rate (float):
Ramp rate in tesla per minute.
- Raises:
- ConnectionError:
If the transport is not open.
- ValueError:
If rate exceeds the configured maximum or is negative.
- abstractmethod set_target_current(current: float) None[source]¶
Set the target output current.
- Args:
- current (float):
Desired target current in amps.
- Raises:
- ConnectionError:
If the transport is not open.
- ValueError:
If current exceeds the configured maximum.
- abstractmethod set_target_field(field: float) None[source]¶
Set the target magnetic field.
- Args:
- field (float):
Desired target field in tesla.
- Raises:
- ConnectionError:
If the transport is not open.
- ValueError:
If field exceeds the configured maximum.
- abstract property status: MagnetStatus¶
Return a consolidated status snapshot.
- Returns:
- (MagnetStatus):
Current operational state, output readings, and heater status.
- Raises:
- ConnectionError:
If the transport is not open.
- class stoner_measurement.instruments.MagnetLimits(max_current: float, max_field: float | None = None, max_ramp_rate: float | None = None)[source]¶
Bases:
objectOperating limits for a superconducting magnet power supply.
- Attributes:
- max_current (float):
Maximum permitted output current in amps.
- max_field (float | None):
Maximum permitted field in tesla, or
Noneif not configured.- max_ramp_rate (float | None):
Maximum permitted ramp rate in amps per second or tesla per minute (instrument-specific units), or
Noneif not configured.
- class stoner_measurement.instruments.MagnetState(*values)[source]¶
Bases:
EnumOperational state of a superconducting magnet power supply.
- Attributes:
- STANDBY:
The supply is powered but not actively ramping.
- RAMPING:
The output is being ramped towards the programmed target.
- AT_TARGET:
The output has reached the programmed target field or current.
- PERSISTENT:
The magnet is in persistent mode (heater off, leads de-energised).
- QUIESCENT:
The supply is in a low-power idle state.
- FAULT:
A recoverable fault condition has been detected.
- QUENCH:
A quench has been detected; the magnet protection circuit has discharged the stored energy.
- UNKNOWN:
The state cannot be determined from the instrument response.
- AT_TARGET = 'at_target'¶
- FAULT = 'fault'¶
- PERSISTENT = 'persistent'¶
- QUENCH = 'quench'¶
- QUIESCENT = 'quiescent'¶
- RAMPING = 'ramping'¶
- STANDBY = 'standby'¶
- UNKNOWN = 'unknown'¶
- class stoner_measurement.instruments.MagnetStatus(state: MagnetState, current: float, field: float | None, voltage: float | None, persistent: bool, heater_on: bool | None, at_target: bool, message: str | None = None)[source]¶
Bases:
objectConsolidated status snapshot of a magnet power supply.
- Attributes:
- state (MagnetState):
Current operational state of the supply.
- current (float):
Output current in amps.
- field (float | None):
Estimated magnetic field in tesla, or
Noneif the magnet constant is not configured.- voltage (float | None):
Output voltage in volts, or
Noneif not reported by the instrument.- persistent (bool):
Truewhen the supply is operating in persistent mode.- heater_on (bool | None):
Truewhen the persistent switch heater is energised,Falsewhen it is off, orNoneif the state is unknown.- at_target (bool):
Truewhen the output has reached the programmed target.- message (str | None):
Optional human-readable status or error message from the instrument, or
Noneif no message is available.
- state: MagnetState = <dataclasses._MISSING_TYPE object>¶
- class stoner_measurement.instruments.MeasureFunction(*values)[source]¶
Bases:
EnumMeasurement function selected on an SMU.
- Attributes:
- VOLT:
Voltage measurement.
- CURR:
Current measurement.
- RES:
Resistance measurement (4-wire or 2-wire depending on driver configuration).
- POW:
Power (derived from simultaneous voltage and current readings).
- CURR = 'CURR'¶
- POW = 'POW'¶
- RES = 'RES'¶
- VOLT = 'VOLT'¶
- class stoner_measurement.instruments.Nanovoltmeter(transport: BaseTransport, protocol: BaseProtocol)[source]¶
Bases:
BaseInstrumentAbstract base class for nanovoltmeter instruments.
Provides a uniform interface for high-sensitivity DC voltage measurements. Nanovoltmeters are commonly used in low-resistance measurements, Hall-effect measurements, and thermoelectric studies.
- Attributes:
- transport (BaseTransport):
Transport layer instance.
- protocol (BaseProtocol):
Protocol instance.
- Examples:
>>> # Demonstrate interface using a minimal concrete implementation >>> from stoner_measurement.instruments.transport import NullTransport >>> from stoner_measurement.instruments.protocol import ScpiProtocol >>> from stoner_measurement.instruments.nanovoltmeter import Nanovoltmeter >>> class _NVM(Nanovoltmeter): ... def measure_voltage(self): return 1.23e-6 ... def get_range(self): return 0.1 ... def set_range(self, value): pass ... def get_autorange(self): return True ... def set_autorange(self, state): pass ... def get_nplc(self): return 5.0 ... def set_nplc(self, value): pass >>> nvm = _NVM(NullTransport(), ScpiProtocol()) >>> nvm.measure_voltage() 1.23e-06
- abort() None[source]¶
Abort a running measurement sequence.
- Raises:
- NotImplementedError:
If trigger configuration is not supported by the instrument.
- clear_buffer() None[source]¶
Clear the instrument reading buffer.
- Raises:
- NotImplementedError:
If buffer operations are not supported by the instrument.
- abstractmethod get_autorange() bool[source]¶
Return
Trueif autorange is currently enabled.- Returns:
- (bool):
Truewhen the instrument selects the range automatically.
- Raises:
- ConnectionError:
If the transport is not open.
- get_buffer_count() int[source]¶
Return the number of readings currently stored in the buffer.
- Raises:
- NotImplementedError:
If buffer operations are not supported by the instrument.
- abstractmethod get_capabilities() NanovoltmeterCapabilities[source]¶
Return static capability metadata.
- Returns:
- (NanovoltmeterCapabilities):
Capability descriptor.
- get_filter_count() int[source]¶
Return the configured filter averaging count.
- Raises:
- NotImplementedError:
If filtering is not supported by the instrument.
- get_filter_enabled() bool[source]¶
Return whether filtering is enabled.
- Raises:
- NotImplementedError:
If filtering is not supported by the instrument.
- abstractmethod get_measure_function() NanovoltmeterFunction[source]¶
Return the active measurement function.
- Returns:
- (NanovoltmeterFunction):
Active measurement function.
- abstractmethod get_nplc() float[source]¶
Return the integration time in power-line cycles.
- Returns:
- (float):
Integration time in power-line cycles.
- Raises:
- ConnectionError:
If the transport is not open.
- abstractmethod get_range() float[source]¶
Return the current voltage measurement range in volts.
- Returns:
- (float):
Active measurement range in volts.
0.0typically indicates autorange is active.
- Raises:
- ConnectionError:
If the transport is not open.
- get_trigger_count() int[source]¶
Return the configured trigger count.
- Raises:
- NotImplementedError:
If trigger configuration is not supported by the instrument.
- get_trigger_source() NanovoltmeterTriggerSource[source]¶
Return the trigger source selection.
- Raises:
- NotImplementedError:
If trigger configuration is not supported by the instrument.
- initiate() None[source]¶
Arm the trigger system and begin measurements.
- Raises:
- NotImplementedError:
If trigger configuration is not supported by the instrument.
- abstractmethod measure_voltage() float[source]¶
Trigger a voltage measurement and return the result in volts.
- Returns:
- (float):
Measured voltage in volts.
- Raises:
- ConnectionError:
If the transport is not open.
- read_buffer(count: int | None = None) tuple[float, ...][source]¶
Read values from the instrument buffer.
- Keyword Parameters:
- count (int | None):
Optional number of points to read from the start of the buffer. If
None, read all available points.
- Returns:
- (tuple[float, …]):
Parsed buffer values.
- Raises:
- NotImplementedError:
If buffer operations are not supported by the instrument.
- abstractmethod set_autorange(state: bool) None[source]¶
Enable or disable automatic range selection.
- Args:
- state (bool):
Trueto enable autorange,Falseto use the manually set range.
- Raises:
- ConnectionError:
If the transport is not open.
- set_filter_count(count: int) None[source]¶
Set the filter averaging count.
- Raises:
- NotImplementedError:
If filtering is not supported by the instrument.
- set_filter_enabled(state: bool) None[source]¶
Enable or disable measurement filtering.
- Raises:
- NotImplementedError:
If filtering is not supported by the instrument.
- abstractmethod set_measure_function(function: NanovoltmeterFunction) None[source]¶
Set the active measurement function.
- Args:
- function (NanovoltmeterFunction):
Function to select.
- abstractmethod set_nplc(value: float) None[source]¶
Set the integration time in power-line cycles.
- Args:
- value (float):
Integration time in power-line cycles. Longer integration times improve resolution but reduce throughput.
- Raises:
- ConnectionError:
If the transport is not open.
- ValueError:
If value is outside the valid range for this instrument.
- abstractmethod set_range(value: float) None[source]¶
Set the voltage measurement range.
- Args:
- value (float):
Measurement range in volts. Pass
0.0to enable autorange on instruments that conflate the two settings.
- Raises:
- ConnectionError:
If the transport is not open.
- ValueError:
If value is not a valid range for this instrument.
- set_trigger_count(count: int) None[source]¶
Set the trigger count.
- Raises:
- NotImplementedError:
If trigger configuration is not supported by the instrument.
- set_trigger_source(source: NanovoltmeterTriggerSource) None[source]¶
Set the trigger source selection.
- Raises:
- NotImplementedError:
If trigger configuration is not supported by the instrument.
- class stoner_measurement.instruments.NanovoltmeterCapabilities(has_function_selection: bool = False, has_filter: bool = False, has_trigger: bool = False, has_buffer: bool = False, supported_functions: tuple[NanovoltmeterFunction, ...] = (NanovoltmeterFunction.VOLT,))[source]¶
Bases:
objectStatic capability descriptor for a nanovoltmeter driver.
- supported_functions: tuple[NanovoltmeterFunction, ...] = (NanovoltmeterFunction.VOLT,)¶
- class stoner_measurement.instruments.NanovoltmeterFunction(*values)[source]¶
Bases:
EnumMeasurement functions for nanovoltmeters.
- TEMP = 'TEMP'¶
- VOLT = 'VOLT'¶
- class stoner_measurement.instruments.NanovoltmeterTriggerSource(*values)[source]¶
Bases:
EnumTrigger-source selection for nanovoltmeters.
- BUS = 'BUS'¶
- EXT = 'EXT'¶
- IMM = 'IMM'¶
- MAN = 'MAN'¶
- TIM = 'TIM'¶
- class stoner_measurement.instruments.PIDParameters(p: float, i: float, d: float)[source]¶
Bases:
objectPID gain parameters for one control loop.
- Attributes:
- p (float):
Proportional gain (dimensionless; instrument-specific range).
- i (float):
Integral gain, sometimes labelled reset or Ti (units instrument-specific; commonly seconds or repeats/minute).
- d (float):
Derivative gain, sometimes labelled rate or Td (units instrument-specific; commonly seconds).
- class stoner_measurement.instruments.PulsedSweepConfiguration(width: float, off_time: float, low_level: float = 0.0)[source]¶
Bases:
objectPulse parameters for a pulsed current sweep.
When used alongside
CurrentSweepConfiguration, each point in the sweep becomes a pulse: the output ramps to the programmed current for width seconds, then falls to low_level for off_time seconds before the next point.- Attributes:
- width (float):
Pulse-on duration in seconds.
- off_time (float):
Duration at low_level between pulses, in seconds.
- low_level (float):
Output current during the off phase, in amps. Defaults to
0.0.
- class stoner_measurement.instruments.RampState(*values)[source]¶
Bases:
EnumWhether a control loop’s setpoint is currently being ramped.
- Attributes:
- IDLE:
No active ramp; setpoint is at the programmed value.
- RAMPING:
Setpoint is being ramped towards the target at the programmed rate.
- IDLE = 'idle'¶
- RAMPING = 'ramping'¶
- class stoner_measurement.instruments.SensorStatus(*values)[source]¶
Bases:
EnumValidity and range status of a temperature sensor reading.
- Attributes:
- OK:
Reading is within the calibrated range and appears valid.
- INVALID:
Reading cannot be trusted (e.g. sensor disconnected or failed).
- OVERRANGE:
Measured signal exceeds the upper calibration limit.
- UNDERRANGE:
Measured signal is below the lower calibration limit.
- FAULT:
Hardware fault detected on the sensor channel.
- FAULT = 'fault'¶
- INVALID = 'invalid'¶
- OK = 'ok'¶
- OVERRANGE = 'overrange'¶
- UNDERRANGE = 'underrange'¶
- class stoner_measurement.instruments.SourceMeter(transport: BaseTransport, protocol: BaseProtocol)[source]¶
Bases:
BaseInstrumentAbstract base class for source-measure unit (SMU) instruments.
A source-meter can source voltage or current while simultaneously measuring the complementary quantity, making it suitable for current-voltage (I–V) characterisation.
Subclasses must implement the core abstract methods. Optional capability methods raise
NotImplementedErrorby default; drivers override only those methods that their hardware supports. Callers should consultget_capabilities()to determine which optional features are available before invoking them.- Attributes:
- transport (BaseTransport):
Transport layer instance.
- protocol (BaseProtocol):
Protocol instance.
- Examples:
>>> # Demonstrate interface using a minimal concrete implementation >>> from stoner_measurement.instruments.transport import NullTransport >>> from stoner_measurement.instruments.protocol import ScpiProtocol >>> from stoner_measurement.instruments.source_meter import ( ... SourceMeter, SourceMode, SourceMeterCapabilities, ... ) >>> class _SM(SourceMeter): ... def get_source_mode(self): return SourceMode.VOLT ... def set_source_mode(self, mode): pass ... def get_source_level(self): return 1.0 ... def set_source_level(self, value): pass ... def get_compliance(self): return 0.1 ... def set_compliance(self, value): pass ... def get_nplc(self): return 1.0 ... def set_nplc(self, value): pass ... def measure_voltage(self): return 1.0 ... def measure_current(self): return 0.001 ... def output_enabled(self): return False ... def enable_output(self, state): pass ... def get_capabilities(self): return SourceMeterCapabilities() >>> sm = _SM(NullTransport(), ScpiProtocol()) >>> sm.get_source_mode() <SourceMode.VOLT: 'VOLT'> >>> sm.get_capabilities().has_sweep False
- abort() None[source]¶
Abort trigger execution.
- Raises:
- NotImplementedError:
If the driver does not support trigger abort. Check
SourceMeterCapabilities.has_trigger_modelbefore calling.
- clear_buffer() None[source]¶
Clear the instrument reading buffer.
- Raises:
- NotImplementedError:
If the driver does not support reading buffer control. Check
SourceMeterCapabilities.has_bufferbefore calling.
- configure_custom_sweep(values: tuple[float, ...], *, delay: float = 0.0) None[source]¶
Configure a custom point-by-point source sweep.
- Args:
- values (tuple[float, …]):
Explicit source values to program.
- Keyword Parameters:
- delay (float):
Source settling delay between points in seconds.
- Raises:
- NotImplementedError:
If the driver does not support source sweep configuration. Check
SourceMeterCapabilities.has_sweepbefore calling.
- configure_linear_sweep(start: float, stop: float, points: int, *, delay: float = 0.0) None[source]¶
Configure a linear source sweep.
- Args:
- start (float):
Sweep start value in source units.
- stop (float):
Sweep stop value in source units.
- points (int):
Number of points in the sweep.
- Keyword Parameters:
- delay (float):
Source settling delay between sweep points in seconds.
- Raises:
- NotImplementedError:
If the driver does not support source sweep configuration. Check
SourceMeterCapabilities.has_sweepbefore calling.
- Examples:
>>> from stoner_measurement.instruments.transport import NullTransport >>> from stoner_measurement.instruments.protocol import ScpiProtocol >>> from stoner_measurement.instruments.source_meter import ( ... SourceMeter, SourceMode, SourceMeterCapabilities, ... ) >>> class _SM(SourceMeter): ... def get_source_mode(self): return SourceMode.VOLT ... def set_source_mode(self, mode): pass ... def get_source_level(self): return 0.0 ... def set_source_level(self, value): pass ... def get_compliance(self): return 0.0 ... def set_compliance(self, value): pass ... def get_nplc(self): return 1.0 ... def set_nplc(self, value): pass ... def measure_voltage(self): return 0.0 ... def measure_current(self): return 0.0 ... def output_enabled(self): return False ... def enable_output(self, state): pass ... def get_capabilities(self): return SourceMeterCapabilities() >>> _SM(NullTransport(), ScpiProtocol()).configure_linear_sweep(0.0, 1.0, 11, delay=0.01) Traceback (most recent call last): ... NotImplementedError: _SM does not support source sweep configuration. ...
- configure_log_sweep(start: float, stop: float, points: int, *, delay: float = 0.0) None[source]¶
Configure a logarithmic source sweep.
- Args:
- start (float):
Sweep start value in source units.
- stop (float):
Sweep stop value in source units.
- points (int):
Number of points in the sweep.
- Keyword Parameters:
- delay (float):
Source settling delay between sweep points in seconds.
- Raises:
- NotImplementedError:
If the driver does not support source sweep configuration. Check
SourceMeterCapabilities.has_sweepbefore calling.
- configure_source_sweep(config: SourceSweepConfiguration) None[source]¶
Configure a source sweep.
- Args:
- config (SourceSweepConfiguration):
Source sweep configuration.
- Raises:
- NotImplementedError:
If the driver does not support source sweep configuration. Check
SourceMeterCapabilities.has_sweepbefore calling.
- configure_trigger_model(config: TriggerModelConfiguration) None[source]¶
Configure trigger and arm behaviour.
- Args:
- config (TriggerModelConfiguration):
Trigger and arm model configuration.
- Raises:
- NotImplementedError:
If the driver does not support trigger model configuration. Check
SourceMeterCapabilities.has_trigger_modelbefore calling.
- abstractmethod enable_output(state: bool) None[source]¶
Enable or disable the source output.
- Args:
- state (bool):
Trueto enable the output,Falseto disable it.
- Raises:
- ConnectionError:
If the transport is not open.
- get_buffer_size() int[source]¶
Return reading buffer capacity.
- Returns:
- (int):
Number of readings that the buffer can store.
- Raises:
- NotImplementedError:
If the driver does not support reading buffer control. Check
SourceMeterCapabilities.has_bufferbefore calling.
- abstractmethod get_capabilities() SourceMeterCapabilities[source]¶
Return the static capability descriptor for this SMU driver.
- Returns:
- (SourceMeterCapabilities):
Descriptor advertising which optional feature groups are supported by this driver.
- Examples:
>>> caps = sm.get_capabilities() >>> caps.has_sweep False
- abstractmethod get_compliance() float[source]¶
Return the compliance limit (A in voltage mode, V in current mode).
- Returns:
- (float):
Compliance value in amps or volts.
- Raises:
- ConnectionError:
If the transport is not open.
- get_measure_functions() tuple[MeasureFunction, ...][source]¶
Return enabled measurement functions.
- Returns:
- (tuple[MeasureFunction, …]):
Enabled measurement functions.
- Raises:
- NotImplementedError:
If the driver does not support function selection. Check
SourceMeterCapabilities.has_function_selectionbefore calling.
- abstractmethod get_nplc() float[source]¶
Return the integration time in power-line cycles (NPLC).
- Returns:
- (float):
Integration time in power-line cycles.
- Raises:
- ConnectionError:
If the transport is not open.
- get_source_delay() float[source]¶
Return source delay in seconds.
- Returns:
- (float):
Source delay in seconds.
- Raises:
- NotImplementedError:
If the driver does not support source delay control. Check
SourceMeterCapabilities.has_source_delaybefore calling.
- abstractmethod get_source_level() float[source]¶
Return the programmed source level (V or A).
- Returns:
- (float):
Source amplitude in volts or amps depending on
get_source_mode().
- Raises:
- ConnectionError:
If the transport is not open.
- abstractmethod get_source_mode() SourceMode[source]¶
Return the current source mode.
- initiate() None[source]¶
Arm and initiate acquisition.
- Raises:
- NotImplementedError:
If the driver does not support trigger initiation. Check
SourceMeterCapabilities.has_trigger_modelbefore calling.
- abstractmethod measure_current() float[source]¶
Trigger a current measurement and return the result in amps.
- Returns:
- (float):
Measured current in amps.
- Raises:
- ConnectionError:
If the transport is not open.
- measure_power() float[source]¶
Return power calculated from measured voltage and current.
- Returns:
- (float):
Electrical power in watts.
- measure_resistance() float[source]¶
Return resistance calculated from measured voltage and current.
- Returns:
- (float):
Resistance in ohms.
- Raises:
- ZeroDivisionError:
If the measured current magnitude is below
1e-12A.
- Notes:
Currents with absolute magnitude smaller than
_MIN_CURRENT_FOR_RESISTANCE_CALCULATIONare treated as zero to avoid numerically unstable resistance values.
- abstractmethod measure_voltage() float[source]¶
Trigger a voltage measurement and return the result in volts.
- Returns:
- (float):
Measured voltage in volts.
- Raises:
- ConnectionError:
If the transport is not open.
- abstractmethod output_enabled() bool[source]¶
Return
Trueif the source output is currently enabled.- Returns:
- (bool):
Truewhen the output is on,Falsewhen off.
- Raises:
- ConnectionError:
If the transport is not open.
- read_buffer(count: int | None = None) tuple[float, ...][source]¶
Return readings from the instrument buffer.
- Args:
- count (int | None):
Optional count of readings to request.
- Returns:
- (tuple[float, …]):
Flat tuple of numeric readings from the instrument buffer.
- Raises:
- NotImplementedError:
If the driver does not support reading buffer control. Check
SourceMeterCapabilities.has_bufferbefore calling.
- set_buffer_size(size: int) None[source]¶
Set reading buffer capacity.
- Args:
- size (int):
Number of readings the instrument should retain.
- Raises:
- NotImplementedError:
If the driver does not support reading buffer control. Check
SourceMeterCapabilities.has_bufferbefore calling.
- abstractmethod set_compliance(value: float) None[source]¶
Set the compliance limit.
- Args:
- value (float):
Compliance in amps (voltage mode) or volts (current mode).
- Raises:
- ConnectionError:
If the transport is not open.
- ValueError:
If value exceeds the instrument’s maximum compliance.
- set_measure_functions(functions: tuple[MeasureFunction, ...]) None[source]¶
Enable one or more measurement functions.
- Args:
- functions (tuple[MeasureFunction, …]):
Sequence of measurement functions to enable.
- Raises:
- NotImplementedError:
If the driver does not support function selection. Check
SourceMeterCapabilities.has_function_selectionbefore calling.
- abstractmethod set_nplc(value: float) None[source]¶
Set the integration time in power-line cycles.
- Args:
- value (float):
Integration time in power-line cycles. Typical range is 0.01–10.
- Raises:
- ConnectionError:
If the transport is not open.
- ValueError:
If value is outside the valid range.
- set_source_delay(delay: float) None[source]¶
Set source delay in seconds before each measurement trigger.
- Args:
- delay (float):
Source delay in seconds.
- Raises:
- NotImplementedError:
If the driver does not support source delay control. Check
SourceMeterCapabilities.has_source_delaybefore calling.
- class stoner_measurement.instruments.SourceMeterCapabilities(has_function_selection: bool = False, has_sweep: bool = False, has_source_delay: bool = False, has_trigger_model: bool = False, has_buffer: bool = False)[source]¶
Bases:
objectStatic capability descriptor for a source-meter driver.
- Attributes:
- has_function_selection (bool):
Trueif the driver supports selecting measurement functions viaget_measure_functions()andset_measure_functions().- has_sweep (bool):
Trueif the driver supports built-in source sweeps viaconfigure_source_sweep().- has_source_delay (bool):
Trueif the driver supports a programmable source delay viaget_source_delay()andset_source_delay().- has_trigger_model (bool):
Trueif the driver supports trigger and arm model configuration viaconfigure_trigger_model(),initiate(), andabort().- has_buffer (bool):
Trueif the driver supports a reading buffer viaset_buffer_size(),get_buffer_size(),clear_buffer(), andread_buffer().
- class stoner_measurement.instruments.SourceMode(*values)[source]¶
Bases:
EnumSource output mode of an SMU.
- Attributes:
- VOLT:
Voltage source mode; the instrument outputs a programmed voltage and measures current (or resistance).
- CURR:
Current source mode; the instrument outputs a programmed current and measures voltage (or resistance).
- CURR = 'CURR'¶
- VOLT = 'VOLT'¶
- class stoner_measurement.instruments.SourceSweepConfiguration(start: float = 0.0, stop: float = 0.0, points: int = 0, spacing: SweepSpacing = SweepSpacing.LIN, values: tuple[float, ...] | None = None, delay: float = 0.0)[source]¶
Bases:
objectConfiguration for a source sweep.
- Attributes:
- start (float):
Sweep start value in source units. Ignored for list sweeps.
- stop (float):
Sweep stop value in source units. Ignored for list sweeps.
- points (int):
Number of sweep points. For list sweeps this is inferred from
len(values)by convenience wrappers but must be supplied explicitly when constructing this dataclass directly.- spacing (SweepSpacing):
Point-spacing mode. Defaults to
LIN.- values (tuple[float, …] | None):
Explicit source values for
LISTsweeps. Ignored for linear and logarithmic sweeps.- delay (float):
Source settling delay between sweep points in seconds. A value of
0.0disables the added delay.
- Notes:
Default
start,stop, andpointsvalues are placeholders. Callers should provide values appropriate for the selected spacing mode. For list sweeps, usevaluesand setpointstolen(values). The defaultdelayof0.0seconds disables added settling time.
- spacing: SweepSpacing = 'LIN'¶
- class stoner_measurement.instruments.SweepSpacing(*values)[source]¶
Bases:
EnumPoint-spacing mode for a built-in source sweep.
- Attributes:
- LIN:
Linearly spaced sweep points from start to stop.
- LOG:
Logarithmically spaced sweep points from start to stop.
- LIST:
Arbitrary point list supplied in
SourceSweepConfiguration.values.
- LIN = 'LIN'¶
- LIST = 'LIST'¶
- LOG = 'LOG'¶
- class stoner_measurement.instruments.TemperatureController(transport: BaseTransport, protocol: BaseProtocol)[source]¶
Bases:
BaseInstrumentAbstract base class for temperature controller instruments.
Provides a uniform interface for reading temperatures, managing setpoints, and controlling heater output across a range of cryogenic and laboratory temperature controllers including the Lakeshore 335, 336, 340 and 350, the Oxford Instruments ITC503, and the Oxford Instruments Mercury iTC. All temperature values are in Kelvin unless otherwise stated.
Subclasses must implement the seventeen core abstract methods. Optional capability methods raise
NotImplementedErrorby default; drivers override only those methods that their hardware supports. Callers should consultget_capabilities()to determine which optional features are available before invoking them.- Attributes:
- transport (BaseTransport):
Transport layer instance.
- protocol (BaseProtocol):
Protocol instance.
- Examples:
>>> from stoner_measurement.instruments.transport import NullTransport >>> from stoner_measurement.instruments.protocol import LakeshoreProtocol >>> from stoner_measurement.instruments.temperature_controller import ( ... TemperatureController, ControlMode, SensorStatus, ... ControllerCapabilities, PIDParameters, ... ) >>> class _TC(TemperatureController): ... def get_temperature(self, channel): return 300.0 ... def get_sensor_status(self, channel): return SensorStatus.OK ... def get_input_channel(self, loop): return "A" ... def set_input_channel(self, loop, channel): pass ... def get_setpoint(self, loop): return 300.0 ... def set_setpoint(self, loop, value): pass ... def get_loop_mode(self, loop): return ControlMode.CLOSED_LOOP ... def set_loop_mode(self, loop, mode): pass ... def get_heater_output(self, loop): return 50.0 ... def set_heater_range(self, loop, range_): pass ... def get_pid(self, loop): return PIDParameters(50.0, 1.0, 0.0) ... def set_pid(self, loop, p, i, d): pass ... def get_ramp_rate(self, loop): return 5.0 ... def set_ramp_rate(self, loop, rate): pass ... def get_ramp_enabled(self, loop): return False ... def set_ramp_enabled(self, loop, enabled): pass ... def get_capabilities(self): ... return ControllerCapabilities( ... num_inputs=2, num_loops=1, ... input_channels=("A", "B"), loop_numbers=(1,), ... ) >>> tc = _TC(NullTransport(), LakeshoreProtocol()) >>> tc.get_temperature("A") 300.0 >>> tc.get_capabilities().num_loops 1
- get_alarm_limits(channel: str) tuple[float, float][source]¶
Return the
(low, high)alarm limits for sensor channel in Kelvin.- Args:
- channel (str):
Sensor channel identifier.
- Returns:
- (tuple[float, float]):
(low_limit, high_limit)in Kelvin.
- Raises:
- NotImplementedError:
If the driver does not support alarm limits. Check
ControllerCapabilities.has_alarmbefore calling.- ConnectionError:
If the transport is not open.
- get_alarm_state(channel: str) AlarmState[source]¶
Return the current alarm state for sensor channel.
- Args:
- channel (str):
Sensor channel identifier.
- Returns:
- (AlarmState):
Current alarm condition.
- Raises:
- NotImplementedError:
If the driver does not support alarm monitoring. Check
ControllerCapabilities.has_alarmbefore calling.- ConnectionError:
If the transport is not open.
- get_autotune_status(loop: int) str[source]¶
Return the current autotune status for loop as a string.
The returned string is instrument-specific; typical values include
"idle","running","complete", and"failed".- Args:
- loop (int):
Control loop number (1-based).
- Returns:
- (str):
Human-readable autotune status.
- Raises:
- NotImplementedError:
If the driver does not support autotuning. Check
ControllerCapabilities.has_autotunebefore calling.- ConnectionError:
If the transport is not open.
- abstractmethod get_capabilities() ControllerCapabilities[source]¶
Return the static capability descriptor for this controller driver.
- Returns:
- (ControllerCapabilities):
Descriptor advertising the number of inputs, loops, and which optional feature groups are supported.
- Examples:
>>> caps = tc.get_capabilities() >>> caps.num_loops 1 >>> caps.has_ramp True
- get_controller_status() TemperatureStatus[source]¶
Return a full snapshot of all channels and loops.
Uses
get_capabilities()to discover channels and loops, then assembles aTemperatureStatusfromget_temperature_reading()andget_loop_status().- Returns:
- (TemperatureStatus):
Snapshot of all sensor readings and control-loop states.
- Raises:
- ConnectionError:
If the transport is not open.
- get_excitation(channel: str) float[source]¶
Return the excitation level applied to sensor channel.
The excitation value and its units (voltage, current, or power) are instrument-specific. Common examples: Lakeshore resistive-excitation in μV, or Oxford Mercury excitation in μA.
- Args:
- channel (str):
Sensor channel identifier.
- Returns:
- (float):
Excitation level in instrument-native units.
- Raises:
- NotImplementedError:
If the driver does not support excitation configuration. Check
ControllerCapabilities.has_sensor_excitationbefore calling.- ConnectionError:
If the transport is not open.
- get_filter(channel: str) dict[str, object][source]¶
Return the digital filter settings for sensor channel.
The returned dictionary contains at minimum the keys
"enabled"(bool),"points"(int, number of readings averaged), and"window"(float, percentage window for filter reset).- Args:
- channel (str):
Sensor channel identifier.
- Returns:
- (dict[str, object]):
Filter settings keyed by name.
- Raises:
- NotImplementedError:
If the driver does not support filter configuration. Check
ControllerCapabilities.has_sensor_excitationbefore calling.- ConnectionError:
If the transport is not open.
- get_gas_flow() float[source]¶
Return the cryogen gas-flow valve position as a percentage.
- Returns:
- (float):
Gas-flow valve opening as a percentage (0–100 %).
- Raises:
- NotImplementedError:
If the driver does not support cryogen flow control. Check
ControllerCapabilities.has_cryogen_controlbefore calling.- ConnectionError:
If the transport is not open.
- abstractmethod get_heater_output(loop: int) float[source]¶
Return the heater output percentage for control loop.
- Args:
- loop (int):
Control loop number (1-based).
- Returns:
- (float):
Heater output as a percentage (0–100 %).
- Raises:
- ConnectionError:
If the transport is not open.
- Examples:
>>> tc.get_heater_output(1) 50.0
- abstractmethod get_input_channel(loop: int) str[source]¶
Return the sensor channel currently assigned to control loop.
- Args:
- loop (int):
Control loop number (1-based).
- Returns:
- (str):
Channel identifier of the sensor driving this loop.
- Raises:
- ConnectionError:
If the transport is not open.
- Examples:
>>> tc.get_input_channel(1) 'A'
- abstractmethod get_loop_mode(loop: int) ControlMode[source]¶
Return the active control mode for loop.
- Args:
- loop (int):
Control loop number (1-based).
- Returns:
- (ControlMode):
Current control mode of the loop.
- Raises:
- ConnectionError:
If the transport is not open.
- Examples:
>>> tc.get_loop_mode(1) <ControlMode.CLOSED_LOOP: 'closed_loop'>
- get_loop_status(loop: int) LoopStatus[source]¶
Return a comprehensive status snapshot for control loop.
Calls all relevant core abstract methods and assembles a
LoopStatusdataclass. Drivers that can retrieve all loop data in a single instrument query should override this method.- Args:
- loop (int):
Control loop number (1-based).
- Returns:
- (LoopStatus):
Complete current state of the control loop.
- Raises:
- ConnectionError:
If the transport is not open.
- get_needle_valve() float[source]¶
Return the needle-valve (gas-flow restrictor) position as a percentage.
- Returns:
- (float):
Needle-valve position as a percentage (0 = fully closed, 100 = fully open).
- Raises:
- NotImplementedError:
If the driver does not support needle-valve control. Check
ControllerCapabilities.has_cryogen_controlbefore calling.- ConnectionError:
If the transport is not open.
- get_num_zones(loop: int) int[source]¶
Return the number of configured zones for loop.
- Args:
- loop (int):
Control loop number (1-based).
- Returns:
- (int):
Number of zones configured in the zone table.
- Raises:
- NotImplementedError:
If the driver does not support zone control. Check
ControllerCapabilities.has_zonebefore calling.- ConnectionError:
If the transport is not open.
- abstractmethod get_pid(loop: int) PIDParameters[source]¶
Return the PID parameters for control loop.
- Args:
- loop (int):
Control loop number (1-based).
- Returns:
- (PIDParameters):
Current proportional, integral and derivative gain values.
- Raises:
- ConnectionError:
If the transport is not open.
- Examples:
>>> tc.get_pid(1) PIDParameters(p=50.0, i=1.0, d=0.0)
- abstractmethod get_ramp_enabled(loop: int) bool[source]¶
Return
Trueif setpoint ramping is enabled for loop.- Args:
- loop (int):
Control loop number (1-based).
- Returns:
- (bool):
Truewhen the ramp function is active.
- Raises:
- ConnectionError:
If the transport is not open.
- Examples:
>>> tc.get_ramp_enabled(1) False
- abstractmethod get_ramp_rate(loop: int) float[source]¶
Return the setpoint ramp rate for loop in Kelvin per minute.
- Args:
- loop (int):
Control loop number (1-based).
- Returns:
- (float):
Ramp rate in Kelvin per minute.
- Raises:
- ConnectionError:
If the transport is not open.
- Examples:
>>> tc.get_ramp_rate(1) 5.0
- get_ramp_state(loop: int) RampState[source]¶
Return the current ramp state for loop.
The default implementation infers the state from
get_ramp_enabled(); override in subclasses that can query the actual hardware ramp state directly.
- get_sensor_curve(channel: str) int[source]¶
Return the calibration curve number assigned to sensor channel.
- Args:
- channel (str):
Sensor channel identifier.
- Returns:
- (int):
Curve number (instrument-specific;
0typically means no curve assigned).
- Raises:
- NotImplementedError:
If the driver does not support user calibration curves. Check
ControllerCapabilities.has_user_curvesbefore calling.- ConnectionError:
If the transport is not open.
- abstractmethod get_sensor_status(channel: str) SensorStatus[source]¶
Return the validity/range status of the sensor on channel.
- Args:
- channel (str):
Sensor channel identifier (instrument-specific, e.g.
"A").
- Returns:
- (SensorStatus):
Current validity and range status of the sensor reading.
- Raises:
- ConnectionError:
If the transport is not open.
- Examples:
>>> tc.get_sensor_status("A") <SensorStatus.OK: 'ok'>
- abstractmethod get_setpoint(loop: int) float[source]¶
Return the current setpoint for control loop in Kelvin.
- Args:
- loop (int):
Control loop number (1-based).
- Returns:
- (float):
Setpoint temperature in Kelvin.
- Raises:
- ConnectionError:
If the transport is not open.
- Examples:
>>> tc.get_setpoint(1) 300.0
- abstractmethod get_temperature(channel: str) float[source]¶
Return the current temperature for channel in Kelvin.
- Args:
- channel (str):
Sensor channel identifier (instrument-specific, e.g.
"A").
- Returns:
- (float):
Current temperature in Kelvin.
- Raises:
- ConnectionError:
If the transport is not open.
- Examples:
>>> tc.get_temperature("A") 300.0
- get_temperature_reading(channel: str) TemperatureReading[source]¶
Return a combined temperature value and sensor status for channel.
Calls
get_temperature()andget_sensor_status()and packages the results into aTemperatureReading. Drivers that can fetch both in a single query should override this method.- Args:
- channel (str):
Sensor channel identifier.
- Returns:
- (TemperatureReading):
Current temperature in Kelvin with validity status.
- Raises:
- ConnectionError:
If the transport is not open.
- Examples:
>>> reading = tc.get_temperature_reading("A") >>> reading.value 300.0 >>> reading.status <SensorStatus.OK: 'ok'>
- get_zone(loop: int, zone_index: int) ZoneEntry[source]¶
Return the parameters of zone entry zone_index for loop.
- Args:
- loop (int):
Control loop number (1-based).
- zone_index (int):
Zone entry index (1-based).
- Returns:
- (ZoneEntry):
Zone parameters including PID gains, ramp rate, heater range and heater output power for this table entry.
- Raises:
- NotImplementedError:
If the driver does not support zone control. Check
ControllerCapabilities.has_zonebefore calling.- ConnectionError:
If the transport is not open.
- ramp_to_setpoint(loop: int, target: float, *, rate: float | None = None) None[source]¶
Set a new target setpoint for loop, optionally configuring the ramp rate.
This convenience method performs the typical “ramp to a new temperature” sequence:
If rate is given, set the ramp rate via
set_ramp_rate().Enable ramping via
set_ramp_enabled()(only when the driver advertiseshas_ramp).Write the new setpoint via
set_setpoint().
Callers that need to wait for the temperature to arrive should follow this call with
wait_for_setpoint().- Args:
- loop (int):
Control loop number (1-based).
- target (float):
Desired setpoint in Kelvin.
- Keyword Parameters:
- rate (float | None):
Ramp rate in Kelvin per minute. When provided, the rate is written to the instrument before the setpoint is updated. When
Nonethe currently programmed ramp rate is unchanged. Ignored if the driver does not advertisehas_ramp.
- Raises:
- ConnectionError:
If the transport is not open.
- ValueError:
If target or rate are outside the instrument’s valid range.
- Examples:
>>> tc.ramp_to_setpoint(1, 150.0, rate=5.0)
- set_alarm_enabled(channel: str, enabled: bool) None[source]¶
Enable or disable the alarm for sensor channel.
- Args:
- channel (str):
Sensor channel identifier.
- enabled (bool):
Trueto activate alarm checking,Falseto disable it.
- Raises:
- NotImplementedError:
If the driver does not support alarms. Check
ControllerCapabilities.has_alarmbefore calling.- ConnectionError:
If the transport is not open.
- set_alarm_limits(channel: str, low: float, high: float) None[source]¶
Set the low and high alarm thresholds for sensor channel.
- Args:
- channel (str):
Sensor channel identifier.
- low (float):
Low-alarm temperature threshold in Kelvin.
- high (float):
High-alarm temperature threshold in Kelvin.
- Raises:
- NotImplementedError:
If the driver does not support alarm limits. Check
ControllerCapabilities.has_alarmbefore calling.- ConnectionError:
If the transport is not open.
- set_excitation(channel: str, value: float) None[source]¶
Set the excitation level for sensor channel.
- Args:
- channel (str):
Sensor channel identifier.
- value (float):
Excitation level in instrument-native units.
- Raises:
- NotImplementedError:
If the driver does not support excitation configuration. Check
ControllerCapabilities.has_sensor_excitationbefore calling.- ConnectionError:
If the transport is not open.
- set_filter(channel: str, *, enabled: bool, points: int, window: float) None[source]¶
Configure the digital filter for sensor channel.
- Args:
- channel (str):
Sensor channel identifier.
- Keyword Parameters:
- enabled (bool):
Trueto activate the filter,Falseto disable it.- points (int):
Number of readings to average (1 effectively disables averaging).
- window (float):
Percentage deviation window that triggers a filter reset (0 disables window filtering).
- Raises:
- NotImplementedError:
If the driver does not support filter configuration. Check
ControllerCapabilities.has_sensor_excitationbefore calling.- ConnectionError:
If the transport is not open.
- set_gas_flow(percent: float) None[source]¶
Set the cryogen gas-flow valve to percent open.
- Args:
- percent (float):
Desired valve opening as a percentage (0–100 %).
- Raises:
- NotImplementedError:
If the driver does not support cryogen flow control. Check
ControllerCapabilities.has_cryogen_controlbefore calling.- ConnectionError:
If the transport is not open.
- abstractmethod set_heater_range(loop: int, range_: int) None[source]¶
Set the heater range for control loop.
The meaning of range_ is instrument-specific. A value of
0conventionally means “heater off”.- Args:
- loop (int):
Control loop number (1-based).
- range_ (int):
Heater range index (instrument-specific).
- Raises:
- ConnectionError:
If the transport is not open.
- abstractmethod set_input_channel(loop: int, channel: str) None[source]¶
Assign sensor channel as the input for control loop.
- Args:
- loop (int):
Control loop number (1-based).
- channel (str):
Sensor channel identifier to assign.
- Raises:
- ConnectionError:
If the transport is not open.
- ValueError:
If channel is not a valid sensor input for this instrument.
- abstractmethod set_loop_mode(loop: int, mode: ControlMode) None[source]¶
Set the control mode for loop.
- Args:
- loop (int):
Control loop number (1-based).
- mode (ControlMode):
Desired control mode.
- Raises:
- ConnectionError:
If the transport is not open.
- ValueError:
If mode is not supported by this loop on this instrument.
- set_needle_valve(position: float) None[source]¶
Set the needle-valve position.
- Args:
- position (float):
Desired position as a percentage (0 = fully closed, 100 = fully open).
- Raises:
- NotImplementedError:
If the driver does not support needle-valve control. Check
ControllerCapabilities.has_cryogen_controlbefore calling.- ConnectionError:
If the transport is not open.
- abstractmethod set_pid(loop: int, p: float, i: float, d: float) None[source]¶
Set the PID parameters for control loop.
- Args:
- loop (int):
Control loop number (1-based).
- p (float):
Proportional gain.
- i (float):
Integral gain (reset).
- d (float):
Derivative gain (rate).
- Raises:
- ConnectionError:
If the transport is not open.
- ValueError:
If any gain value is outside the instrument’s valid range.
- abstractmethod set_ramp_enabled(loop: int, enabled: bool) None[source]¶
Enable or disable setpoint ramping for loop.
- Args:
- loop (int):
Control loop number (1-based).
- enabled (bool):
Trueto activate the ramp function,Falseto disable it.
- Raises:
- ConnectionError:
If the transport is not open.
- abstractmethod set_ramp_rate(loop: int, rate: float) None[source]¶
Set the setpoint ramp rate for loop.
- Args:
- loop (int):
Control loop number (1-based).
- rate (float):
Ramp rate in Kelvin per minute. A value of
0typically disables ramping or sets an unlimited rate (instrument-specific).
- Raises:
- ConnectionError:
If the transport is not open.
- ValueError:
If rate is negative or exceeds the instrument’s maximum.
- set_sensor_curve(channel: str, curve_num: int) None[source]¶
Assign calibration curve curve_num to sensor channel.
- Args:
- channel (str):
Sensor channel identifier.
- curve_num (int):
Curve number to assign (instrument-specific).
- Raises:
- NotImplementedError:
If the driver does not support user calibration curves. Check
ControllerCapabilities.has_user_curvesbefore calling.- ConnectionError:
If the transport is not open.
- abstractmethod set_setpoint(loop: int, value: float) None[source]¶
Set the target temperature for control loop.
- Args:
- loop (int):
Control loop number (1-based).
- value (float):
Desired setpoint in Kelvin.
- Raises:
- ConnectionError:
If the transport is not open.
- ValueError:
If value is outside the instrument’s valid range.
- set_zone(loop: int, zone_index: int, entry: ZoneEntry) None[source]¶
Write a zone table entry for loop.
- Args:
- loop (int):
Control loop number (1-based).
- zone_index (int):
Zone entry index (1-based).
- entry (ZoneEntry):
Zone parameters to write, including PID gains, ramp rate, heater range and heater output power.
- Raises:
- NotImplementedError:
If the driver does not support zone control. Check
ControllerCapabilities.has_zonebefore calling.- ConnectionError:
If the transport is not open.
- start_autotune(loop: int, mode: int = 0) None[source]¶
Start the PID autotuning sequence for loop.
- Args:
- loop (int):
Control loop number (1-based).
- Keyword Parameters:
- mode (int):
Autotune mode (instrument-specific; commonly
0= P only,1= P+I,2= P+I+D). Defaults to0.
- Raises:
- NotImplementedError:
If the driver does not support autotuning. Check
ControllerCapabilities.has_autotunebefore calling.- ConnectionError:
If the transport is not open.
- wait_for_setpoint(loop: int, channel: str, *, tolerance: float = 0.5, timeout: float = 300.0, poll_period: float = 1.0) None[source]¶
Block until the temperature on channel is within tolerance of the setpoint.
Polls
get_temperature()andget_setpoint()at intervals of poll_period seconds until the absolute deviation falls within tolerance, or until timeout seconds have elapsed.- Args:
- loop (int):
Control loop whose setpoint is used as the target.
- channel (str):
Sensor channel to monitor.
- Keyword Parameters:
- tolerance (float):
Acceptable deviation from the setpoint in Kelvin. Defaults to
0.5.- timeout (float):
Maximum time to wait in seconds. Defaults to
300.0.- poll_period (float):
Interval between temperature polls in seconds. Defaults to
1.0.
- Raises:
- TimeoutError:
If the temperature does not reach the setpoint within timeout seconds.
- ConnectionError:
If the transport is not open.
- class stoner_measurement.instruments.TemperatureReading(value: float, status: SensorStatus, units: str = 'K')[source]¶
Bases:
objectA snapshot of a single sensor channel reading.
- Attributes:
- value (float):
Numeric sensor reading expressed in the units given by the units field. Drivers should convert to Kelvin and set
units="K"wherever possible; raw resistance or voltage readings should set the appropriate unit string instead.- status (SensorStatus):
Validity / range status of the reading.
- units (str):
Native units reported by the instrument (e.g.
"K","C","V","Ohm"). Defaults to"K".
- status: SensorStatus = <dataclasses._MISSING_TYPE object>¶
- class stoner_measurement.instruments.TemperatureStatus(temperatures: dict[str, TemperatureReading], loops: dict[int, LoopStatus], error_state: str | None = None)[source]¶
Bases:
objectA full controller status snapshot.
- Attributes:
- temperatures (dict[str, TemperatureReading]):
Mapping of channel identifier to sensor reading.
- loops (dict[int, LoopStatus]):
Mapping of loop number to loop status snapshot.
- error_state (str | None):
Human-readable error/fault description, or
Noneif the controller reports no fault.
- loops: dict[int, LoopStatus] = <dataclasses._MISSING_TYPE object>¶
- temperatures: dict[str, TemperatureReading] = <dataclasses._MISSING_TYPE object>¶
- class stoner_measurement.instruments.TriggerModelConfiguration(trigger_source: TriggerSource = TriggerSource.IMM, trigger_count: int = 1, trigger_delay: float = 0.0, arm_source: TriggerSource = TriggerSource.IMM, arm_count: int = 1)[source]¶
Bases:
objectConfiguration for simple trigger and arm models.
- Attributes:
- trigger_source (TriggerSource):
Source that advances the trigger layer. Defaults to
IMM.- trigger_count (int):
Number of times the trigger layer executes per arm. Defaults to
1.- trigger_delay (float):
Delay in seconds inserted before each measurement trigger. Defaults to
0.0.- arm_source (TriggerSource):
Source that advances the arm layer. Defaults to
IMM.- arm_count (int):
Number of times the arm layer executes. Defaults to
1.
- arm_source: TriggerSource = 'IMM'¶
- trigger_source: TriggerSource = 'IMM'¶
- class stoner_measurement.instruments.TriggerSource(*values)[source]¶
Bases:
EnumTrigger or arm source for a trigger-model configuration.
- Attributes:
- IMM:
Immediate (internal) — the layer completes as soon as it is entered.
- BUS:
IEEE-488 / GPIB bus trigger (
*TRGorGET).- EXT:
External hardware trigger input.
- TLIN:
Trigger link (LAN or digital I/O trigger bus, instrument-specific).
- TIM:
Internal timer-based trigger.
- BUS = 'BUS'¶
- EXT = 'EXT'¶
- IMM = 'IMM'¶
- TIM = 'TIM'¶
- TLIN = 'TLIN'¶
- class stoner_measurement.instruments.ZoneEntry(upper_bound: float, p: float, i: float, d: float, ramp_rate: float, heater_range: int, heater_output: float)[source]¶
Bases:
objectOne entry in a temperature controller’s zone / table control table.
Zone control divides the operating temperature range into consecutive segments. Each segment has its own PID gains, setpoint ramp rate, heater range, and manual heater output power. When the active setpoint crosses upper_bound the controller automatically loads the next zone’s parameters.
- Attributes:
- upper_bound (float):
Upper setpoint boundary for this zone in Kelvin. When the setpoint is at or below this value the zone’s PID and heater settings are applied.
- p (float):
Proportional gain for this zone.
- i (float):
Integral gain (reset) for this zone.
- d (float):
Derivative gain (rate) for this zone.
- ramp_rate (float):
Setpoint ramp rate for this zone in Kelvin per minute. A value of
0means the setpoint changes immediately (no ramp).- heater_range (int):
Heater range index for this zone (instrument-specific;
0conventionally means heater off).- heater_output (float):
Manual heater output power percentage for this zone (0–100 %). Used when the loop is in open-loop mode or as the initial value when entering the zone.
Keithley instrument drivers.
Contains concrete drivers for Keithley source-measure units
(Keithley2400,
Keithley2410,
Keithley2450),
precision current sources
(Keithley6221),
digital multimeters
(Keithley2000,
Keithley2700),
nanovoltmeters
(Keithley2182A,
Keithley182), and
electrometers/picoammeters
(Keithley6845,
Keithley6514,
Keithley6517).
- class stoner_measurement.instruments.keithley.Keithley182(transport: BaseTransport, protocol: BaseProtocol | None = None)[source]¶
Bases:
Keithley2182ADriver for the Keithley 182 nanovoltmeter.
- class stoner_measurement.instruments.keithley.Keithley2000(transport: BaseTransport, protocol: BaseProtocol | None = None)[source]¶
Bases:
DigitalMultimeterDriver for the Keithley 2000 digital multimeter.
- Attributes:
- transport (BaseTransport):
Transport layer (serial, GPIB, or Ethernet).
- protocol (BaseProtocol):
Protocol instance (defaults to
ScpiProtocol).
- get_autorange() bool[source]¶
Return
Trueif autorange is enabled.- Returns:
- (bool):
Truewhen autorange is active.
- get_buffer_count() int[source]¶
Return the number of readings currently stored in the buffer.
- Returns:
- (int):
Number of readings in the trace buffer.
- get_capabilities() DmmCapabilities[source]¶
Return static capability metadata for the Keithley 2000.
- Returns:
- (DmmCapabilities):
Capability descriptor.
- get_filter_count() int[source]¶
Return the averaging filter count.
- Returns:
- (int):
Configured number of readings averaged per sample.
- get_filter_enabled() bool[source]¶
Return
Trueif digital averaging filter is enabled.- Returns:
- (bool):
Truewhen the averaging filter is active.
- get_measure_function() DmmFunction[source]¶
Return the active measurement function.
- Returns:
- (DmmFunction):
Active measurement function.
- get_nplc() float[source]¶
Return the integration time in power-line cycles.
- Returns:
- (float):
Integration time in power-line cycles.
- get_range() float[source]¶
Return the active measurement range in units of the active function.
- Returns:
- (float):
Active measurement range.
- get_trigger_count() int[source]¶
Return the configured trigger count.
- Returns:
- (int):
Number of triggers configured.
- get_trigger_source() DmmTriggerSource[source]¶
Return the trigger source selection.
- Returns:
- (DmmTriggerSource):
Active trigger source.
- measure() float[source]¶
Trigger a measurement and return its value.
- Returns:
- (float):
Measured scalar value in units of the active function.
- read_buffer(count: int | None = None) tuple[float, ...][source]¶
Read values from the instrument trace buffer.
- Keyword Parameters:
- count (int | None):
Number of points to read from the start of the buffer. If
None, read all available points.
- Returns:
- (tuple[float, …]):
Parsed buffer readings.
- Raises:
- ValueError:
If count is not positive.
- set_autorange(state: bool) None[source]¶
Enable or disable autorange.
- Args:
- state (bool):
Trueto enable autorange.
- set_filter_count(count: int) None[source]¶
Set the averaging filter count.
- Args:
- count (int):
Number of readings to average. Must be positive.
- Raises:
- ValueError:
If count is not positive.
- set_filter_enabled(state: bool) None[source]¶
Enable or disable the digital averaging filter.
- Args:
- state (bool):
Trueto enable filtering.
- set_measure_function(function: DmmFunction) None[source]¶
Set the active measurement function.
- Args:
- function (DmmFunction):
Function to select.
- set_nplc(value: float) None[source]¶
Set the integration time in power-line cycles.
- Args:
- value (float):
Integration time in power-line cycles. Must be positive.
- Raises:
- ValueError:
If value is not positive.
- set_range(value: float) None[source]¶
Set the measurement range.
- Args:
- value (float):
Range value in units of the active function. Must be positive.
- Raises:
- ValueError:
If value is not positive.
- set_trigger_count(count: int) None[source]¶
Set the trigger count.
- Args:
- count (int):
Number of triggers. Must be positive.
- Raises:
- ValueError:
If count is not positive.
- set_trigger_source(source: DmmTriggerSource) None[source]¶
Set the trigger source.
- Args:
- source (DmmTriggerSource):
Trigger source to select.
- class stoner_measurement.instruments.keithley.Keithley2182A(transport: BaseTransport, protocol: BaseProtocol | None = None)[source]¶
Bases:
NanovoltmeterDriver for the Keithley 2182A nanovoltmeter.
- Attributes:
- transport (BaseTransport):
Transport layer (serial, GPIB, or Ethernet).
- protocol (BaseProtocol):
Protocol instance (defaults to
ScpiProtocol).
- get_autorange() bool[source]¶
Return
Trueif autorange is enabled.- Returns:
- (bool):
Truewhen autorange is active.
- get_buffer_count() int[source]¶
Return the number of readings currently stored in the buffer.
- Returns:
- (int):
Number of readings in the trace buffer.
- get_capabilities() NanovoltmeterCapabilities[source]¶
Return static capability metadata for the Keithley 2182A.
- Returns:
- (NanovoltmeterCapabilities):
Capability descriptor.
- get_filter_count() int[source]¶
Return the digital filter averaging count.
- Returns:
- (int):
Number of readings averaged per sample.
- get_filter_enabled() bool[source]¶
Return
Trueif the digital filter is enabled.- Returns:
- (bool):
Truewhen the digital filter is active.
- get_measure_function() NanovoltmeterFunction[source]¶
Return the active measurement function.
- Returns:
- (NanovoltmeterFunction):
Active measurement function.
- get_nplc() float[source]¶
Return the integration time in power-line cycles.
- Returns:
- (float):
Integration time in power-line cycles.
- get_range() float[source]¶
Return the active voltage measurement range in volts.
- Returns:
- (float):
Active measurement range in volts.
- get_trigger_count() int[source]¶
Return the configured trigger count.
- Returns:
- (int):
Number of triggers configured.
- get_trigger_source() NanovoltmeterTriggerSource[source]¶
Return the trigger source selection.
- Returns:
- (NanovoltmeterTriggerSource):
Active trigger source.
- measure_voltage() float[source]¶
Trigger a voltage measurement and return the result in volts.
- Returns:
- (float):
Measured voltage in volts.
- read_buffer(count: int | None = None) tuple[float, ...][source]¶
Read values from the instrument trace buffer.
- Keyword Parameters:
- count (int | None):
Number of points to read from the start of the buffer. If
None, read all available points.
- Returns:
- (tuple[float, …]):
Parsed buffer readings.
- Raises:
- ValueError:
If count is not positive.
- set_autorange(state: bool) None[source]¶
Enable or disable autorange.
- Args:
- state (bool):
Trueto enable autorange.
- set_filter_count(count: int) None[source]¶
Set the digital filter averaging count.
- Args:
- count (int):
Number of readings to average. Must be positive.
- Raises:
- ValueError:
If count is not positive.
- set_filter_enabled(state: bool) None[source]¶
Enable or disable the digital filter.
- Args:
- state (bool):
Trueto enable the filter.
- set_measure_function(function: NanovoltmeterFunction) None[source]¶
Set the active measurement function.
- Args:
- function (NanovoltmeterFunction):
Function to select.
- set_nplc(value: float) None[source]¶
Set the integration time in power-line cycles.
- Args:
- value (float):
Integration time in power-line cycles. Must be positive.
- Raises:
- ValueError:
If value is not positive.
- set_range(value: float) None[source]¶
Set the voltage measurement range in volts.
- Args:
- value (float):
Measurement range in volts. Must be positive.
- Raises:
- ValueError:
If value is not positive.
- set_trigger_count(count: int) None[source]¶
Set the trigger count.
- Args:
- count (int):
Number of triggers. Must be positive.
- Raises:
- ValueError:
If count is not positive.
- set_trigger_source(source: NanovoltmeterTriggerSource) None[source]¶
Set the trigger source.
- Args:
- source (NanovoltmeterTriggerSource):
Trigger source to select.
- class stoner_measurement.instruments.keithley.Keithley2400(transport: BaseTransport, protocol: BaseProtocol | None = None)[source]¶
Bases:
SourceMeterDriver for the Keithley 2400 Series SourceMeter.
Implements
SourceMeterusing SCPI commands specific to the Keithley 2400 instrument family. AScpiProtocolinstance is used by default; the transport must be supplied by the caller.- Attributes:
- transport (BaseTransport):
Transport layer (serial, GPIB, or Ethernet).
- protocol (BaseProtocol):
Protocol instance (defaults to
ScpiProtocol).
- Examples:
>>> from stoner_measurement.instruments.transport import NullTransport >>> from stoner_measurement.instruments.protocol import ScpiProtocol >>> from stoner_measurement.instruments.keithley import Keithley2400 >>> t = NullTransport(responses=[ ... b"KEITHLEY INSTRUMENTS INC.,MODEL 2400,1234567,C32 Mar 4 2011\n", ... ]) >>> k = Keithley2400(transport=t) >>> k.connect() >>> k.identify() 'KEITHLEY INSTRUMENTS INC.,MODEL 2400,1234567,C32 Mar 4 2011' >>> k.disconnect()
- abort() None[source]¶
Abort trigger execution.
- Raises:
- ConnectionError:
If the transport is not open.
- Examples:
>>> from stoner_measurement.instruments.transport import NullTransport >>> from stoner_measurement.instruments.keithley import Keithley2400 >>> t = NullTransport() >>> k = Keithley2400(transport=t) >>> k.connect() >>> k.abort() >>> t.write_log[-1] b':ABOR\n' >>> k.disconnect()
- clear_buffer() None[source]¶
Clear the reading buffer.
- Raises:
- ConnectionError:
If the transport is not open.
- Examples:
>>> from stoner_measurement.instruments.transport import NullTransport >>> from stoner_measurement.instruments.keithley import Keithley2400 >>> t = NullTransport() >>> k = Keithley2400(transport=t) >>> k.connect() >>> k.clear_buffer() >>> t.write_log[-1] b':TRAC:CLE\n' >>> k.disconnect()
- configure_source_sweep(config: SourceSweepConfiguration) None[source]¶
Configure linear, logarithmic, or list source sweeps.
- Args:
- config (SourceSweepConfiguration):
Sweep configuration payload.
- Raises:
- ConnectionError:
If the transport is not open.
- ValueError:
If the delay, point count, or list values are invalid.
- Examples:
>>> from stoner_measurement.instruments.transport import NullTransport >>> from stoner_measurement.instruments.keithley import Keithley2400 >>> from stoner_measurement.instruments.source_meter import ( ... SourceSweepConfiguration, SweepSpacing, ... ) >>> t = NullTransport(responses=[b"VOLT\n"]) >>> k = Keithley2400(transport=t) >>> k.connect() >>> k.configure_source_sweep( ... SourceSweepConfiguration( ... start=0.0, stop=1.0, points=5, ... spacing=SweepSpacing.LIN, delay=0.01, ... ) ... ) >>> t.write_log[-1] b':SOUR:DEL 0.01\n' >>> k.disconnect()
- configure_trigger_model(config: TriggerModelConfiguration) None[source]¶
Configure trigger and arm model settings.
- Args:
- config (TriggerModelConfiguration):
Trigger and arm model configuration.
- Raises:
- ConnectionError:
If the transport is not open.
- ValueError:
If trigger or arm counts are not positive, or trigger delay is negative.
- Examples:
>>> from stoner_measurement.instruments.transport import NullTransport >>> from stoner_measurement.instruments.keithley import Keithley2400 >>> from stoner_measurement.instruments.source_meter import ( ... TriggerModelConfiguration, TriggerSource, ... ) >>> t = NullTransport() >>> k = Keithley2400(transport=t) >>> k.connect() >>> k.configure_trigger_model( ... TriggerModelConfiguration( ... trigger_source=TriggerSource.BUS, ... trigger_count=2, ... trigger_delay=0.1, ... ) ... ) >>> t.write_log[0] b':TRIG:SOUR BUS\n' >>> k.disconnect()
- enable_output(state: bool) None[source]¶
Enable or disable the source output.
- Args:
- state (bool):
Trueto turn the output on,Falseto turn it off.
- Raises:
- ConnectionError:
If the transport is not open.
- Examples:
>>> from stoner_measurement.instruments.transport import NullTransport >>> from stoner_measurement.instruments.keithley import Keithley2400 >>> t = NullTransport() >>> k = Keithley2400(transport=t) >>> k.connect() >>> k.enable_output(True) >>> t.write_log[-1] b':OUTP:STAT 1\n' >>> k.enable_output(False) >>> t.write_log[-1] b':OUTP:STAT 0\n' >>> k.disconnect()
- get_buffer_size() int[source]¶
Return reading buffer capacity.
- Returns:
- (int):
Number of readings that can be stored.
- Raises:
- ConnectionError:
If the transport is not open.
- Examples:
>>> from stoner_measurement.instruments.transport import NullTransport >>> from stoner_measurement.instruments.keithley import Keithley2400 >>> t = NullTransport(responses=[b"250\n"]) >>> k = Keithley2400(transport=t) >>> k.connect() >>> k.get_buffer_size() 250 >>> k.disconnect()
- Notes:
Some instruments may return the buffer size in exponential format. The response is therefore parsed as
floatbefore conversion toint.
- get_capabilities() SourceMeterCapabilities[source]¶
Return the capability descriptor for the Keithley 2400 driver.
- Returns:
- (SourceMeterCapabilities):
Descriptor indicating that this driver supports measurement function selection, source sweeps, source delay, trigger/arm model configuration, and reading buffer control.
- Examples:
>>> from stoner_measurement.instruments.transport import NullTransport >>> from stoner_measurement.instruments.keithley import Keithley2400 >>> caps = Keithley2400(transport=NullTransport()).get_capabilities() >>> caps.has_sweep True >>> caps.has_buffer True
- get_compliance() float[source]¶
Return the compliance limit in amps (voltage mode) or volts (current mode).
- Returns:
- (float):
Compliance value.
- Raises:
- ConnectionError:
If the transport is not open.
- Examples:
>>> from stoner_measurement.instruments.transport import NullTransport >>> from stoner_measurement.instruments.keithley import Keithley2400 >>> t = NullTransport(responses=[b"1.000000E-01\n"]) >>> k = Keithley2400(transport=t) >>> k.connect() >>> k.get_compliance() 0.1 >>> k.disconnect()
- get_measure_functions() tuple[MeasureFunction, ...][source]¶
Return enabled measurement functions.
- get_nplc() float[source]¶
Return the integration time in power-line cycles.
- Returns:
- (float):
Integration time in power-line cycles.
- Raises:
- ConnectionError:
If the transport is not open.
- Examples:
>>> from stoner_measurement.instruments.transport import NullTransport >>> from stoner_measurement.instruments.keithley import Keithley2400 >>> t = NullTransport(responses=[b"1.000000E+00\n"]) >>> k = Keithley2400(transport=t) >>> k.connect() >>> k.get_nplc() 1.0 >>> k.disconnect()
- get_source_delay() float[source]¶
Return source delay before each measurement trigger.
- Returns:
- (float):
Source delay in seconds.
- Raises:
- ConnectionError:
If the transport is not open.
- Examples:
>>> from stoner_measurement.instruments.transport import NullTransport >>> from stoner_measurement.instruments.keithley import Keithley2400 >>> t = NullTransport(responses=[b"1.000000E-02\n"]) >>> k = Keithley2400(transport=t) >>> k.connect() >>> k.get_source_delay() 0.01 >>> k.disconnect()
- get_source_level() float[source]¶
Return the programmed source level in volts or amps.
- Returns:
- (float):
Source amplitude in the unit corresponding to the active source mode.
- Raises:
- ConnectionError:
If the transport is not open.
- Examples:
>>> from stoner_measurement.instruments.transport import NullTransport >>> from stoner_measurement.instruments.keithley import Keithley2400 >>> t = NullTransport(responses=[b"1.000000E+00\n"]) >>> k = Keithley2400(transport=t) >>> k.connect() >>> k.get_source_level() 1.0 >>> k.disconnect()
- get_source_mode() SourceMode[source]¶
Return the active source mode.
- Returns:
- (SourceMode):
VOLTif the instrument is sourcing voltage,CURRif it is sourcing current.
- Raises:
- ConnectionError:
If the transport is not open.
- Examples:
>>> from stoner_measurement.instruments.transport import NullTransport >>> from stoner_measurement.instruments.keithley import Keithley2400 >>> from stoner_measurement.instruments.source_meter import SourceMode >>> t = NullTransport(responses=[b"VOLT\n"]) >>> k = Keithley2400(transport=t) >>> k.connect() >>> k.get_source_mode() <SourceMode.VOLT: 'VOLT'> >>> k.disconnect()
- initiate() None[source]¶
Arm and start the trigger model.
- Raises:
- ConnectionError:
If the transport is not open.
- Examples:
>>> from stoner_measurement.instruments.transport import NullTransport >>> from stoner_measurement.instruments.keithley import Keithley2400 >>> t = NullTransport() >>> k = Keithley2400(transport=t) >>> k.connect() >>> k.initiate() >>> t.write_log[-1] b':INIT\n' >>> k.disconnect()
- measure_current() float[source]¶
Trigger a current measurement and return the result in amps.
Configures the sense function to current, triggers a single reading, and returns the parsed floating-point result.
- Returns:
- (float):
Measured current in amps.
- Raises:
- ConnectionError:
If the transport is not open.
- Examples:
>>> from stoner_measurement.instruments.transport import NullTransport >>> from stoner_measurement.instruments.keithley import Keithley2400 >>> t = NullTransport(responses=[b"+1.000000E-03\n"]) >>> k = Keithley2400(transport=t) >>> k.connect() >>> k.measure_current() 0.001 >>> k.disconnect()
- measure_power() float[source]¶
Trigger simultaneous voltage/current measurements and return power in watts.
- Returns:
- (float):
Measured power in watts.
- Raises:
- ConnectionError:
If the transport is not open.
- ValueError:
If the instrument response does not include both voltage and current.
- Examples:
>>> from stoner_measurement.instruments.transport import NullTransport >>> from stoner_measurement.instruments.keithley import Keithley2400 >>> t = NullTransport(responses=[b"+2.000000E+00,+5.000000E-01\n"]) >>> k = Keithley2400(transport=t) >>> k.connect() >>> k.measure_power() 1.0 >>> k.disconnect()
- measure_resistance() float[source]¶
Trigger a resistance measurement and return the result in ohms.
- Returns:
- (float):
Measured resistance in ohms.
- Raises:
- ConnectionError:
If the transport is not open.
- Examples:
>>> from stoner_measurement.instruments.transport import NullTransport >>> from stoner_measurement.instruments.keithley import Keithley2400 >>> t = NullTransport(responses=[b"+1.200000E+03\n"]) >>> k = Keithley2400(transport=t) >>> k.connect() >>> k.measure_resistance() 1200.0 >>> k.disconnect()
- measure_voltage() float[source]¶
Trigger a voltage measurement and return the result in volts.
Configures the sense function to voltage, triggers a single reading, and returns the parsed floating-point result.
- Returns:
- (float):
Measured voltage in volts.
- Raises:
- ConnectionError:
If the transport is not open.
- Examples:
>>> from stoner_measurement.instruments.transport import NullTransport >>> from stoner_measurement.instruments.keithley import Keithley2400 >>> t = NullTransport(responses=[b"+1.234567E+00\n"]) >>> k = Keithley2400(transport=t) >>> k.connect() >>> k.measure_voltage() 1.234567 >>> k.disconnect()
- output_enabled() bool[source]¶
Return
Trueif the source output is currently enabled.- Returns:
- (bool):
Truewhen the output is active.
- Raises:
- ConnectionError:
If the transport is not open.
- Examples:
>>> from stoner_measurement.instruments.transport import NullTransport >>> from stoner_measurement.instruments.keithley import Keithley2400 >>> t = NullTransport(responses=[b"0\n"]) >>> k = Keithley2400(transport=t) >>> k.connect() >>> k.output_enabled() False >>> k.disconnect()
- read_buffer(count: int | None = None) tuple[float, ...][source]¶
Read buffered readings from the internal trace buffer.
- Args:
- count (int | None):
Optional number of readings to return from the start of the buffer.
- Returns:
- (tuple[float, …]):
Flat tuple of numeric readings.
- Raises:
- ConnectionError:
If the transport is not open.
- ValueError:
If count is provided and is not positive.
- Examples:
>>> from stoner_measurement.instruments.transport import NullTransport >>> from stoner_measurement.instruments.keithley import Keithley2400 >>> t = NullTransport(responses=[b"1.0,2.0\n"]) >>> k = Keithley2400(transport=t) >>> k.connect() >>> k.read_buffer() (1.0, 2.0) >>> k.disconnect()
- set_buffer_size(size: int) None[source]¶
Set reading buffer capacity.
- Args:
- size (int):
Number of readings for the buffer to retain.
- Raises:
- ConnectionError:
If the transport is not open.
- ValueError:
If size is not positive.
- Examples:
>>> from stoner_measurement.instruments.transport import NullTransport >>> from stoner_measurement.instruments.keithley import Keithley2400 >>> t = NullTransport() >>> k = Keithley2400(transport=t) >>> k.connect() >>> k.set_buffer_size(100) >>> t.write_log[-1] b':TRAC:POIN 100\n' >>> k.disconnect()
- set_compliance(value: float) None[source]¶
Set the compliance limit.
- Args:
- value (float):
Compliance in amps (voltage mode) or volts (current mode).
- Raises:
- ConnectionError:
If the transport is not open.
- Examples:
>>> from stoner_measurement.instruments.transport import NullTransport >>> from stoner_measurement.instruments.keithley import Keithley2400 >>> t = NullTransport() >>> k = Keithley2400(transport=t) >>> k.connect() >>> k.set_compliance(0.05) >>> t.write_log[-1] b':SENS:CURR:PROT 0.05\n' >>> k.disconnect()
- set_measure_functions(functions: tuple[MeasureFunction, ...]) None[source]¶
Enable one or more measurement functions.
- set_nplc(value: float) None[source]¶
Set the integration time in power-line cycles.
- Args:
- value (float):
Integration time (0.01–10 PLC).
- Raises:
- ConnectionError:
If the transport is not open.
- ValueError:
If value is outside [0.01, 10].
- Examples:
>>> from stoner_measurement.instruments.transport import NullTransport >>> from stoner_measurement.instruments.keithley import Keithley2400 >>> t = NullTransport() >>> k = Keithley2400(transport=t) >>> k.connect() >>> k.set_nplc(5.0) >>> t.write_log # two writes: voltage sense then current sense [b':SENS:VOLT:NPLC 5.0\n', b':SENS:CURR:NPLC 5.0\n'] >>> k.disconnect()
- set_source_delay(delay: float) None[source]¶
Set source delay before each measurement trigger.
- Args:
- delay (float):
Source delay in seconds.
- Raises:
- ConnectionError:
If the transport is not open.
- ValueError:
If delay is negative.
- Examples:
>>> from stoner_measurement.instruments.transport import NullTransport >>> from stoner_measurement.instruments.keithley import Keithley2400 >>> t = NullTransport() >>> k = Keithley2400(transport=t) >>> k.connect() >>> k.set_source_delay(0.01) >>> t.write_log[-1] b':SOUR:DEL 0.01\n' >>> k.disconnect()
- set_source_level(value: float) None[source]¶
Set the source output level.
- Args:
- value (float):
Output amplitude in volts (voltage mode) or amps (current mode).
- Raises:
- ConnectionError:
If the transport is not open.
- Examples:
>>> from stoner_measurement.instruments.transport import NullTransport >>> from stoner_measurement.instruments.keithley import Keithley2400 >>> t = NullTransport() >>> k = Keithley2400(transport=t) >>> k.connect() >>> k.set_source_level(1.5) >>> t.write_log[-1] b':SOUR:AMPL 1.5\n' >>> k.disconnect()
- set_source_mode(mode: SourceMode) None[source]¶
Set the source mode.
- Args:
- mode (SourceMode):
VOLTfor voltage source orCURRfor current source.
- Raises:
- ConnectionError:
If the transport is not open.
- Examples:
>>> from stoner_measurement.instruments.transport import NullTransport >>> from stoner_measurement.instruments.keithley import Keithley2400 >>> from stoner_measurement.instruments.source_meter import SourceMode >>> t = NullTransport() >>> k = Keithley2400(transport=t) >>> k.connect() >>> k.set_source_mode(SourceMode.VOLT) >>> t.write_log[-1] b':SOUR:FUNC:MODE VOLT\n' >>> k.disconnect()
- class stoner_measurement.instruments.keithley.Keithley2410(transport: BaseTransport, protocol: BaseProtocol | None = None)[source]¶
Bases:
Keithley2400Driver for the Keithley 2410 SourceMeter.
This model uses command-level compatibility with the Keithley 2400 implementation for core source/measure, sweep, trigger, and buffer features.
- class stoner_measurement.instruments.keithley.Keithley2450(transport: BaseTransport, protocol: BaseProtocol | None = None)[source]¶
Bases:
Keithley2400Driver for the Keithley 2450 SourceMeter.
This model supports a SCPI command subset that is compatible with the Keithley 2400 implementation exposed by this class hierarchy.
- class stoner_measurement.instruments.keithley.Keithley2700(transport: BaseTransport, protocol: BaseProtocol | None = None)[source]¶
Bases:
Keithley2000Driver for the Keithley 2700 multimeter/data-acquisition system.
- class stoner_measurement.instruments.keithley.Keithley6221(transport: BaseTransport, protocol: BaseProtocol | None = None)[source]¶
Bases:
CurrentSourceDriver for the Keithley 6221 precision AC/DC current source.
Provides DC source-level and compliance control plus AC waveform controls (waveform shape, frequency, and offset current) using SCPI commands. Built-in staircase sweeps (linear, logarithmic, custom list) and pulsed sweeps are also supported.
- configure_pulsed_sweep(sweep: CurrentSweepConfiguration, pulse: PulsedSweepConfiguration) None[source]¶
Configure a pulsed current sweep.
Calls
configure_sweep()to programme the sweep points, then enables pulsed mode and sets pulse timing parameters.- Args:
- sweep (CurrentSweepConfiguration):
Sweep point configuration.
- pulse (PulsedSweepConfiguration):
Pulse timing and baseline current.
pulse.widthandpulse.off_timemust both be positive.
- Raises:
- ValueError:
If
pulse.widthorpulse.off_timeis not positive.
- configure_sweep(config: CurrentSweepConfiguration) None[source]¶
Configure a built-in current sweep.
- Args:
- config (CurrentSweepConfiguration):
Sweep configuration. For
LISTspacing,config.valuesmust be non-empty.
- Raises:
- ValueError:
If
config.spacingisLISTandconfig.valuesis empty orNone.
- get_capabilities() CurrentSourceCapabilities[source]¶
Return static capabilities for Keithley 6221.
- get_waveform() CurrentWaveform[source]¶
Return waveform mode.
- set_waveform(waveform: CurrentWaveform) None[source]¶
Set waveform mode.
- class stoner_measurement.instruments.keithley.Keithley6514(transport: BaseTransport, protocol: BaseProtocol | None = None)[source]¶
Bases:
_KeithleyElectrometerBaseDriver for the Keithley 6514 electrometer.
- class stoner_measurement.instruments.keithley.Keithley6517(transport: BaseTransport, protocol: BaseProtocol | None = None)[source]¶
Bases:
_KeithleyElectrometerBaseDriver for the Keithley 6517 electrometer.
- class stoner_measurement.instruments.keithley.Keithley6845(transport: BaseTransport, protocol: BaseProtocol | None = None)[source]¶
Bases:
_KeithleyElectrometerBaseDriver for the Keithley 6845 picoammeter/electrometer command set.
Lakeshore instrument drivers.
- class stoner_measurement.instruments.lakeshore.Lakeshore335(transport: BaseTransport, protocol: BaseProtocol | None = None)[source]¶
Bases:
_LakeshoreTemperatureControllerBaseConcrete driver for the Lakeshore 335 temperature controller.
- class stoner_measurement.instruments.lakeshore.Lakeshore336(transport: BaseTransport, protocol: BaseProtocol | None = None)[source]¶
Bases:
_LakeshoreTemperatureControllerBaseConcrete driver for the Lakeshore 336 temperature controller.
- class stoner_measurement.instruments.lakeshore.Lakeshore340(transport: BaseTransport, protocol: BaseProtocol | None = None)[source]¶
Bases:
_LakeshoreTemperatureControllerBaseConcrete driver for the Lakeshore 340 temperature controller.
- class stoner_measurement.instruments.lakeshore.Lakeshore525(transport: BaseTransport, protocol: BaseProtocol | None = None)[source]¶
Bases:
MagnetController,MagnetSupplyDriver for a Lakeshore 525 superconducting magnet power supply.
The driver composes
BaseInstrumentcommunication behaviour with theMagnetSupplyinterface for magnet supplies.- Attributes:
- transport (BaseTransport):
Transport used for instrument I/O.
- protocol (BaseProtocol):
Protocol used to format and parse instrument messages.
- Examples:
>>> from stoner_measurement.instruments.lakeshore import Lakeshore525 >>> from stoner_measurement.instruments.transport import NullTransport >>> t = NullTransport(responses=[b"LAKESHORE,MODEL525,SN001,1.0\r\n"]) >>> mps = Lakeshore525(transport=t) >>> mps.connect() >>> mps.identify() 'LAKESHORE,MODEL525,SN001,1.0' >>> mps.disconnect()
- property current: float¶
Return output current in amps.
- Returns:
- (float):
Measured magnet supply current in amps.
- property field: float¶
Return output field in tesla.
- Returns:
- (float):
Measured magnetic field in tesla.
- get_firmware_version() str[source]¶
Return the firmware version from the identification string.
- Returns:
- (str):
Firmware version token when available, otherwise an empty string.
- get_model() str[source]¶
Return the model name from the identification string.
- Returns:
- (str):
Instrument model token when available, otherwise an empty string.
- property heater: bool¶
Return persistent switch heater state.
- Returns:
- (bool):
Truewhen the heater is enabled.
- property limits: MagnetLimits¶
Return configured software limits for this driver instance.
- Returns:
- (MagnetLimits):
Cached configured current/field/ramp limits.
- property magnet_constant: float¶
Return the magnet constant in tesla per amp.
- Returns:
- (float):
Magnet constant in tesla per amp.
- ramp_to_current(current: float, *, wait: bool = False) None[source]¶
Program current target and ramp.
- Args:
- current (float):
Target current in amps.
- Keyword Parameters:
- wait (bool):
When
True, block until ramp completes.
- ramp_to_field(field: float, *, wait: bool = False) None[source]¶
Program field target and ramp.
- Args:
- field (float):
Target field in tesla.
- Keyword Parameters:
- wait (bool):
When
True, block until ramp completes.
- set_limits(limits: MagnetLimits) None[source]¶
Set software limits used by higher-level sequence logic.
- Args:
- limits (MagnetLimits):
Limit configuration to cache for runtime checks.
- set_magnet_constant(tesla_per_amp: float) None[source]¶
Set the software magnet constant used for conversion.
- Args:
- tesla_per_amp (float):
Magnet constant in tesla per amp.
- set_ramp_rate_current(rate: float) None[source]¶
Set the current ramp rate in amps per minute.
- Args:
- rate (float):
Ramp rate in amps per minute.
- set_ramp_rate_field(rate: float) None[source]¶
Set the field ramp rate in tesla per minute.
- Args:
- rate (float):
Ramp rate in tesla per minute.
- set_target_current(current: float) None[source]¶
Set the target current in amps.
- Args:
- current (float):
Target current in amps.
- set_target_field(field: float) None[source]¶
Set the target field in tesla.
- Args:
- field (float):
Target magnetic field in tesla.
- property status: MagnetStatus¶
Return consolidated magnet status.
- Returns:
- (MagnetStatus):
Snapshot of controller state and key readings.
- class stoner_measurement.instruments.lakeshore.LakeshoreM81CurrentSource(transport: BaseTransport, protocol: BaseProtocol | None = None)[source]¶
Bases:
CurrentSourceDriver for the Lakeshore M81 balanced current-source outputs.
Models the M81 as a differential current-source backed by two matched output channels. The public source-level API uses channel 1 amplitude and mirrors channel 2 with opposite polarity for balanced drive. Built-in sweeps (linear, logarithmic, custom list) are also supported; the sweep is mirrored across both channels so the differential current follows the programmed profile.
- configure_sweep(config: CurrentSweepConfiguration) None[source]¶
Configure a built-in balanced current sweep.
Channel 1 is programmed with the positive sense of the sweep; channel 2 is programmed with the mirrored (negated) profile so that the differential output follows the intended sweep.
- Args:
- config (CurrentSweepConfiguration):
Sweep configuration. For
LISTspacing,config.valuesmust be non-empty.
- Raises:
- ValueError:
If
config.spacingisLISTandconfig.valuesis empty orNone.
- get_capabilities() CurrentSourceCapabilities[source]¶
Return static capabilities for Lakeshore M81 current source.
- get_channel_level(channel: int) float[source]¶
Return programmed current for a specific channel in amps.
- get_waveform() CurrentWaveform[source]¶
Return waveform mode.
- set_channel_level(channel: int, value: float) None[source]¶
Set programmed current for a specific channel in amps.
- set_compliance_voltage(value: float) None[source]¶
Set compliance voltage in volts for both channels.
- set_offset_current(value: float) None[source]¶
Set waveform offset current in amps for both channels.
- set_waveform(waveform: CurrentWaveform) None[source]¶
Set waveform mode for both channels.
- class stoner_measurement.instruments.lakeshore.LakeshoreM81LockIn(transport: BaseTransport, protocol: BaseProtocol | None = None, *, sense_slot: int = 1, source_slot: int | None = None)[source]¶
Bases:
LockInAmplifierDriver for the Lakeshore M81 SSM voltage-measurement module in LIA mode.
The Lakeshore M81 Synchronous Source Measure (SSM) system performs lock-in detection via its VM (voltage-measurement) module. Each module occupies a numbered slot on the mainframe. When operating in LIA mode the VM module demodulates against a reference that may come from either an internal AC source module occupying another slot (
source_slot) or an external TTL/sine reference routed to the rear panel.- Notes:
Setting the reference frequency programmatically requires an AC source module connected to the same mainframe. Pass its slot number as
source_slotat construction time. Withoutsource_slotthe reference frequency can still be read (the instrument reports the detected frequency) but cannot be set via this driver.- Keyword Parameters:
- sense_slot (int):
Mainframe slot of the VM measurement module. Defaults to
1.- source_slot (int | None):
Mainframe slot of the AC source module used as the internal reference. When
None(default) the internal reference frequency cannot be set by this driver.
- Attributes:
- transport (BaseTransport):
Transport layer (GPIB, Ethernet, or USB).
- protocol (BaseProtocol):
Protocol instance (defaults to
ScpiProtocol).
- Examples:
>>> from stoner_measurement.instruments.transport import NullTransport >>> lia = LakeshoreM81LockIn(NullTransport(), sense_slot=1, source_slot=2) >>> lia.set_time_constant(100e-3) >>> x, y = lia.measure_xy()
- get_capabilities() LockInAmplifierCapabilities[source]¶
Return static capability metadata for the M81 LIA driver.
- Returns:
- (LockInAmplifierCapabilities):
Capability descriptor. Reference-frequency control is only available when a
source_slotwas supplied at construction.
- get_filter_slope() int[source]¶
Return the output low-pass filter roll-off slope in dB/octave.
- Returns:
- (int):
Filter slope in dB/octave, one of
(6, 12, 18, 24).
- Raises:
- ValueError:
If the instrument returns an unrecognised filter-poles value.
- get_harmonic() int[source]¶
Return the detection harmonic.
- Returns:
- (int):
Active detection harmonic (1 to
_MAX_HARMONIC).
- get_input_coupling() LockInInputCoupling[source]¶
Return the input coupling mode.
- Returns:
- (LockInInputCoupling):
ACorDC.
- get_reference_frequency() float[source]¶
Return the reference frequency in hertz.
When a source slot is configured the frequency is queried from the AC source module; otherwise the locally cached value is returned.
- Returns:
- (float):
Reference frequency in hertz.
- Notes:
The cached value is initialised from
_DEFAULT_INTERNAL_REFERENCE_FREQUENCY_HZand updated byset_reference_frequency()and source-slot queries made via this driver.
- get_reference_phase() float[source]¶
Return the reference phase offset in degrees.
- Returns:
- (float):
Reference phase in degrees.
- get_reference_source() LockInReferenceSource[source]¶
Return the active reference source.
- Returns:
- (LockInReferenceSource):
INTERNALorEXTERNAL.
- get_sensitivity() float[source]¶
Return the active input range (sensitivity) in volts.
- Returns:
- (float):
Sensitivity in volts as reported by the instrument.
- get_time_constant() float[source]¶
Return the output filter time constant in seconds.
- Returns:
- (float):
Time constant in seconds.
- measure_rt() tuple[float, float][source]¶
Measure and return the magnitude (R) and phase (theta) outputs.
- Returns:
- (tuple[float, float]):
(r, theta)whereris in volts andthetais in degrees.
- measure_xy() tuple[float, float][source]¶
Measure and return the in-phase (X) and quadrature (Y) outputs.
- Returns:
- (tuple[float, float]):
(x, y)values in volts.
- set_filter_slope(slope: int) None[source]¶
Set the output low-pass filter roll-off slope.
- Args:
- slope (int):
Slope in dB/octave. Must be one of
(6, 12, 18, 24).
- Raises:
- ValueError:
If slope is not a valid filter-slope value.
- set_harmonic(harmonic: int) None[source]¶
Set the detection harmonic.
- Args:
- harmonic (int):
Harmonic number between 1 and
_MAX_HARMONIC.
- Raises:
- ValueError:
If harmonic is outside the permitted range.
- set_input_coupling(coupling: LockInInputCoupling) None[source]¶
Set the input coupling mode.
- Args:
- coupling (LockInInputCoupling):
Coupling mode to select.
- set_reference_frequency(value: float) None[source]¶
Set the reference frequency via the M81 AC source module.
- Args:
- value (float):
Frequency in hertz. Must be positive.
- Raises:
- NotImplementedError:
If no
source_slotwas provided at construction time.- ValueError:
If value is not positive.
- set_reference_phase(value: float) None[source]¶
Set the reference phase offset in degrees.
- Args:
- value (float):
Phase offset in degrees.
- set_reference_source(source: LockInReferenceSource) None[source]¶
Set the reference source.
- Args:
- source (LockInReferenceSource):
Reference source to select.
Oxford Instruments drivers.
- class stoner_measurement.instruments.oxford.OxfordIPS120(transport: BaseTransport, protocol: BaseProtocol | None = None)[source]¶
Bases:
MagnetController,MagnetSupplyDriver for an Oxford Instruments IPS120 magnet power supply.
- Attributes:
- transport (BaseTransport):
Transport used for instrument I/O.
- protocol (BaseProtocol):
Protocol used to format and parse instrument messages.
- Examples:
>>> from stoner_measurement.instruments.oxford import OxfordIPS120 >>> from stoner_measurement.instruments.transport import NullTransport >>> t = NullTransport( ... responses=[b"VIPS120-10 3.07\r"], ... ) >>> mps = OxfordIPS120(transport=t) >>> mps.connect() >>> mps.identify() 'IPS120-10 3.07' >>> mps.disconnect()
- property current: float¶
Return output current in amps.
- Returns:
- (float):
Measured magnet supply current in amps.
- property field: float¶
Return output field in tesla.
- Returns:
- (float):
Measured magnetic field in tesla.
- get_firmware_version() str[source]¶
Return the firmware version from the identity string.
- Returns:
- (str):
Firmware token when available, otherwise an empty string.
- get_model() str[source]¶
Return the model name from the identity string.
- Returns:
- (str):
Instrument model token when available, otherwise an empty string.
- property heater: bool¶
Return persistent switch heater state.
- Returns:
- (bool):
Truewhen the heater is enabled.
- identify() str[source]¶
Return the instrument identity string.
- Returns:
- (str):
Identity payload from the instrument with the leading Oxford command-echo prefix removed by the protocol parser.
- property limits: MagnetLimits¶
Return configured software limits for this driver instance.
- Returns:
- (MagnetLimits):
Cached configured current/field/ramp limits.
- property magnet_constant: float¶
Return the magnet constant in tesla per amp.
- Returns:
- (float):
Magnet constant in tesla per amp.
- ramp_to_current(current: float, *, wait: bool = False) None[source]¶
Program current target and ramp.
- Args:
- current (float):
Target current in amps.
- Keyword Parameters:
- wait (bool):
When
True, block until ramp completes.
- ramp_to_field(field: float, *, wait: bool = False) None[source]¶
Program field target and ramp.
- Args:
- field (float):
Target field in tesla.
- Keyword Parameters:
- wait (bool):
When
True, block until ramp completes.
- set_limits(limits: MagnetLimits) None[source]¶
Set software limits used by higher-level sequence logic.
- Args:
- limits (MagnetLimits):
Limit configuration to cache for runtime checks.
- set_magnet_constant(tesla_per_amp: float) None[source]¶
Set the software magnet constant used for conversion.
- Args:
- tesla_per_amp (float):
Magnet constant in tesla per amp.
- set_ramp_rate_current(rate: float) None[source]¶
Set the current ramp rate in amps per minute.
- Args:
- rate (float):
Ramp rate in amps per minute.
- set_ramp_rate_field(rate: float) None[source]¶
Set the field ramp rate in tesla per minute.
- Args:
- rate (float):
Ramp rate in tesla per minute.
- set_target_current(current: float) None[source]¶
Set the target current in amps.
- Args:
- current (float):
Target current in amps.
- set_target_field(field: float) None[source]¶
Set the target field in tesla.
- Args:
- field (float):
Target magnetic field in tesla.
- property status: MagnetStatus¶
Return consolidated magnet status.
- Returns:
- (MagnetStatus):
Snapshot of controller state and key readings.
- class stoner_measurement.instruments.oxford.OxfordITC503(transport: BaseTransport, protocol: BaseProtocol | None = None)[source]¶
Bases:
_OxfordTemperatureControllerBaseConcrete driver for the Oxford Instruments ITC503 temperature controller.
- class stoner_measurement.instruments.oxford.OxfordMercuryTemperatureController(transport: BaseTransport, protocol: BaseProtocol | None = None)[source]¶
Bases:
_OxfordTemperatureControllerBaseConcrete driver for the Oxford Instruments Mercury Temperature Controller.