esrf

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

#%TITLE% cenpiccurs.mac
#%NAME%
#  Move all of last scan's motors to peak or center of previous scan.
#
#%CATEGORY% Scans
#
#%DESCRIPTION%
#  Move motors of last scan to peak. Should work for multi motor scans.
#  %BR%
#  In case multiple counters are used for plotting, the last one defined by
#  plotselect will be used, not the first one on the line!
#
#%LOG%
#$Revision: 1.34 $
#%BR%
#$Log: cenpiccurs.mac,v $
#Revision 1.34  2023/04/17 12:01:20  witsch
#Repair of cen and pic, where wrong positions were rendered in
#multi motor scans.
#
#A bug that kept center positions of multiple motors from being calculated
#correctly was fixed. All similar occurences, where the index for the last
#point was being programmed as [-1], where changed to [LDT] (index of the
#last point of the scan).
#
#There was confusion about how to determine the plotted counters and match
#them with their SCAN_D column. Spec's internal variables have been used
#for that in this version.
#
#Cen now has a display macro pcen.
#
#Fixed the flip/neg functions. Neg is now identical to invert and can be
#reverted running neg again.
#
#Adding helper functions by Didier Wermeille.
#
#Diff calculates the differential of all plotted counters, undiff reverts.
#Diff uses a separate data array to do its work, which makes the reversion
#easy. Just plot.
#
#No tests concerning zap scans have been made.
#
#Revision 1.33  2020/07/17 10:23:26  guilloud
#fix encore et encore...
#
#Revision 1.32  2020/07/10 09:22:50  guilloud
#debug and fix for zap.
#
#Revision 1.31  2020/07/02 08:41:12  guilloud
#adapt to SPEC version >= 6.7.0 (SCAN_D changed)
#
#Revision 1.30  2018/09/03 15:01:56  guilloud
#change for PL_Y1... needeed by spec versions > 6.7.xx
#
#Revision 1.29  2018/06/01 16:25:44  guilloud
#no more use PL_Y --->  PL_Y1 is the good variable to use for calculations...
#
#Revision 1.28  2018/06/01 15:58:59  guilloud
#added _calc_cen for ID16B
#
#Revision 1.27  2017/07/17 08:57:37  guilloud
#fix a bug in case of cpc after a d2scan.
#more debug and tests
#
#Revision 1.26  2017/06/28 13:26:11  domingue
#correct bug on macro _invert
#
#Revision 1.25  2017/06/28 11:57:30  domingue
#correct bug with PL_A[0][0][0] in _cen macro. replace it by PL_A[0][0]
#
#Revision 1.24  2017/05/04 09:12:48  berruyer
#_pic
#correction in _pic: remove the test on the first motor (_d array does not exists anymore
#correction in _cen: replace _s and _f array by SCAN_D (@PLA_A)
#
#Revision 1.23  2017/02/21 13:07:33  guilloud
#fix missing point from ZAPARRAY to plot correctly.
#
#Revision 1.22  2017/02/14 14:31:43  guilloud
#+ diff and invert (both also for zap)
#fix plotting problems
#+ cpc_test (with genescan.mac)
#
#Revision 1.21  2017/02/07 08:57:52  guilloud
#unification of zap and stepscans (because of new splot ?)
#/
#
#Revision 1.20  2016/04/04 11:35:50  witsch
#Change numbers to be compared with _stype:
#    # for spec younger from spec6.01 on, _stype is being composed of constant variables.
#    # constant scanType_MotorScan  = 0x1      # motor scan
#    # ascan ors the number of motors shifted left by 8 (|_nm<<8)
#    # was:     if (_stype == 257 || _stype == 513) {
#    # 257 0x101 --> motor scan with one motor
#    # 512 0x201 --> motor scan with two motors
#    if (_stype & scanType_MotorScan) {.
#This should work with the newer splot and the recent spec versions.
#
#Revision 1.19  2016/02/08 12:03:22  guilloud
#+debugs
#zapimage/multi preotection
#
#Revision 1.18  2015/11/04 12:26:50  guilloud
#fix bug in scan detection (zap or not)
#fix bug in keys management.
#
#Revision 1.17  2015/10/14 13:33:34  guilloud
#neg command from bm23
#"g" action in curs (from bm23)
#
#Revision 1.16  2015/10/14 13:25:05  guilloud
#unification of zap and non-zap commands : just use cen for ascan and zapline :)
#
#Revision 1.15  2015/04/22 12:05:31  guilloud
#fix number of motors in case of the last scan before a zap was a d2scan...
#
#Revision 1.14  2015/04/22 08:37:16  guilloud
#fix plotselect recovery after zxxx
#prevent from zxxx after ascan/dscan
#prevent from xxx after zapscan
#fix plotting
#
#Revision 1.13  2015/03/26 13:50:44  guilloud
#adaptation for new SCAN_D array format.
#
#Revision 1.12  2014/12/04 12:55:54  guilloud
#*fix bug of moving a non desired motor with zcen/zpic... (if done after a d2scan)
#
#Revision 1.11  2011/06/28 06:47:58  witsch
#hklscans need to be excluded.
#
#Revision 1.10  2009/11/13 15:03:03  guilloud
#add a waitmove; get_angles after move_em to be sur that long movements really end.
#
#Revision 1.9  2009/06/17 07:01:25  witsch
#Adaption for the use of x[ad]scan, with which the macro pic miscalculated
#the position. The position for the first motor is now taken from the SCAN_D
#array and the others are calculated as before. Tested on ID03/surf5.
#
#Revision 1.7  2008/11/05 13:41:37  guilloud
#bug in loc.var. l60: peak -> _peak...
#
#Revision 1.6  2008/10/31 14:18:37  guilloud
#Add macros do be able to use cen pic diff with a zap scan.
#
#Revision 1.4  2008/02/27 13:22:15  rey
#documentation changes
#
#Revision 1.3  2007/05/09 15:43:01  witsch
#bad message when using cen. Also added counter used for calculation in
#message, so that printouts are clearer.
#
#Revision 1.2  2007/04/16 13:42:17  witsch
#Name change from cenpicall.mac to cenpiccurs.mac.
#Also replace the new macros with the old names pic and cen. Place
#aliases for the recently used names cenall and picall.
#Add Revision and Log RCS tags.
#
#%END%

