moon phase calculation based on phoon

This commit is contained in:
Emil Lerch 2026-01-06 19:15:24 -08:00
parent 9ccb0b2cb2
commit e95c13b1c6
Signed by: lobo
GPG key ID: A7B62D657EF764F8
15 changed files with 2394 additions and 11 deletions

View file

@ -343,6 +343,7 @@ 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/)
## Performance Targets

View file

@ -20,15 +20,14 @@ Features not yet implemented in the Zig version:
- lang query parameter support
- Translation of weather conditions and text (54 languages)
## 5. Moon Phase Calculation
- Real moon phase computation based on date
- Moon phase emoji display
- Moonday calculation
## 6. Astronomical Times
## 5. Astronomical Times
- Calculate dawn, sunrise, zenith, sunset, dusk times
- Based on location coordinates and timezone
- Display in custom format output
## 7. Json output
## 6. Json output
- Does not match wttr.in format
## 7. Moon endpoint
- `/Moon` and `/Moon@YYYY-MM-DD` endpoints not yet implemented
- Moon phase calculation is implemented and available in custom format (%m, %M)

View file

@ -19,6 +19,29 @@ pub fn build(b: *std.Build) void {
const maxminddb_upstream = b.dependency("maxminddb", .{});
// 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",
@ -74,8 +97,10 @@ 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.addConfigHeader(maxminddb_config);
exe.linkLibrary(maxminddb);
exe.linkLibrary(phoon);
exe.linkLibC();
b.installArtifact(exe);
@ -109,8 +134,10 @@ 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.addConfigHeader(maxminddb_config);
tests.linkLibrary(maxminddb);
tests.linkLibrary(phoon);
tests.linkLibC();
const run_tests = b.addRunArtifact(tests);

View file

@ -18,6 +18,7 @@
.url = "git+https://github.com/rockorager/zeit?ref=zig-0.15#7ac64d72dbfb1a4ad549102e7d4e232a687d32d8",
.hash = "zeit-0.6.0-5I6bk36tAgATpSl9wjFmRPMqYN2Mn0JQHgIcRNcqDpJA",
},
.phoon = .{ .path = "libs/phoon_14Aug2014" },
},
.fingerprint = 0x710c2b57e81aa678,
.minimum_zig_version = "0.15.2",

View file

@ -0,0 +1,33 @@
# 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

View file

@ -0,0 +1,50 @@
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
`\
\
`-.
`-.
`--
`--

View file

@ -0,0 +1,467 @@
/* 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;
}

View file

@ -0,0 +1,46 @@
#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

View file

@ -0,0 +1,33 @@
/* 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_ */

View file

@ -0,0 +1,23 @@
.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.

View file

@ -0,0 +1,575 @@
/* 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 );
}

71
src/Moon.zig Normal file
View file

@ -0,0 +1,71 @@
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);
}

View file

@ -64,8 +64,8 @@ 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)

View file

@ -2,6 +2,7 @@ const std = @import("std");
const types = @import("../weather/types.zig");
const emoji = @import("emoji.zig");
const utils = @import("utils.zig");
const Moon = @import("../Moon.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;
@ -52,8 +53,16 @@ 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' => try writer.print("🌕", .{}), // Moon phase placeholder
'M' => try writer.print("15", .{}), // Moon day placeholder
'm' => {
const now = std.time.timestamp();
const moon = Moon.getPhase(now);
try writer.print("{s}", .{moon.emoji()});
},
'M' => {
const now = std.time.timestamp();
const moon = Moon.getPhase(now);
try writer.print("{d}", .{moon.day()});
},
'o' => try writer.print("0%", .{}), // Probability of precipitation placeholder
'D' => try writer.print("06:00", .{}), // Dawn placeholder
'S' => try writer.print("07:30", .{}), // Sunrise placeholder