esrf

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


#%TITLE% KB_FOCUS.MAC
#%NAME%
#  KB_FOCUS
#%CATEGORY%
#%DESCRIPTION%
#  KB_FOCUS is a set of macros designed to focus a kb mirror. Two
#  iteratives methdos of focusing are provided : parabolic fitting and
#  interaction matrix.%BR%
#
#  The principle is to change the bending of the mirror and to control
#  the gain in terme of focusing. The quality of focusing is depicted
#  by a minimal movement of the beam (bpm counters) during a scan
#  (rel_start rel_end) of slits.
#  Once a correction has been done, we try to evaluate the next
#  correction to apply considering the previous one and the gain.
#
#
#%DEPENDANCIES%
# spec_utils.mac
# hg.mac
#
#%EXAMPLE%
#    kbf_focus <bender_inc> <"horiz"|"vert"> <"asym"|"sym"|"both"|"intm">
#               <rel_start> <rel_end> <nb_pts> <counting_time> %BR% %BR%
#
#   <bender_inc>         : Bender motors increment.                     %BR%
#   <"horiz"|"vert">     : Horizontal or vertical focalization.         %BR%
#   <"asym"|"sym"|"both"|"intm"> : Focalization mode.                   %BR%
#   <rel_start>          : Relative start of the SLITS scans. (V and H) %BR%
#   <rel_end>            : Relative end of the SLIT scans.  (V and H)   %BR%
#   <nb_pts>             : Number of points of scans.                   %BR%
#   <counting_time>      : Counting time per point time (must be bigger %BR%
#                          than exposure time for the bpm counters).    %BR%
# %DL%
# %DT%
# %UL%
# %LI% "asym" : Asymetric correction : the two bender motors are
#               actuated in opposite directions. This allows to
#               correct quadratic term of focusing error.
# %LI% "sym"  : Symetric correction : The two bender motors are
#               actuated in the same direction. This allows to correct
#               linear term of focusing error.
# %LI% "both" : Correct both terms.
# %LI% "intm" : Use interaction matrix + quadratic fit.
# %XUL%
#
#%SETUP%
#
# 3 setup commands are needed:
#
#
# %DL%
# %DT% 1) kbf_setup_slits_positions <horiz_gap_open> <horiz_gap_closed>  <vert_gap_open> <vert_gap_closed>   <horiz_offset_max> <vert_offset_max> %DD% %BR%
#
#  Sets the limits position checked for gap/offsets before doing a scan. %BR%
# %UL%
#  %LI% <horiz_gap_open>    : Value of horizontal gap    to be used    for a vertical scan.    (big)
#  %LI% <horiz_gap_closed>  : Value of horizontal gap    to be used    for an horizontal scan. (small)
#  %LI% <vert_gap_open>     : Value of vertical   gap    to be used    for an horizontal scan. (big)
#  %LI% <vert_gap_closed>   : Value of vertical   gap    to be used    for a vertical scan.    (small)
#  %LI% <horiz_offset_max>  : Value of horizontal offset to not exceed for a scan.
#  %LI% <vert_offset_max>   : Value of vertical   offset to not exceed for a scan.
# %XUL%
#
#  example : kbf_setup_slits_positions   0.4  0.02    0.2  0.02    0.1?  0.1?
#
#  If gaps are not inside good limits, the macro ask to open/close them.
#  If offsets are bigger than _offset_max, macro ask to center them.
#
#
# %DT% 2) kbf_setup_motors <horiz_ct> <vert_ct>  <hor_gap> <ver_gap> <hor_off> <v_off> <hor1> <hor2> <ver1> <ver2> %DD% %BR%
#
#  Sets counters and motors names.%BR%
#  You have to define 2 bpm counters and 8 motors for slits and KB benders. %BR%
#
# %UL%
# %LI%<horiz_ct> : Horizontal BPM position  counter.
# %LI%<vert_ct>  : Vertical   BPM position  counter.
# %LI%<hor_gap>  : Horizontal Slits Gap     motor mnemonic.
# %LI%<ver_gap>  : Vertical   Slits Gap     motor mnemonic.
# %LI%<hor_off>  : Horizontal Slits Offset  motor mnemonic.
# %LI%<v_off>    : Vertical   Slits Offset  motor mnemonic.
# %LI%<hor1>     : 1st bender of horizontal mirror (upstream   motor relative to the beam)
# %LI%<hor2>     : 2nd bender of horizontal mirror (downstream motor relative to the beam)
# %LI%<ver1>     : 1st bender of vertical   mirror (upstream   motor relative to the beam)
# %LI%<ver2>     : 2nd bender of vertical   mirror (downstream motor relative to the beam)
# %XUL%
#
#  example : kbf_setup_motors metx mety   kshg ksvg ksho ksvo   pmh1 pmh2 pmv1 pmv2          %BR%
#
#
#
# %DT% 3) kbf_setup <sleep_time>  %DD% %BR%
# %UL%
# %LI% <sleep_time>     : the time to wait for KB stabilization after a move.
# %XUL%
#
# example : kbf_setup 0.2
# (Spec fitting. (recommanded)) %BR%
#
#
#%END%


need spec_utils
need hg
hg_generate("kbf")

kbf_debug


# DONE:
#-donner les corrections en relatif.
#-afficher les statts a chaque scan (min; max; avg; stddev; pictoval).
#-plotselect selon vert or horz.
#-modes de correction :
#  -quadratic
#  -linaire
#  -linaire + quadratic
#  -Interaction Matrix
#-tests pour verifier l'existance des moteurs.
#-tests pour verifier l'existance des counters.
#-check if slits are correctly open.
#-genericiser pour distribuer.
#-change N-var to assarray.
#-improve intercation matrix algorithm.
#-improve intercation matrix algorithm.

# TODO:
#-nicer display of interaction matrix ?
#-pplot safe ?
#-test of bpm ?
#-spec commands between two focusing actions.


#%UU% <sleep_time>
#%MDESC%
#    -Waits <sleep_time> seconds after a movement for stabilization of the KB.
def kbf_setup '{
    global KBF_FIT_PARAM[]
    global KBF_PAR[]
    # KBF_PAR["verbose"]
    # KBF_PAR["limits_setup_done"]
    # KBF_PAR["mot_setup_done"]
    # KBF_PAR["setup_done"]

    # kb motors
    # KBF_PAR["hor1"]
    # KBF_PAR["hor2"]
    # KBF_PAR["ver1"]
    # KBF_PAR["ver2"]

    # counters (bpm)
    # KBF_PAR["vert_ct"]
    # KBF_PAR["hor_ct"]

    # slits motors
    # KBF_PAR["hor_off"]
    # KBF_PAR["vert_off"]
    # KBF_PAR["hor_gap"]
    # KBF_PAR["vert_gap"]

    # slits motors limits
    # KBF_PAR["horgap_max"]
    # KBF_PAR["horoff_max"]
    # KBF_PAR["vergap_max"]
    # KBF_PAR["veroff_max"]

    #  sleep time after bender moving for stabilization
    KBF_PAR["wait_after_move"] = $1

    # Saves stats for history.
    global KBF_STATS[]

    local nbparam
    nbparam = $#

    KBF_PAR["verbose"] = 0

    print "---------------------------------------"

    if (KBF_PAR["limits_setup_done"] ){
        kbf_print("kbf_setup", "limits setup done")
    }
    else{
        kbf_print("kbf_setup", "ERROR--KBFocusing limits setup not done !")
        kbf_print("kbf_setup", " --> run kbf_setup_slits_positions before kbf_setup")
        exit
    }

    if (KBF_PAR["mot_setup_done"] ){
        kbf_print("kbf_setup", "KBFocusing motor setup done")
    }
    else{
        kbf_print("kbf_setup", "ERROR--KBFocusing motor setup not done !")
        kbf_print("kbf_setup",  " --> run kbf_setup_motors before kbf_setup")
        exit
    }

    kbf_print("kbf_setup", "KBfocusing macros setup")

    if (nbparam < 1){
        print ""
        print "$0 : Not enough paramaters !!!"
        print "   usage : $0 <sleep_time>"
        print " --->exit"
        exit
    }

    KBF_FIT_PARAM[0] = 1
    KBF_FIT_PARAM[1] = 1
    KBF_FIT_PARAM[2] = 1

    KBF_PAR["setup_done"] = 1

    print "---------------------------------------"

}'

