#%TITLE% Analog instruments
#%NAME%
# Macros to manage analog instruments as pseudocounters.
#
#%CATEGORY% Detection
#
#%DESCRIPTION%
# This macro set is intended to operate instruments that provide analog
# readings as pseudocounters. They will be called "multimeters" even if they
# might be other type of instruments or signal sources.
# These macros may replace those in %B%pico.mac%B% adding new features.
# The more relevant are:
# %UL%
# %LI%Different type of multimeters and signal sources can be used as
# mentioned above.
# %LI%One can operate multimetres in integrating or averaging mode.
# The integration and the average are implemented by software. In both cases
# the signal is integrated in time with respect to the computer clock. In
# averaging mode the time integral is divided by the counting time
# (time averaging).
# %LI%Counters mnemonics can be freely choosen.
# %LI%Parameters like scale factors and GPIB address, can be set in the
# config file.
# %LI%A new macro (%B%multimshow%B%) displays the configuration in a
# concise way.
# %XUL%
# The implemented signal sources are:
# %UL%
# %LI% Keithley multimeters (and similar) interfaced by GPIB
# with the old protocol and the new series, using the SCPI protocol.
# %LI% Electron current of the Storage Ring.
# %LI% ADC's in ICV150 VME boards.
# %LI% ADC - wago modules.
# %LI% Dummy (simulated) signals for test purposes.
# %XUL%
# This file includes versions of the macros %B%picosetup%B% and
# %B%picopol%B% for backward compatibility.
#
#%EXAMPLE%
# %DL%
# %DT%multimsetup mon2 mon3 integrate=yes scale=1e9
# %DD%This macro configures the pseudocounters mon2 and mon3 to be loaded with
# multimeter readouts. The multimeters must be Keithley-like with GPIB
# interface (the default) and the interface number and address of each
# multimeter is taken respectively from the unit and channel fields of
# the pseudocounter in the config file. %BR%
# The instrument readings will be time-integrated and multiplied by a scale
# factor of 1E9.
#
# %DT%multimsetup i0 address=16
# %DD%The pseudocounter i0 is set to be loaded with the reading of a
# multimeter with the GPIB address 16. In this case the pseudocounter will
# work in averaging mode and the scale factor will be taken from the
# config file.
#
# %DT%multimsetup i0 i1 type=madc device="id23/wct231_thc/1"
# %DD%The pseudocounters i0 and i1 are set each to read a channel from an
# ADC (same server, possibly different board). The unit and channel
# for each counter will be read from the config file.
#
# %DT%multimshow
# %DD%Displays the current configuration.
# %XDL%
#
#%LOG%
#
# $Log: multim.mac,v $
# Revision 3.44 2021/10/05 12:21:41 mauro
# Add readmode=currentac, voltagedc, voltageac for pico_autorange and pico_setrange for DDC and SCPI. For SCPI Keithley models, does the current zero correction with the lowest range according to the model.
#
# Revision 3.44 2020/01/13 16:36:12 mauro
# Add readmode=currentac, voltagedc, voltageac
# for pico_autorange and pico_setrange for DDC and SCPI.
# For SCPI Keithley models, does the current zero correction
# with the lowest range according to the model.
#
# Revision 3.43 2020/01/13 16:36:12 ahoms
# Add readmode=resistance4w - 4-wire resistance (with MC Lagier)
#
# Revision 3.42 2020/01/13 14:54:37 ahoms
# Add TCP Keithley-SCPI interface (with MC Lagier)
#
# Revision 3.41 2019/10/07 13:37:57 ohlsson
# Since esrf_dc("sys/mach/current") will not work in EBS
# I removed the type=srcur option from multisetup
# multim_init_srcur and multim_read_srcur macros removed
#
# Revision 3.40 2018/07/03 07:25:01 mauro
# New multim controller for temporary use with Bliss controller Gpib wrapper into Gpib tango server.
# Soon we will move to Bliss Keithley controller and Keithley tango server.
# Idea is to have a unique keithley controller for both DDC and SCPI protocols.
# New macros for setting the range for Keithley picoammeters.
#
# Revision 3.30 2018/06/01 15:56:13 mauro
# add comment usage (from ID21) (CG)
#
# Revision 3.29 2015/04/07 15:04:43 mauro
# Add the readmode measurement in pico_setrange. Improved "current" readmode in multim_init_kscpi.
#
# Revision 3.28 2015/03/05 10:27:30 papillon
# * add pico_autorange and pico_setrange to manage keithley
# current range
#
# Revision 3.26 2012/09/04 13:31:17 guilloud
# * + name of the involved counter in case of error.
#
# Revision 3.25 2012/05/04 11:45:56 domingue
# keithley6485: add sleep time after *RST; kscpi_flush;
#
# Revision 3.24 2011/11/16 14:47:12 homsrego
# fixed message of general readmode for kscpi
#
# Revision 3.23 2011/08/31 15:05:36 domingue
# keithley6485 serial: correct ser_get with terminator
#
# Revision 3.22 2011/08/30 08:59:49 domingue
# keithley6485:add sleep time kscpi_put and kscpi_get
#
# Revision 3.21 2011/08/24 12:29:16 domingue
# add serial control for kscpi keithley (6485, 65..)
#
# Revision 3.20 2011/03/18 14:23:57 witsch
# added a simple averaging mode. Values are accumulated and then divided by the
# # number of measurements..
#
# Revision 3.19 2011/02/04 15:32:07 witsch
# Line 778, fixing MULTIM[cntr]["sample"] = 1 kept the integration mode from working. The sample mode
# could not be switched on or off. Funny that nobody complained in 8 years :-)
#
# Revision 3.18 2009/09/10 15:00:00 witsch
# Keithley 2010 readmode=resistance extended to accept anything as read mode.
# Allows four-wire resistances with command FRES.
#
# Revision 3.17 2009/09/03 15:53:31 papillon
# * added readmode resistance for K2001
#
# Revision 3.16 2009/06/16 10:08:26 sole
# Support keithley 2001 (found at ID03)
#
# Revision 3.16 2008/08/12 14:13:57 sole
# Keithley 2001 kscpi - pt100 (ID03)
#
# Revision 3.15 2008/08/12 14:13:57 rey
# documentation changes
#
# Revision 3.14 2008/07/15 14:18:31 rey
# added readmode=current for kscpi
#
# Revision 3.13 2008/04/09 16:15:54 rey
# adding readmode possibility for kscpi multimeters.
# implemented readmode=pt100 for thermocouple type pt100 on
# k2010. tested.
#
# Revision 3.12 2007/03/21 17:01:09 claustre
# In version 3.9 a global declaration instead of local has been
# added into the function _multimsetup(). Just restored now !!
#
# Revision 3.11 2007/03/15 14:31:58 blissadm
# when reading values back from keithley, return either value from the left of A or from the right... In case there are 2 values, or if there is no value at all => exit
#
# Revision 3.10 2007/03/06 10:26:30 claustre
# added suport for TANGO electrometer device server
#
# Revision 3.9 2007/02/26 12:57:01 beteva
# added madc type. Fixed bug in multim_read_kscpi
#
# Revision 3.8 2005/07/25 08:04:07 witsch
# in init_kscpi a read? was issued, without reading the value, which
# lead to a gpib time out at the next read?.
# added a gpib_get
#
# Revision 3.7 2004/11/03 18:16:54 fajardo
# Bug for integrating mode fixed. Gate option useless
#
# Revision 3.6 2004/09/01 18:51:25 ahoms
# Added gate option in integrate mode to normalise time with sec cntr.
#
# Revision 3.5 2004/08/27 07:02:42 claustre
# Added lvmeadc type for IcvADC Linux device server (monochannel device)
#
# Revision 3.4 2003/07/08 15:06:15 witsch
# initialization commands for the kspci type added, little cosmetic work.
#
# Revision 3.3 2003/06/11 08:15:41 witsch
# new type Keithley with SCPI protocol added.
#
# Revision 3.2 2002/05/23 13:20:59 ahoms
# added "sample" and "polltime" setup parameters and "loop_n" field in MULTIM
# now defines global MULTIM[] MULTIMPSEUDO[]
#
#%END%
#%UU% <counter1> [<counter2> ...] [<parameter>=<value> ...]
#%MDESC%
# This macro configures the list of pseudocounters to be loaded with the
# analog readings. It is possible to add parameters to change
# the defaults. All the parameters will affect to all the
# counters regardless of their position in the line. If one wants to set
# particular parameters to specific counters, he should use
# several %B%multimsetup%B% lines. As it is explained below whenever a
# essential parameter, like the scale factor or the instrument address, is not
# especified it will be taken from the config file.%BR%
# Parameters that are meaningless for a particular type of signal source are
# accepted but ignored.%BR%
# The currently implemented parameters are the following:%BR%
# %BR%%BR%%B%type%B%: the type of signal source. Accepted values are:
# %DL%
# %LI%"kgpib" (Keithley-like behaviour with GPIB interface) (Default)
# %LI%"kscpi" (Keithley with GPIB interface using the SCPI protocol) (6485)
# this accepts an extra "readmode" parameter (ex: readmode=pt100) that
# will configure the readmode an initialization. Accepted values are:
# %DL%
# %LI%"pt100" (read temperature/pt100 thermocouple) (k2010)
# %LI%"resistance" (read resistance) (tested on k2001 and k6514)
# %LI%"resistance4w" (read 4-wire resistance) (tested on dmm7510)
# %LI%"current" (read current) (tested on k6485 and k6514)
# %XDL%
# %LI%"vmeadc" (ADC channel in a ICV150 VME board with OS9 taco server)
# %LI%"lvmeadc" (ADC channel in a ICV150 VME board with Linux taco server)
# %LI%"madc" (ADC channel read with MAdc150 (ICV150 VME board) or WagoCt
# (wago ADC board) Linux device servers)
# %LI%"tangoelect" (Electrometers TANGO device server, for Keithley electrometers)
# %LI%"dummy" (dummy counter, may simulate a constant rate, a peak or an
# exponential decay)
# %XUL%
# %BR%%B%address%B%: the GPIB address of the multimeter in the standard
# %B%spec%B% format. If it
# is not especified, the address for a GPIB instrument is constructed from
# the "unit" and "channel" fields associated to the counter in the config
# file: "unit" corresponds to the interface number and "channel" to the
# instrument address in the bus.%BR%
# %BR%%B%integrate%B%: if set to "yes" makes the pseudocounter work in
# integrating mode.
# The default is "no" that correspond to averaging mode. In one case the
# multimeter readings during the counting interval are time integrated
# by %B%spec%B%, while in the other they are time averaged.%BR%
# %BR%%B%average%B%: if set to "yes" makes the pseudocounter work in
# a simple averaging mode. Values are accumulated and then divided by the
# number of measurements.%BR%
# %BR%%B%scale%B%: The scale factor that multiplies the instrument readout.
# If it is not especified, the value is taken from the "scale" field in the
# config file.%BR%
# %BR%%B%trigger%B%: By default is set to yes. The effect of this parameter is
# device dependent. For "kgpib" multimeters the intruments are triggered at
# the beginning of the counting interval discarding any old values stored in
# the intrument buffers.%BR%
# %BR%%B%device%B%: the device name for ICV150 VME boards.%BR%
# %BR%%B%channel%B%: the channel number for ADC's in ICV150 VME boards.
# If it is not especified, the value is taken from the "channel" field in the
# config file, only mandatory for OS9 device server, the Linux version of the
# server exports one device per channel.%BR%
# %BR%%B%sample%B%: If set to yes, the device will be read only once each
# count; otherwise it will be read as much as possible during the counting
# time (taking into account the %B%polltime%B% parameter). Default is no.%BR%
# %BR%%B%polltime%B%: specifies the minimum time between accesses to the
# device in seconds. By default the devices will be polled during counting
# at a rate determined by the COUNTERSPOLLTIME and the total access time of
# all the %B%multim%B% devices. Sometimes this can represent an overload
# of some resource, which can be controlled by this parameter.%BR%
# %BR%%B%motor%B%, %B%center%B% and %B%fwhm%B%: description of the simulated
# peak for "dummy" signals.%BR%
# %BR%%B%timeconstant%B%: time constant in minutes of an exponentially
# decaying "dummy" signal.
# If it is not especified, the value is taken from the "channel" field in the
# config file.%BR%
#
def multimsetup '{
if ($# == 0) {
if (SETUP) print "Error in line: multimsetup $*"
print "Usage: multimsetup counter1 [counter2 ...] [parameter=value ...]"
} else {
_multimsetup("$*")
}
}'
#%IU% (<argument_string>)
#%MDESC%
# This macro does the actual setup.
#
def _multimsetup(args) '{
global MULTIMPSEUDO[]
global MULTIM[]
local i j
local nparam auxlist0 auxlist1 larr key
list_init auxlist0
list_init auxlist1
list_test MULTIMPSEUDO
nparam = list_splitadd(auxlist0, args)
for (i=1; i<=list_n(auxlist0); i++) {
if (split(auxlist0[i], larr, "=") > 1){
list_remove(auxlist0, auxlist0[i--])
list_add(auxlist1, larr[0])
list_setpar(auxlist1, larr[0], "value", larr[1])
}
}
nparam = list_n(auxlist0)
for (i=1; i<=nparam; i++) {
key = auxlist0[i]
list_add(MULTIMPSEUDO, key)
cdef("user_prepcount","MULTIM[\"t0\"]=time(); MULTIM[\"first\"]=-1\n", \
"multim", 0x10)
cdef("user_prepcount",sprintf("multim_precount(%s)\n",key), key, 0x02)
cdef("user_pollcounts", sprintf("multim_getcounts(%s)\n",key),key,0x02)
cdef("user_getcounts",sprintf("multim_getcounts(%s)\n",key),key,0x02)
list_removepar(MULTIMPSEUDO, key)
for (j=1; j<=list_n(auxlist1); j++){
list_setpar(MULTIMPSEUDO, key, auxlist1[j], \
list_getpar(auxlist1,j,"value"))
}
multim_config(key)
if (!SETUP)
cdef("user_config",sprintf("multim_config(\"%s\")\n",key), key)
else
cdef("user_config","", key, "delete")
setup_tail("multim", key)
}
# Make sure that "loop_n" exists
MULTIM["loop_n"] = MULTIM["loop_n"]
}'
#%UU% <key>
#%MDESC%
# Removes the configuration for a particular pseudocounter. It is called
# internally by the automatic unsetup procedure but it can be also called
# by the user.
#
def multimunsetup '{
local key
key = "$1"
S[cnt_num(key)]=0
list_remove(MULTIMPSEUDO, key)
cdef("", "", key, "delete")
if (!list_n(MULTIMPSEUDO)){
cdef("", "", "multim", "delete")
unglobal MULTIMPSEUDO MULTIM
}
}'
#%IU% (<counter>)
#%MDESC%
# This macro function initialises variables for the pseudocounting.
#
def multim_precount(cntr) '{
if (MULTIM["first"] == -1)
MULTIM["first"] = cntr
MULTIM[cntr]["value"] = MULTIM[cntr]["sum"] = MULTIM[cntr]["noreads"] = 0
MULTIM[cntr]["t0"] = MULTIM[cntr]["t"] = MULTIM["t0"]
if (cntr == MON && COUNT_TIME <= 0) {
MULTIM[cntr]["ctime"] = -COUNT_TIME
COUNT_TIME = 600000
} else
MULTIM[cntr]["ctime"] = -1
}'
#%IU% (<counter>, <polling>)
#%MDESC%
# This macro function reads the signal and averages or performs the
# integration as necessary.
#
def multim_getcounts(cntr) '{
local type ok value t0 t dt
if (cntr == MULTIM["first"])
MULTIM["loop_n"]++
if (t0 = MULTIM[cntr]["t0"]) {
value = MULTIM[cntr]["value"]
type = MULTIM[cntr]["type"]
if (MULTIM[cntr]["sample"])
ok = (MULTIM[cntr]["t0"] > MULTIM[cntr]["t"])
else {
dt = time() - MULTIM[cntr]["t"]
ok = (dt >= MULTIM[cntr]["polltime"])
}
if ((wait(0x22) && ok) || (MULTIM[cntr]["noreads"] == 0)) {
ok = 0
multim_readinstrument
if (ok){
value = value * MULTIM[cntr]["scale"] + MULTIM[cntr]["offset"]
MULTIM[cntr]["noreads"]++;
if (!MULTIM[cntr]["average"]) {
dt = (t = time()) - MULTIM[cntr]["t"]
MULTIM[cntr]["t"] = t
value = (MULTIM[cntr]["sum"] += dt * value)
if (!MULTIM[cntr]["integrate"]) value /= (t-t0)
MULTIM[cntr]["value"] = value
} else {
MULTIM[cntr]["value"] += value
}
}
}
if (!USER_WAITINGCOUNT) {
if (MULTIM[cntr]["integrate"])
MULTIM[cntr]["value"] *= S[sec] / \
(MULTIM[cntr]["t"] - MULTIM[cntr]["t0"])
MULTIM[cntr]["t0"] = 0
if (!MULTIM[cntr]["average"]) {
MULTIM[cntr]["noreads"] /= S[sec]
} else {
if (MULTIM[cntr]["noreads"]) {
MULTIM[cntr]["value"] /= MULTIM[cntr]["noreads"]
}
}
}
if (MULTIM[cntr]["ctime"] > 0 && \
MULTIM[cntr]["ctime"] < fabs(value)) {
stop(2)
}
}
S[cntr] = MULTIM[cntr]["value"]
}'
if (!(whatis("multim_readinstrument")&2)) rdef multim_readinstrument ""
if (!(whatis("multim_initinstrument")&2)) rdef multim_initinstrument ""
#%IU%
#%MDESC%
# Particular initialisation for type=kgpib
#
def multim_init_kgpib '
if (!("address" in MULTIM[cntr]))
MULTIM[cntr]["address"] = sprintf("%s:%s", \
counter_par(cntr, "unit"), counter_par(cntr, "channel"))
if (!gpib_cntl(MULTIM[cntr]["address"], "responsive")) {
printf("Unresponsive GPIB controller for address %s. ",MULTIM[cntr]["address"])
return(1)
}
if (gpib_poll(MULTIM[cntr]["address"]) == -1) {
printf("Unresponsive GPIB address %s. ", MULTIM[cntr]["address"])
return(1)
}
'
#%IU%
#%MDESC%
# Reads the instrument for type=kgpib
#
def multim_read_kgpib '
if (!MULTIM[cntr]["noreads"] && MULTIM[cntr]["trigger"]) {
gpib_put(MULTIM[cntr]["address"], "T0X")
}
if ((kanswer = gpib_get(MULTIM[cntr]["address"])) != "") {
value = substr(kanswer,5)
ok = 1
}
'
#%IU%
#%MDESC%
# Particular initialisation for type=vmeadc%BR%
#
def multim_init_vmeadc '
if (!("device" in MULTIM[cntr])) {
printf("Device name missing. ")
return(1)
}
if (!("channel" in MULTIM[cntr]))
MULTIM[cntr]["channel"] = counter_par(cntr, "channel")
global ESRF_ERR_MSG
ESRF_ERR = -1
#esrf_io(MULTIM[cntr]["device"],"DevReadChannel", MULTIM[cntr]["channel"])
#esrf_io(MULTIM[cntr]["device"], "DevReadSigValues")
if (ESRF_ERR > 0) {
tty_cntl("md")
printf("\n Error while initializing ADC multim\n")
printf(" - counter: %s\n",cnt_mne(cntr))
printf(" - ADC dev: %s\n",MULTIM[cntr]["device"])
printf(" - Errno: %d\n",ESRF_ERR)
printf(" %s\n",ESRF_ERR_MSG)
printf(" Please. Check the VME\n\n")
tty_cntl("me")
printf(" Hint: if you got the message \"RPC client call timed out\":\n")
printf(" - the device server process may have died in the VME\n")
printf(" or\n")
printf(" - you may be having network problems\n\n")
return(1)
}
'
#%IU%
#%MDESC%
# Reads the ADC value for type=vmeadc
#
def multim_read_vmeadc '
ESRF_ERR=-1
value = esrf_io(MULTIM[cntr]["device"],"DevReadChannel", \
MULTIM[cntr]["channel"])
if (!ESRF_ERR)
ok = 1
'
#%IU%
#%MDESC%
# Particular initialisation for type=lvmeadc%BR%
#
def multim_init_lvmeadc '
if (!("device" in MULTIM[cntr])) {
printf("Device name missing. ")
return(1)
}
global ESRF_ERR_MSG
ESRF_ERR = -1
esrf_io(MULTIM[cntr]["device"],"DevReadValue")
if (ESRF_ERR > 0) {
tty_cntl("md")
printf("\n Error while initializing ADC multim\n")
printf(" - counter: %s\n",cnt_mne(cntr))
printf(" - ADC dev: %s\n",MULTIM[cntr]["device"])
printf(" - Errno: %d\n",ESRF_ERR)
printf(" %s\n",ESRF_ERR_MSG)
printf(" Please. Check the Linux VME\n\n")
tty_cntl("me")
printf(" Hint: if you got the message \"RPC client call timed out\":\n")
printf(" - the device server process may have died in the Linux VME\n")
printf(" or\n")
printf(" - you may be having network problems\n\n")
return(1)
}
'
#%IU%
#%MDESC%
# Reads the ADC value for type=lvmeadc
#
def multim_read_lvmeadc '
ESRF_ERR=-1
value = esrf_io(MULTIM[cntr]["device"],"DevReadValue")
if (!ESRF_ERR)
ok = 1
'
#%IU%
#%MDESC% Setup a single channel variables when type=madc.
def multim_init_madc '
local dsconf nbcha
if (!("device" in MULTIM[cntr])) {
printf("Device name missing. ")
return(1)
}
if (!("channel" in MULTIM[cntr]))
MULTIM[cntr]["channel"] = counter_par(cntr, "channel")
if (!("unit" in MULTIM[cntr]))
MULTIM[cntr]["unit"] = counter_par(cntr,"unit")
global ESRF_ERR_MSG
ESRF_ERR = -1
nbcha = esrf_io(MULTIM[cntr]["device"],"DevGetDsConfig",dsconf)
if (nbcha == -1 ) {
tty_cntl("md")
printf("\n Error while initializing ADC multim\n")
printf(" - counter: %s\n",cnt_mne(cntr))
printf(" - ADC dev: %s\n",MULTIM[cntr]["device"])
printf(" - Error : %s\n",ESRF_ERR_MSG)
tty_cntl("me")
return(1)
}
nbcha = (nbcha - 1)/dsconf[0]
npars = dsconf[0]
for (kk=0; kk<nbcha; kk++) {
if ((MULTIM[cntr]["channel"] == dsconf[kk*npars +1]) && \
(MULTIM[cntr]["unit"] == dsconf[kk*npars +2])) {
MULTIM[cntr]["idx"] = kk
break
}
}
'
#%IU%
#%MDESC% Read a single value when type=madc.
def multim_read_madc '
local double array madc_data[64]
ESRF_ERR=-1
esrf_io(MULTIM[cntr]["device"],"DevReadSigValues",madc_data)
if (!ESRF_ERR) {
value = madc_data[MULTIM[cntr]["idx"]]
ok = 1
}
'
#%IU%
#%MDESC%
# Iniatises values for a multimeter for type=dummy. %BR%
# Uses undocumented parameters "motor", "fwhm" and "center" (peak simulation)
# and "timeconstant" (exponential decay).
#
def multim_init_dummy '
if ("motor" in MULTIM[cntr]) {
if ((MULTIM[cntr]["motnum"] = motor_num(MULTIM[cntr]["motor"])) < 0) {
printf("Invalid motor name: \`%s\'. ", MULTIM[cntr]["motor"])
return(1)
}
if (!("fwhm" in MULTIM[cntr])) MULTIM[cntr]["fwhm"] = 1
MULTIM[cntr]["sigma"] = MULTIM[cntr]["fwhm"]/2.35482
} else if ("timeconstant" in MULTIM[cntr]) {
MULTIM[cntr]["timeconstant"] *= 60
MULTIM[cntr]["init_t"] = time()
}
'
#%IU%
#%MDESC%
# Simulates the reading of a multimeter for type=dummy
#
def multim_read_dummy '
if ("motor" in MULTIM[cntr]) {
xx = (A[MULTIM[cntr]["motnum"]]-MULTIM[cntr]["center"])/MULTIM[cntr]["sigma"]
value = exp(-xx*xx/2)
} else if ("timeconstant" in MULTIM[cntr]) {
value = exp(-(time()-MULTIM[cntr]["init_t"])/MULTIM[cntr]["timeconstant"])
} else
value = 1
value += sqrt(value)*(1-rand(10000)/5000)
value = fabs(value)
ok = 1
'
#%IU% (<counter_name>)
#%MDESC%
# This macro checks and configures the internal parameters.
#
def multim_config(cntr_str) '{
local par n i cntr type plist
if ((cntr=cnt_num(cntr_str)) >= 0){
list_init plist
n = list_getparlist(MULTIMPSEUDO, cntr_str, plist)
for (i in MULTIM[cntr]) delete MULTIM[cntr][i];;
for (i = 1; i <= n; i++) {
MULTIM[cntr][list_item(plist,i)] = list_getpar(plist, i, "value")
}
par = MULTIM[cntr]["integrate"]
MULTIM[cntr]["integrate"] = (par == 1 || par == "yes" || par == "YES")
par = MULTIM[cntr]["sample"]
MULTIM[cntr]["sample"] = (par == 1 || par == "yes" || par == "YES")
par = MULTIM[cntr]["average"]
MULTIM[cntr]["average"] = (par == 1 || par == "yes" || par == "YES")
if (!("polltime" in MULTIM[cntr]))
MULTIM[cntr]["polltime"] = 0
if (!("scale" in MULTIM[cntr]))
MULTIM[cntr]["scale"] = counter_par(cntr, "scale")
if (!("offset" in MULTIM[cntr]))
MULTIM[cntr]["offset"] = 0
if (!("type" in MULTIM[cntr]))
MULTIM[cntr]["type"] = "kgpib"
par = MULTIM[cntr]["trigger"]
MULTIM[cntr]["trigger"] = (par == "no" || par == "NO")? 0:1
if (whatis(rmac = sprintf("multim_read_%s", type=MULTIM[cntr]["type"])) & 2) {
cdef("multim_readinstrument", \
sprintf("\nif (type==\"%s\") {\n %s\n}\n", type, rmac), \
sprintf("multim_%s",type))
if (whatis(rmac = sprintf("multim_init_%s", type)) & 2) {
cdef("multim_initinstrument", \
sprintf("\nif (type==\"%s\") {\n %s\n}\n", type, rmac), \
sprintf("multim_%s",type))
}
} else {
printf("Type \"%s\" not recognised for pseudocounter \`%s\'\n",\
type, cntr_str)
}
S[cntr] = 0
if (multim_init(cntr, cntr_str) || integr && cnt_num("sec") < 0) {
counter_par(cntr, "disable", 1)
MULTIM[cntr]["enabled"] = 0
printf("Pseudocounter \`%s\' disabled.\n", cntr_str)
} else {
counter_par(cntr, "disable", 0)
MULTIM[cntr]["enabled"] = 1
}
}
}'
#%IU% (<counter>)
#%MDESC%
# This macro initialises the instruments if there is something to initialise.
# It returns 0 after a correct initialization and -1 if an error happens.
#
def multim_init(cntr, cntr_str) '{
local type par
type = MULTIM[cntr]["type"]
multim_initinstrument
return(0)
}'
#%UU%
#%MDESC%
# This macro displays the current configuration.
#
def multimshow '{
local i cntr enabled integr type scale rate
print
if (list_n(MULTIMPSEUDO) < 1)
print "No pseudocounters configured."
else {
print " # Counter Enabled Int. Type Scale Rate"
for (i=1; i<=list_n(MULTIMPSEUDO); i++){
if ((cntr = cnt_num(MULTIMPSEUDO[i])) >= 0) {
enabled = MULTIM[cntr]["enabled"]?"Yes":"No"
integr = MULTIM[cntr]["integrate"]?"Yes":"No"
if ((type = MULTIM[cntr]["type"]) == "kgpib" || MULTIM[cntr]["type"] == "kscpi")
type = sprintf("%s(%s)", MULTIM[cntr]["type"], MULTIM[cntr]["address"])
scale = MULTIM[cntr]["scale"]
rate = MULTIM[cntr]["noreads"] > 0? MULTIM[cntr]["noreads"]:"-"
} else {
enabled = "unknown"
integr = type = scale = rate = "-"
}
printf("%3d %7s %8s %4s %-12s %8g %8.3g\n", i, MULTIMPSEUDO[i],\
enabled, integr, type, scale, rate)
}
}
}'
#%UU%
#%MDESC%
# This macro is a version of the old %B%picosetup%B% compatible with the
# new macros. It is included only for backward compatibility.
#
def picosetup '{
global PICO_CHANNEL PICO_NO PICO_POLARITY PICO_NOREADS PICO_AVER PICO_INTEG
if ($# > 2) {
if (SETUP) print "Error in line: $0 $*"
print "Usage: picosetup [first gpib channel] [number of picoammeters]"
print " (this macro is now obsolete."\
" Think of switching to \`multimsetup\')"
} else {
PICO_CHANNEL = ($# < 1)? \
getval("The picoammeters start from which gpib channel",PICO_CHANNEL):$1
PICO_NO = ($# < 2)? \
getval("How many picoammeters do you use", PICO_NO):$2
if ($# != 2) PICO_INTEG=yesno("Use them in integrating mode",PICO_INTEG)
_picosetup
}
}'
#%IU%
#%MDESC%
#
#
def _picosetup '{
local i
for (i=0; i<PICO_NO; i++) {
_multimsetup(sprintf("pico%d address=%d integrate=%d scale=%g", i+1, \
PICO_CHANNEL+i, PICO_INTEG, (PICO_POLARITY!=-1)*1e12))
}
}'
#%UU%
#%MDESC%
# This macro changes the sign of the scale factor for those electrometers
# configured by %B%picosetup%B%
#
def picopol '
global PICO_POLARITY
if (PICO_POLARITY == -1) {
PICO_POLARITY = 1
printf ("Polarity of picoampermeter now positive\n")
} else {
PICO_POLARITY = -1
printf ("Polarity of picoampermeter now negative\n")
}
_picosetup
'
#
# New multim controller for temporary use with Bliss controller Gpib wrapper into Gpib tango server.
# Soon we will move to Bliss Keithley controller and Keithley tango server.
# Idea is to have a unique keithley controller for both DDC and SCPI protocols.
# The macro detect by itself if the Keithley uses a DDC or a SCPI protocol.
#
#%EXAMPLE%
#%DL%
#%DT% multimsetup pd0 type=bliss_keithley_gpib device=id09/gpib/pd0
#%BR%
#A pseudo counter named pd0 will be set up for a Keithley
#Picoamperemeter.
#%XDL%
#%END%
#%IU%
#%MDESC%
def multim_init_bliss_keithley_gpib '
local response
if (ds_is_responsive(MULTIM[cntr]["device"]) == 0) {
printf("Unresponsive bliss gpib keithley device %s. ", MULTIM[cntr]["device"])
return(1)
}
sleep(2)
# check if this is a keithley on gpib device and which interface DDC or SCPI
TANGO_ERR=-1
response = tango_io(MULTIM[cntr]["device"],"write_readline","*IDN?")
#sleep(2)
if (response == -1) {
printf("Unresponsive bliss gpib keithley device %s. ", MULTIM[cntr]["device"])
return(2)
}
if (index(response,"KEITHLEY INSTRUMENTS") > 0) {
MULTIM[cntr]["keithley_type"] = "scpi"
} else {
if (index(response,"NDCI") > 0) {
MULTIM[cntr]["keithley_type"] = "ddc"
}
else {
printf("Unsupported gpib device %s. ", MULTIM[cntr]["device"])
return(1)
}
}
if (MULTIM[cntr]["keithley_type"] == "scpi") {
tango_io(MULTIM[cntr]["device"],"write", "*RST")
# needed absolutely
sleep(0.2)
# tango_io(MULTIM[cntr]["device"],"write", "SYST:ZCH ON")
# sleep(.2)
# tango_io(MULTIM[cntr]["device"],"write", "CURR:RANG 2e-9")
# sleep(.2)
# tango_io(MULTIM[cntr]["device"],"write", "SYST:ZCOR ON")
# sleep(.2)
# tango_io(MULTIM[cntr]["device"],"write", "SENS:CURR:RANG:AUTO ON")
# sleep(.2)
# tango_io(MULTIM[cntr]["device"],"write", "SYST:ZCH OFF")
# sleep(.2)
tango_io(MULTIM[cntr]["device"],"write", "SYST:ZCH ON\nCURR:RANG 2e-9\n\
SYST:ZCOR ON\nSENS:CURR:RANG:AUTO ON\nSYST:ZCH OFF")
tango_io(MULTIM[cntr]["device"],"write_readline", "READ?")
#sleep(.2)
}
if (MULTIM[cntr]["keithley_type"] == "ddc") {
# OPTION 1 ==================== 20 sec ======================
# # Select 2nA range
# tango_io(MULTIM[cntr]["device"],"write_readline", "R1X")
# #Enable zero check and perform zero correction
# tango_io(MULTIM[cntr]["device"],"write", "C2X")
# sleep(20)
# tango_io(MULTIM[cntr]["device"],"flush")
# #Enable autorange
# tango_io(MULTIM[cntr]["device"],"write_readline", "R0X")
# # Read a value
# tango_io(MULTIM[cntr]["device"],"write_readline", "T0X")
# OPTION 2
# Read a value
tango_io(MULTIM[cntr]["device"],"write_readline", "T0X")
}
#tango_io(MULTIM[cntr]["device"],"flush")
'
#%IU%
#%MDESC%
def multim_read_bliss_keithley_gpib '{
local response
if (MULTIM[cntr]["keithley_type"] == "scpi") {
response = tango_io(MULTIM[cntr]["device"], "write_readline", "READ?")
local strs, values, n
n = split(response,strs,",")
n = split(strs[0], values, "A")
value = values[0] * 1.0
ok = 1
}
if (MULTIM[cntr]["keithley_type"] == "ddc") {
response = tango_io(MULTIM[cntr]["device"], "write_readline", "T0X")
if ( response != "") {
value = substr(response,5) * 1.0
ok = 1
}
}
}'
# Added by Holger, June 2003
# # ##### ##### ###### ###
# # # # # # # # #
# # # # # # #
### ##### # ###### #
# # # # # #
# # # # # # # #
# # ##### ##### # ###
#Recent Keithley Picoammeters (6485, 65..) use SCPI as communication protocol.
#MCD:07/07/2011: add "com" parameter. By default, is GPIB.
#But can be RS232. In that case, "address" must be set to the serial line index
#in the list config
#example: multimsetup kser type=kscpi address=1 com=RS232
#%EXAMPLE%
#%DL%
#%DT% multimsetup pico2 type=kscpi
#%BR%
#A pseudo counter named pico2 will be set up for a Keithley
#Picoamperemeter using the SCPI protocol. Unit and channel of the named
#counter are used as gpib interface and address respectively.
#%XDL%
#%END%
#%IU%
#%MDESC%
# Particular initialisation for type=kscpi
#
def multim_init_kscpi '
local response, n, manufacturer, waste, model_number, lowest_range_curr, mystr
local IDN[]
if (!("com" in MULTIM[cntr])){
if (("address" in MULTIM[cntr]) && \
(toupper(substr(MULTIM[cntr]["address"], 1, 4)) == "TCP:")) {
MULTIM[cntr]["com"] = "TCP"
MULTIM[cntr]["address"] = substr(MULTIM[cntr]["address"], 5)
} else {
MULTIM[cntr]["com"] = "GPIB"
}
}
if (!("address" in MULTIM[cntr])){
if (MULTIM[cntr]["com"] == "GPIB") {
MULTIM[cntr]["address"] = sprintf("%s:%s", \
counter_par(cntr, "unit"), counter_par(cntr, "channel"))
} else if (MULTIM[cntr]["com"] == "TCP") {
printf("Must provide IP address for kscpi TCP communication\n")
return(1)
} else {
MULTIM[cntr]["address"] = sprintf("%s",counter_par(cntr, "channel"))
}
}
if (MULTIM[cntr]["com"] == "GPIB"){
if (!gpib_cntl(MULTIM[cntr]["address"], "responsive")) {
printf("Unresponsive GPIB controller for address %s. ",MULTIM[cntr]["address"])
return(1)
}
if (gpib_poll(MULTIM[cntr]["address"]) == -1) {
printf("Unresponsive GPIB address %s. ", MULTIM[cntr]["address"])
return(1)
}
} else if (MULTIM[cntr]["com"] == "TCP") {
if (index(MULTIM[cntr]["address"], ":") == 0) {
local port
port = 5025
MULTIM[cntr]["address"] = sprintf("%s:%d", MULTIM[cntr]["address"], port)
}
if (!sock_par(MULTIM[cntr]["address"], "connect")) {
printf("Unresponsive TCP controller for address %s. ",MULTIM[cntr]["address"])
return(1)
}
}
kscpi_put(cntr, "*RST")
# needed absolutely
sleep(0.2)
# MULTIM[cntr]["sample"] = 1 # switch avaraging off
# the above line was there since the first version of the kscpi
# implementation. I have no recollection, why I put it there, but
# it keeps the integration from working.
kscpi_put(cntr, "*IDN?")
response = kscpi_get(cntr)
n = split(response, IDN, ",")
if(n == 4){
manufacturer = IDN[0]
if (index(manufacturer, "KEITHLEY")){
sscanf(IDN[1], "%s%d", waste, model_number)
if(model_number == 2001 || model_number == 2002 || model_number == 2010\
|| model_number == 2700 || model_number == 2701 \
|| model_number == 2750){
lowest_range_curr = -1
MULTIM[cntr]["lowest_range_curr"] = "unused"
}else if(model_number == 6482 || model_number == 6485 || model_number == 6487){
lowest_range_curr = "2e-9"
MULTIM[cntr]["lowest_range_curr"] = "2e-9"
}else if(model_number == 6514){
lowest_range_curr = "20e-12"
MULTIM[cntr]["lowest_range_curr"] = "20e-12"
}
else{
lowest_range_curr = "2e-9"
MULTIM[cntr]["lowest_range_curr"] = "unknown model"
}
}
## fell free to add other models and other manufacters.
}
if ( "readmode" in MULTIM[cntr]) {
if ( MULTIM[cntr]["readmode"] == "pt100") {
printf("<MULTIM> Configuring \"%s\" to read temp/pt100 (addr:%s)\n",cnt_mne(cntr),MULTIM[cntr]["address"])
kscpi_put(cntr, "SENS:FUNC \'TEMP\'")
kscpi_put(cntr, "UNIT:TEMP K")
kscpi_put(cntr, "SENS:TEMP:TRAN FRTD")
kscpi_put(cntr, "SENS:TEMP:FRTD:TYPE PT100")
}
if (MULTIM[cntr]["readmode"] == "current" || MULTIM[cntr]["readmode"] == "currentdc") {
printf("<MULTIM> Configuring \"%s\" to read DC current (addr:%s)\n",cnt_mne(cntr),MULTIM[cntr]["address"])
kscpi_put(cntr, "SENS:FUNC \'CURR:DC\'")
if( lowest_range_curr != -1){
kscpi_put(cntr, "SYST:ZCH ON")
mystr = sprintf("SENS:CURR:RANG %s", lowest_range_curr)
kscpi_put(cntr, mystr)
kscpi_put(cntr, "SYST:ZCOR ON")
kscpi_put(cntr, "SENS:CURR:RANG:AUTO ON")
kscpi_put(cntr, "SYST:ZCH OFF")
}else{
kscpi_put(cntr, "SENS:CURR:RANG:AUTO ON")
}
}
if (MULTIM[cntr]["readmode"] == "currentac") {
printf("<MULTIM> Configuring \"%s\" to read AC current (addr:%s)\n",cnt_mne(cntr),MULTIM[cntr]["address"])
kscpi_put(cntr, "SENS:FUNC \'CURR:AC\'")
if( lowest_range_curr != -1){
kscpi_put(cntr, "SYST:ZCH ON")
mystr = sprintf("SENS:CURR:RANG %s", lowest_range_curr)
kscpi_put(cntr, mystr)
kscpi_put(cntr, "SYST:ZCOR ON")
kscpi_put(cntr, "SENS:CURR:RANG:AUTO ON")
kscpi_put(cntr, "SYST:ZCH OFF")
}else{
kscpi_put(cntr, "SENS:CURR:RANG:AUTO ON")
}
}
if ( MULTIM[cntr]["readmode"] == "voltage" || MULTIM[cntr]["readmode"] == "voltagedc") {
printf("<MULTIM> Configuring \"%s\" to read voltage DC (addr:%s)\n",cnt_mne(cntr),MULTIM[cntr]["address"])
kscpi_put(cntr, "SENS:FUNC \'VOLT:DC\'")
kscpi_put(cntr, "SENS:VOLT:RANG:AUTO ON")
}
if ( MULTIM[cntr]["readmode"] == "voltageac") {
printf("<MULTIM> Configuring \"%s\" to read voltage AC (addr:%s)\n",cnt_mne(cntr),MULTIM[cntr]["address"])
kscpi_put(cntr, "SENS:FUNC \'VOLT:AC\'")
kscpi_put(cntr, "SENS:VOLT:RANG:AUTO ON")
}
if ( MULTIM[cntr]["readmode"] == "resistance") {
printf("<MULTIM> Configuring \"%s\" to read 2-wires resistance (addr:%s)\n",cnt_mne(cntr),MULTIM[cntr]["address"])
kscpi_put(cntr, "SENS:FUNC \'RES\'")
}
if ( MULTIM[cntr]["readmode"] == "resistance4w") {
printf("<MULTIM> Configuring \"%s\" to read 4-wires resistance (addr:%s)\n",cnt_mne(cntr),MULTIM[cntr]["address"])
kscpi_put(cntr, "SENS:FUNC \'FRES\'")
}
} else { # default. no readmode defined
kscpi_put(cntr, "SYSTEM:ZCHECK:STATE OFF") # switch off zero checking
kscpi_put(cntr, "SYST:AZER OFF") # swich off automatic zero checking
}
kscpi_put(cntr, "READ?") # read one value to enable display
#sleep(0.1)
kscpi_flush(cntr) #empty the buffer in the Keithley
'
#%IU%
#%MDESC%
# Reads encoder value for type=kscpi
#
def multim_read_kscpi '{
local kanswer
local strs, values, n
if (MULTIM[cntr]["trigger"]) {
kscpi_put(cntr, "READ?");#sleep(0.1)
}
if ((kanswer = kscpi_get(cntr)) != "") {
n = split(kanswer,strs,",")
sscanf(strs[0], "%g%s", values[0], values[1])
if (fabs(values[0] * 1.0 - values[0]) < 10e-4) {
value = values[0] * 1.0
ok = 1
} else {
printf( "error reading keithley (counter : %s)\n", cnt_mne(cntr))
ok = 0
}
}
}'
def kscpi_put(cntrl,str)'{
local compstr
if (MULTIM[cntrl]["com"] == "GPIB"){
gpib_put(MULTIM[cntrl]["address"], str)
} else if (MULTIM[cntrl]["com"] == "TCP"){
sock_put(MULTIM[cntrl]["address"], sprintf("%s\n", str))
} else {
compstr = sprintf("%s\n",str)
ser_put(MULTIM[cntrl]["address"],compstr)
}
}'
def kscpi_get(cntrl)'{
if (MULTIM[cntrl]["com"] == "GPIB"){
return (gpib_get(MULTIM[cntrl]["address"]))
} else if (MULTIM[cntrl]["com"] == "TCP"){
return sock_get(MULTIM[cntrl]["address"])
} else {
return(ser_get(MULTIM[cntrl]["address"],"\r"))
}
}'
def kscpi_flush(cntrl)'{
if (MULTIM[cntrl]["com"] == "GPIB"){
return (gpib_get(MULTIM[cntrl]["address"]))
} else if (MULTIM[cntrl]["com"] == "TCP"){
return sock_par(MULTIM[cntrl]["address"], "flush")
} else {
return(ser_par(MULTIM[cntrl]["address"],"flush",2))
}
}'
# Added by Laurent, march 2007
#
# TANGO ELECTROMETER DEVICE SERVER
#
#%IU%
#%MDESC%
# Particular initialisation for type=tangoelect%BR%
#
def multim_init_tangoelect '
if (!("device" in MULTIM[cntr])) {
printf("Device name missing. ")
return(1)
}
global ESRF_ERR_MSG
ESRF_ERR = -1
esrf_io(dev="tango:"MULTIM[cntr]["device"],"DevState")
if (ESRF_ERR > 0) {
tty_cntl("md")
printf("\n Error while initializing Tango Electrometer multim\n")
printf(" - counter: %s\n",cnt_mne(cntr))
printf(" - Electrometer dev: %s\n",MULTIM[cntr]["device"])
printf(" - Errno: %d\n",ESRF_ERR)
printf(" %s\n",ESRF_ERR_MSG)
tty_cntl("me")
printf(" Hint: if you got the message \"RPC client call timed out\":\n")
printf(" - the device server process may have died in the Linux box\n")
printf(" or\n")
printf(" - you may be having network problems\n\n")
return(1)
}
'
#%IU%
#%MDESC%
# Reads the ADC value for type=tangoelect
#
def multim_read_tangoelect '
ESRF_ERR=-1
value = esrf_io(dev="tango:"MULTIM[cntr]["device"]"/value","DevRead")
if (!ESRF_ERR)
ok = 1
'
#%UU% counter_name [on/off]
#%MDESC%Toggle Keithley pico amperemeter Automatic ZERO checking .
#Used to achieve optimum accuracy for low current measurements, readings become slower by a factor of 3.
def pico_azero '{
local i, cntr, azero, name, onoff
if ($# > 2 || $# < 1) {
print "Usage: $0 mne [\"on/off\"]"
exit
}
cntr = $1
name = cnt_name(cntr)
if (! MULTIM[cntr]["enabled"]) {
print "Sorry", name, "is not enabled. Rerun multimsetup."
exit
}
gpib_put(MULTIM[cntr]["address"], "SYST:AZER?")
azero = gpib_get(MULTIM[cntr]["address"])
print "Automatic zero check for", name, "is", azero ? "ON" : "OFF"
if ($# == 1) {
if (yesno(sprintf("Do you want to change it to %s", azero ? "OFF" : "ON"), 1)) {
gpib_put(MULTIM[cntr]["address"], sprintf("SYST:AZER %s", azero ? "OFF" : "ON"))
}
} else {
onoff = "$2"
if ( onoff == "ON" || onoff == "on") onoff = 1
else if ( onoff == "OFF" || onoff == "off") onoff = 0
else {
print "Value", "$1", "is not allowed as second argument!"
exit
}
if ( onoff != azero )
gpib_put(MULTIM[cntr]["address"], sprintf("SYST:AZER %s", onoff ? "ON" : "OFF"))
}
}'
#%UU% counter_name [on/off]
#%MDESC%switch Keithley pico amperemeter averging on or off, second argument is
#optional.
def pico_av '{
local i, cntr, av, name, onoff
if ($# > 2 || $# < 1) {
print "Usage: $0 mne [\"on/off\"]"
exit
}
cntr = $1
name = cnt_name(cntr)
if (! MULTIM[cntr]["enabled"]) {
print "Sorry", name, "is not enabled. Rerun multimsetup."
exit
}
av = MULTIM[cntr]["sample"] ? 0 : 1
print "averaging for", name, "is", av ? "ON" : "OFF"
if ($# == 1) {
if (yesno(sprintf("Do you want to change it to %s", av ? "OFF" : "ON"), 1)) {
MULTIM[cntr]["sample"] = av
}
} else {
onoff = "$2"
if ( onoff == "ON" || onoff == "on") onoff = 0
else if ( onoff == "OFF" || onoff == "off") onoff = 1
else {
print "Value", "$1", "is not allowed as second argument!"
exit
}
if ( onoff != av )
MULTIM[cntr]["sample"] = onoff
}
}'
#%UU% counter_name [on/off]
#%MDESC%Please do not use anymore, for backwards compatibility only.
def av_K648x 'pico_av'
#%UU% counter_name [on|off]
#%MDESC%Toggle Keithley pico amperemeter Automatic RANGE.
def pico_autorange '{
local i, cntr, arange, name, onoff, readmode
if ($# > 2 || $# < 1) {
print "Usage: $0 <counter mne> [on|off]"
exit
}
cntr = $1
name = cnt_name(cntr)
if (! MULTIM[cntr]["enabled"]) {
print "Sorry", name, "is not enabled. Rerun multimsetup."
exit
}
if ( "readmode" in MULTIM[cntr]) {
if ( MULTIM[cntr]["readmode"] == "current" || \
MULTIM[cntr]["readmode"] == "currentdc" || \
MULTIM[cntr]["readmode"] == "currentac" ) {
readmode = "CURR"
}
if ( MULTIM[cntr]["readmode"] == "resistance") {
readmode = "RES"
}
if ( MULTIM[cntr]["readmode"] == "resistance4w") {
readmode = "FRES"
}
if ( MULTIM[cntr]["readmode"] == "voltage" || \
MULTIM[cntr]["readmode"] == "voltagedc" || \
MULTIM[cntr]["readmode"] == "voltageac" ) {
readmode = "VOLT"
}
}
else { # default keithley is a a picoammeter
readmode = "CURR"
}
kscpi_put(cntr,sprintf("%s:RANG:AUTO?",readmode))
arange = kscpi_get(cntr)
print "Automatic range for", name, "is", arange ? "ON" : "OFF"
if ($# == 1) {
if (yesno(sprintf("Do you want to change it to %s", arange ? "OFF" : "ON"), 1)) {
kscpi_put(cntr, sprintf("%s:RANG:AUTO %s", readmode, arange ? "OFF" : "ON"))
}
} else {
onoff = "$2"
if ( onoff == "ON" || onoff == "on") onoff = 1
else if ( onoff == "OFF" || onoff == "off") onoff = 0
else {
print "Value", "$1", "is not allowed as second argument!"
exit
}
if ( onoff != arange ) {
kscpi_put(cntr, sprintf("%s:RANG:AUTO %s", readmode, onoff ? "ON" : "OFF"))
print "Automatic range for", name, "is now", onoff ? "ON" : "OFF"
}
}
}'
#%UU% counter_name [range]
#%MDESC%Set Keithley SCPI instrument RANGE.
# Or read the RANGE and asks a range if no range value is asked.
def pico_setrange '{
local msg val
local i, cntr, arange, name, onoff, rvals[], readmode, msg
if ($#<1) {
print "Usage: pico_setrange <mne> [range]"
exit
}
cntr = $1
name = cnt_name(cntr)
if (! MULTIM[cntr]["enabled"]) {
print "Sorry", name, "is not enabled. Rerun multimsetup."
exit
}
if (! MULTIM[cntr]["enabled"]) {
print "Sorry", name, "is not enabled. Rerun multimsetup."
exit
}
if ( "readmode" in MULTIM[cntr]) {
if ( MULTIM[cntr]["readmode"] == "current" || \
MULTIM[cntr]["readmode"] == "currentdc" || \
MULTIM[cntr]["readmode"] == "currentac" ) {
readmode = "CURR"
}
if ( MULTIM[cntr]["readmode"] == "resistance") {
readmode = "RES"
}
if ( MULTIM[cntr]["readmode"] == "resistance4w") {
readmode = "FRES"
}
if ( MULTIM[cntr]["readmode"] == "voltage" || \
MULTIM[cntr]["readmode"] == "voltagedc" || \
MULTIM[cntr]["readmode"] == "voltageac") {
readmode = "VOLT"
}
}else { # default keithley is a a picoammeter
readmode = "CURR"
}
if (readmode == "CURR") {
rvals[0]["name"]= "20pA"
rvals[0]["value"]= "20E-12"
rvals[1]["name"]= "200pA"
rvals[1]["value"]= "200E-12"
rvals[2]["name"]= "2nA"
rvals[2]["value"]= "2E-9"
rvals[3]["name"]= "20nA"
rvals[3]["value"]= "20E-9"
rvals[4]["name"]= "200nA"
rvals[4]["value"]= "200E-9"
rvals[5]["name"]= "2uA"
rvals[5]["value"]= "2E-6"
rvals[6]["name"]= "20uA"
rvals[6]["value"]= "20E-6"
rvals[7]["name"]= "200uA"
rvals[7]["value"]= "200E-6"
rvals[8]["name"]= "2mA"
rvals[8]["value"]= "2E-3"
rvals[9]["name"]= "20mA"
rvals[9]["value"]= "20E-3"
rvals[10]["name"]= "200mA"
rvals[10]["value"]= "200E-3"
rvals[11]["name"]= "2A"
rvals[11]["value"]= "2"
rvals["nb"]= 12
msg= ""
for (i=0; i < rvals["nb"]; i++) {
msg= msg " " rvals[i]["name"]
}
}else if (readmode == "RES" || readmode == "FRES") {
rvals[0]["name"]= "1Ohms"
rvals[0]["value"]= "1"
rvals[1]["name"]= "10Ohms"
rvals[1]["value"]= "10"
rvals[2]["name"]= "20Ohms"
rvals[2]["value"]= "20"
rvals[3]["name"]= "100Ohms"
rvals[3]["value"]= "100"
rvals[4]["name"]= "200Ohms"
rvals[4]["value"]= "200"
rvals[5]["name"]= "1kOhms"
rvals[5]["value"]= "1E3"
rvals[6]["name"]= "2kOhms"
rvals[6]["value"]= "2E3"
rvals[7]["name"]= "10kOhms"
rvals[7]["value"]= "10E3"
rvals[8]["name"]= "20kOhms"
rvals[8]["value"]= "20E3"
rvals[9]["name"]= "100kOhms"
rvals[9]["value"]= "100E3"
rvals[10]["name"]= "200kOhms"
rvals[10]["value"]= "200E3"
rvals[11]["name"]= "1MOhms"
rvals[11]["value"]= "1E6"
rvals[12]["name"]= "2MOhms"
rvals[12]["value"]= "2E6"
rvals[13]["name"]= "10MOhms"
rvals[13]["value"]= "10E6"
rvals[14]["name"]= "20MOhms"
rvals[14]["value"]= "20E6"
rvals[15]["name"]= "100MOhms"
rvals[15]["value"]= "100E6"
rvals[16]["name"]= "200MOhms"
rvals[16]["value"]= "200E6"
rvals[17]["name"]= "1GOhms"
rvals[17]["value"]= "1E9"
rvals[18]["name"]= "2GOhms"
rvals[18]["value"]= "2E9"
rvals[19]["name"]= "20GOhms"
rvals[19]["value"]= "20E9"
rvals[20]["name"]= "200GOhms"
rvals[20]["value"]= "200E9"
rvals["nb"]= 21
msg= ""
for (i=0; i<rvals["nb"]; i++) {
msg= msg " " rvals[i]["name"]
}
}else if (readmode == "VOLT") {
rvals[0]["name"]= "200mV"
rvals[0]["value"]= "200E-3"
rvals[1]["name"]= "2V"
rvals[1]["value"]= "2"
rvals[2]["name"]= "20V"
rvals[2]["value"]= "20"
rvals[3]["name"]= "200V"
rvals[3]["value"]= "200"
rvals[4]["name"]= "750V" # from AC
rvals[4]["value"]= "750" # from AC
rvals[5]["name"]= "1000V"
rvals[5]["value"]= "1E3"
rvals["nb"]= 6
msg= ""
for (i=0; i<rvals["nb"]; i++) {
msg= msg " " rvals[i]["name"]
}
}
if ($# > 2 || $# < 1) {
print "Usage: $0 mne [" msg "]"
exit
}
kscpi_put(cntr, sprintf("%s:RANG:AUTO?", readmode))
arange = kscpi_get(cntr)
if (arange == 1) {
print "Automatic range is ON. Cannot set range !"
print "pico_autorange to switch auto range off first."
exit
}
kscpi_put(cntr, sprintf("%s:RANG?", readmode))
arange = kscpi_get(cntr)
print "Previous range was", arange
if ($# == 1) {
print "Possible range :", msg, "\nAttention, all the ranges are not allowed by all the appliances."
val= getval("Your range", 0)
} else {
val= "$2"
}
arange= 0
for (i=0; i < rvals["nb"]; i++) {
if (rvals[i]["name"] == val) {
arange = rvals[i]["value"]
}
}
if (!arange) {
print "ERROR : Wrong input range !"
} else {
kscpi_put(cntr, sprintf("%s:RANG %s", readmode, arange))
}
kscpi_put(cntr, sprintf("%s:RANG?", readmode))
arange = kscpi_get(cntr)
for (i=0; i<rvals["nb"]; i++) {
if ( arange - rvals[i]["value"] < 0.06 * arange ) {
arange = rvals[i]["value"]
break
}
}
print "New range is", arange
kscpi_put(cntr, "READ?") # read one value to enable display
kscpi_flush(cntr) #empty the buffer in the Keithley
}'
#%UU% <mnemonic> <list|range>
#%MDESC% change gain setting. Allowed values for range are
# "2nA", "20nA", "200nA", "2uA", "20uA", "200uA", "2mA", "auto" and "fix".
# "fix" disables autoranging, keeping the currently chosen gain.
#
def multim_range '{
local usage addr command ranges[] range i status
local ranges_scpi[]
local cntr
ranges["auto"] = "R0"
ranges["2nA"] = "R1"
ranges["20nA"] = "R2"
ranges["200nA"] = "R3"
ranges["2uA"] = "R4"
ranges["20uA"] = "R5"
ranges["200uA"] = "R6"
ranges["2mA"] = "R7"
ranges["20mA"] = "R8"
ranges["200mA"] = "R9"
ranges["fix"] = "R10"
ranges_scpi["auto"] = ":RANG:AUTO ON"
ranges_scpi["2nA"] = ":RANG 2e-9"
ranges_scpi["20nA"] = ":RANG 20e-9"
ranges_scpi["200nA"] = ":RANG 200e-9"
ranges_scpi["2uA"] = ":RANG 2e-6"
ranges_scpi["20uA"] = ":RANG 20e-6"
ranges_scpi["200uA"] = ":RANG 200e-6"
ranges_scpi["2mA"] = ":RANG 2e-3"
ranges_scpi["20mA"] = ":RANG 20e-3"
ranges_scpi["200mA"] = ":RANG 200e-3"
ranges_scpi["fix"] = ":RANG:AUTO OFF"
usage = "$0 <mnemonic> <list|range>\nor use for SCPI devices \"pico_setrange\" which not allows only current ranges."
if ($# < 1) {
print usage; exit
}
if ($# == 2) {
if ("$2" == "list") {
printf ("Allowed ranges are: ")
for (i in ranges) printf ("%s ",i); printf ("\n"); exit
exit
}else { # set range
range = "$2"
if (!(range in ranges)) {
p "coucou"
printf ("Allowed ranges are: ")
local i
for (i in ranges) {
p i
printf ("%s ",i)
printf ("\n")
exit
}
}
}
}
addr = MULTIM[$1]["address"]
ktype = MULTIM[$1]["type"]
if (ktype == "bliss_keithley_gpib"){
cntr = cnt_num($1)
}else{
if (addr == 0) {
addr = "$1"
if ( cnt_mne($1) == "$1") {
if (MULTIM[$1]["address"] != 0) {
addr = MULTIM[$1]["address"]
}
}
}
}
if (ktype == "kscpi") {
command = ranges_scpi[range]
gpib_put(addr, sprintf("%s\r", command))
} else if (ktype == "bliss_keithley_gpib") {
if (MULTIM[cntr]["keithley_type"] == "scpi") {
command = ranges_scpi[range]
}
if (MULTIM[cntr]["keithley_type"] == "ddc") {
command = ranges[range]"X"
}
tango_io(MULTIM[cntr]["device"], "write", sprintf("%s",command))
} else {
command = ranges[range]"X"
gpib_put(addr, sprintf("%s\r", command))
}
if ($# == 1) { # get range
if (ktype == "kscpi") {
gpib_put(addr,":RANG?\r") # query machine status word
printf("%s", gpib_get(addr))
gpib_put(addr,":RANG:AUTO?\r") # query machine status word
if ( gpib_get(addr) == "1") {
print " (AUTO range)\n"
} else {
print " (FIXED range)\n"
}
} else if (ktype == "bliss_keithley_gpib") {
if (MULTIM[cntr]["keithley_type"] == "scpi") {
status = tango_io(MULTIM[cntr]["device"],"write_readline",":RANG?")
print status
status = tango_io(MULTIM[cntr]["device"],"write_readline",":RANG:AUTO?")
if ( status == "1") {
print " (AUTO range)\n"
} else {
print " (FIXED range)\n"
}
}
if (MULTIM[cntr]["keithley_type"] == "ddc") {
# query machine status word
status = tango_io(MULTIM[cntr]["device"],"write_readline","U0X")
i = index (status,"R")
autorange = substr(status,i+1,1)
range = "R"substr(status,i+2,1)
for (i in ranges) if (range == ranges[i]) range = i
if (autorange) print "auto (currently "range")"
else print range" (fixed)"
}
} else {
gpib_put(addr,"U0X\r") # query machine status word
status = gpib_get(addr)
i = index (status,"R")
autorange = substr(status,i+1,1)
range = "R"substr(status,i+2,1)
for (i in ranges) if (range == ranges[i]) range = i
if (autorange) print "auto (currently "range")"
else print range" (fixed)"
}
}
}'
#%UU% mnemonic
#%MDESC% This macro uses a macro fuction which returns the used range of the instrument.
# Example: multim_query_range pd2
def multim_query_range '{
local usage
usage = "Warning, usage is: $0 mnemonic_counter"
if ($# != 1) {
print usage; exit
}
local pindiode pindiode_num
pindiode = "$1"
pindiode_num = cnt_num("$1")
printf( "%s range: %s", pindiode, _multim_query_range(pindiode_num))
}'
#%IU% mnemonic
#%MDESC% This macro function returns the used range of the instrument.
# Example: _multim_query_range(4)
def _multim_query_range(pindiode_num) '{
local retmsg addr ktype
local ranges[] ranges_scpi[]
local ans
ranges["auto"] = "R0"
ranges["2nA"] = "R1"
ranges["20nA"] = "R2"
ranges["200nA"] = "R3"
ranges["2uA"] = "R4"
ranges["20uA"] = "R5"
ranges["200uA"] = "R6"
ranges["2mA"] = "R7"
ranges["20mA"] = "R8"
ranges["200mA"] = "R9"
ranges["fix"] = "R10"
ranges_scpi["auto"] = ":RANG:AUTO ON"
ranges_scpi["2nA"] = ":RANG 2e-9"
ranges_scpi["20nA"] = ":RANG 20e-9"
ranges_scpi["200nA"] = ":RANG 200e-9"
ranges_scpi["2uA"] = ":RANG 2e-6"
ranges_scpi["20uA"] = ":RANG 20e-6"
ranges_scpi["200uA"] = ":RANG 200e-6"
ranges_scpi["2mA"] = ":RANG 2e-3"
ranges_scpi["20mA"] = ":RANG 20e-3"
ranges_scpi["200mA"] = ":RANG 200e-3"
ranges_scpi["fix"] = ":RANG:AUTO OFF"
addr = MULTIM[pindiode_num]["address"]
ktype = MULTIM[pindiode_num]["type"]
if (ktype == "kscpi") {
gpib_put(addr,":RANG?\r") # query machine status word
retmsg = sprintf("%s", gpib_get(addr))
gpib_put(addr,":RANG:AUTO?\r") # query machine status word
if ( gpib_get(addr) == "1") {
retmsg = sprintf("%s (AUTO)\n",retmsg)
} else {
retmsg = sprintf("%s (FIXED)\n",retmsg)
}
}
else if (ktype == "bliss_keithley_gpib") {
if (MULTIM[pindiode_num]["keithley_type"] == "scpi") {
retmsg = tango_io(MULTIM[pindiode_num]["device"],"write_readline",":RANG?")
ans = tango_io(MULTIM[pindiode_num]["device"],"write_readline",":RANG:AUTO?")
if ( ans == "1") {
retmsg = sprintf("%s (AUTO)\n",retmsg)
} else {
retmsg = sprintf("%s (FIXED)\n",retmsg)
}
}
if (MULTIM[pindiode_num]["keithley_type"] == "ddc") {
# query machine status word
status = tango_io(MULTIM[pindiode_num]["device"],"write_readline","U0X")
i = index (status,"R")
autorange = substr(status,i+1,1)
range = "R"substr(status,i+2,1)
for (i in ranges) if (range == ranges[i]) range = i
if (autorange) {
retmsg = sprintf("auto (currently %s)\n",range)
} else {
retmsg = sprintf("%s (fixed)\n",range)
}
}
}
else {
gpib_put(addr,"U0X\r") # query machine status word
status = gpib_get(addr)
i = index (status,"R")
autorange = substr(status,i+1,1)
range = "R"substr(status,i+2,1)
for (i in ranges) if (range == ranges[i]) range = i
if (autorange) {
retmsg = sprintf("auto (currently %s)\n",range)
} else {
retmsg = sprintf("%s (fixed)\n",range)
}
}
return ( retmsg )
}'
#%MACROS%
#%IMACROS%
#%INTERNALS%
# To add a new type of signal source one must do the following:%BR%%BR%
# 1.- Choose a string <type> to identify the type of the instrument.%BR%%BR%
# 2.- If the instrument needs any kind of initialisation include it in a
# macro called %B%multim_init_<type>%B%. This is executed at the end
# of %B%multimsetup%B% and %B%reconfig%B%. One can include here any check
# or parameter initialisation that might speed up the counting. %BR%%BR%
# 3.- Write a macro called %B%multim_read_<type>%B% that gets the instrument
# reading and saves it in the local variable `value'. If the value
# is succesfully set, the macro must set the variable `ok' to 1.%BR%%BR%
# Have a look to %B%multim_read_kgpib%B% and %B%multim_init_kgpib%B% as
# examples of how to check and read the parameters.
#
#%AUTHOR% P. Fajardo, (Original 2/96)%BR%
# Holger a little bit (kscpi, June 2003)%BR%
# $Revision: 3.44 $ / $Date: 2021/10/05 12:21:41 $
#%TOC%
|