""" This script provides the interface to the Rohde & Schwarz HMP4040 Power-Measurement-Unit (SMU).
: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.rohde_schwarz.base_rohde_schwarz import Rohde_Schwarz
[docs]
class HMP4040(Rohde_Schwarz):
"""
Interface to the Power-Measurement-Unit (SMU) HMP4040.
The HMP4040 can source and sink power in all four voltage/current quadrants and
measure voltage and current precisely.
Initialization arguments:
addr (int):
Interface address
interface (Instrument):
GPIB, USBSerial
backend (str):
VISA backend is either '@ni' for NI-Library or
'@py' for pure python pyvisa-py backend.
By default it uses '@ni' on win32 and '@py' on
other platforms.
Example: Initialization
>>> vdd = HMP4040(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
Methods:
init()
connect and initialize
local()
switch back to instrument control
clear()
clear
reset()
reset
identify()
instrument message, reflect address & interface
message("")
instrument message ("string") or ()
error_list()
list of instrument errors
off()
deactivate outputs
on()
reactivate outputs
query_onoff()
return with True=device is on, or False=device off
stair_sweep(start,stop,dstep,stime=0,typ='VIvirts-',stair='Lin',nplc=0.1,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
get_values(typ='virts')
get response of previous stair_sweep(), choosing result rows from previous request typ
close()
terminate interface
com_recover(bool)
detect & attempt to recover out of step communication (maybe after timeout)
inst.write('*RST')
write direct to instrument
ask=inst.query(':READ?')
write and read the answer
Properties:
voltage
get/set voltage
current
get/set current
measure
get voltage,current,resistance, set "vir" to get
stair_step
set incremental ramp to target or (target,dstep) or (target,dstep,stime)
stair_slope
set incremental ramp to target or (target,slopetime)
nplc
set conversion accuracy (number of powerline cycles), [10.0 - 0.1 - 0.01]
i_clamp
set current clamping and current measurement range
v_clamp
set voltage clamping and voltage measurement range
i_range
set current measurement range (and voltage clamping)
v_range
set voltage measurement range (and current clamping)
I_range
set current drive range
V_range
set voltage drive range
i_autorange
set current measurement autorange
v_autorange
set voltage measurement autorange
I_autorange
set current drive autorange
V_autorange
set voltage drive autorange
v_protection
set voltage protection limit
onoff
get/set on state (True)
id
get IDN string
"""
A = "a" # channel A
B = "b" # channel B
C = "c" # channel C
D = "d" # channel D
interchoices = [Interface.usbserial, Interface.gpib]
[docs]
def __init__(self, addr=None, interface=None, backend=None, identify=True, instName=None):
"""
Initialize the Rohde & Schwarz HMP4040 SMU.
Parameters
----------
addr (int):
GPIB or USB address of the instrument.
interface (Interface):
Interface type (USB, GPIB, etc.).
backend (str):
Backend to use for communication.
identify (bool):
If True, identify the instrument during initialization.
instName (str):
Optional instrument name.
"""
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):
"""Set the Setup instrument settings."""
super().setup_inst()
self._measure = "vir"
self.chabcd = self.A
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], {}]
self.stair_step_get = np.array([])
self.stair_measure = ""
[docs]
def on(self):
"""Switch output on."""
self.budget.set_slack(self)
self.inst.write(':OUTP ON')
[docs]
def off(self):
"""Switch output off."""
self.budget.set_slack(self)
self.inst.write(':OUTP OFF')
@property
def onoff(self):
""" Get or set output on/off state. Setting onoff to True will switch on the output, False will switch it off."""
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()
[docs]
def query_onoff(self):
"""" Query the output on/off state. Returns True if output is on, False if output is off. """
self.onoff
[docs]
def query_output_function(self):
"""
Query the output function, return 'DC_VOLTAGE', 'DC_CURRENT' or 'RESISTANCE' depending on the source function,
or the source function name if it is not voltage or current.
Returns
-------
str: 'DC_VOLTAGE', 'DC_CURRENT', 'RESISTANCE' or source function name
"""
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
[docs]
def channel(self, ch='A'):
"""Set channel A, B, C & D."""
self.chabcd = ch.lower()
@property
def ch(self):
"""Get or Set channel A or B."""
return (self.chab)
@ch.setter
def ch(self, ch):
self.channel(ch)
@property
def measure(self):
"""Get or set measure.
Where the measurements are defined by "vir" flags (VOLTAGE,CURRENT,RESISTANCE)
"""
if not self.onoff:
return
self.budget.set_slack(self)
form = ""
sense = ""
formS = " "
senseS = " "
measr = False
if "v" in self._measure:
form += formS + 'VOLT'
sense += senseS + '"VOLT:DC"'
formS = ","
senseS = ","
if "i" in self._measure:
form += formS + 'CURR'
sense += senseS + '"CURR:DC"'
formS = ","
senseS = ","
if "r" in self._measure:
form += formS + 'RES'
sense += senseS + '"RES"'
formS = ","
senseS = ","
measr = True
senses = sense.split(",")
lnf = len(senses)
if lnf <= 0:
return ([])
self.budget.set_slack(self, lnf*0.1)
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.
"""
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))
logger.measure("{!r} voltage := {}".format(self.instName, float(value)))
self.onoff = True
if self.is_local:
self.voltage
@property
def current(self):
"""Get or set output current.
If the current is set the output is switched on immediately.
"""
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.
Returns
-------
float
Number of power line cycles for conversion, between 0.01 and 10.
"""
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
self.budget.set_slack(self)
i_range = self.i_range
new_i_range = i_range
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
self.budget.set_slack(self)
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.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
self.budget.set_slack(self)
v_range = self.v_range
new_v_range = v_range
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
self.budget.set_slack(self)
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.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):
"""
Make a stair sweep from start to stop with step dstep and delay stime, measure according to typ.
Parameters
----------
typ: 'Vvirts-', 'Ivirts-' = Voltage / Current sourced, '-' changes direction.
volts, amps, ohms, timestamp, status are sensed
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
stair: ('Lin','Log) = linear or log source
Returns
-------
List of measured values according to typ, with each value a list of the form [voltage, current, resistance, timestamp, status] depending on the flags in typ.
"""
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.
Parameters
----------
typ: 'Vvirts-', 'Ivirts-' = Voltage / Current sourced, '-' changes direction.
volts, amps, ohms, timestamp, status are sensed
Returns
-------
List of measured values according to typ, with each value a list of the form [voltage, current, resistance, timestamp, status] depending on the flags in typ.
"""
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):
"""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):
"""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)