esrf

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

#%TITLE% stanford_SR570.mac
#%NAME%
#  %B%stanford_SR570.mac%B% - macros to control the Stanford Research
#Systems SR570 Low-Noise Current Preamplifier
#%DESCRIPTION%
# The %B%RS232%B% serial interface allows remote control. The parameters
#of the serial line are:    %BR%
#       - 8 bits                           %BR%
#       - none                             %BR%
#       - 2 stop bit                       %BR%
#       - 9600 bauds                       %BR%
#       - raw (in the SPEC config)         %BR%
#       - terminators CR(0xd) and LF(0xa)  %BR%

# The interface is configured as listen only (ser_put only) ???


# EX:
# st_setup 2
#%UU% [sl_number]
#%MDESC%  Set the serial line sl_number to be used, as in the config
#of SPEC.
def st_setup '{
    global ST_SL ST_PARAMS
    local tmp nb
    local ii

    if ($#) {
        nb = split("$*",tmp)
        for (ii=0; ii<nb; ii++){
            ST_SL[ii] = tmp[ii]
        }
    }
    else {
        nb = input("How many amplifiers do you want to configure? ")
        for (i=0; i<nb; i++)
            ST_SL[i] = getval("Serial line number (as in spec config):",ST_SL[i])
    }
    ST_PARAMS["nb"] = nb
}'


######################################################################
###############################         ##############################
###############################  RANGE  ##############################
###############################         ##############################
######################################################################

#%UU% <nb> <range> (<nb2> <range2> ...)
#%MDESC% Possible ranges : 1,2,5,10,20,50,100, 200, 500
#pA/V or nA/V or uA/V and 1 mA/V. The input is in pA/V. If nb not specified,
#all the connected instruments will be red. If range not specified, the
#current range will be printed. If the range does not exeed 1mA/V but is not
#an exact Stanford range the next lowest one is choosen.
def st_range '{
    local nb ret tmp i

    nb = split("$*", tmp)

    if (nb == 0) {
        for (nb in ST_SL) {
            ret = _st_range(nb)
            printf ("Amplifier #%d range is %g pA/V\n", nb, ret)
        }
    } else {
        for (i = 0; i<nb; i+= 2) {
            ret = _st_range(tmp[i],tmp[i+1])
            if (ret == 0) {
                eprintf("Amplifier #%d does not exist, nothing done\n", tmp[i])
            } else if (ret >=1){

                printf ("Amplifier #%d range set to %s/V\n", tmp[i], st_value_to_scale(ret))

            } else if (ret == -2) {
                eprintf("Amplifier #%d range %g out of limits, nothing done\n", \
                        tmp[i], tmp[i+1])
            } else if ((nb == 1) && (tmp[i+1] == 0)) {
                printf ("Amplifier #%d range is %g pA/V\n", tmp[i], ret)
            }
        }
    }
}'



# Displays in a user friendly manner (1..500 with uA pA nA mA) the range/offset value (1..5000000000)
def st_value_to_scale(value) '{
    local _unit  _range

    if (value >= 1e9){
        _range = value / 1e9
        _unit = "mA"
    }
    else if(value >= 1e6){
        _range = value / 1e6
        _unit = "uA"
    }
    else if(value >= 1000){
        _range = value / 1000
        _unit = "nA"
    }
    else if(value >= 1){
        _range = value
        _unit = "pA"
    }
    else{
         print "st_value_to_scale ERROR : value out of range."
    }

    return (sprintf("%s %s", _range, _unit))
}'


# [<nb>] <range> <unit> (No <nb> = number 0)
# Define the range with an associated unit
#   range : 1,2,5,10,20,50,100,200,500
#   unit  : p n u m (pico nano micro milli)
def st_urange '{
    local _nb_param  _amp_no _unit  _range  _unit_factor

    _nb_param = $#

    if (_nb_param <= 1){
        print "$0 usage :"
        print "   $0 [<nb>] <range> <unit>"
        print "   range : 1, 2, 5, 10, 20, 50, 100, 200, 500"
        print "   unit  : \"pA\" \"nA\" \"uA\" or \"mA\"  (pico nano micro milli)"
        exit
    }
    else{
        if (_nb_param == 3){
            _amp_no = "$1"
            _range = "$2"
            _unit = "$3"
        }
        if (_nb_param == 2) {
            _amp_no = 0
            _range = "$1"
            _unit = "$2"
        }
        if (_nb_param > 3) {
            print "$0 usage :"
            print "   $0 [<nb>] <range> <unit>"
            print "   range : 1, 2, 5, 10, 20, 50, 100, 200, 500"
            print "   unit  : \"pA\" \"nA\" \"uA\" or \"mA\"  (pico nano micro milli)"
            exit
        }

    }

    if (!is_number_in_range(_range, 1, 500)){
        print "error range must be in [1;500]"
        exit
    }

    if (_unit=="pA") {
        _unit_factor = 1
    }
    else if (_unit=="nA") {
        _unit_factor = 1e3
    }
    else if (_unit=="uA"){
        _unit_factor = 1e6
    }
    else if (_unit=="mA"){
        _unit_factor = 1e9
    }
    else{
        print "error : unit must be in \"pA\" \"nA\" \"uA\" or \"mA\" "
        exit
    }

    # print "unit factor = " _unit_factor

    eval(sprintf("st_range %d %d", _amp_no,_range*_unit_factor))
}'


