esrf

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

#%TITLE% par263.mac $Revision: 1.8 $
#%NAME%
#   Princeton Applied Research Potentiostat/Galvanostat model 263A and
#   model VersaStatII 2532
#%DESCRIPTION%
#%SETUP%
#%END%
#
#%HISTORY%
#$Log: par263.mac,v $
#Revision 1.8  2017/04/28 14:51:25  ohlsson
#Mostly refactoring and making sure the 1.8 is the new version for BM08
#
#Revision 1.7  2016/07/05 14:09:02  witsch
#Francesco Carla`s additions.
#
#Revision 1.6  2016/07/01 11:51:29  witsch
#Allow to use two optional counters for potential and instantaneous current.
#some formatting.
#
#Revision 1.5  2012/05/14 15:03:17  claustre
#added blmenu, better hwd handchecking at setup, pct macro
#
#Revision 1.4  2011/10/10 12:34:31  claustre
#Just removed a debug print in _par263_waitcurve()
#
#Revision 1.3  2011/08/30 15:02:44  claustre
#New version copied from ID32 and ID03
#
#Revision 1.1  2006/03/14 08:41:28  perez
#Add vertex support to initial revison done by EP
#
#
#%END%

global PAR263_MODE[]
global PAR263_ESET
global PAR263_PAR[]

PAR263_MODE["0"]= "Unknown"
PAR263_MODE["1"]= "Galvanostat"
PAR263_MODE["2"]= "Potentiostat"


#%IU% (msg, err)
def _par263_print(msg, err) '{
    tty_cntl("md")
    printf("PAR263 %s> ", err?"ERROR":"")
    tty_cntl("me")
    print msg
}'


#%UU% <gpib_addr> <potential_mne> <current_cnt>
#%MDESC% Global setup. The potential unit will be mV and the current unit
# will be A (use the scale factor in config to change the sign and the scale)
#
def par263setup '{
    global PAR263_ADDR
    global PAR263_ANS
    global PAR263_EMOT
    global PAR263_ICNT
    global PAR263_ECNT
    global PAR263_PCNT
    global PAR263_IICNT

    global PAR263_TMB
    local st

    if ( "$#" < 3 ) {
        print "$0 <gpib_addr> <potential_mot_mne> <current_cnt_mne> [<potenial_cnt_mne> <instantaneous_current_cnt_mne>]"
        exit
    }

    PAR263_ADDR= "$1"
    PAR263_EMOT= "$2"
    PAR263_ICNT= "$3"
    PAR263_PCNT= "$4"
    PAR263_IICNT= "$5"

    st= 0

    # --- check device on gpib
    st= _par263_checkid()
    if (!st) { 
        _par263_print(sprintf("No PAR 263A controller at address <%s>", PAR263_ADDR), 1)
        return
    } else {
       _par263_print(sprintf("Using controller at address <%s>", PAR263_ADDR))
    }

    # --- set Data Delimiter charater used to parse answers
    if (st) {
        st= _par263_write("DD 44")
        if (!st) _par263_print("Cannot set data delimiter", 1)
    }


    # --- set potentiostat mode
    if (st) {
        st= _par263_write("MODE 2")
        if (!st) _par263_print("Cannot set mode to potentiostat", 1)
    }

    # --- check pseudos
    if (st && motor_num(PAR263_EMOT)==-1) {
        _par263_print(sprintf("<%s> is not a motor", PAR263_EMOT))
        st= 0
    }
    if (st && cnt_num(PAR263_ICNT)==-1) {
        _par263_print(sprintf("<%s> is not a counter", PAR263_ICNT))
        st= 0
    }

    PAR263_PAR["setup"] = (st)?1:0
    PAR263_PAR["autorange"]=0

    # --- default timebase
    if (whatis("PAR263_TMB") & 0x8000000) {
        PAR263_TMB= 50000
    }

    blmenuadd("PAR263 Potentiostat control","","par263_body","_par263_")

    par263on

    setup_tail("par263")
}'


