Source code for qcodes.instrument_drivers.Keysight.keysight_b220x

import re
import warnings
from functools import wraps
from typing import TYPE_CHECKING, TypeVar

from typing_extensions import ParamSpec

from qcodes.instrument import VisaInstrument, VisaInstrumentKWArgs
from qcodes.validators import Enum, Ints, Lists, MultiType

if TYPE_CHECKING:
    from collections.abc import Callable, Sequence
    from typing import Concatenate

    from typing_extensions import Unpack

    from qcodes.parameters import Parameter


S = TypeVar("S", bound="KeysightB220X")
P = ParamSpec("P")
T = TypeVar("T")


def post_execution_status_poll(
    func: "Callable[Concatenate[S, P], T]",
) -> "Callable[Concatenate[S, P], T]":
    """
    Generates a decorator that clears the instrument's status registers
    before executing the actual call and reads the status register after the
    function call to determine whether an error occurred.

    :param func: function to wrap
    """

    @wraps(func)
    def wrapper(self: S, *args: P.args, **kwargs: P.kwargs) -> T:
        self.clear_status()
        retval = func(self, *args, **kwargs)

        stb = self.get_status()
        if stb:
            warnings.warn(
                f"Instrument status byte indicates an error occurred "
                f"(value of STB was: {stb})! Use `get_error` method "
                f"to poll error message.",
                stacklevel=2,
            )
        return retval

    return wrapper


