mxlib
c++ tools for analyzing astronomical data and other tasks by Jared R. Males. [git repo]
kepler.hpp
Go to the documentation of this file.
1 /** \file kepler.hpp
2  * \author Jared R. Males
3  * \brief Declarations for the utilities related to the Kepler problem.
4  * \ingroup astrofiles
5  *
6  */
7 
8 #ifndef __mx_astro_kepler_hpp__
9 #define __mx_astro_kepler_hpp__
10 
11 #include <iostream>
12 #include <cmath>
13 #include <cstdlib>
14 
15 
16 #include "units.hpp"
17 
18 namespace mx
19 {
20 
21 namespace astro
22 {
23 
24 
25 
26 #ifndef KEPLER_TOL
27 ///The default tolerance for solutions to the Kepler problem
28 #define KEPLER_TOL (1e-8)
29 #endif
30 
31 #ifndef KEPLER_ITMAX
32 ///The default maximum number of iterations for solutions to the Kepler problem
33 #define KEPLER_ITMAX (1000)
34 #endif
35 
36 
37 
38 
39 ///Solve the hyperbolic kepler equation (for e> 1).
40 /** \note this is not tested very well (as of 2014.08.09)
41  *
42  *
43  * \returns -1 on exceeding itmax, otherwise reurns the number of iterations.
44  *
45  * \tparam realT is the type used for arithmetic
46  *
47  * \ingroup kepler
48  */
49 template<typename realT>
50 long hyperbolic_kepler( realT & E, ///< [out] is the eccentric anomaly
51  realT & err, ///< [out] is the error after the last iteration
52  realT e, ///< [in] is the eccentricity
53  realT M, ///< [in] is the mean anomaly
54  realT tol, ///< [in] is the desired tolerance
55  long itmax ///< [in] is the maximum number of iterations
56  )
57 {
58  realT curr, thresh;
59  int is_negative = 0;
60  long n_iter = 0;
61 
62  if(e == 1) e = 1.000001;
63 
64  if(M == 0) return( 0.);
65 
66  err = e * sinh( curr) - curr - M;
67  while( fabs( err) > tol && n_iter < 10000)
68  {
69  n_iter++;
70  curr -= err / (e * cosh( curr) - 1.);
71  err = e * sinh( curr) - curr - M;
72  }
73 
74  E = ( is_negative ? -curr : curr);
75 
76  if(n_iter > itmax)
77  {
78  std::cerr << "hyperbolic kepler failed to converge e=" << e << "\n";
79  return -1;
80  }
81 
82  return n_iter;
83 }
84 
85 ///Calculate the next iteration of Danby's quartic Newton-Raphson method.
86 /** \ingroup kepler
87  */
88 template<typename realT>
89 realT kepler_danby_1( realT e,
90  realT M,
91  realT Ei)
92 {
93  realT cosE, sinE, fi, fi1, fi2, fi3, di1, di2, di3;
94 
95  //These are expensive, do them just once.
96  cosE = cos(Ei);
97  sinE = sin(Ei);
98 
99  fi = Ei - e*sinE - M;
100  fi1 = 1.0 - e*cosE;
101  fi2 = e*sinE;
102  fi3 = e*cosE;
103 
104  di1 = -fi / fi1;
105  di2 = -fi/(fi1 + 0.5*di1*fi2);
106  di3 = -fi/(fi1 + 0.5*di2*fi2 + di2*di2*fi3/6.0);
107 
108  return Ei + di3;
109 }
110 
111 ///Solve Kepler's equation using Danby's quartic Newton-Raphson method.
112 /**
113  * \returns -1 on exceeding itmax.
114  * \returns the number of iterations on success.
115  *
116  * \ingroup kepler
117  */
118 template<typename realT>
119 long solve_kepler_danby( realT & E, ///< [out] is the eccentric anomaly
120  realT & D, ///< [out] is the error after the last iteration
121  realT e, ///< [in] is the eccentricity
122  realT M, ///< [in] is the mean anomaly
123  realT tol, ///< [in] is the desired tolerance
124  long itmax ///< [in] is the maximum number of iterations
125  )
126 {
127  long i;
128  realT lastE, sinE, sign;
129 
130  sinE = sin(M);
131  sign = 1.0;
132  if(sinE < 0.0) sign = -1.0;
133 
134  E = M + 0.85*e*sign;
135 
136  for(i=0; i < itmax; i++)
137  {
138  lastE = E;
139  E = kepler_danby_1(e, M, E);
140 
141  //Test for convergence to within tol
142  //Make sure we have iterated at least twice to prevent early convergence
143  if(i > 1 && fabs(E-lastE) < tol)
144  {
145  D = M - (E-e*sin(E));
146  return i;
147  }
148  }
149 
150  return -1; //This means itmax exceeded.
151 }
152 
153 ///Solve Kepler's equation for any e. Uses solve_kepler_danby if e < 1.0, hyperbolic_kepler otherwise.
154 /**
155  * \returns the number of iterations on success.
156  * \returns -1 if itmax is exceeded.
157  *
158  * \ingroup kepler
159  */
160 template<typename realT>
161 long solve_kepler( realT & E, ///< [out] is the eccentric anomaly
162  realT & D, ///< [out] is the error after the last iteration
163  realT e, ///< [in] is the eccentricity
164  realT M, ///< [in] is the mean anomaly
165  realT tol=KEPLER_TOL, ///< [in] is the desired tolerance
166  long itmax=KEPLER_ITMAX ///< [in] is the maximum number of iterations
167  )
168 {
169  if (e < 1.0)
170  {
171  return solve_kepler_danby(E, D, e, M, tol, itmax);
172  }
173  else return hyperbolic_kepler(E, D, e, M, tol, itmax);
174 
175 }
176 
177 
178 
179 
180 
181 #if 0
182 
183 #endif
184 
185 
186 #if 0
187 
188 ///Calculate the next iteration of Danby's quartic Newton-Raphson method (difference form).
189 //floatT keplerdiff_1(floatT EC, floatT ES, floatT dM, floatT dEi);
190 template<typename arithT>
191 arithT kepler_danby_diff_1(arithT EC, arithT ES, arithT dM, arithT dEi)
192 {
193  arithT cosE0, sinE0, cosdE, sindE, fi, fi1, fi2, fi3, di1, di2, di3;
194 
195  //These are expensive, do them just once.
196  //cosE0 = cos(E0);
197  //sinE0 = sin(E0);
198  cosdE = cos(dEi);
199  sindE = sin(dEi);
200 
201  fi = dEi - EC*sindE + ES*(1-cosdE) - dM;
202  fi1 = 1.0 - EC*cosdE + ES*sindE;
203  fi2 = EC*sindE + ES*cosdE;
204  fi3 = EC*cosdE - ES*sindE;
205 
206  di1 = -fi / fi1;
207  di2 = -fi/(fi1 + 0.5*di1*fi2);
208  di3 = -fi/(fi1 + 0.5*di2*fi2 + di2*di2*fi3/6.0);
209 
210  return dEi + di3;
211 }
212 
213 ///Solve Kepler's equation in difference form using Danby's quartic Newton-Raphson method.
214 /** \param dE [output] is the eccentric anomaly
215  * \param D [output] is the error after the last iteration
216  * \param e [input] is the eccentricity
217  * \param EC [input]
218  * \param ES [input]
219  * \param dM [input]
220  * \param tol [input] is the desired tolerance
221  * \param itmax [input] is the maximum number of iterations
222  * \retval -1 on exceeding itmax, otherwise reurns the number of iterations.
223  */
224 template<typename arithT>
225 long solve_keplerdiff_danby(arithT *dE, arithT *D, arithT e, arithT EC, arithT ES, arithT dM, arithT tol, long itmax)
226 {
227  long i;
228  arithT lastdE, sindE, sign;
229 
230  sindE = ES*cos(dM-ES) + EC*sin(dM-ES);
231  sign = 1.0;
232  if(sindE < 0.0) sign = -1.0;
233 
234  (*dE) = dM + 0.85*sign*e-ES;
235 
236  for(i=0; i < itmax; i++)
237  {
238  lastdE = (*dE);
239  (*dE) = kepler_danby_diff_1(EC, ES, dM, (*dE));
240 
241  //Test for convergence to within tol
242  //Make sure we have iterated at least twice to prevent early convergence
243  if(i > 0. && fabs((*dE)-lastdE) < tol)
244  {
245  (*D) = dM - (*dE - EC*sin(*dE) + ES*(1-cos(*dE)));
246  return i;
247  }
248  }
249 
250  return -1; //This means itmax exceeded.
251 }
252 
253 
254 
255 
256 
257 
258 ///Calculate the inclination and the longitude of the ascending node given a point on the orbit projected onto the plane of the sky, the argument of pericenter, the current true radius, and the current true anomaly.
259 /** Solutions are not always possible. You must check the return value to know if the results are valid.
260  * \param i [output] the inclination (there will be 2)
261  * \param W [output] the longitude of the ascending node (there will be 2)
262  * \param x [input] the x coordinate of the projection onto the reference plane
263  * \param y [input] the y coordinate of the projection onto the reference plane
264  * \param rho [input] the projected separation (yes this is redundant to x and y, but it is passed independently for efficiency)
265  * \param r [input] the physical (3D) separation
266  * \param f [input] the true anomaly
267  * \param w [input] the argument of pericenter
268  * \retval 0 on success
269  * \retval -1 if no solution is possible due to ...
270  * \retval -2 if no solution is possible due to z = 0
271  * \retval -3 if no solution is possible due to ...
272  */
273 //int get_iW_1pt(mx::Vectord &i, mx::Vectord &W, double x, double y, double rho, double r, double f, double w);
274 
275 ///Calculate the longitude of the ascending node given a point on the orbit projected onto the plane of the sky, the argument of pericenter, the current radius, and the current true anomaly.
276 /** Used for the z = 0, i != 0/PI case. i.e. sin(w+f) = 0, cos(w+f) = +/- 1.
277  * w must be either -f or PI - f.
278  * \param W [output] the longitude of the ascending node.
279  * \param x [input] the x coordinate of the starting point
280  * \param y [input] the y coordinate of the starting point
281  * \param r [input] the current 3D separation from the star
282  * \param f [input] the current true anomaly
283  * \param w [input] the argument of pericenter
284  * \retval 0 on success
285  * \retval -1 if the conditions aren't met by w an df
286  */
287 template<typename arithT>
288 int get_W_1pt_z0(arithT &W, arithT x, arithT y, arithT r, arithT f, arithT w)
289 {
290  arithT sinW, cosW;
291 
292  if(w == -f)
293  {
294  sinW = y/r;
295  cosW = x/r;
296  }
297  else if(w == DPI - f)
298  {
299  sinW = -y/r;
300  cosW = -x/r;
301  }
302  else return -1;
303 
304  W = atan2(sinW, cosW);
305  return 0;
306 }
307 
308 #endif
309 
310 } //namespace astro
311 } //namespace mx
312 #endif //__mx_astro_kepler_hpp__
T sign(T x)
The sign function.
Definition: sign.hpp:30
long solve_kepler_danby(realT &E, realT &D, realT e, realT M, realT tol, long itmax)
Solve Kepler's equation using Danby's quartic Newton-Raphson method.
Definition: kepler.hpp:119
realT kepler_danby_1(realT e, realT M, realT Ei)
Calculate the next iteration of Danby's quartic Newton-Raphson method.
Definition: kepler.hpp:89
long hyperbolic_kepler(realT &E, realT &err, realT e, realT M, realT tol, long itmax)
Solve the hyperbolic kepler equation (for e> 1).
Definition: kepler.hpp:50
long solve_kepler(realT &E, realT &D, realT e, realT M, realT tol=KEPLER_TOL, long itmax=KEPLER_ITMAX)
Solve Kepler's equation for any e. Uses solve_kepler_danby if e < 1.0, hyperbolic_kepler otherwise.
Definition: kepler.hpp:161
arithT kepler_danby_diff_1(arithT EC, arithT ES, arithT dM, arithT dEi)
Calculate the next iteration of Danby's quartic Newton-Raphson method (difference form).
Definition: kepler.hpp:191
int get_W_1pt_z0(arithT &W, arithT x, arithT y, arithT r, arithT f, arithT w)
Calculate the inclination and the longitude of the ascending node given a point on the orbit projecte...
Definition: kepler.hpp:288
long solve_keplerdiff_danby(arithT *dE, arithT *D, arithT e, arithT EC, arithT ES, arithT dM, arithT tol, long itmax)
Solve Kepler's equation in difference form using Danby's quartic Newton-Raphson method.
Definition: kepler.hpp:225
The mxlib c++ namespace.
Definition: mxError.hpp:107
Unit specifications and conversions.