need hg
hg_generate("cpc")
hg_config_style("cpc", 1, 2, 3)

#%UU%
#%DESC% Move motor(s) to last scan`s center between half height points.
def cen '{
    local i, cenidx, pts, _center
    local mne_used_cnt idx_used_cnt
    local tmp[]
    cpc_dbg("cen")
    cpc_init()

    if (!(_stype & scanType_MeshScan) && !(_stype & scanType_HKL_Scan) && _stype != 0) {
        cpc_dbg("_cen : Not a mesh.")
        cpc_dbg(sprintf("_cen : CEN=%g", CEN))

        idx_used_cnt = cnt_num(PLOT_SEL["0"])
        mne_used_cnt = cnt_mne(idx_used_cnt)
        print "Using counter", mne_used_cnt, "(idx=", idx_used_cnt, ")  ", "with scan", SCAN_N ":", HEADING

        waitmove; get_angles
        pos = _calc_cen()

        for (i=0; i < _nm; i++) {
            printf("Moving \"%s\" motor from %9.4f to the CENTER (pos=%9.4f) of last scan\n", \
               motor_mne(_m[i]), A[_m[i]], pos[i])
            A[_m[i]] = pos[i]
            CPC_PAR["last_cen"] = pos[i]
        }
        _plot_line(CEN, pl_MIN, pl_MAX)
        move_em
        waitmove; get_angles
    }
    else {
        if (_stype == 0) {
            eprint "Sorry, no scan to work on yet!"
        } else {
            eprint "Sorry, cannot use this macro after this type of scan! Aborted"
            exit
        }
    }
    cpc_restore()
}'

#%UU%
#%DESC% print motor(`s) position(s) of last scan`s center between half height points.
def pcen '{
    local i, cenidx, pts, _center
    local mne_used_cnt idx_used_cnt
    local tmp[]
    cpc_dbg("cen")
    cpc_init()

    if (!(_stype & scanType_MeshScan) && !(_stype & scanType_HKL_Scan) && _stype != 0) {
        cpc_dbg("_cen : Not a mesh.")
        cpc_dbg(sprintf("_cen : CEN=%g", CEN))

        idx_used_cnt = cnt_num(PLOT_SEL["0"])
        mne_used_cnt = cnt_mne(idx_used_cnt)
        print "Using counter", mne_used_cnt, "(idx=", idx_used_cnt, ")  ", "with scan", SCAN_N ":", HEADING

        waitmove; get_angles
        pos = _calc_cen()

        for (i=0; i < _nm; i++) {
            printf("Center of last scan for \"%s\" motor is %9.4f\n", \
               motor_mne(_m[i]), pos[i])
        }
        _plot_line(CEN, pl_MIN, pl_MAX)
    }
    else {
        if (_stype == 0) {
            eprint "Sorry, no scan to work on yet!"
        } else {
            eprint "Sorry, cannot use this macro after this type of scan! Aborted"
            exit
        }
    }
    cpc_restore()
}'





