3#ifndef _TIME_SHIELD_MOON_PHASE_HPP_INCLUDED
4#define _TIME_SHIELD_MOON_PHASE_HPP_INCLUDED
82 const double N =
fix_angle((360.0 / 365.2422) * day);
86 Ec = std::sqrt((1.0 +
kEccent) / (1.0 -
kEccent)) * std::tan(Ec / 2.0);
87 Ec = 2.0 *
rad2deg(std::atan(Ec));
91 const double sun_dist =
kSunSmax / F;
98 const double Ev = 1.2739 * std::sin(
deg2rad(2.0 * (ml - lambda_sun) - MM));
99 const double Ae = 0.1858 * std::sin(
deg2rad(M));
100 const double A3 = 0.37 * std::sin(
deg2rad(M));
101 const double MmP = MM + Ev - Ae - A3;
103 const double mEc = 6.2886 * std::sin(
deg2rad(MmP));
104 const double A4 = 0.214 * std::sin(
deg2rad(2.0 * MmP));
105 const double lP = ml + Ev + mEc - Ae + A4;
107 const double V = 0.6583 * std::sin(
deg2rad(2.0 * (lP - lambda_sun)));
108 const double lPP = lP + V;
111 const double moon_age_deg_wrapped =
fix_angle(lPP - lambda_sun);
112 const double moon_age_rad =
deg2rad(moon_age_deg_wrapped);
113 const double illum = (1.0 - std::cos(moon_age_rad)) / 2.0;
118 const double moon_dfrac = moon_dist /
kMsMax;
119 const double moon_ang =
kMAngSiz / moon_dfrac;
121 out.phase = moon_age_deg_wrapped / 360.0;
122 out.illumination = illum;
124 out.distance_km = moon_dist;
125 out.diameter_deg = moon_ang;
126 out.age_deg = moon_age_deg_wrapped;
127 out.phase_angle_rad = moon_age_rad;
128 out.phase_sin = std::sin(moon_age_rad);
129 out.phase_cos = std::cos(moon_age_rad);
130 out.sun_distance_km = sun_dist;
131 out.sun_diameter_deg = sun_ang;
147 double adate = sdate - 45.0;
149 const double ats = unix_utc_s - 86400.0 * 45.0;
154 double k1 = std::floor((yy + ((mm - 1) * (1.0 / 12.0)) - 1900.0) * 12.3685);
165 if (std::abs(nt2 - sdate) < 0.75) {
169 if (nt1 <= sdate && nt2 > sdate) {
177 const double dates_jd[8] = {
189 for (std::size_t i = 0; i < 8; ++i) {
209 out.previous_first_quarter_unix_s = quarters[1];
210 out.previous_full_unix_s = quarters[2];
211 out.previous_last_quarter_unix_s = quarters[3];
212 out.next_new_unix_s = quarters[4];
213 out.next_first_quarter_unix_s = quarters[5];
214 out.next_full_unix_s = quarters[6];
215 out.next_last_quarter_unix_s = quarters[7];
225 return is_within_window(unix_utc_s, instants.previous_new_unix_s, instants.next_new_unix_s, window_seconds);
234 return is_within_window(unix_utc_s, instants.previous_full_unix_s, instants.next_full_unix_s, window_seconds);
243 return is_within_window(unix_utc_s, instants.previous_first_quarter_unix_s, instants.next_first_quarter_unix_s, window_seconds);
252 return is_within_window(unix_utc_s, instants.previous_last_quarter_unix_s, instants.next_last_quarter_unix_s, window_seconds);
257 return 2440587.5 + unix_utc_s / 86400.0;
269 return (julian_day - 2440587.5) * 86400.0;
283 [[maybe_unused]]
static constexpr double kMNode = 151.950429;
284 [[maybe_unused]]
static constexpr double kMInc = 5.145396;
285 static constexpr double kMecc = 0.054900;
287 static constexpr double kMsMax = 384401.0;
291 static constexpr double kPi = 3.14159265358979323846;
293 static double deg2rad(
double deg)
noexcept {
return deg * (
kPi / 180.0); }
294 static double rad2deg(
double rad)
noexcept {
return rad * (180.0 /
kPi); }
297 a = std::fmod(a, 360.0);
298 if (a < 0.0) a += 360.0;
302 static double kepler(
double m_deg,
double ecc)
noexcept {
303 constexpr double eps = 1e-6;
304 const double m =
deg2rad(m_deg);
306 for (
int i = 0; i < 50; ++i) {
307 const double delta = e - ecc * std::sin(e) - m;
308 e -= delta / (1.0 - ecc * std::cos(e));
309 if (std::abs(delta) <= eps)
break;
314 double mean_phase_jd(
double julian_day,
double lunation_index)
const noexcept {
315 const double jt = (julian_day - 2415020.0) / 36525.0;
316 const double t2 = jt * jt;
317 const double t3 = t2 * jt;
319 return 2415020.75933 +
kSynMonth * lunation_index
322 + 0.00033 * std::sin(
deg2rad(166.56 + 132.87 * jt - 0.009173 * t2));
325 double true_phase_jd(
double lunation_index,
double phase_fraction)
const noexcept {
327 const double kx = lunation_index + phase_fraction;
328 const double t = kx / 1236.85;
329 const double t2 = t * t;
330 const double t3 = t2 * t;
332 double pt = 2415020.75933
336 + 0.00033 * std::sin(
deg2rad(166.56 + 132.87 * t - 0.009173 * t2));
338 const double m = 359.2242 + 29.10535608 * kx - 0.0000333 * t2 - 0.00000347 * t3;
339 const double mprime = 306.0253 + 385.81691806 * kx + 0.0107306 * t2 + 0.00001236 * t3;
340 const double f = 21.2964 + 390.67050646 * kx - 0.0016528 * t2 - 0.00000239 * t3;
343 if (phase_fraction < 0.01 || std::abs(phase_fraction - 0.5) < 0.01) {
344 pt += (0.1734 - 0.000393 * t) * std::sin(
deg2rad(m))
345 + 0.0021 * std::sin(
deg2rad(2 * m))
346 - 0.4068 * std::sin(
deg2rad(mprime))
347 + 0.0161 * std::sin(
deg2rad(2 * mprime))
348 - 0.0004 * std::sin(
deg2rad(3 * mprime))
349 + 0.0104 * std::sin(
deg2rad(2 * f))
350 - 0.0051 * std::sin(
deg2rad(m + mprime))
351 - 0.0074 * std::sin(
deg2rad(m - mprime))
352 + 0.0004 * std::sin(
deg2rad(2 * f + m))
353 - 0.0004 * std::sin(
deg2rad(2 * f - m))
354 - 0.0006 * std::sin(
deg2rad(2 * f + mprime))
355 + 0.0010 * std::sin(
deg2rad(2 * f - mprime))
356 + 0.0005 * std::sin(
deg2rad(m + 2 * mprime));
360 if (std::abs(phase_fraction - 0.25) < 0.01 || std::abs(phase_fraction - 0.75) < 0.01) {
361 pt += (0.1721 - 0.0004 * t) * std::sin(
deg2rad(m))
362 + 0.0021 * std::sin(
deg2rad(2 * m))
363 - 0.6280 * std::sin(
deg2rad(mprime))
364 + 0.0089 * std::sin(
deg2rad(2 * mprime))
365 - 0.0004 * std::sin(
deg2rad(3 * mprime))
366 + 0.0079 * std::sin(
deg2rad(2 * f))
367 - 0.0119 * std::sin(
deg2rad(m + mprime))
368 - 0.0047 * std::sin(
deg2rad(m - mprime))
369 + 0.0003 * std::sin(
deg2rad(2 * f + m))
370 - 0.0004 * std::sin(
deg2rad(2 * f - m))
371 - 0.0006 * std::sin(
deg2rad(2 * f + mprime))
372 + 0.0021 * std::sin(
deg2rad(2 * f - mprime))
373 + 0.0003 * std::sin(
deg2rad(m + 2 * mprime))
374 + 0.0004 * std::sin(
deg2rad(m - 2 * mprime))
375 - 0.0003 * std::sin(
deg2rad(2 * m + mprime));
377 if (phase_fraction < 0.5) {
378 pt += 0.0028 - 0.0004 * std::cos(
deg2rad(m)) + 0.0003 * std::cos(
deg2rad(mprime));
380 pt += -0.0028 + 0.0004 * std::cos(
deg2rad(m)) - 0.0003 * std::cos(
deg2rad(mprime));
389 static bool is_within_window(
double unix_utc_s,
double previous_instant,
double next_instant,
double window_seconds)
noexcept {
390 const double prev_delta = std::abs(unix_utc_s - previous_instant);
391 const double next_delta = std::abs(unix_utc_s - next_instant);
392 return (prev_delta <= window_seconds) || (next_delta <= window_seconds);
Moon phase calculator (geocentric approximation).
static constexpr double kMInc
static constexpr double kSynMonth
bool is_new_moon_window(double unix_utc_s, double window_seconds=kDefaultQuarterWindow_s) const noexcept
Check whether timestamp is inside a window around new moon.
double mean_phase_jd(double julian_day, double lunation_index) const noexcept
static constexpr double kMNode
static constexpr double kMmLong
static bool is_within_window(double unix_utc_s, double previous_instant, double next_instant, double window_seconds) noexcept
static constexpr double kSunSmax
static constexpr double kElongp
static constexpr double kPi
bool is_first_quarter_window(double unix_utc_s, double window_seconds=kDefaultQuarterWindow_s) const noexcept
Check whether timestamp is inside a window around first quarter.
static double julian_day_from_unix_seconds(double unix_utc_s) noexcept
MoonPhaseResult compute(double unix_utc_s) const noexcept
Compute full set of Moon phase parameters for given UTC timestamp.
static constexpr double kElonge
MoonQuarterInstants quarter_instants_unix(double unix_utc_s) const noexcept
Quarter instants around the provided timestamp as a structured result (Unix UTC seconds).
static constexpr double kMsMax
static constexpr double kMmLongp
static double jd_to_unix_seconds(double julian_day) noexcept
static constexpr double kEccent
static constexpr double kMParallax
quarters_unix_s_t quarter_times(double unix_utc_s) const noexcept
Compatibility wrapper returning quarter instants as Unix UTC seconds.
quarters_unix_s_t quarter_times_unix(double unix_utc_s) const noexcept
Compute quarter/new/full instants around given timestamp.
static double fix_angle(double a) noexcept
static constexpr double kMecc
static double rad2deg(double rad) noexcept
double compute_phase(double unix_utc_s) const noexcept
Compute only phase fraction in [0..1) for given UTC timestamp.
std::array< double, 8 > quarters_unix_s_t
Quarter instants as Unix UTC seconds ({new, firstQ, full, lastQ} for previous and next cycles).
static double deg2rad(double deg) noexcept
static constexpr double kDefaultQuarterWindow_s
Default window around phase events (12h).
static double kepler(double m_deg, double ecc) noexcept
static constexpr double kMAngSiz
static int month_from_unix_seconds(double unix_utc_s) noexcept
static int year_from_unix_seconds(double unix_utc_s) noexcept
static constexpr double kSunAngSiz
static constexpr double kEpochJd
bool is_last_quarter_window(double unix_utc_s, double window_seconds=kDefaultQuarterWindow_s) const noexcept
Check whether timestamp is inside a window around last quarter.
bool is_full_moon_window(double unix_utc_s, double window_seconds=kDefaultQuarterWindow_s) const noexcept
Check whether timestamp is inside a window around full moon.
double true_phase_jd(double lunation_index, double phase_fraction) const noexcept
Conversions involving DateTimeStruct and day boundary helpers.
TIME_SHIELD_CONSTEXPR T year_of(ts_t ts=time_shield::ts())
Get the year from the timestamp.
TIME_SHIELD_CONSTEXPR T month_of_year(ts_t ts) noexcept
Get the month of the year.
int64_t ts_t
Unix timestamp in seconds since 1970‑01‑01T00:00:00Z.
Main namespace for the Time Shield library.
astronomy::MoonPhase MoonPhaseCalculator
Convenience alias for the geocentric Moon phase calculator.
Result of Moon phase computation (geocentric approximation).
double age_deg
Phase angle in degrees (0..360).
double distance_km
Distance to Moon in km (approx).
double diameter_deg
Angular diameter of Moon in degrees (approx).
double phase
Phase fraction in [0..1). 0=new moon, 0.5=full moon.
double phase_sin
sin(phase_angle_rad) helper for continuous signal.
double phase_angle_rad
Phase angle in radians (0..2*pi).
double age_days
Age of the Moon in days since new moon (approx).
double phase_cos
cos(phase_angle_rad) helper for continuous signal.
double illumination
Illuminated fraction in [0..1].
Lunar quarter instants (Unix UTC seconds, floating).
double previous_new_unix_s
Previous new moon instant (Unix UTC seconds, double).
double previous_first_quarter_unix_s
Previous first quarter instant (Unix UTC seconds, double).
double previous_full_unix_s
Previous full moon instant (Unix UTC seconds, double).
double next_last_quarter_unix_s
Next last quarter instant (Unix UTC seconds, double).
double next_new_unix_s
Next new moon instant (Unix UTC seconds, double).
double previous_last_quarter_unix_s
Previous last quarter instant (Unix UTC seconds, double).
double next_first_quarter_unix_s
Next first quarter instant (Unix UTC seconds, double).
double next_full_unix_s
Next full moon instant (Unix UTC seconds, double).
Type definitions for time-related units and formats.