Compare commits
No commits in common. "add82dee40e7e0a4443f3aa3d2ec0a62893f2e8d" and "35c9831ba86f0240e80df38f53f655062183ed7b" have entirely different histories.
add82dee40
...
35c9831ba8
20 changed files with 97 additions and 3338 deletions
|
|
@ -343,10 +343,6 @@ The application makes network calls to the following services:
|
|||
**External (other):**
|
||||
- Airport code -> location mapping: [Openflights](https://github.com/jpatokal/openflights)
|
||||
- Ip address -> location mapping: [GeoLite2 City Database](https://github.com/maxmind/libmaxminddb)
|
||||
- Moon phase calculations (vendored): [Phoon](https://acme.com/software/phoon/)
|
||||
- Astronomical calculations (vendored): [Sunriset](http://www.stjarnhimlen.se/comp/sunriset.c)
|
||||
- Note, a small change was made to the original to provide the ability to
|
||||
skip putting main() into the object file
|
||||
|
||||
## Performance Targets
|
||||
|
||||
|
|
|
|||
|
|
@ -11,23 +11,24 @@ Features not yet implemented in the Zig version:
|
|||
- Support transparency and custom styling
|
||||
- Requires image rendering library integration
|
||||
|
||||
## 3. Language/Localization
|
||||
## 3. Multiple Locations Support
|
||||
- Handle colon-separated locations (e.g., `London:Paris:Berlin`)
|
||||
- Process and display weather for multiple cities in one request
|
||||
|
||||
## 4. Language/Localization
|
||||
- Accept-Language header parsing
|
||||
- lang query parameter support
|
||||
- Translation of weather conditions and text (54 languages)
|
||||
|
||||
## 4. Json output
|
||||
## 5. Moon Phase Calculation
|
||||
- Real moon phase computation based on date
|
||||
- Moon phase emoji display
|
||||
- Moonday calculation
|
||||
|
||||
## 6. Astronomical Times
|
||||
- Calculate dawn, sunrise, zenith, sunset, dusk times
|
||||
- Based on location coordinates and timezone
|
||||
- Display in custom format output
|
||||
|
||||
## 7. Json output
|
||||
- Does not match wttr.in format
|
||||
|
||||
## 5. Moon endpoint
|
||||
- `/Moon` and `/Moon@YYYY-MM-DD` endpoints not yet implemented
|
||||
- Moon phase calculation is implemented and available in custom format (%m, %M)
|
||||
|
||||
## ~~Multiple Locations Support~~
|
||||
- Handle colon-separated locations (e.g., `London:Paris:Berlin`)
|
||||
- Process and display weather for multiple cities in one request
|
||||
|
||||
This is advertised as a feature in wttr.in, but it does not work on the live
|
||||
site. Given that this would be an unbounded computational risk and a DOS vector,
|
||||
this is not going to be implemented. That can be done easily through a script,
|
||||
and it will allow the rate limiting to work better that way
|
||||
|
|
|
|||
15
README.md
15
README.md
|
|
@ -92,8 +92,9 @@ Available preconfigured formats: 1, 2, 3, 4 and custom format using percent nota
|
|||
* `format=3`: `Nuremberg: 🌦 +11⁰C`
|
||||
* `format=4`: `Nuremberg: 🌦 🌡️+11°C 🌬️↓4km/h`
|
||||
|
||||
**Note:** Wttr.in claims that you can do multiple locations at one with a ':'
|
||||
separator, but that does not appear to work
|
||||
You can specify multiple locations separated with `:`:
|
||||
|
||||
**Note:** Not yet fully implemented - see [MISSING_FEATURES.md](MISSING_FEATURES.md)
|
||||
|
||||
```bash
|
||||
$ curl localhost:8002/Nuremberg:Hamburg:Berlin?format=3
|
||||
|
|
@ -224,20 +225,18 @@ All configuration is via environment variables:
|
|||
| `WTTR_CACHE_DIR` | `~/.cache/wttr` | Cache directory |
|
||||
| `WTTR_CACHE_SIZE` | `10000` | Max cached responses |
|
||||
| `WTTR_GEOLITE_PATH` | `~/.cache/wttr/GeoLite2-City.mmdb` | GeoIP database path |
|
||||
| `WTTR_GEOCACHE_FILE` | `~/.cache/wttr/geocache.json` | Location name cache |
|
||||
| `IP2LOCATION_API_KEY` | (none) | Optional IP geolocation fallback |
|
||||
| `IP2LOCATION_CACHE_FILE` | `~/.cache/wttr/ip2location.cache` | IP2Location cache file |
|
||||
| `METNO_TOS_IDENTIFYING_EMAIL` | (none) | Email for Met.no API identification |
|
||||
|
||||
## External Services
|
||||
|
||||
### Required
|
||||
### Required (No API Key)
|
||||
- **Met.no Weather API** - Weather data provider (free, no registration)
|
||||
- Set METNO_TOS_IDENTIFYING_EMAIL to identify yourself to Met.No
|
||||
- **IP2Location.io** - IP geolocation service
|
||||
|
||||
- **IP2Location.io** - Fallback IP geolocation
|
||||
- Sign up at https://www.ip2location.io/
|
||||
- Free tier: 30,000 requests/month
|
||||
- Set `IP2LOCATION_API_KEY` environment variable for higher limits
|
||||
- Set via `IP2LOCATION_API_KEY` environment variable
|
||||
|
||||
### Auto-Downloaded
|
||||
- **MaxMind GeoLite2 City** - IP geolocation database
|
||||
|
|
|
|||
53
build.zig
53
build.zig
|
|
@ -19,51 +19,6 @@ pub fn build(b: *std.Build) void {
|
|||
|
||||
const maxminddb_upstream = b.dependency("maxminddb", .{});
|
||||
|
||||
// Build sunriset as a static library
|
||||
const sunriset = b.addLibrary(.{
|
||||
.name = "sunriset",
|
||||
.linkage = .static,
|
||||
.root_module = b.createModule(.{
|
||||
.target = target,
|
||||
.optimize = optimize,
|
||||
.link_libc = true,
|
||||
}),
|
||||
});
|
||||
|
||||
sunriset.addIncludePath(b.path("libs/sunriset"));
|
||||
sunriset.addCSourceFiles(.{
|
||||
.root = b.path("libs/sunriset"),
|
||||
.files = &.{
|
||||
"sunriset.c",
|
||||
},
|
||||
.flags = &.{ "-D_DEFAULT_SOURCE", "-DSUNRISET_NO_MAIN" },
|
||||
});
|
||||
sunriset.linkLibC();
|
||||
sunriset.linkSystemLibrary("m");
|
||||
|
||||
// Build phoon as a static library
|
||||
const phoon = b.addLibrary(.{
|
||||
.name = "phoon",
|
||||
.linkage = .static,
|
||||
.root_module = b.createModule(.{
|
||||
.target = target,
|
||||
.optimize = optimize,
|
||||
.link_libc = true,
|
||||
}),
|
||||
});
|
||||
|
||||
phoon.addIncludePath(b.path("libs/phoon_14Aug2014"));
|
||||
phoon.addCSourceFiles(.{
|
||||
.root = b.path("libs/phoon_14Aug2014"),
|
||||
.files = &.{
|
||||
"astro.c",
|
||||
"date_parse.c",
|
||||
},
|
||||
.flags = &.{ "-std=c99", "-D_DEFAULT_SOURCE" },
|
||||
});
|
||||
phoon.linkLibC();
|
||||
phoon.linkSystemLibrary("m");
|
||||
|
||||
// Build libmaxminddb as a static library
|
||||
const maxminddb = b.addLibrary(.{
|
||||
.name = "maxminddb",
|
||||
|
|
@ -119,12 +74,8 @@ pub fn build(b: *std.Build) void {
|
|||
});
|
||||
exe.root_module.addOptions("build_options", build_options);
|
||||
exe.root_module.addIncludePath(maxminddb_upstream.path("include"));
|
||||
exe.root_module.addIncludePath(b.path("libs/phoon_14Aug2014"));
|
||||
exe.root_module.addIncludePath(b.path("libs/sunriset"));
|
||||
exe.root_module.addConfigHeader(maxminddb_config);
|
||||
exe.linkLibrary(maxminddb);
|
||||
exe.linkLibrary(phoon);
|
||||
exe.linkLibrary(sunriset);
|
||||
exe.linkLibC();
|
||||
|
||||
b.installArtifact(exe);
|
||||
|
|
@ -158,12 +109,8 @@ pub fn build(b: *std.Build) void {
|
|||
});
|
||||
tests.root_module.addOptions("build_options", test_options);
|
||||
tests.root_module.addIncludePath(maxminddb_upstream.path("include"));
|
||||
tests.root_module.addIncludePath(b.path("libs/phoon_14Aug2014"));
|
||||
tests.root_module.addIncludePath(b.path("libs/sunriset"));
|
||||
tests.root_module.addConfigHeader(maxminddb_config);
|
||||
tests.linkLibrary(maxminddb);
|
||||
tests.linkLibrary(phoon);
|
||||
tests.linkLibrary(sunriset);
|
||||
tests.linkLibC();
|
||||
|
||||
const run_tests = b.addRunArtifact(tests);
|
||||
|
|
|
|||
|
|
@ -18,8 +18,6 @@
|
|||
.url = "git+https://github.com/rockorager/zeit?ref=zig-0.15#7ac64d72dbfb1a4ad549102e7d4e232a687d32d8",
|
||||
.hash = "zeit-0.6.0-5I6bk36tAgATpSl9wjFmRPMqYN2Mn0JQHgIcRNcqDpJA",
|
||||
},
|
||||
.phoon = .{ .path = "libs/phoon_14Aug2014" },
|
||||
.sunriset = .{ .path = "libs/sunriset" },
|
||||
},
|
||||
.fingerprint = 0x710c2b57e81aa678,
|
||||
.minimum_zig_version = "0.15.2",
|
||||
|
|
|
|||
|
|
@ -1,33 +0,0 @@
|
|||
# Makefile for phoon
|
||||
|
||||
BINDIR = /usr/local/bin
|
||||
MANDIR = /usr/local/man/man1
|
||||
|
||||
DEFINES = -DOS_BSD
|
||||
#DEFINES = -DOS_SYSV
|
||||
|
||||
CC = cc
|
||||
CFLAGS = -O $(DEFINES) -ansi -pedantic -U__STRICT_ANSI__ -Wall -Wpointer-arith -Wshadow -Wcast-qual -Wcast-align -Wstrict-prototypes -Wmissing-prototypes -Wmissing-declarations -Wredundant-decls -Wno-long-long
|
||||
|
||||
LDFLAGS = -s
|
||||
|
||||
|
||||
all: phoon
|
||||
|
||||
phoon: phoon.o date_parse.o astro.o
|
||||
$(CC) $(LDFLAGS) -o phoon phoon.o date_parse.o astro.o -lm
|
||||
|
||||
.c.o:
|
||||
$(CC) $(CFLAGS) -c $<
|
||||
|
||||
date_parse.o: date_parse.h
|
||||
astro.o: astro.h
|
||||
|
||||
install: all
|
||||
rm -f $(BINDIR)/phoon
|
||||
cp phoon $(BINDIR)
|
||||
rm -f $(MANDIR)/phoon.1
|
||||
cp phoon.1 $(MANDIR)
|
||||
|
||||
clean:
|
||||
rm -f phoon *.o a.out core
|
||||
|
|
@ -1,50 +0,0 @@
|
|||
phoon - display current moon phase
|
||||
|
||||
|
||||
phoon - program to display the PHase of the mOON. Unlike other
|
||||
such programs, which just tell you how long since first quarter
|
||||
or something like that, phoon *shows* you the phase with a little
|
||||
picture. I've put an example at the end of this file. I first
|
||||
wrote this program in Pascal / TOPS-20 at CMU in 1979; I translated
|
||||
it to Ratfor / Software Tools in 1981; and now it's in C / Unix.
|
||||
|
||||
Files in this distribution:
|
||||
|
||||
README this
|
||||
Makefile guess
|
||||
phoon.c phase of moon display
|
||||
astro.c phase of moon calculations
|
||||
astro.h header file
|
||||
phoon.1 manual for phase of moon program
|
||||
date_parse.c date-parsing routine
|
||||
date_parse.h header file
|
||||
|
||||
Unpack the files, edit Makefile and change the options to suit, make,
|
||||
and enjoy! Feedback is welcome - send bug reports, enhancements,
|
||||
checks, money orders, etc. to the addresses below.
|
||||
|
||||
Jef Poskanzer jef@mail.acme.com http://www.acme.com/jef/
|
||||
|
||||
.--
|
||||
.--
|
||||
.-'
|
||||
.-'@
|
||||
/@@@
|
||||
./
|
||||
/@@ o
|
||||
/@@@@
|
||||
|@@@@@
|
||||
/@@@@@ Last Quarter +
|
||||
| @@@@ 4 1:36:10
|
||||
|@ @@@ New Moon -
|
||||
| 3 7:34:53
|
||||
\ . @
|
||||
|
|
||||
\ @
|
||||
\ o
|
||||
`\
|
||||
\
|
||||
`-.
|
||||
`-.
|
||||
`--
|
||||
`--
|
||||
|
|
@ -1,467 +0,0 @@
|
|||
/* Adapted from "moontool.c" by John Walker, Release 2.5
|
||||
**
|
||||
** Quoting from the original:
|
||||
**
|
||||
** The algorithms used in this program to calculate the positions Sun and
|
||||
** Moon as seen from the Earth are given in the book "Practical Astronomy
|
||||
** With Your Calculator" by Peter Duffett-Smith, Second Edition,
|
||||
** Cambridge University Press, 1981. Ignore the word "Calculator" in the
|
||||
** title; this is an essential reference if you're interested in
|
||||
** developing software which calculates planetary positions, orbits,
|
||||
** eclipses, and the like. If you're interested in pursuing such
|
||||
** programming, you should also obtain:
|
||||
**
|
||||
** "Astronomical Formulae for Calculators" by Jean Meeus, Third Edition,
|
||||
** Willmann-Bell, 1985. A must-have.
|
||||
**
|
||||
** "Planetary Programs and Tables from -4000 to +2800" by Pierre
|
||||
** Bretagnon and Jean-Louis Simon, Willmann-Bell, 1986. If you want the
|
||||
** utmost (outside of JPL) accuracy for the planets, it's here.
|
||||
**
|
||||
** "Celestial BASIC" by Eric Burgess, Revised Edition, Sybex, 1985. Very
|
||||
** cookbook oriented, and many of the algorithms are hard to dig out of
|
||||
** the turgid BASIC code, but you'll probably want it anyway.
|
||||
**
|
||||
** See http://www.fourmilab.ch/moontool/
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <math.h>
|
||||
#include <time.h>
|
||||
|
||||
#include "astro.h"
|
||||
|
||||
|
||||
#define FALSE 0
|
||||
#define TRUE 1
|
||||
|
||||
/* Astronomical constants */
|
||||
|
||||
#define epoch 2444238.5 /* 1980 January 0.0 */
|
||||
|
||||
/* Constants defining the Sun's apparent orbit */
|
||||
|
||||
#define elonge 278.833540 /* Ecliptic longitude of the Sun
|
||||
at epoch 1980.0 */
|
||||
#define elongp 282.596403 /* Ecliptic longitude of the Sun at
|
||||
perigee */
|
||||
#define eccent 0.016718 /* Eccentricity of Earth's orbit */
|
||||
#define sunsmax 1.495985e8 /* Semi-major axis of Earth's orbit, km */
|
||||
#define sunangsiz 0.533128 /* Sun's angular size, degrees, at
|
||||
semi-major axis distance */
|
||||
|
||||
/* Elements of the Moon's orbit, epoch 1980.0 */
|
||||
|
||||
#define mmlong 64.975464 /* Moon's mean lonigitude at the epoch */
|
||||
#define mmlongp 349.383063 /* Mean longitude of the perigee at the
|
||||
epoch */
|
||||
#define mlnode 151.950429 /* Mean longitude of the node at the
|
||||
epoch */
|
||||
#define minc 5.145396 /* Inclination of the Moon's orbit */
|
||||
#define mecc 0.054900 /* Eccentricity of the Moon's orbit */
|
||||
#define mangsiz 0.5181 /* Moon's angular size at distance a
|
||||
from Earth */
|
||||
#define msmax 384401.0 /* Semi-major axis of Moon's orbit in km */
|
||||
#define mparallax 0.9507 /* Parallax at distance a from Earth */
|
||||
#define synmonth 29.53058868 /* Synodic month (new Moon to new Moon) */
|
||||
#define lunatbase 2423436.0 /* Base date for E. W. Brown's numbered
|
||||
series of lunations (1923 January 16) */
|
||||
|
||||
/* Properties of the Earth */
|
||||
|
||||
#define earthrad 6378.16 /* Radius of Earth in kilometres */
|
||||
|
||||
|
||||
#define PI 3.14159265358979323846 /* Assume not near black hole nor in
|
||||
Tennessee */
|
||||
|
||||
/* Handy mathematical functions */
|
||||
|
||||
#define sgn(x) (((x) < 0) ? -1 : ((x) > 0 ? 1 : 0)) /* Extract sign */
|
||||
#define abs(x) ((x) < 0 ? (-(x)) : (x)) /* Absolute val */
|
||||
#define fixangle(a) ((a) - 360.0 * (floor((a) / 360.0))) /* Fix angle */
|
||||
#define torad(d) ((d) * (PI / 180.0)) /* Deg->Rad */
|
||||
#define todeg(d) ((d) * (180.0 / PI)) /* Rad->Deg */
|
||||
#define dsin(x) (sin(torad((x)))) /* Sin from deg */
|
||||
#define dcos(x) (cos(torad((x)))) /* Cos from deg */
|
||||
|
||||
|
||||
/*
|
||||
* UNIX_TO_JULIAN -- Convert internal Unix date/time to astronomical
|
||||
* Julian time (i.e. Julian date plus day fraction, expressed as
|
||||
* a double).
|
||||
*/
|
||||
double
|
||||
unix_to_julian( time_t t )
|
||||
{
|
||||
return (double) t / 86400.0 + 2440587.4999996666666666666;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* JYEAR -- Convert Julian date to year, month, day, which are
|
||||
* returned via integer pointers to integers.
|
||||
*/
|
||||
static void
|
||||
jyear( double td, int* yy, int* mm, int* dd )
|
||||
{
|
||||
double j, d, y, m;
|
||||
|
||||
td += 0.5; /* Astronomical to civil */
|
||||
j = floor(td);
|
||||
j = j - 1721119.0;
|
||||
y = floor(((4 * j) - 1) / 146097.0);
|
||||
j = (j * 4.0) - (1.0 + (146097.0 * y));
|
||||
d = floor(j / 4.0);
|
||||
j = floor(((4.0 * d) + 3.0) / 1461.0);
|
||||
d = ((4.0 * d) + 3.0) - (1461.0 * j);
|
||||
d = floor((d + 4.0) / 4.0);
|
||||
m = floor(((5.0 * d) - 3) / 153.0);
|
||||
d = (5.0 * d) - (3.0 + (153.0 * m));
|
||||
d = floor((d + 5.0) / 5.0);
|
||||
y = (100.0 * y) + j;
|
||||
if (m < 10.0)
|
||||
m = m + 3;
|
||||
else {
|
||||
m = m - 9;
|
||||
y = y + 1;
|
||||
}
|
||||
*yy = y;
|
||||
*mm = m;
|
||||
*dd = d;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* MEANPHASE -- Calculates time of the mean new Moon for a given
|
||||
* base date. This argument K to this function is
|
||||
* the precomputed synodic month index, given by:
|
||||
*
|
||||
* K = (year - 1900) * 12.3685
|
||||
*
|
||||
* where year is expressed as a year and fractional year.
|
||||
*/
|
||||
static double
|
||||
meanphase( double sdate, double k )
|
||||
{
|
||||
double t, t2, t3, nt1;
|
||||
|
||||
/* Time in Julian centuries from 1900 January 0.5 */
|
||||
t = (sdate - 2415020.0) / 36525;
|
||||
t2 = t * t; /* Square for frequent use */
|
||||
t3 = t2 * t; /* Cube for frequent use */
|
||||
|
||||
nt1 = 2415020.75933 + synmonth * k
|
||||
+ 0.0001178 * t2
|
||||
- 0.000000155 * t3
|
||||
+ 0.00033 * dsin(166.56 + 132.87 * t - 0.009173 * t2);
|
||||
|
||||
return nt1;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* TRUEPHASE -- Given a K value used to determine the
|
||||
* mean phase of the new moon, and a phase
|
||||
* selector (0.0, 0.25, 0.5, 0.75), obtain
|
||||
* the true, corrected phase time.
|
||||
*/
|
||||
static double
|
||||
truephase( double k, double pha )
|
||||
{
|
||||
double t, t2, t3, pt, m, mprime, f;
|
||||
int apcor = FALSE;
|
||||
|
||||
k += pha; /* Add phase to new moon time */
|
||||
t = k / 1236.85; /* Time in Julian centuries from
|
||||
1900 January 0.5 */
|
||||
t2 = t * t; /* Square for frequent use */
|
||||
t3 = t2 * t; /* Cube for frequent use */
|
||||
pt = 2415020.75933 /* Mean time of phase */
|
||||
+ synmonth * k
|
||||
+ 0.0001178 * t2
|
||||
- 0.000000155 * t3
|
||||
+ 0.00033 * dsin(166.56 + 132.87 * t - 0.009173 * t2);
|
||||
|
||||
m = 359.2242 /* Sun's mean anomaly */
|
||||
+ 29.10535608 * k
|
||||
- 0.0000333 * t2
|
||||
- 0.00000347 * t3;
|
||||
mprime = 306.0253 /* Moon's mean anomaly */
|
||||
+ 385.81691806 * k
|
||||
+ 0.0107306 * t2
|
||||
+ 0.00001236 * t3;
|
||||
f = 21.2964 /* Moon's argument of latitude */
|
||||
+ 390.67050646 * k
|
||||
- 0.0016528 * t2
|
||||
- 0.00000239 * t3;
|
||||
if ((pha < 0.01) || (abs(pha - 0.5) < 0.01)) {
|
||||
|
||||
/* Corrections for New and Full Moon */
|
||||
|
||||
pt += (0.1734 - 0.000393 * t) * dsin(m)
|
||||
+ 0.0021 * dsin(2 * m)
|
||||
- 0.4068 * dsin(mprime)
|
||||
+ 0.0161 * dsin(2 * mprime)
|
||||
- 0.0004 * dsin(3 * mprime)
|
||||
+ 0.0104 * dsin(2 * f)
|
||||
- 0.0051 * dsin(m + mprime)
|
||||
- 0.0074 * dsin(m - mprime)
|
||||
+ 0.0004 * dsin(2 * f + m)
|
||||
- 0.0004 * dsin(2 * f - m)
|
||||
- 0.0006 * dsin(2 * f + mprime)
|
||||
+ 0.0010 * dsin(2 * f - mprime)
|
||||
+ 0.0005 * dsin(m + 2 * mprime);
|
||||
apcor = TRUE;
|
||||
} else if ((abs(pha - 0.25) < 0.01 || (abs(pha - 0.75) < 0.01))) {
|
||||
pt += (0.1721 - 0.0004 * t) * dsin(m)
|
||||
+ 0.0021 * dsin(2 * m)
|
||||
- 0.6280 * dsin(mprime)
|
||||
+ 0.0089 * dsin(2 * mprime)
|
||||
- 0.0004 * dsin(3 * mprime)
|
||||
+ 0.0079 * dsin(2 * f)
|
||||
- 0.0119 * dsin(m + mprime)
|
||||
- 0.0047 * dsin(m - mprime)
|
||||
+ 0.0003 * dsin(2 * f + m)
|
||||
- 0.0004 * dsin(2 * f - m)
|
||||
- 0.0006 * dsin(2 * f + mprime)
|
||||
+ 0.0021 * dsin(2 * f - mprime)
|
||||
+ 0.0003 * dsin(m + 2 * mprime)
|
||||
+ 0.0004 * dsin(m - 2 * mprime)
|
||||
- 0.0003 * dsin(2 * m + mprime);
|
||||
if (pha < 0.5)
|
||||
/* First quarter correction */
|
||||
pt += 0.0028 - 0.0004 * dcos(m) + 0.0003 * dcos(mprime);
|
||||
else
|
||||
/* Last quarter correction */
|
||||
pt += -0.0028 + 0.0004 * dcos(m) - 0.0003 * dcos(mprime);
|
||||
apcor = TRUE;
|
||||
}
|
||||
if (!apcor) {
|
||||
(void)fprintf (stderr,
|
||||
"TRUEPHASE called with invalid phase selector.\n");
|
||||
abort();
|
||||
}
|
||||
return pt;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* PHASEHUNT5 -- Find time of phases of the moon which surround
|
||||
* the current date. Five phases are found, starting
|
||||
* and ending with the new moons which bound the
|
||||
* current lunation.
|
||||
*/
|
||||
void
|
||||
phasehunt5( double sdate, double phases[5] )
|
||||
{
|
||||
double adate, k1, k2, nt1, nt2;
|
||||
int yy, mm, dd;
|
||||
|
||||
adate = sdate - 45;
|
||||
|
||||
jyear(adate, &yy, &mm, &dd);
|
||||
k1 = floor((yy + ((mm - 1) * (1.0 / 12.0)) - 1900) * 12.3685);
|
||||
|
||||
adate = nt1 = meanphase(adate, k1);
|
||||
while (TRUE) {
|
||||
adate += synmonth;
|
||||
k2 = k1 + 1;
|
||||
nt2 = meanphase(adate, k2);
|
||||
if (nt1 <= sdate && nt2 > sdate)
|
||||
break;
|
||||
nt1 = nt2;
|
||||
k1 = k2;
|
||||
}
|
||||
phases [0] = truephase (k1, 0.0);
|
||||
phases [1] = truephase (k1, 0.25);
|
||||
phases [2] = truephase (k1, 0.5);
|
||||
phases [3] = truephase (k1, 0.75);
|
||||
phases [4] = truephase (k2, 0.0);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* PHASEHUNT2 -- Find time of phases of the moon which surround
|
||||
* the current date. Two phases are found.
|
||||
*/
|
||||
void
|
||||
phasehunt2( double sdate, double phases[2], double which[2] )
|
||||
{
|
||||
double phases5[5];
|
||||
|
||||
phasehunt5( sdate, phases5 );
|
||||
phases[0] = phases5[0];
|
||||
which[0] = 0.0;
|
||||
phases[1] = phases5[1];
|
||||
which[1] = 0.25;
|
||||
if ( phases[1] <= sdate ) {
|
||||
phases[0] = phases[1];
|
||||
which[0] = which[1];
|
||||
phases[1] = phases5[2];
|
||||
which[1] = 0.5;
|
||||
if ( phases[1] <= sdate ) {
|
||||
phases[0] = phases[1];
|
||||
which[0] = which[1];
|
||||
phases[1] = phases5[3];
|
||||
which[1] = 0.75;
|
||||
if ( phases[1] <= sdate ) {
|
||||
phases[0] = phases[1];
|
||||
which[0] = which[1];
|
||||
phases[1] = phases5[4];
|
||||
which[1] = 0.0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* KEPLER -- Solve the equation of Kepler.
|
||||
*/
|
||||
static double
|
||||
kepler( double m, double ecc )
|
||||
{
|
||||
double e, delta;
|
||||
#define EPSILON 1E-6
|
||||
|
||||
e = m = torad(m);
|
||||
do {
|
||||
delta = e - ecc * sin(e) - m;
|
||||
e -= delta / (1 - ecc * cos(e));
|
||||
} while (abs (delta) > EPSILON);
|
||||
return e;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* PHASE -- Calculate phase of moon as a fraction:
|
||||
*
|
||||
* The argument is the time for which the phase is requested,
|
||||
* expressed as a Julian date and fraction. Returns the terminator
|
||||
* phase angle as a percentage of a full circle (i.e., 0 to 1),
|
||||
* and stores into pointer arguments the illuminated fraction of
|
||||
* the Moon's disc, the Moon's age in days and fraction, the
|
||||
* distance of the Moon from the centre of the Earth, and the
|
||||
* angular diameter subtended by the Moon as seen by an observer
|
||||
* at the centre of the Earth.
|
||||
*
|
||||
* pphase: Illuminated fraction
|
||||
* mage: Age of moon in days
|
||||
* dist: Distance in kilometres
|
||||
* angdia: Angular diameter in degrees
|
||||
* sudist: Distance to Sun
|
||||
* suangdia: Sun's angular diameter
|
||||
*/
|
||||
double
|
||||
phase( double pdate, double* pphase, double* mage, double* dist, double* angdia, double* sudist, double* suangdia )
|
||||
{
|
||||
|
||||
double Day, N, M, Ec, Lambdasun, ml, MM, MN, Ev, Ae, A3, MmP,
|
||||
mEc, A4, lP, V, lPP, NP, y, x, Lambdamoon, BetaM,
|
||||
MoonAge, MoonPhase,
|
||||
MoonDist, MoonDFrac, MoonAng, MoonPar,
|
||||
F, SunDist, SunAng;
|
||||
|
||||
/* Calculation of the Sun's position */
|
||||
|
||||
Day = pdate - epoch; /* Date within epoch */
|
||||
N = fixangle((360 / 365.2422) * Day); /* Mean anomaly of the Sun */
|
||||
M = fixangle(N + elonge - elongp); /* Convert from perigee
|
||||
co-ordinates to epoch 1980.0 */
|
||||
Ec = kepler(M, eccent); /* Solve equation of Kepler */
|
||||
Ec = sqrt((1 + eccent) / (1 - eccent)) * tan(Ec / 2);
|
||||
Ec = 2 * todeg(atan(Ec)); /* True anomaly */
|
||||
Lambdasun = fixangle(Ec + elongp); /* Sun's geocentric ecliptic
|
||||
longitude */
|
||||
/* Orbital distance factor */
|
||||
F = ((1 + eccent * cos(torad(Ec))) / (1 - eccent * eccent));
|
||||
SunDist = sunsmax / F; /* Distance to Sun in km */
|
||||
SunAng = F * sunangsiz; /* Sun's angular size in degrees */
|
||||
|
||||
|
||||
/* Calculation of the Moon's position */
|
||||
|
||||
/* Moon's mean longitude */
|
||||
ml = fixangle(13.1763966 * Day + mmlong);
|
||||
|
||||
/* Moon's mean anomaly */
|
||||
MM = fixangle(ml - 0.1114041 * Day - mmlongp);
|
||||
|
||||
/* Moon's ascending node mean longitude */
|
||||
MN = fixangle(mlnode - 0.0529539 * Day);
|
||||
|
||||
/* Evection */
|
||||
Ev = 1.2739 * sin(torad(2 * (ml - Lambdasun) - MM));
|
||||
|
||||
/* Annual equation */
|
||||
Ae = 0.1858 * sin(torad(M));
|
||||
|
||||
/* Correction term */
|
||||
A3 = 0.37 * sin(torad(M));
|
||||
|
||||
/* Corrected anomaly */
|
||||
MmP = MM + Ev - Ae - A3;
|
||||
|
||||
/* Correction for the equation of the centre */
|
||||
mEc = 6.2886 * sin(torad(MmP));
|
||||
|
||||
/* Another correction term */
|
||||
A4 = 0.214 * sin(torad(2 * MmP));
|
||||
|
||||
/* Corrected longitude */
|
||||
lP = ml + Ev + mEc - Ae + A4;
|
||||
|
||||
/* Variation */
|
||||
V = 0.6583 * sin(torad(2 * (lP - Lambdasun)));
|
||||
|
||||
/* True longitude */
|
||||
lPP = lP + V;
|
||||
|
||||
/* Corrected longitude of the node */
|
||||
NP = MN - 0.16 * sin(torad(M));
|
||||
|
||||
/* Y inclination coordinate */
|
||||
y = sin(torad(lPP - NP)) * cos(torad(minc));
|
||||
|
||||
/* X inclination coordinate */
|
||||
x = cos(torad(lPP - NP));
|
||||
|
||||
/* Ecliptic longitude */
|
||||
Lambdamoon = todeg(atan2(y, x));
|
||||
Lambdamoon += NP;
|
||||
|
||||
/* Ecliptic latitude */
|
||||
BetaM = todeg(asin(sin(torad(lPP - NP)) * sin(torad(minc))));
|
||||
|
||||
/* Calculation of the phase of the Moon */
|
||||
|
||||
/* Age of the Moon in degrees */
|
||||
MoonAge = lPP - Lambdasun;
|
||||
|
||||
/* Phase of the Moon */
|
||||
MoonPhase = (1 - cos(torad(MoonAge))) / 2;
|
||||
|
||||
/* Calculate distance of moon from the centre of the Earth */
|
||||
|
||||
MoonDist = (msmax * (1 - mecc * mecc)) /
|
||||
(1 + mecc * cos(torad(MmP + mEc)));
|
||||
|
||||
/* Calculate Moon's angular diameter */
|
||||
|
||||
MoonDFrac = MoonDist / msmax;
|
||||
MoonAng = mangsiz / MoonDFrac;
|
||||
|
||||
/* Calculate Moon's parallax */
|
||||
|
||||
MoonPar = mparallax / MoonDFrac;
|
||||
|
||||
*pphase = MoonPhase;
|
||||
*mage = synmonth * (fixangle(MoonAge) / 360.0);
|
||||
*dist = MoonDist;
|
||||
*angdia = MoonAng;
|
||||
*sudist = SunDist;
|
||||
*suangdia = SunAng;
|
||||
return fixangle(MoonAge) / 360.0;
|
||||
}
|
||||
|
|
@ -1,46 +0,0 @@
|
|||
#ifndef _ASTRO_H_
|
||||
#define _ASTRO_H_
|
||||
|
||||
/*
|
||||
* UNIX_TO_JULIAN -- Convert internal Unix date/time to astronomical
|
||||
* Julian time (i.e. Julian date plus day fraction, expressed as
|
||||
* a double).
|
||||
*/
|
||||
double unix_to_julian( time_t t );
|
||||
|
||||
/*
|
||||
* PHASEHUNT5 -- Find time of phases of the moon which surround
|
||||
* the current date. Five phases are found, starting
|
||||
* and ending with the new moons which bound the
|
||||
* current lunation.
|
||||
*/
|
||||
void phasehunt5( double sdate, double phases[5] );
|
||||
|
||||
/*
|
||||
* PHASEHUNT2 -- Find time of phases of the moon which surround
|
||||
* the current date. Two phases are found.
|
||||
*/
|
||||
void phasehunt2( double sdate, double phases[2], double which[2] );
|
||||
|
||||
/*
|
||||
* PHASE -- Calculate phase of moon as a fraction:
|
||||
*
|
||||
* The argument is the time for which the phase is requested,
|
||||
* expressed as a Julian date and fraction. Returns the terminator
|
||||
* phase angle as a percentage of a full circle (i.e., 0 to 1),
|
||||
* and stores into pointer arguments the illuminated fraction of
|
||||
* the Moon's disc, the Moon's age in days and fraction, the
|
||||
* distance of the Moon from the centre of the Earth, and the
|
||||
* angular diameter subtended by the Moon as seen by an observer
|
||||
* at the centre of the Earth.
|
||||
*
|
||||
* pphase: Illuminated fraction
|
||||
* mage: Age of moon in days
|
||||
* dist: Distance in kilometres
|
||||
* angdia: Angular diameter in degrees
|
||||
* sudist: Distance to Sun
|
||||
* suangdia: Sun's angular diameter
|
||||
*/
|
||||
double phase( double pdate, double* pphase, double* mage, double* dist, double* angdia, double* sudist, double* suangdia );
|
||||
|
||||
#endif /* _ASTRO_H_ */
|
||||
File diff suppressed because it is too large
Load diff
|
|
@ -1,33 +0,0 @@
|
|||
/* date_parse.h - parse string dates into internal form
|
||||
**
|
||||
** Copyright © 1995 by Jef Poskanzer <jef@mail.acme.com>.
|
||||
** All rights reserved.
|
||||
**
|
||||
** Redistribution and use in source and binary forms, with or without
|
||||
** modification, are permitted provided that the following conditions
|
||||
** are met:
|
||||
** 1. Redistributions of source code must retain the above copyright
|
||||
** notice, this list of conditions and the following disclaimer.
|
||||
** 2. Redistributions in binary form must reproduce the above copyright
|
||||
** notice, this list of conditions and the following disclaimer in the
|
||||
** documentation and/or other materials provided with the distribution.
|
||||
**
|
||||
** THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||
** ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
** IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
** ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||
** FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
** DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
** OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
** HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
** OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
** SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef _DATE_PARSE_H_
|
||||
#define _DATE_PARSE_H_
|
||||
|
||||
time_t date_parse( char* str );
|
||||
|
||||
#endif /* _DATE_PARSE_H_ */
|
||||
|
|
@ -1,23 +0,0 @@
|
|||
.TH phoon 1 "07 June 1988"
|
||||
.SH NAME
|
||||
phoon - show the PHase of the mOON
|
||||
.SH SYNOPSIS
|
||||
phoon
|
||||
.RB -l
|
||||
.IR lines ]
|
||||
.RI [ date ]
|
||||
.SH DESCRIPTION
|
||||
.I Phoon
|
||||
displays the phase of the moon, either currently
|
||||
or at a specified date / time.
|
||||
Unlike other such programs, which just tell you how long since first quarter
|
||||
or something like that, phoon
|
||||
.I shows
|
||||
you the phase with a cute little picture.
|
||||
You can vary the size of the picture with the -l flag, but only some
|
||||
sizes have pictures defined - other sizes use @'s.
|
||||
.SH "SEE ALSO"
|
||||
date_parse(3)
|
||||
.SH AUTHOR
|
||||
Jef Poskanzer.
|
||||
The moon-phase computation is from "moontool.c", by John Walker.
|
||||
|
|
@ -1,575 +0,0 @@
|
|||
/* phoon - show the phase of the moon
|
||||
**
|
||||
** ver date who remarks
|
||||
** --- ------- --- -------------------------------------------------------------
|
||||
** 03A 01apr95 JP Updated to use date_parse.
|
||||
** 02A 07jun88 JP Changed the phase calculation to use code from John Walker's
|
||||
** "moontool", increasing accuracy tremendously.
|
||||
** Got rid of SINFORCOS, since Apple has probably fixed A/UX
|
||||
** by now.
|
||||
** 01I 03feb88 JP Added 32 lines.
|
||||
** 01H 14jan88 JP Added 22 lines.
|
||||
** 01G 05dec87 JP Added random sabotage to generate Hubert.
|
||||
** Defeated text stuff for moons 28 or larger.
|
||||
** 01F 13oct87 JP Added pumpkin19 in October. Added hubert29.
|
||||
** 01E 14may87 JP Added #ifdef SINFORCOS to handle broken A/UX library.
|
||||
** 01D 02apr87 JP Added 21 lines.
|
||||
** 01C 26jan87 JP Added backgrounds for 29 and 18 lines.
|
||||
** 01B 28dec86 JP Added -l flag, and backgrounds for 19 and 24 lines.
|
||||
** 01A 08nov86 JP Translated from the ratfor version of 12nov85, which itself
|
||||
** was translated from the Pascal version of 05apr79.
|
||||
**
|
||||
** Copyright (C) 1986,1987,1988,1995 by Jef Poskanzer <jef@mail.acme.com>.
|
||||
** All rights reserved.
|
||||
**
|
||||
** Redistribution and use in source and binary forms, with or without
|
||||
** modification, are permitted provided that the following conditions
|
||||
** are met:
|
||||
** 1. Redistributions of source code must retain the above copyright
|
||||
** notice, this list of conditions and the following disclaimer.
|
||||
** 2. Redistributions in binary form must reproduce the above copyright
|
||||
** notice, this list of conditions and the following disclaimer in the
|
||||
** documentation and/or other materials provided with the distribution.
|
||||
**
|
||||
** THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||
** ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
** IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
** ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||
** FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
** DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
** OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
** HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
** OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
** SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <math.h>
|
||||
#include <time.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "date_parse.h"
|
||||
#include "astro.h"
|
||||
|
||||
|
||||
/* Global defines and declarations. */
|
||||
|
||||
#define SECSPERMINUTE 60
|
||||
#define SECSPERHOUR (60 * SECSPERMINUTE)
|
||||
#define SECSPERDAY (24 * SECSPERHOUR)
|
||||
|
||||
#define PI 3.1415926535897932384626433
|
||||
|
||||
#define DEFAULTNUMLINES 23
|
||||
|
||||
#define QUARTERLITLEN 16
|
||||
#define QUARTERLITLENPLUSONE 17
|
||||
|
||||
/* If you change the aspect ratio, the canned backgrounds won't work. */
|
||||
#define ASPECTRATIO 0.5
|
||||
|
||||
|
||||
static void
|
||||
putseconds( long secs )
|
||||
{
|
||||
long days, hours, minutes;
|
||||
|
||||
days = secs / SECSPERDAY;
|
||||
secs = secs - days * SECSPERDAY;
|
||||
hours = secs / SECSPERHOUR;
|
||||
secs = secs - hours * SECSPERHOUR;
|
||||
minutes = secs / SECSPERMINUTE;
|
||||
secs = secs - minutes * SECSPERMINUTE;
|
||||
|
||||
printf( "%ld %2ld:%02ld:%02ld", days, hours, minutes, secs );
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
putmoon( time_t t, int numlines, char* atfiller )
|
||||
{
|
||||
static char background18[18][37] = {
|
||||
" .----------. ",
|
||||
" .--' o . `--. ",
|
||||
" .'@ @@@@@@ O . . `. ",
|
||||
" .'@@ @@@@@@@@ @@@@ . `. ",
|
||||
" .' . @@@@@@@@ @@@@@@ . `. ",
|
||||
" / @@ o @@@@@@. @@@@ O @\\ ",
|
||||
" |@@@@ @@@@@@ @@| ",
|
||||
" / @@@@@ `.-. . @@@@@@@@ . @@\\",
|
||||
" | @@@@ --`-' . o @@@@@@@ |",
|
||||
" |@ @@ @@@@@@ @@@ |",
|
||||
" \\ @@ @ . () @@ @@@@@ /",
|
||||
" | @ @@@ @@@ @@@ | ",
|
||||
" \\ . @@ @\\ . . @@ o / ",
|
||||
" `. @@@@ _\\ / . o .' ",
|
||||
" `. @@ ()--- .' ",
|
||||
" `. / | . o .' ",
|
||||
" `--./ . .--' ",
|
||||
" `----------' "};
|
||||
static char background19[19][39] = {
|
||||
" .----------. ",
|
||||
" .--' o . `--. ",
|
||||
" .-'@ @@@@@@ O . . `-. ",
|
||||
" .' @@ @@@@@@@@ @@@@ . `. ",
|
||||
" / . @@@@@@@@ @@@@@@ . \\ ",
|
||||
" /@@ o @@@@@@. @@@@ O @\\ ",
|
||||
" /@@@@ @@@@@@ @@@\\ ",
|
||||
" . @@@@@ `.-./ . @@@@@@@@ . @@ .",
|
||||
" | @@@@ --`-' . @@@@@@@ |",
|
||||
" |@ @@ ` o @@@@@@ @@@@ |",
|
||||
" | @@ o @@ @@@@@@ |",
|
||||
" ` . @ @@ () @@@ @@@@ '",
|
||||
" \\ @@ @@@@ . @@ . o / ",
|
||||
" \\ @@@@ @@\\ . o / ",
|
||||
" \\ . @@ _\\ / . .-. / ",
|
||||
" `. . ()--- `-' .' ",
|
||||
" `-. ./ | . o .-' ",
|
||||
" `--./ . .--' ",
|
||||
" `----------' "};
|
||||
static char pumpkin19[19][39] = {
|
||||
" @@@@@@@@@@@@ ",
|
||||
" @@@@@@@@@@@@@@@@@@@@ ",
|
||||
" @@@@@@@@@@@@@@@@@@@@@@@@@@ ",
|
||||
" @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ ",
|
||||
" @@@@ @@@@@@@@ @@@@ ",
|
||||
" @@@@@@ @@@@@@@@@@ @@@@@@ ",
|
||||
" @@@@@@@@ @@@@@@@@@@@@ @@@@@@@@ ",
|
||||
" @@@@@@@@@@ @@@@@@ @@@@@@ @@@@@@@@@@",
|
||||
" @@@@@@@@@@@@@@@@@ @@@@@@@@@@@@@@@@@",
|
||||
" @@@@@@@@@@@@@@@@ @@@@@@@@@@@@@@@@",
|
||||
" @@@@@@@@@@@@@@@ @@@@@@@@@@@@@@@",
|
||||
" @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@",
|
||||
" @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ ",
|
||||
" @@@@@ @@ @@@@@ ",
|
||||
" @@@@@@ @@@@@@ ",
|
||||
" @@@@@@@@ @@@@@@@@ ",
|
||||
" @@@@@@@@@ @@@@@@@@@ ",
|
||||
" @@@@@@@@@@@@@@@@@@@@ ",
|
||||
" @@@@@@@@@@@@ "};
|
||||
static char background21[21][43] = {
|
||||
" .----------. ",
|
||||
" .---' O . . `---. ",
|
||||
" .-'@ @@@@@@ . @@@@@ `-. ",
|
||||
" .'@@ @@@@@@@@@ @@@@@@@ . `. ",
|
||||
" / o @@@@@@@@@ @@@@@@@ . \\ ",
|
||||
" /@ o @@@@@@@@@. @@@@@@@ O \\ ",
|
||||
" /@@@ . @@@@@@o @@@@@@@@@@ @@\\ ",
|
||||
" /@@@@@ . @@@@@@@@@@@@@ o @@@\\ ",
|
||||
" .@@@@@ O `.-./ . @@@@@@@@@@@@ @@ .",
|
||||
" | @@@@ --`-' o @@@@@@@@ @@@@ |",
|
||||
" |@ @@@ ` o . @@ . @@@@@@@ |",
|
||||
" | @@ @ .-. @@@ @@@@@@@ |",
|
||||
" ` . @ @@@ `-' . @@@@ @@@@ o '",
|
||||
" \\ @@ @@@@@ . @@ . / ",
|
||||
" \\ @@@@ @\\@@ / . O . o . / ",
|
||||
" \\o @@ \\ \\ / . . / ",
|
||||
" \\ . .\\.-.___ . . .-. / ",
|
||||
" `. `-' `-'.' ",
|
||||
" `-. o / | o O . .-' ",
|
||||
" `---. . . .---' ",
|
||||
" `----------' "};
|
||||
static char background22[22][45] = {
|
||||
" .------------. ",
|
||||
" .--' o . . `--. ",
|
||||
" .-' . O . . `-. ",
|
||||
" .'@ @@@@@@@ . @@@@@ `. ",
|
||||
" .'@@@ @@@@@@@@@@@ @@@@@@@ . `. ",
|
||||
" / o @@@@@@@@@@@ @@@@@@@ . \\ ",
|
||||
" /@@ o @@@@@@@@@@@. @@@@@@@ O \\ ",
|
||||
" /@@@@ . @@@@@@@o @@@@@@@@@@ @@@\\ ",
|
||||
" |@@@@@ . @@@@@@@@@@@@ @@@@| ",
|
||||
" /@@@@@ O `.-./ . @@@@@@@@@@@ @@ \\",
|
||||
" | @@@@ --`-' o . @@@@@@@ @@@@ |",
|
||||
" |@ @@@ @@ @ ` o .-. @@ . @@@@@@ |",
|
||||
" \\ @@@ `-' . @@@ @@@@@@ /",
|
||||
" | . @ @@ @@@@@ . @@@@ @@@ o | ",
|
||||
" \\ @@@@ @\\@@ / . O @@ . . / ",
|
||||
" \\ o @@ \\ \\ / . . o / ",
|
||||
" \\ . .\\.-.___ . . . .-. / ",
|
||||
" `. `-' `-' .' ",
|
||||
" `. o / | o O . .' ",
|
||||
" `-. / . . .-' ",
|
||||
" `--. . .--' ",
|
||||
" `------------' "};
|
||||
static char background23[23][47] = {
|
||||
" .------------. ",
|
||||
" .--' o . . `--. ",
|
||||
" .-' . O . . `-. ",
|
||||
" .-'@ @@@@@@@ . @@@@@ `-. ",
|
||||
" /@@@ @@@@@@@@@@@ @@@@@@@ . \\ ",
|
||||
" ./ o @@@@@@@@@@@ @@@@@@@ . \\. ",
|
||||
" /@@ o @@@@@@@@@@@. @@@@@@@ O \\ ",
|
||||
" /@@@@ . @@@@@@@o @@@@@@@@@@ @@@ \\ ",
|
||||
" |@@@@@ . @@@@@@@@@@@@@ o @@@@| ",
|
||||
" /@@@@@ O `.-./ . @@@@@@@@@@@@ @@ \\",
|
||||
" | @@@@ --`-' o @@@@@@@@ @@@@ |",
|
||||
" |@ @@@ ` o . @@ . @@@@@@@ |",
|
||||
" | @@ @ .-. @@@ @@@@@@@ |",
|
||||
" \\ . @ @@@ `-' . @@@@ @@@@ o /",
|
||||
" | @@ @@@@@ . @@ . | ",
|
||||
" \\ @@@@ @\\@@ / . O . o . / ",
|
||||
" \\ o @@ \\ \\ / . . / ",
|
||||
" `\\ . .\\.-.___ . . .-. /' ",
|
||||
" \\ `-' `-' / ",
|
||||
" `-. o / | o O . .-' ",
|
||||
" `-. / . . .-' ",
|
||||
" `--. . .--' ",
|
||||
" `------------' "};
|
||||
|
||||
static char background24[24][49] = {
|
||||
" .------------. ",
|
||||
" .---' o . . `---. ",
|
||||
" .-' . O . . `-. ",
|
||||
" .'@ @@@@@@@ . @@@@@ `. ",
|
||||
" .'@@ @@@@@@@@@@@ @@@@@@@ . `. ",
|
||||
" / o @@@@@@@@@@@ @@@@@@@ . \\ ",
|
||||
" /@ o @@@@@@@@@@@. @@@@@@@ O \\ ",
|
||||
" /@@@ . @@@@@@@o @@@@@@@@@@ @@@ \\ ",
|
||||
" /@@@@@ . @@@@@@@@@@@@@ o @@@@ \\ ",
|
||||
" |@@@@ O `.-./ . @@@@@@@@@@@@ @@ | ",
|
||||
" / @@@@ --`-' o @@@@@@@@ @@@@ \\",
|
||||
" |@ @@@ @ ` . @@ @@@@@@@ |",
|
||||
" | @ o @ @@@@@@@ |",
|
||||
" \\ @@ .-. @@@ @@@@ o /",
|
||||
" | . @ @@@ `-' . @@@@ | ",
|
||||
" \\ @@ @@@@@ . @@ . / ",
|
||||
" \\ @@@@ @\\@@ / . O . o . / ",
|
||||
" \\ o @@ \\ \\ / . . / ",
|
||||
" \\ . .\\.-.___ . . .-. / ",
|
||||
" `. `-' `-' .' ",
|
||||
" `. o / | o O . .' ",
|
||||
" `-. / . . .-' ",
|
||||
" `---. . .---' ",
|
||||
" `------------' "};
|
||||
static char background29[29][59] = {
|
||||
" .--------------. ",
|
||||
" .---' o . `---. ",
|
||||
" .-' . O . . `-. ",
|
||||
" .-' @@@@@@ . `-. ",
|
||||
" .'@@ @@@@@@@@@@@ @@@@@@@ . `. ",
|
||||
" .'@@@ @@@@@@@@@@@@@@ @@@@@@@@@ `. ",
|
||||
" /@@@ o @@@@@@@@@@@@@@ @@@@@@@@@ O \\ ",
|
||||
" / @@@@@@@@@@@@@@ @ @@@@@@@@@ @@ . \\ ",
|
||||
" /@ o @@@@@@@@@@@ . @@ @@@@@@@@@@@ @@ \\ ",
|
||||
" /@@@ . @@@@@@ o @ @@@@@@@@@@@@@ o @@@@ \\ ",
|
||||
" /@@@@@ @ . @@@@@@@@@@@@@@ @@@@@ \\ ",
|
||||
" |@@@@@ O `.-./ . . @@@@@@@@@@@@@ @@@ | ",
|
||||
" / @@@@@ --`-' o @@@@@@@@@@@ @@@ . \\",
|
||||
" |@ @@@@ . @ @ ` @ @@ . @@@@@@ |",
|
||||
" | @@ o @@ . @@@@@@ |",
|
||||
" | . @ @ @ o @@ o @@@@@@. |",
|
||||
" \\ @ @ @ .-. @@@@ @@@ /",
|
||||
" | @ @ @ `-' . @@@@ . . | ",
|
||||
" \\ . o @ @@@@ . @@ . . / ",
|
||||
" \\ @@@ @@@@@@ . o / ",
|
||||
" \\ @@@@@ @@\\@@ / O . / ",
|
||||
" \\ o @@@ \\ \\ / __ . . .--. / ",
|
||||
" \\ . . \\.-.--- `--' / ",
|
||||
" `. `-' . .' ",
|
||||
" `. o / | ` O . .' ",
|
||||
" `-. / | o .-' ",
|
||||
" `-. . . .-' ",
|
||||
" `---. . .---' ",
|
||||
" `--------------' "};
|
||||
static char hubert29[29][59] = {
|
||||
" .--------------. ",
|
||||
" .---' o . `---. ",
|
||||
" .-' . O . . `-. ",
|
||||
" .-' @@@@@@ . `-. ",
|
||||
" .'@@ @@@@@@@@@@@ @@@@@@@ . `. ",
|
||||
" .'@@@ @@@@@ ___====-_ _-====___ @ `. ",
|
||||
" /@@@ o _--~~~#####// \\\\#####~~~--_ O \\ ",
|
||||
" / _-~##########// ( ) \\\\##########~-_ . \\ ",
|
||||
" /@ o -############// :\\^^/: \\\\############- @@ \\ ",
|
||||
" /@@@ _~############// (@::@) \\\\############~_ @@ \\ ",
|
||||
" /@@@ ~#############(( \\\\// ))#############~ @@ \\ ",
|
||||
" |@@ -###############\\\\ (oo) //###############- @ | ",
|
||||
" / @ -#################\\\\ / \"\" \\ //#################- . \\",
|
||||
" |@ -###################\\\\/ \\//###################- |",
|
||||
" | _#/:##########/\\######( /\\ )######/\\##########:\\#_ |",
|
||||
" | :/ :#/\\#/\\#/\\/ \\#/\\##\\ : : /##/\\#/ \\/\\#/\\#/\\#: \\: |",
|
||||
" \\ \" :/ V V \" V \\#\\: : : :/#/ V \" V V \\: \" /",
|
||||
" | @ \" \" \" \" / : : : : \\ \" \" \" \" | ",
|
||||
" \\ . o @ @@@@ ( : : : : ) @@ . . / ",
|
||||
" \\ @@@ @@@@ __\\ : : : : /__ o / ",
|
||||
" \\ @@@@@ @@\\@(vvv(VVV)(VVV)vvv) . / ",
|
||||
" \\ o @@@ \\ \\ / __ . . .--. / ",
|
||||
" \\ . . \\.-.--- `--' / ",
|
||||
" `. `-' . .' ",
|
||||
" `. o / | ` O . .' ",
|
||||
" `-. / | o .-' ",
|
||||
" `-. . . .-' ",
|
||||
" `---. . .---' ",
|
||||
" `--------------' "};
|
||||
static char background32[32][65] = {
|
||||
" .--------------. ",
|
||||
" .----' o . `----. ",
|
||||
" .-' . O . . `-. ",
|
||||
" .-' @@@@@@ . `-. ",
|
||||
" .'@ @@@@@@@@@@@ @@@@@@@@ . `. ",
|
||||
" .'@@ @@@@@@@@@@@@@@ @@@@@@@@@@ `. ",
|
||||
" .'@@@ o @@@@@@@@@@@@@@ @@@@@@@@@@ o `. ",
|
||||
" /@@@ @@@@@@@@@@@@@@ @ @@@@@@@@@@ @@ . \\ ",
|
||||
" / @@@@@@@@@@@ . @@ @@@@@@@@@@@@ @@ \\ ",
|
||||
" /@ o . @@@@@@ o @ @@@@@@@@@@@@@@ o @@@@ \\ ",
|
||||
" /@@@ . @@@@@@@@@@@@@@@ @@@@@ \\ ",
|
||||
" /@@@@@ @ . @@@@@@@@@@@@@@ @@@ \\ ",
|
||||
" |@@@@@ o `.-./ . @@@@@@@@@@@@ @@@ . | ",
|
||||
" / @@@@@ __`-' o @@ . @@@@@@ \\",
|
||||
" |@ @@@@ . @ ` @ @@ . @@@@@@ |",
|
||||
" | @@ @ o @@@ o @@@@@@. |",
|
||||
" | @ @@@@@ @@@ |",
|
||||
" | . . @ @ @ o @@@@@ . . |",
|
||||
" \\ @ .-. . @@@ . . /",
|
||||
" | @ @ @ @ `-' . / ",
|
||||
" \\ . @ @ . o / ",
|
||||
" \\ o @@@@ . . / ",
|
||||
" \\ @@@ @@@@@@ . o / ",
|
||||
" \\ @@@@@ @@\\@@ / o . / ",
|
||||
" \\ o @@@ \\ \\ / ___ . . .--. / ",
|
||||
" `. . \\.-.--- `--' .' ",
|
||||
" `. `-' . .' ",
|
||||
" `. o / | O . .' ",
|
||||
" `-. / | o .-' ",
|
||||
" `-. . . .-' ",
|
||||
" `----. . .----' ",
|
||||
" `--------------' "};
|
||||
|
||||
static char qlits[4][16] = {
|
||||
"New Moon + ",
|
||||
"First Quarter +",
|
||||
"Full Moon + ",
|
||||
"Last Quarter + ",
|
||||
};
|
||||
static char nqlits[4][16] = {
|
||||
"New Moon - ",
|
||||
"First Quarter -",
|
||||
"Full Moon - ",
|
||||
"Last Quarter - ",
|
||||
};
|
||||
|
||||
struct tm* tmP;
|
||||
double jd, pctphase, angphase, cphase, aom, cdist, cangdia, csund, csuang;
|
||||
double phases[2], which[2];
|
||||
long clocknow;
|
||||
int atflrlen, atflridx, numcols, lin, col, midlin;
|
||||
double mcap, yrad, xrad, y, xright, xleft;
|
||||
int colright, colleft;
|
||||
char c;
|
||||
|
||||
/* Find the length of the atfiller string. */
|
||||
atflrlen = strlen( atfiller );
|
||||
|
||||
/* Figure out the phase. */
|
||||
jd = unix_to_julian( t );
|
||||
pctphase = phase( jd, &cphase, &aom, &cdist, &cangdia, &csund, &csuang );
|
||||
angphase = pctphase * 2.0 * PI;
|
||||
mcap = -cos( angphase );
|
||||
|
||||
/* Get now for use as a random number. */
|
||||
(void) time( &clocknow );
|
||||
|
||||
/* Randomly cheat and generate Hubert. */
|
||||
if ( clocknow % 13 == 3 && cphase > 0.8 )
|
||||
{
|
||||
numlines = 29;
|
||||
clocknow = 3;
|
||||
}
|
||||
|
||||
/* Figure out how big the moon is. */
|
||||
yrad = numlines / 2.0;
|
||||
xrad = yrad / ASPECTRATIO;
|
||||
numcols = xrad * 2;
|
||||
|
||||
/* Figure out some other random stuff. */
|
||||
midlin = numlines / 2;
|
||||
phasehunt2( jd, phases, which );
|
||||
|
||||
/* Now output the moon, a slice at a time. */
|
||||
atflridx = 0;
|
||||
for ( lin = 0; lin < numlines; lin = lin + 1 )
|
||||
{
|
||||
/* Compute the edges of this slice. */
|
||||
y = lin + 0.5 - yrad;
|
||||
xright = xrad * sqrt( 1.0 - ( y * y ) / ( yrad * yrad ) );
|
||||
xleft = -xright;
|
||||
if ( angphase >= 0.0 && angphase < PI )
|
||||
xleft = mcap * xleft;
|
||||
else
|
||||
xright = mcap * xright;
|
||||
colleft = (int) (xrad + 0.5) + (int) (xleft + 0.5);
|
||||
colright = (int) (xrad + 0.5) + (int) (xright + 0.5);
|
||||
|
||||
/* Now output the slice. */
|
||||
for ( col = 0; col < colleft; ++col )
|
||||
putchar( ' ' );
|
||||
for ( ; col <= colright; ++col )
|
||||
{
|
||||
switch ( numlines )
|
||||
{
|
||||
case 18:
|
||||
c = background18[lin][col];
|
||||
break;
|
||||
case 19:
|
||||
tmP = localtime( &t );
|
||||
if ( tmP->tm_mon == 9 &&
|
||||
clocknow % ( 33 - tmP->tm_mday ) == 1 )
|
||||
c = pumpkin19[lin][col];
|
||||
else
|
||||
c = background19[lin][col];
|
||||
break;
|
||||
case 21:
|
||||
c = background21[lin][col];
|
||||
break;
|
||||
case 22:
|
||||
c = background22[lin][col];
|
||||
break;
|
||||
case 23:
|
||||
c = background23[lin][col];
|
||||
break;
|
||||
case 24:
|
||||
c = background24[lin][col];
|
||||
break;
|
||||
case 29:
|
||||
if ( clocknow % 23 == 3 )
|
||||
c = hubert29[lin][col];
|
||||
else
|
||||
c = background29[lin][col];
|
||||
break;
|
||||
case 32:
|
||||
c = background32[lin][col];
|
||||
break;
|
||||
default:
|
||||
c = '@';
|
||||
}
|
||||
if ( c != '@' )
|
||||
putchar( c );
|
||||
else
|
||||
{
|
||||
putchar( atfiller[atflridx] );
|
||||
atflridx = ( atflridx + 1 ) % atflrlen;
|
||||
}
|
||||
}
|
||||
#ifdef notdef
|
||||
for ( ; col <= numcols; ++col )
|
||||
putchar( ' ' );
|
||||
#endif /* notdef */
|
||||
|
||||
if ( numlines <= 27 )
|
||||
{
|
||||
/* Output the end-of-line information, if any. */
|
||||
if ( lin == midlin - 2 )
|
||||
{
|
||||
fputs( "\t ", stdout );
|
||||
fputs( qlits[(int) (which[0] * 4.0 + 0.001)], stdout );
|
||||
}
|
||||
else if ( lin == midlin - 1)
|
||||
{
|
||||
fputs( "\t ", stdout );
|
||||
putseconds( (int) ( ( jd - phases[0] ) * SECSPERDAY ) );
|
||||
}
|
||||
else if ( lin == midlin )
|
||||
{
|
||||
fputs( "\t ", stdout );
|
||||
fputs( nqlits[(int) (which[1] * 4.0 + 0.001)], stdout );
|
||||
}
|
||||
else if ( lin == midlin + 1 )
|
||||
{
|
||||
fputs( "\t ", stdout );
|
||||
putseconds( (int) ( ( phases[1] - jd ) * SECSPERDAY ) );
|
||||
}
|
||||
}
|
||||
|
||||
putchar( '\n' );
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
/* Main program. */
|
||||
|
||||
int
|
||||
main( int argc, char** argv )
|
||||
{
|
||||
time_t t;
|
||||
char buf[100];
|
||||
int numlines, argn;
|
||||
char* usage = "usage: %s [-l <lines>] [<date/time>]\n";
|
||||
|
||||
/* Parge args. */
|
||||
argn = 1;
|
||||
/* Check for -l flag. */
|
||||
numlines = DEFAULTNUMLINES;
|
||||
if ( argc - argn >= 1 )
|
||||
{
|
||||
if ( argv[argn][0] == '-' )
|
||||
{
|
||||
if ( argv[argn][1] != 'l' || argv[argn][2] != '\0' )
|
||||
{
|
||||
fprintf( stderr, usage, argv[0] );
|
||||
exit( 1 );
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( argc - argn < 2 )
|
||||
{
|
||||
fprintf( stderr, usage, argv[0] );
|
||||
exit( 1 );
|
||||
}
|
||||
if ( sscanf( argv[argn + 1], "%d", &numlines ) != 1 )
|
||||
{
|
||||
fprintf( stderr, usage, argv[0] );
|
||||
exit( 1 );
|
||||
}
|
||||
argn += 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Figure out what date and time to use. */
|
||||
if ( argc - argn == 0 )
|
||||
{
|
||||
/* No arguments present - use the current date and time. */
|
||||
t = time( (time_t) 0 );
|
||||
}
|
||||
else if ( argc - argn == 1 || argc - argn == 2 || argc - argn == 3 )
|
||||
{
|
||||
/* One, two, or three args - use them. */
|
||||
strcpy( buf, argv[argn] );
|
||||
if ( argc - argn > 1 )
|
||||
{
|
||||
strcat( buf, " " );
|
||||
strcat( buf, argv[argn + 1] );
|
||||
if ( argc - argn > 2 )
|
||||
{
|
||||
strcat( buf, " " );
|
||||
strcat( buf, argv[argn + 2] );
|
||||
}
|
||||
}
|
||||
t = date_parse( buf );
|
||||
if ( t <= 0 )
|
||||
{
|
||||
fprintf( stderr, "illegal date/time: %s\n", buf );
|
||||
exit( 1 );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Too many args! */
|
||||
fprintf( stderr, usage, argv[0] );
|
||||
exit( 1 );
|
||||
}
|
||||
|
||||
/* Pseudo-randomly decide what the moon is made of, and print it. */
|
||||
if ( t % 17 == 3 )
|
||||
putmoon( t, numlines, "GREENCHEESE" );
|
||||
else
|
||||
putmoon( t, numlines, "@" );
|
||||
|
||||
/* All done. */
|
||||
exit( 0 );
|
||||
}
|
||||
|
|
@ -1,522 +0,0 @@
|
|||
/* +++Date last modified: 05-Jul-1997 */
|
||||
/* Updated comments, 05-Aug-2013 */
|
||||
|
||||
/*
|
||||
|
||||
SUNRISET.C - computes Sun rise/set times, start/end of twilight, and
|
||||
the length of the day at any date and latitude
|
||||
|
||||
Written as DAYLEN.C, 1989-08-16
|
||||
|
||||
Modified to SUNRISET.C, 1992-12-01
|
||||
|
||||
(c) Paul Schlyter, 1989, 1992
|
||||
|
||||
Released to the public domain by Paul Schlyter, December 1992
|
||||
|
||||
*/
|
||||
|
||||
|
||||
#include <stdio.h>
|
||||
#include <math.h>
|
||||
|
||||
|
||||
/* A macro to compute the number of days elapsed since 2000 Jan 0.0 */
|
||||
/* (which is equal to 1999 Dec 31, 0h UT) */
|
||||
|
||||
#define days_since_2000_Jan_0(y,m,d) \
|
||||
(367L*(y)-((7*((y)+(((m)+9)/12)))/4)+((275*(m))/9)+(d)-730530L)
|
||||
|
||||
/* Some conversion factors between radians and degrees */
|
||||
|
||||
#ifndef PI
|
||||
#define PI 3.1415926535897932384
|
||||
#endif
|
||||
|
||||
#define RADEG ( 180.0 / PI )
|
||||
#define DEGRAD ( PI / 180.0 )
|
||||
|
||||
/* The trigonometric functions in degrees */
|
||||
|
||||
#define sind(x) sin((x)*DEGRAD)
|
||||
#define cosd(x) cos((x)*DEGRAD)
|
||||
#define tand(x) tan((x)*DEGRAD)
|
||||
|
||||
#define atand(x) (RADEG*atan(x))
|
||||
#define asind(x) (RADEG*asin(x))
|
||||
#define acosd(x) (RADEG*acos(x))
|
||||
#define atan2d(y,x) (RADEG*atan2(y,x))
|
||||
|
||||
|
||||
/* Following are some macros around the "workhorse" function __daylen__ */
|
||||
/* They mainly fill in the desired values for the reference altitude */
|
||||
/* below the horizon, and also selects whether this altitude should */
|
||||
/* refer to the Sun's center or its upper limb. */
|
||||
|
||||
|
||||
/* This macro computes the length of the day, from sunrise to sunset. */
|
||||
/* Sunrise/set is considered to occur when the Sun's upper limb is */
|
||||
/* 35 arc minutes below the horizon (this accounts for the refraction */
|
||||
/* of the Earth's atmosphere). */
|
||||
#define day_length(year,month,day,lon,lat) \
|
||||
__daylen__( year, month, day, lon, lat, -35.0/60.0, 1 )
|
||||
|
||||
/* This macro computes the length of the day, including civil twilight. */
|
||||
/* Civil twilight starts/ends when the Sun's center is 6 degrees below */
|
||||
/* the horizon. */
|
||||
#define day_civil_twilight_length(year,month,day,lon,lat) \
|
||||
__daylen__( year, month, day, lon, lat, -6.0, 0 )
|
||||
|
||||
/* This macro computes the length of the day, incl. nautical twilight. */
|
||||
/* Nautical twilight starts/ends when the Sun's center is 12 degrees */
|
||||
/* below the horizon. */
|
||||
#define day_nautical_twilight_length(year,month,day,lon,lat) \
|
||||
__daylen__( year, month, day, lon, lat, -12.0, 0 )
|
||||
|
||||
/* This macro computes the length of the day, incl. astronomical twilight. */
|
||||
/* Astronomical twilight starts/ends when the Sun's center is 18 degrees */
|
||||
/* below the horizon. */
|
||||
#define day_astronomical_twilight_length(year,month,day,lon,lat) \
|
||||
__daylen__( year, month, day, lon, lat, -18.0, 0 )
|
||||
|
||||
|
||||
/* This macro computes times for sunrise/sunset. */
|
||||
/* Sunrise/set is considered to occur when the Sun's upper limb is */
|
||||
/* 35 arc minutes below the horizon (this accounts for the refraction */
|
||||
/* of the Earth's atmosphere). */
|
||||
#define sun_rise_set(year,month,day,lon,lat,rise,set) \
|
||||
__sunriset__( year, month, day, lon, lat, -35.0/60.0, 1, rise, set )
|
||||
|
||||
/* This macro computes the start and end times of civil twilight. */
|
||||
/* Civil twilight starts/ends when the Sun's center is 6 degrees below */
|
||||
/* the horizon. */
|
||||
#define civil_twilight(year,month,day,lon,lat,start,end) \
|
||||
__sunriset__( year, month, day, lon, lat, -6.0, 0, start, end )
|
||||
|
||||
/* This macro computes the start and end times of nautical twilight. */
|
||||
/* Nautical twilight starts/ends when the Sun's center is 12 degrees */
|
||||
/* below the horizon. */
|
||||
#define nautical_twilight(year,month,day,lon,lat,start,end) \
|
||||
__sunriset__( year, month, day, lon, lat, -12.0, 0, start, end )
|
||||
|
||||
/* This macro computes the start and end times of astronomical twilight. */
|
||||
/* Astronomical twilight starts/ends when the Sun's center is 18 degrees */
|
||||
/* below the horizon. */
|
||||
#define astronomical_twilight(year,month,day,lon,lat,start,end) \
|
||||
__sunriset__( year, month, day, lon, lat, -18.0, 0, start, end )
|
||||
|
||||
|
||||
/* Function prototypes */
|
||||
|
||||
double __daylen__( int year, int month, int day, double lon, double lat,
|
||||
double altit, int upper_limb );
|
||||
|
||||
int __sunriset__( int year, int month, int day, double lon, double lat,
|
||||
double altit, int upper_limb, double *rise, double *set );
|
||||
|
||||
void sunpos( double d, double *lon, double *r );
|
||||
|
||||
void sun_RA_dec( double d, double *RA, double *dec, double *r );
|
||||
|
||||
double revolution( double x );
|
||||
|
||||
double rev180( double x );
|
||||
|
||||
double GMST0( double d );
|
||||
|
||||
|
||||
|
||||
/* A small test program */
|
||||
|
||||
#ifdef SUNRISET_NO_MAIN
|
||||
int sunriset_test_main(void) { return 0; }
|
||||
#else
|
||||
int main(void)
|
||||
{
|
||||
int year,month,day;
|
||||
double lon, lat;
|
||||
double daylen, civlen, nautlen, astrlen;
|
||||
double rise, set, civ_start, civ_end, naut_start, naut_end,
|
||||
astr_start, astr_end;
|
||||
int rs, civ, naut, astr;
|
||||
char buf[80];
|
||||
|
||||
printf( "Longitude (+ is east) and latitude (+ is north) : " );
|
||||
fgets(buf, 80, stdin);
|
||||
sscanf(buf, "%lf %lf", &lon, &lat );
|
||||
|
||||
for(;;)
|
||||
{
|
||||
printf( "Input date ( yyyy mm dd ) (ctrl-C exits): " );
|
||||
fgets(buf, 80, stdin);
|
||||
sscanf(buf, "%d %d %d", &year, &month, &day );
|
||||
|
||||
daylen = day_length(year,month,day,lon,lat);
|
||||
civlen = day_civil_twilight_length(year,month,day,lon,lat);
|
||||
nautlen = day_nautical_twilight_length(year,month,day,lon,lat);
|
||||
astrlen = day_astronomical_twilight_length(year,month,day,
|
||||
lon,lat);
|
||||
|
||||
printf( "Day length: %5.2f hours\n", daylen );
|
||||
printf( "With civil twilight %5.2f hours\n", civlen );
|
||||
printf( "With nautical twilight %5.2f hours\n", nautlen );
|
||||
printf( "With astronomical twilight %5.2f hours\n", astrlen );
|
||||
printf( "Length of twilight: civil %5.2f hours\n",
|
||||
(civlen-daylen)/2.0);
|
||||
printf( " nautical %5.2f hours\n",
|
||||
(nautlen-daylen)/2.0);
|
||||
printf( " astronomical %5.2f hours\n",
|
||||
(astrlen-daylen)/2.0);
|
||||
|
||||
rs = sun_rise_set ( year, month, day, lon, lat,
|
||||
&rise, &set );
|
||||
civ = civil_twilight ( year, month, day, lon, lat,
|
||||
&civ_start, &civ_end );
|
||||
naut = nautical_twilight ( year, month, day, lon, lat,
|
||||
&naut_start, &naut_end );
|
||||
astr = astronomical_twilight( year, month, day, lon, lat,
|
||||
&astr_start, &astr_end );
|
||||
|
||||
printf( "Sun at south %5.2fh UT\n", (rise+set)/2.0 );
|
||||
|
||||
switch( rs )
|
||||
{
|
||||
case 0:
|
||||
printf( "Sun rises %5.2fh UT, sets %5.2fh UT\n",
|
||||
rise, set );
|
||||
break;
|
||||
case +1:
|
||||
printf( "Sun above horizon\n" );
|
||||
break;
|
||||
case -1:
|
||||
printf( "Sun below horizon\n" );
|
||||
break;
|
||||
}
|
||||
|
||||
switch( civ )
|
||||
{
|
||||
case 0:
|
||||
printf( "Civil twilight starts %5.2fh, "
|
||||
"ends %5.2fh UT\n", civ_start, civ_end );
|
||||
break;
|
||||
case +1:
|
||||
printf( "Never darker than civil twilight\n" );
|
||||
break;
|
||||
case -1:
|
||||
printf( "Never as bright as civil twilight\n" );
|
||||
break;
|
||||
}
|
||||
|
||||
switch( naut )
|
||||
{
|
||||
case 0:
|
||||
printf( "Nautical twilight starts %5.2fh, "
|
||||
"ends %5.2fh UT\n", naut_start, naut_end );
|
||||
break;
|
||||
case +1:
|
||||
printf( "Never darker than nautical twilight\n" );
|
||||
break;
|
||||
case -1:
|
||||
printf( "Never as bright as nautical twilight\n" );
|
||||
break;
|
||||
}
|
||||
|
||||
switch( astr )
|
||||
{
|
||||
case 0:
|
||||
printf( "Astronomical twilight starts %5.2fh, "
|
||||
"ends %5.2fh UT\n", astr_start, astr_end );
|
||||
break;
|
||||
case +1:
|
||||
printf( "Never darker than astronomical twilight\n" );
|
||||
break;
|
||||
case -1:
|
||||
printf( "Never as bright as astronomical twilight\n" );
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
/* The "workhorse" function for sun rise/set times */
|
||||
|
||||
int __sunriset__( int year, int month, int day, double lon, double lat,
|
||||
double altit, int upper_limb, double *trise, double *tset )
|
||||
/***************************************************************************/
|
||||
/* Note: year,month,date = calendar date, 1801-2099 only. */
|
||||
/* Eastern longitude positive, Western longitude negative */
|
||||
/* Northern latitude positive, Southern latitude negative */
|
||||
/* The longitude value IS critical in this function! */
|
||||
/* altit = the altitude which the Sun should cross */
|
||||
/* Set to -35/60 degrees for rise/set, -6 degrees */
|
||||
/* for civil, -12 degrees for nautical and -18 */
|
||||
/* degrees for astronomical twilight. */
|
||||
/* upper_limb: non-zero -> upper limb, zero -> center */
|
||||
/* Set to non-zero (e.g. 1) when computing rise/set */
|
||||
/* times, and to zero when computing start/end of */
|
||||
/* twilight. */
|
||||
/* *rise = where to store the rise time */
|
||||
/* *set = where to store the set time */
|
||||
/* Both times are relative to the specified altitude, */
|
||||
/* and thus this function can be used to compute */
|
||||
/* various twilight times, as well as rise/set times */
|
||||
/* Return value: 0 = sun rises/sets this day, times stored at */
|
||||
/* *trise and *tset. */
|
||||
/* +1 = sun above the specified "horizon" 24 hours. */
|
||||
/* *trise set to time when the sun is at south, */
|
||||
/* minus 12 hours while *tset is set to the south */
|
||||
/* time plus 12 hours. "Day" length = 24 hours */
|
||||
/* -1 = sun is below the specified "horizon" 24 hours */
|
||||
/* "Day" length = 0 hours, *trise and *tset are */
|
||||
/* both set to the time when the sun is at south. */
|
||||
/* */
|
||||
/**********************************************************************/
|
||||
{
|
||||
double d, /* Days since 2000 Jan 0.0 (negative before) */
|
||||
sr, /* Solar distance, astronomical units */
|
||||
sRA, /* Sun's Right Ascension */
|
||||
sdec, /* Sun's declination */
|
||||
sradius, /* Sun's apparent radius */
|
||||
t, /* Diurnal arc */
|
||||
tsouth, /* Time when Sun is at south */
|
||||
sidtime; /* Local sidereal time */
|
||||
|
||||
int rc = 0; /* Return cde from function - usually 0 */
|
||||
|
||||
/* Compute d of 12h local mean solar time */
|
||||
d = days_since_2000_Jan_0(year,month,day) + 0.5 - lon/360.0;
|
||||
|
||||
/* Compute the local sidereal time of this moment */
|
||||
sidtime = revolution( GMST0(d) + 180.0 + lon );
|
||||
|
||||
/* Compute Sun's RA, Decl and distance at this moment */
|
||||
sun_RA_dec( d, &sRA, &sdec, &sr );
|
||||
|
||||
/* Compute time when Sun is at south - in hours UT */
|
||||
tsouth = 12.0 - rev180(sidtime - sRA)/15.0;
|
||||
|
||||
/* Compute the Sun's apparent radius in degrees */
|
||||
sradius = 0.2666 / sr;
|
||||
|
||||
/* Do correction to upper limb, if necessary */
|
||||
if ( upper_limb )
|
||||
altit -= sradius;
|
||||
|
||||
/* Compute the diurnal arc that the Sun traverses to reach */
|
||||
/* the specified altitude altit: */
|
||||
{
|
||||
double cost;
|
||||
cost = ( sind(altit) - sind(lat) * sind(sdec) ) /
|
||||
( cosd(lat) * cosd(sdec) );
|
||||
if ( cost >= 1.0 )
|
||||
rc = -1, t = 0.0; /* Sun always below altit */
|
||||
else if ( cost <= -1.0 )
|
||||
rc = +1, t = 12.0; /* Sun always above altit */
|
||||
else
|
||||
t = acosd(cost)/15.0; /* The diurnal arc, hours */
|
||||
}
|
||||
|
||||
/* Store rise and set times - in hours UT */
|
||||
*trise = tsouth - t;
|
||||
*tset = tsouth + t;
|
||||
|
||||
return rc;
|
||||
} /* __sunriset__ */
|
||||
|
||||
|
||||
|
||||
/* The "workhorse" function */
|
||||
|
||||
|
||||
double __daylen__( int year, int month, int day, double lon, double lat,
|
||||
double altit, int upper_limb )
|
||||
/**********************************************************************/
|
||||
/* Note: year,month,date = calendar date, 1801-2099 only. */
|
||||
/* Eastern longitude positive, Western longitude negative */
|
||||
/* Northern latitude positive, Southern latitude negative */
|
||||
/* The longitude value is not critical. Set it to the correct */
|
||||
/* longitude if you're picky, otherwise set to to, say, 0.0 */
|
||||
/* The latitude however IS critical - be sure to get it correct */
|
||||
/* altit = the altitude which the Sun should cross */
|
||||
/* Set to -35/60 degrees for rise/set, -6 degrees */
|
||||
/* for civil, -12 degrees for nautical and -18 */
|
||||
/* degrees for astronomical twilight. */
|
||||
/* upper_limb: non-zero -> upper limb, zero -> center */
|
||||
/* Set to non-zero (e.g. 1) when computing day length */
|
||||
/* and to zero when computing day+twilight length. */
|
||||
/**********************************************************************/
|
||||
{
|
||||
double d, /* Days since 2000 Jan 0.0 (negative before) */
|
||||
obl_ecl, /* Obliquity (inclination) of Earth's axis */
|
||||
sr, /* Solar distance, astronomical units */
|
||||
slon, /* True solar longitude */
|
||||
sin_sdecl, /* Sine of Sun's declination */
|
||||
cos_sdecl, /* Cosine of Sun's declination */
|
||||
sradius, /* Sun's apparent radius */
|
||||
t; /* Diurnal arc */
|
||||
|
||||
/* Compute d of 12h local mean solar time */
|
||||
d = days_since_2000_Jan_0(year,month,day) + 0.5 - lon/360.0;
|
||||
|
||||
/* Compute obliquity of ecliptic (inclination of Earth's axis) */
|
||||
obl_ecl = 23.4393 - 3.563E-7 * d;
|
||||
|
||||
/* Compute Sun's ecliptic longitude and distance */
|
||||
sunpos( d, &slon, &sr );
|
||||
|
||||
/* Compute sine and cosine of Sun's declination */
|
||||
sin_sdecl = sind(obl_ecl) * sind(slon);
|
||||
cos_sdecl = sqrt( 1.0 - sin_sdecl * sin_sdecl );
|
||||
|
||||
/* Compute the Sun's apparent radius, degrees */
|
||||
sradius = 0.2666 / sr;
|
||||
|
||||
/* Do correction to upper limb, if necessary */
|
||||
if ( upper_limb )
|
||||
altit -= sradius;
|
||||
|
||||
/* Compute the diurnal arc that the Sun traverses to reach */
|
||||
/* the specified altitude altit: */
|
||||
{
|
||||
double cost;
|
||||
cost = ( sind(altit) - sind(lat) * sin_sdecl ) /
|
||||
( cosd(lat) * cos_sdecl );
|
||||
if ( cost >= 1.0 )
|
||||
t = 0.0; /* Sun always below altit */
|
||||
else if ( cost <= -1.0 )
|
||||
t = 24.0; /* Sun always above altit */
|
||||
else t = (2.0/15.0) * acosd(cost); /* The diurnal arc, hours */
|
||||
}
|
||||
return t;
|
||||
} /* __daylen__ */
|
||||
|
||||
|
||||
/* This function computes the Sun's position at any instant */
|
||||
|
||||
void sunpos( double d, double *lon, double *r )
|
||||
/******************************************************/
|
||||
/* Computes the Sun's ecliptic longitude and distance */
|
||||
/* at an instant given in d, number of days since */
|
||||
/* 2000 Jan 0.0. The Sun's ecliptic latitude is not */
|
||||
/* computed, since it's always very near 0. */
|
||||
/******************************************************/
|
||||
{
|
||||
double M, /* Mean anomaly of the Sun */
|
||||
w, /* Mean longitude of perihelion */
|
||||
/* Note: Sun's mean longitude = M + w */
|
||||
e, /* Eccentricity of Earth's orbit */
|
||||
E, /* Eccentric anomaly */
|
||||
x, y, /* x, y coordinates in orbit */
|
||||
v; /* True anomaly */
|
||||
|
||||
/* Compute mean elements */
|
||||
M = revolution( 356.0470 + 0.9856002585 * d );
|
||||
w = 282.9404 + 4.70935E-5 * d;
|
||||
e = 0.016709 - 1.151E-9 * d;
|
||||
|
||||
/* Compute true longitude and radius vector */
|
||||
E = M + e * RADEG * sind(M) * ( 1.0 + e * cosd(M) );
|
||||
x = cosd(E) - e;
|
||||
y = sqrt( 1.0 - e*e ) * sind(E);
|
||||
*r = sqrt( x*x + y*y ); /* Solar distance */
|
||||
v = atan2d( y, x ); /* True anomaly */
|
||||
*lon = v + w; /* True solar longitude */
|
||||
if ( *lon >= 360.0 )
|
||||
*lon -= 360.0; /* Make it 0..360 degrees */
|
||||
}
|
||||
|
||||
void sun_RA_dec( double d, double *RA, double *dec, double *r )
|
||||
/******************************************************/
|
||||
/* Computes the Sun's equatorial coordinates RA, Decl */
|
||||
/* and also its distance, at an instant given in d, */
|
||||
/* the number of days since 2000 Jan 0.0. */
|
||||
/******************************************************/
|
||||
{
|
||||
double lon, obl_ecl, x, y, z;
|
||||
|
||||
/* Compute Sun's ecliptical coordinates */
|
||||
sunpos( d, &lon, r );
|
||||
|
||||
/* Compute ecliptic rectangular coordinates (z=0) */
|
||||
x = *r * cosd(lon);
|
||||
y = *r * sind(lon);
|
||||
|
||||
/* Compute obliquity of ecliptic (inclination of Earth's axis) */
|
||||
obl_ecl = 23.4393 - 3.563E-7 * d;
|
||||
|
||||
/* Convert to equatorial rectangular coordinates - x is unchanged */
|
||||
z = y * sind(obl_ecl);
|
||||
y = y * cosd(obl_ecl);
|
||||
|
||||
/* Convert to spherical coordinates */
|
||||
*RA = atan2d( y, x );
|
||||
*dec = atan2d( z, sqrt(x*x + y*y) );
|
||||
|
||||
} /* sun_RA_dec */
|
||||
|
||||
|
||||
/******************************************************************/
|
||||
/* This function reduces any angle to within the first revolution */
|
||||
/* by subtracting or adding even multiples of 360.0 until the */
|
||||
/* result is >= 0.0 and < 360.0 */
|
||||
/******************************************************************/
|
||||
|
||||
#define INV360 ( 1.0 / 360.0 )
|
||||
|
||||
double revolution( double x )
|
||||
/*****************************************/
|
||||
/* Reduce angle to within 0..360 degrees */
|
||||
/*****************************************/
|
||||
{
|
||||
return( x - 360.0 * floor( x * INV360 ) );
|
||||
} /* revolution */
|
||||
|
||||
double rev180( double x )
|
||||
/*********************************************/
|
||||
/* Reduce angle to within +180..+180 degrees */
|
||||
/*********************************************/
|
||||
{
|
||||
return( x - 360.0 * floor( x * INV360 + 0.5 ) );
|
||||
} /* revolution */
|
||||
|
||||
|
||||
/*******************************************************************/
|
||||
/* This function computes GMST0, the Greenwich Mean Sidereal Time */
|
||||
/* at 0h UT (i.e. the sidereal time at the Greenwhich meridian at */
|
||||
/* 0h UT). GMST is then the sidereal time at Greenwich at any */
|
||||
/* time of the day. I've generalized GMST0 as well, and define it */
|
||||
/* as: GMST0 = GMST - UT -- this allows GMST0 to be computed at */
|
||||
/* other times than 0h UT as well. While this sounds somewhat */
|
||||
/* contradictory, it is very practical: instead of computing */
|
||||
/* GMST like: */
|
||||
/* */
|
||||
/* GMST = (GMST0) + UT * (366.2422/365.2422) */
|
||||
/* */
|
||||
/* where (GMST0) is the GMST last time UT was 0 hours, one simply */
|
||||
/* computes: */
|
||||
/* */
|
||||
/* GMST = GMST0 + UT */
|
||||
/* */
|
||||
/* where GMST0 is the GMST "at 0h UT" but at the current moment! */
|
||||
/* Defined in this way, GMST0 will increase with about 4 min a */
|
||||
/* day. It also happens that GMST0 (in degrees, 1 hr = 15 degr) */
|
||||
/* is equal to the Sun's mean longitude plus/minus 180 degrees! */
|
||||
/* (if we neglect aberration, which amounts to 20 seconds of arc */
|
||||
/* or 1.33 seconds of time) */
|
||||
/* */
|
||||
/*******************************************************************/
|
||||
|
||||
double GMST0( double d )
|
||||
{
|
||||
double sidtim0;
|
||||
/* Sidtime at 0h UT = L (Sun's mean longitude) + 180.0 degr */
|
||||
/* L = M + w, as defined in sunpos(). Since I'm too lazy to */
|
||||
/* add these numbers, I'll let the C compiler do it for me. */
|
||||
/* Any decent C compiler will add the constants at compile */
|
||||
/* time, imposing no runtime or code overhead. */
|
||||
sidtim0 = revolution( ( 180.0 + 356.0470 + 282.9404 ) +
|
||||
( 0.9856002585 + 4.70935E-5 ) * d );
|
||||
return sidtim0;
|
||||
} /* GMST0 */
|
||||
|
|
@ -1,280 +0,0 @@
|
|||
const std = @import("std");
|
||||
const zeit = @import("zeit");
|
||||
const TimeZoneOffsets = @import("location/timezone_offsets.zig");
|
||||
const Coordinates = @import("Coordinates.zig");
|
||||
|
||||
const c_double = f64;
|
||||
// We don't use @cImport here because sunriset.c has a main() function
|
||||
// Instead we declare the functions we need directly
|
||||
extern fn __sunriset__(year: c_int, month: c_int, day: c_int, lon: c_double, lat: c_double, altit: c_double, upper_limb: c_int, rise: *c_double, set: *c_double) c_int;
|
||||
extern fn __daylen__(year: c_int, month: c_int, day: c_int, lon: c_double, lat: c_double, altit: c_double, upper_limb: c_int) c_double;
|
||||
|
||||
/// We will copy these macros in from the c file as proper functions
|
||||
|
||||
// This macro computes the length of the day, from sunrise to sunset.
|
||||
// Sunrise/set is considered to occur when the Sun's upper limb is
|
||||
// 35 arc minutes below the horizon (this accounts for the refraction
|
||||
// of the Earth's atmosphere).
|
||||
fn day_length(year: c_int, month: c_int, day: c_int, lon: c_double, lat: c_double) c_double {
|
||||
return __daylen__(year, month, day, lon, lat, -35.0 / 60.0, 1);
|
||||
}
|
||||
|
||||
// This macro computes the length of the day, including civil twilight.
|
||||
// Civil twilight starts/ends when the Sun's center is 6 degrees below
|
||||
// the horizon.
|
||||
fn day_civil_twilight_length(year: c_int, month: c_int, day: c_int, lon: c_double, lat: c_double) c_double {
|
||||
return __daylen__(year, month, day, lon, lat, -6.0, 0);
|
||||
}
|
||||
|
||||
// This macro computes the length of the day, incl. nautical twilight.
|
||||
// Nautical twilight starts/ends when the Sun's center is 12 degrees
|
||||
// below the horizon.
|
||||
fn day_nautical_twilight_length(year: c_int, month: c_int, day: c_int, lon: c_double, lat: c_double) c_double {
|
||||
return __daylen__(year, month, day, lon, lat, -12.0, 0);
|
||||
}
|
||||
|
||||
// This macro computes the length of the day, incl. astronomical twilight.
|
||||
// Astronomical twilight starts/ends when the Sun's center is 18 degrees
|
||||
// below the horizon.
|
||||
fn day_astronomical_twilight_length(year: c_int, month: c_int, day: c_int, lon: c_double, lat: c_double) c_double {
|
||||
return __daylen__(year, month, day, lon, lat, -18.0, 0);
|
||||
}
|
||||
|
||||
// This macro computes times for sunrise/sunset.
|
||||
// Sunrise/set is considered to occur when the Sun's upper limb is
|
||||
// 35 arc minutes below the horizon (this accounts for the refraction
|
||||
// of the Earth's atmosphere).
|
||||
fn sun_rise_set(year: c_int, month: c_int, day: c_int, lon: c_double, lat: c_double, rise: *c_double, set: *c_double) c_int {
|
||||
return __sunriset__(year, month, day, lon, lat, -35.0 / 60.0, 1, rise, set);
|
||||
}
|
||||
|
||||
// This macro computes the start and end times of civil twilight.
|
||||
// Civil twilight starts/ends when the Sun's center is 6 degrees below
|
||||
// the horizon.
|
||||
fn civil_twilight(year: c_int, month: c_int, day: c_int, lon: c_double, lat: c_double, start: *c_double, end: *c_double) c_int {
|
||||
return __sunriset__(year, month, day, lon, lat, -6.0, 0, start, end);
|
||||
}
|
||||
|
||||
// This macro computes the start and end times of nautical twilight.
|
||||
// Nautical twilight starts/ends when the Sun's center is 12 degrees
|
||||
// below the horizon.
|
||||
fn nautical_twilight(year: c_int, month: c_int, day: c_int, lon: c_double, lat: c_double, start: *c_double, end: *c_double) c_int {
|
||||
return __sunriset__(year, month, day, lon, lat, -12.0, 0, start, end);
|
||||
}
|
||||
|
||||
// This macro computes the start and end times of astronomical twilight.
|
||||
// Astronomical twilight starts/ends when the Sun's center is 18 degrees
|
||||
// below the horizon.
|
||||
fn astronomical_twilight(year: c_int, month: c_int, day: c_int, lon: c_double, lat: c_double, start: *c_double, end: *c_double) c_int {
|
||||
return __sunriset__(year, month, day, lon, lat, -18.0, 0, start, end);
|
||||
}
|
||||
|
||||
const Astronomical = @This();
|
||||
|
||||
dawn: Time, // Hours in UTC (civil twilight start)
|
||||
sunrise: Time, // Hours in UTC
|
||||
zenith: Time, // Hours in UTC (solar noon)
|
||||
sunset: Time, // Hours in UTC
|
||||
dusk: Time, // Hours in UTC (civil twilight end)
|
||||
|
||||
pub const Time = struct {
|
||||
year: i32,
|
||||
month: zeit.Month,
|
||||
day: u5,
|
||||
hour: u5,
|
||||
minute: u6,
|
||||
offset: i32 = 0,
|
||||
|
||||
pub fn init(sunriset_time: f64, year: i32, month: zeit.Month, day: u5) Time {
|
||||
const h: u8 = @intFromFloat(@floor(sunriset_time));
|
||||
return .{
|
||||
.year = year,
|
||||
.month = month,
|
||||
.day = day,
|
||||
.hour = @intCast(h),
|
||||
.minute = @intFromFloat(@floor((sunriset_time - @as(f64, @floatFromInt(h))) * 60.0)),
|
||||
};
|
||||
}
|
||||
|
||||
pub fn adjustTimeToLocal(self: Time, coords: Coordinates) !Time {
|
||||
const ztime: zeit.Time = .{
|
||||
.year = self.year,
|
||||
.month = self.month,
|
||||
.day = self.day,
|
||||
.hour = self.hour,
|
||||
.minute = self.minute,
|
||||
};
|
||||
const original = ztime.instant();
|
||||
const offset = TimeZoneOffsets.getTimezoneOffset(coords);
|
||||
const new = if (offset >= 0)
|
||||
try original.add(.{ .minutes = @abs(offset) })
|
||||
else
|
||||
try original.subtract(.{ .minutes = @abs(offset) });
|
||||
const new_ztime = new.time();
|
||||
|
||||
return .{
|
||||
.year = new_ztime.year,
|
||||
.month = new_ztime.month,
|
||||
.day = new_ztime.day,
|
||||
.hour = new_ztime.hour,
|
||||
.minute = new_ztime.minute,
|
||||
.offset = offset,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn format(self: Time, writer: *std.Io.Writer) std.Io.Writer.Error!void {
|
||||
try writer.print("{d:0>2}:{d:0>2}", .{ self.hour, self.minute });
|
||||
}
|
||||
};
|
||||
/// Returns all times in UTC
|
||||
///
|
||||
/// calculates astronomical data for lat/long and a timestamp using
|
||||
/// sunriset.c (http://www.stjarnhimlen.se/comp/sunriset.c)
|
||||
///
|
||||
/// Note: year,month,date = calendar date, 1801-2099 only.
|
||||
pub fn init(latitude: f64, longitude: f64, timestamp: i64) Astronomical {
|
||||
const instant = zeit.instant(.{ .source = .{ .unix_timestamp = timestamp } }) catch
|
||||
@panic("This can't happen");
|
||||
|
||||
const time = instant.time();
|
||||
const year: c_int = @intCast(time.year);
|
||||
const month: c_int = @intFromEnum(time.month);
|
||||
const day: c_int = @intCast(time.day);
|
||||
|
||||
std.log.debug("year: {}, month: {}, day: {}", .{ year, month, day });
|
||||
var sunrise: f64 = 0;
|
||||
var sunset: f64 = 0;
|
||||
var dawn: f64 = 0;
|
||||
var dusk: f64 = 0;
|
||||
|
||||
// Notes from the c file itself:
|
||||
// Eastern longitude positive, Western longitude negative
|
||||
// Northern latitude positive, Southern latitude negative
|
||||
//
|
||||
// The longitude value IS critical in this function!
|
||||
//
|
||||
// altit = the altitude which the Sun should cross
|
||||
// Set to -35/60 degrees for rise/set, -6 degrees
|
||||
// for civil, -12 degrees for nautical and -18
|
||||
// degrees for astronomical twilight.
|
||||
// upper_limb: non-zero -> upper limb, zero -> center
|
||||
// Set to non-zero (e.g. 1) when computing rise/set
|
||||
// times, and to zero when computing start/end of
|
||||
// twilight.
|
||||
// *rise = where to store the rise time
|
||||
// *set = where to store the set time
|
||||
// Both times are relative to the specified altitude,
|
||||
// and thus this function can be used to compute
|
||||
// various twilight times, as well as rise/set times
|
||||
// Return value: 0 = sun rises/sets this day, times stored at
|
||||
// *trise and *tset.
|
||||
// +1 = sun above the specified "horizon" 24 hours.
|
||||
// *trise set to time when the sun is at south,
|
||||
// minus 12 hours while *tset is set to the south
|
||||
// time plus 12 hours. "Day" length = 24 hours
|
||||
// -1 = sun is below the specified "horizon" 24 hours
|
||||
// "Day" length = 0 hours, *trise and *tset are
|
||||
// both set to the time when the sun is at south.
|
||||
|
||||
// Get sunrise/sunset
|
||||
_ = sun_rise_set(year, month, day, longitude, latitude, &sunrise, &sunset);
|
||||
|
||||
// Get civil twilight (dawn/dusk)
|
||||
_ = civil_twilight(year, month, day, longitude, latitude, &dawn, &dusk);
|
||||
|
||||
// Calculate solar noon (zenith)
|
||||
const zenith = (sunrise + sunset) / 2.0;
|
||||
|
||||
return .{
|
||||
.dawn = Time.init(dawn, time.year, time.month, time.day),
|
||||
.sunrise = Time.init(sunrise, time.year, time.month, time.day),
|
||||
.zenith = Time.init(zenith, time.year, time.month, time.day),
|
||||
.sunset = Time.init(sunset, time.year, time.month, time.day),
|
||||
.dusk = Time.init(dusk, time.year, time.month, time.day),
|
||||
};
|
||||
}
|
||||
|
||||
test "astronomical calculations" {
|
||||
// Test for London on 2024-01-01
|
||||
const timestamp: i64 = 1704067200;
|
||||
const astro = init(51.5074, -0.1278, timestamp);
|
||||
|
||||
// Winter in London: sunrise around 8am, sunset around 4pm UTC
|
||||
// dawn: 07:26, sunrise: 08:06, zenith: 12:03, sunset: 16:01, dusk: 16:41
|
||||
try std.testing.expectEqual(@as(u5, 7), astro.dawn.hour);
|
||||
try std.testing.expectEqual(@as(u6, 26), astro.dawn.minute);
|
||||
try std.testing.expectEqual(@as(u5, 8), astro.sunrise.hour);
|
||||
try std.testing.expectEqual(@as(u6, 6), astro.sunrise.minute);
|
||||
try std.testing.expectEqual(@as(u5, 12), astro.zenith.hour);
|
||||
try std.testing.expectEqual(@as(u6, 3), astro.zenith.minute);
|
||||
try std.testing.expectEqual(@as(u5, 16), astro.sunset.hour);
|
||||
try std.testing.expectEqual(@as(u6, 1), astro.sunset.minute);
|
||||
try std.testing.expectEqual(@as(u5, 16), astro.dusk.hour);
|
||||
try std.testing.expectEqual(@as(u6, 41), astro.dusk.minute);
|
||||
|
||||
// Sanity checks
|
||||
try std.testing.expect(astro.dawn.hour < astro.sunrise.hour);
|
||||
try std.testing.expect(astro.sunset.hour <= astro.dusk.hour);
|
||||
try std.testing.expect(astro.zenith.hour > astro.sunrise.hour and astro.zenith.hour < astro.sunset.hour);
|
||||
}
|
||||
test "Oregon modern time" {
|
||||
// Test for Oregon 2026-01-06
|
||||
const timestamp: i64 = 1767722166;
|
||||
|
||||
const coords: Coordinates = .{
|
||||
.latitude = 44.052071,
|
||||
.longitude = -123.086754,
|
||||
};
|
||||
const astro = init(coords.latitude, coords.longitude, timestamp);
|
||||
|
||||
const sunrise = try astro.sunrise.adjustTimeToLocal(coords);
|
||||
// Sunrise at 7:47, sunset 16:49, zenith 12:18
|
||||
|
||||
try std.testing.expectEqualDeep(Time{
|
||||
.year = 2026,
|
||||
.month = .jan,
|
||||
.day = 6,
|
||||
.hour = 7,
|
||||
.minute = 46,
|
||||
.offset = -480,
|
||||
}, sunrise);
|
||||
|
||||
// UTC times: dawn: 15:14, sunrise: 15:46, zenith: 20:18, sunset: 24:49 (00:49 next day), dusk: 25:22 (01:22 next day)
|
||||
// Local PST times: dawn: 07:14, sunrise: 07:46, zenith: 12:18, sunset: 16:49, dusk: 17:22
|
||||
const sunset = try astro.sunset.adjustTimeToLocal(coords);
|
||||
const dusk = try astro.dusk.adjustTimeToLocal(coords);
|
||||
const zenith = try astro.zenith.adjustTimeToLocal(coords);
|
||||
const dawn = try astro.dawn.adjustTimeToLocal(coords);
|
||||
|
||||
try std.testing.expectEqual(@as(u5, 7), dawn.hour);
|
||||
try std.testing.expectEqual(@as(u6, 14), dawn.minute);
|
||||
try std.testing.expectEqual(@as(u5, 12), zenith.hour);
|
||||
try std.testing.expectEqual(@as(u6, 18), zenith.minute);
|
||||
try std.testing.expectEqual(@as(u5, 16), sunset.hour);
|
||||
try std.testing.expectEqual(@as(u6, 49), sunset.minute);
|
||||
try std.testing.expectEqual(@as(u5, 17), dusk.hour);
|
||||
try std.testing.expectEqual(@as(u6, 22), dusk.minute);
|
||||
|
||||
// Sanity checks
|
||||
try std.testing.expect(dawn.hour <= sunrise.hour);
|
||||
try std.testing.expect(dawn.minute < sunrise.minute or dawn.hour < sunrise.hour);
|
||||
try std.testing.expect(sunset.hour < dusk.hour or (sunset.hour == dusk.hour and sunset.minute < dusk.minute));
|
||||
try std.testing.expect(zenith.hour > sunrise.hour and zenith.hour < sunset.hour);
|
||||
}
|
||||
|
||||
test "format time" {
|
||||
const time1 = Time{ .year = 2026, .month = .jan, .day = 6, .hour = 12, .minute = 30, .offset = 0 };
|
||||
const time2 = Time{ .year = 2026, .month = .jan, .day = 6, .hour = 8, .minute = 15, .offset = 0 };
|
||||
|
||||
var buf1: [5]u8 = undefined;
|
||||
var buf2: [5]u8 = undefined;
|
||||
|
||||
var writer1 = std.Io.Writer.fixed(&buf1);
|
||||
var writer2 = std.Io.Writer.fixed(&buf2);
|
||||
|
||||
try time1.format(&writer1);
|
||||
try time2.format(&writer2);
|
||||
|
||||
try std.testing.expectEqualStrings("12:30", &buf1);
|
||||
try std.testing.expectEqualStrings("08:15", &buf2);
|
||||
}
|
||||
71
src/Moon.zig
71
src/Moon.zig
|
|
@ -1,71 +0,0 @@
|
|||
const std = @import("std");
|
||||
|
||||
const c = @cImport({
|
||||
@cInclude("time.h");
|
||||
@cInclude("astro.h");
|
||||
});
|
||||
|
||||
pub const Phase = struct {
|
||||
phase: f64, // 0.0 to 1.0 (0=new, 0.25=first quarter, 0.5=full, 0.75=last quarter)
|
||||
illuminated: f64, // 0.0 to 1.0
|
||||
age_days: f64, // Age in days
|
||||
distance_km: f64,
|
||||
angular_diameter: f64,
|
||||
|
||||
pub fn emoji(self: Phase) []const u8 {
|
||||
const p = self.phase;
|
||||
if (p < 0.0625) return "🌑"; // New moon
|
||||
if (p < 0.1875) return "🌒"; // Waxing crescent
|
||||
if (p < 0.3125) return "🌓"; // First quarter
|
||||
if (p < 0.4375) return "🌔"; // Waxing gibbous
|
||||
if (p < 0.5625) return "🌕"; // Full moon
|
||||
if (p < 0.6875) return "🌖"; // Waning gibbous
|
||||
if (p < 0.8125) return "🌗"; // Last quarter
|
||||
if (p < 0.9375) return "🌘"; // Waning crescent
|
||||
return "🌑"; // New moon
|
||||
}
|
||||
|
||||
pub fn day(self: Phase) u8 {
|
||||
return @intFromFloat(@round(self.age_days));
|
||||
}
|
||||
};
|
||||
|
||||
pub fn getPhase(timestamp: i64) Phase {
|
||||
const julian = c.unix_to_julian(@intCast(timestamp));
|
||||
|
||||
var illuminated: f64 = 0;
|
||||
var age_days: f64 = 0;
|
||||
var distance: f64 = 0;
|
||||
var angular_diameter: f64 = 0;
|
||||
var sun_distance: f64 = 0;
|
||||
var sun_angular_diameter: f64 = 0;
|
||||
|
||||
const phase_angle = c.phase(
|
||||
julian,
|
||||
&illuminated,
|
||||
&age_days,
|
||||
&distance,
|
||||
&angular_diameter,
|
||||
&sun_distance,
|
||||
&sun_angular_diameter,
|
||||
);
|
||||
|
||||
return .{
|
||||
.phase = phase_angle,
|
||||
.illuminated = illuminated,
|
||||
.age_days = age_days,
|
||||
.distance_km = distance,
|
||||
.angular_diameter = angular_diameter,
|
||||
};
|
||||
}
|
||||
|
||||
test "moon phase calculation" {
|
||||
// Test a known date: 2024-01-01 00:00:00 UTC
|
||||
const timestamp: i64 = 1704067200;
|
||||
const moon = getPhase(timestamp);
|
||||
|
||||
try std.testing.expect(moon.phase >= 0.0 and moon.phase <= 1.0);
|
||||
try std.testing.expect(moon.illuminated >= 0.0 and moon.illuminated <= 1.0);
|
||||
try std.testing.expect(moon.age_days >= 0.0 and moon.age_days <= 29.53);
|
||||
try std.testing.expect(moon.distance_km > 0);
|
||||
}
|
||||
|
|
@ -69,37 +69,16 @@ pub fn init(
|
|||
}
|
||||
|
||||
fn handleWeather(ctx: *Context, req: *httpz.Request, res: *httpz.Response) !void {
|
||||
var client_ip_buf: [47]u8 = undefined;
|
||||
const client_ip = try getClientIp(req, &client_ip_buf);
|
||||
try rateLimitMiddleware(ctx.rate_limiter, client_ip, res);
|
||||
try rateLimitMiddleware(ctx.rate_limiter, req, res);
|
||||
if (res.status == 429) return;
|
||||
try handler.handleWeather(&ctx.options, req, res, client_ip);
|
||||
try handler.handleWeather(&ctx.options, req, res);
|
||||
}
|
||||
|
||||
fn getClientIp(req: *httpz.Request, buf: []u8) ![]const u8 {
|
||||
// Check X-Forwarded-For header first (for proxies)
|
||||
if (req.header("x-forwarded-for")) |xff| {
|
||||
return parseXForwardedFor(xff);
|
||||
}
|
||||
fn rateLimitMiddleware(limiter: *RateLimiter, req: *httpz.Request, res: *httpz.Response) !void {
|
||||
var ip_buf: [45]u8 = undefined;
|
||||
const ip_str = try std.fmt.bufPrint(&ip_buf, "{f}", .{req.address});
|
||||
|
||||
// Check X-Real-IP header
|
||||
if (req.header("x-real-ip")) |real_ip| {
|
||||
return real_ip;
|
||||
}
|
||||
|
||||
// Fall back to connection address
|
||||
const req_addr = try std.fmt.bufPrint(buf, "{f}", .{req.address});
|
||||
return req_addr[0..std.mem.lastIndexOfScalar(u8, req_addr, ':').?];
|
||||
}
|
||||
|
||||
fn parseXForwardedFor(xff: []const u8) []const u8 {
|
||||
// Take first IP from comma-separated list
|
||||
var iter = std.mem.splitScalar(u8, xff, ',');
|
||||
return std.mem.trim(u8, iter.first(), " \t");
|
||||
}
|
||||
|
||||
fn rateLimitMiddleware(limiter: *RateLimiter, client_ip: []const u8, res: *httpz.Response) !void {
|
||||
if (!limiter.shouldAcceptRequest(client_ip)) {
|
||||
if (!limiter.shouldAcceptRequest(ip_str)) {
|
||||
res.status = 429;
|
||||
res.body = "Too Many Requests";
|
||||
}
|
||||
|
|
@ -236,9 +215,7 @@ test "handleWeather: default endpoint uses IP address" {
|
|||
ht.url("/");
|
||||
ht.header("x-forwarded-for", "73.158.64.1");
|
||||
|
||||
var client_ip_buf: [47]u8 = undefined;
|
||||
const client_ip = try getClientIp(ht.req, &client_ip_buf);
|
||||
try handler.handleWeather(&harness.opts, ht.req, ht.res, client_ip);
|
||||
try handler.handleWeather(&harness.opts, ht.req, ht.res);
|
||||
|
||||
try ht.expectStatus(200);
|
||||
}
|
||||
|
|
@ -254,10 +231,7 @@ test "handleWeather: x-forwarded-for with multiple IPs" {
|
|||
ht.url("/");
|
||||
ht.header("x-forwarded-for", "73.158.64.1, 8.8.8.8, 192.168.1.1");
|
||||
|
||||
var client_ip_buf: [47]u8 = undefined;
|
||||
const client_ip = try getClientIp(ht.req, &client_ip_buf);
|
||||
try std.testing.expectEqualStrings("73.158.64.1", client_ip);
|
||||
try handler.handleWeather(&harness.opts, ht.req, ht.res, client_ip);
|
||||
try handler.handleWeather(&harness.opts, ht.req, ht.res);
|
||||
|
||||
try ht.expectStatus(200);
|
||||
}
|
||||
|
|
@ -271,29 +245,11 @@ test "handleWeather: client IP only" {
|
|||
defer ht.deinit();
|
||||
|
||||
// Set connection address to a valid IP that will be in GeoIP database
|
||||
ht.req.address = try std.net.Address.parseIp("73.158.64.1", 0);
|
||||
ht.conn.address = try std.net.Address.parseIp("73.158.64.1", 0);
|
||||
|
||||
ht.url("/");
|
||||
|
||||
var client_ip_buf: [47]u8 = undefined;
|
||||
const client_ip = try getClientIp(ht.req, &client_ip_buf);
|
||||
try std.testing.expectEqualStrings("73.158.64.1", client_ip);
|
||||
try handler.handleWeather(&harness.opts, ht.req, ht.res, client_ip);
|
||||
try handler.handleWeather(&harness.opts, ht.req, ht.res);
|
||||
|
||||
try ht.expectStatus(200);
|
||||
}
|
||||
|
||||
test "parseXForwardedFor extracts first IP" {
|
||||
try std.testing.expectEqualStrings("192.168.1.1", parseXForwardedFor("192.168.1.1"));
|
||||
try std.testing.expectEqualStrings("10.0.0.1", parseXForwardedFor("10.0.0.1, 172.16.0.1"));
|
||||
try std.testing.expectEqualStrings("203.0.113.1", parseXForwardedFor("203.0.113.1, 198.51.100.1, 192.0.2.1"));
|
||||
}
|
||||
|
||||
test "parseXForwardedFor trims whitespace" {
|
||||
try std.testing.expectEqualStrings("192.168.1.1", parseXForwardedFor(" 192.168.1.1 "));
|
||||
try std.testing.expectEqualStrings("10.0.0.1", parseXForwardedFor(" 10.0.0.1 , 172.16.0.1"));
|
||||
}
|
||||
|
||||
test "parseXForwardedFor handles empty string" {
|
||||
try std.testing.expectEqualStrings("", parseXForwardedFor(""));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,8 +22,18 @@ pub fn handleWeather(
|
|||
opts: *HandleWeatherOptions,
|
||||
req: *httpz.Request,
|
||||
res: *httpz.Response,
|
||||
client_ip: []const u8,
|
||||
) !void {
|
||||
var client_ip_buf: [47]u8 = undefined;
|
||||
// We need IP possibly for location, and possibly to determine implicit Imperial/Metric
|
||||
// Let's get it once here
|
||||
const client_ip = blk: {
|
||||
const client_ip = getClientIpFromHeaders(req);
|
||||
if (client_ip.len == 0) {
|
||||
const full_location = try std.fmt.bufPrint(&client_ip_buf, "{f}", .{req.conn.address});
|
||||
break :blk full_location[0..std.mem.lastIndexOf(u8, full_location, ":").?];
|
||||
}
|
||||
break :blk client_ip;
|
||||
};
|
||||
// Get location from path parameter or query string
|
||||
const location = req.param("location") orelse blk: {
|
||||
// Check query string for location parameter
|
||||
|
|
@ -65,6 +75,30 @@ pub fn handleWeather(
|
|||
try handleWeatherInternal(opts, req, res, location, client_ip);
|
||||
}
|
||||
|
||||
fn getClientIpFromHeaders(req: *httpz.Request) []const u8 {
|
||||
// Check X-Forwarded-For header first (for proxies)
|
||||
if (req.header("x-forwarded-for")) |xff| {
|
||||
return parseXForwardedFor(xff);
|
||||
}
|
||||
|
||||
// Check X-Real-IP header
|
||||
if (req.header("x-real-ip")) |real_ip| {
|
||||
return real_ip;
|
||||
}
|
||||
|
||||
// Fall back to client connection
|
||||
return "";
|
||||
}
|
||||
|
||||
fn parseXForwardedFor(xff: []const u8) []const u8 {
|
||||
// Take first IP from comma-separated list
|
||||
var iter = std.mem.splitScalar(u8, xff, ',');
|
||||
if (iter.next()) |first_ip| {
|
||||
return std.mem.trim(u8, first_ip, " \t");
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
fn handleWeatherInternal(
|
||||
opts: *HandleWeatherOptions,
|
||||
req: *httpz.Request,
|
||||
|
|
@ -195,6 +229,21 @@ fn determineFormat(params: QueryParams, user_agent: ?[]const u8) Formatted.Forma
|
|||
return .html;
|
||||
}
|
||||
|
||||
test "parseXForwardedFor extracts first IP" {
|
||||
try std.testing.expectEqualStrings("192.168.1.1", parseXForwardedFor("192.168.1.1"));
|
||||
try std.testing.expectEqualStrings("10.0.0.1", parseXForwardedFor("10.0.0.1, 172.16.0.1"));
|
||||
try std.testing.expectEqualStrings("203.0.113.1", parseXForwardedFor("203.0.113.1, 198.51.100.1, 192.0.2.1"));
|
||||
}
|
||||
|
||||
test "parseXForwardedFor trims whitespace" {
|
||||
try std.testing.expectEqualStrings("192.168.1.1", parseXForwardedFor(" 192.168.1.1 "));
|
||||
try std.testing.expectEqualStrings("10.0.0.1", parseXForwardedFor(" 10.0.0.1 , 172.16.0.1"));
|
||||
}
|
||||
|
||||
test "parseXForwardedFor handles empty string" {
|
||||
try std.testing.expectEqualStrings("", parseXForwardedFor(""));
|
||||
}
|
||||
|
||||
test "imperial units selection logic" {
|
||||
// This test documents the priority order for unit selection:
|
||||
// 1. Explicit ?u or ?m parameter (highest priority)
|
||||
|
|
|
|||
|
|
@ -64,16 +64,16 @@ pub const help_page =
|
|||
\\ %f # temperature (feels like)
|
||||
\\ %w # wind
|
||||
\\ %l # location
|
||||
\\ %m # moon phase emoji
|
||||
\\ %M # moon day
|
||||
\\ %m # * moon phase emoji
|
||||
\\ %M # * moon day
|
||||
\\ %p # precipitation (mm)
|
||||
\\ %o # probability of precipitation
|
||||
\\ %P # pressure (hPa)
|
||||
\\ %D # dawn time
|
||||
\\ %S # sunrise time
|
||||
\\ %z # zenith time
|
||||
\\ %s # sunset time
|
||||
\\ %d # dusk time
|
||||
\\ %D # * dawn time
|
||||
\\ %S # * sunrise time
|
||||
\\ %z # * zenith time
|
||||
\\ %s # * sunset time
|
||||
\\ %d # * dusk time
|
||||
\\
|
||||
\\PNG options:
|
||||
\\
|
||||
|
|
|
|||
|
|
@ -1,12 +1,7 @@
|
|||
const std = @import("std");
|
||||
const zeit = @import("zeit");
|
||||
const types = @import("../weather/types.zig");
|
||||
const emoji = @import("emoji.zig");
|
||||
const utils = @import("utils.zig");
|
||||
const Moon = @import("../Moon.zig");
|
||||
const Astronomical = @import("../Astronomical.zig");
|
||||
const TimeZoneOffsets = @import("../location/timezone_offsets.zig");
|
||||
const Coordinates = @import("../Coordinates.zig");
|
||||
|
||||
pub fn render(allocator: std.mem.Allocator, weather: types.WeatherData, format: []const u8, use_imperial: bool) ![]const u8 {
|
||||
var output: std.ArrayList(u8) = .empty;
|
||||
|
|
@ -57,38 +52,14 @@ pub fn render(allocator: std.mem.Allocator, weather: types.WeatherData, format:
|
|||
const unit = if (use_imperial) "inHg" else "hPa";
|
||||
try writer.print("{d:.2} {s}", .{ pressure, unit });
|
||||
},
|
||||
'm' => {
|
||||
const now = try nowAt(weather.coords);
|
||||
const moon = Moon.getPhase(now);
|
||||
try writer.print("{s}", .{moon.emoji()});
|
||||
},
|
||||
'M' => {
|
||||
const now = try nowAt(weather.coords);
|
||||
const moon = Moon.getPhase(now);
|
||||
try writer.print("{d}", .{moon.day()});
|
||||
},
|
||||
'm' => try writer.print("🌕", .{}), // Moon phase placeholder
|
||||
'M' => try writer.print("15", .{}), // Moon day placeholder
|
||||
'o' => try writer.print("0%", .{}), // Probability of precipitation placeholder
|
||||
'D', 'S', 'z', 's', 'd' => {
|
||||
// Again...we only need approximate, because we simply need
|
||||
// to make sure the day is correct for this. Even a day off
|
||||
// should actually be ok. Unix timestamp is always UTC,
|
||||
// so we convert to local
|
||||
const now = try nowAt(weather.coords);
|
||||
const astro = Astronomical.init(
|
||||
weather.coords.latitude,
|
||||
weather.coords.longitude,
|
||||
now,
|
||||
);
|
||||
const utc_time = switch (code) {
|
||||
'D' => astro.dawn,
|
||||
'S' => astro.sunrise,
|
||||
'z' => astro.zenith,
|
||||
's' => astro.sunset,
|
||||
'd' => astro.dusk,
|
||||
else => unreachable,
|
||||
};
|
||||
try writer.print("{f}", .{try utc_time.adjustTimeToLocal(weather.coords)});
|
||||
},
|
||||
'D' => try writer.print("06:00", .{}), // Dawn placeholder
|
||||
'S' => try writer.print("07:30", .{}), // Sunrise placeholder
|
||||
'z' => try writer.print("12:00", .{}), // Zenith placeholder
|
||||
's' => try writer.print("18:30", .{}), // Sunset placeholder
|
||||
'd' => try writer.print("20:00", .{}), // Dusk placeholder
|
||||
'%' => try writer.print("%", .{}),
|
||||
'n' => try writer.print("\n", .{}),
|
||||
else => {
|
||||
|
|
@ -105,16 +76,6 @@ pub fn render(allocator: std.mem.Allocator, weather: types.WeatherData, format:
|
|||
return output.toOwnedSlice(allocator);
|
||||
}
|
||||
|
||||
fn nowAt(coords: Coordinates) !i64 {
|
||||
const now = try zeit.instant(.{});
|
||||
const offset = TimeZoneOffsets.getTimezoneOffset(coords);
|
||||
const new = if (offset >= 0)
|
||||
try now.add(.{ .minutes = @abs(offset) })
|
||||
else
|
||||
try now.subtract(.{ .minutes = @abs(offset) });
|
||||
return new.unixTimestamp();
|
||||
}
|
||||
|
||||
test "render custom format with location and temp" {
|
||||
const allocator = std.testing.allocator;
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue