caelus 0.11.0 → 0.13.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (45) hide show
  1. package/README.md +1 -1
  2. package/accuracy.json +1 -1
  3. package/dist/src/chart.d.ts +193 -13
  4. package/dist/src/chart.js +163 -13
  5. package/dist/src/compiler.d.ts +44 -3
  6. package/dist/src/compiler.js +44 -3
  7. package/dist/src/core.d.ts +33 -2
  8. package/dist/src/core.js +33 -2
  9. package/dist/src/derived.d.ts +91 -2
  10. package/dist/src/derived.js +91 -2
  11. package/dist/src/directions.d.ts +27 -0
  12. package/dist/src/directions.js +69 -0
  13. package/dist/src/eclipses.d.ts +17 -1
  14. package/dist/src/eclipses.js +17 -1
  15. package/dist/src/events.d.ts +53 -7
  16. package/dist/src/events.js +53 -7
  17. package/dist/src/features.d.ts +69 -7
  18. package/dist/src/features.js +69 -7
  19. package/dist/src/firdaria.d.ts +49 -0
  20. package/dist/src/firdaria.js +62 -0
  21. package/dist/src/houses.d.ts +13 -4
  22. package/dist/src/houses.js +13 -4
  23. package/dist/src/index.d.ts +9 -0
  24. package/dist/src/index.js +9 -0
  25. package/dist/src/lots.d.ts +18 -0
  26. package/dist/src/lots.js +52 -0
  27. package/dist/src/pheno.d.ts +16 -2
  28. package/dist/src/pheno.js +16 -2
  29. package/dist/src/profections.d.ts +27 -0
  30. package/dist/src/profections.js +49 -0
  31. package/dist/src/query.d.ts +47 -6
  32. package/dist/src/query.js +47 -6
  33. package/dist/src/releasing.d.ts +32 -0
  34. package/dist/src/releasing.js +109 -0
  35. package/dist/src/turbo.d.ts +35 -1
  36. package/dist/src/turbo.js +35 -1
  37. package/dist/src/vargas.d.ts +32 -0
  38. package/dist/src/vargas.js +52 -0
  39. package/dist/src/vedic.d.ts +66 -0
  40. package/dist/src/vedic.js +101 -0
  41. package/dist/src/yogas.d.ts +26 -0
  42. package/dist/src/yogas.js +39 -0
  43. package/dist/src/yogini.d.ts +54 -0
  44. package/dist/src/yogini.js +63 -0
  45. package/package.json +1 -1
@@ -59,9 +59,40 @@ export interface EngineData {
59
59
  /** Fixed-star catalog (HYG-derived; ICRS J2000 + proper motions). */
60
60
  fixedStars?: import("./stars.js").StarPack;
61
61
  }
62
+ /**
63
+ * Convert a UT calendar date and time to a Julian Day (UT) — the time
64
+ * coordinate the engine consumes. Pass the result to {@link Engine.position},
65
+ * {@link Engine.longitude}, {@link Engine.chartAt}, and the event/derived
66
+ * functions; {@link Engine.chart} takes the calendar fields directly instead.
67
+ *
68
+ * Arguments are **UT**, not local civil time — convert a local time to UT
69
+ * first (see the `caelus-birth` package). Uses the proleptic Gregorian
70
+ * calendar.
71
+ *
72
+ * @param y Year in UT, e.g. `1990`.
73
+ * @param mo Month, `1`–`12`.
74
+ * @param d Day of month, `1`–`31`.
75
+ * @param h Hour, `0`–`23`. Defaults to `0`.
76
+ * @param mi Minute, `0`–`59`. Defaults to `0`.
77
+ * @param s Second, `0`–`59`. Defaults to `0`.
78
+ * @returns The Julian Day in UT (a fractional day count).
79
+ * @example
80
+ * ```ts
81
+ * const jd = julianDay(2025, 6, 1, 12, 0, 0); // 2025-06-01 12:00 UT
82
+ * engine.longitude("sun", jd);
83
+ * ```
84
+ */
62
85
  export declare function julianDay(y: number, mo: number, d: number, h?: number, mi?: number, s?: number): number;
63
- /** TT - UT1 in seconds. Observed IERS 1955-2025, E&M polynomials before,
64
- * gentle extrapolation after (Earth's rotation sped up post-2016). */
86
+ /**
87
+ * ΔT (TT UT1) in seconds at a given instant — the gap between Terrestrial
88
+ * Time and universal time. Observed IERS values 1955–2025, Espenak–Meeus
89
+ * polynomials before, and a gentle extrapolation after (Earth's rotation sped
90
+ * up post-2016). The engine applies this for you; call it directly only for
91
+ * timescale work.
92
+ *
93
+ * @param jdUt Julian Day (UT).
94
+ * @returns ΔT in seconds.
95
+ */
65
96
  export declare function deltaT(jdUt: number): number;
66
97
  export declare function jdTT(jdUt: number): number;
67
98
  export declare function vsopHeliocentric(series: VsopSeries, jde: number): [number, number, number];
package/dist/src/core.js CHANGED
@@ -17,6 +17,29 @@ export function mod(a, b) {
17
17
  return r !== 0 && (r < 0) !== (b < 0) ? r + b : r;
18
18
  }
19
19
  // ---------------------------------------------------------------- timescale
