esrf

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

#%TITLE% TAB3.MAC
#%NAME%
#  Macros to control three leg table(s) with logical motors
#             xtilt, ytilt and height
#
#
#
#
#    DOC : http://wikiserv.esrf.fr/bliss/index.php/Tab
#
#you must have :
#
#    3 real motors configured.
#    3 macro-motors
#        1 is considered as "master"
#
#    ALL motors (real and macro) must have a 'master' field in their custom parameters to know which is the master.
#
#    the master macmot has 9 or 10 supplementary custom parameters:
#        lb1mne  : real motor mnemonic for leg back 1
#        lb2mne  : real motor mnemonic for leg back 2
#        lfmne  : real motor mnemonic for leg front
#        height  : macro motor mnemonic for Z/Height
#        xtilt  : macro motor mnemonic for first tilt
#        ytilt  : macro motor mnemonic for second tilt
#        d1  : custom distance 1
#        d2  : custom distance 2
#        (d3)  : custom distance 3
#        geometry : number 0..7
#
#
#
#
#%DESCRIPTION%
#  A table with three legs in triangle. One in the front and two in the        equilateral triangle ???
#  back is a common positioning element in ESRF beamlines. This macros
#  implement tilts and height for this kind of table as pseudomotors.
#  This macros can use different geometries.
#  More than one table can be defined in this way in the same spec
#  application.%BR%
#
#  For geometry 0 (standard):%BR%
#     Xtilt is the angle defined by the two back legs.
#     Fixed a middle point B between the two back legs. The height in the
#     middle point between B and the front leg gives the value for the
#     motor height. The motor Ytilt is defined as the angle existing
#     between B and front leg.
#
#     Two parameters must be input to the system: the distance between the
#     two back legs and the distance between B and the front leg.
#     In this geometry distances are given in mm. Angles in mrad.%BR%
#
#  For geometry 1 (side front leg):%BR%
#     The front leg is on the same side and aligned with the first motor.       same side than what ???
#                                                                               first motor ???
#     The fixed point is in the middle of the mirror. For positive x tilt       position of mirror ??
#     is motor 1 higher up then motor 2. For positive y tilt is the
#     front motor higher up then the two back motors.%BR%
#
#  For geometry 2 (ID22 mirror):%BR%
#     Xtilt is the angle defined by the two back legs, but only leg2 moves
#     when a xtilt is requested.
#     Ytilt and Height are defined in the line defined by leg1 and front.
#     For the two movements leg2 follows leg1.%BR%
#
#  For geometry 3 (ID21 mirror):%BR%
#     Xtilt is the angle defined by the two back legs.
#     Fixed a middle point B between the two back legs. The value of this       diff with geo: position of Height
#     point gives the value for the motor Height. The motor Ytilt is
#     defined as the angle existing between B and front leg.
#     Two parameters must be input to the system: the distance between the
#     two back legs and the distance between B and the front leg.
#     In this geometry distances are given in mm. Angles in mrad.%BR%
#
#  For geometry 4 (ID20):%BR%
#
#  For geometry 5 (ID29 mirror):%BR%
#     Xtilt is the angle defined by the two back legs.
#     B is the middle distance point between the two back legs. C is at
#     fixed distance between B and the front leg, used to calculate the
#     height. The Ytilt is defined as the angle between B and the front leg.
#     Three parameters needed: the distance between the two back legs,
#     between B and the front leg and between C and the front leg (all
#     given in mm). Calculated angles in mrad.%BR%
#
#  For geometry 6 (ID30):%BR%
#
#
#  For geometry 7 (ID24 polychromator crystal tripod):%BR%
#    2 back legs (in front of mirror). let B the middle of b1 b2.
#    1 front leg (back of mirror).
#    (front / back are counter-intuitive to reflect the symetrie of the tripod :
#     1 single leg in back and 2 legs in front of the mirror)
#    3 legs are placed as an equilateral triangle.
#    let C the center of the triangle. [B;C] = 1/3 [B;leg2].
#    Height is considered at the triangle center.
#    Xtilt (PSI) is rotation along axis [B;leg2].
#    Ytilt (CHI) is rotation along axis perpanducular to [Xtilt] passing by C.
#    Height (Z)  is considered in C.
#    See pictur on bliss wiki.
#
#  For geometry 8 (ID15A tripod lenses):
#
#            t2 (front)       
#              (*)
#               | 
#    -----------|---------> X
#               |      
#       (*)     |    (*)
#    t3 (leg2)  |  t1 (leg1)
#              lens 
#
#    t1/t2/3 corresponds to motor labels
#    d1= length(t1:t2) = length(t2:t3) = length(t3:t1)
#    d2= length(middle(t1:t3):t2)
#    d3= height of lens aperture from tripod base 