def pic '{
    cpc_init()
    _pic
    cpc_restore()
}'

def curs '{
    cpc_init()
    _curs
    cpc_restore()
}'

def diff '{
    cpc_init()
    _diff
    cpc_restore()
}'

def invert '{
    cpc_init()
    _invert
    cpc_restore()
}'

def uninvert '{
    neg
}'




rdef peak    'pic'
rdef picall  'pic'
rdef peakall 'pic'
rdef cenall  'cen'
rdef zcen    'cen'
rdef zpic    'pic'
rdef zcurs   'curs'


#     PL_Y  - comma-separated list of counter numbers to plot    ----> no more use it !!!
#     PL_YS - space-separated list counter mnemonics to plot
#     PL_Y1 - column in SCAN_D used in analysis info strings
#             - takes number of ploted motor (PLOT_MOTS) into acount
#             - takes number of disabled counters  into acount  (UPDATED in scan)
#
# 94.CYRIL> plotselect toto g1
# 98.CYRIL> p PL_Y
# 5,4
# 99.CYRIL> p PL_Y1
#  6
# 103.CYRIL> p toto "  " g1
# 5  4


# def cpc_plotted_counter_mne() '{
#     local mne_to_plot
#
#     global AA[]
#     AA[0]=0
#     split(PL_YS, AA)
#     mne_to_plot= AA[0]
#
#     return mne_to_plot
# }'


def cpc_init() '{

    local _dbg_msg

    _dbg_msg = sprintf("cpc_init() : PL_Y1=%s", PL_Y1)
    cpc_dbg(_dbg_msg)

    if (!spec_version_newer_than(6, 1, 0)) {
        print " please upgrade SPEC !!"
        exit
    }

    # for spec younger from spec6.01 on, _stype is being composed of constant variables.
    # constant scanType_MotorScan  = 0x1      # motor scan
    # ascan ors the number of motors shifted left by 8 (|_nm<<8)
    # was:     if (_stype == 257 || _stype == 513) {
    # 257 0x101 --> motor scan with one motor
    # 512 0x201 --> motor scan with two motors
    if (_stype & scanType_MotorScan) {
        # ok
        cpc_dbg("cpc_init() : ascan or a2scan")
        CPC_PAR["zapscan"]   = 0
        CPC_PAR["step_scan"] = 1
        # information available but not used:
        CPC_PAR["num_mots"]  = (_stype >> 8) & 0x7
        CPC_PAR["WithGeo"]   = _stype & scanType_WithGeo
    }
    else if(_stype == 320 ) {
        # ok
        cpc_dbg("cpc_init() :  zapscan ")
        CPC_PAR["zapscan"] = 1
        CPC_PAR["zapscan"]["type"] = "zapline"
        CPC_PAR["step_scan"] = 0

        cpc_zap_init
    }
    else if( _stype == 576 ) {
        # ok
        cpc_dbg("cpc_init() :  zapscan ")
        CPC_PAR["zapscan"] = 1
        CPC_PAR["zapscan"]["type"] = "zapimage"
        CPC_PAR["step_scan"] = 0

        cpc_zap_init
    }
    else if( _stype == 832 ) {
        # ok
        cpc_dbg("cpc_init() :  zapscan ")
        CPC_PAR["zapscan"] = 1
        CPC_PAR["zapscan"]["type"] = "zapmultimage"
        CPC_PAR["step_scan"] = 0

        cpc_zap_init
    }
    else {
        # not ok :(
        print "cpc_init : _stype=", _stype
        print "cpc_init : ERROR : Previous scan was not a ascan/dscan nor a zapscan -> exit"
        CPC_PAR["zapscan"] = 0
        CPC_PAR["step_scan"] = 0
        exit
    }
}'


def cpc_restore() '{
    cpc_dbg("cpc_restore() :   ")
    if(CPC_PAR["zapscan"]) {
        cpc_zap_restore
    }
}'


