#include <err.h>
#include <math.h>
#include <stdio.h>
#include "basics.h"
#include "gregorian.h"
#include "utils.h"
int
dayofweek_from_fixed(int rd)
{
return mod(rd, 7);
}
int
kday_onbefore(int dow, int rd)
{
return rd - dayofweek_from_fixed(rd - dow);
}
int
kday_after(int dow, int rd)
{
return kday_onbefore(dow, rd + 7);
}
int
kday_nearest(int dow, int rd)
{
return kday_onbefore(dow, rd + 3);
}
int
nth_kday(int n, int dow, struct date *date)
{
if (n == 0)
errx(1, "%s: invalid n = 0!", __func__);
int rd = fixed_from_gregorian(date);
int kday;
if (n > 0)
kday = kday_onbefore(dow, rd - 1);
else
kday = kday_onbefore(dow, rd + 7);
return 7 * n + kday;
}
double
ephemeris_correction(double t)
{
int rd = (int)floor(t);
int year = gregorian_year_from_fixed(rd);
int y2000 = year - 2000;
int y1700 = year - 1700;
int y1600 = year - 1600;
double y1820 = (year - 1820) / 100.0;
double y1000 = (year - 1000) / 100.0;
double y0 = year / 100.0;
double coef2006[] = { 62.92, 0.32217, 0.005589 };
double coef1987[] = { 63.86, 0.3345, -0.060374, 0.0017275,
0.000651814, 0.00002373599 };
double coef1900[] = { -0.00002, 0.000297, 0.025184, -0.181133,
0.553040, -0.861938, 0.677066, -0.212591 };
double coef1800[] = { -0.000009, 0.003844, 0.083563, 0.865736,
4.867575, 15.845535, 31.332267, 38.291999,
28.316289, 11.636204, 2.043794 };
double coef1700[] = { 8.118780842, -0.005092142,
0.003336121, -0.0000266484 };
double coef1600[] = { 120.0, -0.9808, -0.01532, 0.000140272128 };
double coef500[] = { 1574.2, -556.01, 71.23472, 0.319781,
-0.8503463, -0.005050998, 0.0083572073 };
double coef0[] = { 10583.6, -1014.41, 33.78311, -5.952053,
-0.1798452, 0.022174192, 0.0090316521 };
double c_other = (-20.0 + 32.0 * y1820 * y1820) / 86400.0;
struct date date1 = { 1900, 1, 1 };
struct date date2 = { year, 7, 1 };
double c = gregorian_date_difference(&date1, &date2) / 36525.0;
if (year > 2150) {
return c_other;
} else if (year >= 2051) {
return c_other + 0.5628 * (2150 - year) / 86400.0;
} else if (year >= 2006) {
return poly(y2000, coef2006, nitems(coef2006)) / 86400.0;
} else if (year >= 1987) {
return poly(y2000, coef1987, nitems(coef1987)) / 86400.0;
} else if (year >= 1900) {
return poly(c, coef1900, nitems(coef1900));
} else if (year >= 1800) {
return poly(c, coef1800, nitems(coef1800));
} else if (year >= 1700) {
return poly(y1700, coef1700, nitems(coef1700)) / 86400.0;
} else if (year >= 1600) {
return poly(y1600, coef1600, nitems(coef1600)) / 86400.0;
} else if (year >= 500) {
return poly(y1000, coef500, nitems(coef500)) / 86400.0;
} else if (year > -500) {
return poly(y0, coef0, nitems(coef0)) / 86400.0;
} else {
return c_other;
}
}
double
dynamical_from_universal(double t)
{
return t + ephemeris_correction(t);
}
double
universal_from_dynamical(double t)
{
return t - ephemeris_correction(t);
}
double
julian_centuries(double t)
{
double dt = dynamical_from_universal(t);
double j2000 = 0.5 + gregorian_new_year(2000);
return (dt - j2000) / 36525.0;
}
double
sidereal_from_moment(double t)
{
double j2000 = 0.5 + gregorian_new_year(2000);
int century_days = 36525;
double c = (t - j2000) / century_days;
double coef[] = { 280.46061837, 360.98564736629 * century_days,
0.000387933, -1.0 / 38710000.0 };
return mod_f(poly(c, coef, nitems(coef)), 360);
}
double
equation_of_time(double t)
{
double c = julian_centuries(t);
double epsilon = obliquity(t);
double y = pow(tan_deg(epsilon/2), 2);
double lambda = 280.46645 + 36000.76983 * c + 0.0003032 * c*c;
double anomaly = (357.52910 + 35999.05030 * c -
0.0001559 * c*c - 0.00000048 * c*c*c);
double eccentricity = (0.016708617 - 0.000042037 * c -
0.0000001236 * c*c);
double equation = (y * sin_deg(2*lambda) -
2 * eccentricity * sin_deg(anomaly) +
4 * eccentricity * y * sin_deg(anomaly) * cos_deg(2*lambda) -
1.25 * eccentricity*eccentricity * sin_deg(2*anomaly) -
0.5 * y*y * sin_deg(4*lambda)) / (2*M_PI);
double vmax = 0.5;
if (fabs(equation) < vmax)
return equation;
else
return (equation > 0) ? vmax : -vmax;
}
double
apparent_from_local(double t, double longitude)
{
double ut = t - longitude / 360.0;
return t + equation_of_time(ut);
}
double
local_from_apparent(double t, double longitude)
{
double ut = t - longitude / 360.0;
return t - equation_of_time(ut);
}
double
obliquity(double t)
{
double c = julian_centuries(t);
double coef[] = {
0.0,
angle2deg(0, 0, -46.8150),
angle2deg(0, 0, -0.00059),
angle2deg(0, 0, 0.001813),
};
double correction = poly(c, coef, nitems(coef));
return angle2deg(23, 26, 21.448) + correction;
}
double
declination(double t, double beta, double lambda)
{
double epsilon = obliquity(t);
return arcsin_deg(sin_deg(beta) * cos_deg(epsilon) +
cos_deg(beta) * sin_deg(epsilon) * sin_deg(lambda));
}
double
right_ascension(double t, double beta, double lambda)
{
double epsilon = obliquity(t);
double x = cos_deg(lambda);
double y = (sin_deg(lambda) * cos_deg(epsilon) -
tan_deg(beta) * sin_deg(epsilon));
return arctan_deg(y, x);
}
double
refraction(double elevation)
{
double h = (elevation > 0) ? elevation : 0.0;
double radius = 6.372e6;
double dip = arccos_deg(radius / (radius + h));
double dip2 = (19.0/3600.0) * sqrt(h);
double avg = 34.0 / 60.0;
return avg + dip + dip2;
}
int
dayofyear_from_fixed(int rd)
{
int year = gregorian_year_from_fixed(rd);
struct date date = { year - 1, 12, 31 };
return rd - fixed_from_gregorian(&date);
}
int
format_time(char *buf, size_t size, double t)
{
int hh, mm, ss, i;
t -= floor(t);
i = (int)round(t * 24*60*60);
hh = i / (60*60);
i %= 60*60;
mm = i / 60;
ss = i % 60;
return snprintf(buf, size, "%02d:%02d:%02d", hh, mm, ss);
}
int
format_zone(char *buf, size_t size, double zone)
{
bool positive;
int hh, mm, i;
positive = (zone >= 0);
i = (int)round(fabs(zone) * 24*60);
hh = i / 60;
mm = i % 60;
return snprintf(buf, size, "%c%02d:%02d",
(positive ? '+' : '-'), hh, mm);
}
int
format_location(char *buf, size_t size, const struct location *loc)
{
int d1, d2, m1, m2, s1, s2, i;
bool north, east;
north = (loc->latitude >= 0);
i = (int)round(fabs(loc->latitude) * 60*60);
d1 = i / (60*60);
i %= 60*60;
m1 = i / 60;
s1 = i % 60;
east = (loc->longitude >= 0);
i = (int)round(fabs(loc->longitude) * 60*60);
d2 = i / (60*60);
i %= 60*60;
m2 = i / 60;
s2 = i % 60;
return snprintf(buf, size, "%d°%d'%d\" %c, %d°%d'%d\" %c, %.1lf m",
d1, m1, s1, (north ? 'N' : 'S'),
d2, m2, s2, (east ? 'E' : 'W'),
loc->elevation);
}