#%IU% [parma]
#%MDESC%
#
def tab3_config(motornum, type, par1, par2) '{

   __tab3_debug ">>> tab3_config: ",motornum, type, par1, par2
    if (motornum == "..") {
    }
    else{
        if (type == "mot") {
            # Returns the list of all REAL motors involved in the macro motor(s).
            return tab3_motconfig(motornum, par1, par2)
        }
    }
}'


#%IU% (<motnum>, <mode>, <unit>)
#%MDESC%
#
def tab3_calc(motnum, mode, unit) '{

    __tab3_debug ">>> tab3_calc: ",motnum, mode, unit

   local  master_motnum
   master_motnum = 0

   # motor number
   local lb1num lb2num lfnum
   local hgtnum xtnum ytnum
   local d1, d2, d3, geo

   # lenghts/angles
   local t3_bl1 t3_bl2 t3_fro
   local t3_xti t3_yti t3_hgt

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

   # Get "master" motor.
   if( (master_motnum = motor_num(motor_par(motnum, "master")) ) == -1){
       __tab3_debug ">>> tab3_calc: master is: ", motor_mne(master_motnum)
       printf("t3mh.mac--tab3_calc()--ERROR--cannot get \"master\" for motor %s(no %d)\n", \
              motor_mne(motnum), motnum)
       print "   ---> exit"
       exit
   }
   else{
       __tab3_debug ">>> tab3_calc: master is: ", motor_mne(master_motnum)
   }


   # Get legs motor numbers.
   if ((lb1num = motor_num(motor_par(master_motnum, "lb1mne"))) == -1){
       __tab3_debug ">>> tab3_calc: lb1num: ", motor_mne(lb1num)
       print "t3mh.mac--tab3_calc()--ERROR--cannot get \"lb1mne\" motor (all motors must have \"master\" field !!!)"
   }
   else{
       __tab3_debug ">>> tab3_calc: lb1num: ", motor_mne(lb1num)
   }

   if ((lb2num = motor_num(motor_par(master_motnum, "lb2mne"))) == -1){
       __tab3_debug ">>> tab3_calc: lb2num: ", motor_mne(lb2num)
       print "t3mh.mac--tab3_calc()--ERROR--cannot get \"lb2mne\" motor"
       print "   ---> exit"
       exit
   }
   else{
       __tab3_debug ">>> tab3_calc: lb2num: ", motor_mne(lb2num)
   }

   
   if ((lfnum  = motor_num(motor_par(master_motnum, "lfmne"))) == -1){
       __tab3_debug ">>> tab3_calc: lb3num: ", motor_mne(lfnum)
       print "t3mh.mac--tab3_calc()--ERROR--cannot get \"lfmne\" motor"
       print "   ---> exit"
       exit
   }
   else{
       __tab3_debug ">>> tab3_calc: lb3num: ", motor_mne(lfnum)
   }


   # Numbers of fake motors.
   if ((hgtnum = motor_num(motor_par(master_motnum, "height"))) == -1){
       print "t3mh.mac--tab3_calc()--ERROR--cannot get \"height\""
       print "   ---> exit"
       exit
   }

   if ((xtnum  = motor_num(motor_par(master_motnum, "xtilt"))) == -1){
       print "t3mh.mac--tab3_calc()--ERROR--cannot get \"xtilt\""
       print "   ---> exit"
       exit
   }

   if ((ytnum  = motor_num(motor_par(master_motnum, "ytilt"))) == -1){
       print "t3mh.mac--tab3_calc()--ERROR--cannot get \"ytilt\""
       print "   ---> exit"
       exit
   }


   # Geometrical parameters.
   if ((d1  = motor_par(master_motnum, "d1")) == -1){
       print "t3mh.mac--tab3_calc()--ERROR--cannot get \"d1\""
       print "   ---> exit"
       exit
   }

   if ((d2  = motor_par(master_motnum, "d2")) == -1){
       print "t3mh.mac--tab3_calc()--ERROR--cannot get \"d2\""
       print "   ---> exit"
       exit
   }

   if ((d3  = motor_par(master_motnum, "d3")) == -1){
       print "t3mh.mac--tab3_calc()--ERROR--cannot get \"d3\""
       print "   ---> exit"
       exit
   }

   if ((geo = motor_par(master_motnum, "geometry")) == -1){
       print "t3mh.mac--tab3_calc()--ERROR--cannot get \"geometry\""
       print "   ---> exit"
       exit
   }


   if ( mode == 0 ) {
       # MACRO MOTORS CALCULATION

       t3_bl1 = A[lb1num]
       t3_bl2 = A[lb2num]
       t3_fro = A[lfnum]

       t3_xti = A[xtnum]
       t3_yti = A[ytnum]
       t3_hgt = A[hgtnum]


       if (motnum == hgtnum) {
          # height
           A[motnum] = tab3_tf( 5, t3_bl1, t3_bl2, t3_fro, t3_xti, t3_yti, t3_hgt, geo, d1,d2,d3)
       }
       else if (motnum == xtnum) {
           # xtilt
           A[motnum] = tab3_tf( 3, t3_bl1, t3_bl2, t3_fro, t3_xti, t3_yti, t3_hgt, geo, d1,d2,d3)
       }
       else if (motnum == ytnum) {
           # ytilt
           A[motnum] = tab3_tf( 4, t3_bl1, t3_bl2, t3_fro, t3_xti, t3_yti, t3_hgt, geo, d1,d2,d3)
       }
   }
   else {
       # REAL MOTOR CALCULATION

       t3_bl1 = A[lb1num]
       t3_bl2 = A[lb2num]
       t3_fro = A[lfnum]

       t3_xti = A[xtnum]
       t3_yti = A[ytnum]
       t3_hgt = A[hgtnum]

       __tab3_debug ">>> tab3_calc: -------------------------------- mode 1"
       if ( motnum == -1 ) {
           print "Mne " mne ".Motnum: " motnum
       }
       else if ( motnum == lb1num) {
           # leg back 1
           A[motnum] = tab3_tf( 0, t3_bl1, t3_bl2, t3_fro, t3_xti, t3_yti, t3_hgt, geo, d1,d2,d3)
       }
       else if ( motnum == lb2num) {
           # leg back 2
           A[motnum] = tab3_tf( 1, t3_bl1, t3_bl2, t3_fro, t3_xti, t3_yti, t3_hgt, geo, d1,d2,d3)
       }
       else if ( motnum == lfnum)  {
           # leg front
           A[motnum] = tab3_tf( 2, t3_bl1, t3_bl2, t3_fro, t3_xti, t3_yti, t3_hgt, geo, d1,d2,d3)
       }
   }
   #local myval mymne
   #myval = A[motnum]
   #mymne = motor_mne(motnum)
   #__tab3_debug ">>> tab3_calc: motor ",mymne , " set to : ",myval
   __tab3_debug ">>> tab3_calc: motor ", motor_mne(motnum), " is: ", A[motnum]
}'


