Source code for pylab_ml.smu.keithley.keithley2400

"""Interface to the Power-Measuremet-Unit (SMU) Keithley2400.

:Date: |today|
:Author: Semi-ATE <info@Semi-ATE.org>

"""
import re
import numpy as np
import math
from pylab_ml.base_instrument import logger
from pylab_ml.collate_instrument import Interface
from pylab_ml.smu.keithley.base_keithley import Keithley


[docs] class Keithley2400(Keithley): """ Interface to the Power-Measuremet-Unit (SMU) Keithley2400. :Date: |today| :Author: Semi-ATE <info@Semi-ATE.org> .. image:: ../_static/kethley2400.jpg The Keithley2400 can source and sink power in all four voltage/current quadrants and measure voltage and current precisely """ interchoices = [Interface.usbserial, Interface.gpib]
[docs] def __init__(self, addr=None, interface=None, backend=None, identify=True, instName=None): """ Connect and initialize Keithley2400 instrument. Args: addr (int): Interface address interface (Interface): GPIB, USBSerial backend (str): VISA backend is either '@ni' for NI-Library or '@py' for pure python pyvisa-py backend. On default it uses '@ni' on win32 and '@py' on other platforms. identify (bool): If True, query the instrument ID and print it to the log. instName (string): Instance Name from parent. Example: Initialization >>> vdd = Keithley2400(addr=24) # GPIB or USB address >>> # validate displayed message Id on device >>> vdd.init() # connect and initialize instrument Example: Voltage source >>> vdd.i_clamp = 0.01 # current protection >>> vdd.voltage = 3.3 # set output voltage >>> i = vdd.current # measure (supply) current Example: Current source >>> vdd.v_clamp = 5 # voltage protection >>> vdd.current = 0.1 # set output current_range >>> v = vdd.voltage # measure voltage """ kwargs = {"addr": addr, "interface": interface, "backend": backend, "identify": identify, "instName": instName} super().__init__(**kwargs) logger.debug("Class {}".format(self.__class__.__name__)) self.msg_row_col = (1, 20)
[docs] def setup_inst(self): """Start setup instrument settings, called from class instruments.""" super().setup_inst() self.mqtt_all = ['id', 'output_function', 'onoff', 'voltage', 'current', 'Voltage', 'Current', 'v_autorange', 'i_autorange', 'V_autorange', 'I_autorange', 'v_clamp', 'i_clamp', 'v_range', 'i_range', 'V_range', 'I_range'] if self.inst: if self.interface == Interface.gpib: self.inst.read_termination = '\n' self.inst.write_termination = '\n' else: self.inst.read_termination = '\r' self.inst.write_termination = '\r' self._measure = "vir" self._nplc = 0.1 self._i_clamp = 105e-6 # max current (A) set to reset value=100uA self._i_range = 105e-6 # current range (A) set to 100uA self._v_clamp = 21.0 self._v_range = 21.0 self.inst.write(':SOUR:VOLT:PROT:LEV 20') self.stair_step_set = [[0, 0, 0.1], {"typ": "V"}] self.stair_step_get = np.array([]) self.stair_measure = ""
[docs] def on(self): """Reactivate outputs.""" self.budget.set_slack(self) self.inst.write(':OUTP ON')
[docs] def off(self): """Deactivate outputs.""" self.budget.set_slack(self) self.inst.write(':OUTP OFF')
@property def onoff(self): """Set/get on state (True).""" self.budget.set_slack(self) result = int(self.inst.query(':OUTPUT:STATE?')) == 1 return (result) @onoff.setter def onoff(self, value): if value: self.on() else: self.off() @property def output_function(self): """Get or set output function (VOLT or CURR).""" self.budget.set_slack(self) if self.inst.query(':SOURce:FUNCtion:MODE?') == 'VOLT': return 'DC_VOLTAGE' elif self.inst.output_function.name == 'CURR': return 'DC_CURRENT' else: return self.inst.output_function.name @property def measure(self): """ Get or set measure. Where the measurements are defined by "vir" flags (VOLTAGE,CURRENT,RESISTANCE) eg. "v" for voltage, "i" for current, "r" for resistance, "vi" for voltage and current, etc. PARAMETERS ---------- typ : str String with "vir" flags (VOLTAGE,CURRENT,RESISTANCE) defining the measurements to be made. For example, "v" for voltage, "i" for current, "r" for resistance, "vi" for voltage and current, etc. RETURNS ------- list List of measured values corresponding to the "vir" flags. """ if not self.onoff: return self.budget.set_slack(self) form = "" sense = "" formS = " " senseS = " " # measv = False # measi = False measr = False if "v" in self._measure: form += formS + 'VOLT' sense += senseS + '"VOLT:DC"' formS = "," senseS = "," # measv = True if "i" in self._measure: form += formS + 'CURR' sense += senseS + '"CURR:DC"' formS = "," senseS = "," # measi = True if "r" in self._measure: form += formS + 'RES' sense += senseS + '"RES"' formS = "," senseS = "," measr = True senses = sense.split(",") lnf = len(senses) if lnf <= 0: self.budget.set_slack(self) return ([]) self.budget.set_slack(self, lnf * 0.2) self.inst.write(':SENS:FUNC:CONC ON') if sense != "": self.inst.write(':SENS:FUNC {}'.format(sense)) if measr: self.inst.write(':SENS:RES:MODE MAN') self.inst.write(':TRIG:COUN 1') if form != "": self.inst.write(':FORM:ELEM {}'.format(form)) elements = self.inst.query(':FORM:ELEM?').split(",") logger.info("{!r} measure returns {}".format(self.instName, elements)) lne = len(elements) if lne > 0: value = self.inst.query(':READ?') res = [float(i) for i in value.split(",")] logger.measure("{!r} {} == {}".format(self.instName, elements, res)) self.budget.set_slack(self) return (res) else: self.budget.set_slack(self) return ([]) @measure.setter def measure(self, typ="vir"): self._measure = typ @property def voltage(self): """Get or set output voltage. If the voltage is set the output is switched on immediately. Returns ------- value(float) : output voltage (in V). """ self.budget.set_slack(self) if not self.onoff: return self.inst.write(':SENS:FUNC "VOLT"') self.inst.write(':TRIG:COUN 1') self.inst.write(':FORM:ELEM VOLT') value = self.inst.query(':READ?') logger.measure("{!r} voltage == {}".format(self.instName, float(value))) return float(value) @voltage.setter def voltage(self, value): self.budget.set_slack(self) self.inst.write(':SOUR:FUNC VOLT') self.inst.write(':SOUR:VOLT:MODE FIXED') # self.inst.write(':SOUR:VOLT:RANG {}'.format(1.1*value)) self.inst.write(':SOUR:VOLT:LEV {}'.format(value)) self._Voltage = value logger.measure("{!r} voltage := {}".format(self.instName, float(value))) self.onoff = True if self.is_local: self.voltage @property def Voltage(self): """Get driver Voltage. Returns ------- value(float) : driver Voltage (in V). """ value = self._Voltage return value @property def current(self): """Get or set output current. If the current is set the output is switched on immediately. Returns ------- value(float) : output current (in A). """ self.budget.set_slack(self) if not self.onoff: return self.inst.write(':SENS:FUNC "CURR"') self.inst.write(':TRIG:COUN 1') self.inst.write(':FORM:ELEM CURR') value = self.inst.query(':READ?') logger.measure("{!r} current == {}".format(self.instName, float(value))) return float(value) @current.setter def current(self, value): self.budget.set_slack(self) self.inst.write(':SOUR:FUNC CURR') self.inst.write(':SOUR:CURR:MODE FIXED') # self.inst.write(':SOUR:CURR:RANG {}'.format(1.1*value)) self.inst.write(':SOUR:CURR:LEV {}'.format(value)) logger.measure("{!r} current := {}".format(self.instName, float(value))) self.onoff = True if self.is_local: self.current @property def nplc(self): """Set the conversion number of power line cycles accuracy, for all converters. For a plc of 1.0, conversion rate is 1/50s = 20ms. Accuracy max 10, min 0.01. Returns ------- value(float) : number of power line cycles for conversion. """ self.budget.set_slack(self) val = self.inst.query(':SENS:VOLT:NPLC?') return float(val) @nplc.setter def nplc(self, cycles): # cycles are number of power line cycles for conversion self._nplc = cycles self.budget.set_slack(self) # in Keithley 2400 each measurement (v,i,r) is common accuracy self.inst.write(':SENS:VOLT:NPLC {}'.format(cycles)) # so implicitly self.inst.write(':SENS:CURR:NPLC {}'.format(cycles)) @property def v_autorange(self): """Autorange voltage measurement on or off.""" self.budget.set_slack(self) res = self.inst.query(':SENS:VOLT:RANG:AUTO?') return (float(res) != 0) @v_autorange.setter def v_autorange(self, on): self.budget.set_slack(self) if on: self.inst.write(':SENS:VOLT:RANG:AUTO ON') else: self.inst.write(':SENS:VOLT:RANG:AUTO OFF') @property def i_autorange(self): """Autorange current measurement on or off.""" self.budget.set_slack(self) res = self.inst.query(':SENS:CURR:RANG:AUTO?') return (float(res) != 0) @i_autorange.setter def i_autorange(self, on): self.budget.set_slack(self) if on: self.inst.write(':SENS:CURR:RANG:AUTO ON') else: self.inst.write(':SENS:CURR:RANG:AUTO OFF') @property def V_autorange(self): """Autorange drive voltage on or off.""" self.budget.set_slack(self) res = self.inst.query(':SOUR:VOLT:RANG:AUTO?') return (float(res) != 0) @V_autorange.setter def V_autorange(self, on): self.budget.set_slack(self) if on: self.inst.write(':SOUR:VOLT:RANG:AUTO ON') else: self.inst.write(':SOUR:VOLT:RANG:AUTO OFF') @property def I_autorange(self): """Autorange drive current on or off.""" self.budget.set_slack(self) res = self.inst.query(':SOUR:CURR:RANG:AUTO?') return (float(res) != 0) @I_autorange.setter def I_autorange(self, on): self.budget.set_slack(self) if on: self.inst.write(':SOUR:CURR:RANG:AUTO ON') else: self.inst.write(':SOUR:CURR:RANG:AUTO OFF') @property def I_range(self): """Set the current drive range (A).""" self.budget.set_slack(self) val = float(self.inst.query(':SOUR:CURR:RANG?')) return val @I_range.setter def I_range(self, imax): # imax in A self._I_range = imax self.budget.set_slack(self) self.inst.write(':SOUR:CURR:RANG {}'.format(imax)) logger.info("{!r} I_range {}A".format(self.instName, imax)) @property def V_range(self): """Set the voltage drive range (V).""" self.budget.set_slack(self) val = float(self.inst.query(':SOUR:VOLT:RANG?')) return val @V_range.setter def V_range(self, vmax): # vmax in V self._V_range = vmax self.budget.set_slack(self) self.inst.write(':SOUR:VOLT:RANG {}'.format(vmax)) logger.info("{!r} V_range {}V".format(self.instName, vmax)) @property def i_clamp(self): """Set the current clamping (A), will adjust current measurement range.""" self.budget.set_slack(self) limit = float(self.inst.query(':SENS:CURR:PROT?')) return limit @i_clamp.setter def i_clamp(self, imax): # imax in A self._i_clamp = imax i_range = self.i_range new_i_range = i_range self.budget.set_slack(self) self.inst.write(':SENS:CURR:PROT {}'.format(imax)) # setting the current range needs to enable the voltage source modus self.inst.write(':SOUR:FUNC VOLT') if i_range > 100 * imax: # cannot have compliance (i_clamp) to less than 1% of range new_i_range = 100 * imax elif i_range < imax: # cannot set compliance (i_clamp) to more than range new_i_range = imax if i_range != new_i_range: logger.warning("{!r} i_clamp {}A, i_range accomodating {}A from {}A".format(self.instName, imax, new_i_range, i_range)) self._i_range = new_i_range self.inst.write(':SENS:CURR:RANG {}'.format(new_i_range)) else: logger.info("{!r} i_clamp {}A".format(self.instName, imax)) @property def i_range(self): """Set the current measurement range (A), will adjust current clamping.""" self.budget.set_slack(self) val = float(self.inst.query(':SENS:CURR:RANG?')) return val @i_range.setter def i_range(self, imax): # imax in A self._i_range = imax i_clamp = self.i_clamp new_i_clamp = i_clamp if imax / 100 > i_clamp: # cannot have compliance (i_clamp) to less than 1% of range new_i_clamp = imax / 100 elif i_clamp > imax: # cannot set compliance (i_clamp) to more than range new_i_clamp = imax self.budget.set_slack(self) self.inst.write(':SENS:CURR:RANG {}'.format(imax)) if i_clamp != self._i_clamp: logger.warning("{!r} i_range {}A, i_clamp accomodating {}A from {}A".format(self.instName, imax, new_i_clamp, i_clamp)) self.i_clamp = i_clamp else: logger.info("{!r} i_range {}A".format(self.instName, imax)) @property def v_clamp(self): """Set the voltage clamping (V), will adjust voltage measurement range.""" self.budget.set_slack(self) limit = float(self.inst.query(':SENS:VOLT:PROT?')) return limit @v_clamp.setter def v_clamp(self, vmax): self._v_clamp = vmax v_range = self.v_range new_v_range = v_range self.budget.set_slack(self) self.inst.write(':SENS:VOLT:PROT {}'.format(vmax)) # setting the voltage range needs to enable the current source modus self.inst.write(':SOUR:FUNC CURR') if v_range > 100 * vmax: # cannot have compliance (v_clamp) to less than 1% of range new_v_range = 100 * vmax elif v_range < vmax: # cannot set compliance (v_clamp) to more than range new_v_range = vmax if v_range != new_v_range: logger.warning("{!r} v_clamp {}V. v_range accomodating {}V from {}V".format(self.instName, vmax, new_v_range, v_range)) self._v_range = new_v_range self.inst.write(':SENS:VOLT:RANG {}'.format(new_v_range)) else: logger.info("{!r} v_clamp {}V".format(self.instName, vmax)) @property def v_range(self): """Set the voltage measurement range, will adjust voltage clamping.""" self.budget.set_slack(self) val = float(self.inst.query(':SENS:VOLT:RANG?')) return val @v_range.setter def v_range(self, vmax): # vmax in V self._v_range = vmax v_clamp = self.v_clamp new_v_clamp = v_clamp if (vmax / 100) > v_clamp: # cannot have compliance (v_clamp) to less than 1% of range new_v_clamp = vmax / 100 elif v_clamp > vmax: # cannot set compliance (v_clamp) to more than range new_v_clamp = vmax self.budget.set_slack(self) self.inst.write(':SENS:VOLT:RANG {}'.format(vmax)) if v_clamp != new_v_clamp: logger.warning("{!r} v_range {}V, v_clamp accomodating {}V from {}V".format(self.instName, vmax, new_v_clamp, v_clamp)) self.v_clamp = new_v_clamp else: logger.info("{!r} v_range {}V".format(self.instName, vmax)) @property def v_protection(self): """Set the voltage protection limit|default|min|max, get 4 value tuple.""" self.budget.set_slack(self) lim = self.inst.query(':SOUR:VOLT:PROT:LEV?') lim_def = self.inst.query(':SOUR:VOLT:PROT:LEV? DEF') lim_min = self.inst.query(':SOUR:VOLT:PROT:LEV? MIN') lim_max = self.inst.query(':SOUR:VOLT:PROT:LEV? MAX') return ((float(lim), float(lim_def), float(lim_min), float(lim_max))) @v_protection.setter def v_protection(self, lim): self.budget.set_slack(self) if re.match("def.*", str(lim), re.I): lim = "DEF" elif re.match("min.*", str(lim), re.I): lim = "MIN" elif re.match("max.*", str(lim), re.I): lim = "MAX" self.inst.write(':SOUR:VOLT:PROT:LEV {}'.format(lim))
[docs] def stair_sweep(self, start, stop, dstep, stime=0, typ='V', stair='Lin', wait=False): """ Sweep V or I, measure v,i,r at time t with state s, change by dstep, steptime stime, wait for response. If dstep is None then stime is slopetime (>1ms) & nplc is min : 0.01. Parameters ---------- start : Start value in volts or amps, None implies incremental stop : Stop value in volts or amps dstep : Delta amplitude for Lin, Points to interpolate for Log, stime is slope time for None (>1ms) stime : Delay between steps or slope time if dstep == None typ : 'Vvirts-', 'Ivirts-' = Voltage / Current sourced, '-' changes direction, volts, amps, ohms, timestamp, status are sensed stair : ('Lin','Log) = linear or log source Returns ------- None """ args = [None, stop, dstep] kwargs = {'stime': stime, 'typ': typ, 'stair': stair} self.stair_step_set = [args, kwargs] self.budget.set_slack(self) form = "" sense = "" measure = "" formS = "" senseS = "" measv = False measi = False measr = False if "v" in typ: form += formS + 'VOLT' sense += senseS + '"VOLT:DC"' formS = "," senseS = "," measv = True measure += "v" if "i" in typ: form += formS + 'CURR' sense += senseS + '"CURR:DC"' formS = "," senseS = "," measi = True measure += "i" if "r" in typ: form += formS + 'RES' sense += senseS + '"RES"' formS = "," senseS = "," measr = True measure += "r" if "t" in typ: form += formS + 'TIME' formS = "," measure += "t" if "s" in typ: form += formS + 'STAT' formS = "," measure += "t" if start is None or str(start) == '?': start = self.voltage if start is None: start = 0 logger.warning("{!r} initial start assumed to be 0".format(self.instName)) if dstep is not None: if start > stop and dstep > 0: dstep = -1.0 * dstep direction = "UP" final = stop if "-" in typ: direction = "DOWN" final = start if dstep is None: # stime is slope time # No measurement # 1001 points for 10V == 805ms # 1001 points for 1V == 805ms # 101 points for 1V == 81ms # 11 points for 1V == 8.2ms # V measurement, nplc = 0.01 # 1001 points for 10V == 1300ms # 1001 points for 1V == 1300ms # 101 points for 1V == 132ms # 11 points for 1V == 13.6ms # I measurement, nplc = 0.01 # 1001 points for 10V == 1480ms # 1001 points for 1V == 1440ms # 101 points for 1V == 142ms # 11 points for 1V == 14.6ms # VI (+R) measurement, nplc = 0.01 # 1001 points for 10V == 2680ms # 1001 points for 1V == 2625ms # 101 points for 1V == 265ms # 11 points for 1V == 27ms # R measurement, nplc = 0.01 # 101 points for 1V == 150ms nplc = 0.01 rang = abs(stop - start) max_pts = int(math.log(rang + 1.05) * 50) if measv and measi or measv and measr or measi and measr: meas_cost = 2.66e-3 elif measv: meas_cost = 1.33e-3 elif measi or measr: meas_cost = 1.45e-3 else: meas_cost = 0.8e-3 meas_pts = int(stime / meas_cost) + 2 if meas_pts < max_pts: pts = meas_pts step_time = 0 else: pts = max_pts step_time = (stime - pts * meas_cost) / pts if pts < 2: dstep = (stop - start) step_time = 0 else: dstep = (stop - start) / (pts - 1) logger.debug("{!r} rang {} max_pts {} meas_pts {} pts {} dstep {} step_time {}".format(self.instName, rang, max_pts, meas_pts, pts, dstep, step_time)) else: nplc = self._nplc step_time = stime if stair.upper() == 'LIN': if stop <= start and dstep > 0 or stop >= start and dstep < 0 or dstep == 0: return if (stop-start) / dstep <= 1.0: dstep = (stop-start) / 1.0 logger.debug("{!r} dstep adjusted to {} on this sweep".format(self.instName, dstep)) pts = int((stop-start)/dstep + 1) else: pts = dstep + 1 if pts < 2: pts = 2 self.budget.set_slack(self, 2) self.inst.write(':STATUS:QUE:CLE') # clear error queue senses = sense.split(",") lnf = len(senses) if lnf > 1: self.inst.write(':SENS:FUNC:CONC ON') logger.measure("{!r} stair_sweep {} concurrently".format(self.instName, senses)) else: self.inst.write(':SENS:FUNC:CONC OFF') logger.measure("{!r} stair_sweep {} individually".format(self.instName, senses)) if measr: # resistance measurement self.inst.write(':SENS:RES:MODE MAN') if measv or measi or measr: self.inst.write(':SENS:FUNC {}'.format(sense)) else: self.inst.write(':SENS:FUNC:OFF:ALL') if form != "": self.inst.write(':FORM:ELEM {}'.format(form)) elements = self.inst.query(':FORM:ELEM?').split(",") if measv or measi or measr: logger.info("{!r} stair_sweep returns {} * {}".format(self.instName, elements, pts)) else: logger.info("{!r} stair_sweep drives {} stepoints".format(self.instName, pts)) lne = len(elements) self.inst.write(':SOUR:SWE:DIR {}'.format(direction)) if "V" in typ: # self.voltage = start self.inst.write(':SOUR:FUNC VOLT') self.inst.write(':SOUR:VOLT:START {}'.format(start)) self.inst.write(':SOUR:VOLT:STOP {}'.format(stop)) self.inst.write(':SOUR:VOLT:MODE SWE') self.inst.write(':SYSTEM:AZER:STAT 0') self.inst.write(':SENS:CURR:NPLC {}'.format(nplc)) if stair.upper() == 'LIN': self.inst.write(':SOUR:SWE:SPAC LIN') self.inst.write(':SOUR:VOLT:STEP {}'.format(dstep)) logger.measure("{!r} stair_sweep lin voltage {} points".format(self.instName, pts)) else: self.inst.write(':SOUR:SWE:SPAC LOG') self.inst.write(':SOUR:SWE:POIN {}'.format(pts)) logger.measure("{!r} stair_sweep log voltage {} points".format(self.instName, pts)) elif "I" in typ: # self.current = start self.inst.write(':SOUR:FUNC CURR') self.inst.write(':SOUR:CURR:START {}'.format(start)) self.inst.write(':SOUR:CURR:STOP {}'.format(stop)) self.inst.write(':SOUR:CURR:MODE SWE') self.inst.write(':SYSTEM:AZER:STAT 0') self.inst.write(':SENS:VOLT:NPLC {}'.format(nplc)) if stair.upper() == 'LIN': self.inst.write(':SOUR:SWE:SPAC LIN') self.inst.write(':SOUR:CURR:STEP {}'.format(dstep)) logger.measure("{!r} stair_sweep lin current {} points".format(self.instName, pts)) else: self.inst.write(':SOUR:SWE:SPAC LOG') self.inst.write(':SOUR:SWE:POIN {}'.format(pts)) logger.measure("{!r} stair_sweep log current {} points".format(self.instName, pts)) self.inst.write(':SOUR:DEL {}'.format(step_time)) self.on() self.inst.write(':SYST:TIME:RES') self.stair_measure = measure if wait: self.inst.write(':TRIG:COUN {}'.format(pts)) if measv or measi or measr: value = self.inst.query(':READ?') if "V" in typ: self.inst.write(':SOUR:VOLT:MODE FIXED') self.inst.write(':SOUR:VOLT:LEV {}'.format(final)) self.inst.write(':SENS:VOLT:NPLC {}'.format(self._nplc)) else: self.inst.write(':SOUR:CURR:MODE FIXED') self.inst.write(':SOUR:CURR:LEV {}'.format(final)) self.inst.write(':SENS:CURR:NPLC {}'.format(self._nplc)) self.budget.set_slack(self) res = [float(i) for i in value.split(",")] na = np.array(res) nb = na.reshape((len(na))//lne, lne) nc = nb.transpose() return (nc) else: self.inst.write(':INIT') if "V" in typ: self.inst.write(':SOUR:VOLT:MODE FIXED') self.inst.write(':SOUR:VOLT:LEV {}'.format(final)) self.inst.write(':SENS:VOLT:NPLC {}'.format(self._nplc)) else: self.inst.write(':SOUR:CURR:MODE FIXED') self.inst.write(':SOUR:CURR:LEV {}'.format(final)) self.inst.write(':SENS:CURR:NPLC {}'.format(self._nplc)) else: self.budget.set_slack(self, pts * (step_time * 1.0 + lne * 0.1)) self.inst.write(':TRAC:FEED SENS') self.inst.write(':TRAC:POIN {}'.format(pts)) self.inst.write(':TRAC:CLE') self.inst.write(':TRAC:FEED:CONT NEXT') self.inst.write(':TRIG:COUN {}'.format(pts)) self.inst.write(':INIT') if "V" in typ: self.inst.write(':SOUR:VOLT:MODE FIXED') self.inst.write(':SOUR:VOLT:LEV {}'.format(final)) self.inst.write(':SENS:VOLT:NPLC {}'.format(self._nplc)) else: self.inst.write(':SOUR:CURR:MODE FIXED') self.inst.write(':SOUR:CURR:LEV {}'.format(final)) self.inst.write(':SENS:CURR:NPLC {}'.format(self._nplc))
[docs] def get_values(self, typ=''): """ Transfer last requested measurement sweep results. Get response of previous stair_sweep(), choosing result rows from previous request typ. Parameters ---------- typ : str String with "vir" flags (VOLTAGE,CURRENT,RESISTANCE) defining the measurements to be returned. For example, "v" for voltage, "i" for current, "r" for resistance, "vi" for voltage and current, etc. Returns ------- np.array Numpy array of measured values corresponding to the "vir" flags in typ, with shape (len(typ), number of steps). For example, if typ is "vi", the returned array will have two rows: the first row for voltage measurements and the second row for current measurements, with columns corresponding to each step in the previous stair_sweep(). """ if not typ or typ == '': typ = self.stair_measure unknown_measure = [tm for tm in typ if not tm in "VI" + self.stair_measure] if unknown_measure: logger.error("{!r} get_values '{}' for stair_sweep of '{}' unknown".format(self.instName, unknown_measure, self.stair_measure)) return np.array(None) lne = len(self.stair_measure) length = self.inst.query(':TRAC:POIN?') length = int(float(length)) if length > 0 and lne > 0: self.budget.set_slack(self, length * lne) smL = [] for smi in range(lne): if self.stair_measure[smi] in typ: smL.append(smi) value = self.inst.query(':TRAC:DATA?') res = [float(i) for i in value.split(",")] self.budget.set_slack(self) na = np.array(res) nb = na.reshape((len(na))//lne, lne) nc = nb.transpose() if smL != list(range(lne)): nc = nc[smL] else: self.budget.set_slack(self) nc = np.array(None) return (nc)
@property def stair_step(self): """Set incremental ramp to target or (target,dstep) or (target,dstep,stime). Stair_step to target or target,dstep or target,dstep,stime, using previous stair_sweep() parameters. """ return (self.stair_step_get) @stair_step.setter def stair_step(self, stair_set): if not type(stair_set) in [type(tuple()), type(list())]: stair_set_args, stair_set_kwargs = self.stair_step_set stair_set_args[0] = None stair_set_args[1] = stair_set stair_set_kwargs['wait'] = False stair_set_kwargs['typ'] = stair_set_kwargs['typ'].replace('-', '') self.stair_step_get = self.stair_sweep(*stair_set_args, **stair_set_kwargs) else: if len(stair_set) == 2: stair_set_args, stair_set_kwargs = self.stair_step_set stair_set_args[0] = None stair_set_args[1] = stair_set[0] stair_set_args[2] = stair_set[1] stair_set_kwargs['wait'] = False stair_set_kwargs['typ'] = stair_set_kwargs['typ'].replace('-', '') self.stair_step_get = self.stair_sweep(*stair_set_args, **stair_set_kwargs) if len(stair_set) == 3: stair_set_args, stair_set_kwargs = self.stair_step_set stair_set_args[0] = None stair_set_args[1] = stair_set[0] stair_set_args[2] = stair_set[1] stair_set_kwargs['wait'] = False stair_set_kwargs['stime'] = stair_set[2] stair_set_kwargs['typ'] = stair_set_kwargs['typ'].replace('-', '') self.stair_step_get = self.stair_sweep(*stair_set_args, **stair_set_kwargs) @property def stair_slope(self): """Set incremental ramp to target or (target,slopetime). Stair_slope to target or target,slope_time using previous stair_sweep() parameters. """ return (self.stair_step_get) @stair_slope.setter def stair_slope(self, stair_set): if not type(stair_set) in [type(tuple()), type(list())]: stair_set_args, stair_set_kwargs = self.stair_step_set stair_set_args[0] = None stair_set_args[1] = stair_set stair_set_args[2] = None stair_set_kwargs['wait'] = False stair_set_kwargs['typ'] = stair_set_kwargs['typ'].replace('-', '') self.stair_step_get = self.stair_sweep(*stair_set_args, **stair_set_kwargs) else: stair_set_args, stair_set_kwargs = self.stair_step_set stair_set_args[0] = None stair_set_args[1] = stair_set[0] stair_set_args[2] = None stair_set_kwargs['stime'] = stair_set[1] stair_set_kwargs['wait'] = False stair_set_kwargs['typ'] = stair_set_kwargs['typ'].replace('-', '') self.stair_step_get = self.stair_sweep(*stair_set_args, **stair_set_kwargs)