@typescriptify/sweph 1.0.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.
- package/README.md +422 -0
- package/ephe/semo_18.se1 +0 -0
- package/ephe/sepl_18.se1 +0 -0
- package/originalCode/.eslintrc.json +124 -0
- package/originalCode/.gitattributes +2 -0
- package/originalCode/.github/FUNDING.yml +5 -0
- package/originalCode/.github/workflows/test.yml +35 -0
- package/originalCode/LICENSE +840 -0
- package/originalCode/README.md +91 -0
- package/originalCode/binding.gyp +41 -0
- package/originalCode/constants.js +366 -0
- package/originalCode/docs.gif +0 -0
- package/originalCode/index.d.ts +5115 -0
- package/originalCode/index.js +7 -0
- package/originalCode/index.mjs +109 -0
- package/originalCode/package.json +55 -0
- package/originalCode/src/functions/azalt.cpp +39 -0
- package/originalCode/src/functions/azalt_rev.cpp +35 -0
- package/originalCode/src/functions/calc.cpp +29 -0
- package/originalCode/src/functions/calc_pctr.cpp +31 -0
- package/originalCode/src/functions/calc_ut.cpp +29 -0
- package/originalCode/src/functions/close.cpp +6 -0
- package/originalCode/src/functions/cotrans.cpp +26 -0
- package/originalCode/src/functions/cotrans_sp.cpp +26 -0
- package/originalCode/src/functions/cs2degstr.cpp +19 -0
- package/originalCode/src/functions/cs2lonlatstr.cpp +23 -0
- package/originalCode/src/functions/cs2timestr.cpp +23 -0
- package/originalCode/src/functions/csnorm.cpp +15 -0
- package/originalCode/src/functions/csroundsec.cpp +15 -0
- package/originalCode/src/functions/d2l.cpp +15 -0
- package/originalCode/src/functions/date_conversion.cpp +30 -0
- package/originalCode/src/functions/day_of_week.cpp +15 -0
- package/originalCode/src/functions/degnorm.cpp +15 -0
- package/originalCode/src/functions/deltat.cpp +15 -0
- package/originalCode/src/functions/deltat_ex.cpp +24 -0
- package/originalCode/src/functions/difcs2n.cpp +19 -0
- package/originalCode/src/functions/difcsn.cpp +19 -0
- package/originalCode/src/functions/difdeg2n.cpp +19 -0
- package/originalCode/src/functions/difdegn.cpp +19 -0
- package/originalCode/src/functions/fixstar.cpp +32 -0
- package/originalCode/src/functions/fixstar2.cpp +32 -0
- package/originalCode/src/functions/fixstar2_mag.cpp +28 -0
- package/originalCode/src/functions/fixstar2_ut.cpp +32 -0
- package/originalCode/src/functions/fixstar_mag.cpp +28 -0
- package/originalCode/src/functions/fixstar_ut.cpp +32 -0
- package/originalCode/src/functions/gauquelin_sector.cpp +44 -0
- package/originalCode/src/functions/get_ayanamsa.cpp +15 -0
- package/originalCode/src/functions/get_ayanamsa_ex.cpp +27 -0
- package/originalCode/src/functions/get_ayanamsa_ex_ut.cpp +27 -0
- package/originalCode/src/functions/get_ayanamsa_name.cpp +19 -0
- package/originalCode/src/functions/get_ayanamsa_ut.cpp +15 -0
- package/originalCode/src/functions/get_current_file_data.cpp +28 -0
- package/originalCode/src/functions/get_library_path.cpp +8 -0
- package/originalCode/src/functions/get_orbital_elements.cpp +29 -0
- package/originalCode/src/functions/get_planet_name.cpp +19 -0
- package/originalCode/src/functions/get_tid_acc.cpp +7 -0
- package/originalCode/src/functions/heliacal_pheno_ut.cpp +52 -0
- package/originalCode/src/functions/heliacal_ut.cpp +52 -0
- package/originalCode/src/functions/helio_cross.cpp +33 -0
- package/originalCode/src/functions/helio_cross_ut.cpp +33 -0
- package/originalCode/src/functions/house_name.cpp +20 -0
- package/originalCode/src/functions/house_pos.cpp +36 -0
- package/originalCode/src/functions/houses.cpp +35 -0
- package/originalCode/src/functions/houses_armc.cpp +38 -0
- package/originalCode/src/functions/houses_armc_ex2.cpp +47 -0
- package/originalCode/src/functions/houses_ex.cpp +37 -0
- package/originalCode/src/functions/houses_ex2.cpp +46 -0
- package/originalCode/src/functions/jdet_to_utc.cpp +38 -0
- package/originalCode/src/functions/jdut1_to_utc.cpp +38 -0
- package/originalCode/src/functions/julday.cpp +25 -0
- package/originalCode/src/functions/lat_to_lmt.cpp +27 -0
- package/originalCode/src/functions/lmt_to_lat.cpp +27 -0
- package/originalCode/src/functions/lun_eclipse_how.cpp +34 -0
- package/originalCode/src/functions/lun_eclipse_when.cpp +31 -0
- package/originalCode/src/functions/lun_eclipse_when_loc.cpp +39 -0
- package/originalCode/src/functions/lun_occult_when_glob.cpp +35 -0
- package/originalCode/src/functions/lun_occult_when_loc.cpp +43 -0
- package/originalCode/src/functions/lun_occult_where.cpp +34 -0
- package/originalCode/src/functions/mooncross.cpp +26 -0
- package/originalCode/src/functions/mooncross_node.cpp +30 -0
- package/originalCode/src/functions/mooncross_node_ut.cpp +30 -0
- package/originalCode/src/functions/mooncross_ut.cpp +26 -0
- package/originalCode/src/functions/nod_aps.cpp +42 -0
- package/originalCode/src/functions/nod_aps_ut.cpp +42 -0
- package/originalCode/src/functions/orbit_max_min_true_distance.cpp +37 -0
- package/originalCode/src/functions/pheno.cpp +29 -0
- package/originalCode/src/functions/pheno_ut.cpp +29 -0
- package/originalCode/src/functions/radnorm.cpp +15 -0
- package/originalCode/src/functions/refrac.cpp +23 -0
- package/originalCode/src/functions/refrac_extended.cpp +32 -0
- package/originalCode/src/functions/revjul.cpp +33 -0
- package/originalCode/src/functions/rise_trans.cpp +44 -0
- package/originalCode/src/functions/rise_trans_true_hor.cpp +46 -0
- package/originalCode/src/functions/set_delta_t_userdef.cpp +14 -0
- package/originalCode/src/functions/set_ephe_path.cpp +14 -0
- package/originalCode/src/functions/set_jpl_file.cpp +14 -0
- package/originalCode/src/functions/set_sid_mode.cpp +20 -0
- package/originalCode/src/functions/set_tid_acc.cpp +14 -0
- package/originalCode/src/functions/set_topo.cpp +20 -0
- package/originalCode/src/functions/sidtime.cpp +15 -0
- package/originalCode/src/functions/sidtime0.cpp +21 -0
- package/originalCode/src/functions/sol_eclipse_how.cpp +34 -0
- package/originalCode/src/functions/sol_eclipse_when_glob.cpp +31 -0
- package/originalCode/src/functions/sol_eclipse_when_loc.cpp +39 -0
- package/originalCode/src/functions/sol_eclipse_where.cpp +30 -0
- package/originalCode/src/functions/solcross.cpp +26 -0
- package/originalCode/src/functions/solcross_ut.cpp +26 -0
- package/originalCode/src/functions/split_deg.cpp +35 -0
- package/originalCode/src/functions/time_equ.cpp +25 -0
- package/originalCode/src/functions/utc_time_zone.cpp +48 -0
- package/originalCode/src/functions/utc_to_jd.cpp +37 -0
- package/originalCode/src/functions/version.cpp +8 -0
- package/originalCode/src/functions/vis_limit_mag.cpp +50 -0
- package/originalCode/src/sweph.cpp +150 -0
- package/originalCode/src/sweph.h +119 -0
- package/originalCode/swisseph/swecl.c +6428 -0
- package/originalCode/swisseph/swedate.c +588 -0
- package/originalCode/swisseph/swedate.h +81 -0
- package/originalCode/swisseph/swehel.c +3511 -0
- package/originalCode/swisseph/swehouse.c +3143 -0
- package/originalCode/swisseph/swehouse.h +98 -0
- package/originalCode/swisseph/swejpl.c +958 -0
- package/originalCode/swisseph/swejpl.h +103 -0
- package/originalCode/swisseph/swemmoon.c +1930 -0
- package/originalCode/swisseph/swemplan.c +967 -0
- package/originalCode/swisseph/swemptab.h +10640 -0
- package/originalCode/swisseph/swenut2000a.h +2819 -0
- package/originalCode/swisseph/sweodef.h +326 -0
- package/originalCode/swisseph/sweph.c +8614 -0
- package/originalCode/swisseph/sweph.h +849 -0
- package/originalCode/swisseph/swephexp.h +1020 -0
- package/originalCode/swisseph/swephlib.c +4634 -0
- package/originalCode/swisseph/swephlib.h +189 -0
- package/package.json +28 -0
- package/scripts/gen-swemptab.js +177 -0
- package/scripts/gen-swenut2000a.js +106 -0
- package/src/SwissEph/README.md +268 -0
- package/src/SwissEph/UseCases/Ayanamsa.md +363 -0
- package/src/SwissEph/UseCases/AzimuthAltitude.md +408 -0
- package/src/SwissEph/UseCases/CoordinateSystems.md +337 -0
- package/src/SwissEph/UseCases/DateAndTime.md +368 -0
- package/src/SwissEph/UseCases/DeltaT.md +258 -0
- package/src/SwissEph/UseCases/EphemerisFiles.md +338 -0
- package/src/SwissEph/UseCases/FixedStars.md +300 -0
- package/src/SwissEph/UseCases/GauquelinSectors.md +304 -0
- package/src/SwissEph/UseCases/HeliacalEvents.md +396 -0
- package/src/SwissEph/UseCases/HelioCrossings.md +325 -0
- package/src/SwissEph/UseCases/HousePosition.md +254 -0
- package/src/SwissEph/UseCases/HouseSystems.md +279 -0
- package/src/SwissEph/UseCases/LunarEclipse.md +326 -0
- package/src/SwissEph/UseCases/MeridianTransit.md +279 -0
- package/src/SwissEph/UseCases/MoonCrossings.md +373 -0
- package/src/SwissEph/UseCases/NodesAndApsides.md +307 -0
- package/src/SwissEph/UseCases/Occultation.md +352 -0
- package/src/SwissEph/UseCases/OrbitalElements.md +469 -0
- package/src/SwissEph/UseCases/Phenomena.md +328 -0
- package/src/SwissEph/UseCases/PlanetPositions.md +366 -0
- package/src/SwissEph/UseCases/Planetocentric.md +278 -0
- package/src/SwissEph/UseCases/Refraction.md +314 -0
- package/src/SwissEph/UseCases/RiseAndSet.md +433 -0
- package/src/SwissEph/UseCases/SiderealTime.md +302 -0
- package/src/SwissEph/UseCases/SolarEclipse.md +379 -0
- package/src/SwissEph/UseCases/SunCrossings.md +275 -0
- package/src/SwissEph/UseCases/TopocentricCorrection.md +335 -0
- package/src/SwissEph/errors.ts +10 -0
- package/src/SwissEph/index.ts +823 -0
- package/src/SwissEph/types.ts +291 -0
- package/src/constants.ts +762 -0
- package/src/file-reader.ts +147 -0
- package/src/index.ts +10 -0
- package/src/swecl.ts +4526 -0
- package/src/swedate.ts +376 -0
- package/src/swehel.ts +1939 -0
- package/src/swehouse.ts +2167 -0
- package/src/swejpl.ts +470 -0
- package/src/swemmoon.ts +1318 -0
- package/src/swemplan.ts +585 -0
- package/src/swemptab.ts +4448 -0
- package/src/swenut2000a.ts +2763 -0
- package/src/sweph.ts +3993 -0
- package/src/swephlib.ts +2720 -0
- package/src/types.ts +490 -0
- package/tests/c-style/ayanamsa.test.ts +63 -0
- package/tests/c-style/config.test.ts +96 -0
- package/tests/c-style/crossings.test.ts +81 -0
- package/tests/c-style/date-time.test.ts +114 -0
- package/tests/c-style/eclipses.test.ts +84 -0
- package/tests/c-style/fixed-stars.test.ts +66 -0
- package/tests/c-style/heliacal.test.ts +34 -0
- package/tests/c-style/houses.test.ts +135 -0
- package/tests/c-style/math-utils.test.ts +160 -0
- package/tests/c-style/orbital.test.ts +78 -0
- package/tests/c-style/phenomena.test.ts +42 -0
- package/tests/c-style/planetocentric.test.ts +26 -0
- package/tests/c-style/planets.test.ts +117 -0
- package/tests/c-style/rise-set.test.ts +71 -0
- package/tests/helpers.ts +21 -0
- package/tests/modern/ayanamsa.test.ts +47 -0
- package/tests/modern/calc.test.ts +113 -0
- package/tests/modern/config.test.ts +46 -0
- package/tests/modern/crossings.test.ts +45 -0
- package/tests/modern/eclipses.test.ts +81 -0
- package/tests/modern/errors.test.ts +71 -0
- package/tests/modern/heliacal.test.ts +30 -0
- package/tests/modern/houses.test.ts +87 -0
- package/tests/modern/orbital.test.ts +79 -0
- package/tests/modern/phenomena.test.ts +41 -0
- package/tests/modern/rise-set.test.ts +60 -0
- package/tests/modern/statics.test.ts +99 -0
- package/tests/modern/utilities.test.ts +70 -0
- package/tsconfig.json +20 -0
|
@@ -0,0 +1,279 @@
|
|
|
1
|
+
# House Systems
|
|
2
|
+
|
|
3
|
+
In astrology, the **houses** are twelve divisions of the sky (or the ecliptic) as seen from a specific location on Earth at a specific time. While the zodiac signs divide the ecliptic based on the Sun's annual path, the houses divide it based on the Earth's daily rotation. Each house represents a domain of life experience (self, finances, communication, home, creativity, etc.).
|
|
4
|
+
|
|
5
|
+
Because there are many different mathematical ways to divide the sky into twelve sections, there are many **house systems**. Each produces slightly (or sometimes dramatically) different house cusps. The choice of house system is one of the most debated topics in Western astrology. Vedic astrology predominantly uses Whole Sign houses.
|
|
6
|
+
|
|
7
|
+
The Swiss Ephemeris supports over 20 house systems. Given a time and geographic location, it computes the twelve house cusp longitudes plus several important angles (Ascendant, MC, Vertex, etc.).
|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
## Quick Example
|
|
12
|
+
|
|
13
|
+
```typescript
|
|
14
|
+
import { SwissEph } from '../index';
|
|
15
|
+
|
|
16
|
+
const swe = new SwissEph();
|
|
17
|
+
|
|
18
|
+
// January 1, 2000 at noon UT, London
|
|
19
|
+
const jd = SwissEph.julianDay(2000, 1, 1, 12);
|
|
20
|
+
const geo = { longitude: -0.1276, latitude: 51.5074 };
|
|
21
|
+
|
|
22
|
+
const houses = swe.houses(jd, geo, 'P'); // Placidus
|
|
23
|
+
|
|
24
|
+
console.log(`Ascendant: ${houses.ascendant.toFixed(2)} deg`);
|
|
25
|
+
console.log(`MC: ${houses.mc.toFixed(2)} deg`);
|
|
26
|
+
|
|
27
|
+
// House cusps are 1-indexed: cusps[1] through cusps[12]
|
|
28
|
+
for (let i = 1; i <= 12; i++) {
|
|
29
|
+
console.log(`House ${String(i).padStart(2)} cusp: ${houses.cusps[i].toFixed(2)} deg`);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
swe.close();
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
---
|
|
36
|
+
|
|
37
|
+
## Detailed Examples
|
|
38
|
+
|
|
39
|
+
### Comparing house systems
|
|
40
|
+
|
|
41
|
+
```typescript
|
|
42
|
+
import { SwissEph } from '../index';
|
|
43
|
+
|
|
44
|
+
const swe = new SwissEph();
|
|
45
|
+
const jd = SwissEph.julianDay(2024, 3, 20, 12); // Vernal equinox 2024
|
|
46
|
+
const geo = { longitude: -73.9857, latitude: 40.7484 }; // New York
|
|
47
|
+
|
|
48
|
+
const systems = [
|
|
49
|
+
{ code: 'P', name: 'Placidus' },
|
|
50
|
+
{ code: 'K', name: 'Koch' },
|
|
51
|
+
{ code: 'E', name: 'Equal (from Asc)' },
|
|
52
|
+
{ code: 'W', name: 'Whole Sign' },
|
|
53
|
+
{ code: 'R', name: 'Regiomontanus' },
|
|
54
|
+
{ code: 'C', name: 'Campanus' },
|
|
55
|
+
{ code: 'O', name: 'Porphyry' },
|
|
56
|
+
{ code: 'T', name: 'Topocentric (Polich/Page)' },
|
|
57
|
+
];
|
|
58
|
+
|
|
59
|
+
for (const sys of systems) {
|
|
60
|
+
const h = swe.houses(jd, geo, sys.code);
|
|
61
|
+
console.log(`\n--- ${sys.name} (${sys.code}) ---`);
|
|
62
|
+
console.log(` Asc: ${h.ascendant.toFixed(2)} MC: ${h.mc.toFixed(2)}`);
|
|
63
|
+
for (let i = 1; i <= 12; i++) {
|
|
64
|
+
process.stdout.write(` H${i}: ${h.cusps[i].toFixed(1)} `);
|
|
65
|
+
if (i % 4 === 0) process.stdout.write('\n');
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
swe.close();
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
### Whole Sign and Equal houses
|
|
73
|
+
|
|
74
|
+
Whole Sign and Equal houses are the simplest systems. In Whole Sign, each house is exactly one zodiac sign (30 degrees). In Equal, each house is 30 degrees starting from the Ascendant.
|
|
75
|
+
|
|
76
|
+
```typescript
|
|
77
|
+
import { SwissEph } from '../index';
|
|
78
|
+
|
|
79
|
+
const swe = new SwissEph();
|
|
80
|
+
const jd = SwissEph.julianDay(2024, 1, 1, 0);
|
|
81
|
+
const geo = { longitude: 80.27, latitude: 13.08 }; // Chennai, India
|
|
82
|
+
|
|
83
|
+
// Whole Sign houses (popular in Vedic astrology)
|
|
84
|
+
const ws = swe.houses(jd, geo, 'W');
|
|
85
|
+
console.log('Whole Sign:');
|
|
86
|
+
for (let i = 1; i <= 12; i++) {
|
|
87
|
+
// Each cusp is the start of a sign boundary
|
|
88
|
+
console.log(` House ${i}: ${ws.cusps[i].toFixed(2)} deg`);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// Equal houses from Ascendant
|
|
92
|
+
const eq = swe.houses(jd, geo, 'E');
|
|
93
|
+
console.log('\nEqual from Ascendant:');
|
|
94
|
+
for (let i = 1; i <= 12; i++) {
|
|
95
|
+
// Each cusp is exactly 30 degrees apart, starting from the Ascendant
|
|
96
|
+
console.log(` House ${i}: ${eq.cusps[i].toFixed(2)} deg`);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
swe.close();
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
### Accessing all the angle values
|
|
103
|
+
|
|
104
|
+
Beyond the 12 house cusps, the `houses()` function returns several important astronomical/astrological points:
|
|
105
|
+
|
|
106
|
+
```typescript
|
|
107
|
+
import { SwissEph } from '../index';
|
|
108
|
+
|
|
109
|
+
const swe = new SwissEph();
|
|
110
|
+
const jd = SwissEph.julianDay(2024, 6, 21, 12);
|
|
111
|
+
const geo = { longitude: -0.1276, latitude: 51.5074 }; // London
|
|
112
|
+
|
|
113
|
+
const h = swe.houses(jd, geo, 'P');
|
|
114
|
+
|
|
115
|
+
console.log(`Ascendant (ASC): ${h.ascendant.toFixed(4)} deg`);
|
|
116
|
+
console.log(`Midheaven (MC): ${h.mc.toFixed(4)} deg`);
|
|
117
|
+
console.log(`ARMC (sidereal time): ${h.armc.toFixed(4)} deg`);
|
|
118
|
+
console.log(`Vertex: ${h.vertex.toFixed(4)} deg`);
|
|
119
|
+
console.log(`Equatorial Ascendant: ${h.equatorialAscendant.toFixed(4)} deg`);
|
|
120
|
+
console.log(`Co-Ascendant (Koch): ${h.coAscendantKoch.toFixed(4)} deg`);
|
|
121
|
+
console.log(`Co-Ascendant (Munkasey): ${h.coAscendantMunkasey.toFixed(4)} deg`);
|
|
122
|
+
console.log(`Polar Ascendant: ${h.polarAscendant.toFixed(4)} deg`);
|
|
123
|
+
|
|
124
|
+
swe.close();
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
### Computing houses from ARMC directly
|
|
128
|
+
|
|
129
|
+
If you already know the ARMC (sidereal time in degrees), geographic latitude, and obliquity of the ecliptic, you can compute houses without a Julian Day:
|
|
130
|
+
|
|
131
|
+
```typescript
|
|
132
|
+
import { SwissEph } from '../index';
|
|
133
|
+
|
|
134
|
+
const swe = new SwissEph();
|
|
135
|
+
|
|
136
|
+
// ARMC in degrees (sidereal time * 15)
|
|
137
|
+
const armc = 279.50; // example value
|
|
138
|
+
const lat = 51.5074; // London latitude
|
|
139
|
+
const eps = 23.4393; // mean obliquity of the ecliptic (approx)
|
|
140
|
+
|
|
141
|
+
const h = swe.housesFromArmc(armc, lat, eps, 'P');
|
|
142
|
+
console.log(`Ascendant: ${h.ascendant.toFixed(4)} deg`);
|
|
143
|
+
console.log(`MC: ${h.mc.toFixed(4)} deg`);
|
|
144
|
+
|
|
145
|
+
swe.close();
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
### Getting the name of a house system
|
|
149
|
+
|
|
150
|
+
```typescript
|
|
151
|
+
import { SwissEph } from '../index';
|
|
152
|
+
|
|
153
|
+
const swe = new SwissEph();
|
|
154
|
+
|
|
155
|
+
const codes = 'PKEWCRTADFGHIJLMNOQSUVXY'.split('');
|
|
156
|
+
for (const code of codes) {
|
|
157
|
+
console.log(`${code} = ${swe.houseName(code)}`);
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
swe.close();
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
### Gauquelin sectors (36 sectors)
|
|
164
|
+
|
|
165
|
+
The Gauquelin sector system uses 36 sectors instead of 12 houses. When you request system `'G'`, the `cusps` array contains 37 entries (cusps[1] through cusps[36]).
|
|
166
|
+
|
|
167
|
+
```typescript
|
|
168
|
+
import { SwissEph } from '../index';
|
|
169
|
+
|
|
170
|
+
const swe = new SwissEph();
|
|
171
|
+
const jd = SwissEph.julianDay(2024, 1, 1, 12);
|
|
172
|
+
const geo = { longitude: 2.3522, latitude: 48.8566 }; // Paris
|
|
173
|
+
|
|
174
|
+
const g = swe.houses(jd, geo, 'G');
|
|
175
|
+
console.log('Gauquelin sectors (36):');
|
|
176
|
+
for (let i = 1; i <= 36; i++) {
|
|
177
|
+
console.log(` Sector ${String(i).padStart(2)}: ${g.cusps[i].toFixed(2)} deg`);
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
swe.close();
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
---
|
|
184
|
+
|
|
185
|
+
## Deep Explanation
|
|
186
|
+
|
|
187
|
+
### All supported house systems
|
|
188
|
+
|
|
189
|
+
Each system is identified by a single-character code passed as the `system` parameter.
|
|
190
|
+
|
|
191
|
+
| Code | Name | Description |
|
|
192
|
+
|------|----------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------|
|
|
193
|
+
| `P` | Placidus | The most popular system in modern Western astrology. Divides the diurnal and nocturnal semi-arcs of the ecliptic into three equal time intervals. Fails at high latitudes (>66.5 deg) where some points never rise or set. |
|
|
194
|
+
| `K` | Koch | Also called GOH (Geburtsort-Haeuser). Similar to Placidus in concept but uses a different mathematical approach. Also fails at high latitudes. |
|
|
195
|
+
| `E` | Equal (from Ascendant) | Each house is exactly 30 degrees. House 1 starts at the Ascendant. Simple and unambiguous at all latitudes. The MC does not necessarily fall on the 10th cusp. |
|
|
196
|
+
| `W` | Whole Sign | Each house corresponds to an entire zodiac sign. House 1 is the sign containing the Ascendant (cusp = 0 deg of that sign). The oldest known house system, used in Hellenistic and Vedic astrology. |
|
|
197
|
+
| `B` | Alcabitius | Semi-arc system that divides the diurnal semi-arc of the Ascendant into three equal parts. Medieval European system. |
|
|
198
|
+
| `C` | Campanus | Divides the prime vertical (the great circle passing through the east point, zenith, west point, and nadir) into 12 equal arcs of 30 degrees, then projects them onto the ecliptic. |
|
|
199
|
+
| `R` | Regiomontanus | Divides the celestial equator into 12 equal arcs of 30 degrees, then projects them onto the ecliptic via great circles through the north and south points of the horizon. Popular in horary astrology. |
|
|
200
|
+
| `T` | Polich/Page (Topocentric) | Very similar to Placidus in practice but uses a different mathematical model. Claimed to be more accurate for topocentric purposes. Works at all latitudes. |
|
|
201
|
+
| `A` | Equal (from Ascendant) | Same as `E`. Alternative code. |
|
|
202
|
+
| `D` | Equal (from MC) | Each house is 30 degrees starting from the MC (Midheaven). The MC is exactly on the 10th cusp. |
|
|
203
|
+
| `F` | Carter poli-equatorial | Carter's system projecting from the equator. |
|
|
204
|
+
| `G` | Gauquelin sectors | 36 sectors (not 12 houses) used in the Gauquelin research on planetary positions in relation to the angles. |
|
|
205
|
+
| `H` | Horizon / Azimuthal | Divides the horizon into 12 equal segments of 30 degrees, starting from the east point. |
|
|
206
|
+
| `I` | Sunshine (Treindl) | Based on the proportion of daylight/nighttime hours. |
|
|
207
|
+
| `J` | Sunshine (alternative) | Alternative version of the Sunshine system. |
|
|
208
|
+
| `L` | Pullen SD (sinusoidal delta) | Pullen's sinusoidal delta house system. |
|
|
209
|
+
| `M` | Morinus | Divides the equator into 12 equal arcs, then converts each cusp to ecliptic longitude. Does not use the horizon at all -- only depends on sidereal time. |
|
|
210
|
+
| `N` | Pullen SR (sinusoidal ratio) | Pullen's sinusoidal ratio house system. |
|
|
211
|
+
| `O` | Porphyry | Trisects the arcs between the four angles (ASC, IC, DSC, MC). One of the oldest quadrant-based systems. Simple and works at all latitudes. |
|
|
212
|
+
| `Q` | Pullen | Pullen's general house system. |
|
|
213
|
+
| `S` | Sripati | Used in Indian astrology. Similar to Porphyry but shifts each cusp by half a house: each house cusp is the midpoint of the corresponding Porphyry houses. |
|
|
214
|
+
| `U` | Krusinski | Krusinski-Pisa house system. Divides the vertical joining the Ascendant and Descendant. |
|
|
215
|
+
| `V` | Pisa | Variation of the Krusinski system. |
|
|
216
|
+
| `X` | Axial rotation | Based on the Earth's axial rotation projected onto the ecliptic. |
|
|
217
|
+
| `Y` | APC houses | Astrological houses based on the Ascendant parallel circle. |
|
|
218
|
+
|
|
219
|
+
### Cusps array indexing
|
|
220
|
+
|
|
221
|
+
The `cusps` array is **1-indexed**: house cusp longitudes are in `cusps[1]` through `cusps[12]` (or `cusps[36]` for Gauquelin sectors). `cusps[0]` is unused (always 0).
|
|
222
|
+
|
|
223
|
+
```typescript
|
|
224
|
+
const h = swe.houses(jd, geo, 'P');
|
|
225
|
+
// h.cusps[0] -> 0 (unused)
|
|
226
|
+
// h.cusps[1] -> 1st house cusp (same as Ascendant for most systems)
|
|
227
|
+
// h.cusps[2] -> 2nd house cusp
|
|
228
|
+
// ...
|
|
229
|
+
// h.cusps[12] -> 12th house cusp
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
For most quadrant systems (Placidus, Koch, Regiomontanus, Campanus, Topocentric, Porphyry), `cusps[1]` equals the Ascendant and `cusps[10]` equals the MC. For Equal and Whole Sign, `cusps[10]` may differ from the MC.
|
|
233
|
+
|
|
234
|
+
### The angle values explained
|
|
235
|
+
|
|
236
|
+
| Field | Description |
|
|
237
|
+
|------------------------|------------------------------------------------------------------------------------------------|
|
|
238
|
+
| `ascendant` | The ecliptic degree rising on the eastern horizon. The most important angle in a chart. |
|
|
239
|
+
| `mc` | Medium Coeli (Midheaven). The ecliptic degree at the upper meridian (highest point the ecliptic reaches). |
|
|
240
|
+
| `armc` | ARMC = sidereal time expressed in degrees (sidereal time in hours * 15). Used internally for house position calculations. |
|
|
241
|
+
| `vertex` | The ecliptic degree on the prime vertical in the west. Used in synastry and as a "fated point." |
|
|
242
|
+
| `equatorialAscendant` | The Ascendant calculated on the celestial equator (sometimes called the "East Point"). |
|
|
243
|
+
| `coAscendantKoch` | Co-Ascendant as defined by Koch. |
|
|
244
|
+
| `coAscendantMunkasey` | Co-Ascendant as defined by Munkasey. |
|
|
245
|
+
| `polarAscendant` | The Ascendant of the polar opposite latitude (also called the "Anti-Vertex"). |
|
|
246
|
+
|
|
247
|
+
### High-latitude issues
|
|
248
|
+
|
|
249
|
+
**Placidus and Koch** systems fail at latitudes beyond approximately 66.5 degrees (within the Arctic or Antarctic circles). At these latitudes, some ecliptic degrees never rise or set, making it impossible to compute the time-based house divisions these systems require. The engine will return results but they may be unreliable.
|
|
250
|
+
|
|
251
|
+
Systems that work at **all latitudes** include:
|
|
252
|
+
- Equal (`E`, `A`, `D`)
|
|
253
|
+
- Whole Sign (`W`)
|
|
254
|
+
- Porphyry (`O`)
|
|
255
|
+
- Morinus (`M`)
|
|
256
|
+
- Campanus (`C`)
|
|
257
|
+
- Regiomontanus (`R`)
|
|
258
|
+
- Topocentric/Polich-Page (`T`) -- designed to handle extreme latitudes
|
|
259
|
+
- Axial rotation (`X`)
|
|
260
|
+
- APC (`Y`)
|
|
261
|
+
|
|
262
|
+
### Geographic position convention
|
|
263
|
+
|
|
264
|
+
- **Longitude**: Positive = east, negative = west. For example, New York is about -74 degrees, Tokyo is about +139.7 degrees.
|
|
265
|
+
- **Latitude**: Positive = north, negative = south. For example, Sydney is about -33.9 degrees.
|
|
266
|
+
- **Altitude**: In meters above sea level. Optional (defaults to 0). Only affects topocentric corrections.
|
|
267
|
+
|
|
268
|
+
### Choosing a house system
|
|
269
|
+
|
|
270
|
+
There is no universally "correct" house system. The choice depends on your astrological tradition:
|
|
271
|
+
|
|
272
|
+
- **Modern Western astrology**: Placidus (`P`) is by far the most popular, followed by Koch (`K`) and Equal (`E`).
|
|
273
|
+
- **Traditional/Hellenistic astrology**: Whole Sign (`W`) is the historically oldest system and has seen a major revival.
|
|
274
|
+
- **Horary astrology**: Regiomontanus (`R`) is traditional for horary work.
|
|
275
|
+
- **Vedic/Indian astrology (Jyotish)**: Whole Sign (`W`) or Sripati (`S`).
|
|
276
|
+
- **Uranian/Hamburg School**: Often uses Meridian or equal systems.
|
|
277
|
+
- **Research (Gauquelin)**: The Gauquelin sector system (`G`) with 36 sectors.
|
|
278
|
+
|
|
279
|
+
When in doubt, Placidus is the safe default for Western astrology, and Whole Sign for Vedic or Hellenistic work.
|
|
@@ -0,0 +1,326 @@
|
|
|
1
|
+
# Lunar Eclipses
|
|
2
|
+
|
|
3
|
+
A **lunar eclipse** occurs when the Earth passes between the Sun and the Moon, casting Earth's shadow onto the Moon. Unlike solar eclipses, which are only visible from a narrow path on Earth, a lunar eclipse is visible from anywhere on the night side of the planet where the Moon is above the horizon. This makes lunar eclipses far more commonly observed than solar eclipses.
|
|
4
|
+
|
|
5
|
+
Lunar eclipses can only happen during a Full Moon, when the Sun and Moon are on opposite sides of the Earth. But just as with solar eclipses, the Moon's orbit is tilted about 5 degrees from the ecliptic, so most Full Moons pass above or below Earth's shadow. An eclipse only occurs when the Full Moon is near one of its orbital nodes.
|
|
6
|
+
|
|
7
|
+
During a total lunar eclipse, the Moon does not disappear entirely. Instead, it typically turns a deep red or copper color -- sometimes called a "Blood Moon." This happens because Earth's atmosphere bends (refracts) sunlight around the planet's edge, and the atmosphere filters out shorter blue wavelengths while allowing longer red wavelengths to reach the Moon.
|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
## Quick Example
|
|
12
|
+
|
|
13
|
+
```typescript
|
|
14
|
+
import { SwissEph } from '../index';
|
|
15
|
+
|
|
16
|
+
const swe = new SwissEph();
|
|
17
|
+
|
|
18
|
+
// Find the next lunar eclipse after January 1, 2025
|
|
19
|
+
const jd = SwissEph.julianDay(2025, 1, 1, 0);
|
|
20
|
+
const eclipse = swe.lunarEclipseGlobal(jd);
|
|
21
|
+
|
|
22
|
+
const date = SwissEph.fromJulianDay(eclipse.maximum);
|
|
23
|
+
console.log(`Next lunar eclipse: ${date.year}-${date.month}-${date.day}`);
|
|
24
|
+
console.log(`Maximum at JD: ${eclipse.maximum.toFixed(6)}`);
|
|
25
|
+
|
|
26
|
+
swe.close();
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
---
|
|
30
|
+
|
|
31
|
+
## Detailed Examples
|
|
32
|
+
|
|
33
|
+
### The 2025 March 14 total lunar eclipse
|
|
34
|
+
|
|
35
|
+
```typescript
|
|
36
|
+
import { SwissEph } from '../index';
|
|
37
|
+
import { SE_ECL_TOTAL } from '../../constants';
|
|
38
|
+
|
|
39
|
+
const swe = new SwissEph();
|
|
40
|
+
|
|
41
|
+
// Search for the next total lunar eclipse after 2025-01-01
|
|
42
|
+
const jd = SwissEph.julianDay(2025, 1, 1, 0);
|
|
43
|
+
const eclipse = swe.lunarEclipseGlobal(jd, SE_ECL_TOTAL);
|
|
44
|
+
|
|
45
|
+
const fmt = (jd: number) => {
|
|
46
|
+
if (jd === 0) return '(n/a)';
|
|
47
|
+
const d = SwissEph.fromJulianDay(jd);
|
|
48
|
+
const h = Math.floor(d.hour);
|
|
49
|
+
const m = Math.floor((d.hour - h) * 60);
|
|
50
|
+
return `${d.year}-${String(d.month).padStart(2,'0')}-${String(d.day).padStart(2,'0')} ${h}:${String(m).padStart(2,'0')} UT`;
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
console.log(`Total lunar eclipse`);
|
|
54
|
+
console.log(` Maximum: ${fmt(eclipse.maximum)}`);
|
|
55
|
+
console.log(` Penumbral begin: ${fmt(eclipse.penumbralBegin)}`);
|
|
56
|
+
console.log(` Partial begin: ${fmt(eclipse.partialBegin)}`);
|
|
57
|
+
console.log(` Total begin: ${fmt(eclipse.totalBegin)}`);
|
|
58
|
+
console.log(` Total end: ${fmt(eclipse.totalEnd)}`);
|
|
59
|
+
console.log(` Partial end: ${fmt(eclipse.partialEnd)}`);
|
|
60
|
+
console.log(` Penumbral end: ${fmt(eclipse.penumbralEnd)}`);
|
|
61
|
+
// 2025-03-14, maximum around 06:58 UT
|
|
62
|
+
|
|
63
|
+
swe.close();
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
### Eclipse attributes: magnitude and shadow sizes
|
|
67
|
+
|
|
68
|
+
```typescript
|
|
69
|
+
import { SwissEph } from '../index';
|
|
70
|
+
|
|
71
|
+
const swe = new SwissEph();
|
|
72
|
+
|
|
73
|
+
const jd = SwissEph.julianDay(2025, 1, 1, 0);
|
|
74
|
+
const eclipse = swe.lunarEclipseGlobal(jd);
|
|
75
|
+
|
|
76
|
+
// Get detailed attributes at maximum
|
|
77
|
+
const how = swe.lunarEclipseHow(eclipse.maximum);
|
|
78
|
+
|
|
79
|
+
console.log(`Eclipse type flags: ${how.type}`);
|
|
80
|
+
console.log(`Umbral magnitude: ${how.umbraMagnitude.toFixed(4)}`);
|
|
81
|
+
console.log(`Penumbral magnitude: ${how.penumbraMagnitude.toFixed(4)}`);
|
|
82
|
+
console.log(`Moon diameter: ${how.moonDiameter.toFixed(2)} arc-minutes`);
|
|
83
|
+
console.log(`Umbra diameter: ${how.umbraDiameter.toFixed(2)} arc-minutes`);
|
|
84
|
+
console.log(`Penumbra diameter: ${how.penumbraDiameter.toFixed(2)} arc-minutes`);
|
|
85
|
+
console.log(`Sun dist from node: ${how.sunDistanceFromNode.toFixed(2)} deg`);
|
|
86
|
+
|
|
87
|
+
// For the 2025 Mar 14 eclipse, umbral magnitude is about 1.176
|
|
88
|
+
// (greater than 1.0 = total eclipse; the Moon fits entirely inside the umbra)
|
|
89
|
+
|
|
90
|
+
swe.close();
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
### Local visibility: is the eclipse visible from my city?
|
|
94
|
+
|
|
95
|
+
Use `lunarEclipseLocal` to find when the Moon rises and sets during the eclipse at your location, which tells you how much of the eclipse you can observe.
|
|
96
|
+
|
|
97
|
+
```typescript
|
|
98
|
+
import { SwissEph } from '../index';
|
|
99
|
+
|
|
100
|
+
const swe = new SwissEph();
|
|
101
|
+
|
|
102
|
+
const jd = SwissEph.julianDay(2025, 1, 1, 0);
|
|
103
|
+
|
|
104
|
+
// Check visibility from different cities
|
|
105
|
+
const cities = [
|
|
106
|
+
{ name: 'New York', geo: { longitude: -74.006, latitude: 40.713 } },
|
|
107
|
+
{ name: 'London', geo: { longitude: -0.128, latitude: 51.507 } },
|
|
108
|
+
{ name: 'Tokyo', geo: { longitude: 139.692, latitude: 35.690 } },
|
|
109
|
+
{ name: 'Sydney', geo: { longitude: 151.209, latitude: -33.868 } },
|
|
110
|
+
];
|
|
111
|
+
|
|
112
|
+
const fmt = (jd: number) => {
|
|
113
|
+
if (jd === 0) return '(below horizon)';
|
|
114
|
+
const d = SwissEph.fromJulianDay(jd);
|
|
115
|
+
const h = Math.floor(d.hour);
|
|
116
|
+
const m = Math.floor((d.hour - h) * 60);
|
|
117
|
+
return `${h}:${String(m).padStart(2,'0')} UT`;
|
|
118
|
+
};
|
|
119
|
+
|
|
120
|
+
for (const city of cities) {
|
|
121
|
+
const local = swe.lunarEclipseLocal(jd, city.geo);
|
|
122
|
+
const date = SwissEph.fromJulianDay(local.maximum);
|
|
123
|
+
console.log(`\n${city.name} (${date.year}-${date.month}-${date.day}):`);
|
|
124
|
+
console.log(` Maximum: ${fmt(local.maximum)}`);
|
|
125
|
+
console.log(` Moon rise: ${fmt(local.moonRise)}`);
|
|
126
|
+
console.log(` Moon set: ${fmt(local.moonSet)}`);
|
|
127
|
+
console.log(` Umbra magnitude: ${local.attributes.umbraMagnitude.toFixed(3)}`);
|
|
128
|
+
|
|
129
|
+
// Check if the full eclipse is visible
|
|
130
|
+
if (local.moonRise === 0 || local.moonRise < local.penumbralBegin) {
|
|
131
|
+
console.log(` Full eclipse visible (Moon is up the entire time)`);
|
|
132
|
+
} else {
|
|
133
|
+
console.log(` Partial visibility (Moon rises/sets during eclipse)`);
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
swe.close();
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
### Filtering by eclipse type
|
|
141
|
+
|
|
142
|
+
```typescript
|
|
143
|
+
import { SwissEph } from '../index';
|
|
144
|
+
import { SE_ECL_TOTAL, SE_ECL_PARTIAL, SE_ECL_PENUMBRAL } from '../../constants';
|
|
145
|
+
|
|
146
|
+
const swe = new SwissEph();
|
|
147
|
+
|
|
148
|
+
// Find the next penumbral lunar eclipse
|
|
149
|
+
let jd = SwissEph.julianDay(2024, 1, 1, 0);
|
|
150
|
+
const penumbral = swe.lunarEclipseGlobal(jd, SE_ECL_PENUMBRAL);
|
|
151
|
+
const d1 = SwissEph.fromJulianDay(penumbral.maximum);
|
|
152
|
+
console.log(`Next penumbral: ${d1.year}-${d1.month}-${d1.day}`);
|
|
153
|
+
|
|
154
|
+
// Find the next partial lunar eclipse
|
|
155
|
+
const partial = swe.lunarEclipseGlobal(jd, SE_ECL_PARTIAL);
|
|
156
|
+
const d2 = SwissEph.fromJulianDay(partial.maximum);
|
|
157
|
+
console.log(`Next partial: ${d2.year}-${d2.month}-${d2.day}`);
|
|
158
|
+
|
|
159
|
+
// Find the next total lunar eclipse
|
|
160
|
+
const total = swe.lunarEclipseGlobal(jd, SE_ECL_TOTAL);
|
|
161
|
+
const d3 = SwissEph.fromJulianDay(total.maximum);
|
|
162
|
+
console.log(`Next total: ${d3.year}-${d3.month}-${d3.day}`);
|
|
163
|
+
|
|
164
|
+
swe.close();
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
### Listing all lunar eclipses over a period
|
|
168
|
+
|
|
169
|
+
```typescript
|
|
170
|
+
import { SwissEph } from '../index';
|
|
171
|
+
import { SE_ECL_TOTAL, SE_ECL_PARTIAL, SE_ECL_PENUMBRAL } from '../../constants';
|
|
172
|
+
|
|
173
|
+
const swe = new SwissEph();
|
|
174
|
+
|
|
175
|
+
let jd = SwissEph.julianDay(2024, 1, 1, 0);
|
|
176
|
+
const endJd = SwissEph.julianDay(2028, 1, 1, 0);
|
|
177
|
+
|
|
178
|
+
const typeName = (flags: number): string => {
|
|
179
|
+
if (flags & SE_ECL_TOTAL) return 'Total';
|
|
180
|
+
if (flags & SE_ECL_PARTIAL) return 'Partial';
|
|
181
|
+
if (flags & SE_ECL_PENUMBRAL) return 'Penumbral';
|
|
182
|
+
return 'Unknown';
|
|
183
|
+
};
|
|
184
|
+
|
|
185
|
+
console.log('Lunar eclipses 2024-2027:');
|
|
186
|
+
while (jd < endJd) {
|
|
187
|
+
const ecl = swe.lunarEclipseGlobal(jd);
|
|
188
|
+
if (ecl.maximum > endJd) break;
|
|
189
|
+
|
|
190
|
+
const date = SwissEph.fromJulianDay(ecl.maximum);
|
|
191
|
+
const how = swe.lunarEclipseHow(ecl.maximum);
|
|
192
|
+
|
|
193
|
+
console.log(
|
|
194
|
+
` ${date.year}-${String(date.month).padStart(2,'0')}-${String(date.day).padStart(2,'0')}` +
|
|
195
|
+
` ${typeName(ecl.type).padEnd(10)}` +
|
|
196
|
+
` umbra mag: ${how.umbraMagnitude.toFixed(3)}`
|
|
197
|
+
);
|
|
198
|
+
|
|
199
|
+
jd = ecl.maximum + 1;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
swe.close();
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
### Searching backward in time
|
|
206
|
+
|
|
207
|
+
```typescript
|
|
208
|
+
import { SwissEph } from '../index';
|
|
209
|
+
|
|
210
|
+
const swe = new SwissEph();
|
|
211
|
+
|
|
212
|
+
// Find the most recent lunar eclipse before 2025-01-01
|
|
213
|
+
const jd = SwissEph.julianDay(2025, 1, 1, 0);
|
|
214
|
+
const eclipse = swe.lunarEclipseGlobal(jd, 0, true); // backward = true
|
|
215
|
+
|
|
216
|
+
const date = SwissEph.fromJulianDay(eclipse.maximum);
|
|
217
|
+
console.log(`Most recent lunar eclipse: ${date.year}-${date.month}-${date.day}`);
|
|
218
|
+
|
|
219
|
+
swe.close();
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
---
|
|
223
|
+
|
|
224
|
+
## Deep Explanation
|
|
225
|
+
|
|
226
|
+
### Eclipse types
|
|
227
|
+
|
|
228
|
+
| Type | Constant | Description |
|
|
229
|
+
|------|----------|-------------|
|
|
230
|
+
| **Total** | `SE_ECL_TOTAL` (4) | The Moon passes completely into Earth's umbral shadow. The entire lunar disc turns dark/red. Umbral magnitude > 1.0. |
|
|
231
|
+
| **Partial** | `SE_ECL_PARTIAL` (16) | Only part of the Moon enters the umbra. Part of the Moon remains bright. Umbral magnitude between 0.0 and 1.0. |
|
|
232
|
+
| **Penumbral** | `SE_ECL_PENUMBRAL` (64) | The Moon passes only through Earth's penumbral shadow. The dimming is subtle and often hard to notice visually. Umbral magnitude < 0.0. |
|
|
233
|
+
|
|
234
|
+
### Umbra and penumbra
|
|
235
|
+
|
|
236
|
+
Earth casts two nested shadows into space:
|
|
237
|
+
|
|
238
|
+
- **Umbra**: The dark inner cone of shadow where all direct sunlight is blocked. If you were standing on the Moon inside the umbra, you would see Earth completely blocking the Sun.
|
|
239
|
+
- **Penumbra**: The lighter outer shadow where only part of the Sun is blocked. From inside the penumbra, you would see Earth covering part of the Sun but not all of it.
|
|
240
|
+
|
|
241
|
+
The umbra is about 2.5 times the Moon's diameter at the Moon's distance, which is why total lunar eclipses are possible and can last over an hour.
|
|
242
|
+
|
|
243
|
+
### Understanding magnitude
|
|
244
|
+
|
|
245
|
+
For lunar eclipses, **magnitude** has a specific meaning:
|
|
246
|
+
|
|
247
|
+
- **Umbral magnitude** (`umbraMagnitude`): How deeply the Moon penetrates into the umbra, measured as a fraction of the Moon's diameter. A value of 0.0 means the Moon just touches the umbral edge. A value of 1.0 means the Moon's edge just reaches the center of the umbra (the entire Moon is inside). Values greater than 1.0 mean the Moon is well inside the umbra (deeper totality). For the 2025 Mar 14 eclipse, the umbral magnitude is about 1.178.
|
|
248
|
+
|
|
249
|
+
- **Penumbral magnitude** (`penumbraMagnitude`): Same concept but for the penumbra. Always larger than the umbral magnitude.
|
|
250
|
+
|
|
251
|
+
A negative umbral magnitude means the Moon does not enter the umbra at all (purely penumbral eclipse).
|
|
252
|
+
|
|
253
|
+
### The Danjon scale
|
|
254
|
+
|
|
255
|
+
Astronomers use the **Danjon scale** (L value) to classify the visual appearance of a total lunar eclipse:
|
|
256
|
+
|
|
257
|
+
| L | Description |
|
|
258
|
+
|---|-------------|
|
|
259
|
+
| 0 | Very dark eclipse; Moon almost invisible at mid-totality |
|
|
260
|
+
| 1 | Dark eclipse; gray or brownish color; details hard to see |
|
|
261
|
+
| 2 | Deep red or rust-colored; very dark central shadow with brighter edge |
|
|
262
|
+
| 3 | Brick-red; umbral shadow usually has a bright or yellow rim |
|
|
263
|
+
| 4 | Very bright copper-red or orange; umbral shadow has a bluish, very bright rim |
|
|
264
|
+
|
|
265
|
+
The Danjon value cannot be predicted by ephemeris calculations alone -- it depends on atmospheric conditions, particularly volcanic aerosols. Major volcanic eruptions (like Pinatubo in 1991) can produce very dark eclipses (L=0) for several years afterward. The Swiss Ephemeris predicts *when* and *where* eclipses occur, but not their color.
|
|
266
|
+
|
|
267
|
+
### Why the Moon turns red
|
|
268
|
+
|
|
269
|
+
During totality, the only light reaching the Moon has been refracted (bent) through Earth's atmosphere. Earth's atmosphere acts like a lens that bends sunlight around the planet's edge and filters it:
|
|
270
|
+
|
|
271
|
+
1. Short-wavelength light (blue, violet) is scattered away by the atmosphere (the same Rayleigh scattering that makes our sky blue).
|
|
272
|
+
2. Long-wavelength light (red, orange) passes through more easily and is bent toward the Moon.
|
|
273
|
+
|
|
274
|
+
The result is that the Moon is illuminated by the combined light of all the sunrises and sunsets happening around Earth's edge at that moment. If you were standing on the Moon during a total lunar eclipse, you would see Earth surrounded by a bright red ring.
|
|
275
|
+
|
|
276
|
+
### Eclipse timeline (the `lunarEclipseGlobal` result)
|
|
277
|
+
|
|
278
|
+
| Field | Description |
|
|
279
|
+
|-------|-------------|
|
|
280
|
+
| `penumbralBegin` | Moon enters the penumbra. Very subtle dimming begins. |
|
|
281
|
+
| `partialBegin` | Moon enters the umbra. A dark "bite" appears on the Moon's edge. |
|
|
282
|
+
| `totalBegin` | Moon is completely inside the umbra. Totality begins; Moon turns red. |
|
|
283
|
+
| `maximum` | Mid-eclipse. The Moon is deepest inside the shadow. |
|
|
284
|
+
| `totalEnd` | Moon begins to exit the umbra. Totality ends. |
|
|
285
|
+
| `partialEnd` | Moon completely exits the umbra. Only penumbral shading remains. |
|
|
286
|
+
| `penumbralEnd` | Moon exits the penumbra entirely. Eclipse is over. |
|
|
287
|
+
|
|
288
|
+
For a partial eclipse, `totalBegin` and `totalEnd` are 0 (totality never occurs).
|
|
289
|
+
For a penumbral eclipse, `partialBegin`, `partialEnd`, `totalBegin`, and `totalEnd` are all 0.
|
|
290
|
+
|
|
291
|
+
### The `lunarEclipseHow` result
|
|
292
|
+
|
|
293
|
+
| Field | Description |
|
|
294
|
+
|-------|-------------|
|
|
295
|
+
| `type` | Bitmask of eclipse type flags |
|
|
296
|
+
| `umbraMagnitude` | Fraction of Moon's diameter inside the umbra (>1.0 = total) |
|
|
297
|
+
| `penumbraMagnitude` | Fraction of Moon's diameter inside the penumbra |
|
|
298
|
+
| `moonDiameter` | Apparent diameter of the Moon (arc minutes) |
|
|
299
|
+
| `umbraDiameter` | Apparent diameter of Earth's umbral shadow at the Moon's distance (arc minutes) |
|
|
300
|
+
| `penumbraDiameter` | Apparent diameter of Earth's penumbral shadow (arc minutes) |
|
|
301
|
+
| `sunDistanceFromNode` | Sun's angular distance from the nearest lunar node (degrees) |
|
|
302
|
+
|
|
303
|
+
### Differences from solar eclipses
|
|
304
|
+
|
|
305
|
+
| Aspect | Solar Eclipse | Lunar Eclipse |
|
|
306
|
+
|--------|--------------|---------------|
|
|
307
|
+
| **When** | New Moon | Full Moon |
|
|
308
|
+
| **What happens** | Moon blocks Sun | Earth's shadow covers Moon |
|
|
309
|
+
| **Visibility** | Narrow path (100-200 km wide) | Entire night hemisphere |
|
|
310
|
+
| **Duration of totality** | Up to ~7 minutes | Up to ~1 hour 47 minutes |
|
|
311
|
+
| **Frequency** | 2-5 per year globally | 0-3 per year |
|
|
312
|
+
| **Danger** | Never look at Sun without filter | Safe to watch with naked eye |
|
|
313
|
+
| **Timing** | Different for every location | Same UT time for all observers |
|
|
314
|
+
|
|
315
|
+
### Local visibility considerations
|
|
316
|
+
|
|
317
|
+
Unlike solar eclipses, the timing of a lunar eclipse (in UT) is the same for everyone -- the Moon enters and exits Earth's shadow at the same absolute moment. The question is whether the Moon is above the horizon at your location during that time.
|
|
318
|
+
|
|
319
|
+
The `lunarEclipseLocal` result includes `moonRise` and `moonSet` times, which tell you when the Moon is above the horizon at your location. If the Moon rises after penumbral begin or sets before penumbral end, you will miss part of the eclipse.
|
|
320
|
+
|
|
321
|
+
### Tips
|
|
322
|
+
|
|
323
|
+
- When searching for eclipses, advance `jd` by at least 1 day past the previous result to find the next one.
|
|
324
|
+
- A `lunarEclipseHow` call without a `geo` parameter gives the eclipse attributes as seen from the geocenter (Earth's center). With a `geo` parameter, it accounts for the observer's location, which can slightly affect the apparent magnitude since the Moon's position in the umbra shifts slightly with parallax.
|
|
325
|
+
- Penumbral eclipses are very subtle visually. Most observers cannot notice a penumbral eclipse unless the penumbral magnitude exceeds about 0.7.
|
|
326
|
+
- The `sunDistanceFromNode` value indicates how close the geometry is to producing a central eclipse. Smaller values mean deeper eclipses.
|