#%IU% (<motnum>)
#%MDESC%
#
# -<motnum>  :
# -<unit> :
# -<chan> :
def tab3_motconfig(motnum) '{
    local _dependant_motors_list
    local master_motnum

    master_motnum = motor_num(motor_par(motnum, "master"))

    if (master_motnum < 1){
        print "tab3mh--ERROR--no master motor defined."
        print "   -> exit"
        exit
    }

    # Finds 3 leg motors number in special parameters of master motor.
    _dependant_motors_list = ""
    _dependant_motors_list = _dependant_motors_list     motor_par(master_motnum, "lb1mne")
    _dependant_motors_list = _dependant_motors_list " " motor_par(master_motnum, "lb2mne")
    _dependant_motors_list = _dependant_motors_list " " motor_par(master_motnum, "lfmne")

    __tab3_debug ">>> tab3_config: dependant motor list: ",  _dependant_motors_list

    return _dependant_motors_list
}'


#%IU% (calcidx , tbl1, tbl2, front, xti, yti, z, geometry , d1, d2,d3)
#%MDESC% calcidx gives the index of the value to be calculated. Indexes are
# starting from 0 (tbl1) and go to 5 (z). The 3 leg motors and the 3 pseudo
# motors are all the time given. Depending on calcidx only 3 of them are used
# in the calculation. The geometry allows for different geometries. d1 and d2 are
# the distances between the legs. d3 is the distance between the front leg
#and the center of the rotation.
def tab3_tf(calcidx, t3_bl1, t3_bl2, t3_fro, t3_xti, t3_yti, t3_hgt, geometry, d1, d2, d3) '{
  local reverse res

  __tab3_debug ">>> tab3_tf: ", calcidx, t3_bl1, t3_bl2, t3_fro, t3_xti, t3_yti, t3_hgt, geometry, d1, d2, d3
  if (calcidx > 2 ){
      reverse = 1
  }

  if (geometry == 0) {
    if (reverse) {
      res[3] = 1000*atan( (t3_bl2 - t3_bl1) / d1 ) # xti
      res[4] = 1000*atan( ( (t3_bl1 + t3_bl2)/2 - t3_fro) / d2) # yti
      res[5] = (t3_bl1 + t3_bl2 + 2*t3_fro)/4 # z
    } else {
      res[0] = t3_hgt - d1 * tan(t3_xti/1000) / 2+d2*tan(t3_yti/1000)/2 # tbl 1
      res[1] = t3_hgt + d1 * tan(t3_xti/1000) / 2+d2*tan(t3_yti/1000)/2 # tbl 2
      res[2] = t3_hgt - d2 * tan(t3_yti/1000) / 2  # front
    }
  }
  if (geometry == 1) {
    if (reverse) {
      res[3] = 1000*atan( (t3_bl2 - t3_bl1) / d1 ) # xti
      res[4] = 1000*atan( (t3_fro - t3_bl1) / d2 ) # yti
      res[5] = (t3_bl2 + t3_fro)/2 # z
    } else {
      res[0] = t3_hgt - d1 * tan(t3_xti/1000) / 2 - d2*tan(t3_yti/1000) / 2  # tbl 1
      res[1] = t3_hgt + d1 * tan(t3_xti/1000) / 2 - d2*tan(t3_yti/1000) / 2  # tbl 2
      res[2] = t3_hgt + d2 * tan(t3_yti/1000) / 2 - d1*tan(t3_xti/1000) / 2  # front
    }
  }
  if (geometry == 2) {
    if (reverse) {
      res[3] = deg(atan( (t3_bl2 - t3_bl1) / d1 ))  # xti
      res[4] = deg(atan( (t3_bl1 - t3_fro) / d2 )) # yti
      res[5] = (t3_bl1 + t3_fro)/2            # z
    } else {
      res[0] = t3_hgt + d2 * tan(rad(t3_yti))/2                 # tbl 1
      res[1] = t3_hgt + d2 * tan(rad(t3_yti))/2 + d1 * tan(rad(t3_xti)) # tbl 2
      res[2] = t3_hgt - d2 * tan(rad(t3_yti))/2                 # front
    }
  }
  if (geometry == 3) {
    if (reverse) {
      res[3] = 1000*atan( (t3_bl2 - t3_bl1) / d1 ) # xti
      res[4] = 1000*atan( ( (t3_bl1 + t3_bl2)/2 - t3_fro) / d2) # yti
      res[5] = (t3_bl1 + t3_bl2)/2 # z
    } else {
      res[0] = t3_hgt - d1 * tan(t3_xti/1000) / 2 # tbl 1
      res[1] = t3_hgt + d1 * tan(t3_xti/1000) / 2 # tbl 2
      res[2] = t3_hgt - d2 * tan(t3_yti/1000) # front
    }
  }

  if (geometry == 4) {
    if (reverse) {
      res[3] = 1000*atan( (t3_bl2 - t3_bl1) / d1 )               # xti
      res[4] = 1000*atan( ( t3_fro - (t3_bl1 + t3_bl2)/2 ) / d2) # yti
      res[5] = (t3_bl1 + t3_bl2 + 2*t3_fro)/4                    # hgt
    } else {
      res[0] = t3_hgt - d1 * tan(t3_xti/1000) / 2 - d2 * tan(t3_yti/1000) / 2 # tbl 1
      res[1] = t3_hgt + d1 * tan(t3_xti/1000) / 2 - d2 * tan(t3_yti/1000) / 2 # tbl 2
      res[2] = t3_hgt + d2 * tan(t3_yti/1000) / 2                             # front
    }
  }




  if (geometry == 5) {
    if (reverse) {
      res[3] = 1000*atan( (t3_bl2 - t3_bl1) / d1 ) # xti
      res[4] = 1000*atan( ( (t3_bl1 + t3_bl2)/2 - t3_fro) / d2) # yti
      res[5] = t3_fro + ( (t3_bl1 + t3_bl2)/2 - t3_fro) * (d3/d2) #z
    } else {
      res[0] = t3_hgt - d1 * tan(t3_xti/1000) / 2 + (d2 - d3)*tan(t3_yti/1000)  #tbl 1
      res[1] = t3_hgt + d1 * tan(t3_xti/1000) / 2 + (d2 - d3)*tan(t3_yti/1000)  #tbl 2
      res[2] = t3_hgt - d3 * tan(t3_yti/1000) #front
    }
  }



  if (geometry == 6) {
    if (reverse) {
      res[3] = deg( atan( (t3_bl2 - t3_bl1) / d1 ) )              # xti
      res[4] = deg( atan( ( (t3_bl1 + t3_bl2)/2 - t3_fro) / d2) ) # yti
      res[5] = (t3_bl1 + t3_bl2) / 2                              # z
    } else {
      res[0] = t3_hgt - d1 * tan( rad(t3_xti) ) / 2  # tbl 1
      res[1] = t3_hgt + d1 * tan( rad(t3_xti) ) / 2  # tbl 2
      res[2] = t3_hgt - d2 * tan( rad(t3_yti) )      # front
    }
  }


  # ID24 tripod for polychromator crystal.
  if (geometry == 7) {
    if (reverse) {
      if(d2*d1 == 0){
          print "tab3_tf--ERROR : d1 or d2 == 0"
          print "---> exit"
          exit
      }
      else{
          res[3] = deg(atan((1/d1) * (t3_bl2 - t3_bl1)))              # Xtilt / PSI (deg)
          res[4] = deg(atan((1/d2) * (0.5 *(t3_bl1 + t3_bl2) - t3_fro)))  # Ytilt / CHI (deg)
          res[5] = (1/3) * (t3_bl1 + t3_bl2  + t3_fro)                  # Height / z  (mm)
          #
          __tab3_debug ">>> tab3_tf: from leg1: ", t3_bl1, " leg3: ", t3_bl2, " leg2: ", t3_bl3
          __tab3_debug ">>> tab3_tf: PSI: ", res[3], " CHI: ", res[4], " Z: ", res[5]
      }
    }
    else {
         res[0] = t3_hgt - 0.5 * tan(rad(t3_xti)) *d1 + (1/3) * tan(rad(t3_yti)) * d2   # tbl 1   (mm)  / leg1
         res[1] = t3_hgt + 0.5 * tan(rad(t3_xti)) * d1  + (1/3) * tan(rad(t3_yti)) * d2 # tbl 2   (mm)  / leg3
         res[2] = t3_hgt - 2/3 * tan(rad(t3_yti)) * d2                                  # front   (mm)  / leg2
         #
         __tab3_debug ">>> tab3_tf: from PSI: " , t3_xti , " CHI: " , t3_yti , " Z: ",t3_hgt
         __tab3_debug ">>> tab3_tf: leg1: " , res[0] , " leg3: " , res[1] , " leg2: "  ,res[2]
    }
  }

  if (geometry == 8) {
    if (reverse) {
      local xti yti zlegs zcorr
      xti = atan((1/d2) * (0.5 *(t3_bl1 + t3_bl2) - t3_fro))
      yti = atan((1/d1) * (t3_bl2 - t3_bl1))
      zlegs = (1/3) * (t3_bl1 + t3_bl2  + t3_fro)
      if (d3 > 0) {
        zcorr = d3 * (2 - cos(xti) - cos(yti))
      } else {
        zcorr = 0
      }
      res[3]= deg(xti)      # xtilt (deg)
      res[4]= deg(yti)      # ytilt (deg)
      res[5]= zlegs - zcorr # height (mm)

      __tab3_debug ">>> tab3_tf: from leg1: ", t3_bl1, " leg2: ", t3_bl2, " leg3: ", t3_bl3
      __tab3_debug ">>> tab3_tf: to xtilt: ", res[3], " ytilt: ", res[4], " Z: ", res[5]
    }
    else {
      local zcorr zbase
      if (d3 > 0) {
         zcorr = d3 * (2 - cos(rad(t3_xti)) - cos(rad(t3_yti)))
      } else {
         zcorr = 0
      }
      zbase = t3_hgt + zcorr
      res[0] = zbase - 0.5 * tan(rad(t3_yti)) * d1 + (1/3) * tan(rad(t3_xti)) * d2  # tbl 1   (mm)  / leg1
      res[1] = zbase + 0.5 * tan(rad(t3_yti)) * d1 + (1/3) * tan(rad(t3_xti)) * d2  # tbl 2   (mm)  / leg3
      res[2] = zbase - 2/3 * tan(rad(t3_xti)) * d2                                  # front   (mm)  / leg2

      __tab3_debug ">>> tab3_tf: from xtilt: ", t3_xti, " ytilt: ", t3_yti, " Z: ", t3_hgt
      __tab3_debug ">>> tab3_tf: to leg1: ", res[0], " leg2: ", res[1], " leg3: ", res[2]
    }
  }

  return res[calcidx];
}'


