feat: 完善时标与天象几何计算并扩展输出接口
- 新增时标、ΔT 模型、质心时间与 UT1 支持 - 改进日月食、月掩、行星事件及路径边界计算 - 完善恒星三维自行与动态距离传播 - 扩展 SVG、GeoJSON、KML 输出与底层距离换算工具 - 整理中英文手册、示例资源及回归测试
This commit is contained in:
@@ -79,7 +79,7 @@ func TestMoonMaximumDeclinationsMatchHorizonsBaseline(t *testing.T) {
|
||||
t.Fatalf("unknown declination kind %q", sample.Kind)
|
||||
}
|
||||
|
||||
gotTime := JDE2DateByZone(got.JDE, time.UTC, false)
|
||||
gotTime := JD2DateByZone(got.JD, time.UTC, false)
|
||||
timeDiff := gotTime.Sub(wantTime)
|
||||
if timeDiff < 0 {
|
||||
timeDiff = -timeDiff
|
||||
@@ -124,23 +124,23 @@ func TestMoonMaximumDeclinationSignsAndOrder(t *testing.T) {
|
||||
if event.Declination <= 0 {
|
||||
t.Fatalf("north event #%d should be positive, got %.8f", i+1, event.Declination)
|
||||
}
|
||||
if i > 0 && !(north[i-1].JDE < event.JDE) {
|
||||
t.Fatalf("north events not strictly increasing: %.12f then %.12f", north[i-1].JDE, event.JDE)
|
||||
if i > 0 && !(north[i-1].JD < event.JD) {
|
||||
t.Fatalf("north events not strictly increasing: %.12f then %.12f", north[i-1].JD, event.JD)
|
||||
}
|
||||
}
|
||||
for i, event := range south {
|
||||
if event.Declination >= 0 {
|
||||
t.Fatalf("south event #%d should be negative, got %.8f", i+1, event.Declination)
|
||||
}
|
||||
if i > 0 && !(south[i-1].JDE < event.JDE) {
|
||||
t.Fatalf("south events not strictly increasing: %.12f then %.12f", south[i-1].JDE, event.JDE)
|
||||
if i > 0 && !(south[i-1].JD < event.JD) {
|
||||
t.Fatalf("south events not strictly increasing: %.12f then %.12f", south[i-1].JD, event.JD)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestMoonMaximumDeclinationSearchMatchesMonthlyEvents(t *testing.T) {
|
||||
query := time.Date(2026, time.January, 10, 0, 0, 0, 0, time.UTC)
|
||||
queryJDE := Date2JDE(query)
|
||||
queryJD := Date2JD(query)
|
||||
|
||||
northEvents := append([]DeclinationEvent{}, MoonMaximumNorthDeclinations(2025, time.December)...)
|
||||
northEvents = append(northEvents, MoonMaximumNorthDeclinations(2026, time.January)...)
|
||||
@@ -150,13 +150,13 @@ func TestMoonMaximumDeclinationSearchMatchesMonthlyEvents(t *testing.T) {
|
||||
southEvents = append(southEvents, MoonMaximumSouthDeclinations(2026, time.January)...)
|
||||
southEvents = append(southEvents, MoonMaximumSouthDeclinations(2026, time.February)...)
|
||||
|
||||
assertSameDeclinationEvent(t, "last north", LastMoonMaximumNorthDeclination(queryJDE), expectedDirectionalDeclinationEvent(northEvents, queryJDE, -1, true))
|
||||
assertSameDeclinationEvent(t, "next north", NextMoonMaximumNorthDeclination(queryJDE), expectedDirectionalDeclinationEvent(northEvents, queryJDE, 1, false))
|
||||
assertSameDeclinationEvent(t, "closest north", ClosestMoonMaximumNorthDeclination(queryJDE), expectedClosestDeclinationEvent(northEvents, queryJDE))
|
||||
assertSameDeclinationEvent(t, "last north", LastMoonMaximumNorthDeclination(queryJD), expectedDirectionalDeclinationEvent(northEvents, queryJD, -1, true))
|
||||
assertSameDeclinationEvent(t, "next north", NextMoonMaximumNorthDeclination(queryJD), expectedDirectionalDeclinationEvent(northEvents, queryJD, 1, false))
|
||||
assertSameDeclinationEvent(t, "closest north", ClosestMoonMaximumNorthDeclination(queryJD), expectedClosestDeclinationEvent(northEvents, queryJD))
|
||||
|
||||
assertSameDeclinationEvent(t, "last south", LastMoonMaximumSouthDeclination(queryJDE), expectedDirectionalDeclinationEvent(southEvents, queryJDE, -1, true))
|
||||
assertSameDeclinationEvent(t, "next south", NextMoonMaximumSouthDeclination(queryJDE), expectedDirectionalDeclinationEvent(southEvents, queryJDE, 1, false))
|
||||
assertSameDeclinationEvent(t, "closest south", ClosestMoonMaximumSouthDeclination(queryJDE), expectedClosestDeclinationEvent(southEvents, queryJDE))
|
||||
assertSameDeclinationEvent(t, "last south", LastMoonMaximumSouthDeclination(queryJD), expectedDirectionalDeclinationEvent(southEvents, queryJD, -1, true))
|
||||
assertSameDeclinationEvent(t, "next south", NextMoonMaximumSouthDeclination(queryJD), expectedDirectionalDeclinationEvent(southEvents, queryJD, 1, false))
|
||||
assertSameDeclinationEvent(t, "closest south", ClosestMoonMaximumSouthDeclination(queryJD), expectedClosestDeclinationEvent(southEvents, queryJD))
|
||||
}
|
||||
|
||||
func TestMoonMaximumDeclinationSearchAtExactEventTime(t *testing.T) {
|
||||
@@ -165,29 +165,29 @@ func TestMoonMaximumDeclinationSearchAtExactEventTime(t *testing.T) {
|
||||
t.Fatalf("expected at least two north events spanning Jan 2026 search window, got %d", len(north))
|
||||
}
|
||||
|
||||
exactJDE := north[0].JDE
|
||||
assertSameDeclinationEvent(t, "exact last north", LastMoonMaximumNorthDeclination(exactJDE), north[0])
|
||||
assertSameDeclinationEvent(t, "exact closest north", ClosestMoonMaximumNorthDeclination(exactJDE), north[0])
|
||||
assertSameDeclinationEvent(t, "exact next north", NextMoonMaximumNorthDeclination(exactJDE), north[1])
|
||||
exactJD := north[0].JD
|
||||
assertSameDeclinationEvent(t, "exact last north", LastMoonMaximumNorthDeclination(exactJD), north[0])
|
||||
assertSameDeclinationEvent(t, "exact closest north", ClosestMoonMaximumNorthDeclination(exactJD), north[0])
|
||||
assertSameDeclinationEvent(t, "exact next north", NextMoonMaximumNorthDeclination(exactJD), north[1])
|
||||
}
|
||||
|
||||
func assertSameDeclinationEvent(t *testing.T, name string, got, want DeclinationEvent) {
|
||||
t.Helper()
|
||||
if math.Abs(got.JDE-want.JDE) > 1e-12 {
|
||||
t.Fatalf("%s JDE mismatch: got %.12f want %.12f", name, got.JDE, want.JDE)
|
||||
if math.Abs(got.JD-want.JD) > 1e-12 {
|
||||
t.Fatalf("%s JD mismatch: got %.12f want %.12f", name, got.JD, want.JD)
|
||||
}
|
||||
if math.Float64bits(got.Declination) != math.Float64bits(want.Declination) {
|
||||
t.Fatalf("%s declination mismatch: got %.12f want %.12f", name, got.Declination, want.Declination)
|
||||
}
|
||||
}
|
||||
|
||||
func expectedDirectionalDeclinationEvent(events []DeclinationEvent, queryJDE float64, direction int, includeCurrent bool) DeclinationEvent {
|
||||
func expectedDirectionalDeclinationEvent(events []DeclinationEvent, queryJD float64, direction int, includeCurrent bool) DeclinationEvent {
|
||||
var (
|
||||
found bool
|
||||
best DeclinationEvent
|
||||
)
|
||||
for _, event := range events {
|
||||
delta := event.JDE - queryJDE
|
||||
delta := event.JD - queryJD
|
||||
if !moonMaximumDeclinationMatchesDirection(delta, direction, includeCurrent) {
|
||||
continue
|
||||
}
|
||||
@@ -196,17 +196,17 @@ func expectedDirectionalDeclinationEvent(events []DeclinationEvent, queryJDE flo
|
||||
found = true
|
||||
continue
|
||||
}
|
||||
if math.Abs(delta) < math.Abs(best.JDE-queryJDE) || (math.Abs(delta) == math.Abs(best.JDE-queryJDE) && event.JDE < best.JDE) {
|
||||
if math.Abs(delta) < math.Abs(best.JD-queryJD) || (math.Abs(delta) == math.Abs(best.JD-queryJD) && event.JD < best.JD) {
|
||||
best = event
|
||||
}
|
||||
}
|
||||
return best
|
||||
}
|
||||
|
||||
func expectedClosestDeclinationEvent(events []DeclinationEvent, queryJDE float64) DeclinationEvent {
|
||||
last := expectedDirectionalDeclinationEvent(events, queryJDE, -1, true)
|
||||
next := expectedDirectionalDeclinationEvent(events, queryJDE, 1, false)
|
||||
if math.Abs(queryJDE-last.JDE) <= math.Abs(next.JDE-queryJDE) {
|
||||
func expectedClosestDeclinationEvent(events []DeclinationEvent, queryJD float64) DeclinationEvent {
|
||||
last := expectedDirectionalDeclinationEvent(events, queryJD, -1, true)
|
||||
next := expectedDirectionalDeclinationEvent(events, queryJD, 1, false)
|
||||
if math.Abs(queryJD-last.JD) <= math.Abs(next.JD-queryJD) {
|
||||
return last
|
||||
}
|
||||
return next
|
||||
|
||||
Reference in New Issue
Block a user