esrf

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

#%TITLE% ICPLUS.MAC
#
#%NAME%
#  Macros for operating the Oxford Danfysik 4-channel IC_PLUS electrometer
#
#%CATEGORY%  Detection, BPM
#   
#%OVERVIEW%
#  This macro set allows to control IC-PLUS units in %B%spec%B% and configure
#  their signal inputs as pseudocounters. 
#  %BR%
#  More than one IC-PLUS unit can be operated through this macro set.
#
#%EXAMPLE%
#  %DL%
#  %DT%icplussetup mydev 0 c1=curr1 c2=curr2 c3=curr3 c4=curr4
#  %DD%Configures an IC-PLUS unit connected to the spec serial line #0 and
#      associates its four input channels to the pseudocounters
#      curr1, curr2, curr3 and curr4.
#  %DT%icplussetup bpm0 id33/sl_lid331/3 c1c2=beamX c3c4=beamY
#  %DD%Configures an IC-PLUS unit connected through the serial line device
#      id33/sl_lid331/3.
#      Associates normalised current differences to the pseudocounters
#      beamX and beamY.
#  %DT%icplus
#  %DD%Display the list of IC-PLUS units currently configured.
#  %XDL%
#
#%END%

#%HISTORY%
#$Log: icplus.mac,v $
#Revision 1.2  2008/07/17 14:19:34  rey
#Documentation changes.
#
#Revision 1.1  2008/04/04 12:59:24  rey
#Counter identifiers have been changed from #1, #2 etc to c1, c2
#as newer versions of spec would interpret, (correctly), those
#symbols as comments.
#WARNING: this is an incompatible change but in principle only
#one beamline was using these macros at the time of the
#change.
#
#Revision 1.0  2005/09/23 08:18:35  blissadm
#Initial revision
#
#

#%UU% <icpname> <serline> [<parameter>=<value> ...]
#%MDESC%
#  Configures a IC-PLUS connected to <serline> with the name <icpname>.
#  The following optional parameters can be also set.
#  %UL%
#  %LI%Valid parameters:%DL%
#    %DT%address=<addr>
#    %DD%This parameter allows to address a particular unit when the serial 
#        line is shared by several IC-PLUS units. The default address is 0.
#    %DT% c1=<cnt_mne>
#    %DT% c2=<cnt_mne>
#    %DT% c3=<cnt_mne>
#    %DT% c4=<cnt_mne>
#    %DD%If <cnt_mne> is a valid counter mnemonic, it gets configured as
#        a pseudocounter loaded with the current in the corresponding
#        channel (1 to 4).
#        The "Scale Factor" value in the %B%spec%B% config file applies.
#    %DT% c1c2=<cnt_mne>
#    %DT% c3c4=<cnt_mne>
#    %DD%If <cnt_mne> is a valid counter mnemonic, it gets configured as
#        a pseudocounter loaded with the normalised current difference
#        (c1-c2)/(c1+c2) or (c3-c4)/(c3+c4).
#        The "Scale Factor" value in the %B%spec%B% config file applies.
#    %DT% c12c34=<cnt_mne>
#    %DT% c13c24=<cnt_mne>
#    %DD%If <cnt_mne> is a valid counter mnemonic, it gets configured as
#        a pseudocounter loaded with the normalised current difference
#        (c1+c2-c3-c4)/(c1+c2+c3+c4) or (c1-c2+c3-c4)/(c1+c2+c3+c4).
#        The "Scale Factor" value in the %B%spec%B% config file applies.
#    %DT%autorange[=YES]
#    %DD%Sets the autorange mode
#    %DT%window={<wtime> | AUTO}
#    %DD%Sets the integration window width to <wtime> seconds or to auto mode.
#        The width range of the integration time window is limited.
#    %XDL%
#  %XUL%
#
def icplussetup '{
   local retval

   if (retval = _icplussetup("$*")) {
      if (SETUP) print "Error in line: $0 $*"
      if (retval < 0) {
         print "Usage:  $0 name serial_line [parameter=value ...]"
         if (yesno("\nDisplay online help", 0)) eval("help local icplus")
      }
   } 
}'

