Source code for qcodes.instrument_drivers.Keithley.Keithley_2000

from functools import partial
from typing import TYPE_CHECKING, Any

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

if TYPE_CHECKING:
    from collections.abc import Callable

    from typing_extensions import Unpack

    from qcodes.parameters import Parameter


def _parse_output_string(s: str) -> str:
    """Parses and cleans string outputs of the Keithley"""
    # Remove surrounding whitespace and newline characters
    s = s.strip()

    # Remove surrounding quotes
    if (s[0] == s[-1]) and s.startswith(("'", '"')):
        s = s[1:-1]

    s = s.lower()

    # Convert some results to a better readable version
    conversions = {
        "mov": "moving",
        "rep": "repeat",
    }

    if s in conversions.keys():
        s = conversions[s]

    return s


def _parse_output_bool(value: str) -> bool:
    return True if int(value) == 1 else False


[docs] class Keithley2000(VisaInstrument): """ Driver for the Keithley 2000 multimeter. """ default_terminator = "\n" def __init__( self, name: str, address: str, reset: bool = False, **kwargs: "Unpack[VisaInstrumentKWArgs]", ): super().__init__(name, address, **kwargs) self._trigger_sent = False # Unfortunately the strings have to contain quotation marks and a # newline character, as this is how the instrument returns it. self._mode_map = { "ac current": '"CURR:AC"', "dc current": '"CURR:DC"', "ac voltage": '"VOLT:AC"', "dc voltage": '"VOLT:DC"', "2w resistance": '"RES"', "4w resistance": '"FRES"', "temperature": '"TEMP"', "frequency": '"FREQ"', } self.mode: Parameter = self.add_parameter( "mode", get_cmd="SENS:FUNC?", set_cmd="SENS:FUNC {}", val_mapping=self._mode_map, ) """Parameter mode""" # Mode specific parameters self.nplc: Parameter = self.add_parameter( "nplc", get_cmd=partial(self._get_mode_param, "NPLC", float), set_cmd=partial(self._set_mode_param, "NPLC"), vals=Numbers(min_value=0.01, max_value=10), ) """Parameter nplc""" # TODO: validator, this one is more difficult since different modes # require different validation ranges self.range: Parameter = self.add_parameter( "range", get_cmd=partial(self._get_mode_param, "RANG", float), set_cmd=partial(self._set_mode_param, "RANG"), vals=Numbers(), ) """Parameter range""" self.auto_range_enabled: Parameter = self.add_parameter( "auto_range_enabled", get_cmd=partial(self._get_mode_param, "RANG:AUTO", _parse_output_bool), set_cmd=partial(self._set_mode_param, "RANG:AUTO"), vals=Bool(), ) """Parameter auto_range_enabled""" self.digits: Parameter = self.add_parameter( "digits", get_cmd=partial(self._get_mode_param, "DIG", int), set_cmd=partial(self._set_mode_param, "DIG"), vals=Ints(min_value=4, max_value=7), ) """Parameter digits""" self.averaging_type: Parameter = self.add_parameter( "averaging_type", get_cmd=partial(self._get_mode_param, "AVER:TCON", _parse_output_string), set_cmd=partial(self._set_mode_param, "AVER:TCON"), vals=Enum("moving", "repeat"), ) """Parameter averaging_type""" self.averaging_count: Parameter = self.add_parameter( "averaging_count", get_cmd=partial(self._get_mode_param, "AVER:COUN", int), set_cmd=partial(self._set_mode_param, "AVER:COUN"), vals=Ints(min_value=1, max_value=100), ) """Parameter averaging_count""" self.averaging_enabled: Parameter = self.add_parameter( "averaging_enabled", get_cmd=partial(self._get_mode_param, "AVER:STAT", _parse_output_bool), set_cmd=partial(self._set_mode_param, "AVER:STAT"), vals=Bool(), ) """Parameter averaging_enabled""" # Global parameters self.display_enabled: Parameter = self.add_parameter( "display_enabled", get_cmd="DISP:ENAB?", get_parser=_parse_output_bool, set_cmd="DISP:ENAB {}", set_parser=int, vals=Bool(), ) """Parameter display_enabled""" self.trigger_continuous: Parameter = self.add_parameter( "trigger_continuous", get_cmd="INIT:CONT?", get_parser=_parse_output_bool, set_cmd="INIT:CONT {}", set_parser=int, vals=Bool(), ) """Parameter trigger_continuous""" self.trigger_count: Parameter = self.add_parameter( "trigger_count", get_cmd="TRIG:COUN?", get_parser=int, set_cmd="TRIG:COUN {}", vals=MultiType( Ints(min_value=1, max_value=9999), Enum("inf", "default", "minimum", "maximum"), ), ) """Parameter trigger_count""" self.trigger_delay: Parameter = self.add_parameter( "trigger_delay", get_cmd="TRIG:DEL?", get_parser=float, set_cmd="TRIG:DEL {}", unit="s", vals=Numbers(min_value=0, max_value=999999.999), ) """Parameter trigger_delay""" self.trigger_source: Parameter = self.add_parameter( "trigger_source", get_cmd="TRIG:SOUR?", set_cmd="TRIG:SOUR {}", val_mapping={ "immediate": "IMM", "timer": "TIM", "manual": "MAN", "bus": "BUS", "external": "EXT", }, ) """Parameter trigger_source""" self.trigger_timer: Parameter = self.add_parameter( "trigger_timer", get_cmd="TRIG:TIM?", get_parser=float, set_cmd="TRIG:TIM {}", unit="s", vals=Numbers(min_value=0.001, max_value=999999.999), ) """Parameter trigger_timer""" self.amplitude: Parameter = self.add_parameter( "amplitude", unit="arb.unit", get_cmd=self._read_next_value ) """Parameter amplitude""" self.add_function("reset", call_cmd="*RST") if reset: self.reset() # Set the data format to have only ascii data without units and channels self.write("FORM:DATA ASCII") self.write("FORM:ELEM READ") self.connect_message()
[docs] def trigger(self) -> None: if not self.trigger_continuous(): self.write("INIT") self._trigger_sent = True
def _read_next_value(self) -> float: # Prevent a timeout when no trigger has been sent if not self.trigger_continuous() and not self._trigger_sent: return 0.0 self._trigger_sent = False return float(self.ask("SENSE:DATA:FRESH?")) def _get_mode_param( self, parameter: str, parser: "Callable[[str], Any]" ) -> float | str | bool: """Read the current Keithley mode and ask for a parameter""" mode = _parse_output_string(self._mode_map[self.mode()]) cmd = f"{mode}:{parameter}?" return parser(self.ask(cmd)) def _set_mode_param(self, parameter: str, value: float | str | bool) -> None: """Read the current Keithley mode and set a parameter""" if isinstance(value, bool): value = int(value) mode = _parse_output_string(self._mode_map[self.mode()]) cmd = f"{mode}:{parameter} {value}" self.write(cmd)