#%UU%  <horiz_gap_open> <horiz_gap_closed> <vert_gap_open> <vert_gap_closed>   <horiz_offset_max> <vert_offset_max>
#%MDESC%
# Defines the values to use by safety macros to prevent a scan with
# bad slit gap or offset. Using theses values, the scan macro will
# propose to reduce or increase the gap/offset before doing the scans.
def kbf_setup_slits_positions'{
    global KBF_PAR[]

    local _nbparam

    _nbparam = $#

    KBF_PAR["limits_setup_done"] = 0

    if (_nbparam < 6){
        print "$0 error : not enough paramaters !"
        print "Defines the limits used by safety macros to"     \
            " prevent a scan with bad slit gap or offset."
        print " Usage : $0  <horiz_gap_open> <horiz_gap_closed> <vert_gap_open> <vert_gap_closed>  <horiz_offset_max> <vert_offset_max>"
        print "--> exit"
        exit
    }

    KBF_PAR["horiz_gap_open"]   = "$1"
    KBF_PAR["horiz_gap_closed"] = "$2"
    KBF_PAR["vert_gap_open"]    = "$3"
    KBF_PAR["vert_gap_closed"]  = "$4"
    KBF_PAR["horiz_offset_max"] = "$5"
    KBF_PAR["vert_offset_max"]  = "$6"

    KBF_PAR["limits_setup_done"] = 1
}'

#%UU% <horiz_ct> <vert_ct>    <horgap> <vergap> <horoff> <voff>    <hor1> <hor2> <ver1> <ver2>
#%MDESC%
# Defines counters and motors mnemonnics to use for focusing.
# Checks if motors are well defined.
def kbf_setup_motors '{
    global KBF_PAR[]

    local nbparam

    nbparam = $#

    KBF_PAR["mot_setup_done"] = 0

    if (nbparam < 10){
        print "$0 errror : not enough paramaters !!!"
        print " usage : $0 <horiz_ct>  <vert_ct> <horgap> <vergap> " \
              "<horoff> <voff> <hor1> <hor2> <ver1> <ver2>"
        exit
    }

    # counters
    KBF_PAR["hor_ct"]   = "$1"
    KBF_PAR["vert_ct"]  = "$2"

    # slit motors
    KBF_PAR["hor_gap"]  = "$3"
    KBF_PAR["vert_gap"] = "$4"
    KBF_PAR["hor_off"]  = "$5"
    KBF_PAR["vert_off"] = "$6"

    # kb motors
    KBF_PAR["hor1"]    = "$7"
    KBF_PAR["hor2"]    = "$8"
    KBF_PAR["ver1"]    = "$9"
    KBF_PAR["ver2"]    = "$10"

    # Checks if BPM counters exist.
    kbf_check_counter(KBF_PAR["hor_ct"])
    kbf_check_counter(KBF_PAR["vert_ct"])

    # Checks if all slits motors exist.
    kbf_check_motor(KBF_PAR["hor_gap"])
    kbf_check_motor(KBF_PAR["vert_gap"])
    kbf_check_motor(KBF_PAR["hor_off"])
    kbf_check_motor(KBF_PAR["vert_off"])

    # Checks if all benders motors exist.
    kbf_check_motor(KBF_PAR["hor1"])
    kbf_check_motor(KBF_PAR["hor2"])
    kbf_check_motor(KBF_PAR["ver1"])
    kbf_check_motor(KBF_PAR["ver2"])

    KBF_PAR["mot_setup_done"] = 1
}'



#%UU% <motor type>
#%MDESC%
#    Allow to define
# <motor type> : pico | <NONE>
#
def kbf_setup_motor_type '{
    KBF_PAR["motors_type"] = "$1"
}'

#%UU%
#%MDESC%
# Remove KFFocusing globals.
def kbf_unsetup '{

    unglobal KBF_PAR
    unglobal KBF_FIT_PARAM
    unglobal KBF_INTM_PARS
    unglobal KBF_INTM_DAT
    unglobal KBF_TESTA
    unglobal KBF_STATS

    print "KBFocus unsetup done"
}'



#%UU%
#%MDESC%
#    Display some info about kbf coinfiguration
def kbf_info '{
    print ""
    print "            KB FOCUSING     "
    print " "

    if (!KBF_PAR["setup_done"]) {
        print " setup has not been done."
        exit
    }

    printf(" BPM counters -  horizontal: %s   vertical: %s  \n", KBF_PAR["hor_ct"], KBF_PAR["vert_ct"] )
    print ""
    printf(" Bending motors -   horizontal: %s %s   vertical: %s %s\n",                   \
           KBF_PAR["hor1"], KBF_PAR["hor2"], KBF_PAR["ver1"], KBF_PAR["ver2"])

    print ""
    printf(" Slits motors:\n")
    printf("   horizontal gap: %s  horizontal offset: %s  \n", KBF_PAR["hor_gap"],  KBF_PAR["hor_off"])
    printf("   vertical   gap: %s  vertical   offset: %s  \n", KBF_PAR["vert_gap"], KBF_PAR["vert_off"])

    print ""
    printf(" Sizes for gap/offsets \n")
    printf("   horizontal gap  -  open: %6s  closed: %6s   \n", KBF_PAR["horiz_gap_open"], KBF_PAR["horiz_gap_closed"])
    printf("   vertical   gap  -  open: %6s  closed: %6s   \n", KBF_PAR["vert_gap_open"], KBF_PAR["vert_gap_closed"])
    print ""
    printf(" Slits offsets max:\n")
    printf("   horizontal : %s  vertical :%s   \n", KBF_PAR["horiz_offset_max"], KBF_PAR["vert_offset_max"])

    print ""

}'


######################################################################
##############################          ##############################
##############################  Checks  ##############################
##############################          ##############################
######################################################################

#%UU% (<motmne>)
#%MDESC%
#
def kbf_check_motor(motmne) '{
    if(motor_num(motmne) < 0){
        printf ("ERROR : Motor \"%s\" does not exists.", motmne)
        print   "   please check setup.\n --->exit"
        exit
    }
}'


#%UU% (<cntmne>)
#%MDESC%
#
def kbf_check_counter(cntmne) '{
    if(cnt_num(cntmne) < 0){
        printf ("ERROR : Counter \"%s\" does not exists.", cntmne)
        print   "   please check setup.\n --->exit\n"
        exit
    }
}'


