feat: 完善日月食与月掩几何链路并扩展历法接口

- 新增日月食中心带、偏食带、阴影足迹、等时线、食分线及升落边界计算,支持极区与混合食拓扑
- 新增日食单时刻阴影求解器、站心状态查询、批量采样和 ΔT 覆盖接口
- 重构恒星与行星月掩路径,补充有限盘面接触、站心修正、掩带宽度、极区投影及升落边界
- 扩展 SVG 与 GeoJSON 输出,支持详细面板、全球/极区/地球投影、边界闭合、时间标记和拓扑签名
- 扩展日月食候选搜索、局地搜索、沙罗序列预计算与范围外推,补充系列锚点和成员一致性校验
- 补齐古历纪年、儒略历独有闰日、多公历候选、历法改革跨日及精确日期运算接口
- 优化 ΔT、章动、恒星时、月球地平线、事件根搜索和本地星历缓存,降低重复计算开销并提升边界稳定
This commit is contained in:
2026-09-17 12:27:40 +08:00
parent 9ee2163cc7
commit 2bf8478639
428 changed files with 85981 additions and 7998 deletions
+21 -3
View File
@@ -65,7 +65,7 @@ type SolarEclipseInfo struct {
Type SolarEclipseType
// Centrality 中心性, eclipse centrality.
Centrality SolarEclipseCentrality
// HasSaros 存在沙罗序列信息, has Saros series metadata.
// HasSaros 存在沙罗序列信息(可能是锚点外推结果), has Saros series metadata (possibly extrapolated).
HasSaros bool
// Saros 是沙罗序列信息,包括系列号、系列内序号和总成员数。
// Saros is Saros series metadata with the series number, member index, and total member count.
@@ -86,6 +86,12 @@ type SolarEclipseInfo struct {
Magnitude float64
// Gamma 食甚时影轴到地心的距离, gamma at greatest eclipse.
Gamma float64
// CentralDuration 是食甚点的中心食持续时间,即日食目录(如 NASA「Central Dur.」)
// 的取值口径;沿整条中心线的最大值见 SolarEclipsePath.MaxCentralDuration。
// CentralDuration is the central-phase duration at the greatest eclipse, the
// value catalogues publish; SolarEclipsePath.MaxCentralDuration is the maximum
// along the whole track.
CentralDuration time.Duration
// PathWidthKM 食甚处中心食带宽度, central path width at greatest eclipse.
PathWidthKM float64
@@ -292,6 +298,7 @@ func solarEclipseInfoFromBasic(result basic.SolarEclipseResult, location *time.L
CentralEndOnEarth: solarEclipseTTJDEToTime(result.CentralEndOnEarth, location),
Magnitude: result.Magnitude,
Gamma: result.Gamma,
CentralDuration: solarEclipseDurationFromDays(result.CentralDurationDays),
PathWidthKM: result.PathWidthKM,
GreatestLongitude: result.GreatestLongitude,
GreatestLatitude: result.GreatestLatitude,
@@ -354,10 +361,21 @@ func solarEclipseRange(info SolarEclipseInfo) (time.Time, time.Time, bool) {
}
func solarEclipseTTJDEToTime(ttJDE float64, location *time.Location) time.Time {
if ttJDE == 0 {
return solarEclipseTTJDEToTimeWithDeltaT(ttJDE, basic.DeltaT(ttJDE, true), location)
}
// solarEclipseTTJDEToTimeWithDeltaT 用显式 ΔT 把 TT 换算为时刻:显式 ΔT 的单时刻入口
// 必须走这条路径,否则回填出来的 UT 会落到进程级模型上,与几何使用的 ΔT 不一致。
// solarEclipseTTJDEToTimeWithDeltaT converts TT with an explicit ΔT; the single-instant
// entries must use it so the reported UT matches the ΔT their geometry used.
func solarEclipseTTJDEToTimeWithDeltaT(
ttJDE, deltaTSeconds float64, location *time.Location,
) time.Time {
// 与 lunar 侧一致:0 与 NaN/±Inf 都表示“该时刻不存在”。
if ttJDE == 0 || math.IsNaN(ttJDE) || math.IsInf(ttJDE, 0) {
return time.Time{}
}
utcJDE := basic.TD2UT(ttJDE, false)
utcJDE := ttJDE - deltaTSeconds/86400
return basic.JDE2DateByZone(utcJDE, location, false)
}