Move longitude time correction out of calc_sun

calc_sun reported sun trajectory in seconds since the start of a UTC
day, as they would occur for the specified UTC day. The caller would
then add the UTC timestamp for the start of a UTC day to these numbers.
This lead to complications, as e.g. a sunrise in China would be a
negative value, as it occurred in the last UTC day.

Futhermore, the start of a UTC day was used to signal the need for new
sun calculations. This would lead to recalculations to possibly occur
after its results were needed, such as after sunrise when the target
longitude deviated significantly from the prime meridian.

To fix this, we apply longitude time correction to the start of day
calculation. This way recalculation will occur on the start of the
longitude local day, close midnight for the observer.

We also remove the longitude time correction from calc_sun, so that it
simply returning the number of seconds since the start of the local day
of the observer. The caller then adds the start of the longitude local
day to get the final numbers.
master
Kenny Levinsen 2020-10-19 13:18:07 +02:00
parent b43cd9f343
commit b9f0b8e3c0
3 changed files with 24 additions and 17 deletions

View File

@ -43,9 +43,9 @@ static double sun_hour_angle(double latitude, double declination, double target_
tan(latitude) * tan(declination)); tan(latitude) * tan(declination));
} }
static time_t hour_angle_to_time(double longitude, double eqtime, double hour_angle) { static time_t hour_angle_to_time(double hour_angle, double eqtime) {
// https://www.esrl.noaa.gov/gmd/grad/solcalc/solareqns.PDF // https://www.esrl.noaa.gov/gmd/grad/solcalc/solareqns.PDF
return DEGREES((4.0 * M_PI - 4 * (longitude + hour_angle) - eqtime) * 60); return DEGREES((4.0 * M_PI - 4 * hour_angle - eqtime) * 60);
} }
static enum sun_condition condition(double latitude_rad, double sun_declination) { static enum sun_condition condition(double latitude_rad, double sun_declination) {
@ -54,7 +54,7 @@ static enum sun_condition condition(double latitude_rad, double sun_declination)
return sign_lat == sign_decl ? MIDNIGHT_SUN : POLAR_NIGHT; return sign_lat == sign_decl ? MIDNIGHT_SUN : POLAR_NIGHT;
} }
enum sun_condition calc_sun(struct tm *tm, double longitude, double latitude, struct sun *sun) { enum sun_condition calc_sun(struct tm *tm, double latitude, struct sun *sun) {
double orbit_angle = date_orbit_angle(tm); double orbit_angle = date_orbit_angle(tm);
double decl = sun_declination(orbit_angle); double decl = sun_declination(orbit_angle);
double eqtime = equation_of_time(orbit_angle); double eqtime = equation_of_time(orbit_angle);
@ -62,10 +62,10 @@ enum sun_condition calc_sun(struct tm *tm, double longitude, double latitude, st
double ha_twilight = sun_hour_angle(latitude, decl, SOLAR_START_TWILIGHT); double ha_twilight = sun_hour_angle(latitude, decl, SOLAR_START_TWILIGHT);
double ha_daylight = sun_hour_angle(latitude, decl, SOLAR_END_TWILIGHT); double ha_daylight = sun_hour_angle(latitude, decl, SOLAR_END_TWILIGHT);
sun->dawn = hour_angle_to_time(longitude, eqtime, fabs(ha_twilight)); sun->dawn = hour_angle_to_time(fabs(ha_twilight), eqtime);
sun->dusk = hour_angle_to_time(longitude, eqtime, -fabs(ha_twilight)); sun->dusk = hour_angle_to_time(-fabs(ha_twilight), eqtime);
sun->sunrise = hour_angle_to_time(longitude, eqtime, fabs(ha_daylight)); sun->sunrise = hour_angle_to_time(fabs(ha_daylight), eqtime);
sun->sunset = hour_angle_to_time(longitude, eqtime, -fabs(ha_daylight)); sun->sunset = hour_angle_to_time(-fabs(ha_daylight), eqtime);
return isnan(ha_twilight) || isnan(ha_daylight) ? condition(latitude, decl) : NORMAL; return isnan(ha_twilight) || isnan(ha_daylight) ? condition(latitude, decl) : NORMAL;
} }

View File

@ -22,7 +22,7 @@ struct sun {
time_t dusk; time_t dusk;
}; };
enum sun_condition calc_sun(struct tm *tm, double longitude, double latitude, struct sun *sun); enum sun_condition calc_sun(struct tm *tm, double latitude, struct sun *sun);
void calc_whitepoint(int temp, double *rw, double *gw, double *bw); void calc_whitepoint(int temp, double *rw, double *gw, double *bw);
#endif #endif