#%UU% ()
#%MDESC%
# Checks needed for an HORIZONTAL scan.
def kbf_check_h_slits() '{
    global KBF_PAR[]

    local _rep   _hgap _vgap  _hoff _voff

    ##### GAPS ########
    # horiz scan => horizontal gap must be small.
    #            => vertical   gap must be large.

    # Checks if horz slit gap is closed enough.
    # Adds 1 microns error margin.
    _hgap = A[motor_num(KBF_PAR["hor_gap"])]
    if (_hgap > KBF_PAR["horiz_gap_closed"] + 0.001) {
        printf ("Horizontal slit gap %s seems too large (%f) \n", KBF_PAR["hor_gap"], _hgap )
        if (yesno (sprintf("Do you want to close it to %f ?",  KBF_PAR["horiz_gap_closed"]), 1)) {
            eval( sprintf("mv %s %f",  KBF_PAR["hor_gap"], KBF_PAR["horiz_gap_closed"]))
        }
    }
    else{
        printf ("Horizontal slit gap %s = %f \n", KBF_PAR["hor_gap"] , _hgap)
    }

    # Checks if vertical slit gap is open enough.
    # Subtracts 10 microns error margin.
    _vgap = A[motor_num(KBF_PAR["vert_gap"])]
    if(_vgap < KBF_PAR["vert_gap_open"] - 0.01){
        printf ("Vertical slit gap %s seems too closed (%f) \n", KBF_PAR["vert_gap"], _vgap)
        if (yesno ("do you want to open it ?\n", 1)){
            _rep = getval("New value for vertical gap ? ", KBF_PAR["vert_gap_open"])
            eval (sprintf ("mv %s %f", KBF_PAR["vert_gap"], _rep))
        }
    }
    else{
        printf ("Vertical slit gap %s = %f \n", KBF_PAR["vert_gap"] , _vgap)
    }

    #### OFFSETS ########
    # Is horizontal offset enough centered ?
    # NOT really needed ?
#    _hoff = fabs(A[motor_num(KBF_PAR["hor_off"])])
#    if ( _hoff > KBF_PAR["horiz_offset_max"]) {
#        printf ("Horizontal Offset %s seems too big : %f \n", KBF_PAR["hor_off"], _hoff)
#        if (yesno ("do you want to center it to 0 ?", 1)) {
#            eval (sprintf ("mv %s 0", KBF_PAR["hor_off"]))
#        }
#    }
#
#    # Is vertical offset enough centered ?
#    _voff = fabs(A[motor_num(KBF_PAR["vert_off"])])
#    if (_voff > KBF_PAR["vert_offset_max"]) {
#        printf ("Vertical Offset %s too big : %f \n", KBF_PAR["vert_off"], _voff)
#        if (yesno ("Do you want to center it to 0 ?", 1)) {
#            eval (sprintf ("mv %s 0", KBF_PAR["vert_off"]))
#        }
#    }
}'


#%UU% []
#%MDESC%
# Checks needed for a VERTICAL scan.
def kbf_check_v_slits() '{
    global KBF_PAR[]

    local _rep  _vgap _hgap _voff _hoff

    ##### GAPS ########
    # vert. scan => vertical   gap must be small.
    #            => horizontal gap must be large.

    # Is vertical gap small enough ?
    _vgap = A[motor_num(KBF_PAR["vert_gap"])]
    if (_vgap > KBF_PAR["vert_gap_closed"] + 0.001) {
        printf ("Vertical slit gap %s seems too large (%f) \n", KBF_PAR["vert_gap"], _vgap)
        if (yesno (sprintf("Do you want to close it to %f ?", KBF_PAR["vert_gap_closed"]), 1)) {
            eval( sprintf("mv %s %f",  KBF_PAR["vert_gap"], KBF_PAR["vert_gap_closed"]))
        }
    }
    else{
        printf ("Vertical slit gap %s = %f \n", KBF_PAR["vert_gap"] , _vgap)
    }

    # Is horizontal gap large enough ?
    _hgap = A[motor_num(KBF_PAR["hor_gap"])]
    if( _hgap < KBF_PAR["horiz_gap_open"] - 0.01){
        printf ("Horizontal slit gap %s seems too closed (%f) \n", KBF_PAR["hor_gap"] , _hgap)
        if (yesno ("do you want to open it ?", 1)){
            _rep = getval("New value for horizontal gap ? ", KBF_PAR["horiz_gap_open"])
            eval (sprintf ("mv %s %f", KBF_PAR["hor_gap"], _rep))
        }
    }
    else{
        printf ("Horizontal slit gap %s = %f \n", KBF_PAR["hor_gap"] , _hgap)
    }


    #### OFFSETS ########
    # Is vertical  offset enough centered ?
    # NOT really needed ?
#    _voff = fabs(A[motor_num(KBF_PAR["vert_off"])])
#    if (_voff > KBF_PAR["vert_offset_max"]) {
#        printf ("Horizontal offset %s seems too big.", KBF_PAR["vert_off"])
#        if (yesno ("do you want to center it to 0 ?", 1)) {
#            eval (sprintf ("mv %s 0", KBF_PAR["vert_off"]))
#        }
#    }
#
#    # Is horizontal offset enough centered ?
#    _hoff = fabs(A[motor_num(KBF_PAR["hor_off"])])
#    if (_hoff > KBF_PAR["horiz_offset_max"]) {
#        printf ("Horizontal offset %s seems too big", KBF_PAR["hor_off"])
#        if (yesno ("Do you want to center it to 0 ?", 1)) {
#            eval (sprintf ("mv %s 0", KBF_PAR["hor_off"]))
#        }
#    }
}'



######################################################################
############################              ############################
############################  FIT & PLOT  ############################
############################              ############################
######################################################################


#%UU% (cnt_number)
#%MDESC%
#    Fits data of counter cnt_number with a parabola.
#    Displays the fitted parabla.
#    Results are put in KBF_FIT_PARAM
def kbf_parabola(cnt_number) '{
    global KBF_PAR[]
    global ESRF_ERR
    global KBF_FIT_PARAM[]

    local pars
    local chi2

    chi2 = kbf_spec_fit(cnt_number)
    # print "chi2 = " chi2
    # if (chi2 <0 )
    # if(chi2 > ??)

    if (KBF_PAR["verbose"]) {
        print KBF_FIT_PARAM
    }


    kbf_plotParabola()


}'


#%UU% []
#%MDESC%
#
def kbf_spec_fit(cnt_number) '{
    global KBF_FIT_PARAM[]
    global float array fitdat[NPTS][4]

    local cnt_index_SCAND
    local chi2

    kbf_dbg(sprintf("kbf_spec_fit(cnt_number=%s)", cnt_number))

    # column with counter starts at "PLOT_MOTS" index.

    cnt_index_SCAND = get_SCAN_D_counter_number(cnt_number)

    # y data
    fitdat[:][0] = SCAN_D[:NPTS-1][cnt_index_SCAND+PLOT_MOTS]
    # x*x
    fitdat[:][1] = SCAN_D[:NPTS-1][0] * SCAN_D[:NPTS-1][0]
    # x
    fitdat[:][2] = SCAN_D[:NPTS-1][0]
    # cst
    fitdat[:][3] = 1

    #  array_fit(pars, a [, b, ...]) - Performs a linear fit of the data
    #    in the array a.  The fitted parameters are returned in the array pars.
    #    The function returns the chi-squared value of the fit, if the fit was
    #    successful.  A -1 is returned if the covariance matrix is singular.
    #    The fit algorithm is along the same lines as the lfit() routine in
    #    Numerical Recipes (W.H.  Press, et al., Cambridge University Press,
    #                     1986, page 512).

    KBF_FIT_PARAM[0] = 0
    KBF_FIT_PARAM[1] = 0
    KBF_FIT_PARAM[2] = 0

    # Fills KBF_FIT_PARAM with parameters of the fitted curve
    chi2 = array_fit(KBF_FIT_PARAM, fitdat)
    kbf_dbg(sprintf("kbf_spec_fit(chi2=%g)", chi2))
    return chi2
}'