#%UU%
#%MDESC
# count with autorange on
def pct '{
    PAR263_PAR["autorange"]=1
        cdef("cleanup_once", "PAR263_PAR[\"autorange\"]=0;","par263")
    ct $*
        PAR263_PAR["autorange"]=0
}'


#%IU%
#%MDESC% body macro for blmenu
def par263_body(mode) '{
        if (mode == 1) {
                if (PAR263_PAR["on"]) par263off
                else par263on
        }
        return ((PAR263_PAR["on"])?"On":"Off")
}'


#%UU%
#%MDESC%
def par263unsetup '{
    par263off
}'


#%UU%
#%MDESC% Activates pseudo motor/counters
def par263on '{
        # check if the setup is done
        if (PAR263_PAR["setup"]) {
            cdef("user_checkall", "_par263_checkall;", PAR263_EMOT, 0x01)
            cdef("user_getpangles", "_par263_getpangles;", PAR263_EMOT, 0x01)
            cdef("user_prepcount", "_par263_prepcount;", PAR263_ICNT, 0x02)
            cdef("user_getcounts", "_par263_getIcounts;", PAR263_ICNT, 0x02)
            cdef("user_getcounts", "_par263_getPcounts;", PAR263_PCNT, 0x02)
            cdef("user_getcounts", "_par263_getinstantaneousIcounts;", PAR263_IICNT, 0x02)
            cdef("user_countersrun","if (_par263_waitcurve()) return (1);", PAR263_ICNT, 0x02)
        } else {
            _par263_print("par263 not setup properly, called first par263setup !!", 1)
            par263off
            exit
        }
        PAR263_PAR["on"] = 1
}'


#%UU%
#%MDESC% De-activates pseudo motor/counters
def par263off '{
    cdef("", "", PAR263_EMOT, "delete")
    cdef("", "", PAR263_ICNT, "delete")
    PAR263_PAR["on"] = 0
}'


#%UU% [<time_in_sec>]
#%MDESC% Test curve acquisition
def par263acq '{
    local tps __s[] __n
    __s[0]= "|\r"; __s[1]= "/\r"; __s[2]= "-\r"; __s[3]= "\\\r"
    __n= 0

    tps= $1?$1:1

    _par263_print(sprintf("Starting curve for %.2f sec", tps))
    if (!_par263_startcurve(tps)) {
        _par263_print("Cannot start curve")
        exit
    }
    _par263_print("Waiting curve to finish")
    while (_par263_waitcurve()) {
        printf(__s[__n++%4])
        sleep(0.1)
    }
    _par263_print("Reading curve")
    if (!_par263_readcurve()) {
        _par263_print("Cannot read curve")
        exit
    }
    print
    par263report
}'


#%IU% (acquisition_time)
#%MDESC% Start curve acquisition
def _par263_startcurve(tps) '{
    global PAR263_READ[]
    global PAR263_RUN
    local _np

    PAR263_RUN= 0

    # Set auto range on I and E (needed on VersaStatII because there is
    # no front panel switch for that)
    if (PAR263_PAR["autorange"]) {
        if (!_par263_write("AR 3")) return (0)

        #if (!_par263_write("AR 2")) return (0)
        #if (!_par263_write("I/E -6")) return (0)
    }

    if (!_par263_write("FLT 1")) return (0)

    if (!_par263_write(sprintf("TMB %d", PAR263_TMB))) return (0)
    PAR263_READ["tmb"]= PAR263_TMB

    _np= int(tps*1000000 / PAR263_READ["tmb"])
    if (!_par263_write(sprintf("LP %d", _np))) return (0)
    PAR263_READ["np"]= _np

    if (!_par263_io("LP")) return (0)
    if (PAR263_ANS!=_np) return (0)

    sleep (0.1)
    if (!_par263_write("NC")) return (0)
    if (!_par263_write("TC")) return (0)

    cdef("cleanup_once", "_par263_cleanup;", "_p263_")

    PAR263_RUN= 1
    return (1)
}'


