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 <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) {
|
||||||
|
|
|
@ -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
81
main.c
|
@ -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) {
|
||||||
|
|
Loading…
Reference in New Issue