#%UU% <nb, range>
#%MDESC% Set the sensituvity range for the nb amplifier. The range
#is in pA/V, nb starts from 1. If the range does not exeed 1mA/V but is not
#an exact Stanford range the next lowest one is choosen. Return the current
#range, 0 if instrument not configured, -1 if error to send the command
#-2 if range is out of limits.
def _st_range(nb, range) '{
    global ST_RANGE
    local xx xy c1 c2 tmp_r ret

    if (nb > ST_PARAMS["nb"]){
        return(0)
    }

    if (range < 1.0)
        return(ST_RANGE[nb])


    xx = int(log10(range))
    xy = int(range/pow(10,xx))

    if (xx > 9){
        return (-2)
    }

    if ((xx == 9) && (xy > 1)){
        return (-2)
    }

    if (xy<2) {
        c1 = 1
        c2 = 0
    }
    else if (xy<5) {
        c1 = 2
        c2 = 1
    }
    else {
        c1 = 5
        c2 = 2
    }

    tmp_r =  c1*pow(10,xx)
    cmd=sprintf("SENS%d\r\n",c2+xx*3)

    ret = ser_put(ST_SL[nb],cmd)

    if (ret != -1) {
        ST_RANGE[nb] = tmp_r
        ret = ST_RANGE[nb]
    }

    return(ret)
}'


######################################################################
##############################          ##############################
##############################  OFFSET  ##############################
##############################          ##############################
######################################################################

#%UU% <nb, offset>
#%MDESC% Possible offset current level : 1,2,5,10,20,50,100, 200, 500
#pA or nA or uA and 1, 2,5 mA. The input is in pA. If nb not specified,
#all the connected instruments will be red. If offset not specified, the
#current offset will be printed.
def st_offset '{
local nb ret tmp i lnb

  nb = split("$*",tmp)
  if (nb == 0) {
    for (nb in ST_SL) {
      ret = _st_offset(nb)
      printf ("Amplifier #%d offset is %g pA\n", nb, ret)
    }
  } else {
    if (nb%3 != 0)
      lnb = 2
    else
      lnb = 3

    for (i = 0; i<nb; i+= lnb) {
      if (lnb == 2)
        ret = _st_offset(tmp[i],tmp[i+1])
      else if (lnb == 3)
        ret = _st_offset(tmp[i],tmp[i+1], tmp[i+2])
      if (ret == 0) {
        eprintf("Amplifier #%d does not exist, nothing done\n", tmp[i])
      } else if (ret >=1){
        printf ("Amplifier #%d offset set to %s \n", tmp[i], st_value_to_scale(ret))
      } else if (ret == -2) {
        eprintf("Amplifier #%d offset %g out of limits, nothing done\n", \
		tmp[i], tmp[i+1])
      } else if ((nb == 1) && (tmp[i+1] == 0)) {
          printf ("Amplifier #%d offset is %g pA\n", tmp[i], ret)
      }
    }
  }
}'


