feat: 新增月掩与日月食地理绘图并提升观测计算精度

- 新增月掩恒星和行星:支持搜索、掩甚点、全球掩带及固定地点轨迹计算
- 支持恒星星表坐标转换、有限盘面行星接触事件和月掩 SVG 输出
- 新增日月食及月掩全球投影图、时间标记和 GeoJSON 地理数据接口
- 扩展日食中心线、南北界及偏食足迹采样,支持极区投影
- 修正站心时角、月出月落、月球视半径、折射和恒星自行计算
- 优化内外行星事件搜索、边界选择、极端输入处理和计算稳定性
This commit is contained in:
2026-08-06 12:00:56 +08:00
parent 25dc7ac0bc
commit 9ee2163cc7
137 changed files with 21770 additions and 1746 deletions
+14 -6
View File
@@ -2,6 +2,7 @@ package moon
import (
"errors"
"math"
"time"
"b612.me/astro/basic"
@@ -114,19 +115,21 @@ func riseSetTime(date time.Time, lon, lat, height float64, aero, isRise bool) (t
_, offset := localMidnight.Zone()
timezone := float64(offset) / 3600.0
targetAltitude := -basic.HeightDegreeByLat(height, lat)
if aero {
targetAltitude -= 0.83333
}
horizonDip := basic.HeightDegreeByLat(height, lat)
altitudeFn := func(localJD float64) float64 {
utJD := localJD - timezone/24.0
state := lite.MoonTopocentric(utJD, lon, lat, height)
altitude, _, _ := lite.HorizontalCoordinates(state.RightAscension, state.Declination, utJD, lon, lat)
return altitude
residual := altitude + horizonDip
if aero {
residual += basic.RefractionFromTrueAltitude(altitude, 1010, 10)
residual += liteMoonSemidiameterDegrees(state.DistanceEarthRadii)
}
return residual
}
eventJD, err := lite.SearchRiseSet(localJD, targetAltitude, 15, isRise, altitudeFn)
eventJD, err := lite.SearchRiseSet(localJD, 0, 15, isRise, altitudeFn)
if err != nil {
switch {
case errors.Is(err, lite.ErrNeverRise):
@@ -141,3 +144,8 @@ func riseSetTime(date time.Time, lon, lat, height float64, aero, isRise bool) (t
}
return basic.JDE2DateByZone(eventJD, date.Location(), true), nil
}
func liteMoonSemidiameterDegrees(distanceEarthRadii float64) float64 {
const moonRadiusEarthRadii = 1737.4 / 6378.137
return math.Asin(moonRadiusEarthRadii/distanceEarthRadii) * 180 / math.Pi
}
+17 -6
View File
@@ -2,6 +2,7 @@ package sun
import (
"errors"
"math"
"time"
"b612.me/astro/basic"
@@ -101,19 +102,21 @@ func riseSetTime(date time.Time, lon, lat, height float64, aero, isRise bool) (t
_, offset := localMidnight.Zone()
timezone := float64(offset) / 3600.0
targetAltitude := -basic.HeightDegreeByLat(height, lat)
if aero {
targetAltitude -= 0.8333
}
horizonDip := basic.HeightDegreeByLat(height, lat)
altitudeFn := func(localJD float64) float64 {
utJD := localJD - timezone/24.0
ra, dec := lite.SunApparentRaDec(utJD)
altitude, _, _ := lite.HorizontalCoordinates(ra, dec, utJD, lon, lat)
return altitude
residual := altitude + horizonDip
if aero {
residual += basic.RefractionFromTrueAltitude(altitude, 1010, 10)
residual += liteSunSemidiameterDegrees(lite.SunDistanceAU(utJD))
}
return residual
}
eventJD, err := lite.SearchRiseSet(localJD, targetAltitude, 30, isRise, altitudeFn)
eventJD, err := lite.SearchRiseSet(localJD, 0, 30, isRise, altitudeFn)
if err != nil {
switch {
case errors.Is(err, lite.ErrNeverRise):
@@ -126,3 +129,11 @@ func riseSetTime(date time.Time, lon, lat, height float64, aero, isRise bool) (t
}
return basic.JDE2DateByZone(eventJD, date.Location(), true), nil
}
func liteSunSemidiameterDegrees(distanceAU float64) float64 {
const (
sunRadiusKM = 695700.0
auKM = 149597870.7
)
return math.Asin(sunRadiusKM/(distanceAU*auKM)) * 180 / math.Pi
}