Files
motorBase/docs/trajectoryScan.html

1637 lines
68 KiB
HTML

<HTML>
<HEAD><TITLE>Trajectory Scanning</TITLE>
</HEAD>
<BODY>
<CENTER>
<H1> Trajectory Scanning with the Newport MM4005, Newport XPS, Aerotech Ensemble, and Pro-Dex MAXv
Motor Controllers</H1>
<H2> Mark Rivers</H2>
<H2> June 29, 2007</H2>
<H2> (Additions for Ensemble and MAXv: Tim Mooney, April 4, 2014)</H2>
</CENTER>
<H2>Contents</H2>
<UL>
<LI><A href="#Overview">
Overview</A>
<LI><A href="#Implementation">
Implementation</A>
<LI><A href="#Safety">
Safety</A>
<LI><A href="#Notation">
Notation</A>
<LI><A href="#Defining a Trajectory">
Defining a Trajectory</A>
<LI><A href="#Building a Trajectory">
Building a Trajectory</A>
<LI><A href="#Executing a Trajectory">
Executing a Trajectory</A>
<LI><A href="#Reading Back a Trajectory">
Reading Back a Trajectory</A>
<LI><A href="#Interaction with EPICS Motor Records">
Interaction with EPICS Motor Records</A>
<LI><A href="#Communication with the controller">
Communication with the controller</A>
<LI><A href="#Hardware Notes">
Hardware Notes</A>
<LI><A href="#Installation">
Installation</A>
<LI><A href="#Loading the database">
Loading the database</A>
<LI><A href="#Starting the SNL program">
Starting the SNL program</A>
<LI><A href="#Startup script">
Startup script</A>
<LI><A href="#MEDM screens">
MEDM Screens</A>
<LI><A href="#Autosave">
Autosave</A>
<LI><A href="#Example IDL Procedure">
Example IDL Procedure</A>
<LI><A href="#SPEC_Interface">
SPEC Interface</A>
<LI><A href="#Restrictions">
Restrictions</A>
</UL>
<P>&nbsp;</P>
<H2><A name=Overview>
Overview</A></H2>
<P>The Newport MM4005 and XPS, the Aerotech Ensemble, and the Pro-Dex MAXv motor controllers are capable of executing complex
coordinated motions. Trajectories can be defined that move any or all of
the axes through any set of complex motions. (Currently, the
Ensemble trajectory support can only control a single axis.) The controller will coordinate
these motions, keeping each axis very close to the theoretical position during
the entire motion. The controller can output logic pulses during the execution
of the trajectory, permitting external equipment to be synchronized to the
motion. These capabilities are ideally suited to &#8220;on-the-fly&#8221; data
collection.</P>
<P>At the APS one application of the MM4005 and XPS is to drive the large Newport
diffractometer. This diffractometer has rather long settling times because of
the mass of the moving elements. However, with this trajectory scanning software
one can use SPEC, for example, to compute a set of diffractometer positions for
a scan in HKL space, and then download these positions to the controller. The
entire scan can be executed without stopping, collecting data in a multi-channel
scaler or other triggered buffering detector.
This can reduce data collection times dramatically relative to
traditional step scanning.</P>
<P>This document describes an EPICS interface to the trajectory capabilities of
the MM4005, XPS, Ensemble, and MAXv. This interface is completely general for the controllers, it is not
specific to the Newport diffractometer. The interface lets any EPICS channel
access client do the following:</P>
<UL>
<LI>Define the total number of trajectory elements.
<LI>Define the absolute or relative position of each axis for each point in
the trajectory.
<LI>Define the time for each element of the trajectory, or alternatively the
total execution time with equal time per trajectory element.
<LI>Define the total number of output synchronization pulses. <i>The MAXv can
only send output pulses at trajectory points.</i>
<LI>Define the trajectory elements where pulse outputs begin and end.
<LI>Build and verify the trajectory, checking for errors.
<LI>Define a total time scaling factor from .01 to 100 that will speed up or
slow down the trajectory execution relative to its original definition
(MM4005, MAXv, and Ensemble only).
<LI>Execute the trajectory, checking for completion and errors. This can be
done repeatedly without rebuilding if the only changes are in the start
position or the execution time scale factor. <i>The MAXv must load the
trajectory before every execution.</i>
<LI>Read back the actual position of each axis when each synchronization pulse
was output. <i>The Ensemble and MAXv cannot do this. For these controllers,
position data are usually recorded with a multichannel scaler to which motor
pulses (MAXv) or PSO pulses (Ensemble) are sent. The Ensemble can record
and read back position error with an internal scope.</i>
<LI>Read back the following error (actual-theoretical positions) when each
synchronization pulse was output. <i>The Ensemble and MAXv cannot do this.</i>
</UL>
<P>&nbsp;</P>
<H2><A name=Implementation>
Implementation</A></H2>
<P>The EPICS implementation consists of the following:</P>
<UL>
<LI>A database file, <CODE>trajectoryScan.db.</CODE> This database
contains almost no "logic" with no links between records in the database. The
records are simply variables that channel access clients and the State
Notation Language (SNL) program use.
<LI>SNL programs, <CODE>MM4005_trajectoryScan.st, XPS_trajectoryScan.st,
MAX_trajectoryScan.st, and EnsembleTrajectoryScan.st</CODE>. These programs
implement all of the logic for communicating with the controller and with
channel access clients via the database.
<LI>MEDM screens, <CODE>trajectoryScan.adl,
trajectoryScanDebug.adl, trajectoryPlot.adl</CODE>. These screens are
used to control the building, execution, readback, debugging and plotting of
trajectory scans. <i>MAXv and Ensemble currently use a different display:
<CODE>MAX_trajectoryScan.adl</CODE>.</i>
</UL>
<P>&nbsp;</P>
<H2><A name=Safety>
Safety</A></H2>
<P>The MM4005 and XPS are used at the APS to control the large Newport
diffractometer. This device is capable of moving large masses at high speeds.
The trajectory scanning software does not use the EPICS motor record, and so
does not obey any software limits defined in the motor record. It is very
important that:</P>
<UL>
<LI>The controller be programmed via the front panel (MM4005) or Web interface (XPS)
to have safe software limits
to prevent collisions whenever possible. The trajectory scanning software does
obey the MM4005 internal soft limits. However, the MultipleAxesPVTExecution command
on the XPS <i>does not obey the soft limits</i>! The XPS trajectory scan SNL code
checks that the limits of the trajectory do not exceed the soft limits, and will not
execute the trajectory if they do.
<LI>Care be taken to avoid trajectory execution whenever personnel could be
harmed. The following error thresholds for each axis on the controller should be
set tight enough that the motor power will turn off when a significant
resistance is met. An emergency stop button should be within reach whenever
personnel are working on the diffractometer, and the switch should be in the
Stop position whenever personnel are working for extended periods or in
vulnerable positions on the diffractometer.
</UL>
<P>&nbsp;</P>
<H2><A name=Notation>
Notation</A></H2>
<P>
The database is loaded with <CODE>$(P)</CODE> (prefix) and <CODE>$(R)</CODE>
(record base) macros. For example, <CODE>$(P)</CODE> might be
<CODE>13IDC:</CODE> for the name of the IOC, and <CODE>$(R)</CODE> might be
<CODE>Traj1:</CODE> for the first trajectory in this IOC. The prefix and
record bases are
omitted from the Process Variable (PV) names in this document, but one should be
aware that, for example, <CODE>Nelements</CODE> is really
<CODE>$(P)$(R)Nelements</CODE> or in this case
<CODE>13IDC:Traj1:Nelements</CODE>.</P>
<P>
There are 8 motors in the database, and thus 8 similar records for many
functions. For example, the records defining the trajectory positions are
<CODE>M1Traj, M2Traj ... M8Traj</CODE>. These are referred to in this document
either as <CODE>M1Traj ... M8Traj</CODE> or as <CODE>MnTraj</CODE>.</P>
<P>&nbsp;</P>
<H2><A name="Defining a Trajectory">
Defining a Trajectory</A></H2>
<P>
The MM4005 and XPS always define a trajectory in terms of displacements (i.e.
relative positions) of each motor for each element of the trajectory. Each
trajectory element has an execution time associated with it, and hence there is
a velocity defined for each motor (displacement/time) for each trajectory
element. The MM4005 supports trajectories of up to 2000 elements, while the XPS
supports an essentially unlimited number of elements. </P>
<P>The MAXv controller allows a maximum of 2500 trajectory elements. The number of
elements supported by the Ensemble controller depends on the number of global
double variables specified in the controller's configuration. The default
configuration supports around 70 trajectory elements; the maximum is around
40,000 elements.</P>
<P>
During execution of the trajectory the MM4005/XPS can output a user-definable
number of logic pulses.&nbsp; The trajectory elements where these output pulses
begin and end can also be selected. On the MM4005 these pulses are evenly spaced in distance
along the trajectory, which is a distance in up to 8-dimensional space. On the XPS
the pulses are evenly spaced in time.
The MAXv can output a pulse only at a trajectory point. Ensemble controllers can
output pulses equally spaced in distance; some models (ML, HPe, HLe) can output
pulses at each of an array of user specified positions.
<P>
These output pulses are typically used for the channel-advance of a
multi-channel scaler, allowing detector pulses to be collected in hardware, with
no software overhead at each point. At the time that each pulse is output the
controller captures the theoretical and actual (encoder) position of each
motor. These positions can be read back when the trajectory is complete.
<i>The MAXv and Ensemble controllers do not record positions at each output
pulse.</i></P>
<P>
It is important to remember that the number of data points in a scan is
determined by the number of output pulses, not by the number of trajectory
elements. For example, a constant velocity theta/2-theta scan over 10 degrees
can be defined with a only 1 trajectory element, but could be used to collect 1000
data points at .01 degree steps.</P>
<P>
The following table lists the EPICS Process Variables (PVs) that are used to
define the trajectory. Some of these are explained in more detail below.</P>
<TABLE border=1>
<TBODY>
<TR>
<TD><STRONG>PV Name</STRONG></TD>
<TD><STRONG>Record Type</STRONG></TD>
<TD><STRONG>Description</STRONG></TD></TR>
<TR>
<TD><CODE>NumAxes</CODE></TD>
<TD>longout</TD>
<TD>The number of motors (axes) to be moved. For the MM4005 this is the total number
of axes connected. For XPS it is the number of axes in this MultipleAxes group.</TD></TR>
<TR>
<TD><CODE>Nelements</CODE></TD>
<TD>longout</TD>
<TD>The number of elements in the trajectory, i.e. the number of valid
points in the <CODE>M1Traj...M8Traj</CODE> and <CODE>TimeTraj</CODE>
arrays. Default=1.</TD></TR>
<TR>
<TD><CODE>MoveMode</CODE></TD>
<TD>mbbi</TD>
<TD>Defines the type of position information contained in
<CODE>M1Traj...M8Traj</CODE>. 0=<CODE>Relative</CODE>,
1=<CODE>Absolute</CODE>, 2=<CODE>Hydrid</CODE>.
Default=<CODE>Relative</CODE>. The MAXv and Ensemble support
does not yet handle Hybrid mode.</TD></TR>
<TR>
<TD><CODE>M1Traj ... M8Traj</CODE></TD>
<TD>waveform, double</TD>
<TD>The displacements or absolute positions of each motor for each element
of the trajectory. No defaults.</TD></TR>
<TR>
<TD><CODE>M1Move ... M8Move</CODE></TD>
<TD>bo</TD>
<TD>Flag defining whether each motor is to be moved during the trajectory.
0=<CODE>No</CODE>, 1=<CODE>Yes</CODE>. Default=<CODE>No</CODE>.</TD></TR>
<TR>
<TD><CODE>Npulses</CODE></TD>
<TD>longout</TD>
<TD>Number of synchronization pulses to be output by the MM4005/XPS.
Default=200.</TD></TR>
<TR>
<TD><CODE>StartPulses</CODE></TD>
<TD>longout</TD>
<TD>The trajectory element number where synchronization pulses will start.
Default=1.</TD></TR>
<TR>
<TD><CODE>EndPulses</CODE></TD>
<TD>longout</TD>
<TD>The trajectory element number where synchronization pulses should end.
Default=<CODE>Nelements</CODE>. The SNL program sets
<CODE>EndPulses=Nelements</CODE> whenever <CODE>Nelements</CODE> is
changed, since this is what is normally desired. <CODE>EndPulses</CODE>
can be changed again after changing <CODE>Nelements</CODE> if
desired.</TD></TR>
<TR>
<TD><CODE>TimeMode</CODE></TD>
<TD>bo</TD>
<TD>The mode being used to define the time per trajectory element.
0=<CODE>Total</CODE> means that the total time for the trajectory is being
defined, and the <CODE>TimeTraj</CODE> array will be computed by setting
the execution time for each element = <CODE>Time/Nelements</CODE>.
1=<CODE>Per Element</CODE> means that the time per element has already
been loaded into the <CODE>TimeTraj</CODE> array. This mode permits each
element to have a different execution time. Default =
<CODE>Total</CODE>.</TD></TR>
<TR>
<TD><CODE>Time</CODE></TD>
<TD>ao</TD>
<TD>Total trajectory execution time. Used when <CODE>TimeMode</CODE>=0.
Default=10.</TD></TR>
<TR>
<TD><CODE>TimeTraj</CODE></TD>
<TD>waveform, double</TD>
<TD>The array containing the execution time per trajectory element. This
array is computed by the SNL program if
<CODE>TimeMode</CODE>=<CODE>Total</CODE>, and must be loaded by the user
if <CODE>TimeMode</CODE>=<CODE>Per Element</CODE>.</TD></TR>
<TR>
<TD><CODE>Accel</CODE>(MM4005 and MAXv only)</TD>
<TD>ao</TD>
<TD>The acceleration time for the trajectory. Default=0.5 seconds.
<i>Currently, the Ensemble support gets this value from the motor
record.</i></TD></TR>
<TR> <TD><CODE>PulseDir</CODE> (Ensemble only)</TD> <TD>mbbo</TD> <TD>The
encoder direction in which output pulses will be generated. Choices are
0=<code>Both</code>, 1=<code>Pos</code>, and 2=<code>Neg</code>.
Default=<code>Both</code>.</TD></TR>
<TR>
<TD><CODE>PulseLenUS</CODE> (Ensemble only)</TD>
<TD>ao</TD>
<TD>The pulse length in microseconds. Default=25.</TD></TR>
<TR> <TD><CODE>PulseSrc</CODE> (Ensemble only)</TD> <TD>longout</TD>
<TD>The pulse source. This depends on the controller and
from where it gets its encoder signal. Normally, the value is 1 or 3.
Default=1.</TD></TR>
</TBODY>
</TABLE>
<P>The EPICS interface permits the motor positions for each element of the
trajectory to be defined in one of 3 ways. This flexibility can remove the
burden of converting absolute positions to displacements from the EPICS channel
access client and let the SNL program do the calculations. <CODE>MoveMode
</CODE>can have the following 3 values:</P>
<UL>
<LI><CODE>Relative</CODE>.
This mode maps directly to the way the MM4005 and XPS work internally.
Each trajectory element is a
displacement or distance for the motor to move during that element.
<LI><CODE>Absolute</CODE>.
In this mode
each trajectory element is an absolute motor position to which the controller
will move at each point in the trajectory. In practice the SNL program
computes an internal trajectory with <CODE>Nelements-1</CODE> elements where
<CODE>InternalTraj[i] = MnTraj[i+1]-MnTraj[i]</CODE>.
The SNL program drives all the motors to
the first point in the trajectory, waits for them to get there, and then
executes the internal trajectory. The disadvantage of
<CODE>Absolute</CODE> mode is that a new
trajectory needs to be downloaded and built each time the absolute position of
any motor in the trajectory is changed.&nbsp;
<LI><CODE>Hybrid</CODE>.
In this mode the
trajectory is defined in absolute coordinates, as in <CODE>Absolute</CODE>
mode. Again, an internal trajectory is computed from
<CODE>MnTraj[i+1]-MnTraj[i]</CODE>. However,
when the trajectory is executed the motors are <STRONG>not</STRONG> moved to
the position of the first point of the trajectory. Thus,
<CODE>Hybrid</CODE> mode permits a trajectory to
be defined in absolute coordinates, but executed as motions relative to the
current positions of the motors when the trajectory is executed. Thus the
motors can be moved using the EPICS motor record, and the trajectory executed
at a new absolute position without downloading new values to
<CODE>MnTraj</CODE> or rebuilding the trajectory. </LI>
</UL>
<P>Note that when programming <CODE>TimeTraj</CODE> in <CODE>Absolute</CODE> or
<CODE>Hybrid</CODE> mode <CODE>TimeTraj[i]</CODE> is the time to execute the
move from position <CODE>MnTraj[i]</CODE> to <CODE>MnTraj[i+1]</CODE>.</P>
<P>The MAXv and Ensemble currently do not support <CODE>Hybrid</CODE> mode.
MAXv may, and Ensemble always does, send the motor to a starting point that is
the acceleration distance from the start position, whether the trajectory mode
is <CODE>Absolute</CODE> or <CODE>Relative</CODE>. The acceleration distance is
AccelTime*velocity[0]/2, where AccelTime is the acceleration time, and
velocity[0] is the average velocity over the first trajectory segment.
Currently, the Ensemble support reads the acceleration time directly from the
motor record.
<P>The MM4005 requires the number of elements in a trajectory to be a multiple
of 4. This means that <CODE>Nelements</CODE>
should be a multiple of 4 in
<CODE>Relative</CODE> mode and a multiple of 4
plus 1 in <CODE>Absolute</CODE> or
<CODE>Hybrid</CODE> mode. However, this can be an
inconvenience for EPICS channel access clients. The SNL program works around
this restriction as follows. If
<CODE>Nelements</CODE> is not a multiple of 4 for
<CODE>MoveMode=Relative </CODE>or a multiple
of 4 plus 1 for
<CODE>MoveMode=Absolute</CODE> or
<CODE>Hybrid</CODE>, then up to 3 padding elements
are automatically added to the user defined trajectory to satisfy the
requirement. The padding elements always have a time of 0.1 seconds. The
displacements of the motors in these padding elements is computed to maintain
the same velocity as the last element in the original trajectory. Thus the
padding elements will cause the trajectory to execute for up to 0.3 seconds
longer than requested, and the motors will move slightly farther than requested.
However, there will be no velocity change, and hence no unexpected accelerations
during the padding elements.</P>
<P>The MM4000 has a concept of accelerating onto the trajectory, so that the time
per trajectory element will actually be longer than the specified times for the
first few and last few trajectory elements, as the motors accelerate and deccelerate.
This is typically normalized out during data processing by having a scaler channel
that measures the time (or integrated incident intensity) during each trajectory element.
<P>
The XPS does not do this acceleration automatically, rather the acceleration must be explicitly
defined in the trajectory file that is downloaded to the XPS. The XPS SNL program adds
one padding element at the beginning of the file and one padding element at the end of
the file. The first padding element is calculated to be the minimum time to accelerate
all of the motors from 0 to the velocity of the first actual trajectory element.
This time is calculated using an acceleration of 90% of the maximum allowed
acceleration for each axis (to avoid roundoff problems). It also calculates how far
each axis moves during this acceleration time.
Similarly the final padding element is calculated to be the minimum time to deccelerate
all of the motors from the velocity of the last actual trajectory element to 0.
These padding elements cause the actual range of motion of each of the motors
to be somewhat larger than the range in the trajectory itself. This means that trajectories
cannot execute right up to the software or hardware limits for an axis, because there
will not be room for the acceleration motion.
<P> The MAXv and Ensemble controllers also do not automatically accelerate onto
the trajectory. The SNL programs add an element to the beginning of the
trajectory and one (MAXv) or two (Ensemble) elements to the end. (Ensemble
always does this; MAXv does it if <code>AddAccelDecel=Yes</code>.) These
elements cause the actual range of motion of each of the motors to be somewhat
larger than the range in the trajectory itself. This means that trajectories
cannot execute right up to the software or hardware limits for an axis, because
there will not be room for the acceleration motion.
<P>
The number of trajectory elements, <CODE>Nelements </CODE>is limited as
follows.</P>
<UL>
<LI>The MM4005 allows a maximum of 2000 trajectory elements.
<li>The MAXv allows a maximum of 2500 trajectory elements.
<li>The Ensemble allows a maximum of 70 trajectory elements as delivered, but
its configuration can be modified to support up to 40,000 elements.
<LI>Channel access on EPICS 3.13 is limited to 2000 points, since it has a
16,000 byte limit, and all arrays are double precision, requiring 8 bytes
per element. In EPICS 3.14, the number of points Channel Access can send
is the environment variable <code>EPICS_CA_MAX_ARRAY_BYTES</code> divided
by eight.
<LI>There are 9 double precision arrays that are dimensioned
<CODE>MAX_ELEMENTS</CODE> in the SNL program, and 9 waveform records
(<CODE>MnTraj</CODE> and <CODE>TimeTraj</CODE>) that are dimensioned
<CODE>NELM</CODE> in the database. If 2000 points are allowed then the total
memory used in the IOC is 2000*9*8*2 = 288,000 bytes. This is a significant
amount of memory for an IOC. <CODE>MAX_ELEMENTS</CODE> is presently
defined to be 2000 in the SNL programs. This value can be changed (up to
2000 for the MM4005) and the SNL program recompiled. The value of
<CODE>NELM</CODE> can be set to any value up to <CODE>MAX_ELEMENTS</CODE> when
the database is loaded. </LI>
</UL>
<P>The number of output pulses, <CODE>Npulses</CODE> is limited as follows.</P>
<UL>
<LI>The MM4005 allows a maximum of 2000 output pulses.
<li>The MAXv can only output pulses at trajectory points, so
<CODE>Npulses</CODE> is ignored.
<li>The Ensemble controller can send output pulses equally spaced in position,
and imposes no limit on the number of such pulses. Some Ensemble controllers
(ML, HPe, and HLe) can also generate output pulses at user-specified
positions. Aerotech documentation does not specify the maximum number of
positions.
<LI>Channel access R3.13 is limited to 2000 points, since it has a 16,000 byte
limit, and all arrays are double precision, requiring 8 bytes per element.
In EPICS 3.14, the number of points Channel Access can send
is the environment variable <code>EPICS_CA_MAX_ARRAY_BYTES</code> divided
by eight.
<LI>There are 16 double precision arrays that are dimensioned.
<CODE>MAX_PULSES</CODE> in the SNL program, and 16 waveform records
(<CODE>MnActual</CODE> and <CODE>MnError</CODE>) that are dimensioned
<CODE>NPULSE</CODE> in the database. If 2000 points are allowed then the total
memory used in the IOC is 2000*16*8*2 = 512,000 bytes. This is a significant
amount of memory for an IOC. <CODE>MAX_PULSES</CODE> is presently defined
to be 2000 in the SNL program. This value can be changed (up to 2000 for the MM4005) or
and the SNL program recompiled. The value of <CODE>NPULSE</CODE>
<STRONG>must</STRONG> be set to exactly the value of <CODE>MAX_PULSES</CODE>
when the database is loaded. </LI>
</UL>
<P>&nbsp;</P>
<H2><A name="Building a Trajectory">
Building a Trajectory</A></H2>
<P>After a trajectory has been defined by setting the values of the PVs
described in the previous section it must be built before it can be executed.
Building the trajectory consists of downloading it to the controller and checking it
for errors such as excess velocities or accelerations.</P>
<P>
The following table describes the EPICS PVs used for building a
trajectory.</P>
<TABLE border=1>
<TBODY>
<TR>
<TD><STRONG>PV Name</STRONG></TD>
<TD><STRONG>Record Type</STRONG></TD>
<TD><STRONG>Description</STRONG></TD></TR>
<TR>
<TD><CODE>Build</CODE></TD>
<TD>busy</TD>
<TD>Setting this PV to 1 will build the trajectory, downloading it to the
controller. It will be set back to 0 automatically when the build is
complete.</TD></TR>
<TR>
<TD><CODE>BuildState</CODE></TD>
<TD>mbbi</TD>
<TD>The trajectory build state. 0=<CODE>Done</CODE>,
1=<CODE>Busy</CODE>.</TD></TR>
<TR>
<TD><CODE>BuildStatus</CODE></TD>
<TD>mbbi</TD>
<TD>The trajectory build status. 0=<CODE>Undefined</CODE>,
1=<CODE>Success</CODE>, 2=<CODE>Failure</CODE>.</TD></TR>
<TR>
<TD><CODE>BuildMessage</CODE></TD>
<TD>stringout</TD>
<TD>Progress messages while the build is in progress and error message if
<CODE>BuildStatus=Failure</CODE>.</TD></TR>
<TR>
<TD><CODE>M1MDVS ... M8MDVS</CODE> (MM4005 only)</TD>
<TD>ao</TD>
<TD>The maximum change in velocity allowed between trajectory elements.
This value can be set. These values are read from the MM4005 when the SNL
program starts, so the current values can be seen. The acronym is Maximum
Delta Velocity Set.</TD></TR>
<TR>
<TD><CODE>M1MDVA ... M8MDVA</CODE> (MM4005 only)</TD>
<TD>ao</TD>
<TD>The actual maximum change in velocity between trajectory elements.
This value is read from the MM4005 after the trajectory is built.
<CODE>MnMDVE</CODE> gives the specific trajectory element in which this
maximum change in velocity occurred. If <CODE>MnMDVA</CODE> is greater
than <CODE>MnMDVS</CODE> then the trajectory build will fail. The acronym
is Maximum Delta Velocity Actual. Read-Only.</TD></TR>
<TR>
<TD><CODE>M1MDVE ... M8MDVE</CODE> (MM4005 only)</TD>
<TD>longout</TD>
<TD>The trajectory element number where <CODE>MnMDVA</CODE> occurs. The
acronym is Maximum Delta Velocity Element. Read-Only.</TD></TR>
<TR>
<TD><CODE>M1MVA ... M8MVA</CODE></TD>
<TD>ao</TD>
<TD>The actual maximum velocity. This value is read from the controller after
the trajectory is built. <CODE>MnMVE</CODE> gives the specific trajectory
element in which this maximum velocity occurred (MM4005 only).
If <CODE>MnMVA</CODE> is
greater than the maximum velocity allowed for this motor then the build
will fail. The acronym is Maximum Velocity Actual. Read-Only.</TD></TR>
<TR>
<TD><CODE>M1MVE ... M8MVE</CODE> (MM4005 only)</TD>
<TD>longout</TD>
<TD>The trajectory element number where <CODE>MnMVA</CODE> occurs. The
acronym is Maximum Velocity Element. Read-Only.</TD></TR>
<TR>
<TD><CODE>M1MAA ... M8MAA</CODE></TD>
<TD>ao</TD>
<TD>The actual maximum acceleration. This value is read from the controller
after the trajectory is built. <CODE>MnMAE</CODE> gives the specific
trajectory element in which this maximum acceleration occurred (MM4005 only). If
<CODE>MnMVA</CODE> is greater than the maximum acceleration allowed for
this motor then the build will fail. The acronym is Maximum Acceleration
Actual. Read-Only. (<i>The Ensemble support sets this PV to 0.</i>)</TD></TR>
<TR>
<TD><CODE>M1MAE ... M8MAE</CODE> (MM4005 only)</TD>
<TD>longout</TD>
<TD>The trajectory element number where <CODE>MnMAA</CODE> occurs. The
acronym is Maximum Acceleration Element. Read-Only.</TD></TR>
<TR>
<TD><CODE>AddAccelDecel</CODE> (MAXv only)</TD>
<TD>bo</TD>
<TD>Tell software whether or not to add trajectory elements for accel and
decel. Read-Write.</TD></TR>
<TR>
<TD><CODE>M1Start ... M8Start</CODE> (MAXv, Ensemble only)</TD>
<TD>bo</TD>
<TD>Actual start position of motor, including any distance needed to
accelerate onto the trajectory. Read-Only.</TD></TR>
</TBODY>
</TABLE>
<P>
Channel access clients should do the following to build a trajectory:</P>
<UL>
<LI>Set <CODE>Build</CODE>=1
<LI>Repeatedly read <CODE>Build</CODE>, wait for it to go to
0=<CODE>Done</CODE>.
<LI>When <CODE>Build=Done</CODE> check <CODE>BuildStatus</CODE>. If it is not
1=<CODE>Success</CODE>, then something went wrong.
<LI><CODE>BuildMessage</CODE> can be used to determine what the error was,
although this will probably require a human rather than a program. </LI>
</UL>
<P>
If the build fails then it is useful to look at the
<CODE>trajectoryScanDebug.adl</CODE> MEDM screen to examine the maximum velocity
and acceleration values. See if one or more motors is being commanded to move
too fast.</P>
<P>&nbsp;</P>
<H2><A name="Executing a Trajectory">
Executing a Trajectory</A></H2>
<P>After a trajectory has been successfully built it can be executed. The
trajectory execution consists of the following steps:</P>
<UL>
<LI>Remember the initial positions of all the motors.
<LI>Move to the start position defined by <CODE>MnTraj[0]</CODE>. This is
only done if <CODE>MoveMode=Absolute</CODE>. (MAXv moves to
a start position if <CODE>MoveMode=Absolute</CODE> or if
<CODE>AddAccelDecel=Yes</CODE>. Ensemble always moves to a start position,
because it always adds a accelerate-onto-trajectory element.)
<LI>Trigger the detector start PV.
<LI>Execute the trajectory in either <CODE>Real</CODE> or
<CODE>Simulate</CODE> mode, with the execution time scaled by
<CODE>TimeScale</CODE>. Poll during execution and post channel access monitors
for the current element being executed, for the current positions of the
motors and for any errors.
<LI>When the trajectory is complete:
<UL>
<LI>Trigger the detector stop PV.
<LI>Command the EPICS motors to move to the final position in the trajectory. This does
not actually move the motors, but syncs the EPICS motor VAL and RBV fields
to the actual motor positions.
</UL>
</UL>
<P>
The following table describes the EPICS PVs used for executing a
trajectory.</P>
<TABLE border=1>
<TBODY>
<TR>
<TD><STRONG>PV Name</STRONG></TD>
<TD><STRONG>Record Type</STRONG></TD>
<TD><STRONG>Description</STRONG></TD></TR>
<TR>
<TD><CODE>Execute</CODE></TD>
<TD>busy</TD>
<TD>Setting this PV to 1 will execute the trajectory, performing the steps
described above. It will be set back to 0 automatically when the execution
is complete.</TD></TR>
<TR>
<TD><CODE>ExecState</CODE></TD>
<TD>mbbi</TD>
<TD>The trajectory execution state. 0=<CODE>Done</CODE>, 1=<CODE>Move
Start</CODE>, 2=<CODE>Executing</CODE>, 3=<CODE>Flyback</CODE>.</TD></TR>
<TR>
<TD><CODE>ExecStatus</CODE></TD>
<TD>mbbi</TD>
<TD>The trajectory execute status. 0=<CODE>Undefined</CODE>,
1=<CODE>Success</CODE>, 2=<CODE>Failure</CODE>, 3=<CODE>Abort</CODE>,
4=<CODE>Timeout</CODE>.</TD></TR>
<TR>
<TD><CODE>ExecMessage</CODE></TD>
<TD>stringout</TD>
<TD>Progress messages while the execution is in progress and error message
if <CODE>ExecStatus</CODE> is not <CODE>Success</CODE>.</TD></TR>
<TR>
<TD><CODE>SimMode</CODE> (MM4005 only)</TD>
<TD>bo</TD>
<TD>Simulation mode. 0=<CODE>Real</CODE>, 1=<CODE>Simulate</CODE>. The
MM4005 can execute a trajectory in simulation mode, not actually moving
any motors. Default=<CODE>Real</CODE>.</TD></TR>
<TR>
<TD><CODE>TimeScale</CODE> (MM4005, MAXv, and Ensemble only)</TD>
<TD>ao</TD>
<TD>Scaling time for the trajectory execution. Although a trajectory is
defined with a particular time per element, the execution time can be
scaled from this value. <CODE>TimeScale</CODE> can range from .01 (100
times faster) to 100 (100 times slower). Default=1.0</TD></TR>
<TR>
<TD><CODE>Abort</CODE></TD>
<TD>bo</TD>
<TD>Setting <CODE>Abort</CODE>=1 will immediately abort any motion on the
controller. It sends the AB command to the MM4005 which turns off the motor
power to all motors. To recover from this it is usually necessary to
re-home the motors, and to rebuild the trajectory at least twice before it
will succeed. <CODE>Abort</CODE> is set back to 0 automatically.</TD></TR>
<TR>
<TD><CODE>M1Current ... M8Current</CODE></TD>
<TD>&nbsp;</TD>
<TD>The current position of each motor. These values are updated and
posted during execution of the trajectory. They are <STRONG>not</STRONG>
continuously updated when the trajectory is not executing because that
could interfere with EPICS motor records. This conflict will be eliminated
in a future release of the MM4005 support for the EPICS motor
record.</TD></TR>
<TR>
<TD><CODE>OutBitNum</CODE> (MAXv and Ensemble only)</TD>
<TD>longout</TD>
<TD>If this PV is non-negative, it tells the MAXv which digital
output bit on which to send output pulses. For the Ensemble, it merely
enables output pulses if the number is in the range [0,15].</TD></TR>
<TR>
<TD><CODE>InBitNum</CODE> (MAXv only)</TD>
<TD>longout</TD>
<TD>If this PV is non-negative, it specifies the digital input bit that the
controller will monitor for a rising edge which tells it to begin the
trajectory.</TD></TR>
</TBODY>
</TABLE>
<P>
Channel access clients should do the following to execute a trajectory:</P>
<UL>
<LI>Set <CODE>Execute</CODE>=1
<LI>Repeatedly read <CODE>Execute</CODE>, wait for it to go to
0=<CODE>Done</CODE>.
<LI>When Execute=<CODE>Done</CODE> check ExecStatus. If it is not
1=<CODE>Success</CODE>, then something went wrong.
<LI><CODE>ExecMessage</CODE> can be used to determine what the error was,
although this will probably require a human rather than a program. </LI>
</UL>
<P>The execution can fail because the velocity or acceleration is too large,
even if the build succeeded, if <CODE>TimeScale</CODE> is less than 1.0.</P>
<P>&nbsp;</P>
<H2><A name="Reading Back a Trajectory">
Reading Back a Trajectory</A></H2>
<P>After a trajectory has been executed it is possible to read back from the
MM4005 or XPS the theoretical and actual positions of the motors when each
synchronization pulse was output. The EPICS interface presents this information
as the actual positions and the following errors (actual position minus
theoretical position) since these are usually of most interest to the user.
Obviously the theoretical position can be computed from the actual position and
the following error.</P>
<P>
Reading back this information from the MM4005 (but not from the XPS) is rather slow,
but in many cases this does not
need to be done for each scan. Once it is established that the following errors
are small enough it is possible to execute scans without reading back from the
MM4005 each time. The readback time is determined by the speed of the
communications interface to the MM4005. Each point returned from the MM4005 is
about 200 characters. Using RS-232 at 19,200 baud this requires 0.1 seconds per
point, where the number of points is equal to the number of output pulses. This
is thus 30 seconds for a scan with 300 output pulses. The XPS uses FTP over Ethernet
to read the "Gathering.dat" file containing the theoretical and actual motor
positions. The time to copy this file is very short, even for a large number of points.</P>
<P>
The MAXv controller cannot read back trajectory data. The Ensemble controller
can record actual motor positions periodically during a trajectory. Currently,
this is a compile-time option. (See <code>#define USE_SCOPE 1</code> in
EnsembleTrajectoryScan.st.)
<P>
The following table describes the EPICS PVs used for reading back a
trajectory.</P>
<TABLE border=1>
<TBODY>
<TR>
<TD><STRONG>PV Name</STRONG></TD>
<TD><STRONG>Record Type</STRONG></TD>
<TD><STRONG>Description</STRONG></TD></TR>
<TR>
<TD><CODE>Readback</CODE></TD>
<TD>busy</TD>
<TD>Setting this PV to 1 will read back the results of the
trajectory motion from the controller. It will be set back to 0 automatically
when the readback is complete.</TD></TR>
<TR>
<TD><CODE>ReadState</CODE></TD>
<TD>mbbi</TD>
<TD>The readback state. 0=<CODE>Done</CODE>,
1=<CODE>Busy</CODE>.</TD></TR>
<TR>
<TD><CODE>ReadStatus</CODE></TD>
<TD>mbbi</TD>
<TD>The readback status. 0=<CODE>Undefined</CODE>,
1=<CODE>Success</CODE>, 2=<CODE>Failure</CODE>.</TD></TR>
<TR>
<TD><CODE>ReadMessage</CODE></TD>
<TD>stringout</TD>
<TD>Progress messages while the readback is in progress and
error message if <CODE>ReadStatus</CODE> is not
<CODE>Success</CODE>.</TD></TR>
<TR>
<TD><CODE>Nactual</CODE></TD>
<TD>longout</TD>
<TD>The actual number of pulses output by the controller. This value
is normally equal to <CODE>Npulses</CODE>, but it could be less if a
trajectory did not complete.</TD></TR>
<TR>
<TD><CODE>M1Actual ... M8Actual</CODE></TD>
<TD>waveform, double</TD>
<TD>The actual position of the motor when each pulse was output
by the contoller during the trajectory scan.</TD></TR>
<TR>
<TD><CODE>M1Error ... M8Error</CODE></TD>
<TD>waveform, double</TD>
<TD>The following error of the motor when each pulse was output
by the controller during the trajectory scan. The following error is defined
as the actual position minus the theoretical position. </TD></TR>
</TBODY>
</TABLE>
<P>Channel access clients should do the following to read back a trajectory:</P>
<UL>
<LI>Set <CODE>Readback</CODE>=1
<LI>Repeatedly read <CODE>Readback</CODE>, wait for it to go to
0=<CODE>Done.</CODE>
<LI>When <CODE>Readback=Done</CODE> check <CODE>ReadStatus.</CODE> If it is
not 1=<CODE>Success</CODE>, then something went wrong.
<LI><CODE>ReadMessage </CODE>can be used to determine what the error was,
although this will probably require a human rather than a program. </LI>
</UL>
<P>Note that the readback command reads the global trace buffer of the MM4005.
It can be used to read back this trace buffer even if the previous operation was
not a trajectory execution. This can be useful for debugging operations in
general.</P>
<P>&nbsp;</P>
<H2><A name="Interaction with EPICS Motor Records">
Interaction with EPICS Motor Records</A></H2>
<P>The trajectory scanning does not use the EPICS motor records, but rather
talks directly to the controller. In order to keep the motor records in
sync with the actual motor positions the SNL program always sends the motors
to the final position in the trajectory scan, i.e. the positions where the motors actually
are, after a trajectory execution is complete. This should not result in any actual
motor motion (except for small following errors), but serves to resynchronize the
EPICS motor VAL and RBV fields with the actual motor positions.
One should not move any
motors on the involved controller with the EPICS motor records
while a trajectory scan is in progress. For the MAXv, one
<b>must</b> not move any motor on the involved controller.</P>
<P>
When a motion is aborted with the <CODE>Abort</CODE> PV it will be necessary
to home the motors and synchronize the motor records with the actual positions
of the motors. (This is not necessary for the MAXv and Ensemble controllers.)</P>
<P>&nbsp;</P>
<H2><A name="Communication with the controller">
Communication with the controller</A></H2>
<P>
The communication with the MM4005 uses asyn. It can be used with either the
RS-232 or GPIB interfaces. Debugging can be enabled with the
<CODE>asynSetTraceMask</CODE> and <CODE>asynSetTraceIOMask</CODE> commands.
Communication with the XPS also uses asyn over a TCP/IP socket connection. Debugging
can be enabled and disabled in the same way.
<P>
The timeout for communication with the MM4005 is set to 30 seconds, because
some commands can take a very long time to response. This was found to be necessary to allow for
the long time it takes the MM4005 to respond to the TB command after a VC
command is issued when verifying the trajectory. However, it would probably be
better to determine empirically how long it takes the MM4005 to verify
trajectories as a function of <CODE>Nelements</CODE> and <CODE>Npulses
</CODE>and have the SNL program wait that long after sending the VC command and
before sending the TB command. These measurements have not been done yet.</P>
<P>&nbsp;</P>
<H2><A name="Hardware Notes">
Hardware Notes</A></H2>
<P>
The synchronization output pulses from the MM4005 are provided on pin 12 of
the DB-25 Auxilliary Connector.
The synchronization output pulses from the XPS are provided on pin 12 of
the GPIO2 connector. We have found it convenient to make DB-25 connectors
with a short BNC or LEMO pigtail coming from these pins. The pulse outputs from both
the MM4005 and XPS
are open-collector circuits. The maximum rating in both cases is 30V and 40 mA.</P>
<P>
When using the pulse output as the channel-advance input of the Struck 7201
or SIS 380x multi-scaler some modifications are required. The SIS 380x manual
states that with the LEMO TTL input configuration the inputs are pulled up to
+5V with 1K Ohm resistors in a resistor pack. For our module the factory
configuration was actually a 4.7K Ohm resistor pack. These resistors did pull
the open-collector output up to +5V. However, when driving the cable over a long
distance (~80 feet) the rise time of the signal was quite slow, about 8
microseconds to go from 0V to the TTL threshold. This slow rise time caused the
SIS 380x to double count the channel advance signal most of the time. By
replacing the 4.7K Ohm resistor pack with a 200 Ohm pull-up resistor pack the
rise time was reduced to about 2 microseconds, and the module does not double
count. 200 Ohms is within the spec of the MM4005 and XPS, since it will result in a
current of 5V/200 Ohm = 25 mA, which is less than the 40 mA maximum.</P>
<P>The synchronization output pulses from the MAXv are sent on one of the pins
<code>IO0</code> - <code>IO15</code>, selected with the PV
<code>OutBitNum</code>, which must be in the range[0,15] inclusive. If the
number is outside this range, no output pulses will be generated. The MAXv is
able to start a trajectory on the rising edge of an input pulse, selected by the
PV <code>InBitNum</code>, with the same range. If <code>InBitNum</code> is
outside this range, the controller will start the trajectory as soon as it has
been loaded. These signals are available from the 50-pin SCSI connector on the
front panel of the MAXv.
<P>The synchronization output pulses from the Ensemble are sent on the "Auxiliary
Marker Output". Different controller models have different connectors and pin
numbers for this signal. This signal is RS422. Some Ensemble controllers
implement RS422 in such a way that the differential signals can be used as
complementary TTL signals.
<P>The Ensemble controller must be prepared with the AeroBasic program
<code>motor/motorApp/AerotechSrc/doCommand.ab</code>. You must copy
<code>doCommand.ab</code> to into the user-file directory of Aerotech's
<i>Motion Composer</i> program, load it into <i>Motion Composer</i>, and compile
it to produce the file <code>doCommand.bcx</code>. This file must be copied to
the controller, so it can be executed immediately when EnsembleTrajectoryScan.st
sends the command <code>PROGRAM RUN 1 "doCommand.bcx"</code>. The controller
also must be configured (using Aerotech's <i>Configuration Manager</i>) to
increase the number of global integers and doubles. For N trajectory points,
the number of global doubles (the <code>GlobalDoubles</code> parameter) must be
at least (N+3)*3 (for one motor), and the number of global integers (the
<code>GlobalIntegers</code> parameter) must be at least N+50. Also, the
controller must use the same units as the motor record that talks to it.
<P>&nbsp;</P>
<H2><A name=Installation>
Installation</A></H2>
<P>
The source files for trajectory scanning are in the synApps
<A href="https://github.com/epics-modules/motor">motor module
</A>, in the motorApp/ tree.
</P>
<PRE>
NewportSrc/MM4005_trajectoryScan.st
NewportSrc/XPS_trajectoryScan.st
OmsSrc/MAX_trajectoryScan.st
AerotechSrc/EnsembleTrajectoryScan.st
Db/trajectoryScan.db
op/adl/trajectoryScan.adl
op/adl/trajectoryScanDebug.adl
op/adl/trajectoryPlot.adl
op/adl/MAX_trajectory*.adl
</PRE>
<P>&nbsp;</P>
<H2><A name="Loading the database">
Loading the database</A></H2>
<P>
The database file is called <CODE>trajectoryScan.db.</CODE>.
This database is completely general for the MM4005 and XPS, it makes no
assumptions about the motors defined on particular axis. Thus it can be used
with the Newport diffractometer or any other set of up to 8 motors.
It takes the following macro parameters:
<TABLE style="TEXT-ALIGN: left" cellSpacing=2 cellPadding=2 border=1>
<TBODY>
<TR>
<TH>Macro parameter</TH>
<TH>Description</TH>
</TR>
<TR>
<TD><CODE>$(P)</CODE></TD>
<TD>PV name prefix</TD>
</TR>
<TR>
<TD><CODE>$(R)</CODE></TD>
<TD>PV base record name</TD>
</TR>
<TR>
<TD><CODE>$(NAXES)</CODE></TD>
<TD>Number of axes to be used. 6 for our Newport diffractometers.</TD>
</TR>
<TR>
<TD><CODE>$(NELM)</CODE></TD>
<TD>Maximum trajectory elements</TD>
</TR>
<TR>
<TD><CODE>$(NPULSE)</CODE></TD>
<TD>Maximum number of output pulses</TD>
</TR>
</TBODY>
</TABLE>
<P>&nbsp;</P>
<H2><A name="Starting the SNL program">
Starting the SNL program</A></H2>
<P>
There are different SNL programs for the different controllers. The MM4005 SNL program
is defined as follows:
<PRE>
program MM4005_trajectoryScan("P=13IDC:,R=traj1,M1=M1,M2=M2,M3=M3,M4=M4,M5=M5,M6=M6,M7=M7,M8=M8,PORT=serial1")
</PRE>
The parameters are defined as follows:
<TABLE style="TEXT-ALIGN: left" cellSpacing=2 cellPadding=2 border=1>
<TBODY>
<TR>
<TH>Macro parameter</TH>
<TH>Description</TH>
</TR>
<TR>
<TD><CODE>P</CODE></TD>
<TD>PV name prefix</TD>
</TR>
<TR>
<TD><CODE>R</CODE></TD>
<TD>PV base record name</TD>
</TR>
<TR>
<TD><CODE>M1-M8</CODE></TD>
<TD>EPICS motor record names for axes 1-8.</TD>
</TR>
<TR>
<TD><CODE>PORT</CODE></TD>
<TD>asyn port name for the RS-232 or GPIB port connected to the MM4005</TD>
</TR>
</TBODY>
</TABLE>
<P>
The XPS SNL program is defined as follows:
<PRE>
program XPS_trajectoryScan("P=13BMC:,R=traj1,IPADDR=164.54.160.34,PORT=5001,"
"USERNAME=Administrator,PASSWORD=Administrator,"
"M1=m1,M2=m2,M3=m3,M4=m4,M5=m5,M6=m6,M7=m7,M8=m8,"
"GROUP=g1,"
"P1=p1,P2=p2,P3=p3,P4=p4,P5=p5,P6=p6,P7=p7,P8=p8")
</PRE>
The parameters are defined as follows:
<TABLE style="TEXT-ALIGN: left" cellSpacing=2 cellPadding=2 border=1>
<TBODY>
<TR>
<TH>Macro parameter</TH>
<TH>Description</TH>
<TR>
<TD><CODE>P</CODE></TD>
<TD>PV name prefix</TD>
</TR>
<TR>
<TD><CODE>R</CODE></TD>
<TD>PV base record name</TD>
</TR>
<TR>
<TD><CODE>IPADDR</CODE></TD>
<TD>IP address of the XPS. Can be an IP name if the IOC supports DNS.</TD>
</TR>
<TR>
<TD><CODE>PORT</CODE></TD>
<TD>IP port number that XPS uses for socket connections</TD>
</TR>
<TR>
<TD><CODE>USERNAME</CODE></TD>
<TD>The user name to use to login to XPS for FTP</TD>
</TR>
<TR>
<TD><CODE>PASSWORD</CODE></TD>
<TD>The password for the USERNAME account for FTP</TD>
</TR>
<TR>
<TD><CODE>M1-M8</CODE></TD>
<TD>EPICS motor record names for axes 1-8.</TD>
</TR>
<TR>
<TD><CODE>GROUP</CODE></TD>
<TD>The name of the MultiAxes group for the motors to be moved</TD>
</TR>
<TR>
<TD><CODE>P1-P8</CODE></TD>
<TD>XPS positioner names for axes 1-8 in this GROUP</TD>
</TR>
</TBODY>
</TABLE>
<P>The MAXv SNL program
is defined as follows:
<PRE>
program MAX_trajectoryScan("P=13IDC:,R=traj1,M1=m1,M2=m2,M3=m3,M4=m4,M5=m5,M6=m6,M7=m7,M8=m8,PORT=none")
</PRE>
The parameters are defined as follows:
<TABLE style="TEXT-ALIGN: left" cellSpacing=2 cellPadding=2 border=1>
<TBODY>
<TR>
<TH>Macro parameter</TH>
<TH>Description</TH>
</TR>
<TR>
<TD><CODE>P</CODE></TD>
<TD>PV name prefix</TD>
</TR>
<TR>
<TD><CODE>R</CODE></TD>
<TD>PV base record name</TD>
</TR>
<TR>
<TD><CODE>M1-M8</CODE></TD>
<TD>EPICS motor record names for axes 1-8. You can specify fewer than eight motors, but you can't choose an arbitrary
motor ordering, and you can't skip any motors.</TD>
</TR>
<TR>
<TD><CODE>PORT</CODE></TD>
<TD>Not used. The MAXv trajectory support talks directly to the driver.</TD>
</TR>
</TBODY>
</TABLE>
<P>The Ensemble SNL program
is defined as follows:
<PRE>
program EnsembleTrajectoryScan("P=13IDC:,R=traj1,M1=m1,M2=m2,M3=m3,M4=m4,M5=m5,M6=m6,M7=m7,M8=m8,PORT=tcp1")
</PRE>
The parameters are defined as follows:
<TABLE style="TEXT-ALIGN: left" cellSpacing=2 cellPadding=2 border=1>
<TBODY>
<TR>
<TH>Macro parameter</TH>
<TH>Description</TH>
</TR>
<TR>
<TD><CODE>P</CODE></TD>
<TD>PV name prefix</TD>
</TR>
<TR>
<TD><CODE>R</CODE></TD>
<TD>PV base record name</TD>
</TR>
<TR>
<TD><CODE>M1-M8</CODE></TD>
<TD>EPICS motor record names for axes 1-8. Note that only the first motor actually is used.</TD>
</TR>
<TR>
<TD><CODE>PORT</CODE></TD>
<TD>asyn port name for the connection to the Ensemble. This is the port name specified
as the second argument in the command <code>EnsembleAsynConfig(0, "tcp1", 0, 1, 50, 1000)</code>.
</TD>
</TR>
</TBODY>
</TABLE>
<P>&nbsp;</P>
<H2><A name="Startup script">
Startup script</A></H2>
<H3>XPS</H3>
The following is an example of the lines from a startup script on a Linux system
to load <CODE>trajectoryScan.db</CODE> and start the sequencer for an XPS system.
Note that the <CODE>dbLoadRecords</CODE> and <CODE>seq</CODE> commands are shown on multiple
lines here for clarity, but they must each actually be on a single long line.
<PRE>
# Database for trajectory scanning with the XPS
dbLoadRecords("$(MOTOR)/motorApp/Db/trajectoryScan.db",
"P=13BMC:,R=traj1,NAXES=6,NELM=2000,NPULSE=2000")
iocInit
dbpf("13BMC:traj1DebugLevel","1")
seq(&XPS_trajectoryScan, "P=13BMC:,R=traj1,M1=m33,M2=m34,M3=m35,M4=m36,M5=m37,M6=m38,
IPADDR=164.54.160.124,PORT=5001,GROUP=GROUP1,
P1=PHI,P2=KAPPA,P3=OMEGA,P4=PSI,P5=2THETA,P6=NU")
</PRE>
<H3>MM4005</H3>
The following is an example of the lines from a startup script on a
vxWorks system to load <CODE>trajectoryScan.db</CODE> and start the sequencer
for a vxWorks system.
<PRE>
# Database for trajectory scanning with the MM4005
dbLoadRecords("$(CARS)/CARSApp/Db/trajectoryScan.db", "P=13IDC:,R=traj1,NAXES=6,NELM=2000,NPULSE=2000")
iocInit
# Trajectory scanning with MM4005
seq(&MM4005_trajectoryScan, "P=13IDC:, R=traj1, M1=m25,M2=m26,M3=m27,M4=m28,M5=m29,M6=m30,M7=m31,M8=m32,PORT=serial13")
</PRE>
<H3>MAXv</H3>
The following is an example of the lines from a startup script to load
<CODE>trajectoryScan.db</CODE> and start the sequencer for a MAXv system.
<PRE>
# Database for trajectory scanning with the MAXv
dbLoadRecords("$(MOTOR)/motorApp/Db/trajectoryScan.db",
"P=13BMC:,R=traj1,NAXES=6,NELM=2000,NPULSE=2000")
iocInit
# MAXV trajectory scan
seq &MAX_trajectoryScan, "P=xxx:,R=traj1:,M1=m1,M2=m2,M3=m3,M4=m4,M5=m5,M6=m6,M7=m7,M8=m8,PORT=none"
</PRE>
<H3>Ensemble</H3>
The following is an example of the lines from a startup script to load
<CODE>trajectoryScan.db</CODE> and start the sequencer program for an Ensemble system. Note
that the <CODE>dbLoadRecords</CODE> and <CODE>seq</CODE> commands are shown on
multiple lines here for clarity, but they must each actually be on a single long
line.
<P>The Ensemble trajectory support uses the same infrastructure that the motor record
uses to talk to the controller. Here's are typical commands that set that up:
<PRE>
dbLoadTemplate("AeroAsyn.substitutions")
drvAsynIPPortConfigure("tcp1","164.54.51.77:8000", 0, 0, 0)
EnsembleAsynSetup(1)
EnsembleAsynConfig(0, "tcp1", 0, 1, 50, 1000)
drvAsynMotorConfigure("AeroE1","motorEnsemble",0,1)
</pre>
where the file AeroAsyn.substitutions might have the following content:
<pre>
file "$(MOTOR)/db/asyn_motor.db"
{
pattern
{P, N, M, DTYP, PORT, ADDR, DESC, EGU, DIR, VELO, VBAS, ACCL, BDST, BVEL, BACC, MRES, PREC, DHLM, DLLM, INIT}
{xxxL:, 17, "m$(N)", "asynMotor", AeroE1, 0, "motor $(N)", mm, Pos, .1, 0, .2, 0, 1, .2, 1.25E-5, 7, 0, 0, "SCURVE 100"}
}
</pre>
Here are the database-load and sequencer-start commands:
<pre>
# Database for trajectory scanning with the Ensemble
dbLoadRecords("$(MOTOR)/motorApp/Db/trajectoryScan.db",
"P=13BMC:,R=traj1,NAXES=6,NELM=2000,NPULSE=2000")
iocInit
# Ensemble trajectory scan
seq &EnsembleTrajectoryScan, "P=15iddLAX:,R=traj1:,M1=aero:c0:m1,M2=none,M3=none,M4=none,M5=none,M6=none,M7=none,M8=none,PORT=tcp1"
</PRE>
<P>&nbsp;</P>
<H2><A name="MEDM screens">
MEDM screens</A></H2>
<P>
The following show the MEDM screens with which the user can view and modify
the trajectory scanning parameters.</P>
<P><CODE>trajectoryScan.adl</CODE> is the main screen used to define, build,
execute and read back trajectories. The only thing that cannot be done in MEDM
is to edit the <CODE>MnTraj</CODE> and <CODE>TimeTraj</CODE> arrays, since MEDM
does not provide a method to edit arrays.
<P>
<CODE>trajectoryScan.adl</CODE> is
called with macro parameters <CODE>P, R, TITLE, and M1 ... M8. P</CODE> and
<CODE>R</CODE> are the prefix and record base used when the database was loaded.
<CODE>M1 ... M8</CODE> are the names of the EPICS motors. These are used to label the
plots in <CODE>trajectoryPlot.adl</CODE> using the .DESC fields of the motors. For example
<CODE>trajectoryScan.adl</CODE> in this screen shot was called with
<CODE>
P=13BMC:, R=traj1, TITLE=Trajectory Scans, M1=m33,M2=m34,M3=m35,M4=m36,M5=m37,M6=m38,M7=m25,M8=m26
</CODE>.
<P>
<IMG src="trajectoryScan_files/trajectoryScan.png"></P>
<P>
<CODE>trajectoryPlot.adl</CODE> is used to plot the requested trajectory in
position and time (<CODE>MnTraj</CODE>, <CODE>TimeTraj</CODE>), the readback
positions (<CODE>MnActual</CODE>) and the following errors
(<CODE>MnError</CODE>).</P>
<P>
<IMG src="trajectoryScan_files/trajectoryPlot1.png"><IMG src="trajectoryScan_files/trajectoryPlot2.png"></P>
<P>
<CODE>trajectoryScanDebug.adl</CODE> is used to display detailed information,
useful for debugging.</P>
<P>
<IMG src="trajectoryScan_files/trajectoryScanDebug.png"></P>
<P>MEDM displays for the MAXv and Ensemble are very similar, but also include
start-position PVs written by the MAXv sequencer program, and some controller
specific PVs on the separate displays <code>trajectoryScan_MAXv.adl</code>, and
<code>trajectoryScan_Ensemble.adl</code>. These displays can be called up from
the "More" menu button on the MAX_trajectoryScan.adl display. The plotting
displays for these controllers are different in that they plot as functions of
time, rather than trajectory point.
<P>&nbsp;</P>
<H2><A name="Autosave">Autosave</A></H2>
<P>The user-modifiable PVs that control an instance of trajectoryScan.db can
be autosaved by including a line like the following in the autosave-request
file:
<pre>
file trajectoryScan_settings.req P=$(P),R=traj1:
</pre>
The macros 'P' and 'R' are the same as those specified for the database file.
<P>&nbsp;</P>
<H2><A name="Example IDL Procedure">
Example IDL Procedure</A></H2>
<P>
The following IDL function illustrates how an EPICS channel access client can
define, build, execute and read back a trajectory.</P>
<PRE>
function trajectory_scan, traj, positions, relative=relative, hybrid=hybrid, $
time=time, accel=accel, npulses=npulses, $
build=build, execute=execute, read=read, $
actual=actual, errors=errors
;+
; NAME:
; trajectory_scan
;
; PURPOSE:
; This IDL function loads and execute a complex trajectory
; using the EPICS trajectory scan databases and SNL programs. The EPICS
; support current exists for the Newport MM4005 and XPS motor controllers.
;
;
; CATEGORY:
; EPICS trajectory scanning
;
; CALLING SEQUENCE:
; Result = TRAJECTORY_SCAN(Trajectory, Positions)
;
; INPUTS:
; TRAJ: The EPICS record name for this trajectory, for example '13BMC:traj1'.
;
; POSITIONS: [NELEMENTS, NMOTORS]. The positions of the motors at each element in the trajectory.
; By default the positions are absolute motor positions. If the RELATIVE
; keyword is present then the positions are deltas, i.e. the difference in position
; from the previous point.
;
;
; KEYWORD PARAMETERS:
; RELATIVE: Set this keyword if the positions are deltas, i.e. the difference in position
; from the previous point. Default mode=ABSOLUTE.
; HYBRID: Set this keyword if the trajectory should be executed in "Hybrid" mode, i.e.
; the positions are absolute rather than deltas, but the trajectory should be
; executed from the current motor positions without moving to the first point
; in the postions array. Default mode=ABSOLUTE.
; TIME: If this keyword is a scaler, then it specifies the total time to execute
; the trajectory. If it is an array then it specifies the time per element.
; Default=1 second per element.
; ACCEL: The acceleration time for the trajectory. Only for the MM4005. Default=1 second.
; NPULSES: The number of output pulses during the trajectory. Default=NELEMENTS, the number
; of points in the input Positions array.
; BUILD: Set this keyword to build and verify the trajectory. This is the default.
;
; EXECUTE: Set this keyword to execute the trajectory. This is the default.
;
; READ: Set this keyword to read back the trajectory into ACTUAL and ERROR.
; The default is to not read back.
;
; NOTE: Any or all of these keywords can be set. If none is set then the
; function does not do anything.
;
; OUTPUTS:
; Result: This function returns a status indicating whether the
; selected operations were successful or not. 0=success,
; anything else is a failure.
; ACTUAL: An array of [Nactual, NMOTORS] containing the actual positions of
; each axis.
; ERRORS: An array of [Nactual, NMOTORS] containing the following errors of
; each axis.
; NOTE: The ACTUAL and ERROR outputs are only returned if the READ keyword it set.
;
; SIDE EFFECTS:
; This procedure can move the motors. Be careful!
;
; EXAMPLE:
; positions = [[1,2,3],[.1, .2, .3], [0,3,4]]
; status = TRAJECTORY_SCAN('13IDC:traj1', positions, /read, actual, errors)
; plot, actual[*,0]
; oplot, errors[*,0]
;
; MODIFICATION HISTORY:
; Written by: Mark Rivers, December 15, 2006
;-
t = caget(traj+'NumAxes', maxAxes)
MoveMode = 'Absolute'
if (keyword_set(relative)) then MoveMode = 'Relative'
if (keyword_set(hybrid)) then MoveMode = 'Hybrid'
t = caput(traj+'MoveMode', MoveMode)
if (n_elements(build) eq 0) then build=1
if (n_elements(execute) eq 0) then execute=1
if (keyword_set(build)) then begin
dims = size(positions, /dimensions)
nelements = dims[0]
naxes = dims[1]
if (n_elements(npulses) eq 0) then npulses = nelements
t = caput(traj+'Nelements', nelements)
t = caput(traj+'Npulses', npulses)
; Default is 1 second per element
if (n_elements(time) eq 0) then time = nelements * 1.0
if (n_elements(time) eq 1) then begin
t = caput(traj+'TimeMode', 'Total')
t = caput(traj+'Time', time)
endif else begin
t = caput(traj+'TimeMode', 'Per element')
t = caput(traj+'TimeTraj', time)
endelse
if (n_elements(accel) eq 0) then accel = 1.
t = caput(traj+'Accel', accel)
; The first naxes motors will move.
for i=0, maxAxes-1 do begin
axis = traj + 'M' + strtrim(i+1,2)
if (i lt naxes) then begin
t = caput(axis+'Move', 1)
pos = positions[*,i]
t = caput(axis+'Traj', pos)
endif else begin
t = caput(axis+'Move', 0)
endelse
endfor
; Trajectory is now defined. Build it.
t = caput(traj+'Build', 1)
; Wait for the build to complete. Wait 0.1 second between polls.
repeat begin
wait, 0.1
t = caget(traj+'Build', busy)
endrep until (busy eq 0)
; Make sure the build was successful
t = caget(traj+'BuildStatus', BuildStatus, /string)
if (BuildStatus ne 'Success') then begin
t = caget(traj+'BuildMessage', BuildMessage)
print, 'Build failed, error = ', BuildMessage
return, BuildStatus
endif
endif
if (keyword_set(execute)) then begin
t = caput(traj+'Execute', 1)
; Wait for the execute to complete. Wait 0.1 second between polls.
repeat begin
wait, 0.1
t = caget(traj+'Execute', busy)
endrep until (busy eq 0)
; Make sure the execution was successful
t = caget(traj+'ExecStatus', ExecStatus, /string)
if (ExecStatus ne 'Success') then begin
t = caget(traj+'ExecMessage', ExecMessage)
print, 'Execution failed, error = ', ExecMessage
return, ExecStatus
endif
endif
if (keyword_set(read)) then begin
t = caput(traj+'Readback', 1)
; Wait for the readback to complete. Wait 0.1 second between polls.
repeat begin
wait, 0.1
t = caget(traj+'Readback', busy)
endrep until (busy eq 0)
; Make sure the readback was successful
t = caget(traj+'ReadStatus', ReadStatus, /string)
if (ReadStatus ne 'Success') then begin
t = caget(traj+'ReadMessage', ReadMessage)
print, 'Read failed, error = ', ReadMessage
return, ReadStatus
endif
; Read the actual and error arrays into IDL, return to
; caller
t = caget(traj+'Nactual', nactual)
actual = dblarr(nactual, maxAxes)
errors = dblarr(nactual, maxAxes)
for i=0, maxAxes-1 do begin
axis = traj + 'M' + strtrim(i+1,2)
t = caget(axis+'Actual', temp, max=nactual)
actual[0,i] = temp
t = caget(axis+'Error', temp, max=nactual)
errors[0,i] = temp
endfor
endif
return, 0
end
</PRE>
<P>
This is an IDL main program that defines a trajectory and executes it using trajectory_scan.pro above.
<PRE>
; This program builds a trajectory and executes it.
; The trajectory definition is hybid mode, meaning the positions are
; definined in absolute coordinates rather than displacements from on
; element to the next. However, the motors do not move to the absolute
; position of the first element before executing the trajectory.
; 101 elements in the trajectory. We use 4N+1 since we are defining the
; trajectory in Hybrid mode
nelements = 101
; We will move the first 2 motors (Phi and Kappa)
naxes=2
; Define array of positions
positions = dblarr(nelements, naxes)
; The Phi trajectory is a sin wave with two complete periods and an
; amplitude of +-8 degrees
positions[*,0] = 8.*sin(findgen(nelements)/(nelements-1.)*4.*!pi)
; The Kappa trajectory is a sin wave with one complete period and an
; amplitude of +-20 degrees
positions[*,1] = 20.*sin(findgen(nelements)/(nelements-1.)*2.*!pi)
trajectory = '13BMC:traj1'
; Total time for trajectory
time = 20.
status = trajectory_scan(trajectory, positions, /hybrid, /build, /execute, /read, $
time=time, npulses=300, actual=actual, errors=errors)
end
</PRE>
<P>
These are the IDL commands to execute the above program, and plot the results:
<PRE>
IDL> .run trajectory_test1
IDL> iplot, actual[*,0]
IDL> iplot, actual[*,1], /overplot
IDL> iplot, errors[*,0]
IDL> iplot, errors[*,1], /overplot
</PRE>
<P>
These are the resulting plots:
<P>
<IMG src="trajectoryScan_files/IDL_trajectory_actual.png"></P>
<P>
<IMG src="trajectoryScan_files/IDL_trajectory_errors.png"></P>
<P>&nbsp;</P>
<H2><A name=SPEC_Interface>
SPEC Interface</A></H2>
<P>
A set of SPEC macros allows SPEC to utilize trajectory scanning with EPICS
and the MM4005 or XPS. It requires a multi-channel scaler or some other
detector that can be triggered and has buffering.
<P>
The implementation is done at a low level, so that all of SPEC's standard
scans can be done "on-the-fly" utilizing this trajectory scanning software.
This
was done by providing replacement macros for:
<PRE>
_ascan # Used by all ascan and dscan macros
mesh
hklscan # Used by hscan, kscan and lscan
_hklmesh
_hklline # Used by hkcircle, hlcircle, klcircle, hkradial, hlradial and klradial
_scanabort
resume
_loop
</PRE>
It adds the following new macros:
<PRE>
traj_index # Converts a SPEC motor index to an MM4005 or XPS motor index
traj_build # Builds a trajectory
traj_exec # Executes a trajectory
traj_read_counts # Reads the data from the multi-channel scaler
traj_read_actual # Reads back the actual MM4005 or XPS motor positions
traj_scans_on # Enables trajectory scanning
traj_scans_off # Disables trajectory scanning, uses step scanning
</PRE>
The improvement in performance is dramatic. Using step scanning the
overhead per point is about 1 second, so a 500 point scan takes a minimum of 500
seconds or more than 8 minutes. Using trajectory scanning the total time to
execute a 500 point scan with .002 seconds per point is 3 seconds, including the
time to print and plot the data and write it to the data file.
<P>
It is easy to switch back and forth between traditional step scanning and
trajectory scanning.
<CODE>traj_scans_on</CODE> turns on trajectory scanning for
all subsequent scans. <CODE>traj_scans_off</CODE> reverts back to traditional
step scanning.
<P>
It is possible to have the motor positions and HKL values that SPEC prints
on the screen, plots and stores in the SPEC data file be based upon the
<I>theoretical</I> motor positions during the scan. Alternatively SPEC can use
values based upon the <I>actual</I> motor positions at each point in the scan.
Using the actual motor positions is slower, because the values must be read from
the MM4005 at the end of the trajectory execution. Set the SPEC global variable
<CODE>TRAC_USE_ACTUAL=0</CODE> to use the theoretical motor positions, and
<CODE>TRAJ_USE_ACTUAL=1</CODE> to use the actual motor positions.
<<P>&nbsp;</P>
<H2><A name="Restrictions">
Restrictions</A></H2>
<P>The following are restrictions and problems with the trajectory scanning.
<UL>
<LI>At short trajectory element times with velocity changes the XPS can exceed
the maximum acceleration, even when a simple calculation (delta velocity/delta time)
says that it should not. The fix will be to use the actual spline function that the
XPS uses when calculating the velocities in the trajectory file. We have not run into
this problem at trajectory times larger than 0.01 second.
<li>The MAXv controller does not actually have a trajectory capability, but
instead has a variable velocity capability that can be programmed to
produce the effect of a trajectory. This works well when there are many
motor pulses between trajectory points. When there are few, the times
at which the motors hit trajectory points become erratic.
</UL>
</BODY>
</HTML>