#%IU% ()
#%MDESC% Return 1 if curve still in acquisition
def _par263_waitcurve() '{
    local _ns _as _st

    if (!_par263_io("MON")) {
        _par263_print("Cannot get curve acquisition status", 1)
        return (0)
    }
    else {
        _ns= split(PAR263_ANS, _as, ",")
        _st= int(_as[0])
        PAR263_READ["lp"]= int(_as[2])
        PAR263_RUN= _st

            _par263_io(sprintf("DP %d",PAR263_READ["lp"]),0.1)
        return (_st)
    }
}'


#%IU% ()
#%MDESC% Stop a curve acquisition
def _par263_stopcurve() '{
    if (!_par263_write("HC")) return (0)
    if (!_par263_write("NC")) return (0)
    PAR263_RUN= 0
    return (1)
}'


#%IU% ()
#%MDESC% Read curve minimum, maximum, integral
def _par263_readcurve() '{
    local _ns _as

    if (!_par263_io("IMIN", 0.05)) return (0)
    _ns= split(PAR263_ANS, _as, ",")
    PAR263_READ["imin"]= int(_as[0])
    PAR263_READ["vmin"]= int(_as[1]) * pow(10, int(_as[2]))

    if (!_par263_io("IMAX", 0.05)) return (0)
    _ns= split(PAR263_ANS, _as, ",")
    PAR263_READ["imax"]= int(_as[0])
    PAR263_READ["vmax"]= int(_as[1]) * pow(10, int(_as[2]))

    if (!_par263_io("IINT", 0.05)) return (0)
    _ns= split(PAR263_ANS, _as, ",")
    PAR263_READ["int"]= int(_as[0]) * pow(10, int(_as[1]))

    if (PAR263_READ["lp"])
        PAR263_READ["avg"]= PAR263_READ["int"]/PAR263_READ["lp"]
    else    PAR263_READ["avg"]= 0.

    return (1)
}'


#%UU%
#%MDESC% Report information on last curve acquired
def par263report '{
    tty_cntl("md"); printf("Curve Parameters:\n"); tty_cntl("me")
    printf(" - TimeBase        = %d\n", PAR263_READ["tmb"])
    printf(" - PointsRequested = %d\n", PAR263_READ["np"])
    printf(" - PointsRead      = %d\n", PAR263_READ["lp"])

    tty_cntl("md"); printf("Curve results:\n"); tty_cntl("me")
    printf(" - MINIMUM = %g (at point %d)\n",PAR263_READ["vmin"], PAR263_READ["imin"])
    printf(" - MAXIMUM = %g (at point %d)\n",PAR263_READ["vmax"], PAR263_READ["imax"])
    printf(" - INTEGRAL= %g\n", PAR263_READ["int"])
    printf(" - AVERAGE = %g\n", PAR263_READ["avg"])
}'


#%UU% [<mode>]
#%MDESC% Without <mode>, report the current controller mode
#%BR% With <mode> specified, change the mode
#%BR% Possible modes are:
#%BR% (1) Galvanostat
#%BR% (2) Potentiostat
def par263mode '{
    if (!$#) {
        if (_par263_io("MODE")) {
            _par263_print(sprintf("Mode is %s", PAR263_MODE[PAR263_ANS]))
        }
    }
    else {
        mode= int($1)
        if (mode!=1 && mode!=2) {
            print "PAR263 Modes can be:"
            print "1- "PAR263_MODE[1]
            print "2- "PAR263_MODE[2]
        }
        else {
            _par263_write(sprintf("MODE %d", mode))
            if (_par263_io("MODE")) { 
                _par263_print(sprintf("Mode is %s", PAR263_MODE[PAR263_ANS]))
            }
        }
    }
}'


#%UU%
#%MDESC% Read 10 I samples and return average
def par263readI '{
    local _ns _as _v
    if (_par263_io("READI")) {
        _ns= split(PAR263_ANS, _as, ",")
        if (_ns!=2) {
            _par263_print("Got wrong answer from controller", 1)
            _par263_print(PAR263_ANS, 1)
        }
        else {
            _v= _as[0] * pow(10, _as[1])
            _par263_print(sprintf("Current = %g A", _v))
        }
    }
}'


#%UU%
#%MDESC% Read 10 E samples and return average
def par263readE '{
    local _v
    if (_par263_io("READE")) {
        _v= int(PAR263_ANS)
        _par263_print(sprintf("Potential = %g mV", _v))
    }
}'