#%UU%
#%DESC% Moves motors of last scan to peak.
def _pic '{
    local i, i_at_max, pts
    local _question
    local _peak
    local mne_used_cnt idx_used_cnt
    cpc_dbg("_pic")

    if (!(_stype & scanType_MeshScan) && !(_stype & scanType_HKL_Scan) && _stype != 0) {
        cpc_dbg("_pic: Not a mesh nor a HKL scan")

        i_at_max = array_op("row_at_max", @PL_A[0:LDT][PL_Y1])
        cpc_dbg(sprintf("_pic: PL_Y1=%s i_at_max=%g", PL_Y1, i_at_max))

        idx_used_cnt = cnt_num(PLOT_SEL["0"])
        mne_used_cnt = cnt_mne(idx_used_cnt)
        print "Using counter", mne_used_cnt, "(idx=", idx_used_cnt, ")  ", "with scan", SCAN_N ":", HEADING

        waitmove; get_angles

        for (i=0; i < _nm; i++) {
            _peak = @PL_A[i_at_max][i]

            printf("Moving motor \"%s\" from %9.4f to the PEAK (pos=%9.4f val=%g) of last scan\n", \
                   motor_mne(_m[i]), A[_m[i]], _peak, pl_MAX)

            A[_m[i]] = _peak
            CPC_PAR["last_pic"] = _peak
        }
        _plot_line(pl_xMAX, pl_MIN, pl_MAX)
        move_em
        waitmove; get_angles
    }
    else {
        if (_stype == 0) {
            eprint "Sorry, no scan to work on yet!"
            exit
        } else {
            eprint "Sorry, cannot use this macro after this type of scan! Aborted"
            exit
        }
    }
}'


#%UU%
#%DESC% return motor position(s) of last scan`s center between half height points.
def _calc_cen() '{
    local i, _center, fact_at_cen
    local tmp[]

    fact_at_cen = (CEN - @PL_A[0][0]) / (@PL_A[LDT][0] - @PL_A[0][0])
    cpc_dbg(sprintf("_calc_cen: fact_at_cen %f", fact_at_cen))

    for (i=0; i < _nm; i++) {
        _center = @PL_A[0][i] + fact_at_cen * (@PL_A[LDT][i] - @PL_A[0][i])
        cpc_dbg(sprintf("_calc_cen: motor %d = %f", i,  _center))
        tmp[i] = _center
    }
    return tmp
}'

#%UU%
#%DESC% Draws a line in the plot of the last (motor) scan at the actual position.
def wcen '{
    cpc_dbg("wcen")

    _plot_line(A[_m[0]], pl_MIN, pl_MAX)
    print "Motor ", motor_mne(_m[0]), "is at ", A[_m[0]]
}'


def neg '{
    local ii  __bar

    cpc_dbg("neg")

    for (i = 0; i < PLOT_NUM; i++) {
        cnt_idx = PL_Y1 + i
        SCAN_D[][cnt_idx] *= -1
    }

    _plot_line(SCAN_D[LDT][pl_x], SCAN_D[LDT][PL_Y1]+__bar, \
               SCAN_D[LDT][PL_Y1]-__bar )
}'


def cenneg '{
    cpc_dbg("cenneg")
    neg
    cen
}'


######################################################################
#############################            #############################
#############################  PLOTTING  #############################
#############################            #############################
######################################################################

#%IU% (x, y1, y2)
#%MDESC%
# Draw a vertical red line from (x,y1) to (x,y2).
def _plot_line(x, y1, y2) '{
    float array cen_line[2][2]

    cpc_dbg(sprintf("_plot_line: (x=%s, y1=%s, y2=%s)", x, y1, y2))

    cen_line[0][0] = x; cen_line[1][0] = x
    cen_line[0][1]= y1; cen_line[1][1] = y2

    cpc_plot()

    plot_cntl("addline")
    plot_cntl("colors=::::3")
    array_plot(cen_line)
}'


def cpc_plot() '{
    local _to_plot  _zap_plot_mne

    # _to_plot = cpc_plotted_counter_mne()
    _to_plot = PLOT_SEL[0]

    if (CPC_PAR["zapscan"]) {
        cpc_dbg("cpc_plot() : zapscan")

        _zap_plot_mne = ZAP_PLOT["Y"]
        if (!index(T_L , "Zap" )) {
            T_L = sprintf("Zap%s cnt=%s" , T_L , _zap_plot_mne)
        }
    }
    else {
        cpc_dbg("cpc_plot() : step scan")
    }
    splot
}'

