feat: 引入可切换星历模型并扩展天象观测链路

- 新增半解析、旧模型、自定义与多源堆叠星历实例,提供 Context、Instance、SPK 读取及状态与视位置计算
- 统一太阳、月球和行星实例接口,扩展黄经穿越、留、相位、最近角距、偕日可见、任意高度升落、轨道节点及凌日等天象查询
- 完善日月食、月掩与凌日的月缘剖面、有限盘面接触、站心修正、局地可见性、全球路径和地图输出
- 新增 SVG、GeoJSON、KML 天象输出及凌日、月掩和月缘地形相关缓存与几何求解
- 新增恒星星表导入与查询,支持 CSV、Hipparcos、BSC5、Stellarium 等格式及名称、交叉标识、锥面检索和星等过滤
- 补充 IAU 2006、地球定向、折射消光、大气观测、轨道传播及半解析行星/月球数据支持
- 将默认未来民用时标切换为 TimeScaleLeapHour,更新 2027–2400 农历速查表、中文日期解析和时区边界处理
- 增加事件基线、跨模型回归、几何边界、月缘地形和输出契约测试,完善中英文手册、示例资源、NOTICE 与忽略规则
This commit is contained in:
2026-10-15 23:54:15 +08:00
parent 16c62a97d5
commit 044f2f4381
674 changed files with 62983 additions and 8396 deletions
+12 -17
View File
@@ -1,6 +1,10 @@
package basic
import "math"
import (
"math"
"b612.me/astro/internal/geodata"
)
func uniqueSolarEclipsePathTimes(times []float64) []float64 {
return movingDiskUniqueTimes(times)
@@ -229,12 +233,12 @@ func (solver solarEclipseSolver) centralPathPointAt(jde float64) (SolarEclipsePa
}
longitude, latitude := solarEclipseIntersectionGeodetic(intersection, axis)
sunAltitudeRad := solarEclipseSunAltitudeAtGreatest(jde, longitude, latitude, axis.gst)
sunAltitudeRad := solarEclipseSunAltitudeAtGreatest(jde, longitude, latitude, axis.gst, solver.inst)
radii := solver.shadowRadiiAt(moon[2] - intersection.r2)
widthKM := 0.0
if math.Abs(math.Sin(sunAltitudeRad)) > 1e-12 {
widthKM = math.Abs(2*radii.umbraRadius*solarEclipseEarthEquatorialRadiusKM) / math.Abs(math.Sin(sunAltitudeRad))
widthKM = math.Abs(2*radii.umbraRadius*geodata.EarthEquatorialRadiusKM) / math.Abs(math.Sin(sunAltitudeRad))
}
return SolarEclipsePathPoint{
@@ -364,13 +368,13 @@ func (solver solarEclipseSolver) centralPathLimitsAtAlong(
if fraction <= 0 || fraction >= 1 {
return
}
longitude := normalizeLongitude(first.Longitude + math.Remainder(second.Longitude-first.Longitude, 360)*fraction)
longitude := geodata.NormalizeLongitude(first.Longitude + math.Remainder(second.Longitude-first.Longitude, 360)*fraction)
latitudeValue := first.Latitude + (second.Latitude-first.Latitude)*fraction
candidates = append(candidates, SolarEclipsePathPoint{
JDE: center.JDE,
Longitude: longitude,
Latitude: latitudeValue,
SunAltitude: solarEclipseSunAltitudeAtGreatest(center.JDE, longitude, latitudeValue, axis.gst) / rad,
SunAltitude: solarEclipseSunAltitudeAtGreatest(center.JDE, longitude, latitudeValue, axis.gst, solver.inst) / rad,
WidthKM: center.WidthKM,
})
}
@@ -726,7 +730,7 @@ func (solver solarEclipseSolver) shadowContactPointAt(
return SolarEclipsePathPoint{}, false
}
longitude, latitude := solarEclipseIntersectionGeodetic(intersection, axis)
sunAltitudeRad := solarEclipseSunAltitudeAtGreatest(jde, longitude, latitude, axis.gst)
sunAltitudeRad := solarEclipseSunAltitudeAtGreatest(jde, longitude, latitude, axis.gst, solver.inst)
return SolarEclipsePathPoint{
JDE: jde,
Longitude: longitude,
@@ -1317,7 +1321,7 @@ func solarEclipsePathSphericalInterpolate(
firstWeight*firstVector[2] + secondWeight*secondVector[2],
}
coordinates := solarEclipseXYZToLLR(vector[0], vector[1], vector[2])
longitude := solarEclipseNormalizeSignedRadians(coordinates[0]) / rad
longitude := normalizeSignedRadians(coordinates[0]) / rad
return SolarEclipsePathPoint{
JDE: first.JDE + fraction*(second.JDE-first.JDE),
Longitude: longitude,
@@ -1328,14 +1332,5 @@ func solarEclipsePathSphericalInterpolate(
}
func solarEclipsePathDistanceKM(a, b SolarEclipsePathPoint) float64 {
lat1 := a.Latitude * rad
lat2 := b.Latitude * rad
dlat := lat2 - lat1
dlon := solarEclipseNormalizeSignedRadians((b.Longitude - a.Longitude) * rad)
h := math.Sin(dlat/2)*math.Sin(dlat/2) +
math.Cos(lat1)*math.Cos(lat2)*math.Sin(dlon/2)*math.Sin(dlon/2)
if h > 1 {
h = 1
}
return 2 * solarEclipseEarthEquatorialRadiusKM * math.Asin(math.Sqrt(h))
return geodata.EarthEquatorialRadiusKM * geodata.CentralAngle(a.Longitude, a.Latitude, b.Longitude, b.Latitude)
}