Skip to content
Permalink
master
Switch branches/tags
Go to file
 
 
Cannot retrieve contributors at this time
executable file 494 lines (398 sloc) 12.9 KB
/*---------------------------------------------------------------------------
FILENAME:
lunarCycle.c
PURPOSE:
Provide lunar cycle computation utility.
REVISION HISTORY:
Date Engineer Revision Remarks
08/17/2005 M.S. Teel 0 Original
12/01/2009 M. Hornsby 1 Add Moon Rise and Set
NOTES:
LICENSE:
Copyright (c) 2005, Mark S. Teel (mark@teel.ws)
This source code is released for free distribution under the terms
of the GNU General Public License.
----------------------------------------------------------------------------*/
// ... System header files
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <math.h>
#include <config.h>
#include "sysdefs.h"
typedef struct
{
double rightascension;
double declination;
double parallax;
}
MOONLOCATION;
typedef struct
{
int hr;
int min;
double az;
int event;
}
MOONRISESET;
static double VHz[3], RAn[3], Dec[3];
static MOONRISESET MoonRise, MoonSet;
#define PI 3.1415926535897932384626433832795
#define RAD (PI/180.0)
#define SMALL_FLOAT (1e-12)
// Local methods:
static double GetJulianDate (int year, int month, double day)
{
int a, b, c, e;
if (month < 3)
{
year--;
month += 12;
}
if ((year > 1582) ||
(year == 1582 && month > 10) ||
(year == 1582 && month == 10 && day > 15))
{
a = year/100;
b = 2 - a+a/4;
c = (int)(365.25 * year);
e = (int)(30.6001 * (month + 1));
return (b + c + e + day + 1720994.5);
}
else
{
return 0;
}
}
static double GetSunPosition (double j)
{
double n, x, e, l, dl, v;
int i;
n = 360/365.2422 * j;
i = (int)(n/360);
n = n - i*360.0;
x = n - 3.762863;
if (x < 0)
x += 360;
x *= RAD;
e = x;
do
{
dl = e - 0.016718 * sin(e) - x;
e = e - dl/(1 - 0.016718 * cos(e));
}
while (fabs(dl) >= SMALL_FLOAT);
v = 360/PI * atan(1.01686011182 * tan(e/2));
l = v + 282.596403;
i = (int)(l/360);
l = l - i*360.0;
return l;
}
static double GetMoonPosition (double j, double ls)
{
double ms, l, mm, n, ev, sms, ae, ec;
int i;
ms = 0.985647332099*j - 3.762863;
if (ms < 0)
ms += 360.0;
l = 13.176396*j + 64.975464;
i = (int)(l/360);
l = l - i*360.0;
if (l < 0)
l += 360.0;
mm = l-0.1114041*j-349.383063;
i = (int)(mm/360);
mm -= i*360.0;
n = 151.950429 - 0.0529539*j;
i = (int)(n/360);
n -= i*360.0;
ev = 1.2739*sin((2*(l-ls)-mm)*RAD);
sms = sin(ms*RAD);
ae = 0.1858*sms;
mm += ev-ae- 0.37*sms;
ec = 6.2886*sin(mm*RAD);
l += ev+ec-ae+ 0.214*sin(2*mm*RAD);
l= 0.6583*sin(2*(l-ls)*RAD)+l;
return l;
}
static double GetMoonPhase (int year, int month, int day, int hour)
{
double j = GetJulianDate(year,month,(double)day+(double)hour/24.0)-2444238.5;
double ls = GetSunPosition(j);
double lm = GetMoonPosition(j, ls);
double t = lm - ls;
double retVal;
retVal = (1.0 - cos((lm - ls)*RAD))/2;
retVal *= 1000;
retVal += 0.5;
retVal /= 10;
if (t < 0)
t += 360;
if (t > 180)
retVal *= -1;
return retVal;
}
// Return the sign of a number.
static int getSign( double num)
{
if (num < 0)
return(-1);
if (num > 0)
return(1);
return(0);
}
// Local Sidereal Time for zone in Radians
static double localSiderealTime( double lon, double jd, double tz )
{
double TU, lmst, gmst;
TU = jd / 36525.0;
gmst = 24110.54841 + TU*(8640184.812866 + TU*(0.093104 + TU*(-6.2E-6)));
lmst = gmst - 86636.6 * tz / 24.0 + WV_SECONDS_IN_DAY * lon / 360.0;
lmst = lmst / WV_SECONDS_IN_DAY; // rotations
lmst = lmst - floor(lmst); // fraction of a circle
return lmst*2.0*PI;
}
/* 3-point interpolation */
static double moonInterpolate( double f0, double f1, double f2, double p )
{
double a, b, f;
a = f1 - f0;
b = f2 - f1 - a;
f = f0 + p*(2*a + b*(2*p - 1));
return f;
}
/* test an hour for an event */
static double moonTest(int k, double t0, double lat, double plx)
{
double ha[3];
double a, b, c, d, e, s, z;
double hr, min, time;
double az, hz, nz, dz;
double K1 = 15 * PI / 180.0 * 1.0027379;
double DR = PI / 180.0;
if (RAn[2] < RAn[0])
RAn[2] = RAn[2] + 2.0*PI;
ha[0] = t0 - RAn[0] + k*K1;
ha[2] = t0 - RAn[2] + k*K1 + K1;
ha[1] = (ha[2] + ha[0])/2.0; /* hour angle at half hour */
Dec[1] = (Dec[2] + Dec[0])/2.0; /* declination at half hour */
s = sin(DR*lat);
c = cos(DR*lat);
// refraction + sun semidiameter at horizon + parallax correction
z = cos(DR*(90.567 - 41.685/plx));
if (k <= 0) // first call of function
VHz[0] = s * sin(Dec[0]) + c * cos(Dec[0]) * cos(ha[0]) - z;
VHz[2] = s * sin(Dec[2]) + c * cos(Dec[2]) * cos(ha[2]) - z;
if (getSign(VHz[0]) == getSign(VHz[2]))
return VHz[2]; // no event this hour
VHz[1] = s * sin(Dec[1]) + c * cos(Dec[1]) * cos(ha[1]) - z;
a = 2.0*VHz[2] - 4.0*VHz[1] + 2.0*VHz[0];
b = 4.0*VHz[1] - 3.0*VHz[0] - VHz[2];
d = b*b - 4.0*a*VHz[0];
if (d < 0.0)
return VHz[2]; // no event this hour
d = sqrt(d);
e = (-b + d)/(2.0*a);
if (( e > 1 )||( e < 0.0 ))
e = (-b - d)/(2.0*a);
time = k + e + 1.0/120.0; // time of an event + round up
hr = floor(time);
min = floor((time - hr)*60.0);
hz = ha[0] + e * (ha[2] - ha[0]); // azimuth of the moon at the event
nz = -cos(Dec[1]) * sin(hz);
dz = c * sin(Dec[1]) - s * cos(Dec[1]) * cos(hz);
az = atan2(nz, dz)/DR;
if (az < 0.0)
az = az + 360.0;
if ((VHz[0] < 0.0) && (VHz[2] > 0.0))
{
MoonRise.hr = (int)hr;
MoonRise.min = (int)min;
MoonRise.az = az;
MoonRise.event = 1;
}
if ((VHz[0] > 0.0) && (VHz[2] < 0.0))
{
MoonSet.hr = (int)hr;
MoonSet.min = (int)min;
MoonSet.az = az;
MoonSet.event = 1;
}
return VHz[2];
}
/*
* moon's position using fundamental arguments
* (Van Flandern & Pulkkinen, 1979)
*/
static MOONLOCATION GetMoonLocation(double jd)
{
double d, f, g, h, m, n, s, u, v, w;
MOONLOCATION itshere;
h = 0.606434 + 0.03660110129 * jd;
m = 0.374897 + 0.03629164709 * jd;
f = 0.259091 + 0.03674819520 * jd;
d = 0.827362 + 0.03386319198 * jd;
n = 0.347343 - 0.00014709391 * jd;
g = 0.993126 + 0.00273777850 * jd;
h = h - floor(h);
m = m - floor(m);
f = f - floor(f);
d = d - floor(d);
n = n - floor(n);
g = g - floor(g);
h = h*2*PI;
m = m*2*PI;
f = f*2*PI;
d = d*2*PI;
n = n*2*PI;
g = g*2*PI;
v = 0.39558 * sin(f + n);
v = v + 0.08200 * sin(f);
v = v + 0.03257 * sin(m - f - n);
v = v + 0.01092 * sin(m + f + n);
v = v + 0.00666 * sin(m - f);
v = v - 0.00644 * sin(m + f - 2*d + n);
v = v - 0.00331 * sin(f - 2*d + n);
v = v - 0.00304 * sin(f - 2*d);
v = v - 0.00240 * sin(m - f - 2*d - n);
v = v + 0.00226 * sin(m + f);
v = v - 0.00108 * sin(m + f - 2*d);
v = v - 0.00079 * sin(f - n);
v = v + 0.00078 * sin(f + 2*d + n);
u = 1 - 0.10828 * cos(m);
u = u - 0.01880 * cos(m - 2*d);
u = u - 0.01479 * cos(2*d);
u = u + 0.00181 * cos(2*m - 2*d);
u = u - 0.00147 * cos(2*m);
u = u - 0.00105 * cos(2*d - g);
u = u - 0.00075 * cos(m - 2*d + g);
w = 0.10478 * sin(m);
w = w - 0.04105 * sin(2*f + 2*n);
w = w - 0.02130 * sin(m - 2*d);
w = w - 0.01779 * sin(2*f + n);
w = w + 0.01774 * sin(n);
w = w + 0.00987 * sin(2*d);
w = w - 0.00338 * sin(m - 2*f - 2*n);
w = w - 0.00309 * sin(g);
w = w - 0.00190 * sin(2*f);
w = w - 0.00144 * sin(m + n);
w = w - 0.00144 * sin(m - 2*f - n);
w = w - 0.00113 * sin(m + 2*f + 2*n);
w = w - 0.00094 * sin(m - 2*d + g);
w = w - 0.00092 * sin(2*m - 2*d);
s = w/sqrt(u - v*v); // compute moon's ... right ascension
itshere.rightascension = h + atan(s/sqrt(1 - s*s));
s = v/sqrt(u); // declination ...
itshere.declination = atan(s/sqrt(1 - s*s));
itshere.parallax = 60.40974 * sqrt( u ); // and parallax
return(itshere);
}
// Public methods:
#define PHASE_STR_MAX 128
char *lunarPhaseGet (char *increase, char *decrease, char *full)
{
static char phaseStr[PHASE_STR_MAX];
time_t timeNow = time (NULL);
double phase;
struct tm bknTime;
localtime_r (&timeNow, &bknTime);
// compute the period value
phase = GetMoonPhase (bknTime.tm_year+1900, bknTime.tm_mon+1,
bknTime.tm_mday, bknTime.tm_hour);
if (phase < 0)
snprintf(phaseStr, PHASE_STR_MAX-1, "%s %.0f%c %s", decrease, fabs(phase), '%', full);
else
snprintf(phaseStr, PHASE_STR_MAX-1, "%s %.0f%c %s", increase, phase, '%', full);
return phaseStr;
}
/*
* This is a C language implementation of the Sky and Telscope BASIC
* program 1989 page 78 http://media.skyandtelescope.com/binary/moonup.bas
*
* Its based on the Java implementation by Stephen R. Schmitt
* http://home.att.net/~srschmitt/script_moon_rise_set.html
*/
// calculate MoonRise and MoonSet times
//
// Returns Rise and Set times times returned as packed time (hour*100 + minutes)
//
// packedRise > 0 && packedSet = -1 => the moon rises and never sets
// packedRise = -1 && packSet > 0 => no moon rise and the moon sets
// packedRise = packedSet = -1 => the moon never sets
// packedRise = packedSet = -2 => the moon never rises
int GetMoonRiseSetTimes
(
int year,
int month,
int day,
double zone, // Timezone offset from UTC/GMT in hours
double lat, // Latitude degress N=> +, S=> -
double lon, // longitude degress E=> +, W=> -
short *packedRise, // returned Moon Rise time
double *riseAz, // return Moon Rise Azimuth
short *packedSet, // returned Moon Set time
double *setAz // return Moon Set Azimuth
)
{
int k;
MOONLOCATION mp[3];
double localsidereal;
double ph;
double jd;
// Julian day relative to Jan 1.5, 2000
jd = GetJulianDate(year, month, (double)day) - 2451545.0;
localsidereal = localSiderealTime(lon, jd, zone); // local sidereal time
jd = jd - zone / 24.0; // get moon position at day start
for (k = 0; k < 3; k ++)
{
mp[k] = GetMoonLocation(jd);
jd = jd + 0.5;
}
if (mp[1].rightascension <= mp[0].rightascension)
mp[1].rightascension = mp[1].rightascension + 2*PI;
if (mp[2].rightascension <= mp[1].rightascension)
mp[2].rightascension = mp[2].rightascension + 2*PI;
RAn[0] = mp[0].rightascension;
Dec[0] = mp[0].declination;
MoonRise.event = 0; // initialize
MoonSet.event = 0;
for (k = 0; k < 24; k++) // check each hour of this day
{
ph = (k + 1.0)/24.0;
RAn[2] = moonInterpolate(mp[0].rightascension,
mp[1].rightascension,
mp[2].rightascension,
ph);
Dec[2] = moonInterpolate(mp[0].declination,
mp[1].declination,
mp[2].declination,
ph);
VHz[2] = moonTest(k, localsidereal, lat, mp[1].parallax);
RAn[0] = RAn[2]; // advance to next hour
Dec[0] = Dec[2];
VHz[0] = VHz[2];
}
*packedRise = (short)(MoonRise.hr * 100 + MoonRise.min);
if (riseAz != NULL)
*riseAz = MoonRise.az;
*packedSet = (short)(MoonSet.hr * 100 + MoonSet.min);
if (setAz != NULL)
*setAz = MoonSet.az;
/*check for no MoonRise and/or no MoonSet */
if (! MoonRise.event && ! MoonSet.event) // neither MoonRise nor MoonSet
{
if (VHz[2] < 0)
*packedRise = *packedSet = -2; // the moon never sets
else
*packedRise = *packedSet = -1; // the moon never rises
}
else // check for MoonRise or MoonSet
{
if (! MoonRise.event)
*packedRise = -1; // no MoonRise and the moon sets
else if (! MoonSet.event)
*packedSet = -1; // the moon rises and never sets
}
return OK;
}