#%UU%
#%MDESC% Draws a little red cursor and allow user to move it with + or - keys.
def _curs '{
    local __i  _key  __bar  __mystr

    cpc_dbg("_curs")

    # Draw the cursor.
    _key = "+"
    __i = 0
    __bar = (pl_MAX-pl_MIN)*.05

    _plot_line(SCAN_D[__i][pl_x], SCAN_D[__i][PL_Y1]+__bar,     \
               SCAN_D[__i][PL_Y1]-__bar )

    for (;;) {
        # Wait for a key to be pressed until q or ctrl-c pressed.
        printf("X = %.3f --> ", SCAN_D[__i][pl_x])
        _key = getval("which way(+/-)", _key)

        if (_key == "q") {
            exit
        }
        else if (_key == "+") {
            __i++
        }
        else if (_key == "-") {
            __i--
        }
        else if (_key == "g") {
            __mystr = sprintf("umv %s %s", motor_mne(_m[0]), SCAN_D[__i][pl_x])
            eval(__mystr)
            exit
        }
        else {
            print "what ??"
        }

        # Check the limits.
        if (__i < 0) { __i = 0; beep}
        if (__i >= NPTS) {__i = NPTS-1; beep}

        # Redraw the cursor.
        cpc_dbg(sprintf( "curs: plot_line %s %s %s", SCAN_D[__i][pl_x], SCAN_D[__i][PL_Y1]+__bar, SCAN_D[__i][PL_Y1]-__bar))
        _plot_line (SCAN_D[__i][pl_x],           \
                    SCAN_D[__i][PL_Y1]+__bar,    \
                    SCAN_D[__i][PL_Y1]-__bar)

    }
}'



######################################################################
############################               ###########################
############################  ZAP VERSION  ###########################
############################               ###########################
######################################################################


# cen pic curs for zap.
# The principle is to copy ZAP data into SCAN_D and then to use
# classical cenpicurs macros. It is not very elegant but...


# Fill SCAN_D with data from last zapline scan.
def cpc_zap_init '{
    global HEADING

    global T_L

    local cpc_tab
    local cpc_zap_array
    local zap_point_number
    local aaaa[]
    local ii
    local cnt_index

    cpc_dbg("cpc_zap_init")

    if (CPC_PAR["zapscan"]["type"] == "zapmultimage") {
        print "arg pas encore les zapmultimage ??"
        exit
    }

    if (CPC_PAR["zapscan"]["type"] == "zapimage") {
        print "arg pas encore les zapimage ?"
        exit
    }

    global CPC_PAR

    waitmove

    # Calculates the number of points to plot.
    zap_point_number = ZAP_PAR["fast"]["nbp"]
    cpc_dbg(sprintf("%d zap points to plot", zap_point_number))


    cnt_index = cnt_num(PLOT_SEL[0])

    # We copy the X data of the zap scan into SCAN_D[ii][0]
    # and Y zap data into column of currently plotted counter: SCAN_D[cnt_index][0]
    cpc_dbg(sprintf("copy X zap data in SDCAN_D[:][0]"))
    cpc_dbg(sprintf("copy Y zap data in SDCAN_D[%d][0]", cnt_index))

# X
    for (ii=0; ii<zap_point_number; ii++) {
        SCAN_D[ii][0] = ZAP_PLOTARR[0][ii]
    }
    # plot need 1 point more...
    SCAN_D[zap_point_number][0] = 2*ZAP_PLOTARR[0][zap_point_number-1] - ZAP_PLOTARR[0][zap_point_number-2]

# Y
    for (ii=0 ; ii<zap_point_number ; ii++) {
        SCAN_D[ii][cnt_index] = ZAP_PLOTARR[1][ii]
    }
    # plot need 1 point more...
    SCAN_D[zap_point_number][cnt_index] = ZAP_PLOTARR[1][zap_point_number-1]


    if(list_getpar(HG_PAR, "cpc", "debug_on")) {
        print "X"
        print SCAN_D[0:zap_point_number][0]
        print "Y"
        print SCAN_D[0:zap_point_number][PLOT_MOTS+2]
    }

    # saves to restore plotselections
    CPC_PAR["prev_plot_X"] = PL_X
    CPC_PAR["prev_plot_Y1"] = PL_Y1
    CPC_PAR["prev_plot_PLOT_NUM"] = PLOT_NUM

    # ??
    PL_X = 0

    # ??
    PL_Y1 = cnt_index

    # plot only 1 counter ???
    PLOT_NUM = 1

    # ??
    pl_y  = PLOT_MOTS
    LDT   = zap_point_number
    NPTS  = zap_point_number + 1
    plot_range(0, zap_point_number, 0, "auto")