#%UU% []
#%MDESC%
#    Fit function for interaction matrix mode. ( for ABA method 5 ?)
# ???
def kbf_intm_fit(cnt_number) '{
    global KBF_INTM_PARS[]
    global KBF_INTM_DAT[]
    local chi2  _mean
    local cnt_index_SCAND

    kbf_dbg(sprintf("kbf_intm_dat(cnt_number=%s)", cnt_number ))

    # index in SCAN_D
    cnt_index_SCAND = get_SCAN_D_counter_number(cnt_number)

    _mean = array_op( "sum", SCAN_D[:NPTS-1][cnt_index_SCAND + PLOT_MOTS]) / NPTS
    KBF_INTM_DAT[:][0] = - ( SCAN_D[:NPTS-1][cnt_index_SCAND + PLOT_MOTS]  -  _mean )

    chi2 = array_fit(KBF_INTM_PARS, KBF_INTM_DAT)

    print "chi2=" chi2

    return chi2
}'



#%UU% []
#%MDESC%
#    Plots the parabola resulting of fitting.
def kbf_plotParabola() '{
    global KBF_TESTA
    local pt
    local symbcolor

    kbf_dbg("kbf_plotParabola()")


    for (pt=0 ; pt<NPTS ; pt++) {
        KBF_TESTA[pt][0] = SCAN_D[pt][0]
        KBF_TESTA[pt][1] = kbf_parab( SCAN_D[pt][0])
    }

    #?plot_range("extend", "extend", "extend", "extend")

    plot_cntl("addline")
    symbcolor = plot_cntl("colors[4]")

    plot_cntl("colors=::::3")

    array_plot(KBF_TESTA)

    plot_cntl(t="colors=::::"symbcolor)

}'


######################################################################
##########################                  ##########################
##########################  PICO MOVEMENTS  ##########################
##########################                  ##########################
######################################################################

# oui. ca n-a rien a faire ici ...

#%UU% <parma>
#%MDESC%
#
def pico_mvr(dnum, incr) '{
    global PICO_MVR_RETRIES
    local aux_pos aux_i aux_retries

    kbf_dbg(sprintf("pico_mvr(dnum=%d, incr=%g)", dnum, incr ))

    if ( PICO_MVR_RETRIES == 0 ){
        PICO_MVR_RETRIES = 1
    }

    get_angles
    aux_pos = A[dnum] + incr
    printf("Moving  %s to %f\n", motor_mne(dnum), aux_pos)

    aux_retries = PICO_MVR_RETRIES
    for (aux_i = 0; aux_i < aux_retries; aux_i++) {
        get_angles
        A[dnum] = aux_pos
        move_em; waitmove
    }

    get_angles

    printf("Residual error on %s movement: %f\n", motor_mne(dnum), A[dnum]-aux_pos)

    sleep(KBF_PAR["wait_after_move"])
}'



#%UU% <parma>
#%MDESC%
#
def pico_mv(dnum, pos) '{
        global PICO_MVR_RETRIES
        local aux_pos aux_i aux_retries

        if ( PICO_MVR_RETRIES == 0 ){
            PICO_MVR_RETRIES = 1
        }

        get_angles
        aux_pos = pos
        printf("Moving  %s to %f\n", motor_mne(dnum), aux_pos)
        aux_retries = PICO_MVR_RETRIES

        for (aux_i = 0; aux_i < aux_retries; aux_i++) {
            get_angles
            A[dnum] = aux_pos
            move_em; waitmove
        }

	    get_angles

	    printf("Residual error on %s movement: %f\n", motor_mne(dnum), A[dnum]-aux_pos)

	    sleep(KBF_PAR["wait_after_move"])
}'




######################################################################
############################              ############################
############################  STATISTICS  ############################
############################              ############################
######################################################################

def kbf_disp_history '{
    local ii
    local _min _max _scale _avg _std
    local _f_min _f_max _f_scale _f_avg _f_std

    print "-------------  history of  corrections --------------------"
    for (ii=0; ii< KBF_STATS["scan_number"] ; ii++){
        _min     = KBF_STATS[ii]["raw_min"]
        _max     = KBF_STATS[ii]["raw_max"]
        _avg     = KBF_STATS[ii]["raw_avg"]
        _std     = KBF_STATS[ii]["raw_stdev"]

        _f_min   = KBF_STATS[ii]["fitted_min"]
        _f_max   = KBF_STATS[ii]["fitted_max"]
        _f_avg   = KBF_STATS[ii]["fitted_avg"]
        _f_std   = KBF_STATS[ii]["fitted_stdev"]

        ## DISPLAY ####
        printf ("----------- stats for scan number %d ----------\n", ii)
        print "                    RAW              FITTED "
        printf("min            = %s \t  %s  \n", kbf_fformat(_min),   kbf_fformat(_f_min))
        printf("max            = %s \t  %s  \n", kbf_fformat(_max),   kbf_fformat(_f_max))
        # printf("nb of points   = %s \t  %s  \n", kbf_fformat(_scale), kbf_fformat(_f_scale))
        printf("average        = %s \t  %s  \n", kbf_fformat(_avg),   kbf_fformat(_f_avg))
        printf(" *pictoval     = %s \t  %s  \n", kbf_fformat(_max - _min), kbf_fformat(_f_max - _f_min))
        printf(" *stddev       = ")
        cprint( sprintf("%s",  kbf_fformat(_std)), 1)
        printf(" \t  %s  \n",   kbf_fformat(_f_std))
        print "----------------------------------------"
   }
}'