[docs] class KeysightB220X(VisaInstrument): """ QCodes driver for B2200 / B2201 switch matrix Note: The B2200 consists of up to 4 modules and provides two channel configuration modes, *Normal* and *Auto*. The configuration mode defines whether multiple switch modules are treated as one (*Auto* mode), or separately (*Normal* mode). This driver only implements the *Auto* mode. Please read the manual section on *Channel Configuration Mode* for more info. """ _available_input_ports = Ints(1, 14) _available_output_ports = Ints(1, 48) default_terminator = "\n" def __init__( self, name: str, address: str, **kwargs: "Unpack[VisaInstrumentKWArgs]" ): super().__init__(name, address, **kwargs) self._card = 0 self.get_status: Parameter = self.add_parameter( name="get_status", get_cmd="*ESR?", get_parser=int, docstring="Queries status register.", ) """Queries status register.""" self.get_error: Parameter = self.add_parameter( name="get_error", get_cmd=":SYST:ERR?", docstring="Queries error queue" ) """Queries error queue""" self.connections: Parameter = self.add_parameter( name="connections", get_cmd=f":CLOS:CARD? {self._card}", get_parser=KeysightB220X.parse_channel_list, docstring="queries currently active connections " "and returns a set of tuples {(input, " "output), ...}", ) """ queries currently active connections and returns a set of tuples {(input, output), ...} """ self.connection_rule: Parameter = self.add_parameter( name="connection_rule", get_cmd=self._get_connection_rule, set_cmd=self._set_connection_rule, val_mapping={"free": "FREE", "single": "SROU"}, docstring=( "specifies connection rule. Parameter " "one of 'free' (default) or 'single'.\n\n" "In 'free' mode\n" " - each input port can be connected to " "multiple output ports\n" " - and each output port can be " "connected to multiple input ports.\n" " - Caution: If the Free connection rule " "has been specified, ensure multiple " "input ports are not connected to the " "same output port. Such configurations " "can cause damage\n\n" "In single route mode:\n" " - each input port can be connected to " "only one output port\n" " - and each output port can be " "connected to only one input port.\n" " - existing connection to a port will " "be disconnected when a new connection " "is made.\n" ), ) """ specifies connection rule. Parameter one of 'free' (default) or 'single'. In 'free' mode - each input port can be connected to multiple output ports - and each output port can be connected to multiple input ports. - Caution: If the Free connection rule has been specified, ensure multiple input ports are not connected to the same output port. Such configurations can cause damage In single route mode: - each input port can be connected to only one output port - and each output port can be connected to only one input port. - existing connection to a port will be disconnected when a new connection is made. """ self.connection_sequence: Parameter = self.add_parameter( name="connection_sequence", get_cmd=f":CONN:SEQ? {self._card}", set_cmd=f":CONN:SEQ {self._card},{{}}", val_mapping={"none": "NSEQ", "bbm": "BBM", "mbb": "MBBR"}, docstring="One of 'none', 'bbm' (Break before " "make) or 'mbb' (make before break)", ) """ One of 'none', 'bbm' (Break before make) or 'mbb' (make before break) """ self.bias_input_port: Parameter = self.add_parameter( name="bias_input_port", get_cmd=f":BIAS:PORT? {self._card}", set_cmd=f":BIAS:PORT {self._card},{{}}", vals=MultiType(KeysightB220X._available_input_ports, Enum(-1)), get_parser=int, docstring="Selects the input that will be used as " "bias input port (default 10). The Bias " "input port cannot be used on subsequent " "`connect` or `disconnect` commands if " "Bias mode is ON", ) """ Selects the input that will be used as bias input port (default 10). The Bias input port cannot be used on subsequent `connect` or `disconnect` commands if Bias mode is ON """ self.bias_mode: Parameter = self.add_parameter( name="bias_mode", get_cmd=f":BIAS? {self._card}", set_cmd=f":BIAS {self._card},{{}}", val_mapping={True: 1, False: 0}, docstring="Param: True for ON, False for OFF", ) """Param: True for ON, False for OFF""" self.gnd_input_port: Parameter = self.add_parameter( name="gnd_input_port", get_cmd=f":AGND:PORT? {self._card}", set_cmd=f":AGND:PORT {self._card},{{}}", vals=MultiType(KeysightB220X._available_input_ports, Enum(-1)), get_parser=int, docstring="Selects the input that will be used as " "GND input port (default 12). The GND " "input port cannot be used on subsequent " "`connect` or `disconnect` commands if " "GND mode is ON", ) """ Selects the input that will be used as GND input port (default 12). The GND input port cannot be used on subsequent `connect` or `disconnect` commands if GND mode is ON """ self.gnd_mode: Parameter = self.add_parameter( name="gnd_mode", get_cmd=f":AGND? {self._card}", set_cmd=f":AGND {self._card},{{}}", val_mapping={True: 1, False: 0}, ) """Parameter gnd_mode""" self.unused_inputs: Parameter = self.add_parameter( name="unused_inputs", get_cmd=f":AGND:UNUSED? {self._card}", set_cmd=f":AGND:UNUSED {self._card},'{{}}'", get_parser=lambda response: [ int(x) for x in response.strip("'").split(",") if x.strip().isdigit() ], set_parser=lambda value: str(value).strip("[]"), vals=Lists(KeysightB220X._available_input_ports), ) """Parameter unused_inputs""" self.couple_ports: Parameter = self.add_parameter( name="couple_ports", get_cmd=f":COUP:PORT? {self._card}", set_cmd=f":COUP:PORT {self._card},'{{}}'", set_parser=lambda value: str(value).strip("[]()"), get_parser=lambda response: [ int(x) for x in response.strip("'").split(",") if x.strip().isdigit() ], vals=Lists(Enum(1, 3, 5, 7, 9, 11, 13)), ) """Parameter couple_ports""" self.couple_mode: Parameter = self.add_parameter( name="couple_mode", get_cmd=f":COUP? {self._card}", set_cmd=f":COUP {self._card},{{}}", val_mapping={True: 1, False: 0}, docstring="Param: True for ON, False for OFF", ) """Param: True for ON, False for OFF""" self.connect_message()
[docs] @post_execution_status_poll def connect(self, input_ch: int, output_ch: int) -> None: """Connect given input/output pair. Args: input_ch: Input channel number 1-14 output_ch: Output channel number 1-48 """ KeysightB220X._available_input_ports.validate(input_ch) KeysightB220X._available_output_ports.validate(output_ch) self.write(f":CLOS (@{self._card:01d}{input_ch:02d}{output_ch:02d})")
[docs] @post_execution_status_poll def connect_paths(self, paths: "Sequence[tuple[int, int]]") -> None: channel_list_str = self.to_channel_list(paths) self.write(f":CLOS {channel_list_str}")
[docs] @post_execution_status_poll def disconnect_paths(self, paths: "Sequence[tuple[int, int]]") -> None: channel_list_str = self.to_channel_list(paths) self.write(f":OPEN {channel_list_str}")
[docs] @post_execution_status_poll def disconnect(self, input_ch: int, output_ch: int) -> None: """Disconnect given Input/Output pair. Args: input_ch: Input channel number 1-14 output_ch: Output channel number 1-48 """ KeysightB220X._available_input_ports.validate(input_ch) KeysightB220X._available_output_ports.validate(output_ch) self.write(f":OPEN (@{self._card:01d}{input_ch:02d}{output_ch:02d})")
[docs] @post_execution_status_poll def disconnect_all(self) -> None: """ opens all connections. If ground or bias mode is enabled it will connect all outputs to the GND or Bias Port """ self.write(f":OPEN:CARD {self._card}")
[docs] @post_execution_status_poll def bias_disable_all_outputs(self) -> None: """ Removes all outputs from list of ports that will be connected to GND input if port is unused and bias mode is enabled. """ self.write(f":BIAS:CHAN:DIS:CARD {self._card}")
[docs] @post_execution_status_poll def bias_enable_all_outputs(self) -> None: """ Adds all outputs to list of ports that will be connected to bias input if port is unused and bias mode is enabled. """ self.write(f":BIAS:CHAN:ENAB:CARD {self._card}")
[docs] @post_execution_status_poll def bias_enable_output(self, output: int) -> None: """ Adds `output` to list of ports that will be connected to bias input if port is unused and bias mode is enabled. Args: output: int 1-48 """ KeysightB220X._available_output_ports.validate(output) self.write(f":BIAS:CHAN:ENAB (@{self._card}01{output:02d})")
[docs] @post_execution_status_poll def bias_disable_output(self, output: int) -> None: """ Removes `output` from list of ports that will be connected to bias input if port is unused and bias mode is enabled. Args: output: int 1-48 """ KeysightB220X._available_output_ports.validate(output) self.write(f":BIAS:CHAN:DIS (@{self._card}01{output:02d})")
[docs] @post_execution_status_poll def gnd_enable_output(self, output: int) -> None: """ Adds `output` to list of ports that will be connected to GND input if port is unused and bias mode is enabled. Args: output: int 1-48 """ KeysightB220X._available_output_ports.validate(output) self.write(f":AGND:CHAN:ENAB (@{self._card}01{output:02d})")
[docs] @post_execution_status_poll def gnd_disable_output(self, output: int) -> None: """ Removes `output` from list of ports that will be connected to GND input if port is unused and bias mode is enabled. Args: output: int 1-48 """ KeysightB220X._available_output_ports.validate(output) self.write(f":AGND:CHAN:DIS (@{self._card}01{output:02d})")
[docs] @post_execution_status_poll def gnd_enable_all_outputs(self) -> None: """ Adds all outputs to list of ports that will be connected to GND input if port is unused and bias mode is enabled. """ self.write(f":AGND:CHAN:ENAB:CARD {self._card}")
[docs] @post_execution_status_poll def gnd_disable_all_outputs(self) -> None: """ Removes all outputs from list of ports that will be connected to GND input if port is unused and bias mode is enabled. """ self.write(f":AGND:CHAN:DIS:CARD {self._card}")
[docs] @post_execution_status_poll def couple_port_autodetect(self) -> None: """Autodetect Kelvin connections on Input ports This will detect Kelvin connections on the input ports and enable couple mode for found kelvin connections. Kelvin connections must use input pairs that can be couple-enabled in order to be autodetected. `{(1, 2), (3, 4), (5, 6), (7, 8), (9, 10), (11, 12), (13, 14)}` Also refer to the manual for more information. """ self.write(":COUP:PORT:DET")
[docs] def clear_status(self) -> None: """Clears status register and error queue of the instrument.""" self.write("*CLS")
[docs] def reset(self) -> None: """Performs an instrument reset. Does not reset error queue! """ self.write("*RST")
@post_execution_status_poll def _set_connection_rule(self, mode: str) -> None: if "free" == self.connection_rule() and "SROU" == mode: warnings.warn( "When going from *free* to *single* mode existing " "connections are not released." ) self.write(f":CONN:RULE {self._card},{mode}") @post_execution_status_poll def _get_connection_rule(self) -> str: return self.ask(f":CONN:RULE? {self._card}")
[docs] @staticmethod def parse_channel_list(channel_list: str) -> set[tuple[int, int]]: """Generate a set of (input, output) tuples from a SCPI channel list string. """ pattern = ( r"(?P<card>\d{0,1}?)(?P<input>\d{1,2})(?P<output>\d{2})(?=(?:[,\)\r\n]|$))" ) return { (int(match["input"]), int(match["output"])) for match in re.finditer(pattern, channel_list) }
[docs] def to_channel_list(self, paths: "Sequence[tuple[int, int]]") -> str: chan = [f"{self._card:01d}{i:02d}{o:02d}" for i, o in paths] channel_list = f"(@{','.join(chan)})" return channel_list
[docs] class KeysightB2200(KeysightB220X): """ QCodes driver for B2200 """
[docs] class KeysightB2201(KeysightB220X): """ QCodes driver for B2201 """