#    # copy ZAP counter mne ...
#    PLOT_SEL[0] = ZAP_PLOT["Y"]

    # axis labels.
    X_L = ZAP_PLOT["X"]
    Y_L = ZAP_PLOT["Y"]

    #  _ascan scans the number of motors given by _nm.
    #  The arrays _m[], _s[], _f[] and _d[] contain the motor
    #  numbers, start, finish and step sizes for each of the
    #  scanned motors.

    # print "# motor number"
    _m[0] = ZAP_PAR["fast"]["mne"]

    # only 1 motor in zapline
    delete _m[1]
    _nm = 1

    # start
    _s[0] = ZAP_PAR["fast"]["start"]
    # finish
    _f[0] = ZAP_PAR["fast"]["stop"]
    # step size
    _d[0] = (_f[0] - _s[0]) / ZAP_PAR["fast"]["nbp"]

    T_L = sprintf("Scan %d", SCAN_N)

    # print "# to change ?"
    HEADING = "zap scan"
}'


# restore ...
def cpc_zap_restore '{
    cpc_dbg("cpc_zap_restore")

    PL_X  = CPC_PAR["prev_plot_X"]
    PL_Y1 = CPC_PAR["prev_plot_Y1"]
    PLOT_NUM = CPC_PAR["prev_plot_PLOT_NUM"]
}'

def _invert 'neg'


#%UU% Differentiate currently plotted data %BR%
# (Friedrich Schotte)
def _diff '{
    local i j
    global array CPC_aux_arr[NPTS][COUNTERS+1+PLOT_MOTS]
    array_copy(CPC_aux_arr, SCAN_D)

    local cnt_idx delta_x
    local cnt_idx

    local DIFF_SIGN

    cpc_dbg(sprintf("  _diff() PLOT_NUM = %d PL_Y1 = %d", PLOT_NUM, PL_Y1))

    #???
    DIFF_SIGN = 1

    if ($#==1) {
        if ("$1"<0) {
            DIFF_SIGN= -1
            cpc_dbg("  _diff() : DIFF_SIGN = -1")
        }
    }

    # Loop over plotted counters
    for (i = 0; i < PLOT_NUM; i++) {
        cnt_idx = PL_Y1 + i
        cpc_dbg("  _diff() : loop %d", i)

        # For zap,  nn=2
        if(CPC_PAR["zapscan"]) {
            cnt_idx = 2
        }
        cpc_dbg(sprintf("  _diff() : cnt_idx=%s", cnt_idx))

        if (NPTS >= 2) {
            delta_x = SCAN_D[1][0] - SCAN_D[0][0] # width between points
            if(delta_x == 0) {
                printf("diff: ERROR1 : division by zero; i=%d j=%d\n", i, j)
            }
            else {
                CPC_aux_arr[i][cnt_idx] = (SCAN_D[1][cnt_idx] - SCAN_D[0][cnt_idx]) / delta_x
            }
        }

        for (j = 1; j < NPTS-1; j++) {
            delta_x = SCAN_D[j+1][0] - SCAN_D[j-1][0]
            if(delta_x == 0) {
                printf("diff: ERROR2 : division by zero; i=%d j=%d\n", i, j)
            }
            else {
                CPC_aux_arr[j][cnt_idx] = (SCAN_D[j+1][cnt_idx] - SCAN_D[j-1][cnt_idx]) / delta_x
            }
        }

        if (NPTS >= 2) {
            delta_x = SCAN_D[NPTS-1][0] - SCAN_D[NPTS-2][0]
            if(delta_x == 0) {
                printf("diff: ERROR3 : division by zero; i=%d j=%d\n", i, j)
            }
            else {
                CPC_aux_arr[NPTS-1][cnt_idx] = (SCAN_D[NPTS-1][cnt_idx] - SCAN_D[NPTS-2][cnt_idx]) / delta_x
            }
        }

        # orginal
        # ~ for (j=0; j<NPTS; j++) {
            # ~ SCAN_D[j][cnt_idx] = DIFF_SIGN * diff_d[j]
        # ~ }
        CPC_aux_arr[][cnt_idx] *= DIFF_SIGN
    }

    plot CPC_aux_arr 0 PL_Y

}'

# ???
def undiff '{
    local i j
    local cnt_idx

    cpc_plot()
}'


