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