#%UU% [<set_potential>]
#%MDESC% Read/Write set potential  (unit=mV)
def par263setE '{
    local _v
    if ($#!=1) {
        if (_par263_io("SETE")) {
            _par263_print(sprintf("Set Potential = %d mV", PAR263_ANS))
        }
    }
    else {
        _v= int($1)
        if ((_v<-10000)||(_v>10000)) {
            _par263_print("Range is -10000..10000 mV", 1)
        }
        else {
            _par263_write(sprintf("SETE %d", _v))
        }
    }
}'


#%IU%
def _par263_checkall '{
    if ((PAR263_ESET!=-99999) && \
       (A[motor_num(PAR263_EMOT)]!=PAR263_ESET)) {
        if (!_par263_write(sprintf("SETE %d", A[motor_num(PAR263_EMOT)])))
            _par263_print("Cannot set potential", 1)
    }
}'


#%IU%
def _par263_getpangles '{
    if (_par263_io("SETE")) {
        PAR263_ESET= int(PAR263_ANS)
        A[motor_num(PAR263_EMOT)]= PAR263_ESET
    }
    else {
        PAR263_ESET= -99999
    }
}'


#%IU%
def _par263_getEcounts '{
    local _v
    if (_par263_io("READE")) {
        _v= int(PAR263_ANS)
        S[cnt_num(PAR263_ECNT)]= _v
    }
    else {
        S[cnt_num(PAR263_ECNT)]= -9999
    }
}'


#%IU%
def _par263_getPcounts '{
    local _v
    if (_par263_io("READE")) {
        _v= int(PAR263_ANS)
        S[cnt_num(PAR263_PCNT)]= _v
    }
    else {
        S[cnt_num(PAR263_PCNT)]= -9999
    }
}'


#%IU%
def _par263_getinstantaneousIcounts '{
    local _v
    if (_par263_io("READI")) {
        local _ns _as _v
        _ns= split(PAR263_ANS, _as, ",")
        if (_ns!=2) {
            _par263_print("Got wrong answer from controller", 1)
            _par263_print(PAR263_ANS, 1)
        }
        else {
            _v= _as[0] * pow(10, _as[1])
            _par263_print(sprintf("Current = %g A", _v))
        }

        S[cnt_num(PAR263_IICNT)]= _v
    }
    else {
        S[cnt_num(PAR263_IICNT)]= -9999
    }
}'


#%IU%
def _par263_getIcounts '{
    S[cnt_num(PAR263_ICNT)]= 0.
    if (!PAR263_RUN) {
        if (!_par263_readcurve())
            _par263_print("Cannot read curve")
        else    S[cnt_num(PAR263_ICNT)]= PAR263_READ["avg"]*counter_par(cnt_num(PAR263_ICNT),"scale")
    }
}'


#%IU%
def _par263_prepcount '{
    global PAR263_RUN
    PAR263_RUN= 0
    if (!_par263_startcurve(COUNT_TIME))
        _par263_print("Cannot start curve acquisition", 1)
    else    PAR263_RUN= 1
}'


#%IU%
def _par263_cleanup '{
    if (!_par263_stopcurve())
        _par263_print("Cannot stop curve acquisition", 1)
    else    _par263_print("Curve acquisition stopped")
}'


#%UU%
#%MDESC% Reset default controller parameters
def par263reset '{
    gpib_cntl(PAR263_ADDR, "sdc")
}'


#%UU%
#%MDESC% Report error status on last command executed
def par263error '{
    if (_par263_io("ERR")) {
        if (PAR263_ANS)
            _par263_print(sprintf("ERROR CODE = %d", PAR263_PAR))
        else    _par263_print("NO ERROR")
    }
}'


