spec

Software for Diffraction

3.5. - Motor Macros



mv motor pos            # Move a motor
mvr motor pos           # Move a motor, relatively
mvd motor dial_pos      # Move a motor to a dial position
tw motor inc            # Tweak a motor, interactively

umv motor pos           # Move while updating screen
umvr motor pos          # Move while updating screen

wa                      # Show positions of all motors
lm                      # Show limits of all motors
wm m1 m2 ...            # Show positions and limits of motors
uwm m1 m2 ...           # Show positions while motors are moving

set motor pos           # Set user angle for a motor
set_dial motor pos      # Set dial angle for a motor
set_lm motor low high   # Set user limits for a motor

an tth_pos th_pos       # Move two theta and theta
pl chi_pos phi_pos      # Move chi and phi (four-circle)
uan tth_pos th_pos      # Move while updating screen
upl chi_pos phi_pos     # Move while updating screen


The following macro moves a single motor, adding a comment to the printer that the motor was moved:
# Move a single motor
def mv  '_mv $*; move_poll'
def umv '_mv $*; _update1 $1'   # "update" version of mv
def _mv '
      if ($# != 2) {
              print "Usage:  mv motor position"
              exit
      }
      _check0 "$1"
      waitmove; getangles; A[$1]=$2
      if (PRINTER != ")
              fprintf(PRINTER,"\nmv $1 %g\n", A[$1])
      move_em
'


In mv, as in all the macros that move motors, the move_em macro is invoked, rather than the move_all command. Normally, move_em is defined as
def move_em '
      user_premove
      move_all
      user_postmove
'
One can define the user_premove and/or user_postmove macros to take into account special conditions. For example, to check for limits that depend on the relative position of motors, one could define user_premove as
def user_premove '
      if (fabs(A[tth] - A[th]) > 10) {
              print "Move exceeds Theta - Two Theta relative limit."
              exit
      }
      move_all
'



The set macro changes the offset between user and dial units.
# Define a new motor position
def set '
      if ($# != 2) {
              print "Usage:  set motor new_user_value"
              exit
      }
      {
        local old
        _check0 "$1"
        waitmove; getangles
        old = A[$1]
        if (chg_offset($1, $2))
          exit
        getangles
        if (old != A[$1]) {
          comment "%s reset from %g to %g" "motor_name($1), old, A[$1]"
        } else
          print "No change."
      }
'


The set_dial macro changes the dial position of the motor, which means a change to the contents of the motor controller register. set_dial refuses to set the dial beyond the current software limits for the motor. set_dial also changes the offset to maintain the prior value of the user angle. These two macros document the change in the data file and on the printer.

The set_lm macro converts the user-unit arguments to dial units for the call to set_lim().
Change a motor limit
def set_lm '
      if ($# != 3) {
              print "Usage:  set_lm motor low high"
              exit
      }
      {
          _check0 "$1"
          if (!set_lim($1, dial($1, $2), dial($1, $3))) {
              onp
              printf("\n%s limits set to %g %g (dial units).\n",\
                      motor_name($1), get_lim($1, -1), get_lim($1, +1))
              offp
          }
      }
'



The macros in the above list that begin with a u continuously read motor positions from the controller and show the positions on the screen. The frequency of screen updates is set by the global variable UPDATE, which is used as an argument to the sleep() function. Setting UPDATE=.25 places a 1/4 second pause between updates. The umv macro first calls _mv and then calls the internal _update1 macro. The other updated-move macros are defined similarly.
def umv _'mv $*; _update1 $1 '   # "update" version of mv

# Displays updated position of 1 motor while it is moving
def _update1 '
      if (chk_move)) {
              printf("\n%10.9s\n", motor_name($1))
              while (wait(0x22)) {
                      getangles
                      printf("%10.4f\r", A[$1])
                      sleep(UPDATE)
              }
              getangles
              printf("%10.4f\n", A[$1])
      }
'


The technique for displaying status information about all the motors is a little complicated. spec places no restriction on what order the motors are assigned to the controller, but does recognize that there is a preferred order for displaying motor information. To this end, the macros use an array mA[] which contains reordered motor numbers. The four-circle macro source file contains the following code, which is executed when the command file is read and when the config macro is run.
# Conventionally, the first four motors are tth, th, chi, phi.
# The following code guarantees this.
def _assign '{
      local   i j
      mA[0]=tth
      mA[1]=th
      mA[2]=chi
      mA[3]=phi
      for (i = 4, j = 0; i < MOTORS; j++) {
              if (j == tth || j == th || j == chi || j == phi)
                      continue
              mA[i++] = j
      }
}'
Similar code is contained in the macro source files for the other geometries.


An internal macro named _mo_loop exists to loop through all the motors printing selected fields. Its use is best illustrated by example. First here is its definition:
# Looping routine used in many macros.
# Normally k is set to MOTORS, but can be set to something else, e.g., 4
# (Kludge with printf(" ") avoids auto linefeed on 80th column.)
def _mo_loop '{
      local s
      for (j = i; j < i + 8 && j < k; j++)
              if (motor_name(mA[j]) != "unused") {
                      s = s sprintf("%$1", $2)
                      if (j < i + 7)
                              s = s " "
              }
      print s
}'
It is within this macro that motors named unused are not used in printing motor information.

The wa macro that displays information for all motors is typical of a macro that calls the _mo_loop macro.
# Where - all motors
def wa '
      waitmove; get_angles
      onp
      printf("\nCurrent Positions  (user, dial)\n")
      {
              local i j k
              for (i = 0, k = MOTORS; i < k; i += 8) {
                      _mo_loop 9.9s "motor_name(mA[j])"
                      _mo_loop 9.9s "motor_mne(mA[j])"
                      _mo_loop 9.4f "A[mA[j]]"
                      _mo_loop 9.4f "dial(mA[j], A[mA[j]])"
              }
      }
      offp
'
The first argument for _mo_loop is a printf() field specification, the second argument is the field value. The field values use the mA[] array to reorder the motor numbers.