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 }