import ch.psi.pshell.epics as epics import math class Keithley(object): RANGE_STATES = ['AUTO', '20 mA', '2 mA', '200 uA', '20 uA', '2 uA', '200 nA', '20 nA', '2 nA', '200 pA', '20 pA'] TTYPE_STATES = ['IMM', 'TLIN', 'BUS', 'EXT'] USER_MODE_STATES = ['def setting', 'poll curr fast', 'poll curr medi', 'poll curr slow', 'trig setting', 'trigger BUS', 'trigger TLIN', 'trigger EXT', 'poll volt medi'] SCAN_STATES = ['Passive', 'Event', 'I/O Intr', '10 second', '5 second', '2 second', '1 second', '.5 second', '.2 second', '.1 second'] SCAN_INTERVALS = [0., 0., 0., 10., 5., 2., 1., .5, .2, .1] def __init__(self, base_name, base_channel): self.dwell = 0. self.triggered = False self.base_channel = base_channel self.base_name = base_name self.rangeCh = None self.scanCh = None self.ttypeCh = None self.nplcCh = None self.navgCh = None self.tottimeCh = None self.doinitCh = None self.dotriggerCh = None self.dofetchCh = None self.readoutCh = None def initialize(self): self.rangeCh = epics.ChannelInteger(self.base_name + "Range", self.base_channel + "RANGE") self.scanCh = epics.ChannelInteger(self.base_name + "Scan", self.base_channel + "READSCAN.SCAN") self.ttypeCh = epics.ChannelInteger(self.base_name + "TType", self.base_channel + "TTYPE") self.nplcCh = epics.ChannelDouble(self.base_name + "Nplc", self.base_channel + "NPLC") self.navgCh = epics.ChannelDouble(self.base_name + "Navg", self.base_channel + "NAVG") self.tottimeCh = epics.ChannelDouble(self.base_name + "TotTime", self.base_channel + "TOTTIME") self.doinitCh = epics.ChannelInteger(self.base_name + "DoInit", self.base_channel + "DOINIT") self.dotriggerCh = epics.ChannelInteger(self.base_name + "DoTrigger", self.base_channel + "DOTRIGGER") self.dofetchCh = epics.ChannelInteger(self.base_name + "DoFetch", self.base_channel + "DOFETCH") self.readoutCh = epics.ChannelDouble(self.base_name + "Readout", self.base_channel + "READOUT") # DOZCHOFF # ZCH_SP # DOCURRENT # DORESET # DOSETDEFAULT # USER_MODE self.rangeCh.initialize() self.scanCh.initialize() self.ttypeCh.initialize() self.nplcCh.initialize() self.navgCh.initialize() self.tottimeCh.initialize() self.doinitCh.initialize() self.dotriggerCh.initialize() self.dofetchCh.initialize() self.readoutCh.initialize() def setup(): """ EXPERIMENTAL to set up the keithley after Reset, there are two options 1) do set defaults, set scan, set range 2) set user mode, set range """ #self.dosetdefaultCh.write(1) #self.scanCh.write(9) def prepare(self, dwell, triggered): """ prepare keithley for gpib polling. setting keithley parameters has several issues. the dwell time and trigger mode cannot be set programmatically at the moment. the user should select poll slow (100 ms), medium (20 ms) or fast (2 ms). this method just reads the current value and stores it in self.dwell. dwell: dwell time in seconds. 0.1 - 20.0 in triggered mode, 0.1 - 1.0 in free running mode. triggered: True: wait for self.trig call and trigger once per call. False: 1 Hz free run using EPICS SCAN attribute. """ self.triggered = False self.dwell = self.tottimeCh.read() / 1000. def prepare_not_working(self, dwell, triggered): """ prepare keithley for gpib polling: scan passive, bus triggered, set dwell time this doesn't to work. dwell: dwell time in seconds. 0.1 - 20.0 in triggered mode, 0.1 - 1.0 in free running mode. triggered: True: wait for self.trig call and trigger once per call. False: 1 Hz free run using EPICS SCAN attribute. """ self.triggered = triggered if triggered: self.scanCh.write(0) self.ttypeCh.write(2) else: self.ttypeCh.write(0) self.scanCh.write(6) dwell = min(dwell, 1.) nplc = 5. navg = dwell / 0.1 if navg > 100: nplc *= 2 navg /= 2 navg = min(navg, 100.) nplc = min(nplc, 10.) self.nplcCh.write(nplc) self.navgCh.write(navg) self.dwell = self.tottimeCh.read() / 1000. def trig(self): """ trigger keithleys, wait until done, and read the result into EPICS. the value can then be read by pshell from the channel. if self.prepare was called with triggered = False, this method has no effect. """ if self.triggered: self.doinitCh.write(1) self.dotriggerCh.write(1) def get_dwell(self): """ get dwell time in seconds. """ dwell = self.tottimeCh.read() / 1000. self.dwell = dwell return dwell def fetch(self): """ fetch the current value from the keithley into EPICS. if self.prepare was called with triggered = False, this method has no effect. """ if self.triggered: self.dofetchCh.write(1) def read(self): """ read the curent value. """ return self.readoutCh.read() def release(self): """ switch keithleys to free run. 0.1 s dwell time, 1 s poll interval. _do nothing for now!_ """ return None self.nplcCh.write(5.) self.navgCh.write(1.) self.scanCh.write(6) self.ttypeCh.write(0) def reset(self): """ switch to zero check """ self.doresetCh.write(1) def set_range(self, value): """ set the current range. value can be: - float: current limit in A. values between 2e-11 and 2e-3 set a fixed range. values greater than 0.02 select AUTO. - str: state label of EPICS channel (cf. self.RANGE_STATES). must be one of the state labels, otherwise a ValueError is raised. - int: state index of EPICS channel (0...10) """ if isinstance(value, float): try: value = int(-math.log10(value / 2)) - 1 value = max(value, 0) value = min(value, len(self.RANGE_STATES)-1) except ValueError: value = 0 elif isinstance(value, str): value = self.RANGE_STATES.index(value) self.rangeCh.write(value) KeiSample = Keithley("SampleKeithley", "X03DA-KEITHLEY-1:") KeiReference = Keithley("ReferenceKeithley", "X03DA-KEITHLEY-2:") KeiSample.initialize() KeiReference.initialize()