Carlos Une: 1 New module: +sun.ha 4 files changed, 186 insertions(+), 4 deletions(-)
Copy & paste the following snippet into your terminal to import this patchset into git:
curl -s https://lists.sr.ht/~tomterl/public-inbox/patches/41962/mbox | git am -3Learn more about email & git
--- README.org | 8 +-- man/thp.1.scd | 3 + man/thpd.5.scd | 31 +++++++++++ mods/+sun.ha | 148 +++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 186 insertions(+), 4 deletions(-) create mode 100644 mods/+sun.ha diff --git a/README.org b/README.org index 6c8e372..69a7dfc 100644 --- a/README.org +++ b/README.org @@ -32,16 +32,16 @@ one patche applied (as of now): To build thp with all modules included, you need libgit2 and your platforms libgit2-dev equivalent installed, then issued #+BEGIN_SRC sh :exports code -make EXTRA_MODS="project git clock timer shind uptime moon" -make EXTRA_MODS="project git clock timer shind uptime moon" test +make EXTRA_MODS="project git clock timer shind uptime moon sun" +make EXTRA_MODS="project git clock timer shind uptime moon sun" test make PREFIX="${HOME}" install #+END_SRC To build with just the modules you use in your spec, use ~MODS~: #+BEGIN_SRC sh :exports code -make MODS="path host project timer uptime moon" -make MODS="path host project timer uptime moon" test +make MODS="path host project timer uptime moon sun" +make MODS="path host project timer uptime moon sun" test make PREFIX="${HOME}" install #+END_SRC diff --git a/man/thp.1.scd b/man/thp.1.scd index dcdf50c..cd6a11f 100644 --- a/man/thp.1.scd +++ b/man/thp.1.scd @@ -107,6 +107,9 @@ All modules are optionally compiled into the server executable. *moon* displays an icon representing the current moon phase. +*sunrise* and *sunset* display the time of sunrise and sunset, respectively. + The location and time format can be configured. See thpd(5). + # STYLES Available colors are: black, red, green, yellow, blue, magenta, cyan, white, diff --git a/man/thpd.5.scd b/man/thpd.5.scd index e4d9dfd..1673a01 100644 --- a/man/thpd.5.scd +++ b/man/thpd.5.scd @@ -173,6 +173,37 @@ Valid values: "S" and "N". hemisphere="N" ``` +## sun + +Display the time of sunrise and sunset. + +- latitude[=-23.5898764] + The latitude in decimal format. + +- longitude[=-46.6589231] + The longitude in decimal format. + +- sunrise.layout[=🌅%H:%M] +- sunset.layout[=🌇%H:%M] + Format string as understood by date::asformat from the hare standard library. + +- midnight.sun.msg[=Midnight sun] + Display message when the sun stays above the horizon all day. + +- polar.night.msg[=Polar night] + Display message when the sun stays below the horizon all day. + +*example*: + +``` +[sun] +\# Buenos Aires/AR +latitude=-34.603333 +longitude=-58.381667 +sunrise.layout=%H:%M| +sunset.layout=%H:%M| +``` + # SEE ALSO *thpd*(1) *thp*(1) *thp*(5) diff --git a/mods/+sun.ha b/mods/+sun.ha new file mode 100644 index 0000000..ef9626e --- /dev/null +++ b/mods/+sun.ha @@ -0,0 +1,148 @@ +// Author: Carlos Une <une@fastmail.fm> +// Maintainer: Carlos Une <une@fastmail.fm> +// +// SPDX-FileCopyrightText: 2023 Carlos Une <une@fastmail.fm> +// SPDX-License-Identifier: GPL-3.0-or-later +// Reference: https://en.wikipedia.org/wiki/Sunrise_equation +use config; +use env; +use math; +use strconv; +use strings; +use time; +use time::chrono; +use time::date; + +type midnightsun = !void; // Sun doesn't set +type polarnight = !void; // Sun doesn't rise + +@init fn register_sun() void = { + register("sunrise", &mod_sunrise); + register("sunset", &mod_sunset); +}; + +fn readconfig() (f64, f64, str, str, str, str) = ( + strconv::stof64(config::setting("sun.latitude", "-23.5898764"))!, + strconv::stof64(config::setting("sun.longitude", "-46.6589231"))!, + config::setting("sun.sunrise.layout", "🌅%H:%M"), + config::setting("sun.sunset.layout", "🌇%H:%M"), + config::setting("sun.midnight.sun.msg", "Midnight sun"), + config::setting("sun.polar.night.msg", "Polar night")); + +export fn mod_sunrise(pe: *env::env) str = { + const cfg = readconfig(); + match(sunrise_sunset(local_today_utc_noon(), cfg.0, cfg.1)) { + case let r: (date::date, date::date) => + return strings::dup(date::asformat(cfg.2, &date::in(chrono::LOCAL, r.0)!)!); + case midnightsun => + return strings::dup(cfg.4); + case polarnight => + return strings::dup(cfg.5); + }; +}; + +export fn mod_sunset(pe: *env::env) str = { + const cfg = readconfig(); + match(sunrise_sunset(local_today_utc_noon(), cfg.0, cfg.1)) { + case let r: (date::date, date::date) => + return strings::dup(date::asformat(cfg.3, &date::in(chrono::LOCAL, r.1)!)!); + case midnightsun => + return strings::dup(cfg.4); + case polarnight => + return strings::dup(cfg.5); + }; +}; + +fn rad(a: f64) f64 = a * math::PI/180.0; +fn deg(a: f64) f64 = a * 180.0/math::PI; +fn sin(a: f64) f64 = math::sinf64(rad(a)); +fn cos(a: f64) f64 = math::cosf64(rad(a)); + +// Get today's yyyy/mm/dd in LOCAL tzone, build yyyy/mm/dd 12:00:00 in UTC tzone +fn local_today_utc_noon() date::date = { + const now = date::now(); + return date::new(chrono::UTC, 0, date::year(&now), date::month(&now), date::day(&now), 12, 0, 0, 0)!; +}; + +fn julian_day_n(dt: date::date) f64 = { + const ref = date::new(chrono::UTC, 0, 2000, 1, 1, 12, 0, 0, 0)!; + return chrono::diff(&ref, &dt)!: f64/(24*time::HOUR): f64; +}; + +// https://en.wikipedia.org/wiki/Julian_day#Julian_or_Gregorian_calendar_from_Julian_day_number +fn julian_to_gregorian(jday: f64) date::date = { + const J: int = math::truncf64(jday): int; + const y: int = 4716; + const v: int = 3; + const j: int = 1401; + const u: int = 5; + const m: int = 2; + const s: int = 153; + const n: int = 12; + const w: int = 2; + const r: int = 4; + const B: int = 274277; + const p: int = 1461; + const C: int = -38; + const f: int = J + j + (((4 * J + B) / 146097) * 3) / 4 + C; + const e = r * f + v; + const g = (e % p) / r; + const h = u * g + w; + const D = (h % s) / u + 1; + const M = (h/s + m) % n + 1; + const Y = (e / p) - y + (n + m - M) / n; + const fday = jday - J: f64; + const hour = (fday*24.0): int; + const minute = (fday*24.0*60.0): int - (hour*60); + const second = (fday*24.0*60.0*60.0): int - (hour*3600) - (minute*60); + let date = date::new(chrono::UTC, 0, Y, M, D, 0, minute, second, 0)!; + return date::add(date, (hour+12)*time::HOUR); +}; + +fn mean_solar_time(n: f64, lw: f64) f64 = { + return n - lw/360.0; +}; + +fn solar_mean_anomaly(J: f64) f64 = { + return math::modf64(357.5291 + 0.98560028 * J, 360.0); +}; + +fn equation_of_the_center(M: f64) f64 = { + return 1.9148*sin(M) + 0.02*sin(2.0*M) + 0.0003*sin(3.0*M); +}; + +fn eclyptic_longitude(M: f64, C: f64) f64 = { + return math::modf64(M + C + 180.0 + 102.9372, 360.0); +}; + +fn solar_transit(J: f64, M: f64, lambda: f64) f64 = { + return 2451545.0 + J + 0.0053*sin(M) - 0.0069*sin(2.0*lambda); +}; + +fn declination_of_the_sun(lambda: f64) (f64, f64) = { + let sindelta = sin(lambda)*sin(23.44); + return (sindelta, deg(math::asinf64(sindelta))); +}; + +fn hour_angle(phi: f64, delta: f64) (f64, f64) = { + let cosomega0 = (sin(-0.83) - sin(phi)*sin(delta))/(cos(phi)*cos(delta)); + return (cosomega0, deg(math::acosf64(cosomega0))); +}; + +fn sunrise_sunset(d: date::date, lat: f64, lon: f64) ((date::date, date::date) | midnightsun | polarnight) = { + const J = mean_solar_time(math::ceilf64(julian_day_n(d)), lon); + const M = solar_mean_anomaly(J); + const C = equation_of_the_center(M); + const lambda = eclyptic_longitude(M, C); + const J_transit = solar_transit(J, M, lambda); + const (sindelta, delta) = declination_of_the_sun(lambda); + const (cosomega0, omega0) = hour_angle(lat, delta); + if (cosomega0 < -1.0) { + return midnightsun; + } else if (cosomega0 > 1.0) { + return polarnight; + }; + const J_rise = J_transit - omega0/360.0; + const J_set = J_transit + omega0/360.0; + return (julian_to_gregorian(J_rise), julian_to_gregorian(J_set)); +}; -- 2.30.2
Thanks a lot!