# [<nb>] <offset> <unit> (No <nb> = number 0)
# Define the offset with an associated unit
#   offset : 1,2,5,10,20,50,100,200,500 (only 1,2,5 for mA)
#   unit  : p n u m (pico nano micro milli)
def st_uoffset '{
    local _nb_param  _amp_no _unit  _offset  _unit_factor

    _nb_param = $#

    if (_nb_param <= 1){
        print "$0 usage :"
        print "   $0 [<nb>] <offset> <unit>"
        print "   offset : 1, 2, 5, 10, 20, 50, 100, 200, 500"
        print "   unit  : \"pA\" \"nA\" \"uA\" or \"mA\"  (pico nano micro milli)"
        exit
    }
    else{
        if (_nb_param == 3){
            _amp_no = "$1"
            _offset = "$2"
            _unit = "$3"
        }
        if (_nb_param == 2) {
            _amp_no = 0
            _offset = "$1"
            _unit = "$2"
        }
        if (_nb_param > 3) {
            print "$0 usage :"
            print "   $0 [<nb>] <offset> <unit>"
            print "   offset : 1, 2, 5, 10, 20, 50, 100, 200, 500"
            print "   unit  : \"pA\" \"nA\" \"uA\" or \"mA\"  (pico nano micro milli)"
            exit
        }
    }

    if (!is_number_in_range(_offset, 1, 500)){
        print "error offset must be in [1;500]"
        exit
    }

    if (_unit=="pA") {
        _unit_factor = 1
    }
    else if (_unit=="nA") {
        _unit_factor = 1e3
    }
    else if (_unit=="uA"){
        _unit_factor = 1e6
    }
    else if (_unit=="mA"){
        _unit_factor = 1e9
    }
    else{
        print "error : unit must be in \"pA\" \"nA\" \"uA\" or \"mA\" "
        exit
    }

    # print "unit factor = " _unit_factor

    eval(sprintf("st_offset %d %d", _amp_no,_offset*_unit_factor))
}'


#%UU% (nb,offset, noff)
#%MDESC% Set the offset current level for the nb amplifier. The
#offset is in pA, nb starts from 1. If the offset does not exeed 1mA but is not
#an exact Stanford offset the next lowest one is choosen. Return the current
#offset, 0 if instrument not configured, -1 if error to send the command
#-2 if offset is out of limits. If noff set, set the amplifier to uncalibrated
#input offset vernier, else set to calibrated mode.
def _st_offset(nb, offset, noff) '{
    global ST_OFFSET
    local xx xy c1 c2 tmp_r ret cmd xnoff

    if (nb > ST_PARAMS["nb"]){
        return(0)
    }

    if (offset < 1.0){
        return(ST_OFFSET[nb])
    }

    xx = int(log10(offset))
    xy = int(offset/pow(10,xx))

    if (xx > 9){
        return (-2)
    }

    if (xy<2) {
        c1 = 1
        c2 = 0
    }
    else if (xy<5) {
        c1 = 2
        c2 = 1
    }
    else {
        c1 = 5
        c2 = 2
    }

    tmp_r = c1 * pow(10, xx)
    cmd = sprintf("IOLV%d\r\n", c2 + xx*3)

    ret = ser_put(ST_SL[nb],cmd)
    if (ret == -1){
        return(ret)
    }

    ST_OFFSET[nb] = tmp_r
    ret = ST_OFFSET[nb]

    if (noff && (noff<1000 && noff>-1000)) {
        xnoff = int(noff)
        cmd = sprintf("IOUC1\r\n")
        ser_put(ST_SL[nb],cmd)
        cmd = sprintf("IOUV%d\r\n",xnoff)
        ser_put(ST_SL[nb],cmd)
    }
    else {
        cmd = sprintf("IOUC0\r\n")
        ser_put(ST_SL[nb],cmd)
    }

    return(ret)
}'


#  (nb) switch offset ON for ctrl nb
def st_offset_on '{
    local _nb_param  ret

    _nb_param = $#

    if (_nb_param < 1){
        print "$0 usage :"
        print "   $0 <nb>"
        exit
    }
    ret = ser_put(ST_SL["$1"], "IOON1\r\n")
}'


#  (nb) switch offset OFF for ctrl nb
def st_offset_off '{
    local _nb_param ret

    _nb_param = $#

    if (_nb_param < 1){
        print "$0 usage :"
        print "   $0 <nb>"
        exit
    }
    ret = ser_put(ST_SL["$1"], "IOON0\r\n")
}'


######################################################################
###############################        ###############################
###############################  BIAS  ###############################
###############################        ###############################
######################################################################

#%UU% <nb, bias>
#%MDESC% Possible bias voltage : [-5V ; +5V]
# If nb not specified, all the connected instruments will be set.
# If bias voltage not specified, the current bias voltage will be printed.
def st_bias '{
    local nb  ret  tmp[]  amplifier_index  _bias_val

    nb = split("$*", tmp)
    if (nb == 0) {
        for (nb in ST_SL) {
            ret = ST_BIAS[nb]
            printf ("Amplifier #%d bias is %g V\n", nb, ret)
        }
    }
    else if (nb == 2){
        amplifier_index = tmp[0]
        _bias_val = tmp[1]
        ret = _st_bias(amplifier_index, _bias_val)

        if(ret == 9999){
            printf("Error : cannot send command to amplifier number %d\n", amplifier_index)
        }
        if(ret == 8888){
            printf ("bias out of range : -5 ; +5V \n")
        }
        else{
            printf ("Amplifier #%d bias set to %s Volt(s) \n", amplifier_index, ret)
        }
    }
    else{
        print "error"
        print "usage : st_bias  <nb, bias>"
    }
}'