def _icplussetup(args) '{
   global ICPLUS[]

   local i nparam auxlist0[] auxlist1[] larr 
   local icpname comdev mode
   local address testcomm
   local cscale auto

   nparam = split(args, auxlist0)
   for (i = 0; i < nparam; i++) {
      if (split(auxlist0[i], larr, "=") > 1){
         delete auxlist0[i]
         auxlist1[larr[0]] = larr[1]
      } else if (i > 1) {
         delete auxlist0[i]
         auxlist1[larr[0]] = "yes"
      }
   }
   if (!((0 in auxlist0) && (1 in auxlist0))) {
      printf("Bad parameters\n")
      return(-1)
   }
   icpname = auxlist0[0]
   comdev = auxlist0[1]

   if (icpname + 0 == icpname) {
      printf("Bad IC-PLUS name : %s\n", icpname)
      return(-1)
   }

   if (comdev + 0 == comdev) {
      mode = "spec"
   } else if (index(comdev, "/")) {
      mode = "dserver"
      serpar[0]  = 3; serpar[1] = 256     ; # 1 sec
      serpar[2]  = 4; serpar[3] = 0       ; # No parity
      serpar[4]  = 5; serpar[5] = 0       ; # 8 data bits
      serpar[6]  = 6; serpar[7] = 0       ; # 1 stop bit
      serpar[8]  = 7; serpar[9] = 19200   ; # baud rate
      serpar[10] = 8; serpar[11] = 0xa    ; # newline 
      ESRF_ERR = -1
      if (esrf_io(comdev, "DevSerSetParameter", serpar) < 0)
         return(-1)

   } else {
      printf("Bad serial line : %s\n", comdev)
      return(-1)
   }

   if (ICPLUS["setup_n"] != SETUP_N) {
      list_init ICPLUS
      ICPLUS["setup_n"] = SETUP_N
   }

   address = ("address" in auxlist1)? auxlist1["address"] : 0
   delete auxlist1["address"]

   testcomm = ":CONF" address ":CURR:RANG?\n"
   icplus__put(comdev, mode, testcomm)
   currrang = icplus__get(comdev, mode)
   if (currrang == "") {
      printf("No IC-PLUS found at serial line: %s , address: %d\n", comdev, address)
      return(1)
   }

   list_add(ICPLUS, icpname)
   ICPLUS[icpname]["mode"] = mode
   ICPLUS[icpname]["comdev"] = comdev
   ICPLUS[icpname]["range"] = currrang

   ICPLUS[icpname]["setup"] = "icplussetup " args

   ICPLUS[icpname]["address"] = address
   ICPLUS[icpname]["conf"] = ":CONF" address ":"
   ICPLUS[icpname]["read"] = ":READ" address ":"

   cscale = auxlist1["cscale"] + 0
   delete auxlist1["cscale"]
   ICPLUS[icpname]["cscale"] = cscale = (scale <= 0) ? 1 : cscale

   ICPLUS[icpname][1] = 350e-9
   ICPLUS[icpname][2] = 700e-9
   ICPLUS[icpname][3] = 1.4e-6
   ICPLUS[icpname][4] =   7e-6
   ICPLUS[icpname][5] =  70e-6
   ICPLUS[icpname][6] = 700e-6

   icplus_getrange(icpname)

   auto = auxlist1["autorange"]
   ICPLUS[icpname]["autorange"] = (auto == "yes" || auto == "YES" || auto == 1)
   delete auxlist1["autorange"]

   i = auxlist1["window"]
   delete auxlist1["window"]
   if (i == "auto" || i == "AUTO" || i <= 0) {
      ICPLUS[icpname]["window"] = "auto"
      if (i <= 0)
         printf("IC-PLUS %s: Bad integration window : %g sec\n", icpname, i)
   } else {
      ICPLUS[icpname]["window"] = i
      icplus_setwin(icpname, ICPLUS[icpname]["window"]);
   }

   ICPLUS[icpname]["pseudo"] = 0
   icp_add_pseudo "c1"
   icp_add_pseudo "c2"
   icp_add_pseudo "c3"
   icp_add_pseudo "c4"
   icp_add_pseudo "c1c2"
   icp_add_pseudo "c3c4"
   icp_add_pseudo "c12c34"
   icp_add_pseudo "c13c24"

   if (ICPLUS[icpname]["pseudo"]) {
      macrodef = sprintf("icplus_getcounts(\"%s\", %d)\n", icpname, auto)
      cdef("user_getcounts", macrodef, icpname)
   } else {
      cdef("user_getcounts", "", isgname, "delete")
   }

   setup_tail("icplus", icpname)

   i = ""
   for (i in auxlist1) {
      print "Invalid parameter: " i "=" auxlist1[i]
   } 
   if (i != "")
      return(-1)
   else
      return(0)
}'

def icp_add_pseudo '{
   local chx cnum

   chx = "$1"
   if (chx in auxlist1) {
      ICPLUS[icpname][chx] = cnum = cnt_num(auxlist1[chx])
      delete auxlist1[chx]
      if (cnum >= 0)
         ICPLUS[icpname]["pseudo"] = 1
   } else
      ICPLUS[icpname][chx] = -1
}'