#%UU% (<counter_num>)
#%MDESC%
# Display statistics about previous scan. Raw data and fitted data.
def kbf_disp_stat(cnt_number)'{
    local _min _max _scale _avg _std
    local _f_min _f_max _f_scale _f_avg _f_std
    local _scan_nb
    local cnt_index_SCAND

    kbf_dbg(sprintf("kbf_disp_stat(%d)", cnt_number))

    cnt_index_SCAND = get_SCAN_D_counter_number(cnt_number)

    _scan_nb = KBF_STATS["scan_number"]

    ## RAW DATA ####
    _min   = array_op("min", SCAN_D[:NPTS-1][cnt_index_SCAND + PLOT_MOTS] )
    _max   = array_op("max", SCAN_D[:NPTS-1][cnt_index_SCAND + PLOT_MOTS])
    # _scale = array_op("rows", SCAN_D[:NPTS-1][cnt_index_SCAND + PLOT_MOTS])
    _scale = NPTS
    _avg   = array_op("sum",  SCAN_D[:NPTS-1][cnt_index_SCAND + PLOT_MOTS]) / _scale

    # (sq root of best estimate of variance)
    # _std = sqrt(array_op("sumsq", SCAN_D[:NPTS-1][cnt_index_SCAND + PLOT_MOTS] - _avg ) / (_scale - 1))

    # pymca version ? (sq root of 2nd moment with respect to avg)
    _std   = sqrt(array_op("sumsq", SCAN_D[:NPTS-1][cnt_index_SCAND + PLOT_MOTS] - _avg ) / _scale )


    KBF_STATS[_scan_nb]["raw_min"]   = _min
    KBF_STATS[_scan_nb]["raw_max"]   = _max
    KBF_STATS[_scan_nb]["raw_avg"]   = _avg
    KBF_STATS[_scan_nb]["raw_p2v"]   = _max - _min
    KBF_STATS[_scan_nb]["raw_stdev"] = _std


    ## FITTED DATA ####
    # KBF_TESTA is filled in kbf_plotParabola()
    _f_min   = array_op("min",  KBF_TESTA[][1])
    _f_max   = array_op("max",  KBF_TESTA[][1])
    # _f_scale = array_op("rows", KBF_TESTA[][1])
    _f_scale = NPTS
    _f_avg   = array_op("sum",  KBF_TESTA[][1]) / _f_scale

    # (sq root of best estimate of variance)
    # _f_std   = sqrt(array_op("sumsq", KBF_TESTA[][1] - _f_avg ) / (_f_scale - 1))

    # pymca version ? (sq root of 2nd moment with respect to avg)
    _f_std   = sqrt(array_op("sumsq", KBF_TESTA[][1] - _f_avg ) / _f_scale )

    KBF_STATS[_scan_nb]["fitted_min"]   = _f_min
    KBF_STATS[_scan_nb]["fitted_max"]   = _f_max
    KBF_STATS[_scan_nb]["fitted_avg"]   = _f_avg
    KBF_STATS[_scan_nb]["fitted_p2v"]   = _f_max - _f_min
    KBF_STATS[_scan_nb]["fitted_stdev"] = _f_std

    ## DISPLAY ####
    printf ("----------- stats for scan number %d ----------\n", _scan_nb)
    print "                    RAW              FITTED "
    printf("min            = %s \t  %s  \n", kbf_fformat(_min),   kbf_fformat(_f_min))
    printf("max            = %s \t  %s  \n", kbf_fformat(_max),   kbf_fformat(_f_max))
    printf("nb of points   = %s \t  %s  \n", kbf_fformat(_scale), kbf_fformat(_f_scale))
    printf("average        = %s \t  %s  \n", kbf_fformat(_avg),   kbf_fformat(_f_avg))
    printf(" *pictoval     = %s \t  %s  \n", kbf_fformat(_max - _min), kbf_fformat(_f_max - _f_min))
    printf(" *stddev       = ")
    cprint( sprintf("%s",  kbf_fformat(_std)), 1)
    printf(" \t  %s  \n",   kbf_fformat(_f_std))
    print "----------------------------------------"

}'


#    Returns a decimal-point-centered string of the <val> number.
def kbf_fformat(val) '{
    local _val

    _val = val
    _str = sprintf("%.8f", _val)

    _ival = int (_val)
    _fval = _val - _ival
    _sfval = sprintf("%f", _fval)

    if (_fval == 0){
        return (sprintf("%5d      ", _ival ))
    }
    else{
        return (sprintf("%5d.%s ", _ival , substr(_sfval,3)))
    }
}'


def kbf_test_stat_display '{
    local _min _max _scale _avg _std
    local _f_min _f_max _f_scale _f_avg _f_std

    _min   = 12.05
    _max   = 587.445638745
    _scale = 35
    _avg   = 23.54
    _std   = 22.6

    _f_min   = 116.456
    _f_max   = 13678.556
    _f_scale = 123
    _f_avg   = 523.4
    _f_std   = 6987.1


    # ----------- stats for scan number 0 ------------------
    #                          RAW               FITTED
    # min            =        12.05             116.456
    # max            =       587.44564        13678.556
    # nb of points   =       354                123
    # average        =        23.54             123.4
    #  *pictoval     =       575.39564        13562.1
    #  *stddev       =        22.6             6987.1
    # ------------------------------------------------------


    ## DISPLAY ####
    printf ("----------- stats for scan number %d ----------\n", scan_number)
    print "                    RAW              FITTED "
    printf("min            = %s \t  %s  \n", kbf_fformat(_min),   kbf_fformat(_f_min))
    printf("max            = %s \t  %s  \n", kbf_fformat(_max),   kbf_fformat(_f_max))
    printf("nb of points   = %s \t  %s  \n", kbf_fformat(_scale), kbf_fformat(_f_scale))
    printf("average        = %s \t  %s  \n", kbf_fformat(_avg),   kbf_fformat(_f_avg))
    printf(" *pictoval     = %s \t  %s  \n", kbf_fformat(_max - _min), kbf_fformat(_f_max - _f_min))


    printf(" *stddev       = ")
    cprint( sprintf("%s",  kbf_fformat(_std)), 1)
    printf(" \t  %s  \n",   kbf_fformat(_f_std))

    print "----------------------------------------"

}'


#%IU% ()
#%MDESC%
#
def _kbf_usage() '{
    print  " Usage:  kbf_focus <bender_inc> [\"horiz\"|\"vert\"] [\"asym\"|\"sym\"|\"both\"|\"intm\"(interaction matrix)] " \
        "<rel_start> <rel_end> <nb_pts> <counting_time>"
    print ""
    print " You can also be interested by following macros:"
    print "    * kbf_disp_history "
    print "    * kbf_info "
    print ""
}'



######################################################################
######################################################################
######################################################################
#############################            #############################
#############################  FOCUSING  #############################
#############################            #############################
######################################################################
######################################################################
######################################################################

