Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- class ChromaProgLoad(chroma6310.chroma6310, Instrument):
- """Class for Chroma programmable DC electronic load"""
- def __init__(self, *args, **kwargs):
- super(ChromaProgLoad, self).__init__(*args, **kwargs)
- self.chan_dict = {}
- #self._interface.writebybytes = True
- #self._interface.message_delay = 0.05
- self._interface.update_settings()
- """
- Currently there is no IVI standard for programmable loads.
- Rather than trying to invent one to follow the existing IVI standards
- and then wrapping around it, for now just keep the chroma6310 class
- a bare skeleton and do all the work in this class with the interface
- we want to use.
- """
- """
- Modes of operation: Constant Current (CC), Constant Resistance (CR),
- Constant Voltage (CV), Constant Power (CP).
- Low and high modes available. The low range provides better
- resolution at low current settings.
- Static and dynamic modes available. Static checks the stability of
- the output voltage from a power supply. Dynamic mode takes two
- load levels (CxDLT1, CxDLT2) and a slew rate (CxDL up, CxDL down) to
- switch between the two, which would be useful for transient load
- testing.
- """
- def _chan_num(self, num_or_label):
- num = None
- if type(num_or_label) is str:
- num = self.chan_dict[num_or_label]
- elif type(num_or_label) is int:
- num = num_or_label
- else:
- raise TypeError('Channel number or label expected')
- return num
- def set_chan_label(self, chan_num, label):
- self.chan_dict[label] = chan_num
- def switch_chan(self, num_or_label):
- """Switch to specified channel @num_or_label."""
- num = self._chan_num(num_or_label)
- self._write('CHAN {num:d}'.format(num=num))
- def get_chan_num(self):
- """Return current channel number."""
- return int(self._ask('CHAN?'))
- def get_chan_label(self, num=None):
- """Return channel label for given @num, or current channel label if
- no @num specified."""
- if num is None:
- num = int(self._ask('CHAN?'))
- for key in self.chan_dict:
- if self.chan_dict[key] == num:
- return key
- return str(num)
- def _measure_all(self, mtype='current'):
- """Fetch voltages, currents, or power values measured on all channels
- as a dictionary, where the key is the channel label and the value is
- the measurement."""
- m = mtype[0].upper()
- res = self._ask('FETC:ALL{m:s}?'.format(m=m))
- vals = res.split(',')
- d = {}
- num = 1
- for v in vals:
- label = self.get_chan_label(num)
- d[label] = float(v)
- num += 1
- return d
- def get_load_voltage(self, chan=None):
- """Measure voltage on @chan (number or label), or current channel if
- @chan is None."""
- if not chan is None:
- self.switch_chan(chan)
- return float(self._ask('FETC:VOLT?'))
- def get_load_current(self, chan=None):
- """Measure current on @chan_num (1-based), or current channel if
- @chan_num is None."""
- if not chan is None:
- self.switch_chan(chan)
- return float(self._ask('FETC:CURR?'))
- def get_max_current(self, chan=None):
- """Ask instrument for the maximum current allowed on @chan_num
- (1-based), or current channel if @chan_num is None."""
- if not chan is None:
- self.switch_chan(chan)
- return float(self._ask('CURR:STAT:L1? MAX'))
- def get_load_power(self, chan=None):
- """Measure power on @chan_num (1-based), or current channel if
- @chan_num is None."""
- if not chan is None:
- self.switch_chan(chan)
- # Older Chroma 6314 (not 6314A) doesn't seem to support FETC:POW?
- # Simulate it by doing a voltage and current measurement
- # and multiplying the results
- if self.get_model() == '6314':
- v = float(self._ask('FETC:VOLT?'))
- i = float(self._ask('FETC:CURR?'))
- return v*i
- return float(self._ask('FETC:POW?'))
- def get_all_voltages(self):
- """Fetch voltages measured on all channels as a dictionary, where
- the key is the channel label and the value is the voltage measured."""
- return self._measure_all('voltage')
- def get_all_currents(self):
- """Fetch currents measured on all channels as a dictionary, where
- the key is the channel label and the value is the current measured."""
- return self._measure_all('current')
- def get_all_powers(self):
- """Fetch powers measured on all channels as a dictionary, where
- the key is the channel label and the value is the watts measured."""
- if self.get_model() == '6314':
- all_v = self.get_all_voltages()
- all_i = self.get_all_currents()
- return {k: all_v[k] * all_i[k] for k in all_v}
- return self._measure_all('power')
- def short_enable(self, en, chan=None):
- """Activate or deactivate short-circuit simulation for @chan"""
- if not chan is None:
- self.switch_chan(chan)
- if en:
- state_s = 'ON'
- # Use CRL mode to have 0.01 ohm short
- self._write('MODE CRL')
- else:
- state_s = 'OFF'
- self._write('LOAD:SHOR {state:s}'.format(state=state_s))
- def activate_chan(self, chan=None):
- """Turn on electronic load on given @chan."""
- if not chan is None:
- self.switch_chan(chan)
- self._write('LOAD ON')
- def deactivate_chan(self, chan=None):
- """Turn off electronic load on given @chan."""
- if not chan is None:
- self.switch_chan(chan)
- self._write('LOAD OFF')
- def chan_is_active(self, chan=None):
- """Return True if given @chan is active / has a load enabled."""
- if not chan is None:
- self.switch_chan(chan)
- res = int(self._ask('LOAD?'))
- return res == 1
- def deactivate_all(self):
- """Sets all electronic loads to OFF."""
- self._write('ABOR')
- def _set_param(self, cmd, value, delay=0.0, rnd=None, trys=1, max_dev=None):
- loopdelay = delay
- timeout = trys # default - only try once, no redo's
- while (timeout > 0):
- if (rnd == None):
- self._write(cmd + " " + value)
- time.sleep(loopdelay)
- readback = origRead = self._ask(cmd + "?")[0:len(value)]
- else:
- value = round(float(value),rnd)
- self._write(cmd + " %e" % value)
- time.sleep(loopdelay)
- readback = origRead = self._ask(cmd + "?")
- readback = round(float(readback),rnd)
- if(readback == value):
- break
- elif ((abs(readback - value) / value) > max_dev) and max_dev:
- break #if within allowed deviation, continue.
- loopdelay += 0.01 #delay gets steadily longer with each failed command.
- timeout -= 1
- if (timeout <= 0):
- raise AssertionError("Timeout during setting of '%s'. expecting '%s', got '%s'" % (cmd, value, read))
- return origRead
- def set_static_current(self, setpoint, chan=None, low_range=False,
- slew_rise=0.0032, slew_fall=0.0032, approx_ok=False):
- """Set static current level for constant current mode.
- Optionally, set a slew rate in Amps/uS."""
- raise NotImplementedError('cc mode is just a modified copy of cr mode')
- if not chan is None:
- self.switch_chan(chan)
- if low_range:
- self._write('MODE CCL')
- else:
- self._write('MODE CCH')
- self._set_param('CURR:STAT:RISE', slew_rise, rnd=6)
- self._set_param('CURR:STAT:RISE', slew_rise, rnd=6)
- #self._write('CURR:STAT:RISE {v:.6f}'.format(v=slew_rise))
- #self._write('CURR:STAT:FALL {v:.6f}'.format(v=slew_fall))
- self._set_param('CURR:STAT:L1', setpoint, rnd=6)
- #self._write('CURR:STAT:L1 {v:.6f}'.format(v=setpoint))
- #self._write('CURR:STAT:L2 {v:.6f}'.format(v=setpoint))
- # readback = float(self._ask('CURR:STAT:L1?'))
- # max_dev = 2.0 if approx_ok else 0.015
- # if (abs(readback - setpoint) / setpoint) > max_dev:
- # raise AssertionError('Failed to set current. Expected ' \
- # '{exp:.3f}, got {rb:.3f}'.format(exp=setpoint, rb=readback))
- def set_dynamic_current(self, load1, load2, load1dur, load2dur,
- slew_rise=0.0032, slew_fall=0.0032,
- chan=None, low_range=False, approx_ok=False):
- """
- Set dynamic current level for constant current mode.
- Optionally, set a slew rate in Amps/uS.
- dynamic current mode switched between load one and load two
- once the duration for load one or two elapses.
- current slew rate between the two loads is controlled by
- slew rise and slew fall
- variable: unit:
- ----------------------
- load1 - amps
- load2 - amps
- load1dur - uS
- load2dur - uS
- slew_rise - Amps/uS
- slew_fall - Amps/uS
- """
- raise NotImplementedError('cc mode is just a modified copy of cr mode')
- if not chan is None:
- self.switch_chan(chan)
- if low_range:
- self._write('MODE CCDL')
- else:
- self._write('MODE CCDH')
- self._write('CURR:L1 {v:.6f}'.format(v=setpoint))
- # readback = float(self._ask('RES:L1?'))
- # max_dev = 2.0 if approx_ok else 0.015
- # if (abs(readback - setpoint) / setpoint) > max_dev:
- # raise AssertionError('Failed to set current. Expected ' \
- # '{exp:.3f}, got {rb:.3f}'.format(exp=setpoint, rb=readback))
- def set_resistance(self, setpoint, chan=None, low_range=False,
- slew_rise=0.0032, slew_fall=0.0032, approx_ok=False):
- """Set static resistance level for constant resistance mode.
- Optionally, set a slew rate in Amps/uS."""
- #todo - actually implement slew rate. slew rate not implemented.
- if not chan is None:
- self.switch_chan(chan)
- if low_range:
- self._write('MODE CRL')
- else:
- self._write('MODE CRH')
- self._write('RES:L1 {v:.6f}'.format(v=setpoint))
- readback = float(self._ask('RES:L1?'))
- max_dev = 2.0 if approx_ok else 0.015
- if (abs(readback - setpoint) / setpoint) > max_dev:
- raise AssertionError('Failed to set resistance. Expected ' \
- '{exp:.3f}, got {rb:.3f}'.format(exp=setpoint, rb=readback))
- def get_resistance(self, chan=None):
- """Return static resistance level for channel."""
- if not chan is None:
- self.switch_chan(chan)
- return float(self._ask('RES:L1?'))
- def set_voltage(self, setpoint, current_limit, chan=None,
- response_speed='fast'):
- """Set constant voltage mode level."""
- if not chan is None:
- self.switch_chan(chan)
- self._write('MODE CV')
- self._write('VOLT:L1 {v:.6f}V'.format(v=setpoint))
- readback = float(self._ask('VOLT:L1?'))
- if (readback - setpoint) > 1.0:
- raise AssertionError('Failed to set voltage. Expected ' \
- '{exp:.3f}, got {rb:.3f}'.format(exp=setpoint, rb=readback))
- self._write('VOLT:MODE {resp:s}'.format(resp=response_speed))
- if type(current_limit) is str:
- self._write('VOLT:CURR {lim:s}'.format(lim=current_limit))
- else:
- self._write('VOLT:CURR {lim:.6f}'.format(lim=current_limit))
- def set_power(self, setpoint, chan=None, low_range=False,
- slew_rise=0.0032, slew_fall=0.0032, approx_ok=False):
- """Set static power level for constant power mode.
- Optionally, set a slew rate in Amps/uS."""
- raise NotImplementedError('cp mode is just a modified copy of cr mode')
- if not chan is None:
- self.switch_chan(chan)
- if low_range:
- self._write('MODE CPL')
- else:
- self._write('MODE CPH')
- self._write('RES:L1 {v:.6f}'.format(v=setpoint))
- # readback = float(self._ask('RES:L1?'))
- # max_dev = 2.0 if approx_ok else 0.015
- # if (abs(readback - setpoint) / setpoint) > max_dev:
- # raise AssertionError('Failed to set current. Expected ' \
- # '{exp:.3f}, got {rb:.3f}'.format(exp=setpoint, rb=readback))
- def set_defaults(self):
- """Set instrument to safe defaults."""
- self.deactivate_all()
- self.utility.reset()
- def check_errors(self):
- """Make sure no errors are present. This is ONLY for the current
- channel. Return a tuple of format (error code, error message).
- error code will be 0 if no error exists."""
- code = int(self._ask('STAT:CHAN:COND?').strip('"'))
- errors = []
- # status[4:0] = {OT, RV, OP, OV, OC}
- if code & 0x01:
- errors.append('Over current')
- if code & 0x02:
- errors.append('Over voltage')
- if code & 0x04:
- errors.append('Over power')
- if code & 0x08:
- errors.append('Reverse voltage on input')
- if code & 0x10:
- errors.append('Over temperature')
- error_str = ', '.join(errors)
- try:
- chan = self.get_chan_label()
- except:
- chan = 'Unknown'
- msg = 'Channel {chan:s} status 0x{stat:02x}: {msg:s}'. \
- format(chan=chan, stat=code, msg=error_str)
- return (code, msg)
- def spec_test_start(self, chan=None, mode='voltage',
- center=0.0, high=0.0, low=0.0):
- """Execute specification test on @chan_num"""
- if not chan is None:
- self.switch_chan(chan)
- self._write('SPEC:UNIT VALUE')
- m = mode[0:4].upper()
- # set test limits
- self._write('SPEC:{m:s}:C {v:.6f}'.format(m=m, v=center))
- self._write('SPEC:{m:s}:H {v:.6f}'.format(m=m, v=high))
- self._write('SPEC:{m:s}:L {v:.6f}'.format(m=m, v=low))
- # start test
- self._write('SPEC:TEST ON')
- def spec_test_status_all(self):
- """Return GO-NG (True/False) global status for all channels.
- This should return False (NG) if ANY channel's spec test has
- failed."""
- return (self._ask('SPEC?') == '1')
- def spec_test_status(self, chan=None, mode='voltage'):
- """Return GO-NG (True/False) status for channel's spec test."""
- if not chan is None:
- self.switch_chan(chan)
- m = mode[0:4].upper()
- res = self._ask('SPEC:{m:s}?'.format(m=m))
- return res == '1'
- def spec_test_stop(self, chan=None, mode='voltage'):
- """Stop channel specification test and return True on PASS (GO)
- and False for FAIL (NG)."""
- if not chan is None:
- self.switch_chan(chan)
- # result is only valid BEFORE stopping test
- res = self.spec_test_status()
- self._write('SPEC:TEST OFF')
- return res
- def ocp_test(self):
- """In OCP/OPP mode, the load provides a ramped up current or power to
- test if the UUT voltage reaches a trigger voltage level and the
- OCP or OPP protection is operating normally."""
- raise NotImplementedError() #todo - finish implementing this
- # Start current
- self._write('OCP:ISTA 10.25')
- # End current
- self._write('OCP:IEND 20')
- # Step count
- self._write('OCP:STEP 100')
- # Dwell time (ms)
- self._write('OCP:DWELI 100')
- # Trigger voltage (V)
- self._write('OCP:TRIG:VOLT 4.5')
- # Low level current of specification (A)
- self._write('OCP:SPEC:L 11')
- # High level current of specification (A)
- self._write('OCP:SPEC:H 15')
- # Execute the OCP test
- self._write('OCP ON')
- # Read result of OCP test function:
- # -1: Test stopped
- # -2: Test Ready to execute, waiting?
- # -3: Test finished executing
- # <result>,<ocp current>
- # where <result> = 0 for PASS, 1 for FAIL
- self._write('OCP:RES?')
- def grab(self):
- """Place instrument into remote control mode."""
- # Note: Making this actually turn it OFF because it was
- # causing broken behavior on some devices. As far as I can
- # tell there is no harm in doing this other than lack of
- # front panel lockout during the test.
- self._write('CONF:REM OFF')
Add Comment
Please, Sign In to add comment