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) 2000 - 2005 Liam Girdwood 17 */ 18 19 module nova.pluto; 20 21 import std.math; 22 23 import nova.vsop87; 24 import nova.solar; 25 import nova.earth; 26 import nova.transform; 27 import nova.rise_set; 28 import nova.utility; 29 30 enum PLUTO_COEFFS = 43; 31 32 struct pluto_argument { 33 double J, S, P; 34 } 35 36 struct pluto_longitude { 37 double A, B; 38 } 39 40 struct pluto_latitude { 41 double A, B; 42 } 43 44 struct pluto_radius { 45 double A, B; 46 } 47 48 /* cache variables */ 49 static double cJD = 0.0, cL = 0.0, cB = 0.0, cR = 0.0; 50 51 static const pluto_argument[PLUTO_COEFFS] argument = [ 52 {0, 0, 1}, 53 {0, 0, 2}, 54 {0, 0, 3}, 55 {0, 0, 4}, 56 {0, 0, 5}, 57 {0, 0, 6}, 58 {0, 1, -1}, 59 {0, 1, 0}, 60 {0, 1, 1}, 61 {0, 1, 2}, 62 {0, 1, 3}, 63 {0, 2, -2}, 64 {0, 2, -1}, 65 {0, 2, 0}, 66 {1, -1, 0}, 67 {1, -1, 1}, 68 {1, 0, -3}, 69 {1, 0, -2}, 70 {1, 0, -1}, 71 {1, 0, 0}, 72 {1, 0, 1}, 73 {1, 0, 2}, 74 {1, 0, 3}, 75 {1, 0, 4}, 76 {1, 1, -3}, 77 {1, 1, -2}, 78 {1, 1, -1}, 79 {1, 1, 0}, 80 {1, 1, 1}, 81 {1, 1, 3}, 82 {2, 0, -6}, 83 {2, 0, -5}, 84 {2, 0, -4}, 85 {2, 0, -3}, 86 {2, 0, -2}, 87 {2, 0, -1}, 88 {2, 0, 0}, 89 {2, 0, 1}, 90 {2, 0, 2}, 91 {2, 0, 3}, 92 {3, 0, -2}, 93 {3, 0, -1}, 94 {3, 0, 0} 95 ]; 96 97 98 static const pluto_longitude[PLUTO_COEFFS] longitude = [ 99 {-19799805, 19850055}, 100 {897144, -4954829}, 101 {611149, 1211027}, 102 {-341243, -189585}, 103 {129287, -34992}, 104 {-38164, 30893}, 105 {20442, -9987}, 106 {-4063, -5071}, 107 {-6016, -3336}, 108 {-3956, 3039}, 109 {-667, 3572}, 110 {1276, 501}, 111 {1152, -917}, 112 {630, -1277}, 113 {2571, -459}, 114 {899, -1449}, 115 {-1016, 1043}, 116 {-2343, -1012}, 117 {7042, 788}, 118 {1199, -338}, 119 {418, -67}, 120 {120, -274}, 121 {-60, -159}, 122 {-82, -29}, 123 {-36, -20}, 124 {-40, 7}, 125 {-14, 22}, 126 {4, 13}, 127 {5,2}, 128 {-1,0}, 129 {2,0}, 130 {-4, 5}, 131 {4, -7}, 132 {14, 24}, 133 {-49, -34}, 134 {163, -48}, 135 {9, 24}, 136 {-4, 1}, 137 {-3,1}, 138 {1,3}, 139 {-3, -1}, 140 {5, -3}, 141 {0,0} 142 ]; 143 144 static const pluto_latitude[PLUTO_COEFFS] latitude = [ 145 {-5452852, -14974862}, 146 {3527812, 1672790}, 147 {-1050748, 327647}, 148 {178690, -292153}, 149 {18650, 100340}, 150 {-30697, -25823}, 151 {4878, 11248}, 152 {226, -64}, 153 {2030, -836}, 154 {69, -604}, 155 {-247, -567}, 156 {-57, 1}, 157 {-122, 175}, 158 {-49, -164}, 159 {-197, 199}, 160 {-25, 217}, 161 {589, -248}, 162 {-269, 711}, 163 {185, 193}, 164 {315, 807}, 165 {-130, -43}, 166 {5, 3}, 167 {2, 17}, 168 {2, 5}, 169 {2, 3}, 170 {3, 1}, 171 {2, -1}, 172 {1, -1}, 173 {0, -1}, 174 {0, 0}, 175 {0, -2}, 176 {2, 2}, 177 {-7, 0}, 178 {10, -8}, 179 {-3, 20}, 180 {6, 5}, 181 {14, 17}, 182 {-2, 0}, 183 {0, 0}, 184 {0, 0}, 185 {0, 1}, 186 {0, 0}, 187 {1, 0} 188 ]; 189 190 static const pluto_radius[PLUTO_COEFFS] radius = [ 191 {66865439, 68951812}, 192 {-11827535, -332538}, 193 {1593179, -1438890}, 194 {-18444, 483220}, 195 {-65977, -85431}, 196 {31174, -6032}, 197 {-5794, 22161}, 198 {4601, 4032}, 199 {-1729, 234}, 200 {-415, 702}, 201 {239, 723}, 202 {67, -67}, 203 {1034, -451}, 204 {-129, 504}, 205 {480, -231}, 206 {2, -441}, 207 {-3359, 265}, 208 {7856, -7832}, 209 {36, 45763}, 210 {8663, 8547}, 211 {-809, -769}, 212 {263, -144}, 213 {-126, 32}, 214 {-35, -16}, 215 {-19, -4}, 216 {-15, 8}, 217 {-4, 12}, 218 {5, 6}, 219 {3, 1}, 220 {6, -2}, 221 {2, 2}, 222 {-2, -2}, 223 {14, 13}, 224 {-63, 13}, 225 {136, -236}, 226 {273, 1065}, 227 {251, 149}, 228 {-25, -9}, 229 {9, -2}, 230 {-8, 7}, 231 {2, -10}, 232 {19, 35}, 233 {10, 2} 234 ]; 235 236 extern (C) { 237 238 /*! \fn void ln_get_pluto_equ_coords(double JD, struct ln_equ_posn *position); 239 * \param JD julian Day 240 * \param position Pointer to store position 241 * 242 * Calculates Pluto's equatorial position for the given julian day. 243 */ 244 @nogc void ln_get_pluto_equ_coords(double JD, ref ln_equ_posn position) nothrow 245 { 246 ln_helio_posn h_sol, h_pluto; 247 ln_rect_posn g_sol, g_pluto; 248 double a,b,c; 249 double ra, dec, delta, diff, last, t = 0; 250 251 /* need typdef for solar heliocentric coords */ 252 ln_get_solar_geom_coords(JD, h_sol); 253 ln_get_rect_from_helio(h_sol, g_sol); 254 255 do { 256 last = t; 257 ln_get_pluto_helio_coords(JD - t, h_pluto); 258 ln_get_rect_from_helio(h_pluto, g_pluto); 259 260 /* equ 33.10 pg 229 */ 261 a = g_sol.X + g_pluto.X; 262 b = g_sol.Y + g_pluto.Y; 263 c = g_sol.Z + g_pluto.Z; 264 265 delta = a * a + b * b + c * c; 266 delta = sqrt(delta); 267 t = delta * 0.0057755183; 268 diff = t - last; 269 } while (diff > 0.0001 || diff < -0.0001); 270 271 ra = atan2(b, a); 272 dec = c / delta; 273 dec = asin(dec); 274 275 /* back to hours, degrees */ 276 position.ra = ln_range_degrees(ln_rad_to_deg(ra)); 277 position.dec = ln_rad_to_deg(dec); 278 } 279 280 281 /*! \fn void ln_get_pluto_helio_coords(double JD, struct ln_helio_posn *position) 282 * \param JD Julian Day 283 * \param position Pointer to store new heliocentric position 284 * 285 * Calculate Pluto's heliocentric coordinates for the given julian day. 286 * This function is accurate to within 0.07" in longitude, 0.02" in latitude 287 * and 0.000006 AU in radius vector. 288 * 289 * Note: This function is not valid outside the period of 1885-2099. 290 */ 291 /* Chap 37. Equ 37.1 292 */ 293 294 @nogc void ln_get_pluto_helio_coords(double JD, ref ln_helio_posn position) nothrow 295 { 296 double sum_longitude = 0, sum_latitude = 0, sum_radius = 0; 297 double J, S, P; 298 double t, a, sin_a, cos_a; 299 int i; 300 301 /* check cache first */ 302 if(JD == cJD) { 303 /* cache hit */ 304 position.L = cL; 305 position.B = cB; 306 position.R = cR; 307 return; 308 } 309 310 /* get julian centuries since J2000 */ 311 t =(JD - 2451545.0) / 36525.0; 312 313 /* calculate mean longitudes for jupiter, saturn and pluto */ 314 J = 34.35 + 3034.9057 * t; 315 S = 50.08 + 1222.1138 * t; 316 P = 238.96 + 144.9600 * t; 317 318 /* calc periodic terms in table 37.A */ 319 for (i = 0; i < PLUTO_COEFFS; i++) { 320 a = argument[i].J * J + argument[i].S * S + argument[i].P * P; 321 sin_a = sin(ln_deg_to_rad(a)); 322 cos_a = cos(ln_deg_to_rad(a)); 323 324 /* longitude */ 325 sum_longitude += longitude[i].A * sin_a + longitude[i].B * cos_a; 326 327 /* latitude */ 328 sum_latitude += latitude[i].A * sin_a + latitude[i].B * cos_a; 329 330 /* radius */ 331 sum_radius += radius[i].A * sin_a + radius[i].B * cos_a; 332 } 333 334 /* calc L, B, R */ 335 position.L = 238.958116 + 144.96 * t + sum_longitude * 0.000001; 336 position.B = -3.908239 + sum_latitude * 0.000001; 337 position.R = 40.7241346 + sum_radius * 0.0000001; 338 339 /* save cache */ 340 cJD = JD; 341 cL = position.L; 342 cB = position.B; 343 cR = position.R; 344 } 345 346 /*! \fn double ln_get_pluto_earth_dist(double JD); 347 * \param JD Julian day 348 * \return Distance in AU 349 * 350 * Calculates the distance in AU between the Earth and Pluto for the 351 * given julian day. 352 */ 353 @nogc double ln_get_pluto_earth_dist(double JD) nothrow 354 { 355 ln_helio_posn h_pluto, h_earth; 356 ln_rect_posn g_pluto, g_earth; 357 double x, y, z; 358 359 /* get heliocentric positions */ 360 ln_get_pluto_helio_coords(JD, h_pluto); 361 ln_get_earth_helio_coords(JD, h_earth); 362 363 /* get geocentric coords */ 364 ln_get_rect_from_helio(h_pluto, g_pluto); 365 ln_get_rect_from_helio(h_earth, g_earth); 366 367 /* use pythag */ 368 x = g_pluto.X - g_earth.X; 369 y = g_pluto.Y - g_earth.Y; 370 z = g_pluto.Z - g_earth.Z; 371 x = x * x; 372 y = y * y; 373 z = z * z; 374 375 return sqrt(x + y + z); 376 } 377 378 /*! \fn double ln_get_pluto_solar_dist(double JD); 379 * \param JD Julian day 380 * \return Distance in AU 381 * 382 * Calculates the distance in AU between the Sun and Pluto for the 383 * given julian day. 384 */ 385 @nogc double ln_get_pluto_solar_dist(double JD) nothrow 386 { 387 ln_helio_posn h_pluto; 388 389 /* get heliocentric position */ 390 ln_get_pluto_helio_coords(JD, h_pluto); 391 return h_pluto.R; 392 } 393 394 /*! \fn double ln_get_pluto_magnitude(double JD); 395 * \param JD Julian day 396 * \return Visible magnitude of Pluto 397 * 398 * Calculate the visible magnitude of Pluto for the given 399 * julian day. 400 */ 401 @nogc double ln_get_pluto_magnitude(double JD) nothrow 402 { 403 double delta, r; 404 405 /* get distances */ 406 r = ln_get_pluto_solar_dist(JD); 407 delta = ln_get_pluto_earth_dist(JD); 408 409 return -1.0 + 5.0 * log10(r * delta); 410 } 411 412 /*! \fn double ln_get_pluto_disk(double JD); 413 * \param JD Julian day 414 * \return Illuminated fraction of Plutos disk 415 * 416 * Calculate the illuminated fraction of Pluto's disk for 417 * the given julian day. 418 */ 419 /* Chapter 41 */ 420 @nogc double ln_get_pluto_disk(double JD) nothrow 421 { 422 double r,delta,R; 423 424 /* get distances */ 425 R = ln_get_earth_solar_dist(JD); 426 r = ln_get_pluto_solar_dist(JD); 427 delta = ln_get_pluto_earth_dist(JD); 428 429 /* calc fraction angle */ 430 return (((r + delta) * (r + delta)) - R * R) / (4.0 * r * delta); 431 } 432 433 /*! \fn double ln_get_pluto_phase(double JD); 434 * \param JD Julian day 435 * \return Phase angle of Pluto (degrees) 436 * 437 * Calculate the phase angle of Pluto (Sun - Pluto - Earth) 438 * for the given julian day. 439 */ 440 /* Chapter 41 */ 441 @nogc double ln_get_pluto_phase(double JD) nothrow 442 { 443 double i,r,delta,R; 444 445 /* get distances */ 446 R = ln_get_earth_solar_dist(JD); 447 r = ln_get_pluto_solar_dist(JD); 448 delta = ln_get_pluto_earth_dist(JD); 449 450 /* calc phase */ 451 i = (r * r + delta * delta - R * R) / (2.0 * r * delta); 452 i = acos(i); 453 return ln_rad_to_deg(i); 454 } 455 456 457 /*! \fn double ln_get_pluto_rst(double JD, struct ln_lnlat_posn *observer, struct ln_rst_time *rst); 458 * \param JD Julian day 459 * \param observer Observers position 460 * \param rst Pointer to store Rise, Set and Transit time in JD 461 * \return 0 for success, else 1 for circumpolar. 462 * 463 * Calculate the time the rise, set and transit (crosses the local meridian at upper culmination) 464 * time of Pluto for the given Julian day. 465 * 466 * Note: this functions returns 1 if Pluto is circumpolar, that is it remains the whole 467 * day either above or below the horizon. 468 */ 469 @nogc int ln_get_pluto_rst(double JD, const ref ln_lnlat_posn observer, 470 ref ln_rst_time rst) nothrow 471 { 472 return ln_get_body_rst_horizon(JD, observer, &ln_get_pluto_equ_coords, 473 LN_STAR_STANDART_HORIZON, rst); 474 } 475 476 477 /*! \fn double ln_get_pluto_sdiam(double JD) 478 * \param JD Julian day 479 * \return Semidiameter in arc seconds 480 * 481 * Calculate the semidiameter of Pluto in arc seconds for the 482 * given julian day. 483 */ 484 @nogc double ln_get_pluto_sdiam(double JD) nothrow 485 { 486 double So = 2.07; /* at 1 AU */ 487 double dist; 488 489 dist = ln_get_pluto_earth_dist(JD); 490 return So / dist; 491 } 492 493 /*! \fn void ln_get_pluto_rect_helio(double JD, struct ln_rect_posn *position) 494 * \param JD Julian day. 495 * \param position pointer to return position 496 * 497 * Calculate Plutos rectangular heliocentric coordinates for the 498 * given Julian day. Coordinates are in AU. 499 */ 500 @nogc void ln_get_pluto_rect_helio(double JD, ref ln_rect_posn position) nothrow 501 { 502 ln_helio_posn pluto; 503 504 ln_get_pluto_helio_coords(JD, pluto); 505 ln_get_rect_from_helio(pluto, position); 506 } 507 508 }