def cpc_test '{
    need genescan

    plotselect f1

    # step
    genescan_setup f1 gauss
    ascan m8 0 10 50 0.001
    cen
    sleep(1)


    # step
    a2scan m8 0 10  m9 1 11    50 0.001


    # zap
    genescan_setup f1 gauss 1
    ascan m8 0 10 40 0.001
    cen
    sleep(1)

    # step
    genescan_setup f1 gauss
    ascan m8 0 10 30 0.001
    pic
    sleep(1)

    # step
    genescan_setup f1 gauss
    ascan m8 0 10 30 0.001
    diff
    sleep(0.4)
    diff
    sleep(0.4)
    diff
    sleep(0.4)
    diff
    sleep(0.4)
    diff
    sleep(0.4)

    # zap
    genescan_setup f1 gauss 1
    ascan m8 0 10 20 0.001
    pic
    sleep(1)

    # zap
    genescan_setup f1 gauss 1
    ascan m8 0 10 20 0.001
    invert
    sleep(1)

    # zap
    genescan_setup f1 gauss 1
    ascan m8 0 10 20 0.001
    diff
    sleep(0.4)
# marche pas une seconde fois apres un zap.
    diff

    # step
    genescan_setup f1 gauss
    a2scan m8 0 10  m9 1 11    50 0.001
    diff
    sleep(0.4)
    diff

    # ok on 6.05.02
}'


# some troubles with new spec versions...
#  6.06.12 OK
#
#  # fresh
#  ascan m3 4 5 20 0.001
#  whats SCAN_D
#    A global shared-memory double 4096-row 7-column row-wise array.
#  counters_disable("f1")
#  ascan m3 4 5 20 0.001
#  whats SCAN_D
#    A global shared-memory double 4096-row 7-column row-wise array.
#                                           ^
#
#  6.7.1 PB
#  counters_disable("f1")
#  ascan m3 4 5 20 0.001
#  whats SCAN_D
#    A global shared-memory double 4096-row 6-column row-wise array.
#                                           ^

def cpc_info '{

    print "PL_Y1     =", PL_Y1
    print "PL_YS     =", PL_YS
    print "PLOT_MOTS =", PLOT_MOTS
    print "_stype    =", _stype
    print "PL_A      =", PL_A
    print "SCAN_N    =", SCAN_N
    print "CPC_PAR   =", CPC_PAR
    print "pl_MIN    =", pl_MIN
    print "pl_MAX    =", pl_MAX
    print "pl_xMAX   =", pl_xMAX
    print "CEN       =", CEN
}'


######################################################################
############################               ###########################
############################   ADDITIONS   ###########################
############################               ###########################
######################################################################

# Add functionalities to the cenpiccurs macro from the blissinstaller distribution
# with the friendly permission of Didier Wermeille - 2023/03/16

def min '{
    cpc_init()
    _min
    cpc_restore()
}'

def cofm '{
    cpc_init()
    _cofm
    cpc_restore()
}'

#%UU%
#%DESC% Moves motors of last scan to minimum.
def _min '{
    local i, i_at_min, pts
    local _peak
    local mne_used_cnt idx_used_cnt
    cpc_dbg("_min")

    if (!(_stype & scanType_MeshScan) && !(_stype & scanType_HKL_Scan) && _stype != 0) {
        cpc_dbg("_pic: Not a mesh nor a HKL scan")

        i_at_min = array_op("row_at_min", @PL_A[0:LDT][PL_Y1])
        cpc_dbg(sprintf("_min: PL_Y1=%s i_at_min=%g", PL_Y1, i_at_min))

        idx_used_cnt = cnt_num(PLOT_SEL[0])
        mne_used_cnt = cnt_mne(idx_used_cnt)
        print "Using counter", mne_used_cnt, "(idx=", idx_used_cnt, ")  ", "with scan", SCAN_N ":", HEADING

        waitmove; get_angles

        for (i=0; i < _nm; i++) {
            _peak = @PL_A[i_at_min][i]

            printf("Moving motor \"%s\" from %9.4f to the MIN (pos=%9.4f val=%g) of last scan\n", \
                   motor_mne(_m[i]), A[_m[i]], _peak, pl_MIN)

            A[_m[i]] = _peak
            CPC_PAR["last_pic"] = _peak
        }
        _plot_line(pl_xMIN, pl_MIN, pl_MAX)
        move_em
        waitmove; get_angles
    }
    else {
        if (_stype == 0) {
            eprint "Sorry, no scan to work on yet!"
            exit
        } else {
            eprint "Sorry, cannot use this macro after this type of scan! Aborted"
            exit
        }
    }
}'