25
main.c
View File

@ -64,12 +64,16 @@ static inline void adjust_timerspec(struct itimerspec *timerspec) {
} }
#endif #endif
static time_t round_day(time_t now) { static time_t round_day_offset(time_t now, time_t offset) {
return now - (now % 86400); return now - ((now - offset) % 86400);
} }
static time_t tomorrow(time_t now) { static time_t tomorrow(time_t now, time_t offset) {
return round_day(now) + 86400; return round_day_offset(now, offset) + 86400;
}
static time_t longitude_time_offset(double longitude) {
return longitude * 43200 / M_PI;
} }
struct config { struct config {
@ -94,6 +98,8 @@ struct context {
struct config config; struct config config;
struct sun sun; struct sun sun;
double longitude_time_offset;
enum state state; enum state state;
enum sun_condition condition; enum sun_condition condition;
@ -149,7 +155,7 @@ static void print_trajectory(struct context *ctx) {
static int anim_kelvin_step = 25; static int anim_kelvin_step = 25;
static void recalc_stops(struct context *ctx, time_t now) { static void recalc_stops(struct context *ctx, time_t now) {
time_t day = round_day(now); time_t day = round_day_offset(now, -ctx->longitude_time_offset);
time_t last_day = ctx->calc_day; time_t last_day = ctx->calc_day;
if (day == last_day) { if (day == last_day) {
return; return;
@ -159,7 +165,7 @@ static void recalc_stops(struct context *ctx, time_t now) {
struct sun sun; struct sun sun;
struct tm tm = { 0 }; struct tm tm = { 0 };
gmtime_r(&day, &tm); gmtime_r(&day, &tm);
enum sun_condition cond = calc_sun(&tm, ctx->config.longitude, ctx->config.latitude, &sun); enum sun_condition cond = calc_sun(&tm, ctx->config.latitude, &sun);
switch (cond) { switch (cond) {
case NORMAL: case NORMAL:
@ -272,7 +278,7 @@ static time_t get_deadline_normal(const struct context *ctx, time_t now) {
} else if (now < ctx->sun.dusk) { } else if (now < ctx->sun.dusk) {
return now + ctx->dusk_step_time; return now + ctx->dusk_step_time;
} else { } else {
return tomorrow(now); return tomorrow(now, -ctx->longitude_time_offset);
} }
} }
@ -284,7 +290,7 @@ static time_t get_deadline_transition(const struct context *ctx, time_t now) {
} }
// fallthrough // fallthrough
case POLAR_NIGHT: case POLAR_NIGHT:
return tomorrow(now); return tomorrow(now, -ctx->longitude_time_offset);
default: default:
abort(); abort();
} }
@ -300,7 +306,7 @@ static void update_timer(const struct context *ctx, timer_t timer, time_t now) {
deadline = get_deadline_transition(ctx, now); deadline = get_deadline_transition(ctx, now);
break; break;
case STATE_STATIC: case STATE_STATIC:
deadline = tomorrow(now); deadline = tomorrow(now, -ctx->longitude_time_offset);
break; break;
default: default:
abort(); abort();
@ -595,6 +601,7 @@ static int wlrun(struct config cfg) {
struct context ctx = { struct context ctx = {
.sun = { 0 }, .sun = { 0 },
.condition = SUN_CONDITION_LAST, .condition = SUN_CONDITION_LAST,
.longitude_time_offset = longitude_time_offset(cfg.longitude),
.state = STATE_INITIAL, .state = STATE_INITIAL,
.config = cfg, .config = cfg,
}; };