1*fd1efedcSConrad Meyer /*-
2*fd1efedcSConrad Meyer * SPDX-License-Identifier: BSD-3-Clause
3*fd1efedcSConrad Meyer *
4*fd1efedcSConrad Meyer * Copyright (c) 1989, 1993
5*fd1efedcSConrad Meyer * The Regents of the University of California. All rights reserved.
6*fd1efedcSConrad Meyer *
7*fd1efedcSConrad Meyer * This code is derived from software posted to USENET.
8*fd1efedcSConrad Meyer *
9*fd1efedcSConrad Meyer * Redistribution and use in source and binary forms, with or without
10*fd1efedcSConrad Meyer * modification, are permitted provided that the following conditions
11*fd1efedcSConrad Meyer * are met:
12*fd1efedcSConrad Meyer * 1. Redistributions of source code must retain the above copyright
13*fd1efedcSConrad Meyer * notice, this list of conditions and the following disclaimer.
14*fd1efedcSConrad Meyer * 2. Redistributions in binary form must reproduce the above copyright
15*fd1efedcSConrad Meyer * notice, this list of conditions and the following disclaimer in the
16*fd1efedcSConrad Meyer * documentation and/or other materials provided with the distribution.
17*fd1efedcSConrad Meyer * 3. Neither the name of the University nor the names of its contributors
18*fd1efedcSConrad Meyer * may be used to endorse or promote products derived from this software
19*fd1efedcSConrad Meyer * without specific prior written permission.
20*fd1efedcSConrad Meyer *
21*fd1efedcSConrad Meyer * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22*fd1efedcSConrad Meyer * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23*fd1efedcSConrad Meyer * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24*fd1efedcSConrad Meyer * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25*fd1efedcSConrad Meyer * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26*fd1efedcSConrad Meyer * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27*fd1efedcSConrad Meyer * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28*fd1efedcSConrad Meyer * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29*fd1efedcSConrad Meyer * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30*fd1efedcSConrad Meyer * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31*fd1efedcSConrad Meyer * SUCH DAMAGE.
32*fd1efedcSConrad Meyer */
33*fd1efedcSConrad Meyer
34*fd1efedcSConrad Meyer /*
35*fd1efedcSConrad Meyer * Phase of the Moon. Calculates the current phase of the moon.
36*fd1efedcSConrad Meyer * Based on routines from `Practical Astronomy with Your Calculator',
37*fd1efedcSConrad Meyer * by Duffett-Smith. Comments give the section from the book that
38*fd1efedcSConrad Meyer * particular piece of code was adapted from.
39*fd1efedcSConrad Meyer *
40*fd1efedcSConrad Meyer * -- Keith E. Brandt VIII 1984
41*fd1efedcSConrad Meyer *
42*fd1efedcSConrad Meyer */
43*fd1efedcSConrad Meyer
44*fd1efedcSConrad Meyer #include <stdio.h>
45*fd1efedcSConrad Meyer #include <stdlib.h>
46*fd1efedcSConrad Meyer #include <math.h>
47*fd1efedcSConrad Meyer #include <string.h>
48*fd1efedcSConrad Meyer #include <sysexits.h>
49*fd1efedcSConrad Meyer #include <time.h>
50*fd1efedcSConrad Meyer #include <unistd.h>
51*fd1efedcSConrad Meyer
52*fd1efedcSConrad Meyer #include "calendar.h"
53*fd1efedcSConrad Meyer
54*fd1efedcSConrad Meyer #ifndef PI
55*fd1efedcSConrad Meyer #define PI 3.14159265358979323846
56*fd1efedcSConrad Meyer #endif
57*fd1efedcSConrad Meyer #define EPOCH 85
58*fd1efedcSConrad Meyer #define EPSILONg 279.611371 /* solar ecliptic long at EPOCH */
59*fd1efedcSConrad Meyer #define RHOg 282.680403 /* solar ecliptic long of perigee at EPOCH */
60*fd1efedcSConrad Meyer #define ECCEN 0.01671542 /* solar orbit eccentricity */
61*fd1efedcSConrad Meyer #define lzero 18.251907 /* lunar mean long at EPOCH */
62*fd1efedcSConrad Meyer #define Pzero 192.917585 /* lunar mean long of perigee at EPOCH */
63*fd1efedcSConrad Meyer #define Nzero 55.204723 /* lunar mean long of node at EPOCH */
64*fd1efedcSConrad Meyer #define isleap(y) ((((y) % 4) == 0 && ((y) % 100) != 0) || ((y) % 400) == 0)
65*fd1efedcSConrad Meyer
66*fd1efedcSConrad Meyer static void adj360(double *);
67*fd1efedcSConrad Meyer static double dtor(double);
68*fd1efedcSConrad Meyer static double potm(double onday);
69*fd1efedcSConrad Meyer static double potm_minute(double onday, int olddir);
70*fd1efedcSConrad Meyer
71*fd1efedcSConrad Meyer void
pom(int year,double utcoffset,int * fms,int * nms)72*fd1efedcSConrad Meyer pom(int year, double utcoffset, int *fms, int *nms)
73*fd1efedcSConrad Meyer {
74*fd1efedcSConrad Meyer double ffms[MAXMOONS];
75*fd1efedcSConrad Meyer double fnms[MAXMOONS];
76*fd1efedcSConrad Meyer int i, j;
77*fd1efedcSConrad Meyer
78*fd1efedcSConrad Meyer fpom(year, utcoffset, ffms, fnms);
79*fd1efedcSConrad Meyer
80*fd1efedcSConrad Meyer j = 0;
81*fd1efedcSConrad Meyer for (i = 0; ffms[i] != 0; i++)
82*fd1efedcSConrad Meyer fms[j++] = round(ffms[i]);
83*fd1efedcSConrad Meyer fms[i] = -1;
84*fd1efedcSConrad Meyer for (i = 0; fnms[i] != 0; i++)
85*fd1efedcSConrad Meyer nms[i] = round(fnms[i]);
86*fd1efedcSConrad Meyer nms[i] = -1;
87*fd1efedcSConrad Meyer }
88*fd1efedcSConrad Meyer
89*fd1efedcSConrad Meyer void
fpom(int year,double utcoffset,double * ffms,double * fnms)90*fd1efedcSConrad Meyer fpom(int year, double utcoffset, double *ffms, double *fnms)
91*fd1efedcSConrad Meyer {
92*fd1efedcSConrad Meyer time_t tt;
93*fd1efedcSConrad Meyer struct tm GMT, tmd_today, tmd_tomorrow;
94*fd1efedcSConrad Meyer double days_today, days_tomorrow, today, tomorrow;
95*fd1efedcSConrad Meyer int cnt, d;
96*fd1efedcSConrad Meyer int yeardays;
97*fd1efedcSConrad Meyer int olddir, newdir;
98*fd1efedcSConrad Meyer double *pfnms, *pffms, t;
99*fd1efedcSConrad Meyer
100*fd1efedcSConrad Meyer pfnms = fnms;
101*fd1efedcSConrad Meyer pffms = ffms;
102*fd1efedcSConrad Meyer
103*fd1efedcSConrad Meyer /*
104*fd1efedcSConrad Meyer * We take the phase of the moon one second before and one second
105*fd1efedcSConrad Meyer * after midnight.
106*fd1efedcSConrad Meyer */
107*fd1efedcSConrad Meyer memset(&tmd_today, 0, sizeof(tmd_today));
108*fd1efedcSConrad Meyer tmd_today.tm_year = year - 1900;
109*fd1efedcSConrad Meyer tmd_today.tm_mon = 0;
110*fd1efedcSConrad Meyer tmd_today.tm_mday = -1; /* 31 December */
111*fd1efedcSConrad Meyer tmd_today.tm_hour = 23;
112*fd1efedcSConrad Meyer tmd_today.tm_min = 59;
113*fd1efedcSConrad Meyer tmd_today.tm_sec = 59;
114*fd1efedcSConrad Meyer memset(&tmd_tomorrow, 0, sizeof(tmd_tomorrow));
115*fd1efedcSConrad Meyer tmd_tomorrow.tm_year = year - 1900;
116*fd1efedcSConrad Meyer tmd_tomorrow.tm_mon = 0;
117*fd1efedcSConrad Meyer tmd_tomorrow.tm_mday = 0; /* 01 January */
118*fd1efedcSConrad Meyer tmd_tomorrow.tm_hour = 0;
119*fd1efedcSConrad Meyer tmd_tomorrow.tm_min = 0;
120*fd1efedcSConrad Meyer tmd_tomorrow.tm_sec = 1;
121*fd1efedcSConrad Meyer
122*fd1efedcSConrad Meyer tt = mktime(&tmd_today);
123*fd1efedcSConrad Meyer gmtime_r(&tt, &GMT);
124*fd1efedcSConrad Meyer yeardays = 0;
125*fd1efedcSConrad Meyer for (cnt = EPOCH; cnt < GMT.tm_year; ++cnt)
126*fd1efedcSConrad Meyer yeardays += isleap(1900 + cnt) ? DAYSPERLEAPYEAR : DAYSPERYEAR;
127*fd1efedcSConrad Meyer days_today = (GMT.tm_yday + 1) + ((GMT.tm_hour +
128*fd1efedcSConrad Meyer (GMT.tm_min / FSECSPERMINUTE) + (GMT.tm_sec / FSECSPERHOUR)) /
129*fd1efedcSConrad Meyer FHOURSPERDAY);
130*fd1efedcSConrad Meyer days_today += yeardays;
131*fd1efedcSConrad Meyer
132*fd1efedcSConrad Meyer tt = mktime(&tmd_tomorrow);
133*fd1efedcSConrad Meyer gmtime_r(&tt, &GMT);
134*fd1efedcSConrad Meyer yeardays = 0;
135*fd1efedcSConrad Meyer for (cnt = EPOCH; cnt < GMT.tm_year; ++cnt)
136*fd1efedcSConrad Meyer yeardays += isleap(1900 + cnt) ? DAYSPERLEAPYEAR : DAYSPERYEAR;
137*fd1efedcSConrad Meyer days_tomorrow = (GMT.tm_yday + 1) + ((GMT.tm_hour +
138*fd1efedcSConrad Meyer (GMT.tm_min / FSECSPERMINUTE) + (GMT.tm_sec / FSECSPERHOUR)) /
139*fd1efedcSConrad Meyer FHOURSPERDAY);
140*fd1efedcSConrad Meyer days_tomorrow += yeardays;
141*fd1efedcSConrad Meyer
142*fd1efedcSConrad Meyer today = potm(days_today); /* 30 December 23:59:59 */
143*fd1efedcSConrad Meyer tomorrow = potm(days_tomorrow); /* 31 December 00:00:01 */
144*fd1efedcSConrad Meyer olddir = today > tomorrow ? -1 : +1;
145*fd1efedcSConrad Meyer
146*fd1efedcSConrad Meyer yeardays = 1 + (isleap(year) ? DAYSPERLEAPYEAR : DAYSPERYEAR); /* reuse */
147*fd1efedcSConrad Meyer for (d = 0; d <= yeardays; d++) {
148*fd1efedcSConrad Meyer today = potm(days_today);
149*fd1efedcSConrad Meyer tomorrow = potm(days_tomorrow);
150*fd1efedcSConrad Meyer newdir = today > tomorrow ? -1 : +1;
151*fd1efedcSConrad Meyer if (olddir != newdir) {
152*fd1efedcSConrad Meyer t = potm_minute(days_today - 1, olddir) +
153*fd1efedcSConrad Meyer utcoffset / FHOURSPERDAY;
154*fd1efedcSConrad Meyer if (olddir == -1 && newdir == +1) {
155*fd1efedcSConrad Meyer *pfnms = d - 1 + t;
156*fd1efedcSConrad Meyer pfnms++;
157*fd1efedcSConrad Meyer } else if (olddir == +1 && newdir == -1) {
158*fd1efedcSConrad Meyer *pffms = d - 1 + t;
159*fd1efedcSConrad Meyer pffms++;
160*fd1efedcSConrad Meyer }
161*fd1efedcSConrad Meyer }
162*fd1efedcSConrad Meyer olddir = newdir;
163*fd1efedcSConrad Meyer days_today++;
164*fd1efedcSConrad Meyer days_tomorrow++;
165*fd1efedcSConrad Meyer }
166*fd1efedcSConrad Meyer *pffms = -1;
167*fd1efedcSConrad Meyer *pfnms = -1;
168*fd1efedcSConrad Meyer }
169*fd1efedcSConrad Meyer
170*fd1efedcSConrad Meyer static double
potm_minute(double onday,int olddir)171*fd1efedcSConrad Meyer potm_minute(double onday, int olddir) {
172*fd1efedcSConrad Meyer double period = FSECSPERDAY / 2.0;
173*fd1efedcSConrad Meyer double p1, p2;
174*fd1efedcSConrad Meyer double before, after;
175*fd1efedcSConrad Meyer int newdir;
176*fd1efedcSConrad Meyer
177*fd1efedcSConrad Meyer // printf("---> days:%g olddir:%d\n", days, olddir);
178*fd1efedcSConrad Meyer
179*fd1efedcSConrad Meyer p1 = onday + (period / SECSPERDAY);
180*fd1efedcSConrad Meyer period /= 2;
181*fd1efedcSConrad Meyer
182*fd1efedcSConrad Meyer while (period > 30) { /* half a minute */
183*fd1efedcSConrad Meyer // printf("period:%g - p1:%g - ", period, p1);
184*fd1efedcSConrad Meyer p2 = p1 + (2.0 / SECSPERDAY);
185*fd1efedcSConrad Meyer before = potm(p1);
186*fd1efedcSConrad Meyer after = potm(p2);
187*fd1efedcSConrad Meyer // printf("before:%10.10g - after:%10.10g\n", before, after);
188*fd1efedcSConrad Meyer newdir = before < after ? -1 : +1;
189*fd1efedcSConrad Meyer if (olddir != newdir)
190*fd1efedcSConrad Meyer p1 += (period / SECSPERDAY);
191*fd1efedcSConrad Meyer else
192*fd1efedcSConrad Meyer p1 -= (period / SECSPERDAY);
193*fd1efedcSConrad Meyer period /= 2;
194*fd1efedcSConrad Meyer // printf("newdir:%d - p1:%10.10f - period:%g\n",
195*fd1efedcSConrad Meyer // newdir, p1, period);
196*fd1efedcSConrad Meyer }
197*fd1efedcSConrad Meyer p1 -= floor(p1);
198*fd1efedcSConrad Meyer //exit(0);
199*fd1efedcSConrad Meyer return (p1);
200*fd1efedcSConrad Meyer }
201*fd1efedcSConrad Meyer
202*fd1efedcSConrad Meyer /*
203*fd1efedcSConrad Meyer * potm --
204*fd1efedcSConrad Meyer * return phase of the moon, as a percentage [0 ... 100]
205*fd1efedcSConrad Meyer */
206*fd1efedcSConrad Meyer static double
potm(double onday)207*fd1efedcSConrad Meyer potm(double onday)
208*fd1efedcSConrad Meyer {
209*fd1efedcSConrad Meyer double N, Msol, Ec, LambdaSol, l, Mm, Ev, Ac, A3, Mmprime;
210*fd1efedcSConrad Meyer double A4, lprime, V, ldprime, D, Nm;
211*fd1efedcSConrad Meyer
212*fd1efedcSConrad Meyer N = 360 * onday / 365.2422; /* sec 42 #3 */
213*fd1efedcSConrad Meyer adj360(&N);
214*fd1efedcSConrad Meyer Msol = N + EPSILONg - RHOg; /* sec 42 #4 */
215*fd1efedcSConrad Meyer adj360(&Msol);
216*fd1efedcSConrad Meyer Ec = 360 / PI * ECCEN * sin(dtor(Msol)); /* sec 42 #5 */
217*fd1efedcSConrad Meyer LambdaSol = N + Ec + EPSILONg; /* sec 42 #6 */
218*fd1efedcSConrad Meyer adj360(&LambdaSol);
219*fd1efedcSConrad Meyer l = 13.1763966 * onday + lzero; /* sec 61 #4 */
220*fd1efedcSConrad Meyer adj360(&l);
221*fd1efedcSConrad Meyer Mm = l - (0.1114041 * onday) - Pzero; /* sec 61 #5 */
222*fd1efedcSConrad Meyer adj360(&Mm);
223*fd1efedcSConrad Meyer Nm = Nzero - (0.0529539 * onday); /* sec 61 #6 */
224*fd1efedcSConrad Meyer adj360(&Nm);
225*fd1efedcSConrad Meyer Ev = 1.2739 * sin(dtor(2*(l - LambdaSol) - Mm)); /* sec 61 #7 */
226*fd1efedcSConrad Meyer Ac = 0.1858 * sin(dtor(Msol)); /* sec 61 #8 */
227*fd1efedcSConrad Meyer A3 = 0.37 * sin(dtor(Msol));
228*fd1efedcSConrad Meyer Mmprime = Mm + Ev - Ac - A3; /* sec 61 #9 */
229*fd1efedcSConrad Meyer Ec = 6.2886 * sin(dtor(Mmprime)); /* sec 61 #10 */
230*fd1efedcSConrad Meyer A4 = 0.214 * sin(dtor(2 * Mmprime)); /* sec 61 #11 */
231*fd1efedcSConrad Meyer lprime = l + Ev + Ec - Ac + A4; /* sec 61 #12 */
232*fd1efedcSConrad Meyer V = 0.6583 * sin(dtor(2 * (lprime - LambdaSol))); /* sec 61 #13 */
233*fd1efedcSConrad Meyer ldprime = lprime + V; /* sec 61 #14 */
234*fd1efedcSConrad Meyer D = ldprime - LambdaSol; /* sec 63 #2 */
235*fd1efedcSConrad Meyer return(50 * (1 - cos(dtor(D)))); /* sec 63 #3 */
236*fd1efedcSConrad Meyer }
237*fd1efedcSConrad Meyer
238*fd1efedcSConrad Meyer /*
239*fd1efedcSConrad Meyer * dtor --
240*fd1efedcSConrad Meyer * convert degrees to radians
241*fd1efedcSConrad Meyer */
242*fd1efedcSConrad Meyer static double
dtor(double deg)243*fd1efedcSConrad Meyer dtor(double deg)
244*fd1efedcSConrad Meyer {
245*fd1efedcSConrad Meyer
246*fd1efedcSConrad Meyer return(deg * PI / 180);
247*fd1efedcSConrad Meyer }
248*fd1efedcSConrad Meyer
249*fd1efedcSConrad Meyer /*
250*fd1efedcSConrad Meyer * adj360 --
251*fd1efedcSConrad Meyer * adjust value so 0 <= deg <= 360
252*fd1efedcSConrad Meyer */
253*fd1efedcSConrad Meyer static void
adj360(double * deg)254*fd1efedcSConrad Meyer adj360(double *deg)
255*fd1efedcSConrad Meyer {
256*fd1efedcSConrad Meyer
257*fd1efedcSConrad Meyer for (;;)
258*fd1efedcSConrad Meyer if (*deg < 0)
259*fd1efedcSConrad Meyer *deg += 360;
260*fd1efedcSConrad Meyer else if (*deg > 360)
261*fd1efedcSConrad Meyer *deg -= 360;
262*fd1efedcSConrad Meyer else
263*fd1efedcSConrad Meyer break;
264*fd1efedcSConrad Meyer }
265