esrf

Beamline Instrument Software Support
SPEC Macro documentation: [ Macro Index | BCU Home ]

#%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%