20
+ /**
21
+ * Convert a UT calendar date and time to a Julian Day (UT) — the time
22
+ * coordinate the engine consumes. Pass the result to {@link Engine.position},
23
+ * {@link Engine.longitude}, {@link Engine.chartAt}, and the event/derived
24
+ * functions; {@link Engine.chart} takes the calendar fields directly instead.
25
+ *
26
+ * Arguments are **UT**, not local civil time — convert a local time to UT
27
+ * first (see the `caelus-birth` package). Uses the proleptic Gregorian
28
+ * calendar.
29
+ *
30
+ * @param y Year in UT, e.g. `1990`.
31
+ * @param mo Month, `1`–`12`.
32
+ * @param d Day of month, `1`–`31`.
33
+ * @param h Hour, `0`–`23`. Defaults to `0`.
34
+ * @param mi Minute, `0`–`59`. Defaults to `0`.
35
+ * @param s Second, `0`–`59`. Defaults to `0`.
36
+ * @returns The Julian Day in UT (a fractional day count).
37
+ * @example
38
+ * ```ts
39
+ * const jd = julianDay(2025, 6, 1, 12, 0, 0); // 2025-06-01 12:00 UT
40
+ * engine.longitude("sun", jd);
41
+ * ```
42
+ */
20
43
  export function julianDay(y, mo, d, h = 0, mi = 0, s = 0) {
21
44
  const frac = (h + mi / 60.0 + s / 3600.0) / 24.0;
22
45
  if (mo <= 2) {
@@ -33,8 +56,16 @@ const DT_OBS = [
33
56
  [1980, 50.5], [1985, 54.3], [1990, 56.9], [1995, 60.8], [2000, 63.8],
34
57
  [2005, 64.7], [2010, 66.1], [2015, 67.6], [2020, 69.4], [2025, 69.2],
35
58
  ];
36
- /** TT - UT1 in seconds. Observed IERS 1955-2025, E&M polynomials before,
37
- * gentle extrapolation after (Earth's rotation sped up post-2016). */
59
+ /**
60
+ * ΔT (TT UT1) in seconds at a given instant — the gap between Terrestrial
61
+ * Time and universal time. Observed IERS values 1955–2025, Espenak–Meeus
62
+ * polynomials before, and a gentle extrapolation after (Earth's rotation sped
63
+ * up post-2016). The engine applies this for you; call it directly only for
64
+ * timescale work.
65
+ *
66
+ * @param jdUt Julian Day (UT).
67
+ * @returns ΔT in seconds.
68
+ */
38
69
  export function deltaT(jdUt) {
39
70
  const y = 2000.0 + (jdUt - J2000) / 365.25;
40
71
  if (y >= 1955 && y <= 2025) {
@@ -5,11 +5,50 @@ export declare function midpointLon(a: number, b: number): number;
5
5
  /** UT JDs in [jdStart, jdEnd] when `body` returns to its natal longitude.
6
6
  * Outer-planet returns can show three crossings around a retrograde loop. */
7
7
  export declare function returns(engine: Engine, body: BodyId, natalJd: number, jdStart: number, jdEnd: number, zodiac?: Zodiac, maxHits?: number): number[];
8
+ /**
9
+ * Solar-return instants in `[jdStart, jdEnd]`: the times the Sun returns to its
10
+ * natal longitude (about once a year). Build a chart at each with
11
+ * {@link Engine.chartAt} for the solar-return chart.
12
+ *
13
+ * @param engine The engine used to evaluate positions.
14
+ * @param natalJd Natal Julian Day (UT) — defines the target Sun longitude.
15
+ * @param jdStart Start of the search window, Julian Day (UT).
16
+ * @param jdEnd End of the search window, Julian Day (UT).
17
+ * @param zodiac Zodiac for the longitude match. Defaults to tropical.
18
+ * @returns Return instants as Julian Days (UT), sorted.
19
+ * @example
20
+ * ```ts
21
+ * const natal = julianDay(1990, 6, 10, 14, 30);
22
+ * const [thisYear] = solarReturn(engine, natal, julianDay(2025, 1, 1), julianDay(2026, 1, 1));
23
+ * const returnChart = engine.chartAt(thisYear, 27.95, -82.46);
24
+ * ```
25
+ * @see {@link lunarReturn} for the monthly Moon return.
26
+ */
8
27
  export declare function solarReturn(engine: Engine, natalJd: number, jdStart: number, jdEnd: number, zodiac?: Zodiac): number[];
9
28
  export declare function lunarReturn(engine: Engine, natalJd: number, jdStart: number, jdEnd: number, zodiac?: Zodiac): number[];
10
29
  /** The JD whose real positions are the secondary-progressed positions for the
11
30
  * age (targetJd - natalJd): one day of motion per year of life. */
12
31
  export declare function progressedJd(natalJd: number, targetJd: number, yearLength?: number): number;
32
+ /**
33
+ * Secondary-progressed longitude of a body for a given target date: the
34
+ * "day-for-a-year" method, where one day of real motion after birth maps to one
35
+ * year of life. Equivalent to taking the body's longitude at
36
+ * {@link progressedJd}.
37
+ *
38
+ * @param engine The engine used to evaluate positions.
39
+ * @param body A body id from {@link Engine.bodies}.
40
+ * @param natalJd Natal Julian Day (UT).
41
+ * @param targetJd The date to progress to, Julian Day (UT).
42
+ * @param yearLength Days per year of life. Defaults to the tropical year.
43
+ * @param zodiac Zodiac for the result. Defaults to tropical.
44
+ * @returns The progressed ecliptic longitude in degrees, `[0, 360)`.
45
+ * @example
46
+ * ```ts
47
+ * const natal = julianDay(1990, 6, 10, 14, 30);
48
+ * progressedLongitude(engine, "moon", natal, julianDay(2025, 6, 10));
49
+ * ```
50
+ * @see {@link solarArc} and {@link directedLongitude} for solar-arc directions.
51
+ */
13
52
  export declare function progressedLongitude(engine: Engine, body: BodyId, natalJd: number, targetJd: number, yearLength?: number, zodiac?: Zodiac): number;
14
53
  /** Solar-arc direction angle (degrees, forward): how far the secondary-
15
54
  * progressed Sun has moved from the natal Sun. Add it to any natal longitude. */
@@ -24,6 +63,22 @@ export declare function compositeLongitudes(engine: Engine, jdA: number, jdB: nu
24
63
  export declare function davisonParams(jdA: number, jdB: number, latA: number, lonEastA: number, latB: number, lonEastB: number): [number, number, number];
25
64
  /** The nth-harmonic longitude of a point: lon * n, wrapped to 360. */
26
65
  export declare function harmonicLongitude(lon: number, n: number): number;
66
+ /**
67
+ * The nth-harmonic chart: each body's longitude multiplied by `n` and wrapped
68
+ * to `[0, 360)`. The 5th harmonic surfaces quintiles, the 9th the navamsa, and
69
+ * so on.
70
+ *
71
+ * @param engine The engine used to evaluate positions.
72
+ * @param jd Julian Day (UT).
73
+ * @param bodies The bodies to include.
74
+ * @param n The harmonic number, e.g. `5` or `9`.
75
+ * @param zodiac Zodiac for the base longitudes. Defaults to tropical.
76
+ * @returns Harmonic longitudes in degrees, keyed by body id.
77
+ * @example
78
+ * ```ts
79
+ * harmonicChart(engine, jd, ["sun", "moon", "venus"], 5); // quintile harmonic
80
+ * ```
81
+ */
27
82
  export declare function harmonicChart(engine: Engine, jd: number, bodies: BodyId[], n: number, zodiac?: Zodiac): Record<string, number>;
28
83
  /** Reflection across the solstice (Cancer-Capricorn) axis. */
29
84
  export declare function antiscion(lon: number): number;
@@ -37,15 +92,49 @@ export interface DeclinationPair {
37
92
  b: string;
38
93
  kind: DeclinationKind;
39
94
  }
95
+ /**
96
+ * Parallels and contraparallels among a set of bodies at an instant: pairs
97
+ * whose declinations are equal (parallel) or equal and opposite
98
+ * (contraparallel) within `orb` — the declination analogue of conjunction and
99
+ * opposition.
100
+ *
101
+ * @param engine The engine used to evaluate positions.
102
+ * @param bodies The bodies to compare.
103
+ * @param jd Julian Day (UT).
104
+ * @param orb Declination orb in degrees. Defaults to `1.0`.
105
+ * @returns {@link DeclinationPair}s `{ a, b, kind }`, where `kind` is
106
+ * `"parallel"` or `"contraparallel"`.
107
+ */
40
108
  export declare function declinationAspects(engine: Engine, bodies: BodyId[], jd: number, orb?: number): DeclinationPair[];
41
109
  /** |declination| minus the mean obliquity, degrees. Positive = out of bounds. */
42
110
  export declare function outOfBoundsMargin(engine: Engine, body: BodyId, jd: number): number;
43
111
  export declare function outOfBounds(engine: Engine, body: BodyId, jd: number): boolean;
44
- /** Essential dignities of `body` in `sign`: domicile, exaltation, detriment,
45
- * fall (the last two are the signs opposite domicile and exaltation). */
112
+ /**
113
+ * Essential dignities a body holds in a sign: any of `"domicile"`,
114
+ * `"exaltation"`, `"detriment"`, `"fall"` (the last two are the signs opposite
115
+ * domicile and exaltation). Empty when the body is peregrine there.
116
+ *
117
+ * @param body Body id, e.g. `"mars"`.
118
+ * @param sign A sign index `0`–`11` (Aries = 0) or its name, e.g. `"Aries"`.
119
+ * @returns The dignities held, in the order above; empty if none.
120
+ * @example
121
+ * ```ts
122
+ * dignities("mars", "Aries"); // ["domicile"]
123
+ * dignities("sun", "Libra"); // ["fall"]
124
+ * ```
125
+ */
46
126
  export declare function dignities(body: string, sign: number | string): string[];
47
127
  export declare function dignityOf(engine: Engine, body: BodyId, jd: number, zodiac?: Zodiac): string[];
48
128
  /** Diurnal when the Sun is above the horizon at the given place. */
49
129
  export declare function isDayChart(engine: Engine, jd: number, lat: number, lonEast: number): boolean;
130
+ /**
131
+ * The sect a body belongs to: `"diurnal"` (Sun, Jupiter, Saturn),
132
+ * `"nocturnal"` (Moon, Venus, Mars), or `null` for Mercury and bodies without a
133
+ * fixed sect. Whether a chart is itself day or night — the Sun above or below
134
+ * the horizon — is a separate question.
135
+ *
136
+ * @param body Body id.
137
+ * @returns `"diurnal"`, `"nocturnal"`, or `null`.
138
+ */
50
139
  export declare function planetarySect(body: string): "diurnal" | "nocturnal" | null;
51
140
  export declare function inSect(body: string, dayChart: boolean): boolean | null;
@@ -25,6 +25,25 @@ export function returns(engine, body, natalJd, jdStart, jdEnd, zodiac = "tropica
25
25
  const natalLon = engine.longitude(body, natalJd, { zodiac });
26
26
  return crossings(engine, body, natalLon, jdStart, jdEnd, zodiac, maxHits);
27
27
  }
28
+ /**
29
+ * Solar-return instants in `[jdStart, jdEnd]`: the times the Sun returns to its
30
+ * natal longitude (about once a year). Build a chart at each with
31
+ * {@link Engine.chartAt} for the solar-return chart.
32
+ *
33
+ * @param engine The engine used to evaluate positions.
34
+ * @param natalJd Natal Julian Day (UT) — defines the target Sun longitude.
35
+ * @param jdStart Start of the search window, Julian Day (UT).
36
+ * @param jdEnd End of the search window, Julian Day (UT).
37
+ * @param zodiac Zodiac for the longitude match. Defaults to tropical.
38
+ * @returns Return instants as Julian Days (UT), sorted.
39
+ * @example
40
+ * ```ts
41
+ * const natal = julianDay(1990, 6, 10, 14, 30);
42
+ * const [thisYear] = solarReturn(engine, natal, julianDay(2025, 1, 1), julianDay(2026, 1, 1));
43
+ * const returnChart = engine.chartAt(thisYear, 27.95, -82.46);
44
+ * ```
45
+ * @see {@link lunarReturn} for the monthly Moon return.
46
+ */
28
47
  export function solarReturn(engine, natalJd, jdStart, jdEnd, zodiac = "tropical") {
29
48
  return returns(engine, "sun", natalJd, jdStart, jdEnd, zodiac);
30
49
  }
@@ -37,6 +56,26 @@ export function lunarReturn(engine, natalJd, jdStart, jdEnd, zodiac = "tropical"
37
56
  export function progressedJd(natalJd, targetJd, yearLength = TROPICAL_YEAR) {
38
57
  return natalJd + (targetJd - natalJd) / yearLength;
39
58
  }
59
+ /**
60
+ * Secondary-progressed longitude of a body for a given target date: the
61
+ * "day-for-a-year" method, where one day of real motion after birth maps to one
62
+ * year of life. Equivalent to taking the body's longitude at
63
+ * {@link progressedJd}.
64
+ *
65
+ * @param engine The engine used to evaluate positions.
66
+ * @param body A body id from {@link Engine.bodies}.
67
+ * @param natalJd Natal Julian Day (UT).
68
+ * @param targetJd The date to progress to, Julian Day (UT).
69
+ * @param yearLength Days per year of life. Defaults to the tropical year.
70
+ * @param zodiac Zodiac for the result. Defaults to tropical.
71
+ * @returns The progressed ecliptic longitude in degrees, `[0, 360)`.
72
+ * @example
73
+ * ```ts
74
+ * const natal = julianDay(1990, 6, 10, 14, 30);
75
+ * progressedLongitude(engine, "moon", natal, julianDay(2025, 6, 10));
76
+ * ```
77
+ * @see {@link solarArc} and {@link directedLongitude} for solar-arc directions.
78
+ */
40
79
  export function progressedLongitude(engine, body, natalJd, targetJd, yearLength = TROPICAL_YEAR, zodiac = "tropical") {
41
80
  return engine.longitude(body, progressedJd(natalJd, targetJd, yearLength), { zodiac });
42
81
  }
@@ -82,6 +121,22 @@ export function davisonParams(jdA, jdB, latA, lonEastA, latB, lonEastB) {
82
121
  export function harmonicLongitude(lon, n) {
83
122
  return mod(lon * n, 360);
84
123
  }
124
+ /**
125
+ * The nth-harmonic chart: each body's longitude multiplied by `n` and wrapped
126
+ * to `[0, 360)`. The 5th harmonic surfaces quintiles, the 9th the navamsa, and
127
+ * so on.
128
+ *
129
+ * @param engine The engine used to evaluate positions.
130
+ * @param jd Julian Day (UT).
131
+ * @param bodies The bodies to include.
132
+ * @param n The harmonic number, e.g. `5` or `9`.
133
+ * @param zodiac Zodiac for the base longitudes. Defaults to tropical.
134
+ * @returns Harmonic longitudes in degrees, keyed by body id.
135
+ * @example
136
+ * ```ts
137
+ * harmonicChart(engine, jd, ["sun", "moon", "venus"], 5); // quintile harmonic
138
+ * ```
139
+ */
85
140
  export function harmonicChart(engine, jd, bodies, n, zodiac = "tropical") {
86
141
  const out = {};
87
142
  for (const b of bodies)
@@ -105,6 +160,19 @@ export function declinationAspect(decA, decB, orb = 1.0) {
105
160
  return "contraparallel";
106
161
  return null;
107
162
  }
163
+ /**
164
+ * Parallels and contraparallels among a set of bodies at an instant: pairs
165
+ * whose declinations are equal (parallel) or equal and opposite
166
+ * (contraparallel) within `orb` — the declination analogue of conjunction and
167
+ * opposition.
168
+ *
169
+ * @param engine The engine used to evaluate positions.
170
+ * @param bodies The bodies to compare.
171
+ * @param jd Julian Day (UT).
172
+ * @param orb Declination orb in degrees. Defaults to `1.0`.
173
+ * @returns {@link DeclinationPair}s `{ a, b, kind }`, where `kind` is
174
+ * `"parallel"` or `"contraparallel"`.
175
+ */
108
176
  export function declinationAspects(engine, bodies, jd, orb = 1.0) {
109
177
  const decs = {};
110
178
  for (const b of bodies)
@@ -140,8 +208,20 @@ const EXALTATION = {
140
208
  function signIndex(sign) {
141
209
  return typeof sign === "number" ? sign : SIGNS.indexOf(sign);
142
210
  }
143
- /** Essential dignities of `body` in `sign`: domicile, exaltation, detriment,
144
- * fall (the last two are the signs opposite domicile and exaltation). */
211
+ /**
212
+ * Essential dignities a body holds in a sign: any of `"domicile"`,
213
+ * `"exaltation"`, `"detriment"`, `"fall"` (the last two are the signs opposite
214
+ * domicile and exaltation). Empty when the body is peregrine there.
215
+ *
216
+ * @param body Body id, e.g. `"mars"`.
217
+ * @param sign A sign index `0`–`11` (Aries = 0) or its name, e.g. `"Aries"`.
218
+ * @returns The dignities held, in the order above; empty if none.
219
+ * @example
220
+ * ```ts
221
+ * dignities("mars", "Aries"); // ["domicile"]
222
+ * dignities("sun", "Libra"); // ["fall"]
223
+ * ```
224
+ */
145
225
  export function dignities(body, sign) {
146
226
  const idx = signIndex(sign);
147
227
  const dom = DOMICILE[body] ?? [];
@@ -169,6 +249,15 @@ export function isDayChart(engine, jd, lat, lonEast) {
169
249
  const [, alt] = azAlt(engine.data, sun.lon, sun.lat, jd, lat, lonEast);
170
250
  return alt > 0;
171
251
  }
252
+ /**
253
+ * The sect a body belongs to: `"diurnal"` (Sun, Jupiter, Saturn),
254
+ * `"nocturnal"` (Moon, Venus, Mars), or `null` for Mercury and bodies without a
255
+ * fixed sect. Whether a chart is itself day or night — the Sun above or below
256
+ * the horizon — is a separate question.
257
+ *
258
+ * @param body Body id.
259
+ * @returns `"diurnal"`, `"nocturnal"`, or `null`.
260
+ */
172
261
  export function planetarySect(body) {
173
262
  if (DIURNAL.has(body))
174
263
  return "diurnal";
@@ -0,0 +1,27 @@
1
+ import { Engine, BodyId } from "./chart.js";
2
+ /** Degrees of arc that equal one year of life, per time key. */
3
+ export declare const KEYS: Record<string, number>;
4
+ export declare const TRADITIONAL: BodyId[];
5
+ export interface DirectionArcs {
6
+ mc: number;
7
+ ic: number;
8
+ asc: number | null;
9
+ dsc: number | null;
10
+ }
11
+ /** Direct primary-direction arcs (degrees, [0, 360)) of a body at right
12
+ * ascension `alpha` and declination `delta` to the four angles, for latitude
13
+ * `phi` and right ascension of the MC `ramc`. `asc`/`dsc` are null when the
14
+ * body is circumpolar. */
15
+ export declare function directionArcs(alpha: number, delta: number, ramc: number, phi: number): DirectionArcs;
16
+ /** Years of life corresponding to an arc of direction under a time key. */
17
+ export declare function directionYears(arc: number, key?: string): number;
18
+ export interface PrimaryDirection {
19
+ body: string;
20
+ angle: "MC" | "IC" | "ASC" | "DSC";
21
+ arc: number;
22
+ years: number;
23
+ jd: number;
24
+ }
25
+ /** Direct primary directions of the bodies to the four angles within
26
+ * `maxYears`, by the given time key, sorted by years. */
27
+ export declare function primaryDirections(engine: Engine, natalJd: number, lat: number, lonEast: number, bodies?: BodyId[], key?: string, maxYears?: number, yearLength?: number): PrimaryDirection[];
@@ -0,0 +1,69 @@
1
+ /**
2
+ * astroengine directions -- primary directions to the angles.
3
+ *
4
+ * Primary (mundane) directions carry a planet, by the diurnal rotation of the
5
+ * sphere, to one of the four angles; the arc of rotation, converted by a time
6
+ * key, gives the age of the direction. This covers the well-defined subset:
7
+ * direct directions of a body to the MC, IC, Ascendant, and Descendant.
8
+ *
9
+ * For a body at right ascension alpha and declination delta, latitude phi, and
10
+ * right ascension of the MC (ramc): arc to MC = alpha - ramc; to IC = that
11
+ * - 180; to the Ascendant = alpha - AD - ramc - 90; to the Descendant =
12
+ * alpha + AD - ramc + 90, where AD = asin(tan phi * tan delta) is the
13
+ * ascensional difference. A circumpolar body (|tan phi * tan delta| > 1) has no
14
+ * oblique ascension, so its Ascendant/Descendant directions are undefined. Time
15
+ * keys: Ptolemy 1 deg = 1 year, Naibod 0.9856473 deg = 1 year. Mirrors the
16
+ * Python reference (astroengine/directions.py); the golden fixtures pin them.
17
+ */
18
+ import { angles } from "./houses.js";
19
+ const RAD = Math.PI / 180;
20
+ const DEG = 180 / Math.PI;
21
+ /** Degrees of arc that equal one year of life, per time key. */
22
+ export const KEYS = { ptolemy: 1.0, naibod: 0.9856473 };
23
+ export const TRADITIONAL = ["sun", "moon", "mercury", "venus", "mars", "jupiter", "saturn"];
24
+ const YEAR_DAYS = 365.2422;
25
+ const mod360 = (x) => ((x % 360) + 360) % 360;
26
+ /** Direct primary-direction arcs (degrees, [0, 360)) of a body at right
27
+ * ascension `alpha` and declination `delta` to the four angles, for latitude
28
+ * `phi` and right ascension of the MC `ramc`. `asc`/`dsc` are null when the
29
+ * body is circumpolar. */
30
+ export function directionArcs(alpha, delta, ramc, phi) {
31
+ const arcMc = mod360(alpha - ramc);
32
+ const arcIc = mod360(alpha - ramc - 180);
33
+ const t = Math.tan(phi * RAD) * Math.tan(delta * RAD);
34
+ if (Math.abs(t) > 1)
35
+ return { mc: arcMc, ic: arcIc, asc: null, dsc: null };
36
+ const ad = Math.asin(t) * DEG;
37
+ return {
38
+ mc: arcMc, ic: arcIc,
39
+ asc: mod360(alpha - ad - ramc - 90),
40
+ dsc: mod360(alpha + ad - ramc + 90),
41
+ };
42
+ }
43
+ /** Years of life corresponding to an arc of direction under a time key. */
44
+ export function directionYears(arc, key = "naibod") {
45
+ return arc / KEYS[key];
46
+ }
47
+ /** Direct primary directions of the bodies to the four angles within
48
+ * `maxYears`, by the given time key, sorted by years. */
49
+ export function primaryDirections(engine, natalJd, lat, lonEast, bodies = TRADITIONAL, key = "naibod", maxYears = 90, yearLength = YEAR_DAYS) {
50
+ const armc = angles(engine.data, natalJd, lat, lonEast)[2];
51
+ const ramc = armc * DEG;
52
+ const out = [];
53
+ const ANGLES = ["mc", "ic", "asc", "dsc"];
54
+ for (const b of bodies) {
55
+ const p = engine.position(b, natalJd);
56
+ const arcs = directionArcs(p.ra, p.dec, ramc, lat);
57
+ for (const angle of ANGLES) {
58
+ const arc = arcs[angle];
59
+ if (arc === null)
60
+ continue;
61
+ const years = directionYears(arc, key);
62
+ if (years <= maxYears) {
63
+ out.push({ body: b, angle: angle.toUpperCase(), arc, years, jd: natalJd + years * yearLength });
64
+ }
65
+ }
66
+ }
67
+ out.sort((a, b) => a.years - b.years);
68
+ return out;
69
+ }
@@ -20,5 +20,21 @@ export interface SolarEclipse {
20
20
  }
21
21
  /** Lunar eclipses in [jdStart, jdEnd] (UT JDs). */
22
22
  export declare function lunarEclipses(engine: Engine, jdStart: number, jdEnd: number): LunarEclipse[];
23
- /** Solar eclipses (global circumstances) in [jdStart, jdEnd] (UT JDs). */
23
+ /**
24
+ * Solar eclipses in `[jdStart, jdEnd]`, with global circumstances (not
25
+ * local visibility). Each new Moon in the window is tested for an eclipse and,
26
+ * when found, classified by the Moon's shadow geometry.
27
+ *
28
+ * @param engine The engine used to evaluate positions.
29
+ * @param jdStart Start of the window, Julian Day (UT).
30
+ * @param jdEnd End of the window, Julian Day (UT).
31
+ * @returns {@link SolarEclipse} records: `tMax` (greatest eclipse, JD UT),
32
+ * `type` (`"total"`/`"annular"`/`"hybrid"`/`"partial"`), `gamma` (minimum
33
+ * shadow-axis distance in Earth radii), and the `begin`/`end` JDs.
34
+ * @example
35
+ * ```ts
36
+ * const eclipses = solarEclipses(engine, julianDay(2025, 1, 1), julianDay(2030, 1, 1));
37
+ * eclipses[0].type; // e.g. "partial"
38
+ * ```
39
+ */
24
40
  export declare function solarEclipses(engine: Engine, jdStart: number, jdEnd: number): SolarEclipse[];
@@ -131,7 +131,23 @@ export function lunarEclipses(engine, jdStart, jdEnd) {
131
131
  }
132
132
  return out;
133
133
  }
134
- /** Solar eclipses (global circumstances) in [jdStart, jdEnd] (UT JDs). */
134
+ /**
135
+ * Solar eclipses in `[jdStart, jdEnd]`, with global circumstances (not
136
+ * local visibility). Each new Moon in the window is tested for an eclipse and,
137
+ * when found, classified by the Moon's shadow geometry.
138
+ *
139
+ * @param engine The engine used to evaluate positions.
140
+ * @param jdStart Start of the window, Julian Day (UT).
141
+ * @param jdEnd End of the window, Julian Day (UT).
142
+ * @returns {@link SolarEclipse} records: `tMax` (greatest eclipse, JD UT),
143
+ * `type` (`"total"`/`"annular"`/`"hybrid"`/`"partial"`), `gamma` (minimum
144
+ * shadow-axis distance in Earth radii), and the `begin`/`end` JDs.
145
+ * @example
146
+ * ```ts
147
+ * const eclipses = solarEclipses(engine, julianDay(2025, 1, 1), julianDay(2030, 1, 1));
148
+ * eclipses[0].type; // e.g. "partial"
149
+ * ```
150
+ */
135
151
  export function solarEclipses(engine, jdStart, jdEnd) {
136
152
  const out = [];
137
153
  for (const tNew of syzygies(engine, jdStart - 1, jdEnd + 1, 0.0)) {
@@ -8,22 +8,68 @@ export interface RiseSetOptions {
8
8
  /** Rise/set of the disc center instead of the upper limb. */
9
9
  discCenter?: boolean;
10
10
  }
11
- /** Next rise/set/meridian transit (UT JD) after jdStart, or null when the
12
- * event does not occur in the window (polar day/night). */
11
+ /**
12
+ * The next rise, set, or meridian transit of a body after `jdStart`, as a
13
+ * Julian Day (UT). Accounts for the body's apparent radius, atmospheric
14
+ * refraction, and observer altitude.
15
+ *
16
+ * @param engine The engine used to evaluate positions.
17
+ * @param body A body id from {@link Engine.bodies}.
18
+ * @param jdStart Search start, Julian Day (UT). The result is the first event
19
+ * strictly after this instant.
20
+ * @param latDeg Observer latitude in degrees, north positive.
21
+ * @param lonDeg Observer longitude in degrees, east positive.
22
+ * @param kind `"rise"`, `"set"`, `"mtransit"` (upper/meridian transit), or
23
+ * `"itransit"` (lower transit). Defaults to `"rise"`.
24
+ * @param opts `altM` (observer altitude, m), `pressure` (hPa), `tempC`, and
25
+ * `searchDays` (how far ahead to look; defaults to 2).
26
+ * @returns The event time as a Julian Day (UT), or `null` when it does not
27
+ * occur in the window (e.g. polar day or night).
28
+ * @example
29
+ * ```ts
30
+ * // Next sunrise over London after 2025-06-01
31
+ * const jd = riseSet(engine, "sun", julianDay(2025, 6, 1), 51.5, -0.13, "rise");
32
+ * ```
33
+ */
13
34
  export declare function riseSet(engine: Engine, body: BodyId, jdStart: number, latDeg: number, lonDeg: number, kind?: RiseKind, opts?: RiseSetOptions): number | null;
14
35
  /** UT JDs where the body's apparent longitude crosses targetLon (degrees)
15
36
  * in [jdStart, jdEnd]. Retrograde bodies can cross a degree three times;
16
37
  * every crossing is returned in time order. */
17
38
  export declare function crossings(engine: Engine, body: BodyId, targetLon: number, jdStart: number, jdEnd: number, zodiac?: Zodiac, maxHits?: number): number[];
18
39
  export type PhaseName = "new" | "first_quarter" | "full" | "last_quarter";
19
- /** New/first-quarter/full/last-quarter times in [jdStart, jdEnd], sorted. */
40
+ /**
41
+ * Every principal lunar phase (new, first quarter, full, last quarter) within
42
+ * `[jdStart, jdEnd]`, sorted by time. Found from the Sun–Moon elongation
43
+ * crossing 0°/90°/180°/270°.
44
+ *
45
+ * @param engine The engine used to evaluate positions.
46
+ * @param jdStart Start of the window, Julian Day (UT).
47
+ * @param jdEnd End of the window, Julian Day (UT).
48
+ * @param maxHits Cap on the number of phases returned. Defaults to 60.
49
+ * @returns Sorted `[jdUt, phase]` pairs, where `phase` is one of
50
+ * {@link PhaseName}.
51
+ * @example
52
+ * ```ts
53
+ * const phases = lunarPhases(engine, julianDay(2025, 1, 1), julianDay(2025, 2, 1));
54
+ * // [[jd, "new"], [jd, "first_quarter"], ...]
55
+ * ```
56
+ */
20
57
  export declare function lunarPhases(engine: Engine, jdStart: number, jdEnd: number, maxHits?: number): Array<[number, PhaseName]>;
21
58
  /** Times the body stations (speed crosses zero): [jdUt, direction the body
22
59
  * turns]. Sun and Moon never station. Station timing is ill-conditioned:
23
60
  * expect minute-level differences between ephemerides. */
24
61
  export declare function stations(engine: Engine, body: BodyId, jdStart: number, jdEnd: number, maxHits?: number): Array<[number, "retrograde" | "direct"]>;
25
- /** Gauquelin sector (1..36, float) from rise/set times of the disc center
26
- * with refraction (Swiss Ephemeris method 3). Sectors run from rise: 1-18
27
- * above the horizon, 19-36 below. Null in polar no-rise/no-set
28
- * conditions. */
62
+ /**
63
+ * The Gauquelin sector of a body (1–36, fractional) from the rise/set times of
64
+ * the disc centre with refraction (Swiss Ephemeris method 3). Sectors run from
65
+ * rise: 1–18 above the horizon, 19–36 below.
66
+ *
67
+ * @param engine The engine used to evaluate positions.
68
+ * @param body A body id from {@link Engine.bodies}.
69
+ * @param jdUt Julian Day (UT).
70
+ * @param latDeg Observer latitude in degrees, north positive.
71
+ * @param lonDeg Observer longitude in degrees, east positive.
72
+ * @returns The sector in `[1, 37)`, or `null` in polar no-rise/no-set
73
+ * conditions.
74
+ */
29
75
  export declare function gauquelinSector(engine: Engine, body: BodyId, jdUt: number, latDeg: number, lonDeg: number): number | null;
@@ -44,8 +44,29 @@ function bisect(f, a, b, iters = 45) {
44
44
  }
45
45
  return (a + b) / 2;
46
46
  }
47
- /** Next rise/set/meridian transit (UT JD) after jdStart, or null when the
48
- * event does not occur in the window (polar day/night). */
47
+ /**
48
+ * The next rise, set, or meridian transit of a body after `jdStart`, as a
49
+ * Julian Day (UT). Accounts for the body's apparent radius, atmospheric
50
+ * refraction, and observer altitude.
51
+ *
52
+ * @param engine The engine used to evaluate positions.
53
+ * @param body A body id from {@link Engine.bodies}.
54
+ * @param jdStart Search start, Julian Day (UT). The result is the first event
55
+ * strictly after this instant.
56
+ * @param latDeg Observer latitude in degrees, north positive.
57
+ * @param lonDeg Observer longitude in degrees, east positive.
58
+ * @param kind `"rise"`, `"set"`, `"mtransit"` (upper/meridian transit), or
59
+ * `"itransit"` (lower transit). Defaults to `"rise"`.
60
+ * @param opts `altM` (observer altitude, m), `pressure` (hPa), `tempC`, and
61
+ * `searchDays` (how far ahead to look; defaults to 2).
62
+ * @returns The event time as a Julian Day (UT), or `null` when it does not
63
+ * occur in the window (e.g. polar day or night).
64
+ * @example
65
+ * ```ts
66
+ * // Next sunrise over London after 2025-06-01
67
+ * const jd = riseSet(engine, "sun", julianDay(2025, 6, 1), 51.5, -0.13, "rise");
68
+ * ```
69
+ */
49
70
  export function riseSet(engine, body, jdStart, latDeg, lonDeg, kind = "rise", opts = {}) {
50
71
  const altM = opts.altM ?? 0.0;
51
72
  const pressure = opts.pressure ?? 1013.25;
@@ -110,7 +131,23 @@ export function crossings(engine, body, targetLon, jdStart, jdEnd, zodiac = "tro
110
131
  }
111
132
  return out;
112
133
  }
113
- /** New/first-quarter/full/last-quarter times in [jdStart, jdEnd], sorted. */
134
+ /**
135
+ * Every principal lunar phase (new, first quarter, full, last quarter) within
136
+ * `[jdStart, jdEnd]`, sorted by time. Found from the Sun–Moon elongation
137
+ * crossing 0°/90°/180°/270°.
138
+ *
139
+ * @param engine The engine used to evaluate positions.
140
+ * @param jdStart Start of the window, Julian Day (UT).
141
+ * @param jdEnd End of the window, Julian Day (UT).
142
+ * @param maxHits Cap on the number of phases returned. Defaults to 60.
143
+ * @returns Sorted `[jdUt, phase]` pairs, where `phase` is one of
144
+ * {@link PhaseName}.
145
+ * @example
146
+ * ```ts
147
+ * const phases = lunarPhases(engine, julianDay(2025, 1, 1), julianDay(2025, 2, 1));
148
+ * // [[jd, "new"], [jd, "first_quarter"], ...]
149
+ * ```
150
+ */
114
151
  export function lunarPhases(engine, jdStart, jdEnd, maxHits = 60) {
115
152
  const elong = (t) => mod(engine.longitude("moon", t) - engine.longitude("sun", t), 360);
116
153
  const names = [
@@ -154,10 +191,19 @@ export function stations(engine, body, jdStart, jdEnd, maxHits = 30) {
154
191
  }
155
192
  return out;
156
193
  }
157
- /** Gauquelin sector (1..36, float) from rise/set times of the disc center
158
- * with refraction (Swiss Ephemeris method 3). Sectors run from rise: 1-18
159
- * above the horizon, 19-36 below. Null in polar no-rise/no-set
160
- * conditions. */
194
+ /**
195
+ * The Gauquelin sector of a body (1–36, fractional) from the rise/set times of
196
+ * the disc centre with refraction (Swiss Ephemeris method 3). Sectors run from
197
+ * rise: 1–18 above the horizon, 19–36 below.
198
+ *
199
+ * @param engine The engine used to evaluate positions.
200
+ * @param body A body id from {@link Engine.bodies}.
201
+ * @param jdUt Julian Day (UT).
202
+ * @param latDeg Observer latitude in degrees, north positive.
203
+ * @param lonDeg Observer longitude in degrees, east positive.
204
+ * @returns The sector in `[1, 37)`, or `null` in polar no-rise/no-set
205
+ * conditions.
206
+ */
161
207
  export function gauquelinSector(engine, body, jdUt, latDeg, lonDeg) {
162
208
  const surrounding = (kind) => {
163
209
  let t = riseSet(engine, body, jdUt - 1.3, latDeg, lonDeg, kind, { discCenter: true });