libephemeris 0.1.6__py3-none-any.whl

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.
@@ -0,0 +1,228 @@
1
+ """
2
+ Fixed star position calculations for libephemeris.
3
+
4
+ Computes ecliptic positions for bright fixed stars with:
5
+ - Proper motion correction (linear approximation)
6
+ - IAU 2006 precession from J2000 to date
7
+ - Nutation correction for obliquity
8
+ - Equatorial to ecliptic coordinate transformation
9
+
10
+ Supported stars:
11
+ - Regulus (Alpha Leonis) - Royal Star
12
+ - Spica (Alpha Virginis) - Used for ayanamsha calculations
13
+
14
+ FIXME: Precision - Uses simplified linear proper motion
15
+ Real stars have:
16
+ - Radial velocity (3D motion, not just RA/Dec)
17
+ - Parallax (distance changes apparent position)
18
+ - Acceleration (proper motion not constant)
19
+ Full catalogs like Hipparcos/Gaia provide higher precision.
20
+
21
+ Typical precision: ~1-5 arcseconds over ±100 years from J2000
22
+ For research astronomy, use SIMBAD/Gaia catalogs.
23
+
24
+ References:
25
+ - IAU 2006 Precession: Capitaine et al. A&A 412, 567-586 (2003)
26
+ - Proper motion: Hipparcos/Tycho catalogs
27
+ """
28
+
29
+ import math
30
+ from dataclasses import dataclass
31
+ from typing import Tuple
32
+ from .constants import SE_REGULUS, SE_SPICA_STAR
33
+
34
+
35
+ @dataclass
36
+ class StarData:
37
+ """
38
+ Fixed star astrometric data (ICRS J2000.0 epoch).
39
+
40
+ Attributes:
41
+ ra_j2000: Right Ascension at J2000.0 in degrees
42
+ dec_j2000: Declination at J2000.0 in degrees
43
+ pm_ra: Proper motion in RA (arcsec/year, includes cos(dec) factor)
44
+ pm_dec: Proper motion in Dec (arcsec/year)
45
+
46
+ Note:
47
+ Proper motions are linearized - good for ±200 years from epoch.
48
+ Does not include parallax or radial velocity effects.
49
+ """
50
+
51
+ ra_j2000: float
52
+ dec_j2000: float
53
+ pm_ra: float
54
+ pm_dec: float
55
+
56
+
57
+ # Fixed star catalog (J2000.0 ICRS coordinates from Hipparcos)
58
+ FIXED_STARS = {
59
+ SE_REGULUS: StarData(
60
+ ra_j2000=152.092958, # 10h 08m 22.3s (Alpha Leonis)
61
+ dec_j2000=11.967208, # +11° 58' 02"
62
+ pm_ra=-0.00249, # -249 mas/yr (westward)
63
+ pm_dec=0.00152, # +152 mas/yr (northward)
64
+ ),
65
+ SE_SPICA_STAR: StarData(
66
+ ra_j2000=201.298247, # 13h 25m 11.6s (Alpha Virginis)
67
+ dec_j2000=-11.161319, # -11° 09' 41"
68
+ pm_ra=-0.04235, # -42.35 mas/yr
69
+ pm_dec=-0.03067, # -30.67 mas/yr
70
+ ),
71
+ }
72
+
73
+
74
+ def calc_fixed_star_position(star_id: int, jd_tt: float) -> Tuple[float, float, float]:
75
+ """
76
+ Calculate ecliptic position of a fixed star at given date.
77
+
78
+ Applies proper motion and precession to J2000.0 catalog position.
79
+
80
+ Args:
81
+ star_id: Star identifier (SE_REGULUS, SE_SPICA_STAR)
82
+ jd_tt: Julian Day in Terrestrial Time (TT)
83
+
84
+ Returns:
85
+ Tuple[float, float, float]: (longitude, latitude, distance) where:
86
+ - longitude: Ecliptic longitude of date in degrees (0-360)
87
+ - latitude: Ecliptic latitude of date in degrees
88
+ - distance: Arbitrary large value (100000 AU) - stars are effectively infinite
89
+
90
+ Raises:
91
+ ValueError: If star_id not in catalog
92
+
93
+ Algorithm:
94
+ 1. Apply linear proper motion from J2000 to date
95
+ 2. Precess equatorial coordinates using IAU 2006 formula
96
+ 3. Calculate true obliquity (mean + nutation)
97
+ 4. Transform to ecliptic coordinates
98
+
99
+ FIXME: Precision - Linear proper motion approximation
100
+ - Ignores radial velocity (parallax effect)
101
+ - Assumes constant proper motion (no acceleration)
102
+ - No annual parallax (star distance not modeled)
103
+ Typical error: 1-5 arcsec over ±100 years
104
+
105
+ References:
106
+ IAU 2006 precession (Capitaine et al.)
107
+ Nutation simplified from IAU 2000B
108
+ """
109
+ if star_id not in FIXED_STARS:
110
+ raise ValueError(f"Unknown star ID: {star_id}")
111
+
112
+ star = FIXED_STARS[star_id]
113
+
114
+ # Time from J2000.0
115
+ t_years = (jd_tt - 2451545.0) / 365.25
116
+ T = (jd_tt - 2451545.0) / 36525.0 # Julian centuries
117
+
118
+ # FIXME: Precision - Linear proper motion (ignores curvature)
119
+ # Apply proper motion to J2000 coordinates
120
+ ra_pm = star.ra_j2000 + (star.pm_ra * t_years) / 3600.0
121
+ dec_pm = star.dec_j2000 + (star.pm_dec * t_years) / 3600.0
122
+
123
+ # IAU 2006 precession parameters (arcseconds -> degrees)
124
+ zeta = (2306.2181 * T + 0.30188 * T**2 + 0.017998 * T**3) / 3600.0
125
+ z = (2306.2181 * T + 1.09468 * T**2 + 0.018203 * T**3) / 3600.0
126
+ theta = (2004.3109 * T - 0.42665 * T**2 - 0.041833 * T**3) / 3600.0
127
+
128
+ zeta_r = math.radians(zeta)
129
+ z_r = math.radians(z)
130
+ theta_r = math.radians(theta)
131
+ ra_r = math.radians(ra_pm)
132
+ dec_r = math.radians(dec_pm)
133
+
134
+ # Convert equatorial to Cartesian (unit sphere)
135
+ x0 = math.cos(dec_r) * math.cos(ra_r)
136
+ y0 = math.cos(dec_r) * math.sin(ra_r)
137
+ z0 = math.sin(dec_r)
138
+
139
+ # Apply precession rotation: R_z(-z) · R_y(θ) · R_z(-ζ)
140
+ # Step 1: R_z(-ζ) rotation around z-axis
141
+ x1 = x0 * math.cos(-zeta_r) + y0 * math.sin(-zeta_r)
142
+ y1 = -x0 * math.sin(-zeta_r) + y0 * math.cos(-zeta_r)
143
+ z1 = z0
144
+
145
+ # Step 2: R_y(θ) rotation around y-axis
146
+ x2 = x1 * math.cos(theta_r) - z1 * math.sin(theta_r)
147
+ y2 = y1
148
+ z2 = x1 * math.sin(theta_r) + z1 * math.cos(theta_r)
149
+
150
+ # Step 3: R_z(-z) rotation around z-axis
151
+ x3 = x2 * math.cos(-z_r) + y2 * math.sin(-z_r)
152
+ y3 = -x2 * math.sin(-z_r) + y2 * math.cos(-z_r)
153
+ z3 = z2
154
+
155
+ # Convert back to spherical (RA/Dec of date)
156
+ ra_date = math.atan2(y3, x3)
157
+ dec_date = math.asin(z3)
158
+
159
+ # Calculate mean obliquity of date (IAU 2006)
160
+ eps0 = 23.43929111 - (46.8150 + (0.00059 - 0.001813 * T) * T) * T / 3600.0
161
+
162
+ # FIXME: Precision - Simplified nutation (2-term approximation)
163
+ # Full IAU 2000B has 77 terms. This captures ~99% of effect.
164
+ omega = 125.04452 - 1934.136261 * T # Longitude of lunar ascending node
165
+ L = 280.4665 + 36000.7698 * T # Mean longitude of Sun
166
+
167
+ # Nutation in obliquity (arcseconds)
168
+ deps = 9.20 * math.cos(math.radians(omega)) + 0.57 * math.cos(math.radians(2 * L))
169
+ deps_deg = deps / 3600.0
170
+ eps_true = eps0 + deps_deg
171
+ eps_r = math.radians(eps_true)
172
+
173
+ # Transform equatorial (RA, Dec) to ecliptic (lon, lat)
174
+ sin_lat = math.sin(dec_date) * math.cos(eps_r) - math.cos(dec_date) * math.sin(
175
+ eps_r
176
+ ) * math.sin(ra_date)
177
+ lat = math.asin(sin_lat)
178
+
179
+ y_lon = math.sin(ra_date) * math.cos(eps_r) + math.tan(dec_date) * math.sin(eps_r)
180
+ x_lon = math.cos(ra_date)
181
+ lon = math.degrees(math.atan2(y_lon, x_lon)) % 360.0
182
+ lat_deg = math.degrees(lat)
183
+
184
+ # Distance is arbitrary for fixed stars (effectively infinite)
185
+ dist = 100000.0 # AU (placeholder)
186
+
187
+ return lon, lat_deg, dist
188
+
189
+
190
+ def swe_fixstar_ut(
191
+ star_name: str, tjd_ut: float, iflag: int
192
+ ) -> Tuple[Tuple[float, float, float, float, float, float], int, str]:
193
+ """
194
+ Calculate position of a fixed star.
195
+
196
+ Args:
197
+ star_name: Name of star (e.g. "Regulus", "Spica")
198
+ tjd_ut: Julian Day in UT
199
+ iflag: Calculation flags
200
+
201
+ Returns:
202
+ ((lon, lat, dist, speed_lon, speed_lat, speed_dist), iflag, error_msg)
203
+ """
204
+ # Map name to ID
205
+ star_id = -1
206
+ name_upper = star_name.upper().strip()
207
+ if "," in name_upper:
208
+ name_upper = name_upper.split(",")[0].strip()
209
+
210
+ if name_upper == "REGULUS":
211
+ star_id = SE_REGULUS
212
+ elif name_upper == "SPICA":
213
+ star_id = SE_SPICA_STAR
214
+ else:
215
+ return ((0.0, 0.0, 0.0, 0.0, 0.0, 0.0), iflag, f"Star {star_name} not found")
216
+
217
+ # Calculate position (simplified, no speed)
218
+ # We use TT for calculation (approximate UT=TT for stars is fine? No, use deltat)
219
+ from .state import get_timescale
220
+ ts = get_timescale()
221
+ t = ts.ut1_jd(tjd_ut)
222
+
223
+ try:
224
+ lon, lat, dist = calc_fixed_star_position(star_id, t.tt)
225
+ return ((lon, lat, dist, 0.0, 0.0, 0.0), iflag, "")
226
+ except Exception as e:
227
+ return ((0.0, 0.0, 0.0, 0.0, 0.0, 0.0), iflag, str(e))
228
+