Source code for pylablib.devices.Keithley.multimeter

from ...core.devio import SCPI, interface
from ...core.utils import py3
from .base import GenericKeithleyError, GenericKeithleyBackendError

import collections


TGenericFunctionParameters=collections.namedtuple("TGenericFunctionParameters",["rng","resolution","autorng"])
TFrequencyFunctionParameters=collections.namedtuple("TFrequencyFunctionParameters",["rng","aperture"])
TConfigurationParameters=collections.namedtuple("TConfigurationParameters",["function","rng","resolution"])
TAveragingParameters=collections.namedtuple("TAveragingParameters",["mode","count","enabled"])
[docs] class Keithley2110(SCPI.SCPIDevice): """ Keithley 2110 bench-top multimeter. Args: addr: device address (usually a VISA name). """ Error=GenericKeithleyError ReraiseError=GenericKeithleyBackendError def __init__(self, addr): super().__init__(addr) with self._close_on_error(): self.get_id() self._add_settings_variable("configuration",self.get_configuration,self.set_configuration,ignore_error=(self.Error,)) self._add_settings_variable("functions",lambda: self.get_function("all"), lambda v: self.set_function(v,channel="all"),multiarg=False) def add_funcpar_settings(f): self._add_settings_variable("parameters/"+f,lambda: self.get_function_parameters(function=f),lambda v: self.set_function_parameters(f,*v),multiarg=False,priority=-2) for f in self._supported_parameter_functions: add_funcpar_settings(f) self._add_settings_variable("averaging",self.get_averaging_parameters,self.setup_averaging) self._add_status_variable("readings",lambda: self.get_reading("all")) _p_function=interface.EnumParameterClass("function", {"volt_dc":"VOLT:DC","volt_ac":"VOLT:AC","curr_dc":"CURR:DC","curr_ac":"CURR:AC","cap":"CAP","volt_ratio":"VOLT:DC:RAT","res":"RES","fres":"FRES", "freq_volt":"FREQ:VOLT","freq_curr":"FREQ:CURR","per_volt":"PER:VOLT","per_curr":"PER:CURR","cont":"CONT","diode":"DIOD","temp":"TEMP","tcouple":"TCO","none":"NONE"}) def _normalize_function(self, func): func=func.strip('"').upper().split(":") alias={"CURRENT":"CURR","VOLTAGE":"VOLT","CAPACITANCE":"CAP","RESISTANCE":"RES","FRESISTANCE":"FRES","RATIO":"RAT", "FREQUENCY":"FREQ","PERIOD":"PER","CONTINUITY":"CONT","DIODE":"DIOD","TCOUPLE":"TCO","TEMPERATURE":"TEMP"} func=[alias.get(p,p) for p in func] if len(func)==1: if func[0] in ["CURR","VOLT"]: func=func+["DC"] elif func[0] in ["FREQ","PER"]: func=func+["VOLT"] elif func==["VOLT","RAT"]: func=["VOLT","DC","RAT"] return ":".join(func) def _get_function_kind(self, function): if function in ["VOLT:DC","VOLT:AC","CURR:DC","CURR:AC","VOLT:DC:RAT"]: return "iv" if function in ["RES","FRES"]: return "res" if function=="CAP": return "cap" if function in ["FREQ:VOLT","FREQ:CURR","PER:VOLT","PER:CURR"]: return "freq" if function=="TEMP": return "temp" if function=="TCO": return "tcoup" return "misc" def _check_function_kind(self, function, include, msg): fkind=self._get_function_kind(function) if fkind not in include: function=self._as_parameter_class("function").i(function) raise ValueError("this method only supports {} functions; got {}".format(msg,function)) def _value_to_text(self, value, extra=("MIN","MAX","DEF"),): if isinstance(value,py3.textstring): value=value.upper() if value not in extra: extra=", ".join("'{}'".format(v) for v in extra) raise ValueError("unsupported parameter: '{}'; allowed values are {}".format(value,extra)) else: value="{:.8E}".format(value) return value _p_reading_channel=interface.EnumParameterClass("reading_channel",{"primary":1,"secondary":2}) _reading_channels=["primary","secondary"] @interface.use_parameters(_returns="function",channel="reading_channel") def _get_single_function(self, channel): return self._normalize_function(self.ask(":SENS:FUNC{}?".format(channel)))
[docs] def get_function(self, channel="primary"): """Get measurement function for the given measurement channel (``"primary"`` or ``"secondary"``, or ``"all"`` for both channels)""" if channel=="all": return tuple(self._get_single_function(k) for k in self._reading_channels) return self._get_single_function(channel)
@interface.use_parameters(function="function",channel="reading_channel") def _set_single_function(self, function, channel): self.write(":SENS:FUNC{}".format(channel),'"{}"'.format(function)) return self._wip._get_single_function(channel=channel)
[docs] def set_function(self, function, channel="primary", reset_secondary=True): """ Set measurement function for the given measurement channel (``"primary"``, ``"secondary"``, or ``"all"`` for both channels). If ``reset_secondary==True`` and the primary function is changed, set the secondary function to ``"none"`` to avoid conflicts. """ if channel=="all" or isinstance(function,(tuple,list)): self._set_single_function("none","secondary") return tuple(self._set_single_function(f,k) for f,k in zip(function,self._reading_channels)) if reset_secondary and channel=="primary" and self.get_function()!=function: self._set_single_function("none","secondary") return self._set_single_function(function,channel)
_supported_parameter_functions=["volt_dc","volt_ac","curr_dc","curr_ac","volt_ratio","res","fres","cap","freq_volt","freq_curr","per_volt","per_curr"] def _get_all_function_parameters(self, function, include): result={} if function=="VOLT:DC:RAT": function="VOLT:DC" if "rng" in include: result["rng"]=self.ask(":SENS:{}:RANG?".format(function),"float") if "resolution" in include: result["resolution"]=self.ask(":SENS:{}:RES?".format(function),"float") if "autorng" in include: result["autorng"]=self.ask(":SENS:{}:RANG:AUTO?".format(function),"bool") if "aperture" in include: result["aperture"]=self.ask(":SENS:{}:APER?".format(function.split(":")[0]),"float") return result
[docs] @interface.use_parameters(function="function") def get_vcr_function_parameters(self, function=None): """ Get parameters for the given voltage, current or resistance measurement function. Supported functions are ``"volt_dc"``, ``"volt_ac"``, ``"curr_dc"``, ``"curr_ac"``, ``"res"``, and ``"fres"``. Return tuple ``(rng, resolution, autorng)`` with, correspondingly, measurement range, resolution, and whether autorange is enabled. """ function=self._wop._get_single_function() if function is None else function self._check_function_kind(function,["iv","res"],"current, voltage and resistance") return TGenericFunctionParameters(**self._get_all_function_parameters(function,["rng","resolution","autorng"]))
[docs] @interface.use_parameters(function="function") def get_cap_function_parameters(self, function=None): """ Get parameters for the given capacitance measurement function. The only supported function is ``"cap"``. Return tuple ``(rng, autorng)`` with, correspondingly, measurement range and whether autorange is enabled. """ function=self._wop._get_single_function() if function is None else function self._check_function_kind(function,["cap"],"capacitance") return TGenericFunctionParameters(resolution=None,**self._get_all_function_parameters(function,["rng","autorng"]))
[docs] @interface.use_parameters(function="function") def get_freq_function_parameters(self, function=None): """ Set parameters for the given frequency or period measurement function. Supported functions are ``"freq_volt"``, ``"freq_curr"``, ``"per_volt"``, ``"per_curr"``. Return tuple ``(rng, aperture)`` with, correspondingly, measurement range, and the averaging aperture. """ function=self._wop._get_single_function() if function is None else function self._check_function_kind(function,["freq"],"frequency and period") return TFrequencyFunctionParameters(**self._get_all_function_parameters(function,["rng","aperture"]))
[docs] @interface.use_parameters(function="function") def get_function_parameters(self, function=None): """ Get function parameters for any supported function. Result depends on the function kind. See :meth:`get_vcr_function_parameters`, :meth:`get_cap_function_parameters` and :meth:`get_freq_function_parameters` for details. """ if function is None: function=self._wop._get_single_function() fkind=self._get_function_kind(function) if fkind in ["iv","res"]: return self._wip.get_vcr_function_parameters(function) if fkind=="cap": return self._wip.get_cap_function_parameters(function) if fkind=="freq": return self._wip.get_freq_function_parameters(function) function=self._as_parameter_class("function").i(function) raise ValueError("only current, voltage, resistance, capacitance, frequency and period functions are supported; got {}".format(function))
def _set_all_function_parameters(self, function, rng=None, resolution=None, autorng=None, aperture=None): if function=="VOLT:DC:RAT": function="VOLT:DC" if rng is not None: self.write(":SENS:{}:RANG".format(function),self._value_to_text(rng)) if resolution is not None: self.write(":SENS:{}:RES".format(function),self._value_to_text(resolution)) if autorng is not None: self.write(":SENS:{}:RANG:AUTO".format(function),autorng,"bool") if aperture is not None: self.write(":SENS:{}:APER".format(function),self._value_to_text(aperture))
[docs] @interface.use_parameters(function="function") def set_vcr_function_parameters(self, function=None, rng=None, resolution=None, autorng=None): """ Set parameters for the given voltage, current or resistance measurement function. Supported functions are ``"volt_dc"``, ``"volt_ac"``, ``"curr_dc"``, ``"curr_ac"``, ``"res"``, and ``"fres"``. `rng`, `resolution` and `autorng` are correspondingly, measurement range, resolution, and whether autorange is enabled. `rng` and `resolution` can also have values ``"min"``, ``"max"`` or ``"def"`` for, correspondingly, minimal possible, maximal possible, and default value. """ function=self._wop._get_single_function() if function is None else function self._check_function_kind(function,["iv","res"],"current, voltage and resistance") self._set_all_function_parameters(function,rng=rng,resolution=resolution,autorng=autorng) return self._wip.get_vcr_function_parameters(function=function)
[docs] @interface.use_parameters(function="function") def set_cap_function_parameters(self, function=None, rng=None, autorng=None): """ Set parameters for the given capacitance measurement function. The only supported function is ``"cap"``. `rng` and `autorng` are correspondingly, measurement range and whether autorange is enabled. `rng` can also have values ``"min"``, ``"max"`` or ``"def"`` for, correspondingly, minimal possible, maximal possible, and default value. """ function=self._wop._get_single_function() if function is None else function self._check_function_kind(function,["cap"],"capacitance") self._set_all_function_parameters(function,rng=rng,autorng=autorng) return self._wip.get_cap_function_parameters(function=function)
[docs] @interface.use_parameters(function="function") def set_freq_function_parameters(self, function=None, rng=None, aperture=None): """ Set parameters for the given frequency or period measurement function. Supported functions are ``"freq_volt"``, ``"freq_curr"``, ``"per_volt"``, ``"per_curr"``. `rng` and `aperture` are correspondingly, measurement range and the averaging aperture. `rng` and `aperture` can also have values ``"min"``, ``"max"`` or ``"def"`` for, correspondingly, minimal possible, maximal possible, and default value. """ function=self._wop._get_single_function() if function is None else function self._check_function_kind(function,["freq"],"frequency and period") self._set_all_function_parameters(function,rng=rng,aperture=aperture) return self._wip.get_cap_function_parameters(function=function)
[docs] @interface.use_parameters(function="function") def set_function_parameters(self, function=None, **kwargs): """ Set function parameters for any supported function. Arguments depend on the function kind. See :meth:`set_vcr_function_parameters`, :meth:`set_cap_function_parameters` and :meth:`set_freq_function_parameters` for details. """ if function is None: function=self._wop._get_single_function() fkind=self._get_function_kind(function) if fkind in ["iv","res"]: return self._wip.set_vcr_function_parameters(function,**kwargs) if fkind=="cap": return self._wip.set_cap_function_parameters(function,**kwargs) if fkind=="freq": return self._wip.set_freq_function_parameters(function,**kwargs) function=self._as_parameter_class("function").i(function) raise ValueError("only current, voltage, resistance, capacitance, frequency and period functions are supported; got {}".format(function))
[docs] @interface.use_parameters(_returns=["function",None,None]) def get_configuration(self): """ Get current measurement configuration on the primary channel. Return tuple ``(function, rng, resolution)`` with, correspondingly, measurement function, measurement range and resolution. """ res=self.ask(":CONF?") res=res.strip('"').split() if len(res)==1: func,rng,prec=res[0],None,None else: func,par=res par=par.split(",") if len(par)==1: rng,prec=float(par),None else: rng,prec=float(par[0]),float(par[1]) return TConfigurationParameters(self._normalize_function(func),rng,prec)
[docs] @interface.use_parameters(function="function") def set_configuration(self, function=None, rng=None, resolution=None): """ Set current measurement configuration on the primary channel. `function`, `rng` and `resolution` are, correspondingly, measurement function, measurement range and resolution. """ cfg=self._wop.get_configuration() function=cfg.function if function is None else function if function!=cfg.function: rng=self._value_to_text("DEF" if rng is None else rng) resolution=self._value_to_text("DEF" if resolution is None else resolution) else: rng=self._value_to_text(cfg.rng if rng is None else rng) if rng!=cfg.rng: resolution=self._value_to_text("DEF" if resolution is None else resolution) else: resolution=self._value_to_text(cfg.resolution if resolution is None else resolution) if function in ["CONT","DIOD","TEMP","TCO"]: self.write(":CONF:{}".format(function)) else: self.write(":CONF:{} {},{}".format(function,rng,resolution)) return self.get_configuration()
@interface.use_parameters(channel="reading_channel") def _get_single_reading(self, channel): if channel==2 and self.get_function("secondary")=="none": return None return self.ask("READ{}?".format(channel),"float")
[docs] def get_reading(self, channel="primary"): """Initiate and return the reading of the given measurement channel (``"primary"``, ``"secondary"``, or ``"all"`` for both channels)""" if channel=="all": return tuple(self._get_single_reading(k) for k in self._reading_channels) return self._get_single_reading(channel)
_p_averaging_mode=interface.EnumParameterClass("averaging_mode",{"moving":"MOV","repeat":"REP"})
[docs] @interface.use_parameters(_returns=["averaging_mode",None,None]) def get_averaging_parameters(self): """ Get result averaging parameters. Return tuple ``(mode, count, enabled)`` with, correspondingly, averaging mode (``"moving"`` or ``"repeat"``), number of counts to average, and whether it is enabled. """ mode=self.ask(":AVER:TCON?") count=self.ask(":AVER:COUN?","int") enabled=self.ask(":AVER:STAT?","bool") return TAveragingParameters(mode,count,enabled)
[docs] @interface.use_parameters(mode="averaging_mode") def setup_averaging(self, mode=None, count=None, enabled=None): """ Set result averaging parameters. `mode`, `count` and `enabled` are , correspondingly, averaging mode (``"moving"`` or ``"repeat"``), number of counts to average, and whether it is enabled. """ if mode is not None: self.write(":AVER:TCON",mode) if count is not None: self.write(":AVER:COUN",count,"int") if enabled is not None: self.write(":AVER:STATE",enabled,"bool") return self.get_averaging_parameters()