@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,302 @@
|
|
|
1
|
+
# Sidereal Time
|
|
2
|
+
|
|
3
|
+
**Sidereal time** is "star time" -- it measures the rotation of the Earth relative to the distant stars, rather than relative to the Sun. While a **solar day** (noon to noon) is 24 hours, a **sidereal day** (the time for the stars to return to the same position in the sky) is about **23 hours, 56 minutes, and 4 seconds**. The difference arises because the Earth is simultaneously orbiting the Sun: after one full rotation relative to the stars, it has moved about 1 degree along its orbit, so it needs to rotate a little extra to bring the Sun back to the same position.
|
|
4
|
+
|
|
5
|
+
**Greenwich Sidereal Time (GST)** is the sidereal time at the Prime Meridian (longitude 0 degrees). It tells you which Right Ascension is currently crossing the meridian at Greenwich. To get **Local Sidereal Time (LST)** for any other location, simply add the observer's longitude (in hours):
|
|
6
|
+
|
|
7
|
+
```
|
|
8
|
+
LST = GST + (longitude_degrees / 15)
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
Sidereal time is fundamental to observational astronomy (it tells you which stars and constellations are visible right now), and in astrology it is the basis for house calculations: the **ARMC** (Ascensional Right Ascension of the Midheaven) is simply sidereal time multiplied by 15 (converting hours to degrees).
|
|
12
|
+
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
## Quick Example
|
|
16
|
+
|
|
17
|
+
```typescript
|
|
18
|
+
import { SwissEph } from '../index';
|
|
19
|
+
|
|
20
|
+
const swe = new SwissEph();
|
|
21
|
+
|
|
22
|
+
// Greenwich Sidereal Time at J2000.0
|
|
23
|
+
const jd = SwissEph.julianDay(2000, 1, 1, 12);
|
|
24
|
+
const gst = swe.siderealTime(jd);
|
|
25
|
+
|
|
26
|
+
console.log(`Greenwich Sidereal Time: ${gst.toFixed(6)} hours`);
|
|
27
|
+
// ~18.697374 hours
|
|
28
|
+
|
|
29
|
+
// Convert to hh:mm:ss
|
|
30
|
+
const h = Math.floor(gst);
|
|
31
|
+
const m = Math.floor((gst - h) * 60);
|
|
32
|
+
const s = ((gst - h) * 60 - m) * 60;
|
|
33
|
+
console.log(`GST: ${h}h ${m}m ${s.toFixed(2)}s`);
|
|
34
|
+
|
|
35
|
+
swe.close();
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
---
|
|
39
|
+
|
|
40
|
+
## Detailed Examples
|
|
41
|
+
|
|
42
|
+
### Local Sidereal Time for any location
|
|
43
|
+
|
|
44
|
+
To get the local sidereal time, add the observer's longitude (converted to hours by dividing by 15). East longitudes are positive, west longitudes are negative.
|
|
45
|
+
|
|
46
|
+
```typescript
|
|
47
|
+
import { SwissEph } from '../index';
|
|
48
|
+
|
|
49
|
+
const swe = new SwissEph();
|
|
50
|
+
|
|
51
|
+
const jd = SwissEph.julianDay(2024, 6, 21, 22, ); // June 21, 2024 at 22:00 UT
|
|
52
|
+
|
|
53
|
+
const gst = swe.siderealTime(jd);
|
|
54
|
+
console.log(`Greenwich Sidereal Time: ${formatHMS(gst)}`);
|
|
55
|
+
|
|
56
|
+
// Local Sidereal Time for different cities
|
|
57
|
+
const cities = [
|
|
58
|
+
{ name: 'London', lon: -0.128 },
|
|
59
|
+
{ name: 'New York', lon: -74.006 },
|
|
60
|
+
{ name: 'Tokyo', lon: 139.692 },
|
|
61
|
+
{ name: 'Sydney', lon: 151.209 },
|
|
62
|
+
{ name: 'Mumbai', lon: 72.878 },
|
|
63
|
+
];
|
|
64
|
+
|
|
65
|
+
for (const city of cities) {
|
|
66
|
+
// LST = GST + longitude/15, then normalize to [0, 24)
|
|
67
|
+
let lst = gst + city.lon / 15;
|
|
68
|
+
while (lst < 0) lst += 24;
|
|
69
|
+
while (lst >= 24) lst -= 24;
|
|
70
|
+
|
|
71
|
+
console.log(`${city.name.padEnd(12)} LST: ${formatHMS(lst)}`);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function formatHMS(hours: number): string {
|
|
75
|
+
const h = Math.floor(hours);
|
|
76
|
+
const m = Math.floor((hours - h) * 60);
|
|
77
|
+
const s = ((hours - h) * 60 - m) * 60;
|
|
78
|
+
return `${String(h).padStart(2, '0')}h ${String(m).padStart(2, '0')}m ${s.toFixed(2).padStart(5, '0')}s`;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
swe.close();
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
### What is on the meridian right now?
|
|
85
|
+
|
|
86
|
+
The Local Sidereal Time directly tells you the Right Ascension that is currently crossing your local meridian (due south in the Northern Hemisphere). Any object with that RA is at its highest point in the sky (its culmination or upper transit).
|
|
87
|
+
|
|
88
|
+
```typescript
|
|
89
|
+
import { SwissEph } from '../index';
|
|
90
|
+
import { SE_SUN, SE_MOON, SE_MARS, SE_JUPITER, SEFLG_EQUATORIAL } from '../../constants';
|
|
91
|
+
|
|
92
|
+
const swe = new SwissEph();
|
|
93
|
+
|
|
94
|
+
// Current observer: Los Angeles, some evening
|
|
95
|
+
const lon = -118.243;
|
|
96
|
+
const jd = SwissEph.julianDay(2024, 8, 15, 4); // 4:00 UT = ~9 PM PDT
|
|
97
|
+
|
|
98
|
+
const gst = swe.siderealTime(jd);
|
|
99
|
+
let lst = gst + lon / 15;
|
|
100
|
+
while (lst < 0) lst += 24;
|
|
101
|
+
while (lst >= 24) lst -= 24;
|
|
102
|
+
|
|
103
|
+
console.log(`Local Sidereal Time: ${lst.toFixed(4)} hours`);
|
|
104
|
+
console.log(`RA on the meridian: ${lst.toFixed(4)} hours = ${(lst * 15).toFixed(2)} deg`);
|
|
105
|
+
console.log();
|
|
106
|
+
|
|
107
|
+
// Check how far each planet is from the meridian
|
|
108
|
+
const bodies = [
|
|
109
|
+
{ id: SE_SUN, name: 'Sun' },
|
|
110
|
+
{ id: SE_MOON, name: 'Moon' },
|
|
111
|
+
{ id: SE_MARS, name: 'Mars' },
|
|
112
|
+
{ id: SE_JUPITER, name: 'Jupiter' },
|
|
113
|
+
];
|
|
114
|
+
|
|
115
|
+
for (const b of bodies) {
|
|
116
|
+
const pos = swe.calc(jd, b.id, SEFLG_EQUATORIAL);
|
|
117
|
+
const raHours = pos.longitude / 15; // Convert RA from degrees to hours
|
|
118
|
+
|
|
119
|
+
// Hour angle = LST - RA (how far west of the meridian)
|
|
120
|
+
let ha = lst - raHours;
|
|
121
|
+
while (ha < -12) ha += 24;
|
|
122
|
+
while (ha > 12) ha -= 24;
|
|
123
|
+
|
|
124
|
+
const side = ha > 0 ? 'west of meridian' : 'east of meridian';
|
|
125
|
+
console.log(
|
|
126
|
+
`${b.name.padEnd(9)} RA: ${raHours.toFixed(2)}h ` +
|
|
127
|
+
`HA: ${Math.abs(ha).toFixed(2)}h ${side}` +
|
|
128
|
+
`${Math.abs(ha) < 1 ? ' <-- NEAR TRANSIT' : ''}`
|
|
129
|
+
);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
swe.close();
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
### ARMC: Sidereal time as the basis for house cusps
|
|
136
|
+
|
|
137
|
+
In house calculations, the starting point is the **ARMC** (Ascensional Right Ascension of the MC, also known simply as the MC in Right Ascension). It equals the Local Sidereal Time expressed in degrees:
|
|
138
|
+
|
|
139
|
+
```typescript
|
|
140
|
+
import { SwissEph } from '../index';
|
|
141
|
+
|
|
142
|
+
const swe = new SwissEph();
|
|
143
|
+
|
|
144
|
+
// London, J2000.0
|
|
145
|
+
const jd = SwissEph.julianDay(2000, 1, 1, 12);
|
|
146
|
+
const londonLon = -0.128;
|
|
147
|
+
const londonLat = 51.507;
|
|
148
|
+
|
|
149
|
+
// Get sidereal time and compute ARMC
|
|
150
|
+
const gst = swe.siderealTime(jd);
|
|
151
|
+
let lst = gst + londonLon / 15;
|
|
152
|
+
while (lst < 0) lst += 24;
|
|
153
|
+
while (lst >= 24) lst -= 24;
|
|
154
|
+
|
|
155
|
+
const armc = lst * 15; // Convert hours to degrees
|
|
156
|
+
console.log(`ARMC: ${armc.toFixed(4)} deg`);
|
|
157
|
+
|
|
158
|
+
// Verify: the houses() function returns ARMC as well
|
|
159
|
+
const houses = swe.houses(jd, { longitude: londonLon, latitude: londonLat });
|
|
160
|
+
console.log(`ARMC from houses(): ${houses.armc.toFixed(4)} deg`);
|
|
161
|
+
console.log(`MC: ${houses.mc.toFixed(4)} deg`);
|
|
162
|
+
|
|
163
|
+
// Note: ARMC and MC are NOT the same thing!
|
|
164
|
+
// ARMC is in Right Ascension (equatorial).
|
|
165
|
+
// MC is the ecliptic longitude where the meridian crosses the ecliptic.
|
|
166
|
+
|
|
167
|
+
swe.close();
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
### Sidereal time throughout a day
|
|
171
|
+
|
|
172
|
+
Sidereal time advances about 24h 03m 56s in one solar day (because a sidereal day is shorter, the sidereal clock gains about 3 minutes and 56 seconds per solar day relative to a normal clock):
|
|
173
|
+
|
|
174
|
+
```typescript
|
|
175
|
+
import { SwissEph } from '../index';
|
|
176
|
+
|
|
177
|
+
const swe = new SwissEph();
|
|
178
|
+
|
|
179
|
+
console.log('Sidereal time at Greenwich, March 20, 2024:');
|
|
180
|
+
console.log('UT Time GST');
|
|
181
|
+
|
|
182
|
+
for (let hour = 0; hour <= 24; hour += 3) {
|
|
183
|
+
const jd = SwissEph.julianDay(2024, 3, 20, hour);
|
|
184
|
+
const gst = swe.siderealTime(jd);
|
|
185
|
+
|
|
186
|
+
const h = Math.floor(gst);
|
|
187
|
+
const m = Math.floor((gst - h) * 60);
|
|
188
|
+
const s = ((gst - h) * 60 - m) * 60;
|
|
189
|
+
console.log(
|
|
190
|
+
`${String(hour).padStart(2, '0')}:00 UT ` +
|
|
191
|
+
`${String(h).padStart(2, '0')}h ${String(m).padStart(2, '0')}m ${s.toFixed(1).padStart(4)}s`
|
|
192
|
+
);
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
// Show the sidereal day length
|
|
196
|
+
const jd1 = SwissEph.julianDay(2024, 3, 20, 0);
|
|
197
|
+
const jd2 = SwissEph.julianDay(2024, 3, 21, 0);
|
|
198
|
+
const gst1 = swe.siderealTime(jd1);
|
|
199
|
+
let gst2 = swe.siderealTime(jd2);
|
|
200
|
+
if (gst2 < gst1) gst2 += 24;
|
|
201
|
+
const gain = (gst2 - gst1) - 24;
|
|
202
|
+
console.log(`\nSidereal time gained in 24 solar hours: ${(gain * 60).toFixed(2)} minutes`);
|
|
203
|
+
// ~3.94 minutes
|
|
204
|
+
|
|
205
|
+
swe.close();
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
### At what UT does a specific RA transit?
|
|
209
|
+
|
|
210
|
+
If you want to know when a specific Right Ascension crosses the local meridian:
|
|
211
|
+
|
|
212
|
+
```typescript
|
|
213
|
+
import { SwissEph } from '../index';
|
|
214
|
+
|
|
215
|
+
const swe = new SwissEph();
|
|
216
|
+
|
|
217
|
+
// When does RA = 6h 45m (Sirius, approximately) transit at Greenwich?
|
|
218
|
+
const targetRA = 6 + 45 / 60; // 6.75 hours
|
|
219
|
+
|
|
220
|
+
// Start from midnight on a given date
|
|
221
|
+
const jdStart = SwissEph.julianDay(2024, 1, 15, 0);
|
|
222
|
+
const gstStart = swe.siderealTime(jdStart);
|
|
223
|
+
|
|
224
|
+
// How many sidereal hours until the target RA reaches the meridian?
|
|
225
|
+
let wait = targetRA - gstStart;
|
|
226
|
+
while (wait < 0) wait += 24;
|
|
227
|
+
|
|
228
|
+
// Convert sidereal hours to solar hours
|
|
229
|
+
// 1 sidereal hour = 0.99727 solar hours
|
|
230
|
+
const solarHours = wait * 0.99727;
|
|
231
|
+
|
|
232
|
+
const transitJd = jdStart + solarHours / 24;
|
|
233
|
+
const date = SwissEph.fromJulianDay(transitJd);
|
|
234
|
+
|
|
235
|
+
console.log(`Sirius (RA ~6h 45m) transits Greenwich at approximately:`);
|
|
236
|
+
console.log(` ${date.year}-${String(date.month).padStart(2, '0')}-${String(date.day).padStart(2, '0')} ` +
|
|
237
|
+
`${Math.floor(date.hour)}:${String(Math.floor((date.hour % 1) * 60)).padStart(2, '0')} UT`);
|
|
238
|
+
|
|
239
|
+
swe.close();
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
---
|
|
243
|
+
|
|
244
|
+
## Deep Explanation
|
|
245
|
+
|
|
246
|
+
### Sidereal day vs solar day
|
|
247
|
+
|
|
248
|
+
A **sidereal day** is one complete rotation of Earth relative to the distant stars: 23h 56m 04.0905s of mean solar time. A **solar day** (noon to noon) is longer because while the Earth rotates, it also advances about 1 degree along its orbit. The Sun appears to drift about 1 degree eastward per day against the background stars, so after one sidereal day the Earth must rotate about 4 more minutes to "catch up" to the Sun.
|
|
249
|
+
|
|
250
|
+
This means there are approximately 366.25 sidereal days in one year but only 365.25 solar days. The extra sidereal day comes from the Earth's orbital motion itself -- one full orbit adds one extra rotation relative to the stars.
|
|
251
|
+
|
|
252
|
+
### Mean vs apparent sidereal time
|
|
253
|
+
|
|
254
|
+
The Swiss Ephemeris `siderealTime()` returns **mean sidereal time** corrected for nutation (effectively giving apparent sidereal time). The distinction:
|
|
255
|
+
|
|
256
|
+
- **Mean sidereal time** uses the mean equinox, ignoring the short-period nutation oscillation
|
|
257
|
+
- **Apparent sidereal time** accounts for nutation, which causes the equinox to wobble by up to about 1.2 seconds of time (17" of arc)
|
|
258
|
+
|
|
259
|
+
The nutation correction is small but important for precise house-cusp calculations.
|
|
260
|
+
|
|
261
|
+
### The relationship to Right Ascension
|
|
262
|
+
|
|
263
|
+
Right Ascension is measured along the celestial equator, starting from the vernal equinox (the point where the Sun crosses the equator heading north), and increasing eastward. It is measured in hours (0h to 24h), where 1h = 15 degrees.
|
|
264
|
+
|
|
265
|
+
The Local Sidereal Time is numerically equal to the Right Ascension currently on the observer's meridian. So if LST = 12h 30m, any star with RA = 12h 30m is at its highest point in your sky right now. Stars with smaller RA are west of the meridian (already past their highest), and stars with larger RA are east (still rising).
|
|
266
|
+
|
|
267
|
+
### Hour Angle
|
|
268
|
+
|
|
269
|
+
The **Hour Angle (HA)** of an object is the difference between the Local Sidereal Time and the object's Right Ascension:
|
|
270
|
+
|
|
271
|
+
```
|
|
272
|
+
HA = LST - RA
|
|
273
|
+
```
|
|
274
|
+
|
|
275
|
+
- HA = 0h: the object is on the meridian (transiting)
|
|
276
|
+
- HA > 0: the object is west of the meridian (past transit, heading toward setting)
|
|
277
|
+
- HA < 0: the object is east of the meridian (still rising, before transit)
|
|
278
|
+
- HA = +6h: the object is on the western horizon (approximately)
|
|
279
|
+
- HA = -6h: the object is on the eastern horizon (approximately)
|
|
280
|
+
|
|
281
|
+
### ARMC and house calculations
|
|
282
|
+
|
|
283
|
+
The ARMC (Ascensional Right Ascension of the MC) is the fundamental input for house-cusp calculations. It equals the Local Sidereal Time expressed in degrees (multiply hours by 15). Given the ARMC, the geographic latitude, and the obliquity of the ecliptic, all house systems can compute their cusps.
|
|
284
|
+
|
|
285
|
+
The MC (Medium Coeli, Midheaven) is the ecliptic longitude where the meridian intersects the ecliptic. It is derived from the ARMC by converting from equatorial to ecliptic coordinates. The MC is NOT simply the ARMC in different units -- the conversion depends on the obliquity.
|
|
286
|
+
|
|
287
|
+
### Practical uses of sidereal time
|
|
288
|
+
|
|
289
|
+
- **Telescope alignment**: When setting up a Go-To telescope or an equatorial mount, you need to know the current sidereal time to sync the mount's RA axis
|
|
290
|
+
- **Observing planning**: Knowing the current LST tells you which part of the sky is best placed for observation (objects near the meridian are highest and least affected by atmospheric refraction)
|
|
291
|
+
- **House cusps**: In astrology, sidereal time is the foundation of house calculations via the ARMC
|
|
292
|
+
- **Satellite tracking**: Communication satellites and ground stations use sidereal time to predict satellite positions relative to Earth's rotation
|
|
293
|
+
|
|
294
|
+
### Sidereal time at the vernal equinox
|
|
295
|
+
|
|
296
|
+
At the moment of the vernal equinox (when the Sun crosses 0h RA), the Greenwich Sidereal Time at solar noon equals approximately 0h. This is the annual "reset point." From there, sidereal time gains about 3 minutes 56 seconds per solar day, completing a full 24-hour cycle in one year.
|
|
297
|
+
|
|
298
|
+
Specifically, at the March equinox, the GST at 12:00 UT is close to 0h (it varies slightly because the equinox does not fall at exactly noon). At the June solstice, the GST at noon is close to 6h. At the September equinox, it is close to 12h. At the December solstice, it is close to 18h.
|
|
299
|
+
|
|
300
|
+
### Input time scale
|
|
301
|
+
|
|
302
|
+
The `siderealTime()` method expects the input Julian Day to be in **Universal Time (UT)**, regardless of the `timeMode` setting of the SwissEph instance. This is because sidereal time is a measure of Earth's rotation, which is defined in UT. The function internally applies Delta-T to compute the nutation correction.
|
|
@@ -0,0 +1,379 @@
|
|
|
1
|
+
# Solar Eclipses
|
|
2
|
+
|
|
3
|
+
A **solar eclipse** occurs when the Moon passes between the Earth and the Sun, blocking some or all of the Sun's light. This happens because the Moon's orbit occasionally brings it directly in line between the Earth and Sun. Despite the Sun being about 400 times larger than the Moon, it is also about 400 times farther away, so they appear nearly the same size in our sky -- making total solar eclipses possible.
|
|
4
|
+
|
|
5
|
+
Solar eclipses are among the most dramatic astronomical events. They can only happen during a New Moon, but they do not happen at every New Moon because the Moon's orbit is tilted about 5 degrees relative to the ecliptic. Only when the Moon is near one of its orbital nodes (where its orbit crosses the ecliptic plane) can an eclipse occur.
|
|
6
|
+
|
|
7
|
+
The Swiss Ephemeris can predict solar eclipses with high precision: finding when they happen, what type they are, where the central path falls on Earth, and how the eclipse appears from any given location.
|
|
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 solar eclipse after January 1, 2024
|
|
19
|
+
const jd = SwissEph.julianDay(2024, 1, 1, 0);
|
|
20
|
+
const eclipse = swe.solarEclipseGlobal(jd);
|
|
21
|
+
|
|
22
|
+
// Convert the time of maximum eclipse to a readable date
|
|
23
|
+
const date = SwissEph.fromJulianDay(eclipse.maximum);
|
|
24
|
+
console.log(`Next solar eclipse: ${date.year}-${date.month}-${date.day}`);
|
|
25
|
+
console.log(`Maximum at JD: ${eclipse.maximum.toFixed(6)}`);
|
|
26
|
+
|
|
27
|
+
swe.close();
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
---
|
|
31
|
+
|
|
32
|
+
## Detailed Examples
|
|
33
|
+
|
|
34
|
+
### Finding the 2024 April 8 total solar eclipse
|
|
35
|
+
|
|
36
|
+
```typescript
|
|
37
|
+
import { SwissEph } from '../index';
|
|
38
|
+
import { SE_ECL_TOTAL } from '../../constants';
|
|
39
|
+
|
|
40
|
+
const swe = new SwissEph();
|
|
41
|
+
|
|
42
|
+
// Search for the next total solar eclipse after 2024-01-01
|
|
43
|
+
const jd = SwissEph.julianDay(2024, 1, 1, 0);
|
|
44
|
+
const eclipse = swe.solarEclipseGlobal(jd, SE_ECL_TOTAL);
|
|
45
|
+
|
|
46
|
+
const date = SwissEph.fromJulianDay(eclipse.maximum);
|
|
47
|
+
const hours = (date.hour);
|
|
48
|
+
const h = Math.floor(hours);
|
|
49
|
+
const m = Math.floor((hours - h) * 60);
|
|
50
|
+
const s = Math.round(((hours - h) * 60 - m) * 60);
|
|
51
|
+
console.log(`Total solar eclipse: ${date.year}-${String(date.month).padStart(2,'0')}-${String(date.day).padStart(2,'0')}`);
|
|
52
|
+
console.log(`Maximum at: ${h}:${String(m).padStart(2,'0')}:${String(s).padStart(2,'0')} UT`);
|
|
53
|
+
// Total solar eclipse: 2024-04-08
|
|
54
|
+
// Maximum at: 18:17:... UT
|
|
55
|
+
|
|
56
|
+
swe.close();
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
### Where is the central path?
|
|
60
|
+
|
|
61
|
+
Use `solarEclipseWhere` to find the geographic coordinates of the central line at the moment of maximum eclipse.
|
|
62
|
+
|
|
63
|
+
```typescript
|
|
64
|
+
import { SwissEph } from '../index';
|
|
65
|
+
|
|
66
|
+
const swe = new SwissEph();
|
|
67
|
+
|
|
68
|
+
// First, find the eclipse
|
|
69
|
+
const jd = SwissEph.julianDay(2024, 1, 1, 0);
|
|
70
|
+
const eclipse = swe.solarEclipseGlobal(jd);
|
|
71
|
+
|
|
72
|
+
// Now find where the central line falls at maximum
|
|
73
|
+
const where = swe.solarEclipseWhere(eclipse.maximum);
|
|
74
|
+
console.log(`Central path at maximum:`);
|
|
75
|
+
console.log(` Longitude: ${where.geopos.longitude.toFixed(2)} deg`);
|
|
76
|
+
console.log(` Latitude: ${where.geopos.latitude.toFixed(2)} deg`);
|
|
77
|
+
// For the 2024 Apr 8 eclipse: approximately lon=-104, lat=25 (northern Mexico)
|
|
78
|
+
|
|
79
|
+
console.log(`Eclipse type flags: ${where.type}`);
|
|
80
|
+
console.log(`Obscuration: ${(where.attributes.fraction * 100).toFixed(1)}%`);
|
|
81
|
+
console.log(`Magnitude: ${where.attributes.magnitude.toFixed(4)}`);
|
|
82
|
+
|
|
83
|
+
swe.close();
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
### Tracking the central path across the Earth
|
|
87
|
+
|
|
88
|
+
You can call `solarEclipseWhere` at different times during the eclipse to trace the entire path of the shadow across the Earth's surface.
|
|
89
|
+
|
|
90
|
+
```typescript
|
|
91
|
+
import { SwissEph } from '../index';
|
|
92
|
+
|
|
93
|
+
const swe = new SwissEph();
|
|
94
|
+
|
|
95
|
+
const jd = SwissEph.julianDay(2024, 1, 1, 0);
|
|
96
|
+
const eclipse = swe.solarEclipseGlobal(jd);
|
|
97
|
+
|
|
98
|
+
// Trace from first central contact to last central contact
|
|
99
|
+
// second = start of central phase, third = end of central phase
|
|
100
|
+
const start = eclipse.second;
|
|
101
|
+
const end = eclipse.third;
|
|
102
|
+
const steps = 20;
|
|
103
|
+
const dt = (end - start) / steps;
|
|
104
|
+
|
|
105
|
+
console.log('Central path of the solar eclipse:');
|
|
106
|
+
for (let i = 0; i <= steps; i++) {
|
|
107
|
+
const t = start + i * dt;
|
|
108
|
+
const where = swe.solarEclipseWhere(t);
|
|
109
|
+
const date = SwissEph.fromJulianDay(t);
|
|
110
|
+
const h = Math.floor(date.hour);
|
|
111
|
+
const m = Math.floor((date.hour - h) * 60);
|
|
112
|
+
console.log(
|
|
113
|
+
` ${h}:${String(m).padStart(2,'0')} UT ` +
|
|
114
|
+
`lon=${where.geopos.longitude.toFixed(1)}, lat=${where.geopos.latitude.toFixed(1)}`
|
|
115
|
+
);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
swe.close();
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
### How does the eclipse look from a specific city?
|
|
122
|
+
|
|
123
|
+
Use `solarEclipseHow` to get eclipse attributes at a specific location and time, or use `solarEclipseLocal` to find the next eclipse visible from that location.
|
|
124
|
+
|
|
125
|
+
```typescript
|
|
126
|
+
import { SwissEph } from '../index';
|
|
127
|
+
|
|
128
|
+
const swe = new SwissEph();
|
|
129
|
+
|
|
130
|
+
// Dallas, Texas (in the path of the 2024 April 8 total eclipse)
|
|
131
|
+
const dallas = { longitude: -96.797, latitude: 32.7767 };
|
|
132
|
+
|
|
133
|
+
// Find the next solar eclipse visible from Dallas after 2024-01-01
|
|
134
|
+
const jd = SwissEph.julianDay(2024, 1, 1, 0);
|
|
135
|
+
const local = swe.solarEclipseLocal(jd, dallas);
|
|
136
|
+
|
|
137
|
+
const date = SwissEph.fromJulianDay(local.maximum);
|
|
138
|
+
console.log(`Eclipse visible from Dallas on: ${date.year}-${date.month}-${date.day}`);
|
|
139
|
+
|
|
140
|
+
// Four contact times
|
|
141
|
+
const fmt = (jd: number) => {
|
|
142
|
+
const d = SwissEph.fromJulianDay(jd);
|
|
143
|
+
const h = Math.floor(d.hour);
|
|
144
|
+
const m = Math.floor((d.hour - h) * 60);
|
|
145
|
+
const s = Math.round(((d.hour - h) * 60 - m) * 60);
|
|
146
|
+
return `${h}:${String(m).padStart(2,'0')}:${String(s).padStart(2,'0')} UT`;
|
|
147
|
+
};
|
|
148
|
+
|
|
149
|
+
console.log(`First contact (partial begins): ${fmt(local.firstContact)}`);
|
|
150
|
+
console.log(`Second contact (total begins): ${fmt(local.secondContact)}`);
|
|
151
|
+
console.log(`Maximum eclipse: ${fmt(local.maximum)}`);
|
|
152
|
+
console.log(`Third contact (total ends): ${fmt(local.thirdContact)}`);
|
|
153
|
+
console.log(`Fourth contact (partial ends): ${fmt(local.fourthContact)}`);
|
|
154
|
+
|
|
155
|
+
// Attributes at maximum
|
|
156
|
+
console.log(`\nEclipse attributes:`);
|
|
157
|
+
console.log(` Obscuration fraction: ${(local.attributes.fraction * 100).toFixed(1)}%`);
|
|
158
|
+
console.log(` Magnitude: ${local.attributes.magnitude.toFixed(4)}`);
|
|
159
|
+
console.log(` Sun/Moon diameter ratio: ${local.attributes.ratio.toFixed(4)}`);
|
|
160
|
+
console.log(` Saros cycle: ${local.attributes.sarosCycle}`);
|
|
161
|
+
console.log(` Saros member: ${local.attributes.sarosMember}`);
|
|
162
|
+
|
|
163
|
+
swe.close();
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
### Eclipse attributes at a location using `solarEclipseHow`
|
|
167
|
+
|
|
168
|
+
If you already know the JD of an eclipse (from `solarEclipseGlobal`) and just want to check how it looks from a specific place, `solarEclipseHow` is more direct than `solarEclipseLocal`.
|
|
169
|
+
|
|
170
|
+
```typescript
|
|
171
|
+
import { SwissEph } from '../index';
|
|
172
|
+
|
|
173
|
+
const swe = new SwissEph();
|
|
174
|
+
|
|
175
|
+
// Find the eclipse first
|
|
176
|
+
const jd = SwissEph.julianDay(2024, 1, 1, 0);
|
|
177
|
+
const eclipse = swe.solarEclipseGlobal(jd);
|
|
178
|
+
|
|
179
|
+
// Check from multiple cities
|
|
180
|
+
const cities = [
|
|
181
|
+
{ name: 'Dallas', geo: { longitude: -96.797, latitude: 32.777 } },
|
|
182
|
+
{ name: 'New York', geo: { longitude: -74.006, latitude: 40.713 } },
|
|
183
|
+
{ name: 'London', geo: { longitude: -0.128, latitude: 51.507 } },
|
|
184
|
+
];
|
|
185
|
+
|
|
186
|
+
for (const city of cities) {
|
|
187
|
+
const how = swe.solarEclipseHow(eclipse.maximum, city.geo);
|
|
188
|
+
const fraction = how.attributes.fraction;
|
|
189
|
+
if (fraction > 0) {
|
|
190
|
+
console.log(`${city.name}: ${(fraction * 100).toFixed(1)}% obscured, magnitude ${how.attributes.magnitude.toFixed(3)}`);
|
|
191
|
+
} else {
|
|
192
|
+
console.log(`${city.name}: eclipse not visible`);
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
swe.close();
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
### Filtering by eclipse type
|
|
200
|
+
|
|
201
|
+
You can search specifically for total, annular, partial, or hybrid eclipses.
|
|
202
|
+
|
|
203
|
+
```typescript
|
|
204
|
+
import { SwissEph } from '../index';
|
|
205
|
+
import {
|
|
206
|
+
SE_ECL_TOTAL, SE_ECL_ANNULAR, SE_ECL_PARTIAL, SE_ECL_ANNULAR_TOTAL,
|
|
207
|
+
} from '../../constants';
|
|
208
|
+
|
|
209
|
+
const swe = new SwissEph();
|
|
210
|
+
let jd = SwissEph.julianDay(2024, 1, 1, 0);
|
|
211
|
+
|
|
212
|
+
// Find the next 5 total solar eclipses
|
|
213
|
+
console.log('Next 5 total solar eclipses:');
|
|
214
|
+
for (let i = 0; i < 5; i++) {
|
|
215
|
+
const ecl = swe.solarEclipseGlobal(jd, SE_ECL_TOTAL);
|
|
216
|
+
const date = SwissEph.fromJulianDay(ecl.maximum);
|
|
217
|
+
console.log(` ${date.year}-${String(date.month).padStart(2,'0')}-${String(date.day).padStart(2,'0')}`);
|
|
218
|
+
jd = ecl.maximum + 1; // Move past this eclipse to find the next one
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
// Find the next annular eclipse
|
|
222
|
+
const annular = swe.solarEclipseGlobal(SwissEph.julianDay(2024, 1, 1, 0), SE_ECL_ANNULAR);
|
|
223
|
+
const d = SwissEph.fromJulianDay(annular.maximum);
|
|
224
|
+
console.log(`\nNext annular eclipse: ${d.year}-${d.month}-${d.day}`);
|
|
225
|
+
|
|
226
|
+
// SE_ECL_ANNULAR_TOTAL (= SE_ECL_HYBRID) finds hybrid eclipses
|
|
227
|
+
// A hybrid eclipse is annular in some parts of the path and total in others
|
|
228
|
+
|
|
229
|
+
swe.close();
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
### Searching backward in time
|
|
233
|
+
|
|
234
|
+
Set `backward` to `true` to search for the most recent past eclipse.
|
|
235
|
+
|
|
236
|
+
```typescript
|
|
237
|
+
import { SwissEph } from '../index';
|
|
238
|
+
|
|
239
|
+
const swe = new SwissEph();
|
|
240
|
+
|
|
241
|
+
// Find the most recent solar eclipse before January 1, 2024
|
|
242
|
+
const jd = SwissEph.julianDay(2024, 1, 1, 0);
|
|
243
|
+
const eclipse = swe.solarEclipseGlobal(jd, 0, true); // backward = true
|
|
244
|
+
|
|
245
|
+
const date = SwissEph.fromJulianDay(eclipse.maximum);
|
|
246
|
+
console.log(`Most recent eclipse before 2024: ${date.year}-${date.month}-${date.day}`);
|
|
247
|
+
// 2023-10-14 (annular eclipse across the Americas)
|
|
248
|
+
|
|
249
|
+
swe.close();
|
|
250
|
+
```
|
|
251
|
+
|
|
252
|
+
### Listing eclipses over a range of years
|
|
253
|
+
|
|
254
|
+
```typescript
|
|
255
|
+
import { SwissEph } from '../index';
|
|
256
|
+
import { SE_ECL_TOTAL, SE_ECL_ANNULAR, SE_ECL_PARTIAL, SE_ECL_ANNULAR_TOTAL } from '../../constants';
|
|
257
|
+
|
|
258
|
+
const swe = new SwissEph();
|
|
259
|
+
|
|
260
|
+
let jd = SwissEph.julianDay(2024, 1, 1, 0);
|
|
261
|
+
const endJd = SwissEph.julianDay(2030, 1, 1, 0);
|
|
262
|
+
|
|
263
|
+
const typeNames = (flags: number): string => {
|
|
264
|
+
if (flags & SE_ECL_TOTAL) return 'Total';
|
|
265
|
+
if (flags & SE_ECL_ANNULAR_TOTAL) return 'Hybrid';
|
|
266
|
+
if (flags & SE_ECL_ANNULAR) return 'Annular';
|
|
267
|
+
if (flags & SE_ECL_PARTIAL) return 'Partial';
|
|
268
|
+
return 'Unknown';
|
|
269
|
+
};
|
|
270
|
+
|
|
271
|
+
console.log('Solar eclipses 2024-2029:');
|
|
272
|
+
while (jd < endJd) {
|
|
273
|
+
const ecl = swe.solarEclipseGlobal(jd);
|
|
274
|
+
if (ecl.maximum > endJd) break;
|
|
275
|
+
const date = SwissEph.fromJulianDay(ecl.maximum);
|
|
276
|
+
console.log(` ${date.year}-${String(date.month).padStart(2,'0')}-${String(date.day).padStart(2,'0')} ${typeNames(ecl.type)}`);
|
|
277
|
+
jd = ecl.maximum + 1;
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
swe.close();
|
|
281
|
+
```
|
|
282
|
+
|
|
283
|
+
---
|
|
284
|
+
|
|
285
|
+
## Deep Explanation
|
|
286
|
+
|
|
287
|
+
### Eclipse types
|
|
288
|
+
|
|
289
|
+
| Type | Constant | Description |
|
|
290
|
+
|------|----------|-------------|
|
|
291
|
+
| **Total** | `SE_ECL_TOTAL` (4) | The Moon completely covers the Sun's disc. The sky goes dark, the corona becomes visible. Only possible when the Moon is near perigee (closer to Earth). |
|
|
292
|
+
| **Annular** | `SE_ECL_ANNULAR` (8) | The Moon is centered on the Sun but too far away (near apogee) to cover it completely, leaving a bright ring ("annulus") of sunlight around the Moon. |
|
|
293
|
+
| **Partial** | `SE_ECL_PARTIAL` (16) | The Moon covers only part of the Sun's disc. This happens at the edges of a total/annular path, or when the alignment is not precise enough for a central eclipse. |
|
|
294
|
+
| **Hybrid** | `SE_ECL_ANNULAR_TOTAL` (32) | Also called `SE_ECL_HYBRID`. The eclipse is annular at some points on the path and total at others. This rare type occurs when the Moon's shadow tip just barely reaches Earth's surface. |
|
|
295
|
+
|
|
296
|
+
The `type` field in the result is a bitmask. You can also see `SE_ECL_CENTRAL` (1) if the eclipse has a central path, or `SE_ECL_NONCENTRAL` (2) if it does not. Use bitwise AND to check:
|
|
297
|
+
|
|
298
|
+
```typescript
|
|
299
|
+
import { SE_ECL_TOTAL, SE_ECL_CENTRAL } from '../../constants';
|
|
300
|
+
|
|
301
|
+
if (eclipse.type & SE_ECL_TOTAL) {
|
|
302
|
+
console.log('This is a total eclipse');
|
|
303
|
+
}
|
|
304
|
+
if (eclipse.type & SE_ECL_CENTRAL) {
|
|
305
|
+
console.log('It has a central path across Earth');
|
|
306
|
+
}
|
|
307
|
+
```
|
|
308
|
+
|
|
309
|
+
### The four contacts
|
|
310
|
+
|
|
311
|
+
A solar eclipse has up to four "contact" points, which define the key moments of the eclipse for a specific location:
|
|
312
|
+
|
|
313
|
+
1. **First contact** (`firstContact` / `first`): The Moon's disc first touches the Sun's disc. Partial phase begins.
|
|
314
|
+
2. **Second contact** (`secondContact` / `second`): The Moon completely covers (or maximally overlaps) the Sun. Totality or annularity begins. Only occurs for total/annular eclipses.
|
|
315
|
+
3. **Third contact** (`thirdContact` / `third`): Totality or annularity ends. The Moon begins to move off the Sun's disc.
|
|
316
|
+
4. **Fourth contact** (`fourthContact` / `fourth`): The Moon's disc completely separates from the Sun. Eclipse ends.
|
|
317
|
+
|
|
318
|
+
For a partial eclipse, only first and fourth contacts occur (second and third will be 0).
|
|
319
|
+
|
|
320
|
+
In the **global** result (`solarEclipseGlobal`), the contact times refer to the eclipse overall on Earth's surface. In the **local** result (`solarEclipseLocal`), they refer to the specific observer's location.
|
|
321
|
+
|
|
322
|
+
### Eclipse magnitude vs obscuration
|
|
323
|
+
|
|
324
|
+
These are two different measurements that are often confused:
|
|
325
|
+
|
|
326
|
+
- **Magnitude** (`attributes.magnitude`): The fraction of the Sun's *diameter* covered by the Moon at maximum eclipse. A magnitude of 1.0 means the Moon's diameter exactly matches the Sun's; greater than 1.0 means the Moon is larger (deep total eclipse). For an annular eclipse, magnitude is less than 1.0.
|
|
327
|
+
|
|
328
|
+
- **Obscuration fraction** (`attributes.fraction`): The fraction of the Sun's *area* that is covered. This is what matters for how much light is blocked. Because area scales with the square of the diameter, a magnitude of 0.5 (half the diameter) covers only about 40% of the area, not 50%.
|
|
329
|
+
|
|
330
|
+
For total eclipses, the obscuration is 1.0 (100%). For annular eclipses, even though the Moon is centered on the Sun, the obscuration is less than 1.0 because the ring of Sun around the Moon still shines.
|
|
331
|
+
|
|
332
|
+
### The Saros cycle
|
|
333
|
+
|
|
334
|
+
The **Saros cycle** is a period of approximately 6,585.3 days (18 years, 11 days, and 8 hours) after which the Sun, Moon, and nodes return to nearly the same relative geometry. This means eclipses tend to repeat in a predictable pattern.
|
|
335
|
+
|
|
336
|
+
Each eclipse belongs to a **Saros series** (identified by `attributes.sarosCycle`), and its position within that series is given by `attributes.sarosMember`. A Saros series typically produces about 70-80 eclipses over 1,200-1,400 years, starting with small partial eclipses near one pole, progressing to central (total or annular) eclipses, and ending with small partials near the opposite pole.
|
|
337
|
+
|
|
338
|
+
Because the Saros period is not a whole number of days (the extra 8 hours), each successive eclipse in a series occurs about 120 degrees further west in longitude. After three Saros cycles (54 years 34 days, called an "Exeligmos"), the eclipse returns to approximately the same longitude.
|
|
339
|
+
|
|
340
|
+
### The `solarEclipseGlobal` result
|
|
341
|
+
|
|
342
|
+
| Field | Description |
|
|
343
|
+
|-------|-------------|
|
|
344
|
+
| `type` | Bitmask of eclipse type flags |
|
|
345
|
+
| `maximum` | JD of maximum eclipse (greatest magnitude) |
|
|
346
|
+
| `first` | JD when the eclipse first begins anywhere on Earth |
|
|
347
|
+
| `second` | JD when central eclipse begins (or totality/annularity begins) |
|
|
348
|
+
| `third` | JD when central eclipse ends |
|
|
349
|
+
| `fourth` | JD when the eclipse ends everywhere on Earth |
|
|
350
|
+
| `sunrise` | JD of sunrise for the location of maximum (0 if not applicable) |
|
|
351
|
+
| `sunset` | JD of sunset for the location of maximum (0 if not applicable) |
|
|
352
|
+
|
|
353
|
+
### The `solarEclipseLocal` attributes
|
|
354
|
+
|
|
355
|
+
| Field | Description |
|
|
356
|
+
|-------|-------------|
|
|
357
|
+
| `fraction` | Obscuration: fraction of Sun's area covered (0.0 to 1.0) |
|
|
358
|
+
| `ratio` | Ratio of apparent lunar diameter to solar diameter |
|
|
359
|
+
| `magnitude` | Eclipse magnitude: fraction of Sun's diameter covered |
|
|
360
|
+
| `sarosCycle` | Saros series number |
|
|
361
|
+
| `sarosMember` | Member number within the Saros series |
|
|
362
|
+
| `solarDiameter` | Apparent diameter of the Sun (arc seconds) |
|
|
363
|
+
| `lunarDiameter` | Apparent diameter of the Moon (arc seconds) |
|
|
364
|
+
| `sarosRepetition` | How many eclipses in this Saros series so far |
|
|
365
|
+
| `eclipseLongitude` | Ecliptic longitude where greatest eclipse occurs |
|
|
366
|
+
| `eclipseLatitude` | Ecliptic latitude where greatest eclipse occurs |
|
|
367
|
+
| `eclipseMagnitude` | Magnitude at the point of greatest eclipse |
|
|
368
|
+
| `sunAltitude` | Altitude of the Sun at the location during maximum |
|
|
369
|
+
|
|
370
|
+
### Safety note
|
|
371
|
+
|
|
372
|
+
Never look directly at the Sun during a partial or annular eclipse without proper solar filters. Only during the brief totality phase of a total eclipse (when the Sun is completely covered) is it safe to view without protection. The Swiss Ephemeris helps you calculate exactly when totality begins and ends at your location (second and third contacts), but always exercise caution and use certified eclipse glasses or solar filters.
|
|
373
|
+
|
|
374
|
+
### Tips for working with eclipse calculations
|
|
375
|
+
|
|
376
|
+
- When iterating through eclipses, advance `jd` by at least 1 day past the previous eclipse's maximum to avoid finding the same eclipse again.
|
|
377
|
+
- The `type` filter parameter in `solarEclipseGlobal` skips eclipses that do not match. If you pass `SE_ECL_TOTAL`, it will skip annular, partial, and hybrid eclipses and return the next total one.
|
|
378
|
+
- For the most precise local timing, use `solarEclipseLocal` rather than `solarEclipseGlobal` followed by `solarEclipseHow`. The local function finds the eclipse that is actually visible from that location.
|
|
379
|
+
- Contact times that are 0 indicate that contact did not occur (e.g., second and third contacts are 0 for a partial eclipse, since totality/annularity never happens).
|