#%UU%
#%MDESC%
#    Toggles the debug mode.
if (!(whatis("__tab3_debug")  & 2)) rdef __tab3_debug \'#$*\'

def tab3_debug '{
  if ((whatis("__tab3_debug")>>16) <= 3) { # macro length is 3 bytes: comment
     rdef __tab3_debug "eprint"
     print "tab3 macro debug mode is ON"
  }
  else {
     rdef __tab3_debug \'#\$*\'
     print "tab3 macro debug mode is OFF"
  }
}'


#%UU%(calcidx, t3_bl1, t3_bl2, t3_fro, t3_xti, t3_yti, t3_hgt, geometry, d1, d2, d3)
#%MDESC% Calculates [t3_bl1, t3_bl2, t3_fro] as function of [t3_xti, t3_yti, t3_hgt] if
#calcidx is <=2, and the inverse if calcidx >2
#%BR%- geometry: only geometry 7 is calculated (ID24)
#%BR%- d1 dimension: 233.83 for ID24
#%BR%- d2 dimension: 202.5 for ID24
#%BR%- d3 dimension: unused for ID24
def tab3_u(calcidx, t3_bl1, t3_bl2, t3_fro, t3_xti, t3_yti, t3_hgt, geometry, d1, d2, d3)'{
# tab3_calcu(1, x, x, x, psi, chi, z, 7, d1, d2, 0)
# tab3_calcu(4, leg1, leg3, leg2, x, x, x, 7, d1, d2, 0)
   local res
   if (geometry == 7) {
     if (calcidx > 2 ){
          res[3] = deg(atan((1/d1) * (t3_bl2 - t3_bl1)))              # Xtilt / PSI (deg)
          res[4] = deg(atan((1/d2) * (0.5 *(t3_bl1 + t3_bl2) - t3_fro)))   # Ytilt / CHI (deg)
          res[5] = (1/3) * (t3_bl1 + t3_bl2  + t3_fro)                  # Height / z  (mm)
          __tab3_debug ">>> tab3_tf: (mm) from leg1: ", t3_bl1, " leg3: ", t3_bl2, " leg2: ", t3_bl3
          __tab3_debug ">>> tab3_tf: (deg) PSI: ", res[3], " CHI: ", res[4], " Z: ", res[5]
      }
      else {
         res[0] = t3_hgt - 0.5 * tan(rad(t3_xti)) *d1 + (1/3) * tan(rad(t3_yti)) * d2    # tbl 1   (mm)  / leg1
         res[1] = t3_hgt + 0.5 * tan(rad(t3_xti)) * d1  + (1/3) * tan(rad(t3_yti)) * d2 # tbl 2   (mm)  / leg3
         res[2] = t3_hgt - 2/3 * tan(rad(t3_yti)) * d2                                  # front   (mm)  / leg2
         __tab3_debug ">>> tab3_tf: (deg) from PSI: ", t3_xti, " CHI: ", t3_yti, " Z: ", t3_hgt
         __tab3_debug ">>> tab3_tf: (mm) leg1: ", res[0], " leg3: ", res[1], " leg2: ", res[2]

      }
   }
}'


#%MACROS%
#%IMACROS%
#%DEPENDENCIES%
#%AUTHOR% Vicente Rey. June 1995
#$Revision: 1.5 $, $Date: 2018/03/22 10:23:58 $
#%TOC%