Use sun trajectory
parent
4d339c70d2
commit
a89ae834be
80
color_math.c
80
color_math.c
|
@ -1,13 +1,64 @@
|
|||
#define _USE_MATH_DEFINES
|
||||
#include <math.h>
|
||||
#include <errno.h>
|
||||
#include <time.h>
|
||||
#include "color_math.h"
|
||||
|
||||
static int is_leap(int year) {
|
||||
return (year % 4 == 0 && year % 100 != 0) || year % 400 == 0;
|
||||
}
|
||||
|
||||
static int days_in_year(int year) {
|
||||
return is_leap(year) ? 366 : 365;
|
||||
}
|
||||
|
||||
static double radians(double degrees) {
|
||||
return degrees * M_PI / 180.0;
|
||||
}
|
||||
|
||||
static double degrees(double radians) {
|
||||
return radians * 180.0 / M_PI;
|
||||
}
|
||||
|
||||
static double year_radian(struct tm *tm) {
|
||||
// https://www.esrl.noaa.gov/gmd/grad/solcalc/solareqns.PDF
|
||||
return 2 * M_PI / days_in_year(tm->tm_year) * (tm->tm_yday - 1 + (tm->tm_hour - 12)/24);
|
||||
}
|
||||
|
||||
void sun(struct tm *tm, double longitude, double latitude, time_t *sunrise, time_t *sunset) {
|
||||
double year_rad = year_radian(tm);
|
||||
// https://www.esrl.noaa.gov/gmd/grad/solcalc/solareqns.PDF
|
||||
double eqtime = 229.18 * (0.000075 +
|
||||
0.001868 * cos(year_rad) -
|
||||
0.032077 * sin(year_rad) -
|
||||
0.014615 * cos(2*year_rad) -
|
||||
0.040849 * sin(2*year_rad));
|
||||
double decl = 0.006918 -
|
||||
0.399912 * cos(year_rad) +
|
||||
0.070257 * sin(year_rad) -
|
||||
0.006758 * cos(2*year_rad) +
|
||||
0.000907 * sin(2*year_rad) -
|
||||
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)) -
|
||||
tan(radians(latitude)) * tan(decl)));
|
||||
*sunrise = (720 - 4 * (longitude + fabs(ha)) - eqtime) * 60;
|
||||
*sunset = (720 - 4 * (longitude - fabs(ha)) - eqtime) * 60;
|
||||
}
|
||||
|
||||
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) {
|
||||
*x = 0.244063 + (0.09911e3/temp) + (2.9678e6/pow(temp, 2)) - (4.6070e9/pow(temp, 3));
|
||||
*x = 0.244063 +
|
||||
0.09911e3 / temp +
|
||||
2.9678e6 / pow(temp, 2) -
|
||||
4.6070e9 / pow(temp, 3);
|
||||
} else if (temp > 7000 && temp <= 25000) {
|
||||
*x = 0.237040 + (0.24748e3/temp) + (1.9018e6/pow(temp, 2)) - (2.0064e9/pow(temp, 3));
|
||||
*x = 0.237040 +
|
||||
0.24748e3 / temp +
|
||||
1.9018e6 / pow(temp, 2) -
|
||||
2.0064e9 / pow(temp, 3);
|
||||
} else {
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
|
@ -18,15 +69,30 @@ static int illuminant_d(int temp, double *x, double *y) {
|
|||
|
||||
static int planckian_locus(int temp, double *x, double *y) {
|
||||
if (temp >= 1667 && temp <= 4000) {
|
||||
*x = (-0.2661239e9/pow(temp, 3)) - (0.2343589e6/pow(temp, 2)) + (0.8776956e3/temp) + 0.179910;
|
||||
*x = -0.2661239e9 / pow(temp, 3) -
|
||||
0.2343589e6 / pow(temp, 2) +
|
||||
0.8776956e3 / temp +
|
||||
0.179910;
|
||||
if (temp <= 2222) {
|
||||
*y = (-1.1064814 * pow(*x, 3)) - (1.34811020 * pow(*x, 2)) + (2.18555832 * (*x)) - 0.20219683;
|
||||
*y = -1.1064814 * pow(*x, 3) -
|
||||
1.34811020 * pow(*x, 2) +
|
||||
2.18555832 * (*x) -
|
||||
0.20219683;
|
||||
} else {
|
||||
*y = (-0.9549476 * pow(*x, 3)) - (1.37418593 * pow(*x, 2)) + (2.09137015 * (*x)) - 0.16748867;
|
||||
*y = -0.9549476 * pow(*x, 3) -
|
||||
1.37418593 * pow(*x, 2) +
|
||||
2.09137015 * (*x) -
|
||||
0.16748867;
|
||||
}
|
||||
} else if (temp > 4000 && temp < 25000) {
|
||||
*x = (-3.0258469e9/pow(temp, 3)) + (2.1070379e6/pow(temp, 2)) + (0.2226347e3/temp) + 0.240390;
|
||||
*y = (3.0817580 * pow(*x, 3)) - (5.87338670 * pow(*x, 2)) + (3.75112997 * (*x)) - 0.37001483;
|
||||
*x = -3.0258469e9 / pow(temp, 3) +
|
||||
2.1070379e6 / pow(temp, 2) +
|
||||
0.2226347e3 / temp +
|
||||
0.240390;
|
||||
*y = 3.0817580 * pow(*x, 3) -
|
||||
5.87338670 * pow(*x, 2) +
|
||||
3.75112997 * (*x) -
|
||||
0.37001483;
|
||||
} else {
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
|
|
|
@ -1,6 +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);
|
||||
double clamp(double value);
|
||||
void calc_whitepoint(int temp, double *rw, double *gw, double *bw);
|
||||
|
||||
|
|
125
main.c
125
main.c
|
@ -17,23 +17,34 @@
|
|||
|
||||
enum state {
|
||||
HIGH_TEMP,
|
||||
ANIMATING_TO_LOW,
|
||||
LOW_TEMP,
|
||||
ANIMATING_TO_HIGH,
|
||||
ANIMATING_TO_LOW,
|
||||
};
|
||||
|
||||
static char *state_names[] = {
|
||||
"high temperature",
|
||||
"animating to low temperature",
|
||||
"low temperature",
|
||||
"animating to high temperature",
|
||||
NULL
|
||||
};
|
||||
|
||||
struct context {
|
||||
double gamma;
|
||||
time_t start_time;
|
||||
time_t stop_time;
|
||||
|
||||
int high_temp;
|
||||
int low_temp;
|
||||
int duration;
|
||||
double longitude;
|
||||
double latitude;
|
||||
|
||||
time_t start_time;
|
||||
time_t stop_time;
|
||||
int cur_temp;
|
||||
bool new_output;
|
||||
enum state state;
|
||||
time_t animation_start;
|
||||
bool new_output;
|
||||
|
||||
struct wl_list outputs;
|
||||
};
|
||||
|
@ -195,35 +206,42 @@ static void set_temperature(struct context *ctx) {
|
|||
}
|
||||
}
|
||||
|
||||
static void recalc_stops(struct context *ctx, time_t now) {
|
||||
time_t day = now - (now % 86400);
|
||||
if (day < ctx->stop_time) {
|
||||
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;
|
||||
|
||||
struct tm sunrise, sunset;
|
||||
localtime_r(&ctx->start_time, &sunrise);
|
||||
localtime_r(&ctx->stop_time, &sunset);
|
||||
fprintf(stderr, "cacluated new sun trajectory: sunrise %02d:%02d, sunset %02d:%02d\n",
|
||||
sunrise.tm_hour, sunrise.tm_min,
|
||||
sunset.tm_hour, sunset.tm_min);
|
||||
}
|
||||
|
||||
static void update_temperature(struct context *ctx) {
|
||||
time_t now = time(NULL);
|
||||
time_t t = now % 86400;
|
||||
int temp, temp_pos;
|
||||
double time_pos;
|
||||
|
||||
recalc_stops(ctx, now);
|
||||
|
||||
switch (ctx->state) {
|
||||
case HIGH_TEMP:
|
||||
if (t > ctx->stop_time || t < ctx->start_time) {
|
||||
ctx->state = ANIMATING_TO_LOW;
|
||||
ctx->animation_start = now;
|
||||
}
|
||||
if (now < ctx->stop_time && now > ctx->start_time) {
|
||||
temp = ctx->high_temp;
|
||||
break;
|
||||
case LOW_TEMP:
|
||||
if (t > ctx->start_time || t < ctx->stop_time) {
|
||||
ctx->state = ANIMATING_TO_HIGH;
|
||||
ctx->animation_start = now;
|
||||
}
|
||||
temp = ctx->low_temp;
|
||||
break;
|
||||
case ANIMATING_TO_HIGH:
|
||||
if (now > ctx->animation_start + ctx->duration) {
|
||||
ctx->state = HIGH_TEMP;
|
||||
}
|
||||
time_pos = clamp(((double)now - (double)ctx->animation_start) / (double)ctx->duration);
|
||||
temp_pos = (double)(ctx->high_temp - ctx->low_temp) * time_pos;
|
||||
temp = ctx->low_temp + temp_pos;
|
||||
break;
|
||||
ctx->state = ANIMATING_TO_LOW;
|
||||
ctx->animation_start = ctx->stop_time;
|
||||
// fallthrough
|
||||
case ANIMATING_TO_LOW:
|
||||
if (now > ctx->animation_start + ctx->duration) {
|
||||
ctx->state = LOW_TEMP;
|
||||
|
@ -232,29 +250,46 @@ static void update_temperature(struct context *ctx) {
|
|||
temp_pos = (double)(ctx->high_temp - ctx->low_temp) * time_pos;
|
||||
temp = ctx->high_temp - temp_pos;
|
||||
break;
|
||||
case LOW_TEMP:
|
||||
if (now > ctx->stop_time || now < ctx->start_time) {
|
||||
temp = ctx->low_temp;
|
||||
break;
|
||||
}
|
||||
ctx->state = ANIMATING_TO_HIGH;
|
||||
ctx->animation_start = now;
|
||||
// fallthrough
|
||||
case ANIMATING_TO_HIGH:
|
||||
if (now > ctx->animation_start + ctx->duration) {
|
||||
ctx->state = HIGH_TEMP;
|
||||
}
|
||||
time_pos = clamp(((double)now - (double)ctx->animation_start) / (double)ctx->duration);
|
||||
temp_pos = (double)(ctx->high_temp - ctx->low_temp) * time_pos;
|
||||
temp = ctx->low_temp + temp_pos;
|
||||
break;
|
||||
}
|
||||
|
||||
if (temp != ctx->cur_temp || ctx->new_output) {
|
||||
fprintf(stderr, "state: %d, temp: %d, cur: %d\n", ctx->state, temp, ctx->cur_temp);
|
||||
fprintf(stderr, "state: %s, temp: %d\n", state_names[ctx->state], temp);
|
||||
ctx->cur_temp = temp;
|
||||
ctx->new_output = false;
|
||||
set_temperature(ctx);
|
||||
}
|
||||
}
|
||||
|
||||
static int increments(struct context *ctx) {
|
||||
int diff = ctx->high_temp - ctx->low_temp;
|
||||
diff /= 50;
|
||||
static int increments(struct context *ctx, int to) {
|
||||
int diff = fabs(ctx->cur_temp - to) / 50;
|
||||
int time = (ctx->duration * 1000) / diff;
|
||||
return time;
|
||||
return time > 600000 ? 600000 : time;
|
||||
}
|
||||
|
||||
static int time_to_next_event(struct context *ctx) {
|
||||
switch (ctx->state) {
|
||||
case ANIMATING_TO_HIGH:
|
||||
return increments(ctx, ctx->high_temp);
|
||||
case ANIMATING_TO_LOW:
|
||||
return increments(ctx);
|
||||
return increments(ctx, ctx->low_temp);
|
||||
default:
|
||||
return 300000;
|
||||
return 600000;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -309,9 +344,9 @@ static int display_dispatch_with_timeout(struct wl_display *display, int timeout
|
|||
static const char usage[] = "usage: %s [options]\n"
|
||||
" -h show this help message\n"
|
||||
" -T <value> set high temperature (default: 6500)\n"
|
||||
" -t <value> set low temperature (default: 3500)\n"
|
||||
" -S <value> set ramp up time (default: 6:00)\n"
|
||||
" -s <value> set ramp down time (default: 18:00)\n"
|
||||
" -t <value> set low temperature (default: 4000)\n"
|
||||
" -l <value> set latitude\n"
|
||||
" -L <value> set longitude\n"
|
||||
" -d <value> set ramping duration in minutes (default: 30)\n"
|
||||
" -g <value> set gamma (default: 1)\n";
|
||||
|
||||
|
@ -322,21 +357,15 @@ int main(int argc, char *argv[]) {
|
|||
// Initialize defaults
|
||||
struct context ctx = {
|
||||
.gamma = 1.0,
|
||||
.start_time = 6 * 60 * 60,
|
||||
.stop_time = 18 * 60 * 60,
|
||||
.high_temp = 6500,
|
||||
.low_temp = 3500,
|
||||
.low_temp = 4000,
|
||||
.duration = 30 * 60,
|
||||
.state = HIGH_TEMP,
|
||||
};
|
||||
wl_list_init(&ctx.outputs);
|
||||
|
||||
int opt;
|
||||
time_t now = time(NULL);
|
||||
struct tm tm = { 0 };
|
||||
struct tm current = { 0 };
|
||||
localtime_r(&now, ¤t);
|
||||
while ((opt = getopt(argc, argv, "hT:t:S:s:g:d:")) != -1) {
|
||||
while ((opt = getopt(argc, argv, "hT:t:g:d:l:L:")) != -1) {
|
||||
switch (opt) {
|
||||
case 'T':
|
||||
ctx.high_temp = strtol(optarg, NULL, 10);
|
||||
|
@ -344,17 +373,11 @@ int main(int argc, char *argv[]) {
|
|||
case 't':
|
||||
ctx.low_temp = strtol(optarg, NULL, 10);
|
||||
break;
|
||||
case 'S':
|
||||
memcpy(&tm, ¤t, sizeof tm);
|
||||
if (strptime(optarg, "%H:%M", &tm) != NULL) {
|
||||
ctx.start_time = mktime(&tm) % 86400;
|
||||
}
|
||||
case 'l':
|
||||
ctx.latitude = strtod(optarg, NULL);
|
||||
break;
|
||||
case 's':
|
||||
memcpy(&tm, ¤t, sizeof tm);
|
||||
if (strptime(optarg, "%H:%M", &tm) != NULL) {
|
||||
ctx.stop_time = mktime(&tm) % 86400;
|
||||
}
|
||||
case 'L':
|
||||
ctx.longitude = strtod(optarg, NULL);
|
||||
break;
|
||||
case 'd':
|
||||
ctx.duration = strtod(optarg, NULL) * 60;
|
||||
|
|
Loading…
Reference in New Issue