astro_chart 0.1.0 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (40) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +25 -0
  3. data/LICENSE +17 -16
  4. data/astro_chart.gemspec +7 -6
  5. data/lib/astro_chart/ephemeris.rb +76 -18
  6. data/lib/astro_chart/pure/core.rb +209 -0
  7. data/lib/astro_chart/pure/houses.rb +122 -0
  8. data/lib/astro_chart/pure/moon.rb +341 -0
  9. data/lib/astro_chart/pure/moon_elp.rb +931 -0
  10. data/lib/astro_chart/pure/pluto.rb +220 -0
  11. data/lib/astro_chart/pure/vsop87.rb +152 -0
  12. data/lib/astro_chart/pure/vsop87_data.rb +210 -0
  13. data/lib/astro_chart/pure.rb +63 -0
  14. data/lib/astro_chart/synastry.rb +118 -0
  15. data/lib/astro_chart/version.rb +1 -1
  16. data/lib/astro_chart.rb +6 -1
  17. metadata +19 -45
  18. data/ext/astro_chart/astro_chart_ext.c +0 -99
  19. data/ext/astro_chart/extconf.rb +0 -8
  20. data/ext/astro_chart/swecl.c +0 -6428
  21. data/ext/astro_chart/swedate.c +0 -588
  22. data/ext/astro_chart/swedate.h +0 -81
  23. data/ext/astro_chart/swedll.h +0 -403
  24. data/ext/astro_chart/sweephe4.c +0 -702
  25. data/ext/astro_chart/sweephe4.h +0 -239
  26. data/ext/astro_chart/swehel.c +0 -3511
  27. data/ext/astro_chart/swehouse.c +0 -3143
  28. data/ext/astro_chart/swehouse.h +0 -98
  29. data/ext/astro_chart/swejpl.c +0 -958
  30. data/ext/astro_chart/swejpl.h +0 -103
  31. data/ext/astro_chart/swemmoon.c +0 -1930
  32. data/ext/astro_chart/swemplan.c +0 -967
  33. data/ext/astro_chart/swemptab.h +0 -10640
  34. data/ext/astro_chart/swenut2000a.h +0 -2819
  35. data/ext/astro_chart/sweodef.h +0 -326
  36. data/ext/astro_chart/sweph.c +0 -8614
  37. data/ext/astro_chart/sweph.h +0 -849
  38. data/ext/astro_chart/swephexp.h +0 -1020
  39. data/ext/astro_chart/swephlib.c +0 -4634
  40. data/ext/astro_chart/swephlib.h +0 -189