def icplusunsetup '{
   local idname key

   key = "$1"
   cdef("", "", key, "delete")

   list_remove(ICPLUS, key)
   if (list_n(ICPLUS) <= 0)
      unglobal ICPLUS
}'


def icplus_conf(icpname, comm) '{
   local read

   read = (substr(comm, length(comm)) == "?")
   comm = ICPLUS[icpname]["conf"] comm "\n"

   return(icplus__comm(icpname, comm, read))
}'

def icplus_read(icpname, comm) '{
   comm = ICPLUS[icpname]["read"] comm "\n"

   return(icplus__comm(icpname, comm, 1))
}'

def icplus__comm(icpname, comm, read) '{
   local mode comdev 

   mode = ICPLUS[icpname]["mode"]
   comdev = ICPLUS[icpname]["comdev"]
   icplus__put(comdev, mode, comm)
   if (read) {
      return(icplus__get(comdev, mode))
   } else {
      return("")
   }
}'


def icplus__put(comdev, mode, comm) '{
   local nchar

   if (ISGDEBUG) print "icplus__put[" comdev"]: <" comm ">"
   if (mode == "spec") {
      ser_par(comdev, "flush")
      nchar = ser_put(comdev, comm)
   } else if (mode == "dserver") {
      nchar = esrf_io(comdev, "DevSerWriteString", comm)
   }
}'

def icplus__get(comdev, mode) '{
   local answer len c

   if (mode == "spec") {
      answer = ser_get(comdev, "\n")
   } else if (mode == "dserver") {
      answer = esrf_io(comdev, "DevSerReadString", 2)
   } else {
      print "icplus__get[" comdev"]: Wrong serial mode"
      return("")
   }

   while ((c = asc(answer)) && c <= 32) {
      answer = substr(answer,  2)
   }
   if (c) {
      answer = substr(answer, 1, length(answer) - 1)
      if (ISGDEBUG) print "icplus__get[" comdev"]: <" answer ">"
      return(answer)
   } else
      return("")
}'

#%IU% (<icpname>, <itime>)
#%MDESC%
# Sets the integration window <wtime> in sec
def icplus_setwin(icpname, wtime) '{
   local wsamples

   # The IC-PLUS sampling time is 10 ms (1/100 sec)
   wsamples = int(wtime * 100)
   # the number of averaged samples is 1 to 50
   if (wsamples < 1) wsamples = 1
   if (wsamples > 50) wsamples = 50
   if (wsamples != ICPLUS[icpname]["wsamples"]) {
      printf("IC-PLUS %s: using %g sec as integration window\n", \
                    icpname, wsamples / 100)
      ICPLUS[icpname]["wsamples"] = wsamples
   }
   icplus_conf(icpname, sprintf("WDWCURR %d", wsamples))
}'

#%IU% (<icpname>, <range>)
#%MDESC%
# Selects one of the 6 available gain ranges (1 to 6).
def icplus_setrange(icpname, range) '{
   icplus_conf(icpname, sprintf("CURR:RANG %d", range))
   range = icplus_getrange(icpname)

   printf("Setting IC-PLUS %s to range %d, %g Amps\n", \
              icpname, range, ICPLUS[icpname]["fscale"])
   return(range)
}'

#%IU% (<icpname>)
#%MDESC%
# Returns the currently selected gain range (1 to 6).
def icplus_getrange(icpname) '{
   local comm range fullscale

   range = icplus_conf(icpname, "CURR:RANG?")
   if (range >= 1 && range <= 6) {
      ICPLUS[icpname]["range"] = range
      fullscale = ICPLUS[icpname][range]
      ICPLUS[icpname]["fscale"] = fullscale
      ICPLUS[icpname]["cfactor"] = ICPLUS[icpname]["cscale"] * fullscale / 0x100000
      if (range == 1)
         ICPLUS[icpname]["minval"] = 0
      else 
         ICPLUS[icpname]["minval"] = \
              0.5 * ICPLUS[icpname][range - 1] / fullscale * 0x100000

      if (range == 6)
         ICPLUS[icpname]["maxval"] = 0x200000
      else 
         ICPLUS[icpname]["maxval"] = 0x100000
   }
   return(range)
}'

#%IU% (<icpname>)
#%MDESC%
# 
def icplus_setfullscale(icpname, fs) '{
   local r

   for (r = 1; r <= 6; r++)
      if (ICPLUS[icpname][r] > fs) break;

   icplus_setrange(icpname, r)
   return(ICPLUS[icpname]["fscale"])
}'

