Files
astro/basic/solar_eclipse_duration_width_regression_test.go
T
b612 2bf8478639 feat: 完善日月食与月掩几何链路并扩展历法接口
- 新增日月食中心带、偏食带、阴影足迹、等时线、食分线及升落边界计算,支持极区与混合食拓扑
- 新增日食单时刻阴影求解器、站心状态查询、批量采样和 ΔT 覆盖接口
- 重构恒星与行星月掩路径,补充有限盘面接触、站心修正、掩带宽度、极区投影及升落边界
- 扩展 SVG 与 GeoJSON 输出,支持详细面板、全球/极区/地球投影、边界闭合、时间标记和拓扑签名
- 扩展日月食候选搜索、局地搜索、沙罗序列预计算与范围外推,补充系列锚点和成员一致性校验
- 补齐古历纪年、儒略历独有闰日、多公历候选、历法改革跨日及精确日期运算接口
- 优化 ΔT、章动、恒星时、月球地平线、事件根搜索和本地星历缓存,降低重复计算开销并提升边界稳定
2026-09-17 12:27:40 +08:00

80 lines
2.9 KiB
Go

package basic
import (
"math"
"testing"
)
// Central durations at the greatest eclipse point, from NASA's five millennium
// catalogue ("Central Dur." column). The library publishes the same figure on
// SolarEclipseResult.CentralDurationDays.
var solarEclipseCatalogueCentralDurations = []struct {
date [3]int
seconds float64
toleranceSec float64
}{
{[3]int{1991, 7, 11}, 6*60 + 53, 1.5},
{[3]int{2009, 7, 22}, 6*60 + 39, 1.5},
{[3]int{2024, 4, 8}, 4*60 + 28, 1.5},
{[3]int{2017, 8, 21}, 2*60 + 40, 1.5},
{[3]int{2020, 6, 21}, 38, 1.5},
{[3]int{1136, 6, 1}, 3*60 + 18, 1.5},
}
func TestSolarEclipseCentralDurationMatchesCatalogue(t *testing.T) {
for _, item := range solarEclipseCatalogueCentralDurations {
seed := JDECalc(item.date[0], item.date[1], float64(item.date[2]))
result := SolarEclipseNASABulletinSplitK(seed)
if !result.HasCentral {
t.Fatalf("%04d-%02d-%02d is not a central eclipse", item.date[0], item.date[1], item.date[2])
}
seconds := result.CentralDurationDays * 86400
if math.Abs(seconds-item.seconds) > item.toleranceSec {
t.Fatalf("%04d-%02d-%02d central duration = %.1f s, catalogue %.0f s",
item.date[0], item.date[1], item.date[2], seconds, item.seconds)
}
}
}
// The path width must stay a footprint figure: never wider than the Earth, and
// zero wherever no paired cross-section exists. A one-limit event has no pair at
// all, which is why the catalogues print no width for it (1874-10-10 is listed
// with "-"), and the analytic 2r/sin(altitude) form diverges towards the horizon
// where the pair is the only meaningful measurement.
func TestSolarEclipsePathWidthStaysPhysical(t *testing.T) {
cases := []struct {
date [3]int
oneLimit bool
}{
{[3]int{2009, 7, 22}, false},
{[3]int{2017, 8, 21}, false},
{[3]int{2024, 4, 8}, false},
{[3]int{2020, 6, 21}, false},
{[3]int{1136, 6, 1}, false},
{[3]int{1874, 10, 10}, true},
}
for _, item := range cases {
seed := JDECalc(item.date[0], item.date[1], float64(item.date[2]))
path := SolarEclipseCentralPathNASABulletinSplitK(seed, SolarEclipsePathOptions{})
if len(path.CenterLine) == 0 {
t.Fatalf("%04d-%02d-%02d exported no center line", item.date[0], item.date[1], item.date[2])
}
maximum := 0.0
for _, point := range path.CenterLine {
if point.WidthKM < 0 || point.WidthKM > solarEclipsePathMaxPossibleWidthKM {
t.Fatalf("%04d-%02d-%02d width %.1f km at (%.3f, %.3f) is not a footprint width",
item.date[0], item.date[1], item.date[2], point.WidthKM, point.Longitude, point.Latitude)
}
maximum = math.Max(maximum, point.WidthKM)
}
if item.oneLimit && maximum != 0 {
t.Fatalf("%04d-%02d-%02d is a one-limit event but reports a %.1f km width",
item.date[0], item.date[1], item.date[2], maximum)
}
if !item.oneLimit && maximum <= 0 {
t.Fatalf("%04d-%02d-%02d is a two-limit event but reports no width",
item.date[0], item.date[1], item.date[2])
}
}
}