Calculate dawn and dusk times
parent
ddbb8f9912
commit
83d90d2bea
13
color_math.c
13
color_math.c
|
@ -4,6 +4,10 @@
|
|||
#include <time.h>
|
||||
#include "color_math.h"
|
||||
|
||||
#define SOLAR_HORIZON 90.833
|
||||
#define SOLAR_START_CIVIL_TWILIGHT 6.0
|
||||
#define SOLAR_END_CIVIL_TWILIGHT 3.0
|
||||
|
||||
static int is_leap(int year) {
|
||||
return (year % 4 == 0 && year % 100 != 0) || year % 400 == 0;
|
||||
}
|
||||
|
@ -20,7 +24,7 @@ static double degrees(double radians) {
|
|||
return radians * 180.0 / M_PI;
|
||||
}
|
||||
|
||||
void sun(struct tm *tm, double longitude, double latitude, time_t *sunrise, time_t *sunset) {
|
||||
static void sun_angle(struct tm *tm, double longitude, double latitude, time_t *sunrise, time_t *sunset, double angle) {
|
||||
// https://www.esrl.noaa.gov/gmd/grad/solcalc/solareqns.PDF
|
||||
double year_rad = 2 * M_PI /
|
||||
days_in_year(tm->tm_year) *
|
||||
|
@ -38,12 +42,17 @@ void sun(struct tm *tm, double longitude, double latitude, time_t *sunrise, time
|
|||
0.002697 * cos(3*year_rad) +
|
||||
0.00148 * sin(3*year_rad);
|
||||
double ha = degrees(acos(
|
||||
cos(radians(90.833)) / (cos(radians(latitude)) * cos(decl)) -
|
||||
cos(radians(angle)) / (cos(radians(latitude)) * cos(decl)) -
|
||||
tan(radians(latitude)) * tan(decl)));
|
||||
*sunrise = (720 - 4 * (longitude + fabs(ha)) - eqtime) * 60;
|
||||
*sunset = (720 - 4 * (longitude - fabs(ha)) - eqtime) * 60;
|
||||
}
|
||||
|
||||
void sun(struct tm *tm, double longitude, double latitude, time_t *dawn, time_t *sunrise, time_t *sunset, time_t *dusk) {
|
||||
sun_angle(tm, longitude, latitude, dawn, dusk, SOLAR_HORIZON + SOLAR_START_CIVIL_TWILIGHT);
|
||||
sun_angle(tm, longitude, latitude, sunrise, sunset, SOLAR_HORIZON + SOLAR_END_CIVIL_TWILIGHT);
|
||||
}
|
||||
|
||||
static int illuminant_d(int temp, double *x, double *y) {
|
||||
// https://en.wikipedia.org/wiki/Standard_illuminant#Illuminant_series_D
|
||||
if (temp >= 4000 && temp <= 7000) {
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
#ifndef _COLOR_MATH_H
|
||||
#define _COLOR_MATH_H
|
||||
|
||||
void sun(struct tm *tm, double longitude, double latitude, time_t *sunrise, time_t *sunset);
|
||||
void sun(struct tm *tm, double longitude, double latitude, time_t *dawn, time_t *sunrise, time_t *sunset, time_t *dusk);
|
||||
double clamp(double value);
|
||||
void calc_whitepoint(int temp, double *rw, double *gw, double *bw);
|
||||
|
||||
|
|
81
main.c
81
main.c
|
@ -76,8 +76,10 @@ struct context {
|
|||
double longitude;
|
||||
double latitude;
|
||||
|
||||
time_t start_time;
|
||||
time_t stop_time;
|
||||
time_t dawn;
|
||||
time_t sunrise;
|
||||
time_t sunset;
|
||||
time_t dusk;
|
||||
int cur_temp;
|
||||
enum state state;
|
||||
bool new_output;
|
||||
|
@ -269,68 +271,75 @@ static void set_temperature(struct context *ctx) {
|
|||
|
||||
static void recalc_stops(struct context *ctx, time_t now) {
|
||||
time_t day = now - (now % 86400);
|
||||
time_t true_end = ctx->stop_time + ctx->duration;
|
||||
if (ctx->stop_time == 0) {
|
||||
if (ctx->dusk == 0) {
|
||||
// First calculation
|
||||
} else if (now > true_end) {
|
||||
} else if (now > ctx->dusk) {
|
||||
day += 86400;
|
||||
} else if (day < true_end) {
|
||||
} else if (day < ctx->dusk) {
|
||||
return;
|
||||
}
|
||||
|
||||
struct tm tm = { 0 };
|
||||
gmtime_r(&now, &tm);
|
||||
sun(&tm, ctx->longitude, ctx->latitude, &ctx->start_time, &ctx->stop_time);
|
||||
ctx->start_time += day;
|
||||
ctx->stop_time += day;
|
||||
sun(&tm, ctx->longitude, ctx->latitude, &ctx->dawn, &ctx->sunrise, &ctx->sunset, &ctx->dusk);
|
||||
if (ctx->duration != -1) {
|
||||
ctx->dawn = ctx->sunrise - ctx->duration;
|
||||
ctx->dusk = ctx->sunset + ctx->duration;
|
||||
}
|
||||
ctx->dawn += day;
|
||||
ctx->sunrise += day;
|
||||
ctx->sunset += day;
|
||||
ctx->dusk += day;
|
||||
|
||||
struct tm sunrise, sunset;
|
||||
localtime_r(&ctx->start_time, &sunrise);
|
||||
localtime_r(&ctx->stop_time, &sunset);
|
||||
fprintf(stderr, "calculated new sun trajectory: sunrise %02d:%02d, sunset %02d:%02d\n",
|
||||
struct tm dawn, sunrise, sunset, dusk;
|
||||
localtime_r(&ctx->dawn, &dawn);
|
||||
localtime_r(&ctx->sunrise, &sunrise);
|
||||
localtime_r(&ctx->sunset, &sunset);
|
||||
localtime_r(&ctx->dusk, &dusk);
|
||||
fprintf(stderr, "calculated new sun trajectory: dawn %02d:%02d, sunrise %02d:%02d, sunset %02d:%02d, dusk %02d:%02d\n",
|
||||
dawn.tm_hour, dusk.tm_min,
|
||||
sunrise.tm_hour, sunrise.tm_min,
|
||||
sunset.tm_hour, sunset.tm_min);
|
||||
sunset.tm_hour, sunset.tm_min,
|
||||
dusk.tm_hour, dusk.tm_min);
|
||||
}
|
||||
|
||||
ctx->stop_time -= ctx->duration;
|
||||
static int interpolate_temperature(time_t now, time_t start, time_t stop, int temp_start, int temp_stop) {
|
||||
double time_pos = clamp((double)(now - start) / (double)(stop - start));
|
||||
int temp_pos = (double)(temp_stop - temp_start) * time_pos;
|
||||
return temp_start + temp_pos;
|
||||
}
|
||||
|
||||
static void update_temperature(struct context *ctx, time_t now) {
|
||||
int temp = 0, temp_pos;
|
||||
double time_pos;
|
||||
|
||||
recalc_stops(ctx, now);
|
||||
|
||||
int temp = 0;
|
||||
enum state old_state = ctx->state;
|
||||
switch (ctx->state) {
|
||||
start:
|
||||
case HIGH_TEMP:
|
||||
if (now <= ctx->stop_time && now > ctx->start_time + ctx->duration) {
|
||||
if (now <= ctx->sunset && now > ctx->sunrise) {
|
||||
temp = ctx->high_temp;
|
||||
break;
|
||||
}
|
||||
ctx->state = ANIMATING_TO_LOW;
|
||||
// fallthrough
|
||||
case ANIMATING_TO_LOW:
|
||||
if (now > ctx->start_time && now <= ctx->stop_time + ctx->duration) {
|
||||
time_pos = clamp(((double)now - (double)ctx->stop_time) / (double)ctx->duration);
|
||||
temp_pos = (double)(ctx->high_temp - ctx->low_temp) * time_pos;
|
||||
temp = ctx->high_temp - temp_pos;
|
||||
if (now > ctx->dawn && now <= ctx->dusk) {
|
||||
temp = interpolate_temperature(now, ctx->sunset, ctx->dusk, ctx->high_temp, ctx->low_temp);
|
||||
break;
|
||||
}
|
||||
ctx->state = LOW_TEMP;
|
||||
// fallthrough
|
||||
case LOW_TEMP:
|
||||
if (now > ctx->stop_time + ctx->duration || now <= ctx->start_time) {
|
||||
if (now > ctx->dusk || now <= ctx->dawn) {
|
||||
temp = ctx->low_temp;
|
||||
break;
|
||||
}
|
||||
ctx->state = ANIMATING_TO_HIGH;
|
||||
// fallthrough
|
||||
case ANIMATING_TO_HIGH:
|
||||
if (now <= ctx->start_time + ctx->duration) {
|
||||
time_pos = clamp(((double)now - (double)ctx->start_time) / (double)ctx->duration);
|
||||
temp_pos = (double)(ctx->high_temp - ctx->low_temp) * time_pos;
|
||||
temp = ctx->low_temp + temp_pos;
|
||||
if (now <= ctx->sunrise) {
|
||||
temp = interpolate_temperature(now, ctx->dawn, ctx->sunrise, ctx->low_temp, ctx->high_temp);
|
||||
break;
|
||||
}
|
||||
ctx->state = HIGH_TEMP;
|
||||
|
@ -348,10 +357,9 @@ start:
|
|||
}
|
||||
}
|
||||
|
||||
static int increments(struct context *ctx, int from, int to) {
|
||||
int temp_diff = to - from;
|
||||
static int increments(int temp_diff, int duration) {
|
||||
assert(temp_diff > 0);
|
||||
int time = ctx->duration * 25000 / temp_diff;
|
||||
int time = duration * 25000 / temp_diff;
|
||||
return time > LONG_SLEEP_MS ? LONG_SLEEP_MS : time;
|
||||
}
|
||||
|
||||
|
@ -359,17 +367,18 @@ static int time_to_next_event(struct context *ctx, time_t now) {
|
|||
time_t deadline;
|
||||
switch (ctx->state) {
|
||||
case HIGH_TEMP:
|
||||
deadline = ctx->stop_time;
|
||||
deadline = ctx->sunset;
|
||||
break;
|
||||
case LOW_TEMP:
|
||||
deadline = ctx->start_time;
|
||||
deadline = ctx->dawn;
|
||||
if (deadline < now) {
|
||||
deadline = ((deadline / 86400 + 1) * 86400);
|
||||
}
|
||||
break;
|
||||
case ANIMATING_TO_HIGH:
|
||||
return increments(ctx->high_temp - ctx->low_temp, ctx->sunrise - ctx->dawn);
|
||||
case ANIMATING_TO_LOW:
|
||||
return increments(ctx, ctx->low_temp, ctx->high_temp);
|
||||
return increments(ctx->high_temp - ctx->low_temp, ctx->dusk - ctx->sunset);
|
||||
default:
|
||||
return LONG_SLEEP_MS;
|
||||
}
|
||||
|
@ -447,7 +456,7 @@ int main(int argc, char *argv[]) {
|
|||
.gamma = 1.0,
|
||||
.high_temp = 6500,
|
||||
.low_temp = 4000,
|
||||
.duration = 3600,
|
||||
.duration = -1,
|
||||
.state = HIGH_TEMP,
|
||||
};
|
||||
wl_list_init(&ctx.outputs);
|
||||
|
@ -468,6 +477,7 @@ int main(int argc, char *argv[]) {
|
|||
ctx.longitude = strtod(optarg, NULL);
|
||||
break;
|
||||
case 'd':
|
||||
fprintf(stderr, "using animation duration override\n");
|
||||
ctx.duration = strtod(optarg, NULL) * 60;
|
||||
break;
|
||||
case 'g':
|
||||
|
@ -508,7 +518,6 @@ int main(int argc, char *argv[]) {
|
|||
}
|
||||
wl_display_roundtrip(display);
|
||||
|
||||
|
||||
time_t now = get_time_sec(NULL);
|
||||
update_temperature(&ctx, now);
|
||||
while (display_dispatch_with_timeout(display, wait_adjust(time_to_next_event(&ctx, now))) != -1) {
|
||||
|
|
Loading…
Reference in New Issue