#%UU% [<0|1>]
#%MDESC% Without argument: report cell status
#%BR% With argument: 0= set cell OFF
#%BR%                1= set cell ON
def par263cell '{
    local _v
    if ($#!=1) {
        if (_par263_io("CELL")) {
            _par263_print(sprintf("Cell Status = %s", PAR263_ANS==1?"ON":"OFF"))
        }
    }
    else {
        _v= int($1)
        if (_v!=0 && _v!=1) {
            _par263_print("Parameter should be 0 (OFF) or 1 (ON)", 1)
        }
        else {
            _par263_write(sprintf("CELL %d", _v))
        }
    }
}'


#%UU% [<0|1>]
#%MDESC% Without argument, report dummy cell status
#%BR% With argument: 0= set dummy cell OFF
#%BR%                1= set dummy cell ON
def par263dummy '{
    local _v
    if ($#!=1) {
        if (_par263_io("DUMMY")) {
            _par263_print(sprintf("Dummy Cell = %s", PAR263_ANS==1?"ON":"OFF"))
        }
    }
    else {
        _v= int($1)
        if (_v!=0 && _v!=1) {
            _par263_print("Parameter should be 0 (OFF) or 1 (ON)", 1)
        }
        else {
            _par263_write(sprintf("CELL %d", _v))
        }
    }
}'


#%IU% ()
def _par263_checkid() '{
    gpib_par(PAR263_ADDR,"timeout", 20)
    if (_par263_io("ID")) {
        if ((PAR263_ANS=="2631") || (PAR263_ANS=="2532")) {
               # for huge cycle with too many data to read the controller
               # takes a while to return the data (e.g. 2000 pts takes
               # 4sec.)
               return (1)
        }
    }

    return (0)
}'


#%IU% (cmd)
def _par263_write(cmd) '{
    if (!gpib_put(PAR263_ADDR, cmd)) {
        _par263_print(sprintf("cannot send <%s> on GPIB", cmd), 1)
        return (0)
    }
        sleep(0.1)
    return (1)
}'


#%IU% (cmd)
def _par263_io(cmd, slp) '{
    if (_par263_write(cmd)) {
        if (slp) sleep(slp)
        return _par263_read(cmd)
    }
    else    return (0)
}'


#%IU% ()
def _par263_read() '{
    PAR263_ANS= gpib_get(PAR263_ADDR)
    if (!length(PAR263_ANS)) {
        _par263_print("no answer from controller", 1)
        return (0)
    }
    return (1)
}'


#%UU%
#%MDESC% Interactive session
def par263talk '{
    local ans cmd act
    _par263_talkusage
    while (1) {
        ans= input("PAR263> ")
        ans= __upper(ans)
        act= substr(ans, 0, 1)
        cmd= substr(ans, 3)
        if (act=="W") {
            _par263_write(cmd)
        }
        else if (act=="R") {
            if (length(cmd)>1) {
                _par263_write(cmd)
                sleep(0.1)
            }
            _par263_read()
            printf("PAR263> ")
            tty_cntl("md"); print PAR263_ANS; tty_cntl("me")
        }
        else if (act=="Q") {
            break
        }
        else {
            _par263_talkusage
        }
    }
}'


def _par263_talkusage '{
    print "USAGE: w <cmd> <pars> => Send <cmd> with all arguments <pars>"
    print "       r              => Read answer"
    print "       r <cmd> <pars> => Send <cmd> and read answer"
    print "       q              => Quit talk session"
    print
}'


def __upper(str) '{
    local _i _c _r
    _r= ""
    for (_i=1; _i<=length(str); _i++) {
        _c= asc(substr(str, _i, 1))
        _r= _r sprintf("%c", (_c>=0x60)?_c-0x20:_c)
    }
    return (_r)
}'