#%UU% (nb, bias)
#%MDESC% Set the bias voltage for the amplifier <nb>.
#    Returns the current bias (saved in SPEC variable).
# Returns 9999 in case of serial communication error.
def _st_bias(nb, bias, silent) '{

    global ST_BIAS
    local _bias_value cmd ret tmp_bias  _coeff

    if (nb > ST_PARAMS["nb"]){
        return(0)
    }

    if (bias < -5.0  || bias > 5.0){
        return(8888)
    }

    tmp_bias = ST_BIAS[nb]

    _coeff = 1000
    if (ST_BIAS[nb]["coeff"] != 0){
        _coeff = ST_BIAS[nb]["coeff"]
    }

    _bias_value = bias * _coeff
    cmd = sprintf("BSLV %d\r\n", _bias_value)
    if(!silent) { printf("sending : %s", cmd) }
    ret = ser_put(ST_SL[nb], cmd)
    if (ret == -1){
        return(9999)
    }

    ST_BIAS[nb] = bias
    return(ST_BIAS[nb])
}'


#    st_bias_setup  <coeff>
# with <coeff> ~1000 to adjust conversion Volts <-> SR570 units
# If not defined, uses 1000.
def st_bias_setup '{
    global ST_BIAS
    ST_BIAS[nb]["coeff"] = "$1"
}'

#  switch bias ON for ctrl 0
def st_bias_on '{
     local ret
     ret = ser_put(ST_SL[0], "BSON1\r\n")
}'

#  switch bias OFF for ctrl 0
def st_bias_off '{
    local ret
    ret = ser_put(ST_SL[0], "BSON0\r\n")
}'


#%IU%()
#%MDESC%
# Macro motor implementation
# 
def SR570_config(num, type, p1, p2, p3) '{
    global ST_SL ST_PARAMS

    # p1==controller index p2==number of motors supported
    if(type=="ctrl") {
        if(!(ST_PARAMS["nb"] > 0)) {
            printf("SR570: ERROR: missing setup! Hint: use \"st_setup\"\n")
            return ".error."
        }
        printf("Using Stanford SR570 macro motor controller\n")
    }

    # p1==unit p2==module p3==channel
    if(type=="mot") {
        local cha mne

        cha = motor_par(num, "channel")
        mne = motor_mne(num)
        if(ST_SL[cha] == 0) {
            printf("SR570: ERROR: wrong channel for motor \"%s\"\n", mne)
            return ".error."
        }
        printf("Using motor \"%s\" on Stanford SR570 amplifier #%d\n", mne, cha)
    }
 
}'

#%IU%()
#%MDESC%
# Macro motor implementation
# 
def SR570_cmd(num, key, p1, p2) '{
    global ST_BIAS
    local  mne cha

    if(num == "..") { 
        return
    }

    mne = motor_mne(num)
    cha = motor_par(num, "channel")

    # return the current motor position in mm or deg
    if (key == "position") {
        return ST_BIAS[cha]
    }

    # start a motion (p1==abs pos, p2==rel pos, with pos in mm or deg)
    if (key == "start_one") {
        local ret
        ret = _st_bias(cha, p1, 1)
        if(ret == 9999) {
            printf("SR570: ERROR: setting bias on \"%s\"\n", mne)
        }
        if(ret == 8888) {
            printf("SR570: ERROR: bias out of range on \"%s\"\n", mne)
        }
    }
}'

#%MACROS%
#%IMACROS%
#%TOC%
#%AUTHOR% A.Beteva/BLISS%BR%
#$Revision: 1.7 $, $Date: 2018/06/14 07:04:36 $
#%END%
#%LOG%
#$Log: stanford_SR570.mac,v $
#Revision 1.7  2018/06/14 07:04:36  guilloud
#declaration of local variables...
#
#Revision 1.6  2017/09/12 09:45:21  perez
#Add minimum macro motor for bias control
#
#Revision 1.5  2017/02/07 09:30:01  guilloud
#comments/error messages
#add BIAS support (on/OFF / set / coefficient)
#
#Revision 1.4  2017/01/31 14:20:57  ohlsson
#made sure one can use more than one SR570
#
#Revision 1.3  2016/12/01 12:59:26  guilloud
#typo in file name
#
#Revision 1.2  2016/12/01 12:57:09  guilloud
#Displays in a user friendly manner (1..500 with uA pA nA mA)
#st_uoffset to define offset
#switch offset ON / OFF functions
#
#Revision 1.1  2011/09/07 07:55:26  beteva
#Initial revision