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 <lgirdwood@gmail.com>
17  *
18  *  The functions in this file use the Lunar Solution ELP 2000-82B by
19  *  Michelle Chapront-Touze and Jean Chapront.
20  */
21 
22 module nova.lunar;
23 
24 import std.math;
25 
26 import nova.vsop87;
27 import nova.solar;
28 import nova.earth;
29 import nova.transform;
30 import nova.rise_set;
31 import nova.utility;
32 
33 import nova.lunar_priv;
34 
35 import nova.elp.elp1;
36 import nova.elp.elp2;
37 import nova.elp.elp3;
38 import nova.elp.elp4;
39 import nova.elp.elp5;
40 import nova.elp.elp6;
41 import nova.elp.elp7;
42 import nova.elp.elp8;
43 import nova.elp.elp9;
44 import nova.elp.elp10;
45 import nova.elp.elp11;
46 import nova.elp.elp12;
47 import nova.elp.elp13;
48 import nova.elp.elp14;
49 import nova.elp.elp15;
50 import nova.elp.elp16;
51 import nova.elp.elp17;
52 import nova.elp.elp18;
53 import nova.elp.elp19;
54 import nova.elp.elp20;
55 import nova.elp.elp21;
56 import nova.elp.elp22;
57 import nova.elp.elp23;
58 import nova.elp.elp24;
59 import nova.elp.elp25;
60 import nova.elp.elp26;
61 import nova.elp.elp27;
62 import nova.elp.elp28;
63 import nova.elp.elp29;
64 import nova.elp.elp30;
65 import nova.elp.elp31;
66 import nova.elp.elp32;
67 import nova.elp.elp33;
68 import nova.elp.elp34;
69 import nova.elp.elp35;
70 import nova.elp.elp36;
71 
72 enum LN_LUNAR_STANDART_HORIZON = 0.125;
73 
74 /* AU in KM */
75 enum AU =			149597870;
76 
77 /* sequence sizes */
78 enum ELP1_SIZE =	1023;		/* Main problem. Longitude periodic terms (sine) */
79 enum ELP2_SIZE =	918;		/* Main problem. Latitude (sine) */
80 enum ELP3_SIZE =	704;		/* Main problem. Distance (cosine) */
81 enum ELP4_SIZE =	347;		/* Earth figure perturbations. Longitude */
82 enum ELP5_SIZE =	316;		/* Earth figure perturbations. Latitude */
83 enum ELP6_SIZE =	237;		/* Earth figure perturbations. Distance */
84 enum ELP7_SIZE =	14;		/* Earth figure perturbations. Longitude/t */
85 enum ELP8_SIZE =	11;		/* Earth figure perturbations. Latitude/t */
86 enum ELP9_SIZE =	8;		/* Earth figure perturbations. Distance/t */
87 enum ELP10_SIZE =	14328;		/* Planetary perturbations. Table 1 Longitude */
88 enum ELP11_SIZE =	5233;		/* Planetary perturbations. Table 1 Latitude */
89 enum ELP12_SIZE =	6631;		/* Planetary perturbations. Table 1 Distance */
90 enum ELP13_SIZE =	4384;		/* Planetary perturbations. Table 1 Longitude/t */
91 enum ELP14_SIZE =	833;		/* Planetary perturbations. Table 1 Latitude/t */
92 enum ELP15_SIZE =	1715;		/* Planetary perturbations. Table 1 Distance/t */
93 enum ELP16_SIZE =	170;		/* Planetary perturbations. Table 2 Longitude */
94 enum ELP17_SIZE =	150;		/* Planetary perturbations. Table 2 Latitude */
95 enum ELP18_SIZE =	114;		/* Planetary perturbations. Table 2 Distance */
96 enum ELP19_SIZE =	226;		/* Planetary perturbations. Table 2 Longitude/t */
97 enum ELP20_SIZE =	188;		/* Planetary perturbations. Table 2 Latitude/t */
98 enum ELP21_SIZE =	169;		/* Planetary perturbations. Table 2 Distance/t */
99 enum ELP22_SIZE =	3;		/* Tidal effects. Longitude */
100 enum ELP23_SIZE =	2;		/* Tidal effects. Latitude */
101 enum ELP24_SIZE =	2;		/* Tidal effects. Distance */
102 enum ELP25_SIZE =	6;		/* Tidal effects. Longitude/t */
103 enum ELP26_SIZE =	4;		/* Tidal effects. Latitude/t */
104 enum ELP27_SIZE =	5;		/* Tidal effects. Distance/t */
105 enum ELP28_SIZE =	20;		/* Moon figure perturbations. Longitude */
106 enum ELP29_SIZE =	12;		/* Moon figure perturbations. Latitude */
107 enum ELP30_SIZE =	14;		/* Moon figure perturbations. Distance */
108 enum ELP31_SIZE =	11;		/* Relativistic perturbations. Longitude */
109 enum ELP32_SIZE =	4;		/* Relativistic perturbations. Latitude */
110 enum ELP33_SIZE =	10;		/* Relativistic perturbations. Distance */
111 enum ELP34_SIZE =	28;		/* Planetary perturbations - solar eccentricity. Longitude/t2 */
112 enum ELP35_SIZE =	13;		/* Planetary perturbations - solar eccentricity. Latitude/t2 */
113 enum ELP36_SIZE =	19;		/* Planetary perturbations - solar eccentricity. Distance/t2 */
114 
115 
116 /* Chapront theory lunar constants */
117 enum		RAD =		(648000.0 / PI);
118 enum		DEG =		(PI / 180.0);
119 enum		PIS2 =	(PI / 2.0);
120 enum		ATH =		384747.9806743165;
121 enum		A0 =		384747.9806448954;
122 enum		AM =		0.074801329518;
123 enum		ALPHA =	0.002571881335;
124 enum		DTASM =	(2.0 * ALPHA / (3.0 * AM));
125 enum		W12 =		(1732559343.73604 / RAD);
126 enum		PRECES =	(5029.0966 / RAD);
127 enum		C1 =		60.0;
128 enum		C2 =		3600.0;
129 
130 /* Corrections of the constants for DE200/LE200 */
131 enum		DELNU =	((0.55604 / RAD) / W12);
132 enum		DELE =	(0.01789 / RAD);
133 enum		DELG =	(-0.08066 / RAD);
134 enum		DELNP =	((-0.06424 / RAD) / W12);
135 enum		DELEP =	(-0.12879 / RAD);
136 
137 /*     Precession matrix */
138 enum		P1 =		0.10180391e-4;
139 enum		P2 =		0.47020439e-6;
140 enum		P3 =		-0.5417367e-9;
141 enum		P4 =		-0.2507948e-11;
142 enum		P5 =		0.463486e-14;
143 enum		Q1 =		-0.113469002e-3;
144 enum		Q2 =		0.12372674e-6;
145 enum		Q3 =		0.1265417e-8;
146 enum		Q4 =		-0.1371808e-11;
147 enum		Q5 =		-0.320334e-14;
148 
149 /* initialise lunar constants */
150 void init_lunar_constants ();
151 
152 /* constants with corrections for DE200 / LE200 */
153 static const double[5] W1 =
154 [
155 	((218.0 + (18.0 / 60.0) + (59.95571 / 3600.0))) * DEG,
156 	1732559343.73604 / RAD,
157 	-5.8883 / RAD,
158 	0.006604 / RAD,
159 	-0.00003169 / RAD
160 ];
161 
162 // #if 0
163 // /* These arrays not currently used */
164 // static const double W2[5] =
165 // {
166 // 	((83.0 + (21.0 / 60.0) + (11.67475 / 3600.0))) * DEG,
167 // 	14643420.2632 / RAD,
168 // 	-38.2776 /  RAD,
169 // 	-0.045047 / RAD,
170 // 	0.00021301 / RAD
171 // };
172 //
173 // static const double W3[5] =
174 // {
175 // 	(125.0 + (2.0 / 60.0) + (40.39816 / 3600.0)) * DEG,
176 // 	-6967919.3622 / RAD,
177 // 	6.3622 / RAD,
178 // 	0.007625 / RAD,
179 // 	-0.00003586 / RAD
180 // };
181 //
182 // static const double earth[5] =
183 // {
184 // 	(100.0 + (27.0 / 60.0) + (59.22059 / 3600.0)) * DEG,
185 // 	129597742.2758 / RAD,
186 // 	-0.0202 / RAD,
187 // 	0.000009 / RAD,
188 // 	0.00000015 / RAD
189 // };
190 //
191 // static const double peri[5] =
192 // {
193 // 	(102.0 + (56.0 / 60.0) + (14.42753 / 3600.0)) * DEG,
194 // 	1161.2283 / RAD,
195 // 	0.5327 / RAD,
196 // 	-0.000138 / RAD,
197 // 	0.0
198 // };
199 // #endif
200 
201 /* Delaunay's arguments.*/
202 static const double[5][4] del = [
203 	[ 5.198466741027443, 7771.377146811758394, -0.000028449351621, 0.000000031973462, -0.000000000154365 ],
204 	[ -0.043125180208125, 628.301955168488007, -0.000002680534843, 0.000000000712676, 0.000000000000727 ],
205 	[ 2.355555898265799, 8328.691426955554562, 0.000157027757616, 0.000000250411114, -0.000000001186339 ],
206 	[ 1.627905233371468, 8433.466158130539043, -0.000059392100004, -0.000000004949948, 0.000000000020217 ]
207 ];
208 
209 
210 static const double[2] zeta =
211 [
212 	(218.0 + (18.0 / 60.0) + (59.95571 / 3600.0)) * DEG,
213 	((1732559343.73604 / RAD) + PRECES)
214 ];
215 
216 
217 /* Planetary arguments */
218 static const double[2][8] p =
219 [
220 	[(252.0 + 15.0 / C1 + 3.25986 / C2 ) * DEG, 538101628.68898 / RAD ],
221 	[(181.0 + 58.0 / C1 + 47.28305 / C2) * DEG, 210664136.43355 / RAD ],
222 	[(100.0 + (27.0 / 60.0) + (59.22059 / 3600.0)) * DEG, 129597742.2758 / RAD],
223 	[(355.0 + 25.0 / C1 + 59.78866 / C2) * DEG, 68905077.59284 / RAD ],
224 	[(34.0 + 21.0 / C1 + 5.34212 / C2) * DEG, 10925660.42861 / RAD ],
225 	[(50.0 + 4.0 / C1 + 38.89694 / C2) * DEG, 4399609.65932 / RAD ],
226 	[(314.0 + 3.0 / C1 + 18.01841 / C2) * DEG, 1542481.19393 / RAD ],
227 	[(304.0 + 20.0 / C1 + 55.19575 / C2) * DEG, 786550.32074 / RAD ]
228 ];
229 
230 extern (C) {
231 
232 /* sum lunar elp1 series */
233 static @nogc double sum_series_elp1 (double[] t) nothrow
234 {
235 	double result = 0;
236 	double x,y;
237 	double tgv;
238 	int i,j,k;
239 
240 	for (j = 0; j < ELP1_SIZE; j++) {
241 		/* derivatives of A */
242 		tgv = elp1[j].B[0] + DTASM * elp1[j].B[4];
243 		x = elp1[j].A + tgv * (DELNP - AM * DELNU) +
244 			elp1[j].B[1] * DELG + elp1[j].B[2] *
245 			DELE + elp1[j].B[3] * DELEP;
246 
247 		y = 0;
248 		for (k = 0; k < 5; k++) {
249 			for (i = 0; i < 4; i++)
250 				y += elp1[j].ilu[i] * del[i][k] * t[k];
251 		}
252 
253 		/* y in correct quad */
254 		y = ln_range_radians2(y);
255 		result += x * sin(y);
256 	}
257 	return result;
258 }
259 
260 /* sum lunar elp2 series */
261 static @nogc double sum_series_elp2 (double[] t) nothrow
262 {
263 	double result = 0;
264 	double x,y;
265 	double tgv;
266 	int i,j,k;
267 
268 	for (j = 0; j < ELP2_SIZE; j++) {
269 		/* derivatives of A */
270 		tgv = elp2[j].B[0] + DTASM * elp2[j].B[4];
271 		x = elp2[j].A + tgv * (DELNP - AM * DELNU) +
272 			elp2[j].B[1] * DELG + elp2[j].B[2] *
273 			DELE + elp2[j].B[3] * DELEP;
274 
275 		y = 0;
276 		for (k = 0; k < 5; k++) {
277 			for (i = 0; i < 4; i++)
278 				y += elp2[j].ilu[i] * del[i][k] * t[k];
279 		}
280 		/* y in correct quad */
281 		y = ln_range_radians2(y);
282 		result += x * sin(y);
283 	}
284 	return result;
285 }
286 
287 /* sum lunar elp3 series */
288 static @nogc double sum_series_elp3 (double[] t) nothrow
289 {
290 	double result = 0;
291 	double x,y;
292 	double tgv;
293 	int i,j,k;
294 
295 	for (j = 0; j < ELP3_SIZE; j++) {
296 		/* derivatives of A */
297 		tgv = elp3[j].B[0] + DTASM * elp3[j].B[4];
298 		x = elp3[j].A + tgv * (DELNP - AM * DELNU) +
299 			elp3[j].B[1] * DELG + elp3[j].B[2] *
300 			DELE + elp3[j].B[3] * DELEP;
301 
302 		y = 0;
303 		for (k = 0; k < 5; k++) {
304 			for (i = 0; i < 4; i++)
305 				y += elp3[j].ilu[i] * del[i][k] * t[k];
306 		}
307 		y += (PI_2);
308 		/* y in correct quad */
309 		y = ln_range_radians2(y);
310 		result += x * sin(y);
311 	}
312 	return result;
313 }
314 
315 
316 /* sum lunar elp4 series */
317 static @nogc double sum_series_elp4(double[] t) nothrow
318 {
319 	double result = 0;
320 	int i,j,k;
321 	double y;
322 
323 	for (j = 0; j < ELP4_SIZE; j++) {
324 		y = elp4[j].O * DEG;
325 		for (k = 0; k < 2; k++)  {
326 			y += elp4[j].iz * zeta[k] * t[k];
327 			for (i = 0; i < 4; i++)
328 				y += elp4[j].ilu[i] * del[i][k] * t[k];
329 		}
330 		/* put y in correct quad */
331 		y = ln_range_radians2(y);
332 		result += elp4[j].A * sin(y);
333 	}
334 	return result;
335 }
336 
337 /* sum lunar elp5 series */
338 static @nogc double sum_series_elp5(double[] t) nothrow
339 {
340 	double result = 0;
341 	int i,j,k;
342 	double y;
343 
344 	for (j = 0; j < ELP5_SIZE; j++) {
345 		y = elp5[j].O * DEG;
346 		for (k = 0; k < 2; k++) {
347 			y += elp5[j].iz * zeta[k] * t[k];
348 			for (i = 0; i < 4; i++)
349 				y += elp5[j].ilu[i] * del[i][k] * t[k];
350 		}
351 		/* put y in correct quad */
352 		y = ln_range_radians2(y);
353 		result += elp5[j].A * sin(y);
354 	}
355 	return result;
356 }
357 
358 
359 /* sum lunar elp6 series */
360 static @nogc double sum_series_elp6(double[] t) nothrow
361 {
362 	double result = 0;
363 	int i,j,k;
364 	double y;
365 
366 	for (j = 0; j < ELP6_SIZE; j++) {
367 		y = elp6[j].O * DEG;
368 		for (k = 0; k < 2; k++) {
369 			y += elp6[j].iz * zeta[k] * t[k];
370 			for (i = 0; i < 4; i++)
371 				y += elp6[j].ilu[i] * del[i][k] * t[k];
372 		}
373 		/* put y in correct quad */
374 		y = ln_range_radians2(y);
375 		result += elp6[j].A * sin(y);
376 	}
377 	return result;
378 }
379 
380 /* sum lunar elp7 series */
381 static @nogc double sum_series_elp7(double[] t) nothrow
382 {
383 	double result = 0;
384 	int i,j,k;
385 	double y, A;
386 
387 	for (j = 0; j < ELP7_SIZE; j++) {
388 		A = elp7[j].A * t[1];
389 		y = elp7[j].O * DEG;
390 		for (k = 0; k < 2; k++) {
391 			y += elp7[j].iz * zeta[k] * t[k];
392 			for (i = 0; i < 4; i++)
393 				y += elp7[j].ilu[i] * del[i][k] * t[k];
394 		}
395 		/* put y in correct quad */
396 		y = ln_range_radians2(y);
397 		result += A * sin(y);
398 	}
399 	return result;
400 }
401 
402 /* sum lunar elp8 series */
403 static @nogc double sum_series_elp8(double[] t) nothrow
404 {
405 	double result = 0;
406 	int i,j,k;
407 	double y, A;
408 
409 	for (j = 0; j < ELP8_SIZE; j++) {
410 		y = elp8[j].O * DEG;
411 		A = elp8[j].A * t[1];
412 		for (k = 0; k < 2; k++) {
413 			y += elp8[j].iz * zeta[k] * t[k];
414 			for (i = 0; i < 4; i++)
415 				y += elp8[j].ilu[i] * del[i][k] * t[k];
416 		}
417 		/* put y in correct quad */
418 		y = ln_range_radians2(y);
419 		result += A * sin(y);
420 	}
421 	return result;
422 }
423 
424 /* sum lunar elp9 series */
425 static @nogc double sum_series_elp9(double[] t) nothrow
426 {
427 	double result = 0;
428 	int i,j,k;
429 	double y, A;
430 
431 	for (j = 0; j < ELP9_SIZE; j++) {
432 		A = elp9[j].A * t[1];
433 		y = elp9[j].O * DEG;
434 		for (k = 0; k < 2; k++) {
435 			y += elp9[j].iz * zeta[k] * t[k];
436 			for (i = 0; i < 4; i++)
437 				y += elp9[j].ilu[i] * del[i][k] * t[k];
438 		}
439 		/* put y in correct quad */
440 		y = ln_range_radians2(y);
441 		result += A * sin(y);
442 	}
443 	return result;
444 }
445 
446 /* sum lunar elp10 series */
447 static @nogc double sum_series_elp10(double[] t) nothrow
448 {
449 	double result = 0;
450 	int i,j,k;
451 	double y;
452 
453 	for (j = 0; j < ELP10_SIZE; j++) {
454 		y = elp10[j].theta * DEG;
455 
456 		for (k = 0; k < 2; k++) {
457 			y += (elp10[j].ipla[8] * del[0][k]
458 			+ elp10[j].ipla[9] * del[2][k]
459 			+ elp10[j].ipla[10] * del [3][k]) * t[k];
460 			for (i = 0; i < 8; i++)
461 				y += elp10[j].ipla[i] * p[i][k] * t[k];
462 		}
463 
464 		/* put y in correct quad */
465 		y = ln_range_radians2(y);
466 		result += elp10[j].O * sin(y);
467 	}
468 	return result;
469 }
470 
471 /* sum lunar elp11 series */
472 static @nogc double sum_series_elp11(double[] t) nothrow
473 {
474 	double result = 0;
475 	int i,j,k;
476 	double y;
477 
478 	for (j = 0; j < ELP11_SIZE; j++) {
479 		y = elp11[j].theta * DEG;
480 		for (k = 0; k < 2; k++)  {
481 			y += (elp11[j].ipla[8] * del[0][k]
482 			+ elp11[j].ipla[9] * del[2][k]
483 			+ elp11[j].ipla[10] * del [3][k]) * t[k];
484 			for (i = 0; i < 8; i++)
485 				y += elp11[j].ipla[i] * p[i][k] * t[k];
486 		}
487 		/* put y in correct quad */
488 		y = ln_range_radians2(y);
489 		result += elp11[j].O * sin(y);
490 	}
491 	return result;
492 }
493 
494 /* sum lunar elp12 series */
495 static @nogc double sum_series_elp12(double[] t) nothrow
496 {
497 	double result = 0;
498 	int i,j,k;
499 	double y;
500 
501 	for (j = 0; j < ELP12_SIZE; j++) {
502 		y = elp12[j].theta * DEG;
503 		for (k = 0; k < 2; k++) {
504 			y += (elp12[j].ipla[8] * del[0][k]
505 			+ elp12[j].ipla[9] * del[2][k]
506 			+ elp12[j].ipla[10] * del [3][k]) * t[k];
507 			for (i = 0; i < 8; i++)
508 				y += elp12[j].ipla[i] * p[i][k] * t[k];
509 		}
510 		/* put y in correct quad */
511 		y = ln_range_radians2(y);
512 		result += elp12[j].O * sin(y);
513 	}
514 	return result;
515 }
516 
517 /* sum lunar elp13 series */
518 static @nogc double sum_series_elp13(double[] t) nothrow
519 {
520 	double result = 0;
521 	int i,j,k;
522 	double y,x;
523 
524 	for (j = 0; j < ELP13_SIZE; j++) {
525 		y = elp13[j].theta * DEG;
526 		for (k = 0; k < 2; k++) {
527 			y += (elp13[j].ipla[8] * del[0][k]
528 			+ elp13[j].ipla[9] * del[2][k]
529 			+ elp13[j].ipla[10] * del [3][k]) * t[k];
530 			for (i = 0; i < 8; i++)
531 				y += elp13[j].ipla[i] * p[i][k] * t[k];
532 		}
533 		/* put y in correct quad */
534 		y = ln_range_radians2(y);
535 		x = elp13[j].O * t[1];
536 		result += x * sin(y);
537 	}
538 	return result;
539 }
540 
541 /* sum lunar elp14 series */
542 static @nogc double sum_series_elp14(double[] t) nothrow
543 {
544 	double result = 0;
545 	int i,j,k;
546 	double y,x;
547 
548 	for (j = 0; j < ELP14_SIZE; j++) {
549 		y = elp14[j].theta * DEG;
550 		for (k = 0; k < 2; k++)  {
551 			y += (elp14[j].ipla[8] * del[0][k]
552 			+ elp14[j].ipla[9] * del[2][k]
553 			+ elp14[j].ipla[10] * del [3][k]) * t[k];
554 			for (i = 0; i < 8; i++)
555 				y += elp14[j].ipla[i] * p[i][k] * t[k];
556 		}
557 		/* put y in correct quad */
558 		y = ln_range_radians2(y);
559 		x = elp14[j].O * t[1];
560 		result += x * sin(y);
561 	}
562 	return result;
563 }
564 
565 
566 /* sum lunar elp15 series */
567 static @nogc double sum_series_elp15(double[] t) nothrow
568 {
569 	double result = 0;
570 	int i,j,k;
571 	double y,x;
572 
573 	for (j = 0; j < ELP15_SIZE; j++) {
574 		y = elp15[j].theta * DEG;
575 		for (k = 0; k < 2; k++) {
576 			y += (elp15[j].ipla[8] * del[0][k]
577 			+ elp15[j].ipla[9] * del[2][k]
578 			+ elp15[j].ipla[10] * del [3][k]) * t[k];
579 			for (i = 0; i < 8; i++)
580 				y += elp15[j].ipla[i] * p[i][k] * t[k];
581 		}
582 		/* put y in correct quad */
583 		y = ln_range_radians2(y);
584 		x = elp15[j].O * t[1];
585 		result += x * sin(y);
586 	}
587 	return result;
588 }
589 
590 /* sum lunar elp16 series */
591 static @nogc double sum_series_elp16(double[] t) nothrow
592 {
593 	double result = 0;
594 	int i,j,k;
595 	double y;
596 
597 	for (j = 0; j < ELP16_SIZE; j++) {
598 		y = elp16[j].theta * DEG;
599 		for (k = 0; k < 2; k++) {
600 			for (i = 0; i < 4; i++)
601 				y += elp16[j].ipla[i + 7] * del[i][k] * t[k];
602 			for (i = 0; i < 7; i++)
603 				y += elp16[j].ipla[i] * p[i][k] * t[k];
604 		}
605 		/* put y in correct quad */
606 		y = ln_range_radians2(y);
607 		result += elp16[j].O * sin(y);
608 	}
609 	return result;
610 }
611 
612 static @nogc double sum_series_elp17(double[] t) nothrow
613 {
614 	double result = 0;
615 	int i,j,k;
616 	double y;
617 
618 	for (j = 0; j < ELP17_SIZE; j++) {
619 		y = elp17[j].theta * DEG;
620 		for (k = 0; k < 2; k++) {
621 			for (i = 0; i < 4; i++)
622 				y += elp17[j].ipla[i + 7] * del[i][k] * t[k];
623 			for (i = 0; i < 7; i++)
624 				y += elp17[j].ipla[i] * p[i][k] * t[k];
625 		}
626 		/* put y in correct quad */
627 		y = ln_range_radians2(y);
628 		result += elp17[j].O * sin(y);
629 	}
630 	return result;
631 }
632 
633 static @nogc double sum_series_elp18(double[] t) nothrow
634 {
635 	double result = 0;
636 	int i,j,k;
637 	double y;
638 
639 	for (j = 0; j < ELP18_SIZE; j++) {
640 		y = elp18[j].theta * DEG;
641 		for (k = 0; k < 2; k++) {
642 			for (i = 0; i < 4; i++)
643 				y += elp18[j].ipla[i + 7] * del[i][k] * t[k];
644 			for (i = 0; i < 7; i++)
645 				y += elp18[j].ipla[i] * p[i][k] * t[k];
646 		}
647 		/* put y in correct quad */
648 		y = ln_range_radians2(y);
649 		result += elp18[j].O * sin(y);
650 	}
651 	return result;
652 }
653 
654 static @nogc double sum_series_elp19(double[] t) nothrow
655 {
656 	double result = 0;
657 	int i,j,k;
658 	double y,x;
659 
660 	for (j = 0; j < ELP19_SIZE; j++) {
661 		y = elp19[j].theta * DEG;
662 		for (k = 0; k < 2; k++) {
663 			for (i = 0; i < 4; i++)
664 				y += elp19[j].ipla[i + 7] * del[i][k] * t[k];
665 			for (i = 0; i < 7; i++)
666 				y += elp19[j].ipla[i] * p[i][k] * t[k];
667 		}
668 		/* put y in correct quad */
669 		y = ln_range_radians2(y);
670 		x = elp19[j].O * t[1];
671 		result += x * sin(y);
672 	}
673 	return result;
674 }
675 
676 static @nogc double sum_series_elp20(double[] t) nothrow
677 {
678 	double result = 0;
679 	int i,j,k;
680 	double y,x;
681 
682 	for (j = 0; j < ELP20_SIZE; j++) {
683 		y = elp20[j].theta * DEG;
684 		for (k = 0; k < 2; k++) {
685 			for (i = 0; i < 4; i++)
686 				y += elp20[j].ipla[i + 7] * del[i][k] * t[k];
687 			for (i = 0; i < 7; i++)
688 				y += elp20[j].ipla[i] * p[i][k] * t[k];
689 		}
690 		/* put y in correct quad */
691 		y = ln_range_radians2(y);
692 		x = elp20[j].O * t[1];
693 		result += x * sin(y);
694 	}
695 	return result;
696 }
697 
698 static @nogc double sum_series_elp21(double[] t) nothrow
699 {
700 	double result = 0;
701 	int i,j,k;
702 	double y,x;
703 
704 	for (j = 0; j < ELP21_SIZE; j++) {
705 		y = elp21[j].theta * DEG;
706 		for (k = 0; k < 2; k++) {
707 			for (i = 0; i < 4; i++)
708 				y += elp21[j].ipla[i + 7] * del[i][k] * t[k];
709 			for (i = 0; i < 7; i++)
710 				y += elp21[j].ipla[i] * p[i][k] * t[k];
711 		}
712 		/* put y in correct quad */
713 		y = ln_range_radians2(y);
714 		x = elp21[j].O * t[1];
715 		result += x * sin(y);
716 	}
717 	return result;
718 }
719 
720 /* sum lunar elp22 series */
721 static @nogc double sum_series_elp22(double[] t) nothrow
722 {
723 	double result = 0;
724 	int i,j,k;
725 	double y;
726 
727 	for (j = 0; j < ELP22_SIZE; j++) {
728 		y = elp22[j].O * DEG;
729 		for (k = 0; k < 2; k++) {
730 			y += elp22[j].iz * zeta[k] * t[k];
731 			for (i = 0; i < 4; i++)
732 				y += elp22[j].ilu[i] * del[i][k] * t[k];
733 		}
734 		/* put y in correct quad */
735 		y = ln_range_radians2(y);
736 		result += elp22[j].A * sin(y);
737 	}
738 	return result;
739 }
740 
741 /* sum lunar elp23 series */
742 static @nogc double sum_series_elp23(double[] t) nothrow
743 {
744 	double result = 0;
745 	int i,j,k;
746 	double y;
747 
748 	for (j = 0; j < ELP23_SIZE; j++) {
749 		y = elp23[j].O * DEG;
750 		for (k = 0; k < 2; k++) {
751 			y += elp23[j].iz * zeta[k] * t[k];
752 			for (i = 0; i < 4; i++)
753 				y += elp23[j].ilu[i] * del[i][k] * t[k];
754 		}
755 		/* put y in correct quad */
756 		y = ln_range_radians2(y);
757 		result += elp23[j].A * sin(y);
758 	}
759 	return result;
760 }
761 
762 /* sum lunar elp24 series */
763 static @nogc double sum_series_elp24(double[] t) nothrow
764 {
765 	double result = 0;
766 	int i,j,k;
767 	double y;
768 
769 	for (j = 0; j < ELP24_SIZE; j++) {
770 		y = elp24[j].O * DEG;
771 		for (k = 0; k < 2; k++) {
772 			y += elp24[j].iz * zeta[k] * t[k];
773 			for (i = 0; i < 4; i++)
774 				y += elp24[j].ilu[i] * del[i][k] * t[k];
775 		}
776 		/* put y in correct quad */
777 		y = ln_range_radians2(y);
778 		result += elp24[j].A * sin(y);
779 	}
780 	return result;
781 }
782 
783 /* sum lunar elp25 series */
784 static @nogc double sum_series_elp25(double[] t) nothrow
785 {
786 	double result = 0;
787 	int i,j,k;
788 	double y, A;
789 
790 	for (j = 0; j < ELP25_SIZE; j++) {
791 		A = elp25[j].A * t[1];
792 		y = elp25[j].O * DEG;
793 		for (k = 0; k < 2; k++) {
794 			y += elp25[j].iz * zeta[k] * t[k];
795 			for (i = 0; i < 4; i++)
796 				y += elp25[j].ilu[i] * del[i][k] * t[k];
797 		}
798 		/* put y in correct quad */
799 		y = ln_range_radians2(y);
800 		result += A * sin(y);
801 	}
802 	return result;
803 }
804 
805 /* sum lunar elp26 series */
806 static @nogc double sum_series_elp26(double[] t) nothrow
807 {
808 	double result = 0;
809 	int i,j,k;
810 	double y, A;
811 
812 	for (j = 0; j < ELP26_SIZE; j++) {
813 		A = elp26[j].A * t[1];
814 		y = elp26[j].O * DEG;
815 		for (k = 0; k < 2; k++) {
816 			y += elp26[j].iz * zeta[k] * t[k];
817 			for (i = 0; i < 4; i++)
818 				y += elp26[j].ilu[i] * del[i][k] * t[k];
819 		}
820 		/* put y in correct quad */
821 		y = ln_range_radians2(y);
822 		result += A * sin(y);
823 	}
824 	return result;
825 }
826 
827 /* sum lunar elp27 series */
828 static @nogc double sum_series_elp27(double[] t) nothrow
829 {
830 	double result = 0;
831 	int i,j,k;
832 	double y, A;
833 
834 	for (j = 0; j < ELP27_SIZE; j++) {
835 		A = elp27[j].A * t[1];
836 		y = elp27[j].O * DEG;
837 		for (k = 0; k < 2; k++) {
838 			y += elp27[j].iz * zeta[k] * t[k];
839 			for (i = 0; i < 4; i++)
840 				y += elp27[j].ilu[i] * del[i][k] * t[k];
841 		}
842 		/* put y in correct quad */
843 		y = ln_range_radians2(y);
844 		result += A * sin(y);
845 	}
846 	return result;
847 }
848 
849 /* sum lunar elp28 series */
850 static @nogc double sum_series_elp28(double[] t) nothrow
851 {
852 	double result = 0;
853 	int i,j,k;
854 	double y;
855 
856 	for (j = 0; j < ELP28_SIZE; j++) {
857 		y = elp28[j].O * DEG;
858 		for (k = 0; k < 2; k++) {
859 			y += elp28[j].iz * zeta[k] * t[k];
860 			for (i = 0; i < 4; i++)
861 				y += elp28[j].ilu[i] * del[i][k] * t[k];
862 		}
863 		/* put y in correct quad */
864 		y = ln_range_radians2(y);
865 		result += elp28[j].A * sin(y);
866 	}
867 	return result;
868 }
869 
870 /* sum lunar elp29 series */
871 static @nogc double sum_series_elp29(double[] t) nothrow
872 {
873 	double result = 0;
874 	int i,j,k;
875 	double y;
876 
877 	for (j = 0; j < ELP29_SIZE; j++) {
878 		y = elp29[j].O * DEG;
879 		for (k = 0; k < 2; k++) {
880 			y += elp29[j].iz * zeta[k] * t[k];
881 			for (i = 0; i < 4; i++)
882 				y += elp29[j].ilu[i] * del[i][k] * t[k];
883 		}
884 		/* put y in correct quad */
885 		y = ln_range_radians2(y);
886 		result += elp29[j].A * sin(y);
887 	}
888 	return result;
889 }
890 
891 
892 /* sum lunar elp30 series */
893 static @nogc double sum_series_elp30(double[] t) nothrow
894 {
895 	double result = 0;
896 	int i,j,k;
897 	double y;
898 
899 	for (j = 0; j < ELP30_SIZE; j++) {
900 		y = elp30[j].O * DEG;
901 		for (k = 0; k < 2; k++) {
902 			y += elp30[j].iz * zeta[k] * t[k];
903 			for (i = 0; i < 4; i++)
904 				y += elp30[j].ilu[i] * del[i][k] * t[k];
905 		}
906 		/* put y in correct quad */
907 		y = ln_range_radians2(y);
908 		result += elp30[j].A * sin(y);
909 	}
910 	return result;
911 }
912 
913 
914 /* sum lunar elp31 series */
915 static @nogc double sum_series_elp31(double[] t) nothrow
916 {
917 	double result = 0;
918 	int i,j,k;
919 	double y;
920 
921 	for (j = 0; j < ELP31_SIZE; j++) {
922 		y = elp31[j].O * DEG;
923 		for (k = 0; k < 2; k++) {
924 			y += elp31[j].iz * zeta[k] * t[k];
925 			for (i = 0; i < 4; i++)
926 				y += elp31[j].ilu[i] * del[i][k] * t[k];
927 		}
928 		/* put y in correct quad */
929 		y = ln_range_radians2(y);
930 		result += elp31[j].A * sin(y);
931 	}
932 	return result;
933 }
934 
935 /* sum lunar elp32 series */
936 static @nogc double sum_series_elp32(double[] t) nothrow
937 {
938 	double result = 0;
939 	int i,j,k;
940 	double y;
941 
942 	for (j = 0; j < ELP32_SIZE; j++) {
943 		y = elp32[j].O * DEG;
944 		for (k = 0; k < 2; k++) {
945 			y += elp32[j].iz * zeta[k] * t[k];
946 			for (i = 0; i < 4; i++)
947 				y += elp32[j].ilu[i] * del[i][k] * t[k];
948 		}
949 		/* put y in correct quad */
950 		y = ln_range_radians2(y);
951 		result += elp32[j].A * sin(y);
952 	}
953 	return result;
954 }
955 
956 /* sum lunar elp33 series */
957 static @nogc double sum_series_elp33(double[] t) nothrow
958 {
959 	double result = 0;
960 	int i,j,k;
961 	double y;
962 
963 	for (j = 0; j < ELP33_SIZE; j++) {
964 		y = elp33[j].O * DEG;
965 		for (k = 0; k < 2; k++) {
966 			y += elp33[j].iz * zeta[k] * t[k];
967 			for (i = 0; i < 4; i++)
968 				y += elp33[j].ilu[i] * del[i][k] * t[k];
969 		}
970 		/* put y in correct quad */
971 		y = ln_range_radians2(y);
972 		result += elp33[j].A * sin(y);
973 	}
974 	return result;
975 }
976 
977 /* sum lunar elp34 series */
978 static @nogc double sum_series_elp34(double[] t) nothrow
979 {
980 	double result = 0;
981 	int i,j,k;
982 	double y, A;
983 
984 	for (j = 0; j < ELP34_SIZE; j++) {
985 		A = elp34[j].A * t[2];
986 		y = elp34[j].O * DEG;
987 		for (k = 0; k < 2; k++) {
988 			y += elp34[j].iz * zeta[k] * t[k];
989 			for (i = 0; i < 4; i++)
990 				y += elp34[j].ilu[i] * del[i][k] * t[k];
991 		}
992 		/* put y in correct quad */
993 		y = ln_range_radians2(y);
994 		result += A * sin(y);
995 	}
996 	return result;
997 }
998 /* sum lunar elp35 series */
999 static @nogc double sum_series_elp35(double[] t) nothrow
1000 {
1001 	double result = 0;
1002 	int i,j,k;
1003 	double y, A;
1004 
1005 	for (j = 0; j < ELP35_SIZE; j++) {
1006 		A = elp35[j].A * t[2];
1007 		y = elp35[j].O * DEG;
1008 		for (k = 0; k < 2; k++) {
1009 			y += elp35[j].iz * zeta[k] * t[k];
1010 			for (i = 0; i < 4; i++)
1011 				y += elp35[j].ilu[i] * del[i][k] * t[k];
1012 		}
1013 		/* put y in correct quad */
1014 		y = ln_range_radians2(y);
1015 		result += A * sin(y);
1016 	}
1017 	return result;
1018 }
1019 
1020 /* sum lunar elp36 series */
1021 static @nogc double sum_series_elp36(double[] t) nothrow
1022 {
1023 	double result = 0;
1024 	int i,j,k;
1025 	double y, A;
1026 
1027 	for (j = 0; j < ELP36_SIZE; j++) {
1028 		A = elp36[j].A * t[2];
1029 		y = elp36[j].O * DEG;
1030 		for (k = 0; k < 2; k++) {
1031 			y += elp36[j].iz * zeta[k] * t[k];
1032 			for (i = 0; i < 4; i++)
1033 				y += elp36[j].ilu[i] * del[i][k] * t[k];
1034 		}
1035 		/* put y in correct quad */
1036 		y = ln_range_radians2(y);
1037 		result += A * sin(y);
1038 	}
1039 	return result;
1040 }
1041 
1042 /* internal function used for find_max/find zero lunar phase calculations */
1043 static @nogc double lunar_phase(double jd, ref double arg) nothrow
1044 {
1045 	ln_lnlat_posn moon;
1046 	ln_helio_posn sol;
1047 	double phase;
1048 
1049 	ln_get_lunar_ecl_coords(jd, moon, 0);
1050 	ln_get_solar_geom_coords(jd, sol);
1051 
1052 	phase = fmod((ln_rad_to_deg(moon.lng - sol.L))
1053 		+ 3.0 * PI - arg, 2.0 * PI) - PI;
1054 
1055 	return phase;
1056 }
1057 
1058 /* internal function used for find_max/find zero lunar phase calculations */
1059 static @nogc double lunar_distance(double jd, ref double arg) nothrow
1060 {
1061 	return ln_get_lunar_earth_dist(jd);
1062 }
1063 
1064 /* internal function used for find_max/find zero lunar phase calculations */
1065 static @nogc double lunar_neg_distance(double jd, ref double arg) nothrow
1066 {
1067 	return -ln_get_lunar_earth_dist(jd);
1068 }
1069 
1070 /* internal function used for find_max/find zero lunar phase calculations */
1071 static @nogc double _lunar_ecl_lat(double jd, ref double arg) nothrow
1072 {
1073 	ln_lnlat_posn pos;
1074 
1075 	ln_get_lunar_ecl_coords(jd, pos, 0);
1076 
1077 	return pos.lat;
1078 }
1079 
1080 /*! \fn void ln_get_lunar_geo_posn(double JD, struct ln_rect_posn *pos, double precision);
1081 * \param JD Julian day.
1082 * \param pos Pointer to a geocentric position structure to held result.
1083 * \param precision The truncation level of the series in radians for longitude
1084 * and latitude and in km for distance. (Valid range 0 - 0.01, 0 being highest accuracy)
1085 * \ingroup lunar
1086 *
1087 * Calculate the rectangular geocentric lunar coordinates to the inertial mean
1088 * ecliptic and equinox of J2000.
1089 * The geocentric coordinates returned are in units of km.
1090 *
1091 * This function is based upon the Lunar Solution ELP2000-82B by
1092 * Michelle Chapront-Touze and Jean Chapront of the Bureau des Longitudes,
1093 * Paris.
1094 */
1095 /* ELP 2000-82B theory */
1096 @nogc void ln_get_lunar_geo_posn(double JD, ref ln_rect_posn moon, double precision) nothrow
1097 {
1098 	double[5] t;
1099 	double[36] elp;
1100 	double a,b,c;
1101 	double x,y,z;
1102 	double pw,qw, pwqw, pw2, qw2, ra;
1103 
1104 	/* calc julian centuries */
1105 	t[0] = 1.0;
1106 	t[1] =(JD - 2451545.0) / 36525.0;
1107 	t[2] = t[1] * t[1];
1108 	t[3] = t[2] * t[1];
1109 	t[4] = t[3] * t[1];
1110 
1111 	/* sum elp series */
1112 	elp[0] = sum_series_elp1(t);
1113 	elp[1] = sum_series_elp2(t);
1114 	elp[2] = sum_series_elp3(t);
1115 	elp[3] = sum_series_elp4(t);
1116 	elp[4] = sum_series_elp5(t);
1117 	elp[5] = sum_series_elp6(t);
1118 	elp[6] = sum_series_elp7(t);
1119 	elp[7] = sum_series_elp8(t);
1120 	elp[8] = sum_series_elp9(t);
1121 	elp[9] = sum_series_elp10(t);
1122 	elp[10] = sum_series_elp11(t);
1123 	elp[11] = sum_series_elp12(t);
1124 	elp[12] = sum_series_elp13(t);
1125 	elp[13] = sum_series_elp14(t);
1126 	elp[14] = sum_series_elp15(t);
1127 	elp[15] = sum_series_elp16(t);
1128 	elp[16] = sum_series_elp17(t);
1129 	elp[17] = sum_series_elp18(t);
1130 	elp[18] = sum_series_elp19(t);
1131 	elp[19] = sum_series_elp20(t);
1132 	elp[20] = sum_series_elp21(t);
1133 	elp[21] = sum_series_elp22(t);
1134 	elp[22] = sum_series_elp23(t);
1135 	elp[23] = sum_series_elp24(t);
1136 	elp[24] = sum_series_elp25(t);
1137 	elp[25] = sum_series_elp26(t);
1138 	elp[26] = sum_series_elp27(t);
1139 	elp[27] = sum_series_elp28(t);
1140 	elp[28] = sum_series_elp29(t);
1141 	elp[29] = sum_series_elp30(t);
1142 	elp[30] = sum_series_elp31(t);
1143 	elp[31] = sum_series_elp32(t);
1144 	elp[32] = sum_series_elp33(t);
1145 	elp[33] = sum_series_elp34(t);
1146 	elp[34] = sum_series_elp35(t);
1147 	elp[35] = sum_series_elp36(t);
1148 
1149 	a = elp[0] + elp[3] + elp[6] + elp[9] + elp[12] +
1150 		elp[15] + elp[18] + elp[21] + elp[24] +
1151 		elp[27] + elp[30] + elp[33];
1152 	b = elp[1] + elp[4] + elp[7] + elp[10] + elp[13] +
1153 		elp[16] + elp[19] + elp[22] + elp[25] +
1154 		elp[28] + elp[31] + elp[34];
1155 	c = elp[2] + elp[5] + elp[8] + elp[11] + elp[14] +
1156 		elp[17] + elp[20] + elp[23] + elp[26] +
1157 		elp[29] + elp[32] + elp[35];
1158 
1159 	/* calculate geocentric coords */
1160 	a = a / RAD + W1[0] + W1[1] * t[1] + W1[2] * t[2] + W1[3] * t[3]
1161 	    + W1[4] * t[4];
1162 	b = b / RAD;
1163 	c = c * A0 / ATH;
1164 
1165 	x = c * cos(b);
1166 	y = x * sin(a);
1167 	x = x * cos(a);
1168 	z = c * sin(b);
1169 
1170 	/* Laskars series */
1171 	pw = (P1 + P2 * t[1] + P3 * t[2] + P4 * t[3] + P5 * t[4]) * t[1];
1172 	qw = (Q1 + Q2 * t[1] + Q3 * t[2] + Q4 * t[3] + Q5 * t[4]) * t[1];
1173 	ra = 2.0 * sqrt(1.0 - pw * pw - qw * qw);
1174 	pwqw = 2.0 * pw * qw;
1175 	pw2 = 1.0 - 2.0 * pw * pw;
1176 	qw2 = 1.0 - 2.0 * qw * qw;
1177 	pw = pw * ra;
1178 	qw = qw * ra;
1179 	a = pw2 * x + pwqw * y + pw * z;
1180 	b = pwqw * x + qw2 * y - qw * z;
1181 	c = -pw * x + qw * y + (pw2 + qw2 - 1.0) * z;
1182 
1183 	/* save result */
1184 	moon.X = a;
1185 	moon.Y = b;
1186 	moon.Z = c;
1187 }
1188 
1189 /*! \fn void ln_get_lunar_equ_coords_prec(double JD, struct ln_equ_posn *position, double precision);
1190 * \param JD Julian Day
1191 * \param position Pointer to a struct ln_lnlat_posn to store result.
1192 * \param precision The truncation level of the series in radians for longitude
1193 * and latitude and in km for distance. (Valid range 0 - 0.01, 0 being highest accuracy)
1194 * \ingroup lunar
1195 *
1196 * Calculate the lunar RA and DEC for Julian day JD.
1197 * Accuracy is better than 10 arcsecs in right ascension and 4 arcsecs in declination.
1198 */
1199 @nogc void ln_get_lunar_equ_coords_prec(double JD, ref ln_equ_posn position,
1200 	double precision) nothrow
1201 {
1202 	ln_lnlat_posn ecl;
1203 
1204 	ln_get_lunar_ecl_coords(JD, ecl, precision);
1205 	ln_get_equ_from_ecl(ecl, JD, position);
1206 }
1207 
1208 /*! \fn void ln_get_lunar_equ_coords(double JD, struct ln_equ_posn *position);
1209 * \param JD Julian Day
1210 * \param position Pointer to a struct ln_lnlat_posn to store result.
1211 * \ingroup lunar
1212 *
1213 * Calculate the lunar RA and DEC for Julian day JD at the highest precision.
1214 * Accuracy is better than 10 arcsecs in right ascension and 4 arcsecs in declination.
1215 */
1216 @nogc void ln_get_lunar_equ_coords(double JD, ref ln_equ_posn position) nothrow
1217 {
1218 	ln_get_lunar_equ_coords_prec(JD, position, 0);
1219 }
1220 
1221 /*! \fn void ln_get_lunar_ecl_coords(double JD, struct ln_lnlat_posn *position, double precision);
1222 * \param JD Julian Day
1223 * \param position Pointer to a struct ln_lnlat_posn to store result.
1224 * \param precision The truncation level of the series in radians for longitude
1225 * and latitude and in km for distance. (Valid range 0 - 0.01, 0 being highest accuracy)
1226 * \ingroup lunar
1227 *
1228 * Calculate the lunar longitude and latitude for Julian day JD.
1229 * Accuracy is better than 10 arcsecs in longitude and 4 arcsecs in latitude.
1230 *
1231 * Per Meeus, chapter 47, if you need higher precision than this, see
1232 * Chaperont's _Lunar Tables and Programs_.
1233 */
1234 @nogc void ln_get_lunar_ecl_coords(double JD, ref ln_lnlat_posn position,
1235 	double precision) nothrow
1236 {
1237 	ln_rect_posn moon;
1238 
1239 	/* get lunar geocentric position */
1240 	ln_get_lunar_geo_posn(JD, moon, precision);
1241 
1242 	/* convert to long and lat */
1243 	position.lng = atan2(moon.Y, moon.X);
1244 	position.lat = atan2(moon.Z,
1245 		(sqrt((moon.X * moon.X) + (moon.Y * moon.Y))));
1246 	position.lng = ln_range_degrees(ln_rad_to_deg(position.lng));
1247 	position.lat = ln_rad_to_deg(position.lat);
1248 }
1249 
1250 /*! \fn double ln_get_lunar_earth_dist(double JD);
1251 * \param JD Julian Day
1252 * \return The distance between the Earth and Moon in km.
1253 * \ingroup lunar
1254 *
1255 * Calculates the distance between the centre of the Earth and the
1256 * centre of the Moon in km.
1257 */
1258 @nogc double ln_get_lunar_earth_dist(double JD) nothrow
1259 {
1260 	ln_rect_posn moon;
1261 
1262 	ln_get_lunar_geo_posn(JD, moon, 0.00001);
1263 	return sqrt((moon.X * moon.X) + (moon.Y * moon.Y) + (moon.Z * moon.Z));
1264 }
1265 
1266 
1267 /*! \fn double ln_get_lunar_phase(double JD);
1268 * \param JD Julian Day
1269 * \return Phase angle. (Value between 0 and 180)
1270 * \ingroup lunar
1271 *
1272 * Calculates the angle Sun - Moon - Earth.
1273 */
1274 @nogc double ln_get_lunar_phase(double JD) nothrow
1275 {
1276 	double phase = 0;
1277 	ln_lnlat_posn moon, sunlp;
1278 	double lunar_elong;
1279 	double R, delta;
1280 
1281 	/* get lunar and solar long + lat */
1282 	ln_get_lunar_ecl_coords(JD, moon, 0.0001);
1283 	ln_get_solar_ecl_coords(JD, sunlp);
1284 
1285 	/* calc lunar geocentric elongation equ 48.2 */
1286 	lunar_elong = acos(cos(ln_deg_to_rad(moon.lat)) *
1287 		cos(ln_deg_to_rad(sunlp.lng - moon.lng)));
1288 
1289 	/* now calc phase Equ 48.2 */
1290 	R = ln_get_earth_solar_dist(JD);
1291 	delta = ln_get_lunar_earth_dist(JD);
1292 	R = R * AU; /* convert R to km */
1293 	phase = atan2((R * sin(lunar_elong)), (delta - R * cos(lunar_elong)));
1294 	return ln_rad_to_deg(phase);
1295 }
1296 
1297 /*! \fn double ln_get_lunar_disk(double JD);
1298 * \param JD Julian Day
1299 * \return Illuminated fraction. (Value between 0 and 1)
1300 * \brief Calculate the illuminated fraction of the moons disk
1301 * \ingroup lunar
1302 *
1303 * Calculates the illuminated fraction of the Moon's disk.
1304 */
1305 @nogc double ln_get_lunar_disk(double JD) nothrow
1306 {
1307 	double i;
1308 
1309 	/* Equ 48.1 */
1310 	i = ln_deg_to_rad(ln_get_lunar_phase(JD));
1311 	return (1.0 + cos(i)) / 2.0;
1312 }
1313 
1314 /*! \fn double ln_get_lunar_bright_limb(double JD);
1315 * \param JD Julian Day
1316 * \return The position angle in degrees.
1317 * \brief Calculate the position angle of the Moon's bright limb.
1318 * \ingroup lunar
1319 *
1320 * Calculates the position angle of the midpoint of the illuminated limb of the
1321 * moon, reckoned eastward from the north point of the disk.
1322 *
1323 * The angle is near 270 deg for first quarter and near 90 deg after a full moon.
1324 * The position angle of the cusps are +90 deg and -90 deg.
1325 */
1326 @nogc double ln_get_lunar_bright_limb(double JD) nothrow
1327 {
1328 	double angle;
1329 	double x,y;
1330 
1331 	ln_equ_posn moon, sunlp;
1332 
1333 	/* get lunar and solar long + lat */
1334 	ln_get_lunar_equ_coords(JD, moon);
1335 	ln_get_solar_equ_coords(JD, sunlp);
1336 
1337 	/* Equ 48.5 */
1338 	x = cos(ln_deg_to_rad(sunlp.dec)) * sin(ln_deg_to_rad(sunlp.ra - moon.ra));
1339 	y = sin((ln_deg_to_rad(sunlp.dec)) * cos(ln_deg_to_rad(moon.dec)))
1340 		- (cos(ln_deg_to_rad(sunlp.dec)) * sin(ln_deg_to_rad(moon.dec))
1341 		* cos(ln_deg_to_rad(sunlp.ra - moon.ra)));
1342 	angle = atan2(x,y);
1343 
1344 	angle = ln_range_radians(angle);
1345 	return ln_rad_to_deg(angle);
1346 }
1347 
1348 
1349 /*! \fn double ln_get_lunar_rst(double JD, struct ln_lnlat_posn *observer, struct ln_rst_time *rst);
1350 * \param JD Julian day
1351 * \param observer Observers position
1352 * \param rst Pointer to store Rise, Set and Transit time in JD
1353 * \return 0 for success, else 1 for circumpolar.
1354 * \todo Improve lunar standard altitude for rst
1355 *
1356 * Calculate the time the rise, set and transit (crosses the local meridian at upper culmination)
1357 * time of the Moon for the given Julian day.
1358 *
1359 * Note: this functions returns 1 if the Moon is circumpolar, that is it remains the whole
1360 * day either above or below the horizon.
1361 */
1362 @nogc int ln_get_lunar_rst(double JD, const ref ln_lnlat_posn observer,
1363 	ref ln_rst_time rst) nothrow
1364 {
1365     return ln_get_body_rst_horizon(JD, observer,
1366                     cast(get_equ_body_coords_t)&ln_get_lunar_equ_coords,
1367                     LN_LUNAR_STANDART_HORIZON, rst);
1368 }
1369 
1370 /*! \fn double ln_get_lunar_sdiam(double JD)
1371 * \param JD Julian day
1372 * \return Semidiameter in arc seconds
1373 * \todo Use Topocentric distance.
1374 *
1375 * Calculate the semidiameter of the Moon in arc seconds for the
1376 * given julian day.
1377 */
1378 @nogc double ln_get_lunar_sdiam(double JD) nothrow
1379 {
1380 	double So = 358473400;
1381 	double dist;
1382 
1383 	dist = ln_get_lunar_earth_dist(JD);
1384 	return So / dist;
1385 }
1386 
1387 /*! \fn double ln_get_lunar_long_asc_node(double JD);
1388 * \param JD Julian Day.
1389 * \return Longitude of ascending node in degrees.
1390 *
1391 * Calculate the mean longitude of the Moons ascending node
1392 * for the given Julian day.
1393 */
1394 @nogc double ln_get_lunar_long_asc_node(double JD) nothrow
1395 {
1396 	/* calc julian centuries */
1397 	double T =(JD - 2451545.0) / 36525.0;
1398 	double omega = 125.0445479;
1399 	double T2 = T * T;
1400 	double T3 = T2 * T;
1401 	double T4 = T3 * T;
1402 
1403 	/* equ 47.7 */
1404 	omega -= 1934.1362891 * T + 0.0020754 * T2 + T3 / 467441.0 -
1405 		T4 / 60616000.0;
1406 	return omega;
1407 }
1408 
1409 
1410 /*! \fn double ln_get_lunar_long_perigee(double JD);
1411 * \param JD Julian Day
1412 * \return Longitude of Moons mean perigee in degrees.
1413 *
1414 * Calculate the longitude of the Moon's mean perigee.
1415 */
1416 @nogc double ln_get_lunar_long_perigee(double JD) nothrow
1417 {
1418 	/* calc julian centuries */
1419 	double T =(JD - 2451545.0) / 36525.0;
1420 	double per = 83.3532465;
1421 	double T2 = T * T;
1422 	double T3 = T2 * T;
1423 	double T4 = T3 * T;
1424 
1425 	/* equ 47.7 */
1426 	per += 4069.0137287 * T - 0.0103200 * T2 -
1427 		T3 / 80053.0 + T4 / 18999000.0;
1428 	return per;
1429 }
1430 
1431 /*! \fn double ln_get_lunar_arg_latitude(double JD);
1432 * \param JD Julian Day
1433 * \return Moon's argument of latitude
1434 *
1435 * Calculate the Moon's argument of latitude (mean distance of the Moon from its ascending node)
1436 */
1437 @nogc double ln_get_lunar_arg_latitude(double JD) nothrow
1438 {
1439 	/* calc julian centuries */
1440 	double T =(JD - 2451545.0) / 36525.0;
1441 	double arg = 93.2720993;
1442 	double T2 = T * T;
1443 	double T3 = T2 * T;
1444 	double T4 = T3 * T;
1445 
1446 	/* equ 45.5 */
1447 	arg += 483202.0175273 * T - 0.0034029 * T2 -
1448 		T3 / 3526000.0 + T4 / 863310000.0;
1449 
1450 	return arg;
1451 }
1452 
1453 @nogc void ln_get_lunar_selenographic_coords(double JD, ref ln_lnlat_posn moon,
1454 	ref ln_lnlat_posn position) nothrow
1455 {
1456 	/* equ 51.1 */
1457 	static const double I = 0.02692030744861093755; // 1.54242 deg in radians
1458 	double Omega = ln_get_lunar_long_asc_node(JD);
1459 	double W = ln_deg_to_rad(moon.lng - Omega);
1460 	double F = ln_get_lunar_arg_latitude(JD);
1461 
1462 	double tan_Ay = sin(W) * cos(ln_deg_to_rad(moon.lat)) * cos(I) -
1463 		sin(ln_deg_to_rad(moon.lat)) * sin(I);
1464 	double tan_Ax = cos(W) * cos(ln_deg_to_rad(moon.lat));
1465 
1466 	position.lng =
1467 		ln_range_degrees(ln_rad_to_deg(atan2(tan_Ay, tan_Ax)) - F);
1468 	position.lng =
1469 		(position.lng > 180.0 ? position.lng - 360.0 : position.lng);
1470 	position.lat =
1471 		ln_rad_to_deg(asin(-sin(W) * cos(ln_deg_to_rad(moon.lat)) * sin(I) -
1472 		sin(ln_deg_to_rad(moon.lat))*cos(I)));
1473 }
1474 
1475 /*! \fn void ln_get_lunar_opt_libr_coords(double JD, struct ln_lnlat_posn *position)
1476 * \param JD Julian Day
1477 * \param position Pointer to a struct ln_lnlat_posn to store result.
1478 *
1479 * Calculate Lunar optical libration coordinates, also known as selenographic Earth coordinates.
1480 * This is a point on the surface of the Moon where the Earth is in the zenith.
1481 */
1482 @nogc void ln_get_lunar_opt_libr_coords(double JD, ref ln_lnlat_posn position) nothrow
1483 {
1484 	ln_lnlat_posn moon;
1485 	ln_get_lunar_ecl_coords(JD, moon, 0);
1486 	ln_get_lunar_selenographic_coords(JD, moon, position);
1487 }
1488 
1489 /*! \fn void ln_get_lunar_subsolar_coords(double JD, struct ln_lnlat_posn *position)
1490 * \param JD Julian Day
1491 * \param position Pointer to a struct ln_lnlat_posn to store result.
1492 *
1493 * Calculate coordinates of the subsolar point, aslo known as selenographic coordinates of the Sun.
1494 * This is a point on the surface of the Moon where the Sun is in the zenith.
1495 */
1496 @nogc void ln_get_lunar_subsolar_coords(double JD, ref ln_lnlat_posn position) nothrow
1497 {
1498 	ln_lnlat_posn moon;
1499 	ln_lnlat_posn sun;
1500 	double EM_dist = ln_get_lunar_earth_dist(JD);
1501 	double ES_dist = ln_get_earth_solar_dist(JD) * AU;
1502 	double dist_ratio = EM_dist / ES_dist;
1503 
1504 	ln_get_solar_ecl_coords(JD, sun);
1505 	ln_get_lunar_ecl_coords(JD, moon, 0);
1506 
1507 	moon.lng = sun.lng + 180.0 + 57.296 * dist_ratio *
1508 		cos(ln_deg_to_rad(moon.lat)) *
1509 		sin(ln_deg_to_rad(sun.lng - moon.lng));
1510 	moon.lat = dist_ratio * moon.lat;
1511 
1512 	ln_get_lunar_selenographic_coords(JD, moon, position);
1513 }
1514 
1515 /*
1516  * Notes on phase calculations.
1517  * In general, exact k cannot be easily computed (Meeus chapter 46 gives
1518  * approximation formula) and there is no guarantee that you can compute
1519  * exactly k for *next* (not accidentally previous) phase (or opposite -
1520  * previous, not accidentally next).
1521 
1522  * Therefore k is firstly approximated and decreased (or increased) by 2
1523  * and then while loop increase this k until nd (JD of mean phase) is fisrt
1524  * one below or above given JD. This loop runs several times (1-3) only.
1525  */
1526 
1527 /*! \fn double ln_lunar_next_phase(double jd, double phase)
1528 * \param jd Julian Day
1529 * \param phase 0 for new moon, 0.25 for first quarter, 0.5 for full moon, 0.75 for last quarter
1530 * \return Julian day when the moon will next be at the specified phase.
1531 *
1532 * Find next moon phase relative to given time expressed as Julian Day.
1533 *
1534 */
1535 @nogc double ln_lunar_next_phase(double jd, double phase) nothrow
1536 {
1537 	double ph, k, angle;
1538 
1539 	k = floor((jd - 2451550.09766) / 29.530588861) + phase - 2.0;
1540 
1541 	while ((ph = 2451550.09766 + 29.530588861 * k + 0.00015437 *
1542 		(k / 1236.85) * (k / 1236.85)) < jd)
1543 			k += 1.0;
1544 
1545 	angle = 2.0 * PI * phase;
1546 
1547 	while ((ph = ln_find_zero(cast(find_zero_t)&lunar_phase, ph, ph + 0.01, angle)) < jd)
1548 		ph += 29.530588861;
1549 
1550 	return ph;
1551 }
1552 
1553 /*! \fn double ln_lunar_previous_phase(double jd, double phase)
1554 * \param jd Julian Day
1555 * \param phase 0 for new moon, 0.25 for first quarter, 0.5 for full moon, 0.75 for last quarter
1556 * \return Julian day when the moon was last at the specified phase
1557 *
1558 * Find previous moon phase relative to given time expressed as Julian Day.
1559 *
1560 */
1561 @nogc double ln_lunar_previous_phase(double jd, double phase) nothrow
1562 {
1563 	double ph, k, angle;
1564 
1565 	k = floor((jd - 2451550.09766) / 29.530588861) + phase + 2.0;
1566 
1567 	while ((ph = 2451550.09766 + 29.530588861 * k + 0.00015437 *
1568 		(k / 1236.85) * (k / 1236.85)) > jd)
1569 			k -= 1.0;
1570 
1571 	angle = 2.0 * PI * phase;
1572 
1573 	while ((ph = ln_find_zero(&lunar_phase, ph, ph + 0.01, angle)) > jd)
1574 		ph -= 29.530588861;
1575 
1576 	return ph;
1577 }
1578 
1579 /*! \fn double ln_lunar_next_apsis(double jd, int mode)
1580 * \param jd Julian Day
1581 * \param apogee 0 for perigee, 1 for apogee
1582 *
1583 * Find next moon apogee or perigee relative to given time expressed as Julian Day.
1584 *
1585 */
1586 @nogc double ln_lunar_next_apsis(double jd, int apogee) nothrow
1587 {
1588 	double ap, k;
1589 
1590 	k = floor((jd - 2451534.6698) / 27.55454989) + (0.5 * apogee) - 2.0;
1591 
1592 	while ((ap = 2451534.6698 + 27.55454989 * k + 0.0006691 *
1593 		(k / 1325.55) * (k / 1325.55)) < jd)
1594 			k += 1.0;
1595 
1596     double ignored;
1597 	if (apogee) {
1598 		while ((ap = ln_find_max(&lunar_distance, ap - 3.0, ap + 3.0, ignored)) < jd)
1599 			ap += 27.55454989;
1600 	} else {
1601 		while ((ap = ln_find_max(&lunar_neg_distance, ap - 3.0, ap + 3.0, ignored)) < jd)
1602 			ap += 27.55454989;
1603 	}
1604 
1605 	return ap;
1606 }
1607 
1608 /*! \fn double ln_lunar_previous_apsis(double jd, int mode)
1609 * \param jd Julian Day
1610 * \param apogee 0 for perigee, 1 for apogee
1611 *
1612 * Find previous moon apogee or perigee relative to given time expressed as Julian Day.
1613 *
1614 */
1615 @nogc double ln_lunar_previous_apsis(double jd, int apogee) nothrow
1616 {
1617 	double ap, k;
1618 
1619 	k = floor((jd - 2451534.6698) / 27.55454989) + (0.5 * apogee) + 2.0;
1620 
1621 	while ((ap = 2451534.6698 + 27.55454989 * k + 0.0006691 *
1622 		(k / 1325.55) * (k / 1325.55)) > jd)
1623 		k -= 1.0;
1624 
1625     double ignored;
1626 	if (apogee) {
1627 		while ((ap = ln_find_max(&lunar_distance, ap - 3.0, ap + 3.0, ignored)) > jd)
1628 			ap -= 27.55454989;
1629 	} else {
1630 		while ((ap = ln_find_max(&lunar_neg_distance, ap - 3.0, ap + 3.0, ignored)) > jd)
1631 			ap -= 27.55454989;
1632 	}
1633 
1634 	return ap;
1635 }
1636 
1637 /*! \fn double ln_lunar_next_node(double jd, int mode)
1638 * \param jd Julian Day
1639 * \param mode 0 for ascending, 1 for descending
1640 * \return Julian day when the moon will next be at the specified node.
1641 *
1642 * Find next moon node relative to given time expressed as Julian Day.
1643 *
1644 */
1645 @nogc double ln_lunar_next_node(double jd, int mode) nothrow
1646 {
1647 	double nd, k;
1648 
1649 	k = floor((jd - 2451565.1619) / 27.212220817) + (0.5 * mode) - 2.0;
1650 
1651 	while ((nd = 2451565.1619 + 27.212220817 * k) < jd)
1652 		k += 1.0;
1653 
1654     double ignored;
1655 	while ((nd = ln_find_zero(&_lunar_ecl_lat, nd - 3.0,
1656 		nd + 3.0, ignored)) < jd)
1657 			nd += 27.212220817;
1658 
1659 	return nd;
1660 }
1661 
1662 /*! \fn double ln_lunar_previous_node(double jd, int mode)
1663 * \param jd Julian Day
1664 * \param mode 0 for ascending, 1 for descending
1665 * \return Julian day when the moon was last at the specified node.
1666 *
1667 * Find previous lunar node relative to given time expressed as Julian Day.
1668 *
1669 */
1670 @nogc double ln_lunar_previous_node(double jd, int mode) nothrow
1671 {
1672 	double nd, k;
1673 
1674 	k = floor((jd - 2451565.1619) / 27.212220817) + (0.5 * mode) + 2.0;
1675 
1676 	while ((nd = 2451565.1619 + 27.212220817 * k) > jd)
1677 		k -= 1.0;
1678 
1679     double ignored;
1680 	while ((nd = ln_find_zero(&_lunar_ecl_lat, nd - 3.0,
1681 		nd + 3.0, ignored)) > jd)
1682 			nd -= 27.212220817 ;
1683 
1684 	return nd;
1685 }
1686 
1687 /*! \example lunar.c
1688  *
1689  * Examples of how to use Lunar functions.
1690  */
1691 
1692 }