@@ -0,0 +1,341 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "core"
4
+ require_relative "moon_elp"
5
+
6
+ module AstroChart
7
+ module Pure
8
+ # Moon — MIT-safe 純 Ruby 月球位置(零外部依賴)。
9
+ #
10
+ # 只使用公開文獻公式,不參考 Swiss Ephemeris C 原始碼:
11
+ # - 月球黃經/黃緯/距離:Jean Meeus, "Astronomical Algorithms" 2nd ed.,
12
+ # Ch. 47(ELP-2000/82 截斷級數:Table 47.A 60 項 + Table 47.B 60 項),
13
+ # 再以公開發表的 ELP-2000/82B 級數(Chapront-Touzé & Chapront
14
+ # 1983/1988, CDS VizieR VI/79)補充(moon_elp.rb):
15
+ # * 主問題:Meeus 截斷線以下黃經 +197 項(至 0.005")、
16
+ # 黃緯 +221 項(至 0.002")
17
+ # * 攝動:地球扁率(ELP4/5/8)+ 行星攝動 Table 1/2(ELP10/11/16/17),
18
+ # 取代 Meeus 的 A1/A2/A3 加法捷徑(那些正是這些級數檔的最大列)
19
+ # 補充後截斷誤差 < 0.1"(黃經)/ < 0.05"(黃緯)。
20
+ # 實測 vs JPL Horizons(1972-11 近地點朔望,最惡條件):黃經差 0.12"。
21
+ # - 視位置 = 幾何位置 + 章動 Δψ(Meeus Ch. 47 慣例;月亮距地近、
22
+ # 光行差以「地心速度」定義對月球不適用,光行時 ~1.3s → ~0.0002°,
23
+ # 在精度預算內忽略)。
24
+ # - 真(密切)北交點:由月球黃道直角座標 r 與中央差分速度 v 取
25
+ # 角動量 h = r × v,升交點方向 n = ẑ × h = (−h_y, h_x, 0),
26
+ # Ω = atan2(n_y, n_x) = atan2(h_x, −h_y)(標準軌道力學,如
27
+ # Vallado "Fundamentals of Astrodynamics";黃道座標系)。
28
+ # 與平均交點可差達 ±1.75°,不可混用。
29
+ #
30
+ # 精度註記(2026-07 驗證):TRUE_NODE vs Swiss Ephemeris 全域(1900-2026
31
+ # 每 2 天 dense 掃描)max ≈ 0.018°、mean ≈ 0.0026°。殘差尖峰集中在
32
+ # 「近地點 + 朔望」時刻(誤差被 1/i ≈ 11 倍放大的敏感點),且已用
33
+ # JPL Horizons 狀態向量獨立仲裁:本實作的密切交點與 Horizons 差
34
+ # ~0.0006°,SE(內建 Moshier 類 fallback 星曆,月黃經實測差 Horizons
35
+ # 1.75")差 ~0.018° — 即殘差主要是 SE 側的近似誤差,繼續加項無法
36
+ # 收斂(往 SE 誤差擬合反而踩授權線),此為此法的自然底線。
37
+ #
38
+ # 時間尺度慣例:公開 API 一律吃 **JD(UT)**,內部經 Core.jd_tt 換算 TT
39
+ # 後代入級數(月亮 ~13°/day,ΔT ≈ 70s 就是 ~0.01°,不可省)。
40
+ #
41
+ # 公開 API(module_function,皆回傳 Float 度 [0,360)):
42
+ # Moon.apparent_longitude(jd_ut) -> 月球視黃經(幾何 + Δψ)
43
+ # Moon.true_node(jd_ut) -> 真北交點視黃經(密切 Ω + Δψ)
44
+ # Moon.geometric_position(jd_tt) -> [λ_deg, β_deg, Δ_km](幾何,平春分點)
45
+ module Moon
46
+ # 中央差分步長(日)。0.005d ≈ 7.2 分鐘:對 27.3 天週期的軌道,
47
+ # 二階截斷誤差 ~(2π·0.005/27.3)² ~ 1e-6,遠小於交點 0.02° 目標。
48
+ NODE_DIFF_STEP_DAYS = 0.005
49
+
50
+ # --- Meeus Table 47.A:黃經 Σl(單位 1e-6 度)與距離 Σr(單位 1e-3 km) ---
51
+ # 各列 [D, M, M', F, l_coeff, r_coeff];含 M 的項需乘 E^|M|(E 見 47.6)。
52
+ LON_DIST_TERMS = [
53
+ [0, 0, 1, 0, 6_288_774, -20_905_355],
54
+ [2, 0, -1, 0, 1_274_027, -3_699_111],
55
+ [2, 0, 0, 0, 658_314, -2_955_968],
56
+ [0, 0, 2, 0, 213_618, -569_925],
57
+ [0, 1, 0, 0, -185_116, 48_888],
58
+ [0, 0, 0, 2, -114_332, -3_149],
59
+ [2, 0, -2, 0, 58_793, 246_158],
60
+ [2, -1, -1, 0, 57_066, -152_138],
61
+ [2, 0, 1, 0, 53_322, -170_733],
62
+ [2, -1, 0, 0, 45_758, -204_586],
63
+ [0, 1, -1, 0, -40_923, -129_620],
64
+ [1, 0, 0, 0, -34_720, 108_743],
65
+ [0, 1, 1, 0, -30_383, 104_755],
66
+ [2, 0, 0, -2, 15_327, 10_321],
67
+ [0, 0, 1, 2, -12_528, 0],
68
+ [0, 0, 1, -2, 10_980, 79_661],
69
+ [4, 0, -1, 0, 10_675, -34_782],
70
+ [0, 0, 3, 0, 10_034, -23_210],
71
+ [4, 0, -2, 0, 8_548, -21_636],
72
+ [2, 1, -1, 0, -7_888, 24_208],
73
+ [2, 1, 0, 0, -6_766, 30_824],
74
+ [1, 0, -1, 0, -5_163, -8_379],
75
+ [1, 1, 0, 0, 4_987, -16_675],
76
+ [2, -1, 1, 0, 4_036, -12_831],
77
+ [2, 0, 2, 0, 3_994, -10_445],
78
+ [4, 0, 0, 0, 3_861, -11_650],
79
+ [2, 0, -3, 0, 3_665, 14_403],
80
+ [0, 1, -2, 0, -2_689, -7_003],
81
+ [2, 0, -1, 2, -2_602, 0],
82
+ [2, -1, -2, 0, 2_390, 10_056],
83
+ [1, 0, 1, 0, -2_348, 6_322],
84
+ [2, -2, 0, 0, 2_236, -9_884],
85
+ [0, 1, 2, 0, -2_120, 5_751],
86
+ [0, 2, 0, 0, -2_069, 0],
87
+ [2, -2, -1, 0, 2_048, -4_950],
88
+ [2, 0, 1, -2, -1_773, 4_130],
89
+ [2, 0, 0, 2, -1_595, 0],
90
+ [4, -1, -1, 0, 1_215, -3_958],
91
+ [0, 0, 2, 2, -1_110, 0],
92
+ [3, 0, -1, 0, -892, 3_258],
93
+ [2, 1, 1, 0, -810, 2_616],
94
+ [4, -1, -2, 0, 759, -1_897],
95
+ [0, 2, -1, 0, -713, -2_117],
96
+ [2, 2, -1, 0, -700, 2_354],
97
+ [2, 1, -2, 0, 691, 0],
98
+ [2, -1, 0, -2, 596, 0],
99
+ [4, 0, 1, 0, 549, -1_423],
100
+ [0, 0, 4, 0, 537, -1_117],
101
+ [4, -1, 0, 0, 520, -1_571],
102
+ [1, 0, -2, 0, -487, -1_739],
103
+ [2, 1, 0, -2, -399, 0],
104
+ [0, 0, 2, -2, -381, -4_421],
105
+ [1, 1, 1, 0, 351, 0],
106
+ [3, 0, -2, 0, -340, 0],
107
+ [4, 0, -3, 0, 330, 0],
108
+ [2, -1, 2, 0, 327, 0],
109
+ [0, 2, 1, 0, -323, 1_165],
110
+ [1, 1, -1, 0, 299, 0],
111
+ [2, 0, 3, 0, 294, 0],
112
+ [2, 0, -1, -2, 0, 8_752]
113
+ ].freeze
114
+
115
+ # --- Meeus Table 47.B:黃緯 Σb(單位 1e-6 度) ---
116
+ # 各列 [D, M, M', F, b_coeff];含 M 的項需乘 E^|M|。
117
+ LAT_TERMS = [
118
+ [0, 0, 0, 1, 5_128_122],
119
+ [0, 0, 1, 1, 280_602],
120
+ [0, 0, 1, -1, 277_693],
121
+ [2, 0, 0, -1, 173_237],
122
+ [2, 0, -1, 1, 55_413],
123
+ [2, 0, -1, -1, 46_271],
124
+ [2, 0, 0, 1, 32_573],
125
+ [0, 0, 2, 1, 17_198],
126
+ [2, 0, 1, -1, 9_266],
127
+ [0, 0, 2, -1, 8_822],
128
+ [2, -1, 0, -1, 8_216],
129
+ [2, 0, -2, -1, 4_324],
130
+ [2, 0, 1, 1, 4_200],
131
+ [2, 1, 0, -1, -3_359],
132
+ [2, -1, -1, 1, 2_463],
133
+ [2, -1, 0, 1, 2_211],
134
+ [2, -1, -1, -1, 2_065],
135
+ [0, 1, -1, -1, -1_870],
136
+ [4, 0, -1, -1, 1_828],
137
+ [0, 1, 0, 1, -1_794],
138
+ [0, 0, 0, 3, -1_749],
139
+ [0, 1, -1, 1, -1_565],
140
+ [1, 0, 0, 1, -1_491],
141
+ [0, 1, 1, 1, -1_475],
142
+ [0, 1, 1, -1, -1_410],
143
+ [0, 1, 0, -1, -1_344],
144
+ [1, 0, 0, -1, -1_335],
145
+ [0, 0, 3, 1, 1_107],
146
+ [4, 0, 0, -1, 1_021],
147
+ [4, 0, -1, 1, 833],
148
+ [0, 0, 1, -3, 777],
149
+ [4, 0, -2, 1, 671],
150
+ [2, 0, 0, -3, 607],
151
+ [2, 0, 2, -1, 596],
152
+ [2, -1, 1, -1, 491],
153
+ [2, 0, -2, 1, -451],
154
+ [0, 0, 3, -1, 439],
155
+ [2, 0, 2, 1, 422],
156
+ [2, 0, -3, -1, 421],
157
+ [2, 1, -1, 1, -366],
158
+ [2, 1, 0, 1, -351],
159
+ [4, 0, 0, 1, 331],
160
+ [2, -1, 1, 1, 315],
161
+ [2, -2, 0, -1, 302],
162
+ [0, 0, 1, 3, -283],
163
+ [2, 1, 1, -1, -229],
164
+ [1, 1, 0, -1, 223],
165
+ [1, 1, 0, 1, 223],
166
+ [0, 1, -2, -1, -220],
167
+ [2, 1, -1, -1, -220],
168
+ [1, 0, 1, 1, -185],
169
+ [2, -1, -2, -1, 181],
170
+ [0, 1, 2, 1, -177],
171
+ [4, 0, -2, -1, 176],
172
+ [4, -1, -1, -1, 166],
173
+ [1, 0, 1, -1, -164],
174
+ [4, 0, 1, -1, 132],
175
+ [1, 0, -1, -1, -119],
176
+ [4, -1, 0, -1, 115],
177
+ [2, -2, 0, 1, 107]
178
+ ].freeze
179
+
180
+ module_function
181
+
182
+ # --- 月球視黃經(度,[0,360))。引數 **JD(UT)**。 ---
183
+ # 幾何黃經(平春分點 of date)+ 章動 Δψ = 視黃經(真春分點 of date)。
184
+ def apparent_longitude(jd_ut)
185
+ tt = Core.jd_tt(jd_ut)
186
+ lam, _beta, _delta = geometric_position(tt)
187
+ dpsi_deg, = Core.nutation(tt)
188
+ Core.norm360(lam + dpsi_deg)
189
+ end
190
+
191
+ # --- 真(密切)北交點視黃經(度,[0,360))。引數 **JD(UT)**。 ---
192
+ # 由 r × v 的角動量向量取升交點方向;速度用 TT 上 ±NODE_DIFF_STEP_DAYS
193
+ # 的中央差分。幾何 Ω(平春分點)+ Δψ = 視位置(真春分點),與
194
+ # calc_ut 慣例一致。
195
+ def true_node(jd_ut)
196
+ tt = Core.jd_tt(jd_ut)
197
+ r = rectangular(tt)
198
+ r1 = rectangular(tt - NODE_DIFF_STEP_DAYS)
199
+ r2 = rectangular(tt + NODE_DIFF_STEP_DAYS)
200
+ v = [
201
+ (r2[0] - r1[0]) / (2.0 * NODE_DIFF_STEP_DAYS),
202
+ (r2[1] - r1[1]) / (2.0 * NODE_DIFF_STEP_DAYS),
203
+ (r2[2] - r1[2]) / (2.0 * NODE_DIFF_STEP_DAYS)
204
+ ]
205
+ # h = r × v(黃道直角座標,z 軸指向黃道北極)
206
+ hx = r[1] * v[2] - r[2] * v[1]
207
+ hy = r[2] * v[0] - r[0] * v[2]
208
+ hz = r[0] * v[1] - r[1] * v[0]
209
+ h_norm = Math.sqrt(hx * hx + hy * hy + hz * hz)
210
+ raise Core::DomainError, "degenerate lunar angular momentum at JD(TT)=#{tt}" if h_norm.zero?
211
+
212
+ # 升交點方向 n = ẑ × h = (−h_y, h_x, 0) → Ω = atan2(h_x, −h_y)
213
+ omega = Math.atan2(hx, -hy) * Core::RAD2DEG
214
+ dpsi_deg, = Core.nutation(tt)
215
+ Core.norm360(omega + dpsi_deg)
216
+ end
217
+
218
+ # --- 幾何黃道座標(平春分點 of date)。引數 **JD(TT)**。 ---
219
+ # 回傳 [λ_deg(未正規化前先算後 norm360), β_deg, Δ_km]。
220
+ # Meeus Ch. 47:λ = L' + Σl/1e6、β = Σb/1e6、Δ = 385000.56 + Σr/1e3。
221
+ def geometric_position(jd_tt)
222
+ t = (jd_tt - 2_451_545.0) / 36_525.0
223
+
224
+ # 基本引數(Meeus 47.1–47.5,度)
225
+ lp = 218.3164477 + 481_267.88123421 * t - 0.0015786 * t * t +
226
+ t**3 / 538_841.0 - t**4 / 65_194_000.0
227
+ d = 297.8501921 + 445_267.1114034 * t - 0.0018819 * t * t +
228
+ t**3 / 545_868.0 - t**4 / 113_065_000.0
229
+ m = 357.5291092 + 35_999.0502909 * t - 0.0001536 * t * t +
230
+ t**3 / 24_490_000.0
231
+ mp = 134.9633964 + 477_198.8675055 * t + 0.0087414 * t * t +
232
+ t**3 / 69_699.0 - t**4 / 14_712_000.0
233
+ f = 93.2720950 + 483_202.0175233 * t - 0.0036539 * t * t -
234
+ t**3 / 3_526_000.0 + t**4 / 863_310_000.0
235
+
236
+ # 離心率修正 E(Meeus 47.6)
237
+ e = 1.0 - 0.002516 * t - 0.0000074 * t * t
238
+ e2 = e * e
239
+
240
+ d_rad = d * Core::DEG2RAD
241
+ m_rad = m * Core::DEG2RAD
242
+ mp_rad = mp * Core::DEG2RAD
243
+ f_rad = f * Core::DEG2RAD
244
+
245
+ sum_l = 0.0
246
+ sum_r = 0.0
247
+ LON_DIST_TERMS.each do |cd, cm, cmp, cf, lc, rc|
248
+ arg = cd * d_rad + cm * m_rad + cmp * mp_rad + cf * f_rad
249
+ ecc = cm.zero? ? 1.0 : (cm.abs == 1 ? e : e2)
250
+ sum_l += lc * ecc * Math.sin(arg)
251
+ sum_r += rc * ecc * Math.cos(arg)
252
+ end
253
+
254
+ sum_b = 0.0
255
+ LAT_TERMS.each do |cd, cm, cmp, cf, bc|
256
+ arg = cd * d_rad + cm * m_rad + cmp * mp_rad + cf * f_rad
257
+ ecc = cm.zero? ? 1.0 : (cm.abs == 1 ? e : e2)
258
+ sum_b += bc * ecc * Math.sin(arg)
259
+ end
260
+
261
+ # ELP-2000/82 主問題補充項(moon_elp.rb;Meeus 截斷線以下)。
262
+ # E 因子沿用 Meeus 慣例推廣為 E^|M|(補充項 |M| 最大 4)。
263
+ SUPP_LON_TERMS.each do |cd, cm, cmp, cf, lc|
264
+ arg = cd * d_rad + cm * m_rad + cmp * mp_rad + cf * f_rad
265
+ ecc = cm.zero? ? 1.0 : e**cm.abs
266
+ sum_l += lc * ecc * Math.sin(arg)
267
+ end
268
+ SUPP_LAT_TERMS.each do |cd, cm, cmp, cf, bc|
269
+ arg = cd * d_rad + cm * m_rad + cmp * mp_rad + cf * f_rad
270
+ ecc = cm.zero? ? 1.0 : e**cm.abs
271
+ sum_b += bc * ecc * Math.sin(arg)
272
+ end
273
+
274
+ # 攝動項(moon_elp.rb:地球扁率 ELP4/5/8 + 行星攝動 ELP10/11/16/17)。
275
+ # 取代 Meeus 的 A1/A2/A3 加法捷徑 — 那些捷徑正是這些級數檔的最大列
276
+ # (A1 = 18V−16T−M'、A2/A3 同理),改用完整級數避免雙重計入。
277
+ # ζ = W1 + p·t ≡ Meeus of-date L'(lp);攝動振幅不乘 E 因子。
278
+ sum_l += pert_sum(t, lp, d, m, mp, f,
279
+ PERT_LON_ZETA, PERT_LON_PLAN1, PERT_LON_PLAN2)
280
+ sum_b += pert_sum(t, lp, d, m, mp, f,
281
+ PERT_LAT_ZETA, PERT_LAT_PLAN1, PERT_LAT_PLAN2)
282
+
283
+ lam = Core.norm360(lp + sum_l * 1.0e-6)
284
+ beta = sum_b * 1.0e-6
285
+ delta = 385_000.56 + sum_r * 1.0e-3
286
+ [lam, beta, delta]
287
+ end
288
+
289
+ # --- ELP 攝動級數求和(單位 1e-6 度)。內部方法。 ---
290
+ # 引數皆為度:lp = ζ(≡ Meeus of-date L')、d/m/mp/f = Delaunay。
291
+ # zeta_terms: [iz, iD, iM, iMp, iF, phase, coeff, tpow]
292
+ # plan1_terms: [8 行星, iD, iMp, iF, phase, coeff](Table 1 無 M)
293
+ # plan2_terms: [7 行星, iD, iM, iMp, iF, phase, coeff]
294
+ def pert_sum(t, lp, d, m, mp, f, zeta_terms, plan1_terms, plan2_terms)
295
+ sum = 0.0
296
+
297
+ zeta_terms.each do |iz, id_, im, imp, if_, pha, coeff, tpow|
298
+ arg = pha + iz * lp + id_ * d + im * m + imp * mp + if_ * f
299
+ x = coeff * Math.sin(arg * Core::DEG2RAD)
300
+ sum += tpow == 1 ? x * t : x
301
+ end
302
+
303
+ planet = Array.new(8) do |i|
304
+ base, rate = PLANETARY_ARGS[i]
305
+ base + rate * t
306
+ end
307
+
308
+ plan1_terms.each do |p1, p2, p3, p4, p5, p6, p7, p8, id_, imp, if_, pha, coeff|
309
+ arg = pha + p1 * planet[0] + p2 * planet[1] + p3 * planet[2] +
310
+ p4 * planet[3] + p5 * planet[4] + p6 * planet[5] +
311
+ p7 * planet[6] + p8 * planet[7] +
312
+ id_ * d + imp * mp + if_ * f
313
+ sum += coeff * Math.sin(arg * Core::DEG2RAD)
314
+ end
315
+
316
+ plan2_terms.each do |p1, p2, p3, p4, p5, p6, p7, id_, im, imp, if_, pha, coeff|
317
+ arg = pha + p1 * planet[0] + p2 * planet[1] + p3 * planet[2] +
318
+ p4 * planet[3] + p5 * planet[4] + p6 * planet[5] +
319
+ p7 * planet[6] +
320
+ id_ * d + im * m + imp * mp + if_ * f
321
+ sum += coeff * Math.sin(arg * Core::DEG2RAD)
322
+ end
323
+
324
+ sum
325
+ end
326
+
327
+ # --- 黃道直角座標(平春分點 of date,km)。引數 **JD(TT)**。 ---
328
+ def rectangular(jd_tt)
329
+ lam, beta, delta = geometric_position(jd_tt)
330
+ lam_rad = lam * Core::DEG2RAD
331
+ beta_rad = beta * Core::DEG2RAD
332
+ cb = Math.cos(beta_rad)
333
+ [
334
+ delta * cb * Math.cos(lam_rad),
335
+ delta * cb * Math.sin(lam_rad),
336
+ delta * Math.sin(beta_rad)
337
+ ]
338
+ end
339
+ end
340
+ end
341
+ end