#%UU%
#%DESC% Moves motors of last scan to center of mass.
def _cofm '{
    local i, fact_at_pos, pts
    local _peak
    local mne_used_cnt idx_used_cnt
    cpc_dbg("_cofm")

    if (!(_stype & scanType_MeshScan) && !(_stype & scanType_HKL_Scan) && _stype != 0) {
        cpc_dbg("_cofm: Not a mesh nor a HKL scan")
        cpc_dbg(sprintf("_cofm: Center of Mass=%g", pl_COM))

        fact_at_pos = (pl_COM - @PL_A[0][0]) / (@PL_A[LDT][0] - @PL_A[0][0])

        idx_used_cnt = PL_Y1
        mne_used_cnt = cnt_mne(idx_used_cnt)
        print "Using counter", mne_used_cnt, "(idx=", idx_used_cnt, ")  ", "with scan", SCAN_N ":", HEADING

        waitmove; get_angles

        for (i=0; i < _nm; i++) {
            _peak = @PL_A[0][i] + fact_at_pos * (@PL_A[LDT][i] - @PL_A[0][i])

            printf("Moving motor \"%s\" from %9.4f to the CofM (pos=%9.4f) of last scan ", \
                   motor_mne(_m[i]), A[_m[i]], _peak)

            printf("(Last scan was %s: %s)\n\n", SCAN_N, HEADING)

            A[_m[i]] = _peak
            CPC_PAR["last_pic"] = _peak
        }
        _plot_line(pl_COM, pl_MIN, pl_MAX)
        move_em
        waitmove; get_angles
    }
    else {
        if (_stype == 0) {
            eprint "Sorry, no scan to work on yet!"
            exit
        } else {
            eprint "Sorry, cannot use this macro after this type of scan! Aborted"
            exit
        }
    }
}'

#%UU%
#%DESC% CALCULATES BUT NOT MOVE to last scan center of mass.
def wcofm '{
    local i, fact_at_pos, pts, _center
    local mne_used_cnt
    cpc_dbg("_cofm")


    if (!(_stype & scanType_MeshScan) && !(_stype & scanType_HKL_Scan) && _stype != 0) {
        cpc_dbg("_cofm : Not a mesh.")
        cpc_dbg(sprintf("_cofm : Center of Mass=%g", pl_COM))

        fact_at_pos = (pl_COM - @PL_A[0][0]) / (@PL_A[LDT][0] - @PL_A[0][0])

        idx_used_cnt = cnt_num(PLOT_SEL["0"])
        mne_used_cnt = cnt_mne(idx_used_cnt)
        print "(Using counter", mne_used_cnt, "with scan", SCAN_N ":", HEADING ")"
        waitmove; get_angles

        _center = @PL_A[0][0] + fact_at_pos * (@PL_A[LDT][0] - @PL_A[0][0])
        printf(" CENTER OF MASS of last scan : %9.4f ",  _center)

        _plot_line(pl_COM, pl_MIN, pl_MAX)

    }
    else {
        if (_stype == 0) {
            eprint "Sorry, no scan to work on yet!"
        } else {
            eprint "Sorry, cannot use this macro after this type of scan! Aborted"
            exit
        }
    }
}'

#%UU%
#%DESC% Shows position of last scan minimum.
def wmin '{
    local i, i_at_min, pts, _peak
    local mne_used_cnt
    cpc_dbg("_min")


    if (!(_stype & scanType_MeshScan) && !(_stype & scanType_HKL_Scan) && _stype != 0) {
        cpc_dbg("_min: Not a mesh nor a HKL scan.")
        cpc_dbg(sprintf("_min: Center of Mass=%g", pl_xMIN))

        i_at_min = array_op("row_at_min", @PL_A[0:LDT][PL_Y1])
        cpc_dbg(sprintf("_min: PL_Y1=%s i_at_min=%g", PL_Y1, i_at_min))

        idx_used_cnt = cnt_num(PLOT_SEL["0"])
        mne_used_cnt = cnt_mne(idx_used_cnt)
        print "Using counter", mne_used_cnt, "(idx=", idx_used_cnt, ")  ", "with scan", SCAN_N ":", HEADING

        waitmove; get_angles

        _peak = @PL_A[i_at_min][0]
        printf(" MINIMUM of last scan : %9.4f ",  _peak)

        _plot_line(pl_xMIN, pl_MIN, pl_MAX)

    }
    else {
        if (_stype == 0) {
            eprint "Sorry, no scan to work on yet!"
        } else {
            eprint "Sorry, cannot use this macro after this type of scan! Aborted"
            exit
        }
    }
}'

#%MACROS%
#%AUTHOR% H. Witsch, based on the work of L. Claustre, BLISS (C.G:ugly zap version) and D. Wermeille
#%BR%
#$Revision: 1.34 $