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.