#%UU% init_pot down_pot up_pot slope ppsec
#%MDESC% Generate a single vertex from init_pot to down_pot, then up to
# up_pot and finally down to init_pot. All the potentials are in mV.
# The slope will be given in mV/sec. The parameter ppsec is the number
# of points per second
#
def par263vertex '{
    local init_pot up_pot down_pot slope ppsec
    local init_dac up_dac down_dac tmb
    local mr mr_cmd cmd
    local n1 n2 n3 points

    if($# < 5) {
        p "Usage: $0 init_pot down_pot up_pot slope ppsec [spp]"
        p "   potentials are in mV"
        p "   slope in mV/second"
        p "   spp stands for sample per point (NO EVERAGING)"
        exit
    }

    # Set MODULATION RESOLUTION to 2.5muV per count
    # mr = 0.0025 ; mr_cmd = 0
    # Set MODULATION RESOLUTION to 25muV  per count
    # mr = 0.025  ; mr_cmd = 1
    # Set MODULATION RESOLUTION to 250muV per count
    mr = 0.250  ; mr_cmd = 2

    init_pot = $1
    down_pot = $2
    up_pot   = $3
    slope    = $4
    ppsec    = $5
    spp      = 1
    if($#>5) { spp     = $6 }


    init_dac = int(init_pot/mr)
    down_dac = int(down_pot/mr)
    up_dac   = int(up_pot/mr)
    tmb      = int(1000000/(ppsec*spp))


    if((init_dac>8000) || (init_dac<-8000)) {
        _par263_print( \
        sprintf("init_dac %dmV out of range +/-%dmV",init_pot,8000*mr), 1)
        exit
    }
    if((up_dac>8000) || (up_dac<-8000)) {
        _par263_print(
          sprintf("up_dac %dmV out of range +/-%dmV", up_pot, 8000 * mr), 1)
        exit
    }
    if((down_dac>8000) || (down_dac<-8000)) {
        _par263_print( \
        sprintf("down_dac %dmV out of range +/-%dmV",down_pot,8000*mr), 1)
        exit
    }
    if((slope>200) || (slope<1)) {
        _par263_print(sprintf("slope %dmV/s out of range [1,200]",slope), 1)
        exit
    }
    if((tmb>50000) || (tmb<400)) {
        _par263_print(sprintf("ppsec %d out of range [20,2500]",ppsec), 1)
        exit
    }


    # calculate the number of points of each slope
    n1=int(fabs(down_pot - init_pot)*(ppsec/spp)/slope)
    n2=int(fabs(up_pot   - down_pot)*(ppsec/spp)/slope) + n1
    n3=int(fabs(up_pot   - init_pot)*(ppsec/spp)/slope) + n2


    # arbitrary the number of points is fixed to 1020 but the program
    # and this value may be adapted to slope implementation
    points=n3
    if(points>=3072) {
        _par263_print( \
        sprintf("too many points, decrease ppsec or increase slope",ppsec), 1)
        _par263_print(sprintf("n1=%d n2=%d n3=%d\n",n1,n2,n3), 1)
        exit
    }

    #
    # Program coming from F.RENNER and tested with the DOS program
    #
    _par263_print(sprintf("Starting curve"))

    # ??
    _par263_write("27")
    _par263_write("DCL")
    _par263_write("FLT 65")

    # Record I and E
    _par263_write("SIE 3")

    # Auto range off for I and E
    _par263_write("AR 0")

    # The destination CURVE is the 1st one
    _par263_write("DCV 0")
    _par263_write("PAM 0")
    cmd=sprintf("S/P %d",spp)
    _par263_print(cmd)
    _par263_write(cmd)

    # Ramp modulation on (INITIAL and VERTEX unit is in count and use need MR)
    _par263_write("MM 1")
    _par263_write("INTRP 1")

    # Set CURRENT TO VOLTAGE CONVERTER to 1uA
    _par263_write("I/E -6")

    # Set MODULATION RESOLUTION to 250muV per count
    cmd=sprintf("MR %d",mr_cmd)
    _par263_print(cmd)
    _par263_write(cmd)

    cmd=sprintf("TMB %d",tmb)
    _par263_print(cmd)
    _par263_write(cmd)

    cmd=sprintf("LP %d",points)
    _par263_print(cmd)
    _par263_write(cmd)

    cmd=sprintf("INITIAL  0   %d",init_dac)
    _par263_print(cmd)
    _par263_write(cmd)

    cmd=sprintf("VERTEX %d %d",n1,down_dac)
    _par263_print(cmd)
    _par263_write(cmd)

    cmd=sprintf("VERTEX %d %d",n2,up_dac)
    _par263_print(cmd)
    _par263_write(cmd)

    cmd=sprintf("VERTEX %d %d",n3,int_dac)
    _par263_print(cmd)
    _par263_write(cmd)

    _par263_write("NC")

    # Switch on the E on the CELL
    _par263_write("CELL 1")

    # Take curve i.e. starts producing E VERTEX
    _par263_write("TC")

    # Wait for the end of TC i.e. commands not treated
    _par263_print("Waiting curve to finish")
    while (_par263_waitcurve()) {
        printf("Currently accessed point: %d\r",PAR263_READ["lp"])
        sleep(0.1)
    }
    printf("Currently accessed point: %d\n",PAR263_READ["lp"])

    # Switch off the E on the CELL
    _par263_write("CELL 0")

    # Display the I/E
    _par263_print("Reading curves")
    _par263vertex_plot(points)
}'


