1 /* 2 * This library is free software; you can redistribute it and/or 3 * modify it under the terms of the GNU Lesser General Public 4 * License as published by the Free Software Foundation; either 5 * version 2 of the License, or (at your option) any later version. 6 * 7 * This library is distributed in the hope that it will be useful, 8 * but WITHOUT ANY WARRANTY; without even the implied warranty of 9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 10 * Lesser General Public License for more details. 11 * 12 * You should have received a copy of the GNU General Public License 13 * along with this program; if not, write to the Free Software 14 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 15 * 16 * Copyright (C) 2002 - 2005 Liam Girdwood <lgirdwood@gmail.com> 17 * Copyright (C) 2004 Petr Kubanek 18 */ 19 20 module nova.hyperbolic_motion; 21 22 import std.math; 23 24 import nova.parabolic_motion; 25 import nova.solar; 26 import nova.earth; 27 import nova.transform; 28 import nova.rise_set; 29 import nova.dynamical_time; 30 import nova.sidereal_time; 31 import nova.utility; 32 33 enum GAUS_GRAV = 0.01720209895; /* Gaussian gravitational constant k */ 34 enum PREC = 1e-10; 35 36 extern (C) { 37 38 /*! \fn double ln_solve_hyp_barker (double Q1, double G, double t); 39 * \param Q1 See 35.0 40 * \param G See 35.0 41 * \param t Time since perihelion in days 42 * \return Solution of Barkers equation 43 * 44 * Solve Barkers equation. LIAM add more 45 */ 46 /* Equ 34.3, Barkers Equation */ 47 @nogc double ln_solve_hyp_barker (double Q1, double G, double t) nothrow 48 { 49 double S, S0, S1, Y, G1, Q2, Q3, Z1, F; 50 int Z, L; 51 52 Q2 = Q1 * t; 53 S = 2 / (3 * fabs(Q2)); 54 S = 2 / tan(2 * atan(cbrt (tan(atan(S) / 2)))); 55 56 if (t< 0) 57 S = -S; 58 59 L = 0; 60 61 /* we have initial s, so now do the iteration */ 62 do { 63 S0 = S; 64 Z = 1; 65 Y = S * S; 66 G1 = -Y * S; 67 Q3 = Q2 + 2.0 * G * S * Y / 3.0; 68 69 next_z: 70 Z++; 71 G1 = -G1 * G * Y; 72 Z1 = (Z - (Z + 1) * G) / (2.0 * Z + 1.0); 73 F = Z1 * G1; 74 Q3 = Q3 + F; 75 76 if (Z > 100 || fabs(F) > 10000) 77 return double.nan; 78 79 if (fabs(F) > PREC) 80 goto next_z; 81 82 L++; 83 if (L > 100) 84 return double.nan; 85 86 do { 87 S1 = S; 88 S = (2 * S * S * S / 3 + Q3) / (S * S + 1); 89 } while (fabs(S - S1) > PREC); 90 } while (fabs(S - S0) > PREC); 91 92 return S; 93 } 94 95 /*! \fn double ln_get_hyp_true_anomaly (double q, double e, double t); 96 * \param q Perihelion distance in AU 97 * \param e Orbit eccentricity 98 * \param t Time since perihelion 99 * \return True anomaly (degrees) 100 * 101 * Calculate the true anomaly. 102 */ 103 /* equ 30.1 */ 104 @nogc double ln_get_hyp_true_anomaly (double q, double e, double t) nothrow 105 { 106 double v, s, Q, gama; 107 108 Q = (GAUS_GRAV / (2.0 * q)) * sqrt((1.0 + e) / q); 109 gama = (1.0 - e) / (1.0 + e); 110 111 s = ln_solve_hyp_barker(Q, gama, t); 112 v = 2.0 * atan(s); 113 114 return ln_range_degrees(ln_rad_to_deg(v)); 115 } 116 117 /*! \fn double ln_get_hyp_radius_vector (double q, double e, double t); 118 * \param q Perihelion distance in AU 119 * \param e Orbit eccentricity 120 * \param t Time since perihelion in days 121 * \return Radius vector AU 122 * 123 * Calculate the radius vector. 124 */ 125 /* equ 30.2 */ 126 @nogc double ln_get_hyp_radius_vector (double q, double e, double t) nothrow 127 { 128 return q * (1.0 + e) / 129 (1.0 + e * cos(ln_deg_to_rad(ln_get_hyp_true_anomaly(q, e, t)))); 130 } 131 132 /*! \fn void ln_get_hyp_helio_rect_posn(struct ln_hyp_orbit *orbit, double JD, struct ln_rect_posn *posn); 133 * \param orbit Orbital parameters of object. 134 * \param JD Julian day 135 * \param posn Position pointer to store objects position 136 * 137 * Calculate the objects rectangular heliocentric position given it's orbital 138 * elements for the given julian day. 139 */ 140 @nogc void ln_get_hyp_helio_rect_posn(ref ln_hyp_orbit orbit, double JD, 141 ref ln_rect_posn posn) nothrow 142 { 143 double A, B, C, F, G, H, P, Q, R; 144 double sin_e, cos_e; 145 double a, b, c; 146 double sin_omega, sin_i, cos_omega, cos_i; 147 double r, v, t; 148 149 /* time since perihelion */ 150 t = JD - orbit.JD; 151 152 /* J2000 obliquity of the ecliptic */ 153 sin_e = 0.397777156; 154 cos_e = 0.917482062; 155 156 /* equ 33.7 */ 157 sin_omega = sin(ln_deg_to_rad(orbit.omega)); 158 cos_omega = cos(ln_deg_to_rad(orbit.omega)); 159 sin_i = sin(ln_deg_to_rad(orbit.i)); 160 cos_i = cos(ln_deg_to_rad(orbit.i)); 161 F = cos_omega; 162 G = sin_omega * cos_e; 163 H = sin_omega * sin_e; 164 P = -sin_omega * cos_i; 165 Q = cos_omega * cos_i * cos_e - sin_i * sin_e; 166 R = cos_omega * cos_i * sin_e + sin_i * cos_e; 167 168 /* equ 33.8 */ 169 A = atan2(F, P); 170 B = atan2(G, Q); 171 C = atan2(H, R); 172 a = sqrt(F * F + P * P); 173 b = sqrt(G * G + Q * Q); 174 c = sqrt(H * H + R * R); 175 176 /* get true anomaly */ 177 v = ln_get_hyp_true_anomaly(orbit.q, orbit.e, t); 178 179 /* get radius vector */ 180 r = ln_get_hyp_radius_vector(orbit.q, orbit.e, t); 181 182 /* equ 33.9 */ 183 posn.X = r * a * sin(A + ln_deg_to_rad(orbit.w + v)); 184 posn.Y = r * b * sin(B + ln_deg_to_rad(orbit.w + v)); 185 posn.Z = r * c * sin(C + ln_deg_to_rad(orbit.w + v)); 186 } 187 188 /*! \fn void ln_get_hyp_geo_rect_posn(struct ln_hyp_orbit *orbit, double JD, struct ln_rect_posn *posn); 189 * \param orbit Orbital parameters of object. 190 * \param JD Julian day 191 * \param posn Position pointer to store objects position 192 * 193 * Calculate the objects rectangular geocentric position given it's orbital 194 * elements for the given julian day. 195 */ 196 @nogc void ln_get_hyp_geo_rect_posn(ref ln_hyp_orbit orbit, double JD, 197 ref ln_rect_posn posn) nothrow 198 { 199 ln_rect_posn p_posn, e_posn; 200 ln_helio_posn earth; 201 202 /* parabolic helio rect coords */ 203 ln_get_hyp_helio_rect_posn(orbit, JD, p_posn); 204 205 /* earth rect coords */ 206 ln_get_earth_helio_coords(JD, earth); 207 208 ln_get_rect_from_helio(earth, e_posn); 209 posn.X = p_posn.X - e_posn.X; 210 posn.Y = p_posn.Y - e_posn.Y; 211 posn.Z = p_posn.Z - e_posn.Z; 212 } 213 214 /*! 215 * \fn void ln_get_hyp_body_equ_coords(double JD, struct ln_hyp_orbit *orbit, struct ln_equ_posn *posn) 216 * \param JD Julian Day. 217 * \param orbit Orbital parameters. 218 * \param posn Pointer to hold asteroid position. 219 * 220 * Calculate a bodies equatorial coordinates for the given julian day. 221 */ 222 @nogc void ln_get_hyp_body_equ_coords(double JD, ref ln_hyp_orbit orbit, 223 ref ln_equ_posn posn) nothrow 224 { 225 ln_rect_posn body_rect_posn, sol_rect_posn; 226 double dist, t; 227 double x, y, z; 228 229 /* get solar and body rect coords */ 230 ln_get_hyp_helio_rect_posn(orbit, JD, body_rect_posn); 231 ln_get_solar_geo_coords(JD, sol_rect_posn); 232 233 /* calc distance and light time */ 234 dist = ln_get_rect_distance(body_rect_posn, sol_rect_posn); 235 t = ln_get_light_time(dist); 236 237 /* repeat calculation with new time (i.e. JD - t) */ 238 ln_get_hyp_helio_rect_posn(orbit, JD - t, body_rect_posn); 239 240 /* calc equ coords equ 33.10 */ 241 x = sol_rect_posn.X + body_rect_posn.X; 242 y = sol_rect_posn.Y + body_rect_posn.Y; 243 z = sol_rect_posn.Z + body_rect_posn.Z; 244 245 posn.ra = ln_range_degrees(ln_rad_to_deg(atan2(y, x))); 246 posn.dec = ln_rad_to_deg(atan2(z,sqrt(x * x + y * y))); 247 } 248 249 /*! 250 * \fn double ln_get_hyp_body_earth_dist(double JD, struct ln_hyp_orbit *orbit) 251 * \param JD Julian day. 252 * \param orbit Orbital parameters 253 * \returns Distance in AU 254 * 255 * Calculate the distance between a body and the Earth 256 * for the given julian day. 257 */ 258 @nogc double ln_get_hyp_body_earth_dist(double JD, ref ln_hyp_orbit orbit) nothrow 259 { 260 ln_rect_posn body_rect_posn, earth_rect_posn; 261 262 /* get solar and body rect coords */ 263 ln_get_hyp_geo_rect_posn(orbit, JD, body_rect_posn); 264 earth_rect_posn.X = 0.0; 265 earth_rect_posn.Y = 0.0; 266 earth_rect_posn.Z = 0.0; 267 268 /* calc distance */ 269 return ln_get_rect_distance(body_rect_posn, earth_rect_posn); 270 } 271 272 /*! 273 * \fn double ln_get_hyp_body_solar_dist(double JD, struct ln_hyp_orbit *orbit) 274 * \param JD Julian Day. 275 * \param orbit Orbital parameters 276 * \return The distance in AU between the Sun and the body. 277 * 278 * Calculate the distance between a body and the Sun. 279 */ 280 @nogc double ln_get_hyp_body_solar_dist(double JD, ref ln_hyp_orbit orbit) nothrow 281 { 282 ln_rect_posn body_rect_posn, sol_rect_posn; 283 284 /* get solar and body rect coords */ 285 ln_get_hyp_helio_rect_posn (orbit, JD, body_rect_posn); 286 sol_rect_posn.X = 0.0; 287 sol_rect_posn.Y = 0.0; 288 sol_rect_posn.Z = 0.0; 289 290 /* calc distance */ 291 return ln_get_rect_distance(body_rect_posn, sol_rect_posn); 292 } 293 294 /*! \fn double ln_get_hyp_body_phase_angle(double JD, struct ln_hyp_orbit *orbit); 295 * \param JD Julian day 296 * \param orbit Orbital parameters 297 * \return Phase angle. 298 * 299 * Calculate the phase angle of the body. The angle Sun - body - Earth. 300 */ 301 @nogc double ln_get_hyp_body_phase_angle(double JD, ref ln_hyp_orbit orbit) nothrow 302 { 303 double r, R, d; 304 double t; 305 double phase; 306 307 /* time since perihelion */ 308 t = JD - orbit.JD; 309 310 /* get radius vector */ 311 r = ln_get_hyp_radius_vector(orbit.q, orbit.e, t); 312 313 /* get solar and Earth-Sun distances */ 314 R = ln_get_earth_solar_dist(JD); 315 d = ln_get_hyp_body_solar_dist(JD, orbit); 316 317 phase = (r * r + d * d - R * R) / (2.0 * r * d); 318 return ln_range_degrees(ln_rad_to_deg(acos(phase))); 319 } 320 321 /*! \fn double ln_get_hyp_body_elong(double JD, struct ln_hyp_orbit *orbit); 322 * \param JD Julian day 323 * \param orbit Orbital parameters 324 * \return Elongation to the Sun. 325 * 326 * Calculate the bodies elongation to the Sun.. 327 */ 328 @nogc double ln_get_hyp_body_elong(double JD, ref ln_hyp_orbit orbit) nothrow 329 { 330 double r, R, d; 331 double t; 332 double elong; 333 334 /* time since perihelion */ 335 t = JD - orbit.JD; 336 337 /* get radius vector */ 338 r = ln_get_hyp_radius_vector(orbit.q, orbit.e, t); 339 340 /* get solar and Earth-Sun distances */ 341 R = ln_get_earth_solar_dist(JD); 342 d = ln_get_hyp_body_solar_dist(JD, orbit); 343 344 elong = (R * R + d * d - r * r) / ( 2.0 * R * d ); 345 return ln_range_degrees(ln_rad_to_deg(acos(elong))); 346 } 347 348 /*! \fn double ln_get_hyp_body_rst(double JD, struct ln_lnlat_posn *observer, struct ln_hyp_orbit *orbit, struct ln_rst_time *rst); 349 * \param JD Julian day 350 * \param observer Observers position 351 * \param orbit Orbital parameters 352 * \param rst Pointer to store Rise, Set and Transit time in JD 353 * \return 0 for success, else 1 for circumpolar. 354 * 355 * Calculate the time the rise, set and transit (crosses the local meridian at upper culmination) 356 * time of a body with a parabolic orbit for the given Julian day. 357 * 358 * Note: this functions returns 1 if the body is circumpolar, that is it remains the whole 359 * day above or below the horizon. Returns -1 when it remains whole day below the horizon. 360 */ 361 @nogc int ln_get_hyp_body_rst(double JD, const ref ln_lnlat_posn observer, 362 ref ln_hyp_orbit orbit, ref ln_rst_time rst) nothrow 363 { 364 return ln_get_hyp_body_rst_horizon(JD, observer, orbit, 365 LN_STAR_STANDART_HORIZON, rst); 366 } 367 368 /*! \fn double ln_get_hyp_body_rst_horizon(double JD, struct ln_lnlat_posn *observer, struct ln_hyp_orbit *orbit, double horizon, struct ln_rst_time *rst); 369 * \param JD Julian day 370 * \param observer Observers position 371 * \param orbit Orbital parameters 372 * \param horizon Horizon height 373 * \param rst Pointer to store Rise, Set and Transit time in JD 374 * \return 0 for success, 1 for circumpolar (above the horizon), -1 for circumpolar (bellow the horizon) 375 * 376 * Calculate the time the rise, set and transit (crosses the local meridian at upper culmination) 377 * time of a body with a parabolic orbit for the given Julian day. 378 * 379 * Note: this functions returns 1 if the body is circumpolar, that is it remains the whole 380 * day above or below the horizon. Returns -1 when it remains whole day below the horizon. 381 */ 382 @nogc int ln_get_hyp_body_rst_horizon(double JD, const ref ln_lnlat_posn observer, 383 ref ln_hyp_orbit orbit, double horizon, ref ln_rst_time rst) nothrow 384 { 385 return ln_get_motion_body_rst_horizon(JD, observer, 386 cast(get_motion_body_coords_t)&ln_get_hyp_body_equ_coords, &orbit, 387 horizon, rst); 388 } 389 390 /*! \fn double ln_get_hyp_body_next_rst(double JD, struct ln_lnlat_posn *observer, struct ln_hyp_orbit *orbit, struct ln_rst_time *rst); 391 * \param JD Julian day 392 * \param observer Observers position 393 * \param orbit Orbital parameters 394 * \param rst Pointer to store Rise, Set and Transit time in JD 395 * \return 0 for success, else 1 for circumpolar (above the horizon), -1 for circumpolar (bellow the horizon) 396 * 397 * Calculate the time of next rise, set and transit (crosses the local meridian at upper culmination) 398 * time of a body with an hyperbolic orbit for the given Julian day. 399 * 400 * This function guarantee, that rise, set and transit will be in <JD, JD+1> range. 401 * 402 * Note: this functions returns 1 if the body is circumpolar, that is it remains the whole 403 * day above the horizon. Returns -1 when it remains the whole day below the horizon. 404 */ 405 @nogc int ln_get_hyp_body_next_rst(double JD, const ref ln_lnlat_posn observer, 406 ref ln_hyp_orbit orbit, ref ln_rst_time rst) nothrow 407 { 408 return ln_get_hyp_body_next_rst_horizon(JD, observer, orbit, 409 LN_STAR_STANDART_HORIZON, rst); 410 } 411 412 /*! \fn double ln_get_hyp_body_next_rst_horizon(double JD, struct ln_lnlat_posn *observer, struct ln_hyp_orbit *orbit, double horizon, struct ln_rst_time *rst); 413 * \param JD Julian day 414 * \param observer Observers position 415 * \param orbit Orbital parameters 416 * \param horizon Horizon height 417 * \param rst Pointer to store Rise, Set and Transit time in JD 418 * \return 0 for success, else 1 for circumpolar (above the horizon), -1 for circumpolar (bellow the horizon) 419 * 420 * Calculate the time of next rise, set and transit (crosses the local meridian at upper culmination) 421 * time of a body with an hyperbolic orbit for the given Julian day. 422 * 423 * This function guarantee, that rise, set and transit will be in <JD, JD+1> range. 424 * 425 * Note: this functions returns 1 if the body is circumpolar, that is it remains the whole 426 * day above the horizon. Returns -1 when it remains the whole day below the horizon. 427 */ 428 @nogc int ln_get_hyp_body_next_rst_horizon(double JD, const ref ln_lnlat_posn observer, 429 ref ln_hyp_orbit orbit, double horizon, ref ln_rst_time rst) nothrow 430 { 431 return ln_get_motion_body_next_rst_horizon(JD, observer, 432 cast(get_motion_body_coords_t)&ln_get_hyp_body_equ_coords, &orbit, 433 horizon, rst); 434 } 435 436 /*! \fn double ln_get_hyp_body_next_rst_horizon_future(double JD, struct ln_lnlat_posn *observer, struct ln_hyp_orbit *orbit, double horizon, int day_limit, struct ln_rst_time *rst); 437 * \param JD Julian day 438 * \param observer Observers position 439 * \param orbit Orbital parameters 440 * \param horizon Horizon height 441 * \param day_limit Maximal number of days that will be searched for next rise and set 442 * \param rst Pointer to store Rise, Set and Transit time in JD 443 * \return 0 for success, else 1 for circumpolar (above the horizon), -1 for circumpolar (bellow the horizon) 444 * 445 * Calculate the time of next rise, set and transit (crosses the local meridian at upper culmination) 446 * time of a body with an hyperbolic orbit for the given Julian day. 447 * 448 * This function guarantee, that rise, set and transit will be in <JD, JD + day_limit> range. 449 * 450 * Note: this functions returns 1 if the body is circumpolar, that is it remains the whole 451 * day above the horizon. Returns -1 when it remains the whole day below the horizon. 452 */ 453 @nogc int ln_get_hyp_body_next_rst_horizon_future(double JD, 454 const ref ln_lnlat_posn observer, ref ln_hyp_orbit orbit, 455 double horizon, int day_limit, ref ln_rst_time rst) nothrow 456 { 457 return ln_get_motion_body_next_rst_horizon_future(JD, observer, 458 cast(get_motion_body_coords_t)&ln_get_hyp_body_equ_coords, &orbit, 459 horizon, day_limit, rst); 460 } 461 462 }