#%UU% []
#%MDESC%
#
def kbf_focus '{
    global KBF_PAR
    # why unglobal ???
    # unglobal res_a res_b res_c
    # why global ???
    # global res_a res_b res_c

    local res_a res_b res_c

    local _sleep_time
    local _cmd1  _cmd2  _corr_mode
    local _motd_mne _motu_mne  cnt_number _motu_num _motd_num
    local _incrb  _direction  _relstart  _relend  _npts _count_time
    local soffmot  _snb
    local ka1 ka2 kb1 kb2 det
    local corr1 corr2  _mean
    local _scan_cmd

    local cnt_index_SCAND

    local pico_incr



    print " "
    print "                KB FOCUS          "
    print " "


    if ($# != 7) {
        _kbf_usage()
        exit
    }

    if ($1 <= 0){
        _kbf_usage()
        exit
    }
    else{
        KBF_PAR["incrb"]  =  $1
    }

    cdef("cleanup_once", "; _kbf_cleanup ;", "_kbf_focus_")


    if (PLOT_MODE & 0x40) {
        kbf_err("You are using background substraction for SPEC plotting")
        kbf_err("  It could be hazardous for fitting.")
        if(yesno("Do you want to disable it (and to re-enable it after focusing) ?", 1)) {
            setplot -0x40
            KBF_PAR["BS_to_reactivate"] = 1
        }
        else{
            kbf_msg("Ok. As you want...")
            KBF_PAR["BS_to_reactivate"] = 0
        }
    }
    else{
        KBF_PAR["BS_to_reactivate"] = 0
    }


    KBF_PAR["direction"]  = "$2"
    KBF_PAR["corr_mode"]  = "$3"
    KBF_PAR["relstart"]   =  $4
    KBF_PAR["relend"]     =  $5
    KBF_PAR["npts"]       =  $6
    KBF_PAR["count_time"] =  $7

    # Fills local variable with fields of KBF_PAR.
    # ???? to check ?
    # PC : this does not work for local variabales it seems !
    _incrb      = KBF_PAR["incrb"]
    _direction  = KBF_PAR["direction"]
    _corr_mode  = KBF_PAR["corr_mode"]
    _relstart   = KBF_PAR["relstart"]
    _relend     = KBF_PAR["relend"]
    _npts       = KBF_PAR["npts"]
    _count_time = KBF_PAR["count_time"]

    kbf_dbg(sprintf("_npts = %d _relstart=%g _relend=%g  "  , _npts, _relstart, _relend))

    kbf_dbg(sprintf("direction = %s"  , _direction))

    # data for INTM-like fit. ( method 5 : ABA?)
    global float array KBF_INTM_DAT[_npts+1][3]

    ## Defines motors/counters to use depending on _direction.
    if ( _direction == "horiz") {
       _motd_mne = KBF_PAR["hor1"]
       _motu_mne = KBF_PAR["hor2"]
       cnt_number      = cnt_num(KBF_PAR["hor_ct"])
       soffmot   = KBF_PAR["hor_off"]
       gapmot    = KBF_PAR["hor_gap"]

       kbf_check_h_slits()

       eval(sprintf("plotselect %s", KBF_PAR["hor_ct"]))
    }
    else if (_direction == "vert") {
       _motd_mne  = KBF_PAR["ver1"]
       _motu_mne  = KBF_PAR["ver2"]
       cnt_number = cnt_num(KBF_PAR["vert_ct"])
       soffmot    = KBF_PAR["vert_off"]
       gapmot     = KBF_PAR["vert_gap"]

       kbf_check_v_slits()

       eval(sprintf("plotselect %s", KBF_PAR["vert_ct"]))
    }
    else {
        kbf_print("kbf_focus", sprintf("ERROR--%s : wrong _direction", _direction))
        kbf_print("kbf_focus", "Please choose between  horiz  or  vert.")
        print "  ----> exit"
        exit
    }


    ## Checks correction mode.
    if (_corr_mode != "asym" && _corr_mode != "sym" && \
        _corr_mode != "both" && _corr_mode != "intm"){
        print "Please choose correction mode in {\"asym\" ; \"sym\" ; \"both\" ; \"intm\" (interaction matrix)}"
        print "  ----> exit"
        exit
    }


    # Bender motors : down / up  relative to the beam direction.
    _motd_num = motor_num(_motd_mne)
    _motu_num = motor_num(_motu_mne)


    # Saves original positions to be able to come back to initial
    # bender values.
    get_angles
    KBF_PAR["down_bender_orig"] = A[_motd_num]
    KBF_PAR["up_bender_orig"]   = A[_motu_num]

    printf("Saving original positions of bender motors    \n")
    printf("  %s : %g \n", _motd_mne , A[_motd_num])
    printf("  %s : %g \n", _motu_mne , A[_motu_num])


    ######################################################################
    ###############################         ##############################
    ###############################  SCANS  ##############################
    ###############################         ##############################
    ######################################################################


    global double array KBF_TESTA[_npts+1][2]
    global double array KBF_CURVE[_npts+1][1]

    KBF_STATS["scan_number"] = 0


    ############################  1ST SCAN  #############################
    print "----------------====== 1ST SCAN =======-------------------"
    _scan_cmd = sprintf("d2scan %s %f %f %s 0 0 %d %f",                 \
                        soffmot, _relstart, _relend, gapmot, _npts, _count_time)
    kbf_print("kbf_focus", sprintf("Running %s", _scan_cmd))
    eval(_scan_cmd)


    # ???
    kbf_dbg("splot scan1")
    splot


    ##############################  1ST FIT  ############################
    kbf_parabola(cnt_number)
    kbf_disp_stat(cnt_number)

    res_a[0] = KBF_FIT_PARAM[0]
    res_b[0] = KBF_FIT_PARAM[1]
    res_c[0] = KBF_FIT_PARAM[2]


    cnt_index_SCAND = get_SCAN_D_counter_number(cnt_number)

    # ???
    KBF_CURVE[:][0] = SCAN_D[:NPTS-1][cnt_index_SCAND + PLOT_MOTS]



    ##############################  1ST MOVE  ###########################
    KBF_PAR["down_bender_last"] = A[_motd_num]
    KBF_PAR["up_bender_last"]   = A[_motu_num]

    # 1st motor (motd : down bender).

    if(KBF_PAR["motors_type"] == "pico") {
        # picos with encoder => soft closed-loop.
        pico_incr = _incrb
        pico_mvr(_motd_num, pico_incr)
    }
    else{
        # Classical SPEC motors.
        A[_motd_num] += _incrb
        kbf_print("kbf_focus", sprintf("Moving  %s to %f\n", _motd_mne, A[_motd_num]))
        move_em; waitmove
        sleep(KBF_PAR["wait_after_move"])
    }


    # 2nd motor (motu : up bender).
    if(KBF_PAR["motors_type"] == "pico") {
        # picos with encoder => soft closed-loop.
        if (_corr_mode == "asym") {
            pico_incr = -_incrb
        }
        else if (_corr_mode == "both") {
            pico_incr = _incrb
        }
        else {
            pico_incr = 0
        }
        pico_mvr(_motu_num, pico_incr)
    }
    else{
        # Classical SPEC motors.
        if (_corr_mode == "asym") {
            A[_motu_num] -= _incrb
        }
        else if (_corr_mode == "both") {
            A[_motu_num] += _incrb
        }
        else if (_corr_mode == "sym") {
            A[_motu_num] += _incrb
        }
        else {
            # intm
            A[_motu_num] += 0
        }

        kbf_print("kbf_focus", sprintf("Moving %s to %f \n", _motu_mne, A[_motu_num]))

        move_em; waitmove

        sleep(KBF_PAR["wait_after_move"])
    }

    # sleep to wait for stabilization ???  (previous sleep is not enough ???)
    _sleep_time = 1
    printf( "sleeping %d second for stabilization", _sleep_time)
    sleep(_sleep_time)


    ############################  2ND SCAN  ##############################
    KBF_STATS["scan_number"]++

    print "----------------====== 2ND SCAN =======-------------------"
    kbf_print("kbf_focus", sprintf( "Running 2nd scan : %s", _scan_cmd))


    kbf_dbg(sprintf("_scan_cmd=%s", _scan_cmd))
    eval(_scan_cmd)


    ##############################  2ND FIT  #############################
    # ???
    kbf_dbg("splot scan2")
    splot

    kbf_dbg("kbf_parabola")
    kbf_parabola(cnt_number)

    kbf_disp_stat(cnt_number)

    res_a[1]  = KBF_FIT_PARAM[0]
    res_b[1]  = KBF_FIT_PARAM[1]
    res_c[1]  = KBF_FIT_PARAM[2]

    # Data for INTM fitting (first motor).
    if (_incrb != 0){
        KBF_INTM_DAT[:][1] = (SCAN_D[:NPTS-1][cnt_index_SCAND + PLOT_MOTS] - KBF_CURVE[:][0]) / _incrb

        _mean = array_op( "sum", KBF_INTM_DAT[:][1]) / NPTS
        KBF_INTM_DAT[:][1] = KBF_INTM_DAT[:][1]  - _mean
        KBF_CURVE[:][0] = SCAN_D[:NPTS-1][cnt_index_SCAND + PLOT_MOTS]
    }
    else{
        print "Error : OH oh _incrb =0"
        exit
    }

    if ( ( _corr_mode == "both" ) || ( _corr_mode == "intm" ) ) {

        ##############################  2ND MOVE  ###########################

        KBF_PAR["down_bender_last"] = A[_motd_num]
        KBF_PAR["up_bender_last"]   = A[_motu_num]

        # 1st bender
        if(KBF_PAR["motors_type"] == "pico") {
            if (_corr_mode == "intm") {
                pico_incr = 0
            }
            else {
                pico_incr = _incrb
            }
            pico_mvr(_motd_num, pico_incr)
        }
        else{
            if (_corr_mode == "intm") {
                A[_motd_num] += 0
            }
            else {
                A[_motd_num] += _incrb
            }
            printf("Moving  %s to %f ; %s to %f\n", _motd_mne, A[_motd_num], _motu_mne, A[_motu_num])
            move_em; waitmove
        }


        # 2nd bender
        if(KBF_PAR["motors_type"] == "pico") {
            if (_corr_mode == "intm") {
                pico_incr = _incrb
            }
            else {
                pico_incr = -_incrb
            }
            pico_mvr(_motu_num, pico_incr)
        }
        else{
            if (_corr_mode == "intm") {
                A[_motu_num] += _incrb
            }
            else {
                A[_motu_num] -= _incrb
            }

            printf("Moving  %s to %f ; %s to %f\n", _motd_mne, A[_motd_num], _motu_mne, A[_motu_num])
            move_em; waitmove
        }


        sleep(KBF_PAR["wait_after_move"])

        # sleep to wait for stabilization ???  (previous sleep is not enough ???)
        _sleep_time = 1
        printf( "sleeping %d second for stabilization", _sleep_time)
        sleep(_sleep_time)


        #####################################################################
        KBF_STATS["scan_number"]++
        print "----------------====== 3RD SCAN =======-------------------"
        kbf_print("kbf_focus", sprintf("Running 3rd scan : %s", _scan_cmd))
        eval(_scan_cmd)

        # fit of the 3th scan
        # ???
        kbf_dbg("splot scan3")
        splot
        kbf_parabola(cnt_number)
        kbf_disp_stat(cnt_number)

        res_a[2] = KBF_FIT_PARAM[0]
        res_b[2] = KBF_FIT_PARAM[1]
        res_c[2] = KBF_FIT_PARAM[2]

        if ( _corr_mode == "intm" ) {
            ka1 = (res_a[1] - res_a[0]) / _incrb
            ka2 = (res_a[2] - res_a[1]) / _incrb
            kb1 = (res_b[1] - res_b[0]) / _incrb
            kb2 = (res_b[2] - res_b[1]) / _incrb

            det = ka1*kb2 - kb1*ka2
            if (det == 0){
                print "det==0 ? -> exit "
                exit
            }

            print "K = "
            printf("   %.6g %.6g\n", ka1, ka2)
            printf("   %.6g %.6g\n", kb1, kb2)

            # TODO : Check if there is no 0 element in K.
            if (ka1*ka2*kb1*kb2 ==0){
                print "error: there is a 0 element in K"
            }


            # Data for INTM fitting (second motor). (ABA method ?)
            if (_incrb != 0){
                KBF_INTM_DAT[:][2] = (SCAN_D[:NPTS-1][cnt_index_SCAND + PLOT_MOTS] - KBF_CURVE[:][0]) / _incrb
                _mean = array_op( "sum", KBF_INTM_DAT[:][2]) / NPTS
                KBF_INTM_DAT[:][2] = KBF_INTM_DAT[:][2]  - _mean

                #
                KBF_CURVE[:][0] = SCAN_D[:NPTS-1][cnt_index_SCAND + PLOT_MOTS]
            }
            else{
                print "Error : oulala  _incrb=0"
                exit
            }
        }
    }
    else{
        # ???
        print "No 2nd move/3rd scan for \"sym\" and \"asym\"."
    }


    while (1){

        ###################  Fit of the Nth Scan ####################
        splot
        kbf_parabola(cnt_number)
        kbf_disp_stat(cnt_number)

        _snb = KBF_STATS["scan_number"]

        res_a[_snb] = KBF_FIT_PARAM[0]
        res_b[_snb] = KBF_FIT_PARAM[1]
        res_c[_snb] = KBF_FIT_PARAM[2]

        if (KBF_PAR["verbose"]) {
            print "-----------------------------"
            print "res_a = ", res_a
            print "res_b = ", res_b
            print "-----------------------------"
        }

        #################  CORRECTION  #################

        if (_corr_mode == "asym") {
           corr1 = -_incrb *  res_a[_snb] / ( res_a[1] - res_a[0] + 0.0)
           corr2 = -corr1
        } else if (_corr_mode == "sym") {
           corr1 = -_incrb *  res_b[_snb] / ( res_b[1] - res_b[0] + 0.0)
           corr2 = corr1
        } else if (_corr_mode == "both") {
           corr1 = -_incrb * (  res_a[_snb] / ( res_a[2]-res_a[1]+0.0) + res_b[_snb] / ( res_b[1]-res_b[0]+0.0) )
           corr2 = -_incrb * ( -res_a[_snb] / ( res_a[2]-res_a[1]+0.0) + res_b[_snb] / ( res_b[1]-res_b[0]+0.0) )
        } else if (_corr_mode == "intm") {

            # ???
            corr1 = ( -res_a[_snb]*kb2 + res_b[_snb]*ka2 ) / det
            corr2 = ( -res_b[_snb]*ka1 + res_a[_snb]*kb1 ) / det

            # ???
            kbf_intm_fit(cnt_number)
            corr1_intm = KBF_INTM_PARS[0]
            corr2_intm = KBF_INTM_PARS[1]

            # C est quoi la diff entre les 2 ???
            # A verifier : corr1  c est intm  et corr1_intm c est ABA ????

        }

        print "--------------------------------"
        print "what do you want to do ?"
        print " (a)pply this correction and iterate"
        printf( "       corr1: %g\n", corr1)
        printf( "       corr2: %g\n", corr2)
        print " (b) apply interaction matrix correction and iterate"
        printf( "   corr1_intm: %g\n", corr1_intm)
        printf( "   corr2_intm: %g\n", corr2_intm)
        print " (m)odify corrections (divide by 2) and iterate"
        print " (c)hange start/end and redo the scan ?"
        print " (q)uit without corrections"
        print " (e)xit and cancel LAST corrections"
        printf("         mv %s %f \n", _motd_mne, KBF_PAR["down_bender_last"])
        printf("         mv %s %f \n", _motu_mne, KBF_PAR["up_bender_last"])
        print " e(x)it and cancel ALL corrections"
        printf("         mv %s %f \n", _motd_mne, KBF_PAR["down_bender_orig"])
        printf("         mv %s %f \n", _motu_mne, KBF_PAR["up_bender_orig"])
        print " (r)edo the scan without corrections"
        print " display (h)istory"
        print "     scan = ", _scan_cmd
        print "--------------------------------"


        # 3 ??? parameteriser ca ?
        if ((fabs(corr1_intm) > 3) || (fabs(corr2_intm) > 3)) {
            printf("\nWARNING: Very large correction : corr1_intm=%s > 3  or corr2_intm=%s > 3\n", \
                   corr1_intm, corr2_intm)
            printf("Make sure that this makes sense.\n")
            printf("Otherwise, exit and chose different starting values.\n\n")
        }

        rep = getval("what do you want to do?", "b")

        while( rep != "a" && rep != "q" && rep != "r" && rep != "b" && rep != "c" && rep != "m" && rep!="e" && rep!="x" && rep!="h"){
            rep = getval("what do you want to do ?", "b")
        }

        if (rep == "a"){
            print "------APPLYING CORRECTION AND ITERATING------"
            KBF_PAR["down_bender_last"] = A[_motd_num]
            KBF_PAR["up_bender_last"]   = A[_motu_num]

            if(KBF_PAR["motors_type"] == "pico") {
                pico_mvr(_motd_num, corr1)
                pico_mvr(_motu_num, corr2)
            }
            else{
                _cmd1 = sprintf("mv %s %f", _motd_mne, A[_motd_num] + corr1)
                _cmd2 = sprintf("mv %s %f", _motu_mne, A[_motu_num] + corr2)

                # Correction :
                eval(_cmd1)
                eval(_cmd2)
            }

            kbf_print("kbf_focu", sprintf("re-Running scan : %s ", _scan_cmd))

            # Nth scan
            eval(_scan_cmd)

            splot
            kbf_parabola(cnt_number)
            kbf_disp_stat(cnt_number)


            KBF_STATS["scan_number"]++
        }
        else if (rep == "b"){
            print "------APPLYING INT. MATRIX CORRECTION AND ITERATING------"

            KBF_PAR["down_bender_last"] = A[_motd_num]
            KBF_PAR["up_bender_last"]   = A[_motu_num]

            if(KBF_PAR["motors_type"] == "pico") {
                pico_mvr(_motd_num, corr1_intm)
                pico_mvr(_motu_num, corr2_intm)
            }
            else{
                _cmd1 = sprintf("mv %s %f", _motd_mne, A[_motd_num] + corr1_intm)
                _cmd2 = sprintf("mv %s %f", _motu_mne, A[_motu_num] + corr2_intm)

                eval(_cmd1)
                eval(_cmd2)
            }

            kbf_print("kbf_focu", sprintf("re-Running scan : %s ", _scan_cmd))

            # Nth scan
            eval(_scan_cmd)

            splot
            kbf_parabola(cnt_number)
            kbf_disp_stat(cnt_number)


            KBF_STATS["scan_number"]++
        }
        else if (rep == "m"){
            print "------MODIFYING CORRECTIONS (/2) AND ITERATING------"

            corr1_intm = getval("Correction on 1st actuator: ", corr1_intm/2)
            corr2_intm = getval("Correction on 2nd actuator: ", corr2_intm/2)

            if (KBF_PAR["motors_type"] == "pico") {
                pico_mvr(_motd_num, corr1_intm)
                pico_mvr(_motu_num, corr2_intm)
            }
            else{
                _cmd1 = sprintf("mv %s %f", _motd_mne, A[_motd_num] + corr1_intm)
                _cmd2 = sprintf("mv %s %f", _motu_mne, A[_motu_num] + corr2_intm)

                eval(_cmd1)
                eval(_cmd2)
            }

            kbf_print("kbf_focu", sprintf("re-Running scan : %s ", _scan_cmd))
            # Nth scan
            eval(_scan_cmd)

            splot
            kbf_parabola(ccnt)

            n++
        }
        else if (rep == "c"){
            print "-------------  CHANGE START/END  ------------"

            _relstart = getval("new start ?", _relstart)
            _relend   = getval("new end ?", _relend)

            _scan_cmd = sprintf("d2scan %s %f %f %s 0 0 %d %f",         \
                        soffmot, _relstart, _relend, gapmot, _npts, _count_time)

            print "------------- REDO THE SCAN ------------"

            kbf_print("kbf_focu", sprintf("re-Running scan : %s ", _scan_cmd))
            # Nth scan
            eval(_scan_cmd)

            splot
            kbf_parabola(cnt_number)
            kbf_disp_stat(cnt_number)


            KBF_STATS["scan_number"]++
        }
        else if (rep == "r"){
            print "------------- REDO THE SCAN ------------"
            print "re-Running scan : " _scan_cmd
            # Nth scan
            eval(_scan_cmd)
            splot
            kbf_parabola(cnt_number)
            kbf_disp_stat(cnt_number)

            KBF_STATS["scan_number"]++
        }
        else if (rep == "q"){
            print "---------- ENDING ITERATIONS -----------"
            # PC / SB : we do not try to cancel all corrections
            ## Come-back to initial positions.
            #_cmd1 = sprintf("mv %s %f", _motd_mne, oldd)
            #_cmd2 = sprintf("mv %s %f", _motu_mne, oldu)
            #printf("Ok. Returning to original positions...please wait")
            #eval(_cmd1)
            #eval(_cmd2)
            exit
        }
        else if (rep == "e"){
            print "---------- Exit and cancel LAST corrections  -------- "
            # Come-back to PREVIOUS positions.
            _cmd1 = sprintf("mv %s %f", _motd_mne, KBF_PAR["down_bender_last"])
            _cmd2 = sprintf("mv %s %f", _motu_mne, KBF_PAR["up_bender_last"])
            printf("Ok. Returning to previous positions... please wait. \n")
            eval(_cmd1)
            eval(_cmd2)
            exit
        }
        else if(rep == "x"){
            # Come-back to ORIGINAL positions.
            print "----- Exit and cancel ALL corrections  -------"
            _cmd1 = sprintf("mv %s %f", _motd_mne, KBF_PAR["down_bender_orig"])
            _cmd2 = sprintf("mv %s %f", _motu_mne, KBF_PAR["up_bender_orig"])
            printf("Ok. Returning to original positions... please wait. \n")
            eval(_cmd1)
            eval(_cmd2)
            exit
        }
        else if (rep == "h"){
            print "------- DISPLAY HISTORY  ----"
            kbf_disp_history
        }
        else {
            print "ERROR"
        }
    }

    _kbf_cleanup

}'


def _kbf_cleanup '{
    if(KBF_PAR["BS_to_reactivate"]){
        if (PLOT_MODE & 0x40){
            # BS already active
        }
        else{
            kbf_msg("re activation background substraction for SPEC ploting")
            setplot -0x40
        }
        KBF_PAR["BS_to_reactivate"] = 0
    }

    cdef("cleanup_once", "", "_kbf_focus_", "delete")

}'


#%UU% []
#%MDESC%
#
def kbf_parab(x) '{
    global KBF_FIT_PARAM[]

    return KBF_FIT_PARAM[0] * x * x +  KBF_FIT_PARAM[1] * x + KBF_FIT_PARAM[2]
}'



#%IU% (<title>, <message>)
#%MDESC%
#    Prints title in bold and message in regular font.
def kbf_print(title, message) '{
    tty_cntl("md"); printf("KBF");
    printf("--%s", title); tty_cntl("me")
    printf("--%s\n", message)
}'



# AUTHORS : V.Rey, C.Guilloud
# ALGO : O.Hignette, ?