#
#%IU%
#%MDESC% Get from the VERSASTAT the contains of its CURVE buffers and plot them
# This macro expect I in the CURVE_0 and E in CURVE_1
#
def _par263vertex_plot(n) '{
    local short array x[n]
    local short array y[n]
    local short array dat[2][n]
    local f
    local n_curve n_rd


    if(n>3071) {
        _par263_print(sprintf("%d higher than max 1024 allowed points",n), 1)
        exit
    }

    # Get I values (i.e. y axis)
    _par263_write(sprintf("BD 0 %d",n))
    n_rd = gpib_get(PAR263_ADDR,dat[1])
    if(n != n_rd) _par263_print("Error reading I values", 1);

    # Get U values (i.e. x axis)
    n_curve=1024*(int((n)/1024)+1)
    _par263_write(sprintf("BD %d %d",n_curve,n))
    n_rd = gpib_get(PAR263_ADDR,dat[0])
    if(n != n_rd) _par263_print("Error reading U values", 1);

    #
    array_op("swap",dat)

    # init plotting device
    plot_cntl("filter1,open")
    plot_cntl("erase")
    plot_cntl("lines")
    plot_range("auto","auto","auto","auto")
    plot_move(0,0,sprintf("VERSASTAT I/E %d points",n))
    array_plot(dat[0][1:n-1],dat[1][1:n-1])

    f=getval("File name to dump I/E","voltagram.dat")
    unix(sprintf("/bin/rm -f %s", f))
    on(f);offt
    print("#################################################\n# " date())
    print("# I U")
    array_dump(dat)
    print("\n\n\n\n")
    off(f);ont
    close(f)

}'


################################################
### GALVANOSTAT MACROS (francesco 07/03/2013)###
################################################


###################################
# this macro is setting the current

def par263setI '{
local gal_cur1 galvcurr gal_ran

# added to avoid running command in the wrong mode
    (_par263_io("MODE")) 
    if(PAR263_ANS==2) {_par263_print(sprintf("Wrong mode! command can not be executed, Mode is %s", PAR263_MODE[PAR263_ANS]))
    return (0)
    } 
    

    if($# != 1) {
        p "  Usage: set current in muA"
        p "  Maximum current:  100mA"
        exit
    }

gal_cur1 = $1

    if((fabs(gal_cur1)>=0.02))   {gal_ran=-10;galvcurr=(gal_cur1*10000)}
    if((fabs(gal_cur1)>=.2))     {gal_ran=-9; galvcurr=(gal_cur1*1000)}
    if((fabs(gal_cur1)>=2))      {gal_ran=-8; galvcurr=(gal_cur1*100)}
    if((fabs(gal_cur1)>=20))     {gal_ran=-7; galvcurr=(gal_cur1*10)}
    if((fabs(gal_cur1)>=200))    {gal_ran=-6; galvcurr=(gal_cur1*1)}
    if((fabs(gal_cur1)>=2000))   { gal_ran=-5; galvcurr=(gal_cur1*0.1)}
    if((fabs(gal_cur1)>=20000))  {gal_ran=-4; galvcurr=(gal_cur1*0.01)}
    if((fabs(gal_cur1)>=200000)) {p "maximum current 100mA"; exit}

  cmd=sprintf("SETI %d %d",galvcurr,gal_ran)
  _par263_write(cmd) 
  p cmd

}'

