@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
package/src/sweph.ts
ADDED
|
@@ -0,0 +1,3993 @@
|
|
|
1
|
+
/*************************************************************
|
|
2
|
+
* sweph.ts — Main Swiss Ephemeris calculation engine
|
|
3
|
+
* Translated from sweph.c
|
|
4
|
+
*
|
|
5
|
+
* Copyright (C) 1997 - 2021 Astrodienst AG, Switzerland. (AGPL)
|
|
6
|
+
*************************************************************/
|
|
7
|
+
|
|
8
|
+
import {
|
|
9
|
+
SE_VERSION, PI, TWOPI, DEGTORAD, RADTODEG,
|
|
10
|
+
J2000, B1950, J1900, B1850, STR,
|
|
11
|
+
SE_ECL_NUT,
|
|
12
|
+
SE_SUN, SE_MOON, SE_MERCURY, SE_VENUS, SE_MARS,
|
|
13
|
+
SE_JUPITER, SE_SATURN, SE_URANUS, SE_NEPTUNE, SE_PLUTO,
|
|
14
|
+
SE_MEAN_NODE, SE_TRUE_NODE, SE_MEAN_APOG, SE_OSCU_APOG,
|
|
15
|
+
SE_EARTH, SE_CHIRON, SE_PHOLUS,
|
|
16
|
+
SE_CERES, SE_PALLAS, SE_JUNO, SE_VESTA,
|
|
17
|
+
SE_INTP_APOG, SE_INTP_PERG,
|
|
18
|
+
SE_NPLANETS, SE_AST_OFFSET, SE_PLMOON_OFFSET,
|
|
19
|
+
SE_FICT_OFFSET, SE_FICT_OFFSET_1, SE_FICT_MAX, SE_NFICT_ELEM,
|
|
20
|
+
SE_COMET_OFFSET,
|
|
21
|
+
SE_NALL_NAT_POINTS,
|
|
22
|
+
SEFLG_JPLEPH, SEFLG_SWIEPH, SEFLG_MOSEPH,
|
|
23
|
+
SEFLG_HELCTR, SEFLG_TRUEPOS, SEFLG_J2000, SEFLG_NONUT,
|
|
24
|
+
SEFLG_SPEED3, SEFLG_SPEED, SEFLG_NOGDEFL, SEFLG_NOABERR,
|
|
25
|
+
SEFLG_EQUATORIAL, SEFLG_XYZ, SEFLG_RADIANS,
|
|
26
|
+
SEFLG_BARYCTR, SEFLG_TOPOCTR,
|
|
27
|
+
SEFLG_SIDEREAL, SEFLG_ICRS,
|
|
28
|
+
SEFLG_DPSIDEPS_1980, SEFLG_JPLHOR, SEFLG_JPLHOR_APPROX,
|
|
29
|
+
SEFLG_CENTER_BODY, SEFLG_TEST_PLMOON,
|
|
30
|
+
SEFLG_EPHMASK, SEFLG_DEFAULTEPH,
|
|
31
|
+
SE_SIDBITS, SE_SIDBIT_ECL_T0, SE_SIDBIT_SSY_PLANE,
|
|
32
|
+
SE_SIDBIT_USER_UT, SE_SIDBIT_ECL_DATE,
|
|
33
|
+
SE_SIDBIT_NO_PREC_OFFSET, SE_SIDBIT_PREC_ORIG,
|
|
34
|
+
SE_SIDM_FAGAN_BRADLEY, SE_SIDM_LAHIRI,
|
|
35
|
+
SE_SIDM_TRUE_CITRA, SE_SIDM_TRUE_REVATI, SE_SIDM_TRUE_PUSHYA,
|
|
36
|
+
SE_SIDM_TRUE_SHEORAN, SE_SIDM_TRUE_MULA,
|
|
37
|
+
SE_SIDM_GALCENT_0SAG, SE_SIDM_GALCENT_RGILBRAND,
|
|
38
|
+
SE_SIDM_GALEQU_IAU1958, SE_SIDM_GALEQU_TRUE,
|
|
39
|
+
SE_SIDM_GALEQU_MULA, SE_SIDM_GALALIGN_MARDYKS,
|
|
40
|
+
SE_SIDM_GALCENT_MULA_WILHELM, SE_SIDM_GALCENT_COCHRANE,
|
|
41
|
+
SE_SIDM_GALEQU_FIORENZA, SE_SIDM_VALENS_MOON,
|
|
42
|
+
SE_SIDM_J2000, SE_SIDM_J1900, SE_SIDM_B1950,
|
|
43
|
+
SE_SIDM_SURYASIDDHANTA, SE_SIDM_SURYASIDDHANTA_MSUN,
|
|
44
|
+
SE_SIDM_ARYABHATA, SE_SIDM_ARYABHATA_MSUN,
|
|
45
|
+
SE_SIDM_SS_REVATI, SE_SIDM_SS_CITRA,
|
|
46
|
+
SE_SIDM_ARYABHATA_522,
|
|
47
|
+
SE_SIDM_BABYL_BRITTON, SE_SIDM_BABYL_KUGLER1,
|
|
48
|
+
SE_SIDM_BABYL_KUGLER2, SE_SIDM_BABYL_KUGLER3,
|
|
49
|
+
SE_SIDM_BABYL_HUBER, SE_SIDM_BABYL_ETPSC,
|
|
50
|
+
SE_SIDM_ALDEBARAN_15TAU, SE_SIDM_HIPPARCHOS, SE_SIDM_SASSANIAN,
|
|
51
|
+
SE_SIDM_LAHIRI_1940, SE_SIDM_LAHIRI_VP285,
|
|
52
|
+
SE_SIDM_KRISHNAMURTI_VP291, SE_SIDM_LAHIRI_ICRC,
|
|
53
|
+
SE_SIDM_USER, SE_NSIDM_PREDEF,
|
|
54
|
+
SE_PLANET_NAMES, SE_FICTITIOUS_NAMES,
|
|
55
|
+
SEI_EPSILON, SEI_NUTATION,
|
|
56
|
+
SEI_EMB, SEI_EARTH, SEI_SUN, SEI_MOON, SEI_MERCURY, SEI_VENUS,
|
|
57
|
+
SEI_MARS, SEI_JUPITER, SEI_SATURN, SEI_URANUS, SEI_NEPTUNE, SEI_PLUTO,
|
|
58
|
+
SEI_SUNBARY, SEI_ANYBODY,
|
|
59
|
+
SEI_CHIRON, SEI_PHOLUS, SEI_CERES, SEI_PALLAS, SEI_JUNO, SEI_VESTA,
|
|
60
|
+
SEI_NPLANETS,
|
|
61
|
+
SEI_MEAN_NODE, SEI_TRUE_NODE, SEI_MEAN_APOG, SEI_OSCU_APOG,
|
|
62
|
+
SEI_INTP_APOG, SEI_INTP_PERG,
|
|
63
|
+
SEI_NNODE_ETC,
|
|
64
|
+
SEI_FLG_HELIO, SEI_FLG_ROTATE, SEI_FLG_ELLIPSE, SEI_FLG_EMBHEL,
|
|
65
|
+
SEI_FILE_PLANET, SEI_FILE_MOON, SEI_FILE_MAIN_AST, SEI_FILE_ANY_AST,
|
|
66
|
+
OK, ERR, NOT_AVAILABLE, BEYOND_EPH_LIMITS,
|
|
67
|
+
J_TO_J2000, J2000_TO_J,
|
|
68
|
+
AUNIT, CLIGHT, HELGRAVCONST, GEOGCONST,
|
|
69
|
+
SUN_EARTH_MRAT, EARTH_MOON_MRAT, KGAUSS,
|
|
70
|
+
EARTH_RADIUS, EARTH_OBLATENESS, EARTH_ROT_SPEED,
|
|
71
|
+
LIGHTTIME_AUNIT, SUN_RADIUS, PARSEC_TO_AUNIT, KM_S_TO_AU_CTY,
|
|
72
|
+
SSY_PLANE_NODE_E2000, SSY_PLANE_NODE, SSY_PLANE_INCL,
|
|
73
|
+
MOON_SPEED_INTV, PLAN_SPEED_INTV,
|
|
74
|
+
MEAN_NODE_SPEED_INTV, NODE_CALC_INTV, NODE_CALC_INTV_MOSH,
|
|
75
|
+
NUT_SPEED_INTV, DEFL_SPEED_INTV,
|
|
76
|
+
MOSHPLEPH_START, MOSHPLEPH_END,
|
|
77
|
+
MOSHLUEPH_START, MOSHLUEPH_END,
|
|
78
|
+
MOSHNDEPH_START, MOSHNDEPH_END,
|
|
79
|
+
CHIRON_START, CHIRON_END, PHOLUS_START, PHOLUS_END,
|
|
80
|
+
SE_TIDAL_DEFAULT, SE_TIDAL_MOSEPH,
|
|
81
|
+
SE_TIDAL_DE200, SE_TIDAL_DE403, SE_TIDAL_DE404, SE_TIDAL_DE405,
|
|
82
|
+
SE_TIDAL_DE406, SE_TIDAL_DE421, SE_TIDAL_DE422, SE_TIDAL_DE430,
|
|
83
|
+
SE_TIDAL_DE431, SE_TIDAL_DE441,
|
|
84
|
+
SE_DE_NUMBER,
|
|
85
|
+
SEMOD_PREC_NEWCOMB,
|
|
86
|
+
AYANAMSA, AyaInit,
|
|
87
|
+
PLA_DIAM,
|
|
88
|
+
AS_MAXCH,
|
|
89
|
+
MAXORD,
|
|
90
|
+
SEI_FILE_TEST_ENDIAN,
|
|
91
|
+
SEI_FILE_NMAXPLAN,
|
|
92
|
+
SEI_NEPHFILES,
|
|
93
|
+
SEI_CURR_FPOS,
|
|
94
|
+
J_EARTH, J_MOON, J_SUN, J_SBARY,
|
|
95
|
+
PNOINT2JPL,
|
|
96
|
+
} from './constants';
|
|
97
|
+
|
|
98
|
+
import type {
|
|
99
|
+
SweData, PlanData, FileData, Epsilon, Nut, SavePositions, SidData,
|
|
100
|
+
NodeData,
|
|
101
|
+
} from './types';
|
|
102
|
+
|
|
103
|
+
import {
|
|
104
|
+
createPlanData, createEpsilon, createNut, createSavePositions,
|
|
105
|
+
createDefaultSweData,
|
|
106
|
+
} from './types';
|
|
107
|
+
|
|
108
|
+
import {
|
|
109
|
+
squareSum, sweDegnorm, sweRadnorm, sweDifrad2n,
|
|
110
|
+
swiPrecess, swiNutation, swiEpsiln,
|
|
111
|
+
swiCoortrf, swiCoortrf2,
|
|
112
|
+
swiCartpol, swiPolcart, swiCartpolSp, swiPolcartSp,
|
|
113
|
+
swiCrossProd, swiMod2PI,
|
|
114
|
+
swiBias, swiIcrs2fk5, swiFK4_FK5,
|
|
115
|
+
sweDeltatEx, sweDeltat,
|
|
116
|
+
sweSetTidAcc, sweGetTidAcc,
|
|
117
|
+
sweSidtime0, sweSidtime,
|
|
118
|
+
swiGuessEpheFlag,
|
|
119
|
+
swiLdpPeps,
|
|
120
|
+
swiGenFilename,
|
|
121
|
+
swiDotProdUnit,
|
|
122
|
+
swiEcheb, swiEdcheb,
|
|
123
|
+
swiCrc32,
|
|
124
|
+
} from './swephlib';
|
|
125
|
+
|
|
126
|
+
import { SE1FileReader } from './file-reader';
|
|
127
|
+
|
|
128
|
+
import {
|
|
129
|
+
swiOpenJplFile, swiCloseJplFile, swiPleph, swiGetJplDenum,
|
|
130
|
+
} from './swejpl';
|
|
131
|
+
|
|
132
|
+
import {
|
|
133
|
+
swiMoshplan, swiOscElPlan, swiGetFictName,
|
|
134
|
+
} from './swemplan';
|
|
135
|
+
|
|
136
|
+
import {
|
|
137
|
+
swiMoshmoon, swiMeanNode, swiMeanApog, swiIntpApsides,
|
|
138
|
+
} from './swemmoon';
|
|
139
|
+
|
|
140
|
+
/* ================================================================
|
|
141
|
+
* Internal constants
|
|
142
|
+
* ================================================================ */
|
|
143
|
+
|
|
144
|
+
const IS_PLANET = 0;
|
|
145
|
+
const IS_MOON = 1;
|
|
146
|
+
const IS_ANY_BODY = 2;
|
|
147
|
+
const IS_MAIN_ASTEROID = 3;
|
|
148
|
+
|
|
149
|
+
const SEFLG_COORDSYS = SEFLG_EQUATORIAL | SEFLG_XYZ | SEFLG_RADIANS;
|
|
150
|
+
|
|
151
|
+
const DO_SAVE = true;
|
|
152
|
+
const NO_SAVE = false;
|
|
153
|
+
|
|
154
|
+
/* ================================================================
|
|
155
|
+
* PNOEXT2INT: external planet number → internal planet number
|
|
156
|
+
* ================================================================ */
|
|
157
|
+
const PNOEXT2INT: readonly number[] = [
|
|
158
|
+
SEI_SUN, // 0 = SE_SUN → SEI_SUN (=0=SEI_EARTH)
|
|
159
|
+
SEI_MOON, // 1 = SE_MOON
|
|
160
|
+
SEI_MERCURY, // 2 = SE_MERCURY
|
|
161
|
+
SEI_VENUS, // 3 = SE_VENUS
|
|
162
|
+
SEI_MARS, // 4 = SE_MARS
|
|
163
|
+
SEI_JUPITER, // 5 = SE_JUPITER
|
|
164
|
+
SEI_SATURN, // 6 = SE_SATURN
|
|
165
|
+
SEI_URANUS, // 7 = SE_URANUS
|
|
166
|
+
SEI_NEPTUNE, // 8 = SE_NEPTUNE
|
|
167
|
+
SEI_PLUTO, // 9 = SE_PLUTO
|
|
168
|
+
SEI_MEAN_NODE, // 10 = SE_MEAN_NODE
|
|
169
|
+
SEI_TRUE_NODE, // 11 = SE_TRUE_NODE
|
|
170
|
+
SEI_MEAN_APOG, // 12 = SE_MEAN_APOG
|
|
171
|
+
SEI_OSCU_APOG, // 13 = SE_OSCU_APOG
|
|
172
|
+
SEI_EARTH, // 14 = SE_EARTH
|
|
173
|
+
SEI_CHIRON, // 15 = SE_CHIRON
|
|
174
|
+
SEI_PHOLUS, // 16 = SE_PHOLUS
|
|
175
|
+
SEI_CERES, // 17 = SE_CERES
|
|
176
|
+
SEI_PALLAS, // 18 = SE_PALLAS
|
|
177
|
+
SEI_JUNO, // 19 = SE_JUNO
|
|
178
|
+
SEI_VESTA, // 20 = SE_VESTA
|
|
179
|
+
SEI_INTP_APOG, // 21 = SE_INTP_APOG
|
|
180
|
+
SEI_INTP_PERG, // 22 = SE_INTP_PERG
|
|
181
|
+
];
|
|
182
|
+
|
|
183
|
+
/* ================================================================
|
|
184
|
+
* Ayanamsa names table
|
|
185
|
+
* ================================================================ */
|
|
186
|
+
const AYANAMSA_NAME: readonly string[] = [
|
|
187
|
+
"Fagan/Bradley", /* 0 */
|
|
188
|
+
"Lahiri", /* 1 */
|
|
189
|
+
"De Luce", /* 2 */
|
|
190
|
+
"Raman", /* 3 */
|
|
191
|
+
"Usha/Shashi", /* 4 */
|
|
192
|
+
"Krishnamurti", /* 5 */
|
|
193
|
+
"Djwhal Khul", /* 6 */
|
|
194
|
+
"Yukteshwar", /* 7 */
|
|
195
|
+
"J.N. Bhasin", /* 8 */
|
|
196
|
+
"Babylonian/Kugler 1", /* 9 */
|
|
197
|
+
"Babylonian/Kugler 2", /* 10 */
|
|
198
|
+
"Babylonian/Kugler 3", /* 11 */
|
|
199
|
+
"Babylonian/Huber", /* 12 */
|
|
200
|
+
"Babylonian/Eta Piscium", /* 13 */
|
|
201
|
+
"Babylonian/Aldebaran = 15 Tau", /* 14 */
|
|
202
|
+
"Hipparchos", /* 15 */
|
|
203
|
+
"Sassanian", /* 16 */
|
|
204
|
+
"Galact. Center = 0 Sag", /* 17 */
|
|
205
|
+
"J2000", /* 18 */
|
|
206
|
+
"J1900", /* 19 */
|
|
207
|
+
"B1950", /* 20 */
|
|
208
|
+
"Suryasiddhanta", /* 21 */
|
|
209
|
+
"Suryasiddhanta, mean Sun", /* 22 */
|
|
210
|
+
"Aryabhata", /* 23 */
|
|
211
|
+
"Aryabhata, mean Sun", /* 24 */
|
|
212
|
+
"SS Revati", /* 25 */
|
|
213
|
+
"SS Citra", /* 26 */
|
|
214
|
+
"True Citra", /* 27 */
|
|
215
|
+
"True Revati", /* 28 */
|
|
216
|
+
"True Pushya", /* 29 */
|
|
217
|
+
"Galactic Center (Gil Brand)", /* 30 */
|
|
218
|
+
"Gal. Equator (IAU1958)", /* 31 */
|
|
219
|
+
"Gal. Equator", /* 32 */
|
|
220
|
+
"Gal. Equator mid-Mula", /* 33 */
|
|
221
|
+
"Skydram (Mardyks)", /* 34 */
|
|
222
|
+
"True Mula (Chandra Hari)", /* 35 */
|
|
223
|
+
"Dhruva/Gal.Center/Mula (Wilhelm)", /* 36 */
|
|
224
|
+
"Aryabhata 522", /* 37 */
|
|
225
|
+
"Babylonian/Britton", /* 38 */
|
|
226
|
+
"True Sheoran", /* 39 */
|
|
227
|
+
"Galactic Center (Cochrane)", /* 40 */
|
|
228
|
+
"Galactic Equator (Fiorenza)", /* 41 */
|
|
229
|
+
"Valens (Moon)", /* 42 */
|
|
230
|
+
"Lahiri 1940", /* 43 */
|
|
231
|
+
"Lahiri VP285", /* 44 */
|
|
232
|
+
"Krishnamurti VP291", /* 45 */
|
|
233
|
+
"Lahiri ICRC", /* 46 */
|
|
234
|
+
];
|
|
235
|
+
|
|
236
|
+
/* ================================================================
|
|
237
|
+
* EFF_ARR: effective mass factor for light deflection near sun
|
|
238
|
+
* r = min distance of photon from sun center (fraction of Rsun)
|
|
239
|
+
* m = effective mass factor
|
|
240
|
+
* ================================================================ */
|
|
241
|
+
const EFF_ARR: readonly { r: number; m: number }[] = [
|
|
242
|
+
{r: 1.000, m: 1.000000}, {r: 0.990, m: 0.999979}, {r: 0.980, m: 0.999940},
|
|
243
|
+
{r: 0.970, m: 0.999881}, {r: 0.960, m: 0.999811}, {r: 0.950, m: 0.999724},
|
|
244
|
+
{r: 0.940, m: 0.999622}, {r: 0.930, m: 0.999497}, {r: 0.920, m: 0.999354},
|
|
245
|
+
{r: 0.910, m: 0.999192}, {r: 0.900, m: 0.999000}, {r: 0.890, m: 0.998786},
|
|
246
|
+
{r: 0.880, m: 0.998535}, {r: 0.870, m: 0.998242}, {r: 0.860, m: 0.997919},
|
|
247
|
+
{r: 0.850, m: 0.997571}, {r: 0.840, m: 0.997198}, {r: 0.830, m: 0.996792},
|
|
248
|
+
{r: 0.820, m: 0.996316}, {r: 0.810, m: 0.995791}, {r: 0.800, m: 0.995226},
|
|
249
|
+
{r: 0.790, m: 0.994625}, {r: 0.780, m: 0.993991}, {r: 0.770, m: 0.993326},
|
|
250
|
+
{r: 0.760, m: 0.992598}, {r: 0.750, m: 0.991770}, {r: 0.740, m: 0.990873},
|
|
251
|
+
{r: 0.730, m: 0.989919}, {r: 0.720, m: 0.988912}, {r: 0.710, m: 0.987856},
|
|
252
|
+
{r: 0.700, m: 0.986755}, {r: 0.690, m: 0.985610}, {r: 0.680, m: 0.984398},
|
|
253
|
+
{r: 0.670, m: 0.982986}, {r: 0.660, m: 0.981437}, {r: 0.650, m: 0.979779},
|
|
254
|
+
{r: 0.640, m: 0.978024}, {r: 0.630, m: 0.976182}, {r: 0.620, m: 0.974256},
|
|
255
|
+
{r: 0.610, m: 0.972253}, {r: 0.600, m: 0.970174}, {r: 0.590, m: 0.968024},
|
|
256
|
+
{r: 0.580, m: 0.965594}, {r: 0.570, m: 0.962797}, {r: 0.560, m: 0.959758},
|
|
257
|
+
{r: 0.550, m: 0.956515}, {r: 0.540, m: 0.953088}, {r: 0.530, m: 0.949495},
|
|
258
|
+
{r: 0.520, m: 0.945741}, {r: 0.510, m: 0.941838}, {r: 0.500, m: 0.937790},
|
|
259
|
+
{r: 0.490, m: 0.933563}, {r: 0.480, m: 0.928668}, {r: 0.470, m: 0.923288},
|
|
260
|
+
{r: 0.460, m: 0.917527}, {r: 0.450, m: 0.911432}, {r: 0.440, m: 0.905035},
|
|
261
|
+
{r: 0.430, m: 0.898353}, {r: 0.420, m: 0.891022}, {r: 0.410, m: 0.882940},
|
|
262
|
+
{r: 0.400, m: 0.874312}, {r: 0.390, m: 0.865206}, {r: 0.380, m: 0.855423},
|
|
263
|
+
{r: 0.370, m: 0.844619}, {r: 0.360, m: 0.833074}, {r: 0.350, m: 0.820876},
|
|
264
|
+
{r: 0.340, m: 0.808031}, {r: 0.330, m: 0.793962}, {r: 0.320, m: 0.778931},
|
|
265
|
+
{r: 0.310, m: 0.763021}, {r: 0.300, m: 0.745815}, {r: 0.290, m: 0.727557},
|
|
266
|
+
{r: 0.280, m: 0.708234}, {r: 0.270, m: 0.687583}, {r: 0.260, m: 0.665741},
|
|
267
|
+
{r: 0.250, m: 0.642597}, {r: 0.240, m: 0.618252}, {r: 0.230, m: 0.592586},
|
|
268
|
+
{r: 0.220, m: 0.565747}, {r: 0.210, m: 0.537697}, {r: 0.200, m: 0.508554},
|
|
269
|
+
{r: 0.190, m: 0.478420}, {r: 0.180, m: 0.447322}, {r: 0.170, m: 0.415454},
|
|
270
|
+
{r: 0.160, m: 0.382892}, {r: 0.150, m: 0.349955}, {r: 0.140, m: 0.316691},
|
|
271
|
+
{r: 0.130, m: 0.283565}, {r: 0.120, m: 0.250431}, {r: 0.110, m: 0.218327},
|
|
272
|
+
{r: 0.100, m: 0.186794}, {r: 0.090, m: 0.156287}, {r: 0.080, m: 0.128421},
|
|
273
|
+
{r: 0.070, m: 0.102237}, {r: 0.060, m: 0.077393}, {r: 0.050, m: 0.054833},
|
|
274
|
+
{r: 0.040, m: 0.036361}, {r: 0.030, m: 0.020953}, {r: 0.020, m: 0.009645},
|
|
275
|
+
{r: 0.010, m: 0.002767}, {r: 0.000, m: 0.000000},
|
|
276
|
+
];
|
|
277
|
+
|
|
278
|
+
/* ================================================================
|
|
279
|
+
* Part 1: Initialization, utility, and configuration functions
|
|
280
|
+
* ================================================================ */
|
|
281
|
+
|
|
282
|
+
/** Return library version string */
|
|
283
|
+
export function sweVersion(): string {
|
|
284
|
+
return SE_VERSION;
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
/** Free planet data arrays (clear computed positions) */
|
|
288
|
+
export function freePlanets(swed: SweData): void {
|
|
289
|
+
for (let i = 0; i < SEI_NPLANETS; i++) {
|
|
290
|
+
const pd = swed.pldat[i];
|
|
291
|
+
pd.segp = null;
|
|
292
|
+
pd.refep = null;
|
|
293
|
+
pd.neval = 0;
|
|
294
|
+
pd.tseg0 = 0;
|
|
295
|
+
pd.tseg1 = 0;
|
|
296
|
+
}
|
|
297
|
+
for (let i = 0; i < SEI_NNODE_ETC; i++) {
|
|
298
|
+
const nd = swed.nddat[i];
|
|
299
|
+
nd.segp = null;
|
|
300
|
+
nd.refep = null;
|
|
301
|
+
nd.neval = 0;
|
|
302
|
+
nd.tseg0 = 0;
|
|
303
|
+
nd.tseg1 = 0;
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
/** Initialize swed if not yet done */
|
|
308
|
+
export function swiInitSwedIfStart(swed: SweData): void {
|
|
309
|
+
if (!swed.swedIsInitialised) {
|
|
310
|
+
/* Initialize with default state values */
|
|
311
|
+
swed.ephePathIsSet = false;
|
|
312
|
+
swed.jplFileIsOpen = false;
|
|
313
|
+
swed.lastEpheflag = SEFLG_MOSEPH;
|
|
314
|
+
swed.geoposIsSet = false;
|
|
315
|
+
swed.ayanaIsSet = false;
|
|
316
|
+
swed.swedIsInitialised = true;
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
/** Close but keep topocentric settings */
|
|
321
|
+
function swiCloseKeepTopoEtc(swed: SweData): void {
|
|
322
|
+
freePlanets(swed);
|
|
323
|
+
/* Clear file data */
|
|
324
|
+
for (const fd of swed.fidat) {
|
|
325
|
+
fd.fnam = '';
|
|
326
|
+
fd.fversion = 0;
|
|
327
|
+
fd.reader = null;
|
|
328
|
+
}
|
|
329
|
+
/* Clear computed planet data */
|
|
330
|
+
for (const pd of swed.pldat) {
|
|
331
|
+
pd.teval = 0;
|
|
332
|
+
pd.xflgs = 0;
|
|
333
|
+
pd.iephe = 0;
|
|
334
|
+
pd.x.fill(0);
|
|
335
|
+
pd.xreturn.fill(0);
|
|
336
|
+
}
|
|
337
|
+
for (const nd of swed.nddat) {
|
|
338
|
+
nd.teval = 0;
|
|
339
|
+
nd.xflgs = 0;
|
|
340
|
+
nd.iephe = 0;
|
|
341
|
+
nd.x.fill(0);
|
|
342
|
+
nd.xreturn.fill(0);
|
|
343
|
+
}
|
|
344
|
+
/* Clear saved positions */
|
|
345
|
+
for (const sp of swed.savedat) {
|
|
346
|
+
sp.tsave = 0;
|
|
347
|
+
sp.iflgsave = 0;
|
|
348
|
+
sp.ipl = 0;
|
|
349
|
+
sp.xsaves.fill(0);
|
|
350
|
+
}
|
|
351
|
+
/* Clear epsilon/nutation cache */
|
|
352
|
+
swed.oec.teps = 0;
|
|
353
|
+
swed.oec2000.teps = 0;
|
|
354
|
+
swed.nut.tnut = 0;
|
|
355
|
+
swed.nut2000.tnut = 0;
|
|
356
|
+
swed.nutv.tnut = 0;
|
|
357
|
+
swed.initDtDone = false;
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
/** Full close: reset everything */
|
|
361
|
+
export function sweClose(swed: SweData): void {
|
|
362
|
+
swiCloseKeepTopoEtc(swed);
|
|
363
|
+
swiCloseJplFile(swed);
|
|
364
|
+
swed.ephePathIsSet = false;
|
|
365
|
+
swed.jplFileIsOpen = false;
|
|
366
|
+
swed.geoposIsSet = false;
|
|
367
|
+
swed.ayanaIsSet = false;
|
|
368
|
+
swed.sidd.sidMode = 0;
|
|
369
|
+
swed.sidd.ayanT0 = 0;
|
|
370
|
+
swed.sidd.t0 = 0;
|
|
371
|
+
swed.sidd.t0IsUT = false;
|
|
372
|
+
swed.topd.geolon = 0;
|
|
373
|
+
swed.topd.geolat = 0;
|
|
374
|
+
swed.topd.geoalt = 0;
|
|
375
|
+
swed.topd.teval = 0;
|
|
376
|
+
swed.topd.tjdUt = 0;
|
|
377
|
+
swed.topd.xobs.fill(0);
|
|
378
|
+
swed.isTidAccManual = false;
|
|
379
|
+
swed.tidAcc = 0;
|
|
380
|
+
swed.astroModels.fill(0);
|
|
381
|
+
swed.dpsi = null;
|
|
382
|
+
swed.deps = null;
|
|
383
|
+
swed.swedIsInitialised = false;
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
/** Set ephemeris file path */
|
|
387
|
+
export function sweSetEphePath(swed: SweData, path: string): void {
|
|
388
|
+
swiInitSwedIfStart(swed);
|
|
389
|
+
swed.ephepath = path || '.';
|
|
390
|
+
swed.ephePathIsSet = true;
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
/** Set JPL file name */
|
|
394
|
+
export function sweSetJplFile(swed: SweData, fname: string): void {
|
|
395
|
+
swiInitSwedIfStart(swed);
|
|
396
|
+
swed.jplfnam = fname;
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
/* ================================================================
|
|
400
|
+
* Obliquity and Nutation helpers
|
|
401
|
+
* ================================================================ */
|
|
402
|
+
|
|
403
|
+
/** Compute obliquity of ecliptic */
|
|
404
|
+
export function calcEpsilon(tjd: number, iflag: number, e: Epsilon, swed: SweData): void {
|
|
405
|
+
e.teps = tjd;
|
|
406
|
+
e.eps = swiEpsiln(tjd, iflag, swed);
|
|
407
|
+
e.seps = Math.sin(e.eps);
|
|
408
|
+
e.ceps = Math.cos(e.eps);
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
/** Ensure oec and oec2000 are computed for the given tjd */
|
|
412
|
+
export function swiCheckEcliptic(tjd: number, iflag: number, swed: SweData): void {
|
|
413
|
+
if (swed.oec2000.teps !== J2000) {
|
|
414
|
+
calcEpsilon(J2000, iflag, swed.oec2000, swed);
|
|
415
|
+
}
|
|
416
|
+
if (tjd === J2000) {
|
|
417
|
+
swed.oec.teps = swed.oec2000.teps;
|
|
418
|
+
swed.oec.eps = swed.oec2000.eps;
|
|
419
|
+
swed.oec.seps = swed.oec2000.seps;
|
|
420
|
+
swed.oec.ceps = swed.oec2000.ceps;
|
|
421
|
+
} else if (swed.oec.teps !== tjd || tjd === 0) {
|
|
422
|
+
calcEpsilon(tjd, iflag, swed.oec, swed);
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
/** Ensure nut and nutv are computed for the given tjd */
|
|
427
|
+
export function swiCheckNutation(tjd: number, iflag: number, swed: SweData): void {
|
|
428
|
+
if (swed.nut.tnut !== tjd || tjd === 0) {
|
|
429
|
+
const nutlo: [number, number] = [0, 0];
|
|
430
|
+
swiNutation(tjd, iflag, nutlo, swed);
|
|
431
|
+
swed.nut.tnut = tjd;
|
|
432
|
+
swed.nut.nutlo[0] = nutlo[0];
|
|
433
|
+
swed.nut.nutlo[1] = nutlo[1];
|
|
434
|
+
swed.nut.snut = Math.sin(nutlo[1]);
|
|
435
|
+
swed.nut.cnut = Math.cos(nutlo[1]);
|
|
436
|
+
nutMatrix(swed.nut, swed.oec);
|
|
437
|
+
}
|
|
438
|
+
if (swed.nutv.tnut !== tjd || tjd === 0) {
|
|
439
|
+
/* nutation at slightly different time for speed computation */
|
|
440
|
+
const nutlo: [number, number] = [0, 0];
|
|
441
|
+
const tjdv = tjd + NUT_SPEED_INTV;
|
|
442
|
+
swiNutation(tjdv, iflag, nutlo, swed);
|
|
443
|
+
swed.nutv.tnut = tjdv;
|
|
444
|
+
swed.nutv.nutlo[0] = nutlo[0];
|
|
445
|
+
swed.nutv.nutlo[1] = nutlo[1];
|
|
446
|
+
swed.nutv.snut = Math.sin(nutlo[1]);
|
|
447
|
+
swed.nutv.cnut = Math.cos(nutlo[1]);
|
|
448
|
+
nutMatrix(swed.nutv, swed.oec);
|
|
449
|
+
}
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
/** Compute 3x3 nutation matrix */
|
|
453
|
+
export function nutMatrix(nu: Nut, oe: Epsilon): void {
|
|
454
|
+
const psi = nu.nutlo[0];
|
|
455
|
+
const eps = oe.eps;
|
|
456
|
+
const epsMod = eps + nu.nutlo[1];
|
|
457
|
+
const sinpsi = Math.sin(psi);
|
|
458
|
+
const cospsi = Math.cos(psi);
|
|
459
|
+
const sineps0 = oe.seps;
|
|
460
|
+
const coseps0 = oe.ceps;
|
|
461
|
+
const sinepsM = Math.sin(epsMod);
|
|
462
|
+
const cosepsM = Math.cos(epsMod);
|
|
463
|
+
nu.matrix[0][0] = cospsi;
|
|
464
|
+
nu.matrix[0][1] = sinpsi * coseps0;
|
|
465
|
+
nu.matrix[0][2] = sinpsi * sineps0;
|
|
466
|
+
nu.matrix[1][0] = -sinpsi * cosepsM;
|
|
467
|
+
nu.matrix[1][1] = cospsi * cosepsM * coseps0 + sinepsM * sineps0;
|
|
468
|
+
nu.matrix[1][2] = cospsi * cosepsM * sineps0 - sinepsM * coseps0;
|
|
469
|
+
nu.matrix[2][0] = -sinpsi * sinepsM;
|
|
470
|
+
nu.matrix[2][1] = cospsi * sinepsM * coseps0 - cosepsM * sineps0;
|
|
471
|
+
nu.matrix[2][2] = cospsi * sinepsM * sineps0 + cosepsM * coseps0;
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
/* ================================================================
|
|
475
|
+
* plausIflag: sanitize iflag
|
|
476
|
+
* ================================================================ */
|
|
477
|
+
function plausIflag(iflag: number, ipl: number, tjd: number, swed: SweData): number {
|
|
478
|
+
let epheflag = iflag & SEFLG_EPHMASK;
|
|
479
|
+
if (epheflag === 0) {
|
|
480
|
+
epheflag = SEFLG_DEFAULTEPH;
|
|
481
|
+
iflag |= SEFLG_DEFAULTEPH;
|
|
482
|
+
}
|
|
483
|
+
/* JPL: allow if file is open, otherwise fall back to SWIEPH */
|
|
484
|
+
if (epheflag & SEFLG_JPLEPH) {
|
|
485
|
+
if (!swed.jplFileIsOpen) {
|
|
486
|
+
iflag &= ~SEFLG_JPLEPH;
|
|
487
|
+
iflag |= SEFLG_SWIEPH;
|
|
488
|
+
epheflag = SEFLG_SWIEPH;
|
|
489
|
+
}
|
|
490
|
+
}
|
|
491
|
+
/* SWIEPH is allowed — if files aren't loaded, individual functions fall back to MOSEPH */
|
|
492
|
+
/* Speed flag: SPEED3 implies SPEED */
|
|
493
|
+
if (iflag & SEFLG_SPEED3) {
|
|
494
|
+
iflag |= SEFLG_SPEED;
|
|
495
|
+
}
|
|
496
|
+
/* SPEED is always needed internally */
|
|
497
|
+
iflag |= SEFLG_SPEED;
|
|
498
|
+
/* If heliocentric and BARYCTR both set, clear BARYCTR */
|
|
499
|
+
if ((iflag & SEFLG_HELCTR) && (iflag & SEFLG_BARYCTR)) {
|
|
500
|
+
iflag &= ~SEFLG_BARYCTR;
|
|
501
|
+
}
|
|
502
|
+
/* Heliocentric: clear topocentric */
|
|
503
|
+
if (iflag & SEFLG_HELCTR) {
|
|
504
|
+
iflag &= ~SEFLG_TOPOCTR;
|
|
505
|
+
}
|
|
506
|
+
/* Sun/barycentre combination: clear barycentric for Sun */
|
|
507
|
+
if (ipl === SE_SUN && (iflag & SEFLG_BARYCTR)) {
|
|
508
|
+
/* nothing special needed */
|
|
509
|
+
}
|
|
510
|
+
/* If sidereal, add NONUT */
|
|
511
|
+
if (iflag & SEFLG_SIDEREAL) {
|
|
512
|
+
/* do NOT add NONUT for sidereal mode; nutation handled by ayanamsa */
|
|
513
|
+
}
|
|
514
|
+
return iflag;
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
/* ================================================================
|
|
518
|
+
* Planet name
|
|
519
|
+
* ================================================================ */
|
|
520
|
+
export function sweGetPlanetName(ipl: number, swed: SweData): string {
|
|
521
|
+
if (ipl !== 0 && ipl === swed.iSavedPlanetName) {
|
|
522
|
+
return swed.savedPlanetName;
|
|
523
|
+
}
|
|
524
|
+
let s: string;
|
|
525
|
+
if (ipl >= 0 && ipl < SE_NPLANETS) {
|
|
526
|
+
s = SE_PLANET_NAMES[ipl];
|
|
527
|
+
} else if (ipl >= SE_FICT_OFFSET && ipl <= SE_FICT_MAX) {
|
|
528
|
+
s = swiGetFictName(ipl - SE_FICT_OFFSET);
|
|
529
|
+
} else if (ipl >= SE_AST_OFFSET) {
|
|
530
|
+
const iast = ipl - SE_AST_OFFSET;
|
|
531
|
+
s = `asteroid ${iast}`;
|
|
532
|
+
} else {
|
|
533
|
+
s = `body ${ipl}`;
|
|
534
|
+
}
|
|
535
|
+
swed.iSavedPlanetName = ipl;
|
|
536
|
+
swed.savedPlanetName = s;
|
|
537
|
+
return s;
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
export function sweGetAyanamsaName(isidm: number): string {
|
|
541
|
+
if (isidm < SE_NSIDM_PREDEF) {
|
|
542
|
+
return AYANAMSA_NAME[isidm];
|
|
543
|
+
}
|
|
544
|
+
return "unknown";
|
|
545
|
+
}
|
|
546
|
+
|
|
547
|
+
/* ================================================================
|
|
548
|
+
* Topocentric setup
|
|
549
|
+
* ================================================================ */
|
|
550
|
+
export function sweSetTopo(swed: SweData, geolon: number, geolat: number, geoalt: number): void {
|
|
551
|
+
swiInitSwedIfStart(swed);
|
|
552
|
+
if (swed.topd.geolon !== geolon || swed.topd.geolat !== geolat || swed.topd.geoalt !== geoalt) {
|
|
553
|
+
swed.topd.geolon = geolon;
|
|
554
|
+
swed.topd.geolat = geolat;
|
|
555
|
+
swed.topd.geoalt = geoalt;
|
|
556
|
+
swed.topd.teval = 0;
|
|
557
|
+
swed.topd.tjdUt = 0;
|
|
558
|
+
swed.topd.xobs.fill(0);
|
|
559
|
+
swed.geoposIsSet = true;
|
|
560
|
+
/* invalidate saved planet positions */
|
|
561
|
+
for (const sp of swed.savedat) {
|
|
562
|
+
sp.tsave = 0;
|
|
563
|
+
}
|
|
564
|
+
}
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
/* ================================================================
|
|
568
|
+
* swiForceAppPosEtc: invalidate saved positions
|
|
569
|
+
* ================================================================ */
|
|
570
|
+
export function swiForceAppPosEtc(swed: SweData): void {
|
|
571
|
+
for (const sp of swed.savedat) {
|
|
572
|
+
sp.tsave = 0;
|
|
573
|
+
}
|
|
574
|
+
}
|
|
575
|
+
|
|
576
|
+
/* ================================================================
|
|
577
|
+
* Time equation helpers
|
|
578
|
+
* ================================================================ */
|
|
579
|
+
export function sweTimeEqu(swed: SweData, tjdUt: number): { retc: number; e: number; serr: string } {
|
|
580
|
+
const dt = sweDeltatEx(tjdUt, -1, swed);
|
|
581
|
+
const tjdEt = tjdUt + dt;
|
|
582
|
+
const res = sweCalc(swed, tjdEt, SE_SUN, SEFLG_EQUATORIAL);
|
|
583
|
+
if (res.flags < 0) {
|
|
584
|
+
return { retc: ERR, e: 0, serr: res.serr };
|
|
585
|
+
}
|
|
586
|
+
const ra = res.xx[0]; /* right ascension of sun in degrees */
|
|
587
|
+
const sidt = sweSidtime(swed, tjdUt); /* sidereal time in hours */
|
|
588
|
+
let e = sidt * 15 - ra; /* equation of time */
|
|
589
|
+
/* normalize to [-180, 180) degrees */
|
|
590
|
+
e = sweDegnorm(e + 180) - 180;
|
|
591
|
+
/* convert to fraction of day */
|
|
592
|
+
e /= 360.0;
|
|
593
|
+
return { retc: OK, e, serr: '' };
|
|
594
|
+
}
|
|
595
|
+
|
|
596
|
+
export function sweLmtToLat(swed: SweData, tjdLmt: number, geolon: number): { retc: number; tjdLat: number; serr: string } {
|
|
597
|
+
const diffLon = geolon / 360.0;
|
|
598
|
+
let tjdLat = tjdLmt - diffLon;
|
|
599
|
+
const eqRes = sweTimeEqu(swed, tjdLat);
|
|
600
|
+
if (eqRes.retc === ERR) return { retc: ERR, tjdLat: 0, serr: eqRes.serr };
|
|
601
|
+
tjdLat = tjdLmt - diffLon - eqRes.e;
|
|
602
|
+
const eqRes2 = sweTimeEqu(swed, tjdLat);
|
|
603
|
+
if (eqRes2.retc === ERR) return { retc: ERR, tjdLat: 0, serr: eqRes2.serr };
|
|
604
|
+
tjdLat = tjdLmt - diffLon - eqRes2.e;
|
|
605
|
+
const eqRes3 = sweTimeEqu(swed, tjdLat);
|
|
606
|
+
if (eqRes3.retc === ERR) return { retc: ERR, tjdLat: 0, serr: eqRes3.serr };
|
|
607
|
+
tjdLat = tjdLmt - diffLon - eqRes3.e;
|
|
608
|
+
return { retc: OK, tjdLat, serr: '' };
|
|
609
|
+
}
|
|
610
|
+
|
|
611
|
+
export function sweLatToLmt(swed: SweData, tjdLat: number, geolon: number): { retc: number; tjdLmt: number; serr: string } {
|
|
612
|
+
const diffLon = geolon / 360.0;
|
|
613
|
+
let tjdLmt = tjdLat + diffLon;
|
|
614
|
+
const eqRes = sweTimeEqu(swed, tjdLat);
|
|
615
|
+
if (eqRes.retc === ERR) return { retc: ERR, tjdLmt: 0, serr: eqRes.serr };
|
|
616
|
+
tjdLmt = tjdLat + diffLon + eqRes.e;
|
|
617
|
+
const eqRes2 = sweTimeEqu(swed, tjdLmt - diffLon);
|
|
618
|
+
if (eqRes2.retc === ERR) return { retc: ERR, tjdLmt: 0, serr: eqRes2.serr };
|
|
619
|
+
tjdLmt = tjdLat + diffLon + eqRes2.e;
|
|
620
|
+
const eqRes3 = sweTimeEqu(swed, tjdLmt - diffLon);
|
|
621
|
+
if (eqRes3.retc === ERR) return { retc: ERR, tjdLmt: 0, serr: eqRes3.serr };
|
|
622
|
+
tjdLmt = tjdLat + diffLon + eqRes3.e;
|
|
623
|
+
return { retc: OK, tjdLmt, serr: '' };
|
|
624
|
+
}
|
|
625
|
+
|
|
626
|
+
/* ================================================================
|
|
627
|
+
* Part 2: Precession speed, nutation, aberration, deflection,
|
|
628
|
+
* embofs, denormalize, calcSpeed, meff, observer, denum
|
|
629
|
+
* ================================================================ */
|
|
630
|
+
|
|
631
|
+
/** Apply precession speed correction */
|
|
632
|
+
export function swiPrecessSpeed(x: Float64Array | number[], t: number, iflag: number, direction: number, swed: SweData): void {
|
|
633
|
+
/* compute precessed position at t + dt */
|
|
634
|
+
const dt = PLAN_SPEED_INTV;
|
|
635
|
+
const xx = [x[0], x[1], x[2]];
|
|
636
|
+
const x2 = [x[0] - x[3] * dt, x[1] - x[4] * dt, x[2] - x[5] * dt];
|
|
637
|
+
swiPrecess(xx, t, iflag, direction, swed);
|
|
638
|
+
swiPrecess(x2, t - dt, iflag, direction, swed);
|
|
639
|
+
for (let i = 0; i <= 2; i++) {
|
|
640
|
+
x[i] = xx[i];
|
|
641
|
+
x[i + 3] = (xx[i] - x2[i]) / dt;
|
|
642
|
+
}
|
|
643
|
+
}
|
|
644
|
+
|
|
645
|
+
/** Apply nutation to position + speed */
|
|
646
|
+
export function swiNutate(x: Float64Array | number[], iflag: number, backward: boolean, swed: SweData): void {
|
|
647
|
+
const mat = swed.nut.matrix;
|
|
648
|
+
const matv = swed.nutv.matrix;
|
|
649
|
+
const xx = [0, 0, 0, 0, 0, 0];
|
|
650
|
+
if (!backward) {
|
|
651
|
+
/* true equator → mean equator */
|
|
652
|
+
for (let i = 0; i <= 2; i++) {
|
|
653
|
+
xx[i] = mat[i][0] * x[0] + mat[i][1] * x[1] + mat[i][2] * x[2];
|
|
654
|
+
}
|
|
655
|
+
/* speed: apply nutv matrix to speed part */
|
|
656
|
+
for (let i = 0; i <= 2; i++) {
|
|
657
|
+
xx[i + 3] = mat[i][0] * x[3] + mat[i][1] * x[4] + mat[i][2] * x[5];
|
|
658
|
+
}
|
|
659
|
+
/* nutation speed correction */
|
|
660
|
+
if (iflag & SEFLG_SPEED) {
|
|
661
|
+
for (let i = 0; i <= 2; i++) {
|
|
662
|
+
xx[i + 3] += (matv[i][0] - mat[i][0]) * x[0] / NUT_SPEED_INTV
|
|
663
|
+
+ (matv[i][1] - mat[i][1]) * x[1] / NUT_SPEED_INTV
|
|
664
|
+
+ (matv[i][2] - mat[i][2]) * x[2] / NUT_SPEED_INTV;
|
|
665
|
+
}
|
|
666
|
+
}
|
|
667
|
+
} else {
|
|
668
|
+
/* mean equator → true equator (transpose) */
|
|
669
|
+
for (let i = 0; i <= 2; i++) {
|
|
670
|
+
xx[i] = mat[0][i] * x[0] + mat[1][i] * x[1] + mat[2][i] * x[2];
|
|
671
|
+
}
|
|
672
|
+
for (let i = 0; i <= 2; i++) {
|
|
673
|
+
xx[i + 3] = mat[0][i] * x[3] + mat[1][i] * x[4] + mat[2][i] * x[5];
|
|
674
|
+
}
|
|
675
|
+
if (iflag & SEFLG_SPEED) {
|
|
676
|
+
for (let i = 0; i <= 2; i++) {
|
|
677
|
+
xx[i + 3] += (matv[0][i] - mat[0][i]) * x[0] / NUT_SPEED_INTV
|
|
678
|
+
+ (matv[1][i] - mat[1][i]) * x[1] / NUT_SPEED_INTV
|
|
679
|
+
+ (matv[2][i] - mat[2][i]) * x[2] / NUT_SPEED_INTV;
|
|
680
|
+
}
|
|
681
|
+
}
|
|
682
|
+
}
|
|
683
|
+
for (let i = 0; i <= 5; i++) x[i] = xx[i];
|
|
684
|
+
}
|
|
685
|
+
|
|
686
|
+
/** Aberration of light (private, relativistic formula from Meeus) */
|
|
687
|
+
function aberrLight(xx: Float64Array | number[], xe: Float64Array | number[]): void {
|
|
688
|
+
const u = [xx[0], xx[1], xx[2]];
|
|
689
|
+
const ru = Math.sqrt(squareSum(u));
|
|
690
|
+
if (ru === 0) return;
|
|
691
|
+
/* Earth velocity in AU/d → fraction of speed of light */
|
|
692
|
+
const v = [
|
|
693
|
+
xe[3] / 24.0 / 3600.0 / CLIGHT * AUNIT,
|
|
694
|
+
xe[4] / 24.0 / 3600.0 / CLIGHT * AUNIT,
|
|
695
|
+
xe[5] / 24.0 / 3600.0 / CLIGHT * AUNIT,
|
|
696
|
+
];
|
|
697
|
+
const v2 = v[0] * v[0] + v[1] * v[1] + v[2] * v[2];
|
|
698
|
+
const b_1 = Math.sqrt(1 - v2);
|
|
699
|
+
const f1 = (u[0] * v[0] + u[1] * v[1] + u[2] * v[2]) / ru;
|
|
700
|
+
const f2 = 1.0 + f1 / (1.0 + b_1);
|
|
701
|
+
for (let i = 0; i <= 2; i++) {
|
|
702
|
+
xx[i] = (b_1 * xx[i] + f2 * ru * v[i]) / (1.0 + f1);
|
|
703
|
+
}
|
|
704
|
+
}
|
|
705
|
+
|
|
706
|
+
/** Aberration of light, external version with speed correction */
|
|
707
|
+
export function swiAberrLightEx(
|
|
708
|
+
xx: Float64Array | number[], xe: Float64Array | number[],
|
|
709
|
+
xedt: Float64Array | number[], dt: number, iflag: number,
|
|
710
|
+
): void {
|
|
711
|
+
const xxs = [xx[0], xx[1], xx[2], xx[3], xx[4], xx[5]];
|
|
712
|
+
aberrLight(xx, xe);
|
|
713
|
+
if (iflag & SEFLG_SPEED) {
|
|
714
|
+
const xx2 = [xxs[0] - dt * xxs[3], xxs[1] - dt * xxs[4], xxs[2] - dt * xxs[5]];
|
|
715
|
+
aberrLight(xx2, xedt);
|
|
716
|
+
for (let i = 0; i <= 2; i++) {
|
|
717
|
+
xx[i + 3] = (xx[i] - xx2[i]) / dt;
|
|
718
|
+
}
|
|
719
|
+
}
|
|
720
|
+
}
|
|
721
|
+
|
|
722
|
+
/** Aberration of light with speed correction using finite differences */
|
|
723
|
+
export function swiAberrLight(xx: Float64Array | number[], xe: Float64Array | number[], iflag: number): void {
|
|
724
|
+
const xxs = [xx[0], xx[1], xx[2], xx[3], xx[4], xx[5]];
|
|
725
|
+
const u = [xx[0], xx[1], xx[2]];
|
|
726
|
+
const ru = Math.sqrt(squareSum(u));
|
|
727
|
+
if (ru === 0) return;
|
|
728
|
+
const v = [
|
|
729
|
+
xe[3] / 24.0 / 3600.0 / CLIGHT * AUNIT,
|
|
730
|
+
xe[4] / 24.0 / 3600.0 / CLIGHT * AUNIT,
|
|
731
|
+
xe[5] / 24.0 / 3600.0 / CLIGHT * AUNIT,
|
|
732
|
+
];
|
|
733
|
+
const v2 = v[0] * v[0] + v[1] * v[1] + v[2] * v[2];
|
|
734
|
+
const b_1 = Math.sqrt(1 - v2);
|
|
735
|
+
let f1 = (u[0] * v[0] + u[1] * v[1] + u[2] * v[2]) / ru;
|
|
736
|
+
let f2 = 1.0 + f1 / (1.0 + b_1);
|
|
737
|
+
for (let i = 0; i <= 2; i++) {
|
|
738
|
+
xx[i] = (b_1 * xx[i] + f2 * ru * v[i]) / (1.0 + f1);
|
|
739
|
+
}
|
|
740
|
+
if (iflag & SEFLG_SPEED) {
|
|
741
|
+
const intv = PLAN_SPEED_INTV;
|
|
742
|
+
const u2 = [xxs[0] - intv * xxs[3], xxs[1] - intv * xxs[4], xxs[2] - intv * xxs[5]];
|
|
743
|
+
const ru2 = Math.sqrt(u2[0] * u2[0] + u2[1] * u2[1] + u2[2] * u2[2]);
|
|
744
|
+
f1 = (u2[0] * v[0] + u2[1] * v[1] + u2[2] * v[2]) / ru2;
|
|
745
|
+
f2 = 1.0 + f1 / (1.0 + b_1);
|
|
746
|
+
const xx2 = [0, 0, 0];
|
|
747
|
+
for (let i = 0; i <= 2; i++) {
|
|
748
|
+
xx2[i] = (b_1 * u2[i] + f2 * ru2 * v[i]) / (1.0 + f1);
|
|
749
|
+
}
|
|
750
|
+
for (let i = 0; i <= 2; i++) {
|
|
751
|
+
const dx1 = xx[i] - xxs[i];
|
|
752
|
+
const dx2 = xx2[i] - u2[i];
|
|
753
|
+
xx[i + 3] += (dx1 - dx2) / intv;
|
|
754
|
+
}
|
|
755
|
+
}
|
|
756
|
+
}
|
|
757
|
+
|
|
758
|
+
/** Gravitational deflection of light by Sun */
|
|
759
|
+
export function swiDeflectLight(
|
|
760
|
+
xx: Float64Array | number[],
|
|
761
|
+
dt: number,
|
|
762
|
+
iflag: number,
|
|
763
|
+
swed: SweData,
|
|
764
|
+
): void {
|
|
765
|
+
const pedp = swed.pldat[SEI_EARTH];
|
|
766
|
+
const psdp = swed.pldat[SEI_SUNBARY];
|
|
767
|
+
const iephe = pedp.iephe;
|
|
768
|
+
const xearth = [pedp.x[0], pedp.x[1], pedp.x[2], pedp.x[3], pedp.x[4], pedp.x[5]];
|
|
769
|
+
if (iflag & SEFLG_TOPOCTR) {
|
|
770
|
+
for (let i = 0; i <= 5; i++) xearth[i] += swed.topd.xobs[i];
|
|
771
|
+
}
|
|
772
|
+
/* U = planetgeo */
|
|
773
|
+
const u = [xx[0], xx[1], xx[2]];
|
|
774
|
+
/* Eh = earthhel (for Moshier: earth position IS heliocentric) */
|
|
775
|
+
const e = [0, 0, 0];
|
|
776
|
+
if (iephe === SEFLG_JPLEPH || iephe === SEFLG_SWIEPH) {
|
|
777
|
+
for (let i = 0; i <= 2; i++) e[i] = xearth[i] - psdp.x[i];
|
|
778
|
+
} else {
|
|
779
|
+
for (let i = 0; i <= 2; i++) e[i] = xearth[i];
|
|
780
|
+
}
|
|
781
|
+
/* Q = planethel = xx + earth - sun(t-tau) */
|
|
782
|
+
const xsun = [0, 0, 0, 0, 0, 0];
|
|
783
|
+
if (iephe === SEFLG_JPLEPH || iephe === SEFLG_SWIEPH) {
|
|
784
|
+
for (let i = 0; i <= 2; i++) xsun[i] = psdp.x[i] - dt * psdp.x[i + 3];
|
|
785
|
+
for (let i = 3; i <= 5; i++) xsun[i] = psdp.x[i];
|
|
786
|
+
} else {
|
|
787
|
+
for (let i = 0; i <= 5; i++) xsun[i] = psdp.x[i];
|
|
788
|
+
}
|
|
789
|
+
const q = [
|
|
790
|
+
xx[0] + xearth[0] - xsun[0],
|
|
791
|
+
xx[1] + xearth[1] - xsun[1],
|
|
792
|
+
xx[2] + xearth[2] - xsun[2],
|
|
793
|
+
];
|
|
794
|
+
let ru = Math.sqrt(u[0] * u[0] + u[1] * u[1] + u[2] * u[2]);
|
|
795
|
+
let rq = Math.sqrt(q[0] * q[0] + q[1] * q[1] + q[2] * q[2]);
|
|
796
|
+
let re = Math.sqrt(e[0] * e[0] + e[1] * e[1] + e[2] * e[2]);
|
|
797
|
+
if (ru === 0 || rq === 0 || re === 0) return;
|
|
798
|
+
for (let i = 0; i <= 2; i++) { u[i] /= ru; q[i] /= rq; e[i] /= re; }
|
|
799
|
+
let uq = u[0] * q[0] + u[1] * q[1] + u[2] * q[2];
|
|
800
|
+
let ue = u[0] * e[0] + u[1] * e[1] + u[2] * e[2];
|
|
801
|
+
let qe = q[0] * e[0] + q[1] * e[1] + q[2] * e[2];
|
|
802
|
+
/* meff: effective mass factor when body passes through solar disc */
|
|
803
|
+
let sina = Math.sqrt(1 - ue * ue);
|
|
804
|
+
let sinSunr = SUN_RADIUS / re;
|
|
805
|
+
let meffFact = (sina < sinSunr) ? meff(sina / sinSunr) : 1;
|
|
806
|
+
let g1 = 2.0 * HELGRAVCONST * meffFact / CLIGHT / CLIGHT / AUNIT / re;
|
|
807
|
+
let g2 = 1.0 + qe;
|
|
808
|
+
/* deflected position */
|
|
809
|
+
const xx2 = [0, 0, 0];
|
|
810
|
+
for (let i = 0; i <= 2; i++) {
|
|
811
|
+
xx2[i] = ru * (u[i] + g1 / g2 * (uq * e[i] - ue * q[i]));
|
|
812
|
+
}
|
|
813
|
+
if (iflag & SEFLG_SPEED) {
|
|
814
|
+
const dtsp = -DEFL_SPEED_INTV;
|
|
815
|
+
const u2 = [xx[0] - dtsp * xx[3], xx[1] - dtsp * xx[4], xx[2] - dtsp * xx[5]];
|
|
816
|
+
const e2 = [0, 0, 0];
|
|
817
|
+
if (iephe === SEFLG_JPLEPH || iephe === SEFLG_SWIEPH) {
|
|
818
|
+
for (let i = 0; i <= 2; i++)
|
|
819
|
+
e2[i] = xearth[i] - psdp.x[i] - dtsp * (xearth[i + 3] - psdp.x[i + 3]);
|
|
820
|
+
} else {
|
|
821
|
+
for (let i = 0; i <= 2; i++) e2[i] = xearth[i] - dtsp * xearth[i + 3];
|
|
822
|
+
}
|
|
823
|
+
const q2 = [
|
|
824
|
+
u2[0] + xearth[0] - xsun[0] - dtsp * (xearth[3] - xsun[3]),
|
|
825
|
+
u2[1] + xearth[1] - xsun[1] - dtsp * (xearth[4] - xsun[4]),
|
|
826
|
+
u2[2] + xearth[2] - xsun[2] - dtsp * (xearth[5] - xsun[5]),
|
|
827
|
+
];
|
|
828
|
+
ru = Math.sqrt(u2[0] * u2[0] + u2[1] * u2[1] + u2[2] * u2[2]);
|
|
829
|
+
rq = Math.sqrt(q2[0] * q2[0] + q2[1] * q2[1] + q2[2] * q2[2]);
|
|
830
|
+
re = Math.sqrt(e2[0] * e2[0] + e2[1] * e2[1] + e2[2] * e2[2]);
|
|
831
|
+
if (ru > 0 && rq > 0 && re > 0) {
|
|
832
|
+
for (let i = 0; i <= 2; i++) { u2[i] /= ru; q2[i] /= rq; e2[i] /= re; }
|
|
833
|
+
uq = u2[0] * q2[0] + u2[1] * q2[1] + u2[2] * q2[2];
|
|
834
|
+
ue = u2[0] * e2[0] + u2[1] * e2[1] + u2[2] * e2[2];
|
|
835
|
+
qe = q2[0] * e2[0] + q2[1] * e2[1] + q2[2] * e2[2];
|
|
836
|
+
sina = Math.sqrt(1 - ue * ue);
|
|
837
|
+
sinSunr = SUN_RADIUS / re;
|
|
838
|
+
meffFact = (sina < sinSunr) ? meff(sina / sinSunr) : 1;
|
|
839
|
+
g1 = 2.0 * HELGRAVCONST * meffFact / CLIGHT / CLIGHT / AUNIT / re;
|
|
840
|
+
g2 = 1.0 + qe;
|
|
841
|
+
const xx3 = [0, 0, 0];
|
|
842
|
+
for (let i = 0; i <= 2; i++) {
|
|
843
|
+
xx3[i] = ru * (u2[i] + g1 / g2 * (uq * e2[i] - ue * q2[i]));
|
|
844
|
+
}
|
|
845
|
+
for (let i = 0; i <= 2; i++) {
|
|
846
|
+
const dx1 = xx2[i] - xx[i];
|
|
847
|
+
const dx2 = xx3[i] - u2[i] * ru;
|
|
848
|
+
xx[i + 3] += (dx1 - dx2) / dtsp;
|
|
849
|
+
}
|
|
850
|
+
}
|
|
851
|
+
}
|
|
852
|
+
/* set deflected position */
|
|
853
|
+
for (let i = 0; i <= 2; i++) xx[i] = xx2[i];
|
|
854
|
+
}
|
|
855
|
+
|
|
856
|
+
/** meff: effective mass factor for light deflection (interpolation from table) */
|
|
857
|
+
function meff(r: number): number {
|
|
858
|
+
/* r = fraction of sun radius at which photon passes */
|
|
859
|
+
if (r <= 0) return 0.0;
|
|
860
|
+
if (r >= 1) return 1.0;
|
|
861
|
+
let i: number;
|
|
862
|
+
for (i = 0; EFF_ARR[i].r > r; i++) { /* find bracket */ }
|
|
863
|
+
const f = (r - EFF_ARR[i - 1].r) / (EFF_ARR[i].r - EFF_ARR[i - 1].r);
|
|
864
|
+
return EFF_ARR[i - 1].m + f * (EFF_ARR[i].m - EFF_ARR[i - 1].m);
|
|
865
|
+
}
|
|
866
|
+
|
|
867
|
+
/** Earth-Moon barycentre → Earth offset */
|
|
868
|
+
function embofs(xemb: Float64Array | number[], xmoon: Float64Array | number[]): void {
|
|
869
|
+
for (let i = 0; i <= 5; i++) {
|
|
870
|
+
xemb[i] -= xmoon[i] / (EARTH_MOON_MRAT + 1.0);
|
|
871
|
+
}
|
|
872
|
+
}
|
|
873
|
+
|
|
874
|
+
/** Denormalize positions from barycentric/heliocentric to geocentric */
|
|
875
|
+
function denormalizePositions(
|
|
876
|
+
x: Float64Array | number[],
|
|
877
|
+
xearth: Float64Array | number[],
|
|
878
|
+
xsun: Float64Array | number[],
|
|
879
|
+
iflag: number,
|
|
880
|
+
): void {
|
|
881
|
+
if (iflag & SEFLG_HELCTR) {
|
|
882
|
+
/* heliocentric: subtract sun from body */
|
|
883
|
+
for (let i = 0; i <= 5; i++) x[i] -= xsun[i];
|
|
884
|
+
} else if (iflag & SEFLG_BARYCTR) {
|
|
885
|
+
/* barycentric: do nothing, positions are already barycentric */
|
|
886
|
+
} else {
|
|
887
|
+
/* geocentric: subtract earth from body */
|
|
888
|
+
for (let i = 0; i <= 5; i++) x[i] -= xearth[i];
|
|
889
|
+
}
|
|
890
|
+
}
|
|
891
|
+
|
|
892
|
+
/** Compute speed from 3 positions */
|
|
893
|
+
function calcSpeed(x0: Float64Array | number[], x1: Float64Array | number[], x2: Float64Array | number[], dt: number): void {
|
|
894
|
+
for (let i = 0; i <= 2; i++) {
|
|
895
|
+
const b = (x0[i] - x2[i]) / 2;
|
|
896
|
+
const a = (x0[i] + x2[i]) / 2 - x1[i];
|
|
897
|
+
x1[i + 3] = (2 * a + b) / dt;
|
|
898
|
+
}
|
|
899
|
+
}
|
|
900
|
+
|
|
901
|
+
/* ================================================================
|
|
902
|
+
* swiGetObserver: compute observer position (topocentric)
|
|
903
|
+
* ================================================================ */
|
|
904
|
+
export function swiGetObserver(tjdUt: number, iflag: number, doSave: boolean, xobs: Float64Array | number[], swed: SweData): void {
|
|
905
|
+
if (doSave && swed.topd.teval === tjdUt && swed.topd.tjdUt === tjdUt) {
|
|
906
|
+
for (let i = 0; i <= 5; i++) xobs[i] = swed.topd.xobs[i];
|
|
907
|
+
return;
|
|
908
|
+
}
|
|
909
|
+
const ell = swed.topd;
|
|
910
|
+
const phi = ell.geolat * DEGTORAD;
|
|
911
|
+
const lam = ell.geolon * DEGTORAD;
|
|
912
|
+
const alt = ell.geoalt;
|
|
913
|
+
const sinPhi = Math.sin(phi);
|
|
914
|
+
const cosPhi = Math.cos(phi);
|
|
915
|
+
/* Compute geocentric observer position */
|
|
916
|
+
const fl = 1.0 - EARTH_OBLATENESS;
|
|
917
|
+
const fl2 = fl * fl;
|
|
918
|
+
const cc2 = 1 / (cosPhi * cosPhi + fl2 * sinPhi * sinPhi);
|
|
919
|
+
const cc3 = Math.sqrt(cc2);
|
|
920
|
+
const ss = fl2 * cc2;
|
|
921
|
+
const ss2 = Math.sqrt(ss);
|
|
922
|
+
/* cartesian coordinates of observer */
|
|
923
|
+
const rr = (EARTH_RADIUS * cc3 + alt) * cosPhi;
|
|
924
|
+
const zz = (EARTH_RADIUS * ss2 + alt) * sinPhi;
|
|
925
|
+
/* Convert to AU */
|
|
926
|
+
const re = rr / AUNIT;
|
|
927
|
+
const ze = zz / AUNIT;
|
|
928
|
+
/* Sidereal time at observer */
|
|
929
|
+
const st = sweSidtime(swed, tjdUt) * 15 * DEGTORAD + lam;
|
|
930
|
+
xobs[0] = re * Math.cos(st);
|
|
931
|
+
xobs[1] = re * Math.sin(st);
|
|
932
|
+
xobs[2] = ze;
|
|
933
|
+
/* Speed: observer rotates with Earth */
|
|
934
|
+
const dt = 1.0 / 86400.0;
|
|
935
|
+
const st2 = st + EARTH_ROT_SPEED * DEGTORAD * dt;
|
|
936
|
+
const xobs2 = [
|
|
937
|
+
re * Math.cos(st2),
|
|
938
|
+
re * Math.sin(st2),
|
|
939
|
+
ze,
|
|
940
|
+
];
|
|
941
|
+
xobs[3] = (xobs2[0] - xobs[0]) / dt;
|
|
942
|
+
xobs[4] = (xobs2[1] - xobs[1]) / dt;
|
|
943
|
+
xobs[5] = 0; /* no radial speed change */
|
|
944
|
+
if (doSave) {
|
|
945
|
+
swed.topd.teval = tjdUt;
|
|
946
|
+
swed.topd.tjdUt = tjdUt;
|
|
947
|
+
for (let i = 0; i <= 5; i++) swed.topd.xobs[i] = xobs[i];
|
|
948
|
+
}
|
|
949
|
+
}
|
|
950
|
+
|
|
951
|
+
/** Get DE number for given ephemeris */
|
|
952
|
+
export function swiGetDenum(ipl: number, iflag: number, swed: SweData): number {
|
|
953
|
+
const epheflag = iflag & SEFLG_EPHMASK;
|
|
954
|
+
if (epheflag === SEFLG_JPLEPH && swed.jpldenum !== 0) {
|
|
955
|
+
return swed.jpldenum;
|
|
956
|
+
}
|
|
957
|
+
if (epheflag === SEFLG_SWIEPH) {
|
|
958
|
+
const fdp = swed.fidat[SEI_FILE_PLANET];
|
|
959
|
+
if (fdp.reader !== null && fdp.swephDenum !== 0) {
|
|
960
|
+
return fdp.swephDenum;
|
|
961
|
+
}
|
|
962
|
+
}
|
|
963
|
+
/* Moshier ephemeris: return DE404 */
|
|
964
|
+
return 404;
|
|
965
|
+
}
|
|
966
|
+
|
|
967
|
+
/** Compute centre body for SE_CENTER_BODY flag */
|
|
968
|
+
function calcCenterBody(ipl: number, iflag: number, swed: SweData): number {
|
|
969
|
+
/* Not relevant for Moshier, return 0 */
|
|
970
|
+
return 0;
|
|
971
|
+
}
|
|
972
|
+
|
|
973
|
+
/* ================================================================
|
|
974
|
+
* Part 3: appPosRest and appPosEtc* functions
|
|
975
|
+
* ================================================================ */
|
|
976
|
+
|
|
977
|
+
/**
|
|
978
|
+
* appPosRest: rest of apparent position computation.
|
|
979
|
+
* Converts equatorial J2000 geocentric to requested coord system.
|
|
980
|
+
*/
|
|
981
|
+
function appPosRest(
|
|
982
|
+
pdp: PlanData, iflag: number,
|
|
983
|
+
xx: Float64Array | number[],
|
|
984
|
+
xxSaved: Float64Array | number[],
|
|
985
|
+
swed: SweData,
|
|
986
|
+
): number {
|
|
987
|
+
let serr = '';
|
|
988
|
+
/* For SEFLG_SPEED flag, we always compute speed (already done) */
|
|
989
|
+
/* ---- If J2000 requested, skip precession/nutation ---- */
|
|
990
|
+
if ((iflag & SEFLG_J2000) === 0 && (iflag & SEFLG_ICRS) === 0) {
|
|
991
|
+
/* precess from J2000 to equinox of date */
|
|
992
|
+
swiPrecessSpeed(xx, pdp.teval, iflag, J2000_TO_J, swed);
|
|
993
|
+
/* nutation */
|
|
994
|
+
if ((iflag & SEFLG_NONUT) === 0) {
|
|
995
|
+
swiNutate(xx, iflag, false, swed);
|
|
996
|
+
}
|
|
997
|
+
}
|
|
998
|
+
if (iflag & SEFLG_ICRS) {
|
|
999
|
+
swiIcrs2fk5(xx, iflag, true);
|
|
1000
|
+
}
|
|
1001
|
+
/* ---- To ecliptic ---- */
|
|
1002
|
+
/* Save equatorial position first */
|
|
1003
|
+
for (let i = 0; i <= 5; i++) xxSaved[i + 18] = xx[i];
|
|
1004
|
+
/* Convert equatorial → ecliptic */
|
|
1005
|
+
const oe = ((iflag & SEFLG_J2000) !== 0 || (iflag & SEFLG_ICRS) !== 0) ? swed.oec2000 : swed.oec;
|
|
1006
|
+
swiCoortrf2(xx, xx, oe.seps, oe.ceps);
|
|
1007
|
+
if (iflag & SEFLG_SPEED) {
|
|
1008
|
+
swiCoortrf2(xx, xx, oe.seps, oe.ceps, 3, 3);
|
|
1009
|
+
}
|
|
1010
|
+
if ((iflag & SEFLG_NONUT) === 0 && (iflag & SEFLG_J2000) === 0 && (iflag & SEFLG_ICRS) === 0) {
|
|
1011
|
+
swiCoortrf2(xx, xx, swed.nut.snut, swed.nut.cnut);
|
|
1012
|
+
if (iflag & SEFLG_SPEED) {
|
|
1013
|
+
swiCoortrf2(xx, xx, swed.nut.snut, swed.nut.cnut, 3, 3);
|
|
1014
|
+
}
|
|
1015
|
+
}
|
|
1016
|
+
/* Save ecliptic cartesian */
|
|
1017
|
+
for (let i = 0; i <= 5; i++) xxSaved[i + 6] = xx[i];
|
|
1018
|
+
/* ---- Convert to polar if not XYZ ---- */
|
|
1019
|
+
/* ecliptic polar */
|
|
1020
|
+
swiCartpolSp(xx, xxSaved);
|
|
1021
|
+
/* equatorial cartesian already saved at offset 18 */
|
|
1022
|
+
/* equatorial polar */
|
|
1023
|
+
const xeq = [xxSaved[18], xxSaved[19], xxSaved[20], xxSaved[21], xxSaved[22], xxSaved[23]];
|
|
1024
|
+
swiCartpolSp(xeq, new Float64Array(6));
|
|
1025
|
+
/* Re-compute equatorial polar from the saved equatorial cartesian */
|
|
1026
|
+
const xeqPol = new Float64Array(6);
|
|
1027
|
+
swiCartpolSp(
|
|
1028
|
+
[xxSaved[18], xxSaved[19], xxSaved[20], xxSaved[21], xxSaved[22], xxSaved[23]],
|
|
1029
|
+
xeqPol,
|
|
1030
|
+
);
|
|
1031
|
+
for (let i = 0; i <= 5; i++) xxSaved[i + 12] = xeqPol[i];
|
|
1032
|
+
return OK;
|
|
1033
|
+
}
|
|
1034
|
+
|
|
1035
|
+
/**
|
|
1036
|
+
* appPosEtcPlan: apparent position for major planets (not Sun, Moon, nodes/apsides).
|
|
1037
|
+
* Assumes equatorial J2000 geocentric position+speed is in pdp.x[0..5].
|
|
1038
|
+
*/
|
|
1039
|
+
function appPosEtcPlan(ipl: number, iflag: number, swed: SweData): number {
|
|
1040
|
+
let serr = '';
|
|
1041
|
+
const epheflag = iflag & SEFLG_EPHMASK;
|
|
1042
|
+
let ipli: number;
|
|
1043
|
+
if (ipl >= SE_SUN && ipl < SE_NPLANETS) {
|
|
1044
|
+
ipli = PNOEXT2INT[ipl];
|
|
1045
|
+
} else {
|
|
1046
|
+
ipli = ipl;
|
|
1047
|
+
}
|
|
1048
|
+
const pdp = swed.pldat[ipli];
|
|
1049
|
+
const pedp = swed.pldat[SEI_EARTH];
|
|
1050
|
+
const psdp = swed.pldat[SEI_SUNBARY];
|
|
1051
|
+
const xx = new Float64Array(6);
|
|
1052
|
+
/* Start from stored heliocentric equatorial J2000 position */
|
|
1053
|
+
for (let i = 0; i <= 5; i++) xx[i] = pdp.x[i];
|
|
1054
|
+
/* Geocentric: subtract earth */
|
|
1055
|
+
if ((iflag & SEFLG_HELCTR) === 0 && (iflag & SEFLG_BARYCTR) === 0) {
|
|
1056
|
+
for (let i = 0; i <= 5; i++) xx[i] -= pedp.x[i];
|
|
1057
|
+
}
|
|
1058
|
+
if (iflag & SEFLG_HELCTR) {
|
|
1059
|
+
/* Already heliocentric: subtract sun barycentre */
|
|
1060
|
+
if (pdp.iflg & SEI_FLG_HELIO) {
|
|
1061
|
+
/* Already heliocentric, nothing to do */
|
|
1062
|
+
} else {
|
|
1063
|
+
for (let i = 0; i <= 5; i++) xx[i] -= psdp.x[i];
|
|
1064
|
+
}
|
|
1065
|
+
}
|
|
1066
|
+
/* Light-time correction */
|
|
1067
|
+
if ((iflag & SEFLG_TRUEPOS) === 0) {
|
|
1068
|
+
const dt = Math.sqrt(squareSum(xx)) * LIGHTTIME_AUNIT;
|
|
1069
|
+
if (pedp.iephe === SEFLG_JPLEPH || pedp.iephe === SEFLG_SWIEPH) {
|
|
1070
|
+
/* For JPL/SWIEPH: re-read sun/earth at t-dt for proper light-time */
|
|
1071
|
+
const t = pedp.teval - dt;
|
|
1072
|
+
const dx = new Float64Array(6);
|
|
1073
|
+
const xearth = new Float64Array(6);
|
|
1074
|
+
const xsun = new Float64Array(6);
|
|
1075
|
+
let retcLt = OK;
|
|
1076
|
+
if (pedp.iephe === SEFLG_JPLEPH) {
|
|
1077
|
+
const jplSerr: string[] = [''];
|
|
1078
|
+
if ((iflag & SEFLG_HELCTR) || (iflag & SEFLG_BARYCTR)) {
|
|
1079
|
+
retcLt = swiPleph(swed, t, J_EARTH, J_SBARY, xearth, jplSerr);
|
|
1080
|
+
} else {
|
|
1081
|
+
retcLt = swiPleph(swed, t, J_SUN, J_SBARY, xsun, jplSerr);
|
|
1082
|
+
}
|
|
1083
|
+
if (retcLt !== OK) {
|
|
1084
|
+
swiCloseJplFile(swed); swed.jplFileIsOpen = false;
|
|
1085
|
+
}
|
|
1086
|
+
}
|
|
1087
|
+
if (retcLt === OK && (pedp.iephe === SEFLG_SWIEPH)) {
|
|
1088
|
+
if ((iflag & SEFLG_HELCTR) || (iflag & SEFLG_BARYCTR)) {
|
|
1089
|
+
const rc = sweplanSwieph(swed, t, SEI_EARTH, SEI_FILE_PLANET, iflag,
|
|
1090
|
+
NO_SAVE, null, xearth, xsun, null);
|
|
1091
|
+
retcLt = rc.retc;
|
|
1092
|
+
} else {
|
|
1093
|
+
const rc = sweph(swed, t, SEI_SUNBARY, SEI_FILE_PLANET, iflag, null, NO_SAVE, xsun);
|
|
1094
|
+
retcLt = rc.retc;
|
|
1095
|
+
}
|
|
1096
|
+
}
|
|
1097
|
+
if (retcLt === OK) {
|
|
1098
|
+
if ((iflag & SEFLG_HELCTR) || (iflag & SEFLG_BARYCTR)) {
|
|
1099
|
+
for (let i = 0; i <= 5; i++) dx[i] = xearth[i];
|
|
1100
|
+
if (!(iflag & SEFLG_BARYCTR)) for (let i = 0; i <= 5; i++) dx[i] -= xsun[i];
|
|
1101
|
+
} else {
|
|
1102
|
+
for (let i = 0; i <= 5; i++) dx[i] = pedp.x[i] - xsun[i];
|
|
1103
|
+
}
|
|
1104
|
+
/* recompute geocentric: pdp.x - (new observer) */
|
|
1105
|
+
for (let i = 0; i <= 2; i++) {
|
|
1106
|
+
xx[i] = pdp.x[i] - dx[i];
|
|
1107
|
+
}
|
|
1108
|
+
/* keep speed from stored */
|
|
1109
|
+
for (let i = 3; i <= 5; i++) {
|
|
1110
|
+
xx[i] = pdp.x[i] - dx[i];
|
|
1111
|
+
}
|
|
1112
|
+
} else {
|
|
1113
|
+
/* fallback: velocity extrapolation */
|
|
1114
|
+
for (let i = 0; i <= 2; i++) xx[i] -= dt * xx[i + 3];
|
|
1115
|
+
}
|
|
1116
|
+
} else {
|
|
1117
|
+
/* Moshier: velocity extrapolation */
|
|
1118
|
+
for (let i = 0; i <= 2; i++) xx[i] -= dt * xx[i + 3];
|
|
1119
|
+
}
|
|
1120
|
+
}
|
|
1121
|
+
/* Gravitational deflection of light */
|
|
1122
|
+
if ((iflag & SEFLG_TRUEPOS) === 0 && (iflag & SEFLG_NOGDEFL) === 0) {
|
|
1123
|
+
swiDeflectLight(xx, 0, iflag, swed);
|
|
1124
|
+
}
|
|
1125
|
+
/* Aberration */
|
|
1126
|
+
if ((iflag & SEFLG_TRUEPOS) === 0 && (iflag & SEFLG_NOABERR) === 0) {
|
|
1127
|
+
swiAberrLight(xx, pedp.x, iflag);
|
|
1128
|
+
}
|
|
1129
|
+
/* Frame bias */
|
|
1130
|
+
if ((iflag & SEFLG_J2000) === 0 && (iflag & SEFLG_ICRS) === 0) {
|
|
1131
|
+
swiBias(xx, pdp.teval, iflag, false, swed);
|
|
1132
|
+
}
|
|
1133
|
+
/* Precession, nutation, and final coordinate system */
|
|
1134
|
+
return appPosRest(pdp, iflag, xx, pdp.xreturn, swed);
|
|
1135
|
+
}
|
|
1136
|
+
|
|
1137
|
+
/** appPosEtcPlanOsc: apparent position for osculating element bodies */
|
|
1138
|
+
function appPosEtcPlanOsc(ipl: number, ipli: number, iflag: number, swed: SweData): number {
|
|
1139
|
+
const pdp = swed.pldat[ipli];
|
|
1140
|
+
const pedp = swed.pldat[SEI_EARTH];
|
|
1141
|
+
const xx = new Float64Array(6);
|
|
1142
|
+
for (let i = 0; i <= 5; i++) xx[i] = pdp.x[i];
|
|
1143
|
+
/* Geocentric */
|
|
1144
|
+
if ((iflag & SEFLG_HELCTR) === 0 && (iflag & SEFLG_BARYCTR) === 0) {
|
|
1145
|
+
for (let i = 0; i <= 5; i++) xx[i] -= pedp.x[i];
|
|
1146
|
+
}
|
|
1147
|
+
/* Light-time */
|
|
1148
|
+
if ((iflag & SEFLG_TRUEPOS) === 0) {
|
|
1149
|
+
const dt = Math.sqrt(squareSum(xx)) * LIGHTTIME_AUNIT;
|
|
1150
|
+
for (let i = 0; i <= 2; i++) xx[i] -= dt * xx[i + 3];
|
|
1151
|
+
}
|
|
1152
|
+
/* Deflection */
|
|
1153
|
+
if ((iflag & SEFLG_TRUEPOS) === 0 && (iflag & SEFLG_NOGDEFL) === 0) {
|
|
1154
|
+
swiDeflectLight(xx, 0, iflag, swed);
|
|
1155
|
+
}
|
|
1156
|
+
/* Aberration */
|
|
1157
|
+
if ((iflag & SEFLG_TRUEPOS) === 0 && (iflag & SEFLG_NOABERR) === 0) {
|
|
1158
|
+
swiAberrLight(xx, pedp.x, iflag);
|
|
1159
|
+
}
|
|
1160
|
+
/* Frame bias */
|
|
1161
|
+
if ((iflag & SEFLG_J2000) === 0 && (iflag & SEFLG_ICRS) === 0) {
|
|
1162
|
+
swiBias(xx, pdp.teval, iflag, false, swed);
|
|
1163
|
+
}
|
|
1164
|
+
return appPosRest(pdp, iflag, xx, pdp.xreturn, swed);
|
|
1165
|
+
}
|
|
1166
|
+
|
|
1167
|
+
/** appPosEtcSun: apparent position for the Sun */
|
|
1168
|
+
function appPosEtcSun(iflag: number, swed: SweData): number {
|
|
1169
|
+
const pedp = swed.pldat[SEI_EARTH];
|
|
1170
|
+
const psdp = swed.pldat[SEI_SUNBARY];
|
|
1171
|
+
const xx = new Float64Array(6);
|
|
1172
|
+
/* Sun position = -Earth geocentric (sun is at centre in heliocentric) */
|
|
1173
|
+
if (iflag & SEFLG_HELCTR) {
|
|
1174
|
+
/* Heliocentric sun: always 0 */
|
|
1175
|
+
xx.fill(0);
|
|
1176
|
+
} else if (iflag & SEFLG_BARYCTR) {
|
|
1177
|
+
/* Barycentric sun: SSB → Sun */
|
|
1178
|
+
for (let i = 0; i <= 5; i++) xx[i] = psdp.x[i];
|
|
1179
|
+
} else {
|
|
1180
|
+
/* Geocentric sun = sunBary - earth */
|
|
1181
|
+
for (let i = 0; i <= 5; i++) xx[i] = psdp.x[i] - pedp.x[i];
|
|
1182
|
+
}
|
|
1183
|
+
/* Light-time */
|
|
1184
|
+
if ((iflag & SEFLG_TRUEPOS) === 0) {
|
|
1185
|
+
const dt = Math.sqrt(squareSum(xx)) * LIGHTTIME_AUNIT;
|
|
1186
|
+
for (let i = 0; i <= 2; i++) xx[i] -= dt * xx[i + 3];
|
|
1187
|
+
}
|
|
1188
|
+
/* Gravitational deflection: not for sun itself */
|
|
1189
|
+
/* Aberration */
|
|
1190
|
+
if ((iflag & SEFLG_TRUEPOS) === 0 && (iflag & SEFLG_NOABERR) === 0) {
|
|
1191
|
+
swiAberrLight(xx, pedp.x, iflag);
|
|
1192
|
+
}
|
|
1193
|
+
/* Frame bias */
|
|
1194
|
+
if ((iflag & SEFLG_J2000) === 0 && (iflag & SEFLG_ICRS) === 0) {
|
|
1195
|
+
swiBias(xx, pedp.teval, iflag, false, swed);
|
|
1196
|
+
}
|
|
1197
|
+
/* Use pedp for xreturn storage for Sun */
|
|
1198
|
+
/* Store in a temporary PlanData-like structure: we reuse psdp */
|
|
1199
|
+
/* Actually, we'll store sun's xreturn in the savedat slot */
|
|
1200
|
+
return appPosRest(psdp, iflag, xx, psdp.xreturn, swed);
|
|
1201
|
+
}
|
|
1202
|
+
|
|
1203
|
+
/** appPosEtcMoon: apparent position for the Moon */
|
|
1204
|
+
function appPosEtcMoon(iflag: number, swed: SweData): number {
|
|
1205
|
+
const pdp = swed.pldat[SEI_MOON];
|
|
1206
|
+
const pedp = swed.pldat[SEI_EARTH];
|
|
1207
|
+
const psdp = swed.pldat[SEI_SUNBARY];
|
|
1208
|
+
const xx = new Float64Array(6);
|
|
1209
|
+
const xxm = new Float64Array(6);
|
|
1210
|
+
const xobs = new Float64Array(6);
|
|
1211
|
+
const xobs2 = new Float64Array(6);
|
|
1212
|
+
const xe = new Float64Array(6);
|
|
1213
|
+
/* For Moshier, Moon is already geocentric equatorial J2000 */
|
|
1214
|
+
for (let i = 0; i <= 5; i++) { xx[i] = pdp.x[i]; xxm[i] = xx[i]; }
|
|
1215
|
+
/* to solar system barycentric */
|
|
1216
|
+
for (let i = 0; i <= 5; i++) xx[i] += pedp.x[i];
|
|
1217
|
+
/*******************************
|
|
1218
|
+
* observer
|
|
1219
|
+
*******************************/
|
|
1220
|
+
if (iflag & SEFLG_TOPOCTR) {
|
|
1221
|
+
if (swed.topd.teval !== pdp.teval || swed.topd.teval === 0) {
|
|
1222
|
+
swiGetObserver(pdp.teval, iflag | SEFLG_NONUT, DO_SAVE, xobs, swed);
|
|
1223
|
+
} else {
|
|
1224
|
+
for (let i = 0; i <= 5; i++) xobs[i] = swed.topd.xobs[i];
|
|
1225
|
+
}
|
|
1226
|
+
for (let i = 0; i <= 5; i++) xxm[i] -= xobs[i];
|
|
1227
|
+
for (let i = 0; i <= 5; i++) xobs[i] += pedp.x[i];
|
|
1228
|
+
} else if (iflag & SEFLG_BARYCTR) {
|
|
1229
|
+
for (let i = 0; i <= 5; i++) xobs[i] = 0;
|
|
1230
|
+
for (let i = 0; i <= 5; i++) xxm[i] += pedp.x[i];
|
|
1231
|
+
} else if (iflag & SEFLG_HELCTR) {
|
|
1232
|
+
for (let i = 0; i <= 5; i++) xobs[i] = psdp.x[i];
|
|
1233
|
+
for (let i = 0; i <= 5; i++) xxm[i] += pedp.x[i] - psdp.x[i];
|
|
1234
|
+
} else {
|
|
1235
|
+
for (let i = 0; i <= 5; i++) xobs[i] = pedp.x[i];
|
|
1236
|
+
}
|
|
1237
|
+
/*******************************
|
|
1238
|
+
* light-time
|
|
1239
|
+
*******************************/
|
|
1240
|
+
if ((iflag & SEFLG_TRUEPOS) === 0) {
|
|
1241
|
+
const dt = Math.sqrt(squareSum(xxm)) * LIGHTTIME_AUNIT;
|
|
1242
|
+
const t = pdp.teval - dt;
|
|
1243
|
+
let ltDone = false;
|
|
1244
|
+
/* JPL/SWIEPH: recompute Moon, Earth (and Sun if helio) at t-dt */
|
|
1245
|
+
if (pdp.iephe === SEFLG_JPLEPH) {
|
|
1246
|
+
const jplSerr: string[] = [''];
|
|
1247
|
+
const xxJpl = new Float64Array(6);
|
|
1248
|
+
let retcLt = swiPleph(swed, t, J_MOON, J_EARTH, xxJpl, jplSerr);
|
|
1249
|
+
if (retcLt === OK) {
|
|
1250
|
+
retcLt = swiPleph(swed, t, J_EARTH, J_SBARY, xe, jplSerr);
|
|
1251
|
+
}
|
|
1252
|
+
const xs = new Float64Array(6);
|
|
1253
|
+
if (retcLt === OK && (iflag & SEFLG_HELCTR)) {
|
|
1254
|
+
retcLt = swiPleph(swed, t, J_SUN, J_SBARY, xs, jplSerr);
|
|
1255
|
+
}
|
|
1256
|
+
if (retcLt === OK) {
|
|
1257
|
+
/* moon position barycentric = moon_geocentric + earth */
|
|
1258
|
+
for (let i = 0; i <= 5; i++) xx[i] = xxJpl[i] + xe[i];
|
|
1259
|
+
ltDone = true;
|
|
1260
|
+
} else {
|
|
1261
|
+
swiCloseJplFile(swed); swed.jplFileIsOpen = false;
|
|
1262
|
+
}
|
|
1263
|
+
} else if (pdp.iephe === SEFLG_SWIEPH) {
|
|
1264
|
+
const xxSw = new Float64Array(6);
|
|
1265
|
+
const xeSw = new Float64Array(6);
|
|
1266
|
+
const xsSw = new Float64Array(6);
|
|
1267
|
+
const rc = sweplanSwieph(swed, t, SEI_MOON, SEI_FILE_MOON, iflag,
|
|
1268
|
+
NO_SAVE, xxSw, xeSw, xsSw, null);
|
|
1269
|
+
if (rc.retc === OK) {
|
|
1270
|
+
for (let i = 0; i <= 5; i++) { xx[i] = xxSw[i] + xeSw[i]; xe[i] = xeSw[i]; }
|
|
1271
|
+
ltDone = true;
|
|
1272
|
+
}
|
|
1273
|
+
}
|
|
1274
|
+
if (!ltDone) {
|
|
1275
|
+
/* Moshier path: approximate light-time correction */
|
|
1276
|
+
for (let i = 0; i <= 2; i++) {
|
|
1277
|
+
xx[i] -= dt * xx[i + 3];
|
|
1278
|
+
xe[i] = pedp.x[i] - dt * pedp.x[i + 3];
|
|
1279
|
+
xe[i + 3] = pedp.x[i + 3];
|
|
1280
|
+
}
|
|
1281
|
+
}
|
|
1282
|
+
if (iflag & SEFLG_TOPOCTR) {
|
|
1283
|
+
swiGetObserver(t, iflag | SEFLG_NONUT, NO_SAVE, xobs2, swed);
|
|
1284
|
+
for (let i = 0; i <= 5; i++) xobs2[i] += xe[i];
|
|
1285
|
+
} else if (iflag & SEFLG_BARYCTR) {
|
|
1286
|
+
for (let i = 0; i <= 5; i++) xobs2[i] = 0;
|
|
1287
|
+
} else if (iflag & SEFLG_HELCTR) {
|
|
1288
|
+
for (let i = 0; i <= 5; i++) xobs2[i] = 0;
|
|
1289
|
+
} else {
|
|
1290
|
+
for (let i = 0; i <= 5; i++) xobs2[i] = xe[i];
|
|
1291
|
+
}
|
|
1292
|
+
}
|
|
1293
|
+
/* to correct center: subtract observer */
|
|
1294
|
+
for (let i = 0; i <= 5; i++) xx[i] -= xobs[i];
|
|
1295
|
+
/* Aberration */
|
|
1296
|
+
if ((iflag & SEFLG_TRUEPOS) === 0 && (iflag & SEFLG_NOABERR) === 0) {
|
|
1297
|
+
swiAberrLight(xx, xobs, iflag);
|
|
1298
|
+
if (iflag & SEFLG_SPEED) {
|
|
1299
|
+
for (let i = 3; i <= 5; i++) xx[i] += xobs[i] - xobs2[i];
|
|
1300
|
+
}
|
|
1301
|
+
}
|
|
1302
|
+
/* if !speedflag, speed = 0 */
|
|
1303
|
+
if (!(iflag & SEFLG_SPEED)) {
|
|
1304
|
+
for (let i = 3; i <= 5; i++) xx[i] = 0;
|
|
1305
|
+
}
|
|
1306
|
+
/* Frame bias */
|
|
1307
|
+
if ((iflag & SEFLG_J2000) === 0 && (iflag & SEFLG_ICRS) === 0) {
|
|
1308
|
+
swiBias(xx, pdp.teval, iflag, false, swed);
|
|
1309
|
+
}
|
|
1310
|
+
return appPosRest(pdp, iflag, xx, pdp.xreturn, swed);
|
|
1311
|
+
}
|
|
1312
|
+
|
|
1313
|
+
/** appPosEtcSbar: apparent position for solar system barycentre */
|
|
1314
|
+
function appPosEtcSbar(iflag: number, swed: SweData): number {
|
|
1315
|
+
/* SSB from geocentric perspective = -Earth */
|
|
1316
|
+
const pedp = swed.pldat[SEI_EARTH];
|
|
1317
|
+
const psdp = swed.pldat[SEI_SUNBARY];
|
|
1318
|
+
const xx = new Float64Array(6);
|
|
1319
|
+
for (let i = 0; i <= 5; i++) xx[i] = -pedp.x[i];
|
|
1320
|
+
return appPosRest(psdp, iflag, xx, psdp.xreturn, swed);
|
|
1321
|
+
}
|
|
1322
|
+
|
|
1323
|
+
/** appPosEtcMean: apparent position for mean node/apogee */
|
|
1324
|
+
function appPosEtcMean(ipl: number, iflag: number, swed: SweData): number {
|
|
1325
|
+
let ipli: number;
|
|
1326
|
+
if (ipl === SE_MEAN_NODE) ipli = SEI_MEAN_NODE;
|
|
1327
|
+
else if (ipl === SE_MEAN_APOG) ipli = SEI_MEAN_APOG;
|
|
1328
|
+
else if (ipl === SE_INTP_APOG) ipli = SEI_INTP_APOG;
|
|
1329
|
+
else if (ipl === SE_INTP_PERG) ipli = SEI_INTP_PERG;
|
|
1330
|
+
else ipli = SEI_MEAN_NODE;
|
|
1331
|
+
const pdp = swed.nddat[ipli];
|
|
1332
|
+
const xx = new Float64Array(6);
|
|
1333
|
+
for (let i = 0; i <= 5; i++) xx[i] = pdp.x[i];
|
|
1334
|
+
/* These are ecliptic of date; convert to equatorial J2000 */
|
|
1335
|
+
/* First to cartesian */
|
|
1336
|
+
swiPolcart(xx, xx);
|
|
1337
|
+
swiPolcartSp(xx, xx);
|
|
1338
|
+
/* Ecliptic of date → equatorial of date */
|
|
1339
|
+
swiCoortrf(xx, xx, -swed.oec.eps);
|
|
1340
|
+
swiCoortrf(xx, xx, -swed.oec.eps, 3, 3);
|
|
1341
|
+
/* If not J2000 and not NONUT, add nutation (backward: true equinox → mean) */
|
|
1342
|
+
if ((iflag & SEFLG_J2000) === 0 && (iflag & SEFLG_NONUT) === 0) {
|
|
1343
|
+
swiNutate(xx, iflag, true, swed);
|
|
1344
|
+
}
|
|
1345
|
+
/* Precess to J2000 */
|
|
1346
|
+
if ((iflag & SEFLG_J2000) === 0) {
|
|
1347
|
+
swiPrecessSpeed(xx, pdp.teval, iflag, J_TO_J2000, swed);
|
|
1348
|
+
}
|
|
1349
|
+
/* Now in equatorial J2000; frame bias */
|
|
1350
|
+
if ((iflag & SEFLG_J2000) === 0 && (iflag & SEFLG_ICRS) === 0) {
|
|
1351
|
+
swiBias(xx, pdp.teval, iflag, true, swed);
|
|
1352
|
+
}
|
|
1353
|
+
/* Now do the forward part: J2000 → requested system */
|
|
1354
|
+
/* But wait, appPosRest expects equatorial J2000 as input */
|
|
1355
|
+
return appPosRest(pdp, iflag, xx, pdp.xreturn, swed);
|
|
1356
|
+
}
|
|
1357
|
+
|
|
1358
|
+
/* ================================================================
|
|
1359
|
+
* Part 4: mainPlanet, mainPlanetBary, sweMoonInt, swePlanInt
|
|
1360
|
+
* ================================================================ */
|
|
1361
|
+
|
|
1362
|
+
/** Compute a main planet (SWIEPH with MOSEPH fallback) */
|
|
1363
|
+
function mainPlanet(
|
|
1364
|
+
tjd: number, ipli: number, epheflag: number, iflag: number,
|
|
1365
|
+
swed: SweData,
|
|
1366
|
+
): { retc: number; serr: string } {
|
|
1367
|
+
let serr = '';
|
|
1368
|
+
let retc: number;
|
|
1369
|
+
/* Ensure oec2000 is computed */
|
|
1370
|
+
swiCheckEcliptic(J2000, iflag, swed);
|
|
1371
|
+
let useEphe = epheflag;
|
|
1372
|
+
/* ---- JPLEPH path ---- */
|
|
1373
|
+
if (useEphe === SEFLG_JPLEPH) {
|
|
1374
|
+
const jplSerr: string[] = [''];
|
|
1375
|
+
const pedp = swed.pldat[SEI_EARTH];
|
|
1376
|
+
const psdp = swed.pldat[SEI_SUNBARY];
|
|
1377
|
+
const pdp = swed.pldat[ipli];
|
|
1378
|
+
const ictr = (ipli === SEI_MOON) ? J_EARTH : J_SBARY;
|
|
1379
|
+
/* earth (barycentric) */
|
|
1380
|
+
if (tjd !== pedp.teval || tjd === 0) {
|
|
1381
|
+
const xpe = new Float64Array(6);
|
|
1382
|
+
retc = swiPleph(swed, tjd, J_EARTH, J_SBARY, xpe, jplSerr);
|
|
1383
|
+
if (retc !== OK) {
|
|
1384
|
+
swiCloseJplFile(swed); swed.jplFileIsOpen = false;
|
|
1385
|
+
if (retc === NOT_AVAILABLE) { serr = jplSerr[0] + ' \nusing Swiss Eph; '; useEphe = SEFLG_SWIEPH; }
|
|
1386
|
+
else return { retc, serr: jplSerr[0] };
|
|
1387
|
+
} else {
|
|
1388
|
+
for (let i = 0; i <= 5; i++) pedp.x[i] = xpe[i];
|
|
1389
|
+
pedp.teval = tjd; pedp.xflgs = -1; pedp.iephe = SEFLG_JPLEPH;
|
|
1390
|
+
}
|
|
1391
|
+
}
|
|
1392
|
+
if (useEphe === SEFLG_JPLEPH) {
|
|
1393
|
+
/* sun (barycentric) */
|
|
1394
|
+
if (tjd !== psdp.teval || tjd === 0) {
|
|
1395
|
+
const xps = new Float64Array(6);
|
|
1396
|
+
retc = swiPleph(swed, tjd, J_SUN, J_SBARY, xps, jplSerr);
|
|
1397
|
+
if (retc !== OK) {
|
|
1398
|
+
swiCloseJplFile(swed); swed.jplFileIsOpen = false;
|
|
1399
|
+
if (retc === NOT_AVAILABLE) { serr = jplSerr[0] + ' \nusing Swiss Eph; '; useEphe = SEFLG_SWIEPH; }
|
|
1400
|
+
else return { retc, serr: jplSerr[0] };
|
|
1401
|
+
} else {
|
|
1402
|
+
for (let i = 0; i <= 5; i++) psdp.x[i] = xps[i];
|
|
1403
|
+
psdp.teval = tjd; psdp.xflgs = -1; psdp.iephe = SEFLG_JPLEPH;
|
|
1404
|
+
}
|
|
1405
|
+
}
|
|
1406
|
+
}
|
|
1407
|
+
if (useEphe === SEFLG_JPLEPH) {
|
|
1408
|
+
if (ipli === SEI_EARTH) {
|
|
1409
|
+
/* already got earth above */
|
|
1410
|
+
} else if (ipli === SEI_SUNBARY) {
|
|
1411
|
+
/* already got sun above */
|
|
1412
|
+
} else {
|
|
1413
|
+
if (tjd !== pdp.teval || pdp.iephe !== SEFLG_JPLEPH) {
|
|
1414
|
+
const xp = new Float64Array(6);
|
|
1415
|
+
retc = swiPleph(swed, tjd, PNOINT2JPL[ipli], ictr, xp, jplSerr);
|
|
1416
|
+
if (retc !== OK) {
|
|
1417
|
+
swiCloseJplFile(swed); swed.jplFileIsOpen = false;
|
|
1418
|
+
if (retc === NOT_AVAILABLE) { serr = jplSerr[0] + ' \nusing Swiss Eph; '; useEphe = SEFLG_SWIEPH; }
|
|
1419
|
+
else return { retc, serr: jplSerr[0] };
|
|
1420
|
+
} else {
|
|
1421
|
+
for (let i = 0; i <= 5; i++) pdp.x[i] = xp[i];
|
|
1422
|
+
pdp.teval = tjd; pdp.xflgs = -1; pdp.iephe = SEFLG_JPLEPH;
|
|
1423
|
+
}
|
|
1424
|
+
}
|
|
1425
|
+
}
|
|
1426
|
+
}
|
|
1427
|
+
if (useEphe === SEFLG_JPLEPH) return { retc: OK, serr };
|
|
1428
|
+
/* fall through to SWIEPH on NOT_AVAILABLE */
|
|
1429
|
+
}
|
|
1430
|
+
/* ---- SWIEPH path ---- */
|
|
1431
|
+
if (useEphe === SEFLG_SWIEPH) {
|
|
1432
|
+
const rc = sweplanSwieph(swed, tjd, ipli, SEI_FILE_PLANET, iflag,
|
|
1433
|
+
DO_SAVE, null, null, null, null);
|
|
1434
|
+
if (rc.retc === OK) {
|
|
1435
|
+
/* geocentric, lighttime etc. done by caller */
|
|
1436
|
+
return { retc: OK, serr: serr + rc.serr };
|
|
1437
|
+
}
|
|
1438
|
+
if (rc.retc === ERR) return rc;
|
|
1439
|
+
/* NOT_AVAILABLE: fall back to Moshier */
|
|
1440
|
+
serr = serr + rc.serr + ' \nusing Moshier eph.; ';
|
|
1441
|
+
useEphe = SEFLG_MOSEPH;
|
|
1442
|
+
}
|
|
1443
|
+
/* ---- MOSEPH path ---- */
|
|
1444
|
+
retc = swiMoshplan(swed, tjd, ipli, DO_SAVE, null, null);
|
|
1445
|
+
if (retc === ERR) {
|
|
1446
|
+
return { retc: ERR, serr: serr + `Moshier ephemeris error for planet ${ipli}` };
|
|
1447
|
+
}
|
|
1448
|
+
/* For Moshier: sunBary = 0 (heliocentric frame) */
|
|
1449
|
+
const psdpM = swed.pldat[SEI_SUNBARY];
|
|
1450
|
+
for (let i = 0; i <= 5; i++) psdpM.x[i] = 0;
|
|
1451
|
+
psdpM.teval = tjd;
|
|
1452
|
+
psdpM.iephe = SEFLG_MOSEPH;
|
|
1453
|
+
return { retc: OK, serr };
|
|
1454
|
+
}
|
|
1455
|
+
|
|
1456
|
+
/** Compute a main planet in barycentric coords */
|
|
1457
|
+
function mainPlanetBary(
|
|
1458
|
+
tjd: number, ipli: number, epheflag: number, iflag: number,
|
|
1459
|
+
doSave: boolean,
|
|
1460
|
+
xp: Float64Array | null, xe: Float64Array | null,
|
|
1461
|
+
xs: Float64Array | null, xm: Float64Array | null,
|
|
1462
|
+
swed: SweData,
|
|
1463
|
+
): { retc: number; serr: string } {
|
|
1464
|
+
let serr = '';
|
|
1465
|
+
let retc: number;
|
|
1466
|
+
let useEphe = epheflag;
|
|
1467
|
+
swiCheckEcliptic(J2000, iflag, swed);
|
|
1468
|
+
/* ---- JPLEPH path ---- */
|
|
1469
|
+
if (useEphe === SEFLG_JPLEPH) {
|
|
1470
|
+
const jplSerr: string[] = [''];
|
|
1471
|
+
const pedp = swed.pldat[SEI_EARTH];
|
|
1472
|
+
const psdp = swed.pldat[SEI_SUNBARY];
|
|
1473
|
+
const pdp = swed.pldat[ipli];
|
|
1474
|
+
const ictr = (ipli === SEI_MOON) ? J_EARTH : J_SBARY;
|
|
1475
|
+
/* earth */
|
|
1476
|
+
const xpe = new Float64Array(6);
|
|
1477
|
+
retc = swiPleph(swed, tjd, J_EARTH, J_SBARY, xpe, jplSerr);
|
|
1478
|
+
if (retc !== OK) {
|
|
1479
|
+
swiCloseJplFile(swed); swed.jplFileIsOpen = false;
|
|
1480
|
+
if (retc === NOT_AVAILABLE) { serr = jplSerr[0] + ' \nusing Swiss Eph; '; useEphe = SEFLG_SWIEPH; }
|
|
1481
|
+
else return { retc, serr: jplSerr[0] };
|
|
1482
|
+
}
|
|
1483
|
+
if (useEphe === SEFLG_JPLEPH) {
|
|
1484
|
+
if (doSave) { for (let i = 0; i <= 5; i++) pedp.x[i] = xpe[i]; pedp.teval = tjd; pedp.iephe = SEFLG_JPLEPH; }
|
|
1485
|
+
if (xe !== null) for (let i = 0; i <= 5; i++) xe[i] = xpe[i];
|
|
1486
|
+
/* sun */
|
|
1487
|
+
const xps = new Float64Array(6);
|
|
1488
|
+
retc = swiPleph(swed, tjd, J_SUN, J_SBARY, xps, jplSerr);
|
|
1489
|
+
if (retc !== OK) {
|
|
1490
|
+
swiCloseJplFile(swed); swed.jplFileIsOpen = false;
|
|
1491
|
+
if (retc === NOT_AVAILABLE) { serr = jplSerr[0] + ' \nusing Swiss Eph; '; useEphe = SEFLG_SWIEPH; }
|
|
1492
|
+
else return { retc, serr: jplSerr[0] };
|
|
1493
|
+
}
|
|
1494
|
+
}
|
|
1495
|
+
if (useEphe === SEFLG_JPLEPH) {
|
|
1496
|
+
const xps = new Float64Array(6);
|
|
1497
|
+
swiPleph(swed, tjd, J_SUN, J_SBARY, xps, jplSerr);
|
|
1498
|
+
if (doSave) { for (let i = 0; i <= 5; i++) psdp.x[i] = xps[i]; psdp.teval = tjd; psdp.iephe = SEFLG_JPLEPH; }
|
|
1499
|
+
if (xs !== null) for (let i = 0; i <= 5; i++) xs[i] = xps[i];
|
|
1500
|
+
/* target planet */
|
|
1501
|
+
if (ipli !== SEI_EARTH && ipli !== SEI_SUNBARY) {
|
|
1502
|
+
const xpp = new Float64Array(6);
|
|
1503
|
+
retc = swiPleph(swed, tjd, PNOINT2JPL[ipli], ictr, xpp, jplSerr);
|
|
1504
|
+
if (retc !== OK) {
|
|
1505
|
+
swiCloseJplFile(swed); swed.jplFileIsOpen = false;
|
|
1506
|
+
if (retc === NOT_AVAILABLE) { serr = jplSerr[0] + ' \nusing Swiss Eph; '; useEphe = SEFLG_SWIEPH; }
|
|
1507
|
+
else return { retc, serr: jplSerr[0] };
|
|
1508
|
+
} else {
|
|
1509
|
+
if (doSave) { for (let i = 0; i <= 5; i++) pdp.x[i] = xpp[i]; pdp.teval = tjd; pdp.iephe = SEFLG_JPLEPH; }
|
|
1510
|
+
if (xp !== null) for (let i = 0; i <= 5; i++) xp[i] = xpp[i];
|
|
1511
|
+
}
|
|
1512
|
+
} else if (ipli === SEI_EARTH) {
|
|
1513
|
+
if (xp !== null) for (let i = 0; i <= 5; i++) xp[i] = (xe !== null ? xe[i] : pedp.x[i]);
|
|
1514
|
+
}
|
|
1515
|
+
if (useEphe === SEFLG_JPLEPH) return { retc: OK, serr };
|
|
1516
|
+
}
|
|
1517
|
+
}
|
|
1518
|
+
/* ---- SWIEPH path ---- */
|
|
1519
|
+
if (useEphe === SEFLG_SWIEPH) {
|
|
1520
|
+
const rc = sweplanSwieph(swed, tjd, ipli, SEI_FILE_PLANET, iflag,
|
|
1521
|
+
doSave, xp, xe, xs, xm);
|
|
1522
|
+
if (rc.retc === OK) return { retc: OK, serr: serr + rc.serr };
|
|
1523
|
+
if (rc.retc === ERR || rc.retc === BEYOND_EPH_LIMITS) return rc;
|
|
1524
|
+
/* NOT_AVAILABLE: fall back to Moshier */
|
|
1525
|
+
serr = serr + rc.serr + ' \nusing Moshier eph.; ';
|
|
1526
|
+
useEphe = SEFLG_MOSEPH;
|
|
1527
|
+
}
|
|
1528
|
+
/* ---- MOSEPH path ---- */
|
|
1529
|
+
retc = swiMoshplan(swed, tjd, ipli, doSave, xp, xe);
|
|
1530
|
+
if (retc === ERR) {
|
|
1531
|
+
return { retc: ERR, serr: serr + `Moshier ephemeris error for planet ${ipli}` };
|
|
1532
|
+
}
|
|
1533
|
+
if (xs !== null) for (let i = 0; i <= 5; i++) xs[i] = 0;
|
|
1534
|
+
return { retc: OK, serr };
|
|
1535
|
+
}
|
|
1536
|
+
|
|
1537
|
+
/** Compute Moon (SWIEPH with MOSEPH fallback) */
|
|
1538
|
+
function sweMoonInt(
|
|
1539
|
+
tjd: number, iflag: number, swed: SweData,
|
|
1540
|
+
): { retc: number; serr: string } {
|
|
1541
|
+
let serr = '';
|
|
1542
|
+
swiCheckEcliptic(J2000, iflag, swed);
|
|
1543
|
+
let epheflag = iflag & SEFLG_EPHMASK;
|
|
1544
|
+
/* ---- JPLEPH path ---- */
|
|
1545
|
+
if (epheflag === SEFLG_JPLEPH) {
|
|
1546
|
+
const jplSerr: string[] = [''];
|
|
1547
|
+
const pdpMoon = swed.pldat[SEI_MOON];
|
|
1548
|
+
const pedp = swed.pldat[SEI_EARTH];
|
|
1549
|
+
const psdp = swed.pldat[SEI_SUNBARY];
|
|
1550
|
+
/* Moon relative to Earth */
|
|
1551
|
+
const xxm = new Float64Array(6);
|
|
1552
|
+
let retc = swiPleph(swed, tjd, J_MOON, J_EARTH, xxm, jplSerr);
|
|
1553
|
+
if (retc !== OK) {
|
|
1554
|
+
swiCloseJplFile(swed); swed.jplFileIsOpen = false;
|
|
1555
|
+
if (retc === NOT_AVAILABLE) { serr = jplSerr[0] + ' \ntrying Swiss Eph; '; epheflag = SEFLG_SWIEPH; }
|
|
1556
|
+
else return { retc, serr: jplSerr[0] };
|
|
1557
|
+
}
|
|
1558
|
+
if (epheflag === SEFLG_JPLEPH) {
|
|
1559
|
+
/* Earth barycentric */
|
|
1560
|
+
const xe = new Float64Array(6);
|
|
1561
|
+
retc = swiPleph(swed, tjd, J_EARTH, J_SBARY, xe, jplSerr);
|
|
1562
|
+
if (retc !== OK) {
|
|
1563
|
+
swiCloseJplFile(swed); swed.jplFileIsOpen = false;
|
|
1564
|
+
if (retc === NOT_AVAILABLE) { serr = jplSerr[0] + ' \ntrying Swiss Eph; '; epheflag = SEFLG_SWIEPH; }
|
|
1565
|
+
else return { retc, serr: jplSerr[0] };
|
|
1566
|
+
} else {
|
|
1567
|
+
for (let i = 0; i <= 5; i++) pedp.x[i] = xe[i];
|
|
1568
|
+
pedp.teval = tjd; pedp.xflgs = -1; pedp.iephe = SEFLG_JPLEPH;
|
|
1569
|
+
/* Moon stored geocentric like Moshier */
|
|
1570
|
+
for (let i = 0; i <= 5; i++) pdpMoon.x[i] = xxm[i];
|
|
1571
|
+
pdpMoon.teval = tjd; pdpMoon.xflgs = -1; pdpMoon.iephe = SEFLG_JPLEPH;
|
|
1572
|
+
/* Sun */
|
|
1573
|
+
const xs = new Float64Array(6);
|
|
1574
|
+
retc = swiPleph(swed, tjd, J_SUN, J_SBARY, xs, jplSerr);
|
|
1575
|
+
if (retc === OK) {
|
|
1576
|
+
for (let i = 0; i <= 5; i++) psdp.x[i] = xs[i];
|
|
1577
|
+
psdp.teval = tjd; psdp.iephe = SEFLG_JPLEPH;
|
|
1578
|
+
}
|
|
1579
|
+
return { retc: OK, serr };
|
|
1580
|
+
}
|
|
1581
|
+
}
|
|
1582
|
+
/* fall through to SWIEPH */
|
|
1583
|
+
}
|
|
1584
|
+
/* ---- SWIEPH path ---- */
|
|
1585
|
+
if (epheflag === SEFLG_SWIEPH) {
|
|
1586
|
+
const rc = swemoonSwieph(swed, tjd, iflag, DO_SAVE, null);
|
|
1587
|
+
if (rc.retc === OK) return { retc: OK, serr: serr + rc.serr };
|
|
1588
|
+
if (rc.retc === ERR) return rc;
|
|
1589
|
+
/* NOT_AVAILABLE: fall back to Moshier */
|
|
1590
|
+
serr = serr + rc.serr + ' \nusing Moshier eph. for moon; ';
|
|
1591
|
+
}
|
|
1592
|
+
/* ---- MOSEPH path ---- */
|
|
1593
|
+
const serrArr: string[] = [''];
|
|
1594
|
+
const retc = swiMoshmoon(swed, tjd, DO_SAVE, null, serrArr);
|
|
1595
|
+
if (retc === ERR) {
|
|
1596
|
+
return { retc: ERR, serr: serr + (serrArr[0] || 'Moshier moon error') };
|
|
1597
|
+
}
|
|
1598
|
+
return { retc: OK, serr };
|
|
1599
|
+
}
|
|
1600
|
+
|
|
1601
|
+
/**
|
|
1602
|
+
* swePlanInt: compute planet positions (internal).
|
|
1603
|
+
* This is the main dispatch function (SWIEPH with MOSEPH fallback).
|
|
1604
|
+
*/
|
|
1605
|
+
function swePlanInt(
|
|
1606
|
+
tjd: number, ipl: number, ipli: number, iflag: number,
|
|
1607
|
+
doSave: boolean,
|
|
1608
|
+
xpret: Float64Array | null,
|
|
1609
|
+
xeret: Float64Array | null,
|
|
1610
|
+
xsret: Float64Array | null,
|
|
1611
|
+
swed: SweData,
|
|
1612
|
+
): { retc: number; serr: string } {
|
|
1613
|
+
let serr = '';
|
|
1614
|
+
const epheflag = iflag & SEFLG_EPHMASK;
|
|
1615
|
+
/* ---- SWIEPH path ---- */
|
|
1616
|
+
if (epheflag === SEFLG_SWIEPH) {
|
|
1617
|
+
const rc = sweplanSwieph(swed, tjd, ipli, SEI_FILE_PLANET, iflag,
|
|
1618
|
+
doSave, xpret, xeret, xsret, null);
|
|
1619
|
+
if (rc.retc === OK) return rc;
|
|
1620
|
+
if (rc.retc === ERR) return rc;
|
|
1621
|
+
/* NOT_AVAILABLE: fall back to Moshier */
|
|
1622
|
+
serr = rc.serr + ' \nusing Moshier eph.; ';
|
|
1623
|
+
}
|
|
1624
|
+
/* ---- MOSEPH path ---- */
|
|
1625
|
+
swiCheckEcliptic(J2000, iflag, swed);
|
|
1626
|
+
const retc = swiMoshplan(swed, tjd, ipli, doSave, xpret, xeret);
|
|
1627
|
+
if (retc === ERR) {
|
|
1628
|
+
return { retc: ERR, serr: serr + `Moshier ephemeris error for planet ${ipli}` };
|
|
1629
|
+
}
|
|
1630
|
+
/* sunbary = 0 for Moshier */
|
|
1631
|
+
if (xsret !== null) {
|
|
1632
|
+
xsret.fill(0);
|
|
1633
|
+
}
|
|
1634
|
+
const psdp = swed.pldat[SEI_SUNBARY];
|
|
1635
|
+
for (let i = 0; i <= 5; i++) psdp.x[i] = 0;
|
|
1636
|
+
psdp.teval = tjd;
|
|
1637
|
+
psdp.iephe = SEFLG_MOSEPH;
|
|
1638
|
+
return { retc: OK, serr };
|
|
1639
|
+
}
|
|
1640
|
+
|
|
1641
|
+
/* ================================================================
|
|
1642
|
+
* Part 5: sweCalc, sweCalcUt, sweCalcInt, lunarOscElem, etc.
|
|
1643
|
+
* ================================================================ */
|
|
1644
|
+
|
|
1645
|
+
/** Compute osculating elements for Moon nodes/apsides */
|
|
1646
|
+
function lunarOscElem(
|
|
1647
|
+
tjd: number, ipl: number, iflag: number, swed: SweData,
|
|
1648
|
+
): { retc: number; serr: string } {
|
|
1649
|
+
let serr = '';
|
|
1650
|
+
const ipli = SEI_OSCU_APOG; /* for both true node and osc apogee */
|
|
1651
|
+
const pdp = (ipl === SE_TRUE_NODE) ? swed.nddat[SEI_TRUE_NODE] : swed.nddat[SEI_OSCU_APOG];
|
|
1652
|
+
/* We need the Moon first */
|
|
1653
|
+
const moonRes = sweMoonInt(tjd, iflag, swed);
|
|
1654
|
+
if (moonRes.retc === ERR) return moonRes;
|
|
1655
|
+
const pdpMoon = swed.pldat[SEI_MOON];
|
|
1656
|
+
/* For true node: compute osculating orbital elements from moon position+speed */
|
|
1657
|
+
/* Get moon geocentric position */
|
|
1658
|
+
const pedp = swed.pldat[SEI_EARTH];
|
|
1659
|
+
const xm = new Float64Array(6);
|
|
1660
|
+
for (let i = 0; i <= 5; i++) xm[i] = pdpMoon.x[i] - pedp.x[i];
|
|
1661
|
+
/* Convert to ecliptic of date */
|
|
1662
|
+
swiCoortrf2(xm, xm, swed.oec.seps, swed.oec.ceps);
|
|
1663
|
+
swiCoortrf2(xm, xm, swed.oec.seps, swed.oec.ceps, 3, 3);
|
|
1664
|
+
/* Compute osculating elements */
|
|
1665
|
+
swiCartpol(xm, xm);
|
|
1666
|
+
/* Node longitude (simplified: ascending node ≈ lon - 180 when lat=0 crossing) */
|
|
1667
|
+
/* This is a significant simplification. For better accuracy, we'd need
|
|
1668
|
+
the full orbital element computation. For now, use mean elements as fallback. */
|
|
1669
|
+
if (ipl === SE_TRUE_NODE) {
|
|
1670
|
+
/* Compute true node using 3 positions and interpolation */
|
|
1671
|
+
const speed_intv = NODE_CALC_INTV_MOSH;
|
|
1672
|
+
const xpos = [new Float64Array(6), new Float64Array(6), new Float64Array(6)];
|
|
1673
|
+
const xnorm = [0, 0, 0, 0, 0, 0];
|
|
1674
|
+
const xx = [0, 0, 0, 0, 0, 0];
|
|
1675
|
+
/* Compute moon at 3 times */
|
|
1676
|
+
for (let ipos = 0; ipos < 3; ipos++) {
|
|
1677
|
+
const t = tjd + (ipos - 1) * speed_intv;
|
|
1678
|
+
if (ipos === 1) {
|
|
1679
|
+
/* Use already computed moon */
|
|
1680
|
+
for (let i = 0; i <= 5; i++) xpos[ipos][i] = pdpMoon.x[i] - pedp.x[i];
|
|
1681
|
+
} else {
|
|
1682
|
+
/* Compute moon at offset time */
|
|
1683
|
+
const xxm = new Float64Array(6);
|
|
1684
|
+
swiMoshmoon(swed, t, NO_SAVE, xxm);
|
|
1685
|
+
/* Also need earth at that time */
|
|
1686
|
+
const xxe = new Float64Array(6);
|
|
1687
|
+
swiMoshplan(swed, t, SEI_EARTH, NO_SAVE, null, xxe);
|
|
1688
|
+
for (let i = 0; i <= 5; i++) xpos[ipos][i] = xxm[i] - xxe[i];
|
|
1689
|
+
}
|
|
1690
|
+
}
|
|
1691
|
+
/* Compute the ascending node from the cross product of position vectors */
|
|
1692
|
+
/* Cross product of positions at t-dt and t+dt gives normal to orbital plane */
|
|
1693
|
+
swiCrossProd(xpos[0], xpos[2], new Float64Array(6));
|
|
1694
|
+
/* Node = cross product of orbital plane normal with ecliptic normal */
|
|
1695
|
+
/* The ecliptic normal in equatorial J2000 coords is (0, -sin(eps), cos(eps)) */
|
|
1696
|
+
const eclNorm = [0, -swed.oec2000.seps, swed.oec2000.ceps];
|
|
1697
|
+
const orbNorm = new Float64Array(6);
|
|
1698
|
+
swiCrossProd(xpos[0], xpos[2], orbNorm);
|
|
1699
|
+
/* Node direction = eclNorm x orbNorm */
|
|
1700
|
+
const nodeVec = new Float64Array(6);
|
|
1701
|
+
swiCrossProd(
|
|
1702
|
+
new Float64Array([eclNorm[0], eclNorm[1], eclNorm[2]]),
|
|
1703
|
+
orbNorm,
|
|
1704
|
+
nodeVec,
|
|
1705
|
+
);
|
|
1706
|
+
const rNode = Math.sqrt(squareSum(nodeVec));
|
|
1707
|
+
if (rNode > 0) {
|
|
1708
|
+
for (let i = 0; i <= 2; i++) nodeVec[i] /= rNode;
|
|
1709
|
+
}
|
|
1710
|
+
/* Convert to polar */
|
|
1711
|
+
swiCartpol(nodeVec, nodeVec);
|
|
1712
|
+
/* Set distance to mean distance */
|
|
1713
|
+
const ndp = swed.nddat[SEI_TRUE_NODE];
|
|
1714
|
+
ndp.x[0] = nodeVec[0];
|
|
1715
|
+
ndp.x[1] = 0;
|
|
1716
|
+
ndp.x[2] = pdpMoon.x[2]; /* approximate distance */
|
|
1717
|
+
/* Convert back to cartesian equatorial J2000 for storage */
|
|
1718
|
+
swiPolcart(ndp.x, ndp.x);
|
|
1719
|
+
/* Speed: difference of two nodes */
|
|
1720
|
+
const xpos2 = [new Float64Array(6), new Float64Array(6), new Float64Array(6)];
|
|
1721
|
+
/* Simplified: compute speed from finite difference */
|
|
1722
|
+
ndp.x[3] = 0;
|
|
1723
|
+
ndp.x[4] = 0;
|
|
1724
|
+
ndp.x[5] = 0;
|
|
1725
|
+
ndp.teval = tjd;
|
|
1726
|
+
ndp.iephe = SEFLG_MOSEPH;
|
|
1727
|
+
return { retc: OK, serr };
|
|
1728
|
+
}
|
|
1729
|
+
/* Osculating apogee */
|
|
1730
|
+
if (ipl === SE_OSCU_APOG) {
|
|
1731
|
+
/* Simplified: use mean apogee as approximation */
|
|
1732
|
+
const pol = new Float64Array(6);
|
|
1733
|
+
const serrArr: string[] = [''];
|
|
1734
|
+
const retc = swiMeanApog(tjd, pol, serrArr);
|
|
1735
|
+
if (retc === ERR) return { retc: ERR, serr: serrArr[0] };
|
|
1736
|
+
const ndp = swed.nddat[SEI_OSCU_APOG];
|
|
1737
|
+
/* pol is in ecliptic of date, convert to equatorial J2000 */
|
|
1738
|
+
swiPolcart(pol, pol);
|
|
1739
|
+
swiCoortrf(pol, pol, -swed.oec.eps);
|
|
1740
|
+
swiPrecess(pol, tjd, iflag, J_TO_J2000, swed);
|
|
1741
|
+
for (let i = 0; i <= 2; i++) ndp.x[i] = pol[i];
|
|
1742
|
+
ndp.x[3] = 0;
|
|
1743
|
+
ndp.x[4] = 0;
|
|
1744
|
+
ndp.x[5] = 0;
|
|
1745
|
+
ndp.teval = tjd;
|
|
1746
|
+
ndp.iephe = SEFLG_MOSEPH;
|
|
1747
|
+
return { retc: OK, serr };
|
|
1748
|
+
}
|
|
1749
|
+
return { retc: OK, serr };
|
|
1750
|
+
}
|
|
1751
|
+
|
|
1752
|
+
/** Plan for osculating elements (fictitious planets) */
|
|
1753
|
+
function swiPlanForOscElem(
|
|
1754
|
+
iflag: number, tjd: number, ipl: number, swed: SweData,
|
|
1755
|
+
): { retc: number; serr: string } {
|
|
1756
|
+
let serr = '';
|
|
1757
|
+
const iplFict = ipl - SE_FICT_OFFSET;
|
|
1758
|
+
const ipli = SEI_ANYBODY;
|
|
1759
|
+
const pdp = swed.pldat[ipli];
|
|
1760
|
+
/* Need Earth and Sun */
|
|
1761
|
+
const pedp = swed.pldat[SEI_EARTH];
|
|
1762
|
+
const psdp = swed.pldat[SEI_SUNBARY];
|
|
1763
|
+
/* Compute Earth if needed */
|
|
1764
|
+
if (pedp.teval !== tjd) {
|
|
1765
|
+
const res = mainPlanet(tjd, SEI_EARTH, SEFLG_MOSEPH, iflag, swed);
|
|
1766
|
+
if (res.retc === ERR) return res;
|
|
1767
|
+
}
|
|
1768
|
+
/* Compute fictitious planet */
|
|
1769
|
+
const xp = pdp.x;
|
|
1770
|
+
const retc = swiOscElPlan(swed, tjd, xp, iplFict, ipli, pedp.x, psdp.x);
|
|
1771
|
+
if (retc === ERR) {
|
|
1772
|
+
serr = `error computing fictitious planet ${ipl}`;
|
|
1773
|
+
return { retc: ERR, serr };
|
|
1774
|
+
}
|
|
1775
|
+
pdp.teval = tjd;
|
|
1776
|
+
pdp.iephe = SEFLG_MOSEPH;
|
|
1777
|
+
return { retc: OK, serr };
|
|
1778
|
+
}
|
|
1779
|
+
|
|
1780
|
+
/** Compute interpolated apsides (Lilith, Priapus) */
|
|
1781
|
+
function intpApsides(
|
|
1782
|
+
tjd: number, ipl: number, iflag: number, swed: SweData,
|
|
1783
|
+
): { retc: number; serr: string } {
|
|
1784
|
+
let serr = '';
|
|
1785
|
+
const ipli = (ipl === SE_INTP_APOG) ? SEI_INTP_APOG : SEI_INTP_PERG;
|
|
1786
|
+
const ndp = swed.nddat[ipli];
|
|
1787
|
+
const pol = new Float64Array(6);
|
|
1788
|
+
swiIntpApsides(tjd, pol, ipli);
|
|
1789
|
+
/* pol is in ecliptic of date (polar) */
|
|
1790
|
+
/* Convert to equatorial J2000 cartesian */
|
|
1791
|
+
swiPolcart(pol, pol);
|
|
1792
|
+
swiCoortrf(pol, pol, -swed.oec.eps);
|
|
1793
|
+
swiPrecess(pol, tjd, iflag, J_TO_J2000, swed);
|
|
1794
|
+
for (let i = 0; i <= 2; i++) ndp.x[i] = pol[i];
|
|
1795
|
+
/* Speed: compute at t +/- dt */
|
|
1796
|
+
const dt = MEAN_NODE_SPEED_INTV;
|
|
1797
|
+
const pol2 = new Float64Array(6);
|
|
1798
|
+
swiIntpApsides(tjd - dt, pol2, ipli);
|
|
1799
|
+
swiPolcart(pol2, pol2);
|
|
1800
|
+
swiCoortrf(pol2, pol2, -swed.oec.eps);
|
|
1801
|
+
swiPrecess(pol2, tjd - dt, iflag, J_TO_J2000, swed);
|
|
1802
|
+
for (let i = 0; i <= 2; i++) {
|
|
1803
|
+
ndp.x[i + 3] = (ndp.x[i] - pol2[i]) / dt;
|
|
1804
|
+
}
|
|
1805
|
+
ndp.teval = tjd;
|
|
1806
|
+
ndp.iephe = SEFLG_MOSEPH;
|
|
1807
|
+
return { retc: OK, serr };
|
|
1808
|
+
}
|
|
1809
|
+
|
|
1810
|
+
/* ================================================================
|
|
1811
|
+
* sweCalcInt: the internal calc dispatcher
|
|
1812
|
+
* ================================================================ */
|
|
1813
|
+
function sweCalcInt(
|
|
1814
|
+
swed: SweData, tjd: number, ipl: number, iflag: number,
|
|
1815
|
+
): { flags: number; xx: Float64Array; serr: string } {
|
|
1816
|
+
const xx = new Float64Array(24);
|
|
1817
|
+
let serr = '';
|
|
1818
|
+
let retc = OK;
|
|
1819
|
+
let iflgRet = iflag;
|
|
1820
|
+
swiInitSwedIfStart(swed);
|
|
1821
|
+
/* Check ecliptic/nutation */
|
|
1822
|
+
const epheflag = iflag & SEFLG_EPHMASK;
|
|
1823
|
+
const tjdEt = tjd;
|
|
1824
|
+
swiCheckEcliptic(tjdEt, iflag, swed);
|
|
1825
|
+
if ((iflag & SEFLG_NONUT) === 0) {
|
|
1826
|
+
swiCheckNutation(tjdEt, iflag, swed);
|
|
1827
|
+
}
|
|
1828
|
+
/* ---- Ecliptic & Nutation special case ---- */
|
|
1829
|
+
/* Returns degrees directly; sweCalc must skip conversion for SE_ECL_NUT */
|
|
1830
|
+
if (ipl === SE_ECL_NUT) {
|
|
1831
|
+
xx[0] = swed.oec.eps * RADTODEG;
|
|
1832
|
+
xx[1] = swed.oec2000.eps * RADTODEG;
|
|
1833
|
+
if (swed.nut.tnut === tjdEt) {
|
|
1834
|
+
xx[2] = swed.nut.nutlo[0] * RADTODEG;
|
|
1835
|
+
xx[3] = swed.nut.nutlo[1] * RADTODEG;
|
|
1836
|
+
}
|
|
1837
|
+
xx[4] = 0; xx[5] = 0;
|
|
1838
|
+
return { flags: iflag, xx, serr };
|
|
1839
|
+
}
|
|
1840
|
+
/* ---- Sun ---- */
|
|
1841
|
+
if (ipl === SE_SUN) {
|
|
1842
|
+
/* Compute Earth (which gives us Sun) */
|
|
1843
|
+
const res = mainPlanet(tjdEt, SEI_EARTH, epheflag, iflag, swed);
|
|
1844
|
+
if (res.retc === ERR) {
|
|
1845
|
+
return { flags: ERR, xx, serr: res.serr };
|
|
1846
|
+
}
|
|
1847
|
+
retc = appPosEtcSun(iflag, swed);
|
|
1848
|
+
if (retc === ERR) {
|
|
1849
|
+
return { flags: ERR, xx, serr: 'error in appPosEtcSun' };
|
|
1850
|
+
}
|
|
1851
|
+
const psdp = swed.pldat[SEI_SUNBARY];
|
|
1852
|
+
for (let i = 0; i <= 23; i++) xx[i] = psdp.xreturn[i];
|
|
1853
|
+
return { flags: iflgRet, xx, serr };
|
|
1854
|
+
}
|
|
1855
|
+
/* ---- Moon ---- */
|
|
1856
|
+
if (ipl === SE_MOON) {
|
|
1857
|
+
const res = mainPlanet(tjdEt, SEI_EARTH, epheflag, iflag, swed);
|
|
1858
|
+
if (res.retc === ERR) {
|
|
1859
|
+
return { flags: ERR, xx, serr: res.serr };
|
|
1860
|
+
}
|
|
1861
|
+
const moonRes = sweMoonInt(tjdEt, iflag, swed);
|
|
1862
|
+
if (moonRes.retc === ERR) {
|
|
1863
|
+
return { flags: ERR, xx, serr: moonRes.serr };
|
|
1864
|
+
}
|
|
1865
|
+
retc = appPosEtcMoon(iflag, swed);
|
|
1866
|
+
if (retc === ERR) {
|
|
1867
|
+
return { flags: ERR, xx, serr: 'error in appPosEtcMoon' };
|
|
1868
|
+
}
|
|
1869
|
+
const pdp = swed.pldat[SEI_MOON];
|
|
1870
|
+
for (let i = 0; i <= 23; i++) xx[i] = pdp.xreturn[i];
|
|
1871
|
+
return { flags: iflgRet, xx, serr };
|
|
1872
|
+
}
|
|
1873
|
+
/* ---- Earth ---- */
|
|
1874
|
+
if (ipl === SE_EARTH) {
|
|
1875
|
+
const res = mainPlanet(tjdEt, SEI_EARTH, epheflag, iflag, swed);
|
|
1876
|
+
if (res.retc === ERR) {
|
|
1877
|
+
return { flags: ERR, xx, serr: res.serr };
|
|
1878
|
+
}
|
|
1879
|
+
/* Geocentric Earth is always 0,0,0 */
|
|
1880
|
+
if ((iflag & SEFLG_HELCTR) !== 0 || (iflag & SEFLG_BARYCTR) !== 0) {
|
|
1881
|
+
const pedp = swed.pldat[SEI_EARTH];
|
|
1882
|
+
/* pedp.x is equatorial J2000 heliocentric cartesian + speed */
|
|
1883
|
+
for (let i = 0; i <= 5; i++) xx[i] = pedp.x[i];
|
|
1884
|
+
/* Frame bias (only if not J2000/ICRS) */
|
|
1885
|
+
if ((iflag & SEFLG_J2000) === 0 && (iflag & SEFLG_ICRS) === 0) {
|
|
1886
|
+
swiBias(xx, tjdEt, iflag, false, swed);
|
|
1887
|
+
}
|
|
1888
|
+
/* Fill all 24 xreturn slots (precession, nutation, eq→ecl, polar) */
|
|
1889
|
+
appPosRest(pedp, iflag, xx, xx, swed);
|
|
1890
|
+
} else {
|
|
1891
|
+
/* Geocentric earth = 0 */
|
|
1892
|
+
xx.fill(0);
|
|
1893
|
+
}
|
|
1894
|
+
return { flags: iflgRet, xx, serr };
|
|
1895
|
+
}
|
|
1896
|
+
/* ---- Mercury..Pluto (major planets) ---- */
|
|
1897
|
+
if (ipl >= SE_MERCURY && ipl <= SE_PLUTO) {
|
|
1898
|
+
const ipli = PNOEXT2INT[ipl];
|
|
1899
|
+
/* Need Earth first */
|
|
1900
|
+
const earthRes = mainPlanet(tjdEt, SEI_EARTH, epheflag, iflag, swed);
|
|
1901
|
+
if (earthRes.retc === ERR) {
|
|
1902
|
+
return { flags: ERR, xx, serr: earthRes.serr };
|
|
1903
|
+
}
|
|
1904
|
+
/* Now the planet */
|
|
1905
|
+
const planRes = swePlanInt(tjdEt, ipl, ipli, iflag, DO_SAVE, null, null, null, swed);
|
|
1906
|
+
if (planRes.retc === ERR || planRes.retc === NOT_AVAILABLE) {
|
|
1907
|
+
return { flags: ERR, xx, serr: planRes.serr };
|
|
1908
|
+
}
|
|
1909
|
+
retc = appPosEtcPlan(ipl, iflag, swed);
|
|
1910
|
+
if (retc === ERR) {
|
|
1911
|
+
return { flags: ERR, xx, serr: 'error in appPosEtcPlan' };
|
|
1912
|
+
}
|
|
1913
|
+
const pdp = swed.pldat[ipli];
|
|
1914
|
+
for (let i = 0; i <= 23; i++) xx[i] = pdp.xreturn[i];
|
|
1915
|
+
return { flags: iflgRet, xx, serr };
|
|
1916
|
+
}
|
|
1917
|
+
/* ---- Chiron, Pholus, Ceres, Pallas, Juno, Vesta ---- */
|
|
1918
|
+
if (ipl >= SE_CHIRON && ipl <= SE_VESTA) {
|
|
1919
|
+
const ipli = PNOEXT2INT[ipl];
|
|
1920
|
+
/* Need Earth first */
|
|
1921
|
+
const earthRes = mainPlanet(tjdEt, SEI_EARTH, epheflag, iflag, swed);
|
|
1922
|
+
if (earthRes.retc === ERR) {
|
|
1923
|
+
return { flags: ERR, xx, serr: earthRes.serr };
|
|
1924
|
+
}
|
|
1925
|
+
const planRes = swePlanInt(tjdEt, ipl, ipli, iflag, DO_SAVE, null, null, null, swed);
|
|
1926
|
+
if (planRes.retc === ERR || planRes.retc === NOT_AVAILABLE) {
|
|
1927
|
+
return { flags: ERR, xx, serr: planRes.serr };
|
|
1928
|
+
}
|
|
1929
|
+
retc = appPosEtcPlan(ipl, iflag, swed);
|
|
1930
|
+
if (retc === ERR) {
|
|
1931
|
+
return { flags: ERR, xx, serr: 'error in appPosEtcPlan' };
|
|
1932
|
+
}
|
|
1933
|
+
const pdp = swed.pldat[ipli];
|
|
1934
|
+
for (let i = 0; i <= 23; i++) xx[i] = pdp.xreturn[i];
|
|
1935
|
+
return { flags: iflgRet, xx, serr };
|
|
1936
|
+
}
|
|
1937
|
+
/* ---- Mean Node ---- */
|
|
1938
|
+
if (ipl === SE_MEAN_NODE) {
|
|
1939
|
+
const earthRes = mainPlanet(tjdEt, SEI_EARTH, epheflag, iflag, swed);
|
|
1940
|
+
if (earthRes.retc === ERR) {
|
|
1941
|
+
return { flags: ERR, xx, serr: earthRes.serr };
|
|
1942
|
+
}
|
|
1943
|
+
const ndp = swed.nddat[SEI_MEAN_NODE];
|
|
1944
|
+
const pol = new Float64Array(6);
|
|
1945
|
+
const serrArr: string[] = [''];
|
|
1946
|
+
retc = swiMeanNode(tjdEt, pol, serrArr);
|
|
1947
|
+
if (retc === ERR) {
|
|
1948
|
+
return { flags: ERR, xx, serr: serrArr[0] };
|
|
1949
|
+
}
|
|
1950
|
+
/* pol is ecliptic of date polar */
|
|
1951
|
+
/* Compute speed: compute at t - dt */
|
|
1952
|
+
const dt = MEAN_NODE_SPEED_INTV;
|
|
1953
|
+
const pol2 = new Float64Array(6);
|
|
1954
|
+
swiMeanNode(tjdEt - dt, pol2);
|
|
1955
|
+
pol[3] = sweDifrad2n(pol[0], pol2[0]) / dt;
|
|
1956
|
+
pol[4] = (pol[1] - pol2[1]) / dt;
|
|
1957
|
+
pol[5] = (pol[2] - pol2[2]) / dt;
|
|
1958
|
+
/* Convert to equatorial J2000 cartesian */
|
|
1959
|
+
const xEcl = new Float64Array(6);
|
|
1960
|
+
swiPolcartSp(pol, xEcl);
|
|
1961
|
+
/* Ecliptic of date → equatorial of date */
|
|
1962
|
+
swiCoortrf(xEcl, xEcl, -swed.oec.eps);
|
|
1963
|
+
swiCoortrf(xEcl, xEcl, -swed.oec.eps, 3, 3);
|
|
1964
|
+
/* Nutation: true → mean (if not NONUT) */
|
|
1965
|
+
if ((iflag & SEFLG_NONUT) === 0) {
|
|
1966
|
+
swiNutate(xEcl, iflag, true, swed);
|
|
1967
|
+
}
|
|
1968
|
+
/* Precess to J2000 */
|
|
1969
|
+
swiPrecessSpeed(xEcl, tjdEt, iflag, J_TO_J2000, swed);
|
|
1970
|
+
/* Store */
|
|
1971
|
+
for (let i = 0; i <= 5; i++) ndp.x[i] = xEcl[i];
|
|
1972
|
+
ndp.teval = tjdEt;
|
|
1973
|
+
ndp.iephe = SEFLG_MOSEPH;
|
|
1974
|
+
/* Now apply forward transformations through appPosRest */
|
|
1975
|
+
const xxTemp = new Float64Array(6);
|
|
1976
|
+
for (let i = 0; i <= 5; i++) xxTemp[i] = ndp.x[i];
|
|
1977
|
+
retc = appPosRest(ndp, iflag, xxTemp, ndp.xreturn, swed);
|
|
1978
|
+
for (let i = 0; i <= 23; i++) xx[i] = ndp.xreturn[i];
|
|
1979
|
+
return { flags: iflgRet, xx, serr };
|
|
1980
|
+
}
|
|
1981
|
+
/* ---- True Node ---- */
|
|
1982
|
+
if (ipl === SE_TRUE_NODE) {
|
|
1983
|
+
const earthRes = mainPlanet(tjdEt, SEI_EARTH, epheflag, iflag, swed);
|
|
1984
|
+
if (earthRes.retc === ERR) {
|
|
1985
|
+
return { flags: ERR, xx, serr: earthRes.serr };
|
|
1986
|
+
}
|
|
1987
|
+
const moonRes2 = sweMoonInt(tjdEt, iflag, swed);
|
|
1988
|
+
if (moonRes2.retc === ERR) {
|
|
1989
|
+
return { flags: ERR, xx, serr: moonRes2.serr };
|
|
1990
|
+
}
|
|
1991
|
+
const oscRes = lunarOscElem(tjdEt, ipl, iflag, swed);
|
|
1992
|
+
if (oscRes.retc === ERR) {
|
|
1993
|
+
return { flags: ERR, xx, serr: oscRes.serr };
|
|
1994
|
+
}
|
|
1995
|
+
const ndp = swed.nddat[SEI_TRUE_NODE];
|
|
1996
|
+
const xxTemp = new Float64Array(6);
|
|
1997
|
+
for (let i = 0; i <= 5; i++) xxTemp[i] = ndp.x[i];
|
|
1998
|
+
retc = appPosRest(ndp, iflag, xxTemp, ndp.xreturn, swed);
|
|
1999
|
+
for (let i = 0; i <= 23; i++) xx[i] = ndp.xreturn[i];
|
|
2000
|
+
return { flags: iflgRet, xx, serr };
|
|
2001
|
+
}
|
|
2002
|
+
/* ---- Mean Apogee (Lilith) ---- */
|
|
2003
|
+
if (ipl === SE_MEAN_APOG) {
|
|
2004
|
+
const earthRes = mainPlanet(tjdEt, SEI_EARTH, epheflag, iflag, swed);
|
|
2005
|
+
if (earthRes.retc === ERR) {
|
|
2006
|
+
return { flags: ERR, xx, serr: earthRes.serr };
|
|
2007
|
+
}
|
|
2008
|
+
const ndp = swed.nddat[SEI_MEAN_APOG];
|
|
2009
|
+
const pol = new Float64Array(6);
|
|
2010
|
+
const serrArr: string[] = [''];
|
|
2011
|
+
retc = swiMeanApog(tjdEt, pol, serrArr);
|
|
2012
|
+
if (retc === ERR) {
|
|
2013
|
+
return { flags: ERR, xx, serr: serrArr[0] };
|
|
2014
|
+
}
|
|
2015
|
+
/* Speed */
|
|
2016
|
+
const dt = MEAN_NODE_SPEED_INTV;
|
|
2017
|
+
const pol2 = new Float64Array(6);
|
|
2018
|
+
swiMeanApog(tjdEt - dt, pol2);
|
|
2019
|
+
pol[3] = sweDifrad2n(pol[0], pol2[0]) / dt;
|
|
2020
|
+
pol[4] = (pol[1] - pol2[1]) / dt;
|
|
2021
|
+
pol[5] = (pol[2] - pol2[2]) / dt;
|
|
2022
|
+
/* Convert to equatorial J2000 cartesian */
|
|
2023
|
+
const xEcl = new Float64Array(6);
|
|
2024
|
+
swiPolcartSp(pol, xEcl);
|
|
2025
|
+
swiCoortrf(xEcl, xEcl, -swed.oec.eps);
|
|
2026
|
+
swiCoortrf(xEcl, xEcl, -swed.oec.eps, 3, 3);
|
|
2027
|
+
if ((iflag & SEFLG_NONUT) === 0) {
|
|
2028
|
+
swiNutate(xEcl, iflag, true, swed);
|
|
2029
|
+
}
|
|
2030
|
+
swiPrecessSpeed(xEcl, tjdEt, iflag, J_TO_J2000, swed);
|
|
2031
|
+
for (let i = 0; i <= 5; i++) ndp.x[i] = xEcl[i];
|
|
2032
|
+
ndp.teval = tjdEt;
|
|
2033
|
+
ndp.iephe = SEFLG_MOSEPH;
|
|
2034
|
+
const xxTemp = new Float64Array(6);
|
|
2035
|
+
for (let i = 0; i <= 5; i++) xxTemp[i] = ndp.x[i];
|
|
2036
|
+
retc = appPosRest(ndp, iflag, xxTemp, ndp.xreturn, swed);
|
|
2037
|
+
for (let i = 0; i <= 23; i++) xx[i] = ndp.xreturn[i];
|
|
2038
|
+
return { flags: iflgRet, xx, serr };
|
|
2039
|
+
}
|
|
2040
|
+
/* ---- Osculating Apogee ---- */
|
|
2041
|
+
if (ipl === SE_OSCU_APOG) {
|
|
2042
|
+
const earthRes = mainPlanet(tjdEt, SEI_EARTH, epheflag, iflag, swed);
|
|
2043
|
+
if (earthRes.retc === ERR) {
|
|
2044
|
+
return { flags: ERR, xx, serr: earthRes.serr };
|
|
2045
|
+
}
|
|
2046
|
+
const moonRes3 = sweMoonInt(tjdEt, iflag, swed);
|
|
2047
|
+
if (moonRes3.retc === ERR) {
|
|
2048
|
+
return { flags: ERR, xx, serr: moonRes3.serr };
|
|
2049
|
+
}
|
|
2050
|
+
const oscRes = lunarOscElem(tjdEt, ipl, iflag, swed);
|
|
2051
|
+
if (oscRes.retc === ERR) {
|
|
2052
|
+
return { flags: ERR, xx, serr: oscRes.serr };
|
|
2053
|
+
}
|
|
2054
|
+
const ndp = swed.nddat[SEI_OSCU_APOG];
|
|
2055
|
+
const xxTemp = new Float64Array(6);
|
|
2056
|
+
for (let i = 0; i <= 5; i++) xxTemp[i] = ndp.x[i];
|
|
2057
|
+
retc = appPosRest(ndp, iflag, xxTemp, ndp.xreturn, swed);
|
|
2058
|
+
for (let i = 0; i <= 23; i++) xx[i] = ndp.xreturn[i];
|
|
2059
|
+
return { flags: iflgRet, xx, serr };
|
|
2060
|
+
}
|
|
2061
|
+
/* ---- Interpolated Apogee (Lilith) / Perigee ---- */
|
|
2062
|
+
if (ipl === SE_INTP_APOG || ipl === SE_INTP_PERG) {
|
|
2063
|
+
const earthRes = mainPlanet(tjdEt, SEI_EARTH, epheflag, iflag, swed);
|
|
2064
|
+
if (earthRes.retc === ERR) {
|
|
2065
|
+
return { flags: ERR, xx, serr: earthRes.serr };
|
|
2066
|
+
}
|
|
2067
|
+
const intpRes = intpApsides(tjdEt, ipl, iflag, swed);
|
|
2068
|
+
if (intpRes.retc === ERR) {
|
|
2069
|
+
return { flags: ERR, xx, serr: intpRes.serr };
|
|
2070
|
+
}
|
|
2071
|
+
const ipli2 = (ipl === SE_INTP_APOG) ? SEI_INTP_APOG : SEI_INTP_PERG;
|
|
2072
|
+
const ndp = swed.nddat[ipli2];
|
|
2073
|
+
const xxTemp = new Float64Array(6);
|
|
2074
|
+
for (let i = 0; i <= 5; i++) xxTemp[i] = ndp.x[i];
|
|
2075
|
+
retc = appPosRest(ndp, iflag, xxTemp, ndp.xreturn, swed);
|
|
2076
|
+
for (let i = 0; i <= 23; i++) xx[i] = ndp.xreturn[i];
|
|
2077
|
+
return { flags: iflgRet, xx, serr };
|
|
2078
|
+
}
|
|
2079
|
+
/* ---- Fictitious planets ---- */
|
|
2080
|
+
if (ipl >= SE_FICT_OFFSET && ipl <= SE_FICT_MAX) {
|
|
2081
|
+
const earthRes = mainPlanet(tjdEt, SEI_EARTH, epheflag, iflag, swed);
|
|
2082
|
+
if (earthRes.retc === ERR) {
|
|
2083
|
+
return { flags: ERR, xx, serr: earthRes.serr };
|
|
2084
|
+
}
|
|
2085
|
+
const fictRes = swiPlanForOscElem(iflag, tjdEt, ipl, swed);
|
|
2086
|
+
if (fictRes.retc === ERR) {
|
|
2087
|
+
return { flags: ERR, xx, serr: fictRes.serr };
|
|
2088
|
+
}
|
|
2089
|
+
const pdp = swed.pldat[SEI_ANYBODY];
|
|
2090
|
+
retc = appPosEtcPlanOsc(ipl, SEI_ANYBODY, iflag, swed);
|
|
2091
|
+
if (retc === ERR) {
|
|
2092
|
+
return { flags: ERR, xx, serr: 'error in appPosEtcPlanOsc' };
|
|
2093
|
+
}
|
|
2094
|
+
for (let i = 0; i <= 23; i++) xx[i] = pdp.xreturn[i];
|
|
2095
|
+
return { flags: iflgRet, xx, serr };
|
|
2096
|
+
}
|
|
2097
|
+
/* ---- Asteroids (> SE_AST_OFFSET) ---- */
|
|
2098
|
+
if (ipl >= SE_AST_OFFSET) {
|
|
2099
|
+
serr = `Asteroid ${ipl - SE_AST_OFFSET} not available in Moshier ephemeris`;
|
|
2100
|
+
return { flags: ERR, xx, serr };
|
|
2101
|
+
}
|
|
2102
|
+
serr = `unknown planet number ${ipl}`;
|
|
2103
|
+
return { flags: ERR, xx, serr };
|
|
2104
|
+
}
|
|
2105
|
+
|
|
2106
|
+
/* ================================================================
|
|
2107
|
+
* sweCalc: main public calculation function (ET input)
|
|
2108
|
+
* ================================================================ */
|
|
2109
|
+
export function sweCalc(
|
|
2110
|
+
swed: SweData, tjdEt: number, ipl: number, iflag: number,
|
|
2111
|
+
): { flags: number; xx: Float64Array; serr: string } {
|
|
2112
|
+
swiInitSwedIfStart(swed);
|
|
2113
|
+
iflag = plausIflag(iflag, ipl, tjdEt, swed);
|
|
2114
|
+
const result = sweCalcInt(swed, tjdEt, ipl, iflag);
|
|
2115
|
+
/* Build the 6-element return array from the 24-element xreturn */
|
|
2116
|
+
const xxOut = new Float64Array(6);
|
|
2117
|
+
if (result.flags !== ERR) {
|
|
2118
|
+
/* SE_ECL_NUT returns degrees directly from sweCalcInt; no conversion needed */
|
|
2119
|
+
if (ipl === SE_ECL_NUT) {
|
|
2120
|
+
for (let i = 0; i < 6; i++) xxOut[i] = result.xx[i];
|
|
2121
|
+
} else {
|
|
2122
|
+
/* Select which 6 values to return based on flags */
|
|
2123
|
+
let offset = 0;
|
|
2124
|
+
if (iflag & SEFLG_EQUATORIAL) {
|
|
2125
|
+
if (iflag & SEFLG_XYZ) {
|
|
2126
|
+
offset = 18; /* equatorial cartesian */
|
|
2127
|
+
} else {
|
|
2128
|
+
offset = 12; /* equatorial polar */
|
|
2129
|
+
}
|
|
2130
|
+
} else {
|
|
2131
|
+
if (iflag & SEFLG_XYZ) {
|
|
2132
|
+
offset = 6; /* ecliptic cartesian */
|
|
2133
|
+
} else {
|
|
2134
|
+
offset = 0; /* ecliptic polar */
|
|
2135
|
+
}
|
|
2136
|
+
}
|
|
2137
|
+
for (let i = 0; i < 6; i++) xxOut[i] = result.xx[offset + i];
|
|
2138
|
+
/* Radians to degrees: if SEFLG_RADIANS NOT set, convert */
|
|
2139
|
+
if ((iflag & SEFLG_RADIANS) === 0 && (iflag & SEFLG_XYZ) === 0) {
|
|
2140
|
+
xxOut[0] *= RADTODEG;
|
|
2141
|
+
xxOut[1] *= RADTODEG;
|
|
2142
|
+
xxOut[3] *= RADTODEG;
|
|
2143
|
+
xxOut[4] *= RADTODEG;
|
|
2144
|
+
}
|
|
2145
|
+
/* Normalize longitude */
|
|
2146
|
+
if ((iflag & SEFLG_XYZ) === 0 && (iflag & SEFLG_RADIANS) === 0) {
|
|
2147
|
+
xxOut[0] = sweDegnorm(xxOut[0]);
|
|
2148
|
+
} else if ((iflag & SEFLG_XYZ) === 0 && (iflag & SEFLG_RADIANS) !== 0) {
|
|
2149
|
+
xxOut[0] = sweRadnorm(xxOut[0]);
|
|
2150
|
+
}
|
|
2151
|
+
}
|
|
2152
|
+
}
|
|
2153
|
+
/* Apply sidereal correction */
|
|
2154
|
+
if (result.flags !== ERR && (iflag & SEFLG_SIDEREAL) !== 0 && ipl !== SE_ECL_NUT) {
|
|
2155
|
+
const ayaRes = swiGetAyanamsaEx(swed, tjdEt, iflag);
|
|
2156
|
+
if (ayaRes.retc === ERR) {
|
|
2157
|
+
return { flags: ERR, xx: xxOut, serr: ayaRes.serr };
|
|
2158
|
+
}
|
|
2159
|
+
const aya = ayaRes.daya;
|
|
2160
|
+
if ((iflag & SEFLG_XYZ) === 0) {
|
|
2161
|
+
if ((iflag & SEFLG_RADIANS) !== 0) {
|
|
2162
|
+
xxOut[0] -= aya * DEGTORAD;
|
|
2163
|
+
xxOut[0] = sweRadnorm(xxOut[0]);
|
|
2164
|
+
} else {
|
|
2165
|
+
xxOut[0] -= aya;
|
|
2166
|
+
xxOut[0] = sweDegnorm(xxOut[0]);
|
|
2167
|
+
}
|
|
2168
|
+
}
|
|
2169
|
+
}
|
|
2170
|
+
return { flags: result.flags, xx: xxOut, serr: result.serr };
|
|
2171
|
+
}
|
|
2172
|
+
|
|
2173
|
+
/** sweCalcUt: main public calculation function (UT input) */
|
|
2174
|
+
export function sweCalcUt(
|
|
2175
|
+
swed: SweData, tjdUt: number, ipl: number, iflag: number,
|
|
2176
|
+
): { flags: number; xx: Float64Array; serr: string } {
|
|
2177
|
+
const dt = sweDeltatEx(tjdUt, iflag, swed);
|
|
2178
|
+
return sweCalc(swed, tjdUt + dt, ipl, iflag);
|
|
2179
|
+
}
|
|
2180
|
+
|
|
2181
|
+
/* ================================================================
|
|
2182
|
+
* Part 6: Ayanamsa (sidereal mode)
|
|
2183
|
+
* ================================================================ */
|
|
2184
|
+
|
|
2185
|
+
/** Set sidereal mode */
|
|
2186
|
+
export function sweSetSidMode(swed: SweData, sidMode: number, t0: number, ayanT0: number): void {
|
|
2187
|
+
swiInitSwedIfStart(swed);
|
|
2188
|
+
const sidBits = sidMode & SE_SIDBITS;
|
|
2189
|
+
const sidModeBase = sidMode & 0xFF;
|
|
2190
|
+
swed.sidd.sidMode = sidMode;
|
|
2191
|
+
if (sidModeBase === SE_SIDM_USER) {
|
|
2192
|
+
swed.sidd.ayanT0 = ayanT0;
|
|
2193
|
+
swed.sidd.t0 = t0;
|
|
2194
|
+
} else if (sidModeBase < SE_NSIDM_PREDEF) {
|
|
2195
|
+
const ayaInit = AYANAMSA[sidModeBase];
|
|
2196
|
+
swed.sidd.ayanT0 = ayaInit.ayanT0;
|
|
2197
|
+
swed.sidd.t0 = ayaInit.t0;
|
|
2198
|
+
swed.sidd.t0IsUT = ayaInit.t0IsUT;
|
|
2199
|
+
}
|
|
2200
|
+
swed.ayanaIsSet = true;
|
|
2201
|
+
swiForceAppPosEtc(swed);
|
|
2202
|
+
}
|
|
2203
|
+
|
|
2204
|
+
/** Get ayanamsa for a given tjd (ET), with ephemeris flag */
|
|
2205
|
+
export function sweGetAyanamsaEx(
|
|
2206
|
+
swed: SweData, tjdEt: number, iflag: number,
|
|
2207
|
+
): { retc: number; daya: number; serr: string } {
|
|
2208
|
+
const res = swiGetAyanamsaEx(swed, tjdEt, iflag);
|
|
2209
|
+
return res;
|
|
2210
|
+
}
|
|
2211
|
+
|
|
2212
|
+
/** Get ayanamsa for a given tjd (UT), with ephemeris flag */
|
|
2213
|
+
export function sweGetAyanamsaExUt(
|
|
2214
|
+
swed: SweData, tjdUt: number, iflag: number,
|
|
2215
|
+
): { retc: number; daya: number; serr: string } {
|
|
2216
|
+
const dt = sweDeltatEx(tjdUt, iflag, swed);
|
|
2217
|
+
return swiGetAyanamsaEx(swed, tjdUt + dt, iflag);
|
|
2218
|
+
}
|
|
2219
|
+
|
|
2220
|
+
/** Get ayanamsa (simple, ET) */
|
|
2221
|
+
export function sweGetAyanamsa(swed: SweData, tjdEt: number): number {
|
|
2222
|
+
const res = swiGetAyanamsaEx(swed, tjdEt, 0);
|
|
2223
|
+
return res.daya;
|
|
2224
|
+
}
|
|
2225
|
+
|
|
2226
|
+
/** Get ayanamsa (simple, UT) */
|
|
2227
|
+
export function sweGetAyanamsaUt(swed: SweData, tjdUt: number): number {
|
|
2228
|
+
const dt = sweDeltat(tjdUt, swed);
|
|
2229
|
+
return sweGetAyanamsa(swed, tjdUt + dt);
|
|
2230
|
+
}
|
|
2231
|
+
|
|
2232
|
+
/** Internal ayanamsa computation */
|
|
2233
|
+
function swiGetAyanamsaEx(
|
|
2234
|
+
swed: SweData, tjdEt: number, iflag: number,
|
|
2235
|
+
): { retc: number; daya: number; serr: string } {
|
|
2236
|
+
let serr = '';
|
|
2237
|
+
if (!swed.ayanaIsSet) {
|
|
2238
|
+
sweSetSidMode(swed, SE_SIDM_FAGAN_BRADLEY, 0, 0);
|
|
2239
|
+
}
|
|
2240
|
+
const sidMode = swed.sidd.sidMode;
|
|
2241
|
+
const sidBits = sidMode & SE_SIDBITS;
|
|
2242
|
+
const sidModeBase = sidMode & 0xFF;
|
|
2243
|
+
let t0 = swed.sidd.t0;
|
|
2244
|
+
const ayanT0 = swed.sidd.ayanT0;
|
|
2245
|
+
const t0IsUT = swed.sidd.t0IsUT;
|
|
2246
|
+
/* Star-based ayanamsas */
|
|
2247
|
+
iflag = plausIflag(iflag, -1, tjdEt, swed);
|
|
2248
|
+
const epheflag = iflag & SEFLG_EPHMASK;
|
|
2249
|
+
const otherflag = iflag & ~SEFLG_EPHMASK;
|
|
2250
|
+
iflag &= SEFLG_EPHMASK;
|
|
2251
|
+
iflag |= SEFLG_NONUT;
|
|
2252
|
+
const iflagGalequ = iflag | SEFLG_TRUEPOS;
|
|
2253
|
+
let iflagTrue = iflag;
|
|
2254
|
+
if (otherflag & SEFLG_TRUEPOS) iflagTrue |= SEFLG_TRUEPOS;
|
|
2255
|
+
if (otherflag & SEFLG_NOABERR) iflagTrue |= SEFLG_NOABERR;
|
|
2256
|
+
if (otherflag & SEFLG_NOGDEFL) iflagTrue |= SEFLG_NOGDEFL;
|
|
2257
|
+
if (sidModeBase === SE_SIDM_TRUE_CITRA) {
|
|
2258
|
+
const res = sweFixstar(swed, 'Spica', tjdEt, iflagTrue);
|
|
2259
|
+
if (res.flags === ERR) return { retc: ERR, daya: 0, serr: res.serr };
|
|
2260
|
+
return { retc: res.flags & SEFLG_EPHMASK, daya: sweDegnorm(res.xx[0] - 180), serr: '' };
|
|
2261
|
+
}
|
|
2262
|
+
if (sidModeBase === SE_SIDM_TRUE_REVATI) {
|
|
2263
|
+
const res = sweFixstar(swed, ',zePsc', tjdEt, iflagTrue);
|
|
2264
|
+
if (res.flags === ERR) return { retc: ERR, daya: 0, serr: res.serr };
|
|
2265
|
+
return { retc: res.flags & SEFLG_EPHMASK, daya: sweDegnorm(res.xx[0] - 359.8333333333), serr: '' };
|
|
2266
|
+
}
|
|
2267
|
+
if (sidModeBase === SE_SIDM_TRUE_PUSHYA) {
|
|
2268
|
+
const res = sweFixstar(swed, ',deCnc', tjdEt, iflagTrue);
|
|
2269
|
+
if (res.flags === ERR) return { retc: ERR, daya: 0, serr: res.serr };
|
|
2270
|
+
return { retc: res.flags & SEFLG_EPHMASK, daya: sweDegnorm(res.xx[0] - 106), serr: '' };
|
|
2271
|
+
}
|
|
2272
|
+
if (sidModeBase === SE_SIDM_TRUE_SHEORAN) {
|
|
2273
|
+
const res = sweFixstar(swed, ',deCnc', tjdEt, iflagTrue);
|
|
2274
|
+
if (res.flags === ERR) return { retc: ERR, daya: 0, serr: res.serr };
|
|
2275
|
+
return { retc: res.flags & SEFLG_EPHMASK, daya: sweDegnorm(res.xx[0] - 103.49264221625), serr: '' };
|
|
2276
|
+
}
|
|
2277
|
+
if (sidModeBase === SE_SIDM_TRUE_MULA) {
|
|
2278
|
+
const res = sweFixstar(swed, ',laSco', tjdEt, iflagTrue);
|
|
2279
|
+
if (res.flags === ERR) return { retc: ERR, daya: 0, serr: res.serr };
|
|
2280
|
+
return { retc: res.flags & SEFLG_EPHMASK, daya: sweDegnorm(res.xx[0] - 240), serr: '' };
|
|
2281
|
+
}
|
|
2282
|
+
if (sidModeBase === SE_SIDM_GALCENT_0SAG) {
|
|
2283
|
+
const res = sweFixstar(swed, ',SgrA*', tjdEt, iflagTrue);
|
|
2284
|
+
if (res.flags === ERR) return { retc: ERR, daya: 0, serr: res.serr };
|
|
2285
|
+
return { retc: res.flags & SEFLG_EPHMASK, daya: sweDegnorm(res.xx[0] - 240.0), serr: '' };
|
|
2286
|
+
}
|
|
2287
|
+
if (sidModeBase === SE_SIDM_GALCENT_COCHRANE) {
|
|
2288
|
+
const res = sweFixstar(swed, ',SgrA*', tjdEt, iflagTrue);
|
|
2289
|
+
if (res.flags === ERR) return { retc: ERR, daya: 0, serr: res.serr };
|
|
2290
|
+
return { retc: res.flags & SEFLG_EPHMASK, daya: sweDegnorm(res.xx[0] - 270.0), serr: '' };
|
|
2291
|
+
}
|
|
2292
|
+
if (sidModeBase === SE_SIDM_GALCENT_RGILBRAND) {
|
|
2293
|
+
const res = sweFixstar(swed, ',SgrA*', tjdEt, iflagTrue);
|
|
2294
|
+
if (res.flags === ERR) return { retc: ERR, daya: 0, serr: res.serr };
|
|
2295
|
+
return { retc: res.flags & SEFLG_EPHMASK, daya: sweDegnorm(res.xx[0] - 210.0 - 90.0 * 0.3819660113), serr: '' };
|
|
2296
|
+
}
|
|
2297
|
+
if (sidModeBase === SE_SIDM_GALCENT_MULA_WILHELM) {
|
|
2298
|
+
const res = sweFixstar(swed, ',SgrA*', tjdEt, iflagTrue | SEFLG_EQUATORIAL);
|
|
2299
|
+
if (res.flags === ERR) return { retc: ERR, daya: 0, serr: res.serr };
|
|
2300
|
+
const eps = swiEpsiln(tjdEt, iflag, swed) * RADTODEG;
|
|
2301
|
+
const mc = swiArmcToMc(res.xx[0], eps);
|
|
2302
|
+
return { retc: res.flags & SEFLG_EPHMASK, daya: sweDegnorm(mc - 246.6666666667), serr: '' };
|
|
2303
|
+
}
|
|
2304
|
+
if (sidModeBase === SE_SIDM_GALEQU_IAU1958) {
|
|
2305
|
+
const res = sweFixstar(swed, ',GP1958', tjdEt, iflagGalequ);
|
|
2306
|
+
if (res.flags === ERR) return { retc: ERR, daya: 0, serr: res.serr };
|
|
2307
|
+
return { retc: res.flags & SEFLG_EPHMASK, daya: sweDegnorm(res.xx[0] - 150), serr: '' };
|
|
2308
|
+
}
|
|
2309
|
+
if (sidModeBase === SE_SIDM_GALEQU_TRUE) {
|
|
2310
|
+
const res = sweFixstar(swed, ',GPol', tjdEt, iflagGalequ);
|
|
2311
|
+
if (res.flags === ERR) return { retc: ERR, daya: 0, serr: res.serr };
|
|
2312
|
+
return { retc: res.flags & SEFLG_EPHMASK, daya: sweDegnorm(res.xx[0] - 150), serr: '' };
|
|
2313
|
+
}
|
|
2314
|
+
if (sidModeBase === SE_SIDM_GALEQU_MULA) {
|
|
2315
|
+
const res = sweFixstar(swed, ',GPol', tjdEt, iflagGalequ);
|
|
2316
|
+
if (res.flags === ERR) return { retc: ERR, daya: 0, serr: res.serr };
|
|
2317
|
+
return { retc: res.flags & SEFLG_EPHMASK, daya: sweDegnorm(res.xx[0] - 150 - 6.6666666667), serr: '' };
|
|
2318
|
+
}
|
|
2319
|
+
/* Get the reference date t0 */
|
|
2320
|
+
let tjd0 = t0;
|
|
2321
|
+
if (t0IsUT) {
|
|
2322
|
+
tjd0 = t0 + sweDeltatEx(t0, iflag, swed);
|
|
2323
|
+
}
|
|
2324
|
+
/* General precession since t0 */
|
|
2325
|
+
const { dpre: dPreJ, deps: dOblJ } = swiLdpPeps(tjdEt);
|
|
2326
|
+
const { dpre: dPre0, deps: dObl0 } = swiLdpPeps(tjd0);
|
|
2327
|
+
/* Precession correction for ayanamsa */
|
|
2328
|
+
let precCorr = 0;
|
|
2329
|
+
if (sidModeBase < SE_NSIDM_PREDEF && AYANAMSA[sidModeBase].precOffset !== 0 &&
|
|
2330
|
+
AYANAMSA[sidModeBase].precOffset !== -1 &&
|
|
2331
|
+
(sidBits & SE_SIDBIT_NO_PREC_OFFSET) === 0 &&
|
|
2332
|
+
(sidBits & SE_SIDBIT_PREC_ORIG) === 0) {
|
|
2333
|
+
/* Apply precession offset correction */
|
|
2334
|
+
/* For now, simplified: no precession model switching */
|
|
2335
|
+
}
|
|
2336
|
+
/* Ayanamsa = initial offset + precession from t0 to tjd */
|
|
2337
|
+
let daya = (dPreJ - dPre0) * RADTODEG + ayanT0;
|
|
2338
|
+
/* SSY_PLANE correction */
|
|
2339
|
+
if (sidBits & SE_SIDBIT_SSY_PLANE) {
|
|
2340
|
+
/* project onto solar system invariable plane */
|
|
2341
|
+
/* not yet supported - skip */
|
|
2342
|
+
}
|
|
2343
|
+
/* ECL_T0 and ECL_DATE modes */
|
|
2344
|
+
/* These are not supported in simplified mode */
|
|
2345
|
+
return { retc: OK, daya: sweDegnorm(daya), serr };
|
|
2346
|
+
}
|
|
2347
|
+
|
|
2348
|
+
/** Get ayanamsa with speed */
|
|
2349
|
+
export function swiGetAyanamsaWithSpeed(
|
|
2350
|
+
swed: SweData, tjdEt: number, iflag: number,
|
|
2351
|
+
): { retc: number; daya: number; dayaSpeed: number; serr: string } {
|
|
2352
|
+
const res = swiGetAyanamsaEx(swed, tjdEt, iflag);
|
|
2353
|
+
if (res.retc === ERR) {
|
|
2354
|
+
return { retc: ERR, daya: 0, dayaSpeed: 0, serr: res.serr };
|
|
2355
|
+
}
|
|
2356
|
+
/* Compute speed as finite difference */
|
|
2357
|
+
const dt = 0.01;
|
|
2358
|
+
const res2 = swiGetAyanamsaEx(swed, tjdEt + dt, iflag);
|
|
2359
|
+
let dayaSpeed = 0;
|
|
2360
|
+
if (res2.retc === OK) {
|
|
2361
|
+
dayaSpeed = sweDifrad2n(res2.daya * DEGTORAD, res.daya * DEGTORAD) * RADTODEG / dt;
|
|
2362
|
+
}
|
|
2363
|
+
return { retc: OK, daya: res.daya, dayaSpeed, serr: res.serr };
|
|
2364
|
+
}
|
|
2365
|
+
|
|
2366
|
+
/** Tropical RA → sidereal longitude */
|
|
2367
|
+
export function swiTropRa2SidLon(
|
|
2368
|
+
xin: Float64Array | number[],
|
|
2369
|
+
xout: Float64Array | number[],
|
|
2370
|
+
xoutr: Float64Array | number[],
|
|
2371
|
+
iflag: number,
|
|
2372
|
+
swed: SweData,
|
|
2373
|
+
): void {
|
|
2374
|
+
/* Not yet implemented for this simplified port */
|
|
2375
|
+
for (let i = 0; i < 6; i++) xout[i] = xin[i];
|
|
2376
|
+
for (let i = 0; i < 6; i++) xoutr[i] = xin[i];
|
|
2377
|
+
}
|
|
2378
|
+
|
|
2379
|
+
/** Tropical RA → sidereal longitude (Sosy method) */
|
|
2380
|
+
export function swiTropRa2SidLonSosy(
|
|
2381
|
+
xin: Float64Array | number[],
|
|
2382
|
+
xout: Float64Array | number[],
|
|
2383
|
+
iflag: number,
|
|
2384
|
+
swed: SweData,
|
|
2385
|
+
): void {
|
|
2386
|
+
/* Not yet implemented for this simplified port */
|
|
2387
|
+
for (let i = 0; i < 6; i++) xout[i] = xin[i];
|
|
2388
|
+
}
|
|
2389
|
+
|
|
2390
|
+
/* ================================================================
|
|
2391
|
+
* Part 7: SE1 file reading — readConst, getNewSegment, rotBack, sweph
|
|
2392
|
+
* ================================================================ */
|
|
2393
|
+
|
|
2394
|
+
/**
|
|
2395
|
+
* Load an SE1 ephemeris file into memory for later use by sweph().
|
|
2396
|
+
* The filename should be the base name (e.g. "sepl_18.se1", "semo_18.se1").
|
|
2397
|
+
*/
|
|
2398
|
+
export function sweSetEphemerisFile(
|
|
2399
|
+
filename: string, buffer: ArrayBuffer, swed: SweData,
|
|
2400
|
+
): void {
|
|
2401
|
+
if (!swed.ephemerisFiles) swed.ephemerisFiles = new Map();
|
|
2402
|
+
swed.ephemerisFiles.set(filename.toLowerCase(), buffer);
|
|
2403
|
+
}
|
|
2404
|
+
|
|
2405
|
+
/** Look up a pre-loaded ephemeris file buffer by filename */
|
|
2406
|
+
function findEphemerisFile(fname: string, swed: SweData): ArrayBuffer | null {
|
|
2407
|
+
if (!swed.ephemerisFiles) return null;
|
|
2408
|
+
/* Try exact name first, then basename (strip directory prefix) */
|
|
2409
|
+
const key = fname.toLowerCase();
|
|
2410
|
+
let buf = swed.ephemerisFiles.get(key) ?? null;
|
|
2411
|
+
if (!buf) {
|
|
2412
|
+
/* Try without directory prefix */
|
|
2413
|
+
const slash = key.lastIndexOf('/');
|
|
2414
|
+
if (slash >= 0) {
|
|
2415
|
+
buf = swed.ephemerisFiles.get(key.substring(slash + 1)) ?? null;
|
|
2416
|
+
}
|
|
2417
|
+
}
|
|
2418
|
+
return buf;
|
|
2419
|
+
}
|
|
2420
|
+
|
|
2421
|
+
/**
|
|
2422
|
+
* readConst: read constants from an SE1 ephemeris file header.
|
|
2423
|
+
* Translated from sweph.c read_const() (lines 4509-4887).
|
|
2424
|
+
*/
|
|
2425
|
+
function readConst(
|
|
2426
|
+
ifno: number, swed: SweData,
|
|
2427
|
+
): { retc: number; serr: string } {
|
|
2428
|
+
const fdp = swed.fidat[ifno];
|
|
2429
|
+
const reader = fdp.reader;
|
|
2430
|
+
if (!reader) return { retc: ERR, serr: 'no reader' };
|
|
2431
|
+
let serr = '';
|
|
2432
|
+
/* ---- version string ---- */
|
|
2433
|
+
const versionLine = reader.readLine();
|
|
2434
|
+
if (versionLine === null) {
|
|
2435
|
+
return closeAndError(fdp, swed, `Ephemeris file ${fdp.fnam} is damaged (0a).`);
|
|
2436
|
+
}
|
|
2437
|
+
/* extract version number */
|
|
2438
|
+
let sp = 0;
|
|
2439
|
+
while (sp < versionLine.length && (versionLine.charCodeAt(sp) < 48 || versionLine.charCodeAt(sp) > 57)) sp++;
|
|
2440
|
+
fdp.fversion = sp < versionLine.length ? parseInt(versionLine.substring(sp), 10) : 0;
|
|
2441
|
+
/* ---- filename check ---- */
|
|
2442
|
+
const fnameLine = reader.readLine();
|
|
2443
|
+
if (fnameLine === null) {
|
|
2444
|
+
return closeAndError(fdp, swed, `Ephemeris file ${fdp.fnam} is damaged (0b).`);
|
|
2445
|
+
}
|
|
2446
|
+
const fnamFromFile = fnameLine.trim().toLowerCase();
|
|
2447
|
+
const fnamActual = fdp.fnam.toLowerCase().replace(/^.*\//, '');
|
|
2448
|
+
if (fnamFromFile !== fnamActual) {
|
|
2449
|
+
return closeAndError(fdp, swed, `Ephemeris file name '${fnamActual}' wrong; rename '${fnamFromFile}'.`);
|
|
2450
|
+
}
|
|
2451
|
+
/* ---- copyright ---- */
|
|
2452
|
+
const copyrightLine = reader.readLine();
|
|
2453
|
+
if (copyrightLine === null) {
|
|
2454
|
+
return closeAndError(fdp, swed, `Ephemeris file ${fdp.fnam} is damaged (0c).`);
|
|
2455
|
+
}
|
|
2456
|
+
/* ---- asteroid orbital elements (if single asteroid file) ---- */
|
|
2457
|
+
if (ifno === SEI_FILE_ANY_AST) {
|
|
2458
|
+
const elemLine = reader.readLine(AS_MAXCH * 2);
|
|
2459
|
+
if (elemLine === null) {
|
|
2460
|
+
return closeAndError(fdp, swed, `Ephemeris file ${fdp.fnam} is damaged (0d).`);
|
|
2461
|
+
}
|
|
2462
|
+
swed.astelem = elemLine;
|
|
2463
|
+
/* Parse H, G, diameter from element line */
|
|
2464
|
+
/* Skip MPC number and name portion */
|
|
2465
|
+
let si = 0;
|
|
2466
|
+
while (si < elemLine.length && elemLine[si] === ' ') si++;
|
|
2467
|
+
while (si < elemLine.length && elemLine[si] >= '0' && elemLine[si] <= '9') si++;
|
|
2468
|
+
if (si < elemLine.length) si++; // skip space after number
|
|
2469
|
+
const nameOffset = si;
|
|
2470
|
+
const lastnam = 19;
|
|
2471
|
+
fdp.astnam = elemLine.substring(nameOffset, nameOffset + lastnam + nameOffset).trim();
|
|
2472
|
+
swed.astH = parseFloat(elemLine.substring(35 + nameOffset) || '0');
|
|
2473
|
+
swed.astG = parseFloat(elemLine.substring(42 + nameOffset) || '0');
|
|
2474
|
+
if (swed.astG === 0) swed.astG = 0.15;
|
|
2475
|
+
const diamStr = elemLine.substring(51 + nameOffset, 58 + nameOffset);
|
|
2476
|
+
swed.astDiam = parseFloat(diamStr) || 0;
|
|
2477
|
+
if (swed.astDiam === 0) {
|
|
2478
|
+
swed.astDiam = 1329 / Math.sqrt(0.15) * Math.pow(10, -0.2 * swed.astH);
|
|
2479
|
+
}
|
|
2480
|
+
}
|
|
2481
|
+
/* ---- endianness test ---- */
|
|
2482
|
+
/* Read 4 raw bytes to detect endianness */
|
|
2483
|
+
const testBytes = reader.readBytes(4);
|
|
2484
|
+
const testLE = testBytes[0] | (testBytes[1] << 8) | (testBytes[2] << 16) | (testBytes[3] << 24);
|
|
2485
|
+
const testBE = (testBytes[0] << 24) | (testBytes[1] << 16) | (testBytes[2] << 8) | testBytes[3];
|
|
2486
|
+
let isLittleEndian: boolean;
|
|
2487
|
+
if ((testLE >>> 0) === SEI_FILE_TEST_ENDIAN) {
|
|
2488
|
+
isLittleEndian = true;
|
|
2489
|
+
} else if ((testBE >>> 0) === SEI_FILE_TEST_ENDIAN) {
|
|
2490
|
+
isLittleEndian = false;
|
|
2491
|
+
} else {
|
|
2492
|
+
return closeAndError(fdp, swed, `Ephemeris file ${fdp.fnam} is damaged (0f).`);
|
|
2493
|
+
}
|
|
2494
|
+
reader.setLittleEndian(isLittleEndian);
|
|
2495
|
+
fdp.iflg = isLittleEndian ? 1 : 0; // store endianness in iflg (1=LE, 0=BE)
|
|
2496
|
+
/* ---- file length check ---- */
|
|
2497
|
+
const storedLen = reader.readInt32();
|
|
2498
|
+
if (storedLen !== reader.length) {
|
|
2499
|
+
return closeAndError(fdp, swed, `Ephemeris file ${fdp.fnam} is damaged (0h).`);
|
|
2500
|
+
}
|
|
2501
|
+
/* ---- DE number ---- */
|
|
2502
|
+
fdp.swephDenum = reader.readInt32();
|
|
2503
|
+
/* ---- start and end epoch ---- */
|
|
2504
|
+
fdp.tfstart = reader.readFloat64();
|
|
2505
|
+
fdp.tfend = reader.readFloat64();
|
|
2506
|
+
/* ---- number of planets ---- */
|
|
2507
|
+
let nplan = reader.readUint16();
|
|
2508
|
+
let nbytesIpl = 2;
|
|
2509
|
+
if (nplan > 256) {
|
|
2510
|
+
nbytesIpl = 4;
|
|
2511
|
+
nplan = nplan % 256;
|
|
2512
|
+
}
|
|
2513
|
+
if (nplan < 1 || nplan > 20) {
|
|
2514
|
+
return closeAndError(fdp, swed, `Ephemeris file ${fdp.fnam} is damaged (0i).`);
|
|
2515
|
+
}
|
|
2516
|
+
fdp.npl = nplan;
|
|
2517
|
+
/* ---- which planets ---- */
|
|
2518
|
+
for (let i = 0; i < nplan; i++) {
|
|
2519
|
+
fdp.ipl[i] = nbytesIpl === 4 ? reader.readInt32() : reader.readUintN(nbytesIpl);
|
|
2520
|
+
}
|
|
2521
|
+
/* ---- asteroid name (if applicable) ---- */
|
|
2522
|
+
if (ifno === SEI_FILE_ANY_AST) {
|
|
2523
|
+
/* Read 30-byte name field (may contain old-style name or be overridden) */
|
|
2524
|
+
reader.readBytes(30); // skip old name field
|
|
2525
|
+
/* Name was already parsed from orbital elements above */
|
|
2526
|
+
}
|
|
2527
|
+
/* ---- CRC check ---- */
|
|
2528
|
+
const crcPos = reader.position;
|
|
2529
|
+
const storedCrc = reader.readUint32();
|
|
2530
|
+
/* Read header area from start to CRC position */
|
|
2531
|
+
reader.seekSet(0);
|
|
2532
|
+
const headerBuf = reader.readBytes(crcPos);
|
|
2533
|
+
const computedCrc = swiCrc32(headerBuf, crcPos);
|
|
2534
|
+
if (computedCrc !== storedCrc) {
|
|
2535
|
+
return closeAndError(fdp, swed, `Ephemeris file ${fdp.fnam} is damaged (0n). CRC mismatch.`);
|
|
2536
|
+
}
|
|
2537
|
+
reader.seekSet(crcPos + 4);
|
|
2538
|
+
/* ---- general constants ---- */
|
|
2539
|
+
swed.gcdat.clight = reader.readFloat64();
|
|
2540
|
+
swed.gcdat.aunit = reader.readFloat64();
|
|
2541
|
+
swed.gcdat.helgravconst = reader.readFloat64();
|
|
2542
|
+
swed.gcdat.ratme = reader.readFloat64();
|
|
2543
|
+
swed.gcdat.sunradius = reader.readFloat64();
|
|
2544
|
+
/* ---- per-planet constants ---- */
|
|
2545
|
+
for (let kpl = 0; kpl < fdp.npl; kpl++) {
|
|
2546
|
+
const iplFile = fdp.ipl[kpl];
|
|
2547
|
+
let pdp;
|
|
2548
|
+
if (iplFile >= SE_AST_OFFSET || iplFile >= SE_PLMOON_OFFSET) {
|
|
2549
|
+
pdp = swed.pldat[SEI_ANYBODY];
|
|
2550
|
+
} else {
|
|
2551
|
+
pdp = swed.pldat[iplFile];
|
|
2552
|
+
}
|
|
2553
|
+
pdp.ibdy = iplFile;
|
|
2554
|
+
pdp.lndx0 = reader.readInt32();
|
|
2555
|
+
/* flags: 1 byte → int32 */
|
|
2556
|
+
pdp.iflg = reader.readUint8();
|
|
2557
|
+
/* ncoe: 1 byte → int */
|
|
2558
|
+
pdp.ncoe = reader.readUint8();
|
|
2559
|
+
/* rmax */
|
|
2560
|
+
const lng = reader.readInt32();
|
|
2561
|
+
pdp.rmax = lng / 1000.0;
|
|
2562
|
+
if (iplFile >= SE_PLMOON_OFFSET && iplFile < SE_AST_OFFSET) {
|
|
2563
|
+
if ((iplFile % 100) === 99 || Math.floor((iplFile - 9000) / 100) === SE_MARS) {
|
|
2564
|
+
pdp.rmax = lng / 1000000.0;
|
|
2565
|
+
}
|
|
2566
|
+
}
|
|
2567
|
+
/* 10 doubles: tfstart, tfend, dseg, telem, prot, dprot, qrot, dqrot, peri, dperi */
|
|
2568
|
+
pdp.tfstart = reader.readFloat64();
|
|
2569
|
+
pdp.tfend = reader.readFloat64();
|
|
2570
|
+
pdp.dseg = reader.readFloat64();
|
|
2571
|
+
pdp.nndx = Math.floor((pdp.tfend - pdp.tfstart + 0.1) / pdp.dseg);
|
|
2572
|
+
pdp.telem = reader.readFloat64();
|
|
2573
|
+
pdp.prot = reader.readFloat64();
|
|
2574
|
+
pdp.dprot = reader.readFloat64();
|
|
2575
|
+
pdp.qrot = reader.readFloat64();
|
|
2576
|
+
pdp.dqrot = reader.readFloat64();
|
|
2577
|
+
pdp.peri = reader.readFloat64();
|
|
2578
|
+
pdp.dperi = reader.readFloat64();
|
|
2579
|
+
/* reference ellipse coefficients */
|
|
2580
|
+
if (pdp.iflg & SEI_FLG_ELLIPSE) {
|
|
2581
|
+
pdp.refep = new Float64Array(pdp.ncoe * 2);
|
|
2582
|
+
for (let i = 0; i < pdp.ncoe * 2; i++) {
|
|
2583
|
+
pdp.refep[i] = reader.readFloat64();
|
|
2584
|
+
}
|
|
2585
|
+
pdp.segp = null;
|
|
2586
|
+
}
|
|
2587
|
+
}
|
|
2588
|
+
return { retc: OK, serr };
|
|
2589
|
+
}
|
|
2590
|
+
|
|
2591
|
+
/** Close file and return error */
|
|
2592
|
+
function closeAndError(fdp: FileData, swed: SweData, serr: string): { retc: number; serr: string } {
|
|
2593
|
+
fdp.reader = null;
|
|
2594
|
+
freePlanets(swed);
|
|
2595
|
+
return { retc: ERR, serr };
|
|
2596
|
+
}
|
|
2597
|
+
|
|
2598
|
+
/**
|
|
2599
|
+
* getNewSegment: read and unpack a segment of Chebyshev coefficients.
|
|
2600
|
+
* Translated from sweph.c get_new_segment() (lines 4366-4502).
|
|
2601
|
+
*/
|
|
2602
|
+
function getNewSegment(
|
|
2603
|
+
tjd: number, ipli: number, ifno: number, swed: SweData,
|
|
2604
|
+
): { retc: number; serr: string } {
|
|
2605
|
+
const pdp = swed.pldat[ipli];
|
|
2606
|
+
const fdp = swed.fidat[ifno];
|
|
2607
|
+
const reader = fdp.reader;
|
|
2608
|
+
if (!reader) return { retc: ERR, serr: 'no reader' };
|
|
2609
|
+
/* compute segment number */
|
|
2610
|
+
const iseg = Math.floor((tjd - pdp.tfstart) / pdp.dseg);
|
|
2611
|
+
pdp.tseg0 = pdp.tfstart + iseg * pdp.dseg;
|
|
2612
|
+
pdp.tseg1 = pdp.tseg0 + pdp.dseg;
|
|
2613
|
+
/* get file position of coefficients from segment index */
|
|
2614
|
+
const indexPos = pdp.lndx0 + iseg * 3;
|
|
2615
|
+
reader.seekSet(indexPos);
|
|
2616
|
+
const fpos = reader.readUintN(3);
|
|
2617
|
+
reader.seekSet(fpos);
|
|
2618
|
+
/* allocate/clear coefficient buffer */
|
|
2619
|
+
if (pdp.segp === null) {
|
|
2620
|
+
pdp.segp = new Float64Array(pdp.ncoe * 3);
|
|
2621
|
+
}
|
|
2622
|
+
pdp.segp.fill(0);
|
|
2623
|
+
/* read coefficients for 3 coordinates */
|
|
2624
|
+
for (let icoord = 0; icoord < 3; icoord++) {
|
|
2625
|
+
let idbl = icoord * pdp.ncoe;
|
|
2626
|
+
/* read header: determines packing sizes */
|
|
2627
|
+
const c0 = reader.readUint8();
|
|
2628
|
+
const c1 = reader.readUint8();
|
|
2629
|
+
let nsizes: number;
|
|
2630
|
+
const nsize = [0, 0, 0, 0, 0, 0];
|
|
2631
|
+
let nco: number;
|
|
2632
|
+
if (c0 & 128) {
|
|
2633
|
+
nsizes = 6;
|
|
2634
|
+
const c2 = reader.readUint8();
|
|
2635
|
+
const c3 = reader.readUint8();
|
|
2636
|
+
nsize[0] = Math.floor(c1 / 16);
|
|
2637
|
+
nsize[1] = c1 % 16;
|
|
2638
|
+
nsize[2] = Math.floor(c2 / 16);
|
|
2639
|
+
nsize[3] = c2 % 16;
|
|
2640
|
+
nsize[4] = Math.floor(c3 / 16);
|
|
2641
|
+
nsize[5] = c3 % 16;
|
|
2642
|
+
nco = nsize[0] + nsize[1] + nsize[2] + nsize[3] + nsize[4] + nsize[5];
|
|
2643
|
+
} else {
|
|
2644
|
+
nsizes = 4;
|
|
2645
|
+
nsize[0] = Math.floor(c0 / 16);
|
|
2646
|
+
nsize[1] = c0 % 16;
|
|
2647
|
+
nsize[2] = Math.floor(c1 / 16);
|
|
2648
|
+
nsize[3] = c1 % 16;
|
|
2649
|
+
nco = nsize[0] + nsize[1] + nsize[2] + nsize[3];
|
|
2650
|
+
}
|
|
2651
|
+
if (nco > pdp.ncoe) {
|
|
2652
|
+
pdp.segp = null;
|
|
2653
|
+
return { retc: ERR, serr: `error in ephemeris file ${fdp.fnam}: ${nco} coefficients instead of ${pdp.ncoe}.` };
|
|
2654
|
+
}
|
|
2655
|
+
/* unpack coefficients */
|
|
2656
|
+
for (let i = 0; i < nsizes; i++) {
|
|
2657
|
+
if (nsize[i] === 0) continue;
|
|
2658
|
+
if (i < 4) {
|
|
2659
|
+
/* size groups 0..3: (4-i) bytes per coefficient */
|
|
2660
|
+
const bytesPerCoef = 4 - i;
|
|
2661
|
+
const count = nsize[i];
|
|
2662
|
+
for (let m = 0; m < count; m++, idbl++) {
|
|
2663
|
+
const val = reader.readUintN(bytesPerCoef);
|
|
2664
|
+
if (val & 1) {
|
|
2665
|
+
pdp.segp[idbl] = -(((val + 1) / 2) / 1e+9 * pdp.rmax / 2);
|
|
2666
|
+
} else {
|
|
2667
|
+
pdp.segp[idbl] = (val / 2) / 1e+9 * pdp.rmax / 2;
|
|
2668
|
+
}
|
|
2669
|
+
}
|
|
2670
|
+
} else if (i === 4) {
|
|
2671
|
+
/* half-byte packing: 2 coefficients per byte */
|
|
2672
|
+
const count = nsize[i];
|
|
2673
|
+
const nBytes = Math.floor((count + 1) / 2);
|
|
2674
|
+
let j = 0;
|
|
2675
|
+
for (let m = 0; m < nBytes && j < count; m++) {
|
|
2676
|
+
let val = reader.readUint8();
|
|
2677
|
+
for (let n = 0, o = 16; n < 2 && j < count; n++, j++, idbl++) {
|
|
2678
|
+
if (val & o) {
|
|
2679
|
+
pdp.segp[idbl] = -(((val + o) / o / 2) * pdp.rmax / 2 / 1e+9);
|
|
2680
|
+
} else {
|
|
2681
|
+
pdp.segp[idbl] = (val / o / 2) * pdp.rmax / 2 / 1e+9;
|
|
2682
|
+
}
|
|
2683
|
+
val = val % o;
|
|
2684
|
+
o = Math.floor(o / 16);
|
|
2685
|
+
}
|
|
2686
|
+
}
|
|
2687
|
+
} else if (i === 5) {
|
|
2688
|
+
/* quarter-byte packing: 4 coefficients per byte */
|
|
2689
|
+
const count = nsize[i];
|
|
2690
|
+
const nBytes = Math.floor((count + 3) / 4);
|
|
2691
|
+
let j = 0;
|
|
2692
|
+
for (let m = 0; m < nBytes && j < count; m++) {
|
|
2693
|
+
let val = reader.readUint8();
|
|
2694
|
+
for (let n = 0, o = 64; n < 4 && j < count; n++, j++, idbl++) {
|
|
2695
|
+
if (val & o) {
|
|
2696
|
+
pdp.segp[idbl] = -(((val + o) / o / 2) * pdp.rmax / 2 / 1e+9);
|
|
2697
|
+
} else {
|
|
2698
|
+
pdp.segp[idbl] = (val / o / 2) * pdp.rmax / 2 / 1e+9;
|
|
2699
|
+
}
|
|
2700
|
+
val = val % o;
|
|
2701
|
+
o = Math.floor(o / 4);
|
|
2702
|
+
}
|
|
2703
|
+
}
|
|
2704
|
+
}
|
|
2705
|
+
}
|
|
2706
|
+
}
|
|
2707
|
+
return { retc: OK, serr: '' };
|
|
2708
|
+
}
|
|
2709
|
+
|
|
2710
|
+
/**
|
|
2711
|
+
* rotBack: rotate Chebyshev coefficients from orbital plane back to equatorial J2000.
|
|
2712
|
+
* Translated from sweph.c rot_back() (lines 4962-5053).
|
|
2713
|
+
*/
|
|
2714
|
+
function rotBack(ipli: number, swed: SweData): void {
|
|
2715
|
+
const seps2000 = 0.39777715572793088; // sin(J2000 obliquity)
|
|
2716
|
+
const ceps2000 = 0.91748206215761929; // cos(J2000 obliquity)
|
|
2717
|
+
const pdp = swed.pldat[ipli];
|
|
2718
|
+
const nco = pdp.ncoe;
|
|
2719
|
+
const segp = pdp.segp!;
|
|
2720
|
+
const t = pdp.tseg0 + pdp.dseg / 2;
|
|
2721
|
+
const tdiff = (t - pdp.telem) / 365250.0;
|
|
2722
|
+
let qav: number, pav: number;
|
|
2723
|
+
if (ipli === SEI_MOON) {
|
|
2724
|
+
let dn = pdp.prot + tdiff * pdp.dprot;
|
|
2725
|
+
const nWrap = Math.floor(dn / TWOPI);
|
|
2726
|
+
dn -= nWrap * TWOPI;
|
|
2727
|
+
qav = (pdp.qrot + tdiff * pdp.dqrot) * Math.cos(dn);
|
|
2728
|
+
pav = (pdp.qrot + tdiff * pdp.dqrot) * Math.sin(dn);
|
|
2729
|
+
} else {
|
|
2730
|
+
qav = pdp.qrot + tdiff * pdp.dqrot;
|
|
2731
|
+
pav = pdp.prot + tdiff * pdp.dprot;
|
|
2732
|
+
}
|
|
2733
|
+
/* Copy coefficients to local array x[i][3] */
|
|
2734
|
+
const x: number[][] = [];
|
|
2735
|
+
for (let i = 0; i < nco; i++) {
|
|
2736
|
+
x.push([segp[i], segp[nco + i], segp[2 * nco + i]]);
|
|
2737
|
+
}
|
|
2738
|
+
/* Add reference ellipse if flagged */
|
|
2739
|
+
if (pdp.iflg & SEI_FLG_ELLIPSE) {
|
|
2740
|
+
const refep = pdp.refep!;
|
|
2741
|
+
let omtild = pdp.peri + tdiff * pdp.dperi;
|
|
2742
|
+
const nWrap = Math.floor(omtild / TWOPI);
|
|
2743
|
+
omtild -= nWrap * TWOPI;
|
|
2744
|
+
const com = Math.cos(omtild);
|
|
2745
|
+
const som = Math.sin(omtild);
|
|
2746
|
+
for (let i = 0; i < nco; i++) {
|
|
2747
|
+
x[i][0] = segp[i] + com * refep[i] - som * refep[nco + i];
|
|
2748
|
+
x[i][1] = segp[nco + i] + com * refep[nco + i] + som * refep[i];
|
|
2749
|
+
}
|
|
2750
|
+
}
|
|
2751
|
+
/* Construct right-handed orthonormal system (equinoctal variables) */
|
|
2752
|
+
const cosih2 = 1.0 / (1.0 + qav * qav + pav * pav);
|
|
2753
|
+
/* orbit pole */
|
|
2754
|
+
const uiz = [2.0 * pav * cosih2, -2.0 * qav * cosih2, (1.0 - qav * qav - pav * pav) * cosih2];
|
|
2755
|
+
/* origin of longitudes */
|
|
2756
|
+
const uix = [(1.0 + qav * qav - pav * pav) * cosih2, 2.0 * qav * pav * cosih2, -2.0 * pav * cosih2];
|
|
2757
|
+
/* orthogonal in orbital plane */
|
|
2758
|
+
const uiy = [2.0 * qav * pav * cosih2, (1.0 - qav * qav + pav * pav) * cosih2, 2.0 * qav * cosih2];
|
|
2759
|
+
/* Rotate to actual orientation in space */
|
|
2760
|
+
pdp.neval = pdp.ncoe;
|
|
2761
|
+
for (let i = 0; i < nco; i++) {
|
|
2762
|
+
const xrot = x[i][0] * uix[0] + x[i][1] * uiy[0] + x[i][2] * uiz[0];
|
|
2763
|
+
let yrot = x[i][0] * uix[1] + x[i][1] * uiy[1] + x[i][2] * uiz[1];
|
|
2764
|
+
let zrot = x[i][0] * uix[2] + x[i][1] * uiy[2] + x[i][2] * uiz[2];
|
|
2765
|
+
if (Math.abs(xrot) + Math.abs(yrot) + Math.abs(zrot) >= 1e-14) {
|
|
2766
|
+
pdp.neval = i;
|
|
2767
|
+
}
|
|
2768
|
+
x[i][0] = xrot;
|
|
2769
|
+
x[i][1] = yrot;
|
|
2770
|
+
x[i][2] = zrot;
|
|
2771
|
+
if (ipli === SEI_MOON) {
|
|
2772
|
+
/* rotate from ecliptic to J2000 equator */
|
|
2773
|
+
x[i][1] = ceps2000 * yrot - seps2000 * zrot;
|
|
2774
|
+
x[i][2] = seps2000 * yrot + ceps2000 * zrot;
|
|
2775
|
+
}
|
|
2776
|
+
}
|
|
2777
|
+
pdp.neval++;
|
|
2778
|
+
/* Write back */
|
|
2779
|
+
for (let i = 0; i < nco; i++) {
|
|
2780
|
+
segp[i] = x[i][0];
|
|
2781
|
+
segp[nco + i] = x[i][1];
|
|
2782
|
+
segp[2 * nco + i] = x[i][2];
|
|
2783
|
+
}
|
|
2784
|
+
}
|
|
2785
|
+
|
|
2786
|
+
/**
|
|
2787
|
+
* sweph: core SE1 file reader. Opens file if needed, reads segment, evaluates Chebyshev.
|
|
2788
|
+
* Translated from sweph.c sweph() (lines 2124-2357).
|
|
2789
|
+
*/
|
|
2790
|
+
function sweph(
|
|
2791
|
+
swed: SweData, tjd: number, ipli: number, ifno: number,
|
|
2792
|
+
iflag: number, xsunb: Float64Array | number[] | null,
|
|
2793
|
+
doSave: boolean,
|
|
2794
|
+
xpret: Float64Array | null,
|
|
2795
|
+
): { retc: number; serr: string } {
|
|
2796
|
+
let ipl = ipli;
|
|
2797
|
+
if (ipli > SE_AST_OFFSET) ipl = SEI_ANYBODY;
|
|
2798
|
+
if (ipli > SE_PLMOON_OFFSET && ipli < SE_AST_OFFSET) ipl = SEI_ANYBODY;
|
|
2799
|
+
const pdp = swed.pldat[ipl];
|
|
2800
|
+
const pedp = swed.pldat[SEI_EARTH];
|
|
2801
|
+
const psdp = swed.pldat[SEI_SUNBARY];
|
|
2802
|
+
const fdp = swed.fidat[ifno];
|
|
2803
|
+
const xp = doSave ? pdp.x : new Float64Array(6);
|
|
2804
|
+
/* ---- cache check ---- */
|
|
2805
|
+
const speedf1 = pdp.xflgs & SEFLG_SPEED;
|
|
2806
|
+
const speedf2 = iflag & SEFLG_SPEED;
|
|
2807
|
+
if (tjd === pdp.teval
|
|
2808
|
+
&& pdp.iephe === SEFLG_SWIEPH
|
|
2809
|
+
&& (!speedf2 || speedf1)
|
|
2810
|
+
&& ipl < SEI_ANYBODY) {
|
|
2811
|
+
if (xpret !== null) {
|
|
2812
|
+
for (let i = 0; i <= 5; i++) xpret[i] = pdp.x[i];
|
|
2813
|
+
}
|
|
2814
|
+
return { retc: OK, serr: '' };
|
|
2815
|
+
}
|
|
2816
|
+
/* ---- get correct ephemeris file ---- */
|
|
2817
|
+
if (fdp.reader !== null) {
|
|
2818
|
+
/* if tjd is beyond file range, or new body needed, close old file */
|
|
2819
|
+
if (tjd < fdp.tfstart || tjd > fdp.tfend
|
|
2820
|
+
|| (ipl === SEI_ANYBODY && ipli !== pdp.ibdy)) {
|
|
2821
|
+
fdp.reader = null;
|
|
2822
|
+
pdp.refep = null;
|
|
2823
|
+
pdp.segp = null;
|
|
2824
|
+
}
|
|
2825
|
+
}
|
|
2826
|
+
if (fdp.reader === null) {
|
|
2827
|
+
const fname = swiGenFilename(tjd, ipli);
|
|
2828
|
+
const buffer = findEphemerisFile(fname, swed);
|
|
2829
|
+
if (!buffer) {
|
|
2830
|
+
return { retc: NOT_AVAILABLE, serr: '' };
|
|
2831
|
+
}
|
|
2832
|
+
fdp.reader = new SE1FileReader(buffer);
|
|
2833
|
+
fdp.fnam = fname.replace(/^.*\//, ''); // basename
|
|
2834
|
+
const rc = readConst(ifno, swed);
|
|
2835
|
+
if (rc.retc !== OK) return rc;
|
|
2836
|
+
}
|
|
2837
|
+
/* ---- range check ---- */
|
|
2838
|
+
if (tjd < fdp.tfstart || tjd > fdp.tfend) {
|
|
2839
|
+
let msg: string;
|
|
2840
|
+
if (ipli > SE_AST_OFFSET) {
|
|
2841
|
+
msg = `asteroid No. ${ipli - SE_AST_OFFSET} (${fdp.fnam}): `;
|
|
2842
|
+
} else if (ipli !== SEI_MOON) {
|
|
2843
|
+
msg = `planets eph. file (${fdp.fnam}): `;
|
|
2844
|
+
} else {
|
|
2845
|
+
msg = `moon eph. file (${fdp.fnam}): `;
|
|
2846
|
+
}
|
|
2847
|
+
if (tjd < fdp.tfstart) {
|
|
2848
|
+
msg += `jd ${tjd} < lower limit ${fdp.tfstart};`;
|
|
2849
|
+
} else {
|
|
2850
|
+
msg += `jd ${tjd} > upper limit ${fdp.tfend};`;
|
|
2851
|
+
}
|
|
2852
|
+
return { retc: NOT_AVAILABLE, serr: msg };
|
|
2853
|
+
}
|
|
2854
|
+
/* ---- get planet position ---- */
|
|
2855
|
+
if (pdp.segp === null || tjd < pdp.tseg0 || tjd > pdp.tseg1) {
|
|
2856
|
+
const rc = getNewSegment(tjd, ipl, ifno, swed);
|
|
2857
|
+
if (rc.retc !== OK) return rc;
|
|
2858
|
+
if (pdp.iflg & SEI_FLG_ROTATE) {
|
|
2859
|
+
rotBack(ipl, swed);
|
|
2860
|
+
} else {
|
|
2861
|
+
pdp.neval = pdp.ncoe;
|
|
2862
|
+
}
|
|
2863
|
+
}
|
|
2864
|
+
/* evaluate Chebyshev polynomial */
|
|
2865
|
+
let t = (tjd - pdp.tseg0) / pdp.dseg;
|
|
2866
|
+
t = t * 2 - 1;
|
|
2867
|
+
const needSpeed = doSave || !!(iflag & SEFLG_SPEED);
|
|
2868
|
+
const segp = pdp.segp!;
|
|
2869
|
+
for (let i = 0; i <= 2; i++) {
|
|
2870
|
+
xp[i] = swiEcheb(t, segp.subarray(i * pdp.ncoe), pdp.neval);
|
|
2871
|
+
if (needSpeed) {
|
|
2872
|
+
xp[i + 3] = swiEdcheb(t, segp.subarray(i * pdp.ncoe), pdp.neval) / pdp.dseg * 2;
|
|
2873
|
+
} else {
|
|
2874
|
+
xp[i + 3] = 0;
|
|
2875
|
+
}
|
|
2876
|
+
}
|
|
2877
|
+
/* ---- barycentric Sun special case ---- */
|
|
2878
|
+
if (ipl === SEI_SUNBARY && (pdp.iflg & SEI_FLG_EMBHEL)) {
|
|
2879
|
+
/* File has heliocentric Earth, not barycentric Sun.
|
|
2880
|
+
* Compute: sunBary = EMB - helioEarth */
|
|
2881
|
+
const tsv = pedp.teval;
|
|
2882
|
+
pedp.teval = 0; // force recomputation
|
|
2883
|
+
const xemb = new Float64Array(6);
|
|
2884
|
+
const rc = sweph(swed, tjd, SEI_EMB, ifno, iflag | SEFLG_SPEED, null, NO_SAVE, xemb);
|
|
2885
|
+
if (rc.retc !== OK) return rc;
|
|
2886
|
+
pedp.teval = tsv;
|
|
2887
|
+
for (let i = 0; i <= 2; i++) xp[i] = xemb[i] - xp[i];
|
|
2888
|
+
if (needSpeed) {
|
|
2889
|
+
for (let i = 3; i <= 5; i++) xp[i] = xemb[i] - xp[i];
|
|
2890
|
+
}
|
|
2891
|
+
}
|
|
2892
|
+
/* ---- asteroid helio → bary conversion ---- */
|
|
2893
|
+
if (xsunb !== null && (iflag & SEFLG_SWIEPH)) {
|
|
2894
|
+
if (ipl >= SEI_ANYBODY) {
|
|
2895
|
+
for (let i = 0; i <= 2; i++) xp[i] += xsunb[i];
|
|
2896
|
+
if (needSpeed) {
|
|
2897
|
+
for (let i = 3; i <= 5; i++) xp[i] += xsunb[i];
|
|
2898
|
+
}
|
|
2899
|
+
}
|
|
2900
|
+
}
|
|
2901
|
+
/* ---- save results ---- */
|
|
2902
|
+
if (doSave) {
|
|
2903
|
+
pdp.teval = tjd;
|
|
2904
|
+
pdp.xflgs = -1; // force new light-time computation
|
|
2905
|
+
if (ifno === SEI_FILE_PLANET || ifno === SEI_FILE_MOON) {
|
|
2906
|
+
pdp.iephe = SEFLG_SWIEPH;
|
|
2907
|
+
} else {
|
|
2908
|
+
pdp.iephe = psdp.iephe;
|
|
2909
|
+
}
|
|
2910
|
+
}
|
|
2911
|
+
if (xpret !== null) {
|
|
2912
|
+
for (let i = 0; i <= 5; i++) xpret[i] = xp[i];
|
|
2913
|
+
}
|
|
2914
|
+
return { retc: OK, serr: '' };
|
|
2915
|
+
}
|
|
2916
|
+
|
|
2917
|
+
/**
|
|
2918
|
+
* swemoonSwieph: compute geocentric Moon from SE1 file.
|
|
2919
|
+
* Translated from sweph.c swemoon() (lines 1759-1793).
|
|
2920
|
+
*/
|
|
2921
|
+
function swemoonSwieph(
|
|
2922
|
+
swed: SweData, tjd: number, iflag: number,
|
|
2923
|
+
doSave: boolean, xpret: Float64Array | null,
|
|
2924
|
+
): { retc: number; serr: string } {
|
|
2925
|
+
const pdp = swed.pldat[SEI_MOON];
|
|
2926
|
+
const xp = doSave ? pdp.x : new Float64Array(6);
|
|
2927
|
+
/* cache check */
|
|
2928
|
+
const speedf1 = pdp.xflgs & SEFLG_SPEED;
|
|
2929
|
+
const speedf2 = iflag & SEFLG_SPEED;
|
|
2930
|
+
if (tjd === pdp.teval
|
|
2931
|
+
&& pdp.iephe === SEFLG_SWIEPH
|
|
2932
|
+
&& (!speedf2 || speedf1)) {
|
|
2933
|
+
/* already computed */
|
|
2934
|
+
} else {
|
|
2935
|
+
const rc = sweph(swed, tjd, SEI_MOON, SEI_FILE_MOON, iflag, null, doSave, xp);
|
|
2936
|
+
if (rc.retc !== OK) return rc;
|
|
2937
|
+
if (doSave) {
|
|
2938
|
+
pdp.teval = tjd;
|
|
2939
|
+
pdp.xflgs = -1;
|
|
2940
|
+
pdp.iephe = SEFLG_SWIEPH;
|
|
2941
|
+
}
|
|
2942
|
+
}
|
|
2943
|
+
if (xpret !== null) {
|
|
2944
|
+
for (let i = 0; i <= 5; i++) xpret[i] = pdp.x[i];
|
|
2945
|
+
}
|
|
2946
|
+
return { retc: OK, serr: '' };
|
|
2947
|
+
}
|
|
2948
|
+
|
|
2949
|
+
/**
|
|
2950
|
+
* sweplanSwieph: compute planet positions from SE1 files (Swiss Ephemeris).
|
|
2951
|
+
* Computes barycentric Sun, Moon, Earth, and target planet.
|
|
2952
|
+
* Translated from sweph.c sweplan() (lines 1819-1967).
|
|
2953
|
+
*/
|
|
2954
|
+
function sweplanSwieph(
|
|
2955
|
+
swed: SweData, tjd: number, ipli: number, ifno: number, iflag: number,
|
|
2956
|
+
doSave: boolean,
|
|
2957
|
+
xpret: Float64Array | null,
|
|
2958
|
+
xperet: Float64Array | null,
|
|
2959
|
+
xpsret: Float64Array | null,
|
|
2960
|
+
xpmret: Float64Array | null,
|
|
2961
|
+
): { retc: number; serr: string } {
|
|
2962
|
+
const pdp = swed.pldat[ipli];
|
|
2963
|
+
const pebdp = swed.pldat[SEI_EMB];
|
|
2964
|
+
const psbdp = swed.pldat[SEI_SUNBARY];
|
|
2965
|
+
const pmdp = swed.pldat[SEI_MOON];
|
|
2966
|
+
let doEarth = false, doMoon = false, doSunBary = false;
|
|
2967
|
+
const xxp = new Float64Array(6);
|
|
2968
|
+
const xxm = new Float64Array(6);
|
|
2969
|
+
const xxs = new Float64Array(6);
|
|
2970
|
+
const xxe = new Float64Array(6);
|
|
2971
|
+
/* Determine what needs computing */
|
|
2972
|
+
if (doSave || ipli === SEI_SUNBARY || (pdp.iflg & SEI_FLG_HELIO)
|
|
2973
|
+
|| xpsret !== null || (iflag & SEFLG_HELCTR))
|
|
2974
|
+
doSunBary = true;
|
|
2975
|
+
if (doSave || ipli === SEI_EARTH || xperet !== null)
|
|
2976
|
+
doEarth = true;
|
|
2977
|
+
if (ipli === SEI_MOON) {
|
|
2978
|
+
doEarth = true;
|
|
2979
|
+
doSunBary = true;
|
|
2980
|
+
}
|
|
2981
|
+
if (doSave || ipli === SEI_MOON || ipli === SEI_EARTH || xperet !== null || xpmret !== null)
|
|
2982
|
+
doMoon = true;
|
|
2983
|
+
const xp = doSave ? pdp.x : xxp;
|
|
2984
|
+
const xpe = doSave ? pebdp.x : xxe;
|
|
2985
|
+
const xps = doSave ? psbdp.x : xxs;
|
|
2986
|
+
const xpm = doSave ? pmdp.x : xxm;
|
|
2987
|
+
const speedf2 = iflag & SEFLG_SPEED;
|
|
2988
|
+
/* ---- barycentric Sun ---- */
|
|
2989
|
+
if (doSunBary) {
|
|
2990
|
+
const speedf1 = psbdp.xflgs & SEFLG_SPEED;
|
|
2991
|
+
if (tjd === psbdp.teval && psbdp.iephe === SEFLG_SWIEPH && (!speedf2 || speedf1)) {
|
|
2992
|
+
for (let i = 0; i <= 5; i++) xps[i] = psbdp.x[i];
|
|
2993
|
+
} else {
|
|
2994
|
+
const rc = sweph(swed, tjd, SEI_SUNBARY, SEI_FILE_PLANET, iflag, null, doSave, xps);
|
|
2995
|
+
if (rc.retc !== OK) return rc;
|
|
2996
|
+
}
|
|
2997
|
+
if (xpsret !== null) for (let i = 0; i <= 5; i++) xpsret[i] = xps[i];
|
|
2998
|
+
}
|
|
2999
|
+
/* ---- Moon ---- */
|
|
3000
|
+
if (doMoon) {
|
|
3001
|
+
const speedf1 = pmdp.xflgs & SEFLG_SPEED;
|
|
3002
|
+
if (tjd === pmdp.teval && pmdp.iephe === SEFLG_SWIEPH && (!speedf2 || speedf1)) {
|
|
3003
|
+
for (let i = 0; i <= 5; i++) xpm[i] = pmdp.x[i];
|
|
3004
|
+
} else {
|
|
3005
|
+
const rc = sweph(swed, tjd, SEI_MOON, SEI_FILE_MOON, iflag, null, doSave, xpm);
|
|
3006
|
+
if (rc.retc === ERR) return rc;
|
|
3007
|
+
/* If moon file not available, fall back to Moshier moon */
|
|
3008
|
+
if (swed.fidat[SEI_FILE_MOON].reader === null) {
|
|
3009
|
+
const serrArr: string[] = [''];
|
|
3010
|
+
const retc = swiMoshmoon(swed, tjd, doSave, xpm, serrArr);
|
|
3011
|
+
if (retc !== OK) return { retc, serr: serrArr[0] };
|
|
3012
|
+
}
|
|
3013
|
+
}
|
|
3014
|
+
if (xpmret !== null) for (let i = 0; i <= 5; i++) xpmret[i] = xpm[i];
|
|
3015
|
+
}
|
|
3016
|
+
/* ---- barycentric Earth ---- */
|
|
3017
|
+
if (doEarth) {
|
|
3018
|
+
const speedf1 = pebdp.xflgs & SEFLG_SPEED;
|
|
3019
|
+
if (tjd === pebdp.teval && pebdp.iephe === SEFLG_SWIEPH && (!speedf2 || speedf1)) {
|
|
3020
|
+
for (let i = 0; i <= 5; i++) xpe[i] = pebdp.x[i];
|
|
3021
|
+
} else {
|
|
3022
|
+
const rc = sweph(swed, tjd, SEI_EMB, SEI_FILE_PLANET, iflag, null, doSave, xpe);
|
|
3023
|
+
if (rc.retc !== OK) return rc;
|
|
3024
|
+
/* Earth from EMB and Moon */
|
|
3025
|
+
embofs(xpe, xpm);
|
|
3026
|
+
if (xpe === pebdp.x || (iflag & SEFLG_SPEED)) {
|
|
3027
|
+
const xpeSpeed = new Float64Array(xpe.buffer, xpe.byteOffset + 24, 3);
|
|
3028
|
+
const xpmSpeed = new Float64Array(xpm.buffer, xpm.byteOffset + 24, 3);
|
|
3029
|
+
embofs(xpeSpeed, xpmSpeed);
|
|
3030
|
+
}
|
|
3031
|
+
}
|
|
3032
|
+
if (xperet !== null) for (let i = 0; i <= 5; i++) xperet[i] = xpe[i];
|
|
3033
|
+
}
|
|
3034
|
+
/* ---- target planet ---- */
|
|
3035
|
+
if (ipli === SEI_MOON) {
|
|
3036
|
+
for (let i = 0; i <= 5; i++) xp[i] = xpm[i];
|
|
3037
|
+
} else if (ipli === SEI_EARTH) {
|
|
3038
|
+
for (let i = 0; i <= 5; i++) xp[i] = xpe[i];
|
|
3039
|
+
} else if (ipli === SEI_SUN || ipli === SEI_SUNBARY) {
|
|
3040
|
+
for (let i = 0; i <= 5; i++) xp[i] = xps[i];
|
|
3041
|
+
} else {
|
|
3042
|
+
/* planet */
|
|
3043
|
+
const speedf1 = pdp.xflgs & SEFLG_SPEED;
|
|
3044
|
+
if (tjd === pdp.teval && pdp.iephe === SEFLG_SWIEPH && (!speedf2 || speedf1)) {
|
|
3045
|
+
for (let i = 0; i <= 5; i++) xp[i] = pdp.x[i];
|
|
3046
|
+
} else {
|
|
3047
|
+
const rc = sweph(swed, tjd, ipli, ifno, iflag, null, doSave, xp);
|
|
3048
|
+
if (rc.retc !== OK) return rc;
|
|
3049
|
+
/* If planet is heliocentric in file, convert to barycentric */
|
|
3050
|
+
if (pdp.iflg & SEI_FLG_HELIO) {
|
|
3051
|
+
for (let i = 0; i <= 2; i++) xp[i] += xps[i];
|
|
3052
|
+
if (doSave || (iflag & SEFLG_SPEED))
|
|
3053
|
+
for (let i = 3; i <= 5; i++) xp[i] += xps[i];
|
|
3054
|
+
}
|
|
3055
|
+
}
|
|
3056
|
+
}
|
|
3057
|
+
if (xpret !== null) for (let i = 0; i <= 5; i++) xpret[i] = xp[i];
|
|
3058
|
+
return { retc: OK, serr: '' };
|
|
3059
|
+
}
|
|
3060
|
+
|
|
3061
|
+
/* ================================================================
|
|
3062
|
+
* Part 8: Fixed stars (builtin catalog for ayanamsa)
|
|
3063
|
+
* ================================================================ */
|
|
3064
|
+
|
|
3065
|
+
/** Parsed fixed star data */
|
|
3066
|
+
interface FixedStar {
|
|
3067
|
+
starname: string;
|
|
3068
|
+
starbayer: string;
|
|
3069
|
+
epoch: number; // 0=ICRS, 1950=B1950, 2000=J2000
|
|
3070
|
+
ra: number; // radians
|
|
3071
|
+
de: number; // radians
|
|
3072
|
+
ramot: number; // RA proper motion, radians/century
|
|
3073
|
+
demot: number; // Dec proper motion, radians/century
|
|
3074
|
+
radvel: number; // radial velocity, AU/century
|
|
3075
|
+
parall: number; // parallax, radians
|
|
3076
|
+
mag: number; // visual magnitude
|
|
3077
|
+
}
|
|
3078
|
+
|
|
3079
|
+
/** Builtin star records — required for star-based ayanamsas */
|
|
3080
|
+
const BUILTIN_STARS: Array<{ names: string[]; record: string }> = [
|
|
3081
|
+
{ names: ['spica', 'Spica'],
|
|
3082
|
+
record: 'Spica,alVir,ICRS,13,25,11.57937,-11,09,40.7501,-42.35,-30.67,1,13.06,0.97,-10,3672' },
|
|
3083
|
+
{ names: [',zePsc', 'revati', 'Revati'],
|
|
3084
|
+
record: 'Revati,zePsc,ICRS,01,13,43.88735,+07,34,31.2745,145,-55.69,15,18.76,5.187,06,174' },
|
|
3085
|
+
{ names: [',deCnc', 'pushya', 'Pushya'],
|
|
3086
|
+
record: 'Pushya,deCnc,ICRS,08,44,41.09921,+18,09,15.5034,-17.67,-229.26,17.14,24.98,3.94,18,2027' },
|
|
3087
|
+
{ names: [',laSco', 'mula', 'Mula'],
|
|
3088
|
+
record: 'Mula,laSco,ICRS,17,33,36.52012,-37,06,13.7648,-8.53,-30.8,-3,5.71,1.62,-37,11673' },
|
|
3089
|
+
{ names: [',SgrA*'],
|
|
3090
|
+
record: 'Gal. Center,SgrA*,2000,17,45,40.03599,-29,00,28.1699,-2.755718425,-5.547,0.0,0.125,999.99,0,0' },
|
|
3091
|
+
{ names: [',GP1958'],
|
|
3092
|
+
record: 'Gal. Pole IAU1958,GP1958,1950,12,49,0.0,27,24,0.0,0.0,0.0,0.0,0.0,0.0,0,0' },
|
|
3093
|
+
{ names: [',GPol'],
|
|
3094
|
+
record: 'Gal. Pole,GPol,ICRS,12,51,36.7151981,27,06,11.193172,0.0,0.0,0.0,0.0,0.0,0,0' },
|
|
3095
|
+
];
|
|
3096
|
+
|
|
3097
|
+
/** Parse a comma-separated star record string into FixedStar data */
|
|
3098
|
+
function fixstarCutString(srecord: string): { star: FixedStar; starOut: string; serr: string } | null {
|
|
3099
|
+
const cpos = srecord.split(',');
|
|
3100
|
+
if (cpos.length < 14) {
|
|
3101
|
+
return null;
|
|
3102
|
+
}
|
|
3103
|
+
const starname = cpos[0].trim();
|
|
3104
|
+
const starbayer = cpos[1].trim();
|
|
3105
|
+
const starOut = starname ? `${starname},${starbayer}` : starbayer;
|
|
3106
|
+
const epochStr = cpos[2].trim();
|
|
3107
|
+
let epoch: number;
|
|
3108
|
+
if (epochStr === 'ICRS' || epochStr === '0') {
|
|
3109
|
+
epoch = 0;
|
|
3110
|
+
} else {
|
|
3111
|
+
epoch = parseFloat(epochStr);
|
|
3112
|
+
}
|
|
3113
|
+
const ra_h = parseFloat(cpos[3]);
|
|
3114
|
+
const ra_m = parseFloat(cpos[4]);
|
|
3115
|
+
const ra_s = parseFloat(cpos[5]);
|
|
3116
|
+
const de_d = parseFloat(cpos[6]);
|
|
3117
|
+
const sde_d = cpos[6];
|
|
3118
|
+
const de_m = parseFloat(cpos[7]);
|
|
3119
|
+
const de_s = parseFloat(cpos[8]);
|
|
3120
|
+
let ra_pm = parseFloat(cpos[9]);
|
|
3121
|
+
let de_pm = parseFloat(cpos[10]);
|
|
3122
|
+
let radv = parseFloat(cpos[11]);
|
|
3123
|
+
let parall = parseFloat(cpos[12]);
|
|
3124
|
+
if (parall < 0) parall = -parall;
|
|
3125
|
+
const mag = parseFloat(cpos[13]);
|
|
3126
|
+
/* RA and Dec in degrees */
|
|
3127
|
+
let ra = (ra_s / 3600.0 + ra_m / 60.0 + ra_h) * 15.0;
|
|
3128
|
+
let de: number;
|
|
3129
|
+
if (!sde_d.includes('-')) {
|
|
3130
|
+
de = de_s / 3600.0 + de_m / 60.0 + de_d;
|
|
3131
|
+
} else {
|
|
3132
|
+
de = -de_s / 3600.0 - de_m / 60.0 + de_d;
|
|
3133
|
+
}
|
|
3134
|
+
/* proper motion in degrees/century (new format: units of 0.1 arcsec) */
|
|
3135
|
+
ra_pm = ra_pm / 10.0 / 3600.0;
|
|
3136
|
+
de_pm = de_pm / 10.0 / 3600.0;
|
|
3137
|
+
parall /= 1000.0;
|
|
3138
|
+
/* parallax in degrees */
|
|
3139
|
+
if (parall > 1) {
|
|
3140
|
+
parall = 1 / parall / 3600.0;
|
|
3141
|
+
} else {
|
|
3142
|
+
parall /= 3600;
|
|
3143
|
+
}
|
|
3144
|
+
/* radial velocity in AU per century */
|
|
3145
|
+
radv *= KM_S_TO_AU_CTY;
|
|
3146
|
+
/* convert to radians */
|
|
3147
|
+
ra *= DEGTORAD;
|
|
3148
|
+
de *= DEGTORAD;
|
|
3149
|
+
ra_pm *= DEGTORAD;
|
|
3150
|
+
de_pm *= DEGTORAD;
|
|
3151
|
+
ra_pm /= Math.cos(de); // catalogues give proper motion in RA as great circle
|
|
3152
|
+
parall *= DEGTORAD;
|
|
3153
|
+
return {
|
|
3154
|
+
star: { starname, starbayer, epoch, ra, de, ramot: ra_pm, demot: de_pm, radvel: radv, parall, mag },
|
|
3155
|
+
starOut,
|
|
3156
|
+
serr: '',
|
|
3157
|
+
};
|
|
3158
|
+
}
|
|
3159
|
+
|
|
3160
|
+
/** Look up a star name in the builtin catalog */
|
|
3161
|
+
function getBuiltinStar(star: string): { record: string; sstar: string } | null {
|
|
3162
|
+
for (const entry of BUILTIN_STARS) {
|
|
3163
|
+
for (const name of entry.names) {
|
|
3164
|
+
if (name.startsWith(',')) {
|
|
3165
|
+
// Bayer designation: check if star contains it
|
|
3166
|
+
if (star.includes(name)) {
|
|
3167
|
+
return { record: entry.record, sstar: name };
|
|
3168
|
+
}
|
|
3169
|
+
} else {
|
|
3170
|
+
// Traditional name: check prefix match (case insensitive)
|
|
3171
|
+
if (star.toLowerCase().startsWith(name.toLowerCase())) {
|
|
3172
|
+
return { record: entry.record, sstar: name.toLowerCase() };
|
|
3173
|
+
}
|
|
3174
|
+
}
|
|
3175
|
+
}
|
|
3176
|
+
}
|
|
3177
|
+
return null;
|
|
3178
|
+
}
|
|
3179
|
+
|
|
3180
|
+
/** ARMC to MC conversion (from swehouse.c) */
|
|
3181
|
+
export function swiArmcToMc(armc: number, eps: number): number {
|
|
3182
|
+
const VERY_SMALL = 1e-10;
|
|
3183
|
+
if (Math.abs(armc - 90) > VERY_SMALL && Math.abs(armc - 270) > VERY_SMALL) {
|
|
3184
|
+
const tant = Math.tan(armc * DEGTORAD);
|
|
3185
|
+
let mc = Math.atan(tant / Math.cos(eps * DEGTORAD)) * RADTODEG;
|
|
3186
|
+
if (armc > 90 && armc <= 270) {
|
|
3187
|
+
mc = sweDegnorm(mc + 180);
|
|
3188
|
+
}
|
|
3189
|
+
return mc;
|
|
3190
|
+
} else {
|
|
3191
|
+
return Math.abs(armc - 90) <= VERY_SMALL ? 90 : 270;
|
|
3192
|
+
}
|
|
3193
|
+
}
|
|
3194
|
+
|
|
3195
|
+
/** Calculate fixed star position from parsed star data */
|
|
3196
|
+
function fixstarCalcFromStruct(
|
|
3197
|
+
stardata: FixedStar, tjd: number, iflag: number, swed: SweData,
|
|
3198
|
+
): { flags: number; xx: Float64Array; serr: string; starOut: string } {
|
|
3199
|
+
let serr = '';
|
|
3200
|
+
const iflgsave = iflag;
|
|
3201
|
+
iflag |= SEFLG_SPEED;
|
|
3202
|
+
iflag = plausIflag(iflag, -1, tjd, swed);
|
|
3203
|
+
const epheflag = iflag & SEFLG_EPHMASK;
|
|
3204
|
+
if (swed.lastEpheflag !== epheflag) {
|
|
3205
|
+
freePlanets(swed);
|
|
3206
|
+
for (let i = 0; i < SEI_NEPHFILES; i++) {
|
|
3207
|
+
swed.fidat[i].reader = null;
|
|
3208
|
+
}
|
|
3209
|
+
swed.lastEpheflag = epheflag;
|
|
3210
|
+
}
|
|
3211
|
+
if ((iflag & SEFLG_SIDEREAL) && !swed.ayanaIsSet) {
|
|
3212
|
+
sweSetSidMode(swed, SE_SIDM_FAGAN_BRADLEY, 0, 0);
|
|
3213
|
+
}
|
|
3214
|
+
/* obliquity and nutation */
|
|
3215
|
+
swiCheckEcliptic(tjd, iflag, swed);
|
|
3216
|
+
swiCheckNutation(tjd, iflag, swed);
|
|
3217
|
+
const starOut = `${stardata.starname},${stardata.starbayer}`;
|
|
3218
|
+
const { epoch, ramot: ra_pm, demot: de_pm, radvel: radv, parall, ra, de } = stardata;
|
|
3219
|
+
let t: number;
|
|
3220
|
+
if (epoch === 1950) {
|
|
3221
|
+
t = tjd - B1950;
|
|
3222
|
+
} else {
|
|
3223
|
+
t = tjd - J2000;
|
|
3224
|
+
}
|
|
3225
|
+
const x = new Float64Array(6);
|
|
3226
|
+
x[0] = ra;
|
|
3227
|
+
x[1] = de;
|
|
3228
|
+
/* distance from parallax */
|
|
3229
|
+
let rdist: number;
|
|
3230
|
+
if (parall === 0) {
|
|
3231
|
+
rdist = 1000000000;
|
|
3232
|
+
} else {
|
|
3233
|
+
rdist = 1.0 / (parall * RADTODEG * 3600) * PARSEC_TO_AUNIT;
|
|
3234
|
+
}
|
|
3235
|
+
x[2] = rdist;
|
|
3236
|
+
x[3] = ra_pm / 36525.0;
|
|
3237
|
+
x[4] = de_pm / 36525.0;
|
|
3238
|
+
x[5] = radv / 36525.0;
|
|
3239
|
+
/* Cartesian space motion vector */
|
|
3240
|
+
swiPolcartSp(x, x);
|
|
3241
|
+
/* FK5 */
|
|
3242
|
+
if (epoch === 1950) {
|
|
3243
|
+
swiFK4_FK5(x, B1950);
|
|
3244
|
+
swiPrecess(x, B1950, 0, J_TO_J2000, swed);
|
|
3245
|
+
const x3 = new Float64Array(x.buffer, x.byteOffset + 24, 3);
|
|
3246
|
+
swiPrecess(x3, B1950, 0, J_TO_J2000, swed);
|
|
3247
|
+
}
|
|
3248
|
+
/* FK5 to ICRF, if epoch != 0 (ICRS data is already in ICRF) */
|
|
3249
|
+
if (epoch !== 0) {
|
|
3250
|
+
swiIcrs2fk5(x, iflag, true); // backward, i.e. to ICRF
|
|
3251
|
+
if (swiGetDenum(SEI_SUN, iflag, swed) >= 403) {
|
|
3252
|
+
swiBias(x, J2000, SEFLG_SPEED, false, swed);
|
|
3253
|
+
}
|
|
3254
|
+
}
|
|
3255
|
+
/* Earth/Sun for parallax, light deflection, aberration */
|
|
3256
|
+
const xearth = new Float64Array(6);
|
|
3257
|
+
const xearth_dt = new Float64Array(6);
|
|
3258
|
+
const xsun = new Float64Array(6);
|
|
3259
|
+
const xsun_dt = new Float64Array(6);
|
|
3260
|
+
const dt = PLAN_SPEED_INTV * 0.1;
|
|
3261
|
+
if (!(iflag & SEFLG_BARYCTR) && (!(iflag & SEFLG_HELCTR) || !(iflag & SEFLG_MOSEPH))) {
|
|
3262
|
+
const ret1 = mainPlanetBary(tjd - dt, SEI_EARTH, epheflag, iflag, false, xearth_dt, xearth_dt, xsun_dt, null, swed);
|
|
3263
|
+
if (ret1.retc !== OK) return { flags: ERR, xx: new Float64Array(6), serr: ret1.serr, starOut };
|
|
3264
|
+
const ret2 = mainPlanetBary(tjd, SEI_EARTH, epheflag, iflag, true, xearth, xearth, xsun, null, swed);
|
|
3265
|
+
if (ret2.retc !== OK) return { flags: ERR, xx: new Float64Array(6), serr: ret2.serr, starOut };
|
|
3266
|
+
}
|
|
3267
|
+
/* observer: geocenter or topocenter */
|
|
3268
|
+
const xobs = new Float64Array(6);
|
|
3269
|
+
const xobs_dt = new Float64Array(6);
|
|
3270
|
+
let xpo: Float64Array | null = null;
|
|
3271
|
+
let xpo_dt: Float64Array | null = null;
|
|
3272
|
+
if (iflag & SEFLG_TOPOCTR) {
|
|
3273
|
+
swiGetObserver(tjd - dt, iflag | SEFLG_NONUT, false, xobs_dt, swed);
|
|
3274
|
+
swiGetObserver(tjd, iflag | SEFLG_NONUT, false, xobs, swed);
|
|
3275
|
+
for (let i = 0; i <= 5; i++) {
|
|
3276
|
+
xobs[i] += xearth[i];
|
|
3277
|
+
xobs_dt[i] += xearth_dt[i];
|
|
3278
|
+
}
|
|
3279
|
+
} else if (!(iflag & SEFLG_BARYCTR) && (!(iflag & SEFLG_HELCTR) || !(iflag & SEFLG_MOSEPH))) {
|
|
3280
|
+
for (let i = 0; i <= 5; i++) {
|
|
3281
|
+
xobs[i] = xearth[i];
|
|
3282
|
+
xobs_dt[i] = xearth_dt[i];
|
|
3283
|
+
}
|
|
3284
|
+
}
|
|
3285
|
+
/* determine parallax reference point */
|
|
3286
|
+
if ((iflag & SEFLG_HELCTR) && (iflag & SEFLG_MOSEPH)) {
|
|
3287
|
+
xpo = null;
|
|
3288
|
+
xpo_dt = null;
|
|
3289
|
+
} else if (iflag & SEFLG_HELCTR) {
|
|
3290
|
+
xpo = xsun;
|
|
3291
|
+
xpo_dt = xsun_dt;
|
|
3292
|
+
} else if (iflag & SEFLG_BARYCTR) {
|
|
3293
|
+
xpo = null;
|
|
3294
|
+
xpo_dt = null;
|
|
3295
|
+
} else {
|
|
3296
|
+
xpo = xobs;
|
|
3297
|
+
xpo_dt = xobs_dt;
|
|
3298
|
+
}
|
|
3299
|
+
/* position and speed at tjd */
|
|
3300
|
+
if (xpo === null) {
|
|
3301
|
+
for (let i = 0; i <= 2; i++) {
|
|
3302
|
+
x[i] += t * x[i + 3];
|
|
3303
|
+
}
|
|
3304
|
+
} else {
|
|
3305
|
+
for (let i = 0; i <= 2; i++) {
|
|
3306
|
+
x[i] += t * x[i + 3];
|
|
3307
|
+
x[i] -= xpo[i];
|
|
3308
|
+
x[i + 3] -= xpo[i + 3];
|
|
3309
|
+
}
|
|
3310
|
+
}
|
|
3311
|
+
/* relativistic deflection of light */
|
|
3312
|
+
if ((iflag & SEFLG_TRUEPOS) === 0 && (iflag & SEFLG_NOGDEFL) === 0) {
|
|
3313
|
+
swiDeflectLight(x, 0, iflag & SEFLG_SPEED, swed);
|
|
3314
|
+
}
|
|
3315
|
+
/* annual aberration of light */
|
|
3316
|
+
if ((iflag & SEFLG_TRUEPOS) === 0 && (iflag & SEFLG_NOABERR) === 0) {
|
|
3317
|
+
swiAberrLightEx(x, xpo!, xpo_dt!, dt, iflag & SEFLG_SPEED);
|
|
3318
|
+
}
|
|
3319
|
+
/* ICRS to J2000 */
|
|
3320
|
+
if (!(iflag & SEFLG_ICRS) && (swiGetDenum(SEI_SUN, iflag, swed) >= 403 || (iflag & SEFLG_BARYCTR))) {
|
|
3321
|
+
swiBias(x, tjd, iflag, false, swed);
|
|
3322
|
+
}
|
|
3323
|
+
/* save J2000 coordinates for sidereal positions */
|
|
3324
|
+
const xxsv = new Float64Array(6);
|
|
3325
|
+
for (let i = 0; i <= 5; i++) xxsv[i] = x[i];
|
|
3326
|
+
/* precession, equator 2000 → equator of date */
|
|
3327
|
+
let oe = swed.oec2000;
|
|
3328
|
+
if ((iflag & SEFLG_J2000) === 0) {
|
|
3329
|
+
swiPrecess(x, tjd, iflag, J2000_TO_J, swed);
|
|
3330
|
+
if (iflag & SEFLG_SPEED) {
|
|
3331
|
+
swiPrecessSpeed(x, tjd, iflag, J2000_TO_J, swed);
|
|
3332
|
+
}
|
|
3333
|
+
oe = swed.oec;
|
|
3334
|
+
}
|
|
3335
|
+
/* nutation */
|
|
3336
|
+
if (!(iflag & SEFLG_NONUT)) {
|
|
3337
|
+
swiNutate(x, iflag, false, swed);
|
|
3338
|
+
}
|
|
3339
|
+
/* transformation to ecliptic */
|
|
3340
|
+
if ((iflag & SEFLG_EQUATORIAL) === 0) {
|
|
3341
|
+
swiCoortrf2(x, x, oe.seps, oe.ceps);
|
|
3342
|
+
if (iflag & SEFLG_SPEED) {
|
|
3343
|
+
const x3 = new Float64Array(x.buffer, x.byteOffset + 24, 3);
|
|
3344
|
+
swiCoortrf2(x3, x3, oe.seps, oe.ceps);
|
|
3345
|
+
}
|
|
3346
|
+
if (!(iflag & SEFLG_NONUT)) {
|
|
3347
|
+
swiCoortrf2(x, x, swed.nut.snut, swed.nut.cnut);
|
|
3348
|
+
if (iflag & SEFLG_SPEED) {
|
|
3349
|
+
const x3 = new Float64Array(x.buffer, x.byteOffset + 24, 3);
|
|
3350
|
+
swiCoortrf2(x3, x3, swed.nut.snut, swed.nut.cnut);
|
|
3351
|
+
}
|
|
3352
|
+
}
|
|
3353
|
+
}
|
|
3354
|
+
/* sidereal positions */
|
|
3355
|
+
if (iflag & SEFLG_SIDEREAL) {
|
|
3356
|
+
if (swed.sidd.sidMode & SE_SIDBIT_ECL_T0) {
|
|
3357
|
+
swiTropRa2SidLon(xxsv, x, xxsv, iflag, swed);
|
|
3358
|
+
if (iflag & SEFLG_EQUATORIAL) {
|
|
3359
|
+
for (let i = 0; i <= 5; i++) x[i] = xxsv[i];
|
|
3360
|
+
}
|
|
3361
|
+
} else if (swed.sidd.sidMode & SE_SIDBIT_SSY_PLANE) {
|
|
3362
|
+
swiTropRa2SidLonSosy(xxsv, x, iflag, swed);
|
|
3363
|
+
if (iflag & SEFLG_EQUATORIAL) {
|
|
3364
|
+
for (let i = 0; i <= 5; i++) x[i] = xxsv[i];
|
|
3365
|
+
}
|
|
3366
|
+
} else {
|
|
3367
|
+
swiCartpolSp(x, x);
|
|
3368
|
+
const dayaRes = swiGetAyanamsaWithSpeed(swed, tjd, iflag);
|
|
3369
|
+
if (dayaRes.retc === ERR) {
|
|
3370
|
+
return { flags: ERR, xx: new Float64Array(6), serr: dayaRes.serr, starOut };
|
|
3371
|
+
}
|
|
3372
|
+
x[0] -= dayaRes.daya * DEGTORAD;
|
|
3373
|
+
x[3] -= dayaRes.dayaSpeed * DEGTORAD;
|
|
3374
|
+
swiPolcartSp(x, x);
|
|
3375
|
+
}
|
|
3376
|
+
}
|
|
3377
|
+
/* transformation to polar coordinates */
|
|
3378
|
+
if ((iflag & SEFLG_XYZ) === 0) {
|
|
3379
|
+
swiCartpolSp(x, x);
|
|
3380
|
+
}
|
|
3381
|
+
/* radians to degrees */
|
|
3382
|
+
if ((iflag & SEFLG_RADIANS) === 0 && (iflag & SEFLG_XYZ) === 0) {
|
|
3383
|
+
for (let i = 0; i < 2; i++) {
|
|
3384
|
+
x[i] *= RADTODEG;
|
|
3385
|
+
x[i + 3] *= RADTODEG;
|
|
3386
|
+
}
|
|
3387
|
+
}
|
|
3388
|
+
const xx = new Float64Array(6);
|
|
3389
|
+
for (let i = 0; i <= 5; i++) xx[i] = x[i];
|
|
3390
|
+
if (!(iflgsave & SEFLG_SPEED)) {
|
|
3391
|
+
for (let i = 3; i <= 5; i++) xx[i] = 0;
|
|
3392
|
+
}
|
|
3393
|
+
/* clean up ephemeris flag */
|
|
3394
|
+
if ((iflgsave & SEFLG_EPHMASK) === 0) {
|
|
3395
|
+
iflag = iflag & ~SEFLG_DEFAULTEPH;
|
|
3396
|
+
}
|
|
3397
|
+
iflag = iflag & ~SEFLG_SPEED;
|
|
3398
|
+
return { flags: iflag, xx, serr, starOut };
|
|
3399
|
+
}
|
|
3400
|
+
|
|
3401
|
+
/** Fixed star position (ET) */
|
|
3402
|
+
export function sweFixstar(
|
|
3403
|
+
swed: SweData, star: string, tjd: number, iflag: number,
|
|
3404
|
+
): { flags: number; xx: Float64Array; serr: string; starOut: string } {
|
|
3405
|
+
/* Try builtin star catalog first */
|
|
3406
|
+
const builtin = getBuiltinStar(star);
|
|
3407
|
+
if (builtin) {
|
|
3408
|
+
const parsed = fixstarCutString(builtin.record);
|
|
3409
|
+
if (!parsed) {
|
|
3410
|
+
return { flags: ERR, xx: new Float64Array(6), serr: 'error parsing builtin star data', starOut: star };
|
|
3411
|
+
}
|
|
3412
|
+
return fixstarCalcFromStruct(parsed.star, tjd, iflag, swed);
|
|
3413
|
+
}
|
|
3414
|
+
return {
|
|
3415
|
+
flags: ERR,
|
|
3416
|
+
xx: new Float64Array(6),
|
|
3417
|
+
serr: `star '${star}' not found in builtin catalog`,
|
|
3418
|
+
starOut: star,
|
|
3419
|
+
};
|
|
3420
|
+
}
|
|
3421
|
+
|
|
3422
|
+
/** Fixed star position (UT) */
|
|
3423
|
+
export function sweFixstarUt(
|
|
3424
|
+
swed: SweData, star: string, tjdUt: number, iflag: number,
|
|
3425
|
+
): { flags: number; xx: Float64Array; serr: string; starOut: string } {
|
|
3426
|
+
iflag = plausIflag(iflag, -1, tjdUt, swed);
|
|
3427
|
+
let epheflag = iflag & SEFLG_EPHMASK;
|
|
3428
|
+
if (epheflag === 0) {
|
|
3429
|
+
epheflag = SEFLG_SWIEPH;
|
|
3430
|
+
iflag |= SEFLG_SWIEPH;
|
|
3431
|
+
}
|
|
3432
|
+
const deltat = sweDeltatEx(tjdUt, iflag, swed);
|
|
3433
|
+
let ret = sweFixstar(swed, star, tjdUt + deltat, iflag);
|
|
3434
|
+
if (ret.flags !== ERR && (ret.flags & SEFLG_EPHMASK) !== epheflag) {
|
|
3435
|
+
const deltat2 = sweDeltatEx(tjdUt, ret.flags, swed);
|
|
3436
|
+
ret = sweFixstar(swed, star, tjdUt + deltat2, iflag);
|
|
3437
|
+
}
|
|
3438
|
+
return ret;
|
|
3439
|
+
}
|
|
3440
|
+
|
|
3441
|
+
/** Fixed star magnitude */
|
|
3442
|
+
export function sweFixstarMag(
|
|
3443
|
+
swed: SweData, star: string,
|
|
3444
|
+
): { mag: number; serr: string; starOut: string } {
|
|
3445
|
+
const builtin = getBuiltinStar(star);
|
|
3446
|
+
if (builtin) {
|
|
3447
|
+
const parsed = fixstarCutString(builtin.record);
|
|
3448
|
+
if (!parsed) {
|
|
3449
|
+
return { mag: 0, serr: 'error parsing builtin star data', starOut: star };
|
|
3450
|
+
}
|
|
3451
|
+
return { mag: parsed.star.mag, serr: '', starOut: parsed.starOut };
|
|
3452
|
+
}
|
|
3453
|
+
return { mag: 0, serr: `star '${star}' not found in builtin catalog`, starOut: star };
|
|
3454
|
+
}
|
|
3455
|
+
|
|
3456
|
+
/**
|
|
3457
|
+
* Fixed star position (hash-optimized variant, ET).
|
|
3458
|
+
* In this implementation, identical to sweFixstar (no external star catalog).
|
|
3459
|
+
* C: swe_fixstar2 (sweph.c:6817-6875)
|
|
3460
|
+
*/
|
|
3461
|
+
export function sweFixstar2(
|
|
3462
|
+
swed: SweData, star: string, tjd: number, iflag: number,
|
|
3463
|
+
): { flags: number; xx: Float64Array; serr: string; starOut: string } {
|
|
3464
|
+
return sweFixstar(swed, star, tjd, iflag);
|
|
3465
|
+
}
|
|
3466
|
+
|
|
3467
|
+
/**
|
|
3468
|
+
* Fixed star position (hash-optimized variant, UT).
|
|
3469
|
+
* C: swe_fixstar2_ut (sweph.c:6877-6897)
|
|
3470
|
+
*/
|
|
3471
|
+
export function sweFixstar2Ut(
|
|
3472
|
+
swed: SweData, star: string, tjdUt: number, iflag: number,
|
|
3473
|
+
): { flags: number; xx: Float64Array; serr: string; starOut: string } {
|
|
3474
|
+
return sweFixstarUt(swed, star, tjdUt, iflag);
|
|
3475
|
+
}
|
|
3476
|
+
|
|
3477
|
+
/**
|
|
3478
|
+
* Fixed star magnitude (hash-optimized variant).
|
|
3479
|
+
* C: swe_fixstar2_mag (sweph.c:6910-6943)
|
|
3480
|
+
*/
|
|
3481
|
+
export function sweFixstar2Mag(
|
|
3482
|
+
swed: SweData, star: string,
|
|
3483
|
+
): { mag: number; serr: string; starOut: string } {
|
|
3484
|
+
return sweFixstarMag(swed, star);
|
|
3485
|
+
}
|
|
3486
|
+
|
|
3487
|
+
/* ==================================================================
|
|
3488
|
+
* Cross functions — longitude/node crossings
|
|
3489
|
+
* C: swe_solcross, swe_mooncross, swe_mooncross_node, swe_helio_cross
|
|
3490
|
+
* ================================================================== */
|
|
3491
|
+
|
|
3492
|
+
const CROSS_PRECISION = 1 / 3600000.0; // one milliarcsecond
|
|
3493
|
+
|
|
3494
|
+
/** Normalize degree difference to [-180, 180). C: swe_difdeg2n */
|
|
3495
|
+
export function sweDifdeg2n(p1: number, p2: number): number {
|
|
3496
|
+
const dif = sweDegnorm(p1 - p2);
|
|
3497
|
+
if (dif >= 180.0) return dif - 360.0;
|
|
3498
|
+
return dif;
|
|
3499
|
+
}
|
|
3500
|
+
|
|
3501
|
+
/**
|
|
3502
|
+
* Find when Sun crosses a given ecliptic longitude (ET).
|
|
3503
|
+
* Returns JD of next crossing after jdEt. Error: returns jdEt - 1.
|
|
3504
|
+
* C: swe_solcross (sweph.c:8320-8342)
|
|
3505
|
+
*/
|
|
3506
|
+
export function sweSolcross(
|
|
3507
|
+
swed: SweData, x2cross: number, jdEt: number, iflag: number,
|
|
3508
|
+
): { jd: number; serr: string } {
|
|
3509
|
+
const flag = iflag | SEFLG_SPEED;
|
|
3510
|
+
let res = sweCalc(swed, jdEt, SE_SUN, flag);
|
|
3511
|
+
if (res.flags < 0) return { jd: jdEt - 1, serr: res.serr };
|
|
3512
|
+
const xlp = 360.0 / 365.24;
|
|
3513
|
+
let dist = sweDegnorm(x2cross - res.xx[0]);
|
|
3514
|
+
let jd = jdEt + dist / xlp;
|
|
3515
|
+
for (;;) {
|
|
3516
|
+
res = sweCalc(swed, jd, SE_SUN, flag);
|
|
3517
|
+
if (res.flags < 0) return { jd: jdEt - 1, serr: res.serr };
|
|
3518
|
+
dist = sweDifdeg2n(x2cross, res.xx[0]);
|
|
3519
|
+
jd += dist / res.xx[3];
|
|
3520
|
+
if (Math.abs(dist) < CROSS_PRECISION) break;
|
|
3521
|
+
}
|
|
3522
|
+
return { jd, serr: res.serr };
|
|
3523
|
+
}
|
|
3524
|
+
|
|
3525
|
+
/**
|
|
3526
|
+
* Find when Sun crosses a given ecliptic longitude (UT).
|
|
3527
|
+
* C: swe_solcross_ut (sweph.c:8354-8376)
|
|
3528
|
+
*/
|
|
3529
|
+
export function sweSolcrossUt(
|
|
3530
|
+
swed: SweData, x2cross: number, jdUt: number, iflag: number,
|
|
3531
|
+
): { jd: number; serr: string } {
|
|
3532
|
+
const flag = iflag | SEFLG_SPEED;
|
|
3533
|
+
let res = sweCalcUt(swed, jdUt, SE_SUN, flag);
|
|
3534
|
+
if (res.flags < 0) return { jd: jdUt - 1, serr: res.serr };
|
|
3535
|
+
const xlp = 360.0 / 365.24;
|
|
3536
|
+
let dist = sweDegnorm(x2cross - res.xx[0]);
|
|
3537
|
+
let jd = jdUt + dist / xlp;
|
|
3538
|
+
for (;;) {
|
|
3539
|
+
res = sweCalcUt(swed, jd, SE_SUN, flag);
|
|
3540
|
+
if (res.flags < 0) return { jd: jdUt - 1, serr: res.serr };
|
|
3541
|
+
dist = sweDifdeg2n(x2cross, res.xx[0]);
|
|
3542
|
+
jd += dist / res.xx[3];
|
|
3543
|
+
if (Math.abs(dist) < CROSS_PRECISION) break;
|
|
3544
|
+
}
|
|
3545
|
+
return { jd, serr: res.serr };
|
|
3546
|
+
}
|
|
3547
|
+
|
|
3548
|
+
/**
|
|
3549
|
+
* Find when Moon crosses a given ecliptic longitude (ET).
|
|
3550
|
+
* C: swe_mooncross (sweph.c:8388-8410)
|
|
3551
|
+
*/
|
|
3552
|
+
export function sweMooncross(
|
|
3553
|
+
swed: SweData, x2cross: number, jdEt: number, iflag: number,
|
|
3554
|
+
): { jd: number; serr: string } {
|
|
3555
|
+
const flag = iflag | SEFLG_SPEED;
|
|
3556
|
+
let res = sweCalc(swed, jdEt, SE_MOON, flag);
|
|
3557
|
+
if (res.flags < 0) return { jd: jdEt - 1, serr: res.serr };
|
|
3558
|
+
const xlp = 360.0 / 27.32;
|
|
3559
|
+
let dist = sweDegnorm(x2cross - res.xx[0]);
|
|
3560
|
+
let jd = jdEt + dist / xlp;
|
|
3561
|
+
for (;;) {
|
|
3562
|
+
res = sweCalc(swed, jd, SE_MOON, flag);
|
|
3563
|
+
if (res.flags < 0) return { jd: jdEt - 1, serr: res.serr };
|
|
3564
|
+
dist = sweDifdeg2n(x2cross, res.xx[0]);
|
|
3565
|
+
jd += dist / res.xx[3];
|
|
3566
|
+
if (Math.abs(dist) < CROSS_PRECISION) break;
|
|
3567
|
+
}
|
|
3568
|
+
return { jd, serr: res.serr };
|
|
3569
|
+
}
|
|
3570
|
+
|
|
3571
|
+
/**
|
|
3572
|
+
* Find when Moon crosses a given ecliptic longitude (UT).
|
|
3573
|
+
* C: swe_mooncross_ut (sweph.c:8424-8446)
|
|
3574
|
+
*/
|
|
3575
|
+
export function sweMooncrossUt(
|
|
3576
|
+
swed: SweData, x2cross: number, jdUt: number, iflag: number,
|
|
3577
|
+
): { jd: number; serr: string } {
|
|
3578
|
+
const flag = iflag | SEFLG_SPEED;
|
|
3579
|
+
let res = sweCalcUt(swed, jdUt, SE_MOON, flag);
|
|
3580
|
+
if (res.flags < 0) return { jd: jdUt - 1, serr: res.serr };
|
|
3581
|
+
const xlp = 360.0 / 27.32;
|
|
3582
|
+
let dist = sweDegnorm(x2cross - res.xx[0]);
|
|
3583
|
+
let jd = jdUt + dist / xlp;
|
|
3584
|
+
for (;;) {
|
|
3585
|
+
res = sweCalcUt(swed, jd, SE_MOON, flag);
|
|
3586
|
+
if (res.flags < 0) return { jd: jdUt - 1, serr: res.serr };
|
|
3587
|
+
dist = sweDifdeg2n(x2cross, res.xx[0]);
|
|
3588
|
+
jd += dist / res.xx[3];
|
|
3589
|
+
if (Math.abs(dist) < CROSS_PRECISION) break;
|
|
3590
|
+
}
|
|
3591
|
+
return { jd, serr: res.serr };
|
|
3592
|
+
}
|
|
3593
|
+
|
|
3594
|
+
/**
|
|
3595
|
+
* Find next Moon crossing over its node (zero latitude), ET.
|
|
3596
|
+
* C: swe_mooncross_node (sweph.c:8455-8485)
|
|
3597
|
+
*/
|
|
3598
|
+
export function sweMooncrossNode(
|
|
3599
|
+
swed: SweData, jdEt: number, iflag: number,
|
|
3600
|
+
): { jd: number; xlon: number; xlat: number; serr: string } {
|
|
3601
|
+
const flag = iflag | SEFLG_SPEED;
|
|
3602
|
+
let res = sweCalc(swed, jdEt, SE_MOON, flag);
|
|
3603
|
+
if (res.flags < 0) return { jd: jdEt - 1, xlon: 0, xlat: 0, serr: res.serr };
|
|
3604
|
+
let xlat = res.xx[1];
|
|
3605
|
+
let jd = jdEt + 1;
|
|
3606
|
+
/* advance day-by-day until sign change in latitude */
|
|
3607
|
+
for (;;) {
|
|
3608
|
+
res = sweCalc(swed, jd, SE_MOON, flag);
|
|
3609
|
+
if (res.flags < 0) return { jd: jdEt - 1, xlon: 0, xlat: 0, serr: res.serr };
|
|
3610
|
+
if ((res.xx[1] >= 0 && xlat < 0) || (res.xx[1] < 0 && xlat > 0)) break;
|
|
3611
|
+
jd += 1;
|
|
3612
|
+
}
|
|
3613
|
+
/* Newton iteration on latitude */
|
|
3614
|
+
let dist = res.xx[1];
|
|
3615
|
+
for (;;) {
|
|
3616
|
+
jd -= dist / res.xx[4];
|
|
3617
|
+
res = sweCalc(swed, jd, SE_MOON, flag);
|
|
3618
|
+
if (res.flags < 0) return { jd: jdEt - 1, xlon: 0, xlat: 0, serr: res.serr };
|
|
3619
|
+
dist = res.xx[1];
|
|
3620
|
+
if (Math.abs(dist) < CROSS_PRECISION) break;
|
|
3621
|
+
}
|
|
3622
|
+
return { jd, xlon: res.xx[0], xlat: res.xx[1], serr: res.serr };
|
|
3623
|
+
}
|
|
3624
|
+
|
|
3625
|
+
/**
|
|
3626
|
+
* Find next Moon crossing over its node (zero latitude), UT.
|
|
3627
|
+
* C: swe_mooncross_node_ut (sweph.c:8492-8522)
|
|
3628
|
+
*/
|
|
3629
|
+
export function sweMooncrossNodeUt(
|
|
3630
|
+
swed: SweData, jdUt: number, iflag: number,
|
|
3631
|
+
): { jd: number; xlon: number; xlat: number; serr: string } {
|
|
3632
|
+
const flag = iflag | SEFLG_SPEED;
|
|
3633
|
+
let res = sweCalcUt(swed, jdUt, SE_MOON, flag);
|
|
3634
|
+
if (res.flags < 0) return { jd: jdUt - 1, xlon: 0, xlat: 0, serr: res.serr };
|
|
3635
|
+
let xlat = res.xx[1];
|
|
3636
|
+
let jd = jdUt + 1;
|
|
3637
|
+
for (;;) {
|
|
3638
|
+
res = sweCalcUt(swed, jd, SE_MOON, flag);
|
|
3639
|
+
if (res.flags < 0) return { jd: jdUt - 1, xlon: 0, xlat: 0, serr: res.serr };
|
|
3640
|
+
if ((res.xx[1] >= 0 && xlat < 0) || (res.xx[1] < 0 && xlat > 0)) break;
|
|
3641
|
+
jd += 1;
|
|
3642
|
+
}
|
|
3643
|
+
let dist = res.xx[1];
|
|
3644
|
+
for (;;) {
|
|
3645
|
+
jd -= dist / res.xx[4];
|
|
3646
|
+
res = sweCalcUt(swed, jd, SE_MOON, flag);
|
|
3647
|
+
if (res.flags < 0) return { jd: jdUt - 1, xlon: 0, xlat: 0, serr: res.serr };
|
|
3648
|
+
dist = res.xx[1];
|
|
3649
|
+
if (Math.abs(dist) < CROSS_PRECISION) break;
|
|
3650
|
+
}
|
|
3651
|
+
return { jd, xlon: res.xx[0], xlat: res.xx[1], serr: res.serr };
|
|
3652
|
+
}
|
|
3653
|
+
|
|
3654
|
+
/**
|
|
3655
|
+
* Find when a planet crosses a heliocentric longitude (ET).
|
|
3656
|
+
* dir >= 0: next crossing; dir < 0: previous crossing.
|
|
3657
|
+
* C: swe_helio_cross (sweph.c:8532-8568)
|
|
3658
|
+
*/
|
|
3659
|
+
export function sweHelioCross(
|
|
3660
|
+
swed: SweData, ipl: number, x2cross: number, jdEt: number, iflag: number, dir: number,
|
|
3661
|
+
): { retval: number; jdCross: number; serr: string } {
|
|
3662
|
+
const flag = iflag | SEFLG_SPEED | SEFLG_HELCTR;
|
|
3663
|
+
if (ipl === SE_SUN || ipl === SE_MOON ||
|
|
3664
|
+
(ipl >= SE_MEAN_NODE && ipl <= SE_OSCU_APOG) ||
|
|
3665
|
+
(ipl >= SE_INTP_APOG && ipl < SE_NPLANETS)) {
|
|
3666
|
+
return { retval: ERR, jdCross: 0, serr: `swe_helio_cross: not possible for object ${ipl}` };
|
|
3667
|
+
}
|
|
3668
|
+
let res = sweCalc(swed, jdEt, ipl, flag);
|
|
3669
|
+
if (res.flags < 0) return { retval: ERR, jdCross: 0, serr: res.serr };
|
|
3670
|
+
let xlp = res.xx[3];
|
|
3671
|
+
if (ipl === SE_CHIRON) xlp = 0.01971; // mean speed
|
|
3672
|
+
let dist = sweDegnorm(x2cross - res.xx[0]);
|
|
3673
|
+
let jd: number;
|
|
3674
|
+
if (dir >= 0) {
|
|
3675
|
+
jd = jdEt + dist / xlp;
|
|
3676
|
+
} else {
|
|
3677
|
+
dist = 360.0 - dist;
|
|
3678
|
+
jd = jdEt - dist / xlp;
|
|
3679
|
+
}
|
|
3680
|
+
for (;;) {
|
|
3681
|
+
res = sweCalc(swed, jd, ipl, flag);
|
|
3682
|
+
if (res.flags < 0) return { retval: ERR, jdCross: 0, serr: res.serr };
|
|
3683
|
+
dist = sweDifdeg2n(x2cross, res.xx[0]);
|
|
3684
|
+
jd += dist / res.xx[3];
|
|
3685
|
+
if (Math.abs(dist) < CROSS_PRECISION) break;
|
|
3686
|
+
}
|
|
3687
|
+
return { retval: OK, jdCross: jd, serr: res.serr };
|
|
3688
|
+
}
|
|
3689
|
+
|
|
3690
|
+
/**
|
|
3691
|
+
* Find when a planet crosses a heliocentric longitude (UT).
|
|
3692
|
+
* C: swe_helio_cross_ut (sweph.c:8578-8614)
|
|
3693
|
+
*/
|
|
3694
|
+
export function sweHelioCrossUt(
|
|
3695
|
+
swed: SweData, ipl: number, x2cross: number, jdUt: number, iflag: number, dir: number,
|
|
3696
|
+
): { retval: number; jdCross: number; serr: string } {
|
|
3697
|
+
const flag = iflag | SEFLG_SPEED | SEFLG_HELCTR;
|
|
3698
|
+
if (ipl === SE_SUN || ipl === SE_MOON ||
|
|
3699
|
+
(ipl >= SE_MEAN_NODE && ipl <= SE_OSCU_APOG) ||
|
|
3700
|
+
(ipl >= SE_INTP_APOG && ipl < SE_NPLANETS)) {
|
|
3701
|
+
return { retval: ERR, jdCross: 0, serr: `swe_helio_cross: not possible for object ${ipl}` };
|
|
3702
|
+
}
|
|
3703
|
+
let res = sweCalcUt(swed, jdUt, ipl, flag);
|
|
3704
|
+
if (res.flags < 0) return { retval: ERR, jdCross: 0, serr: res.serr };
|
|
3705
|
+
let xlp = res.xx[3];
|
|
3706
|
+
if (ipl === SE_CHIRON) xlp = 0.01971;
|
|
3707
|
+
let dist = sweDegnorm(x2cross - res.xx[0]);
|
|
3708
|
+
let jd: number;
|
|
3709
|
+
if (dir >= 0) {
|
|
3710
|
+
jd = jdUt + dist / xlp;
|
|
3711
|
+
} else {
|
|
3712
|
+
dist = 360.0 - dist;
|
|
3713
|
+
jd = jdUt - dist / xlp;
|
|
3714
|
+
}
|
|
3715
|
+
for (;;) {
|
|
3716
|
+
res = sweCalcUt(swed, jd, ipl, flag);
|
|
3717
|
+
if (res.flags < 0) return { retval: ERR, jdCross: 0, serr: res.serr };
|
|
3718
|
+
dist = sweDifdeg2n(x2cross, res.xx[0]);
|
|
3719
|
+
jd += dist / res.xx[3];
|
|
3720
|
+
if (Math.abs(dist) < CROSS_PRECISION) break;
|
|
3721
|
+
}
|
|
3722
|
+
return { retval: OK, jdCross: jd, serr: res.serr };
|
|
3723
|
+
}
|
|
3724
|
+
|
|
3725
|
+
/* ==================================================================
|
|
3726
|
+
* Planetocentric calculations
|
|
3727
|
+
* C: swe_calc_pctr (sweph.c:8041-8282)
|
|
3728
|
+
* ================================================================== */
|
|
3729
|
+
|
|
3730
|
+
/**
|
|
3731
|
+
* Compute position of ipl as seen from iplctr (planetocentric).
|
|
3732
|
+
* E.g. ipl=SE_MARS, iplctr=SE_JUPITER gives Mars as seen from Jupiter.
|
|
3733
|
+
* C: swe_calc_pctr (sweph.c:8041-8282)
|
|
3734
|
+
*/
|
|
3735
|
+
export function sweCalcPctr(
|
|
3736
|
+
swed: SweData, tjd: number, ipl: number, iplctr: number, iflag: number,
|
|
3737
|
+
): { flags: number; xx: Float64Array; serr: string } {
|
|
3738
|
+
const xxret = new Float64Array(6);
|
|
3739
|
+
let serr = '';
|
|
3740
|
+
if (ipl === iplctr) {
|
|
3741
|
+
return { flags: ERR, xx: xxret, serr: `ipl and iplctr (= ${ipl}) must not be identical\n` };
|
|
3742
|
+
}
|
|
3743
|
+
iflag = plausIflag(iflag, ipl, tjd, swed);
|
|
3744
|
+
const epheflag = iflag & SEFLG_EPHMASK;
|
|
3745
|
+
/* initialize obliquity and nutation */
|
|
3746
|
+
const dt0 = sweDeltatEx(tjd, epheflag, swed);
|
|
3747
|
+
sweCalc(swed, tjd + dt0, SE_ECL_NUT, iflag);
|
|
3748
|
+
iflag &= ~(SEFLG_HELCTR | SEFLG_BARYCTR);
|
|
3749
|
+
let iflag2 = epheflag;
|
|
3750
|
+
iflag2 |= (SEFLG_BARYCTR | SEFLG_J2000 | SEFLG_ICRS | SEFLG_TRUEPOS | SEFLG_EQUATORIAL | SEFLG_XYZ | SEFLG_SPEED);
|
|
3751
|
+
iflag2 |= (SEFLG_NOABERR | SEFLG_NOGDEFL);
|
|
3752
|
+
/* center body position */
|
|
3753
|
+
let res = sweCalc(swed, tjd, iplctr, iflag2);
|
|
3754
|
+
if (res.flags < 0) return { flags: ERR, xx: xxret, serr: res.serr };
|
|
3755
|
+
serr = res.serr;
|
|
3756
|
+
const xxctr = [res.xx[0], res.xx[1], res.xx[2], res.xx[3], res.xx[4], res.xx[5]];
|
|
3757
|
+
/* target body position */
|
|
3758
|
+
res = sweCalc(swed, tjd, ipl, iflag2);
|
|
3759
|
+
if (res.flags < 0) return { flags: ERR, xx: xxret, serr: res.serr };
|
|
3760
|
+
if (res.serr) serr = res.serr;
|
|
3761
|
+
const xx = [res.xx[0], res.xx[1], res.xx[2], res.xx[3], res.xx[4], res.xx[5]];
|
|
3762
|
+
const xx0 = [xx[0], xx[1], xx[2], xx[3], xx[4], xx[5]];
|
|
3763
|
+
/* light-time correction */
|
|
3764
|
+
let dtsaveForDefl = 0;
|
|
3765
|
+
const xxctr2 = [0, 0, 0, 0, 0, 0];
|
|
3766
|
+
const xxsp = [0, 0, 0, 0, 0, 0];
|
|
3767
|
+
if (!(iflag & SEFLG_TRUEPOS)) {
|
|
3768
|
+
const niter = 1;
|
|
3769
|
+
if (iflag & SEFLG_SPEED) {
|
|
3770
|
+
const xxsv0 = [0, 0, 0];
|
|
3771
|
+
for (let i = 0; i <= 2; i++) {
|
|
3772
|
+
xxsv0[i] = xx[i] - xx[i + 3];
|
|
3773
|
+
xxsp[i] = xxsv0[i];
|
|
3774
|
+
}
|
|
3775
|
+
for (let j = 0; j <= niter; j++) {
|
|
3776
|
+
const dx = [0, 0, 0];
|
|
3777
|
+
for (let i = 0; i <= 2; i++) {
|
|
3778
|
+
dx[i] = xxsp[i] - (xxctr[i] - xxctr[i + 3]);
|
|
3779
|
+
}
|
|
3780
|
+
const dt = Math.sqrt(squareSum(dx)) * AUNIT / CLIGHT / 86400.0;
|
|
3781
|
+
for (let i = 0; i <= 2; i++)
|
|
3782
|
+
xxsp[i] = xxsv0[i] - dt * xx0[i + 3];
|
|
3783
|
+
}
|
|
3784
|
+
for (let i = 0; i <= 2; i++)
|
|
3785
|
+
xxsp[i] = xxsv0[i] - xxsp[i];
|
|
3786
|
+
}
|
|
3787
|
+
/* dt and t(apparent) */
|
|
3788
|
+
let t = tjd;
|
|
3789
|
+
for (let j = 0; j <= niter; j++) {
|
|
3790
|
+
const dx = [0, 0, 0];
|
|
3791
|
+
for (let i = 0; i <= 2; i++) {
|
|
3792
|
+
dx[i] = xx[i] - xxctr[i];
|
|
3793
|
+
}
|
|
3794
|
+
const dt = Math.sqrt(squareSum(dx)) * AUNIT / CLIGHT / 86400.0;
|
|
3795
|
+
t = tjd - dt;
|
|
3796
|
+
dtsaveForDefl = dt;
|
|
3797
|
+
for (let i = 0; i <= 2; i++)
|
|
3798
|
+
xx[i] = xx0[i] - dt * xx0[i + 3];
|
|
3799
|
+
}
|
|
3800
|
+
/* part of daily motion from change of dt */
|
|
3801
|
+
if (iflag & SEFLG_SPEED) {
|
|
3802
|
+
for (let i = 0; i <= 2; i++)
|
|
3803
|
+
xxsp[i] = xx0[i] - xx[i] - xxsp[i];
|
|
3804
|
+
}
|
|
3805
|
+
res = sweCalc(swed, t, iplctr, iflag2);
|
|
3806
|
+
if (res.flags < 0) return { flags: ERR, xx: xxret, serr: res.serr };
|
|
3807
|
+
for (let i = 0; i <= 5; i++) xxctr2[i] = res.xx[i];
|
|
3808
|
+
res = sweCalc(swed, t, ipl, iflag2);
|
|
3809
|
+
if (res.flags < 0) return { flags: ERR, xx: xxret, serr: res.serr };
|
|
3810
|
+
for (let i = 0; i <= 5; i++) xx[i] = res.xx[i];
|
|
3811
|
+
}
|
|
3812
|
+
/* conversion to planetocenter */
|
|
3813
|
+
for (let i = 0; i <= 5; i++)
|
|
3814
|
+
xx[i] -= xxctr[i];
|
|
3815
|
+
if (!(iflag & SEFLG_TRUEPOS)) {
|
|
3816
|
+
if (iflag & SEFLG_SPEED)
|
|
3817
|
+
for (let i = 3; i <= 5; i++)
|
|
3818
|
+
xx[i] -= xxsp[i - 3];
|
|
3819
|
+
}
|
|
3820
|
+
if (!(iflag & SEFLG_SPEED))
|
|
3821
|
+
for (let i = 3; i <= 5; i++) xx[i] = 0;
|
|
3822
|
+
/* relativistic deflection of light */
|
|
3823
|
+
if (!(iflag & SEFLG_TRUEPOS) && !(iflag & SEFLG_NOGDEFL))
|
|
3824
|
+
swiDeflectLight(xx, dtsaveForDefl, iflag, swed);
|
|
3825
|
+
/* annual aberration of light */
|
|
3826
|
+
if (!(iflag & SEFLG_TRUEPOS) && !(iflag & SEFLG_NOABERR)) {
|
|
3827
|
+
swiAberrLight(xx, xxctr, iflag);
|
|
3828
|
+
if (iflag & SEFLG_SPEED)
|
|
3829
|
+
for (let i = 3; i <= 5; i++)
|
|
3830
|
+
xx[i] += xxctr[i] - xxctr2[i];
|
|
3831
|
+
}
|
|
3832
|
+
if (!(iflag & SEFLG_SPEED))
|
|
3833
|
+
for (let i = 3; i <= 5; i++) xx[i] = 0;
|
|
3834
|
+
/* ICRS to J2000 */
|
|
3835
|
+
if (!(iflag & SEFLG_ICRS) && swiGetDenum(ipl, epheflag, swed) >= 403) {
|
|
3836
|
+
swiBias(xx, tjd, iflag, false, swed);
|
|
3837
|
+
}
|
|
3838
|
+
/* save J2000 coordinates for sidereal */
|
|
3839
|
+
const xxsv = new Array(24).fill(0);
|
|
3840
|
+
for (let i = 0; i <= 5; i++) xxsv[i] = xx[i];
|
|
3841
|
+
/* precession */
|
|
3842
|
+
let oe = swed.oec2000;
|
|
3843
|
+
if (!(iflag & SEFLG_J2000)) {
|
|
3844
|
+
swiPrecess(xx, tjd, iflag, J2000_TO_J, swed);
|
|
3845
|
+
if (iflag & SEFLG_SPEED) {
|
|
3846
|
+
const spd = [xx[3], xx[4], xx[5]];
|
|
3847
|
+
swiPrecessSpeed(spd, tjd, iflag, J2000_TO_J, swed);
|
|
3848
|
+
xx[3] = spd[0]; xx[4] = spd[1]; xx[5] = spd[2];
|
|
3849
|
+
}
|
|
3850
|
+
oe = swed.oec;
|
|
3851
|
+
}
|
|
3852
|
+
/* nutation */
|
|
3853
|
+
if (!(iflag & SEFLG_NONUT))
|
|
3854
|
+
swiNutate(xx, iflag, false, swed);
|
|
3855
|
+
/* fill xreturn layout */
|
|
3856
|
+
const xreturn = new Array(24).fill(0);
|
|
3857
|
+
/* equatorial cartesian */
|
|
3858
|
+
for (let i = 0; i <= 5; i++) xreturn[18 + i] = xx[i];
|
|
3859
|
+
/* ecliptic conversion */
|
|
3860
|
+
swiCoortrf2(xx, xx, oe.seps, oe.ceps);
|
|
3861
|
+
if (iflag & SEFLG_SPEED)
|
|
3862
|
+
swiCoortrf2(xx, xx, oe.seps, oe.ceps, 3, 3);
|
|
3863
|
+
if (!(iflag & SEFLG_NONUT)) {
|
|
3864
|
+
swiCoortrf2(xx, xx, swed.nut.snut, swed.nut.cnut);
|
|
3865
|
+
if (iflag & SEFLG_SPEED)
|
|
3866
|
+
swiCoortrf2(xx, xx, swed.nut.snut, swed.nut.cnut, 3, 3);
|
|
3867
|
+
}
|
|
3868
|
+
/* ecliptic cartesian */
|
|
3869
|
+
for (let i = 0; i <= 5; i++) xreturn[6 + i] = xx[i];
|
|
3870
|
+
/* sidereal */
|
|
3871
|
+
if (iflag & SEFLG_SIDEREAL) {
|
|
3872
|
+
if (swed.sidd.sidMode & SE_SIDBIT_ECL_T0) {
|
|
3873
|
+
swiTropRa2SidLon(xxsv, xreturn.slice(6), xreturn.slice(18), iflag, swed);
|
|
3874
|
+
} else if (swed.sidd.sidMode & SE_SIDBIT_SSY_PLANE) {
|
|
3875
|
+
swiTropRa2SidLonSosy(xxsv, xreturn.slice(6), iflag, swed);
|
|
3876
|
+
} else {
|
|
3877
|
+
/* traditional: convert to polar, apply ayanamsa, convert back */
|
|
3878
|
+
const eclPol = [0, 0, 0, 0, 0, 0];
|
|
3879
|
+
swiCartpolSp([xreturn[6], xreturn[7], xreturn[8], xreturn[9], xreturn[10], xreturn[11]], eclPol);
|
|
3880
|
+
for (let i = 0; i <= 5; i++) xreturn[i] = eclPol[i];
|
|
3881
|
+
for (let i = 0; i < 24; i++) xxsv[i] = xreturn[i];
|
|
3882
|
+
const ayaRes = swiGetAyanamsaWithSpeed(swed, tjd, iflag);
|
|
3883
|
+
if (ayaRes.retc === ERR) return { flags: ERR, xx: xxret, serr: ayaRes.serr };
|
|
3884
|
+
for (let i = 0; i < 24; i++) xreturn[i] = xxsv[i];
|
|
3885
|
+
xreturn[0] -= ayaRes.daya * DEGTORAD;
|
|
3886
|
+
xreturn[3] -= ayaRes.dayaSpeed * DEGTORAD;
|
|
3887
|
+
const eclCart = [0, 0, 0, 0, 0, 0];
|
|
3888
|
+
swiPolcartSp(xreturn, eclCart);
|
|
3889
|
+
for (let i = 0; i <= 5; i++) xreturn[6 + i] = eclCart[i];
|
|
3890
|
+
}
|
|
3891
|
+
}
|
|
3892
|
+
/* polar coordinates */
|
|
3893
|
+
const eqPol = [0, 0, 0, 0, 0, 0];
|
|
3894
|
+
swiCartpolSp([xreturn[18], xreturn[19], xreturn[20], xreturn[21], xreturn[22], xreturn[23]], eqPol);
|
|
3895
|
+
for (let i = 0; i <= 5; i++) xreturn[12 + i] = eqPol[i];
|
|
3896
|
+
const eclPol2 = [0, 0, 0, 0, 0, 0];
|
|
3897
|
+
swiCartpolSp([xreturn[6], xreturn[7], xreturn[8], xreturn[9], xreturn[10], xreturn[11]], eclPol2);
|
|
3898
|
+
for (let i = 0; i <= 5; i++) xreturn[i] = eclPol2[i];
|
|
3899
|
+
/* radians to degrees */
|
|
3900
|
+
for (let i = 0; i < 2; i++) {
|
|
3901
|
+
xreturn[i] *= RADTODEG;
|
|
3902
|
+
xreturn[i + 3] *= RADTODEG;
|
|
3903
|
+
xreturn[i + 12] *= RADTODEG;
|
|
3904
|
+
xreturn[i + 15] *= RADTODEG;
|
|
3905
|
+
}
|
|
3906
|
+
/* select output based on flags */
|
|
3907
|
+
let offset = 0;
|
|
3908
|
+
if (iflag & SEFLG_EQUATORIAL) {
|
|
3909
|
+
offset = (iflag & SEFLG_XYZ) ? 18 : 12;
|
|
3910
|
+
} else {
|
|
3911
|
+
offset = (iflag & SEFLG_XYZ) ? 6 : 0;
|
|
3912
|
+
}
|
|
3913
|
+
for (let i = 0; i < 6; i++) xxret[i] = xreturn[offset + i];
|
|
3914
|
+
if (!(iflag & SEFLG_SPEED))
|
|
3915
|
+
for (let i = 3; i < 6; i++) xxret[i] = 0;
|
|
3916
|
+
if (iflag & SEFLG_RADIANS) {
|
|
3917
|
+
for (let i = 0; i < 2; i++) xxret[i] *= DEGTORAD;
|
|
3918
|
+
if (iflag & SEFLG_SPEED)
|
|
3919
|
+
for (let i = 3; i < 5; i++) xxret[i] *= DEGTORAD;
|
|
3920
|
+
}
|
|
3921
|
+
/* normalize longitude */
|
|
3922
|
+
if (!(iflag & SEFLG_XYZ) && !(iflag & SEFLG_RADIANS)) {
|
|
3923
|
+
xxret[0] = sweDegnorm(xxret[0]);
|
|
3924
|
+
}
|
|
3925
|
+
return { flags: iflag, xx: xxret, serr };
|
|
3926
|
+
}
|
|
3927
|
+
|
|
3928
|
+
/* ================================================================
|
|
3929
|
+
* JPL file loading and misc public functions
|
|
3930
|
+
* ================================================================ */
|
|
3931
|
+
|
|
3932
|
+
/** Map DE number to tidal acceleration (C: swi_get_tid_acc switch) */
|
|
3933
|
+
function tidAccForDenum(denum: number): number {
|
|
3934
|
+
switch (denum) {
|
|
3935
|
+
case 200: return SE_TIDAL_DE200;
|
|
3936
|
+
case 403: return SE_TIDAL_DE403;
|
|
3937
|
+
case 404: return SE_TIDAL_DE404;
|
|
3938
|
+
case 405: return SE_TIDAL_DE405;
|
|
3939
|
+
case 406: return SE_TIDAL_DE406;
|
|
3940
|
+
case 421: return SE_TIDAL_DE421;
|
|
3941
|
+
case 422: return SE_TIDAL_DE422;
|
|
3942
|
+
case 430: return SE_TIDAL_DE430;
|
|
3943
|
+
case 431: return SE_TIDAL_DE431;
|
|
3944
|
+
case 440: case 441: return SE_TIDAL_DE441;
|
|
3945
|
+
default: return SE_TIDAL_DEFAULT;
|
|
3946
|
+
}
|
|
3947
|
+
}
|
|
3948
|
+
|
|
3949
|
+
/**
|
|
3950
|
+
* Load a JPL binary ephemeris file from an ArrayBuffer.
|
|
3951
|
+
* This is the user-facing API for loading JPL DE files.
|
|
3952
|
+
*/
|
|
3953
|
+
export function sweLoadJplFile(
|
|
3954
|
+
swed: SweData, buffer: ArrayBuffer, fname?: string,
|
|
3955
|
+
): { retc: number; serr: string } {
|
|
3956
|
+
swiInitSwedIfStart(swed);
|
|
3957
|
+
const reader = new SE1FileReader(buffer);
|
|
3958
|
+
const ss = new Float64Array(3);
|
|
3959
|
+
const serr: string[] = [''];
|
|
3960
|
+
const retc = swiOpenJplFile(swed, ss, reader, serr);
|
|
3961
|
+
if (retc !== OK) {
|
|
3962
|
+
return { retc, serr: serr[0] };
|
|
3963
|
+
}
|
|
3964
|
+
swed.jplFileIsOpen = true;
|
|
3965
|
+
swed.jpldenum = swiGetJplDenum(swed);
|
|
3966
|
+
if (fname) swed.jplfnam = fname;
|
|
3967
|
+
/* Set tidal acceleration based on DE number (if not manual) */
|
|
3968
|
+
if (!swed.isTidAccManual) {
|
|
3969
|
+
swed.tidAcc = tidAccForDenum(swed.jpldenum);
|
|
3970
|
+
}
|
|
3971
|
+
return { retc: OK, serr: serr[0] };
|
|
3972
|
+
}
|
|
3973
|
+
|
|
3974
|
+
/**
|
|
3975
|
+
* Get metadata of loaded SE1 ephemeris file.
|
|
3976
|
+
* C: swe_get_current_file_data
|
|
3977
|
+
*/
|
|
3978
|
+
export function sweGetCurrentFileData(swed: SweData, ifno: number): {
|
|
3979
|
+
fname: string; tfstart: number; tfend: number; denum: number;
|
|
3980
|
+
} | null {
|
|
3981
|
+
if (ifno < 0 || ifno > 4) return null;
|
|
3982
|
+
const pfp = swed.fidat[ifno];
|
|
3983
|
+
if (!pfp.fnam) return null;
|
|
3984
|
+
return { fname: pfp.fnam, tfstart: pfp.tfstart, tfend: pfp.tfend, denum: pfp.swephDenum };
|
|
3985
|
+
}
|
|
3986
|
+
|
|
3987
|
+
/**
|
|
3988
|
+
* Get library path (not meaningful in JS, returns empty string).
|
|
3989
|
+
* C: swe_get_library_path
|
|
3990
|
+
*/
|
|
3991
|
+
export function sweGetLibraryPath(): string {
|
|
3992
|
+
return '';
|
|
3993
|
+
}
|