xref: /freebsd/usr.bin/calendar/pom.c (revision 0b8224d1cc9dc6c9778ba04a75b2c8d47e5d7481)
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