1 from __future__
import division, absolute_import
6 from coordConv
import DoubleMax
13 """!Data returned by trapSlew. Fields match inputs to constructor.
15 def __init__(self, dt1, dt2, dt3, vP, a1, a3, slewType):
16 """!Construct a TrapSlewData
18 @param[in] dt1 duration of segment 1 (first constant acceleration) (sec)
19 @param[in] dt2 duration of segment 2 (constant velocity) (sec)
20 @param[in] dt3 duration of segment 3 (last constant acceleration) (sec)
21 @param[in] vP velocity of segment 2 (deg/sec)
22 @param[in] a1 acceleration of segment 1 (deg/sec^2)
23 @param[in] a3 acceleration of segment 3 (deg/sec^2)
24 @param[in] slewType type of slew; one of:
25 0: null slew: vB = vA and rBAi = 0:
27 1: sign(vB - vA) = sign(rBAi), or one of vBA or rBAi = 0:
28 sign(a1) = sign(rBAi), |a1| = aMax, a3 = - a1
29 2: sign(vB - vA) = - sign(rBAi), and a solution exists such that:
30 sign(a1) = sign(rBAi), |a1| = aMax, a3 = - a1
31 3: sign(vB - vA) = - sign(rBAi), and a solution exists such that:
32 sign(a1) = - sign(rBAi), |a1| = aMax, a3 = - a1
33 4: same as type 3 but |a1| reduced as required so a solution exists
44 return "TrapSlewData(dt1=%s, dt2=%s, dt3=%s, vP=%s, a1=%s, a3=%s, slewType=%s)" % \
48 def trapSlew(rBAi, vA, vB, dt2min, vMax, aMax):
49 """!Compute a trapezoidal slew.
51 You may specify a minimum duration for the constant-velocity segment, which is useful for "rounding the
52 corners" of the slew to make it jerk-limited (see mov.fullSlew).
54 @param[in] rBAi distance between "A" and "B" at time t = 0 (deg)
55 @param[in] vA velocity of "A" (deg/sec)
56 @param[in] vB velocity of "B" (deg/sec)
57 @param[in] dt2min minimum duration of segment 2 (constant velocity) (sec)
58 @param[in] vMax maximum allowed velocity (deg/sec)
59 @param[in] aMax maximum allowed acceleration (deg/sec^2)
61 @return a TrapSlewData
63 @throw RuntimeError if:
69 Dies if vMax or aMax are so small as to cause under- or over-flow.
72 The magic number "Fudge" is used to avoid borderline cases.
73 It is set below, and should be a number a bit bigger than one.
76 The slew begins by tracking object A, and ends by tracking object B.
77 Both objects are assumed to be moving at constant velocity.
79 A trapezoidal slew has three segments, two of constant acceleration
80 separated by a constant velocity segment. It is called "trapezoidal"
81 because that is the shape of the v vs. t curve.
83 Here are the initial velocity and constant acceleration for each segment,
84 and the duration of that segment, in the notation of this subroutine:
92 The slew numbering and notation used in this subroutine are quite different
93 than those used in the math notebook. Significant changes include:
94 dt2 = delta-t3 in notebook
95 slew type 0 is not mentioned in the notebook
96 slew type 1 = notebook type 0
97 slew type 2 = notebook type 1
98 slew type 3, 4 = notebook type 3
99 note that the notebook type 2 slew (the only "reversed" slew)
100 is NOT USED by this subroutine, because it is not needed, and it saves
101 the bother of implementing the reversed slew equations; instead,
102 a type 3 or 4 slew (this subr.) is used with reduced acceleration.
105 "Control of the Apache Point 3.5m Telescope: Slewing", R. Owen, 1990, unpub
107 TCC Math Notebook, section on slewing (warning: different notation)
110 2013-12-06 ROwen Converted from mov_TrapSlew.for
112 def reportBug(msgStr):
113 """!Write a message to stderr that includes outputs, then raise RuntimeError
115 sys.stderr.write(
"%s\ntrapSlew(rBai=%s, vA=%s, vB=%s, dt2min=%s, vMax=%s, aMax=%s)\n" % \
116 (msgStr, rBAi, vA, vB, dt2min, vMax, aMax))
117 raise RuntimeError(msgStr)
120 raise RuntimeError(
"dt2min=%s < 0" % (dt2min,))
122 raise RuntimeError(
"vMax=%s < 0" % (vMax,))
124 raise RuntimeError(
"aMax=%s < 0" % (aMax,))
128 if abs(vA) > vMax * Fudge:
129 raise RuntimeError(
"Telescope is moving too fast (|%0.4f| > %s * %s). Stop the telescope, then try your slew again."
131 if abs(vB) * Fudge > vMax:
132 raise RuntimeError(
"Target is moving too fast (|%0.4f| * %s > %s; telescope cannot acquire it." \
140 half_vBAsq = 0.5 * vBA * vBA
141 if (rBAi != 0.0)
and (vBA != 0.0):
142 sign_rBAi = math.copysign(1.0, rBAi)
143 sign_vBA = math.copysign(1.0, vBA)
145 sign_rBAi = math.copysign(1.0, rBAi)
148 sign_vBA = math.copysign(1.0, vBA)
166 if sign_rBAi == sign_vBA:
168 a1 = sign_rBAi * aMax
176 elif abs(vA) * Fudge < vMax
and (dt2min * aMax * abs(vBA)) <= ((aMax * abs(rBAi)) - half_vBAsq):
178 a1 = sign_rBAi * aMax
181 elif (aMax * abs(rBAi) * Fudge) <= half_vBAsq:
183 a1 = - sign_rBAi * aMax
191 a1 = - half_vBAsq / (Fudge * rBAi)
197 max_vdiff = vMax + abs(vA) + abs(vB)
198 if max_vdiff >= min(abs(a1), 1.0) * DoubleMax:
199 raise reportBug(
'Computed slew time is ridiculous')
208 vPB_temp = (0.5 * a1 * dt2)**2 + half_vBAsq + a1 * rBAi
210 raise RuntimeError(
'Bug! Tried to compute square root of negative value')
211 vPB = math.copysign(math.sqrt(vPB_temp), a1) - (0.5 * a1 * dt2)
220 vP = math.copysign(vMax, vP)
222 dt2 = (rBAi + ((half_vBAsq - (vPB * vPB)) / a1)) / vPB
233 if (dt1 < 0.0)
or (dt2 < 0.0)
or (dt3 < 0.0):
234 reportBug(
'Bug! Computed negative duration for one or more segments')
236 reportBug(
'Bug! Computed velocity greater than max velocity')
238 reportBug(
'Bug! Computed acceleration greater than max acceleration')
Data returned by trapSlew.
def trapSlew
Compute a trapezoidal slew.
def __init__
Construct a TrapSlewData.