1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
//
// GENERATED FILE
//
use super::*;
use crate::SpiceContext;
use f2rust_std::*;
const MXNEWT: i32 = 5;
/// Solve Kepler's Equation --- Vector Form
///
/// Solve the equation X = \< EVEC, U(X) > where U(X) is the unit
/// vector \[ COS(X), SIN(X) ] and \< , > denotes the two-dimensional
/// dot product.
///
/// # Brief I/O
///
/// ```text
/// VARIABLE I/O DESCRIPTION
/// -------- --- --------------------------------------------------
/// EVEC I A 2-vector whose magnitude is less than 1.
///
/// The function returns the solution to X = < EVEC, U(X) >
/// ```
///
/// # Detailed Input
///
/// ```text
/// EVEC is any two dimensional vector whose magnitude is
/// less than 1.
/// ```
///
/// # Detailed Output
///
/// ```text
/// The function returns the value X such that the equation
///
/// X = EVEC(1)COS(X) + EVEC(2)SIN(X).
/// ```
///
/// # Exceptions
///
/// ```text
/// 1) If the magnitude of EVEC is greater than or equal to 1,
/// the error SPICE(EVECOUTOFRANGE) is signaled.
/// ```
///
/// # Particulars
///
/// ```text
/// This routine uses bisection and Newton's method to find
/// the root of the equation
///
/// X = EVEC(1)COS(X) + EVEC(2)SIN(X).
///
/// This equation is just a "vector form" of Kepler's equation.
/// ```
///
/// # Examples
///
/// ```text
/// Suppose you need to solve the equation
///
/// M = E - e SIN(E) [ 1 ]
///
/// for E. If we let X = E - M the equation is transformed to
///
/// 0 = X - e SIN( X + M )
///
/// = X - e SIN( M ) COS(X) - e COS(M) SIN ( X )
///
/// Thus if we solve the equation
///
/// X = e SIN(M) COS(X) + e COS(M) SIN(X)
///
/// we can find the value of X we can compute E.
///
/// The code fragment below illustrates how this routine can
/// be used to solve equation [1].
///
/// EVEC(1) = ECC * DSIN(M)
/// EVEC(2) = ECC * DCOS(M)
/// E = M + KPSOLV( EVEC )
/// ```
///
/// # Author and Institution
///
/// ```text
/// J. Diaz del Rio (ODC Space)
/// W.L. Taber (JPL)
/// ```
///
/// # Version
///
/// ```text
/// - SPICELIB Version 1.1.1, 26-OCT-2021 (JDR)
///
/// Edited the header to comply with NAIF standard.
///
/// - SPICELIB Version 1.1.0, 26-AUG-1997 (WLT)
///
/// KPSOLV is now given an initial value of zero so that
/// if an error condition is detected, KPSOLV will have
/// a return value.
///
/// - SPICELIB Version 1.0.0, 03-JAN-1997 (WLT)
/// ```
pub fn kpsolv(ctx: &mut SpiceContext, evec: &[f64; 2]) -> crate::Result<f64> {
let ret = KPSOLV(evec, ctx.raw_context())?;
ctx.handle_errors()?;
Ok(ret)
}
//$Procedure KPSOLV ( Solve Kepler's Equation --- Vector Form )
pub fn KPSOLV(EVEC: &[f64], ctx: &mut Context) -> f2rust_std::Result<f64> {
let EVEC = DummyArray::new(EVEC, 1..=2);
let mut KPSOLV: f64 = 0.0;
let mut COSX: f64 = 0.0;
let mut ECC: f64 = 0.0;
let mut ECC2: f64 = 0.0;
let mut H: f64 = 0.0;
let mut K: f64 = 0.0;
let mut SINX: f64 = 0.0;
let mut X: f64 = 0.0;
let mut XL: f64 = 0.0;
let mut XM: f64 = 0.0;
let mut XU: f64 = 0.0;
let mut Y0: f64 = 0.0;
let mut YPX: f64 = 0.0;
let mut YX: f64 = 0.0;
let mut YXM: f64 = 0.0;
let mut MAXIT: i32 = 0;
//
// MXNEWT is the number of iterations we will perform
// in the Newtons method for finding the solution to
// the vector form of Kepler's equation. It has been
// empirically determined that 5 iterations is always
// sufficient on computers have 64 bit double precision
// numbers.
//
//
// We give the function an initial value, just in case
// we exit without solving Kepler's equation.
//
KPSOLV = 0.0;
H = EVEC[1];
K = EVEC[2];
ECC2 = ((H * H) + (K * K));
if (ECC2 >= 1.0) {
CHKIN(b"KPSOLV", ctx)?;
SETMSG(b"The magnitude of the vector EVEC = ( #, # ) must be less than 1. However, the magnitude of this vector is #.", ctx);
ERRDP(b"#", H, ctx);
ERRDP(b"#", K, ctx);
ERRDP(b"#", f64::sqrt(ECC2), ctx);
SIGERR(b"SPICE(EVECOUTOFRANGE)", ctx)?;
CHKOUT(b"KPSOLV", ctx)?;
return Ok(KPSOLV);
}
//
// We first approximate the equation 0 = X - H * COS(X) - K * SIN(X)
// using bisection. If we let Y(X) = X - H * COS(X) - K * SIN(X)
//
// Y( ECC) = ECC - <EVEC,U(X)> = ECC - ECC*COS(ANGLE_X) > 0
// Y(-ECC) = -ECC - <EVEC,U(X)> = -ECC - ECC*COS(ANGLE_X) < 0
//
// where ANGLE_X is the angle between U(X) and EVEC. Thus -ECC
// and ECC necessarily bracket the root of the equation Y(X) = 0.
//
// Also note that Y'(X) = 1 - < EVEC, V(X) > where V(X) is the
// unit vector given by U'(X). Thus Y is an increasing function
// over the interval from -ECC to ECC.
//
// The mid point of ECC and -ECC is 0 and Y(0) = -H. Thus
// we can do the first bisection step without doing
// much in the way of computations.
//
Y0 = -H;
XM = 0.0;
ECC = f64::sqrt(ECC2);
if (Y0 > 0.0) {
XU = 0.0;
XL = -ECC;
} else if (Y0 < 0.0) {
XU = ECC;
XL = 0.0;
} else {
KPSOLV = 0.0;
return Ok(KPSOLV);
}
//
// Iterate until we are assured of being in a region where
// Newton's method will converge quickly. The formula
// below was empirically determined to give good results.
//
MAXIT = intrinsics::MIN0(&[
32,
intrinsics::MAX0(&[1, intrinsics::IDNINT((1.0 / (1.0 - ECC)))]),
]);
for I in 1..=MAXIT {
//
// Compute the next midpoint. We bracket XM by XL and XU just in
// case some kind of strange rounding occurs in the computation
// of the midpoint.
//
XM = intrinsics::DMAX1(&[XL, intrinsics::DMIN1(&[XU, (0.5 * (XL + XU))])]);
//
// Compute Y at the midpoint of XU and XL
//
YXM = ((XM - (H * f64::cos(XM))) - (K * f64::sin(XM)));
//
// Determine the new upper and lower bounds.
//
if (YXM > 0.0) {
XU = XM;
} else {
XL = XM;
}
}
//
// We've bisected into a region where we can now get rapid
// convergence using Newton's method.
//
X = XM;
for I in 1..=MXNEWT {
COSX = f64::cos(X);
SINX = f64::sin(X);
//
// Compute Y and Y' at X. Use these to get the next
// iteration for X.
//
// For those of you who might be wondering, "Why not put
// in a check for YX .EQ. 0 and return early if we get
// an exact solution?" Here's why. An empirical check
// of those cases where you can actually escape from the
// Do-loop showed that the test YX .EQ. 0 is true
// only about once in every 10000 case of random inputs
// of EVEC. Thus on average the check is a waste of
// time and we don't bother with it.
//
YX = ((X - (H * COSX)) - (K * SINX));
YPX = ((1.0 + (H * SINX)) - (K * COSX));
X = (X - (YX / YPX));
}
KPSOLV = X;
Ok(KPSOLV)
}