Calculate dawn and dusk times

master
Kenny Levinsen 2020-10-04 15:27:46 +02:00
parent ddbb8f9912
commit 83d90d2bea
3 changed files with 57 additions and 39 deletions

View File

@ -4,6 +4,10 @@
#include <time.h> #include <time.h>
#include "color_math.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) { static int is_leap(int year) {
return (year % 4 == 0 && year % 100 != 0) || year % 400 == 0; 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; 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 // https://www.esrl.noaa.gov/gmd/grad/solcalc/solareqns.PDF
double year_rad = 2 * M_PI / double year_rad = 2 * M_PI /
days_in_year(tm->tm_year) * 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.002697 * cos(3*year_rad) +
0.00148 * sin(3*year_rad); 0.00148 * sin(3*year_rad);
double ha = degrees(acos( 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))); tan(radians(latitude)) * tan(decl)));
*sunrise = (720 - 4 * (longitude + fabs(ha)) - eqtime) * 60; *sunrise = (720 - 4 * (longitude + fabs(ha)) - eqtime) * 60;
*sunset = (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) { static int illuminant_d(int temp, double *x, double *y) {
// https://en.wikipedia.org/wiki/Standard_illuminant#Illuminant_series_D // https://en.wikipedia.org/wiki/Standard_illuminant#Illuminant_series_D
if (temp >= 4000 && temp <= 7000) { if (temp >= 4000 && temp <= 7000) {

View File

@ -1,7 +1,7 @@
#ifndef _COLOR_MATH_H #ifndef _COLOR_MATH_H
#define _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); double clamp(double value);
void calc_whitepoint(int temp, double *rw, double *gw, double *bw); void calc_whitepoint(int temp, double *rw, double *gw, double *bw);

81
main.c
View File

@ -76,8 +76,10 @@ struct context {
double longitude; double longitude;
double latitude; double latitude;
time_t start_time; time_t dawn;
time_t stop_time; time_t sunrise;
time_t sunset;
time_t dusk;
int cur_temp; int cur_temp;
enum state state; enum state state;
bool new_output; bool new_output;
@ -269,68 +271,75 @@ static void set_temperature(struct context *ctx) {
static void recalc_stops(struct context *ctx, time_t now) { static void recalc_stops(struct context *ctx, time_t now) {
time_t day = now - (now % 86400); time_t day = now - (now % 86400);
time_t true_end = ctx->stop_time + ctx->duration; if (ctx->dusk == 0) {
if (ctx->stop_time == 0) {
// First calculation // First calculation
} else if (now > true_end) { } else if (now > ctx->dusk) {
day += 86400; day += 86400;
} else if (day < true_end) { } else if (day < ctx->dusk) {
return; return;
} }
struct tm tm = { 0 }; struct tm tm = { 0 };
gmtime_r(&now, &tm); gmtime_r(&now, &tm);
sun(&tm, ctx->longitude, ctx->latitude, &ctx->start_time, &ctx->stop_time); sun(&tm, ctx->longitude, ctx->latitude, &ctx->dawn, &ctx->sunrise, &ctx->sunset, &ctx->dusk);
ctx->start_time += day; if (ctx->duration != -1) {
ctx->stop_time += day; 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; struct tm dawn, sunrise, sunset, dusk;
localtime_r(&ctx->start_time, &sunrise); localtime_r(&ctx->dawn, &dawn);
localtime_r(&ctx->stop_time, &sunset); localtime_r(&ctx->sunrise, &sunrise);
fprintf(stderr, "calculated new sun trajectory: sunrise %02d:%02d, sunset %02d:%02d\n", 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, 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) { static void update_temperature(struct context *ctx, time_t now) {
int temp = 0, temp_pos;
double time_pos;
recalc_stops(ctx, now); recalc_stops(ctx, now);
int temp = 0;
enum state old_state = ctx->state; enum state old_state = ctx->state;
switch (ctx->state) { switch (ctx->state) {
start: start:
case HIGH_TEMP: 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; temp = ctx->high_temp;
break; break;
} }
ctx->state = ANIMATING_TO_LOW; ctx->state = ANIMATING_TO_LOW;
// fallthrough // fallthrough
case ANIMATING_TO_LOW: case ANIMATING_TO_LOW:
if (now > ctx->start_time && now <= ctx->stop_time + ctx->duration) { if (now > ctx->dawn && now <= ctx->dusk) {
time_pos = clamp(((double)now - (double)ctx->stop_time) / (double)ctx->duration); temp = interpolate_temperature(now, ctx->sunset, ctx->dusk, ctx->high_temp, ctx->low_temp);
temp_pos = (double)(ctx->high_temp - ctx->low_temp) * time_pos;
temp = ctx->high_temp - temp_pos;
break; break;
} }
ctx->state = LOW_TEMP; ctx->state = LOW_TEMP;
// fallthrough // fallthrough
case LOW_TEMP: 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; temp = ctx->low_temp;
break; break;
} }
ctx->state = ANIMATING_TO_HIGH; ctx->state = ANIMATING_TO_HIGH;
// fallthrough // fallthrough
case ANIMATING_TO_HIGH: case ANIMATING_TO_HIGH:
if (now <= ctx->start_time + ctx->duration) { if (now <= ctx->sunrise) {
time_pos = clamp(((double)now - (double)ctx->start_time) / (double)ctx->duration); temp = interpolate_temperature(now, ctx->dawn, ctx->sunrise, ctx->low_temp, ctx->high_temp);
temp_pos = (double)(ctx->high_temp - ctx->low_temp) * time_pos;
temp = ctx->low_temp + temp_pos;
break; break;
} }
ctx->state = HIGH_TEMP; ctx->state = HIGH_TEMP;
@ -348,10 +357,9 @@ start:
} }
} }
static int increments(struct context *ctx, int from, int to) { static int increments(int temp_diff, int duration) {
int temp_diff = to - from;
assert(temp_diff > 0); 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; 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; time_t deadline;
switch (ctx->state) { switch (ctx->state) {
case HIGH_TEMP: case HIGH_TEMP:
deadline = ctx->stop_time; deadline = ctx->sunset;
break; break;
case LOW_TEMP: case LOW_TEMP:
deadline = ctx->start_time; deadline = ctx->dawn;
if (deadline < now) { if (deadline < now) {
deadline = ((deadline / 86400 + 1) * 86400); deadline = ((deadline / 86400 + 1) * 86400);
} }
break; break;
case ANIMATING_TO_HIGH: case ANIMATING_TO_HIGH:
return increments(ctx->high_temp - ctx->low_temp, ctx->sunrise - ctx->dawn);
case ANIMATING_TO_LOW: 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: default:
return LONG_SLEEP_MS; return LONG_SLEEP_MS;
} }
@ -447,7 +456,7 @@ int main(int argc, char *argv[]) {
.gamma = 1.0, .gamma = 1.0,
.high_temp = 6500, .high_temp = 6500,
.low_temp = 4000, .low_temp = 4000,
.duration = 3600, .duration = -1,
.state = HIGH_TEMP, .state = HIGH_TEMP,
}; };
wl_list_init(&ctx.outputs); wl_list_init(&ctx.outputs);
@ -468,6 +477,7 @@ int main(int argc, char *argv[]) {
ctx.longitude = strtod(optarg, NULL); ctx.longitude = strtod(optarg, NULL);
break; break;
case 'd': case 'd':
fprintf(stderr, "using animation duration override\n");
ctx.duration = strtod(optarg, NULL) * 60; ctx.duration = strtod(optarg, NULL) * 60;
break; break;
case 'g': case 'g':
@ -508,7 +518,6 @@ int main(int argc, char *argv[]) {
} }
wl_display_roundtrip(display); wl_display_roundtrip(display);
time_t now = get_time_sec(NULL); time_t now = get_time_sec(NULL);
update_temperature(&ctx, now); update_temperature(&ctx, now);
while (display_dispatch_with_timeout(display, wait_adjust(time_to_next_event(&ctx, now))) != -1) { while (display_dispatch_with_timeout(display, wait_adjust(time_to_next_event(&ctx, now))) != -1) {