Files
x03da/script/keithley.py
gac-x03da 90de64bbe0 Closedown
2021-11-24 11:55:02 +01:00

216 lines
7.6 KiB
Python

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.usermodeCh = None
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
self.invertreadoutCh = None
self.setvoltageCh = None
self.voltoutCh = None
def initialize(self):
self.usermodeCh = epics.ChannelString(self.base_name + "UserMode", self.base_channel + "USER_MODE")
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")
self.invertreadoutCh = epics.ChannelInteger(self.base_name + "InvertReadout", self.base_channel + "INVERTREADOUT")
self.setvoltageCh = epics.ChannelDouble(self.base_name + "SetVoltage", self.base_channel + "SETVOLTAGE")
self.voltoutCh = epics.ChannelInteger(self.base_name + "VoltOut", self.base_channel + "VOLTOUT")
# DOZCHOFF
# ZCH_SP
# DOCURRENT
# DORESET
# DOSETDEFAULT
self.usermodeCh.initialize()
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()
self.invertreadoutCh.initialize()
self.setvoltageCh.initialize()
self.voltoutCh.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()