Source code for lightlab.equipment.lab_instruments.Keithley_2400_SM

from . import VISAInstrumentDriver
from lightlab.equipment.abstract_drivers import Configurable
from lightlab.laboratory.instruments import Keithley

import numpy as np
import time
from lightlab import logger


[docs]class Keithley_2400_SM(VISAInstrumentDriver, Configurable): ''' A Keithley 2400 driver. `Manual: <http://research.physics.illinois.edu/bezryadin/labprotocol/Keithley2400Manual.pdf>`__ Usage: :any:`/ipynbs/Hardware/Keithley.ipynb` Capable of sourcing current and measuring voltage, such as a Keithley Also provides interface methods for measuring resistance and measuring power ''' instrument_category = Keithley autoDisable = None # in seconds. NOT IMPLEMENTED _latestCurrentVal = 0 _latestVoltageVal = 0 currStep = None voltStep = None rampStepTime = 0.01 # in seconds. def __init__(self, name=None, address=None, **kwargs): ''' Args: currStep (float): amount to step if ramping in current mode. Default (None) is no ramp voltStep (float): amount to step if ramping in voltage mode. Default (None) is no ramp rampStepTime (float): time to wait on each ramp step point ''' self.currStep = kwargs.pop("currStep", None) self.voltStep = kwargs.pop("voltStep", None) self.rampStepTime = kwargs.pop("rampStepTime", 0.01) VISAInstrumentDriver.__init__(self, name=name, address=address, **kwargs) Configurable.__init__(self, headerIsOptional=False, verboseIsOptional=False)
[docs] def startup(self): self.write('*RST')
[docs] def setPort(self, port): if port == 'Front': self.setConfigParam('ROUT:TERM', 'FRON') elif port == 'Rear': self.setConfigParam('ROUT:TERM', 'REAR')
def __setSourceMode(self, isCurrentSource): if isCurrentSource: sourceStr, meterStr = ('CURR', 'VOLT') else: sourceStr, meterStr = ('VOLT', 'CURR') self.setConfigParam('SOURCE:FUNC', sourceStr) self.setConfigParam('SOURCE:{}:MODE'.format(sourceStr), 'FIXED') self.setConfigParam('SENSE:FUNCTION:OFF:ALL') self.setConfigParam('SENSE:FUNCTION:ON', '"{}"'.format(meterStr)) self.setConfigParam('SENSE:{}:RANGE:AUTO'.format(meterStr), 'ON') self.setConfigParam('RES:MODE', 'MAN') # Manual resistance ranging
[docs] def setVoltageMode(self, protectionCurrent=0.05): self.enable(False) self.__setSourceMode(isCurrentSource=False) self.setProtectionCurrent(protectionCurrent) self._configVoltage(0)
[docs] def setCurrentMode(self, protectionVoltage=1): self.enable(False) self.__setSourceMode(isCurrentSource=True) self.setProtectionVoltage(protectionVoltage) self._configCurrent(0)
def _configCurrent(self, currAmps): currAmps = float(currAmps) if currAmps >= 0: currAmps = np.clip(currAmps, a_min=1e-9, a_max=1.) else: currAmps = np.clip(currAmps, a_min=-1, a_max=-1e-9) if currAmps != 0: needRange = 10 ** np.ceil(np.log10(abs(currAmps))) self.setConfigParam('SOURCE:CURR:RANGE', needRange) self.setConfigParam('SOURCE:CURR', currAmps) self._latestCurrentVal = currAmps def _configVoltage(self, voltVolts): voltVolts = float(voltVolts) if voltVolts != 0: needRange = 10 ** np.ceil(np.log10(np.abs(voltVolts))) self.setConfigParam('SOURCE:VOLT:RANGE', needRange) self.setConfigParam('SOURCE:VOLT', voltVolts) self._latestVoltageVal = voltVolts
[docs] def setCurrent(self, currAmps): ''' This leaves the output on indefinitely ''' currTemp = self._latestCurrentVal if not self.enable() or self.currStep is None: self._configCurrent(currAmps) else: nSteps = int(np.floor(abs(currTemp - currAmps) / self.currStep)) for curr in np.linspace(currTemp, currAmps, 1 + nSteps)[1:]: self._configCurrent(curr) time.sleep(self.rampStepTime)
[docs] def setVoltage(self, voltVolts): voltTemp = self._latestVoltageVal if not self.enable() or self.voltStep is None: self._configVoltage(voltVolts) else: nSteps = int(np.floor(abs(voltTemp - voltVolts) / self.voltStep)) for volt in np.linspace(voltTemp, voltVolts, 1 + nSteps)[1:]: self._configVoltage(volt) time.sleep(self.rampStepTime)
[docs] def getCurrent(self): currGlob = self.getConfigParam('SOURCE:CURR') if type(currGlob) is dict: currGlob = currGlob['&'] return currGlob
[docs] def getVoltage(self): voltGlob = self.getConfigParam('SOURCE:VOLT') if type(voltGlob) is dict: voltGlob = voltGlob['&'] return voltGlob
[docs] def setProtectionVoltage(self, protectionVoltage): self.setConfigParam('VOLT:PROT', protectionVoltage)
[docs] def setProtectionCurrent(self, protectionCurrent): self.setConfigParam('CURR:PROT', protectionCurrent)
@property def protectionVoltage(self): return self.getConfigParam('VOLT:PROT') @property def protectionCurrent(self): return self.getConfigParam('CURR:PROT')
[docs] def measVoltage(self): retStr = self.query('MEASURE:VOLT?') v = float(retStr.split(',')[0]) # first number is voltage always if v >= self.protectionVoltage: logger.warning('Keithley compliance voltage of %s reached', self.protectionVoltage) logger.warning('You are sourcing %smW into the load.', v * self._latestCurrentVal * 1e-3) return v
[docs] def measCurrent(self): retStr = self.query('MEASURE:CURR?') i = float(retStr.split(',')[1]) # second number is current always if i >= self.protectionCurrent: logger.warning('Keithley compliance current of %s reached', self.protectionCurrent) logger.warning('You are sourcing %smW into the load.', i * self._latestVoltageVal * 1e-3) return i
[docs] def enable(self, newState=None): ''' get/set enable state ''' if newState is False: if self.getConfigParam('SOURCE:FUNC') == 'CURR': self.setCurrent(0) else: self.setVoltage(0) if newState is not None: self.setConfigParam('OUTP:STATE', 1 if newState else 0, forceHardware=True) retVal = self.getConfigParam('OUTP:STATE', forceHardware=True) return retVal in ['ON', 1, '1']