######################################
# this macro is for galvanostat setup
#%UU% <gpib_addr> <potential_cnt> <current_cnt>
#%MDESC% Global setup. The potential unit will be mV and the current unit
# will be mA (use the scale factor in config to change the sign and the scale)
#
def par263gsetup '{
	global PAR263_ADDR
	global PAR263_ANS
#	global PAR263_EMOT
	global PAR263_ICNT
        global PAR263_ECNT
        
	global PAR263_TMB
	local st

	if ($#!=3) {
		print "$0 <gpib_addr> <current_cnt_mne> <potential_cnt_mne>"
		exit
	}
        
	PAR263_ADDR= "$1"
	PAR263_ICNT= "$2"
	PAR263_ECNT= "$3"
	st= 0

	# --- check device on gpib
	st= _par263_checkid()
	if (!st)
		_par263_print(sprintf("No PAR 263A controller at address <%s>", PAR263_ADDR), 1)
	else	_par263_print(sprintf("Using controller at address <%s>", PAR263_ADDR))

	# --- set Data Delimiter charater used to parse answers
	if (st) {
		st= _par263_write("DD 44")
		if (!st) _par263_print("Cannot set data delimiter", 1)
	}


	# --- set galvanostat mode
	if (st) {
		st= _par263_write("MODE 1")
		if (!st) _par263_print("Cannot set mode to galvanostat", 1)
	}

	# --- check pseudos
#	if (st && motor_num(PAR263_EMOT)==-1) {
#		_par263_print(sprintf("<%s> is not a motor", PAR263_EMOT))
#		st= 0
#	}
	if (st && cnt_num(PAR263_ICNT)==-1) {
		_par263_print(sprintf("<%s> is not a counter", PAR263_ICNT))
		st= 0
	}
	if (st && cnt_num(PAR263_ECNT)==-1) {
		_par263_print(sprintf("<%s> is not a counter", PAR263_ECNT))
		st= 0
	}
       
        PAR263_PAR["setup"] = (st)?1:0
        PAR263_PAR["autorange"]=0 
        
	# --- default timebase
	if (whatis("PAR263_TMB") & 0x8000000) {
		PAR263_TMB= 50000
	}

#        blmenuadd("PAR263 Potentiostat control","","par263_body","_par263_")

        par263gon
        
	setup_tail("par263")
}'

#######################################
# added by francesco 07/03/2013
# activating pseudomotors/counters for galvanostat session. defines ECNT and ICNT

#%UU% 
#%MDESC% Activates pseudo motor/counters
def par263gon '{
        # check if the setup is done
        if (PAR263_PAR["setup"]) {
	    cdef("user_prepcount", "_par263_prepcount;", PAR263_ICNT, 0x02)
	    cdef("user_getcounts", "_par263_getIcounts;", PAR263_ICNT, 0x02) 
	    cdef("user_prepcount", "_par263_prepcount;", PAR263_ECNT, 0x02)
	    cdef("user_getcounts", "_par263_getEcounts;", PAR263_ECNT, 0x02)        
	    cdef("user_countersrun", "if (_par263_waitcurve()) return (1);", PAR263_ICNT, 0x02)
	    cdef("user_countersrun", "if (_par263_waitcurve()) return (1);", PAR263_ECNT, 0x02)
        } else {
            _par263_print("par263 not setup properly, called first par263setup !!", 1)
            par263off
            exit
        }
        PAR263_PAR["on"] = 1
}'


########################################
# added by francesco 07/03/2013
# de-activating pseudomotors/counters for galvanostat session. defines ECNT and ICNT
# TO BE REMOVED: if executed after par263on erase the setup for galvanostat, use enable and disable instead.

#%UU%
#%MDESC% De-activates pseudo motor/counters in galvanostat
#def par263goff '{
#	cdef("", "", PAR263_ECNT, "delete")
#	cdef("", "", PAR263_ICNT, "delete")
#        PAR263_PAR["on"] = 0
#}'


########################################





#%MACROS%
#%IMACROS%
#%TOC%
#%AUTHOR% E.Papillon