def icplus_default() '{
   local icpname

   if (list_n(ICPLUS) <= 0)
      return(0)

   icpname = ICPLUS["default"]
   if (list_item(ICPLUS, icpname) <= 0) {
      icpname = list_item(ICPLUS, 1)
      ICPLUS["default"] = icpname
   }
   return(icpname)
}'

#%UU% {<full_scale> | <range>}  [<unit>]
#%MDESC%
# 
def icplusrange '{
   local par icpname

   par = $1
   if (par <= 0 || $# > 2) {
      print "Usage: $0 <full_scale> [<unit>]"
      print "       $0 <range> [<unit>]"
      print 
      print "<full_scale> is  current in Amps"
      print "<range> is 1 to 6"
      exit
   }

   if ((icpname = icplus_default()) == 0) {
      print "No IC-PLUS units configured"
      exit
   }

   if (par < 1) 
      icplus_setfullscale(icpname, par)
   else
      icplus_setrange(icpname, par)
}'

def icplus_getcounts(icpname, auto) '{
   local answ i1 i2 i3 i4
   local f imax

   while(1) {
      answ = icplus_read(icpname, "CURRALL?")
      sscanf(answ, "%d %d %d %d", i1, i2, i3, i4)

      if (!ICPLUS[icpname]["autorange"] && !auto)
         break;
      
      imax = i1
      if (i2 > imax) imax = i2
      if (i3 > imax) imax = i3
      if (i4 > imax) imax = i4
      
      if (imax < ICPLUS[icpname]["minval"])
         icplus_setrange(icpname, ICPLUS[icpname]["range"] - 1)
      else if (imax > ICPLUS[icpname]["maxval"])
         icplus_setrange(icpname, ICPLUS[icpname]["range"] + 1)
      else
         break
      sleep(ICPLUS[icpname]["wsamples"] / 100)
   }

   f = ICPLUS[icpname]["cfactor"]
   if ((cnt = ICPLUS[icpname]["c1"]) >= 0)
      S[cnt] = i1 * f / counter_par(cnt, "scale")
   if ((cnt = ICPLUS[icpname]["c2"]) >= 0)
      S[cnt] = i2 * f / counter_par(cnt, "scale")
   if ((cnt = ICPLUS[icpname]["c3"]) >= 0)
      S[cnt] = i3 * f / counter_par(cnt, "scale")
   if ((cnt = ICPLUS[icpname]["c4"]) >= 0)
      S[cnt] = i4 * f / counter_par(cnt, "scale")
   if ((cnt = ICPLUS[icpname]["c1c2"]) >= 0)
      S[cnt] = (i1-i2) / (i1+i2) / counter_par(cnt, "scale")
   if ((cnt = ICPLUS[icpname]["c3c4"]) >= 0)
      S[cnt] = (i3-i4) / (i3+i4) / counter_par(cnt, "scale")
   if ((cnt = ICPLUS[icpname]["c12c34"]) >= 0)
      S[cnt] = (i1+i2-i3-i4) / (i1+i2+i3+i4) / counter_par(cnt, "scale")
   if ((cnt = ICPLUS[icpname]["c13c24"]) >= 0)
      S[cnt] = (i1-i2+i3-i4) / (i1+i2+i3+i4) / counter_par(cnt, "scale")
}'

#%UU%
#%MDESC%
# Adjusts the gain range 
def icplusauto '{
   local icpname auto

   if ((icpname = icplus_default()) == 0) {
      print "No IC-PLUS units configured"
      exit
   }
   auto = 1
   icplus_getcounts(icpname, auto)
}'

#%UU%
#%MDESC%
# Shows the list of IC-PLUS units configured
def icplus '{
   local ndev comdev dev_id

   if (whatis("ICPLUS") & 0x01000000) {
      print "\nIC-PLUS units currently configured:"
      print
      ndev = list_n(ICPLUS)
      printf(" #  %-9s %-8s %-20s %-8s\n", "Name", "Range", "Serial Line", "Address")
      printf("--- --------- -------- -------------------- ---------\n")
      for (i = 1; i <= ndev; i++) {
         name = ICPLUS[i]
         comdev = ICPLUS[name]["comdev"]
         dev_id = "#" comdev "_" ICPLUS[name]["pos"]
         printf("%2d  %-9s %-8s %-20s %-8s\n", i, name, ICPLUS[name]["range"],\
                 comdev, ICPLUS[name]["address"])
      }
   } else {
      print "No IC-PLUS currently configured."
   }
}'

#%MACROS%
#%AUTHOR% P.Fajardo, (Original 04/05).
#  $Revision: 1.2 $ / $Date: 2008/07/17 14:19:34 $
#%TOC%